diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 49a34785..68f9a76f 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -837,7 +837,12 @@ impl ChatComposer { } self.pending_pastes.clear(); + // If there is neither text nor attachments, suppress submission entirely. + let has_attachments = !self.attached_images.is_empty(); text = text.trim().to_string(); + if text.is_empty() && !has_attachments { + return (InputResult::None, true); + } if !text.is_empty() { self.history.record_local_submission(&text); } @@ -1520,6 +1525,33 @@ mod tests { } } + #[test] + fn empty_enter_returns_none() { + use crossterm::event::KeyCode; + use crossterm::event::KeyEvent; + use crossterm::event::KeyModifiers; + + let (tx, _rx) = unbounded_channel::(); + let sender = AppEventSender::new(tx); + let mut composer = ChatComposer::new( + true, + sender, + false, + "Ask Codex to do anything".to_string(), + false, + ); + + // Ensure composer is empty and press Enter. + assert!(composer.textarea.text().is_empty()); + let (result, _needs_redraw) = + composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + + match result { + InputResult::None => {} + other => panic!("expected None for empty enter, got: {other:?}"), + } + } + #[test] fn handle_paste_large_uses_placeholder_and_replaces_on_submit() { use crossterm::event::KeyCode; diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 117b4bad..a1adfaef 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -259,6 +259,20 @@ fn open_fixture(name: &str) -> std::fs::File { File::open(name).expect("open fixture file") } +#[test] +fn empty_enter_during_task_does_not_queue() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(); + + // Simulate running task so submissions would normally be queued. + chat.bottom_pane.set_task_running(true); + + // Press Enter with an empty composer. + chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + + // Ensure nothing was queued. + assert!(chat.queued_user_messages.is_empty()); +} + #[test] fn alt_up_edits_most_recent_queued_message() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual();