@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/cjs/index.cjs
CHANGED
|
@@ -12,71 +12,75 @@ var isEqual = require('lodash/isEqual');
|
|
|
12
12
|
class DefaultUIExtensionRegistry {
|
|
13
13
|
constructor() {
|
|
14
14
|
this.nodeRenderers = new Map();
|
|
15
|
-
this.portRenderers = new Map();
|
|
16
|
-
this.edgeRenderers = new Map();
|
|
17
15
|
}
|
|
18
16
|
registerNodeRenderer(nodeTypeId, renderer) {
|
|
19
|
-
|
|
17
|
+
if (renderer === undefined) {
|
|
18
|
+
this.nodeRenderers.delete(nodeTypeId);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.nodeRenderers.set(nodeTypeId, renderer);
|
|
22
|
+
}
|
|
20
23
|
return this;
|
|
21
24
|
}
|
|
22
25
|
getNodeRenderer(nodeTypeId) {
|
|
23
26
|
return this.nodeRenderers.get(nodeTypeId);
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
28
|
+
getAllNodeRenderers() {
|
|
29
|
+
const result = {};
|
|
30
|
+
for (const [nodeTypeId, renderer] of this.nodeRenderers.entries()) {
|
|
31
|
+
result[nodeTypeId] = renderer;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
this.
|
|
35
|
+
registerIconProvider(provider) {
|
|
36
|
+
this.iconProvider = provider;
|
|
34
37
|
return this;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
return this.
|
|
39
|
+
getIconProvider() {
|
|
40
|
+
return this.iconProvider;
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
// React Flow renderers
|
|
43
|
+
registerConnectionLineRenderer(renderer) {
|
|
44
|
+
this.connectionLineRenderer = renderer;
|
|
41
45
|
return this;
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
return this.
|
|
47
|
+
getConnectionLineRenderer() {
|
|
48
|
+
return this.connectionLineRenderer;
|
|
45
49
|
}
|
|
46
|
-
|
|
47
|
-
this.
|
|
50
|
+
registerMinimapRenderer(renderer) {
|
|
51
|
+
this.minimapRenderer = renderer;
|
|
48
52
|
return this;
|
|
49
53
|
}
|
|
50
|
-
|
|
51
|
-
return this.
|
|
54
|
+
getMinimapRenderer() {
|
|
55
|
+
return this.minimapRenderer;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
this.
|
|
57
|
+
registerControlsRenderer(renderer) {
|
|
58
|
+
this.controlsRenderer = renderer;
|
|
55
59
|
return this;
|
|
56
60
|
}
|
|
57
|
-
|
|
58
|
-
return this.
|
|
61
|
+
getControlsRenderer() {
|
|
62
|
+
return this.controlsRenderer;
|
|
59
63
|
}
|
|
60
|
-
|
|
61
|
-
this.
|
|
64
|
+
registerBackgroundRenderer(renderer) {
|
|
65
|
+
this.backgroundRenderer = renderer;
|
|
62
66
|
return this;
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
return this.
|
|
68
|
+
getBackgroundRenderer() {
|
|
69
|
+
return this.backgroundRenderer;
|
|
66
70
|
}
|
|
67
|
-
|
|
68
|
-
this.
|
|
71
|
+
registerDefaultContextMenuRenderer(renderer) {
|
|
72
|
+
this.defaultContextMenuRenderer = renderer;
|
|
69
73
|
return this;
|
|
70
74
|
}
|
|
71
|
-
|
|
72
|
-
return this.
|
|
75
|
+
getDefaultContextMenuRenderer() {
|
|
76
|
+
return this.defaultContextMenuRenderer;
|
|
73
77
|
}
|
|
74
|
-
|
|
75
|
-
this.
|
|
78
|
+
registerNodeContextMenuRenderer(renderer) {
|
|
79
|
+
this.nodeContextMenuRenderer = renderer;
|
|
76
80
|
return this;
|
|
77
81
|
}
|
|
78
|
-
|
|
79
|
-
return this.
|
|
82
|
+
getNodeContextMenuRenderer() {
|
|
83
|
+
return this.nodeContextMenuRenderer;
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -86,6 +90,7 @@ class AbstractWorkbench {
|
|
|
86
90
|
this.layout = args.layout;
|
|
87
91
|
this.storage = args.storage;
|
|
88
92
|
this.serializer = args.serializer;
|
|
93
|
+
this.genId = args.genId || sparkGraph.generateId;
|
|
89
94
|
}
|
|
90
95
|
// Expose UI registry to adapters (React Flow, CLI) to allow overrides
|
|
91
96
|
getUI() {
|
|
@@ -164,11 +169,14 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
164
169
|
return { ok: issues.every((i) => i.level !== "error"), issues };
|
|
165
170
|
}
|
|
166
171
|
addNode(node) {
|
|
167
|
-
const id = node.nodeId ??
|
|
172
|
+
const id = node.nodeId ??
|
|
173
|
+
this.genId("n", new Set(this.def.nodes.map((n) => n.nodeId)));
|
|
168
174
|
this.def.nodes.push({
|
|
169
175
|
nodeId: id,
|
|
170
176
|
typeId: node.typeId,
|
|
171
177
|
params: node.params,
|
|
178
|
+
initialInputs: node.initialInputs,
|
|
179
|
+
resolvedHandles: node.resolvedHandles,
|
|
172
180
|
});
|
|
173
181
|
if (node.position)
|
|
174
182
|
this.positions[id] = node.position;
|
|
@@ -190,7 +198,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
190
198
|
this.refreshValidation();
|
|
191
199
|
}
|
|
192
200
|
connect(edge) {
|
|
193
|
-
const id = edge.id ?? this.
|
|
201
|
+
const id = edge.id ?? this.genId("e", new Set(this.def.edges.map((e) => e.id)));
|
|
194
202
|
this.def.edges.push({
|
|
195
203
|
id,
|
|
196
204
|
source: { ...edge.source },
|
|
@@ -331,9 +339,6 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
331
339
|
for (const h of Array.from(set))
|
|
332
340
|
h(payload);
|
|
333
341
|
}
|
|
334
|
-
generateId(prefix) {
|
|
335
|
-
return `${prefix}${Math.random().toString(36).slice(2, 8)}`;
|
|
336
|
-
}
|
|
337
342
|
}
|
|
338
343
|
|
|
339
344
|
class CLIWorkbench {
|
|
@@ -465,6 +470,18 @@ class AbstractGraphRunner {
|
|
|
465
470
|
}
|
|
466
471
|
}
|
|
467
472
|
}
|
|
473
|
+
getInputDefaults(def) {
|
|
474
|
+
const out = {};
|
|
475
|
+
for (const n of def.nodes) {
|
|
476
|
+
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
477
|
+
const graphDefaults = n.initialInputs ?? {};
|
|
478
|
+
const merged = { ...dynDefaults, ...graphDefaults };
|
|
479
|
+
if (Object.keys(merged).length > 0) {
|
|
480
|
+
out[n.nodeId] = merged;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
468
485
|
on(event, handler) {
|
|
469
486
|
if (!this.listeners.has(event))
|
|
470
487
|
this.listeners.set(event, new Set());
|
|
@@ -626,16 +643,6 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
626
643
|
}
|
|
627
644
|
return out;
|
|
628
645
|
}
|
|
629
|
-
getInputDefaults(def) {
|
|
630
|
-
const out = {};
|
|
631
|
-
for (const n of def.nodes) {
|
|
632
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
633
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
634
|
-
out[n.nodeId] = dynDefaults;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
return out;
|
|
638
|
-
}
|
|
639
646
|
async snapshotFull() {
|
|
640
647
|
const def = undefined; // UI will supply def/positions on download for local
|
|
641
648
|
const inputs = this.getInputs(this.runtime
|
|
@@ -663,11 +670,25 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
663
670
|
if (payload.def)
|
|
664
671
|
this.build(payload.def);
|
|
665
672
|
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
666
|
-
|
|
673
|
+
this.hydrateSnapshotFull(payload);
|
|
674
|
+
}
|
|
675
|
+
hydrateSnapshotFull(snapshot) {
|
|
676
|
+
// Hydrate via runtime for exact restore (this emits events on runtime emitter)
|
|
667
677
|
this.runtime?.hydrate({
|
|
668
|
-
inputs:
|
|
669
|
-
outputs:
|
|
678
|
+
inputs: snapshot.inputs || {},
|
|
679
|
+
outputs: snapshot.outputs || {},
|
|
670
680
|
});
|
|
681
|
+
// Also emit directly from runner to ensure UI gets events even if engine isn't running
|
|
682
|
+
for (const [nodeId, map] of Object.entries(snapshot.inputs || {})) {
|
|
683
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
684
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const [nodeId, map] of Object.entries(snapshot.outputs || {})) {
|
|
688
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
689
|
+
this.emit("value", { nodeId, handle, value, io: "output" });
|
|
690
|
+
}
|
|
691
|
+
}
|
|
671
692
|
}
|
|
672
693
|
dispose() {
|
|
673
694
|
super.dispose();
|
|
@@ -1224,16 +1245,6 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
1224
1245
|
}
|
|
1225
1246
|
return out;
|
|
1226
1247
|
}
|
|
1227
|
-
getInputDefaults(def) {
|
|
1228
|
-
const out = {};
|
|
1229
|
-
for (const n of def.nodes) {
|
|
1230
|
-
const dynDefaults = n.resolvedHandles?.inputDefaults ?? {};
|
|
1231
|
-
if (Object.keys(dynDefaults).length > 0) {
|
|
1232
|
-
out[n.nodeId] = dynDefaults;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
return out;
|
|
1236
|
-
}
|
|
1237
1248
|
dispose() {
|
|
1238
1249
|
// Idempotent: allow multiple calls safely
|
|
1239
1250
|
if (this.disposed)
|
|
@@ -3214,15 +3225,13 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
3214
3225
|
} })] }));
|
|
3215
3226
|
}
|
|
3216
3227
|
|
|
3217
|
-
function DefaultContextMenu({ open, clientPos,
|
|
3218
|
-
const { registry } = useWorkbenchContext();
|
|
3228
|
+
function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, }) {
|
|
3219
3229
|
const rf = react.useReactFlow();
|
|
3220
|
-
const ids = Array.from(registry.nodes.keys());
|
|
3221
3230
|
const [query, setQuery] = React.useState("");
|
|
3222
3231
|
const q = query.trim().toLowerCase();
|
|
3223
3232
|
const filteredIds = q
|
|
3224
|
-
?
|
|
3225
|
-
:
|
|
3233
|
+
? nodeIds.filter((id) => id.toLowerCase().includes(q))
|
|
3234
|
+
: nodeIds;
|
|
3226
3235
|
const root = { __children: {} };
|
|
3227
3236
|
for (const id of filteredIds) {
|
|
3228
3237
|
const parts = id.split(".");
|
|
@@ -3246,11 +3255,11 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3246
3255
|
if (!ref.current)
|
|
3247
3256
|
return;
|
|
3248
3257
|
if (!ref.current.contains(e.target))
|
|
3249
|
-
onClose();
|
|
3258
|
+
handlers.onClose();
|
|
3250
3259
|
};
|
|
3251
3260
|
const onKey = (e) => {
|
|
3252
3261
|
if (e.key === "Escape")
|
|
3253
|
-
onClose();
|
|
3262
|
+
handlers.onClose();
|
|
3254
3263
|
};
|
|
3255
3264
|
window.addEventListener("mousedown", onDown, true);
|
|
3256
3265
|
window.addEventListener("keydown", onKey);
|
|
@@ -3258,7 +3267,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3258
3267
|
window.removeEventListener("mousedown", onDown, true);
|
|
3259
3268
|
window.removeEventListener("keydown", onKey);
|
|
3260
3269
|
};
|
|
3261
|
-
}, [open,
|
|
3270
|
+
}, [open, handlers]);
|
|
3262
3271
|
// Focus search input when menu opens
|
|
3263
3272
|
const inputRef = React.useRef(null);
|
|
3264
3273
|
React.useEffect(() => {
|
|
@@ -3277,8 +3286,8 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3277
3286
|
const handleClick = (typeId) => {
|
|
3278
3287
|
// project() is deprecated; use screenToFlowPosition for screen coordinates
|
|
3279
3288
|
const p = rf.screenToFlowPosition({ x: clientPos.x, y: clientPos.y });
|
|
3280
|
-
|
|
3281
|
-
onClose();
|
|
3289
|
+
handlers.onAddNode(typeId, { position: p });
|
|
3290
|
+
handlers.onClose();
|
|
3282
3291
|
};
|
|
3283
3292
|
const renderTree = (tree, path = []) => {
|
|
3284
3293
|
const entries = Object.entries(tree?.__children ?? {}).sort((a, b) => a[0].localeCompare(b[0]));
|
|
@@ -3300,8 +3309,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
3300
3309
|
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxRuntime.jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsxRuntime.jsx("div", { className: "px-2 pb-1", children: jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 px-2 py-1 text-sm outline-none focus:border-gray-400", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsxRuntime.jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsxRuntime.jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
3301
3310
|
}
|
|
3302
3311
|
|
|
3303
|
-
function NodeContextMenu({ open, clientPos, nodeId,
|
|
3304
|
-
const { wb, runner, engineKind, registry, outputsMap, outputTypesMap } = useWorkbenchContext();
|
|
3312
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeableOutputs, }) {
|
|
3305
3313
|
const ref = React.useRef(null);
|
|
3306
3314
|
// outside click + ESC
|
|
3307
3315
|
React.useEffect(() => {
|
|
@@ -3311,11 +3319,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3311
3319
|
if (!ref.current)
|
|
3312
3320
|
return;
|
|
3313
3321
|
if (!ref.current.contains(e.target))
|
|
3314
|
-
onClose();
|
|
3322
|
+
handlers.onClose();
|
|
3315
3323
|
};
|
|
3316
3324
|
const onKey = (e) => {
|
|
3317
3325
|
if (e.key === "Escape")
|
|
3318
|
-
onClose();
|
|
3326
|
+
handlers.onClose();
|
|
3319
3327
|
};
|
|
3320
3328
|
window.addEventListener("mousedown", onDown, true);
|
|
3321
3329
|
window.addEventListener("keydown", onKey);
|
|
@@ -3323,48 +3331,26 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3323
3331
|
window.removeEventListener("mousedown", onDown, true);
|
|
3324
3332
|
window.removeEventListener("keydown", onKey);
|
|
3325
3333
|
};
|
|
3326
|
-
}, [open,
|
|
3334
|
+
}, [open, handlers]);
|
|
3327
3335
|
React.useEffect(() => {
|
|
3328
3336
|
if (open)
|
|
3329
3337
|
ref.current?.focus();
|
|
3330
3338
|
}, [open]);
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
const base = tId.slice(0, -2);
|
|
3347
|
-
const tArr = registry.types.get(tId);
|
|
3348
|
-
const tElem = registry.types.get(base);
|
|
3349
|
-
const arrT = tArr?.bakeTarget;
|
|
3350
|
-
const elemT = tElem?.bakeTarget;
|
|
3351
|
-
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3352
|
-
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3353
|
-
out.push(h);
|
|
3354
|
-
}
|
|
3355
|
-
else {
|
|
3356
|
-
const t = registry.types.get(tId);
|
|
3357
|
-
const bt = t?.bakeTarget;
|
|
3358
|
-
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3359
|
-
out.push(h);
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
return out;
|
|
3363
|
-
}
|
|
3364
|
-
catch {
|
|
3365
|
-
return [];
|
|
3366
|
-
}
|
|
3367
|
-
};
|
|
3339
|
+
if (!open || !clientPos || !nodeId)
|
|
3340
|
+
return null;
|
|
3341
|
+
// clamp
|
|
3342
|
+
const MENU_MIN_WIDTH = 180;
|
|
3343
|
+
const PADDING = 16;
|
|
3344
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3345
|
+
(MENU_MIN_WIDTH + PADDING));
|
|
3346
|
+
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3347
|
+
return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3348
|
+
e.preventDefault();
|
|
3349
|
+
e.stopPropagation();
|
|
3350
|
+
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDelete, children: "Delete" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicate, children: "Duplicate" }), canRunPull && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h))), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" })] })), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" })] }));
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose) {
|
|
3368
3354
|
const doBake = async (handleId) => {
|
|
3369
3355
|
try {
|
|
3370
3356
|
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
@@ -3397,7 +3383,6 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3397
3383
|
const newId = wb.addNode({
|
|
3398
3384
|
typeId: singleTarget.nodeTypeId,
|
|
3399
3385
|
position: { x: pos.x + 180, y: pos.y },
|
|
3400
|
-
params: {},
|
|
3401
3386
|
});
|
|
3402
3387
|
runner.update(wb.export());
|
|
3403
3388
|
await runner.whenIdle();
|
|
@@ -3408,12 +3393,9 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3408
3393
|
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
3409
3394
|
const inType = sparkGraph.getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
3410
3395
|
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3411
|
-
const newId =
|
|
3412
|
-
wb.addNode({
|
|
3413
|
-
nodeId: newId,
|
|
3396
|
+
const newId = wb.addNode({
|
|
3414
3397
|
typeId: arrTarget.nodeTypeId,
|
|
3415
3398
|
position: { x: pos.x + 180, y: pos.y },
|
|
3416
|
-
params: {},
|
|
3417
3399
|
});
|
|
3418
3400
|
runner.update(wb.export());
|
|
3419
3401
|
await runner.whenIdle();
|
|
@@ -3431,14 +3413,11 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3431
3413
|
const DY = 160;
|
|
3432
3414
|
const nodeIds = [];
|
|
3433
3415
|
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
3434
|
-
const cv = coercedItems[idx];
|
|
3435
3416
|
const col = idx % COLS;
|
|
3436
3417
|
const row = Math.floor(idx / COLS);
|
|
3437
3418
|
const newId = wb.addNode({
|
|
3438
3419
|
typeId: elemTarget.nodeTypeId,
|
|
3439
3420
|
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
3440
|
-
params: {},
|
|
3441
|
-
initialInputs: { [elemTarget.inputHandle]: structuredClone(cv) },
|
|
3442
3421
|
});
|
|
3443
3422
|
nodeIds.push(newId);
|
|
3444
3423
|
}
|
|
@@ -3456,63 +3435,88 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
3456
3435
|
}
|
|
3457
3436
|
catch { }
|
|
3458
3437
|
};
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3438
|
+
return {
|
|
3439
|
+
onDelete: () => {
|
|
3440
|
+
wb.removeNode(nodeId);
|
|
3441
|
+
onClose();
|
|
3442
|
+
},
|
|
3443
|
+
onDuplicate: async () => {
|
|
3444
|
+
const def = wb.export();
|
|
3445
|
+
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3446
|
+
if (!n)
|
|
3447
|
+
return onClose();
|
|
3448
|
+
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
3449
|
+
const newId = wb.addNode({
|
|
3450
|
+
typeId: n.typeId,
|
|
3451
|
+
params: n.params,
|
|
3452
|
+
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
3453
|
+
initialInputs: n.initialInputs,
|
|
3454
|
+
resolvedHandles: n.resolvedHandles,
|
|
3455
|
+
});
|
|
3456
|
+
await runner.whenIdle();
|
|
3457
|
+
runner.setInputs(newId, { ...runner.getInputs(def)[nodeId] });
|
|
3458
|
+
onClose();
|
|
3459
|
+
},
|
|
3460
|
+
onRunPull: async () => {
|
|
3461
|
+
try {
|
|
3462
|
+
await runner.computeNode(nodeId);
|
|
3463
|
+
}
|
|
3464
|
+
catch { }
|
|
3465
|
+
onClose();
|
|
3466
|
+
},
|
|
3467
|
+
onBake: async (handleId) => {
|
|
3468
|
+
await doBake(handleId);
|
|
3469
|
+
onClose();
|
|
3470
|
+
},
|
|
3471
|
+
onCopyId: async () => {
|
|
3472
|
+
try {
|
|
3473
|
+
await navigator.clipboard.writeText(nodeId);
|
|
3474
|
+
}
|
|
3475
|
+
catch { }
|
|
3476
|
+
onClose();
|
|
3477
|
+
},
|
|
3478
|
+
onClose,
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
function getBakeableOutputs(nodeId, wb, registry, outputTypesMap) {
|
|
3482
|
+
try {
|
|
3465
3483
|
const def = wb.export();
|
|
3466
|
-
const
|
|
3467
|
-
if (!
|
|
3468
|
-
return
|
|
3469
|
-
const
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3484
|
+
const node = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3485
|
+
if (!node)
|
|
3486
|
+
return [];
|
|
3487
|
+
const desc = registry.nodes.get(node.typeId);
|
|
3488
|
+
const handles = Object.keys(desc?.outputs || {});
|
|
3489
|
+
const out = [];
|
|
3490
|
+
for (const h of handles) {
|
|
3491
|
+
const tId = outputTypesMap?.[nodeId]?.[h];
|
|
3492
|
+
if (!tId)
|
|
3493
|
+
continue;
|
|
3494
|
+
if (tId.endsWith("[]")) {
|
|
3495
|
+
const base = tId.slice(0, -2);
|
|
3496
|
+
const tArr = registry.types.get(tId);
|
|
3497
|
+
const tElem = registry.types.get(base);
|
|
3498
|
+
const arrT = tArr?.bakeTarget;
|
|
3499
|
+
const elemT = tElem?.bakeTarget;
|
|
3500
|
+
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3501
|
+
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3502
|
+
out.push(h);
|
|
3503
|
+
}
|
|
3504
|
+
else {
|
|
3505
|
+
const t = registry.types.get(tId);
|
|
3506
|
+
const bt = t?.bakeTarget;
|
|
3507
|
+
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3508
|
+
out.push(h);
|
|
3509
|
+
}
|
|
3491
3510
|
}
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
// clamp
|
|
3498
|
-
const MENU_MIN_WIDTH = 180;
|
|
3499
|
-
const PADDING = 16;
|
|
3500
|
-
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3501
|
-
(MENU_MIN_WIDTH + PADDING));
|
|
3502
|
-
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3503
|
-
const canRunPull = engineKind()?.toString() === "pull";
|
|
3504
|
-
const outs = getBakeableOutputs();
|
|
3505
|
-
return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3506
|
-
e.preventDefault();
|
|
3507
|
-
e.stopPropagation();
|
|
3508
|
-
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDelete, children: "Delete" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDuplicate, children: "Duplicate" }), canRunPull && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleRunPull, children: "Run (pull)" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), outs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), outs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: async () => {
|
|
3509
|
-
await doBake(h);
|
|
3510
|
-
onClose();
|
|
3511
|
-
}, children: ["Bake: ", h] }, h))), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" })] })), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleCopyId, children: "Copy Node ID" })] }));
|
|
3511
|
+
return out;
|
|
3512
|
+
}
|
|
3513
|
+
catch {
|
|
3514
|
+
return [];
|
|
3515
|
+
}
|
|
3512
3516
|
}
|
|
3513
3517
|
|
|
3514
3518
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
3515
|
-
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, } = useWorkbenchContext();
|
|
3519
|
+
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, runner, engineKind, } = useWorkbenchContext();
|
|
3516
3520
|
const nodeValidation = validationByNode;
|
|
3517
3521
|
const edgeValidation = validationByEdge.errors;
|
|
3518
3522
|
// Keep stable references for nodes/edges to avoid unnecessary updates
|
|
@@ -3579,9 +3583,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3579
3583
|
},
|
|
3580
3584
|
}));
|
|
3581
3585
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
3586
|
+
const ui = wb.getUI();
|
|
3582
3587
|
const { nodeTypes, resolveNodeType } = React.useMemo(() => {
|
|
3583
3588
|
// Build nodeTypes map using UI extension registry
|
|
3584
|
-
const ui = wb.getUI();
|
|
3585
3589
|
const custom = new Map(); // Include all types present in registry AND current graph to avoid timing issues
|
|
3586
3590
|
const def = wb.export();
|
|
3587
3591
|
const ids = new Set([
|
|
@@ -3603,7 +3607,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3603
3607
|
const resolver = (nodeTypeId) => custom.has(nodeTypeId) ? `spark-${nodeTypeId}` : "spark-default";
|
|
3604
3608
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
3605
3609
|
// Include uiVersion to recompute when custom renderers are registered
|
|
3606
|
-
}, [wb, registry, uiVersion]);
|
|
3610
|
+
}, [wb, registry, uiVersion, ui]);
|
|
3607
3611
|
const { nodes, edges } = React.useMemo(() => {
|
|
3608
3612
|
const def = wb.export();
|
|
3609
3613
|
const sel = wb.getSelection();
|
|
@@ -3767,15 +3771,59 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3767
3771
|
setNodeMenuOpen(false);
|
|
3768
3772
|
}
|
|
3769
3773
|
};
|
|
3770
|
-
const addNodeAt = React.useCallback((typeId,
|
|
3771
|
-
wb.addNode({
|
|
3772
|
-
|
|
3774
|
+
const addNodeAt = React.useCallback(async (typeId, opts) => {
|
|
3775
|
+
const nodeId = wb.addNode({
|
|
3776
|
+
typeId,
|
|
3777
|
+
initialInputs: opts.initialInputs,
|
|
3778
|
+
position: opts.position,
|
|
3779
|
+
});
|
|
3780
|
+
if (opts.inputs) {
|
|
3781
|
+
runner.update(wb.export());
|
|
3782
|
+
await runner.whenIdle();
|
|
3783
|
+
runner.setInputs(nodeId, opts.inputs);
|
|
3784
|
+
}
|
|
3785
|
+
}, [wb, runner]);
|
|
3773
3786
|
const onCloseMenu = React.useCallback(() => {
|
|
3774
3787
|
setMenuOpen(false);
|
|
3775
3788
|
}, []);
|
|
3776
3789
|
const onCloseNodeMenu = React.useCallback(() => {
|
|
3777
3790
|
setNodeMenuOpen(false);
|
|
3778
3791
|
}, []);
|
|
3792
|
+
const nodeIds = React.useMemo(() => Array.from(registry.nodes.keys()), [registry]);
|
|
3793
|
+
const defaultContextMenuHandlers = React.useMemo(() => ({
|
|
3794
|
+
onAddNode: addNodeAt,
|
|
3795
|
+
onClose: onCloseMenu,
|
|
3796
|
+
}), [addNodeAt, onCloseMenu]);
|
|
3797
|
+
const nodeContextMenuHandlers = React.useMemo(() => {
|
|
3798
|
+
if (!nodeAtMenu)
|
|
3799
|
+
return null;
|
|
3800
|
+
return createNodeContextMenuHandlers(nodeAtMenu, wb, runner, registry, outputsMap, outputTypesMap, onCloseNodeMenu);
|
|
3801
|
+
}, [
|
|
3802
|
+
nodeAtMenu,
|
|
3803
|
+
wb,
|
|
3804
|
+
runner,
|
|
3805
|
+
registry,
|
|
3806
|
+
outputsMap,
|
|
3807
|
+
outputTypesMap,
|
|
3808
|
+
onCloseNodeMenu,
|
|
3809
|
+
]);
|
|
3810
|
+
const canRunPull = React.useMemo(() => engineKind()?.toString() === "pull", [engineKind]);
|
|
3811
|
+
const bakeableOutputs = React.useMemo(() => {
|
|
3812
|
+
if (!nodeAtMenu)
|
|
3813
|
+
return [];
|
|
3814
|
+
return getBakeableOutputs(nodeAtMenu, wb, registry, outputTypesMap);
|
|
3815
|
+
}, [nodeAtMenu, wb, registry, outputTypesMap]);
|
|
3816
|
+
// Get custom renderers from UI extension registry (reactive to uiVersion changes)
|
|
3817
|
+
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = React.useMemo(() => {
|
|
3818
|
+
return {
|
|
3819
|
+
BackgroundRenderer: ui.getBackgroundRenderer(),
|
|
3820
|
+
MinimapRenderer: ui.getMinimapRenderer(),
|
|
3821
|
+
ControlsRenderer: ui.getControlsRenderer(),
|
|
3822
|
+
DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
|
|
3823
|
+
NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
|
|
3824
|
+
connectionLineRenderer: ui.getConnectionLineRenderer(),
|
|
3825
|
+
};
|
|
3826
|
+
}, [ui, uiVersion]);
|
|
3779
3827
|
const onMoveEnd = React.useCallback(() => {
|
|
3780
3828
|
if (rfInstanceRef.current) {
|
|
3781
3829
|
const viewport = rfInstanceRef.current.getViewport();
|
|
@@ -3800,7 +3848,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3800
3848
|
});
|
|
3801
3849
|
}
|
|
3802
3850
|
});
|
|
3803
|
-
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsx(react.ReactFlowProvider, { children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, selectionOnDrag: true, onInit: (inst) => {
|
|
3851
|
+
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsx(react.ReactFlowProvider, { children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
|
|
3804
3852
|
rfInstanceRef.current = inst;
|
|
3805
3853
|
const savedViewport = wb.getViewport();
|
|
3806
3854
|
if (savedViewport) {
|
|
@@ -3811,7 +3859,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
3811
3859
|
zoom: savedViewport.zoom,
|
|
3812
3860
|
});
|
|
3813
3861
|
}
|
|
3814
|
-
}, 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: [jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 }), jsxRuntime.jsx(react.MiniMap, {}), jsxRuntime.jsx(react.Controls, {}), jsxRuntime.jsx(
|
|
3862
|
+
}, 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 ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds })), !!nodeAtMenu &&
|
|
3863
|
+
nodeContextMenuHandlers &&
|
|
3864
|
+
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })))] }) }) }));
|
|
3815
3865
|
});
|
|
3816
3866
|
|
|
3817
3867
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|