in codex-rs/cloud-tasks/src/ui.rs [312:467]
fn draw_diff_overlay(frame: &mut Frame, area: Rect, app: &mut App) {
let inner = overlay_outer(area);
if app.diff_overlay.is_none() {
return;
}
let ov_can_apply = app
.diff_overlay
.as_ref()
.map(super::app::DiffOverlay::current_can_apply)
.unwrap_or(false);
let is_error = app
.diff_overlay
.as_ref()
.and_then(|o| o.sd.wrapped_lines().first().cloned())
.map(|s| s.trim_start().starts_with("Task failed:"))
.unwrap_or(false)
&& !ov_can_apply;
let title = app
.diff_overlay
.as_ref()
.map(|o| o.title.clone())
.unwrap_or_default();
// Title block
let title_ref = title.as_str();
let mut title_spans: Vec<ratatui::text::Span> = if is_error {
vec![
"Details ".magenta(),
"[FAILED]".red().bold(),
" ".into(),
title_ref.magenta(),
]
} else if ov_can_apply {
vec!["Diff: ".magenta(), title_ref.magenta()]
} else {
vec!["Details: ".magenta(), title_ref.magenta()]
};
if let Some(p) = app
.diff_overlay
.as_ref()
.and_then(|o| o.sd.percent_scrolled())
{
title_spans.push(" • ".dim());
title_spans.push(format!("{p}%").dim());
}
frame.render_widget(Clear, inner);
frame.render_widget(
overlay_block().title(Line::from(title_spans)).clone(),
inner,
);
// Content area and optional status bar
let content_full = overlay_content(inner);
let mut content_area = content_full;
if let Some(ov) = app.diff_overlay.as_mut() {
let has_text = ov.current_attempt().is_some_and(AttemptView::has_text);
let has_diff = ov.current_attempt().is_some_and(AttemptView::has_diff) || ov.base_can_apply;
if has_diff || has_text {
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(1), Constraint::Min(1)])
.split(content_full);
// Status bar label
let mut spans: Vec<ratatui::text::Span> = Vec::new();
if has_diff && has_text {
let prompt_lbl = if matches!(ov.current_view, crate::app::DetailView::Prompt) {
"[Prompt]".magenta().bold()
} else {
"Prompt".dim()
};
let diff_lbl = if matches!(ov.current_view, crate::app::DetailView::Diff) {
"[Diff]".magenta().bold()
} else {
"Diff".dim()
};
spans.extend(vec![
prompt_lbl,
" ".into(),
diff_lbl,
" ".into(),
"(← → to switch view)".dim(),
]);
} else if has_text {
spans.push("Conversation".magenta().bold());
} else {
spans.push("Diff".magenta().bold());
}
if let Some(total) = ov.expected_attempts().or({
if ov.attempts.is_empty() {
None
} else {
Some(ov.attempts.len())
}
}) && total > 1
{
spans.extend(vec![
" ".into(),
format!("Attempt {}/{}", ov.selected_attempt + 1, total)
.bold()
.dim(),
" ".into(),
"(Tab/Shift-Tab or [ ] to cycle attempts)".dim(),
]);
}
frame.render_widget(Paragraph::new(Line::from(spans)), rows[0]);
ov.sd.set_width(rows[1].width);
ov.sd.set_viewport(rows[1].height);
content_area = rows[1];
} else {
ov.sd.set_width(content_full.width);
ov.sd.set_viewport(content_full.height);
content_area = content_full;
}
}
// Styled content render
// Choose styling by the active view, not just presence of a diff
let is_diff_view = app
.diff_overlay
.as_ref()
.map(|o| matches!(o.current_view, crate::app::DetailView::Diff))
.unwrap_or(false);
let styled_lines: Vec<Line<'static>> = if is_diff_view {
let raw = app.diff_overlay.as_ref().map(|o| o.sd.wrapped_lines());
raw.unwrap_or(&[])
.iter()
.map(|l| style_diff_line(l))
.collect()
} else {
app.diff_overlay
.as_ref()
.map(|o| style_conversation_lines(&o.sd, o.current_attempt()))
.unwrap_or_default()
};
let raw_empty = app
.diff_overlay
.as_ref()
.map(|o| o.sd.wrapped_lines().is_empty())
.unwrap_or(true);
if app.details_inflight && raw_empty {
draw_centered_spinner(
frame,
content_area,
&mut app.spinner_start,
"Loading details…",
);
} else {
let scroll = app
.diff_overlay
.as_ref()
.map(|o| o.sd.state.scroll)
.unwrap_or(0);
let content = Paragraph::new(Text::from(styled_lines)).scroll((scroll, 0));
frame.render_widget(content, content_area);
}
}