@bian-womp/spark-workbench 0.2.63 → 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.
@@ -1,7 +1,34 @@
1
- import type { GraphDefinition, GraphNodeDefinition, GraphEdgeDefinition } from "@bian-womp/spark-protocol";
1
+ import type { GraphDefinition, GraphNodeDefinition, GraphEdgeDefinition, ResolvedHandles } from "@bian-womp/spark-protocol";
2
2
  import { Registry } from "@bian-womp/spark-graph";
3
3
  import { AbstractWorkbench } from "./AbstractWorkbench";
4
4
  import type { ValidationResult, WorkbenchEventName, WorkbenchEventListener } from "./contracts";
5
+ import type { IGraphRunner } from "../runtime/IGraphRunner";
6
+ export type CopiedGraphData = {
7
+ nodes: Array<{
8
+ typeId: string;
9
+ params?: Record<string, unknown>;
10
+ resolvedHandles?: Partial<ResolvedHandles>;
11
+ position: {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ inputs?: Record<string, unknown>;
16
+ originalNodeId: string;
17
+ }>;
18
+ edges: Array<{
19
+ sourceNodeId: string;
20
+ sourceHandle: string;
21
+ targetNodeId: string;
22
+ targetHandle: string;
23
+ typeId?: string;
24
+ }>;
25
+ bounds: {
26
+ minX: number;
27
+ minY: number;
28
+ maxX: number;
29
+ maxY: number;
30
+ };
31
+ };
5
32
  export declare class InMemoryWorkbench extends AbstractWorkbench {
6
33
  private def;
7
34
  private positions;
@@ -104,5 +131,27 @@ export declare class InMemoryWorkbench extends AbstractWorkbench {
104
131
  }): void;
105
132
  on<K extends WorkbenchEventName>(event: K, handler: WorkbenchEventListener<K>): () => void;
106
133
  private emit;
134
+ /**
135
+ * Copy selected nodes and their internal edges.
136
+ * Returns data in a format suitable for pasting.
137
+ * Positions are normalized relative to the selection bounds center.
138
+ * Uses the same logic as duplicate: copies inputs without bindings and supports copyOutputsFrom.
139
+ */
140
+ copySelection(runner: IGraphRunner, getNodeSize?: (nodeId: string, typeId: string) => {
141
+ width: number;
142
+ height: number;
143
+ } | undefined): CopiedGraphData | null;
144
+ /**
145
+ * Paste copied graph data at the specified center position.
146
+ * Returns the mapping from original node IDs to new node IDs.
147
+ * Uses copyOutputsFrom to copy outputs from original nodes (like duplicate does).
148
+ */
149
+ pasteCopiedData(data: CopiedGraphData, center: {
150
+ x: number;
151
+ y: number;
152
+ }): {
153
+ nodeIdMap: Map<string, string>;
154
+ edgeIds: string[];
155
+ };
107
156
  }
108
157
  //# sourceMappingURL=InMemoryWorkbench.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InMemoryWorkbench.d.ts","sourceRoot":"","sources":["../../../../src/core/InMemoryWorkbench.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InMemoryWorkbench.d.ts","sourceRoot":"","sources":["../../../../src/core/InMemoryWorkbench.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMenuHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/misc/context/ContextMenuHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,iBAAiB,EACrB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAClE,OAAO,EAAE,MAAM,IAAI,GAClB,uBAAuB,CAyLzB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,iBAAiB,EACrB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GACjE,MAAM,EAAE,CAgCV"}
1
+ {"version":3,"file":"ContextMenuHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/misc/context/ContextMenuHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,iBAAiB,EACrB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAClE,OAAO,EAAE,MAAM,IAAI,GAClB,uBAAuB,CAsMzB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,iBAAiB,EACrB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GACjE,MAAM,EAAE,CAgCV"}
package/lib/esm/index.js CHANGED
@@ -365,6 +365,142 @@ class InMemoryWorkbench extends AbstractWorkbench {
365
365
  for (const h of Array.from(set))
366
366
  h(payload);
367
367
  }
