@bian-womp/spark-workbench 0.2.83 → 0.2.84
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 +285 -208
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +16 -25
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +4 -1
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/core/ui-extensions.d.ts +16 -7
- package/lib/cjs/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +2 -0
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts +0 -14
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts +4 -0
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts.map +1 -0
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts +15 -0
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts +2 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +9 -2
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/misc/merge-utils.d.ts +6 -1
- package/lib/cjs/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/cjs/src/misc/types.d.ts +4 -0
- package/lib/cjs/src/misc/types.d.ts.map +1 -1
- package/lib/esm/index.js +287 -210
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +16 -25
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +4 -1
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/core/ui-extensions.d.ts +16 -7
- package/lib/esm/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +2 -0
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts +0 -14
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNodeContent.d.ts +4 -0
- package/lib/esm/src/misc/DefaultNodeContent.d.ts.map +1 -0
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts +15 -0
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts +2 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/hooks.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +9 -2
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/misc/merge-utils.d.ts +6 -1
- package/lib/esm/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/esm/src/misc/types.d.ts +4 -0
- package/lib/esm/src/misc/types.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -75,6 +75,13 @@ class DefaultUIExtensionRegistry {
|
|
|
75
75
|
getNodeContextMenuRenderer() {
|
|
76
76
|
return this.nodeContextMenuRenderer;
|
|
77
77
|
}
|
|
78
|
+
registerSelectionContextMenuRenderer(renderer) {
|
|
79
|
+
this.selectionContextMenuRenderer = renderer;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
getSelectionContextMenuRenderer() {
|
|
83
|
+
return this.selectionContextMenuRenderer;
|
|
84
|
+
}
|
|
78
85
|
// Layout function overrides
|
|
79
86
|
registerEstimateNodeSize(override) {
|
|
80
87
|
this.estimateNodeSizeOverride = override;
|
|
@@ -126,6 +133,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
126
133
|
this._def = { nodes: [], edges: [] };
|
|
127
134
|
this.listeners = new Map();
|
|
128
135
|
this.positions = {};
|
|
136
|
+
this.sizes = {};
|
|
129
137
|
this.selection = {
|
|
130
138
|
nodes: [],
|
|
131
139
|
edges: [],
|
|
@@ -287,27 +295,36 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
287
295
|
});
|
|
288
296
|
}
|
|
289
297
|
// Position and selection APIs for React Flow bridge
|
|
290
|
-
setPositions(
|
|
291
|
-
this.positions = { ...this.positions, ...
|
|
298
|
+
setPositions(positions, options) {
|
|
299
|
+
this.positions = { ...this.positions, ...positions };
|
|
300
|
+
this.emit("graphUiChanged", { change: { type: "moveNodes" }, ...options });
|
|
301
|
+
}
|
|
302
|
+
getPositions() {
|
|
303
|
+
return { ...this.positions };
|
|
304
|
+
}
|
|
305
|
+
setSizes(sizes, options) {
|
|
306
|
+
for (const [nodeId, size] of Object.entries(sizes)) {
|
|
307
|
+
if (size) {
|
|
308
|
+
this.sizes = { ...this.sizes, [nodeId]: size };
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.sizes = lod.omit(this.sizes, nodeId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
292
314
|
this.emit("graphUiChanged", {
|
|
293
|
-
|
|
294
|
-
change: { type: "moveNodes" },
|
|
315
|
+
change: { type: "resizeNodes" },
|
|
295
316
|
...options,
|
|
296
317
|
});
|
|
297
318
|
}
|
|
298
|
-
|
|
299
|
-
return { ...this.
|
|
319
|
+
getSizes() {
|
|
320
|
+
return { ...this.sizes };
|
|
300
321
|
}
|
|
301
322
|
setSelectionInternal(sel, options) {
|
|
302
323
|
if (lod.isEqual(this.selection, sel))
|
|
303
324
|
return;
|
|
304
325
|
this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
|
|
305
326
|
this.emit("selectionChanged", this.selection);
|
|
306
|
-
this.emit("graphUiChanged", {
|
|
307
|
-
def: this._def,
|
|
308
|
-
change: { type: "selection" },
|
|
309
|
-
...options,
|
|
310
|
-
});
|
|
327
|
+
this.emit("graphUiChanged", { change: { type: "selection" }, ...options });
|
|
311
328
|
}
|
|
312
329
|
setSelection(sel, options) {
|
|
313
330
|
this.setSelectionInternal(sel, options);
|
|
@@ -338,10 +355,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
338
355
|
if (lod.isEqual(this.viewport, viewport))
|
|
339
356
|
return;
|
|
340
357
|
this.viewport = { ...viewport };
|
|
341
|
-
this.emit("graphUiChanged", {
|
|
342
|
-
def: this._def,
|
|
343
|
-
change: { type: "viewport" },
|
|
344
|
-
});
|
|
358
|
+
this.emit("graphUiChanged", { change: { type: "viewport" } });
|
|
345
359
|
}
|
|
346
360
|
getViewport() {
|
|
347
361
|
return this.viewport ? { ...this.viewport } : null;
|
|
@@ -353,6 +367,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
353
367
|
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
354
368
|
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
355
369
|
const filteredNodeNames = Object.fromEntries(Object.entries(this.nodeNames).filter(([id]) => defNodeIds.has(id)));
|
|
370
|
+
const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
356
371
|
return {
|
|
357
372
|
positions: Object.keys(filteredPositions).length > 0
|
|
358
373
|
? filteredPositions
|
|
@@ -367,6 +382,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
367
382
|
nodeNames: Object.keys(filteredNodeNames).length > 0
|
|
368
383
|
? filteredNodeNames
|
|
369
384
|
: undefined,
|
|
385
|
+
sizes: Object.keys(filteredSizes).length > 0 ? filteredSizes : undefined,
|
|
370
386
|
};
|
|
371
387
|
}
|
|
372
388
|
setUIState(ui) {
|
|
@@ -381,12 +397,27 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
381
397
|
edges: [...ui.selection.edges],
|
|
382
398
|
};
|
|
383
399
|
this.emit("selectionChanged", this.selection);
|
|
400
|
+
this.emit("graphUiChanged", {
|
|
401
|
+
change: { type: "selection" },
|
|
402
|
+
init: true,
|
|
403
|
+
});
|
|
384
404
|
}
|
|
385
405
|
if (ui.viewport) {
|
|
386
406
|
this.viewport = { ...ui.viewport };
|
|
407
|
+
this.emit("graphUiChanged", { change: { type: "viewport" }, init: true });
|
|
387
408
|
}
|
|
388
409
|
if (ui.nodeNames !== undefined) {
|
|
389
410
|
this.nodeNames = { ...ui.nodeNames };
|
|
411
|
+
for (const [nodeId, name] of Object.entries(ui.nodeNames)) {
|
|
412
|
+
this.emit("graphUiChanged", {
|
|
413
|
+
change: { type: "nodeName", nodeId, name },
|
|
414
|
+
init: true,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (ui.sizes !== undefined) {
|
|
419
|
+
const defNodeIds = new Set(this._def.nodes.map((n) => n.nodeId));
|
|
420
|
+
this.sizes = Object.fromEntries(Object.entries(ui.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
390
421
|
}
|
|
391
422
|
}
|
|
392
423
|
getRuntimeState() {
|
|
@@ -775,8 +806,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
775
806
|
this.nodeNames[nodeId] = name.trim();
|
|
776
807
|
}
|
|
777
808
|
this.emit("graphUiChanged", {
|
|
778
|
-
|
|
779
|
-
change: { type: "nodeName", nodeId },
|
|
809
|
+
change: { type: "nodeName", nodeId, name },
|
|
780
810
|
...options,
|
|
781
811
|
});
|
|
782
812
|
}
|
|
@@ -2196,7 +2226,6 @@ function useWorkbenchBridge(wb) {
|
|
|
2196
2226
|
}, { commit: true });
|
|
2197
2227
|
}, [wb]);
|
|
2198
2228
|
const onNodesChange = React.useCallback((changes) => {
|
|
2199
|
-
// Apply position updates continuously, but mark commit only on drag end
|
|
2200
2229
|
const positions = {};
|
|
2201
2230
|
let commit = false;
|
|
2202
2231
|
changes.forEach((c) => {
|
|
@@ -2206,6 +2235,14 @@ function useWorkbenchBridge(wb) {
|
|
|
2206
2235
|
commit = true;
|
|
2207
2236
|
}
|
|
2208
2237
|
});
|
|
2238
|
+
const sizes = {};
|
|
2239
|
+
changes.forEach((c) => {
|
|
2240
|
+
if (c.type === "dimensions" && c.dimensions) {
|
|
2241
|
+
sizes[c.id] = c.dimensions;
|
|
2242
|
+
if (!c.resizing)
|
|
2243
|
+
commit = true;
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2209
2246
|
// Derive next node selection from change set
|
|
2210
2247
|
const current = wb.getSelection();
|
|
2211
2248
|
const nextNodeIds = new Set(current.nodes);
|
|
@@ -2240,7 +2277,12 @@ function useWorkbenchBridge(wb) {
|
|
|
2240
2277
|
});
|
|
2241
2278
|
}
|
|
2242
2279
|
if (Object.keys(positions).length > 0) {
|
|
2243
|
-
wb.setPositions(positions, {
|
|
2280
|
+
wb.setPositions(positions, {
|
|
2281
|
+
commit: commit && !Object.keys(sizes).length,
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
if (Object.keys(sizes).length > 0) {
|
|
2285
|
+
wb.setSizes(sizes, { commit });
|
|
2244
2286
|
}
|
|
2245
2287
|
}, [wb]);
|
|
2246
2288
|
const onEdgesDelete = React.useCallback((edges) => edges.forEach((e, idx) => wb.disconnect(e.id, { commit: idx === edges.length - 1 })), [wb]);
|
|
@@ -2430,7 +2472,7 @@ function useQueryParamString(key, defaultValue) {
|
|
|
2430
2472
|
return [val, set];
|
|
2431
2473
|
}
|
|
2432
2474
|
|
|
2433
|
-
function toReactFlow(def, positions, registry, opts) {
|
|
2475
|
+
function toReactFlow(def, positions, sizes, registry, opts) {
|
|
2434
2476
|
const EDGE_STYLE_MISSING = { stroke: "#f59e0b", strokeWidth: 2 }; // amber-500
|
|
2435
2477
|
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
2436
2478
|
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
@@ -2474,45 +2516,41 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2474
2516
|
const createHandleBoundsFn = opts.ui?.getCreateHandleBounds() ?? createHandleBounds;
|
|
2475
2517
|
const createHandleLayoutFn = opts.ui?.getCreateHandleLayout() ?? createHandleLayout;
|
|
2476
2518
|
const estimateNodeSizeFn = opts.ui?.getEstimateNodeSize() ?? estimateNodeSize;
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
2480
|
-
// If layoutNode is overridden, use it directly; otherwise use default with internal overrides
|
|
2481
|
-
const geom = layoutNodeOverride
|
|
2519
|
+
const computeLayout = (node, overrides) => {
|
|
2520
|
+
return layoutNodeOverride
|
|
2482
2521
|
? layoutNodeOverride({
|
|
2483
|
-
node
|
|
2522
|
+
node,
|
|
2484
2523
|
registry,
|
|
2485
2524
|
showValues: opts.showValues,
|
|
2486
|
-
overrides
|
|
2525
|
+
overrides,
|
|
2487
2526
|
})
|
|
2488
2527
|
: layoutNode({
|
|
2489
|
-
node
|
|
2528
|
+
node,
|
|
2490
2529
|
registry,
|
|
2491
2530
|
showValues: opts.showValues,
|
|
2492
|
-
overrides
|
|
2531
|
+
overrides,
|
|
2493
2532
|
}, {
|
|
2494
2533
|
estimateNodeSize: estimateNodeSizeFn,
|
|
2495
2534
|
createHandleBounds: createHandleBoundsFn,
|
|
2496
2535
|
createHandleLayout: createHandleLayoutFn,
|
|
2497
2536
|
});
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
typeId: sparkGraph.getInputTypeId(inputSource, id),
|
|
2501
|
-
}));
|
|
2502
|
-
const outputHandles = geom.outputOrder.map((id) => ({
|
|
2503
|
-
id,
|
|
2504
|
-
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
2505
|
-
}));
|
|
2506
|
-
nodeHandleMap[n.nodeId] = {
|
|
2507
|
-
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
2508
|
-
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
2509
|
-
};
|
|
2510
|
-
// Append placeholder entries for any missing handles (below valid ones)
|
|
2537
|
+
};
|
|
2538
|
+
const calculateDimensionsWithMissingHandles = (geom, extraInputs, extraOutputs) => {
|
|
2511
2539
|
const baseLeftCount = geom.inputOrder.length;
|
|
2512
2540
|
const baseRightCount = geom.outputOrder.length;
|
|
2513
|
-
const
|
|
2514
|
-
const
|
|
2515
|
-
|
|
2541
|
+
const baseRows = Math.max(baseLeftCount, baseRightCount);
|
|
2542
|
+
const newRows = Math.max(baseLeftCount + extraInputs.length, baseRightCount + extraOutputs.length);
|
|
2543
|
+
return {
|
|
2544
|
+
baseLeftCount,
|
|
2545
|
+
baseRightCount,
|
|
2546
|
+
baseRows,
|
|
2547
|
+
newRows,
|
|
2548
|
+
width: geom.width,
|
|
2549
|
+
height: geom.height + Math.max(0, newRows - baseRows) * NODE_ROW_HEIGHT_PX,
|
|
2550
|
+
};
|
|
2551
|
+
};
|
|
2552
|
+
const createMissingHandleLayouts = (extraInputs, extraOutputs, baseLeftCount, baseRightCount) => {
|
|
2553
|
+
const left = extraInputs.map((id, i) => ({
|
|
2516
2554
|
...createHandleLayoutFn({
|
|
2517
2555
|
id,
|
|
2518
2556
|
type: "target",
|
|
@@ -2521,7 +2559,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2521
2559
|
}),
|
|
2522
2560
|
missing: true,
|
|
2523
2561
|
}));
|
|
2524
|
-
const
|
|
2562
|
+
const right = extraOutputs.map((id, i) => ({
|
|
2525
2563
|
...createHandleLayoutFn({
|
|
2526
2564
|
id,
|
|
2527
2565
|
type: "source",
|
|
@@ -2530,36 +2568,58 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2530
2568
|
}),
|
|
2531
2569
|
missing: true,
|
|
2532
2570
|
}));
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
];
|
|
2538
|
-
// Precompute handle bounds (including missing) so edges can render immediately
|
|
2539
|
-
const missingBoundsLeft = extraInputs.map((id, i) => createHandleBoundsFn({
|
|
2571
|
+
return [...left, ...right];
|
|
2572
|
+
};
|
|
2573
|
+
const createMissingHandleBounds = (extraInputs, extraOutputs, baseLeftCount, baseRightCount, nodeWidth) => {
|
|
2574
|
+
const left = extraInputs.map((id, i) => createHandleBoundsFn({
|
|
2540
2575
|
id,
|
|
2541
2576
|
type: "target",
|
|
2542
2577
|
position: react.Position.Left,
|
|
2543
2578
|
rowIndex: baseLeftCount + i,
|
|
2544
|
-
nodeWidth
|
|
2579
|
+
nodeWidth,
|
|
2545
2580
|
}));
|
|
2546
|
-
const
|
|
2581
|
+
const right = extraOutputs.map((id, i) => createHandleBoundsFn({
|
|
2547
2582
|
id,
|
|
2548
2583
|
type: "source",
|
|
2549
2584
|
position: react.Position.Right,
|
|
2550
2585
|
rowIndex: baseRightCount + i,
|
|
2551
|
-
nodeWidth
|
|
2586
|
+
nodeWidth,
|
|
2552
2587
|
}));
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
const
|
|
2588
|
+
return [...left, ...right];
|
|
2589
|
+
};
|
|
2590
|
+
const nodes = def.nodes.map((n) => {
|
|
2591
|
+
const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
|
|
2592
|
+
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
2593
|
+
const customSize = sizes?.[n.nodeId];
|
|
2594
|
+
const sizeOverrides = customSize
|
|
2595
|
+
? { ...overrideSize, width: customSize.width, height: customSize.height }
|
|
2596
|
+
: overrideSize;
|
|
2597
|
+
const extraInputs = Array.from(missingInputsByNode[n.nodeId] || []);
|
|
2598
|
+
const extraOutputs = Array.from(missingOutputsByNode[n.nodeId] || []);
|
|
2599
|
+
const geom = computeLayout(n, sizeOverrides);
|
|
2600
|
+
const finalDims = calculateDimensionsWithMissingHandles(geom, extraInputs, extraOutputs);
|
|
2601
|
+
const renderWidth = customSize?.width ?? finalDims.width;
|
|
2602
|
+
const renderHeight = customSize?.height ?? finalDims.height;
|
|
2603
|
+
const initialGeom = customSize ? computeLayout(n, overrideSize) : geom;
|
|
2604
|
+
const initialDims = customSize
|
|
2605
|
+
? calculateDimensionsWithMissingHandles(initialGeom, extraInputs, extraOutputs)
|
|
2606
|
+
: finalDims;
|
|
2607
|
+
const inputHandles = geom.inputOrder.map((id) => ({
|
|
2608
|
+
id,
|
|
2609
|
+
typeId: sparkGraph.getInputTypeId(inputSource, id),
|
|
2610
|
+
}));
|
|
2611
|
+
const outputHandles = geom.outputOrder.map((id) => ({
|
|
2612
|
+
id,
|
|
2613
|
+
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
2614
|
+
}));
|
|
2615
|
+
nodeHandleMap[n.nodeId] = {
|
|
2616
|
+
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
2617
|
+
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
2618
|
+
};
|
|
2619
|
+
const missingHandleLayouts = createMissingHandleLayouts(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount);
|
|
2620
|
+
const handleLayout = [...geom.handleLayout, ...missingHandleLayouts];
|
|
2621
|
+
const missingHandleBounds = createMissingHandleBounds(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount, renderWidth);
|
|
2622
|
+
const handles = [...geom.handles, ...missingHandleBounds];
|
|
2563
2623
|
return {
|
|
2564
2624
|
id: n.nodeId,
|
|
2565
2625
|
data: {
|
|
@@ -2573,8 +2633,10 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2573
2633
|
])),
|
|
2574
2634
|
handleLayout,
|
|
2575
2635
|
showValues: opts.showValues,
|
|
2576
|
-
renderWidth
|
|
2577
|
-
renderHeight
|
|
2636
|
+
renderWidth,
|
|
2637
|
+
renderHeight,
|
|
2638
|
+
initialWidth: initialDims.width,
|
|
2639
|
+
initialHeight: initialDims.height,
|
|
2578
2640
|
inputValues: opts.inputs?.[n.nodeId],
|
|
2579
2641
|
inputDefaults: opts.inputDefaults?.[n.nodeId],
|
|
2580
2642
|
outputValues: opts.outputs?.[n.nodeId],
|
|
@@ -2592,11 +2654,13 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2592
2654
|
selected: opts.selectedNodeIds
|
|
2593
2655
|
? opts.selectedNodeIds.has(n.nodeId)
|
|
2594
2656
|
: undefined,
|
|
2595
|
-
|
|
2596
|
-
|
|
2657
|
+
measured: {
|
|
2658
|
+
width: renderWidth,
|
|
2659
|
+
height: renderHeight,
|
|
2660
|
+
},
|
|
2597
2661
|
handles,
|
|
2598
|
-
width:
|
|
2599
|
-
height:
|
|
2662
|
+
width: renderWidth,
|
|
2663
|
+
height: renderHeight,
|
|
2600
2664
|
};
|
|
2601
2665
|
});
|
|
2602
2666
|
const edges = def.edges.map((e) => {
|
|
@@ -2803,22 +2867,30 @@ async function upload(parsed, wb, runner) {
|
|
|
2803
2867
|
/**
|
|
2804
2868
|
* Merge UI state from source into target, remapping node IDs using nodeIdMap.
|
|
2805
2869
|
* Preserves target state and adds/updates source state with remapped IDs.
|
|
2870
|
+
* If anchorPos and sourceDef are provided, positions will be offset relative to anchorPos.
|
|
2806
2871
|
*/
|
|
2807
|
-
function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
2872
|
+
function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
|
|
2808
2873
|
const result = {
|
|
2809
2874
|
...targetUI,
|
|
2810
2875
|
};
|
|
2811
2876
|
if (!sourceUI)
|
|
2812
2877
|
return result;
|
|
2813
|
-
// Merge positions
|
|
2878
|
+
// Merge positions with optional offset
|
|
2814
2879
|
if (sourceUI.positions) {
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2880
|
+
if (anchorPos && sourceDef) {
|
|
2881
|
+
// Apply offset when anchorPos and sourceDef are provided
|
|
2882
|
+
result.positions = sparkGraph.offsetImportedPositions(targetUI?.positions ?? {}, sourceUI.positions, sourceDef, nodeIdMap, anchorPos);
|
|
2883
|
+
}
|
|
2884
|
+
else {
|
|
2885
|
+
// Simple remapping without offset
|
|
2886
|
+
result.positions = {
|
|
2887
|
+
...(targetUI?.positions || {}),
|
|
2888
|
+
...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [
|
|
2889
|
+
nodeIdMap[oldId] || oldId,
|
|
2890
|
+
pos,
|
|
2891
|
+
])),
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2822
2894
|
}
|
|
2823
2895
|
// Merge selection: remap node IDs and edge IDs
|
|
2824
2896
|
if (sourceUI.selection) {
|
|
@@ -2841,6 +2913,15 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
|
2841
2913
|
])),
|
|
2842
2914
|
};
|
|
2843
2915
|
}
|
|
2916
|
+
if (sourceUI.sizes) {
|
|
2917
|
+
result.sizes = {
|
|
2918
|
+
...(targetUI?.sizes || {}),
|
|
2919
|
+
...Object.fromEntries(Object.entries(sourceUI.sizes).map(([oldId, size]) => [
|
|
2920
|
+
nodeIdMap[oldId] || oldId,
|
|
2921
|
+
size,
|
|
2922
|
+
])),
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2844
2925
|
return result;
|
|
2845
2926
|
}
|
|
2846
2927
|
|
|
@@ -3098,7 +3179,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3098
3179
|
(type === "graphChanged" || type === "graphUiChanged")) {
|
|
3099
3180
|
const changeType = payload
|
|
3100
3181
|
.change?.type;
|
|
3101
|
-
if (changeType === "moveNode" ||
|
|
3182
|
+
if (changeType === "moveNode" ||
|
|
3183
|
+
changeType === "moveNodes" ||
|
|
3184
|
+
changeType === "resizeNodes")
|
|
3102
3185
|
return prev;
|
|
3103
3186
|
}
|
|
3104
3187
|
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
@@ -3507,6 +3590,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3507
3590
|
else if (changeType === "moveNodes") {
|
|
3508
3591
|
reason = "move-nodes";
|
|
3509
3592
|
}
|
|
3593
|
+
else if (changeType === "resizeNodes") {
|
|
3594
|
+
reason = "resize-nodes";
|
|
3595
|
+
}
|
|
3510
3596
|
else if (changeType === "selection") {
|
|
3511
3597
|
reason = "selection";
|
|
3512
3598
|
}
|
|
@@ -4264,103 +4350,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
4264
4350
|
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) })] }), debug && (jsxRuntime.jsx("div", { className: "mt-3 flex-none min-h-0 h-[50%]", children: jsxRuntime.jsx(DebugEvents, { autoScroll: !!autoScroll, hideWorkbench: !!hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange }) }))] }));
|
|
4265
4351
|
}
|
|
4266
4352
|
|
|
4267
|
-
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
4268
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: id, type: type, position: position, isConnectable: isConnectable, className: className, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + (kind === "input" ? " left-2" : " right-2"), style: {
|
|
4269
|
-
top: (y ?? 0) - 8,
|
|
4270
|
-
...(kind === "input"
|
|
4271
|
-
? { right: "50%" }
|
|
4272
|
-
: { left: "50%", textAlign: "right" }),
|
|
4273
|
-
whiteSpace: "nowrap",
|
|
4274
|
-
overflow: "hidden",
|
|
4275
|
-
textOverflow: "ellipsis",
|
|
4276
|
-
}, children: renderLabel({ kind, id }) }))] }));
|
|
4277
|
-
}
|
|
4278
|
-
function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
4279
|
-
const layout = data.handleLayout ?? [];
|
|
4280
|
-
const byId = React.useMemo(() => {
|
|
4281
|
-
const m = new Map();
|
|
4282
|
-
for (const h of layout) {
|
|
4283
|
-
// Prefer namespaced key to disambiguate inputs/outputs that share id
|
|
4284
|
-
m.set(`${h.type}:${h.id}`, {
|
|
4285
|
-
position: h.position,
|
|
4286
|
-
y: h.y,
|
|
4287
|
-
type: h.type,
|
|
4288
|
-
missing: h.missing,
|
|
4289
|
-
});
|
|
4290
|
-
// Back-compat: also store by id-only if not already set
|
|
4291
|
-
if (!m.has(h.id))
|
|
4292
|
-
m.set(h.id, {
|
|
4293
|
-
position: h.position,
|
|
4294
|
-
y: h.y,
|
|
4295
|
-
type: h.type,
|
|
4296
|
-
missing: h.missing,
|
|
4297
|
-
});
|
|
4298
|
-
}
|
|
4299
|
-
return m;
|
|
4300
|
-
}, [layout]);
|
|
4301
|
-
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
4302
|
-
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
4303
|
-
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
4304
|
-
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
4305
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
4306
|
-
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
4307
|
-
const position = placed?.position ?? react.Position.Left;
|
|
4308
|
-
const y = placed?.y;
|
|
4309
|
-
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
|
|
4310
|
-
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4311
|
-
}), missingInputs.map((h) => {
|
|
4312
|
-
const key = `missing-input:${h.id}`;
|
|
4313
|
-
const position = h.position ?? react.Position.Left;
|
|
4314
|
-
const y = h.y;
|
|
4315
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
|
|
4316
|
-
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: false, className: `${cls} wb-nodrag wb-nowheel`, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4317
|
-
}), (data.outputHandles ?? []).map((h) => {
|
|
4318
|
-
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
4319
|
-
const position = placed?.position ?? react.Position.Right;
|
|
4320
|
-
const y = placed?.y;
|
|
4321
|
-
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
|
|
4322
|
-
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4323
|
-
}), missingOutputs.map((h) => {
|
|
4324
|
-
const key = `missing-output:${h.id}`;
|
|
4325
|
-
const position = h.position ?? react.Position.Right;
|
|
4326
|
-
const y = h.y;
|
|
4327
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
4328
|
-
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4329
|
-
})] }));
|
|
4330
|
-
}
|
|
4331
|
-
|
|
4332
|
-
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
4333
|
-
const updateNodeInternals = react.useUpdateNodeInternals();
|
|
4334
|
-
const { typeId, showValues } = data;
|
|
4335
|
-
const inputEntries = data.inputHandles ?? [];
|
|
4336
|
-
const outputEntries = data.outputHandles ?? [];
|
|
4337
|
-
React.useEffect(() => {
|
|
4338
|
-
updateNodeInternals(id);
|
|
4339
|
-
}, [
|
|
4340
|
-
id,
|
|
4341
|
-
inputEntries.length,
|
|
4342
|
-
outputEntries.length,
|
|
4343
|
-
showValues,
|
|
4344
|
-
updateNodeInternals,
|
|
4345
|
-
]);
|
|
4346
|
-
const status = data.status ?? { activeRuns: 0 };
|
|
4347
|
-
const validation = data.validation ?? {
|
|
4348
|
-
inputs: [],
|
|
4349
|
-
outputs: [],
|
|
4350
|
-
issues: [],
|
|
4351
|
-
};
|
|
4352
|
-
const containerBorder = getNodeBorderClassNames({
|
|
4353
|
-
selected,
|
|
4354
|
-
status,
|
|
4355
|
-
validation,
|
|
4356
|
-
});
|
|
4357
|
-
return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/50 !dark:bg-stone-900", containerBorder), style: {
|
|
4358
|
-
position: "relative",
|
|
4359
|
-
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
4360
|
-
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
4361
|
-
}, children: [jsxRuntime.jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsxRuntime.jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4362
|
-
});
|
|
4363
|
-
DefaultNode.displayName = "DefaultNode";
|
|
4364
4353
|
function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInvalidate, }) {
|
|
4365
4354
|
const ctx = useWorkbenchContext();
|
|
4366
4355
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
@@ -4444,6 +4433,72 @@ function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInv
|
|
|
4444
4433
|
.map((v) => `${v.code}: ${v.message}`)
|
|
4445
4434
|
.join("; ") })), showId && jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
4446
4435
|
}
|
|
4436
|
+
|
|
4437
|
+
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
4438
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: id, type: type, position: position, isConnectable: isConnectable, className: className, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + (kind === "input" ? " left-2" : " right-2"), style: {
|
|
4439
|
+
top: (y ?? 0) - 8,
|
|
4440
|
+
...(kind === "input"
|
|
4441
|
+
? { right: "50%" }
|
|
4442
|
+
: { left: "50%", textAlign: "right" }),
|
|
4443
|
+
whiteSpace: "nowrap",
|
|
4444
|
+
overflow: "hidden",
|
|
4445
|
+
textOverflow: "ellipsis",
|
|
4446
|
+
}, children: renderLabel({ kind, id }) }))] }));
|
|
4447
|
+
}
|
|
4448
|
+
function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
4449
|
+
const layout = data.handleLayout ?? [];
|
|
4450
|
+
const byId = React.useMemo(() => {
|
|
4451
|
+
const m = new Map();
|
|
4452
|
+
for (const h of layout) {
|
|
4453
|
+
// Prefer namespaced key to disambiguate inputs/outputs that share id
|
|
4454
|
+
m.set(`${h.type}:${h.id}`, {
|
|
4455
|
+
position: h.position,
|
|
4456
|
+
y: h.y,
|
|
4457
|
+
type: h.type,
|
|
4458
|
+
missing: h.missing,
|
|
4459
|
+
});
|
|
4460
|
+
// Back-compat: also store by id-only if not already set
|
|
4461
|
+
if (!m.has(h.id))
|
|
4462
|
+
m.set(h.id, {
|
|
4463
|
+
position: h.position,
|
|
4464
|
+
y: h.y,
|
|
4465
|
+
type: h.type,
|
|
4466
|
+
missing: h.missing,
|
|
4467
|
+
});
|
|
4468
|
+
}
|
|
4469
|
+
return m;
|
|
4470
|
+
}, [layout]);
|
|
4471
|
+
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
4472
|
+
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
4473
|
+
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
4474
|
+
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
4475
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
4476
|
+
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
4477
|
+
const position = placed?.position ?? react.Position.Left;
|
|
4478
|
+
const y = placed?.y;
|
|
4479
|
+
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
|
|
4480
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4481
|
+
}), missingInputs.map((h) => {
|
|
4482
|
+
const key = `missing-input:${h.id}`;
|
|
4483
|
+
const position = h.position ?? react.Position.Left;
|
|
4484
|
+
const y = h.y;
|
|
4485
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
|
|
4486
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: false, className: `${cls} wb-nodrag wb-nowheel`, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4487
|
+
}), (data.outputHandles ?? []).map((h) => {
|
|
4488
|
+
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
4489
|
+
const position = placed?.position ?? react.Position.Right;
|
|
4490
|
+
const y = placed?.y;
|
|
4491
|
+
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
|
|
4492
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4493
|
+
}), missingOutputs.map((h) => {
|
|
4494
|
+
const key = `missing-output:${h.id}`;
|
|
4495
|
+
const position = h.position ?? react.Position.Right;
|
|
4496
|
+
const y = h.y;
|
|
4497
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
4498
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4499
|
+
})] }));
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4447
4502
|
function DefaultNodeContent({ data, isConnectable, }) {
|
|
4448
4503
|
const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
|
|
4449
4504
|
const inputEntries = data.inputHandles ?? [];
|
|
@@ -4496,6 +4551,24 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
4496
4551
|
} })] }));
|
|
4497
4552
|
}
|
|
4498
4553
|
|
|
4554
|
+
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
4555
|
+
const nodeRef = React.useRef(null);
|
|
4556
|
+
const { typeId } = data;
|
|
4557
|
+
const status = data.status ?? { activeRuns: 0 };
|
|
4558
|
+
const validation = data.validation ?? {
|
|
4559
|
+
inputs: [],
|
|
4560
|
+
outputs: [],
|
|
4561
|
+
issues: [],
|
|
4562
|
+
};
|
|
4563
|
+
const containerBorder = getNodeBorderClassNames({
|
|
4564
|
+
selected,
|
|
4565
|
+
status,
|
|
4566
|
+
validation,
|
|
4567
|
+
});
|
|
4568
|
+
return (jsxRuntime.jsxs("div", { ref: nodeRef, className: cx("rounded-lg bg-white/50 !dark:bg-stone-900 w-full h-full relative", containerBorder), children: [jsxRuntime.jsx(react.NodeResizer, { isVisible: selected, minWidth: data.initialWidth, minHeight: data.initialHeight }), jsxRuntime.jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsxRuntime.jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4569
|
+
});
|
|
4570
|
+
DefaultNode.displayName = "DefaultNode";
|
|
4571
|
+
|
|
4499
4572
|
// Helper to format shortcut for current platform
|
|
4500
4573
|
function formatShortcut(shortcut) {
|
|
4501
4574
|
const isMac = typeof navigator !== "undefined" &&
|
|
@@ -4776,8 +4849,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4776
4849
|
position: n.position,
|
|
4777
4850
|
type: n.type,
|
|
4778
4851
|
selected: n.selected,
|
|
4779
|
-
|
|
4780
|
-
initialHeight: n.initialHeight,
|
|
4852
|
+
measured: n.measured,
|
|
4781
4853
|
data: n.data && {
|
|
4782
4854
|
typeId: n.data.typeId,
|
|
4783
4855
|
inputHandles: n.data.inputHandles,
|
|
@@ -4811,11 +4883,18 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4811
4883
|
React.useImperativeHandle(ref, () => ({
|
|
4812
4884
|
fitView: () => {
|
|
4813
4885
|
try {
|
|
4814
|
-
rfInstanceRef.current?.fitView(
|
|
4886
|
+
rfInstanceRef.current?.fitView();
|
|
4887
|
+
}
|
|
4888
|
+
catch (err) {
|
|
4889
|
+
console.warn("Failed to fit view", err);
|
|
4815
4890
|
}
|
|
4816
|
-
catch { }
|
|
4817
4891
|
},
|
|
4818
|
-
|
|
4892
|
+
setViewport: (viewport) => {
|
|
4893
|
+
if (rfInstanceRef.current) {
|
|
4894
|
+
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
4895
|
+
}
|
|
4896
|
+
},
|
|
4897
|
+
}), []);
|
|
4819
4898
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
4820
4899
|
const ui = wb.getUI();
|
|
4821
4900
|
const { nodeTypes, resolveNodeType } = React.useMemo(() => {
|
|
@@ -4861,7 +4940,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4861
4940
|
inputsWithDefaults[n.nodeId] = merged;
|
|
4862
4941
|
}
|
|
4863
4942
|
}
|
|
4864
|
-
const out = toReactFlow(wb.def, wb.getPositions(), registry, {
|
|
4943
|
+
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), registry, {
|
|
4865
4944
|
showValues,
|
|
4866
4945
|
inputs: inputsWithDefaults,
|
|
4867
4946
|
inputDefaults: inputDefaultsMap,
|
|
@@ -5333,55 +5412,53 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5333
5412
|
showToast,
|
|
5334
5413
|
]);
|
|
5335
5414
|
// Get custom renderers from UI extension registry (reactive to uiVersion changes)
|
|
5336
|
-
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = React.useMemo(() => {
|
|
5415
|
+
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, SelectionContextMenuRenderer, connectionLineRenderer, } = React.useMemo(() => {
|
|
5337
5416
|
return {
|
|
5338
5417
|
BackgroundRenderer: ui.getBackgroundRenderer(),
|
|
5339
5418
|
MinimapRenderer: ui.getMinimapRenderer(),
|
|
5340
5419
|
ControlsRenderer: ui.getControlsRenderer(),
|
|
5341
5420
|
DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
|
|
5342
5421
|
NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
|
|
5422
|
+
SelectionContextMenuRenderer: ui.getSelectionContextMenuRenderer(),
|
|
5343
5423
|
connectionLineRenderer: ui.getConnectionLineRenderer(),
|
|
5344
5424
|
};
|
|
5345
5425
|
}, [ui, uiVersion]);
|
|
5346
5426
|
const onMoveEnd = React.useCallback(() => {
|
|
5347
5427
|
if (rfInstanceRef.current) {
|
|
5348
5428
|
const viewport = rfInstanceRef.current.getViewport();
|
|
5349
|
-
const viewportData = lod.
|
|
5429
|
+
const viewportData = lod.clone(viewport);
|
|
5350
5430
|
wb.setViewport(viewportData);
|
|
5351
5431
|
}
|
|
5352
5432
|
}, [wb]);
|
|
5353
|
-
|
|
5433
|
+
// Sync viewport when workbench fires graphUiChanged with viewport event
|
|
5354
5434
|
React.useEffect(() => {
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
zoom: currentViewport.zoom,
|
|
5368
|
-
});
|
|
5369
|
-
}
|
|
5370
|
-
});
|
|
5435
|
+
const off = wb.on("graphUiChanged", (event) => {
|
|
5436
|
+
if (event.change?.type === "viewport" &&
|
|
5437
|
+
rfInstanceRef.current &&
|
|
5438
|
+
event.init) {
|
|
5439
|
+
const viewport = wb.getViewport();
|
|
5440
|
+
if (viewport) {
|
|
5441
|
+
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
});
|
|
5445
|
+
return () => off();
|
|
5446
|
+
}, [wb]);
|
|
5371
5447
|
return (jsxRuntime.jsxs("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: [jsxRuntime.jsx(react.ReactFlowProvider, { children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
|
|
5372
5448
|
rfInstanceRef.current = inst;
|
|
5373
5449
|
const savedViewport = wb.getViewport();
|
|
5374
5450
|
if (savedViewport) {
|
|
5375
|
-
|
|
5376
|
-
inst.setViewport(lod.pick(savedViewport, ["x", "y", "zoom"]));
|
|
5451
|
+
inst.setViewport(lod.clone(savedViewport));
|
|
5377
5452
|
}
|
|
5378
|
-
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan",
|
|
5453
|
+
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [BackgroundRenderer ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
5379
5454
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5380
5455
|
: {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
|
|
5381
5456
|
nodeContextMenuHandlers &&
|
|
5382
5457
|
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
|
|
5383
5458
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5384
|
-
: {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen &&
|
|
5459
|
+
: {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen &&
|
|
5460
|
+
selectionMenuPos &&
|
|
5461
|
+
(SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }) }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
|
|
5385
5462
|
});
|
|
5386
5463
|
|
|
5387
5464
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|