#!/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" 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 a module script in the terminal (dialog is cleared so stdin/stdout work # normally — pacman prompts, yay builds, etc. all function as expected). 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" 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 )) [[ "$c" == *"py"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"zfs"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"game"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"netdev"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$c" == *"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" 22 68 10 \ "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 \ "py" "Python tools pyright · pipx · python-pynvim" off \ "zfs" "ZFS support zfs-dkms" off \ "game" "Gaming Steam · Vesktop · Spotify · Prism" off \ "netdev" "Network dev tools nmap · wireshark · kubectl" off \ "wprs" "WPRS wprs-git (AUR)" 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 # ── 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" [[ "$COMPONENTS" == *"py"* ]] && SUMMARY+=" ✦ Python tools\n" [[ "$COMPONENTS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS support\n" [[ "$COMPONENTS" == *"game"* ]] && SUMMARY+=" ✦ Gaming packages\n" [[ "$COMPONENTS" == *"netdev"* ]] && SUMMARY+=" ✦ Network dev tools\n" [[ "$COMPONENTS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n" dialog --backtitle "$BACKTITLE" \ --title " Confirm Installation " \ --yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \ 20 62 || { clear; echo "Aborted."; exit 0; } count_steps "$COMPONENTS" "$DE" # ── Installation ────────────────────────────────────────────────────────────── [[ "$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-Enviroments/hyprland-new.sh" ;; sway) run_module "Sway" "$MODULES/Desktop-Enviroments/sway.sh" ;; esac fi [[ "$COMPONENTS" == *"py"* ]] && run_module "Python Tools" "$MODULES/optional-Modules/python.sh" [[ "$COMPONENTS" == *"zfs"* ]] && run_module "ZFS" "$MODULES/optional-Modules/zfs.sh" [[ "$COMPONENTS" == *"game"* ]] && run_module "Gaming Packages" "$MODULES/optional-Modules/gaming-packages.sh" [[ "$COMPONENTS" == *"netdev"* ]] && run_module "Network Dev Tools" "$MODULES/optional-Modules/network-developer-packages.sh" [[ "$COMPONENTS" == *"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"