/*
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_obsidian19 = require("obsidian");

// src/utils/icons-helper.ts
var import_obsidian = 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"/>`,
  "arrows-selected": `
    <g stroke-width="2" stroke="currentColor" fill="none">
      <defs>
        <marker id="arrow-right" markerWidth="10" markerHeight="7" refX="2" refY="3.5" orient="auto"> <polygon points="2 2, 5 3.5, 2 5" /> </marker>
        <marker id="arrow-left" markerWidth="10" markerHeight="7" refX="4" refY="3.5" orient="auto"> <polygon points="1 3.5, 4 2, 4 5" /> </marker>
      </defs>
      <rect height="100" width="100" stroke-width="15" stroke="currentColor" stroke-dasharray="8,8" fill="transparent"/>
      <line x1="20" y1="30" x2="60" y2="30" stroke-width="5" marker-end="url(#arrow-right)"/>
      <line x1="40" y1="70" x2="80" y2="70" stroke-width="5" marker-start="url(#arrow-left)"/>
    </g>
  `,
  "arrow-right-selected": `
    <g stroke-width="2" stroke="currentColor" fill="none">
      <defs>
          <marker id="arrow-right" markerWidth="10" markerHeight="7" refX="2" refY="3.5" orient="auto"> <polygon points="2 2, 5 3.5, 2 5" /> </marker>
      </defs>
      <rect height="100" width="100" stroke-width="15" stroke="currentColor" stroke-dasharray="8,8" fill="transparent"/>
      <line x1="20" y1="50" x2="60" y2="50" stroke-width="5" marker-end="url(#arrow-right)"/>
    </g>
  `,
  "arrow-left-selected": `
    <g stroke-width="2" stroke="currentColor" fill="none">
      <defs>
          <marker id="arrow-left" markerWidth="10" markerHeight="7" refX="4" refY="3.5" orient="auto"> <polygon points="1 3.5, 4 2, 4 5" /> </marker>
      </defs>
      <rect height="100" width="100" stroke-width="15" stroke="currentColor" stroke-dasharray="8,8" fill="transparent"/>
      <line x1="40" y1="50" x2="80" y2="50" stroke-width="5" marker-start="url(#arrow-left)"/>
    </g>
  `
};
var IconsHelper = class {
  static addIcons() {
    for (const [id, svg] of Object.entries(CUSTOM_ICONS)) {
      (0, import_obsidian.addIcon)(id, svg);
    }
  }
};

// src/utils/debug-helper.ts
var DebugHelper = class {
  constructor(plugin) {
    this.logging = true;
    this.nodeAddedCount = 0;
    this.nodeChangedCount = 0;
    this.edgeAddedCount = 0;
    this.edgeChangedCount = 0;
    this.plugin = plugin;
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (_canvas) => {
        this.nodeAddedCount = 0;
        this.nodeChangedCount = 0;
        this.edgeAddedCount = 0;
        this.edgeChangedCount = 0;
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-added",
      (_canvas, _node) => {
        if (this.logging) console.count("\u{1F7E2} NodeAdded");
        this.nodeAddedCount++;
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-changed",
      (_canvas, _node) => {
        if (this.logging) console.count("\u{1F7E1} NodeChanged");
        this.nodeChangedCount++;
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-added",
      (_canvas, _edge) => {
        if (this.logging) console.count("\u{1F7E2} EdgeAdded");
        this.edgeAddedCount++;
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-changed",
      (_canvas, _edge) => {
        if (this.logging) console.count("\u{1F7E1} EdgeChanged");
        this.edgeChangedCount++;
      }
    ));
  }
  resetEfficiency() {
    this.nodeAddedCount = 0;
    this.nodeChangedCount = 0;
    this.edgeAddedCount = 0;
    this.edgeChangedCount = 0;
  }
  logEfficiency() {
    const canvas = this.plugin.getCurrentCanvas();
    if (!canvas) return;
    console.log("NodeAdded Efficiency:", this.nodeAddedCount / canvas.nodes.size);
    console.log("NodeChanged Efficiency:", this.nodeChangedCount / canvas.nodes.size);
    console.log("EdgeAdded Efficiency:", this.edgeAddedCount / canvas.edges.size);
    console.log("EdgeChanged Efficiency:", this.edgeChangedCount / canvas.edges.size);
  }
  static markBBox(canvas, bbox, duration = -1) {
    const node = canvas.createTextNode({
      pos: { x: bbox.minX, y: bbox.minY },
      size: { width: bbox.maxX - bbox.minX, height: bbox.maxY - bbox.minY },
      text: "",
      focus: false
    });
    node.setData({
      ...node.getData(),
      id: "debug-bbox",
      color: "1",
      styleAttributes: {
        border: "invisible"
      }
    });
    if (duration >= 0) {
      setTimeout(() => {
        canvas.removeNode(node);
      }, duration);
    }
  }
};

// src/settings.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 (const 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) {
    const diffX = (scale - 1) * (bbox.maxX - bbox.minX);
    const 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 isColliding(bbox1, bbox2) {
    return bbox1.minX <= bbox2.maxX && bbox1.maxX >= bbox2.minX && bbox1.minY <= bbox2.maxY && bbox1.maxY >= bbox2.minY;
  }
  static insideBBox(position, bbox, canTouchEdge) {
    if ("x" in position) {
      const x = position.x, y = position.y;
      return canTouchEdge ? x >= bbox.minX && x <= bbox.maxX && y >= bbox.minY && y <= bbox.maxY : x > bbox.minX && x < bbox.maxX && y > bbox.minY && y < bbox.maxY;
    }
    return canTouchEdge ? position.minX >= bbox.minX && position.maxX <= bbox.maxX && position.minY >= bbox.minY && position.maxY <= bbox.maxY : position.minX > bbox.minX && position.maxX < bbox.maxX && position.minY > bbox.minY && position.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 getOppositeSide(side) {
    switch (side) {
      case "top":
        return "bottom";
      case "right":
        return "left";
      case "bottom":
        return "top";
      case "left":
        return "right";
    }
  }
  static isHorizontal(side) {
    return side === "left" || side === "right";
  }
  static direction(side) {
    return side === "right" || side === "bottom" ? 1 : -1;
  }
};

// src/utils/canvas-helper.ts
var import_obsidian2 = require("obsidian");
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_obsidian2.setIcon)(quickSetting, menuOption.icon);
    (0, import_obsidian2.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_obsidian2.setIcon)(menuOptionElement, menuOption.icon);
    (0, import_obsidian2.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_obsidian2.setIcon)(menuOptionElement, menuOption.icon);
    (0, import_obsidian2.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(canvasElements) {
    const bBoxes = canvasElements.map((element) => {
      if (element.getBBox) return element.getBBox();
      const nodeData = element;
      if (nodeData.x !== void 0 && nodeData.y !== void 0 && nodeData.width !== void 0 && nodeData.height !== void 0)
        return { minX: nodeData.x, minY: nodeData.y, maxX: nodeData.x + nodeData.width, maxY: nodeData.y + nodeData.height };
      return null;
    }).filter((bbox) => bbox !== null);
    return BBoxHelper.combineBBoxes(bBoxes);
  }
  static getSmallestAllowedZoomBBox(canvas, bbox) {
    if (canvas.screenshotting) return bbox;
    if (canvas.canvasRect.width === 0 || canvas.canvasRect.height === 0) return bbox;
    const widthZoom = canvas.canvasRect.width / (bbox.maxX - bbox.minX);
    const heightZoom = canvas.canvasRect.height / (bbox.maxY - bbox.minY);
    const requiredZoom = Math.min(widthZoom, heightZoom);
    if (requiredZoom > _CanvasHelper.MAX_ALLOWED_ZOOM) {
      const scaleFactor = requiredZoom / _CanvasHelper.MAX_ALLOWED_ZOOM;
      return BBoxHelper.scaleBBox(bbox, scaleFactor);
    }
    return bbox;
  }
  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.key] === option.value)) != null ? _a : stylableAttribute.options.find((value) => value.value === null);
      if (!selectedStyle) {
        console.warn(`No "null" style option found for stylable attribute "${stylableAttribute.key}"`);
        continue;
      }
      const menuOption = _CanvasHelper.createExpandablePopupMenuOption({
        id: `menu-option-${stylableAttribute.key}`,
        label: stylableAttribute.label,
        icon: selectedStyle.icon
      }, stylableAttribute.options.map((styleOption) => ({
        label: styleOption.label,
        icon: styleOption.icon,
        callback: () => {
          setStyleAttribute(stylableAttribute, styleOption.value);
          currentStyleAttributes[stylableAttribute.key] = styleOption.value;
          (0, import_obsidian2.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_obsidian2.setIcon)(styleMenuButtonElement, "paintbrush");
    (0, import_obsidian2.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.key] === option.value)) != null ? _c : stylableAttribute.options.find((value) => value.value === null);
        if (!selectedStyle) continue;
        (0, import_obsidian2.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_obsidian2.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 = this.createDropdownOptionElement({
              label: styleOption.label,
              icon: styleOption.icon,
              callback: () => {
                setStyleAttribute(stylableAttribute, styleOption.value);
                currentStyleAttributes[stylableAttribute.key] = styleOption.value;
                selectedStyle = styleOption;
                (0, import_obsidian2.setIcon)(iconElement, styleOption.icon);
                styleMenuDropdownSubmenuElement.remove();
              }
            });
            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_obsidian2.setIcon)(selectedIconElement, "check");
              styleMenuDropdownSubmenuOptionElement.appendChild(selectedIconElement);
            }
            styleMenuDropdownSubmenuElement.appendChild(styleMenuDropdownSubmenuOptionElement);
          }
          popupMenuElement.appendChild(styleMenuDropdownSubmenuElement);
        });
      }
      popupMenuElement.appendChild(styleMenuDropdownElement);
    });
  }
  static createDropdownOptionElement(menuOption) {
    const menuDropdownOptionElement = document.createElement("div");
    menuDropdownOptionElement.classList.add("menu-item");
    menuDropdownOptionElement.classList.add("tappable");
    const iconElement = document.createElement("div");
    iconElement.classList.add("menu-item-icon");
    (0, import_obsidian2.setIcon)(iconElement, menuOption.icon);
    menuDropdownOptionElement.appendChild(iconElement);
    const labelElement = document.createElement("div");
    labelElement.classList.add("menu-item-title");
    labelElement.textContent = menuOption.label;
    menuDropdownOptionElement.appendChild(labelElement);
    menuDropdownOptionElement.addEventListener("pointerenter", () => {
      menuDropdownOptionElement.classList.add("selected");
    });
    menuDropdownOptionElement.addEventListener("pointerleave", () => {
      menuDropdownOptionElement.classList.remove("selected");
    });
    menuDropdownOptionElement.addEventListener("click", () => {
      var _a;
      (_a = menuOption.callback) == null ? void 0 : _a.call(menuOption);
    });
    return menuDropdownOptionElement;
  }
  static createDropdownSeparatorElement() {
    const separatorElement = document.createElement("div");
    separatorElement.classList.add("menu-separator");
    return separatorElement;
  }
  static alignToGrid(value, gridSize = this.GRID_SIZE) {
    return Math.round(value / gridSize) * gridSize;
  }
  static getBestSideForFloatingEdge(sourcePos, target) {
    const targetBBox = target.getBBox();
    const possibleSides = ["top", "right", "bottom", "left"];
    const possibleTargetPos = possibleSides.map((side) => [side, BBoxHelper.getCenterOfBBoxSide(targetBBox, side)]);
    let bestSide = null;
    let bestDistance = Infinity;
    for (const [side, pos] of possibleTargetPos) {
      const distance = Math.sqrt(Math.pow(sourcePos.x - pos.x, 2) + Math.pow(sourcePos.y - pos.y, 2));
      if (distance < bestDistance) {
        bestDistance = distance;
        bestSide = side;
      }
    }
    return bestSide;
  }
  static selectEdgesForNodes(canvas, direction) {
    const selection = canvas.getSelectionData();
    if (selection.nodes.length === 0) return;
    const edges = /* @__PURE__ */ new Set();
    for (const nodeData of selection.nodes) {
      const node = canvas.nodes.get(nodeData.id);
      if (!node) continue;
      for (const edge of canvas.getEdgesForNode(node)) {
        switch (direction) {
          case "connected":
            edges.add(edge);
            break;
          case "incoming":
            if (edge.to.node === node) edges.add(edge);
            break;
          case "outgoing":
            if (edge.from.node === node) edges.add(edge);
            break;
        }
      }
    }
    canvas.updateSelection(() => {
      canvas.selection = edges;
    });
  }
};
_CanvasHelper.GRID_SIZE = 20;
_CanvasHelper.MAX_ALLOWED_ZOOM = 1;
var CanvasHelper = _CanvasHelper;

// src/canvas-extensions/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/utils/svg-path-helper.ts
var SvgPathHelper = class {
  static smoothenPathArray(positions, tension) {
    let newPositions = [...positions];
    if (positions.length <= 2) return newPositions;
    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);
    return newPositions;
  }
  static pathArrayToSvgPath(positions) {
    for (let i = 0; i < positions.length - 2; i++) {
      const p1 = positions[i];
      const p2 = positions[i + 1];
      const p3 = positions[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;
      positions.splice(i + 1, 1);
      i--;
    }
    return positions.map(
      (position, index) => `${index === 0 ? "M" : "L"} ${position.x} ${position.y}`
    ).join(" ");
  }
  static pathArrayToRoundedSvgPath(pathArray, targetRadius) {
    if (pathArray.length < 3)
      return this.pathArrayToSvgPath(pathArray);
    pathArray = pathArray.filter((position, index) => {
      if (index === 0) return true;
      const previous = pathArray[index - 1];
      return !(position.x === previous.x && position.y === previous.y);
    });
    const commands = [];
    commands.push(`M ${pathArray[0].x} ${pathArray[0].y}`);
    for (let i = 1; i < pathArray.length - 1; i++) {
      const previous = pathArray[i - 1];
      const current = pathArray[i];
      const next = pathArray[i + 1];
      const prevDelta = { x: current.x - previous.x, y: current.y - previous.y };
      const nextDelta = { x: next.x - current.x, y: next.y - current.y };
      const prevLen = Math.sqrt(prevDelta.x * prevDelta.x + prevDelta.y * prevDelta.y);
      const nextLen = Math.sqrt(nextDelta.x * nextDelta.x + nextDelta.y * nextDelta.y);
      const prevUnit = prevLen ? { x: prevDelta.x / prevLen, y: prevDelta.y / prevLen } : { x: 0, y: 0 };
      const nextUnit = nextLen ? { x: nextDelta.x / nextLen, y: nextDelta.y / nextLen } : { x: 0, y: 0 };
      let dot = prevUnit.x * nextUnit.x + prevUnit.y * nextUnit.y;
      dot = Math.max(-1, Math.min(1, dot));
      const angle = Math.acos(dot);
      if (angle < 0.01 || Math.abs(Math.PI - angle) < 0.01) {
        commands.push(`L ${current.x} ${current.y}`);
        continue;
      }
      const desiredOffset = targetRadius * Math.tan(angle / 2);
      const d = Math.min(desiredOffset, prevLen / 2, nextLen / 2);
      const effectiveRadius = d / Math.tan(angle / 2);
      const firstAnchor = {
        x: current.x - prevUnit.x * d,
        y: current.y - prevUnit.y * d
      };
      const secondAnchor = {
        x: current.x + nextUnit.x * d,
        y: current.y + nextUnit.y * d
      };
      commands.push(`L ${firstAnchor.x} ${firstAnchor.y}`);
      const cross = prevDelta.x * nextDelta.y - prevDelta.y * nextDelta.x;
      const sweepFlag = cross < 0 ? 0 : 1;
      commands.push(`A ${effectiveRadius} ${effectiveRadius} 0 0 ${sweepFlag} ${secondAnchor.x} ${secondAnchor.y}`);
    }
    const last = pathArray[pathArray.length - 1];
    commands.push(`L ${last.x} ${last.y}`);
    return commands.join(" ");
  }
};

// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/edge-pathfinding-method.ts
var EdgePathfindingMethod = class {
  constructor(plugin, canvas, fromNodeBBox, fromPos, fromBBoxSidePos, fromSide, toNodeBBox, toPos, toBBoxSidePos, toSide) {
    this.plugin = plugin;
    this.canvas = canvas;
    this.fromNodeBBox = fromNodeBBox;
    this.fromPos = fromPos;
    this.fromBBoxSidePos = fromBBoxSidePos;
    this.fromSide = fromSide;
    this.toNodeBBox = toNodeBBox;
    this.toPos = toPos;
    this.toBBoxSidePos = toBBoxSidePos;
    this.toSide = toSide;
  }
};

// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-a-star.ts
var MAX_MS_CALCULATION = 100;
var BASIC_DIRECTIONS = [
  { dx: 1, dy: 0 },
  { dx: -1, dy: 0 },
  { dx: 0, dy: 1 },
  { dx: 0, dy: -1 }
];
var DIAGONAL_DIRECTIONS = [
  { dx: 1, dy: 1 },
  { dx: -1, dy: 1 },
  { dx: 1, dy: -1 },
  { dx: -1, dy: -1 }
];
var DIAGONAL_COST = Math.sqrt(2);
var ROUND_PATH_RADIUS = 5;
var SMOOTHEN_PATH_TENSION = 0.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() {
    const nodeBBoxes = [...this.canvas.nodes.values()].filter((node) => {
      const nodeData = node.getData();
      if (nodeData.portal === true) return false;
      const nodeBBox = node.getBBox();
      const nodeContainsFromPos = BBoxHelper.insideBBox(this.fromPos, nodeBBox, true);
      const nodeContainsToPos = BBoxHelper.insideBBox(this.toPos, nodeBBox, true);
      return !nodeContainsFromPos && !nodeContainsToPos;
    }).map((node) => node.getBBox());
    const fromPosWithMargin = BBoxHelper.moveInDirection(this.fromPos, this.fromSide, 10);
    const toPosWithMargin = BBoxHelper.moveInDirection(this.toPos, this.toSide, 10);
    const allowDiagonal = this.plugin.settings.getSetting("edgeStylePathfinderAllowDiagonal");
    const pathArray = this.aStarAlgorithm(fromPosWithMargin, toPosWithMargin, nodeBBoxes, CanvasHelper.GRID_SIZE / 2, allowDiagonal);
    if (!pathArray) return null;
    pathArray.splice(0, 0, this.fromPos);
    pathArray.splice(pathArray.length, 0, this.toPos);
    let svgPath;
    const roundPath = this.plugin.settings.getSetting("edgeStylePathfinderPathRounded");
    if (roundPath) {
      if (allowDiagonal)
        svgPath = SvgPathHelper.pathArrayToSvgPath(SvgPathHelper.smoothenPathArray(pathArray, SMOOTHEN_PATH_TENSION));
      else
        svgPath = SvgPathHelper.pathArrayToRoundedSvgPath(pathArray, ROUND_PATH_RADIUS);
    } else svgPath = SvgPathHelper.pathArrayToSvgPath(pathArray);
    return {
      svgPath,
      center: pathArray[Math.floor(pathArray.length / 2)],
      rotateArrows: false
    };
  }
  aStarAlgorithm(fromPos, toPos, obstacles, gridResolution, allowDiagonal) {
    const start = new Node(
      Math.floor(fromPos.x / gridResolution) * gridResolution,
      Math.floor(fromPos.y / gridResolution) * gridResolution
    );
    if (this.fromSide === "right" && fromPos.x !== start.x) start.x += gridResolution;
    if (this.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 (this.toSide === "right" && toPos.x !== end.x) end.x += gridResolution;
    if (this.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 = [];
    const startTimestamp = performance.now();
    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 (performance.now() - startTimestamp > MAX_MS_CALCULATION)
        return null;
      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, allowDiagonal)) {
        if (neighbor.inList(closedSet))
          continue;
        const tentativeGCost = current.gCost + (allowDiagonal ? this.getMovementCost({
          dx: neighbor.x - current.x,
          dy: neighbor.y - current.y
        }) : 1);
        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, allowDiagonal) {
    const neighbors = [];
    const availableDirections = allowDiagonal ? [...BASIC_DIRECTIONS, ...DIAGONAL_DIRECTIONS] : BASIC_DIRECTIONS;
    for (const direction of availableDirections) {
      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-pathfinding-methods/pathfinding-direct.ts
var EdgePathfindingDirect = class extends EdgePathfindingMethod {
  getPath() {
    return {
      svgPath: SvgPathHelper.pathArrayToSvgPath([this.fromPos, this.toPos]),
      center: {
        x: (this.fromPos.x + this.toPos.x) / 2,
        y: (this.fromPos.y + this.toPos.y) / 2
      },
      rotateArrows: true
    };
  }
};

// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-square.ts
var ROUNDED_EDGE_RADIUS = 5;
var EdgePathfindingSquare = class extends EdgePathfindingMethod {
  getPath() {
    const pathArray = [];
    let center = {
      x: (this.fromPos.x + this.toPos.x) / 2,
      y: (this.fromPos.y + this.toPos.y) / 2
    };
    const idealCenter = BBoxHelper.isHorizontal(this.fromSide) ? {
      x: this.toBBoxSidePos.x,
      y: this.fromBBoxSidePos.y
    } : {
      x: this.fromBBoxSidePos.x,
      y: this.toBBoxSidePos.y
    };
    const isPathCollidingAtFrom = this.fromSide === "top" && idealCenter.y > this.fromPos.y || this.fromSide === "bottom" && idealCenter.y < this.fromPos.y || this.fromSide === "left" && idealCenter.x > this.fromPos.x || this.fromSide === "right" && idealCenter.x < this.fromPos.x;
    const isPathCollidingAtTo = this.toSide === "top" && idealCenter.y > this.toPos.y || this.toSide === "bottom" && idealCenter.y < this.toPos.y || this.toSide === "left" && idealCenter.x > this.toPos.x || this.toSide === "right" && idealCenter.x < this.toPos.x;
    if (this.fromSide === this.toSide) {
      const uPath = this.getUPath(this.fromPos, this.toPos, this.fromSide, this.toSide);
      pathArray.push(...uPath.pathArray);
      center = uPath.center;
    } else if (BBoxHelper.isHorizontal(this.fromSide) === BBoxHelper.isHorizontal(this.toSide)) {
      let zPath;
      if (!isPathCollidingAtFrom || !isPathCollidingAtTo) {
        zPath = this.getZPath(this.fromPos, this.toPos, this.fromSide, this.toSide);
        pathArray.push(...zPath.pathArray);
      } else {
        const fromDirection = BBoxHelper.direction(this.fromSide);
        const firstFromDetourPoint = BBoxHelper.isHorizontal(this.fromSide) ? {
          x: CanvasHelper.alignToGrid(this.fromBBoxSidePos.x + fromDirection * CanvasHelper.GRID_SIZE),
          y: this.fromBBoxSidePos.y
        } : {
          x: this.fromBBoxSidePos.x,
          y: CanvasHelper.alignToGrid(this.fromBBoxSidePos.y + fromDirection * CanvasHelper.GRID_SIZE)
        };
        const toDirection = BBoxHelper.direction(this.toSide);
        const firstToDetourPoint = BBoxHelper.isHorizontal(this.toSide) ? {
          x: CanvasHelper.alignToGrid(this.toBBoxSidePos.x + toDirection * CanvasHelper.GRID_SIZE),
          y: this.toBBoxSidePos.y
        } : {
          x: this.toBBoxSidePos.x,
          y: CanvasHelper.alignToGrid(this.toBBoxSidePos.y + toDirection * CanvasHelper.GRID_SIZE)
        };
        const newFromSide = BBoxHelper.isHorizontal(this.fromSide) ? firstFromDetourPoint.y < this.fromPos.y ? "top" : "bottom" : firstFromDetourPoint.x < firstToDetourPoint.x ? "right" : "left";
        zPath = this.getZPath(firstFromDetourPoint, firstToDetourPoint, newFromSide, BBoxHelper.getOppositeSide(newFromSide));
        pathArray.push(this.fromPos);
        pathArray.push(...zPath.pathArray);
        pathArray.push(this.toPos);
      }
      center = zPath.center;
    } else {
      if (isPathCollidingAtFrom || isPathCollidingAtTo) {
        if (isPathCollidingAtFrom && isPathCollidingAtTo) {
          const direction = BBoxHelper.direction(this.fromSide);
          let firstFromDetourPoint;
          let secondFromDetourPoint;
          if (BBoxHelper.isHorizontal(this.fromSide)) {
            const combinedBBoxes = BBoxHelper.combineBBoxes([this.fromNodeBBox, this.toNodeBBox]);
            firstFromDetourPoint = {
              x: CanvasHelper.alignToGrid((direction > 0 ? combinedBBoxes.maxX : combinedBBoxes.minX) + direction * CanvasHelper.GRID_SIZE),
              y: this.fromBBoxSidePos.y
            };
            secondFromDetourPoint = {
              x: firstFromDetourPoint.x,
              y: BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, this.toSide).y
            };
          } else {
            const combinedBBoxes = BBoxHelper.combineBBoxes([this.fromNodeBBox, this.toNodeBBox]);
            firstFromDetourPoint = {
              x: this.fromBBoxSidePos.x,
              y: CanvasHelper.alignToGrid((direction > 0 ? combinedBBoxes.maxY : combinedBBoxes.minY) + direction * CanvasHelper.GRID_SIZE)
            };
            secondFromDetourPoint = {
              x: BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, this.toSide).x,
              y: firstFromDetourPoint.y
            };
          }
          const uPath = this.getUPath(secondFromDetourPoint, this.toPos, this.toSide, this.toSide);
          pathArray.push(this.fromPos);
          pathArray.push(firstFromDetourPoint);
          pathArray.push(...uPath.pathArray);
          center = pathArray[Math.floor(pathArray.length / 2)];
        } else {
          if (isPathCollidingAtFrom) {
            const direction = BBoxHelper.direction(this.fromSide);
            const firstFromDetourPoint = BBoxHelper.isHorizontal(this.fromSide) ? {
              x: CanvasHelper.alignToGrid(this.fromBBoxSidePos.x + direction * CanvasHelper.GRID_SIZE),
              y: this.fromBBoxSidePos.y
            } : {
              x: this.fromBBoxSidePos.x,
              y: CanvasHelper.alignToGrid(this.fromBBoxSidePos.y + direction * CanvasHelper.GRID_SIZE)
            };
            const useUPath = BBoxHelper.isHorizontal(this.fromSide) ? this.toPos.y > BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, BBoxHelper.getOppositeSide(this.toSide)).y === BBoxHelper.direction(this.toSide) > 0 : this.toPos.x > BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, BBoxHelper.getOppositeSide(this.toSide)).x === BBoxHelper.direction(this.toSide) > 0;
            const connectionSide = useUPath ? this.toSide : BBoxHelper.getOppositeSide(this.toSide);
            const secondFromDetourPoint = BBoxHelper.isHorizontal(this.fromSide) ? {
              x: firstFromDetourPoint.x,
              y: BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, connectionSide).y
            } : {
              x: BBoxHelper.getCenterOfBBoxSide(this.fromNodeBBox, connectionSide).x,
              y: firstFromDetourPoint.y
            };
            const path = useUPath ? this.getUPath(secondFromDetourPoint, this.toPos, this.toSide, this.toSide) : this.getZPath(secondFromDetourPoint, this.toPos, this.toSide, this.toSide);
            pathArray.push(this.fromPos);
            pathArray.push(firstFromDetourPoint);
            pathArray.push(...path.pathArray);
            center = path.center;
          }
          if (isPathCollidingAtTo) {
            const direction = BBoxHelper.direction(this.toSide);
            const firstToDetourPoint = BBoxHelper.isHorizontal(this.toSide) ? {
              x: CanvasHelper.alignToGrid(this.toBBoxSidePos.x + direction * CanvasHelper.GRID_SIZE),
              y: this.toBBoxSidePos.y
            } : {
              x: this.toBBoxSidePos.x,
              y: CanvasHelper.alignToGrid(this.toBBoxSidePos.y + direction * CanvasHelper.GRID_SIZE)
            };
            const useUPath = BBoxHelper.isHorizontal(this.toSide) ? this.fromPos.y > BBoxHelper.getCenterOfBBoxSide(this.toNodeBBox, BBoxHelper.getOppositeSide(this.fromSide)).y === BBoxHelper.direction(this.fromSide) > 0 : this.fromPos.x > BBoxHelper.getCenterOfBBoxSide(this.toNodeBBox, BBoxHelper.getOppositeSide(this.fromSide)).x === BBoxHelper.direction(this.fromSide) > 0;
            const connectionSide = useUPath ? this.fromSide : BBoxHelper.getOppositeSide(this.fromSide);
            const secondToDetourPoint = BBoxHelper.isHorizontal(this.toSide) ? {
              x: firstToDetourPoint.x,
              y: BBoxHelper.getCenterOfBBoxSide(this.toNodeBBox, connectionSide).y
            } : {
              x: BBoxHelper.getCenterOfBBoxSide(this.toNodeBBox, connectionSide).x,
              y: firstToDetourPoint.y
            };
            const path = useUPath ? this.getUPath(this.fromPos, secondToDetourPoint, this.fromSide, this.fromSide) : this.getZPath(this.fromPos, secondToDetourPoint, this.fromSide, this.fromSide);
            pathArray.push(...path.pathArray);
            pathArray.push(secondToDetourPoint);
            pathArray.push(firstToDetourPoint);
            pathArray.push(this.toPos);
            center = path.center;
          }
        }
      } else {
        pathArray.push(
          this.fromPos,
          idealCenter,
          this.toPos
        );
        center = {
          x: pathArray[1].x,
          y: pathArray[1].y
        };
      }
    }
    const svgPath = this.plugin.settings.getSetting("edgeStyleSquarePathRounded") ? SvgPathHelper.pathArrayToRoundedSvgPath(pathArray, ROUNDED_EDGE_RADIUS) : SvgPathHelper.pathArrayToSvgPath(pathArray);
    return { svgPath, center, rotateArrows: false };
  }
  getUPath(fromPos, toPos, fromSide, toSide) {
    const direction = BBoxHelper.direction(fromSide);
    if (BBoxHelper.isHorizontal(fromSide)) {
      const xExtremum = direction > 0 ? Math.max(fromPos.x, toPos.x) : Math.min(fromPos.x, toPos.x);
      const x = CanvasHelper.alignToGrid(xExtremum + direction * CanvasHelper.GRID_SIZE);
      return {
        pathArray: [
          fromPos,
          { x, y: fromPos.y },
          { x, y: toPos.y },
          toPos
        ],
        center: {
          x,
          y: (fromPos.y + toPos.y) / 2
        }
      };
    } else {
      const yExtremum = direction > 0 ? Math.max(fromPos.y, toPos.y) : Math.min(fromPos.y, toPos.y);
      const y = CanvasHelper.alignToGrid(yExtremum + direction * CanvasHelper.GRID_SIZE);
      return {
        pathArray: [
          fromPos,
          { x: fromPos.x, y },
          { x: toPos.x, y },
          toPos
        ],
        center: {
          x: (fromPos.x + toPos.x) / 2,
          y
        }
      };
    }
  }
  getZPath(fromPos, toPos, fromSide, toSide) {
    if (BBoxHelper.isHorizontal(fromSide)) {
      const midX = fromPos.x + (toPos.x - fromPos.x) / 2;
      return {
        pathArray: [
          fromPos,
          { x: midX, y: fromPos.y },
          { x: midX, y: toPos.y },
          toPos
        ],
        center: {
          x: midX,
          y: (fromPos.y + toPos.y) / 2
        }
      };
    } else {
      const midY = fromPos.y + (toPos.y - fromPos.y) / 2;
      return {
        pathArray: [
          fromPos,
          { x: fromPos.x, y: midY },
          { x: toPos.x, y: midY },
          toPos
        ],
        center: {
          x: (fromPos.x + toPos.x) / 2,
          y: midY
        }
      };
    }
  }
};

// src/utils/text-helper.ts
var TextHelper = class {
  static toCamelCase(str) {
    return str.replace(/-./g, (x) => x[1].toUpperCase());
  }
  static toTitleCase(str) {
    return str.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
  }
};

// src/canvas-extensions/advanced-styles/style-config.ts
function styleAttributeValidator(json) {
  var _a;
  const hasKey = json.key !== void 0;
  const hasLabel = json.label !== void 0;
  const hasOptions = Array.isArray(json.options);
  if (!hasKey) console.error('Style attribute is missing the "key" property');
  if (!hasLabel) console.error('Style attribute is missing the "label" property');
  if (!hasOptions) console.error('Style attribute is missing the "options" property or it is not an array');
  json.key = TextHelper.toCamelCase(json.key);
  let optionsValid = true;
  let hasDefault = false;
  for (const option of json.options) {
    const hasIcon = option.icon !== void 0;
    const hasLabel2 = option.label !== void 0;
    const hasValue = option.value !== void 0;
    if (!hasIcon) console.error(`Style attribute option (${(_a = option.value) != null ? _a : option.label}) is missing the "icon" property`);
    if (!hasLabel2) console.error(`Style attribute option (${option.value}) is missing the "label" property`);
    if (!hasValue) console.error(`Style attribute option (${option.label}) is missing the "value" property`);
    if (!hasIcon || !hasLabel2 || !hasValue) optionsValid = false;
    if (option.value === null) hasDefault = true;
  }
  if (!hasDefault) console.error('Style attribute is missing a default option (option with a "value" of null)');
  const isValid = hasKey && hasLabel && hasOptions && optionsValid && hasDefault;
  return isValid ? json : null;
}
var BUILTIN_NODE_STYLE_ATTRIBUTES = [
  {
    key: "textAlign",
    label: "Text Alignment",
    nodeTypes: ["text"],
    options: [
      {
        icon: "align-left",
        label: "Left",
        value: null
      },
      {
        icon: "align-center",
        label: "Center",
        value: "center"
      },
      {
        icon: "align-right",
        label: "Right",
        value: "right"
      }
    ]
  },
  {
    key: "shape",
    label: "Shape",
    nodeTypes: ["text"],
    options: [
      {
        icon: "rectangle-horizontal",
        label: "Round Rectangle",
        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"
      }
    ]
  },
  {
    key: "border",
    label: "Border",
    options: [
      {
        icon: "border-solid",
        label: "Solid",
        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 = [
  {
    key: "path",
    label: "Path Style",
    options: [
      {
        icon: "path-solid",
        label: "Solid",
        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"
      }
    ]
  },
  {
    key: "arrow",
    label: "Arrow Style",
    options: [
      {
        icon: "arrow-triangle",
        label: "Triangle",
        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"
      },
      {
        icon: "tally-1",
        label: "Blunt",
        value: "blunt"
      }
    ]
  },
  {
    key: "pathfindingMethod",
    label: "Pathfinding Method",
    options: [
      {
        icon: "pathfinding-method-bezier",
        label: "Bezier",
        value: null
      },
      {
        icon: "slash",
        label: "Direct",
        value: "direct"
      },
      {
        icon: "pathfinding-method-square",
        label: "Square",
        value: "square"
      },
      {
        icon: "map",
        label: "A*",
        value: "a-star"
      }
    ]
  }
];

// src/managers/css-styles-config-manager.ts
var import_obsidian3 = require("obsidian");
var CssStylesConfigManager = class {
  constructor(plugin, trigger, validate) {
    this.plugin = plugin;
    this.validate = validate;
    this.cachedConfig = null;
    this.configRegex = new RegExp(`\\/\\*\\s*@${trigger}\\s*\\n([\\s\\S]*?)\\*\\/`, "g");
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "css-change",
      () => {
        this.cachedConfig = null;
      }
    ));
  }
  getStyles() {
    if (this.cachedConfig) return this.cachedConfig;
    this.cachedConfig = [];
    const styleSheets = document.styleSheets;
    for (let i = 0; i < styleSheets.length; i++) {
      const sheet = styleSheets.item(i);
      if (!sheet) continue;
      const styleSheetConfigs = this.parseStyleConfigsFromCSS(sheet);
      for (const config of styleSheetConfigs) {
        const validConfig = this.validate(config);
        if (!validConfig) continue;
        this.cachedConfig.push(validConfig);
      }
    }
    return this.cachedConfig;
  }
  parseStyleConfigsFromCSS(sheet) {
    var _a, _b;
    const textContent = (_b = (_a = sheet == null ? void 0 : sheet.ownerNode) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim();
    if (!textContent) return [];
    const configs = [];
    const matches = textContent.matchAll(this.configRegex);
    for (const match of matches) {
      const yamlString = match[1];
      const configYaml = (0, import_obsidian3.parseYaml)(yamlString);
      configs.push(configYaml);
    }
    return configs;
  }
};

// src/canvas-extensions/advanced-styles/edge-styles.ts
var GET_EDGE_CSS_STYLES_MANAGER = (plugin) => new CssStylesConfigManager(plugin, "advanced-canvas-edge-style", styleAttributeValidator);
var EDGE_PATHFINDING_METHODS = {
  "direct": EdgePathfindingDirect,
  "square": EdgePathfindingSquare,
  "a-star": EdgePathfindingAStar
};
var MAX_LIVE_UPDATE_SELECTION_SIZE = 5;
var EdgeStylesExtension = class extends CanvasExtension {
  isEnabled() {
    return "edgesStylingFeatureEnabled";
  }
  init() {
    this.cssStylesManager = GET_EDGE_CSS_STYLES_MANAGER(this.plugin);
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenuCreated(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-changed",
      (canvas, edge) => this.onEdgeChanged(canvas, edge)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-center-requested",
      (canvas, edge, center) => this.onEdgeCenterRequested(canvas, edge, center)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-added",
      (canvas, node) => {
        if (canvas.dirty.size > 1 && !canvas.isPasting) return;
        this.updateAllEdgesInArea(canvas, node.getBBox());
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-moved",
      // Only update edges this way if a node got moved with the arrow keys
      (canvas, node, keyboard) => node.initialized && keyboard ? this.updateAllEdgesInArea(canvas, node.getBBox()) : void 0
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-removed",
      (canvas, node) => this.updateAllEdgesInArea(canvas, node.getBBox())
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:dragging-state-changed",
      (canvas, isDragging) => {
        if (isDragging) return;
        const selectedNodes = canvas.getSelectionData().nodes.map((nodeData) => canvas.nodes.get(nodeData.id)).filter((node) => node !== void 0);
        const selectedNodeBBoxes = selectedNodes.map((node) => node.getBBox());
        const selectedNodeBBox = BBoxHelper.combineBBoxes(selectedNodeBBoxes);
        this.updateAllEdgesInArea(canvas, selectedNodeBBox);
      }
    ));
  }
  // Skip if isDragging and setting isn't enabled and not connecting an edge
  shouldUpdateEdge(canvas) {
    return !canvas.isDragging || this.plugin.settings.getSetting("edgeStyleUpdateWhileDragging") || canvas.canvasEl.hasClass("is-connecting");
  }
  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,
      [
        ...BUILTIN_EDGE_STYLE_ATTRIBUTES,
        /* Legacy */
        ...this.plugin.settings.getSetting("customEdgeStyleAttributes"),
        ...this.cssStylesManager.getStyles()
      ],
      (_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.key]: value
        }
      });
    }
    canvas.pushHistory(canvas.getData());
  }
  updateAllEdgesInArea(canvas, bbox) {
    if (!this.shouldUpdateEdge(canvas)) return;
    for (const edge of canvas.edges.values()) {
      if (!BBoxHelper.isColliding(edge.getBBox(), bbox)) continue;
      canvas.markDirty(edge);
    }
  }
  onEdgeChanged(canvas, edge) {
    var _a, _b, _c, _d, _e, _f, _g;
    if (!canvas.dirty.has(edge) && !canvas.selection.has(edge)) return;
    if (!this.shouldUpdateEdge(canvas)) {
      const tooManySelected = canvas.selection.size > MAX_LIVE_UPDATE_SELECTION_SIZE;
      if (tooManySelected) return;
      const groupNodesSelected = [...canvas.selection].some((item) => {
        var _a2;
        return ((_a2 = item.getData()) == null ? void 0 : _a2.type) === "group";
      });
      if (groupNodesSelected) return;
    }
    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 && pathfindingMethod in EDGE_PATHFINDING_METHODS) {
      const fromNodeBBox = edge.from.node.getBBox();
      const fromBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(fromNodeBBox, edge.from.side);
      const fromPos = edge.from.end === "none" ? fromBBoxSidePos : edge.bezier.from;
      const toNodeBBox = edge.to.node.getBBox();
      const toBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(toNodeBBox, edge.to.side);
      const toPos = edge.to.end === "none" ? toBBoxSidePos : edge.bezier.to;
      const path = new EDGE_PATHFINDING_METHODS[pathfindingMethod](
        this.plugin,
        canvas,
        fromNodeBBox,
        fromPos,
        fromBBoxSidePos,
        edge.from.side,
        toNodeBBox,
        toPos,
        toBBoxSidePos,
        edge.to.side
      ).getPath();
      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);
  }
  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 if (arrowStyle === "blunt")
      return `-10,8 10,8 10,6 -10,6`;
    else
      return `0,0 6.5,10.4 -6.5,10.4`;
  }
};

// src/canvas-extensions/advanced-styles/node-styles.ts
var GET_NODE_CSS_STYLES_MANAGER = (plugin) => new CssStylesConfigManager(plugin, "advanced-canvas-node-style", styleAttributeValidator);
var NodeStylesExtension = class extends CanvasExtension {
  isEnabled() {
    return "nodeStylingFeatureEnabled";
  }
  init() {
    this.cssStylesManager = GET_NODE_CSS_STYLES_MANAGER(this.plugin);
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (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 = [
      ...BUILTIN_NODE_STYLE_ATTRIBUTES,
      /* Legacy */
      ...this.plugin.settings.getSetting("customNodeStyleAttributes"),
      ...this.cssStylesManager.getStyles()
    ].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.key]: value
        }
      });
    }
    canvas.pushHistory(canvas.getData());
  }
};

// src/canvas-extensions/variable-breakpoint-canvas-extension.ts
var VARIABLE_BREAKPOINT_CSS_VAR = "--variable-breakpoint";
var VariableBreakpointCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "variableBreakpointFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-breakpoint-changed",
      (canvas, node, breakpointRef) => this.onNodeBreakpointChanged(canvas, node, breakpointRef)
    ));
  }
  onNodeBreakpointChanged(canvas, node, breakpointRef) {
    if (!node.initialized) return;
    if (node.breakpoint === void 0) {
      const computedStyle = window.getComputedStyle(node.nodeEl);
      const variableBreakpointString = computedStyle.getPropertyValue(VARIABLE_BREAKPOINT_CSS_VAR);
      let numberBreakpoint;
      if (variableBreakpointString.length > 0 && !isNaN(numberBreakpoint = parseFloat(variableBreakpointString)))
        node.breakpoint = numberBreakpoint;
      else node.breakpoint = null;
    }
    if (node.breakpoint === null) return;
    breakpointRef.value = canvas.zoom > node.breakpoint;
  }
};

// src/settings.ts
var README_URL = "https://github.com/Developer-Mike/obsidian-advanced-canvas?tab=readme-ov-file";
var SPENT_HOURS = 250;
var RECEIVED_DONATIONS = 606;
var HOURLY_RATE_GOAL = 15;
var KOFI_PAGE_URL = "https://ko-fi.com/X8X27IA08";
var KOFI_BADGE_URI = "data:image/webp;base64, ";
var DEFAULT_SETTINGS_VALUES = {
  nodeTypeOnDoubleClick: "text",
  alignNewNodesToGrid: true,
  defaultTextNodeDimensions: [260, 60],
  defaultFileNodeDimensions: [400, 400],
  minNodeSize: 60,
  maxNodeWidth: -1,
  disableFontSizeRelativeToZoom: false,
  canvasMetadataCompatibilityEnabled: true,
  enableSingleNodeLinks: true,
  combineCustomStylesInDropdown: false,
  nodeStylingFeatureEnabled: true,
  customNodeStyleAttributes: [],
  defaultTextNodeColor: 0,
  defaultTextNodeStyleAttributes: {},
  edgesStylingFeatureEnabled: true,
  customEdgeStyleAttributes: [],
  inheritEdgeColorFromNode: false,
  defaultEdgeColor: 0,
  defaultEdgeLineDirection: "unidirectional",
  defaultEdgeStyleAttributes: {},
  edgeStyleUpdateWhileDragging: false,
  edgeStyleSquarePathRounded: true,
  edgeStylePathfinderAllowDiagonal: false,
  edgeStylePathfinderPathRounded: true,
  variableBreakpointFeatureEnabled: false,
  zOrderingControlFeatureEnabled: false,
  zOrderingControlShowOneLayerShiftOptions: false,
  aspectRatioControlFeatureEnabled: false,
  commandsFeatureEnabled: true,
  zoomToClonedNode: true,
  cloneNodeMargin: 20,
  expandNodeStepSize: 20,
  nativeFileSearchEnabled: true,
  floatingEdgeFeatureEnabled: true,
  allowFloatingEdgeCreation: false,
  newEdgeFromSideFloating: false,
  flipEdgeFeatureEnabled: true,
  betterExportFeatureEnabled: true,
  betterReadonlyEnabled: false,
  hideBackgroundGridWhenInReadonly: true,
  disableNodePopup: false,
  disableZoom: false,
  disablePan: false,
  autoResizeNodeFeatureEnabled: false,
  autoResizeNodeEnabledByDefault: false,
  autoResizeNodeMaxHeight: -1,
  autoResizeNodeSnapToGrid: true,
  collapsibleGroupsFeatureEnabled: true,
  collapsedGroupPreviewOnDrag: true,
  focusModeFeatureEnabled: false,
  presentationFeatureEnabled: true,
  showSetStartNodeInPopup: false,
  defaultSlideDimensions: [1200, 675],
  wrapInSlidePadding: 20,
  resetViewportOnPresentationEnd: true,
  useArrowKeysToChangeSlides: true,
  usePgUpPgDownKeysToChangeSlides: true,
  zoomToSlideWithoutPadding: true,
  useUnclampedZoomWhilePresenting: false,
  fullscreenPresentationEnabled: true,
  slideTransitionAnimationDuration: 0.5,
  slideTransitionAnimationIntensity: 1.25,
  canvasEncapsulationEnabled: false,
  portalsFeatureEnabled: true,
  showEdgesIntoDisabledPortals: true,
  autoFileNodeEdgesFeatureEnabled: false,
  autoFileNodeEdgesFrontmatterKey: "canvas-edges",
  edgeHighlightEnabled: false,
  highlightIncomingEdges: false,
  edgeSelectionEnabled: false,
  selectEdgeByDirection: false
};
var SETTINGS = {
  general: {
    label: "General",
    description: "General settings of the Advanced Canvas plugin.",
    disableToggle: true,
    children: {
      nodeTypeOnDoubleClick: {
        label: "Node type on double click",
        description: "The type of node that will be created when double clicking on the canvas.",
        type: "dropdown",
        options: {
          "text": "Text",
          "file": "File"
        }
      },
      alignNewNodesToGrid: {
        label: "Always align new nodes to grid",
        description: "When enabled, new nodes will be aligned to the grid.",
        type: "boolean"
      },
      defaultTextNodeDimensions: {
        label: "Default text node dimensions",
        description: "The default dimensions of a text node.",
        type: "dimension",
        parse: (value) => {
          const width = Math.max(1, parseInt(value[0]) || 0);
          const height = Math.max(1, parseInt(value[1]) || 0);
          return [width, height];
        }
      },
      defaultFileNodeDimensions: {
        label: "Default file node dimensions",
        description: "The default dimensions of a file node.",
        type: "dimension",
        parse: (value) => {
          const width = Math.max(1, parseInt(value[0]) || 0);
          const height = Math.max(1, parseInt(value[1]) || 0);
          return [width, height];
        }
      },
      minNodeSize: {
        label: "Minimum node size",
        description: "The minimum size (either width or height) of a node.",
        type: "number",
        parse: (value) => Math.max(1, parseInt(value) || 0)
      },
      maxNodeWidth: {
        label: "Maximum node width",
        description: "The maximum width of a node. Set to -1 for no limit.",
        type: "number",
        parse: (value) => Math.max(-1, parseInt(value) || 0)
      },
      disableFontSizeRelativeToZoom: {
        label: "Disable font size relative to zoom",
        description: "When enabled, the font size of e.g. group node titles and edge labels will not increase when zooming out.",
        type: "boolean"
      }
    }
  },
  commandsFeatureEnabled: {
    label: "Extended commands",
    description: "Add more commands to the canvas.",
    infoSection: "canvas-commands",
    children: {
      zoomToClonedNode: {
        label: "Zoom to cloned node",
        description: "When enabled, the canvas will zoom to the cloned node.",
        type: "boolean"
      },
      cloneNodeMargin: {
        label: "Clone node margin",
        description: "The margin between the cloned node and the source node.",
        type: "number",
        parse: (value) => Math.max(0, parseInt(value) || 0)
      },
      expandNodeStepSize: {
        label: "Expand node step size",
        description: "The step size for expanding the node.",
        type: "number",
        parse: (value) => Math.max(1, parseInt(value) || 0)
      }
    }
  },
  canvasMetadataCompatibilityEnabled: {
    label: "Enable .canvas metadata cache compatibility",
    description: "Make .canvas files compatible with the backlinks and outgoing links feature and show the connections in the graph view.",
    infoSection: "full-metadata-cache-support",
    children: {
      enableSingleNodeLinks: {
        label: "Enable support for linking to a node using a [[wikilink]]",
        description: "When enabled, you can link and embed a node using [[canvas-file#node-id]].",
        type: "boolean"
      }
    }
  },
  nativeFileSearchEnabled: {
    label: "Native-like file search",
    description: "When enabled, the file search will be done using the native Obsidian file search.",
    infoSection: "native-like-file-search",
    children: {}
  },
  autoFileNodeEdgesFeatureEnabled: {
    label: "Auto file node edges",
    description: "Automatically create edges between file nodes based their frontmatter links.",
    infoSection: "auto-file-node-edges",
    children: {
      autoFileNodeEdgesFrontmatterKey: {
        label: "Frontmatter key name",
        description: "The frontmatter key to fetch the outgoing edges from. (Keep the default to ensure best compatibility.)",
        type: "text",
        parse: (value) => value.trim() || "canvas-edges"
      }
    }
  },
  portalsFeatureEnabled: {
    label: "Portals",
    description: "Create portals to other canvases.",
    infoSection: "portals",
    children: {
      showEdgesIntoDisabledPortals: {
        label: "Show edges into disabled portals",
        description: "When enabled, edges into disabled portals will be shown.",
        type: "boolean"
      }
    }
  },
  collapsibleGroupsFeatureEnabled: {
    label: "Collapsible groups",
    description: "Group nodes can be collapsed and expanded to keep the canvas organized.",
    infoSection: "collapsible-groups",
    children: {
      collapsedGroupPreviewOnDrag: {
        label: "Collapsed group preview on drag",
        description: "When enabled, a group that is collapsed show its border while dragging a node.",
        type: "boolean"
      }
    }
  },
  combineCustomStylesInDropdown: {
    label: "Combine custom styles",
    description: "Combine all style attributes of Advanced Canvas in a single dropdown.",
    children: {}
  },
  nodeStylingFeatureEnabled: {
    label: "Node styling",
    description: "Style your nodes with different shapes and borders.",
    infoSection: "node-styles",
    children: {
      customNodeStyleAttributes: {
        label: "Custom node style settings",
        description: "Add custom style settings for nodes. (Go to GitHub for more information)",
        type: "button",
        onClick: () => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles")
      },
      defaultTextNodeColor: {
        label: "Default text node color",
        description: "The default color of a text node. The default range is from 0 to 6, where 0 is no color. The range can be extended by using the Custom Colors feature of Advanced Canvas.",
        type: "number",
        parse: (value) => Math.max(0, parseInt(value) || 0)
      },
      defaultTextNodeStyleAttributes: {
        label: "Default text node style attributes",
        type: "styles",
        getParameters(settingsManager) {
          return [
            ...BUILTIN_NODE_STYLE_ATTRIBUTES,
            /* BUILTINS */
            ...settingsManager.nodeCssStylesManager.getStyles(),
            /* CUSTOM CSS STYLES */
            ...settingsManager.getSetting("customNodeStyleAttributes")
            /* LEGACY CUSTOM STYLES */
          ].filter((setting) => {
            var _a;
            return setting.nodeTypes === void 0 || ((_a = setting.nodeTypes) == null ? void 0 : _a.includes("text"));
          });
        }
      }
    }
  },
  edgesStylingFeatureEnabled: {
    label: "Edges styling",
    description: "Style your edges with different path styles.",
    infoSection: "edge-styles",
    children: {
      customEdgeStyleAttributes: {
        label: "Custom edge style settings",
        description: "Add custom style settings for edges. (Go to GitHub for more information)",
        type: "button",
        onClick: () => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles")
      },
      inheritEdgeColorFromNode: {
        label: "Inherit edge color from node",
        description: "When creating a new edge by dragging from a node, the edge will inherit the color of the node it is dragged from.",
        type: "boolean"
      },
      defaultEdgeColor: {
        label: "Default edge color",
        description: "The default color of an edge. The default range is from 0 to 6, where 0 is no color. The range can be extended by using the Custom Colors feature of Advanced Canvas.",
        type: "number",
        parse: (value) => Math.max(0, parseInt(value) || 0)
      },
      defaultEdgeLineDirection: {
        label: "Default edge line direction",
        description: "The default line direction of an edge.",
        type: "dropdown",
        options: {
          "nondirectional": "Nondirectional",
          "unidirectional": "Unidirectional",
          "bidirectional": "Bidirectional"
        }
      },
      defaultEdgeStyleAttributes: {
        label: "Default edge style attributes",
        type: "styles",
        getParameters(settingsManager) {
          return [
            ...BUILTIN_EDGE_STYLE_ATTRIBUTES,
            /* BUILTINS */
            ...settingsManager.edgeCssStylesManager.getStyles(),
            /* CUSTOM CSS STYLES */
            ...settingsManager.getSetting("customEdgeStyleAttributes")
            /* LEGACY CUSTOM STYLES */
          ];
        }
      },
      edgeStyleUpdateWhileDragging: {
        label: "Update edge style while dragging (Can be very slow)",
        description: "When enabled, the edge style will be updated while dragging an edge.",
        type: "boolean"
      },
      edgeStyleSquarePathRounded: {
        label: "Square path rounded",
        description: "When enabled, the square path's corners will be rounded.",
        type: "boolean"
      },
      edgeStylePathfinderAllowDiagonal: {
        label: "A* allow diagonal",
        description: "When enabled, the A* path style will allow diagonal paths.",
        type: "boolean"
      },
      edgeStylePathfinderPathRounded: {
        label: "A* rounded path",
        description: "When enabled, the A* path style will be rounded.",
        type: "boolean"
      }
    }
  },
  floatingEdgeFeatureEnabled: {
    label: "Floating edges (auto edge side)",
    description: "Floating edges are automatically placed on the most suitable side of the node.",
    infoSection: "floating-edges-automatic-edge-side",
    children: {
      allowFloatingEdgeCreation: {
        label: "Allow floating edges creation",
        description: "Allow floating edges creation by dragging the edge over the target node without placing it over a specific side connection point. (If disabled, floating edges can only be created and used by other Advanced Canvas features.)",
        type: "boolean"
      },
      newEdgeFromSideFloating: {
        label: "New edge from side floating",
        description: 'When enabled, the "from" side of the edge will always be floating.',
        type: "boolean"
      }
    }
  },
  flipEdgeFeatureEnabled: {
    label: "Flip edges",
    description: "Flip the direction of edges using the popup menu.",
    infoSection: "flip-edge",
    children: {}
  },
  presentationFeatureEnabled: {
    label: "Presentations",
    description: "Create a presentation from your canvas.",
    infoSection: "presentation-mode",
    children: {
      showSetStartNodeInPopup: {
        label: 'Show "Set Start Node" in node popup',
        description: "If turned off, you can still set the start node using the corresponding command.",
        type: "boolean"
      },
      defaultSlideDimensions: {
        label: "Default slide dimensions",
        description: "The default dimensions of a slide.",
        type: "dimension",
        parse: (value) => {
          const width = Math.max(1, parseInt(value[0]) || 0);
          const height = Math.max(1, parseInt(value[1]) || 0);
          return [width, height];
        }
      },
      wrapInSlidePadding: {
        label: "Wrap in slide padding",
        description: "The padding of the slide when wrapping the canvas in a slide.",
        type: "number",
        parse: (value) => Math.max(0, parseInt(value) || 0)
      },
      resetViewportOnPresentationEnd: {
        label: "Reset viewport on presentation end",
        description: "When enabled, the viewport will be reset to the original position after the presentation ends.",
        type: "boolean"
      },
      useArrowKeysToChangeSlides: {
        label: "Use arrow keys to change slides",
        description: "When enabled, you can use the arrow keys to change slides in presentation mode.",
        type: "boolean"
      },
      usePgUpPgDownKeysToChangeSlides: {
        label: "Use PgUp/PgDown keys to change slides",
        description: "When enabled, you can use the PgUp/PgDown keys to change slides in presentation mode (Makes the presentation mode compatible with most presentation remotes).",
        type: "boolean"
      },
      zoomToSlideWithoutPadding: {
        label: "Zoom to slide without padding",
        description: "When enabled, the canvas will zoom to the slide without padding.",
        type: "boolean"
      },
      useUnclampedZoomWhilePresenting: {
        label: "Use unclamped zoom while presenting",
        description: "When enabled, the zoom will not be clamped while presenting.",
        type: "boolean"
      },
      fullscreenPresentationEnabled: {
        label: "Enter fullscreen while presenting",
        description: "When enabled, presentations automatically request fullscreen. Disable to keep Obsidian windowed during presentations.",
        type: "boolean"
      },
      slideTransitionAnimationDuration: {
        label: "Slide transition animation duration",
        description: "The duration of the slide transition animation in seconds. Set to 0 to disable the animation.",
        type: "number",
        parse: (value) => Math.max(0, parseFloat(value) || 0)
      },
      slideTransitionAnimationIntensity: {
        label: "Slide transition animation intensity",
        description: "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.",
        type: "number",
        parse: (value) => Math.max(0, parseFloat(value) || 0)
      }
    }
  },
  zOrderingControlFeatureEnabled: {
    label: "Z ordering controls",
    description: "Change the persistent z-index of nodes using the context menu.",
    children: {
      zOrderingControlShowOneLayerShiftOptions: {
        label: "Show one layer shift options",
        description: "When enabled, you can move nodes one layer forward or backward.",
        type: "boolean"
      }
    }
  },
  aspectRatioControlFeatureEnabled: {
    label: "Aspect ratio control",
    description: "Change the aspect ratio of nodes using the context menu.",
    children: {}
  },
  variableBreakpointFeatureEnabled: {
    label: "Variable breakpoint",
    description: `Change the zoom breakpoint (the zoom level at which the nodes won't render their content anymore) on a per-node basis using the ${VARIABLE_BREAKPOINT_CSS_VAR} CSS variable.`,
    infoSection: "variable-breakpoints",
    children: {}
  },
  autoResizeNodeFeatureEnabled: {
    label: "Auto resize node",
    description: "Automatically resize the height of a node to fit the content.",
    infoSection: "auto-node-resizing",
    children: {
      autoResizeNodeEnabledByDefault: {
        label: "Enable auto resize by default",
        description: "When enabled, the auto resize feature will be enabled by default for all nodes.",
        type: "boolean"
      },
      autoResizeNodeMaxHeight: {
        label: "Max height",
        description: "The maximum height of the node when auto resizing (-1 for unlimited).",
        type: "number",
        parse: (value) => {
          var _a;
          return Math.max(-1, (_a = parseInt(value)) != null ? _a : -1);
        }
      },
      autoResizeNodeSnapToGrid: {
        label: "Snap to grid",
        description: "When enabled, the height of the node will snap to the grid.",
        type: "boolean"
      }
    }
  },
  canvasEncapsulationEnabled: {
    label: "Canvas encapsulation",
    description: "Encapsulate a selection of nodes and edges into a new canvas using the context menu.",
    infoSection: "encapsulate-selection",
    children: {}
  },
  betterReadonlyEnabled: {
    label: "Better readonly",
    description: "Improve the readonly mode.",
    infoSection: "better-readonly",
    children: {
      hideBackgroundGridWhenInReadonly: {
        label: "Hide background grid when in readonly",
        description: "When enabled, the background grid will be hidden when in readonly mode.",
        type: "boolean"
      }
    }
  },
  edgeHighlightEnabled: {
    label: "Edge highlight",
    description: "Highlight outgoing (and optionally incoming) edges of a selected node.",
    infoSection: "edge-highlight",
    children: {
      highlightIncomingEdges: {
        label: "Highlight incoming edges",
        description: "When enabled, incoming edges will also be highlighted.",
        type: "boolean"
      }
    }
  },
  edgeSelectionEnabled: {
    label: "Edge selection",
    description: "Select edges connected to the selected node(s) using the popup menu.",
    infoSection: "edge-selection",
    children: {
      selectEdgeByDirection: {
        label: "Select edge by direction",
        description: "Select incoming or outgoing edges using separate popup menu items.",
        type: "boolean"
      }
    }
  },
  focusModeFeatureEnabled: {
    label: "Focus mode",
    description: "Focus on a single node and blur all other nodes.",
    infoSection: "focus-mode",
    children: {}
  }
};
var SettingsManager = class {
  constructor(plugin) {
    this.plugin = plugin;
    this.nodeCssStylesManager = GET_NODE_CSS_STYLES_MANAGER(plugin);
    this.edgeCssStylesManager = GET_EDGE_CSS_STYLES_MANAGER(plugin);
  }
  async loadSettings() {
    this.settings = Object.assign({}, DEFAULT_SETTINGS_VALUES, await this.plugin.loadData());
    this.plugin.app.workspace.trigger("advanced-canvas:settings-changed");
  }
  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("advanced-canvas:settings-changed");
  }
  addSettingsTab() {
    this.settingsTab = new AdvancedCanvasPluginSettingTab(this.plugin, this);
    this.plugin.addSettingTab(this.settingsTab);
  }
};
var AdvancedCanvasPluginSettingTab = class extends import_obsidian4.PluginSettingTab {
  constructor(plugin, settingsManager) {
    super(plugin.app, plugin);
    this.settingsManager = settingsManager;
  }
  display() {
    const { containerEl } = this;
    containerEl.empty();
    this.createKofiBanner(containerEl);
    for (const [headingId, heading] of Object.entries(SETTINGS)) {
      this.createFeatureHeading(
        containerEl,
        heading.label,
        heading.description,
        heading.infoSection,
        heading.disableToggle ? null : headingId
      );
      const settingsHeaderChildrenContainerEl = document.createElement("div");
      settingsHeaderChildrenContainerEl.classList.add("settings-header-children");
      settingsHeaderChildrenContainerEl.appendChild(document.createElement("span"));
      containerEl.appendChild(settingsHeaderChildrenContainerEl);
      for (const [settingId, setting] of Object.entries(heading.children)) {
        if (!(settingId in DEFAULT_SETTINGS_VALUES)) continue;
        switch (setting.type) {
          case "text":
            this.createTextSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "number":
            this.createNumberSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "dimension":
            this.createDimensionSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "boolean":
            this.createBooleanSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "dropdown":
            this.createDropdownSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "button":
            this.createButtonSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
          case "styles":
            this.createStylesSetting(settingsHeaderChildrenContainerEl, settingId, setting);
            break;
        }
      }
    }
  }
  createFeatureHeading(containerEl, label, description, infoSection, settingsKey) {
    const setting = new import_obsidian4.Setting(containerEl).setHeading().setClass("ac-settings-heading").setName(label).setDesc(description);
    if (infoSection !== void 0) {
      setting.addExtraButton(
        (button) => button.setTooltip("Open github documentation").setIcon("info").onClick(async () => {
          window.open(`${README_URL}#${infoSection}`);
        })
      );
    }
    if (settingsKey !== null) {
      setting.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_obsidian4.Notice("Reload obsidian to apply the changes.");
        })
      );
    }
    return setting;
  }
  createTextSetting(containerEl, settingId, setting) {
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addText(
      (text) => text.setValue(this.settingsManager.getSetting(settingId)).onChange(async (value) => {
        await this.settingsManager.setSetting({ [settingId]: setting.parse ? setting.parse(value) : value });
      })
    );
  }
  createNumberSetting(containerEl, settingId, setting) {
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addText(
      (text) => text.setValue(this.settingsManager.getSetting(settingId).toString()).onChange(async (value) => {
        await this.settingsManager.setSetting({ [settingId]: setting.parse(value) });
      })
    );
  }
  createDimensionSetting(containerEl, settingId, setting) {
    let text1;
    let text2;
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addText((text) => {
      text1 = text.setValue(this.settingsManager.getSetting(settingId)[0].toString()).onChange(async (value) => await this.settingsManager.setSetting({ [settingId]: setting.parse([value, text2.getValue()]) }));
    }).addText((text) => {
      text2 = text.setValue(this.settingsManager.getSetting(settingId)[1].toString()).onChange(async (value) => await this.settingsManager.setSetting({ [settingId]: setting.parse([text1.getValue(), value]) }));
    });
  }
  createBooleanSetting(containerEl, settingId, setting) {
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addToggle(
      (toggle) => toggle.setValue(this.settingsManager.getSetting(settingId)).onChange(async (value) => {
        await this.settingsManager.setSetting({ [settingId]: value });
      })
    );
  }
  createDropdownSetting(containerEl, settingId, setting) {
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addDropdown(
      (dropdown) => dropdown.addOptions(setting.options).setValue(this.settingsManager.getSetting(settingId)).onChange(async (value) => {
        await this.settingsManager.setSetting({ [settingId]: value });
      })
    );
  }
  createButtonSetting(containerEl, settingId, setting) {
    new import_obsidian4.Setting(containerEl).setName(setting.label).setDesc(setting.description).addButton(
      (button) => button.setButtonText("Open").onClick(() => setting.onClick())
    );
  }
  createStylesSetting(containerEl, settingId, setting) {
    const nestedContainerEl = document.createElement("details");
    nestedContainerEl.classList.add("setting-item");
    containerEl.appendChild(nestedContainerEl);
    const summaryEl = document.createElement("summary");
    summaryEl.textContent = setting.label;
    nestedContainerEl.appendChild(summaryEl);
    for (const styleAttribute of setting.getParameters(this.settingsManager)) {
      new import_obsidian4.Setting(nestedContainerEl).setName(styleAttribute.label).addDropdown(
        (dropdown) => {
          var _a;
          return dropdown.addOptions(Object.fromEntries(styleAttribute.options.map((option) => [option.value, option.value === null ? `${option.label} (default)` : option.label]))).setValue((_a = this.settingsManager.getSetting(settingId)[styleAttribute.key]) != null ? _a : "null").onChange(async (value) => {
            const newValue = this.settingsManager.getSetting(settingId);
            if (value === "null") delete newValue[styleAttribute.key];
            else newValue[styleAttribute.key] = value;
            await this.settingsManager.setSetting({
              [settingId]: newValue
            });
          });
        }
      );
    }
  }
  async createKofiBanner(containerEl) {
    const banner = document.createElement("div");
    banner.classList.add("kofi-banner");
    const title = document.createElement("h1");
    title.textContent = "Enjoying the plugin?";
    banner.appendChild(title);
    const description = document.createElement("p");
    description.innerHTML = `
      Currently, Advanced Canvas has received <b>about ${RECEIVED_DONATIONS}\xA3</b> in donations with a total of <b>about ${SPENT_HOURS} hours</b> spent on development. <br>
      <br>
      Please help me develop this plugin further by reaching the goal of <b>${HOURLY_RATE_GOAL}\xA3/hour</b> \u2764\uFE0F
    `;
    banner.appendChild(description);
    const progressContainer = document.createElement("div");
    progressContainer.classList.add("progress-container");
    const progressbar = document.createElement("progress");
    progressbar.value = RECEIVED_DONATIONS / SPENT_HOURS;
    progressbar.max = HOURLY_RATE_GOAL;
    progressContainer.appendChild(progressbar);
    const hourlyRate = document.createElement("span");
    hourlyRate.classList.add("hourly-rate");
    hourlyRate.textContent = `${(RECEIVED_DONATIONS / SPENT_HOURS).toString()}\xA3/h`;
    progressContainer.appendChild(hourlyRate);
    banner.appendChild(progressContainer);
    const koFiButton = document.createElement("a");
    koFiButton.classList.add("ac-kofi-button");
    koFiButton.href = KOFI_PAGE_URL;
    koFiButton.target = "_blank";
    const koFiImage = document.createElement("img");
    koFiImage.src = KOFI_BADGE_URI;
    koFiButton.appendChild(koFiImage);
    banner.appendChild(koFiButton);
    containerEl.appendChild(banner);
  }
};

// src/managers/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/patchers/canvas-patcher.ts
var import_view = require("@codemirror/view");

// 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/patchers/canvas-patcher.ts
var import_obsidian5 = require("obsidian");

// 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/patchers/patcher.ts
var Patcher = class _Patcher {
  constructor(plugin) {
    this.plugin = plugin;
    this.patch();
  }
  static async waitForMapValueLookup(map, viewType, patch) {
    return new Promise((resolve) => {
      const uninstaller = around(map, {
        [viewType]: (next) => function(...args) {
          const view = next.call(this, ...args);
          patch(view);
          const patchedView = next.call(this, ...args);
          uninstaller();
          resolve(patchedView);
          return patchedView;
        }
      });
    });
  }
  static async waitForViewRequest(plugin, viewType, patch) {
    return this.waitForMapValueLookup(plugin.app.viewRegistry.viewByType, viewType, patch);
  }
  static OverrideExisting(fn) {
    return Object.assign(fn, { __overrideExisting: true });
  }
  static patchThisAndPrototype(plugin, object, patches, uninstallers) {
    _Patcher.patch(plugin, object, patches, false, uninstallers);
    return _Patcher.patchPrototype(plugin, object, patches, uninstallers);
  }
  static patchPrototype(plugin, target, patches, uninstallers) {
    return _Patcher.patch(plugin, target, patches, true, uninstallers);
  }
  static patch(plugin, object, patches, prototype = false, uninstallers) {
    if (!object) return null;
    const target = prototype ? object.constructor.prototype : object;
    for (const key of Object.keys(patches)) {
      const patch = patches[key];
      if (patch == null ? void 0 : patch.__overrideExisting) {
        if (typeof target[key] !== "function")
          throw new Error(`Method ${String(key)} does not exist on target`);
      }
    }
    const uninstaller = around(target, patches);
    if (uninstallers) uninstallers.push(uninstaller);
    plugin.register(uninstaller);
    return object;
  }
  static async patchOnce(plugin, object, patches) {
    const uninstallers = [];
    const value = await new Promise(
      (resolve) => this.patch(plugin, object, patches(resolve), false, uninstallers)
    );
    for (const uninstall of uninstallers) uninstall();
    return value;
  }
  static tryPatchWorkspacePrototype(plugin, getTarget, patches, uninstallers) {
    return new Promise((resolve) => {
      const result = _Patcher.patchPrototype(plugin, getTarget(), patches, uninstallers);
      if (result) {
        resolve(result);
        return;
      }
      const listener = plugin.app.workspace.on("layout-change", () => {
        const result2 = _Patcher.patchPrototype(plugin, getTarget(), patches, uninstallers);
        if (result2) {
          plugin.app.workspace.offref(listener);
          resolve(result2);
        }
      });
      plugin.registerEvent(listener);
    });
  }
};

// src/utils/migration-helper.ts
var CURRENT_SPEC_VERSION = "1.0-1.0";
var _MigrationHelper = class _MigrationHelper {
  static needsMigration(canvas) {
    var _a;
    return ((_a = canvas.metadata) == null ? void 0 : _a.version) !== CURRENT_SPEC_VERSION;
  }
  static migrate(canvas) {
    var _a, _b;
    let version = (_b = (_a = canvas.metadata) == null ? void 0 : _a.version) != null ? _b : "undefined";
    if (version === CURRENT_SPEC_VERSION) return canvas;
    while (version !== CURRENT_SPEC_VERSION) {
      const migrationFunction = _MigrationHelper.MIGRATIONS[version];
      if (!migrationFunction) {
        console.error(`No migration function found for version ${version}. Critical error!`);
        break;
      }
      const { version: newVersion, canvas: migratedCanvas } = migrationFunction(canvas);
      version = newVersion;
      canvas = migratedCanvas;
      if (!canvas.metadata) canvas.metadata = { version, frontmatter: {} };
      else canvas.metadata.version = version;
    }
    return canvas;
  }
};
_MigrationHelper.MIGRATIONS = {
  undefined: (canvas) => {
    var _a, _b, _c;
    const TARGET_SPEC_VERSION = "1.0-1.0";
    let startNode;
    const globalInterdimensionalEdges = {};
    for (const node of (_a = canvas.nodes) != null ? _a : []) {
      node.dynamicHeight = node.autoResizeHeight;
      delete node.autoResizeHeight;
      node.ratio = node.sideRatio;
      delete node.sideRatio;
      node.collapsed = node.isCollapsed;
      delete node.isCollapsed;
      if (node.portalToFile) {
        node.portal = true;
        delete node.portalToFile;
      }
      if (node.isStartNode) {
        startNode = node.id;
        delete node.isStartNode;
      }
      if (node.edgesToNodeFromPortal) {
        const edgesToNodeFromPortal = node.edgesToNodeFromPortal;
        for (const [portalId, edges] of Object.entries(edgesToNodeFromPortal)) {
          if (!(portalId in globalInterdimensionalEdges)) globalInterdimensionalEdges[portalId] = [];
          for (const edge of edges) {
            if (edge.fromNode !== node.id) edge.fromNode = `${portalId}-${edge.fromNode}`;
            if (edge.toNode !== node.id) edge.toNode = `${portalId}-${edge.toNode}`;
          }
          globalInterdimensionalEdges[portalId].push(...edges);
        }
        delete node.edgesToNodeFromPortal;
      }
    }
    for (const node of (_b = canvas.nodes) != null ? _b : []) {
      if (!(node.id in globalInterdimensionalEdges)) continue;
      node.interdimensionalEdges = globalInterdimensionalEdges[node.id];
    }
    (_c = canvas.metadata) != null ? _c : canvas.metadata = {
      version: TARGET_SPEC_VERSION,
      frontmatter: {},
      startNode
    };
    return { version: TARGET_SPEC_VERSION, canvas };
  }
};
var MigrationHelper = _MigrationHelper;

// src/patchers/canvas-patcher.ts
var CanvasPatcher = class extends Patcher {
  async patch() {
    const loadedCanvasViewLeafs = this.plugin.app.workspace.getLeavesOfType("canvas").filter((leaf) => !(0, import_obsidian5.requireApiVersion)("1.7.2") || !leaf.isDeferred);
    if (loadedCanvasViewLeafs.length > 0) {
      console.debug(`Patching and reloading loaded canvas views (Count: ${loadedCanvasViewLeafs.length})`);
      this.patchCanvas(loadedCanvasViewLeafs.first().view);
      for (const leaf of loadedCanvasViewLeafs) leaf.rebuildView();
    } else {
      await Patcher.waitForViewRequest(this.plugin, "canvas", (view) => this.patchCanvas(view));
      console.debug(`Patched canvas view on first request`);
    }
  }
  patchCanvas(view) {
    const that = this;
    Patcher.patchPrototype(this.plugin, view, {
      setEphemeralState: Patcher.OverrideExisting((next) => function(state) {
        var _a, _b, _c;
        if (state == null ? void 0 : state.subpath) {
          const nodeId = state.subpath.replace(/^#/, "");
          const node = this.canvas.nodes.get(nodeId);
          if (node) {
            this.canvas.selectOnly(node);
            this.canvas.zoomToSelection();
            return;
          }
        }
        if (((_b = (_a = state.match) == null ? void 0 : _a.matches) == null ? void 0 : _b[0]) && !((_c = state.match) == null ? void 0 : _c.nodeId)) {
          const match = state.match.matches[0];
          const elementType = match[0] === 0 ? "nodes" : "edges";
          const elementIndex = match[1];
          const element = elementType === "nodes" ? Array.from(this.canvas.nodes.values())[elementIndex] : Array.from(this.canvas.edges.values())[elementIndex];
          if (element) {
            this.canvas.selectOnly(element);
            this.canvas.zoomToSelection();
            return;
          }
        }
        return next.call(this, state);
      }),
      setViewData: Patcher.OverrideExisting((next) => function(json, ...args) {
        json = json !== "" ? json : '{"nodes": [], "edges": []}';
        try {
          const canvasData = dist_default.parse(json);
          if (MigrationHelper.needsMigration(canvasData)) {
            if (this.file) that.plugin.createFileSnapshot(this.file.path, json);
            json = JSON.stringify(MigrationHelper.migrate(canvasData));
          }
        } catch (e) {
          console.error("Failed to migrate canvas data:", e);
        }
        let result;
        try {
          result = next.call(this, json, ...args);
        } catch (e) {
          console.error("Invalid JSON, repairing through Advanced Canvas:", e);
          if (this.file) that.plugin.createFileSnapshot(this.file.path, json);
          json = JSON.stringify(dist_default.parse(json), null, 2);
          result = next.call(this, json, ...args);
        }
        that.plugin.app.workspace.trigger("advanced-canvas:canvas-changed", this.canvas);
        return result;
      }),
      close: Patcher.OverrideExisting((next) => function(...args) {
        that.plugin.app.workspace.trigger("advanced-canvas:canvas-view-unloaded:before", this);
        return next.call(this, ...args);
      })
    });
    Patcher.patchPrototype(this.plugin, view.canvas, {
      markViewportChanged: Patcher.OverrideExisting((next) => function(...args) {
        that.plugin.app.workspace.trigger("advanced-canvas:viewport-changed:before", this);
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:viewport-changed:after", this);
        return result;
      }),
      markMoved: Patcher.OverrideExisting((next) => function(node) {
        const result = next.call(this, node);
        if (!this.viewportChanged) {
          if (node.prevX !== node.x || node.prevY !== node.y)
            that.plugin.app.workspace.trigger("advanced-canvas:node-moved", this, node, !this.isDragging);
          if (node.prevWidth !== node.width || node.prevHeight !== node.height)
            that.plugin.app.workspace.trigger("advanced-canvas:node-resized", this, node);
        }
        node.prevX = node.x;
        node.prevY = node.y;
        node.prevWidth = node.width;
        node.prevHeight = node.height;
        return result;
      }),
      onDoubleClick: Patcher.OverrideExisting((next) => function(event) {
        const preventDefault = { value: false };
        that.plugin.app.workspace.trigger("advanced-canvas:double-click", this, event, preventDefault);
        if (!preventDefault.value) next.call(this, event);
      }),
      setDragging: Patcher.OverrideExisting((next) => function(dragging) {
        const result = next.call(this, dragging);
        that.plugin.app.workspace.trigger("advanced-canvas:dragging-state-changed", this, dragging);
        return result;
      }),
      // OBSIDIAN-FIX
      cloneData: Patcher.OverrideExisting((next) => function(elements, shift) {
        const result = next.call(this, elements, shift);
        elements.nodes = elements.nodes.map((nodeData) => JSON.parse(JSON.stringify(nodeData)));
        elements.edges = elements.edges.map((edgeData) => JSON.parse(JSON.stringify(edgeData)));
        return result;
      }),
      getContainingNodes: Patcher.OverrideExisting((next) => function(bbox) {
        const result = next.call(this, bbox);
        that.plugin.app.workspace.trigger("advanced-canvas:containing-nodes-requested", this, bbox, result);
        return result;
      }),
      updateSelection: Patcher.OverrideExisting((next) => function(update) {
        const oldSelection = new Set(this.selection);
        const result = next.call(this, update);
        that.plugin.app.workspace.trigger("advanced-canvas:selection-changed", this, oldSelection, (update2) => next.call(this, update2));
        return result;
      }),
      createTextNode: Patcher.OverrideExisting((next) => function(...args) {
        const node = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-created", this, node);
        return node;
      }),
      createFileNode: Patcher.OverrideExisting((next) => function(...args) {
        const node = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-created", this, node);
        return node;
      }),
      createFileNodes: Patcher.OverrideExisting((next) => function(...args) {
        const nodes = next.call(this, ...args);
        nodes.forEach((node) => that.plugin.app.workspace.trigger("advanced-canvas:node-created", this, node));
        return nodes;
      }),
      createGroupNode: Patcher.OverrideExisting((next) => function(...args) {
        const node = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-created", this, node);
        return node;
      }),
      createLinkNode: Patcher.OverrideExisting((next) => function(...args) {
        const node = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-created", this, node);
        return node;
      }),
      addNode: Patcher.OverrideExisting((next) => function(node) {
        that.patchNode(node);
        return next.call(this, node);
      }),
      addEdge: Patcher.OverrideExisting((next) => function(edge) {
        that.patchEdge(edge);
        if (!this.viewportChanged) that.plugin.app.workspace.trigger("advanced-canvas:edge-created", this, edge);
        return next.call(this, edge);
      }),
      removeNode: Patcher.OverrideExisting((next) => function(node) {
        const result = next.call(this, node);
        if (!this.isClearing) that.plugin.app.workspace.trigger("advanced-canvas:node-removed", this, node);
        return result;
      }),
      removeEdge: Patcher.OverrideExisting((next) => function(edge) {
        const result = next.call(this, edge);
        if (!this.isClearing) that.plugin.app.workspace.trigger("advanced-canvas:edge-removed", this, edge);
        return result;
      }),
      handleCopy: Patcher.OverrideExisting((next) => function(...args) {
        this.isCopying = true;
        const result = next.call(this, ...args);
        this.isCopying = false;
        return result;
      }),
      handlePaste: Patcher.OverrideExisting((next) => function(...args) {
        this.isPasting = true;
        const result = next.call(this, ...args);
        this.isPasting = false;
        return result;
      }),
      getSelectionData: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        if (this.isCopying) that.plugin.app.workspace.trigger("advanced-canvas:copy", this, result);
        return result;
      }),
      zoomToBbox: Patcher.OverrideExisting((next) => function(bbox) {
        that.plugin.app.workspace.trigger("advanced-canvas:zoom-to-bbox:before", this, bbox);
        const result = next.call(this, bbox);
        that.plugin.app.workspace.trigger("advanced-canvas:zoom-to-bbox:after", this, bbox);
        return result;
      }),
      // Custom
      zoomToRealBbox: (_next) => function(bbox) {
        if (this.canvasRect.width === 0 || this.canvasRect.height === 0) return;
        that.plugin.app.workspace.trigger("advanced-canvas:zoom-to-bbox:before", this, bbox);
        const widthZoom = this.canvasRect.width / (bbox.maxX - bbox.minX);
        const heightZoom = this.canvasRect.height / (bbox.maxY - bbox.minY);
        const zoom = this.screenshotting ? Math.min(widthZoom, heightZoom) : Math.clamp(Math.min(widthZoom, heightZoom), -4, 1);
        this.tZoom = Math.log2(zoom);
        this.zoomCenter = null;
        this.tx = (bbox.minX + bbox.maxX) / 2;
        this.ty = (bbox.minY + bbox.maxY) / 2;
        this.markViewportChanged();
        that.plugin.app.workspace.trigger("advanced-canvas:zoom-to-bbox:after", this, bbox);
      },
      setReadonly: Patcher.OverrideExisting((next) => function(readonly) {
        const result = next.call(this, readonly);
        that.plugin.app.workspace.trigger("advanced-canvas:readonly-changed", this, readonly);
        return result;
      }),
      undo: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        this.importData(this.getData(), true);
        that.plugin.app.workspace.trigger("advanced-canvas:undo", this);
        return result;
      }),
      redo: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        this.importData(this.getData(), true);
        that.plugin.app.workspace.trigger("advanced-canvas:redo", this);
        return result;
      }),
      clear: Patcher.OverrideExisting((next) => function(...args) {
        this.isClearing = true;
        const result = next.call(this, ...args);
        this.isClearing = false;
        return result;
      }),
      /*setData: Patcher.OverrideExisting(next => function (...args: any): void {
        //
        const result = next.call(this, ...args)
        //
        return result
      }),*/
      getData: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:data-requested", this, result);
        return result;
      }),
      importData: Patcher.OverrideExisting((next) => function(data, clearCanvas, silent) {
        var _a;
        const targetFilePath = (_a = this.view.file) == null ? void 0 : _a.path;
        const setData = (data2) => {
          if (!this.view.file || this.view.file.path !== targetFilePath) return;
          this.importData(data2, true, true);
        };
        if (!silent) that.plugin.app.workspace.trigger("advanced-canvas:data-loaded:before", this, data, setData);
        const result = next.call(this, data, clearCanvas);
        if (!silent) that.plugin.app.workspace.trigger("advanced-canvas:data-loaded:after", this, data, setData);
        return result;
      }),
      requestSave: Patcher.OverrideExisting((next) => function(...args) {
        that.plugin.app.workspace.trigger("advanced-canvas:canvas-saved:before", this);
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:canvas-saved:after", this);
        return result;
      })
    });
    Patcher.patchPrototype(this.plugin, view.canvas.menu, {
      render: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:popup-menu-created", this.canvas);
        next.call(this);
        return result;
      })
    });
    Patcher.patchPrototype(this.plugin, view.canvas.nodeInteractionLayer, {
      setTarget: Patcher.OverrideExisting((next) => function(node) {
        const result = next.call(this, node);
        that.plugin.app.workspace.trigger("advanced-canvas:node-interaction", this.canvas, node);
        return result;
      })
    });
    this.plugin.registerEditorExtension([import_view.EditorView.updateListener.of((update) => {
      if (!update.docChanged) return;
      const editor = update.state.field(import_obsidian5.editorInfoField);
      const node = editor.node;
      if (!node) return;
      that.plugin.app.workspace.trigger("advanced-canvas:node-text-content-changed", node.canvas, node, update);
    })]);
  }
  patchNode(node) {
    const that = this;
    Patcher.patch(this.plugin, node, {
      setData: Patcher.OverrideExisting((next) => function(data, addHistory) {
        const result = next.call(this, data);
        if (node.initialized && !node.isDirty) {
          node.isDirty = true;
          that.plugin.app.workspace.trigger("advanced-canvas:node-changed", this.canvas, node);
          delete node.isDirty;
        }
        this.canvas.data = this.canvas.getData();
        if (this.initialized) this.canvas.view.requestSave();
        if (addHistory) this.canvas.pushHistory(this.canvas.data);
        return result;
      }),
      setZIndex: (_next) => function(value) {
        this.setData({
          ...this.getData(),
          zIndex: value
        }, true);
        this.updateZIndex();
      },
      updateZIndex: Patcher.OverrideExisting((next) => function() {
        const persistentZIndex = this.getData().zIndex;
        if (persistentZIndex === void 0) return next.call(this);
        this.canvas.zIndexCounter = Math.max(this.canvas.zIndexCounter, persistentZIndex);
        this.renderZIndex();
      }),
      renderZIndex: Patcher.OverrideExisting((next) => function() {
        const persistentZIndex = this.getData().zIndex;
        if (persistentZIndex === void 0) return next.call(this);
        this.zIndex = persistentZIndex;
        if (this.canvas.selection.size === 1 && this.canvas.selection.has(this))
          this.zIndex = this.canvas.zIndexCounter + 1;
        this.nodeEl.style.zIndex = this.zIndex.toString();
      }),
      setIsEditing: Patcher.OverrideExisting((next) => function(editing, ...args) {
        const result = next.call(this, editing, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-editing-state-changed", this.canvas, node, editing);
        return result;
      }),
      updateBreakpoint: Patcher.OverrideExisting((next) => function(breakpoint) {
        const breakpointRef = { value: breakpoint };
        that.plugin.app.workspace.trigger("advanced-canvas:node-breakpoint-changed", this.canvas, node, breakpointRef);
        return next.call(this, breakpointRef.value);
      }),
      getBBox: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-bbox-requested", this.canvas, node, result);
        return result;
      }),
      onConnectionPointerdown: Patcher.OverrideExisting((next) => function(e, side) {
        const addEdgeEventRef = that.plugin.app.workspace.on("advanced-canvas:edge-added", (_canvas, edge) => {
          that.plugin.app.workspace.trigger("advanced-canvas:edge-connection-dragging:before", this.canvas, edge, e, true, "to");
          that.plugin.app.workspace.offref(addEdgeEventRef);
          document.addEventListener("pointerup", (e2) => {
            that.plugin.app.workspace.trigger("advanced-canvas:edge-connection-dragging:after", this.canvas, edge, e2, true, "to");
          }, { once: true });
        });
        const result = next.call(this, e, side);
        return result;
      }),
      // File nodes
      setFile: (next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-changed", this.canvas, this);
        return result;
      },
      setFilePath: (next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:node-changed", this.canvas, this);
        return result;
      }
    });
    this.runAfterInitialized(node, () => {
      this.plugin.app.workspace.trigger("advanced-canvas:node-added", node.canvas, node);
      this.plugin.app.workspace.trigger("advanced-canvas:node-changed", node.canvas, node);
    });
  }
  patchEdge(edge) {
    const that = this;
    Patcher.patch(this.plugin, edge, {
      setData: Patcher.OverrideExisting((next) => function(data, addHistory) {
        const result = next.call(this, data);
        if (this.initialized && !this.isDirty) {
          this.isDirty = true;
          that.plugin.app.workspace.trigger("advanced-canvas:edge-changed", this.canvas, this);
          delete this.isDirty;
        }
        this.canvas.data = this.canvas.getData();
        if (this.initialized) this.canvas.view.requestSave();
        if (addHistory) this.canvas.pushHistory(this.canvas.getData());
        return result;
      }),
      render: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:edge-changed", this.canvas, this);
        return result;
      }),
      getCenter: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        that.plugin.app.workspace.trigger("advanced-canvas:edge-center-requested", this.canvas, this, result);
        return result;
      }),
      onConnectionPointerdown: Patcher.OverrideExisting((next) => function(e) {
        const cancelRef = { value: false };
        that.plugin.app.workspace.trigger("advanced-canvas:edge-connection-try-dragging:before", this.canvas, this, e, cancelRef);
        if (cancelRef.value) return;
        const previousEnds = { from: this.from, to: this.to };
        const result = next.call(this, e);
        const eventPos = this.canvas.posFromEvt(e);
        const fromPos = BBoxHelper.getCenterOfBBoxSide(this.from.node.getBBox(), this.from.side);
        const toPos = BBoxHelper.getCenterOfBBoxSide(this.to.node.getBBox(), this.to.side);
        const draggingSide = Math.hypot(eventPos.x - fromPos.x, eventPos.y - fromPos.y) > Math.hypot(eventPos.x - toPos.x, eventPos.y - toPos.y) ? "to" : "from";
        that.plugin.app.workspace.trigger("advanced-canvas:edge-connection-dragging:before", this.canvas, this, e, false, draggingSide, previousEnds);
        document.addEventListener("pointerup", (e2) => {
          that.plugin.app.workspace.trigger("advanced-canvas:edge-connection-dragging:after", this.canvas, this, e2, false, draggingSide, previousEnds);
        }, { once: true });
        return result;
      })
    });
    this.runAfterInitialized(edge, () => {
      this.plugin.app.workspace.trigger("advanced-canvas:edge-added", 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);
  }
};

// src/patchers/link-suggestions-patcher.ts
var import_obsidian6 = require("obsidian");
var LinkSuggestionsPatcher = class extends Patcher {
  async patch() {
    var _a;
    if (!this.plugin.settings.getSetting("enableSingleNodeLinks")) return;
    const suggestManager = (_a = this.plugin.app.workspace.editorSuggest.suggests.find((s) => s.suggestManager)) == null ? void 0 : _a.suggestManager;
    if (!suggestManager) return console.warn("LinkSuggestionsPatcher: No suggest manager found.");
    Patcher.patchThisAndPrototype(this.plugin, suggestManager, {
      getHeadingSuggestions: Patcher.OverrideExisting((next) => async function(context, path, subpath) {
        const result = await next.call(this, context, path, subpath);
        if (!path.endsWith(".canvas")) return result;
        const currentFilePath = this.getSourcePath();
        const targetFile = this.app.metadataCache.getFirstLinkpathDest(path, currentFilePath);
        if (!targetFile) return result;
        if (!(targetFile instanceof import_obsidian6.TFile) || targetFile.extension !== "canvas")
          return result;
        const fileCache = this.app.metadataCache.getFileCache(targetFile);
        if (!fileCache) return result;
        const canvasNodeCaches = fileCache.nodes;
        if (!canvasNodeCaches) return result;
        for (const nodeId of Object.keys(canvasNodeCaches)) {
          if (nodeId === subpath) continue;
          const suggestion = {
            file: targetFile,
            heading: nodeId,
            level: 1,
            matches: [],
            path,
            subpath: `#${nodeId}`,
            score: 0,
            type: "heading"
          };
          result.push(suggestion);
        }
        return result;
      })
    });
  }
};

// src/advanced-canvas-embed.ts
var import_obsidian7 = require("obsidian");
var AdvancedCanvasEmbed = class extends import_obsidian7.Component {
  constructor(context, file, subpath) {
    super();
    this.onModifyCallback = (file) => {
      if (file.path !== this.file.path) return;
      this.loadFile();
    };
    this.context = context;
    this.file = file;
    this.subpath = subpath;
    if (!subpath) console.warn("AdvancedCanvasEmbed: No subpath provided. This embed will not work as expected.");
  }
  onload() {
    this.context.app.vault.on("modify", this.onModifyCallback);
  }
  onunload() {
    this.context.app.vault.off("modify", this.onModifyCallback);
  }
  async loadFile() {
    if (!this.subpath) return;
    const nodeId = this.subpath.replace(/^#/, "");
    const canvasContent = await this.context.app.vault.cachedRead(this.file);
    if (!canvasContent) return console.warn("AdvancedCanvasEmbed: No canvas content found.");
    const canvasJson = JSON.parse(canvasContent);
    const canvasNode = canvasJson.nodes.find((node) => node.id === nodeId);
    if (!canvasNode) {
      this.context.containerEl.classList.add("mod-empty");
      this.context.containerEl.textContent = "Node not found";
      return;
    }
    let nodeContent = "";
    if (canvasNode.type === "text") nodeContent = canvasNode.text;
    else if (canvasNode.type === "group") nodeContent = `**Group Node:** ${canvasNode.label}`;
    else if (canvasNode.type === "file") nodeContent = `**File Node:** ${canvasNode.file}`;
    this.context.containerEl.classList.add("markdown-embed");
    this.context.containerEl.empty();
    import_obsidian7.MarkdownRenderer.render(this.context.app, nodeContent, this.context.containerEl, this.file.path, this);
  }
};

// src/patchers/embed-patcher.ts
var EmbedPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("enableSingleNodeLinks")) return;
    Patcher.patch(this.plugin, this.plugin.app.embedRegistry.embedByExtension, {
      canvas: (next) => function(context, file, subpath) {
        if (subpath) return new AdvancedCanvasEmbed(context, file, subpath);
        return next.call(this, context, file, subpath);
      }
    });
  }
};

// src/patchers/metadata-cache-patcher.ts
var import_obsidian8 = require("obsidian");

// src/utils/filepath-helper.ts
var FilepathHelper = class {
  static extension(path) {
    var _a;
    return (path == null ? void 0 : path.includes(".")) ? (_a = path == null ? void 0 : path.split(".")) == null ? void 0 : _a.pop() : void 0;
  }
};

// src/utils/hash-helper.ts
var HashHelper = class _HashHelper {
  static async getBytesHash(bytes) {
    const cryptoBytes = await crypto.subtle.digest("SHA-256", new Uint8Array(bytes));
    return _HashHelper.arrayBufferToHexString(cryptoBytes);
  }
  static arrayBufferToHexString(buffer) {
    const uint8Array = new Uint8Array(buffer);
    const hexArray = [];
    for (const byte of uint8Array) {
      hexArray.push((byte >>> 4).toString(16));
      hexArray.push((byte & 15).toString(16));
    }
    return hexArray.join("");
  }
};

// src/utils/task-queue.ts
var TaskQueue = class {
  constructor() {
    this.running = false;
    this.queue = [];
    this.onFinished = () => {
    };
  }
  async add(task) {
    return new Promise((resolve) => {
      this.queue.push([resolve, task]);
      if (!this.running) this.run();
    });
  }
  setOnFinished(callback) {
    this.onFinished = callback;
  }
  async run() {
    var _a;
    this.running = true;
    while (this.queue.length > 0) {
      const [resolver, task] = (_a = this.queue.shift()) != null ? _a : [void 0, void 0];
      await (task == null ? void 0 : task());
      resolver == null ? void 0 : resolver();
    }
    this.running = false;
    this.onFinished();
  }
};

// src/patchers/metadata-cache-patcher.ts
var MetadataCachePatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    Patcher.patchPrototype(this.plugin, this.plugin.app.metadataCache, {
      getCache: Patcher.OverrideExisting((next) => function(filepath, ...args) {
        if (FilepathHelper.extension(filepath) === "canvas") {
          if (!this.fileCache.hasOwnProperty(filepath)) return null;
          const hash = this.fileCache[filepath].hash;
          return this.metadataCache[hash] || null;
        }
        return next.call(this, filepath, ...args);
      }),
      computeFileMetadataAsync: Patcher.OverrideExisting((next) => async function(file, ...args) {
        if (file instanceof import_obsidian8.TFile && (file == null ? void 0 : file.extension) === "canvas")
          return CanvasMetadataHandler.computeCanvasFileMetadataAsync.call(this, file);
        return next.call(this, file, ...args);
      }),
      resolveLinks: Patcher.OverrideExisting((next) => async function(filepath) {
        const result = next.call(this, filepath);
        if (FilepathHelper.extension(filepath) === "canvas")
          CanvasMetadataHandler.resolveCanvasLinks.call(this, filepath);
        return result;
      })
    });
  }
};
var _CanvasMetadataHandler = class _CanvasMetadataHandler {
  static async computeCanvasFileMetadataAsync(file) {
    this.uniqueFileLookup.add(file.name.toLowerCase(), file);
    let isStale = true;
    if (!this.fileCache.hasOwnProperty(file.path))
      this.saveFileCache(file.path, { mtime: 0, size: 0, hash: "" });
    else {
      const cache2 = this.fileCache[file.path];
      const unchanged = cache2.mtime === file.stat.mtime && cache2.size === file.stat.size;
      const hasMetadataCache = cache2.hash && this.metadataCache.hasOwnProperty(cache2.hash);
      if (unchanged && hasMetadataCache)
        isStale = false;
    }
    if (isStale) {
      _CanvasMetadataHandler.linkResolveQueue.setOnFinished(() => this.trigger("finished"));
      await _CanvasMetadataHandler.metadataQueue.add(
        () => _CanvasMetadataHandler.updateMetadataCache.call(this, file)
      );
    }
    _CanvasMetadataHandler.linkResolveQueue.setOnFinished(() => this.trigger("resolved"));
    await _CanvasMetadataHandler.linkResolveQueue.add(
      () => _CanvasMetadataHandler.resolveCanvasLinks.call(this, file.path)
    );
  }
  static async updateMetadataCache(file) {
    const bytes = await this.vault.readBinary(file);
    const data = new TextDecoder().decode(new Uint8Array(bytes));
    const hash = await HashHelper.getBytesHash(bytes);
    const cache2 = {
      mtime: file.stat.mtime,
      size: file.stat.size,
      hash
    };
    this.saveFileCache(file.path, cache2);
    let metadata = this.metadataCache[cache2.hash];
    if (metadata) return this.trigger(
      "changed",
      file,
      data,
      metadata
    );
    const slowIndexingTimeout = setTimeout(() => {
      new import_obsidian8.Notice(`Canvas indexing taking a long time for file ${file.path}`);
    }, 1e4);
    try {
      metadata = await _CanvasMetadataHandler.computeCanvasMetadataAsync.call(this, data);
    } finally {
      clearTimeout(slowIndexingTimeout);
    }
    if (metadata) {
      this.saveMetaCache(hash, metadata);
      this.trigger("changed", file, data, metadata);
    } else {
      console.log("Canvas metadata failed to parse", file);
    }
  }
  static async computeCanvasMetadataAsync(data) {
    var _a, _b, _c, _d, _e;
    const content = JSON.parse(data || "{}");
    const metadata = {
      v: 1
    };
    const frontmatter = (_a = content.metadata) == null ? void 0 : _a.frontmatter;
    metadata.frontmatterPosition = {
      start: { line: 0, col: 0, offset: 0 },
      end: { line: 0, col: 0, offset: 0 }
    };
    metadata.frontmatter = frontmatter;
    metadata.frontmatterLinks = [];
    for (const [key, value] of Object.entries(frontmatter != null ? frontmatter : {})) {
      const getLinks = (value2) => value2.map((v) => {
        if (!v.startsWith("[[") || !v.endsWith("]]")) return null;
        const [link, ...aliases] = v.slice(2, -2).split("|");
        return {
          key,
          displayText: aliases.length > 0 ? aliases.join("|") : link,
          link,
          original: v
        };
      }).filter((v) => v !== null);
      if (typeof value === "string") (_b = metadata.frontmatterLinks) == null ? void 0 : _b.push(...getLinks([value]));
      else if (Array.isArray(value)) (_c = metadata.frontmatterLinks) == null ? void 0 : _c.push(...getLinks(value));
    }
    metadata.nodes = {};
    metadata.links = [];
    metadata.embeds = [];
    await Promise.all(((_d = content.nodes) != null ? _d : []).map(async (node, index) => {
      var _a2, _b2;
      if (node.type !== "text") return;
      const text = node.text;
      const buffer = new TextEncoder().encode(text).buffer;
      const nodeMetadata = await this.computeMetadataAsync(buffer);
      if (!nodeMetadata) return;
      metadata.nodes[node.id] = nodeMetadata;
      metadata.links.push(...((_a2 = nodeMetadata.links) != null ? _a2 : []).map((link) => ({
        ...link,
        position: {
          nodeId: node.id,
          start: { line: 0, col: 1, offset: 0 },
          // 0 for node
          end: { line: 0, col: 1, offset: index }
          // index of node
        }
      })));
      metadata.embeds.push(...((_b2 = nodeMetadata.embeds) != null ? _b2 : []).map((embed2) => ({
        ...embed2,
        position: {
          nodeId: node.id,
          start: { line: 0, col: 1, offset: 0 },
          // 0 for node
          end: { line: 0, col: 1, offset: index }
          // index of node
        }
      })));
    }));
    for (const [index, node] of ((_e = content.nodes) != null ? _e : []).entries()) {
      if (node.type !== "file") continue;
      const file = node.file;
      if (!file) continue;
      metadata.embeds.push({
        link: file,
        original: file,
        displayText: file,
        position: {
          start: { line: 0, col: 1, offset: 0 },
          // 0 for nodes
          end: { line: 0, col: 1, offset: index }
          // index of node
        }
      });
    }
    return metadata;
  }
  static async resolveCanvasLinks(filepath) {
    var _a, _b, _c, _d, _e;
    const file = this.vault.getAbstractFileByPath(filepath);
    if (!(file instanceof import_obsidian8.TFile)) return;
    const metadata = this.getFileCache(file);
    const references = [...(_a = metadata == null ? void 0 : metadata.links) != null ? _a : [], ...(_b = metadata == null ? void 0 : metadata.embeds) != null ? _b : []];
    const referenceLinks = references.map((ref) => ref.link).sort();
    const resolvedLinks = {};
    const unresolvedLinks = {};
    for (const link of referenceLinks) {
      const resolved = this.getFirstLinkpathDest(link, filepath);
      if (resolved) {
        (_d = resolvedLinks[_c = resolved.path]) != null ? _d : resolvedLinks[_c] = 0;
        resolvedLinks[resolved.path]++;
      } else {
        const strippedLink = link.endsWith(".md") ? link.slice(0, -3) : link;
        (_e = unresolvedLinks[strippedLink]) != null ? _e : unresolvedLinks[strippedLink] = 0;
        unresolvedLinks[strippedLink]++;
      }
    }
    this.resolvedLinks[filepath] = resolvedLinks;
    this.unresolvedLinks[filepath] = unresolvedLinks;
    await sleep(1);
    this.trigger("resolve", file);
  }
};
_CanvasMetadataHandler.metadataQueue = new TaskQueue();
_CanvasMetadataHandler.linkResolveQueue = new TaskQueue();
var CanvasMetadataHandler = _CanvasMetadataHandler;

// src/patchers/backlinks-patcher.ts
var import_obsidian9 = require("obsidian");
var BacklinksPatcher = class extends Patcher {
  constructor() {
    super(...arguments);
    this.isRecomputingBacklinks = false;
  }
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    const that = this;
    await Patcher.waitForViewRequest(this.plugin, "backlink", (view) => {
      Patcher.patchPrototype(this.plugin, view.backlink, {
        recomputeBacklink: Patcher.OverrideExisting((next) => function(file, ...args) {
          that.isRecomputingBacklinks = true;
          const result = next.call(this, file, ...args);
          that.isRecomputingBacklinks = false;
          return result;
        })
      });
    });
    Patcher.patchPrototype(this.plugin, this.plugin.app.vault, {
      recurseChildrenAC: (_next) => function(origin, traverse) {
        for (let stack = [origin]; stack.length > 0; ) {
          const current = stack.pop();
          if (current) {
            traverse(current);
            if (current instanceof import_obsidian9.TFolder) stack = stack.concat(current.children);
          }
        }
      },
      getMarkdownFiles: Patcher.OverrideExisting((next) => function(...args) {
        if (!that.isRecomputingBacklinks) return next.call(this, ...args);
        const files = [];
        const root = this.getRoot();
        this.recurseChildrenAC(root, (child) => {
          if (child instanceof import_obsidian9.TFile && (child.extension === "md" || child.extension === "canvas")) {
            files.push(child);
          }
        });
        return files;
      })
    });
  }
};

// src/patchers/outgoing-links-patcher.ts
var OutgoingLinksPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    await Patcher.waitForViewRequest(this.plugin, "outgoing-link", (view) => {
      Patcher.patchPrototype(this.plugin, view.outgoingLink, {
        recomputeLinks: Patcher.OverrideExisting((next) => function(...args) {
          var _a;
          const isCanvas = ((_a = this.file) == null ? void 0 : _a.extension) === "canvas";
          if (isCanvas) this.file.extension = "md";
          const result = next.call(this, ...args);
          if (isCanvas) this.file.extension = "canvas";
          return result;
        }),
        recomputeUnlinked: Patcher.OverrideExisting((next) => function(...args) {
          var _a;
          const isCanvas = ((_a = this.file) == null ? void 0 : _a.extension) === "canvas";
          if (isCanvas) this.file.extension = "md";
          const result = next.call(this, ...args);
          if (isCanvas) this.file.extension = "canvas";
          return result;
        })
      });
    });
  }
};

// src/patchers/file-manager-patcher.ts
var FileManagerPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    const that = this;
    Patcher.patch(this.plugin, this.plugin.app.fileManager, {
      processFrontMatter: Patcher.OverrideExisting((next) => async function(file, fn, options) {
        if ((file == null ? void 0 : file.extension) === "canvas") {
          that.plugin.app.vault.process(file, (data) => {
            const content = JSON.parse(data);
            fn(content.metadata.frontmatter);
            return JSON.stringify(content, null, 2);
          });
          return;
        }
        return next.call(this, file, fn, options);
      })
    });
  }
};

// src/patchers/properties-patcher.ts
var PropertiesPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    if (!this.plugin.app.viewRegistry.viewByType["file-properties"]) return;
    await Patcher.waitForViewRequest(this.plugin, "file-properties", (view) => {
      Patcher.patchPrototype(this.plugin, view, {
        isSupportedFile: Patcher.OverrideExisting((next) => function(file) {
          if ((file == null ? void 0 : file.extension) === "canvas") return true;
          return next.call(this, file);
        }),
        updateFrontmatter: Patcher.OverrideExisting((next) => function(file, content) {
          var _a, _b, _c;
          if ((file == null ? void 0 : file.extension) === "canvas") {
            let frontmatter;
            try {
              frontmatter = (_c = (_b = (_a = JSON.parse(content)) == null ? void 0 : _a.metadata) == null ? void 0 : _b.frontmatter) != null ? _c : {};
            } catch (e) {
              frontmatter = {};
            }
            this.rawFrontmatter = JSON.stringify(frontmatter, null, 2);
            this.frontmatter = frontmatter;
            return frontmatter;
          }
          return next.call(this, file, content);
        }),
        saveFrontmatter: Patcher.OverrideExisting((next) => function(frontmatter) {
          var _a;
          if (((_a = this.file) == null ? void 0 : _a.extension) === "canvas") {
            if (this.file !== this.modifyingFile) return;
            this.app.vault.process(this.file, (data) => {
              const content = JSON.parse(data);
              if (content == null ? void 0 : content.metadata) content.metadata.frontmatter = frontmatter;
              return JSON.stringify(content, null, 2);
            });
            return;
          }
          return next.call(this, frontmatter);
        })
      });
    });
  }
};

// src/patchers/search-patcher.ts
var SearchPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    const that = this;
    await Patcher.waitForViewRequest(this.plugin, "search", (view) => {
      const uninstallers = [];
      Patcher.patchThisAndPrototype(this.plugin, view, {
        startSearch: (next) => function(...args) {
          const result = next.call(this, ...args);
          if (this.searchQuery) {
            that.patchSearchQuery(this.searchQuery);
            uninstallers.forEach((uninstall) => uninstall());
          }
          return result;
        }
      }, uninstallers);
    });
  }
  patchSearchQuery(searchQuery) {
    Patcher.patchThisAndPrototype(this.plugin, searchQuery, {
      _match: Patcher.OverrideExisting((next) => function(data) {
        var _a, _b;
        const isCanvas = (_b = (_a = data.strings.filepath) == null ? void 0 : _a.endsWith(".canvas")) != null ? _b : false;
        if (isCanvas && !data.cache)
          data.cache = this.app.metadataCache.getCache(data.strings.filepath);
        return next.call(this, data);
      })
    });
  }
};

// src/patchers/search-command-patcher.ts
var import_obsidian10 = require("obsidian");
var SearchCommandPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("nativeFileSearchEnabled")) return;
    const that = this;
    Patcher.patch(this.plugin, this.plugin.app.commands.commands["editor:open-search"], {
      checkCallback: Patcher.OverrideExisting((next) => function(checking) {
        if (that.plugin.app.workspace.activeEditor) return next.call(this, checking);
        const activeCanvasView = that.plugin.getCurrentCanvasView();
        if (!activeCanvasView) return next.call(this, checking);
        if (checking) return true;
        if (!activeCanvasView.canvas.searchEl) new CanvasSearchView(activeCanvasView);
        return true;
      })
    });
  }
};
var CanvasSearchView = class {
  constructor(view) {
    this.searchMatches = [];
    this.matchIndex = 0;
    this.view = view;
    this.createSearchView();
  }
  createSearchView() {
    this.containerEl = document.createElement("div");
    this.containerEl.className = "document-search-container";
    const documentSearch = document.createElement("div");
    documentSearch.className = "document-search";
    this.containerEl.appendChild(documentSearch);
    const searchInputContainer = document.createElement("div");
    searchInputContainer.className = "search-input-container document-search-input";
    documentSearch.appendChild(searchInputContainer);
    this.searchInput = document.createElement("input");
    this.searchInput.type = "text";
    this.searchInput.placeholder = "Find...";
    this.searchInput.addEventListener("keydown", (e) => this.onKeyDown(e));
    this.searchInput.addEventListener("input", () => this.onInput());
    searchInputContainer.appendChild(this.searchInput);
    this.searchCount = document.createElement("div");
    this.searchCount.className = "document-search-count";
    this.searchCount.style.display = "none";
    this.searchCount.textContent = "0 / 0";
    searchInputContainer.appendChild(this.searchCount);
    const documentSearchButtons = document.createElement("div");
    documentSearchButtons.className = "document-search-buttons";
    documentSearch.appendChild(documentSearchButtons);
    const previousButton = document.createElement("button");
    previousButton.className = "clickable-icon document-search-button";
    previousButton.setAttribute("aria-label", "Previous\nShift + F3");
    previousButton.setAttribute("data-tooltip-position", "top");
    (0, import_obsidian10.setIcon)(previousButton, "arrow-up");
    previousButton.addEventListener("click", () => this.changeMatch(this.matchIndex - 1));
    documentSearchButtons.appendChild(previousButton);
    const nextButton = document.createElement("button");
    nextButton.className = "clickable-icon document-search-button";
    nextButton.setAttribute("aria-label", "Next\nF3");
    nextButton.setAttribute("data-tooltip-position", "top");
    (0, import_obsidian10.setIcon)(nextButton, "arrow-down");
    nextButton.addEventListener("click", () => this.changeMatch(this.matchIndex + 1));
    documentSearchButtons.appendChild(nextButton);
    const closeButton = document.createElement("button");
    closeButton.className = "clickable-icon document-search-close-button";
    closeButton.setAttribute("aria-label", "Exit search");
    closeButton.setAttribute("data-tooltip-position", "top");
    (0, import_obsidian10.setIcon)(closeButton, "x");
    closeButton.addEventListener("click", () => this.close());
    documentSearch.appendChild(closeButton);
    this.view.canvas.wrapperEl.appendChild(this.containerEl);
    this.view.canvas.searchEl = this.containerEl;
    this.searchInput.focus();
  }
  onKeyDown(e) {
    if (e.key === "Enter" || e.key === "F3")
      this.changeMatch(this.matchIndex + (e.shiftKey ? -1 : 1));
    else if (e.key === "Escape")
      this.close();
  }
  onInput() {
    const hasQuery = this.searchInput.value.length > 0;
    this.searchCount.style.display = hasQuery ? "block" : "none";
    if (!hasQuery) this.searchMatches = [];
    else {
      this.searchMatches = Array.from(this.view.canvas.nodes.values()).map((node) => {
        const nodeData = node.getData();
        let content = void 0;
        if (nodeData.type === "text") content = nodeData.text;
        else if (nodeData.type === "group") content = nodeData.label;
        else if (nodeData.type === "file") content = node.child.data;
        if (!content) return null;
        const matches = [];
        const regex = new RegExp(this.searchInput.value, "gi");
        let match;
        while ((match = regex.exec(content)) !== null) {
          matches.push([match.index, match.index + match[0].length]);
        }
        return { nodeId: node.id, content, matches };
      }).filter((match) => match && match.matches.length > 0);
    }
    this.changeMatch(0);
  }
  changeMatch(index) {
    if (this.searchMatches.length === 0) this.matchIndex = -1;
    else {
      if (index < 0) index += this.searchMatches.length;
      this.matchIndex = index % this.searchMatches.length;
    }
    const match = this.searchMatches[this.matchIndex];
    if (match) this.goToMatch(match);
    this.searchCount.textContent = `${this.matchIndex + 1} / ${this.searchMatches.length}`;
  }
  goToMatch(match) {
    this.view.setEphemeralState({ match });
  }
  close() {
    this.containerEl.remove();
    this.view.canvas.searchEl = void 0;
  }
};

