Keep text and images in place when resized on defense - #227
Conversation
On defense a text box or image mirrors attack, so it hangs from its bottom-right corner. Dragging the right-hand resize handle moved the left edge instead, and because the width was measured from that moving edge, the box ran away from the pointer. A resize now pins the box's on-screen top-left, and on release stores the size together with the canonical position that keeps it there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughText and image resizing now tracks a pinned screen position and saves both size and position through provider methods that look up items by ID. The resize builders also handle changes between attack and defense sides during a resize. ChangesPlaced Box Resizing
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant Pointer
participant PlacedTextBuilder
participant TextProvider
participant StrategyState
Pointer->>PlacedTextBuilder: Move resize handle
PlacedTextBuilder->>PlacedTextBuilder: Update pinned position and displayed size
Pointer->>PlacedTextBuilder: End resize
PlacedTextBuilder->>TextProvider: Save size and document position by ID
PlacedTextBuilder->>StrategyState: Mark strategy unsaved
Merge Risk: ⚪ Minimal · up to No actionable resize regression remains identified; the change is mergeable after normal checks. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to A resize appears capable of changing an item on a different page if the page changes before the gesture ends. This is a conditional document-integrity risk, not an identified access-control issue. Retained concerns
Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Hardening Proposals
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Switching sides while holding a resize handle left the pin at the old side's screen position, and release read it on the new side, so the stored position landed hundreds of pixels off. The pin now moves to where the box shows on the new side. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A side switch mid-resize mirrors the box away from the held pointer, and the next drag update measured the width from the new left edge, so the box jumped hundreds of pixels wide. The builders now remember how far the left edge moved and measure from where it was. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comments Outside DiffThese findings could not be posted inline.
|
A resize waited one frame after release before storing, so the final size was laid out. A page switch inside that frame could land the resize on the next page's copy of the item, which keeps the same id. Pointer events arrive between frames, and a frame builds and lays out together. So on release the box on screen always matches the size it was last built at. Storing that size with the rendered box makes the commit synchronous and exact. A final move the frame never drew is dropped, so the box stays where the user last saw it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Storing the size last drawn at release dropped movement that arrived after the last frame: a quick drag could be lost entirely. Go back to storing the final size one frame after release, when it is laid out. Write it only if the text or image being resized is still loaded. A page switch in that frame loads the next page's copy, which keeps the same id, so the resize can no longer land on it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With the write deferred a frame, a page save right after release still read the old size, and the page switch then replaced the item, so the resize was lost. Store the size, and a position from the current layout, as soon as the handle is released. Hold the pin one more frame, then store the position again if the final layout moved it, only while the same item is still loaded. Normally that second write finds nothing to change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On defense, dragging a text box's resize handle moved the wrong edge. Images had the same bug.
A text box or image on defense mirrors its attack placement, so it hangs from its bottom-right corner. When the width grew, the left edge moved and the right edge stayed put. The width was measured from that moving left edge to the pointer, so each drag update fed the next one and the box shot off to the left, away from the cursor.
Reproduction
Left: before. Right: after. Rows: start, mid-drag, released. The red dot is the pointer. These captures come from a throwaway widget test that renders the real
PlacedTextBuilderon defense (run on the cloud branch, where the code is identical).Fix
CanonicalPositionedBoxtakes an optionalpinnedScreenPosition. While it is set, the box keeps that on-screen top-left at any size.PlacedTextBuilderandPlacedImageBuildernow position themselves, so they can pin during a resize. The lists pass themisAttack, which they already read. When the drag starts they pin the current top-left. On release they store the new size, and the canonical position that keeps the top-left there, synchronously, so a page save right after sees it. The pin holds for one more frame. Once the final size is laid out, the position is stored again if it moved, but only while the provider still holds the exact item that was resized. So a page switch can't touch the next page's copy (same id). On attack that position is the one already stored.TextProvider.updateSizebecameresize(id, size:, position:)andPlacedImageProvider.updateScalebecameresize(id, scale:, position:). They look up by id rather than index.No data or payload shape changes. The fix writes the same
size/scaleandpositionfields as before.Tests
test/placed_box_resize_test.dartcovers text and images on attack and defense, plus a side switch mid-resize a release in the same frame as the last move, and a page switch right after release. It drives the real resize handle and checks that the top-left never moves, both during the drag and after release from the stored position alone. With the pin disabled, the defense text case fails: the left edge moved 15.5 px on the first update.test/text_widget_resilience_test.dartharness:PlacedTextBuildernow positions itself, so it is no longer wrapped inPositioned.flutter test --no-pub: 649 passed.dart analyzeis clean on touched files.This bug shipped to desktop in
desktop-stable-v4.6.2+102. This is themaincopy of #226 plus #231 (cloud); the only difference is that main'sImageWidgetkeeps itslink:argument, and the new test stubs the Hive-backed color library and strategy storage that main reads.🤖 Generated with Claude Code
Not safe to merge until an immediate page switch preserves the position of resized text and images on defense.
Findings
Summary
The PR saves text and image dimensions immediately when resizing ends, then corrects their positions after the next frame. On defense, switching pages before that frame can save an incorrect position, causing the item to reopen shifted. This needs fixing before merge.
Reviews (4) · Last reviewed commit: "Store a resize's size on release and set..."