@bian-womp/spark-workbench 0.2.54 → 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 +234 -184
- 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 +0 -1
- package/lib/cjs/src/core/InMemoryWorkbench.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/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/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/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 +235 -185
- 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 +0 -1
- package/lib/esm/src/core/InMemoryWorkbench.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/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/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/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,4 +1,4 @@
|
|
|
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';
|
|
@@ -10,71 +10,75 @@ import isEqual from 'lodash/isEqual';
|
|
|
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() {
|
|
@@ -162,11 +167,14 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
162
167
|
return { ok: issues.every((i) => i.level !== "error"), issues };
|
|
163
168
|
}
|
|
164
169
|
addNode(node) {
|
|
165
|
-
const id = node.nodeId ??
|
|
170
|
+
const id = node.nodeId ??
|
|
171
|
+
this.genId("n", new Set(this.def.nodes.map((n) => n.nodeId)));
|
|
166
172
|
this.def.nodes.push({
|
|
167
173
|
nodeId: id,
|
|
168
174
|
typeId: node.typeId,
|
|
169
175
|
params: node.params,
|
|
176
|
+
initialInputs: node.initialInputs,
|
|
177
|
+
resolvedHandles: node.resolvedHandles,
|
|
170
178
|
});
|
|
171
179
|
if (node.position)
|
|
172
180
|
this.positions[id] = node.position;
|
|
@@ -188,7 +196,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
188
196
|
this.refreshValidation();
|
|
189
197
|
}
|
|
190
198
|
connect(edge) {
|
|
191
|
-
const id = edge.id ?? this.
|
|
199
|
+
const id = edge.id ?? this.genId("e", new Set(this.def.edges.map((e) => e.id)));
|
|
192
200
|
this.def.edges.push({
|
|
193
201
|
id,
|
|
194
202
|
source: { ...edge.source },
|
|
@@ -329,9 +337,6 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
329
337
|
for (const h of Array.from(set))
|
|
330
338
|
h(payload);
|
|
331
339
|
}
|
|
332
|
-
generateId(prefix) {
|
|
333
|
-
return `${prefix}${Math.random().toString(36).slice(2, 8)}`;
|
|
334
|
-
}
|
|
335
340
|
}
|
|
336
341
|
|
|
337
342
|
class CLIWorkbench {
|
|
@@ -463,6 +468,18 @@ class AbstractGraphRunner {
|
|
|
463
468
|
}
|
|
464
469
|
}
|
|
465
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
|
+
}
|
|
466
483
|
on(event, handler) {
|
|
467
484
|
if (!this.listeners.has(event))
|
|
468
485
|
this.listeners.set(event, new Set());
|
|
@@ -624,16 +641,6 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
624
641
|
}
|
|
625
642
|
return out;
|
|
626
643
|
}
|
|
627
|
-
getInputDefaults(def) {
|
|
628
|
-
const out = {};
|
|
629
|
-
for (const n of def.nodes) {
|
|
630
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
631
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
632
|
-
out[n.nodeId] = dynDefaults;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
return out;
|
|
636
|
-
}
|
|
637
644
|
async snapshotFull() {
|
|
638
645
|
const def = undefined; // UI will supply def/positions on download for local
|
|
639
646
|
const inputs = this.getInputs(this.runtime
|
|
@@ -661,11 +668,25 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
661
668
|
if (payload.def)
|
|
662
669
|
this.build(payload.def);
|
|
663
670
|
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
664
|
-
|
|
671
|
+
this.hydrateSnapshotFull(payload);
|
|
672
|
+
}
|
|
673
|
+
hydrateSnapshotFull(snapshot) {
|
|
674
|
+
// Hydrate via runtime for exact restore (this emits events on runtime emitter)
|
|
665
675
|
this.runtime?.hydrate({
|
|
666
|
-
inputs:
|
|
667
|
-
outputs:
|
|
676
|
+
inputs: snapshot.inputs || {},
|
|
677
|
+
outputs: snapshot.outputs || {},
|
|
668
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
|
+
}
|
|
669
690
|
}
|
|
670
691
|
dispose() {
|
|
671
692
|
super.dispose();
|
|
@@ -1222,16 +1243,6 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1222
1243
|
}
|
|
1223
1244
|
return out;
|
|
1224
1245
|
}
|
|
1225
|
-
getInputDefaults(def) {
|
|
1226
|
-
const out = {};
|
|
1227
|
-
for (const n of def.nodes) {
|
|
1228
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
1229
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
1230
|
-
out[n.nodeId] = dynDefaults;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return out;
|
|
1234
|
-
}
|
|
1235
1246
|
dispose() {
|
|
1236
1247
|
// Idempotent: allow multiple calls safely
|
|
1237
1248
|
if (this.disposed)
|
|
@@ -3212,15 +3223,13 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
3212
3223
|
} })] }));
|
|
3213
3224
|
}
|
|
3214
3225
|
|
|
3215
|
-
function DefaultContextMenu({ open, clientPos,
|
|
3216
|
-
const { registry } = useWorkbenchContext();
|
|
3226
|
+
function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, }) {
|
|
3217
3227
|
const rf = useReactFlow();
|
|
3218
|
-
const ids = Array.from(registry.nodes.keys());
|
|
3219
3228
|
const [query, setQuery] = useState("");
|
|
3220
3229
|
const q = query.trim().toLowerCase();
|
|
3221
3230
|
const filteredIds = q
|
|
3222
|
-
?
|
|
3223
|
-
:
|
|
3231
|
+
? nodeIds.filter((id) => id.toLowerCase().includes(q))
|
|
3232
|
+
: nodeIds;
|
|
3224
3233
|
const root = { __children: {} };
|
|
3225
3234
|
for (const id of filteredIds) {
|
|
3226
3235
|
const parts = id.split(".");
|
|
@@ -3244,11 +3253,11 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3244
3253
|
if (!ref.current)
|
|
3245
3254
|
return;
|
|
3246
3255
|
if (!ref.current.contains(e.target))
|
|
3247
|
-
onClose();
|
|
3256
|
+
handlers.onClose();
|
|
3248
3257
|
};
|
|
3249
3258
|
const onKey = (e) => {
|
|
3250
3259
|
if (e.key === "Escape")
|
|
3251
|
-
onClose();
|
|
3260
|
+
handlers.onClose();
|
|
3252
3261
|
};
|
|
3253
3262
|
window.addEventListener("mousedown", onDown, true);
|
|
3254
3263
|
window.addEventListener("keydown", onKey);
|
|
@@ -3256,7 +3265,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3256
3265
|
window.removeEventListener("mousedown", onDown, true);
|
|
3257
3266
|
window.removeEventListener("keydown", onKey);
|
|
3258
3267
|
};
|
|
3259
|
-
}, [open,
|
|
3268
|
+
}, [open, handlers]);
|
|
3260
3269
|
// Focus search input when menu opens
|
|
3261
3270
|
const inputRef = useRef(null);
|
|
3262
3271
|
useEffect(() => {
|
|
@@ -3275,8 +3284,8 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3275
3284
|
const handleClick = (typeId) => {
|
|
3276
3285
|
// project() is deprecated; use screenToFlowPosition for screen coordinates
|
|
3277
3286
|
const p = rf.screenToFlowPosition({ x: clientPos.x, y: clientPos.y });
|
|
3278
|
-
|
|
3279
|
-
onClose();
|
|
3287
|
+
handlers.onAddNode(typeId, { position: p });
|
|
3288
|
+
handlers.onClose();
|
|
3280
3289
|
};
|
|
3281
3290
|
const renderTree = (tree, path = []) => {
|
|
3282
3291
|
const entries = Object.entries(tree?.__children ?? {}).sort((a, b) => a[0].localeCompare(b[0]));
|
|
@@ -3298,8 +3307,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3298
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" })) })] }));
|
|
3299
3308
|
}
|
|
3300
3309
|
|
|
3301
|
-
function NodeContextMenu({ open, clientPos, nodeId,
|
|
3302
|
-
const { wb, runner, engineKind, registry, outputsMap, outputTypesMap } = useWorkbenchContext();
|
|
3310
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeableOutputs, }) {
|
|
3303
3311
|
const ref = useRef(null);
|
|
3304
3312
|
// outside click + ESC
|
|
3305
3313
|
useEffect(() => {
|
|
@@ -3309,11 +3317,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3309
3317
|
if (!ref.current)
|
|
3310
3318
|
return;
|
|
3311
3319
|
if (!ref.current.contains(e.target))
|
|
3312
|
-
onClose();
|
|
3320
|
+
handlers.onClose();
|
|
3313
3321
|
};
|
|
3314
3322
|
const onKey = (e) => {
|
|
3315
3323
|
if (e.key === "Escape")
|
|
3316
|
-
onClose();
|
|
3324
|
+
handlers.onClose();
|
|
3317
3325
|
};
|
|
3318
3326
|
window.addEventListener("mousedown", onDown, true);
|
|
3319
3327
|
window.addEventListener("keydown", onKey);
|
|
@@ -3321,48 +3329,26 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3321
3329
|
window.removeEventListener("mousedown", onDown, true);
|
|
3322
3330
|
window.removeEventListener("keydown", onKey);
|
|
3323
3331
|
};
|
|
3324
|
-
}, [open,
|
|
3332
|
+
}, [open, handlers]);
|
|
3325
3333
|
useEffect(() => {
|
|
3326
3334
|
if (open)
|
|
3327
3335
|
ref.current?.focus();
|
|
3328
3336
|
}, [open]);
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
const base = tId.slice(0, -2);
|
|
3345
|
-
const tArr = registry.types.get(tId);
|
|
3346
|
-
const tElem = registry.types.get(base);
|
|
3347
|
-
const arrT = tArr?.bakeTarget;
|
|
3348
|
-
const elemT = tElem?.bakeTarget;
|
|
3349
|
-
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3350
|
-
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3351
|
-
out.push(h);
|
|
3352
|
-
}
|
|
3353
|
-
else {
|
|
3354
|
-
const t = registry.types.get(tId);
|
|
3355
|
-
const bt = t?.bakeTarget;
|
|
3356
|
-
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3357
|
-
out.push(h);
|
|
3358
|
-
}
|
|
3359
|
-
}
|
|
3360
|
-
return out;
|
|
3361
|
-
}
|
|
3362
|
-
catch {
|
|
3363
|
-
return [];
|
|
3364
|
-
}
|
|
3365
|
-
};
|
|
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) {
|
|
3366
3352
|
const doBake = async (handleId) => {
|
|
3367
3353
|
try {
|
|
3368
3354
|
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
@@ -3395,7 +3381,6 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3395
3381
|
const newId = wb.addNode({
|
|
3396
3382
|
typeId: singleTarget.nodeTypeId,
|
|
3397
3383
|
position: { x: pos.x + 180, y: pos.y },
|
|
3398
|
-
params: {},
|
|
3399
3384
|
});
|
|
3400
3385
|
runner.update(wb.export());
|
|
3401
3386
|
await runner.whenIdle();
|
|
@@ -3406,12 +3391,9 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3406
3391
|
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
3407
3392
|
const inType = getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
3408
3393
|
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3409
|
-
const newId =
|
|
3410
|
-
wb.addNode({
|
|
3411
|
-
nodeId: newId,
|
|
3394
|
+
const newId = wb.addNode({
|
|
3412
3395
|
typeId: arrTarget.nodeTypeId,
|
|
3413
3396
|
position: { x: pos.x + 180, y: pos.y },
|
|
3414
|
-
params: {},
|
|
3415
3397
|
});
|
|
3416
3398
|
runner.update(wb.export());
|
|
3417
3399
|
await runner.whenIdle();
|
|
@@ -3429,14 +3411,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3429
3411
|
const DY = 160;
|
|
3430
3412
|
const nodeIds = [];
|
|
3431
3413
|
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
3432
|
-
const cv = coercedItems[idx];
|
|
3433
3414
|
const col = idx % COLS;
|
|
3434
3415
|
const row = Math.floor(idx / COLS);
|
|
3435
3416
|
const newId = wb.addNode({
|
|
3436
3417
|
typeId: elemTarget.nodeTypeId,
|
|
3437
3418
|
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
3438
|
-
params: {},
|
|
3439
|
-
initialInputs: { [elemTarget.inputHandle]: structuredClone(cv) },
|
|
3440
3419
|
});
|
|
3441
3420
|
nodeIds.push(newId);
|
|
3442
3421
|
}
|
|
@@ -3454,63 +3433,88 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3454
3433
|
}
|
|
3455
3434
|
catch { }
|
|
3456
3435
|
};
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
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 {
|
|
3463
3481
|
const def = wb.export();
|
|
3464
|
-
const
|
|
3465
|
-
if (!
|
|
3466
|
-
return
|
|
3467
|
-
const
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
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
|
+
}
|
|
3489
3508
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
// clamp
|
|
3496
|
-
const MENU_MIN_WIDTH = 180;
|
|
3497
|
-
const PADDING = 16;
|
|
3498
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3499
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
3500
|
-
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3501
|
-
const canRunPull = engineKind()?.toString() === "pull";
|
|
3502
|
-
const outs = getBakeableOutputs();
|
|
3503
|
-
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) => {
|
|
3504
|
-
e.preventDefault();
|
|
3505
|
-
e.stopPropagation();
|
|
3506
|
-
}, 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 () => {
|
|
3507
|
-
await doBake(h);
|
|
3508
|
-
onClose();
|
|
3509
|
-
}, 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
|
+
}
|
|
3510
3514
|
}
|
|
3511
3515
|
|
|
3512
3516
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
3513
|
-
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();
|
|
3514
3518
|
const nodeValidation = validationByNode;
|
|
3515
3519
|
const edgeValidation = validationByEdge.errors;
|
|
3516
3520
|
// Keep stable references for nodes/edges to avoid unnecessary updates
|
|
@@ -3577,9 +3581,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3577
3581
|
},
|
|
3578
3582
|
}));
|
|
3579
3583
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
3584
|
+
const ui = wb.getUI();
|
|
3580
3585
|
const { nodeTypes, resolveNodeType } = useMemo(() => {
|
|
3581
3586
|
// Build nodeTypes map using UI extension registry
|
|
3582
|
-
const ui = wb.getUI();
|
|
3583
3587
|
const custom = new Map(); // Include all types present in registry AND current graph to avoid timing issues
|
|
3584
3588
|
const def = wb.export();
|
|
3585
3589
|
const ids = new Set([
|
|
@@ -3601,7 +3605,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3601
3605
|
const resolver = (nodeTypeId) => custom.has(nodeTypeId) ? `spark-${nodeTypeId}` : "spark-default";
|
|
3602
3606
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
3603
3607
|
// Include uiVersion to recompute when custom renderers are registered
|
|
3604
|
-
}, [wb, registry, uiVersion]);
|
|
3608
|
+
}, [wb, registry, uiVersion, ui]);
|
|
3605
3609
|
const { nodes, edges } = useMemo(() => {
|
|
3606
3610
|
const def = wb.export();
|
|
3607
3611
|
const sel = wb.getSelection();
|
|
@@ -3765,15 +3769,59 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3765
3769
|
setNodeMenuOpen(false);
|
|
3766
3770
|
}
|
|
3767
3771
|
};
|
|
3768
|
-
const addNodeAt = useCallback((typeId,
|
|
3769
|
-
wb.addNode({
|
|
3770
|
-
|
|
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]);
|
|
3771
3784
|
const onCloseMenu = useCallback(() => {
|
|
3772
3785
|
setMenuOpen(false);
|
|
3773
3786
|
}, []);
|
|
3774
3787
|
const onCloseNodeMenu = useCallback(() => {
|
|
3775
3788
|
setNodeMenuOpen(false);
|
|
3776
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]);
|
|
3777
3825
|
const onMoveEnd = useCallback(() => {
|
|
3778
3826
|
if (rfInstanceRef.current) {
|
|
3779
3827
|
const viewport = rfInstanceRef.current.getViewport();
|
|
@@ -3798,7 +3846,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3798
3846
|
});
|
|
3799
3847
|
}
|
|
3800
3848
|
});
|
|
3801
|
-
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) => {
|
|
3802
3850
|
rfInstanceRef.current = inst;
|
|
3803
3851
|
const savedViewport = wb.getViewport();
|
|
3804
3852
|
if (savedViewport) {
|
|
@@ -3809,7 +3857,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3809
3857
|
zoom: savedViewport.zoom,
|
|
3810
3858
|
});
|
|
3811
3859
|
}
|
|
3812
|
-
}, 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 })))] }) }) }));
|
|
3813
3863
|
});
|
|
3814
3864
|
|
|
3815
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, }) {
|