Files
zesdex/docs/CODEMAPS/frontend.md
T

167 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TUI (Terminal User Interface)
Dibangun di atas **ratatui** + **crossterm**. Kode ada di `apps/interfaces/tui/src/`.
## Struktur Source
```
apps/interfaces/tui/src/
├── run.rs # Event loop utama
├── state.rs # AppStateRest — single source of truth
├── action.rs # apply_action(): satu-satunya mutator state
├── turn.rs # Spawn agent turn di background thread
├── lib.rs # Re-export publik
├── controller/
│ ├── input.rs # Key handler → Vec<Action>
│ └── command.rs # Slash command parser
├── view/
│ ├── mod.rs # Layout + pre_render() + draw()
│ ├── chat.rs # Chat transcript panel (dengan display cache)
│ ├── sidebar.rs # Sidebar: workflow, tasks, usage
│ ├── status.rs # Status bar satu baris
│ ├── markdown.rs # Markdown → styled Span (pulldown-cmark)
│ ├── workflow.rs # Workflow/hive-mind progress panel
│ ├── theme.rs # Tokyo Night color palette (const)
│ └── overlays/ # 16 overlay panel
└── model/ # Data model lokal TUI
```
## Render Pipeline (Per Frame)
```
run_loop_inner() [50ms in-flight / 200ms idle]
├── drain expired toasts (1x, bukan 2x)
├── if dirty:
│ view::pre_render(&mut state) ← update cache (markdown, token count)
│ terminal.draw(|f| view::draw(f, &state))
│ state.dirty = false
└── poll events → apply_action → Action::Tick
```
### Optimasi Performa
| Masalah lama | Solusi saat ini |
|---|---|
| `count_tokens` (tiktoken) setiap frame | Cache `cached_token_count`, update hanya saat pesan baru |
| `render_markdown` ulang setiap frame | `display_lines_cache` di `AppStateRest`, rebuild saat `transcript_cache.dirty` |
| `Vec::remove(0)` untuk evict pesan lama | `VecDeque::pop_front()` — O(1) |
| `Mutex<bool>` untuk `turn_in_flight` | `Arc<AtomicBool>` — lock-free |
| Render terus meski idle | Skip `terminal.draw()` jika `dirty == false` |
| Poll 50ms konstan | Adaptif: 50ms saat in-flight, 200ms saat idle |
| `drain_expired_toasts` 2x per iterasi | Sekali saja di `run_loop_inner` |
## State (AppStateRest)
`AppStateRest` di `state.rs` adalah satu-satunya sumber kebenaran TUI:
```
AppStateRest {
settings: Settings // provider, model, dll
app_config: AppConfig // endpoint, env vars
workspace_roots: Vec<PathBuf> // working directories
session_dir / session_id // path sesi aktif
memory_dir // direktori memory
session_runtime: Option<SessionRuntime> // history pesan, usage stats
transcript_cache: TranscriptCache // VecDeque<ChatMessageDisplay>
scroll: ScrollState // offset scroll pane chat
input: InputState // buffer, cursor, history, autocomplete
misc: MiscState // overlay aktif, toasts, flags
turn_events: Arc<Mutex<VecDeque<TurnEvent>>> // queue event dari agent
turn_in_flight_flag: Arc<AtomicBool> // apakah agent sedang jalan
abort_flag: Arc<AtomicBool> // sinyal abort oleh user
// Cache performa
display_lines_cache: Vec<Line<'static>> // hasil render markdown
cached_token_count: usize // token count terkini
token_count_dirty: bool // perlu hitung ulang?
last_render_width: u16 // lebar terminal saat render terakhir
dirty: bool // perlu render ulang?
quit: bool // keluar dari loop?
}
```
**Aturan mutasi:**
- Dimutasi hanya dari `action.rs::apply_action()` dan `run.rs` (untuk dirty/quit)
- Semua fungsi `view/*` bersifat read-only terhadap state
- `pre_render_chat()` boleh mutasi hanya field cache (`display_lines_cache`, `cached_token_count`, `token_count_dirty`)
## Input & Actions
`controller/input.rs::handle_key()``Vec<Action>``apply_action(&mut state, action)`
Semua mutasi state melewati satu titik: `apply_action`. Controller tidak tahu *bagaimana* state diubah, hanya *action apa* yang dihasilkan.
### Action Utama
| Action | Efek |
|--------|------|
| `SubmitInput(text)` | Push ke transcript, spawn agent turn |
| `Tick` | Drain `TurnEvent` queue, update state dari hasil agent |
| `ScrollUp/Down` | Ubah `scroll.offset` |
| `OpenOverlay(v)` | Set `misc.overlay = v` |
| `Resize(w, h)` | Invalidasi cache display, set `last_render_width` |
| `AbortTurn` | Store `true` ke `abort_flag` |
| `ForceQuit` | Set `quit = true` |
## Overlays (16 Panel)
| Overlay | File | Fungsi |
|---------|------|--------|
| `Help` | `overlays/help.rs` | Daftar shortcut keyboard |
| `Settings` | `overlays/settings.rs` | Panel pengaturan |
| `Bash` | `overlays/bash.rs` | Background shell jobs |
| `QuitConfirm` | `overlays/quit_confirm.rs` | Konfirmasi keluar |
| `KeyInput` | `overlays/key_input.rs` | Capture key binding |
| `Editor` | `overlays/editor.rs` | File editor inline |
| `Effort` | `overlays/effort.rs` | Pilih level reasoning LLM |
| `Mcp` | `overlays/mcp.rs` | Manajemen MCP server |
| `Todo` | `overlays/todo.rs` | Daftar TODO |
| `Rewind` | `overlays/rewind.rs` | Navigasi history pesan |
| `Learning` | `overlays/learning.rs` | Viewer lesson |
| `Usage` | `overlays/usage.rs` | Statistik token |
| `Loading` | `overlays/loading.rs` | Spinner generik |
| `ModelSelector` | `overlays/model_selector.rs` | Pilih model LLM |
| `ClearConfirm` | `overlays/clear_confirm.rs` | Konfirmasi clear chat |
## Layout Terminal
```
┌───────────────────────────────────────────────┐
│ │
│ Chat Transcript Sidebar (≥90) │
│ (view/chat.rs) ┌────────────┐ │
│ VecDeque messages │ Workflow │ │
│ + markdown cache ├────────────┤ │
│ scrollable │ Tasks │ │
│ ├────────────┤ │
│ │ Usage │ │
│ └────────────┘ │
├───────────────────────────────────────────────┤
Input Bar + Autocomplete dropdown │
├───────────────────────────────────────────────┤
│ ⚡zesdex READY │ ...center... │ tok · model │
└───────────────────────────────────────────────┘
```
Sidebar hanya tampil jika lebar terminal ≥ 90 kolom.
## Theme
`view/theme.rs` mendefinisikan palette **Tokyo Night** sebagai `const Color`:
`PRIMARY`, `BG`, `SURFACE`, `SURFACE_ELEVATED`, `BORDER`, `TEXT`, `TEXT_DIM`, `TEXT_MUTED`, `SUCCESS`, `WARNING`, `ERROR`, `INFO`, `HIGHLIGHT`, `CODE_BG`, dll.
## Markdown Rendering
`view/markdown.rs::render_markdown(text, width, dim)`:
- Parse dengan `pulldown-cmark`
- Hasilkan `Vec<Span<'static>>` dengan styling
- Support: heading, code block, diff block (warna +/-/@@), list, blockquote, table, inline code, link
- `dim=true` → semua span memakai `TEXT_DIM` + italic (untuk tool output)
- Hasil di-cache di `AppStateRest::display_lines_cache`