1use std::io::{self, IsTerminal, Write};
2
3const LOGO: [&str; 6] = [
4 r" ██╗ ███████╗ █████╗ ███╗ ██╗ ██████╗████████╗██╗ ██╗",
5 r" ██║ ██╔════╝██╔══██╗████╗ ██║ ██╔════╝╚══██╔══╝╚██╗██╔╝",
6 r" ██║ █████╗ ███████║██╔██╗ ██║ ██║ ██║ ╚███╔╝ ",
7 r" ██║ ██╔══╝ ██╔══██║██║╚██╗██║ ██║ ██║ ██╔██╗ ",
8 r" ███████╗███████╗██║ ██║██║ ╚████║ ╚██████╗ ██║ ██╔╝ ██╗",
9 r" ╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝",
10];
11
12const TAGLINE: &str = "The Intelligence Layer for AI Coding";
13
14fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (u8, u8, u8) {
15 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
16 let h2 = h / 60.0;
17 let x = c * (1.0 - (h2 % 2.0 - 1.0).abs());
18 let (r1, g1, b1) = match h2 as u32 {
19 0 => (c, x, 0.0),
20 1 => (x, c, 0.0),
21 2 => (0.0, c, x),
22 3 => (0.0, x, c),
23 4 => (x, 0.0, c),
24 _ => (c, 0.0, x),
25 };
26 let m = l - c / 2.0;
27 (
28 ((r1 + m) * 255.0) as u8,
29 ((g1 + m) * 255.0) as u8,
30 ((b1 + m) * 255.0) as u8,
31 )
32}
33
34fn rgb_fg(r: u8, g: u8, b: u8) -> String {
35 format!("\x1b[38;2;{r};{g};{b}m")
36}
37
38pub fn print_logo_animated() {
39 if !io::stdout().is_terminal() {
40 print_logo_static();
41 return;
42 }
43
44 let mut stdout = io::stdout();
45 let frames = 32;
46 let frame_ms = 50;
47
48 for frame in 0..frames {
49 if frame > 0 {
50 print!("\x1b[{}A", LOGO.len() + 2);
51 }
52
53 let base_hue = (frame as f64 / frames as f64) * 360.0;
54
55 for (i, line) in LOGO.iter().enumerate() {
56 let chars: Vec<char> = line.chars().collect();
57 let mut buf = String::with_capacity(chars.len() * 20);
58
59 for (j, ch) in chars.iter().enumerate() {
60 if *ch == ' ' {
61 buf.push(' ');
62 continue;
63 }
64 let hue = (base_hue + (j as f64 * 2.5) + (i as f64 * 15.0)) % 360.0;
65 let (r, g, b) = hsl_to_rgb(hue, 0.85, 0.65);
66 buf.push_str(&rgb_fg(r, g, b));
67 buf.push(*ch);
68 }
69 buf.push_str("\x1b[0m");
70 let _ = writeln!(stdout, "{buf}");
71 }
72
73 let tag_hue = (base_hue + 120.0) % 360.0;
74 let (tr, tg, tb) = hsl_to_rgb(tag_hue, 0.5, 0.55);
75 let _ = writeln!(
76 stdout,
77 "{} {TAGLINE}\x1b[0m",
78 rgb_fg(tr, tg, tb)
79 );
80 let _ = writeln!(stdout);
81
82 let _ = stdout.flush();
83 std::thread::sleep(std::time::Duration::from_millis(frame_ms));
84 }
85
86 print!("\x1b[{}A", LOGO.len() + 2);
87 print_logo_final_gradient();
88}
89
90pub fn print_logo_static() {
91 print_logo_final_gradient();
92}
93
94fn print_logo_final_gradient() {
95 let mut stdout = io::stdout();
96
97 for (i, line) in LOGO.iter().enumerate() {
98 let chars: Vec<char> = line.chars().collect();
99 let mut buf = String::with_capacity(chars.len() * 20);
100
101 for (j, ch) in chars.iter().enumerate() {
102 if *ch == ' ' {
103 buf.push(' ');
104 continue;
105 }
106 let t = if chars.len() > 1 {
107 j as f64 / (chars.len() - 1) as f64
108 } else {
109 0.5
110 };
111 let row_shift = i as f64 * 8.0;
112 let hue = 160.0 + t * 80.0 + row_shift;
113 let (r, g, b) = hsl_to_rgb(hue % 360.0, 0.75, 0.6);
114 buf.push_str(&rgb_fg(r, g, b));
115 buf.push(*ch);
116 }
117 buf.push_str("\x1b[0m");
118 let _ = writeln!(stdout, "{buf}");
119 }
120
121 let (tr, tg, tb) = hsl_to_rgb(200.0, 0.4, 0.55);
122 let _ = writeln!(
123 stdout,
124 "{} {TAGLINE}\x1b[0m",
125 rgb_fg(tr, tg, tb)
126 );
127 let _ = writeln!(stdout);
128 let _ = stdout.flush();
129}
130
131pub fn print_command_box() {
132 let dim = "\x1b[2m";
133 let rst = "\x1b[0m";
134 let bold = "\x1b[1m";
135 let cyan = "\x1b[36m";
136 let green = "\x1b[32m";
137
138 println!(" {dim}┌─────────────────────────────────────────────────────────┐{rst}");
139 println!(
140 " {dim}│{rst} {cyan}{bold}lean-ctx gain{rst} {dim}Token savings dashboard{rst} {dim}│{rst}"
141 );
142 println!(
143 " {dim}│{rst} {cyan}{bold}lean-ctx dashboard{rst} {dim}Web analytics (browser){rst} {dim}│{rst}"
144 );
145 println!(
146 " {dim}│{rst} {cyan}{bold}lean-ctx benchmark{rst} {dim}Test compression quality{rst} {dim}│{rst}"
147 );
148 println!(
149 " {dim}│{rst} {cyan}{bold}lean-ctx config{rst} {dim}Edit settings{rst} {dim}│{rst}"
150 );
151 println!(
152 " {dim}│{rst} {cyan}{bold}lean-ctx doctor{rst} {dim}Verify installation{rst} {dim}│{rst}"
153 );
154 println!(
155 " {dim}│{rst} {cyan}{bold}lean-ctx update{rst} {dim}Self-update to latest{rst} {dim}│{rst}"
156 );
157 println!(
158 " {dim}│{rst} {cyan}{bold}lean-ctx off{rst} / {cyan}{bold}on{rst} {dim}Toggle compression{rst} {dim}│{rst}"
159 );
160 println!(
161 " {dim}│{rst} {cyan}{bold}lean-ctx uninstall{rst} {dim}Clean removal{rst} {dim}│{rst}"
162 );
163 println!(" {dim}└─────────────────────────────────────────────────────────┘{rst}");
164 println!(" {green}Ready!{rst} Your next AI command will be automatically optimized.");
165 println!(" {dim}Docs: https://leanctx.com/docs{rst}");
166 println!();
167}
168
169pub fn print_step_header(step: u8, total: u8, title: &str) {
170 let dim = "\x1b[2m";
171 let bold = "\x1b[1m";
172 let cyan = "\x1b[36m";
173 let rst = "\x1b[0m";
174 println!();
175 println!(" {cyan}{bold}[{step}/{total}]{rst} {bold}{title}{rst}");
176 println!(" {dim}─────────────────────────────────────────────────────{rst}");
177}
178
179pub fn print_status_ok(msg: &str) {
180 println!(" \x1b[32m✓\x1b[0m {msg}");
181}
182
183pub fn print_status_skip(msg: &str) {
184 println!(" \x1b[2m○\x1b[0m \x1b[2m{msg}\x1b[0m");
185}
186
187pub fn print_status_new(msg: &str) {
188 println!(" \x1b[1;32m✓\x1b[0m \x1b[1m{msg}\x1b[0m");
189}
190
191pub fn print_status_warn(msg: &str) {
192 println!(" \x1b[33m⚠\x1b[0m {msg}");
193}
194
195pub fn spinner_tick(msg: &str, frame: usize) {
196 let frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
197 let ch = frames[frame % frames.len()];
198 print!("\r \x1b[36m{ch}\x1b[0m {msg}");
199 let _ = io::stdout().flush();
200}
201
202pub fn spinner_done(msg: &str) {
203 print!("\r \x1b[32m✓\x1b[0m {msg}\x1b[K\n");
204 let _ = io::stdout().flush();
205}
206
207pub fn print_setup_header() {
208 let dim = "\x1b[2m";
209 let bold = "\x1b[1m";
210 let green = "\x1b[32m";
211 let rst = "\x1b[0m";
212 println!();
213 println!(" {dim}╭──────────────────────────────────────────╮{rst}");
214 println!(
215 " {dim}│{rst} {green}{bold}◆ lean-ctx setup{rst} {dim}│{rst}"
216 );
217 println!(" {dim}│{rst} {dim}Configuring your development environment{rst} {dim}│{rst}");
218 println!(" {dim}╰──────────────────────────────────────────╯{rst}");
219 println!();
220}