refactor(homebrew): 重构 Homebrew 升级管理脚本的配色和清理策略
- 实现终端主题检测功能,支持深色/浅色模式自动切换配色方案 - 添加 256 色终端支持,优化不同背景下的颜色对比度 - 引入版本变更高亮显示,突出包名、旧版本、新版本的变化 - 集成 OSC 11 协议查询终端背景色,提升跨终端兼容性 - 将清理策略从按日期清理改为统一执行 brew cleanup --prune=all - 移除自定义缓存清理逻辑,使用 Homebrew 内置清理机制 - 增强输出着色功能,按语义对 Homebrew 输出进行彩色标记 - 更新文档说明新的清理流程和终端配色特性
This commit is contained in:
+3
-1
@@ -104,7 +104,9 @@ HB_TERMINAL_WIDTH=130 ./brew-upgrade-manager.sh
|
|||||||
|
|
||||||
## 终端主题与配色
|
## 终端主题与配色
|
||||||
|
|
||||||
主脚本默认使用 `HB_COLOR_THEME=auto`。它会优先读取终端提供的背景信息,并通过标准 OSC 11 查询兼容 Apple Terminal、iTerm2、Termius、Kitty、WezTerm、Alacritty 等常见终端;在 SSH 中,终端客户端允许响应透传时也能识别本地背景。深色背景使用偏亮色阶,浅色背景使用更深的色阶,避免黄色、青色在白底上难以辨认。
|
主脚本默认使用 `HB_COLOR_THEME=auto`,优先跟随操作系统当前的日间/夜间外观:支持 macOS、iOS/iPadOS、Windows、WSL、Android,以及使用 freedesktop Portal、GNOME、KDE、Cinnamon 或 XFCE 的 Linux 桌面。
|
||||||
|
|
||||||
|
SSH 会话会直接以客户端终端背景为准,因为远端系统的日夜状态不代表 iPhone、iPad 或 Android 客户端当前主题。其他无法读取系统外观的环境(例如容器和无桌面服务器)也会继续读取 `COLORFGBG`,并通过标准 OSC 11 查询终端当前的真实背景色。Apple Terminal、iTerm2、Termius、Blink、a-Shell、iSH、Kitty、WezTerm、Alacritty 等支持相应信息的终端都能走通用回退。深色背景使用偏亮色阶,浅色背景使用更深的色阶,避免黄色、青色在白底上难以辨认。
|
||||||
|
|
||||||
如果终端屏蔽了背景色查询,或者需要固定配色,可以手动覆盖:
|
如果终端屏蔽了背景色查询,或者需要固定配色,可以手动覆盖:
|
||||||
|
|
||||||
|
|||||||
@@ -59,9 +59,206 @@ query_terminal_background_rgb() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_terminal_theme() {
|
lowercase() {
|
||||||
|
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_apple_appearance_theme() {
|
||||||
|
local appearance="" product_name=""
|
||||||
|
|
||||||
|
[[ "$(uname -s 2>/dev/null || true)" == "Darwin" ]] || return 1
|
||||||
|
command -v defaults >/dev/null 2>&1 || return 1
|
||||||
|
|
||||||
|
product_name="$(sw_vers -productName 2>/dev/null || true)"
|
||||||
|
appearance="$(defaults read -g AppleInterfaceStyle 2>/dev/null || true)"
|
||||||
|
if [[ -z "$appearance" && \
|
||||||
|
("$product_name" == *"iPhone"* || "$product_name" == *"iOS"* || "$product_name" == *"iPad"*) ]]; then
|
||||||
|
appearance="$(defaults read /var/mobile/Library/Preferences/.GlobalPreferences AppleInterfaceStyle 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
case "$appearance" in
|
||||||
|
Dark|dark) printf 'dark' ;;
|
||||||
|
Light|light) printf 'light' ;;
|
||||||
|
'')
|
||||||
|
# macOS 中该键不存在代表浅色;iOS/iPadOS 无权读取时交给终端检测。
|
||||||
|
if [[ "$product_name" == *"iPhone"* || "$product_name" == *"iOS"* || "$product_name" == *"iPad"* ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
printf 'light'
|
||||||
|
;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_windows_appearance_theme() {
|
||||||
|
local powershell="" appearance=""
|
||||||
|
|
||||||
|
if command -v powershell.exe >/dev/null 2>&1; then
|
||||||
|
powershell="powershell.exe"
|
||||||
|
elif command -v powershell >/dev/null 2>&1; then
|
||||||
|
powershell="powershell"
|
||||||
|
fi
|
||||||
|
[[ -n "$powershell" ]] || return 1
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016 # The single-quoted variables belong to PowerShell.
|
||||||
|
appearance="$($powershell -NoLogo -NoProfile -NonInteractive -Command \
|
||||||
|
'$theme = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" -ErrorAction SilentlyContinue; if ($null -ne $theme.SystemUsesLightTheme) { [Console]::Write($theme.SystemUsesLightTheme) } elseif ($null -ne $theme.AppsUseLightTheme) { [Console]::Write($theme.AppsUseLightTheme) }' \
|
||||||
|
2>/dev/null || true)"
|
||||||
|
appearance="${appearance//$'\r'/}"
|
||||||
|
appearance="${appearance//[[:space:]]/}"
|
||||||
|
case "$appearance" in
|
||||||
|
0) printf 'dark' ;;
|
||||||
|
1) printf 'light' ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
theme_from_rgb_255() {
|
||||||
|
local value="$1" red green blue brightness
|
||||||
|
|
||||||
|
value="${value//,/ }"
|
||||||
|
read -r red green blue <<< "$value"
|
||||||
|
if [[ "$red" =~ ^[0-9]+$ && "$green" =~ ^[0-9]+$ && "$blue" =~ ^[0-9]+$ ]] && \
|
||||||
|
((red <= 255 && green <= 255 && blue <= 255)); then
|
||||||
|
brightness=$(((red * 299 + green * 587 + blue * 114) / 1000))
|
||||||
|
if ((brightness >= 128)); then
|
||||||
|
printf 'light'
|
||||||
|
else
|
||||||
|
printf 'dark'
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_linux_appearance_theme() {
|
||||||
|
local setting="" setting_lower="" theme_name="" theme_name_lower=""
|
||||||
|
local kde_reader="" background=""
|
||||||
|
|
||||||
|
# 无图形会话时,gsettings 可能只返回默认值而非真实主题;此时应让
|
||||||
|
# SSH/容器继续使用终端背景检测。
|
||||||
|
if [[ -z "${XDG_CURRENT_DESKTOP:-}" && -z "${DESKTOP_SESSION:-}" && \
|
||||||
|
-z "${DISPLAY:-}" && -z "${WAYLAND_DISPLAY:-}" && \
|
||||||
|
-z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# freedesktop Appearance Portal:GNOME、KDE 和多数 Wayland 桌面的统一接口。
|
||||||
|
if command -v gdbus >/dev/null 2>&1; then
|
||||||
|
setting="$(gdbus call --session \
|
||||||
|
--dest org.freedesktop.portal.Desktop \
|
||||||
|
--object-path /org/freedesktop/portal/desktop \
|
||||||
|
--method org.freedesktop.portal.Settings.Read \
|
||||||
|
org.freedesktop.appearance color-scheme 2>/dev/null || true)"
|
||||||
|
case "$setting" in
|
||||||
|
*'uint32 1'*) printf 'dark'; return ;;
|
||||||
|
*'uint32 2'*) printf 'light'; return ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# GNOME 42+ / Cinnamon 以及兼容 gsettings 的 GTK 桌面。
|
||||||
|
if command -v gsettings >/dev/null 2>&1; then
|
||||||
|
setting="$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null || true)"
|
||||||
|
setting_lower="$(lowercase "$setting")"
|
||||||
|
case "$setting_lower" in
|
||||||
|
*prefer-dark*) printf 'dark'; return ;;
|
||||||
|
*prefer-light*) printf 'light'; return ;;
|
||||||
|
esac
|
||||||
|
theme_name="$(gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null || true)"
|
||||||
|
if [[ -z "$theme_name" ]]; then
|
||||||
|
theme_name="$(gsettings get org.cinnamon.desktop.interface gtk-theme 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [[ -n "$theme_name" ]]; then
|
||||||
|
theme_name_lower="$(lowercase "$theme_name")"
|
||||||
|
if [[ "$theme_name_lower" == *dark* ]]; then
|
||||||
|
printf 'dark'
|
||||||
|
else
|
||||||
|
printf 'light'
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# KDE Plasma 6/5:先看配色方案名,再读取窗口背景色作判断。
|
||||||
|
if command -v kreadconfig6 >/dev/null 2>&1; then
|
||||||
|
kde_reader="kreadconfig6"
|
||||||
|
elif command -v kreadconfig5 >/dev/null 2>&1; then
|
||||||
|
kde_reader="kreadconfig5"
|
||||||
|
fi
|
||||||
|
if [[ -n "$kde_reader" ]]; then
|
||||||
|
theme_name="$($kde_reader --file kdeglobals --group General --key ColorScheme 2>/dev/null || true)"
|
||||||
|
theme_name_lower="$(lowercase "$theme_name")"
|
||||||
|
case "$theme_name_lower" in
|
||||||
|
*dark*) printf 'dark'; return ;;
|
||||||
|
*light*) printf 'light'; return ;;
|
||||||
|
esac
|
||||||
|
background="$($kde_reader --file kdeglobals --group 'Colors:Window' --key BackgroundNormal 2>/dev/null || true)"
|
||||||
|
if [[ -n "$background" ]]; then
|
||||||
|
theme_from_rgb_255 "$background" && return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# XFCE 的主题名通常直接包含 Dark;存在非 Dark 主题时按浅色处理。
|
||||||
|
if command -v xfconf-query >/dev/null 2>&1; then
|
||||||
|
theme_name="$(xfconf-query -c xsettings -p /Net/ThemeName 2>/dev/null || true)"
|
||||||
|
if [[ -n "$theme_name" ]]; then
|
||||||
|
theme_name_lower="$(lowercase "$theme_name")"
|
||||||
|
if [[ "$theme_name_lower" == *dark* ]]; then
|
||||||
|
printf 'dark'
|
||||||
|
else
|
||||||
|
printf 'light'
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_android_appearance_theme() {
|
||||||
|
local night_mode="" night_mode_lower=""
|
||||||
|
|
||||||
|
command -v cmd >/dev/null 2>&1 || return 1
|
||||||
|
night_mode="$(cmd uimode night 2>/dev/null || true)"
|
||||||
|
night_mode_lower="$(lowercase "$night_mode")"
|
||||||
|
case "$night_mode_lower" in
|
||||||
|
*'night mode: yes'*) printf 'dark' ;;
|
||||||
|
*'night mode: no'*) printf 'light' ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_system_appearance_theme() {
|
||||||
|
local kernel
|
||||||
|
|
||||||
|
kernel="$(uname -s 2>/dev/null || true)"
|
||||||
|
case "$kernel" in
|
||||||
|
Darwin)
|
||||||
|
detect_apple_appearance_theme
|
||||||
|
;;
|
||||||
|
Linux)
|
||||||
|
if [[ -n "${WSL_DISTRO_NAME:-}" || "$(uname -r 2>/dev/null || true)" == *[Mm]icrosoft* ]]; then
|
||||||
|
detect_windows_appearance_theme && return
|
||||||
|
fi
|
||||||
|
if [[ -n "${ANDROID_ROOT:-}" || -n "${TERMUX_VERSION:-}" ]]; then
|
||||||
|
detect_android_appearance_theme && return
|
||||||
|
fi
|
||||||
|
detect_linux_appearance_theme
|
||||||
|
;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*)
|
||||||
|
detect_windows_appearance_theme
|
||||||
|
;;
|
||||||
|
FreeBSD|OpenBSD|NetBSD|DragonFly|SunOS)
|
||||||
|
# BSD/Unix 桌面也可能提供 freedesktop、GTK、KDE 或 XFCE 接口。
|
||||||
|
detect_linux_appearance_theme
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_color_theme() {
|
||||||
local requested="${HB_COLOR_THEME:-auto}" background_index="" rgb=""
|
local requested="${HB_COLOR_THEME:-auto}" background_index="" rgb=""
|
||||||
local red green blue brightness
|
local red green blue brightness system_theme=""
|
||||||
|
|
||||||
case "$requested" in
|
case "$requested" in
|
||||||
dark|light)
|
dark|light)
|
||||||
@@ -72,6 +269,17 @@ detect_terminal_theme() {
|
|||||||
*) requested=auto ;;
|
*) requested=auto ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# 本地运行优先跟随操作系统外观。SSH 中远端系统状态不代表客户端
|
||||||
|
# (例如 iOS/Android 上的 Termius),因此直接交给后面的终端背景检测。
|
||||||
|
if [[ -z "${SSH_TTY:-}" && -z "${SSH_CONNECTION:-}" && -z "${SSH_CLIENT:-}" ]]; then
|
||||||
|
system_theme="$(detect_system_appearance_theme || true)"
|
||||||
|
if [[ "$system_theme" == "dark" || "$system_theme" == "light" ]]; then
|
||||||
|
printf '%s' "$system_theme"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 系统状态读取失败时,再查询终端自身提供的背景信息。
|
||||||
# COLORFGBG 的最后一项是背景色索引,常见终端会主动提供。
|
# COLORFGBG 的最后一项是背景色索引,常见终端会主动提供。
|
||||||
if [[ -n "${COLORFGBG:-}" ]]; then
|
if [[ -n "${COLORFGBG:-}" ]]; then
|
||||||
background_index="${COLORFGBG##*[;:]}"
|
background_index="${COLORFGBG##*[;:]}"
|
||||||
@@ -115,20 +323,15 @@ detect_terminal_theme() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 终端未报告背景时,用 macOS 外观作合理回退;其他平台默认深色。
|
# 所有自动检测均不可用时默认深色,也可用 HB_COLOR_THEME 手动覆盖。
|
||||||
if [[ "$(uname -s 2>/dev/null || true)" == "Darwin" ]] && \
|
printf 'dark'
|
||||||
[[ "$(defaults read -g AppleInterfaceStyle 2>/dev/null || true)" != "Dark" ]]; then
|
|
||||||
printf 'light'
|
|
||||||
else
|
|
||||||
printf 'dark'
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
COLOR_THEME="none"
|
COLOR_THEME="none"
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
COLOR_COUNT="$(tput colors 2>/dev/null || true)"
|
COLOR_COUNT="$(tput colors 2>/dev/null || true)"
|
||||||
[[ "$COLOR_COUNT" =~ ^[0-9]+$ ]] || COLOR_COUNT=8
|
[[ "$COLOR_COUNT" =~ ^[0-9]+$ ]] || COLOR_COUNT=8
|
||||||
COLOR_THEME="$(detect_terminal_theme)"
|
COLOR_THEME="$(detect_color_theme)"
|
||||||
if ((COLOR_COUNT >= 256)) && [[ "$COLOR_THEME" == "light" ]]; then
|
if ((COLOR_COUNT >= 256)) && [[ "$COLOR_THEME" == "light" ]]; then
|
||||||
# 浅色背景使用更深的色阶,尤其避免亮黄、亮青在白底上失去对比。
|
# 浅色背景使用更深的色阶,尤其避免亮黄、亮青在白底上失去对比。
|
||||||
RED='\033[38;5;124m'
|
RED='\033[38;5;124m'
|
||||||
|
|||||||
Reference in New Issue
Block a user