show diff output in the pager (#2568)
this shows `/diff` output in an overlay like the transcript, instead of dumping it into history. https://github.com/user-attachments/assets/48e79b65-7f66-45dd-97b3-d5c627ac7349
This commit is contained in:
@@ -5,6 +5,7 @@ use crate::file_search::FileSearchManager;
|
||||
use crate::transcript_app::TranscriptApp;
|
||||
use crate::tui;
|
||||
use crate::tui::TuiEvent;
|
||||
use codex_ansi_escape::ansi_escape_line;
|
||||
use codex_core::ConversationManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
@@ -17,6 +18,7 @@ use crossterm::terminal::EnterAlternateScreen;
|
||||
use crossterm::terminal::LeaveAlternateScreen;
|
||||
use crossterm::terminal::supports_keyboard_enhancement;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::Line;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -126,8 +128,8 @@ impl App {
|
||||
tui.insert_history_lines(lines);
|
||||
}
|
||||
self.transcript_overlay = None;
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
tui.frame_requester().schedule_frame();
|
||||
} else {
|
||||
match event {
|
||||
TuiEvent::Key(key_event) => {
|
||||
@@ -238,7 +240,29 @@ impl App {
|
||||
}
|
||||
AppEvent::CodexOp(op) => self.chat_widget.submit_op(op),
|
||||
AppEvent::DiffResult(text) => {
|
||||
self.chat_widget.add_diff_output(text);
|
||||
// Clear the in-progress state in the bottom pane
|
||||
self.chat_widget.on_diff_complete();
|
||||
|
||||
// Enter alternate screen and set viewport to full size.
|
||||
let _ = execute!(tui.terminal.backend_mut(), EnterAlternateScreen);
|
||||
if let Ok(size) = tui.terminal.size() {
|
||||
self.transcript_saved_viewport = Some(tui.terminal.viewport_area);
|
||||
tui.terminal
|
||||
.set_viewport_area(Rect::new(0, 0, size.width, size.height));
|
||||
let _ = tui.terminal.clear();
|
||||
}
|
||||
|
||||
// Build pager lines directly without the "/diff" header
|
||||
let pager_lines: Vec<ratatui::text::Line<'static>> = if text.trim().is_empty() {
|
||||
vec!["No changes detected.".italic().into()]
|
||||
} else {
|
||||
text.lines().map(ansi_escape_line).collect()
|
||||
};
|
||||
self.transcript_overlay = Some(TranscriptApp::with_title(
|
||||
pager_lines,
|
||||
"D I F F".to_string(),
|
||||
));
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::StartFileSearch(query) => {
|
||||
if !query.is_empty() {
|
||||
|
||||
@@ -818,9 +818,8 @@ impl ChatWidget {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn add_diff_output(&mut self, diff_output: String) {
|
||||
pub(crate) fn on_diff_complete(&mut self) {
|
||||
self.bottom_pane.set_task_running(false);
|
||||
self.add_to_history(history_cell::new_diff_output(diff_output));
|
||||
self.mark_needs_redraw();
|
||||
}
|
||||
|
||||
|
||||
@@ -506,20 +506,6 @@ pub(crate) fn new_completed_mcp_tool_call(
|
||||
Box::new(PlainHistoryCell { lines })
|
||||
}
|
||||
|
||||
pub(crate) fn new_diff_output(message: String) -> PlainHistoryCell {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push(Line::from("/diff".magenta()));
|
||||
|
||||
if message.trim().is_empty() {
|
||||
lines.push(Line::from("No changes detected.".italic()));
|
||||
} else {
|
||||
lines.extend(message.lines().map(ansi_escape_line));
|
||||
}
|
||||
|
||||
lines.push(Line::from(""));
|
||||
PlainHistoryCell { lines }
|
||||
}
|
||||
|
||||
pub(crate) fn new_status_output(
|
||||
config: &Config,
|
||||
usage: &TokenUsage,
|
||||
|
||||
@@ -21,6 +21,7 @@ pub(crate) struct TranscriptApp {
|
||||
pub(crate) transcript_lines: Vec<Line<'static>>,
|
||||
pub(crate) scroll_offset: usize,
|
||||
pub(crate) is_done: bool,
|
||||
title: String,
|
||||
}
|
||||
|
||||
impl TranscriptApp {
|
||||
@@ -29,6 +30,16 @@ impl TranscriptApp {
|
||||
transcript_lines,
|
||||
scroll_offset: 0,
|
||||
is_done: false,
|
||||
title: "T R A N S C R I P T".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_title(transcript_lines: Vec<Line<'static>>, title: String) -> Self {
|
||||
Self {
|
||||
transcript_lines,
|
||||
scroll_offset: 0,
|
||||
is_done: false,
|
||||
title,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +125,11 @@ impl TranscriptApp {
|
||||
} => {
|
||||
self.scroll_offset = usize::MAX;
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
|
||||
fn scroll_area(&self, area: Rect) -> Rect {
|
||||
@@ -130,9 +144,8 @@ impl TranscriptApp {
|
||||
Span::from("/ ".repeat(area.width as usize / 2))
|
||||
.dim()
|
||||
.render_ref(area, buf);
|
||||
Span::from("/ T R A N S C R I P T")
|
||||
.dim()
|
||||
.render_ref(area, buf);
|
||||
let header = format!("/ {}", self.title);
|
||||
Span::from(header).dim().render_ref(area, buf);
|
||||
|
||||
// Main content area (excludes header and bottom status section)
|
||||
let content_area = self.scroll_area(area);
|
||||
|
||||
Reference in New Issue
Block a user