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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 { | |||
| 185 | 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 | 202 | // 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. | |
| 191 | 205 | func MRUStyle(ramp Ramp, index, count int) renderer.Style { | |
| 192 | 206 | if !ramp.rgb || count <= 0 { | |
| 193 | 207 | return ramp.SurfaceRecent | |
| @@ -196,5 +210,6 @@ func MRUStyle(ramp Ramp, index, count int) renderer.Style { | |||
| 196 | 210 | weight := mruWeight(index, count) | |
| 197 | 211 | background := okLabLerp(ramp.SurfaceBar.BackgroundRGB, ramp.SurfaceActive.BackgroundRGB, weight) | |
| 198 | 212 | foreground, _ := primaryText(Theme{Foreground: ramp.foreground, Background: ramp.background}, background) | |
| 213 | + | foreground = okLabLerp(foreground, background, mruTextDim(index, count)) | |
| 199 | 214 | return rgbSurface(foreground, background) | |
| 200 | 215 | } | |
| @@ -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 active | 202 | // 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 | 203 | + | // 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.SurfaceRecent | 207 | 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) { | |||
| 160 | 160 | style := MRUStyle(ramp, index, count) | |
| 161 | 161 | require.Equal(t, okLabLerp(bar, active, weight), style.BackgroundRGB, "count=%d index=%d", count, index) | |
| 162 | 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 | 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 | 188 | func TestMRUStyleWithoutRGBKeepsRecentSurface(t *testing.T) { | |
| 172 | 189 | ramp := neutralRamp(rampTheme(false)) | |
| 173 | 190 | 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)) | ||