diff --git a/settings/theming.nix b/settings/theming.nix index 237a035..970d163 100644 --- a/settings/theming.nix +++ b/settings/theming.nix @@ -270,26 +270,28 @@ let theme[process_end]="${base08}" ''); - # One ` ` per line, applied by theme-apply with a separate - # `hyprctl keyword` each. NOT `hyprctl --batch`: a batch reports one - # combined result, so a single rejected key silently takes the rest of the - # colours with it. Per-key, a bad one names itself in the journal and the - # other nine still land. + # Lua, fed to `hyprctl eval`. NOT `hyprctl keyword`, which answers + # "keyword can't work with non-legacy parsers. Use eval." + # under the Lua config this repo uses (settings/hyprland.nix sets + # configType = "lua"). eval runs in the live VM with isDynamicParse() true, + # so hl.config schedules the refresh that actually repaints the borders. # - # Mirrors the colour keys set statically in settings/hyprland.nix, which - # stay the build-time fallback until theme-apply first runs. - hyprKeywords = s: pkgs.writeText "hypr.keywords" '' - general:col.active_border rgb(${s.base04}) - general:col.inactive_border rgb(${s.base03}) - decoration:shadow:color rgba(${s.base00}99) - group:col.border_active rgb(${s.base04}) - group:col.border_inactive rgb(${s.base03}) - group:col.border_locked_active rgb(${s.base05}) - group:groupbar:text_color rgb(${s.base05}) - group:groupbar:col.active rgb(${s.base04}) - group:groupbar:col.inactive rgb(${s.base03}) - misc:background_color rgb(${s.base00}) - ''; + # Keys are dot-joined here, matching how the Lua config manager flattens + # nested tables — the same shape settings/hyprland.nix already declares, + # which stays the build-time fallback until theme-apply first runs. + # + # One line, not pretty-printed: hyprctl joins its argv into the request + # string, so keeping the payload free of newlines keeps it intact. + hyprLua = s: pkgs.writeText "hypr.lua" ("hl.config({" + lib.concatStringsSep "," [ + ''general={["col.active_border"]="rgb(${s.base04})",["col.inactive_border"]="rgb(${s.base03})"}'' + ''decoration={shadow={color="rgba(${s.base00}99)"}}'' + (''group={["col.border_active"]="rgb(${s.base04})",'' + + ''["col.border_inactive"]="rgb(${s.base03})",'' + + ''["col.border_locked_active"]="rgb(${s.base05})",'' + + ''groupbar={text_color="rgb(${s.base05})",'' + + ''["col.active"]="rgb(${s.base04})",["col.inactive"]="rgb(${s.base03})"}}'') + ''misc={background_color="rgb(${s.base00})"}'' + ] + "})"); # A featherweight icon theme that inherits Papirus-Dark and overrides only # the folder faces, rather than a recoloured copy of the whole 200MB tree @@ -343,7 +345,7 @@ let cp ${gtk4Css s} $out/gtk4.css cp ${ghosttyConf s} $out/ghostty.conf cp ${btopTheme s} $out/btop.theme - cp ${hyprKeywords s} $out/hypr.keywords + cp ${hyprLua s} $out/hypr.lua cp ${pkgs.writeText "vesktop.css" (stylixize s (builtins.readFile "${inputs.self}/templates/vesktop-quickCss.css"))} $out/vesktop.css cp ${pkgs.writeText "zen.css" (stylixize s (builtins.readFile "${inputs.self}/templates/zen-userChrome.css"))} $out/zen.css ln -s ${iconTheme s} $out/icons @@ -360,7 +362,7 @@ let check gtk3.css '@define-color window_bg_color' check gtk4.css 'adw-gtk3/gtk-4.0/gtk.css' check ghostty.conf '^background = #' - check hypr.keywords 'col.active_border rgb(' + check hypr.lua 'col.active_border"]="rgb(' check vesktop.css '^ --bg-4: #' ''; @@ -385,9 +387,15 @@ let theme="$1" [ -d "$theme" ] || exit 0 - # One line per run, so the journal answers "did it fire, and with what?" - # without any guessing: journalctl --user -u quickshell | grep theme-apply - echo "theme-apply: $theme" >&2 + # Everything from here lands in one log, truncated per run. NOT the + # journal: quickshell starts this with execDetached, which drops the + # child's stdio, so anything written to stderr goes nowhere. This file is + # the only record of what a switch actually did. + # cat ~/.local/state/theme-apply.log + log="$HOME/.local/state/theme-apply.log" + mkdir -p "$(dirname "$log")" + exec >"$log" 2>&1 + echo "theme-apply $(date -Is): $theme" ln -sfn "$theme" ${lib.escapeShellArg stateLink} @@ -409,16 +417,12 @@ let cp -r "$theme"/icons/. "$icons"/ chmod -R u+w "$icons" - # Hyprland takes its colours live, one keyword at a time. hyprctl answers - # "ok" and exits 0 even for a key it did not like, so the reply is what - # gets checked — anything else is named in quickshell's journal - # (journalctl --user -u quickshell) instead of vanishing. - while read -r key value; do - [ -n "$key" ] || continue - reply=$(${pkgs.hyprland}/bin/hyprctl keyword "$key" "$value" 2>&1) - [ "$reply" = "ok" ] \ - || echo "theme-apply: hyprctl keyword $key $value -> $reply" >&2 - done < "$theme/hypr.keywords" + # Hyprland takes its colours live. `hyprctl eval`, not `hyprctl keyword`: + # keyword is a legacy-parser command and refuses outright under the Lua + # config. eval answers "ok", or a string naming every key it rejected — + # hl.config carries on past a bad key, so the rest still land. + reply=$(${pkgs.hyprland}/bin/hyprctl eval "$(cat "$theme/hypr.lua")" 2>&1) + [ "$reply" = "ok" ] || echo "hyprctl eval -> $reply" # Ghostty holds its config in one process (gtk-single-instance), so a new # window inherits whatever the daemon parsed at startup — reopening a @@ -440,7 +444,7 @@ let # dev.fred.spotify) — those windows are the same process. if ${pkgs.hyprland}/bin/hyprctl -j clients 2>/dev/null \ | ${pkgs.jq}/bin/jq -e 'any(.[].class; test("ghostty|^dev\\.fred\\."))' >/dev/null 2>&1; then - echo "theme-apply: ghostty reload-config failed; open terminals keep the old theme" >&2 + echo "ghostty reload-config failed; open terminals keep the old theme" else ${pkgs.systemd}/bin/systemctl --user restart ghostty-daemon.service 2>/dev/null || true fi @@ -489,6 +493,19 @@ in stylix.targets.ghostty.enable = false; stylix.targets.btop.enable = false; + # The one that made ghostty look unthemeable. stylix's fish target + # sources base16-fish, which is a base16-SHELL script: at every shell + # start it printf's OSC 4 (palette 0-15) and OSC 10/11 (fg/bg) with the + # BUILD-TIME colours, overwriting whatever ghostty just loaded. So a + # wallpaper switch reloaded ghostty correctly and fish immediately + # painted the old scheme back over it — and opening a new window ran + # fish again, which is why reopening never helped either. Only + # selection-background, which base16-fish does not touch, got through. + # + # Off, fish leaves the palette alone and inherits the terminal's, so + # every ANSI-coloured program in a ghostty window follows a switch live. + stylix.targets.fish.enable = false; + # Everything stylix's gtk target did apart from writing gtk.css. gtk.theme = { package = pkgs.adw-gtk3; name = "adw-gtk3"; }; # Explicitly null, not merely unset: home-manager defaults gtk4.theme to