detached 4f60ef9

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 VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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
213213 | Setting | Effect |
214214 |---|---|
215215 | `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`. |
217217 | `theme.palette = off` | Neutral colors, no accent. |
218218 | `theme.accent = auto` | Pick an accent from your terminal's ANSI colors (blue if unsure). |
219219 | `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. |
221221
222222 - vev only colors its own UI (bars, borders, palette). Pane content is never recolored.
223223 - 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) {
2222
2323 tests := []struct {
2424 name string
25+ mode domain.ThemeMode
26+ paletteOff bool
2527 transparent bool
26- color terminalcap.ColorCapabilities
2728 }{
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},
3133 }
3234 for _, tt := range tests {
3335 t.Run(tt.name, func(t *testing.T) {
3436 cfg := domain.Defaults()
37+ cfg.Theme = tt.mode
38+ cfg.ThemePalette = !tt.paletteOff
3539 cfg.Bar.Transparent = tt.transparent
3640 d := newTestDaemon(t, nil, stubClock{})
3741 d.ApplyConfig(cfg)
3842
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
4148
4249 if tt.transparent {
4350 require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background")
@@ -53,3 +60,35 @@ func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) {
5360 })
5461 }
5562 }
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 string24 name string
25+ mode domain.ThemeMode
26+ paletteOff bool
25 transparent bool27 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.transparent39 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).Styles43+ themedCfg := themeConfigSnapshot{mode: cfg.Theme, paletteOff: tt.paletteOff, accent: cfg.ThemeAccent}
40- got := d.resolveAppliedTheme(raw, tt.color).Resolved.Styles44+ 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+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close