mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
tui: preserve queued drafts across session switches
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
co-authored by
Codex
parent
e0627c8f37
commit
6d0be7f9d8
@@ -888,6 +888,7 @@ impl App {
|
||||
.await
|
||||
{
|
||||
Ok(resumed) => {
|
||||
let input_state = self.chat_widget.capture_thread_input_state();
|
||||
self.shutdown_current_thread().await;
|
||||
self.config = resume_config;
|
||||
tui.set_notification_method(self.config.tui_notification_method);
|
||||
@@ -905,6 +906,7 @@ impl App {
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
self.restore_input_state_after_thread_switch(input_state);
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = target_session.path.display();
|
||||
@@ -1888,6 +1890,11 @@ impl App {
|
||||
self.sync_active_agent_label();
|
||||
}
|
||||
|
||||
fn restore_input_state_after_thread_switch(&mut self, input_state: Option<ThreadInputState>) {
|
||||
self.chat_widget.restore_thread_input_state(input_state);
|
||||
self.chat_widget.drain_queued_inputs_until_blocked();
|
||||
}
|
||||
|
||||
async fn start_fresh_session_with_summary_hint(&mut self, tui: &mut tui::Tui) {
|
||||
// Start a fresh in-memory session while preserving resumability via persisted rollout
|
||||
// history.
|
||||
@@ -1900,6 +1907,7 @@ impl App {
|
||||
self.chat_widget.thread_id(),
|
||||
self.chat_widget.thread_name(),
|
||||
);
|
||||
let input_state = self.chat_widget.capture_thread_input_state();
|
||||
self.shutdown_current_thread().await;
|
||||
let report = self
|
||||
.server
|
||||
@@ -1939,6 +1947,7 @@ impl App {
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
self.restore_input_state_after_thread_switch(input_state);
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
|
||||
@@ -2569,6 +2578,7 @@ impl App {
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
let input_state = self.chat_widget.capture_thread_input_state();
|
||||
self.shutdown_current_thread().await;
|
||||
let init = self.chatwidget_init_for_forked_or_resumed_thread(
|
||||
tui,
|
||||
@@ -2592,6 +2602,7 @@ impl App {
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
self.restore_input_state_after_thread_switch(input_state);
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
@@ -4744,6 +4755,60 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_switch_restores_and_drains_queued_follow_up() {
|
||||
let (mut app, _app_event_rx, _op_rx) = make_test_app_with_channels().await;
|
||||
let thread_id = ThreadId::new();
|
||||
let session_configured = Event {
|
||||
id: "session-configured".to_string(),
|
||||
msg: EventMsg::SessionConfigured(SessionConfiguredEvent {
|
||||
session_id: thread_id,
|
||||
forked_from_id: None,
|
||||
thread_name: None,
|
||||
model: "gpt-test".to_string(),
|
||||
model_provider_id: "test-provider".to_string(),
|
||||
service_tier: None,
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
cwd: PathBuf::from("/tmp/project"),
|
||||
reasoning_effort: None,
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
initial_messages: None,
|
||||
network_proxy: None,
|
||||
rollout_path: Some(PathBuf::new()),
|
||||
}),
|
||||
};
|
||||
app.chat_widget
|
||||
.apply_external_edit("queued follow-up".to_string());
|
||||
app.chat_widget
|
||||
.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
let input_state = app
|
||||
.chat_widget
|
||||
.capture_thread_input_state()
|
||||
.expect("expected queued follow-up state");
|
||||
|
||||
let (chat_widget, _app_event_tx, _rx, mut new_op_rx) =
|
||||
make_chatwidget_manual_with_sender().await;
|
||||
app.chat_widget = chat_widget;
|
||||
app.chat_widget.handle_codex_event(session_configured);
|
||||
while new_op_rx.try_recv().is_ok() {}
|
||||
|
||||
app.restore_input_state_after_thread_switch(Some(input_state));
|
||||
|
||||
match next_user_turn_op(&mut new_op_rx) {
|
||||
Op::UserTurn { items, .. } => assert_eq!(
|
||||
items,
|
||||
vec![UserInput::Text {
|
||||
text: "queued follow-up".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
),
|
||||
other => panic!("expected queued follow-up submission, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn replay_only_thread_keeps_restored_queue_visible() {
|
||||
let (mut app, _app_event_rx, _op_rx) = make_test_app_with_channels().await;
|
||||
|
||||
Reference in New Issue
Block a user