368
+ /**
369
+ * Copy selected nodes and their internal edges.
370
+ * Returns data in a format suitable for pasting.
371
+ * Positions are normalized relative to the selection bounds center.
372
+ * Uses the same logic as duplicate: copies inputs without bindings and supports copyOutputsFrom.
373
+ */
374
+ copySelection(runner, getNodeSize) {
375
+ const selection = this.getSelection();
376
+ if (selection.nodes.length === 0)
377
+ return null;
378
+ const def = this.export();
379
+ const positions = this.getPositions();
380
+ const selectedNodeSet = new Set(selection.nodes);
381
+ // Collect nodes to copy
382
+ const nodesToCopy = def.nodes.filter((n) => selectedNodeSet.has(n.nodeId));
383
+ if (nodesToCopy.length === 0)
384
+ return null;
385
+ // Calculate bounds
386
+ let minX = Infinity;
387
+ let minY = Infinity;
388
+ let maxX = -Infinity;
389
+ let maxY = -Infinity;
390
+ nodesToCopy.forEach((node) => {
391
+ const pos = positions[node.nodeId] || { x: 0, y: 0 };
392
+ const size = getNodeSize?.(node.nodeId, node.typeId) || {
393
+ width: 200,
394
+ height: 100,
395
+ };
396
+ minX = Math.min(minX, pos.x);
397
+ minY = Math.min(minY, pos.y);
398
+ maxX = Math.max(maxX, pos.x + size.width);
399
+ maxY = Math.max(maxY, pos.y + size.height);
400
+ });
401
+ const bounds = { minX, minY, maxX, maxY };
402
+ const centerX = (bounds.minX + bounds.maxX) / 2;
403
+ const centerY = (bounds.minY + bounds.maxY) / 2;
404
+ // Get inputs for each node
405
+ // Include values from inbound edges if those edges are selected
406
+ const allInputs = runner.getInputs(def);
407
+ const selectedEdgeSet = new Set(selection.edges);
408
+ const copiedNodes = nodesToCopy.map((node) => {
409
+ const pos = positions[node.nodeId] || { x: 0, y: 0 };
410
+ // Get all inbound edges for this node
411
+ const inboundEdges = def.edges.filter((e) => e.target.nodeId === node.nodeId);
412
+ // Build set of handles that have inbound edges
413
+ // But only exclude handles whose edges are NOT selected
414
+ const inboundHandlesToExclude = new Set(inboundEdges
415
+ .filter((e) => !selectedEdgeSet.has(e.id)) // Only exclude if edge is not selected
416
+ .map((e) => e.target.handle));
417
+ const allNodeInputs = allInputs[node.nodeId] || {};
418
+ // Include inputs that either:
419
+ // 1. Don't have inbound edges (literal values)
420
+ // 2. Have inbound edges that ARE selected (preserve the value from the edge)
421
+ const inputsToCopy = Object.fromEntries(Object.entries(allNodeInputs).filter(([handle]) => !inboundHandlesToExclude.has(handle)));
422
+ return {
423
+ typeId: node.typeId,
424
+ params: node.params,
425
+ resolvedHandles: node.resolvedHandles,
426
+ position: {
427
+ x: pos.x - centerX,
428
+ y: pos.y - centerY,
429
+ },
430
+ inputs: inputsToCopy,
431
+ originalNodeId: node.nodeId,
432
+ };
433
+ });
434
+ // Collect edges between copied nodes
435
+ const copiedEdges = def.edges
436
+ .filter((edge) => {
437
+ return (selectedNodeSet.has(edge.source.nodeId) &&
438
+ selectedNodeSet.has(edge.target.nodeId));
439
+ })
440
+ .map((edge) => ({
441
+ sourceNodeId: edge.source.nodeId,
442
+ sourceHandle: edge.source.handle,
443
+ targetNodeId: edge.target.nodeId,
444
+ targetHandle: edge.target.handle,
445
+ typeId: edge.typeId,
446
+ }));
447
+ return {
448
+ nodes: copiedNodes,
449
+ edges: copiedEdges,
450
+ bounds,
451
+ };
452
+ }
453
+ /**
454
+ * Paste copied graph data at the specified center position.
455
+ * Returns the mapping from original node IDs to new node IDs.
456
+ * Uses copyOutputsFrom to copy outputs from original nodes (like duplicate does).
457
+ */
458
+ pasteCopiedData(data, center) {
459
+ const nodeIdMap = new Map();
460
+ const edgeIds = [];
461
+ // Add nodes
462
+ for (const nodeData of data.nodes) {
463
+ const newNodeId = this.addNode({
464
+ typeId: nodeData.typeId,
465
+ params: nodeData.params,
466
+ resolvedHandles: nodeData.resolvedHandles,
467
+ position: {
468
+ x: nodeData.position.x + center.x,
469
+ y: nodeData.position.y + center.y,
470
+ },
471
+ }, {
472
+ inputs: nodeData.inputs,
473
+ copyOutputsFrom: nodeData.originalNodeId,
474
+ dry: true,
475
+ });
476
+ nodeIdMap.set(nodeData.originalNodeId, newNodeId);
477
+ }
478
+ // Add edges
479
+ for (const edgeData of data.edges) {
480
+ const newSourceNodeId = nodeIdMap.get(edgeData.sourceNodeId);
481
+ const newTargetNodeId = nodeIdMap.get(edgeData.targetNodeId);
482
+ if (newSourceNodeId && newTargetNodeId) {
483
+ const edgeId = this.connect({
484
+ source: {
485
+ nodeId: newSourceNodeId,
486
+ handle: edgeData.sourceHandle,
487
+ },
488
+ target: {
489
+ nodeId: newTargetNodeId,
490
+ handle: edgeData.targetHandle,
491
+ },
492
+ typeId: edgeData.typeId,
493
+ }, { dry: true });
494
+ edgeIds.push(edgeId);
495
+ }
496
+ }
497
+ // Select the newly pasted nodes
498
+ this.setSelection({
499
+ nodes: Array.from(nodeIdMap.values()),
500
+ edges: edgeIds,
501
+ });
502
+ return { nodeIdMap, edgeIds };
503
+ }
368
504
  }
