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