Mask assignment popup floats next to the drawn box - #87
Merged
Conversation
… of the page MaskAssignmentMenu rendered in the Masks section, well below the image -- after drawing a box near the top of a tall image, the popup asking what to do with it appeared far out of view, disconnected from the point of interaction. Moves it into InteractiveImagePane as an absolutely-positioned popup anchored just below-left of pendingRect, reusing the exact natural-to-displayed scaleX/scaleY math already used to place saved mask rects -- no new positioning logic, just the same established scaling applied to one more rect. Tracks the actual on-screen box regardless of image size or dual/single view mode. Verified for real, not just read -- jsdom has no layout engine, so this class of change can't be meaningfully checked by the unit suite (all 55 tests pass unchanged, since they only assert on roles/testids, never DOM position). Built and ran the real backend+viewer under Podman, drove the actual drag-to-draw flow in a live browser, and screenshotted the popup landing correctly next to boxes drawn both near the top and near the bottom of the image -- confirmed no clipping either case, since nothing in the ancestor chain clips overflow. The real e2e suite (which exercises this exact draw flow) also passes 8/8 against the changed markup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4
Follow-up on the same popup this branch already repositioned:
"Save as global mask" / "Save as mask for this snapshot" / "Cancel"
were full-sentence buttons, needlessly wide now that the popup floats
directly over the image instead of sitting in its own row below it.
- "Save as global mask" -> "+global", "Save as mask for this snapshot"
-> "+unique" -- both keep their full original text as aria-label, so
every existing getByRole("button", {name: "Save as..."}) query (unit
+ e2e) still resolves unchanged. The "+" prefix also now matches the
sibling "+ New category" button's own existing convention, instead of
introducing a third distinct button-labeling style into one menu.
- Cancel becomes a small solid-red circular "x", aria-label="Cancel",
mirroring the same icon-button pattern already used for Approve
(aria-label + glyph) and the masks chip row's remove control.
Verified for real: all 55 unit tests pass unchanged (proving the
aria-label swap preserved every existing role-based query); real
Docker/Podman + Playwright screenshot confirms the compact row actually
renders as intended, not just that the accessible names resolve; real
e2e suite 8/8 against the changed markup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4
stolau
pushed a commit
that referenced
this pull request
Jul 27, 2026
…flict Three PRs landed on main after this branch forked (#86 self-hosting, #87 mask popup, #88 six more e2e stories), two of which touched the same examples/demo-app/CODEMAP.md paragraph this branch also edited. index.html and style.css merged cleanly with no conflict; CODEMAP.md's prose needed manual reconciliation -- kept main's up-to-date "fourteen tests" description and spliced this branch's .thumbs gallery sentence back into the site/ bullet it belongs to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4
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
The mask assignment popup (Global / This snapshot / category buttons) rendered in the Masks
section, well below the image -- after drawing a box near the top of a tall image, the popup
asking what to do with it appeared far out of view.
Moves it into
InteractiveImagePaneas an absolutely-positioned popup anchored just below-left ofthe drawn box, reusing the exact
scaleX/scaleYmath already used to place saved mask rects --no new positioning logic. Tracks the box regardless of image size or dual/single view mode.
Test plan
npm run lint --workspacescleannpm test --workspaces-- 55 viewer tests pass unchanged (they assert roles/testids, not DOMposition, so this alone doesn't prove the fix -- see below)
meaningfully check CSS positioning: built and ran the real stack under Podman, drove the
actual drag-to-draw flow in a live browser, screenshotted the popup landing correctly next
to boxes drawn both near the top and near the bottom of the image, confirmed no clipping
npm run test:e2e -w examples/demo-app-- 8/8, including the two tests that exercise thisexact draw flow
viewer/CODEMAP.mdupdatedCo-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4