detached 3a8b3d9

Merge pull request 'feat(bar): add bar.background option for transparent bars' (#17) from feat/bar-background into main

commit
3a8b3d9633092e989bd3c23c299677622198c47a
author
bnema · · 2026-10-08 08:50 UTC
merge of
0254710 + 4f60ef9 · diff against the first parent
signature
unsigned
7 files · +158 -6
M docs/configuration.md +4 -1
@@ -78,6 +78,8 @@ snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,
7878 bar.top-right =
7979 bar.bottom-right =
8080 bar.interval = 5s
81+# Fill the top and bottom bars with the theme surface; off keeps the terminal background.
82+bar.background = on
8183
8284 # Command palette codes: 2-3 letters or digits.
8385 code.new-tab = CNT
@@ -211,10 +213,11 @@ If the daemon refuses to start because its session data is broken, do not edit t
211213 | Setting | Effect |
212214 |---|---|
213215 | `theme = auto` | Follow your terminal's colors and light/dark mode. |
214-| `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`. |
215217 | `theme.palette = off` | Neutral colors, no accent. |
216218 | `theme.accent = auto` | Pick an accent from your terminal's ANSI colors (blue if unsure). |
217219 | `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. No effect on 16-color and mono terminals, where bars already use it. |
218221
219222 - vev only colors its own UI (bars, borders, palette). Pane content is never recolored.
220223 - 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).
@@ -78,6 +78,8 @@ snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,
78 bar.top-right =78 bar.top-right =
79 bar.bottom-right =79 bar.bottom-right =
80 bar.interval = 5s80 bar.interval = 5s
81+# Fill the top and bottom bars with the theme surface; off keeps the terminal background.
82+bar.background = on
81 83
82 # Command palette codes: 2-3 letters or digits.84 # Command palette codes: 2-3 letters or digits.
83 code.new-tab = CNT85 code.new-tab = CNT
@@ -211,10 +213,11 @@ If the daemon refuses to start because its session data is broken, do not edit t
211 | Setting | Effect |213 | Setting | Effect |
212 |---|---|214 |---|---|
213 | `theme = auto` | Follow your terminal's colors and light/dark mode. |215 | `theme = auto` | Follow your terminal's colors and light/dark mode. |
214-| `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`. |
215 | `theme.palette = off` | Neutral colors, no accent. |217 | `theme.palette = off` | Neutral colors, no accent. |
216 | `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). |
217 | `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. No effect on 16-color and mono terminals, where bars already use it. |
218 221
219 - 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.
220 - 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/adapters/config/config.go +7 -0
@@ -122,6 +122,13 @@ func Parse(r io.Reader) (domain.Config, []domain.Warning, error) {
122122 interval = domain.MinBarInterval
123123 }
124124 cfg.Bar.Interval = interval
125+ case key == "bar.background":
126+ on, ok := parseOnOff(value)
127+ if !ok {
128+ warnings = append(warnings, domain.Warning{Line: lineNo, Msg: fmt.Sprintf("invalid bar.background %q", value)})
129+ continue
130+ }
131+ cfg.Bar.Transparent = !on
125132 case key == "palette.anchor":
126133 warnings = warnDuplicateKey(warnings, seenPaletteKeys, key, lineNo)
127134 if strings.EqualFold(strings.TrimSpace(value), "auto") {
@@ -122,6 +122,13 @@ func Parse(r io.Reader) (domain.Config, []domain.Warning, error) {
122 interval = domain.MinBarInterval122 interval = domain.MinBarInterval
123 }123 }
124 cfg.Bar.Interval = interval124 cfg.Bar.Interval = interval
125+ case key == "bar.background":
126+ on, ok := parseOnOff(value)
127+ if !ok {
128+ warnings = append(warnings, domain.Warning{Line: lineNo, Msg: fmt.Sprintf("invalid bar.background %q", value)})
129+ continue
130+ }
131+ cfg.Bar.Transparent = !on
125 case key == "palette.anchor":132 case key == "palette.anchor":
126 warnings = warnDuplicateKey(warnings, seenPaletteKeys, key, lineNo)133 warnings = warnDuplicateKey(warnings, seenPaletteKeys, key, lineNo)
127 if strings.EqualFold(strings.TrimSpace(value), "auto") {134 if strings.EqualFold(strings.TrimSpace(value), "auto") {
M internal/adapters/config/config_test.go +33 -0
@@ -428,6 +428,39 @@ func TestParseThemePalette(t *testing.T) {
428428 }
429429 }
430430
431+func TestParseBarBackground(t *testing.T) {
432+ t.Parallel()
433+
434+ tests := []struct {
435+ name string
436+ input string
437+ wantTransparent bool
438+ wantWarnings []domain.Warning
439+ }{
440+ {name: "on keeps the theme surface", input: "bar.background = on\n"},
441+ {name: "off makes bars transparent", input: "bar.background = off\n", wantTransparent: true},
442+ {
443+ name: "invalid value warns and keeps default",
444+ input: "bar.background = none\n",
445+ wantWarnings: []domain.Warning{
446+ {Line: 1, Msg: "invalid bar.background \"none\""},
447+ },
448+ },
449+ {name: "absent key keeps default", input: "bar.interval = 2s\n"},
450+ }
451+
452+ for _, tt := range tests {
453+ t.Run(tt.name, func(t *testing.T) {
454+ t.Parallel()
455+
456+ got, warnings, err := Parse(strings.NewReader(tt.input))
457+ require.NoError(t, err)
458+ require.Equal(t, tt.wantTransparent, got.Bar.Transparent)
459+ require.Equal(t, tt.wantWarnings, warnings)
460+ })
461+ }
462+}
463+
431464 func TestDefaultsThemePalette(t *testing.T) {
432465 t.Parallel()
433466
@@ -428,6 +428,39 @@ func TestParseThemePalette(t *testing.T) {
428 }428 }
429 }429 }
430 430
431+func TestParseBarBackground(t *testing.T) {
432+ t.Parallel()
433+
434+ tests := []struct {
435+ name string
436+ input string
437+ wantTransparent bool
438+ wantWarnings []domain.Warning
439+ }{
440+ {name: "on keeps the theme surface", input: "bar.background = on\n"},
441+ {name: "off makes bars transparent", input: "bar.background = off\n", wantTransparent: true},
442+ {
443+ name: "invalid value warns and keeps default",
444+ input: "bar.background = none\n",
445+ wantWarnings: []domain.Warning{
446+ {Line: 1, Msg: "invalid bar.background \"none\""},
447+ },
448+ },
449+ {name: "absent key keeps default", input: "bar.interval = 2s\n"},
450+ }
451+
452+ for _, tt := range tests {
453+ t.Run(tt.name, func(t *testing.T) {
454+ t.Parallel()
455+
456+ got, warnings, err := Parse(strings.NewReader(tt.input))
457+ require.NoError(t, err)
458+ require.Equal(t, tt.wantTransparent, got.Bar.Transparent)
459+ require.Equal(t, tt.wantWarnings, warnings)
460+ })
461+ }
462+}
463+
431 func TestDefaultsThemePalette(t *testing.T) {464 func TestDefaultsThemePalette(t *testing.T) {
432 t.Parallel()465 t.Parallel()
433 466
M internal/domain/config.go +4 -1
@@ -47,11 +47,14 @@ func DefaultSnapshotRestoreProcesses() []string {
4747 return append([]string(nil), defaultSnapshotRestoreProcesses...)
4848 }
4949
50-// BarConfig contains user-configurable bar right-anchor settings.
50+// BarConfig contains user-configurable bar settings.
5151 type BarConfig struct {
5252 TopRight string
5353 BottomRight string
5454 Interval time.Duration
55+ // Transparent keeps the terminal background behind the top and bottom
56+ // bars instead of the theme surface color (bar.background = off).
57+ Transparent bool
5558 }
5659
5760 // CopyConfig contains copy-mode selection and mouse motion settings.
@@ -47,11 +47,14 @@ func DefaultSnapshotRestoreProcesses() []string {
47 return append([]string(nil), defaultSnapshotRestoreProcesses...)47 return append([]string(nil), defaultSnapshotRestoreProcesses...)
48 }48 }
49 49
50-// BarConfig contains user-configurable bar right-anchor settings.50+// BarConfig contains user-configurable bar settings.
51 type BarConfig struct {51 type BarConfig struct {
52 TopRight string52 TopRight string
53 BottomRight string53 BottomRight string
54 Interval time.Duration54 Interval time.Duration
55+ // Transparent keeps the terminal background behind the top and bottom
56+ // bars instead of the theme surface color (bar.background = off).
57+ Transparent bool
55 }58 }
56 59
57 // CopyConfig contains copy-mode selection and mouse motion settings.60 // CopyConfig contains copy-mode selection and mouse motion settings.
A internal/usecase/daemon/bar_background_test.go +94 -0
@@ -0,0 +1,94 @@
1+package daemon
2+
3+import (
4+ "testing"
5+
6+ "github.com/stretchr/testify/require"
7+
8+ renderer "github.com/bnema/vev-vt"
9+ "github.com/bnema/vev/internal/domain"
10+ "github.com/bnema/vev/internal/domain/terminalcap"
11+ themeui "github.com/bnema/vev/internal/usecase/theme"
12+)
13+
14+func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) {
15+ paletteColors := [16]renderer.RGB{}
16+ paletteColors[4] = renderer.RGB{R: 40, G: 90, B: 220}
17+ raw := themeui.Theme{
18+ Foreground: renderer.RGB{R: 230, G: 230, B: 230}, Background: renderer.RGB{R: 8, G: 9, B: 10},
19+ HasFG: true, HasBG: true, Known: true, UsePalette: true,
20+ Palette: paletteColors, PaletteKnown: 1 << 4,
21+ }
22+
23+ tests := []struct {
24+ name string
25+ mode domain.ThemeMode
26+ paletteOff bool
27+ transparent bool
28+ }{
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},
33+ }
34+ for _, tt := range tests {
35+ t.Run(tt.name, func(t *testing.T) {
36+ cfg := domain.Defaults()
37+ cfg.Theme = tt.mode
38+ cfg.ThemePalette = !tt.paletteOff
39+ cfg.Bar.Transparent = tt.transparent
40+ d := newTestDaemon(t, nil, stubClock{})
41+ d.ApplyConfig(cfg)
42+
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
48+
49+ if tt.transparent {
50+ require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background")
51+ } else {
52+ require.True(t, got.SurfaceBar.Equal(themed.SurfaceBar))
53+ }
54+ // Every other role keeps its theme colors.
55+ require.True(t, got.SurfaceInactive.Equal(themed.SurfaceInactive))
56+ require.True(t, got.TabActive.Equal(themed.TabActive))
57+ require.True(t, got.MRUStyle(0, 2).Equal(themed.MRUStyle(0, 2)))
58+ require.True(t, got.CopyStatus.Equal(themed.CopyStatus))
59+ require.True(t, got.StatusBar.Equal(themed.StatusBar))
60+ })
61+ }
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+}
@@ -0,0 +1,94 @@
1+package daemon
2+
3+import (
4+ "testing"
5+
6+ "github.com/stretchr/testify/require"
7+
8+ renderer "github.com/bnema/vev-vt"
9+ "github.com/bnema/vev/internal/domain"
10+ "github.com/bnema/vev/internal/domain/terminalcap"
11+ themeui "github.com/bnema/vev/internal/usecase/theme"
12+)
13+
14+func TestBarBackgroundConfigResolvesBarSurface(t *testing.T) {
15+ paletteColors := [16]renderer.RGB{}
16+ paletteColors[4] = renderer.RGB{R: 40, G: 90, B: 220}
17+ raw := themeui.Theme{
18+ Foreground: renderer.RGB{R: 230, G: 230, B: 230}, Background: renderer.RGB{R: 8, G: 9, B: 10},
19+ HasFG: true, HasBG: true, Known: true, UsePalette: true,
20+ Palette: paletteColors, PaletteKnown: 1 << 4,
21+ }
22+
23+ tests := []struct {
24+ name string
25+ mode domain.ThemeMode
26+ paletteOff bool
27+ transparent bool
28+ }{
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},
33+ }
34+ for _, tt := range tests {
35+ t.Run(tt.name, func(t *testing.T) {
36+ cfg := domain.Defaults()
37+ cfg.Theme = tt.mode
38+ cfg.ThemePalette = !tt.paletteOff
39+ cfg.Bar.Transparent = tt.transparent
40+ d := newTestDaemon(t, nil, stubClock{})
41+ d.ApplyConfig(cfg)
42+
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
48+
49+ if tt.transparent {
50+ require.True(t, got.SurfaceBar.Equal(renderer.DefaultStyle()), "bar fill uses the terminal background")
51+ } else {
52+ require.True(t, got.SurfaceBar.Equal(themed.SurfaceBar))
53+ }
54+ // Every other role keeps its theme colors.
55+ require.True(t, got.SurfaceInactive.Equal(themed.SurfaceInactive))
56+ require.True(t, got.TabActive.Equal(themed.TabActive))
57+ require.True(t, got.MRUStyle(0, 2).Equal(themed.MRUStyle(0, 2)))
58+ require.True(t, got.CopyStatus.Equal(themed.CopyStatus))
59+ require.True(t, got.StatusBar.Equal(themed.StatusBar))
60+ })
61+ }
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+}
M internal/usecase/daemon/config.go +8 -4
@@ -21,9 +21,10 @@ var commandCodePattern = regexp.MustCompile(`^[A-Z0-9]{2,3}$`)
2121 // themeConfigSnapshot is the theme slice of runtimeConfig. Its zero value is
2222 // the default theme configuration: automatic mode with palette inheritance on.
2323 type themeConfigSnapshot struct {
24- mode domain.ThemeMode
25- paletteOff bool
26- accent domain.ThemeAccent
24+ mode domain.ThemeMode
25+ paletteOff bool
26+ accent domain.ThemeAccent
27+ barTransparent bool
2728 }
2829
2930 // runtimeConfig is the immutable derived configuration published by
@@ -96,7 +97,7 @@ func (d *Daemon) ApplyConfig(cfg domain.Config) {
9697 tabs: cfg.Tabs,
9798 ephemeral: cfg.Ephemeral,
9899 scrollback: scrollback,
99- theme: themeConfigSnapshot{mode: cfg.Theme, paletteOff: !cfg.ThemePalette, accent: cfg.ThemeAccent},
100+ theme: themeConfigSnapshot{mode: cfg.Theme, paletteOff: !cfg.ThemePalette, accent: cfg.ThemeAccent, barTransparent: cfg.Bar.Transparent},
100101 }
101102 d.bindings.Store(bindings)
102103 d.config.Store(snapshot)
@@ -359,6 +360,9 @@ func (d *Daemon) resolveAppliedTheme(raw theme.Theme, color terminalcap.ColorCap
359360 config := d.currentThemeConfig()
360361 effective := effectiveThemeForConfig(raw, config)
361362 resolved := theme.ResolveForColor(effective, config.accent, color)
363+ if config.barTransparent {
364+ resolved.Styles = resolved.Styles.WithTransparentBar()
365+ }
362366 return appliedTheme{Raw: resolved.Theme, Resolved: resolved}
363367 }
364368
@@ -21,9 +21,10 @@ var commandCodePattern = regexp.MustCompile(`^[A-Z0-9]{2,3}$`)
21 // themeConfigSnapshot is the theme slice of runtimeConfig. Its zero value is21 // themeConfigSnapshot is the theme slice of runtimeConfig. Its zero value is
22 // the default theme configuration: automatic mode with palette inheritance on.22 // the default theme configuration: automatic mode with palette inheritance on.
23 type themeConfigSnapshot struct {23 type themeConfigSnapshot struct {
24- mode domain.ThemeMode24+ mode domain.ThemeMode
25- paletteOff bool25+ paletteOff bool
26- accent domain.ThemeAccent26+ accent domain.ThemeAccent
27+ barTransparent bool
27 }28 }
28 29
29 // runtimeConfig is the immutable derived configuration published by30 // runtimeConfig is the immutable derived configuration published by
@@ -96,7 +97,7 @@ func (d *Daemon) ApplyConfig(cfg domain.Config) {
96 tabs: cfg.Tabs,97 tabs: cfg.Tabs,
97 ephemeral: cfg.Ephemeral,98 ephemeral: cfg.Ephemeral,
98 scrollback: scrollback,99 scrollback: scrollback,
99- theme: themeConfigSnapshot{mode: cfg.Theme, paletteOff: !cfg.ThemePalette, accent: cfg.ThemeAccent},100+ theme: themeConfigSnapshot{mode: cfg.Theme, paletteOff: !cfg.ThemePalette, accent: cfg.ThemeAccent, barTransparent: cfg.Bar.Transparent},
100 }101 }
101 d.bindings.Store(bindings)102 d.bindings.Store(bindings)
102 d.config.Store(snapshot)103 d.config.Store(snapshot)
@@ -359,6 +360,9 @@ func (d *Daemon) resolveAppliedTheme(raw theme.Theme, color terminalcap.ColorCap
359 config := d.currentThemeConfig()360 config := d.currentThemeConfig()
360 effective := effectiveThemeForConfig(raw, config)361 effective := effectiveThemeForConfig(raw, config)
361 resolved := theme.ResolveForColor(effective, config.accent, color)362 resolved := theme.ResolveForColor(effective, config.accent, color)
363+ if config.barTransparent {
364+ resolved.Styles = resolved.Styles.WithTransparentBar()
365+ }
362 return appliedTheme{Raw: resolved.Theme, Resolved: resolved}366 return appliedTheme{Raw: resolved.Theme, Resolved: resolved}
363 }367 }
364 368
M internal/usecase/theme/styles.go +8 -0
@@ -127,6 +127,14 @@ type Styles struct {
127127 PickerSelectionMuted renderer.Style
128128 }
129129
130+// WithTransparentBar returns s with the top and bottom bar fill on the
131+// terminal's default colors. Tabs, MRU entries, and every other role keep
132+// their theme colors.
133+func (s Styles) WithTransparentBar() Styles {
134+ s.SurfaceBar = renderer.DefaultStyle()
135+ return s
136+}
137+
130138 // NewStyles applies the default automatic policy. New callers that own a
131139 // policy should use Resolve and retain its complete immutable result.
132140 func NewStyles(t Theme) Styles {
@@ -127,6 +127,14 @@ type Styles struct {
127 PickerSelectionMuted renderer.Style127 PickerSelectionMuted renderer.Style
128 }128 }
129 129
130+// WithTransparentBar returns s with the top and bottom bar fill on the
131+// terminal's default colors. Tabs, MRU entries, and every other role keep
132+// their theme colors.
133+func (s Styles) WithTransparentBar() Styles {
134+ s.SurfaceBar = renderer.DefaultStyle()
135+ return s
136+}
137+
130 // NewStyles applies the default automatic policy. New callers that own a138 // NewStyles applies the default automatic policy. New callers that own a
131 // policy should use Resolve and retain its complete immutable result.139 // policy should use Resolve and retain its complete immutable result.
132 func NewStyles(t Theme) Styles {140 func NewStyles(t Theme) Styles {

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