diff --git a/docs/changelog.rst b/docs/changelog.rst index c4759b0150..ed6942eb73 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -89,6 +89,8 @@ Detailed list of changes - Splits layout: Allow setting the bias of the current split using ``layout_action bias`` (:iss:`8222`) +- hints kitten: Workaround for some broken light color themes that make the hints text color too low contrast to read (:iss:`7330`) + 0.39.0 [2025-01-16] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kittens/hints/main.go b/kittens/hints/main.go index c8c408c491..b3512fa592 100644 --- a/kittens/hints/main.go +++ b/kittens/hints/main.go @@ -111,7 +111,28 @@ func decode_hint(x string, alphabet string) (ans int) { return } +func as_rgb(c uint32) [3]float32 { + return [3]float32{float32((c>>16)&255) / 255.0, float32((c>>8)&255) / 255.0, float32(c&255) / 255.0} +} + +func hints_text_color(confval string) (ans string) { + ans = confval + if ans == "auto" { + ans = "bright-gray" + if bc, err := tui.ReadBasicColors(); err == nil { + bg := as_rgb(bc.Background) + c15 := as_rgb(bc.Color15) + c8 := as_rgb(bc.Color8) + if utils.RGBContrast(bg[0], bg[1], bg[2], c8[0], c8[1], c8[2]) > utils.RGBContrast(bg[0], bg[1], bg[2], c15[0], c15[1], c15[2]) { + ans = "bright-black" + } + } + } + return +} + func main(_ *cli.Command, o *Options, args []string) (rc int, err error) { + o.HintsTextColor = hints_text_color(o.HintsTextColor) output := tui.KittenOutputSerializer() if tty.IsTerminal(os.Stdin.Fd()) { return 1, fmt.Errorf("You must pass the text to be hinted on STDIN") diff --git a/kittens/hints/main.py b/kittens/hints/main.py index 62b77231db..53aee84790 100644 --- a/kittens/hints/main.py +++ b/kittens/hints/main.py @@ -229,11 +229,11 @@ def custom_marking() -> None: --hints-text-color -default=bright-gray +default=auto type=str The foreground color for text pointed to by the hints. You can use color names or hex values. For the eight basic named terminal colors you can also use the :code:`bright-` prefix to get the bright variant of the -color. +color. The default is to pick a suitable color automatically. --customize-processing diff --git a/tools/tui/ui_kitten.go b/tools/tui/ui_kitten.go index eead85bb4f..b09aa028a5 100644 --- a/tools/tui/ui_kitten.go +++ b/tools/tui/ui_kitten.go @@ -20,6 +20,37 @@ var RunningAsUI = sync.OnceValue(func() bool { return os.Getenv("KITTEN_RUNNING_AS_UI") != "" }) +type BasicColors struct { + Foreground uint32 `json:"foreground"` + Background uint32 `json:"background"` + Color0 uint32 `json:"color0"` + Color1 uint32 `json:"color1"` + Color2 uint32 `json:"color2"` + Color3 uint32 `json:"color3"` + Color4 uint32 `json:"color4"` + Color5 uint32 `json:"color5"` + Color6 uint32 `json:"color6"` + Color7 uint32 `json:"color7"` + Color8 uint32 `json:"color8"` + Color9 uint32 `json:"color9"` + Color10 uint32 `json:"color10"` + Color11 uint32 `json:"color11"` + Color12 uint32 `json:"color12"` + Color13 uint32 `json:"color13"` + Color14 uint32 `json:"color14"` + Color15 uint32 `json:"color15"` +} + +func ReadBasicColors() (ans BasicColors, err error) { + q := os.Getenv("KITTY_BASIC_COLORS") + if q == "" { + err = fmt.Errorf("No KITTY_BASIC_COLORS env var") + } else { + err = json.Unmarshal(utils.UnsafeStringToBytes(q), &ans) + } + return +} + func PrepareRootCmd(root *cli.Command) { if RunningAsUI() { root.CallbackOnError = func(cmd *cli.Command, err error, during_parsing bool, exit_code int) int { diff --git a/tools/utils/colors.go b/tools/utils/colors.go new file mode 100644 index 0000000000..35da175529 --- /dev/null +++ b/tools/utils/colors.go @@ -0,0 +1,21 @@ +package utils + +import ( + "fmt" +) + +var _ = fmt.Print + +func RGBLuminance(r, g, b float32) float32 { + // From ITU BT 601 https://www.itu.int/rec/R-REC-BT.601 + return 0.299*r + 0.587*g + 0.114*b +} + +func RGBContrast(r1, g1, b1, r2, g2, b2 float32) float32 { + al := RGBLuminance(r1, g1, b1) + bl := RGBLuminance(r2, g2, b2) + if al < bl { + al, bl = bl, al + } + return (al + 0.05) / (bl + 0.05) +}