syncable_cli/agent/ui/
colors.rs

1//! Color theme and styling utilities for terminal UI
2//!
3//! Provides semantic colors and ANSI escape codes for consistent styling.
4
5use colored::Colorize;
6
7/// Status icons for different states
8pub mod icons {
9    pub const PENDING: &str = "○";
10    pub const EXECUTING: &str = "◐";
11    pub const SUCCESS: &str = "✓";
12    pub const ERROR: &str = "✗";
13    pub const WARNING: &str = "⚠";
14    pub const CANCELED: &str = "⊘";
15    pub const CONFIRMING: &str = "⏳";
16    pub const ARROW: &str = "→";
17    pub const THINKING: &str = "💭";
18    pub const ROBOT: &str = "🤖";
19    pub const TOOL: &str = "🔧";
20    pub const SHELL: &str = "🐚";
21    pub const EDIT: &str = "✏️";
22    pub const FILE: &str = "📄";
23    pub const FOLDER: &str = "📁";
24    pub const SECURITY: &str = "🔒";
25    pub const SEARCH: &str = "🔍";
26    pub const DOCKER: &str = "🐳";
27    pub const LINT: &str = "📋";
28    pub const FIX: &str = "🔧";
29    pub const CRITICAL: &str = "🔴";
30    pub const HIGH: &str = "🟠";
31    pub const MEDIUM: &str = "🟡";
32    pub const LOW: &str = "🟢";
33}
34
35/// ANSI escape codes for direct terminal control
36pub mod ansi {
37    /// Clear current line
38    pub const CLEAR_LINE: &str = "\x1b[2K\r";
39    /// Move cursor up one line
40    pub const CURSOR_UP: &str = "\x1b[1A";
41    /// Hide cursor
42    pub const HIDE_CURSOR: &str = "\x1b[?25l";
43    /// Show cursor
44    pub const SHOW_CURSOR: &str = "\x1b[?25h";
45    /// Reset all styles
46    pub const RESET: &str = "\x1b[0m";
47    /// Bold
48    pub const BOLD: &str = "\x1b[1m";
49    /// Dim
50    pub const DIM: &str = "\x1b[2m";
51
52    // 256-color codes for Syncable brand
53    pub const PURPLE: &str = "\x1b[38;5;141m";
54    pub const ORANGE: &str = "\x1b[38;5;216m";
55    pub const PINK: &str = "\x1b[38;5;212m";
56    pub const MAGENTA: &str = "\x1b[38;5;207m";
57    pub const CYAN: &str = "\x1b[38;5;51m";
58    pub const GRAY: &str = "\x1b[38;5;245m";
59    pub const WHITE: &str = "\x1b[38;5;255m";
60    pub const SUCCESS: &str = "\x1b[38;5;114m"; // Green for success
61
62    // Hadolint/Docker specific colors (teal/docker-blue theme)
63    pub const DOCKER_BLUE: &str = "\x1b[38;5;39m";  // Docker brand blue
64    pub const TEAL: &str = "\x1b[38;5;30m";         // Teal for hadolint
65    pub const CRITICAL: &str = "\x1b[38;5;196m";   // Bright red
66    pub const HIGH: &str = "\x1b[38;5;208m";       // Orange
67    pub const MEDIUM: &str = "\x1b[38;5;220m";     // Yellow
68    pub const LOW: &str = "\x1b[38;5;114m";        // Green
69    pub const INFO_BLUE: &str = "\x1b[38;5;75m";   // Light blue for info
70}
71
72/// Format a tool name for display
73pub fn format_tool_name(name: &str) -> String {
74    name.cyan().bold().to_string()
75}
76
77/// Format a status message based on success/failure
78pub fn format_status(success: bool, message: &str) -> String {
79    if success {
80        format!("{} {}", icons::SUCCESS.green(), message.green())
81    } else {
82        format!("{} {}", icons::ERROR.red(), message.red())
83    }
84}
85
86/// Format elapsed time for display
87pub fn format_elapsed(seconds: u64) -> String {
88    if seconds < 60 {
89        format!("{}s", seconds)
90    } else {
91        let mins = seconds / 60;
92        let secs = seconds % 60;
93        format!("{}m {}s", mins, secs)
94    }
95}
96
97/// Format a thinking/reasoning message
98pub fn format_thinking(subject: &str) -> String {
99    format!(
100        "{} {}",
101        icons::THINKING,
102        subject.cyan().italic()
103    )
104}
105
106/// Format an info message
107pub fn format_info(message: &str) -> String {
108    format!("{} {}", icons::ARROW.cyan(), message)
109}
110
111/// Format a warning message
112pub fn format_warning(message: &str) -> String {
113    format!("⚠ {}", message.yellow())
114}
115
116/// Format an error message
117pub fn format_error(message: &str) -> String {
118    format!("{} {}", icons::ERROR.red(), message.red())
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_format_elapsed() {
127        assert_eq!(format_elapsed(5), "5s");
128        assert_eq!(format_elapsed(30), "30s");
129        assert_eq!(format_elapsed(65), "1m 5s");
130        assert_eq!(format_elapsed(125), "2m 5s");
131    }
132}