From 5e784e98d7391779d49db0a88e40455c3f613e04 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Sun, 28 Sep 2025 00:15:04 +0800 Subject: [PATCH] :fire: remove useless files --- .DS_Store | Bin 6148 -> 0 bytes lib/better_menu.sh | 248 ------------------------------------- lib/menu_backup.sh | 300 --------------------------------------------- lib/native_menu.sh | 157 ------------------------ lib/simple_menu.sh | 150 ----------------------- lib/smart_menu.sh | 268 ---------------------------------------- 6 files changed, 1123 deletions(-) delete mode 100644 .DS_Store delete mode 100755 lib/better_menu.sh delete mode 100755 lib/menu_backup.sh delete mode 100755 lib/native_menu.sh delete mode 100644 lib/simple_menu.sh delete mode 100755 lib/smart_menu.sh diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index ecefc357fb0698a899f5a19cbe4f8125574b7b41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKy-ou$47Q=eAtY2b#ykO1-yoD@WaYLlyV%4?1CV#Mb+~wbPb-ir5CH$)P>G>|1v# z_x}Zcnb9JD7!ti;AQ<>(4Dh60)N_24->qLhPwv_Ty@HB}Ul9WWd+-QAN6wKGowWHR ZHvF</dev/null || true -} - -restore_terminal() { - stty "$(save_terminal)" 2>/dev/null || true - printf '\033[?25h' >&2 # Show cursor - printf '\033[0m' >&2 # Reset colors -} - -# Read a single key (handles arrow keys properly) -read_key() { - local key - read -rsn1 key - case "$key" in - $'\033') # ESC sequence - read -rsn2 key 2>/dev/null || key="" - case "$key" in - '[A') echo "UP" ;; - '[B') echo "DOWN" ;; - *) echo "ESC" ;; - esac - ;; - ' ') echo "SPACE" ;; - '') echo "ENTER" ;; - 'q'|'Q') echo "QUIT" ;; - *) echo "OTHER" ;; - esac -} - -# Multi-select menu with proper pagination -multi_select_menu() { - local title="$1" - shift - local -a items=("$@") - - if [[ ${#items[@]} -eq 0 ]]; then - echo "Error: No items provided" >&2 - return 1 - fi - - local -a selected=() - local current=0 - local page_size=10 - local total=${#items[@]} - - # Initialize selection array - for ((i = 0; i < total; i++)); do - selected[i]=false - done - - # Save terminal state - local saved_state="" - saved_state=$(save_terminal) - trap 'test -n "$saved_state" && stty "$saved_state" 2>/dev/null; restore_terminal' EXIT INT TERM - - while true; do - # Calculate pagination - local start_page=$((current / page_size)) - local start_idx=$((start_page * page_size)) - local end_idx=$((start_idx + page_size - 1)) - if [[ $end_idx -ge $total ]]; then - end_idx=$((total - 1)) - fi - - # Clear screen and show header - printf '\033[2J\033[H' >&2 - echo "┌─── $title ───┐" >&2 - echo "│ Found $total items (Page $((start_page + 1)) of $(((total + page_size - 1) / page_size))) │" >&2 - echo "└─────────────────────────────────────────────┘" >&2 - echo "" >&2 - - # Show items for current page - for ((i = start_idx; i <= end_idx; i++)); do - local marker=" " - local checkbox="☐" - - if [[ $i -eq $current ]]; then - marker="▶ " - fi - - if [[ ${selected[i]} == "true" ]]; then - checkbox="☑" - fi - - printf "%s%s %s\n" "$marker" "$checkbox" "${items[i]}" >&2 - done - - echo "" >&2 - echo "Controls: ↑/↓=Navigate Space=Select/Deselect Enter=Confirm Q=Quit" >&2 - - # Show selection summary - local count=0 - for ((i = 0; i < total; i++)); do - if [[ ${selected[i]} == "true" ]]; then - ((count++)) - fi - done - echo "Selected: $count items" >&2 - echo "" >&2 - - # Read key - local key - key=$(read_key) - - case "$key" in - "UP") - ((current--)) - if [[ $current -lt 0 ]]; then - current=$((total - 1)) - fi - ;; - "DOWN") - ((current++)) - if [[ $current -ge $total ]]; then - current=0 - fi - ;; - "SPACE") - if [[ ${selected[current]} == "true" ]]; then - selected[current]=false - else - selected[current]=true - fi - ;; - "ENTER") - # Build result string - local result="" - for ((i = 0; i < total; i++)); do - if [[ ${selected[i]} == "true" ]]; then - result="$result $i" - fi - done - - # Clean up and return - restore_terminal - echo "${result# }" # Remove leading space - return 0 - ;; - "QUIT"|"ESC") - restore_terminal - return 1 - ;; - esac - done -} - -# Simple single-select menu -single_select_menu() { - local title="$1" - shift - local -a items=("$@") - - if [[ ${#items[@]} -eq 0 ]]; then - echo "Error: No items provided" >&2 - return 1 - fi - - local current=0 - local total=${#items[@]} - - # Save terminal state - local saved_state="" - saved_state=$(save_terminal) - trap 'test -n "$saved_state" && stty "$saved_state" 2>/dev/null; restore_terminal' EXIT INT TERM - - while true; do - # Clear screen and show header - printf '\033[2J\033[H' >&2 - echo "┌─── $title ───┐" >&2 - echo "│ Choose one of $total items │" >&2 - echo "└────────────────────────────┘" >&2 - echo "" >&2 - - # Show all items - for ((i = 0; i < total; i++)); do - local marker=" " - if [[ $i -eq $current ]]; then - marker="▶ " - fi - printf "%s%s\n" "$marker" "${items[i]}" >&2 - done - - echo "" >&2 - echo "Controls: ↑/↓=Navigate Enter=Select Q=Quit" >&2 - echo "" >&2 - - # Read key - local key - key=$(read_key) - - case "$key" in - "UP") - ((current--)) - if [[ $current -lt 0 ]]; then - current=$((total - 1)) - fi - ;; - "DOWN") - ((current++)) - if [[ $current -ge $total ]]; then - current=0 - fi - ;; - "ENTER") - restore_terminal - echo "$current" - return 0 - ;; - "QUIT"|"ESC") - restore_terminal - return 1 - ;; - esac - done -} - -# Demo function for testing -demo() { - echo "=== Multi-select Demo ===" >&2 - local result - result=$(multi_select_menu "Test Multi-Select" "Option 1" "Option 2" "Option 3" "Option 4" "Option 5") - if [[ $? -eq 0 ]]; then - echo "You selected indices: $result" >&2 - else - echo "Selection cancelled" >&2 - fi - - echo "" >&2 - echo "=== Single-select Demo ===" >&2 - result=$(single_select_menu "Test Single-Select" "Choice A" "Choice B" "Choice C") - if [[ $? -eq 0 ]]; then - echo "You selected index: $result" >&2 - else - echo "Selection cancelled" >&2 - fi -} - -# Run demo if script is executed directly -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - demo -fi \ No newline at end of file diff --git a/lib/menu_backup.sh b/lib/menu_backup.sh deleted file mode 100755 index 40b4bd6..0000000 --- a/lib/menu_backup.sh +++ /dev/null @@ -1,300 +0,0 @@ -#!/bin/bash - -# Simple interactive menu selector with arrow key support -# No external dependencies, compatible with most bash versions - -declare -a menu_options=() -declare -i selected=0 -declare -i menu_size=0 - -# ANSI escape sequences -readonly ESC=$'\033' -readonly UP="${ESC}[A" -readonly DOWN="${ESC}[B" -readonly ENTER=$'\n' -readonly CLEAR_LINE="${ESC}[2K" -readonly HIDE_CURSOR="${ESC}[?25l" -readonly SHOW_CURSOR="${ESC}[?25h" - -# Set terminal to raw mode for reading single characters -setup_terminal() { - stty -echo -icanon time 0 min 0 -} - -# Restore terminal to normal mode -restore_terminal() { - stty echo icanon - printf "%s" "$SHOW_CURSOR" -} - -# Draw the menu -draw_menu() { - printf "%s" "$HIDE_CURSOR" - - for ((i = 0; i < menu_size; i++)); do - printf "\r%s" "$CLEAR_LINE" - - if [[ $i -eq $selected ]]; then - printf "▶ \033[1;32m%s\033[0m\n" "${menu_options[i]}" - else - printf " %s\n" "${menu_options[i]}" - fi - done - - # Move cursor back to the beginning - printf "${ESC}[%dA" $menu_size -} - -# Read a single key -read_key() { - local key - IFS= read -r -n1 key 2>/dev/null - - if [[ $key == $ESC ]]; then - # Handle escape sequences - IFS= read -r -n2 key 2>/dev/null - case "$key" in - '[A') echo "UP" ;; - '[B') echo "DOWN" ;; - *) echo "ESC" ;; - esac - elif [[ $key == "" ]]; then - echo "ENTER" - else - echo "$key" - fi -} - -# Main menu function -# Usage: show_menu "Title" "option1" "option2" "option3" ... -show_menu() { - local title="$1" - shift - - # Initialize menu options - menu_options=("$@") - menu_size=${#menu_options[@]} - selected=0 - - # Check if we have options - if [[ $menu_size -eq 0 ]]; then - echo "Error: No menu options provided" >&2 - return 1 - fi - - # Setup terminal - setup_terminal - trap restore_terminal EXIT INT TERM - - # Display title - if [[ -n "$title" ]]; then - printf "\n\033[1;34m%s\033[0m\n\n" "$title" - fi - - # Initial draw - draw_menu - - # Main loop - while true; do - local key=$(read_key) - - case "$key" in - "UP") - ((selected--)) - if [[ $selected -lt 0 ]]; then - selected=$((menu_size - 1)) - fi - draw_menu - ;; - "DOWN") - ((selected++)) - if [[ $selected -ge $menu_size ]]; then - selected=0 - fi - draw_menu - ;; - "ENTER") - # Clear the menu - for ((i = 0; i < menu_size; i++)); do - printf "\r%s\n" "$CLEAR_LINE" >&2 - done - printf "${ESC}[%dA" $menu_size >&2 - - # Show selection - printf "Selected: \033[1;32m%s\033[0m\n\n" "${menu_options[selected]}" - - restore_terminal - return $selected - ;; - "q"|"Q") - restore_terminal - echo "Cancelled." >&2 - return 255 - ;; - [0-9]) - # Jump to numbered option - local num=$((key - 1)) - if [[ $num -ge 0 && $num -lt $menu_size ]]; then - selected=$num - draw_menu - fi - ;; - esac - done -} - -# Multi-select menu function -# Usage: show_multi_menu "Title" "option1" "option2" "option3" ... -show_multi_menu() { - local title="$1" - shift - - # Initialize menu options - menu_options=("$@") - menu_size=${#menu_options[@]} - selected=0 - - # Array to track selected items - declare -a selected_items=() - for ((i = 0; i < menu_size; i++)); do - selected_items[i]=false - done - - # Check if we have options - if [[ $menu_size -eq 0 ]]; then - echo "Error: No menu options provided" >&2 - return 1 - fi - - # Setup terminal - setup_terminal - trap restore_terminal EXIT INT TERM - - # Display title - if [[ -n "$title" ]]; then - printf "\n\033[1;34m%s\033[0m\n" "$title" >&2 - printf "\033[0;36mUse SPACE to select/deselect, ENTER to confirm, Q to quit\033[0m\n\n" >&2 - fi - - # Draw multi-select menu - draw_multi_menu() { - printf "%s" "$HIDE_CURSOR" >&2 - - for ((i = 0; i < menu_size; i++)); do - printf "\r%s" "$CLEAR_LINE" >&2 - - local checkbox="☐" - if [[ ${selected_items[i]} == "true" ]]; then - checkbox="\033[1;32m☑\033[0m" - fi - - if [[ $i -eq $selected ]]; then - printf "▶ %s \033[1;32m%s\033[0m\n" "$checkbox" "${menu_options[i]}" >&2 - else - printf " %s %s\n" "$checkbox" "${menu_options[i]}" >&2 - fi - done - - # Move cursor back to the beginning - printf "${ESC}[%dA" $menu_size >&2 - } - - # Initial draw - draw_multi_menu - - # Main loop - while true; do - local key=$(read_key) - - case "$key" in - "UP") - ((selected--)) - if [[ $selected -lt 0 ]]; then - selected=$((menu_size - 1)) - fi - draw_multi_menu - ;; - "DOWN") - ((selected++)) - if [[ $selected -ge $menu_size ]]; then - selected=0 - fi - draw_multi_menu - ;; - " ") - # Toggle selection - if [[ ${selected_items[selected]} == "true" ]]; then - selected_items[selected]="false" - else - selected_items[selected]="true" - fi - draw_multi_menu - ;; - "ENTER") - # Clear the menu - for ((i = 0; i < menu_size; i++)); do - printf "\r%s\n" "$CLEAR_LINE" >&2 - done - printf "${ESC}[%dA" $menu_size >&2 - - # Show selections to stderr so it doesn't interfere with return value - local has_selection=false - printf "Selected items:\n" >&2 - for ((i = 0; i < menu_size; i++)); do - if [[ ${selected_items[i]} == "true" ]]; then - printf " \033[1;32m%s\033[0m\n" "${menu_options[i]}" >&2 - has_selection=true - fi - done - - if [[ $has_selection == "false" ]]; then - printf " None\n" >&2 - fi - printf "\n" >&2 - - restore_terminal - - # Return selected indices as space-separated string - local result="" - for ((i = 0; i < menu_size; i++)); do - if [[ ${selected_items[i]} == "true" ]]; then - result="$result $i" - fi - done - echo "${result# }" # Remove leading space - return 0 - ;; - "q"|"Q") - restore_terminal - echo "Cancelled." >&2 - return 255 - ;; - esac - done -} - -# Example usage function -demo_menu() { - echo "=== Single Select Demo ===" - if show_menu "Choose an action:" "Install package" "Update system" "Clean cache" "Exit"; then - local choice=$? - echo "You selected option $choice" - fi - - echo -e "\n=== Multi Select Demo ===" - local selections=$(show_multi_menu "Choose packages to install:" "git" "vim" "curl" "htop" "tree") - if [[ $? -eq 0 && -n "$selections" ]]; then - echo "Selected indices: $selections" - # Convert indices to actual values - local options=("git" "vim" "curl" "htop" "tree") - echo "Selected packages:" - for idx in $selections; do - echo " - ${options[idx]}" - done - fi -} - -# If script is run directly, show demo -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - demo_menu -fi \ No newline at end of file diff --git a/lib/native_menu.sh b/lib/native_menu.sh deleted file mode 100755 index 9384a19..0000000 --- a/lib/native_menu.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash - -# Simple native bash menu using the built-in select command -# This is the most reliable approach with zero dependencies - -# Multi-select using native bash select with checkboxes simulation -multi_select_native() { - local title="$1" - shift - local -a items=("$@") - - if [[ ${#items[@]} -eq 0 ]]; then - echo "Error: No items provided" >&2 - return 1 - fi - - echo "=== $title ===" >&2 - echo "Select multiple items (enter numbers separated by spaces, or 'done' when finished):" >&2 - echo "" >&2 - - # Display items with numbers - for ((i = 0; i < ${#items[@]}; i++)); do - printf "%2d) %s\n" $((i + 1)) "${items[i]}" >&2 - done - echo "" >&2 - - local -a selected_indices=() - - while true; do - echo "Currently selected: ${#selected_indices[@]} items" >&2 - if [[ ${#selected_indices[@]} -gt 0 ]]; then - echo "Selected indices: ${selected_indices[*]}" >&2 - fi - echo "" >&2 - - read -p "Enter selection (numbers, 'all', 'none', or 'done'): " -r input >&2 - - case "$input" in - "done"|"") - break - ;; - "all") - selected_indices=() - for ((i = 0; i < ${#items[@]}; i++)); do - selected_indices+=($i) - done - echo "Selected all ${#items[@]} items" >&2 - ;; - "none") - selected_indices=() - echo "Cleared all selections" >&2 - ;; - *) - # Parse space-separated numbers - read -ra nums <<< "$input" - for num in "${nums[@]}"; do - if [[ "$num" =~ ^[0-9]+$ ]] && [[ $num -ge 1 ]] && [[ $num -le ${#items[@]} ]]; then - local idx=$((num - 1)) - # Check if already selected - local already_selected=false - if [[ ${#selected_indices[@]} -gt 0 ]]; then - for selected in "${selected_indices[@]}"; do - if [[ $selected -eq $idx ]]; then - already_selected=true - break - fi - done - fi - - if [[ $already_selected == false ]]; then - selected_indices+=($idx) - echo "Added: ${items[idx]}" >&2 - else - echo "Already selected: ${items[idx]}" >&2 - fi - else - echo "Invalid selection: $num (must be 1-${#items[@]})" >&2 - fi - done - ;; - esac - echo "" >&2 - done - - # Convert to space-separated string and return - local result="" - if [[ ${#selected_indices[@]} -gt 0 ]]; then - for idx in "${selected_indices[@]}"; do - result="$result $idx" - done - echo "${result# }" # Remove leading space - else - echo "" # Return empty string for no selections - fi - return 0 -} - -# Simple single-select using native bash select -single_select_native() { - local title="$1" - shift - local -a items=("$@") - - if [[ ${#items[@]} -eq 0 ]]; then - echo "Error: No items provided" >&2 - return 1 - fi - - echo "=== $title ===" >&2 - - # Use PS3 to customize the select prompt - local PS3="Please select an option (1-${#items[@]}): " - - select item in "${items[@]}" "Cancel"; do - if [[ -n "$item" ]]; then - if [[ "$item" == "Cancel" ]]; then - return 1 - else - # Find the index of selected item - for ((i = 0; i < ${#items[@]}; i++)); do - if [[ "${items[i]}" == "$item" ]]; then - echo "$i" - return 0 - fi - done - fi - else - echo "Invalid selection. Please try again." >&2 - fi - done 2>&2 # Redirect select dialog to stderr -} - -# Demo function -demo_native() { - echo "=== Multi-select Demo ===" >&2 - local result - result=$(multi_select_native "Choose Applications" "App 1" "App 2" "App 3" "App 4" "App 5") - if [[ $? -eq 0 ]]; then - echo "You selected indices: '$result'" >&2 - else - echo "Selection cancelled" >&2 - fi - - echo "" >&2 - echo "=== Single-select Demo ===" >&2 - result=$(single_select_native "Choose One App" "Option A" "Option B" "Option C") - if [[ $? -eq 0 ]]; then - echo "You selected index: $result" >&2 - else - echo "Selection cancelled" >&2 - fi -} - -# Run demo if script is executed directly -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - demo_native -fi \ No newline at end of file diff --git a/lib/simple_menu.sh b/lib/simple_menu.sh deleted file mode 100644 index baaa0cd..0000000 --- a/lib/simple_menu.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/bin/bash - -# Simple, clean menu implementation that properly separates output - -# Simple single-select menu - returns selected index -simple_select() { - local title="$1" - shift - local -a options=("$@") - local selected=0 - local key - - # Clear screen and show header - clear >&2 - echo "=== $title ===" >&2 - echo "" >&2 - - while true; do - # Show options - for ((i = 0; i < ${#options[@]}; i++)); do - if [[ $i -eq $selected ]]; then - echo "▶ ${options[i]}" >&2 - else - echo " ${options[i]}" >&2 - fi - done - echo "" >&2 - echo "Use ↑/↓ to navigate, ENTER to select, Q to quit" >&2 - - # Read key - read -rsn1 key - case "$key" in - $'\x1b') - # Arrow key sequence - read -rsn2 key - case "$key" in - '[A') # Up - ((selected--)) - if [[ $selected -lt 0 ]]; then - selected=$((${#options[@]} - 1)) - fi - ;; - '[B') # Down - ((selected++)) - if [[ $selected -ge ${#options[@]} ]]; then - selected=0 - fi - ;; - esac - ;; - '') # Enter - echo "$selected" - return 0 - ;; - 'q'|'Q') - return 1 - ;; - esac - - # Clear screen for next iteration - clear >&2 - echo "=== $title ===" >&2 - echo "" >&2 - done -} - -# Multi-select menu - returns space-separated indices -simple_multi_select() { - local title="$1" - shift - local -a options=("$@") - local selected=0 - local -a selected_items=() - local key - - # Initialize selected items array - for ((i = 0; i < ${#options[@]}; i++)); do - selected_items[i]=false - done - - clear >&2 - echo "=== $title ===" >&2 - echo "" >&2 - - while true; do - # Show options - for ((i = 0; i < ${#options[@]}; i++)); do - local checkbox="☐" - if [[ ${selected_items[i]} == "true" ]]; then - checkbox="☑" - fi - - if [[ $i -eq $selected ]]; then - echo "▶ $checkbox ${options[i]}" >&2 - else - echo " $checkbox ${options[i]}" >&2 - fi - done - echo "" >&2 - echo "Use ↑/↓ to navigate, SPACE to select/deselect, ENTER to confirm, Q to quit" >&2 - - # Read key - read -rsn1 key - case "$key" in - $'\x1b') - # Arrow key sequence - read -rsn2 key - case "$key" in - '[A') # Up - ((selected--)) - if [[ $selected -lt 0 ]]; then - selected=$((${#options[@]} - 1)) - fi - ;; - '[B') # Down - ((selected++)) - if [[ $selected -ge ${#options[@]} ]]; then - selected=0 - fi - ;; - esac - ;; - ' ') # Space - toggle selection - if [[ ${selected_items[selected]} == "true" ]]; then - selected_items[selected]=false - else - selected_items[selected]=true - fi - ;; - '') # Enter - confirm - local result="" - for ((i = 0; i < ${#options[@]}; i++)); do - if [[ ${selected_items[i]} == "true" ]]; then - result="$result $i" - fi - done - echo "${result# }" # Remove leading space - return 0 - ;; - 'q'|'Q') - return 1 - ;; - esac - - # Clear screen for next iteration - clear >&2 - echo "=== $title ===" >&2 - echo "" >&2 - done -} \ No newline at end of file diff --git a/lib/smart_menu.sh b/lib/smart_menu.sh deleted file mode 100755 index ebd0172..0000000 --- a/lib/smart_menu.sh +++ /dev/null @@ -1,268 +0,0 @@ -#!/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 \ No newline at end of file