"""
Terminal buffer management using pyte for visual screen capture.
"""
import logging
from typing import Dict, List, Tuple
import pyte
logger = logging.getLogger(__name__)
class TerminalBuffer:
"""
Manages terminal screen buffer and visual output capture.
Uses pyte to emulate a terminal screen and process ANSI/VT100 sequences.
Provides snapshot of what a human would see in the terminal.
"""
def __init__(self, rows: int = 24, cols: int = 80):
"""
Initialize terminal buffer.
Args:
rows: Number of rows (height)
cols: Number of columns (width)
"""
self.rows = rows
self.cols = cols
# Create pyte screen
self.screen = pyte.Screen(cols, rows)
self.stream = pyte.ByteStream(self.screen)
logger.debug(f"Terminal buffer created: {rows}x{cols}")
def feed(self, data: bytes):
"""
Feed data to the terminal buffer.
Processes ANSI escape sequences and updates screen buffer.
Args:
data: Raw bytes from terminal output
"""
try:
self.stream.feed(data)
except Exception as e:
logger.error(f"Error feeding data to buffer: {e}")
def get_display(self) -> str:
"""
Get current terminal display as plain text.
Returns what a human would see on screen.
Returns:
Terminal display as multi-line string
"""
try:
lines = []
for row_idx in range(self.rows):
line = self.screen.display[row_idx]
lines.append(line)
return "\n".join(lines)
except Exception as e:
logger.error(f"Error getting display: {e}")
return ""
def get_display_lines(self) -> List[str]:
"""
Get current terminal display as list of lines.
Returns:
List of strings, one per row
"""
try:
return [self.screen.display[i] for i in range(self.rows)]
except Exception as e:
logger.error(f"Error getting display lines: {e}")
return []
def get_cursor_position(self) -> Tuple[int, int]:
"""
Get current cursor position.
Returns:
Tuple of (row, col) - zero-indexed
"""
return (self.screen.cursor.y, self.screen.cursor.x)
def get_cursor_info(self) -> Dict[str, int]:
"""
Get detailed cursor information.
Returns:
Dict with cursor details
"""
return {
"row": self.screen.cursor.y,
"col": self.screen.cursor.x,
"visible": not self.screen.cursor.hidden,
}
def get_snapshot(self) -> Dict[str, any]:
"""
Get complete snapshot of terminal state.
Returns:
Dict containing display, cursor position, and metadata
"""
cursor_row, cursor_col = self.get_cursor_position()
return {
"display": self.get_display(),
"lines": self.get_display_lines(),
"cursor": {"row": cursor_row, "col": cursor_col},
"size": {"rows": self.rows, "cols": self.cols},
}
def resize(self, rows: int, cols: int):
"""
Resize the terminal buffer.
Args:
rows: New number of rows
cols: New number of columns
"""
try:
self.rows = rows
self.cols = cols
self.screen.resize(rows, cols)
logger.debug(f"Buffer resized to {rows}x{cols}")
except Exception as e:
logger.error(f"Error resizing buffer: {e}")
raise
def clear(self):
"""Clear the terminal buffer."""
try:
self.screen.reset()
except Exception as e:
logger.error(f"Error clearing buffer: {e}")
def get_line(self, row: int) -> str:
"""
Get specific line from display.
Args:
row: Row index (zero-indexed)
Returns:
Line content as string
"""
if 0 <= row < self.rows:
return self.screen.display[row]
return ""