@bian-womp/spark-workbench 0.3.2 → 0.3.3
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 +253 -131
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +53 -0
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +16 -0
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.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 +0 -3
- 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/load.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +7 -0
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +6 -4
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +5 -3
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +7 -0
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +4 -0
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +254 -132
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +53 -0
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +16 -0
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.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 +0 -3
- 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/load.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +7 -0
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +6 -4
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +5 -3
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +7 -0
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +4 -0
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateId, createSimpleGraphRegistry, GraphBuilder, getTypedOutputValue, isTypedOutput, getInputTypeId, LocalEngine, getTypedOutputTypeId, isInputPrivate, offsetImportedPositions, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
1
|
+
import { generateId, createSimpleGraphRegistry, GraphBuilder, getTypedOutputValue, isTypedOutput, getInputTypeId, LocalEngine, parseJsonPath, setValueAtPathWithCreation, getTypedOutputTypeId, isInputPrivate, offsetImportedPositions, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
2
2
|
import lod from 'lodash';
|
|
3
3
|
import { RemoteRuntimeClient } from '@bian-womp/spark-remote';
|
|
4
4
|
import { Position, Handle, NodeResizer, getBezierPath, BaseEdge, useReactFlow, ReactFlowProvider, ReactFlow, Background, BackgroundVariant, MiniMap, Controls } from '@xyflow/react';
|
|
@@ -144,6 +144,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
144
144
|
edges: [],
|
|
145
145
|
};
|
|
146
146
|
this.nodeNames = {};
|
|
147
|
+
this.customData = {};
|
|
147
148
|
this.runtimeState = null;
|
|
148
149
|
this.viewport = null;
|
|
149
150
|
this.historyState = undefined;
|
|
@@ -172,6 +173,17 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
172
173
|
const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
173
174
|
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
174
175
|
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
176
|
+
// Clean up extData for removed nodes/edges
|
|
177
|
+
if (this.customData.nodes) {
|
|
178
|
+
const filteredExtNodes = Object.fromEntries(Object.entries(this.customData.nodes).filter(([id]) => defNodeIds.has(id)));
|
|
179
|
+
this.customData.nodes =
|
|
180
|
+
Object.keys(filteredExtNodes).length > 0 ? filteredExtNodes : undefined;
|
|
181
|
+
}
|
|
182
|
+
if (this.customData.edges) {
|
|
183
|
+
const filteredExtEdges = Object.fromEntries(Object.entries(this.customData.edges).filter(([id]) => defEdgeIds.has(id)));
|
|
184
|
+
this.customData.edges =
|
|
185
|
+
Object.keys(filteredExtEdges).length > 0 ? filteredExtEdges : undefined;
|
|
186
|
+
}
|
|
175
187
|
this.positions = filteredPositions;
|
|
176
188
|
this.sizes = filteredSizes;
|
|
177
189
|
this.selection = { nodes: filteredNodes, edges: filteredEdges };
|
|
@@ -849,6 +861,107 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
849
861
|
...options,
|
|
850
862
|
});
|
|
851
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Get custom data for a specific node.
|
|
866
|
+
*/
|
|
867
|
+
getCustomNodeData(nodeId) {
|
|
868
|
+
return this.customData.nodes?.[nodeId];
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Set custom data for a specific node.
|
|
872
|
+
*/
|
|
873
|
+
setCustomNodeData(nodeId, data, options) {
|
|
874
|
+
if (!this.customData.nodes) {
|
|
875
|
+
this.customData.nodes = {};
|
|
876
|
+
}
|
|
877
|
+
if (data === undefined) {
|
|
878
|
+
delete this.customData.nodes[nodeId];
|
|
879
|
+
if (Object.keys(this.customData.nodes).length === 0) {
|
|
880
|
+
delete this.customData.nodes;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
this.customData.nodes[nodeId] = data;
|
|
885
|
+
}
|
|
886
|
+
this.emit("graphUiChanged", {
|
|
887
|
+
change: { type: "customNodeData", nodeId, data },
|
|
888
|
+
...options,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get custom data for a specific edge.
|
|
893
|
+
*/
|
|
894
|
+
getCustomEdgeData(edgeId) {
|
|
895
|
+
return this.customData.edges?.[edgeId];
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Set custom data for a specific edge.
|
|
899
|
+
*/
|
|
900
|
+
setCustomEdgeData(edgeId, data, options) {
|
|
901
|
+
if (!this.customData.edges) {
|
|
902
|
+
this.customData.edges = {};
|
|
903
|
+
}
|
|
904
|
+
if (data === undefined) {
|
|
905
|
+
delete this.customData.edges[edgeId];
|
|
906
|
+
if (Object.keys(this.customData.edges).length === 0) {
|
|
907
|
+
delete this.customData.edges;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
this.customData.edges[edgeId] = data;
|
|
912
|
+
}
|
|
913
|
+
this.emit("graphUiChanged", {
|
|
914
|
+
change: { type: "customEdgeData", edgeId, data },
|
|
915
|
+
...options,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get custom metadata.
|
|
920
|
+
*/
|
|
921
|
+
getCustomMetaData() {
|
|
922
|
+
return this.customData.meta;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Set custom metadata.
|
|
926
|
+
*/
|
|
927
|
+
setCustomMetaData(meta, options) {
|
|
928
|
+
if (meta === undefined) {
|
|
929
|
+
delete this.customData.meta;
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
this.customData.meta = meta;
|
|
933
|
+
}
|
|
934
|
+
this.emit("graphUiChanged", {
|
|
935
|
+
change: { type: "customMetaData", meta },
|
|
936
|
+
...options,
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Get all custom data.
|
|
941
|
+
*/
|
|
942
|
+
getCustomData() {
|
|
943
|
+
return { ...this.customData };
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Set all custom data.
|
|
947
|
+
*/
|
|
948
|
+
setCustomData(custom, options) {
|
|
949
|
+
if (custom === undefined) {
|
|
950
|
+
this.customData = {};
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
this.customData = lod.pick(custom, ["nodes", "edges", "meta"]);
|
|
954
|
+
}
|
|
955
|
+
this.emit("graphUiChanged", {
|
|
956
|
+
change: {
|
|
957
|
+
type: "customData",
|
|
958
|
+
nodes: custom?.nodes,
|
|
959
|
+
edges: custom?.edges,
|
|
960
|
+
meta: custom?.meta,
|
|
961
|
+
},
|
|
962
|
+
...options,
|
|
963
|
+
});
|
|
964
|
+
}
|
|
852
965
|
}
|
|
853
966
|
|
|
854
967
|
class CLIWorkbench {
|
|
@@ -909,36 +1022,14 @@ class AbstractGraphRunner {
|
|
|
909
1022
|
this.stagedInputs = {};
|
|
910
1023
|
this.runnerId = "";
|
|
911
1024
|
}
|
|
912
|
-
launch(def, opts) {
|
|
913
|
-
// Auto-stop if engine is already running
|
|
914
|
-
if (this.engine) {
|
|
915
|
-
this.stop();
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
1025
|
async whenIdle() {
|
|
919
1026
|
await this.engine?.whenIdle();
|
|
920
1027
|
}
|
|
921
|
-
stop() {
|
|
922
|
-
if (!this.engine)
|
|
923
|
-
return;
|
|
924
|
-
// Dispose engine (cleans up timers, listeners, etc.)
|
|
925
|
-
this.engine.dispose();
|
|
926
|
-
this.engine = undefined;
|
|
927
|
-
// Emit status but keep runtime alive
|
|
928
|
-
if (this.runMode) {
|
|
929
|
-
this.runMode = undefined;
|
|
930
|
-
this.emit("status", { running: false, runMode: undefined });
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
1028
|
setRunMode(runMode) {
|
|
934
|
-
if (
|
|
935
|
-
|
|
1029
|
+
if (this.engine) {
|
|
1030
|
+
this.engine.setRunMode(runMode);
|
|
1031
|
+
this.emit("status", { running: true, runMode });
|
|
936
1032
|
}
|
|
937
|
-
// Update engine run mode (this will update pause/resume state)
|
|
938
|
-
this.engine.setRunMode(runMode);
|
|
939
|
-
// Update local state and emit status event
|
|
940
|
-
this.runMode = runMode;
|
|
941
|
-
this.emit("status", { running: true, runMode: this.runMode });
|
|
942
1033
|
}
|
|
943
1034
|
getInputDefaults(def) {
|
|
944
1035
|
const out = {};
|
|
@@ -968,17 +1059,10 @@ class AbstractGraphRunner {
|
|
|
968
1059
|
this.engine = undefined;
|
|
969
1060
|
this.runtime?.dispose();
|
|
970
1061
|
this.runtime = undefined;
|
|
971
|
-
if (this.runMode) {
|
|
972
|
-
this.runMode = undefined;
|
|
973
|
-
this.emit("status", { running: false, runMode: undefined });
|
|
974
|
-
}
|
|
975
1062
|
}
|
|
976
1063
|
isRunning() {
|
|
977
1064
|
return !!this.engine;
|
|
978
1065
|
}
|
|
979
|
-
getRunMode() {
|
|
980
|
-
return this.runMode;
|
|
981
|
-
}
|
|
982
1066
|
// Optional undo/redo support
|
|
983
1067
|
async undo() {
|
|
984
1068
|
return false;
|
|
@@ -997,6 +1081,7 @@ let localRunnerCounter = 0;
|
|
|
997
1081
|
class LocalGraphRunner extends AbstractGraphRunner {
|
|
998
1082
|
constructor(registry) {
|
|
999
1083
|
super(registry, { kind: "local" });
|
|
1084
|
+
this.extData = {};
|
|
1000
1085
|
this.setEnvironment = (env, opts) => {
|
|
1001
1086
|
if (!this.runtime)
|
|
1002
1087
|
return;
|
|
@@ -1045,7 +1130,10 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1045
1130
|
}
|
|
1046
1131
|
}
|
|
1047
1132
|
launch(def, opts) {
|
|
1048
|
-
|
|
1133
|
+
if (this.engine) {
|
|
1134
|
+
this.engine.dispose();
|
|
1135
|
+
this.engine = undefined;
|
|
1136
|
+
}
|
|
1049
1137
|
this.build(def);
|
|
1050
1138
|
if (!this.runtime)
|
|
1051
1139
|
throw new Error("Runtime not built");
|
|
@@ -1072,8 +1160,8 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1072
1160
|
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
1073
1161
|
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
1074
1162
|
this.engine.launch(opts?.invalidate);
|
|
1075
|
-
|
|
1076
|
-
this.emit("status", { running: true, runMode
|
|
1163
|
+
const runMode = opts?.runMode ?? "manual";
|
|
1164
|
+
this.emit("status", { running: true, runMode });
|
|
1077
1165
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
1078
1166
|
this.engine.setInputs(nodeId, map);
|
|
1079
1167
|
}
|
|
@@ -1167,6 +1255,24 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1167
1255
|
this.engine.copyOutputs(fromNodeId, toNodeId, options);
|
|
1168
1256
|
}
|
|
1169
1257
|
}
|
|
1258
|
+
async setExtData(data) {
|
|
1259
|
+
if (!data || typeof data !== "object") {
|
|
1260
|
+
this.extData = {};
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
this.extData = { ...this.extData, ...data };
|
|
1264
|
+
}
|
|
1265
|
+
async updateExtData(updates) {
|
|
1266
|
+
if (!this.extData ||
|
|
1267
|
+
typeof this.extData !== "object" ||
|
|
1268
|
+
Array.isArray(this.extData)) {
|
|
1269
|
+
this.extData = {};
|
|
1270
|
+
}
|
|
1271
|
+
for (const { path, value } of updates) {
|
|
1272
|
+
const pathSegments = parseJsonPath(path);
|
|
1273
|
+
setValueAtPathWithCreation(this.extData, pathSegments, value);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1170
1276
|
async snapshotFull() {
|
|
1171
1277
|
const def = undefined; // UI will supply def/positions on download for local
|
|
1172
1278
|
const inputs = this.getInputs(this.runtime
|
|
@@ -1188,7 +1294,8 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1188
1294
|
}
|
|
1189
1295
|
: { nodes: [], edges: [] });
|
|
1190
1296
|
const environment = this.getEnvironment() || {};
|
|
1191
|
-
|
|
1297
|
+
const extData = this.extData;
|
|
1298
|
+
return { def, environment, inputs, outputs, extData };
|
|
1192
1299
|
}
|
|
1193
1300
|
async applySnapshotFull(payload, options) {
|
|
1194
1301
|
if (payload.def && !options?.skipBuild) {
|
|
@@ -1196,6 +1303,9 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1196
1303
|
}
|
|
1197
1304
|
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
1198
1305
|
this.hydrate(payload, { dry: options?.dry });
|
|
1306
|
+
if (payload.extData) {
|
|
1307
|
+
await this.setExtData(payload.extData);
|
|
1308
|
+
}
|
|
1199
1309
|
}
|
|
1200
1310
|
hydrate(snapshot, opts) {
|
|
1201
1311
|
// Hydrate via runtime for exact restore (this emits events on runtime emitter)
|
|
@@ -1565,7 +1675,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1565
1675
|
}
|
|
1566
1676
|
}
|
|
1567
1677
|
launch(def, opts) {
|
|
1568
|
-
|
|
1678
|
+
if (this.engine) {
|
|
1679
|
+
this.engine.dispose();
|
|
1680
|
+
this.engine = undefined;
|
|
1681
|
+
}
|
|
1569
1682
|
// Remote: build remotely then launch
|
|
1570
1683
|
this.ensureClient().then(async (client) => {
|
|
1571
1684
|
await client.api.build(def);
|
|
@@ -1621,8 +1734,8 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1621
1734
|
this.listenersBound = true;
|
|
1622
1735
|
}
|
|
1623
1736
|
this.engine = eng;
|
|
1624
|
-
|
|
1625
|
-
this.emit("status", { running: true, runMode
|
|
1737
|
+
const runMode = opts?.runMode ?? "manual";
|
|
1738
|
+
this.emit("status", { running: true, runMode });
|
|
1626
1739
|
// Re-apply staged inputs using client.setInputs for consistency
|
|
1627
1740
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
1628
1741
|
await eng.setInputs(nodeId, map, undefined).catch(() => {
|
|
@@ -1638,7 +1751,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1638
1751
|
* the runtime state that was just restored.
|
|
1639
1752
|
*/
|
|
1640
1753
|
launchExisting(def, opts) {
|
|
1641
|
-
|
|
1754
|
+
if (this.engine) {
|
|
1755
|
+
this.engine.dispose();
|
|
1756
|
+
this.engine = undefined;
|
|
1757
|
+
}
|
|
1642
1758
|
// Remote: attach to existing runtime and launch (do NOT rebuild)
|
|
1643
1759
|
this.ensureClient().then(async (client) => {
|
|
1644
1760
|
// NOTE: We do NOT call client.build(def) here because the backend runtime
|
|
@@ -1649,14 +1765,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1649
1765
|
});
|
|
1650
1766
|
}
|
|
1651
1767
|
setRunMode(runMode) {
|
|
1652
|
-
if (
|
|
1653
|
-
|
|
1768
|
+
if (this.engine) {
|
|
1769
|
+
this.engine.setRunMode(runMode);
|
|
1770
|
+
this.emit("status", { running: true, runMode });
|
|
1654
1771
|
}
|
|
1655
|
-
// Update engine run mode (sends SetRunMode command to backend)
|
|
1656
|
-
this.engine.setRunMode(runMode);
|
|
1657
|
-
// Update local state and emit status event
|
|
1658
|
-
this.runMode = runMode;
|
|
1659
|
-
this.emit("status", { running: true, runMode: this.runMode });
|
|
1660
1772
|
}
|
|
1661
1773
|
async computeNode(nodeId, options) {
|
|
1662
1774
|
const client = await this.ensureClient();
|
|
@@ -1719,6 +1831,10 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1719
1831
|
const client = await this.ensureClient();
|
|
1720
1832
|
await client.api.setExtData(data);
|
|
1721
1833
|
}
|
|
1834
|
+
async updateExtData(updates) {
|
|
1835
|
+
const client = await this.ensureClient();
|
|
1836
|
+
await client.api.updateExtData(updates);
|
|
1837
|
+
}
|
|
1722
1838
|
async commit(reason) {
|
|
1723
1839
|
const client = await this.ensureClient();
|
|
1724
1840
|
try {
|
|
@@ -2590,35 +2706,40 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2590
2706
|
}));
|
|
2591
2707
|
const handleLayout = geom.handleLayout;
|
|
2592
2708
|
const handles = geom.handles;
|
|
2709
|
+
const baseData = {
|
|
2710
|
+
typeId: n.typeId,
|
|
2711
|
+
params: n.params,
|
|
2712
|
+
inputHandles,
|
|
2713
|
+
outputHandles,
|
|
2714
|
+
inputConnected: Object.fromEntries(inputHandles.map((h) => [
|
|
2715
|
+
h.id,
|
|
2716
|
+
!!connectedInputs[n.nodeId]?.has(h.id),
|
|
2717
|
+
])),
|
|
2718
|
+
handleLayout,
|
|
2719
|
+
showValues: opts.showValues,
|
|
2720
|
+
renderWidth,
|
|
2721
|
+
renderHeight,
|
|
2722
|
+
initialWidth: initialGeom.width,
|
|
2723
|
+
initialHeight: initialGeom.height,
|
|
2724
|
+
inputValues: opts.inputs?.[n.nodeId],
|
|
2725
|
+
inputDefaults: opts.inputDefaults?.[n.nodeId],
|
|
2726
|
+
outputValues: opts.outputs?.[n.nodeId],
|
|
2727
|
+
status: opts.nodeStatus?.[n.nodeId],
|
|
2728
|
+
validation: {
|
|
2729
|
+
inputs: opts.nodeValidation?.inputs?.[n.nodeId] ?? [],
|
|
2730
|
+
outputs: opts.nodeValidation?.outputs?.[n.nodeId] ?? [],
|
|
2731
|
+
issues: opts.nodeValidation?.issues?.[n.nodeId] ?? [],
|
|
2732
|
+
},
|
|
2733
|
+
toString: opts.toString,
|
|
2734
|
+
toElement: opts.toElement,
|
|
2735
|
+
};
|
|
2736
|
+
const customNodeData = opts.customData?.nodes;
|
|
2737
|
+
const mergedData = customNodeData?.[n.nodeId]
|
|
2738
|
+
? { ...baseData, custom: customNodeData[n.nodeId] }
|
|
2739
|
+
: baseData;
|
|
2593
2740
|
return {
|
|
2594
2741
|
id: n.nodeId,
|
|
2595
|
-
data:
|
|
2596
|
-
typeId: n.typeId,
|
|
2597
|
-
params: n.params,
|
|
2598
|
-
inputHandles,
|
|
2599
|
-
outputHandles,
|
|
2600
|
-
inputConnected: Object.fromEntries(inputHandles.map((h) => [
|
|
2601
|
-
h.id,
|
|
2602
|
-
!!connectedInputs[n.nodeId]?.has(h.id),
|
|
2603
|
-
])),
|
|
2604
|
-
handleLayout,
|
|
2605
|
-
showValues: opts.showValues,
|
|
2606
|
-
renderWidth,
|
|
2607
|
-
renderHeight,
|
|
2608
|
-
initialWidth: initialGeom.width,
|
|
2609
|
-
initialHeight: initialGeom.height,
|
|
2610
|
-
inputValues: opts.inputs?.[n.nodeId],
|
|
2611
|
-
inputDefaults: opts.inputDefaults?.[n.nodeId],
|
|
2612
|
-
outputValues: opts.outputs?.[n.nodeId],
|
|
2613
|
-
status: opts.nodeStatus?.[n.nodeId],
|
|
2614
|
-
validation: {
|
|
2615
|
-
inputs: opts.nodeValidation?.inputs?.[n.nodeId] ?? [],
|
|
2616
|
-
outputs: opts.nodeValidation?.outputs?.[n.nodeId] ?? [],
|
|
2617
|
-
issues: opts.nodeValidation?.issues?.[n.nodeId] ?? [],
|
|
2618
|
-
},
|
|
2619
|
-
toString: opts.toString,
|
|
2620
|
-
toElement: opts.toElement,
|
|
2621
|
-
},
|
|
2742
|
+
data: mergedData,
|
|
2622
2743
|
position: positions[n.nodeId] ?? { x: 0, y: 0 },
|
|
2623
2744
|
type: opts.resolveNodeType?.(n.typeId) ?? "spark-default",
|
|
2624
2745
|
selected: opts.selectedNodeIds
|
|
@@ -2661,6 +2782,25 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2661
2782
|
const targetHandleTypeId = targetHandles?.inputs[e.target.handle]
|
|
2662
2783
|
? getInputTypeId(targetHandles.inputs, e.target.handle) ?? "unknown"
|
|
2663
2784
|
: "unknown";
|
|
2785
|
+
const baseEdgeData = {
|
|
2786
|
+
sourceNodeId: e.source.nodeId,
|
|
2787
|
+
sourceNodeTypeId: sourceNode?.typeId || "unknown",
|
|
2788
|
+
sourceHandle: e.source.handle,
|
|
2789
|
+
sourceHandleTypeId,
|
|
2790
|
+
targetNodeId: e.target.nodeId,
|
|
2791
|
+
targetNodeTypeId: targetNode?.typeId || "unknown",
|
|
2792
|
+
targetHandle: e.target.handle,
|
|
2793
|
+
targetHandleTypeId,
|
|
2794
|
+
edgeTypeId,
|
|
2795
|
+
isRunning,
|
|
2796
|
+
hasError,
|
|
2797
|
+
isInvalid: isInvalidEdge,
|
|
2798
|
+
isMissing,
|
|
2799
|
+
};
|
|
2800
|
+
const customEdgeData = opts.customData?.edges;
|
|
2801
|
+
const mergedEdgeData = customEdgeData?.[e.id]
|
|
2802
|
+
? { ...baseEdgeData, custom: customEdgeData[e.id] }
|
|
2803
|
+
: baseEdgeData;
|
|
2664
2804
|
return {
|
|
2665
2805
|
id: e.id,
|
|
2666
2806
|
source: e.source.nodeId,
|
|
@@ -2674,21 +2814,7 @@ function toReactFlow(def, positions, sizes, registry, opts) {
|
|
|
2674
2814
|
style,
|
|
2675
2815
|
label: edgeTypeId,
|
|
2676
2816
|
type: "default",
|
|
2677
|
-
data:
|
|
2678
|
-
sourceNodeId: e.source.nodeId,
|
|
2679
|
-
sourceNodeTypeId: sourceNode?.typeId || "unknown",
|
|
2680
|
-
sourceHandle: e.source.handle,
|
|
2681
|
-
sourceHandleTypeId,
|
|
2682
|
-
targetNodeId: e.target.nodeId,
|
|
2683
|
-
targetNodeTypeId: targetNode?.typeId || "unknown",
|
|
2684
|
-
targetHandle: e.target.handle,
|
|
2685
|
-
targetHandleTypeId,
|
|
2686
|
-
edgeTypeId,
|
|
2687
|
-
isRunning,
|
|
2688
|
-
hasError,
|
|
2689
|
-
isInvalid: isInvalidEdge,
|
|
2690
|
-
isMissing,
|
|
2691
|
-
},
|
|
2817
|
+
data: mergedEdgeData,
|
|
2692
2818
|
};
|
|
2693
2819
|
});
|
|
2694
2820
|
return { nodes, edges };
|
|
@@ -2844,6 +2970,9 @@ async function upload(parsed, wb, runner) {
|
|
|
2844
2970
|
if (extData.runtime && typeof extData.runtime === "object") {
|
|
2845
2971
|
wb.setRuntimeState(extData.runtime);
|
|
2846
2972
|
}
|
|
2973
|
+
if (extData.custom && typeof extData.custom === "object") {
|
|
2974
|
+
wb.setCustomData(extData.custom);
|
|
2975
|
+
}
|
|
2847
2976
|
if (runner.isRunning()) {
|
|
2848
2977
|
await runner.applySnapshotFull({
|
|
2849
2978
|
def: wb.def,
|
|
@@ -2860,6 +2989,9 @@ async function upload(parsed, wb, runner) {
|
|
|
2860
2989
|
runner.setInputs(nodeId, map, { dry: true });
|
|
2861
2990
|
}
|
|
2862
2991
|
}
|
|
2992
|
+
if (extData) {
|
|
2993
|
+
await runner.setExtData(extData);
|
|
2994
|
+
}
|
|
2863
2995
|
}
|
|
2864
2996
|
}
|
|
2865
2997
|
|
|
@@ -3656,15 +3788,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
3656
3788
|
const graphUiTick = useWorkbenchGraphUiTick(wb);
|
|
3657
3789
|
const versionTick = useWorkbenchVersionTick(runner);
|
|
3658
3790
|
const valuesTick = versionTick + graphTick + graphUiTick;
|
|
3659
|
-
// Keep local runMode state loosely in sync with runner status.
|
|
3660
|
-
// - Seed from runner.getRunMode() on mount if available.
|
|
3661
|
-
// - On status events, update only when a non-undefined runMode is reported,
|
|
3662
|
-
// so the UI preserves the last selected mode after stop().
|
|
3663
3791
|
useEffect(() => {
|
|
3664
|
-
const initialMode = runner.getRunMode();
|
|
3665
|
-
if (initialMode) {
|
|
3666
|
-
setRunModeState(initialMode);
|
|
3667
|
-
}
|
|
3668
3792
|
const offRunnerStatus = runner.on("status", (status) => {
|
|
3669
3793
|
if (status.runMode) {
|
|
3670
3794
|
setRunModeState(status.runMode);
|
|
@@ -3824,12 +3948,13 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
3824
3948
|
workbench.setRuntimeState(metadata);
|
|
3825
3949
|
const fullUiState = workbench.getUIState();
|
|
3826
3950
|
const uiWithoutViewport = excludeViewportFromUIState(fullUiState);
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
});
|
|
3951
|
+
// Use updateExtData for efficient batched partial updates
|
|
3952
|
+
const updates = [];
|
|
3953
|
+
if (Object.keys(uiWithoutViewport || {}).length > 0) {
|
|
3954
|
+
updates.push({ path: "ui", value: uiWithoutViewport });
|
|
3955
|
+
}
|
|
3956
|
+
updates.push({ path: "runtime", value: metadata });
|
|
3957
|
+
await graphRunner.updateExtData(updates);
|
|
3833
3958
|
}
|
|
3834
3959
|
catch (err) {
|
|
3835
3960
|
console.warn("[WorkbenchContext] Failed to save runtime metadata:", err);
|
|
@@ -4262,6 +4387,18 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4262
4387
|
else if (changeType === "selection") {
|
|
4263
4388
|
reason = "selection";
|
|
4264
4389
|
}
|
|
4390
|
+
else if (changeType === "customNodeData") {
|
|
4391
|
+
reason = "custom-node-data";
|
|
4392
|
+
}
|
|
4393
|
+
else if (changeType === "customEdgeData") {
|
|
4394
|
+
reason = "custom-edge-data";
|
|
4395
|
+
}
|
|
4396
|
+
else if (changeType === "customMetaData") {
|
|
4397
|
+
reason = "custom-meta";
|
|
4398
|
+
}
|
|
4399
|
+
else if (changeType === "customData") {
|
|
4400
|
+
reason = "custom";
|
|
4401
|
+
}
|
|
4265
4402
|
}
|
|
4266
4403
|
await saveUiRuntimeMetadata(wb, runner);
|
|
4267
4404
|
const history = await runner
|
|
@@ -4367,27 +4504,11 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4367
4504
|
};
|
|
4368
4505
|
}, [runner, wb]);
|
|
4369
4506
|
const isRunning = useCallback(() => runner.isRunning(), [runner]);
|
|
4370
|
-
const getRunMode = useCallback(() => runner.getRunMode(), [runner]);
|
|
4371
|
-
const stop = useCallback(() => runner.stop(), [runner]);
|
|
4372
|
-
// Run mode actions
|
|
4373
4507
|
const setRunMode = useCallback((mode) => {
|
|
4374
4508
|
if (mode === runMode)
|
|
4375
4509
|
return;
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
// Use setRunMode to change run mode without rebuilding
|
|
4379
|
-
try {
|
|
4380
|
-
runner.setRunMode(mode);
|
|
4381
|
-
setRunModeState(mode);
|
|
4382
|
-
}
|
|
4383
|
-
catch (err) {
|
|
4384
|
-
console.error("Failed to set run mode:", err);
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
|
-
else {
|
|
4388
|
-
// Just update state if not running (will be applied on next launch)
|
|
4389
|
-
setRunModeState(mode);
|
|
4390
|
-
}
|
|
4510
|
+
runner.setRunMode(mode);
|
|
4511
|
+
setRunModeState(mode);
|
|
4391
4512
|
}, [runMode, runner]);
|
|
4392
4513
|
const runNodeAction = useCallback(async (nodeId) => {
|
|
4393
4514
|
await runner.computeNode(nodeId);
|
|
@@ -4489,8 +4610,6 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4489
4610
|
removeRegistryError,
|
|
4490
4611
|
removeInputValidationError,
|
|
4491
4612
|
isRunning,
|
|
4492
|
-
getRunMode,
|
|
4493
|
-
stop,
|
|
4494
4613
|
runMode,
|
|
4495
4614
|
setRunMode,
|
|
4496
4615
|
runNode: runNodeAction,
|
|
@@ -4531,8 +4650,6 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
|
4531
4650
|
events,
|
|
4532
4651
|
clearEvents,
|
|
4533
4652
|
isRunning,
|
|
4534
|
-
getRunMode,
|
|
4535
|
-
stop,
|
|
4536
4653
|
runMode,
|
|
4537
4654
|
setRunMode,
|
|
4538
4655
|
runNodeAction,
|
|
@@ -5566,6 +5683,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5566
5683
|
status: n.data.status,
|
|
5567
5684
|
validation: n.data.validation,
|
|
5568
5685
|
inputConnected: n.data.inputConnected,
|
|
5686
|
+
custom: n.data.custom,
|
|
5569
5687
|
},
|
|
5570
5688
|
});
|
|
5571
5689
|
return lod.isEqual(pick(a), pick(b));
|
|
@@ -5581,6 +5699,14 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5581
5699
|
style: e.style,
|
|
5582
5700
|
label: e.label,
|
|
5583
5701
|
type: e.type,
|
|
5702
|
+
data: e.data && {
|
|
5703
|
+
edgeTypeId: e.data.edgeTypeId,
|
|
5704
|
+
isRunning: e.data.isRunning,
|
|
5705
|
+
hasError: e.data.hasError,
|
|
5706
|
+
isInvalid: e.data.isInvalid,
|
|
5707
|
+
isMissing: e.data.isMissing,
|
|
5708
|
+
custom: e.data.custom,
|
|
5709
|
+
},
|
|
5584
5710
|
});
|
|
5585
5711
|
return lod.isEqual(pick(a), pick(b));
|
|
5586
5712
|
};
|
|
@@ -5668,6 +5794,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5668
5794
|
selectedEdgeIds: new Set(sel.edges),
|
|
5669
5795
|
getDefaultNodeSize,
|
|
5670
5796
|
ui,
|
|
5797
|
+
customData: wb.getCustomData(),
|
|
5671
5798
|
});
|
|
5672
5799
|
// Retain references for unchanged items
|
|
5673
5800
|
const stableNodes = retainStabilityById(prevNodesRef.current, out.nodes, isSameNode);
|
|
@@ -6223,9 +6350,6 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6223
6350
|
if (isConnecting) {
|
|
6224
6351
|
return (jsxs("button", { className: "border rounded px-2 py-1.5 text-gray-500 border-gray-400 flex items-center gap-1 disabled:opacity-50", disabled: true, title: "Connecting to backend...", children: [jsx(ClockClockwiseIcon, { size: 16, className: "animate-spin" }), jsx("span", { className: "font-medium ml-1", children: "Connecting..." })] }));
|
|
6225
6352
|
}
|
|
6226
|
-
if (isGraphRunning) {
|
|
6227
|
-
return (jsxs("button", { className: "border rounded px-2 py-1.5 text-red-700 border-red-600 flex items-center gap-1 disabled:opacity-50 disabled:text-gray-400 disabled:border-gray-300", onClick: () => runner.stop(), disabled: !canControl, title: canControl ? "Stop engine" : "Waiting for connection", children: [jsx(StopIcon, { size: 16, weight: "fill" }), jsx("span", { className: "font-medium ml-1", children: "Stop" })] }));
|
|
6228
|
-
}
|
|
6229
6353
|
return (jsxs("button", { className: "border rounded px-2 py-1.5 text-green-700 border-green-600 flex items-center gap-1 disabled:text-gray-400 disabled:border-gray-300 disabled:opacity-50", onClick: (evt) => {
|
|
6230
6354
|
if (evt.shiftKey && !confirm("Invalidate and re-run graph?"))
|
|
6231
6355
|
return;
|
|
@@ -6612,9 +6736,7 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6612
6736
|
if (mode !== runMode) {
|
|
6613
6737
|
await setRunMode(mode);
|
|
6614
6738
|
}
|
|
6615
|
-
},
|
|
6616
|
-
? "Stop before switching run mode"
|
|
6617
|
-
: "Select run mode", children: [jsx("option", { value: "manual", children: "Manual" }), jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsx(TreeStructureIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsx(CornersOutIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsx(DownloadIcon, { size: 24 }) }), jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsx(UploadIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
|
|
6739
|
+
}, title: "Select run mode", children: [jsx("option", { value: "manual", children: "Manual" }), jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsx(TreeStructureIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsx(CornersOutIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsx(DownloadIcon, { size: 24 }) }), jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsx(UploadIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
|
|
6618
6740
|
await downloadCanvasThumbnail(canvasContainerRef.current);
|
|
6619
6741
|
}, title: "Download Flow Thumbnail (SVG)", children: jsx(ImageIcon, { size: 24 }) }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsx(BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsx(ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxs("div", { className: "flex flex-1 min-h-0", children: [jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize }) }), jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
|
|
6620
6742
|
}
|