aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-11 18:45:15 +0200
committerDanilo M. <danix@danix.xyz>2026-09-11 18:45:15 +0200
commitee39bd927e056c2dae9d61230a95a05715b3a8c6 (patch)
tree5539cb2dba13acb2f2b8f607d691f9b9a1dbd858 /bin
parenta77f24d657a6091022754adb1b30750748132216 (diff)
downloadunified-desktop-theme-ee39bd927e056c2dae9d61230a95a05715b3a8c6.tar.gz
unified-desktop-theme-ee39bd927e056c2dae9d61230a95a05715b3a8c6.zip
feat(palette): add five more schemes, and parse-check them all
Adds Catppuccin Frappe and Mocha, which share Macchiato's colour names so their role maps are the same mapping against different values, and Material Ocean, Palenight and Darker, which bring their own names and their own maps. Nine schemes now, all dark. Material ships one accent per hue and three greys where the roles want five depths, so each Material palette carries four interpolated shades, marked as not upstream, and its bright_* terminal slots name the same colours as the normal ones. The selftest now renders every scheme to a temp directory and has rofi parse every theme, not just check that roles resolve. Resolving was never the property that mattered: the Tokyo Night breakage resolved fine and still emitted a palette the themes could not use. Verified the check fails when a name is removed. Catppuccin Latte is deliberately absent. It is light, and that is not a role remap: homepage is pinned theme: dark and the generated card overrides target its dark-mode class. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gbjA2bmswN8jyPDKzrvqe
Diffstat (limited to 'bin')
-rwxr-xr-xbin/udt-palette46
1 files changed, 43 insertions, 3 deletions
diff --git a/bin/udt-palette b/bin/udt-palette
index 1df0c4f..18dc0b1 100755
--- a/bin/udt-palette
+++ b/bin/udt-palette
@@ -417,8 +417,8 @@ TARGETS = [
]
-def generate(roles_path, out_dir):
- scheme, palette, res, snap_names = load(roles_path)
+def generate(roles_path, out_dir, scheme=None):
+ scheme, palette, res, snap_names = load(roles_path, scheme=scheme)
written = []
for target, fn, template in TARGETS:
dest = out_dir / target
@@ -437,6 +437,44 @@ def generate(roles_path, out_dir):
return scheme, written
+def check_rofi_parses(scheme):
+ """Render `scheme` to a temp dir and have rofi parse every theme.
+
+ The role checks below prove a scheme resolves, not that what it emits is
+ valid. A palette missing a name the themes reference parses as a broken
+ theme and every launcher stops opening, which is how exactly that bug
+ shipped once. rofi needs no display for -dump-theme.
+ """
+ import shutil
+ import subprocess
+ import tempfile
+
+ if not shutil.which("rofi"):
+ return None
+
+ with tempfile.TemporaryDirectory() as tmp:
+ out = Path(tmp)
+ generate(None, out, scheme=scheme)
+ udt = out / "rofi" / "udt"
+ # The themes @import siblings, so they need the whole directory.
+ for extra in (REPO / "rofi" / "udt").glob("*.rasi"):
+ if not (udt / extra.name).exists():
+ shutil.copy(extra, udt / extra.name)
+ # accent.rasi is generated per wallpaper; seed it so themes resolve.
+ (udt / "accent.rasi").write_text("* { accent: #ffffffff; }\n")
+
+ for theme in sorted(udt.glob("*.rasi")):
+ if theme.name in ("palette.rasi", "accent.rasi", "common.rasi"):
+ continue
+ proc = subprocess.run(
+ ["rofi", "-no-config", "-theme", str(theme), "-dump-theme"],
+ capture_output=True, text=True)
+ if "Failed to parse" in proc.stderr:
+ raise AssertionError(
+ f"{scheme}: rofi cannot parse {theme.name}\n{proc.stderr.strip()}")
+ return True
+
+
def selftest():
# Every shipped scheme must satisfy every role, or switching to it breaks
# at install time. load() raises on any role the palette cannot supply.
@@ -449,7 +487,9 @@ def selftest():
assert res["bg"] != res["fg"], f"{scheme}: bg and fg are the same colour"
assert len(snap) >= 5, f"{scheme}: only {len(snap)} snap candidates"
seen[scheme] = set(res)
- print(f" {scheme}: {len(res)} roles, {len(snap)} accents")
+ parsed = check_rofi_parses(scheme)
+ note = "" if parsed else " (rofi not installed, parse unchecked)"
+ print(f" {scheme}: {len(res)} roles, {len(snap)} accents{note}")
# Every scheme must define the SAME roles: a generator asks for a role by
# name, so one scheme missing it would fail only once that scheme was