mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-27 09:46:10 -04:00
Pick the colors of authors for the terminal's background
The colors that lazygit derives from the names of authors are picked for a dark background. On a light one, they are too pale to read; against white, every author has a contrast ratio between 2.2:1 and 3.5:1. Now that lazygit knows whether the terminal is dark or light, pick them from a darker range of lightness when it is light. Against white, the contrast ratio is now between 5.2:1 and 9.2:1, and against the background of Solarized Light between 4.8:1 and 8.5:1. On a dark background, nothing changes. If the terminal switches between dark and light while lazygit is running, draw the commits again in the new colors. Add gui.colorScheme for terminals that don't tell us, and for anyone who wants to override what they tell. It is 'auto' by default; 'dark' and 'light' ignore what the terminal says. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7282c21aa5
commit
f51c4aaefc
@@ -169,6 +169,13 @@ gui:
|
||||
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
shortTimeFormat: 3:04PM
|
||||
|
||||
# Whether the terminal has a dark or a light background. The colors of authors
|
||||
# are picked to stand out against it.
|
||||
# One of: 'auto' (default) | 'dark' | 'light'
|
||||
# With 'auto', lazygit asks the terminal, and assumes a dark background if the
|
||||
# terminal doesn't tell.
|
||||
colorScheme: auto
|
||||
|
||||
# Config relating to colors and styles.
|
||||
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
|
||||
theme:
|
||||
@@ -1027,6 +1034,8 @@ gui:
|
||||
|
||||
Lazygit will assign a random color for every commit author in the commits pane by default.
|
||||
|
||||
These colors are picked to be readable against the background of your terminal, and lazygit asks the terminal whether its background is dark or light. If your terminal doesn't tell, lazygit assumes a dark background; set `gui.colorScheme` to `light` if yours is light.
|
||||
|
||||
You can customize the color in case you're not happy with the randomly assigned one:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -141,6 +141,10 @@ type GuiConfig struct {
|
||||
// Format used when displaying time if the time is less than 24 hours ago.
|
||||
// Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
// Whether the terminal has a dark or a light background. The colors of authors are picked to stand out against it.
|
||||
// One of: 'auto' (default) | 'dark' | 'light'
|
||||
// With 'auto', lazygit asks the terminal, and assumes a dark background if the terminal doesn't tell.
|
||||
ColorScheme string `yaml:"colorScheme" jsonschema:"enum=auto,enum=dark,enum=light"`
|
||||
// Config relating to colors and styles.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
@@ -881,6 +885,7 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig {
|
||||
Language: "auto",
|
||||
TimeFormat: "02 Jan 06",
|
||||
ShortTimeFormat: time.Kitchen,
|
||||
ColorScheme: "auto",
|
||||
Theme: ThemeConfig{
|
||||
ActiveBorderColor: []string{"green", "bold"},
|
||||
SearchingActiveBorderColor: []string{"cyan", "bold"},
|
||||
|
||||
@@ -18,6 +18,10 @@ func (config *UserConfig) Validate() error {
|
||||
[]string{"dashboard", "allBranchesLog"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEnum("gui.colorScheme", config.Gui.ColorScheme,
|
||||
[]string{"auto", "dark", "light"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateEnum("gui.showDivergenceFromBaseBranch", config.Gui.ShowDivergenceFromBaseBranch,
|
||||
[]string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
|
||||
return err
|
||||
|
||||
@@ -945,6 +945,9 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
|
||||
gui.c.Log.Infof("Terminal color scheme: %s", g.DetectedColorScheme())
|
||||
g.SetColorSchemeChangeHandler(func(colorScheme gocui.DetectedColorScheme) error {
|
||||
gui.c.Log.Infof("Terminal color scheme changed: %s", colorScheme)
|
||||
gui.applyTerminalBackground()
|
||||
gui.c.Contexts().LocalCommits.HandleRender()
|
||||
gui.c.Contexts().SubCommits.HandleRender()
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -1259,6 +1262,27 @@ func (gui *Gui) setColorScheme() {
|
||||
gui.g.SelFgColor = theme.ActiveBorderColor
|
||||
gui.g.FrameColor = theme.InactiveBorderColor
|
||||
gui.g.SelFrameColor = theme.ActiveBorderColor
|
||||
|
||||
gui.applyTerminalBackground()
|
||||
}
|
||||
|
||||
// applyTerminalBackground tells the colors that depend on the terminal's
|
||||
// background whether it is light.
|
||||
func (gui *Gui) applyTerminalBackground() {
|
||||
authors.SetLightBackground(gui.terminalHasLightBackground())
|
||||
}
|
||||
|
||||
// terminalHasLightBackground goes by gui.colorScheme, or by what the terminal
|
||||
// tells us if that is 'auto'.
|
||||
func (gui *Gui) terminalHasLightBackground() bool {
|
||||
switch gui.UserConfig().Gui.ColorScheme {
|
||||
case "dark":
|
||||
return false
|
||||
case "light":
|
||||
return true
|
||||
default:
|
||||
return gui.g.DetectedColorScheme().ColorScheme == gocui.ColorSchemeLight
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) onUIThread(f func() error) {
|
||||
|
||||
@@ -27,6 +27,10 @@ var (
|
||||
// The styles derived from the names of the other authors
|
||||
authorStyleCache = make(map[string]*style.TextStyle)
|
||||
|
||||
// Whether the terminal has a light background, for the derived styles to
|
||||
// stand out against
|
||||
lightBackground bool
|
||||
|
||||
colorsVersion int
|
||||
)
|
||||
|
||||
@@ -114,13 +118,19 @@ func colorAtPosition(hue, saturation, lightness float64) colorful.Color {
|
||||
// HSL spreads them instead. At one and the same lightness, it gives a
|
||||
// glaring yellow and a blue that all but disappears.
|
||||
//
|
||||
// The lightness range keeps every author above a contrast ratio of 4.5:1
|
||||
// against common dark backgrounds, such as #1e1e1e.
|
||||
// There is one lightness range for a dark background and one for a light
|
||||
// background. Each keeps every author above a contrast ratio of 4.5:1
|
||||
// against common backgrounds of its kind, such as #1e1e1e and #fdf6e3.
|
||||
//
|
||||
// Saturation in HSLuv is a fraction of the most colorful a hue can get at
|
||||
// that lightness, and pale colors are hard to tell apart, so keep it near
|
||||
// the top of its range.
|
||||
return colorful.HSLuv(hue*360.0, 0.8+0.2*saturation, 0.57+0.15*lightness)
|
||||
minLightness := 0.57
|
||||
if lightBackground {
|
||||
minLightness = 0.31
|
||||
}
|
||||
|
||||
return colorful.HSLuv(hue*360.0, 0.8+0.2*saturation, minLightness+0.15*lightness)
|
||||
}
|
||||
|
||||
// ColorPosition says where an author's color lies within the range of hues,
|
||||
@@ -166,6 +176,18 @@ func SetCustomAuthors(customAuthorColors map[string]string) {
|
||||
colorsChanged()
|
||||
}
|
||||
|
||||
// SetLightBackground says whether the terminal has a light background, for the
|
||||
// colors of authors to stand out against.
|
||||
func SetLightBackground(light bool) {
|
||||
if light == lightBackground {
|
||||
return
|
||||
}
|
||||
|
||||
lightBackground = light
|
||||
authorStyleCache = make(map[string]*style.TextStyle)
|
||||
colorsChanged()
|
||||
}
|
||||
|
||||
// colorsChanged drops what was rendered with the previous colors of authors.
|
||||
func colorsChanged() {
|
||||
authorInitialCache = make(map[string]string)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
@@ -58,3 +59,65 @@ func TestAuthorColorsFollowTheConfig(t *testing.T) {
|
||||
assert.Equal(t, style.FgBlue.Sprint("JD"), ShortAuthor("Jane Doe"))
|
||||
assert.Equal(t, style.FgBlue.Sprint("Jane Doe"), LongAuthor("Jane Doe", 8))
|
||||
}
|
||||
|
||||
func TestSetLightBackground(t *testing.T) {
|
||||
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelMillions)
|
||||
defer color.ForceSetColorLevel(oldColorLevel)
|
||||
t.Cleanup(func() {
|
||||
SetLightBackground(false)
|
||||
SetCustomAuthors(nil)
|
||||
})
|
||||
|
||||
SetCustomAuthors(map[string]string{"Jane Doe": "red"})
|
||||
onDarkBackground := ShortAuthor("John Smith")
|
||||
|
||||
SetLightBackground(true)
|
||||
assert.NotEqual(t, onDarkBackground, ShortAuthor("John Smith"))
|
||||
assert.Equal(t, style.FgRed.Sprint("JD"), ShortAuthor("Jane Doe"))
|
||||
}
|
||||
|
||||
func TestAuthorColorsStandOutAgainstTheBackground(t *testing.T) {
|
||||
t.Cleanup(func() { SetLightBackground(false) })
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
lightBackground bool
|
||||
backgrounds []string
|
||||
}{
|
||||
{name: "dark", lightBackground: false, backgrounds: []string{"#000000", "#1e1e1e"}},
|
||||
{name: "light", lightBackground: true, backgrounds: []string{"#ffffff", "#fdf6e3"}},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
SetLightBackground(s.lightBackground)
|
||||
|
||||
for _, backgroundHex := range s.backgrounds {
|
||||
background, err := colorful.Hex(backgroundHex)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// The edges of the range, which is where the contrast is lowest
|
||||
for hue := range 100 {
|
||||
for _, saturation := range []float64{0, 0.99} {
|
||||
for _, lightness := range []float64{0, 0.99} {
|
||||
c := colorAtPosition(float64(hue)/100, saturation, lightness)
|
||||
assert.GreaterOrEqual(t, contrastRatio(c, background), 4.5,
|
||||
"%s on %s", c.Hex(), backgroundHex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// contrastRatio is as defined by the Web Content Accessibility Guidelines
|
||||
func contrastRatio(a colorful.Color, b colorful.Color) float64 {
|
||||
luminance := func(c colorful.Color) float64 {
|
||||
r, g, b := c.LinearRgb()
|
||||
return 0.2126*r + 0.7152*g + 0.0722*b
|
||||
}
|
||||
lighter := max(luminance(a), luminance(b))
|
||||
darker := min(luminance(a), luminance(b))
|
||||
return (lighter + 0.05) / (darker + 0.05)
|
||||
}
|
||||
|
||||
@@ -719,6 +719,16 @@
|
||||
"description": "Format used when displaying time if the time is less than 24 hours ago.\nUses Go's time format syntax: https://pkg.go.dev/time#Time.Format",
|
||||
"default": "3:04PM"
|
||||
},
|
||||
"colorScheme": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"auto",
|
||||
"dark",
|
||||
"light"
|
||||
],
|
||||
"description": "Whether the terminal has a dark or a light background. The colors of authors are picked to stand out against it.\nOne of: 'auto' (default) | 'dark' | 'light'\nWith 'auto', lazygit asks the terminal, and assumes a dark background if the terminal doesn't tell.",
|
||||
"default": "auto"
|
||||
},
|
||||
"theme": {
|
||||
"$ref": "#/$defs/ThemeConfig",
|
||||
"description": "Config relating to colors and styles.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes"
|
||||
|
||||
Reference in New Issue
Block a user