devlens-mcp 0.3.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.
Files changed (175) hide show
  1. package/.claude/settings.json +12 -0
  2. package/.claude/settings.local.json +17 -0
  3. package/INSTALLATION_GUIDE.md +354 -0
  4. package/QUICK_START.md +153 -0
  5. package/README.md +354 -0
  6. package/bin/cli.ts +22 -0
  7. package/bin/register.ts +96 -0
  8. package/dist/bin/cli.d.ts +3 -0
  9. package/dist/bin/cli.d.ts.map +1 -0
  10. package/dist/bin/cli.js +20 -0
  11. package/dist/bin/cli.js.map +1 -0
  12. package/dist/bin/register.d.ts +10 -0
  13. package/dist/bin/register.d.ts.map +1 -0
  14. package/dist/bin/register.js +92 -0
  15. package/dist/bin/register.js.map +1 -0
  16. package/dist/src/config/devlens-config.d.ts +92 -0
  17. package/dist/src/config/devlens-config.d.ts.map +1 -0
  18. package/dist/src/config/devlens-config.js +70 -0
  19. package/dist/src/config/devlens-config.js.map +1 -0
  20. package/dist/src/index.d.ts +35 -0
  21. package/dist/src/index.d.ts.map +1 -0
  22. package/dist/src/index.js +8 -0
  23. package/dist/src/index.js.map +1 -0
  24. package/dist/src/metro/cdp-client.d.ts +48 -0
  25. package/dist/src/metro/cdp-client.d.ts.map +1 -0
  26. package/dist/src/metro/cdp-client.js +127 -0
  27. package/dist/src/metro/cdp-client.js.map +1 -0
  28. package/dist/src/metro/log-collector.d.ts +30 -0
  29. package/dist/src/metro/log-collector.d.ts.map +1 -0
  30. package/dist/src/metro/log-collector.js +114 -0
  31. package/dist/src/metro/log-collector.js.map +1 -0
  32. package/dist/src/metro/metro-bridge.d.ts +56 -0
  33. package/dist/src/metro/metro-bridge.d.ts.map +1 -0
  34. package/dist/src/metro/metro-bridge.js +255 -0
  35. package/dist/src/metro/metro-bridge.js.map +1 -0
  36. package/dist/src/metro/network-inspector.d.ts +34 -0
  37. package/dist/src/metro/network-inspector.d.ts.map +1 -0
  38. package/dist/src/metro/network-inspector.js +100 -0
  39. package/dist/src/metro/network-inspector.js.map +1 -0
  40. package/dist/src/platform/android/adb.d.ts +50 -0
  41. package/dist/src/platform/android/adb.d.ts.map +1 -0
  42. package/dist/src/platform/android/adb.js +137 -0
  43. package/dist/src/platform/android/adb.js.map +1 -0
  44. package/dist/src/platform/android/android-device.d.ts +21 -0
  45. package/dist/src/platform/android/android-device.d.ts.map +1 -0
  46. package/dist/src/platform/android/android-device.js +94 -0
  47. package/dist/src/platform/android/android-device.js.map +1 -0
  48. package/dist/src/platform/android/ui-automator.d.ts +17 -0
  49. package/dist/src/platform/android/ui-automator.d.ts.map +1 -0
  50. package/dist/src/platform/android/ui-automator.js +126 -0
  51. package/dist/src/platform/android/ui-automator.js.map +1 -0
  52. package/dist/src/platform/device-manager.d.ts +28 -0
  53. package/dist/src/platform/device-manager.d.ts.map +1 -0
  54. package/dist/src/platform/device-manager.js +185 -0
  55. package/dist/src/platform/device-manager.js.map +1 -0
  56. package/dist/src/platform/device.d.ts +86 -0
  57. package/dist/src/platform/device.d.ts.map +1 -0
  58. package/dist/src/platform/device.js +7 -0
  59. package/dist/src/platform/device.js.map +1 -0
  60. package/dist/src/platform/ios/accessibility.d.ts +17 -0
  61. package/dist/src/platform/ios/accessibility.d.ts.map +1 -0
  62. package/dist/src/platform/ios/accessibility.js +159 -0
  63. package/dist/src/platform/ios/accessibility.js.map +1 -0
  64. package/dist/src/platform/ios/ios-device.d.ts +22 -0
  65. package/dist/src/platform/ios/ios-device.d.ts.map +1 -0
  66. package/dist/src/platform/ios/ios-device.js +97 -0
  67. package/dist/src/platform/ios/ios-device.js.map +1 -0
  68. package/dist/src/platform/ios/simctl.d.ts +54 -0
  69. package/dist/src/platform/ios/simctl.d.ts.map +1 -0
  70. package/dist/src/platform/ios/simctl.js +192 -0
  71. package/dist/src/platform/ios/simctl.js.map +1 -0
  72. package/dist/src/server.d.ts +3 -0
  73. package/dist/src/server.d.ts.map +1 -0
  74. package/dist/src/server.js +176 -0
  75. package/dist/src/server.js.map +1 -0
  76. package/dist/src/snapshot/formatter.d.ts +18 -0
  77. package/dist/src/snapshot/formatter.d.ts.map +1 -0
  78. package/dist/src/snapshot/formatter.js +86 -0
  79. package/dist/src/snapshot/formatter.js.map +1 -0
  80. package/dist/src/snapshot/ref-registry.d.ts +67 -0
  81. package/dist/src/snapshot/ref-registry.d.ts.map +1 -0
  82. package/dist/src/snapshot/ref-registry.js +169 -0
  83. package/dist/src/snapshot/ref-registry.js.map +1 -0
  84. package/dist/src/snapshot/snapshot-differ.d.ts +57 -0
  85. package/dist/src/snapshot/snapshot-differ.d.ts.map +1 -0
  86. package/dist/src/snapshot/snapshot-differ.js +153 -0
  87. package/dist/src/snapshot/snapshot-differ.js.map +1 -0
  88. package/dist/src/tools/app-tools.d.ts +71 -0
  89. package/dist/src/tools/app-tools.d.ts.map +1 -0
  90. package/dist/src/tools/app-tools.js +97 -0
  91. package/dist/src/tools/app-tools.js.map +1 -0
  92. package/dist/src/tools/device-tools.d.ts +53 -0
  93. package/dist/src/tools/device-tools.d.ts.map +1 -0
  94. package/dist/src/tools/device-tools.js +86 -0
  95. package/dist/src/tools/device-tools.js.map +1 -0
  96. package/dist/src/tools/ds-tools.d.ts +65 -0
  97. package/dist/src/tools/ds-tools.d.ts.map +1 -0
  98. package/dist/src/tools/ds-tools.js +314 -0
  99. package/dist/src/tools/ds-tools.js.map +1 -0
  100. package/dist/src/tools/interaction-tools.d.ts +248 -0
  101. package/dist/src/tools/interaction-tools.d.ts.map +1 -0
  102. package/dist/src/tools/interaction-tools.js +391 -0
  103. package/dist/src/tools/interaction-tools.js.map +1 -0
  104. package/dist/src/tools/metro-tools.d.ts +115 -0
  105. package/dist/src/tools/metro-tools.d.ts.map +1 -0
  106. package/dist/src/tools/metro-tools.js +270 -0
  107. package/dist/src/tools/metro-tools.js.map +1 -0
  108. package/dist/src/tools/navigation-tools.d.ts +36 -0
  109. package/dist/src/tools/navigation-tools.d.ts.map +1 -0
  110. package/dist/src/tools/navigation-tools.js +60 -0
  111. package/dist/src/tools/navigation-tools.js.map +1 -0
  112. package/dist/src/tools/screenshot-tools.d.ts +298 -0
  113. package/dist/src/tools/screenshot-tools.d.ts.map +1 -0
  114. package/dist/src/tools/screenshot-tools.js +565 -0
  115. package/dist/src/tools/screenshot-tools.js.map +1 -0
  116. package/dist/src/tools/snapshot-tools.d.ts +161 -0
  117. package/dist/src/tools/snapshot-tools.d.ts.map +1 -0
  118. package/dist/src/tools/snapshot-tools.js +479 -0
  119. package/dist/src/tools/snapshot-tools.js.map +1 -0
  120. package/dist/src/utils/image-preprocess.d.ts +49 -0
  121. package/dist/src/utils/image-preprocess.d.ts.map +1 -0
  122. package/dist/src/utils/image-preprocess.js +322 -0
  123. package/dist/src/utils/image-preprocess.js.map +1 -0
  124. package/dist/src/utils/retry.d.ts +21 -0
  125. package/dist/src/utils/retry.d.ts.map +1 -0
  126. package/dist/src/utils/retry.js +33 -0
  127. package/dist/src/utils/retry.js.map +1 -0
  128. package/dist/src/visual/comparator.d.ts +51 -0
  129. package/dist/src/visual/comparator.d.ts.map +1 -0
  130. package/dist/src/visual/comparator.js +119 -0
  131. package/dist/src/visual/comparator.js.map +1 -0
  132. package/dist/src/visual/layout-analyzer.d.ts +64 -0
  133. package/dist/src/visual/layout-analyzer.d.ts.map +1 -0
  134. package/dist/src/visual/layout-analyzer.js +198 -0
  135. package/dist/src/visual/layout-analyzer.js.map +1 -0
  136. package/dist/src/visual/screenshot.d.ts +17 -0
  137. package/dist/src/visual/screenshot.d.ts.map +1 -0
  138. package/dist/src/visual/screenshot.js +39 -0
  139. package/dist/src/visual/screenshot.js.map +1 -0
  140. package/docs/figma-workflow.md +289 -0
  141. package/docs/setup-guide.md +360 -0
  142. package/docs/tool-reference.md +622 -0
  143. package/package.json +57 -0
  144. package/src/config/devlens-config.ts +76 -0
  145. package/src/index.ts +5 -0
  146. package/src/metro/cdp-client.ts +160 -0
  147. package/src/metro/log-collector.ts +137 -0
  148. package/src/metro/metro-bridge.ts +307 -0
  149. package/src/metro/network-inspector.ts +134 -0
  150. package/src/platform/android/adb.ts +200 -0
  151. package/src/platform/android/android-device.ts +116 -0
  152. package/src/platform/android/ui-automator.ts +141 -0
  153. package/src/platform/device-manager.ts +229 -0
  154. package/src/platform/device.ts +110 -0
  155. package/src/platform/ios/accessibility.ts +189 -0
  156. package/src/platform/ios/ios-device.ts +116 -0
  157. package/src/platform/ios/simctl.ts +244 -0
  158. package/src/server.ts +228 -0
  159. package/src/snapshot/formatter.ts +102 -0
  160. package/src/snapshot/ref-registry.ts +230 -0
  161. package/src/snapshot/snapshot-differ.ts +220 -0
  162. package/src/tools/app-tools.ts +111 -0
  163. package/src/tools/device-tools.ts +96 -0
  164. package/src/tools/ds-tools.ts +395 -0
  165. package/src/tools/interaction-tools.ts +467 -0
  166. package/src/tools/metro-tools.ts +320 -0
  167. package/src/tools/navigation-tools.ts +71 -0
  168. package/src/tools/screenshot-tools.ts +698 -0
  169. package/src/tools/snapshot-tools.ts +585 -0
  170. package/src/utils/image-preprocess.ts +430 -0
  171. package/src/utils/retry.ts +51 -0
  172. package/src/visual/comparator.ts +191 -0
  173. package/src/visual/layout-analyzer.ts +283 -0
  174. package/src/visual/screenshot.ts +49 -0
  175. package/tsconfig.json +20 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Image preprocessing utilities for removing Figma presentation artifacts
