@juxtapose/core 0.2.0 → 0.4.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.
@@ -3,6 +3,17 @@
3
3
  // A pure function, separate from posting it, so the formatting can be tested
4
4
  // without a network or a token — and so `jux run --json` consumers can render
5
5
  // the same summary elsewhere.
6
+ //
7
+ // The comment is a triage surface, not a report. Two constraints shape it:
8
+ //
9
+ // 1. The `###` heading is the only part that renders in the PR timeline
10
+ // collapse and in GitHub notification emails — tables do not. For a large
11
+ // share of readers the heading IS the comment, so the decisive number
12
+ // (largest mismatch) goes there rather than in row one of a table.
13
+ // 2. Images are impossible. GitHub's sanitizer strips `src` from both
14
+ // `<img src="data:...">` and `![](data:...)`; only https:// survives, via
15
+ // camo. So the text has to carry everything and link out for pixels.
16
+ import { formatPercent } from './format.js';
6
17
  /**
7
18
  * Invisible marker identifying our own comment.
8
19
  *
@@ -11,89 +22,320 @@
11
22
  * A PR with ten pushes ends up with one comment, not ten.
12
23
  */
13
24
  export const COMMENT_MARKER = '<!-- juxtapose-bot -->';
25
+ /** GitHub comments have a size limit, but attention is the binding constraint
26
+ * long before bytes are: nobody triages row 34 in a PR comment. */
27
+ const MAX_ROWS = 30;
14
28
  /** Stories that block a merge — anything a human or agent must judge. */
15
29
  function blocking(report) {
16
30
  return report.results.filter((r) => r.status === 'fail' || r.status === 'new' || r.status === 'stale');
17
31
  }
