From 716af373b95e586d99e62dc52b4fe9ed494431f9 Mon Sep 17 00:00:00 2001 From: CodeWhale Agent Date: Fri, 12 Jun 2026 10:59:52 -0700 Subject: [PATCH] feat(tui): Ctrl+S sends next queued message as a steer When the queued-messages bucket is non-empty, Ctrl+S now pops the front queued follow-up and steers it into the current turn. If the queue is empty the existing Ctrl+S stash behavior is preserved. - Adds a prioritized key arm before the stash handler. - Re-queues the message at the front on steer failure. - Updates English stash/queue keybinding help text. --- crates/tui/src/localization.rs | 6 ++++-- crates/tui/src/tui/ui.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index bc4eecc85..cfc611832 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -1347,7 +1347,7 @@ fn english(id: MessageId) -> &'static str { } MessageId::CmdSlopDescription => "Inspect or export the SlopLedger", MessageId::CmdStashDescription => { - "Park or restore a composer draft (Ctrl+S to push, /stash list/pop)" + "Park or restore a composer draft (Ctrl+S sends next queued message as a steer when the queue is non-empty; otherwise stash, /stash list/pop)" } MessageId::CmdStatusDescription => "Show runtime session status", MessageId::CmdStatuslineDescription => "Configure which items appear in the footer", @@ -1449,7 +1449,9 @@ fn english(id: MessageId) -> &'static str { "Delete character before / after the cursor, or remove selected attachment" } MessageId::KbClearDraft => "Clear the current draft", - MessageId::KbStashDraft => "Stash the current draft (`/stash pop` to restore)", + MessageId::KbStashDraft => { + "Send next queued message as a steer, or stash the current draft (`/stash pop` to restore)" + } MessageId::KbSearchHistory => "Search prompt history and recover local drafts", MessageId::KbInsertNewline => "Insert a newline in the composer", MessageId::KbSendDraft => "Send the current draft", diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index f699023d6..1589cfe39 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -4394,6 +4394,29 @@ async fn run_event_loop( { app.delete_word_backward(); } + KeyCode::Char('s') | KeyCode::Char('S') + if key.modifiers == KeyModifiers::CONTROL + && !app.queued_messages.is_empty() => + { + // Ctrl+S sends the next queued follow-up as a steer into + // the current turn, letting the user manually flush the + // queue without retyping it. + let message = app.pop_queued_message().expect("queue non-empty"); + if let Err(err) = steer_user_message(app, &engine_handle, message.clone()).await + { + app.queued_messages.push_front(message); + app.status_message = Some(format!( + "Steer failed ({err}); {} queued — ↑ to edit, /queue list", + app.queued_message_count() + )); + } else { + app.push_status_toast( + &format!("Steered queued message: {}", message.display), + StatusToastLevel::Info, + Some(3_000), + ); + } + } KeyCode::Char('s') | KeyCode::Char('S') if key.modifiers == KeyModifiers::CONTROL && !app.input.is_empty() => {