# Rofi Unified Theme Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Replace five inconsistent rofi theme systems with one Catppuccin Macchiato palette, three shared layouts, and a wallpaper-driven accent that cannot produce an unreadable result. **Architecture:** A fixed Macchiato palette file plus a single generated `@accent` variable. `udt-accent` extracts the wallpaper's signature color by calling pywal's colorz backend directly (which writes nothing, leaving the terminal's preset colors untouched) and snaps it to one of nine Macchiato accents by perceptual hue in CIELAB. Three layout files import a shared `common.rasi`; eleven call sites are repointed at them. **Tech Stack:** rofi 2.0.0 rasi themes (note: `~/.config/rofi/config.rasi` carries a stale "Version: 1.7.3" comment; `rofi -v` is the authority), Python 3 (stdlib `math`/`sys`/`pathlib` plus `pywal.backends.colorz`), bash. **Repo note:** This project holds the canonical copies under `rofi/` and `bin/`. Installation is by symlink into `~/.config/rofi/udt/` and `~/bin/`, so the working config and the repo never diverge. Tasks 1-8 build and verify; Task 9 installs; Tasks 10-12 migrate call sites. **Design spec:** `docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md` **On verification:** rofi steps come in two kinds. *Syntax* is checkable without a display, and every theme file should be checked this way before it is ever opened: ```bash rofi -no-config -theme /absolute/path/to/theme.rasi -dump-theme >/dev/null ``` Silence means it parsed. A syntax error prints the problem and a line number. Use an absolute path: rofi resolves a bare name against its own theme directories, not the working directory. *Appearance* needs a real window and the user's eye, so those steps run in a terminal on the actual desktop session. Do not judge them by exit code; rofi exits non-zero for ordinary reasons such as dismissal with Escape. --- ### Task 1: Project scaffolding and license **Files:** - Create: `LICENSE`, `README.md`, `.gitignore` - [ ] **Step 1: Fetch the GPLv2 text** ```bash cd ~/Programming/unified-desktop-theme curl -sL https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -o LICENSE head -3 LICENSE ``` Expected: prints ` GNU GENERAL PUBLIC LICENSE` and the version line. - [ ] **Step 2: Create .gitignore** ```bash cat > .gitignore <<'EOF' HANDOFF.md __pycache__/ *.pyc EOF ``` - [ ] **Step 3: Write README.md** ```markdown # unified-desktop-theme A single visual identity for a Hyprland desktop: Catppuccin Macchiato as the fixed base, Noto Sans for UI text, Inconsolata Nerd Font Mono for monospace. The accent color follows the current wallpaper, but is always snapped to a real Macchiato accent, so it can never render as unreadable. Phase 1 covers rofi. Later phases extend the same palette to waybar, dunst, conky, hyprland and quickshell. ## Layout rofi/udt/ the theme files bin/udt-accent wallpaper accent extractor Both are installed by symlink, so the repo holds the canonical copies: ./install.sh ## Design See `docs/superpowers/specs/` for the design spec and the reasoning behind each decision, including why accent extraction is deliberately isolated from the pywal cache. ## License GPLv2 only. See `LICENSE`. ## Development Approach This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback. All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions. The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability. ``` - [ ] **Step 4: Commit** ```bash git add LICENSE README.md .gitignore git commit -m "chore: add GPLv2 license, README and gitignore" ``` --- ### Task 2: The Macchiato palette file **Files:** - Create: `rofi/udt/palette.rasi` - [ ] **Step 1: Write the palette** Values are from the official `catppuccin/palette` repository. Create `rofi/udt/palette.rasi`: ```css /* * Catppuccin Macchiato palette for rofi. * Copyright (C) 2026 Danilo M. * Licensed under the GNU General Public License v2 only. * * Source: https://github.com/catppuccin/palette * Structural colors only. The accent lives in accent.rasi and is generated. */ * { base: #24273aff; mantle: #1e2030ff; crust: #181926ff; text: #cad3f5ff; subtext1: #b8c0e0ff; subtext0: #a5adcbff; overlay2: #939ab7ff; overlay1: #8087a2ff; overlay0: #6e738dff; surface2: #5b6078ff; surface1: #494d64ff; surface0: #363a4fff; rosewater: #f4dbd6ff; flamingo: #f0c6c6ff; pink: #f5bde6ff; mauve: #c6a0f6ff; red: #ed8796ff; maroon: #ee99a0ff; peach: #f5a97fff; yellow: #eed49fff; green: #a6da95ff; teal: #8bd5caff; sky: #91d7e3ff; sapphire: #7dc4e4ff; blue: #8aadf4ff; lavender: #b7bdf8ff; } ``` - [ ] **Step 2: Verify rofi parses it** ```bash rofi -no-config -theme "$PWD/rofi/udt/palette.rasi" -dump-theme >/dev/null && echo "palette parses" ``` Expected: `palette parses`, with no error output. This needs no display. A palette on its own defines variables and draws nothing, so there is nothing to look at yet. Appearance is checked in Task 6, once a layout uses it. - [ ] **Step 3: Commit** ```bash git add rofi/udt/palette.rasi git commit -m "feat: add Catppuccin Macchiato palette for rofi" ``` --- ### Task 3: The accent snapper, hue matching This task is TDD. The self-check is written first and must fail before the implementation exists. **Files:** - Create: `bin/udt-accent` - [ ] **Step 1: Write the failing self-check** Create `bin/udt-accent` containing only the self-check and the constants it needs: ```python #!/usr/bin/env python3 # udt-accent: pick a Catppuccin Macchiato accent matching a wallpaper. # Copyright (C) 2026 Danilo M. # Licensed under the GNU General Public License v2 only. """Extract a wallpaper's signature color and snap it to a Macchiato accent. Usage: udt-accent udt-accent --selftest The extraction deliberately calls pywal's colorz backend directly rather than running `wal -i`, because `wal -i` rewrites the whole ~/.cache/wal directory including the terminal's ANSI colors. See the design spec for why that matters. """ import math import sys # The nine candidate accents. rosewater and flamingo are excluded as # near-neutral tints that capture saturated inputs; maroon, sapphire and # lavender are excluded as near-duplicate hues of red, sky and mauve. ACCENTS = { "pink": "#f5bde6", "mauve": "#c6a0f6", "red": "#ed8796", "peach": "#f5a97f", "yellow": "#eed49f", "green": "#a6da95", "teal": "#8bd5ca", "sky": "#91d7e3", "blue": "#8aadf4", } FALLBACK = "mauve" MIN_CHROMA = 10.0 def selftest(): # Every accent must snap to itself, or the metric is not self-consistent. for name, hexval in ACCENTS.items(): got = snap(hexval) assert got == name, f"{name} ({hexval}) snapped to {got}" # Representative real-world inputs. assert snap("#ff8800") == "peach", snap("#ff8800") assert snap("#00cc44") == "green", snap("#00cc44") # A near-grey has an unstable hue angle and must take the fallback. assert snap("#888888") == FALLBACK, snap("#888888") print("selftest OK") if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "--selftest": selftest() ``` - [ ] **Step 2: Run it to verify it fails** ```bash chmod +x bin/udt-accent python3 bin/udt-accent --selftest ``` Expected: FAIL with `NameError: name 'snap' is not defined`. - [ ] **Step 3: Implement the color math** Insert these functions into `bin/udt-accent` after the `MIN_CHROMA` constant and before `def selftest():`: ```python def _to_lab(hexval): """Convert #rrggbb to CIELAB. sRGB D65, the standard conversion.""" r, g, b = (int(hexval[i:i + 2], 16) / 255 for i in (1, 3, 5)) def linear(c): return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 r, g, b = linear(r), linear(g), linear(b) x = (0.4124 * r + 0.3576 * g + 0.1805 * b) / 0.95047 y = (0.2126 * r + 0.7152 * g + 0.0722 * b) z = (0.0193 * r + 0.1192 * g + 0.9505 * b) / 1.08883 def f(t): return t ** (1 / 3) if t > 0.008856 else 7.787 * t + 16 / 116 fx, fy, fz = f(x), f(y), f(z) return (116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)) def _hue(hexval): """Perceptual hue angle in radians.""" _, a, b = _to_lab(hexval) return math.atan2(b, a) def _chroma(hexval): """Distance from the neutral axis. Near-greys sit close to zero.""" _, a, b = _to_lab(hexval) return math.hypot(a, b) def snap(hexval): """Return the name of the nearest candidate accent by perceptual hue.""" if _chroma(hexval) < MIN_CHROMA: return FALLBACK target = _hue(hexval) def distance(name): delta = abs(_hue(ACCENTS[name]) - target) return min(delta, 2 * math.pi - delta) # hue is circular return min(ACCENTS, key=distance) ``` - [ ] **Step 4: Run the self-check to verify it passes** ```bash python3 bin/udt-accent --selftest ``` Expected: `selftest OK` - [ ] **Step 5: Commit** ```bash git add bin/udt-accent git commit -m "feat: add perceptual hue matching for Macchiato accents" ``` --- ### Task 4: The accent snapper, extraction and output **Files:** - Modify: `bin/udt-accent` - [ ] **Step 1: Add extraction, writing, and the CLI** Add to `bin/udt-accent`. Put the imports with the existing ones at the top, and the functions before `def selftest():`: ```python import os import tempfile from pathlib import Path ``` ```python OUTPUT = Path.home() / ".cache" / "wal" / "udt-accent.rasi" def signature_color(image): """Extract the image's most chromatic mid-tone color. Calls the colorz backend directly. It returns a list and writes nothing, which is what keeps the pywal cache (and so the terminal) untouched. """ from pywal.backends import colorz colors = colorz.get(str(image), 16) # Slot 0 trends near-black and the upper slots near-white; the signature # color of an image lives in the middle. return max(colors[1:7], key=_chroma) def write_accent(name): """Write the accent rasi file atomically.""" hexval = ACCENTS[name] content = ( "/* Generated by udt-accent. Do not edit. */\n" f"* {{ accent: {hexval}ff; }}\n" ) OUTPUT.parent.mkdir(parents=True, exist_ok=True) # Write-then-rename: a rofi launch during a wallpaper change must never # read a half-written file. fd, tmp = tempfile.mkstemp(dir=str(OUTPUT.parent), suffix=".tmp") try: with os.fdopen(fd, "w") as handle: handle.write(content) os.replace(tmp, OUTPUT) except BaseException: if os.path.exists(tmp): os.unlink(tmp) raise def main(image): try: name = snap(signature_color(image)) except Exception as exc: # A broken image must still leave a working theme. print(f"udt-accent: {exc}, falling back to {FALLBACK}", file=sys.stderr) name = FALLBACK write_accent(name) print(f"{name} {ACCENTS[name]}") ``` Replace the existing `__main__` block with: ```python if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "--selftest": selftest() elif len(sys.argv) == 2: main(sys.argv[1]) else: print(__doc__, file=sys.stderr) sys.exit(2) ``` - [ ] **Step 2: Snapshot the pywal cache, then run against real wallpapers** The critical property is that this does NOT disturb the terminal colors. ```bash cp ~/.cache/wal/colors.json /tmp/udt-check/before.json for w in $(find ~/Pictures/wallpapers -type f \( -name '*.jpg' -o -name '*.png' \) | head -6); do printf '%-40s ' "$(basename "$w")" python3 bin/udt-accent "$w" done ``` Expected: six lines, each naming an accent and its hex, with different wallpapers giving different accents. - [ ] **Step 3: Verify the pywal cache was not touched** ```bash diff ~/.cache/wal/colors.json /tmp/udt-check/before.json && echo "ISOLATION OK" ``` Expected: `ISOLATION OK` with no diff output. **If this prints a diff, stop.** It means the terminal colors were modified, which is the exact regression this design exists to prevent. - [ ] **Step 4: Verify the fallback path** ```bash python3 bin/udt-accent /nonexistent/image.png cat ~/.cache/wal/udt-accent.rasi ``` Expected: a warning on stderr, `mauve #c6a0f6` on stdout, and the file containing `* { accent: #c6a0f6ff; }`. - [ ] **Step 5: Re-run the self-check** ```bash python3 bin/udt-accent --selftest ``` Expected: `selftest OK` - [ ] **Step 6: Commit** ```bash git add bin/udt-accent git commit -m "feat: extract wallpaper accent without touching the pywal cache" ``` --- ### Task 5: The shared common.rasi **Files:** - Create: `rofi/udt/common.rasi` - [ ] **Step 1: Write common.rasi** This holds everything the three layouts share. Fonts are the Qt/GTK ones, verified to resolve via `fc-match`. ```css /* * Shared identity for all udt rofi layouts. * Copyright (C) 2026 Danilo M. * Licensed under the GNU General Public License v2 only. */ @import "palette.rasi" @import "accent.rasi" * { font: "Noto Sans 11"; monospace-font: "Inconsolata Nerd Font Mono 11"; background-color: transparent; text-color: @text; margin: 0; padding: 0; spacing: 0; radius: 10px; bar-height: 36px; } window { background-color: @base; border: 2px; border-color: @accent; border-radius: @radius; padding: 16px; } mainbox { spacing: 12px; } inputbar { background-color: @surface0; border-radius: 6px; padding: 10px 12px; spacing: 8px; children: [ prompt, entry ]; } prompt { text-color: @accent; } entry { placeholder: "search"; placeholder-color: @overlay0; cursor: text; } listview { scrollbar: false; cycle: true; dynamic: true; spacing: 4px; } element { border-radius: 6px; padding: 8px 10px; spacing: 10px; cursor: pointer; } element normal.normal { text-color: @text; } element alternate.normal{ text-color: @text; } element normal.urgent { text-color: @red; } element normal.active { text-color: @accent; } element selected.normal { background-color: @accent; text-color: @base; } element selected.urgent { background-color: @red; text-color: @base; } element selected.active { background-color: @accent; text-color: @base; } element-icon { size: 1.2em; vertical-align: 0.5; background-color: transparent; } element-text { vertical-align: 0.5; background-color: transparent; text-color: inherit; } message { background-color: @surface0; border-radius: 6px; padding: 10px; } textbox { text-color: @text; } error-message { background-color: @base; text-color: @red; padding: 12px; } ``` - [ ] **Step 2: Create a placeholder accent so imports resolve** `accent.rasi` will be a symlink to the generated file (Task 9), but the file must exist now for the layouts to parse. ```bash mkdir -p rofi/udt python3 bin/udt-accent --selftest >/dev/null && \ printf '/* Generated by udt-accent. Do not edit. */\n* { accent: #c6a0f6ff; }\n' > rofi/udt/accent.rasi cat rofi/udt/accent.rasi ``` Expected: the file prints with a mauve accent. - [ ] **Step 3: Commit** ```bash git add rofi/udt/common.rasi rofi/udt/accent.rasi git commit -m "feat: add shared rofi styling common to all udt layouts" ``` --- ### Task 6: The list layout `list.rasi` is built first because it serves eight of the eleven call sites. **Files:** - Create: `rofi/udt/list.rasi` - [ ] **Step 1: Write list.rasi** ```css /* * udt list layout: a tall, searchable, single-column list. * For ssh hosts, passwords, emoji, VMs, repos, windows, clipboard. * Copyright (C) 2026 Danilo M. * Licensed under the GNU General Public License v2 only. */ @import "common.rasi" window { width: 680px; anchor: center; location: center; } mainbox { children: [ inputbar, listview ]; } listview { columns: 1; lines: 12; fixed-height: false; } ``` - [ ] **Step 2: Check it parses** ```bash rofi -no-config -theme "$PWD/rofi/udt/list.rasi" -dump-theme >/dev/null && echo "list parses" ``` Expected: `list parses`. Fix any reported syntax error before opening a window. - [ ] **Step 3: Verify it renders** ```bash printf 'alpha\nbravo\ncharlie\ndelta\n' | \ rofi -dmenu -p "list" -theme "$PWD/rofi/udt/list.rasi" ``` Expected: a centered 680px window, Macchiato background, accent border, a search bar, and four rows. The selected row has an accent background with dark text. Press Escape. - [ ] **Step 4: Commit** ```bash git add rofi/udt/list.rasi git commit -m "feat: add udt list layout" ``` --- ### Task 7: The menu layout **Files:** - Create: `rofi/udt/menu.rasi` - [ ] **Step 1: Write menu.rasi** A small box for a handful of fixed choices. No search bar: with four options, typing to filter is pointless and the bar only adds bulk. ```css /* * udt menu layout: a small box for a few fixed options. * For the screenshot menu, notes actions, the power menu. * Copyright (C) 2026 Danilo M. * Licensed under the GNU General Public License v2 only. */ @import "common.rasi" window { width: 380px; anchor: center; location: center; } mainbox { children: [ listview ]; } listview { columns: 1; lines: 6; fixed-height: false; } element { padding: 10px 12px; } ``` - [ ] **Step 2: Check it parses** ```bash rofi -no-config -theme "$PWD/rofi/udt/menu.rasi" -dump-theme >/dev/null && echo "menu parses" ``` Expected: `menu parses`. Fix any reported syntax error before opening a window. - [ ] **Step 3: Verify it renders** ```bash printf 'lock\nlogout\nreboot\nshutdown\n' | \ rofi -dmenu -p "power" -theme "$PWD/rofi/udt/menu.rasi" ``` Expected: a compact 380px window with four rows and no search bar, matching the list layout's colors and border. Press Escape. - [ ] **Step 4: Commit** ```bash git add rofi/udt/menu.rasi git commit -m "feat: add udt menu layout" ``` --- ### Task 8: The launcher layout **Files:** - Create: `rofi/udt/launcher.rasi` - [ ] **Step 1: Write launcher.rasi** ```css /* * udt launcher layout: a searchable icon grid for launching applications. * Copyright (C) 2026 Danilo M. * Licensed under the GNU General Public License v2 only. */ @import "common.rasi" window { width: 880px; anchor: center; location: center; } mainbox { children: [ inputbar, listview ]; } listview { columns: 3; lines: 6; fixed-height: false; } element { padding: 10px; spacing: 12px; } element-icon { size: 2.2em; } ``` - [ ] **Step 2: Check it parses** ```bash rofi -no-config -theme "$PWD/rofi/udt/launcher.rasi" -dump-theme >/dev/null && echo "launcher parses" ``` Expected: `launcher parses`. Fix any reported syntax error before opening a window. - [ ] **Step 3: Verify it renders with real applications** ```bash rofi -show drun -theme "$PWD/rofi/udt/launcher.rasi" ``` Expected: an 880px window, three columns of applications with icons at a larger size, and a working search bar. Typing filters the grid. Press Escape. - [ ] **Step 4: Commit** ```bash git add rofi/udt/launcher.rasi git commit -m "feat: add udt launcher layout" ``` --- ### Task 9: Install script and wallp integration **Files:** - Create: `install.sh` - Modify: `~/bin/wallp` (the `finalize` function) - [ ] **Step 1: Write install.sh** ```bash #!/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" # 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 launcher.rasi menu.rasi list.rasi; do ln -sfn "$repo/rofi/udt/$f" "$target/$f" done # 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" echo "installed:" ls -l "$target" "$HOME/bin/udt-accent" ``` - [ ] **Step 2: Run it and verify the symlinks** ```bash chmod +x install.sh ./install.sh ``` Expected: `palette.rasi`, `common.rasi`, `launcher.rasi`, `menu.rasi` and `list.rasi` point into the repo; `accent.rasi` points into `~/.cache/wal/`; `~/bin/udt-accent` points into the repo. - [ ] **Step 3: Verify the installed path renders** ```bash printf 'one\ntwo\nthree\n' | rofi -dmenu -theme ~/.config/rofi/udt/list.rasi ``` Expected: renders exactly as in Task 6, now through the installed path. Press Escape. - [ ] **Step 4: Hook udt-accent into wallp** `finalize` is currently three lines: ```bash finalize() { update_wpaper apply_theme "$1" } ``` `update_wpaper` maintains `~/.cache/wal/wpaper` as a symlink to the current horizontal wallpaper, so that path is the simplest stable handle on the image and needs no re-resolution. Add the accent refresh after it: ```bash finalize() { update_wpaper apply_theme "$1" # Refresh the rofi accent from the wallpaper. Never fatal: a failure here # must not stop the wallpaper from being set. 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 || true fi } ``` - [ ] **Step 5: Verify the integration end to end** ```bash cp ~/.cache/wal/colors.json /tmp/udt-check/before-wallp.json cat ~/.cache/wal/udt-accent.rasi wallp --restore echo "--- accent after ---" cat ~/.cache/wal/udt-accent.rasi echo "--- terminal colors unchanged? ---" diff ~/.cache/wal/colors.json /tmp/udt-check/before-wallp.json && echo "ISOLATION OK" ``` Expected: the accent file reflects the restored wallpaper, and `ISOLATION OK` confirms the terminal palette did not move. **If the diff is non-empty, stop and investigate before continuing.** - [ ] **Step 6: Commit** ```bash git add install.sh git commit -m "feat: add symlink installer and wire accent refresh into wallp" ``` Note: `~/bin/wallp` is outside this repo, so it is not part of this commit. Mention the edit in the final summary so it is not lost. --- ### Task 10: Migrate the list-layout call sites Eight call sites. Each is a one-line change. `~/bin` is not this repo, so these are edits to the live scripts; no commit here. **Files:** - Modify: `~/bin/blackpearl-sshmenu.sh:4-7` - Modify: `~/bin/blackpearl-emoji.sh:3` - Modify: `~/bin/rofi-qemu.sh:68` - Modify: `~/bin/github-repos.sh:22-23` - Modify: `~/bin/hypr-windows.sh:5-6,17` - Modify: `~/bin/rofipass:111` - Modify: `~/bin/ddgr_search.py` - Modify: `~/.config/hypr/sections/keybindings.lua` - [ ] **Step 1: Back up every file to be edited** ```bash mkdir -p /tmp/udt-check/backup cp ~/bin/blackpearl-sshmenu.sh ~/bin/blackpearl-emoji.sh ~/bin/rofi-qemu.sh \ ~/bin/github-repos.sh ~/bin/hypr-windows.sh ~/bin/rofipass \ ~/bin/ddgr_search.py /tmp/udt-check/backup/ cp ~/.config/hypr/sections/keybindings.lua /tmp/udt-check/backup/ ls /tmp/udt-check/backup/ ``` - [ ] **Step 2: Repoint the three scripts that use the `dir`/`theme` variable pair** `blackpearl-sshmenu.sh`, `github-repos.sh` and `hypr-windows.sh` each define `dir=` and `theme=` and then reference `${dir}/${theme}.rasi`. Replace both variable lines in each file so the existing reference resolves to the new theme: ```bash for f in ~/bin/blackpearl-sshmenu.sh ~/bin/github-repos.sh ~/bin/hypr-windows.sh; do sed -i \ -e 's|^dir=.*|dir="$HOME/.config/rofi/udt"|' \ -e "s|^theme=.*|theme='list'|" "$f" echo "--- $f ---" grep -nE '^dir=|^theme=|\$\{dir\}' "$f" done ``` Expected: each file shows `dir="$HOME/.config/rofi/udt"`, `theme='list'`, and an unchanged `${dir}/${theme}.rasi` reference. - [ ] **Step 3: Repoint the scripts with an inline theme path** ```bash sed -i 's|-theme darknix/runner.rasi|-theme ~/.config/rofi/udt/list.rasi|' \ ~/bin/blackpearl-emoji.sh ~/bin/rofi-qemu.sh sed -i 's|-theme elegantVagrant/elegantvagrant-dark|-theme ~/.config/rofi/udt/list.rasi|' \ ~/bin/rofipass grep -n 'udt' ~/bin/blackpearl-emoji.sh ~/bin/rofi-qemu.sh ~/bin/rofipass ``` Expected: one match in each of the three files. - [ ] **Step 4: Repoint the cliphist keybinding** ```bash sed -i 's|-theme ~/.config/rofi/launchers/type-2/style-1.rasi|-theme ~/.config/rofi/udt/list.rasi|' \ ~/.config/hypr/sections/keybindings.lua grep -n 'cliphist' ~/.config/hypr/sections/keybindings.lua ``` Expected: the binding now references `udt/list.rasi`. - [ ] **Step 5: Give ddgr_search.py a theme** This script currently passes no `-theme` and uses rofi's default. Read it to find the rofi invocation, then add `-theme ~/.config/rofi/udt/list.rasi` to that command: ```bash grep -n 'rofi' ~/bin/ddgr_search.py ``` Apply the edit to the line that actually builds the rofi command, not to the docstring at the top that shows example usage. - [ ] **Step 6: Verify each one launches** Run each and confirm it renders in the new theme. These are interactive; press Escape to dismiss each. ```bash ~/bin/blackpearl-sshmenu.sh ~/bin/hypr-windows.sh ~/bin/github-repos.sh ~/bin/blackpearl-emoji.sh ~/bin/rofi-qemu.sh ~/bin/rofipass ``` Expected: all six show the Macchiato list layout with the accent border. Test the cliphist binding with CTRL+SHIFT+L and the ddgr script per its own usage. --- ### Task 11: Migrate the menu- and launcher-layout call sites **Files:** - Modify: `~/bin/qar-scrotmenu.sh:3` - Modify: `~/bin/blackpearl-notes.sh:3` - Modify: `~/.config/rofi/powermenu/type-4/powermenu.sh` - Modify: `~/bin/blackpearl-appsmenu.sh:5` - Modify: `~/.config/rofi/launchers/type-1/launcher.sh` - [ ] **Step 1: Back up the remaining files** ```bash cp ~/bin/qar-scrotmenu.sh ~/bin/blackpearl-notes.sh ~/bin/blackpearl-appsmenu.sh \ /tmp/udt-check/backup/ cp ~/.config/rofi/powermenu/type-4/powermenu.sh /tmp/udt-check/backup/powermenu.sh cp ~/.config/rofi/launchers/type-1/launcher.sh /tmp/udt-check/backup/launcher.sh ``` - [ ] **Step 2: Repoint the two menu scripts** ```bash sed -i 's|-theme darknix/scrotmenu.rasi|-theme ~/.config/rofi/udt/menu.rasi|' \ ~/bin/qar-scrotmenu.sh sed -i 's|-theme darknix/notes.rasi|-theme ~/.config/rofi/udt/menu.rasi|' \ ~/bin/blackpearl-notes.sh grep -n 'udt' ~/bin/qar-scrotmenu.sh ~/bin/blackpearl-notes.sh ``` Expected: one match in each. - [ ] **Step 3: Repoint the appsmenu launcher** ```bash sed -i 's|-theme darknix/appmenu.rasi|-theme ~/.config/rofi/udt/launcher.rasi|' \ ~/bin/blackpearl-appsmenu.sh grep -n 'udt' ~/bin/blackpearl-appsmenu.sh ``` - [ ] **Step 4: Repoint the two adi1090x scripts** These build a theme path from their own directory layout rather than taking a simple `-theme` argument. Read each one first: ```bash grep -nE 'theme|rasi|dir=' ~/.config/rofi/powermenu/type-4/powermenu.sh | head -20 grep -nE 'theme|rasi|dir=' ~/.config/rofi/launchers/type-1/launcher.sh | head -20 ``` In each, replace the constructed theme path with the fixed new one: `~/.config/rofi/udt/menu.rasi` for the power menu, `~/.config/rofi/udt/launcher.rasi` for the launcher. Do not restructure these scripts, change only the theme path they pass to rofi. - [ ] **Step 5: Verify each one launches** ```bash ~/bin/qar-scrotmenu.sh ~/bin/blackpearl-notes.sh ~/bin/blackpearl-appsmenu.sh ~/.config/rofi/powermenu/type-4/powermenu.sh ~/.config/rofi/launchers/type-1/launcher.sh ``` Expected: the two menu scripts and the power menu show the compact menu layout; appsmenu and the ALT+F2 launcher show the icon grid. Press Escape on each. **Take care with the power menu, it contains real shutdown and reboot entries. Dismiss it with Escape rather than selecting a row.** --- ### Task 12: Final verification and documentation **Files:** - Create: `docs/MIGRATION.md` - [ ] **Step 1: Confirm nothing still references the old themes** ```bash grep -rn 'darknix\|elegantVagrant\|launchers/type-2\|colors/catppuccin' \ ~/bin/*.sh ~/bin/rofipass ~/bin/ddgr_search.py \ ~/.config/hypr/sections/keybindings.lua 2>/dev/null | grep -v archive/ ``` Expected: no output. Any match is a call site that was missed. Commented-out lines are acceptable but should be noted. - [ ] **Step 2: Confirm the accent still tracks wallpapers and isolation holds** ```bash cp ~/.cache/wal/colors.json /tmp/udt-check/final.json for w in $(find ~/Pictures/wallpapers -type f \( -name '*.jpg' -o -name '*.png' \) | head -4); do printf '%-40s ' "$(basename "$w")" udt-accent "$w" done diff ~/.cache/wal/colors.json /tmp/udt-check/final.json && echo "ISOLATION OK" python3 ~/bin/udt-accent --selftest ``` Expected: four accents, `ISOLATION OK`, and `selftest OK`. - [ ] **Step 3: Write docs/MIGRATION.md** Record what changed outside this repo, since those edits are not under version control here: ```markdown # Migration record, phase 1 (rofi) Files edited outside this repository. Backups from the migration run are in `/tmp/udt-check/backup/` and do not survive a reboot; if these need reverting later, use the table below rather than the backups. ## Call sites repointed | File | Was | Now | | --- | --- | --- | | `~/bin/blackpearl-sshmenu.sh` | `launchers/type-2/style-1` | `udt/list.rasi` | | `~/bin/github-repos.sh` | `launchers/type-2/style-1` | `udt/list.rasi` | | `~/bin/hypr-windows.sh` | `launchers/type-2/style-1` | `udt/list.rasi` | | `~/bin/blackpearl-emoji.sh` | `darknix/runner.rasi` | `udt/list.rasi` | | `~/bin/rofi-qemu.sh` | `darknix/runner.rasi` | `udt/list.rasi` | | `~/bin/rofipass` | `elegantVagrant/elegantvagrant-dark` | `udt/list.rasi` | | `~/bin/ddgr_search.py` | (rofi default) | `udt/list.rasi` | | `~/.config/hypr/sections/keybindings.lua` | `launchers/type-2/style-1` | `udt/list.rasi` | | `~/bin/qar-scrotmenu.sh` | `darknix/scrotmenu.rasi` | `udt/menu.rasi` | | `~/bin/blackpearl-notes.sh` | `darknix/notes.rasi` | `udt/menu.rasi` | | `~/.config/rofi/powermenu/type-4/powermenu.sh` | own theme dir | `udt/menu.rasi` | | `~/bin/blackpearl-appsmenu.sh` | `darknix/appmenu.rasi` | `udt/launcher.rasi` | | `~/.config/rofi/launchers/type-1/launcher.sh` | own theme dir | `udt/launcher.rasi` | ## Other edits - `~/bin/wallp`: `finalize()` now calls `udt-accent` with the horizontal wallpaper, guarded so a failure cannot stop the wallpaper being set. ## Not removed The old theme directories are still on disk and untouched: `~/.config/rofi/darknix/`, `elegantVagrant/`, `launchers/`, `applets/`, `powermenu/`. Deleting them is a separate decision, deferred until the new themes have been lived with. Note that `~/.config/rofi/applets/` still has its own launchers referenced from waybar (`applets/bin/mpd.sh`) and is therefore still in use. It is out of scope for phase 1 and will be handled with waybar in a later phase. ## Out of scope `ronema` (rofi NetworkManager applet) was excluded: no longer used, pending archival. ``` - [ ] **Step 4: Commit** ```bash git add docs/MIGRATION.md git commit -m "docs: record phase 1 migration of rofi call sites" ``` - [ ] **Step 5: Final review with the user** Phase 1 is visual, so the user is the test. Ask them to exercise each menu over normal use and report anything that looks wrong: colors off the Macchiato palette, fonts that are not Noto Sans or Inconsolata, a layout that does not suit its job, or an accent that reads poorly against the background. --- ## Deferred to later phases - waybar, dunst, conky, hyprland borders, quickshell (see the spec's "Later phases"). - Deleting the five superseded rofi theme directories, once the new themes have proven themselves. - `~/.config/rofi/applets/`, still live via waybar's mpd button, to be handled alongside waybar. - `~/.config/rofi/config.rasi` still sets `font: "Mono 12"` and an `icon-theme`. The udt themes override the font, so this is harmless, but it is worth cleaning up when rofi's global config is next touched.