diff --git a/agentserver/console/tui_console.py b/agentserver/console/tui_console.py index 971dbe9..a354bf7 100644 --- a/agentserver/console/tui_console.py +++ b/agentserver/console/tui_console.py @@ -201,6 +201,54 @@ class TUIConsole: """Switch focus between output and input.""" event.app.layout.focus_previous() + # Arrow keys for scrolling output (move cursor to scroll view) + @kb.add("up", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_up(event): + """Scroll output up.""" + buf = self.output.buffer + # Move cursor up one line + doc = buf.document + if doc.cursor_position_row > 0: + new_pos = doc.translate_row_col_to_index(doc.cursor_position_row - 1, 0) + buf.cursor_position = new_pos + + @kb.add("down", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_down(event): + """Scroll output down.""" + buf = self.output.buffer + doc = buf.document + if doc.cursor_position_row < doc.line_count - 1: + new_pos = doc.translate_row_col_to_index(doc.cursor_position_row + 1, 0) + buf.cursor_position = new_pos + + @kb.add("pageup", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_pageup(event): + """Scroll output up a page.""" + buf = self.output.buffer + doc = buf.document + new_row = max(0, doc.cursor_position_row - 20) + new_pos = doc.translate_row_col_to_index(new_row, 0) + buf.cursor_position = new_pos + + @kb.add("pagedown", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_pagedown(event): + """Scroll output down a page.""" + buf = self.output.buffer + doc = buf.document + new_row = min(doc.line_count - 1, doc.cursor_position_row + 20) + new_pos = doc.translate_row_col_to_index(new_row, 0) + buf.cursor_position = new_pos + + @kb.add("home", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_home(event): + """Scroll to top of output.""" + self.output.buffer.cursor_position = 0 + + @kb.add("end", filter=lambda: self.app.layout.has_focus(self.output.buffer)) + def handle_output_end(event): + """Scroll to bottom of output.""" + self.output.buffer.cursor_position = len(self.output.buffer.text) + # Output uses BufferControl for native scrolling output_control = BufferControl( buffer=self.output.buffer, @@ -422,9 +470,10 @@ class TUIConsole: self.print_raw("Shortcuts:", "output.system") self.print_raw(" Ctrl+C / Ctrl+D Quit", "output.dim") self.print_raw(" Ctrl+L Clear output", "output.dim") - self.print_raw(" Up/Down Command history", "output.dim") + self.print_raw(" Up/Down Command history (in input)", "output.dim") self.print_raw(" Tab Switch focus (input/output)", "output.dim") - self.print_raw(" Arrow keys Scroll when output focused", "output.dim") + self.print_raw(" Up/Down/PgUp/PgDn Scroll (when output focused)", "output.dim") + self.print_raw(" Home/End Jump to top/bottom of output", "output.dim") async def _cmd_status(self, args: str): """Show status."""