Skip to content

Commit 7ac6194

Browse files
authored
Bug fix: ignore Enter on empty input to avoid queuing blank messages (#3047)
## Summary Pressing Enter with an empty composer was treated as a submission, which queued a blank message while a task was running. This PR suppresses submission when there is no text and no attachments. ## Root Cause - ChatComposer returned Submitted even when the trimmed text was empty. ChatWidget then queued it during a running task, leading to an empty item appearing in the queued list and being popped later with no effect. ## Changes - ChatComposer Enter handling: if trimmed text is empty and there are no attached images, return None instead of Submitted. - No changes to ChatWidget; behavior naturally stops queuing blanks at the source. ## Code Paths - Modified: `tui/src/bottom_pane/chat_composer.rs` - Tests added: - `tui/src/bottom_pane/chat_composer.rs`: `empty_enter_returns_none` - `tui/src/chatwidget/tests.rs`: `empty_enter_during_task_does_not_queue` ## Result ### Before https://github.com/user-attachments/assets/a40e2f6d-42ba-4a82-928b-8f5458f5884d ### After https://github.com/user-attachments/assets/958900b7-a566-44fc-b16c-b80380739c92
1 parent 619436c commit 7ac6194

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

codex-rs/tui/src/bottom_pane/chat_composer.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,12 @@ impl ChatComposer {
837837
}
838838
self.pending_pastes.clear();
839839

840+
// If there is neither text nor attachments, suppress submission entirely.
841+
let has_attachments = !self.attached_images.is_empty();
840842
text = text.trim().to_string();
843+
if text.is_empty() && !has_attachments {
844+
return (InputResult::None, true);
845+
}
841846
if !text.is_empty() {
842847
self.history.record_local_submission(&text);
843848
}
@@ -1520,6 +1525,33 @@ mod tests {
15201525
}
15211526
}
15221527

1528+
#[test]
1529+
fn empty_enter_returns_none() {
1530+
use crossterm::event::KeyCode;
1531+
use crossterm::event::KeyEvent;
1532+
use crossterm::event::KeyModifiers;
1533+
1534+
let (tx, _rx) = unbounded_channel::<AppEvent>();
1535+
let sender = AppEventSender::new(tx);
1536+
let mut composer = ChatComposer::new(
1537+
true,
1538+
sender,
1539+
false,
1540+
"Ask Codex to do anything".to_string(),
1541+
false,
1542+
);
1543+
1544+
// Ensure composer is empty and press Enter.
1545+
assert!(composer.textarea.text().is_empty());
1546+
let (result, _needs_redraw) =
1547+
composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
1548+
1549+
match result {
1550+
InputResult::None => {}
1551+
other => panic!("expected None for empty enter, got: {other:?}"),
1552+
}
1553+
}
1554+
15231555
#[test]
15241556
fn handle_paste_large_uses_placeholder_and_replaces_on_submit() {
15251557
use crossterm::event::KeyCode;

codex-rs/tui/src/chatwidget/tests.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,20 @@ fn open_fixture(name: &str) -> std::fs::File {
259259
File::open(name).expect("open fixture file")
260260
}
261261

262+
#[test]
263+
fn empty_enter_during_task_does_not_queue() {
264+
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
265+
266+
// Simulate running task so submissions would normally be queued.
267+
chat.bottom_pane.set_task_running(true);
268+
269+
// Press Enter with an empty composer.
270+
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
271+
272+
// Ensure nothing was queued.
273+
assert!(chat.queued_user_messages.is_empty());
274+
}
275+
262276
#[test]
263277
fn alt_up_edits_most_recent_queued_message() {
264278
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();

0 commit comments

Comments
 (0)