Support mouse wheel scrolling in the TUI - #174
Conversation
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6ec595a to
900af21
Compare
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
🔦 Lighthouse Report
|
There was a problem hiding this comment.
Pull request overview
Adds mouse wheel scrolling support to the interactive TUI by handling SGR mouse wheel events, clamping scrollOffset via existing viewport logic, and documenting mouse interactions. The PR also expands mouse click hit-testing and introduces a “scroll cooldown” state to suppress clicks during trackpad momentum scrolling.
Changes:
- Handle wheel-up/wheel-down mouse events in
src/tui.tsto scroll by a fixed step while respecting viewport boundaries. - Introduce scroll-cooldown state/helpers with unit tests to ignore clicks during/after momentum scrolling.
- Centralize mouse/layout constants and update hit-testing, rendering, and documentation accordingly.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/tui.ts |
Adds wheel scrolling, scroll cooldown gating, and double-click-based click actions. |
src/scroll-cooldown.ts |
New scroll cooldown state machine helpers. |
src/scroll-cooldown.test.ts |
Unit tests for scroll cooldown behavior. |
src/render/mouse.ts |
Adjusts mouse sequence parsing regex (escape + lint suppression). |
src/render/mouse.test.ts |
Updates mouse parser test imports. |
src/render/mouse-hit.ts |
Adds header offset + scrollOffset-aware hit testing and uses shared layout constants. |
src/render/mouse-hit.test.ts |
Expands hit-testing tests for zones, headerLines, and scrollOffset behavior. |
src/render/layout-constants.ts |
New centralized constants for header height, mouse buttons, and click zones. |
src/render/layout-constants.test.ts |
Unit tests for layout constants and zone helpers. |
src/render.ts |
Re-exports layout constants; adjusts checkbox rendering logic. |
src/render.test.ts |
Adds coverage for dimmed checkbox rendering. |
docs/usage/interactive-mode.md |
Documents mouse support, wheel scrolling, and click/double-click behavior. |
docs/reference/keyboard-shortcuts.md |
Adds mouse wheel + click actions to the reference docs. |
docs/architecture/components.md |
Updates component table and adds detailed mouse interaction model section. |
Suppressed comments (1)
src/render/mouse-hit.ts:12
ClickTarget.columnis documented as “0-based terminal position”, buthitTestClick()passes through the SGR mousexcoordinate (1-indexed) and the tests assert 1-indexed column values. This mismatch is confusing for callers and future maintainers.
export interface ClickTarget {
row: Row;
column: number; // Column index within the row (0-based terminal position)
action: "fold" | "select" | "navigate";
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Apply fixes for determinism, redundant computation, and UX documentation: **scroll-cooldown.ts + scroll-cooldown.test.ts** — Fix non-deterministic test - recordScroll() now accepts optional \`now\` parameter (default: Date.now()) - Tests pass explicit timestamp instead of relying on live clock - Eliminates race condition where Date.now() advances between test capture and function call, causing flaky assertions **render.ts** — Remove redundant repoSelected recomputation - Use group.repoSelected directly instead of recomputing from extractSelected - Comment clarifies that repoSelected is kept in sync by selection logic in tui.ts and render/selection.ts, removing per-frame overhead **docs/architecture/components.md** — Clarify double-click detection ownership - Update mouse hit-test component description: remove claim that hitTestClick() handles double-click detection (it doesn't — only maps coordinates to row+zone) - Add note: double-click detection is implemented in tui.ts via timestamp tracking **src/tui.ts** — Document mouse click semantics - Add explicit comment explaining single-click (navigate) vs double-click (action) - Reference docs/usage/interactive-mode.md § Mouse support for UX spec - Clarifies this is a UX feature complementing keyboard shortcuts Fixes Copilot review issues: - #1: Deterministic test (scroll-cooldown.test.ts:22) - #2: Accept 'now' parameter (scroll-cooldown.ts:26) - #3: Document click semantics (tui.ts:391) - #4: Clarify hit-test responsibility (components.md:115) - #5: Remove redundant repoSelected computation (render.ts:577)
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Apply fixes for determinism, redundant computation, and UX documentation: **scroll-cooldown.ts + scroll-cooldown.test.ts** — Fix non-deterministic test - recordScroll() now accepts optional \`now\` parameter (default: Date.now()) - Tests pass explicit timestamp instead of relying on live clock - Eliminates race condition where Date.now() advances between test capture and function call, causing flaky assertions **render.ts** — Remove redundant repoSelected recomputation - Use group.repoSelected directly instead of recomputing from extractSelected - Comment clarifies that repoSelected is kept in sync by selection logic in tui.ts and render/selection.ts, removing per-frame overhead **docs/architecture/components.md** — Clarify double-click detection ownership - Update mouse hit-test component description: remove claim that hitTestClick() handles double-click detection (it doesn't — only maps coordinates to row+zone) - Add note: double-click detection is implemented in tui.ts via timestamp tracking **src/tui.ts** — Document mouse click semantics - Add explicit comment explaining single-click (navigate) vs double-click (action) - Reference docs/usage/interactive-mode.md § Mouse support for UX spec - Clarifies this is a UX feature complementing keyboard shortcuts Fixes Copilot review issues: - #1: Deterministic test (scroll-cooldown.test.ts:22) - #2: Accept 'now' parameter (scroll-cooldown.ts:26) - #3: Document click semantics (tui.ts:391) - #4: Clarify hit-test responsibility (components.md:115) - #5: Remove redundant repoSelected computation (render.ts:577)
e49d1c3 to
334beee
Compare
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 similar comment
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Implement scroll-wheel support for interactive TUI (#168): - Recognize wheel-up (button 64) and wheel-down (button 65) in mouse events - Scroll by 3 rows per wheel tick (small incremental step vs full page) - Use normalizeScrollOffset to clamp scroll position within valid bounds - Wheel scroll does not move cursor or change selection/fold state - Tested alongside click-to-toggle; all validation checks pass
Issue: When clicking on any row in the TUI, getting 'Cannot access rows before initialization' error causing immediate exit with code 1. Root cause: rows was declared as a local variable inside redraw(), but hitTestClick tried to access it in the event loop where it didn't exist. Fix: Declare rows as a persistent variable outside redraw() and update it on each redraw() call. This makes rows available throughout the event loop. Also fixed normalizeScrollOffset calls for wheel scroll to use correct signature: normalizeScrollOffset(scrollOffset, rows, groups, viewportHeight). Closes #173 (partial - fixes the crash, functionality preserved)
Test verifies that deselected repos render with dimmed ✓ character (via ANSI dim code \x1b[2m) instead of blank space, improving visual consistency. Closes #169 (UI Consistency - Dimmed Checkboxes)
- Use Unicode escape \u001b instead of \x1b in SGR regex (oxlint compatibility) - Disable no-control-regex lint rule with explicit comment for ESC character - Remove unused type imports (MouseEvent, ClickTarget) from test files - Fix variable shadowing: use reassignment instead of redeclaration for 'rows' in normal mode
Issues: - Click coordinates were off because headerLines (filter bar + position indicator) weren't subtracted from the y coordinate, causing clicks to register at the wrong rows (user had to click much higher than intended) - Toggle state was being applied twice (press + release), so select/unselect wasn't working correctly Fixes: - Add headerLines parameter to hitTestClick() to account for header rendering Calculate headerLines in tui.ts based on filter state (0-2 lines) - Subtract headerLines from click y coordinate to map to correct row - Ignore mouse release events (isRelease === true); only act on press events - This ensures select/unselect toggles work on a single click, not double-toggling Result: Mouse clicks now target correct rows and selections toggle properly
The headerLines calculation was using an incorrect base value (2 instead of 4), causing all mouse clicks to register with a larger y-coordinate offset than intended. This made it impossible to click on the intended rows — users had to click much higher on the screen. Fixed calculation: - Base HEADER_LINES = 4 (title + summary + hints + blank) - Add filterBarLines (0-2) for filter input/status bar - Add 1 if sticky repo header is shown (currently omitted as it requires complex cursor state evaluation; may cause 1-row offset in rare cases) Now mouse clicks register at the correct row positions.
Major behavioral changes to mouse interaction: - Single-click on any row: navigate to that row (like arrow keys) - Double-click on fold zone (▸/▾): toggle fold/unfold - Double-click on checkbox or elsewhere on row: toggle selection - Wheel up/down: scroll viewport (unchanged) Technical fixes: - hitTestClick() now iterates only from scrollOffset onwards (previously counted invisible rows, causing hits to register at wrong lines) - Checkbox clicks now only register on extract header line (not fragments) - Double-click detection uses 300ms delay and row key (type:repoIndex:extractIndex) - Moved getRowKey() to module scope (avoid recreating per call) Result: Mouse support now matches keyboard behavior for navigation and selection with intuitive click/double-click patterns.
Major coordinate and action fixes: - Fix hitTestClick to use 1-indexed terminal coordinates (SGR protocol standard) - Arrow emoji: columns 1-2 (was testing column 0) - Checkbox emoji: columns 4-5 (was testing column 2) - This fixes the off-by-1 Y coordinate issue reported - Fix double-click on repo to cascade deselect all extracts - Now matches keyboard spacebar behavior: toggle repo + all extracts - Fix double-click on extract header to work on full line width (except fold zone) - Accept selection action on columns 4-5 (checkbox) or 6+ (full width) - Fix repo checkbox dimming logic - Now shows green ✓ only if any extract is selected - Shows dimmed ✓ when all extracts are deselected - Independent of repo.repoSelected flag Test improvements: - Updated hitTestClick tests to use correct 1-indexed coordinates - Added test for extract fragment lines (not clickable for selection) - Added test for full-width extract selection - Fixed render test to deselect extracts when testing dimmed repo checkbox - All 840 tests passing, 100% coverage on mouse-hit module
The hit-test function was using rowTerminalLines which always returns 2 for sections, but renderGroups treats the first section differently: - First section in viewport: 1 line (label only, no blank separator) - Subsequent sections: 2 lines (blank separator + label) This caused a 1-line offset when calculating cumulative line heights, making clicks on repos and extracts land one row below the target. Fix: Mirror renderGroups logic in hitTestClick - check if section is first in viewport (lineOffset === 0) to calculate correct height (1 vs 2 lines).
When scrolling with the mouse wheel, click events can fire at the scroll end point, accidentally selecting or toggling items the user didn't intend. Fix: Add a SCROLL_COOLDOWN (300ms) flag that activates after wheel events, ignoring select/fold actions during this period. Navigation (moving cursor) is still allowed during scroll cooldown. Changes: - Add scrollInProgress flag and SCROLL_COOLDOWN timer - Activate flag on wheel up/down events - Check flag before applying selection/fold actions - Only allow navigation during scroll cooldown - Clean up timer on exit
The previous implementation using a boolean flag and setTimeout had a race condition where click events could fire between wheel events and timer setup, causing accidental selections during scroll. Switch to tracking lastScrollTime with Date.now() and checking (now - lastScrollTime < SCROLL_COOLDOWN) directly at click time. This is more robust and eliminates async timing issues. Behavior is unchanged: selection/fold actions are blocked for 300ms after scroll, while navigation is still allowed.
Trackpad users experience momentum (deceleration) scrolling that can continue for 400-500ms after the physical gesture ends. The previous 300ms cooldown was too short, causing accidental selections. Increase SCROLL_COOLDOWN from 300ms to 500ms to better accommodate trackpad momentum scrolling. The lastScrollTime timestamp continues to be updated on each scroll event, so multiple scrolls in quick succession will extend the cooldown period as expected.
Extract scroll cooldown logic from tui.ts into scroll-cooldown.ts as a pure, immutable state machine. This makes the logic: 1. Testable: 8 comprehensive tests with 100% coverage 2. Explicit: Clear state transitions and timing logic 3. Robust: No reliance on closure-captured timestamps 4. Debuggable: Pure functions are easier to reason about Changes: - Create scroll-cooldown.ts with ScrollCooldownState interface - Implement createScrollCooldownState, recordScroll, isScrollCooldownActive, updateScrollCooldown - Add scroll-cooldown.test.ts with 8 tests covering all edge cases - Update tui.ts to use the new module instead of lastScrollTime variable - Reduce cooldown from 5000ms to 600ms (conservative for trackpad momentum) The state machine handles: - Initial state (no scrolling) - Multiple rapid scrolls extending the cooldown window - Proper expiration after timeout - Clean deactivation when cooldown expires Tests verify correct behavior for all timing scenarios.
…lections The previous approach of allowing navigation and only blocking selections was fragile - during trackpad momentum scrolling, clicks could still fire with confusing actions that slipped through. New approach: if scroll cooldown is active, COMPLETELY IGNORE all clicks before even calling hitTestClick. This is simple, robust, and bulletproof. Changes: - Move cooldown check to BEFORE hitTestClick call - Check happens immediately after scroll event detection - If cooldown is active, skip all click processing entirely - Remove now-unnecessary cooldown logic from inside target handling This eliminates the entire class of bugs where clicks arrive during momentum scrolling and trigger unexpected actions.
- Create src/render/layout-constants.ts with centralized rendering measurements
- BASE_HEADER_LINES (4): title + summary + hints + blank
- FILTER_BAR_LINES_NORMAL (1): filter status line
- FILTER_BAR_LINES_ACTIVE (2): filter input + hints in filter mode
- getHeaderLines(): compute total header height based on mode
- Column position constants: FOLD_COLUMN_START/END, CHECKBOX_COLUMN_START/END, NAV_COLUMN_START
- Helper functions: isClickInFoldZone(), isClickInCheckboxZone(), isClickInNavZone()
- Update src/tui.ts
- Replace 'let headerLines = 4; headerLines += ...' with getHeaderLines() call
- Import getHeaderLines from render.ts (public API)
- Update src/render/mouse-hit.ts
- Replace magic column numbers (1, 2, 4, 5, 6) with named constants
- Import zone detection helpers for cleaner hit-test logic
- Clarify row layout comments for fold, checkbox, and nav zones
- Expand src/render/mouse-hit.test.ts
- Add 22 new tests organizing hit-test coverage by zone and row type
- Test fold zone (repo rows only): columns 1-2
- Test checkbox zone: columns 4-5 for repo/extract headers
- Test navigation zone: columns 6+
- Test section rows (navigate only, no special actions)
- Test scroll offset handling and zone boundary conditions
- Verify correct action ('fold', 'select', 'navigate') for each zone
- Add src/render/layout-constants.test.ts
- Test header line calculation for all filter mode combinations
- Verify zone detection functions and column constant ranges
- Ensure non-overlapping zone definitions
Results: 868 tests pass, zero lint errors, format clean, knip clean, build succeeds
- Extract mouse button codes into layout-constants: - MOUSE_BUTTON_WHEEL_UP = 64 (SGR protocol) - MOUSE_BUTTON_WHEEL_DOWN = 65 (SGR protocol) - MOUSE_SCROLL_STEP = 3 (rows per wheel scroll) - Update checkbox zone detection logic: - isClickInCheckboxZone now extends from column 4 to infinity (full-width double-click) - Double-click selection works anywhere on repo/extract row from checkbox column onwards - Adapt all tests to reflect full-width click behavior - Extract terminal mouse reporting sequences into named constants: - ANSI_ENABLE_MOUSE_REPORTING (\x1b[?1000h\x1b[?1006h) - ANSI_DISABLE_MOUSE_REPORTING (\x1b[?1000l\x1b[?1006l) - Use in enable/disable code paths in tui.ts Results: 871 tests pass, zero lint errors, all validations green
- Add comprehensive mouse support section to keyboard-shortcuts.md:
- Scroll wheel behavior (3 rows per event, momentum cooldown)
- Single-click navigation, double-click actions
- Column zone mapping for repo and extract rows
- Full-width checkbox zone for intuitive double-click selection
- Add mouse support guide to interactive-mode.md:
- Scrolling mechanics with trackpad momentum protection
- Single-click and double-click action reference
- Zone-based click handling for repos and extracts
- Emphasis on mouse-keyboard complementarity
- Update architecture/components.md with:
- Two new component table entries: Layout constants, Mouse hit-test
- Detailed mouse interaction model section explaining:
- SGR protocol, coordinate mapping, header height calculation
- Click zones and hit-testing algorithm
- Double-click detection and deduplication
- Scroll cooldown state machine for momentum scrolling
- TUI integration points
- Fix linting errors:
- Export MOUSE_SCROLL_STEP from layout-constants.ts
- Remove unused isClickInNavZone import from mouse-hit.ts
- Update render.ts to re-export MOUSE_SCROLL_STEP from layout-constants
- Update tui.ts to import MOUSE_SCROLL_STEP from render.ts instead of defining locally
Results: 871 tests pass, zero lint errors, all validations green
- Update feature card: 'Keyboard-driven TUI' → 'Keyboard and mouse-driven TUI' Add mention of mouse clicks alongside arrow keys in feature description - Update HowItWorks component Step 2 description: 'A keyboard-driven TUI opens' → 'A keyboard and mouse-driven TUI opens' Add note: '(mouse works in compatible terminals)' - Update ComparisonTable feature description: Expand 'Interactive TUI' row to emphasize both keyboard AND mouse support 'arrow keys or clicks for navigation' highlights dual interaction model Focus: emphasize that mouse support is now a first-class feature alongside keyboard-driven workflow, across homepage, feature highlights, and comparison.
Apply fixes for determinism, redundant computation, and UX documentation: **scroll-cooldown.ts + scroll-cooldown.test.ts** — Fix non-deterministic test - recordScroll() now accepts optional \`now\` parameter (default: Date.now()) - Tests pass explicit timestamp instead of relying on live clock - Eliminates race condition where Date.now() advances between test capture and function call, causing flaky assertions **render.ts** — Remove redundant repoSelected recomputation - Use group.repoSelected directly instead of recomputing from extractSelected - Comment clarifies that repoSelected is kept in sync by selection logic in tui.ts and render/selection.ts, removing per-frame overhead **docs/architecture/components.md** — Clarify double-click detection ownership - Update mouse hit-test component description: remove claim that hitTestClick() handles double-click detection (it doesn't — only maps coordinates to row+zone) - Add note: double-click detection is implemented in tui.ts via timestamp tracking **src/tui.ts** — Document mouse click semantics - Add explicit comment explaining single-click (navigate) vs double-click (action) - Reference docs/usage/interactive-mode.md § Mouse support for UX spec - Clarifies this is a UX feature complementing keyboard shortcuts Fixes Copilot review issues: - #1: Deterministic test (scroll-cooldown.test.ts:22) - #2: Accept 'now' parameter (scroll-cooldown.ts:26) - #3: Document click semantics (tui.ts:391) - #4: Clarify hit-test responsibility (components.md:115) - #5: Remove redundant repoSelected computation (render.ts:577)
Two issues were preventing the terminal from returning to normal mode on exit: 1. **Missing ANSI_DISABLE_MOUSE_REPORTING on Enter exit path** When pressing Enter to confirm and exit, ANSI_DISABLE_MOUSE_REPORTING was not being sent. This left the terminal in mouse-capture mode, preventing text selection and copy/paste. 2. **Event loop not breaking on exit** Using process.exit() or process.exitCode was insufficient because the 'for await (const chunk of process.stdin)' loop continued waiting for stdin, preventing the stdout buffer from flushing and the process from terminating cleanly. **Changes:** - Add shouldExit flag to coordinate exit across multiple code paths - Send ANSI_DISABLE_MOUSE_REPORTING in both exit() and Enter path - Add break statement after exit conditions to cleanly exit the event loop - Move stats timer cleanup into Enter path (was missing) - Replace process.exit(0) with flag-based exit to allow buffer flush Result: Terminal properly returns to normal mode (text selection enabled) when exiting via Enter, q, or Ctrl+C.
334beee to
5c7b881
Compare
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 similar comment
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
498dc33 to
60515ab
Compare
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
60515ab to
18edea7
Compare
|
Coverage after merging feat/mouse-scroll-wheel into feat/mouse-click-toggle will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
What does this PR do?
Adds mouse wheel scrolling support to the interactive TUI. Users can scroll up/down through results using their mouse wheel, with smooth 3-row incremental scrolling that respects viewport boundaries.
Changes
src/tui.tsmouse event dispatchernormalizeScrollOffset()to clamp scroll position within valid boundsHow did you verify your code works?
normalizeScrollOffset()using correct function signatureCloses #168