Skip to content

fix(terminal): coalesce scroll events#184

Merged
omarluq merged 3 commits into
mainfrom
fix/tui-scroll-coalescing
Jul 2, 2026
Merged

fix(terminal): coalesce scroll events#184
omarluq merged 3 commits into
mainfrom
fix/tui-scroll-coalescing

Conversation

@omarluq

@omarluq omarluq commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • coalesce queued terminal scroll events into a single net scroll delta
  • avoid drawing once per queued scroll event
  • add table-driven regression coverage for scroll coalescing and non-scroll event preservation

Validation

  • mise exec -- go test ./internal/terminal
  • mise exec -- golangci-lint run ./internal/terminal

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: df89d63f-a431-4926-90a1-66c6528845ac

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2581d and 3607439.

📒 Files selected for processing (1)
  • internal/terminal/render_internal_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/terminal/render_internal_test.go

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved terminal scrolling: page up/down and mouse-wheel inputs now compute and apply scroll deltas more directly.
    • Consecutive scroll events are coalesced to prevent choppy movement and to reduce redundant redraws.
    • Scroll events are handled separately in the render loop, with non-scroll inputs left unaffected.
  • Tests
    • Expanded automated coverage for transcript scrolling, scroll-delta detection, queued scroll coalescing, and render-loop behavior.
    • Added assertions for draw behavior on dirty vs clean frames and handling the latest queued resize.

Walkthrough

Scroll handling now uses shared delta helpers, queued scroll coalescing, and a dedicated app-loop path. The render tests were expanded to cover scroll mapping, queue handling, dirty-frame drawing, resize processing, and the new scrollable test app helper.

Changes

Scroll coalescing feature

Layer / File(s) Summary
Scroll delta extraction helpers
internal/terminal/scroll.go
Refactors transcript and mouse scroll handling to derive deltas through shared event-to-scroll helpers.
Event queue coalescing
internal/terminal/scroll.go
Adds queued scroll aggregation that combines consecutive scroll events and preserves the first non-scroll event as pending.
App loop integration
internal/terminal/app.go
Routes scroll events through handleScrollLoopEvent, which coalesces deltas, scrolls the transcript, draws immediately, and recurses into any pending event.
Scroll and render-loop tests
internal/terminal/render_internal_test.go
Adds coverage for scroll delta mapping, coalescing, queued render-loop events, dirty-frame drawing, resize handling, and the scrollable test app helper.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant handleLoopEvent
  participant handleScrollLoopEvent
  participant coalesceScrollEvents
  participant Transcript

  App->>handleLoopEvent: incoming tcell event
  handleLoopEvent->>handleLoopEvent: scrollDeltaForEvent(event)
  handleLoopEvent->>handleScrollLoopEvent: scroll delta
  handleScrollLoopEvent->>coalesceScrollEvents: coalesce(delta)
  coalesceScrollEvents-->>handleScrollLoopEvent: aggregated delta, pending event
  handleScrollLoopEvent->>Transcript: scrollTranscript(delta)
  handleScrollLoopEvent->>App: draw()
  handleScrollLoopEvent->>handleLoopEvent: handleLoopEvent(pending) if present
Loading

Possibly related PRs

  • omarluq/librecode#75: Also touches the scrollOffset-driven transcript/render path, but from the rendering side rather than event handling.

Poem

A rabbit hops through wheel and key,
Then batches scrolls from queue to sea 🐰
One draw, one hop, one tidy turn,
Pending events softly churn,
And transcript pages bob with glee.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: coalescing terminal scroll events.
Description check ✅ Passed The description matches the changeset and accurately describes scroll coalescing, redraw behavior, and added tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tui-scroll-coalescing

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.53%. Comparing base (fd47b99) to head (3607439).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #184      +/-   ##
==========================================
+ Coverage   82.95%   83.53%   +0.57%     
==========================================
  Files         287      287              
  Lines       23166    23204      +38     
==========================================
+ Hits        19218    19383     +165     
+ Misses       2792     2658     -134     
- Partials     1156     1163       +7     
Flag Coverage Δ
unittests 83.53% <100.00%> (+0.57%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/terminal/app.go`:
- Around line 420-427: The pending event handling in app.handleLoopEvent is
discarding the dirty result, so redraw requests from coalesced.Pending can be
lost after the scroll draw. Update the return path in the loop that processes
coalesced.Pending to capture and propagate the dirty flag from handleLoopEvent
alongside shouldQuit, and ensure the function’s final return reflects that
pending-event mutation. Use app.handleLoopEvent and the coalesced.Pending branch
as the key places to update.

In `@internal/terminal/scroll.go`:
- Around line 55-64: The scrollDeltaForEvent helper currently only gates mouse
scrolling by mode, so PageUp/PageDown key events can still be consumed outside
chat mode before normal dispatch. Update scrollDeltaForEvent, together with
keyScrollDelta if needed, to apply the same modeChat guard to keyboard-based
scroll extraction so non-chat modes continue through handleLoopEvent’s normal
event handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cdc3dc3-e799-4ff3-a8ac-14ca5778d993

📥 Commits

Reviewing files that changed from the base of the PR and between fd47b99 and dbe6350.

📒 Files selected for processing (3)
  • internal/terminal/app.go
  • internal/terminal/render_internal_test.go
  • internal/terminal/scroll.go

Comment thread internal/terminal/app.go
Comment thread internal/terminal/scroll.go
@omarluq omarluq force-pushed the fix/tui-scroll-coalescing branch from dbe6350 to 8b2581d Compare July 2, 2026 03:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/terminal/render_internal_test.go`:
- Around line 794-814: The dirty-frame test is asserting the same expected value
for both subtests, so the “dirty frame draws” case never validates the true
path. Update TestDrawDirtyFrame to use the testCase.dirty value in the assertion
against app.drawDirtyFrame, or split the clean and dirty expectations so the
behavior is checked separately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 98983039-ab97-44d2-895f-8a6aafd83fcb

📥 Commits

Reviewing files that changed from the base of the PR and between dbe6350 and 8b2581d.

📒 Files selected for processing (3)
  • internal/terminal/app.go
  • internal/terminal/render_internal_test.go
  • internal/terminal/scroll.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/terminal/scroll.go
  • internal/terminal/app.go

Comment thread internal/terminal/render_internal_test.go
@sonarqubecloud

sonarqubecloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

@omarluq omarluq merged commit cd52e9e into main Jul 2, 2026
16 checks passed
@omarluq omarluq deleted the fix/tui-scroll-coalescing branch July 2, 2026 03:22
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