@calibrate-ds/core 0.1.39 → 0.1.41

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.
@@ -2,13 +2,16 @@ import type { PixelMatchResult } from "./types.js";
2
2
  /**
3
3
  * Compares two PNG files pixel-by-pixel using pixelmatch.
4
4
  * Saves an annotated diff image to diffOutputPath.
5
- * Returns the match score (0–1, 1 = identical), or null if comparison fails.
5
+ * Returns the match scores (0–1, 1 = identical), or null if comparison fails.
6
6
  *
7
- * Dimension mismatch handling in order:
8
- * 1. If impl is ≥1.5× larger in both dimensions (e.g. 2× DPR retina capture),
9
- * scale it down to the Figma reference size before comparing.
10
- * 2. Otherwise, crop/pad the impl to match the Figma frame is the authoritative
11
- * target viewport.
7
+ * Dimension mismatch: always scale impl to Figma reference dimensions (never pad
8
+ * with white, which inflates scores on mostly-white components).
9
+ *
10
+ * Content score: if high-variance (photographic/raster) regions are detected in
11
+ * the Figma reference, a second masked comparison excludes those patches and
12
+ * reports contentScore — the fidelity of the non-image UI regions only. This
13
+ * prevents placeholder-image differences from killing the score for otherwise
14
+ * faithful implementations.
12
15
  */
13
16
  export declare function runPixelDiff(figmaPath: string, implPath: string, diffOutputPath: string): Promise<PixelMatchResult | null>;
14
17
  //# sourceMappingURL=pixel-diff.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pixel-diff.d.ts","sourceRoot":"","sources":["../../src/verify/pixel-diff.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA0BnD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA+DlC"}
