mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
apply patch spinner
This commit is contained in:
@@ -59,6 +59,8 @@ pub struct App {
|
||||
pub new_task: Option<crate::new_task::NewTaskPage>,
|
||||
// Apply preflight spinner state
|
||||
pub apply_preflight_inflight: bool,
|
||||
// Apply action spinner state
|
||||
pub apply_inflight: bool,
|
||||
// Background enrichment coordination
|
||||
pub list_generation: u64,
|
||||
pub in_flight: std::collections::HashSet<String>,
|
||||
@@ -84,6 +86,7 @@ impl App {
|
||||
env_error: None,
|
||||
new_task: None,
|
||||
apply_preflight_inflight: false,
|
||||
apply_inflight: false,
|
||||
list_generation: 0,
|
||||
in_flight: std::collections::HashSet::new(),
|
||||
}
|
||||
@@ -161,6 +164,11 @@ pub enum AppEvent {
|
||||
skipped: Vec<String>,
|
||||
conflicts: Vec<String>,
|
||||
},
|
||||
/// Background completion of apply action (actual patch application)
|
||||
ApplyFinished {
|
||||
id: TaskId,
|
||||
result: std::result::Result<codex_cloud_tasks_client::ApplyOutcome, String>,
|
||||
},
|
||||
}
|
||||
|
||||
// Convenience aliases; currently unused.
|
||||
|
||||
@@ -373,7 +373,12 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
}
|
||||
}
|
||||
// Advance throbber only while loading.
|
||||
if app.refresh_inflight || app.details_inflight || app.env_loading || app.apply_preflight_inflight {
|
||||
if app.refresh_inflight
|
||||
|| app.details_inflight
|
||||
|| app.env_loading
|
||||
|| app.apply_preflight_inflight
|
||||
|| app.apply_inflight
|
||||
{
|
||||
app.throbber.calc_next();
|
||||
needs_redraw = true;
|
||||
let _ = frame_tx.send(Instant::now() + Duration::from_millis(100));
|
||||
@@ -575,6 +580,37 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.details_inflight = false;
|
||||
needs_redraw = true;
|
||||
}
|
||||
app::AppEvent::ApplyFinished { id, result } => {
|
||||
// Only update if the modal still corresponds to this id.
|
||||
if let Some(m) = &app.apply_modal {
|
||||
if m.task_id != id { continue; }
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
app.apply_inflight = false;
|
||||
match result {
|
||||
Ok(outcome) => {
|
||||
app.status = outcome.message.clone();
|
||||
if matches!(outcome.status, codex_cloud_tasks_client::ApplyStatus::Success) {
|
||||
app.apply_modal = None;
|
||||
app.diff_overlay = None;
|
||||
// Refresh tasks after successful apply
|
||||
let backend2 = backend.clone();
|
||||
let tx2 = tx.clone();
|
||||
let env_sel = app.env_filter.clone();
|
||||
tokio::spawn(async move {
|
||||
let res = app::load_tasks(&*backend2, env_sel.as_deref()).await;
|
||||
let _ = tx2.send(app::AppEvent::TasksLoaded { env: env_sel, result: res });
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
append_error_log(format!("apply_task failed for {}: {e}", id.0));
|
||||
app.status = format!("Apply failed: {e}");
|
||||
}
|
||||
}
|
||||
needs_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Render immediately after processing app events.
|
||||
@@ -724,21 +760,22 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
// Simple apply confirmation modal: y apply, p preflight, n/Esc cancel
|
||||
match key.code {
|
||||
KeyCode::Char('y') => {
|
||||
if let Some(m) = app.apply_modal.take() {
|
||||
if let Some(m) = app.apply_modal.as_ref() {
|
||||
// Keep modal open and animate a spinner while applying
|
||||
app.apply_inflight = true;
|
||||
app.status = format!("Applying '{}'...", m.title);
|
||||
match codex_cloud_tasks_client::CloudBackend::apply_task(&*backend, m.task_id.clone()).await {
|
||||
Ok(outcome) => {
|
||||
app.status = outcome.message.clone();
|
||||
if matches!(outcome.status, codex_cloud_tasks_client::ApplyStatus::Success) {
|
||||
app.diff_overlay = None;
|
||||
if let Ok(tasks) = app::load_tasks(&*backend, app.env_filter.as_deref()).await { app.tasks = tasks; }
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
append_error_log(format!("apply_task failed for {}: {e}", m.task_id.0));
|
||||
app.status = format!("Apply failed: {e}");
|
||||
}
|
||||
}
|
||||
needs_redraw = true;
|
||||
let backend2 = backend.clone();
|
||||
let tx2 = tx.clone();
|
||||
let id2 = m.task_id.clone();
|
||||
tokio::spawn(async move {
|
||||
let res = codex_cloud_tasks_client::CloudBackend::apply_task(&*backend2, id2.clone()).await;
|
||||
let evt = match res {
|
||||
Ok(outcome) => app::AppEvent::ApplyFinished { id: id2, result: Ok(outcome) },
|
||||
Err(e) => app::AppEvent::ApplyFinished { id: id2, result: Err(format!("{e}")) },
|
||||
};
|
||||
let _ = tx2.send(evt);
|
||||
});
|
||||
}
|
||||
}
|
||||
KeyCode::Char('p') => {
|
||||
|
||||
@@ -257,7 +257,12 @@ fn draw_footer(frame: &mut Frame, area: Rect, app: &mut App) {
|
||||
// Draw help text; avoid clearing the whole footer area every frame.
|
||||
frame.render_widget(para, top[0]);
|
||||
// Right side: spinner or clear the spinner area if idle to prevent stale glyphs.
|
||||
if app.refresh_inflight || app.details_inflight || app.env_loading {
|
||||
if app.refresh_inflight
|
||||
|| app.details_inflight
|
||||
|| app.env_loading
|
||||
|| app.apply_preflight_inflight
|
||||
|| app.apply_inflight
|
||||
{
|
||||
draw_inline_spinner(frame, top[1], &mut app.throbber, "Loading…");
|
||||
} else {
|
||||
frame.render_widget(Clear, top[1]);
|
||||
@@ -419,9 +424,13 @@ pub fn draw_apply_modal(frame: &mut Frame, area: Rect, app: &mut App) {
|
||||
.split(content);
|
||||
|
||||
frame.render_widget(header, rows[0]);
|
||||
// Body: spinner while preflight runs; otherwise show result message and path lists
|
||||
if app.apply_preflight_inflight || m.result_message.is_none() {
|
||||
// Body: spinner while preflight/apply runs; otherwise show result message and path lists
|
||||
if app.apply_preflight_inflight {
|
||||
draw_centered_spinner(frame, rows[1], &mut app.throbber, "Checking…");
|
||||
} else if app.apply_inflight {
|
||||
draw_centered_spinner(frame, rows[1], &mut app.throbber, "Applying…");
|
||||
} else if m.result_message.is_none() {
|
||||
draw_centered_spinner(frame, rows[1], &mut app.throbber, "Loading…");
|
||||
} else if let Some(msg) = &m.result_message {
|
||||
let mut body_lines: Vec<Line> = Vec::new();
|
||||
let first = match m.result_level {
|
||||
|
||||
Reference in New Issue
Block a user