CmuxNext: Codex-like status icons by default, plus static status glyphs
Leo, for op-next-motion's Static status mode (Reduce Motion, Austin's headaches): "design new symbols if needed; we do not have to reuse SF Symbols." This adds six custom static status glyphs. Each is told apart by shape alone at 10 pt, crisp at its exact pixel size, tinted by its theme role, and never animated.
| State | Glyph | Tint |
|---|---|---|
| idle | hollow ring | secondary text |
| working | ring with its right half filled | accent (theme text) |
| waiting on you (question, plain blocked) | empty speech bubble | attention |
| needs permission (permission, sign-in) | solid shield | attention |
| done | disc with a check cut out | success |
| error | diamond with ! cut out | danger |
They follow the custom marks already in StatusMarkArt: rings, discs and badges with the figure cut out.
One source, Swift and web
schemas/status-glyphs/static-status.jsonholds each glyph twice:master: vectors on a 20-unit grid (10 pt at 2x, one unit per pixel). Straight edges and stroke widths sit on whole pixels; only curves antialias.x1art: the 10 px (1x) drawing as pixel art, so every 1x edge falls on a pixel.
scripts/cmux-next/gen-status-glyphs.pywrites:- Swift
StaticStatusGlyph.generated.swift: CGPaths, filled even-odd; - web
src/ui/statusGlyphs.generated.ts: SVG path data. --checkruns inbun run check(check:status-glyphs).
- Swift
- Both sides pick the pixel art by the same rule: 10 device pixels, or an odd multiple of 10. See
StaticStatusGlyph.usesHintingandusesPixelArt.
Tokens for op-next-motion
- Swift:
StaticStatusGlyph(state)maps aStatusIndicatorStateto its glyph. It returns nil for busy and paused, which keep their loading style..planis the indicator plan:animation: niland the glyph's tint.StatusMark.vector(_:)draws a glyph anywhere a mark is drawn.
- Status icon set:
StatusIconSet.still("Static") maps every state to these glyphs. It sits in the Debug menu and Debug Settings with the other sets. The Static setting can select it or callStaticStatusGlyph(state)?.plan. - Web:
<StatusGlyph name size label>insrc/ui/StatusGlyph.tsx.
Contact sheet
Gallery ui.status-glyphs, variants sheet and wallpaper. It shows:
- each glyph live at 10 pt;
- the glyph in a row of text;
- the 1x pixel art and the 2x drawing, blown up so their pixels show.
Switch the gallery's appearance to see it in light and dark.
Test
webviews/test/status-glyphs.test.ts (browser lane) loads the sheet in Chromium in light, dark and on the wallpaper, at 1x and 2x. It rasterizes each live glyph at the window's pixel size with the browser's SVG renderer and checks:
- the six glyphs, in order;
- no animation or transition;
- each glyph's color is its theme token;
- at 1x, no partly covered pixels; at 2x, curves only;
- every pair differs in at least 12 whole pixels at 1x and 30 at 2x.
Results: the red commit runs first in CI (react-apps-check); the green run follows on the fix commit.
Codex-like set, now the default
Team feedback via Leo: the colored status glyphs read as iffy, and the team likes Codex's sidebar. StatusIconSet.codex ("Codex-like") follows it. It is now the default for everyone (Leo 2026-10-14, a team decision; it was symbols). Symbols, Static, Dots and the other sets stay as Debug alternates (Debug menu > Status Icons, or Debug Settings > Status Indicators > Status icons).
No motion at all: turn on Reduce Motion (System Settings > Accessibility > Display > Reduce motion). Then the grey spinner becomes a still ring, and nothing else in the set moves.
In the app
A real cmux NEXT DEV build of this branch on a capture mini. The sidebar rows are driven by OSC 7501 from each workspace's terminal: error (deploy), permission (api), done (infra), question (docs-site) and working (cmux-next). Top row: before (symbols, the old default); bottom row: after (Codex-like). Five frames, 133 ms apart. I watched both 4 s recordings: only the working spinner moves, in both.


Full quality:

Full quality:
| State | Glyph | Color |
|---|---|---|
| working | Lawrence's shared spinner (cx-qtgj2), only re-tinted; a still ring under Reduce Motion, Static or loops off | the theme's foreground at 66% (Codex's spinner is (179,179,179) on (29,29,29)) |
| waiting (question, plain blocked) | line bubble | muted loading grey |
| needs permission (permission, sign-in) | line shield | muted loading grey |
| done, unread | dot | Codex's blue #3982F5 (57,130,245) |
| error | line i in a ring | danger red (Codex's is #FF6764) |
- I sampled the colors from a Codex app capture (cmux-app-screenshots
captures/codex/app-20261003) and the team's reference still. - Waiting, permission and error are line glyphs, not filled badges. Red appears only on error.
spinnerandunreadare newStatusIndicatorPlan.Tints. The schema gains acodexset, which the generator emits for the web copy (STATUS_GLYPH_SETS).- Gallery variants
codexandcodex-wallpaper. - cg-lane-adopt's Dots and Pictograms (#20437) stay as Debug alternates.
Not in this PR: the pin and archive hover row actions, regular-weight titles and softer greys from the same feedback. They are sidebar-row changes, which wait for Lawrence's sidebar rework.
Tests:
CodexStatusIconSetTests(Swift) checks each state's plan and the still ring when nothing animates, plus the sampled colors.status-glyphs.test.tschecks the codex sheet in light, dark and on the wallpaper at 1x and 2x: no animation, tint colors, line glyphs, the blue dot, and the glyphs differ pairwise.- Fleet Swift runs (
run-swift-testing-suites.sh, CmuxNext):- Default: red eb93ce43:
codexIsTheDefaultSetForEveryonefails (the default wassymbols); green e7792eba: passes (all ofCodexStatusIconSetTests). The same unrelatedNewTabStyleSwitchTestsrace failed there too; #20436 fixes it on the base. - red 14b65a79: the test commit fails to compile (
StatusIconSethas nocodex,Tinthas nospinner); green 7bad8558:CodexStatusIconSetTestspasses. - Review fix: red 494d9844:
aStillLoaderStaysAStoppedArcNotTheWorkRingfails (a busy loader became the work ring with motion off); green 3e27a51f: passes. - On 3e27a51f one unrelated suite failed,
NewTabStyleSwitchTests(a New Tab template save read backterminal). It passed on 7bad8558, and this PR does not touch New Tab or settings.
- Default: red eb93ce43:
Centered glyph, bigger done dot (Leo 2026-10-14)
Leo on the first capture: "wait that's not centered? why is it superscript?", and the blue done dot was "too tiny".
- The status glyph used to sit on the title's capitals, so on a two-line row it rode above the count badge. It now centers on the line the badge and the other trailing marks share: the middle of the whole row, on one-line and two-line rows alike (
statusGlyphFrame(trailing:centerY:)). - The done dot grows from 4 pt to 6 pt (schema
dot: r 6, a 6x6 pixel-art disc at 1x). - Count badges go away in a stacked follow-up (corner badge on the row's leading icon), so the badge digit is not reworked here. The two-line rows by default come from the notification line; #20483 makes rows one line by default.
Same scene on both builds, clean profiles, workspace-1 selected. Before: this PR's earlier head 7bad8558. After: ad47b4ce. I watched both 4 s recordings: only the cmux-next spinner moves.
plan image 20410/status-center-before-after.png
plan image 20410/status-center-zoom.png
plan gif 20410/status-center-before4.gif (from status-center-before4.mp4)
plan video 20410/status-center-before4.mp4
plan gif 20410/status-center-after4.gif (from status-center-after4.mp4)
plan video 20410/status-center-after4.mp4
uploaded 20410/status-center-before-after.png
uploaded 20410/status-center-zoom.png
uploaded 20410/status-center-before4.gif
uploaded 20410/status-center-before4.mp4
uploaded 20410/status-center-after4.gif
uploaded 20410/status-center-after4.mp4



Full quality:

Full quality:
Receipts (fleet run-swift-testing-suites.sh, CmuxNext):
- red 01821126:
SidebarStatusGlyphCenterTestsfails (the glyph sits 7.5 pt off the row's middle on a two-line row). - green ad47b4ce:
SidebarStatusGlyphCenterTests,SidebarRowGutterTestsandCodexStatusIconSetTestspass; no suite failed. - Release compile (fleet
check-release-compile.sh) on ad47b4ce: BUILD SUCCEEDED.
Crisp at 3x and 5x (review finding)
Review found usesHinting gave 30 px and 50 px output (a 10 pt glyph at 3x or 5x) the 1x 10-grid pixel art, blown up and coarse. Now only exactly 10 device pixels takes the pixel art; every larger size draws the vector master. Swift (gen-status-glyphs.py, regenerated StaticStatusGlyph.generated.swift) and web (StatusGlyph.tsx usesPixelArt, regenerated statusGlyphs.generated.ts) agree.
- Red at 14b17ee55719:
StaticStatusGlyphScaleTests.onlyTenDevicePixelsTakeThePixelArtfails (curves 0 > 0at 30 and 50 px);webviews/test/status-glyph-pixel-art.test.tsfails. - Green at 7853003c94fb: both pass. The fleet suite run's one red,
NewTabStyleSwitchTests, also fails on the red run and is a base test fixed by #20423. - Release compile on 7853003c94fb: BUILD SUCCEEDED.
Changelog
- Changed: Agent status icons follow Codex's sidebar: a grey spinner (a still ring with Reduce Motion), a blue dot when done, quiet line icons for waiting, and red only for errors, centered on the row beside its count.
š¤ Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds two status glyph sets for CmuxNext status indicators: six static glyphs for the Static status mode (Reduce Motion) and a Codex-like set modeled on Codex's sidebar. The Codex-like set is now the default for everyone; the other sets, including Static and Symbols, stay as Debug alternates.
Both sets are generated from one schema (schemas/status-glyphs/static-status.json), which emits the Swift CGPaths and web SVG paths. Glyphs draw pixel art only at exactly 10 device pixels; any larger size uses the vector master, so 3x and 5x no longer blow up the pixel art.
The Codex-like set follows Codex's sidebar: a grey spinner (a still ring when nothing animates), a blue done dot, muted line bubble and shield for waiting, and a red line i for error. The status glyph now centers on the row's middle beside the count badge, on one-line and two-line rows alike.