mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 16:14:44 +00:00
388 lines
13 KiB
Bash
Executable File
388 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# Mole - Install Module
|
|
# Interactive application installer using Homebrew
|
|
#
|
|
# Usage:
|
|
# install.sh # Launch interactive installer
|
|
# install.sh --help # Show help information
|
|
|
|
set -euo pipefail
|
|
|
|
# Get script directory and source common functions
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/../lib/common.sh"
|
|
|
|
# Check if Homebrew is available
|
|
check_homebrew() {
|
|
if ! command -v brew >/dev/null 2>&1; then
|
|
log_error "Homebrew is not installed"
|
|
echo ""
|
|
echo "To install Homebrew, run:"
|
|
echo '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Application categories with descriptions
|
|
declare -A APP_CATEGORIES=(
|
|
["productivity"]="📝 Productivity Apps"
|
|
["development"]="💻 Development Tools"
|
|
["media"]="🎵 Media & Entertainment"
|
|
["utilities"]="🔧 System Utilities"
|
|
["communication"]="💬 Communication"
|
|
["design"]="🎨 Design & Graphics"
|
|
)
|
|
|
|
# Define applications by category
|
|
declare -A APPS=(
|
|
# Productivity
|
|
["notion"]="productivity|Notion|All-in-one workspace for notes and docs"
|
|
["obsidian"]="productivity|Obsidian|Knowledge management and note-taking"
|
|
["raycast"]="productivity|Raycast|Launcher and productivity tool"
|
|
["alfred"]="productivity|Alfred|Application launcher and productivity app"
|
|
["1password"]="productivity|1Password|Password manager"
|
|
|
|
# Development
|
|
["visual-studio-code"]="development|VS Code|Code editor by Microsoft"
|
|
["docker"]="development|Docker|Containerization platform"
|
|
["postman"]="development|Postman|API development and testing"
|
|
["github-desktop"]="development|GitHub Desktop|Git client for GitHub"
|
|
["figma"]="development|Figma|Design and prototyping tool"
|
|
["iterm2"]="development|iTerm2|Terminal replacement"
|
|
|
|
# Media
|
|
["vlc"]="media|VLC|Media player"
|
|
["spotify"]="media|Spotify|Music streaming"
|
|
["handbrake"]="media|HandBrake|Video transcoder"
|
|
["obs"]="media|OBS Studio|Live streaming and recording"
|
|
|
|
# Utilities
|
|
["the-unarchiver"]="utilities|The Unarchiver|Archive utility"
|
|
["appcleaner"]="utilities|AppCleaner|Uninstall applications completely"
|
|
["cleanmymac"]="utilities|CleanMyMac X|System cleaning and optimization"
|
|
["bartender-4"]="utilities|Bartender 4|Menu bar organization"
|
|
|
|
# Communication
|
|
["discord"]="communication|Discord|Voice and text chat"
|
|
["slack"]="communication|Slack|Team communication"
|
|
["telegram"]="communication|Telegram|Messaging app"
|
|
["zoom"]="communication|Zoom|Video conferencing"
|
|
|
|
# Design
|
|
["sketch"]="design|Sketch|Digital design toolkit"
|
|
["adobe-creative-cloud"]="design|Adobe CC|Creative suite"
|
|
["blender"]="design|Blender|3D creation suite"
|
|
)
|
|
|
|
# Initialize global variables
|
|
declare -a selected_apps=()
|
|
declare -a filtered_apps=()
|
|
current_category="all"
|
|
current_line=0
|
|
|
|
# Help information
|
|
show_help() {
|
|
echo "Mole - Interactive App Installer"
|
|
echo "================================="
|
|
echo ""
|
|
echo "Description: Install useful applications using Homebrew Cask"
|
|
echo ""
|
|
echo "Features:"
|
|
echo " • Browse apps by category"
|
|
echo " • Navigate with ↑/↓ arrow keys"
|
|
echo " • Select/deselect apps with SPACE"
|
|
echo " • Filter by category with 1-6 keys"
|
|
echo " • Install selected apps with ENTER"
|
|
echo " • Quit anytime with 'q'"
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " ./install.sh Launch interactive installer"
|
|
echo " ./install.sh --help Show this help message"
|
|
echo ""
|
|
echo "Requirements:"
|
|
echo " • Homebrew must be installed"
|
|
echo " • Internet connection for downloads"
|
|
echo ""
|
|
}
|
|
|
|
# Parse arguments
|
|
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
show_help
|
|
exit 0
|
|
fi
|
|
|
|
# Filter apps by category
|
|
filter_apps_by_category() {
|
|
local category="$1"
|
|
filtered_apps=()
|
|
|
|
for app_key in "${!APPS[@]}"; do
|
|
IFS='|' read -r app_category app_name app_desc <<< "${APPS[$app_key]}"
|
|
if [[ "$category" == "all" || "$app_category" == "$category" ]]; then
|
|
filtered_apps+=("$app_key|$app_category|$app_name|$app_desc")
|
|
fi
|
|
done
|
|
|
|
# Sort alphabetically by name
|
|
IFS=$'\n' filtered_apps=($(sort -t'|' -k3 <<<"${filtered_apps[*]}"))
|
|
unset IFS
|
|
}
|
|
|
|
# Display application list
|
|
display_apps() {
|
|
clear
|
|
echo "📦 Mole - Interactive App Installer"
|
|
echo "══════════════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
# Show category filter
|
|
local category_name="All Applications"
|
|
case "$current_category" in
|
|
"productivity") category_name="${APP_CATEGORIES[productivity]}" ;;
|
|
"development") category_name="${APP_CATEGORIES[development]}" ;;
|
|
"media") category_name="${APP_CATEGORIES[media]}" ;;
|
|
"utilities") category_name="${APP_CATEGORIES[utilities]}" ;;
|
|
"communication") category_name="${APP_CATEGORIES[communication]}" ;;
|
|
"design") category_name="${APP_CATEGORIES[design]}" ;;
|
|
esac
|
|
|
|
echo -e "${PURPLE}Category: $category_name${NC}"
|
|
echo -e "${PURPLE}Showing ${#filtered_apps[@]} applications${NC}"
|
|
echo ""
|
|
|
|
# Display apps (max 15 per page)
|
|
local start_idx=0
|
|
local end_idx=$((${#filtered_apps[@]} - 1))
|
|
local max_display=15
|
|
|
|
if [[ $end_idx -gt $((max_display - 1)) ]]; then
|
|
end_idx=$((max_display - 1))
|
|
fi
|
|
|
|
for ((i=start_idx; i<=end_idx && i<${#filtered_apps[@]}; i++)); do
|
|
IFS='|' read -r app_key app_category app_name app_desc <<< "${filtered_apps[i]}"
|
|
|
|
local prefix=" "
|
|
local line_color="$NC"
|
|
local name_color="$NC"
|
|
|
|
# Current selection highlighting
|
|
if [[ $i -eq $current_line ]]; then
|
|
prefix="▶ "
|
|
line_color="$BLUE"
|
|
name_color="$BLUE"
|
|
fi
|
|
|
|
# Check if app is selected
|
|
local checkbox="[ ]"
|
|
local checkbox_color="$NC"
|
|
for selected in "${selected_apps[@]}"; do
|
|
if [[ "$selected" == "$app_key" ]]; then
|
|
checkbox="[✓]"
|
|
checkbox_color="$GREEN"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Format display
|
|
printf "${line_color}${prefix}${checkbox_color}${checkbox}${NC} "
|
|
printf "${name_color}%-25s${NC} " "$app_name"
|
|
printf "│ %s\n" "$app_desc"
|
|
done
|
|
|
|
echo ""
|
|
echo "──────────────────────────────────────────────────────────────────────"
|
|
|
|
# Show selection summary
|
|
local selected_count=${#selected_apps[@]}
|
|
if [[ $selected_count -eq 0 ]]; then
|
|
echo -e "${BLUE}📋 No applications selected${NC}"
|
|
else
|
|
echo -e "${GREEN}📋 Selected: $selected_count applications${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Show category filters
|
|
echo -e "${PURPLE}🏷️ Categories:${NC}"
|
|
echo " 0 All 1 Productivity 2 Development 3 Media 4 Utilities 5 Communication 6 Design"
|
|
echo ""
|
|
|
|
# Controls
|
|
echo -e "${PURPLE}🎮 Controls:${NC}"
|
|
echo " ↑/↓ Navigate SPACE Select 0-6 Filter ENTER Install ? Help q Quit"
|
|
}
|
|
|
|
# Interactive app selection
|
|
interactive_app_selection() {
|
|
filter_apps_by_category "$current_category"
|
|
current_line=0
|
|
|
|
while true; do
|
|
display_apps
|
|
|
|
# Read key input
|
|
read -rsn1 key
|
|
|
|
case "$key" in
|
|
$'\x1b') # ESC sequences
|
|
read -rsn2 key
|
|
case "$key" in
|
|
'[A') # Up arrow
|
|
((current_line > 0)) && ((current_line--))
|
|
;;
|
|
'[B') # Down arrow
|
|
((current_line < ${#filtered_apps[@]} - 1)) && ((current_line++))
|
|
;;
|
|
esac
|
|
;;
|
|
' ') # Space - toggle selection
|
|
if [[ ${#filtered_apps[@]} -gt 0 && $current_line -lt ${#filtered_apps[@]} ]]; then
|
|
IFS='|' read -r app_key app_category app_name app_desc <<< "${filtered_apps[current_line]}"
|
|
|
|
# Check if already selected
|
|
local found=false
|
|
for i in "${!selected_apps[@]}"; do
|
|
if [[ "${selected_apps[i]}" == "$app_key" ]]; then
|
|
unset 'selected_apps[i]'
|
|
selected_apps=("${selected_apps[@]}") # Re-index array
|
|
found=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ "$found" == "false" ]]; then
|
|
selected_apps+=("$app_key")
|
|
fi
|
|
fi
|
|
;;
|
|
$'\n'|$'\r') # Enter - proceed to installation
|
|
if [[ ${#selected_apps[@]} -gt 0 ]]; then
|
|
break
|
|
fi
|
|
;;
|
|
'q'|'Q') # Quit
|
|
log_info "Installation cancelled"
|
|
return 1
|
|
;;
|
|
[0-6]) # Category filters
|
|
case "$key" in
|
|
'0') current_category="all" ;;
|
|
'1') current_category="productivity" ;;
|
|
'2') current_category="development" ;;
|
|
'3') current_category="media" ;;
|
|
'4') current_category="utilities" ;;
|
|
'5') current_category="communication" ;;
|
|
'6') current_category="design" ;;
|
|
esac
|
|
filter_apps_by_category "$current_category"
|
|
current_line=0
|
|
;;
|
|
'a'|'A') # Select all visible
|
|
for app_data in "${filtered_apps[@]}"; do
|
|
IFS='|' read -r app_key app_category app_name app_desc <<< "$app_data"
|
|
|
|
# Check if already selected
|
|
local found=false
|
|
for selected in "${selected_apps[@]}"; do
|
|
if [[ "$selected" == "$app_key" ]]; then
|
|
found=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ "$found" == "false" ]]; then
|
|
selected_apps+=("$app_key")
|
|
fi
|
|
done
|
|
;;
|
|
'n'|'N') # Select none
|
|
selected_apps=()
|
|
;;
|
|
'?') # Help
|
|
show_help
|
|
echo ""
|
|
read -p "Press any key to continue..." -n 1 -r
|
|
;;
|
|
esac
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
# Install selected applications
|
|
install_applications() {
|
|
log_header "Installing selected applications"
|
|
|
|
echo "You selected ${#selected_apps[@]} application(s) for installation:"
|
|
echo ""
|
|
|
|
for app_key in "${selected_apps[@]}"; do
|
|
IFS='|' read -r app_category app_name app_desc <<< "${APPS[$app_key]}"
|
|
echo " • $app_name - $app_desc"
|
|
done
|
|
|
|
echo ""
|
|
read -p "Continue with installation? (y/N): " -n 1 -r
|
|
echo
|
|
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
echo ""
|
|
log_info "Starting installation..."
|
|
echo ""
|
|
|
|
local successful=0
|
|
local failed=0
|
|
|
|
for app_key in "${selected_apps[@]}"; do
|
|
IFS='|' read -r app_category app_name app_desc <<< "${APPS[$app_key]}"
|
|
|
|
echo -e "${BLUE}Installing $app_name...${NC}"
|
|
|
|
if brew install --cask "$app_key" 2>/dev/null; then
|
|
echo -e " ${GREEN}✓${NC} $app_name installed successfully"
|
|
((successful++))
|
|
else
|
|
echo -e " ${RED}✗${NC} Failed to install $app_name"
|
|
((failed++))
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# Summary
|
|
echo "══════════════════════════════════════════════════════════════════════"
|
|
log_success "Installation complete!"
|
|
echo "📊 Successfully installed: $successful applications"
|
|
if [[ $failed -gt 0 ]]; then
|
|
echo "⚠️ Failed to install: $failed applications"
|
|
fi
|
|
else
|
|
log_info "Installation cancelled"
|
|
fi
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
echo "📦 Mole - Interactive App Installer"
|
|
echo "===================================="
|
|
echo ""
|
|
|
|
# Check Homebrew
|
|
check_homebrew
|
|
|
|
log_info "Checking Homebrew installation..."
|
|
echo ""
|
|
|
|
# Interactive selection
|
|
if ! interactive_app_selection; then
|
|
return 0
|
|
fi
|
|
|
|
clear
|
|
install_applications
|
|
|
|
log_success "App installer finished"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |