Skip to content

Commit 1115c2e

Browse files
feat: add Terminal (ANSI) theme preset that follows the terminal palette (#338)
## Summary Adds a **Terminal (ANSI)** theme preset built entirely from `Color::Reset` and named ANSI colors instead of hardcoded RGB. Since terminals remap already-rendered cells when their palette changes, terminal-theming tools like pywal recolor spotatui live, with no restart needed. Closes #336 ## Details - New `ThemePreset::Terminal` variant wired into the five preset enumeration points (enum, `all()`, `name()`, `from_name()`, `to_theme()`). It mirrors the default cyan look using the terminal palette: Cyan accents, Red/LightRed errors, Yellow hints, Magenta hover, DarkGray inactive, and `Reset` for text/background. - No parser changes: `parse_theme_item` and `color_to_string` already round-trip named ANSI colors, so config persistence, settings-screen cycling, and live preview all work unchanged. - New test `terminal_preset_colors_round_trip_through_config` asserts every color in the preset survives serialize/parse (guards against a future `Color::Indexed`, which `color_to_string` silently collapses to `Reset`). - Changelog entry under Unreleased. ## Testing - `cargo fmt --all`, `cargo clippy --no-default-features --features telemetry -- -D warnings`, and `cargo test --no-default-features --features telemetry` all pass (329 tests). - Manual: cycle Theme Preset to "Terminal (ANSI)" in Settings (`Alt-,`), then change the terminal palette (e.g. `wal -i <image>`) and confirm colors update without restarting.
2 parents 8182cf2 + c9ea2ed commit 1115c2e

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Added
66

77
- **Native cross-source play queue**: Press `z` on any track to add it to a native queue that plays across sources (Spotify, Local Files, Subsonic, and YouTube in one FIFO; internet radio is excluded since a live stream is not a finite track). Queued tracks play before the underlying playlist/album context, then that context resumes where it left off. Open the queue with `Shift+Q`: remove the selected item with `x` (rebindable via `remove_from_queue`, default `x`), reorder it with `J`/`K`, or press `Enter` to skip ahead and play it now. The Queue screen also previews what resumes from the underlying context once the queue drains. The queue survives restarts (persisted in `last_session.yml`). Spotify tracks queue through native (librespot) streaming; when you are controlling an external Spotify Connect device instead, `z` keeps the previous Web-API "add to Spotify's queue" behavior ([#206](https://github.com/LargeModGames/spotatui/issues/206)).
8+
- **Terminal (ANSI) theme preset**: A new theme preset built entirely from the terminal's own ANSI palette colors instead of hardcoded RGB, so terminal-theming tools like pywal recolor spotatui live, without a restart ([#336](https://github.com/LargeModGames/spotatui/issues/336)).
89
- **Playlist Folders First setting**: A new `behavior.group_folders_first` toggle (Settings → "Playlist Folders First") lists your Spotify playlist folders at the top of the Playlists tab, above playlists, keeping each group's existing order. Off by default. Handy because folders keep their creation date and otherwise sink to the bottom of the recency-sorted list.
910

1011
## [v0.40.1] 2026-07-04

src/core/user_config.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ impl Default for Theme {
175175
pub enum ThemePreset {
176176
#[default]
177177
Default,
178+
Terminal,
178179
PookiePink,
179180
Spotify,
180181
Vesper,
@@ -192,6 +193,7 @@ impl ThemePreset {
192193
pub fn all() -> &'static [ThemePreset] {
193194
&[
194195
ThemePreset::Default,
196+
ThemePreset::Terminal,
195197
ThemePreset::PookiePink,
196198
ThemePreset::Spotify,
197199
ThemePreset::Vesper,
@@ -209,6 +211,7 @@ impl ThemePreset {
209211
pub fn name(&self) -> &'static str {
210212
match self {
211213
ThemePreset::Default => "Default (Cyan)",
214+
ThemePreset::Terminal => "Terminal (ANSI)",
212215
ThemePreset::PookiePink => "Pookie Pink",
213216
ThemePreset::Spotify => "Spotify",
214217
ThemePreset::Vesper => "Vesper",
@@ -226,6 +229,7 @@ impl ThemePreset {
226229
pub fn from_name(name: &str) -> Self {
227230
match name {
228231
"Default (Cyan)" => ThemePreset::Default,
232+
"Terminal (ANSI)" => ThemePreset::Terminal,
229233
"Pookie Pink" => ThemePreset::PookiePink,
230234
"Spotify" => ThemePreset::Spotify,
231235
"Vesper" => ThemePreset::Vesper,
@@ -262,6 +266,29 @@ impl ThemePreset {
262266
pub fn to_theme(self) -> Theme {
263267
match self {
264268
ThemePreset::Default => Theme::default(),
269+
// Deliberately uses named ANSI colors (unlike the RGB rationale in
270+
// Theme::default) so terminal-palette tools like pywal restyle the UI
271+
// live, without restarting spotatui.
272+
ThemePreset::Terminal => Theme {
273+
analysis_bar: Color::Cyan,
274+
analysis_bar_text: Color::Reset,
275+
active: Color::Cyan,
276+
banner: Color::Cyan,
277+
error_border: Color::Red,
278+
error_text: Color::LightRed,
279+
hint: Color::Yellow,
280+
hovered: Color::Magenta,
281+
inactive: Color::DarkGray,
282+
playbar_background: Color::Reset,
283+
playbar_progress: Color::Cyan,
284+
playbar_progress_text: Color::Reset,
285+
playbar_text: Color::Reset,
286+
selected: Color::Cyan,
287+
text: Color::Reset,
288+
background: Color::Reset,
289+
header: Color::Reset,
290+
highlighted_lyrics: Color::Cyan,
291+
},
265292
ThemePreset::PookiePink => Theme {
266293
analysis_bar: Color::Rgb(255, 255, 255), // White
267294
analysis_bar_text: Color::Rgb(165, 30, 100), // Dark pink
@@ -1956,6 +1983,35 @@ mod tests {
19561983
);
19571984
}
19581985

1986+
#[test]
1987+
fn terminal_preset_colors_round_trip_through_config() {
1988+
use super::{color_to_string, parse_theme_item, ThemePreset};
1989+
1990+
let theme = ThemePreset::Terminal.to_theme();
1991+
for color in [
1992+
theme.analysis_bar,
1993+
theme.analysis_bar_text,
1994+
theme.active,
1995+
theme.banner,
1996+
theme.error_border,
1997+
theme.error_text,
1998+
theme.hint,
1999+
theme.hovered,
2000+
theme.inactive,
2001+
theme.playbar_background,
2002+
theme.playbar_progress,
2003+
theme.playbar_progress_text,
2004+
theme.playbar_text,
2005+
theme.selected,
2006+
theme.text,
2007+
theme.background,
2008+
theme.header,
2009+
theme.highlighted_lyrics,
2010+
] {
2011+
assert_eq!(parse_theme_item(&color_to_string(color)).unwrap(), color);
2012+
}
2013+
}
2014+
19592015
#[test]
19602016
fn test_reserved_key() {
19612017
use super::check_reserved_keys;

0 commit comments

Comments
 (0)