#!/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/udt" # 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/udt/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/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" # 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 # of the palette and this points pywalfox at it, leaving ~/.cache/wal holding # nothing but the link. mkdir -p "$HOME/.cache/wal" ln -sfn "$HOME/.cache/udt/colors.json" "$HOME/.cache/wal/colors.json" 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, a sibling checkout with its own # install.sh, and that script is called here rather than having this one copy # the stylesheet into place. # # The reason is that the palette reaches the bar as THREE generated files, not # one: styles/theme.css, dots.colors for the two dot scripts, which are bash # and cannot read @define-color, and two tints of the Slackware mark, which # has to contrast with the accent pill at rest and the dark pill on hover. # Copying only the stylesheet left the dots and the mark on the previous # scheme's colours after a switch, and a second writer for those files here # would drift from the one over there. # # Called with the palette already generated, and never fatal: a failure in the # bar's install must not stop kitty, Kvantum or Sublime from being themed. The # same reasoning as udt-appthemes below. # # 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. waybar_repo="$repo/../waybar-theme-udt" if [ -x "$waybar_repo/install.sh" ]; then UDT_PALETTE_DONE=1 "$waybar_repo/install.sh" >/dev/null || \ echo "waybar: its install.sh failed, bar left on the old colours" >&2 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" # 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. # # Only the two GTK directories are installed. The upstream theme this is # derived from also ships cinnamon, gnome-shell, metacity, xfwm4 and plank # directories, none of which anything on this desktop reads. gtk_theme="$HOME/.themes/udt" install -Dm644 "$repo/templates/gtk/index.theme" "$gtk_theme/index.theme" for v in 3.0 4.0; do src="gtk3.css" [ "$v" = "4.0" ] && src="gtk4.css" install -Dm644 "$repo/templates/gtk/$src" "$gtk_theme/gtk-$v/gtk.css" # gtk-dark.css is byte-identical to gtk.css upstream; apps ask for one or # the other depending on the prefer-dark setting, so both must exist. install -Dm644 "$repo/templates/gtk/$src" "$gtk_theme/gtk-$v/gtk-dark.css" rm -rf "$gtk_theme/gtk-$v/assets" mkdir -p "$gtk_theme/gtk-$v/assets" cp "$repo"/templates/gtk/assets/*.svg "$gtk_theme/gtk-$v/assets/" done # GTK3 reads the theme name from gsettings on Wayland, not settings.ini: the # file is written too, but gsettings is what actually wins. It was once pinned # to Breeze, silently overriding the file. sed -i 's/^gtk-theme-name=.*/gtk-theme-name=udt/' "$HOME/.config/gtk-3.0/settings.ini" if command -v gsettings >/dev/null 2>&1; then gsettings set org.gnome.desktop.interface gtk-theme 'udt' 2>/dev/null || true fi # GTK4 ignores gtk-theme-name entirely; its stylesheet is imported by # ~/.config/gtk-4.0/gtk.css through this symlink. ln -sfn "$gtk_theme/gtk-4.0" "$HOME/.config/gtk-4.0/theme" # 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 # conky reads one config at startup and never rereads it, so the generated file # has to be in place before the restart below. Symlinked like everything else # the repo owns: the host-specific parts (mpd host, the ${goto} offsets in # conky.text) live in conky.conf.in, so only the colours differ per scheme. mkdir -p "$HOME/.config/conky" ln -sfn "$repo/templates/conky.conf" "$HOME/.config/conky/conky.conf" # Reload what is running. Writing the configs is not enough: waybar, kitty and # Hyprland reread on a signal, 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" # 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 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"