# 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.