#!/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 } launch_codex() { if ! command -v kitty >/dev/null 2>&1; then notify "kitty ist nicht installiert." return 0 fi if ! command -v codex >/dev/null 2>&1; then notify "codex ist nicht installiert." return 0 fi kitty --title "Codex" sh -lc 'cd "$HOME" && exec codex' >/dev/null 2>&1 & } launch_opencode() { if ! command -v kitty >/dev/null 2>&1; then notify "kitty ist nicht installiert." return 0 fi if ! command -v opencode >/dev/null 2>&1; then notify "opencode ist nicht installiert." return 0 fi kitty --title "opencode" sh -lc 'cd "$HOME" && exec opencode' >/dev/null 2>&1 & } choice="$( printf '%s\n' \ "๐Ÿ“ Projekt Management" \ "๓ฐŒ˜ Homelab Controlcenter" \ "๐Ÿณ Docker Control" \ "๓ฐšฉ Codex" \ "๓ฐšฉ opencode" \ "๎ž• 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 ;; *"Codex"*) launch_codex ;; *"opencode"*) launch_opencode ;; *"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