Initial ThinkPad Hyprland dotfiles

This commit is contained in:
Pascal
2026-04-28 03:59:07 +02:00
commit 6eb922c417
56 changed files with 6587 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HYPR_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
MODE="${1:-wallpaper}"
export HYPR_DIR
export HYPR_SWITCHER_THEME_DIR="$HYPR_DIR/Themes"
export HYPR_SWITCHER_WALLPAPER_DIR="${WALLPAPER_DIR:-$HOME/Bilder/Wallpaper}"
notify() {
notify-send "AGS Switcher" "$1" >/dev/null 2>&1 || true
}
if ! command -v ags >/dev/null 2>&1; then
notify "ags ist nicht installiert."
exit 1
fi
cd "$HYPR_DIR"
ags quit --instance hypr-switcher >/dev/null 2>&1 || true
exec ags run "$HYPR_DIR/ags/switcher.tsx" "$MODE"

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
choice="$(
printf '%s\n' \
"󰌪 Theme wechseln" \
"󰸉 Wallpaper wechseln" |
wofi --dmenu --prompt "󰉼 Aussehen" --insensitive
)"
case "$choice" in
*"Theme wechseln"*)
"$SCRIPT_DIR/ags-switcher.sh" theme
;;
*"Wallpaper wechseln"*)
"$SCRIPT_DIR/ags-switcher.sh" wallpaper
;;
esac

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -euo pipefail
notify() {
notify-send "󰕾 Audio" "$1"
}
require_wpctl() {
if ! command -v wpctl >/dev/null 2>&1; then
notify "wpctl ist nicht installiert."
exit 1
fi
}
choose_sink() {
wpctl status |
awk '
/Sinks:/ {in_sinks=1; next}
/Sources:/ {in_sinks=0}
in_sinks && /\*/ {gsub(/^[[:space:]]*[│├└─* ]*/, ""); print "󰓃 " $0}
in_sinks && /^[[:space:]]*[│├└─ ]*[0-9]+\./ {gsub(/^[[:space:]]*[│├└─ ]*/, ""); print "󰓃 " $0}
' |
wofi --dmenu --prompt "󰕾 Audioausgabe" --insensitive
}
require_wpctl
choice="$(
printf '%s\n' \
"󰝝 Lauter" \
"󰝞 Leiser" \
"󰖁 Stumm schalten" \
"󰓃 Ausgabe wechseln" \
"󰍬 Mikrofon stumm" \
"󰎆 Play/Pause" \
"󰒮 Naechster Titel" \
"󰒭 Vorheriger Titel" \
"󰩟 Status" |
wofi --dmenu --prompt "󰕾 Audio" --insensitive
)"
case "$choice" in
*"Lauter"*)
wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
;;
*"Leiser"*)
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
;;
*"Stumm schalten"*)
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
;;
*"Ausgabe wechseln"*)
selection="$(choose_sink)"
[ -n "$selection" ] || exit 0
sink_id="$(printf '%s\n' "$selection" | sed -n 's/.* \([0-9][0-9]*\)\..*/\1/p')"
[ -n "$sink_id" ] || exit 0
wpctl set-default "$sink_id" && notify "Audioausgabe gewechselt."
;;
*"Mikrofon stumm"*)
wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
;;
*"Play/Pause"*)
playerctl play-pause
;;
*"Naechster Titel"*)
playerctl next
;;
*"Vorheriger Titel"*)
playerctl previous
;;
*"Status"*)
wpctl status | wofi --dmenu --prompt "󰩟 Audiostatus"
;;
esac

View File

