@bian-womp/spark-workbench 0.2.33 → 0.2.35

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 (37) hide show
  1. package/lib/cjs/index.cjs +226 -103
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
  4. package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
  5. package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  6. package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +1 -0
  7. package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  8. package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  9. package/lib/cjs/src/misc/mapping.d.ts +2 -0
  10. package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
  11. package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +4 -2
  12. package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
  13. package/lib/cjs/src/runtime/IGraphRunner.d.ts +3 -2
  14. package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
  15. package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +3 -2
  16. package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
  17. package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +3 -3
  18. package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  19. package/lib/esm/index.js +226 -103
  20. package/lib/esm/index.js.map +1 -1
  21. package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
  22. package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
  23. package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  24. package/lib/esm/src/misc/context/WorkbenchContext.d.ts +1 -0
  25. package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  26. package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  27. package/lib/esm/src/misc/mapping.d.ts +2 -0
  28. package/lib/esm/src/misc/mapping.d.ts.map +1 -1
  29. package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +4 -2
  30. package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
  31. package/lib/esm/src/runtime/IGraphRunner.d.ts +3 -2
  32. package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
  33. package/lib/esm/src/runtime/LocalGraphRunner.d.ts +3 -2
  34. package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
  35. package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +3 -3
  36. package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  37. package/package.json +4 -4
package/lib/cjs/index.cjs CHANGED
@@ -339,6 +339,7 @@ class AbstractGraphRunner {
339
339
  if (this.engine) {
340
340
  throw new Error("Engine already running. Stop the current engine first.");
341
341
  }
342
+ this.currentDef = def;
342
343
  }
