diff --git a/bin/clean.sh b/bin/clean.sh index 0c987e4..30743c8 100755 --- a/bin/clean.sh +++ b/bin/clean.sh @@ -257,65 +257,66 @@ safe_clean() { } start_cleanup() { + clear + echo "" + echo -e "${PURPLE}🧹 Clean Your Mac${NC}" echo "" echo "Mole will remove app caches, browser data, developer tools, and temporary files." - echo "" if [[ "$DRY_RUN" != "true" && -t 0 ]]; then - echo -e "${BLUE}Tip:${NC} Want a preview first? Run 'mole clean --dry-run'." echo "" + echo -e "${BLUE}Tip:${NC} Want a preview first? Run 'mole clean --dry-run'." fi if [[ "$DRY_RUN" == "true" ]]; then + echo "" echo -e "${YELLOW}🧪 Dry Run mode:${NC} showing what would be removed (no deletions)." echo "" SYSTEM_CLEAN=false return fi - local password="" if [[ -t 0 ]]; then - echo "Enter admin password for system-level cleanup (or press Enter to skip):" - echo -n "> " + echo "" + echo "System-level cleanup removes system caches and temp files, optional." + echo -en "${BLUE}Enter admin password to enable, or press Enter to skip: ${NC}" read -s password echo "" - else - password="" - echo "" - echo -e "${BLUE}ℹ${NC} Running in non-interactive mode" - echo " • System-level cleanup will be skipped (requires password)" - echo " • User-level cleanup will proceed automatically" - echo "" - fi - - if [[ -n "$password" ]] && echo "$password" | sudo -S true 2>/dev/null; then - SYSTEM_CLEAN=true - # Start sudo keepalive with error handling and shorter intervals - ( - local retry_count=0 - while true; do - if ! sudo -n true 2>/dev/null; then - ((retry_count++)) - if [[ $retry_count -ge 3 ]]; then - log_warning "Sudo keepalive failed, system-level cleanup may be interrupted" >&2 - exit 1 + + if [[ -n "$password" ]] && echo "$password" | sudo -S true 2>/dev/null; then + SYSTEM_CLEAN=true + # Start sudo keepalive with error handling + ( + local retry_count=0 + while true; do + if ! sudo -n true 2>/dev/null; then + ((retry_count++)) + if [[ $retry_count -ge 3 ]]; then + exit 1 + fi + sleep 5 + continue fi - sleep 5 - continue - fi - retry_count=0 - sleep 30 - kill -0 "$$" 2>/dev/null || exit - done - ) 2>/dev/null & - SUDO_KEEPALIVE_PID=$! - log_info "Starting comprehensive cleanup with admin privileges..." + retry_count=0 + sleep 30 + kill -0 "$$" 2>/dev/null || exit + done + ) 2>/dev/null & + SUDO_KEEPALIVE_PID=$! + else + SYSTEM_CLEAN=false + if [[ -n "$password" ]]; then + echo "" + echo -e "${YELLOW}⚠️ Invalid password, continuing with user-level cleanup${NC}" + fi + fi else SYSTEM_CLEAN=false - log_info "Starting user-level cleanup..." - if [[ -n "$password" ]]; then - echo -e "${YELLOW}⚠️ Invalid password, continuing with user-level cleanup${NC}" - fi + echo "" + echo -e "${BLUE}ℹ${NC} Running in non-interactive mode" + echo " • System-level cleanup skipped (requires interaction)" + echo " • User-level cleanup will proceed automatically" + echo "" fi } diff --git a/bin/uninstall.sh b/bin/uninstall.sh index 3318602..12fa52a 100755 --- a/bin/uninstall.sh +++ b/bin/uninstall.sh @@ -19,29 +19,29 @@ source "$SCRIPT_DIR/../lib/batch_uninstall.sh" # Help information show_help() { - echo "App Uninstaller" - echo "===============" + echo "Usage: mole uninstall" echo "" - echo "Uninstall applications and clean their data completely." + echo "Interactive application uninstaller - Remove apps completely" echo "" - echo "Controls:" - echo " ↑/↓ Navigate" - echo " SPACE Select/deselect" - echo " ENTER Confirm" - echo " Q Quit" - echo "" - echo "Usage:" - echo " ./uninstall.sh Launch interactive uninstaller" - echo " ./uninstall.sh --help Show this help message" + echo "Keyboard Controls:" + echo " ↑/↓ Navigate items" + echo " Space Select/deselect" + echo " Enter Confirm and uninstall" + echo " Q / ESC Quit" echo "" echo "What gets cleaned:" echo " • Application bundle" - echo " • Application Support data" + echo " • Application Support data (12+ locations)" echo " • Cache files" echo " • Preference files" echo " • Log files" echo " • Saved application state" echo " • Container data (sandboxed apps)" + echo " • WebKit storage, HTTP storage, cookies" + echo " • Extensions, plugins, services" + echo "" + echo "Examples:" + echo " mole uninstall Launch interactive uninstaller" echo "" } @@ -116,11 +116,13 @@ scan_applications() { local temp_file=$(mktemp) - echo -n "Scanning... " >&2 - # Pre-cache current epoch to avoid repeated calls local current_epoch=$(date "+%s") + # Spinner for scanning feedback + local spinner_chars="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" + local spinner_idx=0 + # First pass: quickly collect all valid app paths and bundle IDs local -a app_data_tuples=() while IFS= read -r -d '' app_path; do @@ -279,8 +281,10 @@ scan_applications() { process_app_metadata "$app_data_tuple" "$temp_file" "$current_epoch" & pids+=($!) - # Update progress - echo -ne "\rScanning... $app_count/$total_apps" >&2 + # Update progress with spinner + local spinner_char="${spinner_chars:$((spinner_idx % 10)):1}" + echo -ne "\r🗑️ ${spinner_char} Scanning... $app_count/$total_apps" >&2 + ((spinner_idx++)) # Wait if we've hit max parallel limit if (( ${#pids[@]} >= max_parallel )); then @@ -294,7 +298,8 @@ scan_applications() { wait "$pid" 2>/dev/null done - echo -e "\rFound $app_count applications ✓" >&2 + echo -e "\r🗑️ ✓ Found $app_count applications " >&2 + echo "" >&2 # Check if we found any applications if [[ ! -s "$temp_file" ]]; then diff --git a/lib/app_selector.sh b/lib/app_selector.sh index cb9aeb0..49ba3f3 100755 --- a/lib/app_selector.sh +++ b/lib/app_selector.sh @@ -37,17 +37,11 @@ select_apps_for_uninstall() { menu_options+=("$(format_app_display "$display_name" "$size" "$last_used")") done - echo "" - echo "🗑️ App Uninstaller" - echo "" - echo "Mole will uninstall selected apps and clean all their related files." - echo "" - echo "Found ${#apps_data[@]} apps. Select apps to remove:" echo "" # Use paginated menu - result will be stored in MOLE_SELECTION_RESULT MOLE_SELECTION_RESULT="" - paginated_multi_select "Select Apps to Remove" "${menu_options[@]}" + paginated_multi_select "🗑️ Select Apps to Remove" "${menu_options[@]}" local exit_code=$? if [[ $exit_code -ne 0 ]]; then diff --git a/lib/batch_uninstall.sh b/lib/batch_uninstall.sh index 827c84a..ca3bcae 100755 --- a/lib/batch_uninstall.sh +++ b/lib/batch_uninstall.sh @@ -20,8 +20,18 @@ batch_uninstall_applications() { local -a app_details=() echo "" - echo -e "${BLUE}📋 Analyzing selected applications...${NC}" + + # Show analyzing message with spinner + local spinner_chars="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" + local spinner_idx=0 + local analyzed=0 + for selected_app in "${selected_apps[@]}"; do + # Update spinner + local spinner_char="${spinner_chars:$((spinner_idx % 10)):1}" + ((analyzed++)) + echo -ne "\r🗑️ ${spinner_char} Analyzing... $analyzed/${#selected_apps[@]}" >&2 + ((spinner_idx++)) IFS='|' read -r epoch app_path app_name bundle_id size last_used <<< "$selected_app" # Check if app is running @@ -47,6 +57,9 @@ batch_uninstall_applications() { app_details+=("$app_name|$app_path|$bundle_id|$total_kb|$encoded_files") done + # Clear spinner line + echo -ne "\r\033[K" >&2 + # Format size display if [[ $total_estimated_size -gt 1048576 ]]; then local size_display=$(echo "$total_estimated_size" | awk '{printf "%.2fGB", $1/1024/1024}') diff --git a/lib/paginated_menu.sh b/lib/paginated_menu.sh index 61d860f..8ce3b32 100755 --- a/lib/paginated_menu.sh +++ b/lib/paginated_menu.sh @@ -63,8 +63,12 @@ paginated_multi_select() { draw_menu() { printf "\033[H\033[J" >&2 # Clear screen and move to top - # Header - printf "%s\n%s\n" "$title" "$(printf '=%.0s' $(seq 1 ${#title}))" >&2 + # Header - strip emoji for length calculation + local title_clean="${title//[^[:print:]]/}" + local title_len_approx=$(( ${#title_clean} - 4 )) # Rough adjustment for emoji + [[ $title_len_approx -lt 10 ]] && title_len_approx=10 + + printf "${PURPLE}%s${NC}\n%s\n" "$title" "$(printf '=%.0s' $(seq 1 $title_len_approx))" >&2 # Status local selected_count=0 @@ -92,7 +96,7 @@ paginated_multi_select() { done print_line "" - print_line "↑↓: Navigate | Space: Select | Enter: Confirm | Q: Exit" + print_line "${GRAY}↑/↓${NC} Navigate ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}Q/ESC${NC} Quit" } # Show help screen @@ -105,9 +109,7 @@ Help - Navigation Controls ↑ / ↓ Navigate up/down Space Select/deselect item Enter Confirm selection - A Select all - N Deselect all - Q Exit + Q / ESC Exit Press any key to continue... EOF