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