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