Files
jiu/tools/ds-compare.mjs
T
wangjia 019d73cfc5 build(tools): ds-compare.mjs 还原保真目检编排 + gitignore 对比图
一键流程:对某屏按 A/B/C 截原型基准 + 与 Flutter golden 用 montage 并排
→ design/_compare/<prefix>_<theme>.png 人工目检。封装 design-distill skill 的
shoot-prototype + montage。对比图可重生,已 gitignore。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSKEiHsvauyxYUW2itzUXX
2026-06-25 11:10:12 +08:00

71 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* ds-compare.mjs — 还原保真目检编排(原型基准 ↔ Flutter golden 并排)
*
* design-distill 阶段4「保真闸」的项目侧一键流程:对某屏,按 A/B/C 三主题
* 截原型基准图,再与已生成的 Flutter golden 用 montage 并排,产出对比图供人工目检。
* 跨渲染器(Chromium↔Skia)不跑严格 pixelmatch;回归靠 Flutter golden 自比(flutter test)。
*
* 前置:先生成 Flutter golden
* cd client && flutter test --update-goldens test/golden/<screen>_golden_test.dart
*
* 用法(从仓库根运行):
* node tools/ds-compare.mjs --html .superpowers/prototype/screens/inventory.html \
* --prefix inventory_list [--themes a,b,c] [--width 1280] [--height 860] [--wait-for .app]
*
* 输出:design/_compare/<prefix>_<theme>.pnggitignore,可重生)。
* 依赖:design-distill skillshoot-prototype.mjs / montage.mjs),默认 ~/.claude/skills/design-distill,可用 DD_SKILL 覆盖。
*/
import { parseArgs } from 'node:util';
import { execFileSync } from 'node:child_process';
import { mkdirSync, existsSync } from 'node:fs';
import { homedir } from 'node:os';
import { join, resolve } from 'node:path';
const { values } = parseArgs({
options: {
html: { type: 'string' },
prefix: { type: 'string' },
themes: { type: 'string', default: 'a,b,c' },
width: { type: 'string', default: '1280' },
height: { type: 'string', default: '860' },
'wait-for': { type: 'string', default: '.app' },
'goldens-dir': { type: 'string', default: 'client/test/golden/goldens' },
help: { type: 'boolean', default: false },
},
});
if (values.help || !values.html || !values.prefix) {
console.log(`用法: node tools/ds-compare.mjs --html <原型.html> --prefix <golden前缀> [--themes a,b,c --width 1280 --height 860 --wait-for .app]`);
process.exit(values.help ? 0 : 1);
}
const SKILL = process.env.DD_SKILL || join(homedir(), '.claude/skills/design-distill');
const shoot = join(SKILL, 'tools/shoot-prototype.mjs');
const montage = join(SKILL, 'tools/montage.mjs');
for (const t of [shoot, montage]) {
if (!existsSync(t)) { console.error(`[ds-compare] 找不到 skill 工具: ${t}(设 DD_SKILL 覆盖)`); process.exit(2); }
}
const outDir = 'design/_compare';
mkdirSync(outDir, { recursive: true });
const themes = values.themes.split(',').map((s) => s.trim()).filter(Boolean);
for (const theme of themes) {
const base = join(outDir, `${values.prefix}_proto_${theme}.png`);
const golden = resolve(values['goldens-dir'], `${values.prefix}_${theme}.png`);
const out = join(outDir, `${values.prefix}_${theme}.png`);
execFileSync('node', [shoot, values.html, base,
'--width', values.width, '--height', values.height,
'--theme', theme, '--lang', 'zh', '--wait-for', values['wait-for']],
{ stdio: 'inherit' });
if (!existsSync(golden)) {
console.error(`[ds-compare] ⚠ 缺 Flutter golden: ${golden} —— 先跑 flutter test --update-goldens。跳过 ${theme} 的并排。`);
continue;
}
execFileSync('node', [montage, out, base, golden, '--dir', 'v'], { stdio: 'inherit' });
console.log(`[ds-compare] ✅ ${theme}: ${out}(上=原型 下=Flutter,人工目检)`);
}