Files
Nomarchy/modules/home/theme.nix
Bernardo Magri f97054c804 feat: icon themes — Papirus, resolved from the JSON, for GTK + rofi
Ships papirus-icon-theme and resolves an icon-theme name once in
theme.nix → nomarchy.theme.iconTheme: the JSON's optional `icons` field,
else Papirus-Dark/Light picked by `mode`. That single value feeds:
- Stylix `stylix.icons` → gtk.iconTheme (Thunar and GTK apps)
- rofi `show-icons` + `icon-theme` (the launcher now shows app icons)

A preset or theme-state.json can set `icons` to any theme in the icon
package to override per theme.

Verified: flake check green; iconTheme resolves Papirus-Dark for
tokyo-night and Papirus-Light for summer-day; gtk.enable true with
gtk.iconTheme.name set; papirus in home.packages and the live ISO
closure; rofi config.rasi carries icon-theme + show-icons:true. The
rendered icons need a real session to see.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:12:19 +01:00

83 lines
2.9 KiB
Nix

# Nomarchy theming engine.
#
# nomarchy.stateFile (theme-state.json, inside the consuming flake) is
# ingested at evaluation time — pure, because flake files are store
# paths — and exposed to every other module as `config.nomarchy.theme`.
#
# Theme changes are fully Home Manager managed: `nomarchy-theme-sync
# apply <theme>` writes the JSON and runs `home-manager switch`, baking
# everything (Hyprland, Waybar, Ghostty, btop, Stylix) into one
# read-only generation. No runtime patching, no partial states; theme
# history is generation history.
#
# The one runtime exception is the wallpaper (swww is imperative by
# nature): applied at session start, after every switch (hook below),
# and cycled instantly with `nomarchy-theme-sync bg next`.
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy;
themeState = builtins.fromJSON (builtins.readFile cfg.stateFile);
# Defaults guarantee evaluation succeeds on a sparse or older state
# file (e.g. one written before a schema field was added). The shipped
# Tokyo Night preset provides the color/ansi fallbacks; the path is
# relative to this module, so it resolves inside Nomarchy's own flake
# source even when consumed downstream.
preset = builtins.fromJSON (builtins.readFile ../../themes/tokyo-night.json);
defaults = preset // {
fonts = {
mono = "JetBrainsMono Nerd Font";
ui = "Inter";
size = 11;
};
ui = {
gapsIn = 5;
gapsOut = 12;
borderSize = 2;
rounding = 10;
activeOpacity = 1.0;
inactiveOpacity = 0.95;
terminalOpacity = 0.96;
blur = true;
shadow = true;
};
# Icon theme name; "" means "pick by mode" (resolved below). A preset
# or the state file can name any theme in the shipped icon package.
icons = "";
};
parsed = lib.recursiveUpdate defaults themeState;
# Resolve the icon theme once and expose it on nomarchy.theme so both
# the GTK side (stylix.nix) and rofi (rofi.nix) agree. The shipped
# package (papirus-icon-theme) carries the Dark/Light pair.
iconTheme =
if parsed.icons != "" then parsed.icons
else if parsed.mode == "light" then "Papirus-Light"
else "Papirus-Dark";
in
{
config = {
nomarchy.theme = parsed // { inherit iconTheme; };
nomarchy.lib = {
# "#7aa2f7" -> "rgb(7aa2f7)" (Hyprland color syntax)
rgb = c: "rgb(${lib.removePrefix "#" c})";
# "#16161e" -> "rgba(16161eaa)" (Hyprland color + hex alpha)
rgba = c: alpha: "rgba(${lib.removePrefix "#" c}${alpha})";
};
home.packages = [ cfg.package ];
# After a switch the new theme's wallpaper should show without
# logging out. Best-effort: outside a graphical session swww isn't
# running and this is a no-op.
home.activation.nomarchyWallpaper = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
run ${lib.getExe cfg.package} --quiet wallpaper || true
'';
};
}