343
344
  setInput(nodeId, handle, value) {
344
345
  if (!this.stagedInputs[nodeId])
@@ -395,6 +396,7 @@ class AbstractGraphRunner {
395
396
  this.engine = undefined;
396
397
  this.runtime?.dispose();
397
398
  this.runtime = undefined;
399
+ this.currentDef = undefined;
398
400
  if (this.runningKind) {
399
401
  this.runningKind = undefined;
400
402
  this.emit("status", { running: false, engine: undefined });
@@ -429,12 +431,14 @@ class LocalGraphRunner extends AbstractGraphRunner {
429
431
  this.emit("transport", { state: "local" });
430
432
  }
431
433
  build(def) {
434
+ this.currentDef = def;
432
435
  const builder = new sparkGraph.GraphBuilder(this.registry);
433
436
  this.runtime = builder.build(def);
434
437
  // Signal UI that freshly built graph should be considered invalidated
435
438
  this.emit("invalidate", { reason: "graph-built" });
436
439
  }
437
440
  update(def) {
441
+ this.currentDef = def;
438
442
  if (!this.runtime)
439
443
  return;
440
444
  // Prevent mid-run churn while wiring changes are applied
@@ -499,11 +503,11 @@ class LocalGraphRunner extends AbstractGraphRunner {
499
503
  if (eng instanceof sparkGraph.BatchedEngine)
500
504
  eng.flush();
501
505
  }
502
- getOutputs(def) {
506
+ getOutputs() {
503
507
  const out = {};
504
- if (!this.runtime)
508
+ if (!this.runtime || !this.currentDef)
505
509
  return out;
506
- for (const n of def.nodes) {
510
+ for (const n of this.currentDef.nodes) {
507
511
  const desc = this.registry.nodes.get(n.typeId);
508
512
  const handles = Object.keys(desc?.outputs ?? {});
509
513
  for (const h of handles) {
@@ -517,15 +521,17 @@ class LocalGraphRunner extends AbstractGraphRunner {
517
521
  }
518
522
  return out;
519
523
  }
520
- getInputs(def) {
524
+ getInputs() {
521
525
  const out = {};
522
- for (const n of def.nodes) {
526
+ if (!this.currentDef)
527
+ return out;
528
+ for (const n of this.currentDef.nodes) {
523
529
  const staged = this.stagedInputs[n.nodeId] ?? {};
524
530
  const runtimeInputs = this.runtime
525
531
  ? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
526
532
  : {};
527
533
  // Build inbound handle set for this node from current def
528
- const inbound = new Set(def.edges
534
+ const inbound = new Set(this.currentDef.edges
529
535
  .filter((e) => e.target.nodeId === n.nodeId)
530
536
  .map((e) => e.target.handle));
531
537
  // Merge staged only for non-inbound handles so UI reflects runtime values for wired inputs
@@ -539,26 +545,22 @@ class LocalGraphRunner extends AbstractGraphRunner {
539
545
  }
540
546
  return out;
541
547
  }
548
+ getInputDefaults() {
549
+ const out = {};
550
+ if (!this.currentDef)
551
+ return out;
552
+ for (const n of this.currentDef.nodes) {
553
+ const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
554
+ if (Object.keys(dynDefaults).length > 0) {
555
+ out[n.nodeId] = dynDefaults;
556
+ }
557
+ }
558
+ return out;
559
+ }
542
560
  async snapshotFull() {
543
561
  const def = undefined; // UI will supply def/positions on download for local
544
- const inputs = this.getInputs(this.runtime
545
- ? {
546
- nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
547
- nodeId: id,
548
- typeId: "",
549
- })),
550
- edges: [],
551
- }
552
- : { nodes: [], edges: [] });
553
- const outputs = this.getOutputs(this.runtime
554
- ? {
555
- nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
556
- nodeId: id,
557
- typeId: "",
558
- })),
559
- edges: [],
560
- }
561
- : { nodes: [], edges: [] });
562
+ const inputs = this.getInputs();
563
+ const outputs = this.getOutputs();
562
564
  const environment = this.getEnvironment() || {};
563
565
  return { def, environment, inputs, outputs };
564
566
  }
@@ -567,7 +569,10 @@ class LocalGraphRunner extends AbstractGraphRunner {
567
569
  this.build(payload.def);
568
570
  this.setEnvironment?.(payload.environment || {}, { merge: false });
569
571
  // Hydrate via runtime for exact restore and re-emit
570
- this.runtime?.hydrate({ inputs: payload.inputs || {}, outputs: payload.outputs || {} }, { reemit: true });
572
+ this.runtime?.hydrate({
573
+ inputs: payload.inputs || {},
574
+ outputs: payload.outputs || {},
575
+ });
571
576
  }
572
577
  dispose() {
573
578
  super.dispose();
@@ -632,8 +637,8 @@ class RemoteGraphRunner extends AbstractGraphRunner {
632
637
  this.emit("registry", this.registry);
633
638
  // Trigger update so validation/UI refreshes using last known graph
634
639
  try {
635
- if (this.lastDef)
636
- this.update(this.lastDef);
640
+ if (this.currentDef)
641
+ this.update(this.currentDef);
637
642
  }
638
643
  catch {
639
644
  console.error("Failed to update graph definition after registry changed");
@@ -651,12 +656,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
651
656
  console.warn("Unsupported operation for remote runner");
652
657
  }
653
658
  update(def) {
659
+ this.currentDef = def;
654
660
  // Remote: forward update; ignore errors (fire-and-forget)
655
661
  this.ensureRemoteRunner().then(async (runner) => {
656
662
  try {
657
663
  await runner.update(def);
658
664
  this.emit("invalidate", { reason: "graph-updated" });
659
- this.lastDef = def;
660
665
  }
661
666
  catch { }
662
667
  });
@@ -668,7 +673,6 @@ class RemoteGraphRunner extends AbstractGraphRunner {
668
673
  await runner.build(def);
669
674
  // Signal UI after remote build as well
670
675
  this.emit("invalidate", { reason: "graph-built" });
671
- this.lastDef = def;
672
676
  // Hydrate current remote inputs/outputs (including defaults) into cache
673
677
  try {
674
678
  const snap = await runner.snapshot();
@@ -804,12 +808,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
804
808
  // For now, we expose an async helper on RemoteRunner. Keep sync signature per interface.
805
809
  return undefined;
806
810
  }
807
- getOutputs(def) {
811
+ getOutputs() {
808
812
  const out = {};
809
813
  const cache = this.valueCache;
810
- if (!cache)
814
+ if (!cache || !this.currentDef)
811
815
  return out;
812
- for (const n of def.nodes) {
816
+ for (const n of this.currentDef.nodes) {
813
817
  const resolved = n.resolvedHandles?.outputs;
814
818
  const desc = this.registry.nodes.get(n.typeId);
815
819
  const handles = Object.keys(resolved ?? desc?.outputs ?? {});
@@ -825,17 +829,19 @@ class RemoteGraphRunner extends AbstractGraphRunner {
825
829
  }
826
830
  return out;
827
831
  }
828
- getInputs(def) {
832
+ getInputs() {
829
833
  const out = {};
830
834
  const cache = this.valueCache;
831
- for (const n of def.nodes) {
835
+ if (!this.currentDef)
836
+ return out;
837
+ for (const n of this.currentDef.nodes) {
832
838
  const staged = this.stagedInputs[n.nodeId] ?? {};
833
839
  const resolved = n.resolvedHandles?.inputs;
834
840
  const desc = this.registry.nodes.get(n.typeId);
835
841
  const handles = Object.keys(resolved ?? desc?.inputs ?? {});
836
842
  const cur = {};
837
843
  // Build inbound handle set for this node to honor wiring precedence
838
- const inbound = new Set(def.edges
844
+ const inbound = new Set(this.currentDef.edges
839
845
  .filter((e) => e.target.nodeId === n.nodeId)
840
846
  .map((e) => e.target.handle));
841
847
  for (const h of handles) {
@@ -854,6 +860,18 @@ class RemoteGraphRunner extends AbstractGraphRunner {
854
860
  }
855
861
  return out;
856
862
  }
863
+ getInputDefaults() {
864
+ const out = {};
865
+ if (!this.currentDef)
866
+ return out;
867
+ for (const n of this.currentDef.nodes) {
868
+ const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
869
+ if (Object.keys(dynDefaults).length > 0) {
870
+ out[n.nodeId] = dynDefaults;
871
+ }
872
+ }
873
+ return out;
874
+ }
857
875
  dispose() {
858
876
  super.dispose();
859
877
  this.runner = undefined;
@@ -1605,6 +1623,7 @@ function toReactFlow(def, positions, registry, opts) {
1605
1623
  renderWidth: initialWidth,
1606
1624
  renderHeight: initialHeight,
1607
1625
  inputValues: opts.inputs?.[n.nodeId],
1626
+ inputDefaults: opts.inputDefaults?.[n.nodeId],
1608
1627
  outputValues: opts.outputs?.[n.nodeId],
1609
1628
  status: opts.nodeStatus?.[n.nodeId],
1610
1629
  validation: {
@@ -1629,7 +1648,7 @@ function toReactFlow(def, positions, registry, opts) {
1629
1648
  });
1630
1649
  const edges = def.edges.map((e) => {
1631
1650
  const st = opts.edgeStatus?.[e.id];
1632
- const isRunning = !!st?.activeRuns;
1651
+ const isRunning = (st?.activeRuns || 0) > 0;
1633
1652
  const hasError = !!st?.lastError;
1634
1653
  const isInvalidEdge = !!opts.edgeValidation?.[e.id];
1635
1654
  const sourceMissing = !validHandleMap[e.source.nodeId]?.outputs.has(e.source.handle);
@@ -1666,7 +1685,7 @@ function getNodeBorderClassNames(args) {
1666
1685
  const hasError = !!status.lastError;
1667
1686
  const hasValidationError = issues.some((i) => i.level === "error");
1668
1687
  const hasValidationWarning = !hasValidationError && issues.length > 0;
1669
- const isRunning = !!status.activeRuns;
1688
+ const isRunning = (status.activeRuns || 0) > 0;
1670
1689
  const isInvalid = !!status.invalidated && !isRunning && !hasError;
1671
1690
  // Keep border width constant to avoid layout reflow on selection toggles
1672
1691
  const borderWidth = "border";
@@ -1770,8 +1789,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
1770
1789
  const valuesTick = versionTick + graphTick + graphUiTick;
1771
1790
  // Def and IO values
1772
1791
  const def = wb.export();
1773
- const inputsMap = React.useMemo(() => runner.getInputs(def), [runner, def, valuesTick]);
1774
- const outputsMap = React.useMemo(() => runner.getOutputs(def), [runner, def, valuesTick]);
1792
+ const inputsMap = React.useMemo(() => runner.getInputs(), [runner, valuesTick]);
1793
+ const inputDefaultsMap = React.useMemo(() => runner.getInputDefaults(), [runner, valuesTick]);
1794
+ const outputsMap = React.useMemo(() => runner.getOutputs(), [runner, valuesTick]);
1775
1795
  const outputTypesMap = React.useMemo(() => {
1776
1796
  const out = {};
1777
1797
  // Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
@@ -1934,7 +1954,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
1934
1954
  }
1935
1955
  catch { }
1936
1956
  };
1937
- const off1 = runner.on("value", (e) => {
1957
+ const offRunnerValue = runner.on("value", (e) => {
1938
1958
  if (e?.io === "input") {
1939
1959
  const nodeId = e?.nodeId;
1940
1960
  setNodeStatus((s) => ({
@@ -1944,7 +1964,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
1944
1964
  }
1945
1965
  return add("runner", "value")(e);
1946
1966
  });
1947
- const off2 = runner.on("error", (e) => {
1967
+ const offRunnerError = runner.on("error", (e) => {
1948
1968
  const edgeError = e;
1949
1969
  const nodeError = e;
1950
1970
  const registryError = e;
@@ -1997,14 +2017,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
1997
2017
  }
1998
2018
  return add("runner", "error")(e);
1999
2019
  });
2000
- const off3 = runner.on("invalidate", (e) => {
2020
+ const offRunnerInvalidate = runner.on("invalidate", (e) => {
2001
2021
  // After build/update, pull resolved handles and merge in-place (no graphChanged)
2002
2022
  if (e?.reason === "graph-updated" || e?.reason === "graph-built") {
2003
2023
  refreshResolvedHandles();
2004
2024
  }
2005
2025
  return add("runner", "invalidate")(e);
2006
2026
  });
2007
- const off3b = runner.on("stats", (s) => {
2027
+ const offRunnerStats = runner.on("stats", (s) => {
2008
2028
  if (!s)
2009
2029
  return;
2010
2030
  if (s.kind === "node-start") {
@@ -2051,8 +2071,12 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2051
2071
  const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
2052
2072
  setNodeStatus((prev) => {
2053
2073
  const current = prev[id]?.activeRuns ?? 0;
2054
- const nextActive = current - 1;
2055
2074
  const currentRunIds = prev[id]?.activeRunIds ?? [];
2075
+ if (isValidRunId && !currentRunIds.includes(runId)) {
2076
+ console.warn(`[WorkbenchContext] node-done event for unknown runId: node=${id} runId=${runId}`, { event: s, currentRunIds });
2077
+ return prev; // Ignore stale event
2078
+ }
2079
+ const nextActive = Math.max(0, current - 1); // Prevent negative values
2056
2080
  const nextRunIds = isValidRunId
2057
2081
  ? currentRunIds.filter((rid) => rid !== runId)
2058
2082
  : currentRunIds;
@@ -2097,15 +2121,17 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2097
2121
  const current = prev[id]?.activeRuns ?? 0;
2098
2122
  return {
2099
2123
  ...prev,
2100
- [id]: { ...prev[id], activeRuns: current - 1 },
2124
+ [id]: { ...prev[id], activeRuns: Math.max(0, current - 1) }, // Prevent negative values
2101
2125
  };
2102
2126
  });
2103
2127
  }
2104
2128
  return add("runner", "stats")(s);
2105
2129
  });
2106
- const off4 = wb.on("graphChanged", add("workbench", "graphChanged"));
2130
+ const offWbGraphChanged = wb.on("graphChanged", add("workbench", "graphChanged"));
2131
+ const offWbGraphUiChanged = wb.on("graphUiChanged", add("workbench", "graphUiChanged"));
2132
+ const offWbValidationChanged = wb.on("validationChanged", add("workbench", "validationChanged"));
2107
2133
  // Ensure newly added nodes start as invalidated until first evaluation
2108
- const off4c = wb.on("graphChanged", (e) => {
2134
+ const offWbAddNode = wb.on("graphChanged", (e) => {
2109
2135
  const change = e.change;
2110
2136
  if (change?.type === "addNode" && typeof change.nodeId === "string") {
2111
2137
  const id = change.nodeId;
@@ -2115,17 +2141,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2115
2141
  }));
2116
2142
  }
2117
2143
  });
2118
- const off4b = wb.on("graphUiChanged", add("workbench", "graphUiChanged"));
2119
- const off5 = wb.on("validationChanged", add("workbench", "validationChanged"));
2120
- const off5b = wb.on("validationChanged", (r) => setValidation(r));
2121
- const off6 = wb.on("selectionChanged", (sel) => {
2144
+ const offWbdSetValidation = wb.on("validationChanged", (r) => setValidation(r));
2145
+ const offWbSelectionChanged = wb.on("selectionChanged", (sel) => {
2122
2146
  setSelectedNodeId(sel.nodes?.[0]);
2123
2147
  setSelectedEdgeId(sel.edges?.[0]);
2124
2148
  });
2125
- const off7 = wb.on("error", add("workbench", "error"));
2126
- wb.refreshValidation();
2149
+ const offWbError = wb.on("error", add("workbench", "error"));
2127
2150
  // Registry updates: swap registry and refresh graph validation/UI
2128
- const offReg = runner.on("registry", (newReg) => {
2151
+ const offRunnerRegistry = runner.on("registry", (newReg) => {
2129
2152
  try {
2130
2153
  setRegistry(newReg);
2131
2154
  wb.setRegistry(newReg);
@@ -2141,29 +2164,40 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2141
2164
  console.error("Failed to handle registry changed event");
2142
2165
  }
2143
2166
  });
2167
+ // Handle transport disconnect: reset runtime status when connection is lost
2168
+ const offRunnerTransport = runner.on("transport", (t) => {
2169
+ if (t.state === "disconnected") {
2170
+ console.info("[WorkbenchContext] Transport disconnected, resetting node status");
2171
+ setNodeStatus({});
2172
+ setEdgeStatus({});
2173
+ setFallbackStarts({});
2174
+ errorRunsRef.current = {};
2175
+ }
2176
+ });
2177
+ wb.refreshValidation();
2144
2178
  return () => {
2145
- off1();
2146
- off2();
2147
- off3();
2148
- off3b();
2149
- off4();
2150
- off4b();
2151
- off4c();
2152
- off5();
2153
- off5b();
2154
- off6();
2155
- off7();
2156
- offReg();
2179
+ offRunnerValue();
2180
+ offRunnerError();
2181
+ offRunnerInvalidate();
2182
+ offRunnerStats();
2183
+ offWbGraphChanged();
2184
+ offWbGraphUiChanged();
2185
+ offWbValidationChanged();
2186
+ offWbError();
2187
+ offWbAddNode();
2188
+ offWbdSetValidation();
2189
+ offWbSelectionChanged();
2190
+ offRunnerRegistry();
2191
+ offRunnerTransport();
2157
2192
  };
2158
2193
  }, [runner, wb]);
2159
- // Push incremental updates into running engine without full reload
2194
+ // Keep runner.currentDef in sync with the workbench graph at all times.
2195
+ // When an engine/runtime exists, this also pushes incremental wiring changes into it.
2160
2196
  React.useEffect(() => {
2161
- if (runner.isRunning()) {
2162
- try {
2163
- runner.update(def);
2164
- }
2165
- catch { }
2197
+ try {
2198
+ runner.update(def);
2166
2199
  }
2200
+ catch { }
2167
2201
  }, [runner, def, graphTick]);
2168
2202
  const validationByNode = React.useMemo(() => {
2169
2203
  const inputs = {};
@@ -2252,6 +2286,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2252
2286
  edgeStatus,
2253
2287
  valuesTick,
2254
2288
  inputsMap,
2289
+ inputDefaultsMap,
2255
2290
  outputsMap,
2256
2291
  outputTypesMap,
2257
2292
  validationByNode,
@@ -2294,6 +2329,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2294
2329
  removeSystemError,
2295
2330
  removeRegistryError,
2296
2331
  inputsMap,
2332
+ inputDefaultsMap,
2297
2333
  outputsMap,
2298
2334
  validationByNode,
2299
2335
  validationByEdge,
@@ -2379,7 +2415,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
2379
2415
  return String(value ?? "");
2380
2416
  }
2381
2417
  };
2382
- const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, clearSystemErrors, clearRegistryErrors, removeSystemError, removeRegistryError, } = useWorkbenchContext();
2418
+ const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, clearSystemErrors, clearRegistryErrors, removeSystemError, removeRegistryError, } = useWorkbenchContext();
2383
2419
  const nodeValidationIssues = validationByNode.issues;
2384
2420
  const edgeValidationIssues = validationByEdge.issues;
2385
2421
  const nodeValidationHandles = validationByNode;
@@ -2394,8 +2430,33 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
2394
2430
  .filter(([k]) => !sparkGraph.isInputPrivate(effectiveHandles.inputs, k))
2395
2431
  .map(([k]) => k);
2396
2432
  const outputHandles = Object.keys(effectiveHandles.outputs);
2397
- const nodeInputs = selectedNodeId ? inputsMap[selectedNodeId] ?? {} : {};
2433
+ const nodeInputsRaw = selectedNodeId ? inputsMap[selectedNodeId] ?? {} : {};
2434
+ const nodeInputsDefaults = selectedNodeId
2435
+ ? inputDefaultsMap[selectedNodeId] ?? {}
2436
+ : {};
2437
+ // Keep defaults separate for placeholder use (don't merge into nodeInputs)
2438
+ const nodeInputs = nodeInputsRaw;
2398
2439
  const nodeOutputs = selectedNodeId ? outputsMap[selectedNodeId] ?? {} : {};
2440
+ // Helper to truncate long values
2441
+ const truncateValue = (str, maxLen = 50) => {
2442
+ if (str.length <= maxLen)
2443
+ return str;
2444
+ const start = Math.floor(maxLen / 2) - 5;
2445
+ const end = str.length - Math.floor(maxLen / 2) + 5;
2446
+ return `${str.slice(0, start)}...${str.slice(end)}`;
2447
+ };
2448
+ // Helper to copy to clipboard
2449
+ const copyToClipboard = (text) => {
2450
+ navigator.clipboard.writeText(text).catch(() => {
2451
+ // Fallback for older browsers
2452
+ const textarea = document.createElement("textarea");
2453
+ textarea.value = text;
2454
+ document.body.appendChild(textarea);
2455
+ textarea.select();
2456
+ document.execCommand("copy");
2457
+ document.body.removeChild(textarea);
2458
+ });
2459
+ };
2399
2460
  const selectedNodeStatus = selectedNodeId
2400
2461
  ? nodeStatus?.[selectedNodeId]
2401
2462
  : undefined;
@@ -2481,19 +2542,30 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
2481
2542
  }, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(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) => {
2482
2543
  e.stopPropagation();
2483
2544
  deleteEdgeById(selectedEdge.id);
2484
- }, 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 &&
2545
+ }, 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 &&
2485
2546
  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 &&
2486
2547
  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 ??
2487
2548
  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) => {
2488
2549
  const typeId = sparkGraph.getInputTypeId(effectiveHandles.inputs, h);
2489
2550
  const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
2490
2551
  e.target.handle === h);
2552
+ const inbound = new Set(def.edges
2553
+ .filter((e) => e.target.nodeId === selectedNodeId &&
2554
+ e.target.handle === h)
2555
+ .map((e) => e.target.handle));
2556
+ const hasDefault = !inbound.has(h) && nodeInputsDefaults[h] !== undefined;
2557
+ const defaultStr = hasDefault
2558
+ ? safeToString(typeId, nodeInputsDefaults[h])
2559
+ : undefined;
2491
2560
  const commonProps = {
2492
2561
  style: { flex: 1 },
2493
2562
  disabled: isLinked,
2494
2563
  };
2495
2564
  const current = nodeInputs[h];
2565
+ const hasValue = current !== undefined && current !== null;
2496
2566
  const value = drafts[h] ?? safeToString(typeId, current);
2567
+ const displayValue = hasValue ? value : "";
2568
+ const placeholder = hasDefault ? defaultStr : undefined;
2497
2569
  const onChangeText = (text) => setDrafts((d) => ({ ...d, [h]: text }));
2498
2570
  const commit = () => {
2499
2571
  const draft = drafts[h];
@@ -2506,6 +2578,11 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
2506
2578
  const orig = originals[h] ?? safeToString(typeId, current);
2507
2579
  setDrafts((d) => ({ ...d, [h]: orig }));
2508
2580
  };
2581
+ const clearInput = () => {
2582
+ setInput(h, undefined);
2583
+ setDrafts((d) => ({ ...d, [h]: "" }));
2584
+ setOriginals((o) => ({ ...o, [h]: "" }));
2585
+ };
2509
2586
  const isEnum = typeId?.startsWith("enum:");
2510
2587
  const inIssues = selectedNodeHandleValidation.inputs.filter((m) => m.handle === h);
2511
2588
  const hasValidation = inIssues.length > 0;
@@ -2513,25 +2590,43 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
2513
2590
  const title = inIssues
2514
2591
  .map((v) => `${v.code}: ${v.message}`)
2515
2592
  .join("; ");
2516
- 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: typeId })] }), hasValidation && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (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: current !== undefined && current !== null
2517
- ? String(current)
2518
- : "", onChange: (e) => {
2519
- const val = e.target.value;
2520
- const raw = val === "" ? undefined : Number(val);
2521
- setInput(h, raw);
2522
- // keep drafts/originals in sync with label for display elsewhere
2523
- const display = safeToString(typeId, raw);
2524
- setDrafts((d) => ({ ...d, [h]: display }));
2525
- setOriginals((o) => ({ ...o, [h]: display }));
2526
- }, ...commonProps, children: [jsxRuntime.jsx("option", { value: "", children: "(select)" }), registry.enums.get(typeId)?.options.map((opt) => (jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] })) : isLinked ? (toElement(typeId, current)) : (jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", placeholder: isLinked ? "wired" : undefined, value: value, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
2527
- if (e.key === "Enter")
2528
- commit();
2529
- if (e.key === "Escape")
2530
- revert();
2531
- }, ...commonProps }))] }, h));
2532
- }))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: prettyHandle(h) }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: outputTypesMap[selectedNodeId]?.[h] ?? "" })] }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
2593
+ 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: typeId })] }), 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", value: current !== undefined && current !== null
2594
+ ? String(current)
2595
+ : "", onChange: (e) => {
2596
+ const val = e.target.value;
2597
+ const raw = val === "" ? undefined : Number(val);
2598
+ setInput(h, raw);
2599
+ // keep drafts/originals in sync with label for display elsewhere
2600
+ const display = safeToString(typeId, raw);
2601
+ setDrafts((d) => ({ ...d, [h]: display }));
2602
+ setOriginals((o) => ({ ...o, [h]: display }));
2603
+ }, ...commonProps, children: [jsxRuntime.jsx("option", { value: "", children: placeholder
2604
+ ? `Default: ${placeholder}`
2605
+ : "(select)" }), registry.enums
2606
+ .get(typeId)
2607
+ ?.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: (() => {
2608
+ const displayStr = safeToString(typeId, current);
2609
+ const isLong = displayStr.length > 50;
2610
+ const truncated = isLong
2611
+ ? truncateValue(displayStr)
2612
+ : displayStr;
2613
+ return (jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("span", { className: "truncate", children: truncated }), isLong && (jsxRuntime.jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded", onClick: () => copyToClipboard(displayStr), title: "Copy full value", children: jsxRuntime.jsx(react$1.CopyIcon, { size: 14 }) }))] }));
2614
+ })() }) })) : (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", placeholder: placeholder
2615
+ ? `Default: ${placeholder}`
2616
+ : undefined, value: displayValue, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
2617
+ if (e.key === "Enter")
2618
+ commit();
2619
+ if (e.key === "Escape")
2620
+ revert();
2621
+ }, ...commonProps }), 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 }) }))] }))] }, h));
2622
+ }))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: prettyHandle(h) }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: outputTypesMap[selectedNodeId]?.[h] ?? "" })] }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: (() => {
2533
2623
  const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], effectiveHandles.outputs[h]);