@@ -0,0 +1,133 @@
#!/usr/bin/env bash
set -euo pipefail
notify() {
notify-send "󰂯 Bluetooth" "$1"
}
require_bluetoothctl() {
if ! command -v bluetoothctl >/dev/null 2>&1; then
notify "bluetoothctl ist nicht installiert."
exit 1
fi
}
wofi_pick() {
wofi --dmenu --prompt "$1" --insensitive
}
adapter_powered() {
{ bluetoothctl show 2>/dev/null || true; } | awk -F': ' '/Powered/ {print $2; exit}'
}
device_name() {
local mac="$1"
bluetoothctl info "$mac" 2>/dev/null | awk -F': ' '/Name/ {print $2; exit}'
}
pick_device() {
local prompt="$1"
bluetoothctl devices |
awk '{mac=$2; $1=""; $2=""; sub(/^ */, ""); printf "󰂯 %s [%s]\n", $0, mac}' |
wofi_pick "$prompt"
}
selected_mac() {
sed -n 's/.*\[\([0-9A-Fa-f:]\{17\}\)\].*/\1/p'
}
scan_devices() {
notify "Scan laeuft fuer 8 Sekunden."
timeout 8 bluetoothctl scan on >/dev/null 2>&1 || true
bluetoothctl scan off >/dev/null 2>&1 || true
local selection mac name
selection="$(pick_device "󰂯 Geraet verbinden")"
[ -n "$selection" ] || exit 0
mac="$(printf '%s\n' "$selection" | selected_mac)"
name="$(device_name "$mac")"
[ -n "$name" ] || name="$mac"
bluetoothctl pair "$mac" >/dev/null 2>&1 || true
bluetoothctl trust "$mac" >/dev/null 2>&1 || true
bluetoothctl connect "$mac" && notify "Verbunden mit $name."
}
connect_saved_device() {
local selection mac name
selection="$(pick_device "󰛳 Gekoppeltes Geraet verbinden")"
[ -n "$selection" ] || exit 0
mac="$(printf '%s\n' "$selection" | selected_mac)"
name="$(device_name "$mac")"
[ -n "$name" ] || name="$mac"
bluetoothctl connect "$mac" && notify "Verbunden mit $name."
}
disconnect_device() {
local selection mac name
selection="$(
bluetoothctl devices Connected |
awk '{mac=$2; $1=""; $2=""; sub(/^ */, ""); printf "󰂯 %s [%s]\n", $0, mac}' |
wofi_pick "󰅖 Bluetooth trennen"
)"
[ -n "$selection" ] || exit 0
mac="$(printf '%s\n' "$selection" | selected_mac)"
name="$(device_name "$mac")"
[ -n "$name" ] || name="$mac"
bluetoothctl disconnect "$mac" && notify "$name getrennt."
}
remove_device() {
local selection mac name
selection="$(pick_device "󰆴 Geraet entfernen")"
[ -n "$selection" ] || exit 0
mac="$(printf '%s\n' "$selection" | selected_mac)"
name="$(device_name "$mac")"
[ -n "$name" ] || name="$mac"
bluetoothctl remove "$mac" && notify "$name entfernt."
}
require_bluetoothctl
power="$(adapter_powered)"
[ -n "$power" ] || power="unknown"
choice="$(
printf '%s\n' \
"󰂯 Geraet suchen und verbinden" \
"󰂲 Bluetooth ein/aus" \
"󰛳 Gekoppelte Geraete" \
"󰅖 Verbundenes Geraet trennen" \
"󰆴 Geraet entfernen" \
"󰩟 Status: $power" |
wofi_pick "󰂯 Bluetooth"
)"
case "$choice" in
*"Geraet suchen"*)
bluetoothctl power on >/dev/null
bluetoothctl agent on >/dev/null 2>&1 || true
bluetoothctl default-agent >/dev/null 2>&1 || true
scan_devices
;;
*"Bluetooth ein/aus"*)
if [ "$power" = "yes" ]; then
bluetoothctl power off && notify "Bluetooth ausgeschaltet."
else
bluetoothctl power on && notify "Bluetooth eingeschaltet."
fi
;;
*"Gekoppelte Geraete"*)
connect_saved_device
;;
*"Verbundenes Geraet trennen"*)
disconnect_device
;;
*"Geraet entfernen"*)
remove_device
;;
*"Status:"*)
bluetoothctl show | wofi --dmenu --prompt "󰩟 Bluetoothstatus"
;;
esac

369
config/hypr/Scripts/dev-menu.sh Executable file
View File

