diff --git a/codex-rs/tui2/src/bottom_pane/chat_composer.rs b/codex-rs/tui2/src/bottom_pane/chat_composer.rs index 3d5de81a9..f3472b293 100644 --- a/codex-rs/tui2/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui2/src/bottom_pane/chat_composer.rs @@ -1577,11 +1577,20 @@ impl ChatComposer { selection_active: bool, scroll_position: Option<(usize, usize)>, copy_selection_key: KeyBinding, - ) { + ) -> bool { + if self.transcript_scrolled == scrolled + && self.transcript_selection_active == selection_active + && self.transcript_scroll_position == scroll_position + && self.transcript_copy_selection_key == copy_selection_key + { + return false; + } + self.transcript_scrolled = scrolled; self.transcript_selection_active = selection_active; self.transcript_scroll_position = scroll_position; self.transcript_copy_selection_key = copy_selection_key; + true } fn sync_popups(&mut self) { diff --git a/codex-rs/tui2/src/bottom_pane/mod.rs b/codex-rs/tui2/src/bottom_pane/mod.rs index 961254def..466da8cda 100644 --- a/codex-rs/tui2/src/bottom_pane/mod.rs +++ b/codex-rs/tui2/src/bottom_pane/mod.rs @@ -389,13 +389,15 @@ impl BottomPane { scroll_position: Option<(usize, usize)>, copy_selection_key: crate::key_hint::KeyBinding, ) { - self.composer.set_transcript_ui_state( + let updated = self.composer.set_transcript_ui_state( scrolled, selection_active, scroll_position, copy_selection_key, ); - self.request_redraw(); + if updated { + self.request_redraw(); + } } /// Show a generic list selection view with the provided items. diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index 605877662..681deb9cd 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -968,6 +968,7 @@ impl ChatWidget { if let Some(cell) = cell { self.bottom_pane.hide_status_indicator(); self.add_boxed_history(cell); + self.request_redraw(); } if is_idle { self.app_event_tx.send(AppEvent::StopCommitAnimation); @@ -1009,6 +1010,7 @@ impl ChatWidget { #[inline] fn handle_streaming_delta(&mut self, delta: String) { // Before streaming agent content, flush any active exec cell group. + let mut needs_redraw = self.active_cell.is_some(); self.flush_active_cell(); if self.stream_controller.is_none() { @@ -1019,6 +1021,7 @@ impl ChatWidget { .map(super::status_indicator_widget::StatusIndicatorWidget::elapsed_seconds); self.add_to_history(history_cell::FinalMessageSeparator::new(elapsed_seconds)); self.needs_final_message_separator = false; + needs_redraw = true; } self.stream_controller = Some(StreamController::new( self.last_rendered_width.get().map(|w| w.saturating_sub(2)), @@ -1029,7 +1032,9 @@ impl ChatWidget { { self.app_event_tx.send(AppEvent::StartCommitAnimation); } - self.request_redraw(); + if needs_redraw { + self.request_redraw(); + } } pub(crate) fn handle_exec_end_now(&mut self, ev: ExecCommandEndEvent) {