#!/bin/bash # tui-install.sh — TUI installer for the_miro's Arch dotfiles set -uo pipefail # ── Paths ───────────────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOTFILES_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" MODULES="$DOTFILES_DIR/setup/modules" APPS="$MODULES/optional-Modules/apps" LOG="$HOME/dotfiles-install.log" TMP_D="$(mktemp -d)" trap 'rm -rf "$TMP_D"' EXIT BACKTITLE="the_miro's Arch Dotfiles" # ── Cyberqueer dialog theme ─────────────────────────────────────────────────── export DIALOGRC="$TMP_D/dialogrc" cat > "$DIALOGRC" <<'EOF' use_shadow = ON use_colors = ON screen_color = (BLACK,BLACK,ON) shadow_color = (BLACK,BLACK,ON) title_color = (MAGENTA,BLACK,ON) border_color = (MAGENTA,BLACK,ON) button_active_color = (BLACK,MAGENTA,ON) button_inactive_color = (WHITE,BLACK,OFF) button_key_active_color = (BLACK,CYAN,ON) button_key_inactive_color = (CYAN,BLACK,ON) button_label_active_color = (BLACK,MAGENTA,ON) button_label_inactive_color = (WHITE,BLACK,OFF) inputbox_color = (WHITE,BLACK,OFF) inputbox_border_color = (MAGENTA,BLACK,ON) menubox_color = (WHITE,BLACK,OFF) menubox_border_color = (MAGENTA,BLACK,ON) item_color = (WHITE,BLACK,OFF) item_selected_color = (BLACK,MAGENTA,ON) tag_color = (CYAN,BLACK,ON) tag_selected_color = (BLACK,CYAN,ON) tag_key_color = (CYAN,BLACK,ON) tag_key_selected_color = (BLACK,CYAN,ON) check_color = (WHITE,BLACK,OFF) check_selected_color = (BLACK,MAGENTA,ON) uarrow_color = (MAGENTA,BLACK,ON) darrow_color = (MAGENTA,BLACK,ON) EOF # ── State ───────────────────────────────────────────────────────────────────── STEP=0 TOTAL=0 # ── Helpers ─────────────────────────────────────────────────────────────────── require_dialog() { command -v dialog &>/dev/null && return echo "dialog not found — installing..." sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; } } die() { clear printf "\n Error: %s\n\n" "$1" >&2 exit 1 } log_sep() { printf "\n══════════════════════════════════\n %s\n %s\n" "$1" "$(date)" >> "$LOG" } run_module() { local label="$1" script="$2" STEP=$(( STEP + 1 )) log_sep "[$STEP/$TOTAL] $label" clear printf "\n\033[1;35m [$STEP/$TOTAL] %s\033[0m\n" "$label" printf "\033[35m ─────────────────────────────────────────────\033[0m\n\n" local rc=0 bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]} if [[ $rc -ne 0 ]]; then dialog --backtitle "$BACKTITLE" \ --title " Module Failed " \ --yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \ || { clear; exit 1; } fi } count_steps() { local c="$1" de="$2" a="${3:-}" TOTAL=0 [[ "$c" == *"pkg"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"core"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"svc"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"shell"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"de"* && "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"claude"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"networking-cli"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"disk-recovery"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"himalaya"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"gnuplot"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"povray"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"blender"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"toot"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"db-clients"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"mysql"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"productivity"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"yt-dlp"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"sox"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"imagemagick"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"ffmpeg"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"localtunnel"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"butter"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"tlp"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"steam"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"vesktop"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"spotify"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"prism"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"vintagestory"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"localsend"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"croc"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"onlyoffice"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"wireshark"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"k8s"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"docker"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"podman"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"cockpit"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"ssh-server"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"python"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"zfs"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"wprs"* ]] && TOTAL=$(( TOTAL + 1 )) } # ── Preflight ───────────────────────────────────────────────────────────────── [[ $EUID -eq 0 ]] && die "Run as your normal user (not root)." command -v pacman &>/dev/null || die "pacman not found — Arch Linux required." require_dialog > "$LOG" printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG" # ── Welcome ─────────────────────────────────────────────────────────────────── dialog --backtitle "$BACKTITLE" \ --title " Welcome " \ --msgbox "\n\ the_miro's Arch dotfiles installer\n\ Cyberqueer · Wayland · Hyprland\n\ ─────────────────────────────────────────\n\ \n\ Arch Linux — network admin, development & gaming\n\ \n\ Source: $DOTFILES_DIR\n\ Log: $LOG\n" 14 62 # ── Component selection ─────────────────────────────────────────────────────── COMPONENTS=$(dialog --backtitle "$BACKTITLE" \ --title " Select Components " \ --checklist "Space toggles · Enter confirms · Esc quits" 16 68 5 \ "pkg" "Package managers yay · nvm · rust" on \ "core" "Core packages 100+ base system packages" on \ "svc" "Core services NetworkManager · cronie · fail2ban" on \ "shell" "Shell setup zsh · nvim · yazi · micro · starship" on \ "de" "Desktop environment (choose on next screen)" off \ 3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; } # ── DE selection ────────────────────────────────────────────────────────────── DE="none" if [[ "$COMPONENTS" == *"de"* ]]; then DE=$(dialog --backtitle "$BACKTITLE" \ --title " Desktop Environment " \ --menu "Select a desktop environment:" 11 56 3 \ "hyprland" "Hyprland — Wayland WM, full setup (primary)" \ "sway" "Sway — Wayland tiling WM" \ "none" "Skip DE installation" \ 3>&1 1>&2 2>&3) || DE="none" fi # ── Apps selection ──────────────────────────────────────────────────────────── SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \ --title " Applications " \ --checklist "Optional applications — installed after base components:" 40 76 32 \ "claude" "Claude Code Anthropic CLI via npm" off \ "networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \ "disk-recovery" "Disk Recovery ddrescue · f3" off \ "himalaya" "Himalaya terminal email client (AUR)" off \ "gnuplot" "Gnuplot scientific plotting" off \ "povray" "POV-Ray ray-tracing renderer" off \ "blender" "Blender 3D creation suite" off \ "toot" "toot Mastodon CLI client (AUR)" off \ "db-clients" "DB Clients pgcli · mycli" off \ "mysql" "MySQL / MariaDB mariadb server + setup" off \ "productivity" "Productivity taskwarrior · watson · jrnl" off \ "yt-dlp" "yt-dlp YouTube / media downloader" off \ "sox" "SoX audio processing toolkit" off \ "imagemagick" "ImageMagick image manipulation" off \ "ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \ "localtunnel" "LocalTunnel expose localhost via tunnel" off \ "butter" "butter btrfs snapshot backup (AUR)" off \ "tlp" "TLP laptop power management" off \ "steam" "Steam gaming platform" off \ "vesktop" "Vesktop Discord + Vencord theme" off \ "spotify" "Spotify launcher + Spicetify theming" off \ "prism" "PrismLauncher Minecraft launcher (Flatpak)" off \ "vintagestory" "Vintage Story survival game (AUR)" off \ "localsend" "LocalSend LAN file transfer (AUR)" off \ "croc" "croc cross-platform file transfer" off \ "onlyoffice" "OnlyOffice office suite (AUR)" off \ "wireshark" "Wireshark network packet analyser (GUI)" off \ "k8s" "Kubernetes tools kubectl · podman-desktop" off \ "docker" "Docker docker · docker-compose" off \ "podman" "Podman rootless containers · buildah" off \ "cockpit" "Cockpit web UI · machines · podman" off \ "ssh-server" "SSH server openssh · key-auth · enabled" off \ "python" "Python tools pyright · pipx · pynvim" off \ "zfs" "ZFS zfs-dkms kernel module" off \ "wprs" "WPRS wprs-git (AUR)" off \ 3>&1 1>&2 2>&3) || SELECTED_APPS="" # ── Confirmation ────────────────────────────────────────────────────────────── SUMMARY="" [[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n" [[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n" [[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n" [[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n" [[ "$COMPONENTS" == *"de"* && "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n" if [[ -n "$SELECTED_APPS" ]]; then SUMMARY+="\n Applications:\n" [[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n" [[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n" [[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n" [[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n" [[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n" [[ "$SELECTED_APPS" == *"povray"* ]] && SUMMARY+=" ✦ POV-Ray\n" [[ "$SELECTED_APPS" == *"blender"* ]] && SUMMARY+=" ✦ Blender\n" [[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n" [[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n" [[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n" [[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n" [[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n" [[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n" [[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n" [[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n" [[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n" [[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n" [[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n" [[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n" [[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n" [[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n" [[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n" [[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n" [[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n" [[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n" [[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n" [[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n" [[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n" [[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n" [[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n" [[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n" [[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n" [[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n" [[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n" [[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n" fi dialog --backtitle "$BACKTITLE" \ --title " Confirm Installation " \ --yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \ 24 62 || { clear; echo "Aborted."; exit 0; } count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS" # ── Installation: base components ───────────────────────────────────────────── [[ "$COMPONENTS" == *"pkg"* ]] && run_module "Package Managers" "$MODULES/package-managers.sh" [[ "$COMPONENTS" == *"core"* ]] && run_module "Core Packages" "$MODULES/core-packages.sh" [[ "$COMPONENTS" == *"svc"* ]] && run_module "Core Services" "$MODULES/core.sh" [[ "$COMPONENTS" == *"shell"* ]] && run_module "Shell Setup" "$MODULES/shell-setup.sh" if [[ "$COMPONENTS" == *"de"* && "$DE" != "none" ]]; then case "$DE" in hyprland) run_module "Hyprland" "$MODULES/Desktop-Environments/hyprland.sh" ;; sway) run_module "Sway" "$MODULES/Desktop-Environments/sway.sh" ;; esac fi # ── Installation: applications ──────────────────────────────────────────────── [[ "$SELECTED_APPS" == *"claude"* ]] && run_module "Claude Code" "$APPS/claude.sh" [[ "$SELECTED_APPS" == *"networking-cli"* ]] && run_module "Networking CLI" "$APPS/networking-cli.sh" [[ "$SELECTED_APPS" == *"disk-recovery"* ]] && run_module "Disk Recovery" "$APPS/disk-recovery.sh" [[ "$SELECTED_APPS" == *"himalaya"* ]] && run_module "Himalaya" "$APPS/himalaya.sh" [[ "$SELECTED_APPS" == *"gnuplot"* ]] && run_module "Gnuplot" "$APPS/gnuplot.sh" [[ "$SELECTED_APPS" == *"povray"* ]] && run_module "POV-Ray" "$APPS/povray.sh" [[ "$SELECTED_APPS" == *"blender"* ]] && run_module "Blender" "$APPS/blender.sh" [[ "$SELECTED_APPS" == *"toot"* ]] && run_module "toot" "$APPS/toot.sh" [[ "$SELECTED_APPS" == *"db-clients"* ]] && run_module "DB Clients" "$APPS/db-clients.sh" [[ "$SELECTED_APPS" == *"mysql"* ]] && run_module "MySQL / MariaDB" "$APPS/mysql.sh" [[ "$SELECTED_APPS" == *"productivity"* ]] && run_module "Productivity" "$APPS/productivity.sh" [[ "$SELECTED_APPS" == *"yt-dlp"* ]] && run_module "yt-dlp" "$APPS/yt-dlp.sh" [[ "$SELECTED_APPS" == *"sox"* ]] && run_module "SoX" "$APPS/sox.sh" [[ "$SELECTED_APPS" == *"imagemagick"* ]] && run_module "ImageMagick" "$APPS/imagemagick.sh" [[ "$SELECTED_APPS" == *"ffmpeg"* ]] && run_module "FFmpeg extras" "$APPS/ffmpeg.sh" [[ "$SELECTED_APPS" == *"localtunnel"* ]] && run_module "LocalTunnel" "$APPS/localtunnel.sh" [[ "$SELECTED_APPS" == *"butter"* ]] && run_module "butter" "$APPS/butter.sh" [[ "$SELECTED_APPS" == *"tlp"* ]] && run_module "TLP" "$APPS/tlp.sh" [[ "$SELECTED_APPS" == *"steam"* ]] && run_module "Steam" "$APPS/steam.sh" [[ "$SELECTED_APPS" == *"vesktop"* ]] && run_module "Vesktop" "$APPS/vesktop.sh" [[ "$SELECTED_APPS" == *"spotify"* ]] && run_module "Spotify" "$APPS/spotify.sh" [[ "$SELECTED_APPS" == *"prism"* ]] && run_module "PrismLauncher" "$APPS/prismlauncher.sh" [[ "$SELECTED_APPS" == *"vintagestory"* ]] && run_module "Vintage Story" "$APPS/vintagestory.sh" [[ "$SELECTED_APPS" == *"localsend"* ]] && run_module "LocalSend" "$APPS/localsend.sh" [[ "$SELECTED_APPS" == *"croc"* ]] && run_module "croc" "$APPS/croc.sh" [[ "$SELECTED_APPS" == *"onlyoffice"* ]] && run_module "OnlyOffice" "$APPS/onlyoffice.sh" [[ "$SELECTED_APPS" == *"wireshark"* ]] && run_module "Wireshark" "$APPS/wireshark.sh" [[ "$SELECTED_APPS" == *"k8s"* ]] && run_module "Kubernetes Tools" "$APPS/k8s.sh" [[ "$SELECTED_APPS" == *"docker"* ]] && run_module "Docker" "$APPS/docker.sh" [[ "$SELECTED_APPS" == *"podman"* ]] && run_module "Podman" "$APPS/podman.sh" [[ "$SELECTED_APPS" == *"cockpit"* ]] && run_module "Cockpit" "$APPS/cockpit.sh" [[ "$SELECTED_APPS" == *"ssh-server"* ]] && run_module "SSH Server" "$APPS/ssh-server.sh" [[ "$SELECTED_APPS" == *"python"* ]] && run_module "Python Tools" "$MODULES/optional-Modules/python.sh" [[ "$SELECTED_APPS" == *"zfs"* ]] && run_module "ZFS" "$MODULES/optional-Modules/zfs.sh" [[ "$SELECTED_APPS" == *"wprs"* ]] && run_module "WPRS" "$MODULES/optional-Modules/wprs.sh" # ── Done ────────────────────────────────────────────────────────────────────── dialog --backtitle "$BACKTITLE" \ --title " Done " \ --msgbox "\n All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect.\n" 12 58 clear printf "\n Done. Log: %s\n\n" "$LOG"