""" Terminal UI con scrollback semplice Usa input() standard + visualizzazione scrollback """ import os import sys import signal from typing import Optional, Callable class TerminalUI: """ Terminal UI con scrollback. Uso semplice: input() per input, history buffer per scrollback. """ def __init__(self, scrollback: int = 10000, prompt: str = "❯ "): self.scrollback = scrollback self.prompt = prompt # History buffer self.history: list = [] # Callback self.agent_callback: Optional[Callable[[str], str]] = None def _print(self, text: str): """Stampa con colori base""" # Semplice - solo reset e bold if text.startswith("πŸ‘€"): print(f"\033[1;36m{text}\033[0m") elif text.startswith("πŸ€–"): print(f"\033[1;32m{text}\033[0m") elif text.startswith("πŸ€”"): print(f"\033[1;33m{text}\033[0m") elif text.startswith("❌"): print(f"\033[1;31m{text}\033[0m") else: print(text) def _wrap_line(self, text: str, width: int = 80) -> list: """Wrap linea a width""" lines = [] while len(text) > width: lines.append(text[:width]) text = text[width:] if text: lines.append(text) return lines def _add_history(self, text: str): """Aggiunge a history""" self.history.append(text) while len(self.history) > self.scrollback: self.history.pop(0) def _show_history(self, lines: int = None): """Mostra ultime N linee di history""" if lines is None: # Determina righe visibili try: size = os.get_terminal_size() lines = size.lines - 5 # spazio per header, status, prompt except: lines = 20 # Mostra ultime righe start = max(0, len(self.history) - lines) for line in self.history[start:]: self._print(line) def _process(self, cmd: str): """Processa comando""" if not cmd.strip(): return self._add_history(f"πŸ‘€ {cmd}") # Thinking print("\033[1;33mπŸ€” Pensando...\033[0m") try: resp = self.agent_callback(cmd) # Rimuovi thinking (print sopra con risposta) print("\033[2A\033[K", end="") # move up 2, clear line # Stampa risposta for line in resp.split('\n'): if line.strip(): self._add_history(f"πŸ€– {line}") self._print(f"πŸ€– {line}") except Exception as e: print("\033[2A\033[K", end="") err_msg = f"❌ {str(e)}" self._add_history(err_msg) self._print(err_msg) def _scroll_help(self): """Mostra aiuto scroll""" print("\n" + "=" * 60) print("SCROLLBACK - Premi q per uscire, frecce per navigare") print("=" * 60) print() # Paginator semplice page_size = 30 start = 0 total = len(self.history) while start < total: # Mostra pagina for i in range(start, min(start + page_size, total)): self._print(self.history[i]) start += page_size if start < total: print() print(f"[{start}/{total} righe] - Premi ENTER per continuare, q per uscire") try: choice = input().strip() if choice.lower() == 'q': break except: break print() print("=" * 60) print("FINE SCROLLBACK") print("=" * 60) def start(self, agent_callback: Callable[[str], str]): """Avvia UI""" self.agent_callback = agent_callback # Signal handler def sigint_handler(signum, frame): print("\n\033[1;33mπŸ‘‹ Arrivederci!\033[0m\n") sys.exit(0) signal.signal(signal.SIGINT, sigint_handler) # Header print() print("\033[1;36m" + "=" * 50 + "\033[0m") print("\033[1;36m=\033[0m" + " " * 15 + "\033[1;33mLangCode Agent\033[0m" + " " * 15 + "\033[1;36m=\033[0m") print("\033[1;36m" + "=" * 50 + "\033[0m") print() print("digita /help per aiuto, /exit per uscire") print() while True: try: # Input user_input = input(f"\033[1;35m{self.prompt}\033[0m ").strip() except EOFError: break except KeyboardInterrupt: print("\n\033[1;33mπŸ‘‹ Arrivederci!\033[0m") break if not user_input: continue # Comandi speciali if user_input.lower() == '/exit': print("\033[1;33mπŸ‘‹ Arrivederci!\033[0m") break elif user_input.lower() == '/quit': print("\033[1;33mπŸ‘‹ Arrivederci!\033[0m") break elif user_input.lower() == '/help': print() print("\033[1;36m" + "=" * 40 + "\033[0m") print("COMANDI:") print(" /help - Questo messaggio") print(" /clear - Pulisci schermo") print(" /history - Mostra cronologia") print(" /scroll - Entra in scrollback") print(" /exit, /quit - Esci") print() print("SCROLLBACK:") print(" /scroll - Mostra tutto lo scrollback") print(" Frecce su/giΓΉ - Naviga cronologia (in some terminals)") print() print("\033[1;36m" + "=" * 40 + "\033[0m") print() continue elif user_input.lower() == '/clear': os.system('clear' if os.name != 'nt' else 'cls') continue elif user_input.lower() == '/history': print() self._show_history(50) print() continue elif user_input.lower() == '/scroll': self._scroll_help() continue # Processa comando normale self._process(user_input) print() def create_terminal_ui(scrollback: int = 10000, prompt: str = "❯ ") -> TerminalUI: return TerminalUI(scrollback=scrollback, prompt=prompt) if __name__ == "__main__": def test(text): lines = [f"Ricevuto: {text}"] lines += [f"Linea {i}: contenuto di esempio" for i in range(1, 31)] return "\n".join(lines) ui = create_terminal_ui() ui.start(test)