@bian-womp/spark-workbench 0.2.82 → 0.2.84

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.
Files changed (53) hide show
  1. package/lib/cjs/index.cjs +293 -212
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/core/InMemoryWorkbench.d.ts +16 -25
  4. package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
  5. package/lib/cjs/src/core/contracts.d.ts +4 -1
  6. package/lib/cjs/src/core/contracts.d.ts.map +1 -1
  7. package/lib/cjs/src/core/ui-extensions.d.ts +16 -7
  8. package/lib/cjs/src/core/ui-extensions.d.ts.map +1 -1
  9. package/lib/cjs/src/index.d.ts +2 -0
  10. package/lib/cjs/src/index.d.ts.map +1 -1
  11. package/lib/cjs/src/misc/DefaultNode.d.ts +0 -14
  12. package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
  13. package/lib/cjs/src/misc/DefaultNodeContent.d.ts +4 -0
  14. package/lib/cjs/src/misc/DefaultNodeContent.d.ts.map +1 -0
  15. package/lib/cjs/src/misc/DefaultNodeHeader.d.ts +15 -0
  16. package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -0
  17. package/lib/cjs/src/misc/WorkbenchCanvas.d.ts +2 -0
  18. package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  19. package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  20. package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
  21. package/lib/cjs/src/misc/mapping.d.ts +9 -2
  22. package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
  23. package/lib/cjs/src/misc/merge-utils.d.ts +6 -1
  24. package/lib/cjs/src/misc/merge-utils.d.ts.map +1 -1
  25. package/lib/cjs/src/misc/types.d.ts +4 -0
  26. package/lib/cjs/src/misc/types.d.ts.map +1 -1
  27. package/lib/esm/index.js +295 -214
  28. package/lib/esm/index.js.map +1 -1
  29. package/lib/esm/src/core/InMemoryWorkbench.d.ts +16 -25
  30. package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
  31. package/lib/esm/src/core/contracts.d.ts +4 -1
  32. package/lib/esm/src/core/contracts.d.ts.map +1 -1
  33. package/lib/esm/src/core/ui-extensions.d.ts +16 -7
  34. package/lib/esm/src/core/ui-extensions.d.ts.map +1 -1
  35. package/lib/esm/src/index.d.ts +2 -0
  36. package/lib/esm/src/index.d.ts.map +1 -1
  37. package/lib/esm/src/misc/DefaultNode.d.ts +0 -14
  38. package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
  39. package/lib/esm/src/misc/DefaultNodeContent.d.ts +4 -0
  40. package/lib/esm/src/misc/DefaultNodeContent.d.ts.map +1 -0
  41. package/lib/esm/src/misc/DefaultNodeHeader.d.ts +15 -0
  42. package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -0
  43. package/lib/esm/src/misc/WorkbenchCanvas.d.ts +2 -0
  44. package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  45. package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  46. package/lib/esm/src/misc/hooks.d.ts.map +1 -1
  47. package/lib/esm/src/misc/mapping.d.ts +9 -2
  48. package/lib/esm/src/misc/mapping.d.ts.map +1 -1
  49. package/lib/esm/src/misc/merge-utils.d.ts +6 -1
  50. package/lib/esm/src/misc/merge-utils.d.ts.map +1 -1
  51. package/lib/esm/src/misc/types.d.ts +4 -0
  52. package/lib/esm/src/misc/types.d.ts.map +1 -1
  53. 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, useUpdateNodeInternals, useReactFlow, ReactFlowProvider, ReactFlow, Background, BackgroundVariant, MiniMap, Controls } from '@xyflow/react';
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: [],
@@ -285,27 +293,36 @@ class InMemoryWorkbench extends AbstractWorkbench {
285
293
  });
286
294
  }
287
295
  // Position and selection APIs for React Flow bridge
