# actions.py — Registry aksi TUI (single source of truth). # # Semua aksi didefinisikan satu kali di sini, lengkap dengan: # * shortcut key langsung (dipakai oleh dispatch global) # * spesifikasi item menu (dipakai oleh menubar) # * handler & predikat enabled # Dengan begitu shortcut dan menu selalu sinkron; developer cukup # menambah/edit satu entri untuk fitur baru. from .keycodes import ( KEY_CTRL_C, KEY_CTRL_ENTER, KEY_CTRL_N, KEY_CTRL_O, KEY_ESC, KEY_F2, KEY_F4, KEY_F6, ) from .input import ( new_session_popup, session_browser_popup, rename_popup, model_selector_popup, workspace_popup, ) from .agent import submit, log # ---------------------------------------------------------------- handlers --- def _exit_app(app, stdscr): app.running = False def _cancel_inference(app, stdscr): app.llm.cancel_requested = True app.agent_done.set() log(app, "system", " Inference cancelled by user") def _send_prompt(app, stdscr): submit(app, stdscr) def _ready(app): return not app.processing # ------------------------------------------------------------- action spec --- # Tiap aksi: key, label, mnemonic, shortcut (teks), handler, enabled. ACTIONS = { "new_session": { "key": KEY_CTRL_N, "label": "New Session", "mnemonic": "N", "shortcut": "Ctrl+N", "handler": new_session_popup, "enabled": _ready, }, "open_session": { "key": KEY_CTRL_O, "label": "Open Session", "mnemonic": "O", "shortcut": "Ctrl+O", "handler": session_browser_popup, "enabled": _ready, }, "rename_session": { "key": KEY_F2, "label": "Rename Session", "mnemonic": "R", "shortcut": "F2", "handler": rename_popup, "enabled": lambda app: _ready(app) and app.current_session is not None, }, "exit_app": { "key": KEY_CTRL_C, "label": "Exit", "mnemonic": "E", "shortcut": "Ctrl+C", "handler": _exit_app, "enabled": lambda app: True, }, "send_prompt": { "key": KEY_CTRL_ENTER, "label": "Send Prompt", "mnemonic": "P", "shortcut": "Ctrl+Enter", "handler": _send_prompt, "enabled": _ready, }, "cancel_inference": { "key": KEY_ESC, "label": "Cancel Inference", "mnemonic": "C", "shortcut": "Esc", "handler": _cancel_inference, "enabled": lambda app: app.processing, }, "select_model": { "key": KEY_F4, "label": "Select Model", "mnemonic": "S", "shortcut": "F4", "handler": model_selector_popup, "enabled": _ready, }, "change_workspace": { "key": KEY_F6, "label": "Change Workspace", "mnemonic": "C", "shortcut": "F6", "handler": workspace_popup, "enabled": _ready, }, } # ---------------------------------------------------------------- menu bar --- def _item(action_id: str) -> dict: item = dict(ACTIONS[action_id]) item["id"] = action_id return item # Struktur menu bar. Urutan & mnemonic sesuai spesifikasi: # Alt+S → [S]ession, Alt+A → [A]gent, Alt+M → [M]odel, Alt+W → [W]orkspace MENUS = [ { "name": "Session", "mnemonic": "S", "items": [ _item("new_session"), _item("open_session"), _item("rename_session"), "sep", _item("exit_app"), ], }, { "name": "Agent", "mnemonic": "A", "items": [ _item("send_prompt"), _item("cancel_inference"), ], }, { "name": "Model", "mnemonic": "M", "items": [ _item("select_model"), ], }, { "name": "Workspace", "mnemonic": "W", "items": [ _item("change_workspace"), ], }, ] # ------------------------------------------------------------------ execute --- def perform(app, stdscr, action_id: str): # Jalankan aksi; abaikan jika disabled (mis. aksi READY-only saat processing). spec = ACTIONS.get(action_id) if not spec or not spec["enabled"](app): return spec["handler"](app, stdscr)