@bian-womp/spark-workbench 0.2.73 → 0.2.75

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 +257 -47
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/core/InMemoryWorkbench.d.ts +0 -3
  4. package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
  5. package/lib/cjs/src/index.d.ts +1 -0
  6. package/lib/cjs/src/index.d.ts.map +1 -1
  7. package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts +16 -0
  8. package/lib/cjs/src/misc/KeyboardShortcutToast.d.ts.map +1 -0
  9. package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  10. package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +3 -11
  11. package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  12. package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  13. package/lib/cjs/src/misc/context-menu/ContextMenuHandlers.d.ts +5 -0
  14. package/lib/cjs/src/misc/context-menu/ContextMenuHandlers.d.ts.map +1 -1
  15. package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts +1 -1
  16. package/lib/cjs/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
  17. package/lib/cjs/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
  18. package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
  19. package/lib/cjs/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
  20. package/lib/cjs/src/misc/load.d.ts.map +1 -1
  21. package/lib/cjs/src/misc/viewport-utils.d.ts +9 -0
  22. package/lib/cjs/src/misc/viewport-utils.d.ts.map +1 -0
  23. package/lib/cjs/src/runtime/IGraphRunner.d.ts +13 -1
  24. package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
  25. package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +5 -0
  26. package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  27. package/lib/esm/index.js +256 -48
  28. package/lib/esm/index.js.map +1 -1
  29. package/lib/esm/src/core/InMemoryWorkbench.d.ts +0 -3
  30. package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
  31. package/lib/esm/src/index.d.ts +1 -0
  32. package/lib/esm/src/index.d.ts.map +1 -1
  33. package/lib/esm/src/misc/KeyboardShortcutToast.d.ts +16 -0
  34. package/lib/esm/src/misc/KeyboardShortcutToast.d.ts.map +1 -0
  35. package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
  36. package/lib/esm/src/misc/context/WorkbenchContext.d.ts +3 -11
  37. package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
  38. package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
  39. package/lib/esm/src/misc/context-menu/ContextMenuHandlers.d.ts +5 -0
  40. package/lib/esm/src/misc/context-menu/ContextMenuHandlers.d.ts.map +1 -1
  41. package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts +1 -1
  42. package/lib/esm/src/misc/context-menu/ContextMenuHelpers.d.ts.map +1 -1
  43. package/lib/esm/src/misc/context-menu/DefaultContextMenu.d.ts.map +1 -1
  44. package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
  45. package/lib/esm/src/misc/context-menu/SelectionContextMenu.d.ts.map +1 -1
  46. package/lib/esm/src/misc/load.d.ts.map +1 -1
  47. package/lib/esm/src/misc/viewport-utils.d.ts +9 -0
  48. package/lib/esm/src/misc/viewport-utils.d.ts.map +1 -0
  49. package/lib/esm/src/runtime/IGraphRunner.d.ts +13 -1
  50. package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
  51. package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +5 -0
  52. package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
  53. package/package.json +4 -4
