Files
Modus/install.sh
T
sankalp 1fc7edd483 feat(install): symlink hypridle.conf to ~/.config/hypr
Create symlink during installation so hypridle uses Modus config
2026-07-12 20:27:15 +05:45

486 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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
AUTO_YES=false
for arg in "$@"; do
case "$arg" in
-y|--yes) AUTO_YES=true ;;
esac
done
REPO_URL="https://github.com/S4NKALP/Modus.git"
INSTALL_DIR="$HOME/.config/Modus"
PACKAGES=(
uv
fabric-cli-git
cliphist
gnome-bluetooth-3.0
slurp
ffmpeg
hypridle
hyprsunset
hyprpicker
hyprshot
grim
libnotify
matugen-bin
playerctl
gtk-session-lock
awww
apple-fonts
swappy
wl-clipboard
webp-pixbuf-loader
wf-recorder
acpi
brightnessctl
power-profiles-daemon
uwsm
cinnamon-desktop
ddcutil
at-spi2-core
gcc
make
pkgconf
appmenu-gtk-module
libdbusmenu-gtk3
libdbusmenu-qt5
meson
ninja
wayland-protocols
)
# 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=12
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 ""
if [ "$AUTO_YES" = false ]; then
read -rp " ${YELLOW}${INFO}${RESET} View full package list? (y/N): " view_packages
else
view_packages="n"
fi
if [[ "$view_packages" =~ ^[Yy]$ ]]; then
echo ""
printf " ${DIM}• %s${RESET}\n" "${PACKAGES[@]}"
echo ""
fi
# Confirmation
if [ "$AUTO_YES" = false ]; then
read -rp " ${BOLD}Proceed with installation? (y/N):${RESET} " confirm
else
confirm="y"
fi
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>/dev/null || 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>/dev/null || true
success "Repository updated"
else
step "Cloning repository..."
git clone --quiet "$REPO_URL" "$INSTALL_DIR" 2>/dev/null || 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
progress "Building Global Menu shim"
step "Compiling libmenu_button_shim.so..."
SHIM_SRC="$INSTALL_DIR/src/globalmenu/libmenu_button_shim.c"
SHIM_OUT="$INSTALL_DIR/src/globalmenu/libmenu_button_shim.so"
if [ -f "$SHIM_SRC" ]; then
if gcc -shared -fPIC -O2 -o "$SHIM_OUT" "$SHIM_SRC" $(pkg-config --cflags --libs gtk+-3.0) -ldl 2>/dev/null; then
success "libmenu_button_shim.so built successfully"
else
warn "Failed to compile libmenu_button_shim.so - global menu may not work"
fi
else
warn "libmenu_button_shim.c not found - skipping shim build"
fi
progress "Building App Capture module"
step "Compiling libappcapture.so..."
if [ -d "$INSTALL_DIR/src/window/switcher/app-capture" ]; then
if (
cd "$INSTALL_DIR/src/window/switcher/app-capture"
meson setup builddir --wipe >/dev/null 2>&1 || meson setup builddir >/dev/null 2>&1
meson compile -C builddir >/dev/null 2>&1
); then
success "libappcapture.so built successfully"
else
warn "Failed to compile App Capture module"
fi
else
warn "App Capture module directory not found"
fi
progress "Configuring Hyprland"
HYPR_CONFIG="$HOME/.config/hypr/hyprland.lua"
MODUS_MODULE_LINE="dofile(\"$INSTALL_DIR/config/hypr/modus.lua\")"
if [ -f "$HYPR_CONFIG" ]; then
step "Checking Hyprland Lua configuration..."
if grep -qF "$MODUS_MODULE_LINE" "$HYPR_CONFIG"; then
success "Modus module already loaded"
else
step "Adding Modus module to Hyprland Lua config..."
{
echo ""
echo "-- Modus configuration"
echo "$MODUS_MODULE_LINE"
} >> "$HYPR_CONFIG"
success "Modus configuration added"
fi
else
warn "Hyprland Lua config not found at $HYPR_CONFIG"
info "You may need to manually add: $MODUS_MODULE_LINE"
fi
step "Symlinking hypridle.conf..."
HYPR_DIR="$HOME/.config/hypr"
HYPRIDLE_CONF="$INSTALL_DIR/config/hypr/hypridle.conf"
HYPRIDLE_TARGET="$HYPR_DIR/hypridle.conf"
mkdir -p "$HYPR_DIR"
if [ -f "$HYPRIDLE_CONF" ]; then
if [ -L "$HYPRIDLE_TARGET" ]; then
rm "$HYPRIDLE_TARGET"
fi
ln -sf "$HYPRIDLE_CONF" "$HYPRIDLE_TARGET"
success "hypridle.conf symlinked"
else
warn "hypridle.conf not found at $HYPRIDLE_CONF"
fi
progress "Configuring global environment"
APP_CONF_DIR="$HOME/.config/environment.d"
APP_CONF_FILE="$APP_CONF_DIR/appmenu.conf"
step "Writing environment.d config..."
mkdir -p "$APP_CONF_DIR"
cat > "$APP_CONF_FILE" << 'EOF'
GTK_MODULES=appmenu-gtk-module
UBUNTU_MENUPROXY=1
EOF
success "Environment config written to $APP_CONF_FILE"
PAM_FILE="$HOME/.pam_environment"
step "Updating pam_environment..."
for entry in "GTK_MODULES DEFAULT=appmenu-gtk-module" "UBUNTU_MENUPROXY DEFAULT=1"; do
var_name="${entry%% *}"
if ! grep -q "^${var_name} " "$PAM_FILE" 2>/dev/null; then
echo "$entry" >> "$PAM_FILE"
fi
done
success ".pam_environment updated"
progress "Configuring Matugen"
MATUGEN_CONFIG="$HOME/.config/matugen/config.toml"
MODUS_BLOCK='[templates.modus]
input_path = "~/.config/Modus/config/matugen/templates/modus.css"
output_path = "~/.config/Modus/src/shared/styles/colors.css"
post_hook = "fabric-cli exec modus '\''app.set_css()'\'' &"'
HYPR_BLOCK='[templates.hyprland]
input_path = "~/.config/Modus/config/matugen/templates/hyprland-colors.lua"
output_path = "~/.config/Modus/config/matugen/colors.lua"'
if [ -f "$MATUGEN_CONFIG" ]; then
step "Checking matugen config..."
# ---- Modus1 ----
if grep -qF "[templates.modus]" "$MATUGEN_CONFIG"; then
success "modus1 template already exists"
else
step "Adding modus1 template..."
echo "" >> "$MATUGEN_CONFIG"
echo "$MODUS_BLOCK" >> "$MATUGEN_CONFIG"
success "modus template added"
fi
# ---- Hyprland ----
if grep -qF "[templates.hyprland]" "$MATUGEN_CONFIG"; then
success "hyprland template already exists"
else
step "Adding hyprland template..."
echo "" >> "$MATUGEN_CONFIG"
echo "$HYPR_BLOCK" >> "$MATUGEN_CONFIG"
success "hyprland template added"
fi
else
warn "matugen config not found at $MATUGEN_CONFIG"
info "Create it first or install matugen"
fi
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 -- uv run --project "$INSTALL_DIR" start >/dev/null 2>&1 & then
disown
sleep 2
if pgrep -x "modus" >/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 ""