Agent chat: even transcript spacing, meta rows that hug their message

Merged
TE
16d ago
+458-29

#22675 has landed. feat-cmux-next 78aaa62 is merged in (merge 537c356). Out of scope here: SteerMark, the prompt's receipt, and when the action rows show. #22773 owns those, and this PR leaves them alone.

Problem

The agent chat transcript has empty bands (Leo: "weird amounts of negative space"):

  • An older prompt's action row is hidden with opacity: 0 but still takes 4px of margin and a 20px line, and the row's 16px of padding sits under it. That makes about a 40px band under the bubble before the next message.
  • A reply's footer (time, Copy, Retry) starts about 22px under the reply. That is the reply row's 16px padding plus a 28px row whose content is centered in it.
  • A footer sits only 8px above the next prompt, so turns don't read as turns.
  • The row estimates in model.ts left out the prompt's action row and its steer line, and put the footer row at 37px against 36px drawn. Rows jumped when they first drew.

Change

Four spacing tokens in conversation.css set the rhythm. model.ts estimates rows with the same numbers.

TokenValueGapSource
--cv-meta-gap4pxa message to its meta row (prompt actions, reply footer)60.png, Leo's Claude Code desktop capture: the meta row starts about 6px under the bubble at 1x. Rounded down to a 4px step.
--cv-meta-row24pxthe meta row's line60.png: the ✓ and copy row is about 24px tall at 1x.
--cv-message-gap8pxa message (or its meta row) to the next row in its turn60.png: the meta row's center sits 26px above the "5m 54s" line's center, so the gap is about 4–8px. 8px matches the existing step-row padding.
--cv-turn-gap24pxa turn's footer to the next promptInferred. Neither 60.png nor any published Anthropic screenshot shows a turn boundary. The desktop docs and download page have no transcript images. 24px is one meta row, three message gaps.

Tool blocks keep their margins: the 2px .cv-tools margin, the 26px step rhythm, and the 8px padding of step rows.

  • .cv-user__actions gets a fixed 24px height and a 4px top margin. Hidden, it keeps only that line. Its visibility rules are unchanged.
  • A reply directly above its footer gets acpmux-row--before-footer: 4px padding and no 34px minimum height, so a one-line reply's footer also hugs it. The footer row drops the 34px minimum, sits on a 24px line, and ends with the 24px turn gap. Its buttons are 24px.
  • Message rows pad 8px instead of 16px.
  • User bubbles are translucent: the theme's text at 12% over transparent. Before, the fill was mixed into the opaque --acpmux-base. The composer hairline rim stays. The fill and rim sit in one rule block, apart from the bubble's padding, radius and width. The shared glass primitive (#22815, ui-glass) replaces that block once it lands. agent-pane-chat-column.test.ts asserted an opaque fill (alpha 255); it now asserts a translucent one (0 < alpha < 255).
  • A gallery fixture, agent-pane.transcript / rhythm, shows two prompts in one turn, a footer, the next turn and a Worked for turn.

The plan had a reserve-or-overlay Debug toggle for the hidden row. I dropped it along with the SteerMark change.

Screenshots

Captured from the gallery builds of feat-cmux-next f6d9065 (before) and this branch at 98e81fe (after). Playwright at 2x, pointer away.

BeforeAfter

The new rhythm fixture, after only (dark, light):


Light theme bubble over the page:

Changelog

  • Fixed: Agent chat transcript spacing: a message's actions and a reply's footer sit right under it, turns are evenly spaced, and prompt bubbles are translucent.

Observed / Not run

Test: webviews/test/agent-pane-transcript-spacing.test.ts (Playwright, real Chromium). It measures:

  • the bubble to its meta row;
  • an older prompt's bubble to the next row;
  • the prompt's meta row to Worked for;
  • a reply to its footer's text;
  • a footer to the next prompt.

Ran:

  • scripts/web-lint.sh on each commit. Format and lint have 0 errors. vp check exits 1 on two jsx-a11y/prefer-tag-over-role warnings at UserBubble.tsx role="img", which are on feat-cmux-next too.

  • Fleet cmux-ci run --class light --script scripts/ci/run-webviews-tests.sh on 5b159fa (test only) and 8c03dbe (fix). Both ran 581 files with the same 8 failures, none in files this PR touches: column-edges, apps-flow, html-paste, hover-row-sweep, stats-page, minimap-feel, tooltip-surface, whats-new. Those workers had no Playwright browsers installed, so the new test skipped there. Those runs say nothing about it.

  • I queued the same runs on a chromium worker, for the red (the final test on feat-cmux-next, throwaway commit 86ec022) and the green (98e81fe). No fleet worker has both the chromium and ci-steps labels, so neither step ever started. I cancelled both and deleted the throwaway branch. The new test's only real run is GitHub's web / react-apps-check, which installs Playwright browsers, on this PR's head. A red on the base has not been run.

  • GitHub web / react-apps-check at 98e81fe: the new test failed, one-line reply to footer text at 16px (limit 10). The 34px row minimum was still on the reply. Fixed in 5df2d8c. The same run failed agent-pane-chat-column on the opaque-bubble assertion, updated in cc530bd.

  • GitHub web / react-apps-check at 5df2d8c: agent-pane-transcript-spacing, agent-pane-chat-column and agent-pane-turn-footer pass. CI tests the merge ref (Merge 5df2d8c… into f30d3282…), which brings in 59 base commits. The six other red files are base reds: the same assertions fail on unrelated PRs #22816 and #22819 (merged with base 41f16153), and composer-history also fails on #22830.

FileAssertion hereSame on
composer-historypress ArrowUp: layout shift 0.0002 (limit 0)#22816, #22819, #22830
composer-mentiontype "Fix @rea": layout shift 0.0002 (limit 0)#22816, #22819
composer-suggested-promptReceived: "Type / for commands" (file not on this branch, only in the merge)#22816, #22819
composer-location-stripExpected: <= 2, Received: 2.5#22816, #22819
quiet-access-chipReceived: "rgb(125, 129, 153)"#22816, #22819
agent-pane-wheel-pass-throughExpected: < 10, Received: 12#22816, #22819

agent-pane-background-tasks is a base red too (red on #22675's head); #22833 fixes it.

  • Review follow-up: 1d599f5 (red) adds webviews/src/agent-session/acpmux/rowEstimates.test.ts (no browser). It checks the estimates against the CSS: a reply over its footer, a task's result, the footer, the chat's last footer, and re-placing a kept row when the row after it changes its height. The same commit adds lower bounds to the spacing layout test. 73cf35e fixes model.ts:

    • A reply over its footer is estimated as its text plus 4px with no 34px floor, the text-less fallback too.
    • A task's result is 26 + 8.
    • A Chief prompt gets no action-row height.
    • The chat's last footer ends at 8px instead of 24px (acpmux-row--last-footer), so there's no band over the composer.
    • Footer buttons use the prompt's 5px radius.
  • Second review follow-up: a footer carries its answer's text for Copy (turns.ts), so the estimator measured the answer instead of the footer's line. 2e89dba (red) gives the fixture footers text, shapes one chat through turnView, and puts a hidden row after the last footer. 64349c8 estimates a footer before the text path and treats the last footer as the last row that draws (nextDrawnRow, also used for App.tsx's class).

  • Fleet, cmux-ci run --class light --script scripts/ci/run-webviews-tests.sh, full suite. The 8 files failing in every run lack Playwright browsers on these workers: agent-pane-column-edges, apps-flow, composer-html-paste-edit, hover-row-sweep, stats-page, thread-minimap-feel, tooltip-surface, whats-new-text-selection.

RoundCommitStepResult
1 red1d599f5d3004d7755f928757b60a12bFAIL rowEstimates.test.ts: Expected: 4/Received: 0 (reply over a footer), Expected: < 34/Received: 34 (reply placed again), Expected: 34/Received: 42 (task result). 585 files, 9 failed
1 green73cf35ea0af434d9562156087094407PASS rowEstimates.test.ts, PASS agent-pane-transcript-spacing.test.ts. 585 files, 8 failed
2 red2e89dbafcc934d276d93e02f7e68298FAIL rowEstimates.test.ts: Expected: 48/Received: 372, Expected: 32/Received: 372, Expected: 48/Received: 303.75; the turnView and hidden-row tests fail too. 585 files, 9 failed
2 green64349c84055486160e22d8b58fc4a8ePASS rowEstimates.test.ts, PASS agent-pane-transcript-spacing.test.ts. 585 files, 8 failed

Not run: the macOS app. Webview only, no Swift.

Agent session: cosmic-gray

🤖 Generated with Claude Code