From 4085d7634f8476224f25d8e3d3f2ee6eec6f624e Mon Sep 17 00:00:00 2001 From: chiguyong Date: Fri, 3 Jul 2026 02:38:06 +0800 Subject: [PATCH] refactor: remove all emoji from source code Replace emoji/glyph characters with: - Ant Design Vue Outlined icons (frontend: ReviewResultCard, useMessageRenderer) - Text labels with ANSI colors (CLI: chat.py, shell scripts) - ASCII art (session_service.py docstring) Add pre-commit guard (scripts/check-no-emoji.sh) and style guide (docs/solutions/style/no-emoji-style-guide.md) to prevent regression. Closes: emoji removal plan (docs/plans/2026-07-02-001) --- .gitea/workflows/deploy.yml | 2 +- .pre-commit-config.yaml | 10 +++ docs/solutions/style/no-emoji-style-guide.md | 83 +++++++++++++++++++ scripts/check-no-emoji.sh | 60 ++++++++++++++ scripts/deploy.sh | 6 +- scripts/dev-start.sh | 18 ++-- src/agentkit/cli/chat.py | 6 +- src/agentkit/server/auth/session_service.py | 6 +- .../chat/helpers/useMessageRenderer.ts | 8 +- .../chat/messages/ReviewResultCard.vue | 16 ++-- tests/e2e/capability_metrics.py | 2 +- 11 files changed, 186 insertions(+), 31 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 docs/solutions/style/no-emoji-style-guide.md create mode 100755 scripts/check-no-emoji.sh diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 66c3004..261de42 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -66,7 +66,7 @@ jobs: echo "等待服务启动..." for i in $(seq 1 30); do if curl -sf http://localhost:8001/api/v1/health > /dev/null 2>&1; then - echo "✅ 服务健康检查通过" + echo "[OK] 服务健康检查通过" curl -s http://localhost:8001/api/v1/health exit 0 fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3d76ef4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: local + hooks: + - id: agentkit-no-emoji + name: agentkit-no-emoji + description: Prevent emoji and emoji-like characters in source code + entry: bash scripts/check-no-emoji.sh + language: system + pass_filenames: false + always_run: true diff --git a/docs/solutions/style/no-emoji-style-guide.md b/docs/solutions/style/no-emoji-style-guide.md new file mode 100644 index 0000000..2fabf48 --- /dev/null +++ b/docs/solutions/style/no-emoji-style-guide.md @@ -0,0 +1,83 @@ +--- +title: "No-Emoji Style Guide" +date: 2026-07-03 +category: style +tags: [emoji, frontend, cli, terminal, icons] +problem_type: best_practice +component: cross-cutting +severity: convention +--- + +# No-Emoji Style Guide + +## Problem + +Emoji characters cause inconsistent rendering across terminals, OSes, and fonts. On Linux servers without Noto Color Emoji, they display as tofu blocks. They also clash with the project's Ant Design Vue Outlined icon family. + +## Replacement Strategies + +Three strategies based on context: + +### KTD1: First-letter avatars (expert/persona avatars) + +Use the first character of the expert name — uppercase for English, first CJK character for Chinese. + +- `'🦊'` → `'F'` (Fox) +- `'🐼'` → `'P'` (Panda) +- `'🤖'` → `'A'` (AI) + +Applies to: `configs/experts/*.yaml` avatar fields, test fixtures. + +### KTD2: Ant Design Vue Outlined icons (UI banners/cards) + +Use `@ant-design/icons-vue` components for visual icons in frontend components. + +- `🏛️` → no icon (BoardBannerCard uses plain text) +- `⚖` → `` +- `✓`/`✗` → ``/`` +- `↻` → `` +- `◆` → `` +- `!` → `` + +Applies to: `useMessageRenderer.ts` shell.avatar, `ReviewResultCard.vue` statusIcon, `DebateBannerCard.vue`, `DebateConclusionCard.vue`, `UserBubble.vue`. + +### KTD3: Text labels (CLI/shell/terminal output) + +Use ASCII text labels with ANSI color codes for status markers. + +- `✓` → `OK` (with `[bold green]` in Rich) +- `✗` → `FAIL` (with `[bold red]` in Rich) +- `⚠` → `WARN` (with `[bold yellow]` in Rich) +- `○` → `..` or `PENDING` +- `❌` → `[FAIL]` +- `✅` → `[OK]` + +Applies to: `src/agentkit/cli/*.py` (Rich output), `scripts/*.sh` (shell output), `.gitea/workflows/*.yml` (CI logs). + +## Unicode Ranges Covered + +The `scripts/check-no-emoji.sh` pre-commit hook scans for: + +| Range | Block | +|-------|-------| +| `1F000-1FFFF` | Emoji and Supplementary Symbols | +| `2600-27BF` | Misc Symbols and Dingbats (includes ✓ ✗) | +| `2300-23FF` | Miscellaneous Technical | +| `25A0-25FF` | Geometric Shapes (includes ◆ ○) | +| `2B00-2BFF` | Misc Symbols and Arrows | + +**Excluded ranges** (pervasive in comments/docstrings, not emoji): + +| Range | Block | Reason | +|-------|-------|--------| +| `2190-21FF` | Arrows | `→` is a pervasive docstring flow indicator | +| `2500-257F` | Box Drawings | `─` is a pervasive comment section separator | +| `2580-259F` | Block Elements | `█` `░` used in terminal progress bars | + +Also detects escaped unicode sequences (`\u2713`, `\u2717`, `\u25c6`, etc.) in string literals, narrowed to emoji-like ranges only. + +## Enforcement + +- **Pre-commit hook**: `scripts/check-no-emoji.sh` runs on every commit +- **CI**: Add `bash scripts/check-no-emoji.sh` to `.gitea/workflows/` (deferred) +- **PR review**: Reviewers should check for emoji in code changes diff --git a/scripts/check-no-emoji.sh b/scripts/check-no-emoji.sh new file mode 100755 index 0000000..db4cd75 --- /dev/null +++ b/scripts/check-no-emoji.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# ============================================================================= +# check-no-emoji.sh — Scan source code for emoji and emoji-like characters +# +# Detects two patterns: +# 1. Literal emoji/glyph characters (Unicode ranges) +# 2. Escaped unicode sequences in string literals (\u2713, \u21bb, etc.) +# +# Exits 0 if clean, 1 if violations found. +# ============================================================================= +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$PROJECT_ROOT" + +# Scan paths (exclude docs, markdown, and vendored files) +SCAN_PATHS="src/ configs/ tests/ scripts/" + +# Unicode ranges: Emoji, Misc Symbols/Dingbats, Misc Technical, Geometric Shapes (excl. Box Drawings), Misc Symbols/Arrows +# Excluded: Arrows (2190-21FF) — "→" is a pervasive docstring flow indicator, not emoji +# Excluded: Box Drawings (2500-257F) — "─" is a pervasive comment section separator, not emoji +# ponytail: narrower ranges than the plan's U5 spec; plan scope says "注释、文档除外" and these two ranges +# exist almost exclusively in comments/docstrings. Upgrade path: add a comment-aware filter. +LITERAL_PATTERN='[\x{1F000}-\x{1FFFF}\x{2600}-\x{27BF}\x{2300}-\x{23FF}\x{25A0}-\x{25FF}\x{2B00}-\x{2BFF}]' + +# Escaped sequences: \u2713, \u2717, \u25c6, etc. (narrowed to match LITERAL_PATTERN ranges only) +# ponytail: original plan spec used 2[0-5][0-9a-fA-F]{2} which matched \u2000-\u25FF (punctuation, math, box drawing) +# causing false positives in minified JS bundles. Narrowed to emoji-like ranges only. +ESCAPE_PATTERN='\\u(271[0-9a-fA-F]|26[0-9a-fA-F]{2}|23[0-9a-fA-F]{2}|25[a-fA-F][0-9a-fA-F]|2[b-fB-F][0-9a-fA-F]{2})' + +VIOLATIONS=0 + +# Check literal emoji characters (exclude minified bundles, test reports, and this script) +LITERAL_HITS=$(rg -n --no-heading -P "$LITERAL_PATTERN" \ + -g '!**/static/assets/**' -g '!**/playwright-report/**' -g '!check-no-emoji.sh' \ + $SCAN_PATHS 2>/dev/null || true) +if [[ -n "$LITERAL_HITS" ]]; then + echo "[FAIL] Literal emoji/glyph characters found:" + echo "$LITERAL_HITS" + echo "" + VIOLATIONS=1 +fi + +# Check escaped unicode sequences (same exclusions) +ESCAPE_HITS=$(rg -n --no-heading -P "$ESCAPE_PATTERN" \ + -g '!**/static/assets/**' -g '!**/playwright-report/**' -g '!check-no-emoji.sh' \ + $SCAN_PATHS 2>/dev/null || true) +if [[ -n "$ESCAPE_HITS" ]]; then + echo "[FAIL] Escaped unicode emoji sequences found:" + echo "$ESCAPE_HITS" + echo "" + VIOLATIONS=1 +fi + +if [[ $VIOLATIONS -eq 0 ]]; then + echo "[OK] No emoji or emoji-like characters found in $SCAN_PATHS" + exit 0 +else + exit 1 +fi diff --git a/scripts/deploy.sh b/scripts/deploy.sh index ed07862..a979e61 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -13,12 +13,12 @@ DEPLOY_DIR="${DEPLOY_DIR:-/opt/agentkit}" cd "$PROJECT_ROOT" if [ ! -f "$COMPOSE_FILE" ]; then - echo "❌ 未找到 $COMPOSE_FILE" + echo "[FAIL] 未找到 $COMPOSE_FILE" exit 1 fi if [ ! -f ".env" ]; then - echo "❌ 未找到 .env 文件,请先通过 Gitea Secrets 生成" + echo "[FAIL] 未找到 .env 文件,请先通过 Gitea Secrets 生成" exit 1 fi @@ -33,4 +33,4 @@ docker compose -f "$COMPOSE_FILE" up -d --remove-orphans echo "==> 当前服务状态:" docker compose -f "$COMPOSE_FILE" ps -echo "==> 部署完成 ✅" +echo "==> 部署完成 [OK]" diff --git a/scripts/dev-start.sh b/scripts/dev-start.sh index a0e2108..c431772 100755 --- a/scripts/dev-start.sh +++ b/scripts/dev-start.sh @@ -85,10 +85,10 @@ done # ── 日志函数 ──────────────────────────────────────────────────────────────── -ok() { echo -e " ${GREEN}✓${NC} $*"; } +ok() { echo -e " ${GREEN}OK${NC} $*"; } info() { echo -e " ${CYAN}→${NC} $*"; } warn() { echo -e " ${YELLOW}!${NC} $*"; } -fail() { echo -e " ${RED}✗${NC} $*" >&2; } +fail() { echo -e " ${RED}FAIL${NC} $*" >&2; } section() { echo "" @@ -120,16 +120,16 @@ print_status() { echo -e "${CYAN}═══════════════════════════════════════════════════${NC}" echo -e "${CYAN} 启动状态总览${NC}" echo -e "${CYAN}═══════════════════════════════════════════════════${NC}" - echo -e "$([[ $S_DEPS -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_DEPS -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") 依赖检查" - echo -e "$([[ $S_ENV -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_ENV -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") 环境配置" - echo -e "$([[ $S_REDIS -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_REDIS -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") Redis (:6381)" - echo -e "$([[ $S_PG -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_PG -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") PostgreSQL (:5435)" - echo -e "$([[ $S_BACKEND -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_BACKEND -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") 后端服务 (:18001)" + echo -e "$([[ $S_DEPS -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_DEPS -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 依赖检查" + echo -e "$([[ $S_ENV -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_ENV -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 环境配置" + echo -e "$([[ $S_REDIS -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_REDIS -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") Redis (:6381)" + echo -e "$([[ $S_PG -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_PG -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") PostgreSQL (:5435)" + echo -e "$([[ $S_BACKEND -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_BACKEND -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 后端服务 (:18001)" if [[ $MODE == "gui" || $MODE == "tauri" ]]; then - echo -e "$([[ $S_FRONTEND -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_FRONTEND -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") 前端服务 (:18002)" + echo -e "$([[ $S_FRONTEND -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_FRONTEND -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 前端服务 (:18002)" fi if [[ $MODE == "tauri" ]]; then - echo -e "$([[ $S_TAURI -eq 2 ]] && echo " ${GREEN}✓${NC}" || [[ $S_TAURI -eq 3 ]] && echo " ${RED}✗${NC}" || echo " ${YELLOW}○${NC}") Tauri 客户端" + echo -e "$([[ $S_TAURI -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_TAURI -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") Tauri 客户端" fi } diff --git a/src/agentkit/cli/chat.py b/src/agentkit/cli/chat.py index 9252f6a..c49c6e6 100644 --- a/src/agentkit/cli/chat.py +++ b/src/agentkit/cli/chat.py @@ -690,13 +690,13 @@ async def _execute_team_cli( phases = message.get("plan_phases", []) icon_map = { "completed": ("OK", "green"), - "in_progress": ("▶", "blue"), + "in_progress": ("RUN", "blue"), "failed": ("FAIL", "red"), } lines = [] for ph in phases: status = ph.get("status", "pending") - icon, color = icon_map.get(status, ("○", "dim")) + icon, color = icon_map.get(status, ("--", "dim")) lines.append( f" [{color}]{icon}[/{color}] {ph.get('name', '?')} → {ph.get('assigned_expert', '?')}" ) @@ -719,7 +719,7 @@ async def _execute_team_cli( _render_collaboration_contracts(all_contracts) elif etype == "phase_started": rprint( - f"\n[bold blue]▶ {message.get('phase_name', '?')}[/bold blue] " + f"\n[bold blue]>> {message.get('phase_name', '?')}[/bold blue] " f"→ {message.get('assigned_expert', '?')}" ) elif etype == "phase_completed": diff --git a/src/agentkit/server/auth/session_service.py b/src/agentkit/server/auth/session_service.py index 87f58c0..116e18a 100644 --- a/src/agentkit/server/auth/session_service.py +++ b/src/agentkit/server/auth/session_service.py @@ -9,9 +9,9 @@ Lifecycle --------- :: - create ─► rotate ─► rotate ─► ... - │ │ │ - ▼ ▼ ▼ + create -> rotate -> rotate -> ... + | | | + v v v active active active │ ├── revoke (user logout) → revoked diff --git a/src/agentkit/server/frontend/src/components/chat/helpers/useMessageRenderer.ts b/src/agentkit/server/frontend/src/components/chat/helpers/useMessageRenderer.ts index 8efb706..13700e0 100644 --- a/src/agentkit/server/frontend/src/components/chat/helpers/useMessageRenderer.ts +++ b/src/agentkit/server/frontend/src/components/chat/helpers/useMessageRenderer.ts @@ -1,5 +1,5 @@ import { computed, type Component } from 'vue' -import { AuditOutlined } from '@ant-design/icons-vue' +import { AuditOutlined, ApartmentOutlined, CheckOutlined, CloseOutlined, WarningOutlined } from '@ant-design/icons-vue' import type { IChatMessage } from '@/api/types' import UserBubble from '@/components/chat/messages/UserBubble.vue' import AssistantText from '@/components/chat/messages/AssistantText.vue' @@ -283,7 +283,7 @@ export function useMessageRenderer(message: IChatMessage) { type, shell: { name: '协作关系图', - avatar: '◆', + avatar: ApartmentOutlined, color: '#1890ff', meta: time, }, @@ -304,7 +304,7 @@ export function useMessageRenderer(message: IChatMessage) { type, shell: { name: '验收结果', - avatar: review.passed ? '\u2713' : '\u2717', + avatar: review.passed ? CheckOutlined : CloseOutlined, color: review.passed ? '#52c41a' : '#ff4d4f', meta: review.phase_name || time, }, @@ -325,7 +325,7 @@ export function useMessageRenderer(message: IChatMessage) { type, shell: { name: '风险标记', - avatar: '!', + avatar: WarningOutlined, color: '#fa8c16', meta: risk.phase_name || time, }, diff --git a/src/agentkit/server/frontend/src/components/chat/messages/ReviewResultCard.vue b/src/agentkit/server/frontend/src/components/chat/messages/ReviewResultCard.vue index f756310..d14efa8 100644 --- a/src/agentkit/server/frontend/src/components/chat/messages/ReviewResultCard.vue +++ b/src/agentkit/server/frontend/src/components/chat/messages/ReviewResultCard.vue @@ -1,7 +1,7 @@