diff --git a/homebrew/README.md b/homebrew/README.md index 0296f74..46a8206 100644 --- a/homebrew/README.md +++ b/homebrew/README.md @@ -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 等支持相应信息的终端都能走通用回退。深色背景使用偏亮色阶,浅色背景使用更深的色阶,避免黄色、青色在白底上难以辨认。 如果终端屏蔽了背景色查询,或者需要固定配色,可以手动覆盖: diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index 4db9eea..36cbd07 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -59,9 +59,206 @@ query_terminal_background_rgb() { 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 red green blue brightness + local red green blue brightness system_theme="" case "$requested" in dark|light) @@ -72,6 +269,17 @@ detect_terminal_theme() { *) requested=auto ;; 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 的最后一项是背景色索引,常见终端会主动提供。 if [[ -n "${COLORFGBG:-}" ]]; then background_index="${COLORFGBG##*[;:]}" @@ -115,20 +323,15 @@ detect_terminal_theme() { return fi - # 终端未报告背景时,用 macOS 外观作合理回退;其他平台默认深色。 - if [[ "$(uname -s 2>/dev/null || true)" == "Darwin" ]] && \ - [[ "$(defaults read -g AppleInterfaceStyle 2>/dev/null || true)" != "Dark" ]]; then - printf 'light' - else - printf 'dark' - fi + # 所有自动检测均不可用时默认深色,也可用 HB_COLOR_THEME 手动覆盖。 + printf 'dark' } COLOR_THEME="none" if [[ -t 1 ]]; then COLOR_COUNT="$(tput colors 2>/dev/null || true)" [[ "$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 # 浅色背景使用更深的色阶,尤其避免亮黄、亮青在白底上失去对比。 RED='\033[38;5;124m'