288
- setPositions(map, options) {
289
- this.positions = { ...this.positions, ...map };
296
+ setPositions(positions, options) {
297
+ this.positions = { ...this.positions, ...positions };
298
+ this.emit("graphUiChanged", { change: { type: "moveNodes" }, ...options });
299
+ }
300
+ getPositions() {
301
+ return { ...this.positions };
302
+ }
303
+ setSizes(sizes, options) {
304
+ for (const [nodeId, size] of Object.entries(sizes)) {
305
+ if (size) {
306
+ this.sizes = { ...this.sizes, [nodeId]: size };
307
+ }
308
+ else {
309
+ this.sizes = lod.omit(this.sizes, nodeId);
310
+ }
311
+ }
290
312
  this.emit("graphUiChanged", {
291
- def: this._def,
292
- change: { type: "moveNodes" },
313
+ change: { type: "resizeNodes" },
293
314
  ...options,
294
315
  });
295
316
  }
296
- getPositions() {
297
- return { ...this.positions };
317
+ getSizes() {
318
+ return { ...this.sizes };
298
319
  }
299
320
  setSelectionInternal(sel, options) {
300
321
  if (lod.isEqual(this.selection, sel))
301
322
  return;
302
323
  this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
303
324
  this.emit("selectionChanged", this.selection);
304
- this.emit("graphUiChanged", {
305
- def: this._def,
306
- change: { type: "selection" },
307
- ...options,
308
- });
325
+ this.emit("graphUiChanged", { change: { type: "selection" }, ...options });
309
326
  }
310
327
  setSelection(sel, options) {
311
328
  this.setSelectionInternal(sel, options);
@@ -336,10 +353,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
336
353
  if (lod.isEqual(this.viewport, viewport))
337
354
  return;
338
355
  this.viewport = { ...viewport };
339
- this.emit("graphUiChanged", {
340
- def: this._def,
341
- change: { type: "viewport" },
342
- });
356
+ this.emit("graphUiChanged", { change: { type: "viewport" } });
343
357
  }
344
358
  getViewport() {
345
359
  return this.viewport ? { ...this.viewport } : null;
@@ -351,6 +365,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
351
365
  const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
352
366
  const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
353
367
  const filteredNodeNames = Object.fromEntries(Object.entries(this.nodeNames).filter(([id]) => defNodeIds.has(id)));
368
+ const filteredSizes = Object.fromEntries(Object.entries(this.sizes).filter(([id]) => defNodeIds.has(id)));
354
369
  return {
355
370
  positions: Object.keys(filteredPositions).length > 0
356
371
  ? filteredPositions
@@ -365,6 +380,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
365
380
  nodeNames: Object.keys(filteredNodeNames).length > 0
366
381
  ? filteredNodeNames
367
382
  : undefined,
383
+ sizes: Object.keys(filteredSizes).length > 0 ? filteredSizes : undefined,
368
384
  };
369
385
  }
370
386
  setUIState(ui) {
@@ -379,12 +395,27 @@ class InMemoryWorkbench extends AbstractWorkbench {
379
395
  edges: [...ui.selection.edges],
380
396
  };
381
397
  this.emit("selectionChanged", this.selection);
398
+ this.emit("graphUiChanged", {
399
+ change: { type: "selection" },
400
+ init: true,
401
+ });
382
402
  }
383
403
  if (ui.viewport) {
384
404
  this.viewport = { ...ui.viewport };
405
+ this.emit("graphUiChanged", { change: { type: "viewport" }, init: true });
385
406
  }
386
407
  if (ui.nodeNames !== undefined) {
387
408
  this.nodeNames = { ...ui.nodeNames };
409
+ for (const [nodeId, name] of Object.entries(ui.nodeNames)) {
410
+ this.emit("graphUiChanged", {
411
+ change: { type: "nodeName", nodeId, name },
412
+ init: true,
413
+ });
414
+ }
415
+ }
416
+ if (ui.sizes !== undefined) {
417
+ const defNodeIds = new Set(this._def.nodes.map((n) => n.nodeId));
418
+ this.sizes = Object.fromEntries(Object.entries(ui.sizes).filter(([id]) => defNodeIds.has(id)));
388
419
  }
389
420
  }
390
421
  getRuntimeState() {
@@ -773,8 +804,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
773
804
  this.nodeNames[nodeId] = name.trim();
774
805
  }
775
806
  this.emit("graphUiChanged", {
776
- def: this._def,
777
- change: { type: "nodeName", nodeId },
807
+ change: { type: "nodeName", nodeId, name },
778
808
  ...options,
779
809
  });
780
810
  }
@@ -2194,7 +2224,6 @@ function useWorkbenchBridge(wb) {
2194
2224
  }, { commit: true });
2195
2225
  }, [wb]);
2196
2226
  const onNodesChange = useCallback((changes) => {
2197
- // Apply position updates continuously, but mark commit only on drag end
2198
2227
  const positions = {};
2199
2228
  let commit = false;
2200
2229
  changes.forEach((c) => {
@@ -2204,6 +2233,14 @@ function useWorkbenchBridge(wb) {
2204
2233
  commit = true;
2205
2234
  }
2206
2235
  });
2236
+ const sizes = {};
2237
+ changes.forEach((c) => {
2238
+ if (c.type === "dimensions" && c.dimensions) {
2239
+ sizes[c.id] = c.dimensions;
2240
+ if (!c.resizing)
2241
+ commit = true;
2242
+ }
2243
+ });
2207
2244
  // Derive next node selection from change set
2208
2245
  const current = wb.getSelection();
2209
2246
  const nextNodeIds = new Set(current.nodes);
@@ -2238,7 +2275,12 @@ function useWorkbenchBridge(wb) {
2238
2275
  });
2239
2276
  }
2240
2277
  if (Object.keys(positions).length > 0) {
2241
- wb.setPositions(positions, { commit });
2278
+ wb.setPositions(positions, {
2279
+ commit: commit && !Object.keys(sizes).length,
2280
+ });
2281
+ }
2282
+ if (Object.keys(sizes).length > 0) {
2283
+ wb.setSizes(sizes, { commit });
2242
2284
  }
2243
2285
  }, [wb]);
2244
2286
  const onEdgesDelete = useCallback((edges) => edges.forEach((e, idx) => wb.disconnect(e.id, { commit: idx === edges.length - 1 })), [wb]);
@@ -2290,8 +2332,9 @@ function useWorkbenchBridge(wb) {
2290
2332
  function useWorkbenchGraphTick(wb) {
2291
2333
  const [tick, setTick] = useState(0);
2292
2334
  useEffect(() => {
2293
- const bump = () => setTick((t) => t + 1);
2294
- const off = wb.on("graphChanged", bump);
2335
+ const off = wb.on("graphChanged", () => {
2336
+ setTick((t) => t + 1);
2337
+ });
2295
2338
  return () => off();
2296
2339
  }, [wb]);
2297
2340
  return tick;
@@ -2299,8 +2342,11 @@ function useWorkbenchGraphTick(wb) {
2299
2342
  function useWorkbenchGraphUiTick(wb) {
2300
2343
  const [tick, setTick] = useState(0);
2301
2344
  useEffect(() => {
2302
- const bump = () => setTick((t) => t + 1);
2303
- const off = wb.on("graphUiChanged", bump);
2345
+ const off = wb.on("graphUiChanged", (evt) => {
2346
+ if (evt.change?.type === "viewport")
2347
+ return;
2348
+ setTick((t) => t + 1);
2349
+ });
2304
2350
  return () => off();
2305
2351
  }, [wb]);
2306
2352
  return tick;
@@ -2424,7 +2470,7 @@ function useQueryParamString(key, defaultValue) {
2424
2470
  return [val, set];
2425
2471
  }
2426
2472
 
2427
- function toReactFlow(def, positions, registry, opts) {
2473
+ function toReactFlow(def, positions, sizes, registry, opts) {
2428
2474
  const EDGE_STYLE_MISSING = { stroke: "#f59e0b", strokeWidth: 2 }; // amber-500
2429
2475
  const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
2430
2476
  const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
@@ -2468,45 +2514,41 @@ function toReactFlow(def, positions, registry, opts) {
2468
2514
  const createHandleBoundsFn = opts.ui?.getCreateHandleBounds() ?? createHandleBounds;
2469
2515
  const createHandleLayoutFn = opts.ui?.getCreateHandleLayout() ?? createHandleLayout;
2470
2516
  const estimateNodeSizeFn = opts.ui?.getEstimateNodeSize() ?? estimateNodeSize;
2471
- const nodes = def.nodes.map((n) => {
2472
- const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
2473
- const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
2474
- // If layoutNode is overridden, use it directly; otherwise use default with internal overrides
2475
- const geom = layoutNodeOverride
2517
+ const computeLayout = (node, overrides) => {
2518
+ return layoutNodeOverride
2476
2519
  ? layoutNodeOverride({
2477
- node: n,
2520
+ node,
2478
2521
  registry,
2479
2522
  showValues: opts.showValues,
2480
- overrides: overrideSize,
2523
+ overrides,
2481
2524
  })
2482
2525
  : layoutNode({
2483
- node: n,
2526
+ node,
2484
2527
  registry,
2485
2528
  showValues: opts.showValues,
2486
- overrides: overrideSize,
2529
+ overrides,
2487
2530
  }, {
2488
2531
  estimateNodeSize: estimateNodeSizeFn,
2489
2532
  createHandleBounds: createHandleBoundsFn,
2490
2533
  createHandleLayout: createHandleLayoutFn,
2491
2534
  });
2492
- const inputHandles = geom.inputOrder.map((id) => ({
2493
- id,
2494
- typeId: getInputTypeId(inputSource, id),
2495
- }));
2496
- const outputHandles = geom.outputOrder.map((id) => ({
2497
- id,
2498
- typeId: formatDeclaredTypeSignature(outputSource[id]),
2499
- }));
2500
- nodeHandleMap[n.nodeId] = {
2501
- inputs: new Set(inputHandles.map((h) => h.id)),
2502
- outputs: new Set(outputHandles.map((h) => h.id)),
2503
- };
2504
- // Append placeholder entries for any missing handles (below valid ones)
2535
+ };
2536
+ const calculateDimensionsWithMissingHandles = (geom, extraInputs, extraOutputs) => {
2505
2537
  const baseLeftCount = geom.inputOrder.length;
2506
2538
  const baseRightCount = geom.outputOrder.length;
2507
- const extraInputs = Array.from(missingInputsByNode[n.nodeId] || []);
2508
- const extraOutputs = Array.from(missingOutputsByNode[n.nodeId] || []);
2509
- const extraHandleLayoutLeft = extraInputs.map((id, i) => ({
2539
+ const baseRows = Math.max(baseLeftCount, baseRightCount);
2540
+ const newRows = Math.max(baseLeftCount + extraInputs.length, baseRightCount + extraOutputs.length);
2541
+ return {
2542
+ baseLeftCount,
2543
+ baseRightCount,
2544
+ baseRows,
2545
+ newRows,
2546
+ width: geom.width,
2547
+ height: geom.height + Math.max(0, newRows - baseRows) * NODE_ROW_HEIGHT_PX,
2548
+ };
2549
+ };
2550
+ const createMissingHandleLayouts = (extraInputs, extraOutputs, baseLeftCount, baseRightCount) => {
2551
+ const left = extraInputs.map((id, i) => ({
2510
2552
  ...createHandleLayoutFn({
2511
2553
  id,
2512
2554
  type: "target",
@@ -2515,7 +2557,7 @@ function toReactFlow(def, positions, registry, opts) {
2515
2557
  }),
2516
2558
  missing: true,
2517
2559
  }));
2518
- const extraHandleLayoutRight = extraOutputs.map((id, i) => ({
2560
+ const right = extraOutputs.map((id, i) => ({
2519
2561
  ...createHandleLayoutFn({
2520
2562
  id,
2521
2563
  type: "source",
@@ -2524,36 +2566,58 @@ function toReactFlow(def, positions, registry, opts) {
2524
2566
  }),
2525
2567
  missing: true,
2526
2568
  }));
2527
- const handleLayout = [
2528
- ...geom.handleLayout,
2529
- ...extraHandleLayoutLeft,
2530
- ...extraHandleLayoutRight,
2531
- ];
2532
- // Precompute handle bounds (including missing) so edges can render immediately
2533
- const missingBoundsLeft = extraInputs.map((id, i) => createHandleBoundsFn({
2569
+ return [...left, ...right];
2570
+ };
2571
+ const createMissingHandleBounds = (extraInputs, extraOutputs, baseLeftCount, baseRightCount, nodeWidth) => {
2572
+ const left = extraInputs.map((id, i) => createHandleBoundsFn({
2534
2573
  id,
2535
2574
  type: "target",
2536
2575
  position: Position.Left,
2537
2576
  rowIndex: baseLeftCount + i,
2538
- nodeWidth: geom.width,
2577
+ nodeWidth,
2539
2578
  }));
2540
- const missingBoundsRight = extraOutputs.map((id, i) => createHandleBoundsFn({
2579
+ const right = extraOutputs.map((id, i) => createHandleBoundsFn({
2541
2580
  id,
2542
2581
  type: "source",
2543
2582
  position: Position.Right,
2544
2583
  rowIndex: baseRightCount + i,
2545
- nodeWidth: geom.width,
2584
+ nodeWidth,
2546
2585
  }));
2547
- const handles = [
2548
- ...geom.handles,
2549
- ...missingBoundsLeft,
2550
- ...missingBoundsRight,
2551
- ];
2552
- // Adjust node height to accommodate missing handle rows
2553
- const baseRows = Math.max(baseLeftCount, baseRightCount);
2554
- const newRows = Math.max(baseLeftCount + extraInputs.length, baseRightCount + extraOutputs.length);
2555
- const initialWidth = geom.width;
2556
- const initialHeight = geom.height + Math.max(0, newRows - baseRows) * NODE_ROW_HEIGHT_PX;
2586
+ return [...left, ...right];
2587
+ };
2588
+ const nodes = def.nodes.map((n) => {
2589
+ const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
2590
+ const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
2591
+ const customSize = sizes?.[n.nodeId];
2592
+ const sizeOverrides = customSize
2593
+ ? { ...overrideSize, width: customSize.width, height: customSize.height }
2594
+ : overrideSize;
2595
+ const extraInputs = Array.from(missingInputsByNode[n.nodeId] || []);
2596
+ const extraOutputs = Array.from(missingOutputsByNode[n.nodeId] || []);
2597
+ const geom = computeLayout(n, sizeOverrides);
2598
+ const finalDims = calculateDimensionsWithMissingHandles(geom, extraInputs, extraOutputs);
2599
+ const renderWidth = customSize?.width ?? finalDims.width;
2600
+ const renderHeight = customSize?.height ?? finalDims.height;
2601
+ const initialGeom = customSize ? computeLayout(n, overrideSize) : geom;
2602
+ const initialDims = customSize
2603
+ ? calculateDimensionsWithMissingHandles(initialGeom, extraInputs, extraOutputs)
2604
+ : finalDims;
2605
+ const inputHandles = geom.inputOrder.map((id) => ({
2606
+ id,
2607
+ typeId: getInputTypeId(inputSource, id),
2608
+ }));
2609
+ const outputHandles = geom.outputOrder.map((id) => ({
2610
+ id,
2611
+ typeId: formatDeclaredTypeSignature(outputSource[id]),
2612
+ }));
2613
+ nodeHandleMap[n.nodeId] = {
2614
+ inputs: new Set(inputHandles.map((h) => h.id)),
2615
+ outputs: new Set(outputHandles.map((h) => h.id)),
2616
+ };
2617
+ const missingHandleLayouts = createMissingHandleLayouts(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount);
2618
+ const handleLayout = [...geom.handleLayout, ...missingHandleLayouts];
2619
+ const missingHandleBounds = createMissingHandleBounds(extraInputs, extraOutputs, finalDims.baseLeftCount, finalDims.baseRightCount, renderWidth);
2620
+ const handles = [...geom.handles, ...missingHandleBounds];
2557
2621
  return {
2558
2622
  id: n.nodeId,
2559
2623
  data: {
@@ -2567,8 +2631,10 @@ function toReactFlow(def, positions, registry, opts) {
2567
2631
  ])),
2568
2632
  handleLayout,
2569
2633
  showValues: opts.showValues,
2570
- renderWidth: initialWidth,
2571
- renderHeight: initialHeight,
2634
+ renderWidth,
2635
+ renderHeight,
2636
+ initialWidth: initialDims.width,
2637
+ initialHeight: initialDims.height,
2572
2638
  inputValues: opts.inputs?.[n.nodeId],
2573
2639
  inputDefaults: opts.inputDefaults?.[n.nodeId],
2574
2640
  outputValues: opts.outputs?.[n.nodeId],
@@ -2586,11 +2652,13 @@ function toReactFlow(def, positions, registry, opts) {
2586
2652
  selected: opts.selectedNodeIds
2587
2653
  ? opts.selectedNodeIds.has(n.nodeId)
2588
2654
  : undefined,
2589
- initialWidth,
2590
- initialHeight,
2655
+ measured: {
2656
+ width: renderWidth,
2657
+ height: renderHeight,
2658
+ },
2591
2659
  handles,
2592
- width: initialWidth,
2593
- height: initialHeight,
2660
+ width: renderWidth,
2661
+ height: renderHeight,
2594
2662
  };
2595
2663
  });
2596
2664
  const edges = def.edges.map((e) => {
@@ -2797,22 +2865,30 @@ async function upload(parsed, wb, runner) {
2797
2865
  /**
2798
2866
  * Merge UI state from source into target, remapping node IDs using nodeIdMap.
2799
2867
  * Preserves target state and adds/updates source state with remapped IDs.
2868
+ * If anchorPos and sourceDef are provided, positions will be offset relative to anchorPos.
2800
2869
  */
2801
- function mergeUIState(targetUI, sourceUI, nodeIdMap) {
2870
+ function mergeUIState(targetUI, sourceUI, nodeIdMap, anchorPos, sourceDef) {
2802
2871
  const result = {
2803
2872
  ...targetUI,
2804
2873
  };
2805
2874
  if (!sourceUI)
2806
2875
  return result;
2807
- // Merge positions (already handled by mergeSnapshotData, but included for completeness)
2876
+ // Merge positions with optional offset
2808
2877
  if (sourceUI.positions) {
2809
- result.positions = {
2810
- ...(targetUI?.positions || {}),
2811
- ...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [
2812
- nodeIdMap[oldId] || oldId,
2813
- pos,
2814
- ])),
2815
- };
2878
+ if (anchorPos && sourceDef) {
2879
+ // Apply offset when anchorPos and sourceDef are provided
2880
+ result.positions = offsetImportedPositions(targetUI?.positions ?? {}, sourceUI.positions, sourceDef, nodeIdMap, anchorPos);
2881
+ }
2882
+ else {
2883
+ // Simple remapping without offset
2884
+ result.positions = {
2885
+ ...(targetUI?.positions || {}),
2886
+ ...Object.fromEntries(Object.entries(sourceUI.positions).map(([oldId, pos]) => [
2887
+ nodeIdMap[oldId] || oldId,
2888
+ pos,
2889
+ ])),
2890
+ };
2891
+ }
2816
2892
  }
2817
2893
  // Merge selection: remap node IDs and edge IDs
2818
2894
  if (sourceUI.selection) {
@@ -2835,6 +2911,15 @@ function mergeUIState(targetUI, sourceUI, nodeIdMap) {
2835
2911
  ])),
2836
2912
  };
2837
2913
  }
2914
+ if (sourceUI.sizes) {
2915
+ result.sizes = {
2916
+ ...(targetUI?.sizes || {}),
2917
+ ...Object.fromEntries(Object.entries(sourceUI.sizes).map(([oldId, size]) => [
2918
+ nodeIdMap[oldId] || oldId,
2919
+ size,
2920
+ ])),
2921
+ };
2922
+ }
2838
2923
  return result;
2839
2924
  }
2840
2925
 
@@ -3092,7 +3177,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3092
3177
  (type === "graphChanged" || type === "graphUiChanged")) {
3093
3178
  const changeType = payload
3094
3179
  .change?.type;
3095
- if (changeType === "moveNode" || changeType === "moveNodes")
3180
+ if (changeType === "moveNode" ||
3181
+ changeType === "moveNodes" ||
3182
+ changeType === "resizeNodes")
3096
3183
  return prev;
3097
3184
  }
3098
3185
  const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
@@ -3501,6 +3588,9 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3501
3588
  else if (changeType === "moveNodes") {
3502
3589
  reason = "move-nodes";
3503
3590
  }
3591
+ else if (changeType === "resizeNodes") {
3592
+ reason = "resize-nodes";
3593
+ }
3504
3594
  else if (changeType === "selection") {
3505
3595
  reason = "selection";
3506
3596
  }
@@ -4258,103 +4348,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
4258
4348
  }, 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 }) }))] }));
4259
4349
  }
4260
4350
 
4261
- function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
4262
- 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: {
4263
- top: (y ?? 0) - 8,
4264
- ...(kind === "input"
4265
- ? { right: "50%" }
4266
- : { left: "50%", textAlign: "right" }),
4267
- whiteSpace: "nowrap",
4268
- overflow: "hidden",
4269
- textOverflow: "ellipsis",
4270
- }, children: renderLabel({ kind, id }) }))] }));
4271
- }
4272
- function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
4273
- const layout = data.handleLayout ?? [];
4274
- const byId = React.useMemo(() => {
4275
- const m = new Map();
4276
- for (const h of layout) {
4277
- // Prefer namespaced key to disambiguate inputs/outputs that share id
4278
- m.set(`${h.type}:${h.id}`, {
4279
- position: h.position,
4280
- y: h.y,
4281
- type: h.type,
4282
- missing: h.missing,
4283
- });
4284
- // Back-compat: also store by id-only if not already set
4285
- if (!m.has(h.id))
4286
- m.set(h.id, {
4287
- position: h.position,
4288
- y: h.y,
4289
- type: h.type,
4290
- missing: h.missing,
4291
- });
4292
- }
4293
- return m;
4294
- }, [layout]);
4295
- const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
4296
- const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
4297
- const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
4298
- const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
4299
- return (jsxs(Fragment, { children: [(data.inputHandles ?? []).map((h) => {
4300
- const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
4301
- const position = placed?.position ?? Position.Left;
4302
- const y = placed?.y;
4303
- const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
4304
- return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
4305
- }), missingInputs.map((h) => {
4306
- const key = `missing-input:${h.id}`;
4307
- const position = h.position ?? Position.Left;
4308
- const y = h.y;
4309
- const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
4310
- 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));
4311
- }), (data.outputHandles ?? []).map((h) => {
4312
- const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
4313
- const position = placed?.position ?? Position.Right;
4314
- const y = placed?.y;
4315
- const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
4316
- return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
4317
- }), missingOutputs.map((h) => {
4318
- const key = `missing-output:${h.id}`;
4319
- const position = h.position ?? Position.Right;
4320
- const y = h.y;
4321
- const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
4322
- return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
4323
- })] }));
4324
- }
4325
-
4326
- const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
4327
- const updateNodeInternals = useUpdateNodeInternals();
4328
- const { typeId, showValues } = data;
4329
- const inputEntries = data.inputHandles ?? [];
4330
- const outputEntries = data.outputHandles ?? [];
4331
- React.useEffect(() => {
4332
- updateNodeInternals(id);
4333
- }, [
4334
- id,
4335
- inputEntries.length,
4336
- outputEntries.length,
4337
- showValues,
4338
- updateNodeInternals,
4339
- ]);
4340
- const status = data.status ?? { activeRuns: 0 };
4341
- const validation = data.validation ?? {
4342
- inputs: [],
4343
- outputs: [],
4344
- issues: [],
4345
- };
4346
- const containerBorder = getNodeBorderClassNames({
4347
- selected,
4348
- status,
4349
- validation,
4350
- });
4351
- return (jsxs("div", { className: cx("rounded-lg bg-white/50 !dark:bg-stone-900", containerBorder), style: {
4352
- position: "relative",
4353
- minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
4354
- minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
4355
- }, children: [jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
4356
- });
4357
- DefaultNode.displayName = "DefaultNode";
4358
4351
  function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInvalidate, }) {
4359
4352
  const ctx = useWorkbenchContext();
4360
4353
  const [isEditing, setIsEditing] = React.useState(false);
@@ -4438,6 +4431,72 @@ function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInv
4438
4431
  .map((v) => `${v.code}: ${v.message}`)
4439
4432
  .join("; ") })), showId && jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
