gpur 0.1.0

btop-style GPU monitor TUI — NVIDIA, AMD, Apple Silicon; Linux, macOS, Windows
//! Maps an hjkl-theme `Theme` onto the handful of styles gpur draws with.

use anyhow::Context;
use hjkl_theme::{Color, Theme};
use hjkl_theme_tui::ToRatatui;
use ratatui::style::{Color as RColor, Modifier, Style};
use std::path::Path;

pub struct UiTheme {
    pub fg: RColor,
    pub bg: RColor,
    pub border: Style,
    pub border_selected: Style,
    pub title: Style,
    pub dim: Style,
    pub gauge_util: Style,
    pub gauge_vram: Style,
    pub spark_util: Style,
    pub spark_power: Style,
    pub temp_ok: Style,
    pub temp_warn: Style,
    pub temp_crit: Style,
    pub accent: RColor,
}

pub fn load(path: Option<&Path>) -> anyhow::Result<UiTheme> {
    let theme = match path {
        Some(p) => hjkl_theme::loader::load_from_path(p)
            .with_context(|| format!("loading theme {}", p.display()))?,
        None => hjkl_theme::loader::default_theme(),
    };
    Ok(UiTheme::from_theme(&theme))
}

impl UiTheme {
    fn from_theme(t: &Theme) -> Self {
        // Palette names follow the built-in (Catppuccin-Mocha-ish) theme; every
        // lookup carries a hard fallback so any palette works.
        let fg =
            t.ui.foreground
                .unwrap_or_else(|| pal(t, &["text"], Color::rgb(0xcd, 0xd6, 0xf4)));
        let bg =
            t.ui.background
                .unwrap_or_else(|| pal(t, &["base"], Color::rgb(0x1e, 0x1e, 0x2e)));
        let accent = pal(t, &["mauve", "blue"], Color::rgb(0xcb, 0xa6, 0xf7));
        let green = pal(t, &["green"], Color::rgb(0xa6, 0xe3, 0xa1));
        let yellow = pal(t, &["yellow", "peach"], Color::rgb(0xf9, 0xe2, 0xaf));
        let red = pal(t, &["red"], Color::rgb(0xf3, 0x8b, 0xa8));
        let blue = pal(t, &["blue", "sky"], Color::rgb(0x89, 0xb4, 0xfa));
        let teal = pal(t, &["teal", "sky"], Color::rgb(0x94, 0xe2, 0xd5));
        let dim = pal(t, &["overlay0", "surface2"], Color::rgb(0x6c, 0x70, 0x86));

        Self {
            fg: fg.to_ratatui(),
            bg: bg.to_ratatui(),
            border: Style::new().fg(dim.to_ratatui()),
            border_selected: Style::new().fg(accent.to_ratatui()),
            title: Style::new()
                .fg(accent.to_ratatui())
                .add_modifier(Modifier::BOLD),
            dim: Style::new().fg(dim.to_ratatui()),
            gauge_util: Style::new().fg(green.to_ratatui()),
            gauge_vram: Style::new().fg(blue.to_ratatui()),
            spark_util: Style::new().fg(green.to_ratatui()),
            spark_power: Style::new().fg(teal.to_ratatui()),
            temp_ok: Style::new().fg(green.to_ratatui()),
            temp_warn: Style::new().fg(yellow.to_ratatui()),
            temp_crit: Style::new().fg(red.to_ratatui()),
            accent: accent.to_ratatui(),
        }
    }

    pub fn temp_style(&self, c: f64) -> Style {
        if c >= 90.0 {
            self.temp_crit
        } else if c >= 75.0 {
            self.temp_warn
        } else {
            self.temp_ok
        }
    }
}

fn pal(t: &Theme, names: &[&str], fallback: Color) -> Color {
    names
        .iter()
        .find_map(|n| t.palette.get(*n).copied())
        .unwrap_or(fallback)
}