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