From cb47f60debf5d32c2f15d65313eef1889b848cce Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sat, 7 Mar 2026 23:00:39 +1300 Subject: [PATCH] fix: resolve node click-to-select failures caused by d3-drag dead zone Add nodeClickDistance={5} to match nodeDragThreshold, eliminating the 0-5px dead zone where d3-drag suppressed clicks but drags hadn't started. Stop header pointerdown propagation to prevent pane deselection race. Fix header drag threshold to use screen pixels for zoom-independent behavior. Co-Authored-By: Claude Opus 4.6 --- src/components/WorkflowCanvas.tsx | 1 + src/components/nodes/FloatingNodeHeader.tsx | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 1cc1706e..912e38cf 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -1973,6 +1973,7 @@ export function WorkflowCanvas() { } selectNodesOnDrag={false} nodeDragThreshold={5} + nodeClickDistance={5} zoomOnScroll={false} zoomOnPinch={!isModalOpen} minZoom={0.1} diff --git a/src/components/nodes/FloatingNodeHeader.tsx b/src/components/nodes/FloatingNodeHeader.tsx index 9d264980..acb41238 100644 --- a/src/components/nodes/FloatingNodeHeader.tsx +++ b/src/components/nodes/FloatingNodeHeader.tsx @@ -216,6 +216,7 @@ export function FloatingNodeHeader({ if (e.button !== 0) return; e.preventDefault(); + e.stopPropagation(); const startX = e.clientX; const startY = e.clientY; @@ -243,15 +244,17 @@ export function FloatingNodeHeader({ isDraggingRef.current = false; const handlePointerMove = (e: PointerEvent) => { - const { zoom } = getViewport(); - const dx = (e.clientX - startX) / zoom; - const dy = (e.clientY - startY) / zoom; + const screenDx = e.clientX - startX; + const screenDy = e.clientY - startY; - if (Math.abs(dx) > 2 || Math.abs(dy) > 2) { + if (!isDraggingRef.current && (Math.abs(screenDx) > 5 || Math.abs(screenDy) > 5)) { isDraggingRef.current = true; } if (isDraggingRef.current) { + const { zoom } = getViewport(); + const dx = screenDx / zoom; + const dy = screenDy / zoom; setNodes(nodes => nodes.map(n => { const startPos = startPositions.get(n.id); if (!startPos) return n;