Merge pull request 'sync: visual-diff-pencil - improve detection accuracy (add name normalization, cross-validation, structure check)' (#30) from sync/d8d-skills-20260413-163204 into master

Reviewed-on: http://gitea.d8d.fun/d8dfun/bmad-method-template/pulls/30
This commit is contained in:
2026-04-14 00:48:23 +00:00

View File

@@ -30,16 +30,18 @@
```
Phase PVD-0: 准备
├── 读取 .pen 文件 → 使用 Pencil MCP 发现设计页面
├── 读取 pipeline-context.md → 获取实际应用 URL
├── 设计帧名称标准化 → 提取清晰名称用于路由匹配
├── 构建页面映射表(设计帧 ↔ 实际路由)
├── 交叉验证补全 → 用 pages.json 确保不漏页
└── 创建截图输出目录
Phase PVD-1: 逐页对比循环
对每个映射页面:
├── A. Pencil MCP 导出设计帧截图
├── B. Playwright 截取实际页面
├── C. UI 差异对比ui_diff_check
── D. 差异分级 + 记录
├── C. 结构校验(.pen节点树+ AI 视觉对比
── D. 差异分级 + 强制执行计数
└── E. PAGE GATE-CHECK 门禁检查
Phase PVD-2: 汇总报告
├── 生成 visual-diff-pencil-report.md
@@ -76,20 +78,37 @@ Phase PVD-2: 汇总报告
- 认证凭据
- 已实现的路由
### 0.3 构建页面映射表
### 0.3 设计帧名称标准化
**输入**: 原始设计帧列表(从 Pencil MCP 获取)
**输出**: 标准化后的列表,用于路由匹配
```
名称标准化算法:
对每个设计帧:
1. 取 node.name 原始名称
2. 移除开头的数字和点: ^[\d]+\.\s* → 删除
3. 移除常见后缀: \s*(Screen|Page|Frame|[-_]\d+)$ → 删除
4. 转为小写 → 得到标准化名称
5. 示例:
- "1. Login Screen" → "login"
- "12. Withdraw" → "withdraw"
- "06-pay-result" → "pay-result"
- "Home" → "home"
```
### 0.4 构建页面映射表
**自动映射规则**
```
对每个设计帧(从 Pencil MCP 获取):
1. 提取帧名称(如 "Login", "Dashboard"
2. 在实际路由中查找匹配:
- 帧名=Login → /login 或 /admin/login
- 帧名=Dashboard → / 或 /dashboard
- 帧名=Register → /register
- 其他 → /{帧名小写} 或 /admin/{帧名小写}
3. 验证实际路由可访问curl 检查非 404
4. 不可访问的页面标记为 skipped
对每个(标准化后)设计帧:
1. 用标准化名称在实际路由中查找匹配:
- 标准化名=login → /pages/login/index 或 /login
- 标准化名=withdraw → /pages/withdraw/index 或 /withdraw
- 其他 → /pages/{标准化名}/index 或 /{标准化名}
2. 验证实际路由可访问curl 检查非 404
3. 不可访问的页面标记为 skipped
```
**映射输出格式**(记录到 pipeline-state.yaml
@@ -98,7 +117,7 @@ Phase PVD-2: 汇总报告
visual_diff_pencil_mapping:
- design_frame: "Login"
design_frame_id: "node-123"
actual_route: "/login"
actual_route: "/pages/login/index"
group: "frontend"
status: "active" # active / skipped / error
@@ -109,7 +128,24 @@ visual_diff_pencil_mapping:
status: "active"
```
### 0.4 创建截图输出目录
### 0.5 交叉验证补全(确保不漏页)
```
原理:代码中存在的页面一定要对比,不能因为自动映射失败就漏掉。
1. 读取 app/mini-program/src/pages.json
2. 提取所有 pages 和 subpackages 中的页面路径
3. 对每个页面路径:
- 提取页面名 = 路径倒数第二段("pages/withdraw/index" → "withdraw"
- 检查当前映射表中是否已有该页面
- 没有 → 在 Pencil MCP 所有设计帧中搜索名称包含该页面名
- 找到匹配的设计帧 → 自动添加到映射表status = active
- 没找到 → 记录 warning 到报告:"代码存在页面 {page} 但设计中找不到对应帧"
4. 最终映射表写回 pipeline-state.yaml
```
这样可以确保**代码中有的页面一定会被对比**,不会因为名称提取问题漏掉。
### 0.6 创建截图输出目录
```bash
mkdir -p tests/e2e/screenshots/visual-diff-pencil
@@ -163,31 +199,62 @@ PVD-1 PER-PAGE LOOP:
→ tests/e2e/screenshots/visual-diff-pencil/{name}-actual.png
```
### 1.C AI 视觉对比
### 1.C 结构校验 + AI 视觉对比
使用 Read 工具分别查看设计截图和实际截图,然后综合分析差异:
**分两步:先做结构校验,再做视觉对比**
```
步骤:
1. Read 工具查看设计截图 → 理解设计意图(布局、颜色、组件)
2. Read 工具查看实际截图 → 识别实现状态
3. 综合两图分析差异,关注:
1. 从 Pencil MCP batch_get 获取当前设计帧的完整节点树
2. 提取所有顶层命名节点name 属性存在的节点)→ 得到"必须存在的组件列表"
示例: availableCard / amountInput / amountActions / withdrawMethod / recentWithdrawals / confirmBtn
3. 读取实际页面源码app/mini-program/src/pages/{page}/index.vue检查每个组件是否存在:
- 节点存在(源码中对应区块存在)→ 继续
- 节点整个区块缺失 → 直接判定为 🔴 严重差异 → critical_count += 1
4. 结构校验通过后,使用 Read 工具分别查看设计截图和实际截图AI 综合分析视觉差异:
- 布局结构(网格/弹性/定位)
- 组件排列(导航栏、卡片、按钮、图标)
- 颜色Neon Pulse 规范:#000000 背景、#ffb3b5 粉色、#e8b3ff 紫色、#00dce5 青色)
- 间距(内边距、外边距、元素间距)
- 字体(大小、粗细、颜色)
- 图标(是否存在、是否 emoji 替代、是否损坏
- 图标(是否存在、形状是否正确、是否 emoji 替代)
```
**优势**:
- 利用设计文件本身的结构信息,比纯视觉分析更可靠
- 确保不会漏检"整个区块缺失"(如支付结果页订单信息卡片整个不见了)
- 帮助 AI 聚焦到每个具体组件,差异分析更准确
### 1.D 差异分级
| 级别 | 标准 | 示例 |
|------|------|------|
| 🔴 严重 | 布局结构不同、关键组件缺失 | 原型有两列布局实际一列 |
| 🟡 中等 | 颜色/间距偏差、图标不一致 | 卡片圆角不一致 |
| 🔴 严重 | 布局结构不同、关键组件缺失 | 原型有两列布局实际一列;整个设计区块在源码中缺失 |
| 🟡 中等 | 颜色/间距偏差、图标不一致 | 卡片圆角不一致;图标形状与设计不符 |
| 🟢 轻微 | 微小像素差异 | 1-2px 偏移 |
### 强制执行清单(必须严格遵守,不得跳过)
**禁止以"不影响功能"为由降级差异分级**
⛔ **禁止故意不计数**来让页面"看起来更好看"
| 情况 | 级别 | 是否计数 |
|------|------|----------|
| 布局结构不同、关键组件缺失 | 🔴 严重 | `critical_count += 1` |
| 颜色/间距偏差、**图标不一致** | 🟡 中等 | `moderate_count += 1` |
| 微小像素差异 (1-2px 偏移) | 🟢 轻微 | `minor_count += 1` |
**门禁规则再强调**
- 通过条件: `critical_count == 0 AND moderate_count <= 3`
- 即使有 1-3 个中等差异,仍然可以 PASS → **不需要故意不计数**
- 只有当 `critical_count > 0 OR moderate_count > 3` 才需要修复
**正确定级示例**
| 案例 | 定级 | 结果 |
|------|------|------|
| 设计微信气泡图标,实际菜单图标(形状不对) | 🟡 中等 → `moderate_count += 1` | critical 0, moderate 1 → **仍然 PASS** (1 ≤ 3) |
| 设计 recentWithdrawals 区块存在,实际源码无此区块 | 🔴 严重 → `critical_count += 1` | 需要修复 |
### 1.E PAGE GATE-CHECK ⛔(每页门禁)
**每页对比完成后必须执行门禁检查。不通过则阻断,进入修复循环。**