@bian-womp/spark-workbench 0.3.3 → 0.3.4

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 CHANGED
@@ -5518,7 +5518,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
5518
5518
  !handlers.onRedo && jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxRuntime.jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsxRuntime.jsx("div", { className: "px-2 pb-1", children: jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 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() }) }), jsxRuntime.jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsxRuntime.jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
5519
5519
  }
5520
5520
 
5521
- function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, enableKeyboardShortcuts = true, keyboardShortcuts = {
5521
+ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, wb, enableKeyboardShortcuts = true, keyboardShortcuts = {
5522
5522
  copy: "⌘/Ctrl + C",
5523
5523
  duplicate: "⌘/Ctrl + E",
5524
5524
  duplicateWithEdges: "⌘/Ctrl + Shift + E",
@@ -5552,6 +5552,10 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
5552
5552
  }, [open]);
5553
5553
  if (!open || !clientPos || !nodeId)
5554
5554
  return null;
5555
+ // Determine if this is a start node (no inbound edges)
5556
+ const isStartNode = wb
5557
+ ? !wb.def.edges.some((e) => e.target.nodeId === nodeId)
5558
+ : false;
5555
5559
  // clamp
5556
5560
  const MENU_MIN_WIDTH = 180;
5557
5561
  const PADDING = 16;
@@ -5561,7 +5565,7 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
5561
5565
  return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
5562
5566
  e.preventDefault();
5563
5567
  e.stopPropagation();
5564
- }, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.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 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
5568
+ }, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: isStartNode ? "Run workflow" : "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.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 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
5565
5569
  }
5566
5570
 
5567
5571
  function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {
@@ -5647,7 +5651,56 @@ function useKeyboardShortcutToast() {
5647
5651
  return { toast, showToast, hideToast };
5648
5652
  }
5649
5653
 
5650
- const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
5654
+ const SelectionBoundOverlay = ({ selection, rfInstance }) => {
5655
+ const selectionBounds = React.useMemo(() => {
5656
+ if (typeof document === "undefined" ||
5657
+ !rfInstance ||
5658
+ selection.nodes.length < 2) {
5659
+ return null;
5660
+ }
5661
+ let bounds = null;
5662
+ for (const nodeId of selection.nodes) {
5663
+ const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
5664
+ if (!el)
5665
+ continue;
5666
+ const rect = el.getBoundingClientRect();
5667
+ if (!bounds) {
5668
+ bounds = {
5669
+ left: rect.left,
5670
+ top: rect.top,
5671
+ right: rect.right,
5672
+ bottom: rect.bottom,
5673
+ };
5674
+ }
5675
+ else {
5676
+ bounds.left = Math.min(bounds.left, rect.left);
5677
+ bounds.top = Math.min(bounds.top, rect.top);
5678
+ bounds.right = Math.max(bounds.right, rect.right);
5679
+ bounds.bottom = Math.max(bounds.bottom, rect.bottom);
5680
+ }
5681
+ }
5682
+ return bounds;
5683
+ }, [selection.nodes, rfInstance]);
5684
+ if (!selectionBounds || selection.nodes.length < 2) {
5685
+ return null;
5686
+ }
5687
+ const { left, top, right, bottom } = selectionBounds;
5688
+ const width = right - left;
5689
+ const height = bottom - top;
5690
+ return (jsxRuntime.jsx("div", { style: {
5691
+ position: "fixed",
5692
+ left: `${left}px`,
5693
+ top: `${top}px`,
5694
+ width: `${width}px`,
5695
+ height: `${height}px`,
5696
+ border: "1px dashed #0ea5e9",
5697
+ pointerEvents: "none",
5698
+ zIndex: 4,
5699
+ boxSizing: "border-box",
5700
+ } }));
5701
+ };
5702
+
5703
+ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize, reactFlowProps }, ref) => {
5651
5704
  const { wb, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
5652
5705
  const nodeValidation = validationByNode;
5653
5706
  const edgeValidation = validationByEdge.errors;
@@ -5762,27 +5815,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
5762
5815
  }, [uiVersion, ui]);
5763
5816
  const { nodes, edges } = React.useMemo(() => {
5764
5817
  const sel = wb.getSelection();
5765
- // Merge defaults with inputs for node display (defaults shown in lighter gray)
5766
- const inputsWithDefaults = {};
5767
- for (const n of wb.def.nodes) {
5768
- const nodeInputs = inputsMap[n.nodeId] ?? {};
5769
- const nodeDefaults = inputDefaultsMap[n.nodeId] ?? {};
5770
- const inbound = new Set(wb.def.edges
5771
- .filter((e) => e.target.nodeId === n.nodeId)
5772
- .map((e) => e.target.handle));
5773
- const merged = { ...nodeInputs };
5774
- for (const [h, v] of Object.entries(nodeDefaults)) {
5775
- if (!inbound.has(h) && merged[h] === undefined) {
5776
- merged[h] = v;
5777
- }
5778
- }
5779
- if (Object.keys(merged).length > 0) {
5780
- inputsWithDefaults[n.nodeId] = merged;
5781
- }
5782
- }
5783
5818
  const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), wb.registry, {
5784
5819
  showValues,
5785
- inputs: inputsWithDefaults,
5820
+ inputs: inputsMap,
5786
5821
  inputDefaults: inputDefaultsMap,
5787
5822
  outputs: outputsMap,
5788
5823
  resolveNodeType,
@@ -5901,13 +5936,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
5901
5936
  resolveNodeType,
5902
5937
  ]);
5903
5938
  const throttled = useThrottledValue({ nodes, edges }, 100);
5904
- const [menuOpen, setMenuOpen] = React.useState(false);
5905
- const [menuPos, setMenuPos] = React.useState(null);
5906
- const [nodeMenuOpen, setNodeMenuOpen] = React.useState(false);
5907
- const [nodeMenuPos, setNodeMenuPos] = React.useState(null);
5908
- const [nodeAtMenu, setNodeAtMenu] = React.useState(null);
5909
- const [selectionMenuPos, setSelectionMenuPos] = React.useState(null);
5910
- const [selectionMenuOpen, setSelectionMenuOpen] = React.useState(false);
5939
+ const [menuState, setMenuState] = React.useState(null);
5911
5940
  // Compute the rectangular screen-space bounds of the current selection
5912
5941
  const getSelectionScreenBounds = () => {
5913
5942
  if (typeof document === "undefined")
@@ -5948,31 +5977,24 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
5948
5977
  if (target) {
5949
5978
  // Resolve node id from data-id attribute React Flow sets
5950
5979
  const nodeId = target.getAttribute("data-id");
5951
- const isSelected = nodeId && selection.nodes.includes(nodeId);
5952
- if (isSelected && isSingleNodeSelected) {
5953
- // Right-clicked on the single selected node - show node menu
5954
- setNodeAtMenu(nodeId);
5955
- setNodeMenuPos({ x: e.clientX, y: e.clientY });
5956
- setNodeMenuOpen(true);
5957
- setMenuOpen(false);
5958
- setSelectionMenuOpen(false);
5980
+ if (!nodeId)
5959
5981
  return;
5960
- }
5961
- else if (isSelected) {
5982
+ const isSelected = selection.nodes.includes(nodeId);
5983
+ if (isSelected && !isSingleNodeSelected) {
5962
5984
  // Right-clicked on a node that's part of multi-selection - show selection menu
5963
- setSelectionMenuPos({ x: e.clientX, y: e.clientY });
5964
- setSelectionMenuOpen(true);
5965
- setMenuOpen(false);
5966
- setNodeMenuOpen(false);
5985
+ setMenuState({
5986
+ type: "selection",
5987
+ menuPos: { x: e.clientX, y: e.clientY },
5988
+ });
5967
5989
  return;
5968
5990
  }
5969
5991
  else {
5970
5992
  // Right-clicked on a non-selected node - show node menu
5971
- setNodeAtMenu(nodeId);
5972
- setNodeMenuPos({ x: e.clientX, y: e.clientY });
5973
- setNodeMenuOpen(true);
5974
- setMenuOpen(false);
5975
- setSelectionMenuOpen(false);
5993
+ setMenuState({
5994
+ type: "node",
5995
+ menuPos: { x: e.clientX, y: e.clientY },
5996
+ nodeId,
5997
+ });
5976
5998
  return;
5977
5999
  }
5978
6000
  }
@@ -5984,32 +6006,22 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
5984
6006
  if (isSelected && isSingleNodeSelected) {
5985
6007
  // Right-clicked on an edge, but only one node is selected - show node menu
5986
6008
  const nodeId = selection.nodes[0];
5987
- setNodeAtMenu(nodeId);
5988
- setNodeMenuPos({ x: e.clientX, y: e.clientY });
5989
- setNodeMenuOpen(true);
5990
- setMenuOpen(false);
5991
- setSelectionMenuOpen(false);
6009
+ setMenuState({
6010
+ type: "node",
6011
+ menuPos: { x: e.clientX, y: e.clientY },
6012
+ nodeId,
6013
+ });
5992
6014
  return;
5993
6015
  }
5994
6016
  else if (isSelected) {
5995
6017
  // Right-clicked on a selected edge with multiple nodes - show selection menu
5996
- setSelectionMenuPos({ x: e.clientX, y: e.clientY });
5997
- setSelectionMenuOpen(true);
5998
- setMenuOpen(false);
5999
- setNodeMenuOpen(false);
6018
+ setMenuState({
6019
+ type: "selection",
6020
+ menuPos: { x: e.clientX, y: e.clientY },
6021
+ });
6000
6022
  return;
6001
6023
  }
6002
6024
  }
6003
- // If only one node is selected (even with edges), show node menu for empty space clicks
6004
- if (isSingleNodeSelected) {
6005
- const nodeId = selection.nodes[0];
6006
- setNodeAtMenu(nodeId);
6007
- setNodeMenuPos({ x: e.clientX, y: e.clientY });
6008
- setNodeMenuOpen(true);
6009
- setMenuOpen(false);
6010
- setSelectionMenuOpen(false);
6011
- return;
6012
- }
6013
6025
  // Check if the cursor is inside the rectangular bounds of the current selection
6014
6026
  // (for multi-selection when right-clicking on empty space within selection bounds)
6015
6027
  const selectionBounds = getSelectionScreenBounds();
@@ -6019,28 +6031,38 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6019
6031
  e.clientX <= right &&
6020
6032
  e.clientY >= top &&
6021
6033
  e.clientY <= bottom) {
6022
- setSelectionMenuPos({ x: e.clientX, y: e.clientY });
6023
- setSelectionMenuOpen(true);
6024
- setMenuOpen(false);
6025
- setNodeMenuOpen(false);
6034
+ // If only one node is selected (even with edges), show node menu for empty space clicks
6035
+ if (isSingleNodeSelected) {
6036
+ const nodeId = selection.nodes[0];
6037
+ setMenuState({
6038
+ type: "node",
6039
+ menuPos: { x: e.clientX, y: e.clientY },
6040
+ nodeId,
6041
+ });
6042
+ return;
6043
+ }
6044
+ setMenuState({
6045
+ type: "selection",
6046
+ menuPos: { x: e.clientX, y: e.clientY },
6047
+ });
6026
6048
  return;
6027
6049
  }
