browser_automation_cli/color.rs
1//! Color output utilities.
2//!
3//! Colors are off by default (agent-friendly). Enable with
4//! `browser-automation-cli config set color true` (XDG config). Setting `NO_COLOR`
5//! to any value disables colors per <https://no-color.org/> (OS convention).
6
7use std::env;
8use std::sync::OnceLock;
9
10/// Returns true if color output is enabled.
11///
12/// Priority: `NO_COLOR` (presence disables, per OS convention) >
13/// XDG config `color = true` > default (off).
14pub fn is_enabled() -> bool {
15 static COLORS_ENABLED: OnceLock<bool> = OnceLock::new();
16 *COLORS_ENABLED.get_or_init(|| {
17 if env::var_os("NO_COLOR").is_some() {
18 return false;
19 }
20 crate::xdg::load_config()
21 .ok()
22 .and_then(|c| c.color)
23 .unwrap_or(false)
24 })
25}
26
27/// Format text in red (errors)
28pub fn red(text: &str) -> String {
29 if is_enabled() {
30 format!("\x1b[31m{}\x1b[0m", text)
31 } else {
32 text.to_string()
33 }
34}
35
36/// Format text in green (success)
37pub fn green(text: &str) -> String {
38 if is_enabled() {
39 format!("\x1b[32m{}\x1b[0m", text)
40 } else {
41 text.to_string()
42 }
43}
44
45/// Format text in yellow (warnings)
46pub fn yellow(text: &str) -> String {
47 if is_enabled() {
48 format!("\x1b[33m{}\x1b[0m", text)
49 } else {
50 text.to_string()
51 }
52}
53
54/// Format text in cyan (info)
55pub fn cyan(text: &str) -> String {
56 if is_enabled() {
57 format!("\x1b[36m{}\x1b[0m", text)
58 } else {
59 text.to_string()
60 }
61}
62
63/// Format text in bold
64pub fn bold(text: &str) -> String {
65 if is_enabled() {
66 format!("\x1b[1m{}\x1b[0m", text)
67 } else {
68 text.to_string()
69 }
70}
71
72/// Format text dimmed
73pub fn dim(text: &str) -> String {
74 if is_enabled() {
75 format!("\x1b[2m{}\x1b[0m", text)
76 } else {
77 text.to_string()
78 }
79}