use std::sync::atomic::{AtomicU8, Ordering};
use ratatui::style::{Color, Modifier, Style};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Tier {
Full,
Compatible,
}
impl Tier {
fn as_code(self) -> u8 {
match self {
Tier::Full => 1,
Tier::Compatible => 2,
}
}
fn from_code(code: u8) -> Option<Tier> {
match code {
1 => Some(Tier::Full),
2 => Some(Tier::Compatible),
_ => None,
}
}
}
static TIER: AtomicU8 = AtomicU8::new(0);
pub(crate) fn detect() -> Tier {
match std::env::var("COLORTERM")
.unwrap_or_default()
.to_lowercase()
.as_str()
{
"truecolor" | "24bit" => Tier::Full,
_ => Tier::Compatible,
}
}
pub(crate) fn init(override_tier: Option<Tier>) {
set_tier(override_tier.unwrap_or_else(detect));
}
pub(crate) fn set_tier(tier: Tier) {
TIER.store(tier.as_code(), Ordering::Relaxed);
}
#[inline]
pub(crate) fn tier() -> Tier {
Tier::from_code(TIER.load(Ordering::Relaxed)).unwrap_or_else(detect)
}
#[inline]
fn pick(full: Color, compatible: Color) -> Color {
match tier() {
Tier::Full => full,
Tier::Compatible => compatible,
}
}
#[inline]
pub(crate) fn bg() -> Color {
pick(Color::Rgb(30, 30, 46), Color::Indexed(235))
}
#[inline]
pub(crate) fn bg_sunken() -> Color {
pick(Color::Rgb(17, 17, 27), Color::Indexed(233))
}
#[inline]
pub(crate) fn bg_hover() -> Color {
pick(Color::Rgb(40, 40, 56), Color::Indexed(236))
}
#[inline]
pub(crate) fn line_color() -> Color {
pick(Color::Rgb(49, 50, 68), Color::Indexed(238))
}
#[inline]
pub(crate) fn line_strong_color() -> Color {
pick(Color::Rgb(69, 71, 90), Color::Indexed(240))
}
#[inline]
pub(crate) fn text_color() -> Color {
pick(Color::Rgb(205, 214, 244), Color::Indexed(189))
}
#[inline]
pub(crate) fn text_dim_color() -> Color {
pick(Color::Rgb(166, 173, 200), Color::Indexed(145))
}
#[inline]
pub(crate) fn text_faint_color() -> Color {
pick(Color::Rgb(127, 132, 156), Color::Indexed(102))
}
#[inline]
pub(crate) fn accent_color() -> Color {
pick(Color::Rgb(67, 171, 229), Color::Indexed(75))
}
#[inline]
pub(crate) fn accent_2_color() -> Color {
pick(Color::Rgb(217, 119, 87), Color::Indexed(173))
}
#[inline]
pub(crate) fn success_color() -> Color {
pick(Color::Rgb(166, 227, 161), Color::Indexed(151))
}
#[inline]
pub(crate) fn warning_color() -> Color {
pick(Color::Rgb(249, 226, 175), Color::Indexed(223))
}
#[inline]
pub(crate) fn danger_color() -> Color {
pick(Color::Rgb(243, 139, 168), Color::Indexed(211))
}
#[inline]
pub(crate) fn info_color() -> Color {
pick(Color::Rgb(116, 199, 236), Color::Indexed(117))
}
#[inline]
pub(crate) fn bg_danger_color() -> Color {
pick(Color::Rgb(75, 35, 44), Color::Indexed(52))
}
#[inline]
pub(crate) fn bg_warning_color() -> Color {
pick(Color::Rgb(74, 60, 33), Color::Indexed(58))
}
pub(crate) fn blend_over(beneath: Color, over: Color, alpha: f64) -> Color {
let (Color::Rgb(br, bg, bb), Color::Rgb(or, og, ob)) = (beneath, over) else {
return over;
};
if tier() != Tier::Full {
return over;
}
let a = alpha.clamp(0.0, 1.0);
let mix = |o: u8, b: u8| -> u8 { (a * f64::from(o) + (1.0 - a) * f64::from(b)).round() as u8 };
Color::Rgb(mix(or, br), mix(og, bg), mix(ob, bb))
}
pub(crate) fn toggle_on() -> &'static str {
match tier() {
Tier::Full => "─●",
Tier::Compatible => "[on]",
}
}
pub(crate) fn toggle_off() -> &'static str {
match tier() {
Tier::Full => "○─",
Tier::Compatible => "[off]",
}
}
pub(crate) fn edit_glyph() -> &'static str {
"✎"
}
pub(crate) fn base() -> Style {
Style::default().fg(text_color()).bg(bg())
}
pub(crate) fn body() -> Style {
Style::default().fg(text_color())
}
pub(crate) fn line_strong() -> Style {
Style::default().fg(line_strong_color())
}
pub(crate) fn dim() -> Style {
Style::default().fg(text_dim_color())
}
pub(crate) fn faint() -> Style {
Style::default().fg(text_faint_color())
}
pub(crate) fn label() -> Style {
Style::default()
.fg(text_dim_color())
.add_modifier(Modifier::BOLD)
}
pub(crate) fn accent() -> Style {
Style::default().fg(accent_color())
}
pub(crate) fn orange() -> Style {
Style::default().fg(accent_2_color())
}
pub(crate) fn warning() -> Style {
Style::default().fg(warning_color())
}
pub(crate) fn danger() -> Style {
Style::default().fg(danger_color())
}
pub(crate) fn selected_row() -> Style {
Style::default().bg(bg_hover())
}
pub(crate) fn util_color(pct: f64) -> Color {
let pct = pct.clamp(0.0, 100.0);
if pct >= 80.0 {
danger_color()
} else if pct >= 60.0 {
warning_color()
} else {
text_dim_color()
}
}
pub(crate) fn info() -> Style {
Style::default().fg(info_color())
}
pub(crate) fn success() -> Style {
Style::default().fg(success_color())
}