use anstyle::{AnsiColor, Color as AnstyleColor, RgbColor};
use ratatui::style::Color;
pub fn anstyle_to_ratatui_color(color: AnstyleColor) -> Color {
match color {
AnstyleColor::Ansi(ansi) => ansi_to_ratatui(ansi),
AnstyleColor::Ansi256(c) => Color::Indexed(c.0),
AnstyleColor::Rgb(RgbColor(r, g, b)) => Color::Rgb(r, g, b),
}
}
fn ansi_to_ratatui(color: AnsiColor) -> Color {
match color {
AnsiColor::Black => Color::Black,
AnsiColor::Red => Color::Red,
AnsiColor::Green => Color::Green,
AnsiColor::Yellow => Color::Yellow,
AnsiColor::Blue => Color::Blue,
AnsiColor::Magenta => Color::Magenta,
AnsiColor::Cyan => Color::Cyan,
AnsiColor::White => Color::White,
AnsiColor::BrightBlack => Color::DarkGray,
AnsiColor::BrightRed => Color::LightRed,
AnsiColor::BrightGreen => Color::LightGreen,
AnsiColor::BrightYellow => Color::LightYellow,
AnsiColor::BrightBlue => Color::LightBlue,
AnsiColor::BrightMagenta => Color::LightMagenta,
AnsiColor::BrightCyan => Color::LightCyan,
AnsiColor::BrightWhite => Color::White,
}
}
fn ansi_hue_variant(hue: &str, light: bool) -> Option<Color> {
let (dark, lit) = match hue {
"red" => (Color::LightRed, Color::Red),
"green" => (Color::LightGreen, Color::Green),
"blue" => (Color::LightBlue, Color::Blue),
"magenta" => (Color::LightMagenta, Color::Magenta),
"yellow" => (Color::LightYellow, Color::Yellow),
"cyan" => (Color::LightCyan, Color::Cyan),
_ => return None,
};
Some(if light { lit } else { dark })
}
pub fn hex_to_ratatui_color(hex: &str) -> Option<Color> {
let hex = hex.trim().trim_start_matches('#');
if hex.len() != 6 {
return None;
}
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
Some(Color::Rgb(r, g, b))
}
pub fn resolve_agent_color(token: &str, fallback: Color, light: bool) -> Color {
use vtcode_config::constants::ui::agent_mode_hue;
agent_mode_hue(token)
.and_then(|h| ansi_hue_variant(h, light))
.or_else(|| ansi_hue_variant(token, light))
.or_else(|| hex_to_ratatui_color(token))
.unwrap_or(fallback)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard_ansi_colors() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Black)),
Color::Black
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Red)),
Color::Red
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Green)),
Color::Green
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Yellow)),
Color::Yellow
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Blue)),
Color::Blue
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Cyan)),
Color::Cyan
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::White)),
Color::White
);
}
#[test]
fn magenta_maps_to_magenta_not_dark_gray() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::Magenta)),
Color::Magenta
);
}
#[test]
fn bright_ansi_colors() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightBlack)),
Color::DarkGray
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightRed)),
Color::LightRed
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightGreen)),
Color::LightGreen
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightYellow)),
Color::LightYellow
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightBlue)),
Color::LightBlue
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightMagenta)),
Color::LightMagenta
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightCyan)),
Color::LightCyan
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightWhite)),
Color::White
);
}
#[test]
fn bright_magenta_maps_to_light_magenta() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(AnsiColor::BrightMagenta)),
Color::LightMagenta
);
}
#[test]
fn ansi256_color() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi256(anstyle::Ansi256Color(42))),
Color::Indexed(42)
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi256(anstyle::Ansi256Color(0))),
Color::Indexed(0)
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi256(anstyle::Ansi256Color(255))),
Color::Indexed(255)
);
}
#[test]
fn rgb_color() {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Rgb(RgbColor(255, 128, 0))),
Color::Rgb(255, 128, 0)
);
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Rgb(RgbColor(0, 0, 0))),
Color::Rgb(0, 0, 0)
);
}
#[test]
fn all_16_ansi_colors_covered() {
let colors = [
(AnsiColor::Black, Color::Black),
(AnsiColor::Red, Color::Red),
(AnsiColor::Green, Color::Green),
(AnsiColor::Yellow, Color::Yellow),
(AnsiColor::Blue, Color::Blue),
(AnsiColor::Magenta, Color::Magenta),
(AnsiColor::Cyan, Color::Cyan),
(AnsiColor::White, Color::White),
(AnsiColor::BrightBlack, Color::DarkGray),
(AnsiColor::BrightRed, Color::LightRed),
(AnsiColor::BrightGreen, Color::LightGreen),
(AnsiColor::BrightYellow, Color::LightYellow),
(AnsiColor::BrightBlue, Color::LightBlue),
(AnsiColor::BrightMagenta, Color::LightMagenta),
(AnsiColor::BrightCyan, Color::LightCyan),
(AnsiColor::BrightWhite, Color::White),
];
for (input, expected) in colors {
assert_eq!(
anstyle_to_ratatui_color(AnstyleColor::Ansi(input)),
expected,
"mismatch for {input:?}"
);
}
}
}