From addce489b4ba0754a13bbfcb9f3581472b95dd96 Mon Sep 17 00:00:00 2001 From: Anton Volnuhin Date: Thu, 6 Aug 2026 01:49:45 +0300 Subject: [PATCH] kitty: remember the light/dark mode across restarts kitty gives *-theme.auto.conf priority over everything -- includes and even --override -- and has no option to stop following the OS. So the only way to start in a chosen mode is to control what those files contain. Make them generated: themes/{light,dark,no-preference}.conf canonical, the real themes theme-mode auto | light | dark apply-theme-mode.sh writes the three .auto.conf Under `auto` each appearance keeps its own theme, so kitty follows the OS as before. Pinned, all three get the pinned theme, so kitty starts in it whatever the OS says and a config reload no longer resets it. `theme` now records the mode and reloads instead of calling set-colors, which is why the setting survives restarts. A bare `theme` records `auto` rather than pinning when the toggle lands on the macOS appearance, so nothing is pinned unless it actually differs. The kitten shim hashes the automatic theme files before running `kitten themes` and copies whichever changed into themes/, so picking a new light theme while pinned to dark is kept and applied on the next switch. Covered by tests/test_theme_mode.sh. conf.d/kitty_theme.fish is gone: theme-mode is now the source of truth, so the universal-variable tracker it maintained is redundant. --- .../private_executable_apply-theme-mode.sh | 68 ++++++++ .../private_executable_test_theme_mode.sh | 152 ++++++++++++++++++ private_dot_config/kitty/theme-mode | 1 + .../kitty/themes/private_dark.conf | 31 ++++ .../kitty/themes/private_light.conf | 31 ++++ .../kitty/themes/private_no-preference.conf | 28 ++++ .../private_fish/conf.d/kitty_theme.fish | 16 -- .../functions/__kitty_config_dir.fish | 7 + .../functions/__kitty_macos_appearance.fish | 7 + .../functions/__kitty_theme_current.fish | 15 +- .../functions/__kitty_theme_mode_setting.fish | 13 ++ .../functions/private_kitten.fish | 49 +++++- .../private_fish/functions/theme.fish | 73 ++++----- 13 files changed, 421 insertions(+), 70 deletions(-) create mode 100644 private_dot_config/kitty/private_executable_apply-theme-mode.sh create mode 100644 private_dot_config/kitty/private_tests/private_executable_test_theme_mode.sh create mode 100644 private_dot_config/kitty/theme-mode create mode 100644 private_dot_config/kitty/themes/private_dark.conf create mode 100644 private_dot_config/kitty/themes/private_light.conf create mode 100644 private_dot_config/kitty/themes/private_no-preference.conf delete mode 100644 private_dot_config/private_fish/conf.d/kitty_theme.fish create mode 100644 private_dot_config/private_fish/functions/__kitty_config_dir.fish create mode 100644 private_dot_config/private_fish/functions/__kitty_macos_appearance.fish create mode 100644 private_dot_config/private_fish/functions/__kitty_theme_mode_setting.fish diff --git a/private_dot_config/kitty/private_executable_apply-theme-mode.sh b/private_dot_config/kitty/private_executable_apply-theme-mode.sh new file mode 100644 index 0000000..5039f4e --- /dev/null +++ b/private_dot_config/kitty/private_executable_apply-theme-mode.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +# Regenerate the *-theme.auto.conf files kitty reads at startup from the +# canonical themes in ./themes, honouring the mode recorded in ./theme-mode: +# +# auto every appearance keeps its own theme, so kitty follows the OS +# light every appearance gets the light theme, so kitty stays light +# dark every appearance gets the dark theme, so kitty stays dark +# +# kitty gives the automatic theme files priority over everything else -- includes +# and even --override -- so pinning a mode means writing the wanted theme into +# whichever file the OS makes kitty pick. That is why the real themes live in +# ./themes and these three files are generated. + +set -eu + +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +config_dir=${1:-"$script_dir"} +canonical_dir="$config_dir/themes" +mode_file="$config_dir/theme-mode" + +mode=auto +if [ -f "$mode_file" ]; then + mode=$(tr -d ' \t\r\n' < "$mode_file") +fi +case "$mode" in + auto | light | dark) ;; + *) mode=auto ;; +esac + +mkdir -p "$canonical_dir" + +# Adopt the current automatic themes as canonical when they have not been +# captured yet. Only safe while following the OS: under a pinned mode the +# generated files all hold the pinned theme and would overwrite the others. +if [ "$mode" = auto ]; then + for appearance in dark light no-preference; do + canonical="$canonical_dir/$appearance.conf" + generated="$config_dir/$appearance-theme.auto.conf" + if [ ! -f "$canonical" ] && [ -f "$generated" ]; then + cp "$generated" "$canonical" + fi + done +fi + +for appearance in dark light no-preference; do + if [ "$mode" = auto ]; then + source_name=$appearance + else + source_name=$mode + fi + + canonical="$canonical_dir/$source_name.conf" + [ -f "$canonical" ] || continue + + generated="$config_dir/$appearance-theme.auto.conf" + mode_bits=600 + [ -f "$generated" ] && mode_bits=$(stat -f '%Lp' "$generated") + + tmp=$(mktemp "$generated.tmp.XXXXXX") + trap 'rm -f "$tmp"' EXIT HUP INT TERM + cat "$canonical" > "$tmp" + chmod "$mode_bits" "$tmp" + mv -f "$tmp" "$generated" + trap - EXIT HUP INT TERM +done + +"$script_dir/repair-auto-themes.sh" "$config_dir" diff --git a/private_dot_config/kitty/private_tests/private_executable_test_theme_mode.sh b/private_dot_config/kitty/private_tests/private_executable_test_theme_mode.sh new file mode 100644 index 0000000..3cf01d2 --- /dev/null +++ b/private_dot_config/kitty/private_tests/private_executable_test_theme_mode.sh @@ -0,0 +1,152 @@ +#!/bin/sh + +# Behavioural regression for the remembered light/dark mode: +# - apply-theme-mode.sh generates the automatic theme files from ./themes +# - a pinned mode puts one theme in all three, so kitty starts in it +# - `theme auto` restores per-appearance themes without losing any +# - `kitten themes` changes are captured even while a mode is pinned +# - a bare `theme` records `auto` when the result already matches macOS + +set -eu + +root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +fish_bin=${FISH:-$(command -v fish)} +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT HUP INT TERM + +config_dir="$tmpdir/config" +fake_bin="$tmpdir/bin" +mkdir -p "$config_dir/themes" "$fake_bin" + +DARK='#111111' +LIGHT='#eeeeee' +NOPREF='#222222' +NEW_LIGHT='#abcdef' + +printf 'background %s\nmacos_titlebar_color background\n' "$DARK" \ + >"$config_dir/themes/dark.conf" +printf 'background %s\nmacos_titlebar_color background\n' "$LIGHT" \ + >"$config_dir/themes/light.conf" +printf 'background %s\nmacos_titlebar_color background\n' "$NOPREF" \ + >"$config_dir/themes/no-preference.conf" + +bg() { + awk '/^background/ { print $2; exit }' "$1" +} + +expect() { + # expect