chasm_cli/
colors.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: Apache-2.0
3//! Terminal color scheme - Tokyo Night inspired
4//!
5//! Provides consistent styling across all CLI output.
6//! Colors match the demo screenshot in assets/demo.svg.
7//!
8//! ## Tokyo Night Palette
9//! - Blue (#7aa2f7) - prompts, indicators
10//! - Green (#9ece6a) - success, OK, Yes
11//! - Purple (#bb9af7) - headers, labels
12//! - Cyan (#7dcfff) - hashes, paths, info
13//! - Orange (#ff9e64) - counts, numbers
14//! - Text (#c0caf5) - default text
15//! - Dim (#565f89) - separators, borders
16
17use colored::{ColoredString, Colorize};
18
19/// Status indicators with consistent colors
20pub struct Status;
21
22impl Status {
23    /// Success indicator: `[OK]` in green
24    pub fn ok() -> ColoredString {
25        "[OK]".green()
26    }
27
28    /// Info indicator: `[i]` in cyan
29    pub fn info() -> ColoredString {
30        "[i]".cyan()
31    }
32
33    /// Warning indicator: `[!]` in yellow/orange
34    pub fn warn() -> ColoredString {
35        "[!]".yellow()
36    }
37
38    /// Error indicator: `[X]` in red
39    pub fn error() -> ColoredString {
40        "[X]".red()
41    }
42
43    /// Progress/fetch indicator: `[<]` in blue
44    pub fn fetch() -> ColoredString {
45        "[<]".blue()
46    }
47
48    /// Action indicator: `[>]` in yellow
49    pub fn action() -> ColoredString {
50        "[>]".yellow()
51    }
52
53    /// Index/register indicator: `[#]` in blue
54    pub fn index() -> ColoredString {
55        "[#]".blue()
56    }
57
58    /// Detail indicator: `[*]` in blue
59    pub fn detail() -> ColoredString {
60        "[*]".blue()
61    }
62
63    /// Summary indicator: `[=]` in blue
64    pub fn summary() -> ColoredString {
65        "[=]".blue()
66    }
67
68    /// Detect indicator: `[D]` in blue bold
69    pub fn detect() -> ColoredString {
70        "[D]".blue().bold()
71    }
72
73    /// Add/found indicator: `[+]` in green bold
74    pub fn add() -> ColoredString {
75        "[+]".green().bold()
76    }
77
78    /// Package/restore indicator: `[P]` in blue
79    pub fn package() -> ColoredString {
80        "[P]".blue()
81    }
82}
83
84/// Text styling helpers
85pub trait StyledText {
86    /// Style as a header/label (purple/magenta bold)
87    fn header(&self) -> ColoredString;
88    /// Style as a path/identifier (cyan)
89    fn path(&self) -> ColoredString;
90    /// Style as a hash/ID (cyan)
91    fn hash(&self) -> ColoredString;
92    /// Style as a count/number (yellow/orange)
93    fn count(&self) -> ColoredString;
94    /// Style as success value (green)
95    fn success(&self) -> ColoredString;
96    /// Style as a separator (dim gray)
97    fn separator(&self) -> ColoredString;
98    /// Style as an error (red)
99    fn err(&self) -> ColoredString;
100    /// Style as a warning (yellow)
101    fn warning(&self) -> ColoredString;
102    /// Style as branding (cyan bold)
103    fn brand(&self) -> ColoredString;
104}
105
106impl StyledText for str {
107    fn header(&self) -> ColoredString {
108        self.magenta().bold()
109    }
110
111    fn path(&self) -> ColoredString {
112        self.cyan()
113    }
114
115    fn hash(&self) -> ColoredString {
116        self.cyan()
117    }
118
119    fn count(&self) -> ColoredString {
120        self.yellow()
121    }
122
123    fn success(&self) -> ColoredString {
124        self.green()
125    }
126
127    fn separator(&self) -> ColoredString {
128        self.dimmed()
129    }
130
131    fn err(&self) -> ColoredString {
132        self.red()
133    }
134
135    fn warning(&self) -> ColoredString {
136        self.yellow()
137    }
138
139    fn brand(&self) -> ColoredString {
140        self.cyan().bold()
141    }
142}
143
144impl StyledText for String {
145    fn header(&self) -> ColoredString {
146        self.as_str().magenta().bold()
147    }
148
149    fn path(&self) -> ColoredString {
150        self.as_str().cyan()
151    }
152
153    fn hash(&self) -> ColoredString {
154        self.as_str().cyan()
155    }
156
157    fn count(&self) -> ColoredString {
158        self.as_str().yellow()
159    }
160
161    fn success(&self) -> ColoredString {
162        self.as_str().green()
163    }
164
165    fn separator(&self) -> ColoredString {
166        self.as_str().dimmed()
167    }
168
169    fn err(&self) -> ColoredString {
170        self.as_str().red()
171    }
172
173    fn warning(&self) -> ColoredString {
174        self.as_str().yellow()
175    }
176
177    fn brand(&self) -> ColoredString {
178        self.as_str().cyan().bold()
179    }
180}
181
182/// Create a horizontal separator line
183pub fn separator(width: usize) -> ColoredString {
184    "═".repeat(width).dimmed()
185}
186
187/// Create a simple separator line
188pub fn line(width: usize) -> ColoredString {
189    "=".repeat(width).dimmed()
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn test_status_indicators() {
198        // Just verify they compile and return ColoredString
199        let _ = Status::ok();
200        let _ = Status::info();
201        let _ = Status::warn();
202        let _ = Status::error();
203        let _ = Status::fetch();
204    }
205
206    #[test]
207    fn test_styled_text() {
208        let text = "test";
209        let _ = text.header();
210        let _ = text.path();
211        let _ = text.hash();
212        let _ = text.count();
213        let _ = text.success();
214    }
215}