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