diff --git a/codex-rs/tui/src/app/startup.rs b/codex-rs/tui/src/app/startup.rs index 4f03ceed03..21badd2f67 100644 --- a/codex-rs/tui/src/app/startup.rs +++ b/codex-rs/tui/src/app/startup.rs @@ -1296,7 +1296,7 @@ See the Codex keymap documentation for supported actions and examples." if !tui.is_owned_screen() && tui.is_alt_screen_active() { app.close_transcript_overlay(tui); } else if let Err(err) = - tui.set_mouse_capture(crate::tui::MouseCapture::Inherit) + tui.set_mouse_capture(app.chat_widget.mouse_capture(tui.is_owned_screen())) { break Err(err.into()); } diff --git a/codex-rs/tui/src/chatwidget/questions.rs b/codex-rs/tui/src/chatwidget/questions.rs index 83884b3228..c65a10c216 100644 --- a/codex-rs/tui/src/chatwidget/questions.rs +++ b/codex-rs/tui/src/chatwidget/questions.rs @@ -3,9 +3,25 @@ use super::*; use crate::bottom_pane::QuestionSubmission; +use crate::tui::MouseCapture; use codex_protocol::items::AsyncUserInputQuestion; impl ChatWidget { + pub(crate) fn mouse_capture(&self, owned_screen: bool) -> MouseCapture { + if owned_screen + && !self.bottom_pane.has_active_modal() + && self + .bottom_pane + .questions + .as_ref() + .is_some_and(|q| q.expanded) + { + MouseCapture::Disabled + } else { + MouseCapture::Inherit + } + } + pub(super) fn take_question_drafts(&mut self) -> Option> { if matches!( self.pending_notification, diff --git a/codex-rs/tui/src/chatwidget/tests/questions_tests.rs b/codex-rs/tui/src/chatwidget/tests/questions_tests.rs index 6357208c95..13c345bd1c 100644 --- a/codex-rs/tui/src/chatwidget/tests/questions_tests.rs +++ b/codex-rs/tui/src/chatwidget/tests/questions_tests.rs @@ -668,6 +668,27 @@ fn open_questions(chat: &mut ChatWidget, options: Option>) { render_bottom_popup(chat, /*width*/ 80); } +#[tokio::test] +async fn expanded_question_yields_mouse_to_terminal_for_links() { + use crate::tui::MouseCapture::Disabled; + use crate::tui::MouseCapture::Inherit; + + let (mut chat, _rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await; + let url = "https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/r2/overview"; + chat.add_async_questions("message", &[question(url, /*options*/ None)]); + assert_eq!(chat.mouse_capture(/*owned_screen*/ true), Inherit); + chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::ALT)); + assert_eq!(chat.mouse_capture(/*owned_screen*/ true), Disabled); + assert_eq!(chat.mouse_capture(/*owned_screen*/ false), Inherit); + chat.handle_key_event(KeyEvent::new(KeyCode::Down, KeyModifiers::ALT)); + assert_eq!(chat.mouse_capture(/*owned_screen*/ true), Inherit); + + chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::ALT)); + chat.open_warnings(&[]); + assert!(render_bottom_popup(&chat, /*width*/ 100).contains(url)); + assert_eq!(chat.mouse_capture(/*owned_screen*/ true), Disabled); +} + #[tokio::test] async fn question_queue_key_does_not_steer_the_running_turn() { let (mut chat, _rx, mut ops) = make_chatwidget_manual(/*model_override*/ None).await;