icons: cmux custom family (polished)

Closed
LU
7d ago
+388-15

Summary

a cmux custom icon family behind a new Debug Settings choice, Icons: Family (icons.family, under Shape and Icons), so the team can try it in the app and flip back. the default stays pack, so nothing changes unless someone picks cmux custom.

the family redraws 37 pack names in the agent chat's own recipe: 1.5 line, round caps and joins, 3-unit corners on the 24 grid, line at rest and a filled solid with the details cut out (the chat's eyes, the terminal prompt). the agent chat itself is unchanged. a few that read differently:

  • App Store: a shopping bag with the cmux prompt on its front, so it says "store" and "cmux apps", not browser extensions
  • folder: Untitled UI's folder (free set), scaled to the same live area
  • new chat: the agent bubble with a plus on its shoulder; workspace: an open tray; settings: a toothed gear; diff: a page with ±

how it's wired:

  • design/icons/source-custom/custom.json holds the drawings. build_pack.py builds them into an overlay pack next to the shipped one (cmux-icons-custom.json native, cmuxIconsCustom.json web) and fails if an overlay name isn't in the pack.
  • native: IconPack.bundled lays the overlay over the shipped pack when the app asks (IconPack.useOverlay). the tunable store loads its file off the main actor, after the first icons draw, so DebugSettingsService reads icons.family from the file itself at launch. a change applies after a relaunch.
  • web: AgentPaneTunables.documentAttributes sets data-icon-family="custom", and useIcons() swaps in the custom pack while it's set. it subscribes to the attribute, so a pooled page that learns it when claimed redraws.

notes for review:

  • sidebar toggle and the up/down arrows are drawn too but left out of the overlay here: those names aren't in the pack on this base (the titlebar still draws SF Symbols). a follow-up can add them with the titlebar moving to the pack.
  • the shots are crops (3x) from the design playground, which also has separate switches on that aren't in this PR: workspace rows lead with a folder (sidebar.workspaceIcon, so before shows the chat bubble there) and the New Tab create actions are outlined pills (the Cursor top strip style). what this PR changes is the drawings: the house, the bag with the prompt, the folder glyph, the terminal, browser and chat icons.
  • Untitled UI's free license allows commercial use and forbids redistributing their icons as a set; one folder inside the app reads as fine, flagging it anyway.
Before (sidebar, 3x)After (sidebar, 3x)
BeforeAfter
Before (New Tab create actions, 3x)After (New Tab create actions, 3x)
BeforeAfter

Testing

  • bun test src/agent-session/acpmux/icons/: 2 pass (pack outputs match the sources, the overlay only redraws names the pack has, the merged pack takes the overlay's drawings)
  • bunx vp check on the icon files and vite.config.ts: formatting, lint and types pass
  • python3 scripts/icons/build_pack.py --check: up to date
  • ./scripts/lint-ios-package-conventions.sh: ok
  • native: the same change built and ran in a tagged dev build on an older base (the playground), where the custom icons showed in the sidebar, tab strip and New Tab page after a relaunch. this branch's Swift (the documentAttributes hookup is new here) was not built locally. IconFamilyAttributeTests and IconFamilyLaunchTests run in CI, not run locally.
  • localization: no user-facing strings; the only new text is the Debug Settings label and choice titles, DEV/NIGHTLY only

Changelog

none


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