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