use ratatui::style::Color;
pub struct Gradient {
pub start: (u8, u8, u8),
pub mid: (u8, u8, u8),
pub end: (u8, u8, u8),
}
pub struct Theme {
pub name: &'static str,
pub main_bg: Color,
pub main_fg: Color,
pub title: Color,
pub hi_fg: Color,
pub selected_bg: Color,
pub selected_fg: Color,
pub inactive_fg: Color,
pub graph_text: Color,
pub meter_bg: Color,
pub proc_misc: Color,
pub div_line: Color,
pub session_id: Color,
pub status_fg: Color,
pub warning_fg: Color,
pub cpu_box: Color,
pub mem_box: Color,
pub net_box: Color,
pub proc_box: Color,
pub cpu_grad: Gradient,
pub proc_grad: Gradient,
pub used_grad: Gradient,
pub free_grad: Gradient,
pub cached_grad: Gradient,
}
impl Default for Theme {
fn default() -> Self {
Self::btop()
}
}
pub const THEME_NAMES: &[&str] = &[
"btop",
"dracula",
"catppuccin",
"tokyo-night",
"gruvbox",
"nord",
"light",
"white",
"high-contrast",
"protanopia",
"deuteranopia",
"tritanopia",
];
impl Theme {
pub fn by_name(name: &str) -> Option<Self> {
match name {
"btop" => Some(Self::btop()),
"dracula" => Some(Self::dracula()),
"catppuccin" => Some(Self::catppuccin()),
"tokyo-night" => Some(Self::tokyo_night()),
"gruvbox" => Some(Self::gruvbox()),
"nord" => Some(Self::nord()),
"light" => Some(Self::light()),
"white" => Some(Self::white()),
"high-contrast" => Some(Self::high_contrast()),
"protanopia" => Some(Self::protanopia()),
"deuteranopia" => Some(Self::deuteranopia()),
"tritanopia" => Some(Self::tritanopia()),
_ => None,
}
}
pub fn btop() -> Self {
Self {
name: "btop",
main_bg: Color::Rgb(25, 25, 25),
main_fg: Color::Rgb(204, 204, 204),
title: Color::Rgb(238, 238, 238),
hi_fg: Color::Rgb(181, 64, 64),
selected_bg: Color::Rgb(106, 47, 47),
selected_fg: Color::Rgb(238, 238, 238),
inactive_fg: Color::Rgb(64, 64, 64),
graph_text: Color::Rgb(96, 96, 96),
meter_bg: Color::Rgb(64, 64, 64),
proc_misc: Color::Rgb(13, 231, 86),
div_line: Color::Rgb(48, 48, 48),
session_id: Color::Rgb(176, 160, 112),
status_fg: Color::Rgb(220, 76, 76),
warning_fg: Color::Rgb(220, 160, 50),
cpu_box: Color::Rgb(85, 109, 89),
mem_box: Color::Rgb(108, 108, 75),
net_box: Color::Rgb(92, 88, 141),
proc_box: Color::Rgb(128, 82, 82),
cpu_grad: Gradient {
start: (119, 202, 155),
mid: (203, 192, 108),
end: (220, 76, 76),
},
proc_grad: Gradient {
start: (128, 208, 163),
mid: (220, 209, 121),
end: (212, 84, 84),
},
used_grad: Gradient {
start: (89, 43, 38),
mid: (217, 98, 109),
end: (255, 71, 105),
},
free_grad: Gradient {
start: (56, 79, 33),
mid: (181, 230, 133),
end: (220, 255, 133),
},
cached_grad: Gradient {
start: (22, 51, 80),
mid: (116, 230, 252),
end: (38, 197, 255),
},
}
}
pub fn dracula() -> Self {
Self {
name: "dracula",
main_bg: Color::Rgb(40, 42, 54),
main_fg: Color::Rgb(248, 248, 242),
title: Color::Rgb(248, 248, 242),
hi_fg: Color::Rgb(255, 121, 198),
selected_bg: Color::Rgb(68, 71, 90),
selected_fg: Color::Rgb(248, 248, 242),
inactive_fg: Color::Rgb(98, 114, 164),
graph_text: Color::Rgb(98, 114, 164),
meter_bg: Color::Rgb(68, 71, 90),
proc_misc: Color::Rgb(80, 250, 123),
div_line: Color::Rgb(68, 71, 90),
session_id: Color::Rgb(241, 250, 140),
status_fg: Color::Rgb(255, 85, 85),
warning_fg: Color::Rgb(241, 250, 140),
cpu_box: Color::Rgb(139, 233, 253),
mem_box: Color::Rgb(189, 147, 249),
net_box: Color::Rgb(255, 121, 198),
proc_box: Color::Rgb(255, 85, 85),
cpu_grad: Gradient {
start: (80, 250, 123),
mid: (241, 250, 140),
end: (255, 85, 85),
},
proc_grad: Gradient {
start: (80, 250, 123),
mid: (241, 250, 140),
end: (255, 85, 85),
},
used_grad: Gradient {
start: (68, 71, 90),
mid: (255, 121, 198),
end: (255, 85, 85),
},
free_grad: Gradient {
start: (40, 42, 54),
mid: (80, 250, 123),
end: (139, 233, 253),
},
cached_grad: Gradient {
start: (40, 42, 54),
mid: (139, 233, 253),
end: (189, 147, 249),
},
}
}
pub fn catppuccin() -> Self {
Self {
name: "catppuccin",
main_bg: Color::Rgb(30, 30, 46),
main_fg: Color::Rgb(205, 214, 244),
title: Color::Rgb(205, 214, 244),
hi_fg: Color::Rgb(243, 139, 168),
selected_bg: Color::Rgb(49, 50, 68),
selected_fg: Color::Rgb(205, 214, 244),
inactive_fg: Color::Rgb(108, 112, 134),
graph_text: Color::Rgb(147, 153, 178),
meter_bg: Color::Rgb(49, 50, 68),
proc_misc: Color::Rgb(166, 227, 161),
div_line: Color::Rgb(69, 71, 90),
session_id: Color::Rgb(249, 226, 175),
status_fg: Color::Rgb(243, 139, 168),
warning_fg: Color::Rgb(249, 226, 175),
cpu_box: Color::Rgb(137, 180, 250),
mem_box: Color::Rgb(203, 166, 247),
net_box: Color::Rgb(245, 194, 231),
proc_box: Color::Rgb(242, 205, 205),
cpu_grad: Gradient {
start: (166, 227, 161),
mid: (249, 226, 175),
end: (243, 139, 168),
},
proc_grad: Gradient {
start: (148, 226, 213),
mid: (249, 226, 175),
end: (243, 139, 168),
},
used_grad: Gradient {
start: (49, 50, 68),
mid: (245, 194, 231),
end: (243, 139, 168),
},
free_grad: Gradient {
start: (30, 30, 46),
mid: (166, 227, 161),
end: (148, 226, 213),
},
cached_grad: Gradient {
start: (30, 30, 46),
mid: (137, 180, 250),
end: (203, 166, 247),
},
}
}
pub fn tokyo_night() -> Self {
Self {
name: "tokyo-night",
main_bg: Color::Rgb(26, 27, 38), main_fg: Color::Rgb(169, 177, 214), title: Color::Rgb(192, 202, 245), hi_fg: Color::Rgb(247, 118, 142), selected_bg: Color::Rgb(41, 46, 66), selected_fg: Color::Rgb(192, 202, 245), inactive_fg: Color::Rgb(65, 72, 104), graph_text: Color::Rgb(86, 95, 137), meter_bg: Color::Rgb(59, 66, 97), proc_misc: Color::Rgb(158, 206, 106), div_line: Color::Rgb(26, 27, 38), session_id: Color::Rgb(224, 175, 104), status_fg: Color::Rgb(247, 118, 142), warning_fg: Color::Rgb(224, 175, 104), cpu_box: Color::Rgb(125, 207, 255), mem_box: Color::Rgb(187, 154, 247), net_box: Color::Rgb(247, 118, 142), proc_box: Color::Rgb(255, 158, 100), cpu_grad: Gradient {
start: (158, 206, 106),
mid: (224, 175, 104),
end: (247, 118, 142),
},
proc_grad: Gradient {
start: (115, 218, 202),
mid: (224, 175, 104),
end: (247, 118, 142),
},
used_grad: Gradient {
start: (41, 46, 66),
mid: (255, 158, 100),
end: (247, 118, 142),
},
free_grad: Gradient {
start: (26, 27, 38),
mid: (158, 206, 106),
end: (115, 218, 202),
},
cached_grad: Gradient {
start: (26, 27, 38),
mid: (125, 207, 255),
end: (187, 154, 247),
},
}
}
pub fn gruvbox() -> Self {
Self {
name: "gruvbox",
main_bg: Color::Rgb(40, 40, 40), main_fg: Color::Rgb(235, 219, 178), title: Color::Rgb(251, 241, 199), hi_fg: Color::Rgb(251, 73, 52), selected_bg: Color::Rgb(80, 73, 69), selected_fg: Color::Rgb(251, 241, 199), inactive_fg: Color::Rgb(124, 111, 100), graph_text: Color::Rgb(168, 153, 132), meter_bg: Color::Rgb(60, 56, 54), proc_misc: Color::Rgb(184, 187, 38), div_line: Color::Rgb(50, 48, 47), session_id: Color::Rgb(250, 189, 47), status_fg: Color::Rgb(251, 73, 52), warning_fg: Color::Rgb(250, 189, 47), cpu_box: Color::Rgb(131, 165, 152), mem_box: Color::Rgb(211, 134, 155), net_box: Color::Rgb(254, 128, 25), proc_box: Color::Rgb(251, 73, 52), cpu_grad: Gradient {
start: (184, 187, 38), mid: (250, 189, 47), end: (251, 73, 52), },
proc_grad: Gradient {
start: (142, 192, 124), mid: (250, 189, 47), end: (251, 73, 52), },
used_grad: Gradient {
start: (60, 56, 54), mid: (254, 128, 25), end: (251, 73, 52), },
free_grad: Gradient {
start: (40, 40, 40), mid: (184, 187, 38), end: (142, 192, 124), },
cached_grad: Gradient {
start: (40, 40, 40), mid: (131, 165, 152), end: (211, 134, 155), },
}
}
pub fn nord() -> Self {
Self {
name: "nord",
main_bg: Color::Rgb(46, 52, 64), main_fg: Color::Rgb(216, 222, 233), title: Color::Rgb(236, 239, 244), hi_fg: Color::Rgb(191, 97, 106), selected_bg: Color::Rgb(67, 76, 94), selected_fg: Color::Rgb(236, 239, 244), inactive_fg: Color::Rgb(76, 86, 106), graph_text: Color::Rgb(76, 86, 106), meter_bg: Color::Rgb(59, 66, 82), proc_misc: Color::Rgb(163, 190, 140), div_line: Color::Rgb(46, 52, 64), session_id: Color::Rgb(235, 203, 139), status_fg: Color::Rgb(191, 97, 106), warning_fg: Color::Rgb(235, 203, 139), cpu_box: Color::Rgb(136, 192, 208), mem_box: Color::Rgb(180, 142, 173), net_box: Color::Rgb(208, 135, 112), proc_box: Color::Rgb(191, 97, 106), cpu_grad: Gradient {
start: (163, 190, 140),
mid: (235, 203, 139),
end: (191, 97, 106),
},
proc_grad: Gradient {
start: (143, 188, 187),
mid: (235, 203, 139),
end: (191, 97, 106),
},
used_grad: Gradient {
start: (59, 66, 82),
mid: (208, 135, 112),
end: (191, 97, 106),
},
free_grad: Gradient {
start: (46, 52, 64),
mid: (163, 190, 140),
end: (143, 188, 187),
},
cached_grad: Gradient {
start: (46, 52, 64),
mid: (136, 192, 208),
end: (180, 142, 173),
},
}
}
pub fn light() -> Self {
Self {
name: "light",
main_bg: Color::Rgb(253, 246, 227), main_fg: Color::Rgb(88, 110, 117), title: Color::Rgb(7, 54, 66), hi_fg: Color::Rgb(203, 75, 22), selected_bg: Color::Rgb(238, 232, 213), selected_fg: Color::Rgb(7, 54, 66), inactive_fg: Color::Rgb(147, 161, 161), graph_text: Color::Rgb(131, 148, 150), meter_bg: Color::Rgb(238, 232, 213), proc_misc: Color::Rgb(133, 153, 0), div_line: Color::Rgb(238, 232, 213), session_id: Color::Rgb(181, 137, 0), status_fg: Color::Rgb(220, 50, 47), warning_fg: Color::Rgb(203, 75, 22), cpu_box: Color::Rgb(38, 139, 210), mem_box: Color::Rgb(108, 113, 196), net_box: Color::Rgb(42, 161, 152), proc_box: Color::Rgb(220, 50, 47), cpu_grad: Gradient {
start: (133, 153, 0), mid: (181, 137, 0), end: (220, 50, 47), },
proc_grad: Gradient {
start: (42, 161, 152), mid: (181, 137, 0), end: (220, 50, 47), },
used_grad: Gradient {
start: (238, 232, 213), mid: (203, 75, 22), end: (220, 50, 47), },
free_grad: Gradient {
start: (253, 246, 227), mid: (133, 153, 0), end: (42, 161, 152), },
cached_grad: Gradient {
start: (253, 246, 227), mid: (38, 139, 210), end: (108, 113, 196), },
}
}
pub fn white() -> Self {
Self {
name: "white",
main_bg: Color::Rgb(255, 255, 255), main_fg: Color::Rgb(31, 35, 40), title: Color::Rgb(0, 0, 0), hi_fg: Color::Rgb(207, 34, 46), selected_bg: Color::Rgb(221, 244, 255), selected_fg: Color::Rgb(9, 105, 218), inactive_fg: Color::Rgb(140, 149, 159), graph_text: Color::Rgb(101, 109, 118), meter_bg: Color::Rgb(234, 238, 242), proc_misc: Color::Rgb(26, 127, 55), div_line: Color::Rgb(208, 215, 222), session_id: Color::Rgb(154, 103, 0), status_fg: Color::Rgb(207, 34, 46), warning_fg: Color::Rgb(191, 135, 0), cpu_box: Color::Rgb(9, 105, 218), mem_box: Color::Rgb(130, 80, 223), net_box: Color::Rgb(26, 127, 55), proc_box: Color::Rgb(207, 34, 46), cpu_grad: Gradient {
start: (26, 127, 55), mid: (191, 135, 0), end: (207, 34, 46), },
proc_grad: Gradient {
start: (9, 105, 218), mid: (191, 135, 0), end: (207, 34, 46), },
used_grad: Gradient {
start: (234, 238, 242), mid: (191, 135, 0), end: (207, 34, 46), },
free_grad: Gradient {
start: (255, 255, 255), mid: (26, 127, 55), end: (9, 105, 218), },
cached_grad: Gradient {
start: (255, 255, 255), mid: (9, 105, 218), end: (130, 80, 223), },
}
}
pub fn high_contrast() -> Self {
Self {
name: "high-contrast",
main_bg: Color::Rgb(0, 0, 0),
main_fg: Color::Rgb(255, 255, 255),
title: Color::Rgb(255, 255, 255),
hi_fg: Color::Rgb(255, 255, 0),
selected_bg: Color::Rgb(255, 255, 0),
selected_fg: Color::Rgb(0, 0, 0),
inactive_fg: Color::Rgb(128, 128, 128),
graph_text: Color::Rgb(192, 192, 192),
meter_bg: Color::Rgb(64, 64, 64),
proc_misc: Color::Rgb(0, 255, 255),
div_line: Color::Rgb(96, 96, 96),
session_id: Color::Rgb(255, 255, 0),
status_fg: Color::Rgb(255, 255, 0),
warning_fg: Color::Rgb(255, 255, 0),
cpu_box: Color::Rgb(255, 255, 255),
mem_box: Color::Rgb(255, 255, 255),
net_box: Color::Rgb(255, 255, 255),
proc_box: Color::Rgb(255, 255, 255),
cpu_grad: Gradient {
start: (0, 255, 255),
mid: (255, 255, 255),
end: (255, 255, 0),
},
proc_grad: Gradient {
start: (0, 255, 255),
mid: (255, 255, 255),
end: (255, 255, 0),
},
used_grad: Gradient {
start: (32, 32, 32),
mid: (192, 192, 192),
end: (255, 255, 0),
},
free_grad: Gradient {
start: (32, 32, 32),
mid: (192, 192, 192),
end: (0, 255, 255),
},
cached_grad: Gradient {
start: (32, 32, 32),
mid: (128, 128, 255),
end: (255, 255, 255),
},
}
}
pub fn protanopia() -> Self {
Self {
name: "protanopia",
main_bg: Color::Rgb(20, 20, 32),
main_fg: Color::Rgb(220, 220, 220),
title: Color::Rgb(255, 255, 255),
hi_fg: Color::Rgb(254, 97, 0), selected_bg: Color::Rgb(40, 40, 60),
selected_fg: Color::Rgb(255, 255, 255),
inactive_fg: Color::Rgb(96, 96, 112),
graph_text: Color::Rgb(140, 140, 160),
meter_bg: Color::Rgb(48, 48, 64),
proc_misc: Color::Rgb(100, 143, 255), div_line: Color::Rgb(48, 48, 64),
session_id: Color::Rgb(255, 176, 0), status_fg: Color::Rgb(254, 97, 0), warning_fg: Color::Rgb(255, 176, 0), cpu_box: Color::Rgb(100, 143, 255),
mem_box: Color::Rgb(120, 94, 240),
net_box: Color::Rgb(220, 38, 127),
proc_box: Color::Rgb(254, 97, 0),
cpu_grad: Gradient {
start: (100, 143, 255),
mid: (255, 176, 0),
end: (254, 97, 0),
},
proc_grad: Gradient {
start: (100, 143, 255),
mid: (255, 176, 0),
end: (254, 97, 0),
},
used_grad: Gradient {
start: (40, 40, 60),
mid: (220, 38, 127),
end: (254, 97, 0),
},
free_grad: Gradient {
start: (20, 20, 40),
mid: (100, 143, 255),
end: (255, 176, 0),
},
cached_grad: Gradient {
start: (20, 20, 40),
mid: (120, 94, 240),
end: (100, 143, 255),
},
}
}
pub fn deuteranopia() -> Self {
Self {
name: "deuteranopia",
main_bg: Color::Rgb(18, 24, 40),
main_fg: Color::Rgb(222, 222, 230),
title: Color::Rgb(255, 255, 255),
hi_fg: Color::Rgb(255, 194, 10), selected_bg: Color::Rgb(30, 40, 70),
selected_fg: Color::Rgb(255, 255, 255),
inactive_fg: Color::Rgb(100, 108, 130),
graph_text: Color::Rgb(148, 156, 178),
meter_bg: Color::Rgb(42, 52, 82),
proc_misc: Color::Rgb(26, 133, 255), div_line: Color::Rgb(42, 52, 82),
session_id: Color::Rgb(255, 194, 10), status_fg: Color::Rgb(255, 102, 0), warning_fg: Color::Rgb(255, 194, 10),
cpu_box: Color::Rgb(26, 133, 255),
mem_box: Color::Rgb(156, 106, 222),
net_box: Color::Rgb(255, 194, 10),
proc_box: Color::Rgb(255, 102, 0),
cpu_grad: Gradient {
start: (26, 133, 255),
mid: (255, 194, 10),
end: (255, 102, 0),
},
proc_grad: Gradient {
start: (26, 133, 255),
mid: (255, 194, 10),
end: (255, 102, 0),
},
used_grad: Gradient {
start: (30, 40, 70),
mid: (255, 194, 10),
end: (255, 102, 0),
},
free_grad: Gradient {
start: (18, 24, 48),
mid: (26, 133, 255),
end: (180, 210, 255),
},
cached_grad: Gradient {
start: (18, 24, 48),
mid: (156, 106, 222),
end: (26, 133, 255),
},
}
}
pub fn tritanopia() -> Self {
Self {
name: "tritanopia",
main_bg: Color::Rgb(24, 20, 22),
main_fg: Color::Rgb(224, 224, 224),
title: Color::Rgb(255, 255, 255),
hi_fg: Color::Rgb(220, 50, 47), selected_bg: Color::Rgb(64, 32, 40),
selected_fg: Color::Rgb(255, 255, 255),
inactive_fg: Color::Rgb(120, 104, 108),
graph_text: Color::Rgb(168, 152, 156),
meter_bg: Color::Rgb(60, 40, 48),
proc_misc: Color::Rgb(64, 196, 208), div_line: Color::Rgb(48, 32, 38),
session_id: Color::Rgb(255, 140, 144), status_fg: Color::Rgb(220, 50, 47),
warning_fg: Color::Rgb(255, 140, 144),
cpu_box: Color::Rgb(64, 196, 208),
mem_box: Color::Rgb(198, 120, 221),
net_box: Color::Rgb(220, 50, 47),
proc_box: Color::Rgb(255, 140, 144),
cpu_grad: Gradient {
start: (64, 196, 208),
mid: (255, 140, 144),
end: (220, 50, 47),
},
proc_grad: Gradient {
start: (64, 196, 208),
mid: (255, 140, 144),
end: (220, 50, 47),
},
used_grad: Gradient {
start: (40, 24, 28),
mid: (255, 140, 144),
end: (220, 50, 47),
},
free_grad: Gradient {
start: (20, 28, 32),
mid: (64, 196, 208),
end: (180, 232, 240),
},
cached_grad: Gradient {
start: (20, 28, 32),
mid: (198, 120, 221),
end: (64, 196, 208),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_presets_load() {
for name in THEME_NAMES {
assert!(Theme::by_name(name).is_some(), "theme '{}' not found", name);
}
}
#[test]
fn unknown_returns_none() {
assert!(Theme::by_name("nonexistent").is_none());
}
#[test]
fn default_is_btop() {
let t = Theme::default();
assert_eq!(t.name, "btop");
}
}