// src/canvas-extensions/metadata-canvas-extension.ts
var import_obsidian11 = require("obsidian");
var MetadataCanvasExtension = class extends CanvasExtension {
  constructor() {
    super(...arguments);
    this.canvasCssclassesCache = /* @__PURE__ */ new Map();
  }
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.onCanvasChanged(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-metadata-changed",
      (canvas) => this.onMetadataChanged(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-view-unloaded:before",
      (view) => this.onCanvasViewUnloaded(view)
    ));
  }
  onCanvasChanged(canvas) {
    var _a;
    const metadata = (_a = canvas.data) == null ? void 0 : _a.metadata;
    if (!metadata || metadata.version !== CURRENT_SPEC_VERSION)
      return new import_obsidian11.Notice("Metadata node not found or version mismatch. Should have been migrated (but wasn't).");
    const that = this;
    const validator = {
      get(target, key) {
        if (typeof target[key] === "object" && target[key] !== null)
          return new Proxy(target[key], validator);
        else return target[key];
      },
      set(target, key, value) {
        target[key] = value;
        that.plugin.app.workspace.trigger("advanced-canvas:canvas-metadata-changed", canvas);
        canvas.requestSave();
        return true;
      }
    };
    canvas.metadata = new Proxy(metadata, validator);
    this.plugin.app.workspace.trigger("advanced-canvas:canvas-metadata-changed", canvas);
  }
  onMetadataChanged(canvas) {
    var _a, _b, _c;
    const oldCssClasses = this.canvasCssclassesCache.get(canvas.view);
    if (oldCssClasses) canvas.wrapperEl.classList.remove(...oldCssClasses);
    const currentClasses = (_c = (_b = (_a = canvas.metadata) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.cssclasses) != null ? _c : [];
    this.canvasCssclassesCache.set(canvas.view, currentClasses);
    if (currentClasses.length > 0) canvas.wrapperEl.classList.add(...currentClasses);
  }
  onCanvasViewUnloaded(view) {
    this.canvasCssclassesCache.delete(view);
  }
};

// src/utils/modal-helper.ts
var import_obsidian12 = require("obsidian");
var AbstractSelectionModal = class extends import_obsidian12.FuzzySuggestModal {
  constructor(app, placeholder, suggestions) {
    super(app);
    this.suggestions = suggestions;
    this.setPlaceholder(placeholder);
    this.setInstructions([{
      command: "\u2191\u2193",
      purpose: "to navigate"
    }, {
      command: "esc",
      purpose: "to dismiss"
    }]);
  }
  getItems() {
    return this.suggestions;
  }
  getItemText(item) {
    return item;
  }
  onChooseItem(item, evt) {
  }
  awaitInput() {
    return new Promise((resolve, _reject) => {
      this.onChooseItem = (item) => {
        resolve(item);
      };
      this.open();
    });
  }
};
var FileNameModal = class extends import_obsidian12.SuggestModal {
  constructor(app, parentPath, fileExtension) {
    super(app);
    this.parentPath = parentPath.replace(/^\//, "").replace(/\/$/, "");
    this.fileExtension = fileExtension;
  }
  getSuggestions(query) {
    const queryWithoutExtension = query.replace(new RegExp(`\\.${this.fileExtension}$`), "");
    if (queryWithoutExtension === "") return [];
    const queryWithExtension = queryWithoutExtension + "." + this.fileExtension;
    const suggestions = [queryWithExtension];
    if (this.parentPath.length > 0) suggestions.splice(0, 0, `${this.parentPath}/${queryWithExtension}`);
    return suggestions.filter((s) => this.app.vault.getAbstractFileByPath(s) === null);
  }
  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_obsidian12.SuggestModal {
  constructor(app, extensionsRegex, suggestNewFile = false) {
    super(app);
    this.files = this.app.vault.getFiles().map((file) => file.path).filter((path) => {
      var _a;
      return (_a = FilepathHelper.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_obsidian12.TFile)
          return resolve(file);
        if (!this.suggestNewFile) return;
        if (FilepathHelper.extension(path) === void 0) path += ".md";
        const newFile = this.app.vault.create(path, "");
        resolve(newFile);
      };
      this.open();
    });
  }
};

// src/canvas-extensions/node-ratio-canvas-extension.ts
var NodeRatioCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "canvas:node-menu",
      (menu, node) => this.onNodeMenu(menu, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-resized",
      (canvas, node) => this.onNodeResized(canvas, node)
    ));
  }
  onNodeMenu(menu, node) {
    if (!this.plugin.settings.getSetting("aspectRatioControlFeatureEnabled")) return;
    menu.addItem((item) => {
      item.setTitle("Set Aspect Ratio").setIcon("aspect-ratio").onClick(async () => {
        const NO_RATIO = "No ratio enforcement";
        const newRatioString = await new AbstractSelectionModal(this.plugin.app, "Enter aspect ratio (width:height)", ["16:9", "4:3", "3:2", "1:1", NO_RATIO]).awaitInput();
        const nodeData = node.getData();
        if (newRatioString === NO_RATIO) {
          node.setData({
            ...nodeData,
            ratio: void 0
          });
          return;
        }
        const [width, height] = newRatioString.split(":").map(Number);
        if (width && height) {
          node.setData({
            ...nodeData,
            ratio: width / height
          });
          node.setData({
            ...node.getData(),
            width: nodeData.height * (width / height)
          });
        }
      });
    });
  }
  onNodeResized(_canvas, node) {
    const nodeData = node.getData();
    if (!nodeData.ratio) 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.ratio)
      nodeSize.width = nodeSize.height * nodeData.ratio;
    else nodeSize.height = nodeSize.width / nodeData.ratio;
    node.setData({
      ...nodeData,
      width: nodeSize.width,
      height: nodeSize.height
    });
  }
};

// src/canvas-extensions/group-canvas-extension.ts
var GROUP_NODE_SIZE = { width: 300, height: 300 };
var GROUP_NODE_PADDING = 20;
var GroupCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.addCommand({
      id: "create-group-around-selection",
      name: "Group selected nodes",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size > 0,
        (canvas) => this.createGroupAroundSelection(canvas)
      )
    });
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (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
              });
            }
          )
        );
      }
    ));
  }
  createGroupAroundSelection(canvas) {
    const bbox = BBoxHelper.combineBBoxes(
      Array.from(canvas.selection.values()).map((e) => e.getBBox())
    );
    const paddedBBox = BBoxHelper.enlargeBBox(bbox, GROUP_NODE_PADDING);
    canvas.createGroupNode({
      pos: { x: paddedBBox.minX, y: paddedBBox.minY },
      size: {
        width: paddedBBox.maxX - paddedBBox.minX,
        height: paddedBBox.maxY - paddedBBox.minY
      }
    });
  }
};

// src/canvas-extensions/presentation-canvas-extension.ts
var import_obsidian13 = 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.visitedNodeIds = [];
    this.fullscreenModalObserver = null;
    this.presentationUsesFullscreen = false;
  }
  isEnabled() {
    return "presentationFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "canvas:selection-menu",
      (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(
      "advanced-canvas:canvas-changed",
      (canvas) => this.onCanvasChanged(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenuCreated(canvas)
    ));
  }
  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)
      })
    );
  }
  setStartNode(canvas, node) {
    if (!node) return;
    canvas.metadata["startNode"] = node.getData().id;
  }
  getDefaultSlideSize() {
    const slideSize = this.plugin.settings.getSetting("defaultSlideDimensions");
    return { width: slideSize[0], height: slideSize[1] };
  }
  getSlideAspectRatio() {
    const slideSize = this.getDefaultSlideSize();
    return slideSize.width / slideSize.height;
  }
  addSlide(canvas, pos, bbox) {
    const isStartNode = canvas.metadata["startNode"] === void 0;
    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(),
      ratio: slideAspectRatio
    });
    if (isStartNode) canvas.metadata["startNode"] = groupNode.getData().id;
  }
  async animateNodeTransition(canvas, fromNode, toNode) {
    const removePadding = this.plugin.settings.getSetting("zoomToSlideWithoutPadding");
    const animationDurationMs = this.plugin.settings.getSetting("slideTransitionAnimationDuration") * 1e3;
    const toNodeBBox = CanvasHelper.getSmallestAllowedZoomBBox(canvas, toNode.getBBox());
    const toNodeBBoxPadded = removePadding ? toNodeBBox : BBoxHelper.enlargeBBox(toNodeBBox, 50);
    console.log({ toNodeBBox, toNodeBBoxPadded });
    if (animationDurationMs > 0 && fromNode) {
      const animationIntensity = this.plugin.settings.getSetting("slideTransitionAnimationIntensity");
      const fromNodeBBox = CanvasHelper.getSmallestAllowedZoomBBox(canvas, fromNode.getBBox());
      const fromNodeBBoxPadded = removePadding ? fromNodeBBox : BBoxHelper.enlargeBBox(fromNodeBBox, 50);
      const currentNodeBBoxEnlarged = BBoxHelper.scaleBBox(fromNodeBBoxPadded, animationIntensity);
      canvas.zoomToRealBbox(currentNodeBBoxEnlarged);
      await sleep(animationDurationMs / 2);
      if (fromNode.getData().id !== toNode.getData().id) {
        const nextNodeBBoxEnlarged = BBoxHelper.scaleBBox(toNodeBBoxPadded, animationIntensity);
        canvas.zoomToRealBbox(nextNodeBBoxEnlarged);
        await sleep(animationDurationMs / 2);
      }
    }
    canvas.zoomToRealBbox(toNodeBBoxPadded);
  }
  async startPresentation(canvas, tryContinue = false) {
    if (!tryContinue || this.visitedNodeIds.length === 0) {
      const startNode2 = canvas.metadata["startNode"] && canvas.nodes.get(canvas.metadata["startNode"]);
      if (!startNode2) {
        new import_obsidian13.Notice("No start node found. Please mark a node as a start node trough the popup menu.");
        return;
      }
      this.visitedNodeIds = [startNode2.getData().id];
    }
    this.savedViewport = {
      x: canvas.tx,
      y: canvas.ty,
      zoom: canvas.tZoom
    };
    const shouldEnterFullscreen = this.plugin.settings.getSetting("fullscreenPresentationEnabled");
    this.presentationUsesFullscreen = shouldEnterFullscreen;
    canvas.wrapperEl.focus();
    canvas.wrapperEl.classList.add("presentation-mode");
    if (shouldEnterFullscreen) {
      try {
        await canvas.wrapperEl.requestFullscreen();
      } catch (e) {
        this.presentationUsesFullscreen = false;
      }
    }
    canvas.setReadonly(true);
    if (this.plugin.settings.getSetting("useUnclampedZoomWhilePresenting"))
      canvas.screenshotting = true;
    canvas.wrapperEl.onkeydown = (e) => {
      if (e.key === "Escape") {
        e.preventDefault();
        e.stopPropagation();
        this.endPresentation(canvas);
        return;
      }
      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);
      }
    };
    if (this.presentationUsesFullscreen) {
      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;
    if (this.presentationUsesFullscreen)
      await sleep(500);
    const startNodeId = this.visitedNodeIds.first();
    if (!startNodeId) return;
    const startNode = canvas.nodes.get(startNodeId);
    if (!startNode) return;
    this.animateNodeTransition(canvas, void 0, startNode);
  }
  endPresentation(canvas) {
    var _a;
    if (!this.isPresentationMode) return;
    if (this.presentationUsesFullscreen) {
      (_a = this.fullscreenModalObserver) == null ? void 0 : _a.disconnect();
      this.fullscreenModalObserver = null;
      canvas.wrapperEl.onfullscreenchange = null;
      if (document.fullscreenElement) document.exitFullscreen();
    }
    canvas.wrapperEl.onkeydown = null;
    canvas.setReadonly(false);
    if (this.plugin.settings.getSetting("useUnclampedZoomWhilePresenting"))
      canvas.screenshotting = false;
    canvas.wrapperEl.classList.remove("presentation-mode");
    if (this.plugin.settings.getSetting("resetViewportOnPresentationEnd"))
      canvas.setViewport(this.savedViewport.x, this.savedViewport.y, this.savedViewport.zoom);
    this.isPresentationMode = false;
    this.presentationUsesFullscreen = false;
  }
  nextNode(canvas) {
    var _a;
    const fromNodeId = this.visitedNodeIds.last();
    if (!fromNodeId) return;
    const fromNode = canvas.nodes.get(fromNodeId);
    if (!fromNode) return;
    const outgoingEdges = canvas.getEdgesForNode(fromNode).filter((edge) => edge.from.node.getData().id === fromNodeId);
    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.visitedNodeIds.filter((visitedNodeId) => visitedNodeId === fromNodeId).length - 1;
      const nextEdge = sortedEdges[traversedEdgesCount];
      toNode = nextEdge.to.node;
    }
    if (toNode) {
      this.visitedNodeIds.push(toNode.getData().id);
      this.animateNodeTransition(canvas, fromNode, toNode);
    } else {
      this.animateNodeTransition(canvas, fromNode, fromNode);
    }
  }
  previousNode(canvas) {
    const fromNodeId = this.visitedNodeIds.pop();
    if (!fromNodeId) return;
    const fromNode = canvas.nodes.get(fromNodeId);
    if (!fromNode) return;
    const toNodeId = this.visitedNodeIds.last();
    let toNode = toNodeId ? canvas.nodes.get(toNodeId) : null;
    if (!toNode) {
      toNode = fromNode;
      this.visitedNodeIds.push(fromNodeId);
    }
    this.animateNodeTransition(canvas, fromNode, toNode);
  }
};

// src/canvas-extensions/z-ordering-canvas-extension.ts
var ZOrderingCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "zOrderingControlFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "canvas:node-menu",
      (menu, node) => this.nodeContextMenu(node, menu)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "canvas:selection-menu",
      (menu, canvas) => this.selectionContextMenu(canvas, menu)
    ));
  }
  nodeContextMenu(node, menu) {
    this.addZOrderingContextMenuItems(node.canvas, [node], menu);
  }
  selectionContextMenu(canvas, menu) {
    const selectedNodes = canvas.getSelectionData().nodes.map((nodeData) => canvas.nodes.get(nodeData.id)).filter((node) => node !== void 0);
    this.addZOrderingContextMenuItems(canvas, selectedNodes, menu);
  }
  addZOrderingContextMenuItems(canvas, nodes, menu) {
    menu.addSeparator();
    if (this.plugin.settings.getSetting("zOrderingControlShowOneLayerShiftOptions") && nodes.length === 1) {
      menu.addItem((item) => {
        item.setTitle("Move one layer forward");
        item.setIcon("arrow-up");
        item.onClick(() => this.moveOneLayer(canvas, nodes.first(), true));
      });
      menu.addItem((item) => {
        item.setTitle("Move one layer backward");
        item.setIcon("arrow-down");
        item.onClick(() => this.moveOneLayer(canvas, nodes.first(), false));
      });
    }
    menu.addItem((item) => {
      item.setTitle("Bring to Front");
      item.setIcon("bring-to-front");
      item.onClick(() => this.moveMaxLayers(canvas, nodes, true));
    });
    menu.addItem((item) => {
      item.setTitle("Send to Back");
      item.setIcon("send-to-back");
      item.onClick(() => this.moveMaxLayers(canvas, nodes, false));
    });
    if (nodes.some((node) => node.getData().zIndex !== void 0)) {
      menu.addItem((item) => {
        item.setTitle("Remove persistent z-index");
        item.setIcon("pin-off");
        item.onClick(() => this.removePersistentZIndexes(canvas, nodes));
      });
    }
    menu.addSeparator();
  }
  moveOneLayer(canvas, selectedNode, forward) {
    const selectedNodeBBox = selectedNode.getBBox();
    const collidingNodes = [...canvas.nodes.values()].filter((node) => BBoxHelper.isColliding(selectedNodeBBox, node.getBBox())).filter((node) => node !== selectedNode);
    const nearestZIndexNode = collidingNodes.sort((a, b) => forward ? a.zIndex - b.zIndex : b.zIndex - a.zIndex).filter((node) => forward ? node.zIndex > selectedNode.zIndex : node.zIndex < selectedNode.zIndex).first();
    if (nearestZIndexNode === void 0) return;
    const targetZIndex = nearestZIndexNode.zIndex;
    this.setNodesZIndex([nearestZIndexNode], selectedNode.zIndex);
    this.setNodesZIndex([selectedNode], targetZIndex);
  }
  moveMaxLayers(canvas, selectedNodes, forward) {
    const targetZIndex = forward ? Math.max(...this.getAllZIndexes(canvas)) + 1 : Math.min(...this.getAllZIndexes(canvas)) - selectedNodes.length;
    this.setNodesZIndex(selectedNodes, targetZIndex);
  }
  removePersistentZIndexes(_canvas, nodes) {
    for (const node of nodes) node.setZIndex(void 0);
  }
  setNodesZIndex(nodes, zIndex) {
    const sortedNodes = nodes.sort((a, b) => a.zIndex - b.zIndex);
    for (let i = 0; i < sortedNodes.length; i++) {
      const node = sortedNodes[i];
      const finalZIndex = zIndex + i;
      node.setZIndex(finalZIndex);
    }
  }
  getAllZIndexes(canvas) {
    return [...canvas.nodes.values()].map((n) => n.zIndex);
  }
};

// src/canvas-extensions/better-readonly-canvas-extension.ts
var BetterReadonlyCanvasExtension = class extends CanvasExtension {
  constructor() {
    super(...arguments);
    this.isMovingToBBox = false;
  }
  isEnabled() {
    return "betterReadonlyEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.updatePopupMenu(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:viewport-changed:before",
      (canvas) => this.onBeforeViewPortChanged(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:zoom-to-bbox:before",
      () => this.isMovingToBBox = true
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:readonly-changed",
      (canvas, _readonly) => {
        this.updatePopupMenu(canvas);
        this.updateLockedZoom(canvas);
        this.updateLockedPan(canvas);
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.addQuickSettings(canvas)
    ));
  }
  onBeforeViewPortChanged(canvas) {
    var _a, _b, _c, _d, _e, _f;
    if (this.isMovingToBBox) {
      this.isMovingToBBox = 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;
    }
  }
  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/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(
      "canvas:selection-menu",
      (menu, canvas) => {
        menu.addItem(
          (item) => item.setTitle("Encapsulate").setIcon("file-plus").onClick(() => this.encapsulateSelection(canvas))
        );
      }
    ));
  }
  async encapsulateSelection(canvas) {
    var _a, _b, _c, _d;
    const selection = canvas.getSelectionData();
    const canvasSettings = this.plugin.app.internalPlugins.plugins.canvas.instance.options;
    const defaultNewCanvasLocation = canvasSettings.newFileLocation;
    let targetFolderPath = this.plugin.app.vault.getRoot().path;
    if (defaultNewCanvasLocation === "current") targetFolderPath = (_c = (_b = (_a = canvas.view.file) == null ? void 0 : _a.parent) == null ? void 0 : _b.path) != null ? _c : targetFolderPath;
    else if (defaultNewCanvasLocation === "folder") targetFolderPath = (_d = canvasSettings.newFileFolderPath) != null ? _d : targetFolderPath;
    const targetFilePath = await new FileNameModal(
      this.plugin.app,
      targetFolderPath,
      "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 import_obsidian14 = require("obsidian");
var DIRECTIONS = ["up", "down", "left", "right"];
var CommandsCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "commandsFeatureEnabled";
  }
  init() {
    this.plugin.addCommand({
      id: "toggle-readonly",
      name: "Toggle readonly",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (_canvas) => true,
        (canvas) => canvas.setReadonly(!canvas.readonly)
      )
    });
    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()
      )
    });
    this.plugin.addCommand({
      id: "zoom-to-fit",
      name: "Zoom to fit",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (_canvas) => true,
        (canvas) => canvas.zoomToFit()
      )
    });
    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)
        )
      });
      this.plugin.addCommand({
        id: `navigate-${direction}`,
        name: `Navigate ${direction}`,
        checkCallback: CanvasHelper.canvasCommand(
          this.plugin,
          (canvas) => canvas.getSelectionData().nodes.length === 1,
          (canvas) => this.navigate(canvas, direction)
        )
      });
    }
    this.plugin.addCommand({
      id: "flip-selection-horizontally",
      name: "Flip selection horizontally",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly && canvas.selection.size > 0,
        (canvas) => this.flipSelection(
          canvas,
          true
        )
      )
    });
    this.plugin.addCommand({
      id: "flip-selection-vertically",
      name: "Flip selection vertically",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly && canvas.selection.size > 0,
        (canvas) => this.flipSelection(canvas, false)
      )
    });
    this.plugin.addCommand({
      id: "select-connected-edges",
      name: "Select connected edges",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size > 0,
        (canvas) => CanvasHelper.selectEdgesForNodes(canvas, "connected")
      )
    });
    this.plugin.addCommand({
      id: "select-incoming-edges",
      name: "Select incoming edges",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size > 0,
        (canvas) => CanvasHelper.selectEdgesForNodes(canvas, "incoming")
      )
    });
    this.plugin.addCommand({
      id: "select-outgoing-edges",
      name: "Select outgoing edges",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size > 0,
        (canvas) => CanvasHelper.selectEdgesForNodes(canvas, "outgoing")
      )
    });
    this.plugin.addCommand({
      id: "swap-nodes",
      name: "Swap nodes",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly && canvas.getSelectionData().nodes.length === 2,
        (canvas) => {
          const selectedNodes = canvas.getSelectionData().nodes.map((nodeData) => canvas.nodes.get(nodeData.id)).filter((node) => node !== void 0);
          if (selectedNodes.length !== 2) return;
          const [nodeA, nodeB] = selectedNodes;
          const nodeAData = nodeA.getData();
          const nodeBData = nodeB.getData();
          nodeA.setData({ ...nodeAData, x: nodeBData.x, y: nodeBData.y, width: nodeBData.width, height: nodeBData.height });
          nodeB.setData({ ...nodeBData, x: nodeAData.x, y: nodeAData.y, width: nodeAData.width, height: nodeAData.height });
          canvas.pushHistory(canvas.getData());
        }
      )
    });
    this.plugin.addCommand({
      id: "copy-wikilink-to-node",
      name: "Copy wikilink to node",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly && canvas.selection.size === 1,
        (canvas) => {
          const file = canvas.view.file;
          if (!file) return;
          const nodeData = canvas.getSelectionData().nodes[0];
          if (!nodeData) return;
          const wikilink = `[[${file.path}#${nodeData.id}|${file.name} (${TextHelper.toTitleCase(nodeData.type)} node)]]`;
          navigator.clipboard.writeText(wikilink);
          new import_obsidian14.Notice("Copied wikilink to node to clipboard.", 2e3);
        }
      )
    });
    this.plugin.addCommand({
      id: "pull-outgoing-links-to-canvas",
      name: "Pull outgoing links to canvas",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly,
        (canvas) => {
          var _a, _b, _c;
          const canvasFile = canvas.view.file;
          if (!canvasFile) return;
          let selectedNodeIds = canvas.getSelectionData().nodes.map((node) => node.id);
          if (selectedNodeIds.length === 0) selectedNodeIds = [...canvas.nodes.keys()];
          const metadata = this.plugin.app.metadataCache.getFileCache(canvasFile);
          if (!metadata) return;
          const outgoingLinks = /* @__PURE__ */ new Set();
          for (const nodeId of selectedNodeIds) {
            let relativeFile = canvasFile;
            let nodeOutgoingLinks = (_b = (_a = metadata.nodes) == null ? void 0 : _a[nodeId]) == null ? void 0 : _b.links;
            if (!nodeOutgoingLinks) {
              const file = (_c = canvas.nodes.get(nodeId)) == null ? void 0 : _c.file;
              if (!file) continue;
              const fileMetadata = this.plugin.app.metadataCache.getFileCache(file);
              nodeOutgoingLinks = fileMetadata == null ? void 0 : fileMetadata.links;
              relativeFile = file;
            }
            if (!nodeOutgoingLinks) continue;
            for (const nodeOutgoingLink of nodeOutgoingLinks) {
              const resolvedLink = this.plugin.app.metadataCache.getFirstLinkpathDest(nodeOutgoingLink.link, relativeFile.path);
              if (!(resolvedLink instanceof import_obsidian14.TFile)) continue;
              outgoingLinks.add(resolvedLink);
            }
          }
          const existingFileNodes = /* @__PURE__ */ new Set([canvas.view.file]);
          for (const node of canvas.nodes.values()) {
            if (node.getData().type !== "file" || !node.file) continue;
            existingFileNodes.add(node.file);
          }
          for (const outgoingLink of outgoingLinks) {
            if (existingFileNodes.has(outgoingLink)) continue;
            this.createFileNode(canvas, outgoingLink);
          }
        }
      )
    });
    this.plugin.addCommand({
      id: "pull-backlinks-to-canvas",
      name: "Pull backlinks to canvas",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => !canvas.readonly,
        (canvas) => {
          const canvasFile = canvas.view.file;
          if (!canvasFile) return;
          const selectedNodesData = canvas.getSelectionData().nodes.map((node) => node);
          const backlinks = /* @__PURE__ */ new Set();
          if (selectedNodesData.length > 0) {
            for (const nodeData of selectedNodesData) {
              if (nodeData.type !== "file" || !nodeData.file) continue;
              const file = this.plugin.app.vault.getFileByPath(nodeData.file);
              if (!file) continue;
              const nodeBacklinks = this.plugin.app.metadataCache.getBacklinksForFile(file);
              if (!nodeBacklinks) continue;
              for (const nodeBacklink of nodeBacklinks.data.keys()) {
                const resolvedLink = this.plugin.app.metadataCache.getFirstLinkpathDest(nodeBacklink, file.path);
                if (!(resolvedLink instanceof import_obsidian14.TFile)) continue;
                backlinks.add(resolvedLink);
              }
            }
          } else {
            const canvasBacklinks = this.plugin.app.metadataCache.getBacklinksForFile(canvasFile);
            if (!canvasBacklinks) return;
            for (const canvasBacklink of canvasBacklinks.data.keys()) {
              const resolvedLink = this.plugin.app.metadataCache.getFirstLinkpathDest(canvasBacklink, canvasFile.path);
              if (!(resolvedLink instanceof import_obsidian14.TFile)) continue;
              backlinks.add(resolvedLink);
            }
          }
          const existingFileNodes = /* @__PURE__ */ new Set([canvas.view.file]);
          for (const node of canvas.nodes.values()) {
            if (node.getData().type !== "file" || !node.file) continue;
            existingFileNodes.add(node.file);
          }
          for (const backlink of backlinks) {
            if (existingFileNodes.has(backlink)) continue;
            this.createFileNode(canvas, backlink);
          }
        }
      )
    });
  }
  createTextNode(canvas) {
    const size = canvas.config.defaultTextNodeDimensions;
    const pos = CanvasHelper.getCenterCoordinates(canvas, size);
    canvas.createTextNode({ pos, size });
  }
  async createFileNode(canvas, file) {
    const size = canvas.config.defaultFileNodeDimensions;
    const pos = CanvasHelper.getCenterCoordinates(canvas, size);
    file != null ? file : 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
    });
  }
  flipSelection(canvas, horizontally) {
    const selectionData = canvas.getSelectionData();
    if (selectionData.nodes.length === 0) return;
    const nodeIds = /* @__PURE__ */ new Set();
    for (const nodeData of selectionData.nodes) {
      nodeIds.add(nodeData.id);
      const node = canvas.nodes.get(nodeData.id);
      if (!node) continue;
      const newX = horizontally ? 2 * selectionData.center.x - nodeData.x - nodeData.width : nodeData.x;
      const newY = horizontally ? nodeData.y : 2 * selectionData.center.y - nodeData.y - nodeData.height;
      node.setData({
        ...nodeData,
        x: newX,
        y: newY
      });
    }
    for (const edge of canvas.edges.values()) {
      const edgeData = edge.getData();
      let newFromSide = edgeData.fromSide;
      if (nodeIds.has(edgeData.fromNode) && BBoxHelper.isHorizontal(edgeData.fromSide) === horizontally)
        newFromSide = BBoxHelper.getOppositeSide(edgeData.fromSide);
      let newToSide = edgeData.toSide;
      if (nodeIds.has(edgeData.toNode) && BBoxHelper.isHorizontal(edgeData.toSide) === horizontally)
        newToSide = BBoxHelper.getOppositeSide(edgeData.toSide);
      edge.setData({
        ...edgeData,
        fromSide: newFromSide,
        toSide: newToSide
      });
    }
    canvas.pushHistory(canvas.getData());
  }
  navigate(canvas, direction) {
    const node = this.getNextNode(canvas, direction);
    if (!node) return;
    canvas.updateSelection(() => {
      canvas.selection = /* @__PURE__ */ new Set([node]);
    });
  }
  getNextNode(canvas, direction) {
    var _a;
    const selectedNodeData = (_a = canvas.getSelectionData().nodes) == null ? void 0 : _a.first();
    if (!selectedNodeData) return;
    const selectedNodeBBox = {
      minX: selectedNodeData.x,
      minY: selectedNodeData.y,
      maxX: selectedNodeData.x + selectedNodeData.width,
      maxY: selectedNodeData.y + selectedNodeData.height
    };
    const possibleTargetNodes = Array.from(canvas.nodes.values()).filter((node) => {
      const nodeData = node.getData();
      return nodeData.id !== selectedNodeData.id && (nodeData.type === "text" || nodeData.type === "file");
    });
    const closestNode = possibleTargetNodes.reduce((closestNode2, node) => {
      const nodeBBox = node.getBBox();
      const isInVerticalRange = selectedNodeBBox.minY <= nodeBBox.maxY && selectedNodeBBox.maxY >= nodeBBox.minY;
      const isInHorizontalRange = selectedNodeBBox.minX <= nodeBBox.maxX && selectedNodeBBox.maxX >= nodeBBox.minX;
      if (["up", "down"].includes(direction) && !isInHorizontalRange) return closestNode2;
      if (["left", "right"].includes(direction) && !isInVerticalRange) return closestNode2;
      let distance = -1;
      switch (direction) {
        case "up":
          distance = selectedNodeBBox.minY - nodeBBox.maxY;
          break;
        case "down":
          distance = nodeBBox.minY - selectedNodeBBox.maxY;
          break;
        case "left":
          distance = selectedNodeBBox.minX - nodeBBox.maxX;
          break;
        case "right":
          distance = nodeBBox.minX - selectedNodeBBox.maxX;
          break;
      }
      if (distance < 0) return closestNode2;
      if (!closestNode2) return { node, distance };
      if (distance < closestNode2.distance) return { node, distance };
      if (distance === closestNode2.distance) {
        const selectedNodeCenter = {
          x: selectedNodeData.x + selectedNodeData.width / 2,
          y: selectedNodeData.y + selectedNodeData.height / 2
        };
        const closestNodeCenter = {
          x: closestNode2.node.x + closestNode2.node.width / 2,
          y: closestNode2.node.y + closestNode2.node.height / 2
        };
        const nodeCenter = {
          x: node.x + node.width / 2,
          y: node.y + node.height / 2
        };
        const closestNodeDistance = Math.sqrt(
          Math.pow(selectedNodeCenter.x - closestNodeCenter.x, 2) + Math.pow(selectedNodeCenter.y - closestNodeCenter.y, 2)
        );
        const nodeDistance = Math.sqrt(
          Math.pow(selectedNodeCenter.x - nodeCenter.x, 2) + Math.pow(selectedNodeCenter.y - nodeCenter.y, 2)
        );
        if (nodeDistance < closestNodeDistance) return { node, distance };
      }
      return closestNode2;
    }, null);
    return closestNode == null ? void 0 : closestNode.node;
  }
};

// src/canvas-extensions/auto-resize-node-canvas-extension.ts
var AutoResizeNodeCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "autoResizeNodeFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-created",
      (canvas, node) => this.onNodeCreated(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenuCreated(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-editing-state-changed",
      (canvas, node, editing) => this.onNodeEditingStateChanged(canvas, node, editing)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-text-content-changed",
      (canvas, node, viewUpdate) => this.onNodeTextContentChanged(canvas, node, viewUpdate.view.dom)
    ));
  }
  isValidNodeType(nodeData) {
    return nodeData.type === "text" || nodeData.type === "file" && nodeData.file.endsWith(".md");
  }
  onNodeCreated(_canvas, node) {
    const autoResizeNodeEnabledByDefault = this.plugin.settings.getSetting("autoResizeNodeEnabledByDefault");
    if (!autoResizeNodeEnabledByDefault) return;
    const nodeData = node.getData();
    if (nodeData.type !== "text" && nodeData.type !== "file") return;
    node.setData({
      ...node.getData(),
      dynamicHeight: true
    });
  }
  onPopupMenuCreated(canvas) {
    if (canvas.readonly) return;
    const selectedNodes = canvas.getSelectionData().nodes.filter((nodeData) => this.isValidNodeType(nodeData)).map((nodeData) => canvas.nodes.get(nodeData.id)).filter((node) => node !== void 0);
    if (selectedNodes.length === 0) return;
    const autoResizeHeightEnabled = selectedNodes.some((node) => node.getData().dynamicHeight);
    CanvasHelper.addPopupMenuOption(
      canvas,
      CanvasHelper.createPopupMenuOption({
        id: "auto-resize-height",
        label: autoResizeHeightEnabled ? "Disable auto-resize" : "Enable auto-resize",
        icon: autoResizeHeightEnabled ? "scan-text" : "lock",
        callback: () => this.toggleAutoResizeHeightEnabled(canvas, selectedNodes, autoResizeHeightEnabled)
      })
    );
  }
  toggleAutoResizeHeightEnabled(canvas, nodes, autoResizeHeight) {
    nodes.forEach((node) => node.setData({
      ...node.getData(),
      dynamicHeight: !autoResizeHeight
    }));
    this.onPopupMenuCreated(canvas);
  }
  canBeResized(node) {
    const nodeData = node.getData();
    return nodeData.dynamicHeight;
  }
  async onNodeEditingStateChanged(_canvas, node, editing) {
    if (!this.isValidNodeType(node.getData())) return;
    if (!this.canBeResized(node)) return;
    await sleep(10);
    if (editing) {
      this.onNodeTextContentChanged(_canvas, node, node.child.editMode.cm.dom);
      return;
    }
    const renderedMarkdownContainer = node.nodeEl.querySelector(".markdown-preview-view.markdown-rendered");
    if (!renderedMarkdownContainer) return;
    renderedMarkdownContainer.style.height = "min-content";
    const newHeight = renderedMarkdownContainer.clientHeight;
    renderedMarkdownContainer.style.removeProperty("height");
    this.setNodeHeight(node, newHeight);
  }
  async onNodeTextContentChanged(_canvas, node, dom) {
    if (!this.isValidNodeType(node.getData())) return;
    if (!this.canBeResized(node)) return;
    const cmScroller = dom.querySelector(".cm-scroller");
    if (!cmScroller) return;
    cmScroller.style.height = "min-content";
    const newHeight = cmScroller.scrollHeight;
    cmScroller.style.removeProperty("height");
    this.setNodeHeight(node, newHeight);
  }
  setNodeHeight(node, height) {
    if (height === 0) return;
    const maxHeight = this.plugin.settings.getSetting("autoResizeNodeMaxHeight");
    if (maxHeight != -1 && height > maxHeight) height = maxHeight;
    const nodeData = node.getData();
    height = Math.max(height, node.canvas.config.minContainerDimension);
    if (this.plugin.settings.getSetting("autoResizeNodeSnapToGrid"))
      height = Math.ceil(height / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE;
    node.setData({
      ...nodeData,
      height
    });
  }
};