@@ -0,0 +1,369 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOTS=(
"$HOME/Projekte"
"$HOME/Projects"
"$HOME/Code"
"$HOME/Developer"
"$HOME/dev"
"$HOME/test"
)
notify() {
notify-send "󰅩 Dev Menue" "$1" >/dev/null 2>&1 || true
}
pick() {
wofi --dmenu --prompt "$1" --insensitive
}
terminal_hold() {
local title="$1"
shift
if command -v kitty >/dev/null 2>&1; then
kitty --title "$title" sh -lc "$*; printf '\n'; read -r -p 'Enter zum Schliessen... ' _"
else
notify "kitty ist nicht installiert."
fi
}
shell_quote() {
printf '%q' "$1"
}
code_cmd() {
if command -v code >/dev/null 2>&1; then
printf '%s\n' code
elif command -v codium >/dev/null 2>&1; then
printf '%s\n' codium
else
return 1
fi
}
docker_available() {
if ! command -v docker >/dev/null 2>&1; then
notify "Docker ist nicht installiert."
return 1
fi
if ! docker info >/dev/null 2>&1; then
notify "Docker laeuft nicht oder ist nicht erreichbar."
return 1
fi
}
has_compose() {
local project="$1"
[[ -f "$project/compose.yml" ||
-f "$project/compose.yaml" ||
-f "$project/docker-compose.yml" ||
-f "$project/docker-compose.yaml" ]]
}
project_label() {
local project="$1"
local marker=""
if [[ -d "$project/.git" ]]; then
marker+=" "
fi
if has_compose "$project"; then
marker+="󰡨 "
fi
printf '%s%s' "$marker" "${project#$HOME/}"
}
pick_project() {
local roots=()
local projects=()
local labels=()
local root project choice
for root in "${PROJECT_ROOTS[@]}"; do
[[ -d "$root" ]] && roots+=("$root")
done
if ((${#roots[@]} == 0)); then
notify "Keine Projektordner gefunden."
return 1
fi
mapfile -t projects < <(
{
find "${roots[@]}" -maxdepth 4 -type d -name .git -printf '%h\n' 2>/dev/null
find "${roots[@]}" -maxdepth 3 -type d \( -name node_modules -o -name vendor \) -prune -o \
-type f \( -name compose.yml -o -name compose.yaml -o -name docker-compose.yml -o -name docker-compose.yaml -o -name package.json -o -name Cargo.toml -o -name pyproject.toml -o -name go.mod \) \
-printf '%h\n' 2>/dev/null
} |
sort -u
)
if ((${#projects[@]} == 0)); then
notify "Keine Projekte gefunden."
return 1
fi
for project in "${projects[@]}"; do
labels+=("$(project_label "$project")")
done
choice="$(printf '%s\n' "${labels[@]}" | pick "📁 Projekt")"
[[ -z "${choice:-}" ]] && return 1
for i in "${!labels[@]}"; do
if [[ "$choice" == "${labels[$i]}" ]]; then
printf '%s\n' "${projects[$i]}"
return 0
fi
done
return 1
}
open_project_browser() {
local project="$1"
local choice url
choice="$(
printf '%s\n' \
"🌐 http://localhost:3000" \
"🌐 http://localhost:5173" \
"🌐 http://localhost:8000" \
"🌐 http://localhost:8080" \
"🌐 http://localhost:5000" \
"🌐 http://localhost:4200" \
"📂 Projektordner im Browser" |
pick "🌐 Projekt im Browser"
)"
case "$choice" in
*"Projektordner"*)
xdg-open "$project" >/dev/null 2>&1 &
;;
*"http"*)
url="${choice#* }"
xdg-open "$url" >/dev/null 2>&1 &
;;
esac
}
project_menu() {
local project project_q choice editor
project="$(pick_project)" || return 0
project_q="$(shell_quote "$project")"
choice="$(
printf '%s\n' \
"📂 Projekt oeffnen (Code)" \
"🌐 Projekt im Browser oeffnen" \
"🐳 Docker Compose starten" \
"🛑 Docker stoppen" \
"🔄 Container neu starten" \
"📜 Logs anzeigen" \
"📦 Git Repo Status anzeigen" |
pick "📁 $(basename "$project")"
)"
case "$choice" in
*"Projekt oeffnen"*)
if editor="$(code_cmd)"; then
"$editor" "$project" >/dev/null 2>&1 &
else
notify "VS Code/Codium ist nicht installiert."
fi
;;
*"Browser"*)
open_project_browser "$project"
;;
*"Docker Compose starten"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Compose: $(basename "$project")" "cd $project_q && docker compose up -d && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Docker stoppen"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Stop: $(basename "$project")" "cd $project_q && docker compose stop && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Container neu starten"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Restart: $(basename "$project")" "cd $project_q && docker compose restart && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Logs anzeigen"*)
docker_available || return 0
if has_compose "$project"; then
if command -v kitty >/dev/null 2>&1; then
kitty --title "Logs: $(basename "$project")" sh -lc "cd $project_q && docker compose logs -f --tail=200"
else
notify "kitty ist nicht installiert."
fi
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Git Repo Status"*)
if [[ -d "$project/.git" ]]; then
terminal_hold "Git Status: $(basename "$project")" "cd $project_q && git status --short --branch"
else
notify "Kein Git Repository."
fi
;;
esac
}
pick_container() {
local containers=()
local labels=()
local line name status choice
docker_available || return 1
mapfile -t containers < <(docker ps -a --format '{{.Names}}|{{.Status}}' | sort)
if ((${#containers[@]} == 0)); then
notify "Keine Docker Container gefunden."
return 1
fi
for line in "${containers[@]}"; do
name="${line%%|*}"
status="${line#*|}"
labels+=("🐳 $name · $status")
done
choice="$(printf '%s\n' "${labels[@]}" | pick "🐳 Container")"
[[ -z "${choice:-}" ]] && return 1
for i in "${!labels[@]}"; do
if [[ "$choice" == "${labels[$i]}" ]]; then
printf '%s\n' "${containers[$i]%%|*}"
return 0
fi
done
return 1
}
docker_menu() {
local choice container running_ids all_ids
choice="$(
printf '%s\n' \
"▶️ Alle Container starten" \
"⏹️ Alle stoppen" \
"🔄 Einzelnen Container neu starten" \
"📊 docker stats" \
"📜 Logs anzeigen" \
"🧹 Cleanup dangling images" |
pick "🐳 Docker Control"
)"
case "$choice" in
*"Alle Container starten"*)
docker_available || return 0
all_ids="$(docker ps -aq)"
if [[ -n "$all_ids" ]]; then
terminal_hold "Docker Start" "docker start $all_ids && docker ps"
else
notify "Keine Container gefunden."
fi
;;
*"Alle stoppen"*)
docker_available || return 0
running_ids="$(docker ps -q)"
if [[ -n "$running_ids" ]]; then
terminal_hold "Docker Stop" "docker stop $running_ids && docker ps -a"
else
notify "Keine laufenden Container."
fi
;;
*"Einzelnen Container neu starten"*)
container="$(pick_container)" || return 0
terminal_hold "Docker Restart: $container" "docker restart '$container' && docker ps -a --filter name='^/$container$'"
;;
*"docker stats"*)
docker_available || return 0
if command -v kitty >/dev/null 2>&1; then
kitty --title "docker stats" sh -lc "docker stats"
else
notify "kitty ist nicht installiert."
fi
;;
*"Logs anzeigen"*)
container="$(pick_container)" || return 0
if command -v kitty >/dev/null 2>&1; then
kitty --title "Logs: $container" sh -lc "docker logs -f --tail=200 '$container'"
else
notify "kitty ist nicht installiert."
fi
;;
*"Cleanup dangling images"*)
docker_available || return 0
terminal_hold "Docker Cleanup" "docker image prune -f"
;;
esac
}
choice="$(
printf '%s\n' \
"📁 Projekt Management" \
"󰌘 Homelab Controlcenter" \
"🐳 Docker Control" \
" Terminal" \
" Projektordner" \
" VS Code / Codium" \
"󰊢 Git GUI" |
pick "󰅩 Dev Menue"
)"
case "$choice" in
*"Projekt Management"*)
project_menu
;;
*"Homelab Controlcenter"*)
"$SCRIPT_DIR/homelab-control.sh"
;;
*"Docker Control"*)
docker_menu
;;
*"Terminal"*)
kitty
;;
*"Projektordner"*)
nautilus
;;
*"VS Code"*|*"Codium"*)
if editor="$(code_cmd)"; then
"$editor" >/dev/null 2>&1 &
else
notify "VS Code/Codium ist nicht installiert."
fi
;;
*"Git GUI"*)
if command -v gitg >/dev/null 2>&1; then
gitg
elif command -v git-cola >/dev/null 2>&1; then
git-cola
else
notify "gitg oder git-cola ist nicht installiert."
fi
;;
esac

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env bash
set -euo pipefail
notify() {
notify-send "󰍹 Display" "$1"
}
wofi_pick() {
wofi --dmenu --prompt "$1" --insensitive
}
connected_monitors() {
hyprctl monitors all 2>/dev/null | awk '
/^Monitor / {
name=$2
disabled=0
}
/disabled: true/ {
disabled=1
}
/^$/ && name != "" {
if (!disabled) print name
name=""
}
END {
if (name != "" && !disabled) print name
}
'
}
all_monitors() {
hyprctl monitors all 2>/dev/null | awk '/^Monitor / {print $2}'
}
first_monitor() {
connected_monitors | head -n 1
}
second_monitor() {
connected_monitors | sed -n '2p'
}
show_status() {
hyprctl monitors all | wofi --dmenu --prompt "󰩟 Displaystatus"
}
choose_monitor() {
local monitors
monitors="$(all_monitors)"
if [ -z "$monitors" ]; then
notify "Keine Monitore ueber hyprctl gefunden."
exit 0
fi
printf '%s\n' "$monitors" | awk '{print "󰍹 " $0}' | wofi_pick "$1"
}
disable_monitor() {
local selection monitor
selection="$(choose_monitor "󰍹 Monitor deaktivieren")"
[ -n "$selection" ] || exit 0
monitor="${selection#* }"
hyprctl keyword monitor "$monitor,disable" && notify "$monitor deaktiviert."
}
enable_preferred() {
local selection monitor
selection="$(choose_monitor "󰍹 Monitor aktivieren")"
[ -n "$selection" ] || exit 0
monitor="${selection#* }"
hyprctl keyword monitor "$monitor,preferred,auto,1" && notify "$monitor aktiviert."
}
extend_right() {
local primary secondary
primary="$(first_monitor)"
secondary="$(second_monitor)"
if [ -z "$primary" ] || [ -z "$secondary" ]; then
notify "Dafuer muessen mindestens zwei aktive Monitore vorhanden sein."
exit 0
fi
hyprctl keyword monitor "$primary,preferred,0x0,1"
hyprctl keyword monitor "$secondary,preferred,auto-right,1"
notify "Displays erweitert."
}
mirror_displays() {
local primary secondary
primary="$(first_monitor)"
secondary="$(second_monitor)"
if [ -z "$primary" ] || [ -z "$secondary" ]; then
notify "Dafuer muessen mindestens zwei aktive Monitore vorhanden sein."
exit 0
fi
hyprctl keyword monitor "$primary,preferred,0x0,1"
hyprctl keyword monitor "$secondary,preferred,0x0,1,mirror,$primary"
notify "Displays gespiegelt."
}
choice="$(
printf '%s\n' \
"󰍹 Status anzeigen" \
"󰑓 Display-Konfig neu laden" \
"󰹑 Monitor aktivieren" \
"󰶐 Monitor deaktivieren" \
"󰹑 Displays erweitern" \
"󰹑 Displays spiegeln" \
"󰍹 Grafisches Display-Tool" |
wofi_pick "󰍹 Display"
)"
case "$choice" in
*"Status anzeigen"*)
show_status
;;
*"Display-Konfig neu laden"*)
hyprctl reload
;;
*"Monitor aktivieren"*)
enable_preferred
;;
*"Monitor deaktivieren"*)
disable_monitor
;;
*"Displays erweitern"*)
extend_right
;;
*"Displays spiegeln"*)
mirror_displays
;;
*"Grafisches Display-Tool"*)
if command -v nwg-displays >/dev/null 2>&1; then
nwg-displays
elif command -v wdisplays >/dev/null 2>&1; then
wdisplays
else
notify "Installiere nwg-displays oder wdisplays fuer ein grafisches Display-Tool."
fi
;;
esac

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HYPR_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
notify() {
notify-send "Homelab" "$1" >/dev/null 2>&1 || true
}
if ! command -v ags >/dev/null 2>&1; then
notify "ags ist nicht installiert."
exit 1
fi
if ! command -v sshpass >/dev/null 2>&1; then
notify "sshpass ist nicht installiert."
exit 1
fi
cd "$HYPR_DIR"
ags quit --instance homelab-control >/dev/null 2>&1 || true
exec ags run "$HYPR_DIR/ags/homelab.tsx"

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if pgrep -x wofi >/dev/null; then
pkill -x wofi
exit 0
fi
choice="$(
printf '%s\n' \
"󰀻 Apps" \
"󰅩 Dev Menue" \
"󰒓 Einstellungen" \
" Terminal" \
" Dateien" \
"󰑓 Hyprland neu laden" \
"󰍃 Session beenden" |
wofi --dmenu --prompt "󰣇 Hauptmenue" --insensitive
)"
case "$choice" in
*"Apps"*)
wofi --show drun
;;
*"Dev Menue"*)
"$SCRIPT_DIR/dev-menu.sh"
;;
*"Einstellungen"*)
"$SCRIPT_DIR/settings-menu.sh"
;;
*"Terminal"*)
kitty
;;
*"Dateien"*)
nautilus
;;
*"Hyprland neu laden"*)
hyprctl reload
;;
*"Session beenden"*)
if command -v hyprshutdown >/dev/null 2>&1; then
hyprshutdown
else
hyprctl dispatch exit
fi
;;
esac

View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bash
set -euo pipefail
notify() {
notify-send "󰤨 Netzwerk" "$1"
}
require_nmcli() {
if ! command -v nmcli >/dev/null 2>&1; then
notify "nmcli ist nicht installiert."
exit 1
fi
}
wofi_pick() {
wofi --dmenu --prompt "$1" --insensitive
}
wifi_status() {
nmcli -t -f WIFI general | head -n 1
}
active_connection() {
nmcli -t -f NAME,TYPE connection show --active | awk -F: '$2 ~ /wireless|ethernet/ {print $1; exit}'
}
connect_wifi() {
nmcli radio wifi on
nmcli device wifi rescan >/dev/null 2>&1 || true
local selection ssid security password
selection="$(
nmcli -t -f IN-USE,SSID,SECURITY,SIGNAL device wifi list |
awk -F: '
$2 != "" {
icon = $1 == "*" ? "󰁥" : "󰤨"
lock = $3 == "" ? "offen" : "gesichert"
printf "%s %s [%s, %s%%]\n", icon, $2, lock, $4
}
' |
wofi_pick "󰤨 WLAN verbinden"
)"
[ -n "$selection" ] || exit 0
ssid="$(printf '%s\n' "$selection" | sed -E 's/^[^ ]+ //; s/ \[.*$//')"
security="$(nmcli -t -f SSID,SECURITY device wifi list | awk -F: -v ssid="$ssid" '$1 == ssid {print $2; exit}')"
if [ -n "$security" ]; then
password="$(printf '' | wofi --dmenu --password --prompt "󰌾 Passwort fuer $ssid")"
[ -n "$password" ] || exit 0
nmcli device wifi connect "$ssid" password "$password" && notify "Verbunden mit $ssid."
else
nmcli device wifi connect "$ssid" && notify "Verbunden mit $ssid."
fi
}
saved_connections() {
local selection name
selection="$(
nmcli -t -f NAME,TYPE connection show |
awk -F: '$2 ~ /wireless|ethernet/ {printf "󰛳 %s\n", $1}' |
wofi_pick "󰛳 Gespeicherte Verbindungen"
)"
[ -n "$selection" ] || exit 0
name="${selection#* }"
nmcli connection up "$name" && notify "Verbindung $name gestartet."
}
disconnect_network() {
local conn
conn="$(active_connection)"
if [ -z "$conn" ]; then
notify "Keine aktive Netzwerkverbindung gefunden."
exit 0
fi
nmcli connection down "$conn" && notify "Verbindung $conn getrennt."
}
require_nmcli
current="$(active_connection)"
[ -n "$current" ] || current="Nicht verbunden"
choice="$(
printf '%s\n' \
"󰤨 WLAN verbinden" \
"󰖩 WLAN ein/aus" \
"󰛳 Gespeicherte Verbindungen" \
"󰅖 Aktive Verbindung trennen" \
"󰑓 Netzwerk neu scannen" \
"󰩟 Status: $current" |
wofi_pick "󰤨 Netzwerk"
)"
case "$choice" in
*"WLAN verbinden"*)
connect_wifi
;;
*"WLAN ein/aus"*)
if [ "$(wifi_status)" = "enabled" ]; then
nmcli radio wifi off && notify "WLAN ausgeschaltet."
else
nmcli radio wifi on && notify "WLAN eingeschaltet."
fi
;;
*"Gespeicherte Verbindungen"*)
saved_connections
;;
*"Aktive Verbindung trennen"*)
disconnect_network
;;
*"Netzwerk neu scannen"*)
nmcli device wifi rescan && notify "WLAN-Scan gestartet."
;;
*"Status:"*)
nmcli general status | wofi --dmenu --prompt "󰩟 Netzwerkstatus"
;;
esac

211
config/hypr/Scripts/power-menu.py Executable file
View File

@@ -0,0 +1,211 @@
#!/usr/bin/env python3
import os
import shlex
import subprocess
from pathlib import Path
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk, Gtk # noqa: E402
HYPR_DIR = Path.home() / ".config" / "hypr"
THEME_DIR = HYPR_DIR / "Themes"
CURRENT_WALLPAPER = HYPR_DIR / "current-wallpaper"
DEFAULT_THEME = {
"NAME": "Power",
"ACCENT": "#f38ba8",
"ACCENT_2": "#cba6f7",
"BACKGROUND_HEX": "#18141f",
"PANEL_HEX": "#313244",
"FOREGROUND": "#f5e0dc",
"MUTED": "#a6adc8",
"SELECTED_TEXT": "#11111b",
}
def parse_theme_file(path):
theme = {}
for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
try:
parsed = shlex.split(value)
theme[key] = parsed[0] if parsed else ""
except ValueError:
theme[key] = value.strip("\"'")
return theme
def load_current_theme():
current_wallpaper = ""
if CURRENT_WALLPAPER.exists():
current_wallpaper = CURRENT_WALLPAPER.read_text(encoding="utf-8", errors="ignore").strip()
for theme_file in sorted(THEME_DIR.glob("*.theme")):
theme = parse_theme_file(theme_file)
if theme.get("WALLPAPER") == current_wallpaper:
return {**DEFAULT_THEME, **theme}
rose = THEME_DIR / "rose-night.theme"
if rose.exists():
return {**DEFAULT_THEME, **parse_theme_file(rose)}
return DEFAULT_THEME
def run(command):
subprocess.Popen(command, start_new_session=True)
Gtk.main_quit()
class PowerMenu(Gtk.Window):
def __init__(self):
super().__init__(title="Power")
self.theme = load_current_theme()
self.set_decorated(False)
self.set_resizable(False)
self.set_keep_above(True)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True)
self.set_app_paintable(True)
self.connect("key-press-event", self.on_key_press)
self.connect("focus-out-event", lambda *_: Gtk.main_quit())
overlay = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=18)
overlay.get_style_context().add_class("power-shell")
title = Gtk.Label(label=self.theme.get("NAME", "Power"))
title.get_style_context().add_class("power-title")
overlay.pack_start(title, False, False, 0)
grid = Gtk.Grid()
grid.set_column_spacing(14)
grid.set_row_spacing(14)
grid.set_halign(Gtk.Align.CENTER)
actions = [
("", "Sperren", ["hyprlock"]),
("󰤄", "Ruhezustand", ["systemctl", "suspend"]),
("󰗽", "Abmelden", ["hyprctl", "dispatch", "exit"]),
("󰜉", "Neustart", ["systemctl", "reboot"]),
("", "Ausschalten", ["systemctl", "poweroff"]),
("󰗼", "Abbrechen", None),
]
for index, (icon, label, command) in enumerate(actions):
button = self.make_button(icon, label, command)
grid.attach(button, index % 3, index // 3, 1, 1)
overlay.pack_start(grid, False, False, 0)
self.add(overlay)
self.apply_css()
def make_button(self, icon, label, command):
button = Gtk.Button()
button.get_style_context().add_class("power-button")
button.set_size_request(150, 118)
button.set_relief(Gtk.ReliefStyle.NONE)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
box.set_halign(Gtk.Align.CENTER)
box.set_valign(Gtk.Align.CENTER)
icon_label = Gtk.Label(label=icon)
icon_label.get_style_context().add_class("power-icon")
text_label = Gtk.Label(label=label)
text_label.get_style_context().add_class("power-label")
box.pack_start(icon_label, False, False, 0)
box.pack_start(text_label, False, False, 0)
button.add(box)
if command:
button.connect("clicked", lambda *_: run(command))
else:
button.connect("clicked", lambda *_: Gtk.main_quit())
return button
def apply_css(self):
css = f"""
window {{
background: transparent;
}}
.power-shell {{
margin: 0;
padding: 24px;
border-radius: 26px;
border: 1px solid {self.theme["ACCENT"]};
background: alpha({self.theme["BACKGROUND_HEX"]}, 0.94);
box-shadow: 0 22px 70px alpha(#000000, 0.55);
}}
.power-title {{
color: {self.theme["FOREGROUND"]};
font-family: "JetBrainsMono Nerd Font", "JetBrains Mono", sans-serif;
font-size: 18px;
font-weight: 800;
}}
.power-button {{
color: {self.theme["FOREGROUND"]};
background: alpha({self.theme["PANEL_HEX"]}, 0.82);
border: 1px solid alpha({self.theme["ACCENT_2"]}, 0.42);
border-radius: 18px;
transition: 160ms ease;
}}
.power-button:hover,
.power-button:focus {{
color: {self.theme["SELECTED_TEXT"]};
background: linear-gradient(135deg, {self.theme["ACCENT"]}, {self.theme["ACCENT_2"]});
border-color: {self.theme["ACCENT"]};
}}
.power-icon {{
font-family: "JetBrainsMono Nerd Font", "JetBrains Mono", sans-serif;
font-size: 34px;
font-weight: 800;
}}
.power-label {{
font-family: "JetBrainsMono Nerd Font", "JetBrains Mono", sans-serif;
font-size: 13px;
font-weight: 800;
}}
"""
provider = Gtk.CssProvider()
provider.load_from_data(css.encode("utf-8"))
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
)
def on_key_press(self, _widget, event):
if event.keyval in (Gdk.KEY_Escape, Gdk.KEY_q):
Gtk.main_quit()
if __name__ == "__main__":
window = PowerMenu()
window.show_all()
Gtk.main()

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env bash
set -euo pipefail
screenshot_dir="${XDG_PICTURES_DIR:-$HOME/Pictures}/Screenshots"
notify() {
notify-send "󰸉 Screenshot" "$1"
}
require_cmd() {
local cmd="$1"
if ! command -v "$cmd" >/dev/null 2>&1; then
notify "$cmd ist nicht installiert."
exit 1
fi
}
filename() {
date +%Y-%m-%d_%H-%M-%S.png
}
take_hyprshot() {
local mode="$1"
local name="$2"
require_cmd hyprshot
mkdir -p "$screenshot_dir"
hyprshot -m "$mode" -o "$screenshot_dir" -f "$name" -s
printf '%s/%s\n' "$screenshot_dir" "$name"
}
annotate_region() {
local name path
require_cmd satty
name="$(filename)"
path="$(take_hyprshot region "$name")"
satty --filename "$path" --output-filename "$path"
}
quick_region() {
local name
name="$(filename)"
take_hyprshot region "$name" >/dev/null
notify "Bereich gespeichert."
}
quick_window() {
local name
name="$(filename)"
take_hyprshot window "$name" >/dev/null
notify "Fenster gespeichert."
}
quick_output() {
local name
name="$(filename)"
take_hyprshot output "$name" >/dev/null
notify "Bildschirm gespeichert."
}
copy_region() {
require_cmd hyprshot
hyprshot -m region --clipboard-only -s
notify "Bereich in die Zwischenablage kopiert."
}
show_menu() {
require_cmd wofi
local choice
choice="$(
printf '%s\n' \
"󰹑 Bereich markieren" \
"󰸉 Bereich speichern" \
"󰍹 Fenster speichern" \
"󰹑 Bildschirm speichern" \
"󰅌 Bereich kopieren" |
wofi --dmenu --prompt "󰸉 Screenshot" --insensitive
)"
case "$choice" in
*"Bereich markieren"*)
annotate_region
;;
*"Bereich speichern"*)
quick_region
;;
*"Fenster speichern"*)
quick_window
;;
*"Bildschirm speichern"*)
quick_output
;;
*"Bereich kopieren"*)
copy_region
;;
esac
}
case "${1:-menu}" in
annotate-region)
annotate_region
;;
region)
quick_region
;;
window)
quick_window
;;
output)
quick_output
;;
copy-region)
copy_region
;;
menu)
show_menu
;;
*)
notify "Unbekannter Screenshot-Modus: $1"
exit 2
;;
esac

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
choice="$(
printf '%s\n' \
"󰉼 Aussehen" \
"󰕾 Audio" \
"󰂯 Bluetooth" \
"󰍹 Display" \
"󰤨 Netzwerk" \
"󰸉 Screenshot" \
"󰒓 System" |
wofi --dmenu --prompt "󰒓 Einstellungen" --insensitive
)"
case "$choice" in
*"Aussehen"*)
"$SCRIPT_DIR/appearance-menu.sh"
;;
*"Audio"*)
"$SCRIPT_DIR/audio-menu.sh"
;;
*"Netzwerk"*)
"$SCRIPT_DIR/network-menu.sh"
;;
*"Bluetooth"*)
"$SCRIPT_DIR/bluetooth-menu.sh"
;;
*"Display"*)
"$SCRIPT_DIR/display-menu.sh"
;;
*"Screenshot"*)
"$SCRIPT_DIR/screenshot-menu.sh"
;;
*"System"*)
"$SCRIPT_DIR/system-menu.sh"
;;
esac

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -euo pipefail
notify() {
notify-send "󰒓 System" "$1"
}
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
start_detached() {
local command_name="$1"
local unit_name="${command_name//[^[:alnum:]_.-]/-}"
if command -v systemd-run >/dev/null 2>&1; then
systemd-run --user --unit="$unit_name" --collect "$@" >/dev/null 2>&1 && return
fi
if command -v setsid >/dev/null 2>&1; then
setsid -f "$@" >/dev/null 2>&1
else
"$@" >/dev/null 2>&1 &
fi
}
restart_waybar() {
local i
pkill -x waybar >/dev/null 2>&1 || true
for i in {1..20}; do
pgrep -x waybar >/dev/null 2>&1 || break
sleep 0.05
done
pgrep -x waybar >/dev/null 2>&1 && pkill -9 -x waybar >/dev/null 2>&1 || true
start_detached waybar
}
choice="$(
printf '%s\n' \
"󰑓 Hyprland neu laden" \
"󰌢 Waybar neu starten" \
"󰗽 Bildschirm heller" \
"󰗾 Bildschirm dunkler" \
"󰍃 Session beenden" |
wofi --dmenu --prompt "󰒓 System" --insensitive
)"
case "$choice" in
*"Hyprland neu laden"*)
hyprctl reload
;;
*"Waybar neu starten"*)
restart_waybar
;;
*"Bildschirm heller"*)
if command -v brightnessctl >/dev/null 2>&1; then
brightnessctl -e4 -n2 set 5%+
else
notify "brightnessctl ist nicht installiert."
fi
;;
*"Bildschirm dunkler"*)
if command -v brightnessctl >/dev/null 2>&1; then
brightnessctl -e4 -n2 set 5%-
else
notify "brightnessctl ist nicht installiert."
fi
;;
*"Session beenden"*)
if command -v hyprshutdown >/dev/null 2>&1; then
hyprshutdown
else
hyprctl dispatch exit
fi
;;
esac

