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 --- AGENTS.md | 14 ++++++++++---- bin/udt-accent | 46 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9576bc6..068587e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -199,16 +199,22 @@ One colour moves: `@accent`. `bin/udt-accent` reads the current wallpaper, extracts its signature colour, and snaps it to the nearest of ten Catppuccin accents by perceptual hue in CIELAB. `wallp` calls it on every wallpaper change. -It writes nine things: `~/.cache/udt/udt-accent.rasi` (rofi), +It writes ten things: `~/.cache/udt/udt-accent.rasi` (rofi), `~/.cache/udt/udt-border.lua` (Hyprland), `~/.cache/udt/hyprlock-border.conf` (hyprlock, the same three hues as the window border, as a hyprlang gradient), `~/.cache/wal/colors.json` (Firefox, via pywalfox), `~/.cache/udt/udt-palette.qml` (quickshell), `~/.local/share/udt/sddm-theme.conf` (the SDDM greeter, palette plus wallpaper, read through a root-made `theme.conf.user` symlink), and the accent -substitution in three generated files: Sublime's installed +substitution in four generated files: Sublime's installed `Packages/User/udt.sublime-color-scheme`, grub-theme-udt's `theme.txt` (its -`# udt:` line) and `templates/homepage/custom.css` (its `--udt-accent` line). +`# udt:` line), `templates/homepage/custom.css` (its `--udt-accent` line) and +conky-theme-udt's `conky.conf` (its `color1` and `color5` lines, the `heading` +role, which is the scheme accent in every scheme, and `highlight`, the icons +and bars, which takes the accent's next hue on the wheel, the border's leading +neighbour, so it stays apart from the headings). conky watches that file's inode with +inotify and reloads on a write, so it is rewritten in place: an atomic rename +would end the watch silently. It then starts `udt-homepage` and `udt-grub` detached, for the two outputs that need a root step on another filesystem or host, and `udt-keyboard`, which lights an Ajazz AK820 Pro in the accent through `ak820-ctl` (a no-op without @@ -328,7 +334,7 @@ profile, and the new tab stays blank. Not everything tracks the wallpaper. rofi, quickshell, Hyprland borders, hyprlock's border (through `~/.cache/udt/hyprlock-border.conf`), Sublime's -colour scheme, Firefox, GRUB, the homepage, the AK820 Pro keyboard, the LED strip and the TRADFRI bulbs do; conky, Qt, GTK, waybar, hyprlock's panel palette +colour scheme, Firefox, GRUB, the homepage, conky's headings and icons, the AK820 Pro keyboard, the LED strip and the TRADFRI bulbs do; the rest of conky, Qt, GTK, waybar, hyprlock's panel palette (from `templates/hyprlock/hyprlock-palette.conf`) and Sublime's UI chrome sit on the scheme's accent, lavender by default, which is also the fallback when a wallpaper is too grey to snap. Qt and GTK apps only reread a theme on restart, 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