// src/canvas-extensions/portals-canvas-extension.ts
var import_obsidian15 = require("obsidian");
var PORTAL_ID_DELIMITER = "||";
var PORTAL_ID_PREFIX = `acportal${PORTAL_ID_DELIMITER}`;
var PORTAL_PADDING = 50;
var MIN_OPEN_PORTAL_SIZE = { width: 200, height: 200 };
var PortalsCanvasExtension = class _PortalsCanvasExtension extends CanvasExtension {
  isEnabled() {
    return "portalsFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.vault.on("modify", (file) => {
      for (const canvas of this.plugin.getCanvases())
        this.onFileModified(canvas, file);
    }));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenu(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-removed",
      (canvas, node) => this.onNodeRemoved(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-moved",
      (canvas, node, _keyboard) => this.onNodeMoved(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-resized",
      (canvas, node) => this.onNodeResized(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:dragging-state-changed",
      (canvas, startedDragging) => this.onDraggingStateChanged(canvas, startedDragging)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:containing-nodes-requested",
      (canvas, bbox, nodes) => this.onContainingNodesRequested(canvas, bbox, nodes)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-connection-try-dragging:before",
      (canvas, edge, event, cancelRef) => this.onEdgeConnectionTryDraggingBefore(canvas, edge, event, cancelRef)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-connection-dragging:after",
      (canvas, edge, event, newEdge, side, previousEnds) => this.onEdgeConnectionDraggingAfter(canvas, edge, event, newEdge, side, previousEnds)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:selection-changed",
      (canvas, oldSelection, updateSelection) => this.onSelectionChanged(canvas, oldSelection, updateSelection)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:data-requested",
      (canvas, data) => this.onGetData(canvas, data)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:data-loaded:before",
      (canvas, data, setData) => {
        this.onSetData(canvas, data).then((newData) => {
          if (newData.nodes.length === data.nodes.length && newData.edges.length === data.edges.length) return;
          setData(newData);
        });
      }
    ));
  }
  onFileModified(canvas, file) {
    const isAffected = Object.values(canvas.nodes).filter(
      (nodeData) => nodeData.getData().type === "file" && nodeData.currentPortalFile === file.path
    ).length > 0;
    if (!isAffected) return;
    canvas.setData(canvas.getData());
    canvas.history.current--;
    canvas.history.data.pop();
  }
  onContainingNodesRequested(_canvas, _bbox, nodes) {
    const filteredNodes = nodes.filter((node) => !_PortalsCanvasExtension.isPortalElement(node.id));
    nodes.splice(0, nodes.length, ...filteredNodes);
  }
  onSelectionChanged(canvas, _oldSelection, updateSelection) {
    updateSelection(() => {
      const updatedSelection = Array.from(canvas.selection).filter((canvasElement) => !_PortalsCanvasExtension.isPortalElement(canvasElement.id));
      canvas.selection = new Set(updatedSelection);
    });
  }
  onDraggingStateChanged(canvas, startedDragging) {
    if (!startedDragging) return;
    if (!canvas.getSelectionData().nodes.some((node) => node.type === "file" && node.portal)) return;
    const objectSnappingEnabled = canvas.options.snapToObjects;
    if (!objectSnappingEnabled) return;
    canvas.toggleObjectSnapping(false);
    const dragEndEventRef = this.plugin.app.workspace.on(
      "advanced-canvas:dragging-state-changed",
      (canvas2, startedDragging2) => {
        if (startedDragging2) return;
        canvas2.toggleObjectSnapping(objectSnappingEnabled);
        this.plugin.app.workspace.offref(dragEndEventRef);
      }
    );
    this.plugin.registerEvent(dragEndEventRef);
  }
  onNodeMoved(canvas, portalNode) {
    const portalNodeData = portalNode.getData();
    if (portalNodeData.type !== "file" || !portalNodeData.isPortalLoaded) return;
    const nestedNodes = this.getContainingNodes(canvas, portalNode);
    const containingNodesBBox = CanvasHelper.getBBox(nestedNodes);
    const portalOffset = {
      x: portalNodeData.x - containingNodesBBox.minX + PORTAL_PADDING,
      y: portalNodeData.y - containingNodesBBox.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
      });
    }
  }
  onNodeResized(_canvas, portalNode) {
    const portalNodeData = portalNode.getData();
    if (portalNodeData.type !== "file" || !portalNodeData.isPortalLoaded) return;
    portalNode.setData({
      ...portalNodeData,
      x: portalNode.prevX ? portalNode.prevX : portalNodeData.x,
      y: portalNode.prevY ? portalNode.prevY : portalNodeData.y,
      width: portalNode.prevWidth ? portalNode.prevWidth : portalNodeData.width,
      height: portalNode.prevHeight ? portalNode.prevHeight : portalNodeData.height
    });
  }
  onNodeRemoved(canvas, portalNode) {
    const portalNodeData = portalNode.getData();
    if (portalNodeData.type !== "file" || !portalNodeData.portal) return;
    for (const node of this.getContainingNodes(canvas, portalNode, false))
      canvas.removeNode(node);
    for (const edge of this.getContainingEdges(canvas, portalNode, false))
      canvas.removeEdge(edge);
  }
  onEdgeConnectionTryDraggingBefore(_canvas, edge, _event, cancelRef) {
    if (!_PortalsCanvasExtension.isPortalElement(edge.id)) return;
    cancelRef.value = true;
    new import_obsidian15.Notice("Updating edges from portals is not supported yet.");
  }
  onEdgeConnectionDraggingAfter(canvas, edge, _event, _newEdge, _side, _previousEnds) {
    if (_PortalsCanvasExtension.isPortalElement(edge.id)) return;
    if (!_PortalsCanvasExtension.isPortalElement(edge.from.node.id) || !_PortalsCanvasExtension.isPortalElement(edge.to.node.id)) return;
    canvas.removeEdge(edge);
    new import_obsidian15.Notice("Creating edges with both ends in portals are not supported yet.");
  }
  onPopupMenu(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.portal) this.setPortalOpen(canvas, node, false);
      return null;
    }).filter((node) => node !== null);
    if (selectedFileNodes.length !== 1) return;
    const portalNode = selectedFileNodes.first();
    const portalNodeData = portalNode.getData();
    if (portalNodeData.portal && portalNodeData.file !== portalNode.currentPortalFile)
      this.setPortalOpen(canvas, portalNode, true);
    CanvasHelper.addPopupMenuOption(
      canvas,
      CanvasHelper.createPopupMenuOption({
        id: "toggle-portal",
        label: portalNodeData.portal ? "Close portal" : "Open portal",
        icon: portalNodeData.portal ? "door-open" : "door-closed",
        callback: () => {
          this.setPortalOpen(canvas, portalNode, !portalNodeData.portal);
          this.onPopupMenu(canvas);
        }
      })
    );
  }
  setPortalOpen(canvas, portalNode, open) {
    const portalNodeData = portalNode.getData();
    portalNode.setData({
      ...portalNodeData,
      portal: open
    });
    portalNode.currentPortalFile = open ? portalNodeData.file : void 0;
    canvas.setData(canvas.getData());
  }
  // Remove all edges and nodes from portals
  onGetData(_canvas, data) {
    data.nodes = data.nodes.filter((nodeData) => !_PortalsCanvasExtension.isPortalElement(nodeData.id));
    for (const nodeData of data.nodes) delete nodeData.isPortalLoaded;
    const portalsIdMap = new Map(
      data.nodes.filter((nodeData) => nodeData.portal).map((nodeData) => [nodeData.id, nodeData])
    );
    data.edges = data.edges.filter((edgeData) => {
      var _a;
      if (_PortalsCanvasExtension.isPortalElement(edgeData.id)) return false;
      const isFromNodeFromPortal = _PortalsCanvasExtension.getNestedIds(edgeData.fromNode).length > 1;
      const isToNodeFromPortal = _PortalsCanvasExtension.getNestedIds(edgeData.toNode).length > 1;
      if (!isFromNodeFromPortal && !isToNodeFromPortal) return true;
      if (isFromNodeFromPortal && isToNodeFromPortal) return false;
      const targetPortalId = this.getParentPortalId(isFromNodeFromPortal ? edgeData.fromNode : edgeData.toNode);
      const targetPortalData = portalsIdMap.get(targetPortalId);
      if (!targetPortalData) return false;
      (_a = targetPortalData.interdimensionalEdges) != null ? _a : targetPortalData.interdimensionalEdges = [];
      targetPortalData.interdimensionalEdges.push(edgeData);
      return false;
    });
  }
  // Add all edges and nodes from portals
  async onSetData(canvas, dataRef) {
    if (!(dataRef == null ? void 0 : dataRef.nodes)) return 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 nodeData of data.nodes) {
      if (nodeData.type !== "file" || !nodeData.isPortalLoaded) continue;
      const interdimensionalEdges = nodeData.interdimensionalEdges;
      if (!interdimensionalEdges) continue;
      for (const edge of interdimensionalEdges) data.edges.push(edge);
      delete nodeData.interdimensionalEdges;
    }
    return data;
  }
  async tryOpenPortal(canvas, portalNodeData, nestedPortalFiles = /* @__PURE__ */ new Set()) {
    const addedData = { nodes: [], edges: [] };
    if (portalNodeData.type !== "file" || !portalNodeData.portal) return addedData;
    if (portalNodeData.file === canvas.view.file.path) return addedData;
    if (nestedPortalFiles.has(portalNodeData.file)) return addedData;
    nestedPortalFiles.add(portalNodeData.file);
    const portalFile = this.plugin.app.vault.getAbstractFileByPath(portalNodeData.file);
    if (!(portalFile instanceof import_obsidian15.TFile) || portalFile.extension !== "canvas") return addedData;
    const portalFileDataString = await this.plugin.app.vault.cachedRead(portalFile);
    if (portalFileDataString === "") return addedData;
    const portalFileData = JSON.parse(portalFileDataString);
    if (!portalFileData) return addedData;
    portalNodeData.isPortalLoaded = true;
    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) {
      let newNodeId = `${portalNodeData.id}${PORTAL_ID_DELIMITER}${nodeDataFromPortal.id}`;
      if (!newNodeId.startsWith(PORTAL_ID_PREFIX))
        newNodeId = PORTAL_ID_PREFIX + newNodeId;
      const addedNode = {
        ...nodeDataFromPortal,
        id: newNodeId,
        x: nodeDataFromPortal.x + portalOffset.x,
        y: nodeDataFromPortal.y + portalOffset.y
      };
      addedData.nodes.push(addedNode);
      const nestedNodes = await this.tryOpenPortal(canvas, addedNode, nestedPortalFiles);
      addedData.nodes.push(...nestedNodes.nodes);
      addedData.edges.push(...nestedNodes.edges);
    }
    for (const edgeDataFromPortal of portalFileData.edges) {
      let newEdgeId = `${portalNodeData.id}${PORTAL_ID_DELIMITER}${edgeDataFromPortal.id}`;
      if (!newEdgeId.startsWith(PORTAL_ID_PREFIX))
        newEdgeId = PORTAL_ID_PREFIX + newEdgeId;
      let fromNodeId = `${portalNodeData.id}${PORTAL_ID_DELIMITER}${edgeDataFromPortal.fromNode}`;
      if (!fromNodeId.startsWith(PORTAL_ID_PREFIX))
        fromNodeId = PORTAL_ID_PREFIX + fromNodeId;
      let toNodeId = `${portalNodeData.id}${PORTAL_ID_DELIMITER}${edgeDataFromPortal.toNode}`;
      if (!toNodeId.startsWith(PORTAL_ID_PREFIX))
        toNodeId = PORTAL_ID_PREFIX + toNodeId;
      addedData.edges.push({
        ...edgeDataFromPortal,
        id: newEdgeId,
        fromNode: fromNodeId,
        toNode: toNodeId
      });
    }
    const targetSize = this.getPortalSize(CanvasHelper.getBBox(addedData.nodes));
    portalNodeData.width = targetSize.width;
    portalNodeData.height = targetSize.height;
    return addedData;
  }
  // Helper functions
  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;
  }
  getContainingNodes(canvas, portalNode, directChildren = true) {
    return Array.from(canvas.nodes.values()).filter((node) => this.isChildOfPortal(portalNode.getData(), node.getData(), directChildren));
  }
  getContainingEdges(canvas, portalNode, directChildren = true) {
    return Array.from(canvas.edges.values()).filter((edge) => this.isChildOfPortal(portalNode.getData(), edge.getData(), directChildren));
  }
  getParentPortalId(elementId) {
    const nestedIds = _PortalsCanvasExtension.getNestedIds(elementId);
    if (nestedIds.length < 2) return void 0;
    return nestedIds.slice(0, -1).join(PORTAL_ID_DELIMITER);
  }
  static getNestedIds(id) {
    if (!this.isPortalElement(id)) return [id];
    const trimmedId = id.substring(PORTAL_ID_PREFIX.length);
    return trimmedId.split(PORTAL_ID_DELIMITER);
  }
  static isPortalElement(id) {
    return id.startsWith(PORTAL_ID_PREFIX);
  }
  isChildOfPortal(portal, canvasElement, directChild = true) {
    const nestedIds = _PortalsCanvasExtension.getNestedIds(canvasElement.id);
    if (nestedIds.length < 2) return false;
    return canvasElement.id !== portal.id && // Not the portal itself
    nestedIds.contains(portal.id) && // Is a child of the portal
    (!directChild || nestedIds[nestedIds.length - 2] === portal.id);
  }
};

// src/canvas-extensions/frontmatter-control-button-canvas-extension.ts
var import_obsidian16 = require("obsidian");
var FrontmatterControlButtonCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "canvasMetadataCompatibilityEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.addQuickSettings(canvas)
    ));
  }
  addQuickSettings(canvas) {
    var _a;
    if (!canvas) return;
    const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement;
    if (!settingsContainer) return;
    CanvasHelper.addControlMenuButton(
      settingsContainer,
      CanvasHelper.createControlMenuButton({
        id: "properties-button",
        icon: "info",
        label: "Properties",
        callback: () => {
          var _a2;
          const propertiesPlugin = this.plugin.app.internalPlugins.plugins["properties"];
          if (!(propertiesPlugin == null ? void 0 : propertiesPlugin._loaded)) {
            new import_obsidian16.Notice(`Core plugin "Properties view" was not found or isn't enabled. Enable it and restart Obsidian.`);
            return;
          }
          let propertiesLeaf = (_a2 = this.plugin.app.workspace.getLeavesOfType("file-properties").first()) != null ? _a2 : null;
          if (!propertiesLeaf) {
            propertiesLeaf = this.plugin.app.workspace.getRightLeaf(false);
            propertiesLeaf == null ? void 0 : propertiesLeaf.setViewState({ type: "file-properties" });
          }
          if (propertiesLeaf) this.plugin.app.workspace.revealLeaf(propertiesLeaf);
        }
      })
    );
  }
};

// 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(
      "advanced-canvas:settings-changed",
      () => this.modifyCanvasSettings(this.plugin.getCurrentCanvas())
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.modifyCanvasSettings(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:double-click",
      (canvas, event, preventDefault) => this.onDoubleClick(canvas, event, preventDefault)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-created",
      (canvas, node) => {
        this.enforceNodeGridAlignment(canvas, node);
        this.applyDefaultNodeStyles(canvas, node);
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-created",
      (canvas, edge) => this.applyDefaultEdgeStyles(canvas, edge)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-resized",
      (canvas, node) => this.enforceMaxNodeWidth(canvas, node)
    ));
  }
  modifyCanvasSettings(canvas) {
    if (!canvas) return;
    const defaultTextNodeDimensionsArray = this.plugin.settings.getSetting("defaultTextNodeDimensions");
    canvas.config.defaultTextNodeDimensions = {
      width: defaultTextNodeDimensionsArray[0],
      height: defaultTextNodeDimensionsArray[1]
    };
    const defaultFileNodeDimensionsArray = this.plugin.settings.getSetting("defaultFileNodeDimensions");
    canvas.config.defaultFileNodeDimensions = {
      width: defaultFileNodeDimensionsArray[0],
      height: defaultFileNodeDimensionsArray[1]
    };
    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;
    const 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: CanvasHelper.alignToGrid(nodeData.x),
      y: CanvasHelper.alignToGrid(nodeData.y)
    });
  }
  applyDefaultNodeStyles(_canvas, node) {
    const nodeData = node.getData();
    if (nodeData.type !== "text") return;
    let color = this.plugin.settings.getSetting("defaultTextNodeColor").toString();
    if (color === "0") color = void 0;
    node.setData({
      ...nodeData,
      color,
      styleAttributes: {
        ...nodeData.styleAttributes,
        ...this.plugin.settings.getSetting("defaultTextNodeStyleAttributes")
      }
    });
  }
  async applyDefaultEdgeStyles(canvas, edge) {
    var _a;
    const edgeData = edge.getData();
    let color = this.plugin.settings.getSetting("defaultEdgeColor").toString();
    if (this.plugin.settings.getSetting("inheritEdgeColorFromNode"))
      color = (_a = edge.from.node.getData().color) != null ? _a : color;
    if (color === "0") color = void 0;
    edge.setData({
      ...edgeData,
      color,
      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"
    });
  }
  enforceMaxNodeWidth(_canvas, node) {
    const maxNodeWidth = this.plugin.settings.getSetting("maxNodeWidth");
    if (maxNodeWidth <= 0) return;
    const nodeData = node.getData();
    if (nodeData.type !== "text" && nodeData.type !== "file" || nodeData.portal) return;
    if (nodeData.width <= maxNodeWidth) return;
    node.setData({
      ...nodeData,
      x: node.prevX !== void 0 ? node.prevX : nodeData.x,
      // Reset the position to the previous value
      width: maxNodeWidth
    });
  }
};

// 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(
      "advanced-canvas:popup-menu-created",
      (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 = [];
    const style = getComputedStyle(document.body);
    let colorIndex = DEFAULT_COLORS_COUNT + 1;
    while (style.getPropertyValue(`--canvas-color-${colorIndex}`)) {
      colors.push(colorIndex.toString());
      colorIndex++;
    }
    return colors;
  }
};

// src/canvas-extensions/collapsible-groups-canvas-extension.ts
var import_obsidian17 = require("obsidian");
var CollapsibleGroupsCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "collapsibleGroupsFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-changed",
      (canvas, node) => this.onNodeChanged(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-bbox-requested",
      (canvas, node, bbox) => this.onNodeBBoxRequested(canvas, node, bbox)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:copy",
      (canvas, selectionData) => this.onCopy(canvas, selectionData)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:data-requested",
      (_canvas, data) => this.expandNodes(data)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:data-loaded:before",
      (_canvas, data, _setData) => this.collapseNodes(data)
    ));
    this.plugin.addCommand({
      id: "toggle-collapse-group",
      name: "Toggle collapse group",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size === 1 && canvas.selection.values().next().value.getData().type === "group",
        (canvas) => this.toggleCollapseGroup(canvas, canvas.selection.values().next().value)
      )
    });
  }
  toggleCollapseGroup(canvas, node) {
    const data = node.getData();
    this.setCollapsed(node.canvas, node, data.collapsed ? void 0 : true);
    canvas.markMoved(node);
  }
  onNodeChanged(canvas, groupNode) {
    var _a, _b;
    const groupNodeData = groupNode.getData();
    if (groupNodeData.type !== "group") return;
    (_a = groupNode.collapseEl) == null ? void 0 : _a.remove();
    const collapseEl = document.createElement("span");
    collapseEl.className = "collapse-button";
    (0, import_obsidian17.setIcon)(collapseEl, groupNodeData.collapsed ? "plus-circle" : "minus-circle");
    collapseEl.onclick = () => this.toggleCollapseGroup(canvas, groupNode);
    groupNode.collapseEl = collapseEl;
    (_b = groupNode.labelEl) == null ? void 0 : _b.insertAdjacentElement("afterend", collapseEl);
  }
  onCopy(_canvas, selectionData) {
    for (const collapsedGroupData of selectionData.nodes) {
      if (collapsedGroupData.type !== "group" || !collapsedGroupData.collapsed || !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(), collapsed });
    canvas.setData(canvas.getData());
    canvas.history.current--;
    canvas.pushHistory(canvas.getData());
    canvas.requestSave();
  }
  onNodeBBoxRequested(canvas, node, bbox) {
    var _a, _b;
    const nodeData = node.getData();
    if (nodeData.type !== "group" || !nodeData.collapsed) return;
    const collapseElBBox = (_a = node.collapseEl) == null ? void 0 : _a.getBoundingClientRect();
    if (!collapseElBBox) return;
    const labelElBBox = (_b = node.labelEl) == null ? void 0 : _b.getBoundingClientRect();
    if (!labelElBBox) return;
    const minPos = canvas.posFromClient({ x: collapseElBBox.left, y: collapseElBBox.top });
    const maxPos = canvas.posFromClient({ x: labelElBBox.right, y: collapseElBBox.bottom });
    bbox.minX = minPos.x;
    bbox.minY = minPos.y;
    bbox.maxX = maxPos.x;
    bbox.maxY = maxPos.y;
  }
  expandNodes(data) {
    var _a;
    if (!data) return;
    data.nodes = (_a = data.nodes) == null ? void 0 : _a.flatMap((groupNodeData) => {
      const collapsedData = groupNodeData.collapsedData;
      if (collapsedData === void 0) return [groupNodeData];
      delete groupNodeData.collapsedData;
      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) {
    var _a;
    (_a = data == null ? void 0 : data.nodes) == null ? void 0 : _a.forEach((groupNodeData) => {
      var _a2, _b, _c, _d;
      if (!groupNodeData.collapsed) 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));
      const newContainedNodesData = containedNodesData.filter((nodeData) => {
        var _a3, _b2, _c2;
        return !((_c2 = (_b2 = (_a3 = groupNodeData.collapsedData) == null ? void 0 : _a3.nodes) == null ? void 0 : _b2.some((e) => e.id === nodeData.id)) != null ? _c2 : false);
      });
      const newContainedEdgesData = containedEdgesData.filter((edgeData) => {
        var _a3, _b2, _c2;
        return !((_c2 = (_b2 = (_a3 = groupNodeData.collapsedData) == null ? void 0 : _a3.edges) == null ? void 0 : _b2.some((n) => n.id === edgeData.id)) != null ? _c2 : false);
      });
      groupNodeData.collapsedData = {
        nodes: [
          ...(_b = (_a2 = groupNodeData.collapsedData) == null ? void 0 : _a2.nodes) != null ? _b : [],
          ...newContainedNodesData.map((nodeData) => ({
            ...nodeData,
            // Store the relative position of the node to the group
            x: nodeData.x - groupNodeData.x,
            y: nodeData.y - groupNodeData.y
          }))
        ],
        edges: [
          ...(_d = (_c = groupNodeData.collapsedData) == null ? void 0 : _c.edges) != null ? _d : [],
          ...newContainedEdgesData
        ]
      };
    });
  }
};

// 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(
      "advanced-canvas:canvas-changed",
      (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/auto-file-node-edges-canvas-extension.ts
var AUTO_EDGE_ID_PREFIX = "afe";
var AutoFileNodeEdgesCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "autoFileNodeEdgesFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.metadataCache.on("changed", (file) => {
      for (const canvas of this.plugin.getCanvases())
        this.onMetadataChanged(canvas, file);
    }));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-added",
      (canvas, node) => this.onNodeChanged(canvas, node)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-changed",
      (canvas, node) => this.onNodeChanged(canvas, node)
    ));
  }
  onMetadataChanged(canvas, file) {
    var _a;
    for (const node of canvas.nodes.values()) {
      if (node.getData().type !== "file" || ((_a = node.file) == null ? void 0 : _a.path) !== file.path) continue;
      this.updateFileNodeEdges(canvas, node);
    }
  }
  onNodeChanged(canvas, node) {
    if (node.getData().type !== "file") return;
    for (const node2 of canvas.nodes.values()) {
      if (node2.getData().type !== "file") continue;
      this.updateFileNodeEdges(canvas, node2);
    }
  }
  updateFileNodeEdges(canvas, node) {
    const edges = this.getFileNodeEdges(canvas, node);
    const newEdges = Array.from(edges.values()).filter((edge) => !canvas.edges.has(edge.id));
    canvas.importData({ nodes: [], edges: newEdges }, false, false);
    for (const edge of canvas.edges.values()) {
      if (edge.id.startsWith(`${AUTO_EDGE_ID_PREFIX}${node.id}`) && !edges.has(edge.id))
        canvas.removeEdge(edge);
    }
  }
  getFileNodeEdges(canvas, node) {
    var _a, _b;
    const canvasFile = canvas.view.file;
    if (!canvasFile || !node.file) return /* @__PURE__ */ new Map();
    const fileMetadata = this.plugin.app.metadataCache.getFileCache(node.file);
    if (!fileMetadata) return /* @__PURE__ */ new Map();
    const linkedFilesFrontmatterKey = this.plugin.settings.getSetting("autoFileNodeEdgesFrontmatterKey");
    const fileLinksToBeLinkedTo = (_b = (_a = fileMetadata.frontmatterLinks) == null ? void 0 : _a.filter((link) => link.key.split(".")[0] === linkedFilesFrontmatterKey)) != null ? _b : [];
    const filepathsToBeLinkedTo = fileLinksToBeLinkedTo.map((link) => this.plugin.app.metadataCache.getFirstLinkpathDest(link.link, canvasFile.path)).map((file) => file == null ? void 0 : file.path).filter((path) => path !== null);
    const nodesToBeLinkedTo = Array.from(canvas.nodes.values()).filter((otherNode) => {
      var _a2;
      return otherNode.id !== node.id && filepathsToBeLinkedTo.includes((_a2 = otherNode.file) == null ? void 0 : _a2.path);
    });
    const newEdges = /* @__PURE__ */ new Map();
    for (const otherNode of nodesToBeLinkedTo) {
      const edgeId = `${AUTO_EDGE_ID_PREFIX}${node.id}${otherNode.id}`;
      const bestFromSide = CanvasHelper.getBestSideForFloatingEdge(BBoxHelper.getCenterOfBBoxSide(otherNode.getBBox(), "right"), node);
      const bestToSide = CanvasHelper.getBestSideForFloatingEdge(BBoxHelper.getCenterOfBBoxSide(node.getBBox(), "left"), otherNode);
      newEdges.set(edgeId, {
        id: edgeId,
        fromNode: node.id,
        fromSide: bestFromSide,
        fromFloating: true,
        toNode: otherNode.id,
        toSide: bestToSide,
        toFloating: true
      });
    }
    return newEdges;
  }
};

// src/canvas-extensions/flip-edge-canvas-extension.ts
var FlipEdgeCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "flipEdgeFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenuCreated(canvas)
    ));
  }
  onPopupMenuCreated(canvas) {
    var _a, _b;
    const popupMenuEl = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl;
    if (!popupMenuEl) return;
    const POSSIBLE_ICONS = ["lucide-arrow-right", "lucide-move-horizontal", "line-horizontal"];
    let edgeDirectionButton = null;
    for (const icon of POSSIBLE_ICONS) {
      edgeDirectionButton = (_b = popupMenuEl.querySelector(`button:not([id]) > .svg-icon.${icon}`)) == null ? void 0 : _b.parentElement;
      if (edgeDirectionButton) break;
    }
    if (!edgeDirectionButton) return;
    edgeDirectionButton.addEventListener("click", () => this.onEdgeDirectionDropdownCreated(canvas));
  }
  onEdgeDirectionDropdownCreated(canvas) {
    const dropdownEl = document.body.querySelector("div.menu");
    if (!dropdownEl) return;
    const separatorEl = CanvasHelper.createDropdownSeparatorElement();
    dropdownEl.appendChild(separatorEl);
    const flipEdgeButton = CanvasHelper.createDropdownOptionElement({
      icon: "flip-horizontal-2",
      label: "Flip Edge",
      callback: () => this.flipEdge(canvas)
    });
    dropdownEl.appendChild(flipEdgeButton);
  }
  flipEdge(canvas) {
    const selectedEdges = [...canvas.selection].filter((item) => item.path !== void 0);
    if (selectedEdges.length === 0) return;
    for (const edge of selectedEdges) {
      edge.update({
        ...edge.from,
        node: edge.to.node,
        side: edge.to.side
      }, {
        ...edge.to,
        node: edge.from.node,
        side: edge.from.side
      });
    }
    canvas.pushHistory(canvas.getData());
  }
};

// src/canvas-extensions/edge-selection-canvas-extension.ts
var DIRECTION_MENU_MAP = {
  connected: {
    id: "select-connected-edges",
    icon: "arrows-selected",
    label: "Select Connected Edges"
  },
  outgoing: {
    id: "select-outgoing-edges",
    icon: "arrow-right-selected",
    label: "Select Outgoing Edges"
  },
  incoming: {
    id: "select-incoming-edges",
    icon: "arrow-left-selected",
    label: "Select Incoming Edges"
  }
};
var EdgeSelectionCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "edgeSelectionEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:popup-menu-created",
      (canvas) => this.onPopupMenuCreated(canvas)
    ));
  }
  onPopupMenuCreated(canvas) {
    var _a;
    const popupMenuEl = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl;
    if (!popupMenuEl) return;
    const selectionNodeData = canvas.getSelectionData().nodes;
    if (canvas.readonly || selectionNodeData.length === 0) return;
    const selectEdgeByDirection = this.plugin.settings.getSetting("selectEdgeByDirection");
    const menuDirectionSet = /* @__PURE__ */ new Set(["connected"]);
    if (selectionNodeData.length === 1) {
      const node = canvas.nodes.get(selectionNodeData[0].id);
      if (!node) return;
      const edges = canvas.getEdgesForNode(node);
      if (edges.length === 0) return;
      if (selectEdgeByDirection) {
        edges.forEach((edge) => {
          if (edge.from.node === node) {
            menuDirectionSet.add("outgoing");
          } else if (edge.to.node === node) {
            menuDirectionSet.add("incoming");
          }
        });
      }
    } else if (selectEdgeByDirection) {
      menuDirectionSet.add("outgoing");
      menuDirectionSet.add("incoming");
    }
    menuDirectionSet.forEach((direction) => {
      const config = DIRECTION_MENU_MAP[direction];
      CanvasHelper.addPopupMenuOption(canvas, CanvasHelper.createPopupMenuOption({
        ...config,
        callback: () => CanvasHelper.selectEdgesForNodes(canvas, direction)
      }));
    });
  }
};

