Files
buzz/desktop
ss-dev-00 90f526be62 feat(desktop): fold focus-mode agent work into one transcript block
Slice C. Focus mode gave every thought and tool step its own row and its own
disclosure, so a turn that did real work scrolled its answer off screen behind
a stack of chrome. This adopts berd's shipping transcript model: within a turn,
everything between the prompt and the answer that is thinking, a tool step, or
an interim agent note becomes one "work block" — a thin rail of small glyph
bullets that folds to a single "N steps" line when the work is done.

Grouping is variant-aware **at the list boundary** rather than inside the
grouping module: `TranscriptDisplayBlockView` runs the additive work-block
transform only for `conversation`, so `default` and `compactPreview` walk the
same segments on the identical code path they always did. That makes slice B's
byte-for-byte baseline fixture hold by construction rather than by assertion —
no future edit to the block can regress the other two variants without first
moving that branch.

Behaviour, mirroring berd:

- Live: the block is open and the rail IS the status, so there is no header
  line restating it. The last three steps show in arrival order; older ones go
  behind an "N previous steps" disclosure, so a long run cannot push the answer
  out of view.
- Finished: folds to "N steps" with a chevron that rotates on open.
- The fold animates. `<details>` cannot — its content is either laid out or
  not, with no intermediate height — so a block that was live when it mounted
  renders open for a paint and then settles closed, giving the height tween a
  start state that was actually painted. A block already finished on mount
  (scrollback) never had a rail on screen and closes immediately.
- Reader choice wins: once the reader toggles a block, policy stops opening and
  closing it.

One deliberate departure from berd: a finished block holding a failure folds to
"N steps · 1 failed". A bare count is the one thing a reader cannot distinguish
a clean run from a broken one by, and a fold that hides a failure behind a
neutral number invites them not to open it. The rail bullet itself stays muted —
failure is a glyph shape, not a colour, so one bad step does not read as an
alarm across the run.

Items are projected once into rail entries, and the glyph, the body, and the
folded line's counts all read that projection; two independent classifications
of the same item is how a headline and chain eligibility drifted apart on an
earlier attempt. The projection is closed in the type system rather than by
convention, so a future `TranscriptItem` variant cannot silently wear a wrench
and impossible pairs are not representable.

Four defects fixed on top, each mutation-checked in isolation:

- **A reader's open block was folded shut by the agent posting again.** Work
  blocks are derived and ided after their first step, and only the LAST
  assistant message is exempt from the block — so a second answer demotes the
  first, the runs on either side merge, the old block unmounts, and the
  `useState` holding the reader's expansion goes with it. The choice now lives
  above the block, keyed by the STEP ids it was taken on, since the steps
  survive regrouping. An open choice beats a folded one (hiding steps a reader
  asked for loses what they were reading; showing extra costs a scroll), and
  the choice is recorded against every step in the block — recording only the
  first leaves the absorbed block's stale `open` entry behind and, since open
  wins, the merged block could never be folded again.
- **A finished block went live again between turns.** `lastTurnId` walked the
  display blocks, but a turn that has only emitted setup lifecycle rows
  classifies to zero segments and produces no block — so for the whole gap
  between `turn_started` and the next turn's first renderable item, "newest
  turn with a block" was the turn that had already ended, and its trailing item
  became the streaming item: a settled 6-step block re-opened, dropped to its
  last three steps, then folded back. Every turn, not an edge case. Liveness
  now comes from the newest turn id in the item stream, and a trailing item is
  only reported as streaming when the live turn owns it.
- **The running step's pulse ignored reduced motion.** `WorkBlockRailGlyph`
  used bare `animate-pulse`, which no `prefers-reduced-motion` block in the app
  matches — all 20 are scoped to `buzz-*`/`motion-*`/`t-skel-*`. Now
  `motion-safe:animate-pulse`. That swap also repaired three assertions it
  would otherwise have disarmed: they matched `.animate-pulse` as a substring
  and, worse, all three ran on a folded rail with no rows, so "nothing pulses"
  held against a build where abandoned steps pulse forever.
- **A work block whose running step had no live session never settled.**

Two test-hygiene fixes worth keeping: a gap test was pinning a sequence whose
`session/new` card moved `streamingItemId` on its own, so those frames passed
against the pre-fix code and proved nothing (the plain no-restart path now
leads); and the orphaned suite inherited React Query's default 300000ms
`gcTime`, arming a five-minute timer at teardown that node:test waited out —
`gcTime: 0` takes the full desktop suite from 307s to 102s, verified against a
negative control rather than on the passing tree alone.

Squashed from ss-dev-02/tool-chain-cards (#6536) at 91a5754ad.

Co-authored-by: ss-dev-02 <9e916f802f7932c38e630ac6b4726f5db7ca326c0d98e9bab495309eef13fe5a@buzz.block.builderlab.xyz>
Co-authored-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: ss-dev-00 <a02c4e0850e5e612b4ddf95dbe2f5c56467cf27c6552203bc833ff438fb31971@buzz.block.builderlab.xyz>
2026-08-26 15:28:48 -07:00
..
2026-08-03 21:51:17 -04:00
…

Buzz

Desktop chat shell with:

  • Tauri + React + TypeScript + Vite
  • Tailwind CSS
  • shadcn/ui-ready shared components
  • Biome (lint/format/check)
  • Feature-driven frontend structure

Scripts

  • pnpm dev - run the web frontend
  • pnpm tauri dev - run the desktop app
  • pnpm build - typecheck and build frontend
  • pnpm typecheck - TypeScript checks
  • pnpm lint - Biome lint
  • pnpm format - Biome format (write)
  • pnpm check - Biome check

Structure

  • src/shared - reusable app-wide code (ui, lib, styles)
  • src/features - feature modules (vertical slices)
  • src/app - top-level app composition