@bian-womp/spark-workbench 0.3.71 → 0.3.73

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.
Files changed (81) hide show
  1. package/lib/cjs/index.cjs +143 -352
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/adapters/cli/index.d.ts.map +1 -1
  4. package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
  5. package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
  6. package/lib/cjs/src/core/contracts.d.ts.map +1 -1
  7. package/lib/cjs/src/core/ui-extensions.d.ts.map +1 -1
  8. package/lib/cjs/src/examples/reactflow/App.d.ts.map +1 -1
  9. package/lib/cjs/src/misc/DebugEvents.d.ts.map +1 -1
  10. package/lib/cjs/src/misc/DefaultEdge.d.ts.map +1 -1
  11. package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
  12. package/lib/cjs/src/misc/DefaultNodeContent.d.ts +1 -1
  13. package/lib/cjs/src/misc/DefaultNodeContent.d.ts.map +1 -1
  14. package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -1
  15. package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
  16. package/lib/cjs/src/misc/IssueBadge.d.ts.map +1 -1
  17. package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts +1 -1
  18. package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts.map +1 -1
  19. package/lib/cjs/src/misc/NodeHandles.d.ts.map +1 -1
  20. package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  21. package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
  22. package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  23. package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  24. package/lib/cjs/src/misc/context-menu/ContextMenuButton.d.ts +1 -1
  25. package/lib/cjs/src/misc/context-menu/ContextMenuButton.d.ts.map +1 -1
  26. package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
  27. package/lib/cjs/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
  28. package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
  29. package/lib/cjs/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
  30. package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
  31. package/lib/cjs/src/misc/layout.d.ts.map +1 -1
  32. package/lib/cjs/src/misc/load.d.ts.map +1 -1
  33. package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
  34. package/lib/cjs/src/misc/merge-utils.d.ts.map +1 -1
  35. package/lib/cjs/src/misc/thumbnail-utils.d.ts.map +1 -1
  36. package/lib/cjs/src/misc/value.d.ts.map +1 -1
  37. package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
  38. package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
  39. package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
  40. package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  41. package/lib/esm/index.js +143 -352
  42. package/lib/esm/index.js.map +1 -1
  43. package/lib/esm/src/adapters/cli/index.d.ts.map +1 -1
  44. package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
  45. package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
  46. package/lib/esm/src/core/contracts.d.ts.map +1 -1
  47. package/lib/esm/src/core/ui-extensions.d.ts.map +1 -1
  48. package/lib/esm/src/examples/reactflow/App.d.ts.map +1 -1
  49. package/lib/esm/src/misc/DebugEvents.d.ts.map +1 -1
  50. package/lib/esm/src/misc/DefaultEdge.d.ts.map +1 -1
  51. package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
  52. package/lib/esm/src/misc/DefaultNodeContent.d.ts +1 -1
  53. package/lib/esm/src/misc/DefaultNodeContent.d.ts.map +1 -1
  54. package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -1
  55. package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
  56. package/lib/esm/src/misc/IssueBadge.d.ts.map +1 -1
  57. package/lib/esm/src/misc/KeyboardShortcutToast.d.ts +1 -1
  58. package/lib/esm/src/misc/KeyboardShortcutToast.d.ts.map +1 -1
  59. package/lib/esm/src/misc/NodeHandles.d.ts.map +1 -1
  60. package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  61. package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
  62. package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  63. package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  64. package/lib/esm/src/misc/context-menu/ContextMenuButton.d.ts +1 -1
  65. package/lib/esm/src/misc/context-menu/ContextMenuButton.d.ts.map +1 -1
  66. package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
  67. package/lib/esm/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
  68. package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
  69. package/lib/esm/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
  70. package/lib/esm/src/misc/hooks.d.ts.map +1 -1
  71. package/lib/esm/src/misc/layout.d.ts.map +1 -1
  72. package/lib/esm/src/misc/load.d.ts.map +1 -1
  73. package/lib/esm/src/misc/mapping.d.ts.map +1 -1
  74. package/lib/esm/src/misc/merge-utils.d.ts.map +1 -1
  75. package/lib/esm/src/misc/thumbnail-utils.d.ts.map +1 -1
  76. package/lib/esm/src/misc/value.d.ts.map +1 -1
  77. package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
  78. package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
  79. package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
  80. package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  81. 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 (selectedNodeSet.has(edge.source.nodeId) &&
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 (NODE_HEADER_HEIGHT_PX +
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 (NODE_HEADER_HEIGHT_PX +
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 = [], } = args;
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 (element.getAttribute("stroke") ||
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 (x + width >= bounds.minX &&
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, } = options;
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
- ? extractNodeHandles(nodeEl, position.x, position.y, dimensions.width)
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", } = options;
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
- (type === "graphChanged" || type === "graphUiChanged")) {
4220
- const changeType = payload
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}`, { runId, event: s });
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}`, { event: s, currentRunIds });
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 (jsxRuntime.jsx(WorkbenchContext.Provider, { value: value, children: children }));
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" ? (jsxRuntime.jsx(react$1.XCircleIcon, { size: size, weight: "fill" })) : (jsxRuntime.jsx(react$1.WarningCircleIcon, { size: size, weight: "fill" })) }));
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 (jsxRuntime.jsx(react.BaseEdge, { id: id, path: edgePath, style: style, markerEnd: markerEnd }));
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
- ? nodeStatus?.[selectedNodeId]
5475
- : undefined;
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
- .map((v) => `${v.code}: ${v.message}`)
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", " ", 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) => {
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
- ? declaredTypes.join(" | ")
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
- .map((v) => `${v.code}: ${v.message}`)
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 && (jsxRuntime.jsx("span", { className: "text-gray-400 text-xs ml-4", children: formatShortcut(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 && (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 &&
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, } = props;
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, } = useWorkbenchBridge(wb, handlesMap);
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
- .filter((n) => !prevNodeIds.has(n.id))
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
- .filter((e) => !prevEdgeIds.has(e.id))
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, { getDefaultNodeSize: overrides.getDefaultNodeSize });
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 && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
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, } = useWorkbenchContext();
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 !== null &&
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 (jsxRuntime.jsx("span", { className: "ml-1 opacity-60", children: baseToString(typeId, value) }));
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" && (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) => {
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);