fix(desktop): make header chrome zoom-correct and tidy split-pane#941
Draft
tellaho wants to merge 8 commits into
Draft
fix(desktop): make header chrome zoom-correct and tidy split-pane#941tellaho wants to merge 8 commits into
tellaho wants to merge 8 commits into
Conversation
7b20477 to
82bb9fa
Compare
16b1df0 to
40c251b
Compare
tellaho
added a commit
that referenced
this pull request
Jun 11, 2026
- Drop inner data-testids in split layout so RightAuxiliaryPane's wrapper testid is the only match (fixes Playwright strict-mode violations across messaging/profile/channels/mentions specs) - Update messaging.spec.ts to the renamed right-auxiliary-pane-resize-handle testid - Restore standalone top padding (pt-[4.75rem]) in MessageThreadPanel's scroll region so single-panel thread content clears the overlay header - Return a callback ref from useMeasuredCssVariable so the measurement re-runs when the header mounts inside the lazy ChannelPane (cold-load no longer sticks at the 5.75rem fallback) - Pad ForumView with the measured channel chrome variable; the overlay header was intercepting clicks on "Start a new post..." (regression introduced by this branch, caught by file-attachment.spec.ts) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tellaho
added a commit
that referenced
this pull request
Jun 11, 2026
- Drop inner data-testids in split layout so RightAuxiliaryPane's wrapper testid is the only match (fixes Playwright strict-mode violations across messaging/profile/channels/mentions specs) - Update messaging.spec.ts to the renamed right-auxiliary-pane-resize-handle testid - Restore standalone top padding (pt-[4.75rem]) in MessageThreadPanel's scroll region so single-panel thread content clears the overlay header - Return a callback ref from useMeasuredCssVariable so the measurement re-runs when the header mounts inside the lazy ChannelPane (cold-load no longer sticks at the 5.75rem fallback) - Pad ForumView with the measured channel chrome variable; the overlay header was intercepting clicks on "Start a new post..." (regression introduced by this branch, caught by file-attachment.spec.ts) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a0b5052 to
4fa1983
Compare
Convert arbitrary px Tailwind values in header chrome to rem-based units so layout respects browser zoom (verified at 100%, 125%, 150%). Also lands the structural cleanup that fell out of the fix: - Introduce RightAuxiliaryPane wrapper that owns width/border/resize; inner panels render content + slim split header (single responsibility). - Centralize chrome layout helpers in shared/layout (chromeLayout.ts, TopChromeInsetHeader, AuxiliaryPanelHeader, MainInsetContext, plus measurement utilities for CSS-variable-driven sizing). - Normalize header action icon sizing. - Remove split-pane animation residue and dead in-panel resize buttons. - Drop unreachable pt-[4.75rem] clause and orphaned peer-hover selectors. - Make UserProfilePanel resize props optional (Pulse path stays alive). Squashed from 14 iteration commits onto latest main. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Update the shared Button primitive to use rounded-lg controls, tighter icon/text spacing, and a consistent default SVG size. - Make outline buttons shadowless with softer `border-input/40` borders and muted hover backgrounds. - Align Input and Textarea with the same rounded-lg, shadowless, background-backed control treatment. - Align Toggle radius and outline styling with the updated shared control language. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Keep chromeLayout focused on layout geometry by removing component-specific header button and title style exports. - Add auxiliary panel header content primitives for title and action grouping while keeping content padding tied to channelChrome layout values. - Convert channel and huddle header actions to generic outline buttons instead of chrome-specific override classes. - Reuse the same thread, activity, and profile panel header content across split and single-pane shells. - Keep panel close controls borderless with ghost buttons while preserving outline affordance for navigation actions. - Let MemoryRefreshButton receive its visual variant from the owning panel header instead of encoding panel layout styling itself. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Move chrome CSS variable names and default values into shared chrome layout objects - Apply main inset chrome defaults through the shared inline style object in AppShell - Reuse the shared channel content padding measurement object in ChannelScreen - Keep Tailwind chrome class fragments static so generated classes remain discoverable Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Drop inner data-testids in split layout so RightAuxiliaryPane's wrapper testid is the only match (fixes Playwright strict-mode violations across messaging/profile/channels/mentions specs) - Update messaging.spec.ts to the renamed right-auxiliary-pane-resize-handle testid - Restore standalone top padding (pt-[4.75rem]) in MessageThreadPanel's scroll region so single-panel thread content clears the overlay header - Return a callback ref from useMeasuredCssVariable so the measurement re-runs when the header mounts inside the lazy ChannelPane (cold-load no longer sticks at the 5.75rem fallback) - Pad ForumView with the measured channel chrome variable; the overlay header was intercepting clicks on "Start a new post..." (regression introduced by this branch, caught by file-attachment.spec.ts) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The overlay header (negative-margin + measured CSS var) only had its padding counterpart in MessageTimeline and ForumView's list. Sweep the rest of the bodies that render beneath it: - ForumThreadPanel: pad both the loading and loaded roots; the "Back to posts" bar and post head rendered under the header - ViewLoadingFallback: pad the forum skeleton like the channel one - ChannelFindBar: float it below the measured chrome (absolute + channelChrome.top); it was fully hidden behind the header band whenever cmd-F opened it - chromeLayout: add a channelChrome.top fragment for absolute offsets below the measured header Verified with screenshot probes against a freshly served build on an isolated port (another dev server was squatting on 4173, serving a stale worktree build). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route-level loading fallbacks rendered their header skeleton at the very top of the pane — behind the global search chrome — while the channel body skeleton still reserved the full overlay-header height, leaving a dead gap above the message rows. The header skeleton now flows through TopChromeInsetHeader like real inset headers, and the channel/forum bodies only reserve the measured chrome height when no in-flow header skeleton sits above them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4fa1983 to
1901210
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Overview
Category: improvement
User Impact: Header chrome in the desktop app now scales correctly at every zoom level (100% / 125% / 150%), and every view that sits beneath it — forum lists, forum threads, single-pane message threads, the ⌘F find bar — renders below the header instead of hiding (or becoming unclickable) underneath it.
Problem: Channel, thread, profile, and agent-session headers were sized with arbitrary px Tailwind values (
h-[92px],pt-[48px],h-[76px], …) that don't respect zoom, so at 125% / 150% headers drifted out of alignment with their backdrops. The header also overlays content (negative margin + translucent backdrop), and several bodies never compensated for it: forum content rendered under the header and intercepted clicks, the find bar opened fully hidden behind it, and single-pane threads lost their top clearance.Solution: Drive header chrome from a small set of shared layout primitives — the real chrome height is measured into a CSS variable (
useMeasuredCssVariable, via a callback ref so lazily-mounted headers still trigger measurement on first open) and every body beneath the header clears it with the samechannelChromefragments. Split-pane mode gets a realRightAuxiliaryPanewrapper that owns width / border / resize / testid, with inner panels reduced to content + a slim split header. Heads-up: this branch also softens the shared form controls (button / input / textarea / toggle — icon buttons 36→32px,rounded-md→rounded-lg, opaque input backgrounds), so it is not pixel-identical to main; the restyle is intentional andAgentSessionThreadPanel's simplified buttons depend on it.Changes
File changes
desktop/src/shared/layout/chromeLayout.ts (new)
Single source of truth for chrome CSS variables (
--sprout-top-chrome-height,--sprout-channel-content-top-padding), their defaults, and the Tailwind class fragments (topChromeInset,topChromeBackdrop,channelChrome— padding, height, negative margin, and absolute top offset) every consumer shares.desktop/src/shared/layout/useMeasuredCssVariable.ts, observeElementBlockSize.ts (new)
Hook that observes an element's block size and writes it as a CSS variable on a target element. Returns a callback ref instead of taking a ref object, so a header that mounts inside a lazy subtree (first-ever channel open) still triggers measurement instead of silently sticking at the fallback value.
desktop/src/shared/layout/MainInsetContext.tsx, TopChromeInsetHeader.tsx, AuxiliaryPanelHeader.tsx (new)
Shared targets and building blocks: the main-inset element that receives the measured variable, the standard header strip below the system chrome, and the slim split-pane panel header plus the content-padding class inner panels use to clear it.
desktop/src/features/channels/ui/RightAuxiliaryPane.tsx (new)
Split-pane wrapper that owns the right panel's width, divider, resize handle (
right-auxiliary-pane-resize-handle), and the panel testid — inner panels no longer duplicate any of it.desktop/src/features/chat/ui/ChatHeader.tsx, AppTopChrome.tsx, AppShell.tsx, TopChromeBackdrop.tsx
Replace the px constellation with the shared rem/measured fragments; the header backdrop overlay now derives from
chromeLayoutinstead of hard-coding its own heights. Drops thetop-rightactions portal in favor of inline placement.desktop/src/features/channels/ui/ChannelScreen.tsx, ChannelScreenHeader.tsx
Wire the measurement: the screen passes the hook's callback ref into the header chrome wrapper and applies the measured variable to the main inset, keyed by channel and disabled in single-pane view.
desktop/src/features/channels/ui/ChannelPane.tsx
Composes the split layout through
RightAuxiliaryPane(testids live on the wrapper only) and floats the ⌘F find bar just below the measured chrome — it previously rendered at the top of the pane, fully hidden behind the header band.desktop/src/features/messages/ui/MessageThreadPanel.tsx, AgentSessionThreadPanel.tsx, profile/ui/UserProfilePanel.tsx
Each panel splits into a slim split-mode body vs. a standalone
<aside>; split mode defers chrome to the wrapper, standalone keeps its own. Single-pane top clearance is restored (pt-[4.75rem]) so thread content no longer slides under the overlay header on narrow widths.desktop/src/features/forum/ui/ForumView.tsx, ForumThreadPanel.tsx
Forum list, composer, and thread (including its loading state) now clear the measured chrome. Before, the overlay header sat on top of "Start a new post…" and the "Back to posts" bar — visually clipped and click-intercepted.
desktop/src/features/messages/ui/MessageTimeline.tsx, useComposerHeightPadding.ts
Timeline content clears the measured chrome via the shared fragment; composer height padding follows the same rem-based scheme.
desktop/src/shared/ui/ViewLoadingFallback.tsx
Loading skeletons honor the chrome: the header skeleton flows through
TopChromeInsetHeader(it used to render behind the global search strip), and channel/forum body skeletons only reserve the measured header height when no in-flow header skeleton sits above them — so the skeleton matches the loaded layout instead of leaving a dead gap.desktop/src/shared/ui/button.tsx, input.tsx, textarea.tsx, toggle.tsx, sidebar.tsx
The intentional control softening: icon buttons 36→32px with 18px icons,
rounded-md→rounded-lg, inputs/textareas get opaque backgrounds, outline variant softened. App-wide visual change — see Overview.desktop/src/features/home/ui/HomeView.tsx, InboxDetailPane.tsx, InboxListPane.tsx, FeedSection.tsx, HomeChannelActions.tsx (deleted)
Home/inbox panes consume the shared chrome fragments instead of local px offsets; the standalone channel-actions portal component is no longer needed with inline header actions.
desktop/src/features/channels/ui/ChannelMembersBar.tsx, agent-memory/ui/MemorySection.tsx, huddle/components/HuddleIndicator.tsx, messages/ui/ComposerEmojiPicker.tsx, MessageComposerToolbar.tsx, settings/UpdateIndicator.tsx
Smaller consumers updated to the shared fragments and the new control sizing.
desktop/tests/e2e/messaging.spec.ts
Resize-handle assertions target the renamed
right-auxiliary-pane-resize-handleon the wrapper, whose width is what the size assertions measure.desktop/tests/helpers/zoom-shot.mjs (new)
Screenshot helper for zoom comparisons (
--port,--zoom,--open-thread/--open-profile/--open-agent).Reproduction Steps
cd desktop && pnpm test:e2e:smoke— 178/178 passing, including the previously-broken forum file-attachment spec.Screenshots/Demos
Zoom-alignment comparisons can be generated with the new helper:
node tests/helpers/zoom-shot.mjs --zoom 1.5 --open-threadagainst a built bundle (--portto compare baseline vs. branch).