Repository navigation
Releases: allenguarnes/vomd
Releases · allenguarnes/vomd
Release list
VOMD v1.2.0
Added
- Select and copy text. Drag to select (a drag that starts on a link selects, and a click still follows it), double-click for a word, triple-click for a paragraph, heading, code block or table row,
Shift+click to extend,Ctrl+Afor everything, and drag past the top or bottom of the window to scroll.Ctrl+C(alsoCtrl+Insert,Ctrl+Shift+Cand the Copy key) copies plain text: list items start with-, quoted lines with>, table cells are tab-separated, an image is its alt text and a link its text. The text is made only when a program pastes it, so copying a whole 5 MB document costs no memory until then (X11 INCR and Wayland pipes, in pieces of 64 KiB). On X11 and Wayland a selection made with the mouse (andCtrl+A) is also the primary selection, pasted with a middle click; the newprimary_selection = on|offsetting turns that off. As the window closes on X11 the clipboard manager, if there is one, is asked to keep the clipboard. Needs the newclipboardfeature, on by default; the smallest build leaves it out. Escclears the selection before it quits (after closing the toolbar's menu, as before).- A copy button on code blocks, as on GitHub: while the pointer is over a fenced or indented code block, a button shows in its upper right corner, and a click copies the block's code to the clipboard (not the primary selection, and a selection is left alone). The code is as written, with its tabs, indentation and blank lines, without the language tag, without the quote marks or list indent of what holds the block, and without a line break after the last line; a block of several MB is made only when something pastes it. The button turns into a check mark for two seconds, lights up and shows the hand under the pointer, scales with the zoom and the display, and is left off blocks too small for it. If there is no clipboard, a note says so. Part of the
clipboardfeature, so the smallest build has none.
Fixed
- An image decode that started just as the images wanted changed is counted in the room the image cache keeps free. Before, the cache could briefly fill the memory that decode used.
- With remote images on, a picture near the window could be fetched again and again after a few arrived at once: each body let in was the first thing the next one's room was made from.
Budgets, measured on the release runner
A shared runner is two to three times slower than the machine the time budgets
are for, so start and scroll times are reported here, not gated.
| Metric | Build | Measured | Budget | |
|---|---|---|---|---|
| ✅ | stripped binary | default |
3.91 MB | ≤ 4.0 MB |
| cold start, tiny.md | default |
115.5 ms | ≤ 100 ms (first of 20 runs) | |
| ✅ | warm start, tiny.md | default |
38.7 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | default |
48.7 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | default |
43.9 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | default |
45.4 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | default |
41.8 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | default |
147.1 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | default |
140.6 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | default |
17928 KB | ≤ 30000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 65 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | default |
32528 KB | ≤ 60000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 2295 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | stripped binary | --no-default-features |
2.99 MB | ≤ 3.0 MB |
| ✅ | cold start, tiny.md | --no-default-features |
28.3 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, tiny.md | --no-default-features |
40.4 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | --no-default-features |
49.2 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | --no-default-features |
45.9 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | --no-default-features |
46.6 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | --no-default-features |
44.0 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | --no-default-features |
150.1 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | --no-default-features |
139.9 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | --no-default-features |
18108 KB | ≤ 25000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 64 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | --no-default-features |
35256 KB | ≤ 55000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 2090 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | scroll frame, 4K, p95 | default |
5.45 ms | ≤ 8 ms |
| ✅ | scroll frame, 4K, p95 | --no-default-features |
5.11 ms | ≤ 8 ms |
| ✅ | image RSS, 50 local PNGs | default |
44700 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs | default |
41796 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 16 ms | default |
41876 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 50 ms | default |
41888 KB | ≤ 62768 KB |
| ✅ | image RSS, 16,384 remote JPEGs of 325 KB cut to 178 bytes | default |
28368 KB | ≤ 62768 KB |
VOMD v1.1.0
Added
- Open a file from the window:
Ctrl+Oor the toolbar's folder button shows the desktop's file dialog (the XDG portal's, so KDE's, GNOME's or the GTK one, elsezenityorkdialog), starting in the document's folder. The empty window says so, and offers dropping a file only on X11, where it works. - A Debian package (
vomd_<version>-1_amd64.deb, Debian 12+ and Ubuntu 24.04+) with a man page, and an AppImage for any x86-64 distribution with glibc 2.36 or newer, both attached to each release and checked by CI on Debian 12, Debian 13 and Ubuntu 24.04, on X11 and Wayland. - The Arch packages list the file dialogs Open can use as optional dependencies:
xdg-desktop-portal-gtk,zenityandkdialog. KDE and GNOME already have a portal file chooser.
Fixed
- On X11 the portal's file dialog belongs to the VOMD window, so the desktop keeps it in front of it. (On Wayland it still has no parent window.)
- A portal that crashes or stops answering no longer blocks Ctrl+O: the dialog's calls give up after 25 seconds, and if the portal leaves the session bus while its dialog is up, the window says so and Ctrl+O works again. Before, "A file dialog is already open" stayed until VOMD was restarted.
- The empty window offers dropping a file only on X11, decided by the display VOMD connected to, not by environment variables.
- The toolbar's settings button opens
settings.iniin a terminal editor set as the default (Terminal=true, such as Neovim) throughxdg-terminal-exec. Before, on desktopsxdg-opendoesn't know (Hyprland, Sway) the editor started with no terminal and waited unseen. If the file can't be opened, a note in the window says why. - Release binaries are built on Debian 12 (glibc 2.36), so a glibc older than 2.39 no longer prints "weak version `GLIBC_2.39' not found" at every start.
- Documents over 1 MB, which are parsed in pieces, render the same as when parsed whole when the line that ends an HTML block looks like a table, a code fence or another HTML block, or when a code fence is left open in a list item. Before, the piece could be cut inside a code block or a table, and the rest of the document rendered differently.
Budgets, measured on the release runner
A shared runner is two to three times slower than the machine the time budgets
are for, so start and scroll times are reported here, not gated.
| Metric | Build | Measured | Budget | |
|---|---|---|---|---|
| ✅ | stripped binary | default |
3.73 MB | ≤ 4.0 MB |
| ✅ | cold start, tiny.md | default |
94.5 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, tiny.md | default |
27.4 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | default |
35.0 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | default |
32.4 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | default |
35.1 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | default |
30.3 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | default |
115.8 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | default |
82.8 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | default |
17808 KB | ≤ 30000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 44 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | default |
32556 KB | ≤ 60000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 1391 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | stripped binary | --no-default-features |
2.99 MB | ≤ 3.0 MB |
| ✅ | cold start, tiny.md | --no-default-features |
75.9 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, tiny.md | --no-default-features |
30.9 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | --no-default-features |
68.3 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | --no-default-features |
33.3 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | --no-default-features |
93.6 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | --no-default-features |
30.9 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | --no-default-features |
106.2 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | --no-default-features |
78.1 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | --no-default-features |
18264 KB | ≤ 25000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 42 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | --no-default-features |
32848 KB | ≤ 55000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 1297 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | scroll frame, 4K, p95 | default |
3.60 ms | ≤ 8 ms |
| ✅ | scroll frame, 4K, p95 | --no-default-features |
3.41 ms | ≤ 8 ms |
| ✅ | image RSS, 50 local PNGs | default |
44736 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs | default |
41912 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 16 ms | default |
42036 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 50 ms | default |
41948 KB | ≤ 62768 KB |
| ✅ | image RSS, 16,384 remote JPEGs of 325 KB cut to 178 bytes | default |
28440 KB | ≤ 62768 KB |
VOMD v1.0.0
The first release, for Linux (x86-64; Windows and macOS compile but are unsupported).
Added
- CommonMark and GitHub Flavored Markdown: tables, task lists, strikethrough, footnotes, alerts, extended autolinks, hidden YAML front matter. Raw HTML is stripped, never run; a few tags are styled as on GitHub.
- Our own text layout and renderer on
swash,winitandsoftbuffer, with embedded Inter and JetBrains Mono subsets and a lazy system-font fallback for other scripts and emoji. - Light and dark themes following the system, zoom 50–300%, HiDPI.
- Links: anchors, relative Markdown files in the same window with Back, web links in the browser; a URL preview on hover.
- Reload on
R/F5and when the window regains focus, keeping the reading position. - A slim toolbar (hidden with
B) with an overflow menu on narrow windows. settings.iniwith live apply, written back without losing comments.- Local PNG and JPEG images, decoded lazily at their displayed size under a 32 MB cache.
- Remote images, off by default: loaded per document with
I, through the systemcurl. - A one-line notice for files over 10 MB, which still render.
- Packaging: Arch PKGBUILDs
vomd(from source) andvomd-bin, and a release tarball with a desktop entry, icon and the README's image. - The app icon (an eye over the Markdown mark). The window's Wayland app ID and X11
WM_CLASSarevomd, so the desktop shows it for the window. - The system theme comes from the desktop portal's
color-scheme(Plasma's and GNOME's dark mode, followed live, and back to winit andGTK_THEMEwhen the preference is withdrawn), which say nothing on Wayland. Only the portal's own broadcasts count (not a signal another client addresses to VOMD), none is missed while starting (the first read is reported at once; signals are followed in order, and one that races a read triggers a backed-off re-read, 50 ms doubling to 5 s, until a read completes undisturbed), and the bus going away also returns to winit andGTK_THEME. - Wide layout:
W, the toolbar's button (next to the theme's) orwide = trueinsettings.inilets the text span the window instead of stopping atline_width; it is saved. A change of the column's width (this, or resizing the window) rescales every height, until its block is measured again, by how its estimate changed, so the scrollbar is right while measuring goes on; the estimates read what the prepass stored (20 bytes per block), not the text, so this costs two to three times the plain remeasure (0.5 ms on a 42,000-block document). - The scrollbar also shows while the pointer is over its column.
- Budgets measured by
cargo xtask budgets: binary size, cold and warm start, idle RSS, idle CPU, 4K scroll frame time. CI gates size, memory and idle CPU and reports the times (--timings-advisory). Fuzz targets for the parser, layout and settings.
Budgets, measured on the release runner
A shared runner is two to three times slower than the machine the time budgets
are for, so start and scroll times are reported here, not gated.
| Metric | Build | Measured | Budget | |
|---|---|---|---|---|
| ✅ | stripped binary | default |
3.70 MB | ≤ 4.0 MB |
| ✅ | cold start, tiny.md | default |
46.3 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, tiny.md | default |
37.6 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | default |
47.3 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | default |
43.5 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | default |
44.7 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | default |
42.5 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | default |
145.7 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | default |
136.3 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | default |
17704 KB | ≤ 30000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 64 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | default |
32268 KB | ≤ 60000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 2281 ms) | default |
0 ticks/3s | ≤ 1 tick |
| ✅ | stripped binary | --no-default-features |
2.95 MB | ≤ 3.0 MB |
| ✅ | cold start, tiny.md | --no-default-features |
28.5 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, tiny.md | --no-default-features |
38.3 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, readme.md | --no-default-features |
46.3 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, readme.md | --no-default-features |
43.5 ms | ≤ 50 ms (median of 19) |
| ✅ | cold start, spec.md | --no-default-features |
45.8 ms | ≤ 100 ms (first of 20 runs) |
| ✅ | warm start, spec.md | --no-default-features |
42.4 ms | ≤ 50 ms (median of 19) |
| cold start, huge.md | --no-default-features |
141.3 ms | ≤ 100 ms (first of 20 runs) | |
| warm start, huge.md | --no-default-features |
137.1 ms | ≤ 50 ms (median of 19) | |
| ✅ | idle RSS, 50 KB doc, 1080p | --no-default-features |
18112 KB | ≤ 25000 KB |
| ✅ | idle CPU, 50 KB doc (idle at 65 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | idle RSS, 5 MB doc, 1080p | --no-default-features |
34564 KB | ≤ 55000 KB |
| ✅ | idle CPU, 5 MB doc (idle at 2093 ms) | --no-default-features |
0 ticks/3s | ≤ 1 tick |
| ✅ | scroll frame, 4K, p95 | default |
7.50 ms | ≤ 8 ms |
| ✅ | scroll frame, 4K, p95 | --no-default-features |
4.92 ms | ≤ 8 ms |
| ✅ | image RSS, 50 local PNGs | default |
44632 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs | default |
41744 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 16 ms | default |
41856 KB | ≤ 62768 KB |
| ✅ | image RSS, 50 remote PNGs in view, polled every 50 ms | default |
42024 KB | ≤ 62768 KB |
| ✅ | image RSS, 16,384 remote JPEGs of 325 KB cut to 178 bytes | default |
28896 KB | ≤ 62768 KB |