4440
4433
  }
4434
+
4435
+ function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
4436
+ 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: {
4437
+ top: (y ?? 0) - 8,
4438
+ ...(kind === "input"
4439
+ ? { right: "50%" }
4440
+ : { left: "50%", textAlign: "right" }),
4441
+ whiteSpace: "nowrap",
4442
+ overflow: "hidden",
4443
+ textOverflow: "ellipsis",
4444
+ }, children: renderLabel({ kind, id }) }))] }));
4445
+ }
4446
+ function NodeHandles({ data, isConnectable, getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
4447
+ const layout = data.handleLayout ?? [];
4448
+ const byId = React.useMemo(() => {
4449
+ const m = new Map();
4450
+ for (const h of layout) {
4451
+ // Prefer namespaced key to disambiguate inputs/outputs that share id
4452
+ m.set(`${h.type}:${h.id}`, {
4453
+ position: h.position,
4454
+ y: h.y,
4455
+ type: h.type,
4456
+ missing: h.missing,
4457
+ });
4458
+ // Back-compat: also store by id-only if not already set
4459
+ if (!m.has(h.id))
4460
+ m.set(h.id, {
4461
+ position: h.position,
4462
+ y: h.y,
4463
+ type: h.type,
4464
+ missing: h.missing,
4465
+ });
4466
+ }
4467
+ return m;
4468
+ }, [layout]);
4469
+ const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
4470
+ const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
4471
+ const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
4472
+ const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
4473
+ return (jsxs(Fragment, { children: [(data.inputHandles ?? []).map((h) => {
4474
+ const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
4475
+ const position = placed?.position ?? Position.Left;
4476
+ const y = placed?.y;
4477
+ const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ?? "";
4478
+ return (jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
4479
+ }), missingInputs.map((h) => {
4480
+ const key = `missing-input:${h.id}`;
4481
+ const position = h.position ?? Position.Left;
4482
+ const y = h.y;
4483
+ const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 wb-nodrag wb-nowheel";
4484
+ 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));
4485
+ }), (data.outputHandles ?? []).map((h) => {
4486
+ const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
4487
+ const position = placed?.position ?? Position.Right;
4488
+ const y = placed?.y;
4489
+ const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ?? "";
4490
+ return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
4491
+ }), missingOutputs.map((h) => {
4492
+ const key = `missing-output:${h.id}`;
4493
+ const position = h.position ?? Position.Right;
4494
+ const y = h.y;
4495
+ const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
4496
+ return (jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
4497
+ })] }));
4498
+ }
4499
+
4441
4500
  function DefaultNodeContent({ data, isConnectable, }) {
4442
4501
  const { showValues, inputValues, inputDefaults, outputValues, toString } = data;
4443
4502
  const inputEntries = data.inputHandles ?? [];
@@ -4490,6 +4549,24 @@ function DefaultNodeContent({ data, isConnectable, }) {
4490
4549
  } })] }));
