perf(tui): render streaming token secara inkremental + kurangi redraw sia-sia

- fix(view): token streaming kini benar-benar tampil — sebelumnya cache
  display_lines tidak pernah di-rebuild saat pesan terakhir berubah
  (msg_count == cached_count), jadi teks AI streaming tidak pernah muncul
  sampai pesan baru/resize
- perf(view): streaming kini hanya re-render pesan TERAKHIR (splice di
  batas cached_last_start) → O(konten baru) per token, bukan O(seluruh
  history); guard cached_last_len mencegah re-render pada frame spinner
  tanpa token baru
- perf(run): skip chrono::Utc::now() + drain toasts saat tidak ada toast
- perf(misc): drain_expired_toasts tidak lagi clone seluruh daftar toast
- perf(view): render_toasts fast-path saat toasts kosong
This commit is contained in:
asepharyana
2026-08-27 22:23:28 +07:00
parent 171597ca05
commit 271721694b
5 changed files with 58 additions and 12 deletions
+11
View File
@@ -103,6 +103,13 @@ pub struct AppStateRest {
pub last_render_width: u16,
/// Number of messages that were in the cache when it was last built.
pub cached_msg_count: usize,
/// Cache index where the last message's rendered lines begin. Used to
/// splice streaming updates (only re-render the trailing message).
pub cached_last_start: usize,
/// Content+reasoning byte length of the last message when it was last
/// rendered. Guards the streaming branch from re-rendering on ticks
/// where no new token arrived (spinner-only frames).
pub cached_last_len: usize,
/// Terminal width at the time of the last full cache build.
pub render_width_at_cache: u16,
/// Atomic flag set when the user aborts the current turn.
@@ -180,6 +187,8 @@ impl Default for AppStateRest {
token_count_dirty: true,
last_render_width: 0,
cached_msg_count: 0,
cached_last_start: 0,
cached_last_len: 0,
render_width_at_cache: 0,
}
}
@@ -230,6 +239,8 @@ impl AppStateRest {
token_count_dirty: true,
last_render_width: 0,
cached_msg_count: 0,
cached_last_start: 0,
cached_last_len: 0,
render_width_at_cache: 0,
}
}