1
+ {"version":3,"file":"pixel-diff.d.ts","sourceRoot":"","sources":["../../src/verify/pixel-diff.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgGnD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoElC"}
@@ -1,9 +1,12 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ // Patch size (px) and luminance-variance threshold for raster region detection.
4
+ // Photos/textures → variance >> 600; flat UI (solid fills, gradients) → variance < 300.
5
+ const PATCH_SIZE = 16;
6
+ const RASTER_VARIANCE_THRESHOLD = 600;
3
7
  /**
4
- * Nearest-neighbor downscale. Used to normalise a DPR impl screenshot to
5
- * the reference dimensions before pixel-diffing avoids the "every row is
6
- * shifted" artifact that crop/pad produces on retina captures.
8
+ * Nearest-neighbor scale to target dimensions (handles both up and down).
9
+ * Used to align impl screenshot to Figma reference dimensions before diffing.
7
10
  */
8
11
  function scaleDown(src, targetW, targetH, PNG) {
9
12
  const out = new PNG({ width: targetW, height: targetH });
@@ -23,16 +26,69 @@ function scaleDown(src, targetW, targetH, PNG) {
23
26
  }
24
27
  return out;
25
28
  }
29
+ /**
30
+ * Divides the Figma reference into PATCH_SIZE×PATCH_SIZE tiles and marks tiles
31
+ * whose luminance variance exceeds RASTER_VARIANCE_THRESHOLD as "raster" patches.
32
+ * Returns the boolean mask (indexed row-major by patch) plus the patch grid dimensions.
33
+ */
34
+ function buildRasterMask(data, imgWidth, imgHeight, patchSize, threshold) {
35
+ const patchCols = Math.ceil(imgWidth / patchSize);
36
+ const patchRows = Math.ceil(imgHeight / patchSize);
37
+ const mask = [];
38
+ for (let pr = 0; pr < patchRows; pr++) {
39
+ for (let pc = 0; pc < patchCols; pc++) {
40
+ const lums = [];
41
+ for (let py = pr * patchSize; py < Math.min((pr + 1) * patchSize, imgHeight); py++) {
42
+ for (let px = pc * patchSize; px < Math.min((pc + 1) * patchSize, imgWidth); px++) {
43
+ const i = (py * imgWidth + px) * 4;
44
+ lums.push(0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]);
45
+ }
46
+ }
47
+ const mean = lums.reduce((a, b) => a + b, 0) / lums.length;
48
+ const variance = lums.reduce((acc, v) => acc + (v - mean) ** 2, 0) / lums.length;
49
+ mask.push(variance > threshold);
50
+ }
51
+ }
52
+ return { mask, patchCols, patchRows };
53
+ }
54
+ /**
55
+ * Fills masked patches in-place with neutral mid-gray (128,128,128,255).
56
+ * When applied to both images before pixelmatch, masked patches contribute
57
+ * 0 diff pixels, so only non-masked (content) pixels affect the score.
58
+ * Returns the number of pixels filled.
59
+ */
60
+ function applyRasterMask(target, mask, patchSize, patchCols, imgWidth, imgHeight) {
61
+ const patchRows = Math.ceil(imgHeight / patchSize);
62
+ let maskedPixels = 0;
63
+ for (let pr = 0; pr < patchRows; pr++) {
64
+ for (let pc = 0; pc < patchCols; pc++) {
65
+ if (!mask[pr * patchCols + pc])
66
+ continue;
67
+ for (let py = pr * patchSize; py < Math.min((pr + 1) * patchSize, imgHeight); py++) {
68
+ for (let px = pc * patchSize; px < Math.min((pc + 1) * patchSize, imgWidth); px++) {
69
+ const i = (py * imgWidth + px) * 4;
70
+ target[i] = target[i + 1] = target[i + 2] = 128;
71
+ target[i + 3] = 255;
72
+ maskedPixels++;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return maskedPixels;
78
+ }
26
79
  /**
27
80
  * Compares two PNG files pixel-by-pixel using pixelmatch.
28
81
  * Saves an annotated diff image to diffOutputPath.
29
- * Returns the match score (0–1, 1 = identical), or null if comparison fails.
82
+ * Returns the match scores (0–1, 1 = identical), or null if comparison fails.
83
+ *
84
+ * Dimension mismatch: always scale impl to Figma reference dimensions (never pad
85
+ * with white, which inflates scores on mostly-white components).
30
86
  *
31
- * Dimension mismatch handling in order:
32
- * 1. If impl is ≥1.5× larger in both dimensions (e.g. DPR retina capture),
33
- * scale it down to the Figma reference size before comparing.
34
- * 2. Otherwise, crop/pad the impl to match — the Figma frame is the authoritative
35
- * target viewport.
87
+ * Content score: if high-variance (photographic/raster) regions are detected in
88
+ * the Figma reference, a second masked comparison excludes those patches and
89
+ * reports contentScore the fidelity of the non-image UI regions only. This
90
+ * prevents placeholder-image differences from killing the score for otherwise
91
+ * faithful implementations.
36
92
  */
37
93
  export async function runPixelDiff(figmaPath, implPath, diffOutputPath) {
38
94
  try {
@@ -41,49 +97,36 @@ export async function runPixelDiff(figmaPath, implPath, diffOutputPath) {
41
97
  const figmaPng = PNG.sync.read(await fs.readFile(figmaPath));
42
98
  let implPng = PNG.sync.read(await fs.readFile(implPath));
43
99
  const { width, height } = figmaPng;
44
- // Align impl to Figma dimensions so pixelmatch can compare.
100
+ // Align impl to Figma dimensions scale always, never pad with white.
45
101
  if (implPng.width !== width || implPng.height !== height) {
46
- const wRatio = implPng.width / width;
47
- const hRatio = implPng.height / height;
48
- if (wRatio >= 1.5 && hRatio >= 1.5) {
49
- // Oversized capture (e.g. 2× DPR retina screenshot) — scale down so
50
- // every row aligns instead of being cropped at the halfway point.
51
- implPng = scaleDown(implPng, width, height, PNG);
52
- }
53
- else {
54
- // Small mismatch — crop/pad to reference size
55
- const aligned = new PNG({ width, height });
56
- for (let i = 0; i < aligned.data.length; i += 4) {
57
- aligned.data[i] = 255;
58
- aligned.data[i + 1] = 255;
59
- aligned.data[i + 2] = 255;
60
- aligned.data[i + 3] = 255;
61
- }
62
- const copyW = Math.min(width, implPng.width);
63
- const copyH = Math.min(height, implPng.height);
64
- for (let y = 0; y < copyH; y++) {
65
- for (let x = 0; x < copyW; x++) {
66
- const s = (y * implPng.width + x) * 4;
67
- const d = (y * width + x) * 4;
68
- aligned.data[d] = implPng.data[s];
69
- aligned.data[d + 1] = implPng.data[s + 1];
70
- aligned.data[d + 2] = implPng.data[s + 2];
71
- aligned.data[d + 3] = implPng.data[s + 3];
72
- }
73
- }
74
- implPng = aligned;
75
- }
102
+ implPng = scaleDown(implPng, width, height, PNG);
76
103
  }
104
+ // ── Full-frame diff (primary score + diff image) ─────────────────────
77
105
  const diffPng = new PNG({ width, height });
78
106
  const totalPixels = width * height;
79
107
  const diffPixels = pixelmatch(figmaPng.data, implPng.data, diffPng.data, width, height, { threshold: 0.1, includeAA: false });
80
108
  await fs.mkdir(path.dirname(diffOutputPath), { recursive: true });
81
109
  await fs.writeFile(diffOutputPath, PNG.sync.write(diffPng));
82
- return {
83
- score: parseFloat((1 - diffPixels / totalPixels).toFixed(4)),
84
- diffPixels,
85
- totalPixels,
86
- };
110
+ const score = parseFloat((1 - diffPixels / totalPixels).toFixed(4));
111
+ // ── Content-only score (exclude raster image patches) ────────────────
112
+ const { mask, patchCols } = buildRasterMask(figmaPng.data, width, height, PATCH_SIZE, RASTER_VARIANCE_THRESHOLD);
113
+ const rasterPatchCount = mask.filter(Boolean).length;
114
+ if (rasterPatchCount === 0) {
115
+ return { score, diffPixels, totalPixels };
116
+ }
117
+ // At least one raster patch: run a second comparison on masked copies.
118
+ // Both images get the same patches filled with mid-gray → pixelmatch sees
119
+ // 0 diff in those patches → contentDiffPixels counts content regions only.
120
+ const figmaMasked = Buffer.from(figmaPng.data);
121
+ const implMasked = Buffer.from(implPng.data);
122
+ const maskedPixels = applyRasterMask(figmaMasked, mask, PATCH_SIZE, patchCols, width, height);
123
+ applyRasterMask(implMasked, mask, PATCH_SIZE, patchCols, width, height);
124
+ const diffMasked = new PNG({ width, height });
125
+ const contentDiffPixels = pixelmatch(figmaMasked, implMasked, diffMasked.data, width, height, { threshold: 0.1, includeAA: false });
126
+ const contentPixels = totalPixels - maskedPixels;
127
+ const contentScore = parseFloat((contentPixels > 0 ? 1 - contentDiffPixels / contentPixels : 1).toFixed(4));
128
+ const rasterPixelRatio = parseFloat((maskedPixels / totalPixels).toFixed(4));
129
+ return { score, contentScore, rasterPixelRatio, diffPixels, totalPixels };
87
130
  }
88
131
  catch {
89
132
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"pixel-diff.js","sourceRoot":"","sources":["../../src/verify/pixel-diff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pixel-diff.js","sourceRoot":"","sources":["../../src/verify/pixel-diff.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,16 @@ export type VerifyScreenshots = {
6
6
  diff: string | null;
7
7
  };
8
8
  export type PixelMatchResult = {
9
+ /** Full-frame raw pixel match score (0–1, 1 = identical). */
9
10
  score: number;
11
+ /**
12
+ * Score computed only over non-raster regions — excludes high-variance patches
13
+ * (detected photographic images) that are expected to differ (placeholder vs real asset).
14
+ * Present only when at least one raster patch was detected.
15
+ */
16
+ contentScore?: number;
17
+ /** Fraction of total pixels identified as raster image regions (0–1). */
18
+ rasterPixelRatio?: number;
10
19
  diffPixels: number;
11
20
  totalPixels: number;
12
21
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/verify/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,yFAAyF;IACzF,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACzC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,iBAAiB,EAAE,SAAS,GAAG,mBAAmB,GAAG,yBAAyB,CAAC;CAClF,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB,gGAAgG;IAChG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/verify/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,yFAAyF;IACzF,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACzC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC3B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,iBAAiB,EAAE,SAAS,GAAG,mBAAmB,GAAG,yBAAyB,CAAC;CAClF,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB,gGAAgG;IAChG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calibrate-ds/core",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,8 +27,8 @@
27
27
  "pixelmatch": "^6.0.0",
28
28
  "pngjs": "^7.0.0",
29
29
  "zod": "^4.3.6",
30
- "@calibrate-ds/figma-client": "^0.1.39",
31
- "@calibrate-ds/shared-types": "^0.1.39"
30
+ "@calibrate-ds/shared-types": "^0.1.41",
31
+ "@calibrate-ds/figma-client": "^0.1.41"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsc -p tsconfig.json",