diff --git a/.chezmoiignore b/.chezmoiignore index 73125a2..ec17d8c 100644 --- a/.chezmoiignore +++ b/.chezmoiignore @@ -65,6 +65,7 @@ Library # every machine flag the others' mode. run_after_kitty-theme-mode.sh # regenerates them after apply. .config/kitty/theme-mode +.config/kitty/.theme-generated .config/kitty/dark-theme.auto.conf .config/kitty/light-theme.auto.conf .config/kitty/no-preference-theme.auto.conf diff --git a/private_dot_config/kitty/private_executable_apply-theme-mode.sh b/private_dot_config/kitty/private_executable_apply-theme-mode.sh index 6b97eff..3744bf1 100644 --- a/private_dot_config/kitty/private_executable_apply-theme-mode.sh +++ b/private_dot_config/kitty/private_executable_apply-theme-mode.sh @@ -47,18 +47,46 @@ 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 +# Checksums of the files as this script last wrote them. Anything that differs +# now was rewritten by something else -- in practice the themes kitten saving a +# theme for that appearance -- so it is a new choice and becomes canonical. +# Recording it here rather than in the fish `kitten` wrapper means the choice +# survives however the kitten was launched: `kitty +kitten themes`, a keybinding +# or another shell all bypass that wrapper. +state_file="$config_dir/.theme-generated" + +# cksum is POSIX, unlike shasum/sha256sum which differ across platforms. +sum_of() { + [ -f "$1" ] || return 1 + cksum < "$1" +} + +recorded_sum() { + [ -f "$state_file" ] || return 1 + awk -v want="$1" '$1 == want { $1 = ""; sub(/^ /, ""); print; found = 1 } + END { exit !found }' "$state_file" +} + +for appearance in dark light no-preference; do + canonical="$canonical_dir/$appearance.conf" + generated="$config_dir/$appearance-theme.auto.conf" + [ -f "$generated" ] || continue + + # First adoption of an existing setup: take whatever is there, but only + # while following the OS. Under a pinned mode every generated file holds + # the pinned theme, so adopting them would overwrite the other appearances. + if [ ! -f "$canonical" ]; then + [ "$mode" = auto ] && cp "$generated" "$canonical" + continue + fi + + # With no state yet we cannot tell a fresh choice from a file we wrote, so + # leave canonical alone; the checksums recorded below make the next run + # able to tell. + now=$(sum_of "$generated") || continue + was=$(recorded_sum "$appearance") || continue + [ "$now" = "$was" ] || cp "$generated" "$canonical" +done for appearance in dark light no-preference; do if [ "$mode" = auto ]; then @@ -85,3 +113,16 @@ for appearance in dark light no-preference; do done "$script_dir/repair-auto-themes.sh" "$config_dir" + +# Record what we ended up with, after the repair step, so the next run can tell +# an externally-saved theme from one of ours. +tmp_state=$(mktemp "$state_file.tmp.XXXXXX") +trap 'rm -f "$tmp_state"' EXIT HUP INT TERM +for appearance in dark light no-preference; do + generated="$config_dir/$appearance-theme.auto.conf" + [ -f "$generated" ] || continue + printf '%s %s\n' "$appearance" "$(sum_of "$generated")" >> "$tmp_state" +done +chmod 600 "$tmp_state" +mv -f "$tmp_state" "$state_file" +trap - EXIT HUP INT TERM 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 index 567998f..7f00381 100644 --- 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 @@ -117,6 +117,37 @@ expect "new light theme is used once selected" \ expect "dark theme survived the whole exchange" \ "$config_dir/theme-modes/dark.conf" "$DARK" +# --- a theme saved without going through the fish shim is still kept ----- +# `kitty +kitten themes`, a keybinding, or any non-fish shell writes the +# automatic theme file directly and never runs the `kitten` function. The +# capture therefore lives in apply-theme-mode.sh, keyed on checksums, or the +# next regeneration silently reverts the choice. +echo dark >"$config_dir/theme-mode" +"$root/apply-theme-mode.sh" "$config_dir" + +BYPASS_DARK='#0d0f31' +printf 'background %s\n' "$BYPASS_DARK" >"$config_dir/dark-theme.auto.conf" +"$root/apply-theme-mode.sh" "$config_dir" +expect "theme saved outside the shim becomes canonical" \ + "$config_dir/theme-modes/dark.conf" "$BYPASS_DARK" +expect "and stays applied" "$config_dir/dark-theme.auto.conf" "$BYPASS_DARK" + +# the same for a light theme while the pin is on dark +BYPASS_LIGHT='#fafafa' +printf 'background %s\n' "$BYPASS_LIGHT" >"$config_dir/light-theme.auto.conf" +"$root/apply-theme-mode.sh" "$config_dir" +expect "light theme saved outside the shim becomes canonical" \ + "$config_dir/theme-modes/light.conf" "$BYPASS_LIGHT" +expect "but the dark pin still wins on screen" \ + "$config_dir/light-theme.auto.conf" "$BYPASS_DARK" + +# and a further run must not mistake our own output for a new choice +"$root/apply-theme-mode.sh" "$config_dir" +expect "adoption is idempotent (dark)" \ + "$config_dir/theme-modes/dark.conf" "$BYPASS_DARK" +expect "adoption is idempotent (light)" \ + "$config_dir/theme-modes/light.conf" "$BYPASS_LIGHT" + # --- a bare `theme` records auto when it lands on the macOS appearance ---- echo auto >"$config_dir/theme-mode" "$root/apply-theme-mode.sh" "$config_dir" diff --git a/private_dot_config/kitty/theme-modes/private_dark.conf b/private_dot_config/kitty/theme-modes/private_dark.conf index 824b9ce..8ba5d9a 100644 --- a/private_dot_config/kitty/theme-modes/private_dark.conf +++ b/private_dot_config/kitty/theme-modes/private_dark.conf @@ -1,29 +1,37 @@ -background #1c2836 -foreground #ffffff -cursor #bbbbbb -selection_background #b4d5ff -color0 #000000 -color8 #545454 -color1 #f9555f -color9 #fa8b8e -color2 #20af89 -color10 #34bb99 -color3 #fdf029 -color11 #ffff55 -color4 #589cf5 -color12 #589cf5 -color5 #934d95 -color13 #e75598 -color6 #1e9ee6 -color14 #3978bb -color7 #bbbbbb -color15 #ffffff -selection_foreground #1c2836 +# 1984 theme by Bertie Blackman, https://github.com/covertbert/iterm2-1984/ + +foreground #ffffff +background #0d0f31 +selection_foreground #000000 +selection_background #00d5eb +color0 #000000 +color1 #ff16b0 +color2 #b3f361 +color3 #ffea16 +color4 #46bdff +color5 #f806fa +color6 #59e1e3 +color7 #feffff +color8 #000000 +color9 #ff16b0 +color10 #b3f361 +color11 #ffea16 +color12 #46bdff +color13 #f806fa +color14 #6be4e6 +color15 #feffff + +# URL styles +url_color #f806fa +url_style single + +# Cursor styles +cursor #59e1e3 # START_AUTOGENERATED_TAB_STYLE # Feel free to update these colors manually and remove these comments. -active_tab_foreground #444444 -active_tab_background #b4d5ff +active_tab_foreground #eeeeee +active_tab_background #00d5eb inactive_tab_foreground #ffffff -inactive_tab_background #16202b +inactive_tab_background #0a0c27 # END_AUTOGENERATED_TAB_STYLE diff --git a/private_dot_config/private_fish/functions/private_kitten.fish b/private_dot_config/private_fish/functions/private_kitten.fish index 767a345..1da787b 100644 --- a/private_dot_config/private_fish/functions/private_kitten.fish +++ b/private_dot_config/private_fish/functions/private_kitten.fish @@ -4,49 +4,17 @@ function kitten --description 'Run kitten and restore Kitty theme overrides' set config_dir "$KITTY_CONFIG_DIRECTORY" end - set -l is_themes false - if test (count $argv) -gt 0; and test "$argv[1]" = themes - set is_themes true - end - - # The automatic theme files are generated from $config_dir/theme-modes, so record - # their state first: whichever one the kitten rewrites is the theme just - # chosen for that appearance, and has to be captured before the mode is - # re-applied over the top of it. - set -l appearances dark light no-preference - set -l before - if test $is_themes = true - for appearance in $appearances - set -l file "$config_dir/$appearance-theme.auto.conf" - if test -r "$file" - set -a before (command shasum <"$file" | string split -f1 ' ') - else - set -a before missing - end - end - end - command kitten $argv set -l kitten_status $status + # Re-apply the pinned mode over whatever the themes kitten just wrote, and + # reload so the change shows immediately. apply-theme-mode.sh does the + # capturing itself, by checksum, so a theme saved through some other route + # -- `kitty +kitten themes`, a keybinding, another shell -- is picked up on + # its next run rather than being lost. if test $kitten_status -eq 0 - and test $is_themes = true - for index in (seq (count $appearances)) - set -l appearance $appearances[$index] - set -l file "$config_dir/$appearance-theme.auto.conf" - test -r "$file" - or continue - - set -l now (command shasum <"$file" | string split -f1 ' ') - test "$now" = "$before[$index]" - and continue - - command mkdir -p "$config_dir/theme-modes" - or return $status - command cp "$file" "$config_dir/theme-modes/$appearance.conf" - or return $status - end - + and test (count $argv) -gt 0 + and test "$argv[1]" = themes "$HOME/.config/kitty/apply-theme-mode.sh" "$config_dir" or return $status