feat(desktop): add focused thread mode#2108
Merged
Merged
Conversation
A thread is where the work actually happens, but it opened into a 380px
right rail it shared with three other panels — one of four interchangeable
occupants of a peek slot. At 100+ messages of humans and agents working
back and forth, that reads as secondary to the channel that merely holds
its root message.
Focus mode makes the thread a place. A right-anchored drawer overlays the
channel content area, leaving a 72px scrim-dimmed sliver of the channel
visible: orientation ("the channel is where you are, the thread is where
you're focused") plus a tall click target back. The sidebar is never
covered, so channel-hopping stays live.
This is additive. The split pane is preserved exactly and is one setting
away; focus is the default. An Appearance setting picks between them —
in-channel toggling comes later, once the surface itself has settled.
Notes on the pieces:
- Scrim fades toward `background`, not black. A black wash is a multiply:
it scales text and surface down together, so dark-on-light text keeps
its contrast ratio and stays readable at any opacity short of a solid
bar. Fading toward the surface colour compresses text against it
instead.
- `shadow-panel-left` is a new token. Tailwind's stock shadows are all
y-offset, so they cast nothing on the drawer's only exposed edge. Both
layers run -x, following the precedent already set by `content-edge`.
A left-only border cannot do this job: it tapers out at each rounded
corner instead of turning it.
- Enter/exit decouple opacity from transform, which a single CSS keyframe
pair cannot. Front-loading the fade leaves the rest of the enter as pure
travel — and stops the drawer's own entrance from exposing its contents'
load order. `AnimatePresence` keeps the drawer mounted through its exit.
- `MessageThreadPanelSkeleton` is extracted because the change pushed
`MessageThreadPanel` past the file-size guard.
- The screenshot helper gains `--local-storage` and `BUZZ_HEADED=1`.
Headless Chromium's software rasterizer mis-renders `backdrop-filter`
under a rounded clip, which the drawer now has; WKWebView is unaffected,
so this is capture-only.
Co-Authored-By: Claude <noreply@anthropic.com>
* origin/main: Route lag-tolerant reads to an optional Postgres read replica (#2084) Polish community profile onboarding UI (#2088) fix(cli): report presence subject (#2104) test(desktop): fix Escape-before-mount race in channel browser e2e (#2101) style(desktop): match PendingInviteGate to startup interstitials (#2097) Remove additional agents gallery (#2098) fix(agents): stop the reply loop and the premature kickoff closer (#2094) chore(release): release Buzz Desktop version 0.4.18 (#2091) fix(desktop): recover first community joins (#2087) fix(chart): support CPU-only relay autoscaling (#2086) feat(chart): add relay horizontal autoscaling (#2077) fix: recover community access visibility (#2074) chore(release): release Buzz Desktop version 0.4.17 (#2082) fix(desktop): own native WebSocket lifecycle (#2076) fix(desktop): onboarding defaults — API key field and model search (#2081) feat(desktop): show agent owners in messages (#2080) fix(desktop): restore onboarding provider choices (#2079) Fix Claude onboarding terminal popup (#2078)
morgmart
enabled auto-merge (squash)
July 19, 2026 04:20
wesbillman
approved these changes
Jul 19, 2026
wesbillman
left a comment
Collaborator
There was a problem hiding this comment.
Approving on Wes's behalf after full review in Buzz.
- CI fully green: all smoke + integration E2E shards, Desktop Core/Build, DCO; branch merged with latest main.
- Diff matches the stated intent; desktop-only. The one shared-code risk —
useAnchoredScroll's mount pin now scrolling in the layout effect with an rAF settling pass (affects every timeline, not just threads) — is covered by the passing scroll-history/timeline-no-shift/virtualization smoke specs, and the unit test was strengthened to assert the new first-frame pin rather than weakened. - Solid details: escape-surface counter is fail-safe (idempotent release, tested); split stays the default so existing users see no change; the new spec runs in the smoke project (2 tests, non-vacuous); the skeleton extraction is a real file split rather than a size-guard bump.
Non-blocking nits: useThreadViewModeSwitch locates the thread body via a global data-testid query (DOM coupling — fine while only one thread panel mounts), and the animation-timing comment blocks are much chattier than repo norm.
Reviewed-by: Brain (Buzz agent) on behalf of Wes
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
Testing
cd desktop && pnpm checkcd desktop && pnpm typecheckcd desktop && pnpm test(3,164 tests)cd desktop && pnpm exec playwright test tests/e2e/thread-focus-mode.spec.ts --project=smoke(2 tests)Screenshots
Screen.Recording.2026-07-18.at.7.42.18.PM.mov