fix(tui/theme): suppress OSC background probe when theme is explicit

This commit is contained in:
James Peter
2025-09-25 20:28:03 +10:00
parent c47f676795
commit 166fa57eaa
2 changed files with 75 additions and 6 deletions
+36 -4
View File
@@ -14,6 +14,7 @@ use codex_core::protocol::AskForApproval;
use codex_core::protocol::SandboxPolicy; use codex_core::protocol::SandboxPolicy;
use codex_core::config_types::ThemeColors; use codex_core::config_types::ThemeColors;
use codex_core::config_types::ThemeName; use codex_core::config_types::ThemeName;
use regex_lite::Regex;
use codex_login::AuthMode; use codex_login::AuthMode;
use codex_login::CodexAuth; use codex_login::CodexAuth;
use codex_ollama::DEFAULT_OSS_MODEL; use codex_ollama::DEFAULT_OSS_MODEL;
@@ -91,6 +92,21 @@ mod updates;
pub use cli::Cli; pub use cli::Cli;
fn theme_configured_in_config_file(codex_home: &std::path::Path) -> bool {
let config_path = codex_home.join("config.toml");
let Ok(contents) = std::fs::read_to_string(&config_path) else {
return false;
};
let table_pattern = Regex::new(r"(?m)^\s*\[tui\.theme\]").expect("valid regex");
if table_pattern.is_match(&contents) {
return true;
}
let inline_pattern = Regex::new(r"(?m)^\s*tui\.theme\s*=").expect("valid regex");
inline_pattern.is_match(&contents)
}
// (tests access modules directly within the crate) // (tests access modules directly within the crate)
pub async fn run_main( pub async fn run_main(
@@ -166,6 +182,9 @@ pub async fn run_main(
std::process::exit(1); std::process::exit(1);
} }
}; };
let theme_override_in_cli = cli_kv_overrides
.iter()
.any(|(path, _)| path.starts_with("tui.theme"));
let mut config = { let mut config = {
// Load configuration and support CLI overrides. // Load configuration and support CLI overrides.
@@ -184,7 +203,7 @@ pub async fn run_main(
// we load config.toml here to determine project state. // we load config.toml here to determine project state.
#[allow(clippy::print_stderr)] #[allow(clippy::print_stderr)]
let config_toml = { let (config_toml, theme_set_in_config_file) = {
let codex_home = match find_codex_home() { let codex_home = match find_codex_home() {
Ok(codex_home) => codex_home, Ok(codex_home) => codex_home,
Err(err) => { Err(err) => {
@@ -193,8 +212,10 @@ pub async fn run_main(
} }
}; };
let theme_set_in_config_file = theme_configured_in_config_file(&codex_home);
match load_config_as_toml_with_cli_overrides(&codex_home, cli_kv_overrides) { match load_config_as_toml_with_cli_overrides(&codex_home, cli_kv_overrides) {
Ok(config_toml) => config_toml, Ok(config_toml) => (config_toml, theme_set_in_config_file),
Err(err) => { Err(err) => {
eprintln!("Error loading config.toml: {err}"); eprintln!("Error loading config.toml: {err}");
std::process::exit(1); std::process::exit(1);
@@ -202,6 +223,8 @@ pub async fn run_main(
} }
}; };
let theme_configured_explicitly = theme_set_in_config_file || theme_override_in_cli;
let should_show_trust_screen = determine_repo_trust_state( let should_show_trust_screen = determine_repo_trust_state(
&mut config, &mut config,
&config_toml, &config_toml,
@@ -269,6 +292,7 @@ pub async fn run_main(
should_show_trust_screen, should_show_trust_screen,
startup_footer_notice, startup_footer_notice,
latest_upgrade_version, latest_upgrade_version,
theme_configured_explicitly,
) )
.map_err(|err| std::io::Error::other(err.to_string())) .map_err(|err| std::io::Error::other(err.to_string()))
} }
@@ -279,6 +303,7 @@ fn run_ratatui_app(
should_show_trust_screen: bool, should_show_trust_screen: bool,
startup_footer_notice: Option<String>, startup_footer_notice: Option<String>,
latest_upgrade_version: Option<String>, latest_upgrade_version: Option<String>,
theme_configured_explicitly: bool,
) -> color_eyre::Result<codex_core::protocol::TokenUsage> { ) -> color_eyre::Result<codex_core::protocol::TokenUsage> {
color_eyre::install()?; color_eyre::install()?;
@@ -291,7 +316,7 @@ fn run_ratatui_app(
tracing::error!("panic: {info}"); tracing::error!("panic: {info}");
prev_hook(info); prev_hook(info);
})); }));
maybe_apply_terminal_theme_detection(&mut config); maybe_apply_terminal_theme_detection(&mut config, theme_configured_explicitly);
let (mut terminal, terminal_info) = tui::init(&config)?; let (mut terminal, terminal_info) = tui::init(&config)?;
if config.tui.alternate_screen { if config.tui.alternate_screen {
@@ -406,7 +431,14 @@ fn cleanup_session_worktrees_and_print() {
let _ = std::fs::remove_file(&file); let _ = std::fs::remove_file(&file);
} }
fn maybe_apply_terminal_theme_detection(config: &mut Config) { fn maybe_apply_terminal_theme_detection(config: &mut Config, theme_configured_explicitly: bool) {
if theme_configured_explicitly {
tracing::info!(
"Terminal theme autodetect skipped due to explicit theme configuration"
);
return;
}
let theme = &mut config.tui.theme; let theme = &mut config.tui.theme;
let autodetect_disabled = std::env::var("CODE_DISABLE_THEME_AUTODETECT") let autodetect_disabled = std::env::var("CODE_DISABLE_THEME_AUTODETECT")
+39 -2
View File
@@ -221,10 +221,45 @@ fn query_osc_background_color() -> Option<(u8, u8, u8)> {
tty_w.flush().ok()?; tty_w.flush().ok()?;
let reply = read_osc_reply(&mut tty_r, Duration::from_millis(150))?; let reply = read_osc_reply(&mut tty_r, Duration::from_millis(150))?;
let _ = tty_w.write_all(b"\r\x1b[K");
let _ = tty_w.flush();
let reply_str = String::from_utf8_lossy(&reply); let reply_str = String::from_utf8_lossy(&reply);
parse_osc_rgb(&reply_str) parse_osc_rgb(&reply_str)
} }
fn osc_background_query_supported() -> bool {
if env::var("TMUX").is_ok() || env::var("STY").is_ok() {
return false;
}
let term = env::var("TERM").unwrap_or_default();
if term.is_empty() {
return false;
}
let term_lower = term.to_ascii_lowercase();
const UNSUPPORTED_PREFIXES: [&str; 2] = ["screen", "tmux"];
if UNSUPPORTED_PREFIXES
.iter()
.any(|prefix| term_lower.starts_with(prefix))
{
return false;
}
const UNSUPPORTED_TERMS: [&str; 5] = [
"dumb",
"linux",
"vt100",
"xterm-color",
"ansi",
];
if UNSUPPORTED_TERMS.contains(&term_lower.as_str()) {
return false;
}
true
}
fn xterm_color_to_rgb(idx: u32) -> Option<(u8, u8, u8)> { fn xterm_color_to_rgb(idx: u32) -> Option<(u8, u8, u8)> {
if idx <= 15 { if idx <= 15 {
return Some(ANSI_16_TO_RGB[idx as usize]); return Some(ANSI_16_TO_RGB[idx as usize]);
@@ -286,8 +321,10 @@ pub fn detect_dark_terminal_background() -> Option<bool> {
} }
} }
if let Some(rgb) = query_osc_background_color() { if osc_background_query_supported() {
return Some(detect_dark_from_rgb(rgb)); if let Some(rgb) = query_osc_background_color() {
return Some(detect_dark_from_rgb(rgb));
}
} }
if let Some(rgb) = parse_colorfgbg_env() { if let Some(rgb) = parse_colorfgbg_env() {