package/lib/esm/index.js CHANGED
@@ -6,7 +6,6 @@ import React, { useCallback, useState, useRef, useEffect, useMemo, createContext
6
6
  import cx from 'classnames';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { XCircleIcon, WarningCircleIcon, CopyIcon, TrashIcon, XIcon, ArrowClockwiseIcon, ClockClockwiseIcon, StopIcon, PlayIcon, PlugsConnectedIcon, WifiHighIcon, WifiSlashIcon, PlayPauseIcon, LightningIcon, TreeStructureIcon, CornersOutIcon, DownloadIcon, UploadIcon, BugBeetleIcon, ListBulletsIcon } from '@phosphor-icons/react';
9
- import isEqual from 'lodash/isEqual';
10
9
 
11
10
  class DefaultUIExtensionRegistry {
12
11
  constructor() {
@@ -278,7 +277,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
278
277
  }
279
278
  // Position and selection APIs for React Flow bridge
280
279
  setPositions(map, options) {
281
- this.positions = { ...map };
280
+ this.positions = { ...this.positions, ...map };
282
281
  this.emit("graphUiChanged", {
283
282
  def: this.def,
284
283
  change: { type: "moveNodes" },
@@ -321,12 +320,13 @@ class InMemoryWorkbench extends AbstractWorkbench {
321
320
  // Clear selection
322
321
  this.setSelection({ nodes: [], edges: [] }, options);
323
322
  }
324
- setViewport(viewport, options) {
323
+ setViewport(viewport) {
324
+ if (lod.isEqual(this.viewport, viewport))
325
+ return;
325
326
  this.viewport = { ...viewport };
326
327
  this.emit("graphUiChanged", {
327
328
  def: this.def,
328
329
  change: { type: "viewport" },
329
- ...options,
330
330
  });
331
331
  }
332
332
  getViewport() {
@@ -969,6 +969,24 @@ class LocalGraphRunner extends AbstractGraphRunner {
969
969
  }
970
970
  }
971
971
 
972
+ function isValidViewport(viewport) {
973
+ return (viewport !== null &&
974
+ typeof viewport === "object" &&
975
+ "x" in viewport &&
976
+ "y" in viewport &&
977
+ "zoom" in viewport &&
978
+ typeof viewport.x === "number" &&
979
+ typeof viewport.y === "number" &&
980
+ typeof viewport.zoom === "number");
981
+ }
982
+ function excludeViewportFromUIState(uiState) {
983
+ if (!uiState) {
984
+ return {};
985
+ }
986
+ const { viewport: _ignoredViewport, ...rest } = uiState;
987
+ return rest;
988
+ }
989
+
972
990
  // Counter for generating readable runner IDs
973
991
  let remoteRunnerCounter = 0;
974
992
  class RemoteGraphRunner extends AbstractGraphRunner {
@@ -1140,9 +1158,26 @@ class RemoteGraphRunner extends AbstractGraphRunner {
1140
1158
  this.clientPromise = (async () => {
1141
1159
  // Build client config from backend config
1142
1160
  const clientConfig = this.buildClientConfig(backend);
1143
- // Create client with custom event handler if provided
1161
+ // Wrap custom event handler to intercept flow-viewport events and emit viewport event
1162
+ const wrappedOnCustomEvent = (event) => {
1163
+ const msg = event?.message;
1164
+ if (msg &&
1165
+ typeof msg === "object" &&
1166
+ "type" in msg &&
1167
+ msg.type === "flow-viewport") {
1168
+ const viewport = msg.payload?.viewport;
1169
+ if (isValidViewport(viewport)) {
1170
+ this.emit("viewport", { viewport });
1171
+ }
1172
+ }
1173
+ // Call original handler if provided
1174
+ if (backend.onCustomEvent) {
1175
+ backend.onCustomEvent(event);
1176
+ }
1177
+ };
1178
+ // Create client with wrapped custom event handler
1144
1179
  const client = new RuntimeApiClient(clientConfig, {
1145
- onCustomEvent: backend.onCustomEvent,
1180
+ onCustomEvent: wrappedOnCustomEvent,
1146
1181
  runnerId: this.runnerId,
1147
1182
  });
1148
1183
  // Setup event subscriptions
@@ -1417,6 +1452,20 @@ class RemoteGraphRunner extends AbstractGraphRunner {
1417
1452
  const client = await this.ensureClient();
1418
1453
  await client.copyOutputs(fromNodeId, toNodeId, options);
1419
1454
  }
1455
+ async setViewport(viewport) {
1456
+ const client = await this.ensureClient();
1457
+ const transport = client.transport;
1458
+ if (transport && transport.send) {
1459
+ transport.send({
1460
+ seq: Date.now(),
1461
+ ts: Date.now(),
1462
+ message: {
1463
+ type: "SetViewport",
1464
+ payload: { viewport },
1465
+ },
1466
+ });
1467
+ }
1468
+ }
1420
1469
  triggerExternal(nodeId, event, options) {
1421
1470
  // If engine exists, call directly; otherwise ensure client (fire-and-forget)
1422
1471
  if (this.engine) {
@@ -2434,7 +2483,8 @@ function isSnapshotPayload(parsed) {
2434
2483
  async function download(wb, runner) {
2435
2484
  try {
2436
2485
  const def = wb.export();
2437
- const uiState = wb.getUIState();
2486
+ const fullUiState = wb.getUIState();
2487
+ const uiState = excludeViewportFromUIState(fullUiState);
2438
2488
  const runtimeState = wb.getRuntimeState();
2439
2489
  let snapshot;
2440
2490
  if (runner.isRunning()) {
@@ -2444,7 +2494,7 @@ async function download(wb, runner) {
2444
2494
  def,
2445
2495
  extData: {
2446
2496
  ...(fullSnapshot.extData || {}),
2447
- ui: uiState,
2497
+ ui: Object.keys(uiState || {}).length > 0 ? uiState : undefined,
2448
2498
  runtime: runtimeState || undefined,
2449
2499
  },
2450
2500
  };
@@ -2456,7 +2506,10 @@ async function download(wb, runner) {
2456
2506
  inputs,
2457
2507
  outputs: {},
2458
2508
  environment: {},
2459
- extData: { ui: uiState, runtime: runtimeState || undefined },
2509
+ extData: {
2510
+ ui: Object.keys(uiState || {}).length > 0 ? uiState : undefined,
2511
+ runtime: runtimeState || undefined,
2512
+ },
2460
2513
  };
2461
2514
  }
2462
2515
  downloadJSON(snapshot, `spark-snapshot-${generateTimestamp()}.json`);
@@ -2480,7 +2533,8 @@ async function upload(parsed, wb, runner) {
2480
2533
  }
2481
2534
  await wb.load(def);
2482
2535
  if (extData.ui && typeof extData.ui === "object") {
2483
- wb.setUIState(extData.ui);
2536
+ const uiWithoutViewport = excludeViewportFromUIState(extData.ui);
2537
+ wb.setUIState(uiWithoutViewport);
2484
2538
  }
2485
2539
  if (extData.runtime && typeof extData.runtime === "object") {
2486
2540
  wb.setRuntimeState(extData.runtime);
@@ -2727,11 +2781,12 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
2727
2781
  }
2728
2782
  // Save cleaned metadata to workbench state
2729
2783
  wb.setRuntimeState(metadata);
2730
- // Get UI state
2731
- const uiState = wb.getUIState();
2732
- // Save both runtime and UI state to extData (merge to preserve both)
2784
+ const fullUiState = wb.getUIState();
2785
+ const uiWithoutViewport = excludeViewportFromUIState(fullUiState);
2733
2786
  await runner.setExtData?.({
2734
- ...(uiState ? { ui: uiState } : {}),
2787
+ ...(Object.keys(uiWithoutViewport || {}).length > 0
2788
+ ? { ui: uiWithoutViewport }
2789
+ : {}),
2735
2790
  runtime: metadata,
2736
2791
  });
2737
2792
  }
@@ -3165,6 +3220,16 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3165
3220
  }
3166
3221
  });
3167
3222
  const offWbGraphUiChanged = wb.on("graphUiChanged", async (event) => {
3223
+ // Handle viewport changes separately (send via SetViewport command, not commit)
3224
+ if (event.change?.type === "viewport") {
3225
+ const viewport = wb.getViewport();
3226
+ if (viewport && runner.setViewport) {
3227
+ runner.setViewport(viewport).catch((err) => {
3228
+ console.warn("[WorkbenchContext] Failed to send viewport update:", err);
3229
+ });
3230
+ }
3231
+ return;
3232
+ }
3168
3233
  // Only commit if commit flag is true (e.g., drag end, not during dragging)
3169
3234
  if (event.commit) {
3170
3235
  // Build detailed reason from change type
@@ -3180,9 +3245,6 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3180
3245
  else if (changeType === "selection") {
3181
3246
  reason = "selection";
3182
3247
  }
3183
- else if (changeType === "viewport") {
3184
- reason = "viewport";
3185
- }
3186
3248
  }
3187
3249
  await saveUiRuntimeMetadata();
3188
3250
  const history = await runner
@@ -3214,7 +3276,12 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3214
3276
  console.error("Failed to handle registry changed event");
3215
3277
  }
3216
3278
  });
3217
- // Handle transport changes: reset runtime status when connection is lost
3279
+ const offFlowViewport = runner.on("viewport", (event) => {
3280
+ const viewport = event.viewport;
3281
+ if (isValidViewport(viewport)) {
3282
+ wb.setViewport(viewport);
3283
+ }
3284
+ });
3218
3285
  const offRunnerTransport = runner.on("transport", (t) => {
3219
3286
  if (t.state === "disconnected") {
3220
3287
  console.info("[WorkbenchContext] Transport disconnected, resetting node status");
@@ -3254,6 +3321,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
3254
3321
  offWbSelectionChanged();
3255
3322
  offRunnerRegistry();
3256
3323
  offRunnerTransport();
3324
+ offFlowViewport();
3257
3325
  };
3258
3326
  }, [runner, wb, setRegistry]);
3259
3327
  const isRunning = useCallback(() => runner.isRunning(), [runner]);
@@ -3612,6 +3680,47 @@ function createNodeCopyHandler(wb, runner, nodeId, getDefaultNodeSize, onCopyRes
3612
3680
  * Creates base selection context menu handlers.
3613
3681
  */
3614
3682
  function createSelectionContextMenuHandlers(wb, onClose, getDefaultNodeSize, onCopyResult, runner) {
3683
+ const onDuplicate = runner
3684
+ ? () => {
3685
+ const selection = wb.getSelection();
3686
+ if (selection.nodes.length === 0) {
3687
+ onClose();
3688
+ return;
3689
+ }
3690
+ const def = wb.export();
3691
+ const positions = wb.getPositions();
3692
+ const newNodes = [];
3693
+ // Duplicate each selected node
3694
+ for (const nodeId of selection.nodes) {
3695
+ const n = def.nodes.find((n) => n.nodeId === nodeId);
3696
+ if (!n)
3697
+ continue;
3698
+ const pos = positions[nodeId] || { x: 0, y: 0 };
3699
+ // Get inputs without bindings (literal values only)
3700
+ const allInputs = runner.getInputs(def)[nodeId] || {};
3701
+ const inboundHandles = new Set(def.edges
3702
+ .filter((e) => e.target.nodeId === nodeId)
3703
+ .map((e) => e.target.handle));
3704
+ const inputsWithoutBindings = Object.fromEntries(Object.entries(allInputs).filter(([handle]) => !inboundHandles.has(handle)));
3705
+ const newNodeId = wb.addNode({
3706
+ typeId: n.typeId,
3707
+ params: n.params,
3708
+ position: { x: pos.x + 24, y: pos.y + 24 },
3709
+ resolvedHandles: n.resolvedHandles,
3710
+ }, {
3711
+ inputs: inputsWithoutBindings,
3712
+ copyOutputsFrom: nodeId,
3713
+ dry: true,
3714
+ });
3715
+ newNodes.push(newNodeId);
3716
+ }
3717
+ // Select all newly duplicated nodes
3718
+ if (newNodes.length > 0) {
3719
+ wb.setSelection({ nodes: newNodes, edges: [] }, { commit: true, reason: "duplicate-selection" });
3720
+ }
3721
+ onClose();
3722
+ }
3723
+ : undefined;
3615
3724
  return {
3616
3725
  onCopy: runner
3617
3726
  ? createCopyHandler(wb, runner, getDefaultNodeSize, onCopyResult)
@@ -3623,13 +3732,14 @@ function createSelectionContextMenuHandlers(wb, onClose, getDefaultNodeSize, onC
3623
3732
  wb.deleteSelection({ commit: true, reason: "delete-selection" });
3624
3733
  onClose();
3625
3734
  },
3735
+ onDuplicate,
3626
3736
  onClose,
3627
3737
  };
3628
3738
  }
3629
3739
  /**
3630
3740
  * Creates base default context menu handlers.
3631
3741
  */
3632
- function createDefaultContextMenuHandlers(onAddNode, onClose, onPaste, runner, getCopiedData, clearCopiedData, history) {
3742
+ function createDefaultContextMenuHandlers(onAddNode, onClose, onPaste, runner, getCopiedData, clearCopiedData, history, wb) {
3633
3743
  // Wrap paste handler to clear storage after paste
3634
3744
  const wrappedOnPaste = onPaste && getCopiedData && clearCopiedData
3635
3745
  ? (position) => {
@@ -3640,12 +3750,22 @@ function createDefaultContextMenuHandlers(onAddNode, onClose, onPaste, runner, g
3640
3750
  const hasPasteData = getCopiedData ? () => !!getCopiedData() : undefined;
3641
3751
  const canUndo = history ? history.undoCount > 0 : undefined;
3642
3752
  const canRedo = history ? history.redoCount > 0 : undefined;
3753
+ const onSelectAll = wb
3754
+ ? () => {
3755
+ const def = wb.export();
3756
+ const allNodeIds = def.nodes.map((n) => n.nodeId);
3757
+ const allEdgeIds = def.edges.map((e) => e.id);
3758
+ wb.setSelection({ nodes: allNodeIds, edges: allEdgeIds }, { commit: true, reason: "select-all" });
3759
+ onClose();
3760
+ }
3761
+ : undefined;
3643
3762
  return {
3644
3763
  onAddNode,
3645
3764
  onPaste: wrappedOnPaste,
3646
3765
  hasPasteData,
3647
3766
  onUndo: runner ? () => runner.undo().then(() => onClose()) : undefined,
3648
3767
  onRedo: runner ? () => runner.redo().then(() => onClose()) : undefined,
3768
+ onSelectAll,
3649
3769
  canUndo,
3650
3770
  canRedo,
3651
3771
  onClose,
@@ -4185,6 +4305,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
4185
4305
  undo: "⌘/Ctrl + Z",
4186
4306
  redo: "⌘/Ctrl + Shift + Z",
4187
4307
  paste: "⌘/Ctrl + V",
4308
+ selectAll: "⌘/Ctrl + A",
4188
4309
  }, }) {
4189
4310
  const rf = useReactFlow();
4190
4311
  const [query, setQuery] = useState("");
@@ -4287,7 +4408,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
4287
4408
  return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
4288
4409
  e.preventDefault();
4289
4410
  e.stopPropagation();
4290
- }, children: [hasPasteData && handlers.onPaste && (jsx(ContextMenuButton, { label: "Paste", onClick: handlePaste, shortcut: keyboardShortcuts.paste, enableKeyboardShortcuts: enableKeyboardShortcuts })), (handlers.onUndo || handlers.onRedo) && (jsxs(Fragment, { children: [hasPasteData && handlers.onPaste && (jsx("div", { className: "h-px bg-gray-200 my-1" })), handlers.onUndo && (jsx(ContextMenuButton, { label: "Undo", onClick: handlers.onUndo, disabled: !canUndo, shortcut: keyboardShortcuts.undo, enableKeyboardShortcuts: enableKeyboardShortcuts })), handlers.onRedo && (jsx(ContextMenuButton, { label: "Redo", onClick: handlers.onRedo, disabled: !canRedo, shortcut: keyboardShortcuts.redo, enableKeyboardShortcuts: enableKeyboardShortcuts }))] })), hasPasteData &&
4411
+ }, children: [hasPasteData && handlers.onPaste && (jsx(ContextMenuButton, { label: "Paste", onClick: handlePaste, shortcut: keyboardShortcuts.paste, enableKeyboardShortcuts: enableKeyboardShortcuts })), (handlers.onUndo || handlers.onRedo || handlers.onSelectAll) && (jsxs(Fragment, { children: [hasPasteData && handlers.onPaste && (jsx("div", { className: "h-px bg-gray-200 my-1" })), handlers.onSelectAll && (jsx(ContextMenuButton, { label: "Select All", onClick: handlers.onSelectAll, shortcut: keyboardShortcuts.selectAll, enableKeyboardShortcuts: enableKeyboardShortcuts })), handlers.onSelectAll && (handlers.onUndo || handlers.onRedo) && (jsx("div", { className: "h-px bg-gray-200 my-1" })), handlers.onUndo && (jsx(ContextMenuButton, { label: "Undo", onClick: handlers.onUndo, disabled: !canUndo, shortcut: keyboardShortcuts.undo, enableKeyboardShortcuts: enableKeyboardShortcuts })), handlers.onRedo && (jsx(ContextMenuButton, { label: "Redo", onClick: handlers.onRedo, disabled: !canRedo, shortcut: keyboardShortcuts.redo, enableKeyboardShortcuts: enableKeyboardShortcuts })), (handlers.onUndo || handlers.onRedo) && (jsx("div", { className: "h-px bg-gray-200 my-1" }))] })), hasPasteData &&
4291
4412
  handlers.onPaste &&
4292
4413
  !handlers.onUndo &&
4293
4414
  !handlers.onRedo && jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 rounded px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
@@ -4295,7 +4416,8 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
4295
4416
 
4296
4417
  function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeableOutputs, enableKeyboardShortcuts = true, keyboardShortcuts = {
4297
4418
  copy: "⌘/Ctrl + C",
4298
- duplicate: "⌘/Ctrl + D",
4419
+ duplicate: "⌘/Ctrl + E",
4420
+ duplicateWithEdges: "⌘/Ctrl + Shift + E",
4299
4421
  delete: "Delete",
4300
4422
  }, }) {
4301
4423
  const ref = useRef(null);
@@ -4335,11 +4457,12 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeab
4335
4457
  return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
4336
4458
  e.preventDefault();
4337
4459
  e.stopPropagation();
4338
- }, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicateWithEdges, children: "Duplicate with edges" }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxs(Fragment, { children: [jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
4460
+ }, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxs(Fragment, { children: [jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
4339
4461
  }
4340
4462
 
4341
4463
  function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {
4342
4464
  copy: "⌘/Ctrl + C",
4465
+ duplicate: "⌘/Ctrl + E",
4343
4466
  delete: "Delete",
4344
4467
  }, }) {
4345
4468
  const ref = useRef(null);
@@ -4379,7 +4502,45 @@ function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcu
4379
4502
  return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
4380
4503
  e.preventDefault();
4381
4504
  e.stopPropagation();
4382
- }, children: [jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Selection" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts })] }));
4505
+ }, children: [jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Selection" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), handlers.onDuplicate && (jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts })), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts })] }));
4506
+ }
4507
+
4508
+ function KeyboardShortcutToast({ message, duration = 1000, onClose, }) {
4509
+ const [isVisible, setIsVisible] = useState(true);
4510
+ const onCloseRef = useRef(onClose);
4511
+ const fadeOutTimerRef = useRef(null);
4512
+ // Keep onClose ref up to date
4513
+ useEffect(() => {
4514
+ onCloseRef.current = onClose;
4515
+ }, [onClose]);
4516
+ useEffect(() => {
4517
+ const timer = setTimeout(() => {
4518
+ setIsVisible(false);
4519
+ // Wait for fade-out animation before calling onClose
4520
+ fadeOutTimerRef.current = setTimeout(() => {
4521
+ onCloseRef.current();
4522
+ }, 300);
4523
+ }, duration);
4524
+ return () => {
4525
+ clearTimeout(timer);
4526
+ if (fadeOutTimerRef.current) {
4527
+ clearTimeout(fadeOutTimerRef.current);
4528
+ }
4529
+ };
4530
+ }, [duration]);
4531
+ return (jsx("div", { className: `fixed top-4 left-1/2 -translate-x-1/2 z-[2000] pointer-events-none transition-opacity duration-300 ${isVisible ? "opacity-100" : "opacity-0"}`, children: jsx("div", { className: "bg-white border border-gray-300 rounded-lg shadow-lg px-2 py-1 text-sm text-gray-700 font-medium whitespace-nowrap", children: message }) }));
4532
+ }
4533
+ // Hook to manage toast state
4534
+ function useKeyboardShortcutToast() {
4535
+ const [toast, setToast] = useState(null);
4536
+ const showToast = (message) => {
4537
+ const id = Date.now();
4538
+ setToast({ message, id });
4539
+ };
4540
+ const hideToast = () => {
4541
+ setToast(null);
4542
+ };
4543
+ return { toast, showToast, hideToast };
4383
4544
  }
4384
4545
 
4385
4546
  const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
@@ -4424,7 +4585,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4424
4585
  inputConnected: n.data.inputConnected,
4425
4586
  },
4426
4587
  });
4427
- return isEqual(pick(a), pick(b));
4588
+ return lod.isEqual(pick(a), pick(b));
4428
4589
  };
4429
4590
  const isSameEdge = (a, b) => {
4430
4591
  const pick = (e) => ({
@@ -4438,7 +4599,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4438
4599
  label: e.label,
4439
4600
  type: e.type,
4440
4601
  });
4441
- return isEqual(pick(a), pick(b));
4602
+ return lod.isEqual(pick(a), pick(b));
4442
4603
  };
4443
4604
  // Expose imperative API
4444
4605
  const rfInstanceRef = useRef(null);
@@ -4754,7 +4915,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4754
4915
  return;
4755
4916
  wb.pasteCopiedData(data, position, { commit: true, reason: "paste" });
4756
4917
  onCloseMenu();
4757
- }, runner, () => storage.get(), () => storage.set(null), historyState);
4918
+ }, runner, () => storage.get(), () => storage.set(null), historyState, wb);
4758
4919
  if (overrides?.getDefaultContextMenuHandlers) {
4759
4920
  return overrides.getDefaultContextMenuHandlers(wb, baseHandlers);
4760
4921
  }
@@ -4819,9 +4980,13 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4819
4980
  redo: "⌘/Ctrl + Shift + Z",
4820
4981
  copy: "⌘/Ctrl + C",
4821
4982
  paste: "⌘/Ctrl + V",
4822
- duplicate: "⌘/Ctrl + D",
4983
+ duplicate: "⌘/Ctrl + E",
4984
+ duplicateWithEdges: "⌘/Ctrl + Shift + E",
4985
+ selectAll: "⌘/Ctrl + A",
4823
4986
  delete: "Delete",
4824
4987
  };
4988
+ // Toast notification for keyboard shortcuts
4989
+ const { toast, showToast, hideToast } = useKeyboardShortcutToast();
4825
4990
  // Keyboard shortcut handler
4826
4991
  useEffect(() => {
4827
4992
  if (!enableKeyboardShortcuts)
@@ -4847,6 +5012,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4847
5012
  defaultContextMenuHandlers.onUndo &&
4848
5013
  defaultContextMenuHandlers.canUndo) {
4849
5014
  if (defaultContextMenuHandlers.canUndo) {
5015
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5016
+ showToast(`Undo (${modKeyLabel} + Z)`);
4850
5017
  defaultContextMenuHandlers.onUndo();
4851
5018
  }
4852
5019
  }
@@ -4860,6 +5027,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4860
5027
  defaultContextMenuHandlers.onRedo &&
4861
5028
  defaultContextMenuHandlers.canRedo) {
4862
5029
  if (defaultContextMenuHandlers.canRedo) {
5030
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5031
+ showToast(`Redo (${modKeyLabel} + Shift + Z)`);
4863
5032
  defaultContextMenuHandlers.onRedo();
4864
5033
  }
4865
5034
  }
@@ -4870,6 +5039,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4870
5039
  const selection = wb.getSelection();
4871
5040
  if (selection.nodes.length > 0 || selection.edges.length > 0) {
4872
5041
  e.preventDefault();
5042
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5043
+ showToast(`Copy (${modKeyLabel} + C)`);
4873
5044
  // If single node selected, use node context menu handler; otherwise use selection handler
4874
5045
  if (selection.nodes.length === 1 && nodeContextMenuHandlers?.onCopy) {
4875
5046
  nodeContextMenuHandlers.onCopy();
@@ -4880,14 +5051,37 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4880
5051
  }
4881
5052
  return;
4882
5053
  }
4883
- // Duplicate: Cmd/Ctrl + D
4884
- if (modKey && key === "d" && !e.shiftKey && !e.altKey) {
5054
+ // Duplicate: Cmd/Ctrl + E
5055
+ if (modKey && key === "e" && !e.shiftKey && !e.altKey) {
4885
5056
  const selection = wb.getSelection();
4886
5057
  if (selection.nodes.length === 1 &&
4887
5058
  nodeContextMenuHandlers?.onDuplicate) {
5059
+ // Single node selected - use node context menu handler
4888
5060
  e.preventDefault();
5061
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5062
+ showToast(`Duplicate (${modKeyLabel} + E)`);
4889
5063
  nodeContextMenuHandlers.onDuplicate();
4890
5064
  }
5065
+ else if (selection.nodes.length > 1 &&
5066
+ selectionContextMenuHandlers.onDuplicate) {
5067
+ // Multiple nodes selected - use selection context menu handler
5068
+ e.preventDefault();
5069
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5070
+ showToast(`Duplicate (${modKeyLabel} + E)`);
5071
+ selectionContextMenuHandlers.onDuplicate();
5072
+ }
5073
+ return;
5074
+ }
5075
+ // Duplicate with edges: Cmd/Ctrl + Shift + E
5076
+ if (modKey && key === "e" && e.shiftKey && !e.altKey) {
5077
+ const selection = wb.getSelection();
5078
+ if (selection.nodes.length === 1 &&
5079
+ nodeContextMenuHandlers?.onDuplicateWithEdges) {
5080
+ e.preventDefault();
5081
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5082
+ showToast(`Duplicate with edges (${modKeyLabel} + Shift + E)`);
5083
+ nodeContextMenuHandlers.onDuplicateWithEdges();
5084
+ }
4891
5085
  return;
4892
5086
  }
4893
5087
  // Paste: Cmd/Ctrl + V
@@ -4898,6 +5092,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4898
5092
  defaultContextMenuHandlers.hasPasteData() &&
4899
5093
  "onPaste" in defaultContextMenuHandlers &&
4900
5094
  defaultContextMenuHandlers.onPaste) {
5095
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5096
+ showToast(`Paste (${modKeyLabel} + V)`);
4901
5097
  const center = rfInstanceRef.current?.screenToFlowPosition({
4902
5098
  x: window.innerWidth / 2,
4903
5099
  y: window.innerHeight / 2,
@@ -4906,6 +5102,16 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4906
5102
  }
4907
5103
  return;
4908
5104
  }
5105
+ // Select All: Cmd/Ctrl + A
5106
+ if (modKey && key === "a" && !e.shiftKey && !e.altKey) {
5107
+ e.preventDefault();
5108
+ if (defaultContextMenuHandlers.onSelectAll) {
5109
+ const modKeyLabel = isMac ? "⌘" : "Ctrl";
5110
+ showToast(`Select All (${modKeyLabel} + A)`);
5111
+ defaultContextMenuHandlers.onSelectAll();
5112
+ }
5113
+ return;
5114
+ }
4909
5115
  // Note: Delete/Backspace is handled by ReactFlow's deleteKeyCode prop
4910
5116
  // which triggers onNodesDelete/onEdgesDelete, so we don't need to handle it here
4911
5117
  };
@@ -4921,6 +5127,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4921
5127
  selectionContextMenuHandlers,
4922
5128
  nodeContextMenuHandlers,
4923
5129
  rfInstanceRef,
5130
+ showToast,
4924
5131
  ]);
4925
5132
  // Get custom renderers from UI extension registry (reactive to uiVersion changes)
4926
5133
  const { BackgroundRenderer, MinimapRenderer, ControlsRenderer, DefaultContextMenuRenderer, NodeContextMenuRenderer, connectionLineRenderer, } = useMemo(() => {
@@ -4936,7 +5143,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4936
5143
  const onMoveEnd = useCallback(() => {
4937
5144
  if (rfInstanceRef.current) {
4938
5145
  const viewport = rfInstanceRef.current.getViewport();
4939
- wb.setViewport({ x: viewport.x, y: viewport.y, zoom: viewport.zoom });
5146
+ const viewportData = lod.pick(viewport, ["x", "y", "zoom"]);
5147
+ wb.setViewport(viewportData);
4940
5148
  }
4941
5149
  }, [wb]);
4942
5150
  const viewportRef = useRef(null);
@@ -4957,24 +5165,24 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
4957
5165
  });
4958
5166
  }
4959
5167
  });
