diff --git a/src/components/nodes/BaseNode.tsx b/src/components/nodes/BaseNode.tsx index ba44b363..fb5cd96a 100644 --- a/src/components/nodes/BaseNode.tsx +++ b/src/components/nodes/BaseNode.tsx @@ -205,8 +205,14 @@ export function BaseNode({ ${!fullBleed && selected && settingsExpanded ? "border-blue-500" : ""} ${className} `} - onMouseEnter={() => setHoveredNodeId(id)} - onMouseLeave={() => setHoveredNodeId(null)} + onMouseEnter={() => { + if (document.querySelector('.react-flow__pane.dragging')) return; + setHoveredNodeId(id); + }} + onMouseLeave={() => { + if (document.querySelector('.react-flow__pane.dragging')) return; + setHoveredNodeId(null); + }} >
{children}
diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 472206ab..c0a74cc9 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -382,6 +382,10 @@ let nodeIdCounter = 0; let groupIdCounter = 0; let autoSaveIntervalId: ReturnType | null = null; +// RAF debounce for hover updates — coalesces rapid mouseenter/mouseleave events +// into a single store update per animation frame +let hoverRafId: number | null = null; + // Track pending save-generation syncs to ensure IDs are resolved before workflow save const pendingImageSyncs = new Map>(); @@ -526,7 +530,11 @@ const workflowStoreImpl: StateCreator = (set, get) => ({ }, setHoveredNodeId: (id: string | null) => { - set({ hoveredNodeId: id }); + if (hoverRafId !== null) cancelAnimationFrame(hoverRafId); + hoverRafId = requestAnimationFrame(() => { + hoverRafId = null; + if (get().hoveredNodeId !== id) set({ hoveredNodeId: id }); + }); }, addNode: (type: NodeType, position: XYPosition, initialData?: Partial) => {