v0.9.0 e5e4ded

ui-driver.md RawHistoryFolder

UI driver

vev --ui-driver lets a script or test read the rendered screen and send keys, over JSON Lines on stdin/stdout. It uses the normal vev client, so what you capture is what a user sees.

Start

vev --ui-driver                      # new numbered session, 80x24
vev --ui-driver --session work       # named local session
vev --ui-driver --remote user@host   # remote session on a configured host
vev --ui-driver --picker             # start in the session picker
Option Meaning
--session NAME create that named session (with --remote: open that remote session)
--remote HOST target a configured remote host
--cols N --rows N viewport size, max 512×256
--picker start in the picker (not with --session/--remote)
--socket PATH connect to an existing client instead (see below)

The driver uses your normal broker and daemon. It never starts, stops, or reconfigures them. Closing stdin detaches; sessions keep running.

Protocol

The first line is the discovery response:

{"version":1,"id":0,"result":{"attachment":"<handle>","generation":0,"control":true,"status":"picker"}}

status is one of:

Status Meaning Keys accepted?
picker in the local picker no
connecting attaching no
attached attached to a session yes

Wait for attached, then use the generation it reports for input. Never assume generation == 1.

  • A run that asked for a session stays connecting while it retries, even if the broker is missing. It never shows the picker first.
  • picker appears for --picker runs, a refused destination, or a failure that retrying cannot fix, with a short notice.
  • capture and wait keep working in every state.

Every request has version, a nonzero id, op, and attachment:

{"version":1,"id":1,"op":"capture","attachment":"<handle>","format":"both"}
{"version":1,"id":2,"op":"text","attachment":"<handle>","generation":1,"text":"printf 'driver ok'"}
{"version":1,"id":3,"op":"keys","attachment":"<handle>","generation":1,"keys":["Enter"]}
{"version":1,"id":4,"op":"wait","attachment":"<handle>","after_action":3,"expect":{"text_contains":"driver ok"}}

Operations

  • capture: format is text (default), cells, or both. Returns one string per row, plus cursor, geometry, and focus.
  • text: literal printable UTF-8. No control characters; use keys for Enter or Tab.
  • keys: Enter, Escape, Tab, Backspace, Up, Down, Left, Right, Home, End, PageUp, PageDown, Space, one printable ASCII character, Ctrl+<char>, or Alt+<char>.
  • wait: waits until all conditions match. Conditions: text_contains, session, focus, status. With after_action, only screens after that action count.

Completion

  • An action is processed when vev has handled and rendered it. That does not mean the shell has finished running the command: use wait for that.
  • If a request times out after the input was accepted, the reply has accepted:true and an action_id. The input is not undone. Use wait with after_action to follow up.

Limits

Limit Value
Request size 64 KiB
Input per request 16 KiB, 1–256 keys
Request timeout 5 s default, 30 s max
Connections per attachment 4
Active waits 4

Error codes include invalid_request, stale_attachment, unavailable, timeout, outcome_unknown, capture_too_large, input_busy, and navigation_failed. Errors never contain screen content or credentials.

Observe an interactive client

Observation is off by default. Enable it when you attach:

vev --ui-observe attach work    # read-only
vev --ui-control new work       # read and send input

The client prints a private socket path to stderr (or use --ui-socket /abs/path). Connect to it:

vev --ui-driver --socket /abs/path/ui.sock
  • The socket is private (0600), and on Linux only your user can connect.
  • --ui-observe rejects keys and text.
  • Observers never resize, redraw, or slow down the real terminal. Disconnecting leaves the client running.

Not supported: image capture, mouse, paste, resize commands, and TCP access.