4491
4550
  }
4492
4551
 
4552
+ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
4553
+ const nodeRef = useRef(null);
4554
+ const { typeId } = data;
4555
+ const status = data.status ?? { activeRuns: 0 };
4556
+ const validation = data.validation ?? {
4557
+ inputs: [],
4558
+ outputs: [],
4559
+ issues: [],
4560
+ };
4561
+ const containerBorder = getNodeBorderClassNames({
4562
+ selected,
4563
+ status,
4564
+ validation,
4565
+ });
4566
+ 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 })] }));
4567
+ });
4568
+ DefaultNode.displayName = "DefaultNode";
4569
+
4493
4570
  // Helper to format shortcut for current platform
4494
4571
  function formatShortcut(shortcut) {
4495
4572
  const isMac = typeof navigator !== "undefined" &&
@@ -4770,8 +4847,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4770
4847
  position: n.position,
4771
4848
  type: n.type,
4772
4849
  selected: n.selected,
4773
- initialWidth: n.initialWidth,
4774
- initialHeight: n.initialHeight,
4850
+ measured: n.measured,
4775
4851
  data: n.data && {
4776
4852
  typeId: n.data.typeId,
4777
4853
  inputHandles: n.data.inputHandles,
@@ -4805,11 +4881,18 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4805
4881
  useImperativeHandle(ref, () => ({
4806
4882
  fitView: () => {
4807
4883
  try {
4808
- rfInstanceRef.current?.fitView({ padding: 0.2 });
4884
+ rfInstanceRef.current?.fitView();
4885
+ }
4886
+ catch (err) {
4887
+ console.warn("Failed to fit view", err);
4809
4888
  }
4810
- catch { }
4811
4889
  },
4812
- }));
4890
+ setViewport: (viewport) => {
4891
+ if (rfInstanceRef.current) {
4892
+ rfInstanceRef.current.setViewport(lod.clone(viewport));
4893
+ }
4894
+ },
4895
+ }), []);
4813
4896
  const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
