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.
Files changed (85) hide show
  1. package/README.md +149 -0
  2. package/bin/p2f.mjs +18 -0
  3. package/dist/cli.js +203 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/clean.js +35 -0
  6. package/dist/commands/clean.js.map +1 -0
  7. package/dist/commands/doctor.js +148 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/generate.js +120 -0
  10. package/dist/commands/generate.js.map +1 -0
  11. package/dist/commands/init.js +46 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/paths.js +58 -0
  14. package/dist/commands/paths.js.map +1 -0
  15. package/dist/commands/remove.js +23 -0
  16. package/dist/commands/remove.js.map +1 -0
  17. package/dist/commands/screenshots-audit.js +39 -0
  18. package/dist/commands/screenshots-audit.js.map +1 -0
  19. package/dist/commands/skills-install.js +21 -0
  20. package/dist/commands/skills-install.js.map +1 -0
  21. package/dist/commands/sync.js +84 -0
  22. package/dist/commands/sync.js.map +1 -0
  23. package/dist/commands/update.js +93 -0
  24. package/dist/commands/update.js.map +1 -0
  25. package/dist/core/existing-page-detector.js +463 -0
  26. package/dist/core/existing-page-detector.js.map +1 -0
  27. package/dist/core/fail-fast.js +50 -0
  28. package/dist/core/fail-fast.js.map +1 -0
  29. package/dist/core/feature-coverage-builder.js +667 -0
  30. package/dist/core/feature-coverage-builder.js.map +1 -0
  31. package/dist/core/flutter-project-scanner.js +393 -0
  32. package/dist/core/flutter-project-scanner.js.map +1 -0
  33. package/dist/core/implementation-plan-writer.js +190 -0
  34. package/dist/core/implementation-plan-writer.js.map +1 -0
  35. package/dist/core/logger.js +39 -0
  36. package/dist/core/logger.js.map +1 -0
  37. package/dist/core/package-info.js +33 -0
  38. package/dist/core/package-info.js.map +1 -0
  39. package/dist/core/paths.js +37 -0
  40. package/dist/core/paths.js.map +1 -0
  41. package/dist/core/playwright-capture.js +539 -0
  42. package/dist/core/playwright-capture.js.map +1 -0
  43. package/dist/core/playwright-cli.js +26 -0
  44. package/dist/core/playwright-cli.js.map +1 -0
  45. package/dist/core/playwright-install.js +40 -0
  46. package/dist/core/playwright-install.js.map +1 -0
  47. package/dist/core/prd-clone.js +131 -0
  48. package/dist/core/prd-clone.js.map +1 -0
  49. package/dist/core/prd-install.js +108 -0
  50. package/dist/core/prd-install.js.map +1 -0
  51. package/dist/core/profile.js +149 -0
  52. package/dist/core/profile.js.map +1 -0
  53. package/dist/core/project-doc-reader.js +252 -0
  54. package/dist/core/project-doc-reader.js.map +1 -0
  55. package/dist/core/report-writer.js +90 -0
  56. package/dist/core/report-writer.js.map +1 -0
  57. package/dist/core/route-name.js +60 -0
  58. package/dist/core/route-name.js.map +1 -0
  59. package/dist/core/run-context.js +160 -0
  60. package/dist/core/run-context.js.map +1 -0
  61. package/dist/core/screenshot-auditor.js +405 -0
  62. package/dist/core/screenshot-auditor.js.map +1 -0
  63. package/dist/core/screenshot-exploration-plan-writer.js +200 -0
  64. package/dist/core/screenshot-exploration-plan-writer.js.map +1 -0
  65. package/dist/core/semantic-model-builder.js +922 -0
  66. package/dist/core/semantic-model-builder.js.map +1 -0
  67. package/dist/core/skill-install.js +78 -0
  68. package/dist/core/skill-install.js.map +1 -0
  69. package/dist/core/stage-stub.js +24 -0
  70. package/dist/core/stage-stub.js.map +1 -0
  71. package/dist/core/task-index-writer.js +149 -0
  72. package/dist/core/task-index-writer.js.map +1 -0
  73. package/dist/core/update-checker.js +155 -0
  74. package/dist/core/update-checker.js.map +1 -0
  75. package/dist/core/vue-page-locator.js +748 -0
  76. package/dist/core/vue-page-locator.js.map +1 -0
  77. package/dist/core/vue-project-reader.js +116 -0
  78. package/dist/core/vue-project-reader.js.map +1 -0
  79. package/docs/artifacts-and-agent.md +203 -0
  80. package/docs/development.md +118 -0
  81. package/docs/usage.md +246 -0
  82. package/package.json +50 -0
  83. package/skills/p2f/SKILL.md +303 -0
  84. package/skills/p2f/references/page-layout-patterns.md +120 -0
  85. 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