/* 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 `