2534
- return toElement(typeId, value);
2624
+ const displayStr = safeToString(typeId, value);
2625
+ const isLong = displayStr.length > 50;
2626
+ const truncated = isLong
2627
+ ? truncateValue(displayStr)
2628
+ : displayStr;
2629
+ return (jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("span", { className: "truncate", children: truncated }), isLong && (jsxRuntime.jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded", onClick: () => copyToClipboard(displayStr), title: "Copy full value", children: jsxRuntime.jsx(react$1.CopyIcon, { size: 14 }) }))] }));
2535
2630
  })() }), (() => {
2536
2631
  const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
2537
2632
  if (outIssues.length === 0)
@@ -2673,7 +2768,7 @@ function DefaultNodeHeader({ id, title, validation, right, showId, onInvalidate,
2673
2768
  .join("; ") })), showId && jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
2674
2769
  }
2675
2770
  function DefaultNodeContent({ data, isConnectable, }) {
2676
- const { showValues, inputValues, outputValues, toString } = data;
2771
+ const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
2677
2772
  const inputEntries = data.inputHandles ?? [];
2678
2773
  const outputEntries = data.outputHandles ?? [];
2679
2774
  const status = data.status ?? { activeRuns: 0 };
@@ -2702,14 +2797,23 @@ function DefaultNodeContent({ data, isConnectable, }) {
2702
2797
  if (!showValues)
2703
2798
  return undefined;
2704
2799
  if (kind === "input") {
2705
- const txt = toString(entry.typeId, inputValues?.[entry.id]);
2706
- return typeof txt === "string" ? txt : String(txt);
2800
+ const value = inputValues?.[entry.id];
2801
+ const isDefault = value !== undefined &&
2802
+ inputDefaults?.[entry.id] !== undefined &&
2803
+ JSON.stringify(value) ===
2804
+ JSON.stringify(inputDefaults[entry.id]);
2805
+ const txt = toString(entry.typeId, value);
2806
+ const str = typeof txt === "string" ? txt : String(txt);
2807
+ return { text: str, isDefault };
2707
2808
  }
2708
2809
  const resolved = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
2709
2810
  const txt = toString(resolved.typeId, resolved.value);
2710
- return typeof txt === "string" ? txt : String(txt);
2811
+ return {
2812
+ text: typeof txt === "string" ? txt : String(txt),
2813
+ isDefault: false,
2814
+ };
2711
2815
  })();
