@testivai/mcp 0.8.2 → 0.9.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 CHANGED
@@ -10,10 +10,10 @@ signals cannot settle.
10
10
 
11
11
  | Tool | What it does |
12
12
  |---|---|
13
- | `get_visual_results` | Reads `visual-report/results.json` and returns a one-line verdict per snapshot: passed (an auto-passed diff says which configured criterion passed it) / size changed (the current capture's dimensions differ from the baseline's: a taller or shorter page is read with the DOM summary and the page shift, and with an identical DOM it is the same content laid out differently, not content added; a width change is the viewport or device scale, not the page; every padded pixel counts as changed, so it is never passed and never noise) / likely render noise (the DOM and computed styles both match and the diff is small and scattered; still not proof) / concentrated in one area or large (the DOM and computed styles match, but every diff region fits in a box covering at most a third of the page, or over 1% of pixels differ: a bad capture or a change the style check does not cover, such as size, overflow, transform or an image, so compare the images before approving) / style-only change (identical DOM, different computed styles: a real change) / structural change (with the DOM summary) / styles not compared (identical DOM, style check unavailable: could be render noise or a stylesheet-only change, so compare the images before approving). Snapshots that share an identical signal (style-only change on the same elements, the same page shift, identical DOM with styles not compared, a large spread-out diff, likely render noise) are grouped first; a diff concentrated in one area is never grouped. Each missing baseline (committed, but no capture this run) gets its own line (several share one verdict line, so a partial run stays short), with nothing to approve: an intended removal deletes the baseline directory. It ends with the paths to open the HTML report and each diff image |
14
- | `explain_snapshot` | Layered evidence for one snapshot: pixel regions and the shape of the diff (`layers.pixel.shape`: scattered, concentrated or large), element attribution (which selectors shifted vs changed, whole-page shift), the DOM/style signal, and interpretation guidance; for a missing baseline, status `missing`, the committed baseline files and what to check instead |
13
+ | `get_visual_results` | Reads `visual-report/results.json`; when two or more snapshots share one environment change, opens with one line saying so and how many of them are rendering drift; then returns a one-line verdict per snapshot: passed (an auto-passed diff says which configured criterion passed it) / size changed (the current capture's dimensions differ from the baseline's: a taller or shorter page is read with the DOM summary and the page shift, and with an identical DOM it is the same content laid out differently, not content added; a width change is the viewport or device scale, not the page; every padded pixel counts as changed, so it is never passed and never noise) / rendering drift (the DOM and computed styles both match and the environment the snapshot was captured in changed since the baseline: a browser build, OS or distro, device scale or capture method, named in the verdict; the machine changed, not the page, so it is never called noise) / likely render noise (the DOM and computed styles both match and the diff is small and scattered; still not proof) / concentrated in one area or large (the DOM and computed styles match, but every diff region fits in a box covering at most a third of the page, or over 1% of pixels differ: a bad capture or a change the style check does not cover, such as size, overflow, transform or an image, so compare the images before approving) / style-only change (identical DOM, different computed styles: a real change) / structural change (with the DOM summary) / styles not compared (identical DOM, style check unavailable: could be render noise or a stylesheet-only change, so compare the images before approving). Snapshots that share an identical signal (style-only change on the same elements, the same page shift, identical DOM with styles not compared, a large spread-out diff, likely render noise) are grouped first; a diff concentrated in one area is never grouped. Each missing baseline (committed, but no capture this run) gets its own line (several share one verdict line, so a partial run stays short), with nothing to approve: an intended removal deletes the baseline directory. It ends with the paths to open the HTML report and each diff image |
14
+ | `explain_snapshot` | Layered evidence for one snapshot: pixel regions and the shape of the diff (`layers.pixel.shape`: scattered, concentrated or large), element attribution (which selectors shifted vs changed, whole-page shift), the DOM/style signal, the capture environment (`layers.environment`: what changed since the baseline, the rendering changes worded, and whether the diff is rendering drift), and interpretation guidance; for a missing baseline, status `missing`, the committed baseline files and what to check instead |
15
15
  | `get_report` | The raw `results.json` payload, for agents that parse structured data |
