thomas.koppandClaude Opus 5 de59a5a904 feat: add preview tuning — hi-DPI PDF previewer and opener setup
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>
2026-09-23 20:12:42 +02:00

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 #282828 were 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:

  1. Yazi calls pdftoppm without -r, so pages render at poppler's 150 DPI default — an A4 page is 1241x1754 px.
  2. [preview] max_width / max_height default 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.

S
Description
Gruvbox Dark theme for Yazi — panel colours, remapped icon table, own tmTheme, and sharper PDF previews
https://git.shron.de/templis/yazi-gruvbox
Readme MIT
54 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 79.1%
Lua 20.9%