detached abde31c

docs(readme): rewrite README, split reference docs

Lead with vev's strengths (resilient UDP remote attach, server-side
rendering, near-stdlib dependency footprint, perf harness) and move
the configuration and terminal reference material to docs/.

Fix inaccuracies found while verifying against the code: bare vev
always creates an ephemeral session, kill --daemon behaves like
--all on the wire, code.rename-tab=RNT was missing from the config
example, and the offline recovery state re-runs the SSH bootstrap
instead of resuming over SSH stdio. Document the default UDP port
range in remote-resilience.md and the AZERTY top-row aliases.
commit
abde31ced0b1e1666472adc3bebe10506c644d77
author
brice · · 2026-07-14 14:11 UTC
parent
20dcfd4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +139 -216
M README.md +39 -214
@@ -1,251 +1,76 @@
11 # vev
2-***Norwegian: web, weave, tissue***
32
4-There are many terminal multiplexers, but this one is mine. Minimal aesthetic, purely focused on performance, rendering high-output of vertical text, agentic coding, and remote work over SSH. Built in Go with no external deps as much as possible.
3+*Norwegian: to weave*
54
6-> [!IMPORTANT]
7-> This is totally in alpha.
5+A terminal multiplexer for Linux. One Go binary, a per-user daemon, a thin client.
86
9-## Install
7+> [!WARNING]
8+> Alpha software, Linux only. Expect breaking changes.
109
11-```sh
12-go install github.com/bnema/vev@latest
13-```
10+What it is built around:
1411
15-From a checkout:
12+- **Remote attach that survives bad networks.** SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. Inspired by mosh, with its own protocol.
13+- **Server-side rendering.** The daemon keeps a full VT screen per pane and sends minimal diffs to the client, so high-output programs (agents, build logs) stay smooth locally and over SSH.
14+- **Nothing but the standard library.** VT emulation, renderer, wire protocol, and UDP transport are all in this repo. The only runtime dependencies are `golang.org/x/sys` and `golang.org/x/term`.
15+- **Performance treated as a feature.** A reproducible harness measures 252 scenarios across workloads and transports, including lossy and high-latency UDP.
16+
17+## Install
1618
1719 ```sh
18-make install # install vev and default bar scripts to Go's bin directory
19-go build -o vev . # local binary
20+go install github.com/bnema/vev@latest
2021 ```
2122
22-`make install` places `vev-bar-top-right` and `vev-bar-bottom-right` beside the installed `vev` binary. If you install another way, copy the scripts from `scripts/` into a directory on `PATH` or replace the bar commands in config.
23+From a checkout, `make install` also installs the default status bar scripts.
2324
2425 ## Usage
2526
2627 ```text
27-vev attach to (or create) an ephemeral session
28-vev new <name> create and attach to a named session
29-vev attach <name> attach to an existing session (alias: a)
30-vev attach user@host[:session] attach to a remote vev daemon over SSH
31-vev ls list sessions (alias: list)
32-vev kill <name> kill a session
33-vev kill -- <name> kill a name that starts with -
34-vev kill --all kill all sessions and stop the daemon
35-vev kill --daemon stop the active vev daemon
36-vev --help show help (aliases: -h, help)
37-vev --version show version (alias: version)
38-```
39-
40-Ephemeral sessions get a number (`0`, `1`, `2`, ...) and are temporary daemon-retained sessions. They survive detach and transient client connection loss while the daemon is running, but they are not persisted to disk and disappear when the daemon is killed, exits, or the machine restarts. Named sessions are persisted, survive daemon restarts, and can be resumed later. New and renamed session names must start with a letter or number and may contain only letters, numbers, dots, underscores, and dashes.
41-
42-The daemon starts on first use and exits when no sessions remain. Only one client is attached to a session at a time; attaching again displaces the old client.
43-
44-## Configuration
45-
46-vev reads `$XDG_CONFIG_HOME/vev/config`, or `~/.config/vev/config` when `XDG_CONFIG_HOME` is unset. Missing files use defaults. The daemon reloads mtime/size changes while running, polling about every two seconds.
47-
48-```text
49-# Theme: auto follows the client; dark/light force built-in palettes.
50-theme = auto
51-
52-# Palette placement: auto, center, top-left, top, top-right, left, right,
53-# bottom-left, bottom, or bottom-right.
54-palette.anchor = auto
55-
56-# One prewarmed floating terminal per tab. An empty command uses the normal shell.
57-floating.command =
58-floating.width = 80%
59-floating.height = 80%
60-
61-# Include the focused pane's OSC terminal title in tab labels (top bar and
62-# session picker). The process name always shows; off keeps only that.
63-tabs.terminal-title = on
64-
65-# Rebindable actions. Leave a line out to keep its built-in binding.
66-open-palette = alt+space
67-toggle-floating-pane = alt+f
68-jump-attention = alt+a
69-focus-pane-left = alt+h
70-focus-pane-right = alt+l
71-focus-pane-up = alt+k
72-focus-pane-down = alt+j
73-switch-tab-1 = alt+1
74-switch-tab-2 = alt+2
75-switch-tab-3 = alt+3
76-switch-tab-4 = alt+4
77-switch-tab-5 = alt+5
78-switch-tab-6 = alt+6
79-switch-tab-7 = alt+7
80-switch-tab-8 = alt+8
81-switch-tab-9 = alt+9
82-
83-# Named session snapshots restore layout, cwd, visible content, scrollback,
84-# and foreground processes from this allowlist. Omit the key for these defaults;
85-# set it empty to disable process relaunch.
86-snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode
87-
88-# Right bar anchors. Empty command values disable the matching anchor.
89-bar.top-right = vev-bar-top-right
90-bar.bottom-right = vev-bar-bottom-right
91-bar.interval = 5s
92-
93-# Command palette codes are trimmed, uppercased, then checked as 2-3 letters or digits.
94-code.new-tab = CNT
95-code.new-session = CNS
96-code.close-tab = CLT
97-code.split-right = SPR
98-code.split-left = SPL
99-code.split-up = SPU
100-code.split-down = SPD
101-code.stack-pane = STP
102-code.toggle-stack = TST
103-code.close-pane = CLP
104-code.focus-pane-left = FPL
105-code.focus-pane-right = FPR
106-code.focus-pane-up = FPU
107-code.focus-pane-down = FPD
108-code.next-tab = NXT
109-code.previous-tab = PVT
110-code.back-session = BSK
111-code.jump-recent-session = JRS
112-code.session-picker = SSP
113-code.visual-mode = VIS
114-code.toggle-floating-pane = FLT
115-code.rename-session = RNS
116-code.detach = DET
117-```
118-
119-Configuring an action replaces all of its built-in aliases. In the example above, the focus-pane lines keep Alt+h/j/k/l but not the Alt+Arrow aliases; omit those lines to keep all built-ins.
120-
121-Key specs support `alt+<char>`, `alt+space`, and `alt+left/right/up/down`; digit key specs support `alt+1` through `alt+9`, not `alt+0`. `jump-attention` first opens the oldest attention tab in the current session; if none exists, it opens the oldest attention tab in another session. Invalid entries are logged as warnings and skipped where possible; duplicate config keys use the last value, binding conflicts keep the later action's defaults, and command-code conflicts drop the conflicting override.
122-
123-`floating.command` runs through the normal shell; leave it empty to launch that shell directly. `floating.width` and `floating.height` require exact percentages from `1%` through `100%`; invalid values log a warning and leave the last accepted value (or the `80%` default) intact. Reloading does not relaunch or immediately alter an installed floating terminal: a changed command is used on its next launch, while changed dimensions take effect when that terminal is next shown or resized. Only configuration—not floating runtime state—is restored after a daemon restart; restored tabs stay cold until first visited.
124-
125-`tabs.terminal-title` accepts `on` or `off` and defaults to `on`; invalid values log a warning and keep the last accepted value. Reload applies a changed value live.
126-
127-### Command palette anchoring
128-
129-`palette.anchor` accepts `auto` or the nine anchors `center`, `top-left`, `top`, `top-right`, `left`, `right`, `bottom-left`, `bottom`, and `bottom-right`. Values are trimmed and case-insensitive. The default is `auto`: palettes use the bottom shelf when the terminal is narrower than 96 columns, and a 64-column bottom-right rail at 96 columns or wider. An explicit anchor changes placement only; palette query and selection are preserved. Invalid values are warned about and leave the last valid value unchanged, as do duplicate entries until a valid replacement is supplied (the last valid value wins). Configuration reload applies a changed anchor live and repositions an open palette without resetting its query or selection.
130-
131-### Bar right anchors
132-
133-The top and bottom bar right anchors run configurable commands on the daemon host. By default, `bar.top-right = vev-bar-top-right` prints CPU, memory, then hour text (for example `C 12% M 48% 09:41`), and `bar.bottom-right = vev-bar-bottom-right` prints compact git state for the focused pane's `VEV_PANE_CWD`. Set either value empty to disable that anchor, or set it to another command to replace it.
134-
135-Both anchors share `bar.interval`, which defaults to `5s`; values below `1s` are clamped to `1s`. vev also refreshes around attach, resize, focus, and copy-feedback changes. Commands are bounded by a timeout and output limit. On failure or timeout, vev keeps the last good value; if there is none, the anchor is empty.
136-
137-Scripts receive these environment variables when available: `VEV_ANCHOR` (`top-right` or `bottom-right`), `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`. vev reads stdout only, uses the first line only, treats it as plain UTF-8 display text, and strips ANSI escape sequences and other control characters. Unicode and Nerd Font glyphs are supported. The v1 API does not support colors, styles, JSON, streaming output, or per-anchor intervals. The default git script runs `git status --porcelain` each refresh, so increase `bar.interval` or replace the command if that is too expensive for a large repository.
138-
139-## Remote attach
140-
141-```sh
142-vev attach user@host
143-vev attach user@host:session
28+vev create an ephemeral session
29+vev new <name> create a named session
30+vev attach <name> attach to a session (alias: a)
31+vev attach user@host[:session] attach to a remote daemon over SSH
32+vev ls list sessions
33+vev kill <name> kill a session (--all kills everything)
14434 ```
14535
146-Remote attach uses SSH only to start a UDP proxy on the remote host; the session itself then talks directly to the remote host over UDP. `vev` must be installed on the remote host, and the host name you pass to `vev attach` must be reachable both by SSH for bootstrap and by UDP for the session transport. Omitting `:session` opens an ephemeral remote session. Remote sessions can resume after temporary network disconnects, such as sleep or Wi-Fi changes, while the daemon still has the session. Named sessions additionally persist across daemon restarts.
147-
148-vev's resilient remote attach work is inspired by [mosh](https://mosh.org/)'s mobile-shell approach to UDP-based recovery across flaky networks. vev uses its own wire protocol and implementation; it is not compatible with mosh clients or servers and does not copy mosh protocol code.
149-
150-The default remote transport uses UDP. Its proxy binds a port in a fixed range on the remote (default `61000-61023`), so a host firewall that only allows SSH will drop the session datagrams and attach fails with `remote UDP transport unavailable: probe UDP transport`. Allow inbound UDP for that range on the remote to fix it — for example `sudo ufw allow 61000:61023/udp`, or on a trusted Tailscale network `sudo ufw allow in on tailscale0`. Override the range with `VEV_UDP_PORT_RANGE` on the remote (e.g. `VEV_UDP_PORT_RANGE=51000-51009`, a single `VEV_UDP_PORT_RANGE=51000`, or `VEV_UDP_PORT_RANGE=0` for a random ephemeral port).
151-
152-Set `VEV_REMOTE_TRANSPORT=stdio` to use SSH stdio compatibility mode on networks where direct UDP is blocked or the SSH target is only reachable through jump-host or SSH-config-only routing. Stdio mode keeps all traffic inside SSH and does not require inbound UDP, but it may not notice sleep or Wi-Fi loss promptly. See `docs/remote-resilience.md` for concise operational notes.
153-
154-## Terminal color
155-
156-When the attaching client reports truecolor support, vev advertises newly spawned panes as `TERM=xterm-direct` and exports `COLORTERM=truecolor` to child processes. Otherwise, panes use the conservative `TERM=xterm-256color` and do not receive `COLORTERM`. This works for local and remote attach because the client capability is carried inside vev's protocol rather than relying on SSH `SendEnv`/`AcceptEnv`.
157-
158-Applications that use terminfo can detect direct color from `xterm-direct`; applications that use the common environment convention can use `COLORTERM=truecolor` when it is present. Panes restored from a saved snapshot start conservatively before any client attaches; after attach or resume, newly spawned panes use that client's reported capability.
159-
160-## Terminal compatibility
161-
162-vev keeps a server-side VT screen for each pane. It tracks scroll regions with DECOM origin mode, ANSI insert mode, cursor-position and mode-query reports, alternate-screen state, bracketed paste, mouse tracking, synchronized updates, OSC 9/777 notifications, OSC 52 clipboard set requests, and double-width cells. Resize and edit operations repair double-width cells so CJK or emoji glyph halves are not left behind.
36+The daemon starts on first use and exits with the last session. Ephemeral sessions are numbered, survive detach, and disappear with the daemon. Named sessions persist across daemon restarts and come back with their layout, scrollback, and allowlisted processes.
16337
16438 ## Keys
16539
166-All bindings use Alt directly; there is no prefix key.
167-
168-| Key | Action |
169-|---|---|
170-| Alt+Space | open the command palette |
171-| Alt+f | show or hide the current tab's floating terminal |
172-| Alt+1 .. Alt+9 | switch to tab by number |
173-| Alt+h/j/k/l | focus pane left/down/up/right |
174-| Alt+Arrow | focus pane by direction |
175-| Alt+a | jump to a session with attention |
176-
177-Some terminals encode Alt+Space as a non-breaking space instead of `ESC Space`; in those terminals, the palette binding depends on whether the terminal can send `ESC Space` for Alt+Space.
178-
179-## Command palette
180-
181-Open the palette with Alt+Space. Type a command code or fuzzy-match the label/description. Use Up/Down or Ctrl-N/Ctrl-P to move, Tab to complete the selected command code, Enter to run, and Esc/Ctrl-C to close. With an empty query, Tab completes the most recently successful command; commands that take an argument complete with a trailing space and preserve an argument already entered.
182-
183-Successful commands are promoted near the top the next time the palette opens. `BSK` toggles back to your previously active session. Typing `JRS` lists recent sessions by number in the status bar, and `JRS N` jumps to number N; the numbers are captured when you start typing, so a busy MRU list never shifts under you. An invalid or stale number leaves the palette open.
184-
185-| Code | Action |
186-|---|---|
187-| CNT | create new tab |
188-| CNS | create and switch to a named session |
189-| CLT | close current tab |
190-| SPR / SPL / SPU / SPD | split pane right / left / up / down |
191-| STP | stack a new pane |
192-| TST | toggle focused stack |
193-| CLP | close focused pane |
194-| FPL / FPR / FPU / FPD | focus pane left / right / up / down |
195-| NXT / PVT | switch to next / previous tab |
196-| BSK | toggle the previously active session |
197-| JRS N | jump directly to recent session rank N |
198-| SSP | open session picker |
199-| VIS | enter visual mode |
200-| FLT | show or hide the current tab's floating terminal |
201-| RNS | rename session |
202-| RNT | rename current tab |
203-| DET | detach |
204-
205-Rename prompts are prefilled with the current session name. Empty names and names already used by another session are rejected.
206-
207-## Session picker
208-
209-Open it with `SSP` in the palette. It lists sessions and tabs — tab entries use the same `name (title)` form as the top bar, ellipsized to the list width — previews the selected target when space allows, and marks stopped sessions. Wide pickers place the preview beside the list with a muted divider; narrower pickers stack it below the list, then hide it when neither layout has useful space. A clipped preview stays anchored to the bottom of the target's visible terminal frame and never loads its scrollback.
40+No prefix key; everything is Alt.
21041
21142 | Key | Action |
21243 |---|---|
213-| j / k, Up / Down | move selection |
214-| Enter | switch to or resume selected target |
215-| x | kill/delete selected target |
216-| q, Esc, Ctrl-C | close picker |
217-
218-## Panes and tabs
44+| Alt+Space | command palette |
45+| Alt+f | floating terminal for the current tab |
46+| Alt+1 .. 9 | switch tab |
47+| Alt+h/j/k/l, Alt+Arrow | focus pane |
48+| Alt+a | jump to a session needing attention |
21949
220-Tabs hold pane layouts. Panes can be split left/right/up/down, stacked, focused by direction, and closed from the command palette. Clicking a pane focuses it.
50+The palette does the rest: type a short code (`SPR` split right, `CNT` new tab, `SSP` session picker, ...) or fuzzy-search the command list. Scroll up with the mouse to enter scrollback; vim keys move, `v` selects, `y` copies via OSC 52.
22151
222-Each tab also owns one independent floating terminal. It prewarms asynchronously on the tab's first visit, opens centered at the configured percentage of the pane-content area, and dims normal pane content while leaving status bars and popup chrome crisp. Hiding it retains the process, screen, and scrollback; switching tabs restores each tab's own visible or hidden state. Process exit clears the slot so the next open recreates it. The title combines the foreground process and OSC terminal title as `ProcessName: ProcessTerminalTitle`, falling back to either available title and then the configured command or shell.
52+## Remote attach
22353
224-While visible, the floating terminal receives ordinary input, bracketed paste, mouse input, clipboard-path injection, and scrollback/copy mode. Global vev bindings remain active, and a bare Esc is passed to the floating application. `Alt+f` and `FLT` toggle visibility; there is no separate floating-pane close command.
54+```sh
55+vev attach user@host[:session]
56+```
22557
226-The top bar shows each tab as `name (title)`, where the name is the tab's rename or number and the title is the focused pane's `ProcessName: ProcessTerminalTitle`. On truecolor clients the name renders bold and the parenthesized title slightly muted (in both the top bar and the session picker), derived from the client's reported theme; other terminals render plain labels. Set `tabs.terminal-title = off` to drop the `ProcessTerminalTitle` half from tab labels (top bar and session picker) and show only the process name; floating and stacked pane title bars are unaffected. Labels share the row width: when everything fits they render in full, otherwise unused space flows from short tabs to long ones and long labels are ellipsized, degrading to the bare tab name when space runs out. The bottom bar shows the active session followed by recent sessions, fading toward older entries. Ephemeral sessions are marked with `*`, for example `0*`.
58+vev must be installed on the remote. If your firewall only allows SSH, open the UDP range first (default `61000:61023`, override with `VEV_UDP_PORT_RANGE`):
22759
228-## Scrollback mode and copy
60+```sh
61+sudo ufw allow 61000:61023/udp
62+```
22963
230-Scrollback mode freezes a view over history while the program keeps running underneath. Enter it with the `VIS` palette command or by scrolling up with the mouse wheel. Passive browsing is labeled `[SCROLL]`; selecting lines changes the status to `[SELECT]`. Scrolling back to the bottom exits to the live pane.
64+Where UDP is not an option, `VEV_REMOTE_TRANSPORT=stdio` keeps everything inside SSH at the cost of slower disconnect detection. Details in [docs/remote-resilience.md](docs/remote-resilience.md).
23165
232-| Key | Action |
233-|---|---|
234-| j / k, Up / Down | move one line |
235-| PgUp / PgDn | move one page |
236-| g / G | jump to top / bottom |
237-| v or Space | toggle line selection |
238-| y or Enter | copy selection and exit |
239-| q, Esc, Ctrl-C | exit without copying |
66+## Configuration
24067
241-Copy uses OSC 52 clipboard sequences when supported by your terminal. After a successful copy, the bottom bar briefly shows `copied N chars to clipboard`.
68+Optional file at `~/.config/vev/config`, reloaded live. Themes, key bindings, palette codes, the floating terminal, status bar scripts: see [docs/configuration.md](docs/configuration.md). Terminal color and VT compatibility notes live in [docs/terminal.md](docs/terminal.md).
24269
24370 ## Development
24471
24572 ```sh
246-go install golang.org/x/tools/cmd/goimports@latest
247-go install github.com/vektra/mockery/v2@latest
24873 make test # go test ./... -race
249-make lint # goimports check, then go vet ./...
74+make lint # goimports check, go vet
25075 make mocks # regenerate mocks
25176 ```
@@ -1,251 +1,76 @@
1 # vev1 # vev
2-***Norwegian: web, weave, tissue***
3 2
4-There are many terminal multiplexers, but this one is mine. Minimal aesthetic, purely focused on performance, rendering high-output of vertical text, agentic coding, and remote work over SSH. Built in Go with no external deps as much as possible.3+*Norwegian: to weave*
5 4
6-> [!IMPORTANT]5+A terminal multiplexer for Linux. One Go binary, a per-user daemon, a thin client.
7-> This is totally in alpha.
8 6
9-## Install7+> [!WARNING]
8+> Alpha software, Linux only. Expect breaking changes.
10 9
11-```sh10+What it is built around:
12-go install github.com/bnema/vev@latest
13-```
14 11
15-From a checkout:12+- **Remote attach that survives bad networks.** SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. Inspired by mosh, with its own protocol.
13+- **Server-side rendering.** The daemon keeps a full VT screen per pane and sends minimal diffs to the client, so high-output programs (agents, build logs) stay smooth locally and over SSH.
14+- **Nothing but the standard library.** VT emulation, renderer, wire protocol, and UDP transport are all in this repo. The only runtime dependencies are `golang.org/x/sys` and `golang.org/x/term`.
15+- **Performance treated as a feature.** A reproducible harness measures 252 scenarios across workloads and transports, including lossy and high-latency UDP.
16+
17+## Install
16 18
17 ```sh19 ```sh
18-make install # install vev and default bar scripts to Go's bin directory20+go install github.com/bnema/vev@latest
19-go build -o vev . # local binary
20 ```21 ```
21 22
22-`make install` places `vev-bar-top-right` and `vev-bar-bottom-right` beside the installed `vev` binary. If you install another way, copy the scripts from `scripts/` into a directory on `PATH` or replace the bar commands in config.23+From a checkout, `make install` also installs the default status bar scripts.
23 24
24 ## Usage25 ## Usage
25 26
26 ```text27 ```text
27-vev attach to (or create) an ephemeral session28+vev create an ephemeral session
28-vev new <name> create and attach to a named session29+vev new <name> create a named session
29-vev attach <name> attach to an existing session (alias: a)30+vev attach <name> attach to a session (alias: a)
30-vev attach user@host[:session] attach to a remote vev daemon over SSH31+vev attach user@host[:session] attach to a remote daemon over SSH
31-vev ls list sessions (alias: list)32+vev ls list sessions
32-vev kill <name> kill a session33+vev kill <name> kill a session (--all kills everything)
33-vev kill -- <name> kill a name that starts with -
34-vev kill --all kill all sessions and stop the daemon
35-vev kill --daemon stop the active vev daemon
36-vev --help show help (aliases: -h, help)
37-vev --version show version (alias: version)
38-```
39-
40-Ephemeral sessions get a number (`0`, `1`, `2`, ...) and are temporary daemon-retained sessions. They survive detach and transient client connection loss while the daemon is running, but they are not persisted to disk and disappear when the daemon is killed, exits, or the machine restarts. Named sessions are persisted, survive daemon restarts, and can be resumed later. New and renamed session names must start with a letter or number and may contain only letters, numbers, dots, underscores, and dashes.
41-
42-The daemon starts on first use and exits when no sessions remain. Only one client is attached to a session at a time; attaching again displaces the old client.
43-
44-## Configuration
45-
46-vev reads `$XDG_CONFIG_HOME/vev/config`, or `~/.config/vev/config` when `XDG_CONFIG_HOME` is unset. Missing files use defaults. The daemon reloads mtime/size changes while running, polling about every two seconds.
47-
48-```text
49-# Theme: auto follows the client; dark/light force built-in palettes.
50-theme = auto
51-
52-# Palette placement: auto, center, top-left, top, top-right, left, right,
53-# bottom-left, bottom, or bottom-right.
54-palette.anchor = auto
55-
56-# One prewarmed floating terminal per tab. An empty command uses the normal shell.
57-floating.command =
58-floating.width = 80%
59-floating.height = 80%
60-
61-# Include the focused pane's OSC terminal title in tab labels (top bar and
62-# session picker). The process name always shows; off keeps only that.
63-tabs.terminal-title = on
64-
65-# Rebindable actions. Leave a line out to keep its built-in binding.
66-open-palette = alt+space
67-toggle-floating-pane = alt+f
68-jump-attention = alt+a
69-focus-pane-left = alt+h
70-focus-pane-right = alt+l
71-focus-pane-up = alt+k
72-focus-pane-down = alt+j
73-switch-tab-1 = alt+1
74-switch-tab-2 = alt+2
75-switch-tab-3 = alt+3
76-switch-tab-4 = alt+4
77-switch-tab-5 = alt+5
78-switch-tab-6 = alt+6
79-switch-tab-7 = alt+7
80-switch-tab-8 = alt+8
81-switch-tab-9 = alt+9
82-
83-# Named session snapshots restore layout, cwd, visible content, scrollback,
84-# and foreground processes from this allowlist. Omit the key for these defaults;
85-# set it empty to disable process relaunch.
86-snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode
87-
88-# Right bar anchors. Empty command values disable the matching anchor.
89-bar.top-right = vev-bar-top-right
90-bar.bottom-right = vev-bar-bottom-right
91-bar.interval = 5s
92-
93-# Command palette codes are trimmed, uppercased, then checked as 2-3 letters or digits.
94-code.new-tab = CNT
95-code.new-session = CNS
96-code.close-tab = CLT
97-code.split-right = SPR
98-code.split-left = SPL
99-code.split-up = SPU
100-code.split-down = SPD
101-code.stack-pane = STP
102-code.toggle-stack = TST
103-code.close-pane = CLP
104-code.focus-pane-left = FPL
105-code.focus-pane-right = FPR
106-code.focus-pane-up = FPU
107-code.focus-pane-down = FPD
108-code.next-tab = NXT
109-code.previous-tab = PVT
110-code.back-session = BSK
111-code.jump-recent-session = JRS
112-code.session-picker = SSP
113-code.visual-mode = VIS
114-code.toggle-floating-pane = FLT
115-code.rename-session = RNS
116-code.detach = DET
117-```
118-
119-Configuring an action replaces all of its built-in aliases. In the example above, the focus-pane lines keep Alt+h/j/k/l but not the Alt+Arrow aliases; omit those lines to keep all built-ins.
120-
121-Key specs support `alt+<char>`, `alt+space`, and `alt+left/right/up/down`; digit key specs support `alt+1` through `alt+9`, not `alt+0`. `jump-attention` first opens the oldest attention tab in the current session; if none exists, it opens the oldest attention tab in another session. Invalid entries are logged as warnings and skipped where possible; duplicate config keys use the last value, binding conflicts keep the later action's defaults, and command-code conflicts drop the conflicting override.
122-
123-`floating.command` runs through the normal shell; leave it empty to launch that shell directly. `floating.width` and `floating.height` require exact percentages from `1%` through `100%`; invalid values log a warning and leave the last accepted value (or the `80%` default) intact. Reloading does not relaunch or immediately alter an installed floating terminal: a changed command is used on its next launch, while changed dimensions take effect when that terminal is next shown or resized. Only configuration—not floating runtime state—is restored after a daemon restart; restored tabs stay cold until first visited.
124-
125-`tabs.terminal-title` accepts `on` or `off` and defaults to `on`; invalid values log a warning and keep the last accepted value. Reload applies a changed value live.
126-
127-### Command palette anchoring
128-
129-`palette.anchor` accepts `auto` or the nine anchors `center`, `top-left`, `top`, `top-right`, `left`, `right`, `bottom-left`, `bottom`, and `bottom-right`. Values are trimmed and case-insensitive. The default is `auto`: palettes use the bottom shelf when the terminal is narrower than 96 columns, and a 64-column bottom-right rail at 96 columns or wider. An explicit anchor changes placement only; palette query and selection are preserved. Invalid values are warned about and leave the last valid value unchanged, as do duplicate entries until a valid replacement is supplied (the last valid value wins). Configuration reload applies a changed anchor live and repositions an open palette without resetting its query or selection.
130-
131-### Bar right anchors
132-
133-The top and bottom bar right anchors run configurable commands on the daemon host. By default, `bar.top-right = vev-bar-top-right` prints CPU, memory, then hour text (for example `C 12% M 48% 09:41`), and `bar.bottom-right = vev-bar-bottom-right` prints compact git state for the focused pane's `VEV_PANE_CWD`. Set either value empty to disable that anchor, or set it to another command to replace it.
134-
135-Both anchors share `bar.interval`, which defaults to `5s`; values below `1s` are clamped to `1s`. vev also refreshes around attach, resize, focus, and copy-feedback changes. Commands are bounded by a timeout and output limit. On failure or timeout, vev keeps the last good value; if there is none, the anchor is empty.
136-
137-Scripts receive these environment variables when available: `VEV_ANCHOR` (`top-right` or `bottom-right`), `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`. vev reads stdout only, uses the first line only, treats it as plain UTF-8 display text, and strips ANSI escape sequences and other control characters. Unicode and Nerd Font glyphs are supported. The v1 API does not support colors, styles, JSON, streaming output, or per-anchor intervals. The default git script runs `git status --porcelain` each refresh, so increase `bar.interval` or replace the command if that is too expensive for a large repository.
138-
139-## Remote attach
140-
141-```sh
142-vev attach user@host
143-vev attach user@host:session
144 ```34 ```
145 35
146-Remote attach uses SSH only to start a UDP proxy on the remote host; the session itself then talks directly to the remote host over UDP. `vev` must be installed on the remote host, and the host name you pass to `vev attach` must be reachable both by SSH for bootstrap and by UDP for the session transport. Omitting `:session` opens an ephemeral remote session. Remote sessions can resume after temporary network disconnects, such as sleep or Wi-Fi changes, while the daemon still has the session. Named sessions additionally persist across daemon restarts.36+The daemon starts on first use and exits with the last session. Ephemeral sessions are numbered, survive detach, and disappear with the daemon. Named sessions persist across daemon restarts and come back with their layout, scrollback, and allowlisted processes.
147-
148-vev's resilient remote attach work is inspired by [mosh](https://mosh.org/)'s mobile-shell approach to UDP-based recovery across flaky networks. vev uses its own wire protocol and implementation; it is not compatible with mosh clients or servers and does not copy mosh protocol code.
149-
150-The default remote transport uses UDP. Its proxy binds a port in a fixed range on the remote (default `61000-61023`), so a host firewall that only allows SSH will drop the session datagrams and attach fails with `remote UDP transport unavailable: probe UDP transport`. Allow inbound UDP for that range on the remote to fix it — for example `sudo ufw allow 61000:61023/udp`, or on a trusted Tailscale network `sudo ufw allow in on tailscale0`. Override the range with `VEV_UDP_PORT_RANGE` on the remote (e.g. `VEV_UDP_PORT_RANGE=51000-51009`, a single `VEV_UDP_PORT_RANGE=51000`, or `VEV_UDP_PORT_RANGE=0` for a random ephemeral port).
151-
152-Set `VEV_REMOTE_TRANSPORT=stdio` to use SSH stdio compatibility mode on networks where direct UDP is blocked or the SSH target is only reachable through jump-host or SSH-config-only routing. Stdio mode keeps all traffic inside SSH and does not require inbound UDP, but it may not notice sleep or Wi-Fi loss promptly. See `docs/remote-resilience.md` for concise operational notes.
153-
154-## Terminal color
155-
156-When the attaching client reports truecolor support, vev advertises newly spawned panes as `TERM=xterm-direct` and exports `COLORTERM=truecolor` to child processes. Otherwise, panes use the conservative `TERM=xterm-256color` and do not receive `COLORTERM`. This works for local and remote attach because the client capability is carried inside vev's protocol rather than relying on SSH `SendEnv`/`AcceptEnv`.
157-
158-Applications that use terminfo can detect direct color from `xterm-direct`; applications that use the common environment convention can use `COLORTERM=truecolor` when it is present. Panes restored from a saved snapshot start conservatively before any client attaches; after attach or resume, newly spawned panes use that client's reported capability.
159-
160-## Terminal compatibility
161-
162-vev keeps a server-side VT screen for each pane. It tracks scroll regions with DECOM origin mode, ANSI insert mode, cursor-position and mode-query reports, alternate-screen state, bracketed paste, mouse tracking, synchronized updates, OSC 9/777 notifications, OSC 52 clipboard set requests, and double-width cells. Resize and edit operations repair double-width cells so CJK or emoji glyph halves are not left behind.
163 37
164 ## Keys38 ## Keys
165 39
166-All bindings use Alt directly; there is no prefix key.40+No prefix key; everything is Alt.
167-
168-| Key | Action |
169-|---|---|
170-| Alt+Space | open the command palette |
171-| Alt+f | show or hide the current tab's floating terminal |
172-| Alt+1 .. Alt+9 | switch to tab by number |
173-| Alt+h/j/k/l | focus pane left/down/up/right |
174-| Alt+Arrow | focus pane by direction |
175-| Alt+a | jump to a session with attention |
176-
177-Some terminals encode Alt+Space as a non-breaking space instead of `ESC Space`; in those terminals, the palette binding depends on whether the terminal can send `ESC Space` for Alt+Space.
178-
179-## Command palette
180-
181-Open the palette with Alt+Space. Type a command code or fuzzy-match the label/description. Use Up/Down or Ctrl-N/Ctrl-P to move, Tab to complete the selected command code, Enter to run, and Esc/Ctrl-C to close. With an empty query, Tab completes the most recently successful command; commands that take an argument complete with a trailing space and preserve an argument already entered.
182-
183-Successful commands are promoted near the top the next time the palette opens. `BSK` toggles back to your previously active session. Typing `JRS` lists recent sessions by number in the status bar, and `JRS N` jumps to number N; the numbers are captured when you start typing, so a busy MRU list never shifts under you. An invalid or stale number leaves the palette open.
184-
185-| Code | Action |
186-|---|---|
187-| CNT | create new tab |
188-| CNS | create and switch to a named session |
189-| CLT | close current tab |
190-| SPR / SPL / SPU / SPD | split pane right / left / up / down |
191-| STP | stack a new pane |
192-| TST | toggle focused stack |
193-| CLP | close focused pane |
194-| FPL / FPR / FPU / FPD | focus pane left / right / up / down |
195-| NXT / PVT | switch to next / previous tab |
196-| BSK | toggle the previously active session |
197-| JRS N | jump directly to recent session rank N |
198-| SSP | open session picker |
199-| VIS | enter visual mode |
200-| FLT | show or hide the current tab's floating terminal |
201-| RNS | rename session |
202-| RNT | rename current tab |
203-| DET | detach |
204-
205-Rename prompts are prefilled with the current session name. Empty names and names already used by another session are rejected.
206-
207-## Session picker
208-
209-Open it with `SSP` in the palette. It lists sessions and tabs — tab entries use the same `name (title)` form as the top bar, ellipsized to the list width — previews the selected target when space allows, and marks stopped sessions. Wide pickers place the preview beside the list with a muted divider; narrower pickers stack it below the list, then hide it when neither layout has useful space. A clipped preview stays anchored to the bottom of the target's visible terminal frame and never loads its scrollback.
210 41
211 | Key | Action |42 | Key | Action |
212 |---|---|43 |---|---|
213-| j / k, Up / Down | move selection |44+| Alt+Space | command palette |
214-| Enter | switch to or resume selected target |45+| Alt+f | floating terminal for the current tab |
215-| x | kill/delete selected target |46+| Alt+1 .. 9 | switch tab |
216-| q, Esc, Ctrl-C | close picker |47+| Alt+h/j/k/l, Alt+Arrow | focus pane |
217-48+| Alt+a | jump to a session needing attention |
218-## Panes and tabs
219 49
220-Tabs hold pane layouts. Panes can be split left/right/up/down, stacked, focused by direction, and closed from the command palette. Clicking a pane focuses it.50+The palette does the rest: type a short code (`SPR` split right, `CNT` new tab, `SSP` session picker, ...) or fuzzy-search the command list. Scroll up with the mouse to enter scrollback; vim keys move, `v` selects, `y` copies via OSC 52.
221 51
222-Each tab also owns one independent floating terminal. It prewarms asynchronously on the tab's first visit, opens centered at the configured percentage of the pane-content area, and dims normal pane content while leaving status bars and popup chrome crisp. Hiding it retains the process, screen, and scrollback; switching tabs restores each tab's own visible or hidden state. Process exit clears the slot so the next open recreates it. The title combines the foreground process and OSC terminal title as `ProcessName: ProcessTerminalTitle`, falling back to either available title and then the configured command or shell.52+## Remote attach
223 53
224-While visible, the floating terminal receives ordinary input, bracketed paste, mouse input, clipboard-path injection, and scrollback/copy mode. Global vev bindings remain active, and a bare Esc is passed to the floating application. `Alt+f` and `FLT` toggle visibility; there is no separate floating-pane close command.54+```sh
55+vev attach user@host[:session]
56+```
225 57
226-The top bar shows each tab as `name (title)`, where the name is the tab's rename or number and the title is the focused pane's `ProcessName: ProcessTerminalTitle`. On truecolor clients the name renders bold and the parenthesized title slightly muted (in both the top bar and the session picker), derived from the client's reported theme; other terminals render plain labels. Set `tabs.terminal-title = off` to drop the `ProcessTerminalTitle` half from tab labels (top bar and session picker) and show only the process name; floating and stacked pane title bars are unaffected. Labels share the row width: when everything fits they render in full, otherwise unused space flows from short tabs to long ones and long labels are ellipsized, degrading to the bare tab name when space runs out. The bottom bar shows the active session followed by recent sessions, fading toward older entries. Ephemeral sessions are marked with `*`, for example `0*`.58+vev must be installed on the remote. If your firewall only allows SSH, open the UDP range first (default `61000:61023`, override with `VEV_UDP_PORT_RANGE`):
227 59
228-## Scrollback mode and copy60+```sh
61+sudo ufw allow 61000:61023/udp
62+```
229 63
230-Scrollback mode freezes a view over history while the program keeps running underneath. Enter it with the `VIS` palette command or by scrolling up with the mouse wheel. Passive browsing is labeled `[SCROLL]`; selecting lines changes the status to `[SELECT]`. Scrolling back to the bottom exits to the live pane.64+Where UDP is not an option, `VEV_REMOTE_TRANSPORT=stdio` keeps everything inside SSH at the cost of slower disconnect detection. Details in [docs/remote-resilience.md](docs/remote-resilience.md).
231 65
232-| Key | Action |66+## Configuration
233-|---|---|
234-| j / k, Up / Down | move one line |
235-| PgUp / PgDn | move one page |
236-| g / G | jump to top / bottom |
237-| v or Space | toggle line selection |
238-| y or Enter | copy selection and exit |
239-| q, Esc, Ctrl-C | exit without copying |
240 67
241-Copy uses OSC 52 clipboard sequences when supported by your terminal. After a successful copy, the bottom bar briefly shows `copied N chars to clipboard`.68+Optional file at `~/.config/vev/config`, reloaded live. Themes, key bindings, palette codes, the floating terminal, status bar scripts: see [docs/configuration.md](docs/configuration.md). Terminal color and VT compatibility notes live in [docs/terminal.md](docs/terminal.md).
242 69
243 ## Development70 ## Development
244 71
245 ```sh72 ```sh
246-go install golang.org/x/tools/cmd/goimports@latest
247-go install github.com/vektra/mockery/v2@latest
248 make test # go test ./... -race73 make test # go test ./... -race
249-make lint # goimports check, then go vet ./...74+make lint # goimports check, go vet
250 make mocks # regenerate mocks75 make mocks # regenerate mocks
251 ```76 ```
A docs/configuration.md +85 -0
@@ -0,0 +1,85 @@
1+# Configuration
2+
3+vev reads `~/.config/vev/config` (`$XDG_CONFIG_HOME` respected). No file means defaults. The daemon picks up changes within a couple of seconds; no restart needed.
4+
5+```text
6+# Theme: auto follows the client; dark/light force built-in palettes.
7+theme = auto
8+
9+# Palette placement: auto, center, top-left, top, top-right, left, right,
10+# bottom-left, bottom, or bottom-right.
11+palette.anchor = auto
12+
13+# One prewarmed floating terminal per tab. An empty command uses the normal shell.
14+floating.command =
15+floating.width = 80%
16+floating.height = 80%
17+
18+# Show the focused pane's terminal title in tab labels; off keeps the process name only.
19+tabs.terminal-title = on
20+
21+# Rebindable actions. Leave a line out to keep its built-in binding.
22+open-palette = alt+space
23+toggle-floating-pane = alt+f
24+jump-attention = alt+a
25+focus-pane-left = alt+h
26+focus-pane-right = alt+l
27+focus-pane-up = alt+k
28+focus-pane-down = alt+j
29+switch-tab-1 = alt+1
30+# ... through switch-tab-9 = alt+9
31+
32+# Processes relaunched when a named session is restored. Empty disables relaunch.
33+snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode
34+
35+# Right bar anchors: commands run on the daemon host. Empty disables an anchor.
36+bar.top-right = vev-bar-top-right
37+bar.bottom-right = vev-bar-bottom-right
38+bar.interval = 5s
39+
40+# Command palette codes: 2-3 letters or digits.
41+code.new-tab = CNT
42+code.new-session = CNS
43+code.close-tab = CLT
44+code.split-right = SPR
45+code.split-left = SPL
46+code.split-up = SPU
47+code.split-down = SPD
48+code.stack-pane = STP
49+code.toggle-stack = TST
50+code.close-pane = CLP
51+code.focus-pane-left = FPL
52+code.focus-pane-right = FPR
53+code.focus-pane-up = FPU
54+code.focus-pane-down = FPD
55+code.next-tab = NXT
56+code.previous-tab = PVT
57+code.back-session = BSK
58+code.jump-recent-session = JRS
59+code.session-picker = SSP
60+code.visual-mode = VIS
61+code.toggle-floating-pane = FLT
62+code.rename-session = RNS
63+code.rename-tab = RNT
64+code.detach = DET
65+```
66+
67+Invalid values log a warning and keep the last valid value.
68+
69+## Bindings
70+
71+Key specs: `alt+<char>`, `alt+space`, `alt+left/right/up/down`, `alt+1` through `alt+9`. Configuring an action replaces all of its built-in aliases (set `focus-pane-left` and the Alt+Arrow alias is gone). Tab switching also accepts the top-row symbols of non-QWERTY layouts, so AZERTY works without extra config.
72+
73+## Palette anchor
74+
75+`auto` uses a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query.
76+
77+## Floating terminal
78+
79+The command runs through your shell. A changed command applies on the next launch; changed dimensions on the next show or resize. Floating state is not restored across daemon restarts.
80+
81+## Bar anchors
82+
83+Anchor commands run on the daemon host every `bar.interval` (minimum 1s). vev reads the first line of stdout, strips ANSI codes, and keeps the last good value on failure. Scripts get `VEV_ANCHOR`, `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`.
84+
85+The default bottom-right script runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository.
@@ -0,0 +1,85 @@
1+# Configuration
2+
3+vev reads `~/.config/vev/config` (`$XDG_CONFIG_HOME` respected). No file means defaults. The daemon picks up changes within a couple of seconds; no restart needed.
4+
5+```text
6+# Theme: auto follows the client; dark/light force built-in palettes.
7+theme = auto
8+
9+# Palette placement: auto, center, top-left, top, top-right, left, right,
10+# bottom-left, bottom, or bottom-right.
11+palette.anchor = auto
12+
13+# One prewarmed floating terminal per tab. An empty command uses the normal shell.
14+floating.command =
15+floating.width = 80%
16+floating.height = 80%
17+
18+# Show the focused pane's terminal title in tab labels; off keeps the process name only.
19+tabs.terminal-title = on
20+
21+# Rebindable actions. Leave a line out to keep its built-in binding.
22+open-palette = alt+space
23+toggle-floating-pane = alt+f
24+jump-attention = alt+a
25+focus-pane-left = alt+h
26+focus-pane-right = alt+l
27+focus-pane-up = alt+k
28+focus-pane-down = alt+j
29+switch-tab-1 = alt+1
30+# ... through switch-tab-9 = alt+9
31+
32+# Processes relaunched when a named session is restored. Empty disables relaunch.
33+snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode
34+
35+# Right bar anchors: commands run on the daemon host. Empty disables an anchor.
36+bar.top-right = vev-bar-top-right
37+bar.bottom-right = vev-bar-bottom-right
38+bar.interval = 5s
39+
40+# Command palette codes: 2-3 letters or digits.
41+code.new-tab = CNT
42+code.new-session = CNS
43+code.close-tab = CLT
44+code.split-right = SPR
45+code.split-left = SPL
46+code.split-up = SPU
47+code.split-down = SPD
48+code.stack-pane = STP
49+code.toggle-stack = TST
50+code.close-pane = CLP
51+code.focus-pane-left = FPL
52+code.focus-pane-right = FPR
53+code.focus-pane-up = FPU
54+code.focus-pane-down = FPD
55+code.next-tab = NXT
56+code.previous-tab = PVT
57+code.back-session = BSK
58+code.jump-recent-session = JRS
59+code.session-picker = SSP
60+code.visual-mode = VIS
61+code.toggle-floating-pane = FLT
62+code.rename-session = RNS
63+code.rename-tab = RNT
64+code.detach = DET
65+```
66+
67+Invalid values log a warning and keep the last valid value.
68+
69+## Bindings
70+
71+Key specs: `alt+<char>`, `alt+space`, `alt+left/right/up/down`, `alt+1` through `alt+9`. Configuring an action replaces all of its built-in aliases (set `focus-pane-left` and the Alt+Arrow alias is gone). Tab switching also accepts the top-row symbols of non-QWERTY layouts, so AZERTY works without extra config.
72+
73+## Palette anchor
74+
75+`auto` uses a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query.
76+
77+## Floating terminal
78+
79+The command runs through your shell. A changed command applies on the next launch; changed dimensions on the next show or resize. Floating state is not restored across daemon restarts.
80+
81+## Bar anchors
82+
83+Anchor commands run on the daemon host every `bar.interval` (minimum 1s). vev reads the first line of stdout, strips ANSI codes, and keeps the last good value on failure. Scripts get `VEV_ANCHOR`, `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`.
84+
85+The default bottom-right script runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository.
M docs/remote-resilience.md +2 -2
@@ -5,7 +5,7 @@ Remote attach starts with SSH bootstrap and then carries the vev session over au
55 ## Operational model
66
77 - SSH bootstrap starts or finds the remote UDP endpoint.
8-- UDP carries normal session traffic after bootstrap.
8+- UDP carries normal session traffic after bootstrap. The remote proxy binds a port in a fixed range, `61000-61023` by default; `VEV_UDP_PORT_RANGE` overrides it (a range, a single port, or `0` for a random ephemeral port).
99 - Named sessions remain recoverable across daemon restarts; ephemeral sessions remain recoverable only while the daemon retains them.
1010 - `VEV_REMOTE_TRANSPORT=stdio` keeps all traffic inside SSH when direct UDP is blocked.
1111
@@ -19,7 +19,7 @@ Resilience work uses these user-facing states:
1919 | `degraded` | authenticated UDP packets still arrive, but complete-record or ACK progress is delayed |
2020 | `probing` | authenticated UDP packet contact is absent, so vev probes for path recovery |
2121 | `resuming` | a replacement transport is attaching to the same session |
22-| `offline` | authenticated UDP contact remains absent and vev resumes over SSH stdio |
22+| `offline` | authenticated UDP contact remains absent; vev re-runs the SSH bootstrap and re-dials UDP with the resume token |
2323 | `expired` | the session or recovery window is no longer available |
2424
2525 When a terminal is in raw mode, recovery messages appear as vev status text rather than as bytes written into the remote program's output stream.
@@ -5,7 +5,7 @@ Remote attach starts with SSH bootstrap and then carries the vev session over au
5 ## Operational model5 ## Operational model
6 6
7 - SSH bootstrap starts or finds the remote UDP endpoint.7 - SSH bootstrap starts or finds the remote UDP endpoint.
8-- UDP carries normal session traffic after bootstrap.8+- UDP carries normal session traffic after bootstrap. The remote proxy binds a port in a fixed range, `61000-61023` by default; `VEV_UDP_PORT_RANGE` overrides it (a range, a single port, or `0` for a random ephemeral port).
9 - Named sessions remain recoverable across daemon restarts; ephemeral sessions remain recoverable only while the daemon retains them.9 - Named sessions remain recoverable across daemon restarts; ephemeral sessions remain recoverable only while the daemon retains them.
10 - `VEV_REMOTE_TRANSPORT=stdio` keeps all traffic inside SSH when direct UDP is blocked.10 - `VEV_REMOTE_TRANSPORT=stdio` keeps all traffic inside SSH when direct UDP is blocked.
11 11
@@ -19,7 +19,7 @@ Resilience work uses these user-facing states:
19 | `degraded` | authenticated UDP packets still arrive, but complete-record or ACK progress is delayed |19 | `degraded` | authenticated UDP packets still arrive, but complete-record or ACK progress is delayed |
20 | `probing` | authenticated UDP packet contact is absent, so vev probes for path recovery |20 | `probing` | authenticated UDP packet contact is absent, so vev probes for path recovery |
21 | `resuming` | a replacement transport is attaching to the same session |21 | `resuming` | a replacement transport is attaching to the same session |
22-| `offline` | authenticated UDP contact remains absent and vev resumes over SSH stdio |22+| `offline` | authenticated UDP contact remains absent; vev re-runs the SSH bootstrap and re-dials UDP with the resume token |
23 | `expired` | the session or recovery window is no longer available |23 | `expired` | the session or recovery window is no longer available |
24 24
25 When a terminal is in raw mode, recovery messages appear as vev status text rather than as bytes written into the remote program's output stream.25 When a terminal is in raw mode, recovery messages appear as vev status text rather than as bytes written into the remote program's output stream.
A docs/terminal.md +13 -0
@@ -0,0 +1,13 @@
1+# Terminal support
2+
3+## Color
4+
5+When the client reports truecolor, new panes get `TERM=xterm-direct` and `COLORTERM=truecolor`; otherwise `TERM=xterm-256color`. The capability travels inside vev's own protocol, so it works over SSH without `SendEnv`/`AcceptEnv`. Panes restored from a snapshot start conservative until a client attaches.
6+
7+## VT compatibility
8+
9+vev keeps a server-side VT screen per pane: scroll regions (DECOM), insert mode, cursor and mode-query reports, alternate screen, bracketed paste, mouse tracking, synchronized updates, OSC 9/777 notifications, OSC 52 clipboard, and double-width cells. Resizes repair double-width cells so CJK and emoji halves are never left behind.
10+
11+## Alt+Space caveat
12+
13+Some terminals encode Alt+Space as a non-breaking space instead of `ESC Space`. If the palette does not open, rebind `open-palette`.
@@ -0,0 +1,13 @@
1+# Terminal support
2+
3+## Color
4+
5+When the client reports truecolor, new panes get `TERM=xterm-direct` and `COLORTERM=truecolor`; otherwise `TERM=xterm-256color`. The capability travels inside vev's own protocol, so it works over SSH without `SendEnv`/`AcceptEnv`. Panes restored from a snapshot start conservative until a client attaches.
6+
7+## VT compatibility
8+
9+vev keeps a server-side VT screen per pane: scroll regions (DECOM), insert mode, cursor and mode-query reports, alternate screen, bracketed paste, mouse tracking, synchronized updates, OSC 9/777 notifications, OSC 52 clipboard, and double-width cells. Resizes repair double-width cells so CJK and emoji halves are never left behind.
10+
11+## Alt+Space caveat
12+
13+Some terminals encode Alt+Space as a non-breaking space instead of `ESC Space`. If the palette does not open, rebind `open-palette`.

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