mirror of
https://github.com/tw93/Mole.git
synced 2026-02-11 03:39:16 +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
|
# Get brand-friendly name for an application
|
||||||
# Args: $1 - application name
|
# Args: $1 - application name
|
||||||
# Returns: branded name if mapping exists, original name otherwise
|
# Returns: localized name based on system language preference
|
||||||
get_brand_name() {
|
get_brand_name() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
|
|
||||||
case "$name" in
|
# Detect if system primary language is Chinese
|
||||||
"qiyimac" | "爱奇艺") echo "iQiyi" ;;
|
local is_chinese=false
|
||||||
"wechat" | "微信") echo "WeChat" ;;
|
local sys_lang
|
||||||
"QQ") echo "QQ" ;;
|
sys_lang=$(defaults read -g AppleLanguages 2>/dev/null | grep -o 'zh-Hans\|zh-Hant\|zh' | head -1 || echo "")
|
||||||
"VooV Meeting" | "腾讯会议") echo "VooV Meeting" ;;
|
[[ -n "$sys_lang" ]] && is_chinese=true
|
||||||
"dingtalk" | "钉钉") echo "DingTalk" ;;
|
|
||||||
"NeteaseMusic" | "网易云音乐") echo "NetEase Music" ;;
|
# Return localized names based on system language
|
||||||
"BaiduNetdisk" | "百度网盘") echo "Baidu NetDisk" ;;
|
if [[ "$is_chinese" == true ]]; then
|
||||||
"alipay" | "支付宝") echo "Alipay" ;;
|
# Chinese system - prefer Chinese names
|
||||||
"taobao" | "淘宝") echo "Taobao" ;;
|
case "$name" in
|
||||||
"futunn" | "富途牛牛") echo "Futu NiuNiu" ;;
|
"qiyimac" | "iQiyi") echo "爱奇艺" ;;
|
||||||
"tencent lemon" | "Tencent Lemon Cleaner") echo "Tencent Lemon" ;;
|
"wechat" | "WeChat") echo "微信" ;;
|
||||||
"keynote" | "Keynote") echo "Keynote" ;;
|
"QQ") echo "QQ" ;;
|
||||||
"pages" | "Pages") echo "Pages" ;;
|
"VooV Meeting") echo "腾讯会议" ;;
|
||||||
"numbers" | "Numbers") echo "Numbers" ;;
|
"dingtalk" | "DingTalk") echo "钉钉" ;;
|
||||||
*) echo "$name" ;;
|
"NeteaseMusic" | "NetEase Music") echo "网易云音乐" ;;
|
||||||
esac
|
"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
|
# Save original terminal settings and ensure they're restored on exit
|
||||||
local stty_orig
|
local stty_orig
|
||||||
stty_orig=$(stty -g < "$tty_path" 2> /dev/null || echo "")
|
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
|
while ((attempts < 3)); do
|
||||||
local password=""
|
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; }
|
hide_cursor() { [[ -t 1 ]] && printf '\033[?25l' >&2 || true; }
|
||||||
show_cursor() { [[ -t 1 ]] && printf '\033[?25h' >&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
|
# Keyboard input - read single keypress
|
||||||
read_key() {
|
read_key() {
|
||||||
local key rest read_status
|
local key rest read_status
|
||||||
|
|||||||
@@ -130,9 +130,10 @@ batch_uninstall_applications() {
|
|||||||
running_apps+=("$app_name")
|
running_apps+=("$app_name")
|
||||||
fi
|
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
|
if [[ ! -w "$(dirname "$app_path")" ]] || [[ "$(get_file_owner "$app_path")" == "root" ]]; then
|
||||||
sudo_apps+=("$app_name")
|
needs_sudo=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Calculate size for summary (including system files)
|
# Calculate size for summary (including system files)
|
||||||
@@ -150,6 +151,10 @@ batch_uninstall_applications() {
|
|||||||
# Check if system files require sudo
|
# Check if system files require sudo
|
||||||
# shellcheck disable=SC2128
|
# shellcheck disable=SC2128
|
||||||
if [[ -n "$system_files" ]]; then
|
if [[ -n "$system_files" ]]; then
|
||||||
|
needs_sudo=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$needs_sudo" == "true" ]]; then
|
||||||
sudo_apps+=("$app_name")
|
sudo_apps+=("$app_name")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
4
mole
4
mole
@@ -22,8 +22,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
source "$SCRIPT_DIR/lib/core/common.sh"
|
source "$SCRIPT_DIR/lib/core/common.sh"
|
||||||
|
|
||||||
# Version info
|
# Version info
|
||||||
VERSION="1.13.3"
|
VERSION="1.13.4"
|
||||||
MOLE_TAGLINE="can dig deep to clean your Mac."
|
MOLE_TAGLINE="Deep clean and optimize your Mac."
|
||||||
|
|
||||||
# Check if Touch ID is already configured
|
# Check if Touch ID is already configured
|
||||||
is_touchid_configured() {
|
is_touchid_configured() {
|
||||||
|
|||||||
Reference in New Issue
Block a user