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