fix(tui): keep Ghostty motion override live
This commit is contained in:
@@ -658,6 +658,7 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
|
||||
if let Err(e) = settings.set(&key, value) {
|
||||
return CommandResult::error(format!("{e}"));
|
||||
}
|
||||
settings.apply_env_overrides();
|
||||
|
||||
let mut action = None;
|
||||
match key.as_str() {
|
||||
@@ -835,6 +836,8 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
|
||||
},
|
||||
),
|
||||
"composer_vim_mode" | "vim_mode" | "vim" => settings.composer_vim_mode.clone(),
|
||||
"low_motion" | "motion" => settings.low_motion.to_string(),
|
||||
"fancy_animations" | "fancy" | "animations" => settings.fancy_animations.to_string(),
|
||||
_ => value.to_string(),
|
||||
};
|
||||
|
||||
@@ -1394,6 +1397,44 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_fancy_animations_obeys_ghostty_override() {
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"codewhale-tui-ghostty-fancy-config-test-{}",
|
||||
std::process::id()
|
||||
));
|
||||
fs::create_dir_all(&temp_root).unwrap();
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
let prev_term_program = env::var_os("TERM_PROGRAM");
|
||||
// Safety: test-only environment mutation guarded by EnvGuard's lock.
|
||||
unsafe {
|
||||
env::set_var("TERM_PROGRAM", "Ghostty");
|
||||
}
|
||||
|
||||
let mut app = create_test_app();
|
||||
assert!(!app.fancy_animations);
|
||||
|
||||
let result = set_config_value(&mut app, "fancy_animations", "true", false);
|
||||
|
||||
assert!(!result.is_error);
|
||||
assert!(
|
||||
!app.fancy_animations,
|
||||
"Ghostty compatibility override must keep the water strip disabled"
|
||||
);
|
||||
assert_eq!(
|
||||
result.message.as_deref(),
|
||||
Some("fancy_animations = false (session only, add --save to persist)")
|
||||
);
|
||||
|
||||
// Safety: cleanup under EnvGuard's lock.
|
||||
unsafe {
|
||||
match prev_term_program {
|
||||
Some(v) => env::set_var("TERM_PROGRAM", v),
|
||||
None => env::remove_var("TERM_PROGRAM"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_model_accepts_future_deepseek_model_id() {
|
||||
let mut app = create_test_app();
|
||||
|
||||
+61
-11
@@ -456,18 +456,23 @@ impl Settings {
|
||||
self.low_motion = true;
|
||||
self.fancy_animations = false;
|
||||
}
|
||||
// VS Code (TERM_PROGRAM=vscode, #1356), Ghostty (TERM_PROGRAM=ghostty,
|
||||
// #1445), and a few VTE terminals (#1470) produce visible flicker at
|
||||
// 120 FPS. Drop to the 30 FPS low-motion cap for them automatically.
|
||||
// VS Code (TERM_PROGRAM=vscode, #1356), Ghostty (#1445), and a few
|
||||
// VTE terminals (#1470) produce visible flicker at 120 FPS. Drop to
|
||||
// the 30 FPS low-motion cap for them automatically. Ghostty may report
|
||||
// either TERM_PROGRAM=Ghostty/ghostty or TERM=xterm-ghostty.
|
||||
// Like NO_ANIMATIONS above, this unconditionally overrides any
|
||||
// disk-loaded value — consistent precedence: env signals always win.
|
||||
let term_program = std::env::var("TERM_PROGRAM")
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
let term = std::env::var("TERM")
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
let term_forces_low_motion =
|
||||
matches!(term_program.as_str(), "vscode" | "ghostty") || term.contains("ghostty");
|
||||
let vte_env_forces_low_motion = std::env::var_os("TILIX_ID").is_some_and(|v| !v.is_empty())
|
||||
|| std::env::var_os("TERMINATOR_UUID").is_some_and(|v| !v.is_empty());
|
||||
if matches!(
|
||||
std::env::var("TERM_PROGRAM").as_deref(),
|
||||
Ok("vscode") | Ok("ghostty")
|
||||
) || vte_env_forces_low_motion
|
||||
{
|
||||
if term_forces_low_motion || vte_env_forces_low_motion {
|
||||
self.low_motion = true;
|
||||
self.fancy_animations = false;
|
||||
}
|
||||
@@ -1673,6 +1678,7 @@ mod tests {
|
||||
let prev_tmux = std::env::var_os("TMUX");
|
||||
let prev_sty = std::env::var_os("STY");
|
||||
let prev_term_program = std::env::var_os("TERM_PROGRAM");
|
||||
let prev_term = std::env::var_os("TERM");
|
||||
let prev_ssh_client = std::env::var_os("SSH_CLIENT");
|
||||
let prev_ssh_tty = std::env::var_os("SSH_TTY");
|
||||
let prev_tilix_id = std::env::var_os("TILIX_ID");
|
||||
@@ -1690,6 +1696,7 @@ mod tests {
|
||||
std::env::remove_var("TMUX");
|
||||
std::env::remove_var("STY");
|
||||
std::env::remove_var("TERM_PROGRAM");
|
||||
std::env::remove_var("TERM");
|
||||
std::env::remove_var("SSH_CLIENT");
|
||||
std::env::remove_var("SSH_TTY");
|
||||
std::env::remove_var("TILIX_ID");
|
||||
@@ -1736,6 +1743,10 @@ mod tests {
|
||||
Some(v) => std::env::set_var("TERM_PROGRAM", v),
|
||||
None => std::env::remove_var("TERM_PROGRAM"),
|
||||
}
|
||||
match prev_term {
|
||||
Some(v) => std::env::set_var("TERM", v),
|
||||
None => std::env::remove_var("TERM"),
|
||||
}
|
||||
match prev_ssh_client {
|
||||
Some(v) => std::env::set_var("SSH_CLIENT", v),
|
||||
None => std::env::remove_var("SSH_CLIENT"),
|
||||
@@ -1799,18 +1810,18 @@ mod tests {
|
||||
let prev = std::env::var_os("TERM_PROGRAM");
|
||||
// SAFETY: serialised by the guard.
|
||||
unsafe {
|
||||
std::env::set_var("TERM_PROGRAM", "ghostty");
|
||||
std::env::set_var("TERM_PROGRAM", "Ghostty");
|
||||
}
|
||||
let mut settings = Settings::default();
|
||||
assert!(!settings.low_motion, "default is animated");
|
||||
settings.apply_env_overrides();
|
||||
assert!(
|
||||
settings.low_motion,
|
||||
"TERM_PROGRAM=ghostty must enable low_motion to prevent flickering (#1445)"
|
||||
"TERM_PROGRAM=Ghostty must enable low_motion to prevent flickering (#1445)"
|
||||
);
|
||||
assert!(
|
||||
!settings.fancy_animations,
|
||||
"TERM_PROGRAM=ghostty must disable fancy_animations"
|
||||
"TERM_PROGRAM=Ghostty must disable fancy_animations"
|
||||
);
|
||||
// SAFETY: cleanup under the guard.
|
||||
unsafe {
|
||||
@@ -1821,10 +1832,44 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ghostty_term_fallback_forces_low_motion_on() {
|
||||
let _g = term_program_test_guard();
|
||||
let prev_program = std::env::var_os("TERM_PROGRAM");
|
||||
let prev_term = std::env::var_os("TERM");
|
||||
// SAFETY: serialised by the guard.
|
||||
unsafe {
|
||||
std::env::remove_var("TERM_PROGRAM");
|
||||
std::env::set_var("TERM", "xterm-ghostty");
|
||||
}
|
||||
let mut settings = Settings::default();
|
||||
settings.apply_env_overrides();
|
||||
assert!(
|
||||
settings.low_motion,
|
||||
"TERM=xterm-ghostty must enable low_motion when TERM_PROGRAM is absent"
|
||||
);
|
||||
assert!(
|
||||
!settings.fancy_animations,
|
||||
"TERM=xterm-ghostty must disable fancy_animations"
|
||||
);
|
||||
// SAFETY: cleanup under the guard.
|
||||
unsafe {
|
||||
match prev_program {
|
||||
Some(v) => std::env::set_var("TERM_PROGRAM", v),
|
||||
None => std::env::remove_var("TERM_PROGRAM"),
|
||||
}
|
||||
match prev_term {
|
||||
Some(v) => std::env::set_var("TERM", v),
|
||||
None => std::env::remove_var("TERM"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_vscode_term_program_does_not_force_low_motion() {
|
||||
let _g = term_program_test_guard();
|
||||
let prev = std::env::var_os("TERM_PROGRAM");
|
||||
let prev_term = std::env::var_os("TERM");
|
||||
let prev_ssh_client = std::env::var_os("SSH_CLIENT");
|
||||
let prev_ssh_tty = std::env::var_os("SSH_TTY");
|
||||
let prev_tilix_id = std::env::var_os("TILIX_ID");
|
||||
@@ -1838,6 +1883,7 @@ mod tests {
|
||||
unsafe {
|
||||
std::env::remove_var("SSH_CLIENT");
|
||||
std::env::remove_var("SSH_TTY");
|
||||
std::env::remove_var("TERM");
|
||||
std::env::remove_var("TILIX_ID");
|
||||
std::env::remove_var("TERMINATOR_UUID");
|
||||
std::env::remove_var("TMUX");
|
||||
@@ -1861,6 +1907,10 @@ mod tests {
|
||||
Some(v) => std::env::set_var("TERM_PROGRAM", v),
|
||||
None => std::env::remove_var("TERM_PROGRAM"),
|
||||
}
|
||||
match prev_term {
|
||||
Some(v) => std::env::set_var("TERM", v),
|
||||
None => std::env::remove_var("TERM"),
|
||||
}
|
||||
if let Some(v) = prev_ssh_client {
|
||||
std::env::set_var("SSH_CLIENT", v);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user