cmux NEXT menus and pickers: checkmarks, icons, text and shortcuts on one menu grid (draft: red measurement first)

TE
1d ago
+336-34

HOLD: landing waits until base is restored (webview reds from #27559, #27570 and #27580); the grid coordinator lifts this line.

Leo, 2026-10-29: "we need to have better fing grids. We need to have the fing stuff aligned on f***ing grids and lines" (and, the night before, misaligned checkmarks).

Puts every webview menu outside the Claude Code pickers on AppKit's menu metrics (schemas/menu-metrics, --cmux-menu-*; spec: leo-workspace design/grid.md 5.3): the highlight 5 from the popup edge, radius 5, 28 rows, the checkmark centred on the state column at 10 in every row of a menu that has one, icons on one column, and the text on one column. One shared rule in popupSurface.css replaces each menu's own row padding.

An icon column is reserved only when a row in the menu carries an icon: the text starts at 15 with no state column, at 22 right after the check (the select keeps #28530's 22, design/settings.md), and at 35 or 42 when there are icons.

Kept as they are, because a newer settled design puts the chosen row's check in its number's place at the trailing edge: the model picker and its side menus (#29791, design/model-picker.md), its effort menu, and the mode menu (Claude Code's look, Leo 2026-10-19; composer-claude-split.test.ts, access-grants-columns.test.ts). Whether grid.md 5.3's leading state column should also cover them is a follow-up for the picker owner.

Measured (Chromium, gallery, light; px from the popup's leading edge)

Menuhighlight inset before → aftercheck x before → aftertext x before → afterrow h before → after
composer + (palette)0 → 5none32 → 3546 → 46 (two-line rows)
chat ⋯ menu4 → 5none35 → 3528 → 28
ui.menu (shared)4 → 518 → 1012 / 30 → 2228 → 28
ui.select5 → 510 → 1022 → 2228 → 28
context menu4 → 5none12 → 1528 → 28
combobox4 → 5none12 → 1524 → 28
project chooser4 → 5284 (trailing) → 1032 → 4228 / 46 → 28 / 44

Right-to-left, every column above measures the same from the menu's leading edge, headings included.

Before / after (gallery, Linux Chromium, 2x, light and dark)

MenuBeforeAfter
ui.menu (light)beforeafter
ui.menu (dark)beforeafter
ui.select (light)beforeafter
ui.select (dark)beforeafter
context menu (light)beforeafter
context menu (dark)beforeafter
combobox (light)beforeafter
combobox (dark)beforeafter
project chooser (light)beforeafter
project chooser (dark)beforeafter
chat ⋯ menu (light)beforeafter
chat ⋯ menu (dark)beforeafter
composer + (light)beforeafter
composer + (dark)beforeafter

I opened every after image: the checks sit on one state column, icons and titles on one column each, headings on the titles' column, and the project chooser's hairline back on the separator inset. The composer + menu's filled search field keeps its own inset (a separate field style, follow-up #30142).

Viewed after image: yes (2x, light and dark, above)

Tests (Linux, real Chromium/WebKit via cmux-ci test):

  • Head: checkmark-alignment, project-menu-geometry, picker-row-geometry, quick-model-menu-layout, model-menu-density, access-grants-columns, composer-claude-split and 16 other menu suites pass (336 pass); bun scripts/check-design-tokens.ts and check-menu-metrics.py's web check pass. The only reds are grid-lines transcript.actions/after-steer (warn-level), which fail the same way on base.
  • Base with this PR's tests: checkmark-alignment and project-menu-geometry fail on every menu (17 tests).

Follow-ups from review: #30142.

Gallery: ui.menu/open, ui.select/open, ui.context-menu/open, ui.combobox/open, agent-pane.composer-plus-menu/balanced, agent-pane.header-tools/menu-open, agent-pane.project-chooser/open-current

Agent session: agent session

🤖 Generated with Claude Code