mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
tui2: stop baking streaming wraps; reflow agent markdown (#8761)
Background Streaming assistant prose in tui2 was being rendered with viewport-width wrapping during streaming, then stored in history cells as already split `Line`s. Those width-derived breaks became indistinguishable from hard newlines, so the transcript could not "un-split" on resize. This also degraded copy/paste, since soft wraps looked like hard breaks. What changed - Introduce width-agnostic `MarkdownLogicalLine` output in `tui2/src/markdown_render.rs`, preserving markdown wrap semantics: initial/subsequent indents, per-line style, and a preformatted flag. - Update the streaming collector (`tui2/src/markdown_stream.rs`) to emit logical lines (newline-gated) and remove any captured viewport width. - Update streaming orchestration (`tui2/src/streaming/*`) to queue and emit logical lines, producing `AgentMessageCell::new_logical(...)`. - Make `AgentMessageCell` store logical lines and wrap at render time in `HistoryCell::transcript_lines_with_joiners(width)`, emitting joiners so copy/paste can join soft-wrap continuations correctly. Overlay deferral When an overlay is active, defer *cells* (not rendered `Vec<Line>`) and render them at overlay close time. This avoids baking width-derived wraps based on a stale width. Tests + docs - Add resize/reflow regression tests + snapshots for streamed agent output. - Expand module/API docs for the new logical-line streaming pipeline and clarify joiner semantics. - Align scrollback-related docs/comments with current tui2 behavior (main draw loop does not flush queued "history lines" to the terminal). More details See `codex-rs/tui2/docs/streaming_wrapping_design.md` for the full problem statement and solution approach, and `codex-rs/tui2/docs/tui_viewport_and_history.md` for viewport vs printed output behavior.
This commit is contained in:
1 parent
771f1ca6ab
commit
c92dbea7c1
13 files changed
+868
-325
No files matched your search
@@ -123,12 +123,42 @@ impl App {
|
||||
}
|
||||
|
||||
/// Close transcript overlay and restore normal UI.
|
||||
///
|
||||
/// Any history emitted while the overlay was open is flushed to the normal-buffer queue here.
|
||||
///
|
||||
/// Importantly, we defer *cells* (not rendered lines) so we can render them against the current
|
||||
/// width on close and avoid baking width-derived wrapping based on an earlier viewport size.
|
||||
/// (This matters if/when scrollback printing is enabled; `Tui::insert_history_lines` currently
|
||||
/// queues lines without printing them during the main draw loop.)
|
||||
pub(crate) fn close_transcript_overlay(&mut self, tui: &mut tui::Tui) {
|
||||
let _ = tui.leave_alt_screen();
|
||||
let was_backtrack = self.backtrack.overlay_preview_active;
|
||||
if !self.deferred_history_lines.is_empty() {
|
||||
let lines = std::mem::take(&mut self.deferred_history_lines);
|
||||
tui.insert_history_lines(lines);
|
||||
if !self.deferred_history_cells.is_empty() {
|
||||
let cells = std::mem::take(&mut self.deferred_history_cells);
|
||||
let width = tui.terminal.last_known_screen_size.width;
|
||||
let mut lines: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
for cell in cells {
|
||||
let mut display = cell.display_lines(width);
|
||||
if display.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only insert a separating blank line for new cells that are not part of an
|
||||
// ongoing stream. Streaming continuations should not accrue extra blank lines
|
||||
// between chunks.
|
||||
if !cell.is_stream_continuation() {
|
||||
if self.has_emitted_history_lines {
|
||||
display.insert(0, ratatui::text::Line::from(""));
|
||||
} else {
|
||||
self.has_emitted_history_lines = true;
|
||||
}
|
||||
}
|
||||
|
||||
lines.extend(display);
|
||||
}
|
||||
if !lines.is_empty() {
|
||||
tui.insert_history_lines(lines);
|
||||
}
|
||||
}
|
||||
self.overlay = None;
|
||||
self.backtrack.overlay_preview_active = false;
|
||||
|
||||
Reference in new issue
Block a user