mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 19:09:43 +00:00
268 lines
8.7 KiB
Bash
Executable File
268 lines
8.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Smart menu with pagination and search for large lists
|
|
# Much better UX for handling many items
|
|
|
|
# Smart multi-select with search and pagination
|
|
smart_multi_select() {
|
|
local title="$1"
|
|
shift
|
|
local -a all_items=("$@")
|
|
|
|
if [[ ${#all_items[@]} -eq 0 ]]; then
|
|
echo "Error: No items provided" >&2
|
|
return 1
|
|
fi
|
|
|
|
local -a selected_indices=()
|
|
local -a filtered_items=()
|
|
local -a filtered_indices=()
|
|
local search_term=""
|
|
local page_size=15
|
|
local current_page=0
|
|
|
|
# Function to filter items based on search
|
|
filter_items() {
|
|
filtered_items=()
|
|
filtered_indices=()
|
|
|
|
if [[ -z "$search_term" ]]; then
|
|
# No search, show all items
|
|
filtered_items=("${all_items[@]}")
|
|
for ((i = 0; i < ${#all_items[@]}; i++)); do
|
|
filtered_indices+=($i)
|
|
done
|
|
else
|
|
# Filter items that contain search term (case insensitive)
|
|
for ((i = 0; i < ${#all_items[@]}; i++)); do
|
|
if [[ "${all_items[i],,}" == *"${search_term,,}"* ]]; then
|
|
filtered_items+=("${all_items[i]}")
|
|
filtered_indices+=($i)
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
# Function to display current page
|
|
show_page() {
|
|
local total_filtered=${#filtered_items[@]}
|
|
local total_pages=$(( (total_filtered + page_size - 1) / page_size ))
|
|
local start_idx=$((current_page * page_size))
|
|
local end_idx=$((start_idx + page_size - 1))
|
|
|
|
if [[ $end_idx -ge $total_filtered ]]; then
|
|
end_idx=$((total_filtered - 1))
|
|
fi
|
|
|
|
printf '\033[2J\033[H' >&2
|
|
echo "╭─────────────────────────────────────────────────────╮" >&2
|
|
echo "│ $title" >&2
|
|
echo "├─────────────────────────────────────────────────────┤" >&2
|
|
echo "│ Total: ${#all_items[@]} | Filtered: $total_filtered | Selected: ${#selected_indices[@]} │" >&2
|
|
|
|
if [[ -n "$search_term" ]]; then
|
|
echo "│ Search: '$search_term' │" >&2
|
|
fi
|
|
|
|
if [[ $total_pages -gt 1 ]]; then
|
|
echo "│ Page $(($current_page + 1)) of $total_pages │" >&2
|
|
fi
|
|
echo "╰─────────────────────────────────────────────────────╯" >&2
|
|
echo "" >&2
|
|
|
|
if [[ $total_filtered -eq 0 ]]; then
|
|
echo "No items match your search." >&2
|
|
echo "" >&2
|
|
else
|
|
# Show items for current page
|
|
for ((i = start_idx; i <= end_idx && i < total_filtered; i++)); do
|
|
local item_idx=${filtered_indices[i]}
|
|
local display_num=$((i + 1))
|
|
|
|
# Check if this item is selected
|
|
local is_selected=false
|
|
if [[ ${#selected_indices[@]} -gt 0 ]]; then
|
|
for selected in "${selected_indices[@]}"; do
|
|
if [[ $selected -eq $item_idx ]]; then
|
|
is_selected=true
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ $is_selected == true ]]; then
|
|
printf "%3d) ✓ %s\n" "$display_num" "${filtered_items[i]}" >&2
|
|
else
|
|
printf "%3d) %s\n" "$display_num" "${filtered_items[i]}" >&2
|
|
fi
|
|
done
|
|
fi
|
|
|
|
echo "" >&2
|
|
echo "Commands:" >&2
|
|
echo " Numbers: Select items (e.g., '1-5', '1 3 7', '10-15')" >&2
|
|
echo " /search: Filter items (e.g., '/chrome')" >&2
|
|
echo " n/p: Next/Previous page | all: Select all | none: Clear all" >&2
|
|
echo " done: Finish selection | quit: Cancel" >&2
|
|
echo "" >&2
|
|
}
|
|
|
|
# Main loop
|
|
while true; do
|
|
filter_items
|
|
show_page
|
|
|
|
read -p "Enter command: " -r input >&2
|
|
|
|
case "$input" in
|
|
"done"|"")
|
|
break
|
|
;;
|
|
"quit"|"q")
|
|
return 1
|
|
;;
|
|
"all")
|
|
selected_indices=()
|
|
for idx in "${filtered_indices[@]}"; do
|
|
selected_indices+=($idx)
|
|
done
|
|
echo "Selected all filtered items (${#filtered_indices[@]})" >&2
|
|
;;
|
|
"none")
|
|
selected_indices=()
|
|
echo "Cleared all selections" >&2
|
|
;;
|
|
"n"|"next")
|
|
local total_pages=$(( (${#filtered_items[@]} + page_size - 1) / page_size ))
|
|
if [[ $((current_page + 1)) -lt $total_pages ]]; then
|
|
((current_page++))
|
|
else
|
|
echo "Already on last page" >&2
|
|
fi
|
|
;;
|
|
"p"|"prev")
|
|
if [[ $current_page -gt 0 ]]; then
|
|
((current_page--))
|
|
else
|
|
echo "Already on first page" >&2
|
|
fi
|
|
;;
|
|
/*)
|
|
# Search functionality
|
|
search_term="${input#/}"
|
|
current_page=0
|
|
echo "Searching for: '$search_term'" >&2
|
|
;;
|
|
*)
|
|
# Parse selection input
|
|
parse_selection "$input"
|
|
;;
|
|
esac
|
|
|
|
[[ "$input" != "n" && "$input" != "next" && "$input" != "p" && "$input" != "prev" ]] && sleep 1
|
|
done
|
|
|
|
# Return selected indices
|
|
local result=""
|
|
if [[ ${#selected_indices[@]} -gt 0 ]]; then
|
|
for idx in "${selected_indices[@]}"; do
|
|
result="$result $idx"
|
|
done
|
|
echo "${result# }"
|
|
else
|
|
echo ""
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Parse selection input (supports ranges and individual numbers)
|
|
parse_selection() {
|
|
local input="$1"
|
|
local start_idx=$((current_page * page_size))
|
|
|
|
# Split input by spaces
|
|
read -ra parts <<< "$input"
|
|
|
|
for part in "${parts[@]}"; do
|
|
if [[ "$part" =~ ^[0-9]+$ ]]; then
|
|
# Single number
|
|
local display_num=$part
|
|
local array_idx=$((display_num - 1))
|
|
|
|
if [[ $array_idx -ge 0 && $array_idx -lt ${#filtered_items[@]} ]]; then
|
|
local real_idx=${filtered_indices[array_idx]}
|
|
toggle_selection "$real_idx"
|
|
else
|
|
echo "Invalid selection: $part (range: 1-${#filtered_items[@]})" >&2
|
|
fi
|
|
|
|
elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
|
# Range like 1-5
|
|
local start_num=${BASH_REMATCH[1]}
|
|
local end_num=${BASH_REMATCH[2]}
|
|
|
|
for ((num = start_num; num <= end_num; num++)); do
|
|
local array_idx=$((num - 1))
|
|
if [[ $array_idx -ge 0 && $array_idx -lt ${#filtered_items[@]} ]]; then
|
|
local real_idx=${filtered_indices[array_idx]}
|
|
toggle_selection "$real_idx"
|
|
fi
|
|
done
|
|
|
|
else
|
|
echo "Invalid format: $part (use numbers, ranges like '1-5', or commands)" >&2
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Toggle selection of an item
|
|
toggle_selection() {
|
|
local idx=$1
|
|
local already_selected=false
|
|
local pos_to_remove=-1
|
|
|
|
# Check if already selected
|
|
if [[ ${#selected_indices[@]} -gt 0 ]]; then
|
|
for ((i = 0; i < ${#selected_indices[@]}; i++)); do
|
|
if [[ ${selected_indices[i]} -eq $idx ]]; then
|
|
already_selected=true
|
|
pos_to_remove=$i
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ $already_selected == true ]]; then
|
|
# Remove from selection
|
|
unset selected_indices[$pos_to_remove]
|
|
selected_indices=("${selected_indices[@]}") # Reindex array
|
|
echo "Removed: ${all_items[idx]}" >&2
|
|
else
|
|
# Add to selection
|
|
selected_indices+=($idx)
|
|
echo "Added: ${all_items[idx]}" >&2
|
|
fi
|
|
}
|
|
|
|
# Demo function
|
|
demo_smart() {
|
|
local test_apps=()
|
|
for i in {1..50}; do
|
|
test_apps+=("Test App $i (${RANDOM}MB)")
|
|
done
|
|
|
|
echo "=== Smart Multi-select Demo ===" >&2
|
|
local result
|
|
result=$(smart_multi_select "Choose Applications to Remove" "${test_apps[@]}")
|
|
if [[ $? -eq 0 ]]; then
|
|
echo "You selected indices: '$result'" >&2
|
|
echo "Selected ${result// /,} out of ${#test_apps[@]} items" >&2
|
|
else
|
|
echo "Selection cancelled" >&2
|
|
fi
|
|
}
|
|
|
|
# Run demo if script is executed directly
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
demo_smart
|
|
fi |