One spinner: every working and loading mark draws the shared arc
Leo, 2026-11-02: "WHY THE F*** IS THE SPINNER ON THE RUNNING SO FING FAST SLOW IT DOWN AND WHY THE F ARE YOU GUYS POSSIBLY DIFFERING THE FING SPINNERS ACROSS THE SIDEBAR AND THE FING RUNNING". Asked whether the original thin arc stays: "we can keep that".
The order: one spinner component for every working mark (sidebar, tabs, Session Info Running and Subagents, transcript status rows; native and web), a test that fails if any surface draws its own, and a list of every call site.
This is redone on the original thin 3/4 arc that feat-cmux-next restored in 3613d7a6. The arc, its 1.5 stroke and its period are unchanged. Nothing here changes how the spinner looks; it only removes the spinners that were not the shared one.
What changed
Moved onto the shared Spinner (conversation/icons.tsx, the cv-spinner arc on the shared clock):
ModelPicker.tsx: the model list's refresh mark was a CSS border ring (.acpmux-mp-refresh-spinner). It is now<Spinner size={14} loading />, and the CSS is removed.harnesses/HarnessesScreen.tsx: the harness row's check mark was a CSS border ring. It is now<Spinner size={12} className="acpmux-harness-spinner" loading />, keeping the row's muted colour and margin. Its separate animation rules in harnesses.css and styles.css are removed.conversation/PlanList.tsx: the todo list's step at work spun its own bordered mark. The mark now holds<Spinner size={12} />and drops its border while the turn runs. A step left in progress when its turn ended keeps its plain ring.ComposerSpinner(unused) is removed, along with its gallery-coverage allowlist line.
Native: the arc loading style (StatusIndicatorPlan.staticPlan(.busy)) now names workingGlyph, so a check or a page loading turns the same glyph as agent work. The value is the same .arc as before, so nothing renders differently; the two can no longer drift apart.
Already on the one component (checked, unchanged):
- Web,
<Spinner>: session-info/ChildSessionRows (Subagents), session-info/WorkflowTasks, conversation/MessageActions (Session Info's Running rows), conversation/CompactionNote, conversation/BackgroundTurnLine, conversation/TaskLine, conversation/Thinking, conversation/UserBubble, shell/ShellRow, started/SessionStatusLine, subagents/AgentCard, subagents/AgentLine, summary/SubagentStatusList, summary/WorkflowSideTab, summary/WorkflowSummary, backgroundTasks/BackgroundTasksPanel, backgroundTasks/SubagentTranscript, HarnessHealthNotice, ChatUnavailableState, CloudStartView, Composer, PeerCopyBanner, SandboxRunView, newtab/NewTabScreen, composer/VideoAttachmentTile, composer/attachmentIntake, composer/inlineAttachments. - Native,
StatusIndicatorView/StatusIndicatorLayer:- sidebar rows, priority rows, group and section headers, badges, roll-ups, hover cards and notice cards (CmuxNextSidebar);
- tabs and their hover cards (CmuxNextTabs);
- Session Info's Running rows and the pane loading state (CmuxNextAgentPane);
- palette search, add-device and add-machine rows, remote state card, onboarding import, Mac screen card, Home first run, Chief pane tools and the debug card grid.
Guard
webviews/test/one-spinner.test.ts fails when:
- a CSS rule turns anything but
cv-spinneron thecmux-spinkeyframes. The only exceptions are the diff viewer page's two loading marks, which are allowlisted with their reason. - a page component writes the keyframes inline.
- native CmuxNext code outside CmuxNextDesign's StatusIndicator and Motion builds its own turning indicator (
NSProgressIndicator(,.spinningStyle,spinAnimation(,stepAnimation(,.progressViewStyle(.circular), a bareProgressView()). - the native arc loading style stops naming
workingGlyph.
Its tests pass, and so do harness-notice-bare-spinner, static-status-loops, agent-pane-plan and spinnerTurns. These fail exactly as they do on base, with identical output:
- gallery-coverage, three tests (one names
agent-pane.turn-error-card); - popover-instant-open's Web Animations listing.
Size
The compact sidebar's spinner size is owned by the sidebar row sizing work. This PR adds no native size constant. The web sizes at the moved call sites (12 and 14) are the sizes those marks had before.
Agent session: agent session
š¤ Generated with Claude Code