Codex Handbook
tui/src/app/agent_message_consolidation.rs 92 lines
//! Transcript consolidation for finalized streaming agent messages.//!//! During streaming, the chat widget emits transient `AgentMessageCell`s so it//! can animate stable lines into scrollback while keeping the active mutable//! tail in the bottom pane. Once the answer finishes, the app replaces that//! trailing run with a single source-backed `AgentMarkdownCell`. This makes the//! transcript the canonical owner of the raw markdown source used for future//! resize re-renders.use std::path::PathBuf;use std::sync::Arc;use color_eyre::eyre::Result;use super::App;use super::resize_reflow::trailing_run_start;use crate::app_event::ConsolidationScrollbackReflow;use crate::history_cell;use crate::history_cell::HistoryCell;use crate::pager_overlay::Overlay;use crate::tui;impl App {    pub(super) fn handle_consolidate_agent_message(        &mut self,        tui: &mut tui::Tui,        source: String,        cwd: PathBuf,        scrollback_reflow: ConsolidationScrollbackReflow,        deferred_history_cell: Option<Box<dyn HistoryCell>>,    ) -> Result<()> {        // Some finalize paths must preserve a last provisional stream cell long        // enough for queue ordering, then fold it into the canonical        // source-backed cell during consolidation.        if let Some(cell) = deferred_history_cell {            let cell: Arc<dyn HistoryCell> = cell.into();            if let Some(Overlay::Transcript(t)) = &mut self.overlay {                t.insert_cell(cell.clone());            }            self.transcript_cells.push(cell);        }        // Walk backward to find the contiguous run of streaming AgentMessageCells that        // belong to the just-finalized stream.        let end = self.transcript_cells.len();        tracing::debug!(            "ConsolidateAgentMessage: transcript_cells.len()={end}, source_len={}",            source.len()        );        let start = trailing_run_start::<history_cell::AgentMessageCell>(&self.transcript_cells);        if start < end {            tracing::debug!(                "ConsolidateAgentMessage: replacing cells [{start}..{end}] with AgentMarkdownCell"            );            let consolidated: Arc<dyn HistoryCell> =                Arc::new(history_cell::AgentMarkdownCell::new(source, &cwd));            self.transcript_cells                .splice(start..end, std::iter::once(consolidated.clone()));            if let Some(Overlay::Transcript(t)) = &mut self.overlay {                t.consolidate_cells(start..end, consolidated.clone());                tui.frame_requester().schedule_frame();            }            self.finish_agent_message_consolidation(tui, scrollback_reflow)?;        } else {            tracing::debug!(                "ConsolidateAgentMessage: no cells to consolidate(start={start}, end={end})",            );            self.maybe_finish_stream_reflow(tui)?;        }        Ok(())    }    fn finish_agent_message_consolidation(        &mut self,        tui: &mut tui::Tui,        scrollback_reflow: ConsolidationScrollbackReflow,    ) -> Result<()> {        match scrollback_reflow {            ConsolidationScrollbackReflow::IfResizeReflowRan => {                self.maybe_finish_stream_reflow(tui)?;            }            ConsolidationScrollbackReflow::Required => {                self.finish_required_stream_reflow(tui)?;            }        }        Ok(())    }}