Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src-rust/crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2560,7 +2560,7 @@ async fn run_interactive(
crossterm::event::KeyModifiers::NONE | crossterm::event::KeyModifiers::SHIFT
) {
if let KeyCode::Char(c) = key.code {
if app.prompt_is_accepting_text() && !app.any_modal_open() {
if app.prompt_is_accepting_text() && !app.any_blocking_modal_open() {
if let Some(burst) = app.try_detect_paste_burst(c) {
app.handle_paste_data(burst);
app.refresh_prompt_input();
Expand All @@ -2579,9 +2579,13 @@ async fn run_interactive(
continue;
}

// Enter => submit input (but NOT when ANY dialog/overlay is open —
// dialogs handle their own Enter in handle_key_event).
let any_dialog_open = app.any_modal_open();
// Enter => submit input (but NOT when a dialog/overlay that
// captures input is open — those handle their own Enter in
// handle_key_event). Gate on the *blocking* predicate:
// `any_modal_open` also counts passive banners that render
// as overlays but never take a keystroke, and those would
// silently eat Enter and strand the conversation.
let any_dialog_open = app.any_blocking_modal_open();
if key.code == KeyCode::Enter && app.is_streaming && !any_dialog_open {
// Queue the message: it will auto-submit once the
// current turn finishes (issue #149).
Expand Down
16 changes: 15 additions & 1 deletion src-rust/crates/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6225,7 +6225,21 @@ impl App {
match crossterm::event::read() {
Ok(Event::Key(k)) if k.kind == KeyEventKind::Press => match k.code {
KeyCode::Char(c) => buf.push(c),
KeyCode::Enter => buf.push('\n'),
KeyCode::Enter => {
// A newline with more input behind it is an interior
// line break of a multi-line paste, so it belongs in
// the text. A newline with nothing behind it is the
// keystroke that ends the line — replay it so the
// caller submits. Swallowing it here is how a pasted
// (or programmatically typed) message ends up sitting
// in the prompt with a trailing '\n', never sent.
if crossterm::event::poll(std::time::Duration::ZERO).unwrap_or(false) {
buf.push('\n');
} else {
self.pending_key = Some(k);
break;
Comment on lines +6236 to +6240
}
}
_ => {
// Non-character key — save it for replay.
self.pending_key = Some(k);
Expand Down