aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md14
-rwxr-xr-xbin/udt-accent72
-rwxr-xr-xbin/udt-palette18
3 files changed, 78 insertions, 26 deletions
diff --git a/AGENTS.md b/AGENTS.md
index afb685a..e874127 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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",