Don't apply contrast adjustment to decorative chars (#34238)

Closes #34234

Release Notes:

- Automatic contrast adjustment in terminal is no longer applied to
decorative characters used in block art.
This commit is contained in:
Richard Feldman 2025-07-10 22:48:49 -04:00 committed by GitHub
parent d52f07b77c
commit 56d0ae6782
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -494,6 +494,22 @@ impl TerminalElement {
}
}
/// Checks if a character is a decorative block/box-like character that should
/// preserve its exact colors without contrast adjustment.
///
/// Fixes https://github.com/zed-industries/zed/issues/34234 - we can
/// expand this list if we run into more similar cases, but the goal
/// is to be conservative here.
fn is_decorative_character(ch: char) -> bool {
matches!(
ch as u32,
// 0x2500..=0x257F Box Drawing
// 0x2580..=0x259F Block Elements
// 0x25A0..=0x25D7 Geometric Shapes (block/box-like subset)
0x2500..=0x25D7
)
}
/// Converts the Alacritty cell styles to GPUI text styles and background color.
fn cell_style(
indexed: &IndexedCell,
@ -508,7 +524,10 @@ impl TerminalElement {
let mut fg = convert_color(&fg, colors);
let bg = convert_color(&bg, colors);
fg = color_contrast::ensure_minimum_contrast(fg, bg, minimum_contrast);
// Only apply contrast adjustment to non-decorative characters
if !Self::is_decorative_character(indexed.c) {
fg = color_contrast::ensure_minimum_contrast(fg, bg, minimum_contrast);
}
// Ghostty uses (175/255) as the multiplier (~0.69), Alacritty uses 0.66, Kitty
// uses 0.75. We're using 0.7 because it's pretty well in the middle of that.
@ -1575,6 +1594,91 @@ mod tests {
use super::*;
use gpui::{AbsoluteLength, Hsla, font};
#[test]
fn test_is_decorative_character() {
// Box Drawing characters (U+2500 to U+257F)
assert!(TerminalElement::is_decorative_character('─')); // U+2500
assert!(TerminalElement::is_decorative_character('│')); // U+2502
assert!(TerminalElement::is_decorative_character('┌')); // U+250C
assert!(TerminalElement::is_decorative_character('┐')); // U+2510
assert!(TerminalElement::is_decorative_character('└')); // U+2514
assert!(TerminalElement::is_decorative_character('┘')); // U+2518
assert!(TerminalElement::is_decorative_character('┼')); // U+253C
// Block Elements (U+2580 to U+259F)
assert!(TerminalElement::is_decorative_character('▀')); // U+2580
assert!(TerminalElement::is_decorative_character('▄')); // U+2584
assert!(TerminalElement::is_decorative_character('█')); // U+2588
assert!(TerminalElement::is_decorative_character('░')); // U+2591
assert!(TerminalElement::is_decorative_character('▒')); // U+2592
assert!(TerminalElement::is_decorative_character('▓')); // U+2593
// Geometric Shapes - block/box-like subset (U+25A0 to U+25D7)
assert!(TerminalElement::is_decorative_character('■')); // U+25A0
assert!(TerminalElement::is_decorative_character('□')); // U+25A1
assert!(TerminalElement::is_decorative_character('▲')); // U+25B2
assert!(TerminalElement::is_decorative_character('▼')); // U+25BC
assert!(TerminalElement::is_decorative_character('◆')); // U+25C6
assert!(TerminalElement::is_decorative_character('●')); // U+25CF
// The specific character from the issue
assert!(TerminalElement::is_decorative_character('◗')); // U+25D7
// Characters that should NOT be considered decorative
assert!(!TerminalElement::is_decorative_character('A'));
assert!(!TerminalElement::is_decorative_character('a'));
assert!(!TerminalElement::is_decorative_character('0'));
assert!(!TerminalElement::is_decorative_character(' '));
assert!(!TerminalElement::is_decorative_character('←')); // U+2190 (Arrow, not in our ranges)
assert!(!TerminalElement::is_decorative_character('→')); // U+2192 (Arrow, not in our ranges)
assert!(!TerminalElement::is_decorative_character('◘')); // U+25D8 (Just outside our range)
assert!(!TerminalElement::is_decorative_character('◙')); // U+25D9 (Just outside our range)
}
#[test]
fn test_decorative_character_boundary_cases() {
// Test exact boundaries of our ranges
// Box Drawing range boundaries
assert!(TerminalElement::is_decorative_character('\u{2500}')); // First char
assert!(TerminalElement::is_decorative_character('\u{257F}')); // Last char
assert!(!TerminalElement::is_decorative_character('\u{24FF}')); // Just before
// Block Elements range boundaries
assert!(TerminalElement::is_decorative_character('\u{2580}')); // First char
assert!(TerminalElement::is_decorative_character('\u{259F}')); // Last char
// Geometric Shapes subset boundaries
assert!(TerminalElement::is_decorative_character('\u{25A0}')); // First char
assert!(TerminalElement::is_decorative_character('\u{25D7}')); // Last char (◗)
assert!(!TerminalElement::is_decorative_character('\u{25D8}')); // Just after
}
#[test]
fn test_decorative_characters_bypass_contrast_adjustment() {
// Decorative characters should not be affected by contrast adjustment
// The specific character from issue #34234
let problematic_char = '◗'; // U+25D7
assert!(
TerminalElement::is_decorative_character(problematic_char),
"Character ◗ (U+25D7) should be recognized as decorative"
);
// Verify some other commonly used decorative characters
assert!(TerminalElement::is_decorative_character('│')); // Vertical line
assert!(TerminalElement::is_decorative_character('─')); // Horizontal line
assert!(TerminalElement::is_decorative_character('█')); // Full block
assert!(TerminalElement::is_decorative_character('▓')); // Dark shade
assert!(TerminalElement::is_decorative_character('■')); // Black square
assert!(TerminalElement::is_decorative_character('●')); // Black circle
// Verify normal text characters are NOT decorative
assert!(!TerminalElement::is_decorative_character('A'));
assert!(!TerminalElement::is_decorative_character('1'));
assert!(!TerminalElement::is_decorative_character('$'));
assert!(!TerminalElement::is_decorative_character(' '));
}
#[test]
fn test_contrast_adjustment_logic() {
// Test the core contrast adjustment logic without needing full app context