preview/ holds a yazi.toml and a pdf-hidpi plugin that fix unreadable PDF previews. Yazi calls pdftoppm without -r (150 DPI) and then caps the image at 600x900, so pages are downscaled and upscaled again; the plugin renders at 300 DPI as PNG and the cap goes to 1600x2400, turning that into supersampling. Previewer and preloader both have to be overridden — they share the cache file. README documents this plus what video previews can and cannot do: the preview is a single ffmpeg keyframe by design, J/K scrubs it, and the opener offers mpv --vo=kitty with shared-memory transfer for inline playback. Targets the 26.9 plugin API (fs.cha, not main's fs.stat). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
yazi-gruvbox
A Gruvbox Dark theme.toml for
Yazi — including the icon table, so the
devicons in front of your files stop looking like a paint spill on an otherwise
muted colour scheme.
Written against Yazi 26.9. It uses the current schema ([mgr], [tabs],
[pick], [cmp], [indicator], …), not the pre-25.x one — older Gruvbox
themes floating around still use [manager] and [select] and will throw
errors on a current Yazi.
What it covers
Panel chrome, status bar, tabs, mode indicator, which-key, confirm/spot/notify popups, picker, input, completion, task manager, help — plus:
File and directory names ([filetype]):
| directories | #83a598 blue |
| regular files | #ebdbb2 fg1 |
| executables | #b8bb26 green |
| archives | #fe8019 orange |
| images | #fabd2f yellow |
| audio / video | #d3869b purple |
| PDF / doc | #8ec07c aqua |
| orphan symlinks | red background |
Icons ([icon]): all 725 entries of Yazi's preset table, every glyph colour
remapped to its nearest Gruvbox tone.
Syntax highlighting (gruvbox-dark.tmTheme): 39 scope rules for the file
preview, written from scratch against
morhetz/gruvbox's own vim highlight links
— comments gray italic, strings green, constants purple, keywords red,
identifiers blue, functions green bold, types yellow, preprocessor aqua,
operators plain foreground.
How the icon colours were derived
Yazi's preset ships 725 icon entries using 221 distinct colours from the nvim-tree/devicons palette — saturated web colours that clash badly with Gruvbox's muted ground.
regen-icons.py maps each of them onto the Gruvbox palette:
- Nearest neighbour in Oklab, so the match is perceptual rather than a naive RGB distance.
- Lightness weighted down (0.7) in the distance metric, so hue drives the result. Docker blue stays blue, Ruby red stays red, instead of everything collapsing toward whatever tone happens to be equally bright.
- Chromatic and greyish sources are matched from separate pools (split at Oklab chroma 0.04). Without that split a vivid magenta can land on grey, and white can land on orange.
- Low-contrast targets are excluded.
#cc241d(2.69:1) and#7c6f64(3.03:1) on#282828were dropped from the target palette — icons are non-text, so 3:1 is the bar. The dimmest icon now sits at 3.48:1.
221 colours end up on 19 Gruvbox tones. Icons stay colourful and stay distinguishable; they just stop shouting.
Install
git clone https://git.opennerds.org/templis/yazi-gruvbox
cp yazi-gruvbox/theme.toml yazi-gruvbox/gruvbox-dark.tmTheme ~/.config/yazi/
theme.toml overrides Yazi's built-in theme-dark.toml; anything it does not
set keeps the default.
Both files are needed — theme.toml points at the .tmTheme for syntax
highlighting in file previews.
Sharper previews (optional)
Not part of the colour scheme, but it belongs in the same corner of
~/.config/yazi — the files are under preview/.
cp preview/yazi.toml ~/.config/yazi/yazi.toml
cp -r preview/plugins/pdf-hidpi.yazi ~/.config/yazi/plugins/
ya cache clear
Why PDFs look mushy by default
Two bottlenecks stack up:
- Yazi calls
pdftoppmwithout-r, so pages render at poppler's 150 DPI default — an A4 page is 1241x1754 px. [preview] max_width/max_heightdefault to 600x900. The page is squeezed to 600 px wide and then scaled back up to fill the pane.
On top of that the page is JPEG-compressed at quality 75, which smears glyph edges.
preview/yazi.toml raises the cap to 1600x2400, and the pdf-hidpi plugin is
Yazi's own PDF previewer with two changes: -r 300 and PNG instead of JPEG.
The page now renders at 2481x3508 and is downscaled into the cap, so the result
is supersampled rather than upscaled — and lossless where the glyphs are.
Both the previewer and the preloader must be overridden. They write the
same cache file, so replacing only one leaves the 150 DPI version in place.
preview/yazi.toml does both.
Yazi has no zoom. What it has is J / K, which turn PDF pages in the
preview, and whatever key you bound to maximise the preview pane.
Videos
The video preview is one still frame by design: ffmpeg pulls a keyframe
at 5 % of the runtime and Yazi shows it as an image. Nothing animates, and no
setting changes that.
What does work:
| Key | |
|---|---|
J / K |
scrub the preview through the file, ±5 % per press |
Tab |
spot panel — codec, resolution, duration |
Enter / o |
open in an mpv window |
O |
pick an opener — including Play here (Kitty) |
The last one is the [opener] block in preview/yazi.toml: it runs
mpv --vo=kitty --vo-kitty-use-shm=yes, which draws the video into the
terminal pane through the Kitty graphics protocol. Yazi blocks until mpv
exits (q). Shared-memory transfer keeps it watchable; without it every frame
goes through the tty as base64 and it crawls. It is still a terminal doing a
framebuffer's job — fine for checking what a clip is, not for watching it.
Defining play replaces Yazi's preset list rather than merging into it, which
is why mpv appears twice there: once windowed, once inline.
Keeping the icon table current
The [icon] block replaces Yazi's preset table wholesale. Icons added by
later Yazi releases will not show up until it is regenerated:
git clone --depth 1 https://github.com/sxyazi/yazi
awk '/^\[icon\]/{f=1} f' yazi/yazi-config/preset/theme-dark.toml > icons-src.toml
./regen-icons.py icons-src.toml # writes icon-gruvbox.toml
Then replace the [icon] block in theme.toml with the generated file. The
script prints which Gruvbox tone each source colour landed on.
regen-tmtheme.py does the same for gruvbox-dark.tmTheme — edit the palette
or the scope rules at the top of the script and re-run it.
Both generators and the pdf-hidpi plugin target the 26.9 plugin API. If
you build against Yazi's main branch instead of the tag you run, you will hit
renamed functions — fs.cha() was fs.stat() on main — and the error only
surfaces at preview time.
Self-contained
Everything needed is in this repository — nothing has to be fetched from a third-party theme repo, and no file here carries a copyleft licence.
Licence
MIT — see LICENSE. The .tmTheme is original work; the derived icon data is
MIT as well. Notices in THIRD-PARTY.md.