mirror of
https://github.com/tw93/Mole.git
synced 2026-02-09 02:04:17 +00:00
Fix mo purge UI glitch by correctly clearing the scanning progress line without overwriting the title.
This commit is contained in:
23
bin/purge.sh
23
bin/purge.sh
@@ -98,7 +98,12 @@ perform_purge() {
|
|||||||
# Function to truncate path in the middle
|
# Function to truncate path in the middle
|
||||||
truncate_path() {
|
truncate_path() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local max_len=80
|
local term_width=$(tput cols 2>/dev/null || echo 80)
|
||||||
|
# Reserve space: "| Scanning " = 12 chars, spinner = 2 chars, margins = 4 chars
|
||||||
|
local max_len=$((term_width - 18))
|
||||||
|
|
||||||
|
# Minimum length to avoid too short
|
||||||
|
[[ $max_len -lt 40 ]] && max_len=40
|
||||||
|
|
||||||
if [[ ${#path} -le $max_len ]]; then
|
if [[ ${#path} -le $max_len ]]; then
|
||||||
echo "$path"
|
echo "$path"
|
||||||
@@ -128,17 +133,23 @@ perform_purge() {
|
|||||||
local spin_char="${spinner_chars:$spinner_idx:1}"
|
local spin_char="${spinner_chars:$spinner_idx:1}"
|
||||||
spinner_idx=$(((spinner_idx + 1) % ${#spinner_chars}))
|
spinner_idx=$(((spinner_idx + 1) % ${#spinner_chars}))
|
||||||
|
|
||||||
# Show title on first line, spinner and scanning info on second line
|
# Clear previous output and redraw
|
||||||
|
# Important: Must move up THEN to start of line to avoid column offset
|
||||||
if [[ -n "$display_path" ]]; then
|
if [[ -n "$display_path" ]]; then
|
||||||
printf '\r%s\n%s %sScanning %s\033[K\033[A' \
|
# Line 1: Move to start, clear, print title
|
||||||
"${PURPLE_BOLD}Purge Project Artifacts${NC}" \
|
printf '\r\033[K%s\n' "${PURPLE_BOLD}Purge Project Artifacts${NC}"
|
||||||
|
# Line 2: Move to start, clear, print scanning info
|
||||||
|
printf '\r\033[K%s %sScanning %s' \
|
||||||
"${BLUE}${spin_char}${NC}" \
|
"${BLUE}${spin_char}${NC}" \
|
||||||
"${GRAY}" "$display_path"
|
"${GRAY}" "$display_path"
|
||||||
|
# Move up THEN to start (important order!)
|
||||||
|
printf '\033[A\r'
|
||||||
else
|
else
|
||||||
printf '\r%s\n%s %sScanning...\033[K\033[A' \
|
printf '\r\033[K%s\n' "${PURPLE_BOLD}Purge Project Artifacts${NC}"
|
||||||
"${PURPLE_BOLD}Purge Project Artifacts${NC}" \
|
printf '\r\033[K%s %sScanning...' \
|
||||||
"${BLUE}${spin_char}${NC}" \
|
"${BLUE}${spin_char}${NC}" \
|
||||||
"${GRAY}"
|
"${GRAY}"
|
||||||
|
printf '\033[A\r'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 0.05
|
sleep 0.05
|
||||||
|
|||||||
@@ -65,6 +65,14 @@ readonly PURGE_CONFIG_FILE="$HOME/.config/mole/purge_paths"
|
|||||||
PURGE_SEARCH_PATHS=()
|
PURGE_SEARCH_PATHS=()
|
||||||
|
|
||||||
# Project indicators for container detection.
|
# Project indicators for container detection.
|
||||||
|
# Monorepo indicators (higher priority)
|
||||||
|
readonly MONOREPO_INDICATORS=(
|
||||||
|
"lerna.json"
|
||||||
|
"pnpm-workspace.yaml"
|
||||||
|
"nx.json"
|
||||||
|
"rush.json"
|
||||||
|
)
|
||||||
|
|
||||||
readonly PROJECT_INDICATORS=(
|
readonly PROJECT_INDICATORS=(
|
||||||
"package.json"
|
"package.json"
|
||||||
"Cargo.toml"
|
"Cargo.toml"
|
||||||
@@ -348,7 +356,7 @@ scan_purge_targets() {
|
|||||||
# Escape regex special characters in target names for fd patterns
|
# Escape regex special characters in target names for fd patterns
|
||||||
local escaped_targets=()
|
local escaped_targets=()
|
||||||
for target in "${PURGE_TARGETS[@]}"; do
|
for target in "${PURGE_TARGETS[@]}"; do
|
||||||
escaped_targets+=("$(printf '%s' "$target" | sed -e 's/[][(){}.^$*+?|\\]/\\&/g')")
|
escaped_targets+=("^$(printf '%s' "$target" | sed -e 's/[][(){}.^$*+?|\\]/\\&/g')\$")
|
||||||
done
|
done
|
||||||
local pattern="($(
|
local pattern="($(
|
||||||
IFS='|'
|
IFS='|'
|
||||||
@@ -764,6 +772,18 @@ clean_project_artifacts() {
|
|||||||
for pid in "${scan_pids[@]+"${scan_pids[@]}"}"; do
|
for pid in "${scan_pids[@]+"${scan_pids[@]}"}"; do
|
||||||
wait "$pid" 2> /dev/null || true
|
wait "$pid" 2> /dev/null || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Stop the scanning monitor (removes purge_scanning file to signal completion)
|
||||||
|
local stats_dir="${XDG_CACHE_HOME:-$HOME/.cache}/mole"
|
||||||
|
rm -f "$stats_dir/purge_scanning" 2> /dev/null || true
|
||||||
|
|
||||||
|
# Give monitor process time to exit and clear its output
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
sleep 0.2
|
||||||
|
# Clear the scanning line but preserve the title
|
||||||
|
printf '\n\033[K'
|
||||||
|
fi
|
||||||
|
|
||||||
# Collect all results
|
# Collect all results
|
||||||
for scan_output in "${scan_temps[@]+"${scan_temps[@]}"}"; do
|
for scan_output in "${scan_temps[@]+"${scan_temps[@]}"}"; do
|
||||||
if [[ -f "$scan_output" ]]; then
|
if [[ -f "$scan_output" ]]; then
|
||||||
@@ -805,44 +825,107 @@ clean_project_artifacts() {
|
|||||||
# Strategy: Find the nearest ancestor directory containing a project indicator file
|
# Strategy: Find the nearest ancestor directory containing a project indicator file
|
||||||
get_project_name() {
|
get_project_name() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local artifact_name
|
|
||||||
artifact_name=$(basename "$path")
|
|
||||||
|
|
||||||
# Start from the parent of the artifact and walk up
|
|
||||||
local current_dir
|
local current_dir
|
||||||
current_dir=$(dirname "$path")
|
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
|
while [[ "$current_dir" != "/" && "$current_dir" != "$HOME" && -n "$current_dir" ]]; do
|
||||||
# Check if current directory contains any project indicator
|
# First check for monorepo indicators (higher priority)
|
||||||
for indicator in "${PROJECT_INDICATORS[@]}"; do
|
if [[ -z "$monorepo_root" ]]; then
|
||||||
if [[ -e "$current_dir/$indicator" ]]; then
|
for indicator in "${MONOREPO_INDICATORS[@]}"; do
|
||||||
# Found a project root, return its name
|
if [[ -e "$current_dir/$indicator" ]]; then
|
||||||
basename "$current_dir"
|
monorepo_root="$current_dir"
|
||||||
return 0
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
# Move up one level
|
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
|
||||||
|
# (only stop if we're beyond reasonable depth)
|
||||||
|
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")
|
current_dir=$(dirname "$current_dir")
|
||||||
done
|
done
|
||||||
|
|
||||||
# Fallback: try the old logic (first directory under search root)
|
# Determine result: monorepo > project > fallback
|
||||||
local search_roots=()
|
local result=""
|
||||||
if [[ ${#PURGE_SEARCH_PATHS[@]} -gt 0 ]]; then
|
if [[ -n "$monorepo_root" ]]; then
|
||||||
search_roots=("${PURGE_SEARCH_PATHS[@]}")
|
result=$(basename "$monorepo_root")
|
||||||
|
elif [[ -n "$project_root" ]]; then
|
||||||
|
result=$(basename "$project_root")
|
||||||
else
|
else
|
||||||
search_roots=("$HOME/www" "$HOME/dev" "$HOME/Projects")
|
# Fallback: first directory under search root
|
||||||
|
local search_roots=()
|
||||||
|
if [[ ${#PURGE_SEARCH_PATHS[@]} -gt 0 ]]; then
|
||||||
|
search_roots=("${PURGE_SEARCH_PATHS[@]}")
|
||||||
|
else
|
||||||
|
search_roots=("$HOME/www" "$HOME/dev" "$HOME/Projects")
|
||||||
|
fi
|
||||||
|
for root in "${search_roots[@]}"; do
|
||||||
|
root="${root%/}"
|
||||||
|
if [[ -n "$root" && "$path" == "$root/"* ]]; then
|
||||||
|
local relative_path="${path#"$root"/}"
|
||||||
|
result=$(echo "$relative_path" | cut -d'/' -f1)
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Final fallback: use grandparent directory
|
||||||
|
if [[ -z "$result" ]]; then
|
||||||
|
result=$(dirname "$(dirname "$path")" | xargs basename)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
for root in "${search_roots[@]}"; do
|
|
||||||
root="${root%/}"
|
echo "$result"
|
||||||
if [[ -n "$root" && "$path" == "$root/"* ]]; then
|
}
|
||||||
local relative_path="${path#"$root"/}"
|
|
||||||
echo "$relative_path" | cut -d'/' -f1
|
# Helper to get artifact display name
|
||||||
return 0
|
# For duplicate artifact names within same project, include parent directory for context
|
||||||
|
get_artifact_display_name() {
|
||||||
|
local path="$1"
|
||||||
|
local artifact_name=$(basename "$path")
|
||||||
|
local project_name=$(get_project_name "$path")
|
||||||
|
local parent_name=$(basename "$(dirname "$path")")
|
||||||
|
|
||||||
|
# Check if there are other items with same artifact name AND same project
|
||||||
|
local has_duplicate=false
|
||||||
|
for other_item in "${safe_to_clean[@]}"; do
|
||||||
|
if [[ "$other_item" != "$path" && "$(basename "$other_item")" == "$artifact_name" ]]; then
|
||||||
|
# Same artifact name, check if same project
|
||||||
|
if [[ "$(get_project_name "$other_item")" == "$project_name" ]]; then
|
||||||
|
has_duplicate=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Final fallback: use grandparent directory
|
# If duplicate exists in same project and parent is not the project itself, show parent/artifact
|
||||||
dirname "$(dirname "$path")" | xargs basename
|
if [[ "$has_duplicate" == "true" && "$parent_name" != "$project_name" && "$parent_name" != "." && "$parent_name" != "/" ]]; then
|
||||||
|
echo "$parent_name/$artifact_name"
|
||||||
|
else
|
||||||
|
echo "$artifact_name"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
# Format display with alignment (like app_selector)
|
# Format display with alignment (like app_selector)
|
||||||
format_purge_display() {
|
format_purge_display() {
|
||||||
@@ -851,7 +934,7 @@ clean_project_artifacts() {
|
|||||||
local size_str="$3"
|
local size_str="$3"
|
||||||
# Terminal width for alignment
|
# Terminal width for alignment
|
||||||
local terminal_width=$(tput cols 2> /dev/null || echo 80)
|
local terminal_width=$(tput cols 2> /dev/null || echo 80)
|
||||||
local fixed_width=28 # Reserve for type and size
|
local fixed_width=38 # Reserve for type, size, and potential "| Recent" (28 + 10)
|
||||||
local available_width=$((terminal_width - fixed_width))
|
local available_width=$((terminal_width - fixed_width))
|
||||||
# Bounds: 24-35 chars for project name
|
# Bounds: 24-35 chars for project name
|
||||||
[[ $available_width -lt 24 ]] && available_width=24
|
[[ $available_width -lt 24 ]] && available_width=24
|
||||||
@@ -868,8 +951,14 @@ clean_project_artifacts() {
|
|||||||
# Build menu options - one line per artifact
|
# Build menu options - one line per artifact
|
||||||
for item in "${safe_to_clean[@]}"; do
|
for item in "${safe_to_clean[@]}"; do
|
||||||
local project_name=$(get_project_name "$item")
|
local project_name=$(get_project_name "$item")
|
||||||
local artifact_type=$(basename "$item")
|
local artifact_type=$(get_artifact_display_name "$item")
|
||||||
local size_kb=$(get_dir_size_kb "$item")
|
local size_kb=$(get_dir_size_kb "$item")
|
||||||
|
|
||||||
|
# Skip empty directories (0 bytes)
|
||||||
|
if [[ $size_kb -eq 0 ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
# Check if recent
|
# Check if recent
|
||||||
local is_recent=false
|
local is_recent=false
|
||||||
|
|||||||
Reference in New Issue
Block a user