Widgets copy their colors when they are created, so they now get role tokens (one unique RGB value per role) instead of colors, and a wrapper around the tcell screen replaces the tokens with the current theme's colors in SetContent. Changing the theme in the Config view swaps that table; the next draw recolors everything. Poster colors that equal a token are nudged off it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
133 lines
4.2 KiB
Go
133 lines
4.2 KiB
Go
package ui
|
|
|
|
import (
|
|
"image/color"
|
|
"testing"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
func TestParseTerminalAnswers(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
got string
|
|
graphics bool
|
|
bg color.RGBA
|
|
hasBG bool
|
|
}{
|
|
{"ghostty", "\x1b_Gi=31;OK\x1b\\\x1b]11;rgb:1e1e/1e1e/2e2e\x1b\\\x1b[?62;22c", true, color.RGBA{30, 30, 46, 255}, true},
|
|
{"text color too", "\x1b]10;rgb:4c4c/4f4f/6969\x1b\\\x1b]11;rgb:efef/f1f1/f5f5\x1b\\\x1b[?62c", false, color.RGBA{239, 241, 245, 255}, true},
|
|
{"bel and two digits", "\x1b]11;rgb:ff/80/00\a\x1b[?1;2c", false, color.RGBA{255, 128, 0, 255}, true},
|
|
{"one digit", "\x1b]11;rgb:f/0/8\x1b\\\x1b[?6c", false, color.RGBA{255, 0, 136, 255}, true},
|
|
{"only device attributes", "\x1b[?1;2c", false, color.RGBA{}, false},
|
|
{"garbage", "\x1b]11;rgb:zz/00/00\x1b\\\x1b[?6c", false, color.RGBA{}, false},
|
|
{"nothing", "", false, color.RGBA{}, false},
|
|
} {
|
|
info := parseTerminalAnswers([]byte(tc.got))
|
|
if info.graphics != tc.graphics || info.hasBG != tc.hasBG || info.bg != tc.bg {
|
|
t.Errorf("%s: %+v", tc.name, info)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChooseTheme(t *testing.T) {
|
|
dark := terminalInfo{bg: color.RGBA{30, 30, 46, 255}, hasBG: true}
|
|
light := terminalInfo{bg: color.RGBA{239, 241, 245, 255}, hasBG: true}
|
|
for _, tc := range []struct {
|
|
setting string
|
|
info terminalInfo
|
|
want string
|
|
}{
|
|
{"", light, "light"},
|
|
{"auto", dark, "dark"},
|
|
{"auto", terminalInfo{}, "dark"},
|
|
{"dark", light, "dark"},
|
|
{"light", dark, "light"},
|
|
} {
|
|
if th, note := chooseTheme(tc.setting, tc.info); th.name != tc.want {
|
|
t.Errorf("%q with %+v: %s (%s)", tc.setting, tc.info, th.name, note)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUseTokens(t *testing.T) {
|
|
oldStyles := tview.Styles
|
|
oldNames := map[string]tcell.Color{}
|
|
for k, v := range tcell.ColorNames {
|
|
oldNames[k] = v
|
|
}
|
|
defer func() {
|
|
tview.Styles = oldStyles
|
|
for k, v := range oldNames {
|
|
tcell.ColorNames[k] = v
|
|
}
|
|
}()
|
|
|
|
useTokens()
|
|
if tview.Styles.PrimitiveBackgroundColor != tokens.bg || tview.Styles.PrimaryTextColor != tokens.text {
|
|
t.Error("tview styles not set")
|
|
}
|
|
// Color names in texts mean the roles.
|
|
if named("yellow") != tokens.yellow || tcell.GetColor("black") != tokens.bg || tcell.GetColor("white") != tokens.text {
|
|
t.Error("color names not set")
|
|
}
|
|
}
|
|
|
|
func TestThemedScreenSwitchesLive(t *testing.T) {
|
|
sim := tcell.NewSimulationScreen("")
|
|
if err := sim.Init(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer sim.Fini()
|
|
sim.SetSize(4, 1)
|
|
s := newThemedScreen(sim, darkTheme)
|
|
poster := tcell.NewRGBColor(10, 20, 30)
|
|
|
|
draw := func() {
|
|
s.SetContent(0, 0, 'a', nil, tcell.StyleDefault.Foreground(tokens.yellow).Background(tokens.bg))
|
|
s.SetContent(1, 0, 'b', nil, tcell.StyleDefault.Foreground(poster).Background(poster))
|
|
}
|
|
check := func(th theme) {
|
|
_, _, st, _ := sim.GetContent(0, 0)
|
|
if fg, bg, _ := st.Decompose(); fg != th.yellow || bg != th.bg {
|
|
t.Errorf("%s: role colors drawn as %v on %v", th.name, fg, bg)
|
|
}
|
|
_, _, st, _ = sim.GetContent(1, 0)
|
|
if fg, bg, _ := st.Decompose(); fg != poster || bg != poster {
|
|
t.Errorf("%s: other colors changed to %v on %v", th.name, fg, bg)
|
|
}
|
|
}
|
|
draw()
|
|
check(darkTheme)
|
|
s.setTheme(lightTheme)
|
|
draw()
|
|
check(lightTheme)
|
|
|
|
// A poster color that equals a role color is nudged off it.
|
|
if c := notToken(tokens.red); c == tokens.red {
|
|
t.Error("role color in a picture not nudged")
|
|
}
|
|
if notToken(poster) != poster {
|
|
t.Error("ordinary color changed")
|
|
}
|
|
}
|
|
|
|
func TestThemeColorsSurvive256Colors(t *testing.T) {
|
|
// Every theme color is its own nearest match in the 256-color palette, so
|
|
// a terminal without 24-bit color shows it exactly and never picks one of
|
|
// the first 16 colors, which color themes change.
|
|
palette := make([]tcell.Color, 256)
|
|
for i := range palette {
|
|
palette[i] = tcell.PaletteColor(i)
|
|
}
|
|
for _, th := range []theme{darkTheme, lightTheme} {
|
|
for _, c := range []tcell.Color{th.bg, th.text, th.border, th.field, th.fieldMuted, th.list, th.pressed, th.toast,
|
|
th.gray, th.yellow, th.green, th.red, th.blue, th.purple} {
|
|
if got := tcell.FindColor(c, palette); got < tcell.PaletteColor(16) {
|
|
t.Errorf("%s theme: %v maps to palette color %d", th.name, c, got-tcell.ColorValid)
|
|
}
|
|
}
|
|
}
|
|
}
|