devlens-mcp 0.3.0 → 0.3.1

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 (162) hide show
  1. package/README.md +10 -21
  2. package/docs/setup-guide.md +11 -31
  3. package/package.json +1 -1
  4. package/.claude/settings.json +0 -12
  5. package/.claude/settings.local.json +0 -17
  6. package/bin/cli.ts +0 -22
  7. package/bin/register.ts +0 -96
  8. package/dist/src/config/devlens-config.d.ts +0 -92
  9. package/dist/src/config/devlens-config.d.ts.map +0 -1
  10. package/dist/src/config/devlens-config.js +0 -70
  11. package/dist/src/config/devlens-config.js.map +0 -1
  12. package/dist/src/index.d.ts +0 -35
  13. package/dist/src/index.d.ts.map +0 -1
  14. package/dist/src/index.js +0 -8
  15. package/dist/src/index.js.map +0 -1
  16. package/dist/src/metro/cdp-client.d.ts +0 -48
  17. package/dist/src/metro/cdp-client.d.ts.map +0 -1
  18. package/dist/src/metro/cdp-client.js +0 -127
  19. package/dist/src/metro/cdp-client.js.map +0 -1
  20. package/dist/src/metro/log-collector.d.ts +0 -30
  21. package/dist/src/metro/log-collector.d.ts.map +0 -1
  22. package/dist/src/metro/log-collector.js +0 -114
  23. package/dist/src/metro/log-collector.js.map +0 -1
  24. package/dist/src/metro/metro-bridge.d.ts +0 -56
  25. package/dist/src/metro/metro-bridge.d.ts.map +0 -1
  26. package/dist/src/metro/metro-bridge.js +0 -255
  27. package/dist/src/metro/metro-bridge.js.map +0 -1
  28. package/dist/src/metro/network-inspector.d.ts +0 -34
  29. package/dist/src/metro/network-inspector.d.ts.map +0 -1
  30. package/dist/src/metro/network-inspector.js +0 -100
  31. package/dist/src/metro/network-inspector.js.map +0 -1
  32. package/dist/src/platform/android/adb.d.ts +0 -50
  33. package/dist/src/platform/android/adb.d.ts.map +0 -1
  34. package/dist/src/platform/android/adb.js +0 -137
  35. package/dist/src/platform/android/adb.js.map +0 -1
  36. package/dist/src/platform/android/android-device.d.ts +0 -21
  37. package/dist/src/platform/android/android-device.d.ts.map +0 -1
  38. package/dist/src/platform/android/android-device.js +0 -94
  39. package/dist/src/platform/android/android-device.js.map +0 -1
  40. package/dist/src/platform/android/ui-automator.d.ts +0 -17
  41. package/dist/src/platform/android/ui-automator.d.ts.map +0 -1
  42. package/dist/src/platform/android/ui-automator.js +0 -126
  43. package/dist/src/platform/android/ui-automator.js.map +0 -1
  44. package/dist/src/platform/device-manager.d.ts +0 -28
  45. package/dist/src/platform/device-manager.d.ts.map +0 -1
  46. package/dist/src/platform/device-manager.js +0 -185
  47. package/dist/src/platform/device-manager.js.map +0 -1
  48. package/dist/src/platform/device.d.ts +0 -86
  49. package/dist/src/platform/device.d.ts.map +0 -1
  50. package/dist/src/platform/device.js +0 -7
  51. package/dist/src/platform/device.js.map +0 -1
  52. package/dist/src/platform/ios/accessibility.d.ts +0 -17
  53. package/dist/src/platform/ios/accessibility.d.ts.map +0 -1
  54. package/dist/src/platform/ios/accessibility.js +0 -159
  55. package/dist/src/platform/ios/accessibility.js.map +0 -1
  56. package/dist/src/platform/ios/ios-device.d.ts +0 -22
  57. package/dist/src/platform/ios/ios-device.d.ts.map +0 -1
  58. package/dist/src/platform/ios/ios-device.js +0 -97
  59. package/dist/src/platform/ios/ios-device.js.map +0 -1
  60. package/dist/src/platform/ios/simctl.d.ts +0 -54
  61. package/dist/src/platform/ios/simctl.d.ts.map +0 -1
  62. package/dist/src/platform/ios/simctl.js +0 -192
  63. package/dist/src/platform/ios/simctl.js.map +0 -1
  64. package/dist/src/server.d.ts +0 -3
  65. package/dist/src/server.d.ts.map +0 -1
  66. package/dist/src/server.js +0 -176
  67. package/dist/src/server.js.map +0 -1
  68. package/dist/src/snapshot/formatter.d.ts +0 -18
  69. package/dist/src/snapshot/formatter.d.ts.map +0 -1
  70. package/dist/src/snapshot/formatter.js +0 -86
  71. package/dist/src/snapshot/formatter.js.map +0 -1
  72. package/dist/src/snapshot/ref-registry.d.ts +0 -67
  73. package/dist/src/snapshot/ref-registry.d.ts.map +0 -1
  74. package/dist/src/snapshot/ref-registry.js +0 -169
  75. package/dist/src/snapshot/ref-registry.js.map +0 -1
  76. package/dist/src/snapshot/snapshot-differ.d.ts +0 -57
  77. package/dist/src/snapshot/snapshot-differ.d.ts.map +0 -1
  78. package/dist/src/snapshot/snapshot-differ.js +0 -153
  79. package/dist/src/snapshot/snapshot-differ.js.map +0 -1
  80. package/dist/src/tools/app-tools.d.ts +0 -71
  81. package/dist/src/tools/app-tools.d.ts.map +0 -1
  82. package/dist/src/tools/app-tools.js +0 -97
  83. package/dist/src/tools/app-tools.js.map +0 -1
  84. package/dist/src/tools/device-tools.d.ts +0 -53
  85. package/dist/src/tools/device-tools.d.ts.map +0 -1
  86. package/dist/src/tools/device-tools.js +0 -86
  87. package/dist/src/tools/device-tools.js.map +0 -1
  88. package/dist/src/tools/ds-tools.d.ts +0 -65
  89. package/dist/src/tools/ds-tools.d.ts.map +0 -1
  90. package/dist/src/tools/ds-tools.js +0 -314
  91. package/dist/src/tools/ds-tools.js.map +0 -1
  92. package/dist/src/tools/interaction-tools.d.ts +0 -248
  93. package/dist/src/tools/interaction-tools.d.ts.map +0 -1
  94. package/dist/src/tools/interaction-tools.js +0 -391
  95. package/dist/src/tools/interaction-tools.js.map +0 -1
  96. package/dist/src/tools/metro-tools.d.ts +0 -115
  97. package/dist/src/tools/metro-tools.d.ts.map +0 -1
  98. package/dist/src/tools/metro-tools.js +0 -270
  99. package/dist/src/tools/metro-tools.js.map +0 -1
  100. package/dist/src/tools/navigation-tools.d.ts +0 -36
  101. package/dist/src/tools/navigation-tools.d.ts.map +0 -1
  102. package/dist/src/tools/navigation-tools.js +0 -60
  103. package/dist/src/tools/navigation-tools.js.map +0 -1
  104. package/dist/src/tools/screenshot-tools.d.ts +0 -298
  105. package/dist/src/tools/screenshot-tools.d.ts.map +0 -1
  106. package/dist/src/tools/screenshot-tools.js +0 -565
  107. package/dist/src/tools/screenshot-tools.js.map +0 -1
  108. package/dist/src/tools/snapshot-tools.d.ts +0 -161
  109. package/dist/src/tools/snapshot-tools.d.ts.map +0 -1
  110. package/dist/src/tools/snapshot-tools.js +0 -479
  111. package/dist/src/tools/snapshot-tools.js.map +0 -1
  112. package/dist/src/utils/image-preprocess.d.ts +0 -49
  113. package/dist/src/utils/image-preprocess.d.ts.map +0 -1
  114. package/dist/src/utils/image-preprocess.js +0 -322
  115. package/dist/src/utils/image-preprocess.js.map +0 -1
  116. package/dist/src/utils/retry.d.ts +0 -21
  117. package/dist/src/utils/retry.d.ts.map +0 -1
  118. package/dist/src/utils/retry.js +0 -33
  119. package/dist/src/utils/retry.js.map +0 -1
  120. package/dist/src/visual/comparator.d.ts +0 -51
  121. package/dist/src/visual/comparator.d.ts.map +0 -1
  122. package/dist/src/visual/comparator.js +0 -119
  123. package/dist/src/visual/comparator.js.map +0 -1
  124. package/dist/src/visual/layout-analyzer.d.ts +0 -64
  125. package/dist/src/visual/layout-analyzer.d.ts.map +0 -1
  126. package/dist/src/visual/layout-analyzer.js +0 -198
  127. package/dist/src/visual/layout-analyzer.js.map +0 -1
  128. package/dist/src/visual/screenshot.d.ts +0 -17
  129. package/dist/src/visual/screenshot.d.ts.map +0 -1
  130. package/dist/src/visual/screenshot.js +0 -39
  131. package/dist/src/visual/screenshot.js.map +0 -1
  132. package/src/config/devlens-config.ts +0 -76
  133. package/src/index.ts +0 -5
  134. package/src/metro/cdp-client.ts +0 -160
  135. package/src/metro/log-collector.ts +0 -137
  136. package/src/metro/metro-bridge.ts +0 -307
  137. package/src/metro/network-inspector.ts +0 -134
  138. package/src/platform/android/adb.ts +0 -200
  139. package/src/platform/android/android-device.ts +0 -116
  140. package/src/platform/android/ui-automator.ts +0 -141
  141. package/src/platform/device-manager.ts +0 -229
  142. package/src/platform/device.ts +0 -110
  143. package/src/platform/ios/accessibility.ts +0 -189
  144. package/src/platform/ios/ios-device.ts +0 -116
  145. package/src/platform/ios/simctl.ts +0 -244
  146. package/src/server.ts +0 -228
  147. package/src/snapshot/formatter.ts +0 -102
  148. package/src/snapshot/ref-registry.ts +0 -230
  149. package/src/snapshot/snapshot-differ.ts +0 -220
  150. package/src/tools/app-tools.ts +0 -111
  151. package/src/tools/device-tools.ts +0 -96
  152. package/src/tools/ds-tools.ts +0 -395
  153. package/src/tools/interaction-tools.ts +0 -467
  154. package/src/tools/metro-tools.ts +0 -320
  155. package/src/tools/navigation-tools.ts +0 -71
  156. package/src/tools/screenshot-tools.ts +0 -698
  157. package/src/tools/snapshot-tools.ts +0 -585
  158. package/src/utils/image-preprocess.ts +0 -430
  159. package/src/utils/retry.ts +0 -51
  160. package/src/visual/comparator.ts +0 -191
  161. package/src/visual/layout-analyzer.ts +0 -283
  162. package/src/visual/screenshot.ts +0 -49
@@ -1,64 +0,0 @@
1
- import type { RefEntry } from "../snapshot/ref-registry.js";
2
- /**
3
- * Layout-aware comparison engine.
4
- *
5
- * Uses the accessibility tree (via RefRegistry) to identify semantic UI regions,
6
- * scales those regions from device coordinate space into Figma image space,
7
- * and compares each region independently via pixelmatch.
8
- *
9
- * This gives per-element similarity scores ("Header: 92%, ChatInput: 44%")
10
- * instead of a single misleading global pixel-diff percentage.
11
- */
12
- export interface RegionComparisonResult {
13
- ref: string;
14
- elementType: string;
15
- text?: string;
16
- label?: string;
17
- /** Element bounds in device pixel space */
18
- deviceBounds: {
19
- left: number;
20
- top: number;
21
- width: number;
22
- height: number;
23
- };
24
- /** Corresponding bounds in Figma image space (after scaling) */
25
- figmaBounds: {
26
- left: number;
27
- top: number;
28
- width: number;
29
- height: number;
30
- };
31
- similarity: number;
32
- diffPixels: number;
33
- totalPixels: number;
34
- verdict: "excellent" | "good" | "partial" | "poor";
35
- }
36
- export interface LayoutReport {
37
- regions: RegionComparisonResult[];
38
- /** Weighted average similarity across all analyzed regions */
39
- overallSimilarity: number;
40
- /** Number of refs skipped (too small, out of bounds, crop failed) */
41
- skippedRefs: number;
42
- deviceDimensions: {
43
- width: number;
44
- height: number;
45
- };
46
- figmaDimensions: {
47
- width: number;
48
- height: number;
49
- };
50
- }
51
- /**
52
- * Core function: for each ref with sufficient area, scale its device bounds
53
- * into Figma image space, crop both images, and compare independently.
54
- *
55
- * @param deviceBuffer Full-screen device screenshot PNG
56
- * @param figmaBuffer Preprocessed Figma image PNG (artifacts already removed)
57
- * @param refs All RefEntry objects from RefRegistry.getAllRefs()
58
- * @param deviceW Device screen width in pixels
59
- * @param deviceH Device screen height in pixels
60
- */
61
- export declare function buildLayoutReport(deviceBuffer: Buffer, figmaBuffer: Buffer, refs: RefEntry[], deviceW: number, deviceH: number): Promise<LayoutReport>;
62
- /** Format layout report as human-readable text for MCP tool output */
63
- export declare function formatLayoutReport(report: LayoutReport): string;
64
- //# sourceMappingURL=layout-analyzer.d.ts.map
@@ -1 +0,0 @@
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;AA6FD;;;;;;;;;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"}
@@ -1,198 +0,0 @@
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.buildLayoutReport = buildLayoutReport;
7
- exports.formatLayoutReport = formatLayoutReport;
8
- const pixelmatch_1 = __importDefault(require("pixelmatch"));
9
- /** Minimum element area (px²) for inclusion in layout analysis */
10
- const MIN_REGION_AREA = 3000;
11
- /** Color difference threshold for pixelmatch (matches global comparator default) */
12
- const REGION_THRESHOLD = 0.1;
13
- function verdictFromSimilarity(s) {
14
- if (s >= 0.92)
15
- return "excellent";
16
- if (s >= 0.80)
17
- return "good";
18
- if (s >= 0.60)
19
- return "partial";
20
- return "poor";
21
- }
22
- /** Scale device-space element bounds into Figma image pixel space */
23
- function scaleToFigmaSpace(bounds, deviceW, deviceH, figmaW, figmaH) {
24
- const scaleX = figmaW / deviceW;
25
- const scaleY = figmaH / deviceH;
26
- return {
27
- left: Math.round(bounds.x * scaleX),
28
- top: Math.round(bounds.y * scaleY),
29
- width: Math.max(1, Math.round(bounds.width * scaleX)),
30
- height: Math.max(1, Math.round(bounds.height * scaleY)),
31
- };
32
- }
33
- /** Clamp bounds to image dimensions. Returns null if there's no overlap. */
34
- function clampBounds(b, maxW, maxH) {
35
- const left = Math.max(0, b.left);
36
- const top = Math.max(0, b.top);
37
- const right = Math.min(maxW, b.left + b.width);
38
- const bottom = Math.min(maxH, b.top + b.height);
39
- if (right <= left || bottom <= top)
40
- return null;
41
- return { left, top, width: right - left, height: bottom - top };
42
- }
43
- /**
44
- * Crop a PNG buffer to a region and return raw RGBA bytes + dimensions.
45
- * Uses sharp's raw() output so pixelmatch can consume it directly
46
- * without a PNG encode/decode round-trip.
47
- */
48
- async function cropToRaw(imageBuffer, bounds) {
49
- try {
50
- const sharp = (await import("sharp")).default;
51
- const result = await sharp(imageBuffer)
52
- .extract({ left: bounds.left, top: bounds.top, width: bounds.width, height: bounds.height })
53
- .ensureAlpha()
54
- .raw()
55
- .toBuffer({ resolveWithObject: true });
56
- return { data: result.data, width: result.info.width, height: result.info.height };
57
- }
58
- catch {
59
- return null;
60
- }
61
- }
62
- /**
63
- * Resize raw RGBA bytes to target dimensions.
64
- * Used to match device crop and Figma crop pixel dimensions before pixelmatch.
65
- */
66
- async function resizeRaw(raw, targetW, targetH) {
67
- if (raw.width === targetW && raw.height === targetH)
68
- return raw;
69
- try {
70
- const sharp = (await import("sharp")).default;
71
- const result = await sharp(raw.data, {
72
- raw: { width: raw.width, height: raw.height, channels: 4 },
73
- })
74
- .resize(targetW, targetH, { fit: "fill" })
75
- .raw()
76
- .toBuffer({ resolveWithObject: true });
77
- return { data: result.data, width: result.info.width, height: result.info.height };
78
- }
79
- catch {
80
- return null;
81
- }
82
- }
83
- /**
84
- * Core function: for each ref with sufficient area, scale its device bounds
85
- * into Figma image space, crop both images, and compare independently.
86
- *
87
- * @param deviceBuffer Full-screen device screenshot PNG
88
- * @param figmaBuffer Preprocessed Figma image PNG (artifacts already removed)
89
- * @param refs All RefEntry objects from RefRegistry.getAllRefs()
90
- * @param deviceW Device screen width in pixels
91
- * @param deviceH Device screen height in pixels
92
- */
93
- async function buildLayoutReport(deviceBuffer, figmaBuffer, refs, deviceW, deviceH) {
94
- const sharp = (await import("sharp")).default;
95
- const figmaMeta = await sharp(figmaBuffer).metadata();
96
- const figmaW = figmaMeta.width;
97
- const figmaH = figmaMeta.height;
98
- const regions = [];
99
- let skippedRefs = 0;
100
- for (const entry of refs) {
101
- const { bounds } = entry;
102
- const area = bounds.width * bounds.height;
103
- if (area < MIN_REGION_AREA) {
104
- skippedRefs++;
105
- continue;
106
- }
107
- const deviceBounds = { left: bounds.x, top: bounds.y, width: bounds.width, height: bounds.height };
108
- const figmaBounds = scaleToFigmaSpace(bounds, deviceW, deviceH, figmaW, figmaH);
109
- const clampedDevice = clampBounds(deviceBounds, deviceW, deviceH);
110
- const clampedFigma = clampBounds(figmaBounds, figmaW, figmaH);
111
- if (!clampedDevice || !clampedFigma) {
112
- skippedRefs++;
113
- continue;
114
- }
115
- // Crop both in parallel
116
- const [deviceCrop, figmaCrop] = await Promise.all([
117
- cropToRaw(deviceBuffer, clampedDevice),
118
- cropToRaw(figmaBuffer, clampedFigma),
119
- ]);
120
- if (!deviceCrop || !figmaCrop) {
121
- skippedRefs++;
122
- continue;
123
- }
124
- // Resize Figma crop to exactly match device crop dimensions
125
- const figmaResized = await resizeRaw(figmaCrop, deviceCrop.width, deviceCrop.height);
126
- if (!figmaResized) {
127
- skippedRefs++;
128
- continue;
129
- }
130
- const totalPixels = deviceCrop.width * deviceCrop.height;
131
- const diff = Buffer.alloc(totalPixels * 4);
132
- const diffPixels = (0, pixelmatch_1.default)(deviceCrop.data, figmaResized.data, diff, deviceCrop.width, deviceCrop.height, { threshold: REGION_THRESHOLD, includeAA: false });
133
- const similarity = 1 - diffPixels / totalPixels;
134
- regions.push({
135
- ref: entry.ref,
136
- elementType: entry.node.type,
137
- text: entry.node.text,
138
- label: entry.node.label,
139
- deviceBounds,
140
- figmaBounds,
141
- similarity,
142
- diffPixels,
143
- totalPixels,
144
- verdict: verdictFromSimilarity(similarity),
145
- });
146
- }
147
- // Weighted average by region area
148
- const totalWeight = regions.reduce((s, r) => s + r.totalPixels, 0);
149
- const overallSimilarity = totalWeight > 0
150
- ? regions.reduce((s, r) => s + r.similarity * r.totalPixels, 0) / totalWeight
151
- : 0;
152
- return {
153
- regions,
154
- overallSimilarity,
155
- skippedRefs,
156
- deviceDimensions: { width: deviceW, height: deviceH },
157
- figmaDimensions: { width: figmaW, height: figmaH },
158
- };
159
- }
160
- /** Format layout report as human-readable text for MCP tool output */
161
- function formatLayoutReport(report) {
162
- const scaleX = (report.deviceDimensions.width / report.figmaDimensions.width).toFixed(2);
163
- const scaleY = (report.deviceDimensions.height / report.figmaDimensions.height).toFixed(2);
164
- const lines = [
165
- "--- Layout Region Analysis ---",
166
- `Device: ${report.deviceDimensions.width}x${report.deviceDimensions.height}px`,
167
- `Figma: ${report.figmaDimensions.width}x${report.figmaDimensions.height}px`,
168
- `Scale: ${scaleX}x (horizontal) ${scaleY}x (vertical)`,
169
- `Regions: ${report.regions.length} analyzed, ${report.skippedRefs} too small to compare`,
170
- "",
171
- ];
172
- if (report.regions.length === 0) {
173
- lines.push("No regions to analyze. Run devlens_snapshot first to populate element refs.");
174
- return lines.join("\n");
175
- }
176
- // Sort ascending by similarity — problem areas at the bottom
177
- const sorted = [...report.regions].sort((a, b) => a.similarity - b.similarity);
178
- for (const r of sorted) {
179
- const icon = r.verdict === "excellent" ? "✓"
180
- : r.verdict === "good" ? "~"
181
- : r.verdict === "partial" ? "!"
182
- : "X";
183
- const pct = (r.similarity * 100).toFixed(1);
184
- const label = r.text
185
- ? `"${r.text.slice(0, 35)}"`
186
- : r.label
187
- ? `label="${r.label.slice(0, 35)}"`
188
- : r.elementType;
189
- const suffix = (r.verdict === "poor" || r.verdict === "partial")
190
- ? ` <- investigate (${r.diffPixels.toLocaleString()} diff pixels)`
191
- : "";
192
- lines.push(` [${icon}] [${r.ref}] ${label}: ${pct}%${suffix}`);
193
- }
194
- lines.push("");
195
- lines.push(`Overall (area-weighted): ${(report.overallSimilarity * 100).toFixed(1)}%`);
196
- return lines.join("\n");
197
- }
198
- //# sourceMappingURL=layout-analyzer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"layout-analyzer.js","sourceRoot":"","sources":["../../../src/visual/layout-analyzer.ts"],"names":[],"mappings":"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"}
@@ -1,17 +0,0 @@
1
- import type { Device } from "../platform/device.js";
2
- import type { Bounds } from "../platform/device.js";
3
- /**
4
- * Screenshot utilities — capture, encode, crop, and save screenshots.
5
- */
6
- /** Take a screenshot and return it as base64-encoded PNG */
7
- export declare function captureScreenshot(device: Device): Promise<{
8
- buffer: Buffer;
9
- base64: string;
10
- }>;
11
- /** Save a screenshot buffer to disk */
12
- export declare function saveScreenshot(buffer: Buffer, filePath: string): Promise<void>;
13
- /** Crop a screenshot to a specific element's bounds */
14
- export declare function cropToElement(screenshotBuffer: Buffer, bounds: Bounds): Promise<Buffer>;
15
- /** Generate a default screenshot filename with timestamp */
16
- export declare function defaultScreenshotPath(): string;
17
- //# sourceMappingURL=screenshot.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/visual/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;GAEG;AAEH,4DAA4D;AAC5D,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAID;AAED,uCAAuC;AACvC,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,uDAAuD;AACvD,wBAAsB,aAAa,CACjC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,4DAA4D;AAC5D,wBAAgB,qBAAqB,IAAI,MAAM,CAG9C"}
@@ -1,39 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.captureScreenshot = captureScreenshot;
4
- exports.saveScreenshot = saveScreenshot;
5
- exports.cropToElement = cropToElement;
6
- exports.defaultScreenshotPath = defaultScreenshotPath;
7
- const promises_1 = require("fs/promises");
8
- /**
9
- * Screenshot utilities — capture, encode, crop, and save screenshots.
10
- */
11
- /** Take a screenshot and return it as base64-encoded PNG */
12
- async function captureScreenshot(device) {
13
- const buffer = await device.takeScreenshot();
14
- const base64 = buffer.toString("base64");
15
- return { buffer, base64 };
16
- }
17
- /** Save a screenshot buffer to disk */
18
- async function saveScreenshot(buffer, filePath) {
19
- await (0, promises_1.writeFile)(filePath, buffer);
20
- }
21
- /** Crop a screenshot to a specific element's bounds */
22
- async function cropToElement(screenshotBuffer, bounds) {
23
- const sharp = (await import("sharp")).default;
24
- return sharp(screenshotBuffer)
25
- .extract({
26
- left: Math.max(0, Math.round(bounds.x)),
27
- top: Math.max(0, Math.round(bounds.y)),
28
- width: Math.max(1, Math.round(bounds.width)),
29
- height: Math.max(1, Math.round(bounds.height)),
30
- })
31
- .png()
32
- .toBuffer();
33
- }
34
- /** Generate a default screenshot filename with timestamp */
35
- function defaultScreenshotPath() {
36
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
37
- return `/tmp/devlens-screenshot-${timestamp}.png`;
38
- }
39
- //# sourceMappingURL=screenshot.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/visual/screenshot.ts"],"names":[],"mappings":";;AASA,8CAOC;AAGD,wCAKC;AAGD,sCAeC;AAGD,sDAGC;AAhDD,0CAAwC;AAIxC;;GAEG;AAEH,4DAA4D;AACrD,KAAK,UAAU,iBAAiB,CAAC,MAAc;IAIpD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,uCAAuC;AAChC,KAAK,UAAU,cAAc,CAClC,MAAc,EACd,QAAgB;IAEhB,MAAM,IAAA,oBAAS,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,uDAAuD;AAChD,KAAK,UAAU,aAAa,CACjC,gBAAwB,EACxB,MAAc;IAEd,MAAM,KAAK,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAE9C,OAAO,KAAK,CAAC,gBAAgB,CAAC;SAC3B,OAAO,CAAC;QACP,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACtC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;KAC/C,CAAC;SACD,GAAG,EAAE;SACL,QAAQ,EAAE,CAAC;AAChB,CAAC;AAED,4DAA4D;AAC5D,SAAgB,qBAAqB;IACnC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,OAAO,2BAA2B,SAAS,MAAM,CAAC;AACpD,CAAC"}
@@ -1,76 +0,0 @@
1
- import { z } from "zod";
2
- import { readFile } from "fs/promises";
3
- import { resolve } from "path";
4
-
5
- /**
6
- * DevLens configuration file support.
7
- *
8
- * Loaded from (in priority order):
9
- * 1. Path pointed to by DEVLENS_CONFIG env var
10
- * 2. ./devlens.config.json in CWD
11
- *
12
- * Place devlens.config.json in your consumer app root and point to it
13
- * via the MCP env block:
14
- *
15
- * "env": {
16
- * "METRO_PORT": "8081",
17
- * "FIGMA_TOKEN": "figd_xxx",
18
- * "DEVLENS_CONFIG": "/path/to/your-app/devlens.config.json"
19
- * }
20
- */
21
-
22
- export const DesignSystemConfigSchema = z.object({
23
- /** Design system identifier, e.g. "jds3" */
24
- name: z.string(),
25
- /** Absolute or CWD-relative path to the consumer app root */
26
- projectRoot: z.string(),
27
- /** Relative path from projectRoot to the tokens file, e.g. "src/constants/figmaTokens.ts" */
28
- tokensFile: z.string(),
29
- /** Relative path from projectRoot to the generated component interface directory */
30
- componentsDir: z.string().optional(),
31
- /** Glob pattern for component source files (default: "src/**\/*.tsx") */
32
- componentsGlob: z.string().default("src/**/*.tsx"),
33
- });
34
-
35
- export const DevLensConfigSchema = z.object({
36
- designSystem: DesignSystemConfigSchema.optional(),
37
- });
38
-
39
- export type DevLensConfig = z.infer<typeof DevLensConfigSchema>;
40
- export type DesignSystemConfig = z.infer<typeof DesignSystemConfigSchema>;
41
-
42
- /**
43
- * Load DevLens config from DEVLENS_CONFIG env var path, or ./devlens.config.json in CWD.
44
- * Returns empty config {} if neither is found or both are invalid.
45
- * Never throws — invalid config is logged and skipped.
46
- */
47
- export async function loadDevLensConfig(): Promise<DevLensConfig> {
48
- const candidates: string[] = [];
49
-
50
- const envPath = process.env.DEVLENS_CONFIG;
51
- if (envPath) {
52
- candidates.push(resolve(envPath));
53
- }
54
- candidates.push(resolve(process.cwd(), "devlens.config.json"));
55
-
56
- for (const candidate of candidates) {
57
- try {
58
- const raw = await readFile(candidate, "utf-8");
59
- const parsed = JSON.parse(raw);
60
- const result = DevLensConfigSchema.safeParse(parsed);
61
- if (result.success) {
62
- console.error(`[devlens] Config loaded from: ${candidate}`);
63
- return result.data;
64
- } else {
65
- console.error(
66
- `[devlens] Config at ${candidate} is invalid:`,
67
- JSON.stringify(result.error.format(), null, 2)
68
- );
69
- }
70
- } catch {
71
- // File not found or JSON parse error — try next candidate
72
- }
73
- }
74
-
75
- return {};
76
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- import { startServer } from "./server.js";
2
-
3
- export async function createServer() {
4
- return startServer();
5
- }
@@ -1,160 +0,0 @@
1
- import WebSocket from "ws";
2
-
3
- /**
4
- * Chrome DevTools Protocol (CDP) client for communicating with the
5
- * Metro/Hermes debugger. Connects via WebSocket and sends JSON-RPC messages.
6
- *
7
- * Metro exposes the debugger endpoint at:
8
- * ws://localhost:{METRO_PORT}/inspector/device?device=0&page=-1
9
- *
10
- * Through CDP we can:
11
- * - Get console logs (Console.enable → Console.messageAdded)
12
- * - Intercept network requests (Network.enable → Network.requestWillBeSent)
13
- * - Execute JavaScript in the RN context (Runtime.evaluate)
14
- * - Trigger hot reload (via Runtime.evaluate)
15
- */
16
-
17
- export interface CdpMessage {
18
- id?: number;
19
- method?: string;
20
- params?: any;
21
- result?: any;
22
- error?: { message: string; code?: number };
23
- }
24
-
25
- type MessageHandler = (message: CdpMessage) => void;
26
-
27
- export class CdpClient {
28
- private ws: WebSocket | null = null;
29
- private messageId: number = 0;
30
- private pendingRequests: Map<
31
- number,
32
- { resolve: (value: any) => void; reject: (error: Error) => void }
33
- > = new Map();
34
- private eventHandlers: Map<string, MessageHandler[]> = new Map();
35
- private connected: boolean = false;
36
-
37
- constructor(private endpoint: string) {}
38
-
39
- /** Connect to the CDP endpoint */
40
- async connect(): Promise<void> {
41
- return new Promise((resolve, reject) => {
42
- this.ws = new WebSocket(this.endpoint);
43
-
44
- const timeout = setTimeout(() => {
45
- reject(new Error(`CDP connection timeout to ${this.endpoint}`));
46
- }, 5000);
47
-
48
- this.ws.on("open", () => {
49
- clearTimeout(timeout);
50
- this.connected = true;
51
- resolve();
52
- });
53
-
54
- this.ws.on("message", (data: WebSocket.Data) => {
55
- try {
56
- const message: CdpMessage = JSON.parse(data.toString());
57
- this.handleMessage(message);
58
- } catch {
59
- // Ignore malformed messages
60
- }
61
- });
62
-
63
- this.ws.on("close", () => {
64
- this.connected = false;
65
- // Reject all pending requests
66
- for (const [, { reject }] of this.pendingRequests) {
67
- reject(new Error("CDP connection closed"));
68
- }
69
- this.pendingRequests.clear();
70
- });
71
-
72
- this.ws.on("error", (err) => {
73
- clearTimeout(timeout);
74
- if (!this.connected) {
75
- reject(err);
76
- }
77
- });
78
- });
79
- }
80
-
81
- /** Send a CDP command and wait for the response */
82
- async send(method: string, params?: any): Promise<any> {
83
- if (!this.ws || !this.connected) {
84
- throw new Error("CDP client not connected");
85
- }
86
-
87
- const id = ++this.messageId;
88
- const message = JSON.stringify({ id, method, params });
89
-
90
- return new Promise((resolve, reject) => {
91
- const timeout = setTimeout(() => {
92
- this.pendingRequests.delete(id);
93
- reject(new Error(`CDP request timeout: ${method}`));
94
- }, 10000);
95
-
96
- this.pendingRequests.set(id, {
97
- resolve: (result) => {
98
- clearTimeout(timeout);
99
- resolve(result);
100
- },
101
- reject: (error) => {
102
- clearTimeout(timeout);
103
- reject(error);
104
- },
105
- });
106
-
107
- this.ws!.send(message);
108
- });
109
- }
110
-
111
- /** Register an event handler for CDP events */
112
- on(method: string, handler: MessageHandler): void {
113
- const handlers = this.eventHandlers.get(method) || [];
114
- handlers.push(handler);
115
- this.eventHandlers.set(method, handlers);
116
- }
117
-
118
- /** Remove event handlers for a method */
119
- off(method: string): void {
120
- this.eventHandlers.delete(method);
121
- }
122
-
123
- /** Check if connected */
124
- isConnected(): boolean {
125
- return this.connected;
126
- }
127
-
128
- /** Close the connection */
129
- close(): void {
130
- if (this.ws) {
131
- this.ws.close();
132
- this.ws = null;
133
- this.connected = false;
134
- }
135
- }
136
-
137
- private handleMessage(message: CdpMessage): void {
138
- // Response to a request
139
- if (message.id !== undefined) {
140
- const pending = this.pendingRequests.get(message.id);
141
- if (pending) {
142
- this.pendingRequests.delete(message.id);
143
- if (message.error) {
144
- pending.reject(new Error(message.error.message));
145
- } else {
146
- pending.resolve(message.result);
147
- }
148
- }
149
- return;
150
- }
151
-
152
- // Event notification
153
- if (message.method) {
154
- const handlers = this.eventHandlers.get(message.method) || [];
155
- for (const handler of handlers) {
156
- handler(message);
157
- }
158
- }
159
- }
160
- }