4814
4897
  const ui = wb.getUI();
4815
4898
  const { nodeTypes, resolveNodeType } = useMemo(() => {
@@ -4855,7 +4938,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4855
4938
  inputsWithDefaults[n.nodeId] = merged;
4856
4939
  }
4857
4940
  }
4858
- const out = toReactFlow(wb.def, wb.getPositions(), registry, {
4941
+ const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), registry, {
4859
4942
  showValues,
4860
4943
  inputs: inputsWithDefaults,
4861
4944
  inputDefaults: inputDefaultsMap,
@@ -5327,55 +5410,53 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
5327
5410
  showToast,
5328
5411
  ]);
5329
5412
  // Get custom renderers from UI extension registry (reactive to uiVersion changes)
5330
- const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
5413
+ const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, SelectionContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
5331
5414
  return {
5332
5415
  BackgroundRenderer: ui.getBackgroundRenderer(),
5333
5416
  MinimapRenderer: ui.getMinimapRenderer(),
5334
5417
  ControlsRenderer: ui.getControlsRenderer(),
5335
5418
  DefaultContextMenuRenderer: ui.getDefaultContextMenuRenderer(),
5336
5419
  NodeContextMenuRenderer: ui.getNodeContextMenuRenderer(),
5420
+ SelectionContextMenuRenderer: ui.getSelectionContextMenuRenderer(),
5337
5421
  connectionLineRenderer: ui.getConnectionLineRenderer(),
5338
5422
  };
