"""Keyboard input handling for terminal UI.""" import sys import select import termios import tty from typing import Optional, Callable from dataclasses import dataclass @dataclass class KeyAction: """Represents a keyboard action.""" key: str action: str description: str class KeyboardHandler: """ Non-blocking keyboard input handler. Uses terminal raw mode to capture single keypresses without waiting for Enter. """ # Key mappings ACTIONS = { "q": "quit", "Q": "quit", "\x03": "quit", # Ctrl+C "r": "refresh", "R": "refresh", "f": "filter", "F": "filter", "t": "filter_trades", "T": "filter_trades", "l": "filter_all", "L": "filter_all", "e": "filter_errors", "E": "filter_errors", "1": "tab_general", "2": "tab_monthly", "3": "tab_weekly", "4": "tab_daily", } def __init__(self): self._old_settings = None self._enabled = False def enable(self) -> bool: """ Enable raw keyboard input mode. Returns: True if enabled successfully """ try: if not sys.stdin.isatty(): return False self._old_settings = termios.tcgetattr(sys.stdin) tty.setcbreak(sys.stdin.fileno()) self._enabled = True return True except Exception: return False def disable(self) -> None: """Restore normal terminal mode.""" if self._enabled and self._old_settings: try: termios.tcsetattr( sys.stdin, termios.TCSADRAIN, self._old_settings, ) except Exception: pass self._enabled = False def get_key(self, timeout: float = 0.1) -> Optional[str]: """ Get a keypress if available (non-blocking). Args: timeout: Seconds to wait for input Returns: Key character or None if no input """ if not self._enabled: return None try: readable, _, _ = select.select([sys.stdin], [], [], timeout) if readable: return sys.stdin.read(1) except Exception: pass return None def get_action(self, timeout: float = 0.1) -> Optional[str]: """ Get action name for pressed key. Args: timeout: Seconds to wait for input Returns: Action name or None """ key = self.get_key(timeout) if key: return self.ACTIONS.get(key) return None def __enter__(self): """Context manager entry.""" self.enable() return self def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit.""" self.disable() return False