// node_modules/html-to-image/es/util.js
function resolveUrl(url, baseUrl) {
  if (url.match(/^[a-z]+:\/\//i)) {
    return url;
  }
  if (url.match(/^\/\//)) {
    return window.location.protocol + url;
  }
  if (url.match(/^[a-z]+:/i)) {
    return url;
  }
  const doc = document.implementation.createHTMLDocument();
  const base = doc.createElement("base");
  const a = doc.createElement("a");
  doc.head.appendChild(base);
  doc.body.appendChild(a);
  if (baseUrl) {
    base.href = baseUrl;
  }
  a.href = url;
  return a.href;
}
var uuid = /* @__PURE__ */ (() => {
  let counter = 0;
  const random = () => (
    // eslint-disable-next-line no-bitwise
    `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
  );
  return () => {
    counter += 1;
    return `u${random()}${counter}`;
  };
})();
function toArray(arrayLike) {
  const arr = [];
  for (let i = 0, l = arrayLike.length; i < l; i++) {
    arr.push(arrayLike[i]);
  }
  return arr;
}
function px(node, styleProperty) {
  const win = node.ownerDocument.defaultView || window;
  const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
  return val ? parseFloat(val.replace("px", "")) : 0;
}
function getNodeWidth(node) {
  const leftBorder = px(node, "border-left-width");
  const rightBorder = px(node, "border-right-width");
  return node.clientWidth + leftBorder + rightBorder;
}
function getNodeHeight(node) {
  const topBorder = px(node, "border-top-width");
  const bottomBorder = px(node, "border-bottom-width");
  return node.clientHeight + topBorder + bottomBorder;
}
function getImageSize(targetNode, options = {}) {
  const width = options.width || getNodeWidth(targetNode);
  const height = options.height || getNodeHeight(targetNode);
  return { width, height };
}
function getPixelRatio() {
  let ratio;
  let FINAL_PROCESS;
  try {
    FINAL_PROCESS = process;
  } catch (e) {
  }
  const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
  if (val) {
    ratio = parseInt(val, 10);
    if (Number.isNaN(ratio)) {
      ratio = 1;
    }
  }
  return ratio || window.devicePixelRatio || 1;
}
var canvasDimensionLimit = 16384;
function checkCanvasDimensions(canvas) {
  if (canvas.width > canvasDimensionLimit || canvas.height > canvasDimensionLimit) {
    if (canvas.width > canvasDimensionLimit && canvas.height > canvasDimensionLimit) {
      if (canvas.width > canvas.height) {
        canvas.height *= canvasDimensionLimit / canvas.width;
        canvas.width = canvasDimensionLimit;
      } else {
        canvas.width *= canvasDimensionLimit / canvas.height;
        canvas.height = canvasDimensionLimit;
      }
    } else if (canvas.width > canvasDimensionLimit) {
      canvas.height *= canvasDimensionLimit / canvas.width;
      canvas.width = canvasDimensionLimit;
    } else {
      canvas.width *= canvasDimensionLimit / canvas.height;
      canvas.height = canvasDimensionLimit;
    }
  }
}
function createImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.decode = () => resolve(img);
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.crossOrigin = "anonymous";
    img.decoding = "async";
    img.src = url;
  });
}
async function svgToDataURL(svg) {
  return Promise.resolve().then(() => new XMLSerializer().serializeToString(svg)).then(encodeURIComponent).then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
}
async function nodeToDataURL(node, width, height) {
  const xmlns = "http://www.w3.org/2000/svg";
  const svg = document.createElementNS(xmlns, "svg");
  const foreignObject = document.createElementNS(xmlns, "foreignObject");
  svg.setAttribute("width", `${width}`);
  svg.setAttribute("height", `${height}`);
  svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
  foreignObject.setAttribute("width", "100%");
  foreignObject.setAttribute("height", "100%");
  foreignObject.setAttribute("x", "0");
  foreignObject.setAttribute("y", "0");
  foreignObject.setAttribute("externalResourcesRequired", "true");
  svg.appendChild(foreignObject);
  foreignObject.appendChild(node);
  return svgToDataURL(svg);
}
var isInstanceOfElement = (node, instance) => {
  if (node instanceof instance)
    return true;
  const nodePrototype = Object.getPrototypeOf(node);
  if (nodePrototype === null)
    return false;
  return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
};

// node_modules/html-to-image/es/clone-pseudos.js
function formatCSSText(style) {
  const content = style.getPropertyValue("content");
  return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
}
function formatCSSProperties(style) {
  return toArray(style).map((name) => {
    const value = style.getPropertyValue(name);
    const priority = style.getPropertyPriority(name);
    return `${name}: ${value}${priority ? " !important" : ""};`;
  }).join(" ");
}
function getPseudoElementStyle(className, pseudo, style) {
  const selector = `.${className}:${pseudo}`;
  const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style);
  return document.createTextNode(`${selector}{${cssText}}`);
}
function clonePseudoElement(nativeNode, clonedNode, pseudo) {
  const style = window.getComputedStyle(nativeNode, pseudo);
  const content = style.getPropertyValue("content");
  if (content === "" || content === "none") {
    return;
  }
  const className = uuid();
  try {
    clonedNode.className = `${clonedNode.className} ${className}`;
  } catch (err) {
    return;
  }
  const styleElement = document.createElement("style");
  styleElement.appendChild(getPseudoElementStyle(className, pseudo, style));
  clonedNode.appendChild(styleElement);
}
function clonePseudoElements(nativeNode, clonedNode) {
  clonePseudoElement(nativeNode, clonedNode, ":before");
  clonePseudoElement(nativeNode, clonedNode, ":after");
}

// node_modules/html-to-image/es/mimes.js
var WOFF = "application/font-woff";
var JPEG = "image/jpeg";
var mimes = {
  woff: WOFF,
  woff2: WOFF,
  ttf: "application/font-truetype",
  eot: "application/vnd.ms-fontobject",
  png: "image/png",
  jpg: JPEG,
  jpeg: JPEG,
  gif: "image/gif",
  tiff: "image/tiff",
  svg: "image/svg+xml",
  webp: "image/webp"
};
function getExtension(url) {
  const match = /\.([^./]*?)$/g.exec(url);
  return match ? match[1] : "";
}
function getMimeType(url) {
  const extension = getExtension(url).toLowerCase();
  return mimes[extension] || "";
}

// node_modules/html-to-image/es/dataurl.js
function getContentFromDataUrl(dataURL) {
  return dataURL.split(/,/)[1];
}
function isDataUrl(url) {
  return url.search(/^(data:)/) !== -1;
}
function makeDataUrl(content, mimeType) {
  return `data:${mimeType};base64,${content}`;
}
async function fetchAsDataURL(url, init, process2) {
  const res = await fetch(url, init);
  if (res.status === 404) {
    throw new Error(`Resource "${res.url}" not found`);
  }
  const blob = await res.blob();
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = reject;
    reader.onloadend = () => {
      try {
        resolve(process2({ res, result: reader.result }));
      } catch (error) {
        reject(error);
      }
    };
    reader.readAsDataURL(blob);
  });
}
var cache = {};
function getCacheKey(url, contentType, includeQueryParams) {
  let key = url.replace(/\?.*/, "");
  if (includeQueryParams) {
    key = url;
  }
  if (/ttf|otf|eot|woff2?/i.test(key)) {
    key = key.replace(/.*\//, "");
  }
  return contentType ? `[${contentType}]${key}` : key;
}
async function resourceToDataURL(resourceUrl, contentType, options) {
  const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);
  if (cache[cacheKey] != null) {
    return cache[cacheKey];
  }
  if (options.cacheBust) {
    resourceUrl += (/\?/.test(resourceUrl) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
  }
  let dataURL;
  try {
    const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {
      if (!contentType) {
        contentType = res.headers.get("Content-Type") || "";
      }
      return getContentFromDataUrl(result);
    });
    dataURL = makeDataUrl(content, contentType);
  } catch (error) {
    dataURL = options.imagePlaceholder || "";
    let msg = `Failed to fetch resource: ${resourceUrl}`;
    if (error) {
      msg = typeof error === "string" ? error : error.message;
    }
    if (msg) {
      console.warn(msg);
    }
  }
  cache[cacheKey] = dataURL;
  return dataURL;
}

// node_modules/html-to-image/es/clone-node.js
async function cloneCanvasElement(canvas) {
  const dataURL = canvas.toDataURL();
  if (dataURL === "data:,") {
    return canvas.cloneNode(false);
  }
  return createImage(dataURL);
}
async function cloneVideoElement(video, options) {
  if (video.currentSrc) {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = video.clientWidth;
    canvas.height = video.clientHeight;
    ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
    const dataURL2 = canvas.toDataURL();
    return createImage(dataURL2);
  }
  const poster = video.poster;
  const contentType = getMimeType(poster);
  const dataURL = await resourceToDataURL(poster, contentType, options);
  return createImage(dataURL);
}
async function cloneIFrameElement(iframe) {
  var _a;
  try {
    if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {
      return await cloneNode(iframe.contentDocument.body, {}, true);
    }
  } catch (_b) {
  }
  return iframe.cloneNode(false);
}
async function cloneSingleNode(node, options) {
  if (isInstanceOfElement(node, HTMLCanvasElement)) {
    return cloneCanvasElement(node);
  }
  if (isInstanceOfElement(node, HTMLVideoElement)) {
    return cloneVideoElement(node, options);
  }
  if (isInstanceOfElement(node, HTMLIFrameElement)) {
    return cloneIFrameElement(node);
  }
  return node.cloneNode(false);
}
var isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SLOT";
async function cloneChildren(nativeNode, clonedNode, options) {
  var _a, _b;
  let children = [];
  if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
    children = toArray(nativeNode.assignedNodes());
  } else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {
    children = toArray(nativeNode.contentDocument.body.childNodes);
  } else {
    children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
  }
  if (children.length === 0 || isInstanceOfElement(nativeNode, HTMLVideoElement)) {
    return clonedNode;
  }
  await children.reduce((deferred, child) => deferred.then(() => cloneNode(child, options)).then((clonedChild) => {
    if (clonedChild) {
      clonedNode.appendChild(clonedChild);
    }
  }), Promise.resolve());
  return clonedNode;
}
function cloneCSSStyle(nativeNode, clonedNode) {
  const targetStyle = clonedNode.style;
  if (!targetStyle) {
    return;
  }
  const sourceStyle = window.getComputedStyle(nativeNode);
  if (sourceStyle.cssText) {
    targetStyle.cssText = sourceStyle.cssText;
    targetStyle.transformOrigin = sourceStyle.transformOrigin;
  } else {
    toArray(sourceStyle).forEach((name) => {
      let value = sourceStyle.getPropertyValue(name);
      if (name === "font-size" && value.endsWith("px")) {
        const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;
        value = `${reducedFont}px`;
      }
      if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && name === "display" && value === "inline") {
        value = "block";
      }
      if (name === "d" && clonedNode.getAttribute("d")) {
        value = `path(${clonedNode.getAttribute("d")})`;
      }
      targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
    });
  }
}
function cloneInputValue(nativeNode, clonedNode) {
  if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
    clonedNode.innerHTML = nativeNode.value;
  }
  if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
    clonedNode.setAttribute("value", nativeNode.value);
  }
}
function cloneSelectValue(nativeNode, clonedNode) {
  if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
    const clonedSelect = clonedNode;
    const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute("value"));
    if (selectedOption) {
      selectedOption.setAttribute("selected", "");
    }
  }
}
function decorate(nativeNode, clonedNode) {
  if (isInstanceOfElement(clonedNode, Element)) {
    cloneCSSStyle(nativeNode, clonedNode);
    clonePseudoElements(nativeNode, clonedNode);
    cloneInputValue(nativeNode, clonedNode);
    cloneSelectValue(nativeNode, clonedNode);
  }
  return clonedNode;
}
async function ensureSVGSymbols(clone, options) {
  const uses = clone.querySelectorAll ? clone.querySelectorAll("use") : [];
  if (uses.length === 0) {
    return clone;
  }
  const processedDefs = {};
  for (let i = 0; i < uses.length; i++) {
    const use = uses[i];
    const id = use.getAttribute("xlink:href");
    if (id) {
      const exist = clone.querySelector(id);
      const definition = document.querySelector(id);
      if (!exist && definition && !processedDefs[id]) {
        processedDefs[id] = await cloneNode(definition, options, true);
      }
    }
  }
  const nodes = Object.values(processedDefs);
  if (nodes.length) {
    const ns = "http://www.w3.org/1999/xhtml";
    const svg = document.createElementNS(ns, "svg");
    svg.setAttribute("xmlns", ns);
    svg.style.position = "absolute";
    svg.style.width = "0";
    svg.style.height = "0";
    svg.style.overflow = "hidden";
    svg.style.display = "none";
    const defs = document.createElementNS(ns, "defs");
    svg.appendChild(defs);
    for (let i = 0; i < nodes.length; i++) {
      defs.appendChild(nodes[i]);
    }
    clone.appendChild(svg);
  }
  return clone;
}
async function cloneNode(node, options, isRoot) {
  if (!isRoot && options.filter && !options.filter(node)) {
    return null;
  }
  return Promise.resolve(node).then((clonedNode) => cloneSingleNode(clonedNode, options)).then((clonedNode) => cloneChildren(node, clonedNode, options)).then((clonedNode) => decorate(node, clonedNode)).then((clonedNode) => ensureSVGSymbols(clonedNode, options));
}

// node_modules/html-to-image/es/embed-resources.js
var URL_REGEX = /url\((['"]?)([^'"]+?)\1\)/g;
var URL_WITH_FORMAT_REGEX = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
var FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
function toRegex(url) {
  const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
  return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
}
function parseURLs(cssText) {
  const urls = [];
  cssText.replace(URL_REGEX, (raw, quotation, url) => {
    urls.push(url);
    return raw;
  });
  return urls.filter((url) => !isDataUrl(url));
}
async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {
  try {
    const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;
    const contentType = getMimeType(resourceURL);
    let dataURL;
    if (getContentFromUrl) {
      const content = await getContentFromUrl(resolvedURL);
      dataURL = makeDataUrl(content, contentType);
    } else {
      dataURL = await resourceToDataURL(resolvedURL, contentType, options);
    }
    return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);
  } catch (error) {
  }
  return cssText;
}
function filterPreferredFontFormat(str, { preferredFontFormat }) {
  return !preferredFontFormat ? str : str.replace(FONT_SRC_REGEX, (match) => {
    while (true) {
      const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];
      if (!format) {
        return "";
      }
      if (format === preferredFontFormat) {
        return `src: ${src};`;
      }
    }
  });
}
function shouldEmbed(url) {
  return url.search(URL_REGEX) !== -1;
}
async function embedResources(cssText, baseUrl, options) {
  if (!shouldEmbed(cssText)) {
    return cssText;
  }
  const filteredCSSText = filterPreferredFontFormat(cssText, options);
  const urls = parseURLs(filteredCSSText);
  return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));
}

// node_modules/html-to-image/es/embed-images.js
async function embedProp(propName, node, options) {
  var _a;
  const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);
  if (propValue) {
    const cssString = await embedResources(propValue, null, options);
    node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));
    return true;
  }
  return false;
}
async function embedBackground(clonedNode, options) {
  if (!await embedProp("background", clonedNode, options)) {
    await embedProp("background-image", clonedNode, options);
  }
  if (!await embedProp("mask", clonedNode, options)) {
    await embedProp("mask-image", clonedNode, options);
  }
}
async function embedImageNode(clonedNode, options) {
  const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);
  if (!(isImageElement && !isDataUrl(clonedNode.src)) && !(isInstanceOfElement(clonedNode, SVGImageElement) && !isDataUrl(clonedNode.href.baseVal))) {
    return;
  }
  const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;
  const dataURL = await resourceToDataURL(url, getMimeType(url), options);
  await new Promise((resolve, reject) => {
    clonedNode.onload = resolve;
    clonedNode.onerror = reject;
    const image = clonedNode;
    if (image.decode) {
      image.decode = resolve;
    }
    if (image.loading === "lazy") {
      image.loading = "eager";
    }
    if (isImageElement) {
      clonedNode.srcset = "";
      clonedNode.src = dataURL;
    } else {
      clonedNode.href.baseVal = dataURL;
    }
  });
}
async function embedChildren(clonedNode, options) {
  const children = toArray(clonedNode.childNodes);
  const deferreds = children.map((child) => embedImages(child, options));
  await Promise.all(deferreds).then(() => clonedNode);
}
async function embedImages(clonedNode, options) {
  if (isInstanceOfElement(clonedNode, Element)) {
    await embedBackground(clonedNode, options);
    await embedImageNode(clonedNode, options);
    await embedChildren(clonedNode, options);
  }
}

// node_modules/html-to-image/es/apply-style.js
function applyStyle(node, options) {
  const { style } = node;
  if (options.backgroundColor) {
    style.backgroundColor = options.backgroundColor;
  }
  if (options.width) {
    style.width = `${options.width}px`;
  }
  if (options.height) {
    style.height = `${options.height}px`;
  }
  const manual = options.style;
  if (manual != null) {
    Object.keys(manual).forEach((key) => {
      style[key] = manual[key];
    });
  }
  return node;
}

// node_modules/html-to-image/es/embed-webfonts.js
var cssFetchCache = {};
async function fetchCSS(url) {
  let cache2 = cssFetchCache[url];
  if (cache2 != null) {
    return cache2;
  }
  const res = await fetch(url);
  const cssText = await res.text();
  cache2 = { url, cssText };
  cssFetchCache[url] = cache2;
  return cache2;
}
async function embedFonts(data, options) {
  let cssText = data.cssText;
  const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
  const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
  const loadFonts = fontLocs.map(async (loc) => {
    let url = loc.replace(regexUrl, "$1");
    if (!url.startsWith("https://")) {
      url = new URL(url, data.url).href;
    }
    return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
      cssText = cssText.replace(loc, `url(${result})`);
      return [loc, result];
    });
  });
  return Promise.all(loadFonts).then(() => cssText);
}
function parseCSS(source) {
  if (source == null) {
    return [];
  }
  const result = [];
  const commentsRegex = /(\/\*[\s\S]*?\*\/)/gi;
  let cssText = source.replace(commentsRegex, "");
  const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
  while (true) {
    const matches = keyframesRegex.exec(cssText);
    if (matches === null) {
      break;
    }
    result.push(matches[0]);
  }
  cssText = cssText.replace(keyframesRegex, "");
  const importRegex = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
  const combinedCSSRegex = "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})";
  const unifiedRegex = new RegExp(combinedCSSRegex, "gi");
  while (true) {
    let matches = importRegex.exec(cssText);
    if (matches === null) {
      matches = unifiedRegex.exec(cssText);
      if (matches === null) {
        break;
      } else {
        importRegex.lastIndex = unifiedRegex.lastIndex;
      }
    } else {
      unifiedRegex.lastIndex = importRegex.lastIndex;
    }
    result.push(matches[0]);
  }
  return result;
}
async function getCSSRules(styleSheets, options) {
  const ret = [];
  const deferreds = [];
  styleSheets.forEach((sheet) => {
    if ("cssRules" in sheet) {
      try {
        toArray(sheet.cssRules || []).forEach((item, index) => {
          if (item.type === CSSRule.IMPORT_RULE) {
            let importIndex = index + 1;
            const url = item.href;
            const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
              try {
                sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
              } catch (error) {
                console.error("Error inserting rule from remote css", {
                  rule,
                  error
                });
              }
            })).catch((e) => {
              console.error("Error loading remote css", e.toString());
            });
            deferreds.push(deferred);
          }
        });
      } catch (e) {
        const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
        if (sheet.href != null) {
          deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
            inline.insertRule(rule, sheet.cssRules.length);
          })).catch((err) => {
            console.error("Error loading remote stylesheet", err);
          }));
        }
        console.error("Error inlining remote css file", e);
      }
    }
  });
  return Promise.all(deferreds).then(() => {
    styleSheets.forEach((sheet) => {
      if ("cssRules" in sheet) {
        try {
          toArray(sheet.cssRules || []).forEach((item) => {
            ret.push(item);
          });
        } catch (e) {
          console.error(`Error while reading CSS rules from ${sheet.href}`, e);
        }
      }
    });
    return ret;
  });
}
function getWebFontRules(cssRules) {
  return cssRules.filter((rule) => rule.type === CSSRule.FONT_FACE_RULE).filter((rule) => shouldEmbed(rule.style.getPropertyValue("src")));
}
async function parseWebFontRules(node, options) {
  if (node.ownerDocument == null) {
    throw new Error("Provided element is not within a Document");
  }
  const styleSheets = toArray(node.ownerDocument.styleSheets);
  const cssRules = await getCSSRules(styleSheets, options);
  return getWebFontRules(cssRules);
}
async function getWebFontCSS(node, options) {
  const rules = await parseWebFontRules(node, options);
  const cssTexts = await Promise.all(rules.map((rule) => {
    const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
    return embedResources(rule.cssText, baseUrl, options);
  }));
  return cssTexts.join("\n");
}
async function embedWebFonts(clonedNode, options) {
  const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
  if (cssText) {
    const styleNode = document.createElement("style");
    const sytleContent = document.createTextNode(cssText);
    styleNode.appendChild(sytleContent);
    if (clonedNode.firstChild) {
      clonedNode.insertBefore(styleNode, clonedNode.firstChild);
    } else {
      clonedNode.appendChild(styleNode);
    }
  }
}

// node_modules/html-to-image/es/index.js
async function toSvg(node, options = {}) {
  const { width, height } = getImageSize(node, options);
  const clonedNode = await cloneNode(node, options, true);
  await embedWebFonts(clonedNode, options);
  await embedImages(clonedNode, options);
  applyStyle(clonedNode, options);
  const datauri = await nodeToDataURL(clonedNode, width, height);
  return datauri;
}
async function toCanvas(node, options = {}) {
  const { width, height } = getImageSize(node, options);
  const svg = await toSvg(node, options);
  const img = await createImage(svg);
  const canvas = document.createElement("canvas");
  const context = canvas.getContext("2d");
  const ratio = options.pixelRatio || getPixelRatio();
  const canvasWidth = options.canvasWidth || width;
  const canvasHeight = options.canvasHeight || height;
  canvas.width = canvasWidth * ratio;
  canvas.height = canvasHeight * ratio;
  if (!options.skipAutoScale) {
    checkCanvasDimensions(canvas);
  }
  canvas.style.width = `${canvasWidth}`;
  canvas.style.height = `${canvasHeight}`;
  if (options.backgroundColor) {
    context.fillStyle = options.backgroundColor;
    context.fillRect(0, 0, canvas.width, canvas.height);
  }
  context.drawImage(img, 0, 0, canvas.width, canvas.height);
  return canvas;
}
async function toPng(node, options = {}) {
  const canvas = await toCanvas(node, options);
  return canvas.toDataURL();
}

// src/canvas-extensions/export-canvas-extension.ts
var import_obsidian18 = require("obsidian");
var MAX_ALLOWED_LOADING_TIME = 1e4;
var ExportCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "betterExportFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-breakpoint-changed",
      (canvas, node, breakpointRef) => {
        if (canvas.screenshotting) breakpointRef.value = true;
      }
    ));
    this.plugin.addCommand({
      id: "export-all-as-image",
      name: "Export canvas as image",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.nodes.size > 0,
        (canvas) => this.showExportImageSettingsModal(canvas, null)
      )
    });
    this.plugin.addCommand({
      id: "export-selected-as-image",
      name: "Export selected nodes as image",
      checkCallback: CanvasHelper.canvasCommand(
        this.plugin,
        (canvas) => canvas.selection.size > 0,
        (canvas) => this.showExportImageSettingsModal(
          canvas,
          canvas.getSelectionData().nodes.map((nodeData) => canvas.nodes.get(nodeData.id)).filter((node) => node !== void 0)
        )
      )
    });
  }
  async showExportImageSettingsModal(canvas, nodesToExport) {
    const modal = new import_obsidian18.Modal(this.plugin.app);
    modal.setTitle("Export image settings");
    let pixelRatioSetting = null;
    let noFontExportSetting = null;
    let transparentBackgroundSetting = null;
    const updateDynamicSettings = () => {
      var _a, _b, _c, _d, _e, _f;
      if (svg) {
        (_a = pixelRatioSetting == null ? void 0 : pixelRatioSetting.settingEl) == null ? void 0 : _a.hide();
        (_b = noFontExportSetting == null ? void 0 : noFontExportSetting.settingEl) == null ? void 0 : _b.show();
        (_c = transparentBackgroundSetting == null ? void 0 : transparentBackgroundSetting.settingEl) == null ? void 0 : _c.hide();
      } else {
        (_d = pixelRatioSetting == null ? void 0 : pixelRatioSetting.settingEl) == null ? void 0 : _d.show();
        (_e = noFontExportSetting == null ? void 0 : noFontExportSetting.settingEl) == null ? void 0 : _e.hide();
        (_f = transparentBackgroundSetting == null ? void 0 : transparentBackgroundSetting.settingEl) == null ? void 0 : _f.show();
      }
    };
    let svg = false;
    new import_obsidian18.Setting(modal.contentEl).setName("Export file format").setDesc("Choose the file format to export the canvas as.").addDropdown(
      (dropdown) => dropdown.addOptions({
        png: "PNG",
        svg: "SVG"
      }).setValue(svg ? "svg" : "png").onChange((value) => {
        svg = value === "svg";
        updateDynamicSettings();
      })
    );
    let pixelRatioFactor = 1;
    pixelRatioSetting = new import_obsidian18.Setting(modal.contentEl).setName("Pixel ratio").setDesc("Higher pixel ratios result in higher resolution images but also larger file sizes.").addSlider(
      (slider) => slider.setDynamicTooltip().setLimits(0.2, 5, 0.1).setValue(pixelRatioFactor).onChange((value) => pixelRatioFactor = value)
    );
    let noFontExport = true;
    noFontExportSetting = new import_obsidian18.Setting(modal.contentEl).setName("Skip font export").setDesc("This will not include the fonts in the exported SVG. This will make the SVG file smaller.").addToggle(
      (toggle) => toggle.setValue(noFontExport).onChange((value) => noFontExport = value)
    );
    let theme = document.body.classList.contains("theme-dark") ? "dark" : "light";
    new import_obsidian18.Setting(modal.contentEl).setName("Theme").setDesc("The theme used for the export.").addDropdown(
      (dropdown) => dropdown.addOptions({
        light: "Light",
        dark: "Dark"
      }).setValue(theme).onChange((value) => theme = value)
    );
    let watermark = false;
    new import_obsidian18.Setting(modal.contentEl).setName("Show logo").setDesc("This will add an Obsidian + Advanced Canvas logo to the bottom left.").addToggle(
      (toggle) => toggle.setValue(watermark).onChange((value) => watermark = value)
    );
    let garbledText = false;
    new import_obsidian18.Setting(modal.contentEl).setName("Privacy mode").setDesc("This will obscure any text on your canvas.").addToggle(
      (toggle) => toggle.setValue(garbledText).onChange((value) => garbledText = value)
    );
    let transparentBackground = false;
    transparentBackgroundSetting = new import_obsidian18.Setting(modal.contentEl).setName("Transparent background").setDesc("This will make the background of the image transparent.").addToggle(
      (toggle) => toggle.setValue(transparentBackground).onChange((value) => transparentBackground = value)
    );
    new import_obsidian18.Setting(modal.contentEl).addButton(
      (button) => button.setButtonText("Save").setCta().onClick(async () => {
        modal.close();
        this.exportImage(
          canvas,
          nodesToExport,
          svg,
          svg ? 1 : pixelRatioFactor,
          svg ? noFontExport : false,
          theme,
          watermark,
          garbledText,
          svg ? true : transparentBackground
        );
      })
    );
    updateDynamicSettings();
    modal.open();
  }
  async exportImage(canvas, nodesToExport, svg, pixelRatioFactor, noFontExport, theme, watermark, garbledText, transparentBackground) {
    var _a, _b, _c;
    const cachedTheme = document.body.classList.contains("theme-dark") ? "dark" : "light";
    if (theme !== cachedTheme) {
      document.body.classList.toggle("theme-dark", theme === "dark");
      document.body.classList.toggle("theme-light", theme === "light");
    }
    const isWholeCanvas = nodesToExport === null;
    if (!nodesToExport) nodesToExport = [...canvas.nodes.values()];
    const nodesToExportIds = nodesToExport.map((node) => node.getData().id);
    const edgesToExport = [...canvas.edges.values()].filter((edge) => {
      const edgeData = edge.getData();
      return nodesToExportIds.includes(edgeData.fromNode) && nodesToExportIds.includes(edgeData.toNode);
    });
    const backgroundColor = transparentBackground ? void 0 : window.getComputedStyle(canvas.canvasEl).getPropertyValue("--canvas-background");
    new import_obsidian18.Notice("Exporting the canvas. Please wait...");
    const interactionBlocker = this.getInteractionBlocker();
    document.body.appendChild(interactionBlocker);
    canvas.screenshotting = true;
    canvas.canvasEl.classList.add("is-exporting");
    if (garbledText) canvas.canvasEl.classList.add("is-text-garbled");
    let watermarkEl = null;
    const cachedSelection = new Set(canvas.selection);
    canvas.deselectAll();
    const cachedViewport = { x: canvas.x, y: canvas.y, zoom: canvas.zoom };
    try {
      const targetBoundingBox = CanvasHelper.getBBox([...nodesToExport, ...edgesToExport]);
      let enlargedTargetBoundingBox = BBoxHelper.scaleBBox(targetBoundingBox, 1.1);
      const enlargedTargetBoundingBoxSize = { width: enlargedTargetBoundingBox.maxX - enlargedTargetBoundingBox.minX, height: enlargedTargetBoundingBox.maxY - enlargedTargetBoundingBox.minY };
      const canvasElSize = { width: canvas.canvasEl.clientWidth, height: canvas.canvasEl.clientHeight };
      const requiredPixelRatio = Math.max(enlargedTargetBoundingBoxSize.width / canvasElSize.width, enlargedTargetBoundingBoxSize.height / canvasElSize.height);
      const pixelRatio = svg ? void 0 : Math.round(requiredPixelRatio * pixelRatioFactor);
      watermarkEl = watermark ? this.getWatermark(enlargedTargetBoundingBox) : null;
      if (watermarkEl) canvas.canvasEl.appendChild(watermarkEl);
      const actualAspectRatio = canvas.canvasRect.width / canvas.canvasRect.height;
      const targetAspectRatio = (enlargedTargetBoundingBox.maxX - enlargedTargetBoundingBox.minX) / (enlargedTargetBoundingBox.maxY - enlargedTargetBoundingBox.minY);
      let adjustedBoundingBox = { ...enlargedTargetBoundingBox };
      if (actualAspectRatio > targetAspectRatio) {
        const targetHeight = enlargedTargetBoundingBox.maxY - enlargedTargetBoundingBox.minY;
        const actualWidth = targetHeight * actualAspectRatio;
        adjustedBoundingBox.maxX = enlargedTargetBoundingBox.minX + actualWidth;
      } else {
        const targetWidth = enlargedTargetBoundingBox.maxX - enlargedTargetBoundingBox.minX;
        const actualHeight = targetWidth / actualAspectRatio;
        adjustedBoundingBox.maxY = enlargedTargetBoundingBox.minY + actualHeight;
      }
      canvas.zoomToRealBbox(adjustedBoundingBox);
      canvas.setViewport(canvas.tx, canvas.ty, canvas.tZoom);
      await sleep(10);
      let canvasScale = parseFloat(((_a = canvas.canvasEl.style.transform.match(/scale\((\d+(\.\d+)?)\)/)) == null ? void 0 : _a[1]) || "1");
      const edgePathsBBox = BBoxHelper.combineBBoxes(edgesToExport.map((edge) => {
        const edgeCenter = edge.getCenter();
        const labelWidth = edge.labelElement ? edge.labelElement.wrapperEl.getBoundingClientRect().width / canvasScale : 0;
        return { minX: edgeCenter.x - labelWidth / 2, minY: edgeCenter.y, maxX: edgeCenter.x + labelWidth / 2, maxY: edgeCenter.y };
      }));
      const enlargedEdgePathsBBox = BBoxHelper.enlargeBBox(edgePathsBBox, 1.1);
      enlargedTargetBoundingBox = BBoxHelper.combineBBoxes([enlargedTargetBoundingBox, enlargedEdgePathsBBox]);
      adjustedBoundingBox = BBoxHelper.combineBBoxes([adjustedBoundingBox, enlargedEdgePathsBBox]);
      canvas.zoomToRealBbox(adjustedBoundingBox);
      canvas.setViewport(canvas.tx, canvas.ty, canvas.tZoom);
      await sleep(10);
      const canvasViewportBBox = canvas.getViewportBBox();
      canvasScale = parseFloat(((_b = canvas.canvasEl.style.transform.match(/scale\((\d+(\.\d+)?)\)/)) == null ? void 0 : _b[1]) || "1");
      let width = (canvasViewportBBox.maxX - canvasViewportBBox.minX) * canvasScale;
      let height = (canvasViewportBBox.maxY - canvasViewportBBox.minY) * canvasScale;
      if (actualAspectRatio > targetAspectRatio)
        width = height * targetAspectRatio;
      else height = width / targetAspectRatio;
      let unloadedNodes = nodesToExport.filter((node) => node.initialized === false || node.isContentMounted === false);
      const startTimestamp = performance.now();
      while (unloadedNodes.length > 0 && performance.now() - startTimestamp < MAX_ALLOWED_LOADING_TIME) {
        await sleep(10);
        unloadedNodes = nodesToExport.filter((node) => node.initialized === false || node.isContentMounted === false);
        console.info(`Waiting for ${unloadedNodes.length} nodes to finish loading...`);
      }
      if (unloadedNodes.length === 0) {
        const nodeElements = nodesToExport.map((node) => node.nodeEl);
        const edgePathAndArrowElements = edgesToExport.map((edge) => [edge.lineGroupEl, edge.lineEndGroupEl]).flat();
        const edgeLabelElements = edgesToExport.map((edge) => {
          var _a2;
          return (_a2 = edge.labelElement) == null ? void 0 : _a2.wrapperEl;
        }).filter((labelElement) => labelElement !== void 0);
        const filter = (element) => {
          var _a2, _b2, _c2, _d;
          if (((_a2 = element.classList) == null ? void 0 : _a2.contains("canvas-node")) && !nodeElements.includes(element))
            return false;
          if (((_c2 = (_b2 = element.parentElement) == null ? void 0 : _b2.classList) == null ? void 0 : _c2.contains("canvas-edges")) && !edgePathAndArrowElements.includes(element))
            return false;
          if (((_d = element.classList) == null ? void 0 : _d.contains("canvas-path-label-wrapper")) && !edgeLabelElements.includes(element))
            return false;
          return true;
        };
        const options = {
          pixelRatio,
          backgroundColor,
          height,
          width,
          filter
        };
        if (noFontExport) options.fontEmbedCSS = "";
        let imageDataUri = svg ? await toSvg(canvas.canvasEl, options) : await toPng(canvas.canvasEl, options);
        if (svg) {
          const header = `<?xml version="1.0" encoding="UTF-8"?>`;
          imageDataUri = imageDataUri.replace(
            encodeURIComponent("<svg "),
            encodeURIComponent(`${header}<svg `)
          );
        }
        let baseFilename = `${((_c = canvas.view.file) == null ? void 0 : _c.basename) || "Untitled"}`;
        if (!isWholeCanvas) baseFilename += ` - Selection of ${nodesToExport.length}`;
        const filename = `${baseFilename}.${svg ? "svg" : "png"}`;
        const downloadEl = document.createElement("a");
        downloadEl.href = imageDataUri;
        downloadEl.download = filename;
        downloadEl.click();
      } else {
        const ERROR_MESSAGE = "Export cancelled: Nodes did not finish loading in time";
        new import_obsidian18.Notice(ERROR_MESSAGE);
        console.error(ERROR_MESSAGE);
      }
    } finally {
      canvas.screenshotting = false;
      canvas.canvasEl.classList.remove("is-exporting");
      if (garbledText) canvas.canvasEl.classList.remove("is-text-garbled");
      if (watermarkEl) canvas.canvasEl.removeChild(watermarkEl);
      canvas.updateSelection(() => canvas.selection = cachedSelection);
      canvas.setViewport(cachedViewport.x, cachedViewport.y, cachedViewport.zoom);
      interactionBlocker.remove();
      if (theme !== cachedTheme) {
        document.body.classList.toggle("theme-dark", cachedTheme === "dark");
        document.body.classList.toggle("theme-light", cachedTheme === "light");
      }
    }
  }
  getInteractionBlocker() {
    const interactionBlocker = document.createElement("div");
    interactionBlocker.classList.add("progress-bar-container");
    const progressBar = document.createElement("div");
    progressBar.classList.add("progress-bar");
    interactionBlocker.appendChild(progressBar);
    const progressBarMessage = document.createElement("div");
    progressBarMessage.classList.add("progress-bar-message", "u-center-text");
    progressBarMessage.innerText = "Generating image...";
    progressBar.appendChild(progressBarMessage);
    const progressBarIndicator = document.createElement("div");
    progressBarIndicator.classList.add("progress-bar-indicator");
    progressBar.appendChild(progressBarIndicator);
    const progressBarLine = document.createElement("div");
    progressBarLine.classList.add("progress-bar-line");
    progressBarIndicator.appendChild(progressBarLine);
    const progressBarSublineIncrease = document.createElement("div");
    progressBarSublineIncrease.classList.add("progress-bar-subline", "mod-increase");
    progressBarIndicator.appendChild(progressBarSublineIncrease);
    const progressBarSublineDecrease = document.createElement("div");
    progressBarSublineDecrease.classList.add("progress-bar-subline", "mod-decrease");
    progressBarIndicator.appendChild(progressBarSublineDecrease);
    return interactionBlocker;
  }
  getWatermark(bbox) {
    const bboxWidth = bbox.maxX - bbox.minX;
    const width = Math.max(200, bboxWidth * 0.3);
    const WATERMARK_SIZE = { width: 215, height: 25 };
    const height = WATERMARK_SIZE.height / WATERMARK_SIZE.width * width;
    const watermarkPadding = {
      x: bboxWidth * 0.02,
      y: bboxWidth * 0.014
    };
    bbox.maxY += height + watermarkPadding.y;
    const watermarkEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    watermarkEl.id = "watermark-ac";
    watermarkEl.style.transform = `translate(${bbox.minX + watermarkPadding.x}px, ${bbox.maxY - height - watermarkPadding.y}px)`;
    watermarkEl.setAttrs({
      viewBox: `0 0 ${WATERMARK_SIZE.width} ${WATERMARK_SIZE.height}`,
      width: width.toString(),
      fill: "currentColor"
    });
    watermarkEl.innerHTML = '<path d="M7 14.6a12 12 0 0 1 2.8-.6 10 10 0 0 1 .5-8.8l.4-.7a32.9 32.9 0 0 0 .9-2.3v-1c-.1-.4-.3-.7-.7-1.1-.6-.2-1.1 0-1.6.3L4.2 5.1c-.3.2-.5.6-.6 1l-.4 3a14.6 14.6 0 0 1 3.7 5.5Zm-4-4.2-.1.3-2.8 6c-.2.7-.1 1.4.4 1.9L4.8 23a8.7 8.7 0 0 0 .8-8.7c-.7-1.8-1.9-3.2-2.6-4Z"/><path d="M5.8 23.5H6a23.8 23.8 0 0 1 7.4 1.4c1.2.4 2.3-.5 2.5-1.7a7 7 0 0 1 .8-2.7c-.8-2-1.6-3.2-2.6-4a5 5 0 0 0-2.9-1.3c-1.6-.2-3 .2-4 .5.6 2.3.4 5-1.4 7.8Z"/><path d="m17.4 19.3 2-3c0-.4 0-.7-.2-1a18 18 0 0 1-2-3.5c-.7-1.4-.7-3.5-.8-4.6 0-.4 0-.7-.3-1l-3.4-4.3v.6L12 4l-.5 1-.3.6A11 11 0 0 0 10 9.4c0 1.3 0 2.8.9 4.7h.4c1.1.2 2.3.6 3.5 1.6 1 .8 1.8 2 2.5 3.6ZM39.8 4.5c-6 0-10.3 3.7-10.3 8.9 0 5.1 4.3 8.9 10.3 8.9 5.9 0 10.2-3.8 10.2-9 0-5-4.3-8.8-10.2-8.8Zm0 3.5c3.5 0 6.1 2.1 6.1 5.4 0 3.2-2.6 5.4-6.1 5.4-3.6 0-6.2-2.2-6.2-5.4 0-3.3 2.6-5.4 6.2-5.4Zm15.7 12.6c.8.9 2.5 1.7 4.6 1.7 4.3 0 6.8-3 6.8-6.6C67 12 64.4 9 60.1 9c-2.1 0-3.8.8-4.6 1.7v-6h-3.9V22h3.9v-1.4Zm-.1-5c0-2 1.7-3.4 3.9-3.4 2 0 3.9 1.2 3.9 3.5 0 2.2-1.8 3.5-4 3.5-2.1 0-3.8-1.4-3.8-3.4v-.2ZM67.3 20a11 11 0 0 0 7.2 2.3c4 0 7-1.5 7-4.4 0-3-2.9-3.5-6.1-3.8-2.8-.4-3.6-.4-3.6-1.1 0-.7.9-1 2.5-1 2 0 3.7.5 4.8 1.6l2-2.3A9.7 9.7 0 0 0 74.5 9c-4 0-6.5 1.7-6.5 4.3 0 2.7 2.5 3.3 5.6 3.7 2.8.3 4 .3 4 1.2 0 .8-1 1.1-2.8 1.1-2.2 0-4.1-.7-5.7-2l-1.8 2.5ZM82.8 8h4V4.9h-4V8Zm3.9 1.4h-3.8V22h3.8V9.4Zm13.1 11.2V22h3.9V4.8h-3.9v6C99 9.8 97.4 9 95.2 9c-4.3 0-6.8 3-6.8 6.6 0 3.6 2.5 6.6 6.8 6.6 2.2 0 3.8-.8 4.6-1.7Zm.1-5v.2c0 2-1.7 3.4-3.9 3.4-2 0-3.9-1.3-3.9-3.5 0-2.3 1.8-3.5 4-3.5 2.1 0 3.8 1.4 3.8 3.4ZM106 8h4V4.9h-4V8Zm3.9 1.4H106V22h3.9V9.4Zm7 12.9a8 8 0 0 0 5.2-1.7c.6 1.2 2.2 2 5 1.4v-2.8c-1.4.3-1.7 0-1.7-.7v-4.6c0-3.2-2.3-4.8-6.4-4.8-3.5 0-6.2 1.5-7 3.8l3.4 1c.4-1 1.7-1.8 3.5-1.8 2 0 2.8.8 2.8 1.7v.1l-5 .5c-3 .3-5.2 1.5-5.2 4 0 2.4 2.2 3.9 5.4 3.9Zm4.8-5.1c0 1.4-2.2 2.3-4.1 2.3-1.5 0-2.4-.5-2.4-1.3s.7-1.1 2-1.3l4.5-.4v.7Zm6.7 4.8h3.8v-6c0-2.2 1.2-3.5 3.3-3.5 2 0 3 1.3 3 3.4V22h3.8v-7.2c0-3.5-2.2-5.7-5.5-5.7-2 0-3.6.8-4.6 1.8V9.4h-3.8V22Z"/><path fill-rule="evenodd" stroke="currentColor" stroke-width="0.5px" d="M191.822 20.035A3.288 3.288 0 0 0 191.812 19.951A2.225 2.225 0 0 0 191.728 19.825A3.914 3.914 0 0 0 191.649 19.779A2.868 2.868 0 0 0 191.535 19.755Q191.388 19.755 191.329 19.85A9.99 9.99 0 0 0 191.248 20.005A9.059 9.059 0 0 0 191.234 20.042A20.576 20.576 0 0 1 191.04 20.456A15.291 15.291 0 0 1 190.688 20.854A13.176 13.176 0 0 1 190.086 21.116A18.046 18.046 0 0 1 189.778 21.141A16.538 16.538 0 0 1 189.506 21.117A22.39 22.39 0 0 1 189.246 21.057A12.53 12.53 0 0 1 188.825 20.838A15.259 15.259 0 0 1 188.718 20.746Q188.476 20.518 188.315 20.126A19.046 19.046 0 0 1 188.212 19.778Q188.177 19.602 188.163 19.395A41.406 41.406 0 0 1 188.154 19.118A38.021 38.021 0 0 1 188.176 18.694Q188.2 18.485 188.249 18.307A18.247 18.247 0 0 1 188.319 18.1A22.415 22.415 0 0 1 188.472 17.79Q188.57 17.628 188.687 17.506A13.277 13.277 0 0 1 188.732 17.463A15.164 15.164 0 0 1 189.051 17.229A13.142 13.142 0 0 1 189.274 17.134A20.296 20.296 0 0 1 189.592 17.058A16.216 16.216 0 0 1 189.834 17.039Q190.032 17.039 190.179 17.076A8.428 8.428 0 0 1 190.265 17.102A20.826 20.826 0 0 1 190.385 17.15Q190.443 17.176 190.492 17.203A10.532 10.532 0 0 1 190.548 17.235A13.851 13.851 0 0 1 190.796 17.429A16.001 16.001 0 0 1 190.849 17.484Q190.989 17.634 191.087 17.816A28.524 28.524 0 0 0 191.143 17.923A32.076 32.076 0 0 0 191.164 17.96A8.099 8.099 0 0 0 191.246 18.077A7.413 7.413 0 0 0 191.259 18.093A5.796 5.796 0 0 0 191.329 18.16A4.911 4.911 0 0 0 191.371 18.191A2.41 2.41 0 0 0 191.488 18.228A2.956 2.956 0 0 0 191.507 18.229A3.152 3.152 0 0 0 191.589 18.219A2.121 2.121 0 0 0 191.714 18.131A4.225 4.225 0 0 0 191.758 18.048A3.083 3.083 0 0 0 191.78 17.935A2.636 2.636 0 0 0 191.777 17.896Q191.77 17.853 191.751 17.792A15.123 15.123 0 0 0 191.735 17.743A12.471 12.471 0 0 0 191.685 17.624Q191.648 17.544 191.595 17.456Q191.506 17.308 191.365 17.148A29.801 29.801 0 0 0 191.346 17.127A16.118 16.118 0 0 0 191.103 16.907A19.423 19.423 0 0 0 190.972 16.819A20.302 20.302 0 0 0 190.685 16.673A25.928 25.928 0 0 0 190.461 16.591A19.442 19.442 0 0 0 190.11 16.518A25.565 25.565 0 0 0 189.806 16.5A24.519 24.519 0 0 0 189.175 16.579A21.58 21.58 0 0 0 188.868 16.686Q188.441 16.871 188.13 17.207A22.873 22.873 0 0 0 187.739 17.792A27.604 27.604 0 0 0 187.643 18.023A28.76 28.76 0 0 0 187.489 18.699A36.041 36.041 0 0 0 187.468 19.09Q187.468 19.657 187.65 20.189A25.617 25.617 0 0 0 187.925 20.762A21.934 21.934 0 0 0 188.182 21.085Q188.35 21.26 188.553 21.376Q188.756 21.491 188.973 21.558Q189.19 21.624 189.411 21.652Q189.631 21.68 189.841 21.68Q190.33 21.68 190.687 21.519A14.886 14.886 0 0 0 190.758 21.484A23.571 23.571 0 0 0 191.1 21.27A18.016 18.016 0 0 0 191.371 21.019A21.893 21.893 0 0 0 191.572 20.749A16.519 16.519 0 0 0 191.714 20.473A24.164 24.164 0 0 0 191.766 20.326Q191.812 20.183 191.82 20.08A5.511 5.511 0 0 0 191.822 20.035ZM210.729 20.539A10.256 10.256 0 0 0 210.709 20.331Q210.681 20.195 210.613 20.089A6.408 6.408 0 0 0 210.603 20.074Q210.477 19.888 210.285 19.769Q210.092 19.65 209.865 19.58Q209.637 19.51 209.427 19.468Q209.112 19.398 208.909 19.332Q208.769 19.286 208.67 19.234A8.38 8.38 0 0 1 208.591 19.188Q208.506 19.132 208.459 19.069A3.226 3.226 0 0 1 208.43 19.024A4.079 4.079 0 0 1 208.384 18.844A4.773 4.773 0 0 1 208.384 18.831Q208.384 18.614 208.563 18.485A6.016 6.016 0 0 1 208.611 18.453A9.288 9.288 0 0 1 208.884 18.349Q209.018 18.32 209.175 18.32A15.627 15.627 0 0 1 209.378 18.332Q209.479 18.346 209.562 18.373A6.348 6.348 0 0 1 209.714 18.446A8.886 8.886 0 0 1 209.849 18.555Q209.934 18.64 209.98 18.739A5.694 5.694 0 0 1 209.98 18.74A21.387 21.387 0 0 0 210.014 18.808Q210.051 18.879 210.082 18.923A4.301 4.301 0 0 0 210.103 18.95A1.554 1.554 0 0 0 210.167 18.995Q210.193 19.005 210.226 19.01A4.094 4.094 0 0 0 210.281 19.013Q210.34 19.013 210.381 19A1.678 1.678 0 0 0 210.421 18.982A2.886 2.886 0 0 0 210.467 18.945A2.322 2.322 0 0 0 210.498 18.908A2.481 2.481 0 0 0 210.521 18.867A1.908 1.908 0 0 0 210.533 18.824Q210.539 18.79 210.54 18.77A1.657 1.657 0 0 0 210.54 18.761Q210.54 18.719 210.503 18.605A25.229 25.229 0 0 0 210.491 18.569Q210.451 18.451 210.345 18.329A12.347 12.347 0 0 0 210.295 18.275A9.871 9.871 0 0 0 210.149 18.153Q210.034 18.074 209.879 18.005A12.077 12.077 0 0 0 209.651 17.933Q209.534 17.907 209.395 17.895A28.523 28.523 0 0 0 209.161 17.886Q208.923 17.886 208.736 17.922A15.474 15.474 0 0 0 208.692 17.932A15.47 15.47 0 0 0 208.491 17.991A11.956 11.956 0 0 0 208.335 18.061Q208.083 18.201 207.943 18.439A10.425 10.425 0 0 0 207.835 18.69A8.678 8.678 0 0 0 207.803 18.922A8.458 8.458 0 0 0 207.827 19.126A6.855 6.855 0 0 0 207.898 19.304A10.914 10.914 0 0 0 208.01 19.466A8.765 8.765 0 0 0 208.118 19.573A13.929 13.929 0 0 0 208.273 19.684A12.994 12.994 0 0 0 208.276 19.685A9.406 9.406 0 0 0 208.342 19.722Q208.379 19.741 208.423 19.759A19.716 19.716 0 0 0 208.482 19.783Q208.569 19.817 208.683 19.854A61.793 61.793 0 0 0 208.794 19.888Q208.975 19.943 209.246 20.011A175.761 175.761 0 0 0 209.259 20.014Q209.392 20.049 209.543 20.091A11.079 11.079 0 0 1 209.724 20.159A9.425 9.425 0 0 1 209.812 20.207Q209.931 20.28 210.011 20.389A3.951 3.951 0 0 1 210.079 20.538Q210.092 20.597 210.092 20.665A4.625 4.625 0 0 1 209.993 20.949A7.213 7.213 0 0 1 209.879 21.068A6.795 6.795 0 0 1 209.657 21.188Q209.485 21.246 209.245 21.246Q208.982 21.246 208.8 21.18A6.716 6.716 0 0 1 208.611 21.078Q208.398 20.91 208.293 20.665A7.271 7.271 0 0 0 208.241 20.564A5.814 5.814 0 0 0 208.192 20.497A1.892 1.892 0 0 0 208.095 20.437Q208.064 20.429 208.027 20.427A3.869 3.869 0 0 0 208.013 20.427A2.684 2.684 0 0 0 207.902 20.45A2.607 2.607 0 0 0 207.824 20.504A2.794 2.794 0 0 0 207.773 20.573A2.276 2.276 0 0 0 207.747 20.679Q207.747 20.868 207.849 21.029A18.825 18.825 0 0 0 207.938 21.159Q207.996 21.236 208.055 21.295A13.024 13.024 0 0 0 208.518 21.581A14.764 14.764 0 0 0 208.521 21.582A14.51 14.51 0 0 0 208.761 21.645Q208.883 21.667 209.025 21.675A30.93 30.93 0 0 0 209.203 21.68Q209.548 21.68 209.798 21.62A13.282 13.282 0 0 0 210.019 21.547A17.031 17.031 0 0 0 210.226 21.44Q210.382 21.345 210.481 21.229A11.064 11.064 0 0 0 210.597 21.065A7.971 7.971 0 0 0 210.684 20.851A23.38 23.38 0 0 0 210.707 20.737Q210.728 20.629 210.729 20.55A6.492 6.492 0 0 0 210.729 20.539ZM149.486 20.469A20.565 20.565 0 0 0 149.509 20.43Q149.535 20.385 149.569 20.321A90.19 90.19 0 0 0 149.602 20.259Q149.675 20.119 149.749 19.962A37.1 37.1 0 0 0 149.84 19.75A31.562 31.562 0 0 0 149.874 19.657A12.867 12.867 0 0 0 149.902 19.573Q149.927 19.482 149.927 19.419Q149.927 19.3 149.843 19.22Q149.759 19.139 149.64 19.139A2.273 2.273 0 0 0 149.482 19.204Q149.434 19.248 149.395 19.321A14.995 14.995 0 0 0 149.383 19.346Q149.363 19.388 149.329 19.465Q149.283 19.566 149.231 19.682Q149.178 19.797 149.133 19.899A99.51 99.51 0 0 1 149.111 19.946Q149.082 20.011 149.066 20.042Q148.968 19.938 148.891 19.851A68.292 68.292 0 0 1 148.881 19.839A1775.331 1775.331 0 0 1 148.805 19.754Q148.754 19.696 148.695 19.629A79.458 79.458 0 0 1 148.64 19.566Q148.552 19.465 148.44 19.328A466.983 466.983 0 0 1 148.358 19.23Q148.246 19.093 148.095 18.907A1543.854 1543.854 0 0 1 148.044 18.845Q148.506 18.6 148.8 18.303A10.277 10.277 0 0 0 149.021 17.98A9.508 9.508 0 0 0 149.094 17.606Q149.094 17.473 149.047 17.311A16.194 16.194 0 0 0 149.031 17.26A10.364 10.364 0 0 0 148.872 16.958A12.188 12.188 0 0 0 148.825 16.899A10.761 10.761 0 0 0 148.611 16.71A13.818 13.818 0 0 0 148.45 16.616Q148.241 16.511 147.945 16.501A18.707 18.707 0 0 0 147.883 16.5A15.973 15.973 0 0 0 147.614 16.522A11.629 11.629 0 0 0 147.334 16.605A13.053 13.053 0 0 0 147.095 16.742A10.73 10.73 0 0 0 146.945 16.875A10.831 10.831 0 0 0 146.739 17.186A10.281 10.281 0 0 0 146.718 17.239A11.786 11.786 0 0 0 146.65 17.524A10.583 10.583 0 0 0 146.644 17.634A10.236 10.236 0 0 0 146.703 17.97A13.462 13.462 0 0 0 146.788 18.163A32.918 32.918 0 0 0 146.933 18.407Q147.038 18.57 147.176 18.747Q146.98 18.852 146.773 18.985Q146.567 19.118 146.396 19.29Q146.224 19.461 146.112 19.689A10.729 10.729 0 0 0 146.016 20.001A14.179 14.179 0 0 0 146 20.217A12.88 12.88 0 0 0 146.038 20.523A16.369 16.369 0 0 0 146.098 20.714A13.928 13.928 0 0 0 146.374 21.16A15.957 15.957 0 0 0 146.399 21.187A14.894 14.894 0 0 0 146.71 21.434A18.905 18.905 0 0 0 146.914 21.54A15.13 15.13 0 0 0 147.256 21.645Q147.417 21.675 147.602 21.679A26.164 26.164 0 0 0 147.659 21.68Q147.953 21.68 148.181 21.614A20.209 20.209 0 0 0 148.417 21.529A15.925 15.925 0 0 0 148.583 21.446A16.498 16.498 0 0 0 148.784 21.309A13.958 13.958 0 0 0 148.888 21.218A33.819 33.819 0 0 0 149.023 21.079A26.636 26.636 0 0 0 149.115 20.973L149.266 21.124Q149.338 21.196 149.462 21.307A122.767 122.767 0 0 0 149.542 21.379Q149.655 21.479 149.734 21.54A17.292 17.292 0 0 0 149.752 21.554Q149.821 21.606 149.869 21.634A5.331 5.331 0 0 0 149.889 21.645A4.838 4.838 0 0 0 149.916 21.659Q149.95 21.674 149.973 21.677A5.79 5.79 0 0 0 150.011 21.68A4.806 4.806 0 0 0 150.032 21.68A2.48 2.48 0 0 0 150.12 21.663Q150.153 21.651 150.188 21.629A5.246 5.246 0 0 0 150.225 21.603Q150.326 21.526 150.326 21.372A2.778 2.778 0 0 0 150.319 21.308A2.031 2.031 0 0 0 150.284 21.232A5.843 5.843 0 0 0 150.256 21.198Q150.214 21.15 150.144 21.085A119.856 119.856 0 0 1 150.081 21.031Q150.004 20.966 149.948 20.916A37.409 37.409 0 0 1 149.931 20.9Q149.85 20.826 149.784 20.767Q149.717 20.707 149.654 20.641A52.27 52.27 0 0 0 149.62 20.605Q149.565 20.548 149.486 20.469ZM172.432 21.183L172.432 19.279A51.556 51.556 0 0 0 172.432 19.211Q172.431 19.149 172.429 19.073A18.916 18.916 0 0 0 172.409 18.853A21.017 21.017 0 0 0 172.404 18.821Q172.383 18.691 172.334 18.565Q172.285 18.439 172.194 18.334Q172.005 18.117 171.757 18.002A12.033 12.033 0 0 0 171.449 17.908A16.569 16.569 0 0 0 171.172 17.886A16.106 16.106 0 0 0 170.97 17.898Q170.864 17.912 170.773 17.94A9.472 9.472 0 0 0 170.745 17.949Q170.563 18.012 170.427 18.107A14.499 14.499 0 0 0 170.272 18.229A12 12 0 0 0 170.192 18.31Q170.094 18.418 170.024 18.509A59.749 59.749 0 0 0 170.024 18.43Q170.023 18.399 170.022 18.372A28.64 28.64 0 0 0 170.021 18.32Q170.018 18.267 170.016 18.224A56.933 56.933 0 0 0 170.014 18.187Q170.01 18.131 169.996 18.093Q169.982 18.054 169.961 18.019A1.527 1.527 0 0 0 169.935 17.979Q169.912 17.952 169.874 17.928A2.736 2.736 0 0 0 169.733 17.886A3.297 3.297 0 0 0 169.723 17.886A3.514 3.514 0 0 0 169.666 17.891Q169.625 17.897 169.594 17.914A7.268 7.268 0 0 0 169.552 17.938Q169.533 17.95 169.517 17.962A3.925 3.925 0 0 0 169.506 17.97A2.17 2.17 0 0 0 169.455 18.054Q169.444 18.085 169.44 18.122A3.872 3.872 0 0 0 169.44 18.124A16 16 0 0 0 169.433 18.203Q169.431 18.239 169.43 18.281A34.968 34.968 0 0 0 169.429 18.369L169.429 21.183Q169.429 21.294 169.434 21.378A16.104 16.104 0 0 0 169.44 21.439A2.976 2.976 0 0 0 169.455 21.508A2.094 2.094 0 0 0 169.513 21.596A3.572 3.572 0 0 0 169.615 21.658A3.123 3.123 0 0 0 169.73 21.68Q169.849 21.68 169.947 21.596A2.227 2.227 0 0 0 170.009 21.503A3.192 3.192 0 0 0 170.024 21.439Q170.034 21.366 170.037 21.265A29.244 29.244 0 0 0 170.038 21.183L170.038 20.028A136.267 136.267 0 0 1 170.039 19.891Q170.041 19.696 170.049 19.559Q170.059 19.377 170.08 19.258A13.249 13.249 0 0 1 170.096 19.182Q170.112 19.114 170.133 19.066Q170.164 18.992 170.206 18.922Q170.332 18.691 170.57 18.555Q170.808 18.418 171.074 18.418Q171.263 18.418 171.438 18.506Q171.613 18.593 171.711 18.768A5.723 5.723 0 0 1 171.751 18.856Q171.768 18.902 171.781 18.956A12.31 12.31 0 0 1 171.795 19.027Q171.823 19.188 171.823 19.454L171.823 21.183Q171.823 21.294 171.828 21.378A16.104 16.104 0 0 0 171.834 21.439A2.976 2.976 0 0 0 171.849 21.508A2.094 2.094 0 0 0 171.907 21.596A3.143 3.143 0 0 0 172.114 21.68A4.083 4.083 0 0 0 172.131 21.68Q172.25 21.68 172.348 21.596A2.116 2.116 0 0 0 172.409 21.5A3.049 3.049 0 0 0 172.422 21.439A17.011 17.011 0 0 0 172.428 21.359Q172.432 21.282 172.432 21.183ZM199.648 21.183L199.648 19.279A51.556 51.556 0 0 0 199.648 19.211Q199.647 19.149 199.645 19.073A18.916 18.916 0 0 0 199.625 18.853A21.017 21.017 0 0 0 199.62 18.821Q199.599 18.691 199.55 18.565Q199.501 18.439 199.41 18.334Q199.221 18.117 198.973 18.002A12.033 12.033 0 0 0 198.665 17.908A16.569 16.569 0 0 0 198.388 17.886A16.106 16.106 0 0 0 198.186 17.898Q198.08 17.912 197.989 17.94A9.472 9.472 0 0 0 197.961 17.949Q197.779 18.012 197.643 18.107A14.499 14.499 0 0 0 197.488 18.229A12 12 0 0 0 197.408 18.31Q197.31 18.418 197.24 18.509A59.749 59.749 0 0 0 197.24 18.43Q197.239 18.399 197.238 18.372A28.64 28.64 0 0 0 197.237 18.32Q197.234 18.267 197.232 18.224A56.933 56.933 0 0 0 197.23 18.187Q197.226 18.131 197.212 18.093Q197.198 18.054 197.177 18.019A1.527 1.527 0 0 0 197.151 17.979Q197.128 17.952 197.09 17.928A2.736 2.736 0 0 0 196.949 17.886A3.297 3.297 0 0 0 196.939 17.886A3.514 3.514 0 0 0 196.882 17.891Q196.841 17.897 196.81 17.914A7.268 7.268 0 0 0 196.768 17.938Q196.749 17.95 196.733 17.962A3.925 3.925 0 0 0 196.722 17.97A2.17 2.17 0 0 0 196.671 18.054Q196.66 18.085 196.656 18.122A3.872 3.872 0 0 0 196.656 18.124A16 16 0 0 0 196.649 18.203Q196.647 18.239 196.646 18.281A34.968 34.968 0 0 0 196.645 18.369L196.645 21.183Q196.645 21.294 196.65 21.378A16.104 16.104 0 0 0 196.656 21.439A2.976 2.976 0 0 0 196.671 21.508A2.094 2.094 0 0 0 196.729 21.596A3.572 3.572 0 0 0 196.831 21.658A3.123 3.123 0 0 0 196.946 21.68Q197.065 21.68 197.163 21.596A2.227 2.227 0 0 0 197.225 21.503A3.192 3.192 0 0 0 197.24 21.439Q197.25 21.366 197.253 21.265A29.244 29.244 0 0 0 197.254 21.183L197.254 20.028A136.267 136.267 0 0 1 197.255 19.891Q197.257 19.696 197.265 19.559Q197.275 19.377 197.296 19.258A13.249 13.249 0 0 1 197.312 19.182Q197.328 19.114 197.349 19.066Q197.38 18.992 197.422 18.922Q197.548 18.691 197.786 18.555Q198.024 18.418 198.29 18.418Q198.479 18.418 198.654 18.506Q198.829 18.593 198.927 18.768A5.723 5.723 0 0 1 198.967 18.856Q198.984 18.902 198.997 18.956A12.31 12.31 0 0 1 199.011 19.027Q199.039 19.188 199.039 19.454L199.039 21.183Q199.039 21.294 199.044 21.378A16.104 16.104 0 0 0 199.05 21.439A2.976 2.976 0 0 0 199.065 21.508A2.094 2.094 0 0 0 199.123 21.596A3.143 3.143 0 0 0 199.33 21.68A4.083 4.083 0 0 0 199.347 21.68Q199.466 21.68 199.564 21.596A2.116 2.116 0 0 0 199.625 21.5A3.049 3.049 0 0 0 199.638 21.439A17.011 17.011 0 0 0 199.644 21.359Q199.648 21.282 199.648 21.183ZM176.534 20.532A3.117 3.117 0 0 0 176.522 20.444A2.336 2.336 0 0 0 176.443 20.326A3.647 3.647 0 0 0 176.368 20.278A2.675 2.675 0 0 0 176.254 20.252Q176.195 20.252 176.154 20.273A1.457 1.457 0 0 0 176.132 20.287Q176.086 20.322 176.051 20.364A0.884 0.884 0 0 0 176.043 20.373Q176.031 20.389 176.013 20.424Q175.988 20.469 175.96 20.522A19.281 19.281 0 0 1 175.922 20.59A16.39 16.39 0 0 1 175.904 20.62Q175.877 20.664 175.869 20.685A0.832 0.832 0 0 0 175.869 20.686Q175.729 20.938 175.481 21.071Q175.232 21.204 174.938 21.204A11.312 11.312 0 0 1 174.567 21.146A9.482 9.482 0 0 1 174.123 20.823A12.884 12.884 0 0 1 173.909 20.4Q173.825 20.123 173.825 19.762Q173.825 19.265 174.005 18.929A11.724 11.724 0 0 1 174.133 18.737A9.92 9.92 0 0 1 174.858 18.365A13.208 13.208 0 0 1 174.952 18.362Q175.149 18.362 175.297 18.41A7.63 7.63 0 0 1 175.348 18.429A11.407 11.407 0 0 1 175.489 18.497A8.28 8.28 0 0 1 175.621 18.59Q175.729 18.684 175.796 18.782A95.188 95.188 0 0 1 175.827 18.828Q175.857 18.874 175.879 18.908A25.527 25.527 0 0 1 175.897 18.936Q175.946 19.027 175.988 19.076Q176.018 19.111 176.044 19.133A2.786 2.786 0 0 0 176.065 19.15Q176.093 19.169 176.119 19.175A1.041 1.041 0 0 0 176.132 19.178A5.79 5.79 0 0 0 176.17 19.181A4.806 4.806 0 0 0 176.191 19.181A2.779 2.779 0 0 0 176.373 19.113A3.543 3.543 0 0 0 176.38 19.107A2.405 2.405 0 0 0 176.464 18.932A3.241 3.241 0 0 0 176.464 18.915Q176.464 18.824 176.426 18.737A9.75 9.75 0 0 0 176.371 18.629A8.253 8.253 0 0 0 176.338 18.579A16.685 16.685 0 0 0 176.24 18.444A21.161 21.161 0 0 0 176.149 18.338A11.789 11.789 0 0 0 175.991 18.194A14.908 14.908 0 0 0 175.873 18.114A13.143 13.143 0 0 0 175.68 18.017A17.807 17.807 0 0 0 175.481 17.949A16.11 16.11 0 0 0 175.266 17.906Q175.155 17.891 175.03 17.887A28.494 28.494 0 0 0 174.945 17.886Q174.427 17.886 174.095 18.065A15.968 15.968 0 0 0 173.748 18.312A13.867 13.867 0 0 0 173.566 18.523Q173.37 18.803 173.293 19.139Q173.216 19.475 173.216 19.79A33.884 33.884 0 0 0 173.227 20.07Q173.238 20.204 173.26 20.318A15.789 15.789 0 0 0 173.297 20.466Q173.357 20.666 173.426 20.811A13.31 13.31 0 0 0 173.475 20.903A12.835 12.835 0 0 0 173.544 21.018Q173.583 21.076 173.631 21.136A21.776 21.776 0 0 0 173.647 21.155A11.374 11.374 0 0 0 173.779 21.291A15.635 15.635 0 0 0 173.93 21.407Q174.105 21.526 174.361 21.603A16.697 16.697 0 0 0 174.604 21.656Q174.772 21.68 174.973 21.68Q175.414 21.68 175.712 21.53A20.695 20.695 0 0 0 175.965 21.379A14.971 14.971 0 0 0 176.195 21.183A16.44 16.44 0 0 0 176.327 21.025Q176.39 20.939 176.432 20.854A8.98 8.98 0 0 0 176.457 20.798Q176.53 20.62 176.534 20.541A1.736 1.736 0 0 0 176.534 20.532ZM153.742 20.147L155.975 20.147L156.423 21.253Q156.492 21.426 156.55 21.53A10.295 10.295 0 0 0 156.574 21.572A2.117 2.117 0 0 0 156.693 21.663Q156.745 21.68 156.815 21.68A3.648 3.648 0 0 0 156.926 21.664A2.913 2.913 0 0 0 157.06 21.575Q157.151 21.47 157.151 21.372Q157.151 21.295 157.127 21.218Q157.108 21.159 157.071 21.067A48.599 48.599 0 0 0 157.046 21.008L155.401 17.088Q155.352 16.969 155.293 16.833A12.279 12.279 0 0 0 155.259 16.762Q155.217 16.68 155.177 16.64A3.694 3.694 0 0 0 155.091 16.569A4.791 4.791 0 0 0 155.03 16.539Q154.939 16.5 154.841 16.5A6.137 6.137 0 0 0 154.757 16.506Q154.692 16.515 154.642 16.539A4.007 4.007 0 0 0 154.506 16.645A4.629 4.629 0 0 0 154.502 16.651Q154.448 16.717 154.403 16.814A11.485 11.485 0 0 0 154.393 16.836Q154.344 16.948 154.288 17.095L152.699 21.036A43.035 43.035 0 0 0 152.676 21.093Q152.648 21.163 152.631 21.215A13.482 13.482 0 0 0 152.626 21.232Q152.601 21.309 152.601 21.386Q152.601 21.49 152.69 21.583A4.693 4.693 0 0 0 152.692 21.586A3.044 3.044 0 0 0 152.91 21.68A4.049 4.049 0 0 0 152.923 21.68A4.561 4.561 0 0 0 153.008 21.673Q153.057 21.663 153.093 21.643A1.998 1.998 0 0 0 153.165 21.575A9.744 9.744 0 0 0 153.207 21.5Q153.248 21.42 153.293 21.305A35.447 35.447 0 0 0 153.308 21.267L153.742 20.147ZM167.875 21.085A156.604 156.604 0 0 0 167.919 21.236A174.514 174.514 0 0 0 167.935 21.288A9.866 9.866 0 0 0 167.991 21.434A8.679 8.679 0 0 0 168.015 21.481Q168.064 21.568 168.127 21.624Q168.19 21.68 168.281 21.68Q168.4 21.68 168.488 21.607A2.978 2.978 0 0 0 168.535 21.557Q168.567 21.514 168.573 21.467A1.833 1.833 0 0 0 168.575 21.442A5.283 5.283 0 0 0 168.567 21.354Q168.559 21.307 168.544 21.257A113.074 113.074 0 0 1 168.533 21.221Q168.511 21.15 168.505 21.127A9.687 9.687 0 0 1 168.48 21.059Q168.468 21.022 168.456 20.98A21.964 21.964 0 0 1 168.439 20.91Q168.412 20.799 168.408 20.605A35.786 35.786 0 0 1 168.407 20.525Q168.407 20.467 168.41 20.329A329.739 329.739 0 0 1 168.411 20.305Q168.414 20.147 168.418 19.965Q168.421 19.783 168.425 19.619A290.299 290.299 0 0 0 168.426 19.542Q168.428 19.437 168.428 19.384A50.037 50.037 0 0 0 168.415 19.009A40.849 40.849 0 0 0 168.393 18.796A10.854 10.854 0 0 0 168.312 18.497A9.517 9.517 0 0 0 168.211 18.32Q168.064 18.117 167.767 18.002Q167.472 17.887 166.962 17.886A46.251 46.251 0 0 0 166.951 17.886Q166.608 17.886 166.356 17.946A13.799 13.799 0 0 0 166.15 18.012Q165.917 18.108 165.764 18.227A10.032 10.032 0 0 0 165.677 18.303Q165.509 18.467 165.453 18.632A19.398 19.398 0 0 0 165.429 18.708Q165.407 18.782 165.4 18.833A3.082 3.082 0 0 0 165.397 18.873A2.545 2.545 0 0 0 165.417 18.975A2.432 2.432 0 0 0 165.478 19.059Q165.558 19.132 165.67 19.132Q165.76 19.132 165.815 19.096A1.703 1.703 0 0 0 165.866 19.045Q165.922 18.957 165.971 18.838Q166.027 18.698 166.136 18.6Q166.244 18.502 166.381 18.443A11.745 11.745 0 0 1 166.636 18.365A13.202 13.202 0 0 1 166.675 18.359Q166.832 18.334 166.993 18.334Q167.28 18.334 167.443 18.404A5.36 5.36 0 0 1 167.459 18.411A6.24 6.24 0 0 1 167.586 18.489A4.723 4.723 0 0 1 167.7 18.621A7.268 7.268 0 0 1 167.786 18.86A8.677 8.677 0 0 1 167.795 18.922A52.243 52.243 0 0 1 167.815 19.182A58.488 58.488 0 0 1 167.819 19.272A58.673 58.673 0 0 1 167.762 19.292Q167.682 19.319 167.627 19.335Q167.553 19.356 167.476 19.377Q167.425 19.39 167.299 19.415A131.725 131.725 0 0 1 167.277 19.419Q167.133 19.447 166.972 19.475A202.132 202.132 0 0 0 166.738 19.517A176.919 176.919 0 0 0 166.664 19.531A182.46 182.46 0 0 0 166.596 19.544Q166.511 19.561 166.465 19.571A21.283 21.283 0 0 0 166.454 19.573A87.884 87.884 0 0 0 166.304 19.607Q166.228 19.625 166.144 19.646A150.463 150.463 0 0 0 166.087 19.661Q165.88 19.713 165.698 19.829A9.901 9.901 0 0 0 165.422 20.092A11.635 11.635 0 0 0 165.39 20.14Q165.28 20.312 165.266 20.569A14.069 14.069 0 0 0 165.264 20.644Q165.264 20.833 165.334 21.019Q165.404 21.204 165.551 21.351A10.381 10.381 0 0 0 165.782 21.523A13.004 13.004 0 0 0 165.919 21.589A11.509 11.509 0 0 0 166.167 21.659Q166.282 21.678 166.413 21.68A19.554 19.554 0 0 0 166.44 21.68A22.373 22.373 0 0 0 166.719 21.663A16.841 16.841 0 0 0 166.969 21.614Q167.203 21.547 167.382 21.453A21.631 21.631 0 0 0 167.527 21.369Q167.6 21.322 167.661 21.274A12.71 12.71 0 0 0 167.683 21.257A45.541 45.541 0 0 0 167.754 21.196Q167.825 21.135 167.872 21.088A13.449 13.449 0 0 0 167.875 21.085ZM195.091 21.085A156.604 156.604 0 0 0 195.135 21.236A174.514 174.514 0 0 0 195.151 21.288A9.866 9.866 0 0 0 195.207 21.434A8.679 8.679 0 0 0 195.231 21.481Q195.28 21.568 195.343 21.624Q195.406 21.68 195.497 21.68Q195.616 21.68 195.704 21.607A2.978 2.978 0 0 0 195.751 21.557Q195.783 21.514 195.789 21.467A1.833 1.833 0 0 0 195.791 21.442A5.283 5.283 0 0 0 195.783 21.354Q195.775 21.307 195.76 21.257A113.074 113.074 0 0 1 195.749 21.221Q195.727 21.15 195.721 21.127A9.687 9.687 0 0 1 195.696 21.059Q195.684 21.022 195.672 20.98A21.964 21.964 0 0 1 195.655 20.91Q195.628 20.799 195.624 20.605A35.786 35.786 0 0 1 195.623 20.525Q195.623 20.467 195.626 20.329A329.739 329.739 0 0 1 195.627 20.305Q195.63 20.147 195.634 19.965Q195.637 19.783 195.641 19.619A290.299 290.299 0 0 0 195.642 19.542Q195.644 19.437 195.644 19.384A50.037 50.037 0 0 0 195.631 19.009A40.849 40.849 0 0 0 195.609 18.796A10.854 10.854 0 0 0 195.528 18.497A9.517 9.517 0 0 0 195.427 18.32Q195.28 18.117 194.983 18.002Q194.688 17.887 194.178 17.886A46.251 46.251 0 0 0 194.167 17.886Q193.824 17.886 193.572 17.946A13.799 13.799 0 0 0 193.366 18.012Q193.133 18.108 192.98 18.227A10.032 10.032 0 0 0 192.893 18.303Q192.725 18.467 192.669 18.632A19.398 19.398 0 0 0 192.645 18.708Q192.623 18.782 192.616 18.833A3.082 3.082 0 0 0 192.613 18.873A2.545 2.545 0 0 0 192.633 18.975A2.432 2.432 0 0 0 192.694 19.059Q192.774 19.132 192.886 19.132Q192.976 19.132 193.031 19.096A1.703 1.703 0 0 0 193.082 19.045Q193.138 18.957 193.187 18.838Q193.243 18.698 193.352 18.6Q193.46 18.502 193.597 18.443A11.745 11.745 0 0 1 193.852 18.365A13.202 13.202 0 0 1 193.891 18.359Q194.048 18.334 194.209 18.334Q194.496 18.334 194.659 18.404A5.36 5.36 0 0 1 194.675 18.411A6.24 6.24 0 0 1 194.802 18.489A4.723 4.723 0 0 1 194.916 18.621A7.268 7.268 0 0 1 195.002 18.86A8.677 8.677 0 0 1 195.011 18.922A52.243 52.243 0 0 1 195.031 19.182A58.488 58.488 0 0 1 195.035 19.272A58.673 58.673 0 0 1 194.978 19.292Q194.898 19.319 194.843 19.335Q194.769 19.356 194.692 19.377Q194.641 19.39 194.515 19.415A131.725 131.725 0 0 1 194.493 19.419Q194.349 19.447 194.188 19.475A202.132 202.132 0 0 0 193.954 19.517A176.919 176.919 0 0 0 193.88 19.531A182.46 182.46 0 0 0 193.812 19.544Q193.727 19.561 193.681 19.571A21.283 21.283 0 0 0 193.67 19.573A87.884 87.884 0 0 0 193.52 19.607Q193.444 19.625 193.36 19.646A150.463 150.463 0 0 0 193.303 19.661Q193.096 19.713 192.914 19.829A9.901 9.901 0 0 0 192.638 20.092A11.635 11.635 0 0 0 192.606 20.14Q192.496 20.312 192.482 20.569A14.069 14.069 0 0 0 192.48 20.644Q192.48 20.833 192.55 21.019Q192.62 21.204 192.767 21.351A10.381 10.381 0 0 0 192.998 21.523A13.004 13.004 0 0 0 193.135 21.589A11.509 11.509 0 0 0 193.383 21.659Q193.498 21.678 193.629 21.68A19.554 19.554 0 0 0 193.656 21.68A22.373 22.373 0 0 0 193.935 21.663A16.841 16.841 0 0 0 194.185 21.614Q194.419 21.547 194.598 21.453A21.631 21.631 0 0 0 194.743 21.369Q194.816 21.322 194.877 21.274A12.71 12.71 0 0 0 194.899 21.257A45.541 45.541 0 0 0 194.97 21.196Q195.041 21.135 195.088 21.088A13.449 13.449 0 0 0 195.091 21.085ZM206.424 21.085A156.604 156.604 0 0 0 206.468 21.236A174.514 174.514 0 0 0 206.484 21.288A9.866 9.866 0 0 0 206.54 21.434A8.679 8.679 0 0 0 206.564 21.481Q206.613 21.568 206.676 21.624Q206.739 21.68 206.83 21.68Q206.949 21.68 207.037 21.607A2.978 2.978 0 0 0 207.084 21.557Q207.116 21.514 207.122 21.467A1.833 1.833 0 0 0 207.124 21.442A5.283 5.283 0 0 0 207.116 21.354Q207.108 21.307 207.093 21.257A113.074 113.074 0 0 1 207.082 21.221Q207.06 21.15 207.054 21.127A9.687 9.687 0 0 1 207.029 21.059Q207.017 21.022 207.005 20.98A21.964 21.964 0 0 1 206.988 20.91Q206.961 20.799 206.957 20.605A35.786 35.786 0 0 1 206.956 20.525Q206.956 20.467 206.959 20.329A329.739 329.739 0 0 1 206.96 20.305Q206.963 20.147 206.967 19.965Q206.97 19.783 206.974 19.619A290.299 290.299 0 0 0 206.975 19.542Q206.977 19.437 206.977 19.384A50.037 50.037 0 0 0 206.964 19.009A40.849 40.849 0 0 0 206.942 18.796A10.854 10.854 0 0 0 206.861 18.497A9.517 9.517 0 0 0 206.76 18.32Q206.613 18.117 206.316 18.002Q206.021 17.887 205.511 17.886A46.251 46.251 0 0 0 205.5 17.886Q205.157 17.886 204.905 17.946A13.799 13.799 0 0 0 204.699 18.012Q204.466 18.108 204.313 18.227A10.032 10.032 0 0 0 204.226 18.303Q204.058 18.467 204.002 18.632A19.398 19.398 0 0 0 203.978 18.708Q203.956 18.782 203.949 18.833A3.082 3.082 0 0 0 203.946 18.873A2.545 2.545 0 0 0 203.966 18.975A2.432 2.432 0 0 0 204.027 19.059Q204.107 19.132 204.219 19.132Q204.309 19.132 204.364 19.096A1.703 1.703 0 0 0 204.415 19.045Q204.471 18.957 204.52 18.838Q204.576 18.698 204.685 18.6Q204.793 18.502 204.93 18.443A11.745 11.745 0 0 1 205.185 18.365A13.202 13.202 0 0 1 205.224 18.359Q205.381 18.334 205.542 18.334Q205.829 18.334 205.992 18.404A5.36 5.36 0 0 1 206.008 18.411A6.24 6.24 0 0 1 206.135 18.489A4.723 4.723 0 0 1 206.249 18.621A7.268 7.268 0 0 1 206.335 18.86A8.677 8.677 0 0 1 206.344 18.922A52.243 52.243 0 0 1 206.364 19.182A58.488 58.488 0 0 1 206.368 19.272A58.673 58.673 0 0 1 206.311 19.292Q206.231 19.319 206.176 19.335Q206.102 19.356 206.025 19.377Q205.974 19.39 205.848 19.415A131.725 131.725 0 0 1 205.826 19.419Q205.682 19.447 205.521 19.475A202.132 202.132 0 0 0 205.287 19.517A176.919 176.919 0 0 0 205.213 19.531A182.46 182.46 0 0 0 205.145 19.544Q205.06 19.561 205.014 19.571A21.283 21.283 0 0 0 205.003 19.573A87.884 87.884 0 0 0 204.853 19.607Q204.777 19.625 204.693 19.646A150.463 150.463 0 0 0 204.636 19.661Q204.429 19.713 204.247 19.829A9.901 9.901 0 0 0 203.971 20.092A11.635 11.635 0 0 0 203.939 20.14Q203.829 20.312 203.815 20.569A14.069 14.069 0 0 0 203.813 20.644Q203.813 20.833 203.883 21.019Q203.953 21.204 204.1 21.351A10.381 10.381 0 0 0 204.331 21.523A13.004 13.004 0 0 0 204.468 21.589A11.509 11.509 0 0 0 204.716 21.659Q204.831 21.678 204.962 21.68A19.554 19.554 0 0 0 204.989 21.68A22.373 22.373 0 0 0 205.268 21.663A16.841 16.841 0 0 0 205.518 21.614Q205.752 21.547 205.931 21.453A21.631 21.631 0 0 0 206.076 21.369Q206.149 21.322 206.21 21.274A12.71 12.71 0 0 0 206.232 21.257A45.541 45.541 0 0 0 206.303 21.196Q206.374 21.135 206.421 21.088A13.449 13.449 0 0 0 206.424 21.085ZM177.794 19.902L179.971 19.902Q180.062 19.902 180.157 19.895Q180.251 19.888 180.332 19.853Q180.412 19.818 180.461 19.738Q180.51 19.657 180.51 19.51A8.226 8.226 0 0 0 180.508 19.461Q180.504 19.391 180.489 19.283A16.26 16.26 0 0 0 180.46 19.129Q180.442 19.055 180.416 18.973A26.778 26.778 0 0 0 180.409 18.95A17.318 17.318 0 0 0 180.314 18.72A21.358 21.358 0 0 0 180.237 18.579A13.255 13.255 0 0 0 179.998 18.285A15.344 15.344 0 0 0 179.943 18.236Q179.761 18.082 179.502 17.984A14.303 14.303 0 0 0 179.228 17.912Q179.1 17.891 178.954 17.887A24.995 24.995 0 0 0 178.886 17.886Q178.461 17.886 178.146 18.023A13.781 13.781 0 0 0 178.123 18.033Q177.801 18.18 177.591 18.436A16.765 16.765 0 0 0 177.319 18.911A19.805 19.805 0 0 0 177.276 19.038A24.845 24.845 0 0 0 177.174 19.655A28.502 28.502 0 0 0 177.171 19.776Q177.171 20.175 177.273 20.525Q177.374 20.875 177.588 21.131Q177.801 21.386 178.127 21.533A15.933 15.933 0 0 0 178.507 21.648Q178.679 21.678 178.874 21.68A26.983 26.983 0 0 0 178.9 21.68A24.666 24.666 0 0 0 179.157 21.667Q179.289 21.654 179.401 21.625A13.385 13.385 0 0 0 179.478 21.603A19.788 19.788 0 0 0 179.675 21.529Q179.793 21.477 179.887 21.414Q180.055 21.302 180.157 21.176Q180.234 21.08 180.289 21A17.945 17.945 0 0 0 180.321 20.952A14.683 14.683 0 0 0 180.372 20.859A17.681 17.681 0 0 0 180.402 20.798Q180.44 20.714 180.44 20.63A2.258 2.258 0 0 0 180.378 20.474A2.928 2.928 0 0 0 180.374 20.469A2.17 2.17 0 0 0 180.216 20.399A2.698 2.698 0 0 0 180.209 20.399Q180.097 20.399 180.041 20.466A26.352 26.352 0 0 0 180.002 20.513Q179.987 20.532 179.974 20.549A12.788 12.788 0 0 0 179.95 20.581Q179.873 20.7 179.786 20.819Q179.698 20.938 179.583 21.029A8.961 8.961 0 0 1 179.405 21.137A11.002 11.002 0 0 1 179.31 21.176Q179.152 21.232 178.928 21.232Q178.711 21.232 178.512 21.162A9.41 9.41 0 0 1 178.157 20.937A10.742 10.742 0 0 1 178.155 20.935A10.148 10.148 0 0 1 177.998 20.73A13.99 13.99 0 0 1 177.899 20.522A14.497 14.497 0 0 1 177.829 20.268Q177.806 20.143 177.798 20A26.817 26.817 0 0 1 177.794 19.902ZM160.49 21.05L160.49 21.267A12.437 12.437 0 0 0 160.49 21.29Q160.491 21.321 160.493 21.362A37.885 37.885 0 0 0 160.494 21.376A4.031 4.031 0 0 0 160.51 21.469A4.921 4.921 0 0 0 160.525 21.512Q160.553 21.582 160.613 21.631Q160.66 21.67 160.74 21.678A4.425 4.425 0 0 0 160.784 21.68A3.105 3.105 0 0 0 160.895 21.661A2.79 2.79 0 0 0 160.994 21.596A2.175 2.175 0 0 0 161.062 21.48A2.923 2.923 0 0 0 161.068 21.446A13.958 13.958 0 0 0 161.073 21.381Q161.078 21.305 161.078 21.204A44.546 44.546 0 0 0 161.078 21.197L161.078 17.004A44.226 44.226 0 0 0 161.076 16.874A49.881 49.881 0 0 0 161.075 16.829A5.027 5.027 0 0 0 161.062 16.732A4.139 4.139 0 0 0 161.04 16.665Q161.008 16.591 160.945 16.546Q160.882 16.5 160.77 16.5Q160.694 16.5 160.641 16.521A2.056 2.056 0 0 0 160.595 16.546Q160.532 16.591 160.504 16.665A4.762 4.762 0 0 0 160.478 16.766A5.995 5.995 0 0 0 160.473 16.829Q160.469 16.92 160.469 17.011L160.469 18.516A2.504 2.504 0 0 0 160.457 18.498Q160.428 18.459 160.357 18.38A10.281 10.281 0 0 0 160.279 18.302Q160.234 18.262 160.179 18.22A21.748 21.748 0 0 0 160.105 18.166Q159.944 18.054 159.717 17.97A13.078 13.078 0 0 0 159.455 17.905Q159.329 17.886 159.188 17.886Q158.801 17.886 158.524 18.01A11.161 11.161 0 0 0 158.411 18.068A15.529 15.529 0 0 0 158.032 18.379A14.284 14.284 0 0 0 157.918 18.527Q157.732 18.803 157.659 19.132A31.593 31.593 0 0 0 157.6 19.484A24.8 24.8 0 0 0 157.585 19.748A25.356 25.356 0 0 0 157.6 20.012Q157.616 20.17 157.652 20.347Q157.718 20.679 157.9 20.977Q158.082 21.274 158.394 21.477A11.715 11.715 0 0 0 158.748 21.629Q158.901 21.667 159.08 21.677A22.684 22.684 0 0 0 159.202 21.68A19.893 19.893 0 0 0 159.439 21.667Q159.56 21.652 159.664 21.622A11.619 11.619 0 0 0 159.703 21.61Q159.916 21.54 160.07 21.442Q160.224 21.344 160.326 21.236Q160.425 21.13 160.487 21.054A20.372 20.372 0 0 0 160.49 21.05ZM184.01 21.05L184.01 21.267A12.437 12.437 0 0 0 184.01 21.29Q184.011 21.321 184.013 21.362A37.885 37.885 0 0 0 184.014 21.376A4.031 4.031 0 0 0 184.03 21.469A4.921 4.921 0 0 0 184.045 21.512Q184.073 21.582 184.133 21.631Q184.18 21.67 184.26 21.678A4.425 4.425 0 0 0 184.304 21.68A3.105 3.105 0 0 0 184.415 21.661A2.79 2.79 0 0 0 184.514 21.596A2.175 2.175 0 0 0 184.582 21.48A2.923 2.923 0 0 0 184.588 21.446A13.958 13.958 0 0 0 184.593 21.381Q184.598 21.305 184.598 21.204A44.546 44.546 0 0 0 184.598 21.197L184.598 17.004A44.226 44.226 0 0 0 184.596 16.874A49.881 49.881 0 0 0 184.595 16.829A5.027 5.027 0 0 0 184.582 16.732A4.139 4.139 0 0 0 184.56 16.665Q184.528 16.591 184.465 16.546Q184.402 16.5 184.29 16.5Q184.214 16.5 184.161 16.521A2.056 2.056 0 0 0 184.115 16.546Q184.052 16.591 184.024 16.665A4.762 4.762 0 0 0 183.998 16.766A5.995 5.995 0 0 0 183.993 16.829Q183.989 16.92 183.989 17.011L183.989 18.516A2.504 2.504 0 0 0 183.977 18.498Q183.948 18.459 183.877 18.38A10.281 10.281 0 0 0 183.799 18.302Q183.754 18.262 183.699 18.22A21.748 21.748 0 0 0 183.625 18.166Q183.464 18.054 183.237 17.97A13.078 13.078 0 0 0 182.975 17.905Q182.849 17.886 182.708 17.886Q182.321 17.886 182.044 18.01A11.161 11.161 0 0 0 181.931 18.068A15.529 15.529 0 0 0 181.552 18.379A14.284 14.284 0 0 0 181.438 18.527Q181.252 18.803 181.179 19.132A31.593 31.593 0 0 0 181.12 19.484A24.8 24.8 0 0 0 181.105 19.748A25.356 25.356 0 0 0 181.12 20.012Q181.136 20.17 181.172 20.347Q181.238 20.679 181.42 20.977Q181.602 21.274 181.914 21.477A11.715 11.715 0 0 0 182.268 21.629Q182.421 21.667 182.6 21.677A22.684 22.684 0 0 0 182.722 21.68A19.893 19.893 0 0 0 182.959 21.667Q183.08 21.652 183.184 21.622A11.619 11.619 0 0 0 183.223 21.61Q183.436 21.54 183.59 21.442Q183.744 21.344 183.846 21.236Q183.945 21.13 184.007 21.054A20.372 20.372 0 0 0 184.01 21.05ZM164.312 18.264L163.325 20.903L162.331 18.264Q162.271 18.09 162.211 17.993A6.228 6.228 0 0 0 162.205 17.984A2.038 2.038 0 0 0 162.077 17.895Q162.04 17.886 161.995 17.886Q161.848 17.886 161.771 17.977A4.199 4.199 0 0 0 161.73 18.035Q161.694 18.095 161.694 18.152A4.814 4.814 0 0 0 161.699 18.22A3.684 3.684 0 0 0 161.712 18.278A9.92 9.92 0 0 0 161.723 18.311Q161.738 18.355 161.766 18.427A84.529 84.529 0 0 0 161.785 18.474L162.891 21.232Q162.952 21.378 163.01 21.483A12.632 12.632 0 0 0 163.056 21.558A2.582 2.582 0 0 0 163.21 21.666Q163.255 21.678 163.309 21.68A5.626 5.626 0 0 0 163.325 21.68A4.256 4.256 0 0 0 163.424 21.669A2.598 2.598 0 0 0 163.588 21.558Q163.646 21.469 163.706 21.338A30.263 30.263 0 0 0 163.752 21.232L164.858 18.474A79.208 79.208 0 0 0 164.883 18.411Q164.913 18.334 164.927 18.292A7.545 7.545 0 0 0 164.932 18.278A3.771 3.771 0 0 0 164.946 18.209A4.945 4.945 0 0 0 164.949 18.152Q164.949 18.097 164.909 18.032A4.909 4.909 0 0 0 164.872 17.981A2.49 2.49 0 0 0 164.723 17.893A3.793 3.793 0 0 0 164.648 17.886A3.446 3.446 0 0 0 164.568 17.895Q164.513 17.908 164.475 17.941A2.009 2.009 0 0 0 164.438 17.984A7.867 7.867 0 0 0 164.399 18.054Q164.358 18.133 164.318 18.248A26.244 26.244 0 0 0 164.312 18.264ZM202.861 18.264L201.874 20.903L200.88 18.264Q200.82 18.09 200.76 17.993A6.228 6.228 0 0 0 200.754 17.984A2.038 2.038 0 0 0 200.626 17.895Q200.589 17.886 200.544 17.886Q200.397 17.886 200.32 17.977A4.199 4.199 0 0 0 200.279 18.035Q200.243 18.095 200.243 18.152A4.814 4.814 0 0 0 200.248 18.22A3.684 3.684 0 0 0 200.261 18.278A9.92 9.92 0 0 0 200.272 18.311Q200.287 18.355 200.315 18.427A84.529 84.529 0 0 0 200.334 18.474L201.44 21.232Q201.501 21.378 201.559 21.483A12.632 12.632 0 0 0 201.605 21.558A2.582 2.582 0 0 0 201.759 21.666Q201.804 21.678 201.858 21.68A5.626 5.626 0 0 0 201.874 21.68A4.256 4.256 0 0 0 201.973 21.669A2.598 2.598 0 0 0 202.137 21.558Q202.195 21.469 202.255 21.338A30.263 30.263 0 0 0 202.301 21.232L203.407 18.474A79.208 79.208 0 0 0 203.432 18.411Q203.462 18.334 203.476 18.292A7.545 7.545 0 0 0 203.481 18.278A3.771 3.771 0 0 0 203.495 18.209A4.945 4.945 0 0 0 203.498 18.152Q203.498 18.097 203.458 18.032A4.909 4.909 0 0 0 203.421 17.981A2.49 2.49 0 0 0 203.272 17.893A3.793 3.793 0 0 0 203.197 17.886A3.446 3.446 0 0 0 203.117 17.895Q203.062 17.908 203.024 17.941A2.009 2.009 0 0 0 202.987 17.984A7.867 7.867 0 0 0 202.948 18.054Q202.907 18.133 202.867 18.248A26.244 26.244 0 0 0 202.861 18.264ZM159.076 21.172A9.992 9.992 0 0 0 159.321 21.204Q159.524 21.204 159.731 21.134A9.14 9.14 0 0 0 159.831 21.093A9.773 9.773 0 0 0 160.102 20.9A10.93 10.93 0 0 0 160.26 20.697A14.782 14.782 0 0 0 160.371 20.473Q160.437 20.309 160.461 20.094A24.26 24.26 0 0 0 160.476 19.818Q160.476 19.356 160.343 19.073A18.755 18.755 0 0 0 160.322 19.029Q160.196 18.777 160.025 18.632A12.131 12.131 0 0 0 159.883 18.528A9.354 9.354 0 0 0 159.64 18.418A19.608 19.608 0 0 0 159.535 18.392Q159.402 18.362 159.3 18.362A11.478 11.478 0 0 0 159.207 18.366A9.465 9.465 0 0 0 158.845 18.467Q158.642 18.572 158.499 18.761Q158.355 18.95 158.282 19.206A17.995 17.995 0 0 0 158.242 19.373A20.798 20.798 0 0 0 158.208 19.755A27.923 27.923 0 0 0 158.211 19.894Q158.227 20.21 158.317 20.438Q158.371 20.576 158.438 20.688A11.647 11.647 0 0 0 158.586 20.886Q158.747 21.057 158.943 21.131A12.502 12.502 0 0 0 159.076 21.172ZM182.596 21.172A9.992 9.992 0 0 0 182.841 21.204Q183.044 21.204 183.251 21.134A9.14 9.14 0 0 0 183.351 21.093A9.773 9.773 0 0 0 183.622 20.9A10.93 10.93 0 0 0 183.78 20.697A14.782 14.782 0 0 0 183.891 20.473Q183.957 20.309 183.981 20.094A24.26 24.26 0 0 0 183.996 19.818Q183.996 19.356 183.863 19.073A18.755 18.755 0 0 0 183.842 19.029Q183.716 18.777 183.545 18.632A12.131 12.131 0 0 0 183.403 18.528A9.354 9.354 0 0 0 183.16 18.418A19.608 19.608 0 0 0 183.055 18.392Q182.922 18.362 182.82 18.362A11.478 11.478 0 0 0 182.727 18.366A9.465 9.465 0 0 0 182.365 18.467Q182.162 18.572 182.019 18.761Q181.875 18.95 181.802 19.206A17.995 17.995 0 0 0 181.762 19.373A20.798 20.798 0 0 0 181.728 19.755A27.923 27.923 0 0 0 181.731 19.894Q181.747 20.21 181.837 20.438Q181.891 20.576 181.958 20.688A11.647 11.647 0 0 0 182.106 20.886Q182.267 21.057 182.463 21.131A12.502 12.502 0 0 0 182.596 21.172ZM155.772 19.608L153.931 19.608L154.834 17.263L155.772 19.608ZM147.704 19.422A124.043 124.043 0 0 0 147.736 19.461A187.068 187.068 0 0 0 147.977 19.75A209.521 209.521 0 0 0 148.062 19.85Q148.233 20.049 148.405 20.238A280.942 280.942 0 0 0 148.527 20.372Q148.575 20.424 148.619 20.471A146.266 146.266 0 0 0 148.702 20.56A32.139 32.139 0 0 1 148.675 20.594Q148.644 20.633 148.602 20.683A116.402 116.402 0 0 1 148.562 20.732A12.456 12.456 0 0 1 148.433 20.863A15.356 15.356 0 0 1 148.342 20.938A13.22 13.22 0 0 1 148.163 21.051A16.357 16.357 0 0 1 148.041 21.11Q147.869 21.183 147.652 21.183A9.592 9.592 0 0 1 147.375 21.144A8.638 8.638 0 0 1 147.243 21.092Q147.057 21.001 146.921 20.854Q146.784 20.707 146.711 20.525Q146.637 20.343 146.637 20.154A8.237 8.237 0 0 1 146.655 19.979A6.431 6.431 0 0 1 146.711 19.822A10.984 10.984 0 0 1 146.895 19.564A12.277 12.277 0 0 1 146.903 19.556A14.899 14.899 0 0 1 147.147 19.354A16.679 16.679 0 0 1 147.176 19.335Q147.33 19.237 147.491 19.146Q147.572 19.259 147.704 19.422ZM167.826 19.706Q167.826 20.014 167.802 20.256A11.719 11.719 0 0 1 167.753 20.495A9.411 9.411 0 0 1 167.672 20.679Q167.595 20.819 167.469 20.917Q167.343 21.015 167.193 21.078Q167.042 21.141 166.885 21.173A16.044 16.044 0 0 1 166.654 21.202A14.108 14.108 0 0 1 166.58 21.204A10.041 10.041 0 0 1 166.436 21.194Q166.337 21.18 166.258 21.145A7.637 7.637 0 0 1 166.147 21.084A5.572 5.572 0 0 1 166.045 20.998A5.836 5.836 0 0 1 165.939 20.839A5.49 5.49 0 0 1 165.926 20.805A6.447 6.447 0 0 1 165.894 20.689A5.291 5.291 0 0 1 165.887 20.602Q165.887 20.553 165.905 20.476A4.746 4.746 0 0 1 165.935 20.386A6.234 6.234 0 0 1 165.971 20.319A4.921 4.921 0 0 1 166.039 20.232A6.678 6.678 0 0 1 166.111 20.168A5.299 5.299 0 0 1 166.201 20.113Q166.247 20.091 166.302 20.072A10.661 10.661 0 0 1 166.356 20.056A55.947 55.947 0 0 1 166.613 19.996A71.051 71.051 0 0 1 166.832 19.955A87.518 87.518 0 0 0 167.072 19.909Q167.194 19.884 167.329 19.853A147.944 147.944 0 0 0 167.42 19.832Q167.527 19.805 167.61 19.781A25.521 25.521 0 0 0 167.658 19.766A34.628 34.628 0 0 0 167.728 19.742Q167.762 19.731 167.792 19.719A18.009 18.009 0 0 0 167.826 19.706ZM195.042 19.706Q195.042 20.014 195.018 20.256A11.719 11.719 0 0 1 194.969 20.495A9.411 9.411 0 0 1 194.888 20.679Q194.811 20.819 194.685 20.917Q194.559 21.015 194.409 21.078Q194.258 21.141 194.101 21.173A16.044 16.044 0 0 1 193.87 21.202A14.108 14.108 0 0 1 193.796 21.204A10.041 10.041 0 0 1 193.652 21.194Q193.553 21.18 193.474 21.145A7.637 7.637 0 0 1 193.363 21.084A5.572 5.572 0 0 1 193.261 20.998A5.836 5.836 0 0 1 193.155 20.839A5.49 5.49 0 0 1 193.142 20.805A6.447 6.447 0 0 1 193.11 20.689A5.291 5.291 0 0 1 193.103 20.602Q193.103 20.553 193.121 20.476A4.746 4.746 0 0 1 193.151 20.386A6.234 6.234 0 0 1 193.187 20.319A4.921 4.921 0 0 1 193.255 20.232A6.678 6.678 0 0 1 193.327 20.168A5.299 5.299 0 0 1 193.417 20.113Q193.463 20.091 193.518 20.072A10.661 10.661 0 0 1 193.572 20.056A55.947 55.947 0 0 1 193.829 19.996A71.051 71.051 0 0 1 194.048 19.955A87.518 87.518 0 0 0 194.288 19.909Q194.41 19.884 194.545 19.853A147.944 147.944 0 0 0 194.636 19.832Q194.743 19.805 194.826 19.781A25.521 25.521 0 0 0 194.874 19.766A34.628 34.628 0 0 0 194.944 19.742Q194.978 19.731 195.008 19.719A18.009 18.009 0 0 0 195.042 19.706ZM206.375 19.706Q206.375 20.014 206.351 20.256A11.719 11.719 0 0 1 206.302 20.495A9.411 9.411 0 0 1 206.221 20.679Q206.144 20.819 206.018 20.917Q205.892 21.015 205.742 21.078Q205.591 21.141 205.434 21.173A16.044 16.044 0 0 1 205.203 21.202A14.108 14.108 0 0 1 205.129 21.204A10.041 10.041 0 0 1 204.985 21.194Q204.886 21.18 204.807 21.145A7.637 7.637 0 0 1 204.696 21.084A5.572 5.572 0 0 1 204.594 20.998A5.836 5.836 0 0 1 204.488 20.839A5.49 5.49 0 0 1 204.475 20.805A6.447 6.447 0 0 1 204.443 20.689A5.291 5.291 0 0 1 204.436 20.602Q204.436 20.553 204.454 20.476A4.746 4.746 0 0 1 204.484 20.386A6.234 6.234 0 0 1 204.52 20.319A4.921 4.921 0 0 1 204.588 20.232A6.678 6.678 0 0 1 204.66 20.168A5.299 5.299 0 0 1 204.75 20.113Q204.796 20.091 204.851 20.072A10.661 10.661 0 0 1 204.905 20.056A55.947 55.947 0 0 1 205.162 19.996A71.051 71.051 0 0 1 205.381 19.955A87.518 87.518 0 0 0 205.621 19.909Q205.743 19.884 205.878 19.853A147.944 147.944 0 0 0 205.969 19.832Q206.076 19.805 206.159 19.781A25.521 25.521 0 0 0 206.207 19.766A34.628 34.628 0 0 0 206.277 19.742Q206.311 19.731 206.341 19.719A18.009 18.009 0 0 0 206.375 19.706ZM179.887 19.482L177.801 19.482A28.069 28.069 0 0 1 177.814 19.387Q177.827 19.303 177.847 19.202A10.166 10.166 0 0 1 177.92 18.976A12.334 12.334 0 0 1 177.969 18.88Q178.018 18.789 178.095 18.691A8.296 8.296 0 0 1 178.243 18.546A9.616 9.616 0 0 1 178.284 18.516Q178.396 18.439 178.54 18.387Q178.683 18.334 178.865 18.334Q179.166 18.334 179.376 18.464Q179.586 18.593 179.705 18.796A14.726 14.726 0 0 1 179.78 18.94Q179.813 19.014 179.831 19.082A7.079 7.079 0 0 1 179.849 19.164A57.705 57.705 0 0 1 179.879 19.404A51.717 51.717 0 0 1 179.887 19.482ZM147.722 18.46A5.735 5.735 0 0 0 147.714 18.448Q147.698 18.426 147.666 18.383A159.702 159.702 0 0 1 147.622 18.324A204.664 204.664 0 0 1 147.575 18.261Q147.526 18.194 147.484 18.138A28.341 28.341 0 0 0 147.469 18.119Q147.447 18.089 147.435 18.075A1.535 1.535 0 0 0 147.428 18.068Q147.372 17.991 147.313 17.854A6.976 6.976 0 0 1 147.255 17.63A6.56 6.56 0 0 1 147.253 17.578Q147.253 17.466 147.299 17.357Q147.344 17.249 147.428 17.165A6.428 6.428 0 0 1 147.588 17.049A7.566 7.566 0 0 1 147.631 17.029A6.258 6.258 0 0 1 147.86 16.977A7.29 7.29 0 0 1 147.89 16.976A6.985 6.985 0 0 1 148.099 17.006A5.558 5.558 0 0 1 148.342 17.158Q148.513 17.34 148.513 17.585A5.96 5.96 0 0 1 148.479 17.788A5.572 5.572 0 0 1 148.45 17.854A8.463 8.463 0 0 1 148.321 18.039A9.906 9.906 0 0 1 148.279 18.082A14.839 14.839 0 0 1 148.113 18.22A17.789 17.789 0 0 1 148.027 18.278A67.874 67.874 0 0 1 147.801 18.415A77.011 77.011 0 0 1 147.722 18.46Z"/>';
    return watermarkEl;
  }
};

// src/canvas-extensions/floating-edge-canvas-extension.ts
var FloatingEdgeCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "floatingEdgeFeatureEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:data-loaded:after",
      (canvas, data, setData) => this.onLoadData(canvas, data)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-moved",
      (canvas, node) => this.onNodeMoved(canvas, node)
    ));
    if (this.plugin.settings.getSetting("allowFloatingEdgeCreation")) {
      this.plugin.registerEvent(this.plugin.app.workspace.on(
        "advanced-canvas:edge-connection-dragging:before",
        (canvas, edge, event, newEdge, side) => this.onEdgeStartedDragging(canvas, edge, event, newEdge, side)
      ));
    }
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:edge-connection-dragging:after",
      (canvas, edge, event, newEdge, side) => this.onEdgeStoppedDragging(canvas, edge, event, newEdge, side)
    ));
  }
  onLoadData(canvas, data) {
    for (const edgeData of data.edges) {
      const edge = canvas.edges.get(edgeData.id);
      if (!edge) return console.warn("Imported edge is not yet loaded :(");
      this.updateEdgeConnectionSide(edge);
    }
  }
  onNodeMoved(canvas, node) {
    const affectedEdges = canvas.getEdgesForNode(node);
    for (const edge of affectedEdges)
      this.updateEdgeConnectionSide(edge);
  }
  updateEdgeConnectionSide(edge) {
    const edgeData = edge.getData();
    if (edgeData.fromFloating) {
      const fixedNodeConnectionPoint = BBoxHelper.getCenterOfBBoxSide(edge.to.node.getBBox(), edge.to.side);
      const bestSide = CanvasHelper.getBestSideForFloatingEdge(fixedNodeConnectionPoint, edge.from.node);
      if (bestSide !== edge.from.side) {
        edge.setData({
          ...edgeData,
          fromSide: bestSide
        });
      }
    }
    if (edgeData.toFloating) {
      const fixedNodeConnectionPoint = BBoxHelper.getCenterOfBBoxSide(edge.from.node.getBBox(), edge.from.side);
      const bestSide = CanvasHelper.getBestSideForFloatingEdge(fixedNodeConnectionPoint, edge.to.node);
      if (bestSide !== edge.to.side) {
        edge.setData({
          ...edgeData,
          toSide: bestSide
        });
      }
    }
  }
  onEdgeStartedDragging(canvas, edge, _event, newEdge, _side) {
    if (newEdge && this.plugin.settings.getSetting("newEdgeFromSideFloating")) edge.setData({
      ...edge.getData(),
      fromFloating: true
      // New edges can only get dragged from the "from" side
    });
    let cachedViewportNodes = null;
    let hasNaNFloatingEdgeDropZones = false;
    this.onPointerMove = (event) => {
      if (cachedViewportNodes === null || hasNaNFloatingEdgeDropZones || canvas.viewportChanged) {
        hasNaNFloatingEdgeDropZones = false;
        cachedViewportNodes = canvas.getViewportNodes().map((node) => {
          const nodeFloatingEdgeDropZone = this.getFloatingEdgeDropZoneForNode(node);
          if (isNaN(nodeFloatingEdgeDropZone.minX) || isNaN(nodeFloatingEdgeDropZone.minY) || isNaN(nodeFloatingEdgeDropZone.maxX) || isNaN(nodeFloatingEdgeDropZone.maxY))
            hasNaNFloatingEdgeDropZones = true;
          return [node, nodeFloatingEdgeDropZone];
        });
      }
      for (const [node, nodeFloatingEdgeDropZoneClientRect] of cachedViewportNodes) {
        const hovering = BBoxHelper.insideBBox({ x: event.clientX, y: event.clientY }, nodeFloatingEdgeDropZoneClientRect, true);
        node.nodeEl.classList.toggle("hovering-floating-edge-zone", hovering);
      }
    };
    document.addEventListener("pointermove", this.onPointerMove);
  }
  onEdgeStoppedDragging(_canvas, edge, event, _newEdge, side) {
    document.removeEventListener("pointermove", this.onPointerMove);
    const dropZoneNode = side === "from" ? edge.from.node : edge.to.node;
    const floatingEdgeDropZone = this.getFloatingEdgeDropZoneForNode(dropZoneNode);
    const wasDroppedInFloatingEdgeDropZone = this.plugin.settings.getSetting("allowFloatingEdgeCreation") ? BBoxHelper.insideBBox({ x: event.clientX, y: event.clientY }, floatingEdgeDropZone, true) : false;
    const edgeData = edge.getData();
    if (side === "from" && wasDroppedInFloatingEdgeDropZone == edgeData.fromFloating) return;
    if (side === "to" && wasDroppedInFloatingEdgeDropZone == edgeData.toFloating) return;
    if (side === "from") edgeData.fromFloating = wasDroppedInFloatingEdgeDropZone;
    else edgeData.toFloating = wasDroppedInFloatingEdgeDropZone;
    edge.setData(edgeData);
    this.updateEdgeConnectionSide(edge);
  }
  getFloatingEdgeDropZoneForNode(node) {
    const nodeElClientBoundingRect = node.nodeEl.getBoundingClientRect();
    const nodeFloatingEdgeDropZoneElStyle = window.getComputedStyle(node.nodeEl, ":after");
    const nodeFloatingEdgeDropZoneSize = {
      width: parseFloat(nodeFloatingEdgeDropZoneElStyle.getPropertyValue("width")),
      height: parseFloat(nodeFloatingEdgeDropZoneElStyle.getPropertyValue("height"))
    };
    return {
      minX: nodeElClientBoundingRect.left + (nodeElClientBoundingRect.width - nodeFloatingEdgeDropZoneSize.width) / 2,
      minY: nodeElClientBoundingRect.top + (nodeElClientBoundingRect.height - nodeFloatingEdgeDropZoneSize.height) / 2,
      maxX: nodeElClientBoundingRect.right - (nodeElClientBoundingRect.width - nodeFloatingEdgeDropZoneSize.width) / 2,
      maxY: nodeElClientBoundingRect.bottom - (nodeElClientBoundingRect.height - nodeFloatingEdgeDropZoneSize.height) / 2
    };
  }
};

// src/canvas-extensions/edge-highlight-canvas-extension.ts
var EdgeHighlightCanvasExtension = class extends CanvasExtension {
  isEnabled() {
    return "edgeHighlightEnabled";
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:selection-changed",
      (canvas, oldSelection, updateSelection) => this.onSelectionChanged(canvas, oldSelection)
    ));
  }
  onSelectionChanged(canvas, oldSelection) {
    var _a, _b, _c, _d, _e;
    const connectedEdgesToBeHighlighted = new Set(canvas.getSelectionData().nodes.flatMap((nodeData) => {
      var _a2, _b2;
      return [
        ...(_a2 = canvas.edgeFrom.get(canvas.nodes.get(nodeData.id))) != null ? _a2 : [],
        ...this.plugin.settings.getSetting("highlightIncomingEdges") ? (_b2 = canvas.edgeTo.get(canvas.nodes.get(nodeData.id))) != null ? _b2 : [] : []
      ];
    }));
    for (const edge of canvas.edges.values()) {
      const isFocused = canvas.selection.has(edge) || connectedEdgesToBeHighlighted.has(edge);
      edge.lineGroupEl.classList.toggle("is-focused", isFocused);
      (_b = (_a = edge.lineEndGroupEl) == null ? void 0 : _a.classList) == null ? void 0 : _b.toggle("is-focused", isFocused);
      (_e = (_d = (_c = edge.labelElement) == null ? void 0 : _c.textareaEl) == null ? void 0 : _d.classList) == null ? void 0 : _e.toggle("is-focused", isFocused);
    }
  }
};

// src/canvas-extensions/dataset-exposers/canvas-metadata-exposer.ts
var CanvasMetadataExposerExtension = class extends CanvasExtension {
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-metadata-changed",
      (canvas) => this.updateExposedSettings(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.updateExposedSettings(canvas)
    ));
  }
  updateExposedSettings(canvas) {
    const startNodeId = canvas.metadata["startNode"];
    for (const [nodeId, node] of canvas.nodes) {
      if (nodeId === startNodeId) node.nodeEl.dataset.isStartNode = "true";
      else delete node.nodeEl.dataset.isStartNode;
    }
  }
};

// src/canvas-extensions/dataset-exposers/node-exposer.ts
var CANVAS_NODE_IFRAME_BODY_CLASS = "canvas-node-iframe-body";
function getExposedNodeData(settings) {
  const exposedData = [];
  if (settings.getSetting("nodeStylingFeatureEnabled")) exposedData.push("styleAttributes");
  if (settings.getSetting("collapsibleGroupsFeatureEnabled")) exposedData.push("collapsed");
  if (settings.getSetting("portalsFeatureEnabled")) exposedData.push("isPortalLoaded");
  return exposedData;
}
var NodeExposerExtension = class extends CanvasExtension {
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-changed",
      (_canvas, node) => {
        var _a, _b;
        const nodeData = node == null ? void 0 : node.getData();
        if (!nodeData) return;
        this.setDataAttributes(node.nodeEl, nodeData);
        const iframe = (_b = (_a = node.nodeEl.querySelector("iframe")) == null ? void 0 : _a.contentDocument) == null ? void 0 : _b.body;
        if (iframe) this.setDataAttributes(iframe, nodeData);
      }
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:node-editing-state-changed",
      (_canvas, node, editing) => {
        var _a, _b;
        if (!editing) return;
        const nodeData = node.getData();
        if (!nodeData) return;
        const iframe = (_b = (_a = node.nodeEl.querySelector("iframe")) == null ? void 0 : _a.contentDocument) == null ? void 0 : _b.body;
        if (!iframe) return;
        iframe.classList.add(CANVAS_NODE_IFRAME_BODY_CLASS);
        new MutationObserver(() => iframe.classList.toggle(CANVAS_NODE_IFRAME_BODY_CLASS, true)).observe(iframe, { attributes: true, attributeFilter: ["class"] });
        this.setDataAttributes(iframe, nodeData);
      }
    ));
  }
  setDataAttributes(element, nodeData) {
    for (const exposedDataKey of getExposedNodeData(this.plugin.settings)) {
      const datasetPairs = nodeData[exposedDataKey] && typeof nodeData[exposedDataKey] === "object" ? Object.entries(nodeData[exposedDataKey]) : [[exposedDataKey, nodeData[exposedDataKey]]];
      for (const [key, value] of datasetPairs) {
        if (!value) delete element.dataset[key];
        else element.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(
      "advanced-canvas:node-interaction",
      (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;
          }
        }
        if (PortalsCanvasExtension.isPortalElement(node.id)) interactionEl.dataset.isFromPortal = "true";
        else delete interactionEl.dataset.isFromPortal;
      }
    ));
  }
};

// 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(
      "advanced-canvas:edge-changed",
      (_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] && typeof edgeData[exposedDataKey] === "object" ? Object.entries(edgeData[exposedDataKey]) : [[exposedDataKey, edgeData[exposedDataKey]]];
          for (const [key, value] of datasetPairs) {
            const stringifiedKey = key == null ? void 0 : key.toString();
            if (!stringifiedKey) continue;
            if (!value) {
              delete edge.path.display.dataset[stringifiedKey];
              if ((_a = edge.fromLineEnd) == null ? void 0 : _a.el) delete edge.fromLineEnd.el.dataset[stringifiedKey];
              if ((_b = edge.toLineEnd) == null ? void 0 : _b.el) delete edge.toLineEnd.el.dataset[stringifiedKey];
            } else {
              edge.path.display.dataset[stringifiedKey] = value.toString();
              if ((_c = edge.fromLineEnd) == null ? void 0 : _c.el) edge.fromLineEnd.el.dataset[stringifiedKey] = value.toString();
              if ((_d = edge.toLineEnd) == null ? void 0 : _d.el) edge.toLineEnd.el.dataset[stringifiedKey] = value.toString();
            }
          }
        }
      }
    ));
  }
};

// src/canvas-extensions/dataset-exposers/canvas-wrapper-exposer.ts
var EXPOSED_SETTINGS = [
  "disableFontSizeRelativeToZoom",
  "hideBackgroundGridWhenInReadonly",
  "collapsibleGroupsFeatureEnabled",
  "collapsedGroupPreviewOnDrag",
  "allowFloatingEdgeCreation"
];
var CanvasWrapperExposerExtension = class extends CanvasExtension {
  isEnabled() {
    return true;
  }
  init() {
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:settings-changed",
      () => this.updateExposedSettings(this.plugin.getCurrentCanvas())
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:canvas-changed",
      (canvas) => this.updateExposedSettings(canvas)
    ));
    this.plugin.registerEvent(this.plugin.app.workspace.on(
      "advanced-canvas:dragging-state-changed",
      (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/patchers/bases-table-view-patcher.ts
var BasesTableViewPatcher = class extends Patcher {
  async patch() {
    if (!this.plugin.settings.getSetting("canvasMetadataCompatibilityEnabled")) return;
    const bases = this.plugin.app.internalPlugins.getEnabledPluginById("bases");
    if (!bases) return;
    this.patchViewFactory(bases);
  }
  async patchViewFactory(bases) {
    const that = this;
    await Patcher.patchOnce(this.plugin, bases.registrations.table, (resolve) => ({
      factory: Patcher.OverrideExisting((next) => function(...args) {
        const view = next.call(this, ...args);
        that.patchTableView(view);
        resolve(view);
        return view;
      })
    }));
  }
  async patchTableView(basesView) {
    const that = this;
    await Patcher.patchOnce(this.plugin, basesView, (resolve) => ({
      updateVirtualDisplay: Patcher.OverrideExisting((next) => function(...args) {
        const result = next.call(this, ...args);
        if (this.rows.length > 0) {
          const row = this.rows.first();
          that.patchTableRow(row);
          resolve(row);
        }
        return result;
      })
    }));
  }
  async patchTableRow(row) {
    const that = this;
    await Patcher.patchOnce(this.plugin, row, (resolve) => ({
      render: Patcher.OverrideExisting((next) => function(...args) {
        let result = next.call(this, ...args);
        if (this.cells.length > 0) {
          const cell = this.cells.first();
          that.patchTableCell(cell);
          resolve(cell);
          result = next.call(this, ...args);
        }
        return result;
      })
    }));
  }
  async patchTableCell(cell) {
    Patcher.patchPrototype(this.plugin, cell, {
      render: Patcher.OverrideExisting((next) => function(ctx) {
        var _a;
        const isCanvas = ((_a = ctx.file) == null ? void 0 : _a.extension) === "canvas";
        if (isCanvas) ctx.file.extension = "md";
        const result = next.call(this, ctx);
        if (isCanvas) ctx.file.extension = "canvas";
        return result;
      })
    });
  }
};

// src/main.ts
var PATCHERS = [
  // Core canvas patchers
  CanvasPatcher,
  SearchCommandPatcher,
  // Core metadata patchers
  MetadataCachePatcher,
  FileManagerPatcher,
  // Direct metadata dependant patchers
  PropertiesPatcher,
  !(0, import_obsidian19.requireApiVersion)("1.12.0") && BacklinksPatcher,
  OutgoingLinksPatcher,
  // Metadata dependant patchers
  (0, import_obsidian19.requireApiVersion)("1.9.0") && BasesTableViewPatcher,
  LinkSuggestionsPatcher,
  EmbedPatcher,
  SearchPatcher
];
var CANVAS_EXTENSIONS = [
  // Advanced JSON Canvas Extensions
  MetadataCanvasExtension,
  NodeStylesExtension,
  EdgeStylesExtension,
  NodeRatioCanvasExtension,
  FloatingEdgeCanvasExtension,
  AutoResizeNodeCanvasExtension,
  CollapsibleGroupsCanvasExtension,
  ColorPaletteCanvasExtension,
  PresentationCanvasExtension,
  PortalsCanvasExtension,
  // UI Extensions (Non-savable data)
  CanvasMetadataExposerExtension,
  CanvasWrapperExposerExtension,
  NodeExposerExtension,
  EdgeExposerExtension,
  NodeInteractionExposerExtension,
  FrontmatterControlButtonCanvasExtension,
  BetterDefaultSettingsCanvasExtension,
  CommandsCanvasExtension,
  BetterReadonlyCanvasExtension,
  GroupCanvasExtension,
  VariableBreakpointCanvasExtension,
  EdgeHighlightCanvasExtension,
  AutoFileNodeEdgesCanvasExtension,
  FlipEdgeCanvasExtension,
  ZOrderingCanvasExtension,
  ExportCanvasExtension,
  FocusModeCanvasExtension,
  EncapsulateCanvasExtension,
  EdgeSelectionCanvasExtension
];
var AdvancedCanvasPlugin = class extends import_obsidian19.Plugin {
  async onload() {
    IconsHelper.addIcons();
    this.settings = new SettingsManager(this);
    await this.settings.loadSettings();
    this.settings.addSettingsTab();
    this.windowsManager = new WindowsManager(this);
    this.patchers = PATCHERS.map((Patcher2) => {
      if (!Patcher2) return;
      try {
        return new Patcher2(this);
      } catch (e) {
        console.error(`Error initializing patcher ${Patcher2.name}:`, e);
      }
    });
    this.canvasExtensions = CANVAS_EXTENSIONS.map((Extension) => {
      try {
        return new Extension(this);
      } catch (e) {
        console.error(`Error initializing ac-extension ${Extension.name}:`, e);
      }
    });
  }
  onunload() {
  }
  getCanvases() {
    return this.app.workspace.getLeavesOfType("canvas").map((leaf) => {
      var _a;
      return (_a = leaf.view) == null ? void 0 : _a.canvas;
    }).filter((canvas) => canvas);
  }
  getCurrentCanvasView() {
    const canvasView = this.app.workspace.getActiveViewOfType(import_obsidian19.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 */