use ratatui::style::{Color, Style};
use serde::de::{self, Deserializer, Visitor};
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter};
use tracing::warn;
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter, Display, Default)]
pub enum ThemeName {
#[strum(serialize = "Andromeda")]
Andromeda,
#[strum(serialize = "Aurora")]
Aurora,
#[strum(serialize = "Ayu Dark")]
AyuDark,
#[strum(serialize = "Bubblegum")]
Bubblegum,
#[strum(serialize = "Catppuccin Latte")]
CatppuccinLatte,
#[strum(serialize = "Catppuccin Mocha")]
#[default]
CatppuccinMocha,
#[strum(serialize = "Cyberpunk")]
Cyberpunk,
#[strum(serialize = "Deep Ocean")]
DeepOcean,
#[strum(serialize = "Deep Sky")]
DeepSky,
#[strum(serialize = "Diamond")]
Diamond,
#[strum(serialize = "Gold")]
Gold,
#[strum(serialize = "Dracula")]
Dracula,
#[strum(serialize = "Everforest Dark")]
EverforestDark,
#[strum(serialize = "GitHub Dark")]
GitHubDark,
#[strum(serialize = "GitHub Light")]
GitHubLight,
#[strum(serialize = "Gruvbox Dark")]
GruvboxDark,
#[strum(serialize = "Gruvbox Light")]
GruvboxLight,
#[strum(serialize = "Inferno")]
Inferno,
#[strum(serialize = "Kanagawa")]
Kanagawa,
#[strum(serialize = "Material Ocean")]
MaterialOcean,
#[strum(serialize = "Matrix")]
Matrix,
#[strum(serialize = "Monokai")]
Monokai,
#[strum(serialize = "Neon")]
Neon,
#[strum(serialize = "Nightfox")]
Nightfox,
#[strum(serialize = "Nord")]
Nord,
#[strum(serialize = "One Dark")]
OneDark,
#[strum(serialize = "Obsidian Forge")]
ObsidianForge,
#[strum(serialize = "Oxocarbon")]
Oxocarbon,
#[strum(serialize = "Arctic Whiteout")]
ArcticWhiteout,
#[strum(serialize = "PaperColor Light")]
PaperColorLight,
#[strum(serialize = "Bioluminescent Reef")]
BioluminescentReef,
#[strum(serialize = "Black Hole")]
BlackHole,
#[strum(serialize = "Rainbow")]
Rainbow,
#[strum(serialize = "Rose Pine")]
RosePine,
#[strum(serialize = "Solarized Dark")]
SolarizedDark,
#[strum(serialize = "Solarized Light")]
SolarizedLight,
#[strum(serialize = "Synthwave '84")]
Synthwave84,
#[strum(serialize = "Tokyo Night")]
TokyoNight,
#[strum(serialize = "Vesper")]
Vesper,
#[strum(serialize = "Zenburn")]
Zenburn,
#[strum(serialize = "Sakura")]
Sakura,
}
impl ThemeName {
pub fn sorted_for_ui() -> Vec<Self> {
let mut themes: Vec<Self> = Self::iter().collect();
themes.sort_by_key(|theme| theme.to_string());
themes
}
}
impl Serialize for ThemeName {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = match self {
ThemeName::Andromeda => "andromeda",
ThemeName::Aurora => "aurora",
ThemeName::AyuDark => "ayu_dark",
ThemeName::Bubblegum => "bubblegum",
ThemeName::CatppuccinLatte => "catppuccin_latte",
ThemeName::CatppuccinMocha => "catppuccin_mocha",
ThemeName::Cyberpunk => "cyberpunk",
ThemeName::DeepOcean => "deep_ocean",
ThemeName::DeepSky => "deep_sky",
ThemeName::Diamond => "diamond",
ThemeName::Gold => "gold",
ThemeName::Dracula => "dracula",
ThemeName::EverforestDark => "everforest_dark",
ThemeName::GitHubDark => "github_dark",
ThemeName::GitHubLight => "github_light",
ThemeName::GruvboxDark => "gruvbox_dark",
ThemeName::GruvboxLight => "gruvbox_light",
ThemeName::Inferno => "inferno",
ThemeName::Kanagawa => "kanagawa",
ThemeName::MaterialOcean => "material_ocean",
ThemeName::Matrix => "matrix",
ThemeName::Monokai => "monokai",
ThemeName::Neon => "neon",
ThemeName::Nightfox => "nightfox",
ThemeName::Nord => "nord",
ThemeName::OneDark => "one_dark",
ThemeName::ObsidianForge => "obsidian_forge",
ThemeName::Oxocarbon => "oxocarbon",
ThemeName::ArcticWhiteout => "arctic_whiteout",
ThemeName::PaperColorLight => "papercolor_light",
ThemeName::BlackHole => "black_hole",
ThemeName::BioluminescentReef => "bioluminescent_reef",
ThemeName::Rainbow => "rainbow",
ThemeName::RosePine => "rose_pine",
ThemeName::SolarizedDark => "solarized_dark",
ThemeName::SolarizedLight => "solarized_light",
ThemeName::Synthwave84 => "synthwave_84",
ThemeName::TokyoNight => "tokyo_night",
ThemeName::Vesper => "vesper",
ThemeName::Zenburn => "zenburn",
ThemeName::Sakura => "sakura",
};
serializer.serialize_str(s)
}
}
impl<'de> Deserialize<'de> for ThemeName {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ThemeNameVisitor;
impl<'de> Visitor<'de> for ThemeNameVisitor {
type Value = ThemeName;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a theme name string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(parse_theme_name(v))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(parse_theme_name(&v))
}
fn visit_bool<E>(self, _v: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_i64<E>(self, _v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_u64<E>(self, _v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_f64<E>(self, _v: f64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ThemeName::default())
}
fn visit_seq<A>(self, _seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
Ok(ThemeName::default())
}
fn visit_map<A>(self, _map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
Ok(ThemeName::default())
}
fn visit_some<D2>(self, deserializer: D2) -> Result<Self::Value, D2::Error>
where
D2: Deserializer<'de>,
{
deserializer.deserialize_any(ThemeNameVisitor)
}
}
deserializer.deserialize_any(ThemeNameVisitor)
}
}
enum ThemeResolution {
Supported(ThemeName),
Deprecated {
replacement: ThemeName,
deprecated_alias: &'static str,
},
Unknown,
}
fn parse_theme_name(raw: &str) -> ThemeName {
match resolve_theme_name(raw) {
ThemeResolution::Supported(theme) => theme,
ThemeResolution::Deprecated {
replacement,
deprecated_alias,
} => {
warn!(
"Theme '{}' is deprecated; using '{}'.",
deprecated_alias, replacement
);
replacement
}
ThemeResolution::Unknown => {
warn!(
"Unknown theme '{}'; falling back to '{}'.",
raw,
ThemeName::default()
);
ThemeName::default()
}
}
}
fn resolve_theme_name(raw: &str) -> ThemeResolution {
let normalized = normalize_theme_name_key(raw);
if normalized.is_empty() {
return ThemeResolution::Unknown;
}
let supported = match normalized.as_str() {
"andromeda" => Some(ThemeName::Andromeda),
"aurora" => Some(ThemeName::Aurora),
"ayu_dark" => Some(ThemeName::AyuDark),
"bubblegum" => Some(ThemeName::Bubblegum),
"catppuccin_latte" => Some(ThemeName::CatppuccinLatte),
"catppuccin_mocha" => Some(ThemeName::CatppuccinMocha),
"cyberpunk" => Some(ThemeName::Cyberpunk),
"deep_ocean" => Some(ThemeName::DeepOcean),
"deep_sky" => Some(ThemeName::DeepSky),
"diamond" => Some(ThemeName::Diamond),
"gold" => Some(ThemeName::Gold),
"dracula" => Some(ThemeName::Dracula),
"everforest_dark" => Some(ThemeName::EverforestDark),
"github_dark" => Some(ThemeName::GitHubDark),
"github_light" => Some(ThemeName::GitHubLight),
"gruvbox_dark" => Some(ThemeName::GruvboxDark),
"gruvbox_light" => Some(ThemeName::GruvboxLight),
"inferno" => Some(ThemeName::Inferno),
"kanagawa" => Some(ThemeName::Kanagawa),
"material_ocean" => Some(ThemeName::MaterialOcean),
"matrix" => Some(ThemeName::Matrix),
"monokai" => Some(ThemeName::Monokai),
"neon" => Some(ThemeName::Neon),
"nightfox" => Some(ThemeName::Nightfox),
"nord" => Some(ThemeName::Nord),
"one_dark" => Some(ThemeName::OneDark),
"obsidian_forge" => Some(ThemeName::ObsidianForge),
"oxocarbon" => Some(ThemeName::Oxocarbon),
"arctic_whiteout" => Some(ThemeName::ArcticWhiteout),
"papercolor_light" => Some(ThemeName::PaperColorLight),
"black_hole" => Some(ThemeName::BlackHole),
"bioluminescent_reef" => Some(ThemeName::BioluminescentReef),
"rainbow" => Some(ThemeName::Rainbow),
"rose_pine" => Some(ThemeName::RosePine),
"solarized_dark" => Some(ThemeName::SolarizedDark),
"solarized_light" => Some(ThemeName::SolarizedLight),
"synthwave_84" => Some(ThemeName::Synthwave84),
"tokyo_night" => Some(ThemeName::TokyoNight),
"vesper" => Some(ThemeName::Vesper),
"zenburn" => Some(ThemeName::Zenburn),
"sakura" => Some(ThemeName::Sakura),
_ => None,
};
if let Some(theme) = supported {
return ThemeResolution::Supported(theme);
}
let deprecated = match normalized.as_str() {
"catppuccin" => Some(("catppuccin", ThemeName::CatppuccinMocha)),
"synthwave84" => Some(("synthwave84", ThemeName::Synthwave84)),
"tokyonight" => Some(("tokyonight", ThemeName::TokyoNight)),
"flowers" => Some(("flowers", ThemeName::Sakura)),
_ => None,
};
if let Some((alias, replacement)) = deprecated {
return ThemeResolution::Deprecated {
replacement,
deprecated_alias: alias,
};
}
ThemeResolution::Unknown
}
fn normalize_theme_name_key(input: &str) -> String {
input
.trim()
.to_lowercase()
.replace('\'', "")
.replace(['-', ' '], "_")
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeEffects {
pub local_enabled: bool,
pub flicker_hz: f32,
pub flicker_intensity: f32,
pub local_burst_duty: f32,
pub local_burst_hz: f32,
pub local_idle_intensity: f32,
pub local_burst_boost: f32,
pub wave_enabled: bool,
pub wave_hz: f32,
pub wave_intensity: f32,
pub wave_wavelength: f32,
pub wave_angle_degrees: f32,
pub wave_mode: WaveMode,
pub particle: ThemeParticleEffect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WaveMode {
Linear,
RadialOut,
#[allow(dead_code)]
RadialIn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParticleLayerMode {
None,
Background,
#[allow(dead_code)]
Foreground,
#[allow(dead_code)]
Both,
}
impl ParticleLayerMode {
pub fn has_background(self) -> bool {
matches!(self, Self::Background | Self::Both)
}
pub fn has_foreground(self) -> bool {
matches!(self, Self::Foreground | Self::Both)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParticleProfile {
None,
Sakura,
Matrix,
Diamond,
BioluminescentReef,
BlackHole,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeParticleEffect {
pub enabled: bool,
pub layer_mode: ParticleLayerMode,
pub profile: ParticleProfile,
pub density: f32,
pub speed: f32,
pub intensity: f32,
}
impl Default for ThemeParticleEffect {
fn default() -> Self {
Self {
enabled: false,
layer_mode: ParticleLayerMode::None,
profile: ParticleProfile::None,
density: 0.0,
speed: 0.0,
intensity: 0.0,
}
}
}
impl Default for ThemeEffects {
fn default() -> Self {
Self {
local_enabled: false,
flicker_hz: 0.0,
flicker_intensity: 0.0,
local_burst_duty: 1.0,
local_burst_hz: 0.0,
local_idle_intensity: 1.0,
local_burst_boost: 1.0,
wave_enabled: false,
wave_hz: 0.0,
wave_intensity: 0.0,
wave_wavelength: 0.0,
wave_angle_degrees: 0.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect::default(),
}
}
}
impl ThemeEffects {
pub fn enabled(&self) -> bool {
self.local_enabled || self.wave_enabled || self.particle.enabled
}
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeSemantic {
pub text: Color,
pub subtext0: Color,
pub subtext1: Color,
pub overlay0: Color,
pub surface0: Color,
pub surface1: Color,
pub surface2: Color,
pub border: Color,
pub white: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeHeatmap {
pub low: Color,
pub medium: Color,
pub high: Color,
pub empty: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeStream {
pub inflow: Color,
pub outflow: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeCategorical {
pub rosewater: Color,
pub flamingo: Color,
pub pink: Color,
pub mauve: Color,
pub red: Color,
pub maroon: Color,
pub peach: Color,
pub yellow: Color,
pub green: Color,
pub teal: Color,
pub sky: Color,
pub sapphire: Color,
pub blue: Color,
pub lavender: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeScale {
pub speed: [Color; 8],
pub ip_hash: [Color; 14],
pub heatmap: ThemeHeatmap,
pub stream: ThemeStream,
pub categorical: ThemeCategorical,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeStateSlots {
pub error: Color,
pub warning: Color,
pub success: Color,
pub info: Color,
pub selected: Color,
pub complete: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeMetricSlots {
pub download: Color,
pub upload: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemePeerSlots {
pub discovered: Color,
pub connected: Color,
pub disconnected: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeAccentSlots {
pub sky: Color,
pub teal: Color,
pub peach: Color,
pub sapphire: Color,
pub maroon: Color,
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeRoleSlots {
pub state: ThemeStateSlots,
pub metric: ThemeMetricSlots,
pub peer: ThemePeerSlots,
pub accent: ThemeAccentSlots,
}
pub fn color_to_rgb(color: Color) -> (u8, u8, u8) {
match color {
Color::Rgb(r, g, b) => (r, g, b),
Color::Reset => (255, 255, 255),
Color::DarkGray => (128, 128, 128),
Color::Red => (255, 0, 0),
Color::LightRed => (255, 102, 102),
Color::Green => (0, 255, 0),
Color::LightGreen => (102, 255, 102),
Color::Yellow => (255, 255, 0),
Color::LightYellow => (255, 255, 153),
Color::Blue => (0, 0, 255),
Color::LightBlue => (102, 102, 255),
Color::Magenta => (255, 0, 255),
Color::LightMagenta => (255, 102, 255),
Color::Cyan => (0, 255, 255),
Color::LightCyan => (102, 255, 255),
Color::Gray => (192, 192, 192),
Color::White => (255, 255, 255),
Color::Black => (0, 0, 0),
Color::Indexed(i) => (i, i, i),
}
}
pub fn blend_colors(c1: (u8, u8, u8), c2: (u8, u8, u8), ratio: f64) -> Color {
let r = (c1.0 as f64 * (1.0 - ratio) + c2.0 as f64 * ratio) as u8;
let g = (c1.1 as f64 * (1.0 - ratio) + c2.1 as f64 * ratio) as u8;
let b = (c1.2 as f64 * (1.0 - ratio) + c2.2 as f64 * ratio) as u8;
Color::Rgb(r, g, b)
}
#[derive(Debug, Clone, Copy)]
pub struct ThemeContext {
pub theme: Theme,
pub frame_time: f64,
}
impl ThemeContext {
pub fn new(theme: Theme, frame_time: f64) -> Self {
Self { theme, frame_time }
}
pub fn apply(&self, style: Style) -> Style {
style
}
pub fn state_error(&self) -> Color {
self.theme.role_slots().state.error
}
pub fn state_warning(&self) -> Color {
self.theme.role_slots().state.warning
}
pub fn state_success(&self) -> Color {
self.theme.role_slots().state.success
}
pub fn state_info(&self) -> Color {
self.theme.role_slots().state.info
}
pub fn state_selected(&self) -> Color {
self.theme.role_slots().state.selected
}
pub fn state_complete(&self) -> Color {
self.theme.role_slots().state.complete
}
pub fn metric_download(&self) -> Color {
self.theme.role_slots().metric.download
}
pub fn metric_upload(&self) -> Color {
self.theme.role_slots().metric.upload
}
pub fn peer_discovered(&self) -> Color {
self.theme.role_slots().peer.discovered
}
pub fn peer_connected(&self) -> Color {
self.theme.role_slots().peer.connected
}
pub fn peer_disconnected(&self) -> Color {
self.theme.role_slots().peer.disconnected
}
pub fn accent_sky(&self) -> Color {
self.theme.role_slots().accent.sky
}
pub fn accent_teal(&self) -> Color {
self.theme.role_slots().accent.teal
}
pub fn accent_peach(&self) -> Color {
self.theme.role_slots().accent.peach
}
pub fn accent_sapphire(&self) -> Color {
self.theme.role_slots().accent.sapphire
}
pub fn accent_maroon(&self) -> Color {
self.theme.role_slots().accent.maroon
}
pub fn apply_effects_to_color_at(
&self,
color: Color,
x: u16,
y: u16,
frame_width: u16,
frame_height: u16,
) -> Color {
if !self.theme.effects.enabled() {
return color;
}
let mut out = color;
let (r, g, b) = color_to_rgb(color);
if self.theme.effects.local_enabled {
let freq = self.theme.effects.flicker_hz as f64;
let intensity = self.theme.effects.flicker_intensity as f64;
if intensity > 0.001 {
let phase_offset = (r as f64 * 3.0 + g as f64 * 5.0 + b as f64 * 7.0) * 0.01;
let duty = self.theme.effects.local_burst_duty.clamp(0.0, 1.0) as f64;
let burst_hz = if self.theme.effects.local_burst_hz <= 0.0 {
freq * 0.35
} else {
self.theme.effects.local_burst_hz as f64
};
let idle_intensity = self.theme.effects.local_idle_intensity.clamp(0.0, 1.0) as f64;
let burst_boost = self.theme.effects.local_burst_boost.max(0.0) as f64;
let burst_gate =
(((self.frame_time * burst_hz) + (phase_offset * 0.75)).sin() + 1.0) / 2.0;
let in_burst = burst_gate <= duty;
let effective_intensity = if in_burst {
intensity * burst_boost
} else {
intensity * idle_intensity
};
if effective_intensity <= 0.001 {
return out;
}
let base_wave = (self.frame_time * freq).sin();
let drift_wave = ((self.frame_time * freq * 1.4) + phase_offset).sin();
let wave = (base_wave + drift_wave) / 2.0;
out = if wave > 0.0 {
let factor = wave * effective_intensity;
blend_colors((r, g, b), (255, 255, 255), factor)
} else {
let factor = wave.abs() * (effective_intensity * 0.8);
blend_colors((r, g, b), (0, 0, 0), factor)
};
}
}
if self.theme.effects.wave_enabled {
let wave_hz = self.theme.effects.wave_hz as f64;
let intensity = self.theme.effects.wave_intensity as f64;
let wavelength = self.theme.effects.wave_wavelength.max(1.0) as f64;
let phase = match self.theme.effects.wave_mode {
WaveMode::Linear => {
let angle = (self.theme.effects.wave_angle_degrees as f64).to_radians();
let dir_x = angle.cos();
let dir_y = angle.sin();
let pos = (x as f64 * dir_x + y as f64 * dir_y) / wavelength;
(self.frame_time * wave_hz * std::f64::consts::TAU) + pos
}
WaveMode::RadialOut => {
let cx = (frame_width.saturating_sub(1) as f64) * 0.5;
let cy = (frame_height.saturating_sub(1) as f64) * 0.5;
let dx = x as f64 - cx;
let dy = y as f64 - cy;
let dist = (dx * dx + dy * dy).sqrt() / wavelength;
(self.frame_time * wave_hz * std::f64::consts::TAU) - dist
}
WaveMode::RadialIn => {
let cx = (frame_width.saturating_sub(1) as f64) * 0.5;
let cy = (frame_height.saturating_sub(1) as f64) * 0.5;
let dx = x as f64 - cx;
let dy = y as f64 - cy;
let dist = (dx * dx + dy * dy).sqrt() / wavelength;
(self.frame_time * wave_hz * std::f64::consts::TAU) + dist
}
};
let wave = phase.sin();
let (rr, gg, bb) = color_to_rgb(out);
out = if self.theme.name == ThemeName::BlackHole {
let factor = ((wave + 1.0) * 0.5) * intensity;
blend_colors((rr, gg, bb), (0, 0, 0), factor)
} else if wave > 0.0 {
let factor = wave * intensity;
blend_colors((rr, gg, bb), (255, 255, 255), factor)
} else {
let factor = wave.abs() * intensity;
blend_colors((rr, gg, bb), (0, 0, 0), factor)
};
}
out
}
}
#[derive(Debug, Clone, Copy)]
pub struct Theme {
pub name: ThemeName,
pub effects: ThemeEffects,
pub semantic: ThemeSemantic,
pub scale: ThemeScale,
}
impl Theme {
pub fn role_slots(&self) -> ThemeRoleSlots {
ThemeRoleSlots {
state: ThemeStateSlots {
error: self.scale.categorical.red,
warning: self.scale.categorical.yellow,
success: self.scale.categorical.green,
info: self.scale.categorical.blue,
selected: self.scale.categorical.mauve,
complete: self.scale.categorical.lavender,
},
metric: ThemeMetricSlots {
download: self.scale.categorical.sky,
upload: self.scale.categorical.green,
},
peer: ThemePeerSlots {
discovered: self.scale.categorical.yellow,
connected: self.scale.categorical.teal,
disconnected: self.scale.categorical.maroon,
},
accent: ThemeAccentSlots {
sky: self.scale.categorical.sky,
teal: self.scale.categorical.teal,
peach: self.scale.categorical.peach,
sapphire: self.scale.categorical.sapphire,
maroon: self.scale.categorical.maroon,
},
}
}
pub fn builtin(name: ThemeName) -> Self {
match name {
ThemeName::Andromeda => Self::andromeda(),
ThemeName::Aurora => Self::aurora(),
ThemeName::AyuDark => Self::ayu_dark(),
ThemeName::Bubblegum => Self::bubblegum(),
ThemeName::CatppuccinLatte => Self::catppuccin_latte(),
ThemeName::CatppuccinMocha => Self::catppuccin_mocha(),
ThemeName::Cyberpunk => Self::cyberpunk(),
ThemeName::DeepOcean => Self::deep_ocean(),
ThemeName::DeepSky => Self::deep_sky(),
ThemeName::Diamond => Self::diamond(),
ThemeName::Gold => Self::gold(),
ThemeName::Dracula => Self::dracula(),
ThemeName::EverforestDark => Self::everforest_dark(),
ThemeName::GitHubDark => Self::github_dark(),
ThemeName::GitHubLight => Self::github_light(),
ThemeName::GruvboxDark => Self::gruvbox_dark(),
ThemeName::GruvboxLight => Self::gruvbox_light(),
ThemeName::Inferno => Self::inferno(),
ThemeName::Kanagawa => Self::kanagawa(),
ThemeName::MaterialOcean => Self::material_ocean(),
ThemeName::Matrix => Self::matrix(),
ThemeName::Monokai => Self::monokai(),
ThemeName::Neon => Self::neon(),
ThemeName::Nightfox => Self::nightfox(),
ThemeName::Nord => Self::nord(),
ThemeName::OneDark => Self::one_dark(),
ThemeName::ObsidianForge => Self::obsidian_forge(),
ThemeName::Oxocarbon => Self::oxocarbon(),
ThemeName::ArcticWhiteout => Self::arctic_whiteout(),
ThemeName::PaperColorLight => Self::papercolor_light(),
ThemeName::BlackHole => Self::black_hole(),
ThemeName::BioluminescentReef => Self::bioluminescent_reef(),
ThemeName::Rainbow => Self::rainbow(),
ThemeName::RosePine => Self::rose_pine(),
ThemeName::SolarizedDark => Self::solarized_dark(),
ThemeName::SolarizedLight => Self::solarized_light(),
ThemeName::Synthwave84 => Self::synthwave_84(),
ThemeName::TokyoNight => Self::tokyo_night(),
ThemeName::Vesper => Self::vesper(),
ThemeName::Zenburn => Self::zenburn(),
ThemeName::Sakura => Self::sakura(),
}
}
pub fn catppuccin_mocha() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(245, 224, 220),
flamingo: Color::Rgb(242, 205, 205),
pink: Color::Rgb(245, 194, 231),
mauve: Color::Rgb(203, 166, 247),
red: Color::Rgb(243, 139, 168),
maroon: Color::Rgb(235, 160, 172),
peach: Color::Rgb(250, 179, 135),
yellow: Color::Rgb(249, 226, 175),
green: Color::Rgb(166, 227, 161),
teal: Color::Rgb(148, 226, 213),
sky: Color::Rgb(137, 220, 235),
sapphire: Color::Rgb(116, 199, 236),
blue: Color::Rgb(137, 180, 250),
lavender: Color::Rgb(180, 190, 254),
};
Self {
name: ThemeName::CatppuccinMocha,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(205, 214, 244),
subtext1: Color::Rgb(186, 194, 222),
subtext0: Color::Rgb(166, 173, 200),
overlay0: Color::Rgb(108, 112, 134),
surface2: Color::Rgb(88, 91, 112),
surface1: Color::Rgb(69, 71, 90),
surface0: Color::Rgb(49, 50, 68),
border: Color::Rgb(108, 112, 134),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.maroon,
categorical.red,
categorical.flamingo,
categorical.pink,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(69, 71, 90),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn neon() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 220, 245),
flamingo: Color::Rgb(255, 150, 230),
pink: Color::Rgb(255, 70, 230),
mauve: Color::Rgb(210, 90, 255),
red: Color::Rgb(255, 60, 120),
maroon: Color::Rgb(255, 90, 160),
peach: Color::Rgb(255, 170, 80),
yellow: Color::Rgb(255, 240, 90),
green: Color::Rgb(100, 255, 190),
teal: Color::Rgb(0, 255, 255),
sky: Color::Rgb(80, 220, 255),
sapphire: Color::Rgb(40, 190, 255),
blue: Color::Rgb(40, 110, 255),
lavender: Color::Rgb(190, 170, 255),
};
Self {
name: ThemeName::Neon,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 9.0,
flicker_intensity: 0.35,
local_burst_duty: 0.08,
local_burst_hz: 0.8,
local_idle_intensity: 0.05,
local_burst_boost: 1.20,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(230, 255, 255),
subtext1: Color::Rgb(140, 230, 245),
subtext0: Color::Rgb(90, 200, 220),
overlay0: Color::Rgb(30, 70, 95),
surface2: Color::Rgb(18, 40, 64),
surface1: Color::Rgb(35, 70, 100),
surface0: Color::Rgb(8, 22, 42),
border: Color::Rgb(60, 100, 160),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(200, 255, 255),
Color::Rgb(120, 255, 240),
Color::Rgb(60, 245, 255),
Color::Rgb(80, 190, 255),
Color::Rgb(170, 120, 255),
Color::Rgb(255, 90, 230),
Color::Rgb(255, 60, 190),
Color::Rgb(255, 40, 150),
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.teal,
medium: categorical.teal,
high: categorical.teal,
empty: Color::Rgb(30, 45, 65),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn bubblegum() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 208, 235),
flamingo: Color::Rgb(255, 173, 219),
pink: Color::Rgb(245, 132, 204),
mauve: Color::Rgb(213, 126, 203),
red: Color::Rgb(236, 92, 149),
maroon: Color::Rgb(191, 79, 128),
peach: Color::Rgb(255, 172, 194),
yellow: Color::Rgb(255, 198, 216),
green: Color::Rgb(229, 160, 207),
teal: Color::Rgb(217, 149, 212),
sky: Color::Rgb(205, 141, 224),
sapphire: Color::Rgb(193, 132, 217),
blue: Color::Rgb(181, 122, 206),
lavender: Color::Rgb(210, 172, 238),
};
Self {
name: ThemeName::Bubblegum,
effects: ThemeEffects {
wave_enabled: true,
wave_hz: 0.45,
wave_intensity: 0.08,
wave_wavelength: 26.0,
wave_mode: WaveMode::RadialOut,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(255, 236, 247),
subtext1: Color::Rgb(245, 206, 228),
subtext0: Color::Rgb(224, 176, 207),
overlay0: Color::Rgb(171, 117, 152),
surface2: Color::Rgb(112, 67, 98),
surface1: Color::Rgb(89, 50, 78),
surface0: Color::Rgb(63, 31, 56),
border: Color::Rgb(203, 142, 180),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(255, 240, 250),
Color::Rgb(255, 220, 240),
Color::Rgb(255, 200, 230),
Color::Rgb(255, 180, 220),
Color::Rgb(255, 160, 210),
Color::Rgb(255, 140, 205),
Color::Rgb(255, 120, 200),
Color::Rgb(255, 100, 195),
],
ip_hash: [
categorical.rosewater,
categorical.flamingo,
categorical.pink,
categorical.mauve,
categorical.red,
categorical.maroon,
categorical.peach,
categorical.yellow,
categorical.lavender,
categorical.teal,
categorical.green,
categorical.sky,
categorical.sapphire,
categorical.blue,
],
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(89, 50, 78),
},
stream: ThemeStream {
inflow: categorical.mauve,
outflow: categorical.pink,
},
categorical,
},
}
}
pub fn deep_ocean() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(206, 233, 255),
flamingo: Color::Rgb(168, 212, 247),
pink: Color::Rgb(143, 191, 236),
mauve: Color::Rgb(110, 165, 222),
red: Color::Rgb(86, 142, 206),
maroon: Color::Rgb(68, 123, 186),
peach: Color::Rgb(52, 106, 168),
yellow: Color::Rgb(37, 90, 151),
green: Color::Rgb(31, 78, 136),
teal: Color::Rgb(25, 67, 121),
sky: Color::Rgb(19, 57, 106),
sapphire: Color::Rgb(14, 47, 92),
blue: Color::Rgb(10, 38, 79),
lavender: Color::Rgb(7, 31, 67),
};
Self {
name: ThemeName::DeepOcean,
effects: ThemeEffects {
wave_enabled: true,
wave_hz: 0.32,
wave_intensity: 0.18,
wave_wavelength: 52.0,
wave_angle_degrees: -72.0,
wave_mode: WaveMode::Linear,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(178, 217, 247),
subtext1: Color::Rgb(130, 181, 222),
subtext0: Color::Rgb(97, 149, 195),
overlay0: Color::Rgb(54, 93, 132),
surface2: Color::Rgb(27, 54, 85),
surface1: Color::Rgb(18, 41, 69),
surface0: Color::Rgb(8, 22, 44),
border: Color::Rgb(60, 110, 158),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.maroon,
categorical.red,
categorical.rosewater,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sapphire,
medium: categorical.sky,
high: categorical.rosewater,
empty: Color::Rgb(16, 33, 58),
},
stream: ThemeStream {
inflow: categorical.sapphire,
outflow: categorical.sky,
},
categorical,
},
}
}
pub fn deep_sky() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(20, 66, 122),
flamingo: Color::Rgb(32, 82, 142),
pink: Color::Rgb(46, 102, 166),
mauve: Color::Rgb(63, 124, 191),
red: Color::Rgb(86, 149, 216),
maroon: Color::Rgb(110, 171, 231),
peach: Color::Rgb(136, 193, 244),
yellow: Color::Rgb(163, 211, 250),
green: Color::Rgb(190, 225, 252),
teal: Color::Rgb(214, 236, 255),
sky: Color::Rgb(230, 244, 255),
sapphire: Color::Rgb(206, 229, 250),
blue: Color::Rgb(180, 212, 242),
lavender: Color::Rgb(152, 191, 230),
};
Self {
name: ThemeName::DeepSky,
effects: ThemeEffects {
wave_enabled: true,
wave_hz: 0.34,
wave_intensity: 0.16,
wave_wavelength: 56.0,
wave_angle_degrees: -68.0,
wave_mode: WaveMode::Linear,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(32, 78, 136),
subtext1: Color::Rgb(55, 103, 161),
subtext0: Color::Rgb(80, 126, 183),
overlay0: Color::Rgb(116, 157, 208),
surface2: Color::Rgb(170, 198, 226),
surface1: Color::Rgb(214, 232, 247),
surface0: Color::Rgb(202, 223, 241),
border: Color::Rgb(104, 149, 201),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.blue,
categorical.sapphire,
categorical.sky,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.rosewater,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.sky,
high: categorical.rosewater,
empty: Color::Rgb(192, 214, 235),
},
stream: ThemeStream {
inflow: categorical.sapphire,
outflow: categorical.sky,
},
categorical,
},
}
}
pub fn gold() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 248, 226),
flamingo: Color::Rgb(255, 234, 168),
pink: Color::Rgb(255, 220, 120),
mauve: Color::Rgb(243, 198, 83),
red: Color::Rgb(224, 172, 53),
maroon: Color::Rgb(221, 170, 58),
peach: Color::Rgb(208, 154, 46),
yellow: Color::Rgb(194, 139, 35),
green: Color::Rgb(180, 125, 30),
teal: Color::Rgb(166, 112, 26),
sky: Color::Rgb(153, 101, 23),
sapphire: Color::Rgb(141, 91, 21),
blue: Color::Rgb(130, 82, 19),
lavender: Color::Rgb(120, 74, 18),
};
Self {
name: ThemeName::Gold,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 10.0,
flicker_intensity: 0.22,
local_burst_duty: 0.10,
local_burst_hz: 1.2,
local_idle_intensity: 0.03,
local_burst_boost: 1.45,
wave_enabled: true,
wave_hz: 0.75,
wave_intensity: 0.18,
wave_wavelength: 34.0,
wave_angle_degrees: 22.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect::default(),
},
semantic: ThemeSemantic {
text: Color::Rgb(255, 236, 175),
subtext1: Color::Rgb(248, 214, 142),
subtext0: Color::Rgb(229, 186, 106),
overlay0: Color::Rgb(168, 126, 58),
surface2: Color::Rgb(92, 64, 27),
surface1: Color::Rgb(74, 49, 20),
surface0: Color::Rgb(54, 34, 13),
border: Color::Rgb(210, 166, 79),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.maroon,
categorical.red,
categorical.rosewater,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.yellow,
medium: categorical.yellow,
high: categorical.yellow,
empty: Color::Rgb(78, 53, 22),
},
stream: ThemeStream {
inflow: categorical.peach,
outflow: categorical.flamingo,
},
categorical,
},
}
}
pub fn dracula() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(248, 248, 242),
flamingo: Color::Rgb(255, 184, 108),
pink: Color::Rgb(255, 121, 198),
mauve: Color::Rgb(189, 147, 249),
red: Color::Rgb(255, 85, 85),
maroon: Color::Rgb(255, 110, 139),
peach: Color::Rgb(255, 184, 108),
yellow: Color::Rgb(241, 250, 140),
green: Color::Rgb(80, 250, 123),
teal: Color::Rgb(139, 233, 253),
sky: Color::Rgb(139, 233, 253),
sapphire: Color::Rgb(98, 114, 164),
blue: Color::Rgb(139, 233, 253),
lavender: Color::Rgb(189, 147, 249),
};
Self {
name: ThemeName::Dracula,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(248, 248, 242),
subtext1: Color::Rgb(189, 147, 249),
subtext0: Color::Rgb(98, 114, 164),
overlay0: Color::Rgb(68, 71, 90),
surface2: Color::Rgb(68, 71, 90),
surface1: Color::Rgb(56, 59, 77),
surface0: Color::Rgb(40, 42, 54),
border: Color::Rgb(95, 100, 128),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(68, 71, 90),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn nord() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(236, 239, 244),
flamingo: Color::Rgb(216, 222, 233),
pink: Color::Rgb(191, 97, 106),
mauve: Color::Rgb(180, 142, 173),
red: Color::Rgb(191, 97, 106),
maroon: Color::Rgb(208, 135, 112),
peach: Color::Rgb(208, 135, 112),
yellow: Color::Rgb(235, 203, 139),
green: Color::Rgb(163, 190, 140),
teal: Color::Rgb(143, 188, 187),
sky: Color::Rgb(136, 192, 208),
sapphire: Color::Rgb(129, 161, 193),
blue: Color::Rgb(94, 129, 172),
lavender: Color::Rgb(180, 142, 173),
};
Self {
name: ThemeName::Nord,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(236, 239, 244),
subtext1: Color::Rgb(216, 222, 233),
subtext0: Color::Rgb(143, 188, 187),
overlay0: Color::Rgb(76, 86, 106),
surface2: Color::Rgb(59, 66, 82),
surface1: Color::Rgb(46, 52, 64),
surface0: Color::Rgb(43, 48, 59),
border: Color::Rgb(98, 112, 137),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.mauve,
categorical.blue,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.sky,
high: categorical.green,
empty: Color::Rgb(46, 52, 64),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn gruvbox_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(235, 219, 178),
flamingo: Color::Rgb(214, 93, 14),
pink: Color::Rgb(211, 134, 155),
mauve: Color::Rgb(211, 134, 155),
red: Color::Rgb(251, 73, 52),
maroon: Color::Rgb(204, 36, 29),
peach: Color::Rgb(254, 128, 25),
yellow: Color::Rgb(250, 189, 47),
green: Color::Rgb(184, 187, 38),
teal: Color::Rgb(142, 192, 124),
sky: Color::Rgb(131, 165, 152),
sapphire: Color::Rgb(69, 133, 136),
blue: Color::Rgb(131, 165, 152),
lavender: Color::Rgb(214, 93, 14),
};
Self {
name: ThemeName::GruvboxDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(235, 219, 178),
subtext1: Color::Rgb(213, 196, 161),
subtext0: Color::Rgb(168, 153, 132),
overlay0: Color::Rgb(124, 111, 100),
surface2: Color::Rgb(66, 61, 58),
surface1: Color::Rgb(50, 48, 47),
surface0: Color::Rgb(40, 40, 40),
border: Color::Rgb(97, 88, 78),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.blue,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.maroon,
categorical.mauve,
categorical.pink,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.yellow,
medium: categorical.yellow,
high: categorical.yellow,
empty: Color::Rgb(60, 56, 54),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn tokyo_night() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(192, 202, 245),
flamingo: Color::Rgb(255, 158, 100),
pink: Color::Rgb(247, 118, 142),
mauve: Color::Rgb(187, 154, 247),
red: Color::Rgb(247, 118, 142),
maroon: Color::Rgb(255, 158, 100),
peach: Color::Rgb(255, 158, 100),
yellow: Color::Rgb(224, 175, 104),
green: Color::Rgb(158, 206, 106),
teal: Color::Rgb(125, 207, 255),
sky: Color::Rgb(125, 207, 255),
sapphire: Color::Rgb(122, 162, 247),
blue: Color::Rgb(122, 162, 247),
lavender: Color::Rgb(187, 154, 247),
};
Self {
name: ThemeName::TokyoNight,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(192, 202, 245),
subtext1: Color::Rgb(169, 177, 214),
subtext0: Color::Rgb(113, 123, 174),
overlay0: Color::Rgb(65, 72, 104),
surface2: Color::Rgb(41, 46, 66),
surface1: Color::Rgb(60, 65, 90),
surface0: Color::Rgb(26, 27, 38),
border: Color::Rgb(89, 98, 142),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.mauve,
categorical.blue,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(36, 40, 59),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn one_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(171, 178, 191),
flamingo: Color::Rgb(209, 154, 102),
pink: Color::Rgb(198, 120, 221),
mauve: Color::Rgb(198, 120, 221),
red: Color::Rgb(224, 108, 117),
maroon: Color::Rgb(190, 80, 70),
peach: Color::Rgb(209, 154, 102),
yellow: Color::Rgb(229, 192, 123),
green: Color::Rgb(152, 195, 121),
teal: Color::Rgb(86, 182, 194),
sky: Color::Rgb(97, 175, 239),
sapphire: Color::Rgb(97, 175, 239),
blue: Color::Rgb(97, 175, 239),
lavender: Color::Rgb(198, 120, 221),
};
Self {
name: ThemeName::OneDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(171, 178, 191),
subtext1: Color::Rgb(146, 150, 165),
subtext0: Color::Rgb(111, 119, 137),
overlay0: Color::Rgb(73, 78, 90),
surface2: Color::Rgb(47, 51, 61),
surface1: Color::Rgb(65, 72, 80),
surface0: Color::Rgb(30, 33, 39),
border: Color::Rgb(97, 105, 121),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(40, 44, 52),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn solarized_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(131, 148, 150),
flamingo: Color::Rgb(203, 75, 22),
pink: Color::Rgb(211, 54, 130),
mauve: Color::Rgb(108, 113, 196),
red: Color::Rgb(220, 50, 47),
maroon: Color::Rgb(203, 75, 22),
peach: Color::Rgb(203, 75, 22),
yellow: Color::Rgb(181, 137, 0),
green: Color::Rgb(133, 153, 0),
teal: Color::Rgb(42, 161, 152),
sky: Color::Rgb(38, 139, 210),
sapphire: Color::Rgb(38, 139, 210),
blue: Color::Rgb(38, 139, 210),
lavender: Color::Rgb(108, 113, 196),
};
Self {
name: ThemeName::SolarizedDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(131, 148, 150),
subtext1: Color::Rgb(147, 161, 161),
subtext0: Color::Rgb(101, 123, 131),
overlay0: Color::Rgb(88, 110, 117),
surface2: Color::Rgb(7, 54, 66),
surface1: Color::Rgb(0, 90, 110),
surface0: Color::Rgb(0, 33, 44),
border: Color::Rgb(0, 130, 160),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.teal,
medium: categorical.teal,
high: categorical.teal,
empty: Color::Rgb(7, 54, 66),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn monokai() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(248, 248, 242),
flamingo: Color::Rgb(253, 151, 31),
pink: Color::Rgb(249, 38, 114),
mauve: Color::Rgb(174, 129, 255),
red: Color::Rgb(249, 38, 114),
maroon: Color::Rgb(204, 102, 119),
peach: Color::Rgb(253, 151, 31),
yellow: Color::Rgb(230, 219, 116),
green: Color::Rgb(166, 226, 46),
teal: Color::Rgb(102, 217, 239),
sky: Color::Rgb(102, 217, 239),
sapphire: Color::Rgb(117, 113, 94),
blue: Color::Rgb(102, 217, 239),
lavender: Color::Rgb(174, 129, 255),
};
Self {
name: ThemeName::Monokai,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(248, 248, 242),
subtext1: Color::Rgb(174, 129, 255),
subtext0: Color::Rgb(117, 113, 94),
overlay0: Color::Rgb(73, 72, 62),
surface2: Color::Rgb(49, 50, 43),
surface1: Color::Rgb(70, 72, 65),
surface0: Color::Rgb(27, 28, 24),
border: Color::Rgb(96, 96, 82),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.green,
medium: categorical.green,
high: categorical.green,
empty: Color::Rgb(39, 40, 34),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn everforest_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(211, 198, 170),
flamingo: Color::Rgb(230, 126, 128),
pink: Color::Rgb(231, 138, 131),
mauve: Color::Rgb(215, 153, 33),
red: Color::Rgb(230, 126, 128),
maroon: Color::Rgb(229, 152, 117),
peach: Color::Rgb(229, 152, 117),
yellow: Color::Rgb(219, 188, 127),
green: Color::Rgb(167, 192, 128),
teal: Color::Rgb(131, 192, 146),
sky: Color::Rgb(127, 187, 179),
sapphire: Color::Rgb(115, 163, 145),
blue: Color::Rgb(127, 187, 179),
lavender: Color::Rgb(214, 153, 182),
};
Self {
name: ThemeName::EverforestDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(211, 198, 170),
subtext1: Color::Rgb(167, 192, 128),
subtext0: Color::Rgb(133, 147, 138),
overlay0: Color::Rgb(94, 100, 104),
surface2: Color::Rgb(59, 69, 71),
surface1: Color::Rgb(47, 56, 58),
surface0: Color::Rgb(43, 51, 57),
border: Color::Rgb(84, 101, 103),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.green,
medium: categorical.green,
high: categorical.green,
empty: Color::Rgb(59, 69, 71),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn kanagawa() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(220, 215, 186),
flamingo: Color::Rgb(210, 126, 154),
pink: Color::Rgb(210, 126, 154),
mauve: Color::Rgb(149, 127, 184),
red: Color::Rgb(195, 64, 67),
maroon: Color::Rgb(195, 64, 67),
peach: Color::Rgb(255, 160, 102),
yellow: Color::Rgb(192, 163, 110),
green: Color::Rgb(118, 148, 106),
teal: Color::Rgb(106, 149, 137),
sky: Color::Rgb(126, 156, 216),
sapphire: Color::Rgb(101, 133, 153),
blue: Color::Rgb(126, 156, 216),
lavender: Color::Rgb(149, 127, 184),
};
Self {
name: ThemeName::Kanagawa,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(220, 215, 186),
subtext1: Color::Rgb(166, 173, 200),
subtext0: Color::Rgb(114, 113, 133),
overlay0: Color::Rgb(84, 84, 111),
surface2: Color::Rgb(54, 54, 75),
surface1: Color::Rgb(42, 42, 62),
surface0: Color::Rgb(31, 31, 40),
border: Color::Rgb(84, 84, 111),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sapphire,
medium: categorical.sapphire,
high: categorical.sapphire,
empty: Color::Rgb(54, 54, 75),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn github_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(201, 209, 217),
flamingo: Color::Rgb(255, 122, 127),
pink: Color::Rgb(210, 153, 255),
mauve: Color::Rgb(188, 140, 255),
red: Color::Rgb(248, 81, 73),
maroon: Color::Rgb(255, 123, 114),
peach: Color::Rgb(255, 166, 87),
yellow: Color::Rgb(210, 153, 34),
green: Color::Rgb(63, 185, 80),
teal: Color::Rgb(57, 197, 207),
sky: Color::Rgb(103, 193, 255),
sapphire: Color::Rgb(88, 166, 255),
blue: Color::Rgb(88, 166, 255),
lavender: Color::Rgb(188, 140, 255),
};
Self {
name: ThemeName::GitHubDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(201, 209, 217),
subtext1: Color::Rgb(139, 148, 158),
subtext0: Color::Rgb(110, 118, 129),
overlay0: Color::Rgb(48, 54, 61),
surface2: Color::Rgb(33, 38, 45),
surface1: Color::Rgb(50, 60, 70),
surface0: Color::Rgb(13, 17, 23),
border: Color::Rgb(70, 79, 90),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(33, 38, 45),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn solarized_light() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(101, 123, 131),
flamingo: Color::Rgb(203, 75, 22),
pink: Color::Rgb(211, 54, 130),
mauve: Color::Rgb(108, 113, 196),
red: Color::Rgb(220, 50, 47),
maroon: Color::Rgb(203, 75, 22),
peach: Color::Rgb(203, 75, 22),
yellow: Color::Rgb(181, 137, 0),
green: Color::Rgb(133, 153, 0),
teal: Color::Rgb(42, 161, 152),
sky: Color::Rgb(38, 139, 210),
sapphire: Color::Rgb(38, 139, 210),
blue: Color::Rgb(38, 139, 210),
lavender: Color::Rgb(108, 113, 196),
};
Self {
name: ThemeName::SolarizedLight,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(88, 110, 117),
subtext1: Color::Rgb(88, 110, 117),
subtext0: Color::Rgb(131, 148, 150),
overlay0: Color::Rgb(147, 161, 161),
surface2: Color::Rgb(238, 232, 213),
surface1: Color::Rgb(253, 246, 227),
surface0: Color::Rgb(255, 255, 240),
border: Color::Rgb(147, 161, 161),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(238, 232, 213),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn matrix() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(0, 255, 65),
flamingo: Color::Rgb(0, 143, 17),
pink: Color::Rgb(0, 255, 65),
mauve: Color::Rgb(0, 59, 0),
red: Color::Rgb(255, 95, 95),
maroon: Color::Rgb(0, 143, 17),
peach: Color::Rgb(0, 255, 65),
yellow: Color::Rgb(255, 240, 120),
green: Color::Rgb(0, 255, 65),
teal: Color::Rgb(0, 255, 65),
sky: Color::Rgb(0, 255, 65),
sapphire: Color::Rgb(0, 255, 65),
blue: Color::Rgb(0, 255, 65),
lavender: Color::Rgb(0, 255, 65),
};
Self {
name: ThemeName::Matrix,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 5.0,
flicker_intensity: 0.16,
particle: ThemeParticleEffect {
enabled: true,
layer_mode: ParticleLayerMode::Background,
profile: ParticleProfile::Matrix,
density: 0.026,
speed: 0.82,
intensity: 0.62,
},
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(0, 255, 65),
subtext1: Color::Rgb(0, 204, 52),
subtext0: Color::Rgb(0, 143, 17),
overlay0: Color::Rgb(0, 89, 11),
surface2: Color::Rgb(0, 59, 0),
surface1: Color::Rgb(0, 180, 0),
surface0: Color::Rgb(0, 0, 0),
border: Color::Rgb(0, 143, 17),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(0, 59, 0),
Color::Rgb(0, 89, 11),
Color::Rgb(0, 143, 17),
Color::Rgb(0, 204, 52),
Color::Rgb(0, 255, 65),
Color::Rgb(102, 255, 102),
Color::Rgb(153, 255, 153),
Color::Rgb(204, 255, 204),
],
ip_hash: [
Color::Rgb(0, 255, 65),
Color::Rgb(0, 204, 52),
Color::Rgb(0, 143, 17),
Color::Rgb(0, 89, 11),
Color::Rgb(0, 59, 0),
Color::Rgb(0, 255, 65),
Color::Rgb(0, 204, 52),
Color::Rgb(0, 143, 17),
Color::Rgb(0, 89, 11),
Color::Rgb(0, 59, 0),
Color::Rgb(0, 255, 65),
Color::Rgb(0, 204, 52),
Color::Rgb(0, 143, 17),
Color::Rgb(0, 89, 11),
],
heatmap: ThemeHeatmap {
low: Color::Rgb(0, 59, 0),
medium: Color::Rgb(0, 143, 17),
high: Color::Rgb(0, 255, 65),
empty: Color::Rgb(0, 20, 0),
},
stream: ThemeStream {
inflow: Color::Rgb(0, 255, 65),
outflow: Color::Rgb(0, 143, 17),
},
categorical,
},
}
}
pub fn catppuccin_latte() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(220, 138, 120),
flamingo: Color::Rgb(221, 120, 120),
pink: Color::Rgb(234, 118, 203),
mauve: Color::Rgb(136, 57, 239),
red: Color::Rgb(210, 15, 57),
maroon: Color::Rgb(230, 69, 83),
peach: Color::Rgb(254, 100, 11),
yellow: Color::Rgb(223, 142, 29),
green: Color::Rgb(64, 160, 43),
teal: Color::Rgb(23, 146, 153),
sky: Color::Rgb(4, 165, 229),
sapphire: Color::Rgb(32, 159, 181),
blue: Color::Rgb(30, 102, 245),
lavender: Color::Rgb(114, 135, 253),
};
Self {
name: ThemeName::CatppuccinLatte,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(76, 79, 105),
subtext1: Color::Rgb(92, 95, 119),
subtext0: Color::Rgb(108, 111, 133),
overlay0: Color::Rgb(156, 160, 176),
surface2: Color::Rgb(172, 176, 190),
surface1: Color::Rgb(188, 192, 204),
surface0: Color::Rgb(204, 208, 218),
border: Color::Rgb(130, 134, 151),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.maroon,
categorical.red,
categorical.flamingo,
categorical.pink,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(188, 192, 204),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn cyberpunk() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 0, 255),
pink: Color::Rgb(255, 0, 255),
mauve: Color::Rgb(150, 0, 255),
red: Color::Rgb(255, 0, 60),
maroon: Color::Rgb(255, 0, 100),
peach: Color::Rgb(255, 100, 0),
yellow: Color::Rgb(253, 245, 0),
green: Color::Rgb(0, 255, 159),
teal: Color::Rgb(0, 255, 255),
sky: Color::Rgb(0, 184, 255),
sapphire: Color::Rgb(0, 114, 255),
blue: Color::Rgb(5, 217, 255),
lavender: Color::Rgb(150, 0, 255),
};
Self {
name: ThemeName::Cyberpunk,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 9.0,
flicker_intensity: 0.26,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(253, 245, 0),
subtext1: Color::Rgb(0, 255, 255),
subtext0: Color::Rgb(255, 0, 255),
overlay0: Color::Rgb(50, 0, 100),
surface2: Color::Rgb(45, 8, 82),
surface1: Color::Rgb(120, 50, 160),
surface0: Color::Rgb(0, 0, 0),
border: Color::Rgb(255, 0, 255),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.blue,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.pink,
medium: categorical.pink,
high: categorical.pink,
empty: Color::Rgb(30, 0, 60),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn ayu_dark() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(230, 225, 207),
flamingo: Color::Rgb(255, 180, 84),
pink: Color::Rgb(240, 117, 181),
mauve: Color::Rgb(223, 177, 242),
red: Color::Rgb(255, 51, 51),
maroon: Color::Rgb(242, 121, 131),
peach: Color::Rgb(255, 180, 84),
yellow: Color::Rgb(242, 151, 24),
green: Color::Rgb(184, 204, 82),
teal: Color::Rgb(149, 230, 203),
sky: Color::Rgb(54, 163, 217),
sapphire: Color::Rgb(54, 163, 217),
blue: Color::Rgb(54, 163, 217),
lavender: Color::Rgb(223, 177, 242),
};
Self {
name: ThemeName::AyuDark,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(230, 225, 207),
subtext1: Color::Rgb(171, 176, 191),
subtext0: Color::Rgb(92, 103, 115),
overlay0: Color::Rgb(62, 71, 82),
surface2: Color::Rgb(33, 39, 47),
surface1: Color::Rgb(55, 65, 75),
surface0: Color::Rgb(15, 20, 25),
border: Color::Rgb(84, 96, 112),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.teal,
medium: categorical.teal,
high: categorical.teal,
empty: Color::Rgb(25, 30, 36),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn zenburn() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(220, 220, 204),
flamingo: Color::Rgb(223, 175, 143),
pink: Color::Rgb(220, 140, 195),
mauve: Color::Rgb(156, 144, 186),
red: Color::Rgb(204, 147, 147),
maroon: Color::Rgb(188, 131, 121),
peach: Color::Rgb(223, 175, 143),
yellow: Color::Rgb(240, 223, 175),
green: Color::Rgb(127, 159, 127),
teal: Color::Rgb(147, 177, 187),
sky: Color::Rgb(140, 208, 211),
sapphire: Color::Rgb(115, 139, 140),
blue: Color::Rgb(140, 208, 211),
lavender: Color::Rgb(156, 144, 186),
};
Self {
name: ThemeName::Zenburn,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(220, 220, 204),
subtext1: Color::Rgb(159, 159, 159),
subtext0: Color::Rgb(127, 159, 127),
overlay0: Color::Rgb(83, 83, 83),
surface2: Color::Rgb(71, 71, 71),
surface1: Color::Rgb(63, 63, 63),
surface0: Color::Rgb(50, 50, 50),
border: Color::Rgb(105, 105, 105),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.green,
medium: categorical.green,
high: categorical.green,
empty: Color::Rgb(71, 71, 71),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn synthwave_84() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 126, 219),
pink: Color::Rgb(255, 126, 219),
mauve: Color::Rgb(114, 241, 184),
red: Color::Rgb(249, 126, 114),
maroon: Color::Rgb(249, 126, 114),
peach: Color::Rgb(254, 238, 0),
yellow: Color::Rgb(254, 238, 0),
green: Color::Rgb(114, 241, 184),
teal: Color::Rgb(54, 249, 246),
sky: Color::Rgb(54, 249, 246),
sapphire: Color::Rgb(54, 249, 246),
blue: Color::Rgb(54, 249, 246),
lavender: Color::Rgb(114, 241, 184),
};
Self {
name: ThemeName::Synthwave84,
effects: ThemeEffects {
wave_enabled: true,
wave_hz: 0.85,
wave_intensity: 0.11,
wave_wavelength: 30.0,
wave_mode: WaveMode::RadialOut,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(249, 126, 114),
subtext1: Color::Rgb(255, 126, 219),
subtext0: Color::Rgb(54, 249, 246),
overlay0: Color::Rgb(103, 78, 131),
surface2: Color::Rgb(65, 55, 90),
surface1: Color::Rgb(70, 50, 90),
surface0: Color::Rgb(36, 27, 47),
border: Color::Rgb(255, 126, 219),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.rosewater,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.pink,
medium: categorical.pink,
high: categorical.pink,
empty: Color::Rgb(52, 43, 73),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn github_light() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(36, 41, 47),
flamingo: Color::Rgb(207, 34, 46),
pink: Color::Rgb(130, 80, 223),
mauve: Color::Rgb(130, 80, 223),
red: Color::Rgb(207, 34, 46),
maroon: Color::Rgb(207, 34, 46),
peach: Color::Rgb(154, 103, 0),
yellow: Color::Rgb(154, 103, 0),
green: Color::Rgb(26, 127, 55),
teal: Color::Rgb(5, 153, 112),
sky: Color::Rgb(5, 153, 112),
sapphire: Color::Rgb(9, 105, 218),
blue: Color::Rgb(9, 105, 218),
lavender: Color::Rgb(130, 80, 223),
};
Self {
name: ThemeName::GitHubLight,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(36, 41, 47),
subtext1: Color::Rgb(87, 96, 106),
subtext0: Color::Rgb(101, 109, 118),
overlay0: Color::Rgb(208, 215, 222),
surface2: Color::Rgb(220, 226, 233),
surface1: Color::Rgb(246, 248, 250),
surface0: Color::Rgb(255, 255, 255),
border: Color::Rgb(163, 173, 185),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.sapphire,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(234, 238, 242),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn vesper() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 128, 0),
pink: Color::Rgb(255, 175, 0),
mauve: Color::Rgb(160, 160, 160),
red: Color::Rgb(255, 128, 0),
maroon: Color::Rgb(255, 128, 0),
peach: Color::Rgb(255, 175, 0),
yellow: Color::Rgb(255, 175, 0),
green: Color::Rgb(160, 160, 160),
teal: Color::Rgb(160, 160, 160),
sky: Color::Rgb(160, 160, 160),
sapphire: Color::Rgb(160, 160, 160),
blue: Color::Rgb(160, 160, 160),
lavender: Color::Rgb(160, 160, 160),
};
Self {
name: ThemeName::Vesper,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(255, 255, 255),
subtext1: Color::Rgb(160, 160, 160),
subtext0: Color::Rgb(110, 110, 110),
overlay0: Color::Rgb(70, 70, 70),
surface2: Color::Rgb(40, 40, 40),
surface1: Color::Rgb(60, 60, 60),
surface0: Color::Rgb(16, 16, 16),
border: Color::Rgb(120, 120, 120),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(80, 80, 80),
Color::Rgb(110, 110, 110),
Color::Rgb(140, 140, 140),
Color::Rgb(160, 160, 160),
Color::Rgb(180, 180, 180),
Color::Rgb(255, 175, 0),
Color::Rgb(255, 128, 0),
Color::Rgb(255, 255, 255),
],
ip_hash: [
Color::Rgb(255, 255, 255),
Color::Rgb(255, 128, 0),
Color::Rgb(255, 175, 0),
Color::Rgb(160, 160, 160),
Color::Rgb(110, 110, 110),
Color::Rgb(255, 255, 255),
Color::Rgb(255, 128, 0),
Color::Rgb(255, 175, 0),
Color::Rgb(160, 160, 160),
Color::Rgb(110, 110, 110),
Color::Rgb(255, 255, 255),
Color::Rgb(255, 128, 0),
Color::Rgb(255, 175, 0),
Color::Rgb(160, 160, 160),
],
heatmap: ThemeHeatmap {
low: categorical.peach,
medium: categorical.peach,
high: categorical.peach,
empty: Color::Rgb(30, 30, 30),
},
stream: ThemeStream {
inflow: Color::Rgb(255, 175, 0),
outflow: Color::Rgb(255, 128, 0),
},
categorical,
},
}
}
pub fn material_ocean() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(143, 147, 162),
flamingo: Color::Rgb(255, 83, 112),
pink: Color::Rgb(240, 113, 120),
mauve: Color::Rgb(199, 146, 234),
red: Color::Rgb(240, 113, 120),
maroon: Color::Rgb(240, 113, 120),
peach: Color::Rgb(247, 140, 108),
yellow: Color::Rgb(255, 203, 107),
green: Color::Rgb(195, 232, 141),
teal: Color::Rgb(137, 221, 255),
sky: Color::Rgb(137, 221, 255),
sapphire: Color::Rgb(130, 170, 255),
blue: Color::Rgb(130, 170, 255),
lavender: Color::Rgb(199, 146, 234),
};
Self {
name: ThemeName::MaterialOcean,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(143, 147, 162),
subtext1: Color::Rgb(113, 123, 145),
subtext0: Color::Rgb(105, 114, 138),
overlay0: Color::Rgb(53, 57, 74),
surface2: Color::Rgb(37, 41, 58),
surface1: Color::Rgb(45, 50, 75),
surface0: Color::Rgb(15, 17, 26),
border: Color::Rgb(100, 110, 140),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.teal,
medium: categorical.teal,
high: categorical.teal,
empty: Color::Rgb(25, 27, 41),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn gruvbox_light() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(60, 56, 54),
flamingo: Color::Rgb(175, 58, 3),
pink: Color::Rgb(143, 63, 113),
mauve: Color::Rgb(143, 63, 113),
red: Color::Rgb(157, 0, 6),
maroon: Color::Rgb(157, 0, 6),
peach: Color::Rgb(175, 58, 3),
yellow: Color::Rgb(181, 118, 20),
green: Color::Rgb(121, 116, 14),
teal: Color::Rgb(66, 123, 88),
sky: Color::Rgb(7, 102, 120),
sapphire: Color::Rgb(7, 102, 120),
blue: Color::Rgb(7, 102, 120),
lavender: Color::Rgb(143, 63, 113),
};
Self {
name: ThemeName::GruvboxLight,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(60, 56, 54),
subtext1: Color::Rgb(80, 73, 69),
subtext0: Color::Rgb(102, 92, 84),
overlay0: Color::Rgb(146, 131, 116),
surface2: Color::Rgb(213, 196, 161),
surface1: Color::Rgb(200, 185, 155),
surface0: Color::Rgb(251, 241, 199),
border: Color::Rgb(173, 154, 132),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.blue,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.maroon,
categorical.mauve,
categorical.pink,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.yellow,
medium: categorical.yellow,
high: categorical.yellow,
empty: Color::Rgb(213, 196, 161),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn oxocarbon() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 126, 182),
pink: Color::Rgb(255, 126, 182),
mauve: Color::Rgb(190, 149, 255),
red: Color::Rgb(238, 83, 103),
maroon: Color::Rgb(238, 83, 103),
peach: Color::Rgb(255, 169, 123),
yellow: Color::Rgb(255, 233, 123),
green: Color::Rgb(66, 190, 101),
teal: Color::Rgb(51, 177, 255),
sky: Color::Rgb(130, 207, 255),
sapphire: Color::Rgb(130, 207, 255),
blue: Color::Rgb(130, 207, 255),
lavender: Color::Rgb(190, 149, 255),
};
Self {
name: ThemeName::Oxocarbon,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(255, 255, 255),
subtext1: Color::Rgb(221, 221, 221),
subtext0: Color::Rgb(171, 171, 171),
overlay0: Color::Rgb(82, 82, 82),
surface2: Color::Rgb(57, 57, 57),
surface1: Color::Rgb(50, 55, 65),
surface0: Color::Rgb(22, 22, 22),
border: Color::Rgb(110, 110, 110),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(38, 38, 38),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn rainbow() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 0, 0),
flamingo: Color::Rgb(255, 127, 0),
pink: Color::Rgb(255, 0, 255),
mauve: Color::Rgb(127, 0, 255),
red: Color::Rgb(255, 0, 0),
maroon: Color::Rgb(127, 0, 0),
peach: Color::Rgb(255, 127, 0),
yellow: Color::Rgb(255, 255, 0),
green: Color::Rgb(0, 255, 0),
teal: Color::Rgb(0, 255, 255),
sky: Color::Rgb(0, 127, 255),
sapphire: Color::Rgb(0, 0, 255),
blue: Color::Rgb(0, 0, 255),
lavender: Color::Rgb(127, 0, 255),
};
Self {
name: ThemeName::Rainbow,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 2.0,
flicker_intensity: 0.1,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(242, 246, 252),
subtext1: Color::Rgb(208, 217, 235),
subtext0: Color::Rgb(171, 184, 209),
overlay0: Color::Rgb(100, 119, 154),
surface2: Color::Rgb(49, 64, 92),
surface1: Color::Rgb(36, 49, 74),
surface0: Color::Rgb(20, 30, 48),
border: Color::Rgb(127, 149, 189),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(255, 0, 0),
Color::Rgb(255, 127, 0),
Color::Rgb(255, 255, 0),
Color::Rgb(0, 255, 0),
Color::Rgb(0, 255, 255),
Color::Rgb(0, 0, 255),
Color::Rgb(127, 0, 255),
Color::Rgb(255, 0, 255),
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sapphire,
medium: categorical.sapphire,
high: categorical.pink,
empty: Color::Rgb(50, 50, 50),
},
stream: ThemeStream {
inflow: Color::Rgb(0, 255, 255),
outflow: Color::Rgb(255, 0, 255),
},
categorical,
},
}
}
pub fn inferno() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 100, 0),
pink: Color::Rgb(255, 50, 0),
mauve: Color::Rgb(150, 0, 0),
red: Color::Rgb(255, 0, 0),
maroon: Color::Rgb(150, 0, 0),
peach: Color::Rgb(255, 150, 0),
yellow: Color::Rgb(255, 255, 0),
green: Color::Rgb(255, 200, 0),
teal: Color::Rgb(255, 220, 100),
sky: Color::Rgb(255, 240, 150),
sapphire: Color::Rgb(255, 255, 200),
blue: Color::Rgb(255, 255, 200),
lavender: Color::Rgb(150, 0, 0),
};
Self {
name: ThemeName::Inferno,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 10.0,
flicker_intensity: 0.30,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(255, 200, 0),
subtext1: Color::Rgb(255, 150, 0),
subtext0: Color::Rgb(255, 100, 0),
overlay0: Color::Rgb(150, 50, 0),
surface2: Color::Rgb(80, 20, 0),
surface1: Color::Rgb(100, 40, 20),
surface0: Color::Rgb(20, 0, 0),
border: Color::Rgb(255, 50, 0),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(80, 0, 0),
Color::Rgb(150, 0, 0),
Color::Rgb(200, 50, 0),
Color::Rgb(255, 80, 0),
Color::Rgb(255, 120, 0),
Color::Rgb(255, 180, 0),
Color::Rgb(255, 220, 0),
Color::Rgb(255, 255, 100),
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.maroon,
medium: categorical.maroon,
high: categorical.yellow,
empty: Color::Rgb(40, 10, 0),
},
stream: ThemeStream {
inflow: Color::Rgb(255, 255, 0),
outflow: Color::Rgb(255, 50, 0),
},
categorical,
},
}
}
pub fn aurora() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(200, 255, 200),
flamingo: Color::Rgb(150, 255, 150),
pink: Color::Rgb(255, 150, 255),
mauve: Color::Rgb(200, 150, 255),
red: Color::Rgb(100, 255, 100),
maroon: Color::Rgb(50, 200, 150),
peach: Color::Rgb(100, 200, 255),
yellow: Color::Rgb(150, 255, 255),
green: Color::Rgb(0, 255, 128),
teal: Color::Rgb(0, 255, 255),
sky: Color::Rgb(128, 255, 255),
sapphire: Color::Rgb(128, 128, 255),
blue: Color::Rgb(150, 150, 255),
lavender: Color::Rgb(200, 150, 255),
};
Self {
name: ThemeName::Aurora,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 3.0,
flicker_intensity: 0.28,
wave_enabled: true,
wave_hz: 0.65,
wave_intensity: 0.12,
wave_wavelength: 34.0,
wave_angle_degrees: 45.0,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(150, 255, 200),
subtext1: Color::Rgb(100, 200, 255),
subtext0: Color::Rgb(150, 150, 255),
overlay0: Color::Rgb(40, 60, 100),
surface2: Color::Rgb(20, 30, 60),
surface1: Color::Rgb(30, 45, 90),
surface0: Color::Rgb(5, 5, 25),
border: Color::Rgb(0, 255, 128),
white: Color::White,
},
scale: ThemeScale {
speed: [
Color::Rgb(5, 5, 40),
Color::Rgb(20, 40, 100),
Color::Rgb(40, 80, 150),
Color::Rgb(0, 150, 150),
Color::Rgb(0, 200, 100),
Color::Rgb(0, 255, 128),
Color::Rgb(100, 255, 200),
Color::Rgb(200, 255, 255),
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sapphire,
medium: categorical.sapphire,
high: categorical.teal,
empty: Color::Rgb(20, 30, 60),
},
stream: ThemeStream {
inflow: Color::Rgb(0, 255, 255),
outflow: Color::Rgb(200, 150, 255),
},
categorical,
},
}
}
pub fn andromeda() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 255, 255),
flamingo: Color::Rgb(255, 74, 133),
pink: Color::Rgb(255, 74, 133),
mauve: Color::Rgb(173, 112, 255),
red: Color::Rgb(255, 76, 110),
maroon: Color::Rgb(255, 76, 110),
peach: Color::Rgb(255, 202, 125),
yellow: Color::Rgb(255, 230, 109),
green: Color::Rgb(0, 230, 152),
teal: Color::Rgb(0, 230, 230),
sky: Color::Rgb(0, 150, 255),
sapphire: Color::Rgb(0, 150, 255),
blue: Color::Rgb(0, 150, 255),
lavender: Color::Rgb(173, 112, 255),
};
Self {
name: ThemeName::Andromeda,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(213, 218, 227),
subtext1: Color::Rgb(153, 158, 167),
subtext0: Color::Rgb(116, 123, 136),
overlay0: Color::Rgb(59, 64, 72),
surface2: Color::Rgb(43, 48, 59),
surface1: Color::Rgb(65, 70, 80),
surface0: Color::Rgb(29, 32, 38),
border: Color::Rgb(79, 86, 100),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(43, 48, 59),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn rose_pine() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(224, 222, 244),
flamingo: Color::Rgb(246, 193, 119),
pink: Color::Rgb(235, 111, 146),
mauve: Color::Rgb(196, 167, 231),
red: Color::Rgb(235, 111, 146),
maroon: Color::Rgb(235, 188, 186),
peach: Color::Rgb(246, 193, 119),
yellow: Color::Rgb(246, 193, 119),
green: Color::Rgb(49, 116, 143),
teal: Color::Rgb(156, 207, 216),
sky: Color::Rgb(156, 207, 216),
sapphire: Color::Rgb(144, 140, 170),
blue: Color::Rgb(156, 207, 216),
lavender: Color::Rgb(196, 167, 231),
};
Self {
name: ThemeName::RosePine,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(224, 222, 244),
subtext1: Color::Rgb(144, 140, 170),
subtext0: Color::Rgb(110, 106, 134),
overlay0: Color::Rgb(64, 61, 82),
surface2: Color::Rgb(49, 45, 73),
surface1: Color::Rgb(65, 60, 90),
surface0: Color::Rgb(25, 23, 36),
border: Color::Rgb(92, 88, 120),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.teal,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(38, 35, 58),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn nightfox() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(219, 225, 245),
flamingo: Color::Rgb(244, 163, 116),
pink: Color::Rgb(210, 156, 255),
mauve: Color::Rgb(187, 154, 247),
red: Color::Rgb(242, 109, 130),
maroon: Color::Rgb(219, 118, 126),
peach: Color::Rgb(244, 163, 116),
yellow: Color::Rgb(230, 201, 126),
green: Color::Rgb(126, 207, 143),
teal: Color::Rgb(86, 205, 205),
sky: Color::Rgb(131, 206, 255),
sapphire: Color::Rgb(110, 176, 255),
blue: Color::Rgb(99, 156, 255),
lavender: Color::Rgb(175, 152, 252),
};
Self {
name: ThemeName::Nightfox,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(210, 220, 248),
subtext1: Color::Rgb(174, 186, 223),
subtext0: Color::Rgb(142, 156, 196),
overlay0: Color::Rgb(92, 104, 145),
surface2: Color::Rgb(55, 66, 96),
surface1: Color::Rgb(40, 50, 76),
surface0: Color::Rgb(26, 33, 54),
border: Color::Rgb(92, 104, 145),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(40, 50, 76),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn papercolor_light() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(70, 78, 87),
flamingo: Color::Rgb(181, 90, 60),
pink: Color::Rgb(161, 85, 170),
mauve: Color::Rgb(123, 102, 204),
red: Color::Rgb(200, 72, 65),
maroon: Color::Rgb(161, 81, 85),
peach: Color::Rgb(196, 122, 62),
yellow: Color::Rgb(155, 132, 26),
green: Color::Rgb(77, 133, 67),
teal: Color::Rgb(51, 135, 122),
sky: Color::Rgb(55, 130, 171),
sapphire: Color::Rgb(67, 112, 182),
blue: Color::Rgb(52, 98, 175),
lavender: Color::Rgb(122, 100, 182),
};
Self {
name: ThemeName::PaperColorLight,
effects: ThemeEffects::default(),
semantic: ThemeSemantic {
text: Color::Rgb(55, 62, 72),
subtext1: Color::Rgb(84, 92, 104),
subtext0: Color::Rgb(109, 117, 129),
overlay0: Color::Rgb(150, 145, 133),
surface2: Color::Rgb(228, 221, 205),
surface1: Color::Rgb(241, 237, 225),
surface0: Color::Rgb(250, 248, 240),
border: Color::Rgb(158, 149, 133),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.sapphire,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.blue,
high: categorical.blue,
empty: Color::Rgb(228, 221, 205),
},
stream: ThemeStream {
inflow: categorical.blue,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn black_hole() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(245, 245, 245),
flamingo: Color::Rgb(224, 224, 224),
pink: Color::Rgb(204, 204, 204),
mauve: Color::Rgb(184, 184, 184),
red: Color::Rgb(164, 164, 164),
maroon: Color::Rgb(144, 144, 144),
peach: Color::Rgb(124, 124, 124),
yellow: Color::Rgb(104, 104, 104),
green: Color::Rgb(84, 84, 84),
teal: Color::Rgb(72, 72, 72),
sky: Color::Rgb(60, 60, 60),
sapphire: Color::Rgb(48, 48, 48),
blue: Color::Rgb(36, 36, 36),
lavender: Color::Rgb(172, 172, 172),
};
Self {
name: ThemeName::BlackHole,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 5.2,
flicker_intensity: 0.18,
local_burst_duty: 0.16,
local_burst_hz: 0.75,
local_idle_intensity: 0.07,
local_burst_boost: 1.28,
wave_enabled: true,
wave_hz: 0.28,
wave_intensity: 0.16,
wave_wavelength: 64.0,
wave_angle_degrees: -34.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect {
enabled: true,
layer_mode: ParticleLayerMode::Foreground,
profile: ParticleProfile::BlackHole,
density: 0.05,
speed: 1.0,
intensity: 0.9,
},
},
semantic: ThemeSemantic {
text: Color::Rgb(234, 234, 234),
subtext1: Color::Rgb(188, 188, 188),
subtext0: Color::Rgb(144, 144, 144),
overlay0: Color::Rgb(98, 98, 98),
surface2: Color::Rgb(26, 26, 26),
surface1: Color::Rgb(12, 12, 12),
surface0: Color::Rgb(0, 0, 0),
border: Color::Rgb(116, 116, 116),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.mauve,
medium: categorical.mauve,
high: categorical.mauve,
empty: Color::Rgb(6, 8, 14),
},
stream: ThemeStream {
inflow: categorical.lavender,
outflow: categorical.teal,
},
categorical,
},
}
}
pub fn obsidian_forge() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(248, 238, 226),
flamingo: Color::Rgb(255, 171, 130),
pink: Color::Rgb(230, 165, 145),
mauve: Color::Rgb(183, 152, 216),
red: Color::Rgb(255, 108, 92),
maroon: Color::Rgb(214, 92, 78),
peach: Color::Rgb(255, 165, 88),
yellow: Color::Rgb(244, 204, 120),
green: Color::Rgb(154, 209, 126),
teal: Color::Rgb(121, 198, 176),
sky: Color::Rgb(128, 188, 226),
sapphire: Color::Rgb(103, 163, 214),
blue: Color::Rgb(84, 136, 196),
lavender: Color::Rgb(193, 176, 224),
};
Self {
name: ThemeName::ObsidianForge,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 5.8,
flicker_intensity: 0.11,
local_burst_duty: 0.12,
local_burst_hz: 0.7,
local_idle_intensity: 0.05,
local_burst_boost: 1.16,
wave_enabled: false,
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(230, 220, 206),
subtext1: Color::Rgb(191, 176, 156),
subtext0: Color::Rgb(156, 137, 114),
overlay0: Color::Rgb(108, 91, 74),
surface2: Color::Rgb(74, 62, 52),
surface1: Color::Rgb(48, 40, 34),
surface0: Color::Rgb(22, 18, 16),
border: Color::Rgb(135, 110, 88),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sapphire,
categorical.sky,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.maroon,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sapphire,
medium: categorical.sapphire,
high: categorical.peach,
empty: Color::Rgb(40, 32, 28),
},
stream: ThemeStream {
inflow: categorical.sky,
outflow: categorical.peach,
},
categorical,
},
}
}
pub fn arctic_whiteout() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(243, 248, 255),
flamingo: Color::Rgb(222, 236, 252),
pink: Color::Rgb(206, 228, 252),
mauve: Color::Rgb(177, 206, 244),
red: Color::Rgb(226, 112, 126),
maroon: Color::Rgb(197, 95, 108),
peach: Color::Rgb(235, 170, 120),
yellow: Color::Rgb(229, 205, 126),
green: Color::Rgb(132, 194, 152),
teal: Color::Rgb(103, 188, 192),
sky: Color::Rgb(116, 190, 236),
sapphire: Color::Rgb(94, 167, 219),
blue: Color::Rgb(84, 142, 206),
lavender: Color::Rgb(171, 188, 236),
};
Self {
name: ThemeName::ArcticWhiteout,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 4.6,
flicker_intensity: 0.08,
local_burst_duty: 0.16,
local_burst_hz: 0.6,
local_idle_intensity: 0.04,
local_burst_boost: 1.12,
wave_enabled: true,
wave_hz: 0.32,
wave_intensity: 0.08,
wave_wavelength: 64.0,
wave_angle_degrees: -35.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect::default(),
},
semantic: ThemeSemantic {
text: Color::Rgb(34, 56, 88),
subtext1: Color::Rgb(58, 84, 120),
subtext0: Color::Rgb(87, 111, 146),
overlay0: Color::Rgb(134, 156, 186),
surface2: Color::Rgb(214, 226, 242),
surface1: Color::Rgb(244, 248, 253),
surface0: Color::Rgb(252, 254, 255),
border: Color::Rgb(124, 146, 178),
white: Color::Black,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.sapphire,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.sapphire,
high: categorical.teal,
empty: Color::Rgb(222, 232, 245),
},
stream: ThemeStream {
inflow: categorical.sapphire,
outflow: categorical.teal,
},
categorical,
},
}
}
pub fn diamond() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(235, 245, 255),
flamingo: Color::Rgb(208, 229, 255),
pink: Color::Rgb(199, 221, 255),
mauve: Color::Rgb(170, 197, 245),
red: Color::Rgb(255, 120, 140),
maroon: Color::Rgb(214, 101, 126),
peach: Color::Rgb(255, 192, 145),
yellow: Color::Rgb(245, 220, 140),
green: Color::Rgb(144, 224, 187),
teal: Color::Rgb(124, 220, 224),
sky: Color::Rgb(150, 215, 255),
sapphire: Color::Rgb(122, 191, 245),
blue: Color::Rgb(100, 160, 235),
lavender: Color::Rgb(188, 205, 255),
};
Self {
name: ThemeName::Diamond,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 7.4,
flicker_intensity: 0.16,
local_burst_duty: 0.08,
local_burst_hz: 0.9,
local_idle_intensity: 0.02,
local_burst_boost: 1.35,
wave_enabled: true,
wave_hz: 0.44,
wave_intensity: 0.11,
wave_wavelength: 52.0,
wave_angle_degrees: 74.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect {
enabled: true,
layer_mode: ParticleLayerMode::Background,
profile: ParticleProfile::Diamond,
density: 0.006,
speed: 23.4,
intensity: 0.52,
},
},
semantic: ThemeSemantic {
text: Color::Rgb(228, 240, 255),
subtext1: Color::Rgb(184, 205, 232),
subtext0: Color::Rgb(146, 170, 202),
overlay0: Color::Rgb(93, 114, 145),
surface2: Color::Rgb(44, 60, 86),
surface1: Color::Rgb(28, 41, 64),
surface0: Color::Rgb(12, 20, 36),
border: Color::Rgb(108, 136, 176),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.red,
categorical.peach,
categorical.yellow,
categorical.green,
categorical.teal,
categorical.sky,
categorical.blue,
categorical.mauve,
],
ip_hash: [
categorical.rosewater,
categorical.sky,
categorical.sapphire,
categorical.blue,
categorical.teal,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.pink,
categorical.mauve,
categorical.lavender,
categorical.flamingo,
categorical.red,
categorical.maroon,
],
heatmap: ThemeHeatmap {
low: categorical.sky,
medium: categorical.sky,
high: categorical.mauve,
empty: Color::Rgb(28, 41, 64),
},
stream: ThemeStream {
inflow: categorical.sky,
outflow: categorical.teal,
},
categorical,
},
}
}
pub fn bioluminescent_reef() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(222, 249, 242),
flamingo: Color::Rgb(162, 234, 214),
pink: Color::Rgb(154, 220, 238),
mauve: Color::Rgb(138, 190, 231),
red: Color::Rgb(234, 112, 136),
maroon: Color::Rgb(194, 94, 119),
peach: Color::Rgb(242, 174, 122),
yellow: Color::Rgb(238, 214, 128),
green: Color::Rgb(72, 212, 174),
teal: Color::Rgb(56, 203, 196),
sky: Color::Rgb(96, 198, 236),
sapphire: Color::Rgb(84, 173, 222),
blue: Color::Rgb(72, 153, 206),
lavender: Color::Rgb(144, 172, 232),
};
Self {
name: ThemeName::BioluminescentReef,
effects: ThemeEffects {
local_enabled: true,
flicker_hz: 5.6,
flicker_intensity: 0.10,
local_burst_duty: 0.14,
local_burst_hz: 0.8,
local_idle_intensity: 0.05,
local_burst_boost: 1.18,
wave_enabled: true,
wave_hz: 0.38,
wave_intensity: 0.14,
wave_wavelength: 46.0,
wave_angle_degrees: -55.0,
wave_mode: WaveMode::Linear,
particle: ThemeParticleEffect {
enabled: true,
layer_mode: ParticleLayerMode::Background,
profile: ParticleProfile::BioluminescentReef,
density: 0.024,
speed: 0.42,
intensity: 0.72,
},
},
semantic: ThemeSemantic {
text: Color::Rgb(213, 245, 239),
subtext1: Color::Rgb(156, 223, 209),
subtext0: Color::Rgb(116, 193, 181),
overlay0: Color::Rgb(58, 112, 114),
surface2: Color::Rgb(26, 67, 74),
surface1: Color::Rgb(16, 47, 56),
surface0: Color::Rgb(8, 28, 34),
border: Color::Rgb(82, 165, 154),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.green,
categorical.yellow,
categorical.peach,
categorical.red,
categorical.pink,
categorical.mauve,
categorical.lavender,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.blue,
medium: categorical.teal,
high: categorical.green,
empty: Color::Rgb(16, 47, 56),
},
stream: ThemeStream {
inflow: categorical.sky,
outflow: categorical.green,
},
categorical,
},
}
}
pub fn sakura() -> Self {
let categorical = ThemeCategorical {
rosewater: Color::Rgb(255, 240, 246),
flamingo: Color::Rgb(255, 188, 220),
pink: Color::Rgb(255, 142, 198),
mauve: Color::Rgb(228, 154, 199),
red: Color::Rgb(214, 102, 138),
maroon: Color::Rgb(133, 84, 66),
peach: Color::Rgb(191, 138, 112),
yellow: Color::Rgb(234, 204, 163),
green: Color::Rgb(154, 184, 154),
teal: Color::Rgb(128, 174, 176),
sky: Color::Rgb(139, 186, 228),
sapphire: Color::Rgb(111, 162, 206),
blue: Color::Rgb(93, 138, 188),
lavender: Color::Rgb(208, 184, 230),
};
Self {
name: ThemeName::Sakura,
effects: ThemeEffects {
particle: ThemeParticleEffect {
enabled: true,
layer_mode: ParticleLayerMode::Background,
profile: ParticleProfile::Sakura,
density: 0.020,
speed: 0.68,
intensity: 0.75,
},
..ThemeEffects::default()
},
semantic: ThemeSemantic {
text: Color::Rgb(255, 214, 236),
subtext1: Color::Rgb(245, 182, 214),
subtext0: Color::Rgb(222, 149, 178),
overlay0: Color::Rgb(176, 108, 130),
surface2: Color::Rgb(121, 76, 82),
surface1: Color::Rgb(95, 57, 63),
surface0: Color::Rgb(74, 43, 49),
border: Color::Rgb(186, 118, 104),
white: Color::White,
},
scale: ThemeScale {
speed: [
categorical.sky,
categorical.sapphire,
categorical.blue,
categorical.teal,
categorical.pink,
categorical.mauve,
categorical.peach,
categorical.rosewater,
],
ip_hash: categorical_ip_hash(categorical),
heatmap: ThemeHeatmap {
low: categorical.sky,
medium: categorical.pink,
high: categorical.maroon,
empty: Color::Rgb(73, 55, 67),
},
stream: ThemeStream {
inflow: categorical.sky,
outflow: categorical.teal,
},
categorical,
},
}
}
}
impl Default for Theme {
fn default() -> Self {
Self::catppuccin_mocha()
}
}
fn categorical_ip_hash(categorical: ThemeCategorical) -> [Color; 14] {
[
categorical.rosewater,
categorical.flamingo,
categorical.pink,
categorical.mauve,
categorical.red,
categorical.maroon,
categorical.peach,
categorical.yellow,
categorical.green,
categorical.teal,
categorical.sky,
categorical.sapphire,
categorical.blue,
categorical.lavender,
]
}
#[cfg(test)]
mod tests {
use super::*;
fn all_theme_names() -> Vec<ThemeName> {
vec![
ThemeName::Andromeda,
ThemeName::Aurora,
ThemeName::AyuDark,
ThemeName::Bubblegum,
ThemeName::CatppuccinLatte,
ThemeName::CatppuccinMocha,
ThemeName::Cyberpunk,
ThemeName::DeepOcean,
ThemeName::DeepSky,
ThemeName::Diamond,
ThemeName::Gold,
ThemeName::Dracula,
ThemeName::EverforestDark,
ThemeName::GitHubDark,
ThemeName::GitHubLight,
ThemeName::GruvboxDark,
ThemeName::GruvboxLight,
ThemeName::Inferno,
ThemeName::Kanagawa,
ThemeName::MaterialOcean,
ThemeName::Matrix,
ThemeName::Monokai,
ThemeName::Neon,
ThemeName::Nightfox,
ThemeName::Nord,
ThemeName::OneDark,
ThemeName::ObsidianForge,
ThemeName::Oxocarbon,
ThemeName::ArcticWhiteout,
ThemeName::PaperColorLight,
ThemeName::BioluminescentReef,
ThemeName::BlackHole,
ThemeName::Rainbow,
ThemeName::RosePine,
ThemeName::SolarizedDark,
ThemeName::SolarizedLight,
ThemeName::Synthwave84,
ThemeName::TokyoNight,
ThemeName::Vesper,
ThemeName::Zenburn,
ThemeName::Sakura,
]
}
fn relative_luminance(color: Color) -> f64 {
let (r, g, b) = color_to_rgb(color);
let to_linear = |v: u8| {
let v = v as f64 / 255.0;
if v <= 0.03928 {
v / 12.92
} else {
((v + 0.055) / 1.055).powf(2.4)
}
};
let r = to_linear(r);
let g = to_linear(g);
let b = to_linear(b);
0.2126 * r + 0.7152 * g + 0.0722 * b
}
fn contrast_ratio(a: Color, b: Color) -> f64 {
let la = relative_luminance(a);
let lb = relative_luminance(b);
let (bright, dark) = if la >= lb { (la, lb) } else { (lb, la) };
(bright + 0.05) / (dark + 0.05)
}
fn color_distance(a: Color, b: Color) -> f64 {
let (ar, ag, ab) = color_to_rgb(a);
let (br, bg, bb) = color_to_rgb(b);
let dr = ar as f64 - br as f64;
let dg = ag as f64 - bg as f64;
let db = ab as f64 - bb as f64;
(dr * dr + dg * dg + db * db).sqrt()
}
#[test]
fn test_known_themes_snake_case() {
let themes = vec![
("andromeda", ThemeName::Andromeda),
("aurora", ThemeName::Aurora),
("ayu_dark", ThemeName::AyuDark),
("bubblegum", ThemeName::Bubblegum),
("catppuccin_latte", ThemeName::CatppuccinLatte),
("catppuccin_mocha", ThemeName::CatppuccinMocha),
("cyberpunk", ThemeName::Cyberpunk),
("deep_ocean", ThemeName::DeepOcean),
("deep_sky", ThemeName::DeepSky),
("diamond", ThemeName::Diamond),
("gold", ThemeName::Gold),
("dracula", ThemeName::Dracula),
("everforest_dark", ThemeName::EverforestDark),
("github_dark", ThemeName::GitHubDark),
("github_light", ThemeName::GitHubLight),
("gruvbox_dark", ThemeName::GruvboxDark),
("gruvbox_light", ThemeName::GruvboxLight),
("inferno", ThemeName::Inferno),
("kanagawa", ThemeName::Kanagawa),
("material_ocean", ThemeName::MaterialOcean),
("matrix", ThemeName::Matrix),
("monokai", ThemeName::Monokai),
("neon", ThemeName::Neon),
("nightfox", ThemeName::Nightfox),
("nord", ThemeName::Nord),
("one_dark", ThemeName::OneDark),
("obsidian_forge", ThemeName::ObsidianForge),
("oxocarbon", ThemeName::Oxocarbon),
("arctic_whiteout", ThemeName::ArcticWhiteout),
("papercolor_light", ThemeName::PaperColorLight),
("black_hole", ThemeName::BlackHole),
("bioluminescent_reef", ThemeName::BioluminescentReef),
("rainbow", ThemeName::Rainbow),
("rose_pine", ThemeName::RosePine),
("solarized_dark", ThemeName::SolarizedDark),
("solarized_light", ThemeName::SolarizedLight),
("synthwave_84", ThemeName::Synthwave84),
("tokyo_night", ThemeName::TokyoNight),
("vesper", ThemeName::Vesper),
("zenburn", ThemeName::Zenburn),
("sakura", ThemeName::Sakura),
];
for (input, expected) in themes {
let deserialized: ThemeName = serde_json::from_str(&format!("\"{}\"", input)).unwrap();
assert_eq!(deserialized, expected, "Failed for input: {}", input);
}
}
#[test]
fn test_known_themes_display_format() {
let themes = vec![
("Andromeda", ThemeName::Andromeda),
("Aurora", ThemeName::Aurora),
("Ayu Dark", ThemeName::AyuDark),
("Bubblegum", ThemeName::Bubblegum),
("Catppuccin Latte", ThemeName::CatppuccinLatte),
("Catppuccin Mocha", ThemeName::CatppuccinMocha),
("Cyberpunk", ThemeName::Cyberpunk),
("Deep Ocean", ThemeName::DeepOcean),
("Deep Sky", ThemeName::DeepSky),
("Diamond", ThemeName::Diamond),
("Gold", ThemeName::Gold),
("Dracula", ThemeName::Dracula),
("Everforest Dark", ThemeName::EverforestDark),
("GitHub Dark", ThemeName::GitHubDark),
("GitHub Light", ThemeName::GitHubLight),
("Gruvbox Dark", ThemeName::GruvboxDark),
("Gruvbox Light", ThemeName::GruvboxLight),
("Inferno", ThemeName::Inferno),
("Kanagawa", ThemeName::Kanagawa),
("Material Ocean", ThemeName::MaterialOcean),
("Matrix", ThemeName::Matrix),
("Monokai", ThemeName::Monokai),
("Neon", ThemeName::Neon),
("Nightfox", ThemeName::Nightfox),
("Nord", ThemeName::Nord),
("One Dark", ThemeName::OneDark),
("Obsidian Forge", ThemeName::ObsidianForge),
("Oxocarbon", ThemeName::Oxocarbon),
("Arctic Whiteout", ThemeName::ArcticWhiteout),
("PaperColor Light", ThemeName::PaperColorLight),
("Black Hole", ThemeName::BlackHole),
("Bioluminescent Reef", ThemeName::BioluminescentReef),
("Rainbow", ThemeName::Rainbow),
("Rose Pine", ThemeName::RosePine),
("Solarized Dark", ThemeName::SolarizedDark),
("Solarized Light", ThemeName::SolarizedLight),
("Synthwave '84", ThemeName::Synthwave84),
("Tokyo Night", ThemeName::TokyoNight),
("Vesper", ThemeName::Vesper),
("Zenburn", ThemeName::Zenburn),
("Sakura", ThemeName::Sakura),
];
for (input, expected) in themes {
let deserialized: ThemeName = serde_json::from_str(&format!("\"{}\"", input)).unwrap();
assert_eq!(deserialized, expected, "Failed for input: {}", input);
}
}
#[test]
fn test_unknown_themes_default_to_catppuccin_mocha() {
let unknown_themes = vec![
"cuppochinmocha",
"invalid_theme",
"unknown",
"",
" ",
"CatpuccinMocha",
"mocha",
"dark_theme",
];
for input in unknown_themes {
let deserialized: ThemeName = serde_json::from_str(&format!("\"{}\"", input)).unwrap();
assert_eq!(
deserialized,
ThemeName::CatppuccinMocha,
"Unknown theme '{}' should default to CatppuccinMocha",
input
);
}
}
#[test]
fn test_deprecated_theme_aliases_map_to_replacements() {
let aliases = vec![
("catppuccin", ThemeName::CatppuccinMocha),
("synthwave84", ThemeName::Synthwave84),
("tokyonight", ThemeName::TokyoNight),
("flowers", ThemeName::Sakura),
];
for (input, expected) in aliases {
let deserialized: ThemeName = serde_json::from_str(&format!("\"{}\"", input)).unwrap();
assert_eq!(
deserialized, expected,
"Deprecated alias '{}' mismatch",
input
);
}
}
#[test]
fn test_theme_deserialize_non_string_types_fallback_to_default() {
let invalid_types = vec!["123", "true", "null", "[]", "{}"];
for input in invalid_types {
let deserialized: ThemeName = serde_json::from_str(input).unwrap();
assert_eq!(
deserialized,
ThemeName::CatppuccinMocha,
"Non-string value '{}' should fallback to default",
input
);
}
}
#[test]
fn test_theme_name_normalization_accepts_case_and_delimiter_variants() {
let variants = vec![
("TOKYO-NIGHT", ThemeName::TokyoNight),
(" synthwave '84 ", ThemeName::Synthwave84),
("GitHub_Dark", ThemeName::GitHubDark),
];
for (input, expected) in variants {
let deserialized: ThemeName = serde_json::from_str(&format!("\"{}\"", input)).unwrap();
assert_eq!(deserialized, expected, "Variant '{}' mismatch", input);
}
}
#[test]
fn test_theme_default_is_catppuccin_mocha() {
assert_eq!(ThemeName::default(), ThemeName::CatppuccinMocha);
}
#[test]
fn test_theme_name_roundtrip() {
for theme in all_theme_names() {
let serialized = serde_json::to_string(&theme).unwrap();
let deserialized: ThemeName = serde_json::from_str(&serialized).unwrap();
assert_eq!(theme, deserialized);
}
}
#[test]
fn test_theme_semantic_readability_guards() {
for name in all_theme_names() {
let theme = Theme::builtin(name);
let surface0 = theme.semantic.surface0;
let text_contrast = contrast_ratio(theme.semantic.text, surface0);
assert!(
text_contrast >= 4.5,
"{name}: text contrast too low ({text_contrast:.2})"
);
let subtext_contrast = contrast_ratio(theme.semantic.subtext0, surface0);
assert!(
subtext_contrast >= 3.0,
"{name}: subtext0 contrast too low ({subtext_contrast:.2})"
);
let border_contrast = contrast_ratio(theme.semantic.border, surface0);
assert!(
border_contrast >= 2.0,
"{name}: border contrast too low ({border_contrast:.2})"
);
let surface_separation = contrast_ratio(theme.semantic.surface2, surface0);
assert!(
surface_separation >= 1.2,
"{name}: surface2 too close to surface0 ({surface_separation:.2})"
);
}
}
#[test]
fn test_theme_status_colors_are_distinct() {
for name in all_theme_names() {
let theme = Theme::builtin(name);
let red = theme.scale.categorical.red;
let yellow = theme.scale.categorical.yellow;
let green = theme.scale.categorical.green;
let red_yellow = color_distance(red, yellow);
let red_green = color_distance(red, green);
let yellow_green = color_distance(yellow, green);
assert!(
red_yellow >= 20.0,
"{name}: red/yellow too similar ({red_yellow:.1})"
);
assert!(
red_green >= 20.0,
"{name}: red/green too similar ({red_green:.1})"
);
assert!(
yellow_green >= 20.0,
"{name}: yellow/green too similar ({yellow_green:.1})"
);
}
}
#[test]
fn test_theme_effects_within_comfort_bounds() {
for name in all_theme_names() {
let theme = Theme::builtin(name);
let effects = theme.effects;
assert!(
effects.flicker_hz <= 12.0,
"{name}: flicker_hz too high ({:.2})",
effects.flicker_hz
);
assert!(
effects.flicker_intensity <= 0.35,
"{name}: flicker_intensity too high ({:.2})",
effects.flicker_intensity
);
assert!(
effects.local_burst_boost <= 1.5,
"{name}: local_burst_boost too high ({:.2})",
effects.local_burst_boost
);
assert!(
effects.wave_intensity <= 0.2,
"{name}: wave_intensity too high ({:.2})",
effects.wave_intensity
);
}
}
#[test]
fn test_theme_effects_enabled_flag_tracks_presence_of_effects() {
let static_theme = Theme::builtin(ThemeName::Nord);
let effect_theme = Theme::builtin(ThemeName::Diamond);
assert!(
!static_theme.effects.enabled(),
"Nord should report effects disabled"
);
assert!(
effect_theme.effects.enabled(),
"Diamond should report effects enabled"
);
}
#[test]
fn test_particle_themes_enable_particle_profiles() {
let sakura = Theme::builtin(ThemeName::Sakura);
let matrix = Theme::builtin(ThemeName::Matrix);
let diamond = Theme::builtin(ThemeName::Diamond);
let reef = Theme::builtin(ThemeName::BioluminescentReef);
let black_hole = Theme::builtin(ThemeName::BlackHole);
assert!(sakura.effects.particle.enabled);
assert_eq!(sakura.effects.particle.profile, ParticleProfile::Sakura);
assert_eq!(
sakura.effects.particle.layer_mode,
ParticleLayerMode::Background
);
assert!(matrix.effects.particle.enabled);
assert_eq!(matrix.effects.particle.profile, ParticleProfile::Matrix);
assert_eq!(
matrix.effects.particle.layer_mode,
ParticleLayerMode::Background
);
assert!(diamond.effects.particle.enabled);
assert_eq!(diamond.effects.particle.profile, ParticleProfile::Diamond);
assert_eq!(
diamond.effects.particle.layer_mode,
ParticleLayerMode::Background
);
assert!(reef.effects.particle.enabled);
assert_eq!(
reef.effects.particle.profile,
ParticleProfile::BioluminescentReef
);
assert_eq!(
reef.effects.particle.layer_mode,
ParticleLayerMode::Background
);
assert!(black_hole.effects.particle.enabled);
assert_eq!(
black_hole.effects.particle.profile,
ParticleProfile::BlackHole
);
assert_eq!(
black_hole.effects.particle.layer_mode,
ParticleLayerMode::Foreground
);
}
#[test]
fn test_non_particle_theme_keeps_particle_effects_disabled() {
let nord = Theme::builtin(ThemeName::Nord);
assert!(!nord.effects.particle.enabled);
assert_eq!(nord.effects.particle.layer_mode, ParticleLayerMode::None);
assert_eq!(nord.effects.particle.profile, ParticleProfile::None);
}
}