From 1c750eef2ce2384bcfed4e5c0ba235b92785d1d3 Mon Sep 17 00:00:00 2001 From: Jafeth Garro <45522320+IAmJafeth@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:54:58 -0600 Subject: [PATCH] Created security.md --- .gitignore | 2 + content/.obsidian/community-plugins.json | 4 +- content/.obsidian/hotkeys.json | 11 + .../plugins/callout-manager/data.json | 11 + .../.obsidian/plugins/callout-manager/main.js | 4367 +++++++++++++++++ .../plugins/callout-manager/manifest.json | 10 + .../plugins/callout-manager/styles.css | 1 + .../plugins/emoji-shortcodes/data.json | 9 + .../plugins/emoji-shortcodes/main.js | 2069 ++++++++ .../plugins/emoji-shortcodes/manifest.json | 11 + .../plugins/emoji-shortcodes/styles.css | 31 + content/.obsidian/workspace.json | 49 +- content/security.md | 8 + 13 files changed, 6571 insertions(+), 12 deletions(-) create mode 100644 content/.obsidian/hotkeys.json create mode 100644 content/.obsidian/plugins/callout-manager/data.json create mode 100644 content/.obsidian/plugins/callout-manager/main.js create mode 100644 content/.obsidian/plugins/callout-manager/manifest.json create mode 100644 content/.obsidian/plugins/callout-manager/styles.css create mode 100644 content/.obsidian/plugins/emoji-shortcodes/data.json create mode 100644 content/.obsidian/plugins/emoji-shortcodes/main.js create mode 100644 content/.obsidian/plugins/emoji-shortcodes/manifest.json create mode 100644 content/.obsidian/plugins/emoji-shortcodes/styles.css create mode 100644 content/security.md diff --git a/.gitignore b/.gitignore index cd22eac..8aa578d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ public/ quartz/.quartz-cache .DS_Store +workspace.json +.idea/discord.xml diff --git a/content/.obsidian/community-plugins.json b/content/.obsidian/community-plugins.json index f88e4a9..b294459 100644 --- a/content/.obsidian/community-plugins.json +++ b/content/.obsidian/community-plugins.json @@ -1,3 +1,5 @@ [ - "obsidian-style-settings" + "obsidian-style-settings", + "emoji-shortcodes", + "callout-manager" ] \ No newline at end of file diff --git a/content/.obsidian/hotkeys.json b/content/.obsidian/hotkeys.json new file mode 100644 index 0000000..feae1e4 --- /dev/null +++ b/content/.obsidian/hotkeys.json @@ -0,0 +1,11 @@ +{ + "callout-manager:manage-callouts": [ + { + "modifiers": [ + "Mod", + "Shift" + ], + "key": "C" + } + ] +} \ No newline at end of file diff --git a/content/.obsidian/plugins/callout-manager/data.json b/content/.obsidian/plugins/callout-manager/data.json new file mode 100644 index 0000000..659b457 --- /dev/null +++ b/content/.obsidian/plugins/callout-manager/data.json @@ -0,0 +1,11 @@ +{ + "callouts": { + "custom": [], + "settings": {} + }, + "calloutDetection": { + "obsidian": true, + "theme": true, + "snippet": true + } +} \ No newline at end of file diff --git a/content/.obsidian/plugins/callout-manager/main.js b/content/.obsidian/plugins/callout-manager/main.js new file mode 100644 index 0000000..a25859a --- /dev/null +++ b/content/.obsidian/plugins/callout-manager/main.js @@ -0,0 +1,4367 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + default: () => CalloutManagerPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian25 = require("obsidian"); + +// node_modules/obsidian-extra/dist/esm/functions/getFloatingWindows.js +function getFloatingWindows(app2) { + var _a, _b, _c, _d; + return (_d = (_c = (_b = (_a = app2 === null || app2 === void 0 ? void 0 : app2.workspace) === null || _a === void 0 ? void 0 : _a.floatingSplit) === null || _b === void 0 ? void 0 : _b.children) === null || _c === void 0 ? void 0 : _c.map((split) => split.win)) !== null && _d !== void 0 ? _d : []; +} + +// node_modules/obsidian-extra/dist/esm/functions/getCurrentThemeID.js +function getCurrentThemeID(app2) { + const theme = app2.customCss.theme; + return theme === "" ? null : theme; +} + +// node_modules/obsidian-extra/dist/esm/functions/getCurrentColorScheme.js +function getCurrentThemeID2(app2) { + const { body } = app2.workspace.containerEl.doc; + return body.classList.contains("theme-dark") ? "dark" : "light"; +} + +// node_modules/obsidian-extra/dist/esm/functions/getThemeManifest.js +function getThemeManifest(app2, themeID) { + const manifests = app2.customCss.themes; + if (!Object.prototype.hasOwnProperty.call(manifests, themeID)) { + return null; + } + return manifests[themeID]; +} + +// node_modules/obsidian-extra/dist/esm/functions/isThemeInstalled.js +function isThemeInstalled(app2, themeID) { + return getThemeManifest(app2, themeID) !== null; +} + +// node_modules/obsidian-extra/dist/esm/functions/getThemeStyleElement.js +function getThemeStyleElement(app2) { + const currentTheme = getCurrentThemeID(app2); + if (currentTheme == null || !isThemeInstalled(app2, currentTheme)) { + return null; + } + return app2.customCss.styleEl; +} + +// node_modules/obsidian-extra/dist/esm/functions/isSnippetEnabled.js +function isSnippetEnabled(app2, snippetID) { + return app2.customCss.enabledSnippets.has(snippetID); +} + +// node_modules/obsidian-extra/dist/esm/functions/fetchObsidianStyleSheet.js +var import_obsidian = require("obsidian"); + +// node_modules/obsidian-extra/dist/esm/internal/utils/versionCompare.js +function versionCompare(a, b) { + const aParts = a.split(".").map((n) => parseInt(n, 10)); + const bParts = b.split(".").map((n) => parseInt(n, 10)); + const partsSize = Math.max(aParts.length, bParts.length); + arrayPadEnd(aParts, partsSize, 0); + arrayPadEnd(bParts, partsSize, 0); + for (let i = 0; i < partsSize; i++) { + if (aParts[i] < bParts[i]) + return -1; + if (aParts[i] > bParts[i]) + return 1; + } + return 0; +} +function arrayPadEnd(arr, length, fill) { + const missing = length - arr.length; + if (missing > 0) { + arr.push(...new Array(missing).fill(fill)); + } +} + +// node_modules/obsidian-extra/dist/esm/functions/fetchObsidianStyleSheet.js +var __awaiter = function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +function fetchObsidianStyleSheet(app2) { + return __awaiter(this, void 0, void 0, function* () { + let errors = []; + const orElse = (cb) => (ex) => { + errors.push(ex); + return cb(); + }; + const result = yield viaElectron("app.css").catch(orElse(() => viaFetch("app.css"))).catch(orElse(() => viaDom("app.css"))); + result._errors = errors; + return result; + }); +} +function viaFetch(path) { + return __awaiter(this, void 0, void 0, function* () { + if (import_obsidian.Platform.isDesktopApp) { + throw new Error("Obsidian styles via fetch() does not work under Electron."); + } + return fetch(`/${path}`).then((r) => r.text()).then((t) => ({ + method: "fetch", + cssText: t + })); + }); +} +function viaElectron(path) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + if (versionCompare(import_obsidian.apiVersion, "1.1.16") > 0) { + throw new Error(`Obsidian ${import_obsidian.apiVersion} has not been tested with this function`); + } + const require2 = globalThis.require; + const electron = (_a = globalThis.electron) !== null && _a !== void 0 ? _a : require2 === null || require2 === void 0 ? void 0 : require2("electron"); + if (electron == null) { + throw new Error("Unable to get electron module from web renderer process"); + } + const fs = require2 === null || require2 === void 0 ? void 0 : require2("fs/promises"); + if ((fs === null || fs === void 0 ? void 0 : fs.readFile) == null) { + throw new Error("Unable to get fs/promises module from web renderer process"); + } + const resources = electron.ipcRenderer.sendSync("resources"); + return fs.readFile(`${resources}/${path}`, "utf8").then((t) => ({ + method: "electron", + cssText: t + })); + }); +} +function viaDom(path) { + let found = false; + const lines = []; + for (const styleSheet of Array.from(document.styleSheets)) { + if (!(styleSheet.ownerNode instanceof HTMLLinkElement)) + continue; + const href = styleSheet.ownerNode.getAttribute("href"); + if (href !== path && href !== `/${path}`) + continue; + found = true; + for (const rule of Array.from(styleSheet.cssRules)) { + lines.push(rule.cssText); + } + } + if (!found) { + throw new Error("Unable to find element for Obsidian's stylesheet"); + } + return { + method: "dom", + cssText: lines.join("\n") + }; +} + +// node_modules/obsidian-extra/dist/esm/functions/getInstalledSnippetIDs.js +function getInstalledSnippetIDs(app2) { + return app2.customCss.snippets; +} + +// node_modules/obsidian-extra/dist/esm/functions/getSnippetStyleElements.js +function getSnippetStyleElements(app2) { + const styleManager = app2.customCss; + const snippets = getInstalledSnippetIDs(app2); + const map = /* @__PURE__ */ new Map(); + for (let i = 0, elI = 0; i < snippets.length; i++) { + const snippetID = styleManager.snippets[i]; + if (isSnippetEnabled(app2, snippetID)) { + map.set(snippetID, styleManager.extraStyleEls[elI]); + elI++; + } + } + return map; +} + +// node_modules/obsidian-extra/dist/esm/functions/openPluginSettings.js +function openPluginSettings(app2, plugin) { + var _a, _b; + const settingManager = app2.setting; + const pluginId = typeof plugin === "string" ? plugin : plugin.manifest.id; + if (((_a = settingManager.activeTab) === null || _a === void 0 ? void 0 : _a.id) !== pluginId) { + settingManager.openTabById(""); + } + settingManager.open(); + if (((_b = settingManager.activeTab) === null || _b === void 0 ? void 0 : _b.id) !== pluginId) { + settingManager.openTabById(pluginId); + } +} + +// node_modules/obsidian-extra/dist/esm/functions/createCustomStyleSheet.js +var Counter = Symbol("CustomStylesheet count"); +function foreachWindow(app2, fn) { + fn(app2.workspace.containerEl.doc, true); + for (const float of getFloatingWindows(app2)) { + fn(float.document, false); + } +} +function createCustomStyleSheet(app2, plugin) { + var _a; + let result; + const pl = plugin; + const plId = plugin.manifest.id; + const ssId = ((_a = pl[Counter]) !== null && _a !== void 0 ? _a : pl[Counter] = 0).toString(); + pl[Counter]++; + const styleEl = app2.workspace.containerEl.doc.createElement("style"); + const styleElInFloats = []; + styleEl.setAttribute("data-source-plugin", plId); + styleEl.setAttribute("data-source-id", ssId); + function unapply() { + styleElInFloats.splice(0, styleElInFloats.length).forEach((el) => el.remove()); + styleEl.detach(); + foreachWindow(app2, (doc) => { + for (const styleEl2 of Array.from(doc.head.querySelectorAll("style"))) { + if (result.is(styleEl2)) { + styleEl2.remove(); + } + } + }); + } + function reapply() { + unapply(); + foreachWindow(app2, (doc, isFloating) => { + let lastEl = doc.head.lastElementChild; + for (let el = lastEl; el != null; el = el.previousElementSibling) { + lastEl = el; + if (lastEl.tagName === "STYLE") { + break; + } + } + if (!isFloating) { + lastEl === null || lastEl === void 0 ? void 0 : lastEl.insertAdjacentElement("afterend", styleEl); + return; + } + const styleElClone = styleEl.cloneNode(true); + styleElInFloats.push(styleElClone); + lastEl === null || lastEl === void 0 ? void 0 : lastEl.insertAdjacentElement("afterend", styleElClone); + }); + } + app2.workspace.on("css-change", reapply); + app2.workspace.on("layout-change", reapply); + result = Object.freeze(Object.defineProperties(() => { + unapply(); + app2.workspace.off("css-change", reapply); + app2.workspace.off("layout-change", reapply); + }, { + css: { + enumerable: true, + configurable: false, + get() { + return styleEl.textContent; + }, + set(v) { + styleEl.textContent = v; + for (const styleEl2 of styleElInFloats) { + styleEl2.textContent = v; + } + } + }, + is: { + enumerable: false, + configurable: false, + value: (el) => { + return el.getAttribute("data-source-plugin") === plId && el.getAttribute("data-source-id") === ssId; + } + }, + setAttribute: { + enumerable: false, + configurable: false, + value: (attr, value) => { + if (attr === "data-source-id" || attr === "data-source-plugin") { + throw new Error(`Cannot change attribute '${attr}' on custom style sheet.`); + } + styleEl.setAttribute(attr, value); + for (const styleEl2 of styleElInFloats) { + styleEl2.setAttribute(attr, value); + } + } + }, + removeAttribute: { + enumerable: false, + configurable: false, + value: (attr) => { + if (attr === "data-source-id" || attr === "data-source-plugin") { + throw new Error(`Cannot remove attribute '${attr}' from custom style sheet.`); + } + styleEl.removeAttribute(attr); + for (const styleEl2 of styleElInFloats) { + styleEl2.removeAttribute(attr); + } + } + } + })); + reapply(); + return result; +} + +// node_modules/obsidian-extra/dist/esm/functions/detectPlatformBrowser.js +var import_obsidian2 = require("obsidian"); + +// node_modules/obsidian-extra/dist/esm/functions/detectPlatformRuntime.js +var import_obsidian3 = require("obsidian"); + +// node_modules/obsidian-extra/dist/esm/functions/detectPlatformOperatingSystem.js +var import_obsidian4 = require("obsidian"); + +// src/ui/paned-setting-tab.ts +var import_obsidian6 = require("obsidian"); + +// node_modules/obsidian-extra/dist/esm/functions/closeSettings.js +function closeSettings(app2) { + const settingManager = app2.setting; + settingManager.close(); +} + +// src/ui/pane-layers.ts +var import_obsidian5 = require("obsidian"); +var UIPaneLayers = class { + constructor(options) { + this.layers = []; + this.closeParent = options.close; + this.navInstance = { + open: (pane) => this.push(pane), + close: () => this.pop(), + replace: (pane) => this.top = pane + }; + } + /** + * Pushes a new pane on top of the stack. + * The active pane will be suspended. + * + * @param pane The pane to push. + */ + push(pane) { + const { activePane: oldPane } = this; + if (oldPane !== void 0) { + const title = oldPane.title; + this.layers.push({ + scroll: { top: this.scrollEl.scrollTop, left: this.scrollEl.scrollLeft }, + state: oldPane.suspendState(), + pane: oldPane, + title: typeof title === "string" ? title : title.subtitle + }); + this.setPaneVariables(oldPane, false); + this.containerEl.empty(); + } + const newPane = this.activePane = pane; + this.setPaneVariables(newPane, true); + newPane.onReady(); + this.doDisplay(true); + this.scrollEl.scrollTo({ top: 0, left: 0 }); + } + /** + * Pops the active pane off the stack. + * The active pane will be destroyed, and the one underneath it will be restored. + * + * @param pane The pane to push. + */ + pop(options) { + if (this.activePane === void 0) { + this.closeParent(); + return void 0; + } + const noDisplay = options?.noDisplay ?? false; + const oldPane = this.activePane; + const newPane = this.layers.pop(); + this.activePane = void 0; + this.setPaneVariables(oldPane, false); + oldPane.onClose(options?.cancelled ?? false); + if (!noDisplay) { + this.containerEl.empty(); + } + if (newPane !== void 0) { + this.activePane = newPane.pane; + this.setPaneVariables(newPane.pane, true); + newPane.pane.restoreState(newPane.state); + if (!noDisplay) { + this.doDisplay(true); + this.scrollEl.scrollTo(newPane.scroll); + } + } + return oldPane; + } + /** + * Removes all panes off the stack. + * All panes will be destroyed. + * + * @param pane The pane to push. + */ + clear(options) { + const removed = []; + const opts = { + noDisplay: true, + ...options ?? {} + }; + while (this.activePane !== void 0) { + removed.push(this.pop(opts)); + } + return removed; + } + /** + * The top-most (i.e. currently active) pane in the layers. + */ + get top() { + return this.activePane; + } + set top(pane) { + const { activePane: oldTop } = this; + if (oldTop !== void 0) { + this.setPaneVariables(oldTop, false); + oldTop.onClose(false); + } + const newPane = this.activePane = pane; + this.setPaneVariables(newPane, true); + newPane.onReady(); + this.doDisplay(true); + } + doDisplay(renderControls) { + const { activePane, titleEl, navEl, containerEl } = this; + if (activePane === void 0) { + return; + } + navEl.empty(); + if (this.layers.length > 0) { + new import_obsidian5.ButtonComponent(this.navEl).setIcon("lucide-arrow-left-circle").setClass("clickable-icon").setTooltip(`Back to ${this.layers[this.layers.length - 1].title}`).onClick(() => this.navInstance.close()); + } + titleEl.empty(); + const { title } = activePane; + if (typeof title === "string") { + titleEl.createEl("h2", { text: title }); + } else { + titleEl.createEl("h2", { text: title.title }); + titleEl.createEl("h3", { text: title.subtitle }); + } + if (renderControls) { + this.controlsEl.empty(); + activePane.displayControls(); + } + containerEl.empty(); + activePane.display(); + } + setPaneVariables(pane, attached) { + const notAttachedError = () => { + throw new Error("Not attached"); + }; + Object.defineProperties(pane, { + nav: { + configurable: true, + enumerable: true, + get: attached ? () => this.navInstance : notAttachedError + }, + containerEl: { + configurable: true, + enumerable: true, + get: attached ? () => this.containerEl : notAttachedError + }, + controlsEl: { + configurable: true, + enumerable: true, + get: attached ? () => this.controlsEl : notAttachedError + } + }); + } +}; + +// src/ui/paned-setting-tab.ts +var UISettingTab = class extends import_obsidian6.PluginSettingTab { + constructor(plugin, createDefault) { + super(plugin.app, plugin); + this.plugin = plugin; + this.createDefault = createDefault; + this.initLayer = null; + this.layers = new UIPaneLayers({ + close: () => closeSettings(this.app) + }); + } + openWithPane(pane) { + this.initLayer = pane; + openPluginSettings(this.plugin.app, this.plugin); + } + /** @override */ + hide() { + this.initLayer = null; + this.layers.clear(); + super.hide(); + } + display() { + const { containerEl, layers } = this; + containerEl.empty(); + containerEl.classList.add("calloutmanager-setting-tab", "calloutmanager-pane"); + const headerEl = containerEl.createDiv({ cls: "calloutmanager-setting-tab-header" }); + layers.navEl = headerEl.createDiv({ cls: "calloutmanager-setting-tab-nav" }); + layers.titleEl = headerEl.createDiv({ cls: "calloutmanager-setting-tab-title" }); + const controlsEl = headerEl.createDiv({ cls: "calloutmanager-setting-tab-controls" }); + layers.controlsEl = controlsEl.createDiv(); + layers.scrollEl = containerEl.createDiv({ + cls: "calloutmanager-setting-tab-viewport vertical-tab-content" + }); + layers.containerEl = layers.scrollEl.createDiv({ cls: "calloutmanager-setting-tab-content" }); + controlsEl.createDiv({ cls: "modal-close-button" }, (closeButtonEl) => { + closeButtonEl.addEventListener("click", (ev) => { + if (!ev.isTrusted) + return; + closeSettings(this.app); + }); + }); + layers.clear(); + const initLayer = this.initLayer ?? this.createDefault(); + this.initLayer = null; + layers.top = initLayer; + } +}; + +// src/api-common.ts +var emitter = Symbol("emitter"); +var destroy = Symbol("destroy"); + +// src/api-v1.ts +var import_obsidian7 = require("obsidian"); + +// src/util/color.ts +function toHSV(color) { + if ("h" in color && "s" in color && "v" in color) + return color; + const rFloat = color.r / 255; + const gFloat = color.g / 255; + const bFloat = color.b / 255; + const cmax = Math.max(rFloat, gFloat, bFloat); + const cmin = Math.min(rFloat, gFloat, bFloat); + const delta = cmax - cmin; + let h = 0; + if (cmax !== cmin) { + switch (cmax) { + case rFloat: + h = (60 * ((gFloat - bFloat) / delta) + 360) % 360; + break; + case gFloat: + h = (60 * ((bFloat - rFloat) / delta) + 120) % 360; + break; + case bFloat: + h = (60 * ((rFloat - gFloat) / delta) + 240) % 360; + break; + } + } + const s = cmax === 0 ? 0 : delta / cmax * 100; + const v = cmax * 100; + const hsv = { h, s, v }; + if ("a" in color) { + hsv.a = color.a / 255 * 100; + } + return hsv; +} +function toHexRGB(color) { + const parts = [color.r, color.g, color.b, ..."a" in color ? [color.a] : []]; + return parts.map((c) => c.toString(16).padStart(2, "0")).join(""); +} +var REGEX_RGB = /^\s*rgba?\(\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?\s*)\)\s*$/i; +function parseColorRGB(rgb) { + const matches2 = REGEX_RGB.exec(rgb); + if (matches2 === null) + return null; + const components = matches2.slice(1).map((v) => v.trim()); + const rgbComponents = rgbComponentStringsToNumber(components); + if (rgbComponents === null) { + return null; + } + if (void 0 !== rgbComponents.find((v) => isNaN(v) || v < 0 || v > 255)) { + return null; + } + return { + r: rgbComponents[0], + g: rgbComponents[1], + b: rgbComponents[2] + }; +} +function rgbComponentStringsToNumber(components) { + if (components[0].endsWith("%")) { + if (void 0 !== components.slice(1, 3).find((c) => !c.endsWith("%"))) { + return null; + } + return components.map((v) => parseFloat(v.substring(0, v.length - 1))).map((v) => Math.floor(v * 255 / 100)); + } + if (void 0 !== components.slice(1, 3).find((c) => c.endsWith("%"))) { + return null; + } + return components.map((v) => parseInt(v, 10)); +} + +// src/callout-util.ts +function getColorFromCallout(callout) { + return parseColorRGB(`rgb(${callout.color})`); +} +function getTitleFromCallout(callout) { + const matches2 = /^(.)(.*)/u.exec(callout.id); + if (matches2 == null) + return callout.id; + const firstChar = matches2[1].toLocaleUpperCase(); + const remainingChars = matches2[2].toLocaleLowerCase().replace(/-+/g, " "); + return `${firstChar}${remainingChars}`; +} + +// src/api-v1.ts +var CalloutManagerAPI_V1 = class { + constructor(plugin, consumer) { + this.plugin = plugin; + this.consumer = consumer; + this[emitter] = new import_obsidian7.Events(); + if (consumer != null) { + console.debug("Created API V1 Handle:", { plugin: consumer.manifest.id }); + } + } + /** + * Called to destroy an API handle bound to a consumer. + */ + [(emitter, destroy)]() { + const consumer = this.consumer; + console.debug("Destroyed API V1 Handle:", { plugin: consumer.manifest.id }); + } + /** @override */ + getCallouts() { + return this.plugin.callouts.values().map((callout) => Object.freeze({ ...callout })); + } + /** @override */ + getColor(callout) { + const color = getColorFromCallout(callout); + return color ?? { invalid: callout.color }; + } + /** @override */ + getTitle(callout) { + return getTitleFromCallout(callout); + } + /** @override */ + on(event, listener) { + if (this.consumer == null) { + throw new Error("Cannot listen for events without an API consumer."); + } + this[emitter].on(event, listener); + } + /** @override */ + off(event, listener) { + if (this.consumer == null) { + throw new Error("Cannot listen for events without an API consumer."); + } + this[emitter].off(event, listener); + } +}; + +// src/apis.ts +var CalloutManagerAPIs = class { + constructor(plugin) { + this.plugin = plugin; + this.handles = /* @__PURE__ */ new Map(); + } + /** + * Creates (or gets) an instance of the Callout Manager API for a plugin. + * If the plugin is undefined, only trivial functions are available. + * + * @param version The API version. + * @param consumerPlugin The plugin using the API. + * + * @internal + */ + async newHandle(version, consumerPlugin, cleanupFunc) { + if (version !== "v1") + throw new Error(`Unsupported Callout Manager API: ${version}`); + if (consumerPlugin == null) { + return new CalloutManagerAPI_V1(this.plugin, void 0); + } + const existing = this.handles.get(consumerPlugin); + if (existing != null) { + return existing; + } + consumerPlugin.register(cleanupFunc); + const handle = new CalloutManagerAPI_V1(this.plugin, consumerPlugin); + this.handles.set(consumerPlugin, handle); + return handle; + } + /** + * Destroys an API handle created by {@link newHandle}. + * + * @param version The API version. + * @param consumerPlugin The plugin using the API. + * + * @internal + */ + destroyHandle(version, consumerPlugin) { + if (version !== "v1") + throw new Error(`Unsupported Callout Manager API: ${version}`); + const handle = this.handles.get(consumerPlugin); + if (handle == null) + return; + handle[destroy](); + this.handles.delete(consumerPlugin); + } + emitEventForCalloutChange(id) { + for (const handle of this.handles.values()) { + handle[emitter].trigger("change"); + } + } +}; + +// src/callout-collection.ts +var CalloutCollection = class { + constructor(resolver) { + this.resolver = resolver; + this.invalidated = /* @__PURE__ */ new Set(); + this.invalidationCount = 0; + this.cacheById = /* @__PURE__ */ new Map(); + this.cached = false; + this.snippets = new CalloutCollectionSnippets(this.invalidateSource.bind(this)); + this.builtin = new CalloutCollectionObsidian(this.invalidateSource.bind(this)); + this.theme = new CalloutCollectionTheme(this.invalidateSource.bind(this)); + this.custom = new CalloutCollectionCustom(this.invalidateSource.bind(this)); + } + get(id) { + if (!this.cached) + this.buildCache(); + const cached = this.cacheById.get(id); + if (cached === void 0) { + return void 0; + } + if (this.invalidated.has(cached)) { + this.resolveOne(cached); + } + return cached.callout; + } + /** + * Checks if a callout with this ID is in the collection. + * @param id The callout ID. + * @returns True if the callout is in the collection. + */ + has(id) { + if (!this.cached) + this.buildCache(); + return this.cacheById.has(id); + } + /** + * Gets all the known {@link CalloutID callout IDs}. + * @returns The callout IDs. + */ + keys() { + if (!this.cached) + this.buildCache(); + return Array.from(this.cacheById.keys()); + } + /** + * Gets all the known {@link Callout callouts}. + * @returns The callouts. + */ + values() { + if (!this.cached) + this.buildCache(); + this.resolveAll(); + return Array.from(this.cacheById.values()).map((c) => c.callout); + } + /** + * Returns a function that will return `true` if the collection has changed since the function was created. + * @returns The function. + */ + hasChanged() { + const countSnapshot = this.invalidationCount; + return () => this.invalidationCount !== countSnapshot; + } + /** + * Resolves the settings of a callout. + * This removes it from the set of invalidated callout caches. + * + * @param cached The callout's cache entry. + */ + resolveOne(cached) { + this.doResolve(cached); + this.invalidated.delete(cached); + } + /** + * Resolves the settings of all callouts. + */ + resolveAll() { + for (const cached of this.invalidated.values()) { + this.doResolve(cached); + } + this.invalidated.clear(); + } + doResolve(cached) { + cached.callout = this.resolver(cached.id); + cached.callout.sources = Array.from(cached.sources.values()).map(sourceFromKey); + } + /** + * Builds the initial cache of callouts. + * This creates the cache entries and associates them to a source. + */ + buildCache() { + this.invalidated.clear(); + this.cacheById.clear(); + { + const source = sourceToKey({ type: "builtin" }); + for (const callout of this.builtin.get()) { + this.addCalloutSource(callout, source); + } + } + if (this.theme.theme != null) { + const source = sourceToKey({ type: "theme", theme: this.theme.theme }); + for (const callout of this.theme.get()) { + this.addCalloutSource(callout, source); + } + } + for (const snippet of this.snippets.keys()) { + const source = sourceToKey({ type: "snippet", snippet }); + for (const callout of this.snippets.get(snippet)) { + this.addCalloutSource(callout, source); + } + } + { + const source = sourceToKey({ type: "custom" }); + for (const callout of this.custom.keys()) { + this.addCalloutSource(callout, source); + } + } + this.cached = true; + } + /** + * Marks a callout as invalidated. + * This forces the callout to be resolved again. + * + * @param id The callout ID. + */ + invalidate(id) { + if (!this.cached) + return; + const callout = this.cacheById.get(id); + if (callout !== void 0) { + console.debug("Invalided Callout Cache:", id); + this.invalidated.add(callout); + } + } + addCalloutSource(id, sourceKey) { + let callout = this.cacheById.get(id); + if (callout == null) { + callout = new CachedCallout(id); + this.cacheById.set(id, callout); + } + callout.sources.add(sourceKey); + this.invalidated.add(callout); + } + removeCalloutSource(id, sourceKey) { + const callout = this.cacheById.get(id); + if (callout == null) { + return; + } + callout.sources.delete(sourceKey); + if (callout.sources.size === 0) { + this.cacheById.delete(id); + this.invalidated.delete(callout); + } + } + /** + * Called whenever a callout source has any changes. + * This will add or remove callouts from the cache, or invalidate a callout to mark it as requiring re-resolving. + * + * @param src The source that changed. + * @param data A diff of changes. + */ + invalidateSource(src, data) { + const sourceKey = sourceToKey(src); + if (!this.cached) { + return; + } + for (const removed of data.removed) { + this.removeCalloutSource(removed, sourceKey); + } + for (const added of data.added) { + this.addCalloutSource(added, sourceKey); + } + for (const changed of data.changed) { + const callout = this.cacheById.get(changed); + if (callout != null) { + this.invalidated.add(callout); + } + } + this.invalidationCount++; + } +}; +var CachedCallout = class { + constructor(id) { + this.id = id; + this.sources = /* @__PURE__ */ new Set(); + this.callout = null; + } +}; +var CalloutCollectionSnippets = class { + constructor(invalidate) { + this.data = /* @__PURE__ */ new Map(); + this.invalidate = invalidate; + } + get(id) { + const value = this.data.get(id); + if (value === void 0) { + return void 0; + } + return Array.from(value.values()); + } + set(id, callouts) { + const source = { type: "snippet", snippet: id }; + const old = this.data.get(id); + const updated = new Set(callouts); + this.data.set(id, updated); + if (old === void 0) { + this.invalidate(source, { added: callouts, changed: [], removed: [] }); + return; + } + const diffs = diff(old, updated); + this.invalidate(source, { + added: diffs.added, + removed: diffs.removed, + changed: diffs.same + }); + } + delete(id) { + const old = this.data.get(id); + const deleted = this.data.delete(id); + if (old !== void 0) { + this.invalidate( + { type: "snippet", snippet: id }, + { + added: [], + changed: [], + removed: Array.from(old.keys()) + } + ); + } + return deleted; + } + clear() { + for (const id of Array.from(this.data.keys())) { + this.delete(id); + } + } + keys() { + return Array.from(this.data.keys()); + } +}; +var CalloutCollectionObsidian = class { + constructor(invalidate) { + this.data = /* @__PURE__ */ new Set(); + this.invalidate = invalidate; + } + set(callouts) { + const old = this.data; + const updated = this.data = new Set(callouts); + const diffs = diff(old, updated); + this.invalidate( + { type: "builtin" }, + { + added: diffs.added, + removed: diffs.removed, + changed: diffs.same + } + ); + } + get() { + return Array.from(this.data.values()); + } +}; +var CalloutCollectionTheme = class { + constructor(invalidate) { + this.data = /* @__PURE__ */ new Set(); + this.invalidate = invalidate; + this.oldTheme = ""; + } + get theme() { + return this.oldTheme; + } + set(theme, callouts) { + const old = this.data; + const oldTheme = this.oldTheme; + const updated = this.data = new Set(callouts); + this.oldTheme = theme; + if (this.oldTheme === theme) { + const diffs = diff(old, updated); + this.invalidate( + { type: "theme", theme }, + { + added: diffs.added, + removed: diffs.removed, + changed: diffs.same + } + ); + return; + } + this.invalidate( + { type: "theme", theme: oldTheme ?? "" }, + { + added: [], + removed: Array.from(old.values()), + changed: [] + } + ); + this.invalidate( + { type: "theme", theme }, + { + added: callouts, + removed: [], + changed: [] + } + ); + } + delete() { + const old = this.data; + const oldTheme = this.oldTheme; + this.data = /* @__PURE__ */ new Set(); + this.oldTheme = null; + this.invalidate( + { type: "theme", theme: oldTheme ?? "" }, + { + added: [], + removed: Array.from(old.values()), + changed: [] + } + ); + } + get() { + return Array.from(this.data.values()); + } +}; +var CalloutCollectionCustom = class { + constructor(invalidate) { + this.data = []; + this.invalidate = invalidate; + } + has(id) { + return void 0 !== this.data.find((existingId) => existingId === id); + } + add(...ids) { + const set = new Set(this.data); + const added = []; + for (const id of ids) { + if (!set.has(id)) { + added.push(id); + set.add(id); + this.data.push(id); + } + } + if (added.length > 0) { + this.invalidate({ type: "custom" }, { added, removed: [], changed: [] }); + } + } + delete(...ids) { + const { data } = this; + const removed = []; + for (const id of ids) { + const index = data.findIndex((existingId) => id === existingId); + if (index !== void 0) { + data.splice(index, 1); + removed.push(id); + } + } + if (removed.length > 0) { + this.invalidate({ type: "custom" }, { added: [], removed, changed: [] }); + } + } + keys() { + return this.data.slice(0); + } + clear() { + const removed = this.data; + this.data = []; + this.invalidate({ type: "custom" }, { added: [], removed, changed: [] }); + } +}; +function diff(before, after) { + const added = []; + const removed = []; + const same = []; + for (const item of before.values()) { + (after.has(item) ? same : removed).push(item); + } + for (const item of after.values()) { + if (!before.has(item)) { + added.push(item); + } + } + return { added, removed, same }; +} +function sourceToKey(source) { + switch (source.type) { + case "builtin": + return "builtin"; + case "snippet": + return `snippet:${source.snippet}`; + case "theme": + return `theme:${source.theme}`; + case "custom": + return `custom`; + } +} +function sourceFromKey(sourceKey) { + if (sourceKey === "builtin") { + return { type: "builtin" }; + } + if (sourceKey === "custom") { + return { type: "custom" }; + } + if (sourceKey.startsWith("snippet:")) { + return { type: "snippet", snippet: sourceKey.substring("snippet:".length) }; + } + if (sourceKey.startsWith("theme:")) { + return { type: "theme", theme: sourceKey.substring("theme:".length) }; + } + throw new Error("Unknown source key: " + sourceKey); +} + +// src/ui/component/callout-preview.ts +var import_obsidian8 = require("obsidian"); +var NO_ATTACH = Symbol(); +var CalloutPreviewComponent = class extends import_obsidian8.Component { + constructor(containerEl, options) { + super(); + const { color, icon, id, title, content } = options; + const frag = document.createDocumentFragment(); + const calloutEl = this.calloutEl = frag.createDiv({ cls: ["callout", "calloutmanager-preview"] }); + const titleElContainer = calloutEl.createDiv({ cls: "callout-title" }); + this.iconEl = titleElContainer.createDiv({ cls: "callout-icon" }); + const titleEl = this.titleEl = titleElContainer.createDiv({ cls: "callout-title-inner" }); + const contentEl = this.contentEl = content === void 0 ? void 0 : calloutEl.createDiv({ cls: "callout-content" }); + this.setIcon(icon); + this.setColor(color); + this.setCalloutID(id); + if (title == null) + titleEl.textContent = id; + else if (typeof title === "function") + title(titleEl); + else if (typeof title === "string") + titleEl.textContent = title; + else + titleEl.appendChild(title); + if (contentEl != null) { + if (typeof content === "function") + content(contentEl); + else if (typeof content === "string") + contentEl.textContent = content; + else + contentEl.appendChild(content); + } + if (containerEl != NO_ATTACH) { + CalloutPreviewComponent.prototype.attachTo.call(this, containerEl); + } + } + /** + * Changes the callout ID. + * This will *not* change the appearance of the preview. + * + * @param id The new ID to use. + */ + setCalloutID(id) { + const { calloutEl } = this; + calloutEl.setAttribute("data-callout", id); + return this; + } + /** + * Changes the callout icon. + * + * @param icon The ID of the new icon to use. + */ + setIcon(icon) { + const { iconEl, calloutEl } = this; + calloutEl.style.setProperty("--callout-icon", icon); + iconEl.empty(); + const iconSvg = (0, import_obsidian8.getIcon)(icon); + if (iconSvg != null) { + this.iconEl.appendChild(iconSvg); + } + return this; + } + /** + * Changes the callout color. + * + * @param color The color to use. + */ + setColor(color) { + const { calloutEl } = this; + if (color == null) { + calloutEl.style.removeProperty("--callout-color"); + return this; + } + calloutEl.style.setProperty("--callout-color", `${color.r}, ${color.g}, ${color.b}`); + return this; + } + /** + * Attaches the callout preview to a DOM element. + * This places it at the end of the element. + * + * @param containerEl The container to attach to. + */ + attachTo(containerEl) { + containerEl.appendChild(this.calloutEl); + return this; + } + /** + * Resets the `--callout-color` and `--callout-icon` CSS properties added to the callout element. + */ + resetStylePropertyOverrides() { + const { calloutEl } = this; + calloutEl.style.removeProperty("--callout-color"); + calloutEl.style.removeProperty("--callout-icon"); + } +}; +var IsolatedCalloutPreviewComponent = class extends CalloutPreviewComponent { + constructor(containerEl, options) { + super(NO_ATTACH, options); + const frag = document.createDocumentFragment(); + const focused = options.focused ?? false; + const colorScheme = options.colorScheme; + const readingView = (options.viewType ?? "reading") === "reading"; + const cssEls = options?.cssEls ?? getCurrentStyles(containerEl?.doc); + const shadowHostEl = this.shadowHostEl = frag.createDiv(); + const shadowRoot = this.shadowRoot = shadowHostEl.attachShadow({ delegatesFocus: false, mode: "closed" }); + const shadowHead = this.shadowHead = shadowRoot.createEl("head"); + const shadowBody = this.shadowBody = shadowRoot.createEl("body"); + const styleEls = this.styleEls = []; + for (const cssEl of cssEls) { + const cssElClone = cssEl.cloneNode(true); + if (cssEl.tagName === "STYLE") { + styleEls.push(cssElClone); + } + shadowHead.appendChild(cssElClone); + } + shadowHead.createEl("style", { text: SHADOW_DOM_RESET_STYLES }); + this.customStyleEl = shadowHead.createEl("style", { attr: { "data-custom-styles": "true" } }); + shadowBody.classList.add(`theme-${colorScheme}`, "obsidian-app"); + const viewContentEl = shadowBody.createDiv({ cls: "app-container" }).createDiv({ cls: "horizontal-main-container" }).createDiv({ cls: "workspace" }).createDiv({ cls: "workspace-split mod-root" }).createDiv({ cls: `workspace-tabs ${focused ? "mod-active" : ""}` }).createDiv({ cls: "workspace-tab-container" }).createDiv({ cls: `workspace-leaf ${focused ? "mod-active" : ""}` }).createDiv({ cls: "workspace-leaf-content" }).createDiv({ cls: "view-content" }); + const calloutParentEl = readingView ? createReadingViewContainer(viewContentEl) : createLiveViewContainer(viewContentEl); + calloutParentEl.appendChild(this.calloutEl); + if (containerEl != null) { + IsolatedCalloutPreviewComponent.prototype.attachTo.call(this, containerEl); + } + } + /** + * Replaces the `