1204
config/hypr/Scripts/theme-menu.sh Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
if pgrep -x wofi >/dev/null; then
pkill -x wofi
else
wofi --show drun
fi

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HYPR_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
WALLPAPER_DIR="${WALLPAPER_DIR:-$HOME/Bilder/Wallpaper}"
HYPRPAPER_CONF="$HYPR_DIR/hyprpaper.conf"
CURRENT_WALLPAPER="$HYPR_DIR/current-wallpaper"
TRANSITION_TYPE="${TRANSITION_TYPE:-grow}"
TRANSITION_DURATION="${TRANSITION_DURATION:-1.0}"
TRANSITION_FPS="${TRANSITION_FPS:-60}"
TRANSITION_POS="${TRANSITION_POS:-center}"
notify() {
notify-send "󰸉 Wallpaper" "$1" >/dev/null 2>&1 || true
}
start_detached() {
local command_name="$1"
local unit_name="${command_name//[^[:alnum:]_.-]/-}"
if command -v systemd-run >/dev/null 2>&1; then
systemd-run --user --unit="$unit_name" --collect "$@" >/dev/null 2>&1 && return
fi
if command -v setsid >/dev/null 2>&1; then
setsid -f "$@" >/dev/null 2>&1
else
"$@" >/dev/null 2>&1 &
fi
}
apply_wallpaper() {
local wallpaper="$1"
cat >"$HYPRPAPER_CONF" <<EOF
wallpaper {
monitor =
path = $wallpaper
fit_mode = cover
}
EOF
printf '%s\n' "$wallpaper" >"$CURRENT_WALLPAPER"
if command -v awww >/dev/null 2>&1; then
if ! pgrep -x awww-daemon >/dev/null 2>&1; then
start_detached awww-daemon
sleep 0.2
fi
awww img "$wallpaper" \
--transition-type "$TRANSITION_TYPE" \
--transition-duration "$TRANSITION_DURATION" \
--transition-fps "$TRANSITION_FPS" \
--transition-pos "$TRANSITION_POS" >/dev/null 2>&1 || true
return
fi
if command -v swww >/dev/null 2>&1; then
if ! pgrep -x swww-daemon >/dev/null 2>&1; then
start_detached swww-daemon
sleep 0.2
fi
swww img "$wallpaper" \
--transition-type "$TRANSITION_TYPE" \
--transition-duration "$TRANSITION_DURATION" \
--transition-fps "$TRANSITION_FPS" \
--transition-pos "$TRANSITION_POS" >/dev/null 2>&1 || true
return
fi
if command -v hyprctl >/dev/null 2>&1; then
if ! pgrep -x hyprpaper >/dev/null 2>&1; then
start_detached hyprpaper
sleep 0.2
fi
hyprctl hyprpaper preload "$wallpaper" >/dev/null || true
hyprctl hyprpaper wallpaper ",$wallpaper" >/dev/null || true
fi
}
preview_wallpaper() {
local wallpaper="$1"
if command -v swayimg >/dev/null 2>&1; then
start_detached swayimg "$wallpaper"
return
fi
if command -v imv >/dev/null 2>&1; then
start_detached imv "$wallpaper"
return
fi
if command -v kitty >/dev/null 2>&1; then
start_detached kitty \
--class wallpaper-preview \
--title "Wallpaper Vorschau" \
sh -c 'clear; printf "%s\n\n" "$1"; kitty +kitten icat --fit both --align center "$1"; printf "\nEnter schliesst die Vorschau."; read -r _' \
sh "$wallpaper"
return
fi
notify "Kein Vorschauprogramm gefunden."
}
if [[ "${1:-}" == "--apply" ]]; then
wallpaper="${2:-}"
if [[ -z "$wallpaper" || ! -f "$wallpaper" ]]; then
notify "Wallpaper nicht gefunden."
exit 1
fi
apply_wallpaper "$wallpaper"
notify "$(basename "$wallpaper") angewendet."
exit 0
fi
if [[ "${1:-}" == "--preview" ]]; then
wallpaper="${2:-}"
if [[ -z "$wallpaper" || ! -f "$wallpaper" ]]; then
notify "Wallpaper nicht gefunden."
exit 1
fi
preview_wallpaper "$wallpaper"
exit 0
fi
pick_wallpaper() {
local entries=()
local wallpaper
mapfile -t wallpapers < <(
find "$WALLPAPER_DIR" -maxdepth 1 -type f \
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' -o -iname '*.gif' \) |
sort
)
if ((${#wallpapers[@]} == 0)); then
notify "Keine Bilder in $WALLPAPER_DIR gefunden."
exit 1
fi
for wallpaper in "${wallpapers[@]}"; do
entries+=("󰸉 $(basename "$wallpaper")")
done
choice="$(
printf '%s\n' "${entries[@]}" |
wofi --dmenu --prompt "󰸉 Wallpaper" --insensitive
)"
[[ -z "${choice:-}" ]] && exit 0
for i in "${!entries[@]}"; do
if [[ "$choice" == "${entries[$i]}" ]]; then
printf '%s\n' "${wallpapers[$i]}"
return
fi
done
}
while true; do
wallpaper="$(pick_wallpaper)"
[[ -z "${wallpaper:-}" ]] && exit 0
action="$(
printf '%s\n' \
"󰋩 Vorschau" \
"󰄬 Anwenden" \
"󰁍 Zurueck" |
wofi --dmenu --prompt "󰸉 $(basename "$wallpaper")" --insensitive
)"
case "$action" in
*"Vorschau"*)
preview_wallpaper "$wallpaper"
;;
*"Anwenden"*)
apply_wallpaper "$wallpaper"
notify "$(basename "$wallpaper") angewendet."
exit 0
;;
*"Zurueck"*)
;;
*)
exit 0
;;
esac
done