Files
Nomarchy/tools/theme-shot.nix
Bernardo Magri 41cd350a52
All checks were successful
Check / eval (push) Successful in 3m6s
feat(theme): capture Boreal picker preview + V2-verify default
Close the honesty gap on #45. Rendered the full Boreal desktop
headlessly (tools/theme-shot.nix, softGL 1920x1080) and confirmed the
session reads as Boreal — aurora wallpaper, Waybar violet accent
(#B79BE8) workspace pill, GeistMono clock, rofi launcher violet
prompt/selection — not Tokyo Night. Added themes/boreal/preview.png
(480x270, matches the existing 21) so the picker row is no longer
plain-name.

theme-shot: park the cursor bottom-right before the desktop shot so the
frame (which doubles as the preview source) is clean; reusable for the
other two missing previews.

Verified: V0 flake check; V2 theme-shot desktop/menu shots viewed;
contrast 24x7 pass. Preview is a headless bare-desktop capture (not the
floating-terminal composition of the others) — hardware recapture noted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 18:29:27 +01:00

81 lines
3.7 KiB
Nix

# Themed-desktop capture harness (item 28 slice c) — boots the full
# desktop headlessly (software-GL Hyprland) at 1920x1080 and QMP-dumps
# two screenshots into $out: desktop.png and menu.png (rofi root open).
# Maintainer tool, NOT a checks.* gate (a full-desktop VM is far too
# heavy for CI). Impure: THEME env picks the theme slug.
# THEME=<slug> nix build --impure -f tools/theme-shot.nix --no-link --print-out-paths
# The guest gets a minimal ~/.nomarchy (just the theme JSON as
# theme-state.json), seeded via tmpfiles BEFORE greetd starts the
# session, so the session's own exec-once wallpaper paint works —
# assets come from the themesDir baked into the tool. Bar, menus AND
# wallpaper render.
let
flake = builtins.getFlake ("git+file://" + toString ../.);
inherit (flake.inputs) nixpkgs home-manager;
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ flake.overlays.default ];
config.allowUnfree = true;
};
slug = let s = builtins.getEnv "THEME"; in if s == "" then "boreal" else s;
in
pkgs.testers.runNixOSTest {
name = "theme-shot-${slug}";
node.pkgsReadOnly = false; # nomarchy sets nixpkgs.config
node.specialArgs = { username = "nomarchy"; };
nodes.machine = { pkgs, lib, ... }: {
imports = [ flake.nixosModules.nomarchy home-manager.nixosModules.home-manager ];
virtualisation.memorySize = 4096;
virtualisation.cores = 4;
# Software GL for Hyprland (GLES/aquamarine) without a host GPU.
hardware.graphics.enable = true;
environment.variables.LIBGL_ALWAYS_SOFTWARE = "1";
boot.initrd.availableKernelModules = [ "virtio_gpu" ];
virtualisation.qemu.options = [ "-vga none" "-device virtio-gpu-pci,xres=1920,yres=1080" ];
users.users.nomarchy = {
isNormalUser = true;
password = "test";
extraGroups = [ "wheel" "video" ];
};
nomarchy.system.stateFile = flake + "/themes/${slug}.json";
# Seed the runtime state (theme-sync reads $NOMARCHY_PATH, default
# ~/.nomarchy) before greetd auto-starts the session, so the
# exec-once `nomarchy-theme-sync wallpaper` finds it and paints.
# NixOS activation creates /home/nomarchy before systemd runs.
systemd.tmpfiles.rules = [
"d /home/nomarchy/.nomarchy 0755 nomarchy users -"
"C /home/nomarchy/.nomarchy/theme-state.json 0644 nomarchy users - ${flake + "/themes/${slug}.json"}"
];
services.greetd.settings.initial_session = {
command = "start-hyprland";
user = "nomarchy";
};
home-manager.useGlobalPkgs = true; # HM must see the overlay'd node pkgs
home-manager.users.nomarchy = {
imports = [ flake.homeModules.nomarchy ];
nomarchy.stateFile = flake + "/themes/${slug}.json";
nomarchy.idle.enable = false; # no lock/dpms during the shot
};
};
testScript = ''
machine.wait_for_unit("multi-user.target")
machine.wait_for_file("/run/user/1000/hypr", 180)
# hyprctl dispatch helper (session env resolved from the runtime dir).
hy = "su - nomarchy -c 'XDG_RUNTIME_DIR=/run/user/1000 HYPRLAND_INSTANCE_SIGNATURE=$(ls /run/user/1000/hypr | head -1) hyprctl dispatch"
# The session exec-once paints the wallpaper (softGL first paint at
# 1920x1080 is slow) give it a generous settle.
machine.sleep(30)
# Park the cursor in the bottom-right corner so it doesn't sit in the
# middle of the desktop shot (that frame doubles as the theme-picker
# preview.png source).
machine.execute(hy + " movecursor 1912 1075' >/dev/null 2>&1")
machine.sleep(1)
machine.screenshot("desktop")
# Open the root menu for the polish review.
machine.execute(hy + " exec nomarchy-menu' >/dev/null 2>&1")
machine.sleep(8)
machine.screenshot("menu")
'';
}