16
- | `get_diff` (alias `get_snapshot_diff`) | Returns the baseline, current, and diff **images** for one snapshot, downscaled to fit model context, so the agent can see the change; for a missing baseline, the committed baseline image only |
16
+ | `get_diff` (alias `get_snapshot_diff`) | Returns the baseline, current, and diff **images** for one snapshot so the agent can see the change: the full page downscaled to fit model context (it shows where the changes are), then, when it had to be downscaled, each changed region cropped at native scale with 32 px around it and as wide as the page (up to 1280 px), so text, wrapping and clipping stay readable on a tall full-page capture (up to 3 crops per call, each at most 1280×1024 px and 2,000,000 px in all; nearby regions share a crop, changes come before shifts, a wider area is sampled at 1/2 scale or smaller, a taller one shows its top rows, and after a size change each capture is cut to its own bounds). `region` (an index the response lists), or `y` and `height` (a full-width band), return only that crop; for a missing baseline, the committed baseline image only |
17
17
  | `list_baselines` | Lists the committed baselines under `.testivai/baselines/` |
18
18
  | `approve_snapshot` / `approve_all` | Promote reviewed captures to committed baselines (same as `testivai approve`), **only after a human confirms** |
19
19
 
@@ -79,8 +79,9 @@ to override) and respects `reportDir` from `.testivai/config.json`.
79
79
  (4.20% pixels differ) and the DOM changed (2 added, 1 removed, 0 attribute
80
80
  changes) — a real structural change; confirm it is intended before approving`.
81
81
  4. Agent calls `explain_snapshot homepage` for the evidence, and `get_diff
82
- homepage` to look at the images, then confirms the change matches the task
83
- (or fixes its own regression).
82
+ homepage` to look at the images (the full page, then the changed regions at
83
+ native scale), then confirms the change matches the task (or fixes its own
84
+ regression).
84
85
  5. Agent reports to the human: what changed, whether it looks intended, and
85
86
  which snapshots need approval. If the human says to approve, the agent calls
86
87
  `approve_snapshot`; otherwise the human approves with `npx testivai approve`
