diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-05 18:49:28 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-05 18:49:28 +0200 |
| commit | a8f0a5f74cf44cb1877154cf71b5c594bc303b05 (patch) | |
| tree | 35d80506ba6afe5e7bf4647da36c93a829b5fd49 | |
| parent | 9c468ffb40f832ff25f23bd6027b93ae8d40c7bf (diff) | |
| download | unified-desktop-theme-a8f0a5f74cf44cb1877154cf71b5c594bc303b05.tar.gz unified-desktop-theme-a8f0a5f74cf44cb1877154cf71b5c594bc303b05.zip | |
feat(accent): GRUB and homepage follow the wallpaper accent
Both sat on the scheme's fixed accent, on the grounds that nothing at boot
and nothing on the dashboard host can read the wallpaper. Neither needs to:
both are generated here, and both already carry the wallpaper itself, so a
wallpaper change already leaves them stale and raises their root steps.
Following the accent adds no step.
udt-accent rewrites the accent in the generated files before it starts
udt-grub and udt-homepage: the accent= value on theme.txt's '# udt:' line,
which grub-theme-udt's build reads for its PNG slices, and a single
--udt-accent line in homepage's custom.css that every accent rule now
reads. Sublime's rewrite moves onto the same helper, which also keeps the
file's mode rather than leaving it 0600.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | AGENTS.md | 14 | ||||
| -rwxr-xr-x | bin/udt-accent | 72 | ||||
| -rwxr-xr-x | bin/udt-palette | 18 |
3 files changed, 78 insertions, 26 deletions
@@ -199,14 +199,16 @@ 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 seven things: `~/.cache/udt/udt-accent.rasi` (rofi), +It writes nine 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 Sublime's installed `Packages/User/udt.sublime-color-scheme`. +substitution in three 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). 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 @@ -215,7 +217,11 @@ started from hypr-theme's autostart, reapplies the accent on every plug-in. Its firmware ignores a mode change unless the lights go off first and settle for a few seconds, hence the off, sleep 3, mode sequence; one second was not enough. -**Sublime's accent is replaced, not substituted into a placeholder.** Its +**Sublime's, GRUB's and the homepage's accents are replaced, not substituted +into a placeholder.** GRUB and the homepage are generated by udt-palette with +the scheme's accent, then rewritten in place, before `udt-grub` and +`udt-homepage` start. Both already refresh on every wallpaper change for the +wallpaper itself, so following the accent adds no root step. Sublime's colour scheme cannot cascade, so the accent has to live in the one generated file rather than arriving as a second one, and `install.sh` copies that file out of the repo. `write_sublime` therefore rewrites the `"accent"` variable @@ -266,7 +272,7 @@ those two and pywal's wallpaper-derived colours come back. Not everything tracks the wallpaper. rofi, quickshell, Hyprland borders, hyprlock's border (through `~/.cache/udt/hyprlock-border.conf`), Sublime's -colour scheme, Firefox and the AK820 Pro keyboard do; conky, Qt, GTK, waybar, hyprlock's panel palette +colour scheme, Firefox, GRUB, the homepage and the AK820 Pro keyboard do; 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 d758ea6..69698d1 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -47,6 +47,12 @@ HYPRLOCK_BORDER_OUTPUT = CACHE / "hyprlock-border.conf" SUBLIME = (Path.home() / ".config" / "sublime-text" / "Packages" / "User" / "udt.sublime-color-scheme") QML_OUTPUT = CACHE / "udt-palette.qml" +# Generated by udt-palette with the scheme's accent and rewritten here with the +# wallpaper's. Both already go stale on a wallpaper change, GRUB's background +# and homepage's staged wallpaper, so following the accent costs no extra step. +REPO = Path(__file__).resolve().parent.parent +GRUB_THEME = REPO.parent / "grub-theme-udt" / "theme.txt" +HOMEPAGE_CSS = REPO / "templates" / "homepage" / "custom.css" # The installed rofi palette, itself generated by udt-palette. Read rather than # duplicated so the QML singleton carries exactly the colours rofi uses, under @@ -381,27 +387,42 @@ def write_colors_json(name, image): subprocess.run(["pywalfox", "update"], capture_output=True, check=False) +def rewrite(path, pattern, value): + """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. + """ + try: + text = path.read_text() + except FileNotFoundError: + return + 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) + + def write_sublime(name): """Substitute the accent into the Sublime colour scheme install.sh copied. In place: Sublime colour schemes do not cascade, so the accent cannot arrive as a second file. Sublime watches Packages/User and reloads a - scheme when its file changes, so no signal is needed. A missing file is not - an error, Sublime simply may not be installed here. - - This reads the installed copy and has nowhere to recover @ACCENT@ from - once substituted, so the previous accent is replaced by matching the - variable line rather than a placeholder. + scheme when its file changes, so no signal is needed. """ - try: - text = SUBLIME.read_text() - except FileNotFoundError: - return + rewrite(SUBLIME, r'("accent":\s*")[^"]*', ACCENTS[name]) - new, count = re.subn(r'("accent":\s*)"[^"]*"', - lambda m: f'{m.group(1)}"{ACCENTS[name]}"', text, count=1) - if count and new != text: - write_atomic(SUBLIME, new) + +def write_grub(name): + """The accent on theme.txt's '# udt:' line, which GRUB's build reads.""" + rewrite(GRUB_THEME, r"^(# udt: .*\baccent=)#[0-9a-fA-F]{6}", ACCENTS[name]) + + +def write_homepage(name): + """The --udt-accent triple every accent in homepage's custom.css reads.""" + r, g, b = bytes.fromhex(ACCENTS[name].lstrip("#")) + rewrite(HOMEPAGE_CSS, r"^(\s*--udt-accent:\s*)[^;]*", f"{r} {g} {b}") def icon_theme(): @@ -520,6 +541,9 @@ def main(image): write_sublime(name) write_sddm(name, image) write_colors_json(name, image) + # Before the helpers below start, which build and stage these two. + write_grub(name) + write_homepage(name) # Hyprland only rereads its config on request, and may not be running. subprocess.run(["hyprctl", "reload"], capture_output=True, check=False) @@ -565,6 +589,26 @@ 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. + with tempfile.TemporaryDirectory() as tmpdir: + f = Path(tmpdir) / "f" + cases = [ + (write_grub, "# udt: bg_alt=#1e2030 accent=#b7bdf8\nx = \"#b7bdf8\"\n", + lambda h: f"# udt: bg_alt=#1e2030 accent={h}\nx = \"#b7bdf8\"\n"), + (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:]))), + ] + for write, start, want in cases: + globals()["GRUB_THEME"] = globals()["HOMEPAGE_CSS"] = f + f.write_text(start) + f.chmod(0o644) + 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" + # A near-grey has an unstable hue angle and must take the fallback. assert snap("#888888") == FALLBACK, snap("#888888") diff --git a/bin/udt-palette b/bin/udt-palette index cdeb571..86c53d6 100755 --- a/bin/udt-palette +++ b/bin/udt-palette @@ -592,7 +592,6 @@ def gen_homepage(res, scheme, palette): sr, sg, sb, _ = res["surface"] ar, ag, ab, _ = res["surface_alt"] br, bg_, bb, _ = res["bg"] - accent = hex6(res["accent"]) return f"""/* {BANNER.format(scheme=scheme)} * @@ -603,7 +602,10 @@ def gen_homepage(res, scheme, palette): .theme-gray {{ {rows} - --color-logo-start: {triple('accent')}; + /* The scheme's accent until udt-accent rewrites this line with the + * wallpaper's. Every accent below reads it, so that is one line to match. */ + --udt-accent: {triple('accent')}; + --color-logo-start: var(--udt-accent); --color-logo-stop: {triple('info')}; }} @@ -622,10 +624,9 @@ def gen_homepage(res, scheme, palette): background-color: rgb({br} {bg_} {bb} / 50%); }} -/* Accent on secondary labels. Fixed, not wallpaper-tracking: this runs on a - * server with no wallpaper to read. */ +/* Accent on secondary labels. */ .theme-gray .text-theme-500 {{ - color: {accent}; + color: rgb(var(--udt-accent)); }} """ @@ -693,9 +694,10 @@ def gen_conky(res, scheme, palette, template): def gen_grub(res, scheme, palette, template): """GRUB: substitute into the theme template in the grub-theme-udt repo. - theme.txt has no include, so the colours are baked in. The accent is the - scheme's fixed one: nothing at boot can read the wallpaper. The PNG slices - that carry colour are drawn by that repo's build script from this output. + theme.txt has no include, so the colours are baked in. The accent lands only + on the '# udt:' line, where the build script reads it to draw the PNG + slices that carry colour. udt-accent rewrites it there with the wallpaper's, + which is no less stale than the background snapshot the build already takes. """ out = template.replace("@SCHEME@", scheme) for role in ("bg", "bg_alt", "bg_deep", "surface", "fg", "fg_dim", |