369
505
 
370
506
  class CLIWorkbench {
@@ -3646,7 +3782,7 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
3646
3782
  .map((e) => e.target.handle));
3647
3783
  const allInputs = runner.getInputs(def)[nodeId] || {};
3648
3784
  const inputsWithoutBindings = Object.fromEntries(Object.entries(allInputs).filter(([handle]) => !inboundHandles.has(handle)));
3649
- wb.addNode({
3785
+ const newNodeId = wb.addNode({
3650
3786
  typeId: n.typeId,
3651
3787
  params: n.params,
3652
3788
  position: { x: pos.x + 24, y: pos.y + 24 },
@@ -3656,6 +3792,11 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
3656
3792
  copyOutputsFrom: nodeId,
3657
3793
  dry: true,
3658
3794
  });
3795
+ // Select the newly duplicated node
3796
+ wb.setSelection({
3797
+ nodes: [newNodeId],
3798
+ edges: [],
3799
+ });
3659
3800
  onClose();
3660
3801
  },
3661
3802
  onDuplicateWithEdges: async () => {
@@ -3687,6 +3828,11 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
3687
3828
  typeId: edge.typeId,
3688
3829
  }, { dry: true });
3689
3830
  }
3831
+ // Select the newly duplicated node and edges
3832
+ wb.setSelection({
3833
+ nodes: [newNodeId],
3834
+ edges: [],
3835
+ });
3690
3836
  onClose();
3691
3837
  },
3692
3838
  onRunPull: async () => {