mirror of
https://github.com/tw93/Mole.git
synced 2026-02-16 22:49:12 +00:00
Fix search problems and best practices
This commit is contained in:
@@ -456,7 +456,10 @@ safe_clean() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_cleanup() {
|
start_cleanup() {
|
||||||
clear
|
if [[ -t 1 ]]; then
|
||||||
|
# Avoid relying on TERM since CI often runs without it
|
||||||
|
printf '\033[2J\033[H'
|
||||||
|
fi
|
||||||
printf '\n'
|
printf '\n'
|
||||||
echo -e "${PURPLE_BOLD}Clean Your Mac${NC}"
|
echo -e "${PURPLE_BOLD}Clean Your Mac${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -73,11 +73,12 @@ scan_applications() {
|
|||||||
local cache_dir="$HOME/.cache/mole"
|
local cache_dir="$HOME/.cache/mole"
|
||||||
local cache_file="$cache_dir/app_scan_cache"
|
local cache_file="$cache_dir/app_scan_cache"
|
||||||
local cache_ttl=86400 # 24 hours
|
local cache_ttl=86400 # 24 hours
|
||||||
|
local force_rescan="${1:-false}"
|
||||||
|
|
||||||
mkdir -p "$cache_dir" 2> /dev/null
|
mkdir -p "$cache_dir" 2> /dev/null
|
||||||
|
|
||||||
# Check if cache exists and is fresh
|
# Check if cache exists and is fresh
|
||||||
if [[ -f "$cache_file" ]]; then
|
if [[ $force_rescan == false && -f "$cache_file" ]]; then
|
||||||
local cache_age=$(($(date +%s) - $(get_file_mtime "$cache_file")))
|
local cache_age=$(($(date +%s) - $(get_file_mtime "$cache_file")))
|
||||||
[[ $cache_age -eq $(date +%s) ]] && cache_age=86401 # Handle missing file
|
[[ $cache_age -eq $(date +%s) ]] && cache_age=86401 # Handle missing file
|
||||||
if [[ $cache_age -lt $cache_ttl ]]; then
|
if [[ $cache_age -lt $cache_ttl ]]; then
|
||||||
@@ -405,11 +406,15 @@ trap cleanup EXIT INT TERM
|
|||||||
# Main function
|
# Main function
|
||||||
main() {
|
main() {
|
||||||
# Parse args
|
# Parse args
|
||||||
|
local force_rescan=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
"--debug")
|
"--debug")
|
||||||
export MO_DEBUG=1
|
export MO_DEBUG=1
|
||||||
;;
|
;;
|
||||||
|
"--force-rescan")
|
||||||
|
force_rescan=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -425,7 +430,7 @@ main() {
|
|||||||
# (scan_applications handles cache internally)
|
# (scan_applications handles cache internally)
|
||||||
local needs_scanning=true
|
local needs_scanning=true
|
||||||
local cache_file="$HOME/.cache/mole/app_scan_cache"
|
local cache_file="$HOME/.cache/mole/app_scan_cache"
|
||||||
if [[ -f "$cache_file" ]]; then
|
if [[ $force_rescan == false && -f "$cache_file" ]]; then
|
||||||
local cache_age=$(($(date +%s) - $(get_file_mtime "$cache_file")))
|
local cache_age=$(($(date +%s) - $(get_file_mtime "$cache_file")))
|
||||||
[[ $cache_age -eq $(date +%s) ]] && cache_age=86401 # Handle missing file
|
[[ $cache_age -eq $(date +%s) ]] && cache_age=86401 # Handle missing file
|
||||||
[[ $cache_age -lt 86400 ]] && needs_scanning=false
|
[[ $cache_age -lt 86400 ]] && needs_scanning=false
|
||||||
@@ -444,7 +449,7 @@ main() {
|
|||||||
|
|
||||||
# Scan applications
|
# Scan applications
|
||||||
local apps_file=""
|
local apps_file=""
|
||||||
if ! apps_file=$(scan_applications); then
|
if ! apps_file=$(scan_applications "$force_rescan"); then
|
||||||
if [[ "${MOLE_ALT_SCREEN_ACTIVE:-}" == "1" ]]; then
|
if [[ "${MOLE_ALT_SCREEN_ACTIVE:-}" == "1" ]]; then
|
||||||
printf "\033[2J\033[H" >&2
|
printf "\033[2J\033[H" >&2
|
||||||
leave_alt_screen
|
leave_alt_screen
|
||||||
|
|||||||
@@ -73,3 +73,93 @@ update_via_homebrew() {
|
|||||||
# Clear update cache
|
# Clear update cache
|
||||||
rm -f "$HOME/.cache/mole/version_check" "$HOME/.cache/mole/update_message" 2> /dev/null || true
|
rm -f "$HOME/.cache/mole/version_check" "$HOME/.cache/mole/update_message" 2> /dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Remove apps from Dock
|
||||||
|
# Args: app paths to remove
|
||||||
|
remove_apps_from_dock() {
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local plist="$HOME/Library/Preferences/com.apple.dock.plist"
|
||||||
|
[[ -f "$plist" ]] || return 0
|
||||||
|
|
||||||
|
if ! command -v python3 > /dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute Python helper to prune dock entries for the given app paths
|
||||||
|
python3 - "$@" << 'PY' 2>/dev/null || return 0
|
||||||
|
import os
|
||||||
|
import plistlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
plist_path = os.path.expanduser('~/Library/Preferences/com.apple.dock.plist')
|
||||||
|
if not os.path.exists(plist_path):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def normalise(path):
|
||||||
|
if not path:
|
||||||
|
return ''
|
||||||
|
return os.path.normpath(os.path.realpath(path.rstrip('/')))
|
||||||
|
|
||||||
|
targets = {normalise(arg) for arg in sys.argv[1:] if arg}
|
||||||
|
targets = {t for t in targets if t}
|
||||||
|
if not targets:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
with open(plist_path, 'rb') as fh:
|
||||||
|
try:
|
||||||
|
data = plistlib.load(fh)
|
||||||
|
except Exception:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
apps = data.get('persistent-apps')
|
||||||
|
if not isinstance(apps, list):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
filtered = []
|
||||||
|
for item in apps:
|
||||||
|
try:
|
||||||
|
url = item['tile-data']['file-data']['_CFURLString']
|
||||||
|
except (KeyError, TypeError):
|
||||||
|
filtered.append(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not isinstance(url, str):
|
||||||
|
filtered.append(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
parsed = urllib.parse.urlparse(url)
|
||||||
|
path = urllib.parse.unquote(parsed.path or '')
|
||||||
|
if not path:
|
||||||
|
filtered.append(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
candidate = normalise(path)
|
||||||
|
if any(candidate == t or candidate.startswith(t + os.sep) for t in targets):
|
||||||
|
changed = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
filtered.append(item)
|
||||||
|
|
||||||
|
if not changed:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
data['persistent-apps'] = filtered
|
||||||
|
with open(plist_path, 'wb') as fh:
|
||||||
|
try:
|
||||||
|
plistlib.dump(data, fh, fmt=plistlib.FMT_BINARY)
|
||||||
|
except Exception:
|
||||||
|
plistlib.dump(data, fh)
|
||||||
|
|
||||||
|
# Restart Dock to apply changes
|
||||||
|
try:
|
||||||
|
subprocess.run(['killall', 'Dock'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ read_key() {
|
|||||||
case "$key" in
|
case "$key" in
|
||||||
$'\n' | $'\r') echo "ENTER" ;;
|
$'\n' | $'\r') echo "ENTER" ;;
|
||||||
' ') echo "SPACE" ;;
|
' ') echo "SPACE" ;;
|
||||||
|
'/') echo "FILTER" ;;
|
||||||
|
'q' | 'Q') echo "QUIT" ;;
|
||||||
|
'R') echo "RETRY" ;;
|
||||||
$'\x03') echo "QUIT" ;;
|
$'\x03') echo "QUIT" ;;
|
||||||
$'\x7f' | $'\x08') echo "DELETE" ;;
|
$'\x7f' | $'\x08') echo "DELETE" ;;
|
||||||
$'\x1b')
|
$'\x1b')
|
||||||
|
|||||||
@@ -11,16 +11,25 @@ source "$_MOLE_MANAGE_DIR/../ui/menu_simple.sh"
|
|||||||
|
|
||||||
# Config file paths
|
# Config file paths
|
||||||
readonly WHITELIST_CONFIG_CLEAN="$HOME/.config/mole/whitelist"
|
readonly WHITELIST_CONFIG_CLEAN="$HOME/.config/mole/whitelist"
|
||||||
readonly WHITELIST_CONFIG_OPTIMIZE="$HOME/.config/mole/whitelist_checks"
|
readonly WHITELIST_CONFIG_OPTIMIZE="$HOME/.config/mole/whitelist_optimize"
|
||||||
|
readonly WHITELIST_CONFIG_OPTIMIZE_LEGACY="$HOME/.config/mole/whitelist_checks"
|
||||||
|
|
||||||
# Default whitelist patterns defined in lib/core/common.sh:
|
# Default whitelist patterns defined in lib/core/common.sh:
|
||||||
# - DEFAULT_WHITELIST_PATTERNS
|
# - DEFAULT_WHITELIST_PATTERNS
|
||||||
# - FINDER_METADATA_SENTINEL
|
# - FINDER_METADATA_SENTINEL
|
||||||
|
|
||||||
# Save whitelist patterns to config
|
# Save whitelist patterns to config (defaults to "clean" for legacy callers)
|
||||||
save_whitelist_patterns() {
|
save_whitelist_patterns() {
|
||||||
local mode="$1"
|
local mode="clean"
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
case "$1" in
|
||||||
|
clean | optimize)
|
||||||
|
mode="$1"
|
||||||
shift
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
local -a patterns
|
local -a patterns
|
||||||
patterns=("$@")
|
patterns=("$@")
|
||||||
|
|
||||||
@@ -170,13 +179,21 @@ load_whitelist() {
|
|||||||
local mode="${1:-clean}"
|
local mode="${1:-clean}"
|
||||||
local -a patterns=()
|
local -a patterns=()
|
||||||
local config_file
|
local config_file
|
||||||
|
local legacy_file=""
|
||||||
|
|
||||||
if [[ "$mode" == "optimize" ]]; then
|
if [[ "$mode" == "optimize" ]]; then
|
||||||
config_file="$WHITELIST_CONFIG_OPTIMIZE"
|
config_file="$WHITELIST_CONFIG_OPTIMIZE"
|
||||||
|
legacy_file="$WHITELIST_CONFIG_OPTIMIZE_LEGACY"
|
||||||
else
|
else
|
||||||
config_file="$WHITELIST_CONFIG_CLEAN"
|
config_file="$WHITELIST_CONFIG_CLEAN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local using_legacy="false"
|
||||||
|
if [[ ! -f "$config_file" && -n "$legacy_file" && -f "$legacy_file" ]]; then
|
||||||
|
config_file="$legacy_file"
|
||||||
|
using_legacy="true"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -f "$config_file" ]]; then
|
if [[ -f "$config_file" ]]; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
# shellcheck disable=SC2295
|
# shellcheck disable=SC2295
|
||||||
@@ -210,6 +227,11 @@ load_whitelist() {
|
|||||||
unique_patterns+=("$pattern")
|
unique_patterns+=("$pattern")
|
||||||
done
|
done
|
||||||
CURRENT_WHITELIST_PATTERNS=("${unique_patterns[@]}")
|
CURRENT_WHITELIST_PATTERNS=("${unique_patterns[@]}")
|
||||||
|
|
||||||
|
# Migrate legacy optimize config to the new path automatically
|
||||||
|
if [[ "$mode" == "optimize" && "$using_legacy" == "true" && "$config_file" != "$WHITELIST_CONFIG_OPTIMIZE" ]]; then
|
||||||
|
save_whitelist_patterns "$mode" "${CURRENT_WHITELIST_PATTERNS[@]}"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
CURRENT_WHITELIST_PATTERNS=()
|
CURRENT_WHITELIST_PATTERNS=()
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ paginated_multi_select() {
|
|||||||
for ((i = 0; i < items_per_page; i++)); do
|
for ((i = 0; i < items_per_page; i++)); do
|
||||||
printf "${clear_line}\n" >&2
|
printf "${clear_line}\n" >&2
|
||||||
done
|
done
|
||||||
printf "${clear_line}${GRAY}Type to filter | Delete | Enter | / Exit | ESC${NC}\n" >&2
|
printf "${clear_line}${GRAY}Type to filter | Delete | Enter Confirm | ESC Cancel${NC}\n" >&2
|
||||||
printf "${clear_line}" >&2
|
printf "${clear_line}" >&2
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
@@ -480,9 +480,8 @@ paginated_multi_select() {
|
|||||||
local -a _segs_filter=(
|
local -a _segs_filter=(
|
||||||
"${GRAY}Filter: ${filter_status}${NC}"
|
"${GRAY}Filter: ${filter_status}${NC}"
|
||||||
"${GRAY}Delete${NC}"
|
"${GRAY}Delete${NC}"
|
||||||
"${GRAY}Enter${NC}"
|
"${GRAY}Enter Confirm${NC}"
|
||||||
"${GRAY}/ Exit${NC}"
|
"${GRAY}ESC Cancel${NC}"
|
||||||
"${GRAY}ESC${NC}"
|
|
||||||
)
|
)
|
||||||
_print_wrapped_controls "$sep" "${_segs_filter[@]}"
|
_print_wrapped_controls "$sep" "${_segs_filter[@]}"
|
||||||
else
|
else
|
||||||
@@ -668,15 +667,8 @@ paginated_multi_select() {
|
|||||||
CHAR:*)
|
CHAR:*)
|
||||||
if [[ "$filter_mode" == "true" ]]; then
|
if [[ "$filter_mode" == "true" ]]; then
|
||||||
local ch="${key#CHAR:}"
|
local ch="${key#CHAR:}"
|
||||||
# Special handling for /: exit filter mode
|
|
||||||
if [[ "$ch" == "/" ]]; then
|
|
||||||
filter_mode="false"
|
|
||||||
unset MOLE_READ_KEY_FORCE_CHAR
|
|
||||||
filter_query=""
|
|
||||||
applied_query=""
|
|
||||||
rebuild_view
|
|
||||||
# avoid accidental leading spaces
|
# avoid accidental leading spaces
|
||||||
elif [[ -n "$filter_query" || "$ch" != " " ]]; then
|
if [[ -n "$filter_query" || "$ch" != " " ]]; then
|
||||||
filter_query+="$ch"
|
filter_query+="$ch"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ batch_uninstall_applications() {
|
|||||||
local -a sudo_apps=()
|
local -a sudo_apps=()
|
||||||
local total_estimated_size=0
|
local total_estimated_size=0
|
||||||
local -a app_details=()
|
local -a app_details=()
|
||||||
local -a dock_cleanup_paths=()
|
|
||||||
|
|
||||||
# Analyze selected apps with progress indicator
|
# Analyze selected apps with progress indicator
|
||||||
if [[ -t 1 ]]; then start_inline_spinner "Scanning files..."; fi
|
if [[ -t 1 ]]; then start_inline_spinner "Scanning files..."; fi
|
||||||
@@ -315,7 +314,6 @@ batch_uninstall_applications() {
|
|||||||
((files_cleaned++))
|
((files_cleaned++))
|
||||||
((total_items++))
|
((total_items++))
|
||||||
success_items+=("$app_name")
|
success_items+=("$app_name")
|
||||||
dock_cleanup_paths+=("$app_path")
|
|
||||||
else
|
else
|
||||||
((failed_count++))
|
((failed_count++))
|
||||||
failed_items+=("$app_name:$reason")
|
failed_items+=("$app_name:$reason")
|
||||||
@@ -406,8 +404,22 @@ batch_uninstall_applications() {
|
|||||||
print_summary_block "$summary_status" "Uninstall complete" "${summary_details[@]}"
|
print_summary_block "$summary_status" "Uninstall complete" "${summary_details[@]}"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|
||||||
if [[ ${#dock_cleanup_paths[@]} -gt 0 ]]; then
|
# Clean up Dock entries for uninstalled apps
|
||||||
remove_apps_from_dock "${dock_cleanup_paths[@]}"
|
if [[ $success_count -gt 0 ]]; then
|
||||||
|
local -a removed_paths=()
|
||||||
|
for detail in "${app_details[@]}"; do
|
||||||
|
IFS='|' read -r app_name app_path _ _ _ _ <<< "$detail"
|
||||||
|
# Check if this app was successfully removed
|
||||||
|
for success_name in "${success_items[@]}"; do
|
||||||
|
if [[ "$success_name" == "$app_name" ]]; then
|
||||||
|
removed_paths+=("$app_path")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
if [[ ${#removed_paths[@]} -gt 0 ]]; then
|
||||||
|
remove_apps_from_dock "${removed_paths[@]}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean up sudo keepalive if it was started
|
# Clean up sudo keepalive if it was started
|
||||||
|
|||||||
Reference in New Issue
Block a user