# input.py — Keyboard handling dan workspace popup. # handle_key() adalah dispatch besar yang menerjemahkan # key code curses menjadi aksi pada state app. import curses import os from .agent import submit, log def handle_key(app, stdscr, key): # -- Ctrl shortcuts -- if key == 4: # Ctrl+D → submit query ke LLM submit(app, stdscr) elif key == 23: # Ctrl+W → popup ganti workspace workspace_popup(app, stdscr) elif key == 3: # Ctrl+C → exit app.running = False elif key == 12: # Ctrl+L → clear chat log app.log.clear() # -- Enter: buat baris baru di input buffer -- elif key in (curses.KEY_ENTER, 10, 13): app.input_buffer.insert(app.input_line + 1, "") app.input_line += 1 app.input_col = 0 # -- Backspace: hapus karakter sebelumnya atau gabung baris -- elif key in (curses.KEY_BACKSPACE, 127): if app.input_col > 0: line = app.input_buffer[app.input_line] app.input_buffer[app.input_line] = ( line[: app.input_col - 1] + line[app.input_col :] ) app.input_col -= 1 elif app.input_line > 0: carry = app.input_buffer.pop(app.input_line) app.input_line -= 1 app.input_col = len(app.input_buffer[app.input_line]) app.input_buffer[app.input_line] += carry # -- Navigation arrows -- elif key == curses.KEY_UP: if app.input_line > 0: app.input_line -= 1 app.input_col = min( app.input_col, len(app.input_buffer[app.input_line]) ) elif key == curses.KEY_DOWN: if app.input_line < len(app.input_buffer) - 1: app.input_line += 1 app.input_col = min( app.input_col, len(app.input_buffer[app.input_line]) ) elif key == curses.KEY_LEFT: if app.input_col > 0: app.input_col -= 1 elif app.input_line > 0: app.input_line -= 1 app.input_col = len(app.input_buffer[app.input_line]) elif key == curses.KEY_RIGHT: if app.input_col < len(app.input_buffer[app.input_line]): app.input_col += 1 elif app.input_line < len(app.input_buffer) - 1: app.input_line += 1 app.input_col = 0 elif key == curses.KEY_HOME: app.input_col = 0 elif key == curses.KEY_END: app.input_col = len(app.input_buffer[app.input_line]) # -- Page Up / Down: scroll chat area -- elif key == curses.KEY_PPAGE: app.scroll = max(0, app.scroll - (app.h - 10)) elif key == curses.KEY_NPAGE: app.scroll += app.h - 10 # -- Resize terminal: tidak perlu action, next loop akan baca ukuran baru -- elif key == curses.KEY_RESIZE: pass # -- Tab: insert 4 spasi -- elif key == 9: line = app.input_buffer[app.input_line] app.input_buffer[app.input_line] = ( line[: app.input_col] + " " + line[app.input_col :] ) app.input_col += 4 # -- Printable characters -- elif 32 <= key <= 255: ch = chr(key) line = app.input_buffer[app.input_line] app.input_buffer[app.input_line] = ( line[: app.input_col] + ch + line[app.input_col :] ) app.input_col += 1 def workspace_popup(app, stdscr): # Overlay window kecil di tengah layar untuk input path workspace pw = min(60, app.w - 4) ph = 3 px = (app.w - pw) // 2 py = app.h // 2 - 1 win = curses.newwin(ph, pw, py, px) win.box() win.addstr(0, 2, " Workspace path: ") win.addstr(1, 2, " " * (pw - 4)) curses.echo() # tampilkan input user ws = win.getstr(1, 2, pw - 5).decode("utf-8") curses.noecho() del win ws = ws.strip() if ws: resolved = os.path.abspath(ws) if os.path.isdir(resolved): os.chdir(resolved) log(app, "system", f"Workspace \u2192 {resolved}") else: log(app, "error", f"Invalid directory: {resolved}") stdscr.touchwin() stdscr.refresh()