cmux NEXT: agent work breathes a calm accent dot instead of a spinner on every row
Leo (2026-10-20), on a list of ~17 running sessions with a grey spinner on each row: "Can you think of anything better than just having these spinners? After a certain point, isn't this mildly ugly? What are the alternatives?"
Part 2 of the activity-indicators lane. Part 1, the frozen transcript spinners, was #22786 (merged).
What changes
- Agent work defaults to a breathing dot. It's a small dot in the theme accent (
Palette.accentFill: the text color, or the hueappearance.accentnames; never Claude orange) that breathes once every 2.4 s. It runs on the shared clock, so a list of working rows breathes as one instead of 17 rings turning.- Native: every status indicator that draws work (sidebar rows and group headers, the switcher, tabs and tab badges, hover cards).
- Agent pane: the running transcript rows (compaction, tool groups, MCP calls, message cards, command and shell rows).
- Loading keeps its spinner (a page, a command, a harness check), so loading and agent work still read apart.
- Reduce Motion and the static look: nothing moves. Native keeps its existing still glyphs; on web the dot holds at full strength. An occluded or off-screen row's dot also stays still and stays a dot.
- Debug Settings → Status Indicators → "Working look" (
status.working) keeps the alternatives:- Shimmer: a still dot, and on web pages a light band crosses the running row's words.
- Spinner: the old turning arc.
- Web pages get the choice as
data-working-look, through both WebTheme and the agent pane theme.
- New Motion loop
breath(2.4 s, Debug-tunable, never under 1 s). Web:cmux-breatheandcmux-shimmerkeyframes in desktop.css, kept in phase byui/spinnerClock.ts.
The options (Linux gallery, agent-pane.working-look, dark)
These are the agent pane's running rows; the "Compacting the conversation…" row carries the working mark. The native sidebar, switcher and tabs draw the same plan with the native indicator layer; there's no native capture here (no Mac build on this host).
| Breathing Dot (default), bright | Breathing Dot, dim (1.2 s later) |
|---|---|
![]() | ![]() |
| Shimmer (still dot; a light band crosses the words) | Spinner (before) |
![]() | ![]() |
Reduce Motion / Static: the dot holds at full strength.

Why Breathing Dot is the default: one small accent dot per row, breathing slowly and in step, reads as calm "still working" even across 17 rows. Shimmer moves more of the screen (every running row's words), and a spinner on each row is the noise Leo pointed at. Shimmer is a good fit for a single row (it's what "Thinking" already does), so it stays as an option.
Changelog
Changed: A working agent shows a calm breathing dot in your accent color instead of a spinner; Debug Settings keeps Shimmer and Spinner.
Tests
StatusWorkingLookTests(CmuxNextDesign): breathe is the default; each look's plan; a still or hidden row keeps the dot; loading and known progress keep their marks; the breath is slower than the waiting pulse.test/static-status-loops.test.ts(browser): each look runs exactly its own loop on agent work, a loading spinner still turns, and Static and Reduce Motion stop all of them.spinnerTurns.test.tsx: running rows mark themselves as agent work; a loading spinner never takes the dot.
Observed / Not run
- Ran on huge-blue:
bun run check(clean), andbun teston conversation, started, gallery and the spinner clock (82 pass). WithCMUX_BROWSER_TESTS=1: static-status-loops, agent-pane-plan and spinner-clock (18 pass). Gallery tests: 43 pass.prepush-gateran on push. - Elapsed time: hovering a sidebar row already shows how long it has been working (the hover card's
activitySince). Elapsed time on a wide row is not in this PR; it needs native layout and a capture, so it comes as a follow-up. - Not run: the Swift build and
StatusWorkingLookTests. No Mac compiles on this host, so those run on CI.
Agent session: huge-blue
🤖 Generated with Claude Code



