@bian-womp/spark-workbench 0.3.71 → 0.3.72
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 +143 -352
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/adapters/cli/index.d.ts.map +1 -1
- package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/cjs/src/examples/reactflow/App.d.ts.map +1 -1
- package/lib/cjs/src/misc/DebugEvents.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultEdge.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts +1 -1
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/IssueBadge.d.ts.map +1 -1
- package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts +1 -1
- package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeHandles.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuButton.d.ts +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuButton.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
- package/lib/cjs/src/misc/layout.d.ts.map +1 -1
- package/lib/cjs/src/misc/load.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/cjs/src/misc/thumbnail-utils.d.ts.map +1 -1
- package/lib/cjs/src/misc/value.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +143 -352
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/adapters/cli/index.d.ts.map +1 -1
- package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/esm/src/examples/reactflow/App.d.ts.map +1 -1
- package/lib/esm/src/misc/DebugEvents.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultEdge.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNodeContent.d.ts +1 -1
- package/lib/esm/src/misc/DefaultNodeContent.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/IssueBadge.d.ts.map +1 -1
- package/lib/esm/src/misc/KeyboardShortcutToast.d.ts +1 -1
- package/lib/esm/src/misc/KeyboardShortcutToast.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeHandles.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuButton.d.ts +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuButton.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/hooks.d.ts.map +1 -1
- package/lib/esm/src/misc/layout.d.ts.map +1 -1
- package/lib/esm/src/misc/load.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/esm/src/misc/thumbnail-utils.d.ts.map +1 -1
- package/lib/esm/src/misc/value.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +6 -4
package/lib/esm/index.js
CHANGED
|
@@ -194,13 +194,11 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
194
194
|
// Clean up extData for removed nodes/edges
|
|
195
195
|
if (this.customData.nodes) {
|
|
196
196
|
const filteredExtNodes = Object.fromEntries(Object.entries(this.customData.nodes).filter(([id]) => defNodeIds.has(id)));
|
|
197
|
-
this.customData.nodes =
|
|
198
|
-
Object.keys(filteredExtNodes).length > 0 ? filteredExtNodes : undefined;
|
|
197
|
+
this.customData.nodes = Object.keys(filteredExtNodes).length > 0 ? filteredExtNodes : undefined;
|
|
199
198
|
}
|
|
200
199
|
if (this.customData.edges) {
|
|
201
200
|
const filteredExtEdges = Object.fromEntries(Object.entries(this.customData.edges).filter(([id]) => defEdgeIds.has(id)));
|
|
202
|
-
this.customData.edges =
|
|
203
|
-
Object.keys(filteredExtEdges).length > 0 ? filteredExtEdges : undefined;
|
|
201
|
+
this.customData.edges = Object.keys(filteredExtEdges).length > 0 ? filteredExtEdges : undefined;
|
|
204
202
|
}
|
|
205
203
|
this.positions = filteredPositions;
|
|
206
204
|
this.sizes = filteredSizes;
|
|
@@ -253,8 +251,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
253
251
|
this.refreshValidation();
|
|
254
252
|
}
|
|
255
253
|
addNode(node, options) {
|
|
256
|
-
const id = node.nodeId ??
|
|
257
|
-
this.genId("n", new Set(this._def.nodes.map((n) => n.nodeId)));
|
|
254
|
+
const id = node.nodeId ?? this.genId("n", new Set(this._def.nodes.map((n) => n.nodeId)));
|
|
258
255
|
this._def.nodes.push({
|
|
259
256
|
nodeId: id,
|
|
260
257
|
typeId: node.typeId,
|
|
@@ -441,9 +438,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
441
438
|
const filteredNodeNames = Object.fromEntries(Object.entries(this.nodeNames).filter(([id]) => defNodeIds.has(id)));
|
|
442
439
|
const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
443
440
|
return {
|
|
444
|
-
positions: Object.keys(filteredPositions).length > 0
|
|
445
|
-
? filteredPositions
|
|
446
|
-
: undefined,
|
|
441
|
+
positions: Object.keys(filteredPositions).length > 0 ? filteredPositions : undefined,
|
|
447
442
|
selection: filteredNodes.length > 0 || filteredEdges.length > 0
|
|
448
443
|
? {
|
|
449
444
|
nodes: filteredNodes,
|
|
@@ -451,9 +446,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
451
446
|
}
|
|
452
447
|
: undefined,
|
|
453
448
|
viewport: this.viewport ? { ...this.viewport } : undefined,
|
|
454
|
-
nodeNames: Object.keys(filteredNodeNames).length > 0
|
|
455
|
-
? filteredNodeNames
|
|
456
|
-
: undefined,
|
|
449
|
+
nodeNames: Object.keys(filteredNodeNames).length > 0 ? filteredNodeNames : undefined,
|
|
457
450
|
sizes: Object.keys(filteredSizes).length > 0 ? filteredSizes : undefined,
|
|
458
451
|
};
|
|
459
452
|
}
|
|
@@ -598,17 +591,14 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
598
591
|
: undefined,
|
|
599
592
|
size,
|
|
600
593
|
inputs: inputsToCopy,
|
|
601
|
-
customData: customNodeData !== undefined
|
|
602
|
-
? lod.cloneDeep(customNodeData)
|
|
603
|
-
: undefined,
|
|
594
|
+
customData: customNodeData !== undefined ? lod.cloneDeep(customNodeData) : undefined,
|
|
604
595
|
originalNodeId: node.nodeId,
|
|
605
596
|
};
|
|
606
597
|
});
|
|
607
598
|
// Collect edges between copied nodes
|
|
608
599
|
const copiedEdges = this.def.edges
|
|
609
600
|
.filter((edge) => {
|
|
610
|
-
return
|
|
611
|
-
selectedNodeSet.has(edge.target.nodeId));
|
|
601
|
+
return selectedNodeSet.has(edge.source.nodeId) && selectedNodeSet.has(edge.target.nodeId);
|
|
612
602
|
})
|
|
613
603
|
.map((edge) => ({
|
|
614
604
|
sourceNodeId: edge.source.nodeId,
|
|
@@ -1324,13 +1314,9 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1324
1314
|
const out = {};
|
|
1325
1315
|
for (const n of def.nodes) {
|
|
1326
1316
|
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
1327
|
-
const runtimeInputs = this.runtime
|
|
1328
|
-
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
1329
|
-
: {};
|
|
1317
|
+
const runtimeInputs = this.runtime ? (this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}) : {};
|
|
1330
1318
|
// Build inbound handle set for this node from current def
|
|
1331
|
-
const inbound = new Set(def.edges
|
|
1332
|
-
.filter((e) => e.target.nodeId === n.nodeId)
|
|
1333
|
-
.map((e) => e.target.handle));
|
|
1319
|
+
const inbound = new Set(def.edges.filter((e) => e.target.nodeId === n.nodeId).map((e) => e.target.handle));
|
|
1334
1320
|
// Merge staged only for non-inbound handles so UI reflects runtime values for wired inputs
|
|
1335
1321
|
const merged = { ...runtimeInputs };
|
|
1336
1322
|
for (const [h, v] of Object.entries(staged)) {
|
|
@@ -1386,9 +1372,7 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1386
1372
|
this.extData = { ...this.extData, ...data };
|
|
1387
1373
|
}
|
|
1388
1374
|
async updateExtData(updates) {
|
|
1389
|
-
if (!this.extData ||
|
|
1390
|
-
typeof this.extData !== "object" ||
|
|
1391
|
-
Array.isArray(this.extData)) {
|
|
1375
|
+
if (!this.extData || typeof this.extData !== "object" || Array.isArray(this.extData)) {
|
|
1392
1376
|
this.extData = {};
|
|
1393
1377
|
}
|
|
1394
1378
|
let hasCustomUpdate = false;
|
|
@@ -1715,10 +1699,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1715
1699
|
// Wrap custom event handler to intercept viewport events and emit viewport event
|
|
1716
1700
|
const wrappedOnCustomEvent = (event) => {
|
|
1717
1701
|
const msg = event?.message;
|
|
1718
|
-
if (msg &&
|
|
1719
|
-
typeof msg === "object" &&
|
|
1720
|
-
"type" in msg &&
|
|
1721
|
-
msg.type === "viewport") {
|
|
1702
|
+
if (msg && typeof msg === "object" && "type" in msg && msg.type === "viewport") {
|
|
1722
1703
|
const viewport = msg.payload?.viewport;
|
|
1723
1704
|
if (isValidViewport(viewport)) {
|
|
1724
1705
|
this.emit("viewport", { viewport });
|
|
@@ -2161,9 +2142,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
2161
2142
|
const handles = Object.keys(resolved ?? desc?.inputs ?? {});
|
|
2162
2143
|
const cur = {};
|
|
2163
2144
|
// Build inbound handle set for this node to honor wiring precedence
|
|
2164
|
-
const inbound = new Set(def.edges
|
|
2165
|
-
.filter((e) => e.target.nodeId === n.nodeId)
|
|
2166
|
-
.map((e) => e.target.handle));
|
|
2145
|
+
const inbound = new Set(def.edges.filter((e) => e.target.nodeId === n.nodeId).map((e) => e.target.handle));
|
|
2167
2146
|
for (const h of handles) {
|
|
2168
2147
|
const rec = cache.get(this.getCacheKey(n.nodeId, h, "input"));
|
|
2169
2148
|
if (rec)
|
|
@@ -2336,9 +2315,7 @@ function summarizeDeep(value) {
|
|
|
2336
2315
|
const out = {};
|
|
2337
2316
|
for (const [k, v] of Object.entries(obj)) {
|
|
2338
2317
|
// Special-case any 'url' field
|
|
2339
|
-
if (typeof v === "string" &&
|
|
2340
|
-
k.toLowerCase() === "url" &&
|
|
2341
|
-
v.startsWith("data:")) {
|
|
2318
|
+
if (typeof v === "string" && k.toLowerCase() === "url" && v.startsWith("data:")) {
|
|
2342
2319
|
out[k] = formatDataUrlAsLabel(v);
|
|
2343
2320
|
continue;
|
|
2344
2321
|
}
|
|
@@ -2413,19 +2390,14 @@ function estimateNodeSize(args) {
|
|
|
2413
2390
|
* Used for positioning handles in React Flow.
|
|
2414
2391
|
*/
|
|
2415
2392
|
function getHandleLayoutY(rowIndex) {
|
|
2416
|
-
return
|
|
2417
|
-
rowIndex * NODE_ROW_HEIGHT_PX +
|
|
2418
|
-
NODE_ROW_HEIGHT_PX / 2);
|
|
2393
|
+
return NODE_HEADER_HEIGHT_PX + rowIndex * NODE_ROW_HEIGHT_PX + NODE_ROW_HEIGHT_PX / 2;
|
|
2419
2394
|
}
|
|
2420
2395
|
/**
|
|
2421
2396
|
* Calculate the Y position for handle bounds (top + centering offset).
|
|
2422
2397
|
* Used for hit-testing and edge routing.
|
|
2423
2398
|
*/
|
|
2424
2399
|
function getHandleBoundsY(rowIndex) {
|
|
2425
|
-
return
|
|
2426
|
-
rowIndex * NODE_ROW_HEIGHT_PX +
|
|
2427
|
-
(NODE_ROW_HEIGHT_PX - HANDLE_SIZE_PX) / 2 +
|
|
2428
|
-
1);
|
|
2400
|
+
return NODE_HEADER_HEIGHT_PX + rowIndex * NODE_ROW_HEIGHT_PX + (NODE_ROW_HEIGHT_PX - HANDLE_SIZE_PX) / 2 + 1;
|
|
2429
2401
|
}
|
|
2430
2402
|
/**
|
|
2431
2403
|
* Calculate the X position for handle bounds based on position and node width.
|
|
@@ -2464,7 +2436,7 @@ function createHandleLayout(args) {
|
|
|
2464
2436
|
};
|
|
2465
2437
|
}
|
|
2466
2438
|
function layoutNode(args, overrides) {
|
|
2467
|
-
const { node, registry, showValues, overrides: sizeOverrides, missingInputs = [], missingOutputs = []
|
|
2439
|
+
const { node, registry, showValues, overrides: sizeOverrides, missingInputs = [], missingOutputs = [] } = args;
|
|
2468
2440
|
const { inputs, outputs } = computeEffectiveHandles(node, registry);
|
|
2469
2441
|
const inputOrder = Object.keys(inputs).filter((k) => !isInputPrivate(inputs, k));
|
|
2470
2442
|
const outputOrder = Object.keys(outputs);
|
|
@@ -2577,14 +2549,11 @@ function useWorkbenchBridge(wb, handlesMap) {
|
|
|
2577
2549
|
// Check if target input handle is a non-array type (typeId doesn't end with [])
|
|
2578
2550
|
// If so, remove any existing connections to that handle before connecting
|
|
2579
2551
|
const targetHandles = handlesMap?.[params.target];
|
|
2580
|
-
const targetTypeId = targetHandles
|
|
2581
|
-
? getInputTypeId(targetHandles.inputs, params.targetHandle)
|
|
2582
|
-
: undefined;
|
|
2552
|
+
const targetTypeId = targetHandles ? getInputTypeId(targetHandles.inputs, params.targetHandle) : undefined;
|
|
2583
2553
|
const isArrayInput = targetTypeId?.endsWith("[]") ?? false;
|
|
2584
2554
|
if (!isArrayInput) {
|
|
2585
2555
|
// Find existing edges to this input handle
|
|
2586
|
-
const existingEdges = wb.def.edges.filter((e) => e.target.nodeId === params.target &&
|
|
2587
|
-
e.target.handle === params.targetHandle);
|
|
2556
|
+
const existingEdges = wb.def.edges.filter((e) => e.target.nodeId === params.target && e.target.handle === params.targetHandle);
|
|
2588
2557
|
// Remove existing edges (without committing yet)
|
|
2589
2558
|
for (const edge of existingEdges) {
|
|
2590
2559
|
wb.disconnect(edge.id, { commit: false });
|
|
@@ -2747,9 +2716,7 @@ function useThrottledValue(value, intervalMs) {
|
|
|
2747
2716
|
const lastSetAtRef = useRef(0);
|
|
2748
2717
|
const timeoutRef = useRef(null);
|
|
2749
2718
|
useEffect(() => {
|
|
2750
|
-
const now = typeof performance !== "undefined" && performance.now
|
|
2751
|
-
? performance.now()
|
|
2752
|
-
: Date.now();
|
|
2719
|
+
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2753
2720
|
const elapsed = now - lastSetAtRef.current;
|
|
2754
2721
|
if (elapsed >= intervalMs) {
|
|
2755
2722
|
lastSetAtRef.current = now;
|
|
@@ -2760,10 +2727,7 @@ function useThrottledValue(value, intervalMs) {
|
|
|
2760
2727
|
window.clearTimeout(timeoutRef.current);
|
|
2761
2728
|
}
|
|
2762
2729
|
timeoutRef.current = window.setTimeout(() => {
|
|
2763
|
-
lastSetAtRef.current =
|
|
2764
|
-
typeof performance !== "undefined" && performance.now
|
|
2765
|
-
? performance.now()
|
|
2766
|
-
: Date.now();
|
|
2730
|
+
lastSetAtRef.current = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2767
2731
|
setThrottled(value);
|
|
2768
2732
|
timeoutRef.current = null;
|
|
2769
2733
|
}, Math.max(0, intervalMs - elapsed));
|
|
@@ -2925,9 +2889,7 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2925
2889
|
const geom = computeLayout(n, sizeOverrides, extraInputs, extraOutputs);
|
|
2926
2890
|
const renderWidth = customSize?.width ?? geom.width;
|
|
2927
2891
|
const renderHeight = customSize?.height ?? geom.height;
|
|
2928
|
-
const initialGeom = customSize
|
|
2929
|
-
? computeLayout(n, overrideSize, extraInputs, extraOutputs)
|
|
2930
|
-
: geom;
|
|
2892
|
+
const initialGeom = customSize ? computeLayout(n, overrideSize, extraInputs, extraOutputs) : geom;
|
|
2931
2893
|
const inputHandles = geom.inputOrder.map((id) => ({
|
|
2932
2894
|
id,
|
|
2933
2895
|
typeId: getInputTypeId(inputSource, id) ?? "unknown",
|
|
@@ -2948,10 +2910,7 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2948
2910
|
inputHandles,
|
|
2949
2911
|
outputHandles,
|
|
2950
2912
|
handles: opts.handles[n.nodeId],
|
|
2951
|
-
inputConnected: Object.fromEntries(inputHandles.map((h) => [
|
|
2952
|
-
h.id,
|
|
2953
|
-
!!connectedInputs[n.nodeId]?.has(h.id),
|
|
2954
|
-
])),
|
|
2913
|
+
inputConnected: Object.fromEntries(inputHandles.map((h) => [h.id, !!connectedInputs[n.nodeId]?.has(h.id)])),
|
|
2955
2914
|
handleLayout,
|
|
2956
2915
|
showValues: opts.showValues,
|
|
2957
2916
|
renderWidth,
|
|
@@ -2975,17 +2934,13 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2975
2934
|
toElement: opts.toElement,
|
|
2976
2935
|
};
|
|
2977
2936
|
const customNodeData = opts.customData?.nodes;
|
|
2978
|
-
const mergedData = customNodeData?.[n.nodeId]
|
|
2979
|
-
? { ...baseData, custom: customNodeData[n.nodeId] }
|
|
2980
|
-
: baseData;
|
|
2937
|
+
const mergedData = customNodeData?.[n.nodeId] ? { ...baseData, custom: customNodeData[n.nodeId] } : baseData;
|
|
2981
2938
|
return {
|
|
2982
2939
|
id: n.nodeId,
|
|
2983
2940
|
data: mergedData,
|
|
2984
2941
|
position: positions[n.nodeId] ?? { x: 0, y: 0 },
|
|
2985
2942
|
type: opts.resolveNodeType?.(n.typeId) ?? "spark-default",
|
|
2986
|
-
selected: opts.selectedNodeIds
|
|
2987
|
-
? opts.selectedNodeIds.has(n.nodeId)
|
|
2988
|
-
: undefined,
|
|
2943
|
+
selected: opts.selectedNodeIds ? opts.selectedNodeIds.has(n.nodeId) : undefined,
|
|
2989
2944
|
measured: {
|
|
2990
2945
|
width: renderWidth,
|
|
2991
2946
|
height: renderHeight,
|
|
@@ -3017,11 +2972,10 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
3017
2972
|
const sourceNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
|
|
3018
2973
|
const targetNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
|
|
3019
2974
|
const sourceHandleTypeId = sourceHandles?.outputs[e.source.handle]
|
|
3020
|
-
? formatDeclaredTypeSignature(sourceHandles.outputs[e.source.handle]) ??
|
|
3021
|
-
"unknown"
|
|
2975
|
+
? (formatDeclaredTypeSignature(sourceHandles.outputs[e.source.handle]) ?? "unknown")
|
|
3022
2976
|
: "unknown";
|
|
3023
2977
|
const targetHandleTypeId = targetHandles?.inputs[e.target.handle]
|
|
3024
|
-
? getInputTypeId(targetHandles.inputs, e.target.handle) ?? "unknown"
|
|
2978
|
+
? (getInputTypeId(targetHandles.inputs, e.target.handle) ?? "unknown")
|
|
3025
2979
|
: "unknown";
|
|
3026
2980
|
const baseEdgeData = {
|
|
3027
2981
|
sourceNodeId: e.source.nodeId,
|
|
@@ -3039,18 +2993,14 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
3039
2993
|
isMissing,
|
|
3040
2994
|
};
|
|
3041
2995
|
const customEdgeData = opts.customData?.edges;
|
|
3042
|
-
const mergedEdgeData = customEdgeData?.[e.id]
|
|
3043
|
-
? { ...baseEdgeData, custom: customEdgeData[e.id] }
|
|
3044
|
-
: baseEdgeData;
|
|
2996
|
+
const mergedEdgeData = customEdgeData?.[e.id] ? { ...baseEdgeData, custom: customEdgeData[e.id] } : baseEdgeData;
|
|
3045
2997
|
return {
|
|
3046
2998
|
id: e.id,
|
|
3047
2999
|
source: e.source.nodeId,
|
|
3048
3000
|
target: e.target.nodeId,
|
|
3049
3001
|
sourceHandle: e.source.handle,
|
|
3050
3002
|
targetHandle: e.target.handle,
|
|
3051
|
-
selected: opts.selectedEdgeIds
|
|
3052
|
-
? opts.selectedEdgeIds.has(e.id)
|
|
3053
|
-
: undefined,
|
|
3003
|
+
selected: opts.selectedEdgeIds ? opts.selectedEdgeIds.has(e.id) : undefined,
|
|
3054
3004
|
animated: isRunning && !isMissing,
|
|
3055
3005
|
style,
|
|
3056
3006
|
label: edgeTypeId,
|
|
@@ -3073,13 +3023,7 @@ function getNodeBorderClassNames(args) {
|
|
|
3073
3023
|
// Keep border width constant to avoid layout reflow on selection toggles
|
|
3074
3024
|
const borderWidth = "border";
|
|
3075
3025
|
const borderStyle = isInvalid ? "border-dashed" : "border-solid";
|
|
3076
|
-
const severity = hasError || hasValidationError
|
|
3077
|
-
? "red"
|
|
3078
|
-
: hasValidationWarning
|
|
3079
|
-
? "amber"
|
|
3080
|
-
: isRunning
|
|
3081
|
-
? "blue"
|
|
3082
|
-
: "gray";
|
|
3026
|
+
const severity = hasError || hasValidationError ? "red" : hasValidationWarning ? "amber" : isRunning ? "blue" : "gray";
|
|
3083
3027
|
const borderBySeverity = {
|
|
3084
3028
|
red: "border-red-500",
|
|
3085
3029
|
amber: "border-amber-500",
|
|
@@ -3148,10 +3092,7 @@ function downloadJSON(payload, filename) {
|
|
|
3148
3092
|
function isSnapshotPayload(parsed) {
|
|
3149
3093
|
return (parsed !== null &&
|
|
3150
3094
|
typeof parsed === "object" &&
|
|
3151
|
-
("def" in parsed ||
|
|
3152
|
-
"inputs" in parsed ||
|
|
3153
|
-
"outputs" in parsed ||
|
|
3154
|
-
"environment" in parsed));
|
|
3095
|
+
("def" in parsed || "inputs" in parsed || "outputs" in parsed || "environment" in parsed));
|
|
3155
3096
|
}
|
|
3156
3097
|
async function download(wb, runner) {
|
|
3157
3098
|
try {
|
|
@@ -3257,18 +3198,13 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
|
|
|
3257
3198
|
// Simple remapping without offset
|
|
3258
3199
|
result.positions = {
|
|
3259
3200
|
...(targetUI?.positions || {}),
|
|
3260
|
-
...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [
|
|
3261
|
-
nodeIdMap[oldId] || oldId,
|
|
3262
|
-
pos,
|
|
3263
|
-
])),
|
|
3201
|
+
...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [nodeIdMap[oldId] || oldId, pos])),
|
|
3264
3202
|
};
|
|
3265
3203
|
}
|
|
3266
3204
|
}
|
|
3267
3205
|
// Merge selection: remap node IDs and edge IDs
|
|
3268
3206
|
if (sourceUI.selection) {
|
|
3269
|
-
const remappedNodes = (sourceUI.selection.nodes || [])
|
|
3270
|
-
.map((id) => nodeIdMap[id] || id)
|
|
3271
|
-
.filter((id) => id); // Filter out invalid mappings
|
|
3207
|
+
const remappedNodes = (sourceUI.selection.nodes || []).map((id) => nodeIdMap[id] || id).filter((id) => id); // Filter out invalid mappings
|
|
3272
3208
|
const remappedEdges = sourceUI.selection.edges || []; // Edge IDs don't need remapping typically
|
|
3273
3209
|
result.selection = {
|
|
3274
3210
|
nodes: [...(targetUI?.selection?.nodes || []), ...remappedNodes],
|
|
@@ -3279,19 +3215,13 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
|
|
|
3279
3215
|
if (sourceUI.nodeNames) {
|
|
3280
3216
|
result.nodeNames = {
|
|
3281
3217
|
...(targetUI?.nodeNames || {}),
|
|
3282
|
-
...Object.fromEntries(Object.entries(sourceUI.nodeNames).map(([oldId, name]) => [
|
|
3283
|
-
nodeIdMap[oldId] || oldId,
|
|
3284
|
-
name,
|
|
3285
|
-
])),
|
|
3218
|
+
...Object.fromEntries(Object.entries(sourceUI.nodeNames).map(([oldId, name]) => [nodeIdMap[oldId] || oldId, name])),
|
|
3286
3219
|
};
|
|
3287
3220
|
}
|
|
3288
3221
|
if (sourceUI.sizes) {
|
|
3289
3222
|
result.sizes = {
|
|
3290
3223
|
...(targetUI?.sizes || {}),
|
|
3291
|
-
...Object.fromEntries(Object.entries(sourceUI.sizes).map(([oldId, size]) => [
|
|
3292
|
-
nodeIdMap[oldId] || oldId,
|
|
3293
|
-
size,
|
|
3294
|
-
])),
|
|
3224
|
+
...Object.fromEntries(Object.entries(sourceUI.sizes).map(([oldId, size]) => [nodeIdMap[oldId] || oldId, size])),
|
|
3295
3225
|
};
|
|
3296
3226
|
}
|
|
3297
3227
|
return result;
|
|
@@ -3326,9 +3256,7 @@ function parseViewportTransform(transform) {
|
|
|
3326
3256
|
if (!translateMatch) {
|
|
3327
3257
|
const matrixMatch = transform.match(/matrix\(([^)]+)\)/);
|
|
3328
3258
|
if (matrixMatch) {
|
|
3329
|
-
const values = matrixMatch[1]
|
|
3330
|
-
.split(",")
|
|
3331
|
-
.map((v) => parseFloat(v.trim()));
|
|
3259
|
+
const values = matrixMatch[1].split(",").map((v) => parseFloat(v.trim()));
|
|
3332
3260
|
if (values.length >= 6) {
|
|
3333
3261
|
scale = Math.sqrt(values[0] * values[0] + values[1] * values[1]);
|
|
3334
3262
|
translateX = values[4];
|
|
@@ -3385,9 +3313,7 @@ function parseBorderRadius(borderRadiusStr) {
|
|
|
3385
3313
|
*/
|
|
3386
3314
|
function extractStrokeColor(element) {
|
|
3387
3315
|
if (element instanceof SVGPathElement) {
|
|
3388
|
-
return
|
|
3389
|
-
window.getComputedStyle(element).stroke ||
|
|
3390
|
-
"#b1b1b7");
|
|
3316
|
+
return element.getAttribute("stroke") || window.getComputedStyle(element).stroke || "#b1b1b7";
|
|
3391
3317
|
}
|
|
3392
3318
|
const style = window.getComputedStyle(element);
|
|
3393
3319
|
return (style.borderColor || style.borderTopColor || "#6b7280" // gray-500 default
|
|
@@ -3410,10 +3336,7 @@ function extractStrokeWidth(element, minWidth = 1) {
|
|
|
3410
3336
|
* Checks if a rectangle intersects with visible bounds
|
|
3411
3337
|
*/
|
|
3412
3338
|
function isRectVisible(x, y, width, height, bounds) {
|
|
3413
|
-
return
|
|
3414
|
-
x <= bounds.maxX &&
|
|
3415
|
-
y + height >= bounds.minY &&
|
|
3416
|
-
y <= bounds.maxY);
|
|
3339
|
+
return x + width >= bounds.minX && x <= bounds.maxX && y + height >= bounds.minY && y <= bounds.maxY;
|
|
3417
3340
|
}
|
|
3418
3341
|
/**
|
|
3419
3342
|
* Parses path data to get bounding box
|
|
@@ -3529,8 +3452,7 @@ function extractNodeStyles(nodeContent, nodeBackgroundColor) {
|
|
|
3529
3452
|
* Determines if a handle is a source (output) or target (input)
|
|
3530
3453
|
*/
|
|
3531
3454
|
function isHandleSource(handleEl) {
|
|
3532
|
-
return (handleEl.classList.contains("react-flow__handle-right") ||
|
|
3533
|
-
handleEl.classList.contains("react-flow__handle-source"));
|
|
3455
|
+
return (handleEl.classList.contains("react-flow__handle-right") || handleEl.classList.contains("react-flow__handle-source"));
|
|
3534
3456
|
}
|
|
3535
3457
|
/**
|
|
3536
3458
|
* Extracts handle position and calculates absolute coordinates
|
|
@@ -3625,7 +3547,7 @@ function extractNodeTitle(nodeEl, nodeX, nodeY) {
|
|
|
3625
3547
|
* Extracts visible nodes from React Flow viewport
|
|
3626
3548
|
*/
|
|
3627
3549
|
function extractNodes(viewport, visibleBounds, options = {}) {
|
|
3628
|
-
const { exportHandles = true, exportNodeTitle = true, nodeBackgroundColor
|
|
3550
|
+
const { exportHandles = true, exportNodeTitle = true, nodeBackgroundColor } = options;
|
|
3629
3551
|
const nodes = [];
|
|
3630
3552
|
let minX = Infinity;
|
|
3631
3553
|
let minY = Infinity;
|
|
@@ -3640,15 +3562,10 @@ function extractNodes(viewport, visibleBounds, options = {}) {
|
|
|
3640
3562
|
if (!nodeContent)
|
|
3641
3563
|
return;
|
|
3642
3564
|
const styles = extractNodeStyles(nodeContent, nodeBackgroundColor);
|
|
3643
|
-
const handles = exportHandles
|
|
3644
|
-
|
|
3645
|
-
: [];
|
|
3646
|
-
const title = exportNodeTitle
|
|
3647
|
-
? extractNodeTitle(nodeEl, position.x, position.y)
|
|
3648
|
-
: undefined;
|
|
3565
|
+
const handles = exportHandles ? extractNodeHandles(nodeEl, position.x, position.y, dimensions.width) : [];
|
|
3566
|
+
const title = exportNodeTitle ? extractNodeTitle(nodeEl, position.x, position.y) : undefined;
|
|
3649
3567
|
// Only include node if it's within visible viewport (or if ignoring viewport)
|
|
3650
|
-
if (!visibleBounds ||
|
|
3651
|
-
isRectVisible(position.x, position.y, dimensions.width, dimensions.height, visibleBounds)) {
|
|
3568
|
+
if (!visibleBounds || isRectVisible(position.x, position.y, dimensions.width, dimensions.height, visibleBounds)) {
|
|
3652
3569
|
nodes.push({
|
|
3653
3570
|
x: position.x,
|
|
3654
3571
|
y: position.y,
|
|
@@ -3681,7 +3598,7 @@ function extractNodes(viewport, visibleBounds, options = {}) {
|
|
|
3681
3598
|
* Creates SVG element with dot pattern background (matching React Flow)
|
|
3682
3599
|
*/
|
|
3683
3600
|
function createSVGElement(width, height, options = {}) {
|
|
3684
|
-
const { exportBackgroundPattern = true, backgroundPatternColor = "#f1f1f1", backgroundColor = "#ffffff"
|
|
3601
|
+
const { exportBackgroundPattern = true, backgroundPatternColor = "#f1f1f1", backgroundColor = "#ffffff" } = options;
|
|
3685
3602
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
3686
3603
|
svg.setAttribute("width", String(width));
|
|
3687
3604
|
svg.setAttribute("height", String(height));
|
|
@@ -3831,9 +3748,7 @@ async function captureCanvasThumbnail(containerElement, options = {}) {
|
|
|
3831
3748
|
const transform = parseViewportTransform(viewportTransform);
|
|
3832
3749
|
// Calculate visible bounds (null if ignoring viewport)
|
|
3833
3750
|
const viewportRect = reactFlowViewport.getBoundingClientRect();
|
|
3834
|
-
const visibleBounds = ignoreViewport
|
|
3835
|
-
? null
|
|
3836
|
-
: calculateVisibleBounds(viewportRect, transform);
|
|
3751
|
+
const visibleBounds = ignoreViewport ? null : calculateVisibleBounds(viewportRect, transform);
|
|
3837
3752
|
// Extract edges and nodes (pass null bounds if ignoring viewport to get all)
|
|
3838
3753
|
const { edges, bounds: edgeBounds } = extractEdgePaths(reactFlowViewport, visibleBounds);
|
|
3839
3754
|
const { nodes, bounds: nodeBounds } = extractNodes(reactFlowViewport, visibleBounds, {
|
|
@@ -4213,13 +4128,9 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4213
4128
|
// Subscribe to runner/workbench events
|
|
4214
4129
|
useEffect(() => {
|
|
4215
4130
|
const add = (source, type) => (payload) => setEvents((prev) => {
|
|
4216
|
-
if (source === "workbench" &&
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
.change?.type;
|
|
4220
|
-
if (changeType === "moveNode" ||
|
|
4221
|
-
changeType === "moveNodes" ||
|
|
4222
|
-
changeType === "resizeNodes")
|
|
4131
|
+
if (source === "workbench" && (type === "graphChanged" || type === "graphUiChanged")) {
|
|
4132
|
+
const changeType = payload.change?.type;
|
|
4133
|
+
if (changeType === "moveNode" || changeType === "moveNodes" || changeType === "resizeNodes")
|
|
4223
4134
|
return prev;
|
|
4224
4135
|
}
|
|
4225
4136
|
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
@@ -4356,8 +4267,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4356
4267
|
// Track custom errors for UI display
|
|
4357
4268
|
setSystemErrors((prev) => {
|
|
4358
4269
|
// Avoid duplicates by checking message and code
|
|
4359
|
-
if (prev.some((err) => err.message === systemError.message &&
|
|
4360
|
-
err.code === systemError.code)) {
|
|
4270
|
+
if (prev.some((err) => err.message === systemError.message && err.code === systemError.code)) {
|
|
4361
4271
|
return prev;
|
|
4362
4272
|
}
|
|
4363
4273
|
return [...prev, systemError];
|
|
@@ -4387,7 +4297,10 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4387
4297
|
// Validate runId is a non-empty string
|
|
4388
4298
|
const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
|
|
4389
4299
|
if (!isValidRunId) {
|
|
4390
|
-
console.warn(`[WorkbenchContext] node-start event missing or invalid runId for node ${id}`, {
|
|
4300
|
+
console.warn(`[WorkbenchContext] node-start event missing or invalid runId for node ${id}`, {
|
|
4301
|
+
runId,
|
|
4302
|
+
event: s,
|
|
4303
|
+
});
|
|
4391
4304
|
}
|
|
4392
4305
|
setNodeStatus((prev) => {
|
|
4393
4306
|
const current = prev[id]?.activeRuns ?? 0;
|
|
@@ -4397,9 +4310,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4397
4310
|
[id]: {
|
|
4398
4311
|
...prev[id],
|
|
4399
4312
|
activeRuns: current + 1,
|
|
4400
|
-
activeRunIds: isValidRunId
|
|
4401
|
-
? [...currentRunIds, runId]
|
|
4402
|
-
: currentRunIds,
|
|
4313
|
+
activeRunIds: isValidRunId ? [...currentRunIds, runId] : currentRunIds,
|
|
4403
4314
|
progress: 0,
|
|
4404
4315
|
},
|
|
4405
4316
|
};
|
|
@@ -4443,13 +4354,14 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4443
4354
|
const current = prev[id]?.activeRuns ?? 0;
|
|
4444
4355
|
const currentRunIds = prev[id]?.activeRunIds ?? [];
|
|
4445
4356
|
if (isValidRunId && !currentRunIds.includes(runId)) {
|
|
4446
|
-
console.warn(`[WorkbenchContext] node-done event for unknown runId: node=${id} runId=${runId}`, {
|
|
4357
|
+
console.warn(`[WorkbenchContext] node-done event for unknown runId: node=${id} runId=${runId}`, {
|
|
4358
|
+
event: s,
|
|
4359
|
+
currentRunIds,
|
|
4360
|
+
});
|
|
4447
4361
|
return prev; // Ignore stale event
|
|
4448
4362
|
}
|
|
4449
4363
|
const nextActive = Math.max(0, current - 1); // Prevent negative values
|
|
4450
|
-
const nextRunIds = isValidRunId
|
|
4451
|
-
? currentRunIds.filter((rid) => rid !== runId)
|
|
4452
|
-
: currentRunIds;
|
|
4364
|
+
const nextRunIds = isValidRunId ? currentRunIds.filter((rid) => rid !== runId) : currentRunIds;
|
|
4453
4365
|
const keepProgress = hadError || nextActive > 0;
|
|
4454
4366
|
// Clear error flag for this runId
|
|
4455
4367
|
if (isValidRunId && errorRunsRef.current[id]?.[runId]) {
|
|
@@ -4575,15 +4487,12 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4575
4487
|
}
|
|
4576
4488
|
}
|
|
4577
4489
|
}
|
|
4578
|
-
else if (event.change?.type !== "setInputs" &&
|
|
4579
|
-
event.change?.type !== "updateResolvedHandles") {
|
|
4490
|
+
else if (event.change?.type !== "setInputs" && event.change?.type !== "updateResolvedHandles") {
|
|
4580
4491
|
await runner.update(event.def, { dry: event.dry });
|
|
4581
4492
|
}
|
|
4582
4493
|
if (event.commit) {
|
|
4583
4494
|
await saveUiRuntimeMetadata(wb, runner);
|
|
4584
|
-
const history = await runner
|
|
4585
|
-
.commit(event.reason ?? reason)
|
|
4586
|
-
.catch((err) => {
|
|
4495
|
+
const history = await runner.commit(event.reason ?? reason).catch((err) => {
|
|
4587
4496
|
console.error("[WorkbenchContext] Error committing after update:", err);
|
|
4588
4497
|
return undefined;
|
|
4589
4498
|
});
|
|
@@ -4602,9 +4511,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4602
4511
|
setSelectedEdgeId(sel.edges?.[0]);
|
|
4603
4512
|
if (sel.commit) {
|
|
4604
4513
|
await saveUiRuntimeMetadata(wb, runner);
|
|
4605
|
-
const history = await runner
|
|
4606
|
-
.commit(sel.reason ?? "selection")
|
|
4607
|
-
.catch((err) => {
|
|
4514
|
+
const history = await runner.commit(sel.reason ?? "selection").catch((err) => {
|
|
4608
4515
|
console.error("[WorkbenchContext] Error committing selection change:", err);
|
|
4609
4516
|
return undefined;
|
|
4610
4517
|
});
|
|
@@ -4656,9 +4563,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4656
4563
|
}
|
|
4657
4564
|
}
|
|
4658
4565
|
await saveUiRuntimeMetadata(wb, runner);
|
|
4659
|
-
const history = await runner
|
|
4660
|
-
.commit(event.reason ?? reason)
|
|
4661
|
-
.catch((err) => {
|
|
4566
|
+
const history = await runner.commit(event.reason ?? reason).catch((err) => {
|
|
4662
4567
|
console.error("[WorkbenchContext] Error committing UI changes:", err);
|
|
4663
4568
|
return undefined;
|
|
4664
4569
|
});
|
|
@@ -4719,9 +4624,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4719
4624
|
const metadata = workbenchRuntimeState;
|
|
4720
4625
|
let changed = false;
|
|
4721
4626
|
// If nodeId is specified, only update that node; otherwise update all nodes
|
|
4722
|
-
const nodesToUpdate = event.nodeId
|
|
4723
|
-
? wb.def.nodes.filter((n) => n.nodeId === event.nodeId)
|
|
4724
|
-
: wb.def.nodes;
|
|
4627
|
+
const nodesToUpdate = event.nodeId ? wb.def.nodes.filter((n) => n.nodeId === event.nodeId) : wb.def.nodes;
|
|
4725
4628
|
for (const n of nodesToUpdate) {
|
|
4726
4629
|
const cur = next[n.nodeId];
|
|
4727
4630
|
if (!cur)
|
|
@@ -4932,12 +4835,12 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4932
4835
|
getNodeDisplayName,
|
|
4933
4836
|
setNodeName,
|
|
4934
4837
|
]);
|
|
4935
|
-
return
|
|
4838
|
+
return jsx(WorkbenchContext.Provider, { value: value, children: children });
|
|
4936
4839
|
}
|
|
4937
4840
|
|
|
4938
4841
|
function IssueBadge({ level, title, size = 12, className, }) {
|
|
4939
4842
|
const colorClass = level === "error" ? "text-red-600" : "text-amber-600";
|
|
4940
|
-
return (jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ?
|
|
4843
|
+
return (jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ? jsx(XCircleIcon, { size: size, weight: "fill" }) : jsx(WarningCircleIcon, { size: size, weight: "fill" }) }));
|
|
4941
4844
|
}
|
|
4942
4845
|
|
|
4943
4846
|
function DefaultNodeHeader({ id, typeId, validation, right, showId, }) {
|
|
@@ -4972,9 +4875,7 @@ function DefaultNodeHeader({ id, typeId, validation, right, showId, }) {
|
|
|
4972
4875
|
const trimmed = editValue.trim();
|
|
4973
4876
|
const defaultDisplayName = getDefaultDisplayName();
|
|
4974
4877
|
// If the trimmed value matches the default display name or typeId, clear the custom name
|
|
4975
|
-
ctx.setNodeName(id, trimmed === defaultDisplayName || trimmed === effectiveTypeId
|
|
4976
|
-
? undefined
|
|
4977
|
-
: trimmed);
|
|
4878
|
+
ctx.setNodeName(id, trimmed === defaultDisplayName || trimmed === effectiveTypeId ? undefined : trimmed);
|
|
4978
4879
|
setIsEditing(false);
|
|
4979
4880
|
}, [ctx, id, editValue, getDefaultDisplayName, effectiveTypeId, typeId]);
|
|
4980
4881
|
const handleCancel = React.useCallback(() => {
|
|
@@ -5011,19 +4912,13 @@ function DefaultNodeHeader({ id, typeId, validation, right, showId, }) {
|
|
|
5011
4912
|
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors", title: "Run from here", children: jsx(FastForwardIcon, { size: 10, weight: "fill" }) }), jsx("button", { onClick: (e) => {
|
|
5012
4913
|
e.stopPropagation();
|
|
5013
4914
|
ctx.abortNode(id);
|
|
5014
|
-
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors", title: "Abort node", children: jsx(StopIcon, { size: 10, weight: "fill" }) })] })), right, validation.issues && validation.issues.length > 0 && (jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
|
|
5015
|
-
? "error"
|
|
5016
|
-
: "warning", size: 12, className: "w-3 h-3", title: validation.issues
|
|
5017
|
-
.map((v) => `${v.code}: ${v.message}`)
|
|
5018
|
-
.join("; ") })), showId && jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
4915
|
+
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors", title: "Abort node", children: jsx(StopIcon, { size: 10, weight: "fill" }) })] })), right, validation.issues && validation.issues.length > 0 && (jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error") ? "error" : "warning", size: 12, className: "w-3 h-3", title: validation.issues.map((v) => `${v.code}: ${v.message}`).join("; ") })), showId && jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
5019
4916
|
}
|
|
5020
4917
|
|
|
5021
4918
|
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
5022
4919
|
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: {
|
|
5023
4920
|
top: (y ?? 0) - 8,
|
|
5024
|
-
...(kind === "input"
|
|
5025
|
-
? { right: "50%" }
|
|
5026
|
-
: { left: "50%", textAlign: "right" }),
|
|
4921
|
+
...(kind === "input" ? { right: "50%" } : { left: "50%", textAlign: "right" }),
|
|
5027
4922
|
whiteSpace: "nowrap",
|
|
5028
4923
|
overflow: "hidden",
|
|
5029
4924
|
textOverflow: "ellipsis",
|
|
@@ -5083,7 +4978,7 @@ function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClas
|
|
|
5083
4978
|
})] }));
|
|
5084
4979
|
}
|
|
5085
4980
|
|
|
5086
|
-
function DefaultNodeContent({ data, isConnectable
|
|
4981
|
+
function DefaultNodeContent({ data, isConnectable }) {
|
|
5087
4982
|
const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
|
|
5088
4983
|
const inputEntries = data.inputHandles ?? [];
|
|
5089
4984
|
const outputEntries = data.outputHandles ?? [];
|
|
@@ -5108,9 +5003,7 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
5108
5003
|
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === handleId);
|
|
5109
5004
|
const hasAny = vIssues.length > 0;
|
|
5110
5005
|
const hasErr = vIssues.some((v) => v.level === "error");
|
|
5111
|
-
const title = vIssues
|
|
5112
|
-
.map((v) => `${v.code}: ${v.message}`)
|
|
5113
|
-
.join("; ");
|
|
5006
|
+
const title = vIssues.map((v) => `${v.code}: ${v.message}`).join("; ");
|
|
5114
5007
|
const valueText = (() => {
|
|
5115
5008
|
if (!showValues)
|
|
5116
5009
|
return undefined;
|
|
@@ -5118,8 +5011,7 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
5118
5011
|
const value = inputValues?.[entry.id];
|
|
5119
5012
|
const isDefault = value !== undefined &&
|
|
5120
5013
|
inputDefaults?.[entry.id] !== undefined &&
|
|
5121
|
-
JSON.stringify(value) ===
|
|
5122
|
-
JSON.stringify(inputDefaults[entry.id]);
|
|
5014
|
+
JSON.stringify(value) === JSON.stringify(inputDefaults[entry.id]);
|
|
5123
5015
|
const txt = toString(entry.typeId, value);
|
|
5124
5016
|
const str = typeof txt === "string" ? txt : String(txt);
|
|
5125
5017
|
return { text: str, isDefault };
|
|
@@ -5135,7 +5027,7 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
5135
5027
|
} })] }));
|
|
5136
5028
|
}
|
|
5137
5029
|
|
|
5138
|
-
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable
|
|
5030
|
+
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable }) {
|
|
5139
5031
|
const nodeRef = useRef(null);
|
|
5140
5032
|
const { typeId } = data;
|
|
5141
5033
|
const status = data.status ?? { activeRuns: 0 };
|
|
@@ -5162,7 +5054,7 @@ const DefaultEdge = React.memo(function DefaultEdge({ id, sourceX, sourceY, targ
|
|
|
5162
5054
|
targetY,
|
|
5163
5055
|
targetPosition,
|
|
5164
5056
|
});
|
|
5165
|
-
return
|
|
5057
|
+
return jsx(BaseEdge, { id: id, path: edgePath, style: style, markerEnd: markerEnd });
|
|
5166
5058
|
});
|
|
5167
5059
|
|
|
5168
5060
|
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose, getDefaultNodeSize, onCopyResult, runNode, runFromHere) {
|
|
@@ -5226,9 +5118,7 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
|
|
|
5226
5118
|
*/
|
|
5227
5119
|
function createCopyHandler(wb, runner, getDefaultNodeSize, onCopyResult) {
|
|
5228
5120
|
return () => {
|
|
5229
|
-
const getNodeSize = getDefaultNodeSize
|
|
5230
|
-
? (nodeId, typeId) => getDefaultNodeSize(typeId)
|
|
5231
|
-
: undefined;
|
|
5121
|
+
const getNodeSize = getDefaultNodeSize ? (nodeId, typeId) => getDefaultNodeSize(typeId) : undefined;
|
|
5232
5122
|
const data = wb.copySelection(runner, getNodeSize);
|
|
5233
5123
|
if (onCopyResult) {
|
|
5234
5124
|
onCopyResult(data);
|
|
@@ -5243,9 +5133,7 @@ function createNodeCopyHandler(wb, runner, nodeId, getDefaultNodeSize, onCopyRes
|
|
|
5243
5133
|
// Select the node first, then copy
|
|
5244
5134
|
const currentSelection = wb.getSelection();
|
|
5245
5135
|
wb.setSelection({ nodes: [nodeId], edges: [] });
|
|
5246
|
-
const getNodeSize = getDefaultNodeSize
|
|
5247
|
-
? (nodeId, typeId) => getDefaultNodeSize(typeId)
|
|
5248
|
-
: undefined;
|
|
5136
|
+
const getNodeSize = getDefaultNodeSize ? (nodeId, typeId) => getDefaultNodeSize(typeId) : undefined;
|
|
5249
5137
|
const data = wb.copySelection(runner, getNodeSize);
|
|
5250
5138
|
// Restore original selection
|
|
5251
5139
|
wb.setSelection(currentSelection);
|
|
@@ -5334,8 +5222,7 @@ function getBakeableOutputs(nodeId, wb, registry, outputTypesMap) {
|
|
|
5334
5222
|
const tElem = registry.types.get(base);
|
|
5335
5223
|
const arrT = tArr?.bakeTarget;
|
|
5336
5224
|
const elemT = tElem?.bakeTarget;
|
|
5337
|
-
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
5338
|
-
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
5225
|
+
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) || (elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
5339
5226
|
out.push(h);
|
|
5340
5227
|
}
|
|
5341
5228
|
else {
|
|
@@ -5357,9 +5244,7 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
5357
5244
|
const scrollRef = useRef(null);
|
|
5358
5245
|
const [copied, setCopied] = useState(false);
|
|
5359
5246
|
const rows = useMemo(() => {
|
|
5360
|
-
const filtered = hideWorkbench
|
|
5361
|
-
? events.filter((e) => e.source !== "workbench")
|
|
5362
|
-
: events;
|
|
5247
|
+
const filtered = hideWorkbench ? events.filter((e) => e.source !== "workbench") : events;
|
|
5363
5248
|
return filtered.slice().reverse();
|
|
5364
5249
|
}, [events, hideWorkbench]);
|
|
5365
5250
|
useEffect(() => {
|
|
@@ -5428,17 +5313,17 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5428
5313
|
const selectedEdge = wb.def.edges.find((e) => e.id === selectedEdgeId);
|
|
5429
5314
|
// Use precomputed handles map from context
|
|
5430
5315
|
const effectiveHandles = selectedNode
|
|
5431
|
-
? handlesMap[selectedNode.nodeId] ?? {
|
|
5316
|
+
? (handlesMap[selectedNode.nodeId] ?? {
|
|
5432
5317
|
inputs: {},
|
|
5433
5318
|
outputs: {},
|
|
5434
5319
|
inputDefaults: {},
|
|
5435
|
-
}
|
|
5320
|
+
})
|
|
5436
5321
|
: { inputs: {}, outputs: {}, inputDefaults: {} };
|
|
5437
5322
|
const inputHandles = Object.entries(effectiveHandles.inputs)
|
|
5438
5323
|
.filter(([k]) => !isInputPrivate(effectiveHandles.inputs, k))
|
|
5439
5324
|
.map(([k]) => k);
|
|
5440
5325
|
const outputHandles = Object.keys(effectiveHandles.outputs);
|
|
5441
|
-
const nodeInputsRaw = selectedNodeId ? inputsMap[selectedNodeId] ?? {} : {};
|
|
5326
|
+
const nodeInputsRaw = selectedNodeId ? (inputsMap[selectedNodeId] ?? {}) : {};
|
|
5442
5327
|
const nodeInputsDefaults = selectedNodeId
|
|
5443
5328
|
? {
|
|
5444
5329
|
...effectiveHandles.inputDefaults,
|
|
@@ -5447,7 +5332,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5447
5332
|
: {};
|
|
5448
5333
|
// Keep defaults separate for placeholder use (don't merge into nodeInputs)
|
|
5449
5334
|
const nodeInputs = nodeInputsRaw;
|
|
5450
|
-
const nodeOutputs = selectedNodeId ? outputsMap[selectedNodeId] ?? {} : {};
|
|
5335
|
+
const nodeOutputs = selectedNodeId ? (outputsMap[selectedNodeId] ?? {}) : {};
|
|
5451
5336
|
// Helper to truncate long values
|
|
5452
5337
|
const truncateValue = (str, maxLen = 50) => {
|
|
5453
5338
|
if (str.length <= maxLen)
|
|
@@ -5468,15 +5353,9 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5468
5353
|
document.body.removeChild(textarea);
|
|
5469
5354
|
});
|
|
5470
5355
|
};
|
|
5471
|
-
const selectedNodeStatus = selectedNodeId
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
const selectedNodeValidation = selectedNodeId
|
|
5475
|
-
? nodeValidationIssues?.[selectedNodeId] ?? []
|
|
5476
|
-
: [];
|
|
5477
|
-
const selectedEdgeValidation = selectedEdge
|
|
5478
|
-
? edgeValidationIssues?.[selectedEdge.id] ?? []
|
|
5479
|
-
: [];
|
|
5356
|
+
const selectedNodeStatus = selectedNodeId ? nodeStatus?.[selectedNodeId] : undefined;
|
|
5357
|
+
const selectedNodeValidation = selectedNodeId ? (nodeValidationIssues?.[selectedNodeId] ?? []) : [];
|
|
5358
|
+
const selectedEdgeValidation = selectedEdge ? (edgeValidationIssues?.[selectedEdge.id] ?? []) : [];
|
|
5480
5359
|
const selectedNodeHandleValidation = selectedNodeId
|
|
5481
5360
|
? {
|
|
5482
5361
|
inputs: nodeValidationHandles?.inputs?.[selectedNodeId] ?? [],
|
|
@@ -5506,10 +5385,8 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5506
5385
|
if (outIssues.length === 0)
|
|
5507
5386
|
return null;
|
|
5508
5387
|
const outErr = outIssues.some((m) => m.level === "error");
|
|
5509
|
-
const outTitle = outIssues
|
|
5510
|
-
|
|
5511
|
-
.join("; ");
|
|
5512
|
-
return (jsx(IssueBadge, { level: outErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: outTitle }));
|
|
5388
|
+
const outTitle = outIssues.map((v) => `${v.code}: ${v.message}`).join("; ");
|
|
5389
|
+
return jsx(IssueBadge, { level: outErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: outTitle });
|
|
5513
5390
|
}, [selectedNodeHandleValidation]);
|
|
5514
5391
|
// Render output display value
|
|
5515
5392
|
const renderOutputDisplay = useCallback((outputValue, effectiveHandle) => {
|
|
@@ -5551,9 +5428,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5551
5428
|
const { typeId: displayTypeId, value: displayValue } = unwrapForDisplay(typeId, current);
|
|
5552
5429
|
const display = safeToString(displayTypeId, displayValue);
|
|
5553
5430
|
const wasOriginal = originals[h];
|
|
5554
|
-
const isDirty = drafts[h] !== undefined &&
|
|
5555
|
-
wasOriginal !== undefined &&
|
|
5556
|
-
drafts[h] !== wasOriginal;
|
|
5431
|
+
const isDirty = drafts[h] !== undefined && wasOriginal !== undefined && drafts[h] !== wasOriginal;
|
|
5557
5432
|
if (!isDirty) {
|
|
5558
5433
|
nextDrafts[h] = display;
|
|
5559
5434
|
nextOriginals[h] = display;
|
|
@@ -5576,7 +5451,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5576
5451
|
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto select-none`, children: [jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsx("div", { className: "mb-2", children: contextPanel }), inputValidationErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [inputValidationErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Input Validation Error" }), jsx("div", { className: "break-words", children: err.message }), jsxs("div", { className: "text-[10px] text-red-600 mt-1", children: [err.nodeId, ".", err.handle, " (type: ", err.typeId, ")"] })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeInputValidationError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), inputValidationErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearInputValidationErrors, children: "Clear all" }))] })), systemErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsx("div", { className: "break-words", children: err.message })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Registry Error" }), jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
5577
5452
|
e.stopPropagation();
|
|
5578
5453
|
deleteEdgeById(m.data?.edgeId);
|
|
5579
|
-
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192
|
|
5454
|
+
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192 ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), renderEdgeStatus(), jsx("div", { className: "mt-1", children: jsx("button", { className: "text-xs px-2 py-1 border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
5580
5455
|
e.stopPropagation();
|
|
5581
5456
|
deleteEdgeById(selectedEdge.id);
|
|
5582
5457
|
}, title: "Delete this edge", children: "Delete edge" }) }), jsxs("div", { className: "flex items-center gap-2 mt-1", children: [jsxs("label", { className: "w-20 flex flex-col", children: [jsx("span", { children: "Type" }), jsx("span", { className: "text-gray-500 text-[11px]", children: "DataTypeId" })] }), jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: selectedEdge.typeId ?? "", onChange: (e) => {
|
|
@@ -5586,26 +5461,17 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5586
5461
|
}, children: [jsx("option", { value: "", children: "(infer from source)" }), Array.from(wb.registry.types.keys()).map((tid) => (jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` }), jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
5587
5462
|
e.stopPropagation();
|
|
5588
5463
|
deleteEdgeById(selectedEdge.id);
|
|
5589
|
-
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxs("div", { children: [selectedNode && (jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.activeRuns &&
|
|
5590
|
-
selectedNodeStatus.activeRuns > 0 && (jsxs("div", { className: "mt-1 text-xs text-blue-700 bg-blue-50 border border-blue-200 rounded px-2 py-1", children: [jsxs("div", { className: "font-semibold", children: ["Running (", selectedNodeStatus.activeRuns, ")"] }), selectedNodeStatus.activeRunIds &&
|
|
5591
|
-
selectedNodeStatus.activeRunIds.length > 0 ? (jsxs("div", { className: "mt-1", children: [jsx("div", { className: "text-[10px] text-blue-600", children: "RunIds:" }), jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: selectedNodeStatus.activeRunIds.map((runId, idx) => (jsx("span", { className: "text-[10px] px-1.5 py-0.5 bg-blue-100 border border-blue-300 rounded font-mono", children: runId }, idx))) })] })) : (jsx("div", { className: "text-[10px] text-blue-600 mt-1", children: "RunIds not available (some runs may have started without runId)" }))] })), !!selectedNodeStatus?.lastError && (jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
5592
|
-
selectedNodeStatus.lastError) }))] })), jsxs("div", { className: "mb-2", children: [jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
5464
|
+
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxs("div", { children: [selectedNode && (jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.activeRuns && selectedNodeStatus.activeRuns > 0 && (jsxs("div", { className: "mt-1 text-xs text-blue-700 bg-blue-50 border border-blue-200 rounded px-2 py-1", children: [jsxs("div", { className: "font-semibold", children: ["Running (", selectedNodeStatus.activeRuns, ")"] }), selectedNodeStatus.activeRunIds && selectedNodeStatus.activeRunIds.length > 0 ? (jsxs("div", { className: "mt-1", children: [jsx("div", { className: "text-[10px] text-blue-600", children: "RunIds:" }), jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: selectedNodeStatus.activeRunIds.map((runId, idx) => (jsx("span", { className: "text-[10px] px-1.5 py-0.5 bg-blue-100 border border-blue-300 rounded font-mono", children: runId }, idx))) })] })) : (jsx("div", { className: "text-[10px] text-blue-600 mt-1", children: "RunIds not available (some runs may have started without runId)" }))] })), !!selectedNodeStatus?.lastError && (jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ?? selectedNodeStatus.lastError) }))] })), jsxs("div", { className: "mb-2", children: [jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
5593
5465
|
const typeId = getInputTypeId(effectiveHandles.inputs, h);
|
|
5594
5466
|
const declaredTypes = getInputDeclaredTypes(effectiveHandles.inputs, h);
|
|
5595
|
-
const typeLabel = Array.isArray(declaredTypes)
|
|
5596
|
-
|
|
5597
|
-
: typeId ?? "";
|
|
5598
|
-
const isLinked = wb.def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
5599
|
-
e.target.handle === h);
|
|
5467
|
+
const typeLabel = Array.isArray(declaredTypes) ? declaredTypes.join(" | ") : (typeId ?? "");
|
|
5468
|
+
const isLinked = wb.def.edges.some((e) => e.target.nodeId === selectedNodeId && e.target.handle === h);
|
|
5600
5469
|
const inbound = new Set(wb.def.edges
|
|
5601
|
-
.filter((e) => e.target.nodeId === selectedNodeId &&
|
|
5602
|
-
e.target.handle === h)
|
|
5470
|
+
.filter((e) => e.target.nodeId === selectedNodeId && e.target.handle === h)
|
|
5603
5471
|
.map((e) => e.target.handle));
|
|
5604
5472
|
const { typeId: defaultTypeId, value: defaultValue } = unwrapForDisplay(typeId, nodeInputsDefaults[h]);
|
|
5605
5473
|
const hasDefault = !inbound.has(h) && nodeInputsDefaults[h] !== undefined;
|
|
5606
|
-
const defaultStr = hasDefault
|
|
5607
|
-
? safeToString(defaultTypeId, defaultValue)
|
|
5608
|
-
: undefined;
|
|
5474
|
+
const defaultStr = hasDefault ? safeToString(defaultTypeId, defaultValue) : undefined;
|
|
5609
5475
|
const commonProps = {
|
|
5610
5476
|
style: { flex: 1 },
|
|
5611
5477
|
disabled: isLinked,
|
|
@@ -5643,12 +5509,8 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5643
5509
|
const inIssues = selectedNodeHandleValidation.inputs.filter((m) => m.handle === h);
|
|
5644
5510
|
const hasValidation = inIssues.length > 0;
|
|
5645
5511
|
const hasErr = inIssues.some((m) => m.level === "error");
|
|
5646
|
-
const title = inIssues
|
|
5647
|
-
|
|
5648
|
-
.join("; ");
|
|
5649
|
-
return (jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxs("label", { className: "w-32 flex flex-col", children: [jsx("span", { children: prettyHandle(h) }), jsx("span", { className: "text-gray-500 text-[11px]", children: typeLabel })] }), hasValidation && (jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", value: current !== undefined && current !== null
|
|
5650
|
-
? String(current)
|
|
5651
|
-
: "", onChange: (e) => {
|
|
5512
|
+
const title = inIssues.map((v) => `${v.code}: ${v.message}`).join("; ");
|
|
5513
|
+
return (jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxs("label", { className: "w-32 flex flex-col", children: [jsx("span", { children: prettyHandle(h) }), jsx("span", { className: "text-gray-500 text-[11px]", children: typeLabel })] }), hasValidation && (jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", value: current !== undefined && current !== null ? String(current) : "", onChange: (e) => {
|
|
5652
5514
|
const val = e.target.value;
|
|
5653
5515
|
const raw = val === "" ? undefined : Number(val);
|
|
5654
5516
|
setInput(h, raw);
|
|
@@ -5656,13 +5518,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5656
5518
|
const display = safeToString(typeId, raw);
|
|
5657
5519
|
setDrafts((d) => ({ ...d, [h]: display }));
|
|
5658
5520
|
setOriginals((o) => ({ ...o, [h]: display }));
|
|
5659
|
-
}, ...commonProps, children: [jsx("option", { value: "", children: placeholder
|
|
5660
|
-
? `Default: ${placeholder}`
|
|
5661
|
-
: "(select)" }), wb.registry.enums
|
|
5662
|
-
.get(typeId)
|
|
5663
|
-
?.options.map((opt) => (jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] }), hasValue && !isLinked && (jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded text-gray-500 hover:text-gray-700", onClick: clearInput, title: "Clear input value", children: jsx(XCircleIcon, { size: 16 }) }))] })) : isLinked ? (jsx("div", { className: "flex items-center gap-1 flex-1", children: jsx("div", { className: "flex-1 min-w-0", children: renderLinkedInputDisplay(displayTypeId, displayValue) }) })) : (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", placeholder: placeholder
|
|
5664
|
-
? `Default: ${placeholder}`
|
|
5665
|
-
: undefined, value: value, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
5521
|
+
}, ...commonProps, children: [jsx("option", { value: "", children: placeholder ? `Default: ${placeholder}` : "(select)" }), wb.registry.enums.get(typeId)?.options.map((opt) => (jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] }), hasValue && !isLinked && (jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded text-gray-500 hover:text-gray-700", onClick: clearInput, title: "Clear input value", children: jsx(XCircleIcon, { size: 16 }) }))] })) : isLinked ? (jsx("div", { className: "flex items-center gap-1 flex-1", children: jsx("div", { className: "flex-1 min-w-0", children: renderLinkedInputDisplay(displayTypeId, displayValue) }) })) : (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", placeholder: placeholder ? `Default: ${placeholder}` : undefined, value: value, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
5666
5522
|
if (e.key === "Enter")
|
|
5667
5523
|
commit();
|
|
5668
5524
|
if (e.key === "Escape")
|
|
@@ -5676,12 +5532,11 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5676
5532
|
|
|
5677
5533
|
// Helper to format shortcut for current platform
|
|
5678
5534
|
function formatShortcut(shortcut) {
|
|
5679
|
-
const isMac = typeof navigator !== "undefined" &&
|
|
5680
|
-
navigator.userAgent.toLowerCase().includes("mac");
|
|
5535
|
+
const isMac = typeof navigator !== "undefined" && navigator.userAgent.toLowerCase().includes("mac");
|
|
5681
5536
|
return shortcut.replace(/⌘\/Ctrl/g, isMac ? "⌘" : "Ctrl");
|
|
5682
5537
|
}
|
|
5683
|
-
function ContextMenuButton({ label, onClick, disabled = false, shortcut
|
|
5684
|
-
return (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between", onClick: onClick, disabled: disabled, children: [jsx("span", { children: label }), shortcut &&
|
|
5538
|
+
function ContextMenuButton({ label, onClick, disabled = false, shortcut }) {
|
|
5539
|
+
return (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between", onClick: onClick, disabled: disabled, children: [jsx("span", { children: label }), shortcut && jsx("span", { className: "text-gray-400 text-xs ml-4", children: formatShortcut(shortcut) })] }));
|
|
5685
5540
|
}
|
|
5686
5541
|
|
|
5687
5542
|
function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, keyboardShortcuts = {
|
|
@@ -5694,9 +5549,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, keyb
|
|
|
5694
5549
|
const [query, setQuery] = useState("");
|
|
5695
5550
|
const [hasPasteData, setHasPasteData] = useState(false);
|
|
5696
5551
|
const q = query.trim().toLowerCase();
|
|
5697
|
-
const filteredIds = q
|
|
5698
|
-
? nodeIds.filter((id) => id.toLowerCase().includes(q))
|
|
5699
|
-
: nodeIds;
|
|
5552
|
+
const filteredIds = q ? nodeIds.filter((id) => id.toLowerCase().includes(q)) : nodeIds;
|
|
5700
5553
|
const canUndo = handlers.canUndo ?? false;
|
|
5701
5554
|
const canRedo = handlers.canRedo ?? false;
|
|
5702
5555
|
useEffect(() => {
|
|
@@ -5758,8 +5611,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, keyb
|
|
|
5758
5611
|
// Clamp menu position to viewport
|
|
5759
5612
|
const MENU_MIN_WIDTH = 180;
|
|
5760
5613
|
const PADDING = 16; // rough padding/shadow
|
|
5761
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
5762
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
5614
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) - (MENU_MIN_WIDTH + PADDING));
|
|
5763
5615
|
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
5764
5616
|
const handleClick = (typeId) => {
|
|
5765
5617
|
// project() is deprecated; use screenToFlowPosition for screen coordinates
|
|
@@ -5791,10 +5643,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, keyb
|
|
|
5791
5643
|
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
5792
5644
|
e.preventDefault();
|
|
5793
5645
|
e.stopPropagation();
|
|
5794
|
-
}, children: [hasPasteData && handlers.onPaste && (jsx(ContextMenuButton, { label: "Paste", onClick: handlePaste, shortcut: keyboardShortcuts.paste })), (handlers.onUndo || handlers.onRedo || handlers.onSelectAll) && (jsxs(Fragment, { children: [hasPasteData && handlers.onPaste &&
|
|
5795
|
-
handlers.onPaste &&
|
|
5796
|
-
!handlers.onUndo &&
|
|
5797
|
-
!handlers.onRedo && jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 rounded px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
5646
|
+
}, children: [hasPasteData && handlers.onPaste && (jsx(ContextMenuButton, { label: "Paste", onClick: handlePaste, shortcut: keyboardShortcuts.paste })), (handlers.onUndo || handlers.onRedo || handlers.onSelectAll) && (jsxs(Fragment, { children: [hasPasteData && handlers.onPaste && jsx("div", { className: "h-px bg-gray-200 my-1" }), handlers.onSelectAll && (jsx(ContextMenuButton, { label: "Select All", onClick: handlers.onSelectAll, shortcut: keyboardShortcuts.selectAll })), handlers.onSelectAll && (handlers.onUndo || handlers.onRedo) && jsx("div", { className: "h-px bg-gray-200 my-1" }), handlers.onUndo && (jsx(ContextMenuButton, { label: "Undo", onClick: handlers.onUndo, disabled: !canUndo, shortcut: keyboardShortcuts.undo })), handlers.onRedo && (jsx(ContextMenuButton, { label: "Redo", onClick: handlers.onRedo, disabled: !canRedo, shortcut: keyboardShortcuts.redo })), (handlers.onUndo || handlers.onRedo) && jsx("div", { className: "h-px bg-gray-200 my-1" })] })), hasPasteData && handlers.onPaste && !handlers.onUndo && !handlers.onRedo && (jsx("div", { className: "h-px bg-gray-200 my-1" })), jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 rounded px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? renderTree(root) : jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" }) })] }));
|
|
5798
5647
|
}
|
|
5799
5648
|
|
|
5800
5649
|
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, wb, keyboardShortcuts = {
|
|
@@ -5831,18 +5680,13 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
|
|
|
5831
5680
|
if (!open || !clientPos || !nodeId)
|
|
5832
5681
|
return null;
|
|
5833
5682
|
// Determine if this is a start node (no inbound edges)
|
|
5834
|
-
const isStartNode = wb
|
|
5835
|
-
? !wb.def.edges.some((e) => e.target.nodeId === nodeId)
|
|
5836
|
-
: false;
|
|
5683
|
+
const isStartNode = wb ? !wb.def.edges.some((e) => e.target.nodeId === nodeId) : false;
|
|
5837
5684
|
// Check if node has outbound edges (required for "Run workflow" / "Run from here")
|
|
5838
|
-
const hasOutboundEdges = wb
|
|
5839
|
-
? wb.def.edges.some((e) => e.source.nodeId === nodeId)
|
|
5840
|
-
: false;
|
|
5685
|
+
const hasOutboundEdges = wb ? wb.def.edges.some((e) => e.source.nodeId === nodeId) : false;
|
|
5841
5686
|
// clamp
|
|
5842
5687
|
const MENU_MIN_WIDTH = 180;
|
|
5843
5688
|
const PADDING = 16;
|
|
5844
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
5845
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
5689
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) - (MENU_MIN_WIDTH + PADDING));
|
|
5846
5690
|
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
5847
5691
|
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
5848
5692
|
e.preventDefault();
|
|
@@ -5886,8 +5730,7 @@ function SelectionContextMenu({ open, clientPos, handlers, keyboardShortcuts = {
|
|
|
5886
5730
|
// Clamp menu position to viewport
|
|
5887
5731
|
const MENU_MIN_WIDTH = 180;
|
|
5888
5732
|
const PADDING = 16;
|
|
5889
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
5890
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
5733
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) - (MENU_MIN_WIDTH + PADDING));
|
|
5891
5734
|
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 100);
|
|
5892
5735
|
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
5893
5736
|
e.preventDefault();
|
|
@@ -5895,7 +5738,7 @@ function SelectionContextMenu({ open, clientPos, handlers, keyboardShortcuts = {
|
|
|
5895
5738
|
}, children: [jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Selection" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy }), handlers.onDuplicate && (jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate })), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete })] }));
|
|
5896
5739
|
}
|
|
5897
5740
|
|
|
5898
|
-
function KeyboardShortcutToast({ message, duration = 1000, onClose
|
|
5741
|
+
function KeyboardShortcutToast({ message, duration = 1000, onClose }) {
|
|
5899
5742
|
const [isVisible, setIsVisible] = useState(true);
|
|
5900
5743
|
const onCloseRef = useRef(onClose);
|
|
5901
5744
|
const fadeOutTimerRef = useRef(null);
|
|
@@ -5952,7 +5795,7 @@ const SelectionActiveSync = ({ selection }) => {
|
|
|
5952
5795
|
};
|
|
5953
5796
|
|
|
5954
5797
|
const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
5955
|
-
const { showValues, toString, toElement, getDefaultNodeSize, reactFlowProps, children
|
|
5798
|
+
const { showValues, toString, toElement, getDefaultNodeSize, reactFlowProps, children } = props;
|
|
5956
5799
|
const { wb, handlesMap, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
|
|
5957
5800
|
const nodeValidation = validationByNode;
|
|
5958
5801
|
const edgeValidation = validationByEdge.errors;
|
|
@@ -6036,7 +5879,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6036
5879
|
}
|
|
6037
5880
|
},
|
|
6038
5881
|
}), []);
|
|
6039
|
-
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete
|
|
5882
|
+
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete } = useWorkbenchBridge(wb, handlesMap);
|
|
6040
5883
|
const ui = useMemo(() => wb.getUI(), [wb, uiVersion]);
|
|
6041
5884
|
const { nodeTypes, resolveNodeType } = useMemo(() => {
|
|
6042
5885
|
// Build nodeTypes map using UI extension registry
|
|
@@ -6052,7 +5895,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6052
5895
|
for (const [typeId, comp] of custom.entries()) {
|
|
6053
5896
|
types[`spark-${typeId}`] = comp;
|
|
6054
5897
|
}
|
|
6055
|
-
const resolver = (nodeTypeId) => custom.has(nodeTypeId) ? `spark-${nodeTypeId}` : "spark-default";
|
|
5898
|
+
const resolver = (nodeTypeId) => (custom.has(nodeTypeId) ? `spark-${nodeTypeId}` : "spark-default");
|
|
6056
5899
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
6057
5900
|
// Include uiVersion to recompute when custom renderers are registered
|
|
6058
5901
|
// Include registryVersion to recompute when registry enums/types change
|
|
@@ -6097,12 +5940,8 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6097
5940
|
try {
|
|
6098
5941
|
const prevNodeIds = new Set(prevNodesRef.current.map((n) => n.id));
|
|
6099
5942
|
const nextNodeIds = new Set(out.nodes.map((n) => n.id));
|
|
6100
|
-
const addedNodeIds = out.nodes
|
|
6101
|
-
|
|
6102
|
-
.map((n) => n.id);
|
|
6103
|
-
const removedNodeIds = prevNodesRef.current
|
|
6104
|
-
.filter((n) => !nextNodeIds.has(n.id))
|
|
6105
|
-
.map((n) => n.id);
|
|
5943
|
+
const addedNodeIds = out.nodes.filter((n) => !prevNodeIds.has(n.id)).map((n) => n.id);
|
|
5944
|
+
const removedNodeIds = prevNodesRef.current.filter((n) => !nextNodeIds.has(n.id)).map((n) => n.id);
|
|
6106
5945
|
const prevNodeMap = new Map(prevNodesRef.current.map((n) => [n.id, n]));
|
|
6107
5946
|
const changedNodeIds = out.nodes
|
|
6108
5947
|
.filter((n) => {
|
|
@@ -6112,9 +5951,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6112
5951
|
.map((n) => n.id);
|
|
6113
5952
|
// Detect handle updates (ids/length changes) for targeted debug
|
|
6114
5953
|
const toIds = (arr) => Array.isArray(arr)
|
|
6115
|
-
? arr
|
|
6116
|
-
.map((h) => h && typeof h === "object" && "id" in h ? String(h.id) : "")
|
|
6117
|
-
.filter(Boolean)
|
|
5954
|
+
? arr.map((h) => (h && typeof h === "object" && "id" in h ? String(h.id) : "")).filter(Boolean)
|
|
6118
5955
|
: [];
|
|
6119
5956
|
const handlesEqual = (a, b) => {
|
|
6120
5957
|
const aIds = toIds(a);
|
|
@@ -6139,12 +5976,8 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6139
5976
|
.map((n) => n.id);
|
|
6140
5977
|
const prevEdgeIds = new Set(prevEdgesRef.current.map((e) => e.id));
|
|
6141
5978
|
const nextEdgeIds = new Set(out.edges.map((e) => e.id));
|
|
6142
|
-
const addedEdgeIds = out.edges
|
|
6143
|
-
|
|
6144
|
-
.map((e) => e.id);
|
|
6145
|
-
const removedEdgeIds = prevEdgesRef.current
|
|
6146
|
-
.filter((e) => !nextEdgeIds.has(e.id))
|
|
6147
|
-
.map((e) => e.id);
|
|
5979
|
+
const addedEdgeIds = out.edges.filter((e) => !prevEdgeIds.has(e.id)).map((e) => e.id);
|
|
5980
|
+
const removedEdgeIds = prevEdgesRef.current.filter((e) => !nextEdgeIds.has(e.id)).map((e) => e.id);
|
|
6148
5981
|
const prevEdgeMap = new Map(prevEdgesRef.current.map((e) => [e.id, e]));
|
|
6149
5982
|
const changedEdgeIds = out.edges
|
|
6150
5983
|
.filter((e) => {
|
|
@@ -6152,10 +5985,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6152
5985
|
return p ? !isSameEdge(p, e) : false;
|
|
6153
5986
|
})
|
|
6154
5987
|
.map((e) => e.id);
|
|
6155
|
-
if (addedNodeIds.length ||
|
|
6156
|
-
removedNodeIds.length ||
|
|
6157
|
-
changedNodeIds.length ||
|
|
6158
|
-
handleChanged.length) {
|
|
5988
|
+
if (addedNodeIds.length || removedNodeIds.length || changedNodeIds.length || handleChanged.length) {
|
|
6159
5989
|
// eslint-disable-next-line no-console
|
|
6160
5990
|
console.debug("[WorkbenchCanvas] node updates", {
|
|
6161
5991
|
added: addedNodeIds,
|
|
@@ -6164,9 +5994,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6164
5994
|
handleChanged,
|
|
6165
5995
|
});
|
|
6166
5996
|
}
|
|
6167
|
-
if (addedEdgeIds.length ||
|
|
6168
|
-
removedEdgeIds.length ||
|
|
6169
|
-
changedEdgeIds.length) {
|
|
5997
|
+
if (addedEdgeIds.length || removedEdgeIds.length || changedEdgeIds.length) {
|
|
6170
5998
|
// eslint-disable-next-line no-console
|
|
6171
5999
|
console.debug("[WorkbenchCanvas] edge updates", {
|
|
6172
6000
|
added: addedEdgeIds,
|
|
@@ -6290,10 +6118,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6290
6118
|
const selectionBounds = getSelectionScreenBounds();
|
|
6291
6119
|
if (selectionBounds && hasMultipleNodesSelected) {
|
|
6292
6120
|
const { left, top, right, bottom } = selectionBounds;
|
|
6293
|
-
if (e.clientX >= left &&
|
|
6294
|
-
e.clientX <= right &&
|
|
6295
|
-
e.clientY >= top &&
|
|
6296
|
-
e.clientY <= bottom) {
|
|
6121
|
+
if (e.clientX >= left && e.clientX <= right && e.clientY >= top && e.clientY <= bottom) {
|
|
6297
6122
|
// If only one node is selected (even with edges), show node menu for empty space clicks
|
|
6298
6123
|
if (isSingleNodeSelected) {
|
|
6299
6124
|
const nodeId = selection.nodes[0];
|
|
@@ -6367,7 +6192,9 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6367
6192
|
}, runner);
|
|
6368
6193
|
if (overrides?.getSelectionContextMenuHandlers) {
|
|
6369
6194
|
const selection = wb.getSelection();
|
|
6370
|
-
return overrides.getSelectionContextMenuHandlers(wb, selection, baseHandlers, {
|
|
6195
|
+
return overrides.getSelectionContextMenuHandlers(wb, selection, baseHandlers, {
|
|
6196
|
+
getDefaultNodeSize: overrides.getDefaultNodeSize,
|
|
6197
|
+
});
|
|
6371
6198
|
}
|
|
6372
6199
|
return baseHandlers;
|
|
6373
6200
|
}, [wb, runner, overrides, onCloseSelectionMenu]);
|
|
@@ -6437,20 +6264,15 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6437
6264
|
const handleKeyDown = async (e) => {
|
|
6438
6265
|
// Check if target is inside WorkbenchCanvas container
|
|
6439
6266
|
const target = e.target;
|
|
6440
|
-
if (!containerRef.current ||
|
|
6441
|
-
!(containerRef.current.contains(target) ||
|
|
6442
|
-
containerRef.current == target)) {
|
|
6267
|
+
if (!containerRef.current || !(containerRef.current.contains(target) || containerRef.current == target)) {
|
|
6443
6268
|
return;
|
|
6444
6269
|
}
|
|
6445
6270
|
// Ignore if typing in input/textarea
|
|
6446
|
-
if (target.tagName === "INPUT" ||
|
|
6447
|
-
target.tagName === "TEXTAREA" ||
|
|
6448
|
-
target.isContentEditable) {
|
|
6271
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
|
|
6449
6272
|
return;
|
|
6450
6273
|
}
|
|
6451
6274
|
// Detect Mac platform using userAgent (navigator.platform is deprecated)
|
|
6452
|
-
const isMac = typeof navigator !== "undefined" &&
|
|
6453
|
-
navigator.userAgent.toLowerCase().includes("mac");
|
|
6275
|
+
const isMac = typeof navigator !== "undefined" && navigator.userAgent.toLowerCase().includes("mac");
|
|
6454
6276
|
const modKey = isMac ? e.metaKey : e.ctrlKey;
|
|
6455
6277
|
const key = e.key.toLowerCase();
|
|
6456
6278
|
// Undo: Cmd/Ctrl + Z
|
|
@@ -6511,16 +6333,14 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6511
6333
|
if (!isShortcutEnabled("duplicate"))
|
|
6512
6334
|
return;
|
|
6513
6335
|
const selection = wb.getSelection();
|
|
6514
|
-
if (selection.nodes.length === 1 &&
|
|
6515
|
-
nodeContextMenuHandlers?.onDuplicate) {
|
|
6336
|
+
if (selection.nodes.length === 1 && nodeContextMenuHandlers?.onDuplicate) {
|
|
6516
6337
|
// Single node selected - use node context menu handler
|
|
6517
6338
|
e.preventDefault();
|
|
6518
6339
|
const modKeyLabel = isMac ? "⌘" : "Ctrl";
|
|
6519
6340
|
showToast(`Duplicate (${modKeyLabel} + E)`);
|
|
6520
6341
|
nodeContextMenuHandlers.onDuplicate();
|
|
6521
6342
|
}
|
|
6522
|
-
else if (selection.nodes.length > 1 &&
|
|
6523
|
-
selectionContextMenuHandlers.onDuplicate) {
|
|
6343
|
+
else if (selection.nodes.length > 1 && selectionContextMenuHandlers.onDuplicate) {
|
|
6524
6344
|
// Multiple nodes selected - use selection context menu handler
|
|
6525
6345
|
e.preventDefault();
|
|
6526
6346
|
const modKeyLabel = isMac ? "⌘" : "Ctrl";
|
|
@@ -6601,9 +6421,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6601
6421
|
// Sync viewport when workbench fires graphUiChanged with viewport event
|
|
6602
6422
|
useEffect(() => {
|
|
6603
6423
|
const off = wb.on("graphUiChanged", (event) => {
|
|
6604
|
-
if (event.change?.type === "viewport" &&
|
|
6605
|
-
rfInstanceRef.current &&
|
|
6606
|
-
event.init) {
|
|
6424
|
+
if (event.change?.type === "viewport" && rfInstanceRef.current && event.init) {
|
|
6607
6425
|
const viewport = wb.getViewport();
|
|
6608
6426
|
if (viewport) {
|
|
6609
6427
|
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
@@ -6626,36 +6444,33 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
|
|
|
6626
6444
|
(DefaultContextMenuRenderer ? (jsx(DefaultContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, keyboardShortcuts: keyboardShortcuts })) : (jsx(DefaultContextMenu, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, keyboardShortcuts: keyboardShortcuts }))), menuState?.type === "node" &&
|
|
6627
6445
|
nodeContextMenuHandlers &&
|
|
6628
6446
|
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, keyboardShortcuts: keyboardShortcuts })) : (jsx(NodeContextMenu, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), menuState?.type === "selection" &&
|
|
6629
|
-
(SelectionContextMenuRenderer ? (jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })) : (jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })))] }), jsx(SelectionActiveSync, { selection: selection })] }), toast &&
|
|
6447
|
+
(SelectionContextMenuRenderer ? (jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })) : (jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })))] }), jsx(SelectionActiveSync, { selection: selection })] }), toast && jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id)] }));
|
|
6630
6448
|
});
|
|
6631
6449
|
const WorkbenchCanvas = WorkbenchCanvasComponent;
|
|
6632
6450
|
|
|
6633
6451
|
function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
6634
|
-
const { wb, registryVersion, runner, selectedNodeId, handlesMap, runAutoLayout, runMode, setRunMode, isRunning
|
|
6452
|
+
const { wb, registryVersion, runner, selectedNodeId, handlesMap, runAutoLayout, runMode, setRunMode, isRunning } = useWorkbenchContext();
|
|
6635
6453
|
const [transportStatus, setTransportStatus] = useState({
|
|
6636
6454
|
state: "local",
|
|
6637
6455
|
});
|
|
6638
6456
|
const selectedNode = wb.def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
6639
6457
|
const effectiveHandles = selectedNode
|
|
6640
|
-
? handlesMap[selectedNode.nodeId] ?? {
|
|
6458
|
+
? (handlesMap[selectedNode.nodeId] ?? {
|
|
6641
6459
|
inputs: {},
|
|
6642
6460
|
outputs: {},
|
|
6643
6461
|
inputDefaults: {},
|
|
6644
|
-
}
|
|
6462
|
+
})
|
|
6645
6463
|
: { inputs: {}, outputs: {}, inputDefaults: {} };
|
|
6646
6464
|
const [exampleState, setExampleState] = useState(example ?? "");
|
|
6647
6465
|
const isGraphRunning = isRunning();
|
|
6648
6466
|
// Render Start/Stop button based on transport and runner state
|
|
6649
6467
|
const renderStartStopButton = useCallback(() => {
|
|
6650
6468
|
// Check if transport is connecting/retrying
|
|
6651
|
-
const isConnecting = transportStatus.state === "connecting" ||
|
|
6652
|
-
transportStatus.state === "retrying";
|
|
6469
|
+
const isConnecting = transportStatus.state === "connecting" || transportStatus.state === "retrying";
|
|
6653
6470
|
// Only allow Start/Stop when transport is connected or local
|
|
6654
6471
|
// For local backend, always allow control (transport state is "local")
|
|
6655
6472
|
// For remote backends, require connection
|
|
6656
|
-
const canControl = transportStatus.state === "connected" ||
|
|
6657
|
-
transportStatus.state === "local" ||
|
|
6658
|
-
backendKind === "local"; // Always allow control for local backend
|
|
6473
|
+
const canControl = transportStatus.state === "connected" || transportStatus.state === "local" || backendKind === "local"; // Always allow control for local backend
|
|
6659
6474
|
if (isConnecting) {
|
|
6660
6475
|
return (jsxs("button", { className: "border rounded px-2 py-1.5 text-gray-500 border-gray-400 flex items-center gap-1 disabled:opacity-50", disabled: true, title: "Connecting to backend...", children: [jsx(ClockClockwiseIcon, { size: 16, className: "animate-spin" }), jsx("span", { className: "font-medium ml-1", children: "Connecting..." })] }));
|
|
6661
6476
|
}
|
|
@@ -6675,9 +6490,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6675
6490
|
const message = err instanceof Error ? err.message : String(err);
|
|
6676
6491
|
alert(message);
|
|
6677
6492
|
}
|
|
6678
|
-
}, disabled: !canControl, title: !canControl
|
|
6679
|
-
? "Waiting for connection"
|
|
6680
|
-
: `Start ${runMode === "manual" ? "manual" : "auto"} mode`, children: [jsx(RocketIcon, { size: 16, weight: "fill" }), jsx("span", { className: "font-medium ml-1", children: "Start" })] }));
|
|
6493
|
+
}, disabled: !canControl, title: !canControl ? "Waiting for connection" : `Start ${runMode === "manual" ? "manual" : "auto"} mode`, children: [jsx(RocketIcon, { size: 16, weight: "fill" }), jsx("span", { className: "font-medium ml-1", children: "Start" })] }));
|
|
6681
6494
|
}, [transportStatus, isGraphRunning, runner, runMode, wb, backendKind]);
|
|
6682
6495
|
const defaultExamples = useMemo(() => [
|
|
6683
6496
|
{
|
|
@@ -6917,11 +6730,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6917
6730
|
try {
|
|
6918
6731
|
const parsed = JSON.parse(String(raw));
|
|
6919
6732
|
if (Array.isArray(parsed)) {
|
|
6920
|
-
value = parsed.map((v) => [
|
|
6921
|
-
Number(v?.[0] ?? 0),
|
|
6922
|
-
Number(v?.[1] ?? 0),
|
|
6923
|
-
Number(v?.[2] ?? 0),
|
|
6924
|
-
]);
|
|
6733
|
+
value = parsed.map((v) => [Number(v?.[0] ?? 0), Number(v?.[1] ?? 0), Number(v?.[2] ?? 0)]);
|
|
6925
6734
|
break;
|
|
6926
6735
|
}
|
|
6927
6736
|
}
|
|
@@ -6951,14 +6760,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6951
6760
|
});
|
|
6952
6761
|
}
|
|
6953
6762
|
return baseSetInput;
|
|
6954
|
-
}, [
|
|
6955
|
-
overrides,
|
|
6956
|
-
baseSetInput,
|
|
6957
|
-
runner,
|
|
6958
|
-
selectedNodeId,
|
|
6959
|
-
wb.registry,
|
|
6960
|
-
registryVersion,
|
|
6961
|
-
]);
|
|
6763
|
+
}, [overrides, baseSetInput, runner, selectedNodeId, wb.registry, registryVersion]);
|
|
6962
6764
|
const baseToString = useCallback((typeId, value) => {
|
|
6963
6765
|
if (value === undefined || value === null)
|
|
6964
6766
|
return "";
|
|
@@ -6969,13 +6771,8 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6969
6771
|
const pre = preformatValueForDisplay(typeId, value, wb.registry);
|
|
6970
6772
|
if (pre !== undefined)
|
|
6971
6773
|
return pre;
|
|
6972
|
-
if (typeof value === "object" &&
|
|
6973
|
-
value
|
|
6974
|
-
"url" in value &&
|
|
6975
|
-
typeof value.url === "string") {
|
|
6976
|
-
const title = "title" in value && typeof value.title === "string"
|
|
6977
|
-
? value.title
|
|
6978
|
-
: "";
|
|
6774
|
+
if (typeof value === "object" && value !== null && "url" in value && typeof value.url === "string") {
|
|
6775
|
+
const title = "title" in value && typeof value.title === "string" ? value.title : "";
|
|
6979
6776
|
const url = String(value.url || "");
|
|
6980
6777
|
// value.ts handles data URL formatting
|
|
6981
6778
|
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
@@ -6988,23 +6785,17 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6988
6785
|
const round4 = (n) => Math.round(Number(n) * 10000) / 10000;
|
|
6989
6786
|
if (typeId === "base.vec3" && Array.isArray(value)) {
|
|
6990
6787
|
const a = value;
|
|
6991
|
-
return [
|
|
6992
|
-
round4(Number(a[0] ?? 0)),
|
|
6993
|
-
round4(Number(a[1] ?? 0)),
|
|
6994
|
-
round4(Number(a[2] ?? 0)),
|
|
6995
|
-
].join(",");
|
|
6788
|
+
return [round4(Number(a[0] ?? 0)), round4(Number(a[1] ?? 0)), round4(Number(a[2] ?? 0))].join(",");
|
|
6996
6789
|
}
|
|
6997
6790
|
const stringifyRounded = (v) => {
|
|
6998
6791
|
try {
|
|
6999
|
-
return JSON.stringify(v, (_k, val) => typeof val === "number" ? round4(val) : val);
|
|
6792
|
+
return JSON.stringify(v, (_k, val) => (typeof val === "number" ? round4(val) : val));
|
|
7000
6793
|
}
|
|
7001
6794
|
catch {
|
|
7002
6795
|
return String(v);
|
|
7003
6796
|
}
|
|
7004
6797
|
};
|
|
7005
|
-
if (typeId?.endsWith("[]") ||
|
|
7006
|
-
Array.isArray(value) ||
|
|
7007
|
-
(typeof value === "object" && value !== null)) {
|
|
6798
|
+
if (typeId?.endsWith("[]") || Array.isArray(value) || (typeof value === "object" && value !== null)) {
|
|
7008
6799
|
return stringifyRounded(value);
|
|
7009
6800
|
}
|
|
7010
6801
|
if (typeof value === "number") {
|
|
@@ -7014,7 +6805,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
7014
6805
|
return String(value);
|
|
7015
6806
|
}, [wb.registry, registryVersion]);
|
|
7016
6807
|
const baseToElement = useCallback((typeId, value) => {
|
|
7017
|
-
return
|
|
6808
|
+
return jsx("span", { className: "ml-1 opacity-60", children: baseToString(typeId, value) });
|
|
7018
6809
|
}, [baseToString]);
|
|
7019
6810
|
const toString = useMemo(() => {
|
|
7020
6811
|
if (overrides?.toString)
|
|
@@ -7028,7 +6819,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
7028
6819
|
return overrides.toElement(baseToElement, { registry: wb.registry });
|
|
7029
6820
|
return baseToElement;
|
|
7030
6821
|
}, [overrides, baseToElement, wb.registry, registryVersion]);
|
|
7031
|
-
return (jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [isGraphRunning ? (jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runMode === "manual" ? "Manual" : "Auto"] })) : (jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" &&
|
|
6822
|
+
return (jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [isGraphRunning ? (jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runMode === "manual" ? "Manual" : "Auto"] })) : (jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && jsx(PlugsConnectedIcon, { size: 14, className: "text-gray-500" }), transportStatus.state === "connecting" && (jsx(ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && jsx(WifiHighIcon, { size: 14, className: "text-green-600" }), transportStatus.state === "disconnected" && jsx(WifiSlashIcon, { size: 14, className: "text-red-600" }), transportStatus.state === "retrying" && (jsx(ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: isGraphRunning, title: isGraphRunning ? "Stop engine before switching example" : undefined, children: [jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: backendKind, onChange: (e) => onBackendKindChange(e.target.value), disabled: isGraphRunning, title: isGraphRunning ? "Stop engine before switching backend" : undefined, children: [jsx("option", { value: "local", children: "Local" }), jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && !!onHttpBaseUrlChange && (jsx("input", { className: "border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && !!onWsUrlChange && (jsx("input", { className: "border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runMode, onChange: async (e) => {
|
|
7032
6823
|
const mode = e.target.value;
|
|
7033
6824
|
if (mode !== runMode) {
|
|
7034
6825
|
await setRunMode(mode);
|