5339
5423
  }, [ui, uiVersion]);
5340
5424
  const onMoveEnd = useCallback(() => {
5341
5425
  if (rfInstanceRef.current) {
5342
5426
  const viewport = rfInstanceRef.current.getViewport();
5343
- const viewportData = lod.pick(viewport, ["x", "y", "zoom"]);
5427
+ const viewportData = lod.clone(viewport);
5344
5428
  wb.setViewport(viewportData);
5345
5429
  }
5346
5430
  }, [wb]);
5347
- const viewportRef = useRef(null);
5431
+ // Sync viewport when workbench fires graphUiChanged with viewport event
5348
5432
  useEffect(() => {
5349
- if (!rfInstanceRef.current)
5350
- return;
5351
- const currentViewport = wb.getViewport();
5352
- if (currentViewport &&
5353
- (!viewportRef.current ||
5354
- viewportRef.current.x !== currentViewport.x ||
5355
- viewportRef.current.y !== currentViewport.y ||
5356
- viewportRef.current.zoom !== currentViewport.zoom)) {
5357
- viewportRef.current = currentViewport;
5358
- rfInstanceRef.current.setViewport({
5359
- x: currentViewport.x,
5360
- y: currentViewport.y,
5361
- zoom: currentViewport.zoom,
5362
- });
5363
- }
5364
- });
5433
+ const off = wb.on("graphUiChanged", (event) => {
5434
+ if (event.change?.type === "viewport" &&
5435
+ rfInstanceRef.current &&
5436
+ event.init) {
5437
+ const viewport = wb.getViewport();
5438
+ if (viewport) {
5439
+ rfInstanceRef.current.setViewport(lod.clone(viewport));
5440
+ }
5441
+ }
5442
+ });
5443
+ return () => off();
5444
+ }, [wb]);
5365
5445
  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) => {
5366
5446
  rfInstanceRef.current = inst;
5367
5447
  const savedViewport = wb.getViewport();
5368
5448
  if (savedViewport) {
5369
- viewportRef.current = savedViewport;
5370
- inst.setViewport(lod.pick(savedViewport, ["x", "y", "zoom"]));
5449
+ inst.setViewport(lod.clone(savedViewport));
5371
5450
  }
5372
- }, 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, ...(enableKeyboardShortcuts !== false
5451
+ }, 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
5373
5452
  ? { enableKeyboardShortcuts, keyboardShortcuts }
5374
5453
  : {}) })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
5375
5454
  nodeContextMenuHandlers &&
5376
5455
  (NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
5377
5456
  ? { enableKeyboardShortcuts, keyboardShortcuts }
5378
- : {}) })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen && selectionMenuPos && (jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))] }) }), toast && (jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
5457
+ : {}) })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), selectionMenuOpen &&
5458
+ selectionMenuPos &&
5459
+ (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))] }));
5379
5460
  });
5380
5461
 
5381
5462
  function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {