Skip to content

Commit d6c841f

Browse files
fix(tui): keep status indicator visible during streaming when messages are queued
Signed-off-by: vinay.badhan <[email protected]>
1 parent b09f62a commit d6c841f

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

codex-rs/tui/src/chatwidget.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,11 @@ impl ChatWidget {
632632
if let Some(controller) = self.stream_controller.as_mut() {
633633
let (cell, is_idle) = controller.on_commit_tick();
634634
if let Some(cell) = cell {
635-
self.bottom_pane.hide_status_indicator();
635+
// Don't hide the status indicator during streaming if there are queued messages
636+
// This ensures queued messages remain visible to the user
637+
if self.queued_user_messages.is_empty() {
638+
self.bottom_pane.hide_status_indicator();
639+
}
636640
self.add_boxed_history(cell);
637641
}
638642
if is_idle {
@@ -665,7 +669,11 @@ impl ChatWidget {
665669

666670
fn handle_stream_finished(&mut self) {
667671
if self.task_complete_pending {
668-
self.bottom_pane.hide_status_indicator();
672+
// Only hide the status indicator if there are no queued messages
673+
// This ensures queued messages remain visible until the task completes
674+
if self.queued_user_messages.is_empty() {
675+
self.bottom_pane.hide_status_indicator();
676+
}
669677
self.task_complete_pending = false;
670678
}
671679
// A completed stream indicates non-exec content was just inserted.

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,3 +2344,73 @@ printf 'fenced within fenced\n'
23442344

23452345
assert_snapshot!(term.backend().vt100().screen().contents());
23462346
}
2347+
2348+
#[test]
2349+
fn queued_messages_remain_visible_during_streaming() {
2350+
// Test for GitHub issue #4446: Messages sent while final output text is streamed
2351+
// should be properly queued and visible, not injected into the streamed text.
2352+
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
2353+
2354+
// Start a task to enable queuing
2355+
chat.handle_codex_event(Event {
2356+
id: "task-1".into(),
2357+
msg: EventMsg::TaskStarted(TaskStartedEvent {
2358+
model_context_window: None,
2359+
}),
2360+
});
2361+
2362+
// Start streaming some content
2363+
chat.handle_codex_event(Event {
2364+
id: "task-1".into(),
2365+
msg: EventMsg::AgentMessageDelta(AgentMessageDeltaEvent {
2366+
delta: "This is streaming content".into(),
2367+
}),
2368+
});
2369+
2370+
// Simulate a user sending a message during streaming
2371+
chat.handle_key_event(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::NONE));
2372+
chat.handle_key_event(KeyEvent::new(KeyCode::Char('e'), KeyModifiers::NONE));
2373+
chat.handle_key_event(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::NONE));
2374+
chat.handle_key_event(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::NONE));
2375+
chat.handle_key_event(KeyEvent::new(KeyCode::Char('o'), KeyModifiers::NONE));
2376+
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
2377+
2378+
// The message should be queued, not submitted immediately
2379+
assert_eq!(chat.queued_user_messages.len(), 1);
2380+
assert_eq!(chat.queued_user_messages.front().unwrap().text, "hello");
2381+
2382+
// No operation should have been sent to the backend yet
2383+
assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty));
2384+
2385+
// Continue streaming and commit some content
2386+
chat.handle_codex_event(Event {
2387+
id: "task-1".into(),
2388+
msg: EventMsg::AgentMessageDelta(AgentMessageDeltaEvent {
2389+
delta: "\n".into(),
2390+
}),
2391+
});
2392+
2393+
// Drive commit ticks to process the streaming content
2394+
chat.on_commit_tick();
2395+
2396+
// The queued message should still be visible and not have been submitted
2397+
assert_eq!(chat.queued_user_messages.len(), 1);
2398+
assert_eq!(chat.queued_user_messages.front().unwrap().text, "hello");
2399+
2400+
// Still no operation should have been sent
2401+
assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty));
2402+
2403+
// Complete the task
2404+
chat.handle_codex_event(Event {
2405+
id: "task-1".into(),
2406+
msg: EventMsg::TaskComplete(TaskCompleteEvent {
2407+
last_agent_message: Some("Task completed".into()),
2408+
}),
2409+
});
2410+
2411+
// Now the queued message should be submitted
2412+
assert!(chat.queued_user_messages.is_empty());
2413+
2414+
// Drain rx to avoid unused warnings
2415+
let _ = drain_insert_history(&mut rx);
2416+
}

0 commit comments

Comments
 (0)