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