99 lines
4.6 KiB
Markdown
99 lines
4.6 KiB
Markdown
---
|
||
name: tui-verify
|
||
description: Drive and visually verify this TUI (claude-cloak, or any terminal app / embedded claude pane) by running it inside a headless tmux session, sending real keystrokes, and capturing the rendered screen as text. Use whenever you need to confirm a UI/pane/rendering change actually works end-to-end — not just that tests pass — e.g. "verify the pane expands", "does @ show the menu", "check the layout at a small size". Claude Code's own TUI cannot run in a non-tty, so tmux is the way to give it a real terminal.
|
||
---
|
||
|
||
# Verifying a TUI with tmux
|
||
|
||
The app renders to a real terminal and cannot run in a plain pipe. tmux gives it
|
||
a genuine tty in the background, lets you type into it, and lets you read back
|
||
exactly what's on screen as plain text — so you can *see* a rendering change
|
||
instead of guessing from tests.
|
||
|
||
## The safety rule (read first)
|
||
|
||
**Never use `pkill`, `killall`, `kill -9 <pattern>`, or any broad
|
||
process-matching kill.** The user runs their own real claude-cloak instances
|
||
(including the one hosting your session) on the same machine. `pkill -f
|
||
claude-cloak` will kill all of them. Only ever tear down tmux sessions **you
|
||
created, by their exact name**:
|
||
|
||
```bash
|
||
tmux kill-session -t <your-session-name> # only your own named session
|
||
```
|
||
|
||
Pick a distinctive session name (e.g. `ccverify`) so you never touch anything
|
||
else. Before starting, `tmux ls` to see what's already there and avoid name
|
||
clashes — never kill a session you didn't create.
|
||
|
||
## Core loop
|
||
|
||
1. **Build** the thing you're testing (`cargo build --release`).
|
||
2. **Launch** it in a detached tmux session, sized to the scenario you care
|
||
about. Small sizes (e.g. `-x 100 -y 20`) are what expose layout/overflow
|
||
bugs — a big terminal hides them.
|
||
|
||
```bash
|
||
tmux new-session -d -s ccverify -x 100 -y 20 "./target/release/claude-cloak"
|
||
sleep 2 # give it time to draw the first frame
|
||
```
|
||
|
||
3. **Capture** the rendered screen as text:
|
||
|
||
```bash
|
||
tmux capture-pane -t ccverify -p
|
||
```
|
||
|
||
Add `-e` to include ANSI escapes (then `| cat -A`) when you need to inspect
|
||
colors/attributes — e.g. which menu row is highlighted:
|
||
|
||
```bash
|
||
tmux capture-pane -t ccverify -p -e | cat -A
|
||
```
|
||
|
||
4. **Send keystrokes**, then `sleep` briefly (the app redraws on its own ~30fps
|
||
tick — give it 1–2s) and capture again:
|
||
|
||
```bash
|
||
tmux send-keys -t ccverify "a" # a literal key
|
||
tmux send-keys -t ccverify "Down" "Enter" # named keys
|
||
tmux send-keys -t ccverify "C-u" # ctrl-u (clear line)
|
||
tmux send-keys -t ccverify "@src" # a literal string
|
||
```
|
||
|
||
5. **Tear down your session by name** when done (see safety rule).
|
||
|
||
## send-keys gotchas
|
||
|
||
- Each space-separated token is interpreted as a **key name** (`Down`, `Enter`,
|
||
`Space`, `C-f`, `Escape`). So `send-keys "Down Down Down"` sends three Down
|
||
keys — but if the app has meanwhile changed state (e.g. a menu collapsed to a
|
||
single match and closed), the same tokens can land as literal text. If you see
|
||
literal `Down Down Down` in an input box, that's this — resend as needed.
|
||
- To send a literal string that contains spaces or key-like words, send it as
|
||
one quoted argument and/or use `-l` (literal): `tmux send-keys -l -t s "Down"`.
|
||
- Control/modifier chords: `C-u`, `C-f`, `C-q`, `S-Tab` (shift-tab). This app
|
||
avoids Alt on purpose — don't test Alt chords.
|
||
- After any keystroke, **sleep before capturing.** Captures taken mid-repaint
|
||
show a transient frame (a half-drawn box), which can look like a bug that
|
||
isn't there. 1–1.5s is usually enough.
|
||
|
||
## Reading the claude-cloak screen
|
||
|
||
- The bottom footer line shows the bound proxy port and the active keybinding
|
||
hints — a quick sanity check that the app is alive and in the expected mode.
|
||
- The embedded pane is the lower bordered box titled `claude · <id>`. Its
|
||
compact framing (`src/term.rs`) shows one context row, the input box (bracketed
|
||
by `──── … ──` rules), and either the statusLine (idle) or an `@`/`/` menu.
|
||
- To spawn an embedded claude pane for pane tests: press `a` (model picker),
|
||
navigate with `Down`, `Enter` to spawn. It resumes-then-clears to a fresh
|
||
chat. `ctrl-f` toggles fullscreen (shows the child's raw screen verbatim —
|
||
useful to see what Ink actually drew vs. what we crop).
|
||
|
||
## Why this beats tests alone
|
||
|
||
Unit tests over `compact_frame` / `compact_view_range` pin the framing math, but
|
||
they can't catch a PTY-sizing feedback loop where Ink never draws the rows we'd
|
||
measure — that only shows up when a real `claude` child renders into a real PTY.
|
||
Run the tmux loop for any change to pane sizing, cropping, cursor, or layout.
|