diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index e6a3266d6a3..d946ea68ea6 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -2259,14 +2259,27 @@ impl ChatComposer { } fn footer_props(&self) -> FooterProps { + let mode = self.footer_mode(); + let is_wsl = { + #[cfg(target_os = "linux")] + { + mode == FooterMode::ShortcutOverlay && crate::clipboard_paste::is_probably_wsl() + } + #[cfg(not(target_os = "linux"))] + { + false + } + }; + FooterProps { - mode: self.footer_mode(), + mode, esc_backtrack_hint: self.esc_backtrack_hint, use_shift_enter_hint: self.use_shift_enter_hint, is_task_running: self.is_task_running, quit_shortcut_key: self.quit_shortcut_key, steer_enabled: self.steer_enabled, collaboration_modes_enabled: self.collaboration_modes_enabled, + is_wsl, context_window_percent: self.context_window_percent, context_window_used_tokens: self.context_window_used_tokens, } diff --git a/codex-rs/tui/src/bottom_pane/footer.rs b/codex-rs/tui/src/bottom_pane/footer.rs index 69c5e7bb007..1ded8ce3198 100644 --- a/codex-rs/tui/src/bottom_pane/footer.rs +++ b/codex-rs/tui/src/bottom_pane/footer.rs @@ -32,8 +32,6 @@ //! In short: `single_line_footer_layout` chooses *what* best fits, and the two //! render helpers choose whether to draw the chosen line or the default //! `FooterProps` mapping. -#[cfg(target_os = "linux")] -use crate::clipboard_paste::is_probably_wsl; use crate::key_hint; use crate::key_hint::KeyBinding; use crate::render::line_utils::prefix_lines; @@ -63,6 +61,7 @@ pub(crate) struct FooterProps { pub(crate) is_task_running: bool, pub(crate) steer_enabled: bool, pub(crate) collaboration_modes_enabled: bool, + pub(crate) is_wsl: bool, /// Which key the user must press again to quit. /// /// This is rendered when `mode` is `FooterMode::QuitShortcutReminder`. @@ -554,15 +553,10 @@ fn footer_from_props_lines( vec![left_side_line(collaboration_mode_indicator, state)] } FooterMode::ShortcutOverlay => { - #[cfg(target_os = "linux")] - let is_wsl = is_probably_wsl(); - #[cfg(not(target_os = "linux"))] - let is_wsl = false; - let state = ShortcutsState { use_shift_enter_hint: props.use_shift_enter_hint, esc_backtrack_hint: props.esc_backtrack_hint, - is_wsl, + is_wsl: props.is_wsl, collaboration_modes_enabled: props.collaboration_modes_enabled, }; shortcut_overlay_lines(state) @@ -961,6 +955,7 @@ const SHORTCUTS: &[ShortcutDescriptor] = &[ mod tests { use super::*; use insta::assert_snapshot; + use pretty_assertions::assert_eq; use ratatui::Terminal; use ratatui::backend::TestBackend; @@ -1077,6 +1072,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1092,6 +1088,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1107,6 +1104,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: true, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1122,6 +1120,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1137,6 +1136,7 @@ mod tests { is_task_running: true, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1152,6 +1152,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1167,6 +1168,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1182,6 +1184,7 @@ mod tests { is_task_running: true, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: Some(72), context_window_used_tokens: None, @@ -1197,6 +1200,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: Some(123_456), @@ -1212,6 +1216,7 @@ mod tests { is_task_running: true, steer_enabled: false, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1227,6 +1232,7 @@ mod tests { is_task_running: true, steer_enabled: true, collaboration_modes_enabled: false, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1240,6 +1246,7 @@ mod tests { is_task_running: false, steer_enabled: false, collaboration_modes_enabled: true, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1266,6 +1273,7 @@ mod tests { is_task_running: true, steer_enabled: false, collaboration_modes_enabled: true, + is_wsl: false, quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, @@ -1278,4 +1286,41 @@ mod tests { Some(CollaborationModeIndicator::Plan), ); } + + #[test] + fn paste_image_shortcut_prefers_ctrl_alt_v_under_wsl() { + let descriptor = SHORTCUTS + .iter() + .find(|descriptor| descriptor.id == ShortcutId::PasteImage) + .expect("paste image shortcut"); + + let is_wsl = { + #[cfg(target_os = "linux")] + { + crate::clipboard_paste::is_probably_wsl() + } + #[cfg(not(target_os = "linux"))] + { + false + } + }; + + let expected_key = if is_wsl { + key_hint::ctrl_alt(KeyCode::Char('v')) + } else { + key_hint::ctrl(KeyCode::Char('v')) + }; + + let actual_key = descriptor + .binding_for(ShortcutsState { + use_shift_enter_hint: false, + esc_backtrack_hint: false, + is_wsl, + collaboration_modes_enabled: false, + }) + .expect("shortcut binding") + .key; + + assert_eq!(actual_key, expected_key); + } }