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
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.
- The remaining pickers.
- 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
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.rsalready single-sources theselection vocabulary — and says so in its own contract:
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:
views/fleet_list.rsviews/fleet_detail.rsviews/fleet_roster.rsviews/fleet_setup.rsWalking Fleet list → detail → roster → setup,
Endworks once,PageDownworks in half of them,
Tabin half, and←/→in one.fleet_detail— thescreen 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
model_picker.rsLeftRightTabBackTabPageUpPageDownHomeEndprovider_picker.rsTabBackTabhlsession_picker.rsPageUpPageDownonlyfile_picker.rsPageUpPageDownonlyslash_menu.rsmode_picker.rsstatus_picker.rsprovider_pickerandmodel_pickersit in the same flow — pick a provider,then a model — and use two different idioms for the same motion:
h/lversus
Left/Right.Home/Endexists in exactly one picker.The fix follows a pattern this repo already chose
A
menu_navsibling tomenu_style, single-sourcing the movementvocabulary the way
menu_stylesingle-sources the selection vocabulary:Up/Down,PageUp/PageDown,Home/EndLeft/Rightfor moving between panes/columns,Tab/BackTabfor moving between focus regions — one meaning each, everywhereaxes 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
menu_navwith the axis vocabulary, adopted by the two worst offenders(
fleet_detail, which has no paging at all, andprovider_picker, whoseh/lcontradicts its neighbour). Prove it deletes code.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
navigation as a gap.