@@ -550,11 +550,13 @@ impl TerminalElement {
minimum_contrast: f32,
) -> TextRun {
let flags = indexed.cell.flags;
+ let is_true_color = matches!(fg, terminal::alacritty_terminal::vte::ansi::Color::Spec(_));
let mut fg = convert_color(&fg, colors);
let bg = convert_color(&bg, colors);
- // Only apply contrast adjustment to non-decorative characters
- if !Self::is_decorative_character(indexed.c) {
+ // Skip contrast adjustment for true-color (24-bit RGB) foregrounds — the
+ // application chose that exact color. Also skip for decorative characters.
+ if !is_true_color && !Self::is_decorative_character(indexed.c) {
fg = ensure_minimum_contrast(fg, bg, minimum_contrast);
}
@@ -1848,6 +1850,42 @@ mod tests {
);
}
+ #[test]
+ fn test_true_color_red_blue_not_washed_out_on_dark_bg() {
+ // Red and blue have inherently low perceptual luminance in APCA.
+ // Pure #ff0000 only achieves Lc ~35 against #1e1e1e — below the
+ // default Lc 45 threshold. ensure_minimum_contrast would lighten
+ // them, washing out the color. This is why cell_style skips the
+ // adjustment for Color::Spec (24-bit true color).
+ let dark_bg = gpui::Hsla {
+ h: 0.0,
+ s: 0.0,
+ l: 0.05,
+ a: 1.0,
+ };
+
+ for (name, r, g, b) in [
+ ("red", 225, 80, 80),
+ ("blue", 80, 80, 225),
+ ("pure red", 255, 0, 0),
+ ] {
+ let color = terminal::rgba_color(r, g, b);
+ let contrast = apca_contrast(color, dark_bg).abs();
+ assert!(
+ contrast < 45.0,
+ "{name} should have APCA < 45 on dark bg, got {contrast}",
+ );
+
+ let adjusted = ensure_minimum_contrast(color, dark_bg, 45.0);
+ assert!(
+ adjusted.l > color.l,
+ "{name} would be lightened by contrast adjustment (l: {} -> {})",
+ color.l,
+ adjusted.l,
+ );
+ }
+ }
+
#[test]
fn test_white_on_white_contrast_issue() {
// This test reproduces the exact issue from the bug report