feat(tui): optimize performance by caching display lines and token counts, and improve action handling

This commit is contained in:
asepharyana
2026-07-20 13:52:20 +07:00
parent 148ba4e07b
commit 87abe8c335
8 changed files with 180 additions and 60 deletions
+30 -6
View File
@@ -76,6 +76,13 @@ fn run_loop(
}
/// The core single-process render/input loop.
///
/// Performance optimizations applied here:
/// - Skip `terminal.draw()` when `state.dirty == false` (nothing changed)
/// - Adaptive poll timeout: 50ms when a turn is in-flight (spinner needs
/// smooth updates), 200ms when idle (no reason to busy-loop)
/// - Toast expiry is drained once here instead of in two places
/// - Chat display lines are cached and rebuilt only when content changes
fn run_loop_inner(
state: &mut AppStateRest,
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
@@ -84,15 +91,32 @@ fn run_loop_inner(
if state.quit {
break;
}
// Drain expired toasts exactly once per loop iteration (was being
// done here AND in Action::Tick before this fix).
let now_ms = chrono::Utc::now().timestamp_millis();
state.misc.drain_expired_toasts(now_ms);
terminal.draw(|f| {
view::draw(f, state);
state.dirty = false;
})?;
// Poll terminal with 50 ms timeout
if crossterm::event::poll(Duration::from_millis(50))? {
// Skip render when nothing has changed — avoids expensive markdown
// re-parse and layout recalculation every cycle while idle.
if state.dirty {
// Pre-warm display caches before entering the terminal.draw closure.
// This lets view::draw work with &AppStateRest (no mutation inside draw).
view::pre_render(state);
terminal.draw(|f| {
view::draw(f, state);
state.dirty = false;
})?;
}
// Adaptive poll: fast when animating, slow when idle.
let poll_timeout = if state.turn_in_flight() {
Duration::from_millis(50) // smooth spinner @ ~20fps
} else {
Duration::from_millis(200) // idle: 5fps, saves CPU
};
if crossterm::event::poll(poll_timeout)? {
match crossterm::event::read()? {
Event::Key(key) => {
if key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat {