Qt-removal R7.5b-3: Smart Guides canvas visual (closes R7.5b)#150
Merged
Conversation
Ports the legacy scene's smart-guide alignment snapping - the last and largest of R7.5's 3 canvas-visual parity gaps. While dragging, dashed guide lines appear when the dragged node's edges/centers align with another node's, and the position snaps to the alignment. The snap algorithm is a pure module (smartGuides.ts) porting legacy's _calculate_smart_guide_snap exactly: 6 same-key-only alignment keys, strict <5px tolerance, first-hit key/candidate ordering, per-axis independence, early exit, candidate-valued union-span guide lines. Integrated as the recorded layered-pass decision (guides overwrite per-axis after RF's native grid-snap) with guide lines rendered via ViewportPortal in Qt's exact dash color. Live drag verification surfaced two integration bugs the automated suite could not see - node dimensions were unavailable from both the local nodes array (toFlowNodes rebuilds wipe them) and RF's internal store (adoptUserNodes rebuilds internal measured from the user objects) - fixed with a DOM-measurement fallback, plus a review-found drag-release bounce off corrected positions, fixed by settling the drag-stop change at the last corrected frame's position.
This was referenced Jul 26, 2026
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.
Summary
Ports the legacy scene's smart-guide alignment snapping — the last and largest of R7.5's three canvas-visual parity gaps. While dragging a node, dashed guide lines appear when its edges or centers align with another node's, and the position snaps to the alignment. With this, R7.5b is complete: all three canvas visuals (faded connections, orthogonal routing, smart guides) are real, and the Help panel's "Grid and Guide Controls" entry is restored to its original legacy wording — every claim in it is now true for the first time in the rewrite.
The algorithm
web_ui/src/app/canvas/smartGuides.tsis a pure, framework-free port of legacy's_calculate_smart_guide_snap(confirmed againstgraphlink_scene.pyby recon, not reimplemented from memory): six same-key-only alignment keys (left/center/right, top/middle/bottom), strict< 5pxtolerance, first-hit key ordering, first-candidate ordering, per-axis independence (X and Y may snap against different candidates), early exit once both axes snap, and candidate-valued guide lines spanning the union of the two rects. Sixteen unit tests cover every documented rule by name.Recorded design decisions
snapToGridruns first (inside RF, before the change is emitted); the smart-guide pass then overwrites only the axes it matched. This reproduces legacy's per-axis "guides beat grid" priority without re-implementing grid-snap manually. Known consequence (recorded in the ledger): tolerance is evaluated against the already-grid-snapped position, so with large grid sizes an off-grid candidate can be unreachable by guides — an accepted fidelity trade.!isSelected()), plus — a documented extension with no legacy precedent — a dragged Frame/Container's own members, which move in lockstep and would otherwise self-align permanently.<ViewportPortal>(theQGraphicsLineItemanalog — same transformed layer, flow coordinates), dash colorrgba(128,128,128,0.784)= Qt'sQColor(128,128,128,200)exactly.Defects found and fixed before merge
Live drag verification surfaced two integration bugs the entire automated suite could not see, plus the adversarial review found one:
toFlowNodesrebuilds the nodes array from every scene snapshot with fresh objects, wiping themeasuredbookkeeping RF writes back via dimension changes.@xyflow/systemsource:adoptUserNodesrebuilds internalmeasuredfrom the user objects, so both copies get wiped on every publish. Final fix:measuredNodeSizeprefers the internal store and falls back to the live DOM element'soffsetWidth/offsetHeight(pre-transform layout px = flow units at any zoom — also the most faithful translation of legacy's ownsceneBoundingRect()reads).Test plan
smartGuides.test.ts(new): 16 unit tests, one per documented algorithm rule (back-solve math for all six keys, strict-tolerance boundary at exactly 5px, first-hit vs closest, candidate ordering, different-candidate X/Y, early exit, guide extents)backend/tests/test_canvas.py: intent test;ViewPopover.test.tsx: checkbox coverage; sceneStore fixture updatedViewportPortalagainst installed types, traced RF's drag-stop internals — 1 real defect (fixed above), 1 accepted deviation (documented), 1 consequence note (recorded)left:100px, top:100px, height:270px(union-span math exact: min-of-tops 100 → max-of-bottoms 370), the guide cleared on release, and the backend committed exactly{x: 100}— also proving the release-bounce fix end-to-end