diff --git a/src/components/nodes/FloatingNodeHeader.tsx b/src/components/nodes/FloatingNodeHeader.tsx index 83379b95..9d264980 100644 --- a/src/components/nodes/FloatingNodeHeader.tsx +++ b/src/components/nodes/FloatingNodeHeader.tsx @@ -263,50 +263,56 @@ export function FloatingNodeHeader({ } }; - const handlePointerUp = () => { + const handlePointerUp = (e: PointerEvent) => { const wasDragging = isDraggingRef.current; document.removeEventListener('pointermove', handlePointerMove); document.removeEventListener('pointerup', handlePointerUp); isDraggingRef.current = false; - // Check group membership if the node was actually dragged + // Check group membership for ALL moved nodes if (wasDragging) { const store = useWorkflowStore.getState(); - const allNodes = getNodes(); - const draggedNode = allNodes.find(n => n.id === id); - - if (!draggedNode) return; - - // Get node dimensions (prefer measured, fallback to defaults) - const defaults = defaultNodeDimensions[type] || { width: 300, height: 280 }; - const nodeWidth = draggedNode.measured?.width || (draggedNode.style?.width as number) || defaults.width; - const nodeHeight = draggedNode.measured?.height || (draggedNode.style?.height as number) || defaults.height; - - // Calculate node center - const nodeCenterX = draggedNode.position.x + nodeWidth / 2; - const nodeCenterY = draggedNode.position.y + nodeHeight / 2; - - // Check if node center is inside any group - let targetGroupId: string | undefined; - - for (const group of Object.values(store.groups)) { - const inBoundsX = nodeCenterX >= group.position.x && nodeCenterX <= group.position.x + group.size.width; - const inBoundsY = nodeCenterY >= group.position.y && nodeCenterY <= group.position.y + group.size.height; - - if (inBoundsX && inBoundsY) { - targetGroupId = group.id; - break; + const { zoom } = getViewport(); + const dx = (e.clientX - startX) / zoom; + const dy = (e.clientY - startY) / zoom; + + for (const [nodeId, startPos] of startPositions) { + // Calculate final position deterministically from drag delta + const finalX = startPos.x + dx; + const finalY = startPos.y + dy; + + // Get node dimensions from store (always fresh) + const storeNode = store.nodes.find(n => n.id === nodeId); + if (!storeNode) continue; + + const nodeType = storeNode.type as NodeType; + const defaults = defaultNodeDimensions[nodeType] || { width: 300, height: 280 }; + const nodeWidth = storeNode.measured?.width || (storeNode.style?.width as number) || defaults.width; + const nodeHeight = storeNode.measured?.height || (storeNode.style?.height as number) || defaults.height; + + // Calculate node center + const nodeCenterX = finalX + nodeWidth / 2; + const nodeCenterY = finalY + nodeHeight / 2; + + // Check if node center is inside any group + let targetGroupId: string | undefined; + + for (const group of Object.values(store.groups)) { + const inBoundsX = nodeCenterX >= group.position.x && nodeCenterX <= group.position.x + group.size.width; + const inBoundsY = nodeCenterY >= group.position.y && nodeCenterY <= group.position.y + group.size.height; + + if (inBoundsX && inBoundsY) { + targetGroupId = group.id; + break; + } } - } - // Get the node's current groupId - const currentNode = store.nodes.find((n) => n.id === id); - const currentGroupId = currentNode?.groupId; - - // Update groupId if it changed - if (targetGroupId !== currentGroupId) { - store.setNodeGroupId(id, targetGroupId); + // Update groupId if it changed + const currentGroupId = storeNode.groupId; + if (targetGroupId !== currentGroupId) { + store.setNodeGroupId(nodeId, targetGroupId); + } } } };