/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin: https://github.com/terrastruct/d2-obsidian */ var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/lodash.debounce/index.js var require_lodash = __commonJS({ "node_modules/lodash.debounce/index.js"(exports, module2) { var FUNC_ERROR_TEXT = "Expected a function"; var NAN = 0 / 0; var symbolTag = "[object Symbol]"; var reTrim = /^\s+|\s+$/g; var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; var reIsBinary = /^0b[01]+$/i; var reIsOctal = /^0o[0-7]+$/i; var freeParseInt = parseInt; var freeGlobal = typeof global == "object" && global && global.Object === Object && global; var freeSelf = typeof self == "object" && self && self.Object === Object && self; var root = freeGlobal || freeSelf || Function("return this")(); var objectProto = Object.prototype; var objectToString = objectProto.toString; var nativeMax = Math.max; var nativeMin = Math.min; var now = function() { return root.Date.now(); }; function debounce2(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != "function") { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = "maxWait" in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = "trailing" in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = void 0; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { lastInvokeTime = time; timerId = setTimeout(timerExpired, wait); return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result2 = wait - timeSinceLastCall; return maxing ? nativeMin(result2, maxWait - timeSinceLastInvoke) : result2; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait; } function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = void 0; if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = void 0; return result; } function cancel() { if (timerId !== void 0) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = void 0; } function flush() { return timerId === void 0 ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === void 0) { return leadingEdge(lastCallTime); } if (maxing) { timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === void 0) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } function isObject(value) { var type = typeof value; return !!value && (type == "object" || type == "function"); } function isObjectLike(value) { return !!value && typeof value == "object"; } function isSymbol(value) { return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag; } function toNumber(value) { if (typeof value == "number") { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == "function" ? value.valueOf() : value; value = isObject(other) ? other + "" : other; } if (typeof value != "string") { return value === 0 ? value : +value; } value = value.replace(reTrim, ""); var isBinary = reIsBinary.test(value); return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; } module2.exports = debounce2; } }); // src/main.ts var main_exports = {}; __export(main_exports, { default: () => D2Plugin }); module.exports = __toCommonJS(main_exports); var import_obsidian3 = require("obsidian"); // src/settings.ts var import_obsidian = require("obsidian"); // src/constants.ts var LAYOUT_ENGINES = { DAGRE: { value: "dagre", label: "dagre" }, ELK: { value: "elk", label: "ELK" }, TALA: { value: "tala", label: "TALA" } }; var RecompileIcon = ` `; // src/settings.ts var DEFAULT_SETTINGS = { layoutEngine: "dagre", debounce: 500, theme: 0, apiToken: "", d2Path: "", pad: 100, sketch: false, containerHeight: 800 }; var D2SettingsTab = class extends import_obsidian.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } addTALASettings() { const talaSettings = this.containerEl.createEl("div"); talaSettings.createEl("h3", { text: "TALA settings" }); new import_obsidian.Setting(talaSettings).setName("API token").setDesc('To use TALA, copy your API token here or in ~/.local/state/tstruct/auth.json under the field "api_token"').addText((text) => text.setPlaceholder("tstruct_...").setValue(this.plugin.settings.apiToken).setDisabled(this.plugin.settings.layoutEngine !== LAYOUT_ENGINES.TALA.value).onChange(async (value) => { if (value && !value.startsWith("tstruct_")) { new import_obsidian.Notice("Invalid API token"); } else { this.plugin.settings.apiToken = value; await this.plugin.saveSettings(); } })); this.talaSettings = talaSettings; } display() { const { containerEl } = this; containerEl.empty(); containerEl.createEl("h1", { text: "D2 plugin settings" }); new import_obsidian.Setting(containerEl).setName("Layout engine").setDesc('Available layout engines include "dagre", "ELK", and "TALA" (TALA must be installed separately from D2)').addDropdown((dropdown) => { dropdown.addOption(LAYOUT_ENGINES.DAGRE.value, LAYOUT_ENGINES.DAGRE.label).addOption(LAYOUT_ENGINES.ELK.value, LAYOUT_ENGINES.ELK.label).addOption(LAYOUT_ENGINES.TALA.value, LAYOUT_ENGINES.TALA.label).setValue(this.plugin.settings.layoutEngine).onChange(async (value) => { var _a; this.plugin.settings.layoutEngine = value; await this.plugin.saveSettings(); if (value === LAYOUT_ENGINES.TALA.value) { this.addTALASettings(); } else { (_a = this.talaSettings) == null ? void 0 : _a.remove(); } }); }); new import_obsidian.Setting(containerEl).setName("Theme ID").setDesc("Available themes are located at https://github.com/terrastruct/d2/tree/master/d2themes").addText((text) => text.setPlaceholder("Enter a theme ID").setValue(String(this.plugin.settings.theme)).onChange(async (value) => { if (!isNaN(Number(value)) || value === "") { this.plugin.settings.theme = Number(value || DEFAULT_SETTINGS.theme); await this.plugin.saveSettings(); } else { new import_obsidian.Notice("Please specify a valid number"); } })); new import_obsidian.Setting(containerEl).setName("Pad").setDesc("Pixels padded around the rendered diagram").addText((text) => text.setPlaceholder(String(DEFAULT_SETTINGS.pad)).setValue(String(this.plugin.settings.pad)).onChange(async (value) => { if (isNaN(Number(value))) { new import_obsidian.Notice("Please specify a valid number"); this.plugin.settings.pad = Number(DEFAULT_SETTINGS.pad); } else if (value === "") { this.plugin.settings.pad = Number(DEFAULT_SETTINGS.pad); } else { this.plugin.settings.pad = Number(value); } await this.plugin.saveSettings(); })); new import_obsidian.Setting(containerEl).setName("Sketch mode").setDesc("Render the diagram to look like it was sketched by hand").addToggle((toggle) => toggle.setValue(this.plugin.settings.sketch).onChange(async (value) => { this.plugin.settings.sketch = value; await this.plugin.saveSettings(); })); new import_obsidian.Setting(containerEl).setName("Container height").setDesc("Diagram max render height in pixels (Requires d2 v0.2.2 and up)").addText((text) => text.setPlaceholder(String(DEFAULT_SETTINGS.containerHeight)).setValue(String(this.plugin.settings.containerHeight)).onChange(async (value) => { if (isNaN(Number(value))) { new import_obsidian.Notice("Please specify a valid number"); this.plugin.settings.containerHeight = Number(DEFAULT_SETTINGS.containerHeight); } else if (value === "") { this.plugin.settings.containerHeight = Number(DEFAULT_SETTINGS.containerHeight); } else { this.plugin.settings.containerHeight = Number(value); } await this.plugin.saveSettings(); })); new import_obsidian.Setting(containerEl).setName("Debounce").setDesc("How often should the diagram refresh in milliseconds (min 100)").addText((text) => text.setPlaceholder(String(DEFAULT_SETTINGS.debounce)).setValue(String(this.plugin.settings.debounce)).onChange(async (value) => { if (isNaN(Number(value))) { new import_obsidian.Notice("Please specify a valid number"); this.plugin.settings.debounce = Number(DEFAULT_SETTINGS.debounce); } else if (value === "") { this.plugin.settings.debounce = Number(DEFAULT_SETTINGS.debounce); } else if (Number(value) < 100) { new import_obsidian.Notice("The value must be greater than 100"); this.plugin.settings.debounce = Number(DEFAULT_SETTINGS.debounce); } else { this.plugin.settings.debounce = Number(value); } await this.plugin.saveSettings(); })); new import_obsidian.Setting(containerEl).setName("Path (optional)").setDesc("Customize the local path to the directory `d2` is installed in (ex. if d2 is located at `/usr/local/bin/d2`, then the path is `/usr/local/bin`). This is only necessary if `d2` is not found automatically by the plugin (but is installed).").addText((text) => { text.setPlaceholder("/usr/local/Cellar").setValue(this.plugin.settings.d2Path).onChange(async (value) => { this.plugin.settings.d2Path = value; await this.plugin.saveSettings(); }); }); if (this.plugin.settings.layoutEngine === LAYOUT_ENGINES.TALA.value) { this.addTALASettings(); } } }; // src/processor.ts var import_obsidian2 = require("obsidian"); var import_child_process = require("child_process"); var import_path = require("path"); var import_lodash = __toESM(require_lodash()); var import_os = __toESM(require("os")); var D2Processor = class { constructor(plugin) { this.attemptExport = async (source, el, ctx) => { var _a; el.createEl("h6", { text: "Generating D2 diagram...", cls: "D2__Loading" }); const pageContainer = ctx.containerEl; let pageID = pageContainer.dataset.pageID; if (!pageID) { pageID = Math.floor(Math.random() * Date.now()).toString(); pageContainer.dataset.pageID = pageID; } let debouncedFunc = this.debouncedMap.get(pageID); if (!debouncedFunc) { await this.export(source, el, ctx); debouncedFunc = (0, import_lodash.default)(this.export, this.plugin.settings.debounce, { leading: true }); this.debouncedMap.set(pageID, debouncedFunc); return; } (_a = this.abortControllerMap.get(pageID)) == null ? void 0 : _a.abort(); const newAbortController = new AbortController(); this.abortControllerMap.set(pageID, newAbortController); await debouncedFunc(source, el, ctx, newAbortController.signal); }; this.isValidUrl = (urlString) => { let url; try { url = new URL(urlString); } catch (e) { return false; } return url.protocol === "http:" || url.protocol === "https:"; }; this.formatLinks = (svgEl) => { const links = svgEl.querySelectorAll("a"); links.forEach((link) => { var _a; const href = (_a = link.getAttribute("href")) != null ? _a : ""; if (!this.isValidUrl(href)) { link.classList.add("internal-link"); link.setAttribute("data-href", href); link.setAttribute("target", "_blank"); link.setAttribute("rel", "noopener"); } }); }; this.sanitizeSVGIDs = (svgEl, docID) => { const overrides = svgEl.querySelectorAll("marker, mask, filter"); const overrideIDs = []; overrides.forEach((override) => { const id = override.getAttribute("id"); if (id) { overrideIDs.push(id); } }); return overrideIDs.reduce((svgHTML, overrideID) => { return svgHTML.replaceAll(overrideID, [overrideID, docID].join("-")); }, svgEl.outerHTML); }; this.export = async (source, el, ctx, signal) => { try { const image = await this.generatePreview(source, signal); if (image) { el.empty(); this.prevImage = image; this.insertImage(image, el, ctx); const button = new import_obsidian2.ButtonComponent(el).setClass("Preview__Recompile").setIcon("recompile").onClick((e) => { e.preventDefault(); e.stopPropagation(); el.empty(); this.attemptExport(source, el, ctx); }); button.buttonEl.createEl("span", { text: "Recompile" }); } } catch (err) { el.empty(); const errorEl = el.createEl("pre", { cls: "markdown-rendered pre Preview__Error" }); errorEl.createEl("code", { text: "D2 Compilation Error:", cls: "Preview__Error--Title" }); errorEl.createEl("code", { text: err.message }); if (this.prevImage) { this.insertImage(this.prevImage, el, ctx); } } finally { const pageContainer = ctx.containerEl; this.abortControllerMap.delete(pageContainer.dataset.id); } }; this.plugin = plugin; this.debouncedMap = /* @__PURE__ */ new Map(); this.abortControllerMap = /* @__PURE__ */ new Map(); } insertImage(image, el, ctx) { const parser = new DOMParser(); const svg = parser.parseFromString(image, "image/svg+xml"); const containerEl = el.createDiv(); const svgEl = svg.documentElement; svgEl.style.maxHeight = `${this.plugin.settings.containerHeight}px`; svgEl.style.maxWidth = "100%"; svgEl.style.height = "fit-content"; svgEl.style.width = "fit-content"; this.formatLinks(svgEl); containerEl.innerHTML = this.sanitizeSVGIDs(svgEl, ctx.docId); } async generatePreview(source, signal) { var _a, _b; const pathArray = [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin"]; if (import_os.default.platform() === "win32") { pathArray.push(`C:Program FilesD2`); } else { pathArray.push(`${process.env.HOME}/.local/bin`); } let GOPATH = ""; try { GOPATH = (0, import_child_process.execSync)("go env GOPATH", { env: { ...process.env, PATH: pathArray.join(import_path.delimiter) } }).toString(); } catch (error) { } if (GOPATH) { pathArray.push(`${GOPATH.replace("\n", "")}/bin`); } if (this.plugin.settings.d2Path) { pathArray.push(this.plugin.settings.d2Path); } const options = { ...process.env, env: { PATH: pathArray.join(import_path.delimiter) }, signal }; if (this.plugin.settings.apiToken) { options.env.TSTRUCT_TOKEN = this.plugin.settings.apiToken; } let args = [ `d2`, "-", `--theme=${this.plugin.settings.theme}`, `--layout=${this.plugin.settings.layoutEngine}`, `--pad=${this.plugin.settings.pad}`, `--sketch=${this.plugin.settings.sketch}`, "--bundle=false", "--scale=1" ]; const cmd = args.join(" "); const child = (0, import_child_process.exec)(cmd, options); (_a = child.stdin) == null ? void 0 : _a.write(source); (_b = child.stdin) == null ? void 0 : _b.end(); let stdout; let stderr; if (child.stdout) { child.stdout.on("data", (data) => { if (stdout === void 0) { stdout = data; } else { stdout += data; } }); } if (child.stderr) { child.stderr.on("data", (data) => { if (stderr === void 0) { stderr = data; } else { stderr += data; } }); } return new Promise((resolve, reject) => { child.on("error", reject); child.on("close", (code) => { if (code === 0) { resolve(stdout); return; } else if (stderr) { console.error(stderr); reject(new Error(stderr)); } else if (stdout) { console.error(stdout); reject(new Error(stdout)); } }); }); } }; // src/main.ts var D2Plugin = class extends import_obsidian3.Plugin { async onload() { (0, import_obsidian3.addIcon)("recompile", RecompileIcon); await this.loadSettings(); this.addSettingTab(new D2SettingsTab(this.app, this)); const processor = new D2Processor(this); this.registerMarkdownCodeBlockProcessor("d2", processor.attemptExport); this.processor = processor; } onunload() { const abortControllers = this.processor.abortControllerMap.values(); Array.from(abortControllers).forEach((controller) => { controller.abort(); }); } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings() { await this.saveData(this.settings); } };