From 6b6ac80c924325f1229bf7f230fd80cde0ce7744 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 21 Oct 2026 19:12:23 +0200 Subject: feat(accent): conky headings and icons follow the wallpaper udt-accent now rewrites two lines of conky-theme-udt's conky.conf: color1 (heading) takes the accent, which every scheme already uses for heading, and color5 (highlight: title icons, bars) takes the accent's next hue on the wheel, the border's leading neighbour, so the two stay apart. Left on the scheme's own hue, highlight kept the dashboard pink whatever the wallpaper. The rewrite is in place, not atomic: conky watches the file's inode, and a rename would end the watch silently. The selftest checks both lines twice over and that the inode survives. Co-Authored-By: Claude Opus 5.5 --- bin/udt-accent | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'bin') diff --git a/bin/udt-accent b/bin/udt-accent index 1388db1..ebbac91 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -54,6 +54,13 @@ QML_OUTPUT = CACHE / "udt-palette.qml" REPO = Path(__file__).resolve().parent.parent GRUB_THEME = REPO.parent / "grub-theme-udt" / "theme.txt" HOMEPAGE_CSS = REPO / "templates" / "homepage" / "custom.css" +# conky's heading role is the scheme accent in every scheme, so it follows the +# wallpaper's. highlight (titles' icons, bars, the moon) left on the scheme's +# own hue kept the dashboard that hue whatever the wallpaper, so it takes the +# accent's next hue on the wheel, the border's leading neighbour: related to +# the heading, still told apart from it. color1 is heading and color5 +# highlight in conky.conf.in. +CONKY_CONF = REPO.parent / "conky-theme-udt" / "conky.conf" # The installed rofi palette, itself generated by udt-palette. Read rather than # duplicated so the QML singleton carries exactly the colours rofi uses, under @@ -429,12 +436,15 @@ def write_firefox(image): print(f"udt-accent: firefox new tab not updated: {exc}", file=sys.stderr) -def rewrite(path, pattern, value): +def rewrite(path, pattern, value, in_place=False): """Replace group 1's tail in the first line matching pattern with value. For generated files whose accent is replaced by matching the line that carries it, not a placeholder, so a second run rewrites the first run's accent. A missing file is not an error: its consumer may not be here. + + `in_place` keeps the inode, for a reader that watches the file itself + rather than its directory: an atomic rename would silently end the watch. """ try: text = path.read_text() @@ -443,7 +453,10 @@ def rewrite(path, pattern, value): new, count = re.subn(pattern, lambda m: m.group(1) + value, text, count=1, flags=re.M) if count and new != text: - write_atomic(path, new, mode=path.stat().st_mode & 0o777) + if in_place: + path.write_text(new) + else: + write_atomic(path, new, mode=path.stat().st_mode & 0o777) def write_sublime(name): @@ -467,6 +480,21 @@ def write_homepage(name): rewrite(HOMEPAGE_CSS, r"^(\s*--udt-accent:\s*)[^;]*", f"{r} {g} {b}") +def write_conky(name): + """The accent on conky.conf's color1 line (heading), its next hue on color5 + (highlight). + + In place: conky watches the config's inode with inotify and reloads on a + write, so no restart is needed, but a rename would end the watch. A torn + read is not a risk in practice, since conky acts on the event at its next + update, long after this small write has finished. + """ + after = hue_neighbours(name)[1] + for slot, hexval in (("color1", ACCENTS[name]), ("color5", ACCENTS[after])): + rewrite(CONKY_CONF, rf"^(\s*{slot}\s*=\s*')#[0-9a-fA-F]{{6}}", + hexval, in_place=True) + + def icon_theme(): """The icon theme the appearance drawer selected, or "" if unreadable. @@ -616,6 +644,7 @@ def main(image): # Before the helpers below start, which build and stage these two. write_grub(name) write_homepage(name) + write_conky(name) # Hyprland only rereads its config on request, and may not be running. subprocess.run(["hyprctl", "reload"], capture_output=True, check=False) @@ -664,8 +693,8 @@ def selftest(): assert '"#f5a97f"' not in second and '"#8bd5ca"' in second assert third == second, "accent substitution is not idempotent" - # GRUB and homepage go through the same match-the-line rewrite, so the - # same twice-over test: the scheme's accent first, then the last run's. + # GRUB, homepage and conky go through the same match-the-line rewrite, so + # the same twice-over test: the scheme's accent first, then the last run's. with tempfile.TemporaryDirectory() as tmpdir: f = Path(tmpdir) / "f" cases = [ @@ -674,15 +703,24 @@ def selftest(): (write_homepage, ".t {\n --udt-accent: 183 189 248;\n}\n", lambda h: ".t {\n --udt-accent: %d %d %d;\n}\n" % tuple(bytes.fromhex(h[1:]))), + (write_conky, + " color1 = '#cc0dbf',\n color4 = '#37daea',\n color5 = '#ef60d9',\n", + lambda h: f" color1 = '{h}',\n color4 = '#37daea',\n color5 = " + f"'{ACCENTS[hue_neighbours(HEX_NAME[h])[1]]}',\n"), ] + HEX_NAME = {v: k for k, v in ACCENTS.items()} for write, start, want in cases: globals()["GRUB_THEME"] = globals()["HOMEPAGE_CSS"] = f + globals()["CONKY_CONF"] = f f.write_text(start) f.chmod(0o644) + inode = f.stat().st_ino for name in list(ACCENTS)[:2]: write(name) assert f.read_text() == want(ACCENTS[name]), f.read_text() assert f.stat().st_mode & 0o777 == 0o644, "rewrite lost the mode" + if write is write_conky: + assert f.stat().st_ino == inode, "conky rewrite replaced the inode" # A near-grey has an unstable hue angle and must take the fallback. assert snap("#888888") == FALLBACK, snap("#888888") -- cgit v1.2.3