M0: scaffold Cargo workspace, core types, event bus, permission engine, storage actor
Seven-crate workspace per docs/01-architecture.md. harness-core gets the domain types (Session/Message/Part/ToolState), a broadcast EventBus, the last-match-wins wildcard permission evaluator, and a SQLite storage actor (dedicated thread + mpsc, JSON-blob rows) with roundtrip tests. All other crates are compiling stubs. CI runs fmt/clippy -D warnings/test.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user