Files
Panama/docs/superpowers/specs/2026-08-23-settings-navigation-redesign.md
Gabriel Brown 5490fd285d Fold thirty-one settings pages into fifteen categories with tabs
The sidebar was a flat scan of thirty-one rows; now it reads like a
settings app. Multi-subject categories (Input, Network & Sharing,
Applications, Users & Accounts, Privacy & Security, System) carry an
Appearance-style tab strip above the page, drawn by the shell so the
leaf pages themselves are untouched. The taxonomy lives in one new
file, services/SettingsRoutes.qml; the sidebar, the strip, route
validation, search breadcrumbs, and both generators derive from it.

ShellState.settingsPage still holds leaf ids, so every deep link, IPC
call, and search result keeps working — and now lands on the exact
tab. Dictation moves out of Sound onto its own page under Input, with
a handoff back to Sound for the microphone. The strip scrolls when
System's nine tabs outgrow a tiled window. All 161 contracts pass.

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
2026-08-23 20:21:31 -04:00

70 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Settings navigation redesign — 31 flat pages become 15 categories
Approved 2026-08-23. The sidebar's 31 entries collapse into 15 categories. Multi-subject
categories render an Appearance-style tab strip above the page; single-subject categories
render no strip. Every old page id keeps working forever.
## The taxonomy
| Category (sidebar) | Tabs (leaf page ids) |
|---|---|
| Home | — (`home`) |
| Appearance | — (`appearance`, keeps its internal tabs) |
| Desktop & Dock | — (`desktop`) |
| Displays | — (`displays`) |
| Sound | — (`sound`, Dictation card moves out) |
| Notifications & Focus | — (`notifications`) |
| Input | Keyboard (`shortcuts`), Mouse & Touchpad (`mouse`), Dictation (`dictation`, new) |
| Network & Sharing | Connections (`connectivity`), Firewall (`firewall`), Sharing (`sharing`), Printers (`printers`) |
| Home & Phone | — (`home-phone`) |
| Applications | Applications (`applications`), Gaming (`gaming`), Screen Intelligence (`screen-intelligence`) |
| Users & Accounts | Users (`users`), Online Accounts (`accounts`) |
| Privacy & Security | Privacy (`privacy`), SSH Keys (`ssh-keys`) |
| Power & Lock | — (`power`) |
| Accessibility | — (`accessibility`) |
| System | About (`about`), Software Update (`updates`), System Health (`services`), Storage (`storage`), Snapshots (`snapshots`), Containers (`containers`), Date & Time (`datetime`), Region & Language (`region`), Manual (`manual`) |
## Architecture
`ShellState.settingsPage` keeps holding the **leaf** id, exactly as today. The page loader's
switch, the ownership contracts, HomePage's direct jumps, and every external caller are
untouched by the grouping itself. What changes:
- **`services/SettingsRoutes.qml` (new singleton)** — the one source of truth for the
taxonomy: `categories` (page, label, icon, tabs), `resolve(id)` (category id → first
available leaf; leaf → itself; unknown → home), `categoryOf(leaf)`, `tabsFor(leaf)`
(availability-filtered), `breadcrumb(leaf)`, `pageAvailable(leaf)` (the podman/snapper
gating, moved from the sidebar).
- **`SettingsSidebar.qml`** — destinations come from `SettingsRoutes.categories`; a row
highlights when it is the category owning the current leaf; clicking requests the
category id and `ShellState` resolves it.
- **`ShellState.showSettings`** — the literal allow-list becomes `SettingsRoutes.resolve()`.
- **`SettingsShell.qml`** — renders a `SettingsTabs` strip between the titlebar and the
page loader whenever the current leaf's category has more than one available tab.
Tab click is just `openSettings(leafId)`. Adds the `dictation` case.
- **`SettingsTabs.qml`** — the strip becomes horizontally scrollable when it overflows
(System has nine tabs; the window tiles down to 900px).
- **`SoundPage.qml` / `DictationPage.qml` (new)** — the Dictation card moves to its own
page under Input, with a labeled handoff noting the microphone is chosen in Sound.
- **`SettingsSearch.qml`** — dictation `extraEntries` re-route to `dictation`; the search
result subtitle becomes the `SettingsRoutes` breadcrumb ("System Storage"). Because
results carry leaf ids, search now lands on the exact tab with no further work.
## Blast radius owned by this change
- `scripts/panama-settings-commands` regex-parses the sidebar's destinations array; it
moves to parsing `SettingsRoutes.qml` and regenerates the Vicinae commands (one per
leaf; `home` still skipped; `dictation` gained). `scripts/panama-settings-docs` checked
the same way.
- Test contracts updated in the same change, not after: `settings-nav-contract`,
`settings-jump-contract`, `search-routing-contract`, `gnome-handoff-contract`,
`manual-contract`, `sound-page-contract`, plus any contract found grepping page ids.
- `modules/settings/README.md` gains the navigation model; manual chapter
`05-making-it-yours.md` updated where it describes the sidebar.
## Non-goals
- No per-tab deep links into Appearance's internal tabs from search (unchanged today).
- No persistence of last-selected tab per category (Appearance already resets; same rule).
- No renaming of leaf ids, files, or schema groups.