764 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			764 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/*
 | 
						|
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);
 | 
						|
var __decorateClass = (decorators, target, key, kind) => {
 | 
						|
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
 | 
						|
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
 | 
						|
    if (decorator = decorators[i])
 | 
						|
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
 | 
						|
  if (kind && result)
 | 
						|
    __defProp(target, key, result);
 | 
						|
  return result;
 | 
						|
};
 | 
						|
 | 
						|
// src/main.ts
 | 
						|
var main_exports = {};
 | 
						|
__export(main_exports, {
 | 
						|
  default: () => LovelyMindmap
 | 
						|
});
 | 
						|
module.exports = __toCommonJS(main_exports);
 | 
						|
var import_obsidian2 = require("obsidian");
 | 
						|
 | 
						|
// src/tool/index.ts
 | 
						|
function debounce(delay = 100) {
 | 
						|
  let lastTime = 0;
 | 
						|
  let timer;
 | 
						|
  return function(target, propertyKey, descriptor) {
 | 
						|
    const originalMethod = descriptor.value;
 | 
						|
    descriptor.value = function(...args) {
 | 
						|
      const now = Date.now();
 | 
						|
      clearTimeout(timer);
 | 
						|
      if (now - lastTime < delay) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      timer = setTimeout(() => {
 | 
						|
        originalMethod.apply(this, args);
 | 
						|
        lastTime = 0;
 | 
						|
      }, delay);
 | 
						|
      lastTime = now;
 | 
						|
    };
 | 
						|
    return descriptor;
 | 
						|
  };
 | 
						|
}
 | 
						|
function calcDistance(a, b) {
 | 
						|
  return Math.sqrt(
 | 
						|
    (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2
 | 
						|
  );
 | 
						|
}
 | 
						|
function findClosestNodeByBbox(pos, nodes) {
 | 
						|
  return nodes.reduce((prev, cur, idx) => {
 | 
						|
    const a = [cur.bbox.minX, cur.bbox.minY];
 | 
						|
    const b = [cur.bbox.maxX, cur.bbox.minY];
 | 
						|
    const c = [cur.bbox.minX, cur.bbox.maxY];
 | 
						|
    const d = [cur.bbox.maxX, cur.bbox.maxY];
 | 
						|
    const distance = Math.min(
 | 
						|
      calcDistance(pos, a),
 | 
						|
      calcDistance(pos, b),
 | 
						|
      calcDistance(pos, c),
 | 
						|
      calcDistance(pos, d)
 | 
						|
    );
 | 
						|
    if (idx === 0) {
 | 
						|
      return {
 | 
						|
        node: cur,
 | 
						|
        distance
 | 
						|
      };
 | 
						|
    }
 | 
						|
    return distance < prev.distance ? { node: cur, distance } : prev;
 | 
						|
  }, {
 | 
						|
    node: {},
 | 
						|
    distance: 0
 | 
						|
  });
 | 
						|
}
 | 
						|
function uuid() {
 | 
						|
  const first = Math.floor(Math.random() * 9 + 1);
 | 
						|
  const rest = String(Math.random()).slice(2, 10);
 | 
						|
  const random9 = first + rest;
 | 
						|
  return string10To64(Date.now()) + string10To64(random9);
 | 
						|
}
 | 
						|
function string10To64(str) {
 | 
						|
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 | 
						|
  const radix = chars.length;
 | 
						|
  let num = typeof str === "string" ? parseInt(str) : str;
 | 
						|
  const res = [];
 | 
						|
  do {
 | 
						|
    const mod = num % radix;
 | 
						|
    res.push(chars[mod]);
 | 
						|
    num = (num - mod) / radix;
 | 
						|
  } while (num > 0);
 | 
						|
  return res.join("");
 | 
						|
}
 | 
						|
var supportedModifiers = ["mod", "ctrl", "meta", "shift", "alt"];
 | 
						|
var navigationKeys = [
 | 
						|
  "tab",
 | 
						|
  "enter",
 | 
						|
  "arrowup",
 | 
						|
  "arrowdown",
 | 
						|
  "arrowleft",
 | 
						|
  "arrowright"
 | 
						|
  /** 'backspace', 'delete', 'escape', 'space' */
 | 
						|
];
 | 
						|
