207 lines
9.4 KiB
Markdown
207 lines
9.4 KiB
Markdown
# Owlen TUI UX & Keybinding Playbook
|
||
|
||
*Last updated: October 25, 2025*
|
||
|
||
This playbook documents the design principles, modal ergonomics, command
|
||
metadata, theming tokens, and animation policy that guide Owlen’s terminal UI.
|
||
Use it both as a contributor reference and as a migration guide when extending
|
||
custom keymaps or UI affordances.
|
||
|
||
---
|
||
|
||
## 1. Modal Philosophy
|
||
|
||
Owlen embraces a Vim-inspired modal workflow with discoverability helpers
|
||
layered on top.
|
||
|
||
| Mode | Entry | Primary Purpose | Escape |
|
||
|-----------------|------------------------------------------|----------------------------------------------------------------------|--------|
|
||
| **Normal** | `Esc`, `Ctrl+[`, application start | Navigation, pane switching, command prefixes, leader chords | `i`, `:`, `?`, `F1` |
|
||
| **Editing** | `i`, `a`, `Enter` in Normal | Compose prompt, multi-line editing, history cycling (`Ctrl+↑/↓`) | `Esc` |
|
||
| **Visual** | `v` in Normal | Region selection across chat, thinking, and input panes | `Esc`, `v` |
|
||
| **Command** | `:` | Run palette commands (`:session save`, `:mode code`, `:limits`) | `Esc`, `Enter` |
|
||
| **Help** | `F1`, `?` | Cheat sheet tabs, onboarding tour | `Esc`, `F1` |
|
||
| **Browsers** | `:sessions`, `:themes`, repo search keys | Context-specific pickers with shared navigation primitives | `Esc` |
|
||
|
||
Modal transitions fire `AppEvent::Composer::ModeChanged`, enabling keymap
|
||
state to reset cleanly (see `ChatApp::set_input_mode`). Maintain this flow
|
||
when adding new modes to avoid orphaned key sequences.
|
||
|
||
### Focus Shortcuts
|
||
|
||
- `Ctrl+1…5` focus Files, Chat, Code, Thinking, Input (mirrors leader chords).
|
||
- `Tab` / `Shift+Tab` cycle panes; `g t` toggles Files visibility in Vim keymap.
|
||
- `Ctrl+Alt+5` (Vim) / `Alt+5` (Emacs) jump back to the input editor.
|
||
|
||
Keep focus hops single-chord and mnemonic. Prefer documenting any new shortcut
|
||
in the cheat sheet (`render_guidance_cheatsheet`) and the status HUD hints.
|
||
|
||
---
|
||
|
||
## 2. Keybinding Design & Migration
|
||
|
||
### Built-in Profiles
|
||
|
||
- **Vim** (default): stored in [`crates/owlen-tui/keymap.toml`](../crates/owlen-tui/keymap.toml).
|
||
- **Emacs**: stored in [`crates/owlen-tui/keymap_emacs.toml`](../crates/owlen-tui/keymap_emacs.toml).
|
||
|
||
Switch at runtime via `:keymap vim|emacs`; persist with
|
||
`ui.keymap_profile = "vim"` or `"emacs"` in `config.toml`.
|
||
|
||
### Custom Keymaps
|
||
|
||
Set `ui.keymap_path = "/absolute/path/to/keymap.toml"` to override the built-in
|
||
profiles. The schema matches the shipped TOML files:
|
||
|
||
```toml
|
||
[[bindings]]
|
||
mode = "normal"
|
||
sequence = ["g", "t"]
|
||
command = "workspace.toggle_files"
|
||
```
|
||
|
||
**Migration notes (v0.2+)**
|
||
|
||
1. **Multi-step sequences**: All keymaps now support arbitrary-length
|
||
sequences (`Ctrl+W`, `w`, `Ctrl+K`, `←`). Update custom maps to the trie
|
||
syntax if you previously shadowed hard-coded Rust handlers.
|
||
2. **Leader remapping**: `ui.keymap_leader` defaults to space. Custom profiles
|
||
should mirror the new leader sections so the cheat sheet can surface
|
||
alternate hints (`binding_pair_string`).
|
||
3. **Discoverability hooks**: Register new commands in
|
||
`commands::catalog()` with category, summary, tags, and optional
|
||
`preview` callback. The command palette relies on this metadata for fuzzy
|
||
search and tag filtering.
|
||
4. **Help overlay**: If you introduce new focus or layout commands, add them to
|
||
`build_cheatsheet_sections` so onboarding and help stay in sync.
|
||
|
||
Run `cargo test -p owlen-tui --test chat_snapshots` after keymap changes to
|
||
refresh guidance snapshots.
|
||
|
||
---
|
||
|
||
## 3. Command Metadata Schema
|
||
|
||
Located in [`crates/owlen-tui/src/commands/catalog.rs`](../crates/owlen-tui/src/commands/catalog.rs),
|
||
each command descriptor should populate:
|
||
|
||
- `category`: High-level grouping shown in the palette (`navigation`, `session`, `provider`).
|
||
- `keywords`: Fuzzy aliases; include verbs (open, focus), nouns (chat, files), and tags (`#agent`).
|
||
- `key_hint`: Optional string rendered in previews (e.g., `Ctrl+Shift+F`).
|
||
- `modes`: Restrict availability by `InputMode` when needed (e.g., editing-only commands).
|
||
- `preview`: Use for sidecar previews (`commands::Preview`), returning lines that explain impact.
|
||
|
||
When adding new commands:
|
||
|
||
1. Define the handler in `commands::handlers`.
|
||
2. Register metadata in `catalog()` and supply tests in
|
||
`crates/owlen-tui/tests/state_tests.rs` or palette-specific snapshots.
|
||
3. Update the cheat sheet if the command introduces a new focus or mode affordance.
|
||
|
||
---
|
||
|
||
## 4. Theming & Layer Tokens
|
||
|
||
Themes live in `owlen_core::theme`. The TUI applies a glass/neon layer via
|
||
`GlassPalette::for_theme_with_mode` using `config.ui.layers`:
|
||
|
||
| Setting | Default | Description |
|
||
|--------------------------|---------|----------------------------------------------------------|
|
||
| `shadow_elevation` | `2` | Depth of drop shadow around the chat stage. |
|
||
| `glass_tint` | `0.82` | Opacity mix between base theme background and frosted layer. |
|
||
| `neon_intensity` | `60` | Percentage controlling accent saturation. |
|
||
| `focus_ring` | `true` | Draws neon outlines around the active stage container. |
|
||
|
||
**Best practices**
|
||
|
||
- Keep contrasts AA+; avoid low-alpha custom tints when `accessibility_high_contrast = true`.
|
||
- Re-render custom palettes through the snapshot suite to catch regression diffs.
|
||
|
||
---
|
||
|
||
## 5. Animation Policy
|
||
|
||
Controlled by `config.ui.animations`:
|
||
|
||
| Field | Effect | Range |
|
||
|--------------------|--------------------------------------------------|------------|
|
||
| `micro` | Enables pane glow, gauge easing, mode flash. | `true/false` |
|
||
| `gauge_smoothing` | Exponential smoothing factor for usage gauges. | `0.05–1.0` |
|
||
| `pane_decay` | Decay applied to pulse effects on pane switches. | `0.2–0.95` |
|
||
|
||
Accessibility flags (`ui.accessibility.high_contrast`, `ui.accessibility.reduced_chrome`) automatically disable micro animations. When adding new animations:
|
||
|
||
1. Guard with `ChatApp::micro_animations_enabled()`.
|
||
2. Provide non-animated fallbacks (e.g., direct gauge `snap`).
|
||
3. Update documentation and consider adding snapshot coverage under both
|
||
animated and non-animated configurations.
|
||
|
||
---
|
||
|
||
## 6. Status Surface & Toasts
|
||
|
||
The status HUD (`render_status`) now renders three columns:
|
||
|
||
1. **Left**: Mode/operating badges, focus hints, agent state, help shortcuts.
|
||
2. **Center**: Primary status message with auto-leveled badge (info/success/warning/error) and contextual usage gauges.
|
||
3. **Right**: Repository/path summary, provider & model badge, streaming hints, and toast history.
|
||
|
||
Toast enhancements:
|
||
|
||
- Icons per severity (`ⓘ`, `✔`, `⚠`, `✖`).
|
||
- Optional keyboard hints via `push_toast_with_hint`.
|
||
- Progress bar countdown plus remaining seconds.
|
||
- Persistent history (20 entries) exposed to the HUD for quick recall.
|
||
|
||
**Guidelines**
|
||
|
||
- Provide actionable hints (e.g., `"F12 · Debug log"`, `":limits"`).
|
||
- Reserve `ToastLevel::Error` for recoverable issues the user must address.
|
||
- Use status primary line for the most recent action; employ `system_status`
|
||
for secondary context.
|
||
|
||
---
|
||
|
||
## 7. Contributor Checklist
|
||
|
||
When modifying TUI UX or keybindings:
|
||
|
||
1. Update this playbook if the change affects modal ergonomics, theming tokens,
|
||
animation policy, or keymap conventions.
|
||
2. Extend the cheat sheet / onboarding overlays for new focus or command
|
||
shortcuts.
|
||
3. Refresh snapshots: `INSTA_UPDATE=always cargo test -p owlen-tui --test chat_snapshots`.
|
||
4. Run `cargo clippy -p owlen-tui -- -D warnings` and the `owlen-tui` test suite.
|
||
5. Document user-facing changes in `CHANGELOG.md`.
|
||
|
||
Keeping these steps in sync ensures Owlen’s keyboard-first UX remains
|
||
predictable, accessible, and discoverable.
|
||
|
||
## 8. Screenshot Pipeline
|
||
|
||
Use the scripted pipeline to regenerate gallery assets and documentation
|
||
illustrations:
|
||
|
||
- `cargo xtask screenshots` emits ANSI dumps under `images/generated` and, by
|
||
default, converts them to PNG with `chafa`. Pass `--no-png` to skip PNG
|
||
rendering or `--chafa /path/to/chafa` to override the binary. Each scene is
|
||
deterministic—no network calls—and mirrors the regression snapshots.
|
||
- The pipeline reuses the same stub provider harness as the snapshot tests, so
|
||
new scenes should be added in tandem with `chat_snapshots.rs` to keep visual
|
||
regression coverage and documentation imagery aligned.
|
||
|
||
## 9. Transcript Compression
|
||
|
||
- The compactor lives under `[chat]` in `config.toml`. Defaults keep
|
||
`auto_compress = true`, `trigger_tokens = 6000`, and retain the last eight
|
||
turns verbatim.
|
||
- Strategy is configurable: `provider` summaries call back through the active
|
||
model (or `chat.model_override`), while `local` uses a heuristic bullet list
|
||
for fully offline runs.
|
||
- Users can disable the feature per session with `owlen --no-auto-compress`, or
|
||
at runtime via `:compress auto on|off`. `:compress now` triggers an immediate
|
||
compaction even when auto mode is disabled.
|
||
- Each compression pass replaces older turns with a system summary annotated by
|
||
`message.metadata.compression` (strategy, timestamps, token deltas, and the
|
||
archived message ids) to support audits and future rehydration tools.
|