Oscar Plaisant 5b65eb2b2a update
2024-10-01 11:38:50 +02:00

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);
}
};