function convertHotkey2Array(hotkey) {
 | 
						|
  const parts = hotkey.split("+");
 | 
						|
  let modifier = null;
 | 
						|
  let key = null;
 | 
						|
  if (parts.length === 1) {
 | 
						|
    key = parts[0].trim();
 | 
						|
    if (!navigationKeys.includes(key.toLocaleLowerCase()) && !/^[a-zA-Z0-9]$/.test(key)) {
 | 
						|
      throw new Error("Invalid key. Expected a single alphanumeric character or a navigation key but got " + key);
 | 
						|
    }
 | 
						|
    return [[], key];
 | 
						|
  } else if (parts.length === 2) {
 | 
						|
    modifier = parts[0].trim();
 | 
						|
    if (!supportedModifiers.includes(modifier.toLocaleLowerCase())) {
 | 
						|
      throw new Error(`Invalid modifier. Expected [${supportedModifiers.join(", ")}].`);
 | 
						|
    }
 | 
						|
    key = parts[1].trim();
 | 
						|
    if (!navigationKeys.includes(key.toLocaleLowerCase()) && !/^[a-zA-Z0-9]$/.test(key)) {
 | 
						|
      throw new Error("Invalid key. Expected a single alphanumeric character or a navigation key but got " + key);
 | 
						|
    }
 | 
						|
    return [[modifier.charAt(0).toUpperCase() + modifier.slice(1)], key];
 | 
						|
  }
 | 
						|
  throw new Error("Invalid hotkey format. Expected a single alphanumeric character or a modifier followed by a single alphanumeric character but got " + hotkey);
 | 
						|
}
 | 
						|
 | 
						|
// node_modules/autobind-decorator/lib/esm/index.js
 | 
						|
function _typeof(obj) {
 | 
						|
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
 | 
						|
    _typeof = function _typeof2(obj2) {
 | 
						|
      return typeof obj2;
 | 
						|
    };
 | 
						|
  } else {
 | 
						|
    _typeof = function _typeof2(obj2) {
 | 
						|
      return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
 | 
						|
    };
 | 
						|
  }
 | 
						|
  return _typeof(obj);
 | 
						|
}
 | 
						|
function boundMethod(target, key, descriptor) {
 | 
						|
  var fn = descriptor.value;
 | 
						|
  if (typeof fn !== "function") {
 | 
						|
    throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
 | 
						|
  }
 | 
						|
  var definingProperty = false;
 | 
						|
  return {
 | 
						|
    configurable: true,
 | 
						|
    get: function get() {
 | 
						|
      if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== "function") {
 | 
						|
        return fn;
 | 
						|
      }
 | 
						|
      var boundFn = fn.bind(this);
 | 
						|
      definingProperty = true;
 | 
						|
      Object.defineProperty(this, key, {
 | 
						|
        configurable: true,
 | 
						|
        get: function get2() {
 | 
						|
          return boundFn;
 | 
						|
        },
 | 
						|
        set: function set(value) {
 | 
						|
          fn = value;
 | 
						|
          delete this[key];
 | 
						|
        }
 | 
						|
      });
 | 
						|
      definingProperty = false;
 | 
						|
      return boundFn;
 | 
						|
    },
 | 
						|
    set: function set(value) {
 | 
						|
      fn = value;
 | 
						|
    }
 | 
						|
  };
 | 
						|
}
 | 
						|
