diff --git a/.gitignore b/.gitignore index d348235..c8d5c92 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__/ data/ .superpowers/ *.db +.playwright-mcp/ diff --git a/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md b/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md index 4732fd3..ff96b13 100644 --- a/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md +++ b/docs/superpowers/plans/2026-09-29-building-materials-ai-kefu.md @@ -392,7 +392,7 @@ python -m pytest -q && git add -A && git commit -m "feat: 会话编排建材化+ **Files:** - Modify: `app/main.py`、`app/services/email_service.py`、`app/api/routes/chat_rest.py`、`app/services/rate_limit.py`、`app/config.py`(如有 DB 默认名) -- [ ] **Step 1: 逐文件更名** +- [x] **Step 1: 逐文件更名** | 文件 | 改动 | |---|---| @@ -402,14 +402,14 @@ python -m pytest -q && git add -A && git commit -m "feat: 会话编排建材化+ | `app/services/rate_limit.py` | docstring "Mirrors hesu's" 更新 | | `app/config.py` + `.env.example` | DATABASE_URL 默认 `data/hesupipe_ai_kefu.db` | -- [ ] **Step 2: 全局残留扫描** +- [x] **Step 2: 全局残留扫描** ```bash grep -rin "汽车\|vehicle\|byd\|toyota\|hesu auto\|borderlesscar\|VOLT\|XYH\|cc-ai-kefu\|cc_ai_kefu\|car\b\|cars\|什么车\|车型\|车价" app/ tests/ scripts/ --include="*.py" | grep -v "docs/" ``` 预期:无输出(docstring/注释里的行业残留一并清零;`car\b|cars|什么车|车型` 抓 boundary_filter 话术池等用户可见输出)。 -- [ ] **Step 3: 全量测试 + Commit** +- [x] **Step 3: 全量测试 + Commit** ```bash python -m pytest -q && git add -A && git commit -m "chore: 服务端品牌与行业残留更名清零" @@ -422,7 +422,7 @@ python -m pytest -q && git add -A && git commit -m "chore: 服务端品牌与行 **Files:** - Modify: `docker-compose.yml`、`deploy.sh`、`openresty.conf`、`.env.example` -- [ ] **Step 1: 更名** +- [x] **Step 1: 更名** | 文件 | 改动 | |---|---| @@ -431,14 +431,14 @@ python -m pytest -q && git add -A && git commit -m "chore: 服务端品牌与行 | `openresty.conf` | upstream `cc_ai_kefu`→`hesupipe_ai_kefu`;127.0.0.1:8016→8017;alias `/www/cc_ai_kefu/`→`/www/hesupipe_ai_kefu/`;日志名同步(含注释掉的 HTTPS 段) | | `.env.example` | `APP_PORT=8017`;`PUSH_EXTERNAL_KEY_PREFIX=ai-kefu-jc`;`PUSH_ENABLED=false`(开发期) | -- [ ] **Step 2: 残留扫描** +- [x] **Step 2: 残留扫描** ```bash grep -rn "cc_ai_kefu\|cc-ai-kefu\|8016" docker-compose.yml deploy.sh openresty.conf .env.example ``` 预期:无输出。 -- [ ] **Step 3: 测试 + Commit** +- [x] **Step 3: 测试 + Commit** ```bash python -m pytest -q && git add -A && git commit -m "chore: 部署配置更名(hs-ai-kefu/8017/hesupipe_ai_kefu)" @@ -452,14 +452,14 @@ python -m pytest -q && git add -A && git commit -m "chore: 部署配置更名(hs - Modify: `static/chat.html`、`static/widget.js`、`static/js/chat.js`、`static/js/theme.js`、`static/admin.html`、`static/widget-test.html` - Test: `tests/test_invite_static.py`、`tests/test_theme_static.py` -- [ ] **Step 1: 了解静态测试的真实性质(避免走错路)** +- [x] **Step 1: 了解静态测试的真实性质(避免走错路)** `tests/test_invite_static.py`/`test_theme_static.py` 是**结构回归网**,不是文案断言—— 它们断言的是邀请卡面板出厂 hide、pointer-events、回流顺序、theme.js 与 widget.js 调色板逐字同步、无残留旧色 hex 等结构性质。所以本任务的测试策略是: **直接改前端文件,然后跑结构回归测试防破坏**(它们会在你漏改某处色值/结构时指路)。 -- [ ] **Step 2: 前端文案与配置** +- [x] **Step 2: 前端文案与配置** | 文件 | 改动 | |---|---| @@ -471,14 +471,14 @@ python -m pytest -q && git add -A && git commit -m "chore: 部署配置更名(hs | `widget.js` | **GDPR 行**:首次对话邀请卡/首条消息附加 "By chatting, you agree to our privacy policy."(链接指向 hesupipe.com 隐私政策页,URL 实施时与需求方确认,可先 `https://hesupipe.com/privacy-policy/` 占位) | | 头像 | `kefu.png`/`kefu-small.png` 替换(需求方提供 logo 或用品牌蓝底 "H" 字母临时生成) | -- [ ] **Step 3: 跑静态断言与全量测试至绿** +- [x] **Step 3: 跑静态断言与全量测试至绿** ```bash python -m pytest tests/test_invite_static.py tests/test_theme_static.py -q python -m pytest -q ``` -- [ ] **Step 4: 残留扫描 + Commit** +- [x] **Step 4: 残留扫描 + Commit** ```bash grep -rin "HESU Support\|hesu auto\|vehicle\|VOLT\|borderlesscar\|1903717\|f08a31" static/ | grep -v blog diff --git a/static/admin.html b/static/admin.html index 555b1b8..d5d3127 100644 --- a/static/admin.html +++ b/static/admin.html @@ -3,7 +3,7 @@
-Expected behavior:
Pick a color and Apply, or use a preset. Each click tears down the old widget (host + script + invite dismissal state) and re-embeds with data-color. "Test Broken Avatar" points data-avatar at a missing image to verify the 🤖 emoji fallback.
- + - + @@ -103,7 +103,7 @@ "data-mobile": "true", "data-debug": "true", "data-whatsapp": "https://wa.me/8613800138000", - "data-email": "mailto:demo@hesu-auto.com" + "data-email": "mailto:demo@hesupipe.com" }; var rebuildLoading = false, rebuildPending = null; @@ -142,9 +142,9 @@ } /* ===== 断言面板(spec §8.2) ===== */ - var CURRENT_HARDCODED = { // 现状代表值(spec §4),Δ ≤ 8/通道 - primary: "#f08a31", gradientEnd: "#F08A34", deepText: "#8C4D1D", - captionText: "#C16C2E", softBg: "#FFF8F3", borderSoft: "#F0D5BF" + var CURRENT_HARDCODED = { // 现状代表值(HESUPIPE 品牌蓝 #066fc0 派生),Δ ≤ 8/通道 + primary: "#066fc0", gradientEnd: "#0666bb", deepText: "#14517a", + captionText: "#2174ab", softBg: "#f3f9fe", borderSoft: "#bbdcf2" }; function hexToRgb6(h) { h = String(h).replace(/^#/, ""); @@ -161,7 +161,7 @@ if (!t || !d) { rows.push("❌ theme.js / widget debug 钩子未就绪"); } else { - var p = t.derivePalette("#f08a31"); + var p = t.derivePalette("#066fc0"); Object.keys(CURRENT_HARDCODED).forEach(function (k) { var delta = channelDelta(p[k], CURRENT_HARDCODED[k]); check("default " + k + " Δ≤8", delta <= 8, p[k] + " vs " + CURRENT_HARDCODED[k] + ", Δ=" + delta); diff --git a/static/widget.js b/static/widget.js index e5b2617..6d60dc9 100644 --- a/static/widget.js +++ b/static/widget.js @@ -7,25 +7,25 @@ // Save config immediately (before DOM ready) var position = script.getAttribute("data-position") || "bottom-right"; var theme = script.getAttribute("data-theme") || "light"; - var title = script.getAttribute("data-title") || "HESU Support"; + var title = script.getAttribute("data-title") || "HESUPIPE Support"; var autoOpen = script.getAttribute("data-auto-open") === "true"; - var whatsapp = script.getAttribute("data-whatsapp") || "https://wa.me/+8619037179250"; - var email = script.getAttribute("data-email") || "mailto:info@borderlesscar.com"; + var whatsapp = script.getAttribute("data-whatsapp") || "https://wa.me/+8615937108317"; + var email = script.getAttribute("data-email") || "mailto:hesu@hesuu.com"; var autoInvite = script.getAttribute("data-auto-invite") !== "false"; var isMobile = script.getAttribute("data-mobile") === "true"; var wsBase = new URL(script.src).origin; /* ===== 主题派生单元:与 static/js/theme.js 逐字同步,tests/test_theme_static.py 强制;只可整块替换,不可单边修改 ===== */ - var DEFAULT_COLOR = "#f08a31"; + var DEFAULT_COLOR = "#066fc0"; var DEFAULT_PALETTE = { - primary: "#f08a31", - primaryRgb: "240,138,49", - gradientEnd: "#f08e2c", - deepText: "#8a4c1d", - captionText: "#c16d2d", - softBg: "#fef8f4", - borderSoft: "#f0d3bd" + primary: "#066fc0", + primaryRgb: "6,111,192", + gradientEnd: "#0666bb", + deepText: "#14517a", + captionText: "#2174ab", + softBg: "#f3f9fe", + borderSoft: "#bbdcf2" }; function clampNum(v, lo, hi) { @@ -219,6 +219,7 @@ ".chat-footer { padding:10px 10px; background:white; display:flex; align-items:center; justify-content:space-between; font-size:11px; color:var(--w-caption-text); gap:20px; }", ".chat-footer .footer-left { flex:1; }", ".chat-footer .footer-right { flex-shrink:0; }", + ".chat-footer a { color:var(--w-caption-text); text-decoration:underline; }", // Typing ".typing { display:none; align-self:flex-start; padding:8px 12px; background:" + c.bubble + "; border-radius:12px; }", @@ -275,6 +276,8 @@ ".invite-start-btn:hover { opacity:0.95; }", ".invite-dismiss-btn { border-radius:20px; border:1px solid var(--w-border-soft); background:white; padding:14px 20px; color:var(--w-deep-text); font-size:14px; font-weight:500; cursor:pointer; transition:background 0.2s; }", ".invite-dismiss-btn:hover { background:#FEFEFE; }", + ".invite-privacy { padding:2px 16px 12px; font-size:11px; color:var(--w-caption-text); text-align:center; }", + ".invite-privacy a { color:var(--w-caption-text); text-decoration:underline; }", // Responsive "@media(max-width:450px){ .chat-panel{width:calc(100vw - 20px);height:calc(100vh - 200px);bottom:60px;right:0px;} .invite-panel{width:calc(100vw - 24px);} .invite-topics{grid-template-columns:1fr;} }", @@ -297,19 +300,20 @@ '', '