Compare commits

..

2 Commits

Author SHA1 Message Date
Max Richter
a1ea56093c fix: correctly handle node wrapper resizing
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m57s
2025-12-01 19:48:40 +01:00
Max Richter
1850e21810 fix: make clipboard work 2025-12-01 19:30:44 +01:00
3 changed files with 28 additions and 28 deletions

View File

@@ -397,17 +397,17 @@ export class GraphManager extends EventEmitter<{
} }
createNodeId() { createNodeId() {
const max = Math.max(0, ...this.nodes.keys()); return Math.max(0, ...this.nodes.keys()) + 1;
return max + 1;
} }
createGraph(nodes: Node[], edges: [number, number, number, string][]) { createGraph(nodes: Node[], edges: [number, number, number, string][]) {
// map old ids to new ids // map old ids to new ids
const idMap = new Map<number, number>(); const idMap = new Map<number, number>();
let startId = this.createNodeId()
nodes = nodes.map((node) => { nodes = nodes.map((node) => {
const id = this.createNodeId(); const id = startId++;
idMap.set(node.id, id); idMap.set(node.id, id);
const type = this.registry.getNode(node.type); const type = this.registry.getNode(node.type);
if (!type) { if (!type) {

View File

@@ -26,15 +26,16 @@ export class GraphState {
constructor(private graph: GraphManager) { } constructor(private graph: GraphManager) { }
cameraPosition: [number, number, number] = $state([0, 0, 4]); width = $state(100);
wrapper = $state<HTMLDivElement>(null!); height = $state(100);
wrapper = $state<HTMLDivElement>(null!);
rect: DOMRect = $derived( rect: DOMRect = $derived(
this.wrapper ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0), (this.wrapper && this.width && this.height) ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0),
); );
width = $derived(this.rect?.width ?? 100);
height = $derived(this.rect?.height ?? 100);
camera = $state<OrthographicCamera>(null!); camera = $state<OrthographicCamera>(null!);
cameraPosition: [number, number, number] = $state([0, 0, 4]);
clipboard: null | { clipboard: null | {
nodes: Node[]; nodes: Node[];
@@ -192,43 +193,42 @@ export class GraphState {
copyNodes() { copyNodes() {
if (this.activeNodeId === -1 && !this.selectedNodes?.size) if (this.activeNodeId === -1 && !this.selectedNodes?.size)
return; return;
let _nodes = [ let nodes = [
this.activeNodeId, this.activeNodeId,
...(this.selectedNodes?.values() || []), ...(this.selectedNodes?.values() || []),
] ]
.map((id) => this.graph.getNode(id)) .map((id) => this.graph.getNode(id))
.filter(Boolean) as Node[]; .filter(b => !!b);
const _edges = this.graph.getEdgesBetweenNodes(_nodes); const edges = this.graph.getEdgesBetweenNodes(nodes);
_nodes = $state.snapshot( nodes = nodes.map((node) => ({
_nodes.map((_node) => ({ ...node,
..._node, position: [
tmp: { this.mousePosition[0] - node.position[0],
downX: this.mousePosition[0] - _node.position[0], this.mousePosition[1] - node.position[1],
downY: this.mousePosition[1] - _node.position[1], ],
}, tmp: undefined,
})), }));
);
this.clipboard = { this.clipboard = {
nodes: _nodes, nodes: nodes,
edges: _edges, edges: edges,
}; };
} }
pasteNodes() { pasteNodes() {
if (!this.clipboard) return; if (!this.clipboard) return;
const _nodes = this.clipboard.nodes const nodes = this.clipboard.nodes
.map((node) => { .map((node) => {
node.tmp = node.tmp || {}; node.tmp = node.tmp || {};
node.position[0] = this.mousePosition[0] - (node?.tmp?.downX || 0); node.position[0] = this.mousePosition[0] - node.position[0];
node.position[1] = this.mousePosition[1] - (node?.tmp?.downY || 0); node.position[1] = this.mousePosition[1] - node.position[1];
return node; return node;
}) })
.filter(Boolean) as Node[]; .filter(Boolean) as Node[];
const newNodes = this.graph.createGraph(_nodes, this.clipboard.edges); const newNodes = this.graph.createGraph(nodes, this.clipboard.edges);
this.selectedNodes.clear(); this.selectedNodes.clear();
for (const node of newNodes) { for (const node of newNodes) {
this.selectedNodes.add(node.id); this.selectedNodes.add(node.id);

View File

@@ -37,14 +37,14 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
key: "c", key: "c",
ctrl: true, ctrl: true,
description: "Copy active nodes", description: "Copy active nodes",
callback: graphState.copyNodes, callback: () => graphState.copyNodes(),
}); });
keymap.addShortcut({ keymap.addShortcut({
key: "v", key: "v",
ctrl: true, ctrl: true,
description: "Paste nodes", description: "Paste nodes",
callback: graphState.pasteNodes, callback: () => graphState.pasteNodes(),
}); });
keymap.addShortcut({ keymap.addShortcut({