function boundClass(target) {
 | 
						|
  var keys;
 | 
						|
  if (typeof Reflect !== "undefined" && typeof Reflect.ownKeys === "function") {
 | 
						|
    keys = Reflect.ownKeys(target.prototype);
 | 
						|
  } else {
 | 
						|
    keys = Object.getOwnPropertyNames(target.prototype);
 | 
						|
    if (typeof Object.getOwnPropertySymbols === "function") {
 | 
						|
      keys = keys.concat(Object.getOwnPropertySymbols(target.prototype));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  keys.forEach(function(key) {
 | 
						|
    if (key === "constructor") {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    var descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);
 | 
						|
    if (typeof descriptor.value === "function") {
 | 
						|
      Object.defineProperty(target.prototype, key, boundMethod(target, key, descriptor));
 | 
						|
    }
 | 
						|
  });
 | 
						|
  return target;
 | 
						|
}
 | 
						|
function autobind() {
 | 
						|
  if (arguments.length === 1) {
 | 
						|
    return boundClass.apply(void 0, arguments);
 | 
						|
  }
 | 
						|
  return boundMethod.apply(void 0, arguments);
 | 
						|
}
 | 
						|
 | 
						|
// src/module/node.ts
 | 
						|
var Node = class {
 | 
						|
  constructor(main) {
 | 
						|
    this.main = main;
 | 
						|
  }
 | 
						|
  getNavigationNode() {
 | 
						|
    const node = this.getSingleSelection();
 | 
						|
    if (!node || !node.isFocused || node.isEditing)
 | 
						|
      return null;
 | 
						|
    return node;
 | 
						|
  }
 | 
						|
  getCreationNode() {
 | 
						|
    const node = this.getSingleSelection();
 | 
						|
    if (!node || !node.isFocused || !node.isEditing)
 | 
						|
      return null;
 | 
						|
    return node;
 | 
						|
  }
 | 
						|
  getSelection() {
 | 
						|
    return this.main.canvas.selection;
 | 
						|
  }
 | 
						|
  getSingleSelection() {
 | 
						|
    const selections = this.main.canvas.selection;
 | 
						|
    if (selections.size === 0 || selections.size > 1) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    return selections.values().next().value;
 | 
						|
  }
 | 
						|
  getFromNodes(node) {
 | 
						|
    const fromNodeFilter = (edge) => edge.to.node.id === node.id;
 | 
						|
    return this.main.canvas.getEdgesForNode(node).filter(fromNodeFilter).map((edge) => edge.from.node);
 | 
						|
  }
 | 
						|
  getToNodes(node) {
 | 
						|
    const toNodeFilter = (edge) => edge.from.node.id === node.id;
 | 
						|
    return this.main.canvas.getEdgesForNode(node).filter(toNodeFilter).map((edge) => edge.to.node);
 | 
						|
  }
 | 
						|
  getSibNodes(target) {
 | 
						|
    const fromNodes = this.getFromNodes(target);
 | 
						|
    const toNodes = this.getToNodes(fromNodes[0]);
 | 
						|
    return toNodes.filter((node) => node.id !== target.id);
 | 
						|
  }
 | 
						|
  createChildren() {
 | 
						|
    const selection = this.getNavigationNode();
 | 
						|
    if (!selection)
 | 
						|
      return;
 | 
						|
    const {
 | 
						|
      x,
 | 
						|
      y,
 | 
						|
      width,
 | 
						|
      height
 | 
						|
    } = selection;
 | 
						|
    const rightSideNodeFilter = (node) => {
 | 
						|
      var _a, _b, _c;
 | 
						|
      return ((_a = node == null ? void 0 : node.to) == null ? void 0 : _a.side) === "left" && selection.id !== ((_c = (_b = node == null ? void 0 : node.to) == null ? void 0 : _b.node) == null ? void 0 : _c.id);
 | 
						|
    };
 | 
						|
    const sibNodes = this.main.canvas.getEdgesForNode(selection).filter(rightSideNodeFilter).map((node) => node.to.node);
 | 
						|
    const nextNodeY = sibNodes.length > 0 ? Math.max(...sibNodes.map((node) => node.y)) + this.main.setting.EPSILON : y;
 | 
						|
    const childNode = this.main.canvas.createTextNode({
 | 
						|
      pos: {
 | 
						|
        x: x + width + 200,
 | 
						|
        y: nextNodeY
 | 
						|
      },
 | 
						|
      size: {
 | 
						|
        height,
 | 
						|
        width
 | 
						|
      },
 | 
						|
      text: "",
 | 
						|
      focus: false,
 | 
						|
      save: true
 | 
						|
    });
 | 
						|
    const data = this.main.canvas.getData();
 | 
						|
    this.main.canvas.importData({
 | 
						|
      "edges": [
 | 
						|
        ...data.edges,
 | 
						|
        {
 | 
						|
          "id": uuid(),
 | 
						|
          "fromNode": selection.id,
 | 
						|
          "fromSide": "right",
 | 
						|
          "toNode": childNode.id,
 | 
						|
          "toSide": "left"
 | 
						|
        }
 | 
						|
      ],
 | 
						|
      "nodes": data.nodes
 | 
						|
    });
 | 
						|
    this.main.layout.useSide(selection, sibNodes.concat(childNode));
 | 
						|
    this.main.view.zoomToNode(childNode);
 | 
						|
  }
 | 
						|
  createBeforeSibNode() {
 | 
						|
    this.createSibNodeHelper(true);
 | 
						|
  }
 | 
						|
  createAfterSibNode() {
 | 
						|
    this.createSibNodeHelper(false);
 | 
						|
  }
 | 
						|
  createSibNodeHelper(isBefore) {
 | 
						|
    const selection = this.getNavigationNode();
 | 
						|
    if (!selection)
 | 
						|
      return;
 | 
						|
    const {
 | 
						|
      x,
 | 
						|
      y,
 | 
						|
      width,
 | 
						|
      height
 | 
						|
    } = selection;
 | 
						|
    const { EPSILON } = this.main.setting;
 | 
						|
    const fromNode = this.getFromNodes(selection)[0];
 | 
						|
    const toNodes = this.getToNodes(fromNode);
 | 
						|
    const willInsertedNode = this.main.canvas.createTextNode({
 | 
						|
      pos: {
 | 
						|
        x,
 | 
						|
        y: isBefore ? y - EPSILON : y + EPSILON
 | 
						|
      },
 | 
						|
      size: {
 | 
						|
        height,
 | 
						|
        width
 | 
						|
      },
 | 
						|
      text: "",
 | 
						|
      focus: false,
 | 
						|
      save: true
 | 
						|
    });
 | 
						|
    const data = this.main.canvas.getData();
 | 
						|
    this.main.canvas.importData({
 | 
						|
      "edges": [
 | 
						|
        ...data.edges,
 | 
						|
        {
 | 
						|
          "id": uuid(),
 | 
						|
          "fromNode": fromNode.id,
 | 
						|
          "fromSide": "right",
 | 
						|
          "toNode": willInsertedNode.id,
 | 
						|
          "toSide": "left"
 | 
						|
        }
 | 
						|
      ],
 | 
						|
      "nodes": data.nodes
 | 
						|
    });
 | 
						|
    this.main.layout.useSide(fromNode, toNodes.concat(willInsertedNode));
 | 
						|
    this.main.view.zoomToNode(willInsertedNode);
 | 
						|
  }
 | 
						|
};
 | 
						|
__decorateClass([
 | 
						|
  debounce()
 | 
						|
], Node.prototype, "createChildren", 1);
 | 
						|
__decorateClass([
 | 
						|
  debounce()
 | 
						|
], Node.prototype, "createBeforeSibNode", 1);
 | 
						|
__decorateClass([
 | 
						|
  debounce()
 | 
						|
], Node.prototype, "createAfterSibNode", 1);
 | 
						|
Node = __decorateClass([
 | 
						|
  autobind
 | 
						|
], Node);
 | 
						|
 | 
						|
// src/module/keymap.ts
 | 
						|
var Keymap = class {
 | 
						|
  constructor(main) {
 | 
						|
    this.hotkeys = [];
 | 
						|
    this.main = main;
 | 
						|
    this.node = main.node;
 | 
						|
  }
 | 
						|
  async help() {
 | 
						|
    if (this.main.view.isCreating())
 | 
						|
      return;
 | 
						|
    console.debug("this:\n", this);
 | 
						|
    console.debug("app:\n", this.main.app);
 | 
						|
    console.debug("canvas:\n", this.main.canvas);
 | 
						|
    console.debug("selections:\n", this.main.canvas.selection.values().next());
 | 
						|
  }
 | 
						|
  nodeNavigation(_, context) {
 | 
						|
    const { key } = context;
 | 
						|
    const selection = this.node.getSingleSelection();
 | 
						|
    if (!selection || selection.isEditing) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const { OFFSET_WEIGHT } = this.main.setting;
 | 
						|
    const data = this.main.canvas.getViewportNodes();
 | 
						|
    const offsetX = (a, b) => Math.abs(b.x - a.x);
 | 
						|
    const offsetY = (a, b) => Math.abs(b.y - a.y);
 | 
						|
    const endpointOffset = (a, b) => Math.min(
 | 
						|
      Math.abs(b.y - a.y + 2 / a.height),
 | 
						|
      Math.abs(b.y + b.height - a.y - 2 / a.height),
 | 
						|
      Math.abs(b.x - a.x + 2 / a.width),
 | 
						|
      Math.abs(b.x + b.width - a.x + 2 / a.width)
 | 
						|
    );
 | 
						|
    const calcDistance2 = (a, b) => key === "ArrowLeft" || key === "ArrowRight" ? offsetX(a, b) + endpointOffset(a, b) ** OFFSET_WEIGHT : offsetY(a, b) + endpointOffset(a, b) ** OFFSET_WEIGHT;
 | 
						|
    const isSameDirection = (node) => {
 | 
						|
      const notSelf = node.id !== selection.id;
 | 
						|
      const strategies = {
 | 
						|
        ArrowRight: notSelf && node.x > selection.x + selection.width,
 | 
						|
        ArrowLeft: notSelf && node.x + node.width < selection.x,
 | 
						|
        ArrowUp: notSelf && node.y + node.height < selection.y,
 | 
						|
        ArrowDown: notSelf && node.y > selection.y + selection.height
 | 
						|
      };
 | 
						|
      return strategies[key];
 | 
						|
    };
 | 
						|
    const midpoints = data.filter(isSameDirection).map((node) => ({
 | 
						|
      node,
 | 
						|
      offsetX: offsetX(selection, node),
 | 
						|
      offsetY: offsetY(selection, node),
 | 
						|
      endpointOffset: endpointOffset(selection, node),
 | 
						|
      distance: calcDistance2(selection, node)
 | 
						|
    })).sort((a, b) => a.distance - b.distance);
 | 
						|
    if (midpoints.length > 0) {
 | 
						|
      this.main.view.zoomToNode(midpoints[0].node);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  blurNode() {
 | 
						|
    if (this.main.view.isCreating()) {
 | 
						|
      this.main.view.creation2Navigation();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (this.main.view.isNavigating()) {
 | 
						|
      this.main.view.useTouch();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  focusNode() {
 | 
						|
    if (this.main.view.isTouching()) {
 | 
						|
      this.main.view.touch2Navigation();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const navigationNode = this.main.node.getNavigationNode();
 | 
						|
    if (!!navigationNode) {
 | 
						|
      this.main.view.useCreation(navigationNode);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  register(modifiers, key, func) {
 | 
						|
    return this.main.app.scope.register(modifiers, key, func);
 | 
						|
  }
 | 
						|
  /**
 | 
						|
   * priority: options > config > default
 | 
						|
   * 1. options: function argument
 | 
						|
   * 2. config: `options.hotkeys`
 | 
						|
   * 3. default: this.register
 | 
						|
   * @param options 
 | 
						|
   */
 | 
						|
  registerAll(options) {
 | 
						|
    const { hotkeys } = this.main.setting;
 | 
						|
    const registerHotkey = (action, callback) => {
 | 
						|
      if (options == null ? void 0 : options[action]) {
 | 
						|
        this.hotkeys.push(options[action]());
 | 
						|
      } else {
 | 
						|
        const [modifier, key] = convertHotkey2Array(hotkeys[action]);
 | 
						|
        this.hotkeys.push(this.register(modifier, key, callback));
 | 
						|
      }
 | 
						|
    };
 | 
						|
    registerHotkey("Focus", this.focusNode);
 | 
						|
    registerHotkey("CreateChild", this.main.node.createChildren);
 | 
						|
    registerHotkey("CreateBeforeSib", this.main.node.createBeforeSibNode);
 | 
						|
    registerHotkey("CreateAfterSib", this.main.node.createAfterSibNode);
 | 
						|
    registerHotkey("ArrowLeft", this.nodeNavigation);
 | 
						|
    registerHotkey("ArrowRight", this.nodeNavigation);
 | 
						|
    registerHotkey("ArrowUp", this.nodeNavigation);
 | 
						|
    registerHotkey("ArrowDown", this.nodeNavigation);
 | 
						|
  }
 | 
						|
  unregisterAll() {
 | 
						|
    this.hotkeys.forEach((key) => this.main.app.scope.unregister(key));
 | 
						|
  }
 | 
						|
};
 | 
						|
__decorateClass([
 | 
						|
  debounce()
 | 
						|
], Keymap.prototype, "help", 1);
 | 
						|
Keymap = __decorateClass([
 | 
						|
  autobind
 | 
						|
], Keymap);
 | 
						|
 | 
						|
// src/module/view.ts
 | 
						|
var View = class {
 | 
						|
  constructor(main) {
 | 
						|
    this.main = main;
 | 
						|
  }
 | 
						|
  isTouching() {
 | 
						|
    return this.main.node.getSelection().size === 0;
 | 
						|
  }
 | 
						|
  isNavigating() {
 | 
						|
    const node = this.main.node.getSingleSelection();
 | 
						|
    if (!node)
 | 
						|
      return false;
 | 
						|
    return node.isFocused && !node.isEditing;
 | 
						|
  }
 | 
						|
  isCreating() {
 | 
						|
    const node = this.main.node.getSingleSelection();
 | 
						|
    if (!node)
 | 
						|
      return false;
 | 
						|
    return node.isFocused && node.isEditing;
 | 
						|
  }
 | 
						|
  useTouch() {
 | 
						|
    this.main.canvas.deselectAll();
 | 
						|
  }
 | 
						|
  useCreation(node) {
 | 
						|
    setTimeout(
 | 
						|
      () => node.startEditing(),
 | 
						|
      this.main.setting.MACRO_TASK_DELAY
 | 
						|
    );
 | 
						|
  }
 | 
						|
  creation2Navigation() {
 | 
						|
    const selection = this.main.node.getSingleSelection();
 | 
						|
    if (!selection || !this.isCreating())
 | 
						|
      return;
 | 
						|
    selection.blur();
 | 
						|
    selection.focus();
 | 
						|
  }
 | 
						|
  touch2Navigation() {
 | 
						|
    const viewportBBox = this.main.canvas.getViewportBBox();
 | 
						|
    const centerPoint = [
 | 
						|
      (viewportBBox.minX + viewportBBox.maxX) / 2,
 | 
						|
      (viewportBBox.minY + viewportBBox.maxY) / 2
 | 
						|
    ];
 | 
						|
    const viewportNodes = this.main.canvas.getViewportNodes();
 | 
						|
    const res = findClosestNodeByBbox(centerPoint, viewportNodes);
 | 
						|
    this.zoomToNode(res.node);
 | 
						|
  }
 | 
						|
  zoomToNode(node) {
 | 
						|
    this.main.canvas.selectOnly(node);
 | 
						|
    this.main.canvas.zoomToSelection();
 | 
						|
    if (this.main.setting.autoFocus) {
 | 
						|
      this.useCreation(node);
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// src/module/setting.ts
 | 
						|
var import_obsidian = require("obsidian");
 | 
						|
var DEFAULT_SETTINGS = {
 | 
						|
  autoFocus: false,
 | 
						|
  hotkeys: {
 | 
						|
    CreateChild: "Tab",
 | 
						|
    CreateBeforeSib: "Shift + Enter",
 | 
						|
    CreateAfterSib: "Enter",
 | 
						|
    Focus: "F",
 | 
						|
    ArrowUp: "Alt + ArrowUp",
 | 
						|
    ArrowDown: "Alt + ArrowDown",
 | 
						|
    ArrowLeft: "Alt + ArrowLeft",
 | 
						|
    ArrowRight: "Alt + ArrowRight"
 | 
						|
  },
 | 
						|
  ROW_GAP: 20,
 | 
						|
  COLUMN_GAP: 200,
 | 
						|
  EPSILON: 1,
 | 
						|
  OFFSET_WEIGHT: 1.1,
 | 
						|
  MACRO_TASK_DELAY: 50
 | 
						|
};
 | 
						|
var Setting = class extends import_obsidian.PluginSettingTab {
 | 
						|
  constructor(main) {
 | 
						|
    super(main.app, main);
 | 
						|
    this.main = main;
 | 
						|
  }
 | 
						|
  display() {
 | 
						|
    const { containerEl } = this;
 | 
						|
    containerEl.empty();
 | 
						|
    containerEl.createEl("h3", { text: "\u{1F917} Lovely Mindmap Settings" });
 | 
						|
    this.addAutoFocus();
 | 
						|
    this.addCreateChildHotkey();
 | 
						|
    this.addCreateBeforeSibHotKey();
 | 
						|
    this.addCreateAfterSibHotKey();
 | 
						|
  }
 | 
						|
  addAutoFocus() {
 | 
						|
    new import_obsidian.Setting(this.containerEl).setName("Auto Focus").setDesc("auto focus node when create new node").addToggle(
 | 
						|
      (component) => component.setValue(this.main.setting.autoFocus).onChange(async (open) => {
 | 
						|
        this.main.setting.autoFocus = open;
 | 
						|
        await this.main.saveData(this.main.setting);
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
  addCreateChildHotkey() {
 | 
						|
    let _hotKey = this.main.setting.hotkeys.CreateChild;
 | 
						|
    let errorText = "";
 | 
						|
    new import_obsidian.Setting(this.containerEl).setName("Create Child Node").setDesc(`Custom your hotkey to create a child node, default is Tab. 
 | 
						|
        You can use any letter, number, or modifier combined with 
 | 
						|
        a letter or number, e.g., \u300CC\u300Dor\u300Ccmd + C\u300Dto create a child node.
 | 
						|
        Use\u300C+\u300Dto separate modifiers and alphanumeric characters.`).addText((text) => text.setPlaceholder("Enter hotkey").setValue(_hotKey).onChange(async (value) => {
 | 
						|
      _hotKey = value;
 | 
						|
      errorText = "";
 | 
						|
    })).addButton(
 | 
						|
      (button) => button.setButtonText("Save").setCta().onClick(async () => {
 | 
						|
        if (errorText) {
 | 
						|
          new import_obsidian.Notice(errorText);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        try {
 | 
						|
          const [modifier, key] = convertHotkey2Array(_hotKey);
 | 
						|
          this.main.setting.hotkeys.CreateChild = _hotKey;
 | 
						|
          await this.main.saveData(this.main.setting);
 | 
						|
          this.main.keymap.unregisterAll();
 | 
						|
          this.main.keymap.registerAll({
 | 
						|
            CreateChild: () => this.main.keymap.register(modifier, key, this.main.node.createChildren)
 | 
						|
          });
 | 
						|
          new import_obsidian.Notice("Save successfully!");
 | 
						|
        } catch (error) {
 | 
						|
          new import_obsidian.Notice(error.message);
 | 
						|
        }
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
  addCreateBeforeSibHotKey() {
 | 
						|
    let _hotKey = this.main.setting.hotkeys.CreateBeforeSib || "Shift+Enter";
 | 
						|
    let errorText = "";
 | 
						|
    new import_obsidian.Setting(this.containerEl).setName("Create Sibling Node Before").setDesc(`Custom your hotkey to create a sibling node before the current node. Default is Shift+Enter. Same as 'Create Child Node'.`).addText((text) => text.setPlaceholder("Enter hotkey").setValue(_hotKey).onChange(async (value) => {
 | 
						|
      _hotKey = value;
 | 
						|
      errorText = "";
 | 
						|
    })).addButton(
 | 
						|
      (button) => button.setButtonText("Save").setCta().onClick(async () => {
 | 
						|
        if (errorText) {
 | 
						|
          new import_obsidian.Notice(errorText);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        try {
 | 
						|
          const [modifier, key] = convertHotkey2Array(_hotKey);
 | 
						|
          this.main.setting.hotkeys.CreateBeforeSib = _hotKey;
 | 
						|
          await this.main.saveData(this.main.setting);
 | 
						|
          this.main.keymap.unregisterAll();
 | 
						|
          this.main.keymap.registerAll({
 | 
						|
            CreateBeforeSib: () => this.main.keymap.register(modifier, key, this.main.node.createBeforeSibNode)
 | 
						|
          });
 | 
						|
          new import_obsidian.Notice("Save successfully!");
 | 
						|
        } catch (error) {
 | 
						|
          new import_obsidian.Notice(error.message);
 | 
						|
        }
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
  addCreateAfterSibHotKey() {
 | 
						|
    let _hotKey = this.main.setting.hotkeys.CreateAfterSib || "Enter";
 | 
						|
    let errorText = "";
 | 
						|
    new import_obsidian.Setting(this.containerEl).setName("Create Sibling Node After").setDesc(`Custom your hotkey to create a sibling node after the current node. Default is Enter. Same as 'Create Child Node'.`).addText((text) => text.setPlaceholder("Enter hotkey").setValue(_hotKey).onChange(async (value) => {
 | 
						|
      _hotKey = value;
 | 
						|
      errorText = "";
 | 
						|
    })).addButton(
 | 
						|
      (button) => button.setButtonText("Save").setCta().onClick(async () => {
 | 
						|
        if (errorText) {
 | 
						|
          new import_obsidian.Notice(errorText);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        try {
 | 
						|
          const [modifier, key] = convertHotkey2Array(_hotKey);
 | 
						|
          this.main.setting.hotkeys.CreateAfterSib = _hotKey;
 | 
						|
          await this.main.saveData(this.main.setting);
 | 
						|
          this.main.keymap.unregisterAll();
 | 
						|
          this.main.keymap.registerAll({
 | 
						|
            CreateAfterSib: () => this.main.keymap.register(modifier, key, this.main.node.createAfterSibNode)
 | 
						|
          });
 | 
						|
          new import_obsidian.Notice("Save successfully!");
 | 
						|
        } catch (error) {
 | 
						|
          new import_obsidian.Notice(error.message);
 | 
						|
        }
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
  async loadSettings() {
 | 
						|
    this.main.setting = { ...DEFAULT_SETTINGS, ...await this.main.loadData() };
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// src/module/layout.ts
 | 
						|
var Layout = class {
 | 
						|
  constructor(main) {
 | 
						|
    this.main = main;
 | 
						|
  }
 | 
						|
  useSide(parent, child) {
 | 
						|
    const { ROW_GAP, COLUMN_GAP } = this.main.setting;
 | 
						|
    const bbox = child.reduce((prev, node, idx) => {
 | 
						|
      return idx > 0 ? {
 | 
						|
        height: prev.height + node.height + ROW_GAP,
 | 
						|
        heightNodes: prev.heightNodes.concat(node.height)
 | 
						|
      } : {
 | 
						|
        height: prev.height + node.height,
 | 
						|
        heightNodes: prev.heightNodes.concat(node.height)
 | 
						|
      };
 | 
						|
    }, {
 | 
						|
      height: 0,
 | 
						|
      heightNodes: []
 | 
						|
    });
 | 
						|
    const top = parent.y + parent.height * 0.5 - bbox.height * 0.5;
 | 
						|
    const getSum = (arr) => arr.reduce((sum, cur) => sum + cur, 0);
 | 
						|
    child.sort((a, b) => a.y - b.y).forEach((node, i) => {
 | 
						|
      node.moveTo({
 | 
						|
        x: parent.width + parent.x + COLUMN_GAP,
 | 
						|
        y: top + ROW_GAP * i + getSum(bbox.heightNodes.slice(0, i))
 | 
						|
      });
 | 
						|
    });
 | 
						|
  }
 | 
						|
  useSurround() {
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// src/main.ts
 | 
						|
var LovelyMindmap = class extends import_obsidian2.Plugin {
 | 
						|
  constructor(app2, manifest) {
 | 
						|
    super(app2, manifest);
 | 
						|
    this.canvas = null;
 | 
						|
    this.intervalTimer = /* @__PURE__ */ new Map();
 | 
						|
    this.node = new Node(this);
 | 
						|
    this.keymap = new Keymap(this);
 | 
						|
    this.view = new View(this);
 | 
						|
    this.setting = new Setting(this);
 | 
						|
    this.layout = new Layout(this);
 | 
						|
  }
 | 
						|
  createCanvasInstance() {
 | 
						|
    const timer = setInterval(() => {
 | 
						|
      var _a, _b;
 | 
						|
      this.canvas = (_b = (_a = app.workspace.getLeavesOfType("canvas").first()) == null ? void 0 : _a.view) == null ? void 0 : _b.canvas;
 | 
						|
      if (!!this.canvas) {
 | 
						|
        clearInterval(this.intervalTimer.get("canvas"));
 | 
						|
      }
 | 
						|
    }, 100);
 | 
						|
    if (!this.intervalTimer.get("canvas")) {
 | 
						|
      this.intervalTimer.set("canvas", timer);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  onActiveLeafChange() {
 | 
						|
    this.app.workspace.on("active-leaf-change", async (leaf) => {
 | 
						|
      var _a, _b;
 | 
						|
      const extension = (_b = (_a = leaf == null ? void 0 : leaf.view) == null ? void 0 : _a.file) == null ? void 0 : _b.extension;
 | 
						|
      if (extension === "canvas") {
 | 
						|
        this.onKeymap();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      this.onunload();
 | 
						|
    });
 | 
						|
  }
 | 
						|
  /**
 | 
						|
   * A series of events for canvas initialization
 | 
						|
   *
 | 
						|
   * - When switching away from the canvas viewport, remove the keyboard shortcuts and canvas instance.
 | 
						|
   * - When switching back to the canvas viewport, re-register the keyboard shortcuts and canvas instance.
 | 
						|
   */
 | 
						|
  onKeymap() {
 | 
						|
    this.createCanvasInstance();
 | 
						|
    this.keymap.registerAll();
 | 
						|
    this.addCommand({
 | 
						|
      id: "blurNode",
 | 
						|
      name: "Blur node",
 | 
						|
      hotkeys: [
 | 
						|
        {
 | 
						|
          modifiers: ["Mod"],
 | 
						|
          key: "Escape"
 | 
						|
        }
 | 
						|
      ],
 | 
						|
      checkCallback: () => this.keymap.blurNode()
 | 
						|
    });
 | 
						|
  }
 | 
						|
  async onload() {
 | 
						|
    await this.setting.loadSettings();
 | 
						|
    this.addSettingTab(new Setting(this));
 | 
						|
    this.onActiveLeafChange();
 | 
						|
    this.onKeymap();
 | 
						|
  }
 | 
						|
  onunload() {
 | 
						|
    this.keymap.unregisterAll();
 | 
						|
    this.intervalTimer.forEach(clearInterval);
 | 
						|
  }
 | 
						|
};
 |