prd-to-flutter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/bin/p2f.mjs +18 -0
- package/dist/cli.js +203 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/clean.js +35 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/doctor.js +148 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/generate.js +120 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/paths.js +58 -0
- package/dist/commands/paths.js.map +1 -0
- package/dist/commands/remove.js +23 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/screenshots-audit.js +39 -0
- package/dist/commands/screenshots-audit.js.map +1 -0
- package/dist/commands/skills-install.js +21 -0
- package/dist/commands/skills-install.js.map +1 -0
- package/dist/commands/sync.js +84 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/update.js +93 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/core/existing-page-detector.js +463 -0
- package/dist/core/existing-page-detector.js.map +1 -0
- package/dist/core/fail-fast.js +50 -0
- package/dist/core/fail-fast.js.map +1 -0
- package/dist/core/feature-coverage-builder.js +667 -0
- package/dist/core/feature-coverage-builder.js.map +1 -0
- package/dist/core/flutter-project-scanner.js +393 -0
- package/dist/core/flutter-project-scanner.js.map +1 -0
- package/dist/core/implementation-plan-writer.js +190 -0
- package/dist/core/implementation-plan-writer.js.map +1 -0
- package/dist/core/logger.js +39 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/package-info.js +33 -0
- package/dist/core/package-info.js.map +1 -0
- package/dist/core/paths.js +37 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/playwright-capture.js +539 -0
- package/dist/core/playwright-capture.js.map +1 -0
- package/dist/core/playwright-cli.js +26 -0
- package/dist/core/playwright-cli.js.map +1 -0
- package/dist/core/playwright-install.js +40 -0
- package/dist/core/playwright-install.js.map +1 -0
- package/dist/core/prd-clone.js +131 -0
- package/dist/core/prd-clone.js.map +1 -0
- package/dist/core/prd-install.js +108 -0
- package/dist/core/prd-install.js.map +1 -0
- package/dist/core/profile.js +149 -0
- package/dist/core/profile.js.map +1 -0
- package/dist/core/project-doc-reader.js +252 -0
- package/dist/core/project-doc-reader.js.map +1 -0
- package/dist/core/report-writer.js +90 -0
- package/dist/core/report-writer.js.map +1 -0
- package/dist/core/route-name.js +60 -0
- package/dist/core/route-name.js.map +1 -0
- package/dist/core/run-context.js +160 -0
- package/dist/core/run-context.js.map +1 -0
- package/dist/core/screenshot-auditor.js +405 -0
- package/dist/core/screenshot-auditor.js.map +1 -0
- package/dist/core/screenshot-exploration-plan-writer.js +200 -0
- package/dist/core/screenshot-exploration-plan-writer.js.map +1 -0
- package/dist/core/semantic-model-builder.js +922 -0
- package/dist/core/semantic-model-builder.js.map +1 -0
- package/dist/core/skill-install.js +78 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/stage-stub.js +24 -0
- package/dist/core/stage-stub.js.map +1 -0
- package/dist/core/task-index-writer.js +149 -0
- package/dist/core/task-index-writer.js.map +1 -0
- package/dist/core/update-checker.js +155 -0
- package/dist/core/update-checker.js.map +1 -0
- package/dist/core/vue-page-locator.js +748 -0
- package/dist/core/vue-page-locator.js.map +1 -0
- package/dist/core/vue-project-reader.js +116 -0
- package/dist/core/vue-project-reader.js.map +1 -0
- package/docs/artifacts-and-agent.md +203 -0
- package/docs/development.md +118 -0
- package/docs/usage.md +246 -0
- package/package.json +50 -0
- package/skills/p2f/SKILL.md +303 -0
- package/skills/p2f/references/page-layout-patterns.md +120 -0
- package/skills/p2f/references/youfi-flutter-guidelines.md +71 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, join, relative } from 'node:path';
|
|
4
|
+
import { inflateSync } from 'node:zlib';
|
|
5
|
+
const NEAR_DUPLICATE_THRESHOLD = 3;
|
|
6
|
+
export function auditAgentScreenshots(input) {
|
|
7
|
+
const screenshotDir = join(input.pageDir, 'agent', 'screenshots');
|
|
8
|
+
const analysisDir = join(input.pageDir, 'cli', 'analysis');
|
|
9
|
+
mkdirSync(analysisDir, { recursive: true });
|
|
10
|
+
const markdownPath = join(analysisDir, 'agent-screenshot-audit.md');
|
|
11
|
+
const jsonPath = join(analysisDir, 'agent-screenshot-audit.json');
|
|
12
|
+
const files = existsSync(screenshotDir)
|
|
13
|
+
? readdirSync(screenshotDir)
|
|
14
|
+
.filter((name) => /\.png$/i.test(name))
|
|
15
|
+
.sort((a, b) => a.localeCompare(b, 'en'))
|
|
16
|
+
.map((name) => join(screenshotDir, name))
|
|
17
|
+
.filter((path) => statSync(path).isFile())
|
|
18
|
+
: [];
|
|
19
|
+
const records = files.map((path) => buildRecord(input.pageDir, path));
|
|
20
|
+
const duplicateGroups = [
|
|
21
|
+
...markExactDuplicates(records),
|
|
22
|
+
...markNearDuplicates(records),
|
|
23
|
+
];
|
|
24
|
+
const audit = {
|
|
25
|
+
pageKey: input.pageKey,
|
|
26
|
+
pageName: input.pageName,
|
|
27
|
+
generatedAt: new Date().toISOString(),
|
|
28
|
+
markdownPath,
|
|
29
|
+
jsonPath,
|
|
30
|
+
summary: {
|
|
31
|
+
total: records.length,
|
|
32
|
+
exactDuplicateGroups: duplicateGroups.filter((g) => g.kind === 'exact').length,
|
|
33
|
+
nearDuplicateGroups: duplicateGroups.filter((g) => g.kind === 'near-visual').length,
|
|
34
|
+
suspectedPlatformShell: records.filter((r) => r.warnings.some((w) => w.includes('平台壳层'))).length,
|
|
35
|
+
duplicateFiles: records.filter((r) => r.duplicateOf).length,
|
|
36
|
+
},
|
|
37
|
+
files: records.map(({ absolutePath: _absolutePath, ...record }) => record),
|
|
38
|
+
duplicateGroups,
|
|
39
|
+
};
|
|
40
|
+
writeFileSync(jsonPath, JSON.stringify(audit, null, 2) + '\n', 'utf8');
|
|
41
|
+
writeFileSync(markdownPath, renderMarkdown(input.pageDir, audit), 'utf8');
|
|
42
|
+
return audit;
|
|
43
|
+
}
|
|
44
|
+
function buildRecord(pageDir, absolutePath) {
|
|
45
|
+
const file = readFileSync(absolutePath);
|
|
46
|
+
const warnings = [];
|
|
47
|
+
let png;
|
|
48
|
+
try {
|
|
49
|
+
png = decodePng(file);
|
|
50
|
+
if (png.warning)
|
|
51
|
+
warnings.push(png.warning);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
warnings.push(`PNG 解码失败:${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
if (png?.rgba && looksLikePlatformShell(png)) {
|
|
57
|
+
warnings.push('疑似截到了原型平台壳层:左侧深色菜单 + 右侧大块空白。请删除并改用 `.phone-screen` 元素截图。');
|
|
58
|
+
}
|
|
59
|
+
const pixelSha256 = png?.rgba
|
|
60
|
+
? sha256(Buffer.concat([Buffer.from(`${png.width}x${png.height}:`), Buffer.from(png.rgba)]))
|
|
61
|
+
: undefined;
|
|
62
|
+
const visualHash = png?.rgba ? dHash(png) : undefined;
|
|
63
|
+
return {
|
|
64
|
+
absolutePath,
|
|
65
|
+
path: relative(pageDir, absolutePath),
|
|
66
|
+
fileName: basename(absolutePath),
|
|
67
|
+
...(png?.width && { width: png.width }),
|
|
68
|
+
...(png?.height && { height: png.height }),
|
|
69
|
+
bytes: file.byteLength,
|
|
70
|
+
fileSha256: sha256(file),
|
|
71
|
+
...(pixelSha256 && { pixelSha256 }),
|
|
72
|
+
...(visualHash && { visualHash }),
|
|
73
|
+
warnings,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function markExactDuplicates(records) {
|
|
77
|
+
const byKey = new Map();
|
|
78
|
+
for (const rec of records) {
|
|
79
|
+
const key = rec.pixelSha256
|
|
80
|
+
? `pixel:${rec.width}x${rec.height}:${rec.pixelSha256}`
|
|
81
|
+
: `file:${rec.fileSha256}`;
|
|
82
|
+
const bucket = byKey.get(key) ?? [];
|
|
83
|
+
bucket.push(rec);
|
|
84
|
+
byKey.set(key, bucket);
|
|
85
|
+
}
|
|
86
|
+
const groups = [];
|
|
87
|
+
let idx = 1;
|
|
88
|
+
for (const bucket of byKey.values()) {
|
|
89
|
+
if (bucket.length < 2)
|
|
90
|
+
continue;
|
|
91
|
+
const representative = bucket[0];
|
|
92
|
+
for (const member of bucket.slice(1)) {
|
|
93
|
+
member.duplicateOf = representative.path;
|
|
94
|
+
member.duplicateKind = 'exact';
|
|
95
|
+
member.warnings.push(`与 ${representative.path} 像素完全一致,保留第一张即可。`);
|
|
96
|
+
}
|
|
97
|
+
groups.push({
|
|
98
|
+
id: `exact-${idx++}`,
|
|
99
|
+
kind: 'exact',
|
|
100
|
+
representative: representative.path,
|
|
101
|
+
members: bucket.map((r) => r.path),
|
|
102
|
+
reason: '解码后的像素 hash 完全一致。',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return groups;
|
|
106
|
+
}
|
|
107
|
+
function markNearDuplicates(records) {
|
|
108
|
+
const candidates = records.filter((r) => !r.duplicateOf && r.visualHash && r.width && r.height);
|
|
109
|
+
const used = new Set();
|
|
110
|
+
const groups = [];
|
|
111
|
+
let idx = 1;
|
|
112
|
+
for (const representative of candidates) {
|
|
113
|
+
if (used.has(representative))
|
|
114
|
+
continue;
|
|
115
|
+
const members = [{ record: representative, distance: 0 }];
|
|
116
|
+
for (const other of candidates) {
|
|
117
|
+
if (other === representative || used.has(other))
|
|
118
|
+
continue;
|
|
119
|
+
if (!sameDimensions(representative, other))
|
|
120
|
+
continue;
|
|
121
|
+
const distance = hashDistance(representative.visualHash, other.visualHash);
|
|
122
|
+
if (distance <= NEAR_DUPLICATE_THRESHOLD)
|
|
123
|
+
members.push({ record: other, distance });
|
|
124
|
+
}
|
|
125
|
+
if (members.length < 2) {
|
|
126
|
+
used.add(representative);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const id = `near-${idx++}`;
|
|
130
|
+
for (const member of members) {
|
|
131
|
+
used.add(member.record);
|
|
132
|
+
if (member.record === representative)
|
|
133
|
+
continue;
|
|
134
|
+
member.record.duplicateOf = representative.path;
|
|
135
|
+
member.record.duplicateKind = 'near-visual';
|
|
136
|
+
member.record.nearDuplicateDistance = member.distance;
|
|
137
|
+
member.record.warnings.push(`与 ${representative.path} 视觉 hash 很接近,若 UI 无差异则不需要重复保留。`);
|
|
138
|
+
}
|
|
139
|
+
groups.push({
|
|
140
|
+
id,
|
|
141
|
+
kind: 'near-visual',
|
|
142
|
+
representative: representative.path,
|
|
143
|
+
members: members.map((m) => m.record.path),
|
|
144
|
+
reason: `dHash distance <= ${NEAR_DUPLICATE_THRESHOLD},尺寸一致或近似一致。`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return groups;
|
|
148
|
+
}
|
|
149
|
+
function sameDimensions(a, b) {
|
|
150
|
+
if (!a.width || !a.height || !b.width || !b.height)
|
|
151
|
+
return false;
|
|
152
|
+
return Math.abs(a.width - b.width) <= 2 && Math.abs(a.height - b.height) <= 2;
|
|
153
|
+
}
|
|
154
|
+
function looksLikePlatformShell(png) {
|
|
155
|
+
if (!png.rgba || png.width < 200 || png.height < 300)
|
|
156
|
+
return false;
|
|
157
|
+
const left = bandStats(png, 0, Math.floor(png.width * 0.58));
|
|
158
|
+
const right = bandStats(png, Math.floor(png.width * 0.68), png.width);
|
|
159
|
+
return left.average < 70 && right.average > 245 && right.lowVariance && right.coverage > 0.9;
|
|
160
|
+
}
|
|
161
|
+
function bandStats(png, x0, x1) {
|
|
162
|
+
let total = 0;
|
|
163
|
+
let totalSq = 0;
|
|
164
|
+
let count = 0;
|
|
165
|
+
let nearWhite = 0;
|
|
166
|
+
for (let y = 0; y < png.height; y += 4) {
|
|
167
|
+
for (let x = x0; x < x1; x += 4) {
|
|
168
|
+
const l = luminanceAt(png.rgba, png.width, x, y);
|
|
169
|
+
total += l;
|
|
170
|
+
totalSq += l * l;
|
|
171
|
+
count += 1;
|
|
172
|
+
if (l > 245)
|
|
173
|
+
nearWhite += 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const average = total / Math.max(1, count);
|
|
177
|
+
const variance = totalSq / Math.max(1, count) - average * average;
|
|
178
|
+
return { average, lowVariance: variance < 18, coverage: nearWhite / Math.max(1, count) };
|
|
179
|
+
}
|
|
180
|
+
function renderMarkdown(pageDir, audit) {
|
|
181
|
+
const lines = [];
|
|
182
|
+
lines.push(`# agent 截图审计 - ${audit.pageName}`);
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('- 该报告用于发现重复截图和疑似截到原型平台壳层的截图。');
|
|
185
|
+
lines.push('- 正确的原型截图必须来自 `.phone-screen` 元素,不应包含 TradeApp Workflow Hub 左侧菜单。');
|
|
186
|
+
lines.push('');
|
|
187
|
+
lines.push(`- 页面目录名:\`${audit.pageKey}\``);
|
|
188
|
+
lines.push(`- 截图总数:${audit.summary.total}`);
|
|
189
|
+
lines.push(`- 精确重复分组:${audit.summary.exactDuplicateGroups}`);
|
|
190
|
+
lines.push(`- 近似重复分组:${audit.summary.nearDuplicateGroups}`);
|
|
191
|
+
lines.push(`- 疑似平台壳层截图:${audit.summary.suspectedPlatformShell}`);
|
|
192
|
+
lines.push(`- 可跳过重复文件数:${audit.summary.duplicateFiles}`);
|
|
193
|
+
lines.push('');
|
|
194
|
+
lines.push('## 必须处理的问题');
|
|
195
|
+
lines.push('');
|
|
196
|
+
const problemFiles = audit.files.filter((file) => file.warnings.length > 0);
|
|
197
|
+
if (!problemFiles.length) {
|
|
198
|
+
lines.push('- (无)');
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
for (const file of problemFiles) {
|
|
202
|
+
lines.push(`- \`${file.path}\``);
|
|
203
|
+
for (const warning of file.warnings)
|
|
204
|
+
lines.push(` - ${warning}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
lines.push('');
|
|
208
|
+
lines.push('## 重复分组');
|
|
209
|
+
lines.push('');
|
|
210
|
+
if (!audit.duplicateGroups.length) {
|
|
211
|
+
lines.push('- (无)');
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
for (const group of audit.duplicateGroups) {
|
|
215
|
+
lines.push(`- ${group.id} · ${group.kind} · 保留 \`${group.representative}\``);
|
|
216
|
+
for (const member of group.members.filter((path) => path !== group.representative)) {
|
|
217
|
+
lines.push(` - 重复:\`${member}\``);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
lines.push('');
|
|
222
|
+
lines.push('## 截图索引');
|
|
223
|
+
lines.push('');
|
|
224
|
+
lines.push('| 文件 | 尺寸 | 重复 | 告警 |');
|
|
225
|
+
lines.push('| --- | --- | --- | --- |');
|
|
226
|
+
for (const file of audit.files) {
|
|
227
|
+
const size = file.width && file.height ? `${file.width}x${file.height}` : '未知';
|
|
228
|
+
const duplicate = file.duplicateOf ? `${file.duplicateKind} of ${file.duplicateOf}` : '';
|
|
229
|
+
lines.push(`| \`${file.path}\` | ${size} | ${escapeTable(duplicate)} | ${escapeTable(file.warnings.join('; '))} |`);
|
|
230
|
+
}
|
|
231
|
+
lines.push('');
|
|
232
|
+
lines.push(`机器可读版本:\`${relative(pageDir, audit.jsonPath)}\``);
|
|
233
|
+
lines.push('');
|
|
234
|
+
return lines.join('\n');
|
|
235
|
+
}
|
|
236
|
+
function decodePng(buf) {
|
|
237
|
+
if (buf.subarray(0, 8).toString('hex') !== '89504e470d0a1a0a') {
|
|
238
|
+
throw new Error('不是 PNG 文件');
|
|
239
|
+
}
|
|
240
|
+
let offset = 8;
|
|
241
|
+
let width = 0;
|
|
242
|
+
let height = 0;
|
|
243
|
+
let bitDepth = 0;
|
|
244
|
+
let colorType = 0;
|
|
245
|
+
let interlace = 0;
|
|
246
|
+
const idat = [];
|
|
247
|
+
while (offset + 8 <= buf.length) {
|
|
248
|
+
const len = buf.readUInt32BE(offset);
|
|
249
|
+
offset += 4;
|
|
250
|
+
const type = buf.subarray(offset, offset + 4).toString('ascii');
|
|
251
|
+
offset += 4;
|
|
252
|
+
const data = buf.subarray(offset, offset + len);
|
|
253
|
+
offset += len + 4;
|
|
254
|
+
if (type === 'IHDR') {
|
|
255
|
+
width = data.readUInt32BE(0);
|
|
256
|
+
height = data.readUInt32BE(4);
|
|
257
|
+
bitDepth = data[8];
|
|
258
|
+
colorType = data[9];
|
|
259
|
+
interlace = data[12];
|
|
260
|
+
}
|
|
261
|
+
else if (type === 'IDAT') {
|
|
262
|
+
idat.push(Buffer.from(data));
|
|
263
|
+
}
|
|
264
|
+
else if (type === 'IEND') {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!width || !height)
|
|
269
|
+
throw new Error('缺少 PNG IHDR');
|
|
270
|
+
if (bitDepth !== 8 || interlace !== 0)
|
|
271
|
+
return { width, height, warning: 'PNG 格式暂不支持像素解码,已退回文件 hash。' };
|
|
272
|
+
const channels = channelsForColorType(colorType);
|
|
273
|
+
if (!channels)
|
|
274
|
+
return { width, height, warning: `PNG color type ${colorType} 暂不支持像素解码,已退回文件 hash。` };
|
|
275
|
+
const inflated = inflateSync(Buffer.concat(idat));
|
|
276
|
+
const rowBytes = width * channels;
|
|
277
|
+
const raw = new Uint8Array(width * height * channels);
|
|
278
|
+
let srcOffset = 0;
|
|
279
|
+
let dstOffset = 0;
|
|
280
|
+
let prev = new Uint8Array(rowBytes);
|
|
281
|
+
for (let y = 0; y < height; y += 1) {
|
|
282
|
+
const filter = inflated[srcOffset++];
|
|
283
|
+
const row = inflated.subarray(srcOffset, srcOffset + rowBytes);
|
|
284
|
+
srcOffset += rowBytes;
|
|
285
|
+
const decoded = new Uint8Array(rowBytes);
|
|
286
|
+
for (let x = 0; x < rowBytes; x += 1) {
|
|
287
|
+
const left = x >= channels ? decoded[x - channels] : 0;
|
|
288
|
+
const up = prev[x] ?? 0;
|
|
289
|
+
const upperLeft = x >= channels ? prev[x - channels] : 0;
|
|
290
|
+
decoded[x] = (row[x] + filterByte(filter, left, up, upperLeft)) & 0xff;
|
|
291
|
+
}
|
|
292
|
+
raw.set(decoded, dstOffset);
|
|
293
|
+
dstOffset += rowBytes;
|
|
294
|
+
prev = decoded;
|
|
295
|
+
}
|
|
296
|
+
return { width, height, rgba: toRgba(raw, colorType, channels) };
|
|
297
|
+
}
|
|
298
|
+
function channelsForColorType(colorType) {
|
|
299
|
+
if (colorType === 0)
|
|
300
|
+
return 1;
|
|
301
|
+
if (colorType === 2)
|
|
302
|
+
return 3;
|
|
303
|
+
if (colorType === 4)
|
|
304
|
+
return 2;
|
|
305
|
+
if (colorType === 6)
|
|
306
|
+
return 4;
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
function filterByte(filter, left, up, upperLeft) {
|
|
310
|
+
if (filter === 0)
|
|
311
|
+
return 0;
|
|
312
|
+
if (filter === 1)
|
|
313
|
+
return left;
|
|
314
|
+
if (filter === 2)
|
|
315
|
+
return up;
|
|
316
|
+
if (filter === 3)
|
|
317
|
+
return Math.floor((left + up) / 2);
|
|
318
|
+
if (filter === 4)
|
|
319
|
+
return paeth(left, up, upperLeft);
|
|
320
|
+
throw new Error(`不支持 PNG filter ${filter}`);
|
|
321
|
+
}
|
|
322
|
+
function paeth(a, b, c) {
|
|
323
|
+
const p = a + b - c;
|
|
324
|
+
const pa = Math.abs(p - a);
|
|
325
|
+
const pb = Math.abs(p - b);
|
|
326
|
+
const pc = Math.abs(p - c);
|
|
327
|
+
if (pa <= pb && pa <= pc)
|
|
328
|
+
return a;
|
|
329
|
+
if (pb <= pc)
|
|
330
|
+
return b;
|
|
331
|
+
return c;
|
|
332
|
+
}
|
|
333
|
+
function toRgba(raw, colorType, channels) {
|
|
334
|
+
const rgba = new Uint8Array((raw.length / channels) * 4);
|
|
335
|
+
for (let i = 0, j = 0; i < raw.length; i += channels, j += 4) {
|
|
336
|
+
if (colorType === 0) {
|
|
337
|
+
const g = raw[i];
|
|
338
|
+
rgba[j] = g;
|
|
339
|
+
rgba[j + 1] = g;
|
|
340
|
+
rgba[j + 2] = g;
|
|
341
|
+
rgba[j + 3] = 255;
|
|
342
|
+
}
|
|
343
|
+
else if (colorType === 2) {
|
|
344
|
+
rgba[j] = raw[i];
|
|
345
|
+
rgba[j + 1] = raw[i + 1];
|
|
346
|
+
rgba[j + 2] = raw[i + 2];
|
|
347
|
+
rgba[j + 3] = 255;
|
|
348
|
+
}
|
|
349
|
+
else if (colorType === 4) {
|
|
350
|
+
const g = raw[i];
|
|
351
|
+
rgba[j] = g;
|
|
352
|
+
rgba[j + 1] = g;
|
|
353
|
+
rgba[j + 2] = g;
|
|
354
|
+
rgba[j + 3] = raw[i + 1];
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
rgba[j] = raw[i];
|
|
358
|
+
rgba[j + 1] = raw[i + 1];
|
|
359
|
+
rgba[j + 2] = raw[i + 2];
|
|
360
|
+
rgba[j + 3] = raw[i + 3];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return rgba;
|
|
364
|
+
}
|
|
365
|
+
function dHash(png) {
|
|
366
|
+
if (!png.rgba || png.width < 2 || png.height < 1)
|
|
367
|
+
return undefined;
|
|
368
|
+
const sampleWidth = 9;
|
|
369
|
+
const sampleHeight = 8;
|
|
370
|
+
const samples = [];
|
|
371
|
+
for (let y = 0; y < sampleHeight; y += 1) {
|
|
372
|
+
for (let x = 0; x < sampleWidth; x += 1) {
|
|
373
|
+
const sx = Math.min(png.width - 1, Math.floor(((x + 0.5) * png.width) / sampleWidth));
|
|
374
|
+
const sy = Math.min(png.height - 1, Math.floor(((y + 0.5) * png.height) / sampleHeight));
|
|
375
|
+
samples.push(luminanceAt(png.rgba, png.width, sx, sy));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
let bits = 0n;
|
|
379
|
+
for (let y = 0; y < sampleHeight; y += 1) {
|
|
380
|
+
for (let x = 0; x < sampleWidth - 1; x += 1) {
|
|
381
|
+
bits = (bits << 1n) | (samples[y * sampleWidth + x] > samples[y * sampleWidth + x + 1] ? 1n : 0n);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return bits.toString(16).padStart(16, '0');
|
|
385
|
+
}
|
|
386
|
+
function luminanceAt(rgba, width, x, y) {
|
|
387
|
+
const idx = (y * width + x) * 4;
|
|
388
|
+
return 0.299 * rgba[idx] + 0.587 * rgba[idx + 1] + 0.114 * rgba[idx + 2];
|
|
389
|
+
}
|
|
390
|
+
function hashDistance(a, b) {
|
|
391
|
+
let x = BigInt(`0x${a}`) ^ BigInt(`0x${b}`);
|
|
392
|
+
let count = 0;
|
|
393
|
+
while (x) {
|
|
394
|
+
count += Number(x & 1n);
|
|
395
|
+
x >>= 1n;
|
|
396
|
+
}
|
|
397
|
+
return count;
|
|
398
|
+
}
|
|
399
|
+
function sha256(buf) {
|
|
400
|
+
return createHash('sha256').update(buf).digest('hex');
|
|
401
|
+
}
|
|
402
|
+
function escapeTable(value) {
|
|
403
|
+
return value.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=screenshot-auditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot-auditor.js","sourceRoot":"","sources":["../../src/core/screenshot-auditor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
import { markComplete } from './run-context.js';
|
|
5
|
+
export function writeScreenshotExplorationPlan(ctx, model, coverage, capture) {
|
|
6
|
+
const analysisDir = join(ctx.pageDir, 'analysis');
|
|
7
|
+
const screenshotDir = ctx.agentScreenshotsDir;
|
|
8
|
+
mkdirSync(analysisDir, { recursive: true });
|
|
9
|
+
mkdirSync(screenshotDir, { recursive: true });
|
|
10
|
+
const markdownPath = join(analysisDir, 'screenshot-exploration-plan.md');
|
|
11
|
+
const jsonPath = join(analysisDir, 'screenshot-exploration-plan.json');
|
|
12
|
+
const payload = {
|
|
13
|
+
pageKey: ctx.pageKey,
|
|
14
|
+
pageName: ctx.options.pageName,
|
|
15
|
+
generatedAt: new Date().toISOString(),
|
|
16
|
+
originalUrl: ctx.options.prototypeUrl,
|
|
17
|
+
targetUrl: capture.targetUrl,
|
|
18
|
+
finalUrl: capture.finalUrl,
|
|
19
|
+
viewport: capture.viewport,
|
|
20
|
+
phoneScreenSelector: '.phone-screen',
|
|
21
|
+
screenshotDir: relative(ctx.taskDir, screenshotDir),
|
|
22
|
+
agentRuntimeLog: relative(ctx.taskDir, join(ctx.agentRuntimeDir, 'agent-screenshot-exploration.json')),
|
|
23
|
+
agentManifest: relative(ctx.taskDir, ctx.agentManifestPath),
|
|
24
|
+
captureMode: capture.captureMode,
|
|
25
|
+
screenshotPolicy: {
|
|
26
|
+
requiredClipSelector: '.phone-screen',
|
|
27
|
+
forbiddenCapture: ['page.screenshot({ fullPage: true })', 'browser viewport screenshot', 'prototype platform shell screenshot'],
|
|
28
|
+
invalidContextExamples: ['login expired', 'relogin context invalid', 'permission guard', 'wrong account', 'wrong route'],
|
|
29
|
+
manualBaselinePolicy: 'If automated exploration captures an invalid or stale context, mark that screenshot as superseded and use the user-confirmed manual screenshot/state as the implementation baseline.',
|
|
30
|
+
rejectIfVisibleTextMatches: [
|
|
31
|
+
'TradeApp Workflow Hub',
|
|
32
|
+
'搜索页面 / 路由 / Screen ID',
|
|
33
|
+
'App 模拟器',
|
|
34
|
+
'UI 规范',
|
|
35
|
+
'交互原型',
|
|
36
|
+
'设计稿',
|
|
37
|
+
],
|
|
38
|
+
dedupeAuditCommand: `p2f screenshots-audit --url "${ctx.options.prototypeUrl}"`,
|
|
39
|
+
},
|
|
40
|
+
runtimeArtifacts: {
|
|
41
|
+
dom: relative(ctx.taskDir, capture.dom),
|
|
42
|
+
accessibility: relative(ctx.taskDir, capture.accessibility),
|
|
43
|
+
computedStyles: relative(ctx.taskDir, capture.computedStyles),
|
|
44
|
+
visibleText: relative(ctx.taskDir, capture.visibleText),
|
|
45
|
+
interactionCandidates: relative(ctx.taskDir, model.candidateInteractionsPath),
|
|
46
|
+
featureCoverage: relative(ctx.taskDir, coverage.markdownPath),
|
|
47
|
+
},
|
|
48
|
+
expectedStates: coverage.expectedStates,
|
|
49
|
+
expectedInteractions: coverage.expectedInteractions,
|
|
50
|
+
candidateInteractions: model.outline.candidateInteractions,
|
|
51
|
+
};
|
|
52
|
+
writeFileSync(jsonPath, JSON.stringify(payload, null, 2) + '\n', 'utf8');
|
|
53
|
+
writeFileSync(markdownPath, renderMarkdown(ctx, model, coverage, capture, screenshotDir), 'utf8');
|
|
54
|
+
logger.success(`Screenshot exploration plan → ${markdownPath}`);
|
|
55
|
+
markComplete(ctx, 'write-screenshot-exploration-plan');
|
|
56
|
+
return { markdownPath, jsonPath };
|
|
57
|
+
}
|
|
58
|
+
function renderMarkdown(ctx, model, coverage, capture, screenshotDir) {
|
|
59
|
+
const lines = [];
|
|
60
|
+
lines.push(`# 截图探索计划 - ${model.pageName}`);
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push('这是给 agent 执行的截图探索计划。`p2f generate` 不再直接截图。');
|
|
63
|
+
lines.push('实现 Flutter 前,agent 必须打开原型页面,观察 live page,主动探索可见流程并保存截图。');
|
|
64
|
+
lines.push('');
|
|
65
|
+
lines.push('## 目标页面');
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push(`- 原始链接:${ctx.options.prototypeUrl}`);
|
|
68
|
+
lines.push(`- 实际探索链接:${capture.targetUrl}`);
|
|
69
|
+
lines.push(`- 运行态探测后的最终链接:${capture.finalUrl}`);
|
|
70
|
+
lines.push(`- 视口:${capture.viewport.width}x${capture.viewport.height} @ deviceScaleFactor=2`);
|
|
71
|
+
lines.push('- 手机屏容器选择器:`.phone-screen`');
|
|
72
|
+
lines.push(`- 截图目录:\`${relative(ctx.taskDir, screenshotDir)}/\``);
|
|
73
|
+
lines.push(`- 探索日志:\`${relative(ctx.taskDir, join(ctx.agentRuntimeDir, 'agent-screenshot-exploration.json'))}\``);
|
|
74
|
+
lines.push(`- agent 产物清单:\`${relative(ctx.taskDir, ctx.agentManifestPath)}\``);
|
|
75
|
+
lines.push('');
|
|
76
|
+
lines.push('## agent 必须执行的行为');
|
|
77
|
+
lines.push('');
|
|
78
|
+
lines.push('- 必须打开上面列出的“实际探索链接”,不要打开原始链接;实际探索链接已附加 `is_mobile=1&app_simulator=1` 并经过 CLI 校验。');
|
|
79
|
+
lines.push('- 截图只能截 `.phone-screen` 元素;禁止保存整个浏览器 viewport 或 `page.screenshot({ fullPage: true })` 的结果。');
|
|
80
|
+
lines.push('- 如果截图里出现 TradeApp Workflow Hub、左侧平台菜单、搜索框、Docs/UI 规范/交互原型/设计稿入口,说明截错了,必须删除重截。');
|
|
81
|
+
lines.push('- 不要把 CLI 识别出的候选交互当作完整结果,它们只能作为线索。');
|
|
82
|
+
lines.push('- 从 live prototype 页面开始,等待可见 UI 稳定后先截图初始态。');
|
|
83
|
+
lines.push('- 滚动所有有意义的滚动容器,不要只滚动 `window`,直到可见内容完整覆盖。');
|
|
84
|
+
lines.push('- 主动检查 Tab、筛选、下拉、展开项、底部抽屉、弹窗、输入聚焦态、空态、加载态、错误态、图表/详情切换等状态。');
|
|
85
|
+
lines.push('- 即使控件没有出现在 `cli/runtime/interactions.candidate.json` 中,只要 live page 上可见,也要尝试探索。');
|
|
86
|
+
lines.push('- 结合 `cli/analysis/feature-coverage.md` 和 Vue 源码寻找初始 DOM 中不明显的状态或流程。');
|
|
87
|
+
lines.push('- 如果自动截图落入登录过期、重登上下文失效、权限拦截、错误账号、错误路由等非目标态,不要把该截图作为实现基准;必须在探索日志中标记为 `invalid-context` 或 `superseded`。');
|
|
88
|
+
lines.push('- 如果用户提供了手动截图或明确确认目标态,必须把它记录为 `manualBaselines`,并优先按该基准实现。');
|
|
89
|
+
lines.push('- 将每次尝试的动作、结果和截图路径记录到 `agent/runtime/agent-screenshot-exploration.json`。');
|
|
90
|
+
lines.push('- 将截图、探索日志、说明文件和最终修改的 Flutter 文件记录到 `agent/manifest.json`。');
|
|
91
|
+
lines.push('- 如果某个状态无法安全到达,必须记录原因,不能静默跳过。');
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push('## 最小截图集合');
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push('- `agent/screenshots/01-initial.png`:首个稳定可见页面状态;必须截取 `.phone-screen` 元素。');
|
|
96
|
+
lines.push('- `agent/screenshots/02-scroll-*.png`:覆盖页面全部内容所需的滚动分段截图。');
|
|
97
|
+
lines.push('- 每个有意义的 Tab、筛选、分段控制状态至少一张截图。');
|
|
98
|
+
lines.push('- 每个通过探索发现的弹窗、抽屉、下拉、选择器、展开区域和输入聚焦态至少一张截图。');
|
|
99
|
+
lines.push('- 对源码/文档预期但默认页面不可见的状态补充截图。');
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('## 截图保存协议');
|
|
102
|
+
lines.push('');
|
|
103
|
+
lines.push('- Playwright 中使用 `await page.locator(".phone-screen").screenshot({ path })`,不要使用整页截图。');
|
|
104
|
+
lines.push('- 保存后检查图片尺寸应接近手机屏宽,不应出现左侧原型平台菜单和右侧大块白屏。');
|
|
105
|
+
lines.push('- 同一状态如果 UI 完全一样,只保留第一张;后续尝试记录为 `no-effect`,不要保存重复 PNG。');
|
|
106
|
+
lines.push('- 如果已保存截图后来被确认是登录失效、权限拦截、旧路由或其他非目标态,不要删除历史线索;在 `supersededScreenshots` 中标记原因,并避免后续按它实现。');
|
|
107
|
+
lines.push('- 探索完成后执行 `p2f screenshots-audit --url "<页面链接>"`,按 `cli/analysis/agent-screenshot-audit.md` 删除重复图或重截错误图。');
|
|
108
|
+
lines.push('- 如果审计报告提示疑似平台壳层截图,必须删除该图并用 `.phone-screen` 重新截图。');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('## 按需读取策略');
|
|
111
|
+
lines.push('');
|
|
112
|
+
lines.push('- 先读 `main.md`、本计划、`cli/analysis/feature-coverage.md` 和 `cli/analysis/implementation-plan.md`。');
|
|
113
|
+
lines.push('- 大型 JSON 只在需要定位结构、样式或 selector 时读取相关片段;不要把全部 `cli/runtime/dom.json` 或 `cli/runtime/computed-styles.json` 一次性塞入上下文。');
|
|
114
|
+
lines.push('- 文案、字号、颜色分别按需查 `cli/runtime/visible-text.txt`、`cli/analysis/text-style-map.md` 和项目主题 token。');
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('## 运行态产物');
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push(`- DOM: \`${relative(ctx.taskDir, capture.dom)}\``);
|
|
119
|
+
lines.push(`- 可访问性树:\`${relative(ctx.taskDir, capture.accessibility)}\``);
|
|
120
|
+
lines.push(`- 计算样式:\`${relative(ctx.taskDir, capture.computedStyles)}\``);
|
|
121
|
+
lines.push(`- 可见文本:\`${relative(ctx.taskDir, capture.visibleText)}\``);
|
|
122
|
+
lines.push(`- 候选交互:\`${relative(ctx.taskDir, model.candidateInteractionsPath)}\``);
|
|
123
|
+
lines.push(`- 功能覆盖清单:\`${relative(ctx.taskDir, coverage.markdownPath)}\``);
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push('## 运行态候选交互');
|
|
126
|
+
lines.push('');
|
|
127
|
+
renderCandidates(lines, model.outline.candidateInteractions);
|
|
128
|
+
lines.push('');
|
|
129
|
+
lines.push('## 源码/文档预期交互');
|
|
130
|
+
lines.push('');
|
|
131
|
+
if (coverage.expectedInteractions.length === 0) {
|
|
132
|
+
lines.push('- (无)');
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
for (const item of coverage.expectedInteractions.slice(0, 80)) {
|
|
136
|
+
lines.push(`- [${item.runtimeStatus}] ${item.kind} · ${item.label} · ${item.sourceFile} · ${item.evidence}`);
|
|
137
|
+
}
|
|
138
|
+
if (coverage.expectedInteractions.length > 80) {
|
|
139
|
+
lines.push(`- …另有 ${coverage.expectedInteractions.length - 80} 项`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
lines.push('');
|
|
143
|
+
lines.push('## 源码/文档预期状态');
|
|
144
|
+
lines.push('');
|
|
145
|
+
if (coverage.expectedStates.length === 0) {
|
|
146
|
+
lines.push('- (无)');
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
for (const state of coverage.expectedStates.slice(0, 80)) {
|
|
150
|
+
lines.push(`- ${state.kind} · ${state.label} · ${state.sourceFile} · ${state.evidence}`);
|
|
151
|
+
}
|
|
152
|
+
if (coverage.expectedStates.length > 80) {
|
|
153
|
+
lines.push(`- …另有 ${coverage.expectedStates.length - 80} 项`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
lines.push('');
|
|
157
|
+
lines.push('## 必须写入的探索日志结构');
|
|
158
|
+
lines.push('');
|
|
159
|
+
lines.push('请按下面结构写入 `agent/runtime/agent-screenshot-exploration.json`:');
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push('```json');
|
|
162
|
+
lines.push('{');
|
|
163
|
+
lines.push(' "pageKey": "page-key",');
|
|
164
|
+
lines.push(' "targetUrl": "实际探索的原型链接",');
|
|
165
|
+
lines.push(' "manualBaselines": [');
|
|
166
|
+
lines.push(' { "source": "user-provided screenshot or description", "state": "用户确认的目标态", "path": "agent/screenshots/manual-baseline.png", "notes": "自动截图落入非目标态时的实现基准" }');
|
|
167
|
+
lines.push(' ],');
|
|
168
|
+
lines.push(' "screenshots": [');
|
|
169
|
+
lines.push(' { "path": "agent/screenshots/01-initial.png", "state": "initial", "action": "open", "clipSelector": ".phone-screen", "notes": "首个稳定可见状态" }');
|
|
170
|
+
lines.push(' ],');
|
|
171
|
+
lines.push(' "attempts": [');
|
|
172
|
+
lines.push(' { "action": "点击某个 Tab", "status": "captured|no-effect|failed|skipped|invalid-context|superseded", "screenshot": "agent/screenshots/...", "notes": "" }');
|
|
173
|
+
lines.push(' ],');
|
|
174
|
+
lines.push(' "supersededScreenshots": [');
|
|
175
|
+
lines.push(' { "path": "agent/screenshots/...", "reason": "登录过期/上下文失效/权限拦截/错误账号/错误路由等非目标态", "replacement": "manualBaselines[0] or agent/screenshots/..." }');
|
|
176
|
+
lines.push(' ],');
|
|
177
|
+
lines.push(' "duplicates": [');
|
|
178
|
+
lines.push(' { "path": "agent/screenshots/...", "duplicateOf": "agent/screenshots/...", "reason": "视觉完全一致,未保留重复图" }');
|
|
179
|
+
lines.push(' ],');
|
|
180
|
+
lines.push(' "uncovered": [');
|
|
181
|
+
lines.push(' { "label": "未覆盖的状态或交互", "reason": "无法到达的原因" }');
|
|
182
|
+
lines.push(' ]');
|
|
183
|
+
lines.push('}');
|
|
184
|
+
lines.push('```');
|
|
185
|
+
lines.push('');
|
|
186
|
+
return lines.join('\n') + '\n';
|
|
187
|
+
}
|
|
188
|
+
function renderCandidates(lines, candidates) {
|
|
189
|
+
if (candidates.length === 0) {
|
|
190
|
+
lines.push('- (未检测到;agent 仍必须手动检查 live page)');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
for (const c of candidates.slice(0, 80)) {
|
|
194
|
+
lines.push(`- ${c.kind} · \`${c.selector}\`${c.label ? ` · ${c.label}` : ''} · 置信度=${c.confidence.toFixed(2)} · ${c.description}`);
|
|
195
|
+
}
|
|
196
|
+
if (candidates.length > 80) {
|
|
197
|
+
lines.push(`- …另有 ${candidates.length - 80} 项`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=screenshot-exploration-plan-writer.js.map
|