Files
zesdex/docs/CODEMAPS/frontend.md
T

7.4 KiB

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