detached bcb097b

Merge pull request #128 from bnema/feat/readme-facelift

docs: center the README hero and give the demo a real terminal window
commit
bcb097b70a38bc5a89d2c0bc9158353ab9718861
author
bnema · · 2026-07-24 21:09 UTC
committer
GitHub · · 2026-07-24 21:09 UTC
merge of
841e10a + a87ac1a · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
5 files · +42 -26
M Makefile +16 -1
@@ -28,6 +28,21 @@ mocks:
2828 demo:
2929 docker build -f scripts/demo/Dockerfile -t vev-demo .
3030 ./scripts/demo/run.sh demo.tape
31- docker run --rm --entrypoint gifsicle -v "$(CURDIR)/scripts/demo/out:/out" vev-demo -O3 --lossy=80 -o /out/demo.gif /out/demo.gif
31+ # vhs cannot emit a transparent margin, so cut the window out after the
32+ # fact: everything outside the rounded window rect becomes GIF
33+ # transparency. The geometry mirrors demo.tape (1200x750 canvas, margin
34+ # 20, border radius 10); keep them in sync.
35+ docker run --rm --entrypoint magick -v "$(CURDIR)/scripts/demo/out:/out" vev-demo \
36+ -size 1200x750 xc:black -fill white \
37+ -draw "roundrectangle 20,20 1179,729 10,10" /out/mask.png
38+ docker run --rm --entrypoint magick -v "$(CURDIR)/scripts/demo/out:/out" vev-demo \
39+ -limit memory 8GiB -limit map 12GiB /out/demo.gif -coalesce \
40+ null: \( /out/mask.png -alpha off \) -compose CopyOpacity \
41+ -layers composite /out/demo-masked.gif
42+ # --lossy above 30 smears the near-identical darks of the window chrome
43+ # (window bar vs terminal background) into visible banding, and the
44+ # conserve-memory fallback for huge inputs roughly doubles the output.
45+ docker run --rm --entrypoint gifsicle -v "$(CURDIR)/scripts/demo/out:/out" vev-demo --no-conserve-memory -O3 --lossy=30 -o /out/demo.gif /out/demo-masked.gif
46+ rm scripts/demo/out/demo-masked.gif scripts/demo/out/mask.png
3247 mkdir -p docs/assets
3348 cp scripts/demo/out/demo.gif docs/assets/demo.gif
@@ -28,6 +28,21 @@ mocks:
28 demo:28 demo:
29 docker build -f scripts/demo/Dockerfile -t vev-demo .29 docker build -f scripts/demo/Dockerfile -t vev-demo .
30 ./scripts/demo/run.sh demo.tape30 ./scripts/demo/run.sh demo.tape
31- docker run --rm --entrypoint gifsicle -v "$(CURDIR)/scripts/demo/out:/out" vev-demo -O3 --lossy=80 -o /out/demo.gif /out/demo.gif31+ # vhs cannot emit a transparent margin, so cut the window out after the
32+ # fact: everything outside the rounded window rect becomes GIF
33+ # transparency. The geometry mirrors demo.tape (1200x750 canvas, margin
34+ # 20, border radius 10); keep them in sync.
35+ docker run --rm --entrypoint magick -v "$(CURDIR)/scripts/demo/out:/out" vev-demo \
36+ -size 1200x750 xc:black -fill white \
37+ -draw "roundrectangle 20,20 1179,729 10,10" /out/mask.png
38+ docker run --rm --entrypoint magick -v "$(CURDIR)/scripts/demo/out:/out" vev-demo \
39+ -limit memory 8GiB -limit map 12GiB /out/demo.gif -coalesce \
40+ null: \( /out/mask.png -alpha off \) -compose CopyOpacity \
41+ -layers composite /out/demo-masked.gif
42+ # --lossy above 30 smears the near-identical darks of the window chrome
43+ # (window bar vs terminal background) into visible banding, and the
44+ # conserve-memory fallback for huge inputs roughly doubles the output.
45+ docker run --rm --entrypoint gifsicle -v "$(CURDIR)/scripts/demo/out:/out" vev-demo --no-conserve-memory -O3 --lossy=30 -o /out/demo.gif /out/demo-masked.gif
46+ rm scripts/demo/out/demo-masked.gif scripts/demo/out/mask.png
32 mkdir -p docs/assets47 mkdir -p docs/assets
33 cp scripts/demo/out/demo.gif docs/assets/demo.gif48 cp scripts/demo/out/demo.gif docs/assets/demo.gif
M README.md +19 -23
@@ -1,17 +1,13 @@
1-# vev
1+<h1 align="center">vev</h1>
22
3-*Norwegian: to weave*
3+<p align="center"><em>Norwegian: to weave</em></p>
44
5-<p align="center"><img src="docs/assets/demo.gif" alt="vev demo: named sessions, splits, stacked panes, floating window, agent bell notification, detach and re-attach" width="800"></p>
6-
7-A terminal multiplexer for Linux and macOS (arm64). One Go binary, a per-user daemon, a thin client.
5+<p align="center">A terminal multiplexer for Linux and macOS.<br>
6+One Go binary, zero runtime dependencies, remote attach that survives bad networks.</p>
87
9-What it is built around:
8+<p align="center"><img src="docs/assets/demo.gif" alt="vev demo: named sessions, splits, stacked panes, floating window, agent bell notification, detach and re-attach" width="800"></p>
109
11-- **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.
12-- **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.
13-- **Nothing but the standard library.** VT emulation, renderer, wire protocol, and UDP transport are all in this repo. Zero runtime dependencies.
14-- **Performance treated as a feature.** A reproducible harness measures 252 scenarios across workloads and transports, including lossy and high-latency UDP.
10+---
1511
1612 ## Install
1713
@@ -42,18 +38,6 @@ vev kill <name> kill a session (--all kills everything)
4238
4339 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.
4440
45-## Scripting
46-
47-`vev cmd` runs control commands against a running daemon; it never starts one. For example:
48-
49-```sh
50-vev cmd split-right
51-vev cmd toast -l warn "build failed"
52-vev cmd list-panes --json
53-```
54-
55-Target a session explicitly with `-s` (`vev cmd -s work new-tab`). Inside a pane, `--self` targets that pane; it cannot be combined with `-s`. Otherwise vev uses `$VEV` inside a pane, then the only live session; ambiguous targets fail. Run `vev cmd --help` for the scriptable command list and `vev cmd <command> --help` for command usage.
56-
5741 ## Keys
5842
5943 No prefix key; everything is Alt.
@@ -74,7 +58,7 @@ The palette does the rest: type a short code (`SPR` split right, `CNT` new tab,
7458 vev attach user@host[:session]
7559 ```
7660
77-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`):
61+SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. 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`):
7862
7963 ```sh
8064 sudo ufw allow 61000:61023/udp
@@ -82,6 +66,18 @@ sudo ufw allow 61000:61023/udp
8266
8367 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).
8468
69+## Scripting
70+
71+`vev cmd` runs control commands against a running daemon; it never starts one. For example:
72+
73+```sh
74+vev cmd split-right
75+vev cmd toast -l warn "build failed"
76+vev cmd list-panes --json
77+```
78+
79+Target a session explicitly with `-s` (`vev cmd -s work new-tab`). Inside a pane, `--self` targets that pane; it cannot be combined with `-s`. Otherwise vev uses `$VEV` inside a pane, then the only live session; ambiguous targets fail. Run `vev cmd --help` for the scriptable command list and `vev cmd <command> --help` for command usage.
80+
8581 ## Configuration
8682
8783 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).
@@ -1,17 +1,13 @@
1-# vev1+<h1 align="center">vev</h1>
2 2
3-*Norwegian: to weave*3+<p align="center"><em>Norwegian: to weave</em></p>
4 4
5-<p align="center"><img src="docs/assets/demo.gif" alt="vev demo: named sessions, splits, stacked panes, floating window, agent bell notification, detach and re-attach" width="800"></p>5+<p align="center">A terminal multiplexer for Linux and macOS.<br>
6-6+One Go binary, zero runtime dependencies, remote attach that survives bad networks.</p>
7-A terminal multiplexer for Linux and macOS (arm64). One Go binary, a per-user daemon, a thin client.
8 7
9-What it is built around:8+<p align="center"><img src="docs/assets/demo.gif" alt="vev demo: named sessions, splits, stacked panes, floating window, agent bell notification, detach and re-attach" width="800"></p>
10 9
11-- **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.10+---
12-- **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.
13-- **Nothing but the standard library.** VT emulation, renderer, wire protocol, and UDP transport are all in this repo. Zero runtime dependencies.
14-- **Performance treated as a feature.** A reproducible harness measures 252 scenarios across workloads and transports, including lossy and high-latency UDP.
15 11
16 ## Install12 ## Install
17 13
@@ -42,18 +38,6 @@ vev kill <name> kill a session (--all kills everything)
42 38
43 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.39 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.
44 40
45-## Scripting
46-
47-`vev cmd` runs control commands against a running daemon; it never starts one. For example:
48-
49-```sh
50-vev cmd split-right
51-vev cmd toast -l warn "build failed"
52-vev cmd list-panes --json
53-```
54-
55-Target a session explicitly with `-s` (`vev cmd -s work new-tab`). Inside a pane, `--self` targets that pane; it cannot be combined with `-s`. Otherwise vev uses `$VEV` inside a pane, then the only live session; ambiguous targets fail. Run `vev cmd --help` for the scriptable command list and `vev cmd <command> --help` for command usage.
56-
57 ## Keys41 ## Keys
58 42
59 No prefix key; everything is Alt.43 No prefix key; everything is Alt.
@@ -74,7 +58,7 @@ The palette does the rest: type a short code (`SPR` split right, `CNT` new tab,
74 vev attach user@host[:session]58 vev attach user@host[:session]
75 ```59 ```
76 60
77-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`):61+SSH bootstraps the connection, then the session runs over UDP and resumes after sleep or Wi-Fi changes. 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`):
78 62
79 ```sh63 ```sh
80 sudo ufw allow 61000:61023/udp64 sudo ufw allow 61000:61023/udp
@@ -82,6 +66,18 @@ sudo ufw allow 61000:61023/udp
82 66
83 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).67 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).
84 68
69+## Scripting
70+
71+`vev cmd` runs control commands against a running daemon; it never starts one. For example:
72+
73+```sh
74+vev cmd split-right
75+vev cmd toast -l warn "build failed"
76+vev cmd list-panes --json
77+```
78+
79+Target a session explicitly with `-s` (`vev cmd -s work new-tab`). Inside a pane, `--self` targets that pane; it cannot be combined with `-s`. Otherwise vev uses `$VEV` inside a pane, then the only live session; ambiguous targets fail. Run `vev cmd --help` for the scriptable command list and `vev cmd <command> --help` for command usage.
80+
85 ## Configuration81 ## Configuration
86 82
87 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).83 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).
M docs/assets/demo.gif +0 -0 binary file: no text diff