6028
6050
  }
6029
6051
  // Right-clicked on empty space with no selection - show default menu
6030
- setMenuPos({ x: e.clientX, y: e.clientY });
6031
- setMenuOpen(true);
6032
- setNodeMenuOpen(false);
6033
- setSelectionMenuOpen(false);
6052
+ setMenuState({
6053
+ type: "default",
6054
+ menuPos: { x: e.clientX, y: e.clientY },
6055
+ });
6034
6056
  };
6035
6057
  const addNodeAt = React.useCallback(async (typeId, opts) => wb.addNode({ typeId }, { inputs: opts.inputs, position: opts.position, commit: true }), [wb]);
6036
6058
  const onCloseMenu = React.useCallback(() => {
6037
- setMenuOpen(false);
6059
+ setMenuState(null);
6038
6060
  }, []);
6039
6061
  const onCloseNodeMenu = React.useCallback(() => {
6040
- setNodeMenuOpen(false);
6062
+ setMenuState(null);
6041
6063
  }, []);
6042
6064
  const onCloseSelectionMenu = React.useCallback(() => {
6043
- setSelectionMenuOpen(false);
6065
+ setMenuState(null);
6044
6066
  }, []);
6045
6067
  React.useEffect(() => {
6046
6068
  const off = wb.on("historyChanged", (event) => {
@@ -6087,8 +6109,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6087
6109
  return baseHandlers;
6088
6110
  }, [wb, runner, overrides, onCloseSelectionMenu]);
6089
6111
  const nodeContextMenuHandlers = React.useMemo(() => {
6090
- if (!nodeAtMenu)
6112
+ if (menuState?.type !== "node")
6091
6113
  return null;
6114
+ const nodeAtMenu = menuState.nodeId;
6092
6115
  // Get storage from override or use workbench's internal storage
6093
6116
  const storage = overrides?.getCopiedDataStorage
6094
6117
  ? overrides.getCopiedDataStorage()
@@ -6104,7 +6127,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6104
6127
  }
6105
6128
  return baseHandlers;
6106
6129
  }, [
6107
- nodeAtMenu,
6130
+ menuState,
6108
6131
  wb,
6109
6132
  runner,
6110
6133
  wb.registry,
@@ -6117,10 +6140,10 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6117
6140
  overrides?.getCopiedDataStorage,
6118
6141
  ]);
6119
6142
  const bakeableOutputs = React.useMemo(() => {
6120
- if (!nodeAtMenu)
6143
+ if (menuState?.type !== "node")
6121
6144
  return [];
6122
- return getBakeableOutputs(nodeAtMenu, wb, wb.registry, outputTypesMap);
6123
- }, [nodeAtMenu, wb, wb.registry, registryVersion, outputTypesMap]);
6145
+ return getBakeableOutputs(menuState.nodeId, wb, wb.registry, outputTypesMap);
6146
+ }, [menuState, wb, wb.registry, registryVersion, outputTypesMap]);
6124
6147
  // Keyboard shortcuts configuration
6125
6148
  const enableKeyboardShortcuts = overrides?.enableKeyboardShortcuts !== false; // Default to true
6126
6149
  const keyboardShortcuts = overrides?.keyboardShortcuts || {
@@ -6190,7 +6213,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6190
6213
  const modKeyLabel = isMac ? "⌘" : "Ctrl";
6191
6214
  showToast(`Copy (${modKeyLabel} + C)`);
6192
6215
  // If single node selected, use node context menu handler; otherwise use selection handler
6193
- if (selection.nodes.length === 1 && nodeContextMenuHandlers?.onCopy) {
6216
+ if (selection.nodes.length === 1 &&
6217
+ nodeContextMenuHandlers?.onCopy) {
6194
6218
  nodeContextMenuHandlers.onCopy();
6195
6219
  }
6196
6220
  else if (selectionContextMenuHandlers.onCopy) {
@@ -6310,21 +6334,25 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
6310
6334
  });
6311
6335
  return () => off();
6312
6336
  }, [wb]);
6313
- return (jsxRuntime.jsxs("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: [jsxRuntime.jsx(react.ReactFlowProvider, { children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
6314
- rfInstanceRef.current = inst;
6315
- const savedViewport = wb.getViewport();
6316
- if (savedViewport) {
6317
- inst.setViewport(lod.clone(savedViewport));
6318
- }
6319
- }, 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 ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
6320
- ? { enableKeyboardShortcuts, keyboardShortcuts }
6321
- : {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })), !!nodeAtMenu &&
6322
- nodeContextMenuHandlers &&
6323
- (NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, ...(enableKeyboardShortcuts !== false
6324
- ? { enableKeyboardShortcuts, keyboardShortcuts }
6325
- : {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), selectionMenuOpen &&
6326
- selectionMenuPos &&
6327
- (SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }) }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
6337
+ const { onInit: userOnInit, ...restReactFlowProps } = reactFlowProps || {};
6338
+ return (jsxRuntime.jsxs("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: [jsxRuntime.jsxs(react.ReactFlowProvider, { children: [jsxRuntime.jsxs(react.ReactFlow, { ...restReactFlowProps, nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
6339
+ rfInstanceRef.current = inst;
6340
+ const savedViewport = wb.getViewport();
6341
+ if (savedViewport) {
6342
+ inst.setViewport(lod.clone(savedViewport));
6343
+ }
6344
+ if (userOnInit) {
6345
+ userOnInit(inst);
6346
+ }
6347
+ }, 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 ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), menuState?.type === "default" &&
6348
+ (DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
6349
+ ? { enableKeyboardShortcuts, keyboardShortcuts }
6350
+ : {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), menuState?.type === "node" &&
6351
+ nodeContextMenuHandlers &&
6352
+ (NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, ...(enableKeyboardShortcuts !== false
6353
+ ? { enableKeyboardShortcuts, keyboardShortcuts }
6354
+ : {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), menuState?.type === "selection" &&
6355
+ (SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }), jsxRuntime.jsx(SelectionBoundOverlay, { selection: wb.getSelection(), rfInstance: rfInstanceRef.current })] }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
6328
6356
  });
6329
6357
 
6330
6358
  function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
@@ -6740,7 +6768,10 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
6740
6768
  }
6741
6769
  }, title: "Select run mode", children: [jsxRuntime.jsx("option", { value: "manual", children: "Manual" }), jsxRuntime.jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsxRuntime.jsx(react$1.TreeStructureIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsxRuntime.jsx(react$1.CornersOutIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsxRuntime.jsx(react$1.DownloadIcon, { size: 24 }) }), jsxRuntime.jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsxRuntime.jsx(react$1.UploadIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
6742
6770
  await downloadCanvasThumbnail(canvasContainerRef.current);
6743
- }, title: "Download Flow Thumbnail (SVG)", children: jsxRuntime.jsx(react$1.ImageIcon, { size: 24 }) }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
6771
+ }, title: "Download Flow Thumbnail (SVG)", children: jsxRuntime.jsx(react$1.ImageIcon, { size: 24 }) }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize, reactFlowProps: {
6772
+ minZoom: 0.1,
6773
+ maxZoom: 5,
6774
+ } }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
6744
6775
  }
6745
6776
  function WorkbenchStudio({ example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
6746
6777
  const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());