source
1# UI driver
2
3`vev --ui-driver` lets a script or test read the rendered screen and send keys, over JSON Lines on stdin/stdout. It uses the normal vev client, so what you capture is what a user sees.
4
5## Start
6
7```sh
8vev --ui-driver                      # new numbered session, 80x24
9vev --ui-driver --session work       # named local session
10vev --ui-driver --remote user@host   # remote session on a configured host
11vev --ui-driver --picker             # start in the session picker
12```
13
14| Option | Meaning |
15|---|---|
16| `--session NAME` | create that named session (with `--remote`: open that remote session) |
17| `--remote HOST` | target a configured remote host |
18| `--cols N --rows N` | viewport size, max 512×256 |
19| `--picker` | start in the picker (not with `--session`/`--remote`) |
20| `--socket PATH` | connect to an existing client instead (see below) |
21
22The driver uses your normal broker and daemon. It never starts, stops, or reconfigures them. Closing stdin detaches; sessions keep running.
23
24## Protocol
25
26The first line is the discovery response:
27
28```json
29{"version":1,"id":0,"result":{"attachment":"<handle>","generation":0,"control":true,"status":"picker"}}
30```
31
32`status` is one of:
33
34| Status | Meaning | Keys accepted? |
35|---|---|---|
36| `picker` | in the local picker | no |
37| `connecting` | attaching | no |
38| `attached` | attached to a session | yes |
39
40Wait for `attached`, then use the `generation` it reports for input. Never assume `generation == 1`.
41
42- A run that asked for a session stays `connecting` while it retries, even if the broker is missing. It never shows the picker first.
43- `picker` appears for `--picker` runs, a refused destination, or a failure that retrying cannot fix, with a short notice.
44- `capture` and `wait` keep working in every state.
45
46Every request has `version`, a nonzero `id`, `op`, and `attachment`:
47
48```json
49{"version":1,"id":1,"op":"capture","attachment":"<handle>","format":"both"}
50{"version":1,"id":2,"op":"text","attachment":"<handle>","generation":1,"text":"printf 'driver ok'"}
51{"version":1,"id":3,"op":"keys","attachment":"<handle>","generation":1,"keys":["Enter"]}
52{"version":1,"id":4,"op":"wait","attachment":"<handle>","after_action":3,"expect":{"text_contains":"driver ok"}}
53```
54
55### Operations
56
57- **`capture`**: `format` is `text` (default), `cells`, or `both`. Returns one string per row, plus cursor, geometry, and focus.
58- **`text`**: literal printable UTF-8. No control characters; use `keys` for Enter or Tab.
59- **`keys`**: `Enter`, `Escape`, `Tab`, `Backspace`, `Up`, `Down`, `Left`, `Right`, `Home`, `End`, `PageUp`, `PageDown`, `Space`, one printable ASCII character, `Ctrl+<char>`, or `Alt+<char>`.
60- **`wait`**: waits until all conditions match. Conditions: `text_contains`, `session`, `focus`, `status`. With `after_action`, only screens after that action count.
61
62### Completion
63
64- An action is `processed` when vev has handled and rendered it. That does not mean the shell has finished running the command: use `wait` for that.
65- If a request times out after the input was accepted, the reply has `accepted:true` and an `action_id`. The input is not undone. Use `wait` with `after_action` to follow up.
66
67## Limits
68
69| Limit | Value |
70|---|---|
71| Request size | 64 KiB |
72| Input per request | 16 KiB, 1–256 keys |
73| Request timeout | 5 s default, 30 s max |
74| Connections per attachment | 4 |
75| Active waits | 4 |
76
77Error codes include `invalid_request`, `stale_attachment`, `unavailable`, `timeout`, `outcome_unknown`, `capture_too_large`, `input_busy`, and `navigation_failed`. Errors never contain screen content or credentials.
78
79## Observe an interactive client
80
81Observation is off by default. Enable it when you attach:
82
83```sh
84vev --ui-observe attach work    # read-only
85vev --ui-control new work       # read and send input
86```
87
88The client prints a private socket path to stderr (or use `--ui-socket /abs/path`). Connect to it:
89
90```sh
91vev --ui-driver --socket /abs/path/ui.sock
92```
93
94- The socket is private (`0600`), and on Linux only your user can connect.
95- `--ui-observe` rejects `keys` and `text`.
96- Observers never resize, redraw, or slow down the real terminal. Disconnecting leaves the client running.
97
98Not supported: image capture, mouse, paste, resize commands, and TCP access.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close