detached 56d7b65

feat(theme): fade MRU history text toward 70% of its strength

commit
56d7b650a69e12a5a107fccae90c5f23d8bbcee5
author
brice · · 2026-10-15 02:25 UTC
parent
fff748e
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +35 -3
M internal/usecase/theme/ramp.go +17 -2
@@ -185,9 +185,23 @@ func mruWeight(index, count int) float64 {
185185 return 0.5 * (1 - float64(index)/float64(count))
186186 }
187187
188+// mruMaxTextDim is how far the oldest history entry's text fades toward its
189+// background: it keeps 70% of its strength.
190+const mruMaxTextDim = 0.3
191+
192+// mruTextDim is the share of the way from the text color to the background for
193+// a history entry: the newest keeps full text, the oldest fades by
194+// mruMaxTextDim, linearly in between: 3 entries → 0, 0.15, 0.3.
195+func mruTextDim(index, count int) float64 {
196+ if count <= 1 {
197+ return 0
198+ }
199+ return mruMaxTextDim * float64(index) / float64(count-1)
200+}
201+
188202 // MRUStyle places history entries by mruWeight between the bar and the active
189-// surface. The background is never moved for contrast; only the text picks the
190-// better of the theme foreground and background.
203+// surface. The background is never moved for contrast; the text picks the
204+// better of the theme foreground and background, then fades by mruTextDim.
191205 func MRUStyle(ramp Ramp, index, count int) renderer.Style {
192206 if !ramp.rgb || count <= 0 {
193207 return ramp.SurfaceRecent
@@ -196,5 +210,6 @@ func MRUStyle(ramp Ramp, index, count int) renderer.Style {
196210 weight := mruWeight(index, count)
197211 background := okLabLerp(ramp.SurfaceBar.BackgroundRGB, ramp.SurfaceActive.BackgroundRGB, weight)
198212 foreground, _ := primaryText(Theme{Foreground: ramp.foreground, Background: ramp.background}, background)
213+ foreground = okLabLerp(foreground, background, mruTextDim(index, count))
199214 return rgbSurface(foreground, background)
200215 }
@@ -185,9 +185,23 @@ func mruWeight(index, count int) float64 {
185 return 0.5 * (1 - float64(index)/float64(count))185 return 0.5 * (1 - float64(index)/float64(count))
186 }186 }
187 187
188+// mruMaxTextDim is how far the oldest history entry's text fades toward its
189+// background: it keeps 70% of its strength.
190+const mruMaxTextDim = 0.3
191+
192+// mruTextDim is the share of the way from the text color to the background for
193+// a history entry: the newest keeps full text, the oldest fades by
194+// mruMaxTextDim, linearly in between: 3 entries → 0, 0.15, 0.3.
195+func mruTextDim(index, count int) float64 {
196+ if count <= 1 {
197+ return 0
198+ }
199+ return mruMaxTextDim * float64(index) / float64(count-1)
200+}
201+
188 // MRUStyle places history entries by mruWeight between the bar and the active202 // MRUStyle places history entries by mruWeight between the bar and the active
189-// surface. The background is never moved for contrast; only the text picks the203+// surface. The background is never moved for contrast; the text picks the
190-// better of the theme foreground and background.204+// better of the theme foreground and background, then fades by mruTextDim.
191 func MRUStyle(ramp Ramp, index, count int) renderer.Style {205 func MRUStyle(ramp Ramp, index, count int) renderer.Style {
192 if !ramp.rgb || count <= 0 {206 if !ramp.rgb || count <= 0 {
193 return ramp.SurfaceRecent207 return ramp.SurfaceRecent
@@ -196,5 +210,6 @@ func MRUStyle(ramp Ramp, index, count int) renderer.Style {
196 weight := mruWeight(index, count)210 weight := mruWeight(index, count)
197 background := okLabLerp(ramp.SurfaceBar.BackgroundRGB, ramp.SurfaceActive.BackgroundRGB, weight)211 background := okLabLerp(ramp.SurfaceBar.BackgroundRGB, ramp.SurfaceActive.BackgroundRGB, weight)
198 foreground, _ := primaryText(Theme{Foreground: ramp.foreground, Background: ramp.background}, background)212 foreground, _ := primaryText(Theme{Foreground: ramp.foreground, Background: ramp.background}, background)
213+ foreground = okLabLerp(foreground, background, mruTextDim(index, count))
199 return rgbSurface(foreground, background)214 return rgbSurface(foreground, background)
200 }215 }
M internal/usecase/theme/ramp_test.go +18 -1
@@ -160,7 +160,7 @@ func TestMRUStylePlacesHistoryBetweenActiveAndBar(t *testing.T) {
160160 style := MRUStyle(ramp, index, count)
161161 require.Equal(t, okLabLerp(bar, active, weight), style.BackgroundRGB, "count=%d index=%d", count, index)
162162 foreground, _ := primaryText(tt.theme, style.BackgroundRGB)
163- require.Equal(t, foreground, style.ForegroundRGB)
163+ require.Equal(t, okLabLerp(foreground, style.BackgroundRGB, mruTextDim(index, count)), style.ForegroundRGB)
164164 }
165165 }
166166 require.Equal(t, okLabLerp(bar, active, 0.5), MRUStyle(ramp, 0, 1).BackgroundRGB, "the newest entry always sits halfway")
@@ -168,6 +168,23 @@ func TestMRUStylePlacesHistoryBetweenActiveAndBar(t *testing.T) {
168168 }
169169 }
170170
171+func TestMRUTextDimFadesOldestToSeventyPercent(t *testing.T) {
172+ tests := []struct {
173+ count int
174+ want []float64
175+ }{
176+ {count: 1, want: []float64{0}},
177+ {count: 2, want: []float64{0, 0.3}},
178+ {count: 3, want: []float64{0, 0.15, 0.3}},
179+ {count: 4, want: []float64{0, 0.1, 0.2, 0.3}},
180+ }
181+ for _, tt := range tests {
182+ for index, want := range tt.want {
183+ require.InDelta(t, want, mruTextDim(index, tt.count), 1e-9, "count=%d index=%d", tt.count, index)
184+ }
185+ }
186+}
187+
171188 func TestMRUStyleWithoutRGBKeepsRecentSurface(t *testing.T) {
172189 ramp := neutralRamp(rampTheme(false))
173190 require.Equal(t, ramp.SurfaceRecent, MRUStyle(ramp, 0, 3))
@@ -160,7 +160,7 @@ func TestMRUStylePlacesHistoryBetweenActiveAndBar(t *testing.T) {
160 style := MRUStyle(ramp, index, count)160 style := MRUStyle(ramp, index, count)
161 require.Equal(t, okLabLerp(bar, active, weight), style.BackgroundRGB, "count=%d index=%d", count, index)161 require.Equal(t, okLabLerp(bar, active, weight), style.BackgroundRGB, "count=%d index=%d", count, index)
162 foreground, _ := primaryText(tt.theme, style.BackgroundRGB)162 foreground, _ := primaryText(tt.theme, style.BackgroundRGB)
163- require.Equal(t, foreground, style.ForegroundRGB)163+ require.Equal(t, okLabLerp(foreground, style.BackgroundRGB, mruTextDim(index, count)), style.ForegroundRGB)
164 }164 }
165 }165 }
166 require.Equal(t, okLabLerp(bar, active, 0.5), MRUStyle(ramp, 0, 1).BackgroundRGB, "the newest entry always sits halfway")166 require.Equal(t, okLabLerp(bar, active, 0.5), MRUStyle(ramp, 0, 1).BackgroundRGB, "the newest entry always sits halfway")
@@ -168,6 +168,23 @@ func TestMRUStylePlacesHistoryBetweenActiveAndBar(t *testing.T) {
168 }168 }
169 }169 }
170 170
171+func TestMRUTextDimFadesOldestToSeventyPercent(t *testing.T) {
172+ tests := []struct {
173+ count int
174+ want []float64
175+ }{
176+ {count: 1, want: []float64{0}},
177+ {count: 2, want: []float64{0, 0.3}},
178+ {count: 3, want: []float64{0, 0.15, 0.3}},
179+ {count: 4, want: []float64{0, 0.1, 0.2, 0.3}},
180+ }
181+ for _, tt := range tests {
182+ for index, want := range tt.want {
183+ require.InDelta(t, want, mruTextDim(index, tt.count), 1e-9, "count=%d index=%d", tt.count, index)
184+ }
185+ }
186+}
187+
171 func TestMRUStyleWithoutRGBKeepsRecentSurface(t *testing.T) {188 func TestMRUStyleWithoutRGBKeepsRecentSurface(t *testing.T) {
172 ramp := neutralRamp(rampTheme(false))189 ramp := neutralRamp(rampTheme(false))
173 require.Equal(t, ramp.SurfaceRecent, MRUStyle(ramp, 0, 3))190 require.Equal(t, ramp.SurfaceRecent, MRUStyle(ramp, 0, 3))

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