tide: derive the light segment background from the terminal's own
A fixed E4E4E4 segment was 1.04:1 against Kaolin Breeze's EBE8E4 -- an invisible pill -- and a neutral grey reads as a foreign object on a warm or cool background anyway. Read the background kitty is showing and place the segment a fixed contrast step below it, so it keeps the theme's tint. Default step is 1.12:1; __tide_terminal_segment_contrast overrides it. Moving the segment invalidates the foregrounds, which are authored at exactly 4.5:1 against E4E4E4 with no headroom, so re-aim them at the derived segment by the single ratio the palette's shared luminance ceiling moved by. That scaling runs in linear light, where luminance is a weighted sum of the channels and so scales exactly; scaling the encoded channels drifts further the darker a color gets, which is where the floor binds. Also fix the baseline capture: the overrides are exported, so a Fish started from a Fish that had already applied a palette was capturing that palette as its dark baseline and could never restore dark. Drop inherited globals first so the baseline comes from the universal scope Tide writes. Dark mode is unchanged.
This commit is contained in:
parent
8c9e5e2c99
commit
e5207ac154
@ -1,8 +1,61 @@
|
||||
function __tide_apply_terminal_appearance --argument-names appearance
|
||||
function __tide_apply_terminal_appearance --argument-names appearance background
|
||||
contains -- $appearance dark light
|
||||
or return 2
|
||||
|
||||
# The palette Tide ships is authored for a dark terminal, where every
|
||||
# segment shares this background.
|
||||
set -l dark_segment_background 303030
|
||||
# The background __tide_terminal_light_color's foregrounds are tuned against,
|
||||
# and the ratio they are tuned to.
|
||||
set -l reference_background E4E4E4
|
||||
set -l reference_contrast 4.5
|
||||
# How far the segment background sits from the terminal background. Set
|
||||
# $__tide_terminal_segment_contrast to taste: 1.04:1 is what a fixed E4E4E4
|
||||
# gave on a warm background and is invisible, 1.4:1 reads heavy.
|
||||
set -l segment_contrast 1.12
|
||||
if string match -qr '^[0-9]+(\.[0-9]+)?$' -- "$__tide_terminal_segment_contrast"
|
||||
and test (math -s0 "round($__tide_terminal_segment_contrast * 100)") -gt 100
|
||||
set segment_contrast $__tide_terminal_segment_contrast
|
||||
end
|
||||
# What the foregrounds are re-aimed at. The extra margin over
|
||||
# $reference_contrast absorbs the rounding back to 8 bits per channel.
|
||||
set -l foreground_contrast 4.55
|
||||
# Used when the terminal will not say what its background is. White is the
|
||||
# common default for light terminals.
|
||||
set -l fallback_background FFFFFF
|
||||
|
||||
set -l segment_background
|
||||
set -l foreground_factor 1
|
||||
|
||||
if test "$appearance" = light
|
||||
test -z "$background"
|
||||
and set background (__tide_terminal_background_color light)
|
||||
string match -qr '^[0-9A-Fa-f]{6}$' -- "$background"
|
||||
or set background $fallback_background
|
||||
set background (string upper -- $background)
|
||||
|
||||
# Place the segment a fixed contrast step below the terminal background,
|
||||
# so it picks up that background's tint instead of imposing a grey.
|
||||
set -l background_luminance (math "max(0.000001, "(__tide_relative_luminance $background)")")
|
||||
set -l segment_luminance (__tide_contrast_ceiling $background_luminance $segment_contrast)
|
||||
set segment_background (__tide_scale_luminance $background \
|
||||
(math "$segment_luminance / $background_luminance"))
|
||||
|
||||
# Every light foreground is tuned to sit at $reference_contrast against
|
||||
# the reference background, so the palette shares one luminance ceiling.
|
||||
# A darker segment lowers that ceiling, and moving every foreground by
|
||||
# the same luminance factor walks them all down to it at once.
|
||||
set -l reference_ceiling (__tide_contrast_ceiling \
|
||||
(__tide_relative_luminance $reference_background) $reference_contrast)
|
||||
set -l segment_ceiling (__tide_contrast_ceiling \
|
||||
(__tide_relative_luminance $segment_background) $foreground_contrast)
|
||||
set foreground_factor (math "min(1, $segment_ceiling / $reference_ceiling)")
|
||||
else
|
||||
set background ''
|
||||
end
|
||||
|
||||
if test "$__tide_terminal_appearance" = "$appearance"
|
||||
and test "$__tide_terminal_background" = "$background"
|
||||
return 1
|
||||
end
|
||||
|
||||
@ -14,6 +67,14 @@ function __tide_apply_terminal_appearance --argument-names appearance
|
||||
string match -q 'tide_*color*' -- $name
|
||||
or continue
|
||||
|
||||
# These overrides are exported, so a Fish started from a Fish that
|
||||
# already applied a palette inherits them and would otherwise
|
||||
# capture that palette as its baseline. Dropping the inherited
|
||||
# global makes the name resolve to its universal value again, which
|
||||
# is the configured dark palette. Every name here comes from the
|
||||
# universal scope, so there is always something left to resolve to.
|
||||
set --erase --global $name
|
||||
|
||||
set -l current_value $$name
|
||||
test (count $current_value) -eq 1
|
||||
or continue
|
||||
@ -31,11 +92,25 @@ function __tide_apply_terminal_appearance --argument-names appearance
|
||||
for index in (seq (count $__tide_terminal_color_names))
|
||||
set -l name $__tide_terminal_color_names[$index]
|
||||
set -l color $__tide_terminal_dark_colors[$index]
|
||||
|
||||
if test "$appearance" = light
|
||||
set color (__tide_terminal_light_color $color)
|
||||
if string match -q '*bg_color*' -- $name
|
||||
and test (string upper -- $color) = $dark_segment_background
|
||||
set color $segment_background
|
||||
else
|
||||
set -l light_color (__tide_terminal_light_color $color)
|
||||
set -l dimmed (__tide_scale_luminance $light_color $foreground_factor)
|
||||
if test -n "$dimmed"
|
||||
set color $dimmed
|
||||
else
|
||||
set color $light_color
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set --global --export $name $color
|
||||
end
|
||||
|
||||
set --global __tide_terminal_appearance $appearance
|
||||
set --global __tide_terminal_background $background
|
||||
end
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
function __tide_contrast_ceiling --argument-names luminance ratio \
|
||||
--description 'Print the highest luminance that still meets a contrast ratio against a background'
|
||||
|
||||
string match -qr '^[0-9]+(\.[0-9]+)?$' -- $luminance
|
||||
or return 1
|
||||
string match -qr '^[0-9]+(\.[0-9]+)?$' -- $ratio
|
||||
or return 1
|
||||
|
||||
# WCAG contrast is (lighter + 0.05) / (darker + 0.05), so the darkest
|
||||
# partner a background can carry at a given ratio falls straight out of it.
|
||||
math "max(0, ($luminance + 0.05) / $ratio - 0.05)"
|
||||
end
|
||||
@ -0,0 +1,15 @@
|
||||
function __tide_relative_luminance --argument-names hex \
|
||||
--description 'Print the WCAG relative luminance of a six-digit hex color'
|
||||
|
||||
string match -qr '^[0-9A-Fa-f]{6}$' -- $hex
|
||||
or return 1
|
||||
|
||||
set -l r (string sub --start 1 --length 2 -- $hex)
|
||||
set -l g (string sub --start 3 --length 2 -- $hex)
|
||||
set -l b (string sub --start 5 --length 2 -- $hex)
|
||||
|
||||
# sRGB relative luminance. WCAG's piecewise-linear segment for channels
|
||||
# below 0.04045 is left out: it only applies under 10/255 and shifts the
|
||||
# result by less than 0.001, which never changes a palette decision here.
|
||||
math "0.2126 * ((0x$r / 255 + 0.055) / 1.055) ^ 2.4 + 0.7152 * ((0x$g / 255 + 0.055) / 1.055) ^ 2.4 + 0.0722 * ((0x$b / 255 + 0.055) / 1.055) ^ 2.4"
|
||||
end
|
||||
@ -0,0 +1,26 @@
|
||||
function __tide_scale_luminance --argument-names hex factor \
|
||||
--description 'Multiply a color\'s luminance by a factor, keeping its hue and saturation'
|
||||
|
||||
string match -qr '^[0-9A-Fa-f]{6}$' -- $hex
|
||||
or return 1
|
||||
string match -qr '^[0-9]+(\.[0-9]+)?$' -- $factor
|
||||
or return 1
|
||||
|
||||
set -l channels
|
||||
|
||||
for start in 1 3 5
|
||||
set -l encoded (string sub --start $start --length 2 -- $hex)
|
||||
# Decode to linear light, scale, re-encode. Scaling there multiplies
|
||||
# relative luminance by exactly this factor, because luminance is a
|
||||
# weighted sum of the linear channels. Scaling the encoded channels
|
||||
# instead would not: sRGB's +0.055 offset makes the error grow as the
|
||||
# color gets darker, which is where the contrast floor actually bites.
|
||||
set -l linear "($factor * ((0x$encoded / 255 + 0.055) / 1.055) ^ 2.4)"
|
||||
set --append channels "round(255 * max(0, min(1, (1.055 * $linear ^ (1 / 2.4)) - 0.055)))"
|
||||
end
|
||||
|
||||
# One math call per color rather than three: the bytes are packed into a
|
||||
# single number and printed back out as hex. -s0 truncates, so each
|
||||
# channel does its own rounding above.
|
||||
printf %06X (math -s0 "$channels[1] * 65536 + $channels[2] * 256 + $channels[3]")
|
||||
end
|
||||
@ -0,0 +1,46 @@
|
||||
function __tide_terminal_background_color --argument-names appearance \
|
||||
--description 'Print the background color kitty is showing for an appearance'
|
||||
|
||||
contains -- $appearance dark light
|
||||
or return 1
|
||||
|
||||
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.
|
||||
for file in $config_dir/$appearance-theme.auto.conf $config_dir/no-preference-theme.auto.conf
|
||||
test -r $file
|
||||
or continue
|
||||
|
||||
set -l hex (string match -gr '^\s*background\s+#?([0-9A-Fa-f]{6})\s*$' <$file | tail -n 1)
|
||||
test -n "$hex"
|
||||
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
|
||||
or continue
|
||||
else
|
||||
test $scaled -lt 500
|
||||
or continue
|
||||
end
|
||||
|
||||
echo $hex
|
||||
return 0
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
@ -0,0 +1,102 @@
|
||||
# Tide Terminal Appearance Design
|
||||
|
||||
## Goal
|
||||
|
||||
Keep the existing Tide prompt unchanged except for its colors. The prompt must
|
||||
follow the active Kitty or Ghostty terminal appearance, update while the shell
|
||||
is open, and continue to work inside tmux.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Do not modify Tide's installed functions.
|
||||
- Do not change prompt items, icons, separators, shape, spacing, or layout.
|
||||
- Do not infer appearance from macOS.
|
||||
- Do not share one terminal's appearance with unrelated Fish sessions.
|
||||
|
||||
## Design
|
||||
|
||||
Add one Fish integration file under `conf.d/`.
|
||||
|
||||
Fish 4.8 already negotiates terminal color-scheme notifications with Kitty,
|
||||
Ghostty, and tmux, consumes the terminal replies, queries the actual background,
|
||||
and publishes the result as the read-only `fish_terminal_color_theme` variable.
|
||||
The integration subscribes to changes of that variable instead of parsing raw
|
||||
terminal input itself.
|
||||
|
||||
The handler changes only Tide color variables:
|
||||
|
||||
- Dark mode keeps the currently configured values exactly.
|
||||
- Light mode substitutes darker, contrast-safe equivalents for foreground
|
||||
colors while preserving each color's semantic role, and replaces the shared
|
||||
segment background `303030` with a shade of the terminal's own background.
|
||||
|
||||
The light segment background is derived rather than fixed. A single neutral
|
||||
value cannot sit at a usable distance from every light theme -- `E4E4E4` is
|
||||
1.03:1 against Kaolin Breeze's `EBE8E4`, which is invisible -- and a neutral
|
||||
grey reads as a foreign object on a warm or cool background. So the handler
|
||||
reads the background kitty is showing and places the segment a fixed contrast
|
||||
step below it, which keeps the segment in the theme's own tint.
|
||||
|
||||
That step is a judgement call rather than a threshold, and it is not only about
|
||||
the segment: the foregrounds are re-aimed at whatever the segment becomes, so a
|
||||
heavier step darkens the text along with it and the segment starts reading as a
|
||||
blob. `1.12:1` is the default. `__tide_terminal_segment_contrast` overrides it,
|
||||
and values that are not a ratio above 1:1 are ignored, since they would put the
|
||||
segment level with or above its own background.
|
||||
|
||||
kitty's background comes from the `*-theme.auto.conf` files. kitty gives those
|
||||
priority over includes and over `--override`, so they are what a window is
|
||||
actually showing however the theme got there. A background whose luminance
|
||||
contradicts the appearance Fish reported means the wrong file was read, and is
|
||||
discarded in favour of the fallback. Terminals that are not kitty get the same
|
||||
treatment against an assumed white background.
|
||||
|
||||
Moving the segment invalidates the foreground palette, because every light
|
||||
foreground is authored to sit at exactly 4.5:1 against the reference background
|
||||
`E4E4E4`, leaving no headroom. Since the whole palette shares one luminance
|
||||
ceiling, a darker segment lowers that ceiling by a single ratio, and every
|
||||
foreground is multiplied by it. That scaling happens in linear light, where
|
||||
relative luminance is a weighted sum of the channels and therefore scales
|
||||
exactly; scaling the encoded channels instead would drift by more as colors get
|
||||
darker, which is where the contrast floor actually binds.
|
||||
|
||||
The overrides are exported, so a Fish started from a Fish that already applied a
|
||||
palette inherits them. The capture step drops those inherited globals first, so
|
||||
each process reads its baseline from the universal scope Tide configures rather
|
||||
than from its parent's active palette.
|
||||
|
||||
Overrides use exported global scope. This shadows Tide's universal defaults for
|
||||
the current Fish process and passes the selected palette to Tide's background
|
||||
rendering subprocess without affecting Fish sessions in other terminal windows.
|
||||
|
||||
After a scheme event, the handler refreshes Tide's cached colors, reloads the
|
||||
generated prompt functions, and requests a command-line repaint. Reapplying the
|
||||
current scheme is a no-op.
|
||||
|
||||
## Failure behavior
|
||||
|
||||
On terminals that do not report a usable appearance,
|
||||
`fish_terminal_color_theme` remains `unknown` and Tide keeps its existing dark
|
||||
configuration. Fish owns the terminal protocol bytes, so they never become user
|
||||
key bindings or command-line input.
|
||||
|
||||
## Verification
|
||||
|
||||
Automated Fish tests verify:
|
||||
|
||||
- only Tide color variables change;
|
||||
- non-color Tide configuration remains identical;
|
||||
- dark values equal the current configuration;
|
||||
- light foregrounds meet the chosen contrast threshold against the derived
|
||||
segment background, on both a theme background and the fallback;
|
||||
- scaling a color's luminance moves it by the requested factor, whatever its hue;
|
||||
- the background probe reads the active theme and rejects one that contradicts
|
||||
the reported appearance;
|
||||
- exported global overrides reach a child Fish process;
|
||||
- repeated events are idempotent, and a changed background is not a repeat;
|
||||
- the integration subscribes to Fish's native terminal-theme variable;
|
||||
- Fish parses every changed file.
|
||||
|
||||
Pseudo-terminal acceptance tests emulate light/dark terminal responses and
|
||||
cover both direct Fish and Fish through tmux. Kitty and Ghostty share the
|
||||
protocol that Fish consumes.
|
||||
@ -22,7 +22,13 @@ const luminance = (hex) => {
|
||||
);
|
||||
};
|
||||
|
||||
const backgroundLuminance = luminance('E4E4E4');
|
||||
const background = (process.argv[2] ?? 'E4E4E4').toUpperCase();
|
||||
if (!/^[0-9A-F]{6}$/.test(background)) {
|
||||
console.error(`background ${background}: expected a six-digit color`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const backgroundLuminance = luminance(background);
|
||||
const failures = [];
|
||||
|
||||
for (const line of input.split('\n')) {
|
||||
@ -40,7 +46,9 @@ for (const line of input.split('\n')) {
|
||||
);
|
||||
|
||||
if (ratio < 4.5) {
|
||||
failures.push(`${dark} -> ${light}: contrast ${ratio.toFixed(2)} is below 4.5`);
|
||||
failures.push(
|
||||
`${dark} -> ${light}: contrast ${ratio.toFixed(2)} against ${background} is below 4.5`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
# vim:ft=kitty
|
||||
background #000000
|
||||
foreground #FFFFFF
|
||||
@ -0,0 +1,3 @@
|
||||
# vim:ft=kitty
|
||||
background #FFFFFF
|
||||
foreground #000000
|
||||
@ -33,17 +33,34 @@ end
|
||||
|
||||
set -l light_mapper $project_root/functions/__tide_terminal_light_color.fish
|
||||
set -l appearance_applicator $project_root/functions/__tide_apply_terminal_appearance.fish
|
||||
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
|
||||
|
||||
test -f $light_mapper
|
||||
or __test_fail "missing light palette mapper"
|
||||
test -f $appearance_applicator
|
||||
or __test_fail "missing terminal appearance applicator"
|
||||
for file in $light_mapper $appearance_applicator $luminance_helper $scale_helper \
|
||||
$ceiling_helper $background_probe
|
||||
test -f $file
|
||||
or __test_fail "missing "(path basename $file)
|
||||
end
|
||||
|
||||
source $light_mapper
|
||||
source $luminance_helper
|
||||
source $scale_helper
|
||||
source $ceiling_helper
|
||||
source $background_probe
|
||||
source $appearance_applicator
|
||||
|
||||
# The applicator exports its overrides, so a shell that already applied a
|
||||
# palette passes them to this one through the environment. Dropping the
|
||||
# inherited globals makes every tide_* name resolve to its universal value
|
||||
# again, which is the configured dark palette this suite is written against.
|
||||
for name in (set --universal --names | string match -e -r '^tide_')
|
||||
set --erase --global $name
|
||||
end
|
||||
|
||||
__test_assert_equal E4E4E4 (__tide_terminal_light_color 303030) \
|
||||
"dark segment background maps to the light segment background"
|
||||
"dark segment background maps to the reference light segment background"
|
||||
__test_assert_equal 006C9E (__tide_terminal_light_color 00AFFF) \
|
||||
"bright blue maps to its contrast-safe light equivalent"
|
||||
__test_assert_equal CF0000 (__tide_terminal_light_color FF0000) \
|
||||
@ -51,13 +68,61 @@ __test_assert_equal CF0000 (__tide_terminal_light_color FF0000) \
|
||||
__test_assert_equal glyph (__tide_terminal_light_color glyph) \
|
||||
"non-color values are preserved"
|
||||
|
||||
set -l reference_luminance (__tide_relative_luminance E4E4E4)
|
||||
test (math -s0 "round($reference_luminance * 1000)") -eq 776
|
||||
or __test_fail "relative luminance of E4E4E4: expected 0.776, got $reference_luminance"
|
||||
|
||||
# Halving a color's luminance has to actually halve it, whatever its hue.
|
||||
# Only the rounding back to 8 bits per channel is allowed to show up.
|
||||
for color in EBE8E4 006C9E 7B6400 4C6C6C
|
||||
set -l halved (__tide_scale_luminance $color 0.5)
|
||||
set -l ratio (math (__tide_relative_luminance $halved)" / "(__tide_relative_luminance $color))
|
||||
set -l scaled (math -s0 "round($ratio * 1000)")
|
||||
test $scaled -ge 490 -a $scaled -le 510
|
||||
or __test_fail "scaling $color by 0.5 moved luminance by $ratio, not 0.5"
|
||||
end
|
||||
|
||||
__test_assert_equal FFFFFF (__tide_scale_luminance 808080 100) \
|
||||
"scaling clamps channels at the top of the range"
|
||||
__test_assert_equal 000000 (__tide_scale_luminance 808080 0) \
|
||||
"scaling clamps channels at the bottom of the range"
|
||||
__tide_scale_luminance nothex 0.5
|
||||
__test_assert_equal 1 $status "scaling rejects values that are not colors"
|
||||
|
||||
__test_assert_equal 0.116667 (__tide_contrast_ceiling 0.7 4.5) \
|
||||
"the contrast ceiling is the darkest partner a background can carry"
|
||||
__test_assert_equal 0 (__tide_contrast_ceiling 0.1 4.5) \
|
||||
"an unreachable contrast ceiling clamps at zero"
|
||||
|
||||
set -l fake_config (mktemp -d)
|
||||
printf 'background #EBE8E4\n' >$fake_config/light-theme.auto.conf
|
||||
# A light background under a dark appearance means the wrong file was read, so
|
||||
# the probe has to skip it and keep looking.
|
||||
printf 'background #FFFFFF\n' >$fake_config/dark-theme.auto.conf
|
||||
printf 'background #101010\n' >$fake_config/no-preference-theme.auto.conf
|
||||
|
||||
__test_assert_equal EBE8E4 \
|
||||
(KITTY_CONFIG_DIRECTORY=$fake_config __tide_terminal_background_color light) \
|
||||
"the probe reads the background kitty is showing"
|
||||
__test_assert_equal 101010 \
|
||||
(KITTY_CONFIG_DIRECTORY=$fake_config __tide_terminal_background_color dark) \
|
||||
"the probe rejects a background that contradicts the appearance"
|
||||
|
||||
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
|
||||
|
||||
set -l empty_palette_result (
|
||||
fish --no-config -c '
|
||||
source $argv[1]
|
||||
source $argv[2]
|
||||
__tide_apply_terminal_appearance light
|
||||
for file in $argv[2..]
|
||||
source $file
|
||||
end
|
||||
__tide_apply_terminal_appearance light $argv[1]
|
||||
printf "status=%s" $status
|
||||
' $light_mapper $appearance_applicator 2>&1 |
|
||||
' EBE8E4 $light_mapper $luminance_helper $scale_helper $ceiling_helper \
|
||||
$background_probe $appearance_applicator 2>&1 |
|
||||
string collect
|
||||
)
|
||||
__test_assert_equal status=2 $empty_palette_result \
|
||||
@ -77,19 +142,19 @@ or __test_fail "the test fixture has no Tide colors"
|
||||
|
||||
set -l before_non_colors (string join \x1e (__test_snapshot no))
|
||||
|
||||
__tide_apply_terminal_appearance light
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
__test_assert_equal 0 $status "light appearance applies successfully"
|
||||
__test_assert_equal E4E4E4 $tide_pwd_bg_color \
|
||||
"light appearance replaces the existing prompt segment background"
|
||||
__test_assert_equal 006C9E $tide_pwd_color_anchors \
|
||||
"light appearance replaces the existing prompt foreground"
|
||||
__test_assert_equal DFDCD8 $tide_pwd_bg_color \
|
||||
"light appearance derives the segment background from the terminal background"
|
||||
__test_assert_equal 006696 $tide_pwd_color_anchors \
|
||||
"light appearance shades foregrounds down to the derived segment background"
|
||||
|
||||
set -l pwd_scope (set --show tide_pwd_bg_color | string collect)
|
||||
string match -q '*set in global scope, exported*' -- $pwd_scope
|
||||
or __test_fail "Tide overrides are not global and exported"
|
||||
|
||||
set -l child_background (fish --no-config -c 'printf %s "$tide_pwd_bg_color"')
|
||||
__test_assert_equal E4E4E4 $child_background \
|
||||
__test_assert_equal DFDCD8 $child_background \
|
||||
"Tide renderer subprocess inherits the light background"
|
||||
|
||||
set -l after_non_colors (string join \x1e (__test_snapshot no))
|
||||
@ -101,7 +166,7 @@ for name in $expected_color_names
|
||||
set --append first_light_state "$name="(string escape -- $$name)
|
||||
end
|
||||
|
||||
__tide_apply_terminal_appearance light
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
__test_assert_equal 1 $status "repeated light appearance is a no-op"
|
||||
|
||||
set -l second_light_state
|
||||
@ -112,14 +177,60 @@ __test_assert_equal (string join \x1e $first_light_state) \
|
||||
(string join \x1e $second_light_state) \
|
||||
"repeated light appearance preserves all color overrides"
|
||||
|
||||
begin
|
||||
for index in (seq (count $expected_dark_colors))
|
||||
set -l dark (string upper -- $expected_dark_colors[$index])
|
||||
test "$dark" = 303030; and continue
|
||||
printf '%s %s\n' $dark (__tide_terminal_light_color $dark)
|
||||
end
|
||||
end | node $project_root/tests/assert_tide_light_contrast.mjs
|
||||
or __test_fail "light palette foreground contrast is below 4.5:1"
|
||||
set --global __test_color_names $expected_color_names
|
||||
set --global __test_dark_colors $expected_dark_colors
|
||||
set --global __test_contrast_checker $project_root/tests/assert_tide_light_contrast.mjs
|
||||
|
||||
function __test_assert_light_contrast --argument-names label
|
||||
begin
|
||||
for index in (seq (count $__test_color_names))
|
||||
set -l name $__test_color_names[$index]
|
||||
set -l dark (string upper -- $__test_dark_colors[$index])
|
||||
test "$dark" = 303030; and continue
|
||||
printf '%s %s\n' $dark (string upper -- $$name)
|
||||
end
|
||||
end | node $__test_contrast_checker $tide_pwd_bg_color
|
||||
or __test_fail "light palette foreground contrast is below 4.5:1 on $label"
|
||||
end
|
||||
|
||||
__test_assert_light_contrast "a terminal-derived segment background"
|
||||
|
||||
# A different terminal background is a different palette, not a repeat.
|
||||
__tide_apply_terminal_appearance light nothex
|
||||
__test_assert_equal 0 $status "a changed terminal background re-applies the palette"
|
||||
__test_assert_equal F2F2F2 $tide_pwd_bg_color \
|
||||
"an unusable terminal background falls back to white"
|
||||
__test_assert_light_contrast "the fallback segment background"
|
||||
|
||||
__tide_apply_terminal_appearance light FFFFFF
|
||||
__test_assert_equal 1 $status \
|
||||
"a background that resolves to the active one is still a no-op"
|
||||
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
__test_assert_equal 0 $status "returning to the earlier background re-applies"
|
||||
__test_assert_equal DFDCD8 $tide_pwd_bg_color \
|
||||
"returning to the earlier background restores its segment background"
|
||||
|
||||
set --global __tide_terminal_segment_contrast 1.4
|
||||
set --global __tide_terminal_appearance ''
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
__test_assert_equal C8C6C2 $tide_pwd_bg_color \
|
||||
"a configured segment contrast overrides the default step"
|
||||
__test_assert_light_contrast "a configured segment contrast"
|
||||
|
||||
# Anything that is not a ratio above 1:1 would make the segment vanish into the
|
||||
# background or sit above it, so it is ignored rather than honoured.
|
||||
for invalid in 0.8 1 nonsense ''
|
||||
set --global __tide_terminal_segment_contrast $invalid
|
||||
set --global __tide_terminal_appearance ''
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
__test_assert_equal DFDCD8 $tide_pwd_bg_color \
|
||||
"a segment contrast of <$invalid> falls back to the default step"
|
||||
end
|
||||
set --erase __tide_terminal_segment_contrast
|
||||
|
||||
set --global __tide_terminal_appearance ''
|
||||
__tide_apply_terminal_appearance light EBE8E4
|
||||
|
||||
__tide_apply_terminal_appearance dark
|
||||
__test_assert_equal 0 $status "dark appearance applies successfully"
|
||||
|
||||
@ -3,15 +3,21 @@
|
||||
set timeout 30
|
||||
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 path_label "direct"
|
||||
set through_tmux 0
|
||||
if {[llength $argv] > 0 && [lindex $argv 0] eq "tmux"} {
|
||||
set path_label "tmux"
|
||||
set through_tmux 1
|
||||
spawn env TERM=xterm-kitty tmux -L tide-appearance-test \
|
||||
spawn env TERM=xterm-kitty KITTY_CONFIG_DIRECTORY=$kitty_config \
|
||||
tmux -L tide-appearance-test \
|
||||
-f "$env(HOME)/.tmux.conf" new-session fish --interactive
|
||||
} else {
|
||||
spawn env TERM=xterm-kitty fish --interactive
|
||||
spawn env TERM=xterm-kitty KITTY_CONFIG_DIRECTORY=$kitty_config fish --interactive
|
||||
}
|
||||
|
||||
if {$through_tmux} {
|
||||
@ -60,7 +66,7 @@ if {$through_tmux} {
|
||||
|
||||
send -- "printf 'CHECK:%s:%s\\n' \$__tide_terminal_appearance \$tide_pwd_bg_color\r"
|
||||
expect {
|
||||
-re {CHECK:light:E4E4E4} {}
|
||||
-re {CHECK:light:F2F2F2} {}
|
||||
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