supercode-frontend-tui 0.4.5

Attachable terminal frontend primitives for Supercode SDK runtimes.
Documentation
// Derived from OpenAI Codex: codex-rs/tui/src/terminal_palette.rs
// Pinned source: 8604689ec5e3437eb79802d8d72249b7722fbf5b
// Copyright 2025 OpenAI
// Licensed under the Apache License, Version 2.0.
// Modified by the Supercode contributors; see docs/legal/codex-frontend-extraction.toml.

//! Deterministic terminal color degradation without donor runtime detection.

use ratatui::style::Color;

use crate::foundation::color::perceptual_distance;

/// Color fidelity the active terminal can display safely.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ColorLevel {
    /// 24-bit RGB colors.
    TrueColor,
    /// Xterm's stable 240-color cube and grayscale range.
    Ansi256,
    /// No authored colors; use the terminal defaults.
    Monochrome,
}

/// Explicit inputs used to resolve terminal color behavior.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ColorCapabilities {
    pub level: ColorLevel,
    pub color_enabled: bool,
}

impl ColorCapabilities {
    /// Resolve capabilities from environment values supplied by the caller.
    ///
    /// Passing values explicitly keeps snapshots deterministic and avoids
    /// mutating process-global environment variables in parallel tests.
    pub fn resolve(
        no_color: bool,
        term: Option<&str>,
        colorterm: Option<&str>,
        force_color: bool,
    ) -> Self {
        if no_color && !force_color {
            return Self::monochrome();
        }
        if term.is_some_and(|value| value.eq_ignore_ascii_case("dumb")) && !force_color {
            return Self::monochrome();
        }

        let truecolor = colorterm.is_some_and(|value| {
            value.eq_ignore_ascii_case("truecolor") || value.eq_ignore_ascii_case("24bit")
        });
        let ansi256 = term.is_some_and(|value| value.to_ascii_lowercase().contains("256color"));
        let level = if truecolor {
            ColorLevel::TrueColor
        } else if ansi256 || force_color {
            ColorLevel::Ansi256
        } else {
            ColorLevel::Monochrome
        };
        Self {
            level,
            color_enabled: level != ColorLevel::Monochrome,
        }
    }

    pub const fn monochrome() -> Self {
        Self {
            level: ColorLevel::Monochrome,
            color_enabled: false,
        }
    }

    /// Return the closest safe Ratatui color for these capabilities.
    pub fn best_color(self, target: (u8, u8, u8)) -> Color {
        match self.level {
            ColorLevel::TrueColor => Color::Rgb(target.0, target.1, target.2),
            ColorLevel::Ansi256 => Color::Indexed(nearest_xterm_index(target)),
            ColorLevel::Monochrome => Color::Reset,
        }
    }
}

fn nearest_xterm_index(target: (u8, u8, u8)) -> u8 {
    xterm_fixed_colors()
        .min_by(|(_, left), (_, right)| {
            perceptual_distance(*left, target)
                .partial_cmp(&perceptual_distance(*right, target))
                .unwrap_or(std::cmp::Ordering::Equal)
        })
        .map_or(16, |(index, _)| index)
}

fn xterm_fixed_colors() -> impl Iterator<Item = (u8, (u8, u8, u8))> {
    let cube = (0u8..6).flat_map(|r| {
        (0u8..6).flat_map(move |g| {
            (0u8..6).map(move |b| {
                let index = 16 + 36 * r + 6 * g + b;
                (
                    index,
                    (xterm_component(r), xterm_component(g), xterm_component(b)),
                )
            })
        })
    });
    let grayscale = (0u8..24).map(|offset| {
        let value = 8 + offset * 10;
        (232 + offset, (value, value, value))
    });
    cube.chain(grayscale)
}

const fn xterm_component(value: u8) -> u8 {
    if value == 0 {
        0
    } else {
        55 + value * 40
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn truecolor_is_not_quantized() {
        let capabilities =
            ColorCapabilities::resolve(false, Some("xterm-256color"), Some("truecolor"), false);
        assert_eq!(capabilities.level, ColorLevel::TrueColor);
        assert_eq!(
            capabilities.best_color((12, 34, 56)),
            Color::Rgb(12, 34, 56)
        );
    }

    #[test]
    fn ansi256_uses_a_stable_index() {
        let capabilities = ColorCapabilities::resolve(false, Some("xterm-256color"), None, false);
        assert_eq!(capabilities.level, ColorLevel::Ansi256);
        assert_eq!(capabilities.best_color((255, 0, 0)), Color::Indexed(196));
        assert_eq!(
            capabilities.best_color((128, 128, 128)),
            Color::Indexed(244)
        );
    }

    #[test]
    fn no_color_disables_authored_colors() {
        let capabilities =
            ColorCapabilities::resolve(true, Some("xterm-256color"), Some("truecolor"), false);
        assert_eq!(capabilities, ColorCapabilities::monochrome());
        assert_eq!(capabilities.best_color((255, 0, 0)), Color::Reset);
    }

    #[test]
    fn dumb_terminal_is_monochrome_unless_forced() {
        assert_eq!(
            ColorCapabilities::resolve(false, Some("dumb"), None, false),
            ColorCapabilities::monochrome()
        );
        assert_eq!(
            ColorCapabilities::resolve(false, Some("dumb"), None, true).level,
            ColorLevel::Ansi256
        );
    }
}