Codex Handbook
tui/src/chatwidget/tokens/chart/palette.rs 152 lines
//! Builds terminal-aware styles and glyph choices for token activity charts.//!//! The palette adapts theme colors to the active terminal color level while//! keeping chart-specific glyph policy local to the token activity renderer.use ratatui::style::Color;use ratatui::style::Style;use ratatui::style::Stylize;use super::TokenActivityView;use crate::color::blend;use crate::render::highlight::foreground_style_for_scopes;use crate::style::accent_style;use crate::terminal_palette::StdoutColorLevel;use crate::terminal_palette::best_color_for_level;use crate::terminal_palette::default_bg;use crate::terminal_palette::default_fg;use crate::terminal_palette::stdout_color_level;// In low-color terminals we distinguish empty vs active cells by glyph (a// width-matched filled/hollow pair). In truecolor terminals the grid uses a// single glyph and lets color carry the intensity (GitHub-style), which keeps// the grid perfectly aligned and free of texture noise.const EMPTY_CELL_GLYPH: &str = "□";const ACTIVE_CELL_GLYPH: &str = "■";const BAR_CELL_GLYPH: &str = "█";/// Stores the terminal-specific styles and glyph strategy for token activity cells.pub(super) struct TokenActivityPalette {    styles: [Style; 5],    bar_style: Style,    /// True when the terminal supports a truecolor gradient, so the grid can    /// encode intensity purely by color and render every cell with a single    /// glyph. False on low-color terminals, where we fall back to a    /// filled/hollow glyph pair so empty and active cells stay distinguishable.    uses_color: bool,}impl TokenActivityPalette {    pub(super) fn current() -> Self {        Self::from_parts(            default_fg(),            default_bg(),            stdout_color_level(),            theme_activity_style(),        )    }    fn from_parts(        default_fg: Option<(u8, u8, u8)>,        default_bg: Option<(u8, u8, u8)>,        color_level: StdoutColorLevel,        active_style: Style,    ) -> Self {        let fallback_palette = || Self::fallback(active_style);        let (Some(fg), Some(bg), Some(anchor)) =            (default_fg, default_bg, activity_anchor_rgb(active_style))        else {            return fallback_palette();        };        if matches!(            color_level,            StdoutColorLevel::Ansi16 | StdoutColorLevel::Unknown        ) {            return fallback_palette();        }        let empty_alpha = if crate::color::is_light(bg) {            0.18        } else {            0.14        };        let alphas = [empty_alpha, 0.22, 0.42, 0.68, 1.00];        let styles = std::array::from_fn(|index| {            let color = if index == 0 {                blend(fg, bg, alphas[index])            } else {                blend(anchor, bg, alphas[index])            };            Style::default().fg(best_color_for_level(color, color_level))        });        let bar_style = Style::default().fg(best_color_for_level(            blend(anchor, bg, /*alpha*/ 0.78),            color_level,        ));        Self {            styles,            bar_style,            uses_color: true,        }    }    fn fallback(active_style: Style) -> Self {        let empty_style = Style::default().dim();        Self {            styles: [                empty_style,                active_style,                active_style,                active_style,                active_style,            ],            bar_style: active_style,            uses_color: false,        }    }    pub(super) fn for_level(&self, level: usize) -> Style {        self.styles[level.min(/*other*/ 4)]    }    pub(super) fn for_bar_level(&self, level: usize) -> Style {        if level == 0 {            self.for_level(/*level*/ 0)        } else {            self.bar_style        }    }    /// The glyph for a cell at `level`. Daily truecolor renders every visible    /// cell with the same square glyph and lets color carry the intensity; in    /// low-color we use the hollow glyph for empty cells so they remain visible    /// without a color gradient. Bar views use full blocks for filled height and    /// spaces for empty height so the silhouette reads as a column chart.    pub(super) fn glyph(&self, view: TokenActivityView, level: usize) -> &'static str {        if view != TokenActivityView::Daily {            return if level == 0 { " " } else { BAR_CELL_GLYPH };        }        if self.uses_color || level > 0 {            ACTIVE_CELL_GLYPH        } else {            EMPTY_CELL_GLYPH        }    }}fn theme_activity_style() -> Style {    foreground_style_for_scopes(&["entity.name.type", "support.type", "variable"])        .unwrap_or_else(accent_style)        .bold()}fn activity_anchor_rgb(style: Style) -> Option<(u8, u8, u8)> {    match style.fg? {        Color::Rgb(r, g, b) => Some((r, g, b)),        _ => None,    }}#[cfg(test)]#[path = "palette_tests.rs"]mod tests;