Files
darman 420f00494f M0: scaffold Cargo workspace, core types, event bus, permission engine, storage actor
Sets up the Cargo workspace (harness-core, harness-tools, harness-providers, harness-mcp, harness-lsp, harness-app, harness-tui) and the ten architecture docs under docs/. harness-core gets its foundational types (session/message/part/model ids), an in-process event bus, a table-driven permission evaluate function, and a SQLite-backed storage actor with schema and roundtrip coverage. Every crate compiles empty and a bin stub prints its version.
2026-07-10 16:19:27 +02:00

50 lines
2.9 KiB
Markdown

# 07 — TUI
`harness-tui`, binary `harness`. Single ratatui app over the engine; zero business logic — it renders `AppEvent`s and calls `EngineHandle`.
## Layout
```
┌─ header: session title · agent · provider/model ─────────────┐
│ chat viewport (scroll) │
│ user block / assistant markdown / tool cards │
│ (tool card: status icon, title, collapsible output, diff) │
├─ jobs strip when active: ● exp-1 running · ✓ fix-1 done │
├─ input (tui-textarea, multiline, / command completion) │
└─ status: tokens in/out · cost · spinner · keymap hints ──────┘
```
## Event loop
`tokio::select!` over:
1. `crossterm::event::EventStream` (keys, resize)
2. `bus.subscribe()``AppEvent`s
3. 33 ms render tick
Events mutate `AppState { sessions, current: SessionView { messages: Vec<MessageView> }, modal: Option<Modal>, jobs, input }` and set a dirty flag; render at most once per tick.
## Streaming markdown rendering
- `MessageView` caches rendered `Vec<Line>` per part, keyed `(part_id, revision, width)`.
- Finished parts render once through the `pulldown-cmark` → ratatui renderer (headings, bold/italic, fenced code, lists, blockquotes; syntect highlighting optional in M6).
- The in-flight tail part re-renders only itself on `PartDelta`; only the portion after the last blank line gets full markdown treatment — earlier paragraphs of the same part are frozen into the cache. This bounds per-delta work.
- **Risk:** ratatui has no incremental layout. The cache + tail-only strategy is the mitigation; fall back to a plain-text tail if profiling shows stutter.
## Modals
- **Permission dialog** (on `PermissionAsked`): permission key, pattern list, metadata — for edits, a colorized unified diff. Keys: `y` allow once, `a` allow always, `n` reject → `engine.permission_reply(id, …)`. Multiple pending asks queue FIFO.
- **Device-code login** (on `AuthPrompt`): shows user code + verification URL; dismisses when the flow completes.
- **Pickers** (shared fuzzy-filter list widget): sessions `Ctrl+S`, agents (`mode: Primary|All`) `Ctrl+A`, models (grouped by provider) `Ctrl+M`.
- **Jobs pane** `Ctrl+J`: lists `JobRecord`s (alias, agent, state, age, objective); `Enter` drills into the child session view — a read-only live follow of its stream (trivial since all sessions publish to the same bus).
## Keybinds & commands
- `Esc` aborts the running turn / closes modal; `Ctrl+C` twice quits.
- Slash commands: `/model`, `/agent`, `/new`, `/sessions`, `/login <provider>`, `/compact`, plus markdown-defined commands with completion.
## Terminal hygiene
- Logging via `tracing` to `~/.local/share/ai-harness/log/` (the TUI owns the terminal — nothing writes to stdout).
- Panic hook + `Drop` guard restore the terminal (disable raw mode, leave alternate screen).