feat(menu): visual theme picker — rofi preview grid

Replace the plain-text `nomarchy-menu theme` list with a grid of real
desktop previews. The grid, the Name→slug map and the active `✓` mark
are generated at eval time from the preset JSONs (`readDir` + `fromJSON`
in rofi.nix); "active" is just `t.slug`, since every switch rebuilds the
menu. Grouped dark-first then light in one scrollable grid (the previews
make the mode obvious — no light/dark submenu split), `flow: horizontal`
so Down scrolls row-by-row.

Previews are committed as themes/<slug>/preview.png at 480×270 (~2.4 MB
for all 21). rofi's element-icon `size` is single-value → a *square*
cell that *contains* the icon, so a 16:9 preview would letterbox; a
build-time imagemagick step centre-crops each to a square so it fills
the cell edge-to-edge (source stays 16:9, crop is reversible). The
window width is derived from `themeGridIconW` so a column is exactly the
icon side — no slack margins. Themes without a preview degrade to a
plain-name row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 17:26:39 +01:00
parent 70334e68bb
commit e1cf190dd2
23 changed files with 123 additions and 15 deletions

View File

@@ -88,19 +88,30 @@ how to override it. Items marked ✓ are shipped.
green inputbar, yellow bottom-border). Remaining: author `.rasi`
identities for the other four ported themes if/when they want one (the
generated palette theme is the default and looks fine)
- **Visual theme picker (preview thumbnails):** replace the plain-text theme
list in `nomarchy-menu theme` with a rofi **icon grid of real desktop
previews** — each theme shown as a screenshot of its themed desktop (waybar
+ a floating terminal), name beneath, the active theme marked, with the
`↩ Back` entry. **Workflow:** Bernardo captures the previews on real
hardware and commits them as `themes/<slug>/preview.png`; a build-time
derivation downscales each to a small thumbnail (imagemagick), and the
picker loads it as the rofi element icon — rofi already renders image files,
which is how the Papirus menu icons work, so a `.png` path in the icon slot
Just Works. Grid layout via a per-invocation `-theme-str` (`listview {
columns; }` + a larger `element-icon`). Graceful fallback when a theme has
no `preview.png` yet: its wallpaper (`backgrounds/`), an accent swatch, or
the plain name — so it degrades cleanly as previews are added incrementally.
- **Visual theme picker (preview thumbnails):** `nomarchy-menu theme` is now
a rofi **icon grid of real desktop previews** instead of a plain-text list —
each theme a screenshot of its themed desktop (waybar + floating terminal),
pretty name beneath, **grouped dark-first then light** in one scrollable grid
(the previews make the mode obvious, so no light/dark submenu split), the
active theme marked `✓`, ending in `↩ Back`. The grid, the Name→slug map and
the active mark are all **generated at eval time** from the preset JSONs in
`rofi.nix` (`builtins.readDir` + `fromJSON`); "active" is just `t.slug`, since
every switch rebuilds the menu. Grid layout via a per-invocation `-theme-str`
(`listview { columns: 3; flow: horizontal; }` so Down scrolls row-by-row, +
vertical `element` cards, name centred below). **Sizing gotcha (learned the
hard way):** rofi's `element-icon` `size` is a **single value → a square
cell** (a two-value `WxH` is silently collapsed), and the icon is *contained*
in that square. So a 16:9 preview letterboxes (theme-coloured bands top/
bottom), and a cell can't be shorter-than-square without the bands returning.
The fix: a build-time imagemagick step **centre-crops each preview to a
square** so it fills the cell edge-to-edge; the window width is derived so a
column is exactly the icon side (no slack margins). One knob, `themeGridIconW`
(240px), drives both icon and window. **Workflow:** Bernardo captures previews
on real hardware and commits them as `themes/<slug>/preview.png`, **already
downscaled to 480×270** (~2.4 MB total for all 21, vs ~28 MB full-res) — the
source stays 16:9 and untouched; only the *displayed* thumb is squared at
build, so the crop is reversible. Graceful fallback when a theme has no
`preview.png`: a plain-name row, so it degrades cleanly.
(A headless VM-render route — `runNixOSTest` + software-GL Hyprland
(`LIBGL_ALWAYS_SOFTWARE` on virtio-gpu) + `machine.screenshot()` QMP
framebuffer dump — was prototyped 2026-06-19 and **works** (themed waybar