4.6 KiB
name, description
| name | description |
|---|---|
| tui-verify | 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:
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
-
Build the thing you're testing (
cargo build --release). -
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.tmux new-session -d -s ccverify -x 100 -y 20 "./target/release/claude-cloak" sleep 2 # give it time to draw the first frame -
Capture the rendered screen as text:
tmux capture-pane -t ccverify -pAdd
-eto include ANSI escapes (then| cat -A) when you need to inspect colors/attributes — e.g. which menu row is highlighted:tmux capture-pane -t ccverify -p -e | cat -A -
Send keystrokes, then
sleepbriefly (the app redraws on its own ~30fps tick — give it 1–2s) and capture again: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 -
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). Sosend-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 literalDown Down Downin 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 withDown,Enterto spawn. It resumes-then-clears to a fresh chat.ctrl-ftoggles 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.