binary file: no text diff

M scripts/demo/Dockerfile +1 -1
@@ -14,7 +14,7 @@ RUN pacman -Sy --noconfirm archlinux-keyring \
1414 && sed -i '/^NoExtract.*usr\/share\/man/d' /etc/pacman.conf \
1515 && pacman -Syu --noconfirm \
1616 vhs ttyd ffmpeg chromium \
17- btop fastfetch gifsicle \
17+ btop fastfetch gifsicle imagemagick \
1818 eza neovim man-db man-pages coreutils \
1919 nodejs npm ncurses \
2020 ttf-jetbrains-mono-nerd \
@@ -14,7 +14,7 @@ RUN pacman -Sy --noconfirm archlinux-keyring \
14 && sed -i '/^NoExtract.*usr\/share\/man/d' /etc/pacman.conf \14 && sed -i '/^NoExtract.*usr\/share\/man/d' /etc/pacman.conf \
15 && pacman -Syu --noconfirm \15 && pacman -Syu --noconfirm \
16 vhs ttyd ffmpeg chromium \16 vhs ttyd ffmpeg chromium \
17- btop fastfetch gifsicle \17+ btop fastfetch gifsicle imagemagick \
18 eza neovim man-db man-pages coreutils \18 eza neovim man-db man-pages coreutils \
19 nodejs npm ncurses \19 nodejs npm ncurses \
20 ttf-jetbrains-mono-nerd \20 ttf-jetbrains-mono-nerd \
M scripts/demo/demo.tape +6 -1
@@ -5,7 +5,12 @@ Set FontFamily "JetBrainsMono Nerd Font"
55 Set FontSize 20
66 Set Width 1200
77 Set Height 750
8-Set Padding 0
8+Set Padding 12
9+Set Margin 20
10+Set MarginFill "#161B22"
11+Set WindowBar Colorful
12+Set WindowBarSize 36
13+Set BorderRadius 10
914 Set Theme "GitHub Dark"
1015 Set TypingSpeed 100ms
1116 Set Framerate 30
@@ -5,7 +5,12 @@ Set FontFamily "JetBrainsMono Nerd Font"
5 Set FontSize 205 Set FontSize 20
6 Set Width 12006 Set Width 1200
7 Set Height 7507 Set Height 750
8-Set Padding 08+Set Padding 12
9+Set Margin 20
10+Set MarginFill "#161B22"
11+Set WindowBar Colorful
12+Set WindowBarSize 36
13+Set BorderRadius 10
9 Set Theme "GitHub Dark"14 Set Theme "GitHub Dark"
10 Set TypingSpeed 100ms15 Set TypingSpeed 100ms
11 Set Framerate 3016 Set Framerate 30

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