32
+ /** A story whose pixels differ. An errored capture is marked 'fail' too, but it
33
+ * is not a visual decision — nothing about it can be reviewed or approved. */
34
+ const changed = (r) => r.status === 'fail' && !r.error;
18
35
  export function buildComment({ report, artifactUrl }) {
19
- const s = report.summary;
20
36
  const needsReview = blocking(report);
21
37
  const lines = [COMMENT_MARKER, ''];
22
- // Lead with the verdict. Someone scanning a PR should not have to parse a
23
- // table to learn whether they need to do anything.
24
38
  if (needsReview.length === 0) {
25
- lines.push(`### Visual tests passed`, '');
26
- lines.push(`${s.pass} ${s.pass === 1 ? 'story' : 'stories'} match their baselines.`);
27
- if (s.skip > 0)
28
- lines.push(`${s.skip} skipped.`);
39
+ lines.push(`### Visual tests passed`, '', passingBody(report));
29
40
  lines.push('', footer(report, artifactUrl));
30
41
  return lines.join('\n');
31
42
  }
32
- lines.push(`### Visual changes need review`, '');
33
- lines.push('| | count |', '| --- | --- |');
34
- if (s.pass > 0)
35
- lines.push(`| pass | ${s.pass} |`);
36
- if (s.fail > 0)
37
- lines.push(`| **changed** | **${s.fail}** |`);
38
- if (s.new > 0)
39
- lines.push(`| **new** | **${s.new}** |`);
40
- if (s.stale > 0)
41
- lines.push(`| **stale** | **${s.stale}** |`);
42
- if (s.skip > 0)
43
- lines.push(`| skipped | ${s.skip} |`);
44
- lines.push('');
45
- // Open by default: the whole point is that a reviewer sees what changed
46
- // without clicking. Collapsed only when the list is long enough to bury the
47
- // rest of the PR conversation.
48
- const open = needsReview.length <= 10 ? ' open' : '';
49
- lines.push(`<details${open}>`, `<summary>${needsReview.length} ${needsReview.length === 1 ? 'story' : 'stories'}</summary>`, '');
50
- lines.push('| story | status | changed |', '| --- | --- | --- |');
51
- for (const r of needsReview.slice(0, MAX_ROWS)) {
52
- lines.push(`| \`${r.storyId}\` | ${label(r.status)} | ${detail(r)} |`);
43
+ const rows = triageOrder(needsReview);
44
+ lines.push(`### ${heading(rows)}`, '');
45
+ // A first run has nothing to compare against, and the table alone does not
46
+ // say so — every other shape is self-explanatory from the heading.
47
+ if (rows.every((r) => r.status === 'new')) {
48
+ lines.push('Nothing to compare against: these stories have never been approved. ' +
49
+ 'Check the captures look right, then approve to establish them as baselines.', '');
53
50
  }
54
- if (needsReview.length > MAX_ROWS) {
55
- lines.push(`| …and ${needsReview.length - MAX_ROWS} more | | |`);
56
- }
57
- lines.push('', '</details>', '');
58
- lines.push('To accept these changes:', '', '```bash', 'jux approve --all', '```', '');
51
+ lines.push(...table(rows));
52
+ lines.push('');
53
+ // Uncollapsed, and one fence. The comment is edited in place, so GitHub does
54
+ // not re-notify on a push — the repeat reader only sees this when they come
55
+ // back to act on it. The first-time reader is the one who would otherwise pay
56
+ // a click to discover the tool even has a CTA.
57
+ lines.push('```bash', ...commands(rows), '```', '');
58
+ lines.push(...guidance(rows));
59
59
  lines.push(footer(report, artifactUrl));
60
60
  return lines.join('\n');
61
61
  }
62
- /** GitHub comments have a size limit; a suite where everything changed should
63
- * not produce an unpostable comment. */
64
- const MAX_ROWS = 50;
65
- function label(status) {
66
- switch (status) {
67
- case 'fail':
68
- return 'changed';
69
- case 'new':
70
- return 'no baseline';
71
- case 'stale':
72
- return 'stale baseline';
73
- default:
74
- return status;
62
+ /**
63
+ * Order by triage cost, not by magnitude alone.
64
+ *
65
+ * Errors lead: a capture that crashed is not a visual decision, and it cannot
66
+ * be ranked by percentage anyway — a crash records 0% (run.ts) while a
67
+ * dimension mismatch records 100% (diff.ts), so sorting on the number would
68
+ * scatter the two error kinds to opposite ends. `error != null` is the only
69
+ * reliable marker.
70
+ *
71
+ * Then fail by magnitude descending, then new, then stale — stale last because
72
+ * its pixels are byte-identical and it is the cheapest thing on the list to
73
+ * clear. Ties break on storyId so the body is stable across re-runs; an
74
+ * unstable order would show up as a phantom edit on every push.
75
+ */
76
+ function triageOrder(results) {
77
+ const rank = (r) => {
78
+ if (r.error)
79
+ return 0;
80
+ if (r.status === 'fail')
81
+ return 1;
82
+ if (r.status === 'new')
83
+ return 2;
84
+ return 3;
85
+ };
86
+ return [...results].sort((a, b) => {
87
+ const byRank = rank(a) - rank(b);
88
+ if (byRank !== 0)
89
+ return byRank;
90
+ if (rank(a) === 1) {
91
+ const byPct = b.mismatchPercent - a.mismatchPercent;
92
+ if (byPct !== 0)
93
+ return byPct;
94
+ }
95
+ return a.storyId.localeCompare(b.storyId);
96
+ });
97
+ }
98
+ /**
99
+ * The heading carries the verdict AND its magnitude.
100
+ *
101
+ * Under a byte-exact hash, "3 changed" is produced equally by three
102
+ * antialiasing hairs and three collapsed layouts. The count alone cannot be
103
+ * acted on; the largest mismatch can.
104
+ */
105
+ function heading(rows) {
106
+ const errors = rows.filter((r) => r.error).length;
107
+ const fails = rows.filter(changed);
108
+ if (errors > 0) {
109
+ const rest = rows.length - errors;
110
+ const tail = rest > 0 ? ` — ${rest} more ${rest === 1 ? 'needs' : 'need'} review` : '';
111
+ return `${count(errors, 'story', 'stories')} failed to capture${tail}`;
75
112
  }
113
+ // Derived from `rows`, not `report.summary`: summary.fail counts errored
114
+ // stories too (run.ts marks them 'fail'), so a summary-based check here would
115
+ // silently depend on the errors branch above catching them first.
116
+ //
117
+ // A first run is 100% new by construction. Calling that a failure teaches
118
+ // people the tool cries wolf on day one.
119
+ if (rows.every((r) => r.status === 'new')) {
120
+ return `${count(rows.length, 'new story', 'new stories')} — no baselines yet`;
121
+ }
122
+ if (rows.every((r) => r.status === 'stale')) {
123
+ return `${count(rows.length, 'stale baseline', 'stale baselines')} — pixels identical, re-approve to re-anchor`;
124
+ }
125
+ if (fails.length === 0) {
126
+ return `${count(rows.length, 'story', 'stories')} need review`;
127
+ }
128
+ const largest = Math.max(...fails.map((r) => r.mismatchPercent));
129
+ // Below 0.1% the individual numbers are noise and a wall of them reads as
130
+ // alarming; saying so once is more useful than quoting the largest hair.
131
+ // `formatPercent` never renders "0.00%", which would read as a tool bug.
132
+ const magnitude = largest < 0.1 ? 'all under 0.1%' : `largest ${formatPercent(largest)}`;
133
+ // Mixed: name the shape, since "5 changes" would overstate what changed.
134
+ if (rows.length > fails.length) {
135
+ return `${count(rows.length, 'story', 'stories')} need review — ${fails.length} changed, ${magnitude}`;
136
+ }
137
+ const noun = count(fails.length, 'visual change', 'visual changes');
138
+ const verb = fails.length === 1 ? 'needs' : 'need';
139
+ const detail = fails.length === 1 && largest >= 0.1 ? formatPercent(largest) : magnitude;
140
+ return `${noun} ${verb} review — ${detail}`;
76
141
  }
142
+ /**
143
+ * Drop columns that carry no information.
144
+ *
145
+ * When every row shares a status the column is a stack of identical strings,
146
+ * and when nothing was compared there is no mismatch to show. Both are the
147
+ * common case: a fail-only run and a first run.
148
+ */
149
+ function table(rows) {
150
+ const statuses = new Set(rows.map((r) => r.status));
151
+ const showStatus = statuses.size > 1;
152
+ // Nothing to say about magnitude when nothing was compared — an all-new run
153
+ // would otherwise carry a column reading "first capture" on every row.
154
+ const showDetail = rows.some((r) => r.error || r.status === 'fail' || r.status === 'stale');
155
+ const head = [
156
+ 'story',
157
+ showStatus ? 'status' : null,
158
+ showDetail ? 'changed' : null,
159
+ ].filter((c) => c !== null);
160
+ const out = [`| ${head.join(' | ')} |`, `| ${head.map(() => '---').join(' | ')} |`];
161
+ for (const r of rows.slice(0, MAX_ROWS)) {
162
+ const cells = [
163
+ `\`${cell(r.storyId)}\``,
164
+ showStatus ? (r.status === 'fail' ? 'changed' : r.status) : null,
165
+ showDetail ? detail(r) : null,
166
+ ].filter((c) => c !== null);
167
+ out.push(`| ${cells.join(' | ')} |`);
168
+ }
169
+ if (rows.length > MAX_ROWS) {
170
+ const omitted = rows.slice(MAX_ROWS);
171
+ // "smallest changes" is only true when the tail is all fails — they sort by
172
+ // magnitude. `new` and `stale` sort after them by storyId, so a mixed tail
173
+ // is cut alphabetically and has no magnitude to be smallest of.
174
+ const why = omitted.every(changed) ? ', smallest changes omitted' : '';
175
+ // A plain line, not a table row: a row would need its cell count to track
176
+ // the columns above, and renders as trailing empty cells.
177
+ out.push('', `_…and ${omitted.length} more${why}._`);
178
+ }
179
+ return out;
180
+ }
181
+ /**
182
+ * One copyable sequence.
183
+ *
184
+ * `jux run` leads because approval reads the LAST LOCAL RUN — the run being
185
+ * reported here happened on a CI runner, so `jux approve` alone either fails
186
+ * with "no run found" or silently approves a stale local run. Keeping it in the
187
+ * same fence is deliberate: a reader who copies a later block must not end up
188
+ * with approve-without-run.
189
+ */
190
+ function commands(rows) {
191
+ const out = [annotate('jux run', 'approval reads a local run, so capture first')];
192
+ // Only worth investigating a change big enough to be something. Below 0.1%
193
+ // is antialiasing territory (see magnitudeHint in agent/handoff.ts), and
194
+ // pointing the "investigate this" tool at noise wastes the reader's time.
195
+ const worthInvestigating = rows.find((r) => changed(r) && r.mismatchPercent >= 0.1);
196
+ if (worthInvestigating) {
197
+ out.push(annotate(`jux handoff ${worthInvestigating.storyId}`, 'unsure? agent-ready prompt: what moved, where'));
198
+ }
199
+ // Never name an errored story here: approving a crashed capture bakes a
200
+ // broken baseline into the manifest, which is the one outcome worse than a
201
+ // red build.
202
+ const approvable = rows.find((r) => !r.error);
203
+ out.push(approvable
204
+ ? annotate('jux approve --all', `or --story ${approvable.storyId}`)
205
+ : 'jux approve --all');
206
+ return out;
207
+ }
208
+ /**
209
+ * Align a trailing `#` comment, or drop it.
210
+ *
211
+ * Story ids are long — the examples already hit 31 characters and a real design
212
+ * system goes well past that. Padding past the column produces a line that
213
+ * wraps inside GitHub's fixed-width code block, which is worse than having no
214
+ * annotation at all.
215
+ */
216
+ function annotate(command, note) {
217
+ return command.length > COMMENT_COLUMN
218
+ ? command
219
+ : `${command.padEnd(COMMENT_COLUMN)} # ${note}`;
220
+ }
221
+ const COMMENT_COLUMN = 32;
222
+ /**
223
+ * Reference material, collapsed.
224
+ *
225
+ * A first-timer reads this once; a repeat reader has internalized it. Only the
226
+ * statuses actually present get a paragraph — a fail-only run should not
227
+ * explain what "stale" means.
228
+ */
229
+ function guidance(rows) {
230
+ const has = (s) => rows.some((r) => r.status === s && !r.error);
231
+ const paras = [];
232
+ if (rows.some((r) => r.error)) {
233
+ paras.push('Some stories failed to capture. That is not a visual decision — approving ' +
234
+ 'one would bake a broken baseline into the manifest. Fix the capture first.');
235
+ }
236
+ if (has('fail')) {
237
+ paras.push('Pixels differ from the approved baseline. Decide intentional vs regression ' +
238
+ 'before approving.');
239
+ }
240
+ if (has('new')) {
241
+ paras.push('New stories have no baseline, so nothing was compared. Confirm the render is ' +
242
+ 'correct, then approve to establish it.');
243
+ }
244
+ if (has('stale')) {
245
+ paras.push('Stale baselines render **byte-identical** pixels — there is nothing to look at. ' +
246
+ 'The baseline was approved on a commit that is not in this history, usually a ' +
247
+ 'branch that never merged. Re-approving re-anchors it.');
248
+ }
249
+ paras.push('Then commit `juxtapose.manifest.json` and `.juxtapose/baselines/` — that is what ' +
250
+ 'makes approval a reviewable diff.');
251
+ paras.push('Prefer a UI? Start the run from the **Juxtapose** panel in Storybook to approve, ' +
252
+ 'diff, and copy agent prompts without leaving the browser.');
253
+ return [
254
+ '<details><summary>What these mean, and what to commit</summary>',
255
+ '',
256
+ ...paras.flatMap((p) => [p, '']),
257
+ '</details>',
258
+ '',
259
+ ];
260
+ }
261
+ function passingBody(report) {
262
+ const s = report.summary;
263
+ // `pass` counts CAPTURED passes, so "42 stories match" is a lie on a narrowed
264
+ // run where only six were captured.
265
+ const narrowed = report.capturedCount < report.totalCount;
266
+ const head = narrowed
267
+ ? `${count(s.pass, 'captured story matches', 'captured stories match')} their baselines.`
268
+ : `${count(s.pass, 'story matches', 'stories match')} their baselines.`;
269
+ return s.skip > 0 ? `${head} ${s.skip} skipped.` : head;
270
+ }
271
+ /**
272
+ * Say something true for every status — an em-dash column is dead weight, and
273
+ * on a first run it is dead weight on every single row.
274
+ */
77
275
  function detail(r) {
78
276
  if (r.error)
79
- return r.error;
80
- if (r.status !== 'fail')
81
- return '—';
82
- return formatPercent(r.mismatchPercent);
277
+ return cell(r.error);
278
+ // Blank only when the whole table is new stories and the column is dropped
279
+ // entirely; alongside other statuses it needs to say why it is not a number.
280
+ if (r.status === 'new')
281
+ return 'first capture';
282
+ // Reachable only after a byte-exact hash match (baseline/status.ts), so this
283
+ // is a guarantee rather than a guess.
284
+ if (r.status === 'stale')
285
+ return 'pixels identical';
286
+ const parts = [`**${formatPercent(r.mismatchPercent)}**`];
287
+ const where = regionHint(r);
288
+ if (where)
289
+ parts.push(where);
290
+ return parts.join(' · ');
291
+ }
292
+ /**
293
+ * How localized the change is — percentage alone cannot tell a text tweak from
294
+ * a token that moved everything. One region at top-left is a button; six
295
+ * scattered regions is a font or a design token.
296
+ *
297
+ * `computeChangedRegions` returns row-scan discovery order, NOT size order, so
298
+ * the largest has to be picked explicitly.
299
+ */
300
+ function regionHint(r) {
301
+ if (!r.viewport || r.changedRegions.length === 0)
302
+ return null;
303
+ const n = r.changedRegions.length;
304
+ const largest = r.changedRegions.reduce((a, b) => b.width * b.height > a.width * a.height ? b : a);
305
+ const v = r.viewport;
306
+ const cx = largest.x + largest.width / 2;
307
+ const cy = largest.y + largest.height / 2;
308
+ const vertical = cy < v.height / 3 ? 'top' : cy > (v.height * 2) / 3 ? 'bottom' : 'middle';
309
+ const horizontal = cx < v.width / 3 ? 'left' : cx > (v.width * 2) / 3 ? 'right' : 'center';
310
+ return `${n} ${n === 1 ? 'region' : 'regions'}, ${vertical}-${horizontal}`;
83
311
  }
84
312
  function footer(report, artifactUrl) {
313
+ // A narrowed capture count reads as alarming without a reason; naming the
314
+ // mechanism rules out "the tool gave up and skipped things".
315
+ const captured = report.capturedCount < report.totalCount
316
+ ? `${report.capturedCount} of ${report.totalCount} captured (change analysis)`
317
+ : `${report.capturedCount}/${report.totalCount} captured`;
85
318
  const parts = [
86
319
  `\`${report.commitSha.slice(0, 7)}\``,
87
- `${report.capturedCount}/${report.totalCount} captured`,
320
+ captured,
88
321
  `${(report.durationMs / 1000).toFixed(1)}s`,
89
322
  ];
90
323
  const meta = `<sub>juxtapose · ${parts.join(' · ')}</sub>`;
91
- return artifactUrl ? `${meta}\n\n<sub>[Images and diffs](${artifactUrl})</sub>` : meta;
324
+ // Describes the destination, not the content: it resolves to a run page with
325
+ // a zip, which is a true statement even if the upload step was skipped.
326
+ return artifactUrl
327
+ ? `${meta}\n\n<sub>[CI run — report.html is in the artifacts](${artifactUrl})</sub>`
328
+ : meta;
329
+ }
330
+ /**
331
+ * A pipe in a cell silently corrupts the table into gibberish. Story ids come
332
+ * from the platform and error strings come from thrown exceptions — neither is
333
+ * ours to trust.
334
+ */
335
+ function cell(value) {
336
+ return value.replace(/\|/g, '\\|');
92
337
  }
93
- /** Never show "0.00%" next to the word "changed" — it reads as a tool bug. */
94
- function formatPercent(pct) {
95
- if (pct >= 0.01)
96
- return `${pct.toFixed(2)}%`;
97
- return pct > 0 ? '<0.01%' : '0%';
338
+ function count(n, one, many) {
339
+ return `${n} ${n === 1 ? one : many}`;
98
340
  }
99
341
  //# sourceMappingURL=comment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/report/comment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/report/comment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Never show "0.00%" next to the word "changed" — it reads as a tool bug. */
2
+ export declare function formatPercent(pct: number): string;
@@ -0,0 +1,12 @@
1
+ // How the mismatch number is written, everywhere it is written.
2
+ //
3
+ // Shared rather than copied because it is a product rule, not a formatter: a
4
+ // real change displaying as "0.00%" reads as a tool bug, so anything below the
5
+ // rounding floor says "<0.01%" instead. Five call sites had their own copy.
6
+ /** Never show "0.00%" next to the word "changed" — it reads as a tool bug. */
7
+ export function formatPercent(pct) {
8
+ if (pct >= 0.01)
9
+ return `${pct.toFixed(2)}%`;
10
+ return pct > 0 ? '<0.01%' : '0%';
11
+ }
12
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/report/format.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,4EAA4E;AAE5E,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7C,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC"}
@@ -18,6 +18,18 @@ export interface GitHubContext {
18
18
  * build that otherwise passed.
19
19
  */
20
20
  export declare function resolveGitHubContext(env?: NodeJS.ProcessEnv): Promise<GitHubContext | null>;
21
+ /**
22
+ * The Actions run page, where uploaded artifacts are listed.
23
+ *
24
+ * Deliberately not derived from env vars alone. `GITHUB_RUN_ID` is set on every
25
+ * Actions run including ones that upload nothing, so keying off it would
26
+ * produce a link to an empty artifacts list — a promise to a dead end, which
27
+ * costs the reader a click and some trust. The caller passes `hasArtifact` only
28
+ * when it actually wrote a report, so no report means no link.
29
+ *
30
+ * We cannot link the artifact file itself: that URL is auth-gated and expires.
31
+ */
32
+ export declare function resolveArtifactUrl(hasArtifact: boolean, env?: NodeJS.ProcessEnv): string | undefined;
21
33
  /**
22
34
  * Create or update the juxtapose comment.
23
35
  *
@@ -28,6 +28,27 @@ export async function resolveGitHubContext(env = process.env) {
28
28
  apiUrl: env.GITHUB_API_URL ?? 'https://api.github.com',
29
29
  };
30
30
  }
31
+ /**
32
+ * The Actions run page, where uploaded artifacts are listed.
33
+ *
34
+ * Deliberately not derived from env vars alone. `GITHUB_RUN_ID` is set on every
35
+ * Actions run including ones that upload nothing, so keying off it would
36
+ * produce a link to an empty artifacts list — a promise to a dead end, which
37
+ * costs the reader a click and some trust. The caller passes `hasArtifact` only
38
+ * when it actually wrote a report, so no report means no link.
39
+ *
40
+ * We cannot link the artifact file itself: that URL is auth-gated and expires.
41
+ */
42
+ export function resolveArtifactUrl(hasArtifact, env = process.env) {
43
+ if (!hasArtifact)
44
+ return undefined;
45
+ const server = env.GITHUB_SERVER_URL;
46
+ const repository = env.GITHUB_REPOSITORY;
47
+ const runId = env.GITHUB_RUN_ID;
48
+ if (!server || !repository || !runId)
49
+ return undefined;
50
+ return `${server}/${repository}/actions/runs/${runId}`;
51
+ }
31
52
  /**
32
53
  * Find the PR number.
33
54
  *
@@ -1 +1 @@
1
- {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/report/github.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/report/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { RunReport } from '../types.js';
2
+ export interface HtmlReportOptions {
3
+ report: RunReport;
4
+ /** storyId -> image bytes. Missing entries are expected: a new story has no
5
+ * baseline, a passing one needs no diff. */
6
+ images: Map<string, {
7
+ baseline?: Buffer;
8
+ current?: Buffer;
9
+ diff?: Buffer;
10
+ }>;
11
+ }
12
+ export declare function buildHtmlReport({ report, images }: HtmlReportOptions): string;