tui: hardcode xterm palette, shimmer blends between fg and bg (#4957)

Instead of querying all 256 terminal colors on startup, which was slow
in some terminals, hardcode the default xterm palette.

Additionally, tweak the shimmer so that it blends between default_fg and
default_bg, instead of "dark gray" (according to the terminal) and pure
white (regardless of terminal theme).
This commit is contained in:
Jeremy Rose
2025-10-08 13:23:13 -07:00
committed by GitHub
parent 96acb8a74e
commit e896db1180
4 changed files with 300 additions and 268 deletions

View File

@@ -8,10 +8,8 @@ use ratatui::style::Style;
use ratatui::text::Span;
use crate::color::blend;
use crate::terminal_palette::default_bg;
use crate::terminal_palette::default_fg;
use crate::terminal_palette::terminal_palette;
const FALLBACK_DARK_GRAY: (u8, u8, u8) = (103, 103, 103);
static PROCESS_START: OnceLock<Instant> = OnceLock::new();
@@ -35,11 +33,11 @@ pub(crate) fn shimmer_spans(text: &str) -> Vec<Span<'static>> {
let has_true_color = supports_color::on_cached(supports_color::Stream::Stdout)
.map(|level| level.has_16m)
.unwrap_or(false);
let band_half_width = 3.0;
let band_half_width = 5.0;
let mut spans: Vec<Span<'static>> = Vec::with_capacity(chars.len());
let default_fg = default_fg();
let palette_dark_gray = terminal_palette().map(|palette| palette[8]);
let base_color = default_fg().unwrap_or((128, 128, 128));
let highlight_color = default_bg().unwrap_or((255, 255, 255));
for (i, ch) in chars.iter().enumerate() {
let i_pos = i as isize + padding as isize;
let pos = pos as isize;
@@ -52,11 +50,8 @@ pub(crate) fn shimmer_spans(text: &str) -> Vec<Span<'static>> {
0.0
};
let style = if has_true_color {
let base = palette_dark_gray
.or(default_fg)
.unwrap_or(FALLBACK_DARK_GRAY);
let highlight = t.clamp(0.0, 1.0);
let (r, g, b) = blend((255, 255, 255), base, highlight);
let (r, g, b) = blend(highlight_color, base_color, highlight * 0.9);
// Allow custom RGB colors, as the implementation is thoughtfully
// adjusting the level of the default foreground color.
#[allow(clippy::disallowed_methods)]

View File

@@ -1,8 +1,7 @@
use crate::color::blend;
use crate::color::is_light;
use crate::color::perceptual_distance;
use crate::terminal_palette::best_color;
use crate::terminal_palette::default_bg;
use crate::terminal_palette::terminal_palette;
use ratatui::style::Color;
use ratatui::style::Style;
@@ -25,25 +24,5 @@ pub fn user_message_bg(terminal_bg: (u8, u8, u8)) -> Color {
} else {
(255, 255, 255)
};
let bottom = terminal_bg;
let Some(color_level) = supports_color::on_cached(supports_color::Stream::Stdout) else {
return Color::default();
};
let target = blend(top, bottom, 0.1);
if color_level.has_16m {
let (r, g, b) = target;
Color::Rgb(r, g, b)
} else if color_level.has_256
&& let Some(palette) = terminal_palette()
&& let Some((i, _)) = palette.into_iter().enumerate().min_by(|(_, a), (_, b)| {
perceptual_distance(*a, target)
.partial_cmp(&perceptual_distance(*b, target))
.unwrap_or(std::cmp::Ordering::Equal)
})
{
Color::Indexed(i as u8)
} else {
Color::default()
}
best_color(blend(top, terminal_bg, 0.1))
}

View File

@@ -1,5 +1,28 @@
pub fn terminal_palette() -> Option<[(u8, u8, u8); 256]> {
imp::terminal_palette()
use crate::color::perceptual_distance;
use ratatui::style::Color;
/// Returns the closest color to the target color that the terminal can display.
pub fn best_color(target: (u8, u8, u8)) -> Color {
let Some(color_level) = supports_color::on_cached(supports_color::Stream::Stdout) else {
return Color::default();
};
if color_level.has_16m {
let (r, g, b) = target;
#[allow(clippy::disallowed_methods)]
Color::Rgb(r, g, b)
} else if color_level.has_256
&& let Some((i, _)) = xterm_fixed_colors().enumerate().min_by(|(_, a), (_, b)| {
perceptual_distance(*a, target)
.partial_cmp(&perceptual_distance(*b, target))
.unwrap_or(std::cmp::Ordering::Equal)
})
{
#[allow(clippy::disallowed_methods)]
Color::Indexed(i as u8)
} else {
#[allow(clippy::disallowed_methods)]
Color::default()
}
}
pub fn requery_default_colors() {
@@ -8,7 +31,6 @@ pub fn requery_default_colors() {
#[derive(Clone, Copy)]
pub struct DefaultColors {
#[allow(dead_code)]
fg: (u8, u8, u8),
bg: (u8, u8, u8),
}
@@ -17,7 +39,6 @@ pub fn default_colors() -> Option<DefaultColors> {
imp::default_colors()
}
#[allow(dead_code)]
pub fn default_fg() -> Option<(u8, u8, u8)> {
default_colors().map(|c| c.fg)
}
@@ -29,8 +50,6 @@ pub fn default_bg() -> Option<(u8, u8, u8)> {
#[cfg(all(unix, not(test)))]
mod imp {
use super::DefaultColors;
use std::mem::MaybeUninit;
use std::os::fd::RawFd;
use std::sync::Mutex;
use std::sync::OnceLock;
@@ -69,14 +88,6 @@ mod imp {
CACHE.get_or_init(|| Mutex::new(Cache::default()))
}
pub(super) fn terminal_palette() -> Option<[(u8, u8, u8); 256]> {
static CACHE: OnceLock<Option<[(u8, u8, u8); 256]>> = OnceLock::new();
*CACHE.get_or_init(|| match query_terminal_palette() {
Ok(Some(palette)) => Some(palette),
_ => None,
})
}
pub(super) fn default_colors() -> Option<DefaultColors> {
let cache = default_colors_cache();
let mut cache = cache.lock().ok()?;
@@ -89,84 +100,6 @@ mod imp {
}
}
#[allow(dead_code)]
fn query_terminal_palette() -> std::io::Result<Option<[(u8, u8, u8); 256]>> {
use std::fs::OpenOptions;
use std::io::ErrorKind;
use std::io::IsTerminal;
use std::io::Read;
use std::io::Write;
use std::os::fd::AsRawFd;
use std::time::Duration;
use std::time::Instant;
if !std::io::stdout().is_terminal() {
return Ok(None);
}
let mut tty = match OpenOptions::new().read(true).write(true).open("/dev/tty") {
Ok(file) => file,
Err(_) => return Ok(None),
};
for index in 0..256 {
write!(tty, "\x1b]4;{index};?\x07")?;
}
tty.flush()?;
let fd = tty.as_raw_fd();
let _termios_guard = unsafe { suppress_echo(fd) };
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFL);
if flags >= 0 {
libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
}
}
let mut palette: [Option<(u8, u8, u8)>; 256] = [None; 256];
let mut buffer = Vec::new();
let mut remaining = palette.len();
let read_deadline = Instant::now() + Duration::from_millis(1500);
while remaining > 0 && Instant::now() < read_deadline {
let mut chunk = [0u8; 512];
match tty.read(&mut chunk) {
Ok(0) => break,
Ok(read) => {
buffer.extend_from_slice(&chunk[..read]);
let newly = apply_palette_responses(&mut buffer, &mut palette);
if newly > 0 {
remaining = remaining.saturating_sub(newly);
}
}
Err(err) if err.kind() == ErrorKind::WouldBlock => {
std::thread::sleep(Duration::from_millis(5));
}
Err(err) if err.kind() == ErrorKind::Interrupted => continue,
Err(_) => return Ok(None),
}
}
remaining = remaining.saturating_sub(apply_palette_responses(&mut buffer, &mut palette));
remaining = remaining.saturating_sub(drain_remaining(&mut tty, &mut buffer, &mut palette));
if remaining > 0 {
return Ok(None);
}
let mut colors = [(0, 0, 0); 256];
for (slot, value) in colors.iter_mut().zip(palette.into_iter()) {
if let Some(rgb) = value {
*slot = rgb;
} else {
return Ok(None);
}
}
Ok(Some(colors))
}
#[allow(dead_code)]
fn query_default_colors() -> std::io::Result<Option<DefaultColors>> {
use std::fs::OpenOptions;
use std::io::ErrorKind;
@@ -236,144 +169,6 @@ mod imp {
Ok(fg.zip(bg).map(|(fg, bg)| DefaultColors { fg, bg }))
}
fn drain_remaining(
tty: &mut std::fs::File,
buffer: &mut Vec<u8>,
palette: &mut [Option<(u8, u8, u8)>; 256],
) -> usize {
use std::io::ErrorKind;
use std::io::Read;
use std::time::Duration;
use std::time::Instant;
let mut chunk = [0u8; 512];
let mut idle_deadline = Instant::now() + Duration::from_millis(50);
let mut newly_filled = 0usize;
loop {
match tty.read(&mut chunk) {
Ok(0) => break,
Ok(read) => {
buffer.extend_from_slice(&chunk[..read]);
newly_filled += apply_palette_responses(buffer, palette);
idle_deadline = Instant::now() + Duration::from_millis(50);
}
Err(err) if err.kind() == ErrorKind::WouldBlock => {
if Instant::now() >= idle_deadline {
break;
}
std::thread::sleep(Duration::from_millis(5));
}
Err(err) if err.kind() == ErrorKind::Interrupted => continue,
Err(_) => break,
}
}
buffer.clear();
newly_filled
}
struct TermiosGuard {
fd: RawFd,
original: libc::termios,
}
impl Drop for TermiosGuard {
fn drop(&mut self) {
unsafe {
libc::tcsetattr(self.fd, libc::TCSANOW, &self.original);
}
}
}
unsafe fn suppress_echo(fd: RawFd) -> Option<TermiosGuard> {
let mut termios = MaybeUninit::<libc::termios>::uninit();
if unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) } != 0 {
return None;
}
let termios = unsafe { termios.assume_init() };
let mut modified = termios;
modified.c_lflag &= !(libc::ECHO | libc::ECHONL);
if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &modified) } != 0 {
return None;
}
Some(TermiosGuard {
fd,
original: termios,
})
}
fn apply_palette_responses(
buffer: &mut Vec<u8>,
palette: &mut [Option<(u8, u8, u8)>; 256],
) -> usize {
let mut newly_filled = 0;
while let Some(start) = buffer.windows(2).position(|window| window == [0x1b, b']']) {
if start > 0 {
buffer.drain(..start);
continue;
}
let mut index = 2; // skip ESC ]
let mut terminator_len = None;
while index < buffer.len() {
match buffer[index] {
0x07 => {
terminator_len = Some(1);
break;
}
0x1b if index + 1 < buffer.len() && buffer[index + 1] == b'\\' => {
terminator_len = Some(2);
break;
}
_ => index += 1,
}
}
let Some(terminator_len) = terminator_len else {
break;
};
let end = index;
let parsed = std::str::from_utf8(&buffer[2..end])
.ok()
.and_then(parse_palette_message);
let processed = end + terminator_len;
buffer.drain(..processed);
if let Some((slot, color)) = parsed
&& palette[slot].is_none()
{
palette[slot] = Some(color);
newly_filled += 1;
}
}
newly_filled
}
fn parse_palette_message(message: &str) -> Option<(usize, (u8, u8, u8))> {
let mut parts = message.splitn(3, ';');
if parts.next()? != "4" {
return None;
}
let index: usize = parts.next()?.trim().parse().ok()?;
if index >= 256 {
return None;
}
let payload = parts.next()?;
let (model, values) = payload.split_once(':')?;
if model != "rgb" && model != "rgba" {
return None;
}
let mut components = values.split('/');
let r = parse_component(components.next()?)?;
let g = parse_component(components.next()?)?;
let b = parse_component(components.next()?)?;
Some((index, (r, g, b)))
}
fn parse_component(component: &str) -> Option<u8> {
let trimmed = component.trim();
if trimmed.is_empty() {
@@ -433,13 +228,277 @@ mod imp {
mod imp {
use super::DefaultColors;
pub(super) fn terminal_palette() -> Option<[(u8, u8, u8); 256]> {
None
}
pub(super) fn default_colors() -> Option<DefaultColors> {
None
}
pub(super) fn requery_default_colors() {}
}
/// The subset of Xterm colors that are usually consistent across terminals.
pub fn xterm_fixed_colors() -> impl Iterator<Item = (u8, u8, u8)> {
XTERM_COLORS.into_iter().skip(16)
}
// Xterm colors; derived from https://ss64.com/bash/syntax-colors.html
pub const XTERM_COLORS: [(u8, u8, u8); 256] = [
// The first 16 colors vary based on terminal theme, so these are likely not the actual colors
// that are displayed when using these indices.
(0, 0, 0), // 0 Black (SYSTEM)
(128, 0, 0), // 1 Maroon (SYSTEM)
(0, 128, 0), // 2 Green (SYSTEM)
(128, 128, 0), // 3 Olive (SYSTEM)
(0, 0, 128), // 4 Navy (SYSTEM)
(128, 0, 128), // 5 Purple (SYSTEM)
(0, 128, 128), // 6 Teal (SYSTEM)
(192, 192, 192), // 7 Silver (SYSTEM)
(128, 128, 128), // 8 Grey (SYSTEM)
(255, 0, 0), // 9 Red (SYSTEM)
(0, 255, 0), // 10 Lime (SYSTEM)
(255, 255, 0), // 11 Yellow (SYSTEM)
(0, 0, 255), // 12 Blue (SYSTEM)
(255, 0, 255), // 13 Fuchsia (SYSTEM)
(0, 255, 255), // 14 Aqua (SYSTEM)
(255, 255, 255), // 15 White (SYSTEM)
// The rest of the colors are consistent in most terminals.
(0, 0, 0), // 16 Grey0
(0, 0, 95), // 17 NavyBlue
(0, 0, 135), // 18 DarkBlue
(0, 0, 175), // 19 Blue3
(0, 0, 215), // 20 Blue3
(0, 0, 255), // 21 Blue1
(0, 95, 0), // 22 DarkGreen
(0, 95, 95), // 23 DeepSkyBlue4
(0, 95, 135), // 24 DeepSkyBlue4
(0, 95, 175), // 25 DeepSkyBlue4
(0, 95, 215), // 26 DodgerBlue3
(0, 95, 255), // 27 DodgerBlue2
(0, 135, 0), // 28 Green4
(0, 135, 95), // 29 SpringGreen4
(0, 135, 135), // 30 Turquoise4
(0, 135, 175), // 31 DeepSkyBlue3
(0, 135, 215), // 32 DeepSkyBlue3
(0, 135, 255), // 33 DodgerBlue1
(0, 175, 0), // 34 Green3
(0, 175, 95), // 35 SpringGreen3
(0, 175, 135), // 36 DarkCyan
(0, 175, 175), // 37 LightSeaGreen
(0, 175, 215), // 38 DeepSkyBlue2
(0, 175, 255), // 39 DeepSkyBlue1
(0, 215, 0), // 40 Green3
(0, 215, 95), // 41 SpringGreen3
(0, 215, 135), // 42 SpringGreen2
(0, 215, 175), // 43 Cyan3
(0, 215, 215), // 44 DarkTurquoise
(0, 215, 255), // 45 Turquoise2
(0, 255, 0), // 46 Green1
(0, 255, 95), // 47 SpringGreen2
(0, 255, 135), // 48 SpringGreen1
(0, 255, 175), // 49 MediumSpringGreen
(0, 255, 215), // 50 Cyan2
(0, 255, 255), // 51 Cyan1
(95, 0, 0), // 52 DarkRed
(95, 0, 95), // 53 DeepPink4
(95, 0, 135), // 54 Purple4
(95, 0, 175), // 55 Purple4
(95, 0, 215), // 56 Purple3
(95, 0, 255), // 57 BlueViolet
(95, 95, 0), // 58 Orange4
(95, 95, 95), // 59 Grey37
(95, 95, 135), // 60 MediumPurple4
(95, 95, 175), // 61 SlateBlue3
(95, 95, 215), // 62 SlateBlue3
(95, 95, 255), // 63 RoyalBlue1
(95, 135, 0), // 64 Chartreuse4
(95, 135, 95), // 65 DarkSeaGreen4
(95, 135, 135), // 66 PaleTurquoise4
(95, 135, 175), // 67 SteelBlue
(95, 135, 215), // 68 SteelBlue3
(95, 135, 255), // 69 CornflowerBlue
(95, 175, 0), // 70 Chartreuse3
(95, 175, 95), // 71 DarkSeaGreen4
(95, 175, 135), // 72 CadetBlue
(95, 175, 175), // 73 CadetBlue
(95, 175, 215), // 74 SkyBlue3
(95, 175, 255), // 75 SteelBlue1
(95, 215, 0), // 76 Chartreuse3
(95, 215, 95), // 77 PaleGreen3
(95, 215, 135), // 78 SeaGreen3
(95, 215, 175), // 79 Aquamarine3
(95, 215, 215), // 80 MediumTurquoise
(95, 215, 255), // 81 SteelBlue1
(95, 255, 0), // 82 Chartreuse2
(95, 255, 95), // 83 SeaGreen2
(95, 255, 135), // 84 SeaGreen1
(95, 255, 175), // 85 SeaGreen1
(95, 255, 215), // 86 Aquamarine1
(95, 255, 255), // 87 DarkSlateGray2
(135, 0, 0), // 88 DarkRed
(135, 0, 95), // 89 DeepPink4
(135, 0, 135), // 90 DarkMagenta
(135, 0, 175), // 91 DarkMagenta
(135, 0, 215), // 92 DarkViolet
(135, 0, 255), // 93 Purple
(135, 95, 0), // 94 Orange4
(135, 95, 95), // 95 LightPink4
(135, 95, 135), // 96 Plum4
(135, 95, 175), // 97 MediumPurple3
(135, 95, 215), // 98 MediumPurple3
(135, 95, 255), // 99 SlateBlue1
(135, 135, 0), // 100 Yellow4
(135, 135, 95), // 101 Wheat4
(135, 135, 135), // 102 Grey53
(135, 135, 175), // 103 LightSlateGrey
(135, 135, 215), // 104 MediumPurple
(135, 135, 255), // 105 LightSlateBlue
(135, 175, 0), // 106 Yellow4
(135, 175, 95), // 107 DarkOliveGreen3
(135, 175, 135), // 108 DarkSeaGreen
(135, 175, 175), // 109 LightSkyBlue3
(135, 175, 215), // 110 LightSkyBlue3
(135, 175, 255), // 111 SkyBlue2
(135, 215, 0), // 112 Chartreuse2
(135, 215, 95), // 113 DarkOliveGreen3
(135, 215, 135), // 114 PaleGreen3
(135, 215, 175), // 115 DarkSeaGreen3
(135, 215, 215), // 116 DarkSlateGray3
(135, 215, 255), // 117 SkyBlue1
(135, 255, 0), // 118 Chartreuse1
(135, 255, 95), // 119 LightGreen
(135, 255, 135), // 120 LightGreen
(135, 255, 175), // 121 PaleGreen1
(135, 255, 215), // 122 Aquamarine1
(135, 255, 255), // 123 DarkSlateGray1
(175, 0, 0), // 124 Red3
(175, 0, 95), // 125 DeepPink4
(175, 0, 135), // 126 MediumVioletRed
(175, 0, 175), // 127 Magenta3
(175, 0, 215), // 128 DarkViolet
(175, 0, 255), // 129 Purple
(175, 95, 0), // 130 DarkOrange3
(175, 95, 95), // 131 IndianRed
(175, 95, 135), // 132 HotPink3
(175, 95, 175), // 133 MediumOrchid3
(175, 95, 215), // 134 MediumOrchid
(175, 95, 255), // 135 MediumPurple2
(175, 135, 0), // 136 DarkGoldenrod
(175, 135, 95), // 137 LightSalmon3
(175, 135, 135), // 138 RosyBrown
(175, 135, 175), // 139 Grey63
(175, 135, 215), // 140 MediumPurple2
(175, 135, 255), // 141 MediumPurple1
(175, 175, 0), // 142 Gold3
(175, 175, 95), // 143 DarkKhaki
(175, 175, 135), // 144 NavajoWhite3
(175, 175, 175), // 145 Grey69
(175, 175, 215), // 146 LightSteelBlue3
(175, 175, 255), // 147 LightSteelBlue
(175, 215, 0), // 148 Yellow3
(175, 215, 95), // 149 DarkOliveGreen3
(175, 215, 135), // 150 DarkSeaGreen3
(175, 215, 175), // 151 DarkSeaGreen2
(175, 215, 215), // 152 LightCyan3
(175, 215, 255), // 153 LightSkyBlue1
(175, 255, 0), // 154 GreenYellow
(175, 255, 95), // 155 DarkOliveGreen2
(175, 255, 135), // 156 PaleGreen1
(175, 255, 175), // 157 DarkSeaGreen2
(175, 255, 215), // 158 DarkSeaGreen1
(175, 255, 255), // 159 PaleTurquoise1
(215, 0, 0), // 160 Red3
(215, 0, 95), // 161 DeepPink3
(215, 0, 135), // 162 DeepPink3
(215, 0, 175), // 163 Magenta3
(215, 0, 215), // 164 Magenta3
(215, 0, 255), // 165 Magenta2
(215, 95, 0), // 166 DarkOrange3
(215, 95, 95), // 167 IndianRed
(215, 95, 135), // 168 HotPink3
(215, 95, 175), // 169 HotPink2
(215, 95, 215), // 170 Orchid
(215, 95, 255), // 171 MediumOrchid1
(215, 135, 0), // 172 Orange3
(215, 135, 95), // 173 LightSalmon3
(215, 135, 135), // 174 LightPink3
(215, 135, 175), // 175 Pink3
(215, 135, 215), // 176 Plum3
(215, 135, 255), // 177 Violet
(215, 175, 0), // 178 Gold3
(215, 175, 95), // 179 LightGoldenrod3
(215, 175, 135), // 180 Tan
(215, 175, 175), // 181 MistyRose3
(215, 175, 215), // 182 Thistle3
(215, 175, 255), // 183 Plum2
(215, 215, 0), // 184 Yellow3
(215, 215, 95), // 185 Khaki3
(215, 215, 135), // 186 LightGoldenrod2
(215, 215, 175), // 187 LightYellow3
(215, 215, 215), // 188 Grey84
(215, 215, 255), // 189 LightSteelBlue1
(215, 255, 0), // 190 Yellow2
(215, 255, 95), // 191 DarkOliveGreen1
(215, 255, 135), // 192 DarkOliveGreen1
(215, 255, 175), // 193 DarkSeaGreen1
(215, 255, 215), // 194 Honeydew2
(215, 255, 255), // 195 LightCyan1
(255, 0, 0), // 196 Red1
(255, 0, 95), // 197 DeepPink2
(255, 0, 135), // 198 DeepPink1
(255, 0, 175), // 199 DeepPink1
(255, 0, 215), // 200 Magenta2
(255, 0, 255), // 201 Magenta1
(255, 95, 0), // 202 OrangeRed1
(255, 95, 95), // 203 IndianRed1
(255, 95, 135), // 204 IndianRed1
(255, 95, 175), // 205 HotPink
(255, 95, 215), // 206 HotPink
(255, 95, 255), // 207 MediumOrchid1
(255, 135, 0), // 208 DarkOrange
(255, 135, 95), // 209 Salmon1
(255, 135, 135), // 210 LightCoral
(255, 135, 175), // 211 PaleVioletRed1
(255, 135, 215), // 212 Orchid2
(255, 135, 255), // 213 Orchid1
(255, 175, 0), // 214 Orange1
(255, 175, 95), // 215 SandyBrown
(255, 175, 135), // 216 LightSalmon1
(255, 175, 175), // 217 LightPink1
(255, 175, 215), // 218 Pink1
(255, 175, 255), // 219 Plum1
(255, 215, 0), // 220 Gold1
(255, 215, 95), // 221 LightGoldenrod2
(255, 215, 135), // 222 LightGoldenrod2
(255, 215, 175), // 223 NavajoWhite1
(255, 215, 215), // 224 MistyRose1
(255, 215, 255), // 225 Thistle1
(255, 255, 0), // 226 Yellow1
(255, 255, 95), // 227 LightGoldenrod1
(255, 255, 135), // 228 Khaki1
(255, 255, 175), // 229 Wheat1
(255, 255, 215), // 230 Cornsilk1
(255, 255, 255), // 231 Grey100
(8, 8, 8), // 232 Grey3
(18, 18, 18), // 233 Grey7
(28, 28, 28), // 234 Grey11
(38, 38, 38), // 235 Grey15
(48, 48, 48), // 236 Grey19
(58, 58, 58), // 237 Grey23
(68, 68, 68), // 238 Grey27
(78, 78, 78), // 239 Grey30
(88, 88, 88), // 240 Grey35
(98, 98, 98), // 241 Grey39
(108, 108, 108), // 242 Grey42
(118, 118, 118), // 243 Grey46
(128, 128, 128), // 244 Grey50
(138, 138, 138), // 245 Grey54
(148, 148, 148), // 246 Grey58
(158, 158, 158), // 247 Grey62
(168, 168, 168), // 248 Grey66
(178, 178, 178), // 249 Grey70
(188, 188, 188), // 250 Grey74
(198, 198, 198), // 251 Grey78
(208, 208, 208), // 252 Grey82
(218, 218, 218), // 253 Grey85
(228, 228, 228), // 254 Grey89
(238, 238, 238), // 255 Grey93
];

View File

@@ -277,7 +277,6 @@ impl Tui {
let enhanced_keys_supported = supports_keyboard_enhancement().unwrap_or(false);
// Cache this to avoid contention with the event reader.
supports_color::on_cached(supports_color::Stream::Stdout);
let _ = crate::terminal_palette::terminal_palette();
let _ = crate::terminal_palette::default_colors();
Self {