- input() normale invece di raw mode - /scroll per vedere tutto lo scrollback con paginazione - /history per vedere ultime 50 righe - Molto piu stabile e compatibile - Niente piu problemi di escape sequences
225 lines
7.0 KiB
Python
225 lines
7.0 KiB
Python
"""
|
||
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)
|