Panama had grown into three configuration surfaces that only agreed because they had been typed to agree: looks.lua hardcoded values, DesktopPreferences independently defaulted the same values, and SystemSettings replayed them at startup. Nothing kept them in sync, and the Lua side read no shared state at all. This lands the first three stages of docs/superpowers/plans/2026-08-17-panama-cohesion.md. Fix silently failing Hyprland writes. On a Lua-configured Hyprland, hyprctl keyword refuses the write, prints the refusal to stdout, and still exits 0, so the HDR, VRR, and direct-scanout toggles persisted their value and reported success while the compositor never changed. Writes now go through hyprctl eval, which has the same hazard on syntax and runtime errors, so success is defined as reading the value back and finding it equal. The existing contract passed throughout the outage because it re-applied the values already in place; the new one flips each value to something it does not hold. Derive preferences from a schema. Every setting used to be restated four times -- a property alias, a JSON adapter property, a change handler, and a line in reset -- where omitting any one failed silently. PreferenceSchema.qml is now the single source, and persistence, validation, reset, and the Hyprland mapping all derive from it. Unknown keys on disk survive a write so a rollback does not discard a newer build's settings, and a corrupt file falls back to shipped defaults. The store moved to ~/.config/panama/settings.json, migrating from the old state directory without deleting it. Share that file with Hyprland. prefs.lua reads it at config time with every shipped literal kept as the fallback, so the config still stands alone. The Lua is the default, the JSON is the truth, and Settings is the editor. The compositor-adjustable surface goes from 3 keys to 23. Also fixes two test-hygiene bugs found by running the suite end to end for the first time: settings-pages-contract could see the window settings-window-contract leaves behind, and the new write contract was persisting its deliberately-wrong values into the user's real store. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
61 lines
3.0 KiB
Bash
Executable File
61 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
fail() {
|
|
printf 'settings pages contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup() {
|
|
qs ipc call settings close >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Start from a clean slate. Closing the Settings window is asynchronous: the
|
|
# shell reports it closed as soon as it drops its own state, while the toplevel
|
|
# survives until the compositor destroys it. Without this wait, running straight
|
|
# after settings-window-contract sees the outgoing window and reads it as a
|
|
# duplicate.
|
|
qs ipc call settings close >/dev/null 2>&1 || true
|
|
for _ in $(seq 1 40); do
|
|
hyprctl -j clients | jq -e '[.[] | select(.title == "Panama Settings")] | length == 0' >/dev/null && break
|
|
sleep 0.1
|
|
done
|
|
hyprctl -j clients | jq -e '[.[] | select(.title == "Panama Settings")] | length == 0' >/dev/null \
|
|
|| fail 'a Settings window was still open when the contract started'
|
|
|
|
pages=(home appearance displays connectivity desktop sound notifications screen-intelligence shortcuts services about)
|
|
for page in "${pages[@]}"; do
|
|
qs ipc call settings page "$page" >/dev/null
|
|
for _ in $(seq 1 20); do
|
|
[[ "$(qs ipc call settings status | jq -r .page)" == "$page" ]] && break
|
|
sleep 0.1
|
|
done
|
|
[[ "$(qs ipc call settings status | jq -r .page)" == "$page" ]] || fail "$page did not route"
|
|
hyprctl -j clients | jq -e '[.[] | select(.title == "Panama Settings")] | length == 1' >/dev/null \
|
|
|| fail "$page created a missing or duplicate Settings window"
|
|
done
|
|
|
|
qs ipc call settings page '__unsupported__' >/dev/null
|
|
[[ "$(qs ipc call settings status | jq -r .page)" == "home" ]] || fail 'unsupported page did not fall back to Home'
|
|
|
|
hyprctl -j binds | jq -e '.[] | select(.description == "Panama Settings" and .key == "I" and .modmask == 64)' >/dev/null \
|
|
|| fail 'Super+I is not registered as Panama Settings'
|
|
hyprctl -j binds | jq -e '.[] | select(.description == "Screen Intelligence" and .key == "S" and .modmask == 65)' >/dev/null \
|
|
|| fail 'Super+Shift+S is not registered as Screen Intelligence'
|
|
|
|
desktop_file="$HOME/.local/share/applications/panama-settings.desktop"
|
|
[[ -L "$desktop_file" || -f "$desktop_file" ]] || fail 'searchable desktop entry is not installed'
|
|
desktop-file-validate "$desktop_file" >/dev/null || fail 'desktop entry is invalid'
|
|
gnome_desktop_file="$HOME/.local/share/applications/gnome-settings-hyprland.desktop"
|
|
[[ -L "$gnome_desktop_file" || -f "$gnome_desktop_file" ]] || fail 'searchable GNOME Settings fallback is not installed'
|
|
desktop-file-validate "$gnome_desktop_file" >/dev/null || fail 'GNOME Settings fallback entry is invalid'
|
|
intelligence_desktop_file="$HOME/.local/share/applications/panama-screen-intelligence.desktop"
|
|
[[ -L "$intelligence_desktop_file" || -f "$intelligence_desktop_file" ]] || fail 'Screen Intelligence desktop entry is not installed'
|
|
desktop-file-validate "$intelligence_desktop_file" >/dev/null || fail 'Screen Intelligence desktop entry is invalid'
|
|
|
|
trap - EXIT
|
|
cleanup
|
|
printf 'settings pages contract: PASS\n'
|