@bian-womp/spark-workbench 0.2.64 → 0.2.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +147 -1
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +50 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/esm/index.js +147 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +50 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/misc/context/ContextMenuHelpers.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -367,6 +367,142 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
367
367
|
for (const h of Array.from(set))
|
|
368
368
|
h(payload);
|
|
369
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Copy selected nodes and their internal edges.
|
|
372
|
+
* Returns data in a format suitable for pasting.
|
|
373
|
+
* Positions are normalized relative to the selection bounds center.
|
|
374
|
+
* Uses the same logic as duplicate: copies inputs without bindings and supports copyOutputsFrom.
|
|
375
|
+
*/
|
|
376
|
+
copySelection(runner, getNodeSize) {
|
|
377
|
+
const selection = this.getSelection();
|
|
378
|
+
if (selection.nodes.length === 0)
|
|
379
|
+
return null;
|
|
380
|
+
const def = this.export();
|
|
381
|
+
const positions = this.getPositions();
|
|
382
|
+
const selectedNodeSet = new Set(selection.nodes);
|
|
383
|
+
// Collect nodes to copy
|
|
384
|
+
const nodesToCopy = def.nodes.filter((n) => selectedNodeSet.has(n.nodeId));
|
|
385
|
+
if (nodesToCopy.length === 0)
|
|
386
|
+
return null;
|
|
387
|
+
// Calculate bounds
|
|
388
|
+
let minX = Infinity;
|
|
389
|
+
let minY = Infinity;
|
|
390
|
+
let maxX = -Infinity;
|
|
391
|
+
let maxY = -Infinity;
|
|
392
|
+
nodesToCopy.forEach((node) => {
|
|
393
|
+
const pos = positions[node.nodeId] || { x: 0, y: 0 };
|
|
394
|
+
const size = getNodeSize?.(node.nodeId, node.typeId) || {
|
|
395
|
+
width: 200,
|
|
396
|
+
height: 100,
|
|
397
|
+
};
|
|
398
|
+
minX = Math.min(minX, pos.x);
|
|
399
|
+
minY = Math.min(minY, pos.y);
|
|
400
|
+
maxX = Math.max(maxX, pos.x + size.width);
|
|
401
|
+
maxY = Math.max(maxY, pos.y + size.height);
|
|
402
|
+
});
|
|
403
|
+
const bounds = { minX, minY, maxX, maxY };
|
|
404
|
+
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
405
|
+
const centerY = (bounds.minY + bounds.maxY) / 2;
|
|
406
|
+
// Get inputs for each node
|
|
407
|
+
// Include values from inbound edges if those edges are selected
|
|
408
|
+
const allInputs = runner.getInputs(def);
|
|
409
|
+
const selectedEdgeSet = new Set(selection.edges);
|
|
410
|
+
const copiedNodes = nodesToCopy.map((node) => {
|
|
411
|
+
const pos = positions[node.nodeId] || { x: 0, y: 0 };
|
|
412
|
+
// Get all inbound edges for this node
|
|
413
|
+
const inboundEdges = def.edges.filter((e) => e.target.nodeId === node.nodeId);
|
|
414
|
+
// Build set of handles that have inbound edges
|
|
415
|
+
// But only exclude handles whose edges are NOT selected
|
|
416
|
+
const inboundHandlesToExclude = new Set(inboundEdges
|
|
417
|
+
.filter((e) => !selectedEdgeSet.has(e.id)) // Only exclude if edge is not selected
|
|
418
|
+
.map((e) => e.target.handle));
|
|
419
|
+
const allNodeInputs = allInputs[node.nodeId] || {};
|
|
420
|
+
// Include inputs that either:
|
|
421
|
+
// 1. Don't have inbound edges (literal values)
|
|
422
|
+
// 2. Have inbound edges that ARE selected (preserve the value from the edge)
|
|
423
|
+
const inputsToCopy = Object.fromEntries(Object.entries(allNodeInputs).filter(([handle]) => !inboundHandlesToExclude.has(handle)));
|
|
424
|
+
return {
|
|
425
|
+
typeId: node.typeId,
|
|
426
|
+
params: node.params,
|
|
427
|
+
resolvedHandles: node.resolvedHandles,
|
|
428
|
+
position: {
|
|
429
|
+
x: pos.x - centerX,
|
|
430
|
+
y: pos.y - centerY,
|
|
431
|
+
},
|
|
432
|
+
inputs: inputsToCopy,
|
|
433
|
+
originalNodeId: node.nodeId,
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
// Collect edges between copied nodes
|
|
437
|
+
const copiedEdges = def.edges
|
|
438
|
+
.filter((edge) => {
|
|
439
|
+
return (selectedNodeSet.has(edge.source.nodeId) &&
|
|
440
|
+
selectedNodeSet.has(edge.target.nodeId));
|
|
441
|
+
})
|
|
442
|
+
.map((edge) => ({
|
|
443
|
+
sourceNodeId: edge.source.nodeId,
|
|
444
|
+
sourceHandle: edge.source.handle,
|
|
445
|
+
targetNodeId: edge.target.nodeId,
|
|
446
|
+
targetHandle: edge.target.handle,
|
|
447
|
+
typeId: edge.typeId,
|
|
448
|
+
}));
|
|
449
|
+
return {
|
|
450
|
+
nodes: copiedNodes,
|
|
451
|
+
edges: copiedEdges,
|
|
452
|
+
bounds,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Paste copied graph data at the specified center position.
|
|
457
|
+
* Returns the mapping from original node IDs to new node IDs.
|
|
458
|
+
* Uses copyOutputsFrom to copy outputs from original nodes (like duplicate does).
|
|
459
|
+
*/
|
|
460
|
+
pasteCopiedData(data, center) {
|
|
461
|
+
const nodeIdMap = new Map();
|
|
462
|
+
const edgeIds = [];
|
|
463
|
+
// Add nodes
|
|
464
|
+
for (const nodeData of data.nodes) {
|
|
465
|
+
const newNodeId = this.addNode({
|
|
466
|
+
typeId: nodeData.typeId,
|
|
467
|
+
params: nodeData.params,
|
|
468
|
+
resolvedHandles: nodeData.resolvedHandles,
|
|
469
|
+
position: {
|
|
470
|
+
x: nodeData.position.x + center.x,
|
|
471
|
+
y: nodeData.position.y + center.y,
|
|
472
|
+
},
|
|
473
|
+
}, {
|
|
474
|
+
inputs: nodeData.inputs,
|
|
475
|
+
copyOutputsFrom: nodeData.originalNodeId,
|
|
476
|
+
dry: true,
|
|
477
|
+
});
|
|
478
|
+
nodeIdMap.set(nodeData.originalNodeId, newNodeId);
|
|
479
|
+
}
|
|
480
|
+
// Add edges
|
|
481
|
+
for (const edgeData of data.edges) {
|
|
482
|
+
const newSourceNodeId = nodeIdMap.get(edgeData.sourceNodeId);
|
|
483
|
+
const newTargetNodeId = nodeIdMap.get(edgeData.targetNodeId);
|
|
484
|
+
if (newSourceNodeId && newTargetNodeId) {
|
|
485
|
+
const edgeId = this.connect({
|
|
486
|
+
source: {
|
|
487
|
+
nodeId: newSourceNodeId,
|
|
488
|
+
handle: edgeData.sourceHandle,
|
|
489
|
+
},
|
|
490
|
+
target: {
|
|
491
|
+
nodeId: newTargetNodeId,
|
|
492
|
+
handle: edgeData.targetHandle,
|
|
493
|
+
},
|
|
494
|
+
typeId: edgeData.typeId,
|
|
495
|
+
}, { dry: true });
|
|
496
|
+
edgeIds.push(edgeId);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
// Select the newly pasted nodes
|
|
500
|
+
this.setSelection({
|
|
501
|
+
nodes: Array.from(nodeIdMap.values()),
|
|
502
|
+
edges: edgeIds,
|
|
503
|
+
});
|
|
504
|
+
return { nodeIdMap, edgeIds };
|
|
505
|
+
}
|
|
370
506
|
}
|
|
371
507
|
|
|
372
508
|
class CLIWorkbench {
|
|
@@ -3648,7 +3784,7 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
|
|
|
3648
3784
|
.map((e) => e.target.handle));
|
|
3649
3785
|
const allInputs = runner.getInputs(def)[nodeId] || {};
|
|
3650
3786
|
const inputsWithoutBindings = Object.fromEntries(Object.entries(allInputs).filter(([handle]) => !inboundHandles.has(handle)));
|
|
3651
|
-
wb.addNode({
|
|
3787
|
+
const newNodeId = wb.addNode({
|
|
3652
3788
|
typeId: n.typeId,
|
|
3653
3789
|
params: n.params,
|
|
3654
3790
|
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
@@ -3658,6 +3794,11 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
|
|
|
3658
3794
|
copyOutputsFrom: nodeId,
|
|
3659
3795
|
dry: true,
|
|
3660
3796
|
});
|
|
3797
|
+
// Select the newly duplicated node
|
|
3798
|
+
wb.setSelection({
|
|
3799
|
+
nodes: [newNodeId],
|
|
3800
|
+
edges: [],
|
|
3801
|
+
});
|
|
3661
3802
|
onClose();
|
|
3662
3803
|
},
|
|
3663
3804
|
onDuplicateWithEdges: async () => {
|
|
@@ -3689,6 +3830,11 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
|
|
|
3689
3830
|
typeId: edge.typeId,
|
|
3690
3831
|
}, { dry: true });
|
|
3691
3832
|
}
|
|
3833
|
+
// Select the newly duplicated node and edges
|
|
3834
|
+
wb.setSelection({
|
|
3835
|
+
nodes: [newNodeId],
|
|
3836
|
+
edges: [],
|
|
3837
|
+
});
|
|
3692
3838
|
onClose();
|
|
3693
3839
|
},
|
|
3694
3840
|
onRunPull: async () => {
|