Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export function useNodePointerInteractions(
return true
}

let hasDraggingStarted = false

const startPosition = ref({ x: 0, y: 0 })

const DRAG_THRESHOLD = 3 // pixels
Expand Down Expand Up @@ -57,7 +59,7 @@ export function useNodePointerInteractions(

startPosition.value = { x: event.clientX, y: event.clientY }

startDrag(event, nodeId)
safeDragStart(event, nodeId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: startDrag called twice with changed selection between calls.

The current flow calls safeDragStart in both onPointerdown (line 62) and onPointermove (line 83), with handleNodeSelect (line 82) potentially modifying the selection between calls. As noted in previous reviews, startDrag reinitializes draggingNodeIds from the current selectedNodeIds each time, meaning the second call captures a different selection than the first.

This breaks the expected behavior where drag state should capture the selection at drag initiation, not mid-operation.

Remove the premature safeDragStart call from onPointerdown. The drag should only be initialized when actual dragging begins (after movement threshold is met or on first multi-select move):

 function onPointerdown(event: PointerEvent) {
   // ... existing checks ...
   startPosition.value = { x: event.clientX, y: event.clientY }
-
-  safeDragStart(event, nodeId)
 }

Then ensure both drag paths in onPointermove call safeDragStart before setting isDraggingVueNodes:

 if (lmbDown && multiSelect && !layoutStore.isDraggingVueNodes.value) {
+  safeDragStart(event, nodeId)
   layoutStore.isDraggingVueNodes.value = true
   handleNodeSelect(event, nodeId)
-  safeDragStart(event, nodeId)
   return
 }

 if (lmbDown && !layoutStore.isDraggingVueNodes.value) {
   const dx = event.clientX - startPosition.value.x
   const dy = event.clientY - startPosition.value.y
   const distance = Math.sqrt(dx * dx + dy * dy)

   if (distance > DRAG_THRESHOLD) {
+    safeDragStart(event, nodeId)
     layoutStore.isDraggingVueNodes.value = true
     handleNodeSelect(event, nodeId)
   }
 }

Also applies to: 83-83

🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts
around lines 62 and 83, the code calls safeDragStart twice (once in
onPointerdown at line 62 and again in onPointermove at line 83), allowing
handleNodeSelect to change selection between calls and causing startDrag to
capture the wrong selection; remove the safeDragStart call from onPointerdown so
drag initialization only happens once when actual dragging begins, and update
onPointermove so both drag-start code paths invoke safeDragStart immediately
before setting isDraggingVueNodes (ensuring safeDragStart runs only at true drag
start and after selection changes are finalized).

}

function onPointermove(event: PointerEvent) {
Expand All @@ -78,7 +80,7 @@ export function useNodePointerInteractions(
if (lmbDown && multiSelect && !layoutStore.isDraggingVueNodes.value) {
layoutStore.isDraggingVueNodes.value = true
handleNodeSelect(event, nodeId)
startDrag(event, nodeId)
safeDragStart(event, nodeId)
return
}
// Check if we should start dragging (pointer moved beyond threshold)
Expand All @@ -102,13 +104,22 @@ export function useNodePointerInteractions(
layoutStore.isDraggingVueNodes.value = false
}

function safeDragStart(event: PointerEvent, nodeId: string) {
try {
startDrag(event, nodeId)
} finally {
hasDraggingStarted = true
}
}

function safeDragEnd(event: PointerEvent) {
try {
const nodeId = toValue(nodeIdRef)
endDrag(event, nodeId)
} catch (error) {
console.error('Error during endDrag:', error)
} finally {
hasDraggingStarted = false
cleanupDragState()
}
}
Expand All @@ -123,9 +134,12 @@ export function useNodePointerInteractions(
}
const wasDragging = layoutStore.isDraggingVueNodes.value

if (wasDragging) {
if (hasDraggingStarted || wasDragging) {
safeDragEnd(event)
return

if (wasDragging) {
return
}
}

// Skip selection handling for right-click (button 2) - context menu handles its own selection
Expand Down
Loading