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 -8
View File
@@ -12,10 +12,10 @@
# longer true, and no test could notice because none of them knew what Panama
# had come to own in the meantime.
#
# This reads the sidebar for the pages that exist and the pages for the panels
# they hand off, and fails on any overlap. It is deliberately derived from both
# sides rather than from a hand-kept list, so absorbing the next page cannot
# leave a stale door behind.
# This reads SettingsRoutes for the pages that exist and the pages for the
# panels they hand off, and fails on any overlap. It is deliberately derived
# from both sides rather than from a hand-kept list, so absorbing the next page
# cannot leave a stale door behind.
#
# Read-only.
@@ -23,14 +23,14 @@ set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
settings_dir="$repo_dir/config/dot/quickshell/modules/settings"
sidebar="$settings_dir/SettingsSidebar.qml"
routes="$repo_dir/config/dot/quickshell/services/SettingsRoutes.qml"
fail() {
printf 'gnome handoff contract: %s\n' "$1" >&2
exit 1
}
[[ -r "$sidebar" ]] || fail "missing $sidebar"
[[ -r "$routes" ]] || fail "missing $routes"
# GNOME panel names that correspond to a Panama page. Only entries whose panel
# genuinely duplicates a Panama page belong here: "network" stays off it because
@@ -52,8 +52,14 @@ declare -A ALLOWED=(
["UsersPage.qml:system-users"]="fingerprint enrollment requires fprintd's guided capture flow, and GNOME's Users panel carries the only good dialog for it"
)
pages="$(grep -oE '\{ *page: "[a-z-]+"' "$sidebar" | sed 's/.*"\(.*\)"/\1/' | sort -u)"
[[ -n "$pages" ]] || fail 'no pages could be read from the sidebar, so this proves nothing'
# The leaves: a tabless category is a page in its own right, and every tab is
# a page. A category that only groups tabs owns no controls itself, so it is
# not something GNOME could be handing a duplicate of.
pages="$( {
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)"
[[ -n "$pages" ]] || fail 'no pages could be read from SettingsRoutes, so this proves nothing'
has_page() {
grep -qx "$1" <<<"$pages"