mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 15:39:42 +00:00
refine sudo requirement checks for uninstallation.
This commit is contained in:
@@ -246,27 +246,53 @@ bytes_to_human_kb() {
|
||||
|
||||
# Get brand-friendly name for an application
|
||||
# Args: $1 - application name
|
||||
# Returns: branded name if mapping exists, original name otherwise
|
||||
# Returns: localized name based on system language preference
|
||||
get_brand_name() {
|
||||
local name="$1"
|
||||
|
||||
case "$name" in
|
||||
"qiyimac" | "爱奇艺") echo "iQiyi" ;;
|
||||
"wechat" | "微信") echo "WeChat" ;;
|
||||
"QQ") echo "QQ" ;;
|
||||
"VooV Meeting" | "腾讯会议") echo "VooV Meeting" ;;
|
||||
"dingtalk" | "钉钉") echo "DingTalk" ;;
|
||||
"NeteaseMusic" | "网易云音乐") echo "NetEase Music" ;;
|
||||
"BaiduNetdisk" | "百度网盘") echo "Baidu NetDisk" ;;
|
||||
"alipay" | "支付宝") echo "Alipay" ;;
|
||||
"taobao" | "淘宝") echo "Taobao" ;;
|
||||
"futunn" | "富途牛牛") echo "Futu NiuNiu" ;;
|
||||
"tencent lemon" | "Tencent Lemon Cleaner") echo "Tencent Lemon" ;;
|
||||
"keynote" | "Keynote") echo "Keynote" ;;
|
||||
"pages" | "Pages") echo "Pages" ;;
|
||||
"numbers" | "Numbers") echo "Numbers" ;;
|
||||
*) echo "$name" ;;
|
||||
esac
|
||||
# Detect if system primary language is Chinese
|
||||
local is_chinese=false
|
||||
local sys_lang
|
||||
sys_lang=$(defaults read -g AppleLanguages 2>/dev/null | grep -o 'zh-Hans\|zh-Hant\|zh' | head -1 || echo "")
|
||||
[[ -n "$sys_lang" ]] && is_chinese=true
|
||||
|
||||
# Return localized names based on system language
|
||||
if [[ "$is_chinese" == true ]]; then
|
||||
# Chinese system - prefer Chinese names
|
||||
case "$name" in
|
||||
"qiyimac" | "iQiyi") echo "爱奇艺" ;;
|
||||
"wechat" | "WeChat") echo "微信" ;;
|
||||
"QQ") echo "QQ" ;;
|
||||
"VooV Meeting") echo "腾讯会议" ;;
|
||||
"dingtalk" | "DingTalk") echo "钉钉" ;;
|
||||
"NeteaseMusic" | "NetEase Music") echo "网易云音乐" ;;
|
||||
"BaiduNetdisk" | "Baidu NetDisk") echo "百度网盘" ;;
|
||||
"alipay" | "Alipay") echo "支付宝" ;;
|
||||
"taobao" | "Taobao") echo "淘宝" ;;
|
||||
"futunn" | "Futu NiuNiu") echo "富途牛牛" ;;
|
||||
"tencent lemon" | "Tencent Lemon Cleaner" | "Tencent Lemon") echo "腾讯柠檬清理" ;;
|
||||
*) echo "$name" ;;
|
||||
esac
|
||||
else
|
||||
# Non-Chinese system - use English names
|
||||
case "$name" in
|
||||
"qiyimac" | "爱奇艺") echo "iQiyi" ;;
|
||||
"wechat" | "微信") echo "WeChat" ;;
|
||||
"QQ") echo "QQ" ;;
|
||||
"腾讯会议") echo "VooV Meeting" ;;
|
||||
"dingtalk" | "钉钉") echo "DingTalk" ;;
|
||||
"网易云音乐") echo "NetEase Music" ;;
|
||||
"百度网盘") echo "Baidu NetDisk" ;;
|
||||
"alipay" | "支付宝") echo "Alipay" ;;
|
||||
"taobao" | "淘宝") echo "Taobao" ;;
|
||||
"富途牛牛") echo "Futu NiuNiu" ;;
|
||||
"腾讯柠檬清理" | "Tencent Lemon Cleaner") echo "Tencent Lemon" ;;
|
||||
"keynote" | "Keynote") echo "Keynote" ;;
|
||||
"pages" | "Pages") echo "Pages" ;;
|
||||
"numbers" | "Numbers") echo "Numbers" ;;
|
||||
*) echo "$name" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -45,7 +45,7 @@ _request_password() {
|
||||
# Save original terminal settings and ensure they're restored on exit
|
||||
local stty_orig
|
||||
stty_orig=$(stty -g < "$tty_path" 2> /dev/null || echo "")
|
||||
trap '[[ -n "$stty_orig" ]] && stty "$stty_orig" < "$tty_path" 2> /dev/null || true' RETURN
|
||||
trap '[[ -n "${stty_orig:-}" ]] && stty "${stty_orig:-}" < "$tty_path" 2> /dev/null || true' RETURN
|
||||
|
||||
while ((attempts < 3)); do
|
||||
local password=""
|
||||
|
||||
152
lib/core/ui.sh
152
lib/core/ui.sh
@@ -17,6 +17,158 @@ clear_screen() { printf '\033[2J\033[H'; }
|
||||
hide_cursor() { [[ -t 1 ]] && printf '\033[?25l' >&2 || true; }
|
||||
show_cursor() { [[ -t 1 ]] && printf '\033[?25h' >&2 || true; }
|
||||
|
||||
# Calculate display width of a string (CJK characters count as 2)
|
||||
# Args: $1 - string to measure
|
||||
# Returns: display width
|
||||
# Note: Works correctly even when LC_ALL=C is set
|
||||
get_display_width() {
|
||||
local str="$1"
|
||||
|
||||
# Check Python availability once and cache the result
|
||||
# Use Python for accurate width calculation if available (cached check)
|
||||
if [[ -z "${MOLE_PYTHON_AVAILABLE:-}" ]]; then
|
||||
if command -v python3 > /dev/null 2>&1; then
|
||||
export MOLE_PYTHON_AVAILABLE=1
|
||||
else
|
||||
export MOLE_PYTHON_AVAILABLE=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${MOLE_PYTHON_AVAILABLE:-0}" == "1" ]]; then
|
||||
python3 -c "
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
s = sys.argv[1]
|
||||
width = 0
|
||||
for char in s:
|
||||
# East Asian Width property
|
||||
ea_width = unicodedata.east_asian_width(char)
|
||||
if ea_width in ('F', 'W'): # Fullwidth or Wide
|
||||
width += 2
|
||||
else:
|
||||
width += 1
|
||||
print(width)
|
||||
" "$str" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
# Fallback: Use wc with UTF-8 locale temporarily
|
||||
local saved_lc_all="${LC_ALL:-}"
|
||||
local saved_lang="${LANG:-}"
|
||||
|
||||
export LC_ALL=en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
|
||||
local char_count byte_count width
|
||||
char_count=$(printf '%s' "$str" | wc -m 2>/dev/null | tr -d ' ')
|
||||
byte_count=$(printf '%s' "$str" | wc -c 2>/dev/null | tr -d ' ')
|
||||
|
||||
# Restore locale
|
||||
if [[ -n "$saved_lc_all" ]]; then
|
||||
export LC_ALL="$saved_lc_all"
|
||||
else
|
||||
unset LC_ALL
|
||||
fi
|
||||
if [[ -n "$saved_lang" ]]; then
|
||||
export LANG="$saved_lang"
|
||||
else
|
||||
unset LANG
|
||||
fi
|
||||
|
||||
# Estimate: if byte_count > char_count, we have multibyte chars
|
||||
# Rough approximation: each multibyte char (CJK) is ~3 bytes and width 2
|
||||
# ASCII chars are 1 byte and width 1
|
||||
if [[ $byte_count -gt $char_count ]]; then
|
||||
local multibyte_chars=$((byte_count - char_count))
|
||||
# Assume most multibyte chars are 2 bytes extra (3 bytes total for UTF-8 CJK)
|
||||
local cjk_chars=$((multibyte_chars / 2))
|
||||
local ascii_chars=$((char_count - cjk_chars))
|
||||
width=$((ascii_chars + cjk_chars * 2))
|
||||
else
|
||||
width=$char_count
|
||||
fi
|
||||
|
||||
echo "$width"
|
||||
}
|
||||
|
||||
# Truncate string by display width (handles CJK correctly)
|
||||
# Args: $1 - string, $2 - max display width
|
||||
# Returns: truncated string with "..." if needed
|
||||
truncate_by_display_width() {
|
||||
local str="$1"
|
||||
local max_width="$2"
|
||||
local current_width
|
||||
current_width=$(get_display_width "$str")
|
||||
|
||||
if [[ $current_width -le $max_width ]]; then
|
||||
echo "$str"
|
||||
return
|
||||
fi
|
||||
|
||||
# Use Python for accurate truncation if available (use cached check)
|
||||
if [[ "${MOLE_PYTHON_AVAILABLE:-0}" == "1" ]]; then
|
||||
python3 -c "
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
s = sys.argv[1]
|
||||
max_w = int(sys.argv[2])
|
||||
result = ''
|
||||
width = 0
|
||||
|
||||
for char in s:
|
||||
ea_width = unicodedata.east_asian_width(char)
|
||||
char_width = 2 if ea_width in ('F', 'W') else 1
|
||||
|
||||
if width + char_width + 3 > max_w: # +3 for '...'
|
||||
break
|
||||
|
||||
result += char
|
||||
width += char_width
|
||||
|
||||
print(result + '...')
|
||||
" "$str" "$max_width" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
# Fallback: Use UTF-8 locale for proper string handling
|
||||
local saved_lc_all="${LC_ALL:-}"
|
||||
local saved_lang="${LANG:-}"
|
||||
export LC_ALL=en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
|
||||
local truncated=""
|
||||
local width=0
|
||||
local i=0
|
||||
local char char_width
|
||||
|
||||
while [[ $i -lt ${#str} ]]; do
|
||||
char="${str:$i:1}"
|
||||
char_width=$(get_display_width "$char")
|
||||
|
||||
if ((width + char_width + 3 > max_width)); then
|
||||
break
|
||||
fi
|
||||
|
||||
truncated+="$char"
|
||||
((width += char_width))
|
||||
((i++))
|
||||
done
|
||||
|
||||
# Restore locale
|
||||
if [[ -n "$saved_lc_all" ]]; then
|
||||
export LC_ALL="$saved_lc_all"
|
||||
else
|
||||
unset LC_ALL
|
||||
fi
|
||||
if [[ -n "$saved_lang" ]]; then
|
||||
export LANG="$saved_lang"
|
||||
else
|
||||
unset LANG
|
||||
fi
|
||||
|
||||
echo "${truncated}..."
|
||||
}
|
||||
|
||||
# Keyboard input - read single keypress
|
||||
read_key() {
|
||||
local key rest read_status
|
||||
|
||||
@@ -130,9 +130,10 @@ batch_uninstall_applications() {
|
||||
running_apps+=("$app_name")
|
||||
fi
|
||||
|
||||
# Check if app requires sudo to delete
|
||||
# Check if app requires sudo to delete (either app bundle or system files)
|
||||
local needs_sudo=false
|
||||
if [[ ! -w "$(dirname "$app_path")" ]] || [[ "$(get_file_owner "$app_path")" == "root" ]]; then
|
||||
sudo_apps+=("$app_name")
|
||||
needs_sudo=true
|
||||
fi
|
||||
|
||||
# Calculate size for summary (including system files)
|
||||
@@ -150,6 +151,10 @@ batch_uninstall_applications() {
|
||||
# Check if system files require sudo
|
||||
# shellcheck disable=SC2128
|
||||
if [[ -n "$system_files" ]]; then
|
||||
needs_sudo=true
|
||||
fi
|
||||
|
||||
if [[ "$needs_sudo" == "true" ]]; then
|
||||
sudo_apps+=("$app_name")
|
||||
fi
|
||||
|
||||
|
||||
4
mole
4
mole
@@ -22,8 +22,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/lib/core/common.sh"
|
||||
|
||||
# Version info
|
||||
VERSION="1.13.3"
|
||||
MOLE_TAGLINE="can dig deep to clean your Mac."
|
||||
VERSION="1.13.4"
|
||||
MOLE_TAGLINE="Deep clean and optimize your Mac."
|
||||
|
||||
# Check if Touch ID is already configured
|
||||
is_touchid_configured() {
|
||||
|
||||
Reference in New Issue
Block a user