127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
# 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()
|