use clap::builder::styling::{AnsiColor, Color, Effects, RgbColor, Style, Styles};
use cnt_core::Severity;
use std::sync::LazyLock;
struct Palette {
text: Color,
gray: Color,
selection: Color,
red: Color,
orange: Color,
yellow: Color,
olive: Color,
green: Color,
cyan: Color,
blue: Color,
purple: Color,
}
const fn rgb(r: u8, g: u8, b: u8) -> Color {
Color::Rgb(RgbColor(r, g, b))
}
const fn ansi(color: AnsiColor) -> Color {
Color::Ansi(color)
}
const DEFAULT_PALETTE: Palette = Palette {
text: rgb(0xBC, 0xBE, 0xC4), gray: rgb(0x7A, 0x7E, 0x85), selection: rgb(0x21, 0x42, 0x83), red: rgb(0xF7, 0x54, 0x64), orange: rgb(0xCF, 0x8E, 0x6D), yellow: rgb(0xE0, 0xBB, 0x65), olive: rgb(0xB3, 0xAE, 0x60), green: rgb(0x6A, 0xAB, 0x73), cyan: rgb(0x2A, 0xAC, 0xB8), blue: rgb(0x56, 0xA8, 0xF5), purple: rgb(0xC7, 0x7D, 0xBA), };
const FALLBACK_PALETTE: Palette = Palette {
text: ansi(AnsiColor::BrightWhite),
gray: ansi(AnsiColor::BrightBlack),
selection: ansi(AnsiColor::Black),
red: ansi(AnsiColor::Red),
orange: ansi(AnsiColor::Yellow),
yellow: ansi(AnsiColor::Yellow),
olive: ansi(AnsiColor::BrightYellow),
green: ansi(AnsiColor::Green),
cyan: ansi(AnsiColor::Cyan),
blue: ansi(AnsiColor::BrightBlue),
purple: ansi(AnsiColor::Magenta),
};
pub struct Theme {
pub clap: Styles,
pub hint: Style,
pub path: Style,
pub header: Style,
pub warn: Style,
pub error: Style,
severity: [Style; 5],
pub table: Style,
pub row_highlight: Style,
pub key: Style,
}
impl Theme {
const fn new(p: &Palette) -> Self {
const BOLD_UNDER: Effects = Effects::BOLD.insert(Effects::UNDERLINE);
Theme {
clap: Styles::styled()
.header(fg(p.orange).effects(BOLD_UNDER))
.usage(fg(p.olive).bold())
.literal(fg(p.blue).bold())
.placeholder(fg(p.purple))
.valid(fg(p.green).bold())
.invalid(fg(p.red).bold())
.error(fg(p.red).effects(BOLD_UNDER)),
hint: fg(p.gray),
path: fg(p.cyan),
header: Style::new().bold(),
warn: fg(p.yellow).bold(),
error: fg(p.red).bold(),
severity: [
fg(p.red).bold(),
fg(p.yellow),
fg(p.green),
fg(p.blue),
fg(p.purple),
],
table: fg(p.text),
row_highlight: Style::new().bg_color(Some(p.selection)).bold(),
key: fg(p.blue).bold(),
}
}
pub fn severity(&self, severity: Severity) -> Style {
self.severity[severity as usize]
}
}
const fn fg(color: Color) -> Style {
Style::new().fg_color(Some(color))
}
const DEFAULT_THEME: Theme = Theme::new(&DEFAULT_PALETTE);
const FALLBACK_THEME: Theme = Theme::new(&FALLBACK_PALETTE);
pub fn theme() -> &'static Theme {
static THEME: LazyLock<&Theme> = LazyLock::new(|| {
if truecolor() {
&DEFAULT_THEME
} else {
&FALLBACK_THEME
}
});
*THEME
}
fn truecolor() -> bool {
if let Ok(colorterm) = std::env::var("COLORTERM")
&& (colorterm.contains("truecolor") || colorterm.contains("24bit"))
{
return true;
}
std::env::var("TERM").is_ok_and(|term| {
term.contains("truecolor") || term.contains("direct") || term.contains("24bit")
})
}