1use colored::Colorize;
7use indicatif::{ProgressBar, ProgressStyle};
8use parking_lot::Mutex;
9use std::fmt::Write;
10use std::time::Duration;
11
12use crate::theme;
13use crate::theme::gradient_string;
14use crate::theme::names::{gradients as gradient_names, tokens};
15
16pub mod rgb {
23 use crate::theme;
24 use crate::theme::names::tokens;
25
26 pub fn accent_primary() -> (u8, u8, u8) {
28 theme::current()
29 .color(tokens::ACCENT_PRIMARY)
30 .to_rgb_tuple()
31 }
32
33 pub fn accent_secondary() -> (u8, u8, u8) {
35 theme::current()
36 .color(tokens::ACCENT_SECONDARY)
37 .to_rgb_tuple()
38 }
39
40 pub fn accent_tertiary() -> (u8, u8, u8) {
42 theme::current()
43 .color(tokens::ACCENT_TERTIARY)
44 .to_rgb_tuple()
45 }
46
47 pub fn warning() -> (u8, u8, u8) {
49 theme::current().color(tokens::WARNING).to_rgb_tuple()
50 }
51
52 pub fn success() -> (u8, u8, u8) {
54 theme::current().color(tokens::SUCCESS).to_rgb_tuple()
55 }
56
57 pub fn error() -> (u8, u8, u8) {
59 theme::current().color(tokens::ERROR).to_rgb_tuple()
60 }
61
62 pub fn text_primary() -> (u8, u8, u8) {
64 theme::current().color(tokens::TEXT_PRIMARY).to_rgb_tuple()
65 }
66
67 pub fn text_secondary() -> (u8, u8, u8) {
69 theme::current()
70 .color(tokens::TEXT_SECONDARY)
71 .to_rgb_tuple()
72 }
73
74 pub fn text_muted() -> (u8, u8, u8) {
76 theme::current().color(tokens::TEXT_MUTED).to_rgb_tuple()
77 }
78
79 pub fn text_dim() -> (u8, u8, u8) {
81 theme::current().color(tokens::TEXT_DIM).to_rgb_tuple()
82 }
83}
84
85static QUIET_MODE: std::sync::LazyLock<Mutex<bool>> =
87 std::sync::LazyLock::new(|| Mutex::new(false));
88
89pub fn set_quiet_mode(enabled: bool) {
91 let mut quiet_mode = QUIET_MODE.lock();
92 *quiet_mode = enabled;
93}
94
95pub fn is_quiet_mode() -> bool {
97 *QUIET_MODE.lock()
98}
99
100pub fn create_spinner(message: &str) -> ProgressBar {
101 if is_quiet_mode() {
103 return ProgressBar::hidden();
104 }
105
106 let pb = ProgressBar::new_spinner();
107
108 if crate::agents::status::is_agent_mode_enabled() {
110 pb.set_style(
111 ProgressStyle::default_spinner()
112 .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏")
113 .template("{spinner:.bright_cyan.bold} {msg}")
114 .expect("Could not set spinner style"),
115 );
116
117 pb.set_message("◎ Iris initializing...");
119
120 let pb_clone = pb.clone();
122 tokio::spawn(async move {
123 let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(200));
124 loop {
125 interval.tick().await;
126 let status_message = crate::agents::status::IRIS_STATUS.get_for_spinner();
127 pb_clone.set_message(status_message.text);
128 }
129 });
130
131 pb.enable_steady_tick(Duration::from_millis(100));
132 } else {
133 pb.set_style(
134 ProgressStyle::default_spinner()
135 .tick_chars("✦✧✶✷✸✹✺✻✼✽")
136 .template("{spinner} {msg}")
137 .expect("Could not set spinner style"),
138 );
139 pb.set_message(message.to_string());
140 pb.enable_steady_tick(Duration::from_millis(100));
141 }
142
143 pb
144}
145
146pub fn print_info(message: &str) {
148 if !is_quiet_mode() {
149 let color = theme::current().color(tokens::INFO);
150 println!("{}", message.truecolor(color.r, color.g, color.b).bold());
151 }
152}
153
154pub fn print_warning(message: &str) {
156 if !is_quiet_mode() {
157 let color = theme::current().color(tokens::WARNING);
158 println!("{}", message.truecolor(color.r, color.g, color.b).bold());
159 }
160}
161
162pub fn print_error(message: &str) {
164 let color = theme::current().color(tokens::ERROR);
166 eprintln!("{}", message.truecolor(color.r, color.g, color.b).bold());
167}
168
169pub fn print_success(message: &str) {
171 if !is_quiet_mode() {
172 let color = theme::current().color(tokens::SUCCESS);
173 println!("{}", message.truecolor(color.r, color.g, color.b).bold());
174 }
175}
176
177pub fn print_version(version: &str) {
178 if !is_quiet_mode() {
179 let t = theme::current();
180 let purple = t.color(tokens::ACCENT_PRIMARY);
181 let cyan = t.color(tokens::ACCENT_SECONDARY);
182 let green = t.color(tokens::SUCCESS);
183
184 println!(
185 "{} {} {}",
186 "🔮 Git-Iris".truecolor(purple.r, purple.g, purple.b).bold(),
187 "version".truecolor(cyan.r, cyan.g, cyan.b),
188 version.truecolor(green.r, green.g, green.b)
189 );
190 }
191}
192
193pub fn print_bordered_content(content: &str) {
195 if !is_quiet_mode() {
196 let color = theme::current().color(tokens::ACCENT_PRIMARY);
197 println!("{}", "━".repeat(50).truecolor(color.r, color.g, color.b));
198 println!("{content}");
199 println!("{}", "━".repeat(50).truecolor(color.r, color.g, color.b));
200 }
201}
202
203pub fn print_message(message: &str) {
205 if !is_quiet_mode() {
206 println!("{message}");
207 }
208}
209
210pub fn print_newline() {
212 if !is_quiet_mode() {
213 println!();
214 }
215}
216
217pub fn create_gradient_text(text: &str) -> String {
219 if let Some(gradient) = theme::current().get_gradient(gradient_names::PRIMARY) {
220 gradient_string(text, gradient)
221 } else {
222 let gradient = vec![
224 (225, 53, 255), (200, 100, 255), (180, 150, 250), (150, 200, 245), (128, 255, 234), ];
230 apply_gradient(text, &gradient)
231 }
232}
233
234pub fn create_secondary_gradient_text(text: &str) -> String {
236 if let Some(gradient) = theme::current().get_gradient(gradient_names::WARM) {
237 gradient_string(text, gradient)
238 } else {
239 let gradient = vec![
241 (255, 106, 193), (255, 150, 180), (255, 200, 160), (248, 230, 140), (241, 250, 140), ];
247 apply_gradient(text, &gradient)
248 }
249}
250
251fn apply_gradient(text: &str, gradient: &[(u8, u8, u8)]) -> String {
252 let chars: Vec<char> = text.chars().collect();
253 let chars_len = chars.len();
254 let gradient_len = gradient.len();
255
256 let mut result = String::new();
257
258 if chars_len == 0 || gradient_len == 0 {
259 return result;
260 }
261
262 chars.iter().enumerate().fold(&mut result, |acc, (i, &c)| {
263 let index = if chars_len == 1 {
264 0
265 } else {
266 i * (gradient_len - 1) / (chars_len - 1)
267 };
268 let (r, g, b) = gradient[index];
269 write!(acc, "{}", c.to_string().truecolor(r, g, b)).expect("writing to string cannot fail");
270 acc
271 });
272
273 result
274}