diff --git a/bin/analyze.sh b/bin/analyze.sh index c8e7fdc..b65b3ee 100755 --- a/bin/analyze.sh +++ b/bin/analyze.sh @@ -25,10 +25,10 @@ readonly MIN_SMALL_FILE_SIZE="10000000" # 10MB # Emoji badges for list displays only readonly BADGE_DIR="🍞" -readonly BADGE_FILE="📔" -readonly BADGE_MEDIA="🌁" +readonly BADGE_FILE="🍚" +readonly BADGE_MEDIA="🥟" readonly BADGE_BUNDLE="🥜" -readonly BADGE_LOG="📝" +readonly BADGE_LOG="🍹" readonly BADGE_APP="🐣" # Global state @@ -662,9 +662,9 @@ display_cleanup_suggestions_compact() { echo "" if [[ -n "$action_command" ]]; then if [[ "$action_command" == "mole clean" ]]; then - echo " ${GRAY}→ Run${NC} ${YELLOW}mole clean${NC} ${GRAY}to cleanup system files${NC}" + echo " ${GRAY}${ICON_NAV_RIGHT} Run${NC} ${YELLOW}mole clean${NC} ${GRAY}to cleanup system files${NC}" else - echo " ${GRAY}→ Review and ${NC}${YELLOW}$action_command${NC}" + echo " ${GRAY}${ICON_NAV_RIGHT} Review and ${NC}${YELLOW}$action_command${NC}" fi fi echo "" @@ -740,7 +740,7 @@ display_cleanup_suggestions() { echo "" echo " Tip: Run 'mole clean' to perform cleanup operations" else - echo " ${GREEN}✓${NC} No obvious cleanup opportunities found" + echo " ${GREEN}${ICON_SUCCESS}${NC} No obvious cleanup opportunities found" fi echo "" } @@ -971,14 +971,14 @@ display_recommendations() { echo " ${YELLOW}Quick Actions:${NC}" if [[ "$CURRENT_PATH" == "$HOME/Downloads"* ]]; then - echo " → Delete ${RED}[Can Delete]${NC} items (installers/DMG)" - echo " → Review ${YELLOW}[Review]${NC} items (videos/archives)" + echo " ${ICON_NAV_RIGHT} Delete ${RED}[Can Delete]${NC} items (installers/DMG)" + echo " ${ICON_NAV_RIGHT} Review ${YELLOW}[Review]${NC} items (videos/archives)" elif [[ "$CURRENT_PATH" == "$HOME/Library"* ]]; then - echo " → Run ${GREEN}mole clean${NC} to clear caches safely" - echo " → Check Xcode/developer caches if applicable" + echo " ${ICON_NAV_RIGHT} Run ${GREEN}mole clean${NC} to clear caches safely" + echo " ${ICON_NAV_RIGHT} Check Xcode/developer caches if applicable" else - echo " → Review ${RED}[Can Delete]${NC} and ${YELLOW}[Review]${NC} items" - echo " → Run ${GREEN}mole analyze ~/Library${NC} to check caches" + echo " ${ICON_NAV_RIGHT} Review ${RED}[Can Delete]${NC} and ${YELLOW}[Review]${NC} items" + echo " ${ICON_NAV_RIGHT} Run ${GREEN}mole analyze ~/Library${NC} to check caches" fi echo "" } @@ -1124,7 +1124,7 @@ display_directory_list() { # Highlight selected line if [[ $idx -eq $cursor_pos ]]; then - printf " ${BLUE}▶${NC} %s [${GREEN}%s${NC}] %5s%% %s\n" \ + printf " ${BLUE}${ICON_ARROW}${NC} %s [${GREEN}%s${NC}] %5s%% %s\n" \ "$bar" "$human_size" "$percentage" "$dirname" else printf " %s [${BLUE}%s${NC}] %5s%% %s\n" \ @@ -1183,7 +1183,7 @@ display_interactive_menu() { echo "" # Show navigation hints - echo "${GRAY}↑↓ Navigate | → Drill Down | ← Go Back | f Files | t Types | q Quit${NC}" + echo "${GRAY}${ICON_NAV_UP}${ICON_NAV_DOWN} Navigate | ${ICON_NAV_RIGHT} Drill Down | ${ICON_NAV_LEFT} Go Back | f Files | t Types | q Quit${NC}" echo "" # Display results based on view mode @@ -1222,12 +1222,12 @@ display_file_types() { # Analyze common file types (bash 3.2 compatible - no associative arrays) local -a type_names=("Videos" "Images" "Archives" "Documents" "Audio") - + local type_name for type_name in "${type_names[@]}"; do local query="" local badge="$BADGE_FILE" - + # Map type name to query and badge case "$type_name" in "Videos") @@ -1251,7 +1251,7 @@ display_file_types() { badge="🎵" ;; esac - + local files=$(mdfind -onlyin "$CURRENT_PATH" "$query" 2>/dev/null) local count=$(echo "$files" | grep -c . || echo "0") local total_size=0 @@ -1526,7 +1526,7 @@ show_volumes_overview() { # Build line (simple display without size) local line="" if [[ $idx -eq $cursor ]]; then - line=$(printf " ${GREEN}▶${NC} ${BLUE}%s${NC}" "$display_name") + line=$(printf " ${GREEN}${ICON_ARROW}${NC} ${BLUE}%s${NC}" "$display_name") else line=$(printf " ${GRAY}%s${NC}" "$display_name") fi @@ -1817,7 +1817,7 @@ interactive_drill_down() { # Build line with emoji badge, size, and name local line if [[ $idx -eq $cursor ]]; then - line=$(printf " ${GREEN}▶${NC} %s%s${NC} %10s %s${NC}" "$color" "$badge" "$human_size" "$name") + line=$(printf " ${GREEN}${ICON_ARROW}${NC} %s%s${NC} %10s %s${NC}" "$color" "$badge" "$human_size" "$name") else line=$(printf " %s%s${NC} %10s %s${NC}" "$color" "$badge" "$human_size" "$name") fi @@ -1842,7 +1842,7 @@ interactive_drill_down() { fi # Bottom help bar - output+=" ${GRAY}↑/↓${NC} Navigate ${GRAY}|${NC} ${GRAY}Enter${NC} Open ${GRAY}|${NC} ${GRAY}←${NC} Back ${GRAY}|${NC} ${GRAY}Del${NC} Delete ${GRAY}|${NC} ${GRAY}O${NC} Finder ${GRAY}|${NC} ${GRAY}Q/ESC${NC} Quit"$'\n' + output+=" ${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Navigate ${GRAY}|${NC} ${GRAY}Enter${NC} Open ${GRAY}|${NC} ${GRAY}${ICON_NAV_LEFT}${NC} Back ${GRAY}|${NC} ${GRAY}Del${NC} Delete ${GRAY}|${NC} ${GRAY}O${NC} Finder ${GRAY}|${NC} ${GRAY}Q/ESC${NC} Quit"$'\n' # Output everything at once (single write = no flicker) printf "%b" "$output" >&2 @@ -1925,7 +1925,7 @@ interactive_drill_down() { open "$selected_path" 2>/dev/null && open_success=true if [[ "$open_success" == "true" ]]; then echo "" - echo " ${GREEN}✓${NC} File opened in external app" + echo " ${GREEN}${ICON_SUCCESS}${NC} File opened in external app" sleep 0.8 fi fi @@ -1946,7 +1946,7 @@ interactive_drill_down() { # Show brief success message if [[ "$open_success" == "true" ]]; then echo "" - echo " ${GREEN}✓${NC} File opened in external app" + echo " ${GREEN}${ICON_SUCCESS}${NC} File opened in external app" sleep 0.8 fi fi @@ -1992,7 +1992,7 @@ interactive_drill_down() { "OPEN") if command -v open >/dev/null 2>&1; then if open "$current_path" >/dev/null 2>&1; then - status_message="${GREEN}✓${NC} Finder opened: ${GRAY}$current_path${NC}" + status_message="${GREEN}${ICON_SUCCESS}${NC} Finder opened: ${GRAY}$current_path${NC}" else status_message="${YELLOW}Warning:${NC} Could not open ${GRAY}$current_path${NC}" fi @@ -2048,7 +2048,7 @@ interactive_drill_down() { fi echo "" - echo -e " ${PURPLE}☛${NC} Press ${GREEN}Enter${NC} to delete, ${GRAY}ESC${NC} to cancel" + echo -e " ${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to delete, ${GRAY}ESC${NC} to cancel" echo "" # Read confirmation @@ -2063,7 +2063,7 @@ interactive_drill_down() { echo "" if ! request_sudo_access "Admin access required to delete this item"; then echo "" - echo " ${RED}✗ Admin access denied${NC}" + echo " ${RED}${ICON_ERROR} Admin access denied${NC}" sleep 1.5 continue fi @@ -2088,7 +2088,7 @@ interactive_drill_down() { fi if [[ "$delete_success" == "true" ]]; then - echo " ${GREEN}✓ Deleted successfully (freed $human_size)${NC}" + echo " ${GREEN}${ICON_SUCCESS} Deleted successfully (freed $human_size)${NC}" sleep 0.8 # Clear cache to force rescan @@ -2104,12 +2104,12 @@ interactive_drill_down() { ((cursor--)) fi else - echo " ${RED}✗ Failed to delete${NC}" + echo " ${RED}${ICON_ERROR} Failed to delete${NC}" echo "" echo " ${YELLOW}Possible reasons:${NC}" - echo " • File is being used by another application" - echo " • Insufficient permissions" - echo " • System protection (SIP) prevents deletion" + echo " ${ICON_LIST} File is being used by another application" + echo " ${ICON_LIST} Insufficient permissions" + echo " ${ICON_LIST} System protection (SIP) prevents deletion" echo "" echo " ${GRAY}Press any key to continue...${NC}" read_key >/dev/null 2>&1 @@ -2298,20 +2298,20 @@ main() { echo "Interactive disk space explorer - Navigate folders sorted by size" echo "" echo "Keyboard Controls:" - echo " ↑/↓ Navigate items" - echo " Enter / → Open selected folder" - echo " ← Go back to parent directory" + echo " ${ICON_NAV_UP}/${ICON_NAV_DOWN} Navigate items" + echo " Enter / ${ICON_NAV_RIGHT} Open selected folder" + echo " ${ICON_NAV_LEFT} Go back to parent directory" echo " Delete Delete selected file/folder (requires confirmation)" echo " O Reveal current directory in Finder" echo " Q / ESC Quit the explorer" echo "" echo "Features:" - echo " • Files and folders sorted by size (largest first)" - echo " • Shows top 16 items per directory" - echo " • Fast parallel scanning with smart timeout" - echo " • Session cache for instant navigation" - echo " • Color coding for large folders (Red >10GB, Yellow >1GB)" - echo " • Safe deletion with confirmation" + echo " ${ICON_LIST} Files and folders sorted by size (largest first)" + echo " ${ICON_LIST} Shows top 16 items per directory" + echo " ${ICON_LIST} Fast parallel scanning with smart timeout" + echo " ${ICON_LIST} Session cache for instant navigation" + echo " ${ICON_LIST} Color coding for large folders (Red >10GB, Yellow >1GB)" + echo " ${ICON_LIST} Safe deletion with confirmation" echo "" echo "Examples:" echo " mole analyze Start exploring from home directory" diff --git a/bin/uninstall.sh b/bin/uninstall.sh index 8746d4c..30aed56 100755 --- a/bin/uninstall.sh +++ b/bin/uninstall.sh @@ -28,21 +28,21 @@ show_help() { echo "Interactive application uninstaller - Remove apps completely" echo "" echo "Keyboard Controls:" - echo " ↑/↓ Navigate items" + echo " ${ICON_NAV_UP}/${ICON_NAV_DOWN} 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 (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 " ${ICON_LIST} Application bundle" + echo " ${ICON_LIST} Application Support data (12+ locations)" + echo " ${ICON_LIST} Cache files" + echo " ${ICON_LIST} Preference files" + echo " ${ICON_LIST} Log files" + echo " ${ICON_LIST} Saved application state" + echo " ${ICON_LIST} Container data (sandboxed apps)" + echo " ${ICON_LIST} WebKit storage, HTTP storage, cookies" + echo " ${ICON_LIST} Extensions, plugins, services" echo "" echo "Examples:" echo " mole uninstall Launch interactive uninstaller" @@ -390,7 +390,7 @@ uninstall_applications() { local total_size_freed=0 echo "" - echo -e "${PURPLE}▶ Uninstalling selected applications${NC}" + echo -e "${PURPLE}${ICON_ARROW} Uninstalling selected applications${NC}" if [[ ${#selected_apps[@]} -eq 0 ]]; then log_warning "No applications selected for uninstallation" @@ -404,14 +404,14 @@ uninstall_applications() { # Check if app is running (use app path for precise matching) if pgrep -f "$app_path" >/dev/null 2>&1; then - echo -e "${YELLOW}⚠ $app_name is currently running${NC}" + echo -e "${YELLOW}${ICON_ERROR} $app_name is currently running${NC}" read -p " Force quit $app_name? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then pkill -f "$app_path" 2>/dev/null || true sleep 2 else - echo -e " ${BLUE}○${NC} Skipped $app_name" + echo -e " ${BLUE}${ICON_EMPTY}${NC} Skipped $app_name" continue fi fi @@ -429,18 +429,18 @@ uninstall_applications() { local total_kb=$((app_size_kb + related_size_kb + system_size_kb)) # Show what will be removed - echo -e "${BLUE}◎${NC} $app_name - Files to be removed:" - echo -e " ${GREEN}✓${NC} Application: $(echo "$app_path" | sed "s|$HOME|~|")" + echo -e "${BLUE}${ICON_CONFIRM}${NC} $app_name - Files to be removed:" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Application: $(echo "$app_path" | sed "s|$HOME|~|")" # Show user-level files while IFS= read -r file; do - [[ -n "$file" && -e "$file" ]] && echo -e " ${GREEN}✓${NC} $(echo "$file" | sed "s|$HOME|~|")" + [[ -n "$file" && -e "$file" ]] && echo -e " ${GREEN}${ICON_SUCCESS}${NC} $(echo "$file" | sed "s|$HOME|~|")" done <<< "$related_files" # Show system-level files if [[ -n "$system_files" ]]; then while IFS= read -r file; do - [[ -n "$file" && -e "$file" ]] && echo -e " ${BLUE}●${NC} System: $file" + [[ -n "$file" && -e "$file" ]] && echo -e " ${BLUE}${ICON_SOLID}${NC} System: $file" done <<< "$system_files" fi @@ -461,9 +461,9 @@ uninstall_applications() { if [[ $REPLY =~ ^[Yy]$ ]]; then # Remove the application if rm -rf "$app_path" 2>/dev/null; then - echo -e " ${GREEN}✓${NC} Removed application" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed application" else - echo -e " ${RED}✗${NC} Failed to remove $app_path" + echo -e " ${RED}${ICON_ERROR}${NC} Failed to remove $app_path" continue fi @@ -471,20 +471,20 @@ uninstall_applications() { while IFS= read -r file; do if [[ -n "$file" && -e "$file" ]]; then if rm -rf "$file" 2>/dev/null; then - echo -e " ${GREEN}✓${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)" fi fi done <<< "$related_files" # Remove system-level files (requires sudo) if [[ -n "$system_files" ]]; then - echo -e " ${BLUE}●${NC} Admin access required for system files" + echo -e " ${BLUE}${ICON_SOLID}${NC} Admin access required for system files" while IFS= read -r file; do if [[ -n "$file" && -e "$file" ]]; then if sudo rm -rf "$file" 2>/dev/null; then - echo -e " ${GREEN}✓${NC} Removed $(basename "$file")" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(basename "$file")" else - echo -e " ${YELLOW}⚠${NC} Failed to remove: $file" + echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file" fi fi done <<< "$system_files" @@ -494,14 +494,14 @@ uninstall_applications() { ((files_cleaned++)) ((total_items++)) - echo -e " ${GREEN}✓${NC} $app_name uninstalled successfully" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} $app_name uninstalled successfully" else - echo -e " ${BLUE}○${NC} Skipped $app_name" + echo -e " ${BLUE}${ICON_EMPTY}${NC} Skipped $app_name" fi done # Show final summary - echo -e "${PURPLE}▶ Uninstallation Summary${NC}" + echo -e "${PURPLE}${ICON_ARROW} Uninstallation Summary${NC}" if [[ $total_size_freed -gt 0 ]]; then if [[ $total_size_freed -gt 1048576 ]]; then # > 1GB @@ -512,10 +512,10 @@ uninstall_applications() { local freed_display="${total_size_freed}KB" fi - echo -e " ${GREEN}✓${NC} Freed $freed_display of disk space" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Freed $freed_display of disk space" fi - echo -e " ${GREEN}✓${NC} Applications uninstalled: $files_cleaned" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Applications uninstalled: $files_cleaned" ((total_size_cleaned += total_size_freed)) } @@ -632,7 +632,7 @@ main() { local extra=$((selection_count-3)) local list="${names[*]}" [[ $extra -gt 0 ]] && list+=" +${extra}" - echo -e "${BLUE}◎${NC} ${selection_count} apps: ${list}" + echo -e "${BLUE}${ICON_CONFIRM}${NC} ${selection_count} apps: ${list}" # Execute batch uninstallation (handles confirmation) batch_uninstall_applications diff --git a/install.sh b/install.sh index 9c7a439..2c72484 100755 --- a/install.sh +++ b/install.sh @@ -29,7 +29,7 @@ VERBOSE=1 readonly ICON_SUCCESS="✓" readonly ICON_ADMIN="●" readonly ICON_CONFIRM="◎" -readonly ICON_ERROR="✗" +readonly ICON_ERROR="☻" # Logging functions log_info() { [[ ${VERBOSE} -eq 1 ]] && echo -e "${BLUE}$1${NC}"; } diff --git a/lib/batch_uninstall.sh b/lib/batch_uninstall.sh index e81026f..b4cc0f3 100755 --- a/lib/batch_uninstall.sh +++ b/lib/batch_uninstall.sh @@ -68,7 +68,7 @@ batch_uninstall_applications() { local app_size_display=$(bytes_to_human "$((total_kb * 1024))") echo -e "${BLUE}${ICON_CONFIRM}${NC} ${app_name} ${GRAY}(${app_size_display})${NC}" - echo -e " ${GREEN}✓${NC} $(echo "$app_path" | sed "s|$HOME|~|")" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} $(echo "$app_path" | sed "s|$HOME|~|")" # Show related files (limit to 5 most important ones for brevity) local file_count=0 @@ -76,7 +76,7 @@ batch_uninstall_applications() { while IFS= read -r file; do if [[ -n "$file" && -e "$file" ]]; then if [[ $file_count -lt $max_files ]]; then - echo -e " ${GREEN}✓${NC} $(echo "$file" | sed "s|$HOME|~|")" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} $(echo "$file" | sed "s|$HOME|~|")" fi ((file_count++)) fi @@ -100,7 +100,7 @@ batch_uninstall_applications() { if [[ ${#running_apps[@]} -gt 0 ]]; then removal_note+=" - will force quit: ${running_apps[*]}" fi - echo -ne "${PURPLE}☛${NC} ${removal_note}. Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: " + echo -ne "${PURPLE}${ICON_ARROW}${NC} ${removal_note}. Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: " IFS= read -r -s -n1 key || key="" case "$key" in diff --git a/lib/common.sh b/lib/common.sh index 360daf6..340a4da 100755 --- a/lib/common.sh +++ b/lib/common.sh @@ -20,16 +20,20 @@ readonly RED="${ESC}[0;31m" readonly GRAY="${ESC}[0;90m" readonly NC="${ESC}[0m" -# Icon definitions -readonly ICON_CONFIRM="◎" # Confirm operation -readonly ICON_ADMIN="⚙" # Admin permission -readonly ICON_SUCCESS="✓" # Success -readonly ICON_ERROR="✗" # Error -readonly ICON_EMPTY="○" # Empty state -readonly ICON_LIST="-" # List item -readonly ICON_MENU="▸" # Menu item -readonly ICON_SYSTEM="❤︎" # System/Architecture info -readonly ICON_SETTINGS="⚙" # Settings/Configuration +# Icon definitions (shared across modules) +readonly ICON_CONFIRM="◎" # Confirm operation / spinner text +readonly ICON_ADMIN="⚙" # Gear indicator for admin/settings/system info +readonly ICON_SUCCESS="✓" # Success mark +readonly ICON_ERROR="☻" # Error / warning mark +readonly ICON_EMPTY="○" # Hollow circle (empty state / unchecked) +readonly ICON_SOLID="●" # Solid circle (selected / system marker) +readonly ICON_LIST="•" # Basic list bullet +readonly ICON_ARROW="➤" # Pointer / prompt indicator +readonly ICON_WARNING="☻" # Warning marker (shares glyph with error) +readonly ICON_NAV_UP="↑" # Navigation up +readonly ICON_NAV_DOWN="↓" # Navigation down +readonly ICON_NAV_LEFT="←" # Navigation left +readonly ICON_NAV_RIGHT="→" # Navigation right # Spinner character helpers (ASCII by default, overridable via env) mo_spinner_chars() { @@ -81,7 +85,7 @@ log_error() { log_header() { rotate_log - echo -e "\n${PURPLE}${ICON_MENU} $1${NC}" + echo -e "\n${PURPLE}${ICON_ARROW} $1${NC}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] SECTION: $1" >> "$LOG_FILE" 2>/dev/null || true } @@ -113,7 +117,7 @@ icon_list() { icon_menu() { local num="$1" local text="$2" - echo -e "${BLUE}${ICON_MENU} ${num}. ${text}${NC}" + echo -e "${BLUE}${ICON_ARROW} ${num}. ${text}${NC}" } # Consistent summary blocks for command results @@ -267,7 +271,7 @@ show_menu_option() { local selected="$3" if [[ "$selected" == "true" ]]; then - echo -e "${BLUE}▶ $number. $text${NC}" + echo -e "${BLUE}${ICON_ARROW} $number. $text${NC}" else echo " $number. $text" fi @@ -376,7 +380,7 @@ request_sudo_access() { # If Touch ID is supported and not forced to use password if [[ "$force_password" != "true" ]] && check_touchid_support; then - echo -e "${GRAY}${ICON_ADMIN}${NC} ${GRAY}${prompt_msg} (Touch ID or password)${NC}" + echo -e "${PURPLE}${ICON_ARROW}${NC} ${prompt_msg} ${GRAY}(Touch ID or password)${NC}" if sudo -v 2>/dev/null; then return 0 else @@ -384,8 +388,8 @@ request_sudo_access() { fi else # Traditional password method - echo -e "${GRAY}${ICON_ADMIN}${NC} ${GRAY}${prompt_msg}${NC}" - echo -ne "${GRAY}${ICON_MENU}${NC} Password: " + echo -e "${PURPLE}${ICON_ARROW}${NC} ${prompt_msg}" + echo -ne "${PURPLE}${ICON_ARROW}${NC} Password: " read -s password echo "" if [[ -n "$password" ]] && echo "$password" | sudo -S true 2>/dev/null; then @@ -439,7 +443,7 @@ update_via_homebrew() { # Get current version local current_version current_version=$(brew list --versions mole 2>/dev/null | awk '{print $2}') - echo -e "${GREEN}✓${NC} Already on latest version (${current_version:-$version})" + echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version (${current_version:-$version})" elif echo "$upgrade_output" | grep -q "Error:"; then log_error "Homebrew upgrade failed" echo "$upgrade_output" | grep "Error:" >&2 @@ -450,7 +454,7 @@ update_via_homebrew() { # Get new version local new_version new_version=$(brew list --versions mole 2>/dev/null | awk '{print $2}') - echo -e "${GREEN}✓${NC} Updated to latest version (${new_version:-$version})" + echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-$version})" fi # Clear version check cache @@ -555,9 +559,9 @@ stop_spinner() { if [[ -n "$result_message" ]]; then if [[ -t 1 ]]; then - printf "\r${MOLE_SPINNER_PREFIX:-}${GREEN}✓${NC} %s\n" "$result_message" + printf "\r${MOLE_SPINNER_PREFIX:-}${GREEN}${ICON_SUCCESS}${NC} %s\n" "$result_message" else - echo " ✓ $result_message" + echo " ${ICON_SUCCESS} $result_message" fi fi } @@ -687,7 +691,7 @@ with_spinner() { local exit_code=$? if [[ -t 1 ]]; then stop_inline_spinner; fi # Exit code 124 means timeout - [[ $exit_code -eq 124 ]] && echo -e " ${YELLOW}⚠${NC} $msg timed out (skipped)" >&2 + [[ $exit_code -eq 124 ]] && echo -e " ${YELLOW}${ICON_WARNING}${NC} $msg timed out (skipped)" >&2 return $exit_code } else @@ -700,7 +704,7 @@ with_spinner() { kill -TERM $cmd_pid 2>/dev/null || true wait $cmd_pid 2>/dev/null || true if [[ -t 1 ]]; then stop_inline_spinner; fi - echo -e " ${YELLOW}⚠${NC} $msg timed out (skipped)" >&2 + echo -e " ${YELLOW}${ICON_WARNING}${NC} $msg timed out (skipped)" >&2 return 124 fi sleep 1 @@ -729,12 +733,12 @@ clean_tool_cache() { return 0 fi if MOLE_SPINNER_PREFIX=" " with_spinner "$label" "$@"; then - echo -e " ${GREEN}✓${NC} $label" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label" else local exit_code=$? # Timeout returns 124, don't show error message (already shown by with_spinner) if [[ $exit_code -ne 124 ]]; then - echo -e " ${YELLOW}⚠${NC} $label failed (skipped)" >&2 + echo -e " ${YELLOW}${ICON_WARNING}${NC} $label failed (skipped)" >&2 fi fi return 0 # Always return success to continue cleanup @@ -752,7 +756,7 @@ prompt_action() { local cancel="${2:-cancel}" echo "" - echo -ne "${PURPLE}☛${NC} Press ${GREEN}Enter${NC} to ${action}, ${GRAY}ESC${NC} to ${cancel}: " + echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to ${action}, ${GRAY}ESC${NC} to ${cancel}: " IFS= read -r -s -n1 key || key="" case "$key" in @@ -1024,13 +1028,13 @@ start_section() { TRACK_SECTION=1 SECTION_ACTIVITY=0 echo "" - echo -e "${PURPLE}▶ $1${NC}" + echo -e "${PURPLE}${ICON_ARROW} $1${NC}" } # End a section (show "Nothing to tidy" if no activity) end_section() { if [[ $TRACK_SECTION -eq 1 && $SECTION_ACTIVITY -eq 0 ]]; then - echo -e " ${BLUE}○${NC} Nothing to tidy" + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Nothing to tidy" fi TRACK_SECTION=0 } diff --git a/lib/paginated_menu.sh b/lib/paginated_menu.sh index be5d53f..112e864 100755 --- a/lib/paginated_menu.sh +++ b/lib/paginated_menu.sh @@ -94,11 +94,11 @@ paginated_multi_select() { render_item() { local idx=$1 is_current=$2 - local checkbox="○" - [[ ${selected[idx]} == true ]] && checkbox="●" + local checkbox="$ICON_EMPTY" + [[ ${selected[idx]} == true ]] && checkbox="$ICON_SOLID" if [[ $is_current == true ]]; then - printf "\r\033[2K${BLUE}> %s %s${NC}\n" "$checkbox" "${items[idx]}" >&2 + printf "\r\033[2K${BLUE}${ICON_ARROW} %s %s${NC}\n" "$checkbox" "${items[idx]}" >&2 else printf "\r\033[2K %s %s\n" "$checkbox" "${items[idx]}" >&2 fi @@ -168,7 +168,7 @@ paginated_multi_select() { # Clear any remaining lines at bottom printf "${clear_line}\n" >&2 - printf "${clear_line}${GRAY}↑/↓${NC} Navigate ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}Q/ESC${NC} Quit\n" >&2 + printf "${clear_line}${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Navigate ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}Q/ESC${NC} Quit\n" >&2 # Clear one more line to ensure no artifacts printf "${clear_line}" >&2 @@ -177,14 +177,14 @@ paginated_multi_select() { # Show help screen show_help() { printf "\033[H\033[J" >&2 - cat >&2 << 'EOF' + cat >&2 </dev/null - # Skip if checked within 3 hours + # Skip if checked recently if [[ -f "$cache" ]]; then local age=$(($(date +%s) - $(stat -f%m "$cache" 2>/dev/null || echo 0))) - [[ $age -lt 10800 ]] && return + [[ $age -lt $ttl ]] && return fi # Background version check (save to file, don't output) @@ -49,7 +50,7 @@ check_for_updates() { local latest=$(get_latest_version) if [[ -n "$latest" && "$VERSION" != "$latest" && "$(printf '%s\n' "$VERSION" "$latest" | sort -V | head -1)" == "$VERSION" ]]; then - echo -e "${YELLOW}New version ${GREEN}${latest}${YELLOW} available (current: ${VERSION})\n${YELLOW}Run ${GREEN}mo update${YELLOW} to upgrade${NC}" > "$msg_cache" + printf "\nUpdate available: %s → %s, run %smo update%s\n\n" "$VERSION" "$latest" "$GREEN" "$NC" > "$msg_cache" else echo -n > "$msg_cache" fi @@ -163,7 +164,7 @@ update_mole() { if [[ "$VERSION" == "$latest" ]]; then echo "" - echo -e "${GREEN}✓${NC} Already on latest version (${VERSION})" + echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version (${VERSION})" echo "" exit 0 fi @@ -229,7 +230,7 @@ update_mole() { if ! printf '%s\n' "$install_output" | grep -Eq "Updated to latest version|Already on latest version"; then local new_version new_version=$("$mole_path" --version 2>/dev/null | awk 'NF {print $NF}' || echo "") - printf '\n%s\n\n' "${GREEN}✓${NC} Updated to latest version (${new_version:-unknown})" + printf '\n%s\n\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})" else printf '\n' fi @@ -245,7 +246,7 @@ update_mole() { if ! printf '%s\n' "$install_output" | grep -Eq "Updated to latest version|Already on latest version"; then local new_version new_version=$("$mole_path" --version 2>/dev/null | awk 'NF {print $NF}' || echo "") - printf '\n%s\n\n' "${GREEN}✓${NC} Updated to latest version (${new_version:-unknown})" + printf '\n%s\n\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})" else printf '\n' fi @@ -330,7 +331,7 @@ remove_mole() { done echo " - ~/.config/mole" echo " - ~/.cache/mole" - echo -ne "${PURPLE}☛${NC} Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: " + echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: " # Read single key IFS= read -r -s -n1 key || key="" @@ -385,9 +386,9 @@ remove_mole() { # Show final result local final_message if [[ "$has_error" == "true" ]]; then - final_message="${YELLOW}⚠ Mole uninstalled with some errors, thank you for using Mole!${NC}" + final_message="${YELLOW}${ICON_ERROR} Mole uninstalled with some errors, thank you for using Mole!${NC}" else - final_message="${GREEN}✓ Mole uninstalled successfully, thank you for using Mole!${NC}" + final_message="${GREEN}${ICON_SUCCESS} Mole uninstalled successfully, thank you for using Mole!${NC}" fi printf '\n%s\n\n' "$final_message"