difflens-cli 1.0.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 +171 -0
- package/dist/differ.d.ts +2 -0
- package/dist/differ.js +293 -0
- package/dist/differ.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +432 -0
- package/dist/index.js.map +1 -0
- package/dist/layout-analyzer.d.ts +4 -0
- package/dist/layout-analyzer.js +140 -0
- package/dist/layout-analyzer.js.map +1 -0
- package/dist/report-builder.d.ts +6 -0
- package/dist/report-builder.js +66 -0
- package/dist/report-builder.js.map +1 -0
- package/dist/screenshotter.d.ts +4 -0
- package/dist/screenshotter.js +142 -0
- package/dist/screenshotter.js.map +1 -0
- package/dist/storage.d.ts +11 -0
- package/dist/storage.js +142 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# DiffLens
|
|
2
|
+
|
|
3
|
+
MCP server that gives AI coding agents "eyes" for UI work. It screenshots localhost pages before and after code changes, compares them visually, and returns structured diff reports with overlay images.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g difflens
|
|
9
|
+
cd your-project
|
|
10
|
+
difflens setup
|
|
11
|
+
# Restart Claude Code — DiffLens is now active
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Then ask Claude: *"take a snapshot of http://localhost:3000"*
|
|
15
|
+
|
|
16
|
+
### Use without installing
|
|
17
|
+
|
|
18
|
+
Add to your project's `.mcp.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"difflens": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "difflens"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## How It Works
|
|
32
|
+
|
|
33
|
+
1. **Snapshot** a page to save a baseline screenshot
|
|
34
|
+
2. Make changes to your code
|
|
35
|
+
3. **Check** the page — DiffLens takes a new screenshot, runs a pixel-level diff against the baseline, clusters changed regions, and returns a report with an annotated overlay image
|
|
36
|
+
|
|
37
|
+
The overlay dims unchanged areas, highlights changed pixels in red, draws borders around detected regions, and includes a legend bar with the change percentage.
|
|
38
|
+
|
|
39
|
+
## Install from Source
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/byzkhan/difflens.git
|
|
43
|
+
cd difflens
|
|
44
|
+
npm install
|
|
45
|
+
npm run build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Tools
|
|
49
|
+
|
|
50
|
+
### snapshot
|
|
51
|
+
|
|
52
|
+
Take a screenshot and save it as a baseline.
|
|
53
|
+
|
|
54
|
+
| Parameter | Type | Default | Description |
|
|
55
|
+
|-----------|------|---------|-------------|
|
|
56
|
+
| `url` | string | required | URL to screenshot |
|
|
57
|
+
| `width` | number | 1280 | Viewport width |
|
|
58
|
+
| `height` | number | 720 | Viewport height |
|
|
59
|
+
| `fullPage` | boolean | true | Capture full scrollable page |
|
|
60
|
+
| `waitForSelector` | string | — | CSS selector to wait for before capture |
|
|
61
|
+
| `waitForTimeout` | number | — | Extra wait time in ms |
|
|
62
|
+
|
|
63
|
+
### check
|
|
64
|
+
|
|
65
|
+
Take a new screenshot and diff it against the latest baseline (or a specific snapshot). Returns a structured report and an overlay image.
|
|
66
|
+
|
|
67
|
+
| Parameter | Type | Default | Description |
|
|
68
|
+
|-----------|------|---------|-------------|
|
|
69
|
+
| `url` | string | required | URL to check |
|
|
70
|
+
| `baselineId` | string | latest | Specific snapshot ID to compare against |
|
|
71
|
+
| `width` | number | 1280 | Viewport width |
|
|
72
|
+
| `height` | number | 720 | Viewport height |
|
|
73
|
+
| `fullPage` | boolean | true | Capture full scrollable page |
|
|
74
|
+
| `waitForSelector` | string | — | CSS selector to wait for |
|
|
75
|
+
| `waitForTimeout` | number | — | Extra wait time in ms |
|
|
76
|
+
|
|
77
|
+
The report includes:
|
|
78
|
+
- **status**: `no_changes`, `minor` (<1%), `significant` (<10%), or `major` (>10%)
|
|
79
|
+
- **percentChanged**: exact pixel change percentage
|
|
80
|
+
- **regions**: list of changed areas with position descriptions and pixel counts
|
|
81
|
+
- **overlay image**: annotated composite showing exactly what changed
|
|
82
|
+
|
|
83
|
+
### check_responsive
|
|
84
|
+
|
|
85
|
+
Run visual diffs at multiple viewport widths to catch responsive design regressions.
|
|
86
|
+
|
|
87
|
+
| Parameter | Type | Default | Description |
|
|
88
|
+
|-----------|------|---------|-------------|
|
|
89
|
+
| `url` | string | required | URL to check |
|
|
90
|
+
| `widths` | number[] | [375, 768, 1024, 1440] | Viewport widths to test |
|
|
91
|
+
| `height` | number | 720 | Viewport height |
|
|
92
|
+
| `fullPage` | boolean | true | Capture full scrollable page |
|
|
93
|
+
| `waitForSelector` | string | — | CSS selector to wait for |
|
|
94
|
+
| `waitForTimeout` | number | — | Extra wait time in ms |
|
|
95
|
+
|
|
96
|
+
### snapshot_element
|
|
97
|
+
|
|
98
|
+
Screenshot a specific DOM element by CSS selector. Returns the image inline.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Default | Description |
|
|
101
|
+
|-----------|------|---------|-------------|
|
|
102
|
+
| `url` | string | required | URL containing the element |
|
|
103
|
+
| `selector` | string | required | CSS selector to capture |
|
|
104
|
+
| `width` | number | 1280 | Viewport width |
|
|
105
|
+
| `height` | number | 720 | Viewport height |
|
|
106
|
+
| `waitForSelector` | string | — | CSS selector to wait for |
|
|
107
|
+
| `waitForTimeout` | number | — | Extra wait time in ms |
|
|
108
|
+
|
|
109
|
+
### list_snapshots
|
|
110
|
+
|
|
111
|
+
List stored snapshots with optional filtering.
|
|
112
|
+
|
|
113
|
+
| Parameter | Type | Default | Description |
|
|
114
|
+
|-----------|------|---------|-------------|
|
|
115
|
+
| `url` | string | — | Filter by URL |
|
|
116
|
+
| `limit` | number | 20 | Max results |
|
|
117
|
+
|
|
118
|
+
### cleanup
|
|
119
|
+
|
|
120
|
+
Delete old snapshots by age or count.
|
|
121
|
+
|
|
122
|
+
| Parameter | Type | Default | Description |
|
|
123
|
+
|-----------|------|---------|-------------|
|
|
124
|
+
| `maxAgeHours` | number | — | Delete snapshots older than this |
|
|
125
|
+
| `maxCount` | number | — | Keep at most this many (newest first) |
|
|
126
|
+
| `dryRun` | boolean | false | Preview without deleting |
|
|
127
|
+
|
|
128
|
+
## Example Workflow
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
You: Take a snapshot of http://localhost:3000
|
|
132
|
+
Agent: [calls snapshot] ✓ Saved baseline snapshot abc123
|
|
133
|
+
|
|
134
|
+
... you edit some CSS ...
|
|
135
|
+
|
|
136
|
+
You: Check localhost:3000 for visual changes
|
|
137
|
+
Agent: [calls check] SIGNIFICANT: 4.2% pixels changed.
|
|
138
|
+
2 changed region(s) detected:
|
|
139
|
+
1. Top-left area (400x60px) — header color change
|
|
140
|
+
2. Middle-center area (600x200px) — card layout shift
|
|
141
|
+
[shows overlay image]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Storage
|
|
145
|
+
|
|
146
|
+
All data is stored locally in `.difflens/`:
|
|
147
|
+
- `.difflens/snapshots/` — PNG screenshots + JSON metadata
|
|
148
|
+
- `.difflens/diffs/` — diff images and overlay composites
|
|
149
|
+
|
|
150
|
+
Add `.difflens/` to your `.gitignore`.
|
|
151
|
+
|
|
152
|
+
## Implementation Details
|
|
153
|
+
|
|
154
|
+
- **Browser reuse**: Playwright Chromium launches once and stays alive. Each screenshot creates an isolated `BrowserContext` (~200ms vs ~3s for a new browser).
|
|
155
|
+
- **Deterministic captures**: Anti-animation CSS is injected before page JS runs. Animations, transitions, and caret blink are disabled. Timezone is locked to UTC, locale to en-US.
|
|
156
|
+
- **Region clustering**: Changed pixels are grouped into a 10x10px grid, then nearby cells are merged within 50px proximity. This turns raw pixel noise into meaningful "the header changed" regions.
|
|
157
|
+
- **Atomic writes**: Snapshots are written to `.tmp` files then renamed, preventing corruption from concurrent access.
|
|
158
|
+
- **stdio-safe**: All logging goes to stderr. stdout is reserved for MCP protocol messages.
|
|
159
|
+
|
|
160
|
+
## Tech Stack
|
|
161
|
+
|
|
162
|
+
- [Model Context Protocol SDK](https://github.com/modelcontextprotocol/typescript-sdk) — MCP server framework
|
|
163
|
+
- [Playwright](https://playwright.dev/) — headless browser automation
|
|
164
|
+
- [pixelmatch](https://github.com/mapbox/pixelmatch) — pixel-level image comparison
|
|
165
|
+
- [sharp](https://sharp.pixelplumbing.com/) — image compositing for overlays
|
|
166
|
+
- [pngjs](https://github.com/lukeapage/pngjs) — PNG encoding/decoding
|
|
167
|
+
- [Zod](https://zod.dev/) — input validation for tool schemas
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
package/dist/differ.d.ts
ADDED
package/dist/differ.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import pixelmatch from 'pixelmatch';
|
|
2
|
+
import { PNG } from 'pngjs';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { getDiffsDir } from './storage.js';
|
|
6
|
+
const log = (msg) => process.stderr.write(`[difflens] ${msg}\n`);
|
|
7
|
+
function decodePng(buffer) {
|
|
8
|
+
return PNG.sync.read(buffer);
|
|
9
|
+
}
|
|
10
|
+
function normalizeDimensions(beforePng, afterPng) {
|
|
11
|
+
const width = Math.max(beforePng.width, afterPng.width);
|
|
12
|
+
const height = Math.max(beforePng.height, afterPng.height);
|
|
13
|
+
const padImage = (png, targetW, targetH) => {
|
|
14
|
+
if (png.width === targetW && png.height === targetH)
|
|
15
|
+
return png;
|
|
16
|
+
const padded = new PNG({ width: targetW, height: targetH, fill: true });
|
|
17
|
+
// Fill with transparent pixels
|
|
18
|
+
padded.data.fill(0);
|
|
19
|
+
// Copy original pixels
|
|
20
|
+
for (let y = 0; y < png.height; y++) {
|
|
21
|
+
for (let x = 0; x < png.width; x++) {
|
|
22
|
+
const srcIdx = (y * png.width + x) * 4;
|
|
23
|
+
const dstIdx = (y * targetW + x) * 4;
|
|
24
|
+
padded.data[srcIdx] !== undefined && padded.data.copy(padded.data, dstIdx, srcIdx, srcIdx); // noop placeholder
|
|
25
|
+
// Direct pixel copy
|
|
26
|
+
padded.data[dstIdx] = png.data[srcIdx];
|
|
27
|
+
padded.data[dstIdx + 1] = png.data[srcIdx + 1];
|
|
28
|
+
padded.data[dstIdx + 2] = png.data[srcIdx + 2];
|
|
29
|
+
padded.data[dstIdx + 3] = png.data[srcIdx + 3];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return padded;
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
before: padImage(beforePng, width, height),
|
|
36
|
+
after: padImage(afterPng, width, height),
|
|
37
|
+
width,
|
|
38
|
+
height,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function clusterRegions(diffData, width, height) {
|
|
42
|
+
const CELL_SIZE = 10;
|
|
43
|
+
const MERGE_DISTANCE = 50;
|
|
44
|
+
const cols = Math.ceil(width / CELL_SIZE);
|
|
45
|
+
const rows = Math.ceil(height / CELL_SIZE);
|
|
46
|
+
// Count changed pixels per grid cell
|
|
47
|
+
// A pixel is "changed" if it's red in the diff output (R=255, G=0 or near 0)
|
|
48
|
+
const cellCounts = Array.from({ length: rows }, () => new Array(cols).fill(0));
|
|
49
|
+
for (let y = 0; y < height; y++) {
|
|
50
|
+
for (let x = 0; x < width; x++) {
|
|
51
|
+
const idx = (y * width + x) * 4;
|
|
52
|
+
const r = diffData[idx];
|
|
53
|
+
const g = diffData[idx + 1];
|
|
54
|
+
const b = diffData[idx + 2];
|
|
55
|
+
const a = diffData[idx + 3];
|
|
56
|
+
// pixelmatch marks diff pixels as red (255, 0, 0) with full alpha
|
|
57
|
+
if (r > 200 && g < 80 && b < 80 && a > 200) {
|
|
58
|
+
const col = Math.floor(x / CELL_SIZE);
|
|
59
|
+
const row = Math.floor(y / CELL_SIZE);
|
|
60
|
+
cellCounts[row][col]++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let boxes = [];
|
|
65
|
+
for (let row = 0; row < rows; row++) {
|
|
66
|
+
for (let col = 0; col < cols; col++) {
|
|
67
|
+
if (cellCounts[row][col] > 0) {
|
|
68
|
+
boxes.push({
|
|
69
|
+
x1: col * CELL_SIZE,
|
|
70
|
+
y1: row * CELL_SIZE,
|
|
71
|
+
x2: Math.min((col + 1) * CELL_SIZE, width),
|
|
72
|
+
y2: Math.min((row + 1) * CELL_SIZE, height),
|
|
73
|
+
changedPixels: cellCounts[row][col],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Iterative merge: merge boxes within proximity
|
|
79
|
+
let merged = true;
|
|
80
|
+
while (merged) {
|
|
81
|
+
merged = false;
|
|
82
|
+
const newBoxes = [];
|
|
83
|
+
const used = new Set();
|
|
84
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
85
|
+
if (used.has(i))
|
|
86
|
+
continue;
|
|
87
|
+
let current = { ...boxes[i] };
|
|
88
|
+
for (let j = i + 1; j < boxes.length; j++) {
|
|
89
|
+
if (used.has(j))
|
|
90
|
+
continue;
|
|
91
|
+
const other = boxes[j];
|
|
92
|
+
// Check proximity
|
|
93
|
+
const dx = Math.max(0, Math.max(current.x1, other.x1) - Math.min(current.x2, other.x2));
|
|
94
|
+
const dy = Math.max(0, Math.max(current.y1, other.y1) - Math.min(current.y2, other.y2));
|
|
95
|
+
if (dx <= MERGE_DISTANCE && dy <= MERGE_DISTANCE) {
|
|
96
|
+
current = {
|
|
97
|
+
x1: Math.min(current.x1, other.x1),
|
|
98
|
+
y1: Math.min(current.y1, other.y1),
|
|
99
|
+
x2: Math.max(current.x2, other.x2),
|
|
100
|
+
y2: Math.max(current.y2, other.y2),
|
|
101
|
+
changedPixels: current.changedPixels + other.changedPixels,
|
|
102
|
+
};
|
|
103
|
+
used.add(j);
|
|
104
|
+
merged = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
newBoxes.push(current);
|
|
108
|
+
}
|
|
109
|
+
boxes = newBoxes;
|
|
110
|
+
}
|
|
111
|
+
// Convert to DiffRegion[]
|
|
112
|
+
const totalPixels = width * height;
|
|
113
|
+
return boxes
|
|
114
|
+
.map((box) => {
|
|
115
|
+
const regionWidth = box.x2 - box.x1;
|
|
116
|
+
const regionHeight = box.y2 - box.y1;
|
|
117
|
+
const regionPixels = regionWidth * regionHeight;
|
|
118
|
+
return {
|
|
119
|
+
x: box.x1,
|
|
120
|
+
y: box.y1,
|
|
121
|
+
width: regionWidth,
|
|
122
|
+
height: regionHeight,
|
|
123
|
+
changedPixels: box.changedPixels,
|
|
124
|
+
percentChanged: (box.changedPixels / regionPixels) * 100,
|
|
125
|
+
description: describePosition(box.x1, box.y1, regionWidth, regionHeight, width, height),
|
|
126
|
+
};
|
|
127
|
+
})
|
|
128
|
+
.sort((a, b) => b.changedPixels - a.changedPixels);
|
|
129
|
+
}
|
|
130
|
+
function describePosition(x, y, w, h, imgW, imgH) {
|
|
131
|
+
const cx = x + w / 2;
|
|
132
|
+
const cy = y + h / 2;
|
|
133
|
+
const horizontal = cx < imgW / 3 ? 'Left' : cx > (imgW * 2) / 3 ? 'Right' : 'Center';
|
|
134
|
+
const vertical = cy < imgH / 3 ? 'Top' : cy > (imgH * 2) / 3 ? 'Bottom' : 'Middle';
|
|
135
|
+
return `${vertical}-${horizontal.toLowerCase()} area (${w}x${h}px)`;
|
|
136
|
+
}
|
|
137
|
+
async function createOverlay(afterBuffer, diffPng, regions, width, height, percentChanged) {
|
|
138
|
+
// Layer 1: Semi-transparent dark overlay for unchanged areas
|
|
139
|
+
const darkOverlay = Buffer.alloc(width * height * 4);
|
|
140
|
+
for (let i = 0; i < width * height; i++) {
|
|
141
|
+
const idx = i * 4;
|
|
142
|
+
const r = diffPng.data[idx];
|
|
143
|
+
const g = diffPng.data[idx + 1];
|
|
144
|
+
const b = diffPng.data[idx + 2];
|
|
145
|
+
const isChanged = r > 200 && g < 80 && b < 80;
|
|
146
|
+
if (!isChanged) {
|
|
147
|
+
// Darken unchanged areas
|
|
148
|
+
darkOverlay[idx] = 0;
|
|
149
|
+
darkOverlay[idx + 1] = 0;
|
|
150
|
+
darkOverlay[idx + 2] = 0;
|
|
151
|
+
darkOverlay[idx + 3] = 80; // semi-transparent black
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
darkOverlay[idx] = 0;
|
|
155
|
+
darkOverlay[idx + 1] = 0;
|
|
156
|
+
darkOverlay[idx + 2] = 0;
|
|
157
|
+
darkOverlay[idx + 3] = 0; // transparent over changed areas
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Layer 2: Red highlight on changed pixels
|
|
161
|
+
const redHighlight = Buffer.alloc(width * height * 4);
|
|
162
|
+
for (let i = 0; i < width * height; i++) {
|
|
163
|
+
const idx = i * 4;
|
|
164
|
+
const r = diffPng.data[idx];
|
|
165
|
+
const g = diffPng.data[idx + 1];
|
|
166
|
+
const b = diffPng.data[idx + 2];
|
|
167
|
+
const isChanged = r > 200 && g < 80 && b < 80;
|
|
168
|
+
if (isChanged) {
|
|
169
|
+
redHighlight[idx] = 255;
|
|
170
|
+
redHighlight[idx + 1] = 0;
|
|
171
|
+
redHighlight[idx + 2] = 0;
|
|
172
|
+
redHighlight[idx + 3] = 100; // semi-transparent red
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Layer 3: Region borders (2px red)
|
|
176
|
+
const borders = Buffer.alloc(width * height * 4);
|
|
177
|
+
const BORDER_WIDTH = 2;
|
|
178
|
+
for (const region of regions) {
|
|
179
|
+
for (let px = region.x; px < region.x + region.width; px++) {
|
|
180
|
+
for (let bw = 0; bw < BORDER_WIDTH; bw++) {
|
|
181
|
+
// Top border
|
|
182
|
+
const topY = region.y + bw;
|
|
183
|
+
if (topY < height && px < width) {
|
|
184
|
+
const idx = (topY * width + px) * 4;
|
|
185
|
+
borders[idx] = 255;
|
|
186
|
+
borders[idx + 1] = 50;
|
|
187
|
+
borders[idx + 2] = 50;
|
|
188
|
+
borders[idx + 3] = 220;
|
|
189
|
+
}
|
|
190
|
+
// Bottom border
|
|
191
|
+
const botY = region.y + region.height - 1 - bw;
|
|
192
|
+
if (botY >= 0 && botY < height && px < width) {
|
|
193
|
+
const idx = (botY * width + px) * 4;
|
|
194
|
+
borders[idx] = 255;
|
|
195
|
+
borders[idx + 1] = 50;
|
|
196
|
+
borders[idx + 2] = 50;
|
|
197
|
+
borders[idx + 3] = 220;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (let py = region.y; py < region.y + region.height; py++) {
|
|
202
|
+
for (let bw = 0; bw < BORDER_WIDTH; bw++) {
|
|
203
|
+
// Left border
|
|
204
|
+
const leftX = region.x + bw;
|
|
205
|
+
if (py < height && leftX < width) {
|
|
206
|
+
const idx = (py * width + leftX) * 4;
|
|
207
|
+
borders[idx] = 255;
|
|
208
|
+
borders[idx + 1] = 50;
|
|
209
|
+
borders[idx + 2] = 50;
|
|
210
|
+
borders[idx + 3] = 220;
|
|
211
|
+
}
|
|
212
|
+
// Right border
|
|
213
|
+
const rightX = region.x + region.width - 1 - bw;
|
|
214
|
+
if (py < height && rightX >= 0 && rightX < width) {
|
|
215
|
+
const idx = (py * width + rightX) * 4;
|
|
216
|
+
borders[idx] = 255;
|
|
217
|
+
borders[idx + 1] = 50;
|
|
218
|
+
borders[idx + 2] = 50;
|
|
219
|
+
borders[idx + 3] = 220;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Layer 4: Legend bar at bottom
|
|
225
|
+
const legendHeight = 40;
|
|
226
|
+
const totalHeight = height + legendHeight;
|
|
227
|
+
const statusText = percentChanged === 0 ? 'No changes' :
|
|
228
|
+
percentChanged < 1 ? 'Minor changes' :
|
|
229
|
+
percentChanged < 10 ? 'Significant changes' : 'Major changes';
|
|
230
|
+
const legendSvg = `<svg width="${width}" height="${legendHeight}">
|
|
231
|
+
<rect width="${width}" height="${legendHeight}" fill="#1a1a2e"/>
|
|
232
|
+
<rect x="10" y="10" width="20" height="20" fill="rgba(255,0,0,0.4)" stroke="#ff3232" stroke-width="2"/>
|
|
233
|
+
<text x="40" y="25" fill="white" font-family="Arial, sans-serif" font-size="14">
|
|
234
|
+
${statusText} — ${percentChanged.toFixed(2)}% pixels changed — ${regions.length} region(s) detected
|
|
235
|
+
</text>
|
|
236
|
+
</svg>`;
|
|
237
|
+
// Composite all layers
|
|
238
|
+
const overlay = await sharp(afterBuffer)
|
|
239
|
+
.resize(width, height, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
|
240
|
+
.ensureAlpha()
|
|
241
|
+
.composite([
|
|
242
|
+
{ input: await sharp(darkOverlay, { raw: { width, height, channels: 4 } }).png().toBuffer(), blend: 'over' },
|
|
243
|
+
{ input: await sharp(redHighlight, { raw: { width, height, channels: 4 } }).png().toBuffer(), blend: 'over' },
|
|
244
|
+
{ input: await sharp(borders, { raw: { width, height, channels: 4 } }).png().toBuffer(), blend: 'over' },
|
|
245
|
+
])
|
|
246
|
+
.png()
|
|
247
|
+
.toBuffer();
|
|
248
|
+
// Extend canvas and add legend
|
|
249
|
+
const final = await sharp(overlay)
|
|
250
|
+
.extend({ bottom: legendHeight, background: { r: 0, g: 0, b: 0, alpha: 255 } })
|
|
251
|
+
.composite([
|
|
252
|
+
{ input: Buffer.from(legendSvg), top: height, left: 0 },
|
|
253
|
+
])
|
|
254
|
+
.png()
|
|
255
|
+
.toBuffer();
|
|
256
|
+
return final;
|
|
257
|
+
}
|
|
258
|
+
export async function computeDiff(beforeBuffer, afterBuffer, diffId) {
|
|
259
|
+
log('Computing visual diff...');
|
|
260
|
+
const beforePng = decodePng(beforeBuffer);
|
|
261
|
+
const afterPng = decodePng(afterBuffer);
|
|
262
|
+
const beforeDims = { width: beforePng.width, height: beforePng.height };
|
|
263
|
+
const afterDims = { width: afterPng.width, height: afterPng.height };
|
|
264
|
+
const resized = beforeDims.width !== afterDims.width || beforeDims.height !== afterDims.height;
|
|
265
|
+
const { before, after, width, height } = normalizeDimensions(beforePng, afterPng);
|
|
266
|
+
// Run pixelmatch
|
|
267
|
+
const diffOutput = new PNG({ width, height });
|
|
268
|
+
const changedPixels = pixelmatch(before.data, after.data, diffOutput.data, width, height, { threshold: 0.1, includeAA: false, diffColor: [255, 0, 0] });
|
|
269
|
+
const totalPixels = width * height;
|
|
270
|
+
const percentChanged = (changedPixels / totalPixels) * 100;
|
|
271
|
+
log(`Diff: ${changedPixels}/${totalPixels} pixels changed (${percentChanged.toFixed(2)}%)`);
|
|
272
|
+
// Save raw diff image
|
|
273
|
+
const diffImagePath = join(getDiffsDir(), `${diffId}-diff.png`);
|
|
274
|
+
const diffBuffer = PNG.sync.write(diffOutput);
|
|
275
|
+
const { writeFile } = await import('node:fs/promises');
|
|
276
|
+
await writeFile(diffImagePath, diffBuffer);
|
|
277
|
+
// Cluster changed regions
|
|
278
|
+
const regions = clusterRegions(diffOutput.data, width, height);
|
|
279
|
+
// Create overlay
|
|
280
|
+
const overlayBuffer = await createOverlay(afterBuffer, diffOutput, regions, width, height, percentChanged);
|
|
281
|
+
const overlayImagePath = join(getDiffsDir(), `${diffId}-overlay.png`);
|
|
282
|
+
await writeFile(overlayImagePath, overlayBuffer);
|
|
283
|
+
return {
|
|
284
|
+
totalPixels,
|
|
285
|
+
changedPixels,
|
|
286
|
+
percentChanged,
|
|
287
|
+
dimensions: { before: beforeDims, after: afterDims, resized },
|
|
288
|
+
regions,
|
|
289
|
+
diffImagePath,
|
|
290
|
+
overlayImagePath,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=differ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.js","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;AAEzE,SAAS,SAAS,CAAC,MAAc;IAC/B,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,mBAAmB,CAC1B,SAAc,EACd,QAAa;IAEb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3D,MAAM,QAAQ,GAAG,CAAC,GAAQ,EAAE,OAAe,EAAE,OAAe,EAAO,EAAE;QACnE,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,GAAG,CAAC;QAEhE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,+BAA+B;QAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,uBAAuB;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,mBAAmB;gBAC/G,oBAAoB;gBACpB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC;QAC1C,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC;QACxC,KAAK;QACL,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,QAAoB,EACpB,KAAa,EACb,MAAc;IAEd,MAAM,SAAS,GAAG,EAAE,CAAC;IACrB,MAAM,cAAc,GAAG,EAAE,CAAC;IAE1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE3C,qCAAqC;IACrC,6EAA6E;IAC7E,MAAM,UAAU,GAAe,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC5B,kEAAkE;YAClE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;gBAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;gBACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;gBACtC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAOD,IAAI,KAAK,GAAU,EAAE,CAAC;IACtB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACpC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YACpC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,KAAK,CAAC,IAAI,CAAC;oBACT,EAAE,EAAE,GAAG,GAAG,SAAS;oBACnB,EAAE,EAAE,GAAG,GAAG,SAAS;oBACnB,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC;oBAC1C,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,MAAM,CAAC;oBAC3C,aAAa,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,OAAO,MAAM,EAAE,CAAC;QACd,MAAM,GAAG,KAAK,CAAC;QACf,MAAM,QAAQ,GAAU,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC1B,IAAI,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,SAAS;gBAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAEvB,kBAAkB;gBAClB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxF,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;gBAExF,IAAI,EAAE,IAAI,cAAc,IAAI,EAAE,IAAI,cAAc,EAAE,CAAC;oBACjD,OAAO,GAAG;wBACR,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;wBAClC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;wBAClC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;wBAClC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;wBAClC,aAAa,EAAE,OAAO,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa;qBAC3D,CAAC;oBACF,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBACZ,MAAM,GAAG,IAAI,CAAC;gBAChB,CAAC;YACH,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QAED,KAAK,GAAG,QAAQ,CAAC;IACnB,CAAC;IAED,0BAA0B;IAC1B,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;IACnC,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,GAAG,EAAc,EAAE;QACvB,MAAM,WAAW,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;QAChD,OAAO;YACL,CAAC,EAAE,GAAG,CAAC,EAAE;YACT,CAAC,EAAE,GAAG,CAAC,EAAE;YACT,KAAK,EAAE,WAAW;YAClB,MAAM,EAAE,YAAY;YACpB,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,cAAc,EAAE,CAAC,GAAG,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,GAAG;YACxD,WAAW,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,CAAC;SACxF,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,gBAAgB,CACvB,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAC1C,IAAY,EAAE,IAAY;IAE1B,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAErB,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IACrF,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IAEnF,OAAO,GAAG,QAAQ,IAAI,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;AACtE,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,WAAmB,EACnB,OAAY,EACZ,OAAqB,EACrB,KAAa,EACb,MAAc,EACd,cAAsB;IAEtB,6DAA6D;IAC7D,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;QAE9C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,yBAAyB;YACzB,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACzB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACzB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,yBAAyB;QACtD,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACzB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACzB,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,iCAAiC;QAC7D,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;QAE9C,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;YACxB,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC1B,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC1B,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,uBAAuB;QACtD,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,CAAC,CAAC;IACvB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC;YAC3D,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC;gBACzC,aAAa;gBACb,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;gBAC3B,IAAI,IAAI,GAAG,MAAM,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;oBAChC,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;oBACpC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;gBAC3F,CAAC;gBACD,gBAAgB;gBAChB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;gBAC/C,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,MAAM,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;oBAC7C,MAAM,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;oBACpC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;gBAC3F,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;YAC5D,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC;gBACzC,cAAc;gBACd,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;gBAC5B,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;oBACjC,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;oBACrC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;gBAC3F,CAAC;gBACD,eAAe;gBACf,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC;gBAChD,IAAI,EAAE,GAAG,MAAM,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;oBACjD,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;oBACtC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;gBAC3F,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,YAAY,GAAG,EAAE,CAAC;IACxB,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,CAAC;IAC1C,MAAM,UAAU,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QACtD,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;YACtC,cAAc,GAAG,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,eAAe,CAAC;IAChE,MAAM,SAAS,GAAG,eAAe,KAAK,aAAa,YAAY;mBAC9C,KAAK,aAAa,YAAY;;;QAGzC,UAAU,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,OAAO,CAAC,MAAM;;SAE5E,CAAC;IAER,uBAAuB;IACvB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC;SACrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;SACrF,WAAW,EAAE;SACb,SAAS,CAAC;QACT,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QAC5G,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QAC7G,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;KACzG,CAAC;SACD,GAAG,EAAE;SACL,QAAQ,EAAE,CAAC;IAEd,+BAA+B;IAC/B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC;SAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;SAC9E,SAAS,CAAC;QACT,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE;KACxD,CAAC;SACD,GAAG,EAAE;SACL,QAAQ,EAAE,CAAC;IAEd,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,YAAoB,EACpB,WAAmB,EACnB,MAAc;IAEd,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAEhC,MAAM,SAAS,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IAExC,MAAM,UAAU,GAAa,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IAClF,MAAM,SAAS,GAAa,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC/E,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,CAAC;IAE/F,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAElF,iBAAiB;IACjB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,UAAU,CAC9B,MAAM,CAAC,IAAI,EACX,KAAK,CAAC,IAAI,EACV,UAAU,CAAC,IAAI,EACf,KAAK,EACL,MAAM,EACN,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAC7D,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;IACnC,MAAM,cAAc,GAAG,CAAC,aAAa,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC;IAE3D,GAAG,CAAC,SAAS,aAAa,IAAI,WAAW,oBAAoB,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE5F,sBAAsB;IACtB,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACvD,MAAM,SAAS,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAE3C,0BAA0B;IAC1B,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAE/D,iBAAiB;IACjB,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3G,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,MAAM,cAAc,CAAC,CAAC;IACtE,MAAM,SAAS,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAEjD,OAAO;QACL,WAAW;QACX,aAAa;QACb,cAAc;QACd,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE;QAC7D,OAAO;QACP,aAAa;QACb,gBAAgB;KACjB,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED