diff --git a/lib/clean/project.sh b/lib/clean/project.sh index c701f08..a34e49c 100644 --- a/lib/clean/project.sh +++ b/lib/clean/project.sh @@ -898,6 +898,70 @@ clean_project_artifacts() { echo "$result" } + # Helper to get project path (more complete than just project name) + # For ~/www/pake/src-tauri/target -> returns "~/www/pake" + # For ~/work/code/MyProject/node_modules -> returns "~/work/code/MyProject" + # Shows the full path relative to HOME with ~ prefix for better clarity + get_project_path() { + local path="$1" + + local current_dir + current_dir=$(dirname "$path") + local monorepo_root="" + local project_root="" + + # Single pass: check both monorepo and project indicators + while [[ "$current_dir" != "/" && "$current_dir" != "$HOME" && -n "$current_dir" ]]; do + # First check for monorepo indicators (higher priority) + if [[ -z "$monorepo_root" ]]; then + for indicator in "${MONOREPO_INDICATORS[@]}"; do + if [[ -e "$current_dir/$indicator" ]]; then + monorepo_root="$current_dir" + break + fi + done + fi + + # Then check for project indicators (save first match) + if [[ -z "$project_root" ]]; then + for indicator in "${PROJECT_INDICATORS[@]}"; do + if [[ -e "$current_dir/$indicator" ]]; then + project_root="$current_dir" + break + fi + done + fi + + # If we found monorepo, we can stop (monorepo always wins) + if [[ -n "$monorepo_root" ]]; then + break + fi + + # If we found project but still checking for monorepo above + local depth=$(echo "${current_dir#"$HOME"}" | LC_ALL=C tr -cd '/' | wc -c | tr -d ' ') + if [[ -n "$project_root" && $depth -lt 2 ]]; then + break + fi + + current_dir=$(dirname "$current_dir") + done + + # Determine result: monorepo > project > fallback + local result="" + if [[ -n "$monorepo_root" ]]; then + result="$monorepo_root" + elif [[ -n "$project_root" ]]; then + result="$project_root" + else + # Fallback: use parent directory of artifact + result=$(dirname "$path") + fi + + # Convert to ~ format for cleaner display + result="${result/#$HOME/~}" + echo "$result" + } + # Helper to get artifact display name # For duplicate artifact names within same project, include parent directory for context get_artifact_display_name() { @@ -927,28 +991,28 @@ clean_project_artifacts() { } # Format display with alignment (like app_selector) format_purge_display() { - local project_name="$1" + local project_path="$1" local artifact_type="$2" local size_str="$3" # Terminal width for alignment local terminal_width=$(tput cols 2> /dev/null || echo 80) - local fixed_width=38 # Reserve for type, size, and potential "| Recent" (28 + 10) + local fixed_width=28 # Reserve for size and artifact type (9 + 3 + 16) local available_width=$((terminal_width - fixed_width)) - # Bounds: 24-35 chars for project name - [[ $available_width -lt 24 ]] && available_width=24 - [[ $available_width -gt 35 ]] && available_width=35 - # Truncate project name if needed - local truncated_name=$(truncate_by_display_width "$project_name" "$available_width") - local current_width=$(get_display_width "$truncated_name") - local char_count=${#truncated_name} + # Bounds: 30-50 chars for project path (increased to accommodate full paths) + [[ $available_width -lt 30 ]] && available_width=30 + [[ $available_width -gt 50 ]] && available_width=50 + # Truncate project path if needed + local truncated_path=$(truncate_by_display_width "$project_path" "$available_width") + local current_width=$(get_display_width "$truncated_path") + local char_count=${#truncated_path} local padding=$((available_width - current_width)) local printf_width=$((char_count + padding)) - # Format: "project_name size | artifact_type" - printf "%-*s %9s | %-13s" "$printf_width" "$truncated_name" "$size_str" "$artifact_type" + # Format: "project_path size | artifact_type" + printf "%-*s %9s | %-13s" "$printf_width" "$truncated_path" "$size_str" "$artifact_type" } # Build menu options - one line per artifact for item in "${safe_to_clean[@]}"; do - local project_name=$(get_project_name "$item") + local project_path=$(get_project_path "$item") local artifact_type=$(get_artifact_display_name "$item") local size_kb=$(get_dir_size_kb "$item") @@ -966,11 +1030,48 @@ clean_project_artifacts() { break fi done - menu_options+=("$(format_purge_display "$project_name" "$artifact_type" "$size_human")") + menu_options+=("$(format_purge_display "$project_path" "$artifact_type" "$size_human")") item_paths+=("$item") item_sizes+=("$size_kb") item_recent_flags+=("$is_recent") done + + # Sort by size descending (largest first) - requested in issue #311 + # Use external sort for better performance with many items + if [[ ${#item_sizes[@]} -gt 0 ]]; then + # Create temporary file with index|size pairs + local sort_temp + sort_temp=$(mktemp) + for ((i = 0; i < ${#item_sizes[@]}; i++)); do + printf '%d|%d\n' "$i" "${item_sizes[i]}" + done > "$sort_temp" + + # Sort by size (field 2) descending, extract indices + local -a sorted_indices=() + while IFS='|' read -r idx size; do + sorted_indices+=("$idx") + done < <(sort -t'|' -k2,2nr "$sort_temp") + rm -f "$sort_temp" + + # Rebuild arrays in sorted order + local -a sorted_menu_options=() + local -a sorted_item_paths=() + local -a sorted_item_sizes=() + local -a sorted_item_recent_flags=() + + for idx in "${sorted_indices[@]}"; do + sorted_menu_options+=("${menu_options[idx]}") + sorted_item_paths+=("${item_paths[idx]}") + sorted_item_sizes+=("${item_sizes[idx]}") + sorted_item_recent_flags+=("${item_recent_flags[idx]}") + done + + # Replace original arrays with sorted versions + menu_options=("${sorted_menu_options[@]}") + item_paths=("${sorted_item_paths[@]}") + item_sizes=("${sorted_item_sizes[@]}") + item_recent_flags=("${sorted_item_recent_flags[@]}") + fi if [[ -t 1 ]]; then stop_inline_spinner fi @@ -1014,7 +1115,7 @@ clean_project_artifacts() { for idx in "${selected_indices[@]}"; do local item_path="${item_paths[idx]}" local artifact_type=$(basename "$item_path") - local project_name=$(get_project_name "$item_path") + local project_path=$(get_project_path "$item_path") local size_kb="${item_sizes[idx]}" local size_human=$(bytes_to_human "$((size_kb * 1024))") # Safety checks @@ -1022,7 +1123,7 @@ clean_project_artifacts() { continue fi if [[ -t 1 ]]; then - start_inline_spinner "Cleaning $project_name/$artifact_type..." + start_inline_spinner "Cleaning $project_path/$artifact_type..." fi if [[ -e "$item_path" ]]; then safe_remove "$item_path" true @@ -1034,7 +1135,7 @@ clean_project_artifacts() { fi if [[ -t 1 ]]; then stop_inline_spinner - echo -e "${GREEN}${ICON_SUCCESS}${NC} $project_name - $artifact_type ${GREEN}($size_human)${NC}" + echo -e "${GREEN}${ICON_SUCCESS}${NC} $project_path - $artifact_type ${GREEN}($size_human)${NC}" fi done # Update count