1use ratatui::style::Color;
2use std::sync::{OnceLock, atomic::{AtomicU16, Ordering}};
3
4const DEFAULT_ROOT_HUE: u16 = 217;
6static ROOT_HUE: AtomicU16 = AtomicU16::new(0);
7static ROOT_HUE_ENV: OnceLock<f32> = OnceLock::new();
8
9pub fn text_primary() -> Color {
10 hsl(hue_jing(), 0.35, 0.72)
11}
12
13pub fn text_secondary() -> Color {
14 hsl(hue_qi(), 0.40, 0.70)
15}
16
17pub fn text_accent() -> Color {
18 hsl(hue_shen(), 0.45, 0.76)
19}
20
21pub fn text_fade(t: f32) -> Color {
22 let t = t.clamp(0.0, 1.0);
23 let sat = 0.08 + 0.20 * t;
24 let light = 0.18 + 0.35 * t;
25 hsl(hue_jing(), sat, light)
26}
27
28pub fn text_warning() -> Color {
29 hsl(12.0, 0.55, 0.62)
30}
31
32pub fn aura_color(energy: f32, noise: f32, shimmer: f32) -> Color {
33 let t = (energy * 0.35 + noise * 0.45 + shimmer * 0.20).clamp(0.0, 1.0);
34
35 let hue = if t < 0.50 {
37 let u = t / 0.50;
38 lerp_hue(hue_jing(), hue_qi(), u)
39 } else if t < 0.88 {
40 let u = (t - 0.50) / 0.38;
41 lerp_hue(hue_qi(), hue_shen(), u)
42 } else {
43 let u = (t - 0.88) / 0.12;
44 lerp_hue(hue_shen(), hue_jing(), u)
45 };
46
47 let sat = (0.20 + 0.45 * energy + 0.18 * (noise - 0.5) + 0.10 * (shimmer - 0.5)).clamp(0.12, 0.72);
48 let light = (0.30 + 0.40 * energy + 0.12 * (noise - 0.5) + 0.10 * (shimmer - 0.5)).clamp(0.20, 0.78);
49 hsl(hue, sat, light)
50}
51
52pub fn hsl(h_deg: f32, s: f32, l: f32) -> Color {
53 let h = (h_deg % 360.0 + 360.0) % 360.0;
54 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
55 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
56 let m = l - c / 2.0;
57
58 let (r1, g1, b1) = match h {
59 h if h < 60.0 => (c, x, 0.0),
60 h if h < 120.0 => (x, c, 0.0),
61 h if h < 180.0 => (0.0, c, x),
62 h if h < 240.0 => (0.0, x, c),
63 h if h < 300.0 => (x, 0.0, c),
64 _ => (c, 0.0, x),
65 };
66
67 let r = ((r1 + m) * 255.0).round().clamp(0.0, 255.0) as u8;
68 let g = ((g1 + m) * 255.0).round().clamp(0.0, 255.0) as u8;
69 let b = ((b1 + m) * 255.0).round().clamp(0.0, 255.0) as u8;
70
71 Color::Rgb(r, g, b)
72}
73
74fn lerp_hue(a: f32, b: f32, t: f32) -> f32 {
75 let mut d = (b - a) % 360.0;
76 if d > 180.0 {
77 d -= 360.0;
78 } else if d < -180.0 {
79 d += 360.0;
80 }
81 (a + d * t + 360.0) % 360.0
82}
83
84fn root_hue() -> f32 {
85 let override_hue = ROOT_HUE.load(Ordering::Relaxed);
86 if override_hue > 0 {
87 return override_hue as f32;
88 }
89 *ROOT_HUE_ENV.get_or_init(|| {
90 let val = std::env::var("RIPL_ROOT_HUE")
91 .ok()
92 .and_then(|v| v.parse::<f32>().ok())
93 .unwrap_or(DEFAULT_ROOT_HUE as f32);
94 val.clamp(1.0, 360.0)
95 })
96}
97
98pub fn set_root_hue(value: u16) {
99 let v = value.clamp(1, 360);
100 ROOT_HUE.store(v, Ordering::Relaxed);
101}
102
103pub fn current_root_hue() -> u16 {
104 let override_hue = ROOT_HUE.load(Ordering::Relaxed);
105 if override_hue > 0 {
106 return override_hue;
107 }
108 let val = std::env::var("RIPL_ROOT_HUE")
109 .ok()
110 .and_then(|v| v.parse::<u16>().ok())
111 .unwrap_or(DEFAULT_ROOT_HUE);
112 val.clamp(1, 360)
113}
114
115fn hue_jing() -> f32 {
116 root_hue()
117}
118
119fn hue_qi() -> f32 {
120 (root_hue() + 222.5) % 360.0
121}
122
123fn hue_shen() -> f32 {
124 (root_hue() + 137.5) % 360.0
125}