fix(theming): skip theme writes and hooks when wallpaper change produces no diff

Issue: #2400
This commit is contained in:
Lysec
2026-04-07 22:29:00 +02:00
parent b8de6e1df2
commit 48fe0f91a5
3 changed files with 33 additions and 8 deletions
+25 -7
View File
@@ -22,7 +22,7 @@ import re
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Optional, Union
from typing import Any, Optional, Tuple, Union
try:
import tomllib
@@ -970,13 +970,15 @@ class TemplateRenderer:
return result
def render_file(self, input_path: Path, output_path: Path) -> bool:
def render_file(self, input_path: Path, output_path: Path) -> Tuple[bool, bool]:
"""Render a template file to an output path.
Returns True if successful, False if skipped due to errors.
Returns (success, wrote) where wrote is False if the file was skipped because
content was already identical (avoids mtime bumps and app reloads).
"""
self._current_file = str(input_path)
success = False
wrote = False
try:
template_text = input_path.read_text()
rendered_text = self.render(template_text)
@@ -984,8 +986,18 @@ class TemplateRenderer:
if self._error_count > 0:
print(f"Skipping {output_path}: template has {self._error_count} error(s)", file=sys.stderr)
else:
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(rendered_text)
out = Path(output_path).expanduser()
out.parent.mkdir(parents=True, exist_ok=True)
skip_write = False
if out.is_file():
try:
if out.read_text() == rendered_text:
skip_write = True
except OSError:
pass
if not skip_write:
out.write_text(rendered_text)
wrote = True
success = True
except FileNotFoundError:
self._log_error(f"Template file not found: {input_path}")
@@ -995,7 +1007,7 @@ class TemplateRenderer:
self._log_error(f"Unexpected error: {e}")
finally:
self._current_file = None
return success
return success, wrote
# --- Custom Colors ---
@@ -1151,7 +1163,13 @@ class TemplateRenderer:
rendered_compare_to = self.render(compare_to)
self.closest_color = find_closest_color(rendered_compare_to, colors_to_compare)
self.render_file(Path(input_path).expanduser(), Path(output_path).expanduser())
ok, wrote = self.render_file(Path(input_path).expanduser(), Path(output_path).expanduser())
if not ok:
continue
# Hooks reload external apps (e.g. Discord); skip when output unchanged
if not wrote:
continue
# Execute pre_hook if specified
pre_hook = template.get("pre_hook")
+4 -1
View File
@@ -23,8 +23,11 @@ Singleton {
if (Settings.data.colorSchemes.useWallpaperColors) {
generateFromWallpaper();
} else if (ColorSchemeService.lastPredefinedSchemeData) {
// Regenerate templates only; skip applyScheme so colors.json and scheme reload stay untouched
// when outputs are unchanged (see template processor skip-identical writes).
generateFromPredefinedScheme(ColorSchemeService.lastPredefinedSchemeData);
} else {
// Re-run predefined scheme templates so {{image}} reflects the new wallpaper path
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme);
}
}
+4
View File
@@ -16,6 +16,9 @@ Singleton {
property string schemesDirectory: Quickshell.shellDir + "/Assets/ColorScheme"
property string downloadedSchemesDirectory: Settings.configDir + "colorschemes"
property string colorsJsonFilePath: Settings.configDir + "colors.json"
// Last successfully parsed predefined scheme JSON (full object). Used to refresh app templates
// on wallpaper changes without re-running applyScheme (avoids rewriting colors.json when unchanged).
property var lastPredefinedSchemeData: null
readonly property string gtkRefreshScript: Quickshell.shellDir + "/Scripts/python/src/theming/gtk-refresh.py"
// prefer-light/prefer-dark only; GTK template post_hook still runs full gtk-refresh.
@@ -203,6 +206,7 @@ Singleton {
}
}
writeColorsToDisk(variant);
lastPredefinedSchemeData = data;
Logger.i("ColorScheme", "Applying color scheme:", getBasename(path));
// Generate templates for predefined color schemes