4960
- return (jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsx(ReactFlowProvider, { children: jsxs(ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
4961
- rfInstanceRef.current = inst;
4962
- const savedViewport = wb.getViewport();
4963
- if (savedViewport) {
4964
- viewportRef.current = savedViewport;
4965
- inst.setViewport({
4966
- x: savedViewport.x,
4967
- y: savedViewport.y,
4968
- zoom: savedViewport.zoom,
4969
- });
4970
- }
4971
- }, 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
4972
- ? { enableKeyboardShortcuts, keyboardShortcuts }
4973
- : {}) })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
4974
- nodeContextMenuHandlers &&
4975
- (NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
5168
+ 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) => {
5169
+ rfInstanceRef.current = inst;
5170
+ const savedViewport = wb.getViewport();
5171
+ if (savedViewport) {
5172
+ viewportRef.current = savedViewport;
5173
+ inst.setViewport({
5174
+ x: savedViewport.x,
5175
+ y: savedViewport.y,
5176
+ zoom: savedViewport.zoom,
5177
+ });
5178
+ }
5179
+ }, 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
4976
5180
  ? { enableKeyboardShortcuts, keyboardShortcuts }
4977
- : {}) })) : (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 }))] }) }) }));
5181
+ : {}) })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
5182
+ nodeContextMenuHandlers &&
5183
+ (NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs, ...(enableKeyboardShortcuts !== false
5184
+ ? { enableKeyboardShortcuts, keyboardShortcuts }
5185
+ : {}) })) : (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))] }));
4978
5186
  });
