Dynamic spacer and silent append when scrolled up

- Spacer only shows when output is at bottom (disappears when scrolled up)
- New content appends silently when user has scrolled up (preserves position)
- is_at_bottom() helper to check scroll state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dullfig 2026-01-11 15:27:21 -08:00
parent 4dcc726280
commit 45d6eea4ac

View file

@ -105,17 +105,35 @@ class OutputBuffer:
self._update_buffer() self._update_buffer()
def _update_buffer(self): def _update_buffer(self):
"""Update the buffer content and scroll to bottom.""" """Update the buffer content, preserve scroll position if user scrolled up."""
# Trim if needed # Trim if needed
if len(self._lines) > self.max_lines: if len(self._lines) > self.max_lines:
self._lines = self._lines[-self.max_lines:] self._lines = self._lines[-self.max_lines:]
# Check if cursor is at end (user hasn't scrolled up)
old_text = self.buffer.text
was_at_end = self.buffer.cursor_position >= len(old_text)
# Update buffer text # Update buffer text
text = "\n".join(self._lines) text = "\n".join(self._lines)
self.buffer.set_document(
Document(text=text, cursor_position=len(text)), if was_at_end:
bypass_readonly=True # Stay at bottom - auto-scroll
) self.buffer.set_document(
Document(text=text, cursor_position=len(text)),
bypass_readonly=True
)
else:
# Preserve scroll position - silent append
old_pos = self.buffer.cursor_position
self.buffer.set_document(
Document(text=text, cursor_position=old_pos),
bypass_readonly=True
)
def is_at_bottom(self) -> bool:
"""Check if output is scrolled to bottom."""
return self.buffer.cursor_position >= len(self.buffer.text)
def clear(self): def clear(self):
"""Clear output.""" """Clear output."""
@ -245,8 +263,14 @@ class TUIConsole:
right_margins=[ScrollbarMargin(display_arrows=True)], right_margins=[ScrollbarMargin(display_arrows=True)],
) )
# Blank line spacer above separator # Dynamic spacer - only show when at bottom of output
spacer = Window(height=1) def get_spacer_height():
if self.output.is_at_bottom():
return 1
return 0
from prompt_toolkit.layout.dimension import Dimension
spacer = Window(height=lambda: Dimension.exact(get_spacer_height()))
# Separator line with status # Separator line with status
def get_separator(): def get_separator():