design-clone 2.1.0 → 2.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 (177) hide show
  1. package/README.md +13 -34
  2. package/SKILL.md +69 -45
  3. package/bin/cli.js +22 -4
  4. package/bin/commands/clone-site.js +31 -171
  5. package/bin/commands/help.js +19 -6
  6. package/bin/commands/init.js +9 -86
  7. package/bin/commands/uninstall.js +105 -0
  8. package/bin/commands/update.js +70 -0
  9. package/bin/commands/verify.js +7 -14
  10. package/bin/utils/paths.js +28 -0
  11. package/bin/utils/validate.js +2 -22
  12. package/bin/utils/version.js +23 -0
  13. package/docs/code-standards.md +789 -0
  14. package/docs/codebase-summary.md +533 -286
  15. package/docs/index.md +74 -0
  16. package/docs/project-overview-pdr.md +797 -0
  17. package/docs/system-architecture.md +718 -0
  18. package/package.json +14 -17
  19. package/src/ai/prompts/design-tokens/basic.md +80 -0
  20. package/src/ai/prompts/design-tokens/section-with-css.md +41 -0
  21. package/src/ai/prompts/design-tokens/section.md +48 -0
  22. package/src/ai/prompts/design-tokens/with-css.md +87 -0
  23. package/src/ai/prompts/structure-analysis/basic.md +55 -0
  24. package/src/ai/prompts/structure-analysis/with-context.md +59 -0
  25. package/src/ai/prompts/structure-analysis/with-dimensions.md +63 -0
  26. package/src/ai/prompts/structure-analysis/with-hierarchy.md +73 -0
  27. package/src/ai/prompts/ux-audit/aggregation.md +42 -0
  28. package/src/ai/prompts/ux-audit/desktop.md +92 -0
  29. package/src/ai/prompts/ux-audit/mobile.md +93 -0
  30. package/src/ai/prompts/ux-audit/tablet.md +92 -0
  31. package/src/core/animation/animation-extractor-ast.js +183 -0
  32. package/src/core/animation/animation-extractor-output.js +152 -0
  33. package/src/core/animation/animation-extractor.js +178 -0
  34. package/src/core/animation/state-capture-detection.js +200 -0
  35. package/src/core/animation/state-capture.js +193 -0
  36. package/src/core/capture/browser-context-pool.js +96 -0
  37. package/src/core/capture/multi-page-screenshot-page.js +110 -0
  38. package/src/core/capture/multi-page-screenshot.js +208 -0
  39. package/src/core/capture/screenshot-extraction.js +186 -0
  40. package/src/core/capture/screenshot-helpers.js +175 -0
  41. package/src/core/capture/screenshot-orchestrator.js +174 -0
  42. package/src/core/capture/screenshot-viewport.js +93 -0
  43. package/src/core/capture/screenshot.js +192 -0
  44. package/src/core/content/content-counter-dom.js +191 -0
  45. package/src/core/content/content-counter.js +76 -0
  46. package/src/core/css/breakpoint-detector.js +66 -0
  47. package/src/core/css/chromium-defaults.json +23 -0
  48. package/src/core/css/computed-style-extractor.js +102 -0
  49. package/src/core/css/css-chunker.js +103 -0
  50. package/src/core/css/filter-css-dead-code.js +120 -0
  51. package/src/core/css/filter-css-html-analyzer.js +110 -0
  52. package/src/core/css/filter-css-selector-matcher.js +172 -0
  53. package/src/core/css/filter-css.js +206 -0
  54. package/src/core/css/merge-css-atrule-processor.js +158 -0
  55. package/src/core/css/merge-css-file-io.js +68 -0
  56. package/src/core/css/merge-css.js +148 -0
  57. package/src/core/detection/framework-detector-routing.js +68 -0
  58. package/src/core/detection/framework-detector-signals.js +65 -0
  59. package/src/core/detection/framework-detector.js +198 -0
  60. package/src/core/dimension/dimension-extractor-card-detector.js +82 -0
  61. package/src/core/dimension/dimension-extractor.js +317 -0
  62. package/src/core/dimension/dimension-output-ai-summary.js +111 -0
  63. package/src/core/dimension/dimension-output.js +173 -0
  64. package/src/core/dimension/dom-tree-analyzer-tree-builders.js +95 -0
  65. package/src/core/dimension/dom-tree-analyzer.js +191 -0
  66. package/src/core/discovery/app-state-snapshot-capture.js +195 -0
  67. package/src/core/discovery/app-state-snapshot-utils.js +178 -0
  68. package/src/core/discovery/app-state-snapshot.js +131 -0
  69. package/src/core/discovery/discover-pages-routes.js +84 -0
  70. package/src/core/discovery/discover-pages-utils.js +177 -0
  71. package/src/core/discovery/discover-pages.js +191 -0
  72. package/src/core/html/html-extractor-inline-styler.js +70 -0
  73. package/src/core/html/html-extractor.js +147 -0
  74. package/src/core/html/semantic-enhancer-mappings.js +200 -0
  75. package/src/core/html/semantic-enhancer-page.js +148 -0
  76. package/src/core/html/semantic-enhancer.js +135 -0
  77. package/src/core/links/rewrite-links-css-rewriter.js +53 -0
  78. package/src/core/links/rewrite-links.js +173 -0
  79. package/src/core/media/asset-validator.js +118 -0
  80. package/src/core/media/extract-assets-downloader.js +187 -0
  81. package/src/core/media/extract-assets-page-scraper.js +115 -0
  82. package/src/core/media/extract-assets.js +159 -0
  83. package/src/core/media/video-capture-convert.js +200 -0
  84. package/src/core/media/video-capture.js +201 -0
  85. package/src/core/{lazy-loader.js → page-prep/lazy-loader.js} +37 -39
  86. package/src/core/section/section-cropper-helpers.js +43 -0
  87. package/src/core/{section-cropper.js → section/section-cropper.js} +11 -88
  88. package/src/core/section/section-detector-strategies.js +139 -0
  89. package/src/core/section/section-detector-utils.js +100 -0
  90. package/src/core/section/section-detector.js +88 -0
  91. package/src/core/tests/test-section-cropper.js +2 -2
  92. package/src/core/tests/test-section-detector.js +2 -2
  93. package/src/post-process/enhance-assets.js +29 -4
  94. package/src/post-process/fetch-images-unsplash-client.js +123 -0
  95. package/src/post-process/fetch-images.js +60 -263
  96. package/src/post-process/inject-gosnap.js +88 -0
  97. package/src/post-process/inject-icons-svg-replacer.js +76 -0
  98. package/src/post-process/inject-icons.js +47 -200
  99. package/src/route-discoverers/base-discoverer-utils.js +137 -0
  100. package/src/route-discoverers/base-discoverer.js +29 -118
  101. package/src/route-discoverers/index.js +1 -1
  102. package/src/shared/config.js +38 -0
  103. package/src/shared/error-codes.js +31 -0
  104. package/src/shared/viewports.js +46 -0
  105. package/src/utils/browser.js +0 -7
  106. package/src/utils/helpers.js +4 -0
  107. package/src/utils/log.js +12 -0
  108. package/src/utils/playwright-loader.js +76 -0
  109. package/src/utils/playwright.js +3 -69
  110. package/src/utils/progress.js +32 -0
  111. package/src/verification/generate-audit-report-css-fixes.js +52 -0
  112. package/src/verification/generate-audit-report-sections.js +158 -0
  113. package/src/verification/generate-audit-report.js +5 -281
  114. package/src/verification/quality-scorer.js +92 -0
  115. package/src/verification/verify-footer-checks.js +103 -0
  116. package/src/verification/verify-footer-helpers.js +178 -0
  117. package/src/verification/verify-footer.js +23 -381
  118. package/src/verification/verify-header-checks.js +104 -0
  119. package/src/verification/verify-header-helpers.js +156 -0
  120. package/src/verification/verify-header.js +23 -365
  121. package/src/verification/verify-layout-report.js +101 -0
  122. package/src/verification/verify-layout.js +13 -259
  123. package/src/verification/verify-menu-checks.js +104 -0
  124. package/src/verification/verify-menu-helpers.js +112 -0
  125. package/src/verification/verify-menu.js +17 -285
  126. package/src/verification/verify-slider-checks.js +115 -0
  127. package/src/verification/verify-slider-constants.js +65 -0
  128. package/src/verification/verify-slider-helpers.js +164 -0
  129. package/src/verification/verify-slider.js +23 -414
  130. package/.env.example +0 -14
  131. package/docs/basic-clone.md +0 -63
  132. package/docs/cli-reference.md +0 -316
  133. package/docs/design-clone-architecture.md +0 -492
  134. package/docs/pixel-perfect.md +0 -117
  135. package/docs/project-roadmap.md +0 -382
  136. package/docs/troubleshooting.md +0 -170
  137. package/requirements.txt +0 -5
  138. package/src/ai/__pycache__/analyze-structure.cpython-313.pyc +0 -0
  139. package/src/ai/__pycache__/extract-design-tokens.cpython-313.pyc +0 -0
  140. package/src/ai/analyze-structure.py +0 -375
  141. package/src/ai/extract-design-tokens.py +0 -782
  142. package/src/ai/prompts/__init__.py +0 -2
  143. package/src/ai/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  144. package/src/ai/prompts/__pycache__/design_tokens.cpython-313.pyc +0 -0
  145. package/src/ai/prompts/__pycache__/structure_analysis.cpython-313.pyc +0 -0
  146. package/src/ai/prompts/__pycache__/ux_audit.cpython-313.pyc +0 -0
  147. package/src/ai/prompts/design_tokens.py +0 -316
  148. package/src/ai/prompts/structure_analysis.py +0 -592
  149. package/src/ai/prompts/ux_audit.py +0 -198
  150. package/src/ai/ux-audit.js +0 -596
  151. package/src/core/animation-extractor.js +0 -526
  152. package/src/core/app-state-snapshot.js +0 -511
  153. package/src/core/content-counter.js +0 -342
  154. package/src/core/design-tokens.js +0 -103
  155. package/src/core/dimension-extractor.js +0 -438
  156. package/src/core/dimension-output.js +0 -305
  157. package/src/core/discover-pages.js +0 -542
  158. package/src/core/dom-tree-analyzer.js +0 -298
  159. package/src/core/extract-assets.js +0 -468
  160. package/src/core/filter-css.js +0 -499
  161. package/src/core/framework-detector.js +0 -538
  162. package/src/core/html-extractor.js +0 -212
  163. package/src/core/merge-css.js +0 -407
  164. package/src/core/multi-page-screenshot.js +0 -380
  165. package/src/core/rewrite-links.js +0 -226
  166. package/src/core/screenshot.js +0 -701
  167. package/src/core/section-detector.js +0 -386
  168. package/src/core/semantic-enhancer.js +0 -492
  169. package/src/core/state-capture.js +0 -598
  170. package/src/core/video-capture.js +0 -546
  171. package/src/utils/__init__.py +0 -16
  172. package/src/utils/__pycache__/__init__.cpython-313.pyc +0 -0
  173. package/src/utils/__pycache__/env.cpython-313.pyc +0 -0
  174. package/src/utils/env.py +0 -134
  175. /package/src/core/{css-extractor.js → css/css-extractor.js} +0 -0
  176. /package/src/core/{cookie-handler.js → page-prep/cookie-handler.js} +0 -0
  177. /package/src/core/{page-readiness.js → page-prep/page-readiness.js} +0 -0
@@ -1,305 +0,0 @@
1
- /**
2
- * Dimension Output Builder
3
- *
4
- * Build and format component dimension output for JSON files.
5
- * Includes sanitization, cross-viewport summary, and AI-friendly format.
6
- */
7
-
8
- // Default viewport configurations
9
- const VIEWPORTS = {
10
- desktop: { width: 1440, height: 900, deviceScaleFactor: 1 },
11
- tablet: { width: 768, height: 1024, deviceScaleFactor: 1 },
12
- mobile: { width: 375, height: 812, deviceScaleFactor: 2 }
13
- };
14
-
15
- /**
16
- * Build final component-dimensions.json output with proper schema
17
- * @param {Object} allViewportDimensions - Dimensions from all viewports
18
- * @param {string} url - Source URL
19
- * @returns {Object} Final JSON structure
20
- */
21
- export function buildDimensionsOutput(allViewportDimensions, url) {
22
- const output = {
23
- meta: {
24
- version: "1.0",
25
- extractedAt: new Date().toISOString(),
26
- url: url,
27
- tool: "design-clone/screenshot.js"
28
- },
29
- viewports: {},
30
- summary: {}
31
- };
32
-
33
- for (const [vpName, vpData] of Object.entries(allViewportDimensions)) {
34
- output.viewports[vpName] = sanitizeViewportData(vpData, vpName);
35
- }
36
-
37
- output.summary = buildCrossViewportSummary(output.viewports);
38
- return output;
39
- }
40
-
41
- /**
42
- * Sanitize viewport data for JSON output
43
- */
44
- export function sanitizeViewportData(data, vpName) {
45
- if (!data) return {};
46
-
47
- const clean = JSON.parse(JSON.stringify(data));
48
- clean.width = VIEWPORTS[vpName]?.width || 0;
49
- clean.height = VIEWPORTS[vpName]?.height || 0;
50
-
51
- function roundNumbers(obj) {
52
- for (const key in obj) {
53
- if (typeof obj[key] === 'number') {
54
- obj[key] = Math.round(obj[key]);
55
- } else if (Array.isArray(obj[key])) {
56
- obj[key].forEach(item => roundNumbers(item));
57
- } else if (typeof obj[key] === 'object' && obj[key] !== null) {
58
- roundNumbers(obj[key]);
59
- }
60
- }
61
- return obj;
62
- }
63
-
64
- function truncateStrings(obj, maxLen = 80) {
65
- for (const key in obj) {
66
- if (typeof obj[key] === 'string' && obj[key].length > maxLen) {
67
- obj[key] = obj[key].slice(0, maxLen) + '...';
68
- } else if (Array.isArray(obj[key])) {
69
- obj[key].forEach(item => truncateStrings(item, maxLen));
70
- } else if (typeof obj[key] === 'object' && obj[key] !== null) {
71
- truncateStrings(obj[key], maxLen);
72
- }
73
- }
74
- return obj;
75
- }
76
-
77
- // Limit array sizes for token efficiency
78
- if (clean.containers && clean.containers.length > 15) {
79
- clean.containers = clean.containers.slice(0, 15);
80
- }
81
- if (clean.images && clean.images.length > 10) {
82
- clean.images = clean.images.slice(0, 10);
83
- }
84
- if (clean.buttons && clean.buttons.length > 10) {
85
- clean.buttons = clean.buttons.slice(0, 10);
86
- }
87
-
88
- return truncateStrings(roundNumbers(clean));
89
- }
90
-
91
- /**
92
- * Build cross-viewport summary for AI consumption.
93
- * Includes section-aware typography and container data.
94
- *
95
- * @param {Object} viewports - Viewport data keyed by name (desktop, tablet, mobile)
96
- * @returns {Object} Summary with:
97
- * - maxContainerWidth: Largest container width across all viewports
98
- * - commonGap: Average gap from card patterns
99
- * - breakpoints: Viewport width breakpoints
100
- * - typography: Flat h1/h2/h3/body sizes by viewport (backward compat)
101
- * - typographyBySection: Typography grouped by section context (hero h1 != content h1)
102
- * - cardPatterns: Card group statistics
103
- * - sections: Section detection summary (found flag + width/containerWidth)
104
- */
105
- export function buildCrossViewportSummary(viewports) {
106
- const summary = {
107
- maxContainerWidth: 0,
108
- commonGap: 0,
109
- breakpoints: {
110
- desktop: VIEWPORTS.desktop.width,
111
- tablet: VIEWPORTS.tablet.width,
112
- mobile: VIEWPORTS.mobile.width
113
- },
114
- // Flat typography for backward compatibility
115
- typography: { h1: {}, h2: {}, h3: {}, body: {} },
116
- // NEW: Typography by section context
117
- typographyBySection: {
118
- hero: {},
119
- content: {},
120
- header: {},
121
- footer: {},
122
- sidebar: {}
123
- },
124
- cardPatterns: { totalGroups: 0, avgCardSize: null },
125
- // NEW: Section summary
126
- sections: {
127
- hero: { found: false, containerWidth: null },
128
- content: { found: false, containerWidth: null },
129
- header: { found: false, containerWidth: null },
130
- footer: { found: false, containerWidth: null },
131
- sidebar: { found: false, width: null }
132
- }
133
- };
134
-
135
- for (const [vpName, vpData] of Object.entries(viewports)) {
136
- if (!vpData) continue;
137
-
138
- // Container section mapping
139
- if (vpData.containers) {
140
- for (const container of vpData.containers) {
141
- if (container.width > summary.maxContainerWidth) {
142
- summary.maxContainerWidth = container.width;
143
- }
144
- // Track section widths
145
- const section = container.section || 'content';
146
- if (summary.sections[section]) {
147
- summary.sections[section].found = true;
148
- // Sidebar uses 'width' field, others use 'containerWidth'
149
- if (section === 'sidebar') {
150
- if (!summary.sections[section].width ||
151
- container.width > summary.sections[section].width) {
152
- summary.sections[section].width = container.width;
153
- }
154
- } else {
155
- if (!summary.sections[section].containerWidth ||
156
- container.width > summary.sections[section].containerWidth) {
157
- summary.sections[section].containerWidth = container.width;
158
- }
159
- }
160
- }
161
- }
162
- }
163
-
164
- // Typography by section
165
- if (vpData.typography) {
166
- for (const typo of vpData.typography) {
167
- const tag = typo.selector?.toLowerCase();
168
- const section = typo.section || 'content';
169
-
170
- // Flat typography (backward compat) - take first found
171
- if (tag === 'h1' && !summary.typography.h1[vpName]) summary.typography.h1[vpName] = typo.fontSize;
172
- if (tag === 'h2' && !summary.typography.h2[vpName]) summary.typography.h2[vpName] = typo.fontSize;
173
- if (tag === 'h3' && !summary.typography.h3[vpName]) summary.typography.h3[vpName] = typo.fontSize;
174
- if (tag === 'p' && !summary.typography.body[vpName]) summary.typography.body[vpName] = typo.fontSize;
175
-
176
- // Typography by section
177
- if (!summary.typographyBySection[section]) {
178
- summary.typographyBySection[section] = {};
179
- }
180
- if (!summary.typographyBySection[section][tag]) {
181
- summary.typographyBySection[section][tag] = {};
182
- }
183
- // Take first found per section/tag/viewport
184
- if (!summary.typographyBySection[section][tag][vpName]) {
185
- summary.typographyBySection[section][tag][vpName] = typo.fontSize;
186
- }
187
- }
188
- }
189
-
190
- // Card patterns (unchanged)
191
- if (vpData.cards && vpData.cards.length > 0) {
192
- summary.cardPatterns.totalGroups += vpData.cards.length;
193
- if (vpName === 'desktop' && vpData.cards[0]?.avgDimensions) {
194
- summary.cardPatterns.avgCardSize = vpData.cards[0].avgDimensions;
195
- }
196
-
197
- const gaps = vpData.cards.map(g => g.gap).filter(g => g > 0);
198
- if (gaps.length > 0) {
199
- summary.commonGap = Math.round(gaps.reduce((a, b) => a + b, 0) / gaps.length);
200
- }
201
- }
202
- }
203
-
204
- return summary;
205
- }
206
-
207
- /**
208
- * Generate AI-friendly summary (compact, <5KB)
209
- * Includes section-aware typography for accurate reconstruction
210
- * @param {Object} fullOutput - Full component-dimensions.json
211
- * @returns {Object} Compact summary for AI prompts
212
- */
213
- export function generateAISummary(fullOutput) {
214
- const { viewports, summary } = fullOutput;
215
- const desktop = viewports.desktop || {};
216
-
217
- function inferSectionPadding(containers) {
218
- if (!containers || containers.length === 0) return "64px 0";
219
- const paddings = containers.slice(0, 5).map(c => ({
220
- v: c.paddingTop || c.paddingBottom || 64,
221
- h: c.paddingLeft || c.paddingRight || 0
222
- }));
223
- const avgV = Math.round(paddings.reduce((s, p) => s + p.v, 0) / paddings.length);
224
- const avgH = Math.round(paddings.reduce((s, p) => s + p.h, 0) / paddings.length);
225
- return `${avgV}px ${avgH}px`;
226
- }
227
-
228
- function inferCardDimensions(cards) {
229
- if (!cards || cards.length === 0) {
230
- return { width: "auto", height: "auto", padding: "24px" };
231
- }
232
- const first = cards[0].avgDimensions || cards[0];
233
- return {
234
- width: first.width ? first.width + "px" : "auto",
235
- height: first.height > 0 ? first.height + "px" : "auto",
236
- padding: (first.paddingTop || first.padding || 24) + "px"
237
- };
238
- }
239
-
240
- /**
241
- * Convert typographyBySection to AI-friendly format with px units
242
- */
243
- function inferTypographyBySection(typographyBySection) {
244
- const result = {};
245
- for (const [section, tags] of Object.entries(typographyBySection || {})) {
246
- if (!tags || Object.keys(tags).length === 0) continue;
247
- result[section] = {};
248
- for (const [tag, sizes] of Object.entries(tags)) {
249
- // Use desktop first, then tablet, then mobile
250
- const size = sizes.desktop || sizes.tablet || sizes.mobile || 0;
251
- if (size > 0) {
252
- result[section][tag] = size + "px";
253
- }
254
- }
255
- // Remove empty sections
256
- if (Object.keys(result[section]).length === 0) {
257
- delete result[section];
258
- }
259
- }
260
- return result;
261
- }
262
-
263
- return {
264
- _comment: "USE THESE EXACT VALUES - DO NOT ESTIMATE",
265
- EXACT_DIMENSIONS: {
266
- container_max_width: summary.maxContainerWidth + "px",
267
- section_padding: inferSectionPadding(desktop.containers),
268
- card_dimensions: inferCardDimensions(desktop.cards),
269
- gap: summary.commonGap + "px"
270
- },
271
- EXACT_TYPOGRAPHY: {
272
- h1: (summary.typography.h1.desktop || 48) + "px",
273
- h2: (summary.typography.h2.desktop || 36) + "px",
274
- h3: (summary.typography.h3.desktop || 24) + "px",
275
- body: (summary.typography.body.desktop || 16) + "px"
276
- },
277
- // NEW: Section-aware typography (hero h1 != content h1)
278
- TYPOGRAPHY_BY_SECTION: inferTypographyBySection(summary.typographyBySection),
279
- // NEW: Section info
280
- SECTIONS: {
281
- hero: summary.sections?.hero || { found: false },
282
- content: summary.sections?.content || { found: false },
283
- header: summary.sections?.header || { found: false },
284
- footer: summary.sections?.footer || { found: false },
285
- sidebar: summary.sections?.sidebar || { found: false }
286
- },
287
- RESPONSIVE: {
288
- desktop_breakpoint: summary.breakpoints.desktop + "px",
289
- tablet_breakpoint: summary.breakpoints.tablet + "px",
290
- mobile_breakpoint: summary.breakpoints.mobile + "px",
291
- typography_scaling: {
292
- h1: {
293
- desktop: (summary.typography.h1.desktop || 48) + "px",
294
- tablet: (summary.typography.h1.tablet || 36) + "px",
295
- mobile: (summary.typography.h1.mobile || 28) + "px"
296
- },
297
- h2: {
298
- desktop: (summary.typography.h2.desktop || 36) + "px",
299
- tablet: (summary.typography.h2.tablet || 28) + "px",
300
- mobile: (summary.typography.h2.mobile || 24) + "px"
301
- }
302
- }
303
- }
304
- };
305
- }