#!/bin/bash # Install udt by symlink, so the repo stays canonical. # Copyright (C) 2026 Danilo M. # Licensed under the GNU General Public License v2 only. set -euo pipefail repo="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" target="$HOME/.config/rofi/udt" mkdir -p "$target" "$HOME/bin" "$HOME/.cache/wal" # Generate every themed config from the selected scheme first: the rofi palette # symlinked below is one of its outputs, so this has to run before anything is # linked. It fails loudly if a role names a colour the scheme does not define, # which is the whole point of keeping the colours in one place. selector="$HOME/.config/udt/roles.conf" if [ ! -f "$selector" ]; then install -Dm644 "$repo/palette/roles.conf.default" "$selector" echo "seeded $selector" fi "$repo/bin/udt-palette" # 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; do ln -sfn "$repo/rofi/udt/$f" "$target/$f" done # launcher.rasi is generated rather than symlinked: its wallpaper path comes # from the wal cache and cannot be committed. Regenerating it is why re-running # this script after moving the repo is safe. rm -f "$target/launcher.rasi" sed "s|@WPAPER@|$HOME/.cache/wal/wpaper|" \ "$repo/rofi/udt/launcher.rasi" > "$target/launcher.rasi" # Seed the generated accent if it does not exist yet, so themes parse on a # fresh install before any wallpaper has been set. if [ ! -f "$HOME/.cache/wal/udt-accent.rasi" ]; then cp "$repo/rofi/udt/accent.rasi" "$HOME/.cache/wal/udt-accent.rasi" fi ln -sfn "$HOME/.cache/wal/udt-accent.rasi" "$target/accent.rasi" ln -sfn "$repo/bin/udt-accent" "$HOME/bin/udt-accent" ln -sfn "$repo/bin/udt-palette" "$HOME/bin/udt-palette" # 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")" # waybar. The bar is waybar-theme-udt, which keeps its own config directory and # imports styles/theme.css under a fixed name, so this is a copy to one path # with no import to repoint: switching scheme rewrites the file the running bar # already reads. # # The stock ~/.config/waybar/ is deliberately left alone. Nothing launches it # any more, and its stylesheets still reference the per-module role names this # generator no longer emits, so writing a new theme in there would leave it # referencing colours that do not exist rather than simply sitting unused. if [ -d "$HOME/.config/waybar-udt" ]; then install -Dm644 "$repo/templates/waybar/theme.css" \ "$HOME/.config/waybar-udt/styles/theme.css" fi install -Dm644 "$repo/templates/terminal/kitty-theme.conf" \ "$HOME/.config/kitty/current-theme.conf" # 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 # 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" # 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 # udt.sublime-theme it would be an unselected theme that never applies. # Renaming it here rather than in the repo keeps the source name obvious. behave="$subl/Material Behave.sublime-theme" if [ -f "$behave" ] && ! grep -q 'Generated by udt-palette' "$behave"; then cp "$behave" "$behave.pre-udt" echo "sublime: kept your theme override as $(basename "$behave").pre-udt" fi install -Dm644 "$repo/templates/sublime/udt.sublime-theme" "$behave" prefs="$subl/Preferences.sublime-settings" if [ -f "$prefs" ] && ! grep -q '"color_scheme".*udt\.sublime-color-scheme' "$prefs"; then sed -i 's|"color_scheme":[[:space:]]*"[^"]*"|"color_scheme": "Packages/User/udt.sublime-color-scheme"|' "$prefs" echo "sublime: color_scheme switched to udt" fi fi # 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 # needs no change to kvantum.kvconfig and no second theme left behind. 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" # 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 # Stage the homepage stylesheet on the dashboard host. Staged, not installed: # /opt/homepage/config is root-owned and this script does not hold root there. # Never fatal, and never slow: a laptop away from that network must still be # able to install the local theme, so the connect timeout is short and any # failure is a warning. if command -v scp >/dev/null 2>&1; then if scp -q -o ConnectTimeout=5 -o BatchMode=yes \ "$repo/templates/homepage/custom.css" \ homepage:hp-stage/custom.css 2>/dev/null; then echo "homepage: staged custom.css ($scheme)" echo " finish with: ssh homepage 'sudo cp ~/hp-stage/custom.css /opt/homepage/config/custom.css'" else echo "homepage: unreachable, skipped" >&2 fi fi # Reload what is running. Writing the configs is not enough: waybar, kitty and # Hyprland reread on a signal, dunst and conky only on a restart, 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 waybar >/dev/null 2>&1 && killall -SIGUSR2 waybar 2>/dev/null && reloaded="$reloaded waybar" 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" # dunst reads ~/.cache/wal/dunstrc, which pywal renders from its own template # dir. Install the generated config into both: the template so a later `wal` # run keeps it, and the cache file so this install reaches dunst now. Both # still carry @ACCENT@, which udt-accent substitutes just below. if [ -f templates/dunstrc ]; then mkdir -p "$HOME/.config/wal/templates" cp templates/dunstrc "$HOME/.config/wal/templates/dunstrc" cp templates/dunstrc "$HOME/.cache/wal/dunstrc" fi # udt-accent regenerates the accent and restarts dunst itself. It needs a # wallpaper to read; without one the accent keeps its current value. if command -v udt-accent >/dev/null 2>&1 && [ -e "$HOME/.cache/wal/wpaper" ]; then udt-accent "$HOME/.cache/wal/wpaper" >/dev/null 2>&1 && reloaded="$reloaded accent+dunst" fi # conky has no hot-reload and nothing respawns it, so it is restarted only if # it was already running. if pgrep -x conky >/dev/null 2>&1; then pkill -x conky 2>/dev/null sleep 0.5 (conky -d >/dev/null 2>&1 &) reloaded="$reloaded conky" fi [ -n "$reloaded" ] && echo "reloaded:$reloaded" echo "installed:" ls -l "$target" "$HOME/bin/udt-accent"