Add mouse wheel and keyboard scrolling support to REPL

- Add scroll_offset to REPLState (max 50 lines)
- Modify _render_repl() to use manual scroll position
- Add scroll_output(delta) method for scroll control
- Add PageUp/PageDown keyboard support (scroll 10 lines)
- Add mouse wheel support via SGR mouse tracking
- Update HUD to show scroll percentage (like vim) and position
- Reset scroll when new output arrives
- Add tests for scroll functionality
This commit is contained in:
2026-03-22 17:26:14 -07:00
parent e5799a346a
commit bfcad4963a
6 changed files with 257 additions and 21 deletions

View File

@@ -199,3 +199,60 @@ class TestReplConfig:
assert repl.config.enabled is False
assert repl.config.intensity == 0.5
assert repl.config.params["display_height"] == 10
class TestReplScrolling:
"""Tests for REPL scrolling functionality."""
@pytest.fixture(autouse=True)
def setup(self):
"""Setup before each test."""
self.repl = ReplEffect()
def test_scroll_offset_initial(self):
"""Scroll offset starts at 0."""
assert self.repl.state.scroll_offset == 0
def test_scroll_output_positive(self):
"""Scrolling with positive delta moves back through buffer."""
# Add some output
self.repl.state.output_buffer = [f"line{i}" for i in range(20)]
# Scroll up 5 lines
self.repl.scroll_output(5)
assert self.repl.state.scroll_offset == 5
def test_scroll_output_negative(self):
"""Scrolling with negative delta moves forward through buffer."""
# Add some output and scroll up first
self.repl.state.output_buffer = [f"line{i}" for i in range(20)]
self.repl.state.scroll_offset = 10
# Scroll down 3 lines
self.repl.scroll_output(-3)
assert self.repl.state.scroll_offset == 7
def test_scroll_output_bounds(self):
"""Scroll offset stays within valid bounds."""
# Add some output
self.repl.state.output_buffer = [f"line{i}" for i in range(10)]
# Try to scroll past top
self.repl.scroll_output(100)
assert self.repl.state.scroll_offset == 9 # max: len(output) - 1
# Try to scroll past bottom
self.repl.state.scroll_offset = 5
self.repl.scroll_output(-100)
assert self.repl.state.scroll_offset == 0
def test_scroll_resets_on_new_output(self):
"""Scroll offset resets when new command output arrives."""
self.repl.state.output_buffer = [f"line{i}" for i in range(20)]
self.repl.state.scroll_offset = 10
# Process a new command
self.repl.process_command("test command")
# Scroll offset should be reset to 0
assert self.repl.state.scroll_offset == 0