mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 19:09:43 +00:00
Uninstallation experience enhancement
This commit is contained in:
@@ -381,8 +381,11 @@ load_applications() {
|
||||
apps_data=()
|
||||
selection_state=()
|
||||
|
||||
# Read apps into array
|
||||
# Read apps into array, skip non-existent apps
|
||||
while IFS='|' read -r epoch app_path app_name bundle_id size last_used size_kb; do
|
||||
# Skip if app path no longer exists
|
||||
[[ ! -e "$app_path" ]] && continue
|
||||
|
||||
apps_data+=("$epoch|$app_path|$app_name|$bundle_id|$size|$last_used|${size_kb:-0}")
|
||||
selection_state+=(false)
|
||||
done < "$apps_file"
|
||||
|
||||
@@ -356,25 +356,25 @@ paginated_multi_select() {
|
||||
for ((i = 0; i < items_per_page + 2; i++)); do
|
||||
printf "${clear_line}\n" >&2
|
||||
done
|
||||
printf "${clear_line}${GRAY}Type to filter${NC} ${GRAY}|${NC} ${GRAY}Delete${NC} Backspace ${GRAY}|${NC} ${GRAY}Enter${NC} Apply ${GRAY}|${NC} ${GRAY}ESC${NC} Cancel\n" >&2
|
||||
printf "${clear_line}Type to filter | Delete | Enter | / Exit | ESC\n" >&2
|
||||
printf "${clear_line}" >&2
|
||||
return
|
||||
else
|
||||
if [[ "$searching" == "true" ]]; then
|
||||
printf "${clear_line}${GRAY}Searching…${NC}\n" >&2
|
||||
printf "${clear_line}Searching…\n" >&2
|
||||
for ((i = 0; i < items_per_page + 2; i++)); do
|
||||
printf "${clear_line}\n" >&2
|
||||
done
|
||||
printf "${clear_line}${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Nav ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}/${NC} Filter ${GRAY}|${NC} ${GRAY}S${NC} Sort ${GRAY}|${NC} ${GRAY}Q${NC} Quit\n" >&2
|
||||
printf "${clear_line}${ICON_NAV_UP}/${ICON_NAV_DOWN} | Space | Enter | / Filter | Q Exit\n" >&2
|
||||
printf "${clear_line}" >&2
|
||||
return
|
||||
else
|
||||
# Post-search: truly empty list
|
||||
printf "${clear_line}${GRAY}No items available${NC}\n" >&2
|
||||
printf "${clear_line}No items available\n" >&2
|
||||
for ((i = 0; i < items_per_page + 2; i++)); do
|
||||
printf "${clear_line}\n" >&2
|
||||
done
|
||||
printf "${clear_line}${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Nav ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}/${NC} Filter ${GRAY}|${NC} ${GRAY}S${NC} Sort ${GRAY}|${NC} ${GRAY}Q${NC} Quit\n" >&2
|
||||
printf "${clear_line}${ICON_NAV_UP}/${ICON_NAV_DOWN} | Space | Enter | / Filter | Q Exit\n" >&2
|
||||
printf "${clear_line}" >&2
|
||||
return
|
||||
fi
|
||||
@@ -419,56 +419,70 @@ paginated_multi_select() {
|
||||
name) sort_label="Name" ;;
|
||||
size) sort_label="Size" ;;
|
||||
esac
|
||||
local arrow="↑"
|
||||
[[ "$sort_reverse" == "true" ]] && arrow="↓"
|
||||
local sort_status="${sort_label} ${arrow}"
|
||||
local sort_status="${sort_label}"
|
||||
|
||||
local filter_status=""
|
||||
if [[ "$filter_mode" == "true" ]]; then
|
||||
filter_status="${YELLOW}${filter_query:-}${NC}"
|
||||
filter_status="${filter_query:-_}"
|
||||
elif [[ -n "$applied_query" ]]; then
|
||||
filter_status="${GREEN}${applied_query}${NC}"
|
||||
filter_status="${applied_query}"
|
||||
else
|
||||
filter_status="${GRAY}—${NC}"
|
||||
filter_status="—"
|
||||
fi
|
||||
|
||||
# Footer with two lines: basic controls and advanced options
|
||||
local sep=" ${GRAY}|${NC} "
|
||||
# Footer: single line with controls
|
||||
local sep=" | "
|
||||
if [[ "$filter_mode" == "true" ]]; then
|
||||
# Filter mode: single line with all filter controls
|
||||
# Filter mode: simple controls without sort
|
||||
local -a _segs_filter=(
|
||||
"${GRAY}Filter Input:${NC} ${filter_status}"
|
||||
"${GRAY}Delete${NC} Back"
|
||||
"${GRAY}Enter${NC} Apply"
|
||||
"${GRAY}/${NC} Clear"
|
||||
"${GRAY}ESC${NC} Cancel"
|
||||
"Filter: ${filter_status}"
|
||||
"Delete"
|
||||
"Enter"
|
||||
"/ Exit"
|
||||
"ESC"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_filter[@]}"
|
||||
else
|
||||
# Normal mode
|
||||
# Normal mode - single line compact format
|
||||
local reverse_arrow="↑"
|
||||
[[ "$sort_reverse" == "true" ]] && reverse_arrow="↓"
|
||||
|
||||
# Determine filter text based on whether filter is active
|
||||
local filter_text="/ Filter"
|
||||
[[ -n "$applied_query" ]] && filter_text="/ Clear"
|
||||
|
||||
if [[ "$has_metadata" == "true" ]]; then
|
||||
# With metadata: two lines (basic + advanced)
|
||||
local -a _segs_basic=(
|
||||
"${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Nav"
|
||||
"${GRAY}Space${NC} Select"
|
||||
"${GRAY}Enter${NC} Confirm"
|
||||
"${GRAY}Q${NC} Quit"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_basic[@]}"
|
||||
local -a _segs_advanced=(
|
||||
"${GRAY}S${NC} ${sort_status}"
|
||||
"${GRAY}R${NC} Reverse"
|
||||
"${GRAY}/${NC} Filter"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_advanced[@]}"
|
||||
if [[ -n "$applied_query" ]]; then
|
||||
# Filtering: hide sort controls
|
||||
local -a _segs_all=(
|
||||
"${ICON_NAV_UP}/${ICON_NAV_DOWN}"
|
||||
"Space"
|
||||
"Enter"
|
||||
"${filter_text}"
|
||||
"Q Exit"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_all[@]}"
|
||||
else
|
||||
# Normal: show full controls
|
||||
local -a _segs_all=(
|
||||
"${ICON_NAV_UP}/${ICON_NAV_DOWN}"
|
||||
"Space"
|
||||
"Enter"
|
||||
"${filter_text}"
|
||||
"S ${sort_status}"
|
||||
"R ${reverse_arrow}"
|
||||
"Q Exit"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_all[@]}"
|
||||
fi
|
||||
else
|
||||
# Without metadata: single line (basic only)
|
||||
# Without metadata: basic controls
|
||||
local -a _segs_simple=(
|
||||
"${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Nav"
|
||||
"${GRAY}Space${NC} Select"
|
||||
"${GRAY}Enter${NC} Confirm"
|
||||
"${GRAY}/${NC} Filter"
|
||||
"${GRAY}Q${NC} Quit"
|
||||
"${ICON_NAV_UP}/${ICON_NAV_DOWN}"
|
||||
"Space"
|
||||
"Enter"
|
||||
"${filter_text}"
|
||||
"Q Exit"
|
||||
)
|
||||
_print_wrapped_controls "$sep" "${_segs_simple[@]}"
|
||||
fi
|
||||
@@ -568,13 +582,23 @@ paginated_multi_select() {
|
||||
fi
|
||||
;;
|
||||
"FILTER")
|
||||
# Trigger filter mode with /
|
||||
filter_mode="true"
|
||||
export MOLE_READ_KEY_FORCE_CHAR=1
|
||||
filter_query=""
|
||||
top_index=0
|
||||
cursor_pos=0
|
||||
rebuild_view
|
||||
# / key: toggle between filter and return
|
||||
if [[ -n "$applied_query" ]]; then
|
||||
# Already filtering, clear and return to full list
|
||||
applied_query=""
|
||||
filter_query=""
|
||||
top_index=0
|
||||
cursor_pos=0
|
||||
rebuild_view
|
||||
else
|
||||
# Enter filter mode
|
||||
filter_mode="true"
|
||||
export MOLE_READ_KEY_FORCE_CHAR=1
|
||||
filter_query=""
|
||||
top_index=0
|
||||
cursor_pos=0
|
||||
rebuild_view
|
||||
fi
|
||||
;;
|
||||
"CHAR:f" | "CHAR:F")
|
||||
if [[ "$filter_mode" == "true" ]]; then
|
||||
@@ -603,9 +627,12 @@ paginated_multi_select() {
|
||||
CHAR:*)
|
||||
if [[ "$filter_mode" == "true" ]]; then
|
||||
local ch="${key#CHAR:}"
|
||||
# Special handling for /: clear filter
|
||||
# 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
|
||||
elif [[ -n "$filter_query" || "$ch" != " " ]]; then
|
||||
|
||||
@@ -168,7 +168,7 @@ paginated_multi_select() {
|
||||
|
||||
# Clear any remaining lines at bottom
|
||||
printf "${clear_line}\n" >&2
|
||||
printf "${clear_line}${GRAY}${ICON_NAV_UP}/${ICON_NAV_DOWN}${NC} Nav ${GRAY}|${NC} ${GRAY}Space${NC} Select ${GRAY}|${NC} ${GRAY}Enter${NC} Confirm ${GRAY}|${NC} ${GRAY}Q${NC} Quit\n" >&2
|
||||
printf "${clear_line}${ICON_NAV_UP}/${ICON_NAV_DOWN} | Space | Enter | Q Exit\n" >&2
|
||||
|
||||
# Clear one more line to ensure no artifacts
|
||||
printf "${clear_line}" >&2
|
||||
|
||||
@@ -92,29 +92,6 @@ format_size_kb() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Check startup items count
|
||||
check_startup_items() {
|
||||
local count=0
|
||||
local dirs=(
|
||||
"$HOME/Library/LaunchAgents"
|
||||
"/Library/LaunchAgents"
|
||||
)
|
||||
|
||||
for dir in "${dirs[@]}"; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
local dir_count
|
||||
dir_count=$(find "$dir" -maxdepth 1 -type f -name "*.plist" 2> /dev/null | wc -l)
|
||||
count=$((count + dir_count))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $count -gt 5 ]]; then
|
||||
local suggested=$((count / 2))
|
||||
[[ $suggested -lt 1 ]] && suggested=1
|
||||
echo "startup_items|Startup Items|${count} items (suggest disable ${suggested})|false"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check cache size
|
||||
check_cache_refresh() {
|
||||
local cache_dir="$HOME/Library/Caches"
|
||||
@@ -240,8 +217,6 @@ EOF
|
||||
|
||||
# Conditional items
|
||||
local item
|
||||
item=$(check_startup_items || true)
|
||||
[[ -n "$item" ]] && items+=("$item")
|
||||
item=$(check_cache_refresh || true)
|
||||
[[ -n "$item" ]] && items+=("$item")
|
||||
item=$(check_mail_downloads || true)
|
||||
|
||||
@@ -136,6 +136,7 @@ batch_uninstall_applications() {
|
||||
fi
|
||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} ${removal_note} ${GREEN}Enter${NC} confirm, ${GRAY}ESC${NC} cancel: "
|
||||
|
||||
drain_pending_input # Clean up any pending input before confirmation
|
||||
IFS= read -r -s -n1 key || key=""
|
||||
drain_pending_input # Clean up any escape sequence remnants
|
||||
case "$key" in
|
||||
@@ -316,6 +317,12 @@ batch_uninstall_applications() {
|
||||
sudo_keepalive_pid=""
|
||||
fi
|
||||
|
||||
# Invalidate cache if any apps were successfully uninstalled
|
||||
if [[ $success_count -gt 0 ]]; then
|
||||
local cache_file="$HOME/.cache/mole/app_scan_cache"
|
||||
rm -f "$cache_file" 2> /dev/null || true
|
||||
fi
|
||||
|
||||
((total_size_cleaned += total_size_freed))
|
||||
unset failed_items
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user