From 659ac556be8f525b892dfda586f92d32af4fd47f Mon Sep 17 00:00:00 2001 From: Arvin <62139570+wnlen@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:11:07 +0800 Subject: [PATCH] fix(ui): restore utf-8 encoding and remove mojibake in select output --- .editorconfig | 9 +++ .gitattributes | 2 +- .github/scripts/check-text-encoding.sh | 30 ++++++++ .github/workflows/text-encoding.yml | 19 +++++ scripts/core/clashctl.sh | 102 ++++++++++++------------- scripts/core/proxy.sh | 10 +-- scripts/dev/check-text-encoding.sh | 4 + 7 files changed, 119 insertions(+), 57 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/scripts/check-text-encoding.sh create mode 100644 .github/workflows/text-encoding.yml create mode 100644 scripts/dev/check-text-encoding.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..acf3bcf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +[*.{sh,yaml,yml,env,md}] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes index d9e72e2..9ed3e6c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -*.sh text eol=lf +*.sh text eol=lf working-tree-encoding=UTF-8 *.yaml text eol=lf *.yml text eol=lf *.env text eol=lf diff --git a/.github/scripts/check-text-encoding.sh b/.github/scripts/check-text-encoding.sh new file mode 100644 index 0000000..d071e54 --- /dev/null +++ b/.github/scripts/check-text-encoding.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +export LANG=C.UTF-8 +export LC_ALL=C.UTF-8 + +if grep -R -n '馃\|鑺\|绛\|閫\|璇\|摝\|殌' scripts/; then + echo "Detected mojibake text in scripts/" >&2 + exit 1 +fi + +python3 - <<'PY' +from pathlib import Path +import sys + +bad = [] +for path in Path("scripts").rglob("*.sh"): + data = path.read_bytes() + if data.startswith(b"\xef\xbb\xbf"): + bad.append(f"{path}: UTF-8 BOM is not allowed") + continue + try: + data.decode("utf-8") + except UnicodeDecodeError as exc: + bad.append(f"{path}: invalid UTF-8 ({exc})") + +if bad: + print("\n".join(bad), file=sys.stderr) + sys.exit(1) +PY diff --git a/.github/workflows/text-encoding.yml b/.github/workflows/text-encoding.yml new file mode 100644 index 0000000..06f87b9 --- /dev/null +++ b/.github/workflows/text-encoding.yml @@ -0,0 +1,19 @@ +name: Text Encoding + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + utf8: + runs-on: ubuntu-latest + env: + LANG: C.UTF-8 + LC_ALL: C.UTF-8 + steps: + - uses: actions/checkout@v4 + - name: Check mojibake and UTF-8 encoding + run: bash .github/scripts/check-text-encoding.sh diff --git a/scripts/core/clashctl.sh b/scripts/core/clashctl.sh index f54c967..8bd0e96 100644 --- a/scripts/core/clashctl.sh +++ b/scripts/core/clashctl.sh @@ -6962,13 +6962,13 @@ print_select_feedback() { current="$(proxy_group_current_display "$group" 2>/dev/null || true)" - ui_title "馃殌 鑺傜偣鍒囨崲瀹屾垚" + ui_title "🚀 节点切换完成" if [ -n "${group:-}" ]; then - ui_kv "馃摝" "宸插垏鎹㈢瓥鐣ョ粍" "$group" + ui_kv "📦" "已切换策略组" "$group" fi - ui_kv "馃殌" "褰撳墠鑺傜偣" "${current:-}" + ui_kv "🚀" "当前节点" "${current:-}" main_feedback_runtime_state ui_next "clashctl status" @@ -6986,12 +6986,12 @@ status_current_proxy_brief() { return 0 fi - echo "鏈惎鍔?" + echo "未启动" return 0 fi if ! proxy_controller_reachable 2>/dev/null; then - echo "鎺у埗鍣ㄤ笉鍙闂?" + echo "控制器不可访问" return 0 fi @@ -7009,7 +7009,7 @@ status_current_proxy_brief() { return 0 fi - echo "鏆傛棤鍙垏鎹㈢瓥鐣ョ粍" + echo "暂无可切换策略组" } cmd_proxy_groups() { @@ -7018,25 +7018,25 @@ cmd_proxy_groups() { prepare if ! status_is_running; then - die_state "浠g悊鍐呮牳鏈繍琛?" "clashon" + die_state "代理内核未运行" "clashon" fi if ! proxy_controller_reachable 2>/dev/null; then - die_state "鎺у埗鍣ㄤ笉鍙闂?" "clashctl doctor" + die_state "控制器不可访问" "clashctl doctor" fi - ui_title "馃摝 绛栫暐缁勫垪琛?" - echo " 馃摝 鍚嶇О 绫诲瀷 褰撳墠鑺傜偣" + ui_title "📦 策略组列表" + echo " 📦 名称 类型 当前节点" while IFS= read -r group; do [ -n "${group:-}" ] || continue found="true" type="$(proxy_group_type "$group" 2>/dev/null || echo "unknown")" current="$(proxy_group_current_display "$group" 2>/dev/null || true)" - printf ' 馃摝 %-20s %-12s %s\n' "$group" "$type" "${current:-}" + printf ' 📦 %-20s %-12s %s\n' "$group" "$type" "${current:-}" done < <(proxy_group_list) if [ "$found" != "true" ]; then - echo " 馃摥 鏆傛棤鍙垏鎹㈢瓥鐣ョ粍" + echo " 📭 暂无可切换策略组" fi ui_blank @@ -7050,33 +7050,33 @@ cmd_proxy_current() { prepare if ! status_is_running; then - die_state "浠g悊鍐呮牳鏈繍琛?" "clashon" + die_state "代理内核未运行" "clashon" fi if ! proxy_controller_reachable 2>/dev/null; then - die_state "鎺у埗鍣ㄤ笉鍙闂?" "clashctl doctor" + die_state "控制器不可访问" "clashctl doctor" fi if [ -n "${1:-}" ]; then group="$1" current="$(proxy_group_current_display "$group" 2>/dev/null || true)" - ui_title "馃殌 褰撳墠鑺傜偣" - ui_kv "馃摝" "绛栫暐缁?" "$group" - ui_kv "馃殌" "褰撳墠鑺傜偣" "${current:-}" + ui_title "🚀 当前节点" + ui_kv "📦" "策略组" "$group" + ui_kv "🚀" "当前节点" "${current:-}" ui_blank return 0 fi - ui_title "馃殌 褰撳墠鑺傜偣鎬昏" + ui_title "🚀 当前节点总览" while IFS= read -r group; do [ -n "${group:-}" ] || continue found="true" current="$(proxy_group_current_display "$group" 2>/dev/null || true)" - printf ' 馃殌 %-20s %s\n' "$group" "${current:-}" + printf ' 🚀 %-20s %s\n' "$group" "${current:-}" done < <(proxy_group_list) if [ "$found" != "true" ]; then - echo " 馃摥 鏆傛棤鍙垏鎹㈢瓥鐣ョ粍" + echo " 📭 暂无可切换策略组" fi ui_blank @@ -7087,35 +7087,35 @@ cmd_proxy_nodes() { local current node found="false" prepare - [ -n "${group:-}" ] || die "璇蜂娇鐢?clashctl select 鍒囨崲鑺傜偣" + [ -n "${group:-}" ] || die "请使用 clashctl select 切换节点" if ! status_is_running; then - die_state "浠g悊鍐呮牳鏈繍琛?" "clashon" + die_state "代理内核未运行" "clashon" fi if ! proxy_controller_reachable 2>/dev/null; then - die_state "鎺у埗鍣ㄤ笉鍙闂?" "clashctl doctor" + die_state "控制器不可访问" "clashctl doctor" fi current="$(proxy_group_current_display "$group" 2>/dev/null || true)" - ui_title "馃殌 鍊欓€夎妭鐐瑰垪琛?" - ui_kv "馃摝" "绛栫暐缁?" "$group" - ui_kv "馃殌" "褰撳墠鑺傜偣" "${current:-}" + ui_title "🚀 候选节点列表" + ui_kv "📦" "策略组" "$group" + ui_kv "🚀" "当前节点" "${current:-}" ui_blank while IFS= read -r node; do [ -n "${node:-}" ] || continue found="true" if [ "$node" = "$current" ]; then - printf ' * 馃殌 %s\n' "$node" + printf ' * 🚀 %s\n' "$node" else - printf ' 馃殌 %s\n' "$node" + printf ' 🚀 %s\n' "$node" fi done < <(proxy_group_selectable_nodes "$group") if [ "$found" != "true" ]; then - echo " 馃摥 该策略组暂无可切换真实节点" + echo " 📭 该策略组暂无可切换真实节点" fi ui_blank @@ -7133,7 +7133,7 @@ proxy_pick_group_for_select() { groups+=("$group") done < <(proxy_group_display_list) - for group in "鑺傜偣閫夋嫨" "鑷姩閫夋嫨"; do + for group in "节点选择" "自动选择"; do if printf '%s\n' "${groups[@]}" | grep -Fxq "$group"; then ordered_groups+=("$group") fi @@ -7141,7 +7141,7 @@ proxy_pick_group_for_select() { for group in "${groups[@]}"; do case "$group" in - 鑺傜偣閫夋嫨|鑷姩閫夋嫨) + 节点选择|自动选择) continue ;; esac @@ -7151,18 +7151,18 @@ proxy_pick_group_for_select() { groups=("${ordered_groups[@]}") count="${#groups[@]}" - [ "$count" -gt 0 ] || die "馃摥 鏆傛棤鍙垏鎹㈢瓥鐣ョ粍" + [ "$count" -gt 0 ] || die "📭 暂无可切换策略组" - echo "馃摝 璇烽€夋嫨绛栫暐缁勶細" >&2 - echo "馃挕 閫氬父浼樺厛閫夋嫨锛氳妭鐐归€夋嫨" >&2 + echo "📦 请选择策略组:" >&2 + echo "💡 通常优先选择:节点选择" >&2 idx=1 for group in "${groups[@]}"; do current="$(proxy_group_current_display "$group" 2>/dev/null || true)" type="$(proxy_group_type_label "$group" 2>/dev/null || echo "unknown")" - printf ' %s) 馃摝 %s [%s] -> 馃殌 %s\n' "$idx" "$group" "$type" "${current:-}" >&2 + printf ' %s) 📦 %s [%s] -> 🚀 %s\n' "$idx" "$group" "$type" "${current:-}" >&2 idx=$((idx + 1)) done - echo " q) 閫€鍑?" >&2 + echo " q) 退出" >&2 echo >&2 idx="$(proxy_pick_index "$count")" || return 1 @@ -7178,15 +7178,15 @@ proxy_select_interactive_guarded() { prepare if ! status_is_running; then - die_state "浠g悊鍐呮牳鏈繍琛?" "clashon" + die_state "代理内核未运行" "clashon" fi if ! proxy_controller_reachable 2>/dev/null; then - die_state "鎺у埗鍣ㄤ笉鍙闂?" "clashctl doctor" + die_state "控制器不可访问" "clashctl doctor" fi if [ -z "${group:-}" ]; then - ui_title "馃殌 鑺傜偣鍒囨崲" + ui_title "🚀 节点切换" choose_group="true" fi @@ -7195,7 +7195,7 @@ proxy_select_interactive_guarded() { group="$(proxy_pick_group_for_select)" || return 0 fi - proxy_group_exists "$group" || die "绛栫暐缁勪笉瀛樺湪锛?group" + proxy_group_exists "$group" || die "策略组不存在:$group" current="$(proxy_group_current_display "$group" 2>/dev/null || true)" nodes=() @@ -7207,8 +7207,8 @@ proxy_select_interactive_guarded() { count="${#nodes[@]}" if [ "$count" -le 0 ]; then ui_warn "该策略组暂无可切换真实节点" - ui_kv "馃摝" "绛栫暐缁?" "$group" - ui_kv "馃殌" "褰撳墠鑺傜偣" "${current:-}" + ui_kv "📦" "策略组" "$group" + ui_kv "🚀" "当前节点" "${current:-}" ui_blank if [ "$choose_group" = "true" ]; then group="" @@ -7220,25 +7220,25 @@ proxy_select_interactive_guarded() { total_count="$(select_total_node_count 2>/dev/null || true)" echo - echo "馃摝 褰撳墠绛栫暐缁勶細$group" - echo "馃殌 褰撳墠鑺傜偣锛?${current:-}" - echo "馃摝 褰撳墠绛栫暐缁勫€欓€夋暟锛?count" - [ -n "${total_count:-}" ] && echo "馃敘 鍏ㄩ儴鑺傜偣鎬绘暟锛?total_count" - echo "鈩癸笍 浠ヤ笅浠呮樉绀哄綋鍓嶇瓥鐣ョ粍鍙垏鎹㈣妭鐐?" + echo "📦 当前策略组:$group" + echo "🚀 当前节点:${current:-}" + echo "📦 当前策略组候选数:$count" + [ -n "${total_count:-}" ] && echo "🔢 全部节点总数:$total_count" + echo "ℹ️ 以下仅显示当前策略组可切换节点" echo - echo "馃殌 璇烽€夋嫨鑺傜偣锛?" + echo "🚀 请选择节点:" idx=1 for node in "${nodes[@]}"; do if [ "$node" = "$current" ]; then - printf ' %s) 馃殌 %s [褰撳墠]\n' "$idx" "$node" + printf ' %s) 🚀 %s [当前]\n' "$idx" "$node" else - printf ' %s) 馃殌 %s\n' "$idx" "$node" + printf ' %s) 🚀 %s\n' "$idx" "$node" fi idx=$((idx + 1)) done - echo " q) 閫€鍑?" + echo " q) 退出" echo idx="$(proxy_pick_index "$count")" || return 0 diff --git a/scripts/core/proxy.sh b/scripts/core/proxy.sh index 451e935..0d23bbc 100644 --- a/scripts/core/proxy.sh +++ b/scripts/core/proxy.sh @@ -849,10 +849,10 @@ proxy_group_select() { local code response_file response_body local available_node found - [ -n "${group:-}" ] || die "绛栫暐缁勫悕绉颁笉鑳戒负绌?" - [ -n "${node:-}" ] || die "鑺傜偣鍚嶇О涓嶈兘涓虹┖" + [ -n "${group:-}" ] || die "策略组名称不能为空" + [ -n "${node:-}" ] || die "节点名称不能为空" - proxy_group_exists "$group" || die "绛栫暐缁勪笉瀛樺湪锛?group" + proxy_group_exists "$group" || die "策略组不存在:$group" proxy_group_can_show_candidates "$group" || die "$(proxy_group_manual_pick_error_message "$group")" proxy_node_is_selectable_candidate "$node" || die "节点不是可切换节点:$node" @@ -866,7 +866,7 @@ proxy_group_select() { done < <(proxy_group_selectable_nodes "$group") if [ "$found" != "true" ]; then - die "鑺傜偣涓嶅瓨鍦ㄤ簬绛栫暐缁勪腑锛?group -> $node" + die "节点不存在于策略组中:$group -> $node" fi base="$(controller_api_base)" @@ -886,7 +886,7 @@ proxy_group_select() { if [ -n "${response_body:-}" ]; then die "controller 原始错误:$response_body" fi - die "鑺傜偣鍒囨崲澶辫触锛歝ontroller 杩斿洖 HTTP $code" + die "节点切换失败:controller 返回 HTTP $code" fi rm -f "$response_file" 2>/dev/null || true diff --git a/scripts/dev/check-text-encoding.sh b/scripts/dev/check-text-encoding.sh new file mode 100644 index 0000000..163d925 --- /dev/null +++ b/scripts/dev/check-text-encoding.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exec bash .github/scripts/check-text-encoding.sh "$@"