1use ratatui::style::Color;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum ThemeMode {
5 Dark,
6 Light,
7 None,
8}
9
10impl ThemeMode {
11 pub fn parse(s: &str) -> Option<Self> {
12 match s.to_lowercase().as_str() {
13 "dark" => Some(Self::Dark),
14 "light" => Some(Self::Light),
15 "none" => Some(Self::None),
16 _ => None,
17 }
18 }
19
20 pub fn detect(cli_theme: Option<&str>) -> Self {
22 if let Some(t) = cli_theme.and_then(Self::parse) {
23 return t;
24 }
25 if std::env::var_os("NO_COLOR").is_some() {
26 return Self::None;
27 }
28 Self::Dark
29 }
30}
31
32#[derive(Debug, Clone, Copy)]
34pub struct Theme {
35 #[allow(dead_code)]
36 pub mode: ThemeMode,
37
38 pub status_needs_input: Color,
40 pub status_processing: Color,
41 pub status_waiting: Color,
42 pub status_unknown: Color,
43 pub status_idle: Color,
44 pub status_finished: Color,
45
46 pub border: Color,
48 pub header: Color,
49 pub footer: Color,
50 pub highlight_key: Color,
51 pub text_primary: Color,
52 pub text_muted: Color,
53
54 pub cost: Color,
56 pub cost_warning: Color,
57 pub cost_danger: Color,
58 pub context_ok: Color,
59 pub context_warning: Color,
60 pub context_danger: Color,
61 pub burn_rate_low: Color,
62 pub burn_rate_mid: Color,
63 pub burn_rate_high: Color,
64 pub sparkline: Color,
65 pub input_accent: Color,
66 pub success: Color,
67 pub error: Color,
68}
69
70impl Theme {
71 pub fn from_mode(mode: ThemeMode) -> Self {
72 match mode {
73 ThemeMode::Dark => Self::dark(),
74 ThemeMode::Light => Self::light(),
75 ThemeMode::None => Self::none(),
76 }
77 }
78
79 fn dark() -> Self {
80 Self {
81 mode: ThemeMode::Dark,
82 status_needs_input: Color::Magenta,
83 status_processing: Color::Green,
84 status_waiting: Color::Yellow,
85 status_unknown: Color::Blue,
86 status_idle: Color::DarkGray,
87 status_finished: Color::Red,
88 border: Color::DarkGray,
89 header: Color::Cyan,
90 footer: Color::DarkGray,
91 highlight_key: Color::Yellow,
92 text_primary: Color::White,
93 text_muted: Color::DarkGray,
94 cost: Color::Yellow,
95 cost_warning: Color::LightRed,
96 cost_danger: Color::Red,
97 context_ok: Color::Green,
98 context_warning: Color::Yellow,
99 context_danger: Color::Red,
100 burn_rate_low: Color::DarkGray,
101 burn_rate_mid: Color::Yellow,
102 burn_rate_high: Color::Red,
103 sparkline: Color::Blue,
104 input_accent: Color::Cyan,
105 success: Color::Green,
106 error: Color::Red,
107 }
108 }
109
110 fn light() -> Self {
111 Self {
112 mode: ThemeMode::Light,
113 status_needs_input: Color::Magenta,
114 status_processing: Color::Blue,
115 status_waiting: Color::Rgb(180, 140, 0), status_unknown: Color::Gray,
117 status_idle: Color::Gray,
118 status_finished: Color::Red,
119 border: Color::Gray,
120 header: Color::Blue,
121 footer: Color::Gray,
122 highlight_key: Color::Blue,
123 text_primary: Color::Black,
124 text_muted: Color::Gray,
125 cost: Color::Rgb(180, 140, 0),
126 cost_warning: Color::Red,
127 cost_danger: Color::LightRed,
128 context_ok: Color::Blue,
129 context_warning: Color::Rgb(180, 140, 0),
130 context_danger: Color::Red,
131 burn_rate_low: Color::Gray,
132 burn_rate_mid: Color::Rgb(180, 140, 0),
133 burn_rate_high: Color::Red,
134 sparkline: Color::Blue,
135 input_accent: Color::Blue,
136 success: Color::Blue,
137 error: Color::Red,
138 }
139 }
140
141 fn none() -> Self {
142 Self {
144 mode: ThemeMode::None,
145 status_needs_input: Color::Reset,
146 status_processing: Color::Reset,
147 status_waiting: Color::Reset,
148 status_unknown: Color::Reset,
149 status_idle: Color::Reset,
150 status_finished: Color::Reset,
151 border: Color::Reset,
152 header: Color::Reset,
153 footer: Color::Reset,
154 highlight_key: Color::Reset,
155 text_primary: Color::Reset,
156 text_muted: Color::Reset,
157 cost: Color::Reset,
158 cost_warning: Color::Reset,
159 cost_danger: Color::Reset,
160 context_ok: Color::Reset,
161 context_warning: Color::Reset,
162 context_danger: Color::Reset,
163 burn_rate_low: Color::Reset,
164 burn_rate_mid: Color::Reset,
165 burn_rate_high: Color::Reset,
166 sparkline: Color::Reset,
167 input_accent: Color::Reset,
168 success: Color::Reset,
169 error: Color::Reset,
170 }
171 }
172
173 pub fn status_color(&self, status: &crate::session::SessionStatus) -> Color {
175 use crate::session::SessionStatus;
176 match status {
177 SessionStatus::NeedsInput => self.status_needs_input,
178 SessionStatus::Processing => self.status_processing,
179 SessionStatus::WaitingInput => self.status_waiting,
180 SessionStatus::Unknown => self.status_unknown,
181 SessionStatus::Idle => self.status_idle,
182 SessionStatus::Finished => self.status_finished,
183 }
184 }
185}