use crate::interactive::tui::transcript::BlockKind;
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
};
use std::sync::atomic::{AtomicBool, Ordering};
static COLOR_ENABLED: AtomicBool = AtomicBool::new(true);
pub(crate) fn set_color_enabled(enabled: bool) {
COLOR_ENABLED.store(enabled, Ordering::Relaxed);
}
fn decide(choice: saya_config::ColorChoice, is_tty: bool, no_color_env: bool) -> bool {
match choice {
saya_config::ColorChoice::Always => true,
saya_config::ColorChoice::Never => false,
saya_config::ColorChoice::Auto => is_tty && !no_color_env,
}
}
pub(crate) fn decide_public(
choice: saya_config::ColorChoice,
is_tty: bool,
no_color_env: bool,
) -> bool {
decide(choice, is_tty, no_color_env)
}
fn c(color: Color) -> Color {
if COLOR_ENABLED.load(Ordering::Relaxed) {
color
} else {
Color::Reset
}
}
pub(super) fn accent() -> Color {
c(Color::Rgb(157, 139, 245))
}
pub(super) fn user_color() -> Color {
c(Color::Rgb(106, 155, 204))
}
pub(super) fn secondary() -> Color {
c(Color::Rgb(168, 162, 154))
}
pub(super) fn success() -> Color {
c(Color::Rgb(127, 174, 107))
}
pub(super) fn warning() -> Color {
c(Color::Rgb(224, 164, 88))
}
pub(super) fn danger() -> Color {
c(Color::Rgb(229, 105, 95))
}
pub(super) fn status_bg() -> Color {
c(Color::Rgb(30, 28, 36))
}
pub(super) fn code_color() -> Color {
c(Color::Rgb(127, 181, 214))
}
pub(super) fn kind_style(kind: BlockKind) -> Style {
match kind {
BlockKind::User => Style::default()
.fg(user_color())
.add_modifier(Modifier::BOLD),
BlockKind::Assistant => Style::default().fg(c(Color::White)),
BlockKind::System => Style::default()
.fg(secondary())
.add_modifier(Modifier::ITALIC),
BlockKind::Error => Style::default().fg(danger()).add_modifier(Modifier::BOLD),
BlockKind::Tool => Style::default().fg(secondary()),
}
}
pub(super) fn rail_style(kind: BlockKind) -> Style {
match kind {
BlockKind::User => Style::default()
.fg(user_color())
.add_modifier(Modifier::BOLD),
BlockKind::Assistant => Style::default().fg(accent()).add_modifier(Modifier::BOLD),
BlockKind::Tool | BlockKind::System => Style::default().fg(secondary()),
BlockKind::Error => Style::default().fg(danger()).add_modifier(Modifier::BOLD),
}
}
pub(super) fn centered(screen: Rect, width: u16, height: u16) -> Rect {
Rect {
x: screen.x + (screen.width.saturating_sub(width)) / 2,
y: screen.y + (screen.height.saturating_sub(height)) / 2,
width,
height,
}
}
#[cfg(test)]
mod tests {
use super::{COLOR_ENABLED, decide};
use saya_config::ColorChoice;
use std::sync::atomic::Ordering;
#[test]
fn color_decision_follows_choice_env_and_tty() {
assert!(decide(ColorChoice::Always, false, true));
assert!(!decide(ColorChoice::Never, true, false));
assert!(decide(ColorChoice::Auto, true, false));
assert!(!decide(ColorChoice::Auto, true, true), "NO_COLOR disables");
assert!(!decide(ColorChoice::Auto, false, false), "pipes stay plain");
let before = COLOR_ENABLED.load(Ordering::Relaxed);
COLOR_ENABLED.store(!before, Ordering::Relaxed);
assert_eq!(COLOR_ENABLED.load(Ordering::Relaxed), !before);
COLOR_ENABLED.store(before, Ordering::Relaxed);
}
}