theming: stop fish repainting the palette, drive hyprland via eval
fish's stylix target sources base16-fish, which printf's OSC 4 and OSC
10/11 with build-time colours at every shell start — it painted the old
scheme back over ghostty on every reload, and again in each new window.
hyprctl keyword is legacy-parser only and refuses under the lua config
("Use eval."); emit hl.config lua and hyprctl eval it instead.
theme-apply now logs to ~/.local/state/theme-apply.log: quickshell starts
it with execDetached, which drops stdio, so stderr went nowhere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
8e33a7ba12
commit
40f795db7b
1 changed files with 52 additions and 35 deletions
|
|
@ -270,26 +270,28 @@ let
|
||||||
theme[process_end]="${base08}"
|
theme[process_end]="${base08}"
|
||||||
'');
|
'');
|
||||||
|
|
||||||
# One `<key> <value>` per line, applied by theme-apply with a separate
|
# Lua, fed to `hyprctl eval`. NOT `hyprctl keyword`, which answers
|
||||||
# `hyprctl keyword` each. NOT `hyprctl --batch`: a batch reports one
|
# "keyword can't work with non-legacy parsers. Use eval."
|
||||||
# combined result, so a single rejected key silently takes the rest of the
|
# under the Lua config this repo uses (settings/hyprland.nix sets
|
||||||
# colours with it. Per-key, a bad one names itself in the journal and the
|
# configType = "lua"). eval runs in the live VM with isDynamicParse() true,
|
||||||
# other nine still land.
|
# so hl.config schedules the refresh that actually repaints the borders.
|
||||||
#
|
#
|
||||||
# Mirrors the colour keys set statically in settings/hyprland.nix, which
|
# Keys are dot-joined here, matching how the Lua config manager flattens
|
||||||
# stay the build-time fallback until theme-apply first runs.
|
# nested tables — the same shape settings/hyprland.nix already declares,
|
||||||
hyprKeywords = s: pkgs.writeText "hypr.keywords" ''
|
# which stays the build-time fallback until theme-apply first runs.
|
||||||
general:col.active_border rgb(${s.base04})
|
#
|
||||||
general:col.inactive_border rgb(${s.base03})
|
# One line, not pretty-printed: hyprctl joins its argv into the request
|
||||||
decoration:shadow:color rgba(${s.base00}99)
|
# string, so keeping the payload free of newlines keeps it intact.
|
||||||
group:col.border_active rgb(${s.base04})
|
hyprLua = s: pkgs.writeText "hypr.lua" ("hl.config({" + lib.concatStringsSep "," [
|
||||||
group:col.border_inactive rgb(${s.base03})
|
''general={["col.active_border"]="rgb(${s.base04})",["col.inactive_border"]="rgb(${s.base03})"}''
|
||||||
group:col.border_locked_active rgb(${s.base05})
|
''decoration={shadow={color="rgba(${s.base00}99)"}}''
|
||||||
group:groupbar:text_color rgb(${s.base05})
|
(''group={["col.border_active"]="rgb(${s.base04})",''
|
||||||
group:groupbar:col.active rgb(${s.base04})
|
+ ''["col.border_inactive"]="rgb(${s.base03})",''
|
||||||
group:groupbar:col.inactive rgb(${s.base03})
|
+ ''["col.border_locked_active"]="rgb(${s.base05})",''
|
||||||
misc:background_color rgb(${s.base00})
|
+ ''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
|
# A featherweight icon theme that inherits Papirus-Dark and overrides only
|
||||||
# the folder faces, rather than a recoloured copy of the whole 200MB tree
|
# 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 ${gtk4Css s} $out/gtk4.css
|
||||||
cp ${ghosttyConf s} $out/ghostty.conf
|
cp ${ghosttyConf s} $out/ghostty.conf
|
||||||
cp ${btopTheme s} $out/btop.theme
|
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 "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
|
cp ${pkgs.writeText "zen.css" (stylixize s (builtins.readFile "${inputs.self}/templates/zen-userChrome.css"))} $out/zen.css
|
||||||
ln -s ${iconTheme s} $out/icons
|
ln -s ${iconTheme s} $out/icons
|
||||||
|
|
@ -360,7 +362,7 @@ let
|
||||||
check gtk3.css '@define-color window_bg_color'
|
check gtk3.css '@define-color window_bg_color'
|
||||||
check gtk4.css 'adw-gtk3/gtk-4.0/gtk.css'
|
check gtk4.css 'adw-gtk3/gtk-4.0/gtk.css'
|
||||||
check ghostty.conf '^background = #'
|
check ghostty.conf '^background = #'
|
||||||
check hypr.keywords 'col.active_border rgb('
|
check hypr.lua 'col.active_border"]="rgb('
|
||||||
check vesktop.css '^ --bg-4: #'
|
check vesktop.css '^ --bg-4: #'
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
@ -385,9 +387,15 @@ let
|
||||||
theme="$1"
|
theme="$1"
|
||||||
[ -d "$theme" ] || exit 0
|
[ -d "$theme" ] || exit 0
|
||||||
|
|
||||||
# One line per run, so the journal answers "did it fire, and with what?"
|
# Everything from here lands in one log, truncated per run. NOT the
|
||||||
# without any guessing: journalctl --user -u quickshell | grep theme-apply
|
# journal: quickshell starts this with execDetached, which drops the
|
||||||
echo "theme-apply: $theme" >&2
|
# 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}
|
ln -sfn "$theme" ${lib.escapeShellArg stateLink}
|
||||||
|
|
||||||
|
|
@ -409,16 +417,12 @@ let
|
||||||
cp -r "$theme"/icons/. "$icons"/
|
cp -r "$theme"/icons/. "$icons"/
|
||||||
chmod -R u+w "$icons"
|
chmod -R u+w "$icons"
|
||||||
|
|
||||||
# Hyprland takes its colours live, one keyword at a time. hyprctl answers
|
# Hyprland takes its colours live. `hyprctl eval`, not `hyprctl keyword`:
|
||||||
# "ok" and exits 0 even for a key it did not like, so the reply is what
|
# keyword is a legacy-parser command and refuses outright under the Lua
|
||||||
# gets checked — anything else is named in quickshell's journal
|
# config. eval answers "ok", or a string naming every key it rejected —
|
||||||
# (journalctl --user -u quickshell) instead of vanishing.
|
# hl.config carries on past a bad key, so the rest still land.
|
||||||
while read -r key value; do
|
reply=$(${pkgs.hyprland}/bin/hyprctl eval "$(cat "$theme/hypr.lua")" 2>&1)
|
||||||
[ -n "$key" ] || continue
|
[ "$reply" = "ok" ] || echo "hyprctl eval -> $reply"
|
||||||
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"
|
|
||||||
|
|
||||||
# Ghostty holds its config in one process (gtk-single-instance), so a new
|
# Ghostty holds its config in one process (gtk-single-instance), so a new
|
||||||
# window inherits whatever the daemon parsed at startup — reopening a
|
# window inherits whatever the daemon parsed at startup — reopening a
|
||||||
|
|
@ -440,7 +444,7 @@ let
|
||||||
# dev.fred.spotify) — those windows are the same process.
|
# dev.fred.spotify) — those windows are the same process.
|
||||||
if ${pkgs.hyprland}/bin/hyprctl -j clients 2>/dev/null \
|
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
|
| ${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
|
else
|
||||||
${pkgs.systemd}/bin/systemctl --user restart ghostty-daemon.service 2>/dev/null || true
|
${pkgs.systemd}/bin/systemctl --user restart ghostty-daemon.service 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
@ -489,6 +493,19 @@ in
|
||||||
stylix.targets.ghostty.enable = false;
|
stylix.targets.ghostty.enable = false;
|
||||||
stylix.targets.btop.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.
|
# Everything stylix's gtk target did apart from writing gtk.css.
|
||||||
gtk.theme = { package = pkgs.adw-gtk3; name = "adw-gtk3"; };
|
gtk.theme = { package = pkgs.adw-gtk3; name = "adw-gtk3"; };
|
||||||
# Explicitly null, not merely unset: home-manager defaults gtk4.theme to
|
# Explicitly null, not merely unset: home-manager defaults gtk4.theme to
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue