Files
codex/codex-rs/tui/src/app/history_ui.rs
T
Eric Traut 2af4f15479 Refactor TUI app module into submodules (#18753)
## Why

The TUI app module had grown past the 512K source-file cap enforced by
CI/CD. This keeps the app entry point below that limit while preserving
the existing runtime behavior and test surface.

## What changed

- Kept the top-level `App` state and run-loop wiring in
`tui/src/app.rs`.
- Split app responsibilities into focused private submodules under
`tui/src/app/`, covering event dispatch, thread routing, session
lifecycle, config persistence, background requests, startup prompts,
input, history UI, platform actions, and thread event buffering.
- Moved the existing app-level tests into `tui/src/app/tests.rs` and
reused the existing snapshot location rather than adding new tests or
snapshots.
- Added module header comments for `app.rs` and the new submodules.

## Follow-up

A future cleanup can move narrow unit tests from `tui/src/app/tests.rs`
into the specific app submodules they exercise. This PR keeps the
existing app-level tests together so the refactor stays focused on the
source-file split.

## Verification

- `cargo test -p codex-tui --lib
app::tests::agent_picker_item_name_snapshot`
- `cargo test -p codex-tui --lib app::tests::clear_ui`
- `cargo test -p codex-tui --lib
app::tests::ctrl_l_clear_ui_after_long_transcript_reuses_clear_header_snapshot`
- `just fix -p codex-tui`

Full `cargo test -p codex-tui` still fails on model-catalog drift
unrelated to this refactor, including stale
`gpt-5.3-codex`/`gpt-5.1-codex` snapshot and migration expectations now
resolving to `gpt-5.4`.
2026-04-20 16:10:35 -07:00

94 lines
3.3 KiB
Rust

//! Terminal history and clear-screen UI helpers for the TUI app.
//!
//! This module owns rendering the fresh session header, clearing inline or alternate-screen UI
//! state, and resetting transcript-related app state after `/clear` or Ctrl-L.
use super::*;
impl App {
pub(super) fn open_url_in_browser(&mut self, url: String) {
if let Err(err) = webbrowser::open(&url) {
self.chat_widget
.add_error_message(format!("Failed to open browser for {url}: {err}"));
return;
}
self.chat_widget
.add_info_message(format!("Opened {url} in your browser."), /*hint*/ None);
}
pub(super) fn clear_ui_header_lines_with_version(
&self,
width: u16,
version: &'static str,
) -> Vec<Line<'static>> {
history_cell::SessionHeaderHistoryCell::new(
self.chat_widget.current_model().to_string(),
self.chat_widget.current_reasoning_effort(),
self.chat_widget.should_show_fast_status(
self.chat_widget.current_model(),
self.chat_widget.current_service_tier(),
),
self.config.cwd.to_path_buf(),
version,
)
.with_yolo_mode(history_cell::is_yolo_mode(&self.config))
.display_lines(width)
}
pub(super) fn clear_ui_header_lines(&self, width: u16) -> Vec<Line<'static>> {
self.clear_ui_header_lines_with_version(width, CODEX_CLI_VERSION)
}
pub(super) fn queue_clear_ui_header(&mut self, tui: &mut tui::Tui) {
let width = tui.terminal.last_known_screen_size.width;
let header_lines = self.clear_ui_header_lines(width);
if !header_lines.is_empty() {
tui.insert_history_lines(header_lines);
self.has_emitted_history_lines = true;
}
}
pub(super) fn clear_terminal_ui(
&mut self,
tui: &mut tui::Tui,
redraw_header: bool,
) -> Result<()> {
let is_alt_screen_active = tui.is_alt_screen_active();
// Drop queued history insertions so stale transcript lines cannot be flushed after /clear.
tui.clear_pending_history_lines();
if is_alt_screen_active {
tui.terminal.clear_visible_screen()?;
} else {
// Some terminals (Terminal.app, Warp) do not reliably drop scrollback when purge and
// clear are emitted as separate backend commands. Prefer a single ANSI sequence.
tui.terminal.clear_scrollback_and_visible_screen_ansi()?;
}
let mut area = tui.terminal.viewport_area;
if area.y > 0 {
// After a full clear, anchor the inline viewport at the top and redraw a fresh header
// box. `insert_history_lines()` will shift the viewport down by the rendered height.
area.y = 0;
tui.terminal.set_viewport_area(area);
}
self.has_emitted_history_lines = false;
if redraw_header {
self.queue_clear_ui_header(tui);
}
Ok(())
}
pub(super) fn reset_app_ui_state_after_clear(&mut self) {
self.overlay = None;
self.transcript_cells.clear();
self.deferred_history_lines.clear();
self.has_emitted_history_lines = false;
self.backtrack = BacktrackState::default();
self.backtrack_render_pending = false;
}
}