mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 15:39:42 +00:00
feat: add spinner feedback to optimization tasks, enhance app discovery for uninstall, and improve UI robustness and signal handling
This commit is contained in:
@@ -16,13 +16,20 @@ source "$SCRIPT_DIR/lib/manage/autofix.sh"
|
|||||||
source "$SCRIPT_DIR/lib/check/all.sh"
|
source "$SCRIPT_DIR/lib/check/all.sh"
|
||||||
|
|
||||||
cleanup_all() {
|
cleanup_all() {
|
||||||
|
stop_inline_spinner 2> /dev/null || true
|
||||||
stop_sudo_session
|
stop_sudo_session
|
||||||
cleanup_temp_files
|
cleanup_temp_files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_interrupt() {
|
||||||
|
cleanup_all
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
# Register unified cleanup handler
|
# Register unified cleanup handler
|
||||||
trap cleanup_all EXIT INT TERM
|
trap cleanup_all EXIT
|
||||||
|
trap handle_interrupt INT TERM
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
clear
|
clear
|
||||||
|
|||||||
@@ -319,10 +319,16 @@ perform_security_fixes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup_all() {
|
cleanup_all() {
|
||||||
|
stop_inline_spinner 2> /dev/null || true
|
||||||
stop_sudo_session
|
stop_sudo_session
|
||||||
cleanup_temp_files
|
cleanup_temp_files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_interrupt() {
|
||||||
|
cleanup_all
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local health_json
|
local health_json
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
@@ -340,7 +346,8 @@ main() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
trap cleanup_all EXIT INT TERM
|
trap cleanup_all EXIT
|
||||||
|
trap handle_interrupt INT TERM
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
clear
|
clear
|
||||||
|
|||||||
@@ -77,6 +77,23 @@ scan_applications() {
|
|||||||
"/Applications"
|
"/Applications"
|
||||||
"$HOME/Applications"
|
"$HOME/Applications"
|
||||||
)
|
)
|
||||||
|
local vol_app_dir
|
||||||
|
local nullglob_was_set=0
|
||||||
|
shopt -q nullglob && nullglob_was_set=1
|
||||||
|
shopt -s nullglob
|
||||||
|
for vol_app_dir in /Volumes/*/Applications; do
|
||||||
|
[[ -d "$vol_app_dir" && -r "$vol_app_dir" ]] || continue
|
||||||
|
if [[ -d "/Applications" && "$vol_app_dir" -ef "/Applications" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ -d "$HOME/Applications" && "$vol_app_dir" -ef "$HOME/Applications" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
app_dirs+=("$vol_app_dir")
|
||||||
|
done
|
||||||
|
if [[ $nullglob_was_set -eq 0 ]]; then
|
||||||
|
shopt -u nullglob
|
||||||
|
fi
|
||||||
|
|
||||||
for app_dir in "${app_dirs[@]}"; do
|
for app_dir in "${app_dirs[@]}"; do
|
||||||
if [[ ! -d "$app_dir" ]]; then continue; fi
|
if [[ ! -d "$app_dir" ]]; then continue; fi
|
||||||
|
|||||||
@@ -111,6 +111,31 @@ scan_applications() {
|
|||||||
|
|
||||||
# First pass: quickly collect all valid app paths and bundle IDs (NO mdls calls)
|
# First pass: quickly collect all valid app paths and bundle IDs (NO mdls calls)
|
||||||
local -a app_data_tuples=()
|
local -a app_data_tuples=()
|
||||||
|
local -a app_dirs=(
|
||||||
|
"/Applications"
|
||||||
|
"$HOME/Applications"
|
||||||
|
)
|
||||||
|
local vol_app_dir
|
||||||
|
local nullglob_was_set=0
|
||||||
|
shopt -q nullglob && nullglob_was_set=1
|
||||||
|
shopt -s nullglob
|
||||||
|
for vol_app_dir in /Volumes/*/Applications; do
|
||||||
|
[[ -d "$vol_app_dir" && -r "$vol_app_dir" ]] || continue
|
||||||
|
if [[ -d "/Applications" && "$vol_app_dir" -ef "/Applications" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ -d "$HOME/Applications" && "$vol_app_dir" -ef "$HOME/Applications" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
app_dirs+=("$vol_app_dir")
|
||||||
|
done
|
||||||
|
if [[ $nullglob_was_set -eq 0 ]]; then
|
||||||
|
shopt -u nullglob
|
||||||
|
fi
|
||||||
|
|
||||||
|
for app_dir in "${app_dirs[@]}"; do
|
||||||
|
if [[ ! -d "$app_dir" ]]; then continue; fi
|
||||||
|
|
||||||
while IFS= read -r -d '' app_path; do
|
while IFS= read -r -d '' app_path; do
|
||||||
if [[ ! -e "$app_path" ]]; then continue; fi
|
if [[ ! -e "$app_path" ]]; then continue; fi
|
||||||
|
|
||||||
@@ -139,12 +164,8 @@ scan_applications() {
|
|||||||
|
|
||||||
# Store tuple: app_path|app_name|bundle_id (display_name will be resolved in parallel later)
|
# Store tuple: app_path|app_name|bundle_id (display_name will be resolved in parallel later)
|
||||||
app_data_tuples+=("${app_path}|${app_name}|${bundle_id}")
|
app_data_tuples+=("${app_path}|${app_name}|${bundle_id}")
|
||||||
done < <(
|
done < <(command find "$app_dir" -name "*.app" -maxdepth 3 -print0 2> /dev/null)
|
||||||
# Scan both system and user application directories
|
done
|
||||||
# Using maxdepth 3 to find apps in subdirectories (e.g., Adobe apps in /Applications/Adobe X/)
|
|
||||||
command find /Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
|
||||||
command find ~/Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
|
||||||
)
|
|
||||||
|
|
||||||
# Second pass: process each app with parallel size calculation
|
# Second pass: process each app with parallel size calculation
|
||||||
local app_count=0
|
local app_count=0
|
||||||
|
|||||||
@@ -174,8 +174,18 @@ opt_saved_state_cleanup() {
|
|||||||
# Removed: opt_local_snapshots - Deletes user Time Machine recovery points, breaks backup continuity
|
# Removed: opt_local_snapshots - Deletes user Time Machine recovery points, breaks backup continuity
|
||||||
|
|
||||||
opt_fix_broken_configs() {
|
opt_fix_broken_configs() {
|
||||||
|
local spinner_started="false"
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Checking preferences..."
|
||||||
|
spinner_started="true"
|
||||||
|
fi
|
||||||
|
|
||||||
local broken_prefs=$(fix_broken_preferences)
|
local broken_prefs=$(fix_broken_preferences)
|
||||||
|
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $broken_prefs -gt 0 ]]; then
|
if [[ $broken_prefs -gt 0 ]]; then
|
||||||
opt_msg "Repaired $broken_prefs corrupted preference files"
|
opt_msg "Repaired $broken_prefs corrupted preference files"
|
||||||
else
|
else
|
||||||
@@ -324,7 +334,7 @@ opt_sqlite_vacuum() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $skipped -gt 0 ]]; then
|
if [[ $skipped -gt 0 ]]; then
|
||||||
echo -e " ${GRAY}Already optimal for $skipped databases (size or integrity limits)${NC}"
|
opt_msg "Already optimal for $skipped databases"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $timed_out -gt 0 ]]; then
|
if [[ $timed_out -gt 0 ]]; then
|
||||||
@@ -520,8 +530,17 @@ opt_disk_permissions_repair() {
|
|||||||
# 1. Checks if default audio output is Bluetooth (precise)
|
# 1. Checks if default audio output is Bluetooth (precise)
|
||||||
# 2. Falls back to Bluetooth + media app detection (compatibility)
|
# 2. Falls back to Bluetooth + media app detection (compatibility)
|
||||||
opt_bluetooth_reset() {
|
opt_bluetooth_reset() {
|
||||||
|
local spinner_started="false"
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Checking Bluetooth..."
|
||||||
|
spinner_started="true"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
if has_bluetooth_hid_connected; then
|
if has_bluetooth_hid_connected; then
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
opt_msg "Bluetooth already optimal"
|
opt_msg "Bluetooth already optimal"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -557,6 +576,9 @@ opt_bluetooth_reset() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$bt_audio_active" == "true" ]]; then
|
if [[ "$bt_audio_active" == "true" ]]; then
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
opt_msg "Bluetooth already optimal"
|
opt_msg "Bluetooth already optimal"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -567,12 +589,21 @@ opt_bluetooth_reset() {
|
|||||||
if pgrep -x bluetoothd > /dev/null 2>&1; then
|
if pgrep -x bluetoothd > /dev/null 2>&1; then
|
||||||
sudo pkill -KILL bluetoothd > /dev/null 2>&1 || true
|
sudo pkill -KILL bluetoothd > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
opt_msg "Bluetooth module restarted"
|
opt_msg "Bluetooth module restarted"
|
||||||
opt_msg "Connectivity issues resolved"
|
opt_msg "Connectivity issues resolved"
|
||||||
else
|
else
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
opt_msg "Bluetooth already optimal"
|
opt_msg "Bluetooth already optimal"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
if [[ "$spinner_started" == "true" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
opt_msg "Bluetooth module restarted"
|
opt_msg "Bluetooth module restarted"
|
||||||
opt_msg "Connectivity issues resolved"
|
opt_msg "Connectivity issues resolved"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -235,11 +235,13 @@ paginated_multi_select() {
|
|||||||
|
|
||||||
local cols="${COLUMNS:-}"
|
local cols="${COLUMNS:-}"
|
||||||
[[ -z "$cols" ]] && cols=$(tput cols 2> /dev/null || echo 80)
|
[[ -z "$cols" ]] && cols=$(tput cols 2> /dev/null || echo 80)
|
||||||
|
[[ "$cols" =~ ^[0-9]+$ ]] || cols=80
|
||||||
|
|
||||||
_strip_ansi_len() {
|
_strip_ansi_len() {
|
||||||
local text="$1"
|
local text="$1"
|
||||||
local stripped
|
local stripped
|
||||||
stripped=$(printf "%s" "$text" | LC_ALL=C awk '{gsub(/\033\[[0-9;]*[A-Za-z]/,""); print}')
|
stripped=$(printf "%s" "$text" | LC_ALL=C awk '{gsub(/\033\[[0-9;]*[A-Za-z]/,""); print}' || true)
|
||||||
|
[[ -z "$stripped" ]] && stripped="$text"
|
||||||
printf "%d" "${#stripped}"
|
printf "%d" "${#stripped}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +253,10 @@ paginated_multi_select() {
|
|||||||
else
|
else
|
||||||
candidate="$line${sep}${s}"
|
candidate="$line${sep}${s}"
|
||||||
fi
|
fi
|
||||||
if (($(_strip_ansi_len "$candidate") > cols)); then
|
local candidate_len
|
||||||
|
candidate_len=$(_strip_ansi_len "$candidate")
|
||||||
|
[[ -z "$candidate_len" ]] && candidate_len=0
|
||||||
|
if ((candidate_len > cols)); then
|
||||||
printf "%s%s\n" "$clear_line" "$line" >&2
|
printf "%s%s\n" "$clear_line" "$line" >&2
|
||||||
line="$s"
|
line="$s"
|
||||||
else
|
else
|
||||||
@@ -526,6 +531,7 @@ paginated_multi_select() {
|
|||||||
# Normal: show full controls with dynamic reduction
|
# Normal: show full controls with dynamic reduction
|
||||||
local term_width="${COLUMNS:-}"
|
local term_width="${COLUMNS:-}"
|
||||||
[[ -z "$term_width" ]] && term_width=$(tput cols 2> /dev/null || echo 80)
|
[[ -z "$term_width" ]] && term_width=$(tput cols 2> /dev/null || echo 80)
|
||||||
|
[[ "$term_width" =~ ^[0-9]+$ ]] || term_width=80
|
||||||
|
|
||||||
# Level 0: Full controls
|
# Level 0: Full controls
|
||||||
local -a _segs=("$nav" "$space_select" "$enter" "$refresh" "$search" "$sort_ctrl" "$order_ctrl" "$exit")
|
local -a _segs=("$nav" "$space_select" "$enter" "$refresh" "$search" "$sort_ctrl" "$order_ctrl" "$exit")
|
||||||
|
|||||||
Reference in New Issue
Block a user