test(bar): cover bar.background across theme modes and live reload
- commit
- 4f60ef9c18ea25a107e7da1a6ca5b5b445e1790b
- author
- brice · · 2026-10-08 08:48 UTC
- parent
- 60de555
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +47 -8
M docs/configuration.md +2 -2
| @@ -213,11 +213,11 @@ If the daemon refuses to start because its session data is broken, do not edit t | |||
| 213 | 213 | | Setting | Effect | | |
| 214 | 214 | |---|---| | |
| 215 | 215 | | `theme = auto` | Follow your terminal's colors and light/dark mode. | | |
| 216 | - | | `theme = dark` / `light` | Neutral built-in colors. Ignores the two settings below. | | |
| 216 | + | | `theme = dark` / `light` | Neutral built-in colors. Ignores `theme.palette` and `theme.accent`. | | |
| 217 | 217 | | `theme.palette = off` | Neutral colors, no accent. | | |
| 218 | 218 | | `theme.accent = auto` | Pick an accent from your terminal's ANSI colors (blue if unsure). | | |
| 219 | 219 | | `theme.accent = 0`–`15` | Use exactly that ANSI color as the accent. | | |
| 220 | - | | `bar.background = off` | Keep the terminal background behind the top and bottom bars (useful with a transparent terminal). Tabs and recent sessions keep their colors. | | |
| 220 | + | | `bar.background = off` | Keep the terminal background behind the top and bottom bars (useful with a transparent terminal). Tabs and recent sessions keep their colors. No effect on 16-color and mono terminals, where bars already use it. | | |
| 221 | 221 | ||
| 222 | 222 | - vev only colors its own UI (bars, borders, palette). Pane content is never recolored. | |
| 223 | 223 | - Tinted backgrounds need a truecolor or 256-color terminal. On 16-color and monochrome terminals vev uses bold, reverse, and faint instead. See [terminal colors](terminal.md#colors). | |
| @@ -213,11 +213,11 @@ If the daemon refuses to start because its session data is broken, do not edit t | |||||
| 213 | | Setting | Effect | | 213 | | Setting | Effect | | ||
| 214 | |---|---| | 214 | |---|---| | ||
| 215 | | `theme = auto` | Follow your terminal's colors and light/dark mode. | | 215 | | `theme = auto` | Follow your terminal's colors and light/dark mode. | | ||
| 216 | - | | `theme = dark` / `light` | Neutral built-in colors. Ignores the two settings below. | | 216 | + | | `theme = dark` / `light` | Neutral built-in colors. Ignores `theme.palette` and `theme.accent`. | |
| 217 | | `theme.palette = off` | Neutral colors, no accent. | | 217 | | `theme.palette = off` | Neutral colors, no accent. | | ||
| 218 | | `theme.accent = auto` | Pick an accent from your terminal's ANSI colors (blue if unsure). | | 218 | | `theme.accent = auto` | Pick an accent from your terminal's ANSI colors (blue if unsure). | | ||
| 219 | | `theme.accent = 0`–`15` | Use exactly that ANSI color as the accent. | | 219 | | `theme.accent = 0`–`15` | Use exactly that ANSI color as the accent. | | ||
| 220 | - | | `bar.background = off` | Keep the terminal background behind the top and bottom bars (useful with a transparent terminal). Tabs and recent sessions keep their colors. | | 220 | + | | `bar.background = off` | Keep the terminal background behind the top and bottom bars (useful with a transparent terminal). Tabs and recent sessions keep their colors. No effect on 16-color and mono terminals, where bars already use it. | |
| 221 | 221 | ||||
| 222 | - vev only colors its own UI (bars, borders, palette). Pane content is never recolored. | 222 | - vev only colors its own UI (bars, borders, palette). Pane content is never recolored. | ||
| 223 | - Tinted backgrounds need a truecolor or 256-color terminal. On 16-color and monochrome terminals vev uses bold, reverse, and faint instead. See [terminal colors](terminal.md#colors). | 223 | - Tinted backgrounds need a truecolor or 256-color terminal. On 16-color and monochrome terminals vev uses bold, reverse, and faint instead. See [terminal colors](terminal.md#colors). | ||
M internal/usecase/daemon/bar_background_test.go +45 -6
| @@ -22,22 +22,29 @@ func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) { | |||
| 22 | 22 | ||
| 23 | 23 | tests := []struct { | |
| 24 | 24 | name string | |
| 25 | + | mode domain.ThemeMode | |
| 26 | + | paletteOff bool | |
| 25 | 27 | transparent bool | |
| 26 | - | color terminalcap.ColorCapabilities | |
| 27 | 28 | }{ | |
| 28 | - | {name: "truecolor themed", color: terminalcap.ColorCapabilities{Mode: terminalcap.TrueColor}}, | |
| 29 | - | {name: "truecolor transparent", transparent: true, color: terminalcap.ColorCapabilities{Mode: terminalcap.TrueColor}}, | |
| 30 | - | {name: "ansi16 transparent", transparent: true, color: terminalcap.ColorCapabilities{Mode: terminalcap.ANSI16}}, | |
| 29 | + | {name: "accent themed"}, | |
| 30 | + | {name: "accent transparent", transparent: true}, | |
| 31 | + | {name: "palette off transparent", paletteOff: true, transparent: true}, | |
| 32 | + | {name: "forced dark transparent", mode: domain.ThemeDark, transparent: true}, | |
| 31 | 33 | } | |
| 32 | 34 | for _, tt := range tests { | |
| 33 | 35 | t.Run(tt.name, func(t *testing.T) { | |
| 34 | 36 | cfg := domain.Defaults() | |
| 37 | + | cfg.Theme = tt.mode | |
| 38 | + | cfg.ThemePalette = !tt.paletteOff | |
| 35 | 39 | cfg.Bar.Transparent = tt.transparent | |
| 36 | 40 | d := newTestDaemon(t, nil, stubClock{}) | |
| 37 | 41 | d.ApplyConfig(cfg) | |
| 38 | 42 | ||
| 39 | - | themed := themeui.ResolveForColor(raw, cfg.ThemeAccent, tt.color).Styles | |
| 40 | - | got := d.resolveAppliedTheme(raw, tt.color).Resolved.Styles | |
| 43 | + | themedCfg := themeConfigSnapshot{mode: cfg.Theme, paletteOff: tt.paletteOff, accent: cfg.ThemeAccent} | |
| 44 | + | themed := themeui.ResolveForColor(effectiveThemeForConfig(raw, themedCfg), cfg.ThemeAccent, terminalcap.ColorCapabilities{}).Styles | |
| 45 | + | require.False(t, themed.SurfaceBar.Equal(renderer.DefaultStyle()), "fixture must produce a tinted bar") | |
| 46 | + | ||
| 47 | + | got := d.resolveAppliedTheme(raw, terminalcap.ColorCapabilities{}).Resolved.Styles | |
| 41 | 48 | ||
| 42 | 49 | if tt.transparent { | |
| 43 | 50 | require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background") | |
| @@ -53,3 +60,35 @@ func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) { | |||
| 53 | 60 | }) | |
| 54 | 61 | } | |
| 55 | 62 | } | |
| 63 | + | ||
| 64 | + | func TestApplyConfigBarBackgroundRepaintsBars(t *testing.T) { | |
| 65 | + | p, releasePTY := newBlockingPTY(t) | |
| 66 | + | d, sess, ac, _ := newManualSessionWithPTYs(t, p) | |
| 67 | + | defer releasePTY() | |
| 68 | + | d.ApplyConfig(domain.Config{Theme: domain.ThemeDark}) | |
| 69 | + | ||
| 70 | + | win := testAttachmentTab(sess) | |
| 71 | + | win.mu.Lock() | |
| 72 | + | win.size = domain.Size{Cols: 40, Rows: 4} | |
| 73 | + | win.mu.Unlock() | |
| 74 | + | pane := win.focusedPane() | |
| 75 | + | pane.mu.Lock() | |
| 76 | + | pane.screen.Resize(40, 4) | |
| 77 | + | pane.mu.Unlock() | |
| 78 | + | ||
| 79 | + | d.paint(sess, ac, true, nil) | |
| 80 | + | ac.sendMu.Lock() | |
| 81 | + | require.True(t, ac.render.cache.valid) | |
| 82 | + | themedFill := ac.render.cache.frame.At(39, 0).Style | |
| 83 | + | themedTab := ac.render.cache.frame.At(1, 0).Style | |
| 84 | + | ac.sendMu.Unlock() | |
| 85 | + | require.False(t, themedFill.Equal(renderer.DefaultStyle()), "themed bar fill is tinted") | |
| 86 | + | ||
| 87 | + | d.ApplyConfig(domain.Config{Theme: domain.ThemeDark, Bar: domain.BarConfig{Transparent: true}}) | |
| 88 | + | ||
| 89 | + | ac.sendMu.Lock() | |
| 90 | + | defer ac.sendMu.Unlock() | |
| 91 | + | require.True(t, ac.render.cache.valid) | |
| 92 | + | require.True(t, ac.render.cache.frame.At(39, 0).Style.Equal(renderer.DefaultStyle()), "reload clears the bar fill") | |
| 93 | + | require.True(t, ac.render.cache.frame.At(1, 0).Style.Equal(themedTab), "tab keeps its theme style") | |
| 94 | + | } | |
| @@ -22,22 +22,29 @@ func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) { | |||||
| 22 | 22 | ||||
| 23 | tests := []struct { | 23 | tests := []struct { | ||
| 24 | name string | 24 | name string | ||
| 25 | + | mode domain.ThemeMode | |||
| 26 | + | paletteOff bool | |||
| 25 | transparent bool | 27 | transparent bool | ||
| 26 | - | color terminalcap.ColorCapabilities | |||
| 27 | }{ | 28 | }{ | ||
| 28 | - | {name: "truecolor themed", color: terminalcap.ColorCapabilities{Mode: terminalcap.TrueColor}}, | 29 | + | {name: "accent themed"}, |
| 29 | - | {name: "truecolor transparent", transparent: true, color: terminalcap.ColorCapabilities{Mode: terminalcap.TrueColor}}, | 30 | + | {name: "accent transparent", transparent: true}, |
| 30 | - | {name: "ansi16 transparent", transparent: true, color: terminalcap.ColorCapabilities{Mode: terminalcap.ANSI16}}, | 31 | + | {name: "palette off transparent", paletteOff: true, transparent: true}, |
| 32 | + | {name: "forced dark transparent", mode: domain.ThemeDark, transparent: true}, | |||
| 31 | } | 33 | } | ||
| 32 | for _, tt := range tests { | 34 | for _, tt := range tests { | ||
| 33 | t.Run(tt.name, func(t *testing.T) { | 35 | t.Run(tt.name, func(t *testing.T) { | ||
| 34 | cfg := domain.Defaults() | 36 | cfg := domain.Defaults() | ||
| 37 | + | cfg.Theme = tt.mode | |||
| 38 | + | cfg.ThemePalette = !tt.paletteOff | |||
| 35 | cfg.Bar.Transparent = tt.transparent | 39 | cfg.Bar.Transparent = tt.transparent | ||
| 36 | d := newTestDaemon(t, nil, stubClock{}) | 40 | d := newTestDaemon(t, nil, stubClock{}) | ||
| 37 | d.ApplyConfig(cfg) | 41 | d.ApplyConfig(cfg) | ||
| 38 | 42 | ||||
| 39 | - | themed := themeui.ResolveForColor(raw, cfg.ThemeAccent, tt.color).Styles | 43 | + | themedCfg := themeConfigSnapshot{mode: cfg.Theme, paletteOff: tt.paletteOff, accent: cfg.ThemeAccent} |
| 40 | - | got := d.resolveAppliedTheme(raw, tt.color).Resolved.Styles | 44 | + | themed := themeui.ResolveForColor(effectiveThemeForConfig(raw, themedCfg), cfg.ThemeAccent, terminalcap.ColorCapabilities{}).Styles |
| 45 | + | require.False(t, themed.SurfaceBar.Equal(renderer.DefaultStyle()), "fixture must produce a tinted bar") | |||
| 46 | + | ||||
| 47 | + | got := d.resolveAppliedTheme(raw, terminalcap.ColorCapabilities{}).Resolved.Styles | |||
| 41 | 48 | ||||
| 42 | if tt.transparent { | 49 | if tt.transparent { | ||
| 43 | require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background") | 50 | require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background") | ||
| @@ -53,3 +60,35 @@ func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) { | |||||
| 53 | }) | 60 | }) | ||
| 54 | } | 61 | } | ||
| 55 | } | 62 | } | ||
| 63 | + | ||||
| 64 | + | func TestApplyConfigBarBackgroundRepaintsBars(t *testing.T) { | |||
| 65 | + | p, releasePTY := newBlockingPTY(t) | |||
| 66 | + | d, sess, ac, _ := newManualSessionWithPTYs(t, p) | |||
| 67 | + | defer releasePTY() | |||
| 68 | + | d.ApplyConfig(domain.Config{Theme: domain.ThemeDark}) | |||
| 69 | + | ||||
| 70 | + | win := testAttachmentTab(sess) | |||
| 71 | + | win.mu.Lock() | |||
| 72 | + | win.size = domain.Size{Cols: 40, Rows: 4} | |||
| 73 | + | win.mu.Unlock() | |||
| 74 | + | pane := win.focusedPane() | |||
| 75 | + | pane.mu.Lock() | |||
| 76 | + | pane.screen.Resize(40, 4) | |||
| 77 | + | pane.mu.Unlock() | |||
| 78 | + | ||||
| 79 | + | d.paint(sess, ac, true, nil) | |||
| 80 | + | ac.sendMu.Lock() | |||
| 81 | + | require.True(t, ac.render.cache.valid) | |||
| 82 | + | themedFill := ac.render.cache.frame.At(39, 0).Style | |||
| 83 | + | themedTab := ac.render.cache.frame.At(1, 0).Style | |||
| 84 | + | ac.sendMu.Unlock() | |||
| 85 | + | require.False(t, themedFill.Equal(renderer.DefaultStyle()), "themed bar fill is tinted") | |||
| 86 | + | ||||
| 87 | + | d.ApplyConfig(domain.Config{Theme: domain.ThemeDark, Bar: domain.BarConfig{Transparent: true}}) | |||
| 88 | + | ||||
| 89 | + | ac.sendMu.Lock() | |||
| 90 | + | defer ac.sendMu.Unlock() | |||
| 91 | + | require.True(t, ac.render.cache.valid) | |||
| 92 | + | require.True(t, ac.render.cache.frame.At(39, 0).Style.Equal(renderer.DefaultStyle()), "reload clears the bar fill") | |||
| 93 | + | require.True(t, ac.render.cache.frame.At(1, 0).Style.Equal(themedTab), "tab keeps its theme style") | |||
| 94 | + | } | |||