Hmbown / Hmbown/Codewhale

Menu navigation has no shared vocabulary: the same key does different things on adjacent screens

Open
#6,290 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
41k
Forks
3.6k
Avg merge
13h 59m
Merged PRs (30d)
299

Description

## The complaint, measured

Founder, 2026-09-16: the Fleet menu is convoluted, and we need "a more
horizontally and vertically aligned way of navigating all menus."

Both halves are one defect. `menu_style.rs` already single-sources the
**selection vocabulary** — and says so in its own contract:

> The selection vocabulary is single-sourced here. Every menu, picker, and
> option list renders its selected row with `selected_row_style` … instead of
> hand-copying the trio.
>
> **This module owns *styling* only. Verbs, action-hint labels, keybindings,
> and localized strings stay with the views.**

So appearance is unified and **movement is not**. Every surface invents its own.

## Evidence — the four Fleet views

8,501 lines across four views, each supporting a different set:

| View | LOC | Home | End | PgUp/PgDn | Tab | ←/→ |
| --- | --- | --- | --- | --- | --- | --- |
| `views/fleet_list.rs` | 1,075 | yes | yes | no | no | no |
| `views/fleet_detail.rs` | 1,849 | no | no | no | no | no |
| `views/fleet_roster.rs` | 1,010 | yes | no | yes | yes | no |
| `views/fleet_setup.rs` | 4,567 | yes | no | yes | yes | yes |

Walking Fleet list → detail → roster → setup, `End` works once, `PageDown`
works in half of them, `Tab` in half, and `←/→` in one. `fleet_detail` — the
screen you reach by pressing Enter on a worker — supports only Up/Down/Enter/Esc.

That is the convolution. No individual screen is badly built; the four disagree
about what the keys mean, so nothing transfers.

## Evidence — the pickers, same story

| Picker | Horizontal / paging keys |
| --- | --- |
| `model_picker.rs` | `Left` `Right` `Tab` `BackTab` `PageUp` `PageDown` `Home` `End` |
| `provider_picker.rs` | `Tab` `BackTab` **`h`** **`l`** |
| `session_picker.rs` | `PageUp` `PageDown` only |
| `file_picker.rs` | `PageUp` `PageDown` only |
| `slash_menu.rs` | none |
| `mode_picker.rs` | none |
| `status_picker.rs` | none |

`provider_picker` and `model_picker` sit in the *same flow* — pick a provider,
then a model — and use **two different idioms for the same motion**: `h`/`l`
versus `Left`/`Right`. `Home`/`End` exists in exactly one picker.

## The fix follows a pattern this repo already chose

A `menu_nav` sibling to `menu_style`, single-sourcing the **movement**
vocabulary the way `menu_style` single-sources the selection vocabulary:

- one vertical axis: `Up`/`Down`, `PageUp`/`PageDown`, `Home`/`End`
- one horizontal axis: `Left`/`Right` for moving between panes/columns, `Tab`/
`BackTab` for moving between focus regions — one meaning each, everywhere
- one commit key and one dismiss key
- surfaces declare which axes they *have*; they do not re-implement what the
axes *do*

This is deliberately not a new framework. Per AGENTS.md an abstraction must
delete caller code: the win is measured in key-handling arms removed from the
seven pickers and four Fleet views, and the module is not worth adding if it
does not delete them.

Accessibility is part of the contract, not a follow-up: every axis a surface
declares must be reachable by keyboard, and the action hints must name the keys
that actually work on that screen — today a hint can name a key the view does
not handle.

## Sequencing

1. `menu_nav` with the axis vocabulary, adopted by the two worst offenders
(`fleet_detail`, which has no paging at all, and `provider_picker`, whose
`h`/`l` contradicts its neighbour). Prove it deletes code.
2. The remaining pickers.
3. The Fleet views — and while there, ask whether four views and 8,501 lines is
the right shape, or whether list/detail/roster are one surface with panes.
That question is only answerable once movement is uniform.

Do not start step 3 before steps 1 and 2 land: rearranging Fleet on top of four
different key vocabularies would bake them in.

## Related

- #6015 and prior reports from @7jrxt42BxFZo4iAnN4CX already named Fleet
navigation as a gap.

Contributor guide

Open the contributing guide

Research direction

Start with AGENTS.md, menu_style.rs, views/fleet_detail.rs, and views/provider_picker.rs, then compare their key-handling code with the other picker and Fleet view files named in the issue. Define the menu_nav vocabulary and adopt it in the two initial offenders before expanding the migration. Done means the abstraction deletes caller handling code, declared axes are keyboard-reachable, and action hints match working keys.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.