redbear-power: runtime theme cycling with + key

Press + to cycle through all 6 themes (dark, light, high-contrast,
nord, gruvbox, solarized-dark) without restarting. Theme choice
shown via flash_toast. Fixed --theme CLI to accept all 6 modes.
This commit is contained in:
2026-07-07 02:40:54 +03:00
parent 75229b0422
commit b3a249e450
3 changed files with 30 additions and 4 deletions
@@ -147,6 +147,7 @@ pub struct App {
pub sched_stats: crate::sched::SchedStats,
pub load_avg: Option<(f64, f64, f64)>,
pub poll_ms: u64,
pub theme_idx: usize,
/// When true, render the Process tab as a tree (parents above
/// children, prefixed with `├─ ` / `└─ ` connector characters).
/// Toggled by the `T` hotkey. Sort modes are honored within
@@ -458,6 +459,7 @@ impl App {
}
}),
poll_ms: crate::app::POLL_MS,
theme_idx: 0,
process_tree: false,
show_full_cmdline: false,
folded: std::collections::BTreeSet::new(),
@@ -111,17 +111,18 @@ fn parse_args() -> Args {
"--theme" => {
if let Some(t) = iter.next() {
match t.as_str() {
"dark" | "light" | "high-contrast" => theme_mode = Some(t),
"dark" | "light" | "high-contrast" | "nord" | "gruvbox"
| "solarized-dark" => theme_mode = Some(t),
other => {
eprintln!(
"redbear-power: unknown theme \"{other}\"; use dark, light, or high-contrast"
"redbear-power: unknown theme \"{other}\"; use dark, light, high-contrast, nord, gruvbox, or solarized-dark"
);
std::process::exit(2);
}
}
} else {
eprintln!(
"redbear-power: --theme requires a mode (dark, light, high-contrast)"
"redbear-power: --theme requires a mode (dark, light, high-contrast, nord, gruvbox, solarized-dark)"
);
std::process::exit(2);
}
@@ -270,6 +271,14 @@ fn main() -> io::Result<()> {
.theme_mode
.as_deref()
.unwrap_or_else(|| cfg.theme.mode.as_str());
app.theme_idx = match theme_mode {
"light" => 1,
"high-contrast" => 2,
"nord" => 3,
"gruvbox" => 4,
"solarized-dark" => 5,
_ => 0,
};
app.theme = crate::theme::Theme::named(theme_mode);
app.refresh();
@@ -975,6 +984,20 @@ fn main() -> io::Result<()> {
"unfrozen"
});
}
Key::Char('+') => {
let themes: [(&str, fn() -> crate::theme::Theme); 6] = [
("dark", crate::theme::Theme::dark),
("light", crate::theme::Theme::light),
("high-contrast", crate::theme::Theme::high_contrast),
("nord", crate::theme::Theme::nord),
("gruvbox", crate::theme::Theme::gruvbox),
("solarized-dark", crate::theme::Theme::solarized_dark),
];
app.theme_idx = (app.theme_idx + 1) % themes.len();
let (name, ctor) = themes[app.theme_idx];
app.theme = ctor();
app.flash_toast(format!("theme: {name}"));
}
Key::Char('k') if app.current_tab == TabId::Process => {
if let Some(pid) = app.selected_pid() {
let comm = {
@@ -2065,7 +2065,7 @@ pub fn render_keybar<'a>(app: &'a App) -> Paragraph<'a> {
gov,
interval,
tab_hints.to_string(),
" T:tab ?:help e:expand f:freeze q:quit".to_string(),
" T:tab +:theme ?:help e:expand f:freeze q:quit".to_string(),
];
if app.frozen {
parts.push(" [FROZEN]".to_string());
@@ -2189,6 +2189,7 @@ INTERACTIVE CONTROLS:
[G] jump to bottom (vim-style)
[j/k] move selection down/up (vim-style)
[?] toggle this help overlay
[+] cycle color theme (6 themes)
MOUSE:
Wheel scroll the per-CPU selection up/down (over the table panel)