fix(tui): decouple mouse scroll from input history via timing-based debounce#85
Merged
yishuiliunian merged 2 commits intomainfrom Apr 5, 2026
Merged
fix(tui): decouple mouse scroll from input history via timing-based debounce#85yishuiliunian merged 2 commits intomainfrom
yishuiliunian merged 2 commits intomainfrom
Conversation
…ebounce xterm alternate scroll (\x1b[?1007h) converts mouse wheel events into Up/Down arrow keys, creating an unresolvable conflict between content scrolling and history navigation. Previous attempts (#6, #13, #30, #42, #79) toggled priority chains without a lasting fix because the two event sources share the same key codes. Resolve by introducing a 30 ms debounce state machine that exploits the timing difference between mouse wheel bursts (<5 ms between events) and keyboard presses (>20 ms): Idle → Pending (defer 30 ms) → Scrolling (burst confirmed) → History (timer expired / other key) - Mouse wheel: rapid-fire Up/Down detected as burst → scroll content - Keyboard Up/Down: single isolated event → history navigation - Multiline cursor: bypasses debounce entirely (immediate response) - Ctrl+P/N: dedicated history bindings, always immediate - Global/modal/autocomplete keys: discard pending debounce (prevents stale timer from polluting state after Ctrl+C, paste, etc.) Stale timer and dropped-tick resilience: - Pending state checks elapsed time on second arrow to handle delayed timers; stale pending is flushed as history before starting new - Scrolling state has lazy 150 ms expiry check in addition to tick- based cleanup, so dropped ticks don't leave stale scroll mode
yishuiliunian
added a commit
that referenced
this pull request
Apr 6, 2026
…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
…nd absolute position indexing (#85) (#88) 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
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
Changes
crates/loopal-tui/src/input/scroll_debounce.rs— debounce state machine (Idle → Pending → Scrolling)crates/loopal-tui/tests/suite/scroll_burst_test.rs— 12 burst/stale/mixed-direction testsinput/mod.rs— integrate debounce intohandle_input_mode_key, adddiscard_pendingon early-return pathsevent.rs/actions.rs—ArrowDebounceTimeoutevent +StartArrowDebounceactionkey_dispatch.rs— spawn 30 ms timer onStartArrowDebouncetui_loop.rs— handle timeout event + tick-based Scrolling expiryapp/mod.rs—arrow_debouncefield on AppTest plan