#!/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'