Draw connections on a canvas instead of as reconciled SVG elements - #330
Merged
Conversation
Connections were rendered by the flow library as one SVG element per link, positioned from the library's own node records and reconciled by React. During a drag that put a node card and the line attached to it on two different update paths, and the line was drawn on screen for a position the node had already left - a gap that grew with pointer speed and closed when movement stopped. Ten attempts to make those two paths agree did not change what the user saw: reducing re-renders, preserving measurements, suspending culling, correcting positions inside the library's change pipeline, handing node state to the library, writing the SVG path imperatively, and rewriting it before every paint. Each addressed when or what was computed, none removed the fact that connection geometry was stored somewhere that could fall behind the gesture. Connections are now drawn by a single canvas layer that redraws each frame from the current node positions and viewport transform. The library is handed no edges at all, so its edge machinery is inert rather than merely invisible. There is no per-link element and no retained geometry, so a link cannot be drawn from a stale position - there is no stored position to be stale. This is the immediate-mode approach long-standing node editors use, and the reporter confirms it resolves the symptom. Hover, selection, orthogonal routing and faded connections are preserved. Hit testing runs against the geometry the canvas last drew, so what is visible and what is clickable cannot disagree. Deleting a selected connection is handled here now, since the library no longer reports edge deletions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Connections were rendered by the flow library as one SVG element per link, positioned from the library's own node records and reconciled by React. During a drag that put the node card and the line attached to it on two different update paths, and the line was drawn on screen for a position the node had already left. The gap grew with pointer speed, pointed opposite the direction of travel, and closed as soon as movement stopped.
Ten prior attempts failed to change what the user saw: reducing re-renders, preserving measurements, suspending culling, correcting positions inside the library's change pipeline, handing node state to the library, writing the SVG path imperatively, and rewriting it before every paint. Each of those changed when or what was computed. None removed the underlying fact that connection geometry was stored somewhere that could fall behind the gesture.
Change
Connections are drawn by a single canvas layer that redraws every frame from the current node positions and the current viewport transform. The library is handed an empty edge list, so its edge machinery is inert rather than merely invisible and is no longer part of the drag path.
There is no per-link element and no retained geometry, so a link cannot be drawn from a position that is out of date - there is no stored position to be out of date. This is the immediate-mode approach long-standing node editors use, for this exact reason.
Preserved: hover, selection, orthogonal routing, and faded connections. Hit testing runs against the geometry the canvas last drew, so what is visible and what is clickable cannot disagree. Deleting a selected connection is handled here now, since the library no longer reports edge deletions.
Test plan