/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ 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: () => AdvancedCanvasPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian10 = require("obsidian"); // node_modules/monkey-around/mjs/index.js function around(obj, factories) { const removers = Object.keys(factories).map((key) => around1(obj, key, factories[key])); return removers.length === 1 ? removers[0] : function() { removers.forEach((r) => r()); }; } function around1(obj, method, createWrapper) { const original = obj[method], hadOwn = obj.hasOwnProperty(method); let current = createWrapper(original); if (original) Object.setPrototypeOf(current, original); Object.setPrototypeOf(wrapper, current); obj[method] = wrapper; return remove; function wrapper(...args) { if (current === original && obj[method] === wrapper) remove(); return current.apply(this, args); } function remove() { if (obj[method] === wrapper) { if (hadOwn) obj[method] = original; else delete obj[method]; } if (current === original) return; current = original; Object.setPrototypeOf(wrapper, original || Function); } } // src/utils/patch-helper.ts var PatchHelper = class { static tryPatchWorkspacePrototype(plugin, getTarget, functions) { return new Promise((resolve) => { const tryPatch = () => { const target = getTarget(); if (!target) return null; const uninstaller = around(target.constructor.prototype, functions); plugin.register(uninstaller); return target; }; const result = tryPatch(); if (result) { resolve(result); return; } const listener = plugin.app.workspace.on("layout-change", () => { const result2 = tryPatch(); if (result2) { plugin.app.workspace.offref(listener); resolve(result2); } }); plugin.registerEvent(listener); }); } static patchObjectPrototype(plugin, target, functions) { const uninstaller = around(target.constructor.prototype, functions); plugin.register(uninstaller); } static patchObjectInstance(plugin, target, functions) { const uninstaller = around(target, functions); plugin.register(uninstaller); } }; // src/core/events.ts var CANVAS_EVENT_PREFIX = "canvas"; var PLUGIN_EVENT_PREFIX = "advanced-canvas"; var CanvasEvent = { // Built-in events SelectionContextMenu: `${CANVAS_EVENT_PREFIX}:selection-menu`, NodeContextMenu: `${CANVAS_EVENT_PREFIX}:node-menu`, EdgeContextMenu: `${CANVAS_EVENT_PREFIX}:edge-menu`, NodeConnectionDropContextMenu: `${CANVAS_EVENT_PREFIX}:node-connection-drop-menu`, // Custom events CanvasChanged: `${PLUGIN_EVENT_PREFIX}:canvas-changed`, ViewportChanged: { Before: `${PLUGIN_EVENT_PREFIX}:viewport-changed:before`, After: `${PLUGIN_EVENT_PREFIX}:viewport-changed:after` }, NodeMoved: `${PLUGIN_EVENT_PREFIX}:node-moved`, DoubleClick: `${PLUGIN_EVENT_PREFIX}:double-click`, DraggingStateChanged: `${PLUGIN_EVENT_PREFIX}:dragging-state-changed`, NodeCreated: `${PLUGIN_EVENT_PREFIX}:node-created`, EdgeCreated: `${PLUGIN_EVENT_PREFIX}:edge-created`, NodeAdded: `${PLUGIN_EVENT_PREFIX}:node-added`, EdgeAdded: `${PLUGIN_EVENT_PREFIX}:edge-added`, NodeChanged: `${PLUGIN_EVENT_PREFIX}:node-changed`, EdgeChanged: `${PLUGIN_EVENT_PREFIX}:edge-changed`, NodeTextContentChanged: `${PLUGIN_EVENT_PREFIX}:node-text-content-changed`, NodeRemoved: `${PLUGIN_EVENT_PREFIX}:node-removed`, EdgeRemoved: `${PLUGIN_EVENT_PREFIX}:edge-removed`, OnCopy: `${PLUGIN_EVENT_PREFIX}:copy`, NodeBBoxRequested: `${PLUGIN_EVENT_PREFIX}:node-bbox-requested`, EdgeCenterRequested: `${PLUGIN_EVENT_PREFIX}:edge-center-requested`, ContainingNodesRequested: `${PLUGIN_EVENT_PREFIX}:containing-nodes-requested`, SelectionChanged: `${PLUGIN_EVENT_PREFIX}:selection-changed`, ZoomToBbox: { Before: `${PLUGIN_EVENT_PREFIX}:zoom-to-bbox:before`, After: `${PLUGIN_EVENT_PREFIX}:zoom-to-bbox:after` }, PopupMenuCreated: `${PLUGIN_EVENT_PREFIX}:popup-menu-created`, NodeInteraction: `${PLUGIN_EVENT_PREFIX}:node-interaction`, Undo: `${PLUGIN_EVENT_PREFIX}:undo`, Redo: `${PLUGIN_EVENT_PREFIX}:redo`, ReadonlyChanged: `${PLUGIN_EVENT_PREFIX}:readonly-changed`, DataRequested: `${PLUGIN_EVENT_PREFIX}:data-requested`, LoadData: `${PLUGIN_EVENT_PREFIX}:load-data`, CanvasSaved: { Before: `${PLUGIN_EVENT_PREFIX}:canvas-saved:before`, After: `${PLUGIN_EVENT_PREFIX}:canvas-saved:after` } }; // src/core/canvas-patcher.ts var import_obsidian = require("obsidian"); var import_view = require("@codemirror/view"); // node_modules/tiny-jsonc/dist/index.js var stringOrCommentRe = /("(?:\\?[^])*?")|(\/\/.*)|(\/\*[^]*?\*\/)/g; var stringOrTrailingCommaRe = /("(?:\\?[^])*?")|(,\s*)(?=]|})/g; var JSONC = { parse: (text) => { text = String(text); try { return JSON.parse(text); } catch (e) { return JSON.parse(text.replace(stringOrCommentRe, "$1").replace(stringOrTrailingCommaRe, "$1")); } } }; var dist_default = JSONC; // src/core/canvas-patcher.ts var CanvasPatcher = class { constructor(plugin) { this.plugin = plugin; this.applyPatches(); } async applyPatches() { const that = this; await new Promise((resolve) => this.plugin.app.workspace.onLayoutReady(() => resolve())); const getCanvasView = async () => { var _a; const canvasLeaf = (_a = this.plugin.app.workspace.getLeavesOfType("canvas")) == null ? void 0 : _a.first(); if (!canvasLeaf) return null; if ((0, import_obsidian.requireApiVersion)("1.7.2")) await canvasLeaf.loadIfDeferred(); return canvasLeaf.view; }; let canvasView = await getCanvasView(); canvasView != null ? canvasView : canvasView = await new Promise((resolve) => { const event = this.plugin.app.workspace.on("layout-change", async () => { const newCanvasView = await getCanvasView(); if (!newCanvasView) return; resolve(newCanvasView); this.plugin.app.workspace.offref(event); }); this.plugin.registerEvent(event); }); console.log("Patching canvas view:", canvasView); PatchHelper.patchObjectPrototype(this.plugin, canvasView, { getViewData: (_next) => function(..._args) { var _a; const canvasData = this.canvas.getData(); canvasData.metadata = (_a = this.canvas.metadata) != null ? _a : {}; return JSON.stringify(canvasData, null, 2); }, setViewData: (next) => function(json, ...args) { let validJson = json !== "" ? json : "{}"; let parsedJson; try { parsedJson = JSON.parse(validJson); } catch (e) { that.plugin.createFileSnapshot(this.file.path, json); parsedJson = dist_default.parse(validJson); validJson = JSON.stringify(parsedJson, null, 2); } const result = next.call(this, validJson, ...args); try { this.canvas.metadata = parsedJson.metadata; } catch (_e) { this.canvas.metadata = {}; } that.triggerWorkspaceEvent(CanvasEvent.CanvasChanged, this.canvas); return result; } }); PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas, { markViewportChanged: (next) => function(...args) { that.triggerWorkspaceEvent(CanvasEvent.ViewportChanged.Before, this); const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.ViewportChanged.After, this); return result; }, markMoved: (next) => function(node) { const result = next.call(this, node); that.triggerWorkspaceEvent(CanvasEvent.NodeMoved, this, node); return result; }, onDoubleClick: (next) => function(event) { const preventDefault = { value: false }; that.triggerWorkspaceEvent(CanvasEvent.DoubleClick, this, event, preventDefault); if (!preventDefault.value) next.call(this, event); }, setDragging: (next) => function(dragging) { const result = next.call(this, dragging); that.triggerWorkspaceEvent(CanvasEvent.DraggingStateChanged, this, dragging); return result; }, getContainingNodes: (next) => function(bbox) { const result = next.call(this, bbox); that.triggerWorkspaceEvent(CanvasEvent.ContainingNodesRequested, this, bbox, result); return result; }, updateSelection: (next) => function(update) { const oldSelection = new Set(this.selection); const result = next.call(this, update); that.triggerWorkspaceEvent(CanvasEvent.SelectionChanged, this, oldSelection, (update2) => next.call(this, update2)); return result; }, createTextNode: (next) => function(...args) { const node = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node); return node; }, createFileNode: (next) => function(...args) { const node = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node); return node; }, createFileNodes: (next) => function(...args) { const nodes = next.call(this, ...args); nodes.forEach((node) => that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node)); return nodes; }, createGroupNode: (next) => function(...args) { const node = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node); return node; }, createLinkNode: (next) => function(...args) { const node = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node); return node; }, addNode: (next) => function(node) { that.patchNode(node); return next.call(this, node); }, addEdge: (next) => function(edge) { that.patchEdge(edge); if (!this.viewportChanged) that.triggerWorkspaceEvent(CanvasEvent.EdgeCreated, this, edge); return next.call(this, edge); }, removeNode: (next) => function(node) { const result = next.call(this, node); that.triggerWorkspaceEvent(CanvasEvent.NodeRemoved, this, node); return result; }, removeEdge: (next) => function(edge) { const result = next.call(this, edge); that.triggerWorkspaceEvent(CanvasEvent.EdgeRemoved, this, edge); return result; }, handleCopy: (next) => function(...args) { this.isCopying = true; const result = next.call(this, ...args); this.isCopying = false; return result; }, getSelectionData: (next) => function(...args) { const result = next.call(this, ...args); if (this.isCopying) that.triggerWorkspaceEvent(CanvasEvent.OnCopy, this, result); return result; }, zoomToBbox: (next) => function(bbox) { that.triggerWorkspaceEvent(CanvasEvent.ZoomToBbox.Before, this, bbox); const result = next.call(this, bbox); that.triggerWorkspaceEvent(CanvasEvent.ZoomToBbox.After, this, bbox); return result; }, setReadonly: (next) => function(readonly) { const result = next.call(this, readonly); that.triggerWorkspaceEvent(CanvasEvent.ReadonlyChanged, this, readonly); return result; }, undo: (next) => function(...args) { const result = next.call(this, ...args); this.importData(this.getData(), true); that.triggerWorkspaceEvent(CanvasEvent.Undo, this); return result; }, redo: (next) => function(...args) { const result = next.call(this, ...args); this.importData(this.getData(), true); that.triggerWorkspaceEvent(CanvasEvent.Redo, this); return result; }, /*setData: (next: any) => function (...args: any) { // const result = next.call(this, ...args) // return result },*/ getData: (next) => function(...args) { const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.DataRequested, this, result); return result; }, importData: (next) => function(data, clearCanvas, silent) { const targetFilePath = this.view.file.path; const setData = (data2) => { if (!this.view.file || this.view.file.path !== targetFilePath) return; this.importData(data2, true, true); that.emitEventsForUnknownDataChanges(this); }; if (!silent) that.triggerWorkspaceEvent(CanvasEvent.LoadData, this, data, setData); const result = next.call(this, data, clearCanvas); that.emitEventsForUnknownDataChanges(this); return result; }, requestSave: (next) => function(...args) { that.triggerWorkspaceEvent(CanvasEvent.CanvasSaved.Before, this); const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.CanvasSaved.After, this); return result; } }); PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas.menu, { render: (next) => function(...args) { const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.PopupMenuCreated, this.canvas); next.call(this); return result; } }); PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas.nodeInteractionLayer, { setTarget: (next) => function(node) { const result = next.call(this, node); that.triggerWorkspaceEvent(CanvasEvent.NodeInteraction, this.canvas, node); return result; } }); this.plugin.registerEditorExtension([import_view.EditorView.updateListener.of((update) => { if (!update.docChanged) return; const editor = update.state.field(import_obsidian.editorInfoField); const node = editor.node; if (!node) return; that.triggerWorkspaceEvent(CanvasEvent.NodeTextContentChanged, node.canvas, node, update); })]); this.plugin.app.workspace.iterateAllLeaves((leaf) => { if (leaf.view.getViewType() !== "canvas") return; const canvasView2 = leaf.view; canvasView2.leaf.rebuildView(); }); } patchNode(node) { const that = this; PatchHelper.patchObjectInstance(this.plugin, node, { setData: (next) => function(data, addHistory) { const result = next.call(this, data); if (node.initialized && !node.isDirty) { node.isDirty = true; that.triggerWorkspaceEvent(CanvasEvent.NodeChanged, this.canvas, node); delete node.isDirty; } this.canvas.data = this.canvas.getData(); this.canvas.view.requestSave(); if (addHistory) this.canvas.pushHistory(this.canvas.getData()); return result; }, getBBox: (next) => function(...args) { const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.NodeBBoxRequested, this.canvas, node, result); return result; } }); this.runAfterInitialized(node, () => { this.triggerWorkspaceEvent(CanvasEvent.NodeAdded, node.canvas, node); this.triggerWorkspaceEvent(CanvasEvent.NodeChanged, node.canvas, node); }); } patchEdge(edge) { const that = this; PatchHelper.patchObjectInstance(this.plugin, edge, { setData: (next) => function(data, addHistory) { const result = next.call(this, data); if (edge.initialized && !edge.isDirty) { edge.isDirty = true; that.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, this.canvas, edge); delete edge.isDirty; } this.canvas.data = this.canvas.getData(); this.canvas.view.requestSave(); if (addHistory) this.canvas.pushHistory(this.canvas.getData()); return result; }, render: (next) => function(...args) { const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, this.canvas, edge); return result; }, getCenter: (next) => function(...args) { const result = next.call(this, ...args); that.triggerWorkspaceEvent(CanvasEvent.EdgeCenterRequested, this.canvas, edge, result); return result; } }); this.runAfterInitialized(edge, () => { this.triggerWorkspaceEvent(CanvasEvent.EdgeAdded, edge.canvas, edge); }); } runAfterInitialized(canvasElement, onReady) { if (canvasElement.initialized) { onReady(); return; } const that = this; const uninstall = around(canvasElement, { initialize: (next) => function(...args) { const result = next.call(this, ...args); onReady(); uninstall(); return result; } }); that.plugin.register(uninstall); } emitEventsForUnknownDataChanges(canvas) { canvas.nodes.forEach((node) => this.runAfterInitialized(node, () => { this.triggerWorkspaceEvent(CanvasEvent.NodeChanged, node.canvas, node); })); canvas.edges.forEach((edge) => this.runAfterInitialized(edge, () => { this.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, edge.canvas, edge); })); } triggerWorkspaceEvent(event, ...args) { this.plugin.app.workspace.trigger(event, ...args); } }; // src/utils/icons-helper.ts var import_obsidian2 = require("obsidian"); var CUSTOM_ICONS = { "shape-pill": ``, "shape-parallelogram": ``, "shape-predefined-process": ` `, "shape-document": ``, "shape-database": ` `, "border-solid": ``, "border-dashed": ``, "border-dotted": ``, "path-solid": ``, "path-dotted": ``, "path-short-dashed": ``, "path-long-dashed": ``, "arrow-triangle": ``, "arrow-triangle-outline": ``, "arrow-thin-triangle": ``, "arrow-halved-triangle": ``, "arrow-diamond": ``, "arrow-diamond-outline": ``, "arrow-circle": ``, "arrow-circle-outline": ``, "pathfinding-method-bezier": ``, "pathfinding-method-square": `` }; var IconsHelper = class { static addIcons() { for (const [id, svg] of Object.entries(CUSTOM_ICONS)) { (0, import_obsidian2.addIcon)(id, svg); } } }; // src/utils/debug-helper.ts var DebugHelper = class { constructor(plugin) { this.plugin = plugin; this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeAdded, (_canvas, _node) => console.count("\u{1F7E2} NodeAdded") )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeChanged, (_canvas, _node) => console.count("\u{1F7E1} NodeChanged") )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeAdded, (_canvas, _edge) => console.count("\u{1F7E2} EdgeAdded") )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeChanged, (_canvas, _edge) => console.count("\u{1F7E1} EdgeChanged") )); } }; // src/settings.ts var import_obsidian3 = require("obsidian"); // src/canvas-extensions/advanced-styles/style-config.ts var BUILTIN_NODE_STYLE_ATTRIBUTES = [ { datasetKey: "textAlign", label: "Text Alignment", nodeTypes: ["text"], options: [ { icon: "align-left", label: "Left (default)", value: null }, { icon: "align-center", label: "Center", value: "center" }, { icon: "align-right", label: "Right", value: "right" } ] }, { datasetKey: "shape", label: "Shape", nodeTypes: ["text"], options: [ { icon: "rectangle-horizontal", label: "Round Rectangle (default)", value: null }, { icon: "shape-pill", label: "Pill", value: "pill" }, { icon: "diamond", label: "Diamond", value: "diamond" }, { icon: "shape-parallelogram", label: "Parallelogram", value: "parallelogram" }, { icon: "circle", label: "Circle", value: "circle" }, { icon: "shape-predefined-process", label: "Predefined Process", value: "predefined-process" }, { icon: "shape-document", label: "Document", value: "document" }, { icon: "shape-database", label: "Database", value: "database" } ] }, { datasetKey: "border", label: "Border", options: [ { icon: "border-solid", label: "Solid (default)", value: null }, { icon: "border-dashed", label: "Dashed", value: "dashed" }, { icon: "border-dotted", label: "Dotted", value: "dotted" }, { icon: "eye-off", label: "Invisible", value: "invisible" } ] } ]; var BUILTIN_EDGE_STYLE_ATTRIBUTES = [ { datasetKey: "path", label: "Path Style", options: [ { icon: "path-solid", label: "Solid (default)", value: null }, { icon: "path-dotted", label: "Dotted", value: "dotted" }, { icon: "path-short-dashed", label: "Short Dashed", value: "short-dashed" }, { icon: "path-long-dashed", label: "Long Dashed", value: "long-dashed" } ] }, { datasetKey: "arrow", label: "Arrow Style", options: [ { icon: "arrow-triangle", label: "Triangle (default)", value: null }, { icon: "arrow-triangle-outline", label: "Triangle Outline", value: "triangle-outline" }, { icon: "arrow-thin-triangle", label: "Thin Triangle", value: "thin-triangle" }, { icon: "arrow-halved-triangle", label: "Halved Triangle", value: "halved-triangle" }, { icon: "arrow-diamond", label: "Diamond", value: "diamond" }, { icon: "arrow-diamond-outline", label: "Diamond Outline", value: "diamond-outline" }, { icon: "arrow-circle", label: "Circle", value: "circle" }, { icon: "arrow-circle-outline", label: "Circle Outline", value: "circle-outline" } ] }, { datasetKey: "pathfindingMethod", label: "Pathfinding Method", options: [ { icon: "pathfinding-method-bezier", label: "Bezier (default)", value: null }, { icon: "slash", label: "Direct", value: "direct" }, { icon: "pathfinding-method-square", label: "Square", value: "square" }, { icon: "map", label: "A*", value: "a-star" } ] } ]; // src/settings.ts var NODE_TYPES_ON_DOUBLE_CLICK = { "text": "Text", "file": "File" }; var EDGE_LINE_DIRECTIONS = { "nondirectional": "Nondirectional", "unidirectional": "Unidirectional", "bidirectional": "Bidirectional" }; var DEFAULT_SETTINGS = { nodeTypeOnDoubleClick: "text", alignNewNodesToGrid: true, defaultTextNodeWidth: 260, defaultTextNodeHeight: 60, defaultFileNodeWidth: 400, defaultFileNodeHeight: 400, minNodeSize: 60, disableFontSizeRelativeToZoom: false, combineCustomStylesInDropdown: false, nodeStylingFeatureEnabled: true, customNodeStyleAttributes: [], defaultTextNodeStyleAttributes: {}, edgesStylingFeatureEnabled: true, customEdgeStyleAttributes: [], defaultEdgeLineDirection: "unidirectional", defaultEdgeStyleAttributes: {}, edgeStyleDirectRotateArrow: false, edgeStylePathfinderGridResolution: 10, edgeStylePathfinderPathLiveUpdate: true, edgeStylePathfinderPathRounded: true, commandsFeatureEnabled: true, zoomToClonedNode: true, cloneNodeMargin: 20, expandNodeStepSize: 20, betterReadonlyEnabled: true, disableNodePopup: false, disableZoom: false, disablePan: false, autoResizeNodeFeatureEnabled: true, autoResizeNodeSnapToGrid: true, collapsibleGroupsFeatureEnabled: true, collapsedGroupPreviewOnDrag: true, focusModeFeatureEnabled: true, presentationFeatureEnabled: true, showSetStartNodeInPopup: false, defaultSlideSize: "1200x675", wrapInSlidePadding: 20, resetViewportOnPresentationEnd: true, useArrowKeysToChangeSlides: true, usePgUpPgDownKeysToChangeSlides: true, zoomToSlideWithoutPadding: true, slideTransitionAnimationDuration: 0.5, slideTransitionAnimationIntensity: 1.25, canvasEncapsulationEnabled: true, portalsFeatureEnabled: true, maintainClosedPortalSize: true, showEdgesIntoDisabledPortals: true }; var _SettingsManager = class _SettingsManager { constructor(plugin) { this.plugin = plugin; } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.plugin.loadData()); this.plugin.app.workspace.trigger(_SettingsManager.SETTINGS_CHANGED_EVENT); } async saveSettings() { await this.plugin.saveData(this.settings); } getSetting(key) { return this.settings[key]; } async setSetting(data) { this.settings = Object.assign(this.settings, data); await this.saveSettings(); this.plugin.app.workspace.trigger(_SettingsManager.SETTINGS_CHANGED_EVENT); } addSettingsTab() { this.settingsTab = new AdvancedCanvasPluginSettingTab(this.plugin, this); this.plugin.addSettingTab(this.settingsTab); } }; _SettingsManager.SETTINGS_CHANGED_EVENT = "advanced-canvas:settings-changed"; var SettingsManager = _SettingsManager; var AdvancedCanvasPluginSettingTab = class extends import_obsidian3.PluginSettingTab { constructor(plugin, settingsManager) { super(plugin.app, plugin); this.settingsManager = settingsManager; } display() { let { containerEl } = this; containerEl.empty(); new import_obsidian3.Setting(containerEl).setHeading().setClass("ac-settings-heading").setName("General"); new import_obsidian3.Setting(containerEl).setName("Node type on double click").setDesc("The type of node that will be created when double clicking on the canvas.").addDropdown( (dropdown) => dropdown.addOptions(NODE_TYPES_ON_DOUBLE_CLICK).setValue(this.settingsManager.getSetting("nodeTypeOnDoubleClick")).onChange(async (value) => await this.settingsManager.setSetting({ nodeTypeOnDoubleClick: value })) ); new import_obsidian3.Setting(containerEl).setName("Always align new nodes to grid").setDesc("When enabled, new nodes will be aligned to the grid.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("alignNewNodesToGrid")).onChange(async (value) => await this.settingsManager.setSetting({ alignNewNodesToGrid: value })) ); new import_obsidian3.Setting(containerEl).setName("Default text node width").setDesc("The default width of a text node.").addText( (text) => text.setValue(this.settingsManager.getSetting("defaultTextNodeWidth").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultTextNodeWidth: Math.max(1, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Default text node height").setDesc("The default height of a text node.").addText( (text) => text.setValue(this.settingsManager.getSetting("defaultTextNodeHeight").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultTextNodeHeight: Math.max(1, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Default file node width").setDesc("The default width of a file node.").addText( (text) => text.setValue(this.settingsManager.getSetting("defaultFileNodeWidth").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultFileNodeWidth: Math.max(1, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Default file node height").setDesc("The default height of a file node.").addText( (text) => text.setValue(this.settingsManager.getSetting("defaultFileNodeHeight").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultFileNodeHeight: Math.max(1, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Minimum node size").setDesc("The minimum size of a node.").addText( (text) => text.setValue(this.settingsManager.getSetting("minNodeSize").toString()).onChange(async (value) => await this.settingsManager.setSetting({ minNodeSize: Math.max(1, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Disable font size relative to zoom").setDesc("When enabled, the font size of e.g. group node titles and edge labels will not increase when zooming out.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("disableFontSizeRelativeToZoom")).onChange(async (value) => await this.settingsManager.setSetting({ disableFontSizeRelativeToZoom: value })) ); this.createFeatureHeading( containerEl, "Combine custom styles", "Combine all style attributes of Advanced Canvas in a single dropdown.", "combineCustomStylesInDropdown" ); this.createFeatureHeading( containerEl, "Node styling", "Style your nodes with different shapes and borders.", "nodeStylingFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Custom node style settings").setDesc("Add custom style settings for nodes. (Go to GitHub for more information)").addButton( (button) => button.setButtonText("Open Tutorial").onClick(() => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles")) ); const allNodeStyleAttributes = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.settingsManager.getSetting("customNodeStyleAttributes")].filter((setting) => { var _a; return setting.nodeTypes === void 0 || ((_a = setting.nodeTypes) == null ? void 0 : _a.includes("text")); }); this.createDefaultStylesSection(containerEl, "Default text node style attributes", "defaultTextNodeStyleAttributes", allNodeStyleAttributes); this.createFeatureHeading( containerEl, "Edges styling", "Style your edges with different path styles.", "edgesStylingFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Custom edge style settings").setDesc("Add custom style settings for edges. (Go to GitHub for more information)").addButton( (button) => button.setButtonText("Open Tutorial").onClick(() => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles")) ); new import_obsidian3.Setting(containerEl).setName("Default edge line direction").setDesc("The default line direction of an edge.").addDropdown( (dropdown) => dropdown.addOptions(EDGE_LINE_DIRECTIONS).setValue(this.settingsManager.getSetting("defaultEdgeLineDirection")).onChange(async (value) => await this.settingsManager.setSetting({ defaultEdgeLineDirection: value })) ); this.createDefaultStylesSection(containerEl, "Default edge style attributes", "defaultEdgeStyleAttributes", [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.settingsManager.getSetting("customEdgeStyleAttributes")]); new import_obsidian3.Setting(containerEl).setName('Rotate arrow if pathfinding method is "Direct"').setDesc('When enabled, the arrow will be rotated to the direction of the edge if the pathfinding method is set to "Direct".').addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStyleDirectRotateArrow")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStyleDirectRotateArrow: value })) ); new import_obsidian3.Setting(containerEl).setName("A* grid resolution").setDesc("The resolution of the grid when using the A* path style. The lower the value, the more precise the path will be. But it will also take longer to calculate.").addText( (text) => text.setValue(this.settingsManager.getSetting("edgeStylePathfinderGridResolution").toString()).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderGridResolution: Math.max(5, parseInt(value)) })) ); new import_obsidian3.Setting(containerEl).setName("Live update A* path").setDesc("When enabled, the A* path style will be updated live while dragging the edge.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStylePathfinderPathLiveUpdate")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderPathLiveUpdate: value })) ); new import_obsidian3.Setting(containerEl).setName("A* rounded path").setDesc("When enabled, the A* path style will be rounded.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStylePathfinderPathRounded")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderPathRounded: value })) ); this.createFeatureHeading( containerEl, "Extended commands", "Add more commands to the canvas.", "commandsFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Zoom to cloned node").setDesc("When enabled, the canvas will zoom to the cloned node.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("zoomToClonedNode")).onChange(async (value) => await this.settingsManager.setSetting({ zoomToClonedNode: value })) ); new import_obsidian3.Setting(containerEl).setName("Clone node margin").setDesc("The margin between the cloned node and the source node.").addText( (text) => text.setValue(this.settingsManager.getSetting("cloneNodeMargin").toString()).onChange(async (value) => await this.settingsManager.setSetting({ cloneNodeMargin: parseInt(value) })) ); new import_obsidian3.Setting(containerEl).setName("Expand node step size").setDesc("The step size for expanding the node.").addText( (text) => text.setValue(this.settingsManager.getSetting("expandNodeStepSize").toString()).onChange(async (value) => await this.settingsManager.setSetting({ expandNodeStepSize: parseInt(value) })) ); this.createFeatureHeading( containerEl, "Better readonly", "Improve the readonly mode.", "betterReadonlyEnabled" ); this.createFeatureHeading( containerEl, "Auto resize node", "Automatically resize the height of a node to fit the content.", "autoResizeNodeFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Snap to grid").setDesc("When enabled, the height of the node will snap to the grid.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("autoResizeNodeSnapToGrid")).onChange(async (value) => await this.settingsManager.setSetting({ autoResizeNodeSnapToGrid: value })) ); this.createFeatureHeading( containerEl, "Collapsible groups", "Group nodes can be collapsed and expanded to keep the canvas organized.", "collapsibleGroupsFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Collapsed group preview on drag").setDesc("When enabled, a group that is collapsed show its border while dragging a node.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("collapsedGroupPreviewOnDrag")).onChange(async (value) => await this.settingsManager.setSetting({ collapsedGroupPreviewOnDrag: value })) ); this.createFeatureHeading( containerEl, "Focus mode", "Focus on a single node and blur all other nodes.", "focusModeFeatureEnabled" ); this.createFeatureHeading( containerEl, "Presentations", "Create a presentation from your canvas.", "presentationFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName('Show "Set Start Node" in node popup').setDesc("If turned off, you can still set the start node using the corresponding command.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("showSetStartNodeInPopup")).onChange(async (value) => await this.settingsManager.setSetting({ showSetStartNodeInPopup: value })) ); new import_obsidian3.Setting(containerEl).setName("Default slide ratio").setDesc("The default ratio of the slide. For example, 16:9 is 1200x675 and 3:2 is 1350x900.").addText( (text) => text.setValue(this.settingsManager.getSetting("defaultSlideSize")).onChange(async (value) => await this.settingsManager.setSetting({ defaultSlideSize: value.replace(" ", "") })) ); new import_obsidian3.Setting(containerEl).setName("Wrap in slide padding").setDesc("The padding of the slide when wrapping the canvas in a slide.").addText( (text) => text.setValue(this.settingsManager.getSetting("wrapInSlidePadding").toString()).onChange(async (value) => await this.settingsManager.setSetting({ wrapInSlidePadding: parseInt(value) })) ); new import_obsidian3.Setting(containerEl).setName("Reset viewport on presentation end").setDesc("When enabled, the viewport will be reset to the original position after the presentation ends.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("resetViewportOnPresentationEnd")).onChange(async (value) => await this.settingsManager.setSetting({ resetViewportOnPresentationEnd: value })) ); new import_obsidian3.Setting(containerEl).setName("Use arrow keys to change slides").setDesc("When enabled, you can use the arrow keys to change slides in presentation mode.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("useArrowKeysToChangeSlides")).onChange(async (value) => await this.settingsManager.setSetting({ useArrowKeysToChangeSlides: value })) ); new import_obsidian3.Setting(containerEl).setName("Use PgUp/PgDown keys to change slides").setDesc("When enabled, you can use the PgUp/PgDown keys to change slides in presentation mode (Makes the presentation mode compatible with most presentation remotes).").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("usePgUpPgDownKeysToChangeSlides")).onChange(async (value) => await this.settingsManager.setSetting({ usePgUpPgDownKeysToChangeSlides: value })) ); new import_obsidian3.Setting(containerEl).setName("Zoom to slide without padding").setDesc("When enabled, the canvas will zoom to the slide without padding.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("zoomToSlideWithoutPadding")).onChange(async (value) => await this.settingsManager.setSetting({ zoomToSlideWithoutPadding: value })) ); new import_obsidian3.Setting(containerEl).setName("Slide transition animation duration").setDesc("The duration of the slide transition animation in seconds. Set to 0 to disable the animation.").addText( (text) => text.setValue(this.settingsManager.getSetting("slideTransitionAnimationDuration").toString()).onChange(async (value) => await this.settingsManager.setSetting({ slideTransitionAnimationDuration: parseFloat(value) })) ); new import_obsidian3.Setting(containerEl).setName("Slide transition animation intensity").setDesc("The intensity of the slide transition animation. The higher the value, the more the canvas will zoom out before zooming in on the next slide.").addText( (text) => text.setValue(this.settingsManager.getSetting("slideTransitionAnimationIntensity").toString()).onChange(async (value) => await this.settingsManager.setSetting({ slideTransitionAnimationIntensity: parseFloat(value) })) ); this.createFeatureHeading( containerEl, "Canvas encapsulation", "Encapsulate a selection of nodes and edges into a new canvas.", "canvasEncapsulationEnabled" ); this.createFeatureHeading( containerEl, "Portals", "Create portals to other canvases.", "portalsFeatureEnabled" ); new import_obsidian3.Setting(containerEl).setName("Maintain closed portal size").setDesc("When enabled, closing a portal will change the size of the portal to the original, closed size.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("maintainClosedPortalSize")).onChange(async (value) => await this.settingsManager.setSetting({ maintainClosedPortalSize: value })) ); new import_obsidian3.Setting(containerEl).setName("Show edges into disabled portals").setDesc("When enabled, edges into disabled portals will be shown by an edge to the portal node.").addToggle( (toggle) => toggle.setValue(this.settingsManager.getSetting("showEdgesIntoDisabledPortals")).onChange(async (value) => await this.settingsManager.setSetting({ showEdgesIntoDisabledPortals: value })) ); const kofiButton = document.createElement("a"); kofiButton.classList.add("kofi-button"); kofiButton.href = "https://ko-fi.com/X8X27IA08"; kofiButton.target = "_blank"; const kofiImage = document.createElement("img"); kofiImage.src = "data:image/webp;base64,UklGRrosAABXRUJQVlA4TK4sAAAv1wNDEL/CoJEkRXUCbvwrekfM/BYQspGkHsCNw/nbvcAzahtJkue7R/GnubUAykDaNvFv9r2CqU3bgHHKGHIH7H9DeOynEYZHCKFOj1neMfXZ0SmmUzuYgs6P2cH0fjuY11JBq5hO7ejVDqZTnWJ29Op+1twlRYq6rzLHZ6dIkSJFCnjb/mlP41jbjKzG2JjQKAiRUTrz/JCnNasnK3MmnnWm07aORtgyyHpA3/+r2BiOqvpXifW0bRH9h4ZtO9DqlUuZ7LSRz/d9JOv8Ofs/iSZZzKPZdHr9ykynsyheLEGwfD6k6WTvcCZ7h/M/ZfHNZ9ejcOBthqPJLJaMLokmw8DraK6m8fJ/tMJGk5FXbvfL/7NYgjyYXQXEg5nE/zP12uw6GPCaYBQlrD5vRzzHchX9VwTLOJpcj4bhixmOriazeIFImh44snA0mkzni1MR8SQcyJjhZMF1XCPGQwmvk/9qlDKhZ1kyjWFOVvNn0tT7yE5An2AgacIoYQjPflwjQ4IvkyRZxHE8j17MbLpvJtdSZnrARHsmfjHPR7a0rJRBp+liKvEYXp9yHslzZpc31zF1TeYkpfTksYijaPZyuhi9EKPBQJV5Ia1HL6ecaB7Hiigl8fQSXC/gi7HwBKkPitLlWPl/FsgdiZ6TSBw9VyqvhuHAGBM+n12ms7neU0t8hU7TLd8O94qWE26FowTHXomHktQH+tstF9Hs+uqZFjDQBKOraRQvDStmwgi+xhlGJ9ka9sryM+kjeYvLV/ZhQtkY3UQNdzoZs38kVwk8cXqdnJhr4l97DJBpwwTxtclwYKZRy52WSZFv4aucYXRarkmnqxlG/pmBfdyzZ22fPjCj2QIZiyH4mT8ZydGMJxEiplwlna6WVygH8hmUz6BHTHg9hwJIITBjKsckP+qr5cmDxet8he2ZAFWchwm0wMH2qgCkx3IEfuafB8IJ8MRYIHhoAtybYxYhCozqjt1Gl77IQjq1DJcce52Uiz8PDTrUIgA7joU4W9m+NWktQyDMA+wz/wzh2x+dMPhMC2kawB3Hol/j1it8mmGTdMkIhMlzsuiqahIt4S2SIuBeNCOMqN9i19XmMCXM7DTB54HlZG4iWZ/vyZUIxwLUvcHJ0yA5VYL10cJTkzyJArwF4tYSydMTIIwVopO027WvzK5LwfD6iLpUnAnLWJM8bd7u8/3DB617x69O6yepF7/AK93V22Ll7o4aty7KZiePtK0eDh9Stt7WLAfzmYjv6bSywDr6zz3ZgEBeJ8ZbLQLW3F64O5rJ1ts2FfSp1pnfwbjHlqGEwPHtN2mbaGGDVPcGr3V+dpLFv3vJ7UxmXXUiaNekQ3GPHZlX02ucSd1agUsW2zVVuS2Ksmw4ypKRTK0z3e0f2basyUeWnBKWK7Nv3R2vWdWdwBrZUFdGnJzJXjdvBTCmlzJPx0qZFZ2mm7ETIGm9XXGWVtenlU2f/Hw48j/vGsCRzHRrB6Tdntm1B0xTs5n2iOn2jSEii7f0CpsATRckrDZ9WvsmwNPn5c8Z8zr0SrplOxBXi3stxCupXde2dV2VZVEUD+v1yjmX3eGa7PmoVuv1+oXuLav6RdwBUbGOmANRM3smk+JGr5hJwil+6/+3Tk8mW++tga/sWKmQh47ihRpH2rV1VRbF2rk7E8zzGebhpXrbdjp4WiEJFe1MmlWUPzg+YMlnK+Ln7/25BydCAxOGNYA89MSAirmkdTtKOmVQrmYXI5bFwzMZYJjJVutt1e5EQkH4dfRyZt0Rjvu5HONak1nik0BeTj5ZtyuMgq2jouQ/kIrg4KhrdfX2WeRgqFk9VNwyzXAB4Fdnogku+hyjjHGpJyanghoMS0kA7llCHUcMYdP6sGaAqUG3TYqnEBZKp5bMn4ShM1dax1UX7MdNQInoE1JJuSVapGXEYvn4yla/1DIK2oT9HtkqKDshmcYj3+fceP7di97HFZGHtgJL6CnBCpna3xG27b2ZRD9Rb6jFiT4JSJZt6STQvP7y5bxm/QixDFY9l2Aqlp0cp2rH78w4wq/uTDV8KoGimiNjipXJ8XyiVgAWz+UJE3v6TAXrWLqjNWiEdLr0xpyF7dsrZl3zLGL7MOf49UFwiVoBjio8XWLYOcwkzlHgQKTTqb/AgXGtP4JvO/FlhFJlq44DjDxtPQuVXseIT3QCHVl9+DBQ3i/0FjjQ2b79ZDiivhahIxv+qlrK+m4onNt5rweC4owLck2Fs3GWcgYecogR+3rlM+pbgFTZHhm1FVYw5OKsz/2wrBxTtsaUxk8FOJMm7IX8VT/R35TuQpQBLV8cOKXKpMcRErCFTt0PHi6iM/S6IBIvZ7KH3q6WUowZUUsbuV0Aa52706KN6FuSxTbtURfTWYpxJvt7pwWv2wknN0yBbu2FixNEHb2EF/scdTGdyIMzyaAd0POeYcIqM3fyao6ACb48KaIa0yy646EKAxjJxEcRhvwx977nkJPvU0uzVjFTwPaUQKQ5f60pMnOcCuOQLDE/fuR96bnjjnzsHaO4LBQywRd+x3Fq7NOqSNjdwW0Ek3K8MLY/fhVt+eY+LZHQsv0a2N7d++HEcDunK1l3GEdRMTCgIoI1XQsdr3IdJMSkCZFUUqIFpBVPC8XV1CkhRL32hiP+IiZB4sOdeQa1ZQBXM50hXn2pItoTEW9Jb6hjE6tS7egaMW855Ii9GkXHJj1fEFzBTSrTFG+jp10YFqu4nDO/u4N94ZplnxKsr9JP+bMp9s2mPGpqX1OVPmZTvDL5nnHPBm1h6xV4DMjezMiykFMwyFv/QOqqlxmKvI7a7HeMJy/P3Kf7YlNWL72Ne/Uujtsl+u6lG/SX802euwx0uWKoNHXCKWVH11wAk5v3s6te1rR6lUlgqA6/1HRE+x4ka7i8KKtm1w2MMOmubaqyyJ3YQm+nMycQkoAvNKApb8iYC1+bZQ/EAuKHQAnuUFASMQbgTGb5pmq6gV1m12zyDCWykE5dSROShfeJHZRSvGAYm+fkehQeLVnD4ctQV+2+243Q30+ROSCjx90xOOQK00SfNvXhfS0Lytxx65pjlwCyxHG4yT3lbW+yiCZXDBeaGV5ZPGD6yAKNrw4fA7Jb89AHRZ0OTDVdfS8vPw+Wvtsw/FVYie6Kr43wMp7wDuMxGM1iUzH0TY13/YREkCbKG2t82Ppbv+eUYdNy4Rg0aXqRXA+4CW/uNTJ6saFwGt9HYt43kKaJsvII1W/t7o6pD1mqE4AKJBtUGWfNoOm4jOl3xNIHN0Mw9t6np+Dt0tfSge2mzXn6kKU5MSG09I6obXM78oVGDvj0m8fS9wNzKrFTECSW+ZDzQyEMD15whPF3/MocmNP/1qvOCfvKBeAIwsIoMS40govDOO2gIxKkibJmYKmR7XymJTkhKkO38JL84Lb8xxzDo23BR55YacOro0XWhxnSYnY94ctxVWUhr4DwfTkArI8BpnFu56/as5yFiMP9Mbbz01Zda6bDlQ5obYjI1+XqJagHd9oSlN0i2LS54mwXIK8EsHw9hMfeXJnZ0FByffBwsbUhRvzl3dPAToPRrejOv53opR82CHDH1VaB976AHphnGNkpNO/UditQAbNrphSaqKbbfvF0IupuC99YHQQ81SpBe7gO0AffZuX2ozo0B6odwNoQlO2cZHvIpPcSu0yPSy+QhCaQnKRC67D6FjZuO3XoXqf2DFgbgrC9I+3RT2Yj4AQzP1QYgGx03ykeS7oxe8vHjEax57CvDCgaAo+kq0P3lCkFnABYD/IwgxgqtFbPmX3KMLk6VIcKH+z+jeeeyGgFTl+tguTGOwwgtllslQ+13Q7MfUNr3D6wibo9ISjD1H0XiJ9t0/KhBQEgI2mlfiScduiky25jkmz3CCIbQsihHx4Y/v5eimyFcN3cDRYqqB76tJuRFZ6hDduNTPHDXbwn/iKHlJIwe810GfOF7oETZybYM4thpUA4NwELG7YdVjp1HfcrDcHtOVPLyxRTrCS3hrsm7AkQKhLdhk0DrbxLeGXiWcXtV5ennrXW9YqD61f6Xvz+NvaH2rpevR7S7l6HmKnFocYm6K4WQoQlpXjnlpq7vnVZGKMtHVDngBUcXdrJfQttVawy0l02VG1q69sO/ZCx+8ER7Dtxdg2hcejACzuC23+mkt7MkTi1DrhoYhyaJ95QOIPYIql5C+sy5twNYIKKCDyMmiAz70HjhCVUGRC9XsIWTWw32rBiPv+VfGPPnBnUo1qlDE9+PWHiJahkLyRrXWdtM0VfFc/10TXjfRfVskT0DqadEZhfJeCTyQy9bWdg9FMDYn30TKwZGhf2+owQcuWdMk2m5w9/WdlDLVnUVyxogHrUWd1pfYv0SfCIV3Vn3XIJ+QrGBS4qsRiJV8G5ZJUS7cTw5Z+//+HHn25ubn784fs/f8WIr755/+OPz3/tTz++/+Z3XwihlBiHaSnKzN7AVP6EqFKGxKJhEL2PHctiTMidrvVUx/ioQ8qH3ELt/dX3Nx9vj5iPP339JQN+9/fjf+6/fvyzwEudJ+dNKpJMUci7tlO6T7uGYDW2hi1LLVJzmfaXj1BHEe2DlSC5F8AXf/9w62l++jPxz/3+xv/P5cajDAOr4HoBmpjcPULtw72mY+WtKSE3epBVxAwCZENSxxUblFd+9/MtxfzyNeUk9JH25/JiJVNHb7mEi9DEJFThVUutiUdj1DJTEFK0f9EN/6tkN2b9kFvuyvxeWU9b8Y3mu701lBXK9yGyudUT0kH58Q6gXNPKTB0onGmNvv47oBzGNa0lrHeGSnnxXNgzmB+8z0IMJyI+SJWfD3KS89KiUXywCSv0jYQz1rknckiM6/HUDmMAZAlNlogQ8st/3d7eipwZ/vbxlsf8nQ1Sd/FRyQV8QAGkH8/YmHV5BokVezZq1pi+/5wtDtJxTGlE0Y5XG8afhucyv/75CP5xu2d4PDkhUOp3cgHtGYbXlstX60g4FSJQqeWWFQcqzTCeqwMHIjA8hlZeUvOPW07z9UH8eHvLuHzFBpkw3jmM4YpJ9vilLHE+gMuJKWZoRFyFvMSkLj6RBpnQpnDXqXM1240JjziJopIPP97ymm8P8N9vblnNr1+LI0v56fiajJ05YAm0c4rZCp/piwtzz3R/ZCVlkX/KDo/U6H5z4K7v+FdIKSHfS/k0PyIqb1ENrxVZ/Zn/72WBlLOROqE2WiHa8q3O4MlfWlr93IS+Eh3WKTsFua/itM59v8PNXlWCjfQ652QnN3yv8puvXp2G+M3vpJFRsplUBagyBsXABrnBkyERzIRrbMoaVnbHaxVZXnLwB8JECi2/5WS+Sf29v34lXdFu/dFK6fltzH2vR/U8w6EBrcg6bIhH2zZluTdmwpptcFcLOnOyEMWfbkXMzTPn/lbE/PIFGWJJvZMSnOdGBdI/NuaHlBKGa452X6Zo92p0wFy9mjPFBKHzpRahMY8qw6tjNOBExyMv/0UGt//+5Uehv/dGGCv2v9eZGlfFYFcmjF03Jk4BH8P4toIR6/C2LjHRqgI9Bmz8eCtlfrmVMt/K4i7ljiqzNK4tj7VgCZeEKT7lnk7BpdQlwWoAWGKx8fWtOvPrl7LYvYCVPUvmspgSiMfqWji9llJzvzqcxCJqbiUADLGi4PHTZ25kURMoeRm0psTAaNXswOcVXwWi0zJH9DgHhR0WHP+41Wh+J4qSwHmTQW0IdgxvpLA+whMhYcwOIDMmHGBKTlozLDi+vFWJG1FswVAaGkOpUjmHxD4GGwrTVQyqvmpah99BdCYNln10OpIowLCxU7+ttKVJW7DEm6LG0esVDhLkAQ16hyIBj1+U4sYO5FbgfmCU+ny2I+2y/AxxbA48FYu8D3CxPJMHRyxaHTOwsrLxWOfM+UAWkDPV9XZ5dBJK+wCEyR7Ax41a/GAGMjvUVhlPe1ZEPhxhUsUvOx2oWfQZBjME0EOrxa9fWLGbEffYzGBLu/iIgJ4H7re65zD4HAAM23o35VBhobMBLUMg5Sc7baRxsjdrbeCoTNgNPsiNYvwghwYLOyO10dUEMuK6cmESBwN0lDZElIDjo2L8Aqi/uhJBa0sPDsRbq+snL/8BgPULKNr90PjjrWbzBQPY2484kfYmtYm2s4DtoMz0E7ju+SbAwfeq8RcprMHah1Ym9nq656fc63RCtz/HdW3bSUV+jtOfw3+oxndSqNiL07UN6quyQ+CqTGAonkJ4mdzO4zPfxRAGGDeq8SMd7IRPK4NCO3KWsseZINfvGxSAPkXmWXEiz6DTiAcbGXxQjf8vhDV/ebplv8iI2qbV1t3rf9UzeGjZx+MkOYVArmbwwuwv8VY1fhFCjda/cW6BtbG/iUwi/3X7ldzXtzrMPWeKKtKKWz9sq6ZN5fGUiU6U47KXcCsDJ5DMGiq0K6Mb5EIM7LVrymLl/OdT8DkTi+fhVCqtEAfcJRmPE54ox88EUdPwziiwdHoANPwPp/xynUyurU7LNaGzq0efGzLVPsV8623TiaDkSmEN2+TCDvsbW5pmi7XNTVw34HLNcPCGwrTa8mfg9mmlbQ74kDqbfuLaZdTbcLScJ9RtuW7L5drhcHCtRMbAxMwV0zZRdDwSEoyJcmTo9bUP4ToJSv7eKrhuYJjrnYAxc7t74hvoh6qJomO0IBPlKIaKht8YBSeTB1shyVBn1xu9Hmu1JVijXoHQ1bhrwh8Foj5DxdKg7JITpWG7/crOG79PZUrUlgr91qZOWvm/wfQBYDgtmmatjY/Bhti2b+eLyeGrJbHCYn2YWmbwME6oOrozzg6cLdjiKuK5nUWz1vZITxpbHHFqsBS6CtqWiP/chzldJq4khhlSSzWVaNWnVaZrJVgX8S7D5hmkj1YxXiqkE/JUCAUR/0+3XjrjNPXlVSpXolaKwXm3I3ZOF1Xlz3pAXrqV5tNnA1hvbP0v7iAyXp3CsnZktGL/9Q7Zp02dilaAd2QYpzG2F9qUwNCtYVqT2trUeBUzl5IjnuI0j/xGNX5PnjoV3t9+wok1gKq1WrtCS1xBdY3dZ0TqzLqiU412QyGI+K1qvFNxxzBbOlRbiwe0VkF/b2uT6j9ZB8ehzFJq4IhTL9vZ1a/aB6gWeoTvd8cPvSPB8U5I5m12DvtkVdmlK7CFK05i4i0PCeFGMX5CQsnXDwFwkhWa9GmOATLZt7nFZvN3mV0sSllZBnFzDjRMhPeK8Q1Sb1aSHc+XGnVWTdyb73tMHXbV6NEumeKjMA+QNq0eMdAwEf5o8yuMqbqzBikznEUosGtPGXRTG2GCHYt+WRM3F6zddnfkF3rglS2p/yxjcPfAi51Nk3oL3hlGaxKZlOHxgnY0lZjED7k/jJKc/4CDSrb7zlJ/92Tmzjhqh12SFza1jG196Te0wCOR4QO0Vm1+BYOdcHfbpWU9++c2ocJmKGY2KQ1sJVWoWPlsryNlRT0gbxq7zSvhqVmgsKUJr5KbeS9sAMtkk06nDo+N2tLmjyrgzBvVwKadv8Gx0p1tFwqLHRtfYfsuc2gqJLVLppiCakRNSB0mVlIXVfiN1r0V2LvaJty1QsqrRuUubCqkMQQNXHpY014YMY8j2CKVmH9qsOsBI2x6q0er0GBz1JxNo+Ct8JSKU5Lk/JJ5HMEWBX6nwN51nLDprVZWocJug5dp51YylcewczWxoDT87uSqtzdWu02GcO2nQjFBqeWuKMwl0VPlykA85XELy9qZURqSpXI3wvNHo92SrCtqGLKOK6R9AWtssbwpjWwA56/J8yLcEsO3Tzkci91uTR+SjHqIcxwaCHza1Luu69oK69oc4Ao7rU2j4D1eLQN9YUhqSObkKrh/NNitMvp1b03DXYUm0Ge5oY22QCzKS2i0ZqEDqQBSAi6YL3jH4VirutquGfTJOGGV14N8bbSDvXegpufRABcnCUUxvRAsld7pUnb7IO52jywDA+OEVV45Yr8RGxQap4cB+N3dkzTd/LAhqVaZouz2F/E1Hk4nK6zyyiA7Ai8xsD0UBBpNv6n2OdKO516oRW88+4OV+7RrpvbVvLDKS/6ggJeif7E+mhxgA14L3Aao48EfTfRrtxlbdyj8MEgrpiNBtPZzj5W9mr7cWCNKPf3IuEuaQozoHSr/h3UNTbqq4COJGwnhu0FaMa0whX4cWDPb5bBF+51JKHxD4p3wQ4nb7Obqpe8/DfHrDpuXaVYvVqz54GlgAaQqKRwJF4BMqhBqLj60DKRQvrMEpSzLk7UJmITgvGbCu4/a/YhpCcJen1szbLQtDTWSH55aNXTLdGdT3861VEgB3eqE0hPqgzBb+mszNGEaUDtIyDCmpWiJywksoRRiHHQVOjPqssQrFKwoLU4SP2wow2+0Isx3TC0jQe30zB6sBCHaziaFQgFNOuX91tLdkaZsIuCkGjO+Wu4NPP6dq2Ukpg1hb+0Mi6YBqUA29G8ViYlcQfMwN0eg/ayPyKH0FJxLTP9Mx4Z3H8DxM9PBp6AZNKEN8sci4XkBUqwN0G96q4Mskg0SnG+Fa6asJ4IJJaqUpM5pQQQfvuBqzi1qSX4m4W4tCx6aEP6mMjPkryG16ZUKm6VzTZqKDSevz/lHCxRhNpDX0rug3Tqp7l0AsWY4XUNhg8zxHuQ9oZXOWsBfkqIqAMat/bUB+m054uBIwoQKi4TnD8j3/wWFFvBLwN1EYX8+YGNIutRIhTBa1veo+Miotir1DeW0jzY+o8GmXXLBPenoFHQujvKRpVZE2h2lXbB/DBTKji1hPOrql4+/ZVQ+FwpGbJc1pQ7eSfsu+tAigOYg6eeVRrjHq9fYPwYKZccUQ6fzvUYrzhNZ7aj3b0GF9l3wvFUA1ak8Br9hg3uchNXwI0SMWQOuB8qXDy8W+JWsJN9vDXmKENTvgganIQjq95ZbeItGKXopkpC6TCxAhqP+XnWT0AxuDrfC5Mw4rLDl5hpuxnyD5nfsmKyIBbLh3MobAlNh2Q64F0CL+4prGe/JZGIU7io0tGxiDpwDy6H5Gyv9TBd6IgWj5hakgB3MbKp/rbcDhw37zIyxPjArU/3dg7PScBs4zj6UDssOsvqd7bBulQZXCtagIyZdG2yQwdzIzW8/wOBf74AHfeE4hn8Y8Uyti/53kKLS9Q5Ys/PI9CCvWrQ8ITN3MxomvIRjy7+8Q1n+SZotR+lyO3/+KHnt2cwfJr1DdQe7+QiAlPXXJQCYErNuZcE6ZvsaWiaJ/FARIuU3v/kZpuWY+DZhGpfY7VlEhyl9rNWwwz7tQLNCKezb4vIX8T6NUguWngmGeAuE6qL/+DX/oTpGH07LMNg2k96hkSXQwV3nO1l1yHNX2GKEEhHugCOlZZNz3hzZiSx/+YAgLsfbzjet2DPmMzcMazA+4prkAvdCUX8SyrZMfVuWcqqscCe5AkrPxhO0Fza0TWVR/Ow9ybSCxZi7L9tUcqIrYj9kVjhgGb8h4AFClYN0D4nsPhVfIuoK6OThOcZ2nZgRCdoW7dYm3wv3tYSznRdl7XHLg3Q1GCY4Kxxbr0dAMQ3fBUOzT5smZf8d03StKcWU+ntqZISdhcw3H3CDsXKpUoz560g4YAnT10HIq0DlQyXDTfbltKqA+/RSNAhdjaIQSiNQtEsOD+oMshdwP/TLO5m3/UFoopdXOMzG6dkZCTZ1G+BDqUsvEhbrag97vc2XxSGzKcu6BvhZQNvNJ3Cf8uVzMQET3s0slfsav5Plv2PortbcCOa+c+FDgUVarIW8eAhX6tuq/9Epmo1EUuH8WST1oCUi9kOaEe8xWMWb3exf+gspdIfyyPQYcd/ZhLM9XO7fdqg3yZQODDAl3Feev/r5Qg2B7089VUD+aizvWX353s2L94wxE9UTypxUGQEuOUkDdiuI7SNQ5hvYCyqT4fVvDGsNLj3JaBLpJurJ6ivmYmyHMf8cklnldCg3cFo1bV1hSafW/YCs2FT1gaf7xiREvjuDpoX3fL54ruP1XbLP+Vx1Z5aTmfVbrJuACmb+BywCNdISldfy7obJF9AtOKVqscgcDZrlrKxCrQ+YaWVrGWhtRFuOO0EDlaF//DPoTUR0OM/s39gLzZ1ZzsYq7PoBpRScJsy4T2id+Df57p9kWTmku2Zbmy6l5kDULKe2CgMFuOhHdJ6gb9YUR9781d/zX3/AvecX6R9MjqFDB0tMhfuBmBoyiM9mdiHHB30zzaiO9HuS+XRw35VD+hn0ZubdHJezMOAW3zfZe+yq4FFbiMg3MNFB3Onnd8BVtVo2HnUNv8HS+YNNaNB9Z0xPY17TVzjClloGng66+uEfjnHkb573EocTHCvWcLaUnutru6jryiak2DhYLkQ0z7YXwND9yMTz0Av4itm7f3vl8fHm/TtsDYcnasEyieV/8LSVXbdivzMJq4EUJOpwyRgarQFIGRD7fuQq2gu8+8Mf3sHcRoTUoIouoiXCz3X6YBg7LOvVV4JJ4pFKLnoNLN2FDX35X7b+gJtrCTcY03yYG5zY5RQWoYXl0vlyZmLvlZ1a4X+BjvlRqRH7ufTJIFT87gXx4SdlV0w3Bo+gknP0xiazS2Gck+pwsechdQC1ajEo6Lf8WGBuJ+sM/R0X754IU3APhNqwuVNIDUIx8L0OfBJdiJH9DCXDFBOpfjie6SyfsDtrc+DuaHUVrIeF59LanyshN1IpffL68SN+w/7aJc9zKB1hYkDHcIpOUq54ZsYy5h9ba2UMSgE5SIL1iyLuLCOxKyNjjQ9PkvzlRcBC8V+OZ/NEta7DgEO4g441U3YaeTO3jfRqaFQSkEu6yxrLNE5SteDuV8Ojm6t4PpuOLy7enJ8Hr3Po+fn5m4vL8WQaRYvXpYFmOCLrkDJYkgodyzMup7wzMcLOaXLPInMnOyM3UcntQuXvgZKhSv/fTKIlW1ti1Ef+hMtJMwt/hOzUET90tgRsgqIbUtZvgXkOoMVrpBIOspSexktNbYm3XA8/XOQvtzIwzANNCsLzI03ZoRBT5pBpLc2M2VvnU+e48ATkgy/D3JZYz6O84q3FuzKmm1p2DsgTjEtvTbtBQYsZ8cYW7PB/HRQ9nc0XflDWlrj2zFCMTktKtZ151mH+pmLnwFyYapBDVp9bA3XSZzrxAFlKK2tL3HgWlL4OexnoLFhqER3Da7N+HLF60PZe4Frrw58JqZhS+eVmZW2JW88c5en472JZ5DWZowGZfsfAVF0DlfdTmxyn9kf6CRDR+IGpLTF6dw6sTkOciwDLrEsx3bMpcFQtVN5/skn69yS6N16VrYQspZW1JeasSc397+iIuNhl7zqiiyY4X+Ews3LIzdIOdNjPYCl/Z/MaSukEWzHdsT7SdCOCF20H7VagrdsEU1pToGQ3KKc9HLFtMV9ofWYWahWltB822GpuMffTHNfeGbAIUHA57s8VuER/Fh4KSFq7NMNVtK1AgJ15ltLcIhtIwX1COqEKnNzcTim2d/SzHGzyPYfdMQObISd3hNCzzA3PtGKHKCiMdf2ulCXvkyOVBEK/0lClEd2agZ+Cm3wvYXdcoRXq7WHHMvda7w/VBtmIi0lkAy02D3mZzDyl11Yfdo6D44vLHJrA7rhGG9PzkznjhH5S9eTiYgiwJTIaVy6GFptfkB8aT1/2hilRVCxfN3CpFMFqphdoFOLamp5knL+rF04WlV8pzXq8sE+Yk8hzaLmhf8IKg4mo6YQJbvOFDVRNLYdMcbl+1Lp+Jl0mAbYG5dEFctukjP9hsR3J9cy61uNZZZJP/irwz3baQNXUugwxB7cmqLhpesYFFQiwd0wPpmeCHNXa/4mc5NqFuW74pMtXphnMNbJ6RYpVU6sg+d4b/fvoilmBAJuhlCbdyCIItgKPqOpsIDjQZAdypXT5uX5ugX9pX+JdW2DR6zkiiZhm+t9C1aN/xiTAxotrzlXydMj93XBoBcnN8u2qhd+L54JNyH60u7bAyv5dhlg2N7r3UecyCbABlczYSp4MAzvvhjuMXvyVwzXySu2YxDj+mICzf2p9F+oTOLPeq95H2IVVdcCjvpd8Pzw8bshLpnJQMNOsa9R4nPhd6C6Bfu/mAJOs2wImumJghtdWmnBDVXVI8WYjhvFBAcANOeV+RHIlyi9Xd4j1eYC70EVvvzBwCGWpnxdU7bPV65cqcGVKSIdGfV8wPg8buNqrc/5HGlhEE/KhgfJpngcYKG9n+L///gatptZA6kxsjIlxECvCHE6AzVi9PIftgqmVKjHjAcBylz2CeKQvY+FGsBMFybOFq6ltIJk6K514Iko7NQvON2h7zhhFNjnqKyEnwSTPmHqod9I+9UPGO3QBzEB8ZF2awdXUVohssk5lmC21QqZZcF6jfZQxY/4tUV+5ZKq60p4EY+ZK3YPYL6DS1dzP5nnWavqN+IeIp/9y4Jn54Y6lVRnIhifk+xyshWjN+Fj1LlBfqAVruEuwx5Z1/Vi1HfOTbu9FH4i2eIl1PGANNV5NrYXUQqkUSsyJlJvuqFIdQusAVHLeiWWu1088ONotjq3WRVk3/jOcnnZdW1fbYuWEjmnP6imSYt9NvJpaBdmXR2nGrFNHCh/8FKAKlmOlvkeYulMr2QcYO6LR5jlECXLQQPx8B/CWZ/wBOeD1LTeQ1b9HjYowFjysuBHWt3rPSn3PMDe3wk13ptROzWwxFdd/DQK85LxFzHg5BFRHXlEfigf1O9ZYaSRmpijFt6UFyGNVD2gB8l3WDZd6FCwGGaBsOV0hIlW0PA0s8KPd2C1WGiEcMCL9t5J/3sBH5vymcZTf7oUndrhItdxhaueE4b3osfJ+Rqkmrpip7zHiZi2vNLgcDgwMlVUDPZZjLKui9V/M5X7gv2ix8n5WqSZWzLyjCLFe1/Geos1+Aq7zjsUq2swlR1OBuWwHhEWHtcOP8qxcqRBuLT1RAbahn4M90/KFAb4sVtNmK5hEiIs4G1Pp+9QIusQqVBNVCLdGeIE778d0nn+xzDPveCzsJpbTDUCXVnHlLaPNLM2ZTnOOk0xqduHWTMkkb12fwS2DUjX7nccq22zlXMwlo/YRjZ0kOoTUYJdqYsdeo1kGaE7nvS2zmLRTVqbqLNPQGO+lxgIJutzTnxATpZKRuSkPwFya4YzMRYCiLPGuwiS3ENXeFKJMB/os09AYu0zWRZvDgFptcuGYMB6lL6Ds3PlhKNHYOnVoV2ECXQa1tvcougG+lWsbXyEQy+wpX/SuK0qF1bKJNQ+YYIuiijCj6uL4VhWxRCuLM9hlUCsKkG061v+nIuzAbLugXKGllDsebUpN25SYHOx5IBRzEBZoIqMIngu1toX4Otn/VUzaQlF9ngFjJTVFEK+ccZSaToL80OfpWjLxZv5z1FnIZOflAKiq2OApIky4Hg0biagF7/ffgspj4E0xCWV7wEK9ZjwaLF8Aq1AiqmJIAGPvAgJHxyiUbzmrPeps0yL+L45AfhUl8Hj3SxnyQxFTJt8NAKxGiaiKawzHMDTaCrP4nAds5U6JGjyvU3Oe9MtlgKBEVgtebxzqgskpxixl5C3ffyaU3p3ZGKGQg3L2rz7fo5CU0ufpJGRM9Xj32J/lVcp5O2aqSh7/BfkukUqp297A131w9YDJWrSZOvn8HHPIbAjtAzDstfyJdszb22Ge4fCAPB7uJEPksqF0rs9qmX4kV6Dnqa50wiEssHK5usukq2oXPDIbhMV1yNWqKXMSbUuAl1zB/b/9E8YKU6ik+NztYK+PltVSxyNF3WX37cAgyzzgQWmaLpL9J0RK7xkrdOQSVD4Ue0W0LnIxmv9+c+BnnFYXDKPHi84xMa3lVlAHBrUrnJxkREVswMVhLWtDcTlWydcfKHiHssuJTDJtOJ4Tb4hnMCqY6XzeJymeTo5kHl6xrMiep6jzEcM+myAQVj+DtckkabqI7ZCFxSBb4Eb05NuNZJwJk01xn+e5O272HiH1sqoPPx2QKnzBa0Rd5aQqwsnZmEf9KCNMeZj81KLkTHQcY9Vs+GKNcOSoupxSTPPxdkWZpXnLrDnYM/83ZcHMpv+6mKbIKCQzraI2ZivTeR8KsVHIejW28v4TaeqAioq1jjHW8Ryay5fWpZPS3osxOinsCkERB9apeh4CQ7Mv7r9l1q7a5O7QZHxw1GxCXtborn59VC6nyyuSJBR4kKWK/JM7/Fy/F75PDjpDyzE7HKIggvYMfQHItpKAQUVUhO6bYpO9Pf9sjc7Yaj+MuTAR+WpTnnzNxpXxvgiMHP1mzZP+G3LzNDIy5yJIkBJoU3zyqWLtJSiOPm9s3kngZtAVRghmZoL/RGE8SW+m8MmitfZfRiltq02Rv5h7nnnnmrH/R+1wHHW5f9R79Bjntwq+U28/R77LEAsT/v+6KXbyIwl1YWLQI15/BVbrVJJA9XUoG9mwchGb8P/FBFWuLuW0FPhKtOVEEwE3N+npE8dKJAleAjWDYxKFBqwZ8f9AhZdCEfP+5Ehq0cZDNcdLdTEdcUJ95wZRqH3NChMBFjxwUQyPMmW0CDytMhPVxfSUEQZ0RRSFutfsWNDoSbxQYSKWWHrqX/FyBPPJCgmwTOWERhcqee8Ta56n6ggrheCFChN9EtIwNu6/mEF1MT1VJ+Aw9t9sMpwaVArNA+AUEoWYB5SM0fcyblGdn6fGyi2XmoIHl3E/JdKLJUlJATTpJRd9osbAzxBXnZ+nIHuo+pImoY7a/Myu1JoMhRnluCerC9L3FIfAa323OlwqJynH+mQX47AXfFSWAMEcly06IaeUC8i1HlzGS+VnM53Vq8X0DSzZ/+xjYSpFljAnE1SNlMUYfa2XqLopQG15bDMvNIkAo30zia1MqlPoBJKMUTVSkmkII+801kx15+cotFpwuYjGAm9DmyoDS8ufMXThmoxhW9fNxwDh1Sm1geRp1fk5GRsuuFzG00vpoOfjA/NiYusKNMmcRCHmETHMRwzTlE1ZnEJ05+d5aHsFaxnPJhcCuwZvxtNo8RzW7HQaQvMf55ewKS+JLgORqpmRPgQRI39+1k2oXFuQEf1nwuT8nEYWnF9cjqfRXG18YEmFI9OfTw9nRLAy/WIuKEmZXQS8dFxkfxKJLnSX0lHYK8zQZeJnlv3z/+Y3H4c0ocJC/qje4MorFjMWnkxwOZn3RVKJx3yyXfVH/V8Jk8x9OBvnl5NoAXtU55dAfJDF/KXeFnjRBm/Gk8gsbosk3cVB0yGriDJUuy6mi7P/kpjlIp5HeyZeJIiZIY73jmm+SEDz2uLluPaPLI6TXiqt6HMddT6eJQpolclFQKpi/Y+PiZ8Jr4vz4BBFdDmZaarkLObPjK83R45ahOo7Aw=="; kofiButton.appendChild(kofiImage); containerEl.appendChild(kofiButton); } createFeatureHeading(containerEl, label, description, settingsKey) { return new import_obsidian3.Setting(containerEl).setHeading().setClass("ac-settings-heading").setName(label).setDesc(description).addToggle( (toggle) => toggle.setTooltip("Requires a reload to take effect.").setValue(this.settingsManager.getSetting(settingsKey)).onChange(async (value) => { await this.settingsManager.setSetting({ [settingsKey]: value }); new import_obsidian3.Notice("Reload obsidian to apply the changes."); }) ); } createDefaultStylesSection(containerEl, label, settingsKey, allStylableAttributes) { const defaultNodeStylesEl = document.createElement("details"); defaultNodeStylesEl.classList.add("setting-item"); const defaultNodeStylesSummaryEl = document.createElement("summary"); defaultNodeStylesSummaryEl.textContent = label; defaultNodeStylesEl.appendChild(defaultNodeStylesSummaryEl); for (const stylableAttribute of allStylableAttributes) { new import_obsidian3.Setting(defaultNodeStylesEl).setName(stylableAttribute.label).addDropdown( (dropdown) => { var _a; return dropdown.addOptions(Object.fromEntries(stylableAttribute.options.map((option) => [option.value, option.label]))).setValue((_a = this.settingsManager.getSetting(settingsKey)[stylableAttribute.datasetKey]) != null ? _a : "null").onChange(async (value) => { const newValue = this.settingsManager.getSetting(settingsKey); if (value === "null") delete newValue[stylableAttribute.datasetKey]; else newValue[stylableAttribute.datasetKey] = value; await this.settingsManager.setSetting({ [settingsKey]: newValue }); }); } ); } containerEl.appendChild(defaultNodeStylesEl); } }; // src/windows-manager.ts var WindowsManager = class { constructor(plugin) { this.windows = [window]; this.plugin = plugin; this.plugin.registerEvent(this.plugin.app.workspace.on( "window-open", (_win, window2) => this.windows.push(window2) )); this.plugin.registerEvent(this.plugin.app.workspace.on( "window-close", (_win, window2) => this.windows = this.windows.filter((w) => w !== window2) )); } }; // src/utils/canvas-helper.ts var import_obsidian4 = require("obsidian"); // src/utils/bbox-helper.ts var BBoxHelper = class { static combineBBoxes(bboxes) { let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; for (let bbox of bboxes) { minX = Math.min(minX, bbox.minX); minY = Math.min(minY, bbox.minY); maxX = Math.max(maxX, bbox.maxX); maxY = Math.max(maxY, bbox.maxY); } return { minX, minY, maxX, maxY }; } static scaleBBox(bbox, scale) { let diffX = (scale - 1) * (bbox.maxX - bbox.minX); let diffY = (scale - 1) * (bbox.maxY - bbox.minY); return { minX: bbox.minX - diffX / 2, maxX: bbox.maxX + diffX / 2, minY: bbox.minY - diffY / 2, maxY: bbox.maxY + diffY / 2 }; } static insideBBox(position, bbox, canTouchEdge) { var _a, _b, _c, _d; const providedBBox = { minX: (_a = position.minX) != null ? _a : position.x, minY: (_b = position.minY) != null ? _b : position.y, maxX: (_c = position.maxX) != null ? _c : position.x, maxY: (_d = position.maxY) != null ? _d : position.y }; return canTouchEdge ? providedBBox.minX >= bbox.minX && providedBBox.maxX <= bbox.maxX && providedBBox.minY >= bbox.minY && providedBBox.maxY <= bbox.maxY : providedBBox.minX > bbox.minX && providedBBox.maxX < bbox.maxX && providedBBox.minY > bbox.minY && providedBBox.maxY < bbox.maxY; } static enlargeBBox(bbox, padding) { return { minX: bbox.minX - padding, minY: bbox.minY - padding, maxX: bbox.maxX + padding, maxY: bbox.maxY + padding }; } static moveInDirection(position, side, distance) { switch (side) { case "top": return { x: position.x, y: position.y - distance }; case "right": return { x: position.x + distance, y: position.y }; case "bottom": return { x: position.x, y: position.y + distance }; case "left": return { x: position.x - distance, y: position.y }; } } static getCenterOfBBoxSide(bbox, side) { switch (side) { case "top": return { x: (bbox.minX + bbox.maxX) / 2, y: bbox.minY }; case "right": return { x: bbox.maxX, y: (bbox.minY + bbox.maxY) / 2 }; case "bottom": return { x: (bbox.minX + bbox.maxX) / 2, y: bbox.maxY }; case "left": return { x: bbox.minX, y: (bbox.minY + bbox.maxY) / 2 }; } } static getSideVector(side) { switch (side) { case "top": return { x: 0, y: 1 }; case "right": return { x: 1, y: 0 }; case "bottom": return { x: 0, y: -1 }; case "left": return { x: -1, y: 0 }; default: return { x: 0, y: 0 }; } } static isHorizontal(side) { return side === "left" || side === "right"; } static direction(side) { return side === "right" || side === "bottom" ? 1 : -1; } }; // src/utils/canvas-helper.ts var _CanvasHelper = class _CanvasHelper { static canvasCommand(plugin, check, run) { return (checking) => { const canvas = plugin.getCurrentCanvas(); if (checking) return canvas !== null && check(canvas); if (canvas) run(canvas); return true; }; } static createControlMenuButton(menuOption) { const quickSetting = document.createElement("div"); if (menuOption.id) quickSetting.id = menuOption.id; quickSetting.classList.add("canvas-control-item"); (0, import_obsidian4.setIcon)(quickSetting, menuOption.icon); (0, import_obsidian4.setTooltip)(quickSetting, menuOption.label, { placement: "left" }); quickSetting.addEventListener("click", () => { var _a; return (_a = menuOption.callback) == null ? void 0 : _a.call(menuOption); }); return quickSetting; } static addControlMenuButton(controlGroup, element) { var _a; if (element.id) (_a = controlGroup.querySelector(`#${element.id}`)) == null ? void 0 : _a.remove(); controlGroup.appendChild(element); } static createCardMenuOption(canvas, menuOption, previewNodeSize, onPlaced) { const menuOptionElement = document.createElement("div"); if (menuOption.id) menuOptionElement.id = menuOption.id; menuOptionElement.classList.add("canvas-card-menu-button"); menuOptionElement.classList.add("mod-draggable"); (0, import_obsidian4.setIcon)(menuOptionElement, menuOption.icon); (0, import_obsidian4.setTooltip)(menuOptionElement, menuOption.label, { placement: "top" }); menuOptionElement.addEventListener("click", (_e) => { onPlaced(canvas, this.getCenterCoordinates(canvas, previewNodeSize())); }); menuOptionElement.addEventListener("pointerdown", (e) => { canvas.dragTempNode(e, previewNodeSize(), (pos) => { canvas.deselectAll(); onPlaced(canvas, pos); }); }); return menuOptionElement; } static addCardMenuOption(canvas, element) { var _a; if (element.id) (_a = canvas == null ? void 0 : canvas.cardMenuEl.querySelector(`#${element.id}`)) == null ? void 0 : _a.remove(); canvas == null ? void 0 : canvas.cardMenuEl.appendChild(element); } static createPopupMenuOption(menuOption) { const menuOptionElement = document.createElement("button"); if (menuOption.id) menuOptionElement.id = menuOption.id; menuOptionElement.classList.add("clickable-icon"); (0, import_obsidian4.setIcon)(menuOptionElement, menuOption.icon); (0, import_obsidian4.setTooltip)(menuOptionElement, menuOption.label, { placement: "top" }); menuOptionElement.addEventListener("click", () => { var _a; return (_a = menuOption.callback) == null ? void 0 : _a.call(menuOption); }); return menuOptionElement; } static createExpandablePopupMenuOption(menuOption, subMenuOptions) { const menuOptionElement = this.createPopupMenuOption({ ...menuOption, callback: () => { var _a, _b, _c; const submenuId = `${menuOption.id}-submenu`; if (menuOptionElement.classList.contains("is-active")) { menuOptionElement.classList.remove("is-active"); (_b = (_a = menuOptionElement.parentElement) == null ? void 0 : _a.querySelector(`#${submenuId}`)) == null ? void 0 : _b.remove(); return; } menuOptionElement.classList.add("is-active"); const submenu = document.createElement("div"); submenu.id = submenuId; submenu.classList.add("canvas-submenu"); for (const subMenuOption of subMenuOptions) { const subMenuOptionElement = this.createPopupMenuOption(subMenuOption); submenu.appendChild(subMenuOptionElement); } (_c = menuOptionElement.parentElement) == null ? void 0 : _c.appendChild(submenu); } }); return menuOptionElement; } static addPopupMenuOption(canvas, element, index = -1) { var _a; const popupMenuEl = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl; if (!popupMenuEl) return; if (element.id) { const optionToReplace = popupMenuEl.querySelector(`#${element.id}`); if (optionToReplace && index === -1) index = Array.from(popupMenuEl.children).indexOf(optionToReplace) - 1; optionToReplace == null ? void 0 : optionToReplace.remove(); } const sisterElement = index >= 0 ? popupMenuEl.children[index] : popupMenuEl.children[popupMenuEl.children.length + index]; popupMenuEl.insertAfter(element, sisterElement); } static getCenterCoordinates(canvas, nodeSize) { const viewBounds = canvas.getViewportBBox(); return { x: (viewBounds.minX + viewBounds.maxX) / 2 - nodeSize.width / 2, y: (viewBounds.minY + viewBounds.maxY) / 2 - nodeSize.height / 2 }; } static getBBox(canvasNodes) { let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; for (const node of canvasNodes) { const nodeData = node.getData ? node.getData() : node; minX = Math.min(minX, nodeData.x); minY = Math.min(minY, nodeData.y); maxX = Math.max(maxX, nodeData.x + nodeData.width); maxY = Math.max(maxY, nodeData.y + nodeData.height); } return { minX, minY, maxX, maxY }; } static zoomToBBox(canvas, bbox) { const PADDING_CORRECTION_FACTOR = 1 / 1.1; const zoomedBBox = BBoxHelper.scaleBBox(bbox, PADDING_CORRECTION_FACTOR); canvas.zoomToBbox(zoomedBBox); const scaleFactor = Math.min( canvas.canvasRect.width / (bbox.maxX - bbox.minX), canvas.canvasRect.height / (bbox.maxY - bbox.minY) ); canvas.tZoom = Math.log2(scaleFactor); } static addStyleAttributesToPopup(plugin, canvas, styleAttributes, currentStyleAttributes, setStyleAttribute) { if (!plugin.settings.getSetting("combineCustomStylesInDropdown")) this.addStyleAttributesButtons(canvas, styleAttributes, currentStyleAttributes, setStyleAttribute); else this.addStyleAttributesDropdownMenu(canvas, styleAttributes, currentStyleAttributes, setStyleAttribute); } static addStyleAttributesButtons(canvas, stylableAttributes, currentStyleAttributes, setStyleAttribute) { var _a; for (const stylableAttribute of stylableAttributes) { const selectedStyle = (_a = stylableAttribute.options.find((option) => currentStyleAttributes[stylableAttribute.datasetKey] === option.value)) != null ? _a : stylableAttribute.options.find((value) => value.value === null); const menuOption = _CanvasHelper.createExpandablePopupMenuOption({ id: `menu-option-${stylableAttribute.datasetKey}`, label: stylableAttribute.label, icon: selectedStyle.icon }, stylableAttribute.options.map((styleOption) => ({ label: styleOption.label, icon: styleOption.icon, callback: () => { setStyleAttribute(stylableAttribute, styleOption.value); currentStyleAttributes[stylableAttribute.datasetKey] = styleOption.value; (0, import_obsidian4.setIcon)(menuOption, styleOption.icon); menuOption.dispatchEvent(new Event("click")); } }))); _CanvasHelper.addPopupMenuOption(canvas, menuOption); } } static addStyleAttributesDropdownMenu(canvas, stylableAttributes, currentStyleAttributes, setStyleAttribute) { var _a, _b; const STYLE_MENU_ID = "style-menu"; const STYLE_MENU_DROPDOWN_ID = "style-menu-dropdown"; const STYLE_MENU_DROPDOWN_SUBMENU_ID = "style-menu-dropdown-submenu"; const popupMenuElement = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl; if (!popupMenuElement) return; (_b = popupMenuElement.querySelector(`#${STYLE_MENU_ID}`)) == null ? void 0 : _b.remove(); const styleMenuButtonElement = document.createElement("button"); styleMenuButtonElement.id = STYLE_MENU_ID; styleMenuButtonElement.classList.add("clickable-icon"); (0, import_obsidian4.setIcon)(styleMenuButtonElement, "paintbrush"); (0, import_obsidian4.setTooltip)(styleMenuButtonElement, "Style", { placement: "top" }); popupMenuElement.appendChild(styleMenuButtonElement); styleMenuButtonElement.addEventListener("click", () => { var _a2, _b2, _c; const isOpen = styleMenuButtonElement.classList.toggle("has-active-menu"); if (!isOpen) { (_a2 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_ID}`)) == null ? void 0 : _a2.remove(); (_b2 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_SUBMENU_ID}`)) == null ? void 0 : _b2.remove(); return; } const styleMenuDropdownElement = document.createElement("div"); styleMenuDropdownElement.id = STYLE_MENU_DROPDOWN_ID; styleMenuDropdownElement.classList.add("menu"); styleMenuDropdownElement.style.position = "absolute"; styleMenuDropdownElement.style.maxHeight = "initial"; styleMenuDropdownElement.style.top = `${popupMenuElement.getBoundingClientRect().height}px`; const canvasWrapperCenterX = canvas.wrapperEl.getBoundingClientRect().left + canvas.wrapperEl.getBoundingClientRect().width / 2; const leftPosition = styleMenuButtonElement.getBoundingClientRect().left - popupMenuElement.getBoundingClientRect().left; const rightPosition = popupMenuElement.getBoundingClientRect().right - styleMenuButtonElement.getBoundingClientRect().right; if (popupMenuElement.getBoundingClientRect().left + leftPosition < canvasWrapperCenterX) styleMenuDropdownElement.style.left = `${leftPosition}px`; else styleMenuDropdownElement.style.right = `${rightPosition}px`; for (const stylableAttribute of stylableAttributes) { const stylableAttributeElement = document.createElement("div"); stylableAttributeElement.classList.add("menu-item"); stylableAttributeElement.classList.add("tappable"); const iconElement = document.createElement("div"); iconElement.classList.add("menu-item-icon"); let selectedStyle = (_c = stylableAttribute.options.find((option) => currentStyleAttributes[stylableAttribute.datasetKey] === option.value)) != null ? _c : stylableAttribute.options.find((value) => value.value === null); (0, import_obsidian4.setIcon)(iconElement, selectedStyle.icon); stylableAttributeElement.appendChild(iconElement); const labelElement = document.createElement("div"); labelElement.classList.add("menu-item-title"); labelElement.textContent = stylableAttribute.label; stylableAttributeElement.appendChild(labelElement); const expandIconElement = document.createElement("div"); expandIconElement.classList.add("menu-item-icon"); (0, import_obsidian4.setIcon)(expandIconElement, "chevron-right"); stylableAttributeElement.appendChild(expandIconElement); styleMenuDropdownElement.appendChild(stylableAttributeElement); stylableAttributeElement.addEventListener("pointerenter", () => { stylableAttributeElement.classList.add("selected"); }); stylableAttributeElement.addEventListener("pointerleave", () => { stylableAttributeElement.classList.remove("selected"); }); stylableAttributeElement.addEventListener("click", () => { var _a3; (_a3 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_SUBMENU_ID}`)) == null ? void 0 : _a3.remove(); const styleMenuDropdownSubmenuElement = document.createElement("div"); styleMenuDropdownSubmenuElement.id = STYLE_MENU_DROPDOWN_SUBMENU_ID; styleMenuDropdownSubmenuElement.classList.add("menu"); styleMenuDropdownSubmenuElement.style.position = "absolute"; styleMenuDropdownSubmenuElement.style.maxHeight = "initial"; const topOffset = parseFloat(window.getComputedStyle(styleMenuDropdownElement).getPropertyValue("padding-top")) + (styleMenuDropdownElement.offsetHeight - styleMenuDropdownElement.clientHeight) / 2; styleMenuDropdownSubmenuElement.style.top = `${stylableAttributeElement.getBoundingClientRect().top - topOffset - popupMenuElement.getBoundingClientRect().top}px`; const leftPosition2 = styleMenuDropdownElement.getBoundingClientRect().right - popupMenuElement.getBoundingClientRect().left; const rightPosition2 = popupMenuElement.getBoundingClientRect().right - styleMenuDropdownElement.getBoundingClientRect().left; if (popupMenuElement.getBoundingClientRect().left + leftPosition2 < canvasWrapperCenterX) styleMenuDropdownSubmenuElement.style.left = `${leftPosition2}px`; else styleMenuDropdownSubmenuElement.style.right = `${rightPosition2}px`; for (const styleOption of stylableAttribute.options) { const styleMenuDropdownSubmenuOptionElement = document.createElement("div"); styleMenuDropdownSubmenuOptionElement.classList.add("menu-item"); styleMenuDropdownSubmenuOptionElement.classList.add("tappable"); const submenuIconElement = document.createElement("div"); submenuIconElement.classList.add("menu-item-icon"); (0, import_obsidian4.setIcon)(submenuIconElement, styleOption.icon); styleMenuDropdownSubmenuOptionElement.appendChild(submenuIconElement); const submenuLabelElement = document.createElement("div"); submenuLabelElement.classList.add("menu-item-title"); submenuLabelElement.textContent = styleOption.label; styleMenuDropdownSubmenuOptionElement.appendChild(submenuLabelElement); if (selectedStyle === styleOption) { styleMenuDropdownSubmenuOptionElement.classList.add("mod-selected"); const selectedIconElement = document.createElement("div"); selectedIconElement.classList.add("menu-item-icon"); selectedIconElement.classList.add("mod-selected"); (0, import_obsidian4.setIcon)(selectedIconElement, "check"); styleMenuDropdownSubmenuOptionElement.appendChild(selectedIconElement); } styleMenuDropdownSubmenuElement.appendChild(styleMenuDropdownSubmenuOptionElement); styleMenuDropdownSubmenuOptionElement.addEventListener("pointerenter", () => { styleMenuDropdownSubmenuOptionElement.classList.add("selected"); }); styleMenuDropdownSubmenuOptionElement.addEventListener("pointerleave", () => { styleMenuDropdownSubmenuOptionElement.classList.remove("selected"); }); styleMenuDropdownSubmenuOptionElement.addEventListener("click", () => { setStyleAttribute(stylableAttribute, styleOption.value); currentStyleAttributes[stylableAttribute.datasetKey] = styleOption.value; selectedStyle = styleOption; (0, import_obsidian4.setIcon)(iconElement, styleOption.icon); styleMenuDropdownSubmenuElement.remove(); }); } popupMenuElement.appendChild(styleMenuDropdownSubmenuElement); }); } popupMenuElement.appendChild(styleMenuDropdownElement); }); } }; _CanvasHelper.GRID_SIZE = 20; var CanvasHelper = _CanvasHelper; // src/core/canvas-extension.ts var CanvasExtension = class { constructor(plugin) { this.plugin = plugin; const isEnabled = this.isEnabled(); if (!(isEnabled === true || this.plugin.settings.getSetting(isEnabled))) return; this.init(); } }; // src/canvas-extensions/group-canvas-extension.ts var GROUP_NODE_SIZE = { width: 300, height: 300 }; var GroupCanvasExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => { CanvasHelper.addCardMenuOption( canvas, CanvasHelper.createCardMenuOption( canvas, { id: "create-group", label: "Drag to add group", icon: "group" }, () => GROUP_NODE_SIZE, (canvas2, pos) => { canvas2.createGroupNode({ pos, size: GROUP_NODE_SIZE }); } ) ); } )); } }; // src/canvas-extensions/presentation-canvas-extension.ts var import_obsidian5 = require("obsidian"); var START_SLIDE_NAME = "Start Slide"; var DEFAULT_SLIDE_NAME = "New Slide"; var PresentationCanvasExtension = class extends CanvasExtension { constructor() { super(...arguments); this.savedViewport = null; this.isPresentationMode = false; this.visitedNodes = []; this.fullscreenModalObserver = null; } isEnabled() { return "presentationFeatureEnabled"; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.SelectionContextMenu, (menu, canvas) => { menu.addItem( (item) => item.setTitle("Wrap in slide").setIcon("gallery-vertical").onClick(() => this.addSlide( canvas, void 0, BBoxHelper.enlargeBBox(BBoxHelper.combineBBoxes( [...canvas.selection.values()].map((element) => element.getBBox()) ), this.plugin.settings.getSetting("wrapInSlidePadding")) )) ); } )); this.plugin.addCommand({ id: "create-new-slide", name: "Create new slide", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly && !this.isPresentationMode, (canvas) => this.addSlide(canvas) ) }); this.plugin.addCommand({ id: "set-start-node", name: "Set start node", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly && !this.isPresentationMode && canvas.getSelectionData().nodes.length === 1, (canvas) => this.setStartNode(canvas, canvas.nodes.get(canvas.getSelectionData().nodes[0].id)) ) }); this.plugin.addCommand({ id: "start-presentation", name: "Start presentation", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => !this.isPresentationMode, (canvas) => this.startPresentation(canvas) ) }); this.plugin.addCommand({ id: "continue-presentation", name: "Continue presentation", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => !this.isPresentationMode, (canvas) => this.startPresentation(canvas, true) ) }); this.plugin.addCommand({ id: "end-presentation", name: "End presentation", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => this.isPresentationMode, (canvas) => this.endPresentation(canvas) ) }); this.plugin.addCommand({ id: "previous-node", name: "Previous node", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => this.isPresentationMode, (canvas) => this.previousNode(canvas) ) }); this.plugin.addCommand({ id: "next-node", name: "Next node", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => this.isPresentationMode, (canvas) => this.nextNode(canvas) ) }); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.onCanvasChanged(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.onPopupMenuCreated(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeMoved, (canvas, node) => this.onNodeMoved(canvas, node) )); } onCanvasChanged(canvas) { CanvasHelper.addCardMenuOption( canvas, CanvasHelper.createCardMenuOption( canvas, { id: "new-slide", label: "Drag to add slide", icon: "gallery-vertical" }, () => this.getDefaultSlideSize(), (canvas2, pos) => this.addSlide(canvas2, pos) ) ); } onPopupMenuCreated(canvas) { if (!this.plugin.settings.getSetting("showSetStartNodeInPopup")) return; const selectedNodesData = canvas.getSelectionData().nodes; if (canvas.readonly || selectedNodesData.length !== 1 || canvas.selection.size > 1) return; const selectedNode = canvas.nodes.get(selectedNodesData[0].id); if (!selectedNode) return; CanvasHelper.addPopupMenuOption( canvas, CanvasHelper.createPopupMenuOption({ id: "start-node", label: "Set as start slide", icon: "play", callback: () => this.setStartNode(canvas, selectedNode) }) ); } onNodeMoved(_canvas, node) { const nodeData = node.getData(); if (!nodeData.sideRatio) return; const nodeBBox = node.getBBox(); const nodeSize = { width: nodeBBox.maxX - nodeBBox.minX, height: nodeBBox.maxY - nodeBBox.minY }; const nodeAspectRatio = nodeSize.width / nodeSize.height; if (nodeAspectRatio < nodeData.sideRatio) nodeSize.width = nodeSize.height * nodeData.sideRatio; else nodeSize.height = nodeSize.width / nodeData.sideRatio; node.setData({ ...nodeData, width: nodeSize.width, height: nodeSize.height }); } getStartNode(canvas) { for (const [_, node] of canvas.nodes) { if (node.getData().isStartNode) return node; } return void 0; } setStartNode(canvas, node) { if (!node) return; const startNode = this.getStartNode(canvas); if (startNode) startNode.setData({ ...startNode.getData(), isStartNode: false }); if (node !== startNode) node.setData({ ...node.getData(), isStartNode: true }, true); } getDefaultSlideSize() { const slideSizeString = this.plugin.settings.getSetting("defaultSlideSize"); const slideSizeArray = slideSizeString.split("x").map((value) => parseInt(value)); return { width: slideSizeArray[0], height: slideSizeArray[1] }; } getSlideAspectRatio() { const slideSize = this.getDefaultSlideSize(); return slideSize.width / slideSize.height; } addSlide(canvas, pos, bbox) { const isStartNode = this.getStartNode(canvas) == null; const slideSize = this.getDefaultSlideSize(); const slideAspectRatio = this.getSlideAspectRatio(); if (bbox) { const bboxWidth = bbox.maxX - bbox.minX; const bboxHeight = bbox.maxY - bbox.minY; if (bboxWidth / bboxHeight > slideAspectRatio) { slideSize.width = bboxWidth; slideSize.height = bboxWidth / slideAspectRatio; } else { slideSize.height = bboxHeight; slideSize.width = bboxHeight * slideAspectRatio; } pos = { x: bbox.minX, y: bbox.minY }; } if (!pos) pos = CanvasHelper.getCenterCoordinates(canvas, this.getDefaultSlideSize()); const groupNode = canvas.createGroupNode({ pos, size: slideSize, label: isStartNode ? START_SLIDE_NAME : DEFAULT_SLIDE_NAME, focus: false }); groupNode.setData({ ...groupNode.getData(), sideRatio: slideAspectRatio, isStartNode: isStartNode ? true : void 0 }); } async animateNodeTransition(canvas, fromNode, toNode) { const useCustomZoomFunction = this.plugin.settings.getSetting("zoomToSlideWithoutPadding"); const animationDurationMs = this.plugin.settings.getSetting("slideTransitionAnimationDuration") * 1e3; if (animationDurationMs > 0 && fromNode) { const animationIntensity = this.plugin.settings.getSetting("slideTransitionAnimationIntensity"); const currentNodeBBoxEnlarged = BBoxHelper.scaleBBox(fromNode.getBBox(), animationIntensity); if (useCustomZoomFunction) CanvasHelper.zoomToBBox(canvas, currentNodeBBoxEnlarged); else canvas.zoomToBbox(currentNodeBBoxEnlarged); await sleep(animationDurationMs / 2); const nextNodeBBoxEnlarged = BBoxHelper.scaleBBox(toNode.getBBox(), animationIntensity); if (useCustomZoomFunction) CanvasHelper.zoomToBBox(canvas, nextNodeBBoxEnlarged); else canvas.zoomToBbox(nextNodeBBoxEnlarged); await sleep(animationDurationMs / 2); } let nodeBBox = toNode.getBBox(); if (useCustomZoomFunction) CanvasHelper.zoomToBBox(canvas, nodeBBox); else canvas.zoomToBbox(nodeBBox); } async startPresentation(canvas, tryContinue = false) { if (!tryContinue || this.visitedNodes.length === 0) { const startNode = this.getStartNode(canvas); if (!startNode) { new import_obsidian5.Notice("No start node found. Please mark a node as a start node trough the popup menu."); return; } this.visitedNodes = [startNode]; } this.savedViewport = { x: canvas.tx, y: canvas.ty, zoom: canvas.tZoom }; canvas.wrapperEl.focus(); canvas.wrapperEl.requestFullscreen(); canvas.wrapperEl.classList.add("presentation-mode"); canvas.setReadonly(true); canvas.wrapperEl.onkeydown = (e) => { if (this.plugin.settings.getSetting("useArrowKeysToChangeSlides")) { if (e.key === "ArrowRight") this.nextNode(canvas); else if (e.key === "ArrowLeft") this.previousNode(canvas); } if (this.plugin.settings.getSetting("usePgUpPgDownKeysToChangeSlides")) { if (e.key === "PageDown") this.nextNode(canvas); else if (e.key === "PageUp") this.previousNode(canvas); } }; this.fullscreenModalObserver = new MutationObserver((mutationRecords) => { mutationRecords.forEach((mutationRecord) => { mutationRecord.addedNodes.forEach((node) => { var _a; document.body.removeChild(node); (_a = document.fullscreenElement) == null ? void 0 : _a.appendChild(node); }); }); const inputField = document.querySelector(".prompt-input"); if (inputField) inputField.focus(); }); this.fullscreenModalObserver.observe(document.body, { childList: true }); canvas.wrapperEl.onfullscreenchange = (_e) => { if (document.fullscreenElement) return; this.endPresentation(canvas); }; this.isPresentationMode = true; await sleep(500); this.animateNodeTransition(canvas, void 0, this.visitedNodes.last()); } endPresentation(canvas) { var _a; (_a = this.fullscreenModalObserver) == null ? void 0 : _a.disconnect(); this.fullscreenModalObserver = null; canvas.wrapperEl.onkeydown = null; canvas.wrapperEl.onfullscreenchange = null; canvas.setReadonly(false); canvas.wrapperEl.classList.remove("presentation-mode"); if (document.fullscreenElement) document.exitFullscreen(); if (this.plugin.settings.getSetting("resetViewportOnPresentationEnd")) canvas.setViewport(this.savedViewport.x, this.savedViewport.y, this.savedViewport.zoom); this.isPresentationMode = false; } nextNode(canvas) { var _a; const fromNode = this.visitedNodes.last(); if (!fromNode) return; const outgoingEdges = canvas.getEdgesForNode(fromNode).filter((edge) => edge.from.node === fromNode); let toNode = (_a = outgoingEdges.first()) == null ? void 0 : _a.to.node; if (outgoingEdges.length > 1) { const sortedEdges = outgoingEdges.sort((a, b) => { if (!a.label) return 1; if (!b.label) return -1; return a.label.localeCompare(b.label); }); const traversedEdgesCount = this.visitedNodes.filter((visitedNode) => visitedNode == fromNode).length - 1; const nextEdge = sortedEdges[traversedEdgesCount]; toNode = nextEdge.to.node; } if (toNode) { this.visitedNodes.push(toNode); this.animateNodeTransition(canvas, fromNode, toNode); } else { this.animateNodeTransition(canvas, fromNode, fromNode); } } previousNode(canvas) { const fromNode = this.visitedNodes.pop(); if (!fromNode) return; let toNode = this.visitedNodes.last(); if (!toNode) { toNode = fromNode; this.visitedNodes.push(fromNode); } this.animateNodeTransition(canvas, fromNode, toNode); } }; // src/canvas-extensions/better-readonly-canvas-extension.ts var BetterReadonlyCanvasExtension = class extends CanvasExtension { isEnabled() { return "betterReadonlyEnabled"; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas, _node) => this.updatePopupMenu(canvas) )); let movingToBBox = false; this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.ViewportChanged.Before, (canvas) => { var _a, _b, _c, _d, _e, _f; if (movingToBBox) { movingToBBox = false; this.updateLockedZoom(canvas); this.updateLockedPan(canvas); return; } if (!canvas.readonly) return; if (this.plugin.settings.getSetting("disableZoom")) { canvas.zoom = (_a = canvas.lockedZoom) != null ? _a : canvas.zoom; canvas.tZoom = (_b = canvas.lockedZoom) != null ? _b : canvas.tZoom; } if (this.plugin.settings.getSetting("disablePan")) { canvas.x = (_c = canvas.lockedX) != null ? _c : canvas.x; canvas.tx = (_d = canvas.lockedX) != null ? _d : canvas.tx; canvas.y = (_e = canvas.lockedY) != null ? _e : canvas.y; canvas.ty = (_f = canvas.lockedY) != null ? _f : canvas.ty; } } )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.ZoomToBbox.Before, () => movingToBBox = true )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.ReadonlyChanged, (canvas, _readonly) => { this.updatePopupMenu(canvas); this.updateLockedZoom(canvas); this.updateLockedPan(canvas); } )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.addQuickSettings(canvas) )); } addQuickSettings(canvas) { var _a; const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement; if (!settingsContainer) return; CanvasHelper.addControlMenuButton( settingsContainer, this.createToggle({ id: "disable-node-popup", label: "Disable node popup", icon: "arrow-up-right-from-circle", callback: () => this.updatePopupMenu(canvas) }, "disableNodePopup") ); CanvasHelper.addControlMenuButton( settingsContainer, this.createToggle({ id: "disable-zoom", label: "Disable zoom", icon: "zoom-in", callback: () => this.updateLockedZoom(canvas) }, "disableZoom") ); CanvasHelper.addControlMenuButton( settingsContainer, this.createToggle({ id: "disable-pan", label: "Disable pan", icon: "move", callback: () => this.updateLockedPan(canvas) }, "disablePan") ); } createToggle(menuOption, settingKey) { const toggle = CanvasHelper.createControlMenuButton({ ...menuOption, callback: () => (async () => { var _a; const newValue = !this.plugin.settings.getSetting(settingKey); await this.plugin.settings.setSetting({ [settingKey]: newValue }); toggle.dataset.toggled = this.plugin.settings.getSetting(settingKey).toString(); (_a = menuOption.callback) == null ? void 0 : _a.call(this); })() }); toggle.classList.add("show-while-readonly"); toggle.dataset.toggled = this.plugin.settings.getSetting(settingKey).toString(); return toggle; } updatePopupMenu(canvas) { const hidden = canvas.readonly && this.plugin.settings.getSetting("disableNodePopup"); canvas.menu.menuEl.style.visibility = hidden ? "hidden" : "visible"; } updateLockedZoom(canvas) { canvas.lockedZoom = canvas.tZoom; } updateLockedPan(canvas) { canvas.lockedX = canvas.tx; canvas.lockedY = canvas.ty; } }; // src/utils/modal-helper.ts var import_obsidian6 = require("obsidian"); // src/utils/path-helper.ts var PathHelper = class { static extension(path) { return path.includes(".") ? path.split(".").pop() : void 0; } }; // src/utils/modal-helper.ts var FileNameModal = class extends import_obsidian6.SuggestModal { constructor(app, parentPath, fileExtension) { super(app); this.parentPath = parentPath; this.fileExtension = fileExtension; } getSuggestions(query) { const queryWithoutExtension = query.replace(new RegExp(`\\.${this.fileExtension}$`), ""); if (queryWithoutExtension === "") return []; const queryWithExtension = queryWithoutExtension + "." + this.fileExtension; const suggestions = [`${this.parentPath}/${queryWithExtension}`, queryWithExtension].filter((s) => this.app.vault.getAbstractFileByPath(s) === null); return suggestions; } renderSuggestion(text, el) { el.setText(text); } onChooseSuggestion(_text, _evt) { } awaitInput() { return new Promise((resolve, _reject) => { this.onChooseSuggestion = (text) => { resolve(text); }; this.open(); }); } }; var FileSelectModal = class extends import_obsidian6.SuggestModal { constructor(app, extensionsRegex, suggestNewFile = false) { super(app); this.files = this.app.vault.getFiles().map((file) => file.path).filter((path) => { var _a; return (_a = PathHelper.extension(path)) == null ? void 0 : _a.match(extensionsRegex != null ? extensionsRegex : /.*/); }); this.suggestNewFile = suggestNewFile; this.setPlaceholder("Type to search..."); this.setInstructions([{ command: "\u2191\u2193", purpose: "to navigate" }, { command: "\u21B5", purpose: "to open" }, { command: "shift \u21B5", purpose: "to create" }, { command: "esc", purpose: "to dismiss" }]); this.scope.register(["Shift"], "Enter", (e) => { this.onChooseSuggestion(this.inputEl.value, e); this.close(); }); } getSuggestions(query) { const suggestions = this.files.filter((path) => path.toLowerCase().includes(query.toLowerCase())); if (suggestions.length === 0 && this.suggestNewFile) suggestions.push(query); return suggestions; } renderSuggestion(path, el) { const simplifiedPath = path.replace(/\.md$/, ""); el.setText(simplifiedPath); } onChooseSuggestion(_path, _evt) { } awaitInput() { return new Promise((resolve, _reject) => { this.onChooseSuggestion = (path, _evt) => { const file = this.app.vault.getAbstractFileByPath(path); if (file instanceof import_obsidian6.TFile) return resolve(file); if (!this.suggestNewFile) return; if (PathHelper.extension(path) === void 0) path += ".md"; const newFile = this.app.vault.create(path, ""); resolve(newFile); }; this.open(); }); } }; // src/canvas-extensions/encapsulate-canvas-extension.ts var ENCAPSULATED_FILE_NODE_SIZE = { width: 300, height: 300 }; var EncapsulateCanvasExtension = class extends CanvasExtension { isEnabled() { return "canvasEncapsulationEnabled"; } init() { this.plugin.addCommand({ id: "encapsulate-selection", name: "Encapsulate selection", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly && canvas.selection.size > 0, (canvas) => this.encapsulateSelection(canvas) ) }); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.SelectionContextMenu, (menu, canvas) => { menu.addItem( (item) => item.setTitle("Encapsulate").setIcon("file-plus").onClick(() => this.encapsulateSelection(canvas)) ); } )); } async encapsulateSelection(canvas) { var _a; const selection = canvas.getSelectionData(); const sourceFileFolder = (_a = canvas.view.file.parent) == null ? void 0 : _a.path; if (!sourceFileFolder) return; const targetFilePath = await new FileNameModal( this.plugin.app, sourceFileFolder, "canvas" ).awaitInput(); const newFileData = { nodes: selection.nodes, edges: selection.edges }; const file = await this.plugin.app.vault.create(targetFilePath, JSON.stringify(newFileData, null, 2)); for (const nodeData of selection.nodes) { const node = canvas.nodes.get(nodeData.id); if (node) canvas.removeNode(node); } canvas.createFileNode({ pos: { x: selection.center.x - ENCAPSULATED_FILE_NODE_SIZE.width / 2, y: selection.center.y - ENCAPSULATED_FILE_NODE_SIZE.height / 2 }, size: ENCAPSULATED_FILE_NODE_SIZE, file }); } }; // src/canvas-extensions/commands-canvas-extension.ts var DIRECTIONS = ["up", "down", "left", "right"]; var CommandsCanvasExtension = class extends CanvasExtension { isEnabled() { return "commandsFeatureEnabled"; } init() { this.plugin.addCommand({ id: "create-text-node", name: "Create text node", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly, (canvas) => this.createTextNode(canvas) ) }); this.plugin.addCommand({ id: "create-file-node", name: "Create file node", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly, (canvas) => this.createFileNode(canvas) ) }); this.plugin.addCommand({ id: "select-all-edges", name: "Select all edges", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => true, (canvas) => canvas.updateSelection( () => canvas.selection = new Set(canvas.edges.values()) ) ) }); this.plugin.addCommand({ id: "zoom-to-selection", name: "Zoom to selection", checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => canvas.selection.size > 0, (canvas) => canvas.zoomToSelection() ) }); for (const direction of DIRECTIONS) { this.plugin.addCommand({ id: `clone-node-${direction}`, name: `Clone node ${direction}`, checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => { var _a; return !canvas.readonly && canvas.selection.size === 1 && ((_a = canvas.selection.values().next().value) == null ? void 0 : _a.getData().type) === "text"; }, (canvas) => this.cloneNode(canvas, direction) ) }); this.plugin.addCommand({ id: `expand-node-${direction}`, name: `Expand node ${direction}`, checkCallback: CanvasHelper.canvasCommand( this.plugin, (canvas) => !canvas.readonly && canvas.selection.size === 1, (canvas) => this.expandNode(canvas, direction) ) }); } } createTextNode(canvas) { const size = canvas.config.defaultTextNodeDimensions; const pos = CanvasHelper.getCenterCoordinates(canvas, size); canvas.createTextNode({ pos, size }); } async createFileNode(canvas) { const size = canvas.config.defaultFileNodeDimensions; const pos = CanvasHelper.getCenterCoordinates(canvas, size); const file = await new FileSelectModal(this.plugin.app, void 0, true).awaitInput(); canvas.createFileNode({ pos, size, file }); } cloneNode(canvas, cloneDirection) { const sourceNode = canvas.selection.values().next().value; if (!sourceNode) return; const sourceNodeData = sourceNode.getData(); const nodeMargin = this.plugin.settings.getSetting("cloneNodeMargin"); const offset = { x: (sourceNode.width + nodeMargin) * (cloneDirection === "left" ? -1 : cloneDirection === "right" ? 1 : 0), y: (sourceNode.height + nodeMargin) * (cloneDirection === "up" ? -1 : cloneDirection === "down" ? 1 : 0) }; const clonedNode = canvas.createTextNode({ pos: { x: sourceNode.x + offset.x, y: sourceNode.y + offset.y }, size: { width: sourceNode.width, height: sourceNode.height } }); clonedNode.setData({ ...clonedNode.getData(), color: sourceNodeData.color, styleAttributes: sourceNodeData.styleAttributes }); if (this.plugin.settings.getSetting("zoomToClonedNode")) canvas.zoomToBbox(clonedNode.getBBox()); } expandNode(canvas, expandDirection) { const node = canvas.selection.values().next().value; if (!node) return; const expandNodeStepSize = this.plugin.settings.getSetting("expandNodeStepSize"); const expand = { x: expandDirection === "left" ? -1 : expandDirection === "right" ? 1 : 0, y: expandDirection === "up" ? -1 : expandDirection === "down" ? 1 : 0 }; node.setData({ ...node.getData(), width: node.width + expand.x * expandNodeStepSize, height: node.height + expand.y * expandNodeStepSize }); } }; // src/canvas-extensions/auto-resize-node-canvas-extension.ts var AutoResizeNodeCanvasExtension = class extends CanvasExtension { isEnabled() { return this.plugin.settings.getSetting("autoResizeNodeFeatureEnabled"); } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.onPopupMenuCreated(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeTextContentChanged, (canvas, node, viewUpdate) => this.onNodeTextContentChanged(canvas, node, viewUpdate) )); } onPopupMenuCreated(canvas) { if (canvas.readonly) return; const selectedNodes = [...canvas.selection].filter((element) => { const elementData = element.getData(); return elementData.type === "text"; }); if (selectedNodes.length === 0) return; const hasLockedHeight = selectedNodes.some((node) => node.getData().lockedHeight); CanvasHelper.addPopupMenuOption( canvas, CanvasHelper.createPopupMenuOption({ id: "lock-height", label: "Toggle locked height", icon: hasLockedHeight ? "ruler" : "pencil-ruler", callback: () => this.setLockedHeight(canvas, selectedNodes, hasLockedHeight) }) ); } setLockedHeight(canvas, nodes, lockedHeight) { const newLockedHeight = lockedHeight ? void 0 : true; nodes.forEach((node) => node.setData({ ...node.getData(), lockedHeight: newLockedHeight })); this.onPopupMenuCreated(canvas); } async onNodeTextContentChanged(canvas, node, viewUpdate) { const nodeData = node.getData(); if (nodeData.lockedHeight) return; let textPadding = 0; const cmScroller = viewUpdate.view.dom.querySelector(".cm-scroller"); if (cmScroller) { for (const pseudo of ["before", "after"]) { const style = window.getComputedStyle(cmScroller, pseudo); const height = parseFloat(style.getPropertyValue("height")); textPadding += height; } } let newHeight = viewUpdate.view.contentHeight + textPadding + 10; if (this.plugin.settings.getSetting("autoResizeNodeSnapToGrid")) newHeight = Math.round(newHeight / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE; node.setData({ ...nodeData, height: newHeight }); } }; // src/canvas-extensions/portals-canvas-extension.ts var import_obsidian7 = require("obsidian"); var PORTAL_PADDING = 50; var MIN_OPEN_PORTAL_SIZE = { width: 200, height: 200 }; var PortalsCanvasExtension = class extends CanvasExtension { isEnabled() { return "portalsFeatureEnabled"; } init() { this.plugin.registerEvent(this.plugin.app.vault.on("modify", (file) => { const canvases = this.plugin.app.workspace.getLeavesOfType("canvas").map((leaf) => leaf.view.canvas); for (const canvas of canvases) { if (canvas === void 0) continue; const hasPortalsToFile = canvas.getData().nodes.filter( (nodeData) => nodeData.type === "file" && nodeData.portalToFile === file.path ).length > 0; if (hasPortalsToFile) { canvas.setData(canvas.getData()); canvas.history.current--; canvas.history.data.pop(); } } })); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.updatePopupMenu(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeRemoved, (canvas, node) => this.onNodeRemoved(canvas, node) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeMoved, (canvas, node) => this.onNodeMoved(canvas, node) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DraggingStateChanged, (canvas, startedDragging) => this.onDraggingStateChanged(canvas, startedDragging) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.ContainingNodesRequested, (canvas, bbox, nodes) => this.onContainingNodesRequested(canvas, bbox, nodes) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.SelectionChanged, (canvas, oldSelection, updateSelection) => this.onSelectionChanged(canvas, oldSelection, updateSelection) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DataRequested, (canvas, data) => this.removePortalCanvasData(canvas, data) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.LoadData, (canvas, data, setData) => { this.getCanvasDataWithPortals(canvas, data).then((newData) => { if (newData.nodes.length === data.nodes.length && newData.edges.length === data.edges.length) return; setData(newData); }); } )); } updatePopupMenu(canvas) { if (canvas.readonly) return; const selectedFileNodes = canvas.getSelectionData().nodes.map((nodeData) => { var _a; const node = canvas.nodes.get(nodeData.id); if (!node) return null; if (nodeData.type !== "file") return null; if (((_a = node.file) == null ? void 0 : _a.extension) === "canvas") return node; if (nodeData.portalToFile) this.setPortalOpen(canvas, node, false); return null; }).filter((node) => node !== null); if (selectedFileNodes.length !== 1) return; const portalNode = selectedFileNodes[0]; const portalNodeData = portalNode.getData(); if (portalNodeData.portalToFile && portalNodeData.file !== portalNodeData.portalToFile) { this.setPortalOpen(canvas, portalNode, true); } CanvasHelper.addPopupMenuOption( canvas, CanvasHelper.createPopupMenuOption({ id: "toggle-portal", label: portalNodeData.portalToFile ? "Close portal" : "Open portal", icon: portalNodeData.portalToFile ? "door-open" : "door-closed", callback: () => { this.setPortalOpen(canvas, portalNode, portalNodeData.portalToFile === void 0); this.updatePopupMenu(canvas); } }) ); } setPortalOpen(canvas, portalNode, open) { const portalNodeData = portalNode.getData(); portalNode.setData({ ...portalNodeData, portalToFile: open ? portalNodeData.file : void 0 }); canvas.setData(canvas.getData()); } onNodeRemoved(canvas, node) { var _a, _b, _c, _d; const nodeData = node.getData(); if (nodeData.type !== "file" || !nodeData.portalToFile) return; Object.keys((_b = (_a = nodeData.portalIdMaps) == null ? void 0 : _a.nodeIdMap) != null ? _b : {}).map((refNodeId) => canvas.nodes.get(refNodeId)).filter((node2) => node2 !== void 0).forEach((node2) => canvas.removeNode(node2)); Object.keys((_d = (_c = nodeData.portalIdMaps) == null ? void 0 : _c.edgeIdMap) != null ? _d : {}).map((refEdgeId) => canvas.edges.get(refEdgeId)).filter((edge) => edge !== void 0).forEach((edge) => canvas.removeEdge(edge)); } onContainingNodesRequested(_canvas, _bbox, nodes) { nodes.splice(0, nodes.length, ...nodes.filter((node) => node.getData().portalId === void 0)); } onSelectionChanged(canvas, oldSelection, updateSelection) { updateSelection(() => { const updatedSelection = Array.from(canvas.selection).filter((node) => node.getData().portalId === void 0); canvas.selection = new Set(updatedSelection); }); const previouslySelectedPortalNodesIds = Array.from(oldSelection).filter((node) => node.getData().portalToFile !== void 0).flatMap((node) => { const portalNodeData = node.getData(); const nestedPortalsIds = this.getNestedPortalsIds(canvas, portalNodeData.id); return [portalNodeData.id, ...nestedPortalsIds]; }); for (const node of canvas.nodes.values()) { const nodeData = node.getData(); if (nodeData.portalId === void 0 || !previouslySelectedPortalNodesIds.includes(nodeData.portalId)) continue; node.updateZIndex(); } } getNestedPortalsIds(canvas, portalId) { const nestedPortalsIds = []; for (const node of canvas.nodes.values()) { const nodeData = node.getData(); if (nodeData.portalId === portalId) { nestedPortalsIds.push(nodeData.id); nestedPortalsIds.push(...this.getNestedPortalsIds(canvas, nodeData.id)); } } return nestedPortalsIds; } onDraggingStateChanged(canvas, startedDragging) { var _a; if (!canvas.getSelectionData().nodes.some((node) => node.type === "file" && node.portalToFile)) return; if (startedDragging) { const objectSnappingEnabled = canvas.options.snapToObjects; this.restoreObjectSnappingState = () => canvas.toggleObjectSnapping(objectSnappingEnabled); if (objectSnappingEnabled) canvas.toggleObjectSnapping(false); } else (_a = this.restoreObjectSnappingState) == null ? void 0 : _a.call(this); } onNodeMoved(canvas, node) { const nodeData = node.getData(); if (nodeData.type !== "file" || !nodeData.portalToFile) return; this.onOpenPortalMoved(canvas, node); } onOpenPortalMoved(canvas, portalNode) { var _a; let portalNodeData = portalNode.getData(); const nestedNodesIdMap = (_a = portalNode.getData().portalIdMaps) == null ? void 0 : _a.nodeIdMap; if (!nestedNodesIdMap) return; const nestedNodes = Object.keys(nestedNodesIdMap).map((refNodeId) => canvas.nodes.get(refNodeId)).filter((node) => node !== void 0); const sourceBBox = CanvasHelper.getBBox(nestedNodes); const targetSize = this.getPortalSize(sourceBBox); if (portalNodeData.width !== targetSize.width || portalNodeData.height !== targetSize.height) { portalNode.setData({ ...portalNodeData, width: targetSize.width, height: targetSize.height }); return; } const portalOffset = { x: portalNodeData.x - sourceBBox.minX + PORTAL_PADDING, y: portalNodeData.y - sourceBBox.minY + PORTAL_PADDING }; for (const nestedNode of nestedNodes) { const nestedNodeData = nestedNode.getData(); nestedNode.setData({ ...nestedNodeData, x: nestedNodeData.x + portalOffset.x, y: nestedNodeData.y + portalOffset.y }); } } removePortalCanvasData(_canvas, data) { var _a, _b; data.edges = data.edges.filter((edgeData) => { var _a2, _b2; if (edgeData.portalId !== void 0) return false; const fromNodeData = data.nodes.find((nodeData) => nodeData.id === edgeData.fromNode); const toNodeData = data.nodes.find((nodeData) => nodeData.id === edgeData.toNode); if (!fromNodeData || !toNodeData) return true; if (fromNodeData.portalId === void 0 && toNodeData.portalId === void 0) { return true; } else if (fromNodeData.portalId !== void 0 && toNodeData.portalId !== void 0) { return false; } else { const fromPortalNodeData = fromNodeData.portalId !== void 0 ? fromNodeData : toNodeData; const notFromPortalNodeData = fromNodeData.portalId !== void 0 ? toNodeData : fromNodeData; notFromPortalNodeData.edgesToNodeFromPortal = (_a2 = notFromPortalNodeData.edgesToNodeFromPortal) != null ? _a2 : {}; notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId] = (_b2 = notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId]) != null ? _b2 : []; notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId].push(edgeData); return false; } }); data.nodes = data.nodes.filter((nodeData) => nodeData.portalId === void 0); for (const portalNodeData of data.nodes) { if (portalNodeData.type !== "file") continue; if (this.plugin.settings.getSetting("maintainClosedPortalSize")) { portalNodeData.width = (_a = portalNodeData.closedPortalWidth) != null ? _a : portalNodeData.width; portalNodeData.height = (_b = portalNodeData.closedPortalHeight) != null ? _b : portalNodeData.height; } delete portalNodeData.closedPortalWidth; delete portalNodeData.closedPortalHeight; delete portalNodeData.portalIdMaps; } } async getCanvasDataWithPortals(canvas, dataRef) { const data = JSON.parse(JSON.stringify(dataRef)); const addedData = await Promise.all(data.nodes.map((nodeData) => this.tryOpenPortal(canvas, nodeData))); for (const newData of addedData) { data.nodes.push(...newData.nodes); data.edges.push(...newData.edges); } for (const originNodeData of data.nodes) { if (originNodeData.edgesToNodeFromPortal === void 0) continue; for (const [relativePortalId, edges] of Object.entries(originNodeData.edgesToNodeFromPortal)) { const idPrefix = originNodeData.portalId ? `${originNodeData.portalId}-` : ""; const portalId = `${idPrefix}${relativePortalId}`; const targetPortalData = data.nodes.find((nodeData) => nodeData.id === portalId); if (!targetPortalData) { delete originNodeData.edgesToNodeFromPortal[portalId]; continue; } if (targetPortalData.portalToFile) { data.edges.push(...edges.map((edge) => ({ ...edge, portalId: originNodeData.portalId, fromNode: `${idPrefix}${edge.fromNode}`, toNode: `${idPrefix}${edge.toNode}` }))); delete originNodeData.edgesToNodeFromPortal[portalId]; } else if (this.plugin.settings.getSetting("showEdgesIntoDisabledPortals")) { data.edges.push(...edges.map((edge) => { const fromNodeId = `${idPrefix}${edge.fromNode}`; const fromNode = data.nodes.find((nodeData) => nodeData.id === fromNodeId); const toNodeId = `${idPrefix}${edge.toNode}`; return { ...edge, fromNode: fromNode ? fromNodeId : portalId, toNode: fromNode ? portalId : toNodeId, portalId // Mark it as temporary }; })); } } if (Object.keys(originNodeData.edgesToNodeFromPortal).length === 0) delete originNodeData.edgesToNodeFromPortal; } return data; } async tryOpenPortal(canvas, portalNodeData) { var _a, _b; const addedData = { nodes: [], edges: [] }; if (portalNodeData.type !== "file" || !portalNodeData.portalToFile) return addedData; portalNodeData.portalToFile = portalNodeData.file; if (portalNodeData.portalToFile === canvas.view.file.path) { portalNodeData.portalToFile = void 0; return addedData; } const portalFile = this.plugin.app.vault.getAbstractFileByPath(portalNodeData.file); if (!(portalFile instanceof import_obsidian7.TFile) || portalFile.extension !== "canvas") { portalNodeData.portalToFile = void 0; return addedData; } const portalFileDataString = await this.plugin.app.vault.cachedRead(portalFile); if (portalFileDataString === "") return addedData; const portalFileData = JSON.parse(portalFileDataString); if (!portalFileData) { portalNodeData.portalToFile = void 0; return addedData; } portalNodeData.portalIdMaps = { nodeIdMap: {}, edgeIdMap: {} }; const sourceMinCoordinates = CanvasHelper.getBBox(portalFileData.nodes); const portalOffset = { x: portalNodeData.x - sourceMinCoordinates.minX + PORTAL_PADDING, y: portalNodeData.y - sourceMinCoordinates.minY + PORTAL_PADDING }; for (const nodeDataFromPortal of portalFileData.nodes) { const refNodeId = `${portalNodeData.id}-${nodeDataFromPortal.id}`; portalNodeData.portalIdMaps.nodeIdMap[refNodeId] = nodeDataFromPortal.id; const addedNode = { ...nodeDataFromPortal, id: refNodeId, x: nodeDataFromPortal.x + portalOffset.x, y: nodeDataFromPortal.y + portalOffset.y, portalId: portalNodeData.id }; addedData.nodes.push(addedNode); const nestedNodes = await this.tryOpenPortal(canvas, addedNode); addedData.nodes.push(...nestedNodes.nodes); addedData.edges.push(...nestedNodes.edges); } for (const edgeDataFromPortal of portalFileData.edges) { const refEdgeId = `${portalNodeData.id}-${edgeDataFromPortal.id}`; portalNodeData.portalIdMaps.edgeIdMap[refEdgeId] = edgeDataFromPortal.id; const fromRefNode = (_a = Object.entries(portalNodeData.portalIdMaps.nodeIdMap).find(([_refNodeId, nodeId]) => nodeId === edgeDataFromPortal.fromNode)) == null ? void 0 : _a[0]; const toRefNode = (_b = Object.entries(portalNodeData.portalIdMaps.nodeIdMap).find(([_refNodeId, nodeId]) => nodeId === edgeDataFromPortal.toNode)) == null ? void 0 : _b[0]; if (!fromRefNode || !toRefNode) continue; addedData.edges.push({ ...edgeDataFromPortal, id: refEdgeId, fromNode: fromRefNode, toNode: toRefNode, portalId: portalNodeData.id }); } const nestedNodesBBox = CanvasHelper.getBBox(addedData.nodes); const targetSize = this.getPortalSize(nestedNodesBBox); portalNodeData.closedPortalWidth = portalNodeData.width; portalNodeData.closedPortalHeight = portalNodeData.height; portalNodeData.width = targetSize.width; portalNodeData.height = targetSize.height; return addedData; } getPortalSize(sourceBBox) { const sourceSize = { width: sourceBBox.maxX - sourceBBox.minX, height: sourceBBox.maxY - sourceBBox.minY }; const targetSize = { width: Math.max(sourceSize.width + PORTAL_PADDING * 2, MIN_OPEN_PORTAL_SIZE.width), height: Math.max(sourceSize.height + PORTAL_PADDING * 2, MIN_OPEN_PORTAL_SIZE.height) }; if (!Number.isFinite(targetSize.width)) targetSize.width = MIN_OPEN_PORTAL_SIZE.width; if (!Number.isFinite(targetSize.height)) targetSize.height = MIN_OPEN_PORTAL_SIZE.height; return targetSize; } }; // src/canvas-extensions/better-default-settings-canvas-extension.ts var BetterDefaultSettingsCanvasExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.modifyCanvasSettings(this.plugin.getCurrentCanvas()); this.plugin.registerEvent(this.plugin.app.workspace.on( SettingsManager.SETTINGS_CHANGED_EVENT, () => this.modifyCanvasSettings(this.plugin.getCurrentCanvas()) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.modifyCanvasSettings(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DoubleClick, (canvas, event, preventDefault) => this.onDoubleClick(canvas, event, preventDefault) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeCreated, (canvas, node) => { this.enforceNodeGridAlignment(canvas, node); this.applyDefaultNodeStyles(canvas, node); } )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeCreated, (canvas, edge) => this.applyDefaultEdgeStyles(canvas, edge) )); } modifyCanvasSettings(canvas) { if (!canvas) return; canvas.config.defaultTextNodeDimensions = { width: this.plugin.settings.getSetting("defaultTextNodeWidth"), height: this.plugin.settings.getSetting("defaultTextNodeHeight") }; canvas.config.defaultFileNodeDimensions = { width: this.plugin.settings.getSetting("defaultFileNodeWidth"), height: this.plugin.settings.getSetting("defaultFileNodeHeight") }; canvas.config.minContainerDimension = this.plugin.settings.getSetting("minNodeSize"); } async onDoubleClick(canvas, event, preventDefault) { if (event.defaultPrevented || event.target !== canvas.wrapperEl || canvas.isDragging || canvas.readonly) return; preventDefault.value = true; let pos = canvas.posFromEvt(event); switch (this.plugin.settings.getSetting("nodeTypeOnDoubleClick")) { case "file": const file = await new FileSelectModal(this.plugin.app, void 0, true).awaitInput(); canvas.createFileNode({ pos, position: "center", file }); break; default: canvas.createTextNode({ pos, position: "center" }); break; } } enforceNodeGridAlignment(canvas, node) { if (!this.plugin.settings.getSetting("alignNewNodesToGrid")) return; const nodeData = node.getData(); node.setData({ ...nodeData, x: Math.round(nodeData.x / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE, y: Math.round(nodeData.y / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE }); } applyDefaultNodeStyles(_canvas, node) { const nodeData = node.getData(); if (nodeData.type !== "text") return; node.setData({ ...nodeData, styleAttributes: { ...nodeData.styleAttributes, ...this.plugin.settings.getSetting("defaultTextNodeStyleAttributes") } }); } async applyDefaultEdgeStyles(canvas, edge) { const edgeData = edge.getData(); edge.setData({ ...edgeData, styleAttributes: { ...edgeData.styleAttributes, ...this.plugin.settings.getSetting("defaultEdgeStyleAttributes") } }); if (canvas.canvasEl.hasClass("is-connecting")) { await new Promise((resolve) => { new MutationObserver(() => { if (!canvas.canvasEl.hasClass("is-connecting")) resolve(); }).observe(canvas.canvasEl, { attributes: true, attributeFilter: ["class"] }); }); } const lineDirection = this.plugin.settings.getSetting("defaultEdgeLineDirection"); edge.setData({ ...edge.getData(), fromEnd: lineDirection === "bidirectional" ? "arrow" : "none", toEnd: lineDirection === "nondirectional" ? "none" : "arrow" }); } }; // src/canvas-extensions/color-palette-canvas-extension.ts var DEFAULT_COLORS_COUNT = 6; var CUSTOM_COLORS_MOD_STYLES_ID = "mod-custom-colors"; var ColorPaletteCanvasExtension = class extends CanvasExtension { constructor() { super(...arguments); this.observer = null; } isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( "window-open", (_win, _window) => this.updateCustomColorModStyleClasses() )); this.plugin.registerEvent(this.plugin.app.workspace.on( "css-change", () => this.updateCustomColorModStyleClasses() )); this.updateCustomColorModStyleClasses(); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.patchColorSelection(canvas) )); this.plugin.register(() => { var _a; return (_a = this.observer) == null ? void 0 : _a.disconnect(); }); } updateCustomColorModStyleClasses() { var _a; const customCss = this.getCustomColors().map((colorId) => ` .mod-canvas-color-${colorId} { --canvas-color: var(--canvas-color-${colorId}); } `).join(""); for (const win of this.plugin.windowsManager.windows) { const doc = win.document; (_a = doc.getElementById(CUSTOM_COLORS_MOD_STYLES_ID)) == null ? void 0 : _a.remove(); const customColorModStyle = doc.createElement("style"); customColorModStyle.id = CUSTOM_COLORS_MOD_STYLES_ID; doc.head.appendChild(customColorModStyle); customColorModStyle.textContent = customCss; } } patchColorSelection(canvas) { if (this.observer) this.observer.disconnect(); this.observer = new MutationObserver((mutations) => { const colorMenuOpened = mutations.some( (mutation) => Object.values(mutation.addedNodes).some( (node) => node instanceof HTMLElement && node.classList.contains("canvas-submenu") && Object.values(node.childNodes).some( (node2) => node2 instanceof HTMLElement && node2.classList.contains("canvas-color-picker-item") ) ) ); if (!colorMenuOpened) return; const submenu = canvas.menu.menuEl.querySelector(".canvas-submenu"); if (!submenu) return; const currentNodeColor = canvas.getSelectionData().nodes.map((node) => node.color).last(); for (const colorId of this.getCustomColors()) { const customColorMenuItem = this.createColorMenuItem(canvas, colorId); if (currentNodeColor === colorId) customColorMenuItem.classList.add("is-active"); submenu.insertBefore(customColorMenuItem, submenu.lastChild); } }); this.observer.observe(canvas.menu.menuEl, { childList: true }); } createColorMenuItem(canvas, colorId) { const menuItem = document.createElement("div"); menuItem.classList.add("canvas-color-picker-item"); menuItem.classList.add(`mod-canvas-color-${colorId}`); menuItem.addEventListener("click", () => { menuItem.classList.add("is-active"); for (const item of canvas.selection) { item.setColor(colorId); } canvas.requestSave(); }); return menuItem; } getCustomColors() { const colors = []; while (true) { const colorId = (DEFAULT_COLORS_COUNT + colors.length + 1).toString(); if (!getComputedStyle(document.body).getPropertyValue(`--canvas-color-${colorId}`)) break; colors.push(colorId); } return colors; } }; // src/canvas-extensions/collapsible-groups-canvas-extension.ts var import_obsidian8 = require("obsidian"); var COLLAPSE_BUTTON_ID = "group-collapse-button"; var CollapsibleGroupsCanvasExtension = class extends CanvasExtension { isEnabled() { return "collapsibleGroupsFeatureEnabled"; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeChanged, (canvas, node) => this.onNodeChanged(canvas, node) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeBBoxRequested, (canvas, node, bbox) => this.onNodeBBoxRequested(canvas, node, bbox) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.OnCopy, (canvas, selectionData) => this.onCopy(canvas, selectionData) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DataRequested, (_canvas, data) => this.expandCollapsedNodes(data) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.LoadData, (_canvas, data, _setData) => this.collapseNodes(data) )); } onNodeChanged(canvas, groupNode) { var _a, _b; const groupNodeData = groupNode.getData(); if (groupNodeData.type !== "group") return; (_a = groupNode.nodeEl) == null ? void 0 : _a.querySelectorAll(`#${COLLAPSE_BUTTON_ID}`).forEach((el) => el.remove()); const collapseButton = document.createElement("span"); collapseButton.id = COLLAPSE_BUTTON_ID; (0, import_obsidian8.setIcon)(collapseButton, groupNodeData.isCollapsed ? "plus-circle" : "minus-circle"); collapseButton.onclick = () => { this.setCollapsed(canvas, groupNode, groupNode.getData().isCollapsed ? void 0 : true); }; (_b = groupNode.labelEl) == null ? void 0 : _b.insertAdjacentElement("afterend", collapseButton); } onCopy(canvas, selectionData) { for (const collapsedGroupData of selectionData.nodes) { if (collapsedGroupData.type !== "group" || !collapsedGroupData.isCollapsed || !collapsedGroupData.collapsedData) continue; selectionData.nodes.push(...collapsedGroupData.collapsedData.nodes.map((nodeData) => ({ ...nodeData, // Restore the relative position of the node to the group x: nodeData.x + collapsedGroupData.x, y: nodeData.y + collapsedGroupData.y }))); selectionData.edges.push(...collapsedGroupData.collapsedData.edges); } } setCollapsed(canvas, groupNode, collapsed) { groupNode.setData({ ...groupNode.getData(), isCollapsed: collapsed }); canvas.setData(canvas.getData()); canvas.history.current--; canvas.pushHistory(canvas.getData()); } onNodeBBoxRequested(_canvas, node, bbox) { var _a, _b; const nodeData = node.getData(); if (nodeData.type !== "group" || !nodeData.isCollapsed) return; bbox.maxX = bbox.minX + ((_b = (_a = node.nodeEl) == null ? void 0 : _a.getBoundingClientRect().width) != null ? _b : 0); bbox.maxY = bbox.minY; } expandCollapsedNodes(data) { data.nodes = data.nodes.flatMap((groupNodeData) => { const collapsedData = groupNodeData.collapsedData; if (collapsedData === void 0) return [groupNodeData]; groupNodeData.collapsedData = void 0; data.edges.push(...collapsedData.edges); return [groupNodeData, ...collapsedData.nodes.map((nodeData) => ({ ...nodeData, // Restore the relative position of the node to the group x: nodeData.x + groupNodeData.x, y: nodeData.y + groupNodeData.y }))]; }); } collapseNodes(data) { data.nodes.forEach((groupNodeData) => { if (!groupNodeData.isCollapsed) return; const groupNodeBBox = CanvasHelper.getBBox([groupNodeData]); const containedNodesData = data.nodes.filter( (nodeData) => nodeData.id !== groupNodeData.id && BBoxHelper.insideBBox(CanvasHelper.getBBox([nodeData]), groupNodeBBox, false) ); const containedEdgesData = data.edges.filter((edgeData) => { return containedNodesData.some((nodeData) => nodeData.id === edgeData.fromNode) || containedNodesData.some((nodeData) => nodeData.id === edgeData.toNode); }); data.nodes = data.nodes.filter((nodeData) => !containedNodesData.includes(nodeData)); data.edges = data.edges.filter((edgeData) => !containedEdgesData.includes(edgeData)); groupNodeData.collapsedData = { nodes: containedNodesData.map((nodeData) => ({ ...nodeData, // Store the relative position of the node to the group x: nodeData.x - groupNodeData.x, y: nodeData.y - groupNodeData.y })), edges: containedEdgesData }; }); } }; // src/canvas-extensions/properties-canvas-extension.ts var import_obsidian9 = require("obsidian"); var PropertiesCanvasExtension = class extends CanvasExtension { constructor() { super(...arguments); this.previousCssclasses = []; } isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.onCanvasChanged(canvas) )); } onCanvasChanged(canvas) { var _a; this.updateCssClasses(canvas); const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement; if (!settingsContainer) return; CanvasHelper.addControlMenuButton( settingsContainer, CanvasHelper.createControlMenuButton({ id: "properties", label: "Properties", icon: "settings-2", callback: () => this.openPropertiesDialog(canvas) }) ); } updateCssClasses(canvas) { var _a, _b; this.previousCssclasses.forEach((cssclass) => { canvas.wrapperEl.classList.remove(cssclass); }); this.previousCssclasses = ((_b = (_a = canvas.metadata) == null ? void 0 : _a.properties) == null ? void 0 : _b.cssclasses) || []; this.previousCssclasses.forEach((cssclass) => { canvas.wrapperEl.classList.add(cssclass); }); } async openPropertiesDialog(canvas) { await new PropertiesModal(this.plugin.app, canvas).awaitDialog(); this.updateCssClasses(canvas); } }; var PropertiesModal = class extends import_obsidian9.Modal { constructor(app, canvas) { super(app); this.canvas = canvas; } onOpen() { new import_obsidian9.Setting(this.contentEl).setHeading().setName("Properties"); new import_obsidian9.Setting(this.contentEl).setClass("properties-field").setName("cssclasses").setTooltip("Add classes to the canvas wrapper element. Separate multiple classes with spaces.").addText( (text) => { var _a, _b; return text.setValue((_b = (_a = this.canvas.metadata.properties) == null ? void 0 : _a.cssclasses) == null ? void 0 : _b.join(" ")).onChange((value) => { var _a2, _b2; this.canvas.metadata.properties = (_b2 = (_a2 = this.canvas.metadata) == null ? void 0 : _a2.properties) != null ? _b2 : {}; this.canvas.metadata.properties.cssclasses = value.split(" "); }); } ); } onClose() { } awaitDialog() { return new Promise((resolve) => { this.onClose = () => { this.contentEl.empty(); resolve(); }; this.open(); }); } }; // src/canvas-extensions/focus-mode-canvas-extension.ts var CONTROL_MENU_FOCUS_TOGGLE_ID = "focus-mode-toggle"; var FocusModeCanvasExtension = class extends CanvasExtension { isEnabled() { return "focusModeFeatureEnabled"; } init() { this.plugin.addCommand({ id: "toggle-focus-mode", name: "Toggle Focus Mode", checkCallback: CanvasHelper.canvasCommand( this.plugin, (_canvas) => true, (canvas) => this.toggleFocusMode(canvas) ) }); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.addControlMenuToggle(canvas) )); } addControlMenuToggle(canvas) { var _a; const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement; if (!settingsContainer) return; const controlMenuFocusToggle = CanvasHelper.createControlMenuButton({ id: CONTROL_MENU_FOCUS_TOGGLE_ID, label: "Focus Mode", icon: "focus", callback: () => this.toggleFocusMode(canvas) }); CanvasHelper.addControlMenuButton(settingsContainer, controlMenuFocusToggle); } toggleFocusMode(canvas) { var _a, _b; const controlMenuFocusToggle = (_b = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.querySelector(`#${CONTROL_MENU_FOCUS_TOGGLE_ID}`); if (!controlMenuFocusToggle) return; const newValue = controlMenuFocusToggle.dataset.toggled !== "true"; canvas.wrapperEl.dataset.focusModeEnabled = newValue.toString(); controlMenuFocusToggle.dataset.toggled = newValue.toString(); } }; // src/canvas-extensions/advanced-styles/node-styles.ts var NodeStylesExtension = class extends CanvasExtension { isEnabled() { return "nodeStylingFeatureEnabled"; } init() { this.allNodeStyles = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customNodeStyleAttributes")]; this.plugin.registerEvent(this.plugin.app.workspace.on( SettingsManager.SETTINGS_CHANGED_EVENT, () => this.allNodeStyles = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customNodeStyleAttributes")] )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.onPopupMenuCreated(canvas) )); } onPopupMenuCreated(canvas) { var _a; const selectionNodeData = canvas.getSelectionData().nodes; if (canvas.readonly || selectionNodeData.length === 0 || selectionNodeData.length !== canvas.selection.size) return; const selectedNodeTypes = new Set(selectionNodeData.map((node) => node.type)); const availableNodeStyles = this.allNodeStyles.filter((style) => !style.nodeTypes || style.nodeTypes.some((type) => selectedNodeTypes.has(type))); CanvasHelper.addStyleAttributesToPopup( this.plugin, canvas, availableNodeStyles, (_a = selectionNodeData[0].styleAttributes) != null ? _a : {}, (attribute, value) => this.setStyleAttributeForSelection(canvas, attribute, value) ); } setStyleAttributeForSelection(canvas, attribute, value) { const selectionNodeData = canvas.getSelectionData().nodes; for (const nodeData of selectionNodeData) { const node = canvas.nodes.get(nodeData.id); if (!node) continue; if (attribute.nodeTypes && !attribute.nodeTypes.includes(nodeData.type)) continue; node.setData({ ...nodeData, styleAttributes: { ...nodeData.styleAttributes, [attribute.datasetKey]: value } }); } canvas.pushHistory(canvas.getData()); } }; // src/canvas-extensions/advanced-styles/edge-pathfinding-methods/edge-pathfinding-method.ts var EdgePathfindingMethod = class { }; // src/utils/svg-path-helper.ts var SvgPathHelper = class { static pathArrayToSvgPath(positions, rounded = false) { const tension = 0.2; let newPositions = [...positions]; if (rounded && positions.length > 2) { newPositions = [positions[0]]; for (let i = 1; i < positions.length - 2; i++) { const p1 = positions[i]; const p2 = positions[i + 1]; const p3 = positions[i + 2]; const t1 = (1 - tension) / 2; const t2 = 1 - t1; const x = t2 * t2 * t2 * p1.x + 3 * t2 * t2 * t1 * p2.x + 3 * t2 * t1 * t1 * p3.x + t1 * t1 * t1 * p2.x; const y = t2 * t2 * t2 * p1.y + 3 * t2 * t2 * t1 * p2.y + 3 * t2 * t1 * t1 * p3.y + t1 * t1 * t1 * p2.y; newPositions.push({ x, y }); } const lastPoint = positions[positions.length - 1]; newPositions.push(lastPoint); } for (let i = 0; i < newPositions.length - 2; i++) { const p1 = newPositions[i]; const p2 = newPositions[i + 1]; const p3 = newPositions[i + 2]; const currentDirection = { x: p2.x - p1.x, y: p2.y - p1.y }; const nextDirection = { x: p3.x - p2.x, y: p3.y - p2.y }; if (currentDirection.x !== nextDirection.x && currentDirection.y !== nextDirection.y) continue; newPositions.splice(i + 1, 1); i--; } return newPositions.map( (position, index) => `${index === 0 ? "M" : "L"} ${position.x} ${position.y}` ).join(" "); } }; // src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-direct.ts var EdgePathfindingDirect = class extends EdgePathfindingMethod { getPath(_plugin, _canvas, fromPos, _fromBBoxSidePos, _fromSide, toPos, _toBBoxSidePos, _toSide, _isDragging) { return { svgPath: SvgPathHelper.pathArrayToSvgPath([fromPos, toPos], false), center: { x: (fromPos.x + toPos.x) / 2, y: (fromPos.y + toPos.y) / 2 }, rotateArrows: true }; } }; // src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-square.ts var EdgePathfindingSquare = class extends EdgePathfindingMethod { getPath(_plugin, _canvas, fromPos, fromBBoxSidePos, fromSide, toPos, toBBoxSidePos, toSide, _isDragging) { let pathArray = []; let center = { x: 0, y: 0 }; if (fromSide === toSide) { const direction = BBoxHelper.direction(fromSide); if (BBoxHelper.isHorizontal(fromSide)) { pathArray = [ fromPos, { x: Math.max(fromBBoxSidePos.x, toBBoxSidePos.x) + direction * CanvasHelper.GRID_SIZE, y: fromBBoxSidePos.y }, { x: Math.max(fromBBoxSidePos.x, toBBoxSidePos.x) + direction * CanvasHelper.GRID_SIZE, y: toBBoxSidePos.y }, toPos ]; } else { pathArray = [ fromPos, { x: fromBBoxSidePos.x, y: Math.max(fromBBoxSidePos.y, toBBoxSidePos.y) + direction * CanvasHelper.GRID_SIZE }, { x: toBBoxSidePos.x, y: Math.max(fromBBoxSidePos.y, toBBoxSidePos.y) + direction * CanvasHelper.GRID_SIZE }, toPos ]; } center = { x: (pathArray[1].x + pathArray[2].x) / 2, y: (pathArray[1].y + pathArray[2].y) / 2 }; } else if (BBoxHelper.isHorizontal(fromSide) === BBoxHelper.isHorizontal(toSide)) { if (BBoxHelper.isHorizontal(fromSide)) { pathArray = [ fromPos, { x: fromBBoxSidePos.x + (toBBoxSidePos.x - fromBBoxSidePos.x) / 2, y: fromBBoxSidePos.y }, { x: fromBBoxSidePos.x + (toBBoxSidePos.x - fromBBoxSidePos.x) / 2, y: toBBoxSidePos.y }, toPos ]; } else { pathArray = [ fromPos, { x: fromBBoxSidePos.x, y: fromBBoxSidePos.y + (toBBoxSidePos.y - fromBBoxSidePos.y) / 2 }, { x: toBBoxSidePos.x, y: fromBBoxSidePos.y + (toBBoxSidePos.y - fromBBoxSidePos.y) / 2 }, toPos ]; } center = { x: (fromBBoxSidePos.x + toBBoxSidePos.x) / 2, y: (fromBBoxSidePos.y + toBBoxSidePos.y) / 2 }; } else { if (BBoxHelper.isHorizontal(fromSide)) { pathArray = [ fromPos, { x: toBBoxSidePos.x, y: fromBBoxSidePos.y }, toPos ]; } else { pathArray = [ fromPos, { x: fromBBoxSidePos.x, y: toBBoxSidePos.y }, toPos ]; } center = { x: pathArray[1].x, y: pathArray[1].y }; } return { svgPath: SvgPathHelper.pathArrayToSvgPath(pathArray, false), center, rotateArrows: false }; } }; // src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-a-star.ts var DIRECTIONS2 = [ { dx: 1, dy: 0 }, { dx: -1, dy: 0 }, { dx: 0, dy: 1 }, { dx: 0, dy: -1 }, { dx: 1, dy: 1 }, { dx: -1, dy: 1 }, { dx: 1, dy: -1 }, { dx: -1, dy: -1 } ]; var DIAGONAL_COST = Math.sqrt(2); var Node = class { constructor(x, y) { this.x = x; this.y = y; this.gCost = 0; this.hCost = 0; this.fCost = 0; this.parent = null; } // Only check for x and y, not gCost, hCost, fCost, or parent inList(nodes) { return nodes.some((n) => n.x === this.x && n.y === this.y); } }; var EdgePathfindingAStar = class extends EdgePathfindingMethod { getPath(plugin, canvas, fromPos, _fromBBoxSidePos, fromSide, toPos, _toBBoxSidePos, toSide, isDragging) { if (isDragging && !plugin.settings.getSetting("edgeStylePathfinderPathLiveUpdate")) return null; const nodeBBoxes = [...canvas.nodes.values()].filter((node) => { const nodeData = node.getData(); const isGroup = nodeData.type === "group"; const isOpenPortal = nodeData.portalToFile !== void 0; return !isGroup && !isOpenPortal; }).map((node) => node.getBBox()); const fromPosWithMargin = BBoxHelper.moveInDirection(fromPos, fromSide, 10); const toPosWithMargin = BBoxHelper.moveInDirection(toPos, toSide, 10); const gridResolution = plugin.settings.getSetting("edgeStylePathfinderGridResolution"); const pathArray = this.aStarAlgorithm(fromPosWithMargin, fromSide, toPosWithMargin, toSide, nodeBBoxes, gridResolution); if (!pathArray) return null; pathArray.splice(0, 0, fromPos); pathArray.splice(pathArray.length, 0, toPos); const roundedPath = plugin.settings.getSetting("edgeStylePathfinderPathRounded"); const svgPath = SvgPathHelper.pathArrayToSvgPath(pathArray, roundedPath); return { svgPath, center: pathArray[Math.floor(pathArray.length / 2)], rotateArrows: false }; } aStarAlgorithm(fromPos, fromSide, toPos, toSide, obstacles, gridResolution) { const start = new Node( Math.floor(fromPos.x / gridResolution) * gridResolution, Math.floor(fromPos.y / gridResolution) * gridResolution ); if (fromSide === "right" && fromPos.x !== start.x) start.x += gridResolution; if (fromSide === "bottom" && fromPos.y !== start.y) start.y += gridResolution; const end = new Node( Math.floor(toPos.x / gridResolution) * gridResolution, Math.floor(toPos.y / gridResolution) * gridResolution ); if (toSide === "right" && toPos.x !== end.x) end.x += gridResolution; if (toSide === "bottom" && toPos.y !== end.y) end.y += gridResolution; if (this.isInsideObstacle(start, obstacles) || this.isInsideObstacle(end, obstacles)) return null; const openSet = [start]; const closedSet = []; while (openSet.length > 0) { let current = null; let lowestFCost = Infinity; for (const node of openSet) { if (node.fCost < lowestFCost) { current = node; lowestFCost = node.fCost; } } if (!current) return null; openSet.splice(openSet.indexOf(current), 1); closedSet.push(current); if (current.x === end.x && current.y === end.y) { return [fromPos, ...this.reconstructPath(current), toPos].map((node) => ({ x: node.x, y: node.y })); } if (!(current.x === start.x && current.y === start.y) && this.isTouchingObstacle(current, obstacles)) continue; for (const neighbor of this.getPossibleNeighbors(current, obstacles, gridResolution)) { if (neighbor.inList(closedSet)) continue; const tentativeGCost = current.gCost + this.getMovementCost({ dx: neighbor.x - current.x, dy: neighbor.y - current.y }); if (!neighbor.inList(openSet) || tentativeGCost < neighbor.gCost) { neighbor.parent = current; neighbor.gCost = tentativeGCost; neighbor.hCost = this.heuristic(neighbor, end); neighbor.fCost = neighbor.gCost + neighbor.hCost; openSet.push(neighbor); } } } return null; } // Manhattan distance heuristic(node, end) { return Math.abs(node.x - end.x) + Math.abs(node.y - end.y); } // Define a function to check if a position isn't inside any obstacle isTouchingObstacle(node, obstacles) { return obstacles.some((obstacle) => BBoxHelper.insideBBox(node, obstacle, true)); } isInsideObstacle(node, obstacles) { return obstacles.some((obstacle) => BBoxHelper.insideBBox(node, obstacle, false)); } // Define a function to calculate movement cost based on direction getMovementCost(direction) { return direction.dx !== 0 && direction.dy !== 0 ? DIAGONAL_COST : 1; } getPossibleNeighbors(node, obstacles, gridResolution) { const neighbors = []; for (const direction of DIRECTIONS2) { const neighbor = new Node( node.x + direction.dx * gridResolution, node.y + direction.dy * gridResolution ); neighbor.gCost = node.gCost + this.getMovementCost(direction); if (this.isInsideObstacle(neighbor, obstacles)) continue; neighbors.push(neighbor); } return neighbors; } reconstructPath(node) { const path = []; while (node) { path.push(node); node = node.parent; } return path.reverse(); } }; // src/canvas-extensions/advanced-styles/edge-styles.ts var EDGE_PATHFINDING_METHODS = { "direct": EdgePathfindingDirect, "square": EdgePathfindingSquare, "a-star": EdgePathfindingAStar }; var EdgeStylesExtension = class extends CanvasExtension { isEnabled() { return "edgesStylingFeatureEnabled"; } init() { this.allEdgeStyleAttributes = [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customEdgeStyleAttributes")]; this.plugin.registerEvent(this.plugin.app.workspace.on( SettingsManager.SETTINGS_CHANGED_EVENT, () => this.allEdgeStyleAttributes = [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customEdgeStyleAttributes")] )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.PopupMenuCreated, (canvas) => this.onPopupMenuCreated(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeChanged, (canvas, edge) => this.onEdgeChanged(canvas, edge) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeCenterRequested, (canvas, edge, center) => this.onEdgeCenterRequested(canvas, edge, center) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeAdded, (canvas, _node) => this.updateAllEdges(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeMoved, (canvas, _node) => this.updateAllEdges(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeRemoved, (canvas, _node) => this.updateAllEdges(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DraggingStateChanged, (canvas, isDragging) => { if (isDragging) return; this.updateAllEdges(canvas); } )); } onPopupMenuCreated(canvas) { var _a; const selectedEdges = [...canvas.selection].filter((item) => item.path !== void 0); if (canvas.readonly || selectedEdges.length === 0 || selectedEdges.length !== canvas.selection.size) return; CanvasHelper.addStyleAttributesToPopup( this.plugin, canvas, this.allEdgeStyleAttributes, (_a = selectedEdges[0].getData().styleAttributes) != null ? _a : {}, (attribute, value) => this.setStyleAttributeForSelection(canvas, attribute, value) ); } setStyleAttributeForSelection(canvas, attribute, value) { const selectedEdges = [...canvas.selection].filter((item) => item.path !== void 0); for (const edge of selectedEdges) { const edgeData = edge.getData(); edge.setData({ ...edgeData, styleAttributes: { ...edgeData.styleAttributes, [attribute.datasetKey]: value } }); } canvas.pushHistory(canvas.getData()); } updateAllEdges(canvas) { for (const edge of canvas.edges.values()) { this.onEdgeChanged(canvas, edge); } } onEdgeChanged(canvas, edge) { var _a, _b, _c, _d, _e, _f, _g; const edgeData = edge.getData(); if (!edge.bezier) return; edge.center = void 0; edge.updatePath(); const pathfindingMethod = (_a = edgeData.styleAttributes) == null ? void 0 : _a.pathfindingMethod; if (pathfindingMethod) { const fromBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(edge.from.node.getBBox(), edge.from.side); const fromPos = edge.from.end === "none" ? fromBBoxSidePos : edge.bezier.from; const toBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(edge.to.node.getBBox(), edge.to.side); const toPos = edge.to.end === "none" ? toBBoxSidePos : edge.bezier.to; const path = new EDGE_PATHFINDING_METHODS[pathfindingMethod]().getPath(this.plugin, canvas, fromPos, fromBBoxSidePos, edge.from.side, toPos, toBBoxSidePos, edge.to.side, canvas.isDragging); if (!path) return; edge.center = path.center; edge.path.interaction.setAttr("d", path == null ? void 0 : path.svgPath); edge.path.display.setAttr("d", path == null ? void 0 : path.svgPath); } (_b = edge.labelElement) == null ? void 0 : _b.render(); const arrowPolygonPoints = this.getArrowPolygonPoints((_c = edgeData.styleAttributes) == null ? void 0 : _c.arrow); if ((_d = edge.fromLineEnd) == null ? void 0 : _d.el) (_e = edge.fromLineEnd.el.querySelector("polygon")) == null ? void 0 : _e.setAttribute("points", arrowPolygonPoints); if ((_f = edge.toLineEnd) == null ? void 0 : _f.el) (_g = edge.toLineEnd.el.querySelector("polygon")) == null ? void 0 : _g.setAttribute("points", arrowPolygonPoints); if (this.plugin.settings.getSetting("edgeStyleDirectRotateArrow")) { this.rotateArrows(edge, pathfindingMethod); } } onEdgeCenterRequested(_canvas, edge, center) { var _a, _b, _c, _d; center.x = (_b = (_a = edge.center) == null ? void 0 : _a.x) != null ? _b : center.x; center.y = (_d = (_c = edge.center) == null ? void 0 : _c.y) != null ? _d : center.y; } getArrowPolygonPoints(arrowStyle) { if (arrowStyle === "halved-triangle") return `-2,0 7.5,12 -2,12`; else if (arrowStyle === "thin-triangle") return `0,0 7,10 0,0 0,10 0,0 -7,10`; else if (arrowStyle === "diamond" || arrowStyle === "diamond-outline") return `0,0 5,10 0,20 -5,10`; else if (arrowStyle === "circle" || arrowStyle === "circle-outline") return `0 0, 4.95 1.8, 7.5 6.45, 6.6 11.7, 2.7 15, -2.7 15, -6.6 11.7, -7.5 6.45, -4.95 1.8`; else return `0,0 6.5,10.4 -6.5,10.4`; } rotateArrows(edge, pathRouteType) { var _a, _b, _c, _d; if (pathRouteType !== "direct") { if ((_a = edge.fromLineEnd) == null ? void 0 : _a.el) edge.fromLineEnd.el.style.translate = ""; if ((_b = edge.toLineEnd) == null ? void 0 : _b.el) edge.toLineEnd.el.style.translate = ""; return; } const setArrowRotation = (element, side, rotation) => { element.style.transform = element.style.transform.replace(/rotate\([-\d]+(deg|rad)\)/g, `rotate(${rotation}rad)`); const offset = BBoxHelper.getSideVector(side); element.style.translate = `${offset.x * 7}px ${offset.y * -7}px`; }; const edgeRotation = Math.atan2(edge.bezier.to.y - edge.bezier.from.y, edge.bezier.to.x - edge.bezier.from.x) - Math.PI / 2; if ((_c = edge.fromLineEnd) == null ? void 0 : _c.el) setArrowRotation(edge.fromLineEnd.el, edge.from.side, edgeRotation); if ((_d = edge.toLineEnd) == null ? void 0 : _d.el) setArrowRotation(edge.toLineEnd.el, edge.to.side, edgeRotation - Math.PI); } }; // src/canvas-extensions/dataset-exposers/node-exposer.ts function getExposedNodeData(settings) { const exposedData = []; if (settings.getSetting("nodeStylingFeatureEnabled")) exposedData.push("styleAttributes"); if (settings.getSetting("collapsibleGroupsFeatureEnabled")) exposedData.push("isCollapsed"); if (settings.getSetting("presentationFeatureEnabled")) exposedData.push("isStartNode"); if (settings.getSetting("portalsFeatureEnabled")) exposedData.push("portalToFile", "portalId"); return exposedData; } var NodeExposerExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeChanged, (_canvas, node) => { const nodeData = node == null ? void 0 : node.getData(); if (!nodeData) return; for (const exposedDataKey of getExposedNodeData(this.plugin.settings)) { const datasetPairs = nodeData[exposedDataKey] instanceof Object ? Object.entries(nodeData[exposedDataKey]) : [[exposedDataKey, nodeData[exposedDataKey]]]; for (const [key, value] of datasetPairs) { if (!value) delete node.nodeEl.dataset[key]; else node.nodeEl.dataset[key] = value; } } } )); } }; // src/canvas-extensions/dataset-exposers/node-interaction-exposer.ts var TARGET_NODE_DATASET_PREFIX = "target"; var NodeInteractionExposerExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.NodeInteraction, (canvas, node) => { const nodeData = node == null ? void 0 : node.getData(); if (!nodeData) return; const interactionEl = canvas.nodeInteractionLayer.interactionEl; if (!interactionEl) return; for (const exposedDataKey of getExposedNodeData(this.plugin.settings)) { const datasetPairs = nodeData[exposedDataKey] instanceof Object ? Object.entries(nodeData[exposedDataKey]) : [[exposedDataKey, nodeData[exposedDataKey]]]; for (const [key, value] of datasetPairs) { const modifiedKey = TARGET_NODE_DATASET_PREFIX + key.toString().charAt(0).toUpperCase() + key.toString().slice(1); if (!value) delete interactionEl.dataset[modifiedKey]; else interactionEl.dataset[modifiedKey] = value; } } } )); } }; // src/canvas-extensions/dataset-exposers/edge-exposer.ts function getExposedEdgeData(settings) { const exposedData = []; if (settings.getSetting("edgesStylingFeatureEnabled")) exposedData.push("styleAttributes"); return exposedData; } var EdgeExposerExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.EdgeChanged, (_canvas, edge) => { var _a, _b, _c, _d; const edgeData = edge == null ? void 0 : edge.getData(); if (!edgeData) return; for (const exposedDataKey of getExposedEdgeData(this.plugin.settings)) { const datasetPairs = edgeData[exposedDataKey] instanceof Object ? Object.entries(edgeData[exposedDataKey]) : [[exposedDataKey, edgeData[exposedDataKey]]]; for (const [key, value] of datasetPairs) { if (!value) { delete edge.path.display.dataset[key]; if ((_a = edge.fromLineEnd) == null ? void 0 : _a.el) delete edge.fromLineEnd.el.dataset[key]; if ((_b = edge.toLineEnd) == null ? void 0 : _b.el) delete edge.toLineEnd.el.dataset[key]; } else { edge.path.display.dataset[key] = value; if ((_c = edge.fromLineEnd) == null ? void 0 : _c.el) edge.fromLineEnd.el.dataset[key] = value; if ((_d = edge.toLineEnd) == null ? void 0 : _d.el) edge.toLineEnd.el.dataset[key] = value; } } } } )); } }; // src/canvas-extensions/dataset-exposers/canvas-wrapper-exposer.ts var EXPOSED_SETTINGS = [ "disableFontSizeRelativeToZoom", "collapsibleGroupsFeatureEnabled", "collapsedGroupPreviewOnDrag" ]; var CanvasWrapperExposerExtension = class extends CanvasExtension { isEnabled() { return true; } init() { this.plugin.registerEvent(this.plugin.app.workspace.on( SettingsManager.SETTINGS_CHANGED_EVENT, () => this.updateExposedSettings(this.plugin.getCurrentCanvas()) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => this.updateExposedSettings(canvas) )); this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.DraggingStateChanged, (canvas, dragging) => { if (dragging) canvas.wrapperEl.dataset.isDragging = "true"; else delete canvas.wrapperEl.dataset.isDragging; } )); } updateExposedSettings(canvas) { if (!canvas) return; for (const setting of EXPOSED_SETTINGS) { canvas.wrapperEl.dataset[setting] = this.plugin.settings.getSetting(setting).toString(); } } }; // src/utils/migration-helper.ts var MigrationHelper = class { constructor(plugin) { this.plugin = plugin; } async migrate() { this.migrateNodeAndEdgeStyles(); } migrateNodeAndEdgeStyles() { this.plugin.registerEvent(this.plugin.app.workspace.on( CanvasEvent.CanvasChanged, (canvas) => { for (const node of canvas.nodes.values()) { const nodeData = node.getData(); const newStyleAttributes = {}; if (nodeData.isSticker) newStyleAttributes["border"] = "invisible"; if (nodeData.borderStyle) newStyleAttributes["border"] = nodeData.borderStyle; if (nodeData.shape) { newStyleAttributes.textAlign = "center"; newStyleAttributes.shape = nodeData.shape; if ((newStyleAttributes == null ? void 0 : newStyleAttributes.shape) === "centered-rectangle") delete newStyleAttributes.shape; if ((newStyleAttributes == null ? void 0 : newStyleAttributes.shape) === "oval") newStyleAttributes.shape = "pill"; } delete nodeData.isSticker; delete nodeData.borderStyle; delete nodeData.shape; node.setData({ ...nodeData, styleAttributes: { ...nodeData.styleAttributes, ...newStyleAttributes } }); } for (const edge of canvas.edges.values()) { const edgeData = edge.getData(); const newStyleAttributes = {}; if (edgeData.edgeStyle) newStyleAttributes.edge = edgeData.edgeStyle; if (edgeData.edgePathRoute) newStyleAttributes.pathfindingMethod = edgeData.edgePathRoute; delete edgeData.edgeStyle; delete edgeData.edgePathRoute; } } )); } }; // src/main.ts var CANVAS_EXTENSIONS = [ // Dataset Exposers CanvasWrapperExposerExtension, NodeExposerExtension, EdgeExposerExtension, NodeInteractionExposerExtension, // Advanced Styles NodeStylesExtension, EdgeStylesExtension, // Basic Extensions BetterDefaultSettingsCanvasExtension, CommandsCanvasExtension, BetterReadonlyCanvasExtension, AutoResizeNodeCanvasExtension, PropertiesCanvasExtension, GroupCanvasExtension, // More Advanced Extensions CollapsibleGroupsCanvasExtension, FocusModeCanvasExtension, EncapsulateCanvasExtension, ColorPaletteCanvasExtension, PresentationCanvasExtension, PortalsCanvasExtension ]; var AdvancedCanvasPlugin = class extends import_obsidian10.Plugin { async onload() { this.migrationHelper = new MigrationHelper(this); await this.migrationHelper.migrate(); IconsHelper.addIcons(); this.settings = new SettingsManager(this); await this.settings.loadSettings(); this.settings.addSettingsTab(); this.windowsManager = new WindowsManager(this); this.canvasPatcher = new CanvasPatcher(this); this.canvasExtensions = CANVAS_EXTENSIONS.map((Extension) => new Extension(this)); } onunload() { } getCurrentCanvasView() { const canvasView = this.app.workspace.getActiveViewOfType(import_obsidian10.ItemView); if ((canvasView == null ? void 0 : canvasView.getViewType()) !== "canvas") return null; return canvasView; } getCurrentCanvas() { var _a; return ((_a = this.getCurrentCanvasView()) == null ? void 0 : _a.canvas) || null; } createFileSnapshot(path, content) { var _a; const fileRecoveryPlugin = (_a = this.app.internalPlugins.plugins["file-recovery"]) == null ? void 0 : _a.instance; if (!fileRecoveryPlugin) return; fileRecoveryPlugin.forceAdd(path, content); } // this.app.plugins.plugins["advanced-canvas"].enableDebugMode() enableDebugMode() { if (this.debugHelper) return; this.debugHelper = new DebugHelper(this); } }; /* nosourcemap */