@bian-womp/spark-workbench 0.2.53 → 0.2.55
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 +383 -292
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/AbstractWorkbench.d.ts +2 -0
- package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +9 -2
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +3 -0
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/core/ui-extensions.d.ts +44 -47
- package/lib/cjs/src/core/ui-extensions.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/DefaultContextMenu.d.ts +2 -12
- package/lib/cjs/src/misc/DefaultContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeContextMenu.d.ts +2 -9
- package/lib/cjs/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/ContextMenuHandlers.d.ts +41 -0
- package/lib/cjs/src/misc/context/ContextMenuHandlers.d.ts.map +1 -0
- package/lib/cjs/src/misc/context/ContextMenuHelpers.d.ts +7 -0
- package/lib/cjs/src/misc/context/ContextMenuHelpers.d.ts.map +1 -0
- package/lib/cjs/src/misc/load.d.ts +5 -0
- package/lib/cjs/src/misc/load.d.ts.map +1 -0
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +3 -2
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +1 -0
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +0 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +384 -295
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/AbstractWorkbench.d.ts +2 -0
- package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +9 -2
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +3 -0
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/core/ui-extensions.d.ts +44 -47
- package/lib/esm/src/core/ui-extensions.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/DefaultContextMenu.d.ts +2 -12
- package/lib/esm/src/misc/DefaultContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeContextMenu.d.ts +2 -9
- package/lib/esm/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/ContextMenuHandlers.d.ts +41 -0
- package/lib/esm/src/misc/context/ContextMenuHandlers.d.ts.map +1 -0
- package/lib/esm/src/misc/context/ContextMenuHelpers.d.ts +7 -0
- package/lib/esm/src/misc/context/ContextMenuHelpers.d.ts.map +1 -0
- package/lib/esm/src/misc/load.d.ts +5 -0
- package/lib/esm/src/misc/load.d.ts.map +1 -0
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +3 -2
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +1 -0
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +0 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/esm/index.js
CHANGED
|
@@ -1,80 +1,84 @@
|
|
|
1
|
-
import { GraphBuilder, createEngine, StepEngine, PullEngine, BatchedEngine, isTypedOutput, getTypedOutputValue, getTypedOutputTypeId, isInputPrivate, getInputTypeId, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
1
|
+
import { generateId, GraphBuilder, createEngine, StepEngine, PullEngine, BatchedEngine, isTypedOutput, getTypedOutputValue, getTypedOutputTypeId, isInputPrivate, getInputTypeId, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
2
2
|
import { RuntimeApiClient } from '@bian-womp/spark-remote';
|
|
3
3
|
import { Position, Handle, useUpdateNodeInternals, useReactFlow, ReactFlowProvider, ReactFlow, Background, BackgroundVariant, MiniMap, Controls } from '@xyflow/react';
|
|
4
4
|
import React, { useCallback, useState, useRef, useEffect, useMemo, createContext, useContext, useImperativeHandle } from 'react';
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { XCircleIcon, WarningCircleIcon, CopyIcon, TrashIcon, XIcon, ArrowClockwiseIcon, ClockClockwiseIcon, StopIcon, PlayIcon, PlugsConnectedIcon, WifiHighIcon, WifiSlashIcon, PlayPauseIcon, LightningIcon, TreeStructureIcon, CornersOutIcon,
|
|
7
|
+
import { XCircleIcon, WarningCircleIcon, CopyIcon, TrashIcon, XIcon, ArrowClockwiseIcon, ClockClockwiseIcon, StopIcon, PlayIcon, PlugsConnectedIcon, WifiHighIcon, WifiSlashIcon, PlayPauseIcon, LightningIcon, TreeStructureIcon, CornersOutIcon, DownloadIcon, UploadIcon, BugBeetleIcon, ListBulletsIcon } from '@phosphor-icons/react';
|
|
8
8
|
import isEqual from 'lodash/isEqual';
|
|
9
9
|
|
|
10
10
|
class DefaultUIExtensionRegistry {
|
|
11
11
|
constructor() {
|
|
12
12
|
this.nodeRenderers = new Map();
|
|
13
|
-
this.portRenderers = new Map();
|
|
14
|
-
this.edgeRenderers = new Map();
|
|
15
13
|
}
|
|
16
14
|
registerNodeRenderer(nodeTypeId, renderer) {
|
|
17
|
-
|
|
15
|
+
if (renderer === undefined) {
|
|
16
|
+
this.nodeRenderers.delete(nodeTypeId);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
this.nodeRenderers.set(nodeTypeId, renderer);
|
|
20
|
+
}
|
|
18
21
|
return this;
|
|
19
22
|
}
|
|
20
23
|
getNodeRenderer(nodeTypeId) {
|
|
21
24
|
return this.nodeRenderers.get(nodeTypeId);
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
26
|
+
getAllNodeRenderers() {
|
|
27
|
+
const result = {};
|
|
28
|
+
for (const [nodeTypeId, renderer] of this.nodeRenderers.entries()) {
|
|
29
|
+
result[nodeTypeId] = renderer;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
29
32
|
}
|
|
30
|
-
|
|
31
|
-
this.
|
|
33
|
+
registerIconProvider(provider) {
|
|
34
|
+
this.iconProvider = provider;
|
|
32
35
|
return this;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
return this.
|
|
37
|
+
getIconProvider() {
|
|
38
|
+
return this.iconProvider;
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// React Flow renderers
|
|
41
|
+
registerConnectionLineRenderer(renderer) {
|
|
42
|
+
this.connectionLineRenderer = renderer;
|
|
39
43
|
return this;
|
|
40
44
|
}
|
|
41
|
-
|
|
42
|
-
return this.
|
|
45
|
+
getConnectionLineRenderer() {
|
|
46
|
+
return this.connectionLineRenderer;
|
|
43
47
|
}
|
|
44
|
-
|
|
45
|
-
this.
|
|
48
|
+
registerMinimapRenderer(renderer) {
|
|
49
|
+
this.minimapRenderer = renderer;
|
|
46
50
|
return this;
|
|
47
51
|
}
|
|
48
|
-
|
|
49
|
-
return this.
|
|
52
|
+
getMinimapRenderer() {
|
|
53
|
+
return this.minimapRenderer;
|
|
50
54
|
}
|
|
51
|
-
|
|
52
|
-
this.
|
|
55
|
+
registerControlsRenderer(renderer) {
|
|
56
|
+
this.controlsRenderer = renderer;
|
|
53
57
|
return this;
|
|
54
58
|
}
|
|
55
|
-
|
|
56
|
-
return this.
|
|
59
|
+
getControlsRenderer() {
|
|
60
|
+
return this.controlsRenderer;
|
|
57
61
|
}
|
|
58
|
-
|
|
59
|
-
this.
|
|
62
|
+
registerBackgroundRenderer(renderer) {
|
|
63
|
+
this.backgroundRenderer = renderer;
|
|
60
64
|
return this;
|
|
61
65
|
}
|
|
62
|
-
|
|
63
|
-
return this.
|
|
66
|
+
getBackgroundRenderer() {
|
|
67
|
+
return this.backgroundRenderer;
|
|
64
68
|
}
|
|
65
|
-
|
|
66
|
-
this.
|
|
69
|
+
registerDefaultContextMenuRenderer(renderer) {
|
|
70
|
+
this.defaultContextMenuRenderer = renderer;
|
|
67
71
|
return this;
|
|
68
72
|
}
|
|
69
|
-
|
|
70
|
-
return this.
|
|
73
|
+
getDefaultContextMenuRenderer() {
|
|
74
|
+
return this.defaultContextMenuRenderer;
|
|
71
75
|
}
|
|
72
|
-
|
|
73
|
-
this.
|
|
76
|
+
registerNodeContextMenuRenderer(renderer) {
|
|
77
|
+
this.nodeContextMenuRenderer = renderer;
|
|
74
78
|
return this;
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
return this.
|
|
80
|
+
getNodeContextMenuRenderer() {
|
|
81
|
+
return this.nodeContextMenuRenderer;
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -84,6 +88,7 @@ class AbstractWorkbench {
|
|
|
84
88
|
this.layout = args.layout;
|
|
85
89
|
this.storage = args.storage;
|
|
86
90
|
this.serializer = args.serializer;
|
|
91
|
+
this.genId = args.genId || generateId;
|
|
87
92
|
}
|
|
88
93
|
// Expose UI registry to adapters (React Flow, CLI) to allow overrides
|
|
89
94
|
getUI() {
|
|
@@ -112,6 +117,13 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
112
117
|
const { positions } = await this.layout.layout(this.def);
|
|
113
118
|
this.positions = positions;
|
|
114
119
|
}
|
|
120
|
+
const defNodeIds = new Set(this.def.nodes.map((n) => n.nodeId));
|
|
121
|
+
const defEdgeIds = new Set(this.def.edges.map((e) => e.id));
|
|
122
|
+
const filteredPositions = Object.fromEntries(Object.entries(this.positions).filter(([id]) => defNodeIds.has(id)));
|
|
123
|
+
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
124
|
+
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
125
|
+
this.positions = filteredPositions;
|
|
126
|
+
this.selection = { nodes: filteredNodes, edges: filteredEdges };
|
|
115
127
|
this.emit("graphChanged", { def: this.def });
|
|
116
128
|
this.refreshValidation();
|
|
117
129
|
}
|
|
@@ -155,11 +167,14 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
155
167
|
return { ok: issues.every((i) => i.level !== "error"), issues };
|
|
156
168
|
}
|
|
157
169
|
addNode(node) {
|
|
158
|
-
const id = node.nodeId ??
|
|
170
|
+
const id = node.nodeId ??
|
|
171
|
+
this.genId("n", new Set(this.def.nodes.map((n) => n.nodeId)));
|
|
159
172
|
this.def.nodes.push({
|
|
160
173
|
nodeId: id,
|
|
161
174
|
typeId: node.typeId,
|
|
162
175
|
params: node.params,
|
|
176
|
+
initialInputs: node.initialInputs,
|
|
177
|
+
resolvedHandles: node.resolvedHandles,
|
|
163
178
|
});
|
|
164
179
|
if (node.position)
|
|
165
180
|
this.positions[id] = node.position;
|
|
@@ -181,7 +196,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
181
196
|
this.refreshValidation();
|
|
182
197
|
}
|
|
183
198
|
connect(edge) {
|
|
184
|
-
const id = edge.id ?? this.
|
|
199
|
+
const id = edge.id ?? this.genId("e", new Set(this.def.edges.map((e) => e.id)));
|
|
185
200
|
this.def.edges.push({
|
|
186
201
|
id,
|
|
187
202
|
source: { ...edge.source },
|
|
@@ -228,29 +243,32 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
228
243
|
});
|
|
229
244
|
}
|
|
230
245
|
// Position and selection APIs for React Flow bridge
|
|
231
|
-
setPosition(nodeId, pos) {
|
|
246
|
+
setPosition(nodeId, pos, opts) {
|
|
232
247
|
this.positions[nodeId] = pos;
|
|
233
248
|
this.emit("graphUiChanged", {
|
|
234
249
|
def: this.def,
|
|
235
250
|
change: { type: "moveNode", nodeId, pos },
|
|
251
|
+
commit: !!opts?.commit === true,
|
|
236
252
|
});
|
|
237
253
|
}
|
|
238
|
-
setPositions(map) {
|
|
254
|
+
setPositions(map, opts) {
|
|
239
255
|
this.positions = { ...map };
|
|
240
256
|
this.emit("graphUiChanged", {
|
|
241
257
|
def: this.def,
|
|
242
258
|
change: { type: "moveNodes" },
|
|
259
|
+
commit: opts?.commit,
|
|
243
260
|
});
|
|
244
261
|
}
|
|
245
262
|
getPositions() {
|
|
246
263
|
return { ...this.positions };
|
|
247
264
|
}
|
|
248
|
-
setSelection(sel) {
|
|
265
|
+
setSelection(sel, opts) {
|
|
249
266
|
this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
|
|
250
267
|
this.emit("selectionChanged", this.selection);
|
|
251
268
|
this.emit("graphUiChanged", {
|
|
252
269
|
def: this.def,
|
|
253
270
|
change: { type: "selection" },
|
|
271
|
+
commit: opts?.commit,
|
|
254
272
|
});
|
|
255
273
|
}
|
|
256
274
|
getSelection() {
|
|
@@ -259,21 +277,31 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
259
277
|
edges: [...this.selection.edges],
|
|
260
278
|
};
|
|
261
279
|
}
|
|
262
|
-
setViewport(viewport) {
|
|
280
|
+
setViewport(viewport, opts) {
|
|
263
281
|
this.viewport = { ...viewport };
|
|
282
|
+
this.emit("graphUiChanged", {
|
|
283
|
+
def: this.def,
|
|
284
|
+
change: { type: "viewport" },
|
|
285
|
+
commit: opts?.commit,
|
|
286
|
+
});
|
|
264
287
|
}
|
|
265
288
|
getViewport() {
|
|
266
289
|
return this.viewport ? { ...this.viewport } : null;
|
|
267
290
|
}
|
|
268
291
|
getUIState() {
|
|
292
|
+
const defNodeIds = new Set(this.def.nodes.map((n) => n.nodeId));
|
|
293
|
+
const defEdgeIds = new Set(this.def.edges.map((e) => e.id));
|
|
294
|
+
const filteredPositions = Object.fromEntries(Object.entries(this.positions).filter(([id]) => defNodeIds.has(id)));
|
|
295
|
+
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
296
|
+
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
269
297
|
return {
|
|
270
|
-
positions: Object.keys(
|
|
271
|
-
?
|
|
298
|
+
positions: Object.keys(filteredPositions).length > 0
|
|
299
|
+
? filteredPositions
|
|
272
300
|
: undefined,
|
|
273
|
-
selection:
|
|
301
|
+
selection: filteredNodes.length > 0 || filteredEdges.length > 0
|
|
274
302
|
? {
|
|
275
|
-
nodes:
|
|
276
|
-
edges:
|
|
303
|
+
nodes: filteredNodes,
|
|
304
|
+
edges: filteredEdges,
|
|
277
305
|
}
|
|
278
306
|
: undefined,
|
|
279
307
|
viewport: this.viewport ? { ...this.viewport } : undefined,
|
|
@@ -309,9 +337,6 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
309
337
|
for (const h of Array.from(set))
|
|
310
338
|
h(payload);
|
|
311
339
|
}
|
|
312
|
-
generateId(prefix) {
|
|
313
|
-
return `${prefix}${Math.random().toString(36).slice(2, 8)}`;
|
|
314
|
-
}
|
|
315
340
|
}
|
|
316
341
|
|
|
317
342
|
class CLIWorkbench {
|
|
@@ -443,6 +468,18 @@ class AbstractGraphRunner {
|
|
|
443
468
|
}
|
|
444
469
|
}
|
|
445
470
|
}
|
|
471
|
+
getInputDefaults(def) {
|
|
472
|
+
const out = {};
|
|
473
|
+
for (const n of def.nodes) {
|
|
474
|
+
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
475
|
+
const graphDefaults = n.initialInputs ?? {};
|
|
476
|
+
const merged = { ...dynDefaults, ...graphDefaults };
|
|
477
|
+
if (Object.keys(merged).length > 0) {
|
|
478
|
+
out[n.nodeId] = merged;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return out;
|
|
482
|
+
}
|
|
446
483
|
on(event, handler) {
|
|
447
484
|
if (!this.listeners.has(event))
|
|
448
485
|
this.listeners.set(event, new Set());
|
|
@@ -604,16 +641,6 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
604
641
|
}
|
|
605
642
|
return out;
|
|
606
643
|
}
|
|
607
|
-
getInputDefaults(def) {
|
|
608
|
-
const out = {};
|
|
609
|
-
for (const n of def.nodes) {
|
|
610
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
611
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
612
|
-
out[n.nodeId] = dynDefaults;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return out;
|
|
616
|
-
}
|
|
617
644
|
async snapshotFull() {
|
|
618
645
|
const def = undefined; // UI will supply def/positions on download for local
|
|
619
646
|
const inputs = this.getInputs(this.runtime
|
|
@@ -641,11 +668,25 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
641
668
|
if (payload.def)
|
|
642
669
|
this.build(payload.def);
|
|
643
670
|
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
644
|
-
|
|
671
|
+
this.hydrateSnapshotFull(payload);
|
|
672
|
+
}
|
|
673
|
+
hydrateSnapshotFull(snapshot) {
|
|
674
|
+
// Hydrate via runtime for exact restore (this emits events on runtime emitter)
|
|
645
675
|
this.runtime?.hydrate({
|
|
646
|
-
inputs:
|
|
647
|
-
outputs:
|
|
676
|
+
inputs: snapshot.inputs || {},
|
|
677
|
+
outputs: snapshot.outputs || {},
|
|
648
678
|
});
|
|
679
|
+
// Also emit directly from runner to ensure UI gets events even if engine isn't running
|
|
680
|
+
for (const [nodeId, map] of Object.entries(snapshot.inputs || {})) {
|
|
681
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
682
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
for (const [nodeId, map] of Object.entries(snapshot.outputs || {})) {
|
|
686
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
687
|
+
this.emit("value", { nodeId, handle, value, io: "output" });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
649
690
|
}
|
|
650
691
|
dispose() {
|
|
651
692
|
super.dispose();
|
|
@@ -840,10 +881,13 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
840
881
|
// Auto-fetch registry on first connection (only once)
|
|
841
882
|
if (!this.registryFetched && !this.registryFetching) {
|
|
842
883
|
// Log loading state (UI can listen to transport status for loading indication)
|
|
843
|
-
console.info("Loading registry from remote...");
|
|
844
|
-
this.fetchRegistry(client)
|
|
884
|
+
console.info("[RemoteGraphRunner] Loading registry from remote...");
|
|
885
|
+
this.fetchRegistry(client)
|
|
886
|
+
.then(() => {
|
|
887
|
+
console.info("[RemoteGraphRunner] Loaded registry from remote");
|
|
888
|
+
})
|
|
889
|
+
.catch((err) => {
|
|
845
890
|
console.error("[RemoteGraphRunner] Failed to fetch registry:", err);
|
|
846
|
-
// Error handling is done inside fetchRegistry, but we catch unhandled rejections
|
|
847
891
|
});
|
|
848
892
|
}
|
|
849
893
|
// Clear promise on success
|
|
@@ -1199,16 +1243,6 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1199
1243
|
}
|
|
1200
1244
|
return out;
|
|
1201
1245
|
}
|
|
1202
|
-
getInputDefaults(def) {
|
|
1203
|
-
const out = {};
|
|
1204
|
-
for (const n of def.nodes) {
|
|
1205
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
1206
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
1207
|
-
out[n.nodeId] = dynDefaults;
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
return out;
|
|
1211
|
-
}
|
|
1212
1246
|
dispose() {
|
|
1213
1247
|
// Idempotent: allow multiple calls safely
|
|
1214
1248
|
if (this.disposed)
|
|
@@ -1477,10 +1511,10 @@ function useWorkbenchBridge(wb) {
|
|
|
1477
1511
|
});
|
|
1478
1512
|
}, [wb]);
|
|
1479
1513
|
const onNodesChange = useCallback((changes) => {
|
|
1480
|
-
// Apply position updates
|
|
1514
|
+
// Apply position updates continuously, but mark commit only on drag end
|
|
1481
1515
|
changes.forEach((c) => {
|
|
1482
1516
|
if (c.type === "position" && c.position) {
|
|
1483
|
-
wb.setPosition(c.id, c.position);
|
|
1517
|
+
wb.setPosition(c.id, c.position, { commit: !c.dragging });
|
|
1484
1518
|
}
|
|
1485
1519
|
});
|
|
1486
1520
|
// Derive next node selection from change set
|
|
@@ -1954,6 +1988,99 @@ function getHandleClassName(args) {
|
|
|
1954
1988
|
return cx("!w-3 !h-3 !bg-white !dark:bg-stone-900", borderColor, kind === "output" && "!rounded-none");
|
|
1955
1989
|
}
|
|
1956
1990
|
|
|
1991
|
+
function generateTimestamp() {
|
|
1992
|
+
const d = new Date();
|
|
1993
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1994
|
+
return `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`;
|
|
1995
|
+
}
|
|
1996
|
+
function downloadJSON(payload, filename) {
|
|
1997
|
+
const pretty = JSON.stringify(payload, null, 2);
|
|
1998
|
+
const blob = new Blob([pretty], { type: "application/json" });
|
|
1999
|
+
const url = URL.createObjectURL(blob);
|
|
2000
|
+
const a = document.createElement("a");
|
|
2001
|
+
a.href = url;
|
|
2002
|
+
a.download = filename;
|
|
2003
|
+
document.body.appendChild(a);
|
|
2004
|
+
a.click();
|
|
2005
|
+
a.remove();
|
|
2006
|
+
URL.revokeObjectURL(url);
|
|
2007
|
+
}
|
|
2008
|
+
function isSnapshotPayload(parsed) {
|
|
2009
|
+
return (parsed !== null &&
|
|
2010
|
+
typeof parsed === "object" &&
|
|
2011
|
+
("def" in parsed ||
|
|
2012
|
+
"inputs" in parsed ||
|
|
2013
|
+
"outputs" in parsed ||
|
|
2014
|
+
"environment" in parsed));
|
|
2015
|
+
}
|
|
2016
|
+
async function download(wb, runner) {
|
|
2017
|
+
try {
|
|
2018
|
+
const def = wb.export();
|
|
2019
|
+
const uiState = wb.getUIState();
|
|
2020
|
+
let snapshot;
|
|
2021
|
+
if (runner.isRunning()) {
|
|
2022
|
+
const fullSnapshot = await runner.snapshotFull();
|
|
2023
|
+
snapshot = {
|
|
2024
|
+
...fullSnapshot,
|
|
2025
|
+
def,
|
|
2026
|
+
extData: {
|
|
2027
|
+
...(fullSnapshot.extData || {}),
|
|
2028
|
+
ui: uiState,
|
|
2029
|
+
},
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
else {
|
|
2033
|
+
const inputs = runner.getInputs(def);
|
|
2034
|
+
snapshot = {
|
|
2035
|
+
def,
|
|
2036
|
+
inputs,
|
|
2037
|
+
outputs: {},
|
|
2038
|
+
environment: {},
|
|
2039
|
+
extData: { ui: uiState },
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
downloadJSON(snapshot, `spark-snapshot-${generateTimestamp()}.json`);
|
|
2043
|
+
}
|
|
2044
|
+
catch (err) {
|
|
2045
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2046
|
+
throw new Error(`Failed to download snapshot: ${message}`);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
async function upload(parsed, wb, runner) {
|
|
2050
|
+
if (!isSnapshotPayload(parsed)) {
|
|
2051
|
+
throw new Error("Invalid snapshot format - expected RuntimeSnapshotFull");
|
|
2052
|
+
}
|
|
2053
|
+
const def = parsed.def;
|
|
2054
|
+
const environment = parsed.environment || {};
|
|
2055
|
+
const inputs = parsed.inputs || {};
|
|
2056
|
+
const outputs = parsed.outputs || {};
|
|
2057
|
+
const extData = parsed.extData || {};
|
|
2058
|
+
if (!def) {
|
|
2059
|
+
throw new Error("Graph definition is empty");
|
|
2060
|
+
}
|
|
2061
|
+
await wb.load(def);
|
|
2062
|
+
if (extData.ui && typeof extData.ui === "object") {
|
|
2063
|
+
wb.setUIState(extData.ui);
|
|
2064
|
+
}
|
|
2065
|
+
if (runner.isRunning()) {
|
|
2066
|
+
await runner.applySnapshotFull({
|
|
2067
|
+
def,
|
|
2068
|
+
environment,
|
|
2069
|
+
inputs,
|
|
2070
|
+
outputs,
|
|
2071
|
+
extData,
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
else {
|
|
2075
|
+
runner.build(wb.export());
|
|
2076
|
+
if (inputs && typeof inputs === "object") {
|
|
2077
|
+
for (const [nodeId, map] of Object.entries(inputs)) {
|
|
2078
|
+
runner.setInputs(nodeId, map);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
1957
2084
|
const WorkbenchContext = createContext(null);
|
|
1958
2085
|
function useWorkbenchContext() {
|
|
1959
2086
|
const ctx = useContext(WorkbenchContext);
|
|
@@ -2139,7 +2266,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2139
2266
|
}
|
|
2140
2267
|
curX += maxWidth + H_GAP;
|
|
2141
2268
|
}
|
|
2142
|
-
wb.setPositions(pos);
|
|
2269
|
+
wb.setPositions(pos, { commit: true });
|
|
2143
2270
|
}, [wb, registry, overrides?.getDefaultNodeSize]);
|
|
2144
2271
|
const updateEdgeType = useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
2145
2272
|
const triggerExternal = useCallback((nodeId, event) => runner.triggerExternal(nodeId, event), [runner]);
|
|
@@ -3096,15 +3223,13 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
3096
3223
|
} })] }));
|
|
3097
3224
|
}
|
|
3098
3225
|
|
|
3099
|
-
function DefaultContextMenu({ open, clientPos,
|
|
3100
|
-
const { registry } = useWorkbenchContext();
|
|
3226
|
+
function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, }) {
|
|
3101
3227
|
const rf = useReactFlow();
|
|
3102
|
-
const ids = Array.from(registry.nodes.keys());
|
|
3103
3228
|
const [query, setQuery] = useState("");
|
|
3104
3229
|
const q = query.trim().toLowerCase();
|
|
3105
3230
|
const filteredIds = q
|
|
3106
|
-
?
|
|
3107
|
-
:
|
|
3231
|
+
? nodeIds.filter((id) => id.toLowerCase().includes(q))
|
|
3232
|
+
: nodeIds;
|
|
3108
3233
|
const root = { __children: {} };
|
|
3109
3234
|
for (const id of filteredIds) {
|
|
3110
3235
|
const parts = id.split(".");
|
|
@@ -3128,11 +3253,11 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3128
3253
|
if (!ref.current)
|
|
3129
3254
|
return;
|
|
3130
3255
|
if (!ref.current.contains(e.target))
|
|
3131
|
-
onClose();
|
|
3256
|
+
handlers.onClose();
|
|
3132
3257
|
};
|
|
3133
3258
|
const onKey = (e) => {
|
|
3134
3259
|
if (e.key === "Escape")
|
|
3135
|
-
onClose();
|
|
3260
|
+
handlers.onClose();
|
|
3136
3261
|
};
|
|
3137
3262
|
window.addEventListener("mousedown", onDown, true);
|
|
3138
3263
|
window.addEventListener("keydown", onKey);
|
|
@@ -3140,7 +3265,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3140
3265
|
window.removeEventListener("mousedown", onDown, true);
|
|
3141
3266
|
window.removeEventListener("keydown", onKey);
|
|
3142
3267
|
};
|
|
3143
|
-
}, [open,
|
|
3268
|
+
}, [open, handlers]);
|
|
3144
3269
|
// Focus search input when menu opens
|
|
3145
3270
|
const inputRef = useRef(null);
|
|
3146
3271
|
useEffect(() => {
|
|
@@ -3159,8 +3284,8 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3159
3284
|
const handleClick = (typeId) => {
|
|
3160
3285
|
// project() is deprecated; use screenToFlowPosition for screen coordinates
|
|
3161
3286
|
const p = rf.screenToFlowPosition({ x: clientPos.x, y: clientPos.y });
|
|
3162
|
-
|
|
3163
|
-
onClose();
|
|
3287
|
+
handlers.onAddNode(typeId, { position: p });
|
|
3288
|
+
handlers.onClose();
|
|
3164
3289
|
};
|
|
3165
3290
|
const renderTree = (tree, path = []) => {
|
|
3166
3291
|
const entries = Object.entries(tree?.__children ?? {}).sort((a, b) => a[0].localeCompare(b[0]));
|
|
@@ -3182,8 +3307,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3182
3307
|
}, children: [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 px-2 py-1 text-sm outline-none focus:border-gray-400", 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" })) })] }));
|
|
3183
3308
|
}
|
|
3184
3309
|
|
|
3185
|
-
function NodeContextMenu({ open, clientPos, nodeId,
|
|
3186
|
-
const { wb, runner, engineKind, registry, outputsMap, outputTypesMap } = useWorkbenchContext();
|
|
3310
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeableOutputs, }) {
|
|
3187
3311
|
const ref = useRef(null);
|
|
3188
3312
|
// outside click + ESC
|
|
3189
3313
|
useEffect(() => {
|
|
@@ -3193,11 +3317,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3193
3317
|
if (!ref.current)
|
|
3194
3318
|
return;
|
|
3195
3319
|
if (!ref.current.contains(e.target))
|
|
3196
|
-
onClose();
|
|
3320
|
+
handlers.onClose();
|
|
3197
3321
|
};
|
|
3198
3322
|
const onKey = (e) => {
|
|
3199
3323
|
if (e.key === "Escape")
|
|
3200
|
-
onClose();
|
|
3324
|
+
handlers.onClose();
|
|
3201
3325
|
};
|
|
3202
3326
|
window.addEventListener("mousedown", onDown, true);
|
|
3203
3327
|
window.addEventListener("keydown", onKey);
|
|
@@ -3205,48 +3329,26 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3205
3329
|
window.removeEventListener("mousedown", onDown, true);
|
|
3206
3330
|
window.removeEventListener("keydown", onKey);
|
|
3207
3331
|
};
|
|
3208
|
-
}, [open,
|
|
3332
|
+
}, [open, handlers]);
|
|
3209
3333
|
useEffect(() => {
|
|
3210
3334
|
if (open)
|
|
3211
3335
|
ref.current?.focus();
|
|
3212
3336
|
}, [open]);
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
const base = tId.slice(0, -2);
|
|
3229
|
-
const tArr = registry.types.get(tId);
|
|
3230
|
-
const tElem = registry.types.get(base);
|
|
3231
|
-
const arrT = tArr?.bakeTarget;
|
|
3232
|
-
const elemT = tElem?.bakeTarget;
|
|
3233
|
-
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3234
|
-
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3235
|
-
out.push(h);
|
|
3236
|
-
}
|
|
3237
|
-
else {
|
|
3238
|
-
const t = registry.types.get(tId);
|
|
3239
|
-
const bt = t?.bakeTarget;
|
|
3240
|
-
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3241
|
-
out.push(h);
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
return out;
|
|
3245
|
-
}
|
|
3246
|
-
catch {
|
|
3247
|
-
return [];
|
|
3248
|
-
}
|
|
3249
|
-
};
|
|
3337
|
+
if (!open || !clientPos || !nodeId)
|
|
3338
|
+
return null;
|
|
3339
|
+
// clamp
|
|
3340
|
+
const MENU_MIN_WIDTH = 180;
|
|
3341
|
+
const PADDING = 16;
|
|
3342
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3343
|
+
(MENU_MIN_WIDTH + PADDING));
|
|
3344
|
+
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3345
|
+
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", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3346
|
+
e.preventDefault();
|
|
3347
|
+
e.stopPropagation();
|
|
3348
|
+
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDelete, children: "Delete" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicate, children: "Duplicate" }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }), bakeableOutputs.length > 0 && (jsxs(Fragment, { children: [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))), jsx("div", { className: "h-px bg-gray-200 my-1" })] })), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" })] }));
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose) {
|
|
3250
3352
|
const doBake = async (handleId) => {
|
|
3251
3353
|
try {
|
|
3252
3354
|
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
@@ -3279,7 +3381,6 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3279
3381
|
const newId = wb.addNode({
|
|
3280
3382
|
typeId: singleTarget.nodeTypeId,
|
|
3281
3383
|
position: { x: pos.x + 180, y: pos.y },
|
|
3282
|
-
params: {},
|
|
3283
3384
|
});
|
|
3284
3385
|
runner.update(wb.export());
|
|
3285
3386
|
await runner.whenIdle();
|
|
@@ -3290,12 +3391,9 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3290
3391
|
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
3291
3392
|
const inType = getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
3292
3393
|
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3293
|
-
const newId =
|
|
3294
|
-
wb.addNode({
|
|
3295
|
-
nodeId: newId,
|
|
3394
|
+
const newId = wb.addNode({
|
|
3296
3395
|
typeId: arrTarget.nodeTypeId,
|
|
3297
3396
|
position: { x: pos.x + 180, y: pos.y },
|
|
3298
|
-
params: {},
|
|
3299
3397
|
});
|
|
3300
3398
|
runner.update(wb.export());
|
|
3301
3399
|
await runner.whenIdle();
|
|
@@ -3313,14 +3411,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3313
3411
|
const DY = 160;
|
|
3314
3412
|
const nodeIds = [];
|
|
3315
3413
|
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
3316
|
-
const cv = coercedItems[idx];
|
|
3317
3414
|
const col = idx % COLS;
|
|
3318
3415
|
const row = Math.floor(idx / COLS);
|
|
3319
3416
|
const newId = wb.addNode({
|
|
3320
3417
|
typeId: elemTarget.nodeTypeId,
|
|
3321
3418
|
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
3322
|
-
params: {},
|
|
3323
|
-
initialInputs: { [elemTarget.inputHandle]: structuredClone(cv) },
|
|
3324
3419
|
});
|
|
3325
3420
|
nodeIds.push(newId);
|
|
3326
3421
|
}
|
|
@@ -3338,63 +3433,88 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3338
3433
|
}
|
|
3339
3434
|
catch { }
|
|
3340
3435
|
};
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3436
|
+
return {
|
|
3437
|
+
onDelete: () => {
|
|
3438
|
+
wb.removeNode(nodeId);
|
|
3439
|
+
onClose();
|
|
3440
|
+
},
|
|
3441
|
+
onDuplicate: async () => {
|
|
3442
|
+
const def = wb.export();
|
|
3443
|
+
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3444
|
+
if (!n)
|
|
3445
|
+
return onClose();
|
|
3446
|
+
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
3447
|
+
const newId = wb.addNode({
|
|
3448
|
+
typeId: n.typeId,
|
|
3449
|
+
params: n.params,
|
|
3450
|
+
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
3451
|
+
initialInputs: n.initialInputs,
|
|
3452
|
+
resolvedHandles: n.resolvedHandles,
|
|
3453
|
+
});
|
|
3454
|
+
await runner.whenIdle();
|
|
3455
|
+
runner.setInputs(newId, { ...runner.getInputs(def)[nodeId] });
|
|
3456
|
+
onClose();
|
|
3457
|
+
},
|
|
3458
|
+
onRunPull: async () => {
|
|
3459
|
+
try {
|
|
3460
|
+
await runner.computeNode(nodeId);
|
|
3461
|
+
}
|
|
3462
|
+
catch { }
|
|
3463
|
+
onClose();
|
|
3464
|
+
},
|
|
3465
|
+
onBake: async (handleId) => {
|
|
3466
|
+
await doBake(handleId);
|
|
3467
|
+
onClose();
|
|
3468
|
+
},
|
|
3469
|
+
onCopyId: async () => {
|
|
3470
|
+
try {
|
|
3471
|
+
await navigator.clipboard.writeText(nodeId);
|
|
3472
|
+
}
|
|
3473
|
+
catch { }
|
|
3474
|
+
onClose();
|
|
3475
|
+
},
|
|
3476
|
+
onClose,
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
function getBakeableOutputs(nodeId, wb, registry, outputTypesMap) {
|
|
3480
|
+
try {
|
|
3347
3481
|
const def = wb.export();
|
|
3348
|
-
const
|
|
3349
|
-
if (!
|
|
3350
|
-
return
|
|
3351
|
-
const
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3482
|
+
const node = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3483
|
+
if (!node)
|
|
3484
|
+
return [];
|
|
3485
|
+
const desc = registry.nodes.get(node.typeId);
|
|
3486
|
+
const handles = Object.keys(desc?.outputs || {});
|
|
3487
|
+
const out = [];
|
|
3488
|
+
for (const h of handles) {
|
|
3489
|
+
const tId = outputTypesMap?.[nodeId]?.[h];
|
|
3490
|
+
if (!tId)
|
|
3491
|
+
continue;
|
|
3492
|
+
if (tId.endsWith("[]")) {
|
|
3493
|
+
const base = tId.slice(0, -2);
|
|
3494
|
+
const tArr = registry.types.get(tId);
|
|
3495
|
+
const tElem = registry.types.get(base);
|
|
3496
|
+
const arrT = tArr?.bakeTarget;
|
|
3497
|
+
const elemT = tElem?.bakeTarget;
|
|
3498
|
+
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3499
|
+
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3500
|
+
out.push(h);
|
|
3501
|
+
}
|
|
3502
|
+
else {
|
|
3503
|
+
const t = registry.types.get(tId);
|
|
3504
|
+
const bt = t?.bakeTarget;
|
|
3505
|
+
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3506
|
+
out.push(h);
|
|
3507
|
+
}
|
|
3373
3508
|
}
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
// clamp
|
|
3380
|
-
const MENU_MIN_WIDTH = 180;
|
|
3381
|
-
const PADDING = 16;
|
|
3382
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3383
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
3384
|
-
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3385
|
-
const canRunPull = engineKind()?.toString() === "pull";
|
|
3386
|
-
const outs = getBakeableOutputs();
|
|
3387
|
-
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", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3388
|
-
e.preventDefault();
|
|
3389
|
-
e.stopPropagation();
|
|
3390
|
-
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDelete, children: "Delete" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDuplicate, children: "Duplicate" }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }), outs.length > 0 && (jsxs(Fragment, { children: [jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), outs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: async () => {
|
|
3391
|
-
await doBake(h);
|
|
3392
|
-
onClose();
|
|
3393
|
-
}, children: ["Bake: ", h] }, h))), jsx("div", { className: "h-px bg-gray-200 my-1" })] })), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleCopyId, children: "Copy Node ID" })] }));
|
|
3509
|
+
return out;
|
|
3510
|
+
}
|
|
3511
|
+
catch {
|
|
3512
|
+
return [];
|
|
3513
|
+
}
|
|
3394
3514
|
}
|
|
3395
3515
|
|
|
3396
3516
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
3397
|
-
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, } = useWorkbenchContext();
|
|
3517
|
+
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, runner, engineKind, } = useWorkbenchContext();
|
|
3398
3518
|
const nodeValidation = validationByNode;
|
|
3399
3519
|
const edgeValidation = validationByEdge.errors;
|
|
3400
3520
|
// Keep stable references for nodes/edges to avoid unnecessary updates
|
|
@@ -3461,9 +3581,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3461
3581
|
},
|
|
3462
3582
|
}));
|
|
3463
3583
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
3584
|
+
const ui = wb.getUI();
|
|
3464
3585
|
const { nodeTypes, resolveNodeType } = useMemo(() => {
|
|
3465
3586
|
// Build nodeTypes map using UI extension registry
|
|
3466
|
-
const ui = wb.getUI();
|
|
3467
3587
|
const custom = new Map(); // Include all types present in registry AND current graph to avoid timing issues
|
|
3468
3588
|
const def = wb.export();
|
|
3469
3589
|
const ids = new Set([
|
|
@@ -3485,7 +3605,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3485
3605
|
const resolver = (nodeTypeId) => custom.has(nodeTypeId) ? `spark-${nodeTypeId}` : "spark-default";
|
|
3486
3606
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
3487
3607
|
// Include uiVersion to recompute when custom renderers are registered
|
|
3488
|
-
}, [wb, registry, uiVersion]);
|
|
3608
|
+
}, [wb, registry, uiVersion, ui]);
|
|
3489
3609
|
const { nodes, edges } = useMemo(() => {
|
|
3490
3610
|
const def = wb.export();
|
|
3491
3611
|
const sel = wb.getSelection();
|
|
@@ -3649,19 +3769,63 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3649
3769
|
setNodeMenuOpen(false);
|
|
3650
3770
|
}
|
|
3651
3771
|
};
|
|
3652
|
-
const addNodeAt = useCallback((typeId,
|
|
3653
|
-
wb.addNode({
|
|
3654
|
-
|
|
3772
|
+
const addNodeAt = useCallback(async (typeId, opts) => {
|
|
3773
|
+
const nodeId = wb.addNode({
|
|
3774
|
+
typeId,
|
|
3775
|
+
initialInputs: opts.initialInputs,
|
|
3776
|
+
position: opts.position,
|
|
3777
|
+
});
|
|
3778
|
+
if (opts.inputs) {
|
|
3779
|
+
runner.update(wb.export());
|
|
3780
|
+
await runner.whenIdle();
|
|
3781
|
+
runner.setInputs(nodeId, opts.inputs);
|
|
3782
|
+
}
|
|
3783
|
+
}, [wb, runner]);
|
|
3655
3784
|
const onCloseMenu = useCallback(() => {
|
|
3656
3785
|
setMenuOpen(false);
|
|
3657
3786
|
}, []);
|
|
3658
3787
|
const onCloseNodeMenu = useCallback(() => {
|
|
3659
3788
|
setNodeMenuOpen(false);
|
|
3660
3789
|
}, []);
|
|
3790
|
+
const nodeIds = useMemo(() => Array.from(registry.nodes.keys()), [registry]);
|
|
3791
|
+
const defaultContextMenuHandlers = useMemo(() => ({
|
|
3792
|
+
onAddNode: addNodeAt,
|
|
3793
|
+
onClose: onCloseMenu,
|
|
3794
|
+
}), [addNodeAt, onCloseMenu]);
|
|
3795
|
+
const nodeContextMenuHandlers = useMemo(() => {
|
|
3796
|
+
if (!nodeAtMenu)
|
|
3797
|
+
return null;
|
|
3798
|
+
return createNodeContextMenuHandlers(nodeAtMenu, wb, runner, registry, outputsMap, outputTypesMap, onCloseNodeMenu);
|
|
3799
|
+
}, [
|
|
3800
|
+
nodeAtMenu,
|
|
3801
|
+
wb,
|
|
3802
|
+
runner,
|
|
3803
|
+
registry,
|
|
3804
|
+
outputsMap,
|
|
3805
|
+
outputTypesMap,
|
|
3806
|
+
onCloseNodeMenu,
|
|
3807
|
+
]);
|
|
3808
|
+
const canRunPull = useMemo(() => engineKind()?.toString() === "pull", [engineKind]);
|
|
3809
|
+
const bakeableOutputs = useMemo(() => {
|
|
3810
|
+
if (!nodeAtMenu)
|
|
3811
|
+
return [];
|
|
3812
|
+
return getBakeableOutputs(nodeAtMenu, wb, registry, outputTypesMap);
|
|
3813
|
+
}, [nodeAtMenu, wb, registry, outputTypesMap]);
|
|
3814
|
+
// Get custom renderers from UI extension registry (reactive to uiVersion changes)
|
|
3815
|
+
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
|
|
3816
|
+
return {
|
|
3817
|
+
BackgroundRenderer: ui.getBackgroundRenderer(),
|
|
3818
|
+
MinimapRenderer: ui.getMinimapRenderer(),
|
|
3819
|
+
ControlsRenderer: ui.getControlsRenderer(),
|
|
3820
|
+
DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
|
|
3821
|
+
NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
|
|
3822
|
+
connectionLineRenderer: ui.getConnectionLineRenderer(),
|
|
3823
|
+
};
|
|
3824
|
+
}, [ui, uiVersion]);
|
|
3661
3825
|
const onMoveEnd = useCallback(() => {
|
|
3662
3826
|
if (rfInstanceRef.current) {
|
|
3663
3827
|
const viewport = rfInstanceRef.current.getViewport();
|
|
3664
|
-
wb.setViewport({ x: viewport.x, y: viewport.y, zoom: viewport.zoom });
|
|
3828
|
+
wb.setViewport({ x: viewport.x, y: viewport.y, zoom: viewport.zoom }, { commit: true });
|
|
3665
3829
|
}
|
|
3666
3830
|
}, [wb]);
|
|
3667
3831
|
const viewportRef = useRef(null);
|
|
@@ -3682,7 +3846,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3682
3846
|
});
|
|
3683
3847
|
}
|
|
3684
3848
|
});
|
|
3685
|
-
return (jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsx(ReactFlowProvider, { children: jsxs(ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, selectionOnDrag: true, onInit: (inst) => {
|
|
3849
|
+
return (jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsx(ReactFlowProvider, { children: jsxs(ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
|
|
3686
3850
|
rfInstanceRef.current = inst;
|
|
3687
3851
|
const savedViewport = wb.getViewport();
|
|
3688
3852
|
if (savedViewport) {
|
|
@@ -3693,7 +3857,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3693
3857
|
zoom: savedViewport.zoom,
|
|
3694
3858
|
});
|
|
3695
3859
|
}
|
|
3696
|
-
}, 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", fitView: true, children: [jsx(Background, { id: "workbench-canvas-background", variant: BackgroundVariant.Dots, gap: 12, size: 1 }), jsx(MiniMap, {}), jsx(Controls, {}), jsx(
|
|
3860
|
+
}, 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", fitView: true, 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 })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds })), !!nodeAtMenu &&
|
|
3861
|
+
nodeContextMenuHandlers &&
|
|
3862
|
+
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })))] }) }) }));
|
|
3697
3863
|
});
|
|
3698
3864
|
|
|
3699
3865
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -3828,9 +3994,20 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3828
3994
|
}
|
|
3829
3995
|
catch { }
|
|
3830
3996
|
});
|
|
3997
|
+
const off3 = wb.on("graphUiChanged", (evt) => {
|
|
3998
|
+
if (!evt.commit)
|
|
3999
|
+
return;
|
|
4000
|
+
try {
|
|
4001
|
+
const cur = wb.export();
|
|
4002
|
+
const inputs = runner.getInputs(cur);
|
|
4003
|
+
onChange({ def: cur, inputs });
|
|
4004
|
+
}
|
|
4005
|
+
catch { }
|
|
4006
|
+
});
|
|
3831
4007
|
return () => {
|
|
3832
4008
|
off1();
|
|
3833
4009
|
off2();
|
|
4010
|
+
off3();
|
|
3834
4011
|
};
|
|
3835
4012
|
}, [wb, runner, onChange]);
|
|
3836
4013
|
const applyExample = useCallback(async (key) => {
|
|
@@ -3866,24 +4043,9 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3866
4043
|
setRegistry,
|
|
3867
4044
|
backendKind,
|
|
3868
4045
|
]);
|
|
3869
|
-
const
|
|
4046
|
+
const download$1 = useCallback(async () => {
|
|
3870
4047
|
try {
|
|
3871
|
-
|
|
3872
|
-
const inputs = runner.getInputs(def);
|
|
3873
|
-
const payload = { def, inputs };
|
|
3874
|
-
const pretty = JSON.stringify(payload, null, 2);
|
|
3875
|
-
const blob = new Blob([pretty], { type: "application/json" });
|
|
3876
|
-
const url = URL.createObjectURL(blob);
|
|
3877
|
-
const a = document.createElement("a");
|
|
3878
|
-
const d = new Date();
|
|
3879
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
3880
|
-
const ts = `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`;
|
|
3881
|
-
a.href = url;
|
|
3882
|
-
a.download = `spark-graph-${ts}.json`;
|
|
3883
|
-
document.body.appendChild(a);
|
|
3884
|
-
a.click();
|
|
3885
|
-
a.remove();
|
|
3886
|
-
URL.revokeObjectURL(url);
|
|
4048
|
+
await download(wb, runner);
|
|
3887
4049
|
}
|
|
3888
4050
|
catch (err) {
|
|
3889
4051
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -3897,51 +4059,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3897
4059
|
return;
|
|
3898
4060
|
const text = await file.text();
|
|
3899
4061
|
const parsed = JSON.parse(text);
|
|
3900
|
-
|
|
3901
|
-
const isSnapshot = parsed &&
|
|
3902
|
-
typeof parsed === "object" &&
|
|
3903
|
-
(parsed.def || parsed.inputs || parsed.outputs || parsed.environment);
|
|
3904
|
-
if (isSnapshot) {
|
|
3905
|
-
const def = parsed.def;
|
|
3906
|
-
const positions = parsed.positions || {};
|
|
3907
|
-
const environment = parsed.environment || {};
|
|
3908
|
-
const inputs = parsed.inputs || {};
|
|
3909
|
-
if (def && runner.isRunning()) {
|
|
3910
|
-
// Remote exact restore path
|
|
3911
|
-
await runner.applySnapshotFull({
|
|
3912
|
-
def,
|
|
3913
|
-
environment,
|
|
3914
|
-
inputs,
|
|
3915
|
-
outputs: parsed.outputs || {},
|
|
3916
|
-
});
|
|
3917
|
-
await wb.load(def);
|
|
3918
|
-
if (positions && typeof positions === "object")
|
|
3919
|
-
wb.setPositions(positions);
|
|
3920
|
-
}
|
|
3921
|
-
else if (!runner.isRunning()) {
|
|
3922
|
-
alert("Engine is not running");
|
|
3923
|
-
}
|
|
3924
|
-
else {
|
|
3925
|
-
alert("Graph definition is empty");
|
|
3926
|
-
}
|
|
3927
|
-
}
|
|
3928
|
-
else {
|
|
3929
|
-
const def = parsed?.def ?? parsed;
|
|
3930
|
-
const inputs = parsed?.inputs ?? {};
|
|
3931
|
-
await wb.load(def);
|
|
3932
|
-
try {
|
|
3933
|
-
runner.build(wb.export());
|
|
3934
|
-
}
|
|
3935
|
-
catch { }
|
|
3936
|
-
if (inputs && typeof inputs === "object") {
|
|
3937
|
-
for (const [nodeId, map] of Object.entries(inputs)) {
|
|
3938
|
-
try {
|
|
3939
|
-
runner.setInputs(nodeId, map);
|
|
3940
|
-
}
|
|
3941
|
-
catch { }
|
|
3942
|
-
}
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
4062
|
+
await upload(parsed, wb, runner);
|
|
3945
4063
|
}
|
|
3946
4064
|
catch (err) {
|
|
3947
4065
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -4197,36 +4315,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
4197
4315
|
// Normal change when not running
|
|
4198
4316
|
onEngineChange?.(kind);
|
|
4199
4317
|
}
|
|
4200
|
-
}, children: [jsx("option", { value: "", children: "Select Engine\u2026" }), jsx("option", { value: "push", children: "Push" }), jsx("option", { value: "batched", children: "Batched" }), jsx("option", { value: "pull", children: "Pull" }), jsx("option", { value: "hybrid", children: "Hybrid" }), jsx("option", { value: "step", children: "Step" })] }), engineKind === "step" && (jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => runner.step(), disabled: !isGraphRunning, title: "Step", children: jsx(PlayPauseIcon, { size: 24 }) })), engineKind === "batched" && (jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => runner.flush(), disabled: !isGraphRunning, title: "Flush", children: jsx(LightningIcon, { size: 24 }) })), 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:
|
|
4201
|
-
try {
|
|
4202
|
-
const def = wb.export();
|
|
4203
|
-
const positions = wb.getPositions();
|
|
4204
|
-
const snapshot = await runner.snapshotFull();
|
|
4205
|
-
const payload = {
|
|
4206
|
-
...snapshot,
|
|
4207
|
-
def,
|
|
4208
|
-
positions,
|
|
4209
|
-
schemaVersion: 1,
|
|
4210
|
-
};
|
|
4211
|
-
const pretty = JSON.stringify(payload, null, 2);
|
|
4212
|
-
const blob = new Blob([pretty], { type: "application/json" });
|
|
4213
|
-
const url = URL.createObjectURL(blob);
|
|
4214
|
-
const a = document.createElement("a");
|
|
4215
|
-
const d = new Date();
|
|
4216
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
4217
|
-
const ts = `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`;
|
|
4218
|
-
a.href = url;
|
|
4219
|
-
a.download = `spark-snapshot-${ts}.json`;
|
|
4220
|
-
document.body.appendChild(a);
|
|
4221
|
-
a.click();
|
|
4222
|
-
a.remove();
|
|
4223
|
-
URL.revokeObjectURL(url);
|
|
4224
|
-
}
|
|
4225
|
-
catch (err) {
|
|
4226
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4227
|
-
alert(message);
|
|
4228
|
-
}
|
|
4229
|
-
}, 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 }) }), 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", 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 })] })] }));
|
|
4318
|
+
}, children: [jsx("option", { value: "", children: "Select Engine\u2026" }), jsx("option", { value: "push", children: "Push" }), jsx("option", { value: "batched", children: "Batched" }), jsx("option", { value: "pull", children: "Pull" }), jsx("option", { value: "hybrid", children: "Hybrid" }), jsx("option", { value: "step", children: "Step" })] }), engineKind === "step" && (jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => runner.step(), disabled: !isGraphRunning, title: "Step", children: jsx(PlayPauseIcon, { size: 24 }) })), engineKind === "batched" && (jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => runner.flush(), disabled: !isGraphRunning, title: "Flush", children: jsx(LightningIcon, { size: 24 }) })), 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 }) }), 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", 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 })] })] }));
|
|
4230
4319
|
}
|
|
4231
4320
|
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
|
|
4232
4321
|
const [registry, setRegistry] = useState(createSimpleGraphRegistry());
|
|
@@ -4301,5 +4390,5 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
|
|
|
4301
4390
|
return (jsx(WorkbenchProvider, { wb: wb, runner: runner, registry: registry, setRegistry: setRegistry, overrides: overrides, uiVersion: uiVersion, children: jsx(WorkbenchStudioCanvas, { setRegistry: setRegistry, autoScroll: autoScroll, onAutoScrollChange: onAutoScrollChange, example: example, onExampleChange: onExampleChange, engine: engine, onEngineChange: onEngineChange, 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 }) }));
|
|
4302
4391
|
}
|
|
4303
4392
|
|
|
4304
|
-
export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, estimateNodeSize, formatDataUrlAsLabel, formatDeclaredTypeSignature, getHandleClassName, getNodeBorderClassNames, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
|
|
4393
|
+
export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, download, estimateNodeSize, formatDataUrlAsLabel, formatDeclaredTypeSignature, getHandleClassName, getNodeBorderClassNames, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, upload, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
|
|
4305
4394
|
//# sourceMappingURL=index.js.map
|