cmux NEXT composer: the look dots in a chat, on hover in the composer's bottom area

Merged
TE
14d ago
+305-14

The composer's two look dots (Claude, Codex; #24367) also sit in a chat composer's bottom area. They stay hidden until the pointer is over that area, and they switch the look in place. Split from #24367 for size. #24367 has landed and the base branch is in this one now, so the diff is only this PR's own 8 files.

Leo-ask: ask-5a69bb86d3
Leo-ask: ask-7fcf472bf8

Leo's words:

  • "lawrence likes the codex way i think we can have as option... the composer looks"
  • "this is the codex composer. i guess we could make the alternative composer like codex. first dot claude, second dot codex. since it's just all looks anyways. first 2 dots minimal on the new tab page, and then when you're in a chat you can also switch the dots if you hover on that bottom area. e.g. there's space down here, and also dead space in codex's thing"
  • (part) "we can have the alt composer like chatgpt styled but for now just have "minimal" be the claude thing"

What changes

  • LookSwitch.tsx: the New Tab page's look dots (DotSwitch) in the chat composer's bottom area. With Claude's look they sit in the quiet row's empty middle. The slot is out of the row's flow, so the row lays out as before. With Codex's look they sit in a 16px band under the box, the dead space Codex leaves, which takes the composer's bottom padding.
  • One LookSwitch instance survives a look change. ComposerBox draws the dots in one element after the box for both looks. With Claude's look that element has no height and the dots sit over the quiet row's empty middle; with Codex's look it is the 16px band under the box. Switching the look by keys therefore keeps the same radios and their focus: arrows keep switching both ways, and Escape returns to the prompt. (This answers the Codex review at 2b7775c8, where the dots remounted under the box and left the keyboard on BODY.)
  • They show while the pointer is over that area or a dot has keyboard focus. They appear at once and fade out when the pointer leaves: an exit animation, with no entry animation. A dot's name shows beside the dots (DotSwitch nameAside), since there is no room above them. The Quick Composer has none.
  • Arrow keys on the dots stay keys from the first moment. DotSwitch used to call any pick within 500 ms of the last pointer press a click. Since performance.now() starts at 0, that included the page's first half second, so one arrow sent the keyboard to the prompt and the next arrow or Escape did nothing. The pick is now read from the events instead of a clock. It is a pointer's pick if a pointer pressed a dot with no key since, or if it is a click that counts its presses (detail 1 or more). Anything else is keys. The New Tab style dots share DotSwitch and get the same fix. (This answers the Codex review at b5bc94de.)
  • ComposerBox gets a bottom slot, drawn once after the box. Composer passes the dots into it.
  • Escape and a click on a dot return to the prompt that shows. In shell mode that is the shell line, since the Markdown field is hidden. One helper, focusPrompt, does this, and the card press and the edit commands use it too, so the three cannot drift apart. (This answers the Codex review at a3421d27.)

Kept: our glass and colours, the theme accent, and no status dot. Not touched: what Claude's look draws at rest, the model menu (#24224), the queue and the mode menu. No default flips.

Gallery

agent-pane.composer-look gains claude-chat-dots (the pointer in the quiet row: the dots show, Claude's dot marked) and codex-chat-dots (the pointer in the band under the box, Codex's dot marked). Before/after: https://cmux-lawrences-mac-mini.tail137216.ts.net:18796/compare/composer-look-chat-dots/?entry=agent-pane.composer-look

Evidence

  • New test in test/composer-look-dots.test.ts: "in a chat the dots show on hover in the composer's bottom area and switch its look". Run against #24367's head (f6052c8f474), it fails: 2 pass, 1 fail, because there are no dots in the chat composer. At this head: 3 pass, 0 fail (25 expects). It checks that the dots are hidden at rest and show when the pointer is over the foot's empty room. It checks that Codex's dot moves the model and access into the box and drops the foot, that the dots then show on hover in the band under the box, and that Claude's dot brings the foot back. Command: CMUX_BROWSER_TESTS=1 bun test test/composer-look-dots.test.ts, run on huge-blue.
  • Keyboard round trip, a new test in the same file: "in a chat the dots switch the look by keys both ways, and Escape goes back to the prompt". It focuses Claude's dot, presses ArrowRight (Codex's look, focus on Codex's dot, the dots still shown), presses ArrowLeft (Claude's look, focus on Claude's dot), then presses Escape (focus on the prompt). Red at 2b7775c8 (0 pass, 1 fail: after ArrowRight focus was on BODY). It was committed alone as c8347a67735. Green after the fix (a3421d27).
  • Shell mode, a new test in the same file: "in shell mode Escape on a look dot goes back to the shell prompt". It types !, focuses a dot and presses Escape, then expects focus on .acpmux-shell-field. Red at a3421d27 (0 pass, 1 fail: focus stayed on the dot's input). It was committed alone as e1709cff76b. Green at the head: 5 pass, 0 fail (33 expects) across the file.
  • Timing, a new test in the same file: "arrow keys keep the keyboard on the dots right after load and right after a click". It sets the page's clock as at a fresh load and presses ArrowRight twice from a keyboard-focused dot, then clicks the marked dot and presses ArrowRight at once. Each time the look changes and focus stays on the radio. Red at 3ea52cb532d, which is b5bc94de's code with the base branch brought in: after the first ArrowRight, focus was on the prompt. It was committed alone as 08b51dadde8. Green at the head: 6 pass, 0 fail (39 expects) across the file. new-tab-style-switch (3), new-tab-style-dots (2), new-tab-minimal-default (6) and new-tab-widget-edit (5) pass at the head.
  • agent-pane-recap-card (7, including its shell-mode test) passes at b5bc94de. These composer browser tests passed at a3421d27, and at the head the reruns of composer-caret-focus, composer-quiet-chips, composer-claude-split, composer-footer-plain and composer-turn-cycle pass again: agent-pane-bottom-stack (2), composer-caret-focus (42), composer-claude-split (18), composer-footer-plain (19), composer-plain-glyphs (10), composer-quiet-chips (32), composer-row-layout (32), composer-row-rhythm (8) and composer-turn-cycle (6). In composer-location-strip, 3 tests fail (the seam hairline, the folder name at 340 px, the corner gap); the same 3 fail on base 487876a066f, so they are base reds.
  • vp check at the head: lint and type-aware lint are clean. One formatting finding remains, in src/pages/settings/components/HostSections.tsx, which is identical to base and not part of this PR.
  • cmux-ci compile-check --ref 99dc75012f7e6af7076a4016c3ca0b805434125e --app passed (job af17e4d66142273ddb2de5d6, 37 s).

No capture: webview-only (agent pane web page); gallery entry agent-pane.composer-look.

Changelog

  • Added: The composer look dots also show on hover under a chat's composer.

Agent session: cosmic-gray

🤖 Generated with Claude Code