fix: pasting nodes
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user