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