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
This commit is contained in:
Gabriel Brown
2026-08-23 20:21:31 -04:00
parent 50077a0c31
commit 5490fd285d
35 changed files with 816 additions and 288 deletions
+14 -6
View File
@@ -38,16 +38,24 @@ declare -A expected=(
[restart-shell]=restart-shell
)
# The per-page settings commands are generated from the settings page list, so
# they are enumerated from that list rather than restated here -- a hand-written
# copy would have to be edited every time a page is added, which is exactly the
# kind of second list this generator exists to avoid.
sidebar="$repo_dir/config/dot/quickshell/modules/settings/SettingsSidebar.qml"
# The per-page settings commands are generated from the settings taxonomy, so
# they are enumerated from it rather than restated here -- a hand-written copy
# would have to be edited every time a page is added, which is exactly the kind
# of second list this generator exists to avoid.
#
# One command per leaf, not per sidebar row: a launcher entry that opened a
# category would land on whichever tab that category happens to open first,
# which is not what somebody typing "firewall" asked for. Home is skipped
# because open-settings already goes there.
routes="$repo_dir/config/dot/quickshell/services/SettingsRoutes.qml"
while read -r page; do
[[ -n "$page" ]] || continue
[[ "$page" == "home" ]] && continue
expected[settings-$page]="settings-page $page"
done < <(grep -oE '\{ page: "[a-z-]+"' "$sidebar" | sed 's/.*"\([a-z-]*\)"/\1/')
done < <( {
grep -oE '\{ page: "[a-z-]+", label: "[^"]*", icon: "[^"]*", tabs: \[\] \}' "$routes"
grep -oE '\{ page: "[a-z-]+", label: "[^"]*" \}' "$routes"
} | sed -E 's/\{ page: "([a-z-]+)".*/\1/' | sort -u)
(( ${#expected[@]} > 18 )) || fail 'no generated per-page commands were found; run scripts/panama-settings-commands'