4979
5187
 
4980
5188
  function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
@@ -5511,5 +5719,5 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
5511
5719
  return (jsx(WorkbenchProvider, { wb: wb, runner: runner, registry: registry, setRegistry: setRegistry, overrides: overrides, uiVersion: uiVersion, children: jsx(WorkbenchStudioCanvas, { setRegistry: setRegistry, autoScroll: autoScroll, onAutoScrollChange: onAutoScrollChange, example: example, onExampleChange: onExampleChange, engine: engine, onEngineChange: onEngineChange, backendKind: backendKind, onBackendKindChange: onBackendKindChangeWithDispose, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, overrides: overrides, onInit: onInit, onChange: onChange }) }));
5512
5720
  }
5513
5721
 
5514
- export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, createCopyHandler, createDefaultContextMenuHandlers, createHandleBounds, createHandleLayout, createNodeContextMenuHandlers, createNodeCopyHandler, createSelectionContextMenuHandlers, download, estimateNodeSize, formatDataUrlAsLabel, formatDeclaredTypeSignature, getBakeableOutputs, getHandleBoundsX, getHandleBoundsY, getHandleClassName, getHandleLayoutY, getNodeBorderClassNames, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, upload, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
5722
+ export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, createCopyHandler, createDefaultContextMenuHandlers, createHandleBounds, createHandleLayout, createNodeContextMenuHandlers, createNodeCopyHandler, createSelectionContextMenuHandlers, download, estimateNodeSize, excludeViewportFromUIState, formatDataUrlAsLabel, formatDeclaredTypeSignature, getBakeableOutputs, getHandleBoundsX, getHandleBoundsY, getHandleClassName, getHandleLayoutY, getNodeBorderClassNames, isValidViewport, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, upload, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
5515
5723
  //# sourceMappingURL=index.js.map