Skip to content

Agent chat: one column for transcript and composer; pinned, steady streaming - #20374

Merged
teamleaderleo merged 10 commits into
feat-cmux-nextfrom
op-next-chat-column
Oct 14, 2026
Merged

teamleaderleo merged 10 commits into
feat-cmux-nextfrom
op-next-chat-column

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Leo finds the side padding in agent chats too wide and uneven, and sees the chat jump and jitter while a reply streams.

What changes

  • The transcript and the composer share one readable column. A narrow pane leaves the page edge (--page-edge, 18px, from CmuxNext: one container level and consistent edge padding (batch 1) #20121) on each side. A wide pane widens the column up to a readable cap and keeps it centered.
  • The view stays at a streaming reply's latest line unless you scroll up. Nothing moves when a queue appears, when a row opens, or when a reply streams a few words at a time.

Before

The transcript's rows sat in a 720px column inside 26.5px gutters. The composer sat in a 760px column inside 16px gutters, so their edges never lined up.

Proof

webviews/test/agent-pane-chat-column.test.ts (Chromium) measures the column at four pane widths. It also reads every painted frame while a reply streams, a queue appears, a command opens and a reply streams word by word. The test commit is red on base; the results are below.

Changelog

  • Changed: Agent chats read in one column shared by the transcript and composer, with page-edge side padding, and stay pinned to the bottom while a reply streams.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

The agent chat now reads in one column shared by the transcript and composer, stays pinned to the streaming reply's latest line, and styles prompts as solid bubbles with a single Copy action.

Previously the transcript used a 720px column inside 26.5px gutters and the composer a 760px column inside 16px gutters, so their edges never lined up. Content growth only glided on the first placement step, so streamed text dropped by the estimate's error and slid past its place at each token; now every step glides and the moves add up. Queued prompts lay over the transcript's foot, and a prompt was a translucent, unevenly padded bubble with two Copy buttons on hover.

  • The column is 720px, 62.5% of a wider pane up to an 800px cap, centered, at least the page edge (18px) from each side; model.ts and the minimap mirror it.
  • The transcript now ends above queued prompts, and rows reveal a floating Copy only for keyboard focus and a selected range (a prompt's hover shows the one under its bubble, 12px text insets, 10px radius).
  • Adds a Chromium regression test (agent-pane-chat-column.test.ts) that measures the column at four pane widths and samples every painted frame while a reply streams, a queue appears, and a command opens. Followed and code-card rest positions allow a few pixels of settle, since line heights are fractional.

Written for commit 9709c3b. Summary will update on new commits.

View guided diff Turn on auto-fix

The transcript's rows and the composer use different columns (720px in
26.5px gutters against 760px in 16px), so their edges never meet. Also
reads every painted frame while a reply streams, a queue appears and a
command opens, so a frame-late fix or a per-token jump fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 4836b1e6-ec57-41f2-96f5-8aed99ed4f88

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The bubble is a 5% tint the wallpaper shows through, 16px from its sides
and 10.5px from its top on a 16px radius, and a hover shows a second
Copy over it; a tool row shows its own Copy on hover too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 14, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@blacksmith-sh

blacksmith-sh Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
nightly cmux-tui resolve/nightly cmux-tui resolve View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

all web/size checks green

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Passes: CI passes on 9709c3b39c.

CI passes on 9709c3b39c (run 38507224073 attempt 1).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards passes on 9709c3b39c (https://github.com/manaflow-ai/cmux/actions/runs/38507224073). This covers only the fast guards, not CI: CI's result is the ci-status check, and the CI failure attribution comment names any failing test.

teamleaderleo and others added 8 commits October 13, 2026 19:29
The command sits at the start of a long transcript, so it is not mounted
while the view is at the end; scroll there first, and find its toggle
by its text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The transcript, the composer and the cards between them share one
column: 720px, 62.5% of a wider pane up to 800px, centered, at least
the page edge (--page-edge) from each side. model.ts and the minimap
mirror it.

A row that grew is placed by its estimate and then by its drawn height
before the frame paints. Only the first step glided, so streaming text
dropped by the estimate's error and slid past its place at each token.
Every step glides now, and the glides add up to the real move.

The queued prompts lie over the transcript's foot; the transcript now
ends above them, so a followed reply's latest line is never under the
queue.

A prompt is a solid bubble on the composer's surface with even 12px
text insets and a 10px radius. A prompt row has no floating Copy (its
Copy sits under the bubble), and no row reveals one on hover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64747bb left it unformatted, so bun run check fails in every PR's
react-apps-check before any test runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A keyboard range that starts at a prompt copies through that row's
floating Copy. It stays, shown only for keyboard focus and a selected
range; a hover shows only the Copy under the bubble.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Growth glides in: the thread slides up from below, and the slide is a
transform that scrollHeight counts, so the view's distance from the end
read every glide as falling behind. The text now has to move only up,
under a line per frame word by word, and come to rest at the end.

The code card height check allowed no rounding: a card is a fractional
height, so its rounded height reads a pixel less at some sub-pixel
offsets (rows are 720px at 760px now, not 707px). A collapse is whole
lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lines are 22.75px and rows are placed by their drawn heights, so the
newest line rests 1-3px from where the last one did (CI); a jump is a
line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo merged commit 9aecbcb into feat-cmux-next Oct 14, 2026
104 of 107 checks passed
@teamleaderleo
teamleaderleo deleted the op-next-chat-column branch October 14, 2026 03:55
teamleaderleo added a commit that referenced this pull request Oct 29, 2026
… own room (#24151)

* cmux NEXT composer test: the model button is quiet, its bolt is inside it, and the row fits on its room (failing)

Leo 2026-10-21, of #23742's after-shot: "WTF IS THIS UGLY BULLSHIT. WE ALREADY
FIXED THE FONTS. THE BOLT DOESN'T GO THERE." And live-qa on nightly 3a63d6d: a
410 px composer read "Opus Med" with about 250 px of the quiet row free.

- composer-fast-toggle: no harness draws a speed button in the box; Fast mode is
  the model menu's last row for Claude and Codex; a running Codex fast tier is a
  small outline bolt inside the model button, before the name; Claude never
  draws a bolt.
- composer-model-quiet (Chromium, the real composer on the gallery stage): the
  bolt's place and line; no fill, border, ring or outline at rest; the effort a
  step back of the name; keyboard focus a pill and a hairline, not the bright
  ring the capture caught; the context ring's glyph about 8 px after the
  effort on the buttons' center line; the button, its labels and the mode in
  the composer's font; "Opus 5.5  Medium" kept at 410, 324 and 264 px, and
  whole-unit steps with nothing overlapping at 204 and 164 px.
- composer-app-font: the composer names the app's UI font once, no composer
  rule names another family, and the quiet row's buttons inherit it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: Codex's bolt goes inside a quiet model button; the quiet row fits on its own room

Leo 2026-10-21, of #23742's after-shot ("GPT-6 Astra Medium" in a bright
outline, a bolt in the box by the mic, a loose ring): "WTF IS THIS UGLY
BULLSHIT. WE ALREADY FIXED THE FONTS. THE BOLT DOESN'T GO THERE."

Bolt. FastToggle.tsx (#23742's bolt button left of the mic) and its CSS are
gone, with the five strings only it used. Fast mode is the model menu's last
checkbox row for every harness that offers it, Codex included. While a Codex
fast tier runs, the model button draws a 12 px outline bolt before the model's
name, in the name's ink, and its tooltip says Fast Mode; Claude Code draws no
bolt anywhere. The stale "no bolt in the composer" comments now say no speed
button, and the gallery's reasoning-codex shows the menu's Fast mode row, with
reasoning-codex-fast for the bolt.

Quiet button. The outline in the shot was keyboard focus, not rest: the
gallery's play closed the menu with Esc, which hands focus back to the button,
and the shared ring (desktop.css, 1.5 px at 48%) drew around bare text. At rest
the button already drew no edge. In the quiet row its focus is now the row's
soft pill and a 1 px hairline at 22% (Increase Contrast keeps the shared ring),
the effort sits one step back of the name in every theme, and the context ring
lost its 8 px margin, so its glyph is 9 px after the effort (was 17) on the
buttons' center line.

Fonts. Nothing regressed: the composer names system-ui (SF Pro in the app's
WebKit) since #20374, and #23742 touched no font. The shot came from headless
Linux Chromium, which has no SF Pro and fell back to Liberation Sans for the
whole page. The quiet row's buttons sat outside `.acpmux-composer-box button {
font: inherit }`, so they drew in the engine's control font; they now inherit
the composer's family, and composer-app-font holds it.

Truncation. COMPOSER_FIT_STEPS shortened on the composer's width (#20802's one
row). Since #22285 the quiet row holds only the mode, the model button and the
ring, so its width steps were stale: a 410 px composer read "Opus Med". The
quiet row now takes no width step (paneWidth is unbounded for
.acpmux-composer-foot, and the container queries skip it); useRowFit fits it on
the units' natural widths against the row's measured room, so it keeps
"Opus 5.5  Medium" down to a 264 px composer here and shortens whole units only
when the row runs out. The box's own row keeps its steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: no dot between the model and its effort

Leo 2026-10-21: "BRO WTF NO WE FUCKING HATE THE DOT", of Lawrence's build
reading "Opus 5.5 • Medium". The one dot the button can draw on base is the
agent-update dot (hq#2569 #12, `.acpmux-model-update-dot`): ModelPicker's chip
face puts it after the model's name, so with Claude Code or Codex outdated it
sits between the name and the effort. No label formatter writes a "•" or "·"
into the button; the fit steps draw whole labels.

The dot is gone. The update stays in the button's tooltip and in the menu's and
picker's Update row. composer-fast-toggle checks, for Claude and Codex, Fast on
and off, with and without an update on offer, that the button's text and every
fit label carry no "•" or "·" and nothing sits between the name and the
effort; composer-model-quiet checks the rendered button reads "Opus 5.5 Medium"
at 410, 324 and 264 px and carries no dot at the narrow steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer test: the quiet row keeps whole labels at every width; the in-box row keeps its steps

composer-row-steps measured whichever row held the model button, and its one table still held the
width steps the quiet row under the box no longer takes (composerRowFit.ts paneWidth). Split it:
the quiet row (the default plain footer) keeps "Opus 5.5  Medium" and "Auto" from 760 down to 280
px; the in-box row (plain footer off through the host's composer event, as Debug Settings does)
keeps every old step, measured at the same widths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT composer: Codex's Ultrafast is a checkbox row under Fast mode

With the bolt button gone, a speed option's tier past Fast (Codex's Ultrafast) could not be picked.
The model button's menu adds an Ultrafast checkbox row under Fast mode when the option has such a
tier: checked while it runs; on picks it, off steps back to Fast. A two-state option has no such
row. Restores the picker.tierUltrafast string from the merge base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT composer: the row past Fast mode reads Ultrafast only for Codex's Ultrafast tier

Any other agent's tier past Fast keeps the name the agent gives it (else its value), so the
second checkbox row never calls another harness's tier Ultrafast (Codex review of #24151).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: keep picker.tierFast, which base's / menu Fast row now names

The PR dropped the speed tier strings with FastToggle.tsx; base's SlashMenu (Composer.tsx) reads
picker.tierFast, so the merged tree failed typecheck. Restored from base unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: the full picker's Fast mode row is on for every tier past off

The full picker read only the option's first tier as on, so with Codex's Ultrafast running it
showed Fast mode off and a click picked Fast instead of turning the tier off (Codex review of
#24151 at 9da89d3). One helper, fastModeToggle (modelPickerLayout.ts), now gives both Fast mode
rows their on/off state: on is any tier past off, a click turns the running tier off, and the
full picker names the running tier (Ultrafast).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer test: the quiet row's model name keeps the button's ink, its effort one step back

composer-footer-plain and composer-caret-focus required the model name and its effort to share one
colour; this PR puts the effort one step back of the name in the quiet row (composerLocation.css,
composer-model-quiet.test.ts), so those checks failed on the PR's head only. In the quiet row they
now check the name is in the button's muted ink and the effort differs from it; the New Tab stage
row (.nt-pickers), which this PR does not restyle, keeps the old same-ink check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: the string catalog only drops the speed button's three strings

Re-derived from feat-cmux-next's Localizable.xcstrings with composer.speedLabel,
composer.speedTooltip and picker.tierStandard deleted, in base's own formatting and key order.
The parsed catalog is unchanged; the diff against base is those three entries alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: split; this PR keeps the quiet model button and the quiet row's fit

The speed control's move (the bolt inside the model button, FastToggle.tsx's removal, the
Ultrafast menu row, fastModeToggle and speedTierName, the three speed strings and
composer-fast-toggle's tests) moves to its own follow-up branch, composer-look-codex-bolt-in-model,
so each lands under ~800 changed lines. Those files go back to feat-cmux-next's versions here;
composerLocation.css keeps base's speed-button rules, composerRowFit.ts base's bolt sentence, and
composer-model-quiet.test.ts drops its two bolt tests (they move with the bolt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* cmux NEXT composer: the quiet row's harness name follows the row's room, as its other labels do

Base's new quiet-row harness (ComposerHarness.tsx) hid its name below a 460 px composer by a
container query, while this PR's fit takes no width step in the quiet row: the fit counted the
hidden 76 px name as shown and shortened the effort with the room free (a 400 px New Tab read
"Med"; CI and huge-blue disagreed on which rows did, by font metrics). The quiet row now skips that
query, as it skips the effort, model and access ones, so the name shows while it fits and the fit
drops it (data-fit-harness) only when the row runs out.

Tests, from the row's measured room (need vs room, probed on huge-blue):
- reasoning-chip-steps: 400 px sits within ~20 px of the boundary in all three panes, so it checks
  the form the row's fit chose, whole; 760 (full) and 300 (short) stay exact.
- composer-footer-harness (base's): at 410 px the row has ~60 px spare, so every name shows whole.
- composer-row-steps / composer-model-quiet: with the harness's name in the row, whole labels hold
  down to 400 px (410 px composer); 300/280 px (264 px composer) shorten; widths on the boundary
  (360 px, 324 px composer) are left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16027-99fc-73f0-9482-5a4babfe1b0a&host=cosmic-gray

* fix(cmux-next): refit quiet composer when controls resize

Agent-Session: https://cmux.com/deeplink/agent?harness=codex&session=01a166ed-0099-7a32-8c00-7de0ff0c2191&host=cmux16

* cmux NEXT composer test: the quiet row's widths and ring gap follow base's row

Base took the harness label out of the quiet row (#24938) and gave the context
affordance one compact size with a 2 px margin (composerFooterShared.css), so:
the row now keeps every whole label down to a 244 px composer (measured on the
fleet: panes 280 and 300 read "Opus 5.5  Medium"), the ring's glyph sits 15 px
after the effort, and the shared focus ring is asserted from the CSS because the
gallery stage loads no pages/shared/desktop.css. composer-app-font also reads
base's new composerFooterShared.css.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-2849-7aa2-bd71-289c4ec9e04f&host=huge-blue

* fix(cmux-next): the quiet row's width steps must not outrank the fit

Review (codex gpt-6-astra xhigh) found it: `.acpmux-composer-bar:not(.acpmux-composer-foot)`
added a class, so each container-query step tied the hiding rules above it at
(0,4,0) and, coming later, won. At a 370-440 px composer whose measured fit had
already chosen the model's mark (data-fit-model="icon"), the step would have
brought the family label back. `:where(:not(...))` adds no specificity, so the
steps keep losing to the fit, as before this PR. composer-fit-steps now refuses a
bare `:not(.acpmux-composer-foot)` in any step (it catches the regression: the
guard fails with the bare selector restored).

Also, on the reviewer's second point, the focus hairline is now measured instead
of only read: the test puts pages/shared/desktop.css in the stage as the app's
pane has it and asserts the shared ring draws 1 px at 22%, not 1.5 px at 48%.
Increase Contrast stays a CSS-text assertion, since prefers-contrast does not
reach the gallery frame.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-2849-7aa2-bd71-289c4ec9e04f&host=huge-blue

* Scope the quiet row's fit to the plain look, not the row element

The quiet row is a look, not an element: ComposerFoot draws the same
.acpmux-composer-bar.acpmux-composer-foot in both looks and marks only the
plain one data-footer-look="plain". Keying the width-step guards and
paneWidth on the class therefore skipped the steps with the plain footer
off too, so the stepped row kept whole labels it had no room for and hid
the harness mark that look draws. Both now read data-footer-look.

Also follows base: the composer names its family through var(--font-ui)
(#24661 and the token move), so the font tests read the token instead of
a literal system-ui, and the ring keeps base's compact 22px box.

Measured in Chromium on huge-blue, these suites: composer-model-quiet,
composer-row-steps, composer-fit-steps, reasoning-chip-steps,
composer-footer-plain, composer-alignment-centers, composer-app-font,
composer-caret-focus. Before this commit, at merge 966ee3e: 46 pass,
17 fail. After: 87 pass, 0 fail. The same suites at base e3134fd:
45 pass, 0 fail, so none of the 17 was a base red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-23d3b9d81891fbbc413e

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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