diff --git a/setup/archbaseos-guided-install.sh b/setup/archbaseos-guided-install.sh index 1f55e0b..1e03df1 100755 --- a/setup/archbaseos-guided-install.sh +++ b/setup/archbaseos-guided-install.sh @@ -27,35 +27,14 @@ error_handler() { echo "" >> "$LOGFILE" echo "ERROR: installer failed at line $line_num (exit code $exit_code)" >> "$LOGFILE" - if command -v dialog &>/dev/null; then - if dialog --clear --title " Installer Error " \ - --yesno \ - "Installation failed at line $line_num (exit code: $exit_code).\n\nSend the log to another system via croc for analysis?" \ - 9 62; then - clear - if ! command -v croc &>/dev/null; then - echo "Installing croc..." - pacman -Sy --noconfirm croc 2>/dev/null || true - fi - if command -v croc &>/dev/null; then - croc send "$LOGFILE" - else - echo "croc unavailable — log is at: $LOGFILE" - fi - else - clear - echo "Log saved to: $LOGFILE" - fi + echo "" + echo "Installation failed at line $line_num (exit code $exit_code)." + read -rp "Send log via croc? [y/N]: " _croc_ans + if [[ "${_croc_ans,,}" == "y" ]]; then + command -v croc &>/dev/null || pacman -Sy --noconfirm croc 2>/dev/null || true + croc send "$LOGFILE" || true else - echo "" - echo "Installation failed at line $line_num (exit code $exit_code)." - read -rp "Send log via croc? [y/N]: " _croc_ans - if [[ "${_croc_ans,,}" == "y" ]]; then - command -v croc &>/dev/null || pacman -Sy --noconfirm croc 2>/dev/null || true - croc send "$LOGFILE" || true - else - echo "Log saved to: $LOGFILE" - fi + echo "Log saved to: $LOGFILE" fi exit "$exit_code" } @@ -434,9 +413,9 @@ if [[ "${_DO_TUI^^}" == "YES" ]]; then echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \ | arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null - echo "Running tui-install.sh as ${USERNAME} inside chroot..." + echo "Running simple-install.sh as ${USERNAME} inside chroot..." arch-chroot /mnt runuser -u "${USERNAME}" -- \ - bash "/home/${USERNAME}/Dotfiles/setup/tui-install.sh" \ + bash "/home/${USERNAME}/Dotfiles/setup/simple-install.sh" \ || echo "Warning: tui-install exited with errors — check ~/dotfiles-install.log in the new system." arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd @@ -454,7 +433,7 @@ echo " umount -R /mnt && reboot" if [[ "${_DO_TUI^^}" != "YES" ]]; then echo echo "After first boot, login as ${USERNAME} and run:" - echo " ~/Dotfiles/setup/tui-install.sh" + echo " ~/Dotfiles/setup/simple-install.sh" fi if [[ "$ENCRYPT_DISK" == "YES" ]]; then echo diff --git a/setup/archiso/overlay/packages.extra b/setup/archiso/overlay/packages.extra index 38885e7..c5351b6 100644 --- a/setup/archiso/overlay/packages.extra +++ b/setup/archiso/overlay/packages.extra @@ -6,5 +6,4 @@ libfido2 btop fastfetch openssh -dialog croc diff --git a/setup/simple-install.sh b/setup/simple-install.sh new file mode 100755 index 0000000..3c7fc0b --- /dev/null +++ b/setup/simple-install.sh @@ -0,0 +1,633 @@ +#!/bin/bash +# simple-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"; tput reset 2>/dev/null || stty sane 2>/dev/null || true' EXIT INT TERM HUP + +ANSWERFILE="${ANSWERFILE:-/answerfile.json}" +ANSWERFILE_MODE=false +[[ -f "$ANSWERFILE" ]] && ANSWERFILE_MODE=true + +TITLE="the_miro's Arch Dotfiles" + +# ── Terminal dimensions ─────────────────────────────────────────────────────── +TERM_H=$(tput lines 2>/dev/null || echo 24) +TERM_W=$(tput cols 2>/dev/null || echo 80) +TUI_W=$(( TERM_W < 74 ? TERM_W - 2 : 72 )) +printf -v TUI_BAR '%.0s─' $(seq 1 "$TUI_W") + +# ── ANSI color codes ────────────────────────────────────────────────────────── +R=$'\e[0m'; B=$'\e[1m'; D=$'\e[2m'; M=$'\e[1;35m'; CY=$'\e[1;36m' + +# ── TUI primitives ──────────────────────────────────────────────────────────── +# All display goes to /dev/tty so functions work correctly inside $() captures. + +_hdr() { + printf '\e[2J\e[H' >/dev/tty + printf "${M} ┌%s┐${R}\n ${M}│ ${B}%-*s${R} ${M}│${R}\n ${M}└%s┘${R}\n\n" \ + "$TUI_BAR" $(( TUI_W - 4 )) "$TITLE" "$TUI_BAR" >/dev/tty +} + +_sep() { printf " ${D}─────────────────────────────────────────────${R}\n\n" >/dev/tty; } + +# tui_msg TITLE MSG +tui_msg() { + _hdr + printf " ${M}${B}%s${R}\n" "$1" >/dev/tty; _sep + printf "%b\n\n" "$2" >/dev/tty + printf " ${D}Press Enter to continue...${R}" >/dev/tty; read -r /dev/tty; _sep + printf "%b\n\n" "$2" >/dev/tty + local a + while true; do + printf " ${CY}[Y/n]${R} > " >/dev/tty; read -r a /dev/tty ;; + esac + done +} + +# tui_input TITLE PROMPT [DEFAULT] → prints value to stdout +tui_input() { + _hdr + printf " ${M}${B}%s${R}\n" "$1" >/dev/tty; _sep + printf " %b\n" "$2" >/dev/tty + [[ -n "${3:-}" ]] && printf " ${D}(default: %s)${R}\n" "$3" >/dev/tty + printf "\n > " >/dev/tty; local v; read -r v /dev/tty; _sep + printf " ${D}%s${R}\n\n" "$prompt" >/dev/tty + + local -a sel=() + local i num=0 + for (( i=0; i/dev/tty + continue + fi + sel+=("$i"); (( num++ )) + if [[ "${_S[$i]}" == "on" ]]; then + printf " ${CY}%3d) [*] %-22s${R} %s\n" "$num" "${_T[$i]}" "${_D[$i]}" >/dev/tty + else + printf " %3d) [ ] %-22s %s\n" "$num" "${_T[$i]}" "${_D[$i]}" >/dev/tty + fi + done + local total_sel=${#sel[@]} + + printf "\n ${D}Number(s) to toggle · 'a' all · 'n' none · Enter confirm:${R}\n > " >/dev/tty + local inp; read -r inp = 0 && k < total_sel )) || continue + local idx="${sel[$k]}" + [[ "${_S[$idx]}" == "on" ]] && _S[$idx]="off" || _S[$idx]="on" + fi + done + done + + local res="" + for (( i=0; i/dev/tty; _sep + printf " ${D}%s${R}\n\n" "$prompt" >/dev/tty + local i + for (( i=0; i/dev/tty + done + printf "\n ${D}Enter number:${R}\n > " >/dev/tty + local inp; read -r inp = 1 && inp <= n )); then + printf '%s' "${_T[$(( inp-1 ))]}"; return 0 + fi + printf " Invalid selection.\n" >/dev/tty; sleep 0.4 + done +} + +# ── State ───────────────────────────────────────────────────────────────────── +STEP=0 +TOTAL=0 + +# ── Helpers ─────────────────────────────────────────────────────────────────── +require_jq() { + command -v jq &>/dev/null && return + echo "jq not found — installing..." + sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; 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${M} [$STEP/$TOTAL] %s${R}\n" "$label" + printf "${D} ─────────────────────────────────────────────${R}\n\n" + + local rc=0 + bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]} + + if [[ $rc -ne 0 ]]; then + if [[ $ANSWERFILE_MODE == true ]]; then + printf "\n Warning: %s exited with code %d — continuing.\n" "$label" "$rc" | tee -a "$LOG" + else + tui_yesno " Module Failed " \ + " $label exited with code $rc.\n\n Continue anyway?" \ + || { clear; exit 1; } + fi + 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 )) + [[ "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"ollama"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"llama-cpp"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"open-webui"* ]] && 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" == *"freeipa-client"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"freeipa-server"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"freeipa-image"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"python"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"zfs"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"wprs"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"chromium"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"firefox-browser"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"zen-browser"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"nyxt"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"librewolf"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"min-browser"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"vscodium"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"zed-ide"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"geany"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"codeblocks"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"rdp-client"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"lamco-rdp-server"* ]] && TOTAL=$(( TOTAL + 1 )) + [[ "$a" == *"qemu"* ]] && TOTAL=$(( TOTAL + 1 )) +} + +# ── Answerfile ──────────────────────────────────────────────────────────────── +AF_HOSTNAME="" +AF_COMPONENTS="" +AF_DE="none" +AF_APPS="" +AF_COLOR_TEXT="" +AF_COLOR_BG="" +AF_COLOR_HIGHLIGHT="" +AF_COLOR_DARK="" +AF_COLOR_RED="" + +load_answerfile() { + require_jq + AF_HOSTNAME=$(jq -r '.hostname // ""' "$ANSWERFILE") + AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE") + AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE") + AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$ANSWERFILE") + AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE") + AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$ANSWERFILE") + AF_COLOR_HIGHLIGHT=$(jq -r '.colors.COLOR_HIGHLIGHT // ""' "$ANSWERFILE") + AF_COLOR_DARK=$(jq -r '.colors.COLOR_DARK // ""' "$ANSWERFILE") + AF_COLOR_RED=$(jq -r '.colors.COLOR_RED // ""' "$ANSWERFILE") +} + +# ── MAC address helper ──────────────────────────────────────────────────────── +get_mac_suffix() { + local mac + mac=$(ip link show 2>/dev/null \ + | awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}') + printf '%s' "${mac//:/}" +} + +# ── Preflight ───────────────────────────────────────────────────────────────── +[[ $EUID -eq 0 ]] && die "Run as your normal user (not root)." +command -v pacman &>/dev/null || die "pacman not found — Arch Linux required." + +if $ANSWERFILE_MODE; then + load_answerfile + printf "Answerfile mode: %s\n" "$ANSWERFILE" | tee -a "$LOG" +fi + +# ── Network check ───────────────────────────────────────────────────────────── +if ! ping -c1 -W3 archlinux.org &>/dev/null; then + if $ANSWERFILE_MODE; then + printf "Warning: no internet connection detected.\n" | tee -a "$LOG" + else + tui_msg " No Network Detected " \ + " No internet connection found.\n\n nmtui will open so you can configure networking.\n Close nmtui when done to continue the installer." + nmtui + if ! ping -c1 -W3 archlinux.org &>/dev/null; then + tui_yesno " Still Offline " \ + " Still no internet connection.\n\n Packages cannot be downloaded without network access.\n\n Continue anyway?" \ + || { clear; echo "Aborted — no network."; exit 1; } + fi + fi +fi + +> "$LOG" +printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG" + +# ── Welcome ─────────────────────────────────────────────────────────────────── +if ! $ANSWERFILE_MODE; then + tui_msg " Welcome " \ + " 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" +fi + +# ── Hostname ────────────────────────────────────────────────────────────────── +HOSTNAME_SET="" +if $ANSWERFILE_MODE; then + if [[ -n "$AF_HOSTNAME" ]]; then + MAC=$(get_mac_suffix) + HOSTNAME_SET="${AF_HOSTNAME}-${MAC}" + printf "Hostname (from answerfile + MAC): %s\n" "$HOSTNAME_SET" | tee -a "$LOG" + fi +else + HOSTNAME_SET=$(tui_input " Hostname " \ + "Hostname for this machine (leave blank to keep default)." "") +fi + +if [[ -n "$HOSTNAME_SET" ]]; then + sudo hostnamectl set-hostname "$HOSTNAME_SET" 2>/dev/null \ + || echo "$HOSTNAME_SET" | sudo tee /etc/hostname > /dev/null + printf "Hostname set: %s\n" "$HOSTNAME_SET" >> "$LOG" +fi + +# ── Component selection ─────────────────────────────────────────────────────── +if $ANSWERFILE_MODE; then + COMPONENTS="$AF_COMPONENTS" +else + COMPONENTS=$(tui_checklist " Select Components " \ + "Number to toggle · Enter to confirm" \ + "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) +fi + +# ── DE selection ────────────────────────────────────────────────────────────── +if $ANSWERFILE_MODE; then + DE="$AF_DE" +else + DE=$(tui_menu " Desktop Environment " \ + "Select a desktop environment:" \ + "hyprlua" "HyprLua — Hyprland with Lua config (recommended)" \ + "hyprland" "Hyprland — Wayland WM, hyprlang config (legacy)" \ + "sway" "Sway — Wayland tiling WM" \ + "kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \ + "gnome" "GNOME — modern Wayland DE" \ + "cosmic" "COSMIC — Rust-built Wayland DE (System76)" \ + "xfce" "XFCE — lightweight X11 DE" \ + "lxqt" "LXQt — lightweight Qt X11 DE" \ + "none" "Skip DE installation") +fi + +# ── Apps selection ──────────────────────────────────────────────────────────── +if $ANSWERFILE_MODE; then + SELECTED_APPS="$AF_APPS" +else + SELECTED_APPS=$(tui_checklist " Applications " \ + "Optional applications — number to toggle, Enter to confirm:" \ + \ + "" "AI / LLM" header \ + "ollama" "Ollama local LLM runner + API server" off \ + "llama-cpp" "llama.cpp standalone inference CLI + server" off \ + "open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \ + "claude" "Claude Code Anthropic CLI via npm" off \ + \ + "" "Networking & Disk" header \ + "networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \ + "disk-recovery" "Disk Recovery ddrescue · f3" off \ + \ + "" "CLI Tools" header \ + "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 \ + \ + "" "Gaming" header \ + "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 \ + \ + "" "File Transfer & Office" header \ + "localsend" "LocalSend LAN file transfer (AUR)" off \ + "croc" "croc cross-platform file transfer" off \ + "onlyoffice" "OnlyOffice office suite (AUR)" off \ + \ + "" "Security" header \ + "wireshark" "Wireshark network packet analyser (GUI)" off \ + \ + "" "Containers & Server" header \ + "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 \ + "freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \ + "freeipa-server" "FreeIPA Server interactive server setup + client gen" off \ + "freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \ + \ + "" "Dev & System" header \ + "python" "Python tools pyright · pipx · pynvim" off \ + "zfs" "ZFS zfs-dkms kernel module" off \ + "wprs" "WPRS wprs-git (AUR)" off \ + \ + "" "Browsers" header \ + "chromium" "Chromium open-source browser (official)" off \ + "firefox-browser" "Firefox Mozilla browser (official)" off \ + "zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \ + "nyxt" "Nyxt keyboard-driven browser (AUR)" off \ + "librewolf" "LibreWolf hardened Firefox fork (AUR)" off \ + "min-browser" "Min minimal Electron browser (AUR)" off \ + \ + "" "IDEs & Editors" header \ + "vscodium" "VSCodium telemetry-free VS Code (AUR)" off \ + "zed-ide" "Zed high-performance Rust IDE (official)" off \ + "geany" "Geany lightweight IDE + plugins (official)" off \ + "codeblocks" "Code::Blocks C/C++ IDE (official)" off \ + "kate" "Kate KDE advanced text editor (official)" off \ + \ + "" "Remote & Virtualization" header \ + "rdp-client" "RDP Client Remmina + FreeRDP + VNC plugins" off \ + "lamco-rdp-server" "Lamco RDP Srv native Wayland RDP server (AUR, Rust)" off \ + "qemu" "QEMU/KVM full virt stack + virt-manager GUI" off) +fi + +# ── Confirmation (interactive mode only) ────────────────────────────────────── +if ! $ANSWERFILE_MODE; then + SUMMARY="" + [[ -n "$HOSTNAME_SET" ]] && SUMMARY+=" ✦ Hostname: $HOSTNAME_SET\n" + [[ "$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" + [[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n" + + if [[ -n "$SELECTED_APPS" ]]; then + SUMMARY+="\n Applications:\n" + for _app in ollama llama-cpp open-webui claude networking-cli disk-recovery \ + himalaya gnuplot povray blender toot db-clients mysql productivity \ + yt-dlp sox imagemagick ffmpeg localtunnel butter tlp steam vesktop \ + spotify prism vintagestory localsend croc onlyoffice wireshark k8s \ + docker podman cockpit ssh-server freeipa-client freeipa-server \ + freeipa-image python zfs wprs chromium firefox-browser zen-browser \ + nyxt librewolf min-browser vscodium zed-ide geany codeblocks kate \ + rdp-client lamco-rdp-server qemu; do + [[ "$SELECTED_APPS" == *"$_app"* ]] && SUMMARY+=" ✦ $_app\n" + done + fi + + tui_yesno " Confirm Installation " \ + " Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \ + || { clear; echo "Aborted."; exit 0; } +fi + +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 [[ "$DE" != "none" ]]; then + case "$DE" in + hyprlua) run_module "HyprLua" "$MODULES/Desktop-Environments/hyprlua.sh" ;; + hyprland) run_module "Hyprland" "$MODULES/Desktop-Environments/hyprland.sh" ;; + sway) run_module "Sway" "$MODULES/Desktop-Environments/sway.sh" ;; + kde-plasma) run_module "KDE Plasma" "$MODULES/Desktop-Environments/kde-plasma.sh" ;; + gnome) run_module "GNOME" "$MODULES/Desktop-Environments/gnome.sh" ;; + cosmic) run_module "COSMIC" "$MODULES/Desktop-Environments/cosmic.sh" ;; + xfce) run_module "XFCE" "$MODULES/Desktop-Environments/xfce.sh" ;; + lxqt) run_module "LXQt" "$MODULES/Desktop-Environments/lxqt.sh" ;; + esac +fi + +# ── Installation: applications ──────────────────────────────────────────────── +[[ "$SELECTED_APPS" == *"ollama"* ]] && run_module "Ollama" "$APPS/ollama.sh" +[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && run_module "llama.cpp" "$APPS/llama-cpp.sh" +[[ "$SELECTED_APPS" == *"open-webui"* ]] && run_module "Open WebUI" "$APPS/open-webui.sh" +[[ "$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" == *"freeipa-client"* ]] && run_module "FreeIPA Client" "$APPS/freeipa-client.sh" +[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && run_module "FreeIPA Server" "$APPS/freeipa-server.sh" +[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && run_module "FreeIPA Image" "$APPS/freeipa-image-builder.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" +[[ "$SELECTED_APPS" == *"chromium"* ]] && run_module "Chromium" "$APPS/chromium.sh" +[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && run_module "Firefox" "$APPS/firefox.sh" +[[ "$SELECTED_APPS" == *"zen-browser"* ]] && run_module "Zen Browser" "$APPS/zen-browser.sh" +[[ "$SELECTED_APPS" == *"nyxt"* ]] && run_module "Nyxt" "$APPS/nyxt.sh" +[[ "$SELECTED_APPS" == *"librewolf"* ]] && run_module "LibreWolf" "$APPS/librewolf.sh" +[[ "$SELECTED_APPS" == *"min-browser"* ]] && run_module "Min Browser" "$APPS/min-browser.sh" +[[ "$SELECTED_APPS" == *"vscodium"* ]] && run_module "VSCodium" "$APPS/vscodium.sh" +[[ "$SELECTED_APPS" == *"zed-ide"* ]] && run_module "Zed IDE" "$APPS/zed.sh" +[[ "$SELECTED_APPS" == *"geany"* ]] && run_module "Geany" "$APPS/geany.sh" +[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "Code::Blocks" "$APPS/codeblocks.sh" +[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "Kate" "$APPS/kate.sh" +[[ "$SELECTED_APPS" == *"rdp-client"* ]] && run_module "RDP Client" "$APPS/rdp-client.sh" +[[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && run_module "Lamco RDP Server" "$APPS/lamco-rdp-server.sh" +[[ "$SELECTED_APPS" == *"qemu"* ]] && run_module "QEMU/KVM" "$APPS/qemu.sh" + +# ── Colorway (final step) ───────────────────────────────────────────────────── +declare -A _cdef +if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then + while IFS='=' read -r k v; do + k="${k%%[[:space:]]*}" + [[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue + v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}" + _cdef[$k]="$v" + done < "$DOTFILES_DIR/colors.conf" +fi +DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}" +DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}" +DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}" +DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}" +DEF_RED="${_cdef[COLOR_RED]:-F50505}" + +_write_colors_conf() { + local out="$1" t="$2" b="$3" h="$4" d="$5" r="$6" + printf 'COLOR_TEXT=%s\nCOLOR_BG=%s\nCOLOR_HIGHLIGHT=%s\nCOLOR_DARK=%s\nCOLOR_RED=%s\n' \ + "${t^^}" "${b^^}" "${h^^}" "${d^^}" "${r^^}" > "$out" +} + +if $ANSWERFILE_MODE; then + if [[ -n "$AF_COLOR_TEXT$AF_COLOR_BG$AF_COLOR_HIGHLIGHT$AF_COLOR_DARK$AF_COLOR_RED" ]]; then + TMP_COLORS="$TMP_D/colors.conf" + _write_colors_conf "$TMP_COLORS" \ + "${AF_COLOR_TEXT:-$DEF_TEXT}" \ + "${AF_COLOR_BG:-$DEF_BG}" \ + "${AF_COLOR_HIGHLIGHT:-$DEF_HIGHLIGHT}" \ + "${AF_COLOR_DARK:-$DEF_DARK}" \ + "${AF_COLOR_RED:-$DEF_RED}" + printf "Applying colorway from answerfile...\n" | tee -a "$LOG" + bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true + fi +else + _hdr + printf " ${M}${B} Colorway (optional) ${R}\n" >/dev/tty; _sep + printf " Customize theme colors — bare 6-digit hex, no #.\n Press Enter to keep each default.\n\n" >/dev/tty + + printf " COLOR_TEXT ${D}[%s]${R} > " "$DEF_TEXT" >/dev/tty; read -r N_TEXT " "$DEF_BG" >/dev/tty; read -r N_BG " "$DEF_HIGHLIGHT" >/dev/tty; read -r N_HIGHLIGHT " "$DEF_DARK" >/dev/tty; read -r N_DARK " "$DEF_RED" >/dev/tty; read -r N_RED &1 | tee -a "$LOG" || true + fi +fi + +# ── Done ────────────────────────────────────────────────────────────────────── +if $ANSWERFILE_MODE; then + printf "\nDone. Log: %s\n" "$LOG" +else + tui_msg " Done " \ + " All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect." + clear + printf "\n Done. Log: %s\n\n" "$LOG" +fi