@bian-womp/spark-workbench 0.2.35 → 0.2.37
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 +117 -54
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +4 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +3 -4
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +3 -3
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +3 -3
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +4 -3
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +117 -54
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +4 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +3 -4
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +3 -3
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +3 -3
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +4 -3
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/esm/index.js
CHANGED
|
@@ -337,12 +337,16 @@ 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;
|
|
341
340
|
}
|
|
342
341
|
setInput(nodeId, handle, value) {
|
|
343
342
|
if (!this.stagedInputs[nodeId])
|
|
344
343
|
this.stagedInputs[nodeId] = {};
|
|
345
|
-
|
|
344
|
+
if (value === undefined) {
|
|
345
|
+
delete this.stagedInputs[nodeId][handle];
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
this.stagedInputs[nodeId][handle] = value;
|
|
349
|
+
}
|
|
346
350
|
if (this.engine) {
|
|
347
351
|
this.engine.setInput(nodeId, handle, value);
|
|
348
352
|
}
|
|
@@ -360,7 +364,14 @@ class AbstractGraphRunner {
|
|
|
360
364
|
return;
|
|
361
365
|
if (!this.stagedInputs[nodeId])
|
|
362
366
|
this.stagedInputs[nodeId] = {};
|
|
363
|
-
|
|
367
|
+
for (const [handle, value] of Object.entries(inputs)) {
|
|
368
|
+
if (value === undefined) {
|
|
369
|
+
delete this.stagedInputs[nodeId][handle];
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
this.stagedInputs[nodeId][handle] = value;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
364
375
|
if (this.engine) {
|
|
365
376
|
// Running: set all inputs
|
|
366
377
|
this.engine.setInputs(nodeId, inputs);
|
|
@@ -394,7 +405,6 @@ class AbstractGraphRunner {
|
|
|
394
405
|
this.engine = undefined;
|
|
395
406
|
this.runtime?.dispose();
|
|
396
407
|
this.runtime = undefined;
|
|
397
|
-
this.currentDef = undefined;
|
|
398
408
|
if (this.runningKind) {
|
|
399
409
|
this.runningKind = undefined;
|
|
400
410
|
this.emit("status", { running: false, engine: undefined });
|
|
@@ -429,14 +439,12 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
429
439
|
this.emit("transport", { state: "local" });
|
|
430
440
|
}
|
|
431
441
|
build(def) {
|
|
432
|
-
this.currentDef = def;
|
|
433
442
|
const builder = new GraphBuilder(this.registry);
|
|
434
443
|
this.runtime = builder.build(def);
|
|
435
444
|
// Signal UI that freshly built graph should be considered invalidated
|
|
436
445
|
this.emit("invalidate", { reason: "graph-built" });
|
|
437
446
|
}
|
|
438
447
|
update(def) {
|
|
439
|
-
this.currentDef = def;
|
|
440
448
|
if (!this.runtime)
|
|
441
449
|
return;
|
|
442
450
|
// Prevent mid-run churn while wiring changes are applied
|
|
@@ -501,11 +509,11 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
501
509
|
if (eng instanceof BatchedEngine)
|
|
502
510
|
eng.flush();
|
|
503
511
|
}
|
|
504
|
-
getOutputs() {
|
|
512
|
+
getOutputs(def) {
|
|
505
513
|
const out = {};
|
|
506
|
-
if (!this.runtime
|
|
514
|
+
if (!this.runtime)
|
|
507
515
|
return out;
|
|
508
|
-
for (const n of
|
|
516
|
+
for (const n of def.nodes) {
|
|
509
517
|
const desc = this.registry.nodes.get(n.typeId);
|
|
510
518
|
const handles = Object.keys(desc?.outputs ?? {});
|
|
511
519
|
for (const h of handles) {
|
|
@@ -519,17 +527,15 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
519
527
|
}
|
|
520
528
|
return out;
|
|
521
529
|
}
|
|
522
|
-
getInputs() {
|
|
530
|
+
getInputs(def) {
|
|
523
531
|
const out = {};
|
|
524
|
-
|
|
525
|
-
return out;
|
|
526
|
-
for (const n of this.currentDef.nodes) {
|
|
532
|
+
for (const n of def.nodes) {
|
|
527
533
|
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
528
534
|
const runtimeInputs = this.runtime
|
|
529
535
|
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
530
536
|
: {};
|
|
531
537
|
// Build inbound handle set for this node from current def
|
|
532
|
-
const inbound = new Set(
|
|
538
|
+
const inbound = new Set(def.edges
|
|
533
539
|
.filter((e) => e.target.nodeId === n.nodeId)
|
|
534
540
|
.map((e) => e.target.handle));
|
|
535
541
|
// Merge staged only for non-inbound handles so UI reflects runtime values for wired inputs
|
|
@@ -543,11 +549,9 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
543
549
|
}
|
|
544
550
|
return out;
|
|
545
551
|
}
|
|
546
|
-
getInputDefaults() {
|
|
552
|
+
getInputDefaults(def) {
|
|
547
553
|
const out = {};
|
|
548
|
-
|
|
549
|
-
return out;
|
|
550
|
-
for (const n of this.currentDef.nodes) {
|
|
554
|
+
for (const n of def.nodes) {
|
|
551
555
|
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
552
556
|
if (Object.keys(dynDefaults).length > 0) {
|
|
553
557
|
out[n.nodeId] = dynDefaults;
|
|
@@ -557,8 +561,24 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
557
561
|
}
|
|
558
562
|
async snapshotFull() {
|
|
559
563
|
const def = undefined; // UI will supply def/positions on download for local
|
|
560
|
-
const inputs = this.getInputs(
|
|
561
|
-
|
|
564
|
+
const inputs = this.getInputs(this.runtime
|
|
565
|
+
? {
|
|
566
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
567
|
+
nodeId: id,
|
|
568
|
+
typeId: "",
|
|
569
|
+
})),
|
|
570
|
+
edges: [],
|
|
571
|
+
}
|
|
572
|
+
: { nodes: [], edges: [] });
|
|
573
|
+
const outputs = this.getOutputs(this.runtime
|
|
574
|
+
? {
|
|
575
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
576
|
+
nodeId: id,
|
|
577
|
+
typeId: "",
|
|
578
|
+
})),
|
|
579
|
+
edges: [],
|
|
580
|
+
}
|
|
581
|
+
: { nodes: [], edges: [] });
|
|
562
582
|
const environment = this.getEnvironment() || {};
|
|
563
583
|
return { def, environment, inputs, outputs };
|
|
564
584
|
}
|
|
@@ -635,8 +655,8 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
635
655
|
this.emit("registry", this.registry);
|
|
636
656
|
// Trigger update so validation/UI refreshes using last known graph
|
|
637
657
|
try {
|
|
638
|
-
if (this.
|
|
639
|
-
this.update(this.
|
|
658
|
+
if (this.lastDef)
|
|
659
|
+
this.update(this.lastDef);
|
|
640
660
|
}
|
|
641
661
|
catch {
|
|
642
662
|
console.error("Failed to update graph definition after registry changed");
|
|
@@ -654,12 +674,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
654
674
|
console.warn("Unsupported operation for remote runner");
|
|
655
675
|
}
|
|
656
676
|
update(def) {
|
|
657
|
-
this.currentDef = def;
|
|
658
677
|
// Remote: forward update; ignore errors (fire-and-forget)
|
|
659
678
|
this.ensureRemoteRunner().then(async (runner) => {
|
|
660
679
|
try {
|
|
661
680
|
await runner.update(def);
|
|
662
681
|
this.emit("invalidate", { reason: "graph-updated" });
|
|
682
|
+
this.lastDef = def;
|
|
663
683
|
}
|
|
664
684
|
catch { }
|
|
665
685
|
});
|
|
@@ -671,6 +691,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
671
691
|
await runner.build(def);
|
|
672
692
|
// Signal UI after remote build as well
|
|
673
693
|
this.emit("invalidate", { reason: "graph-built" });
|
|
694
|
+
this.lastDef = def;
|
|
674
695
|
// Hydrate current remote inputs/outputs (including defaults) into cache
|
|
675
696
|
try {
|
|
676
697
|
const snap = await runner.snapshot();
|
|
@@ -806,12 +827,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
806
827
|
// For now, we expose an async helper on RemoteRunner. Keep sync signature per interface.
|
|
807
828
|
return undefined;
|
|
808
829
|
}
|
|
809
|
-
getOutputs() {
|
|
830
|
+
getOutputs(def) {
|
|
810
831
|
const out = {};
|
|
811
832
|
const cache = this.valueCache;
|
|
812
|
-
if (!cache
|
|
833
|
+
if (!cache)
|
|
813
834
|
return out;
|
|
814
|
-
for (const n of
|
|
835
|
+
for (const n of def.nodes) {
|
|
815
836
|
const resolved = n.resolvedHandles?.outputs;
|
|
816
837
|
const desc = this.registry.nodes.get(n.typeId);
|
|
817
838
|
const handles = Object.keys(resolved ?? desc?.outputs ?? {});
|
|
@@ -827,19 +848,17 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
827
848
|
}
|
|
828
849
|
return out;
|
|
829
850
|
}
|
|
830
|
-
getInputs() {
|
|
851
|
+
getInputs(def) {
|
|
831
852
|
const out = {};
|
|
832
853
|
const cache = this.valueCache;
|
|
833
|
-
|
|
834
|
-
return out;
|
|
835
|
-
for (const n of this.currentDef.nodes) {
|
|
854
|
+
for (const n of def.nodes) {
|
|
836
855
|
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
837
856
|
const resolved = n.resolvedHandles?.inputs;
|
|
838
857
|
const desc = this.registry.nodes.get(n.typeId);
|
|
839
858
|
const handles = Object.keys(resolved ?? desc?.inputs ?? {});
|
|
840
859
|
const cur = {};
|
|
841
860
|
// Build inbound handle set for this node to honor wiring precedence
|
|
842
|
-
const inbound = new Set(
|
|
861
|
+
const inbound = new Set(def.edges
|
|
843
862
|
.filter((e) => e.target.nodeId === n.nodeId)
|
|
844
863
|
.map((e) => e.target.handle));
|
|
845
864
|
for (const h of handles) {
|
|
@@ -858,11 +877,9 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
858
877
|
}
|
|
859
878
|
return out;
|
|
860
879
|
}
|
|
861
|
-
getInputDefaults() {
|
|
880
|
+
getInputDefaults(def) {
|
|
862
881
|
const out = {};
|
|
863
|
-
|
|
864
|
-
return out;
|
|
865
|
-
for (const n of this.currentDef.nodes) {
|
|
882
|
+
for (const n of def.nodes) {
|
|
866
883
|
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
867
884
|
if (Object.keys(dynDefaults).length > 0) {
|
|
868
885
|
out[n.nodeId] = dynDefaults;
|
|
@@ -1731,14 +1748,19 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
1731
1748
|
const clearEvents = useCallback(() => setEvents([]), []);
|
|
1732
1749
|
const [systemErrors, setSystemErrors] = useState([]);
|
|
1733
1750
|
const [registryErrors, setRegistryErrors] = useState([]);
|
|
1751
|
+
const [inputValidationErrors, setInputValidationErrors] = useState([]);
|
|
1734
1752
|
const clearSystemErrors = useCallback(() => setSystemErrors([]), []);
|
|
1735
1753
|
const clearRegistryErrors = useCallback(() => setRegistryErrors([]), []);
|
|
1754
|
+
const clearInputValidationErrors = useCallback(() => setInputValidationErrors([]), []);
|
|
1736
1755
|
const removeSystemError = useCallback((index) => {
|
|
1737
1756
|
setSystemErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
1738
1757
|
}, []);
|
|
1739
1758
|
const removeRegistryError = useCallback((index) => {
|
|
1740
1759
|
setRegistryErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
1741
1760
|
}, []);
|
|
1761
|
+
const removeInputValidationError = useCallback((index) => {
|
|
1762
|
+
setInputValidationErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
1763
|
+
}, []);
|
|
1742
1764
|
// Fallback progress animation: drive progress to 100% over ~2 minutes
|
|
1743
1765
|
const FALLBACK_TOTAL_MS = 2 * 60 * 1000;
|
|
1744
1766
|
const [fallbackStarts, setFallbackStarts] = useState({});
|
|
@@ -1787,14 +1809,15 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
1787
1809
|
const valuesTick = versionTick + graphTick + graphUiTick;
|
|
1788
1810
|
// Def and IO values
|
|
1789
1811
|
const def = wb.export();
|
|
1790
|
-
const inputsMap = useMemo(() => runner.getInputs(), [runner, valuesTick]);
|
|
1791
|
-
const inputDefaultsMap = useMemo(() => runner.getInputDefaults(), [runner, valuesTick]);
|
|
1792
|
-
const outputsMap = useMemo(() => runner.getOutputs(), [runner, valuesTick]);
|
|
1812
|
+
const inputsMap = useMemo(() => runner.getInputs(def), [runner, def, valuesTick]);
|
|
1813
|
+
const inputDefaultsMap = useMemo(() => runner.getInputDefaults(def), [runner, def, valuesTick]);
|
|
1814
|
+
const outputsMap = useMemo(() => runner.getOutputs(def), [runner, def, valuesTick]);
|
|
1793
1815
|
const outputTypesMap = useMemo(() => {
|
|
1794
1816
|
const out = {};
|
|
1795
1817
|
// Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
|
|
1796
1818
|
for (const n of def.nodes) {
|
|
1797
|
-
const
|
|
1819
|
+
const effectiveHandles = computeEffectiveHandles(n, registry);
|
|
1820
|
+
const outputsDecl = effectiveHandles.outputs;
|
|
1798
1821
|
const handles = Object.keys(outputsDecl);
|
|
1799
1822
|
const cur = {};
|
|
1800
1823
|
for (const h of handles) {
|
|
@@ -1955,10 +1978,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
1955
1978
|
const offRunnerValue = runner.on("value", (e) => {
|
|
1956
1979
|
if (e?.io === "input") {
|
|
1957
1980
|
const nodeId = e?.nodeId;
|
|
1981
|
+
const handle = e?.handle;
|
|
1958
1982
|
setNodeStatus((s) => ({
|
|
1959
1983
|
...s,
|
|
1960
1984
|
[nodeId]: { ...s[nodeId], invalidated: true },
|
|
1961
1985
|
}));
|
|
1986
|
+
// Clear validation errors for this input when a valid value is set
|
|
1987
|
+
setInputValidationErrors((prev) => prev.filter((err) => !(err.nodeId === nodeId && err.handle === handle)));
|
|
1962
1988
|
}
|
|
1963
1989
|
return add("runner", "value")(e);
|
|
1964
1990
|
});
|
|
@@ -1967,6 +1993,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
1967
1993
|
const nodeError = e;
|
|
1968
1994
|
const registryError = e;
|
|
1969
1995
|
const systemError = e;
|
|
1996
|
+
const inputValidationError = e;
|
|
1970
1997
|
if (edgeError.kind === "edge-convert") {
|
|
1971
1998
|
const edgeId = edgeError.edgeId;
|
|
1972
1999
|
setEdgeStatus((s) => ({
|
|
@@ -2002,6 +2029,18 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2002
2029
|
return [...prev, registryError];
|
|
2003
2030
|
});
|
|
2004
2031
|
}
|
|
2032
|
+
else if (inputValidationError.kind === "input-validation") {
|
|
2033
|
+
// Track input validation errors for UI display
|
|
2034
|
+
setInputValidationErrors((prev) => {
|
|
2035
|
+
// Avoid duplicates by checking nodeId, handle, and typeId
|
|
2036
|
+
if (prev.some((err) => err.nodeId === inputValidationError.nodeId &&
|
|
2037
|
+
err.handle === inputValidationError.handle &&
|
|
2038
|
+
err.typeId === inputValidationError.typeId)) {
|
|
2039
|
+
return prev;
|
|
2040
|
+
}
|
|
2041
|
+
return [...prev, inputValidationError];
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2005
2044
|
else if (systemError.kind === "system") {
|
|
2006
2045
|
// Track custom errors for UI display
|
|
2007
2046
|
setSystemErrors((prev) => {
|
|
@@ -2125,7 +2164,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2125
2164
|
}
|
|
2126
2165
|
return add("runner", "stats")(s);
|
|
2127
2166
|
});
|
|
2128
|
-
const offWbGraphChanged = wb.on("graphChanged",
|
|
2167
|
+
const offWbGraphChanged = wb.on("graphChanged", (event) => {
|
|
2168
|
+
// Clear validation errors for removed nodes
|
|
2169
|
+
if (event.change?.type === "removeNode") {
|
|
2170
|
+
const removedNodeId = event.change.nodeId;
|
|
2171
|
+
setInputValidationErrors((prev) => prev.filter((err) => err.nodeId !== removedNodeId));
|
|
2172
|
+
}
|
|
2173
|
+
return add("workbench", "graphChanged")(event);
|
|
2174
|
+
});
|
|
2129
2175
|
const offWbGraphUiChanged = wb.on("graphUiChanged", add("workbench", "graphUiChanged"));
|
|
2130
2176
|
const offWbValidationChanged = wb.on("validationChanged", add("workbench", "validationChanged"));
|
|
2131
2177
|
// Ensure newly added nodes start as invalidated until first evaluation
|
|
@@ -2189,13 +2235,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2189
2235
|
offRunnerTransport();
|
|
2190
2236
|
};
|
|
2191
2237
|
}, [runner, wb]);
|
|
2192
|
-
//
|
|
2193
|
-
// When an engine/runtime exists, this also pushes incremental wiring changes into it.
|
|
2238
|
+
// Push incremental updates into running engine without full reload
|
|
2194
2239
|
useEffect(() => {
|
|
2195
|
-
|
|
2196
|
-
|
|
2240
|
+
if (runner.isRunning()) {
|
|
2241
|
+
try {
|
|
2242
|
+
runner.update(def);
|
|
2243
|
+
}
|
|
2244
|
+
catch { }
|
|
2197
2245
|
}
|
|
2198
|
-
catch { }
|
|
2199
2246
|
}, [runner, def, graphTick]);
|
|
2200
2247
|
const validationByNode = useMemo(() => {
|
|
2201
2248
|
const inputs = {};
|
|
@@ -2294,10 +2341,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2294
2341
|
clearEvents,
|
|
2295
2342
|
systemErrors,
|
|
2296
2343
|
registryErrors,
|
|
2344
|
+
inputValidationErrors,
|
|
2297
2345
|
clearSystemErrors,
|
|
2298
2346
|
clearRegistryErrors,
|
|
2347
|
+
clearInputValidationErrors,
|
|
2299
2348
|
removeSystemError,
|
|
2300
2349
|
removeRegistryError,
|
|
2350
|
+
removeInputValidationError,
|
|
2301
2351
|
isRunning,
|
|
2302
2352
|
engineKind,
|
|
2303
2353
|
start,
|
|
@@ -2322,10 +2372,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2322
2372
|
valuesTick,
|
|
2323
2373
|
systemErrors,
|
|
2324
2374
|
registryErrors,
|
|
2375
|
+
inputValidationErrors,
|
|
2325
2376
|
clearSystemErrors,
|
|
2326
2377
|
clearRegistryErrors,
|
|
2378
|
+
clearInputValidationErrors,
|
|
2327
2379
|
removeSystemError,
|
|
2328
2380
|
removeRegistryError,
|
|
2381
|
+
removeInputValidationError,
|
|
2329
2382
|
inputsMap,
|
|
2330
2383
|
inputDefaultsMap,
|
|
2331
2384
|
outputsMap,
|
|
@@ -2413,7 +2466,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2413
2466
|
return String(value ?? "");
|
|
2414
2467
|
}
|
|
2415
2468
|
};
|
|
2416
|
-
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, clearSystemErrors, clearRegistryErrors, removeSystemError, removeRegistryError, } = useWorkbenchContext();
|
|
2469
|
+
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, inputValidationErrors, clearSystemErrors, clearRegistryErrors, clearInputValidationErrors, removeSystemError, removeRegistryError, removeInputValidationError, } = useWorkbenchContext();
|
|
2417
2470
|
const nodeValidationIssues = validationByNode.issues;
|
|
2418
2471
|
const edgeValidationIssues = validationByEdge.issues;
|
|
2419
2472
|
const nodeValidationHandles = validationByNode;
|
|
@@ -2524,7 +2577,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2524
2577
|
}
|
|
2525
2578
|
catch { }
|
|
2526
2579
|
};
|
|
2527
|
-
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto`, children: [jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsx("div", { className: "mb-2", children: contextPanel }), systemErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsx("div", { className: "break-words", children: err.message })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Registry Error" }), jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.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: globalValidationIssues.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}` }), !!m.data?.edgeId && (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) => {
|
|
2580
|
+
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto`, children: [jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsx("div", { className: "mb-2", children: contextPanel }), inputValidationErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [inputValidationErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Input Validation Error" }), jsx("div", { className: "break-words", children: err.message }), jsxs("div", { className: "text-[10px] text-red-600 mt-1", children: [err.nodeId, ".", err.handle, " (type: ", err.typeId, ")"] })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeInputValidationError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), inputValidationErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearInputValidationErrors, children: "Clear all" }))] })), systemErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsx("div", { className: "break-words", children: err.message })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Registry Error" }), jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.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: globalValidationIssues.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}` }), !!m.data?.edgeId && (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) => {
|
|
2528
2581
|
e.stopPropagation();
|
|
2529
2582
|
deleteEdgeById(m.data?.edgeId);
|
|
2530
2583
|
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), (() => {
|
|
@@ -2562,13 +2615,20 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2562
2615
|
const current = nodeInputs[h];
|
|
2563
2616
|
const hasValue = current !== undefined && current !== null;
|
|
2564
2617
|
const value = drafts[h] ?? safeToString(typeId, current);
|
|
2565
|
-
const displayValue =
|
|
2618
|
+
const displayValue = value;
|
|
2566
2619
|
const placeholder = hasDefault ? defaultStr : undefined;
|
|
2567
2620
|
const onChangeText = (text) => setDrafts((d) => ({ ...d, [h]: text }));
|
|
2568
2621
|
const commit = () => {
|
|
2569
2622
|
const draft = drafts[h];
|
|
2570
2623
|
if (draft === undefined)
|
|
2571
2624
|
return;
|
|
2625
|
+
// Only commit if draft differs from current value
|
|
2626
|
+
const currentDisplay = safeToString(typeId, current);
|
|
2627
|
+
if (draft === currentDisplay) {
|
|
2628
|
+
// No change, just sync originals without calling setInput
|
|
2629
|
+
setOriginals((o) => ({ ...o, [h]: draft }));
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2572
2632
|
setInput(h, draft);
|
|
2573
2633
|
setOriginals((o) => ({ ...o, [h]: draft }));
|
|
2574
2634
|
};
|
|
@@ -3386,9 +3446,12 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3386
3446
|
state: "local",
|
|
3387
3447
|
});
|
|
3388
3448
|
const selectedNode = def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
3389
|
-
|
|
3449
|
+
selectedNode
|
|
3390
3450
|
? registry.nodes.get(selectedNode.typeId)
|
|
3391
3451
|
: undefined;
|
|
3452
|
+
const effectiveHandles = selectedNode
|
|
3453
|
+
? computeEffectiveHandles(selectedNode, registry)
|
|
3454
|
+
: { inputs: {}, outputs: {}, inputDefaults: {} };
|
|
3392
3455
|
const [exampleState, setExampleState] = useState(example ?? "");
|
|
3393
3456
|
const defaultExamples = useMemo(() => [
|
|
3394
3457
|
{
|
|
@@ -3463,7 +3526,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3463
3526
|
const off1 = wb.on("graphChanged", () => {
|
|
3464
3527
|
try {
|
|
3465
3528
|
const cur = wb.export();
|
|
3466
|
-
const inputs = runner.getInputs();
|
|
3529
|
+
const inputs = runner.getInputs(cur);
|
|
3467
3530
|
onChange({ def: cur, inputs });
|
|
3468
3531
|
}
|
|
3469
3532
|
catch { }
|
|
@@ -3471,7 +3534,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3471
3534
|
const off2 = runner.on("value", () => {
|
|
3472
3535
|
try {
|
|
3473
3536
|
const cur = wb.export();
|
|
3474
|
-
const inputs = runner.getInputs();
|
|
3537
|
+
const inputs = runner.getInputs(cur);
|
|
3475
3538
|
onChange({ def: cur, inputs });
|
|
3476
3539
|
}
|
|
3477
3540
|
catch { }
|
|
@@ -3517,7 +3580,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3517
3580
|
const downloadGraph = useCallback(() => {
|
|
3518
3581
|
try {
|
|
3519
3582
|
const def = wb.export();
|
|
3520
|
-
const inputs = runner.getInputs();
|
|
3583
|
+
const inputs = runner.getInputs(def);
|
|
3521
3584
|
const payload = { def, inputs };
|
|
3522
3585
|
const pretty = JSON.stringify(payload, null, 2);
|
|
3523
3586
|
const blob = new Blob([pretty], { type: "application/json" });
|
|
@@ -3676,7 +3739,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3676
3739
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId && e.target.handle === handle);
|
|
3677
3740
|
if (isLinked)
|
|
3678
3741
|
return;
|
|
3679
|
-
const typeId =
|
|
3742
|
+
const typeId = effectiveHandles.inputs[handle];
|
|
3680
3743
|
let value = raw;
|
|
3681
3744
|
const parseArray = (s, map) => {
|
|
3682
3745
|
const str = String(s).trim();
|
|
@@ -3753,7 +3816,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3753
3816
|
}
|
|
3754
3817
|
}
|
|
3755
3818
|
runner.setInput(selectedNodeId, handle, value);
|
|
3756
|
-
}, [selectedNodeId, def.edges,
|
|
3819
|
+
}, [selectedNodeId, def.edges, effectiveHandles, runner]);
|
|
3757
3820
|
const setInput = useMemo(() => {
|
|
3758
3821
|
if (overrides?.setInput) {
|
|
3759
3822
|
return overrides.setInput(baseSetInput, {
|