3
+ *
4
+ * Handles:
5
+ * - Rounded corners from Figma frame exports
6
+ * - Grey/black backgrounds and borders
7
+ * - Uniform colored bars (top/bottom/sides)
8
+ * - Transparent padding around content
9
+ */
10
+ export interface PreprocessOptions {
11
+ /** Strategy to use for artifact removal */
12
+ strategy?: "auto" | "trim" | "alpha" | "content" | "none";
13
+ /** Threshold for trim strategy (0-255, default 10) */
14
+ trimThreshold?: number;
15
+ /** Whether to apply additional trim after content detection */
16
+ aggressiveTrim?: boolean;
17
+ /** Enable debug logging */
18
+ debug?: boolean;
19
+ }
20
+ export interface PreprocessResult {
21
+ /** Processed image buffer */
22
+ buffer: Buffer;
23
+ /** Strategy that was applied (may include variant info like "auto-content" or "trim") */
24
+ appliedStrategy: string;
25
+ /** Artifacts that were detected and removed */
26
+ artifactsRemoved: string[];
27
+ /** Original dimensions */
28
+ originalDimensions: {
29
+ width: number;
30
+ height: number;
31
+ };
32
+ /** Final dimensions after preprocessing */
33
+ finalDimensions: {
34
+ width: number;
35
+ height: number;
36
+ };
37
+ /** Crop information */
38
+ cropInfo: {
39
+ left: number;
40
+ top: number;
41
+ width: number;
42
+ height: number;
43
+ };
44
+ }
45
+ /**
46
+ * Main preprocessing function - automatically detects and removes Figma artifacts
47
+ */
48
+ export declare function preprocessImage(buffer: Buffer, options?: PreprocessOptions): Promise<PreprocessResult>;
49
+ //# sourceMappingURL=image-preprocess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-preprocess.d.ts","sourceRoot":"","sources":["../../../src/utils/image-preprocess.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1D,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,0BAA0B;IAC1B,kBAAkB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,2CAA2C;IAC3C,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,uBAAuB;IACvB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAkQD;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAgI3B"}
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.preprocessImage = preprocessImage;
7
+ const sharp_1 = __importDefault(require("sharp"));
8
+ const pngjs_1 = require("pngjs");
9
+ /**
10
+ * Analyze image edges to detect uniform borders or bars
11
+ */
12
+ async function analyzeEdges(buffer) {
13
+ const png = pngjs_1.PNG.sync.read(buffer);
14
+ const { width, height } = png;
15
+ // Sample corner pixels (50x50 region in top-left)
16
+ const cornerSize = Math.min(50, Math.floor(width / 4), Math.floor(height / 4));
17
+ const cornerPixels = [];
18
+ for (let y = 0; y < cornerSize; y++) {
19
+ for (let x = 0; x < cornerSize; x++) {
20
+ const idx = (width * y + x) * 4;
21
+ cornerPixels.push({
22
+ r: png.data[idx],
23
+ g: png.data[idx + 1],
24
+ b: png.data[idx + 2],
25
+ a: png.data[idx + 3],
26
+ });
27
+ }
28
+ }
29
+ // Calculate average corner color
30
+ const sum = cornerPixels.reduce((acc, p) => ({
31
+ r: acc.r + p.r,
32
+ g: acc.g + p.g,
33
+ b: acc.b + p.b,
34
+ a: acc.a + p.a,
35
+ }), { r: 0, g: 0, b: 0, a: 0 });
36
+ const count = cornerPixels.length;
37
+ const avgCornerColor = {
38
+ r: Math.round(sum.r / count),
39
+ g: Math.round(sum.g / count),
40
+ b: Math.round(sum.b / count),
41
+ a: Math.round(sum.a / count),
42
+ };
43
+ // Check if corners have uniform color (grey, white, or black)
44
+ const isGrey = Math.abs(avgCornerColor.r - avgCornerColor.g) < 10 &&
45
+ Math.abs(avgCornerColor.g - avgCornerColor.b) < 10;
46
+ const isDark = avgCornerColor.r < 50 && avgCornerColor.g < 50 && avgCornerColor.b < 50;
47
+ const isLight = avgCornerColor.r > 180 && avgCornerColor.g > 180 && avgCornerColor.b > 180;
48
+ return {
49
+ hasUniformBorder: isGrey && (isDark || isLight),
50
+ avgCornerColor,
51
+ isDarkBorder: isGrey && isDark,
52
+ isLightBorder: isGrey && isLight,
53
+ };
54
+ }
55
+ /**
56
+ * Find content bounds by analyzing pixel patterns
57
+ * Detects and excludes:
58
+ * - Dark bars (black backgrounds)
59
+ * - Light grey/white borders
60
+ * - Transparent padding
61
+ */
62
+ async function findContentBounds(buffer) {
63
+ const png = pngjs_1.PNG.sync.read(buffer);
64
+ const { width, height } = png;
65
+ // Track which rows and columns have meaningful content
66
+ const rowHasContent = new Array(height).fill(false);
67
+ const colHasContent = new Array(width).fill(false);
68
+ // Analyze each row
69
+ for (let y = 0; y < height; y++) {
70
+ let contentPixels = 0;
71
+ for (let x = 0; x < width; x++) {
72
+ const idx = (width * y + x) * 4;
73
+ const r = png.data[idx];
74
+ const g = png.data[idx + 1];
75
+ const b = png.data[idx + 2];
76
+ const alpha = png.data[idx + 3];
77
+ // Pixel is "content" if it's not a presentation artifact
78
+ const isDarkBar = r < 30 && g < 30 && b < 30;
79
+ const isLightGreyBorder = r > 180 && g > 180 && b > 180 &&
80
+ Math.abs(r - g) < 5 && Math.abs(g - b) < 5;
81
+ const isTransparent = alpha < 10;
82
+ if (!isDarkBar && !isLightGreyBorder && !isTransparent) {
83
+ contentPixels++;
84
+ }
85
+ }
86
+ // Row has content if more than 10% of pixels are content
87
+ if (contentPixels > width * 0.1) {
88
+ rowHasContent[y] = true;
89
+ }
90
+ }
91
+ // Analyze each column
92
+ for (let x = 0; x < width; x++) {
93
+ let contentPixels = 0;
94
+ for (let y = 0; y < height; y++) {
95
+ const idx = (width * y + x) * 4;
96
+ const r = png.data[idx];
97
+ const g = png.data[idx + 1];
98
+ const b = png.data[idx + 2];
99
+ const alpha = png.data[idx + 3];
100
+ const isDarkBar = r < 30 && g < 30 && b < 30;
101
+ const isLightGreyBorder = r > 180 && g > 180 && b > 180 &&
102
+ Math.abs(r - g) < 5 && Math.abs(g - b) < 5;
103
+ const isTransparent = alpha < 10;
104
+ if (!isDarkBar && !isLightGreyBorder && !isTransparent) {
105
+ contentPixels++;
106
+ }
107
+ }
108
+ if (contentPixels > height * 0.1) {
109
+ colHasContent[x] = true;
110
+ }
111
+ }
112
+ // Find bounding box
113
+ const minY = rowHasContent.indexOf(true);
114
+ const maxY = rowHasContent.lastIndexOf(true);
115
+ const minX = colHasContent.indexOf(true);
116
+ const maxX = colHasContent.lastIndexOf(true);
117
+ if (minY === -1 || minX === -1) {
118
+ return null; // Could not detect content
119
+ }
120
+ return {
121
+ left: minX,
122
+ top: minY,
123
+ width: maxX - minX + 1,
124
+ height: maxY - minY + 1,
125
+ };
126
+ }
127
+ /**
128
+ * Apply trim strategy using Sharp's built-in trim
129
+ */
130
+ async function applyTrimStrategy(buffer, threshold) {
131
+ const originalMeta = await (0, sharp_1.default)(buffer).metadata();
132
+ const result = await (0, sharp_1.default)(buffer)
133
+ .trim({ threshold })
134
+ .toBuffer({ resolveWithObject: true });
135
+ return {
136
+ buffer: result.data,
137
+ removed: {
138
+ top: 0, // Sharp doesn't expose exact trim amounts
139
+ bottom: 0,
140
+ left: 0,
141
+ right: 0,
142
+ },
143
+ };
144
+ }
145
+ /**
146
+ * Apply alpha channel cropping
147
+ */
148
+ async function applyAlphaCrop(buffer) {
149
+ const png = pngjs_1.PNG.sync.read(buffer);
150
+ const { width, height } = png;
151
+ let minX = width, minY = height, maxX = 0, maxY = 0;
152
+ const alphaThreshold = 10;
153
+ for (let y = 0; y < height; y++) {
154
+ for (let x = 0; x < width; x++) {
155
+ const idx = (width * y + x) * 4;
156
+ const alpha = png.data[idx + 3];
157
+ if (alpha > alphaThreshold) {
158
+ minX = Math.min(minX, x);
159
+ minY = Math.min(minY, y);
160
+ maxX = Math.max(maxX, x);
161
+ maxY = Math.max(maxY, y);
162
+ }
163
+ }
164
+ }
165
+ const bounds = {
166
+ left: minX,
167
+ top: minY,
168
+ width: maxX - minX + 1,
169
+ height: maxY - minY + 1,
170
+ };
171
+ // Only crop if we found meaningful bounds
172
+ if (bounds.width < 10 || bounds.height < 10 ||
173
+ bounds.width === width || bounds.height === height) {
174
+ return null;
175
+ }
176
+ const cropped = await (0, sharp_1.default)(buffer)
177
+ .extract(bounds)
178
+ .toBuffer();
179
+ return { buffer: cropped, bounds };
180
+ }
181
+ /**
182
+ * Apply content-based detection and cropping
183
+ */
184
+ async function applyContentCrop(buffer, aggressiveTrim) {
185
+ const bounds = await findContentBounds(buffer);
186
+ if (!bounds || bounds.width < 50 || bounds.height < 50) {
187
+ return null; // Invalid or too small
188
+ }
189
+ let processed = await (0, sharp_1.default)(buffer)
190
+ .extract(bounds)
191
+ .toBuffer();
192
+ // Apply additional trim to clean up edges
193
+ if (aggressiveTrim) {
194
+ try {
195
+ const trimmed = await (0, sharp_1.default)(processed)
196
+ .trim({ threshold: 15 })
197
+ .toBuffer({ resolveWithObject: true });
198
+ processed = trimmed.data;
199
+ }
200
+ catch {
201
+ // Trim failed, use extracted version
202
+ }
203
+ }
204
+ return { buffer: processed, bounds };
205
+ }
206
+ /**
207
+ * Main preprocessing function - automatically detects and removes Figma artifacts
208
+ */
209
+ async function preprocessImage(buffer, options = {}) {
210
+ const { strategy = "auto", trimThreshold = 10, aggressiveTrim = true, debug = false, } = options;
211
+ const originalMeta = await (0, sharp_1.default)(buffer).metadata();
212
+ const originalDimensions = {
213
+ width: originalMeta.width,
214
+ height: originalMeta.height,
215
+ };
216
+ const artifactsRemoved = [];
217
+ let processedBuffer = buffer;
218
+ let appliedStrategy = strategy;
219
+ let cropInfo = {
220
+ left: 0,
221
+ top: 0,
222
+ width: originalDimensions.width,
223
+ height: originalDimensions.height,
224
+ };
225
+ // Strategy: none - return original
226
+ if (strategy === "none") {
227
+ return {
228
+ buffer,
229
+ appliedStrategy: "none",
230
+ artifactsRemoved: [],
231
+ originalDimensions,
232
+ finalDimensions: originalDimensions,
233
+ cropInfo,
234
+ };
235
+ }
236
+ // Strategy: trim - use Sharp's built-in trim
237
+ if (strategy === "trim") {
238
+ const trimResult = await applyTrimStrategy(buffer, trimThreshold);
239
+ processedBuffer = trimResult.buffer;
240
+ artifactsRemoved.push("uniform-borders");
241
+ appliedStrategy = "trim";
242
+ }
243
+ // Strategy: alpha - crop to alpha channel bounds
244
+ else if (strategy === "alpha") {
245
+ const alphaResult = await applyAlphaCrop(buffer);
246
+ if (alphaResult) {
247
+ processedBuffer = alphaResult.buffer;
248
+ cropInfo = alphaResult.bounds;
249
+ artifactsRemoved.push("transparent-padding");
250
+ appliedStrategy = "alpha";
251
+ }
252
+ else {
253
+ appliedStrategy = "alpha-failed-fallback-none";
254
+ }
255
+ }
256
+ // Strategy: content - detect content bounds
257
+ else if (strategy === "content") {
258
+ const contentResult = await applyContentCrop(buffer, aggressiveTrim);
259
+ if (contentResult) {
260
+ processedBuffer = contentResult.buffer;
261
+ cropInfo = contentResult.bounds;
262
+ artifactsRemoved.push("presentation-artifacts");
263
+ appliedStrategy = "content";
264
+ }
265
+ else {
266
+ appliedStrategy = "content-failed-fallback-none";
267
+ }
268
+ }
269
+ // Strategy: auto - try strategies in order
270
+ else if (strategy === "auto") {
271
+ const edgeAnalysis = await analyzeEdges(buffer);
272
+ if (debug) {
273
+ console.log("[preprocess] Edge analysis:", edgeAnalysis);
274
+ }
275
+ // Try content detection first (most comprehensive)
276
+ const contentResult = await applyContentCrop(buffer, aggressiveTrim);
277
+ if (contentResult) {
278
+ processedBuffer = contentResult.buffer;
279
+ cropInfo = contentResult.bounds;
280
+ if (edgeAnalysis.isDarkBorder) {
281
+ artifactsRemoved.push("dark-bars");
282
+ }
283
+ if (edgeAnalysis.isLightBorder) {
284
+ artifactsRemoved.push("light-borders");
285
+ }
286
+ if (!edgeAnalysis.hasUniformBorder) {
287
+ artifactsRemoved.push("presentation-artifacts");
288
+ }
289
+ appliedStrategy = "auto-content";
290
+ }
291
+ else {
292
+ // Fallback to trim
293
+ const trimResult = await applyTrimStrategy(buffer, trimThreshold);
294
+ processedBuffer = trimResult.buffer;
295
+ artifactsRemoved.push("uniform-borders");
296
+ appliedStrategy = "auto-trim";
297
+ }
298
+ }
299
+ const finalMeta = await (0, sharp_1.default)(processedBuffer).metadata();
300
+ const finalDimensions = {
301
+ width: finalMeta.width,
302
+ height: finalMeta.height,
303
+ };
304
+ if (debug) {
305
+ console.log("[preprocess] Result:", {
306
+ appliedStrategy,
307
+ artifactsRemoved,
308
+ originalDimensions,
309
+ finalDimensions,
310
+ cropInfo,
311
+ });
312
+ }
313
+ return {
314
+ buffer: processedBuffer,
315
+ appliedStrategy,
316
+ artifactsRemoved,
317
+ originalDimensions,
318
+ finalDimensions,
319
+ cropInfo,
320
+ };
321
+ }
322
+ //# sourceMappingURL=image-preprocess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-preprocess.js","sourceRoot":"","sources":["../../../src/utils/image-preprocess.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Generic retry utility for wrapping flaky operations.
3
+ * Supports fixed delay and exponential backoff.
4
+ */
5
+ export interface RetryOptions {
6
+ /** Maximum number of attempts (default: 2) */
7
+ maxAttempts?: number;
8
+ /** Delay between retries in ms (default: 500) */
9
+ delayMs?: number;
10
+ /** Use exponential backoff (default: false) */
11
+ backoff?: boolean;
12
+ /** Called on each retry with the attempt number and error */
13
+ onRetry?: (attempt: number, error: Error) => void;
14
+ }
15
+ /**
16
+ * Retry an async function up to `maxAttempts` times.
17
+ * On failure, waits `delayMs` (optionally with exponential backoff) before retrying.
18
+ * Throws the last error if all attempts fail.
19
+ */
20
+ export declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
21
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../src/utils/retry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACnD;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CA0BZ"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Generic retry utility for wrapping flaky operations.
4
+ * Supports fixed delay and exponential backoff.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.retry = retry;
8
+ /**
9
+ * Retry an async function up to `maxAttempts` times.
10
+ * On failure, waits `delayMs` (optionally with exponential backoff) before retrying.
11
+ * Throws the last error if all attempts fail.
12
+ */
13
+ async function retry(fn, options = {}) {
14
+ const { maxAttempts = 2, delayMs = 500, backoff = false, onRetry, } = options;
15
+ let lastError;
16
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
17
+ try {
18
+ return await fn();
19
+ }
20
+ catch (error) {
21
+ lastError = error;
22
+ if (attempt < maxAttempts) {
23
+ onRetry?.(attempt, error);
24
+ const waitMs = backoff
25
+ ? delayMs * Math.pow(2, attempt - 1)
26
+ : delayMs;
27
+ await new Promise((r) => setTimeout(r, waitMs));
28
+ }
29
+ }
30
+ }
31
+ throw lastError;
32
+ }
33
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/utils/retry.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAkBH,sBA6BC;AAlCD;;;;GAIG;AACI,KAAK,UAAU,KAAK,CACzB,EAAoB,EACpB,UAAwB,EAAE;IAE1B,MAAM,EACJ,WAAW,GAAG,CAAC,EACf,OAAO,GAAG,GAAG,EACb,OAAO,GAAG,KAAK,EACf,OAAO,GACR,GAAG,OAAO,CAAC;IAEZ,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,OAAO,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC1B,MAAM,MAAM,GAAG,OAAO;oBACpB,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;oBACpC,CAAC,CAAC,OAAO,CAAC;gBACZ,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Visual comparison engine — compares screenshots against reference images
3
+ * using pixel-level diffing. Returns a similarity score and a diff image.
4
+ */
5
+ export interface ComparisonResult {
6
+ /** Similarity score between 0.0 (completely different) and 1.0 (identical) */
7
+ similarity: number;
8
+ /** Number of different pixels */
9
+ diffPixels: number;
10
+ /** Total pixels compared */
11
+ totalPixels: number;
12
+ /** Path to the diff image (red = differences) */
13
+ diffImagePath: string;
14
+ /** Human-readable summary */
15
+ summary: string;
16
+ }
17
+ export interface PreprocessConfig {
18
+ /** Whether to enable preprocessing */
19
+ enabled: boolean;
20
+ /** Strategy to use for artifact removal */
21
+ strategy?: "auto" | "trim" | "alpha" | "content" | "none";
22
+ /** Threshold for trim strategy (0-255) */
23
+ trimThreshold?: number;
24
+ /** Apply aggressive trimming after content detection */
25
+ aggressiveTrim?: boolean;
26
+ /** Save preprocessed image for debugging */
27
+ saveDebugImage?: boolean;
28
+ }
29
+ export interface ComparisonOptions {
30
+ /** Color difference threshold (0-1). Lower = stricter. Default: 0.1 */
31
+ threshold?: number;
32
+ /**
33
+ * How to handle dimension mismatches:
34
+ * - "fit" (default): resize both to the smaller dimensions
35
+ * - "scale": resize smaller image up to the larger dimensions
36
+ * - "crop": extract the overlapping region from both
37
+ */
38
+ resizeStrategy?: "fit" | "scale" | "crop";
39
+ /**
40
+ * Preprocess reference image to remove Figma artifacts.
41
+ * Can be boolean (use defaults) or config object for fine control.
42
+ * Defaults to false for backward compatibility.
43
+ */
44
+ preprocessReference?: boolean | PreprocessConfig;
45
+ }
46
+ /**
47
+ * Compare a screenshot buffer against a reference image buffer.
48
+ * Both images are resized/cropped to match dimensions before comparison.
49
+ */
50
+ export declare function compareScreenshots(screenshotBuffer: Buffer, referenceBuffer: Buffer, options?: ComparisonOptions): Promise<ComparisonResult>;
51
+ //# sourceMappingURL=comparator.d.ts.map
@@ -0,0 +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;CAClD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAgI3B"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.compareScreenshots = compareScreenshots;
7
+ const promises_1 = require("fs/promises");
8
+ const pngjs_1 = require("pngjs");
9
+ const pixelmatch_1 = __importDefault(require("pixelmatch"));
10
+ const image_preprocess_js_1 = require("../utils/image-preprocess.js");
11
+ /**
12
+ * Compare a screenshot buffer against a reference image buffer.
13
+ * Both images are resized/cropped to match dimensions before comparison.
14
+ */
15
+ async function compareScreenshots(screenshotBuffer, referenceBuffer, options = {}) {
16
+ const { threshold = 0.1, resizeStrategy = "fit", preprocessReference } = options;
17
+ // Preprocess reference image if requested
18
+ let processedReferenceBuffer = referenceBuffer;
19
+ if (preprocessReference) {
20
+ const preprocessConfig = typeof preprocessReference === "boolean"
21
+ ? { strategy: "auto" }
22
+ : {
23
+ strategy: preprocessReference.strategy,
24
+ trimThreshold: preprocessReference.trimThreshold,
25
+ aggressiveTrim: preprocessReference.aggressiveTrim,
26
+ };
27
+ const preprocessResult = await (0, image_preprocess_js_1.preprocessImage)(referenceBuffer, preprocessConfig);
28
+ processedReferenceBuffer = preprocessResult.buffer;
29
+ // Save debug image if requested
30
+ if (typeof preprocessReference === "object" && preprocessReference.saveDebugImage) {
31
+ const debugPath = `/tmp/devlens-preprocessed-${Date.now()}.png`;
32
+ await (0, promises_1.writeFile)(debugPath, processedReferenceBuffer);
33
+ console.log(`[comparator] Preprocessed reference saved to: ${debugPath}`);
34
+ console.log(`[comparator] Removed artifacts: ${preprocessResult.artifactsRemoved.join(", ") || "none"}`);
35
+ console.log(`[comparator] Strategy used: ${preprocessResult.appliedStrategy}`);
36
+ }
37
+ }
38
+ // Decode both PNGs
39
+ const screenshot = pngjs_1.PNG.sync.read(screenshotBuffer);
40
+ const reference = pngjs_1.PNG.sync.read(processedReferenceBuffer);
41
+ // Resize if dimensions don't match
42
+ let img1 = screenshot;
43
+ let img2 = reference;
44
+ if (screenshot.width !== reference.width ||
45
+ screenshot.height !== reference.height) {
46
+ const sharp = (await import("sharp")).default;
47
+ if (resizeStrategy === "crop") {
48
+ // Extract the overlapping region from both images
49
+ const targetWidth = Math.min(screenshot.width, reference.width);
50
+ const targetHeight = Math.min(screenshot.height, reference.height);
51
+ const cropped1 = await sharp(screenshotBuffer)
52
+ .extract({ left: 0, top: 0, width: targetWidth, height: targetHeight })
53
+ .png()
54
+ .toBuffer();
55
+ const cropped2 = await sharp(processedReferenceBuffer)
56
+ .extract({ left: 0, top: 0, width: targetWidth, height: targetHeight })
57
+ .png()
58
+ .toBuffer();
59
+ img1 = pngjs_1.PNG.sync.read(cropped1);
60
+ img2 = pngjs_1.PNG.sync.read(cropped2);
61
+ }
62
+ else {
63
+ // "fit" = resize to smaller dimensions, "scale" = resize to larger
64
+ const targetWidth = resizeStrategy === "scale"
65
+ ? Math.max(screenshot.width, reference.width)
66
+ : Math.min(screenshot.width, reference.width);
67
+ const targetHeight = resizeStrategy === "scale"
68
+ ? Math.max(screenshot.height, reference.height)
69
+ : Math.min(screenshot.height, reference.height);
70
+ const resized1 = await sharp(screenshotBuffer)
71
+ .resize(targetWidth, targetHeight, { fit: "fill" })
72
+ .png()
73
+ .toBuffer();
74
+ const resized2 = await sharp(processedReferenceBuffer)
75
+ .resize(targetWidth, targetHeight, { fit: "fill" })
76
+ .png()
77
+ .toBuffer();
78
+ img1 = pngjs_1.PNG.sync.read(resized1);
79
+ img2 = pngjs_1.PNG.sync.read(resized2);
80
+ }
81
+ }
82
+ // Create diff image
83
+ const { width, height } = img1;
84
+ 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
+ const totalPixels = width * height;
91
+ const similarity = 1 - diffPixels / totalPixels;
92
+ // Save diff image
93
+ const diffImagePath = `/tmp/devlens-diff-${Date.now()}.png`;
94
+ const diffBuffer = pngjs_1.PNG.sync.write(diff);
95
+ await (0, promises_1.writeFile)(diffImagePath, diffBuffer);
96
+ // Generate summary
97
+ const percentage = (similarity * 100).toFixed(1);
98
+ let summary;
99
+ if (similarity >= 0.95) {
100
+ summary = `Excellent match (${percentage}%). The implementation closely matches the design.`;
101
+ }
102
+ else if (similarity >= 0.85) {
103
+ summary = `Good match (${percentage}%). Minor differences detected — check spacing, colors, or small layout shifts.`;
104
+ }
105
+ else if (similarity >= 0.7) {
106
+ summary = `Partial match (${percentage}%). Noticeable differences — review the diff image for layout or styling issues.`;
107
+ }
108
+ else {
109
+ summary = `Low match (${percentage}%). Significant differences — the layout may need substantial changes.`;
110
+ }
111
+ return {
112
+ similarity,
113
+ diffPixels,
114
+ totalPixels,
115
+ diffImagePath,
116
+ summary,
117
+ };
118
+ }
119
+ //# sourceMappingURL=comparator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparator.js","sourceRoot":"","sources":["../../../src/visual/comparator.ts"],"names":[],"mappings":"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"}