@fragments-sdk/cli 0.2.2

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,279 @@
1
+ import pixelmatch from 'pixelmatch';
2
+ import { PNG } from 'pngjs';
3
+ import {
4
+ BRAND,
5
+ DEFAULTS,
6
+ type Screenshot,
7
+ type DiffResult,
8
+ type BoundingBox,
9
+ } from '../core/index.js';
10
+ import { ServiceError, Timer } from './utils.js';
11
+
12
+ /**
13
+ * Options for diff comparison
14
+ */
15
+ export interface DiffOptions {
16
+ /** Percentage threshold for matching (0-100). Default: 5 */
17
+ threshold?: number;
18
+
19
+ /** Color difference sensitivity (0-1). Lower = more sensitive. Default: 0.1 */
20
+ colorThreshold?: number;
21
+
22
+ /** Include anti-aliased pixels in comparison. Default: false */
23
+ includeAA?: boolean;
24
+ }
25
+
26
+ /**
27
+ * Diff engine for comparing screenshots.
28
+ */
29
+ export class DiffEngine {
30
+ private readonly defaultThreshold: number;
31
+
32
+ constructor(defaultThreshold: number = DEFAULTS.diffThreshold) {
33
+ this.defaultThreshold = defaultThreshold;
34
+ }
35
+
36
+ /**
37
+ * Compare two screenshots and return diff result
38
+ */
39
+ compare(
40
+ current: Screenshot,
41
+ baseline: Screenshot,
42
+ options: DiffOptions = {}
43
+ ): DiffResult {
44
+ const timer = new Timer();
45
+
46
+ // Parse PNG images
47
+ const img1 = PNG.sync.read(current.data);
48
+ const img2 = PNG.sync.read(baseline.data);
49
+
50
+ // Validate dimensions match
51
+ if (img1.width !== img2.width || img1.height !== img2.height) {
52
+ throw new DiffError(
53
+ `Dimension mismatch: current is ${img1.width}x${img1.height}, baseline is ${img2.width}x${img2.height}`,
54
+ 'DIMENSION_MISMATCH',
55
+ 'Ensure both screenshots use the same viewport size'
56
+ );
57
+ }
58
+
59
+ const { width, height } = img1;
60
+ const totalPixels = width * height;
61
+
62
+ // Create diff image
63
+ const diffPng = new PNG({ width, height });
64
+
65
+ // Run pixelmatch comparison
66
+ const diffPixelCount = pixelmatch(
67
+ img1.data,
68
+ img2.data,
69
+ diffPng.data,
70
+ width,
71
+ height,
72
+ {
73
+ threshold: options.colorThreshold ?? 0.1,
74
+ includeAA: options.includeAA ?? false,
75
+ alpha: 0.1,
76
+ diffColor: [255, 0, 0], // Red for differences
77
+ diffColorAlt: [0, 255, 0], // Green for anti-aliased
78
+ }
79
+ );
80
+
81
+ // Calculate diff percentage
82
+ const diffPercentage = (diffPixelCount / totalPixels) * 100;
83
+ const threshold = options.threshold ?? this.defaultThreshold;
84
+
85
+ // Find changed regions
86
+ const changedRegions = this.findChangedRegions(diffPng);
87
+
88
+ // Only include diff image if there are differences
89
+ const diffImage = diffPixelCount > 0 ? PNG.sync.write(diffPng) : undefined;
90
+
91
+ return {
92
+ matches: diffPercentage <= threshold,
93
+ diffPercentage: Math.round(diffPercentage * 100) / 100,
94
+ diffPixelCount,
95
+ totalPixels,
96
+ diffImage: diffImage ? Buffer.from(diffImage) : undefined,
97
+ changedRegions,
98
+ diffTimeMs: timer.elapsed(),
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Quick check if two screenshots are identical (by hash)
104
+ */
105
+ areIdentical(current: Screenshot, baseline: Screenshot): boolean {
106
+ return current.hash === baseline.hash;
107
+ }
108
+
109
+ /**
110
+ * Find bounding boxes of changed regions in the diff image
111
+ */
112
+ private findChangedRegions(diffPng: PNG): BoundingBox[] {
113
+ const { width, height, data } = diffPng;
114
+ const regions: BoundingBox[] = [];
115
+ const visited = new Set<number>();
116
+
117
+ // Scan for red pixels (differences)
118
+ for (let y = 0; y < height; y++) {
119
+ for (let x = 0; x < width; x++) {
120
+ const idx = (y * width + x) * 4;
121
+ const pixelKey = y * width + x;
122
+
123
+ // Check if this is a diff pixel (red channel > 200) and not visited
124
+ if (data[idx] > 200 && !visited.has(pixelKey)) {
125
+ // Flood fill to find region bounds
126
+ const region = this.floodFillBounds(diffPng, x, y, visited);
127
+ if (region) {
128
+ regions.push(region);
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ // Merge overlapping regions
135
+ return this.mergeOverlappingRegions(regions);
136
+ }
137
+
138
+ /**
139
+ * Flood fill to find bounds of a contiguous changed region
140
+ */
141
+ private floodFillBounds(
142
+ diffPng: PNG,
143
+ startX: number,
144
+ startY: number,
145
+ visited: Set<number>
146
+ ): BoundingBox | null {
147
+ const { width, height, data } = diffPng;
148
+ const stack: Array<[number, number]> = [[startX, startY]];
149
+
150
+ let minX = startX;
151
+ let maxX = startX;
152
+ let minY = startY;
153
+ let maxY = startY;
154
+ let pixelCount = 0;
155
+
156
+ while (stack.length > 0) {
157
+ const [x, y] = stack.pop()!;
158
+ const pixelKey = y * width + x;
159
+
160
+ if (
161
+ x < 0 ||
162
+ x >= width ||
163
+ y < 0 ||
164
+ y >= height ||
165
+ visited.has(pixelKey)
166
+ ) {
167
+ continue;
168
+ }
169
+
170
+ const idx = (y * width + x) * 4;
171
+
172
+ // Check if this is a diff pixel
173
+ if (data[idx] <= 200) {
174
+ continue;
175
+ }
176
+
177
+ visited.add(pixelKey);
178
+ pixelCount++;
179
+
180
+ // Update bounds
181
+ minX = Math.min(minX, x);
182
+ maxX = Math.max(maxX, x);
183
+ minY = Math.min(minY, y);
184
+ maxY = Math.max(maxY, y);
185
+
186
+ // Add neighbors (4-connectivity)
187
+ stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]);
188
+ }
189
+
190
+ // Filter out tiny regions (noise)
191
+ if (pixelCount < 10) {
192
+ return null;
193
+ }
194
+
195
+ return {
196
+ x: minX,
197
+ y: minY,
198
+ width: maxX - minX + 1,
199
+ height: maxY - minY + 1,
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Merge overlapping bounding boxes
205
+ */
206
+ private mergeOverlappingRegions(regions: BoundingBox[]): BoundingBox[] {
207
+ if (regions.length <= 1) {
208
+ return regions;
209
+ }
210
+
211
+ // Sort by x coordinate
212
+ const sorted = [...regions].sort((a, b) => a.x - b.x);
213
+ const merged: BoundingBox[] = [];
214
+
215
+ let current = sorted[0];
216
+
217
+ for (let i = 1; i < sorted.length; i++) {
218
+ const next = sorted[i];
219
+
220
+ // Check if regions overlap or are adjacent (with 10px margin)
221
+ if (this.regionsOverlap(current, next, 10)) {
222
+ // Merge into current
223
+ current = this.mergeBoxes(current, next);
224
+ } else {
225
+ merged.push(current);
226
+ current = next;
227
+ }
228
+ }
229
+
230
+ merged.push(current);
231
+ return merged;
232
+ }
233
+
234
+ /**
235
+ * Check if two bounding boxes overlap (with margin)
236
+ */
237
+ private regionsOverlap(a: BoundingBox, b: BoundingBox, margin: number): boolean {
238
+ return !(
239
+ a.x + a.width + margin < b.x ||
240
+ b.x + b.width + margin < a.x ||
241
+ a.y + a.height + margin < b.y ||
242
+ b.y + b.height + margin < a.y
243
+ );
244
+ }
245
+
246
+ /**
247
+ * Merge two bounding boxes
248
+ */
249
+ private mergeBoxes(a: BoundingBox, b: BoundingBox): BoundingBox {
250
+ const minX = Math.min(a.x, b.x);
251
+ const minY = Math.min(a.y, b.y);
252
+ const maxX = Math.max(a.x + a.width, b.x + b.width);
253
+ const maxY = Math.max(a.y + a.height, b.y + b.height);
254
+
255
+ return {
256
+ x: minX,
257
+ y: minY,
258
+ width: maxX - minX,
259
+ height: maxY - minY,
260
+ };
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Error class for diff errors
266
+ */
267
+ export class DiffError extends ServiceError {
268
+ constructor(message: string, code: string, suggestion?: string) {
269
+ super(message, code, suggestion);
270
+ this.name = `${BRAND.name}DiffError`;
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Create a diff engine
276
+ */
277
+ export function createDiffEngine(defaultThreshold?: number): DiffEngine {
278
+ return new DiffEngine(defaultThreshold);
279
+ }