@calibrate-ds/core 0.1.27 → 0.1.28

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.
@@ -4,9 +4,11 @@ import type { PixelMatchResult } from "./types.js";
4
4
  * Saves an annotated diff image to diffOutputPath.
5
5
  * Returns the match score (0–1, 1 = identical), or null if comparison fails.
6
6
  *
7
- * If the images have different dimensions the implementation image is cropped
8
- * or padded with transparency to match the Figma reference size this is
9
- * intentional: the Figma frame size is the authoritative target viewport.
7
+ * Dimension mismatch handling in order:
8
+ * 1. If impl is ≥1.5× larger in both dimensions (e.g. 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.
10
12
  */
11
13
  export declare function runPixelDiff(figmaPath: string, implPath: string, diffOutputPath: string): Promise<PixelMatchResult | null>;
12
14
  //# 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;AAEnD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAuDlC"}
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,13 +1,38 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ /**
4
+ * Nearest-neighbor downscale. Used to normalise a 2× DPR impl screenshot to
5
+ * the 1× reference dimensions before pixel-diffing — avoids the "every row is
6
+ * shifted" artifact that crop/pad produces on retina captures.
7
+ */
8
+ function scaleDown(src, targetW, targetH, PNG) {
9
+ const out = new PNG({ width: targetW, height: targetH });
10
+ const xRatio = src.width / targetW;
11
+ const yRatio = src.height / targetH;
12
+ for (let y = 0; y < targetH; y++) {
13
+ for (let x = 0; x < targetW; x++) {
14
+ const sx = Math.floor(x * xRatio);
15
+ const sy = Math.floor(y * yRatio);
16
+ const s = (sy * src.width + sx) * 4;
17
+ const d = (y * targetW + x) * 4;
18
+ out.data[d] = src.data[s];
19
+ out.data[d + 1] = src.data[s + 1];
20
+ out.data[d + 2] = src.data[s + 2];
21
+ out.data[d + 3] = src.data[s + 3];
22
+ }
23
+ }
24
+ return out;
25
+ }
3
26
  /**
4
27
  * Compares two PNG files pixel-by-pixel using pixelmatch.
5
28
  * Saves an annotated diff image to diffOutputPath.
6
29
  * Returns the match score (0–1, 1 = identical), or null if comparison fails.
7
30
  *
8
- * If the images have different dimensions the implementation image is cropped
9
- * or padded with transparency to match the Figma reference size this is
10
- * intentional: the Figma frame size is the authoritative target viewport.
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.
11
36
  */
12
37
  export async function runPixelDiff(figmaPath, implPath, diffOutputPath) {
13
38
  try {
@@ -18,27 +43,36 @@ export async function runPixelDiff(figmaPath, implPath, diffOutputPath) {
18
43
  const { width, height } = figmaPng;
19
44
  // Align impl to Figma dimensions so pixelmatch can compare.
20
45
  if (implPng.width !== width || implPng.height !== height) {
21
- const aligned = new PNG({ width, height });
22
- // Fill with white background (matches typical browser default)
23
- for (let i = 0; i < aligned.data.length; i += 4) {
24
- aligned.data[i] = 255;
25
- aligned.data[i + 1] = 255;
26
- aligned.data[i + 2] = 255;
27
- aligned.data[i + 3] = 255;
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. 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);
28
52
  }
29
- const copyW = Math.min(width, implPng.width);
30
- const copyH = Math.min(height, implPng.height);
31
- for (let y = 0; y < copyH; y++) {
32
- for (let x = 0; x < copyW; x++) {
33
- const s = (y * implPng.width + x) * 4;
34
- const d = (y * width + x) * 4;
35
- aligned.data[d] = implPng.data[s];
36
- aligned.data[d + 1] = implPng.data[s + 1];
37
- aligned.data[d + 2] = implPng.data[s + 2];
38
- aligned.data[d + 3] = implPng.data[s + 3];
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
+ }
39
73
  }
74
+ implPng = aligned;
40
75
  }
41
- implPng = aligned;
42
76
  }
43
77
  const diffPng = new PNG({ width, height });
44
78
  const totalPixels = width * height;
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calibrate-ds/core",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
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/shared-types": "^0.1.27",
31
- "@calibrate-ds/figma-client": "^0.1.27"
30
+ "@calibrate-ds/shared-types": "^0.1.28",
31
+ "@calibrate-ds/figma-client": "^0.1.28"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsc -p tsconfig.json",