From 236fa4260009c29a086d2c5a3331fdbc853456fc Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Tue, 12 May 2026 07:50:39 -0700 Subject: [PATCH] TUI: refine paused queued-send resume flow --- .../src/bottom_pane/pending_input_preview.rs | 4 ++-- ...ests__render_paused_after_usage_limit.snap | 6 ++--- ...der_paused_steering_after_usage_limit.snap | 6 ++--- codex-rs/tui/src/chatwidget/queued_sends.rs | 6 +++-- .../src/chatwidget/tests/status_and_layout.rs | 24 +++++++++++++++++++ 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs index 3cb932c5fa..8ef00ea8b3 100644 --- a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs +++ b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs @@ -89,12 +89,12 @@ impl PendingInputPreview { Line::from("Queued sends paused after usage limit".cyan().bold()), ); lines.push(Line::from(vec![ - " Press ".into(), + " Empty composer: press ".into(), key_hint::plain(KeyCode::Enter) .display_label() .cyan() .bold(), - " to resume queued sends".into(), + " to review queue".into(), ])); } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_after_usage_limit.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_after_usage_limit.snap index e4a2b6db89..bc34432365 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_after_usage_limit.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_after_usage_limit.snap @@ -6,7 +6,7 @@ Buffer { area: Rect { x: 0, y: 0, width: 48, height: 6 }, content: [ "• Queued sends paused after usage limit ", - " Press enter to resume queued sends ", + " Empty composer: press enter to review queue ", " ", "• Queued follow-up inputs ", " ↳ Try again later ", @@ -16,8 +16,8 @@ Buffer { x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, x: 2, y: 0, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, x: 39, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, - x: 8, y: 1, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, - x: 13, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 24, y: 1, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, + x: 29, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 0, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, x: 2, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 0, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_steering_after_usage_limit.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_steering_after_usage_limit.snap index 139ea0bc32..5666c4d0e5 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_steering_after_usage_limit.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_paused_steering_after_usage_limit.snap @@ -6,7 +6,7 @@ Buffer { area: Rect { x: 0, y: 0, width: 48, height: 5 }, content: [ "• Queued sends paused after usage limit ", - " Press enter to resume queued sends ", + " Empty composer: press enter to review queue ", " ", "• Messages to be submitted at end of turn ", " ↳ Check the final command output. ", @@ -15,8 +15,8 @@ Buffer { x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, x: 2, y: 0, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, x: 39, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, - x: 8, y: 1, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, - x: 13, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 24, y: 1, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD, + x: 29, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 0, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, x: 2, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 0, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, diff --git a/codex-rs/tui/src/chatwidget/queued_sends.rs b/codex-rs/tui/src/chatwidget/queued_sends.rs index c7b5b3bd10..8b8733beb6 100644 --- a/codex-rs/tui/src/chatwidget/queued_sends.rs +++ b/codex-rs/tui/src/chatwidget/queued_sends.rs @@ -51,7 +51,9 @@ impl ChatWidget { pub(crate) fn resume_queued_sends(&mut self) { self.input_queue.queued_sends_paused_after_usage_limit = false; - self.refresh_pending_input_preview(); - self.maybe_send_next_queued_input(); + let resumed_queue = self.maybe_send_next_queued_input(); + if !resumed_queue && !self.has_queued_follow_up_messages() { + self.maybe_show_pending_rate_limit_prompt(); + } } } diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index be4bbb34a9..47d1305d94 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -807,6 +807,30 @@ async fn rate_limit_switch_prompt_shows_when_paused_queue_is_cleared() { assert!(!chat.bottom_pane.no_modal_or_popup_active()); } +#[tokio::test] +async fn resuming_paused_queue_keeps_rate_limit_switch_prompt_deferred() { + let (mut chat, _, mut op_rx) = make_chatwidget_manual(Some("gpt-5")).await; + chat.thread_id = Some(ThreadId::new()); + chat.has_chatgpt_account = true; + chat.rate_limit_switch_prompt = RateLimitSwitchPromptState::Pending; + chat.input_queue + .queued_user_messages + .push_back(UserMessage::from("queued follow-up").into()); + chat.input_queue + .queued_user_message_history_records + .push_back(UserMessageHistoryRecord::UserMessageText); + chat.input_queue.queued_sends_paused_after_usage_limit = true; + + chat.resume_queued_sends(); + + assert!(matches!( + chat.rate_limit_switch_prompt, + RateLimitSwitchPromptState::Pending + )); + assert!(chat.bottom_pane.no_modal_or_popup_active()); + assert!(matches!(next_submit_op(&mut op_rx), Op::UserTurn { .. })); +} + #[tokio::test] async fn rate_limit_switch_prompt_popup_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;