@testivai/mcp 0.8.3 → 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 +6 -5
- package/dist/diff-images.d.ts +104 -0
- package/dist/diff-images.d.ts.map +1 -0
- package/dist/diff-images.js +399 -0
- package/dist/diff-images.js.map +1 -0
- package/dist/lib.d.ts +56 -6
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +118 -15
- package/dist/lib.js.map +1 -1
- package/dist/server.js +34 -50
- package/dist/server.js.map +1 -1
- package/dist/tool-meta.d.ts.map +1 -1
- package/dist/tool-meta.js +17 -3
- package/dist/tool-meta.js.map +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
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
|
|
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
|
|
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
|
|
83
|
-
(or fixes its own
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2HA,gCAGC;AAiBD,8BA0DC;AAOD,0BAiBC;AAkKD,kDA2CC;AA9aD,uCAAyB;AACzB,iCAA4B;AAC5B,+BAA0J;AAE1J;;;;;;;;;GASG;AAEH;;;GAGG;AACU,QAAA,WAAW,GAAG;IACzB,wGAAwG;IACxG,QAAQ,EAAE,CAAC;IACX,2DAA2D;IAC3D,OAAO,EAAE,EAAE;IACX,sFAAsF;IACtF,QAAQ,EAAE,IAAI;IACd,iFAAiF;IACjF,SAAS,EAAE,IAAI;IACf,mFAAmF;IACnF,cAAc,EAAE,OAAS;CACjB,CAAC;AAEX,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,EAAE,CAAC;AA+B9B,kEAAkE;AAClE,SAAS,SAAS,CAAC,CAAM,EAAE,CAAM;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACzF,CAAC;AAED,SAAS,KAAK,CAAC,CAAM,EAAE,CAAM;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AAC3H,CAAC;AAED,0CAA0C;AAC1C,MAAM,IAAI,GAAG,CAAC,CAAM,EAAE,CAAM,EAAW,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;AAEzI,8EAA8E;AAC9E,SAAS,SAAS,CAAC,MAAW,EAAE,MAAyC;IACvE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3F,OAAO,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,+DAA+D;AAC/D,SAAS,MAAM,CAAC,GAAQ,EAAE,MAAyC;IACjE,MAAM,CAAC,GAAG,mBAAW,CAAC,OAAO,CAAC;IAC9B,OAAO,SAAS,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAQ,CAAC;AAC5K,CAAC;AAED,gGAAgG;AAChG,SAAS,UAAU,CAAC,MAAW,EAAE,MAAyC;IACxE,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,IAAS,EAAE,MAAyC;IACnE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,mBAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;IACvG,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,KAAU,EAAE,KAAU,EAAW,EAAE,CACnD,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;AAEjJ,MAAM,OAAO,GAAG,CAAC,CAAM,EAAU,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;AAEvD;;;;GAIG;AACH,SAAgB,UAAU,CAAC,IAAS;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,mBAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAW,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;AACxH,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,GAAG,CAAC,CAAa,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAEvH;;;;;;;;;;;GAWG;AACH,SAAgB,SAAS,CAAC,OAAkC,EAAE,MAAyC;IACrG,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,MAAM,GAA0E,EAAE,CAAC;IACvF,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACjC,IAAI,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,cAAc,KAAK,OAAO,EAAE,CAAC,CAAC;;YAChH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,KAAK,IAAI,MAAM,GAAG,IAAI,EAAE,MAAM,GAAI,CAAC;QACjC,MAAM,GAAG,KAAK,CAAC;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAI,CAAC;gBACxC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;gBACnC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,mBAAW,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,mBAAW,CAAC,SAAS,EAAE,CAAC;oBACvG,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;oBACxH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;oBACpB,MAAM,GAAG,IAAI,CAAC;gBAChB,CAAC;qBAAM,CAAC;oBACN,CAAC,EAAE,CAAC;gBACN,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAgB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC1I,MAAM,KAAK,GAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAgB,CAAC,KAAK,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7B,MAAM,SAAS,GAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAgB,CAAC,KAAK,CAAC;YACvD,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;QACzG,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;QACnC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAExD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAe,CAAC;QACvC,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,IAAI,mBAAW,CAAC,QAAQ,IAAI,IAAI,GAAG,MAAM,GAAG,mBAAW,CAAC,cAAc,EAAE,CAAC;YACvF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,IAAI,IAAI,MAAM,CAAC;IACjB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,SAAgB,OAAO,CAAC,GAAQ,EAAE,GAAQ,EAAE,KAAa;IACvD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACpF,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,WAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACnC,CAAC;AAcD,MAAM,KAAK,GAA8B,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;AAO9H,MAAM,IAAI,GAAG,CAAC,CAAS,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;AAErE,MAAM,OAAO,GAAG,CAAC,CAAS,EAAU,EAAE;IACpC,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC;IACrB,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAA6B,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC;IAC3H,OAAO,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC;AACzB,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAC1C,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,KAAK,iBAAiB,OAAO,CAAC,KAAK,CAAC,+BAA+B,mBAAW,CAAC,QAAQ,MAAM,CAAC;AAEpI,MAAM,OAAO,GAAG,CAAC,CAAM,EAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;AAE/E,+DAA+D;AAC/D,SAAS,UAAU,CAAC,MAAkB;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChF,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;AACzE,CAAC;AAED,SAAS,cAAc,CAAC,MAAkB;IACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAChI,CAAC;AAED,2HAA2H;AAC3H,SAAS,cAAc,CAAC,CAAa,EAAE,IAAkC,EAAE,MAAyB;IAClG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAClB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,GAAG,GACP,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,wBAAwB,CAAC,CAAC,CAAC,EAAE,CAAC;IACjJ,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,SAAS,mBAAW,CAAC,OAAO,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;AACxG,CAAC;AAED,0GAA0G;AAC1G,SAAS,aAAa,CAAC,MAAqB,EAAE,CAAa,EAAE,GAAW;IACtE,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACtD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,iCAAiC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC;YAC5F,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACnH,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,WAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;IAC3G,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,yEAAyE;AACzE,SAAS,WAAW,CAAC,OAAkC,EAAE,MAAyC,EAAE,MAAqB;IACvH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC1B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,CAAC,YAAY,OAAO,CAAC,MAAM,YAAY,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,4EAA4E,CAAC,CAAC;IAChK,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,QAAS,CAAY,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9I,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CACR,WAAW,OAAO,CAAC,MAAM,GAAG,kBAAkB,WAAW,kBAAkB,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC,6DAA6D,CAChK,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACnB,CAAC,CAAC,oPAAoP;QACtP,CAAC,CAAC,4HAA4H,CACjI,CAAC;IAEF,MAAM,OAAO,GAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,OAAO,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACtD,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;QAC9F,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7J,OAAO,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,sCAAsC;AACtC,SAAS,eAAe,CAAC,QAAwB,EAAE,KAAa,EAAE,MAAyC,EAAE,MAAqB;IAChI,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;IACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,qGAAqG,CAAC,CAAC,CAAC;IACvI,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,iBAAiB,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC;QAC/G,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,QAAQ,KAAK,yBAAyB,KAAK,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,IAAI,CAAC,WAAW,KAAK,OAAO,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,MAAM,CAAC,4BAA4B,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC;IACpJ,CAAC;IACD,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,OAAO;QACL,IAAI,CAAC,WAAW,KAAK,OAAO,OAAO,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,CAAC,MAAM,cAAc,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC;QACtH,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,SAAS,aAAa,CAAC,QAAwB,EAAE,OAAoB,EAAE,MAAyC,EAAE,MAAqB;IACrI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;IAChD,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,2BAA2B,QAAQ,CAAC,IAAI,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC;IAC1G,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,IAAI,mBAAW,CAAC,SAAS,GAAG,KAAK,CAAC;IAC9D,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;IAC1F,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,GAAG,uBAAuB,CAAC,CAAC;IAC7F,IAAI,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACnF,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,KAAK,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7G,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,GAAG,WAAW,IAAI,2BAA2B,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IACtJ,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CAAC,KAAmB,EAAE,QAAwB,EAAE,UAAuB,EAAE;IAC1G,MAAM,OAAO,GAAkB,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,KAAK,IAAA,gBAAU,EAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,EAAE,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI;QACxB,CAAC,UAAU,EAAE,QAAQ,CAAC,YAAY,CAAC;QACnC,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC;QACjC,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;KACnB,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,IAAA,kBAAY,EAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClD,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,GAAQ,CAAC;QACb,IAAI,CAAC;YACH,GAAG,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,GAAG,gCAAgC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YACxH,SAAS;QACX,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3B,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAA,kBAAY,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,sEAAsE;IACtE,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1I,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,oBAAc,CAAC,CAAC;QAC5F,IAAI,MAAM,IAAI,UAAU,IAAI,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC5I,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/J,OAAO,OAAO,CAAC;AACjB,CAAC"}
|