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
149 lines
7.3 KiB
Bash
Executable File
149 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# The lock screen follows the color scheme.
|
|
#
|
|
# It did not. hyprlock.conf shipped with Tokyo Night Moon hardcoded in six
|
|
# places, so choosing light mode left the one screen a user sees most often
|
|
# stubbornly dark. Every other surface -- kitty, GTK, the launcher, btop, tmux,
|
|
# neovim -- had been taught to follow the scheme; this was the last one.
|
|
#
|
|
# It is also the worst place to discover a theming bug, because you find out
|
|
# while locked out of the machine and cannot fix it from there. Hence a test.
|
|
#
|
|
# Generated into a fixture, never the live config: this contract must not
|
|
# retheme the lock screen of the desktop it is running on.
|
|
|
|
set -uo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
template="$repo_dir/config/dot/hypr/hyprlock.conf.template"
|
|
theme_apps="$repo_dir/config/dot/quickshell/scripts/panama-theme-apps"
|
|
|
|
fail() {
|
|
printf 'lock screen theme contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
[[ -r "$template" ]] || fail 'hyprlock.conf.template is missing, so nothing generates the lock screen'
|
|
|
|
# The committed template must carry no literal colors. One left behind is a
|
|
# color that silently stays dark in light mode -- exactly the original bug.
|
|
literal="$(grep -vE '^\s*#' "$template" | grep -oE 'rgba\([0-9]+, *[0-9]+, *[0-9]+' || true)"
|
|
[[ -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"
|
|
cp "$template" "$fixture/hypr/"
|
|
|
|
for scheme in dark light; do
|
|
XDG_CONFIG_HOME="$fixture" "$theme_apps" "$scheme" >/dev/null 2>&1
|
|
generated="$fixture/hypr/hyprlock.conf"
|
|
|
|
[[ -r "$generated" ]] || fail "no hyprlock.conf was generated for $scheme"
|
|
|
|
# An unsubstituted placeholder is not a parse error to hyprlock; it is an
|
|
# invalid color it quietly ignores, falling back to its own default.
|
|
leftover="$(grep -oE '@[A-Z_]+@' "$generated" || true)"
|
|
[[ -z "$leftover" ]] \
|
|
|| fail "$scheme left placeholders unsubstituted: $leftover"
|
|
|
|
# Every color hyprlock is given must be a complete decimal triple. It
|
|
# takes rgba(r, g, b, a), NOT hex, and a hex value here is silently ignored.
|
|
while read -r color; do
|
|
[[ -n "$color" ]] || continue
|
|
grep -qE '^rgba\([0-9]{1,3}, [0-9]{1,3}, [0-9]{1,3}$' <<<"$color" \
|
|
|| fail "$scheme produced a malformed color: $color"
|
|
done < <(grep -vE '^\s*#' "$generated" | grep -oE 'rgba\([^)]*' | sed 's/,[^,]*$//')
|
|
|
|
# The Pango markup values are hex, and hyprlock wants them doubled-hashed.
|
|
while read -r pango; do
|
|
[[ -n "$pango" ]] || continue
|
|
grep -qE '^##[0-9a-f]{6}$' <<<"$pango" \
|
|
|| fail "$scheme produced malformed Pango markup color: $pango"
|
|
done < <(grep -vE '^\s*#' "$generated" | grep -oE '##[0-9a-fA-F]{6}')
|
|
|
|
# The whole background block, not a fixed number of lines after it: the
|
|
# color sits near the end, after the blur and noise settings.
|
|
background="$(awk '/^background \{/,/^\}/' "$generated" \
|
|
| grep -vE '^\s*#' | grep -oE 'color = rgba\([0-9]+' | grep -oE '[0-9]+$' | head -1)"
|
|
[[ -n "$background" ]] || fail "$scheme produced no background color"
|
|
|
|
# The point of the whole exercise: a light lock screen must actually be
|
|
# light. 128 splits the two cleanly for these palettes.
|
|
if [[ "$scheme" == "light" ]]; then
|
|
(( background > 128 )) \
|
|
|| fail "light mode produced a DARK lock screen background (red channel $background) -- the original bug"
|
|
else
|
|
(( background < 128 )) \
|
|
|| fail "dark mode produced a LIGHT lock screen background (red channel $background)"
|
|
fi
|
|
done
|
|
|
|
# The two schemes must actually differ, or the substitution is a no-op that
|
|
# passes every check above.
|
|
XDG_CONFIG_HOME="$fixture" "$theme_apps" dark >/dev/null 2>&1
|
|
dark_hash="$(sha256sum "$fixture/hypr/hyprlock.conf" | cut -d' ' -f1)"
|
|
XDG_CONFIG_HOME="$fixture" "$theme_apps" light >/dev/null 2>&1
|
|
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'
|