theming: re-theme every app on wallpaper switch, crossfade wallpaper
settings/theming.nix renders a full config set per wallpaper (GTK, ghostty, btop, hyprland, vesktop, zen, folder icons) from its own base16 palette; theme-apply swaps them in and reloads what can reload. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
42adab61df
commit
1091495674
6 changed files with 560 additions and 227 deletions
|
|
@ -41,146 +41,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
home-manager.users.fred = { config, lib, pkgs, osConfig, ... }: let
|
||||
isDesktop = lib.elem osConfig.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ];
|
||||
c = config.lib.stylix.colors;
|
||||
|
||||
# Pure-Nix hex parsing for color distance calculation.
|
||||
hexDigit = ch: {
|
||||
"0"=0;"1"=1;"2"=2;"3"=3;"4"=4;"5"=5;"6"=6;"7"=7;
|
||||
"8"=8;"9"=9;"a"=10;"b"=11;"c"=12;"d"=13;"e"=14;"f"=15;
|
||||
}.${ch};
|
||||
hexByte = s: hexDigit (builtins.substring 0 1 s) * 16
|
||||
+ hexDigit (builtins.substring 1 1 s);
|
||||
hexToRgb = hex: let h = lib.toLower hex; in {
|
||||
r = hexByte (builtins.substring 0 2 h);
|
||||
g = hexByte (builtins.substring 2 2 h);
|
||||
b = hexByte (builtins.substring 4 2 h);
|
||||
};
|
||||
|
||||
# papirus-folders palette → the front-face fill of each
|
||||
# folder-<color>.svg, read out of the icons themselves.
|
||||
#
|
||||
# NOT the Material Design values the names imply. Papirus paints the
|
||||
# front panel a good deal lighter than its own colour name: nordic's
|
||||
# face is 81a1c1, while 5e81ac is only the small back flap behind it.
|
||||
# Matching a teal accent against the nominal values is what made these
|
||||
# folders come out blue — the real darkcyan is 17x closer than nordic,
|
||||
# the nominal one wasn't.
|
||||
#
|
||||
# Every key here is a variant that actually ships (there is no
|
||||
# "palegreen"; a name with no folder-<name>-*.svg files silently
|
||||
# recolours nothing).
|
||||
pfPalette = {
|
||||
adwaita="93c0ea"; black="4f4f4f"; blue="5294e2"; bluegrey="607d8b";
|
||||
breeze="57b8ec"; brown="ae8e6c"; carmine="a30002"; cyan="00bcd4";
|
||||
darkcyan="45abb7"; deeporange="eb6637"; green="87b158"; grey="8e8e8e";
|
||||
indigo="5c6bc0"; magenta="ca71df"; nordic="81a1c1"; orange="ee923a";
|
||||
palebrown="d1bfae"; paleorange="eeca8f"; pink="f06292"; red="e25252";
|
||||
teal="16a085"; violet="7e57c2"; white="e4e4e4"; yaru="676767";
|
||||
yellow="f9bd30";
|
||||
};
|
||||
|
||||
# Closest papirus-folders color to stylix's accent (base0D), chosen at
|
||||
# eval time via squared RGB distance — no runtime script needed.
|
||||
closestPfColor =
|
||||
let
|
||||
sq = x: x * x;
|
||||
dist = a: b: sq (a.r - b.r) + sq (a.g - b.g) + sq (a.b - b.b);
|
||||
accent = hexToRgb c.base0D;
|
||||
ranked = map (n: { name = n; d = dist accent (hexToRgb pfPalette.${n}); })
|
||||
(builtins.attrNames pfPalette);
|
||||
in (builtins.foldl' (best: x: if x.d < best.d then x else best)
|
||||
(builtins.head ranked)
|
||||
(builtins.tail ranked)).name;
|
||||
|
||||
# Papirus-Dark with folders recolored to the closest matching color.
|
||||
# Replicates papirus-folders' change_color logic directly: for each
|
||||
# colored variant (folder-{color}-*.svg), symlink the generic name
|
||||
# (folder-*.svg) to it. No papirus-folders binary needed.
|
||||
papirusDark = pkgs.runCommand "papirus-dark-${closestPfColor}" {} ''
|
||||
# Copy full icon tree so Papirus-Dark's ../Papirus/… symlinks resolve.
|
||||
tmpthemes=$(mktemp -d)
|
||||
cp -r ${pkgs.papirus-icon-theme}/share/icons/. "$tmpthemes/"
|
||||
chmod -R u+w "$tmpthemes"
|
||||
|
||||
# Recolor folders. Papirus-Dark delegates 48x48 etc. to ../Papirus via
|
||||
# symlinks, so we operate through those resolved paths in the temp tree.
|
||||
for size in 22x22 24x24 32x32 48x48 64x64; do
|
||||
places="$tmpthemes/Papirus-Dark/$size/places"
|
||||
[ -d "$places" ] || continue
|
||||
for f in \
|
||||
"$places/folder-${closestPfColor}"-*.svg \
|
||||
"$places/folder-${closestPfColor}.svg" \
|
||||
"$places/user-${closestPfColor}"-*.svg \
|
||||
"$places/user-${closestPfColor}.svg"; do
|
||||
[ -f "$f" ] || continue
|
||||
fname=$(basename "$f")
|
||||
ln -sf "$fname" "$places/''${fname/-${closestPfColor}/}"
|
||||
done
|
||||
done
|
||||
|
||||
# Copy Papirus-Dark with -L to dereference all symlinks, so the output
|
||||
# is self-contained — no dangling ../Papirus/… pointers.
|
||||
mkdir -p $out/share/icons
|
||||
cp -rL "$tmpthemes/Papirus-Dark" $out/share/icons/
|
||||
'';
|
||||
|
||||
# Raised surfaces are tints of base00, matching quickshell's
|
||||
# Theme.base01/base02 and the GTK overrides in settings/desktop.nix.
|
||||
# Scaling all three channels is what Qt.lighter() does, so all three
|
||||
# toolkits land on the same #2f3440 / #434a5b.
|
||||
tint = f: let
|
||||
ch = k: toString (builtins.floor (builtins.fromJSON c."base00-rgb-${k}" * f));
|
||||
in "rgb(${ch "r"}, ${ch "g"}, ${ch "b"})";
|
||||
|
||||
# Map Material You placeholders to the closest base16 slot in stylix's
|
||||
# palette. The mapping is approximate (Material You has more semantic
|
||||
# colours than base16), but covers the placeholders used in our Zen
|
||||
# userChrome and Vesktop quickCss templates.
|
||||
stylixize = builtins.replaceStrings
|
||||
[
|
||||
"{{colors.primary.default.hex}}"
|
||||
"{{colors.primary_container.default.hex}}"
|
||||
"{{colors.secondary_container.default.hex}}"
|
||||
"{{colors.tertiary_container.default.hex}}"
|
||||
"{{colors.surface.default.hex}}"
|
||||
"{{colors.surface_container.default.hex}}"
|
||||
"{{colors.surface_container_low.default.hex}}"
|
||||
"{{colors.surface_container_high.default.hex}}"
|
||||
"{{colors.on_surface.default.hex}}"
|
||||
"{{colors.on_surface_variant.default.hex}}"
|
||||
"{{colors.outline.default.hex}}"
|
||||
"{{colors.outline_variant.default.hex}}"
|
||||
]
|
||||
[
|
||||
"#${c.base0D}" # primary accent
|
||||
"#${c.base02}" # primary container
|
||||
"#${c.base0C}" # secondary container (cyan)
|
||||
"#${c.base0E}" # tertiary container (purple)
|
||||
"#${c.base00}" # surface (main bg)
|
||||
(tint 1.7) # surface container (urlbar, panels, dialogs)
|
||||
"#${c.base00}" # surface container low
|
||||
(tint 2.4) # surface container high (selected tab, focused urlbar)
|
||||
"#${c.base05}" # on surface (fg)
|
||||
"#${c.base04}" # on surface variant (muted fg)
|
||||
"#${c.base03}" # outline
|
||||
"#${c.base02}" # outline variant
|
||||
];
|
||||
in lib.mkMerge [
|
||||
# Desktop-only: Zen/Vesktop CSS and GTK icon theme
|
||||
(lib.mkIf isDesktop {
|
||||
home.file.".zen/fraudek5.Default Profile/chrome/userChrome.css".text =
|
||||
stylixize (builtins.readFile "${inputs.self}/templates/zen-userChrome.css");
|
||||
|
||||
home.file.".config/vesktop/settings/quickCss.css".text =
|
||||
stylixize (builtins.readFile "${inputs.self}/templates/vesktop-quickCss.css");
|
||||
|
||||
gtk.iconTheme = {
|
||||
package = papirusDark;
|
||||
name = "Papirus-Dark";
|
||||
};
|
||||
})
|
||||
];
|
||||
# Per-app theming that has to follow the RUNTIME wallpaper (GTK, ghostty,
|
||||
# btop, zen, vesktop, folder icons) lives in settings/theming.nix: it
|
||||
# renders one set of config files per wallpaper in walls/ and swaps them
|
||||
# in without a rebuild. Everything above is build-time and stays here.
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue