Skip to content

Commit 4ce486b

Browse files
committed
feat: add light themes
1 parent da085b7 commit 4ce486b

6 files changed

Lines changed: 120 additions & 19 deletions

File tree

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ direct HTTP streams, and YouTube live/videos via `yt-dlp`).
3434
- **Ambient mixer** — five looped beds (rain, fire, cafe, white noise,
3535
thunder), CC0 sources, embedded in the binary. Each channel has its
3636
own volume; mixes layer underneath the main station.
37-
- **Four themes**Tokyo Night, Catppuccin Mocha, Gruvbox Dark, Rose
38-
Pine. Open the theme picker with `t`.
37+
- **Eight themes**dark classics plus light palettes for bright rooms.
38+
Open the theme picker with `t`.
3939
- **Mini mode** — collapses to ~6 lines for a tmux pane (`m`).
4040
- **macOS media key** — the hardware Play/Pause key toggles the
4141
current station while `lofi-player` is running.
@@ -224,7 +224,7 @@ on first run with sensible defaults; a documented example sits at
224224
[`configs/lofi-player.example.yaml`](configs/lofi-player.example.yaml).
225225

226226
```yaml
227-
theme: tokyo-night # tokyo-night | catppuccin-mocha | gruvbox-dark | rose-pine
227+
theme: tokyo-night # see Themes below for all built-in theme ids
228228
volume: 60 # initial volume, 0–100
229229
230230
stations:
@@ -247,12 +247,16 @@ fails.
247247

248248
## Themes
249249

250-
Four palettes ship in the binary:
250+
Eight palettes ship in the binary:
251251

252-
- **Tokyo Night** (default) — cool, neon-on-deep-blue.
253-
- **Catppuccin Mocha** — pastel-on-warm-charcoal.
254-
- **Gruvbox Dark** — earthy, high-contrast.
255-
- **Rose Pine** — muted, soft mauve.
252+
- **Tokyo Night** (default, `tokyo-night`) — cool, neon-on-deep-blue.
253+
- **Catppuccin Mocha** (`catppuccin-mocha`) — pastel-on-warm-charcoal.
254+
- **Gruvbox Dark** (`gruvbox-dark`) — earthy, high-contrast.
255+
- **Rose Pine** (`rose-pine`) — muted, soft mauve.
256+
- **Catppuccin Latte** (`catppuccin-latte`) — airy pastel light theme.
257+
- **Rose Pine Dawn** (`rose-pine-dawn`) — warm sunrise paper with soft mauve.
258+
- **Solarized Light** (`solarized-light`) — classic low-contrast terminal daylight.
259+
- **Paper** (`paper`) — warm ink on cream for bright rooms.
256260

257261
Open the live picker with `t`, preview palettes with `↑/↓` or `j/k`,
258262
then press `enter` to select or `esc` to cancel. The choice is persisted

README.ru.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ TUI-плеер для лофи, чиллхопа и эмбиент-радио
3535
- **Эмбиент-микшер** — пять зацикленных дорожек (rain, fire, cafe,
3636
white noise, thunder), CC0-исходники, вшиты в бинарник. У каждого
3737
канала своя громкость, миксы накладываются под основную станцию.
38-
- **Четыре темы**Tokyo Night, Catppuccin Mocha, Gruvbox Dark,
39-
Rose Pine. Выбор из списка по `t`.
38+
- **Восемь тем**тёмная классика плюс светлые палитры для яркого
39+
окружения. Выбор из списка по `t`.
4040
- **Mini-режим** — сворачивает UI до ~6 строк, чтобы влезть в
4141
tmux-панель (`m`).
4242
- **Media key на macOS** — аппаратная клавиша Play/Pause переключает
@@ -230,7 +230,7 @@ lofi-player --import - < stations.yaml
230230
в [`configs/lofi-player.example.yaml`](configs/lofi-player.example.yaml).
231231

232232
```yaml
233-
theme: tokyo-night # tokyo-night | catppuccin-mocha | gruvbox-dark | rose-pine
233+
theme: tokyo-night # все встроенные id см. в разделе «Темы»
234234
volume: 60 # стартовая громкость, 0–100
235235
236236
stations:
@@ -253,12 +253,16 @@ stations:
253253

254254
## Темы
255255

256-
В бинарник встроены четыре палитры:
256+
В бинарник встроены восемь палитр:
257257

258-
- **Tokyo Night** (по умолчанию) — холодная, неон по тёмно-синему.
259-
- **Catppuccin Mocha** — пастель по тёплому угольному.
260-
- **Gruvbox Dark** — землистая, контрастная.
261-
- **Rose Pine** — приглушённая, мягкая мауве.
258+
- **Tokyo Night** (по умолчанию, `tokyo-night`) — холодная, неон по тёмно-синему.
259+
- **Catppuccin Mocha** (`catppuccin-mocha`) — пастель по тёплому угольному.
260+
- **Gruvbox Dark** (`gruvbox-dark`) — землистая, контрастная.
261+
- **Rose Pine** (`rose-pine`) — приглушённая, мягкая мауве.
262+
- **Catppuccin Latte** (`catppuccin-latte`) — воздушная пастельная светлая тема.
263+
- **Rose Pine Dawn** (`rose-pine-dawn`) — тёплая бумага рассвета с мягкой мауве.
264+
- **Solarized Light** (`solarized-light`) — классический спокойный терминальный daylight.
265+
- **Paper** (`paper`) — тёплые чернила на кремовой бумаге для ярких комнат.
262266

263267
Открой выбор темы по `t`, предпросматривай палитры через `↑/↓` или
264268
`j/k`, затем нажми `enter` для выбора или `esc` для отмены. Выбор

configs/lofi-player.example.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
#
66
# It is created with these defaults on first run if it doesn't exist.
77

8-
# Active color theme. Phase 0 only ships tokyo-night;
9-
# more themes (catppuccin-mocha, gruvbox-dark, rose-pine) arrive in Phase 2.
8+
# Active color theme. Built-ins:
9+
# tokyo-night, catppuccin-mocha, gruvbox-dark, rose-pine,
10+
# catppuccin-latte, rose-pine-dawn, solarized-light, paper.
1011
theme: tokyo-night
1112

1213
# Initial playback volume, 0–100.

internal/theme/theme.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,78 @@ func RosePine() Theme {
121121
}
122122
}
123123

124+
// CatppuccinLatte returns the Catppuccin Latte light palette
125+
// (https://github.com/catppuccin/catppuccin).
126+
func CatppuccinLatte() Theme {
127+
return Theme{
128+
Name: "catppuccin-latte",
129+
Background: lipgloss.Color("#eff1f5"), // Base
130+
Foreground: lipgloss.Color("#4c4f69"), // Text
131+
Muted: lipgloss.Color("#9ca0b0"), // Overlay0
132+
Subtle: lipgloss.Color("#ccd0da"), // Surface0
133+
Primary: lipgloss.Color("#8839ef"), // Mauve
134+
Secondary: lipgloss.Color("#1e66f5"), // Blue
135+
Accent: lipgloss.Color("#ea76cb"), // Pink
136+
Success: lipgloss.Color("#40a02b"), // Green
137+
Warning: lipgloss.Color("#df8e1d"), // Yellow
138+
Info: lipgloss.Color("#04a5e5"), // Sky
139+
}
140+
}
141+
142+
// RosePineDawn returns the Rose Pine Dawn light palette
143+
// (https://rosepinetheme.com/).
144+
func RosePineDawn() Theme {
145+
return Theme{
146+
Name: "rose-pine-dawn",
147+
Background: lipgloss.Color("#faf4ed"), // Base
148+
Foreground: lipgloss.Color("#575279"), // Text
149+
Muted: lipgloss.Color("#9893a5"), // Muted
150+
Subtle: lipgloss.Color("#fffaf3"), // Surface
151+
Primary: lipgloss.Color("#907aa9"), // Iris
152+
Secondary: lipgloss.Color("#56949f"), // Foam
153+
Accent: lipgloss.Color("#d7827e"), // Rose
154+
Success: lipgloss.Color("#286983"), // Pine
155+
Warning: lipgloss.Color("#ea9d34"), // Gold
156+
Info: lipgloss.Color("#b4637a"), // Love
157+
}
158+
}
159+
160+
// SolarizedLight returns the Solarized Light palette
161+
// (https://ethanschoonover.com/solarized/).
162+
func SolarizedLight() Theme {
163+
return Theme{
164+
Name: "solarized-light",
165+
Background: lipgloss.Color("#fdf6e3"), // base3
166+
Foreground: lipgloss.Color("#657b83"), // base00
167+
Muted: lipgloss.Color("#93a1a1"), // base1
168+
Subtle: lipgloss.Color("#eee8d5"), // base2
169+
Primary: lipgloss.Color("#6c71c4"), // violet
170+
Secondary: lipgloss.Color("#268bd2"), // blue
171+
Accent: lipgloss.Color("#d33682"), // magenta
172+
Success: lipgloss.Color("#859900"), // green
173+
Warning: lipgloss.Color("#b58900"), // yellow
174+
Info: lipgloss.Color("#2aa198"), // cyan
175+
}
176+
}
177+
178+
// Paper returns a warm, low-glare light palette tuned for plain terminal
179+
// backgrounds and long listening sessions.
180+
func Paper() Theme {
181+
return Theme{
182+
Name: "paper",
183+
Background: lipgloss.Color("#f7f3ea"),
184+
Foreground: lipgloss.Color("#3f3a32"),
185+
Muted: lipgloss.Color("#8b8174"),
186+
Subtle: lipgloss.Color("#e3dccf"),
187+
Primary: lipgloss.Color("#5c6bc0"),
188+
Secondary: lipgloss.Color("#00897b"),
189+
Accent: lipgloss.Color("#d81b60"),
190+
Success: lipgloss.Color("#558b2f"),
191+
Warning: lipgloss.Color("#c77700"),
192+
Info: lipgloss.Color("#0277bd"),
193+
}
194+
}
195+
124196
// Lookup returns the theme registered under name. If name is empty or
125197
// unknown, Lookup returns Tokyo Night and false; otherwise it returns the
126198
// matched theme and true. Callers can use the bool to warn the user when
@@ -181,6 +253,10 @@ var infos = []Info{
181253
{Name: "catppuccin-mocha", DisplayName: "Catppuccin Mocha", Description: "soft pastels on warm charcoal"},
182254
{Name: "gruvbox-dark", DisplayName: "Gruvbox Dark", Description: "earthy contrast with vintage warmth"},
183255
{Name: "rose-pine", DisplayName: "Rose Pine", Description: "muted mauve, calm and low-glare"},
256+
{Name: "catppuccin-latte", DisplayName: "Catppuccin Latte", Description: "airy pastels on a clean light base"},
257+
{Name: "rose-pine-dawn", DisplayName: "Rose Pine Dawn", Description: "warm sunrise paper with soft mauve"},
258+
{Name: "solarized-light", DisplayName: "Solarized Light", Description: "classic low-contrast terminal daylight"},
259+
{Name: "paper", DisplayName: "Paper", Description: "warm ink on cream for bright rooms"},
184260
}
185261

186262
// registry maps theme names to constructors. Phase 0 shipped only Tokyo
@@ -190,4 +266,8 @@ var registry = map[string]func() Theme{
190266
"catppuccin-mocha": CatppuccinMocha,
191267
"gruvbox-dark": GruvboxDark,
192268
"rose-pine": RosePine,
269+
"catppuccin-latte": CatppuccinLatte,
270+
"rose-pine-dawn": RosePineDawn,
271+
"solarized-light": SolarizedLight,
272+
"paper": Paper,
193273
}

internal/theme/theme_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ func TestLookup(t *testing.T) {
1313
{"catppuccin-mocha", "catppuccin-mocha", "catppuccin-mocha", true},
1414
{"gruvbox-dark", "gruvbox-dark", "gruvbox-dark", true},
1515
{"rose-pine", "rose-pine", "rose-pine", true},
16+
{"catppuccin-latte", "catppuccin-latte", "catppuccin-latte", true},
17+
{"rose-pine-dawn", "rose-pine-dawn", "rose-pine-dawn", true},
18+
{"solarized-light", "solarized-light", "solarized-light", true},
19+
{"paper", "paper", "paper", true},
1620

1721
{"empty string falls back to default", "", "tokyo-night", false},
1822
{"unknown name falls back", "monokai", "tokyo-night", false},

internal/tui/model_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/iRootPro/lofi-player/internal/audio"
1111
"github.com/iRootPro/lofi-player/internal/config"
12+
"github.com/iRootPro/lofi-player/internal/theme"
1213
)
1314

1415
// fixture returns a Model wired up with a real config and a sized window,
@@ -217,11 +218,18 @@ func TestThemePickerViewAndTopChip(t *testing.T) {
217218

218219
m = send(t, m, "t")
219220
out = m.View()
220-
for _, want := range []string{"themes", "Tokyo Night", "Catppuccin Mocha", "Gruvbox Dark", "Rose Pine", "enter", "esc"} {
221+
for _, want := range []string{"themes", "enter", "esc"} {
221222
if !strings.Contains(out, want) {
222223
t.Fatalf("theme picker missing %q; got:\n%s", want, out)
223224
}
224225
}
226+
for _, info := range theme.Infos() {
227+
for _, want := range []string{info.DisplayName, info.Name} {
228+
if !strings.Contains(out, want) {
229+
t.Fatalf("theme picker missing %q; got:\n%s", want, out)
230+
}
231+
}
232+
}
225233
}
226234

227235
func TestUpdate_QuitReturnsTeaQuit(t *testing.T) {

0 commit comments

Comments
 (0)