aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-18 11:42:52 +0200
committerDanilo M. <danix@danix.xyz>2026-10-18 11:42:52 +0200
commit1d527c4e47514540541abf57aa1adb6a14ae5b54 (patch)
treedde8c34cd5e7f25291962d5a6e1bca5ac6631b5d /bin
downloadrofi-scripts-1d527c4e47514540541abf57aa1adb6a14ae5b54.tar.gz
rofi-scripts-1d527c4e47514540541abf57aa1adb6a14ae5b54.zip
Initial commit: rofi-scripts, the scripts behind the rofi menus
Power menu, launcher, screenshot picker, SSH host cards, pass browser, emoji picker, Firefox profile picker, notification actions and an sd-server model picker. install.sh symlinks them into ~/bin; their layouts come from unified-desktop-theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'bin')
-rwxr-xr-xbin/blackpearl-emoji.sh19
-rwxr-xr-xbin/blackpearl-sshmenu.sh40
-rwxr-xr-xbin/firefox-profile48
-rwxr-xr-xbin/notify-actions.sh41
-rwxr-xr-xbin/qar-scrotmenu.sh34
-rwxr-xr-xbin/rofipass351
-rwxr-xr-xbin/sd-webui86
-rwxr-xr-xbin/udt-launcher.sh22
-rwxr-xr-xbin/udt-powermenu.sh96
9 files changed, 737 insertions, 0 deletions
diff --git a/bin/blackpearl-emoji.sh b/bin/blackpearl-emoji.sh
new file mode 100755
index 0000000..3969c43
--- /dev/null
+++ b/bin/blackpearl-emoji.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# blackpearl-emoji.sh - emoji picker, rofi themed by udt
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+rofi -no-lazy-grab -modi emoji -show emoji -display-emoji '󰞅 Emoji:' -theme ~/.config/rofi/udt/grid.rasi
+
diff --git a/bin/blackpearl-sshmenu.sh b/bin/blackpearl-sshmenu.sh
new file mode 100755
index 0000000..b0a0fc1
--- /dev/null
+++ b/bin/blackpearl-sshmenu.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# blackpearl-sshmenu.sh - the hosts in ~/.ssh/config as rofi cards
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# rofi's own ssh mode shows the alias only, so the config is read here.
+# ponytail: plain "Keyword value" lines in ~/.ssh/config only; Include and
+# Match are not followed, wildcard Host patterns are skipped.
+mapfile -t hosts < <(awk '
+ function flush() { if (name != "") print name "\t" user "\t" addr }
+ tolower($1) == "host" { flush(); name = ($2 ~ /[*?]/) ? "" : $2; user = ""; addr = $2 }
+ tolower($1) == "hostname" { addr = $2 }
+ tolower($1) == "user" { user = $2 }
+ END { flush() }' ~/.ssh/config)
+
+# Two lines a card: the alias, then "user · address" with the user only where
+# the config sets one. Entries are joined on "|" so each can hold a newline.
+entries=()
+for h in "${hosts[@]}"; do
+ IFS=$'\t' read -r name user addr <<< "$h"
+ entries+=("<b>$name</b>
+<span size='small' fgalpha='65%'>${user:+$user · }$addr</span>")
+done
+
+i=$(IFS='|'; printf '%s' "${entries[*]}" | rofi -dmenu -i -markup-rows \
+ -sep '|' -eh 2 -format i -p '󰆍 SSH:' -theme ~/.config/rofi/udt/ssh.rasi) || exit
+name=${hosts[i]%%$'\t'*}
+exec rofi-sensible-terminal -e ssh "$name"
diff --git a/bin/firefox-profile b/bin/firefox-profile
new file mode 100755
index 0000000..3920e09
--- /dev/null
+++ b/bin/firefox-profile
@@ -0,0 +1,48 @@
+#!/bin/bash
+# firefox-profile - pick a Firefox profile with rofi, one behind a password
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Pick a Firefox profile with rofi; one profile requires a password.
+# Hash file: printf '%s' 'secret' | sha256sum | cut -d' ' -f1 > ~/.config/firefox-profile.hash
+# ponytail: wrapper gate only, `firefox -P <name>` bypasses it. Encrypt the profile dir if that matters.
+
+PROTECTED="danix"
+HASHFILE="$HOME/.config/firefox-profile.hash"
+INI="$HOME/.mozilla/firefox/profiles.ini"
+
+# Opening a link with exactly one profile running: hand it to that instance,
+# no picker. The protected profile was already unlocked to get it running.
+if [ $# -gt 0 ]; then
+ running=$(pgrep -af '^/usr/lib64/firefox/firefox(-bin)? (.* )?-P ' | sed -n 's/.* -P \([^ ]*\).*/\1/p' | sort -u)
+ [ -n "$running" ] && [ "$(wc -l <<<"$running")" -eq 1 ] && exec firefox -P "$running" "$@"
+fi
+
+profile=$(sed -n 's/^Name=//p' "$INI" | rofi -dmenu -theme ~/.config/rofi/udt/list.rasi -i -p '󰈹 Firefox:') || exit 0
+[ -n "$profile" ] || exit 0
+
+if [ "$profile" = "$PROTECTED" ]; then
+ pass=$(rofi -dmenu -theme ~/.config/rofi/udt/list.rasi -password -l 0 \
+ -mesg "Unlock Firefox profile: $profile" \
+ -theme-str 'mainbox { children: [ message, inputbar ]; padding: 16px; spacing: 8px; } message { background-color: transparent; padding: 0 0 4px 2px; } textbox { font: "Noto Sans Bold 13"; }' \
+ -theme-str 'entry { placeholder: "Password"; }' </dev/null) || exit 0
+ hash=$(printf '%s' "$pass" | sha256sum | cut -d' ' -f1)
+ if [ "$hash" != "$(cat "$HASHFILE" 2>/dev/null)" ]; then
+ notify-send "Firefox" "Wrong password" 2>/dev/null
+ exit 1
+ fi
+fi
+
+exec firefox -P "$profile" "$@"
diff --git a/bin/notify-actions.sh b/bin/notify-actions.sh
new file mode 100755
index 0000000..0c49c6b
--- /dev/null
+++ b/bin/notify-actions.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# Pick one of a notification's actions with rofi and invoke it. The renderer
+# passes the labels and keys as arguments, so nothing here parses JSON.
+#
+# notify-actions.sh <id> <label> <key> [label key ...]
+
+set -u
+
+[[ $# -ge 3 ]] || { echo "usage: ${0##*/} <id> <label> <key> ..." >&2; exit 2; }
+
+id="$1"; shift
+labels=()
+keys=()
+while [[ $# -ge 2 ]]; do
+ labels+=("$1")
+ keys+=("$2")
+ shift 2
+done
+
+choice="$(printf '%s\n' "${labels[@]}" | rofi -dmenu -i -p "Notification" \
+ -theme "$HOME/.config/rofi/udt/menu.rasi")"
+[[ -n "$choice" ]] || exit 0
+
+for i in "${!labels[@]}"; do
+ if [[ "${labels[$i]}" == "$choice" ]]; then
+ notifyctl action "$id" "${keys[$i]}"
+ exit $?
+ fi
+done
diff --git a/bin/qar-scrotmenu.sh b/bin/qar-scrotmenu.sh
new file mode 100755
index 0000000..2b7341f
--- /dev/null
+++ b/bin/qar-scrotmenu.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# qar-scrotmenu.sh - screenshot menu, rofi themed by udt
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+rofi_command=(rofi -theme ~/.config/rofi/udt/screenshot.rasi)
+
+### Options ###
+screen=""
+area=""
+# Variable passed to rofi
+options="$screen\n$area"
+
+chosen="$(echo -e "$options" | "${rofi_command[@]}" -dmenu -selected-row 1 -p '󰄀 Screenshot:')"
+case $chosen in
+ $screen)
+ grimblast -w 3 -n -t png copysave active
+ ;;
+ $area)
+ grimblast -n -t png copysave area
+ ;;
+esac
diff --git a/bin/rofipass b/bin/rofipass
new file mode 100755
index 0000000..b9620f8
--- /dev/null
+++ b/bin/rofipass
@@ -0,0 +1,351 @@
+#!/usr/bin/env sh
+# rofipass - rofi front end for pass
+#
+# Derived from rofipass by Augusto Coronel (https://github.com/aocoronel/rofipass),
+# released under the MIT License, whose notice is kept below as it requires.
+# Modifications Copyright (C) 2026 Danilo M. <danix@danix.xyz>, released under
+# the GNU General Public License version 2 (see LICENSE).
+#
+# MIT License
+#
+# Copyright (c) 2025 Augusto Coronel
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+### Colors
+
+help_color="#7c5cff"
+div_color="#334433"
+label="#f067fc"
+
+###
+
+### Bindings
+
+kb_copy_email="Ctrl+2" # EMAIL
+kb_copy_otp="Ctrl-1" # COPY TOTP
+kb_add_pass="Ctrl+r" # PASSWORD
+kb_add_otp="Ctrl+t" # ADD TOTP
+kb_delete="Ctrl-x" # DELETE
+kb_edit="Ctrl+y" # EDIT
+kb_close_tomb="Ctrl-z" # CLOSE TOMB
+kb_open_tomb="Ctrl-o" # OPEN TOMB
+
+###
+
+clipmethod="$XDG_SESSION_TYPE"
+passdir=${PASSWORD_STORE_DIR:-$HOME/.password-store}
+
+COLOR_YELLOW="$(tput setaf 226)"
+COLOR_RED="$(tput setaf 196)"
+COLOR_GREEN="$(tput setaf 82)"
+COLOR_BLUE="$(tput setaf 87)"
+COLOR_RESET=$(tput sgr0)
+
+printfc() {
+ mode=$1
+ case $mode in
+ "ERROR" | "FATAL")
+ color="$COLOR_RED"
+ ;;
+ "WARN")
+ mode="WARNING"
+ color="$COLOR_YELLOW"
+ ;;
+ "DEBUG")
+ color="$COLOR_BLUE"
+ ;;
+ "INFO")
+ color="$COLOR_GREEN"
+ ;;
+ esac
+ text=$2
+ printf "%s[%s]:%s %s\n" "${color}" "${mode}" "${COLOR_RESET}" "${text}"
+}
+
+check_command() {
+ for cmd in "$@"; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ printfc FATAL "$cmd is not installed."
+ return 1
+ fi
+ done
+}
+
+_notify() {
+ message="$1"
+ header="rofipass"
+ urgency="$2"
+ time=${3:-$time}
+ echo "$message"
+ if [ "$time" -eq 0 ]; then
+ notify-send -u "$urgency" "$header" "$message"
+ else
+ notify-send -u "$urgency" "$header" "$message" --expire-time="$time"000 --wait
+ fi
+}
+
+die() {
+ mode=$1
+ message=$2
+ urgency=$3
+
+ printfc "$mode" "$message"
+ _notify "$message" "$urgency"
+ exit 1
+}
+
+version() {
+ echo "v1.1.0"
+}
+
+usage() {
+ cat <<EOF
+rofipass | A rofi extension for pass
+
+Usage: rofipass [OPTIONS] <COMMAND>
+
+Options:
+ -e <EDITOR> Set editor
+ -f Lift swap restrictions (for tomb)
+ -h Display this help message and exit
+ -l <LENGTH> Default password length on generation
+ -t <TERM> Default terminal emulator (for tomb)
+ -v Display the current version number
+ -T <SECONDS> Clearing time
+
+Example:
+ rofipass -f -l 72 -t kitty -e nvim -T 10
+EOF
+}
+
+_rofi() {
+ rofi -dmenu -theme ~/.config/rofi/udt/list.rasi -i -no-levenshtein-sort "$@"
+}
+
+copy_email() {
+ email=$(pass "$menu" | sed -n 's/^email=//p')
+ case "$clipmethod" in
+ "x11")
+ xclip "$email" || die FATAL "failed to copy the email." "critical"
+ _notify "Copied to clipboard. Clearing in $time seconds" "normal"
+ ;;
+ "wayland")
+ wl-copy "$email" || die FATAL "failed to copy the email." "critical"
+ _notify "Copied to clipboard. Clearing in $time seconds" "normal"
+ ;;
+ esac
+ # clearboard
+}
+
+append_otp() {
+ check_command zbarimg || die FATAL "zbarimg is not installed." "critical"
+ case "$clipmethod" in
+ "x11")
+ xclip -o | zbarimg -q --raw - | pass otp append "$menu" || die FATAL "failed to append OTP to $menu" "critical"
+ _notify "Inserted OTP successfully to $menu" "normal"
+ ;;
+ "wayland")
+ wl-paste --type image/png | zbarimg -q --raw - | pass otp append "$menu" || die FATAL "failed to append OTP to $menu" "critical"
+ _notify "Inserted OTP successfully to $menu" "normal"
+ ;;
+ esac
+}
+
+clearboard() {
+ [ -x "$(command -v notifyctl)" ] && notifyctl close-all
+
+ case "$clipmethod" in
+ "x11")
+ echo "" | xclip -sel clip
+ ;;
+ "wayland")
+ [ -x "$(command -v cliphist)" ] && cliphist wipe
+ echo "" | wl-copy
+ ;;
+ esac
+}
+
+tomb_open() {
+ if [ -n "$FORCE" ]; then
+ $term -e sh -c "pass open -f"
+ else
+ $term -e sh -c "pass open"
+ fi
+}
+
+tomb_close() {
+ $term -e sh -c "pass close"
+ pass_val=$?
+ if [ "$pass_val" -ne 0 ]; then
+ die FATAL "failed to close tomb." -u "critical"
+ else
+ _notify "Your password tomb has been closed!" "normal" 0
+ fi
+}
+
+deleteMenu() {
+ delask=$(printf "1. Yes\n2. No" | _rofi -p '󰆴 Delete:' -mesg "<span color='${label}'>Really delete</span> <span color='${help_color}'>$menu?</span>")
+ val=$?
+ [ $val -eq 1 ] && {
+ _notify "Cancelled!" "low" 0
+ main
+ }
+ [ "$delask" = "1. Yes" ] && pass rm -f "$menu" && _notify "Deleted $menu" "normal" 0
+ main
+}
+
+add_password() {
+ length=${length:-72}
+ addmenu=$(
+ echo | _rofi -p '󰐕 New password:' -lines 0 \
+ -theme-str 'window { width: 25em; } listview { lines: 0; } entry { placeholder: "Enter password name"; }'
+ )
+ val=$?
+ if [ $val -eq 1 ]; then
+ _notify "Cancelled!" "low" 0
+ main
+ elif [ $val -eq 0 ]; then
+ if command -v "passgen.py" >/dev/null 2>&1; then
+ passgen.py 3 | pass insert -e "$addmenu"
+ else
+ pass generate "$addmenu" "$length"
+ fi
+ if [ $val -eq 1 ]; then
+ _notify ERROR "failed to add password" 0
+ main
+ else
+ _notify "Added $addmenu" "normal" 0
+ fi
+ fi
+ main
+}
+
+edit_password() {
+ $term -e sh -c "export EDITOR=\"$editor\"; pass edit \"$menu\""
+}
+
+main() {
+ enable_tomb=0
+ enable_otp=0
+ enable_zbar=0
+
+ if ! command -v "tomb" >/dev/null 2>&1; then
+ enable_tomb=1
+ fi
+ if ! command -v "zbarimg" >/dev/null 2>&1; then
+ enable_zbar=1
+ fi
+
+ pass otp -h >/dev/null 2>&1 || enable_otp=1
+
+ HELP=""
+
+ HELP="$HELP<span color='${label}'>Actions: </span><span color='${help_color}'>${kb_add_pass}</span>: Add <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_delete}</span>: Delete <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_edit}</span>: Edit"
+
+ if [ "$enable_zbar" -eq 0 ]; then
+ ZBAR_HELP=" <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_add_otp}</span>: Add OTP"
+ HELP="$HELP$ZBAR_HELP"
+ fi
+
+ HELP="$HELP
+<span color='${label}'>Copy: </span><span color='${help_color}'>Enter</span>: Copy Password <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_copy_email}</span>: Copy Email"
+
+ if [ "$enable_otp" -eq 0 ]; then
+ OTP_HELP=" <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_copy_otp}</span>: Copy OTP"
+ HELP="$HELP$OTP_HELP"
+ fi
+
+ if [ "$enable_tomb" -eq 0 ]; then
+ TOMB_HELP="<span color='${label}'>Tomb: </span><span color='${help_color}'>${kb_close_tomb}</span>: Close Tomb <span color='${div_color}'>|</span> <span color='${help_color}'>${kb_open_tomb}</span>: Open Tomb"
+ HELP="$HELP
+$TOMB_HELP"
+ fi
+
+ pass=$(find -L "$passdir" -type f -name '*.gpg' -printf '%P\n' | sed 's/\.gpg//')
+ menu=$(echo "${pass}" | _rofi -p '󰌋 Pass:' -mesg "${HELP}" -kb-custom-1 "${kb_add_pass}" -kb-custom-2 "${kb_copy_otp}" -kb-custom-3 "${kb_delete}" -kb-custom-4 "${kb_edit}" -kb-custom-5 "${kb_close_tomb}" -kb-custom-6 "${kb_open_tomb}" -kb-custom-7 "${kb_copy_email}" -kb-custom-8 "${kb_add_otp}")
+
+ val=$?
+ case "$val" in
+ 1) exit ;;
+ 12) deleteMenu ;;
+ 11) # OTP Copy
+ [ "$enable_otp" -eq 0 ] && {
+ timeout 3 pass otp -c "$menu" || die ERROR "failed to copy OTP!" "normal"
+ _notify "Copied to clipboard. Clearing in $time seconds" "normal"
+ # clearboard
+ }
+ ;;
+ 10) add_password ;;
+ 13) edit_password ;;
+ 14) [ "$enable_tomb" -eq 0 ] && tomb_close ;;
+ 15) [ "$enable_tomb" -eq 0 ] && tomb_open ;;
+ 16) copy_email ;;
+ 17) [ "$enable_zbar" -eq 0 ] && append_otp ;;
+ 0)
+ pass -c "$menu" || die ERROR "failed to copy the password!" "normal"
+ _notify "Copied to clipboard. Clearing in $time seconds" "normal"
+ # clearboard
+ ;;
+ esac
+}
+
+while getopts ":hvfl:t:T:e:" opt; do
+ case "$opt" in
+ h)
+ usage
+ exit 0
+ ;;
+ e)
+ editor="$OPTARG"
+ ;;
+ v)
+ version
+ exit 0
+ ;;
+ f)
+ FORCE=true
+ ;;
+ l)
+ length="$OPTARG"
+ ;;
+ t)
+ term="$OPTARG"
+ ;;
+ T)
+ time="$OPTARG"
+ ;;
+ ?)
+ printfc FATAL "invalid option '-$OPTARG'" >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+check_command rofi pass || exit 1
+
+[ ! -d "$passdir" ] && die FATAL "no password directory found!" "low"
+
+[ -z "$editor" ] && editor=${EDITOR:-nano}
+term=${term:-kitty}
+time=${time:-5}
+
+main
diff --git a/bin/sd-webui b/bin/sd-webui
new file mode 100755
index 0000000..0d72547
--- /dev/null
+++ b/bin/sd-webui
@@ -0,0 +1,86 @@
+#!/bin/bash
+# sd-webui - start sd-server with a model picked from rofi
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Start sd-server (stable-diffusion.cpp) with a model picked from rofi and open its web UI.
+# Runs in the foreground: close the terminal (or Ctrl-C) to stop it and free VRAM.
+
+M=/data/LLM-models/SD
+QWEN=(--llm $M/Qwen3VL-8B-Instruct-Q8_0.gguf
+ --llm_vision $M/mmproj-Qwen3VL-8B-Instruct-F16.gguf
+ --vae $M/vae/qwen_image_2.1_vae_bf16.safetensors)
+SDXL=(--cfg-scale 7.0 --steps 25) # same defaults as imggen
+
+# Pick the model (sd-server loads one per process, so choose at start).
+case $(printf '%s\n' \
+ "Qwen Image 2.1 (base, 20 steps, text/edit/RGBA)" \
+ "Qwen Image 2.1 turbo (6 steps)" \
+ "Z-Image Turbo (8 steps, photoreal)" \
+ "FLUX.2 klein 4B (4 steps, edit)" \
+ "Krea 2 Turbo (8 steps, photoreal)" \
+ "Wan2.2 TI2V 5B (video, text/image)" \
+ "SDXL base" \
+ "RealVisXL V5" \
+ "Pony Diffusion V6 XL" \
+ | rofi -dmenu -i -theme ~/.config/rofi/udt/list.rasi -p '󰋩 SD model:' \
+ -theme-str 'entry { placeholder: "Search models"; }') in
+ "Qwen Image 2.1 (base"*) MODEL=(--diffusion-model $M/qwen_image_2.1-Q8_0.gguf "${QWEN[@]}"
+ --cfg-scale 6.0 --cache-mode cache-dit) ;;
+ "Qwen Image 2.1 turbo"*) MODEL=(--diffusion-model $M/Qwen-Image-2.1-viggle-turbo-v0.3-6step-Q8_0.gguf "${QWEN[@]}"
+ --cfg-scale 1.0 --steps 6) ;;
+ Z-Image*) MODEL=(--diffusion-model $M/z_image_turbo-Q8_0.gguf
+ --llm $M/Qwen3-4B-Instruct-2507-Q8_0.gguf --vae $M/vae/flux1-ae.safetensors
+ --cfg-scale 1.0 --steps 8 --diffusion-fa) ;;
+ FLUX.2*) MODEL=(--diffusion-model $M/flux-2-klein-4b-Q8_0.gguf
+ --llm $M/Qwen3-4B-Q8_0.gguf --vae $M/vae/flux2-vae.safetensors
+ --cfg-scale 1.0 --steps 4 --diffusion-fa) ;;
+ # Q6_K: same output as Q8_0 at seed 42, ~20% faster (both still offload on 12G).
+ Krea*) MODEL=(--diffusion-model $M/Krea-2-Turbo-Q6_K.gguf
+ --llm $M/Qwen3VL-4B-Instruct-Q8_0.gguf --vae $M/vae/wan_2.1_vae.safetensors
+ --cfg-scale 1.0 --steps 8 --diffusion-fa) ;;
+ # 1280x704 is the model's native 720p. Frames must be 4k+1 at 24 fps: 49 = 2 s, 121 = 5 s.
+ # Settings follow upstream Wan2.2 5B defaults (cfg 5, shift 5); drop to 832x480 if too slow.
+ Wan2.2*) MODEL=(--diffusion-model $M/Wan2.2-TI2V-5B-Q8_0.gguf
+ --t5xxl $M/umt5-xxl-encoder-Q8_0.gguf --vae $M/vae/wan2.2_vae.safetensors
+ --cfg-scale 5.0 --steps 30 --flow-shift 5.0 --diffusion-fa
+ -W 1280 -H 704 --video-frames 49 --temporal-tiling) ;;
+ # imggen's diffusers weights, symlinked into the layout sd.cpp loads as a directory
+ "SDXL base") MODEL=(-m $M/sdxl-links/sdxl "${SDXL[@]}") ;;
+ RealVisXL*) MODEL=(-m $M/sdxl-links/realvis "${SDXL[@]}") ;;
+ Pony*) MODEL=(-m $M/sdxl-links/pony "${SDXL[@]}" --clip-skip 2) ;;
+ *) exit 0 ;;
+esac
+
+PORT=${PORT:-7860}
+URL=http://127.0.0.1:$PORT/
+
+# Open the browser once the server answers (model loading takes a while).
+(
+ until curl -sf -o /dev/null "$URL"; do sleep 1; done
+ notify-send -a sd-server -i applications-graphics "Stable Diffusion server started" "$URL"
+ xdg-open "$URL" >/dev/null 2>&1
+) &
+trap 'kill $! 2>/dev/null' EXIT
+
+# Log to a file too: the terminal loses the output when the server crashes.
+# stdbuf -oL: line-buffer stdout so the last lines before a crash reach the log.
+LOG=${XDG_CACHE_HOME:-$HOME/.cache}/sd-server.log
+# MODEL goes after the shared defaults so a model can override them (e.g. Wan's -W/-H).
+stdbuf -oL -eL sd-server --listen-port "$PORT" \
+ --lora-model-dir $M/loras \
+ --sampling-method euler -W 1024 -H 1024 --seed -1 \
+ "${MODEL[@]}" \
+ --offload-to-cpu -v "$@" 2>&1 | tee "$LOG"
diff --git a/bin/udt-launcher.sh b/bin/udt-launcher.sh
new file mode 100755
index 0000000..d0a8013
--- /dev/null
+++ b/bin/udt-launcher.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+## Author : Aditya Shakya (adi1090x)
+## Github : @adi1090x
+## Derived from adi1090x/rofi, GPL-3.0 like upstream. Modifications
+## Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+## Rofi : Launcher (Modi Drun, Run, File Browser, Window)
+#
+## Available Styles
+#
+## style-1 style-2 style-3 style-4 style-5
+## style-6 style-7 style-8 style-9 style-10
+## style-11 style-12 style-13 style-14 style-15
+
+dir="$HOME/.config/rofi/udt"
+theme='launcher'
+
+## Run
+rofi \
+ -show drun \
+ -theme ${dir}/${theme}.rasi
diff --git a/bin/udt-powermenu.sh b/bin/udt-powermenu.sh
new file mode 100755
index 0000000..6d8fb84
--- /dev/null
+++ b/bin/udt-powermenu.sh
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+
+## Author : Aditya Shakya (adi1090x)
+## Github : @adi1090x
+## Derived from adi1090x/rofi, GPL-3.0 like upstream. Modifications
+## Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+## Rofi : Power Menu
+#
+## Available Styles
+#
+## style-1 style-2 style-3 style-4 style-5
+
+# Current Theme
+dir="$HOME/.config/rofi/udt"
+theme='powermenu'
+
+# CMDs
+uptime="`uptime -p | sed -e 's/up //g'`"
+host=`hostname`
+
+# Options
+shutdown=''
+reboot=''
+logout=''
+sleep='󰤄'
+yes='󰗠'
+no='󰅙'
+
+# Rofi CMD
+rofi_cmd() {
+ rofi -dmenu \
+ -p "'ndi vidimu ${USER}" \
+ -mesg "Uptime: $uptime" \
+ -theme ${dir}/${theme}.rasi
+}
+
+# Confirmation CMD
+confirm_cmd() {
+ rofi -dmenu \
+ -p 'Confirmation' \
+ -selected-row 1 \
+ -mesg 'Simu Sicuri?' \
+ -theme "$HOME/.config/rofi/udt/confirm.rasi"
+}
+
+# Ask for confirmation
+confirm_exit() {
+ echo -e "$yes\n$no" | confirm_cmd
+}
+
+# Pass variables to rofi dmenu
+run_rofi() {
+ echo -e "$shutdown\n$reboot\n$sleep\n$logout" | rofi_cmd
+}
+
+# Execute Command
+run_cmd() {
+ selected="$(confirm_exit)"
+ if [[ "$selected" == "$yes" ]]; then
+ if [[ $1 == '--shutdown' ]]; then
+ notify-send 'system shutdown' 'the system is going to shutdown now'
+ sleep 1
+ /usr/bin/loginctl poweroff
+ elif [[ $1 == '--reboot' ]]; then
+ notify-send 'system reboot' 'the system is going to reboot now'
+ sleep 1
+ /usr/bin/loginctl reboot
+ elif [[ $1 == '--sleep' ]]; then
+ notify-send 'suspend to ram' 'the system is going to sleep now'
+ sleep 1
+ /usr/bin/loginctl suspend
+ elif [[ $1 == '--logout' ]]; then
+ hyprctl dispatch 'hl.dsp.exit()'
+ fi
+ else
+ exit 0
+ fi
+}
+
+# Actions
+chosen="$(run_rofi)"
+case ${chosen} in
+ $shutdown)
+ run_cmd --shutdown
+ ;;
+ $reboot)
+ run_cmd --reboot
+ ;;
+ $sleep)
+ run_cmd --sleep
+ ;;
+ $logout)
+ run_cmd --logout
+ ;;
+esac