Skip to content

fix: move the context popup onto its selection when the selection changes - #174

Merged
Azganoth merged 1 commit into
mainfrom
bug/context-popup-selection-reposition
Aug 3, 2026
Merged

fix: move the context popup onto its selection when the selection changes#174
Azganoth merged 1 commit into
mainfrom
bug/context-popup-selection-reposition

Conversation

@Azganoth

@Azganoth Azganoth commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

An open context popup stayed where it opened while the selection it acts on moved, so it ended up pointing at text the selection had left behind. Scrolling moved it correctly, which is what made the gap easy to miss.

  • The virtual anchor is now built per request rather than once. Radix registers an anchor by object identity and Floating UI measures only when that identity changes, so a single long-lived anchor could never be re-measured — scroll and resize were the only things that moved the popup.
  • The anchor closes over the request it was built for, which retires the ref that existed only to keep one long-lived anchor current.
  • Explicit memoization rather than compiler-managed: this is a stable-identity contract, keyed on the request so an unrelated render cannot re-register the anchor.
  • The pin is unchanged. A popup holding focus still serves its pinned rect, so re-registering measures nothing new and the popup does not move out from under the user.
  • docs/specification.md — the popup follows its selection, not only the scroll.

Related Issue

Closes #171

Verification

Added to EditorContextPopup.test.tsx: a fresh request that measures the selection somewhere else moves the popup by exactly that distance. The existing pin test now moves the selection under a focused popup and asserts the position does not change, which is the second criterion on the issue.

Manually verified in the desktop app on Windows:

  1. Extending a selection past the popup with Shift+Down keeps the popup against the selection instead of leaving it behind.
  2. Scrolling with the popup open still moves it with the text, and it still hides and returns when the selection leaves and re-enters the viewport.
  3. A popup opened with Shift+F10, holding focus, stays where it was pinned while the selection changes.

Notes

  • The issue's implementation direction named the wrong object: the identity Radix registers is the ref handed to PopoverAnchor, not the anchor carried on the request. Corrected in a comment there.
  • The popup now moves in whole steps, once per keystroke under a held Shift+Arrow. Easing that motion is Ease the context popup's movement when it repositions #172, which this unblocks.
  • The anchor reads the request it was built for rather than the newest one through a ref. If Floating UI measured between a new request's commit and Radix re-registering, it would return the previous selection's rect for that instant, corrected on the next frame. The alternative kept the ref and required suppressing an unnecessary-dependency lint on the memo.

Radix registers a virtual anchor once per object identity, so the single
anchor the popup held could never be re-measured: nothing short of a scroll
or a resize moved it. A fresh identity per request is what asks floating-ui
to measure again, and it lets the anchor read the request it was built for
rather than a ref that existed to keep one object current.
@Azganoth Azganoth added the Bug Something isn't working label Aug 3, 2026
@Azganoth Azganoth self-assigned this Aug 3, 2026
@Azganoth
Azganoth enabled auto-merge (squash) August 3, 2026 18:35
@Azganoth
Azganoth merged commit b1d7829 into main Aug 3, 2026
2 checks passed
@Azganoth
Azganoth deleted the bug/context-popup-selection-reposition branch August 3, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Context popup keeps its position when the selection it anchors to changes

1 participant