From 1b47c7d29b3c51a45100bd570ede5645673b4206 Mon Sep 17 00:00:00 2001 From: Changseo Jang Date: Wed, 18 Mar 2026 23:03:13 +0900 Subject: [PATCH] Improve result pane scroll responsiveness --- journal/2026-03-18/fix_scroll_lag.md | 5 +++++ src/ui/manager.rs | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 journal/2026-03-18/fix_scroll_lag.md diff --git a/journal/2026-03-18/fix_scroll_lag.md b/journal/2026-03-18/fix_scroll_lag.md new file mode 100644 index 0000000..69e9961 --- /dev/null +++ b/journal/2026-03-18/fix_scroll_lag.md @@ -0,0 +1,5 @@ +# Fix result scrolling lag + +- Investigated the result-pane `j`/`k` navigation lag in the TUI. +- Root cause: the UI thread only handled one input event per 100ms render tick and used a bounded UI action channel, which could block on repeated key presses. +- Fix approach: switch the UI action channel to unbounded, increase the UI refresh cadence, and drain all pending crossterm events each frame so held/repeated keys are processed promptly. diff --git a/src/ui/manager.rs b/src/ui/manager.rs index 6520dbe..4b4129d 100644 --- a/src/ui/manager.rs +++ b/src/ui/manager.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use crossbeam_channel::{bounded, select, tick}; +use crossbeam_channel::{select, tick, unbounded}; use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, poll}; use log::{debug, error}; use ratatui::{ @@ -24,7 +24,7 @@ pub struct Manager { impl Manager { pub fn new() -> (Manager, crossbeam_channel::Receiver) { - let (tx, rx) = bounded::(1); + let (tx, rx) = unbounded::(); (Manager { action_tx: tx }, rx) } @@ -34,7 +34,7 @@ impl Manager { pub fn run(mut self, state: Arc>) -> thread::JoinHandle<()> { thread::spawn(move || { let mut terminal = setup_terminal(); - let ticker = tick(Duration::from_millis(100)); + let ticker = tick(Duration::from_millis(16)); loop { select! { @@ -46,8 +46,8 @@ impl Manager { } terminal.draw(|frame| self.render(frame, &state)).unwrap(); - if poll(Duration::from_secs(0)).unwrap() { - self.handle_crossterm_events().unwrap() + while poll(Duration::from_secs(0)).unwrap() { + self.handle_crossterm_events().unwrap(); } } }