diff --git a/docs/config.md b/docs/config.md index c44a577..487e8e1 100644 --- a/docs/config.md +++ b/docs/config.md @@ -56,7 +56,7 @@ theme: activeBorderColor: ["#ff966c", bold] # focused pane inactiveBorderColor: ["#589ed7"] selectedLineBgColor: ["#2d3f76"] # cursor row background (default: palette 8) - inactiveViewSelectedLineBgColor: ["#2f334d"] + inactiveViewSelectedLineBgColor: ["#2f334d"] # in the other panes (default: palette 8, not bold) defaultFgColor: ["#c8d3f5"] optionsTextColor: ["#82aaff"] # keys and labels searchMatchColor: ["#ffc777", bold] diff --git a/internal/ui/app/app.go b/internal/ui/app/app.go index 16712ae..f8ab7c3 100644 --- a/internal/ui/app/app.go +++ b/internal/ui/app/app.go @@ -489,7 +489,7 @@ func (m *Model) setVersions(items []provider.Object) { } func (m Model) Init() tea.Cmd { - cmds := []tea.Cmd{m.sourcesCmd(), m.spin.Tick, m.checkClipboard()} + cmds := []tea.Cmd{m.sourcesCmd(), m.spin.Tick, m.checkClipboard(), tea.RequestBackgroundColor} if m.cachePath != "" || m.placeDir != "" { cmds = append(cmds, tea.Tick(persistEvery, func(time.Time) tea.Msg { return persistTickMsg{} })) } @@ -514,6 +514,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case tea.BackgroundColorMsg: + m.st = m.st.OnBackground(msg.Color, msg.IsDark()) + return m, nil case tea.WindowSizeMsg: m.w, m.h = msg.Width, msg.Height diff --git a/internal/ui/styles/styles.go b/internal/ui/styles/styles.go index ba0200c..8fe991c 100644 --- a/internal/ui/styles/styles.go +++ b/internal/ui/styles/styles.go @@ -3,6 +3,8 @@ package styles import ( + "image/color" + "charm.land/lipgloss/v2" "git.kebcloud.no/keb/lazysecrets/internal/provider" @@ -32,6 +34,10 @@ type Styles struct { KindAzure lipgloss.Style KindDatabricks lipgloss.Style KindRepo lipgloss.Style + + // themed marks the selection colours as set by the theme, so + // OnBackground leaves them. + themed, themedBlurred bool } func Default() Styles { @@ -43,9 +49,12 @@ func Default() Styles { TitleBlurred: lipgloss.NewStyle(), Footer: lipgloss.NewStyle().Faint(true), - // A background rather than reverse, so the row keeps its colours. + // A background rather than reverse, so the row keeps its colours; + // in every pane, so each shows what's selected in it. The focused + // pane's is bold too. Palette 8 until the terminal says what its + // background is (OnBackground). Selected: lipgloss.NewStyle().Background(lipgloss.BrightBlack).Bold(true), - SelectedBlurred: lipgloss.NewStyle().Bold(true), + SelectedBlurred: lipgloss.NewStyle().Background(lipgloss.BrightBlack), Muted: lipgloss.NewStyle().Faint(true), Error: lipgloss.NewStyle().Foreground(lipgloss.Red), Notice: lipgloss.NewStyle().Foreground(lipgloss.Green), @@ -74,3 +83,23 @@ func (s Styles) Kind(k provider.Kind) lipgloss.Style { } return s.KindAzure } + +// OnBackground shades the selection colours from the terminal's +// background, a little lighter on a dark one and darker on a light one: +// a quiet band that keeps the row's colours readable, where palette 8 is +// often too bright. Colours the theme sets are kept. +func (s Styles) OnBackground(bg color.Color, dark bool) Styles { + shade := func(p float64) color.Color { + if dark { + return lipgloss.Lighten(bg, p) + } + return lipgloss.Darken(bg, p) + } + if !s.themed { + s.Selected = s.Selected.Background(shade(0.09)) + } + if !s.themedBlurred { + s.SelectedBlurred = s.SelectedBlurred.Background(shade(0.05)) + } + return s +} diff --git a/internal/ui/styles/theme.go b/internal/ui/styles/theme.go index 0ca2ade..6142d1f 100644 --- a/internal/ui/styles/theme.go +++ b/internal/ui/styles/theme.go @@ -70,8 +70,10 @@ func FromTheme(theme map[string][]string) (Styles, color.Color, error) { st.BorderBlurred = v.fg(lipgloss.NewStyle()) case "selectedLineBgColor": st.Selected = v.bg(lipgloss.NewStyle()).Bold(true) + st.themed = true case "inactiveViewSelectedLineBgColor": st.SelectedBlurred = v.bg(lipgloss.NewStyle()).Bold(true) + st.themedBlurred = true case "defaultFgColor": fg = v.color case "optionsTextColor": diff --git a/internal/ui/styles/theme_test.go b/internal/ui/styles/theme_test.go index fe07e03..4ae4d4d 100644 --- a/internal/ui/styles/theme_test.go +++ b/internal/ui/styles/theme_test.go @@ -1,6 +1,7 @@ package styles import ( + "fmt" "strings" "testing" @@ -62,3 +63,32 @@ func TestEmptyThemeIsDefault(t *testing.T) { t.Errorf("empty theme: %v %v reverse=%v bg=%v", err, fg, st.Selected.GetReverse(), st.Selected.GetBackground()) } } + +func TestOnBackground(t *testing.T) { + hex := func(c any) string { + r, g, b, _ := c.(interface{ RGBA() (r, g, b, a uint32) }).RGBA() + return fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8) + } + st := Default().OnBackground(lipgloss.Color("#282c34"), true) + if got := hex(st.Selected.GetBackground()); got != "#3e424a" { + t.Errorf("dark: selected %s", got) + } + if got := hex(st.SelectedBlurred.GetBackground()); got != "#343840" { + t.Errorf("dark: blurred %s", got) + } + if got := hex(Default().OnBackground(lipgloss.Color("#ffffff"), false).Selected.GetBackground()); got != "#e8e8e8" { + t.Errorf("light: selected %s", got) + } + + themed, _, err := FromTheme(map[string][]string{"selectedLineBgColor": {"#2d3f76"}}) + if err != nil { + t.Fatal(err) + } + st = themed.OnBackground(lipgloss.Color("#282c34"), true) + if got := hex(st.Selected.GetBackground()); got != "#2d3f76" { + t.Errorf("themed: selected %s (want the theme's)", got) + } + if got := hex(st.SelectedBlurred.GetBackground()); got != "#343840" { + t.Errorf("themed: blurred %s (not themed: shaded)", got) + } +}