From 56ad9250e4468d453a4cc8e7f381258e25655134 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Mar 2026 07:42:15 +0000 Subject: [PATCH] Disable pointer-events on node images and content during pan/drag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Browser hit-testing against large base64 elements is expensive even when no React state updates occur. Two CSS-level fixes: 1. pointer-events: none on all .react-flow__node img — images never need direct mouse events (parent containers handle clicks) 2. .canvas-interacting class on during pan/drag disables pointer-events on all node content via CSS descendant selector, toggled via direct DOM access (zero React overhead) https://claude.ai/code/session_01MvD1n4QeXutgwUpKJuDGHa --- src/app/globals.css | 13 +++++++++++++ src/components/WorkflowCanvas.tsx | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 4899a54b..f9b1a6fb 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -40,6 +40,19 @@ body { font-family: inherit; } +/* Skip hit-testing on images inside nodes — parent containers handle all + click/hover events. Prevents expensive bitmap decoding during mouse movement. */ +.react-flow__node img { + pointer-events: none; +} + +/* During active panning or node dragging, disable pointer events on all node + content to eliminate expensive browser hit-testing against the DOM tree. + The class is toggled on via direct DOM access (no React re-render). */ +.canvas-interacting .react-flow__node > * { + pointer-events: none; +} + /* Remove default React Flow styling for output nodes to match custom nodes */ .react-flow__node-output { border: none !important; diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index bd25e2db..209485de 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -1964,10 +1964,10 @@ export function WorkflowCanvas() { onEdgesChange={onEdgesChange} onConnect={handleConnect} onConnectEnd={handleConnectEnd} - onMoveStart={() => { isPanningRef.current = true; setHoveredNodeId(null); }} - onMoveEnd={() => { isPanningRef.current = false; }} - onNodeDragStart={() => { isDraggingNodeRef.current = true; }} - onNodeDragStop={(event, node) => { isDraggingNodeRef.current = false; handleNodeDragStop(event, node); }} + onMoveStart={() => { isPanningRef.current = true; setHoveredNodeId(null); document.documentElement.classList.add("canvas-interacting"); }} + onMoveEnd={() => { isPanningRef.current = false; document.documentElement.classList.remove("canvas-interacting"); }} + onNodeDragStart={() => { isDraggingNodeRef.current = true; document.documentElement.classList.add("canvas-interacting"); }} + onNodeDragStop={(event, node) => { isDraggingNodeRef.current = false; document.documentElement.classList.remove("canvas-interacting"); handleNodeDragStop(event, node); }} onSelectionChange={handleSelectionChange} nodeTypes={nodeTypes} edgeTypes={edgeTypes}