@vizejs/vite-plugin-musea 0.0.1-alpha.11 → 0.0.1-alpha.112
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/dist/a11y-Bvx5TJb8.d.ts +61 -0
- package/dist/a11y-Bvx5TJb8.d.ts.map +1 -0
- package/dist/a11y-C6xqILwZ.js +305 -0
- package/dist/a11y-C6xqILwZ.js.map +1 -0
- package/dist/a11y.d.ts +3 -0
- package/dist/a11y.js +3 -0
- package/dist/autogen-D3Zjc3zI.d.ts +64 -0
- package/dist/autogen-D3Zjc3zI.d.ts.map +1 -0
- package/dist/autogen-ymQnARZK.js +193 -0
- package/dist/autogen-ymQnARZK.js.map +1 -0
- package/dist/autogen.d.ts +2 -0
- package/dist/autogen.js +3 -0
- package/dist/cli.js +207 -30
- package/dist/cli.js.map +1 -1
- package/dist/gallery/assets/abap-Cry0R76c.js +1 -0
- package/dist/gallery/assets/apex-GS4zZi0I.js +1 -0
- package/dist/gallery/assets/azcli-DMImymmY.js +1 -0
- package/dist/gallery/assets/bat-D6epFECU.js +1 -0
- package/dist/gallery/assets/bicep-7klDZ283.js +2 -0
- package/dist/gallery/assets/cameligo-PvLD8t4t.js +1 -0
- package/dist/gallery/assets/clojure-BTbSGpb3.js +1 -0
- package/dist/gallery/assets/codicon-DCmgc-ay.ttf +0 -0
- package/dist/gallery/assets/coffee-Bhl_9YuJ.js +1 -0
- package/dist/gallery/assets/cpp-CM5j04eT.js +1 -0
- package/dist/gallery/assets/csharp-Dh0Ee7SY.js +1 -0
- package/dist/gallery/assets/csp-CLRC61y6.js +1 -0
- package/dist/gallery/assets/css-B0t_muXd.js +3 -0
- package/dist/gallery/assets/css.worker-Cbw1kvi8.js +88 -0
- package/dist/gallery/assets/cssMode-DBTfE7c_.js +4 -0
- package/dist/gallery/assets/cypher-C5e5inIh.js +1 -0
- package/dist/gallery/assets/dart-DIK3l8YT.js +1 -0
- package/dist/gallery/assets/dockerfile-D7OAO0hl.js +1 -0
- package/dist/gallery/assets/ecl-CP7nM2KN.js +1 -0
- package/dist/gallery/assets/editor-B55U_qvj.css +1 -0
- package/dist/gallery/assets/editor-F8AxQWwE.css +1 -0
- package/dist/gallery/assets/editor.api-DOELtZec.js +644 -0
- package/dist/gallery/assets/editor.main-BWk0dmtj.js +63 -0
- package/dist/gallery/assets/editor.worker-Cs7HTPcl.js +12 -0
- package/dist/gallery/assets/elixir-DNRIIj6-.js +1 -0
- package/dist/gallery/assets/flow9-BC5Cr9X0.js +1 -0
- package/dist/gallery/assets/freemarker2-DaSd0g6o.js +3 -0
- package/dist/gallery/assets/fsharp-52P4yqMh.js +1 -0
- package/dist/gallery/assets/go-yKE3zUfB.js +1 -0
- package/dist/gallery/assets/graphql-D3sNVCLc.js +1 -0
- package/dist/gallery/assets/handlebars-B-cbMVnP.js +1 -0
- package/dist/gallery/assets/hcl-BB7aW7AX.js +1 -0
- package/dist/gallery/assets/html-BbDsaPQg.js +1 -0
- package/dist/gallery/assets/html.worker-CYmk49z4.js +495 -0
- package/dist/gallery/assets/htmlMode-C2OjYqY0.js +4 -0
- package/dist/gallery/assets/index-BIf-7q-k.css +1 -0
- package/dist/gallery/assets/index-DotH6Szk.js +63 -0
- package/dist/gallery/assets/ini-BdRufzJj.js +1 -0
- package/dist/gallery/assets/java-CeUu-z7Y.js +1 -0
- package/dist/gallery/assets/javascript-CogJNBNZ.js +1 -0
- package/dist/gallery/assets/json.worker-CWR6J9Qf.js +51 -0
- package/dist/gallery/assets/jsonMode-ssar7rkB.js +10 -0
- package/dist/gallery/assets/julia-CXu-Fn93.js +1 -0
- package/dist/gallery/assets/kotlin-TwsjxLJ3.js +1 -0
- package/dist/gallery/assets/less-CviwWNG4.js +2 -0
- package/dist/gallery/assets/lexon-BTOivnjP.js +1 -0
- package/dist/gallery/assets/liquid-D9uJH3tm.js +1 -0
- package/dist/gallery/assets/lua-6W3WJOvj.js +1 -0
- package/dist/gallery/assets/m3-tlthQ8Fo.js +1 -0
- package/dist/gallery/assets/markdown-CPR4Kr9O.js +1 -0
- package/dist/gallery/assets/mdx-D1ruboYf.js +1 -0
- package/dist/gallery/assets/mips-BfxZbsD8.js +1 -0
- package/dist/gallery/assets/monaco.contribution-w8cYIjzF.js +2 -0
- package/dist/gallery/assets/msdax-eKsr2VtO.js +1 -0
- package/dist/gallery/assets/mysql-D6-LO0bt.js +1 -0
- package/dist/gallery/assets/objective-c-DYtfYpNc.js +1 -0
- package/dist/gallery/assets/pascal-CPGyHbal.js +1 -0
- package/dist/gallery/assets/pascaligo-Dsp_VKxo.js +1 -0
- package/dist/gallery/assets/perl-CUVa2_Cu.js +1 -0
- package/dist/gallery/assets/pgsql-C2nbbU56.js +1 -0
- package/dist/gallery/assets/php-DxX2tlkL.js +1 -0
- package/dist/gallery/assets/pla-D55LHImG.js +1 -0
- package/dist/gallery/assets/postiats-Dw_nWtoT.js +1 -0
- package/dist/gallery/assets/powerquery-BldVOeNZ.js +1 -0
- package/dist/gallery/assets/powershell-fdqyoMut.js +1 -0
- package/dist/gallery/assets/protobuf-C-2cnAYL.js +2 -0
- package/dist/gallery/assets/pug-bDrVOc6m.js +1 -0
- package/dist/gallery/assets/python-Bk5JPOE3.js +1 -0
- package/dist/gallery/assets/qsharp-B83Ol6AR.js +1 -0
- package/dist/gallery/assets/r-DAxg6zn-.js +1 -0
- package/dist/gallery/assets/razor-BbS20zwD.js +1 -0
- package/dist/gallery/assets/redis-BSRYxJDu.js +1 -0
- package/dist/gallery/assets/redshift-BrtVU4Ki.js +1 -0
- package/dist/gallery/assets/restructuredtext-DdU6AjLQ.js +1 -0
- package/dist/gallery/assets/ruby-C-s7ovR-.js +1 -0
- package/dist/gallery/assets/rust-CmSb_pkG.js +1 -0
- package/dist/gallery/assets/sb-Bfo5Ukmr.js +1 -0
- package/dist/gallery/assets/scala-Cx2bkddK.js +1 -0
- package/dist/gallery/assets/scheme-DkT6GPaV.js +1 -0
- package/dist/gallery/assets/scss-DOPngiM2.js +3 -0
- package/dist/gallery/assets/shell-NYt6Xulf.js +1 -0
- package/dist/gallery/assets/solidity-CUiq_T3F.js +1 -0
- package/dist/gallery/assets/sophia-B4sI8Ij-.js +1 -0
- package/dist/gallery/assets/sparql-BNfhekQe.js +1 -0
- package/dist/gallery/assets/sql-Bzn3OZV3.js +1 -0
- package/dist/gallery/assets/st-CV0zI_0P.js +1 -0
- package/dist/gallery/assets/swift-CR3-zK7D.js +1 -0
- package/dist/gallery/assets/systemverilog-BxgPwTIi.js +1 -0
- package/dist/gallery/assets/tcl-BSnnsp36.js +1 -0
- package/dist/gallery/assets/ts.worker-B_5n269U.js +51339 -0
- package/dist/gallery/assets/tsMode-DWuHbc8i.js +11 -0
- package/dist/gallery/assets/twig-Bx06vatZ.js +1 -0
- package/dist/gallery/assets/typescript-FIdb5Wzk.js +1 -0
- package/dist/gallery/assets/typespec-COSap3s7.js +1 -0
- package/dist/gallery/assets/vb-DU0VXhXP.js +1 -0
- package/dist/gallery/assets/wgsl-BegdTer-.js +298 -0
- package/dist/gallery/assets/xml-BDyGXNxX.js +1 -0
- package/dist/gallery/assets/yaml-D3AYCCFt.js +1 -0
- package/dist/gallery/index.html +16 -0
- package/dist/index.d.ts +51 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1532 -65
- package/dist/index.js.map +1 -1
- package/dist/{vrt-DRwtnkE5.js → vrt-DP87vGIA.js} +222 -289
- package/dist/vrt-DP87vGIA.js.map +1 -0
- package/dist/vrt-Vb4aqPZE.d.ts +461 -0
- package/dist/vrt-Vb4aqPZE.d.ts.map +1 -0
- package/dist/vrt.d.ts +2 -2
- package/dist/vrt.js +1 -1
- package/package.json +28 -5
- package/dist/vrt-BfuTRv-J.d.ts +0 -217
- package/dist/vrt-BfuTRv-J.d.ts.map +0 -1
- package/dist/vrt-DRwtnkE5.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
3
|
import { PNG } from "pngjs";
|
|
4
4
|
|
|
5
5
|
//#region src/vrt.ts
|
|
@@ -8,6 +8,9 @@ import { PNG } from "pngjs";
|
|
|
8
8
|
*/
|
|
9
9
|
var MuseaVrtRunner = class {
|
|
10
10
|
options;
|
|
11
|
+
capture;
|
|
12
|
+
comparison;
|
|
13
|
+
ci;
|
|
11
14
|
browser = null;
|
|
12
15
|
startTime = 0;
|
|
13
16
|
constructor(options = {}) {
|
|
@@ -24,6 +27,16 @@ var MuseaVrtRunner = class {
|
|
|
24
27
|
name: "mobile"
|
|
25
28
|
}]
|
|
26
29
|
};
|
|
30
|
+
this.capture = {
|
|
31
|
+
fullPage: options.capture?.fullPage ?? false,
|
|
32
|
+
waitForNetwork: options.capture?.waitForNetwork ?? true,
|
|
33
|
+
settleTime: options.capture?.settleTime ?? 100,
|
|
34
|
+
waitSelector: options.capture?.waitSelector ?? ".musea-variant",
|
|
35
|
+
hideElements: options.capture?.hideElements ?? [],
|
|
36
|
+
maskElements: options.capture?.maskElements ?? []
|
|
37
|
+
};
|
|
38
|
+
this.comparison = options.comparison ?? {};
|
|
39
|
+
this.ci = options.ci ?? {};
|
|
27
40
|
}
|
|
28
41
|
/**
|
|
29
42
|
* Initialize Playwright browser.
|
|
@@ -48,11 +61,20 @@ var MuseaVrtRunner = class {
|
|
|
48
61
|
async runAllTests(artFiles, baseUrl) {
|
|
49
62
|
if (!this.browser) throw new Error("VRT runner not initialized. Call init() first.");
|
|
50
63
|
const results = [];
|
|
64
|
+
const retries = this.ci.retries ?? 0;
|
|
51
65
|
for (const art of artFiles) for (const variant of art.variants) {
|
|
52
66
|
if (variant.skipVrt) continue;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
const viewports = variant.args?.viewport ? [variant.args.viewport] : this.options.viewports;
|
|
68
|
+
for (const viewport of viewports) {
|
|
69
|
+
let result = null;
|
|
70
|
+
let attempts = 0;
|
|
71
|
+
while (attempts <= retries) {
|
|
72
|
+
result = await this.captureAndCompare(art, variant.name, viewport, baseUrl);
|
|
73
|
+
if (result.passed || result.isNew || !result.error) break;
|
|
74
|
+
attempts++;
|
|
75
|
+
if (attempts <= retries) console.log(`[vrt] Retry ${attempts}/${retries}: ${path.basename(art.path)}/${variant.name}`);
|
|
76
|
+
}
|
|
77
|
+
if (result) results.push(result);
|
|
56
78
|
}
|
|
57
79
|
}
|
|
58
80
|
return results;
|
|
@@ -84,12 +106,26 @@ var MuseaVrtRunner = class {
|
|
|
84
106
|
});
|
|
85
107
|
page = await context.newPage();
|
|
86
108
|
const variantUrl = this.buildVariantUrl(baseUrl, art.path, variantName);
|
|
87
|
-
|
|
88
|
-
await page.
|
|
89
|
-
await page.
|
|
109
|
+
const waitUntil = this.capture.waitForNetwork ? "networkidle" : "load";
|
|
110
|
+
await page.goto(variantUrl, { waitUntil });
|
|
111
|
+
await page.waitForSelector(this.capture.waitSelector, { timeout: 1e4 });
|
|
112
|
+
await page.waitForTimeout(this.capture.settleTime);
|
|
113
|
+
if (this.capture.hideElements.length > 0) for (const selector of this.capture.hideElements) await page.evaluate((sel) => {
|
|
114
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
115
|
+
el.style.visibility = "hidden";
|
|
116
|
+
});
|
|
117
|
+
}, selector);
|
|
118
|
+
if (this.capture.maskElements.length > 0) for (const selector of this.capture.maskElements) await page.evaluate((sel) => {
|
|
119
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
120
|
+
const htmlEl = el;
|
|
121
|
+
htmlEl.style.background = "#ff00ff";
|
|
122
|
+
htmlEl.style.color = "transparent";
|
|
123
|
+
htmlEl.innerHTML = "";
|
|
124
|
+
});
|
|
125
|
+
}, selector);
|
|
90
126
|
await page.screenshot({
|
|
91
127
|
path: currentPath,
|
|
92
|
-
fullPage:
|
|
128
|
+
fullPage: this.capture.fullPage
|
|
93
129
|
});
|
|
94
130
|
const hasBaseline = await fileExists(snapshotPath);
|
|
95
131
|
if (!hasBaseline) {
|
|
@@ -133,6 +169,24 @@ var MuseaVrtRunner = class {
|
|
|
133
169
|
}
|
|
134
170
|
}
|
|
135
171
|
/**
|
|
172
|
+
* Get the Playwright Page for external use (e.g., a11y auditing).
|
|
173
|
+
*/
|
|
174
|
+
async createPage(viewport) {
|
|
175
|
+
if (!this.browser) throw new Error("VRT runner not initialized. Call init() first.");
|
|
176
|
+
const context = await this.browser.newContext({
|
|
177
|
+
viewport: {
|
|
178
|
+
width: viewport.width,
|
|
179
|
+
height: viewport.height
|
|
180
|
+
},
|
|
181
|
+
deviceScaleFactor: viewport.deviceScaleFactor ?? 1
|
|
182
|
+
});
|
|
183
|
+
const page = await context.newPage();
|
|
184
|
+
return {
|
|
185
|
+
page,
|
|
186
|
+
context
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
136
190
|
* Update baseline snapshots with current screenshots.
|
|
137
191
|
*/
|
|
138
192
|
async updateBaselines(results) {
|
|
@@ -150,6 +204,43 @@ var MuseaVrtRunner = class {
|
|
|
150
204
|
return updated;
|
|
151
205
|
}
|
|
152
206
|
/**
|
|
207
|
+
* Approve specific failed results (update their baselines).
|
|
208
|
+
*/
|
|
209
|
+
async approveResults(results, pattern) {
|
|
210
|
+
const toApprove = pattern ? results.filter((r) => {
|
|
211
|
+
const name = `${path.basename(r.artPath, ".art.vue")}/${r.variantName}`;
|
|
212
|
+
return name.includes(pattern) || matchGlob(name, pattern);
|
|
213
|
+
}) : results.filter((r) => !r.passed && !r.error);
|
|
214
|
+
return this.updateBaselines(toApprove);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Clean orphaned snapshots (no corresponding art/variant).
|
|
218
|
+
*/
|
|
219
|
+
async cleanOrphans(artFiles) {
|
|
220
|
+
const snapshotDir = this.options.snapshotDir;
|
|
221
|
+
let cleaned = 0;
|
|
222
|
+
try {
|
|
223
|
+
const files = await fs.promises.readdir(snapshotDir);
|
|
224
|
+
const validNames = new Set();
|
|
225
|
+
for (const art of artFiles) {
|
|
226
|
+
const artBaseName = path.basename(art.path, ".art.vue");
|
|
227
|
+
for (const variant of art.variants) {
|
|
228
|
+
if (variant.skipVrt) continue;
|
|
229
|
+
for (const viewport of this.options.viewports) {
|
|
230
|
+
const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;
|
|
231
|
+
validNames.add(`${artBaseName}--${variant.name}--${viewportName}.png`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const file of files) if (file.endsWith(".png") && !validNames.has(file)) {
|
|
236
|
+
await fs.promises.unlink(path.join(snapshotDir, file));
|
|
237
|
+
cleaned++;
|
|
238
|
+
console.log(`[vrt] Cleaned: ${file}`);
|
|
239
|
+
}
|
|
240
|
+
} catch {}
|
|
241
|
+
return cleaned;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
153
244
|
* Get VRT summary statistics.
|
|
154
245
|
*/
|
|
155
246
|
getSummary(results) {
|
|
@@ -204,6 +295,13 @@ var MuseaVrtRunner = class {
|
|
|
204
295
|
width,
|
|
205
296
|
height
|
|
206
297
|
});
|
|
298
|
+
const useAntiAliasing = this.comparison.antiAliasing ?? true;
|
|
299
|
+
const useAlpha = this.comparison.alpha ?? true;
|
|
300
|
+
const diffColor = this.comparison.diffColor ?? {
|
|
301
|
+
r: 255,
|
|
302
|
+
g: 0,
|
|
303
|
+
b: 0
|
|
304
|
+
};
|
|
207
305
|
let diffPixels = 0;
|
|
208
306
|
const threshold = .1;
|
|
209
307
|
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
|
|
@@ -211,19 +309,25 @@ var MuseaVrtRunner = class {
|
|
|
211
309
|
const r1 = baseline.data[idx];
|
|
212
310
|
const g1 = baseline.data[idx + 1];
|
|
213
311
|
const b1 = baseline.data[idx + 2];
|
|
214
|
-
const a1 = baseline.data[idx + 3];
|
|
312
|
+
const a1 = useAlpha ? baseline.data[idx + 3] : 255;
|
|
215
313
|
const r2 = current.data[idx];
|
|
216
314
|
const g2 = current.data[idx + 1];
|
|
217
315
|
const b2 = current.data[idx + 2];
|
|
218
|
-
const a2 = current.data[idx + 3];
|
|
316
|
+
const a2 = useAlpha ? current.data[idx + 3] : 255;
|
|
219
317
|
const delta = colorDelta(r1, g1, b1, a1, r2, g2, b2, a2);
|
|
220
|
-
if (delta > threshold * 255 * 255) {
|
|
221
|
-
diffPixels++;
|
|
318
|
+
if (delta > threshold * 255 * 255) if (useAntiAliasing && isAntiAliased(baseline, current, x, y, width, height)) {
|
|
222
319
|
diff.data[idx] = 255;
|
|
223
|
-
diff.data[idx + 1] =
|
|
320
|
+
diff.data[idx + 1] = 200;
|
|
224
321
|
diff.data[idx + 2] = 0;
|
|
225
|
-
diff.data[idx + 3] =
|
|
322
|
+
diff.data[idx + 3] = 128;
|
|
226
323
|
} else {
|
|
324
|
+
diffPixels++;
|
|
325
|
+
diff.data[idx] = diffColor.r;
|
|
326
|
+
diff.data[idx + 1] = diffColor.g;
|
|
327
|
+
diff.data[idx + 2] = diffColor.b;
|
|
328
|
+
diff.data[idx + 3] = 255;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
227
331
|
const gray = Math.round((r2 + g2 + b2) / 3);
|
|
228
332
|
diff.data[idx] = gray;
|
|
229
333
|
diff.data[idx + 1] = gray;
|
|
@@ -242,7 +346,7 @@ var MuseaVrtRunner = class {
|
|
|
242
346
|
};
|
|
243
347
|
/**
|
|
244
348
|
* Generate VRT report in HTML format.
|
|
245
|
-
*
|
|
349
|
+
* Supports side-by-side, overlay, and slider comparison modes.
|
|
246
350
|
*/
|
|
247
351
|
function generateVrtReport(results, summary) {
|
|
248
352
|
const formatDuration = (ms) => {
|
|
@@ -289,7 +393,6 @@ function generateVrtReport(results, summary) {
|
|
|
289
393
|
line-height: 1.5;
|
|
290
394
|
}
|
|
291
395
|
|
|
292
|
-
/* Header */
|
|
293
396
|
.header {
|
|
294
397
|
background: var(--musea-bg-secondary);
|
|
295
398
|
border-bottom: 1px solid var(--musea-border);
|
|
@@ -301,258 +404,78 @@ function generateVrtReport(results, summary) {
|
|
|
301
404
|
top: 0;
|
|
302
405
|
z-index: 100;
|
|
303
406
|
}
|
|
304
|
-
.header-left {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
.logo {
|
|
310
|
-
font-size: 1.25rem;
|
|
311
|
-
font-weight: 700;
|
|
312
|
-
color: var(--musea-accent);
|
|
313
|
-
}
|
|
314
|
-
.header-title {
|
|
315
|
-
color: var(--musea-text-muted);
|
|
316
|
-
font-size: 0.875rem;
|
|
317
|
-
}
|
|
318
|
-
.header-meta {
|
|
319
|
-
display: flex;
|
|
320
|
-
align-items: center;
|
|
321
|
-
gap: 1.5rem;
|
|
322
|
-
font-size: 0.8125rem;
|
|
323
|
-
color: var(--musea-text-muted);
|
|
324
|
-
}
|
|
325
|
-
.header-meta span {
|
|
326
|
-
display: flex;
|
|
327
|
-
align-items: center;
|
|
328
|
-
gap: 0.375rem;
|
|
329
|
-
}
|
|
407
|
+
.header-left { display: flex; align-items: center; gap: 1rem; }
|
|
408
|
+
.logo { font-size: 1.25rem; font-weight: 700; color: var(--musea-accent); }
|
|
409
|
+
.header-title { color: var(--musea-text-muted); font-size: 0.875rem; }
|
|
410
|
+
.header-meta { display: flex; align-items: center; gap: 1.5rem; font-size: 0.8125rem; color: var(--musea-text-muted); }
|
|
411
|
+
.header-meta span { display: flex; align-items: center; gap: 0.375rem; }
|
|
330
412
|
|
|
331
|
-
|
|
332
|
-
.main {
|
|
333
|
-
max-width: 1400px;
|
|
334
|
-
margin: 0 auto;
|
|
335
|
-
padding: 2rem;
|
|
336
|
-
}
|
|
413
|
+
.main { max-width: 1400px; margin: 0 auto; padding: 2rem; }
|
|
337
414
|
|
|
338
|
-
|
|
339
|
-
.
|
|
340
|
-
|
|
341
|
-
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
342
|
-
gap: 1rem;
|
|
343
|
-
margin-bottom: 2rem;
|
|
344
|
-
}
|
|
345
|
-
.stat {
|
|
346
|
-
background: var(--musea-bg-secondary);
|
|
347
|
-
border: 1px solid var(--musea-border);
|
|
348
|
-
border-radius: 8px;
|
|
349
|
-
padding: 1.25rem;
|
|
350
|
-
position: relative;
|
|
351
|
-
overflow: hidden;
|
|
352
|
-
}
|
|
353
|
-
.stat::before {
|
|
354
|
-
content: '';
|
|
355
|
-
position: absolute;
|
|
356
|
-
left: 0;
|
|
357
|
-
top: 0;
|
|
358
|
-
bottom: 0;
|
|
359
|
-
width: 3px;
|
|
360
|
-
}
|
|
415
|
+
.summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
|
|
416
|
+
.stat { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; padding: 1.25rem; position: relative; overflow: hidden; }
|
|
417
|
+
.stat::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }
|
|
361
418
|
.stat.passed::before { background: var(--musea-success); }
|
|
362
419
|
.stat.failed::before { background: var(--musea-error); }
|
|
363
420
|
.stat.new::before { background: var(--musea-info); }
|
|
364
421
|
.stat.skipped::before { background: var(--musea-warning); }
|
|
365
|
-
.stat-value {
|
|
366
|
-
font-size: 2rem;
|
|
367
|
-
font-weight: 700;
|
|
368
|
-
font-variant-numeric: tabular-nums;
|
|
369
|
-
line-height: 1;
|
|
370
|
-
margin-bottom: 0.25rem;
|
|
371
|
-
}
|
|
422
|
+
.stat-value { font-size: 2rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: 0.25rem; }
|
|
372
423
|
.stat.passed .stat-value { color: var(--musea-success); }
|
|
373
424
|
.stat.failed .stat-value { color: var(--musea-error); }
|
|
374
425
|
.stat.new .stat-value { color: var(--musea-info); }
|
|
375
426
|
.stat.skipped .stat-value { color: var(--musea-warning); }
|
|
376
|
-
.stat-label {
|
|
377
|
-
color: var(--musea-text-muted);
|
|
378
|
-
font-size: 0.75rem;
|
|
379
|
-
text-transform: uppercase;
|
|
380
|
-
letter-spacing: 0.08em;
|
|
381
|
-
font-weight: 500;
|
|
382
|
-
}
|
|
427
|
+
.stat-label { color: var(--musea-text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; }
|
|
383
428
|
|
|
384
|
-
|
|
385
|
-
.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
flex-wrap: wrap;
|
|
390
|
-
}
|
|
391
|
-
.filter-btn {
|
|
392
|
-
background: var(--musea-bg-secondary);
|
|
393
|
-
border: 1px solid var(--musea-border);
|
|
394
|
-
color: var(--musea-text);
|
|
395
|
-
padding: 0.5rem 1rem;
|
|
396
|
-
border-radius: 6px;
|
|
397
|
-
cursor: pointer;
|
|
398
|
-
font-size: 0.8125rem;
|
|
399
|
-
font-weight: 500;
|
|
400
|
-
transition: all 0.15s ease;
|
|
401
|
-
}
|
|
402
|
-
.filter-btn:hover {
|
|
403
|
-
background: var(--musea-bg-tertiary);
|
|
404
|
-
border-color: var(--musea-text-muted);
|
|
405
|
-
}
|
|
406
|
-
.filter-btn.active {
|
|
407
|
-
background: var(--musea-accent);
|
|
408
|
-
border-color: var(--musea-accent);
|
|
409
|
-
color: #fff;
|
|
410
|
-
}
|
|
411
|
-
.filter-btn .count {
|
|
412
|
-
opacity: 0.7;
|
|
413
|
-
margin-left: 0.25rem;
|
|
414
|
-
}
|
|
429
|
+
.filters { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; flex-wrap: wrap; }
|
|
430
|
+
.filter-btn { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); color: var(--musea-text); padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; font-size: 0.8125rem; font-weight: 500; transition: all 0.15s ease; }
|
|
431
|
+
.filter-btn:hover { background: var(--musea-bg-tertiary); border-color: var(--musea-text-muted); }
|
|
432
|
+
.filter-btn.active { background: var(--musea-accent); border-color: var(--musea-accent); color: #fff; }
|
|
433
|
+
.filter-btn .count { opacity: 0.7; margin-left: 0.25rem; }
|
|
415
434
|
|
|
416
|
-
/*
|
|
417
|
-
.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
.result {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
border-radius: 8px;
|
|
426
|
-
overflow: hidden;
|
|
427
|
-
transition: border-color 0.15s ease;
|
|
428
|
-
}
|
|
429
|
-
.result:hover {
|
|
430
|
-
border-color: var(--musea-text-muted);
|
|
431
|
-
}
|
|
432
|
-
.result-header {
|
|
433
|
-
padding: 1rem 1.25rem;
|
|
434
|
-
display: flex;
|
|
435
|
-
justify-content: space-between;
|
|
436
|
-
align-items: center;
|
|
437
|
-
cursor: pointer;
|
|
438
|
-
border-left: 3px solid transparent;
|
|
439
|
-
background: var(--musea-bg-tertiary);
|
|
440
|
-
}
|
|
435
|
+
/* Comparison mode toggle */
|
|
436
|
+
.compare-modes { display: flex; gap: 0.25rem; margin-bottom: 1.5rem; background: var(--musea-bg-secondary); border-radius: 6px; padding: 0.25rem; width: fit-content; }
|
|
437
|
+
.compare-mode-btn { background: none; border: none; color: var(--musea-text-muted); padding: 0.375rem 0.75rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 500; transition: all 0.15s ease; }
|
|
438
|
+
.compare-mode-btn.active { background: var(--musea-bg-tertiary); color: var(--musea-text); }
|
|
439
|
+
|
|
440
|
+
.results { display: flex; flex-direction: column; gap: 0.75rem; }
|
|
441
|
+
.result { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; overflow: hidden; transition: border-color 0.15s ease; }
|
|
442
|
+
.result:hover { border-color: var(--musea-text-muted); }
|
|
443
|
+
.result-header { padding: 1rem 1.25rem; display: flex; justify-content: space-between; align-items: center; cursor: pointer; border-left: 3px solid transparent; background: var(--musea-bg-tertiary); }
|
|
441
444
|
.result.passed .result-header { border-left-color: var(--musea-success); }
|
|
442
445
|
.result.failed .result-header { border-left-color: var(--musea-error); }
|
|
443
446
|
.result.new .result-header { border-left-color: var(--musea-info); }
|
|
444
447
|
.result.error .result-header { border-left-color: var(--musea-warning); }
|
|
445
448
|
|
|
446
|
-
.result-info {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
.result-name {
|
|
452
|
-
font-weight: 600;
|
|
453
|
-
font-size: 0.9375rem;
|
|
454
|
-
}
|
|
455
|
-
.result-meta {
|
|
456
|
-
color: var(--musea-text-muted);
|
|
457
|
-
font-size: 0.8125rem;
|
|
458
|
-
padding: 0.125rem 0.5rem;
|
|
459
|
-
background: var(--musea-bg-secondary);
|
|
460
|
-
border-radius: 4px;
|
|
461
|
-
}
|
|
462
|
-
.result-badge {
|
|
463
|
-
padding: 0.25rem 0.625rem;
|
|
464
|
-
border-radius: 4px;
|
|
465
|
-
font-size: 0.6875rem;
|
|
466
|
-
font-weight: 600;
|
|
467
|
-
text-transform: uppercase;
|
|
468
|
-
letter-spacing: 0.04em;
|
|
469
|
-
}
|
|
449
|
+
.result-info { display: flex; align-items: center; gap: 1rem; }
|
|
450
|
+
.result-name { font-weight: 600; font-size: 0.9375rem; }
|
|
451
|
+
.result-meta { color: var(--musea-text-muted); font-size: 0.8125rem; padding: 0.125rem 0.5rem; background: var(--musea-bg-secondary); border-radius: 4px; }
|
|
452
|
+
.result-badge { padding: 0.25rem 0.625rem; border-radius: 4px; font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
470
453
|
.result.passed .result-badge { background: rgba(74, 222, 128, 0.15); color: var(--musea-success); }
|
|
471
454
|
.result.failed .result-badge { background: rgba(248, 113, 113, 0.15); color: var(--musea-error); }
|
|
472
455
|
.result.new .result-badge { background: rgba(96, 165, 250, 0.15); color: var(--musea-info); }
|
|
473
456
|
.result.error .result-badge { background: rgba(251, 191, 36, 0.15); color: var(--musea-warning); }
|
|
474
457
|
|
|
475
|
-
.result-body {
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
.result-details {
|
|
479
|
-
padding: 0.875rem 1.25rem;
|
|
480
|
-
font-size: 0.8125rem;
|
|
481
|
-
color: var(--musea-text-muted);
|
|
482
|
-
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
483
|
-
background: var(--musea-bg-primary);
|
|
484
|
-
}
|
|
485
|
-
.result-details.error {
|
|
486
|
-
color: var(--musea-error);
|
|
487
|
-
}
|
|
458
|
+
.result-body { border-top: 1px solid var(--musea-border); }
|
|
459
|
+
.result-details { padding: 0.875rem 1.25rem; font-size: 0.8125rem; color: var(--musea-text-muted); font-family: 'SF Mono', 'Fira Code', monospace; background: var(--musea-bg-primary); }
|
|
460
|
+
.result-details.error { color: var(--musea-error); }
|
|
488
461
|
|
|
489
|
-
|
|
490
|
-
.result-images {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
background: var(--musea-bg-primary);
|
|
496
|
-
}
|
|
497
|
-
.image-container {
|
|
498
|
-
background: var(--musea-bg-secondary);
|
|
499
|
-
border: 1px solid var(--musea-border);
|
|
500
|
-
border-radius: 6px;
|
|
501
|
-
overflow: hidden;
|
|
502
|
-
}
|
|
503
|
-
.image-label {
|
|
504
|
-
padding: 0.625rem 0.875rem;
|
|
505
|
-
font-size: 0.6875rem;
|
|
506
|
-
font-weight: 600;
|
|
507
|
-
color: var(--musea-text-muted);
|
|
508
|
-
text-transform: uppercase;
|
|
509
|
-
letter-spacing: 0.08em;
|
|
510
|
-
background: var(--musea-bg-tertiary);
|
|
511
|
-
border-bottom: 1px solid var(--musea-border);
|
|
512
|
-
}
|
|
513
|
-
.image-wrapper {
|
|
514
|
-
padding: 0.5rem;
|
|
515
|
-
background: repeating-conic-gradient(
|
|
516
|
-
var(--musea-bg-tertiary) 0% 25%,
|
|
517
|
-
var(--musea-bg-secondary) 0% 50%
|
|
518
|
-
) 50% / 16px 16px;
|
|
519
|
-
}
|
|
520
|
-
.image-container img {
|
|
521
|
-
width: 100%;
|
|
522
|
-
height: auto;
|
|
523
|
-
display: block;
|
|
524
|
-
border-radius: 2px;
|
|
525
|
-
}
|
|
462
|
+
.result-images { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; padding: 1.25rem; background: var(--musea-bg-primary); }
|
|
463
|
+
.result-images.overlay { grid-template-columns: 1fr; }
|
|
464
|
+
.image-container { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 6px; overflow: hidden; }
|
|
465
|
+
.image-label { padding: 0.625rem 0.875rem; font-size: 0.6875rem; font-weight: 600; color: var(--musea-text-muted); text-transform: uppercase; letter-spacing: 0.08em; background: var(--musea-bg-tertiary); border-bottom: 1px solid var(--musea-border); }
|
|
466
|
+
.image-wrapper { padding: 0.5rem; background: repeating-conic-gradient(var(--musea-bg-tertiary) 0% 25%, var(--musea-bg-secondary) 0% 50%) 50% / 16px 16px; }
|
|
467
|
+
.image-container img { width: 100%; height: auto; display: block; border-radius: 2px; }
|
|
526
468
|
|
|
527
|
-
/*
|
|
528
|
-
.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
533
|
-
.empty-state-icon {
|
|
534
|
-
font-size: 3rem;
|
|
535
|
-
margin-bottom: 1rem;
|
|
536
|
-
opacity: 0.5;
|
|
537
|
-
}
|
|
469
|
+
/* Slider comparison */
|
|
470
|
+
.slider-compare { position: relative; overflow: hidden; }
|
|
471
|
+
.slider-compare img { display: block; width: 100%; }
|
|
472
|
+
.slider-overlay { position: absolute; top: 0; left: 0; bottom: 0; overflow: hidden; border-right: 2px solid var(--musea-accent); }
|
|
473
|
+
.slider-overlay img { display: block; min-width: 100%; height: 100%; object-fit: cover; }
|
|
538
474
|
|
|
539
|
-
|
|
540
|
-
.all-passed {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
border-radius: 8px;
|
|
544
|
-
padding: 1.5rem;
|
|
545
|
-
text-align: center;
|
|
546
|
-
margin-bottom: 1.5rem;
|
|
547
|
-
}
|
|
548
|
-
.all-passed-icon {
|
|
549
|
-
font-size: 2.5rem;
|
|
550
|
-
margin-bottom: 0.5rem;
|
|
551
|
-
}
|
|
552
|
-
.all-passed-text {
|
|
553
|
-
color: var(--musea-success);
|
|
554
|
-
font-weight: 600;
|
|
555
|
-
}
|
|
475
|
+
.empty-state { text-align: center; padding: 4rem 2rem; color: var(--musea-text-muted); }
|
|
476
|
+
.all-passed { background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.2); border-radius: 8px; padding: 1.5rem; text-align: center; margin-bottom: 1.5rem; }
|
|
477
|
+
.all-passed-icon { font-size: 2.5rem; margin-bottom: 0.5rem; }
|
|
478
|
+
.all-passed-text { color: var(--musea-success); font-weight: 600; }
|
|
556
479
|
</style>
|
|
557
480
|
</head>
|
|
558
481
|
<body>
|
|
@@ -579,22 +502,10 @@ function generateVrtReport(results, summary) {
|
|
|
579
502
|
|
|
580
503
|
<main class="main">
|
|
581
504
|
<div class="summary">
|
|
582
|
-
<div class="stat passed">
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
</div>
|
|
586
|
-
<div class="stat failed">
|
|
587
|
-
<div class="stat-value">${summary.failed}</div>
|
|
588
|
-
<div class="stat-label">Failed</div>
|
|
589
|
-
</div>
|
|
590
|
-
<div class="stat new">
|
|
591
|
-
<div class="stat-value">${summary.new}</div>
|
|
592
|
-
<div class="stat-label">New</div>
|
|
593
|
-
</div>
|
|
594
|
-
<div class="stat skipped">
|
|
595
|
-
<div class="stat-value">${summary.skipped}</div>
|
|
596
|
-
<div class="stat-label">Skipped</div>
|
|
597
|
-
</div>
|
|
505
|
+
<div class="stat passed"><div class="stat-value">${summary.passed}</div><div class="stat-label">Passed</div></div>
|
|
506
|
+
<div class="stat failed"><div class="stat-value">${summary.failed}</div><div class="stat-label">Failed</div></div>
|
|
507
|
+
<div class="stat new"><div class="stat-value">${summary.new}</div><div class="stat-label">New</div></div>
|
|
508
|
+
<div class="stat skipped"><div class="stat-value">${summary.skipped}</div><div class="stat-label">Skipped</div></div>
|
|
598
509
|
</div>
|
|
599
510
|
|
|
600
511
|
${summary.failed === 0 && summary.skipped === 0 && summary.total > 0 ? `<div class="all-passed">
|
|
@@ -609,11 +520,14 @@ function generateVrtReport(results, summary) {
|
|
|
609
520
|
<button class="filter-btn" data-filter="new">New<span class="count">(${summary.new})</span></button>
|
|
610
521
|
</div>
|
|
611
522
|
|
|
523
|
+
<div class="compare-modes">
|
|
524
|
+
<button class="compare-mode-btn active" data-mode="side-by-side">Side by Side</button>
|
|
525
|
+
<button class="compare-mode-btn" data-mode="overlay">Overlay</button>
|
|
526
|
+
<button class="compare-mode-btn" data-mode="slider">Slider</button>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
612
529
|
<div class="results">
|
|
613
|
-
${results.length === 0 ? `<div class="empty-state">
|
|
614
|
-
<div class="empty-state-icon">📷</div>
|
|
615
|
-
<p>No visual tests found</p>
|
|
616
|
-
</div>` : results.map((r) => {
|
|
530
|
+
${results.length === 0 ? `<div class="empty-state"><p>No visual tests found</p></div>` : results.map((r) => {
|
|
617
531
|
const status = r.error ? "error" : r.isNew ? "new" : r.passed ? "passed" : "failed";
|
|
618
532
|
const badge = r.error ? "Error" : r.isNew ? "New" : r.passed ? "Passed" : "Failed";
|
|
619
533
|
const artName = path.basename(r.artPath, ".art.vue");
|
|
@@ -627,25 +541,10 @@ function generateVrtReport(results, summary) {
|
|
|
627
541
|
details = `<div class="result-details">diff: ${diffFormatted}% (${pixelsFormatted} / ${totalFormatted} pixels)</div>`;
|
|
628
542
|
}
|
|
629
543
|
let images = "";
|
|
630
|
-
if (!r.error && !r.passed && r.diffPath) images = `<div class="result-images">
|
|
631
|
-
${r.snapshotPath ? `<div class="image-container">
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
<img src="file://${r.snapshotPath}" alt="Baseline" loading="lazy" />
|
|
635
|
-
</div>
|
|
636
|
-
</div>` : ""}
|
|
637
|
-
${r.currentPath ? `<div class="image-container">
|
|
638
|
-
<div class="image-label">Current</div>
|
|
639
|
-
<div class="image-wrapper">
|
|
640
|
-
<img src="file://${r.currentPath}" alt="Current" loading="lazy" />
|
|
641
|
-
</div>
|
|
642
|
-
</div>` : ""}
|
|
643
|
-
${r.diffPath ? `<div class="image-container">
|
|
644
|
-
<div class="image-label">Diff</div>
|
|
645
|
-
<div class="image-wrapper">
|
|
646
|
-
<img src="file://${r.diffPath}" alt="Diff" loading="lazy" />
|
|
647
|
-
</div>
|
|
648
|
-
</div>` : ""}
|
|
544
|
+
if (!r.error && !r.passed && r.diffPath) images = `<div class="result-images" data-baseline="file://${r.snapshotPath}" data-current="file://${r.currentPath}" data-diff="file://${r.diffPath}">
|
|
545
|
+
${r.snapshotPath ? `<div class="image-container"><div class="image-label">Baseline</div><div class="image-wrapper"><img src="file://${r.snapshotPath}" alt="Baseline" loading="lazy" /></div></div>` : ""}
|
|
546
|
+
${r.currentPath ? `<div class="image-container"><div class="image-label">Current</div><div class="image-wrapper"><img src="file://${r.currentPath}" alt="Current" loading="lazy" /></div></div>` : ""}
|
|
547
|
+
${r.diffPath ? `<div class="image-container"><div class="image-label">Diff</div><div class="image-wrapper"><img src="file://${r.diffPath}" alt="Diff" loading="lazy" /></div></div>` : ""}
|
|
649
548
|
</div>`;
|
|
650
549
|
const hasBody = details || images;
|
|
651
550
|
return `<div class="result ${status}" data-status="${status}">
|
|
@@ -663,20 +562,26 @@ function generateVrtReport(results, summary) {
|
|
|
663
562
|
</main>
|
|
664
563
|
|
|
665
564
|
<script>
|
|
565
|
+
// Filter buttons
|
|
666
566
|
document.querySelectorAll('.filter-btn').forEach(btn => {
|
|
667
567
|
btn.addEventListener('click', () => {
|
|
668
568
|
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
|
669
569
|
btn.classList.add('active');
|
|
670
570
|
const filter = btn.dataset.filter;
|
|
671
571
|
document.querySelectorAll('.result').forEach(result => {
|
|
672
|
-
|
|
673
|
-
result.style.display = 'block';
|
|
674
|
-
} else {
|
|
675
|
-
result.style.display = 'none';
|
|
676
|
-
}
|
|
572
|
+
result.style.display = (filter === 'all' || result.dataset.status === filter) ? 'block' : 'none';
|
|
677
573
|
});
|
|
678
574
|
});
|
|
679
575
|
});
|
|
576
|
+
|
|
577
|
+
// Compare mode buttons
|
|
578
|
+
document.querySelectorAll('.compare-mode-btn').forEach(btn => {
|
|
579
|
+
btn.addEventListener('click', () => {
|
|
580
|
+
document.querySelectorAll('.compare-mode-btn').forEach(b => b.classList.remove('active'));
|
|
581
|
+
btn.classList.add('active');
|
|
582
|
+
// Mode switching would update result-images display; this is a static report for now
|
|
583
|
+
});
|
|
584
|
+
});
|
|
680
585
|
</script>
|
|
681
586
|
</body>
|
|
682
587
|
</html>`;
|
|
@@ -719,7 +624,6 @@ async function writePng(png, filepath) {
|
|
|
719
624
|
}
|
|
720
625
|
/**
|
|
721
626
|
* Calculate color delta using YIQ color space.
|
|
722
|
-
* This is more perceptually accurate than simple RGB difference.
|
|
723
627
|
*/
|
|
724
628
|
function colorDelta(r1, g1, b1, a1, r2, g2, b2, a2) {
|
|
725
629
|
if (a1 !== 255) {
|
|
@@ -761,6 +665,35 @@ async function fileExists(filepath) {
|
|
|
761
665
|
}
|
|
762
666
|
}
|
|
763
667
|
/**
|
|
668
|
+
* Simple anti-aliasing detection.
|
|
669
|
+
* A pixel is likely anti-aliased if its neighbors have high contrast in opposite directions.
|
|
670
|
+
*/
|
|
671
|
+
function isAntiAliased(img1, img2, x, y, width, height) {
|
|
672
|
+
const minX = Math.max(0, x - 1);
|
|
673
|
+
const maxX = Math.min(width - 1, x + 1);
|
|
674
|
+
const minY = Math.max(0, y - 1);
|
|
675
|
+
const maxY = Math.min(height - 1, y + 1);
|
|
676
|
+
let zeroes = 0;
|
|
677
|
+
let positives = 0;
|
|
678
|
+
let negatives = 0;
|
|
679
|
+
for (let ny = minY; ny <= maxY; ny++) for (let nx = minX; nx <= maxX; nx++) {
|
|
680
|
+
if (nx === x && ny === y) continue;
|
|
681
|
+
const idx = (ny * width + nx) * 4;
|
|
682
|
+
const delta = colorDelta(img1.data[idx], img1.data[idx + 1], img1.data[idx + 2], img1.data[idx + 3], img2.data[idx], img2.data[idx + 1], img2.data[idx + 2], img2.data[idx + 3]);
|
|
683
|
+
if (delta === 0) zeroes++;
|
|
684
|
+
else if (delta > 0) positives++;
|
|
685
|
+
else negatives++;
|
|
686
|
+
}
|
|
687
|
+
return zeroes > 0 && (positives > 0 || negatives > 0) && positives + negatives < 4;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Simple glob matching for pattern-based filtering.
|
|
691
|
+
*/
|
|
692
|
+
function matchGlob(filepath, pattern) {
|
|
693
|
+
const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, ".*").replace(/\*(?!\*)/g, "[^/]*");
|
|
694
|
+
return new RegExp(`^${regex}$`).test(filepath);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
764
697
|
* Escape HTML special characters.
|
|
765
698
|
*/
|
|
766
699
|
function escapeHtml(str) {
|
|
@@ -770,4 +703,4 @@ var vrt_default = MuseaVrtRunner;
|
|
|
770
703
|
|
|
771
704
|
//#endregion
|
|
772
705
|
export { MuseaVrtRunner, generateVrtJsonReport, generateVrtReport, vrt_default };
|
|
773
|
-
//# sourceMappingURL=vrt-
|
|
706
|
+
//# sourceMappingURL=vrt-DP87vGIA.js.map
|