Skip to content

Redesign snapshot detail: image-centered layout, checkmark approve, masks as hashtags - #82

Merged
stolau merged 1 commit into
mainfrom
snapshot-detail-redesign
Jul 27, 2026
Merged

Redesign snapshot detail: image-centered layout, checkmark approve, masks as hashtags#82
stolau merged 1 commit into
mainfrom
snapshot-detail-redesign

Conversation

@stolau

@stolau stolau commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up UX pass on the snapshot detail view, prompted by two observations:

  1. The image was competing for space with a full-width Approve button, a
    two-row header, and an always-open History section.
  2. Masks (global, per-image, category scopes all exist on the backend) were
    only ever visible as overlay rectangles on one specific snapshot's own
    candidate image -- nowhere else, on that view or any other, was there any
    indication a snapshot had masks applied at all.
  • Header line: status pill + category field, compact.
  • One control line directly above the image: Dual/Single, Show diff,
    Baseline/Candidate tabs.
  • Approve is now a small circular checkmark pinned to the top-right of the
    image area (covers both panes in Dual view), colored via the same
    statusStyles().dot the status pill uses -- amber/red/green/grey -- and
    disabled once status === "pass". This is a deliberate tradeoff: approve
    on pass isn't actually a no-op server-side (compare()'s MAX_DIFF_RATIO
    tolerates ~0.1% drift, so it can still re-pin the baseline), but a simpler
    "green = nothing to click" affordance won out over keeping that reachable
    from this button.
  • Masks now render as a row of hashtag-style chips below the image
    (#global, #this image, #<category>), color-dotted by scope, with a
    remove control only when the mask's id is actually resolvable -- mirrors
    the overlay's own pre-existing limit rather than inventing a new one.
  • History is now a collapsed-by-default <details> section.

Process

Went through two architect SOLID-gate rounds:

  • Round 1 CONCERNS: the chip row's delete control couldn't always resolve a
    per-image mask's id (no backend endpoint lists them), and the original
    "disable Approve on pass" reasoning rested on a false "it's a no-op"
    premise. Both fixed in the design before implementation.
  • Round 2 SOUND.

Real Docker + Playwright verification against the actual built image caught
a bug neither review nor the unit suite could have: in Single view the
checkmark floated far right of the image, because its positioned ancestor
was a full-width flex row rather than shrink-wrapped to the visible pane.
Fixed by nesting the image content in its own relative div, centered by an
outer flex justify-center, so the checkmark anchors to the pane's real
rendered width in both view modes. Confirmed visually after the fix.

Test plan

  • Both architect-flagged fixes mutation-tested (reverted, confirmed the
    dependent test fails, restored)
  • New tests: checkmark disabled on pass / enabled on fail and
    approved-baseline-missing; chip row scope labels + remove-only-when-
    id-known (60 viewer tests total, up from 55)
  • npm run lint --workspaces clean
  • npm test --workspaces -- 36 client + 60 viewer, all passing
  • npm run test:e2e -w examples/demo-app -- 6/6, live-browser run through
    Approve/masks/category flows against this exact redesigned markup
  • backend ruff check + pytest -- 141/141 (no backend changes)
  • Real Docker build + Playwright screenshots of dual view, single view,
    expanded history, and the disabled green checkmark state
  • viewer/CODEMAP.md updated

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4

…asks as hashtag chips

The image was competing with a full-width text Approve button, a two-row
category/status header, and an always-open History section for vertical
space, and masks -- despite three real backend scopes (global, per-image,
category) -- were only ever visible as overlay rectangles while looking
at one specific snapshot; there was no indication anywhere else, on that
snapshot or any other view, that masks existed at all.

- Status pill + category field collapse onto one compact header line;
  view-mode controls (Dual/Single, Show diff, Baseline/Candidate tabs)
  collapse onto one line directly above the image.
- Approve becomes a small circular checkmark pinned to the top-right of
  the image area (the whole comparison container in Dual view, not
  nested in either pane) instead of a full-width button below it, colored
  via the same statusStyles().dot the status pill already uses --
  amber/red/green/grey -- and disabled once status is "pass". Confirmed
  with the author this is a deliberate tradeoff: approving a passing
  snapshot isn't actually a no-op server-side (compare()'s
  MAX_DIFF_RATIO tolerates ~0.1% drift, so it can still re-pin the
  baseline), but a simpler "green = nothing to click" affordance won out
  over keeping that reachable from this button.
- Masks render as a row of Instagram-style hashtag chips below the
  image (#global, #this image, #<category>), color-dotted by scope
  (categoryColor() for category scope, two new fixed constants for the
  other two), with a remove control only when the mask's id is actually
  known -- mirrors the on-image overlay's own pre-existing
  known-id-only-delete limit exactly (resolveMaskIds(), called a second
  time against the same inputs) rather than inventing a second,
  inconsistent notion of "unknown mask."
- History becomes a collapsed-by-default <details> section.

Design went through two architect SOLID-gate rounds (round 1 CONCERNS:
the chip row's delete affordance couldn't always resolve a per-image
mask's id, and the original "disable Approve on pass" justification
rested on a false "it's a no-op" premise; both fixed -- round 2 SOUND).
Real Docker + Playwright verification against the built image caught one
further bug neither review nor the unit suite could have: in Single
view the checkmark floated far right of the actual image, because its
positioned ancestor was a full-width flex row instead of shrink-wrapped
to the visible pane. Fixed by nesting the image content in its own
`relative` div, itself centered by an outer `flex justify-center`, so
the checkmark's absolute positioning anchors to the pane's real
rendered width in both view modes.

Verification: both architect-flagged fixes mutation-tested (reverted,
confirmed the dependent tests fail, restored); new tests added for the
disabled-on-pass checkmark and the chip row's scope labels/remove
availability (60 viewer tests total, up from 55); full root-level
lint/test/e2e suite green, including the real e2e suite's live-browser
drive through Approve, masks, and category flows against this exact
redesigned markup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4
@stolau
stolau merged commit 8fb7d06 into main Jul 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant