Skip to content

Review UI: collapsible identity-aware comments rail, clean dialogs, editor spacing fixes#8

Merged
angusbezzina merged 7 commits into
mainfrom
angus/ui-tweaks
Jun 11, 2026
Merged

Review UI: collapsible identity-aware comments rail, clean dialogs, editor spacing fixes#8
angusbezzina merged 7 commits into
mainfrom
angus/ui-tweaks

Conversation

@angusbezzina

Copy link
Copy Markdown
Collaborator

Summary

Three rounds of review-mode UI work:

Comments rail (attn-d7y → attn-42y)

  • The rail is now always present in a review room: expanded (320px) or a collapsed 48px gutter, toggled from the ReviewBar dock / Cmd+J. It auto-expands only when unresolved feedback exists (one-shot per room).
  • Distinct rail chrome: sidebar background + border, cards inset 12px and fluid-width.
  • Collapsed gutter shows author-avatar chips for unresolved threads and ✓ chips for resolved ones; clicking a chip expands the rail onto that thread. Resolved threads expand to a read-only card (click/Escape collapses — no more in-card Collapse button).
  • Per-author identity: cards carry the initiating author's presence color (left border + monogram avatar) via the existing --peer-avatar-bg-* tokens (darkened to pass WCAG AA for white monograms), matching carets and peer chips.
  • Rust: share() now announces the owner via a self ParticipantJoined (fresh mint and re-Share), so owner-authored comments resolve to a real display name on every window instead of a raw participant id; frontend gains owner-name fallbacks and room-scoped name/kind maps.

Dialogs (attn-0sv)

  • Share/name dialogs fade in via CSS transition + @starting-style (no more zoom keyframes with their two documented WKWebView failure modes), are top-anchored so async content growth never re-centers them, and ShareDialog renders its final structure from open (minting → ready swaps in place; rect verified pixel-identical).

Editor spacing (attn-42y)

  • img.ProseMirror-separator guard in prosemirror.css: Tailwind preflight + content-image rules were styling ProseMirror's zero-size cursor shim as a block image with 1rem margins, inflating paragraphs ending in suggest-changes pilcrows by ~57px (the "huge gap on Enter" bug).

Layout (attn-42y)

  • The shared-document banner spans the full window width; the rail starts beneath it.

An adversarial multi-agent review pass over the diff surfaced 16 findings (4 bug-class, all fixed in-branch; 2 polish items deferred to beads attn-* follow-ups).

Verification

  • svelte-check: 0 errors; web unit tests: 32/32 files; cargo review tests: 413 passed
  • scripts/test-review-e2e.sh: 35 PASS / 0 FAIL (new collapsed/expand/avatar/toggle suites + screenshots)
  • Binary-size gate: 30.21 / 32 MiB
  • Note: the relay-based editorial E2E fails identically on a clean tree (pre-existing environment issue, not this branch)

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
attn Ready Ready Preview, Comment Jun 11, 2026 4:10pm

Request Review

angusbezzina and others added 6 commits June 11, 2026 09:27
… chips (attn-d7y)

The review right rail reserved a fixed 320px whenever the panel was open,
leaving a nearly vacant column when the margin held only resolved threads —
a single full-width dashed strip whose click just moved the cursor and
never showed the resolved content.

- New pure rail-mode derivation (closed/slim/full) in review/rail-mode.ts,
  exposed as reviewStore.railMode: resolved-only margins slim the aside to
  a 48px gutter of icon chips; the document reclaims the width.
- Resolved strips become content-sized chips ("✓ author · resolved" in the
  full rail, icon-only in the gutter). Clicking a chip expands it in place
  to the full read-only ReviewMarginCard (resolved badge, quote, body,
  replies) with a Collapse button; Escape collapses too.
- Unified the active-card and resolved-chip collision layout into one
  layoutCards pass, fixing the pre-existing overlap between the two
  independent passes and letting the expanded resolved card push its
  neighbors.
- locallyDismissed moved from ReviewMargin into the store so the rail
  slims the same tick the last active card is resolved (UI-only Reject now
  survives a panel remount for the session — intended).
- Resolved threads always get an anchor-Y (fallback 0) so the slim gutter
  never renders empty when positions can't resolve.
- Tests: rail-mode.test.ts (new), margin-integration cases for the unified
  pass/chip pitch/visibility rule, and a hard E2E suite in
  test-review-e2e.sh (slim → expand → collapse → mixed) seeded through
  window.__attn_review_store__. Design doc §3 amended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The share-link and name-prompt dialogs opened glitchily: a zoom+fade
keyframe entry scaled the dialog while it appeared (tw-animate keyframes
also have two documented WKWebView failure modes in this app), the 50%/
translate centering re-centered the whole dialog whenever its height
changed, and ShareDialog roughly doubled in height when ShareReady
swapped minting → ready.

- dialog-content/dialog-overlay: entry is now a pure opacity fade via a
  CSS transition + @starting-style (starting:opacity-0) — no keyframes,
  so it can't strand the dialog invisible and bits-ui still unmounts
  instantly on close. Content is anchored at top-[14vh] (translate-x
  centering only) with max-h-[80vh] + scroll, so late-arriving content
  grows the dialog downward instead of re-centering it.
- ShareDialog: minting and ready render the same rows — the command row
  swaps "Minting room…" for the npx command in place, the Direct-link
  card is present from open with a placeholder row, and the copy buttons
  enable when ready. Verified in the running app: rect identical
  (top 101, height 572) across the minting → ready transition. The
  share-invite-url hidden input stays gated on ready — E2E scripts use
  its appearance as the share-ready signal.
- app.css: dropped the now-dead entry-animation workaround (its keyframes
  are gone); kept and re-documented the exit-animation unmount fix for
  the surfaces that still use animate-out (sheet, command palette).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…anner (attn-42y)

Four user-reported UI fixes plus the findings of an adversarial review
pass over the diff.

Rail (always-collapsible, differentiated chrome):
- rail-mode.ts reworked to hidden/collapsed/expanded: in a review room
  the rail is always at least a 48px gutter; the ReviewBar dock gains a
  panel toggle (mirrors Cmd+J); auto-expand fires only for UNRESOLVED
  feedback, one-shot per room. Aside gets bg-sidebar + border; card
  slots inset 12px so cards no longer touch the window edge; cards are
  fluid-width. Collapsed gutter renders author-avatar chips for
  unresolved threads and ✓ chips for resolved ones (clamped below the
  dock, virtualized past 50), each expanding the rail onto its thread.
- Removed the in-card Collapse button: clicking the read-only resolved
  card (guarded against text-selection drags) or Escape shrinks it back
  to its chip; collapsing the rail clears expansion + manual-reanchor
  state, and both window-capture key handlers are gated off while
  collapsed (they otherwise survived invisibly, swallowing Escape and
  confirming reanchors).

Identity (names + colors):
- Rust: share() now announces the owner via a self ParticipantJoined
  (display name) on fresh mint AND re-Share (backfills legacy rooms,
  refreshes renames) — owner-authored comments no longer degrade to a
  raw participant id. Frontend displayNameFor adds an owner fallback
  (profile name on the owner window, 'Owner' elsewhere) and room-scopes
  participantNames; new memoized participantKinds map.
- Cards carry the author's presence color (left border + 16px monogram
  avatar) via --peer-avatar-bg-* tokens, matching carets and peer
  chips; tokens darkened so white monograms pass WCAG AA 4.5:1.
- Clicking an inline mark now expands a collapsed rail.

Editor spacing:
- prosemirror.css gains the upstream img.ProseMirror-separator guard:
  Tailwind preflight + base.css image rules were styling ProseMirror's
  zero-size cursor shim as a block image with 1rem margins, inflating
  every paragraph ending in a suggest-changes pilcrow by ~57px — the
  "huge gap on Enter" bug.

Layout:
- The shared-document banner is now a snippet rendered above a new
  content+rail row in SidebarInset, spanning the full window width with
  the rail starting beneath it; reviewChrome moved into the row so the
  ReviewBar keeps its breadcrumb alignment. Breadcrumb right-inset is
  rail-width aware. Orphan tray clears the floating dock.

Verified: svelte-check 0 errors, 32/32 web test files, 413 cargo review
tests, scripts/test-review-e2e.sh 35 PASS / 0 FAIL (new collapsed/
expand/avatar/toggle assertions + screenshots), binary-size gate 30.21
of 32 MiB. The editorial relay E2E fails identically on the clean tree
(pre-existing). CLAUDE.md dev:collab join description corrected
(daemon-routed, not --as-agent). Follow-ups filed: participantId init
plumbing for resumed-room owner names; ReviewBar dock width tuning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The margin computed anchor positions from view.coordsAtPos (viewport-
relative) but listened for scrolls on its own aside — an element that
never scrolls, since the rail is a SIBLING of the editor's scroll
container. Cards were positioned once on store changes and then froze
while the text moved underneath them, drifting away from the comments'
anchor text immediately on scroll.

- ReviewMargin now tracks the EDITOR's scroll viewport (found from
  view.dom, robust to layout changes) and recomputes positions per
  scroll tick, plus a ResizeObserver on the editor DOM for typing-
  induced reflows. Cards and gutter chips follow their anchors 1:1,
  Google-Docs style; off-screen anchors clip out of view with their
  text (collapsed chips keep position-less orphans pinned below the
  dock, but scrolled-past anchors pass through negative).
- The aside is overflow-hidden: the document scroll is the single
  source of vertical movement, so the rail can never desync by
  scrolling independently. Sticky bottom elements (resolved pill,
  reanchor overlay) became absolute; the orphan tray is plain flow.
- Dropped the rail width transition: WebKit pauses CSS transitions in
  occluded windows, which left the rail stuck mid-transition at ~1px
  (caught because every E2E screenshot went stale); width changes are
  instant and the E2E now asserts exact widths (48/320) instead of a
  ≤60 band that masked the frozen state.
- E2E: new scroll-tracking suite — scrolls the editor viewport 150px,
  asserts the card's on-screen top moves 1:1 (±4px) and returns to its
  anchor on scroll-back. 39 PASS / 0 FAIL. Verified visually in a live
  window: cards at y 237/664 moved to -13/414 after a 250px scroll,
  landing beside the exact text they annotate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eader (attn-z46)

Three pieces of user feedback on the previous rounds:

- Dialogs are viewport-centered again (top-50%/translate-y-50%). The
  top-[14vh] anchor from the dialog-glitch fix read as "not centered";
  centering is safe now because dialogs render their final structure
  from open (ShareDialog's minting → ready swap is height-stable).
  Verified: dialog center == window center exactly.
- ShareDialog right padding no longer collapses on narrow windows: the
  unbreakable font-mono file path drove the dialog's grid track wider
  than its padding box (overflow-x-hidden then clipped every w-full
  child flush to the border). The path now break-alls. Verified:
  24px/24px symmetric padding.
- The comments rail starts at the underside of the header strip (mt-12)
  instead of running the full window height, and owns its toggle in a
  rail-header row — centered in the collapsed gutter (was the off-center
  dock button, now removed from ReviewBar), right-aligned when expanded.
  With the dock no longer overlapping the rail, the chip clearance
  dropped 56 → 8 and became viewport-aware: chips whose anchors are
  still on screen (including the header band above the rail) pin into
  the gutter; only anchors scrolled above the window clip away with
  their text. Verified: rail top 47px, toggle/chip centered ±1px;
  E2E 40 PASS / 0 FAIL including a new toggle-centering assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dismissal, rail padding (attn-2aj)

- Resolved chips carry the thread author's presence color: border +
  16px monogram avatar on the labeled variant; the gutter icon variant
  takes the color via a CSS custom property so its hover foreground
  flip still works.
- New shared composer-keys helper: plain Enter submits, Shift/Alt+Enter
  inserts a newline, Cmd/Ctrl+Enter keeps working. IME-safe for the
  engine we actually ship on: WebKit fires the candidate-confirm Enter
  AFTER compositionend with isComposing=false, so the helper also bails
  on keyCode 229 (blocker caught by the adversarial review pass — the
  isComposing-only guard would have submitted half-composed CJK text).
  Auto-repeat Enter is rejected, and both composers gained an in-flight
  submitting flag so duplicates can't fire once IPC gains real acks.
- All authoring textareas (margin reply, comment composer, suggestion
  replacement/insert/note): rows=4 minimum, resize handle removed.
  The three-way apply editor drops its resize handle too but keeps
  Enter as newline (it edits multi-line replacement text).
- SuggestionComposer no longer dead-keys Enter while the form is
  incomplete (gate hoisted above preventDefault so it falls through to
  a newline); CommentComposer regained dialog-level Cmd+Enter parity;
  Escape during IME composition no longer wipes drafts.
- The Comment|Suggest selection toolbar dismisses after a comment or
  suggestion is CREATED: a submit-only callback collapses the editor
  selection (the load-bearing part — toolbar state alone gets re-derived
  from the still-live selection) and is guarded against a mid-compose
  file switch dispatching into the wrong editor view. Cancel/Escape
  keep the selection for a retry.
- Rail breathing room: cards/chips whose anchors are on screen clamp
  8px below the rail header instead of sitting flush against it;
  anchors scrolled above the window still clip away 1:1 with their
  text. E2E re-anchored its scroll-tracking thread deeper to stay out
  of the pinning band; 41 PASS / 0 FAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The update nudge ("attn X is available") and other toasts had no
dismiss affordance — users had to wait out the 12s timeout. sonner's
closeButton renders an ✕ on every toast.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@angusbezzina angusbezzina merged commit fb629e3 into main Jun 11, 2026
6 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