fix: pasting nodes

This commit is contained in:
2026-05-07 17:39:58 +02:00
parent 4aff3874d3
commit 9a7a7166b7
4 changed files with 37 additions and 30 deletions
@@ -742,25 +742,26 @@ export class GraphManager extends EventEmitter<{
return id;
}
createGraph(nodes: NodeInstance[], edges: [number, number, number, string][]) {
createGraph(nodes: SerializedNode[], edges: [number, number, number, string][]) {
// map old ids to new ids
const idMap = new SvelteMap<number, number>();
let startId = this.createNodeId();
nodes = nodes.map((node) => {
const instances: NodeInstance[] = nodes.map((node) => {
const id = startId++;
idMap.set(node.id, id);
const type = this.registry.getNode(node.type);
if (!type && !node.type.startsWith('__internal/')) {
throw new Error(`Node type not found: ${node.type}`);
}
return { ...node, id, tmp: { type } };
const registryType = this.registry.getNode(node.type);
return { ...node, id, state: { type: registryType } };
});
const _edges = edges.map((edge) => {
const from = nodes.find((n) => n.id === idMap.get(edge[0]));
const to = nodes.find((n) => n.id === idMap.get(edge[2]));
const from = instances.find((n) => n.id === idMap.get(edge[0]));
const to = instances.find((n) => n.id === idMap.get(edge[2]));
if (!from || !to) {
throw new Error('Edge references non-existing node');
@@ -775,14 +776,15 @@ export class GraphManager extends EventEmitter<{
return [from, edge[1], to, edge[3]] as Edge;
});
for (const node of nodes) {
this.nodes.set(node.id, node);
for (const node of instances) {
const n = $state(node);
this.nodes.set(node.id, n);
}
this.edges.push(..._edges);
this.save();
return nodes;
return instances;
}
getUnusedGroups() {
@@ -1,11 +1,16 @@
import { animate, lerp } from '$lib/helpers';
import type { NodeInstance, Socket } from '@nodarium/types';
import type { NodeInstance, SerializedEdge, SerializedNode, Socket } from '@nodarium/types';
import { getContext, setContext } from 'svelte';
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
import type { OrthographicCamera, Vector3 } from 'three';
import type { GraphManager } from './graph-manager.svelte';
import { ColorGenerator } from './graph/colors';
import { getNodeHeight, getParameterHeight } from './helpers/nodeHelpers';
import {
getNodeHeight,
getParameterHeight,
serializeEdge,
serializeNode
} from './helpers/nodeHelpers';
const graphStateKey = Symbol('graph-state');
export function getGraphState() {
@@ -95,8 +100,8 @@ export class GraphState {
cameraPosition: [number, number, number] = $state([140, 100, 3.5]);
clipboard: null | {
nodes: NodeInstance[];
edges: [number, number, number, string][];
nodes: SerializedNode[];
edges: SerializedEdge[];
} = null;
cameraBounds = $derived([
@@ -190,12 +195,10 @@ export class GraphState {
if (this.activeNodeId === -1 && !this.selectedNodes?.size) {
return;
}
let nodes = [
this.activeNodeId,
...(this.selectedNodes?.values() || [])
]
const ids = new SvelteSet([this.activeNodeId, ...(this.selectedNodes?.values() || [])]);
let nodes = [...ids]
.map((id) => this.graph.getNode(id))
.filter(b => !!b);
.filter((b): b is NodeInstance => !!b);
const edges = this.graph.getEdgesBetweenNodes(nodes);
nodes = nodes.map((node) => ({
@@ -203,13 +206,12 @@ export class GraphState {
position: [
this.mousePosition[0] - node.position[0],
this.mousePosition[1] - node.position[1]
],
tmp: undefined
]
}));
this.clipboard = {
nodes: nodes,
edges: edges
nodes: nodes.map(n => serializeNode(n)),
edges: edges.map(e => serializeEdge(e))
};
}
@@ -255,13 +257,16 @@ export class GraphState {
pasteNodes() {
if (!this.clipboard) return;
const nodes = this.clipboard.nodes
.map((node) => {
node.position[0] = this.mousePosition[0] - node.position[0];
node.position[1] = this.mousePosition[1] - node.position[1];
return node;
})
.filter(Boolean) as NodeInstance[];
// Create fresh node objects — never mutate clipboard so repeat pastes work correctly.
// State is also spread (with cleared parents/children) so createGraph's mutations
// don't corrupt the clipboard's stored state references.
const nodes = this.clipboard.nodes.map((node) => ({
...node,
position: [
this.mousePosition[0] - node.position[0],
this.mousePosition[1] - node.position[1]
] as [number, number]
}));
const newNodes = this.graph.createGraph(nodes, this.clipboard.edges);
this.selectedNodes.clear();
@@ -228,7 +228,7 @@
style:transform={`scale(${graphState.cameraPosition[2] * 0.1})`}
class:hovering-sockets={graphState.activeSocket}
>
{#each graph.nodeArray as node, index (node.id)}
{#each graph.nodeArray as node, index (node)}
<NodeEl
bind:node={graph.nodeArray[index]}
inView={node ? graphState.isNodeInView(node) : false}
@@ -8,7 +8,7 @@
graph
? {
...graph,
nodes: graph.nodes.map((n: object) => ({ ...n, tmp: undefined, state: undefined }))
nodes: graph.nodes.map((n: object) => ({ ...n, state: undefined }))
}
: null
);