From 78d7d285a980513101210188ab3da8a4c3b512e8 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 22 Oct 2026 17:25:09 +0200 Subject: install: one function per consumer, runnable alone install.sh was one linear script, so the only way to re-theme kitty after it drifted was to reinstall everything. Each consumer is now a c_ function: `./install.sh kitty gtk` runs only those, no argument runs all of them in definition order, and `--list` prints name, software|hardware and what it themes, read off the function lines themselves. quickshell's Theme status page lists and re-applies consumers through those two calls, so a new consumer is a new function and nothing else. udt-accent now runs before the consumers with a new --no-helpers flag. Before it, every install ran it last and it started homepage, GRUB, keyboard, LED strip, bulbs and TV detached, so re-applying conky also repainted the strip. Those helpers are consumers of their own here, run in the foreground so a re-apply reports how it went; wallp still starts them all on a wallpaper change. Running the accent first means c_sublime fills the template's @ACCENT@ itself, from udt-accent.rasi. Co-Authored-By: Claude Opus 5.5 --- install.sh | 158 +++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 122 insertions(+), 36 deletions(-) (limited to 'install.sh') diff --git a/install.sh b/install.sh index e25e4ec..849d770 100755 --- a/install.sh +++ b/install.sh @@ -7,6 +7,27 @@ set -euo pipefail repo="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" target="$HOME/.config/rofi/udt" +# Each consumer is a c_ function below, run in the order it is defined, +# with what it themes in a comment on the same line, prefixed `hardware:` for +# a device rather than an app. The list is read off the script itself, so +# adding a consumer is adding a function: quickshell's theme page asks for it +# with --list, which prints namesoftware|hardwaredescription. +listing() { + awk '/^c_[a-z0-9_]+\(\) \{ *#/ { + n = $1; sub(/^c_/, "", n); sub(/\(\).*/, "", n) + d = $0; sub(/^[^#]*# */, "", d) + k = "software"; if (sub(/^hardware: */, "", d)) k = "hardware" + print n "\t" k "\t" d + }' "${BASH_SOURCE[0]}" +} +consumers() { listing | cut -f1; } +if [ "${1:-}" = "--list" ]; then listing; exit 0; fi +# Names are checked before anything runs, palette generation included. +all="$(consumers)" +for c in "$@"; do + grep -qx "$c" <<< "$all" || { echo "unknown consumer: $c (have: $(echo $all))" >&2; exit 2; } +done + # Everything this prints also becomes the install tab of quickshell's steps # window when there is something to do, from whichever caller: the usual one # is the appearance panel, not a terminal, so the hints are assumed wanted on @@ -82,6 +103,28 @@ fi # early, and pipefail would read the SIGPIPE as a failed generation. "$repo/bin/udt-palette" | sed -n 1p +# The rofi menus' scripts live in the sibling rofi-scripts repo, which links +# its own. +for f in udt-accent udt-grub udt-keyboard udt-ledstrip udt-bulb udt-tv udt-steps udt-palette udt-machine-name; do + ln -sfn "$repo/bin/$f" "$HOME/bin/$f" +done + +reloaded="" + +# udt-accent regenerates the accent. It needs a wallpaper to read; without one +# the accent keeps its current value. Quickshell watches the palette it writes, +# so notifications and the rest of the shell follow without a restart. It runs +# before every consumer, since udt-palette above has just rewritten conky's, +# GRUB's and the homepage's accent lines with the scheme's own, and the +# hardware consumers below send what it writes. --no-helpers keeps it from +# starting those itself: each is a consumer here, so re-applying conky does +# not also repaint the LED strip. +if [ -e "$HOME/.cache/udt/wpaper" ]; then + "$repo/bin/udt-accent" --no-helpers "$HOME/.cache/udt/wpaper" >/dev/null 2>&1 && + reloaded="$reloaded accent" +fi + +c_rofi() { # launcher and menus # Theme files are symlinked individually; accent.rasi is NOT, it points at the # generated file in the wal cache instead. for f in palette.rasi common.rasi menu.rasi list.rasi grid.rasi powermenu.rasi confirm.rasi screenshot.rasi ssh.rasi; do @@ -102,7 +145,9 @@ if [ ! -f "$HOME/.cache/udt/udt-accent.rasi" ]; then cp "$repo/rofi/udt/accent.rasi" "$HOME/.cache/udt/udt-accent.rasi" fi ln -sfn "$HOME/.cache/udt/udt-accent.rasi" "$target/accent.rasi" +} +c_hyprlock() { # lock screen # hyprlock. The palette is scheme-level, so it is copied like the other # generated configs. The border is wallpaper-level: seeded from the static # fallback so the lock parses before any wallpaper is processed, then rewritten @@ -115,7 +160,9 @@ if [ ! -f "$HOME/.cache/udt/hyprlock-border.conf" ]; then cp "$repo/templates/hyprlock/hyprlock-border.conf" \ "$HOME/.cache/udt/hyprlock-border.conf" fi +} +c_firefox() { # Firefox, through pywalfox # Firefox's bridge is the one consumer that cannot follow: pywalfox hardcodes # /wal/colors.json and honours only XDG_CACHE_HOME, which would move # every cache on the system. So udt-accent writes the real file with the rest @@ -123,33 +170,38 @@ fi # nothing but the link. mkdir -p "$HOME/.cache/wal" ln -sfn "$HOME/.cache/udt/colors.json" "$HOME/.cache/wal/colors.json" - -# The rofi menus' scripts live in the sibling rofi-scripts repo, which links -# its own. -for f in udt-accent udt-grub udt-keyboard udt-ledstrip udt-bulb udt-tv udt-steps udt-palette udt-machine-name; do - ln -sfn "$repo/bin/$f" "$HOME/bin/$f" -done +} # The generated configs for everything that is not rofi. These are copied # rather than symlinked: each app owns its config file and mixes theme keys # with settings that are none of this repo's business. -scheme="$(sed -n 's/^scheme *= *//p' "$selector")" +c_kitty() { # terminal # Written through, not replaced: current-theme.conf is a symlink into the # kitty-conf repo, and install -D would unlink it and leave a plain file behind. mkdir -p "$HOME/.config/kitty" cat "$repo/templates/terminal/kitty-theme.conf" \ > "$HOME/.config/kitty/current-theme.conf" +# kitty rereads its config on a signal. Guarded, like every reload here, +# because it is not necessarily running, and a missing one is not an error. +if pgrep -x kitty >/dev/null 2>&1 && pkill -USR1 -x kitty 2>/dev/null; then + reloaded="$reloaded kitty" +fi +} +c_sublime() { # Sublime Text editor and UI # Sublime Text. Copied into Packages/User, which Sublime watches, so writing it -# reaches a running editor. The file still carries @ACCENT@, which udt-accent -# substitutes below; selecting it is a separate step because color_scheme lives +# reaches a running editor. The template carries @ACCENT@, filled here from the +# accent udt-accent wrote above, and replaced on later wallpaper changes by +# udt-accent itself, matching the line; selecting it is a separate step because color_scheme lives # in Preferences.sublime-settings alongside settings that are none of our # business, so only that one key is rewritten, and only if it is not ours yet. subl="$HOME/.config/sublime-text/Packages/User" if [ -d "$subl" ]; then install -Dm644 "$repo/templates/sublime/udt.sublime-color-scheme" \ "$subl/udt.sublime-color-scheme" + accent="$(sed -n 's/.*accent: \(#[0-9a-fA-F]\{6\}\).*/\1/p' "$HOME/.cache/udt/udt-accent.rasi" 2>/dev/null)" + [ -n "$accent" ] && sed -i "s|@ACCENT@|$accent|" "$subl/udt.sublime-color-scheme" # The UI chrome is an override, and Sublime merges .sublime-theme files by # FILENAME across packages with User last. So it has to be installed under # the name of the theme it layers over, not under a name of its own: called @@ -167,7 +219,9 @@ if [ -d "$subl" ]; then echo "sublime: color_scheme switched to udt" fi fi +} +c_kvantum() { # Qt widget style # Kvantum wants a directory per theme, named after itself, and qt5ct/qt6ct # reach Qt apps through it. The theme is installed under one fixed name rather # than one per scheme: Kvantum caches by name, so reusing it means a switch @@ -176,7 +230,9 @@ kv="$HOME/.config/Kvantum/udt" install -Dm644 "$repo/templates/kvantum/theme.kvconfig" "$kv/udt.kvconfig" install -Dm644 "$repo/templates/kvantum/theme.svg" "$kv/udt.svg" printf '[General]\ntheme=udt\n' > "$HOME/.config/Kvantum/kvantum.kvconfig" +} +c_kde() { # KDE Frameworks apps (kcalc and friends) # KDE Frameworks apps (kcalc and friends) bypass qt6ct twice over off Plasma: # KStyleManager forces Breeze unless kdeglobals names a widgetStyle, and # KColorSchemeManager applies BreezeLight unless kdeglobals names a scheme. @@ -187,7 +243,9 @@ if command -v kwriteconfig6 >/dev/null 2>&1; then kwriteconfig6 --file kdeglobals --group UiSettings --key ColorScheme udt kwriteconfig6 --file kdeglobals --group KDE --key widgetStyle kvantum-dark fi +} +c_gtk() { # GTK3 and GTK4 # GTK3 and GTK4. Installed under one fixed name for every scheme, like Kvantum: # the theme name is set in gsettings and in a symlink, so a per-scheme name # would mean rewriting both on every switch and leaving stale themes behind. @@ -232,12 +290,16 @@ elif ! grep -q '@import url("theme/gtk.css")' "$gtk4_css"; then cat "$gtk4_css.udt-tmp" > "$gtk4_css" rm -f "$gtk4_css.udt-tmp" fi +} +c_qt() { # qt5ct and qt6ct # qt5ct and qt6ct hand Qt apps to Kvantum. Theme keys only: both files also # hold settings and personal lists that are none of this repo's business. ini_merge "$repo/templates/qt-gtk/qt5ct.conf" "$HOME/.config/qt5ct/qt5ct.conf" ini_merge "$repo/templates/qt-gtk/qt6ct.conf" "$HOME/.config/qt6ct/qt6ct.conf" +} +c_nvim() { # neovim colorscheme # neovim. The colorscheme is generated, and selected with one line in # init.vim, rewritten only if it names something else. Like Sublime's # color_scheme key: the rest of init.vim is none of this repo's business. @@ -248,12 +310,16 @@ if [ -f "$nvim_init" ] && ! grep -qx 'colorscheme udt' "$nvim_init"; then sed -i 's/^colorscheme .*/colorscheme udt/' "$nvim_init" grep -qx 'colorscheme udt' "$nvim_init" && echo "nvim: colorscheme switched to udt" fi +} +c_apps() { # Obsidian, Typora, btop, Joplin, Feishin # Obsidian and Typora need more than a copy: the Obsidian snippet has to be # enabled in each vault's appearance.json without disturbing the snippets # already listed there. Skipped silently when neither app is installed. "$repo/bin/udt-appthemes" || true +} +c_conky() { # desktop dashboard # conky rereads its config when it is written in place, but the links below # have to exist for it to find the file at all, and the restart further down # covers a first install and a config replaced by rename. @@ -271,33 +337,6 @@ if [ -d "$conky_repo" ]; then done fi -# Hyprland. hypr-theme is the whole of ~/.config/hypr, linked as one -# directory, and it reads the border this repo generates. A real directory -# there is someone's config: it is left alone rather than replaced. -hypr_repo="$(realpath -m "$repo/../hypr-theme")" -if [ -d "$hypr_repo" ]; then - if [ -L "$HOME/.config/hypr" ] || [ ! -e "$HOME/.config/hypr" ]; then - ln -sfn "$hypr_repo" "$HOME/.config/hypr" - elif [ "$(realpath "$HOME/.config/hypr")" != "$hypr_repo" ]; then - echo "hypr: ~/.config/hypr is a real directory, left alone; move it aside to link hypr-theme" >&2 - fi -fi - -# Reload what is running. Writing the configs is not enough: kitty and -# Hyprland reread on a signal, conky on an in-place write, and rofi -# reads its theme per launch so it needs nothing. Each is guarded, because none -# of them is necessarily running, and a missing one is not an error. -reloaded="" -pgrep -x kitty >/dev/null 2>&1 && pkill -USR1 -x kitty 2>/dev/null && reloaded="$reloaded kitty" -command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 && reloaded="$reloaded hyprland" - -# udt-accent regenerates the accent. It needs a wallpaper to read; without one -# the accent keeps its current value. Quickshell watches the palette it writes, -# so notifications and the rest of the shell follow without a restart. -if command -v udt-accent >/dev/null 2>&1 && [ -e "$HOME/.cache/udt/wpaper" ]; then - udt-accent "$HOME/.cache/udt/wpaper" >/dev/null 2>&1 && reloaded="$reloaded accent" -fi - # conky reloads an in-place write on its own, but its inotify watch is on the # inode, so a first install or a config replaced by rename goes unseen, and a # widget module stays cached until a restart. Nothing respawns it, so it is @@ -323,7 +362,24 @@ if pgrep -x conky >/dev/null 2>&1; then fi reloaded="$reloaded conky" fi +} + +c_hypr() { # Hyprland, window border +# Hyprland. hypr-theme is the whole of ~/.config/hypr, linked as one +# directory, and it reads the border this repo generates. A real directory +# there is someone's config: it is left alone rather than replaced. +hypr_repo="$(realpath -m "$repo/../hypr-theme")" +if [ -d "$hypr_repo" ]; then + if [ -L "$HOME/.config/hypr" ] || [ ! -e "$HOME/.config/hypr" ]; then + ln -sfn "$hypr_repo" "$HOME/.config/hypr" + elif [ "$(realpath "$HOME/.config/hypr")" != "$hypr_repo" ]; then + echo "hypr: ~/.config/hypr is a real directory, left alone; move it aside to link hypr-theme" >&2 + fi +fi +command -v hyprctl >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1 && reloaded="$reloaded hyprland" +} +c_sddm() { # login greeter, root steps # SDDM. The theme code is root-installed, but the palette the greeter reads has # to stay live, so the root block below symlinks the generated file in. Never # run here: /usr/share/sddm is root-owned and this script is not. Only the @@ -347,12 +403,42 @@ $sddm_steps" echo "sddm theme: ${sddm_steps:-up to date}" printf '%s' "$sddm_steps" | "$repo/bin/udt-steps" sddm fi +} +c_grub() { # boot menu, root steps # GRUB. Same shape as SDDM: udt-palette has rendered theme.txt, and udt-grub # builds the theme as the user and prints the root half. udt-accent also runs # it, detached, so a wallpaper change alone rebuilds it too. echo "$repo/bin/udt-grub" +} + +# The rest are what udt-accent starts detached on a wallpaper change. Here they +# run in the foreground, so a re-apply reports how it went; each is a no-op +# without its device or its config, and none of them is fatal to the install. +c_homepage() { # dashboard host, staged for a root copy +"$repo/bin/udt-homepage" || true +} + +c_keyboard() { # hardware: Ajazz AK820 Pro lighting +"$repo/bin/udt-keyboard" || true +} + +c_ledstrip() { # hardware: Tuya LED strip +"$repo/bin/udt-ledstrip" || true +} + +c_bulb() { # hardware: IKEA TRADFRI colour bulbs +"$repo/bin/udt-bulb" || true +} + +c_tv() { # hardware: TV launcher focus highlight +"$repo/bin/udt-tv" || true +} + +# No arguments installs every consumer; names install only those, which is how +# the theme page re-applies one. +for c in ${*:-$all}; do "c_$c"; done echo [ -n "$reloaded" ] && echo "reloaded:$reloaded" -- cgit v1.2.3