fix(tui): replace scroll debounce with batch detection and absolute position indexing#88
Merged
yishuiliunian merged 1 commit intomainfrom Apr 6, 2026
Merged
Conversation
…nd absolute position indexing (#85) The 30ms async timer debounce had race conditions between ArrowDebounceTimeout and event batch processing, causing scroll/history misfires. The tail-window rendering model drifted during output because window_size depended on scroll_offset, creating a feedback loop. Changes: - Delete scroll_debounce.rs (197 lines) and all timer machinery - Batch detection in tui_loop: ≥2 arrows in one batch = mouse wheel → scroll; 1 arrow = keyboard → history. No timers, no races. - ContentScroll struct encapsulates offset + prev_total + LineCache with reset()/to_bottom()/scroll_up()/scroll_down() methods - Absolute position indexing via slice() replaces tail() feedback loop; offset auto-compensated on content growth while pinned - E2E regression tests for both bugs + unit tests for compensation logic
yishuiliunian
added a commit
that referenced
this pull request
Apr 6, 2026
…terministic scroll detection (#88) The previous approach used xterm alternate scroll mode (\x1b[?1007h) to convert mouse wheel events into Up/Down arrow key sequences, then relied on batch-counting heuristics (≥2 arrows per batch = scroll) to distinguish scroll from keyboard input. This was fundamentally unreliable across different terminal emulators and system load conditions. Switch to crossterm's EnableMouseCapture which provides actual MouseEvent::ScrollUp/ScrollDown events, making the distinction deterministic. Up/Down arrow keys now always route to history navigation.
yishuiliunian
added a commit
that referenced
this pull request
Apr 6, 2026
* fix(tui): replace alternate scroll heuristic with MouseCapture for deterministic scroll detection (#88) The previous approach used xterm alternate scroll mode (\x1b[?1007h) to convert mouse wheel events into Up/Down arrow key sequences, then relied on batch-counting heuristics (≥2 arrows per batch = scroll) to distinguish scroll from keyboard input. This was fundamentally unreliable across different terminal emulators and system load conditions. Switch to crossterm's EnableMouseCapture which provides actual MouseEvent::ScrollUp/ScrollDown events, making the distinction deterministic. Up/Down arrow keys now always route to history navigation. * fix: rustfmt module ordering in suite.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tail()feedback loop with absolute position indexing viaslice(), auto-compensating offset on content growthContentScrollstruct encapsulating scroll offset + compensation state + line cache with cleanreset()/to_bottom()/scroll_up()/scroll_down()APIChanges
scroll_debounce.rs(197 lines) — entire async timer state machinecontent_scroll.rs—ContentScrollstruct with render + compensation logictui_loop.rs(batch pre-scan),input/mod.rs(Up/Down → direct history),app/mod.rs(3 fields → 1 struct),render.rs,key_dispatch.rs,key_dispatch_ops.rs,navigation.rs,event.rs,actions.rs,line_cache.rs,progress/mod.rse2e_scroll_test.rs(3 regression tests),scroll_compensation_test.rs(4 unit tests), updated 5 existing test filesTest plan
bazel test //crates/loopal-tui:loopal-tui_test— 377 tests passbazel build //... --config=clippy— zero warningsbazel build //... --config=rustfmt— clean