/*
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": `<rect rx="31.25" height="62.5" width="93.75" y="18.75" x="3.125" stroke-width="8.333" stroke="currentColor" fill="transparent"/>`,
  "shape-parallelogram": `<rect transform="skewX(-20)" rx="5" height="50" width="70" y="25" x="35" stroke-width="8.333" stroke="currentColor" fill="transparent"/>`,
  "shape-predefined-process": `
    <g stroke-width="2" stroke="currentColor" fill="none" transform="matrix(4.166667,0,0,4.166667,0,0)">
      <path d="M 4.999687 3 L 19.000312 3 C 20.104688 3 21 3.895312 21 4.999687 L 21 19.000312 C 21 20.104688 20.104688 21 19.000312 21 L 4.999687 21 C 3.895312 21 3 20.104688 3 19.000312 L 3 4.999687 C 3 3.895312 3.895312 3 4.999687 3 Z M 4.999687 3 "/>
      <path d="M 7 3 L 7 21 "/>
      <path d="M 17 3 L 17 21 "/>
    </g>
  `,
  "shape-document": `<path transform="translate(0, 5)" stroke="currentColor" fill="none" stroke-width="8.333" d="M83.75 25C85.82 25 87.5 26.68 87.5 28.75L87.5 64.375Q68.75 54.25 50 64.375 31.25 74.5 12.5 64.375L12.5 30.625 12.5 28.75C12.5 26.68 14.18 25 16.25 25Z"/>`,
  "shape-database": `
    <g transform="translate(20, 20)" stroke-width="8.333" stroke="currentColor" fill="none">
      <path d="M 1 51 L 1 11 C 1 5.48 14.43 1 31 1 C 47.57 1 61 5.48 61 11 L 61 51 C 61 56.52 47.57 61 31 61 C 14.43 61 1 56.52 1 51 Z"/>
      <path d="M 1 11 C 1 16.52 14.43 21 31 21 C 47.57 21 61 16.52 61 11"/>
    </g>
  `,
  "border-solid": `<path stroke="currentColor" fill="none" stroke-width="8.333" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
  "border-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.333" stroke-dasharray="13.7" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
  "border-dotted": `<path stroke="currentColor" fill="none" stroke-width="8.333" stroke-dasharray="8.7" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
  "path-solid": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
  "path-dotted": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="8.8" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
  "path-short-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="15" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
  "path-long-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="23" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
  "arrow-triangle": `<path stroke="currentColor" fill="currentColor" d="M 15 10 L 85 50 L 15 90 Z"/>`,
  "arrow-triangle-outline": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 15 10 L 85 50 L 15 90 Z"/>`,
  "arrow-thin-triangle": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 15 10 L 85 50 L 15 90"/>`,
  "arrow-halved-triangle": `<path stroke="currentColor" fill="currentColor" d="M 15 50 L 85 50 L 15 90 Z"/>`,
  "arrow-diamond": `<path stroke="currentColor" fill="currentColor" d="M 50 0 L 100 50 L 50 100 L 0 50 Z"/>`,
  "arrow-diamond-outline": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 50 0 L 100 50 L 50 100 L 0 50 Z"/>`,
  "arrow-circle": `<circle stroke="currentColor" fill="currentColor" cx="50" cy="50" r="45"/>`,
  "arrow-circle-outline": `<circle stroke="currentColor" stroke-width="8.5" fill="none" cx="50" cy="50" r="45"/>`,
  "pathfinding-method-bezier": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
  "pathfinding-method-square": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M72.9167 79.1667 72.9167 50 27.0833 50 27.0833 20.8333"/>`
};
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, ";
    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 */