@bian-womp/spark-workbench 0.2.83 → 0.2.85
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 +344 -238
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +27 -28
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +4 -1
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/core/ui-extensions.d.ts +16 -7
- package/lib/cjs/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +2 -0
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts +0 -14
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts +4 -0
- package/lib/cjs/src/misc/DefaultNodeContent.d.ts.map +1 -0
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts +15 -0
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts +2 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +9 -2
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/misc/merge-utils.d.ts +6 -1
- package/lib/cjs/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/cjs/src/misc/types.d.ts +4 -0
- package/lib/cjs/src/misc/types.d.ts.map +1 -1
- package/lib/esm/index.js +346 -240
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +27 -28
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +4 -1
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/core/ui-extensions.d.ts +16 -7
- package/lib/esm/src/core/ui-extensions.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +2 -0
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts +0 -14
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNodeContent.d.ts +4 -0
- package/lib/esm/src/misc/DefaultNodeContent.d.ts.map +1 -0
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts +15 -0
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts +2 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/hooks.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +9 -2
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/misc/merge-utils.d.ts +6 -1
- package/lib/esm/src/misc/merge-utils.d.ts.map +1 -1
- package/lib/esm/src/misc/types.d.ts +4 -0
- package/lib/esm/src/misc/types.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { generateId, GraphBuilder, getTypedOutputValue, isTypedOutput, getInputTypeId, createEngine, StepEngine, PullEngine, BatchedEngine, getTypedOutputTypeId, isInputPrivate, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
1
|
+
import { generateId, GraphBuilder, getTypedOutputValue, isTypedOutput, getInputTypeId, createEngine, StepEngine, PullEngine, BatchedEngine, getTypedOutputTypeId, isInputPrivate, offsetImportedPositions, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry } from '@bian-womp/spark-graph';
|
|
2
2
|
import lod from 'lodash';
|
|
3
3
|
import { RuntimeApiClient } from '@bian-womp/spark-remote';
|
|
4
|
-
import { Position, Handle,
|
|
4
|
+
import { Position, Handle, NodeResizer, useReactFlow, ReactFlowProvider, ReactFlow, Background, BackgroundVariant, MiniMap, Controls } from '@xyflow/react';
|
|
5
5
|
import React, { useCallback, useState, useRef, useEffect, useMemo, createContext, useContext, useImperativeHandle } from 'react';
|
|
6
6
|
import cx from 'classnames';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -73,6 +73,13 @@ class DefaultUIExtensionRegistry {
|
|
|
73
73
|
getNodeContextMenuRenderer() {
|
|
74
74
|
return this.nodeContextMenuRenderer;
|
|
75
75
|
}
|
|
76
|
+
registerSelectionContextMenuRenderer(renderer) {
|
|
77
|
+
this.selectionContextMenuRenderer = renderer;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
getSelectionContextMenuRenderer() {
|
|
81
|
+
return this.selectionContextMenuRenderer;
|
|
82
|
+
}
|
|
76
83
|
// Layout function overrides
|
|
77
84
|
registerEstimateNodeSize(override) {
|
|
78
85
|
this.estimateNodeSizeOverride = override;
|
|
@@ -124,6 +131,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
124
131
|
this._def = { nodes: [], edges: [] };
|
|
125
132
|
this.listeners = new Map();
|
|
126
133
|
this.positions = {};
|
|
134
|
+
this.sizes = {};
|
|
127
135
|
this.selection = {
|
|
128
136
|
nodes: [],
|
|
129
137
|
edges: [],
|
|
@@ -149,9 +157,11 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
149
157
|
const defNodeIds = new Set(this._def.nodes.map((n) => n.nodeId));
|
|
150
158
|
const defEdgeIds = new Set(this._def.edges.map((e) => e.id));
|
|
151
159
|
const filteredPositions = Object.fromEntries(Object.entries(this.positions).filter(([id]) => defNodeIds.has(id)));
|
|
160
|
+
const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
152
161
|
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
153
162
|
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
154
163
|
this.positions = filteredPositions;
|
|
164
|
+
this.sizes = filteredSizes;
|
|
155
165
|
this.selection = { nodes: filteredNodes, edges: filteredEdges };
|
|
156
166
|
this.emit("graphChanged", { def: this._def });
|
|
157
167
|
this.refreshValidation();
|
|
@@ -208,8 +218,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
208
218
|
params: node.params,
|
|
209
219
|
resolvedHandles: node.resolvedHandles,
|
|
210
220
|
});
|
|
211
|
-
if (
|
|
212
|
-
this.positions[id] =
|
|
221
|
+
if (options?.position)
|
|
222
|
+
this.positions[id] = options.position;
|
|
223
|
+
if (options?.size)
|
|
224
|
+
this.sizes[id] = options.size;
|
|
213
225
|
this.emit("graphChanged", {
|
|
214
226
|
def: this._def,
|
|
215
227
|
change: {
|
|
@@ -227,6 +239,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
227
239
|
this._def.nodes = this._def.nodes.filter((n) => n.nodeId !== nodeId);
|
|
228
240
|
this._def.edges = this._def.edges.filter((e) => e.source.nodeId !== nodeId && e.target.nodeId !== nodeId);
|
|
229
241
|
delete this.positions[nodeId];
|
|
242
|
+
delete this.sizes[nodeId];
|
|
230
243
|
delete this.nodeNames[nodeId];
|
|
231
244
|
this.emit("graphChanged", {
|
|
232
245
|
def: this._def,
|
|
@@ -285,27 +298,38 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
285
298
|
});
|
|
286
299
|
}
|
|
287
300
|
// Position and selection APIs for React Flow bridge
|
|
288
|
-
setPositions(
|
|
289
|
-
this.positions = { ...this.positions, ...
|
|
301
|
+
setPositions(positions, options) {
|
|
302
|
+
this.positions = { ...this.positions, ...positions };
|
|
303
|
+
this.emit("graphUiChanged", { change: { type: "moveNodes" }, ...options });
|
|
304
|
+
}
|
|
305
|
+
getPositions() {
|
|
306
|
+
return { ...this.positions };
|
|
307
|
+
}
|
|
308
|
+
setSizes(sizes, options) {
|
|
309
|
+
const updatedSizes = { ...this.sizes };
|
|
310
|
+
for (const [nodeId, size] of Object.entries(sizes)) {
|
|
311
|
+
if (size) {
|
|
312
|
+
updatedSizes[nodeId] = size;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
delete updatedSizes[nodeId];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
this.sizes = updatedSizes;
|
|
290
319
|
this.emit("graphUiChanged", {
|
|
291
|
-
|
|
292
|
-
change: { type: "moveNodes" },
|
|
320
|
+
change: { type: "resizeNodes" },
|
|
293
321
|
...options,
|
|
294
322
|
});
|
|
295
323
|
}
|
|
296
|
-
|
|
297
|
-
return { ...this.
|
|
324
|
+
getSizes() {
|
|
325
|
+
return { ...this.sizes };
|
|
298
326
|
}
|
|
299
327
|
setSelectionInternal(sel, options) {
|
|
300
328
|
if (lod.isEqual(this.selection, sel))
|
|
301
329
|
return;
|
|
302
330
|
this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
|
|
303
331
|
this.emit("selectionChanged", this.selection);
|
|
304
|
-
this.emit("graphUiChanged", {
|
|
305
|
-
def: this._def,
|
|
306
|
-
change: { type: "selection" },
|
|
307
|
-
...options,
|
|
308
|
-
});
|
|
332
|
+
this.emit("graphUiChanged", { change: { type: "selection" }, ...options });
|
|
309
333
|
}
|
|
310
334
|
setSelection(sel, options) {
|
|
311
335
|
this.setSelectionInternal(sel, options);
|
|
@@ -336,10 +360,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
336
360
|
if (lod.isEqual(this.viewport, viewport))
|
|
337
361
|
return;
|
|
338
362
|
this.viewport = { ...viewport };
|
|
339
|
-
this.emit("graphUiChanged", {
|
|
340
|
-
def: this._def,
|
|
341
|
-
change: { type: "viewport" },
|
|
342
|
-
});
|
|
363
|
+
this.emit("graphUiChanged", { change: { type: "viewport" } });
|
|
343
364
|
}
|
|
344
365
|
getViewport() {
|
|
345
366
|
return this.viewport ? { ...this.viewport } : null;
|
|
@@ -351,6 +372,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
351
372
|
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
352
373
|
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
353
374
|
const filteredNodeNames = Object.fromEntries(Object.entries(this.nodeNames).filter(([id]) => defNodeIds.has(id)));
|
|
375
|
+
const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
354
376
|
return {
|
|
355
377
|
positions: Object.keys(filteredPositions).length > 0
|
|
356
378
|
? filteredPositions
|
|
@@ -365,6 +387,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
365
387
|
nodeNames: Object.keys(filteredNodeNames).length > 0
|
|
366
388
|
? filteredNodeNames
|
|
367
389
|
: undefined,
|
|
390
|
+
sizes: Object.keys(filteredSizes).length > 0 ? filteredSizes : undefined,
|
|
368
391
|
};
|
|
369
392
|
}
|
|
370
393
|
setUIState(ui) {
|
|
@@ -379,12 +402,27 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
379
402
|
edges: [...ui.selection.edges],
|
|
380
403
|
};
|
|
381
404
|
this.emit("selectionChanged", this.selection);
|
|
405
|
+
this.emit("graphUiChanged", {
|
|
406
|
+
change: { type: "selection" },
|
|
407
|
+
init: true,
|
|
408
|
+
});
|
|
382
409
|
}
|
|
383
410
|
if (ui.viewport) {
|
|
384
411
|
this.viewport = { ...ui.viewport };
|
|
412
|
+
this.emit("graphUiChanged", { change: { type: "viewport" }, init: true });
|
|
385
413
|
}
|
|
386
414
|
if (ui.nodeNames !== undefined) {
|
|
387
415
|
this.nodeNames = { ...ui.nodeNames };
|
|
416
|
+
for (const [nodeId, name] of Object.entries(ui.nodeNames)) {
|
|
417
|
+
this.emit("graphUiChanged", {
|
|
418
|
+
change: { type: "nodeName", nodeId, name },
|
|
419
|
+
init: true,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (ui.sizes !== undefined) {
|
|
424
|
+
const defNodeIds = new Set(this._def.nodes.map((n) => n.nodeId));
|
|
425
|
+
this.sizes = Object.fromEntries(Object.entries(ui.sizes).filter(([id]) => defNodeIds.has(id)));
|
|
388
426
|
}
|
|
389
427
|
}
|
|
390
428
|
getRuntimeState() {
|
|
@@ -435,6 +473,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
435
473
|
if (selection.nodes.length === 0)
|
|
436
474
|
return null;
|
|
437
475
|
const positions = this.getPositions();
|
|
476
|
+
const sizes = this.getSizes();
|
|
438
477
|
const selectedNodeSet = new Set(selection.nodes);
|
|
439
478
|
// Collect nodes to copy
|
|
440
479
|
const nodesToCopy = this.def.nodes.filter((n) => selectedNodeSet.has(n.nodeId));
|
|
@@ -446,15 +485,16 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
446
485
|
let maxX = -Infinity;
|
|
447
486
|
let maxY = -Infinity;
|
|
448
487
|
nodesToCopy.forEach((node) => {
|
|
449
|
-
const pos = positions[node.nodeId]
|
|
450
|
-
const size = getNodeSize?.(node.nodeId, node.typeId)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
488
|
+
const pos = positions[node.nodeId];
|
|
489
|
+
const size = sizes[node.nodeId] || getNodeSize?.(node.nodeId, node.typeId);
|
|
490
|
+
if (pos) {
|
|
491
|
+
minX = Math.min(minX, pos.x);
|
|
492
|
+
minY = Math.min(minY, pos.y);
|
|
493
|
+
if (size) {
|
|
494
|
+
maxX = Math.max(maxX, pos.x + size.width);
|
|
495
|
+
maxY = Math.max(maxY, pos.y + size.height);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
458
498
|
});
|
|
459
499
|
const bounds = { minX, minY, maxX, maxY };
|
|
460
500
|
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
@@ -464,7 +504,8 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
464
504
|
const allInputs = runner.getInputs(this.def);
|
|
465
505
|
const selectedEdgeSet = new Set(selection.edges);
|
|
466
506
|
const copiedNodes = nodesToCopy.map((node) => {
|
|
467
|
-
const pos = positions[node.nodeId]
|
|
507
|
+
const pos = positions[node.nodeId];
|
|
508
|
+
const size = sizes[node.nodeId];
|
|
468
509
|
// Get all inbound edges for this node
|
|
469
510
|
const inboundEdges = this.def.edges.filter((e) => e.target.nodeId === node.nodeId);
|
|
470
511
|
// Build set of handles that have inbound edges
|
|
@@ -481,10 +522,13 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
481
522
|
typeId: node.typeId,
|
|
482
523
|
params: node.params,
|
|
483
524
|
resolvedHandles: node.resolvedHandles,
|
|
484
|
-
position:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
525
|
+
position: pos
|
|
526
|
+
? {
|
|
527
|
+
x: pos.x - centerX,
|
|
528
|
+
y: pos.y - centerY,
|
|
529
|
+
}
|
|
530
|
+
: undefined,
|
|
531
|
+
size,
|
|
488
532
|
inputs: inputsToCopy,
|
|
489
533
|
originalNodeId: node.nodeId,
|
|
490
534
|
};
|
|
@@ -519,6 +563,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
519
563
|
if (selection.nodes.length === 0)
|
|
520
564
|
return [];
|
|
521
565
|
const positions = this.getPositions();
|
|
566
|
+
const sizes = this.getSizes();
|
|
522
567
|
const newNodes = [];
|
|
523
568
|
// Get inputs without bindings (literal values only)
|
|
524
569
|
const allInputs = runner.getInputs(this.def) || {};
|
|
@@ -527,7 +572,8 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
527
572
|
const n = this.def.nodes.find((n) => n.nodeId === nodeId);
|
|
528
573
|
if (!n)
|
|
529
574
|
continue;
|
|
530
|
-
const pos = positions[nodeId]
|
|
575
|
+
const pos = positions[nodeId];
|
|
576
|
+
const size = sizes[nodeId];
|
|
531
577
|
const inboundHandles = new Set(this.def.edges
|
|
532
578
|
.filter((e) => e.target.nodeId === nodeId)
|
|
533
579
|
.map((e) => e.target.handle));
|
|
@@ -535,10 +581,11 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
535
581
|
const newNodeId = this.addNode({
|
|
536
582
|
typeId: n.typeId,
|
|
537
583
|
params: n.params,
|
|
538
|
-
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
539
584
|
resolvedHandles: n.resolvedHandles,
|
|
540
585
|
}, {
|
|
541
586
|
inputs: inputsWithoutBindings,
|
|
587
|
+
position: pos ? { x: pos.x + 24, y: pos.y + 24 } : undefined,
|
|
588
|
+
size,
|
|
542
589
|
copyOutputsFrom: nodeId,
|
|
543
590
|
dry: true,
|
|
544
591
|
});
|
|
@@ -585,8 +632,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
585
632
|
const coerced = await coerceIfNeeded(outputTypeId, inType, unwrap(outputValue));
|
|
586
633
|
newNodeId = this.addNode({
|
|
587
634
|
typeId: singleTarget.nodeTypeId,
|
|
635
|
+
}, {
|
|
636
|
+
inputs: { [singleTarget.inputHandle]: coerced },
|
|
588
637
|
position: { x: pos.x + 180, y: pos.y },
|
|
589
|
-
}
|
|
638
|
+
});
|
|
590
639
|
}
|
|
591
640
|
else if (isArray && arrTarget) {
|
|
592
641
|
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
@@ -594,8 +643,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
594
643
|
const coerced = await coerceIfNeeded(outputTypeId, inType, unwrap(outputValue));
|
|
595
644
|
newNodeId = this.addNode({
|
|
596
645
|
typeId: arrTarget.nodeTypeId,
|
|
646
|
+
}, {
|
|
597
647
|
position: { x: pos.x + 180, y: pos.y },
|
|
598
|
-
|
|
648
|
+
inputs: { [arrTarget.inputHandle]: coerced },
|
|
649
|
+
});
|
|
599
650
|
}
|
|
600
651
|
else if (isArray && elemTarget) {
|
|
601
652
|
const nodeDesc = registry.nodes.get(elemTarget.nodeTypeId);
|
|
@@ -611,8 +662,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
611
662
|
const row = Math.floor(idx / COLS);
|
|
612
663
|
newNodeId = this.addNode({
|
|
613
664
|
typeId: elemTarget.nodeTypeId,
|
|
665
|
+
}, {
|
|
614
666
|
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
615
|
-
|
|
667
|
+
inputs: { [elemTarget.inputHandle]: coercedItems[idx] },
|
|
668
|
+
});
|
|
616
669
|
}
|
|
617
670
|
}
|
|
618
671
|
if (newNodeId) {
|
|
@@ -634,7 +687,8 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
634
687
|
const n = this.def.nodes.find((n) => n.nodeId === nodeId);
|
|
635
688
|
if (!n)
|
|
636
689
|
return undefined;
|
|
637
|
-
const pos = this.getPositions()[nodeId]
|
|
690
|
+
const pos = this.getPositions()[nodeId];
|
|
691
|
+
const size = this.getSizes()[nodeId];
|
|
638
692
|
// Get inputs without bindings (literal values only)
|
|
639
693
|
const allInputs = runner.getInputs(this.def)[nodeId] || {};
|
|
640
694
|
const inboundHandles = new Set(this.def.edges
|
|
@@ -644,10 +698,11 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
644
698
|
const newNodeId = this.addNode({
|
|
645
699
|
typeId: n.typeId,
|
|
646
700
|
params: n.params,
|
|
647
|
-
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
648
701
|
resolvedHandles: n.resolvedHandles,
|
|
649
702
|
}, {
|
|
650
703
|
inputs: inputsWithoutBindings,
|
|
704
|
+
position: pos ? { x: pos.x + 24, y: pos.y + 24 } : undefined,
|
|
705
|
+
size,
|
|
651
706
|
copyOutputsFrom: nodeId,
|
|
652
707
|
dry: true,
|
|
653
708
|
});
|
|
@@ -665,17 +720,19 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
665
720
|
const n = this.def.nodes.find((n) => n.nodeId === nodeId);
|
|
666
721
|
if (!n)
|
|
667
722
|
return undefined;
|
|
668
|
-
const pos = this.getPositions()[nodeId]
|
|
723
|
+
const pos = this.getPositions()[nodeId];
|
|
724
|
+
const size = this.getSizes()[nodeId];
|
|
669
725
|
// Get all inputs (including those with bindings, since edges will be duplicated)
|
|
670
726
|
const inputs = runner.getInputs(this.def)[nodeId] || {};
|
|
671
727
|
// Add the duplicated node
|
|
672
728
|
const newNodeId = this.addNode({
|
|
673
729
|
typeId: n.typeId,
|
|
674
730
|
params: n.params,
|
|
675
|
-
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
676
731
|
resolvedHandles: n.resolvedHandles,
|
|
677
732
|
}, {
|
|
678
733
|
inputs,
|
|
734
|
+
position: pos ? { x: pos.x + 24, y: pos.y + 24 } : undefined,
|
|
735
|
+
size,
|
|
679
736
|
copyOutputsFrom: nodeId,
|
|
680
737
|
dry: true,
|
|
681
738
|
});
|
|
@@ -709,12 +766,15 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
709
766
|
typeId: nodeData.typeId,
|
|
710
767
|
params: nodeData.params,
|
|
711
768
|
resolvedHandles: nodeData.resolvedHandles,
|
|
712
|
-
position: {
|
|
713
|
-
x: nodeData.position.x + center.x,
|
|
714
|
-
y: nodeData.position.y + center.y,
|
|
715
|
-
},
|
|
716
769
|
}, {
|
|
717
770
|
inputs: nodeData.inputs,
|
|
771
|
+
position: nodeData.position
|
|
772
|
+
? {
|
|
773
|
+
x: nodeData.position.x + center.x,
|
|
774
|
+
y: nodeData.position.y + center.y,
|
|
775
|
+
}
|
|
776
|
+
: undefined,
|
|
777
|
+
size: nodeData.size,
|
|
718
778
|
copyOutputsFrom: nodeData.originalNodeId,
|
|
719
779
|
dry: true,
|
|
720
780
|
});
|
|
@@ -773,8 +833,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
773
833
|
this.nodeNames[nodeId] = name.trim();
|
|
774
834
|
}
|
|
775
835
|
this.emit("graphUiChanged", {
|
|
776
|
-
|
|
777
|
-
change: { type: "nodeName", nodeId },
|
|
836
|
+
change: { type: "nodeName", nodeId, name },
|
|
778
837
|
...options,
|
|
779
838
|
});
|
|
780
839
|
}
|
|
@@ -2194,7 +2253,6 @@ function useWorkbenchBridge(wb) {
|
|
|
2194
2253
|
}, { commit: true });
|
|
2195
2254
|
}, [wb]);
|
|
2196
2255
|
const onNodesChange = useCallback((changes) => {
|
|
2197
|
-
// Apply position updates continuously, but mark commit only on drag end
|
|
2198
2256
|
const positions = {};
|
|
2199
2257
|
let commit = false;
|
|
2200
2258
|
changes.forEach((c) => {
|
|
@@ -2204,6 +2262,14 @@ function useWorkbenchBridge(wb) {
|
|
|
2204
2262
|
commit = true;
|
|
2205
2263
|
}
|
|
2206
2264
|
});
|
|
2265
|
+
const sizes = {};
|
|
2266
|
+
changes.forEach((c) => {
|
|
2267
|
+
if (c.type === "dimensions" && c.dimensions) {
|
|
2268
|
+
sizes[c.id] = c.dimensions;
|
|
2269
|
+
if (!c.resizing)
|
|
2270
|
+
commit = true;
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2207
2273
|
// Derive next node selection from change set
|
|
2208
2274
|
const current = wb.getSelection();
|
|
2209
2275
|
const nextNodeIds = new Set(current.nodes);
|
|
@@ -2238,7 +2304,12 @@ function useWorkbenchBridge(wb) {
|
|
|
2238
2304
|
});
|
|
2239
2305
|
}
|
|
2240
2306
|
if (Object.keys(positions).length > 0) {
|
|
2241
|
-
wb.setPositions(positions, {
|
|
2307
|
+
wb.setPositions(positions, {
|
|
2308
|
+
commit: commit && !Object.keys(sizes).length,
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
if (Object.keys(sizes).length > 0) {
|
|
2312
|
+
wb.setSizes(sizes, { commit });
|
|
2242
2313
|
}
|
|
2243
2314
|
}, [wb]);
|
|
2244
2315
|
const onEdgesDelete = useCallback((edges) => edges.forEach((e, idx) => wb.disconnect(e.id, { commit: idx === edges.length - 1 })), [wb]);
|
|
@@ -2428,7 +2499,7 @@ function useQueryParamString(key, defaultValue) {
|
|
|
2428
2499
|
return [val, set];
|
|
2429
2500
|
}
|
|
2430
2501
|
|
|
2431
|
-
function toReactFlow(def, positions, registry, opts) {
|
|
2502
|
+
function toReactFlow(def, positions, sizes, registry, opts) {
|
|
2432
2503
|
const EDGE_STYLE_MISSING = { stroke: "#f59e0b", strokeWidth: 2 }; // amber-500
|
|
2433
2504
|
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
2434
2505
|
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
@@ -2472,45 +2543,41 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2472
2543
|
const createHandleBoundsFn = opts.ui?.getCreateHandleBounds() ?? createHandleBounds;
|
|
2473
2544
|
const createHandleLayoutFn = opts.ui?.getCreateHandleLayout() ?? createHandleLayout;
|
|
2474
2545
|
const estimateNodeSizeFn = opts.ui?.getEstimateNodeSize() ?? estimateNodeSize;
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
2478
|
-
// If layoutNode is overridden, use it directly; otherwise use default with internal overrides
|
|
2479
|
-
const geom = layoutNodeOverride
|
|
2546
|
+
const computeLayout = (node, overrides) => {
|
|
2547
|
+
return layoutNodeOverride
|
|
2480
2548
|
? layoutNodeOverride({
|
|
2481
|
-
node
|
|
2549
|
+
node,
|
|
2482
2550
|
registry,
|
|
2483
2551
|
showValues: opts.showValues,
|
|
2484
|
-
overrides
|
|
2552
|
+
overrides,
|
|
2485
2553
|
})
|
|
2486
2554
|
: layoutNode({
|
|
2487
|
-
node
|
|
2555
|
+
node,
|
|
2488
2556
|
registry,
|
|
2489
2557
|
showValues: opts.showValues,
|
|
2490
|
-
overrides
|
|
2558
|
+
overrides,
|
|
2491
2559
|
}, {
|
|
2492
2560
|
estimateNodeSize: estimateNodeSizeFn,
|
|
2493
2561
|
createHandleBounds: createHandleBoundsFn,
|
|
2494
2562
|
createHandleLayout: createHandleLayoutFn,
|
|
2495
2563
|
});
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
typeId: getInputTypeId(inputSource, id),
|
|
2499
|
-
}));
|
|
2500
|
-
const outputHandles = geom.outputOrder.map((id) => ({
|
|
2501
|
-
id,
|
|
2502
|
-
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
2503
|
-
}));
|
|
2504
|
-
nodeHandleMap[n.nodeId] = {
|
|
2505
|
-
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
2506
|
-
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
2507
|
-
};
|
|
2508
|
-
// Append placeholder entries for any missing handles (below valid ones)
|
|
2564
|
+
};
|
|
2565
|
+
const calculateDimensionsWithMissingHandles = (geom, extraInputs, extraOutputs) => {
|
|
2509
2566
|
const baseLeftCount = geom.inputOrder.length;
|
|
2510
2567
|
const baseRightCount = geom.outputOrder.length;
|
|
2511
|
-
const
|
|
2512
|
-
const
|
|
2513
|
-
|
|
2568
|
+
const baseRows = Math.max(baseLeftCount, baseRightCount);
|
|
2569
|
+
const newRows = Math.max(baseLeftCount + extraInputs.length, baseRightCount + extraOutputs.length);
|
|
2570
|
+
return {
|
|
2571
|
+
baseLeftCount,
|
|
2572
|
+
baseRightCount,
|
|
2573
|
+
baseRows,
|
|
2574
|
+
newRows,
|
|
2575
|
+
width: geom.width,
|
|
2576
|
+
height: geom.height + Math.max(0, newRows - baseRows) * NODE_ROW_HEIGHT_PX,
|
|
2577
|
+
};
|
|
2578
|
+
};
|
|
2579
|
+
const createMissingHandleLayouts = (extraInputs, extraOutputs, baseLeftCount, baseRightCount) => {
|
|
2580
|
+
const left = extraInputs.map((id, i) => ({
|
|
2514
2581
|
...createHandleLayoutFn({
|
|
2515
2582
|
id,
|
|
2516
2583
|
type: "target",
|
|
@@ -2519,7 +2586,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2519
2586
|
}),
|
|
2520
2587
|
missing: true,
|
|
2521
2588
|
}));
|
|
2522
|
-
const
|
|
2589
|
+
const right = extraOutputs.map((id, i) => ({
|
|
2523
2590
|
...createHandleLayoutFn({
|
|
2524
2591
|
id,
|
|
2525
2592
|
type: "source",
|
|
@@ -2528,36 +2595,58 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2528
2595
|
}),
|
|
2529
2596
|
missing: true,
|
|
2530
2597
|
}));
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
];
|
|
2536
|
-
// Precompute handle bounds (including missing) so edges can render immediately
|
|
2537
|
-
const missingBoundsLeft = extraInputs.map((id, i) => createHandleBoundsFn({
|
|
2598
|
+
return [...left, ...right];
|
|
2599
|
+
};
|
|
2600
|
+
const createMissingHandleBounds = (extraInputs, extraOutputs, baseLeftCount, baseRightCount, nodeWidth) => {
|
|
2601
|
+
const left = extraInputs.map((id, i) => createHandleBoundsFn({
|
|
2538
2602
|
id,
|
|
2539
2603
|
type: "target",
|
|
2540
2604
|
position: Position.Left,
|
|
2541
2605
|
rowIndex: baseLeftCount + i,
|
|
2542
|
-
nodeWidth
|
|
2606
|
+
nodeWidth,
|
|
2543
2607
|
}));
|
|
2544
|
-
const
|
|
2608
|
+
const right = extraOutputs.map((id, i) => createHandleBoundsFn({
|
|
2545
2609
|
id,
|
|
2546
2610
|
type: "source",
|
|
2547
2611
|
position: Position.Right,
|
|
2548
2612
|
rowIndex: baseRightCount + i,
|
|
2549
|
-
nodeWidth
|
|
2613
|
+
nodeWidth,
|
|
2550
2614
|
}));
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
const
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
const
|
|
2615
|
+
return [...left, ...right];
|
|
2616
|
+
};
|
|
2617
|
+
const nodes = def.nodes.map((n) => {
|
|
2618
|
+
const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
|
|
2619
|
+
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
2620
|
+
const customSize = sizes?.[n.nodeId];
|
|
2621
|
+
const sizeOverrides = customSize
|
|
2622
|
+
? { ...overrideSize, width: customSize.width, height: customSize.height }
|
|
2623
|
+
: overrideSize;
|
|
2624
|
+
const extraInputs = Array.from(missingInputsByNode[n.nodeId] || []);
|
|
2625
|
+
const extraOutputs = Array.from(missingOutputsByNode[n.nodeId] || []);
|
|
2626
|
+
const geom = computeLayout(n, sizeOverrides);
|
|
2627
|
+
const finalDims = calculateDimensionsWithMissingHandles(geom, extraInputs, extraOutputs);
|
|
2628
|
+
const renderWidth = customSize?.width ?? finalDims.width;
|
|
2629
|
+
const renderHeight = customSize?.height ?? finalDims.height;
|
|
2630
|
+
const initialGeom = customSize ? computeLayout(n, overrideSize) : geom;
|
|
2631
|
+
const initialDims = customSize
|
|
2632
|
+
? calculateDimensionsWithMissingHandles(initialGeom, extraInputs, extraOutputs)
|
|
2633
|
+
: finalDims;
|
|
2634
|
+
const inputHandles = geom.inputOrder.map((id) => ({
|
|
2635
|
+
id,
|
|
2636
|
+
typeId: getInputTypeId(inputSource, id),
|
|
2637
|
+
}));
|
|
2638
|
+
const outputHandles = geom.outputOrder.map((id) => ({
|
|
2639
|
+
id,
|
|
2640
|
+
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
2641
|
+
}));
|
|
2642
|
+
nodeHandleMap[n.nodeId] = {
|
|
2643
|
+
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
2644
|
+
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
2645
|
+
};
|
|
2646
|
+
const missingHandleLayouts = createMissingHandleLayouts(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount);
|
|
2647
|
+
const handleLayout = [...geom.handleLayout, ...missingHandleLayouts];
|
|
2648
|
+
const missingHandleBounds = createMissingHandleBounds(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount, renderWidth);
|
|
2649
|
+
const handles = [...geom.handles, ...missingHandleBounds];
|
|
2561
2650
|
return {
|
|
2562
2651
|
id: n.nodeId,
|
|
2563
2652
|
data: {
|
|
@@ -2571,8 +2660,10 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2571
2660
|
])),
|
|
2572
2661
|
handleLayout,
|
|
2573
2662
|
showValues: opts.showValues,
|
|
2574
|
-
renderWidth
|
|
2575
|
-
renderHeight
|
|
2663
|
+
renderWidth,
|
|
2664
|
+
renderHeight,
|
|
2665
|
+
initialWidth: initialDims.width,
|
|
2666
|
+
initialHeight: initialDims.height,
|
|
2576
2667
|
inputValues: opts.inputs?.[n.nodeId],
|
|
2577
2668
|
inputDefaults: opts.inputDefaults?.[n.nodeId],
|
|
2578
2669
|
outputValues: opts.outputs?.[n.nodeId],
|
|
@@ -2590,11 +2681,13 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
2590
2681
|
selected: opts.selectedNodeIds
|
|
2591
2682
|
? opts.selectedNodeIds.has(n.nodeId)
|
|
2592
2683
|
: undefined,
|
|
2593
|
-
|
|
2594
|
-
|
|
2684
|
+
measured: {
|
|
2685
|
+
width: renderWidth,
|
|
2686
|
+
height: renderHeight,
|
|
2687
|
+
},
|
|
2595
2688
|
handles,
|
|
2596
|
-
width:
|
|
2597
|
-
height:
|
|
2689
|
+
width: renderWidth,
|
|
2690
|
+
height: renderHeight,
|
|
2598
2691
|
};
|
|
2599
2692
|
});
|
|
2600
2693
|
const edges = def.edges.map((e) => {
|
|
@@ -2801,22 +2894,30 @@ async function upload(parsed, wb, runner) {
|
|
|
2801
2894
|
/**
|
|
2802
2895
|
* Merge UI state from source into target, remapping node IDs using nodeIdMap.
|
|
2803
2896
|
* Preserves target state and adds/updates source state with remapped IDs.
|
|
2897
|
+
* If anchorPos and sourceDef are provided, positions will be offset relative to anchorPos.
|
|
2804
2898
|
*/
|
|
2805
|
-
function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
2899
|
+
function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
|
|
2806
2900
|
const result = {
|
|
2807
2901
|
...targetUI,
|
|
2808
2902
|
};
|
|
2809
2903
|
if (!sourceUI)
|
|
2810
2904
|
return result;
|
|
2811
|
-
// Merge positions
|
|
2905
|
+
// Merge positions with optional offset
|
|
2812
2906
|
if (sourceUI.positions) {
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2907
|
+
if (anchorPos && sourceDef) {
|
|
2908
|
+
// Apply offset when anchorPos and sourceDef are provided
|
|
2909
|
+
result.positions = offsetImportedPositions(targetUI?.positions ?? {}, sourceUI.positions, sourceDef, nodeIdMap, anchorPos);
|
|
2910
|
+
}
|
|
2911
|
+
else {
|
|
2912
|
+
// Simple remapping without offset
|
|
2913
|
+
result.positions = {
|
|
2914
|
+
...(targetUI?.positions || {}),
|
|
2915
|
+
...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [
|
|
2916
|
+
nodeIdMap[oldId] || oldId,
|
|
2917
|
+
pos,
|
|
2918
|
+
])),
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2820
2921
|
}
|
|
2821
2922
|
// Merge selection: remap node IDs and edge IDs
|
|
2822
2923
|
if (sourceUI.selection) {
|
|
@@ -2839,6 +2940,15 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap) {
|
|
|
2839
2940
|
])),
|
|
2840
2941
|
};
|
|
2841
2942
|
}
|
|
2943
|
+
if (sourceUI.sizes) {
|
|
2944
|
+
result.sizes = {
|
|
2945
|
+
...(targetUI?.sizes || {}),
|
|
2946
|
+
...Object.fromEntries(Object.entries(sourceUI.sizes).map(([oldId, size]) => [
|
|
2947
|
+
nodeIdMap[oldId] || oldId,
|
|
2948
|
+
size,
|
|
2949
|
+
])),
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2842
2952
|
return result;
|
|
2843
2953
|
}
|
|
2844
2954
|
|
|
@@ -3096,7 +3206,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3096
3206
|
(type === "graphChanged" || type === "graphUiChanged")) {
|
|
3097
3207
|
const changeType = payload
|
|
3098
3208
|
.change?.type;
|
|
3099
|
-
if (changeType === "moveNode" ||
|
|
3209
|
+
if (changeType === "moveNode" ||
|
|
3210
|
+
changeType === "moveNodes" ||
|
|
3211
|
+
changeType === "resizeNodes")
|
|
3100
3212
|
return prev;
|
|
3101
3213
|
}
|
|
3102
3214
|
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
@@ -3505,6 +3617,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3505
3617
|
else if (changeType === "moveNodes") {
|
|
3506
3618
|
reason = "move-nodes";
|
|
3507
3619
|
}
|
|
3620
|
+
else if (changeType === "resizeNodes") {
|
|
3621
|
+
reason = "resize-nodes";
|
|
3622
|
+
}
|
|
3508
3623
|
else if (changeType === "selection") {
|
|
3509
3624
|
reason = "selection";
|
|
3510
3625
|
}
|
|
@@ -4262,103 +4377,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
4262
4377
|
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) })] }), debug && (jsx("div", { className: "mt-3 flex-none min-h-0 h-[50%]", children: jsx(DebugEvents, { autoScroll: !!autoScroll, hideWorkbench: !!hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange }) }))] }));
|
|
4263
4378
|
}
|
|
4264
4379
|
|
|
4265
|
-
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
4266
|
-
return (jsxs(Fragment, { children: [jsx(Handle, { id: id, type: type, position: position, isConnectable: isConnectable, className: className, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsx("div", { className: labelClassName + (kind === "input" ? " left-2" : " right-2"), style: {
|
|
4267
|
-
top: (y ?? 0) - 8,
|
|
4268
|
-
...(kind === "input"
|
|
4269
|
-
? { right: "50%" }
|
|
4270
|
-
: { left: "50%", textAlign: "right" }),
|
|
4271
|
-
whiteSpace: "nowrap",
|
|
4272
|
-
overflow: "hidden",
|
|
4273
|
-
textOverflow: "ellipsis",
|
|
4274
|
-
}, children: renderLabel({ kind, id }) }))] }));
|
|
4275
|
-
}
|
|
4276
|
-
function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
4277
|
-
const layout = data.handleLayout ?? [];
|
|
4278
|
-
const byId = React.useMemo(() => {
|
|
4279
|
-
const m = new Map();
|
|
4280
|
-
for (const h of layout) {
|
|
4281
|
-
// Prefer namespaced key to disambiguate inputs/outputs that share id
|
|
4282
|
-
m.set(`${h.type}:${h.id}`, {
|
|
4283
|
-
position: h.position,
|
|
4284
|
-
y: h.y,
|
|
4285
|
-
type: h.type,
|
|
4286
|
-
missing: h.missing,
|
|
4287
|
-
});
|
|
4288
|
-
// Back-compat: also store by id-only if not already set
|
|
4289
|
-
if (!m.has(h.id))
|
|
4290
|
-
m.set(h.id, {
|
|
4291
|
-
position: h.position,
|
|
4292
|
-
y: h.y,
|
|
4293
|
-
type: h.type,
|
|
4294
|
-
missing: h.missing,
|
|
4295
|
-
});
|
|
4296
|
-
}
|
|
4297
|
-
return m;
|
|
4298
|
-
}, [layout]);
|
|
4299
|
-
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
4300
|
-
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
4301
|
-
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
4302
|
-
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
4303
|
-
return (jsxs(Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
4304
|
-
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
4305
|
-
const position = placed?.position ?? Position.Left;
|
|
4306
|
-
const y = placed?.y;
|
|
4307
|
-
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
|
|
4308
|
-
return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4309
|
-
}), missingInputs.map((h) => {
|
|
4310
|
-
const key = `missing-input:${h.id}`;
|
|
4311
|
-
const position = h.position ?? Position.Left;
|
|
4312
|
-
const y = h.y;
|
|
4313
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
|
|
4314
|
-
return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: false, className: `${cls} wb-nodrag wb-nowheel`, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4315
|
-
}), (data.outputHandles ?? []).map((h) => {
|
|
4316
|
-
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
4317
|
-
const position = placed?.position ?? Position.Right;
|
|
4318
|
-
const y = placed?.y;
|
|
4319
|
-
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
|
|
4320
|
-
return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4321
|
-
}), missingOutputs.map((h) => {
|
|
4322
|
-
const key = `missing-output:${h.id}`;
|
|
4323
|
-
const position = h.position ?? Position.Right;
|
|
4324
|
-
const y = h.y;
|
|
4325
|
-
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
4326
|
-
return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4327
|
-
})] }));
|
|
4328
|
-
}
|
|
4329
|
-
|
|
4330
|
-
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
4331
|
-
const updateNodeInternals = useUpdateNodeInternals();
|
|
4332
|
-
const { typeId, showValues } = data;
|
|
4333
|
-
const inputEntries = data.inputHandles ?? [];
|
|
4334
|
-
const outputEntries = data.outputHandles ?? [];
|
|
4335
|
-
React.useEffect(() => {
|
|
4336
|
-
updateNodeInternals(id);
|
|
4337
|
-
}, [
|
|
4338
|
-
id,
|
|
4339
|
-
inputEntries.length,
|
|
4340
|
-
outputEntries.length,
|
|
4341
|
-
showValues,
|
|
4342
|
-
updateNodeInternals,
|
|
4343
|
-
]);
|
|
4344
|
-
const status = data.status ?? { activeRuns: 0 };
|
|
4345
|
-
const validation = data.validation ?? {
|
|
4346
|
-
inputs: [],
|
|
4347
|
-
outputs: [],
|
|
4348
|
-
issues: [],
|
|
4349
|
-
};
|
|
4350
|
-
const containerBorder = getNodeBorderClassNames({
|
|
4351
|
-
selected,
|
|
4352
|
-
status,
|
|
4353
|
-
validation,
|
|
4354
|
-
});
|
|
4355
|
-
return (jsxs("div", { className: cx("rounded-lg bg-white/50 !dark:bg-stone-900", containerBorder), style: {
|
|
4356
|
-
position: "relative",
|
|
4357
|
-
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
4358
|
-
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
4359
|
-
}, children: [jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4360
|
-
});
|
|
4361
|
-
DefaultNode.displayName = "DefaultNode";
|
|
4362
4380
|
function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInvalidate, }) {
|
|
4363
4381
|
const ctx = useWorkbenchContext();
|
|
4364
4382
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
@@ -4442,6 +4460,72 @@ function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInv
|
|
|
4442
4460
|
.map((v) => `${v.code}: ${v.message}`)
|
|
4443
4461
|
.join("; ") })), showId && jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
4444
4462
|
}
|
|
4463
|
+
|
|
4464
|
+
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
4465
|
+
return (jsxs(Fragment, { children: [jsx(Handle, { id: id, type: type, position: position, isConnectable: isConnectable, className: className, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsx("div", { className: labelClassName + (kind === "input" ? " left-2" : " right-2"), style: {
|
|
4466
|
+
top: (y ?? 0) - 8,
|
|
4467
|
+
...(kind === "input"
|
|
4468
|
+
? { right: "50%" }
|
|
4469
|
+
: { left: "50%", textAlign: "right" }),
|
|
4470
|
+
whiteSpace: "nowrap",
|
|
4471
|
+
overflow: "hidden",
|
|
4472
|
+
textOverflow: "ellipsis",
|
|
4473
|
+
}, children: renderLabel({ kind, id }) }))] }));
|
|
4474
|
+
}
|
|
4475
|
+
function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
4476
|
+
const layout = data.handleLayout ?? [];
|
|
4477
|
+
const byId = React.useMemo(() => {
|
|
4478
|
+
const m = new Map();
|
|
4479
|
+
for (const h of layout) {
|
|
4480
|
+
// Prefer namespaced key to disambiguate inputs/outputs that share id
|
|
4481
|
+
m.set(`${h.type}:${h.id}`, {
|
|
4482
|
+
position: h.position,
|
|
4483
|
+
y: h.y,
|
|
4484
|
+
type: h.type,
|
|
4485
|
+
missing: h.missing,
|
|
4486
|
+
});
|
|
4487
|
+
// Back-compat: also store by id-only if not already set
|
|
4488
|
+
if (!m.has(h.id))
|
|
4489
|
+
m.set(h.id, {
|
|
4490
|
+
position: h.position,
|
|
4491
|
+
y: h.y,
|
|
4492
|
+
type: h.type,
|
|
4493
|
+
missing: h.missing,
|
|
4494
|
+
});
|
|
4495
|
+
}
|
|
4496
|
+
return m;
|
|
4497
|
+
}, [layout]);
|
|
4498
|
+
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
4499
|
+
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
4500
|
+
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
4501
|
+
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
4502
|
+
return (jsxs(Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
4503
|
+
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
4504
|
+
const position = placed?.position ?? Position.Left;
|
|
4505
|
+
const y = placed?.y;
|
|
4506
|
+
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
|
|
4507
|
+
return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4508
|
+
}), missingInputs.map((h) => {
|
|
4509
|
+
const key = `missing-input:${h.id}`;
|
|
4510
|
+
const position = h.position ?? Position.Left;
|
|
4511
|
+
const y = h.y;
|
|
4512
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
|
|
4513
|
+
return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: false, className: `${cls} wb-nodrag wb-nowheel`, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4514
|
+
}), (data.outputHandles ?? []).map((h) => {
|
|
4515
|
+
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
4516
|
+
const position = placed?.position ?? Position.Right;
|
|
4517
|
+
const y = placed?.y;
|
|
4518
|
+
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
|
|
4519
|
+
return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
4520
|
+
}), missingOutputs.map((h) => {
|
|
4521
|
+
const key = `missing-output:${h.id}`;
|
|
4522
|
+
const position = h.position ?? Position.Right;
|
|
4523
|
+
const y = h.y;
|
|
4524
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
4525
|
+
return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
4526
|
+
})] }));
|
|
4527
|
+
}
|
|
4528
|
+
|
|
4445
4529
|
function DefaultNodeContent({ data, isConnectable, }) {
|
|
4446
4530
|
const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
|
|
4447
4531
|
const inputEntries = data.inputHandles ?? [];
|
|
@@ -4494,6 +4578,24 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
4494
4578
|
} })] }));
|
|
4495
4579
|
}
|
|
4496
4580
|
|
|
4581
|
+
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
4582
|
+
const nodeRef = useRef(null);
|
|
4583
|
+
const { typeId } = data;
|
|
4584
|
+
const status = data.status ?? { activeRuns: 0 };
|
|
4585
|
+
const validation = data.validation ?? {
|
|
4586
|
+
inputs: [],
|
|
4587
|
+
outputs: [],
|
|
4588
|
+
issues: [],
|
|
4589
|
+
};
|
|
4590
|
+
const containerBorder = getNodeBorderClassNames({
|
|
4591
|
+
selected,
|
|
4592
|
+
status,
|
|
4593
|
+
validation,
|
|
4594
|
+
});
|
|
4595
|
+
return (jsxs("div", { ref: nodeRef, className: cx("rounded-lg bg-white/50 !dark:bg-stone-900 w-full h-full relative", containerBorder), children: [jsx(NodeResizer, { isVisible: selected, minWidth: data.initialWidth, minHeight: data.initialHeight }), jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4596
|
+
});
|
|
4597
|
+
DefaultNode.displayName = "DefaultNode";
|
|
4598
|
+
|
|
4497
4599
|
// Helper to format shortcut for current platform
|
|
4498
4600
|
function formatShortcut(shortcut) {
|
|
4499
4601
|
const isMac = typeof navigator !== "undefined" &&
|
|
@@ -4774,8 +4876,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4774
4876
|
position: n.position,
|
|
4775
4877
|
type: n.type,
|
|
4776
4878
|
selected: n.selected,
|
|
4777
|
-
|
|
4778
|
-
initialHeight: n.initialHeight,
|
|
4879
|
+
measured: n.measured,
|
|
4779
4880
|
data: n.data && {
|
|
4780
4881
|
typeId: n.data.typeId,
|
|
4781
4882
|
inputHandles: n.data.inputHandles,
|
|
@@ -4809,11 +4910,18 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4809
4910
|
useImperativeHandle(ref, () => ({
|
|
4810
4911
|
fitView: () => {
|
|
4811
4912
|
try {
|
|
4812
|
-
rfInstanceRef.current?.fitView(
|
|
4913
|
+
rfInstanceRef.current?.fitView();
|
|
4914
|
+
}
|
|
4915
|
+
catch (err) {
|
|
4916
|
+
console.warn("Failed to fit view", err);
|
|
4813
4917
|
}
|
|
4814
|
-
catch { }
|
|
4815
4918
|
},
|
|
4816
|
-
|
|
4919
|
+
setViewport: (viewport) => {
|
|
4920
|
+
if (rfInstanceRef.current) {
|
|
4921
|
+
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
4922
|
+
}
|
|
4923
|
+
},
|
|
4924
|
+
}), []);
|
|
4817
4925
|
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
4818
4926
|
const ui = wb.getUI();
|
|
4819
4927
|
const { nodeTypes, resolveNodeType } = useMemo(() => {
|
|
@@ -4859,7 +4967,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4859
4967
|
inputsWithDefaults[n.nodeId] = merged;
|
|
4860
4968
|
}
|
|
4861
4969
|
}
|
|
4862
|
-
const out = toReactFlow(wb.def, wb.getPositions(), registry, {
|
|
4970
|
+
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), registry, {
|
|
4863
4971
|
showValues,
|
|
4864
4972
|
inputs: inputsWithDefaults,
|
|
4865
4973
|
inputDefaults: inputDefaultsMap,
|
|
@@ -5079,7 +5187,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5079
5187
|
setNodeMenuOpen(false);
|
|
5080
5188
|
setSelectionMenuOpen(false);
|
|
5081
5189
|
};
|
|
5082
|
-
const addNodeAt = useCallback(async (typeId, opts) => wb.addNode({ typeId
|
|
5190
|
+
const addNodeAt = useCallback(async (typeId, opts) => wb.addNode({ typeId }, { inputs: opts.inputs, position: opts.position, commit: true }), [wb]);
|
|
5083
5191
|
const onCloseMenu = useCallback(() => {
|
|
5084
5192
|
setMenuOpen(false);
|
|
5085
5193
|
}, []);
|
|
@@ -5331,55 +5439,53 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5331
5439
|
showToast,
|
|
5332
5440
|
]);
|
|
5333
5441
|
// Get custom renderers from UI extension registry (reactive to uiVersion changes)
|
|
5334
|
-
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
|
|
5442
|
+
const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, SelectionContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
|
|
5335
5443
|
return {
|
|
5336
5444
|
BackgroundRenderer: ui.getBackgroundRenderer(),
|
|
5337
5445
|
MinimapRenderer: ui.getMinimapRenderer(),
|
|
5338
5446
|
ControlsRenderer: ui.getControlsRenderer(),
|
|
5339
5447
|
DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
|
|
5340
5448
|
NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
|
|
5449
|
+
SelectionContextMenuRenderer: ui.getSelectionContextMenuRenderer(),
|
|
5341
5450
|
connectionLineRenderer: ui.getConnectionLineRenderer(),
|
|
5342
5451
|
};
|
|
5343
5452
|
}, [ui, uiVersion]);
|
|
5344
5453
|
const onMoveEnd = useCallback(() => {
|
|
5345
5454
|
if (rfInstanceRef.current) {
|
|
5346
5455
|
const viewport = rfInstanceRef.current.getViewport();
|
|
5347
|
-
const viewportData = lod.
|
|
5456
|
+
const viewportData = lod.clone(viewport);
|
|
5348
5457
|
wb.setViewport(viewportData);
|
|
5349
5458
|
}
|
|
5350
5459
|
}, [wb]);
|
|
5351
|
-
|
|
5460
|
+
// Sync viewport when workbench fires graphUiChanged with viewport event
|
|
5352
5461
|
useEffect(() => {
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
zoom: currentViewport.zoom,
|
|
5366
|
-
});
|
|
5367
|
-
}
|
|
5368
|
-
});
|
|
5462
|
+
const off = wb.on("graphUiChanged", (event) => {
|
|
5463
|
+
if (event.change?.type === "viewport" &&
|
|
5464
|
+
rfInstanceRef.current &&
|
|
5465
|
+
event.init) {
|
|
5466
|
+
const viewport = wb.getViewport();
|
|
5467
|
+
if (viewport) {
|
|
5468
|
+
rfInstanceRef.current.setViewport(lod.clone(viewport));
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
});
|
|
5472
|
+
return () => off();
|
|
5473
|
+
}, [wb]);
|
|
5369
5474
|
return (jsxs("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) => {
|
|
5370
5475
|
rfInstanceRef.current = inst;
|
|
5371
5476
|
const savedViewport = wb.getViewport();
|
|
5372
5477
|
if (savedViewport) {
|
|
5373
|
-
|
|
5374
|
-
inst.setViewport(lod.pick(savedViewport, ["x", "y", "zoom"]));
|
|
5478
|
+
inst.setViewport(lod.clone(savedViewport));
|
|
5375
5479
|
}
|
|
5376
|
-
}, 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",
|
|
5480
|
+
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [BackgroundRenderer ? (jsx(BackgroundRenderer, {})) : (jsx(Background, { id: "workbench-canvas-background", variant: BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsx(MinimapRenderer, {}) : jsx(MiniMap, {}), ControlsRenderer ? jsx(ControlsRenderer, {}) : jsx(Controls, {}), DefaultContextMenuRenderer ? (jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
5377
5481
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5378
5482
|
: {}) })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
|
|
5379
5483
|
nodeContextMenuHandlers &&
|
|
5380
5484
|
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
|
|
5381
5485
|
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
5382
|
-
: {}) })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen &&
|
|
5486
|
+
: {}) })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen &&
|
|
5487
|
+
selectionMenuPos &&
|
|
5488
|
+
(SelectionContextMenuRenderer ? (jsx(SelectionContextMenuRenderer, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }) }), toast && (jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
|
|
5383
5489
|
});
|
|
5384
5490
|
|
|
5385
5491
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|