@bian-womp/spark-workbench 0.2.93 → 0.3.0
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 +252 -293
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/AbstractWorkbench.d.ts +8 -7
- package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +4 -2
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +5 -2
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/examples/reactflow/App.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +1 -0
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts +1 -2
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts +1 -3
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +8 -8
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts +1 -4
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuHandlers.d.ts +6 -2
- package/lib/cjs/src/misc/context-menu/ContextMenuHandlers.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts +1 -1
- package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts +1 -1
- package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +9 -7
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +18 -7
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +7 -4
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +11 -9
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +255 -296
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/AbstractWorkbench.d.ts +8 -7
- package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +4 -2
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +5 -2
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/examples/reactflow/App.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +1 -0
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts +1 -2
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts +1 -3
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +8 -8
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts +1 -4
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuHandlers.d.ts +6 -2
- package/lib/esm/src/misc/context-menu/ContextMenuHandlers.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts +1 -1
- package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts +1 -1
- package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +9 -7
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +18 -7
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +7 -4
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +11 -9
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -135,8 +135,8 @@ class AbstractWorkbench {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
class InMemoryWorkbench extends AbstractWorkbench {
|
|
138
|
-
constructor() {
|
|
139
|
-
super(
|
|
138
|
+
constructor(args) {
|
|
139
|
+
super(args);
|
|
140
140
|
this._def = { nodes: [], edges: [] };
|
|
141
141
|
this.listeners = new Map();
|
|
142
142
|
this.positions = {};
|
|
@@ -150,12 +150,17 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
150
150
|
this.viewport = null;
|
|
151
151
|
this.historyState = undefined;
|
|
152
152
|
this.copiedData = null;
|
|
153
|
+
this._registry = sparkGraph.createSimpleGraphRegistry();
|
|
153
154
|
}
|
|
154
155
|
get def() {
|
|
155
156
|
return this._def;
|
|
156
157
|
}
|
|
158
|
+
get registry() {
|
|
159
|
+
return this._registry;
|
|
160
|
+
}
|
|
157
161
|
setRegistry(registry) {
|
|
158
|
-
this.
|
|
162
|
+
this._registry = registry;
|
|
163
|
+
this.emit("registryChanged", { registry });
|
|
159
164
|
}
|
|
160
165
|
async load(def) {
|
|
161
166
|
this._def = { nodes: [...def.nodes], edges: [...def.edges] };
|
|
@@ -922,30 +927,20 @@ class AbstractGraphRunner {
|
|
|
922
927
|
this.engine.dispose();
|
|
923
928
|
this.engine = undefined;
|
|
924
929
|
// Emit status but keep runtime alive
|
|
925
|
-
if (this.
|
|
926
|
-
this.
|
|
927
|
-
this.emit("status", { running: false,
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
if (!this.engine
|
|
932
|
-
throw new Error("
|
|
933
|
-
}
|
|
934
|
-
// Wait for current engine to be idle
|
|
935
|
-
await this.whenIdle();
|
|
936
|
-
// Capture current state
|
|
937
|
-
const currentInputs = { ...this.stagedInputs };
|
|
938
|
-
// Stop current engine
|
|
939
|
-
this.stop();
|
|
940
|
-
// Ensure runtime is in a clean state (resumed)
|
|
941
|
-
this.runtime.resume();
|
|
942
|
-
// Create and launch new engine (to be implemented by subclasses)
|
|
943
|
-
await this.createAndLaunchEngine(opts);
|
|
944
|
-
// Re-apply staged inputs to new engine using runner's setInputs method
|
|
945
|
-
// This ensures consistency and proper handling of staged inputs
|
|
946
|
-
for (const [nodeId, map] of Object.entries(currentInputs)) {
|
|
947
|
-
await this.setInputs(nodeId, map);
|
|
930
|
+
if (this.runMode) {
|
|
931
|
+
this.runMode = undefined;
|
|
932
|
+
this.emit("status", { running: false, runMode: undefined });
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
setRunMode(runMode) {
|
|
936
|
+
if (!this.engine) {
|
|
937
|
+
throw new Error("Cannot set run mode: engine not running");
|
|
948
938
|
}
|
|
939
|
+
// Update engine run mode (this will update pause/resume state)
|
|
940
|
+
this.engine.setRunMode(runMode);
|
|
941
|
+
// Update local state and emit status event
|
|
942
|
+
this.runMode = runMode;
|
|
943
|
+
this.emit("status", { running: true, runMode: this.runMode });
|
|
949
944
|
}
|
|
950
945
|
getInputDefaults(def) {
|
|
951
946
|
const out = {};
|
|
@@ -975,16 +970,16 @@ class AbstractGraphRunner {
|
|
|
975
970
|
this.engine = undefined;
|
|
976
971
|
this.runtime?.dispose();
|
|
977
972
|
this.runtime = undefined;
|
|
978
|
-
if (this.
|
|
979
|
-
this.
|
|
980
|
-
this.emit("status", { running: false,
|
|
973
|
+
if (this.runMode) {
|
|
974
|
+
this.runMode = undefined;
|
|
975
|
+
this.emit("status", { running: false, runMode: undefined });
|
|
981
976
|
}
|
|
982
977
|
}
|
|
983
978
|
isRunning() {
|
|
984
979
|
return !!this.engine;
|
|
985
980
|
}
|
|
986
|
-
|
|
987
|
-
return this.
|
|
981
|
+
getRunMode() {
|
|
982
|
+
return this.runMode;
|
|
988
983
|
}
|
|
989
984
|
// Optional undo/redo support
|
|
990
985
|
async undo() {
|
|
@@ -1079,7 +1074,7 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1079
1074
|
if (!this.runtime)
|
|
1080
1075
|
throw new Error("Runtime not built");
|
|
1081
1076
|
// Use shared engine factory
|
|
1082
|
-
this.engine = sparkGraph.
|
|
1077
|
+
this.engine = new sparkGraph.UnifiedEngine(this.runtime, opts?.runMode);
|
|
1083
1078
|
if (!this.engine)
|
|
1084
1079
|
throw new Error("Failed to create engine");
|
|
1085
1080
|
this.engine.on("value", (e) => this.emit("value", e));
|
|
@@ -1087,36 +1082,34 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1087
1082
|
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
1088
1083
|
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
1089
1084
|
this.engine.launch(opts?.invalidate);
|
|
1090
|
-
this.
|
|
1091
|
-
this.emit("status", { running: true,
|
|
1085
|
+
this.runMode = opts?.runMode ?? "manual";
|
|
1086
|
+
this.emit("status", { running: true, runMode: this.runMode });
|
|
1092
1087
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
1093
1088
|
this.engine.setInputs(nodeId, map);
|
|
1094
1089
|
}
|
|
1095
1090
|
}
|
|
1096
|
-
async
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
await eng.step();
|
|
1091
|
+
async computeNode(nodeId, options) {
|
|
1092
|
+
if (this.engine)
|
|
1093
|
+
await this.engine.computeNode(nodeId, options);
|
|
1100
1094
|
}
|
|
1101
|
-
async
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
await eng.computeNode(nodeId);
|
|
1095
|
+
async runFromHere(nodeId) {
|
|
1096
|
+
if (this.engine)
|
|
1097
|
+
await this.engine.runFromHere(nodeId);
|
|
1105
1098
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1099
|
+
cancelNodeRuns(nodeIds) {
|
|
1100
|
+
if (this.engine) {
|
|
1101
|
+
this.engine.cancelNodeRuns(nodeIds);
|
|
1102
|
+
}
|
|
1110
1103
|
}
|
|
1111
1104
|
getOutputs(def) {
|
|
1112
1105
|
const out = {};
|
|
1113
|
-
if (!this.
|
|
1106
|
+
if (!this.engine)
|
|
1114
1107
|
return out;
|
|
1115
1108
|
for (const n of def.nodes) {
|
|
1116
1109
|
const desc = this.registry.nodes.get(n.typeId);
|
|
1117
1110
|
const handles = Object.keys(desc?.outputs ?? {});
|
|
1118
1111
|
for (const h of handles) {
|
|
1119
|
-
const v = this.
|
|
1112
|
+
const v = this.engine.getOutput(n.nodeId, h);
|
|
1120
1113
|
if (v !== undefined) {
|
|
1121
1114
|
if (!out[n.nodeId])
|
|
1122
1115
|
out[n.nodeId] = {};
|
|
@@ -1149,19 +1142,8 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1149
1142
|
return out;
|
|
1150
1143
|
}
|
|
1151
1144
|
triggerExternal(nodeId, event, options) {
|
|
1152
|
-
//
|
|
1153
|
-
|
|
1154
|
-
if (options?.dry && !wasPaused && this.runtime) {
|
|
1155
|
-
this.runtime.pause();
|
|
1156
|
-
}
|
|
1157
|
-
try {
|
|
1158
|
-
this.engine?.triggerExternal(nodeId, event);
|
|
1159
|
-
}
|
|
1160
|
-
finally {
|
|
1161
|
-
if (options?.dry && !wasPaused && this.runtime) {
|
|
1162
|
-
this.runtime.resume();
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1145
|
+
// Engine handles dry option via AbstractEngine
|
|
1146
|
+
this.engine?.triggerExternal(nodeId, event, options);
|
|
1165
1147
|
}
|
|
1166
1148
|
// Batch update multiple inputs on a node and trigger a single run
|
|
1167
1149
|
setInputs(nodeId, inputs, options) {
|
|
@@ -1177,40 +1159,23 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
1177
1159
|
this.stagedInputs[nodeId][handle] = value;
|
|
1178
1160
|
}
|
|
1179
1161
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
this.runtime.pause();
|
|
1184
|
-
}
|
|
1185
|
-
try {
|
|
1186
|
-
if (this.engine) {
|
|
1187
|
-
this.engine.setInputs(nodeId, inputs);
|
|
1188
|
-
}
|
|
1189
|
-
else {
|
|
1190
|
-
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
1191
|
-
console.warn("Engine does not exists");
|
|
1192
|
-
for (const [handle, value] of Object.entries(inputs)) {
|
|
1193
|
-
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1162
|
+
if (this.engine) {
|
|
1163
|
+
// Engine handles dry option via AbstractEngine
|
|
1164
|
+
this.engine.setInputs(nodeId, inputs, options);
|
|
1196
1165
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1166
|
+
else {
|
|
1167
|
+
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
1168
|
+
// Note: dry option doesn't apply when engine doesn't exist (no execution to prevent)
|
|
1169
|
+
console.warn("Engine does not exists");
|
|
1170
|
+
for (const [handle, value] of Object.entries(inputs)) {
|
|
1171
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
1200
1172
|
}
|
|
1201
1173
|
}
|
|
1202
1174
|
}
|
|
1203
1175
|
copyOutputs(fromNodeId, toNodeId, options) {
|
|
1204
|
-
if (
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
const fromNode = this.runtime.getNodeData(fromNodeId);
|
|
1208
|
-
if (!fromNode?.outputs)
|
|
1209
|
-
return;
|
|
1210
|
-
// Copy outputs to target node using hydrate
|
|
1211
|
-
// hydrate already pauses internally, so we don't need to handle dry option here
|
|
1212
|
-
// reemit: !options?.dry means don't propagate downstream if dry mode
|
|
1213
|
-
this.runtime.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { reemit: !options?.dry });
|
|
1176
|
+
if (this.engine) {
|
|
1177
|
+
this.engine.copyOutputs(fromNodeId, toNodeId, options);
|
|
1178
|
+
}
|
|
1214
1179
|
}
|
|
1215
1180
|
async snapshotFull() {
|
|
1216
1181
|
const def = undefined; // UI will supply def/positions on download for local
|
|
@@ -1311,7 +1276,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1311
1276
|
}
|
|
1312
1277
|
this.registryFetching = true;
|
|
1313
1278
|
try {
|
|
1314
|
-
const desc = await client.describeRegistry();
|
|
1279
|
+
const desc = await client.api.describeRegistry();
|
|
1315
1280
|
// Register types
|
|
1316
1281
|
for (const t of desc.types) {
|
|
1317
1282
|
if (t.options) {
|
|
@@ -1413,7 +1378,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1413
1378
|
}
|
|
1414
1379
|
}
|
|
1415
1380
|
/**
|
|
1416
|
-
* Build
|
|
1381
|
+
* Build RemoteRuntimeClient config from RemoteExecutionBackend config.
|
|
1417
1382
|
*/
|
|
1418
1383
|
buildClientConfig(backend) {
|
|
1419
1384
|
if (backend.kind === "remote-http") {
|
|
@@ -1436,7 +1401,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1436
1401
|
*/
|
|
1437
1402
|
setupClientSubscriptions(client) {
|
|
1438
1403
|
// Subscribe to transport status changes
|
|
1439
|
-
// Convert
|
|
1404
|
+
// Convert RemoteRuntimeClient.TransportStatus to IGraphRunner.TransportStatus
|
|
1440
1405
|
// Only emit status if it matches this runner's ID
|
|
1441
1406
|
this.transportStatusUnsubscribe = client.onTransportStatus((status) => {
|
|
1442
1407
|
if (status.runnerId && status.runnerId !== this.runnerId)
|
|
@@ -1486,7 +1451,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1486
1451
|
}
|
|
1487
1452
|
};
|
|
1488
1453
|
// Create client with wrapped custom event handler
|
|
1489
|
-
const client = new sparkRemote.
|
|
1454
|
+
const client = new sparkRemote.RemoteRuntimeClient(clientConfig, {
|
|
1490
1455
|
onCustomEvent: wrappedOnCustomEvent,
|
|
1491
1456
|
runnerId: this.runnerId,
|
|
1492
1457
|
});
|
|
@@ -1536,7 +1501,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1536
1501
|
// Auto-handle registry-changed invalidations from remote
|
|
1537
1502
|
// We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
|
|
1538
1503
|
this.ensureClient().then(async (client) => {
|
|
1539
|
-
const eng = client.
|
|
1504
|
+
const eng = client.engine;
|
|
1540
1505
|
if (!this.listenersBound) {
|
|
1541
1506
|
eng.on("invalidate", async (e) => {
|
|
1542
1507
|
if (e.reason === "registry-changed") {
|
|
@@ -1600,7 +1565,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1600
1565
|
// Remote: forward update and await completion
|
|
1601
1566
|
const client = await this.ensureClient();
|
|
1602
1567
|
try {
|
|
1603
|
-
await client.update(def, options);
|
|
1568
|
+
await client.api.update(def, options);
|
|
1604
1569
|
this.emit("invalidate", { reason: "graph-updated" });
|
|
1605
1570
|
this.lastDef = def;
|
|
1606
1571
|
}
|
|
@@ -1613,13 +1578,13 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1613
1578
|
super.launch(def, opts);
|
|
1614
1579
|
// Remote: build remotely then launch
|
|
1615
1580
|
this.ensureClient().then(async (client) => {
|
|
1616
|
-
await client.build(def);
|
|
1581
|
+
await client.api.build(def);
|
|
1617
1582
|
// Signal UI after remote build as well
|
|
1618
1583
|
this.emit("invalidate", { reason: "graph-built" });
|
|
1619
1584
|
this.lastDef = def;
|
|
1620
1585
|
// Hydrate current remote inputs/outputs (including defaults) into cache
|
|
1621
1586
|
try {
|
|
1622
|
-
const snap = await client.snapshot();
|
|
1587
|
+
const snap = await client.api.snapshot();
|
|
1623
1588
|
for (const [nodeId, map] of Object.entries(snap.inputs || {})) {
|
|
1624
1589
|
for (const [handle, value] of Object.entries(map || {})) {
|
|
1625
1590
|
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
@@ -1648,9 +1613,9 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1648
1613
|
async createAndLaunchEngine(opts) {
|
|
1649
1614
|
const client = await this.ensureClient();
|
|
1650
1615
|
// Configure and launch engine on the backend
|
|
1651
|
-
await client.launch(opts);
|
|
1616
|
+
await client.api.launch(opts);
|
|
1652
1617
|
// Get the remote engine proxy and wire up event listeners
|
|
1653
|
-
const eng = client.
|
|
1618
|
+
const eng = client.engine;
|
|
1654
1619
|
if (!this.listenersBound) {
|
|
1655
1620
|
eng.on("value", (e) => {
|
|
1656
1621
|
this.valueCache.set(`${e.nodeId}.${e.handle}`, {
|
|
@@ -1666,11 +1631,11 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1666
1631
|
this.listenersBound = true;
|
|
1667
1632
|
}
|
|
1668
1633
|
this.engine = eng;
|
|
1669
|
-
this.
|
|
1670
|
-
this.emit("status", { running: true,
|
|
1634
|
+
this.runMode = opts?.runMode ?? "manual";
|
|
1635
|
+
this.emit("status", { running: true, runMode: this.runMode });
|
|
1671
1636
|
// Re-apply staged inputs using client.setInputs for consistency
|
|
1672
1637
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
1673
|
-
await eng.setInputs(nodeId, map).catch(() => {
|
|
1638
|
+
await eng.setInputs(nodeId, map, undefined).catch(() => {
|
|
1674
1639
|
// Ignore errors during launch - inputs will be set when user calls setInputs
|
|
1675
1640
|
});
|
|
1676
1641
|
}
|
|
@@ -1693,45 +1658,27 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1693
1658
|
await this.createAndLaunchEngine(opts);
|
|
1694
1659
|
});
|
|
1695
1660
|
}
|
|
1696
|
-
|
|
1661
|
+
setRunMode(runMode) {
|
|
1697
1662
|
if (!this.engine) {
|
|
1698
|
-
throw new Error("
|
|
1699
|
-
}
|
|
1700
|
-
// Wait for current engine to be idle
|
|
1701
|
-
await this.whenIdle();
|
|
1702
|
-
// Capture current state
|
|
1703
|
-
const currentInputs = { ...this.stagedInputs };
|
|
1704
|
-
// For remote runners, we cannot call this.stop() because it sends a Dispose
|
|
1705
|
-
// command that destroys the graphRuntime on the backend. Instead, we rely on
|
|
1706
|
-
// the backend's launch() method to dispose the old engine and create a new one.
|
|
1707
|
-
// Reconfigure engine on the backend (this will dispose old engine and create new one)
|
|
1708
|
-
const client = await this.ensureClient();
|
|
1709
|
-
await client.launch(opts);
|
|
1710
|
-
// Get the remote engine proxy (should be the same RemoteEngine instance)
|
|
1711
|
-
const eng = client.getEngine();
|
|
1712
|
-
// Update local state to reflect new engine kind
|
|
1713
|
-
// Note: The RemoteEngine instance itself doesn't change, but the backend engine does
|
|
1714
|
-
this.engine = eng;
|
|
1715
|
-
this.runningKind = opts?.engine ?? "push";
|
|
1716
|
-
this.emit("status", { running: true, engine: this.runningKind });
|
|
1717
|
-
// Re-apply staged inputs using client.setInputs for consistency
|
|
1718
|
-
for (const [nodeId, map] of Object.entries(currentInputs)) {
|
|
1719
|
-
await eng.setInputs(nodeId, map).catch(() => {
|
|
1720
|
-
// Ignore errors during engine switch - inputs will be set when user calls setInputs
|
|
1721
|
-
});
|
|
1663
|
+
throw new Error("Cannot set run mode: engine not running");
|
|
1722
1664
|
}
|
|
1665
|
+
// Update engine run mode (sends SetRunMode command to backend)
|
|
1666
|
+
this.engine.setRunMode(runMode);
|
|
1667
|
+
// Update local state and emit status event
|
|
1668
|
+
this.runMode = runMode;
|
|
1669
|
+
this.emit("status", { running: true, runMode: this.runMode });
|
|
1723
1670
|
}
|
|
1724
|
-
async
|
|
1671
|
+
async computeNode(nodeId, options) {
|
|
1725
1672
|
const client = await this.ensureClient();
|
|
1726
|
-
await client.
|
|
1673
|
+
await client.engine.computeNode(nodeId, options);
|
|
1727
1674
|
}
|
|
1728
|
-
async
|
|
1675
|
+
async runFromHere(nodeId) {
|
|
1729
1676
|
const client = await this.ensureClient();
|
|
1730
|
-
await client.
|
|
1677
|
+
await client.engine.runFromHere(nodeId);
|
|
1731
1678
|
}
|
|
1732
|
-
async
|
|
1679
|
+
async cancelNodeRuns(nodeIds) {
|
|
1733
1680
|
const client = await this.ensureClient();
|
|
1734
|
-
await client.
|
|
1681
|
+
await client.engine.cancelNodeRuns(nodeIds);
|
|
1735
1682
|
}
|
|
1736
1683
|
async setInputs(nodeId, inputs, options) {
|
|
1737
1684
|
// Update staged inputs (for getInputs to work correctly)
|
|
@@ -1747,7 +1694,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1747
1694
|
}
|
|
1748
1695
|
try {
|
|
1749
1696
|
const client = await this.ensureClient();
|
|
1750
|
-
await client.
|
|
1697
|
+
await client.engine.setInputs(nodeId, inputs, options);
|
|
1751
1698
|
}
|
|
1752
1699
|
catch (err) {
|
|
1753
1700
|
// Emit synthetic events if connection fails
|
|
@@ -1759,20 +1706,20 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1759
1706
|
}
|
|
1760
1707
|
async copyOutputs(fromNodeId, toNodeId, options) {
|
|
1761
1708
|
const client = await this.ensureClient();
|
|
1762
|
-
await client.
|
|
1709
|
+
await client.engine.copyOutputs(fromNodeId, toNodeId, options);
|
|
1763
1710
|
}
|
|
1764
1711
|
async triggerExternal(nodeId, event, options) {
|
|
1765
1712
|
const client = await this.ensureClient();
|
|
1766
|
-
await client.
|
|
1713
|
+
await client.engine.triggerExternal(nodeId, event, options);
|
|
1767
1714
|
}
|
|
1768
1715
|
async setViewport(viewport) {
|
|
1769
1716
|
const client = await this.ensureClient();
|
|
1770
|
-
await client.setViewport(viewport);
|
|
1717
|
+
await client.api.setViewport(viewport);
|
|
1771
1718
|
}
|
|
1772
1719
|
async coerce(from, to, value) {
|
|
1773
1720
|
const client = await this.ensureClient();
|
|
1774
1721
|
try {
|
|
1775
|
-
return await client.coerce(from, to, value);
|
|
1722
|
+
return await client.api.coerce(from, to, value);
|
|
1776
1723
|
}
|
|
1777
1724
|
catch {
|
|
1778
1725
|
return value;
|
|
@@ -1780,12 +1727,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1780
1727
|
}
|
|
1781
1728
|
async setExtData(data) {
|
|
1782
1729
|
const client = await this.ensureClient();
|
|
1783
|
-
await client.setExtData(data);
|
|
1730
|
+
await client.api.setExtData(data);
|
|
1784
1731
|
}
|
|
1785
1732
|
async commit(reason) {
|
|
1786
1733
|
const client = await this.ensureClient();
|
|
1787
1734
|
try {
|
|
1788
|
-
const history = await client.commit(reason);
|
|
1735
|
+
const history = await client.api.commit(reason);
|
|
1789
1736
|
return history;
|
|
1790
1737
|
}
|
|
1791
1738
|
catch (err) {
|
|
@@ -1796,7 +1743,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1796
1743
|
async undo() {
|
|
1797
1744
|
const client = await this.ensureClient();
|
|
1798
1745
|
try {
|
|
1799
|
-
return await client.undo();
|
|
1746
|
+
return await client.api.undo();
|
|
1800
1747
|
}
|
|
1801
1748
|
catch {
|
|
1802
1749
|
return false;
|
|
@@ -1805,7 +1752,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1805
1752
|
async redo() {
|
|
1806
1753
|
const client = await this.ensureClient();
|
|
1807
1754
|
try {
|
|
1808
|
-
return await client.redo();
|
|
1755
|
+
return await client.api.redo();
|
|
1809
1756
|
}
|
|
1810
1757
|
catch {
|
|
1811
1758
|
return false;
|
|
@@ -1814,7 +1761,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1814
1761
|
async snapshotFull() {
|
|
1815
1762
|
const client = await this.ensureClient();
|
|
1816
1763
|
try {
|
|
1817
|
-
return await client.snapshotFull();
|
|
1764
|
+
return await client.api.snapshotFull();
|
|
1818
1765
|
}
|
|
1819
1766
|
catch {
|
|
1820
1767
|
return { def: undefined, environment: {}, inputs: {}, outputs: {} };
|
|
@@ -1825,7 +1772,9 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1825
1772
|
this.hydrateValueCache(payload, { dry: options?.dry });
|
|
1826
1773
|
// Then sync with backend
|
|
1827
1774
|
const client = await this.ensureClient();
|
|
1828
|
-
await client.applySnapshotFull(payload, {
|
|
1775
|
+
await client.api.applySnapshotFull(payload, {
|
|
1776
|
+
skipBuild: options?.skipBuild,
|
|
1777
|
+
});
|
|
1829
1778
|
}
|
|
1830
1779
|
/**
|
|
1831
1780
|
* Hydrates the local valueCache from a snapshot and emits value events.
|
|
@@ -1869,12 +1818,12 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1869
1818
|
async setEnvironment(env, opts) {
|
|
1870
1819
|
// Use client if available, otherwise ensure client and then set environment
|
|
1871
1820
|
if (this.client) {
|
|
1872
|
-
await this.client.setEnvironment(env, opts);
|
|
1821
|
+
await this.client.api.setEnvironment(env, opts);
|
|
1873
1822
|
}
|
|
1874
1823
|
else {
|
|
1875
1824
|
try {
|
|
1876
1825
|
const client = await this.ensureClient();
|
|
1877
|
-
await client.setEnvironment(env, opts);
|
|
1826
|
+
await client.api.setEnvironment(env, opts);
|
|
1878
1827
|
}
|
|
1879
1828
|
catch {
|
|
1880
1829
|
// Silently fail if connection not available
|
|
@@ -1882,7 +1831,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1882
1831
|
}
|
|
1883
1832
|
}
|
|
1884
1833
|
getEnvironment() {
|
|
1885
|
-
// Interface requires sync return, but
|
|
1834
|
+
// Interface requires sync return, but RemoteRuntimeClient.getEnvironment() is async.
|
|
1886
1835
|
// Returns undefined synchronously; callers needing the actual value should:
|
|
1887
1836
|
// - Use snapshotFull() which includes environment
|
|
1888
1837
|
// - Call client.getEnvironment() directly if they have access to the client
|
|
@@ -3649,9 +3598,10 @@ function computeInvalidatedFromMetadata(metadata) {
|
|
|
3649
3598
|
const maxInputTime = Math.max(...Object.values(lastInputAt));
|
|
3650
3599
|
return maxInputTime > (lastSuccessAt ?? lastRunAt ?? 0);
|
|
3651
3600
|
}
|
|
3652
|
-
function WorkbenchProvider({ wb, runner,
|
|
3601
|
+
function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
3653
3602
|
const [nodeStatus, setNodeStatus] = React.useState({});
|
|
3654
3603
|
const [edgeStatus, setEdgeStatus] = React.useState({});
|
|
3604
|
+
const [runMode, setRunModeState] = React.useState("manual");
|
|
3655
3605
|
const [events, setEvents] = React.useState([]);
|
|
3656
3606
|
const clearEvents = React.useCallback(() => setEvents([]), []);
|
|
3657
3607
|
const [systemErrors, setSystemErrors] = React.useState([]);
|
|
@@ -3716,6 +3666,24 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3716
3666
|
const graphUiTick = useWorkbenchGraphUiTick(wb);
|
|
3717
3667
|
const versionTick = useWorkbenchVersionTick(runner);
|
|
3718
3668
|
const valuesTick = versionTick + graphTick + graphUiTick;
|
|
3669
|
+
// Keep local runMode state loosely in sync with runner status.
|
|
3670
|
+
// - Seed from runner.getRunMode() on mount if available.
|
|
3671
|
+
// - On status events, update only when a non-undefined runMode is reported,
|
|
3672
|
+
// so the UI preserves the last selected mode after stop().
|
|
3673
|
+
React.useEffect(() => {
|
|
3674
|
+
const initialMode = runner.getRunMode();
|
|
3675
|
+
if (initialMode) {
|
|
3676
|
+
setRunModeState(initialMode);
|
|
3677
|
+
}
|
|
3678
|
+
const offRunnerStatus = runner.on("status", (status) => {
|
|
3679
|
+
if (status.runMode) {
|
|
3680
|
+
setRunModeState(status.runMode);
|
|
3681
|
+
}
|
|
3682
|
+
});
|
|
3683
|
+
return () => {
|
|
3684
|
+
offRunnerStatus();
|
|
3685
|
+
};
|
|
3686
|
+
}, [runner]);
|
|
3719
3687
|
// Def and IO values
|
|
3720
3688
|
const inputsMap = React.useMemo(() => runner.getInputs(wb.def), [runner, wb, wb.def, valuesTick]);
|
|
3721
3689
|
const inputDefaultsMap = React.useMemo(() => runner.getInputDefaults(wb.def), [runner, wb, wb.def, valuesTick]);
|
|
@@ -3724,7 +3692,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3724
3692
|
const out = {};
|
|
3725
3693
|
// Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
|
|
3726
3694
|
for (const n of wb.def.nodes) {
|
|
3727
|
-
const effectiveHandles = computeEffectiveHandles(n, registry);
|
|
3695
|
+
const effectiveHandles = computeEffectiveHandles(n, wb.registry);
|
|
3728
3696
|
const outputsDecl = effectiveHandles.outputs;
|
|
3729
3697
|
const handles = Object.keys(outputsDecl);
|
|
3730
3698
|
const cur = {};
|
|
@@ -3738,7 +3706,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3738
3706
|
out[n.nodeId] = cur;
|
|
3739
3707
|
}
|
|
3740
3708
|
return out;
|
|
3741
|
-
}, [wb, wb.def, outputsMap, registry]);
|
|
3709
|
+
}, [wb, wb.def, outputsMap, wb.registry, registryVersion]);
|
|
3742
3710
|
// Initialize nodes and derive invalidated status from persisted metadata
|
|
3743
3711
|
React.useEffect(() => {
|
|
3744
3712
|
const workbenchRuntimeState = wb.getRuntimeState() ?? { nodes: {} };
|
|
@@ -3816,7 +3784,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3816
3784
|
const overrideSize = overrides?.getDefaultNodeSize?.(node.typeId) ?? undefined;
|
|
3817
3785
|
const size = estimateNodeSize({
|
|
3818
3786
|
node,
|
|
3819
|
-
registry,
|
|
3787
|
+
registry: wb.registry,
|
|
3820
3788
|
showValues: true,
|
|
3821
3789
|
overrides: overrideSize,
|
|
3822
3790
|
});
|
|
@@ -3834,7 +3802,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3834
3802
|
curX += maxWidth + H_GAP;
|
|
3835
3803
|
}
|
|
3836
3804
|
wb.setPositions(pos, { commit: true, reason: "auto-layout" });
|
|
3837
|
-
}, [wb, wb.def, registry, overrides?.getDefaultNodeSize]);
|
|
3805
|
+
}, [wb, wb.def, wb.registry, registryVersion, overrides?.getDefaultNodeSize]);
|
|
3838
3806
|
const updateEdgeType = React.useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
3839
3807
|
const triggerExternal = React.useCallback((nodeId, event) => runner.triggerExternal(nodeId, event), [runner]);
|
|
3840
3808
|
const getNodeDisplayName = React.useCallback((nodeId) => {
|
|
@@ -3844,9 +3812,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3844
3812
|
const node = wb.def.nodes.find((n) => n.nodeId === nodeId);
|
|
3845
3813
|
if (!node)
|
|
3846
3814
|
return nodeId;
|
|
3847
|
-
const desc = registry.nodes.get(node.typeId);
|
|
3815
|
+
const desc = wb.registry.nodes.get(node.typeId);
|
|
3848
3816
|
return desc?.displayName || node.typeId;
|
|
3849
|
-
}, [wb, registry]);
|
|
3817
|
+
}, [wb, wb.registry, registryVersion]);
|
|
3850
3818
|
const setNodeName = React.useCallback((nodeId, name) => {
|
|
3851
3819
|
wb.setNodeName(nodeId, name, { commit: true, reason: "rename-node" });
|
|
3852
3820
|
}, [wb]);
|
|
@@ -4162,6 +4130,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4162
4130
|
}
|
|
4163
4131
|
return add("runner", "stats")(s);
|
|
4164
4132
|
});
|
|
4133
|
+
const offWbRegistryChanged = wb.on("registryChanged", (evt) => {
|
|
4134
|
+
setRegistryVersion((v) => v + 1);
|
|
4135
|
+
});
|
|
4165
4136
|
const offWbGraphChanged = wb.on("graphChanged", (event) => {
|
|
4166
4137
|
// Clear validation errors for removed nodes
|
|
4167
4138
|
if (event.change?.type === "removeNode") {
|
|
@@ -4318,10 +4289,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4318
4289
|
// Registry updates: swap registry and refresh graph validation/UI
|
|
4319
4290
|
const offRunnerRegistry = runner.on("registry", async (newReg) => {
|
|
4320
4291
|
try {
|
|
4321
|
-
setRegistry(newReg);
|
|
4322
4292
|
wb.setRegistry(newReg);
|
|
4323
4293
|
// Increment registry version to trigger UI updates
|
|
4324
|
-
setRegistryVersion((v) => v + 1);
|
|
4325
4294
|
// Trigger a graph update so the UI revalidates with new types/enums/nodes
|
|
4326
4295
|
try {
|
|
4327
4296
|
await runner.update(wb.def);
|
|
@@ -4392,6 +4361,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4392
4361
|
offRunnerError();
|
|
4393
4362
|
offRunnerInvalidate();
|
|
4394
4363
|
offRunnerStats();
|
|
4364
|
+
offWbRegistryChanged();
|
|
4395
4365
|
offWbGraphChanged();
|
|
4396
4366
|
offWbGraphUiChangedForLog();
|
|
4397
4367
|
offWbGraphUiChanged();
|
|
@@ -4405,18 +4375,39 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4405
4375
|
offFlowViewport();
|
|
4406
4376
|
offWbRuntimeMetadataChanged();
|
|
4407
4377
|
};
|
|
4408
|
-
}, [runner, wb, setRegistry]);
|
|
4409
|
-
const isRunning = React.useCallback(() => runner.isRunning(), [runner]);
|
|
4410
|
-
const engineKind = React.useCallback(() => runner.getRunningEngine(), [runner]);
|
|
4411
|
-
const start = React.useCallback((engine) => {
|
|
4412
|
-
try {
|
|
4413
|
-
runner.launch(wb.def, { engine });
|
|
4414
|
-
}
|
|
4415
|
-
catch { }
|
|
4416
4378
|
}, [runner, wb]);
|
|
4379
|
+
const isRunning = React.useCallback(() => runner.isRunning(), [runner]);
|
|
4380
|
+
const getRunMode = React.useCallback(() => runner.getRunMode(), [runner]);
|
|
4417
4381
|
const stop = React.useCallback(() => runner.stop(), [runner]);
|
|
4418
|
-
|
|
4419
|
-
const
|
|
4382
|
+
// Run mode actions
|
|
4383
|
+
const setRunMode = React.useCallback((mode) => {
|
|
4384
|
+
if (mode === runMode)
|
|
4385
|
+
return;
|
|
4386
|
+
const wasRunning = runner.isRunning();
|
|
4387
|
+
if (wasRunning) {
|
|
4388
|
+
// Use setRunMode to change run mode without rebuilding
|
|
4389
|
+
try {
|
|
4390
|
+
runner.setRunMode(mode);
|
|
4391
|
+
setRunModeState(mode);
|
|
4392
|
+
}
|
|
4393
|
+
catch (err) {
|
|
4394
|
+
console.error("Failed to set run mode:", err);
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
else {
|
|
4398
|
+
// Just update state if not running (will be applied on next launch)
|
|
4399
|
+
setRunModeState(mode);
|
|
4400
|
+
}
|
|
4401
|
+
}, [runMode, runner]);
|
|
4402
|
+
const runNodeAction = React.useCallback(async (nodeId) => {
|
|
4403
|
+
await runner.computeNode(nodeId);
|
|
4404
|
+
}, [runner]);
|
|
4405
|
+
const runFromHereAction = React.useCallback(async (nodeId) => {
|
|
4406
|
+
await runner.runFromHere(nodeId);
|
|
4407
|
+
}, [runner]);
|
|
4408
|
+
const abortNodeAction = React.useCallback((nodeId) => {
|
|
4409
|
+
runner.cancelNodeRuns([nodeId]);
|
|
4410
|
+
}, [runner]);
|
|
4420
4411
|
const validationByNode = React.useMemo(() => {
|
|
4421
4412
|
const inputs = {};
|
|
4422
4413
|
const outputs = {};
|
|
@@ -4483,8 +4474,6 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4483
4474
|
const value = React.useMemo(() => ({
|
|
4484
4475
|
wb,
|
|
4485
4476
|
runner,
|
|
4486
|
-
registry,
|
|
4487
|
-
setRegistry,
|
|
4488
4477
|
selectedNodeId,
|
|
4489
4478
|
selectedEdgeId,
|
|
4490
4479
|
setSelection,
|
|
@@ -4510,11 +4499,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4510
4499
|
removeRegistryError,
|
|
4511
4500
|
removeInputValidationError,
|
|
4512
4501
|
isRunning,
|
|
4513
|
-
|
|
4514
|
-
start,
|
|
4502
|
+
getRunMode,
|
|
4515
4503
|
stop,
|
|
4516
|
-
|
|
4517
|
-
|
|
4504
|
+
runMode,
|
|
4505
|
+
setRunMode,
|
|
4506
|
+
runNode: runNodeAction,
|
|
4507
|
+
runFromHere: runFromHereAction,
|
|
4508
|
+
abortNode: abortNodeAction,
|
|
4518
4509
|
runAutoLayout,
|
|
4519
4510
|
updateEdgeType,
|
|
4520
4511
|
triggerExternal,
|
|
@@ -4526,8 +4517,6 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4526
4517
|
}), [
|
|
4527
4518
|
wb,
|
|
4528
4519
|
runner,
|
|
4529
|
-
registry,
|
|
4530
|
-
setRegistry,
|
|
4531
4520
|
selectedNodeId,
|
|
4532
4521
|
selectedEdgeId,
|
|
4533
4522
|
setSelection,
|
|
@@ -4552,11 +4541,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
4552
4541
|
events,
|
|
4553
4542
|
clearEvents,
|
|
4554
4543
|
isRunning,
|
|
4555
|
-
|
|
4556
|
-
start,
|
|
4544
|
+
getRunMode,
|
|
4557
4545
|
stop,
|
|
4558
|
-
|
|
4559
|
-
|
|
4546
|
+
runMode,
|
|
4547
|
+
setRunMode,
|
|
4548
|
+
runNodeAction,
|
|
4549
|
+
runFromHereAction,
|
|
4550
|
+
abortNodeAction,
|
|
4560
4551
|
runAutoLayout,
|
|
4561
4552
|
wb,
|
|
4562
4553
|
runner,
|
|
@@ -4574,7 +4565,7 @@ function IssueBadge({ level, title, size = 12, className, }) {
|
|
|
4574
4565
|
return (jsxRuntime.jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ? (jsxRuntime.jsx(react$1.XCircleIcon, { size: size, weight: "fill" })) : (jsxRuntime.jsx(react$1.WarningCircleIcon, { size: size, weight: "fill" })) }));
|
|
4575
4566
|
}
|
|
4576
4567
|
|
|
4577
|
-
function DefaultNodeHeader({ id, typeId, validation, right, showId,
|
|
4568
|
+
function DefaultNodeHeader({ id, typeId, validation, right, showId, }) {
|
|
4578
4569
|
const ctx = useWorkbenchContext();
|
|
4579
4570
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
4580
4571
|
const [editValue, setEditValue] = React.useState("");
|
|
@@ -4589,21 +4580,9 @@ function DefaultNodeHeader({ id, typeId, validation, right, showId, onInvalidate
|
|
|
4589
4580
|
const node = ctx.wb.def.nodes.find((n) => n.nodeId === id);
|
|
4590
4581
|
if (!node)
|
|
4591
4582
|
return id;
|
|
4592
|
-
const desc = ctx.registry.nodes.get(node.typeId);
|
|
4583
|
+
const desc = ctx.wb.registry.nodes.get(node.typeId);
|
|
4593
4584
|
return desc?.displayName || node.typeId;
|
|
4594
4585
|
}, [ctx, id, typeId]);
|
|
4595
|
-
const handleInvalidate = React.useCallback(() => {
|
|
4596
|
-
try {
|
|
4597
|
-
if (onInvalidate)
|
|
4598
|
-
return onInvalidate();
|
|
4599
|
-
const kind = ctx.engineKind?.();
|
|
4600
|
-
if (kind === "pull")
|
|
4601
|
-
ctx.runner.computeNode(id);
|
|
4602
|
-
else
|
|
4603
|
-
ctx.triggerExternal?.(id, { type: "invalidate" });
|
|
4604
|
-
}
|
|
4605
|
-
catch { }
|
|
4606
|
-
}, [ctx, id, onInvalidate]);
|
|
4607
4586
|
const handleDoubleClick = React.useCallback((e) => {
|
|
4608
4587
|
// Only allow editing if typeId is provided (enables renaming)
|
|
4609
4588
|
if (!typeId)
|
|
@@ -4648,10 +4627,16 @@ function DefaultNodeHeader({ id, typeId, validation, right, showId, onInvalidate
|
|
|
4648
4627
|
return (jsxRuntime.jsxs("div", { className: "flex items-center justify-center px-2 border-b border-solid border-gray-500 dark:border-gray-400 text-gray-600 dark:text-gray-300", style: {
|
|
4649
4628
|
maxHeight: NODE_HEADER_HEIGHT_PX,
|
|
4650
4629
|
minHeight: NODE_HEADER_HEIGHT_PX,
|
|
4651
|
-
}, children: [isEditing ? (jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onBlur: handleSave, onKeyDown: handleKeyDown, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), className: "flex-1 h-full text-sm bg-transparent border border-blue-500 rounded px-1 outline-none wb-nodrag", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` } })) : (jsxRuntime.jsx("strong", { className: `react-flow__node-title flex-1 h-full text-sm select-none truncate ${typeId ? "cursor-text" : ""}`, style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, onDoubleClick: handleDoubleClick, title: typeId ? "Double-click to rename" : undefined, children: displayName })), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [jsxRuntime.
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4630
|
+
}, children: [isEditing ? (jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onBlur: handleSave, onKeyDown: handleKeyDown, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), className: "flex-1 h-full text-sm bg-transparent border border-blue-500 rounded px-1 outline-none wb-nodrag", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` } })) : (jsxRuntime.jsx("strong", { className: `react-flow__node-title flex-1 h-full text-sm select-none truncate ${typeId ? "cursor-text" : ""}`, style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, onDoubleClick: handleDoubleClick, title: typeId ? "Double-click to rename" : undefined, children: displayName })), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [ctx.runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { onClick: (e) => {
|
|
4631
|
+
e.stopPropagation();
|
|
4632
|
+
ctx.abortNode(id);
|
|
4633
|
+
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors", title: "Abort node", children: jsxRuntime.jsx(react$1.StopIcon, { size: 10, weight: "fill" }) }), jsxRuntime.jsx("button", { onClick: (e) => {
|
|
4634
|
+
e.stopPropagation();
|
|
4635
|
+
void ctx.runFromHere(id);
|
|
4636
|
+
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors", title: "Run from here", children: jsxRuntime.jsx(react$1.PlayIcon, { size: 10, weight: "fill" }) }), jsxRuntime.jsx("button", { onClick: (e) => {
|
|
4637
|
+
e.stopPropagation();
|
|
4638
|
+
void ctx.runNode(id);
|
|
4639
|
+
}, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-green-600 dark:hover:text-green-400 transition-colors", title: "Run node", children: jsxRuntime.jsx(react$1.Circle, { size: 10, weight: "fill" }) })] })), right, validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
|
|
4655
4640
|
? "error"
|
|
4656
4641
|
: "warning", size: 12, className: "w-3 h-3", title: validation.issues
|
|
4657
4642
|
.map((v) => `${v.code}: ${v.message}`)
|
|
@@ -4805,7 +4790,7 @@ const DefaultEdge = React.memo(function DefaultEdge({ id, sourceX, sourceY, targ
|
|
|
4805
4790
|
return (jsxRuntime.jsx(react.BaseEdge, { id: id, path: edgePath, style: style, markerEnd: markerEnd }));
|
|
4806
4791
|
});
|
|
4807
4792
|
|
|
4808
|
-
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose, getDefaultNodeSize, onCopyResult) {
|
|
4793
|
+
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose, getDefaultNodeSize, onCopyResult, runNode, runFromHere) {
|
|
4809
4794
|
return {
|
|
4810
4795
|
onDelete: () => {
|
|
4811
4796
|
wb.removeNode(nodeId, { commit: true });
|
|
@@ -4825,13 +4810,24 @@ function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap,
|
|
|
4825
4810
|
});
|
|
4826
4811
|
onClose();
|
|
4827
4812
|
},
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4813
|
+
onRunNode: runNode
|
|
4814
|
+
? async () => {
|
|
4815
|
+
try {
|
|
4816
|
+
await runNode(nodeId);
|
|
4817
|
+
}
|
|
4818
|
+
catch { }
|
|
4819
|
+
onClose();
|
|
4831
4820
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4821
|
+
: undefined,
|
|
4822
|
+
onRunFromHere: runFromHere
|
|
4823
|
+
? async () => {
|
|
4824
|
+
try {
|
|
4825
|
+
await runFromHere(nodeId);
|
|
4826
|
+
}
|
|
4827
|
+
catch { }
|
|
4828
|
+
onClose();
|
|
4829
|
+
}
|
|
4830
|
+
: undefined,
|
|
4835
4831
|
onBake: async (handleId) => {
|
|
4836
4832
|
const nodePosition = wb.getPositions()[nodeId] || { x: 0, y: 0 };
|
|
4837
4833
|
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
@@ -5048,7 +5044,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5048
5044
|
return String(value ?? "");
|
|
5049
5045
|
}
|
|
5050
5046
|
};
|
|
5051
|
-
const { wb,
|
|
5047
|
+
const { wb, registryVersion, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, inputValidationErrors, clearSystemErrors, clearRegistryErrors, clearInputValidationErrors, removeSystemError, removeRegistryError, removeInputValidationError, } = useWorkbenchContext();
|
|
5052
5048
|
const nodeValidationIssues = validationByNode.issues;
|
|
5053
5049
|
const edgeValidationIssues = validationByEdge.issues;
|
|
5054
5050
|
const nodeValidationHandles = validationByNode;
|
|
@@ -5057,7 +5053,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5057
5053
|
const selectedEdge = wb.def.edges.find((e) => e.id === selectedEdgeId);
|
|
5058
5054
|
// Use computeEffectiveHandles to merge registry defaults with dynamically resolved handles
|
|
5059
5055
|
const effectiveHandles = selectedNode
|
|
5060
|
-
? computeEffectiveHandles(selectedNode, registry)
|
|
5056
|
+
? computeEffectiveHandles(selectedNode, wb.registry)
|
|
5061
5057
|
: { inputs: {}, outputs: {}};
|
|
5062
5058
|
const inputHandles = Object.entries(effectiveHandles.inputs)
|
|
5063
5059
|
.filter(([k]) => !sparkGraph.isInputPrivate(effectiveHandles.inputs, k))
|
|
@@ -5184,7 +5180,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5184
5180
|
setDrafts(nextDrafts);
|
|
5185
5181
|
if (!shallowEqual(originals, nextOriginals))
|
|
5186
5182
|
setOriginals(nextOriginals);
|
|
5187
|
-
}, [selectedNodeId, selectedNode, registry, valuesTick]);
|
|
5183
|
+
}, [selectedNodeId, selectedNode, wb.registry, registryVersion, valuesTick]);
|
|
5188
5184
|
const widthClass = debug ? "w-[480px]" : "w-[320px]";
|
|
5189
5185
|
const deleteEdgeById = (edgeId) => {
|
|
5190
5186
|
if (!edgeId)
|
|
@@ -5204,7 +5200,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5204
5200
|
const v = e.target.value;
|
|
5205
5201
|
const next = v === "" ? undefined : v;
|
|
5206
5202
|
updateEdgeType(selectedEdge.id, next);
|
|
5207
|
-
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
5203
|
+
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(wb.registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
5208
5204
|
e.stopPropagation();
|
|
5209
5205
|
deleteEdgeById(selectedEdge.id);
|
|
5210
5206
|
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.activeRuns &&
|
|
@@ -5274,7 +5270,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
5274
5270
|
setOriginals((o) => ({ ...o, [h]: display }));
|
|
5275
5271
|
}, ...commonProps, children: [jsxRuntime.jsx("option", { value: "", children: placeholder
|
|
5276
5272
|
? `Default: ${placeholder}`
|
|
5277
|
-
: "(select)" }), registry.enums
|
|
5273
|
+
: "(select)" }), wb.registry.enums
|
|
5278
5274
|
.get(typeId)
|
|
5279
5275
|
?.options.map((opt) => (jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] }), hasValue && !isLinked && (jsxRuntime.jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded text-gray-500 hover:text-gray-700", onClick: clearInput, title: "Clear input value", children: jsxRuntime.jsx(react$1.XCircleIcon, { size: 16 }) }))] })) : isLinked ? (jsxRuntime.jsx("div", { className: "flex items-center gap-1 flex-1", children: jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: renderLinkedInputDisplay(typeId, current) }) })) : (jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", placeholder: placeholder
|
|
5280
5276
|
? `Default: ${placeholder}`
|
|
@@ -5413,7 +5409,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
|
|
|
5413
5409
|
!handlers.onRedo && jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxRuntime.jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsxRuntime.jsx("div", { className: "px-2 pb-1", children: jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 rounded px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsxRuntime.jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsxRuntime.jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
5414
5410
|
}
|
|
5415
5411
|
|
|
5416
|
-
function NodeContextMenu({ open, clientPos, nodeId, handlers,
|
|
5412
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
5417
5413
|
copy: "⌘/Ctrl + C",
|
|
5418
5414
|
duplicate: "⌘/Ctrl + E",
|
|
5419
5415
|
duplicateWithEdges: "⌘/Ctrl + Shift + E",
|
|
@@ -5456,7 +5452,7 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeab
|
|
|
5456
5452
|
return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
5457
5453
|
e.preventDefault();
|
|
5458
5454
|
e.stopPropagation();
|
|
5459
|
-
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }),
|
|
5455
|
+
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
5460
5456
|
}
|
|
5461
5457
|
|
|
5462
5458
|
function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
@@ -5543,7 +5539,7 @@ function useKeyboardShortcutToast() {
|
|
|
5543
5539
|
}
|
|
5544
5540
|
|
|
5545
5541
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
5546
|
-
const { wb,
|
|
5542
|
+
const { wb, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
|
|
5547
5543
|
const nodeValidation = validationByNode;
|
|
5548
5544
|
const edgeValidation = validationByEdge.errors;
|
|
5549
5545
|
const [historyState, setHistoryState] = React.useState(wb.getHistory());
|
|
@@ -5621,7 +5617,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5621
5617
|
// Build nodeTypes map using UI extension registry
|
|
5622
5618
|
const custom = new Map(); // Include all types present in registry AND current graph to avoid timing issues
|
|
5623
5619
|
const ids = new Set([
|
|
5624
|
-
...Array.from(registry.nodes.keys()),
|
|
5620
|
+
...Array.from(wb.registry.nodes.keys()),
|
|
5625
5621
|
...wb.def.nodes.map((n) => n.typeId),
|
|
5626
5622
|
]);
|
|
5627
5623
|
for (const typeId of ids) {
|
|
@@ -5640,7 +5636,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5640
5636
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
5641
5637
|
// Include uiVersion to recompute when custom renderers are registered
|
|
5642
5638
|
// Include registryVersion to recompute when registry enums/types change
|
|
5643
|
-
}, [wb, registry, uiVersion, ui]);
|
|
5639
|
+
}, [wb, wb.registry, registryVersion, uiVersion, ui]);
|
|
5644
5640
|
const edgeTypes = React.useMemo(() => {
|
|
5645
5641
|
// Use default edge renderer override if registered, otherwise use DefaultEdge
|
|
5646
5642
|
const customEdgeRenderer = ui.getEdgeRenderer();
|
|
@@ -5666,7 +5662,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5666
5662
|
inputsWithDefaults[n.nodeId] = merged;
|
|
5667
5663
|
}
|
|
5668
5664
|
}
|
|
5669
|
-
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), registry, {
|
|
5665
|
+
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), wb.registry, {
|
|
5670
5666
|
showValues,
|
|
5671
5667
|
inputs: inputsWithDefaults,
|
|
5672
5668
|
inputDefaults: inputDefaultsMap,
|
|
@@ -5933,7 +5929,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5933
5929
|
});
|
|
5934
5930
|
return () => off();
|
|
5935
5931
|
}, [wb]);
|
|
5936
|
-
const nodeIds = React.useMemo(() => Array.from(registry.nodes.keys()), [registry, registryVersion]);
|
|
5932
|
+
const nodeIds = React.useMemo(() => Array.from(wb.registry.nodes.keys()), [wb.registry, registryVersion]);
|
|
5937
5933
|
const defaultContextMenuHandlers = React.useMemo(() => {
|
|
5938
5934
|
// Get storage from override or use workbench's internal storage
|
|
5939
5935
|
const storage = overrides?.getCopiedDataStorage
|
|
@@ -5981,9 +5977,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5981
5977
|
get: () => wb.getCopiedData(),
|
|
5982
5978
|
set: (data) => wb.setCopiedData(data),
|
|
5983
5979
|
};
|
|
5984
|
-
const baseHandlers = createNodeContextMenuHandlers(nodeAtMenu, wb, runner, registry, outputsMap, outputTypesMap, onCloseNodeMenu, overrides?.getDefaultNodeSize, (data) => {
|
|
5980
|
+
const baseHandlers = createNodeContextMenuHandlers(nodeAtMenu, wb, runner, wb.registry, outputsMap, outputTypesMap, onCloseNodeMenu, overrides?.getDefaultNodeSize, (data) => {
|
|
5985
5981
|
storage.set(data);
|
|
5986
|
-
});
|
|
5982
|
+
}, runNode, runFromHere);
|
|
5987
5983
|
if (overrides?.getNodeContextMenuHandlers) {
|
|
5988
5984
|
return overrides.getNodeContextMenuHandlers(wb, nodeAtMenu, baseHandlers);
|
|
5989
5985
|
}
|
|
@@ -5992,7 +5988,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5992
5988
|
nodeAtMenu,
|
|
5993
5989
|
wb,
|
|
5994
5990
|
runner,
|
|
5995
|
-
registry,
|
|
5991
|
+
wb.registry,
|
|
5992
|
+
registryVersion,
|
|
5996
5993
|
outputsMap,
|
|
5997
5994
|
outputTypesMap,
|
|
5998
5995
|
onCloseNodeMenu,
|
|
@@ -6000,12 +5997,11 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6000
5997
|
overrides?.getNodeContextMenuHandlers,
|
|
6001
5998
|
overrides?.getCopiedDataStorage,
|
|
6002
5999
|
]);
|
|
6003
|
-
const canRunPull = React.useMemo(() => engineKind()?.toString() === "pull", [engineKind]);
|
|
6004
6000
|
const bakeableOutputs = React.useMemo(() => {
|
|
6005
6001
|
if (!nodeAtMenu)
|
|
6006
6002
|
return [];
|
|
6007
|
-
return getBakeableOutputs(nodeAtMenu, wb, registry, outputTypesMap);
|
|
6008
|
-
}, [nodeAtMenu, wb, registry, outputTypesMap]);
|
|
6003
|
+
return getBakeableOutputs(nodeAtMenu, wb, wb.registry, outputTypesMap);
|
|
6004
|
+
}, [nodeAtMenu, wb, wb.registry, registryVersion, outputTypesMap]);
|
|
6009
6005
|
// Keyboard shortcuts configuration
|
|
6010
6006
|
const enableKeyboardShortcuts = overrides?.enableKeyboardShortcuts !== false; // Default to true
|
|
6011
6007
|
const keyboardShortcuts = overrides?.keyboardShortcuts || {
|
|
@@ -6201,29 +6197,28 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6201
6197
|
if (savedViewport) {
|
|
6202
6198
|
inst.setViewport(lod.clone(savedViewport));
|
|
6203
6199
|
}
|
|
6204
|
-
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [BackgroundRenderer ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
6200
|
+
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [BackgroundRenderer ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
6205
6201
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6206
|
-
: {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
|
|
6202
|
+
: {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
|
|
6207
6203
|
nodeContextMenuHandlers &&
|
|
6208
|
-
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers,
|
|
6204
|
+
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, ...(enableKeyboardShortcuts !== false
|
|
6209
6205
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6210
|
-
: {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers,
|
|
6206
|
+
: {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), selectionMenuOpen &&
|
|
6211
6207
|
selectionMenuPos &&
|
|
6212
6208
|
(SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }) }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
|
|
6213
6209
|
});
|
|
6214
6210
|
|
|
6215
|
-
function WorkbenchStudioCanvas({
|
|
6216
|
-
const { wb, runner,
|
|
6211
|
+
function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
6212
|
+
const { wb, runner, selectedNodeId, runAutoLayout, runMode, setRunMode, isRunning, } = useWorkbenchContext();
|
|
6217
6213
|
const [transportStatus, setTransportStatus] = React.useState({
|
|
6218
6214
|
state: "local",
|
|
6219
6215
|
});
|
|
6220
6216
|
const selectedNode = wb.def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
6221
6217
|
const effectiveHandles = selectedNode
|
|
6222
|
-
? computeEffectiveHandles(selectedNode, registry)
|
|
6218
|
+
? computeEffectiveHandles(selectedNode, wb.registry)
|
|
6223
6219
|
: { inputs: {}, outputs: {}, inputDefaults: {} };
|
|
6224
6220
|
const [exampleState, setExampleState] = React.useState(example ?? "");
|
|
6225
|
-
const isGraphRunning =
|
|
6226
|
-
const engineKind = runner.getRunningEngine();
|
|
6221
|
+
const isGraphRunning = isRunning();
|
|
6227
6222
|
// Render Start/Stop button based on transport and runner state
|
|
6228
6223
|
const renderStartStopButton = React.useCallback(() => {
|
|
6229
6224
|
// Check if transport is connecting/retrying
|
|
@@ -6239,14 +6234,11 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6239
6234
|
return (jsxRuntime.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: [jsxRuntime.jsx(react$1.StopIcon, { size: 16, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Stop" })] }));
|
|
6240
6235
|
}
|
|
6241
6236
|
return (jsxRuntime.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) => {
|
|
6242
|
-
const kind = engine;
|
|
6243
|
-
if (!kind)
|
|
6244
|
-
return alert("Select an engine first.");
|
|
6245
6237
|
if (evt.shiftKey && !confirm("Invalidate and re-run graph?"))
|
|
6246
6238
|
return;
|
|
6247
6239
|
try {
|
|
6248
6240
|
runner.launch(wb.def, {
|
|
6249
|
-
|
|
6241
|
+
runMode,
|
|
6250
6242
|
invalidate: evt.shiftKey,
|
|
6251
6243
|
});
|
|
6252
6244
|
}
|
|
@@ -6254,12 +6246,10 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6254
6246
|
const message = err instanceof Error ? err.message : String(err);
|
|
6255
6247
|
alert(message);
|
|
6256
6248
|
}
|
|
6257
|
-
}, disabled: !
|
|
6258
|
-
? "
|
|
6259
|
-
:
|
|
6260
|
-
|
|
6261
|
-
: "Start engine", children: [jsxRuntime.jsx(react$1.PlayIcon, { size: 16, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Start" })] }));
|
|
6262
|
-
}, [transportStatus, isGraphRunning, runner, engine, wb]);
|
|
6249
|
+
}, disabled: !canControl, title: !canControl
|
|
6250
|
+
? "Waiting for connection"
|
|
6251
|
+
: `Start ${runMode === "manual" ? "manual" : "auto"} mode`, children: [jsxRuntime.jsx(react$1.PlayIcon, { size: 16, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Start" })] }));
|
|
6252
|
+
}, [transportStatus, isGraphRunning, runner, runMode, wb]);
|
|
6263
6253
|
const defaultExamples = React.useMemo(() => [
|
|
6264
6254
|
{
|
|
6265
6255
|
id: "simple",
|
|
@@ -6366,7 +6356,6 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6366
6356
|
// - For remote backend, registry is automatically managed by RemoteGraphRunner
|
|
6367
6357
|
if (backendKind === "local") {
|
|
6368
6358
|
if (r) {
|
|
6369
|
-
setRegistry(r);
|
|
6370
6359
|
wb.setRegistry(r);
|
|
6371
6360
|
}
|
|
6372
6361
|
}
|
|
@@ -6382,15 +6371,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6382
6371
|
runAutoLayout();
|
|
6383
6372
|
setExampleState(key);
|
|
6384
6373
|
onExampleChange?.(key);
|
|
6385
|
-
}, [
|
|
6386
|
-
runner,
|
|
6387
|
-
wb,
|
|
6388
|
-
onExampleChange,
|
|
6389
|
-
runAutoLayout,
|
|
6390
|
-
examples,
|
|
6391
|
-
setRegistry,
|
|
6392
|
-
backendKind,
|
|
6393
|
-
]);
|
|
6374
|
+
}, [runner, wb, onExampleChange, runAutoLayout, examples, backendKind]);
|
|
6394
6375
|
const download$1 = React.useCallback(async () => {
|
|
6395
6376
|
try {
|
|
6396
6377
|
await download(wb, runner);
|
|
@@ -6450,8 +6431,6 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6450
6431
|
return () => off();
|
|
6451
6432
|
}, [runner, backendKind]);
|
|
6452
6433
|
React.useEffect(() => {
|
|
6453
|
-
if (!engine)
|
|
6454
|
-
return;
|
|
6455
6434
|
if (isGraphRunning)
|
|
6456
6435
|
return;
|
|
6457
6436
|
// Only auto-launch for local backend; require explicit Start for remote
|
|
@@ -6460,12 +6439,12 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6460
6439
|
if (!wb.def.nodes || wb.def.nodes.length === 0)
|
|
6461
6440
|
return;
|
|
6462
6441
|
try {
|
|
6463
|
-
runner.launch(wb.def, {
|
|
6442
|
+
runner.launch(wb.def, { runMode });
|
|
6464
6443
|
}
|
|
6465
6444
|
catch {
|
|
6466
6445
|
// ignore
|
|
6467
6446
|
}
|
|
6468
|
-
}, [
|
|
6447
|
+
}, [runMode, runner, isGraphRunning, wb, backendKind]);
|
|
6469
6448
|
const baseSetInput = React.useCallback((handle, raw) => {
|
|
6470
6449
|
if (!selectedNodeId)
|
|
6471
6450
|
return;
|
|
@@ -6561,11 +6540,11 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6561
6540
|
return overrides.setInput(baseSetInput, {
|
|
6562
6541
|
runner,
|
|
6563
6542
|
selectedNodeId,
|
|
6564
|
-
registry,
|
|
6543
|
+
registry: wb.registry,
|
|
6565
6544
|
});
|
|
6566
6545
|
}
|
|
6567
6546
|
return baseSetInput;
|
|
6568
|
-
}, [overrides, baseSetInput, runner, selectedNodeId, registry]);
|
|
6547
|
+
}, [overrides, baseSetInput, runner, selectedNodeId, wb.registry]);
|
|
6569
6548
|
const baseToString = React.useCallback((typeId, value) => {
|
|
6570
6549
|
if (value === undefined || value === null)
|
|
6571
6550
|
return "";
|
|
@@ -6573,7 +6552,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6573
6552
|
if (sparkGraph.isTypedOutput(value)) {
|
|
6574
6553
|
return baseToString(sparkGraph.getTypedOutputTypeId(value), sparkGraph.getTypedOutputValue(value));
|
|
6575
6554
|
}
|
|
6576
|
-
const pre = preformatValueForDisplay(typeId, value, registry);
|
|
6555
|
+
const pre = preformatValueForDisplay(typeId, value, wb.registry);
|
|
6577
6556
|
if (pre !== undefined)
|
|
6578
6557
|
return pre;
|
|
6579
6558
|
if (typeof value === "object" &&
|
|
@@ -6589,7 +6568,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6589
6568
|
}
|
|
6590
6569
|
if (typeId && typeId.startsWith("enum:")) {
|
|
6591
6570
|
const n = Number(value);
|
|
6592
|
-
const label = registry.enums.get(typeId)?.valueToLabel.get(n);
|
|
6571
|
+
const label = wb.registry.enums.get(typeId)?.valueToLabel.get(n);
|
|
6593
6572
|
return label ?? String(n);
|
|
6594
6573
|
}
|
|
6595
6574
|
const round4 = (n) => Math.round(Number(n) * 10000) / 10000;
|
|
@@ -6619,54 +6598,34 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
6619
6598
|
return String(rounded);
|
|
6620
6599
|
}
|
|
6621
6600
|
return String(value);
|
|
6622
|
-
}, [registry]);
|
|
6601
|
+
}, [wb.registry]);
|
|
6623
6602
|
const baseToElement = React.useCallback((typeId, value) => {
|
|
6624
6603
|
return (jsxRuntime.jsx("span", { className: "ml-1 opacity-60", children: baseToString(typeId, value) }));
|
|
6625
6604
|
}, [baseToString]);
|
|
6626
6605
|
const toString = React.useMemo(() => {
|
|
6627
6606
|
if (overrides?.toString)
|
|
6628
|
-
return overrides.toString(baseToString, { registry });
|
|
6607
|
+
return overrides.toString(baseToString, { registry: wb.registry });
|
|
6629
6608
|
return baseToString;
|
|
6630
|
-
}, [overrides, baseToString, registry]);
|
|
6609
|
+
}, [overrides, baseToString, wb.registry]);
|
|
6631
6610
|
// Optional: toElement (not currently consumed by core UI)
|
|
6632
6611
|
// Consumers can access it by passing through their own node renderers.
|
|
6633
6612
|
const toElement = React.useMemo(() => {
|
|
6634
6613
|
if (overrides?.toElement)
|
|
6635
|
-
return overrides.toElement(baseToElement, { registry });
|
|
6614
|
+
return overrides.toElement(baseToElement, { registry: wb.registry });
|
|
6636
6615
|
return baseToElement;
|
|
6637
|
-
}, [overrides, baseToElement, registry]);
|
|
6638
|
-
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [isGraphRunning ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ",
|
|
6639
|
-
const
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
if (runner.isRunning() &&
|
|
6643
|
-
currentEngine &&
|
|
6644
|
-
kind &&
|
|
6645
|
-
kind !== currentEngine) {
|
|
6646
|
-
try {
|
|
6647
|
-
await runner.switchEngine({
|
|
6648
|
-
engine: kind,
|
|
6649
|
-
batched: { flushIntervalMs: 0 },
|
|
6650
|
-
hybrid: { windowMs: 250, batchThreshold: 3 },
|
|
6651
|
-
});
|
|
6652
|
-
onEngineChange?.(kind);
|
|
6653
|
-
}
|
|
6654
|
-
catch (err) {
|
|
6655
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6656
|
-
alert(`Failed to switch engine: ${message}`);
|
|
6657
|
-
// Reset dropdown to current engine
|
|
6658
|
-
e.target.value = currentEngine;
|
|
6659
|
-
}
|
|
6660
|
-
}
|
|
6661
|
-
else {
|
|
6662
|
-
// Normal change when not running
|
|
6663
|
-
onEngineChange?.(kind);
|
|
6616
|
+
}, [overrides, baseToElement, wb.registry]);
|
|
6617
|
+
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [isGraphRunning ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runMode === "manual" ? "Manual" : "Auto"] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react$1.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react$1.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react$1.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: isGraphRunning, title: isGraphRunning ? "Stop engine before switching example" : undefined, children: [jsxRuntime.jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsxRuntime.jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: backendKind, onChange: (e) => onBackendKindChange(e.target.value), disabled: isGraphRunning, title: isGraphRunning ? "Stop engine before switching backend" : undefined, children: [jsxRuntime.jsx("option", { value: "local", children: "Local" }), jsxRuntime.jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsxRuntime.jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && !!onHttpBaseUrlChange && (jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && !!onWsUrlChange && (jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runMode, onChange: async (e) => {
|
|
6618
|
+
const mode = e.target.value;
|
|
6619
|
+
if (mode !== runMode) {
|
|
6620
|
+
await setRunMode(mode);
|
|
6664
6621
|
}
|
|
6665
|
-
},
|
|
6622
|
+
}, disabled: isGraphRunning, title: isGraphRunning
|
|
6623
|
+
? "Stop before switching run mode"
|
|
6624
|
+
: "Select run mode", children: [jsxRuntime.jsx("option", { value: "manual", children: "Manual" }), jsxRuntime.jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsxRuntime.jsx(react$1.TreeStructureIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsxRuntime.jsx(react$1.CornersOutIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsxRuntime.jsx(react$1.DownloadIcon, { size: 24 }) }), jsxRuntime.jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsxRuntime.jsx(react$1.UploadIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
|
|
6666
6625
|
await downloadCanvasThumbnail(canvasContainerRef.current);
|
|
6667
6626
|
}, title: "Download Flow Thumbnail (SVG)", children: jsxRuntime.jsx(react$1.ImageIcon, { size: 24 }) }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
|
|
6668
6627
|
}
|
|
6669
|
-
function WorkbenchStudio({
|
|
6628
|
+
function WorkbenchStudio({ example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
|
|
6670
6629
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|
|
6671
6630
|
const [wb] = React.useState(() => new InMemoryWorkbench({ ui: new DefaultUIExtensionRegistry() }));
|
|
6672
6631
|
// Store previous runner for cleanup
|
|
@@ -6736,7 +6695,7 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
|
|
|
6736
6695
|
runner.dispose();
|
|
6737
6696
|
onBackendKindChange(v);
|
|
6738
6697
|
}, [isGraphRunning]);
|
|
6739
|
-
return (jsxRuntime.jsx(WorkbenchProvider, { wb: wb, runner: runner,
|
|
6698
|
+
return (jsxRuntime.jsx(WorkbenchProvider, { wb: wb, runner: runner, overrides: overrides, uiVersion: uiVersion, children: jsxRuntime.jsx(WorkbenchStudioCanvas, { setRegistry: setRegistry, autoScroll: autoScroll, onAutoScrollChange: onAutoScrollChange, example: example, onExampleChange: onExampleChange, backendKind: backendKind, onBackendKindChange: onBackendKindChangeWithDispose, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, overrides: overrides, onInit: onInit, onChange: onChange }) }));
|
|
6740
6699
|
}
|
|
6741
6700
|
|
|
6742
6701
|
exports.AbstractWorkbench = AbstractWorkbench;
|