tide: ask the terminal for its background instead of reading config files
Over ssh the light prompt segments came out lighter than the background instead of darker. __tide_terminal_background_color learned the background by reading <appearance>-theme.auto.conf, which on a remote host belongs to *that* machine: devry pins theme-mode to dark, so apply-theme-mode.sh writes the dark theme into all three slots there, including light-theme.auto.conf. Asked for a light background it found #212121, correctly refused it as contradicting the appearance, ran out of files and left the caller on its plain-white fallback -- and a segment derived from white sits above a #EBE8E4 background rather than below it. Dark was unaffected because that path never recomputes the palette. Ask the terminal instead, with an OSC 11 query: it is the one source that describes the window this shell is actually displayed in, whichever machine the shell runs on. The config files stay as the fallback, where they are still right, and the static white behind them. The query costs one 0.2s-bounded read per appearance change, restores the terminal settings before it can fail, and steps aside under tmux and screen, which need the query wrapped and each want it wrapped differently. Covered by a new live pty scenario that reproduces the ssh case exactly -- config files all dark, terminal reporting light -- which fails on the previous ordering, plus unit tests for the reply parser and the fallback chain.
This commit is contained in:
parent
e5207ac154
commit
af52fcf7a3
@ -1,80 +1,59 @@
|
||||
# vim:ft=kitty
|
||||
|
||||
## name: Catppuccin-Latte
|
||||
## author: Pocco81 (https://github.com/Pocco81)
|
||||
## license: MIT
|
||||
## upstream: https://github.com/catppuccin/kitty/blob/main/themes/latte.conf
|
||||
## blurb: Soothing pastel theme for the high-spirited!
|
||||
|
||||
## name: Kaolin Breeze
|
||||
## author: Ogden Webb
|
||||
## license: GPLv3
|
||||
## blurb: Light Kaolin theme with soft colors
|
||||
|
||||
|
||||
# The basic colors
|
||||
foreground #4C4F69
|
||||
background #EFF1F5
|
||||
selection_foreground #EFF1F5
|
||||
selection_background #DC8A78
|
||||
foreground #383e3f
|
||||
background #EBE8E4
|
||||
selection_foreground #EBE8E4
|
||||
selection_background #383e3f
|
||||
|
||||
# Cursor colors
|
||||
cursor #DC8A78
|
||||
cursor_text_color #EFF1F5
|
||||
cursor #383e3f
|
||||
cursor_text_color #EBE8E4
|
||||
|
||||
# URL underline color when hovering with mouse
|
||||
url_color #DC8A78
|
||||
|
||||
# Kitty window border colors
|
||||
active_border_color #7287FD
|
||||
inactive_border_color #9CA0B0
|
||||
bell_border_color #DF8E1D
|
||||
|
||||
# OS Window titlebar colors
|
||||
wayland_titlebar_color system
|
||||
macos_titlebar_color system
|
||||
# kitty window border colors
|
||||
active_border_color #48a9a9
|
||||
inactive_border_color #7D8468
|
||||
|
||||
# Tab bar colors
|
||||
active_tab_foreground #EFF1F5
|
||||
active_tab_background #8839EF
|
||||
inactive_tab_foreground #4C4F69
|
||||
inactive_tab_background #9CA0B0
|
||||
tab_bar_background #BCC0CC
|
||||
|
||||
# Colors for marks (marked text in the terminal)
|
||||
mark1_foreground #EFF1F5
|
||||
mark1_background #7287fD
|
||||
mark2_foreground #EFF1F5
|
||||
mark2_background #8839EF
|
||||
mark3_foreground #EFF1F5
|
||||
mark3_background #209FB5
|
||||
|
||||
# The 16 terminal colors
|
||||
active_tab_background #383e3f
|
||||
active_tab_foreground #C9C2BD
|
||||
inactive_tab_background #7D8468
|
||||
inactive_tab_foreground #EBE8E4
|
||||
|
||||
# black
|
||||
color0 #5C5F77
|
||||
color8 #6C6F85
|
||||
color0 #383e3f
|
||||
color8 #7D8468
|
||||
|
||||
# red
|
||||
color1 #D20F39
|
||||
color9 #D20F39
|
||||
color1 #cd5c60
|
||||
color9 #ef6787
|
||||
|
||||
# green
|
||||
color2 #40A02B
|
||||
color10 #40A02B
|
||||
color2 #39855f
|
||||
color10 #3e594e
|
||||
|
||||
# yellow
|
||||
color3 #DF8E1D
|
||||
color11 #DF8E1D
|
||||
color3 #b87e3c
|
||||
color11 #d1832e
|
||||
|
||||
# blue
|
||||
color4 #1E66F5
|
||||
color12 #1E66F5
|
||||
color4 #2683b5
|
||||
color12 #4F9CB8
|
||||
|
||||
# magenta
|
||||
color5 #EA76CB
|
||||
color13 #EA76CB
|
||||
color5 #845A84
|
||||
color13 #605DB3
|
||||
|
||||
# cyan
|
||||
color6 #179299
|
||||
color14 #179299
|
||||
color6 #48a9a9
|
||||
color14 #008b8b
|
||||
|
||||
# white
|
||||
color7 #ACB0BE
|
||||
color15 #BCC0CC
|
||||
color7 #C9C2BD
|
||||
color15 #60696b
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
function __tide_parse_osc_background --argument-names reply \
|
||||
--description 'Extract RRGGBB from a terminal OSC 11 background report'
|
||||
|
||||
# The reply is OSC 11 ; rgb:R/G/B ST, with one to four hex digits per
|
||||
# channel. Scan for it rather than anchoring: Fish keeps kitty's colour
|
||||
# scheme notifications subscribed, so an unrelated report can share the
|
||||
# buffer with this one.
|
||||
set -l parts (
|
||||
string match -gr \
|
||||
']11;rgb:([0-9A-Fa-f]{1,4})/([0-9A-Fa-f]{1,4})/([0-9A-Fa-f]{1,4})' \
|
||||
-- "$reply"
|
||||
)
|
||||
test (count $parts) -eq 3
|
||||
or return 1
|
||||
|
||||
set -l hex
|
||||
for part in $parts
|
||||
# Each channel is scaled to the width the terminal chose to report it
|
||||
# in, so f, ff and ffff all mean full intensity.
|
||||
set -l max
|
||||
switch (string length -- $part)
|
||||
case 1
|
||||
set max 15
|
||||
case 2
|
||||
set max 255
|
||||
case 3
|
||||
set max 4095
|
||||
case '*'
|
||||
set max 65535
|
||||
end
|
||||
set -a hex (printf '%02X' (math -s0 "round(255 * 0x$part / $max)"))
|
||||
end
|
||||
|
||||
string join '' $hex
|
||||
end
|
||||
@ -1,18 +1,50 @@
|
||||
function __tide_background_agrees_with --argument-names hex appearance \
|
||||
--description 'True when a background is as light or dark as the appearance claims'
|
||||
|
||||
set -l luminance (__tide_relative_luminance $hex)
|
||||
test -n "$luminance"
|
||||
or return 1
|
||||
|
||||
set -l scaled (math -s0 "round($luminance * 1000)")
|
||||
if test "$appearance" = light
|
||||
test $scaled -gt 500
|
||||
else
|
||||
test $scaled -lt 500
|
||||
end
|
||||
end
|
||||
|
||||
function __tide_terminal_background_color --argument-names appearance \
|
||||
--description 'Print the background color kitty is showing for an appearance'
|
||||
--description 'Print the background color the terminal is showing'
|
||||
|
||||
contains -- $appearance dark light
|
||||
or return 1
|
||||
|
||||
# Ask the terminal first. It is the only source that is right over ssh,
|
||||
# where the config files below belong to the *remote* machine and describe
|
||||
# the mode pinned there, not the window this shell is displayed in. A remote
|
||||
# box pinned to dark offers its dark theme as its "light" one, the check
|
||||
# rejects it, and the caller then falls back to plain white -- lighter than
|
||||
# the real background rather than darker.
|
||||
if functions -q __tide_terminal_osc_background
|
||||
set -l queried (__tide_terminal_osc_background)
|
||||
if string match -qr '^[0-9A-Fa-f]{6}$' -- "$queried"
|
||||
set queried (string upper -- $queried)
|
||||
if __tide_background_agrees_with $queried $appearance
|
||||
echo $queried
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
functions -q __kitty_config_dir
|
||||
or return 1
|
||||
|
||||
set -l config_dir (__kitty_config_dir)
|
||||
|
||||
# kitty gives the *-theme.auto.conf files priority over includes and even
|
||||
# over --override, so whatever is in them is what the window is actually
|
||||
# showing -- whether it got there from `kitten themes` or from
|
||||
# apply-theme-mode.sh pinning a mode.
|
||||
# Locally these describe this very window: kitty gives the
|
||||
# *-theme.auto.conf files priority over includes and even over --override,
|
||||
# so whatever is in them is what it is actually showing -- whether that came
|
||||
# from `kitten themes` or from apply-theme-mode.sh pinning a mode.
|
||||
for file in $config_dir/$appearance-theme.auto.conf $config_dir/no-preference-theme.auto.conf
|
||||
test -r $file
|
||||
or continue
|
||||
@ -22,21 +54,11 @@ function __tide_terminal_background_color --argument-names appearance \
|
||||
or continue
|
||||
set hex (string upper -- $hex)
|
||||
|
||||
set -l luminance (__tide_relative_luminance $hex)
|
||||
test -n "$luminance"
|
||||
or continue
|
||||
|
||||
# A background that disagrees with the appearance Fish reported means
|
||||
# we read the wrong file. A pill derived from the wrong background is
|
||||
# worse than the static fallback, so skip it.
|
||||
set -l scaled (math -s0 "round($luminance * 1000)")
|
||||
if test "$appearance" = light
|
||||
test $scaled -gt 500
|
||||
__tide_background_agrees_with $hex $appearance
|
||||
or continue
|
||||
else
|
||||
test $scaled -lt 500
|
||||
or continue
|
||||
end
|
||||
|
||||
echo $hex
|
||||
return 0
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
function __tide_terminal_osc_background \
|
||||
--description 'Ask the terminal which background it is showing (OSC 11)'
|
||||
|
||||
# Only an interactive shell has anyone to ask. Do not test stdin or stdout
|
||||
# here: the caller always reads this function through a command
|
||||
# substitution, so its stdout is a pipe every single time. /dev/tty is the
|
||||
# terminal however the two are redirected, and the stty below fails
|
||||
# harmlessly if there is no controlling terminal to open.
|
||||
status is-interactive
|
||||
or return 1
|
||||
|
||||
# tmux and screen both need the query wrapped, and each wants it wrapped
|
||||
# differently. Leave those to the caller's config-file path instead of
|
||||
# guessing wrong and eating the user's keystrokes waiting for a reply.
|
||||
set -q TMUX
|
||||
and return 1
|
||||
string match -q 'screen*' -- "$TERM"
|
||||
and return 1
|
||||
|
||||
set -l saved (stty -g </dev/tty 2>/dev/null)
|
||||
test -n "$saved"
|
||||
or return 1
|
||||
|
||||
# min 0 / time 2 makes every read give up after two tenths of a second, so a
|
||||
# terminal that ignores OSC 11 costs that much once per appearance change
|
||||
# and can never hang the shell. Restore the settings the moment the read
|
||||
# loop ends, before anything that could fail.
|
||||
stty raw -echo min 0 time 2 </dev/tty 2>/dev/null
|
||||
printf '\e]11;?\e\\' >/dev/tty 2>/dev/null
|
||||
|
||||
set -l reply
|
||||
for attempt in 1 2 3 4 5
|
||||
set -l chunk (dd bs=64 count=1 </dev/tty 2>/dev/null | string collect -N)
|
||||
test -n "$chunk"
|
||||
or break
|
||||
set reply "$reply$chunk"
|
||||
|
||||
# Stop as soon as a whole report has arrived; a channel that is still
|
||||
# being written would parse as a narrower one and give a wrong colour.
|
||||
string match -q '*]11;rgb:*' -- "$reply"
|
||||
and string match -qr '\a|\e\\\\' -- "$reply"
|
||||
and break
|
||||
end
|
||||
|
||||
stty $saved </dev/tty 2>/dev/null
|
||||
|
||||
__tide_parse_osc_background "$reply"
|
||||
end
|
||||
@ -0,0 +1,5 @@
|
||||
# vim:ft=kitty
|
||||
# A machine pinned to dark puts its dark theme in every slot, so even the
|
||||
# "light" one describes a dark window -- the ssh case this fixture stands in for.
|
||||
background #212121
|
||||
foreground #BDB9AE
|
||||
@ -0,0 +1,5 @@
|
||||
# vim:ft=kitty
|
||||
# A machine pinned to dark puts its dark theme in every slot, so even the
|
||||
# "light" one describes a dark window -- the ssh case this fixture stands in for.
|
||||
background #212121
|
||||
foreground #BDB9AE
|
||||
@ -0,0 +1,5 @@
|
||||
# vim:ft=kitty
|
||||
# A machine pinned to dark puts its dark theme in every slot, so even the
|
||||
# "light" one describes a dark window -- the ssh case this fixture stands in for.
|
||||
background #212121
|
||||
foreground #BDB9AE
|
||||
@ -37,9 +37,10 @@ set -l luminance_helper $project_root/functions/__tide_relative_luminance.fish
|
||||
set -l scale_helper $project_root/functions/__tide_scale_luminance.fish
|
||||
set -l ceiling_helper $project_root/functions/__tide_contrast_ceiling.fish
|
||||
set -l background_probe $project_root/functions/__tide_terminal_background_color.fish
|
||||
set -l osc_parser $project_root/functions/__tide_parse_osc_background.fish
|
||||
|
||||
for file in $light_mapper $appearance_applicator $luminance_helper $scale_helper \
|
||||
$ceiling_helper $background_probe
|
||||
$ceiling_helper $background_probe $osc_parser
|
||||
test -f $file
|
||||
or __test_fail "missing "(path basename $file)
|
||||
end
|
||||
@ -49,6 +50,7 @@ source $luminance_helper
|
||||
source $scale_helper
|
||||
source $ceiling_helper
|
||||
source $background_probe
|
||||
source $osc_parser
|
||||
source $appearance_applicator
|
||||
|
||||
# The applicator exports its overrides, so a shell that already applied a
|
||||
@ -112,7 +114,67 @@ set -l empty_config (mktemp -d)
|
||||
KITTY_CONFIG_DIRECTORY=$empty_config __tide_terminal_background_color light
|
||||
__test_assert_equal 1 $status "the probe reports failure when no theme is readable"
|
||||
|
||||
rm -rf $fake_config $empty_config
|
||||
# --- OSC 11 replies ------------------------------------------------------
|
||||
__test_assert_equal EBE8E4 \
|
||||
(__tide_parse_osc_background \e']11;rgb:ebeb/e8e8/e4e4'\e\\) \
|
||||
"a four-digit-per-channel report is scaled to eight bits"
|
||||
__test_assert_equal FFFFFF (__tide_parse_osc_background \e']11;rgb:ff/ff/ff'\a) \
|
||||
"a two-digit report with a BEL terminator is read"
|
||||
__test_assert_equal FFFFFF (__tide_parse_osc_background \e']11;rgb:f/f/f'\a) \
|
||||
"channels are scaled to the width they are reported in"
|
||||
__test_assert_equal 000000 (__tide_parse_osc_background \e']11;rgb:0000/0000/0000'\e\\) \
|
||||
"black is not mistaken for a failed parse"
|
||||
# Fish keeps kitty's colour-scheme notifications on, so another report can be
|
||||
# sitting in the buffer either side of this one.
|
||||
__test_assert_equal EBE8E4 \
|
||||
(__tide_parse_osc_background \e'[?997;1n'\e']11;rgb:ebeb/e8e8/e4e4'\e\\) \
|
||||
"the report is found next to unrelated terminal output"
|
||||
|
||||
__tide_parse_osc_background 'not a reply at all'
|
||||
__test_assert_equal 1 $status "a reply without a colour is a failure, not a colour"
|
||||
__tide_parse_osc_background ''
|
||||
__test_assert_equal 1 $status "an empty reply is a failure"
|
||||
|
||||
# --- the terminal outranks the config files ------------------------------
|
||||
# This is the ssh case: the remote box is pinned to dark, so *its*
|
||||
# light-theme.auto.conf holds a dark theme, while the window on this end is
|
||||
# light. Reading files there yields no usable answer and the caller falls back
|
||||
# to white, which lands lighter than the real background instead of darker.
|
||||
set -l remote_config (mktemp -d)
|
||||
printf 'background #212121\n' >$remote_config/light-theme.auto.conf
|
||||
printf 'background #212121\n' >$remote_config/dark-theme.auto.conf
|
||||
printf 'background #212121\n' >$remote_config/no-preference-theme.auto.conf
|
||||
|
||||
KITTY_CONFIG_DIRECTORY=$remote_config __tide_terminal_background_color light
|
||||
__test_assert_equal 1 $status \
|
||||
"without the terminal, a dark-pinned remote gives the probe nothing to use"
|
||||
|
||||
function __tide_terminal_osc_background
|
||||
echo ebe8e4
|
||||
end
|
||||
__test_assert_equal EBE8E4 \
|
||||
(KITTY_CONFIG_DIRECTORY=$remote_config __tide_terminal_background_color light) \
|
||||
"the terminal's own answer wins over a remote machine's config files"
|
||||
|
||||
# A terminal that cannot be asked has to leave the config files in charge.
|
||||
function __tide_terminal_osc_background
|
||||
return 1
|
||||
end
|
||||
__test_assert_equal EBE8E4 \
|
||||
(KITTY_CONFIG_DIRECTORY=$fake_config __tide_terminal_background_color light) \
|
||||
"the config files still answer when the terminal will not"
|
||||
|
||||
# And an answer contradicting the appearance is refused just like a file's.
|
||||
function __tide_terminal_osc_background
|
||||
echo 212121
|
||||
end
|
||||
__test_assert_equal EBE8E4 \
|
||||
(KITTY_CONFIG_DIRECTORY=$fake_config __tide_terminal_background_color light) \
|
||||
"a terminal answer that contradicts the appearance is skipped"
|
||||
|
||||
functions --erase __tide_terminal_osc_background
|
||||
|
||||
rm -rf $fake_config $empty_config $remote_config
|
||||
|
||||
set -l empty_palette_result (
|
||||
fish --no-config -c '
|
||||
|
||||
@ -6,16 +6,35 @@ log_user 0
|
||||
# The segment background is derived from the theme kitty is showing, so the
|
||||
# emulated terminal responses below and the theme files have to agree. These
|
||||
# fixtures are the white/black the responses report.
|
||||
set kitty_config [file join [file dirname [file normalize [info script]]] fixtures kitty]
|
||||
set fixtures [file join [file dirname [file normalize [info script]]] fixtures]
|
||||
set kitty_config [file join $fixtures kitty]
|
||||
|
||||
# What the emulated terminal answers OSC 11 with, and the segment background
|
||||
# Tide must derive from it.
|
||||
set light_reply "ffff/ffff/ffff"
|
||||
set light_segment "F2F2F2"
|
||||
|
||||
set path_label "direct"
|
||||
set through_tmux 0
|
||||
if {[llength $argv] > 0 && [lindex $argv 0] eq "tmux"} {
|
||||
set mode [expr {[llength $argv] > 0 ? [lindex $argv 0] : "direct"}]
|
||||
|
||||
if {$mode eq "tmux"} {
|
||||
set path_label "tmux"
|
||||
set through_tmux 1
|
||||
spawn env TERM=xterm-kitty KITTY_CONFIG_DIRECTORY=$kitty_config \
|
||||
tmux -L tide-appearance-test \
|
||||
-f "$env(HOME)/.tmux.conf" new-session fish --interactive
|
||||
} elseif {$mode eq "remote"} {
|
||||
# The ssh case: the config files belong to a machine pinned to dark, so
|
||||
# every slot there describes a dark window, while the terminal this shell
|
||||
# is displayed in is light. Only asking the terminal gets this right --
|
||||
# reading the files yields nothing usable and Tide falls back to white,
|
||||
# putting the segment *above* the background instead of below it.
|
||||
set path_label "remote"
|
||||
set kitty_config [file join $fixtures kitty-remote-dark]
|
||||
set light_reply "ebeb/e8e8/e4e4"
|
||||
set light_segment "DFDCD8"
|
||||
spawn env TERM=xterm-kitty KITTY_CONFIG_DIRECTORY=$kitty_config fish --interactive
|
||||
} else {
|
||||
spawn env TERM=xterm-kitty KITTY_CONFIG_DIRECTORY=$kitty_config fish --interactive
|
||||
}
|
||||
@ -31,7 +50,7 @@ if {$through_tmux} {
|
||||
exp_continue
|
||||
}
|
||||
-exact "\033]11;?\033\\" {
|
||||
send -- "\033]11;rgb:ffff/ffff/ffff\033\\"
|
||||
send -- "\033]11;rgb:$light_reply\033\\"
|
||||
exp_continue
|
||||
}
|
||||
-exact "\033\[0c" {
|
||||
@ -49,7 +68,7 @@ if {$through_tmux} {
|
||||
} else {
|
||||
expect {
|
||||
-exact "\033]11;?\033\\" {
|
||||
send -- "\033]11;rgb:ffff/ffff/ffff\033\\"
|
||||
send -- "\033]11;rgb:$light_reply\033\\"
|
||||
exp_continue
|
||||
}
|
||||
-exact "\033\[0c" {
|
||||
@ -66,7 +85,7 @@ if {$through_tmux} {
|
||||
|
||||
send -- "printf 'CHECK:%s:%s\\n' \$__tide_terminal_appearance \$tide_pwd_bg_color\r"
|
||||
expect {
|
||||
-re {CHECK:light:F2F2F2} {}
|
||||
-re "CHECK:light:$light_segment" {}
|
||||
timeout {
|
||||
puts stderr "The initial light terminal background did not apply the light Tide palette"
|
||||
exit 1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user