#!/bin/bash # ███╗ ███╗ ██████╗ ██████╗ ██╗ ██╗███████╗ # ████╗ ████║██╔═══██╗██╔══██╗██║ ██║██╔════╝ # ██╔████╔██║██║ ██║██║ ██║██║ ██║███████╗ # ██║╚██╔╝██║██║ ██║██║ ██║██║ ██║╚════██║ # ██║ ╚═╝ ██║╚██████╔╝██████╔╝╚██████╔╝███████║ # ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ # # A hackable shell for Hyprland # Installation Script for Arch Linux # # Repository: https://github.com/S4NKALP/Modus --branch macos # License: GPLv3 set -e set -u set -o pipefail REPO_URL="https://github.com/S4NKALP/Modus.git" INSTALL_DIR="$HOME/.config/Modus" PACKAGES=( python-fabric-git fabric-cli-git glace-git cliphist gnome-bluetooth-3.0 gobject-introspection slurp ffmpeg hypridle hyprsunset hyprpicker imagemagick libnotify matugen-bin playerctl python-gobject python-pillow python-setproctitle python-toml python-requests python-numpy python-pywayland python-pyxdg python-ijson python-watchdog python-pyotp pyzbar python-psutil python-pydbus python-thefuzz python-pam gtk-session-lock swww apple-fonts swappy wl-clipboard webp-pixbuf-loader wf-recorder acpi brightnessctl power-profiles-daemon uwsm cinnamon-desktop ) # Colors and formatting if [ -t 1 ]; then GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) RED=$(tput setaf 1) CYAN=$(tput setaf 6) BLUE=$(tput setaf 4) BOLD=$(tput bold) DIM=$(tput dim) RESET=$(tput sgr0) else GREEN="" YELLOW="" RED="" CYAN="" BLUE="" BOLD="" DIM="" RESET="" fi # Status symbols ARROW="→" CHECK="✔" CROSS="✖" INFO="ℹ" WARN="⚠" # Progress tracking TOTAL_STEPS=7 CURRENT_STEP=0 # Function for progress indicator progress() { CURRENT_STEP=$((CURRENT_STEP + 1)) echo -e "\n${BOLD}${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${RESET} ${BOLD}$1${RESET}" } step() { echo -e " ${CYAN}${ARROW}${RESET} $1" } success() { echo -e " ${GREEN}${CHECK}${RESET} ${GREEN}$1${RESET}" } warn() { echo -e " ${YELLOW}${WARN}${RESET} ${YELLOW}$1${RESET}" } error() { echo -e "\n${RED}${CROSS}${RESET} ${RED}${BOLD}ERROR:${RESET} ${RED}$1${RESET}\n" >&2 } info() { echo -e " ${BLUE}${INFO}${RESET} ${DIM}$1${RESET}" } spinner() { local pid=$1 local message=$2 local spin=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') local i=0 while kill -0 $pid 2>/dev/null; do printf "\r ${CYAN}${spin[i]}${RESET} $message" i=$(((i + 1) % 10)) sleep 0.1 done printf "\r" } # Cleanup handler cleanup() { if [ -n "${SUDO_KEEPER_PID:-}" ]; then kill $SUDO_KEEPER_PID 2>/dev/null || true fi } trap cleanup EXIT INT TERM # Header clear echo -e "${BOLD}${CYAN}" cat << "EOF" ███╗ ███╗ ██████╗ ██████╗ ██╗ ██╗███████╗ ████╗ ████║██╔═══██╗██╔══██╗██║ ██║██╔════╝ ██╔████╔██║██║ ██║██║ ██║██║ ██║███████╗ ██║╚██╔╝██║██║ ██║██║ ██║██║ ██║╚════██║ ██║ ╚═╝ ██║╚██████╔╝██████╔╝╚██████╔╝███████║ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ EOF echo -e "${RESET}" echo -e "${BOLD} A hackable shell for Hyprland${RESET}" echo -e "${DIM} Installation Script v1.0${RESET}\n" # Pre-flight checks progress "Pre-flight checks" step "Checking operating system..." if ! grep -qi "arch" /etc/os-release; then error "This script requires Arch Linux or an Arch-based distribution" exit 1 fi success "Arch Linux detected" step "Checking user permissions..." if [ "$(id -u)" -eq 0 ]; then error "Please run this script as a regular user, not as root" exit 1 fi success "Running as regular user" step "Checking system requirements..." if ! command -v git &>/dev/null; then error "git is not installed. Please install it first: sudo pacman -S git" exit 1 fi success "All requirements met" # Sudo authentication progress "Requesting permissions" info "Some packages require root privileges for installation" echo "" if ! sudo -v; then error "Sudo authentication failed" exit 1 fi success "Permissions granted" # Keep sudo alive while true; do sudo -n true sleep 60 kill -0 "$$" || exit done 2>/dev/null & SUDO_KEEPER_PID=$! # Show package list progress "Package information" info "Total packages to install: ${BOLD}${#PACKAGES[@]}${RESET}" echo "" read -rp " ${YELLOW}${INFO}${RESET} View full package list? (y/N): " view_packages if [[ "$view_packages" =~ ^[Yy]$ ]]; then echo "" printf " ${DIM}• %s${RESET}\n" "${PACKAGES[@]}" echo "" fi # Confirmation read -rp " ${BOLD}Proceed with installation? (y/N):${RESET} " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then warn "Installation cancelled by user" exit 0 fi # AUR helper setup progress "Setting up AUR helper" aur_helper="yay" if command -v paru &>/dev/null; then aur_helper="paru" success "Found paru" elif command -v yay &>/dev/null; then success "Found yay" else step "Installing yay-bin..." tmpdir=$(mktemp -d) ( git clone --quiet --depth=1 https://aur.archlinux.org/yay-bin.git "$tmpdir/yay-bin" 2>&1 | grep -v "Cloning into" || true cd "$tmpdir/yay-bin" makepkg -si --noconfirm >/dev/null 2>&1 ) & spinner $! "Building yay-bin..." wait $! || { error "Failed to install yay-bin" rm -rf "$tmpdir" exit 1 } rm -rf "$tmpdir" success "yay-bin installed successfully" fi # Repository setup progress "Setting up Modus repository" if [ -d "$INSTALL_DIR" ]; then step "Updating existing repository..." git -C "$INSTALL_DIR" pull --quiet 2>&1 | grep -v "Already up to date" || true success "Repository updated" else step "Cloning repository..." git clone --quiet "$REPO_URL" "$INSTALL_DIR" 2>&1 | grep -v "Cloning into" || true success "Repository cloned" fi info "Location: ${INSTALL_DIR}" # Package installation progress "Installing packages" step "Syncing package databases..." $aur_helper -Syy --noconfirm >/dev/null 2>&1 || true success "Database synced" step "Installing required packages (this may take a while)..." installed=0 failed=0 for pkg in "${PACKAGES[@]}"; do if $aur_helper -S --needed --noconfirm "$pkg" >/dev/null 2>&1; then installed=$((installed + 1)) else failed=$((failed + 1)) warn "Failed to install: $pkg" fi printf "\r ${CYAN}${ARROW}${RESET} Progress: ${installed}/${#PACKAGES[@]} packages" done echo "" if [ $failed -eq 0 ]; then success "All packages installed successfully" else warn "$failed package(s) failed to install" fi # Update check step "Checking for package updates..." outdated=$($aur_helper -Qu 2>/dev/null | awk '{print $1}' || true) to_update=() for pkg in "${PACKAGES[@]}"; do if echo "$outdated" | grep -q "^$pkg\$"; then to_update+=("$pkg") fi done if [ ${#to_update[@]} -gt 0 ]; then step "Updating ${#to_update[@]} outdated package(s)..." $aur_helper -S --noconfirm "${to_update[@]}" >/dev/null 2>&1 || true success "Packages updated" else success "All packages are up-to-date" fi # Configuration # progress "Running configuration" # if [ -f "$INSTALL_DIR/config/config.py" ]; then # step "Initializing Modus configuration..." # if python "$INSTALL_DIR/config/config.py" 2>/dev/null; then # success "Configuration completed" # else # warn "Configuration step failed or was skipped" # fi # else # info "No configuration file found, skipping" # fi # Hyprland configuration progress "Configuring Hyprland" HYPR_CONFIG="$HOME/.config/hypr/hyprland.conf" MODUS_CONF_LINE="source= ~/.config/Modus/config/hypr/modus.conf" if [ -f "$HYPR_CONFIG" ]; then step "Checking Hyprland configuration..." if grep -qF "$MODUS_CONF_LINE" "$HYPR_CONFIG"; then success "Modus configuration already sourced" else step "Adding Modus configuration to Hyprland..." echo "" >> "$HYPR_CONFIG" echo "# Modus configuration" >> "$HYPR_CONFIG" echo "$MODUS_CONF_LINE" >> "$HYPR_CONFIG" success "Modus configuration added to Hyprland" fi else warn "Hyprland config not found at $HYPR_CONFIG" info "You may need to manually add: $MODUS_CONF_LINE" fi # Launch Modus progress "Launching Modus" step "Stopping existing instances..." if killall modus 2>/dev/null; then success "Stopped running instance" sleep 1 else info "No existing instance found" fi step "Starting Modus..." if uwsm app -- python "$INSTALL_DIR/main.py" >/dev/null 2>&1 & then disown sleep 1 if pgrep -f "python.*main.py" >/dev/null; then success "Modus is now running" else warn "Modus may not have started correctly" fi else error "Failed to start Modus" exit 1 fi # Completion echo "" echo -e "${GREEN}${BOLD}╔════════════════════════════════════════╗${RESET}" echo -e "${GREEN}${BOLD}║ ║${RESET}" echo -e "${GREEN}${BOLD}║ Installation completed! 🎉 ║${RESET}" echo -e "${GREEN}${BOLD}║ ║${RESET}" echo -e "${GREEN}${BOLD}╚════════════════════════════════════════╝${RESET}" echo "" info "Modus is running in the background" info "Config location: ${INSTALL_DIR}" info "Repository: ${REPO_URL}" echo ""