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 handle_key(self, stdscr, key)