Skip to content

fix(tui): replace scroll debounce with batch detection and absolute position indexing#88

Merged
yishuiliunian merged 1 commit intomainfrom
fix/tui-scroll-batch-detect-and-anchor
Apr 6, 2026
Merged

fix(tui): replace scroll debounce with batch detection and absolute position indexing#88
yishuiliunian merged 1 commit intomainfrom
fix/tui-scroll-batch-detect-and-anchor

Conversation

@yishuiliunian
Copy link
Copy Markdown
Contributor

Summary

  • Replace timing-based scroll debounce (6th failed attempt) with deterministic batch detection: ≥2 arrow events in one batch = mouse wheel → scroll; single arrow = keyboard → history
  • Fix "scroll jumps to bottom during output" by replacing tail() feedback loop with absolute position indexing via slice(), auto-compensating offset on content growth
  • Extract ContentScroll struct encapsulating scroll offset + compensation state + line cache with clean reset()/to_bottom()/scroll_up()/scroll_down() API

Changes

  • Deleted: scroll_debounce.rs (197 lines) — entire async timer state machine
  • New: content_scroll.rsContentScroll struct with render + compensation logic
  • Modified: tui_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.rs
  • Tests: e2e_scroll_test.rs (3 regression tests), scroll_compensation_test.rs (4 unit tests), updated 5 existing test files

Test plan

  • bazel test //crates/loopal-tui:loopal-tui_test — 377 tests pass
  • bazel build //... --config=clippy — zero warnings
  • bazel build //... --config=rustfmt — clean
  • CI passes

…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 yishuiliunian merged commit a360ed7 into main Apr 6, 2026
3 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant