3864 lines
167 KiB
JavaScript
3864 lines
167 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);
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => AdvancedCanvasPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian10 = require("obsidian");
|
|
|
|
// node_modules/monkey-around/mjs/index.js
|
|
function around(obj, factories) {
|
|
const removers = Object.keys(factories).map((key) => around1(obj, key, factories[key]));
|
|
return removers.length === 1 ? removers[0] : function() {
|
|
removers.forEach((r) => r());
|
|
};
|
|
}
|
|
function around1(obj, method, createWrapper) {
|
|
const original = obj[method], hadOwn = obj.hasOwnProperty(method);
|
|
let current = createWrapper(original);
|
|
if (original)
|
|
Object.setPrototypeOf(current, original);
|
|
Object.setPrototypeOf(wrapper, current);
|
|
obj[method] = wrapper;
|
|
return remove;
|
|
function wrapper(...args) {
|
|
if (current === original && obj[method] === wrapper)
|
|
remove();
|
|
return current.apply(this, args);
|
|
}
|
|
function remove() {
|
|
if (obj[method] === wrapper) {
|
|
if (hadOwn)
|
|
obj[method] = original;
|
|
else
|
|
delete obj[method];
|
|
}
|
|
if (current === original)
|
|
return;
|
|
current = original;
|
|
Object.setPrototypeOf(wrapper, original || Function);
|
|
}
|
|
}
|
|
|
|
// src/utils/patch-helper.ts
|
|
var PatchHelper = class {
|
|
static tryPatchWorkspacePrototype(plugin, getTarget, functions) {
|
|
return new Promise((resolve) => {
|
|
const tryPatch = () => {
|
|
const target = getTarget();
|
|
if (!target)
|
|
return null;
|
|
const uninstaller = around(target.constructor.prototype, functions);
|
|
plugin.register(uninstaller);
|
|
return target;
|
|
};
|
|
const result = tryPatch();
|
|
if (result) {
|
|
resolve(result);
|
|
return;
|
|
}
|
|
const listener = plugin.app.workspace.on("layout-change", () => {
|
|
const result2 = tryPatch();
|
|
if (result2) {
|
|
plugin.app.workspace.offref(listener);
|
|
resolve(result2);
|
|
}
|
|
});
|
|
plugin.registerEvent(listener);
|
|
});
|
|
}
|
|
static patchObjectPrototype(plugin, target, functions) {
|
|
const uninstaller = around(target.constructor.prototype, functions);
|
|
plugin.register(uninstaller);
|
|
}
|
|
static patchObjectInstance(plugin, target, functions) {
|
|
const uninstaller = around(target, functions);
|
|
plugin.register(uninstaller);
|
|
}
|
|
};
|
|
|
|
// src/core/events.ts
|
|
var CANVAS_EVENT_PREFIX = "canvas";
|
|
var PLUGIN_EVENT_PREFIX = "advanced-canvas";
|
|
var CanvasEvent = {
|
|
// Built-in events
|
|
SelectionContextMenu: `${CANVAS_EVENT_PREFIX}:selection-menu`,
|
|
NodeContextMenu: `${CANVAS_EVENT_PREFIX}:node-menu`,
|
|
EdgeContextMenu: `${CANVAS_EVENT_PREFIX}:edge-menu`,
|
|
NodeConnectionDropContextMenu: `${CANVAS_EVENT_PREFIX}:node-connection-drop-menu`,
|
|
// Custom events
|
|
CanvasChanged: `${PLUGIN_EVENT_PREFIX}:canvas-changed`,
|
|
ViewportChanged: {
|
|
Before: `${PLUGIN_EVENT_PREFIX}:viewport-changed:before`,
|
|
After: `${PLUGIN_EVENT_PREFIX}:viewport-changed:after`
|
|
},
|
|
NodeMoved: `${PLUGIN_EVENT_PREFIX}:node-moved`,
|
|
DoubleClick: `${PLUGIN_EVENT_PREFIX}:double-click`,
|
|
DraggingStateChanged: `${PLUGIN_EVENT_PREFIX}:dragging-state-changed`,
|
|
NodeCreated: `${PLUGIN_EVENT_PREFIX}:node-created`,
|
|
EdgeCreated: `${PLUGIN_EVENT_PREFIX}:edge-created`,
|
|
NodeAdded: `${PLUGIN_EVENT_PREFIX}:node-added`,
|
|
EdgeAdded: `${PLUGIN_EVENT_PREFIX}:edge-added`,
|
|
NodeChanged: `${PLUGIN_EVENT_PREFIX}:node-changed`,
|
|
EdgeChanged: `${PLUGIN_EVENT_PREFIX}:edge-changed`,
|
|
NodeTextContentChanged: `${PLUGIN_EVENT_PREFIX}:node-text-content-changed`,
|
|
NodeRemoved: `${PLUGIN_EVENT_PREFIX}:node-removed`,
|
|
EdgeRemoved: `${PLUGIN_EVENT_PREFIX}:edge-removed`,
|
|
OnCopy: `${PLUGIN_EVENT_PREFIX}:copy`,
|
|
NodeBBoxRequested: `${PLUGIN_EVENT_PREFIX}:node-bbox-requested`,
|
|
EdgeCenterRequested: `${PLUGIN_EVENT_PREFIX}:edge-center-requested`,
|
|
ContainingNodesRequested: `${PLUGIN_EVENT_PREFIX}:containing-nodes-requested`,
|
|
SelectionChanged: `${PLUGIN_EVENT_PREFIX}:selection-changed`,
|
|
ZoomToBbox: {
|
|
Before: `${PLUGIN_EVENT_PREFIX}:zoom-to-bbox:before`,
|
|
After: `${PLUGIN_EVENT_PREFIX}:zoom-to-bbox:after`
|
|
},
|
|
PopupMenuCreated: `${PLUGIN_EVENT_PREFIX}:popup-menu-created`,
|
|
NodeInteraction: `${PLUGIN_EVENT_PREFIX}:node-interaction`,
|
|
Undo: `${PLUGIN_EVENT_PREFIX}:undo`,
|
|
Redo: `${PLUGIN_EVENT_PREFIX}:redo`,
|
|
ReadonlyChanged: `${PLUGIN_EVENT_PREFIX}:readonly-changed`,
|
|
DataRequested: `${PLUGIN_EVENT_PREFIX}:data-requested`,
|
|
LoadData: `${PLUGIN_EVENT_PREFIX}:load-data`,
|
|
CanvasSaved: {
|
|
Before: `${PLUGIN_EVENT_PREFIX}:canvas-saved:before`,
|
|
After: `${PLUGIN_EVENT_PREFIX}:canvas-saved:after`
|
|
}
|
|
};
|
|
|
|
// src/core/canvas-patcher.ts
|
|
var import_obsidian = require("obsidian");
|
|
var import_view = require("@codemirror/view");
|
|
|
|
// node_modules/tiny-jsonc/dist/index.js
|
|
var stringOrCommentRe = /("(?:\\?[^])*?")|(\/\/.*)|(\/\*[^]*?\*\/)/g;
|
|
var stringOrTrailingCommaRe = /("(?:\\?[^])*?")|(,\s*)(?=]|})/g;
|
|
var JSONC = {
|
|
parse: (text) => {
|
|
text = String(text);
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
return JSON.parse(text.replace(stringOrCommentRe, "$1").replace(stringOrTrailingCommaRe, "$1"));
|
|
}
|
|
}
|
|
};
|
|
var dist_default = JSONC;
|
|
|
|
// src/core/canvas-patcher.ts
|
|
var CanvasPatcher = class {
|
|
constructor(plugin) {
|
|
this.plugin = plugin;
|
|
this.applyPatches();
|
|
}
|
|
async applyPatches() {
|
|
const that = this;
|
|
await new Promise((resolve) => this.plugin.app.workspace.onLayoutReady(() => resolve()));
|
|
const getCanvasView = async () => {
|
|
var _a;
|
|
const canvasLeaf = (_a = this.plugin.app.workspace.getLeavesOfType("canvas")) == null ? void 0 : _a.first();
|
|
if (!canvasLeaf)
|
|
return null;
|
|
if ((0, import_obsidian.requireApiVersion)("1.7.2"))
|
|
await canvasLeaf.loadIfDeferred();
|
|
return canvasLeaf.view;
|
|
};
|
|
let canvasView = await getCanvasView();
|
|
canvasView != null ? canvasView : canvasView = await new Promise((resolve) => {
|
|
const event = this.plugin.app.workspace.on("layout-change", async () => {
|
|
const newCanvasView = await getCanvasView();
|
|
if (!newCanvasView)
|
|
return;
|
|
resolve(newCanvasView);
|
|
this.plugin.app.workspace.offref(event);
|
|
});
|
|
this.plugin.registerEvent(event);
|
|
});
|
|
console.log("Patching canvas view:", canvasView);
|
|
PatchHelper.patchObjectPrototype(this.plugin, canvasView, {
|
|
getViewData: (_next) => function(..._args) {
|
|
var _a;
|
|
const canvasData = this.canvas.getData();
|
|
canvasData.metadata = (_a = this.canvas.metadata) != null ? _a : {};
|
|
return JSON.stringify(canvasData, null, 2);
|
|
},
|
|
setViewData: (next) => function(json, ...args) {
|
|
let validJson = json !== "" ? json : "{}";
|
|
let parsedJson;
|
|
try {
|
|
parsedJson = JSON.parse(validJson);
|
|
} catch (e) {
|
|
that.plugin.createFileSnapshot(this.file.path, json);
|
|
parsedJson = dist_default.parse(validJson);
|
|
validJson = JSON.stringify(parsedJson, null, 2);
|
|
}
|
|
const result = next.call(this, validJson, ...args);
|
|
try {
|
|
this.canvas.metadata = parsedJson.metadata;
|
|
} catch (_e) {
|
|
this.canvas.metadata = {};
|
|
}
|
|
that.triggerWorkspaceEvent(CanvasEvent.CanvasChanged, this.canvas);
|
|
return result;
|
|
}
|
|
});
|
|
PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas, {
|
|
markViewportChanged: (next) => function(...args) {
|
|
that.triggerWorkspaceEvent(CanvasEvent.ViewportChanged.Before, this);
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.ViewportChanged.After, this);
|
|
return result;
|
|
},
|
|
markMoved: (next) => function(node) {
|
|
const result = next.call(this, node);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeMoved, this, node);
|
|
return result;
|
|
},
|
|
onDoubleClick: (next) => function(event) {
|
|
const preventDefault = { value: false };
|
|
that.triggerWorkspaceEvent(CanvasEvent.DoubleClick, this, event, preventDefault);
|
|
if (!preventDefault.value)
|
|
next.call(this, event);
|
|
},
|
|
setDragging: (next) => function(dragging) {
|
|
const result = next.call(this, dragging);
|
|
that.triggerWorkspaceEvent(CanvasEvent.DraggingStateChanged, this, dragging);
|
|
return result;
|
|
},
|
|
getContainingNodes: (next) => function(bbox) {
|
|
const result = next.call(this, bbox);
|
|
that.triggerWorkspaceEvent(CanvasEvent.ContainingNodesRequested, this, bbox, result);
|
|
return result;
|
|
},
|
|
updateSelection: (next) => function(update) {
|
|
const oldSelection = new Set(this.selection);
|
|
const result = next.call(this, update);
|
|
that.triggerWorkspaceEvent(CanvasEvent.SelectionChanged, this, oldSelection, (update2) => next.call(this, update2));
|
|
return result;
|
|
},
|
|
createTextNode: (next) => function(...args) {
|
|
const node = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node);
|
|
return node;
|
|
},
|
|
createFileNode: (next) => function(...args) {
|
|
const node = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node);
|
|
return node;
|
|
},
|
|
createFileNodes: (next) => function(...args) {
|
|
const nodes = next.call(this, ...args);
|
|
nodes.forEach((node) => that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node));
|
|
return nodes;
|
|
},
|
|
createGroupNode: (next) => function(...args) {
|
|
const node = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node);
|
|
return node;
|
|
},
|
|
createLinkNode: (next) => function(...args) {
|
|
const node = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeCreated, this, node);
|
|
return node;
|
|
},
|
|
addNode: (next) => function(node) {
|
|
that.patchNode(node);
|
|
return next.call(this, node);
|
|
},
|
|
addEdge: (next) => function(edge) {
|
|
that.patchEdge(edge);
|
|
if (!this.viewportChanged)
|
|
that.triggerWorkspaceEvent(CanvasEvent.EdgeCreated, this, edge);
|
|
return next.call(this, edge);
|
|
},
|
|
removeNode: (next) => function(node) {
|
|
const result = next.call(this, node);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeRemoved, this, node);
|
|
return result;
|
|
},
|
|
removeEdge: (next) => function(edge) {
|
|
const result = next.call(this, edge);
|
|
that.triggerWorkspaceEvent(CanvasEvent.EdgeRemoved, this, edge);
|
|
return result;
|
|
},
|
|
handleCopy: (next) => function(...args) {
|
|
this.isCopying = true;
|
|
const result = next.call(this, ...args);
|
|
this.isCopying = false;
|
|
return result;
|
|
},
|
|
getSelectionData: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
if (this.isCopying)
|
|
that.triggerWorkspaceEvent(CanvasEvent.OnCopy, this, result);
|
|
return result;
|
|
},
|
|
zoomToBbox: (next) => function(bbox) {
|
|
that.triggerWorkspaceEvent(CanvasEvent.ZoomToBbox.Before, this, bbox);
|
|
const result = next.call(this, bbox);
|
|
that.triggerWorkspaceEvent(CanvasEvent.ZoomToBbox.After, this, bbox);
|
|
return result;
|
|
},
|
|
setReadonly: (next) => function(readonly) {
|
|
const result = next.call(this, readonly);
|
|
that.triggerWorkspaceEvent(CanvasEvent.ReadonlyChanged, this, readonly);
|
|
return result;
|
|
},
|
|
undo: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
this.importData(this.getData(), true);
|
|
that.triggerWorkspaceEvent(CanvasEvent.Undo, this);
|
|
return result;
|
|
},
|
|
redo: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
this.importData(this.getData(), true);
|
|
that.triggerWorkspaceEvent(CanvasEvent.Redo, this);
|
|
return result;
|
|
},
|
|
/*setData: (next: any) => function (...args: any) {
|
|
//
|
|
const result = next.call(this, ...args)
|
|
//
|
|
return result
|
|
},*/
|
|
getData: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.DataRequested, this, result);
|
|
return result;
|
|
},
|
|
importData: (next) => function(data, clearCanvas, silent) {
|
|
const targetFilePath = this.view.file.path;
|
|
const setData = (data2) => {
|
|
if (!this.view.file || this.view.file.path !== targetFilePath)
|
|
return;
|
|
this.importData(data2, true, true);
|
|
that.emitEventsForUnknownDataChanges(this);
|
|
};
|
|
if (!silent)
|
|
that.triggerWorkspaceEvent(CanvasEvent.LoadData, this, data, setData);
|
|
const result = next.call(this, data, clearCanvas);
|
|
that.emitEventsForUnknownDataChanges(this);
|
|
return result;
|
|
},
|
|
requestSave: (next) => function(...args) {
|
|
that.triggerWorkspaceEvent(CanvasEvent.CanvasSaved.Before, this);
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.CanvasSaved.After, this);
|
|
return result;
|
|
}
|
|
});
|
|
PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas.menu, {
|
|
render: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.PopupMenuCreated, this.canvas);
|
|
next.call(this);
|
|
return result;
|
|
}
|
|
});
|
|
PatchHelper.patchObjectPrototype(this.plugin, canvasView.canvas.nodeInteractionLayer, {
|
|
setTarget: (next) => function(node) {
|
|
const result = next.call(this, node);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeInteraction, this.canvas, node);
|
|
return result;
|
|
}
|
|
});
|
|
this.plugin.registerEditorExtension([import_view.EditorView.updateListener.of((update) => {
|
|
if (!update.docChanged)
|
|
return;
|
|
const editor = update.state.field(import_obsidian.editorInfoField);
|
|
const node = editor.node;
|
|
if (!node)
|
|
return;
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeTextContentChanged, node.canvas, node, update);
|
|
})]);
|
|
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
|
|
if (leaf.view.getViewType() !== "canvas")
|
|
return;
|
|
const canvasView2 = leaf.view;
|
|
canvasView2.leaf.rebuildView();
|
|
});
|
|
}
|
|
patchNode(node) {
|
|
const that = this;
|
|
PatchHelper.patchObjectInstance(this.plugin, node, {
|
|
setData: (next) => function(data, addHistory) {
|
|
const result = next.call(this, data);
|
|
if (node.initialized && !node.isDirty) {
|
|
node.isDirty = true;
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeChanged, this.canvas, node);
|
|
delete node.isDirty;
|
|
}
|
|
this.canvas.data = this.canvas.getData();
|
|
this.canvas.view.requestSave();
|
|
if (addHistory)
|
|
this.canvas.pushHistory(this.canvas.getData());
|
|
return result;
|
|
},
|
|
getBBox: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.NodeBBoxRequested, this.canvas, node, result);
|
|
return result;
|
|
}
|
|
});
|
|
this.runAfterInitialized(node, () => {
|
|
this.triggerWorkspaceEvent(CanvasEvent.NodeAdded, node.canvas, node);
|
|
this.triggerWorkspaceEvent(CanvasEvent.NodeChanged, node.canvas, node);
|
|
});
|
|
}
|
|
patchEdge(edge) {
|
|
const that = this;
|
|
PatchHelper.patchObjectInstance(this.plugin, edge, {
|
|
setData: (next) => function(data, addHistory) {
|
|
const result = next.call(this, data);
|
|
if (edge.initialized && !edge.isDirty) {
|
|
edge.isDirty = true;
|
|
that.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, this.canvas, edge);
|
|
delete edge.isDirty;
|
|
}
|
|
this.canvas.data = this.canvas.getData();
|
|
this.canvas.view.requestSave();
|
|
if (addHistory)
|
|
this.canvas.pushHistory(this.canvas.getData());
|
|
return result;
|
|
},
|
|
render: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, this.canvas, edge);
|
|
return result;
|
|
},
|
|
getCenter: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
that.triggerWorkspaceEvent(CanvasEvent.EdgeCenterRequested, this.canvas, edge, result);
|
|
return result;
|
|
}
|
|
});
|
|
this.runAfterInitialized(edge, () => {
|
|
this.triggerWorkspaceEvent(CanvasEvent.EdgeAdded, edge.canvas, edge);
|
|
});
|
|
}
|
|
runAfterInitialized(canvasElement, onReady) {
|
|
if (canvasElement.initialized) {
|
|
onReady();
|
|
return;
|
|
}
|
|
const that = this;
|
|
const uninstall = around(canvasElement, {
|
|
initialize: (next) => function(...args) {
|
|
const result = next.call(this, ...args);
|
|
onReady();
|
|
uninstall();
|
|
return result;
|
|
}
|
|
});
|
|
that.plugin.register(uninstall);
|
|
}
|
|
emitEventsForUnknownDataChanges(canvas) {
|
|
canvas.nodes.forEach((node) => this.runAfterInitialized(node, () => {
|
|
this.triggerWorkspaceEvent(CanvasEvent.NodeChanged, node.canvas, node);
|
|
}));
|
|
canvas.edges.forEach((edge) => this.runAfterInitialized(edge, () => {
|
|
this.triggerWorkspaceEvent(CanvasEvent.EdgeChanged, edge.canvas, edge);
|
|
}));
|
|
}
|
|
triggerWorkspaceEvent(event, ...args) {
|
|
this.plugin.app.workspace.trigger(event, ...args);
|
|
}
|
|
};
|
|
|
|
// src/utils/icons-helper.ts
|
|
var import_obsidian2 = require("obsidian");
|
|
var CUSTOM_ICONS = {
|
|
"shape-pill": `<rect rx="31.25" height="62.5" width="93.75" y="18.75" x="3.125" stroke-width="8.333" stroke="currentColor" fill="transparent"/>`,
|
|
"shape-parallelogram": `<rect transform="skewX(-20)" rx="5" height="50" width="70" y="25" x="35" stroke-width="8.333" stroke="currentColor" fill="transparent"/>`,
|
|
"shape-predefined-process": `
|
|
<g stroke-width="2" stroke="currentColor" fill="none" transform="matrix(4.166667,0,0,4.166667,0,0)">
|
|
<path d="M 4.999687 3 L 19.000312 3 C 20.104688 3 21 3.895312 21 4.999687 L 21 19.000312 C 21 20.104688 20.104688 21 19.000312 21 L 4.999687 21 C 3.895312 21 3 20.104688 3 19.000312 L 3 4.999687 C 3 3.895312 3.895312 3 4.999687 3 Z M 4.999687 3 "/>
|
|
<path d="M 7 3 L 7 21 "/>
|
|
<path d="M 17 3 L 17 21 "/>
|
|
</g>
|
|
`,
|
|
"shape-document": `<path transform="translate(0, 5)" stroke="currentColor" fill="none" stroke-width="8.333" d="M83.75 25C85.82 25 87.5 26.68 87.5 28.75L87.5 64.375Q68.75 54.25 50 64.375 31.25 74.5 12.5 64.375L12.5 30.625 12.5 28.75C12.5 26.68 14.18 25 16.25 25Z"/>`,
|
|
"shape-database": `
|
|
<g transform="translate(20, 20)" stroke-width="8.333" stroke="currentColor" fill="none">
|
|
<path d="M 1 51 L 1 11 C 1 5.48 14.43 1 31 1 C 47.57 1 61 5.48 61 11 L 61 51 C 61 56.52 47.57 61 31 61 C 14.43 61 1 56.52 1 51 Z"/>
|
|
<path d="M 1 11 C 1 16.52 14.43 21 31 21 C 47.57 21 61 16.52 61 11"/>
|
|
</g>
|
|
`,
|
|
"border-solid": `<path stroke="currentColor" fill="none" stroke-width="8.333" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
|
|
"border-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.333" stroke-dasharray="13.7" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
|
|
"border-dotted": `<path stroke="currentColor" fill="none" stroke-width="8.333" stroke-dasharray="8.7" d="M91.6667 45.8333v4.1667c0 2.0833-2.0833 4.1667-4.1667 4.1667H12.5c-2.0833 0-4.1667-2.0833-4.1667-4.1667v-4.1667"/>`,
|
|
"path-solid": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
|
|
"path-dotted": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="8.8" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
|
|
"path-short-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="15" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
|
|
"path-long-dashed": `<path stroke="currentColor" fill="none" stroke-width="8.5" stroke-dasharray="23" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
|
|
"arrow-triangle": `<path stroke="currentColor" fill="currentColor" d="M 15 10 L 85 50 L 15 90 Z"/>`,
|
|
"arrow-triangle-outline": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 15 10 L 85 50 L 15 90 Z"/>`,
|
|
"arrow-thin-triangle": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 15 10 L 85 50 L 15 90"/>`,
|
|
"arrow-halved-triangle": `<path stroke="currentColor" fill="currentColor" d="M 15 50 L 85 50 L 15 90 Z"/>`,
|
|
"arrow-diamond": `<path stroke="currentColor" fill="currentColor" d="M 50 0 L 100 50 L 50 100 L 0 50 Z"/>`,
|
|
"arrow-diamond-outline": `<path stroke="currentColor" stroke-width="8.5" fill="none" d="M 50 0 L 100 50 L 50 100 L 0 50 Z"/>`,
|
|
"arrow-circle": `<circle stroke="currentColor" fill="currentColor" cx="50" cy="50" r="45"/>`,
|
|
"arrow-circle-outline": `<circle stroke="currentColor" stroke-width="8.5" fill="none" cx="50" cy="50" r="45"/>`,
|
|
"pathfinding-method-bezier": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M37.5 79.1667h35.4167a14.5833 14.5833 90 000-29.1667h-45.8333a14.5833 14.5833 90 010-29.1667H62.5"/>`,
|
|
"pathfinding-method-square": `<path stroke="currentColor" fill="none" stroke-width="8.5" d="M72.9167 79.1667 72.9167 50 27.0833 50 27.0833 20.8333"/>`
|
|
};
|
|
var IconsHelper = class {
|
|
static addIcons() {
|
|
for (const [id, svg] of Object.entries(CUSTOM_ICONS)) {
|
|
(0, import_obsidian2.addIcon)(id, svg);
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/utils/debug-helper.ts
|
|
var DebugHelper = class {
|
|
constructor(plugin) {
|
|
this.plugin = plugin;
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeAdded,
|
|
(_canvas, _node) => console.count("\u{1F7E2} NodeAdded")
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeChanged,
|
|
(_canvas, _node) => console.count("\u{1F7E1} NodeChanged")
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeAdded,
|
|
(_canvas, _edge) => console.count("\u{1F7E2} EdgeAdded")
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeChanged,
|
|
(_canvas, _edge) => console.count("\u{1F7E1} EdgeChanged")
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/settings.ts
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/canvas-extensions/advanced-styles/style-config.ts
|
|
var BUILTIN_NODE_STYLE_ATTRIBUTES = [
|
|
{
|
|
datasetKey: "textAlign",
|
|
label: "Text Alignment",
|
|
nodeTypes: ["text"],
|
|
options: [
|
|
{
|
|
icon: "align-left",
|
|
label: "Left (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "align-center",
|
|
label: "Center",
|
|
value: "center"
|
|
},
|
|
{
|
|
icon: "align-right",
|
|
label: "Right",
|
|
value: "right"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
datasetKey: "shape",
|
|
label: "Shape",
|
|
nodeTypes: ["text"],
|
|
options: [
|
|
{
|
|
icon: "rectangle-horizontal",
|
|
label: "Round Rectangle (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "shape-pill",
|
|
label: "Pill",
|
|
value: "pill"
|
|
},
|
|
{
|
|
icon: "diamond",
|
|
label: "Diamond",
|
|
value: "diamond"
|
|
},
|
|
{
|
|
icon: "shape-parallelogram",
|
|
label: "Parallelogram",
|
|
value: "parallelogram"
|
|
},
|
|
{
|
|
icon: "circle",
|
|
label: "Circle",
|
|
value: "circle"
|
|
},
|
|
{
|
|
icon: "shape-predefined-process",
|
|
label: "Predefined Process",
|
|
value: "predefined-process"
|
|
},
|
|
{
|
|
icon: "shape-document",
|
|
label: "Document",
|
|
value: "document"
|
|
},
|
|
{
|
|
icon: "shape-database",
|
|
label: "Database",
|
|
value: "database"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
datasetKey: "border",
|
|
label: "Border",
|
|
options: [
|
|
{
|
|
icon: "border-solid",
|
|
label: "Solid (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "border-dashed",
|
|
label: "Dashed",
|
|
value: "dashed"
|
|
},
|
|
{
|
|
icon: "border-dotted",
|
|
label: "Dotted",
|
|
value: "dotted"
|
|
},
|
|
{
|
|
icon: "eye-off",
|
|
label: "Invisible",
|
|
value: "invisible"
|
|
}
|
|
]
|
|
}
|
|
];
|
|
var BUILTIN_EDGE_STYLE_ATTRIBUTES = [
|
|
{
|
|
datasetKey: "path",
|
|
label: "Path Style",
|
|
options: [
|
|
{
|
|
icon: "path-solid",
|
|
label: "Solid (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "path-dotted",
|
|
label: "Dotted",
|
|
value: "dotted"
|
|
},
|
|
{
|
|
icon: "path-short-dashed",
|
|
label: "Short Dashed",
|
|
value: "short-dashed"
|
|
},
|
|
{
|
|
icon: "path-long-dashed",
|
|
label: "Long Dashed",
|
|
value: "long-dashed"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
datasetKey: "arrow",
|
|
label: "Arrow Style",
|
|
options: [
|
|
{
|
|
icon: "arrow-triangle",
|
|
label: "Triangle (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "arrow-triangle-outline",
|
|
label: "Triangle Outline",
|
|
value: "triangle-outline"
|
|
},
|
|
{
|
|
icon: "arrow-thin-triangle",
|
|
label: "Thin Triangle",
|
|
value: "thin-triangle"
|
|
},
|
|
{
|
|
icon: "arrow-halved-triangle",
|
|
label: "Halved Triangle",
|
|
value: "halved-triangle"
|
|
},
|
|
{
|
|
icon: "arrow-diamond",
|
|
label: "Diamond",
|
|
value: "diamond"
|
|
},
|
|
{
|
|
icon: "arrow-diamond-outline",
|
|
label: "Diamond Outline",
|
|
value: "diamond-outline"
|
|
},
|
|
{
|
|
icon: "arrow-circle",
|
|
label: "Circle",
|
|
value: "circle"
|
|
},
|
|
{
|
|
icon: "arrow-circle-outline",
|
|
label: "Circle Outline",
|
|
value: "circle-outline"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
datasetKey: "pathfindingMethod",
|
|
label: "Pathfinding Method",
|
|
options: [
|
|
{
|
|
icon: "pathfinding-method-bezier",
|
|
label: "Bezier (default)",
|
|
value: null
|
|
},
|
|
{
|
|
icon: "slash",
|
|
label: "Direct",
|
|
value: "direct"
|
|
},
|
|
{
|
|
icon: "pathfinding-method-square",
|
|
label: "Square",
|
|
value: "square"
|
|
},
|
|
{
|
|
icon: "map",
|
|
label: "A*",
|
|
value: "a-star"
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
// src/settings.ts
|
|
var NODE_TYPES_ON_DOUBLE_CLICK = {
|
|
"text": "Text",
|
|
"file": "File"
|
|
};
|
|
var EDGE_LINE_DIRECTIONS = {
|
|
"nondirectional": "Nondirectional",
|
|
"unidirectional": "Unidirectional",
|
|
"bidirectional": "Bidirectional"
|
|
};
|
|
var DEFAULT_SETTINGS = {
|
|
nodeTypeOnDoubleClick: "text",
|
|
alignNewNodesToGrid: true,
|
|
defaultTextNodeWidth: 260,
|
|
defaultTextNodeHeight: 60,
|
|
defaultFileNodeWidth: 400,
|
|
defaultFileNodeHeight: 400,
|
|
minNodeSize: 60,
|
|
disableFontSizeRelativeToZoom: false,
|
|
combineCustomStylesInDropdown: false,
|
|
nodeStylingFeatureEnabled: true,
|
|
customNodeStyleAttributes: [],
|
|
defaultTextNodeStyleAttributes: {},
|
|
edgesStylingFeatureEnabled: true,
|
|
customEdgeStyleAttributes: [],
|
|
defaultEdgeLineDirection: "unidirectional",
|
|
defaultEdgeStyleAttributes: {},
|
|
edgeStyleDirectRotateArrow: false,
|
|
edgeStylePathfinderGridResolution: 10,
|
|
edgeStylePathfinderPathLiveUpdate: true,
|
|
edgeStylePathfinderPathRounded: true,
|
|
commandsFeatureEnabled: true,
|
|
zoomToClonedNode: true,
|
|
cloneNodeMargin: 20,
|
|
expandNodeStepSize: 20,
|
|
betterReadonlyEnabled: true,
|
|
disableNodePopup: false,
|
|
disableZoom: false,
|
|
disablePan: false,
|
|
autoResizeNodeFeatureEnabled: true,
|
|
autoResizeNodeSnapToGrid: true,
|
|
collapsibleGroupsFeatureEnabled: true,
|
|
collapsedGroupPreviewOnDrag: true,
|
|
focusModeFeatureEnabled: true,
|
|
presentationFeatureEnabled: true,
|
|
showSetStartNodeInPopup: false,
|
|
defaultSlideSize: "1200x675",
|
|
wrapInSlidePadding: 20,
|
|
resetViewportOnPresentationEnd: true,
|
|
useArrowKeysToChangeSlides: true,
|
|
usePgUpPgDownKeysToChangeSlides: true,
|
|
zoomToSlideWithoutPadding: true,
|
|
slideTransitionAnimationDuration: 0.5,
|
|
slideTransitionAnimationIntensity: 1.25,
|
|
canvasEncapsulationEnabled: true,
|
|
portalsFeatureEnabled: true,
|
|
maintainClosedPortalSize: true,
|
|
showEdgesIntoDisabledPortals: true
|
|
};
|
|
var _SettingsManager = class _SettingsManager {
|
|
constructor(plugin) {
|
|
this.plugin = plugin;
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.plugin.loadData());
|
|
this.plugin.app.workspace.trigger(_SettingsManager.SETTINGS_CHANGED_EVENT);
|
|
}
|
|
async saveSettings() {
|
|
await this.plugin.saveData(this.settings);
|
|
}
|
|
getSetting(key) {
|
|
return this.settings[key];
|
|
}
|
|
async setSetting(data) {
|
|
this.settings = Object.assign(this.settings, data);
|
|
await this.saveSettings();
|
|
this.plugin.app.workspace.trigger(_SettingsManager.SETTINGS_CHANGED_EVENT);
|
|
}
|
|
addSettingsTab() {
|
|
this.settingsTab = new AdvancedCanvasPluginSettingTab(this.plugin, this);
|
|
this.plugin.addSettingTab(this.settingsTab);
|
|
}
|
|
};
|
|
_SettingsManager.SETTINGS_CHANGED_EVENT = "advanced-canvas:settings-changed";
|
|
var SettingsManager = _SettingsManager;
|
|
var AdvancedCanvasPluginSettingTab = class extends import_obsidian3.PluginSettingTab {
|
|
constructor(plugin, settingsManager) {
|
|
super(plugin.app, plugin);
|
|
this.settingsManager = settingsManager;
|
|
}
|
|
display() {
|
|
let { containerEl } = this;
|
|
containerEl.empty();
|
|
new import_obsidian3.Setting(containerEl).setHeading().setClass("ac-settings-heading").setName("General");
|
|
new import_obsidian3.Setting(containerEl).setName("Node type on double click").setDesc("The type of node that will be created when double clicking on the canvas.").addDropdown(
|
|
(dropdown) => dropdown.addOptions(NODE_TYPES_ON_DOUBLE_CLICK).setValue(this.settingsManager.getSetting("nodeTypeOnDoubleClick")).onChange(async (value) => await this.settingsManager.setSetting({ nodeTypeOnDoubleClick: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Always align new nodes to grid").setDesc("When enabled, new nodes will be aligned to the grid.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("alignNewNodesToGrid")).onChange(async (value) => await this.settingsManager.setSetting({ alignNewNodesToGrid: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default text node width").setDesc("The default width of a text node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("defaultTextNodeWidth").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultTextNodeWidth: Math.max(1, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default text node height").setDesc("The default height of a text node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("defaultTextNodeHeight").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultTextNodeHeight: Math.max(1, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default file node width").setDesc("The default width of a file node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("defaultFileNodeWidth").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultFileNodeWidth: Math.max(1, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default file node height").setDesc("The default height of a file node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("defaultFileNodeHeight").toString()).onChange(async (value) => await this.settingsManager.setSetting({ defaultFileNodeHeight: Math.max(1, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Minimum node size").setDesc("The minimum size of a node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("minNodeSize").toString()).onChange(async (value) => await this.settingsManager.setSetting({ minNodeSize: Math.max(1, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Disable font size relative to zoom").setDesc("When enabled, the font size of e.g. group node titles and edge labels will not increase when zooming out.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("disableFontSizeRelativeToZoom")).onChange(async (value) => await this.settingsManager.setSetting({ disableFontSizeRelativeToZoom: value }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Combine custom styles",
|
|
"Combine all style attributes of Advanced Canvas in a single dropdown.",
|
|
"combineCustomStylesInDropdown"
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Node styling",
|
|
"Style your nodes with different shapes and borders.",
|
|
"nodeStylingFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Custom node style settings").setDesc("Add custom style settings for nodes. (Go to GitHub for more information)").addButton(
|
|
(button) => button.setButtonText("Open Tutorial").onClick(() => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles"))
|
|
);
|
|
const allNodeStyleAttributes = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.settingsManager.getSetting("customNodeStyleAttributes")].filter((setting) => {
|
|
var _a;
|
|
return setting.nodeTypes === void 0 || ((_a = setting.nodeTypes) == null ? void 0 : _a.includes("text"));
|
|
});
|
|
this.createDefaultStylesSection(containerEl, "Default text node style attributes", "defaultTextNodeStyleAttributes", allNodeStyleAttributes);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Edges styling",
|
|
"Style your edges with different path styles.",
|
|
"edgesStylingFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Custom edge style settings").setDesc("Add custom style settings for edges. (Go to GitHub for more information)").addButton(
|
|
(button) => button.setButtonText("Open Tutorial").onClick(() => window.open("https://github.com/Developer-Mike/obsidian-advanced-canvas/blob/main/README.md#custom-styles"))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default edge line direction").setDesc("The default line direction of an edge.").addDropdown(
|
|
(dropdown) => dropdown.addOptions(EDGE_LINE_DIRECTIONS).setValue(this.settingsManager.getSetting("defaultEdgeLineDirection")).onChange(async (value) => await this.settingsManager.setSetting({ defaultEdgeLineDirection: value }))
|
|
);
|
|
this.createDefaultStylesSection(containerEl, "Default edge style attributes", "defaultEdgeStyleAttributes", [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.settingsManager.getSetting("customEdgeStyleAttributes")]);
|
|
new import_obsidian3.Setting(containerEl).setName('Rotate arrow if pathfinding method is "Direct"').setDesc('When enabled, the arrow will be rotated to the direction of the edge if the pathfinding method is set to "Direct".').addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStyleDirectRotateArrow")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStyleDirectRotateArrow: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("A* grid resolution").setDesc("The resolution of the grid when using the A* path style. The lower the value, the more precise the path will be. But it will also take longer to calculate.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("edgeStylePathfinderGridResolution").toString()).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderGridResolution: Math.max(5, parseInt(value)) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Live update A* path").setDesc("When enabled, the A* path style will be updated live while dragging the edge.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStylePathfinderPathLiveUpdate")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderPathLiveUpdate: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("A* rounded path").setDesc("When enabled, the A* path style will be rounded.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("edgeStylePathfinderPathRounded")).onChange(async (value) => await this.settingsManager.setSetting({ edgeStylePathfinderPathRounded: value }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Extended commands",
|
|
"Add more commands to the canvas.",
|
|
"commandsFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Zoom to cloned node").setDesc("When enabled, the canvas will zoom to the cloned node.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("zoomToClonedNode")).onChange(async (value) => await this.settingsManager.setSetting({ zoomToClonedNode: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Clone node margin").setDesc("The margin between the cloned node and the source node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("cloneNodeMargin").toString()).onChange(async (value) => await this.settingsManager.setSetting({ cloneNodeMargin: parseInt(value) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Expand node step size").setDesc("The step size for expanding the node.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("expandNodeStepSize").toString()).onChange(async (value) => await this.settingsManager.setSetting({ expandNodeStepSize: parseInt(value) }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Better readonly",
|
|
"Improve the readonly mode.",
|
|
"betterReadonlyEnabled"
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Auto resize node",
|
|
"Automatically resize the height of a node to fit the content.",
|
|
"autoResizeNodeFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Snap to grid").setDesc("When enabled, the height of the node will snap to the grid.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("autoResizeNodeSnapToGrid")).onChange(async (value) => await this.settingsManager.setSetting({ autoResizeNodeSnapToGrid: value }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Collapsible groups",
|
|
"Group nodes can be collapsed and expanded to keep the canvas organized.",
|
|
"collapsibleGroupsFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Collapsed group preview on drag").setDesc("When enabled, a group that is collapsed show its border while dragging a node.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("collapsedGroupPreviewOnDrag")).onChange(async (value) => await this.settingsManager.setSetting({ collapsedGroupPreviewOnDrag: value }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Focus mode",
|
|
"Focus on a single node and blur all other nodes.",
|
|
"focusModeFeatureEnabled"
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Presentations",
|
|
"Create a presentation from your canvas.",
|
|
"presentationFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName('Show "Set Start Node" in node popup').setDesc("If turned off, you can still set the start node using the corresponding command.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("showSetStartNodeInPopup")).onChange(async (value) => await this.settingsManager.setSetting({ showSetStartNodeInPopup: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Default slide ratio").setDesc("The default ratio of the slide. For example, 16:9 is 1200x675 and 3:2 is 1350x900.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("defaultSlideSize")).onChange(async (value) => await this.settingsManager.setSetting({ defaultSlideSize: value.replace(" ", "") }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Wrap in slide padding").setDesc("The padding of the slide when wrapping the canvas in a slide.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("wrapInSlidePadding").toString()).onChange(async (value) => await this.settingsManager.setSetting({ wrapInSlidePadding: parseInt(value) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Reset viewport on presentation end").setDesc("When enabled, the viewport will be reset to the original position after the presentation ends.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("resetViewportOnPresentationEnd")).onChange(async (value) => await this.settingsManager.setSetting({ resetViewportOnPresentationEnd: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Use arrow keys to change slides").setDesc("When enabled, you can use the arrow keys to change slides in presentation mode.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("useArrowKeysToChangeSlides")).onChange(async (value) => await this.settingsManager.setSetting({ useArrowKeysToChangeSlides: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Use PgUp/PgDown keys to change slides").setDesc("When enabled, you can use the PgUp/PgDown keys to change slides in presentation mode (Makes the presentation mode compatible with most presentation remotes).").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("usePgUpPgDownKeysToChangeSlides")).onChange(async (value) => await this.settingsManager.setSetting({ usePgUpPgDownKeysToChangeSlides: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Zoom to slide without padding").setDesc("When enabled, the canvas will zoom to the slide without padding.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("zoomToSlideWithoutPadding")).onChange(async (value) => await this.settingsManager.setSetting({ zoomToSlideWithoutPadding: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Slide transition animation duration").setDesc("The duration of the slide transition animation in seconds. Set to 0 to disable the animation.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("slideTransitionAnimationDuration").toString()).onChange(async (value) => await this.settingsManager.setSetting({ slideTransitionAnimationDuration: parseFloat(value) }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Slide transition animation intensity").setDesc("The intensity of the slide transition animation. The higher the value, the more the canvas will zoom out before zooming in on the next slide.").addText(
|
|
(text) => text.setValue(this.settingsManager.getSetting("slideTransitionAnimationIntensity").toString()).onChange(async (value) => await this.settingsManager.setSetting({ slideTransitionAnimationIntensity: parseFloat(value) }))
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Canvas encapsulation",
|
|
"Encapsulate a selection of nodes and edges into a new canvas.",
|
|
"canvasEncapsulationEnabled"
|
|
);
|
|
this.createFeatureHeading(
|
|
containerEl,
|
|
"Portals",
|
|
"Create portals to other canvases.",
|
|
"portalsFeatureEnabled"
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Maintain closed portal size").setDesc("When enabled, closing a portal will change the size of the portal to the original, closed size.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("maintainClosedPortalSize")).onChange(async (value) => await this.settingsManager.setSetting({ maintainClosedPortalSize: value }))
|
|
);
|
|
new import_obsidian3.Setting(containerEl).setName("Show edges into disabled portals").setDesc("When enabled, edges into disabled portals will be shown by an edge to the portal node.").addToggle(
|
|
(toggle) => toggle.setValue(this.settingsManager.getSetting("showEdgesIntoDisabledPortals")).onChange(async (value) => await this.settingsManager.setSetting({ showEdgesIntoDisabledPortals: value }))
|
|
);
|
|
const kofiButton = document.createElement("a");
|
|
kofiButton.classList.add("kofi-button");
|
|
kofiButton.href = "https://ko-fi.com/X8X27IA08";
|
|
kofiButton.target = "_blank";
|
|
const kofiImage = document.createElement("img");
|
|
kofiImage.src = "data:image/webp;base64,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";
|
|
kofiButton.appendChild(kofiImage);
|
|
containerEl.appendChild(kofiButton);
|
|
}
|
|
createFeatureHeading(containerEl, label, description, settingsKey) {
|
|
return new import_obsidian3.Setting(containerEl).setHeading().setClass("ac-settings-heading").setName(label).setDesc(description).addToggle(
|
|
(toggle) => toggle.setTooltip("Requires a reload to take effect.").setValue(this.settingsManager.getSetting(settingsKey)).onChange(async (value) => {
|
|
await this.settingsManager.setSetting({ [settingsKey]: value });
|
|
new import_obsidian3.Notice("Reload obsidian to apply the changes.");
|
|
})
|
|
);
|
|
}
|
|
createDefaultStylesSection(containerEl, label, settingsKey, allStylableAttributes) {
|
|
const defaultNodeStylesEl = document.createElement("details");
|
|
defaultNodeStylesEl.classList.add("setting-item");
|
|
const defaultNodeStylesSummaryEl = document.createElement("summary");
|
|
defaultNodeStylesSummaryEl.textContent = label;
|
|
defaultNodeStylesEl.appendChild(defaultNodeStylesSummaryEl);
|
|
for (const stylableAttribute of allStylableAttributes) {
|
|
new import_obsidian3.Setting(defaultNodeStylesEl).setName(stylableAttribute.label).addDropdown(
|
|
(dropdown) => {
|
|
var _a;
|
|
return dropdown.addOptions(Object.fromEntries(stylableAttribute.options.map((option) => [option.value, option.label]))).setValue((_a = this.settingsManager.getSetting(settingsKey)[stylableAttribute.datasetKey]) != null ? _a : "null").onChange(async (value) => {
|
|
const newValue = this.settingsManager.getSetting(settingsKey);
|
|
if (value === "null")
|
|
delete newValue[stylableAttribute.datasetKey];
|
|
else
|
|
newValue[stylableAttribute.datasetKey] = value;
|
|
await this.settingsManager.setSetting({
|
|
[settingsKey]: newValue
|
|
});
|
|
});
|
|
}
|
|
);
|
|
}
|
|
containerEl.appendChild(defaultNodeStylesEl);
|
|
}
|
|
};
|
|
|
|
// src/windows-manager.ts
|
|
var WindowsManager = class {
|
|
constructor(plugin) {
|
|
this.windows = [window];
|
|
this.plugin = plugin;
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
"window-open",
|
|
(_win, window2) => this.windows.push(window2)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
"window-close",
|
|
(_win, window2) => this.windows = this.windows.filter((w) => w !== window2)
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/utils/canvas-helper.ts
|
|
var import_obsidian4 = require("obsidian");
|
|
|
|
// src/utils/bbox-helper.ts
|
|
var BBoxHelper = class {
|
|
static combineBBoxes(bboxes) {
|
|
let minX = Infinity;
|
|
let minY = Infinity;
|
|
let maxX = -Infinity;
|
|
let maxY = -Infinity;
|
|
for (let bbox of bboxes) {
|
|
minX = Math.min(minX, bbox.minX);
|
|
minY = Math.min(minY, bbox.minY);
|
|
maxX = Math.max(maxX, bbox.maxX);
|
|
maxY = Math.max(maxY, bbox.maxY);
|
|
}
|
|
return { minX, minY, maxX, maxY };
|
|
}
|
|
static scaleBBox(bbox, scale) {
|
|
let diffX = (scale - 1) * (bbox.maxX - bbox.minX);
|
|
let diffY = (scale - 1) * (bbox.maxY - bbox.minY);
|
|
return {
|
|
minX: bbox.minX - diffX / 2,
|
|
maxX: bbox.maxX + diffX / 2,
|
|
minY: bbox.minY - diffY / 2,
|
|
maxY: bbox.maxY + diffY / 2
|
|
};
|
|
}
|
|
static insideBBox(position, bbox, canTouchEdge) {
|
|
var _a, _b, _c, _d;
|
|
const providedBBox = {
|
|
minX: (_a = position.minX) != null ? _a : position.x,
|
|
minY: (_b = position.minY) != null ? _b : position.y,
|
|
maxX: (_c = position.maxX) != null ? _c : position.x,
|
|
maxY: (_d = position.maxY) != null ? _d : position.y
|
|
};
|
|
return canTouchEdge ? providedBBox.minX >= bbox.minX && providedBBox.maxX <= bbox.maxX && providedBBox.minY >= bbox.minY && providedBBox.maxY <= bbox.maxY : providedBBox.minX > bbox.minX && providedBBox.maxX < bbox.maxX && providedBBox.minY > bbox.minY && providedBBox.maxY < bbox.maxY;
|
|
}
|
|
static enlargeBBox(bbox, padding) {
|
|
return {
|
|
minX: bbox.minX - padding,
|
|
minY: bbox.minY - padding,
|
|
maxX: bbox.maxX + padding,
|
|
maxY: bbox.maxY + padding
|
|
};
|
|
}
|
|
static moveInDirection(position, side, distance) {
|
|
switch (side) {
|
|
case "top":
|
|
return { x: position.x, y: position.y - distance };
|
|
case "right":
|
|
return { x: position.x + distance, y: position.y };
|
|
case "bottom":
|
|
return { x: position.x, y: position.y + distance };
|
|
case "left":
|
|
return { x: position.x - distance, y: position.y };
|
|
}
|
|
}
|
|
static getCenterOfBBoxSide(bbox, side) {
|
|
switch (side) {
|
|
case "top":
|
|
return { x: (bbox.minX + bbox.maxX) / 2, y: bbox.minY };
|
|
case "right":
|
|
return { x: bbox.maxX, y: (bbox.minY + bbox.maxY) / 2 };
|
|
case "bottom":
|
|
return { x: (bbox.minX + bbox.maxX) / 2, y: bbox.maxY };
|
|
case "left":
|
|
return { x: bbox.minX, y: (bbox.minY + bbox.maxY) / 2 };
|
|
}
|
|
}
|
|
static getSideVector(side) {
|
|
switch (side) {
|
|
case "top":
|
|
return { x: 0, y: 1 };
|
|
case "right":
|
|
return { x: 1, y: 0 };
|
|
case "bottom":
|
|
return { x: 0, y: -1 };
|
|
case "left":
|
|
return { x: -1, y: 0 };
|
|
default:
|
|
return { x: 0, y: 0 };
|
|
}
|
|
}
|
|
static isHorizontal(side) {
|
|
return side === "left" || side === "right";
|
|
}
|
|
static direction(side) {
|
|
return side === "right" || side === "bottom" ? 1 : -1;
|
|
}
|
|
};
|
|
|
|
// src/utils/canvas-helper.ts
|
|
var _CanvasHelper = class _CanvasHelper {
|
|
static canvasCommand(plugin, check, run) {
|
|
return (checking) => {
|
|
const canvas = plugin.getCurrentCanvas();
|
|
if (checking)
|
|
return canvas !== null && check(canvas);
|
|
if (canvas)
|
|
run(canvas);
|
|
return true;
|
|
};
|
|
}
|
|
static createControlMenuButton(menuOption) {
|
|
const quickSetting = document.createElement("div");
|
|
if (menuOption.id)
|
|
quickSetting.id = menuOption.id;
|
|
quickSetting.classList.add("canvas-control-item");
|
|
(0, import_obsidian4.setIcon)(quickSetting, menuOption.icon);
|
|
(0, import_obsidian4.setTooltip)(quickSetting, menuOption.label, { placement: "left" });
|
|
quickSetting.addEventListener("click", () => {
|
|
var _a;
|
|
return (_a = menuOption.callback) == null ? void 0 : _a.call(menuOption);
|
|
});
|
|
return quickSetting;
|
|
}
|
|
static addControlMenuButton(controlGroup, element) {
|
|
var _a;
|
|
if (element.id)
|
|
(_a = controlGroup.querySelector(`#${element.id}`)) == null ? void 0 : _a.remove();
|
|
controlGroup.appendChild(element);
|
|
}
|
|
static createCardMenuOption(canvas, menuOption, previewNodeSize, onPlaced) {
|
|
const menuOptionElement = document.createElement("div");
|
|
if (menuOption.id)
|
|
menuOptionElement.id = menuOption.id;
|
|
menuOptionElement.classList.add("canvas-card-menu-button");
|
|
menuOptionElement.classList.add("mod-draggable");
|
|
(0, import_obsidian4.setIcon)(menuOptionElement, menuOption.icon);
|
|
(0, import_obsidian4.setTooltip)(menuOptionElement, menuOption.label, { placement: "top" });
|
|
menuOptionElement.addEventListener("click", (_e) => {
|
|
onPlaced(canvas, this.getCenterCoordinates(canvas, previewNodeSize()));
|
|
});
|
|
menuOptionElement.addEventListener("pointerdown", (e) => {
|
|
canvas.dragTempNode(e, previewNodeSize(), (pos) => {
|
|
canvas.deselectAll();
|
|
onPlaced(canvas, pos);
|
|
});
|
|
});
|
|
return menuOptionElement;
|
|
}
|
|
static addCardMenuOption(canvas, element) {
|
|
var _a;
|
|
if (element.id)
|
|
(_a = canvas == null ? void 0 : canvas.cardMenuEl.querySelector(`#${element.id}`)) == null ? void 0 : _a.remove();
|
|
canvas == null ? void 0 : canvas.cardMenuEl.appendChild(element);
|
|
}
|
|
static createPopupMenuOption(menuOption) {
|
|
const menuOptionElement = document.createElement("button");
|
|
if (menuOption.id)
|
|
menuOptionElement.id = menuOption.id;
|
|
menuOptionElement.classList.add("clickable-icon");
|
|
(0, import_obsidian4.setIcon)(menuOptionElement, menuOption.icon);
|
|
(0, import_obsidian4.setTooltip)(menuOptionElement, menuOption.label, { placement: "top" });
|
|
menuOptionElement.addEventListener("click", () => {
|
|
var _a;
|
|
return (_a = menuOption.callback) == null ? void 0 : _a.call(menuOption);
|
|
});
|
|
return menuOptionElement;
|
|
}
|
|
static createExpandablePopupMenuOption(menuOption, subMenuOptions) {
|
|
const menuOptionElement = this.createPopupMenuOption({
|
|
...menuOption,
|
|
callback: () => {
|
|
var _a, _b, _c;
|
|
const submenuId = `${menuOption.id}-submenu`;
|
|
if (menuOptionElement.classList.contains("is-active")) {
|
|
menuOptionElement.classList.remove("is-active");
|
|
(_b = (_a = menuOptionElement.parentElement) == null ? void 0 : _a.querySelector(`#${submenuId}`)) == null ? void 0 : _b.remove();
|
|
return;
|
|
}
|
|
menuOptionElement.classList.add("is-active");
|
|
const submenu = document.createElement("div");
|
|
submenu.id = submenuId;
|
|
submenu.classList.add("canvas-submenu");
|
|
for (const subMenuOption of subMenuOptions) {
|
|
const subMenuOptionElement = this.createPopupMenuOption(subMenuOption);
|
|
submenu.appendChild(subMenuOptionElement);
|
|
}
|
|
(_c = menuOptionElement.parentElement) == null ? void 0 : _c.appendChild(submenu);
|
|
}
|
|
});
|
|
return menuOptionElement;
|
|
}
|
|
static addPopupMenuOption(canvas, element, index = -1) {
|
|
var _a;
|
|
const popupMenuEl = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl;
|
|
if (!popupMenuEl)
|
|
return;
|
|
if (element.id) {
|
|
const optionToReplace = popupMenuEl.querySelector(`#${element.id}`);
|
|
if (optionToReplace && index === -1)
|
|
index = Array.from(popupMenuEl.children).indexOf(optionToReplace) - 1;
|
|
optionToReplace == null ? void 0 : optionToReplace.remove();
|
|
}
|
|
const sisterElement = index >= 0 ? popupMenuEl.children[index] : popupMenuEl.children[popupMenuEl.children.length + index];
|
|
popupMenuEl.insertAfter(element, sisterElement);
|
|
}
|
|
static getCenterCoordinates(canvas, nodeSize) {
|
|
const viewBounds = canvas.getViewportBBox();
|
|
return {
|
|
x: (viewBounds.minX + viewBounds.maxX) / 2 - nodeSize.width / 2,
|
|
y: (viewBounds.minY + viewBounds.maxY) / 2 - nodeSize.height / 2
|
|
};
|
|
}
|
|
static getBBox(canvasNodes) {
|
|
let minX = Infinity;
|
|
let minY = Infinity;
|
|
let maxX = -Infinity;
|
|
let maxY = -Infinity;
|
|
for (const node of canvasNodes) {
|
|
const nodeData = node.getData ? node.getData() : node;
|
|
minX = Math.min(minX, nodeData.x);
|
|
minY = Math.min(minY, nodeData.y);
|
|
maxX = Math.max(maxX, nodeData.x + nodeData.width);
|
|
maxY = Math.max(maxY, nodeData.y + nodeData.height);
|
|
}
|
|
return { minX, minY, maxX, maxY };
|
|
}
|
|
static zoomToBBox(canvas, bbox) {
|
|
const PADDING_CORRECTION_FACTOR = 1 / 1.1;
|
|
const zoomedBBox = BBoxHelper.scaleBBox(bbox, PADDING_CORRECTION_FACTOR);
|
|
canvas.zoomToBbox(zoomedBBox);
|
|
const scaleFactor = Math.min(
|
|
canvas.canvasRect.width / (bbox.maxX - bbox.minX),
|
|
canvas.canvasRect.height / (bbox.maxY - bbox.minY)
|
|
);
|
|
canvas.tZoom = Math.log2(scaleFactor);
|
|
}
|
|
static addStyleAttributesToPopup(plugin, canvas, styleAttributes, currentStyleAttributes, setStyleAttribute) {
|
|
if (!plugin.settings.getSetting("combineCustomStylesInDropdown"))
|
|
this.addStyleAttributesButtons(canvas, styleAttributes, currentStyleAttributes, setStyleAttribute);
|
|
else
|
|
this.addStyleAttributesDropdownMenu(canvas, styleAttributes, currentStyleAttributes, setStyleAttribute);
|
|
}
|
|
static addStyleAttributesButtons(canvas, stylableAttributes, currentStyleAttributes, setStyleAttribute) {
|
|
var _a;
|
|
for (const stylableAttribute of stylableAttributes) {
|
|
const selectedStyle = (_a = stylableAttribute.options.find((option) => currentStyleAttributes[stylableAttribute.datasetKey] === option.value)) != null ? _a : stylableAttribute.options.find((value) => value.value === null);
|
|
const menuOption = _CanvasHelper.createExpandablePopupMenuOption({
|
|
id: `menu-option-${stylableAttribute.datasetKey}`,
|
|
label: stylableAttribute.label,
|
|
icon: selectedStyle.icon
|
|
}, stylableAttribute.options.map((styleOption) => ({
|
|
label: styleOption.label,
|
|
icon: styleOption.icon,
|
|
callback: () => {
|
|
setStyleAttribute(stylableAttribute, styleOption.value);
|
|
currentStyleAttributes[stylableAttribute.datasetKey] = styleOption.value;
|
|
(0, import_obsidian4.setIcon)(menuOption, styleOption.icon);
|
|
menuOption.dispatchEvent(new Event("click"));
|
|
}
|
|
})));
|
|
_CanvasHelper.addPopupMenuOption(canvas, menuOption);
|
|
}
|
|
}
|
|
static addStyleAttributesDropdownMenu(canvas, stylableAttributes, currentStyleAttributes, setStyleAttribute) {
|
|
var _a, _b;
|
|
const STYLE_MENU_ID = "style-menu";
|
|
const STYLE_MENU_DROPDOWN_ID = "style-menu-dropdown";
|
|
const STYLE_MENU_DROPDOWN_SUBMENU_ID = "style-menu-dropdown-submenu";
|
|
const popupMenuElement = (_a = canvas == null ? void 0 : canvas.menu) == null ? void 0 : _a.menuEl;
|
|
if (!popupMenuElement)
|
|
return;
|
|
(_b = popupMenuElement.querySelector(`#${STYLE_MENU_ID}`)) == null ? void 0 : _b.remove();
|
|
const styleMenuButtonElement = document.createElement("button");
|
|
styleMenuButtonElement.id = STYLE_MENU_ID;
|
|
styleMenuButtonElement.classList.add("clickable-icon");
|
|
(0, import_obsidian4.setIcon)(styleMenuButtonElement, "paintbrush");
|
|
(0, import_obsidian4.setTooltip)(styleMenuButtonElement, "Style", { placement: "top" });
|
|
popupMenuElement.appendChild(styleMenuButtonElement);
|
|
styleMenuButtonElement.addEventListener("click", () => {
|
|
var _a2, _b2, _c;
|
|
const isOpen = styleMenuButtonElement.classList.toggle("has-active-menu");
|
|
if (!isOpen) {
|
|
(_a2 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_ID}`)) == null ? void 0 : _a2.remove();
|
|
(_b2 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_SUBMENU_ID}`)) == null ? void 0 : _b2.remove();
|
|
return;
|
|
}
|
|
const styleMenuDropdownElement = document.createElement("div");
|
|
styleMenuDropdownElement.id = STYLE_MENU_DROPDOWN_ID;
|
|
styleMenuDropdownElement.classList.add("menu");
|
|
styleMenuDropdownElement.style.position = "absolute";
|
|
styleMenuDropdownElement.style.maxHeight = "initial";
|
|
styleMenuDropdownElement.style.top = `${popupMenuElement.getBoundingClientRect().height}px`;
|
|
const canvasWrapperCenterX = canvas.wrapperEl.getBoundingClientRect().left + canvas.wrapperEl.getBoundingClientRect().width / 2;
|
|
const leftPosition = styleMenuButtonElement.getBoundingClientRect().left - popupMenuElement.getBoundingClientRect().left;
|
|
const rightPosition = popupMenuElement.getBoundingClientRect().right - styleMenuButtonElement.getBoundingClientRect().right;
|
|
if (popupMenuElement.getBoundingClientRect().left + leftPosition < canvasWrapperCenterX)
|
|
styleMenuDropdownElement.style.left = `${leftPosition}px`;
|
|
else
|
|
styleMenuDropdownElement.style.right = `${rightPosition}px`;
|
|
for (const stylableAttribute of stylableAttributes) {
|
|
const stylableAttributeElement = document.createElement("div");
|
|
stylableAttributeElement.classList.add("menu-item");
|
|
stylableAttributeElement.classList.add("tappable");
|
|
const iconElement = document.createElement("div");
|
|
iconElement.classList.add("menu-item-icon");
|
|
let selectedStyle = (_c = stylableAttribute.options.find((option) => currentStyleAttributes[stylableAttribute.datasetKey] === option.value)) != null ? _c : stylableAttribute.options.find((value) => value.value === null);
|
|
(0, import_obsidian4.setIcon)(iconElement, selectedStyle.icon);
|
|
stylableAttributeElement.appendChild(iconElement);
|
|
const labelElement = document.createElement("div");
|
|
labelElement.classList.add("menu-item-title");
|
|
labelElement.textContent = stylableAttribute.label;
|
|
stylableAttributeElement.appendChild(labelElement);
|
|
const expandIconElement = document.createElement("div");
|
|
expandIconElement.classList.add("menu-item-icon");
|
|
(0, import_obsidian4.setIcon)(expandIconElement, "chevron-right");
|
|
stylableAttributeElement.appendChild(expandIconElement);
|
|
styleMenuDropdownElement.appendChild(stylableAttributeElement);
|
|
stylableAttributeElement.addEventListener("pointerenter", () => {
|
|
stylableAttributeElement.classList.add("selected");
|
|
});
|
|
stylableAttributeElement.addEventListener("pointerleave", () => {
|
|
stylableAttributeElement.classList.remove("selected");
|
|
});
|
|
stylableAttributeElement.addEventListener("click", () => {
|
|
var _a3;
|
|
(_a3 = popupMenuElement.querySelector(`#${STYLE_MENU_DROPDOWN_SUBMENU_ID}`)) == null ? void 0 : _a3.remove();
|
|
const styleMenuDropdownSubmenuElement = document.createElement("div");
|
|
styleMenuDropdownSubmenuElement.id = STYLE_MENU_DROPDOWN_SUBMENU_ID;
|
|
styleMenuDropdownSubmenuElement.classList.add("menu");
|
|
styleMenuDropdownSubmenuElement.style.position = "absolute";
|
|
styleMenuDropdownSubmenuElement.style.maxHeight = "initial";
|
|
const topOffset = parseFloat(window.getComputedStyle(styleMenuDropdownElement).getPropertyValue("padding-top")) + (styleMenuDropdownElement.offsetHeight - styleMenuDropdownElement.clientHeight) / 2;
|
|
styleMenuDropdownSubmenuElement.style.top = `${stylableAttributeElement.getBoundingClientRect().top - topOffset - popupMenuElement.getBoundingClientRect().top}px`;
|
|
const leftPosition2 = styleMenuDropdownElement.getBoundingClientRect().right - popupMenuElement.getBoundingClientRect().left;
|
|
const rightPosition2 = popupMenuElement.getBoundingClientRect().right - styleMenuDropdownElement.getBoundingClientRect().left;
|
|
if (popupMenuElement.getBoundingClientRect().left + leftPosition2 < canvasWrapperCenterX)
|
|
styleMenuDropdownSubmenuElement.style.left = `${leftPosition2}px`;
|
|
else
|
|
styleMenuDropdownSubmenuElement.style.right = `${rightPosition2}px`;
|
|
for (const styleOption of stylableAttribute.options) {
|
|
const styleMenuDropdownSubmenuOptionElement = document.createElement("div");
|
|
styleMenuDropdownSubmenuOptionElement.classList.add("menu-item");
|
|
styleMenuDropdownSubmenuOptionElement.classList.add("tappable");
|
|
const submenuIconElement = document.createElement("div");
|
|
submenuIconElement.classList.add("menu-item-icon");
|
|
(0, import_obsidian4.setIcon)(submenuIconElement, styleOption.icon);
|
|
styleMenuDropdownSubmenuOptionElement.appendChild(submenuIconElement);
|
|
const submenuLabelElement = document.createElement("div");
|
|
submenuLabelElement.classList.add("menu-item-title");
|
|
submenuLabelElement.textContent = styleOption.label;
|
|
styleMenuDropdownSubmenuOptionElement.appendChild(submenuLabelElement);
|
|
if (selectedStyle === styleOption) {
|
|
styleMenuDropdownSubmenuOptionElement.classList.add("mod-selected");
|
|
const selectedIconElement = document.createElement("div");
|
|
selectedIconElement.classList.add("menu-item-icon");
|
|
selectedIconElement.classList.add("mod-selected");
|
|
(0, import_obsidian4.setIcon)(selectedIconElement, "check");
|
|
styleMenuDropdownSubmenuOptionElement.appendChild(selectedIconElement);
|
|
}
|
|
styleMenuDropdownSubmenuElement.appendChild(styleMenuDropdownSubmenuOptionElement);
|
|
styleMenuDropdownSubmenuOptionElement.addEventListener("pointerenter", () => {
|
|
styleMenuDropdownSubmenuOptionElement.classList.add("selected");
|
|
});
|
|
styleMenuDropdownSubmenuOptionElement.addEventListener("pointerleave", () => {
|
|
styleMenuDropdownSubmenuOptionElement.classList.remove("selected");
|
|
});
|
|
styleMenuDropdownSubmenuOptionElement.addEventListener("click", () => {
|
|
setStyleAttribute(stylableAttribute, styleOption.value);
|
|
currentStyleAttributes[stylableAttribute.datasetKey] = styleOption.value;
|
|
selectedStyle = styleOption;
|
|
(0, import_obsidian4.setIcon)(iconElement, styleOption.icon);
|
|
styleMenuDropdownSubmenuElement.remove();
|
|
});
|
|
}
|
|
popupMenuElement.appendChild(styleMenuDropdownSubmenuElement);
|
|
});
|
|
}
|
|
popupMenuElement.appendChild(styleMenuDropdownElement);
|
|
});
|
|
}
|
|
};
|
|
_CanvasHelper.GRID_SIZE = 20;
|
|
var CanvasHelper = _CanvasHelper;
|
|
|
|
// src/core/canvas-extension.ts
|
|
var CanvasExtension = class {
|
|
constructor(plugin) {
|
|
this.plugin = plugin;
|
|
const isEnabled = this.isEnabled();
|
|
if (!(isEnabled === true || this.plugin.settings.getSetting(isEnabled)))
|
|
return;
|
|
this.init();
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/group-canvas-extension.ts
|
|
var GROUP_NODE_SIZE = { width: 300, height: 300 };
|
|
var GroupCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => {
|
|
CanvasHelper.addCardMenuOption(
|
|
canvas,
|
|
CanvasHelper.createCardMenuOption(
|
|
canvas,
|
|
{
|
|
id: "create-group",
|
|
label: "Drag to add group",
|
|
icon: "group"
|
|
},
|
|
() => GROUP_NODE_SIZE,
|
|
(canvas2, pos) => {
|
|
canvas2.createGroupNode({
|
|
pos,
|
|
size: GROUP_NODE_SIZE
|
|
});
|
|
}
|
|
)
|
|
);
|
|
}
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/presentation-canvas-extension.ts
|
|
var import_obsidian5 = require("obsidian");
|
|
var START_SLIDE_NAME = "Start Slide";
|
|
var DEFAULT_SLIDE_NAME = "New Slide";
|
|
var PresentationCanvasExtension = class extends CanvasExtension {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.savedViewport = null;
|
|
this.isPresentationMode = false;
|
|
this.visitedNodes = [];
|
|
this.fullscreenModalObserver = null;
|
|
}
|
|
isEnabled() {
|
|
return "presentationFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.SelectionContextMenu,
|
|
(menu, canvas) => {
|
|
menu.addItem(
|
|
(item) => item.setTitle("Wrap in slide").setIcon("gallery-vertical").onClick(() => this.addSlide(
|
|
canvas,
|
|
void 0,
|
|
BBoxHelper.enlargeBBox(BBoxHelper.combineBBoxes(
|
|
[...canvas.selection.values()].map((element) => element.getBBox())
|
|
), this.plugin.settings.getSetting("wrapInSlidePadding"))
|
|
))
|
|
);
|
|
}
|
|
));
|
|
this.plugin.addCommand({
|
|
id: "create-new-slide",
|
|
name: "Create new slide",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly && !this.isPresentationMode,
|
|
(canvas) => this.addSlide(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "set-start-node",
|
|
name: "Set start node",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly && !this.isPresentationMode && canvas.getSelectionData().nodes.length === 1,
|
|
(canvas) => this.setStartNode(canvas, canvas.nodes.get(canvas.getSelectionData().nodes[0].id))
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "start-presentation",
|
|
name: "Start presentation",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => !this.isPresentationMode,
|
|
(canvas) => this.startPresentation(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "continue-presentation",
|
|
name: "Continue presentation",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => !this.isPresentationMode,
|
|
(canvas) => this.startPresentation(canvas, true)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "end-presentation",
|
|
name: "End presentation",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => this.isPresentationMode,
|
|
(canvas) => this.endPresentation(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "previous-node",
|
|
name: "Previous node",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => this.isPresentationMode,
|
|
(canvas) => this.previousNode(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "next-node",
|
|
name: "Next node",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => this.isPresentationMode,
|
|
(canvas) => this.nextNode(canvas)
|
|
)
|
|
});
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.onCanvasChanged(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.onPopupMenuCreated(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeMoved,
|
|
(canvas, node) => this.onNodeMoved(canvas, node)
|
|
));
|
|
}
|
|
onCanvasChanged(canvas) {
|
|
CanvasHelper.addCardMenuOption(
|
|
canvas,
|
|
CanvasHelper.createCardMenuOption(
|
|
canvas,
|
|
{
|
|
id: "new-slide",
|
|
label: "Drag to add slide",
|
|
icon: "gallery-vertical"
|
|
},
|
|
() => this.getDefaultSlideSize(),
|
|
(canvas2, pos) => this.addSlide(canvas2, pos)
|
|
)
|
|
);
|
|
}
|
|
onPopupMenuCreated(canvas) {
|
|
if (!this.plugin.settings.getSetting("showSetStartNodeInPopup"))
|
|
return;
|
|
const selectedNodesData = canvas.getSelectionData().nodes;
|
|
if (canvas.readonly || selectedNodesData.length !== 1 || canvas.selection.size > 1)
|
|
return;
|
|
const selectedNode = canvas.nodes.get(selectedNodesData[0].id);
|
|
if (!selectedNode)
|
|
return;
|
|
CanvasHelper.addPopupMenuOption(
|
|
canvas,
|
|
CanvasHelper.createPopupMenuOption({
|
|
id: "start-node",
|
|
label: "Set as start slide",
|
|
icon: "play",
|
|
callback: () => this.setStartNode(canvas, selectedNode)
|
|
})
|
|
);
|
|
}
|
|
onNodeMoved(_canvas, node) {
|
|
const nodeData = node.getData();
|
|
if (!nodeData.sideRatio)
|
|
return;
|
|
const nodeBBox = node.getBBox();
|
|
const nodeSize = {
|
|
width: nodeBBox.maxX - nodeBBox.minX,
|
|
height: nodeBBox.maxY - nodeBBox.minY
|
|
};
|
|
const nodeAspectRatio = nodeSize.width / nodeSize.height;
|
|
if (nodeAspectRatio < nodeData.sideRatio)
|
|
nodeSize.width = nodeSize.height * nodeData.sideRatio;
|
|
else
|
|
nodeSize.height = nodeSize.width / nodeData.sideRatio;
|
|
node.setData({
|
|
...nodeData,
|
|
width: nodeSize.width,
|
|
height: nodeSize.height
|
|
});
|
|
}
|
|
getStartNode(canvas) {
|
|
for (const [_, node] of canvas.nodes) {
|
|
if (node.getData().isStartNode)
|
|
return node;
|
|
}
|
|
return void 0;
|
|
}
|
|
setStartNode(canvas, node) {
|
|
if (!node)
|
|
return;
|
|
const startNode = this.getStartNode(canvas);
|
|
if (startNode)
|
|
startNode.setData({ ...startNode.getData(), isStartNode: false });
|
|
if (node !== startNode)
|
|
node.setData({ ...node.getData(), isStartNode: true }, true);
|
|
}
|
|
getDefaultSlideSize() {
|
|
const slideSizeString = this.plugin.settings.getSetting("defaultSlideSize");
|
|
const slideSizeArray = slideSizeString.split("x").map((value) => parseInt(value));
|
|
return { width: slideSizeArray[0], height: slideSizeArray[1] };
|
|
}
|
|
getSlideAspectRatio() {
|
|
const slideSize = this.getDefaultSlideSize();
|
|
return slideSize.width / slideSize.height;
|
|
}
|
|
addSlide(canvas, pos, bbox) {
|
|
const isStartNode = this.getStartNode(canvas) == null;
|
|
const slideSize = this.getDefaultSlideSize();
|
|
const slideAspectRatio = this.getSlideAspectRatio();
|
|
if (bbox) {
|
|
const bboxWidth = bbox.maxX - bbox.minX;
|
|
const bboxHeight = bbox.maxY - bbox.minY;
|
|
if (bboxWidth / bboxHeight > slideAspectRatio) {
|
|
slideSize.width = bboxWidth;
|
|
slideSize.height = bboxWidth / slideAspectRatio;
|
|
} else {
|
|
slideSize.height = bboxHeight;
|
|
slideSize.width = bboxHeight * slideAspectRatio;
|
|
}
|
|
pos = {
|
|
x: bbox.minX,
|
|
y: bbox.minY
|
|
};
|
|
}
|
|
if (!pos)
|
|
pos = CanvasHelper.getCenterCoordinates(canvas, this.getDefaultSlideSize());
|
|
const groupNode = canvas.createGroupNode({
|
|
pos,
|
|
size: slideSize,
|
|
label: isStartNode ? START_SLIDE_NAME : DEFAULT_SLIDE_NAME,
|
|
focus: false
|
|
});
|
|
groupNode.setData({
|
|
...groupNode.getData(),
|
|
sideRatio: slideAspectRatio,
|
|
isStartNode: isStartNode ? true : void 0
|
|
});
|
|
}
|
|
async animateNodeTransition(canvas, fromNode, toNode) {
|
|
const useCustomZoomFunction = this.plugin.settings.getSetting("zoomToSlideWithoutPadding");
|
|
const animationDurationMs = this.plugin.settings.getSetting("slideTransitionAnimationDuration") * 1e3;
|
|
if (animationDurationMs > 0 && fromNode) {
|
|
const animationIntensity = this.plugin.settings.getSetting("slideTransitionAnimationIntensity");
|
|
const currentNodeBBoxEnlarged = BBoxHelper.scaleBBox(fromNode.getBBox(), animationIntensity);
|
|
if (useCustomZoomFunction)
|
|
CanvasHelper.zoomToBBox(canvas, currentNodeBBoxEnlarged);
|
|
else
|
|
canvas.zoomToBbox(currentNodeBBoxEnlarged);
|
|
await sleep(animationDurationMs / 2);
|
|
const nextNodeBBoxEnlarged = BBoxHelper.scaleBBox(toNode.getBBox(), animationIntensity);
|
|
if (useCustomZoomFunction)
|
|
CanvasHelper.zoomToBBox(canvas, nextNodeBBoxEnlarged);
|
|
else
|
|
canvas.zoomToBbox(nextNodeBBoxEnlarged);
|
|
await sleep(animationDurationMs / 2);
|
|
}
|
|
let nodeBBox = toNode.getBBox();
|
|
if (useCustomZoomFunction)
|
|
CanvasHelper.zoomToBBox(canvas, nodeBBox);
|
|
else
|
|
canvas.zoomToBbox(nodeBBox);
|
|
}
|
|
async startPresentation(canvas, tryContinue = false) {
|
|
if (!tryContinue || this.visitedNodes.length === 0) {
|
|
const startNode = this.getStartNode(canvas);
|
|
if (!startNode) {
|
|
new import_obsidian5.Notice("No start node found. Please mark a node as a start node trough the popup menu.");
|
|
return;
|
|
}
|
|
this.visitedNodes = [startNode];
|
|
}
|
|
this.savedViewport = {
|
|
x: canvas.tx,
|
|
y: canvas.ty,
|
|
zoom: canvas.tZoom
|
|
};
|
|
canvas.wrapperEl.focus();
|
|
canvas.wrapperEl.requestFullscreen();
|
|
canvas.wrapperEl.classList.add("presentation-mode");
|
|
canvas.setReadonly(true);
|
|
canvas.wrapperEl.onkeydown = (e) => {
|
|
if (this.plugin.settings.getSetting("useArrowKeysToChangeSlides")) {
|
|
if (e.key === "ArrowRight")
|
|
this.nextNode(canvas);
|
|
else if (e.key === "ArrowLeft")
|
|
this.previousNode(canvas);
|
|
}
|
|
if (this.plugin.settings.getSetting("usePgUpPgDownKeysToChangeSlides")) {
|
|
if (e.key === "PageDown")
|
|
this.nextNode(canvas);
|
|
else if (e.key === "PageUp")
|
|
this.previousNode(canvas);
|
|
}
|
|
};
|
|
this.fullscreenModalObserver = new MutationObserver((mutationRecords) => {
|
|
mutationRecords.forEach((mutationRecord) => {
|
|
mutationRecord.addedNodes.forEach((node) => {
|
|
var _a;
|
|
document.body.removeChild(node);
|
|
(_a = document.fullscreenElement) == null ? void 0 : _a.appendChild(node);
|
|
});
|
|
});
|
|
const inputField = document.querySelector(".prompt-input");
|
|
if (inputField)
|
|
inputField.focus();
|
|
});
|
|
this.fullscreenModalObserver.observe(document.body, { childList: true });
|
|
canvas.wrapperEl.onfullscreenchange = (_e) => {
|
|
if (document.fullscreenElement)
|
|
return;
|
|
this.endPresentation(canvas);
|
|
};
|
|
this.isPresentationMode = true;
|
|
await sleep(500);
|
|
this.animateNodeTransition(canvas, void 0, this.visitedNodes.last());
|
|
}
|
|
endPresentation(canvas) {
|
|
var _a;
|
|
(_a = this.fullscreenModalObserver) == null ? void 0 : _a.disconnect();
|
|
this.fullscreenModalObserver = null;
|
|
canvas.wrapperEl.onkeydown = null;
|
|
canvas.wrapperEl.onfullscreenchange = null;
|
|
canvas.setReadonly(false);
|
|
canvas.wrapperEl.classList.remove("presentation-mode");
|
|
if (document.fullscreenElement)
|
|
document.exitFullscreen();
|
|
if (this.plugin.settings.getSetting("resetViewportOnPresentationEnd"))
|
|
canvas.setViewport(this.savedViewport.x, this.savedViewport.y, this.savedViewport.zoom);
|
|
this.isPresentationMode = false;
|
|
}
|
|
nextNode(canvas) {
|
|
var _a;
|
|
const fromNode = this.visitedNodes.last();
|
|
if (!fromNode)
|
|
return;
|
|
const outgoingEdges = canvas.getEdgesForNode(fromNode).filter((edge) => edge.from.node === fromNode);
|
|
let toNode = (_a = outgoingEdges.first()) == null ? void 0 : _a.to.node;
|
|
if (outgoingEdges.length > 1) {
|
|
const sortedEdges = outgoingEdges.sort((a, b) => {
|
|
if (!a.label)
|
|
return 1;
|
|
if (!b.label)
|
|
return -1;
|
|
return a.label.localeCompare(b.label);
|
|
});
|
|
const traversedEdgesCount = this.visitedNodes.filter((visitedNode) => visitedNode == fromNode).length - 1;
|
|
const nextEdge = sortedEdges[traversedEdgesCount];
|
|
toNode = nextEdge.to.node;
|
|
}
|
|
if (toNode) {
|
|
this.visitedNodes.push(toNode);
|
|
this.animateNodeTransition(canvas, fromNode, toNode);
|
|
} else {
|
|
this.animateNodeTransition(canvas, fromNode, fromNode);
|
|
}
|
|
}
|
|
previousNode(canvas) {
|
|
const fromNode = this.visitedNodes.pop();
|
|
if (!fromNode)
|
|
return;
|
|
let toNode = this.visitedNodes.last();
|
|
if (!toNode) {
|
|
toNode = fromNode;
|
|
this.visitedNodes.push(fromNode);
|
|
}
|
|
this.animateNodeTransition(canvas, fromNode, toNode);
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/better-readonly-canvas-extension.ts
|
|
var BetterReadonlyCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "betterReadonlyEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas, _node) => this.updatePopupMenu(canvas)
|
|
));
|
|
let movingToBBox = false;
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.ViewportChanged.Before,
|
|
(canvas) => {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
if (movingToBBox) {
|
|
movingToBBox = false;
|
|
this.updateLockedZoom(canvas);
|
|
this.updateLockedPan(canvas);
|
|
return;
|
|
}
|
|
if (!canvas.readonly)
|
|
return;
|
|
if (this.plugin.settings.getSetting("disableZoom")) {
|
|
canvas.zoom = (_a = canvas.lockedZoom) != null ? _a : canvas.zoom;
|
|
canvas.tZoom = (_b = canvas.lockedZoom) != null ? _b : canvas.tZoom;
|
|
}
|
|
if (this.plugin.settings.getSetting("disablePan")) {
|
|
canvas.x = (_c = canvas.lockedX) != null ? _c : canvas.x;
|
|
canvas.tx = (_d = canvas.lockedX) != null ? _d : canvas.tx;
|
|
canvas.y = (_e = canvas.lockedY) != null ? _e : canvas.y;
|
|
canvas.ty = (_f = canvas.lockedY) != null ? _f : canvas.ty;
|
|
}
|
|
}
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.ZoomToBbox.Before,
|
|
() => movingToBBox = true
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.ReadonlyChanged,
|
|
(canvas, _readonly) => {
|
|
this.updatePopupMenu(canvas);
|
|
this.updateLockedZoom(canvas);
|
|
this.updateLockedPan(canvas);
|
|
}
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.addQuickSettings(canvas)
|
|
));
|
|
}
|
|
addQuickSettings(canvas) {
|
|
var _a;
|
|
const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement;
|
|
if (!settingsContainer)
|
|
return;
|
|
CanvasHelper.addControlMenuButton(
|
|
settingsContainer,
|
|
this.createToggle({
|
|
id: "disable-node-popup",
|
|
label: "Disable node popup",
|
|
icon: "arrow-up-right-from-circle",
|
|
callback: () => this.updatePopupMenu(canvas)
|
|
}, "disableNodePopup")
|
|
);
|
|
CanvasHelper.addControlMenuButton(
|
|
settingsContainer,
|
|
this.createToggle({
|
|
id: "disable-zoom",
|
|
label: "Disable zoom",
|
|
icon: "zoom-in",
|
|
callback: () => this.updateLockedZoom(canvas)
|
|
}, "disableZoom")
|
|
);
|
|
CanvasHelper.addControlMenuButton(
|
|
settingsContainer,
|
|
this.createToggle({
|
|
id: "disable-pan",
|
|
label: "Disable pan",
|
|
icon: "move",
|
|
callback: () => this.updateLockedPan(canvas)
|
|
}, "disablePan")
|
|
);
|
|
}
|
|
createToggle(menuOption, settingKey) {
|
|
const toggle = CanvasHelper.createControlMenuButton({
|
|
...menuOption,
|
|
callback: () => (async () => {
|
|
var _a;
|
|
const newValue = !this.plugin.settings.getSetting(settingKey);
|
|
await this.plugin.settings.setSetting({ [settingKey]: newValue });
|
|
toggle.dataset.toggled = this.plugin.settings.getSetting(settingKey).toString();
|
|
(_a = menuOption.callback) == null ? void 0 : _a.call(this);
|
|
})()
|
|
});
|
|
toggle.classList.add("show-while-readonly");
|
|
toggle.dataset.toggled = this.plugin.settings.getSetting(settingKey).toString();
|
|
return toggle;
|
|
}
|
|
updatePopupMenu(canvas) {
|
|
const hidden = canvas.readonly && this.plugin.settings.getSetting("disableNodePopup");
|
|
canvas.menu.menuEl.style.visibility = hidden ? "hidden" : "visible";
|
|
}
|
|
updateLockedZoom(canvas) {
|
|
canvas.lockedZoom = canvas.tZoom;
|
|
}
|
|
updateLockedPan(canvas) {
|
|
canvas.lockedX = canvas.tx;
|
|
canvas.lockedY = canvas.ty;
|
|
}
|
|
};
|
|
|
|
// src/utils/modal-helper.ts
|
|
var import_obsidian6 = require("obsidian");
|
|
|
|
// src/utils/path-helper.ts
|
|
var PathHelper = class {
|
|
static extension(path) {
|
|
return path.includes(".") ? path.split(".").pop() : void 0;
|
|
}
|
|
};
|
|
|
|
// src/utils/modal-helper.ts
|
|
var FileNameModal = class extends import_obsidian6.SuggestModal {
|
|
constructor(app, parentPath, fileExtension) {
|
|
super(app);
|
|
this.parentPath = parentPath;
|
|
this.fileExtension = fileExtension;
|
|
}
|
|
getSuggestions(query) {
|
|
const queryWithoutExtension = query.replace(new RegExp(`\\.${this.fileExtension}$`), "");
|
|
if (queryWithoutExtension === "")
|
|
return [];
|
|
const queryWithExtension = queryWithoutExtension + "." + this.fileExtension;
|
|
const suggestions = [`${this.parentPath}/${queryWithExtension}`, queryWithExtension].filter((s) => this.app.vault.getAbstractFileByPath(s) === null);
|
|
return suggestions;
|
|
}
|
|
renderSuggestion(text, el) {
|
|
el.setText(text);
|
|
}
|
|
onChooseSuggestion(_text, _evt) {
|
|
}
|
|
awaitInput() {
|
|
return new Promise((resolve, _reject) => {
|
|
this.onChooseSuggestion = (text) => {
|
|
resolve(text);
|
|
};
|
|
this.open();
|
|
});
|
|
}
|
|
};
|
|
var FileSelectModal = class extends import_obsidian6.SuggestModal {
|
|
constructor(app, extensionsRegex, suggestNewFile = false) {
|
|
super(app);
|
|
this.files = this.app.vault.getFiles().map((file) => file.path).filter((path) => {
|
|
var _a;
|
|
return (_a = PathHelper.extension(path)) == null ? void 0 : _a.match(extensionsRegex != null ? extensionsRegex : /.*/);
|
|
});
|
|
this.suggestNewFile = suggestNewFile;
|
|
this.setPlaceholder("Type to search...");
|
|
this.setInstructions([{
|
|
command: "\u2191\u2193",
|
|
purpose: "to navigate"
|
|
}, {
|
|
command: "\u21B5",
|
|
purpose: "to open"
|
|
}, {
|
|
command: "shift \u21B5",
|
|
purpose: "to create"
|
|
}, {
|
|
command: "esc",
|
|
purpose: "to dismiss"
|
|
}]);
|
|
this.scope.register(["Shift"], "Enter", (e) => {
|
|
this.onChooseSuggestion(this.inputEl.value, e);
|
|
this.close();
|
|
});
|
|
}
|
|
getSuggestions(query) {
|
|
const suggestions = this.files.filter((path) => path.toLowerCase().includes(query.toLowerCase()));
|
|
if (suggestions.length === 0 && this.suggestNewFile)
|
|
suggestions.push(query);
|
|
return suggestions;
|
|
}
|
|
renderSuggestion(path, el) {
|
|
const simplifiedPath = path.replace(/\.md$/, "");
|
|
el.setText(simplifiedPath);
|
|
}
|
|
onChooseSuggestion(_path, _evt) {
|
|
}
|
|
awaitInput() {
|
|
return new Promise((resolve, _reject) => {
|
|
this.onChooseSuggestion = (path, _evt) => {
|
|
const file = this.app.vault.getAbstractFileByPath(path);
|
|
if (file instanceof import_obsidian6.TFile)
|
|
return resolve(file);
|
|
if (!this.suggestNewFile)
|
|
return;
|
|
if (PathHelper.extension(path) === void 0)
|
|
path += ".md";
|
|
const newFile = this.app.vault.create(path, "");
|
|
resolve(newFile);
|
|
};
|
|
this.open();
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/encapsulate-canvas-extension.ts
|
|
var ENCAPSULATED_FILE_NODE_SIZE = { width: 300, height: 300 };
|
|
var EncapsulateCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "canvasEncapsulationEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.addCommand({
|
|
id: "encapsulate-selection",
|
|
name: "Encapsulate selection",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly && canvas.selection.size > 0,
|
|
(canvas) => this.encapsulateSelection(canvas)
|
|
)
|
|
});
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.SelectionContextMenu,
|
|
(menu, canvas) => {
|
|
menu.addItem(
|
|
(item) => item.setTitle("Encapsulate").setIcon("file-plus").onClick(() => this.encapsulateSelection(canvas))
|
|
);
|
|
}
|
|
));
|
|
}
|
|
async encapsulateSelection(canvas) {
|
|
var _a;
|
|
const selection = canvas.getSelectionData();
|
|
const sourceFileFolder = (_a = canvas.view.file.parent) == null ? void 0 : _a.path;
|
|
if (!sourceFileFolder)
|
|
return;
|
|
const targetFilePath = await new FileNameModal(
|
|
this.plugin.app,
|
|
sourceFileFolder,
|
|
"canvas"
|
|
).awaitInput();
|
|
const newFileData = { nodes: selection.nodes, edges: selection.edges };
|
|
const file = await this.plugin.app.vault.create(targetFilePath, JSON.stringify(newFileData, null, 2));
|
|
for (const nodeData of selection.nodes) {
|
|
const node = canvas.nodes.get(nodeData.id);
|
|
if (node)
|
|
canvas.removeNode(node);
|
|
}
|
|
canvas.createFileNode({
|
|
pos: {
|
|
x: selection.center.x - ENCAPSULATED_FILE_NODE_SIZE.width / 2,
|
|
y: selection.center.y - ENCAPSULATED_FILE_NODE_SIZE.height / 2
|
|
},
|
|
size: ENCAPSULATED_FILE_NODE_SIZE,
|
|
file
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/commands-canvas-extension.ts
|
|
var DIRECTIONS = ["up", "down", "left", "right"];
|
|
var CommandsCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "commandsFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.addCommand({
|
|
id: "create-text-node",
|
|
name: "Create text node",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly,
|
|
(canvas) => this.createTextNode(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "create-file-node",
|
|
name: "Create file node",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly,
|
|
(canvas) => this.createFileNode(canvas)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "select-all-edges",
|
|
name: "Select all edges",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => true,
|
|
(canvas) => canvas.updateSelection(
|
|
() => canvas.selection = new Set(canvas.edges.values())
|
|
)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: "zoom-to-selection",
|
|
name: "Zoom to selection",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => canvas.selection.size > 0,
|
|
(canvas) => canvas.zoomToSelection()
|
|
)
|
|
});
|
|
for (const direction of DIRECTIONS) {
|
|
this.plugin.addCommand({
|
|
id: `clone-node-${direction}`,
|
|
name: `Clone node ${direction}`,
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => {
|
|
var _a;
|
|
return !canvas.readonly && canvas.selection.size === 1 && ((_a = canvas.selection.values().next().value) == null ? void 0 : _a.getData().type) === "text";
|
|
},
|
|
(canvas) => this.cloneNode(canvas, direction)
|
|
)
|
|
});
|
|
this.plugin.addCommand({
|
|
id: `expand-node-${direction}`,
|
|
name: `Expand node ${direction}`,
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(canvas) => !canvas.readonly && canvas.selection.size === 1,
|
|
(canvas) => this.expandNode(canvas, direction)
|
|
)
|
|
});
|
|
}
|
|
}
|
|
createTextNode(canvas) {
|
|
const size = canvas.config.defaultTextNodeDimensions;
|
|
const pos = CanvasHelper.getCenterCoordinates(canvas, size);
|
|
canvas.createTextNode({ pos, size });
|
|
}
|
|
async createFileNode(canvas) {
|
|
const size = canvas.config.defaultFileNodeDimensions;
|
|
const pos = CanvasHelper.getCenterCoordinates(canvas, size);
|
|
const file = await new FileSelectModal(this.plugin.app, void 0, true).awaitInput();
|
|
canvas.createFileNode({ pos, size, file });
|
|
}
|
|
cloneNode(canvas, cloneDirection) {
|
|
const sourceNode = canvas.selection.values().next().value;
|
|
if (!sourceNode)
|
|
return;
|
|
const sourceNodeData = sourceNode.getData();
|
|
const nodeMargin = this.plugin.settings.getSetting("cloneNodeMargin");
|
|
const offset = {
|
|
x: (sourceNode.width + nodeMargin) * (cloneDirection === "left" ? -1 : cloneDirection === "right" ? 1 : 0),
|
|
y: (sourceNode.height + nodeMargin) * (cloneDirection === "up" ? -1 : cloneDirection === "down" ? 1 : 0)
|
|
};
|
|
const clonedNode = canvas.createTextNode({
|
|
pos: {
|
|
x: sourceNode.x + offset.x,
|
|
y: sourceNode.y + offset.y
|
|
},
|
|
size: {
|
|
width: sourceNode.width,
|
|
height: sourceNode.height
|
|
}
|
|
});
|
|
clonedNode.setData({
|
|
...clonedNode.getData(),
|
|
color: sourceNodeData.color,
|
|
styleAttributes: sourceNodeData.styleAttributes
|
|
});
|
|
if (this.plugin.settings.getSetting("zoomToClonedNode"))
|
|
canvas.zoomToBbox(clonedNode.getBBox());
|
|
}
|
|
expandNode(canvas, expandDirection) {
|
|
const node = canvas.selection.values().next().value;
|
|
if (!node)
|
|
return;
|
|
const expandNodeStepSize = this.plugin.settings.getSetting("expandNodeStepSize");
|
|
const expand = {
|
|
x: expandDirection === "left" ? -1 : expandDirection === "right" ? 1 : 0,
|
|
y: expandDirection === "up" ? -1 : expandDirection === "down" ? 1 : 0
|
|
};
|
|
node.setData({
|
|
...node.getData(),
|
|
width: node.width + expand.x * expandNodeStepSize,
|
|
height: node.height + expand.y * expandNodeStepSize
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/auto-resize-node-canvas-extension.ts
|
|
var AutoResizeNodeCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return this.plugin.settings.getSetting("autoResizeNodeFeatureEnabled");
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.onPopupMenuCreated(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeTextContentChanged,
|
|
(canvas, node, viewUpdate) => this.onNodeTextContentChanged(canvas, node, viewUpdate)
|
|
));
|
|
}
|
|
onPopupMenuCreated(canvas) {
|
|
if (canvas.readonly)
|
|
return;
|
|
const selectedNodes = [...canvas.selection].filter((element) => {
|
|
const elementData = element.getData();
|
|
return elementData.type === "text";
|
|
});
|
|
if (selectedNodes.length === 0)
|
|
return;
|
|
const hasLockedHeight = selectedNodes.some((node) => node.getData().lockedHeight);
|
|
CanvasHelper.addPopupMenuOption(
|
|
canvas,
|
|
CanvasHelper.createPopupMenuOption({
|
|
id: "lock-height",
|
|
label: "Toggle locked height",
|
|
icon: hasLockedHeight ? "ruler" : "pencil-ruler",
|
|
callback: () => this.setLockedHeight(canvas, selectedNodes, hasLockedHeight)
|
|
})
|
|
);
|
|
}
|
|
setLockedHeight(canvas, nodes, lockedHeight) {
|
|
const newLockedHeight = lockedHeight ? void 0 : true;
|
|
nodes.forEach((node) => node.setData({
|
|
...node.getData(),
|
|
lockedHeight: newLockedHeight
|
|
}));
|
|
this.onPopupMenuCreated(canvas);
|
|
}
|
|
async onNodeTextContentChanged(canvas, node, viewUpdate) {
|
|
const nodeData = node.getData();
|
|
if (nodeData.lockedHeight)
|
|
return;
|
|
let textPadding = 0;
|
|
const cmScroller = viewUpdate.view.dom.querySelector(".cm-scroller");
|
|
if (cmScroller) {
|
|
for (const pseudo of ["before", "after"]) {
|
|
const style = window.getComputedStyle(cmScroller, pseudo);
|
|
const height = parseFloat(style.getPropertyValue("height"));
|
|
textPadding += height;
|
|
}
|
|
}
|
|
let newHeight = viewUpdate.view.contentHeight + textPadding + 10;
|
|
if (this.plugin.settings.getSetting("autoResizeNodeSnapToGrid"))
|
|
newHeight = Math.round(newHeight / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE;
|
|
node.setData({
|
|
...nodeData,
|
|
height: newHeight
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/portals-canvas-extension.ts
|
|
var import_obsidian7 = require("obsidian");
|
|
var PORTAL_PADDING = 50;
|
|
var MIN_OPEN_PORTAL_SIZE = { width: 200, height: 200 };
|
|
var PortalsCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "portalsFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.vault.on("modify", (file) => {
|
|
const canvases = this.plugin.app.workspace.getLeavesOfType("canvas").map((leaf) => leaf.view.canvas);
|
|
for (const canvas of canvases) {
|
|
if (canvas === void 0)
|
|
continue;
|
|
const hasPortalsToFile = canvas.getData().nodes.filter(
|
|
(nodeData) => nodeData.type === "file" && nodeData.portalToFile === file.path
|
|
).length > 0;
|
|
if (hasPortalsToFile) {
|
|
canvas.setData(canvas.getData());
|
|
canvas.history.current--;
|
|
canvas.history.data.pop();
|
|
}
|
|
}
|
|
}));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.updatePopupMenu(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeRemoved,
|
|
(canvas, node) => this.onNodeRemoved(canvas, node)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeMoved,
|
|
(canvas, node) => this.onNodeMoved(canvas, node)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DraggingStateChanged,
|
|
(canvas, startedDragging) => this.onDraggingStateChanged(canvas, startedDragging)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.ContainingNodesRequested,
|
|
(canvas, bbox, nodes) => this.onContainingNodesRequested(canvas, bbox, nodes)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.SelectionChanged,
|
|
(canvas, oldSelection, updateSelection) => this.onSelectionChanged(canvas, oldSelection, updateSelection)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DataRequested,
|
|
(canvas, data) => this.removePortalCanvasData(canvas, data)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.LoadData,
|
|
(canvas, data, setData) => {
|
|
this.getCanvasDataWithPortals(canvas, data).then((newData) => {
|
|
if (newData.nodes.length === data.nodes.length && newData.edges.length === data.edges.length)
|
|
return;
|
|
setData(newData);
|
|
});
|
|
}
|
|
));
|
|
}
|
|
updatePopupMenu(canvas) {
|
|
if (canvas.readonly)
|
|
return;
|
|
const selectedFileNodes = canvas.getSelectionData().nodes.map((nodeData) => {
|
|
var _a;
|
|
const node = canvas.nodes.get(nodeData.id);
|
|
if (!node)
|
|
return null;
|
|
if (nodeData.type !== "file")
|
|
return null;
|
|
if (((_a = node.file) == null ? void 0 : _a.extension) === "canvas")
|
|
return node;
|
|
if (nodeData.portalToFile)
|
|
this.setPortalOpen(canvas, node, false);
|
|
return null;
|
|
}).filter((node) => node !== null);
|
|
if (selectedFileNodes.length !== 1)
|
|
return;
|
|
const portalNode = selectedFileNodes[0];
|
|
const portalNodeData = portalNode.getData();
|
|
if (portalNodeData.portalToFile && portalNodeData.file !== portalNodeData.portalToFile) {
|
|
this.setPortalOpen(canvas, portalNode, true);
|
|
}
|
|
CanvasHelper.addPopupMenuOption(
|
|
canvas,
|
|
CanvasHelper.createPopupMenuOption({
|
|
id: "toggle-portal",
|
|
label: portalNodeData.portalToFile ? "Close portal" : "Open portal",
|
|
icon: portalNodeData.portalToFile ? "door-open" : "door-closed",
|
|
callback: () => {
|
|
this.setPortalOpen(canvas, portalNode, portalNodeData.portalToFile === void 0);
|
|
this.updatePopupMenu(canvas);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
setPortalOpen(canvas, portalNode, open) {
|
|
const portalNodeData = portalNode.getData();
|
|
portalNode.setData({
|
|
...portalNodeData,
|
|
portalToFile: open ? portalNodeData.file : void 0
|
|
});
|
|
canvas.setData(canvas.getData());
|
|
}
|
|
onNodeRemoved(canvas, node) {
|
|
var _a, _b, _c, _d;
|
|
const nodeData = node.getData();
|
|
if (nodeData.type !== "file" || !nodeData.portalToFile)
|
|
return;
|
|
Object.keys((_b = (_a = nodeData.portalIdMaps) == null ? void 0 : _a.nodeIdMap) != null ? _b : {}).map((refNodeId) => canvas.nodes.get(refNodeId)).filter((node2) => node2 !== void 0).forEach((node2) => canvas.removeNode(node2));
|
|
Object.keys((_d = (_c = nodeData.portalIdMaps) == null ? void 0 : _c.edgeIdMap) != null ? _d : {}).map((refEdgeId) => canvas.edges.get(refEdgeId)).filter((edge) => edge !== void 0).forEach((edge) => canvas.removeEdge(edge));
|
|
}
|
|
onContainingNodesRequested(_canvas, _bbox, nodes) {
|
|
nodes.splice(0, nodes.length, ...nodes.filter((node) => node.getData().portalId === void 0));
|
|
}
|
|
onSelectionChanged(canvas, oldSelection, updateSelection) {
|
|
updateSelection(() => {
|
|
const updatedSelection = Array.from(canvas.selection).filter((node) => node.getData().portalId === void 0);
|
|
canvas.selection = new Set(updatedSelection);
|
|
});
|
|
const previouslySelectedPortalNodesIds = Array.from(oldSelection).filter((node) => node.getData().portalToFile !== void 0).flatMap((node) => {
|
|
const portalNodeData = node.getData();
|
|
const nestedPortalsIds = this.getNestedPortalsIds(canvas, portalNodeData.id);
|
|
return [portalNodeData.id, ...nestedPortalsIds];
|
|
});
|
|
for (const node of canvas.nodes.values()) {
|
|
const nodeData = node.getData();
|
|
if (nodeData.portalId === void 0 || !previouslySelectedPortalNodesIds.includes(nodeData.portalId))
|
|
continue;
|
|
node.updateZIndex();
|
|
}
|
|
}
|
|
getNestedPortalsIds(canvas, portalId) {
|
|
const nestedPortalsIds = [];
|
|
for (const node of canvas.nodes.values()) {
|
|
const nodeData = node.getData();
|
|
if (nodeData.portalId === portalId) {
|
|
nestedPortalsIds.push(nodeData.id);
|
|
nestedPortalsIds.push(...this.getNestedPortalsIds(canvas, nodeData.id));
|
|
}
|
|
}
|
|
return nestedPortalsIds;
|
|
}
|
|
onDraggingStateChanged(canvas, startedDragging) {
|
|
var _a;
|
|
if (!canvas.getSelectionData().nodes.some((node) => node.type === "file" && node.portalToFile))
|
|
return;
|
|
if (startedDragging) {
|
|
const objectSnappingEnabled = canvas.options.snapToObjects;
|
|
this.restoreObjectSnappingState = () => canvas.toggleObjectSnapping(objectSnappingEnabled);
|
|
if (objectSnappingEnabled)
|
|
canvas.toggleObjectSnapping(false);
|
|
} else
|
|
(_a = this.restoreObjectSnappingState) == null ? void 0 : _a.call(this);
|
|
}
|
|
onNodeMoved(canvas, node) {
|
|
const nodeData = node.getData();
|
|
if (nodeData.type !== "file" || !nodeData.portalToFile)
|
|
return;
|
|
this.onOpenPortalMoved(canvas, node);
|
|
}
|
|
onOpenPortalMoved(canvas, portalNode) {
|
|
var _a;
|
|
let portalNodeData = portalNode.getData();
|
|
const nestedNodesIdMap = (_a = portalNode.getData().portalIdMaps) == null ? void 0 : _a.nodeIdMap;
|
|
if (!nestedNodesIdMap)
|
|
return;
|
|
const nestedNodes = Object.keys(nestedNodesIdMap).map((refNodeId) => canvas.nodes.get(refNodeId)).filter((node) => node !== void 0);
|
|
const sourceBBox = CanvasHelper.getBBox(nestedNodes);
|
|
const targetSize = this.getPortalSize(sourceBBox);
|
|
if (portalNodeData.width !== targetSize.width || portalNodeData.height !== targetSize.height) {
|
|
portalNode.setData({
|
|
...portalNodeData,
|
|
width: targetSize.width,
|
|
height: targetSize.height
|
|
});
|
|
return;
|
|
}
|
|
const portalOffset = {
|
|
x: portalNodeData.x - sourceBBox.minX + PORTAL_PADDING,
|
|
y: portalNodeData.y - sourceBBox.minY + PORTAL_PADDING
|
|
};
|
|
for (const nestedNode of nestedNodes) {
|
|
const nestedNodeData = nestedNode.getData();
|
|
nestedNode.setData({
|
|
...nestedNodeData,
|
|
x: nestedNodeData.x + portalOffset.x,
|
|
y: nestedNodeData.y + portalOffset.y
|
|
});
|
|
}
|
|
}
|
|
removePortalCanvasData(_canvas, data) {
|
|
var _a, _b;
|
|
data.edges = data.edges.filter((edgeData) => {
|
|
var _a2, _b2;
|
|
if (edgeData.portalId !== void 0)
|
|
return false;
|
|
const fromNodeData = data.nodes.find((nodeData) => nodeData.id === edgeData.fromNode);
|
|
const toNodeData = data.nodes.find((nodeData) => nodeData.id === edgeData.toNode);
|
|
if (!fromNodeData || !toNodeData)
|
|
return true;
|
|
if (fromNodeData.portalId === void 0 && toNodeData.portalId === void 0) {
|
|
return true;
|
|
} else if (fromNodeData.portalId !== void 0 && toNodeData.portalId !== void 0) {
|
|
return false;
|
|
} else {
|
|
const fromPortalNodeData = fromNodeData.portalId !== void 0 ? fromNodeData : toNodeData;
|
|
const notFromPortalNodeData = fromNodeData.portalId !== void 0 ? toNodeData : fromNodeData;
|
|
notFromPortalNodeData.edgesToNodeFromPortal = (_a2 = notFromPortalNodeData.edgesToNodeFromPortal) != null ? _a2 : {};
|
|
notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId] = (_b2 = notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId]) != null ? _b2 : [];
|
|
notFromPortalNodeData.edgesToNodeFromPortal[fromPortalNodeData.portalId].push(edgeData);
|
|
return false;
|
|
}
|
|
});
|
|
data.nodes = data.nodes.filter((nodeData) => nodeData.portalId === void 0);
|
|
for (const portalNodeData of data.nodes) {
|
|
if (portalNodeData.type !== "file")
|
|
continue;
|
|
if (this.plugin.settings.getSetting("maintainClosedPortalSize")) {
|
|
portalNodeData.width = (_a = portalNodeData.closedPortalWidth) != null ? _a : portalNodeData.width;
|
|
portalNodeData.height = (_b = portalNodeData.closedPortalHeight) != null ? _b : portalNodeData.height;
|
|
}
|
|
delete portalNodeData.closedPortalWidth;
|
|
delete portalNodeData.closedPortalHeight;
|
|
delete portalNodeData.portalIdMaps;
|
|
}
|
|
}
|
|
async getCanvasDataWithPortals(canvas, dataRef) {
|
|
const data = JSON.parse(JSON.stringify(dataRef));
|
|
const addedData = await Promise.all(data.nodes.map((nodeData) => this.tryOpenPortal(canvas, nodeData)));
|
|
for (const newData of addedData) {
|
|
data.nodes.push(...newData.nodes);
|
|
data.edges.push(...newData.edges);
|
|
}
|
|
for (const originNodeData of data.nodes) {
|
|
if (originNodeData.edgesToNodeFromPortal === void 0)
|
|
continue;
|
|
for (const [relativePortalId, edges] of Object.entries(originNodeData.edgesToNodeFromPortal)) {
|
|
const idPrefix = originNodeData.portalId ? `${originNodeData.portalId}-` : "";
|
|
const portalId = `${idPrefix}${relativePortalId}`;
|
|
const targetPortalData = data.nodes.find((nodeData) => nodeData.id === portalId);
|
|
if (!targetPortalData) {
|
|
delete originNodeData.edgesToNodeFromPortal[portalId];
|
|
continue;
|
|
}
|
|
if (targetPortalData.portalToFile) {
|
|
data.edges.push(...edges.map((edge) => ({
|
|
...edge,
|
|
portalId: originNodeData.portalId,
|
|
fromNode: `${idPrefix}${edge.fromNode}`,
|
|
toNode: `${idPrefix}${edge.toNode}`
|
|
})));
|
|
delete originNodeData.edgesToNodeFromPortal[portalId];
|
|
} else if (this.plugin.settings.getSetting("showEdgesIntoDisabledPortals")) {
|
|
data.edges.push(...edges.map((edge) => {
|
|
const fromNodeId = `${idPrefix}${edge.fromNode}`;
|
|
const fromNode = data.nodes.find((nodeData) => nodeData.id === fromNodeId);
|
|
const toNodeId = `${idPrefix}${edge.toNode}`;
|
|
return {
|
|
...edge,
|
|
fromNode: fromNode ? fromNodeId : portalId,
|
|
toNode: fromNode ? portalId : toNodeId,
|
|
portalId
|
|
// Mark it as temporary
|
|
};
|
|
}));
|
|
}
|
|
}
|
|
if (Object.keys(originNodeData.edgesToNodeFromPortal).length === 0)
|
|
delete originNodeData.edgesToNodeFromPortal;
|
|
}
|
|
return data;
|
|
}
|
|
async tryOpenPortal(canvas, portalNodeData) {
|
|
var _a, _b;
|
|
const addedData = { nodes: [], edges: [] };
|
|
if (portalNodeData.type !== "file" || !portalNodeData.portalToFile)
|
|
return addedData;
|
|
portalNodeData.portalToFile = portalNodeData.file;
|
|
if (portalNodeData.portalToFile === canvas.view.file.path) {
|
|
portalNodeData.portalToFile = void 0;
|
|
return addedData;
|
|
}
|
|
const portalFile = this.plugin.app.vault.getAbstractFileByPath(portalNodeData.file);
|
|
if (!(portalFile instanceof import_obsidian7.TFile) || portalFile.extension !== "canvas") {
|
|
portalNodeData.portalToFile = void 0;
|
|
return addedData;
|
|
}
|
|
const portalFileDataString = await this.plugin.app.vault.cachedRead(portalFile);
|
|
if (portalFileDataString === "")
|
|
return addedData;
|
|
const portalFileData = JSON.parse(portalFileDataString);
|
|
if (!portalFileData) {
|
|
portalNodeData.portalToFile = void 0;
|
|
return addedData;
|
|
}
|
|
portalNodeData.portalIdMaps = {
|
|
nodeIdMap: {},
|
|
edgeIdMap: {}
|
|
};
|
|
const sourceMinCoordinates = CanvasHelper.getBBox(portalFileData.nodes);
|
|
const portalOffset = {
|
|
x: portalNodeData.x - sourceMinCoordinates.minX + PORTAL_PADDING,
|
|
y: portalNodeData.y - sourceMinCoordinates.minY + PORTAL_PADDING
|
|
};
|
|
for (const nodeDataFromPortal of portalFileData.nodes) {
|
|
const refNodeId = `${portalNodeData.id}-${nodeDataFromPortal.id}`;
|
|
portalNodeData.portalIdMaps.nodeIdMap[refNodeId] = nodeDataFromPortal.id;
|
|
const addedNode = {
|
|
...nodeDataFromPortal,
|
|
id: refNodeId,
|
|
x: nodeDataFromPortal.x + portalOffset.x,
|
|
y: nodeDataFromPortal.y + portalOffset.y,
|
|
portalId: portalNodeData.id
|
|
};
|
|
addedData.nodes.push(addedNode);
|
|
const nestedNodes = await this.tryOpenPortal(canvas, addedNode);
|
|
addedData.nodes.push(...nestedNodes.nodes);
|
|
addedData.edges.push(...nestedNodes.edges);
|
|
}
|
|
for (const edgeDataFromPortal of portalFileData.edges) {
|
|
const refEdgeId = `${portalNodeData.id}-${edgeDataFromPortal.id}`;
|
|
portalNodeData.portalIdMaps.edgeIdMap[refEdgeId] = edgeDataFromPortal.id;
|
|
const fromRefNode = (_a = Object.entries(portalNodeData.portalIdMaps.nodeIdMap).find(([_refNodeId, nodeId]) => nodeId === edgeDataFromPortal.fromNode)) == null ? void 0 : _a[0];
|
|
const toRefNode = (_b = Object.entries(portalNodeData.portalIdMaps.nodeIdMap).find(([_refNodeId, nodeId]) => nodeId === edgeDataFromPortal.toNode)) == null ? void 0 : _b[0];
|
|
if (!fromRefNode || !toRefNode)
|
|
continue;
|
|
addedData.edges.push({
|
|
...edgeDataFromPortal,
|
|
id: refEdgeId,
|
|
fromNode: fromRefNode,
|
|
toNode: toRefNode,
|
|
portalId: portalNodeData.id
|
|
});
|
|
}
|
|
const nestedNodesBBox = CanvasHelper.getBBox(addedData.nodes);
|
|
const targetSize = this.getPortalSize(nestedNodesBBox);
|
|
portalNodeData.closedPortalWidth = portalNodeData.width;
|
|
portalNodeData.closedPortalHeight = portalNodeData.height;
|
|
portalNodeData.width = targetSize.width;
|
|
portalNodeData.height = targetSize.height;
|
|
return addedData;
|
|
}
|
|
getPortalSize(sourceBBox) {
|
|
const sourceSize = {
|
|
width: sourceBBox.maxX - sourceBBox.minX,
|
|
height: sourceBBox.maxY - sourceBBox.minY
|
|
};
|
|
const targetSize = {
|
|
width: Math.max(sourceSize.width + PORTAL_PADDING * 2, MIN_OPEN_PORTAL_SIZE.width),
|
|
height: Math.max(sourceSize.height + PORTAL_PADDING * 2, MIN_OPEN_PORTAL_SIZE.height)
|
|
};
|
|
if (!Number.isFinite(targetSize.width))
|
|
targetSize.width = MIN_OPEN_PORTAL_SIZE.width;
|
|
if (!Number.isFinite(targetSize.height))
|
|
targetSize.height = MIN_OPEN_PORTAL_SIZE.height;
|
|
return targetSize;
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/better-default-settings-canvas-extension.ts
|
|
var BetterDefaultSettingsCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.modifyCanvasSettings(this.plugin.getCurrentCanvas());
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
SettingsManager.SETTINGS_CHANGED_EVENT,
|
|
() => this.modifyCanvasSettings(this.plugin.getCurrentCanvas())
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.modifyCanvasSettings(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DoubleClick,
|
|
(canvas, event, preventDefault) => this.onDoubleClick(canvas, event, preventDefault)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeCreated,
|
|
(canvas, node) => {
|
|
this.enforceNodeGridAlignment(canvas, node);
|
|
this.applyDefaultNodeStyles(canvas, node);
|
|
}
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeCreated,
|
|
(canvas, edge) => this.applyDefaultEdgeStyles(canvas, edge)
|
|
));
|
|
}
|
|
modifyCanvasSettings(canvas) {
|
|
if (!canvas)
|
|
return;
|
|
canvas.config.defaultTextNodeDimensions = {
|
|
width: this.plugin.settings.getSetting("defaultTextNodeWidth"),
|
|
height: this.plugin.settings.getSetting("defaultTextNodeHeight")
|
|
};
|
|
canvas.config.defaultFileNodeDimensions = {
|
|
width: this.plugin.settings.getSetting("defaultFileNodeWidth"),
|
|
height: this.plugin.settings.getSetting("defaultFileNodeHeight")
|
|
};
|
|
canvas.config.minContainerDimension = this.plugin.settings.getSetting("minNodeSize");
|
|
}
|
|
async onDoubleClick(canvas, event, preventDefault) {
|
|
if (event.defaultPrevented || event.target !== canvas.wrapperEl || canvas.isDragging || canvas.readonly)
|
|
return;
|
|
preventDefault.value = true;
|
|
let pos = canvas.posFromEvt(event);
|
|
switch (this.plugin.settings.getSetting("nodeTypeOnDoubleClick")) {
|
|
case "file":
|
|
const file = await new FileSelectModal(this.plugin.app, void 0, true).awaitInput();
|
|
canvas.createFileNode({
|
|
pos,
|
|
position: "center",
|
|
file
|
|
});
|
|
break;
|
|
default:
|
|
canvas.createTextNode({
|
|
pos,
|
|
position: "center"
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
enforceNodeGridAlignment(canvas, node) {
|
|
if (!this.plugin.settings.getSetting("alignNewNodesToGrid"))
|
|
return;
|
|
const nodeData = node.getData();
|
|
node.setData({
|
|
...nodeData,
|
|
x: Math.round(nodeData.x / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE,
|
|
y: Math.round(nodeData.y / CanvasHelper.GRID_SIZE) * CanvasHelper.GRID_SIZE
|
|
});
|
|
}
|
|
applyDefaultNodeStyles(_canvas, node) {
|
|
const nodeData = node.getData();
|
|
if (nodeData.type !== "text")
|
|
return;
|
|
node.setData({
|
|
...nodeData,
|
|
styleAttributes: {
|
|
...nodeData.styleAttributes,
|
|
...this.plugin.settings.getSetting("defaultTextNodeStyleAttributes")
|
|
}
|
|
});
|
|
}
|
|
async applyDefaultEdgeStyles(canvas, edge) {
|
|
const edgeData = edge.getData();
|
|
edge.setData({
|
|
...edgeData,
|
|
styleAttributes: {
|
|
...edgeData.styleAttributes,
|
|
...this.plugin.settings.getSetting("defaultEdgeStyleAttributes")
|
|
}
|
|
});
|
|
if (canvas.canvasEl.hasClass("is-connecting")) {
|
|
await new Promise((resolve) => {
|
|
new MutationObserver(() => {
|
|
if (!canvas.canvasEl.hasClass("is-connecting"))
|
|
resolve();
|
|
}).observe(canvas.canvasEl, { attributes: true, attributeFilter: ["class"] });
|
|
});
|
|
}
|
|
const lineDirection = this.plugin.settings.getSetting("defaultEdgeLineDirection");
|
|
edge.setData({
|
|
...edge.getData(),
|
|
fromEnd: lineDirection === "bidirectional" ? "arrow" : "none",
|
|
toEnd: lineDirection === "nondirectional" ? "none" : "arrow"
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/color-palette-canvas-extension.ts
|
|
var DEFAULT_COLORS_COUNT = 6;
|
|
var CUSTOM_COLORS_MOD_STYLES_ID = "mod-custom-colors";
|
|
var ColorPaletteCanvasExtension = class extends CanvasExtension {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.observer = null;
|
|
}
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
"window-open",
|
|
(_win, _window) => this.updateCustomColorModStyleClasses()
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
"css-change",
|
|
() => this.updateCustomColorModStyleClasses()
|
|
));
|
|
this.updateCustomColorModStyleClasses();
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.patchColorSelection(canvas)
|
|
));
|
|
this.plugin.register(() => {
|
|
var _a;
|
|
return (_a = this.observer) == null ? void 0 : _a.disconnect();
|
|
});
|
|
}
|
|
updateCustomColorModStyleClasses() {
|
|
var _a;
|
|
const customCss = this.getCustomColors().map((colorId) => `
|
|
.mod-canvas-color-${colorId} {
|
|
--canvas-color: var(--canvas-color-${colorId});
|
|
}
|
|
`).join("");
|
|
for (const win of this.plugin.windowsManager.windows) {
|
|
const doc = win.document;
|
|
(_a = doc.getElementById(CUSTOM_COLORS_MOD_STYLES_ID)) == null ? void 0 : _a.remove();
|
|
const customColorModStyle = doc.createElement("style");
|
|
customColorModStyle.id = CUSTOM_COLORS_MOD_STYLES_ID;
|
|
doc.head.appendChild(customColorModStyle);
|
|
customColorModStyle.textContent = customCss;
|
|
}
|
|
}
|
|
patchColorSelection(canvas) {
|
|
if (this.observer)
|
|
this.observer.disconnect();
|
|
this.observer = new MutationObserver((mutations) => {
|
|
const colorMenuOpened = mutations.some(
|
|
(mutation) => Object.values(mutation.addedNodes).some(
|
|
(node) => node instanceof HTMLElement && node.classList.contains("canvas-submenu") && Object.values(node.childNodes).some(
|
|
(node2) => node2 instanceof HTMLElement && node2.classList.contains("canvas-color-picker-item")
|
|
)
|
|
)
|
|
);
|
|
if (!colorMenuOpened)
|
|
return;
|
|
const submenu = canvas.menu.menuEl.querySelector(".canvas-submenu");
|
|
if (!submenu)
|
|
return;
|
|
const currentNodeColor = canvas.getSelectionData().nodes.map((node) => node.color).last();
|
|
for (const colorId of this.getCustomColors()) {
|
|
const customColorMenuItem = this.createColorMenuItem(canvas, colorId);
|
|
if (currentNodeColor === colorId)
|
|
customColorMenuItem.classList.add("is-active");
|
|
submenu.insertBefore(customColorMenuItem, submenu.lastChild);
|
|
}
|
|
});
|
|
this.observer.observe(canvas.menu.menuEl, { childList: true });
|
|
}
|
|
createColorMenuItem(canvas, colorId) {
|
|
const menuItem = document.createElement("div");
|
|
menuItem.classList.add("canvas-color-picker-item");
|
|
menuItem.classList.add(`mod-canvas-color-${colorId}`);
|
|
menuItem.addEventListener("click", () => {
|
|
menuItem.classList.add("is-active");
|
|
for (const item of canvas.selection) {
|
|
item.setColor(colorId);
|
|
}
|
|
canvas.requestSave();
|
|
});
|
|
return menuItem;
|
|
}
|
|
getCustomColors() {
|
|
const colors = [];
|
|
while (true) {
|
|
const colorId = (DEFAULT_COLORS_COUNT + colors.length + 1).toString();
|
|
if (!getComputedStyle(document.body).getPropertyValue(`--canvas-color-${colorId}`))
|
|
break;
|
|
colors.push(colorId);
|
|
}
|
|
return colors;
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/collapsible-groups-canvas-extension.ts
|
|
var import_obsidian8 = require("obsidian");
|
|
var COLLAPSE_BUTTON_ID = "group-collapse-button";
|
|
var CollapsibleGroupsCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "collapsibleGroupsFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeChanged,
|
|
(canvas, node) => this.onNodeChanged(canvas, node)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeBBoxRequested,
|
|
(canvas, node, bbox) => this.onNodeBBoxRequested(canvas, node, bbox)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.OnCopy,
|
|
(canvas, selectionData) => this.onCopy(canvas, selectionData)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DataRequested,
|
|
(_canvas, data) => this.expandCollapsedNodes(data)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.LoadData,
|
|
(_canvas, data, _setData) => this.collapseNodes(data)
|
|
));
|
|
}
|
|
onNodeChanged(canvas, groupNode) {
|
|
var _a, _b;
|
|
const groupNodeData = groupNode.getData();
|
|
if (groupNodeData.type !== "group")
|
|
return;
|
|
(_a = groupNode.nodeEl) == null ? void 0 : _a.querySelectorAll(`#${COLLAPSE_BUTTON_ID}`).forEach((el) => el.remove());
|
|
const collapseButton = document.createElement("span");
|
|
collapseButton.id = COLLAPSE_BUTTON_ID;
|
|
(0, import_obsidian8.setIcon)(collapseButton, groupNodeData.isCollapsed ? "plus-circle" : "minus-circle");
|
|
collapseButton.onclick = () => {
|
|
this.setCollapsed(canvas, groupNode, groupNode.getData().isCollapsed ? void 0 : true);
|
|
};
|
|
(_b = groupNode.labelEl) == null ? void 0 : _b.insertAdjacentElement("afterend", collapseButton);
|
|
}
|
|
onCopy(canvas, selectionData) {
|
|
for (const collapsedGroupData of selectionData.nodes) {
|
|
if (collapsedGroupData.type !== "group" || !collapsedGroupData.isCollapsed || !collapsedGroupData.collapsedData)
|
|
continue;
|
|
selectionData.nodes.push(...collapsedGroupData.collapsedData.nodes.map((nodeData) => ({
|
|
...nodeData,
|
|
// Restore the relative position of the node to the group
|
|
x: nodeData.x + collapsedGroupData.x,
|
|
y: nodeData.y + collapsedGroupData.y
|
|
})));
|
|
selectionData.edges.push(...collapsedGroupData.collapsedData.edges);
|
|
}
|
|
}
|
|
setCollapsed(canvas, groupNode, collapsed) {
|
|
groupNode.setData({ ...groupNode.getData(), isCollapsed: collapsed });
|
|
canvas.setData(canvas.getData());
|
|
canvas.history.current--;
|
|
canvas.pushHistory(canvas.getData());
|
|
}
|
|
onNodeBBoxRequested(_canvas, node, bbox) {
|
|
var _a, _b;
|
|
const nodeData = node.getData();
|
|
if (nodeData.type !== "group" || !nodeData.isCollapsed)
|
|
return;
|
|
bbox.maxX = bbox.minX + ((_b = (_a = node.nodeEl) == null ? void 0 : _a.getBoundingClientRect().width) != null ? _b : 0);
|
|
bbox.maxY = bbox.minY;
|
|
}
|
|
expandCollapsedNodes(data) {
|
|
data.nodes = data.nodes.flatMap((groupNodeData) => {
|
|
const collapsedData = groupNodeData.collapsedData;
|
|
if (collapsedData === void 0)
|
|
return [groupNodeData];
|
|
groupNodeData.collapsedData = void 0;
|
|
data.edges.push(...collapsedData.edges);
|
|
return [groupNodeData, ...collapsedData.nodes.map((nodeData) => ({
|
|
...nodeData,
|
|
// Restore the relative position of the node to the group
|
|
x: nodeData.x + groupNodeData.x,
|
|
y: nodeData.y + groupNodeData.y
|
|
}))];
|
|
});
|
|
}
|
|
collapseNodes(data) {
|
|
data.nodes.forEach((groupNodeData) => {
|
|
if (!groupNodeData.isCollapsed)
|
|
return;
|
|
const groupNodeBBox = CanvasHelper.getBBox([groupNodeData]);
|
|
const containedNodesData = data.nodes.filter(
|
|
(nodeData) => nodeData.id !== groupNodeData.id && BBoxHelper.insideBBox(CanvasHelper.getBBox([nodeData]), groupNodeBBox, false)
|
|
);
|
|
const containedEdgesData = data.edges.filter((edgeData) => {
|
|
return containedNodesData.some((nodeData) => nodeData.id === edgeData.fromNode) || containedNodesData.some((nodeData) => nodeData.id === edgeData.toNode);
|
|
});
|
|
data.nodes = data.nodes.filter((nodeData) => !containedNodesData.includes(nodeData));
|
|
data.edges = data.edges.filter((edgeData) => !containedEdgesData.includes(edgeData));
|
|
groupNodeData.collapsedData = {
|
|
nodes: containedNodesData.map((nodeData) => ({
|
|
...nodeData,
|
|
// Store the relative position of the node to the group
|
|
x: nodeData.x - groupNodeData.x,
|
|
y: nodeData.y - groupNodeData.y
|
|
})),
|
|
edges: containedEdgesData
|
|
};
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/properties-canvas-extension.ts
|
|
var import_obsidian9 = require("obsidian");
|
|
var PropertiesCanvasExtension = class extends CanvasExtension {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.previousCssclasses = [];
|
|
}
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.onCanvasChanged(canvas)
|
|
));
|
|
}
|
|
onCanvasChanged(canvas) {
|
|
var _a;
|
|
this.updateCssClasses(canvas);
|
|
const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement;
|
|
if (!settingsContainer)
|
|
return;
|
|
CanvasHelper.addControlMenuButton(
|
|
settingsContainer,
|
|
CanvasHelper.createControlMenuButton({
|
|
id: "properties",
|
|
label: "Properties",
|
|
icon: "settings-2",
|
|
callback: () => this.openPropertiesDialog(canvas)
|
|
})
|
|
);
|
|
}
|
|
updateCssClasses(canvas) {
|
|
var _a, _b;
|
|
this.previousCssclasses.forEach((cssclass) => {
|
|
canvas.wrapperEl.classList.remove(cssclass);
|
|
});
|
|
this.previousCssclasses = ((_b = (_a = canvas.metadata) == null ? void 0 : _a.properties) == null ? void 0 : _b.cssclasses) || [];
|
|
this.previousCssclasses.forEach((cssclass) => {
|
|
canvas.wrapperEl.classList.add(cssclass);
|
|
});
|
|
}
|
|
async openPropertiesDialog(canvas) {
|
|
await new PropertiesModal(this.plugin.app, canvas).awaitDialog();
|
|
this.updateCssClasses(canvas);
|
|
}
|
|
};
|
|
var PropertiesModal = class extends import_obsidian9.Modal {
|
|
constructor(app, canvas) {
|
|
super(app);
|
|
this.canvas = canvas;
|
|
}
|
|
onOpen() {
|
|
new import_obsidian9.Setting(this.contentEl).setHeading().setName("Properties");
|
|
new import_obsidian9.Setting(this.contentEl).setClass("properties-field").setName("cssclasses").setTooltip("Add classes to the canvas wrapper element. Separate multiple classes with spaces.").addText(
|
|
(text) => {
|
|
var _a, _b;
|
|
return text.setValue((_b = (_a = this.canvas.metadata.properties) == null ? void 0 : _a.cssclasses) == null ? void 0 : _b.join(" ")).onChange((value) => {
|
|
var _a2, _b2;
|
|
this.canvas.metadata.properties = (_b2 = (_a2 = this.canvas.metadata) == null ? void 0 : _a2.properties) != null ? _b2 : {};
|
|
this.canvas.metadata.properties.cssclasses = value.split(" ");
|
|
});
|
|
}
|
|
);
|
|
}
|
|
onClose() {
|
|
}
|
|
awaitDialog() {
|
|
return new Promise((resolve) => {
|
|
this.onClose = () => {
|
|
this.contentEl.empty();
|
|
resolve();
|
|
};
|
|
this.open();
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/focus-mode-canvas-extension.ts
|
|
var CONTROL_MENU_FOCUS_TOGGLE_ID = "focus-mode-toggle";
|
|
var FocusModeCanvasExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "focusModeFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.plugin.addCommand({
|
|
id: "toggle-focus-mode",
|
|
name: "Toggle Focus Mode",
|
|
checkCallback: CanvasHelper.canvasCommand(
|
|
this.plugin,
|
|
(_canvas) => true,
|
|
(canvas) => this.toggleFocusMode(canvas)
|
|
)
|
|
});
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.addControlMenuToggle(canvas)
|
|
));
|
|
}
|
|
addControlMenuToggle(canvas) {
|
|
var _a;
|
|
const settingsContainer = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement;
|
|
if (!settingsContainer)
|
|
return;
|
|
const controlMenuFocusToggle = CanvasHelper.createControlMenuButton({
|
|
id: CONTROL_MENU_FOCUS_TOGGLE_ID,
|
|
label: "Focus Mode",
|
|
icon: "focus",
|
|
callback: () => this.toggleFocusMode(canvas)
|
|
});
|
|
CanvasHelper.addControlMenuButton(settingsContainer, controlMenuFocusToggle);
|
|
}
|
|
toggleFocusMode(canvas) {
|
|
var _a, _b;
|
|
const controlMenuFocusToggle = (_b = (_a = canvas.quickSettingsButton) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.querySelector(`#${CONTROL_MENU_FOCUS_TOGGLE_ID}`);
|
|
if (!controlMenuFocusToggle)
|
|
return;
|
|
const newValue = controlMenuFocusToggle.dataset.toggled !== "true";
|
|
canvas.wrapperEl.dataset.focusModeEnabled = newValue.toString();
|
|
controlMenuFocusToggle.dataset.toggled = newValue.toString();
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/node-styles.ts
|
|
var NodeStylesExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "nodeStylingFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.allNodeStyles = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customNodeStyleAttributes")];
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
SettingsManager.SETTINGS_CHANGED_EVENT,
|
|
() => this.allNodeStyles = [...BUILTIN_NODE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customNodeStyleAttributes")]
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.onPopupMenuCreated(canvas)
|
|
));
|
|
}
|
|
onPopupMenuCreated(canvas) {
|
|
var _a;
|
|
const selectionNodeData = canvas.getSelectionData().nodes;
|
|
if (canvas.readonly || selectionNodeData.length === 0 || selectionNodeData.length !== canvas.selection.size)
|
|
return;
|
|
const selectedNodeTypes = new Set(selectionNodeData.map((node) => node.type));
|
|
const availableNodeStyles = this.allNodeStyles.filter((style) => !style.nodeTypes || style.nodeTypes.some((type) => selectedNodeTypes.has(type)));
|
|
CanvasHelper.addStyleAttributesToPopup(
|
|
this.plugin,
|
|
canvas,
|
|
availableNodeStyles,
|
|
(_a = selectionNodeData[0].styleAttributes) != null ? _a : {},
|
|
(attribute, value) => this.setStyleAttributeForSelection(canvas, attribute, value)
|
|
);
|
|
}
|
|
setStyleAttributeForSelection(canvas, attribute, value) {
|
|
const selectionNodeData = canvas.getSelectionData().nodes;
|
|
for (const nodeData of selectionNodeData) {
|
|
const node = canvas.nodes.get(nodeData.id);
|
|
if (!node)
|
|
continue;
|
|
if (attribute.nodeTypes && !attribute.nodeTypes.includes(nodeData.type))
|
|
continue;
|
|
node.setData({
|
|
...nodeData,
|
|
styleAttributes: {
|
|
...nodeData.styleAttributes,
|
|
[attribute.datasetKey]: value
|
|
}
|
|
});
|
|
}
|
|
canvas.pushHistory(canvas.getData());
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/edge-pathfinding-method.ts
|
|
var EdgePathfindingMethod = class {
|
|
};
|
|
|
|
// src/utils/svg-path-helper.ts
|
|
var SvgPathHelper = class {
|
|
static pathArrayToSvgPath(positions, rounded = false) {
|
|
const tension = 0.2;
|
|
let newPositions = [...positions];
|
|
if (rounded && positions.length > 2) {
|
|
newPositions = [positions[0]];
|
|
for (let i = 1; i < positions.length - 2; i++) {
|
|
const p1 = positions[i];
|
|
const p2 = positions[i + 1];
|
|
const p3 = positions[i + 2];
|
|
const t1 = (1 - tension) / 2;
|
|
const t2 = 1 - t1;
|
|
const x = t2 * t2 * t2 * p1.x + 3 * t2 * t2 * t1 * p2.x + 3 * t2 * t1 * t1 * p3.x + t1 * t1 * t1 * p2.x;
|
|
const y = t2 * t2 * t2 * p1.y + 3 * t2 * t2 * t1 * p2.y + 3 * t2 * t1 * t1 * p3.y + t1 * t1 * t1 * p2.y;
|
|
newPositions.push({ x, y });
|
|
}
|
|
const lastPoint = positions[positions.length - 1];
|
|
newPositions.push(lastPoint);
|
|
}
|
|
for (let i = 0; i < newPositions.length - 2; i++) {
|
|
const p1 = newPositions[i];
|
|
const p2 = newPositions[i + 1];
|
|
const p3 = newPositions[i + 2];
|
|
const currentDirection = {
|
|
x: p2.x - p1.x,
|
|
y: p2.y - p1.y
|
|
};
|
|
const nextDirection = {
|
|
x: p3.x - p2.x,
|
|
y: p3.y - p2.y
|
|
};
|
|
if (currentDirection.x !== nextDirection.x && currentDirection.y !== nextDirection.y)
|
|
continue;
|
|
newPositions.splice(i + 1, 1);
|
|
i--;
|
|
}
|
|
return newPositions.map(
|
|
(position, index) => `${index === 0 ? "M" : "L"} ${position.x} ${position.y}`
|
|
).join(" ");
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-direct.ts
|
|
var EdgePathfindingDirect = class extends EdgePathfindingMethod {
|
|
getPath(_plugin, _canvas, fromPos, _fromBBoxSidePos, _fromSide, toPos, _toBBoxSidePos, _toSide, _isDragging) {
|
|
return {
|
|
svgPath: SvgPathHelper.pathArrayToSvgPath([fromPos, toPos], false),
|
|
center: {
|
|
x: (fromPos.x + toPos.x) / 2,
|
|
y: (fromPos.y + toPos.y) / 2
|
|
},
|
|
rotateArrows: true
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-square.ts
|
|
var EdgePathfindingSquare = class extends EdgePathfindingMethod {
|
|
getPath(_plugin, _canvas, fromPos, fromBBoxSidePos, fromSide, toPos, toBBoxSidePos, toSide, _isDragging) {
|
|
let pathArray = [];
|
|
let center = { x: 0, y: 0 };
|
|
if (fromSide === toSide) {
|
|
const direction = BBoxHelper.direction(fromSide);
|
|
if (BBoxHelper.isHorizontal(fromSide)) {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: Math.max(fromBBoxSidePos.x, toBBoxSidePos.x) + direction * CanvasHelper.GRID_SIZE, y: fromBBoxSidePos.y },
|
|
{ x: Math.max(fromBBoxSidePos.x, toBBoxSidePos.x) + direction * CanvasHelper.GRID_SIZE, y: toBBoxSidePos.y },
|
|
toPos
|
|
];
|
|
} else {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: fromBBoxSidePos.x, y: Math.max(fromBBoxSidePos.y, toBBoxSidePos.y) + direction * CanvasHelper.GRID_SIZE },
|
|
{ x: toBBoxSidePos.x, y: Math.max(fromBBoxSidePos.y, toBBoxSidePos.y) + direction * CanvasHelper.GRID_SIZE },
|
|
toPos
|
|
];
|
|
}
|
|
center = {
|
|
x: (pathArray[1].x + pathArray[2].x) / 2,
|
|
y: (pathArray[1].y + pathArray[2].y) / 2
|
|
};
|
|
} else if (BBoxHelper.isHorizontal(fromSide) === BBoxHelper.isHorizontal(toSide)) {
|
|
if (BBoxHelper.isHorizontal(fromSide)) {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: fromBBoxSidePos.x + (toBBoxSidePos.x - fromBBoxSidePos.x) / 2, y: fromBBoxSidePos.y },
|
|
{ x: fromBBoxSidePos.x + (toBBoxSidePos.x - fromBBoxSidePos.x) / 2, y: toBBoxSidePos.y },
|
|
toPos
|
|
];
|
|
} else {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: fromBBoxSidePos.x, y: fromBBoxSidePos.y + (toBBoxSidePos.y - fromBBoxSidePos.y) / 2 },
|
|
{ x: toBBoxSidePos.x, y: fromBBoxSidePos.y + (toBBoxSidePos.y - fromBBoxSidePos.y) / 2 },
|
|
toPos
|
|
];
|
|
}
|
|
center = {
|
|
x: (fromBBoxSidePos.x + toBBoxSidePos.x) / 2,
|
|
y: (fromBBoxSidePos.y + toBBoxSidePos.y) / 2
|
|
};
|
|
} else {
|
|
if (BBoxHelper.isHorizontal(fromSide)) {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: toBBoxSidePos.x, y: fromBBoxSidePos.y },
|
|
toPos
|
|
];
|
|
} else {
|
|
pathArray = [
|
|
fromPos,
|
|
{ x: fromBBoxSidePos.x, y: toBBoxSidePos.y },
|
|
toPos
|
|
];
|
|
}
|
|
center = {
|
|
x: pathArray[1].x,
|
|
y: pathArray[1].y
|
|
};
|
|
}
|
|
return {
|
|
svgPath: SvgPathHelper.pathArrayToSvgPath(pathArray, false),
|
|
center,
|
|
rotateArrows: false
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/edge-pathfinding-methods/pathfinding-a-star.ts
|
|
var DIRECTIONS2 = [
|
|
{ dx: 1, dy: 0 },
|
|
{ dx: -1, dy: 0 },
|
|
{ dx: 0, dy: 1 },
|
|
{ dx: 0, dy: -1 },
|
|
{ dx: 1, dy: 1 },
|
|
{ dx: -1, dy: 1 },
|
|
{ dx: 1, dy: -1 },
|
|
{ dx: -1, dy: -1 }
|
|
];
|
|
var DIAGONAL_COST = Math.sqrt(2);
|
|
var Node = class {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.gCost = 0;
|
|
this.hCost = 0;
|
|
this.fCost = 0;
|
|
this.parent = null;
|
|
}
|
|
// Only check for x and y, not gCost, hCost, fCost, or parent
|
|
inList(nodes) {
|
|
return nodes.some((n) => n.x === this.x && n.y === this.y);
|
|
}
|
|
};
|
|
var EdgePathfindingAStar = class extends EdgePathfindingMethod {
|
|
getPath(plugin, canvas, fromPos, _fromBBoxSidePos, fromSide, toPos, _toBBoxSidePos, toSide, isDragging) {
|
|
if (isDragging && !plugin.settings.getSetting("edgeStylePathfinderPathLiveUpdate"))
|
|
return null;
|
|
const nodeBBoxes = [...canvas.nodes.values()].filter((node) => {
|
|
const nodeData = node.getData();
|
|
const isGroup = nodeData.type === "group";
|
|
const isOpenPortal = nodeData.portalToFile !== void 0;
|
|
return !isGroup && !isOpenPortal;
|
|
}).map((node) => node.getBBox());
|
|
const fromPosWithMargin = BBoxHelper.moveInDirection(fromPos, fromSide, 10);
|
|
const toPosWithMargin = BBoxHelper.moveInDirection(toPos, toSide, 10);
|
|
const gridResolution = plugin.settings.getSetting("edgeStylePathfinderGridResolution");
|
|
const pathArray = this.aStarAlgorithm(fromPosWithMargin, fromSide, toPosWithMargin, toSide, nodeBBoxes, gridResolution);
|
|
if (!pathArray)
|
|
return null;
|
|
pathArray.splice(0, 0, fromPos);
|
|
pathArray.splice(pathArray.length, 0, toPos);
|
|
const roundedPath = plugin.settings.getSetting("edgeStylePathfinderPathRounded");
|
|
const svgPath = SvgPathHelper.pathArrayToSvgPath(pathArray, roundedPath);
|
|
return {
|
|
svgPath,
|
|
center: pathArray[Math.floor(pathArray.length / 2)],
|
|
rotateArrows: false
|
|
};
|
|
}
|
|
aStarAlgorithm(fromPos, fromSide, toPos, toSide, obstacles, gridResolution) {
|
|
const start = new Node(
|
|
Math.floor(fromPos.x / gridResolution) * gridResolution,
|
|
Math.floor(fromPos.y / gridResolution) * gridResolution
|
|
);
|
|
if (fromSide === "right" && fromPos.x !== start.x)
|
|
start.x += gridResolution;
|
|
if (fromSide === "bottom" && fromPos.y !== start.y)
|
|
start.y += gridResolution;
|
|
const end = new Node(
|
|
Math.floor(toPos.x / gridResolution) * gridResolution,
|
|
Math.floor(toPos.y / gridResolution) * gridResolution
|
|
);
|
|
if (toSide === "right" && toPos.x !== end.x)
|
|
end.x += gridResolution;
|
|
if (toSide === "bottom" && toPos.y !== end.y)
|
|
end.y += gridResolution;
|
|
if (this.isInsideObstacle(start, obstacles) || this.isInsideObstacle(end, obstacles))
|
|
return null;
|
|
const openSet = [start];
|
|
const closedSet = [];
|
|
while (openSet.length > 0) {
|
|
let current = null;
|
|
let lowestFCost = Infinity;
|
|
for (const node of openSet) {
|
|
if (node.fCost < lowestFCost) {
|
|
current = node;
|
|
lowestFCost = node.fCost;
|
|
}
|
|
}
|
|
if (!current)
|
|
return null;
|
|
openSet.splice(openSet.indexOf(current), 1);
|
|
closedSet.push(current);
|
|
if (current.x === end.x && current.y === end.y) {
|
|
return [fromPos, ...this.reconstructPath(current), toPos].map((node) => ({ x: node.x, y: node.y }));
|
|
}
|
|
if (!(current.x === start.x && current.y === start.y) && this.isTouchingObstacle(current, obstacles))
|
|
continue;
|
|
for (const neighbor of this.getPossibleNeighbors(current, obstacles, gridResolution)) {
|
|
if (neighbor.inList(closedSet))
|
|
continue;
|
|
const tentativeGCost = current.gCost + this.getMovementCost({
|
|
dx: neighbor.x - current.x,
|
|
dy: neighbor.y - current.y
|
|
});
|
|
if (!neighbor.inList(openSet) || tentativeGCost < neighbor.gCost) {
|
|
neighbor.parent = current;
|
|
neighbor.gCost = tentativeGCost;
|
|
neighbor.hCost = this.heuristic(neighbor, end);
|
|
neighbor.fCost = neighbor.gCost + neighbor.hCost;
|
|
openSet.push(neighbor);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
// Manhattan distance
|
|
heuristic(node, end) {
|
|
return Math.abs(node.x - end.x) + Math.abs(node.y - end.y);
|
|
}
|
|
// Define a function to check if a position isn't inside any obstacle
|
|
isTouchingObstacle(node, obstacles) {
|
|
return obstacles.some((obstacle) => BBoxHelper.insideBBox(node, obstacle, true));
|
|
}
|
|
isInsideObstacle(node, obstacles) {
|
|
return obstacles.some((obstacle) => BBoxHelper.insideBBox(node, obstacle, false));
|
|
}
|
|
// Define a function to calculate movement cost based on direction
|
|
getMovementCost(direction) {
|
|
return direction.dx !== 0 && direction.dy !== 0 ? DIAGONAL_COST : 1;
|
|
}
|
|
getPossibleNeighbors(node, obstacles, gridResolution) {
|
|
const neighbors = [];
|
|
for (const direction of DIRECTIONS2) {
|
|
const neighbor = new Node(
|
|
node.x + direction.dx * gridResolution,
|
|
node.y + direction.dy * gridResolution
|
|
);
|
|
neighbor.gCost = node.gCost + this.getMovementCost(direction);
|
|
if (this.isInsideObstacle(neighbor, obstacles))
|
|
continue;
|
|
neighbors.push(neighbor);
|
|
}
|
|
return neighbors;
|
|
}
|
|
reconstructPath(node) {
|
|
const path = [];
|
|
while (node) {
|
|
path.push(node);
|
|
node = node.parent;
|
|
}
|
|
return path.reverse();
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/advanced-styles/edge-styles.ts
|
|
var EDGE_PATHFINDING_METHODS = {
|
|
"direct": EdgePathfindingDirect,
|
|
"square": EdgePathfindingSquare,
|
|
"a-star": EdgePathfindingAStar
|
|
};
|
|
var EdgeStylesExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return "edgesStylingFeatureEnabled";
|
|
}
|
|
init() {
|
|
this.allEdgeStyleAttributes = [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customEdgeStyleAttributes")];
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
SettingsManager.SETTINGS_CHANGED_EVENT,
|
|
() => this.allEdgeStyleAttributes = [...BUILTIN_EDGE_STYLE_ATTRIBUTES, ...this.plugin.settings.getSetting("customEdgeStyleAttributes")]
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.PopupMenuCreated,
|
|
(canvas) => this.onPopupMenuCreated(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeChanged,
|
|
(canvas, edge) => this.onEdgeChanged(canvas, edge)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeCenterRequested,
|
|
(canvas, edge, center) => this.onEdgeCenterRequested(canvas, edge, center)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeAdded,
|
|
(canvas, _node) => this.updateAllEdges(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeMoved,
|
|
(canvas, _node) => this.updateAllEdges(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeRemoved,
|
|
(canvas, _node) => this.updateAllEdges(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DraggingStateChanged,
|
|
(canvas, isDragging) => {
|
|
if (isDragging)
|
|
return;
|
|
this.updateAllEdges(canvas);
|
|
}
|
|
));
|
|
}
|
|
onPopupMenuCreated(canvas) {
|
|
var _a;
|
|
const selectedEdges = [...canvas.selection].filter((item) => item.path !== void 0);
|
|
if (canvas.readonly || selectedEdges.length === 0 || selectedEdges.length !== canvas.selection.size)
|
|
return;
|
|
CanvasHelper.addStyleAttributesToPopup(
|
|
this.plugin,
|
|
canvas,
|
|
this.allEdgeStyleAttributes,
|
|
(_a = selectedEdges[0].getData().styleAttributes) != null ? _a : {},
|
|
(attribute, value) => this.setStyleAttributeForSelection(canvas, attribute, value)
|
|
);
|
|
}
|
|
setStyleAttributeForSelection(canvas, attribute, value) {
|
|
const selectedEdges = [...canvas.selection].filter((item) => item.path !== void 0);
|
|
for (const edge of selectedEdges) {
|
|
const edgeData = edge.getData();
|
|
edge.setData({
|
|
...edgeData,
|
|
styleAttributes: {
|
|
...edgeData.styleAttributes,
|
|
[attribute.datasetKey]: value
|
|
}
|
|
});
|
|
}
|
|
canvas.pushHistory(canvas.getData());
|
|
}
|
|
updateAllEdges(canvas) {
|
|
for (const edge of canvas.edges.values()) {
|
|
this.onEdgeChanged(canvas, edge);
|
|
}
|
|
}
|
|
onEdgeChanged(canvas, edge) {
|
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
const edgeData = edge.getData();
|
|
if (!edge.bezier)
|
|
return;
|
|
edge.center = void 0;
|
|
edge.updatePath();
|
|
const pathfindingMethod = (_a = edgeData.styleAttributes) == null ? void 0 : _a.pathfindingMethod;
|
|
if (pathfindingMethod) {
|
|
const fromBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(edge.from.node.getBBox(), edge.from.side);
|
|
const fromPos = edge.from.end === "none" ? fromBBoxSidePos : edge.bezier.from;
|
|
const toBBoxSidePos = BBoxHelper.getCenterOfBBoxSide(edge.to.node.getBBox(), edge.to.side);
|
|
const toPos = edge.to.end === "none" ? toBBoxSidePos : edge.bezier.to;
|
|
const path = new EDGE_PATHFINDING_METHODS[pathfindingMethod]().getPath(this.plugin, canvas, fromPos, fromBBoxSidePos, edge.from.side, toPos, toBBoxSidePos, edge.to.side, canvas.isDragging);
|
|
if (!path)
|
|
return;
|
|
edge.center = path.center;
|
|
edge.path.interaction.setAttr("d", path == null ? void 0 : path.svgPath);
|
|
edge.path.display.setAttr("d", path == null ? void 0 : path.svgPath);
|
|
}
|
|
(_b = edge.labelElement) == null ? void 0 : _b.render();
|
|
const arrowPolygonPoints = this.getArrowPolygonPoints((_c = edgeData.styleAttributes) == null ? void 0 : _c.arrow);
|
|
if ((_d = edge.fromLineEnd) == null ? void 0 : _d.el)
|
|
(_e = edge.fromLineEnd.el.querySelector("polygon")) == null ? void 0 : _e.setAttribute("points", arrowPolygonPoints);
|
|
if ((_f = edge.toLineEnd) == null ? void 0 : _f.el)
|
|
(_g = edge.toLineEnd.el.querySelector("polygon")) == null ? void 0 : _g.setAttribute("points", arrowPolygonPoints);
|
|
if (this.plugin.settings.getSetting("edgeStyleDirectRotateArrow")) {
|
|
this.rotateArrows(edge, pathfindingMethod);
|
|
}
|
|
}
|
|
onEdgeCenterRequested(_canvas, edge, center) {
|
|
var _a, _b, _c, _d;
|
|
center.x = (_b = (_a = edge.center) == null ? void 0 : _a.x) != null ? _b : center.x;
|
|
center.y = (_d = (_c = edge.center) == null ? void 0 : _c.y) != null ? _d : center.y;
|
|
}
|
|
getArrowPolygonPoints(arrowStyle) {
|
|
if (arrowStyle === "halved-triangle")
|
|
return `-2,0 7.5,12 -2,12`;
|
|
else if (arrowStyle === "thin-triangle")
|
|
return `0,0 7,10 0,0 0,10 0,0 -7,10`;
|
|
else if (arrowStyle === "diamond" || arrowStyle === "diamond-outline")
|
|
return `0,0 5,10 0,20 -5,10`;
|
|
else if (arrowStyle === "circle" || arrowStyle === "circle-outline")
|
|
return `0 0, 4.95 1.8, 7.5 6.45, 6.6 11.7, 2.7 15, -2.7 15, -6.6 11.7, -7.5 6.45, -4.95 1.8`;
|
|
else
|
|
return `0,0 6.5,10.4 -6.5,10.4`;
|
|
}
|
|
rotateArrows(edge, pathRouteType) {
|
|
var _a, _b, _c, _d;
|
|
if (pathRouteType !== "direct") {
|
|
if ((_a = edge.fromLineEnd) == null ? void 0 : _a.el)
|
|
edge.fromLineEnd.el.style.translate = "";
|
|
if ((_b = edge.toLineEnd) == null ? void 0 : _b.el)
|
|
edge.toLineEnd.el.style.translate = "";
|
|
return;
|
|
}
|
|
const setArrowRotation = (element, side, rotation) => {
|
|
element.style.transform = element.style.transform.replace(/rotate\([-\d]+(deg|rad)\)/g, `rotate(${rotation}rad)`);
|
|
const offset = BBoxHelper.getSideVector(side);
|
|
element.style.translate = `${offset.x * 7}px ${offset.y * -7}px`;
|
|
};
|
|
const edgeRotation = Math.atan2(edge.bezier.to.y - edge.bezier.from.y, edge.bezier.to.x - edge.bezier.from.x) - Math.PI / 2;
|
|
if ((_c = edge.fromLineEnd) == null ? void 0 : _c.el)
|
|
setArrowRotation(edge.fromLineEnd.el, edge.from.side, edgeRotation);
|
|
if ((_d = edge.toLineEnd) == null ? void 0 : _d.el)
|
|
setArrowRotation(edge.toLineEnd.el, edge.to.side, edgeRotation - Math.PI);
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/dataset-exposers/node-exposer.ts
|
|
function getExposedNodeData(settings) {
|
|
const exposedData = [];
|
|
if (settings.getSetting("nodeStylingFeatureEnabled"))
|
|
exposedData.push("styleAttributes");
|
|
if (settings.getSetting("collapsibleGroupsFeatureEnabled"))
|
|
exposedData.push("isCollapsed");
|
|
if (settings.getSetting("presentationFeatureEnabled"))
|
|
exposedData.push("isStartNode");
|
|
if (settings.getSetting("portalsFeatureEnabled"))
|
|
exposedData.push("portalToFile", "portalId");
|
|
return exposedData;
|
|
}
|
|
var NodeExposerExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeChanged,
|
|
(_canvas, node) => {
|
|
const nodeData = node == null ? void 0 : node.getData();
|
|
if (!nodeData)
|
|
return;
|
|
for (const exposedDataKey of getExposedNodeData(this.plugin.settings)) {
|
|
const datasetPairs = nodeData[exposedDataKey] instanceof Object ? Object.entries(nodeData[exposedDataKey]) : [[exposedDataKey, nodeData[exposedDataKey]]];
|
|
for (const [key, value] of datasetPairs) {
|
|
if (!value)
|
|
delete node.nodeEl.dataset[key];
|
|
else
|
|
node.nodeEl.dataset[key] = value;
|
|
}
|
|
}
|
|
}
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/dataset-exposers/node-interaction-exposer.ts
|
|
var TARGET_NODE_DATASET_PREFIX = "target";
|
|
var NodeInteractionExposerExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.NodeInteraction,
|
|
(canvas, node) => {
|
|
const nodeData = node == null ? void 0 : node.getData();
|
|
if (!nodeData)
|
|
return;
|
|
const interactionEl = canvas.nodeInteractionLayer.interactionEl;
|
|
if (!interactionEl)
|
|
return;
|
|
for (const exposedDataKey of getExposedNodeData(this.plugin.settings)) {
|
|
const datasetPairs = nodeData[exposedDataKey] instanceof Object ? Object.entries(nodeData[exposedDataKey]) : [[exposedDataKey, nodeData[exposedDataKey]]];
|
|
for (const [key, value] of datasetPairs) {
|
|
const modifiedKey = TARGET_NODE_DATASET_PREFIX + key.toString().charAt(0).toUpperCase() + key.toString().slice(1);
|
|
if (!value)
|
|
delete interactionEl.dataset[modifiedKey];
|
|
else
|
|
interactionEl.dataset[modifiedKey] = value;
|
|
}
|
|
}
|
|
}
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/dataset-exposers/edge-exposer.ts
|
|
function getExposedEdgeData(settings) {
|
|
const exposedData = [];
|
|
if (settings.getSetting("edgesStylingFeatureEnabled"))
|
|
exposedData.push("styleAttributes");
|
|
return exposedData;
|
|
}
|
|
var EdgeExposerExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.EdgeChanged,
|
|
(_canvas, edge) => {
|
|
var _a, _b, _c, _d;
|
|
const edgeData = edge == null ? void 0 : edge.getData();
|
|
if (!edgeData)
|
|
return;
|
|
for (const exposedDataKey of getExposedEdgeData(this.plugin.settings)) {
|
|
const datasetPairs = edgeData[exposedDataKey] instanceof Object ? Object.entries(edgeData[exposedDataKey]) : [[exposedDataKey, edgeData[exposedDataKey]]];
|
|
for (const [key, value] of datasetPairs) {
|
|
if (!value) {
|
|
delete edge.path.display.dataset[key];
|
|
if ((_a = edge.fromLineEnd) == null ? void 0 : _a.el)
|
|
delete edge.fromLineEnd.el.dataset[key];
|
|
if ((_b = edge.toLineEnd) == null ? void 0 : _b.el)
|
|
delete edge.toLineEnd.el.dataset[key];
|
|
} else {
|
|
edge.path.display.dataset[key] = value;
|
|
if ((_c = edge.fromLineEnd) == null ? void 0 : _c.el)
|
|
edge.fromLineEnd.el.dataset[key] = value;
|
|
if ((_d = edge.toLineEnd) == null ? void 0 : _d.el)
|
|
edge.toLineEnd.el.dataset[key] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/canvas-extensions/dataset-exposers/canvas-wrapper-exposer.ts
|
|
var EXPOSED_SETTINGS = [
|
|
"disableFontSizeRelativeToZoom",
|
|
"collapsibleGroupsFeatureEnabled",
|
|
"collapsedGroupPreviewOnDrag"
|
|
];
|
|
var CanvasWrapperExposerExtension = class extends CanvasExtension {
|
|
isEnabled() {
|
|
return true;
|
|
}
|
|
init() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
SettingsManager.SETTINGS_CHANGED_EVENT,
|
|
() => this.updateExposedSettings(this.plugin.getCurrentCanvas())
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => this.updateExposedSettings(canvas)
|
|
));
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.DraggingStateChanged,
|
|
(canvas, dragging) => {
|
|
if (dragging)
|
|
canvas.wrapperEl.dataset.isDragging = "true";
|
|
else
|
|
delete canvas.wrapperEl.dataset.isDragging;
|
|
}
|
|
));
|
|
}
|
|
updateExposedSettings(canvas) {
|
|
if (!canvas)
|
|
return;
|
|
for (const setting of EXPOSED_SETTINGS) {
|
|
canvas.wrapperEl.dataset[setting] = this.plugin.settings.getSetting(setting).toString();
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/utils/migration-helper.ts
|
|
var MigrationHelper = class {
|
|
constructor(plugin) {
|
|
this.plugin = plugin;
|
|
}
|
|
async migrate() {
|
|
this.migrateNodeAndEdgeStyles();
|
|
}
|
|
migrateNodeAndEdgeStyles() {
|
|
this.plugin.registerEvent(this.plugin.app.workspace.on(
|
|
CanvasEvent.CanvasChanged,
|
|
(canvas) => {
|
|
for (const node of canvas.nodes.values()) {
|
|
const nodeData = node.getData();
|
|
const newStyleAttributes = {};
|
|
if (nodeData.isSticker)
|
|
newStyleAttributes["border"] = "invisible";
|
|
if (nodeData.borderStyle)
|
|
newStyleAttributes["border"] = nodeData.borderStyle;
|
|
if (nodeData.shape) {
|
|
newStyleAttributes.textAlign = "center";
|
|
newStyleAttributes.shape = nodeData.shape;
|
|
if ((newStyleAttributes == null ? void 0 : newStyleAttributes.shape) === "centered-rectangle")
|
|
delete newStyleAttributes.shape;
|
|
if ((newStyleAttributes == null ? void 0 : newStyleAttributes.shape) === "oval")
|
|
newStyleAttributes.shape = "pill";
|
|
}
|
|
delete nodeData.isSticker;
|
|
delete nodeData.borderStyle;
|
|
delete nodeData.shape;
|
|
node.setData({
|
|
...nodeData,
|
|
styleAttributes: {
|
|
...nodeData.styleAttributes,
|
|
...newStyleAttributes
|
|
}
|
|
});
|
|
}
|
|
for (const edge of canvas.edges.values()) {
|
|
const edgeData = edge.getData();
|
|
const newStyleAttributes = {};
|
|
if (edgeData.edgeStyle)
|
|
newStyleAttributes.edge = edgeData.edgeStyle;
|
|
if (edgeData.edgePathRoute)
|
|
newStyleAttributes.pathfindingMethod = edgeData.edgePathRoute;
|
|
delete edgeData.edgeStyle;
|
|
delete edgeData.edgePathRoute;
|
|
}
|
|
}
|
|
));
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var CANVAS_EXTENSIONS = [
|
|
// Dataset Exposers
|
|
CanvasWrapperExposerExtension,
|
|
NodeExposerExtension,
|
|
EdgeExposerExtension,
|
|
NodeInteractionExposerExtension,
|
|
// Advanced Styles
|
|
NodeStylesExtension,
|
|
EdgeStylesExtension,
|
|
// Basic Extensions
|
|
BetterDefaultSettingsCanvasExtension,
|
|
CommandsCanvasExtension,
|
|
BetterReadonlyCanvasExtension,
|
|
AutoResizeNodeCanvasExtension,
|
|
PropertiesCanvasExtension,
|
|
GroupCanvasExtension,
|
|
// More Advanced Extensions
|
|
CollapsibleGroupsCanvasExtension,
|
|
FocusModeCanvasExtension,
|
|
EncapsulateCanvasExtension,
|
|
ColorPaletteCanvasExtension,
|
|
PresentationCanvasExtension,
|
|
PortalsCanvasExtension
|
|
];
|
|
var AdvancedCanvasPlugin = class extends import_obsidian10.Plugin {
|
|
async onload() {
|
|
this.migrationHelper = new MigrationHelper(this);
|
|
await this.migrationHelper.migrate();
|
|
IconsHelper.addIcons();
|
|
this.settings = new SettingsManager(this);
|
|
await this.settings.loadSettings();
|
|
this.settings.addSettingsTab();
|
|
this.windowsManager = new WindowsManager(this);
|
|
this.canvasPatcher = new CanvasPatcher(this);
|
|
this.canvasExtensions = CANVAS_EXTENSIONS.map((Extension) => new Extension(this));
|
|
}
|
|
onunload() {
|
|
}
|
|
getCurrentCanvasView() {
|
|
const canvasView = this.app.workspace.getActiveViewOfType(import_obsidian10.ItemView);
|
|
if ((canvasView == null ? void 0 : canvasView.getViewType()) !== "canvas")
|
|
return null;
|
|
return canvasView;
|
|
}
|
|
getCurrentCanvas() {
|
|
var _a;
|
|
return ((_a = this.getCurrentCanvasView()) == null ? void 0 : _a.canvas) || null;
|
|
}
|
|
createFileSnapshot(path, content) {
|
|
var _a;
|
|
const fileRecoveryPlugin = (_a = this.app.internalPlugins.plugins["file-recovery"]) == null ? void 0 : _a.instance;
|
|
if (!fileRecoveryPlugin)
|
|
return;
|
|
fileRecoveryPlugin.forceAdd(path, content);
|
|
}
|
|
// this.app.plugins.plugins["advanced-canvas"].enableDebugMode()
|
|
enableDebugMode() {
|
|
if (this.debugHelper)
|
|
return;
|
|
this.debugHelper = new DebugHelper(this);
|
|
}
|
|
};
|
|
|
|
|
|
/* nosourcemap */ |