diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-11 10:34:42 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-11 10:34:42 +0200 |
| commit | f04a1f1dae245e9025344955dbc7e2d18339150b (patch) | |
| tree | b06e9bb68db9941cf143698d5ecb67a08288428c /bin/udt-accent | |
| parent | c9f097f0f5c8f69fed9a8d1e39a596cbc09d75de (diff) | |
| download | unified-desktop-theme-f04a1f1dae245e9025344955dbc7e2d18339150b.tar.gz unified-desktop-theme-f04a1f1dae245e9025344955dbc7e2d18339150b.zip | |
feat: drive the Hyprland border gradient from the wallpaper accent
Replaces the six-stop RGB rainbow with the current accent plus its two
neighbours on the perceptual hue wheel. The borderangle animation rotates a
gradient, so it needs more than one colour to show motion; neighbouring hues
keep that motion legible without leaving the palette.
Emitted as Lua rather than a .conf snippet. Hyprland's Lua parser refuses
hyprctl keyword ("keyword can't work with non-legacy parsers") and has no
source directive, so look_and_feel.lua reads the generated table with dofile()
and falls back to a fixed Macchiato triple when it is absent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6aeE37K4GHBsaM51yLTTq
Diffstat (limited to 'bin/udt-accent')
| -rwxr-xr-x | bin/udt-accent | 56 |
1 files changed, 55 insertions, 1 deletions
diff --git a/bin/udt-accent b/bin/udt-accent index a47460e..fbd6a65 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -14,6 +14,7 @@ including the terminal's ANSI colors. See the design spec for why that matters. import math import os +import subprocess import sys import tempfile from pathlib import Path @@ -37,6 +38,7 @@ FALLBACK = "mauve" MIN_CHROMA = 10.0 OUTPUT = Path.home() / ".cache" / "wal" / "udt-accent.rasi" +BORDER_OUTPUT = Path.home() / ".cache" / "wal" / "udt-border.lua" def _to_lab(hexval): @@ -121,6 +123,45 @@ def write_accent(name): raise +def hue_neighbours(name): + """The accent either side of `name` on the perceptual hue wheel. + + The animated border rotates a gradient, so it needs more than one colour to + show motion. Using the accent's own neighbours keeps the movement visible + while staying inside one region of the palette. + """ + order = sorted(ACCENTS, key=lambda n: _hue(ACCENTS[n])) + i = order.index(name) + return order[(i - 1) % len(order)], order[(i + 1) % len(order)] + + +def write_border(name): + """Write the Hyprland border gradient, atomically. + + Emitted as Lua rather than a .conf snippet: Hyprland's Lua parser refuses + `hyprctl keyword` ("keyword can't work with non-legacy parsers") and has no + source directive, so the config reads this file with dofile() instead. + """ + before, after = hue_neighbours(name) + stops = ", ".join(f'"rgb({ACCENTS[n].lstrip("#")})"' for n in (before, name, after)) + + content = ( + "-- Generated by udt-accent. Do not edit.\n" + f"return {{ {stops} }}\n" + ) + + BORDER_OUTPUT.parent.mkdir(parents=True, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=str(BORDER_OUTPUT.parent), suffix=".tmp") + try: + with os.fdopen(fd, "w") as handle: + handle.write(content) + os.replace(tmp, BORDER_OUTPUT) + except BaseException: + if os.path.exists(tmp): + os.unlink(tmp) + raise + + def main(image): try: name = snap(signature_color(image)) @@ -130,7 +171,13 @@ def main(image): name = FALLBACK write_accent(name) - print(f"{name} {ACCENTS[name]}") + write_border(name) + + # Hyprland only rereads its config on request, and may not be running. + subprocess.run(["hyprctl", "reload"], capture_output=True, check=False) + + before, after = hue_neighbours(name) + print(f"{name} {ACCENTS[name]} (border: {before} .. {name} .. {after})") def selftest(): @@ -146,6 +193,13 @@ def selftest(): # A near-grey has an unstable hue angle and must take the fallback. assert snap("#888888") == FALLBACK, snap("#888888") + # Border neighbours must be distinct from the accent and from each other, + # or the gradient has nothing to animate between. + for name in ACCENTS: + before, after = hue_neighbours(name) + assert len({before, name, after}) == 3, (name, before, after) + assert hue_neighbours("peach") == ("red", "yellow"), hue_neighbours("peach") + print("selftest OK") |
