#!/bin/bash # Omarchy Cleaner - Remove unwanted default applications from Omarchy # Enhanced with gum for a better TUI experience # Version VERSION="2.0" # Configuration BINDINGS_FILE="$HOME/.config/hypr/bindings.conf" REMOVE_BINDINGS=false # App # List from: https://github.com/basecamp/omarchy/blob/master/install/packages.sh DEFAULT_APPS=( # Packages offered for removal "1password-beta" "1password-cli" "kdenlive" "libreoffice" "localsend" "obs-studio" "obsidian" "omarchy-chromium" "signal-desktop" "spotify" "xournalpp" "docker" "docker-buildx" "docker-compose" # Uncomment to include in removal list # "asdcontrol-git" # "alacritty" # "avahi" # "bash-completion" # "bat" # "blueberry" # "brightnessctl" # "btop" # "cargo" # "clang" # "cups" # "cups-browsed" # "cups-filters" # "cups-pdf" # "dust" # "evince" # "eza" # "fastfetch" # "fcitx5" # "fcitx5-gtk" # "fcitx5-qt" # "fd" # "ffmpegthumbnailer" # "fontconfig" # "fzf" # "gcc14" # "github-cli" # "gnome-calculator" # "gnome-keyring" # "gnome-themes-extra" # "gum" # "gvfs-mtp" # "gvfs-smb" # "hypridle" # "hyprland" # "hyprland-qtutils" # "hyprlock" # "hyprpicker" # "hyprshot" # "hyprsunset" # "imagemagick" # "impala" # "imv" # "inetutils" # "iwd" # "jq" # "kvantum-qt5" # "lazydocker" # "lazygit" # "less" # "libyaml" # "libqalculate" # "llvm" # "luarocks" # "mako" # "man" # "mariadb-libs" # "mise" # "mpv" # "nautilus" # "noto-fonts" # "noto-fonts-cjk" # "noto-fonts-emoji" # "noto-fonts-extra" # "nss-mdns" # "omarchy-lazyvim" # "nvim" # "pamixer" # "pinta" # "playerctl" # "plocate" # "plymouth" # "polkit-gnome" # "postgresql-libs" # "power-profiles-daemon" # "python-gobject" # "python-poetry-core" # "python-terminaltexteffects" # "qt5-wayland" # "ripgrep" # "satty" # "slurp" # "starship" # "sushi" # "swaybg" # "swayosd" # "system-config-printer" # "tldr" # "tree-sitter-cli" # "ttf-cascadia-mono-nerd" # "ttf-ia-writer" # "ttf-jetbrains-mono-nerd" # "typora" # "tzupdate" # "ufw" # "ufw-docker" # "unzip" # "uwsm" # "walker-bin" # "waybar" # "wf-recorder" # "whois" # "wireless-regdb" # "wiremix" # "wireplumber" # "wl-clip-persist" # "wl-clipboard" # "wl-screenrec" # "woff2-font-awesome" # "xdg-desktop-portal-gtk" # "xdg-desktop-portal-hyprland" # "xmlstarlet" # "yaru-icon-theme" # "yay" # "zoxide" ) # Webapps # List from: https://github.com/basecamp/omarchy/blob/master/install/packaging/webapps.sh DEFAULT_WEBAPPS=( "HEY" "Basecamp" "WhatsApp" "Google Photos" "Google Contacts" "Google Messages" "ChatGPT" "YouTube" "GitHub" "X" "Figma" "Discord" "Zoom" ) # Function to check if package is installed is_package_installed() { local package="$1" pacman -Qi "$package" &>/dev/null return $? } # Function to check if webapp is installed is_webapp_installed() { local webapp="$1" # Check if .desktop file exists for the webapp local desktop_file="$HOME/.local/share/applications/$webapp.desktop" [[ -f "$desktop_file" ]] return $? } # Function to get list of installed packages from our removal list get_installed_packages() { for app in "${DEFAULT_APPS[@]}"; do if is_package_installed "$app"; then echo "$app" fi done } # Function to get list of installed webapps from our removal list get_installed_webapps() { for webapp in "${DEFAULT_WEBAPPS[@]}"; do if is_webapp_installed "$webapp"; then echo "$webapp" fi done } # Function to find keyboard bindings for an app/webapp find_app_bindings() { local app_name="$1" local bindings=() if [[ ! -f "$BINDINGS_FILE" ]]; then echo "" return fi # Convert app name to lowercase for case-insensitive matching local app_lower=$(echo "$app_name" | tr '[:upper:]' '[:lower:]') # Define webapp URL patterns for known webapps local webapp_domains="" case "$app_lower" in "hey") webapp_domains="app.hey.com|hey.com" ;; "basecamp") webapp_domains="basecamp.com|3.basecamp.com" ;; "whatsapp") webapp_domains="web.whatsapp.com|whatsapp.com" ;; "google photos") webapp_domains="photos.google.com" ;; "google contacts") webapp_domains="contacts.google.com" ;; "google messages") webapp_domains="messages.google.com" ;; "chatgpt") webapp_domains="chatgpt.com|chat.openai.com" ;; "youtube") webapp_domains="youtube.com|youtu.be" ;; "github") webapp_domains="github.com" ;; "x") webapp_domains="x.com|twitter.com" ;; "figma") webapp_domains="figma.com" ;; "discord") webapp_domains="discord.com|discord.gg" ;; "zoom") webapp_domains="zoom.us|zoom.com" ;; esac # Read the bindings file and find matching lines while IFS= read -r line; do # Skip comments and empty lines [[ "$line" =~ ^[[:space:]]*# ]] && continue [[ -z "${line// }" ]] && continue # Check if this is a bindd line if [[ "$line" =~ ^bindd[[:space:]]*= ]]; then local line_lower=$(echo "$line" | tr '[:upper:]' '[:lower:]') # Check for app matches in various formats # Direct app name match (e.g., "spotify", "obsidian", "signal-desktop") if [[ "$line_lower" =~ uwsm[[:space:]]+app[[:space:]]+--[[:space:]]+$app_lower([[:space:]]|$) ]]; then bindings+=("$line") # Terminal app match (e.g., "btop", "lazydocker", "nvim") elif [[ "$line_lower" =~ \$terminal[[:space:]]+-e[[:space:]]+$app_lower([[:space:]]|$) ]]; then bindings+=("$line") # Webapp match - check by URL domain if defined elif [[ "$line_lower" =~ omarchy-launch-webapp ]]; then if [[ -n "$webapp_domains" ]]; then # Extract URL from the line if [[ "$line" =~ omarchy-launch-webapp[[:space:]]+\"([^\"]+)\" ]]; then local url="${BASH_REMATCH[1]}" # Check if URL matches any of the webapp domains if [[ "$url" =~ ($webapp_domains) ]]; then bindings+=("$line") fi fi else # Fallback to label matching for unknown webapps if [[ "$line" =~ ,[[:space:]]*([^,]+),[[:space:]]*exec ]]; then local label=$(echo "$line" | sed -n 's/.*,[[:space:]]*\([^,]*\),[[:space:]]*exec.*/\1/p' | xargs) local label_lower=$(echo "$label" | tr '[:upper:]' '[:lower:]') if [[ "$label_lower" == "$app_lower" ]]; then bindings+=("$line") fi fi fi # Docker special case elif [[ "$app_lower" == "docker" ]] && [[ "$line_lower" =~ (docker|lazydocker) ]]; then bindings+=("$line") # 1password special cases elif [[ "$app_lower" == "1password-beta" || "$app_lower" == "1password-cli" ]] && [[ "$line_lower" =~ 1password ]]; then bindings+=("$line") # Nautilus (File manager) elif [[ "$app_lower" == "nautilus" ]] && [[ "$line_lower" =~ nautilus ]]; then bindings+=("$line") fi fi done < "$BINDINGS_FILE" # Return unique bindings printf '%s\n' "${bindings[@]}" | sort -u } # Function to remove bindings from the config file remove_bindings_from_file() { local bindings_to_remove=("$@") if [[ ${#bindings_to_remove[@]} -eq 0 ]]; then return 0 fi if [[ ! -f "$BINDINGS_FILE" ]]; then gum log --level warn "Bindings file not found at $BINDINGS_FILE" return 1 fi # Create backup local backup_file="${BINDINGS_FILE}.backup.$(date +%Y%m%d_%H%M%S)" cp "$BINDINGS_FILE" "$backup_file" gum log --level info "Created backup: $backup_file" # Create temporary file local temp_file=$(mktemp) local removed_count=0 # Process the file line by line while IFS= read -r line; do local should_remove=false # Check if this line should be removed for binding in "${bindings_to_remove[@]}"; do if [[ "$line" == "$binding" ]]; then should_remove=true ((removed_count++)) break fi done # Write line to temp file if not removing if [[ "$should_remove" == false ]]; then echo "$line" >> "$temp_file" fi done < "$BINDINGS_FILE" # Replace original file with temp file mv "$temp_file" "$BINDINGS_FILE" gum log --level info "✓ Removed $removed_count keyboard binding(s)" return 0 } # Enhanced selection menu using gum with integrated keyboard toggle enhanced_select_packages() { local installed_packages=("$@") local all_items=() local item_types=() local display_items=() local bindings_found=() # Parse arguments - first determine where packages end and webapps begin local separator_index=-1 for i in "${!installed_packages[@]}"; do if [[ "${installed_packages[$i]}" == "--webapps--" ]]; then separator_index=$i break fi done if [[ $separator_index -ge 0 ]]; then # Split into packages and webapps for ((i=0; i/dev/null 2>&1"; then gum log --level info "✓ Removed: $webapp" removed_webapps+=("$webapp") else gum log --level error "✗ Failed: $webapp" failed_webapps+=("$webapp") fi # Show progress bar local percentage=$(( (current * 100) / total )) local filled=$(( percentage / 5 )) local empty=$(( (100 - percentage) / 5 )) printf "Progress: " printf '\033[92m█%.0s\033[0m' $(seq 1 $filled) printf '\033[90m░%.0s\033[0m' $(seq 1 $empty) printf " %d%% (%d/%d)\n" "$percentage" "$current" "$total" echo "" done # Summary for webapps echo "" if [[ ${#removed_webapps[@]} -gt 0 ]]; then gum style --foreground 82 "Successfully removed: ${removed_webapps[*]}" fi if [[ ${#failed_webapps[@]} -gt 0 ]]; then gum style --foreground 214 "Could not remove: ${failed_webapps[*]}" fi # Return the number of failed webapps as exit code return ${#failed_webapps[@]} } # Function to remove packages remove_packages() { local packages=("$@") local failed_packages=() local removed_packages=() if [[ ${#packages[@]} -eq 0 ]]; then return 0 fi echo "" gum style \ --foreground 39 \ --bold \ "📦 Removing ${#packages[@]} package(s)..." echo "" # Ensure we have sudo credentials before starting if ! sudo -n true 2>/dev/null; then gum style --foreground 214 "🔐 Administrator privileges required for package removal" if ! sudo true; then gum log --level error "Failed to obtain sudo privileges" return 1 fi echo "" fi local current=0 local total=${#packages[@]} for pkg in "${packages[@]}"; do ((current++)) # Show current progress gum style --foreground 51 "[$current/$total] Processing: $pkg" if gum spin --spinner dot --title "Removing $pkg..." -- bash -c "sudo pacman -Rns --noconfirm '$pkg' 2>/dev/null"; then gum log --level info "✓ Removed: $pkg" removed_packages+=("$pkg") else gum log --level warn "✗ Failed: $pkg (may have dependencies)" failed_packages+=("$pkg") fi # Show progress bar local percentage=$(( (current * 100) / total )) local filled=$(( percentage / 5 )) local empty=$(( (100 - percentage) / 5 )) printf "Progress: " printf '\033[92m█%.0s\033[0m' $(seq 1 $filled) printf '\033[90m░%.0s\033[0m' $(seq 1 $empty) printf " %d%% (%d/%d)\n" "$percentage" "$current" "$total" echo "" done # Summary for packages echo "" if [[ ${#removed_packages[@]} -gt 0 ]]; then gum style --foreground 82 "Successfully removed: ${removed_packages[*]}" fi if [[ ${#failed_packages[@]} -gt 0 ]]; then gum style --foreground 214 "Could not remove: ${failed_packages[*]}" fi # Return the number of failed packages as exit code return ${#failed_packages[@]} } # Function to remove both packages and webapps remove_items() { local packages=("$@") local webapps=() local all_bindings_to_remove=() # Global success tracking local total_attempted=0 local total_failed=0 # Parse arguments - find separator local separator_index=-1 for i in "${!packages[@]}"; do if [[ "${packages[$i]}" == "--webapps--" ]]; then separator_index=$i break fi done if [[ $separator_index -ge 0 ]]; then # Split into packages and webapps arrays local pkg_array=() local webapp_array=() for ((i=0; i