2712
- return (jsxRuntime.jsxs("span", { className: "flex items-center gap-1 w-full", children: [kind === "output" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [valueText !== undefined ? (jsxRuntime.jsx("span", { className: "opacity-60 truncate pl-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText })) : (jsxRuntime.jsx("span", { style: { flex: 1, minWidth: 0, maxWidth: "100%" } })), jsxRuntime.jsx("span", { className: "truncate shrink-0", style: valueText !== undefined ? { maxWidth: "40%" } : {}, children: prettyHandle(handleId) })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: valueText !== undefined ? { maxWidth: "40%" } : {}, children: prettyHandle(handleId) }), valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pr-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText }))] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "shrink-0", title: title }))] }));
2816
+ return (jsxRuntime.jsxs("span", { className: `flex items-center gap-1 w-full ${valueText?.isDefault ? "text-gray-400" : ""}`, children: [kind === "output" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [valueText !== undefined ? (jsxRuntime.jsx("span", { className: "opacity-60 truncate pl-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText.text })) : (jsxRuntime.jsx("span", { style: { flex: 1, minWidth: 0, maxWidth: "100%" } })), jsxRuntime.jsx("span", { className: "truncate shrink-0", style: valueText !== undefined ? { maxWidth: "40%" } : {}, children: prettyHandle(handleId) })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: valueText !== undefined ? { maxWidth: "40%" } : {}, children: prettyHandle(handleId) }), valueText !== undefined && (jsxRuntime.jsx("span", { className: `truncate pr-1 ${valueText.isDefault ? "text-gray-400" : "opacity-60"}`, style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText.text }))] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "shrink-0", title: title }))] }));
2713
2817
  } })] }));
2714
2818
  }
2715
2819
 
@@ -3006,7 +3110,7 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
3006
3110
  }
3007
3111
 
3008
3112
  const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
3009
- const { wb, registry, inputsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, } = useWorkbenchContext();
3113
+ const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, } = useWorkbenchContext();
3010
3114
  const nodeValidation = validationByNode;
3011
3115
  const edgeValidation = validationByEdge.errors;
3012
3116
  // Keep stable references for nodes/edges to avoid unnecessary updates
@@ -3101,9 +3205,28 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
3101
3205
  const { nodes, edges } = React.useMemo(() => {
3102
3206
  const def = wb.export();
3103
3207
  const sel = wb.getSelection();
3208
+ // Merge defaults with inputs for node display (defaults shown in lighter gray)
3209
+ const inputsWithDefaults = {};
3210
+ for (const n of def.nodes) {
3211
+ const nodeInputs = inputsMap[n.nodeId] ?? {};
3212
+ const nodeDefaults = inputDefaultsMap[n.nodeId] ?? {};
3213
+ const inbound = new Set(def.edges
3214
+ .filter((e) => e.target.nodeId === n.nodeId)
3215
+ .map((e) => e.target.handle));
3216
+ const merged = { ...nodeInputs };
3217
+ for (const [h, v] of Object.entries(nodeDefaults)) {
3218
+ if (!inbound.has(h) && merged[h] === undefined) {
3219
+ merged[h] = v;
3220
+ }
3221
+ }
3222
+ if (Object.keys(merged).length > 0) {
3223
+ inputsWithDefaults[n.nodeId] = merged;
3224
+ }
3225
+ }
3104
3226
  const out = toReactFlow(def, wb.getPositions(), registry, {
3105
3227
  showValues,
3106
- inputs: inputsMap,
3228
+ inputs: inputsWithDefaults,
3229
+ inputDefaults: inputDefaultsMap,
3107
3230
  outputs: outputsMap,
3108
3231
  resolveNodeType,
3109
3232
  toString,
@@ -3342,7 +3465,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3342
3465
  const off1 = wb.on("graphChanged", () => {
3343
3466
  try {
3344
3467
  const cur = wb.export();
3345
- const inputs = runner.getInputs(cur);
3468
+ const inputs = runner.getInputs();
3346
3469
  onChange({ def: cur, inputs });
3347
3470
  }
3348
3471
  catch { }
@@ -3350,7 +3473,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3350
3473
  const off2 = runner.on("value", () => {
3351
3474
  try {
3352
3475
  const cur = wb.export();
3353
- const inputs = runner.getInputs(cur);
3476
+ const inputs = runner.getInputs();
3354
3477
  onChange({ def: cur, inputs });
3355
3478
  }
3356
3479
  catch { }
@@ -3396,7 +3519,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
3396
3519
  const downloadGraph = React.useCallback(() => {
3397
3520
  try {
3398
3521
  const def = wb.export();
3399
- const inputs = runner.getInputs(def);
3522
+ const inputs = runner.getInputs();
3400
3523
  const payload = { def, inputs };
3401
3524
  const pretty = JSON.stringify(payload, null, 2);
3402
3525
  const blob = new Blob([pretty], { type: "application/json" });