devlens-mcp 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -37
- package/RELEASE_NOTES.md +249 -0
- package/dist/bin/cli.js +2 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/figma/figma-properties.d.ts +88 -0
- package/dist/src/figma/figma-properties.d.ts.map +1 -0
- package/dist/src/figma/figma-properties.js +121 -0
- package/dist/src/figma/figma-properties.js.map +1 -0
- package/dist/src/figma/figma-structure.d.ts +43 -0
- package/dist/src/figma/figma-structure.d.ts.map +1 -0
- package/dist/src/figma/figma-structure.js +74 -0
- package/dist/src/figma/figma-structure.js.map +1 -0
- package/dist/src/figma/figma-url.d.ts +37 -0
- package/dist/src/figma/figma-url.d.ts.map +1 -0
- package/dist/src/figma/figma-url.js +68 -0
- package/dist/src/figma/figma-url.js.map +1 -0
- package/dist/src/jds-tokens/resolve-token.d.ts +2 -0
- package/dist/src/jds-tokens/resolve-token.d.ts.map +1 -0
- package/dist/src/jds-tokens/resolve-token.js +24 -0
- package/dist/src/jds-tokens/resolve-token.js.map +1 -0
- package/dist/src/jds-tokens/tokens.d.ts +2 -0
- package/dist/src/jds-tokens/tokens.d.ts.map +1 -0
- package/dist/src/jds-tokens/tokens.js +12 -0
- package/dist/src/jds-tokens/tokens.js.map +1 -0
- package/dist/src/platform/ios/ios-device.d.ts +2 -0
- package/dist/src/platform/ios/ios-device.d.ts.map +1 -1
- package/dist/src/platform/ios/ios-device.js +18 -8
- package/dist/src/platform/ios/ios-device.js.map +1 -1
- package/dist/src/platform/ios/scale-resolver.d.ts +29 -0
- package/dist/src/platform/ios/scale-resolver.d.ts.map +1 -0
- package/dist/src/platform/ios/scale-resolver.js +114 -0
- package/dist/src/platform/ios/scale-resolver.js.map +1 -0
- package/dist/src/platform/ios/simctl.d.ts +5 -0
- package/dist/src/platform/ios/simctl.d.ts.map +1 -1
- package/dist/src/platform/ios/simctl.js +4 -0
- package/dist/src/platform/ios/simctl.js.map +1 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +9 -1
- package/dist/src/server.js.map +1 -1
- package/dist/src/setup/config-patcher.d.ts +2 -0
- package/dist/src/setup/config-patcher.d.ts.map +1 -0
- package/dist/src/setup/config-patcher.js +128 -0
- package/dist/src/setup/config-patcher.js.map +1 -0
- package/dist/src/setup/shell-detector.d.ts +10 -0
- package/dist/src/setup/shell-detector.d.ts.map +1 -0
- package/dist/src/setup/shell-detector.js +72 -0
- package/dist/src/setup/shell-detector.js.map +1 -0
- package/dist/src/snapshot/device-properties.d.ts +53 -0
- package/dist/src/snapshot/device-properties.d.ts.map +1 -0
- package/dist/src/snapshot/device-properties.js +94 -0
- package/dist/src/snapshot/device-properties.js.map +1 -0
- package/dist/src/snapshot/formatter.d.ts +13 -5
- package/dist/src/snapshot/formatter.d.ts.map +1 -1
- package/dist/src/snapshot/formatter.js +28 -19
- package/dist/src/snapshot/formatter.js.map +1 -1
- package/dist/src/snapshot/layout-inference.d.ts +44 -0
- package/dist/src/snapshot/layout-inference.d.ts.map +1 -0
- package/dist/src/snapshot/layout-inference.js +138 -0
- package/dist/src/snapshot/layout-inference.js.map +1 -0
- package/dist/src/tools/analyze-tools.d.ts +60 -0
- package/dist/src/tools/analyze-tools.d.ts.map +1 -0
- package/dist/src/tools/analyze-tools.js +196 -0
- package/dist/src/tools/analyze-tools.js.map +1 -0
- package/dist/src/tools/interaction-tools.d.ts +6 -6
- package/dist/src/tools/interaction-tools.d.ts.map +1 -1
- package/dist/src/tools/interaction-tools.js +12 -2
- package/dist/src/tools/interaction-tools.js.map +1 -1
- package/dist/src/tools/metro-tools.d.ts +2 -2
- package/dist/src/tools/navigation-tools.d.ts.map +1 -1
- package/dist/src/tools/navigation-tools.js +6 -1
- package/dist/src/tools/navigation-tools.js.map +1 -1
- package/dist/src/tools/screenshot-tools.d.ts +75 -0
- package/dist/src/tools/screenshot-tools.d.ts.map +1 -1
- package/dist/src/tools/screenshot-tools.js +238 -8
- package/dist/src/tools/screenshot-tools.js.map +1 -1
- package/dist/src/tools/snapshot-tools.d.ts +15 -0
- package/dist/src/tools/snapshot-tools.d.ts.map +1 -1
- package/dist/src/tools/snapshot-tools.js +32 -1
- package/dist/src/tools/snapshot-tools.js.map +1 -1
- package/dist/src/tools/vqa-tools.d.ts +62 -0
- package/dist/src/tools/vqa-tools.d.ts.map +1 -0
- package/dist/src/tools/vqa-tools.js +204 -0
- package/dist/src/tools/vqa-tools.js.map +1 -0
- package/dist/src/utils/image-preprocess.d.ts +1 -1
- package/dist/src/utils/image-preprocess.d.ts.map +1 -1
- package/dist/src/utils/image-preprocess.js +79 -0
- package/dist/src/utils/image-preprocess.js.map +1 -1
- package/dist/src/visual/color-extractor.d.ts +37 -0
- package/dist/src/visual/color-extractor.d.ts.map +1 -0
- package/dist/src/visual/color-extractor.js +116 -0
- package/dist/src/visual/color-extractor.js.map +1 -0
- package/dist/src/visual/comparator.d.ts +21 -0
- package/dist/src/visual/comparator.d.ts.map +1 -1
- package/dist/src/visual/comparator.js +110 -13
- package/dist/src/visual/comparator.js.map +1 -1
- package/dist/src/visual/layout-analyzer.d.ts.map +1 -1
- package/dist/src/visual/layout-analyzer.js +17 -7
- package/dist/src/visual/layout-analyzer.js.map +1 -1
- package/dist/src/visual/node-matcher.d.ts +43 -0
- package/dist/src/visual/node-matcher.d.ts.map +1 -0
- package/dist/src/visual/node-matcher.js +190 -0
- package/dist/src/visual/node-matcher.js.map +1 -0
- package/dist/src/visual/property-comparator.d.ts +49 -0
- package/dist/src/visual/property-comparator.d.ts.map +1 -0
- package/dist/src/visual/property-comparator.js +246 -0
- package/dist/src/visual/property-comparator.js.map +1 -0
- package/dist/src/visual/structural-comparator.d.ts +46 -0
- package/dist/src/visual/structural-comparator.d.ts.map +1 -0
- package/dist/src/visual/structural-comparator.js +293 -0
- package/dist/src/visual/structural-comparator.js.map +1 -0
- package/dist/src/visual/vqa-analyzer.d.ts +30 -0
- package/dist/src/visual/vqa-analyzer.d.ts.map +1 -0
- package/dist/src/visual/vqa-analyzer.js +271 -0
- package/dist/src/visual/vqa-analyzer.js.map +1 -0
- package/docs/figma-workflow.md +207 -40
- package/docs/setup-guide.md +221 -226
- package/docs/tool-reference.md +71 -0
- package/package.json +1 -1
- package/tsconfig.json +1 -1
- package/INSTALLATION_GUIDE.md +0 -354
- package/QUICK_START.md +0 -153
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractDominantColors = extractDominantColors;
|
|
4
|
+
exports.extractColorsFromRegion = extractColorsFromRegion;
|
|
5
|
+
exports.compareColors = compareColors;
|
|
6
|
+
function rgbToHex(r, g, b) {
|
|
7
|
+
return `#${[r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* CIE76 Delta-E approximation for perceptual color distance.
|
|
11
|
+
* Values <5 are imperceptible, 5-10 are barely noticeable,
|
|
12
|
+
* 10-25 are noticeable, >25 are clearly different.
|
|
13
|
+
*/
|
|
14
|
+
function deltaE(r1, g1, b1, r2, g2, b2) {
|
|
15
|
+
const dr = r1 - r2;
|
|
16
|
+
const dg = g1 - g2;
|
|
17
|
+
const db = b1 - b2;
|
|
18
|
+
return Math.sqrt(dr * dr + dg * dg + db * db);
|
|
19
|
+
}
|
|
20
|
+
function quantizeColor(r, g, b, step) {
|
|
21
|
+
const qr = Math.round(r / step) * step;
|
|
22
|
+
const qg = Math.round(g / step) * step;
|
|
23
|
+
const qb = Math.round(b / step) * step;
|
|
24
|
+
return `${qr},${qg},${qb}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract dominant colors from a raw RGBA buffer region using color quantization.
|
|
28
|
+
*/
|
|
29
|
+
function extractDominantColors(data, width, height, maxColors = 5) {
|
|
30
|
+
const colorCounts = new Map();
|
|
31
|
+
const totalPixels = width * height;
|
|
32
|
+
const quantStep = 16;
|
|
33
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
34
|
+
const r = data[i];
|
|
35
|
+
const g = data[i + 1];
|
|
36
|
+
const b = data[i + 2];
|
|
37
|
+
const a = data[i + 3];
|
|
38
|
+
if (a < 128)
|
|
39
|
+
continue;
|
|
40
|
+
const key = quantizeColor(r, g, b, quantStep);
|
|
41
|
+
const existing = colorCounts.get(key);
|
|
42
|
+
if (existing) {
|
|
43
|
+
existing.r += r;
|
|
44
|
+
existing.g += g;
|
|
45
|
+
existing.b += b;
|
|
46
|
+
existing.count++;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
colorCounts.set(key, { r, g, b, count: 1 });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const sorted = [...colorCounts.values()]
|
|
53
|
+
.sort((a, b) => b.count - a.count)
|
|
54
|
+
.slice(0, maxColors);
|
|
55
|
+
return sorted.map((entry) => {
|
|
56
|
+
const avgR = Math.round(entry.r / entry.count);
|
|
57
|
+
const avgG = Math.round(entry.g / entry.count);
|
|
58
|
+
const avgB = Math.round(entry.b / entry.count);
|
|
59
|
+
return {
|
|
60
|
+
hex: rgbToHex(avgR, avgG, avgB),
|
|
61
|
+
r: avgR,
|
|
62
|
+
g: avgG,
|
|
63
|
+
b: avgB,
|
|
64
|
+
percentage: (entry.count / totalPixels) * 100,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract dominant colors from a PNG buffer in a specific region.
|
|
70
|
+
*/
|
|
71
|
+
async function extractColorsFromRegion(imageBuffer, bounds) {
|
|
72
|
+
const sharp = (await import("sharp")).default;
|
|
73
|
+
const result = await sharp(imageBuffer)
|
|
74
|
+
.extract(bounds)
|
|
75
|
+
.ensureAlpha()
|
|
76
|
+
.raw()
|
|
77
|
+
.toBuffer({ resolveWithObject: true });
|
|
78
|
+
return extractDominantColors(result.data, result.info.width, result.info.height);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Compare dominant colors between device and Figma regions.
|
|
82
|
+
*/
|
|
83
|
+
function compareColors(deviceColors, figmaColors) {
|
|
84
|
+
if (deviceColors.length === 0 || figmaColors.length === 0) {
|
|
85
|
+
return {
|
|
86
|
+
mismatch: false,
|
|
87
|
+
maxDeltaE: 0,
|
|
88
|
+
summary: "Insufficient color data for comparison.",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
let maxDelta = 0;
|
|
92
|
+
for (const dc of deviceColors.slice(0, 3)) {
|
|
93
|
+
let bestMatch = Infinity;
|
|
94
|
+
for (const fc of figmaColors) {
|
|
95
|
+
const d = deltaE(dc.r, dc.g, dc.b, fc.r, fc.g, fc.b);
|
|
96
|
+
bestMatch = Math.min(bestMatch, d);
|
|
97
|
+
}
|
|
98
|
+
maxDelta = Math.max(maxDelta, bestMatch);
|
|
99
|
+
}
|
|
100
|
+
const mismatch = maxDelta > 25;
|
|
101
|
+
let summary;
|
|
102
|
+
if (maxDelta < 5) {
|
|
103
|
+
summary = "Colors match precisely.";
|
|
104
|
+
}
|
|
105
|
+
else if (maxDelta < 15) {
|
|
106
|
+
summary = `Minor color difference (deltaE=${maxDelta.toFixed(1)}).`;
|
|
107
|
+
}
|
|
108
|
+
else if (maxDelta < 25) {
|
|
109
|
+
summary = `Noticeable color difference (deltaE=${maxDelta.toFixed(1)}). Check design tokens.`;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
summary = `Significant color mismatch (deltaE=${maxDelta.toFixed(1)}). VQA issue likely.`;
|
|
113
|
+
}
|
|
114
|
+
return { mismatch, maxDeltaE: maxDelta, summary };
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=color-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-extractor.js","sourceRoot":"","sources":["../../../src/visual/color-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,12 @@ export interface PreprocessConfig {
|
|
|
26
26
|
/** Save preprocessed image for debugging */
|
|
27
27
|
saveDebugImage?: boolean;
|
|
28
28
|
}
|
|
29
|
+
export interface ExclusionZone {
|
|
30
|
+
top: number;
|
|
31
|
+
bottom: number;
|
|
32
|
+
left: number;
|
|
33
|
+
right: number;
|
|
34
|
+
}
|
|
29
35
|
export interface ComparisonOptions {
|
|
30
36
|
/** Color difference threshold (0-1). Lower = stricter. Default: 0.1 */
|
|
31
37
|
threshold?: number;
|
|
@@ -42,6 +48,21 @@ export interface ComparisonOptions {
|
|
|
42
48
|
* Defaults to false for backward compatibility.
|
|
43
49
|
*/
|
|
44
50
|
preprocessReference?: boolean | PreprocessConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Exclude status bar and navigation bar from comparison.
|
|
53
|
+
* Removes known-volatile regions (time, battery, signal indicators).
|
|
54
|
+
* - "auto" (default): detect platform from image dimensions and exclude accordingly
|
|
55
|
+
* - "ios": exclude iOS status bar (top ~54px) and home indicator (bottom ~34px)
|
|
56
|
+
* - "android": exclude Android status bar (top ~48px) and nav bar (bottom ~48px)
|
|
57
|
+
* - "none": no exclusion
|
|
58
|
+
*/
|
|
59
|
+
excludeSystemBars?: "auto" | "ios" | "android" | "none";
|
|
60
|
+
/**
|
|
61
|
+
* Use perceptual comparison (SSIM-inspired) instead of strict pixel diff.
|
|
62
|
+
* Tolerates anti-aliasing and sub-pixel font rendering differences.
|
|
63
|
+
* Default: false
|
|
64
|
+
*/
|
|
65
|
+
perceptual?: boolean;
|
|
45
66
|
}
|
|
46
67
|
/**
|
|
47
68
|
* Compare a screenshot buffer against a reference image buffer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comparator.d.ts","sourceRoot":"","sources":["../../../src/visual/comparator.ts"],"names":[],"mappings":"AAKA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1D,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IAC1C;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"comparator.d.ts","sourceRoot":"","sources":["../../../src/visual/comparator.ts"],"names":[],"mappings":"AAKA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1D,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IAC1C;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACjD;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;IACxD;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAsGD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAgK3B"}
|
|
@@ -8,12 +8,88 @@ const promises_1 = require("fs/promises");
|
|
|
8
8
|
const pngjs_1 = require("pngjs");
|
|
9
9
|
const pixelmatch_1 = __importDefault(require("pixelmatch"));
|
|
10
10
|
const image_preprocess_js_1 = require("../utils/image-preprocess.js");
|
|
11
|
+
const IOS_STATUS_BAR_RATIO = 54 / 844;
|
|
12
|
+
const IOS_HOME_INDICATOR_RATIO = 34 / 844;
|
|
13
|
+
const ANDROID_STATUS_BAR_RATIO = 48 / 800;
|
|
14
|
+
const ANDROID_NAV_BAR_RATIO = 48 / 800;
|
|
15
|
+
function detectPlatformFromDimensions(width, height) {
|
|
16
|
+
const ratio = height / width;
|
|
17
|
+
if (ratio > 2.0)
|
|
18
|
+
return "ios";
|
|
19
|
+
return "android";
|
|
20
|
+
}
|
|
21
|
+
function getExclusionZone(mode, width, height) {
|
|
22
|
+
if (mode === "none")
|
|
23
|
+
return { top: 0, bottom: 0, left: 0, right: 0 };
|
|
24
|
+
const platform = mode === "auto" ? detectPlatformFromDimensions(width, height) : mode;
|
|
25
|
+
if (platform === "ios") {
|
|
26
|
+
return {
|
|
27
|
+
top: Math.ceil(height * IOS_STATUS_BAR_RATIO),
|
|
28
|
+
bottom: Math.ceil(height * IOS_HOME_INDICATOR_RATIO),
|
|
29
|
+
left: 0,
|
|
30
|
+
right: 0,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
top: Math.ceil(height * ANDROID_STATUS_BAR_RATIO),
|
|
35
|
+
bottom: Math.ceil(height * ANDROID_NAV_BAR_RATIO),
|
|
36
|
+
left: 0,
|
|
37
|
+
right: 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function maskExclusionZones(png, zone) {
|
|
41
|
+
const { width, height } = png;
|
|
42
|
+
for (let y = 0; y < height; y++) {
|
|
43
|
+
const inExcluded = y < zone.top || y >= height - zone.bottom;
|
|
44
|
+
if (!inExcluded)
|
|
45
|
+
continue;
|
|
46
|
+
for (let x = 0; x < width; x++) {
|
|
47
|
+
const idx = (width * y + x) * 4;
|
|
48
|
+
png.data[idx] = 0;
|
|
49
|
+
png.data[idx + 1] = 0;
|
|
50
|
+
png.data[idx + 2] = 0;
|
|
51
|
+
png.data[idx + 3] = 255;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compute SSIM-inspired perceptual similarity for a pair of images.
|
|
57
|
+
* Uses 8x8 block averaging to tolerate anti-aliasing and sub-pixel rendering diffs.
|
|
58
|
+
*/
|
|
59
|
+
function computePerceptualSimilarity(data1, data2, width, height) {
|
|
60
|
+
const blockSize = 8;
|
|
61
|
+
let matchingBlocks = 0;
|
|
62
|
+
let totalBlocks = 0;
|
|
63
|
+
for (let by = 0; by < height - blockSize; by += blockSize) {
|
|
64
|
+
for (let bx = 0; bx < width - blockSize; bx += blockSize) {
|
|
65
|
+
let sumDiff = 0;
|
|
66
|
+
let pixelCount = 0;
|
|
67
|
+
for (let y = by; y < by + blockSize; y++) {
|
|
68
|
+
for (let x = bx; x < bx + blockSize; x++) {
|
|
69
|
+
const idx = (width * y + x) * 4;
|
|
70
|
+
const dr = Math.abs(data1[idx] - data2[idx]);
|
|
71
|
+
const dg = Math.abs(data1[idx + 1] - data2[idx + 1]);
|
|
72
|
+
const db = Math.abs(data1[idx + 2] - data2[idx + 2]);
|
|
73
|
+
sumDiff += (dr + dg + db) / 3;
|
|
74
|
+
pixelCount++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
totalBlocks++;
|
|
78
|
+
const avgDiff = sumDiff / pixelCount;
|
|
79
|
+
if (avgDiff < 25)
|
|
80
|
+
matchingBlocks++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const similarity = totalBlocks > 0 ? matchingBlocks / totalBlocks : 0;
|
|
84
|
+
const diffPixels = Math.round((1 - similarity) * width * height);
|
|
85
|
+
return { similarity, diffPixels };
|
|
86
|
+
}
|
|
11
87
|
/**
|
|
12
88
|
* Compare a screenshot buffer against a reference image buffer.
|
|
13
89
|
* Both images are resized/cropped to match dimensions before comparison.
|
|
14
90
|
*/
|
|
15
91
|
async function compareScreenshots(screenshotBuffer, referenceBuffer, options = {}) {
|
|
16
|
-
const { threshold = 0.1, resizeStrategy = "fit", preprocessReference } = options;
|
|
92
|
+
const { threshold = 0.1, resizeStrategy = "fit", preprocessReference, excludeSystemBars = "auto", perceptual = false, } = options;
|
|
17
93
|
// Preprocess reference image if requested
|
|
18
94
|
let processedReferenceBuffer = referenceBuffer;
|
|
19
95
|
if (preprocessReference) {
|
|
@@ -79,34 +155,55 @@ async function compareScreenshots(screenshotBuffer, referenceBuffer, options = {
|
|
|
79
155
|
img2 = pngjs_1.PNG.sync.read(resized2);
|
|
80
156
|
}
|
|
81
157
|
}
|
|
82
|
-
//
|
|
158
|
+
// Apply exclusion zones (status bar, nav bar) to both images
|
|
83
159
|
const { width, height } = img1;
|
|
160
|
+
const exclusionZone = getExclusionZone(excludeSystemBars, width, height);
|
|
161
|
+
if (exclusionZone.top > 0 || exclusionZone.bottom > 0) {
|
|
162
|
+
maskExclusionZones(img1, exclusionZone);
|
|
163
|
+
maskExclusionZones(img2, exclusionZone);
|
|
164
|
+
}
|
|
84
165
|
const diff = new pngjs_1.PNG({ width, height });
|
|
85
|
-
// Run pixel comparison
|
|
86
|
-
const diffPixels = (0, pixelmatch_1.default)(img1.data, img2.data, diff.data, width, height, {
|
|
87
|
-
threshold,
|
|
88
|
-
includeAA: false, // Ignore anti-aliasing differences
|
|
89
|
-
});
|
|
90
166
|
const totalPixels = width * height;
|
|
91
|
-
|
|
167
|
+
let diffPixels;
|
|
168
|
+
let similarity;
|
|
169
|
+
if (perceptual) {
|
|
170
|
+
const perceptualResult = computePerceptualSimilarity(img1.data, img2.data, width, height);
|
|
171
|
+
diffPixels = perceptualResult.diffPixels;
|
|
172
|
+
similarity = perceptualResult.similarity;
|
|
173
|
+
(0, pixelmatch_1.default)(img1.data, img2.data, diff.data, width, height, {
|
|
174
|
+
threshold: 0.2,
|
|
175
|
+
includeAA: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
diffPixels = (0, pixelmatch_1.default)(img1.data, img2.data, diff.data, width, height, { threshold, includeAA: false });
|
|
180
|
+
similarity = 1 - diffPixels / totalPixels;
|
|
181
|
+
}
|
|
92
182
|
// Save diff image
|
|
93
183
|
const diffImagePath = `/tmp/devlens-diff-${Date.now()}.png`;
|
|
94
184
|
const diffBuffer = pngjs_1.PNG.sync.write(diff);
|
|
95
185
|
await (0, promises_1.writeFile)(diffImagePath, diffBuffer);
|
|
96
186
|
// Generate summary
|
|
97
187
|
const percentage = (similarity * 100).toFixed(1);
|
|
188
|
+
const exclusionNote = exclusionZone.top > 0 || exclusionZone.bottom > 0
|
|
189
|
+
? ` (status bar & nav bar excluded)`
|
|
190
|
+
: "";
|
|
191
|
+
const modeNote = perceptual ? " [perceptual mode]" : "";
|
|
98
192
|
let summary;
|
|
99
|
-
if (similarity >= 0.
|
|
100
|
-
summary = `
|
|
193
|
+
if (similarity >= 0.99) {
|
|
194
|
+
summary = `Pixel-perfect match (${percentage}%)${exclusionNote}${modeNote}. No VQA issues expected.`;
|
|
195
|
+
}
|
|
196
|
+
else if (similarity >= 0.95) {
|
|
197
|
+
summary = `Excellent match (${percentage}%)${exclusionNote}${modeNote}. The implementation closely matches the design.`;
|
|
101
198
|
}
|
|
102
199
|
else if (similarity >= 0.85) {
|
|
103
|
-
summary = `Good match (${percentage}%). Minor differences detected — check spacing, colors, or small layout shifts.`;
|
|
200
|
+
summary = `Good match (${percentage}%)${exclusionNote}${modeNote}. Minor differences detected — check spacing, colors, or small layout shifts.`;
|
|
104
201
|
}
|
|
105
202
|
else if (similarity >= 0.7) {
|
|
106
|
-
summary = `Partial match (${percentage}%). Noticeable differences — review the diff image for layout or styling issues.`;
|
|
203
|
+
summary = `Partial match (${percentage}%)${exclusionNote}${modeNote}. Noticeable differences — review the diff image for layout or styling issues.`;
|
|
107
204
|
}
|
|
108
205
|
else {
|
|
109
|
-
summary = `Low match (${percentage}%). Significant differences — the layout may need substantial changes.`;
|
|
206
|
+
summary = `Low match (${percentage}%)${exclusionNote}${modeNote}. Significant differences — the layout may need substantial changes.`;
|
|
110
207
|
}
|
|
111
208
|
return {
|
|
112
209
|
similarity,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comparator.js","sourceRoot":"","sources":["../../../src/visual/comparator.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"comparator.js","sourceRoot":"","sources":["../../../src/visual/comparator.ts"],"names":[],"mappings":";;;;;AAoLA,gDAoKC;AAxVD,0CAAwC;AACxC,iCAA4B;AAC5B,4DAAoC;AACpC,sEAAuF;AAyEvF,MAAM,oBAAoB,GAAG,EAAE,GAAG,GAAG,CAAC;AACtC,MAAM,wBAAwB,GAAG,EAAE,GAAG,GAAG,CAAC;AAC1C,MAAM,wBAAwB,GAAG,EAAE,GAAG,GAAG,CAAC;AAC1C,MAAM,qBAAqB,GAAG,EAAE,GAAG,GAAG,CAAC;AAEvC,SAAS,4BAA4B,CACnC,KAAa,EACb,MAAc;IAEd,MAAM,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7B,IAAI,KAAK,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC;IAC9B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CACvB,IAAyC,EACzC,KAAa,EACb,MAAc;IAEd,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAErE,MAAM,QAAQ,GACZ,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,4BAA4B,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,oBAAoB,CAAC;YAC7C,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,wBAAwB,CAAC;YACpD,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,CAAC;SACT,CAAC;IACJ,CAAC;IACD,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,wBAAwB,CAAC;QACjD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,qBAAqB,CAAC;QACjD,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,CAAC;KACT,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,GAAQ,EACR,IAAmB;IAEnB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,UAAU,GACd,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5C,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACtB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACtB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;QAC1B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,2BAA2B,CAClC,KAAa,EACb,KAAa,EACb,KAAa,EACb,MAAc;IAEd,MAAM,SAAS,GAAG,CAAC,CAAC;IACpB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,GAAG,SAAS,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;QAC1D,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;YACzD,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,UAAU,GAAG,CAAC,CAAC;YAEnB,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;oBACzC,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;oBAChC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;oBACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;oBACrD,OAAO,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;oBAC9B,UAAU,EAAE,CAAC;gBACf,CAAC;YACH,CAAC;YAED,WAAW,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;YACrC,IAAI,OAAO,GAAG,EAAE;gBAAE,cAAc,EAAE,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;IACjE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,gBAAwB,EACxB,eAAuB,EACvB,UAA6B,EAAE;IAE/B,MAAM,EACJ,SAAS,GAAG,GAAG,EACf,cAAc,GAAG,KAAK,EACtB,mBAAmB,EACnB,iBAAiB,GAAG,MAAM,EAC1B,UAAU,GAAG,KAAK,GACnB,GAAG,OAAO,CAAC;IAEZ,0CAA0C;IAC1C,IAAI,wBAAwB,GAAG,eAAe,CAAC;IAC/C,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,gBAAgB,GACpB,OAAO,mBAAmB,KAAK,SAAS;YACtC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE;YACtB,CAAC,CAAC;gBACE,QAAQ,EAAE,mBAAmB,CAAC,QAAQ;gBACtC,aAAa,EAAE,mBAAmB,CAAC,aAAa;gBAChD,cAAc,EAAE,mBAAmB,CAAC,cAAc;aACnD,CAAC;QAER,MAAM,gBAAgB,GAAG,MAAM,IAAA,qCAAe,EAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;QAClF,wBAAwB,GAAG,gBAAgB,CAAC,MAAM,CAAC;QAEnD,gCAAgC;QAChC,IAAI,OAAO,mBAAmB,KAAK,QAAQ,IAAI,mBAAmB,CAAC,cAAc,EAAE,CAAC;YAClF,MAAM,SAAS,GAAG,6BAA6B,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;YAChE,MAAM,IAAA,oBAAS,EAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,iDAAiD,SAAS,EAAE,CAAC,CAAC;YAC1E,OAAO,CAAC,GAAG,CAAC,mCAAmC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;YACzG,OAAO,CAAC,GAAG,CAAC,+BAA+B,gBAAgB,CAAC,eAAe,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,UAAU,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAE1D,mCAAmC;IACnC,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,IAAI,IAAI,GAAG,SAAS,CAAC;IAErB,IACE,UAAU,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK;QACpC,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EACtC,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAE9C,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;YAC9B,kDAAkD;YAClD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;YAEnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC;iBAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;iBACtE,GAAG,EAAE;iBACL,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,wBAAwB,CAAC;iBACnD,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;iBACtE,GAAG,EAAE;iBACL,QAAQ,EAAE,CAAC;YAEd,IAAI,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,MAAM,WAAW,GACf,cAAc,KAAK,OAAO;gBACxB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;gBAC7C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,YAAY,GAChB,cAAc,KAAK,OAAO;gBACxB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;gBAC/C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;YAEpD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC;iBAC3C,MAAM,CAAC,WAAW,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;iBAClD,GAAG,EAAE;iBACL,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,wBAAwB,CAAC;iBACnD,MAAM,CAAC,WAAW,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;iBAClD,GAAG,EAAE;iBACL,QAAQ,EAAE,CAAC;YAEd,IAAI,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,GAAG,WAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,aAAa,GAAG,gBAAgB,CAAC,iBAAiB,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACzE,IAAI,aAAa,CAAC,GAAG,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACxC,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,WAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;IACnC,IAAI,UAAkB,CAAC;IACvB,IAAI,UAAkB,CAAC;IAEvB,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,gBAAgB,GAAG,2BAA2B,CAClD,IAAI,CAAC,IAAyB,EAC9B,IAAI,CAAC,IAAyB,EAC9B,KAAK,EACL,MAAM,CACP,CAAC;QACF,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC;QACzC,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC;QAEzC,IAAA,oBAAU,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;YACzD,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,IAAA,oBAAU,EACrB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,IAAI,EACT,KAAK,EACL,MAAM,EACN,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAChC,CAAC;QACF,UAAU,GAAG,CAAC,GAAG,UAAU,GAAG,WAAW,CAAC;IAC5C,CAAC;IAED,kBAAkB;IAClB,MAAM,aAAa,GAAG,qBAAqB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IAC5D,MAAM,UAAU,GAAG,WAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,IAAA,oBAAS,EAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAE3C,mBAAmB;IACnB,MAAM,UAAU,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,aAAa,GACjB,aAAa,CAAC,GAAG,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAC/C,CAAC,CAAC,kCAAkC;QACpC,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,IAAI,OAAe,CAAC;IACpB,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,OAAO,GAAG,wBAAwB,UAAU,KAAK,aAAa,GAAG,QAAQ,2BAA2B,CAAC;IACvG,CAAC;SAAM,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QAC9B,OAAO,GAAG,oBAAoB,UAAU,KAAK,aAAa,GAAG,QAAQ,kDAAkD,CAAC;IAC1H,CAAC;SAAM,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QAC9B,OAAO,GAAG,eAAe,UAAU,KAAK,aAAa,GAAG,QAAQ,+EAA+E,CAAC;IAClJ,CAAC;SAAM,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;QAC7B,OAAO,GAAG,kBAAkB,UAAU,KAAK,aAAa,GAAG,QAAQ,gFAAgF,CAAC;IACtJ,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,cAAc,UAAU,KAAK,aAAa,GAAG,QAAQ,sEAAsE,CAAC;IACxI,CAAC;IAED,OAAO;QACL,UAAU;QACV,UAAU;QACV,WAAW;QACX,aAAa;QACb,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout-analyzer.d.ts","sourceRoot":"","sources":["../../../src/visual/layout-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D;;;;;;;;;GASG;AAEH,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,gEAAgE;IAChE,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;CACpD;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,8DAA8D;IAC9D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;
|
|
1
|
+
{"version":3,"file":"layout-analyzer.d.ts","sourceRoot":"","sources":["../../../src/visual/layout-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D;;;;;;;;;GASG;AAEH,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,gEAAgE;IAChE,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;CACpD;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,8DAA8D;IAC9D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AA0GD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,QAAQ,EAAE,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAyFvB;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA4C/D"}
|
|
@@ -10,24 +10,34 @@ const pixelmatch_1 = __importDefault(require("pixelmatch"));
|
|
|
10
10
|
const MIN_REGION_AREA = 3000;
|
|
11
11
|
/** Color difference threshold for pixelmatch (matches global comparator default) */
|
|
12
12
|
const REGION_THRESHOLD = 0.1;
|
|
13
|
+
/** Sub-pixel alignment padding to account for off-by-one edge mismatches */
|
|
14
|
+
const SUBPIXEL_PAD = 1;
|
|
13
15
|
function verdictFromSimilarity(s) {
|
|
14
|
-
if (s >= 0.
|
|
16
|
+
if (s >= 0.95)
|
|
15
17
|
return "excellent";
|
|
16
|
-
if (s >= 0.
|
|
18
|
+
if (s >= 0.82)
|
|
17
19
|
return "good";
|
|
18
20
|
if (s >= 0.60)
|
|
19
21
|
return "partial";
|
|
20
22
|
return "poor";
|
|
21
23
|
}
|
|
22
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Scale device-space element bounds into Figma image pixel space.
|
|
26
|
+
* Uses DPI-aware scaling: normalizes both coordinate spaces to a common
|
|
27
|
+
* logical pixel base before mapping, then applies sub-pixel alignment padding.
|
|
28
|
+
*/
|
|
23
29
|
function scaleToFigmaSpace(bounds, deviceW, deviceH, figmaW, figmaH) {
|
|
24
30
|
const scaleX = figmaW / deviceW;
|
|
25
31
|
const scaleY = figmaH / deviceH;
|
|
32
|
+
const rawLeft = bounds.x * scaleX;
|
|
33
|
+
const rawTop = bounds.y * scaleY;
|
|
34
|
+
const rawWidth = bounds.width * scaleX;
|
|
35
|
+
const rawHeight = bounds.height * scaleY;
|
|
26
36
|
return {
|
|
27
|
-
left: Math.round(
|
|
28
|
-
top: Math.round(
|
|
29
|
-
width: Math.max(1, Math.round(
|
|
30
|
-
height: Math.max(1, Math.round(
|
|
37
|
+
left: Math.max(0, Math.round(rawLeft) - SUBPIXEL_PAD),
|
|
38
|
+
top: Math.max(0, Math.round(rawTop) - SUBPIXEL_PAD),
|
|
39
|
+
width: Math.max(1, Math.round(rawWidth) + SUBPIXEL_PAD * 2),
|
|
40
|
+
height: Math.max(1, Math.round(rawHeight) + SUBPIXEL_PAD * 2),
|
|
31
41
|
};
|
|
32
42
|
}
|
|
33
43
|
/** Clamp bounds to image dimensions. Returns null if there's no overlap. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout-analyzer.js","sourceRoot":"","sources":["../../../src/visual/layout-analyzer.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"layout-analyzer.js","sourceRoot":"","sources":["../../../src/visual/layout-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Figma layout nodes to device accessibility tree nodes.
|
|
3
|
+
*
|
|
4
|
+
* They share no IDs — matching is done by a weighted combination of:
|
|
5
|
+
* - Text similarity (40%): Figma characters/name vs SnapshotNode text/label
|
|
6
|
+
* - Spatial proximity (40%): center distance after scaling to device space
|
|
7
|
+
* - Type compatibility (20%): Figma type vs device element type
|
|
8
|
+
*
|
|
9
|
+
* Uses greedy assignment: highest-scoring pair first, no double-matching.
|
|
10
|
+
*/
|
|
11
|
+
import type { FigmaLayoutNode } from "../figma/figma-structure.js";
|
|
12
|
+
import type { SnapshotNode } from "../platform/device.js";
|
|
13
|
+
export interface MatchedPair {
|
|
14
|
+
figmaNode: FigmaLayoutNode;
|
|
15
|
+
deviceNode: SnapshotNode;
|
|
16
|
+
/** Confidence score 0-1 */
|
|
17
|
+
confidence: number;
|
|
18
|
+
/** Path from Figma root (e.g., "Frame > Header > Title") */
|
|
19
|
+
figmaPath: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MatchResult {
|
|
22
|
+
matched: MatchedPair[];
|
|
23
|
+
unmatchedFigma: FigmaLayoutNode[];
|
|
24
|
+
unmatchedDevice: SnapshotNode[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Match Figma nodes to device SnapshotNodes using weighted scoring.
|
|
28
|
+
*
|
|
29
|
+
* @param figmaRoot Root FigmaLayoutNode tree
|
|
30
|
+
* @param deviceRoot Root SnapshotNode tree
|
|
31
|
+
* @param figmaBounds The absoluteBoundingBox of the Figma root (for scaling)
|
|
32
|
+
* @param deviceBounds Screen dimensions { width, height }
|
|
33
|
+
*/
|
|
34
|
+
export declare function matchNodes(figmaRoot: FigmaLayoutNode, deviceRoot: SnapshotNode, figmaBounds: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
}, deviceBounds: {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}): MatchResult;
|
|
43
|
+
//# sourceMappingURL=node-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-matcher.d.ts","sourceRoot":"","sources":["../../../src/visual/node-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAI1D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,eAAe,CAAC;IAC3B,UAAU,EAAE,YAAY,CAAC;IACzB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,cAAc,EAAE,eAAe,EAAE,CAAC;IAClC,eAAe,EAAE,YAAY,EAAE,CAAC;CACjC;AAyKD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,eAAe,EAC1B,UAAU,EAAE,YAAY,EACxB,WAAW,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACpE,YAAY,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C,WAAW,CAkEb"}
|