Paginate transcript history in the TUI (#36948)

## Why

Paginated threads should not require the TUI to load and render their entire
history when a session is resumed.

## What changed

- Hydrate a bounded initial history page for paginated resumes and forks,
  respecting the configured terminal reflow row budget.
- Load older transcript items in the background as users navigate upward, while
  preserving the viewport, live output, and backtrack selection.
- Reconcile review-mode prompts across page boundaries and keep restored token
  usage hidden until it can be attributed to a persisted turn.

## Testing

- Added coverage for paginated resume and fork hydration, older-page merging,
  transcript prepending, backtrack behavior, review prompt filtering, and token
  usage restoration.

GitOrigin-RevId: 42160b943a9f6a1ba0cfe4b195c24ddf410b747d
This commit is contained in:
Felipe Coury authored and copyberry committed 2026-08-04 18:59:32 +00:00
1 parent 989a0b053e
commit 8bfa49e350
26 files changed
+1360 -90

No files matched your search

+13 -4
View File
@@ -620,6 +620,7 @@ impl ChatComposer {
flash: None,
context_window_percent: None,
context_window_used_tokens: None,
context_window_pending: false,
collaboration_mode_indicator: None,
goal_status_indicator: None,
ide_context_active: false,
@@ -1353,10 +1354,14 @@ impl ChatComposer {
}
fn right_footer_line_with_context(&self) -> Line<'static> {
let mut line = context_window_line(
self.footer.context_window_percent,
self.footer.context_window_used_tokens,
);
let mut line = if self.footer.context_window_pending {
Line::default()
} else {
context_window_line(
self.footer.context_window_percent,
self.footer.context_window_used_tokens,
)
};
if let Some(vim_mode) = self.vim_mode_indicator_span() {
line.spans.push(" | ".dim());
line.spans.push(vim_mode);
@@ -4218,6 +4223,10 @@ impl ChatComposer {
self.footer.context_window_used_tokens = used_tokens;
}
pub(crate) fn set_context_window_pending(&mut self, pending: bool) {
self.footer.context_window_pending = pending;
}
pub(crate) fn set_esc_backtrack_hint(&mut self, show: bool) {
self.footer.esc_backtrack_hint = show;
if show {
@@ -23,6 +23,7 @@ pub(super) struct FooterState {
pub(super) flash: Option<FooterFlash>,
pub(super) context_window_percent: Option<i64>,
pub(super) context_window_used_tokens: Option<i64>,
pub(super) context_window_pending: bool,
pub(super) collaboration_mode_indicator: Option<CollaborationModeIndicator>,
pub(super) goal_status_indicator: Option<GoalStatusIndicator>,
pub(super) ide_context_active: bool,
+5
View File
@@ -1123,6 +1123,11 @@ impl BottomPane {
self.request_redraw();
}
pub(crate) fn set_context_window_pending(&mut self, pending: bool) {
self.composer.set_context_window_pending(pending);
self.request_redraw();
}
/// Show a generic list selection view with the provided items.
pub(crate) fn show_selection_view(
&mut self,