@bian-womp/spark-workbench 0.3.61 → 0.3.63

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
@@ -5890,220 +5890,22 @@ function useKeyboardShortcutToast() {
5890
5890
  return { toast, showToast, hideToast };
5891
5891
  }
5892
5892
 
5893
- const SelectionBoundOverlay = ({ selection, rfInstance, viewportTick }) => {
5894
- const overlayRef = React.useRef(null);
5895
- const [parentRect, setParentRect] = React.useState(null);
5896
- const [isDragging, setIsDragging] = React.useState(false);
5897
- const [bounds, setBounds] = React.useState(null);
5898
- const dragStateRef = React.useRef(null);
5899
- const moveListenerRef = React.useRef(null);
5900
- const upListenerRef = React.useRef(null);
5901
- React.useRef(null);
5902
- const { wb } = useWorkbenchContext();
5893
+ const selectionActiveSelector = (state) => state.nodesSelectionActive;
5894
+ const selectionDragActiveSelector = (state) => state.userSelectionActive;
5895
+ const SelectionActiveSync = ({ selection }) => {
5896
+ const store = react.useStoreApi();
5897
+ const currentActive = react.useStore(selectionActiveSelector);
5898
+ const draggingSelection = react.useStore(selectionDragActiveSelector);
5899
+ const active = selection.nodes.length > 1;
5903
5900
  React.useEffect(() => {
5904
- if (!overlayRef.current)
5905
- return;
5906
- const parent = overlayRef.current.parentElement;
5907
- if (!parent)
5908
- return;
5909
- const updateRect = () => {
5910
- setParentRect(parent.getBoundingClientRect());
5911
- };
5912
- updateRect();
5913
- const resizeObserver = new ResizeObserver(updateRect);
5914
- resizeObserver.observe(parent);
5915
- const scrollHandler = () => updateRect();
5916
- window.addEventListener("scroll", scrollHandler, true);
5917
- return () => {
5918
- resizeObserver.disconnect();
5919
- window.removeEventListener("scroll", scrollHandler, true);
5920
- };
5921
- }, [viewportTick]);
5922
- const cleanupDragListeners = React.useCallback(() => {
5923
- if (moveListenerRef.current) {
5924
- window.removeEventListener("mousemove", moveListenerRef.current);
5925
- moveListenerRef.current = null;
5926
- }
5927
- if (upListenerRef.current) {
5928
- window.removeEventListener("mouseup", upListenerRef.current);
5929
- upListenerRef.current = null;
5930
- }
5931
- dragStateRef.current = null;
5932
- setIsDragging(false);
5933
- }, []);
5934
- React.useEffect(() => cleanupDragListeners, [cleanupDragListeners]);
5935
- const handleMouseMove = React.useCallback((e) => {
5936
- if (!rfInstance || !wb || !parentRect)
5937
- return;
5938
- const dragState = dragStateRef.current;
5939
- if (!dragState)
5940
- return;
5941
- e.preventDefault();
5942
- e.stopPropagation();
5943
- const current = rfInstance.screenToFlowPosition({
5944
- x: e.clientX,
5945
- y: e.clientY,
5946
- });
5947
- const dx = current.x - dragState.startFlow.x;
5948
- const dy = current.y - dragState.startFlow.y;
5949
- // Update nodes directly via React Flow for immediate visual feedback
5950
- const nodes = rfInstance.getNodes();
5951
- const updatedNodes = nodes.map((node) => {
5952
- if (dragState.initialPositions[node.id]) {
5953
- const initialPos = dragState.initialPositions[node.id];
5954
- return {
5955
- ...node,
5956
- position: {
5957
- x: initialPos.x + dx,
5958
- y: initialPos.y + dy,
5959
- },
5960
- };
5961
- }
5962
- return node;
5963
- });
5964
- rfInstance.setNodes(updatedNodes);
5965
- // Also update workbench state
5966
- const nextPositions = {};
5967
- for (const [nodeId, pos] of Object.entries(dragState.initialPositions)) {
5968
- nextPositions[nodeId] = { x: pos.x + dx, y: pos.y + dy };
5969
- }
5970
- if (Object.keys(nextPositions).length) {
5971
- wb.setPositions(nextPositions, { commit: false });
5972
- }
5973
- }, [rfInstance, wb, parentRect]);
5974
- const handleMouseUp = React.useCallback((e) => {
5975
- if (!rfInstance || !wb) {
5976
- cleanupDragListeners();
5901
+ if (draggingSelection)
5977
5902
  return;
5903
+ if (currentActive !== active) {
5904
+ console.log("[SelectionActiveSync] setting active selection to", active);
5905
+ store.setState({ nodesSelectionActive: active });
5978
5906
  }
5979
- const dragState = dragStateRef.current;
5980
- if (!dragState) {
5981
- cleanupDragListeners();
5982
- return;
5983
- }
5984
- e.preventDefault();
5985
- e.stopPropagation();
5986
- const current = rfInstance.screenToFlowPosition({
5987
- x: e.clientX,
5988
- y: e.clientY,
5989
- });
5990
- const dx = current.x - dragState.startFlow.x;
5991
- const dy = current.y - dragState.startFlow.y;
5992
- const nextPositions = {};
5993
- for (const [nodeId, pos] of Object.entries(dragState.initialPositions)) {
5994
- nextPositions[nodeId] = { x: pos.x + dx, y: pos.y + dy };
5995
- }
5996
- if (Object.keys(nextPositions).length) {
5997
- wb.setPositions(nextPositions, { commit: true });
5998
- }
5999
- cleanupDragListeners();
6000
- }, [cleanupDragListeners, rfInstance, wb]);
6001
- const handleMouseDown = React.useCallback((e) => {
6002
- if (e.button !== 0)
6003
- return;
6004
- if (!rfInstance || !wb)
6005
- return;
6006
- if (selection.nodes.length < 2)
6007
- return;
6008
- e.preventDefault();
6009
- e.stopPropagation();
6010
- const startFlow = rfInstance.screenToFlowPosition({
6011
- x: e.clientX,
6012
- y: e.clientY,
6013
- });
6014
- const positions = wb.getPositions();
6015
- const initialPositions = {};
6016
- for (const nodeId of selection.nodes) {
6017
- const pos = positions[nodeId];
6018
- if (pos) {
6019
- initialPositions[nodeId] = pos;
6020
- }
6021
- }
6022
- if (!Object.keys(initialPositions).length)
6023
- return;
6024
- dragStateRef.current = { startFlow, initialPositions };
6025
- setIsDragging(true);
6026
- moveListenerRef.current = handleMouseMove;
6027
- upListenerRef.current = handleMouseUp;
6028
- window.addEventListener("mousemove", handleMouseMove, { passive: false });
6029
- window.addEventListener("mouseup", handleMouseUp, { passive: false });
6030
- }, [handleMouseMove, handleMouseUp, rfInstance, selection.nodes, wb]);
6031
- // Continuous bounds update loop
6032
- React.useEffect(() => {
6033
- if (typeof document === "undefined" ||
6034
- !rfInstance ||
6035
- !parentRect ||
6036
- selection.nodes.length < 2) {
6037
- setBounds(null);
6038
- return;
6039
- }
6040
- let animationFrameId = null;
6041
- let isActive = true;
6042
- const updateBounds = () => {
6043
- if (!isActive)
6044
- return;
6045
- let calculatedBounds = null;
6046
- for (const nodeId of selection.nodes) {
6047
- const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
6048
- if (!el)
6049
- continue;
6050
- const rect = el.getBoundingClientRect();
6051
- const relativeLeft = rect.left - parentRect.left;
6052
- const relativeTop = rect.top - parentRect.top;
6053
- const relativeRight = rect.right - parentRect.left;
6054
- const relativeBottom = rect.bottom - parentRect.top;
6055
- if (!calculatedBounds) {
6056
- calculatedBounds = {
6057
- left: relativeLeft,
6058
- top: relativeTop,
6059
- right: relativeRight,
6060
- bottom: relativeBottom,
6061
- };
6062
- }
6063
- else {
6064
- calculatedBounds.left = Math.min(calculatedBounds.left, relativeLeft);
6065
- calculatedBounds.top = Math.min(calculatedBounds.top, relativeTop);
6066
- calculatedBounds.right = Math.max(calculatedBounds.right, relativeRight);
6067
- calculatedBounds.bottom = Math.max(calculatedBounds.bottom, relativeBottom);
6068
- }
6069
- }
6070
- setBounds(calculatedBounds);
6071
- // Continue the animation loop
6072
- if (isActive) {
6073
- animationFrameId = requestAnimationFrame(updateBounds);
6074
- }
6075
- };
6076
- // Start the animation loop
6077
- animationFrameId = requestAnimationFrame(updateBounds);
6078
- return () => {
6079
- isActive = false;
6080
- if (animationFrameId !== null) {
6081
- cancelAnimationFrame(animationFrameId);
6082
- }
6083
- };
6084
- }, [selection.nodes, rfInstance, viewportTick, parentRect]);
6085
- if (!bounds || selection.nodes.length < 2) {
6086
- return jsxRuntime.jsx("div", { ref: overlayRef, style: { display: "none" } });
6087
- }
6088
- const { left, top, right, bottom } = bounds;
6089
- const width = right - left;
6090
- const height = bottom - top;
6091
- return (jsxRuntime.jsx("div", { ref: overlayRef, onMouseDown: handleMouseDown, style: {
6092
- position: "absolute",
6093
- left: `${left}px`,
6094
- top: `${top}px`,
6095
- width: `${width}px`,
6096
- height: `${height}px`,
6097
- border: isDragging ? "2px solid #0ea5e9" : "1px dashed #0ea5e9",
6098
- backgroundColor: isDragging
6099
- ? "rgba(14, 165, 233, 0.05)"
6100
- : "transparent",
6101
- pointerEvents: "auto",
6102
- cursor: isDragging ? "grabbing" : "move",
6103
- zIndex: 4,
6104
- boxSizing: "border-box",
6105
- transition: isDragging ? "none" : "border 0.1s ease",
6106
- } }));
5907
+ }, [active, currentActive, draggingSelection, store]);
5908
+ return null;
6107
5909
  };