@@ -0,0 +1,104 @@
1
+ import { PNG } from 'pngjs';
2
+ import { type ProjectPaths, type RegionInfo, type SnapshotResult, type ToolContent } from './lib';
3
+ /**
4
+ * What get_diff returns for a snapshot: the full page downscaled to fit
5
+ * (it shows where the changes are), then the changed regions cropped at
6
+ * native scale and as wide as the page (up to 1280 px), since a tall
7
+ * full-page capture downscaled to 1024 px is too small to read (390×11844
8
+ * becomes 33×987). The regions are the ones in
9
+ * results.json, on the diff canvas: witness pads the two captures to max
10
+ * width × max height, anchored at the top left, so a region's box is the
11
+ * same place in the baseline, the current capture and the diff.
12
+ */
13
+ /**
14
+ * The bounds on get_diff's crops. Its description (tool-meta.ts), the
15
+ * README and docs/mcp.md quote these numbers, and tests hold them to it.
16
+ */
17
+ export declare const CROP_LIMITS: {
18
+ /** Crops in a get_diff response without `region` or `y`; each is sent as baseline, current and diff. */
19
+ readonly maxCrops: 3;
20
+ /** Pixels of context around each region, on every side. */
21
+ readonly padding: 32;
22
+ /** The widest crop at native scale: a wider area is sampled at 1/2, 1/3 and so on. */
23
+ readonly maxWidth: 1280;
24
+ /** The most rows a crop shows at its scale: a taller area shows its top rows. */
25
+ readonly maxHeight: 1024;
26
+ /** Pixels the crops of one response hold together, in each of the three images. */
27
+ readonly maxTotalPixels: 2000000;
28
+ };
29
+ export interface Box {
30
+ x: number;
31
+ y: number;
32
+ width: number;
33
+ height: number;
34
+ }
35
+ /** How an area of the canvas is shown in a crop. */
36
+ export interface CropWindow {
37
+ /** The area asked for, inside the canvas. */
38
+ area: Box;
39
+ /** The part of it the crop shows: `area`, cut to its top rows when it is taller than the limit. */
40
+ shown: Box;
41
+ /** 1 at native scale; n samples every nth pixel, for an area wider than CROP_LIMITS.maxWidth. */
42
+ scale: number;
43
+ }
44
+ /** A crop of one or more nearby regions (indices into the snapshot's results.json `regions`). */
45
+ export interface PlannedCrop extends CropWindow {
46
+ regions: number[];
47
+ }
48
+ export interface CropPlan {
49
+ /** The crops to send, in the order to send them. */
50
+ crops: PlannedCrop[];
51
+ /** Regions in none of them (over the caps, or with no area inside the canvas), ascending. */
52
+ notShown: number[];
53
+ }
54
+ /**
55
+ * How to show `area`: at native scale when it is at most maxWidth wide,
56
+ * otherwise sampled at the smallest whole step that fits; and its top
57
+ * maxHeight rows (at that scale) when it is taller.
58
+ */
59
+ export declare function cropWindow(area: Box): CropWindow;
60
+ /**
61
+ * Which regions to crop, and how. Each region gets its box plus
62
+ * CROP_LIMITS.padding px on every side, inside the canvas. Regions whose
63
+ * padded areas meet share one crop while the merged area still fits
64
+ * maxWidth × maxHeight. Each crop is then made as wide as the page, up to
65
+ * maxWidth (see widened), and a region that lies inside the box another
66
+ * crop shows joins that crop (a tall region's crop shows only its top rows,
67
+ * so the two cannot always merge). Changes come before shifts (a shift is
68
+ * content that only moved), then results.json order (top to bottom). At
69
+ * most maxCrops crops, holding at most maxTotalPixels together: a crop that
70
+ * would pass it is skipped, and a smaller one after it can still fit.
71
+ */
72
+ export declare function planCrops(regions: ReadonlyArray<RegionInfo>, canvas: {
73
+ width: number;
74
+ height: number;
75
+ }): CropPlan;
76
+ /**
77
+ * Cut `box` out of an image, inside the image's own bounds (a capture
78
+ * smaller than the canvas has no pixels in the padded band), taking every
79
+ * `scale`th pixel. Null when the box is outside the image.
80
+ */
81
+ export declare function cropPng(png: PNG, box: Box, scale: number): {
82
+ png: PNG;
83
+ box: Box;
84
+ } | null;
85
+ /** What get_diff crops on request: one region, or a full-width band. */
86
+ export interface DiffRequest {
87
+ /** Index into the snapshot's results.json `regions`. */
88
+ region?: number;
89
+ /** Top of the band, in canvas pixels; 0 when only `height` is given. */
90
+ y?: number;
91
+ /** Rows of the band; maxHeight at the band's scale when only `y` is given. */
92
+ height?: number;
93
+ }
94
+ /**
95
+ * get_diff's content for a snapshot in the results. Without a request: the
96
+ * verdict, the baseline, current and diff images downscaled to fit, then,
97
+ * when one of them had to be downscaled, the region list and native-scale
98
+ * crops of the changed regions (planCrops); a page that fits is already at
99
+ * native scale, and crops would only repeat its pixels.
100
+ * With `region`, or `y` and `height`: the verdict and only that crop. An
101
+ * image that is not on disk is left out; one that is not a PNG is said.
102
+ */
103
+ export declare function snapshotDiffContent(paths: ProjectPaths, snapshot: SnapshotResult, request?: DiffRequest): ToolContent[];
104
+ //# sourceMappingURL=diff-images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-images.d.ts","sourceRoot":"","sources":["../src/diff-images.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAA0D,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAE1J;;;;;;;;;GASG;AAEH;;;GAGG;AACH,eAAO,MAAM,WAAW;IACtB,wGAAwG;;IAExG,2DAA2D;;IAE3D,sFAAsF;;IAEtF,iFAAiF;;IAEjF,mFAAmF;;CAE3E,CAAC;AAKX,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,IAAI,EAAE,GAAG,CAAC;IACV,mGAAmG;IACnG,KAAK,EAAE,GAAG,CAAC;IACX,iGAAiG;IACjG,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,oDAAoD;IACpD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,6FAA6F;IAC7F,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAwDD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,UAAU,CAGhD;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CA0DjH;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,IAAI,CAiBxF;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA+ID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,GAAE,WAAgB,GAAG,WAAW,EAAE,CA2C3H"}
@@ -0,0 +1,399 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CROP_LIMITS = void 0;
37
+ exports.cropWindow = cropWindow;
38
+ exports.planCrops = planCrops;
39
+ exports.cropPng = cropPng;
40
+ exports.snapshotDiffContent = snapshotDiffContent;
41
+ const fs = __importStar(require("fs"));
42
+ const pngjs_1 = require("pngjs");
43
+ const lib_1 = require("./lib");
44
+ /**
45
+ * What get_diff returns for a snapshot: the full page downscaled to fit
46
+ * (it shows where the changes are), then the changed regions cropped at
47
+ * native scale and as wide as the page (up to 1280 px), since a tall
48
+ * full-page capture downscaled to 1024 px is too small to read (390×11844
49
+ * becomes 33×987). The regions are the ones in
50
+ * results.json, on the diff canvas: witness pads the two captures to max
51
+ * width × max height, anchored at the top left, so a region's box is the
52
+ * same place in the baseline, the current capture and the diff.
53
+ */
54
+ /**
55
+ * The bounds on get_diff's crops. Its description (tool-meta.ts), the
56
+ * README and docs/mcp.md quote these numbers, and tests hold them to it.
57
+ */
58
+ exports.CROP_LIMITS = {
59
+ /** Crops in a get_diff response without `region` or `y`; each is sent as baseline, current and diff. */
60
+ maxCrops: 3,
61
+ /** Pixels of context around each region, on every side. */
62
+ padding: 32,
63
+ /** The widest crop at native scale: a wider area is sampled at 1/2, 1/3 and so on. */
64
+ maxWidth: 1280,
65
+ /** The most rows a crop shows at its scale: a taller area shows its top rows. */
66
+ maxHeight: 1024,
67
+ /** Pixels the crops of one response hold together, in each of the three images. */
68
+ maxTotalPixels: 2000000,
69
+ };
70
+ /** More regions than this are counted, not listed, in the get_diff text. */
71
+ const MAX_LISTED_REGIONS = 20;
72
+ /** The overlap of two boxes, or null when they share no pixel. */
73
+ function intersect(a, b) {
74
+ const x = Math.max(a.x, b.x);
75
+ const y = Math.max(a.y, b.y);
76
+ const right = Math.min(a.x + a.width, b.x + b.width);
77
+ const bottom = Math.min(a.y + a.height, b.y + b.height);
78
+ return right > x && bottom > y ? { x, y, width: right - x, height: bottom - y } : null;
79
+ }
80
+ function union(a, b) {
81
+ const x = Math.min(a.x, b.x);
82
+ const y = Math.min(a.y, b.y);
83
+ return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
84
+ }
85
+ /** Whether two boxes overlap or touch. */
86
+ const meet = (a, b) => a.x <= b.x + b.width && b.x <= a.x + a.width && a.y <= b.y + b.height && b.y <= a.y + a.height;
87
+ /** A region's box inside the canvas; null for a region with no area there. */
88
+ function boxInside(region, canvas) {
89
+ if (![region.x, region.y, region.width, region.height].every(Number.isFinite))
90
+ return null;
91
+ return intersect(region, { x: 0, y: 0, width: canvas.width, height: canvas.height });
92
+ }
93
+ /** A box plus the padding on every side, inside the canvas. */
94
+ function padded(box, canvas) {
95
+ const p = exports.CROP_LIMITS.padding;
96
+ return intersect({ x: box.x - p, y: box.y - p, width: box.width + 2 * p, height: box.height + 2 * p }, { x: 0, y: 0, width: canvas.width, height: canvas.height });
97
+ }
98
+ /** A region's box plus the padding, inside the canvas; null for a region with no area there. */
99
+ function paddedArea(region, canvas) {
100
+ const inside = boxInside(region, canvas);
101
+ return inside ? padded(inside, canvas) : null;
102
+ }
103
+ /**
104
+ * The area as wide as the page, up to maxWidth, centred on it and inside the
105
+ * canvas: whole lines, so wrapping and clipping can be judged (a crop that
106
+ * stops at the region's columns cuts a changed heading mid-word). An area
107
+ * already wider keeps its width.
108
+ */
109
+ function widened(area, canvas) {
110
+ const width = Math.min(canvas.width, Math.max(area.width, exports.CROP_LIMITS.maxWidth));
111
+ if (width <= area.width)
112
+ return area;
113
+ const x = Math.min(Math.max(0, Math.round(area.x + area.width / 2 - width / 2)), canvas.width - width);
114
+ return { ...area, x, width };
115
+ }
116
+ const contains = (outer, inner) => inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.width <= outer.x + outer.width && inner.y + inner.height <= outer.y + outer.height;
117
+ const boxArea = (b) => b.width * b.height;
118
+ /**
119
+ * How to show `area`: at native scale when it is at most maxWidth wide,
120
+ * otherwise sampled at the smallest whole step that fits; and its top
121
+ * maxHeight rows (at that scale) when it is taller.
122
+ */
123
+ function cropWindow(area) {
124
+ const scale = Math.max(1, Math.ceil(area.width / exports.CROP_LIMITS.maxWidth));
125
+ return { area: { ...area }, shown: { ...area, height: Math.min(area.height, exports.CROP_LIMITS.maxHeight * scale) }, scale };
126
+ }
127
+ /** Pixels in each image of a crop. */
128
+ const cropPixels = (w) => Math.ceil(w.shown.width / w.scale) * Math.ceil(w.shown.height / w.scale);
129
+ /**
130
+ * Which regions to crop, and how. Each region gets its box plus
131
+ * CROP_LIMITS.padding px on every side, inside the canvas. Regions whose
132
+ * padded areas meet share one crop while the merged area still fits
133
+ * maxWidth × maxHeight. Each crop is then made as wide as the page, up to
134
+ * maxWidth (see widened), and a region that lies inside the box another
135
+ * crop shows joins that crop (a tall region's crop shows only its top rows,
136
+ * so the two cannot always merge). Changes come before shifts (a shift is
137
+ * content that only moved), then results.json order (top to bottom). At
138
+ * most maxCrops crops, holding at most maxTotalPixels together: a crop that
139
+ * would pass it is skipped, and a smaller one after it can still fit.
140
+ */
141
+ function planCrops(regions, canvas) {
142
+ const notShown = [];
143
+ let groups = [];
144
+ regions.forEach((r, i) => {
145
+ const box = boxInside(r, canvas);
146
+ if (box)
147
+ groups.push({ regions: [i], boxes: [box], area: padded(box, canvas), shift: r.classification === 'shift' });
148
+ else
149
+ notShown.push(i);
150
+ });
151
+ for (let merged = true; merged;) {
152
+ merged = false;
153
+ for (let i = 0; i < groups.length; i++) {
154
+ for (let j = i + 1; j < groups.length;) {
155
+ const a = groups[i];
156
+ const b = groups[j];
157
+ const area = union(a.area, b.area);
158
+ if (meet(a.area, b.area) && area.width <= exports.CROP_LIMITS.maxWidth && area.height <= exports.CROP_LIMITS.maxHeight) {
159
+ groups[i] = { regions: [...a.regions, ...b.regions], boxes: [...a.boxes, ...b.boxes], area, shift: a.shift && b.shift };
160
+ groups.splice(j, 1);
161
+ merged = true;
162
+ }
163
+ else {
164
+ j++;
165
+ }
166
+ }
167
+ }
168
+ }
169
+ // Smallest first, each group whose regions all lie inside a larger crop's
170
+ // shown box joins it; the host's box does not change.
171
+ const windows = new Map(groups.map((g) => [g, cropWindow(widened(g.area, canvas))]));
172
+ for (const g of [...groups].sort((a, b) => boxArea(windows.get(a).shown) - boxArea(windows.get(b).shown))) {
173
+ const shown = windows.get(g).shown;
174
+ const host = groups.find((h) => {
175
+ const hostShown = windows.get(h).shown;
176
+ return h !== g && boxArea(hostShown) >= boxArea(shown) && g.boxes.every((b) => contains(hostShown, b));
177
+ });
178
+ if (!host)
179
+ continue;
180
+ host.regions.push(...g.regions);
181
+ host.boxes.push(...g.boxes);
182
+ host.shift = host.shift && g.shift;
183
+ groups = groups.filter((x) => x !== g);
184
+ }
185
+ for (const g of groups)
186
+ g.regions.sort((m, n) => m - n);
187
+ groups.sort((a, b) => Number(a.shift) - Number(b.shift) || a.regions[0] - b.regions[0]);
188
+ const crops = [];
189
+ let used = 0;
190
+ for (const g of groups) {
191
+ const w = windows.get(g);
192
+ const pixels = cropPixels(w);
193
+ if (crops.length >= exports.CROP_LIMITS.maxCrops || used + pixels > exports.CROP_LIMITS.maxTotalPixels) {
194
+ notShown.push(...g.regions);
195
+ continue;
196
+ }
197
+ crops.push({ ...w, regions: g.regions });
198
+ used += pixels;
199
+ }
200
+ return { crops, notShown: notShown.sort((a, b) => a - b) };
201
+ }
202
+ /**
203
+ * Cut `box` out of an image, inside the image's own bounds (a capture
204
+ * smaller than the canvas has no pixels in the padded band), taking every
205
+ * `scale`th pixel. Null when the box is outside the image.
206
+ */
207
+ function cropPng(png, box, scale) {
208
+ const inside = intersect(box, { x: 0, y: 0, width: png.width, height: png.height });
209
+ if (!inside)
210
+ return null;
211
+ const width = Math.ceil(inside.width / scale);
212
+ const height = Math.ceil(inside.height / scale);
213
+ const out = new pngjs_1.PNG({ width, height });
214
+ for (let y = 0; y < height; y++) {
215
+ const row = ((inside.y + y * scale) * png.width + inside.x) * 4;
216
+ if (scale === 1) {
217
+ png.data.copy(out.data, y * width * 4, row, row + width * 4);
218
+ continue;
219
+ }
220
+ for (let x = 0; x < width; x++) {
221
+ png.data.copy(out.data, (y * width + x) * 4, row + x * scale * 4, row + x * scale * 4 + 4);
222
+ }
223
+ }
224
+ return { png: out, box: inside };
225
+ }
226
+ const OWNER = { baseline: 'the baseline', current: 'the current capture', diff: 'the diff image' };
227
+ const text = (t) => ({ type: 'text', text: t });
228
+ const ordinal = (n) => {
229
+ const tens = n % 100;
230
+ const suffix = tens >= 11 && tens <= 13 ? 'th' : { 1: 'st', 2: 'nd', 3: 'rd' }[n % 10] ?? 'th';
231
+ return `${n}${suffix}`;
232
+ };
233
+ const scaleText = (scale) => scale === 1 ? 'native scale' : `1/${scale} scale (every ${ordinal(scale)} pixel, as it is wider than ${exports.CROP_LIMITS.maxWidth} px)`;
234
+ const boxText = (b) => `x=${b.x} y=${b.y} ${b.width}×${b.height}`;
235
+ /** A region's smallest element, safe to quote in backticks. */
236
+ function selectorOf(region) {
237
+ const selector = region.elements?.[0]?.selector?.replace(/[`\r\n]/g, '').trim();
238
+ if (!selector)
239
+ return null;
240
+ return selector.length > 120 ? `${selector.slice(0, 119)}…` : selector;
241
+ }
242
+ function describeRegion(region) {
243
+ const selector = selectorOf(region);
244
+ return `${boxText(region)}${region.classification ? `, ${region.classification}` : ''}${selector ? `, \`${selector}\`` : ''}`;
245
+ }
246
+ /** The crop's box, what it adds around the region(s), its scale, and where to continue when it shows only the top rows. */
247
+ function describeWindow(w, what, canvas) {
248
+ const s = w.shown;
249
+ const full = w.area.width === canvas.width ? ', full width' : '';
250
+ const top = s.height < w.area.height ? `, the top ${s.height} of its ${w.area.height} rows (get_diff with y: ${s.y + s.height} shows the rows below)` : '';
251
+ return `${boxText(s)} (${what} plus ${exports.CROP_LIMITS.padding} px${full}) at ${scaleText(w.scale)}${top}`;
252
+ }
253
+ /** Each image cut to the window, labelled; an image the window misses, or only partly covers, says so. */
254
+ function croppedImages(images, w, tag) {
255
+ const content = [];
256
+ for (const image of images) {
257
+ const label = `${image.kind}${tag}`;
258
+ const size = `${image.png.width}×${image.png.height}`;
259
+ const cut = cropPng(image.png, w.shown, w.scale);
260
+ if (!cut) {
261
+ content.push(text(`${label}: none — this area is outside ${OWNER[image.kind]}'s ${size}.`));
262
+ continue;
263
+ }
264
+ const whole = cut.box.width === w.shown.width && cut.box.height === w.shown.height;
265
+ content.push(text(whole ? `${label}:` : `${label} (cut to ${OWNER[image.kind]}'s ${size}: ${boxText(cut.box)}):`));
266
+ content.push({ type: 'image', data: pngjs_1.PNG.sync.write(cut.png).toString('base64'), mimeType: 'image/png' });
267
+ }
268
+ return content;
269
+ }
270
+ /** The region list and the planned crops, after the full-page images. */
271
+ function regionCrops(regions, canvas, images) {
272
+ const plan = planCrops(regions, canvas);
273
+ const cropOf = new Map();
274
+ plan.crops.forEach((c, k) => {
275
+ for (const i of c.regions)
276
+ cropOf.set(i, k);
277
+ });
278
+ const lines = [`Regions (${regions.length}) on the ${canvas.width}×${canvas.height} diff canvas, in results.json order (get_diff with region: <n> crops one):`];
279
+ regions.slice(0, MAX_LISTED_REGIONS).forEach((r, i) => {
280
+ const k = cropOf.get(i);
281
+ const crop = k === undefined ? null : plan.crops[k];
282
+ const where = crop ? `crop ${k + 1}${r.y + r.height > crop.shown.y + crop.shown.height ? ' (its top rows)' : ''}` : 'not cropped';
283
+ lines.push(`- #${i} ${describeRegion(r)}: ${where}`);
284
+ });
285
+ if (regions.length > MAX_LISTED_REGIONS) {
286
+ lines.push(`- … and ${regions.length - MAX_LISTED_REGIONS} more (#${MAX_LISTED_REGIONS}–#${regions.length - 1}): get_report lists them, and region: <n> crops any of them`);
287
+ }
288
+ lines.push(plan.crops.length > 0
289
+ ? 'The crops below are at native scale (one image pixel per page pixel) unless they say otherwise: read text, wrapping and clipping there, not in the downscaled full-page images above. get_diff with y and height crops any other band of the page.'
290
+ : 'No region has an area inside the canvas, so there is nothing to crop. get_diff with y and height crops a band of the page.');
291
+ const content = [text(lines.join('\n'))];
292
+ plan.crops.forEach((c, k) => {
293
+ const members = c.regions.slice(0, 6).map((i) => {
294
+ const selector = selectorOf(regions[i]);
295
+ return `#${i}${selector ? ` \`${selector}\`` : ''}`;
296
+ });
297
+ const more = c.regions.length > 6 ? `, and ${c.regions.length - 6} more` : '';
298
+ const which = `${c.regions.length === 1 ? 'region' : 'regions'} ${members.join(', ')}${more}`;
299
+ content.push(text(`Crop ${k + 1} of ${plan.crops.length}: ${which} — ${describeWindow(c, c.regions.length === 1 ? 'the region' : 'the regions', canvas)}:`));
300
+ content.push(...croppedImages(images, c, `, crop ${k + 1}`));
301
+ });
302
+ return content;
303
+ }
304
+ /** One region, cropped on request. */
305
+ function requestedRegion(snapshot, index, canvas, images) {
306
+ const regions = snapshot.regions ?? [];
307
+ if (regions.length === 0) {
308
+ return [text(`${snapshot.name} has no regions in the latest results: get_diff with y and height crops a band of the page instead.`)];
309
+ }
310
+ const region = regions[index];
311
+ if (!region) {
312
+ const range = regions.length === 1 ? '1 region (#0)' : `${regions.length} regions (#0–#${regions.length - 1})`;
313
+ return [text(`${snapshot.name} has ${range}: there is no region #${index}.`)];
314
+ }
315
+ const area = paddedArea(region, canvas);
316
+ if (!area) {
317
+ return [text(`Region #${index} of ${snapshot.name} (${describeRegion(region)}) has no area inside its ${canvas.width}×${canvas.height} canvas.`)];
318
+ }
319
+ const w = cropWindow(widened(area, canvas));
320
+ return [
321
+ text(`Region #${index} of ${regions.length}: ${describeRegion(region)} — ${describeWindow(w, 'the region', canvas)}:`),
322
+ ...croppedImages(images, w, ''),
323
+ ];
324
+ }
325
+ /** A full-width band, cropped on request. */
326
+ function requestedBand(snapshot, request, canvas, images) {
327
+ const y = request.y ?? 0;
328
+ const size = `${canvas.width}×${canvas.height}`;
329
+ if (y >= canvas.height)
330
+ return [text(`y=${y} is below the bottom of ${snapshot.name}'s ${size} canvas.`)];
331
+ const scale = cropWindow({ x: 0, y, width: canvas.width, height: 1 }).scale;
332
+ const asked = request.height ?? exports.CROP_LIMITS.maxHeight * scale;
333
+ const area = { x: 0, y, width: canvas.width, height: Math.min(asked, canvas.height - y) };
334
+ const w = cropWindow(area);
335
+ const end = w.shown.y + w.shown.height;
336
+ const notes = [];
337
+ if (w.shown.height < area.height)
338
+ notes.push(`get_diff with y: ${end} shows the rows below`);
339
+ if (y + asked > canvas.height)
340
+ notes.push(`the canvas ends at y=${canvas.height}`);
341
+ const top = w.shown.height < area.height ? `, the top ${w.shown.height} of the ${asked} rows asked for` : '';
342
+ const header = `Band y=${y}–${end} of the ${size} canvas, full width, at ${scaleText(w.scale)}${top}${notes.length ? ` (${notes.join('; ')})` : ''}:`;
343
+ return [text(header), ...croppedImages(images, w, ', band')];
344
+ }
345
+ /**
346
+ * get_diff's content for a snapshot in the results. Without a request: the
347
+ * verdict, the baseline, current and diff images downscaled to fit, then,
348
+ * when one of them had to be downscaled, the region list and native-scale
349
+ * crops of the changed regions (planCrops); a page that fits is already at
350
+ * native scale, and crops would only repeat its pixels.
351
+ * With `region`, or `y` and `height`: the verdict and only that crop. An
352
+ * image that is not on disk is left out; one that is not a PNG is said.
353
+ */
354
+ function snapshotDiffContent(paths, snapshot, request = {}) {
355
+ const content = [text(`${snapshot.name}: ${(0, lib_1.verdictFor)(snapshot)}`)];
356
+ const wantsBand = request.y !== undefined || request.height !== undefined;
357
+ const onRequest = request.region !== undefined || wantsBand;
358
+ if (request.region !== undefined && wantsBand) {
359
+ content.push(text('Pass region, or y and height, not both.'));
360
+ return content;
361
+ }
362
+ const images = [];
363
+ for (const [kind, rel] of [
364
+ ['baseline', snapshot.baselinePath],
365
+ ['current', snapshot.currentPath],
366
+ ['diff', snapshot.diffPath],
367
+ ]) {
368
+ const abs = rel ? (0, lib_1.resolveImage)(paths, rel) : null;
369
+ if (!abs)
370
+ continue;
371
+ const raw = fs.readFileSync(abs);
372
+ let png;
373
+ try {
374
+ png = pngjs_1.PNG.sync.read(raw);
375
+ }
376
+ catch (err) {
377
+ content.push(text(`${kind}: ${rel} could not be read as a PNG (${err instanceof Error ? err.message : String(err)}).`));
378
+ continue;
379
+ }
380
+ images.push({ kind, png });
381
+ if (!onRequest)
382
+ content.push(...(0, lib_1.imageContent)(kind, raw, png));
383
+ }
384
+ // The regions are on the diff canvas: the larger of the two captures.
385
+ const canvas = images.length > 0 ? { width: Math.max(...images.map((i) => i.png.width)), height: Math.max(...images.map((i) => i.png.height)) } : null;
386
+ if (!onRequest) {
387
+ const downscaled = images.some((i) => Math.max(i.png.width, i.png.height) > lib_1.MAX_IMAGE_EDGE);
388
+ if (canvas && downscaled && snapshot.regions && snapshot.regions.length > 0)
389
+ content.push(...regionCrops(snapshot.regions, canvas, images));
390
+ return content;
391
+ }
392
+ if (!canvas) {
393
+ content.push(text(`No images on disk for ${snapshot.name}.`));
394
+ return content;
395
+ }
396
+ content.push(...(request.region !== undefined ? requestedRegion(snapshot, request.region, canvas, images) : requestedBand(snapshot, request, canvas, images)));
397
+ return content;
398
+ }
399
+ //# sourceMappingURL=diff-images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-images.js","sourceRoot":"","sources":["../src/diff-images.ts"],"names":[],"mappings":"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"}