Preserve composer Escape handling during MCP startup

This commit is contained in:
Charlie Marsh
2026-06-28 11:11:13 -04:00
parent 7b49b934c2
commit 9d6630f82e
3 changed files with 41 additions and 2 deletions
+9 -1
View File
@@ -1298,6 +1298,14 @@ impl BottomPane {
}
pub(crate) fn should_interrupt_running_task(&self, key_event: KeyEvent) -> bool {
self.should_interrupt_running_task_allowing_composer_popup(key_event)
&& !self.composer.popup_active()
}
pub(crate) fn should_interrupt_running_task_allowing_composer_popup(
&self,
key_event: KeyEvent,
) -> bool {
let is_agent_command = self
.composer_text()
.lines()
@@ -1308,7 +1316,7 @@ impl BottomPane {
self.keymap.chat.interrupt_turn.is_pressed(key_event)
&& self.is_task_running
&& !(is_agent_command && key_event.code == KeyCode::Esc)
&& self.no_modal_or_popup_active()
&& self.view_stack.is_empty()
&& !self.composer_should_handle_vim_insert_escape(key_event)
&& self.status.is_some()
}
+3 -1
View File
@@ -159,7 +159,9 @@ impl ChatWidget {
// hint authoritative even after typing opens a composer popup such as slash completion.
if self.mcp_startup_status.is_some()
&& !self.turn_lifecycle.agent_turn_running
&& self.chat_keymap.interrupt_turn.is_pressed(key_event)
&& self
.bottom_pane
.should_interrupt_running_task_allowing_composer_popup(key_event)
{
self.submit_op(AppCommand::interrupt());
return;
@@ -138,6 +138,35 @@ async fn esc_interrupts_mcp_startup_while_slash_popup_is_open() {
assert_eq!(chat.bottom_pane.composer_text(), "/review");
}
#[tokio::test]
async fn esc_during_mcp_startup_leaves_vim_insert_mode_without_interrupting() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let esc = KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE);
notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting);
chat.toggle_vim_mode_and_notify();
chat.handle_key_event(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE));
assert!(chat.should_handle_vim_insert_escape(esc));
chat.handle_key_event(esc);
assert!(!chat.should_handle_vim_insert_escape(esc));
assert!(op_rx.try_recv().is_err());
}
#[tokio::test]
async fn esc_during_mcp_startup_does_not_interrupt_agent_draft() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting);
chat.bottom_pane
.set_composer_text("/agent ".to_string(), Vec::new(), Vec::new());
assert!(chat.bottom_pane.no_modal_or_popup_active());
chat.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
assert!(op_rx.try_recv().is_err());
assert_eq!(chat.bottom_pane.composer_text(), "/agent ");
}
#[tokio::test]
async fn mcp_startup_complete_does_not_clear_running_task() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;