hendrik/tui/app.py

70 lines
2.7 KiB
Python

import curses
from .render import init_colors, draw
from .input import handle_key
class HendrikTUI:
# State holder & orchestrator.
# Semua data UI disimpan di sini, method lain (render, input, agent)
# menerima `app` (self) sebagai parameter pertama.
def __init__(self, llm_client, tools_definition, TOOLS, TOOL_HANDLERS,
build_system_prompt, agent_max_iterations):
# -- dependencies (disuntik dari luar) --
self.llm = llm_client
self.tools_def = tools_definition
self.TOOLS = TOOLS
self.TOOL_HANDLERS = TOOL_HANDLERS
self.build_system_prompt = build_system_prompt
self.agent_max_iterations = agent_max_iterations
# -- UI state --
self.messages = None # chat history yg dikirim ke LLM API
self.log = [] # rendered log (display-only, ada timestamp)
self.input_buffer = [""] # baris-baris input multi-line
self.input_line = 0 # index baris aktif di buffer
self.input_col = 0 # kolom kursor di baris aktif
self.scroll = 0 # scroll offset chat area
self.processing = False # true saat agent sedang memproses
self.running = True # false → keluar dari main loop
self.h, self.w = 0, 0 # ukuran terminal (height, width)
def run(self):
# Masuk ke curses wrapper. wrapper() setup/teardown terminal
# dan menangani restore terminal meskipun terjadi error.
try:
curses.wrapper(self._main)
except KeyboardInterrupt:
pass
print("Exiting.")
def _main(self, stdscr):
# Main loop: draw → getch → handle
curses.use_default_colors()
init_colors()
stdscr.keypad(True) # enable key codes (KEY_UP, KEY_DOWN, dll)
stdscr.refresh()
# Init system prompt — sekali di awal
self.messages = [{"role": "system",
"content": self.build_system_prompt(self.tools_def)}]
while self.running:
self.h, self.w = stdscr.getmaxyx()
# Minimal ukuran terminal biar UI gak rusak
if self.h < 14 or self.w < 40:
stdscr.erase()
stdscr.addstr(0, 0, "Terminal too small (min 40x14)")
stdscr.refresh()
stdscr.getch()
continue
draw(self, stdscr)
# Sembunyikan kursor saat processing, tampilkan high visibility saat idle
curses.curs_set(0 if self.processing else 2)
try:
key = stdscr.getch()
except KeyboardInterrupt:
break
if not self.processing:
handle_key(self, stdscr, key)