Give the desktop real themes, video wallpapers, and honest titlebars

Appearance now opens on Themes: light and dark side by side, each
remembering its own choice, over galleries of ten shipped themes —
Tokyo Moon and Day joined by Moon Rose, Catppuccin, Nord, Gruvbox and
Everforest in both modes. A theme is a complete palette: the catalog
lives in themes.json, Theme.qml reads every color token from the
active record, and one render pipeline carries it to kitty, tmux,
btop, GTK, Vicinae, Firefox's chrome, and the lock screen. The Theme
editor builds new ones from four wells — wheel, hex, or eyedropper —
with derived surfaces, a saturation slider, debounced fine-tune, and
effects that save with the theme. Custom edits finally keep GNOME's
accent, kitty's border, and hyprlock in sync.

Wallpapers can be video: mpvpaper per output, hardware-decoded, muted
and looped, supervised and respawned. Panama owns the pausing — games,
battery, and a bar pill for right now — because the compositor
rebuilds full-screen blur for every frame a video wallpaper draws.
The lock screen gets a still frame.

Titlebars stop lying. GNOME apps get close-only on your chosen side,
the maximize and double-click settings are gone, the Settings window
obeys the same rules, and its titlebar can be turned off entirely.
Typography becomes five labeled dropdowns instead of a wall of
samples.

Contracts updated and written throughout (165 now); per the redesign
workflow none were executed — the full sweep runs once at the end.

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-23 23:39:04 -04:00
parent 7578348db1
commit cb7c09d208
68 changed files with 6115 additions and 1138 deletions
@@ -32,6 +32,33 @@ literal="$(grep -vE '^\s*#' "$template" | grep -oE 'rgba\([0-9]+, *[0-9]+, *[0-9
[[ -z "$literal" ]] \
|| fail "the template still contains hardcoded colors, which will not follow the scheme: $literal"
# ── The colours are written down once ────────────────────────────────────────
# They used to be three copies of the same two tables: one here in
# panama-theme-apps, one in panama-lock, one in setup/scripts/link-dotfiles.
# The copy that got missed was always the seed, because a wrong seed only shows
# up the first time somebody locks a fresh machine -- and by then they are
# locked out of the machine they would fix it on.
helper="$repo_dir/config/dot/quickshell/scripts/panama-palette"
lock_script="$repo_dir/config/dot/quickshell/scripts/panama-lock"
linker="$repo_dir/setup/scripts/link-dotfiles"
grep -q '^render_hyprlock()' "$helper" \
|| fail 'panama-palette does not define render_hyprlock, so the eight substitutions have gone back to living per-caller'
for consumer in "$theme_apps" "$linker"; do
grep -q 'render_hyprlock' "$consumer" \
|| fail "$(basename "$consumer") no longer fills the template through the shared renderer"
done
grep -q 'resolve_theme' "$lock_script" \
|| fail 'panama-lock no longer resolves the theme through the shared resolver, so it has its own palette again'
# A quoted decimal triple in any of them is the table growing back.
for consumer in "$theme_apps" "$lock_script" "$linker"; do
restated="$(grep -vE '^\s*#' "$consumer" \
| grep -oE "['\"][0-9]{1,3}, [0-9]{1,3}, [0-9]{1,3}['\"]" || true)"
[[ -z "$restated" ]] \
|| fail "$(basename "$consumer") carries hardcoded lock-screen colors again: $restated"
done
fixture="$(mktemp -d /tmp/panama-lockscreen.XXXXXX)"
trap 'rm -rf "$fixture"' EXIT
mkdir -p "$fixture/hypr"
@@ -90,4 +117,32 @@ light_hash="$(sha256sum "$fixture/hypr/hyprlock.conf" | cut -d' ' -f1)"
[[ "$dark_hash" != "$light_hash" ]] \
|| fail 'the light and dark lock screens are byte-identical, so the scheme is not being applied'
# ── A THEME reaches it, not only a scheme ────────────────────────────────────
# The lock screen followed light and dark long before it followed themes:
# choosing Catppuccin restyled the shell, the terminal and the launcher, and
# left the lock screen in Tokyo Night.
catalog="$repo_dir/config/dot/quickshell/config/themes.json"
if [[ -r "$catalog" ]]; then
default_dark="$(jq -r '.defaultDark' "$catalog")"
# A theme whose BACKGROUND differs from the default's, not merely its id:
# Moon Rose is Moon's palette with another accent, and asserting against it
# would pass even if the generator ignored the theme entirely.
other="$(jq -r --arg default "$default_dark" '
(.themes[] | select(.id == $default) | .palette.bg) as $base
| [.themes[] | select(.scheme == "dark" and .palette.bg != $base)][0].id' "$catalog")"
if [[ -n "$other" && "$other" != "null" ]]; then
want="$(jq -r --arg id "$other" '.themes[] | select(.id == $id) | .palette.bg' "$catalog")"
triple="$(printf '%d, %d, %d' "0x${want:1:2}" "0x${want:3:2}" "0x${want:5:2}")"
mkdir -p "$fixture/panama"
jq -n --arg id "$other" '{colorScheme: "dark", themeProfileId: $id}' \
>"$fixture/panama/settings.json"
XDG_CONFIG_HOME="$fixture" "$theme_apps" dark >/dev/null 2>&1
grep -q "rgba($triple, 1.0)" "$fixture/hypr/hyprlock.conf" \
|| fail "selecting the \"$other\" theme did not reach the lock screen: its background ($want) is nowhere in the generated config"
fi
fi
printf 'lock screen theme contract: PASS\n'