GNOME's Search panel configures which applications provide results in gnome-shell's overview and which folders are indexed. gnome-shell does not run here, so reimplementing those switches would store preferences that change nothing. Under Panama searching is the launcher's job and Vicinae carries its own preferences, so the Applications page says where search lives and how to rebind the keys that open it, rather than offering settings that would be inert or duplicated. The nav contract is the more useful half. Adding a settings page means editing four files, and missing any one of them fails quietly in a different way: no sidebar row, a row that silently shows Home, a deep link that redirects to Home, or -- worst -- a missing qmldir entry, which makes the page "not a type" and takes the entire settings window down with it. Nothing at runtime cross-checks the four. Having just added four pages by hand, this checks them statically, and also fails on a page file that exists but is unreachable. Verified it catches a missing qmldir registration and a missing allow-list entry. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
90 lines
4.2 KiB
Bash
Executable File
90 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Adding a settings page means editing four separate files, and missing one
|
|
# fails quietly rather than loudly:
|
|
#
|
|
# SettingsSidebar.qml the row you click
|
|
# SettingsShell.qml the case that maps that row to a component, AND the
|
|
# Component declaration itself
|
|
# ShellState.qml the allow-list openSettings() checks -- a page missing
|
|
# here silently redirects to Home, so a deep link or a
|
|
# search result lands on the wrong page with no error
|
|
# modules/settings/qmldir the component registration -- without it the page
|
|
# is "not a type" and the whole settings window fails
|
|
# to load, taking every other page with it
|
|
#
|
|
# Nothing at runtime cross-checks the four. This does, statically.
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
settings_dir="$repo_dir/config/dot/quickshell/modules/settings"
|
|
sidebar="$settings_dir/SettingsSidebar.qml"
|
|
shell_file="$settings_dir/SettingsShell.qml"
|
|
qmldir="$settings_dir/qmldir"
|
|
shell_state="$repo_dir/config/dot/quickshell/services/ShellState.qml"
|
|
|
|
fail() {
|
|
printf 'settings nav contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
for required in "$sidebar" "$shell_file" "$qmldir" "$shell_state"; do
|
|
[[ -r "$required" ]] || fail "cannot read $required"
|
|
done
|
|
|
|
# ── Every sidebar row resolves everywhere ────────────────────────────────────
|
|
pages="$(grep -oE '\{ page: "[a-z-]+"' "$sidebar" | sed 's/.*"\(.*\)"/\1/')"
|
|
[[ -n "$pages" ]] || fail 'no pages found in the sidebar -- this contract is not reading it correctly'
|
|
|
|
allowed_line="$(grep -m1 'const allowed = \[' "$shell_state")" \
|
|
|| fail 'could not find the allow-list in ShellState'
|
|
|
|
while read -r page; do
|
|
[[ -n "$page" ]] || continue
|
|
|
|
# Home is the switch's default arm rather than a case, since it is also
|
|
# where an unknown page falls back to.
|
|
if [[ "$page" != "home" ]]; then
|
|
grep -qE "case \"$page\": return [a-zA-Z]+;" "$shell_file" \
|
|
|| fail "the sidebar offers \"$page\" but SettingsShell has no case for it, so clicking it shows Home"
|
|
fi
|
|
|
|
grep -qF "\"$page\"" <<<"$allowed_line" \
|
|
|| fail "\"$page\" is missing from ShellState's allow-list, so openSettings(\"$page\") silently redirects to Home"
|
|
done <<<"$pages"
|
|
|
|
# ── Every routed component is declared and registered ────────────────────────
|
|
# The case arms name a Component id; each must have a declaration, and the type
|
|
# it instantiates must appear in qmldir.
|
|
while read -r component; do
|
|
[[ -n "$component" ]] || continue
|
|
|
|
declaration="$(grep -oE "Component \{ id: $component; [A-Za-z]+ \{\} \}" "$shell_file")" \
|
|
|| fail "SettingsShell routes to \"$component\" but never declares it"
|
|
|
|
type_name="$(sed -E 's/.*; ([A-Za-z]+) \{\} \}/\1/' <<<"$declaration")"
|
|
grep -qE "^$type_name [0-9.]+ $type_name\.qml$" "$qmldir" \
|
|
|| fail "$type_name is not registered in modules/settings/qmldir -- it will fail to load as \"not a type\", and the whole settings window fails with it"
|
|
|
|
[[ -r "$settings_dir/$type_name.qml" ]] \
|
|
|| fail "$type_name is registered in qmldir but $type_name.qml does not exist"
|
|
done < <({
|
|
grep -oE 'case "[a-z-]+": return [a-zA-Z]+;' "$shell_file" | sed -E 's/.*return ([a-zA-Z]+);/\1/'
|
|
grep -oE 'default: return [a-zA-Z]+;' "$shell_file" | sed -E 's/.*return ([a-zA-Z]+);/\1/'
|
|
} | sort -u)
|
|
|
|
# ── Every page file is reachable ─────────────────────────────────────────────
|
|
# A page nobody can navigate to is dead code that still has to compile. The
|
|
# scaffold SettingsPage.qml is the one file here that is a base class rather
|
|
# than a page.
|
|
while read -r page_file; do
|
|
type_name="$(basename "$page_file" .qml)"
|
|
[[ "$type_name" == "SettingsPage" ]] && continue
|
|
|
|
grep -qE "; $type_name \{\} \}" "$shell_file" \
|
|
|| fail "$type_name.qml exists but nothing in SettingsShell instantiates it"
|
|
done < <(find "$settings_dir" -maxdepth 1 -name '*Page.qml')
|
|
|
|
printf 'settings nav contract: PASS\n'
|