mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
fix(tui): let esc exit empty shell mode (#19986)
## Summary - exit shell mode when `Esc` is pressed while the absorbed `!` is the only input - add direct regression coverage plus a composer snapshot for the restored normal prompt state ## Root cause Shell mode stores the leading `!` outside the editable textarea. After typing only `!`, the textarea is empty but the composer is still in bash mode, so the existing empty-composer `Esc` handling never runs. ## Validation - `just fmt` - `cargo test -p codex-tui bottom_pane::chat_composer::tests::esc_exits_empty_shell_mode` - `cargo test -p codex-tui bottom_pane::chat_composer::tests::footer_mode_snapshots` - `cargo insta pending-snapshots` `cargo test -p codex-tui` still reports unrelated existing `/status` snapshot drift in this local environment because the rendered permissions text is `workspace-write with network access` instead of the older `read-only` fixture text.
This commit is contained in:
committed by
GitHub
Unverified
parent
bc5a1b961e
commit
a036584104
@@ -2972,6 +2972,15 @@ impl ChatComposer {
|
||||
if self.handle_shortcut_overlay_key(&key_event) {
|
||||
return (InputResult::None, true);
|
||||
}
|
||||
if self.is_bash_mode && key_event.code == KeyCode::Esc {
|
||||
if let Some(pasted) = self.paste_burst.flush_before_modified_input() {
|
||||
self.handle_paste(pasted);
|
||||
}
|
||||
if self.textarea.is_empty() {
|
||||
self.is_bash_mode = false;
|
||||
return (InputResult::None, true);
|
||||
}
|
||||
}
|
||||
if key_event.code == KeyCode::Esc {
|
||||
if self.is_empty() {
|
||||
let next_mode = esc_hint_mode(self.footer_mode, self.is_task_running);
|
||||
@@ -4790,6 +4799,19 @@ mod tests {
|
||||
composer.set_text_content("!git status".to_string(), Vec::new(), Vec::new());
|
||||
},
|
||||
);
|
||||
|
||||
snapshot_composer_state(
|
||||
"footer_mode_shell_command_escape_exits_empty_mode",
|
||||
/*enhanced_keys_supported*/ true,
|
||||
|composer| {
|
||||
composer.set_status_line_enabled(/*enabled*/ true);
|
||||
composer.set_status_line(Some(Line::from(
|
||||
"gpt-5.4 high fast · ~/code/codex-1 · Context 0% used",
|
||||
)));
|
||||
composer.set_text_content("!".to_string(), Vec::new(), Vec::new());
|
||||
let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4852,6 +4874,65 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn esc_exits_empty_shell_mode() {
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyModifiers;
|
||||
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
let sender = AppEventSender::new(tx);
|
||||
let mut composer = ChatComposer::new(
|
||||
/*has_input_focus*/ true,
|
||||
sender,
|
||||
/*enhanced_keys_supported*/ false,
|
||||
"Ask Codex to do anything".to_string(),
|
||||
/*disable_paste_burst*/ false,
|
||||
);
|
||||
|
||||
type_chars_humanlike(&mut composer, &['!']);
|
||||
assert!(composer.is_bash_mode);
|
||||
assert_eq!(composer.current_text(), "!");
|
||||
|
||||
let (result, needs_redraw) =
|
||||
composer.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
||||
|
||||
assert!(matches!(result, InputResult::None));
|
||||
assert!(needs_redraw);
|
||||
assert!(!composer.is_bash_mode);
|
||||
assert_eq!(composer.current_text(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn esc_keeps_shell_mode_when_paste_burst_flushes_pending_text() {
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyModifiers;
|
||||
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
let sender = AppEventSender::new(tx);
|
||||
let mut composer = ChatComposer::new(
|
||||
/*has_input_focus*/ true,
|
||||
sender,
|
||||
/*enhanced_keys_supported*/ false,
|
||||
"Ask Codex to do anything".to_string(),
|
||||
/*disable_paste_burst*/ false,
|
||||
);
|
||||
|
||||
type_chars_humanlike(&mut composer, &['!']);
|
||||
let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Char('g'), KeyModifiers::NONE));
|
||||
assert!(composer.is_in_paste_burst());
|
||||
assert_eq!(composer.current_text(), "!");
|
||||
|
||||
let (result, needs_redraw) =
|
||||
composer.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
||||
|
||||
assert!(matches!(result, InputResult::None));
|
||||
assert!(needs_redraw);
|
||||
assert!(composer.is_bash_mode);
|
||||
assert_eq!(composer.current_text(), "!g");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn footer_collapse_snapshots() {
|
||||
fn setup_collab_footer(
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 4485
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" gpt-5.4 high fast · ~/code/codex-1 · Context 0% used "
|
||||
Reference in New Issue
Block a user