@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/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(
|
|
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
|
|
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(
|
|
524
|
+
getInputs() {
|
|
521
525
|
const out = {};
|
|
522
|
-
|
|
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(
|
|
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(
|
|
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({
|
|
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.
|
|
636
|
-
this.update(this.
|
|
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(
|
|
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
|
|
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(
|
|
832
|
+
getInputs() {
|
|
829
833
|
const out = {};
|
|
830
834
|
const cache = this.valueCache;
|
|
831
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
1774
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2119
|
-
const
|
|
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
|
|
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
|
|
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
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
2162
|
-
|
|
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
|
|
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
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
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
|
-
|
|
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
|
|
2706
|
-
|
|
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
|
|
2811
|
+
return {
|
|
2812
|
+
text: typeof txt === "string" ? txt : String(txt),
|
|
2813
|
+
isDefault: false,
|
|
2814
|
+
};
|
|
2711
2815
|
})();
|
|
2712
|
-
return (jsxRuntime.jsxs("span", { className:
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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" });
|