@bian-womp/spark-workbench 0.2.82 → 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 +293 -212
- 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 +295 -214
- 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]);
|
|
@@ -2292,8 +2334,9 @@ function useWorkbenchBridge(wb) {
|
|
|
2292
2334
|
function useWorkbenchGraphTick(wb) {
|
|
2293
2335
|
const [tick, setTick] = React.useState(0);
|
|
2294
2336
|
React.useEffect(() => {
|
|
2295
|
-
const
|
|
2296
|
-
|
|
2337
|
+
const off = wb.on("graphChanged", () => {
|
|
2338
|
+
setTick((t) => t + 1);
|
|
2339
|
+
});
|
|
2297
2340
|
return () => off();
|
|
2298
2341
|
}, [wb]);
|
|
2299
2342
|
return tick;
|
|
@@ -2301,8 +2344,11 @@ function useWorkbenchGraphTick(wb) {
|
|
|
2301
2344
|
function useWorkbenchGraphUiTick(wb) {
|
|
2302
2345
|
const [tick, setTick] = React.useState(0);
|
|
2303
2346
|
React.useEffect(() => {
|
|
2304
|
-
const
|
|
2305
|
-
|
|
2347
|
+
const off = wb.on("graphUiChanged", (evt) => {
|
|
2348
|
+
if (evt.change?.type === "viewport")
|
|
2349
|
+
return;
|
|
2350
|
+
setTick((t) => t + 1);
|
|
2351
|
+
});
|
|
2306
2352
|
return () => off();
|
|
2307
2353
|
}, [wb]);
|
|
2308
2354
|
return tick;
|
|
@@ -2426,7 +2472,7 @@ function useQueryParamString(key, defaultValue) {
|
|
|
2426
2472
|
return [val, set];
|
|
2427
2473
|
}
|
|
2428
2474
|
|
|
2429
|
-
function toReactFlow(def, positions, registry, opts) {
|
|
2475
|
+
function toReactFlow(def, positions, sizes, registry, opts) {
|
|
2430
2476
|
const EDGE_STYLE_MISSING = { stroke: "#f59e0b", strokeWidth: 2 }; // amber-500
|
|
2431
2477
|
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
2432
2478
|
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
@@ -2470,45 +2516,41 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2470
2516
|
const createHandleBoundsFn = opts.ui?.getCreateHandleBounds() ?? createHandleBounds;
|
|
2471
2517
|
const createHandleLayoutFn = opts.ui?.getCreateHandleLayout() ?? createHandleLayout;
|
|
2472
2518
|
const estimateNodeSizeFn = opts.ui?.getEstimateNodeSize() ?? estimateNodeSize;
|
|
2473
|
-
const
|
|
2474
|
-
|
|
2475
|
-
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
2476
|
-
// If layoutNode is overridden, use it directly; otherwise use default with internal overrides
|
|
2477
|
-
const geom = layoutNodeOverride
|
|
2519
|
+
const computeLayout = (node, overrides) => {
|
|
2520
|
+
return layoutNodeOverride
|
|
2478
2521
|
? layoutNodeOverride({
|
|
2479
|
-
node
|
|
2522
|
+
node,
|
|
2480
2523
|
registry,
|
|
2481
2524
|
showValues: opts.showValues,
|
|
2482
|
-
overrides
|
|
2525
|
+
overrides,
|
|
2483
2526
|
})
|
|
2484
2527
|
: layoutNode({
|
|
2485
|
-
node
|
|
2528
|
+
node,
|
|
2486
2529
|
registry,
|
|
2487
2530
|
showValues: opts.showValues,
|
|
2488
|
-
overrides
|
|
2531
|
+
overrides,
|
|
2489
2532
|
}, {
|
|
2490
2533
|
estimateNodeSize: estimateNodeSizeFn,
|
|
2491
2534
|
createHandleBounds: createHandleBoundsFn,
|
|
2492
2535
|
createHandleLayout: createHandleLayoutFn,
|
|
2493
2536
|
});
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
typeId: sparkGraph.getInputTypeId(inputSource, id),
|
|
2497
|
-
}));
|
|
2498
|
-
const outputHandles = geom.outputOrder.map((id) => ({
|
|
2499
|
-
id,
|
|
2500
|
-
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
2501
|
-
}));
|
|
2502
|
-
nodeHandleMap[n.nodeId] = {
|
|
2503
|
-
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
2504
|
-
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
2505
|
-
};
|
|
2506
|
-
// Append placeholder entries for any missing handles (below valid ones)
|
|
2537
|
+
};
|
|
2538
|
+
const calculateDimensionsWithMissingHandles = (geom, extraInputs, extraOutputs) => {
|
|
2507
2539
|
const baseLeftCount = geom.inputOrder.length;
|
|
2508
2540
|
const baseRightCount = geom.outputOrder.length;
|
|
2509
|
-
const
|
|
2510
|
-
const
|
|
2511
|
-
|
|
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) => ({
|
|
2512
2554
|
...createHandleLayoutFn({
|
|
2513
2555
|
id,
|
|
2514
2556
|
type: "target",
|
|
@@ -2517,7 +2559,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2517
2559
|
}),
|
|
2518
2560
|
missing: true,
|
|
2519
2561
|
}));
|
|
2520
|
-
const
|
|
2562
|
+
const right = extraOutputs.map((id, i) => ({
|
|
2521
2563
|
...createHandleLayoutFn({
|
|
2522
2564
|
id,
|
|
2523
2565
|
type: "source",
|
|
@@ -2526,36 +2568,58 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2526
2568
|
}),
|
|
2527
2569
|
missing: true,
|
|
2528
2570
|
}));
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
];
|
|
2534
|
-
// Precompute handle bounds (including missing) so edges can render immediately
|
|
2535
|
-
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({
|
|
2536
2575
|
id,
|
|
2537
2576
|
type: "target",
|
|
2538
2577
|
position: react.Position.Left,
|
|
2539
2578
|
rowIndex: baseLeftCount + i,
|
|
2540
|
-
nodeWidth
|
|
2579
|
+
nodeWidth,
|
|
2541
2580
|
}));
|
|
2542
|
-
const
|
|
2581
|
+
const right = extraOutputs.map((id, i) => createHandleBoundsFn({
|
|
2543
2582
|
id,
|
|
2544
2583
|
type: "source",
|
|
2545
2584
|
position: react.Position.Right,
|
|
2546
2585
|
rowIndex: baseRightCount + i,
|
|
2547
|
-
nodeWidth
|
|
2586
|
+
nodeWidth,
|
|
2548
2587
|
}));
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
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];
|
|
2559
2623
|
return {
|
|
2560
2624
|
id: n.nodeId,
|
|
2561
2625
|
data: {
|
|
@@ -2569,8 +2633,10 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2569
2633
|
])),
|
|
2570
2634
|
handleLayout,
|
|
2571
2635
|
showValues: opts.showValues,
|
|
2572
|
-
renderWidth
|
|
2573
|
-
renderHeight
|
|
2636
|
+
renderWidth,
|
|
2637
|
+
renderHeight,
|
|
2638
|
+
initialWidth: initialDims.width,
|
|
2639
|
+
initialHeight: initialDims.height,
|
|
2574
2640
|
inputValues: opts.inputs?.[n.nodeId],
|
|
2575
2641
|
inputDefaults: opts.inputDefaults?.[n.nodeId],
|
|
2576
2642
|
outputValues: opts.outputs?.[n.nodeId],
|
|
@@ -2588,11 +2654,13 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2588
2654
|
selected: opts.selectedNodeIds
|
|
2589
2655
|
? opts.selectedNodeIds.has(n.nodeId)
|
|
2590
2656
|
: undefined,
|
|
2591
|
-
|
|
2592
|
-
|
|
2657
|
+
measured: {
|
|
2658
|
+
width: renderWidth,
|
|
2659
|
+
height: renderHeight,
|
|
2660
|
+
},
|
|
2593
2661
|
handles,
|
|
2594
|
-
width:
|
|
2595
|
-
height:
|
|
2662
|
+
width: renderWidth,
|
|
2663
|
+
height: renderHeight,
|
|
2596
2664
|
};
|
|
2597
2665
|
});
|
|
2598
2666
|
const edges = def.edges.map((e) => {
|
|
@@ -2799,22 +2867,30 @@ async function upload(parsed, wb, runner) {
|
|
|
2799
2867
|
/**
|
|
2800
2868
|
* Merge UI state from source into target, remapping node IDs using nodeIdMap.
|
|
2801
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.
|
|
2802
2871
|
*/
|
|
2803
|
-
function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
2872
|
+
function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
|
|
2804
2873
|
const result = {
|
|
2805
2874
|
...targetUI,
|
|
2806
2875
|
};
|
|
2807
2876
|
if (!sourceUI)
|
|
2808
2877
|
return result;
|
|
2809
|
-
// Merge positions
|
|
2878
|
+
// Merge positions with optional offset
|
|
2810
2879
|
if (sourceUI.positions) {
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
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
|
+
}
|
|
2818
2894
|
}
|
|
2819
2895
|
// Merge selection: remap node IDs and edge IDs
|
|
2820
2896
|
if (sourceUI.selection) {
|
|
@@ -2837,6 +2913,15 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
|
2837
2913
|
])),
|
|
2838
2914
|
};
|
|
2839
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
|
+
}
|
|
2840
2925
|
return result;
|
|
2841
2926
|
}
|
|
2842
2927
|
|
|
@@ -3094,7 +3179,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3094
3179
|
(type === "graphChanged" || type === "graphUiChanged")) {
|
|
3095
3180
|
const changeType = payload
|
|
3096
3181
|
.change?.type;
|
|
3097
|
-
if (changeType === "moveNode" ||
|
|
3182
|
+
if (changeType === "moveNode" ||
|
|
3183
|
+
changeType === "moveNodes" ||
|
|
3184
|
+
changeType === "resizeNodes")
|
|
3098
3185
|
return prev;
|
|
3099
3186
|
}
|
|
3100
3187
|
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
@@ -3503,6 +3590,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3503
3590
|
else if (changeType === "moveNodes") {
|
|
3504
3591
|
reason = "move-nodes";
|
|
3505
3592
|
}
|
|
3593
|
+
else if (changeType === "resizeNodes") {
|
|
3594
|
+
reason = "resize-nodes";
|
|
3595
|
+
}
|
|
3506
3596
|
else if (changeType === "selection") {
|
|
3507
3597
|
reason = "selection";
|
|
3508
3598
|
}
|
|
@@ -4260,103 +4350,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
4260
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 }) }))] }));
|
|
4261
4351
|
}
|
|
4262
4352
|
|
|
4263
|
-
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
4264
|
-
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: {
|
|
4265
|
-
top: (y ?? 0) - 8,
|
|
4266
|
-
...(kind === "input"
|
|
4267
|
-
? { right: "50%" }
|
|
4268
|
-
: { left: "50%", textAlign: "right" }),
|
|
4269
|
-
whiteSpace: "nowrap",
|
|
4270
|
-
overflow: "hidden",
|
|
4271
|
-
textOverflow: "ellipsis",
|
|
4272
|
-
}, children: renderLabel({ kind, id }) }))] }));
|
|
4273
|
-
}
|
|
4274
|
-
function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
4275
|
-
const layout = data.handleLayout ?? [];
|
|
4276
|
-
const byId = React.useMemo(() => {
|
|
4277
|
-
const m = new Map();
|
|
4278
|
-
for (const h of layout) {
|
|
4279
|
-
// Prefer namespaced key to disambiguate inputs/outputs that share id
|
|
4280
|
-
m.set(`${h.type}:${h.id}`, {
|
|
4281
|
-
position: h.position,
|
|
4282
|
-
y: h.y,
|
|
4283
|
-
type: h.type,
|
|
4284
|
-
missing: h.missing,
|
|
4285
|
-
});
|
|
4286
|
-
// Back-compat: also store by id-only if not already set
|
|
4287
|
-
if (!m.has(h.id))
|
|
4288
|
-
m.set(h.id, {
|
|
4289
|
-
position: h.position,
|
|
4290
|
-
y: h.y,
|
|
4291
|
-
type: h.type,
|
|
4292
|
-
missing: h.missing,
|
|
4293
|
-
});
|
|
4294
|
-
}
|
|
4295
|
-
return m;
|
|
4296
|
-
}, [layout]);
|
|
4297
|
-
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
4298
|
-
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
4299
|
-
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
4300
|
-
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
4301
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
4302
|
-
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
4303
|
-
const position = placed?.position ?? react.Position.Left;
|
|
4304
|
-
const y = placed?.y;
|
|
4305
|
-
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
|
|
4306
|
-
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));
|
|
4307
|
-
}), missingInputs.map((h) => {
|
|
4308
|
-
const key = `missing-input:${h.id}`;
|
|
4309
|
-
const position = h.position ?? react.Position.Left;
|
|
4310
|
-
const y = h.y;
|
|
4311
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
|
|
4312
|
-
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));
|
|
4313
|
-
}), (data.outputHandles ?? []).map((h) => {
|
|
4314
|
-
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
4315
|
-
const position = placed?.position ?? react.Position.Right;
|
|
4316
|
-
const y = placed?.y;
|
|
4317
|
-
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
|
|
4318
|
-
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));
|
|
4319
|
-
}), missingOutputs.map((h) => {
|
|
4320
|
-
const key = `missing-output:${h.id}`;
|
|
4321
|
-
const position = h.position ?? react.Position.Right;
|
|
4322
|
-
const y = h.y;
|
|
4323
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
4324
|
-
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4325
|
-
})] }));
|
|
4326
|
-
}
|
|
4327
|
-
|
|
4328
|
-
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
4329
|
-
const updateNodeInternals = react.useUpdateNodeInternals();
|
|
4330
|
-
const { typeId, showValues } = data;
|
|
4331
|
-
const inputEntries = data.inputHandles ?? [];
|
|
4332
|
-
const outputEntries = data.outputHandles ?? [];
|
|
4333
|
-
React.useEffect(() => {
|
|
4334
|
-
updateNodeInternals(id);
|
|
4335
|
-
}, [
|
|
4336
|
-
id,
|
|
4337
|
-
inputEntries.length,
|
|
4338
|
-
outputEntries.length,
|
|
4339
|
-
showValues,
|
|
4340
|
-
updateNodeInternals,
|
|
4341
|
-
]);
|
|
4342
|
-
const status = data.status ?? { activeRuns: 0 };
|
|
4343
|
-
const validation = data.validation ?? {
|
|
4344
|
-
inputs: [],
|
|
4345
|
-
outputs: [],
|
|
4346
|
-
issues: [],
|
|
4347
|
-
};
|
|
4348
|
-
const containerBorder = getNodeBorderClassNames({
|
|
4349
|
-
selected,
|
|
4350
|
-
status,
|
|
4351
|
-
validation,
|
|
4352
|
-
});
|
|
4353
|
-
return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/50 !dark:bg-stone-900", containerBorder), style: {
|
|
4354
|
-
position: "relative",
|
|
4355
|
-
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
4356
|
-
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
4357
|
-
}, children: [jsxRuntime.jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsxRuntime.jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4358
|
-
});
|
|
4359
|
-
DefaultNode.displayName = "DefaultNode";
|
|
4360
4353
|
function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInvalidate, }) {
|
|
4361
4354
|
const ctx = useWorkbenchContext();
|
|
4362
4355
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
@@ -4440,6 +4433,72 @@ function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInv
|
|
|
4440
4433
|
.map((v) => `${v.code}: ${v.message}`)
|
|
4441
4434
|
.join("; ") })), showId && jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
4442
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
|
+
|
|
4443
4502
|
function DefaultNodeContent({ data, isConnectable, }) {
|
|
4444
4503
|
const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
|
|
4445
4504
|
const inputEntries = data.inputHandles ?? [];
|
|
@@ -4492,6 +4551,24 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
4492
4551
|
} })] }));
|
|
4493
4552
|
}
|
|
4494
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
|
+
|
|
4495
4572
|
// Helper to format shortcut for current platform
|
|
4496
4573
|
function formatShortcut(shortcut) {
|
|
4497
4574
|
const isMac = typeof navigator !== "undefined" &&
|
|
@@ -4772,8 +4849,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4772
4849
|
position: n.position,
|
|
4773
4850
|
type: n.type,
|
|
4774
4851
|
selected: n.selected,
|
|
4775
|
-
|
|
4776
|
-
initialHeight: n.initialHeight,
|
|
4852
|
+
measured: n.measured,
|
|
4777
4853
|
data: n.data && {
|
|
4778
4854
|
typeId: n.data.typeId,
|
|
4779
4855
|
inputHandles: n.data.inputHandles,
|
|
@@ -4807,11 +4883,18 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4807
4883
|
React.useImperativeHandle(ref, () => ({
|
|
4808
4884
|
fitView: () => {
|
|
4809
4885
|
try {
|
|
4810
|
-
rfInstanceRef.current?.fitView(
|
|
4886
|
+
rfInstanceRef.current?.fitView();
|
|
4887
|
+
}
|
|
4888
|
+
catch (err) {
|
|
4889
|
+
console.warn("Failed to fit view", err);
|
|
4811
4890
|
}
|
|
4812
|
-
catch { }
|
|
4813
4891
|
},
|
|
4814
|
-
|
|
4892
|
+
setViewport: (viewport) => {
|
|
4893
|
+
if (rfInstanceRef.current) {
|
|
4894
|
+
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
4895
|
+
}
|
|
4896
|
+
},
|
|
4897
|
+
}), []);
|
|
4815
4898
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
4816
4899
|
const ui = wb.getUI();
|
|
4817
4900
|
const { nodeTypes, resolveNodeType } = React.useMemo(() => {
|
|
@@ -4857,7 +4940,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4857
4940
|
inputsWithDefaults[n.nodeId] = merged;
|
|
4858
4941
|
}
|
|
4859
4942
|
}
|
|
4860
|
-
const out = toReactFlow(wb.def, wb.getPositions(), registry, {
|
|
4943
|
+
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), registry, {
|
|
4861
4944
|
showValues,
|
|
4862
4945
|
inputs: inputsWithDefaults,
|
|
4863
4946
|
inputDefaults: inputDefaultsMap,
|
|
@@ -5329,55 +5412,53 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5329
5412
|
showToast,
|
|
5330
5413
|
]);
|
|
5331
5414
|
// Get custom renderers from UI extension registry (reactive to uiVersion changes)
|
|
5332
|
-
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = React.useMemo(() => {
|
|
5415
|
+
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, SelectionContextMenuRenderer, connectionLineRenderer, } = React.useMemo(() => {
|
|
5333
5416
|
return {
|
|
5334
5417
|
BackgroundRenderer: ui.getBackgroundRenderer(),
|
|
5335
5418
|
MinimapRenderer: ui.getMinimapRenderer(),
|
|
5336
5419
|
ControlsRenderer: ui.getControlsRenderer(),
|
|
5337
5420
|
DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
|
|
5338
5421
|
NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
|
|
5422
|
+
SelectionContextMenuRenderer: ui.getSelectionContextMenuRenderer(),
|
|
5339
5423
|
connectionLineRenderer: ui.getConnectionLineRenderer(),
|
|
5340
5424
|
};
|
|
5341
5425
|
}, [ui, uiVersion]);
|
|
5342
5426
|
const onMoveEnd = React.useCallback(() => {
|
|
5343
5427
|
if (rfInstanceRef.current) {
|
|
5344
5428
|
const viewport = rfInstanceRef.current.getViewport();
|
|
5345
|
-
const viewportData = lod.
|
|
5429
|
+
const viewportData = lod.clone(viewport);
|
|
5346
5430
|
wb.setViewport(viewportData);
|
|
5347
5431
|
}
|
|
5348
5432
|
}, [wb]);
|
|
5349
|
-
|
|
5433
|
+
// Sync viewport when workbench fires graphUiChanged with viewport event
|
|
5350
5434
|
React.useEffect(() => {
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
zoom: currentViewport.zoom,
|
|
5364
|
-
});
|
|
5365
|
-
}
|
|
5366
|
-
});
|
|
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]);
|
|
5367
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) => {
|
|
5368
5448
|
rfInstanceRef.current = inst;
|
|
5369
5449
|
const savedViewport = wb.getViewport();
|
|
5370
5450
|
if (savedViewport) {
|
|
5371
|
-
|
|
5372
|
-
inst.setViewport(lod.pick(savedViewport, ["x", "y", "zoom"]));
|
|
5451
|
+
inst.setViewport(lod.clone(savedViewport));
|
|
5373
5452
|
}
|
|
5374
|
-
}, 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
|
|
5375
5454
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5376
5455
|
: {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
|
|
5377
5456
|
nodeContextMenuHandlers &&
|
|
5378
5457
|
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
|
|
5379
5458
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5380
|
-
: {}) })) : (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))] }));
|
|
5381
5462
|
});
|
|
5382
5463
|
|
|
5383
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, }) {
|