tempo_cli/cli/
formatter.rs

1pub struct CliFormatter;
2
3impl CliFormatter {
4    pub fn print_section_header(title: &str) {
5        println!("\n{}", ansi_color("clean_blue", title, true));
6        println!("{}", "─".repeat(title.len()).dimmed());
7    }
8
9    pub fn print_field(label: &str, value: &str, color: Option<&str>) {
10        let colored_value = match color {
11            Some(c) => ansi_color(c, value, false),
12            None => value.to_string(),
13        };
14        println!(
15            "{} {:<20} {}",
16            "│".dimmed(),
17            format!("{}:", label).dimmed(),
18            colored_value
19        );
20    }
21
22    pub fn print_field_bold(label: &str, value: &str, color: Option<&str>) {
23        let colored_value = match color {
24            Some(c) => ansi_color(c, value, true),
25            None => bold(value),
26        };
27        println!(
28            "{} {:<20} {}",
29            "│".dimmed(),
30            format!("{}:", label).dimmed(),
31            colored_value
32        );
33    }
34
35    pub fn print_status(status: &str, is_active: bool) {
36        let (symbol, color) = if is_active {
37            ("●", "accent")
38        } else {
39            ("○", "red")
40        };
41        println!(
42            "{} {:<20} {} {}",
43            "│".dimmed(),
44            "Status:".dimmed(),
45            ansi_color(color, symbol, false),
46            ansi_color(color, status, true)
47        );
48    }
49
50    pub fn print_summary(title: &str, total: &str) {
51        println!("\n{}", ansi_color("white", title, true));
52        println!("  {}", ansi_color("green", total, true));
53    }
54
55    pub fn print_project_entry(name: &str, duration: &str) {
56        println!(
57            "  {:<25} {}",
58            ansi_color("clean_blue", &truncate_string(name, 25), true),
59            ansi_color("accent", duration, true)
60        );
61    }
62
63    pub fn print_context_entry(context: &str, duration: &str) {
64        let color = get_context_color(context);
65        println!(
66            "    {:<20} {}",
67            ansi_color(color, &format!("├─ {}", context), false),
68            ansi_color("accent", duration, false)
69        );
70    }
71
72    pub fn print_session_entry(
73        _session_id: Option<i64>,
74        project: &str,
75        duration: &str,
76        status: &str,
77        timestamp: &str,
78    ) {
79        let status_symbol = if status == "active" { "●" } else { "○" };
80        let status_color = if status == "active" { "accent" } else { "gray" };
81
82        println!(
83            "  {} {:<20} {:<15} {}",
84            ansi_color(status_color, status_symbol, false),
85            ansi_color("clean_blue", &truncate_string(project, 20), false),
86            ansi_color("accent", duration, false),
87            timestamp.dimmed()
88        );
89    }
90
91    pub fn print_empty_state(message: &str) {
92        println!("\n  {}", message.dimmed());
93    }
94
95    pub fn print_error(message: &str) {
96        println!(
97            "  {} {}",
98            ansi_color("red", "✗", true),
99            ansi_color("red", message, false)
100        );
101    }
102
103    pub fn print_success(message: &str) {
104        println!(
105            "  {} {}",
106            ansi_color("accent", "✓", true),
107            ansi_color("accent", message, false)
108        );
109    }
110
111    pub fn print_warning(message: &str) {
112        println!(
113            "  {} {}",
114            ansi_color("yellow", "⚠", true),
115            ansi_color("yellow", message, false)
116        );
117    }
118
119    pub fn print_info(message: &str) {
120        println!("  {} {}", ansi_color("clean_blue", "ℹ", true), message);
121    }
122
123    pub fn print_block_line(label: &str, value: &str) {
124        println!(
125            "{} {:<20} {}",
126            "│".dimmed(),
127            format!("{}:", label).dimmed(),
128            value
129        );
130    }
131
132    pub fn print_daemon_start(version: &str) {
133        println!(
134            "{}",
135            format!("Starting Tempo Daemon (v{})...", version).dimmed()
136        );
137    }
138
139    pub fn print_daemon_success(pid: u32, info: &str) {
140        let msg = format!("Daemon active. PID: {} [{}]", pid, info);
141        println!(
142            "{} {}",
143            ansi_color("accent", "✓", true),
144            ansi_color("accent", &msg, false)
145        );
146    }
147}
148
149// Helper functions
150pub fn ansi_color(color: &str, text: &str, bold: bool) -> String {
151    let color_code = match color {
152        "red" => "38;2;243;139;168",        // #F38BA8 (ERROR)
153        "green" => "38;2;166;227;161",      // #A6E3A1 (SUCCESS) 
154        "yellow" => "38;2;249;226;175",     // #F9E2AF (WARNING)
155        "blue" => "38;2;137;180;250",       // #89B4FA (PRIMARY_DASHBOARD)
156        "magenta" => "38;2;203;166;247",    // #CBA6F7 (Purple accent)
157        "cyan" => "38;2;148;226;213",       // #94E2D5 (Teal accent)
158        "white" => "38;2;217;224;238",      // #D9E0EE (TEXT_MAIN)
159        "gray" => "38;2;108;112;134",       // #6C7086 (TEXT_SECONDARY)
160        "accent" => "38;2;51;255;153",      // #33FF99 (PRIMARY_FOCUS/Clean green)
161        "neon_cyan" => "38;2;0;255;255",    // #00FFFF (NEON_CYAN)
162        "neon_green" => "38;2;57;255;20",   // #39FF14 (NEON_GREEN)
163        "clean_blue" => "38;2;100;150;255", // #6496FF (CLEAN_BLUE)
164        _ => "38;2;217;224;238",            // default to TEXT_MAIN
165    };
166
167    if bold {
168        format!("\x1b[1;{}m{}\x1b[0m", color_code, text)
169    } else {
170        format!("\x1b[{}m{}\x1b[0m", color_code, text)
171    }
172}
173
174fn bold(text: &str) -> String {
175    format!("\x1b[1m{}\x1b[0m", text)
176}
177
178pub trait StringFormat {
179    fn dimmed(&self) -> String;
180}
181
182impl StringFormat for str {
183    fn dimmed(&self) -> String {
184        format!("\x1b[2m{}\x1b[0m", self)
185    }
186}
187
188fn get_context_color(context: &str) -> &str {
189    match context {
190        "terminal" => "neon_cyan",
191        "ide" => "magenta",
192        "linked" => "yellow",
193        "manual" => "clean_blue",
194        _ => "white",
195    }
196}
197
198pub fn truncate_string(s: &str, max_len: usize) -> String {
199    if s.len() <= max_len {
200        s.to_string()
201    } else {
202        format!("{}…", &s[..max_len.saturating_sub(1)])
203    }
204}
205
206pub fn format_duration_clean(seconds: i64) -> String {
207    let hours = seconds / 3600;
208    let minutes = (seconds % 3600) / 60;
209    let secs = seconds % 60;
210
211    if hours > 0 {
212        format!("{}h {}m", hours, minutes)
213    } else if minutes > 0 {
214        format!("{}m {}s", minutes, secs)
215    } else {
216        format!("{}s", secs)
217    }
218}