6108
5910
 
6109
5911
  const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
@@ -6746,10 +6548,6 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6746
6548
  connectionLineRenderer: ui.getConnectionLineRenderer(),
6747
6549
  };
6748
6550
  }, [ui, uiVersion]);
6749
- const [selectionOverlayTick, setSelectionOverlayTick] = React.useState(0);
6750
- const onMove = React.useCallback(() => {
6751
- setSelectionOverlayTick((t) => t + 1);
6752
- }, []);
6753
6551
  const onMoveEnd = React.useCallback(() => {
6754
6552
  if (rfInstanceRef.current) {
6755
6553
  const viewport = rfInstanceRef.current.getViewport();
@@ -6781,11 +6579,11 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6781
6579
  if (userOnInit) {
6782
6580
  userOnInit(inst);
6783
6581
  }
6784
- }, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMove: onMove, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [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" &&
6582
+ }, 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: [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" &&
6785
6583
  (DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(DefaultContextMenu, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, keyboardShortcuts: keyboardShortcuts }))), menuState?.type === "node" &&
6786
6584
  nodeContextMenuHandlers &&
6787
6585
  (NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(NodeContextMenu, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), menuState?.type === "selection" &&
6788
- (SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })))] }), jsxRuntime.jsx(SelectionBoundOverlay, { selection: selection, rfInstance: rfInstanceRef.current, viewportTick: selectionOverlayTick })] }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
6586
+ (SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, keyboardShortcuts: keyboardShortcuts })))] }), jsxRuntime.jsx(SelectionActiveSync, { selection: selection })] }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
6789
6587
  });
6790
6588
  const WorkbenchCanvas = WorkbenchCanvasComponent;
6791
6589