design-clone 2.1.0 → 3.0.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,298 +0,0 @@
1
- /**
2
- * DOM Tree Analyzer
3
- *
4
- * Traverse DOM tree hierarchically to capture structure,
5
- * semantic landmarks, and parent-child relationships.
6
- *
7
- * Key features:
8
- * - PreOrder traversal (parent before children)
9
- * - W3C landmark detection (header, main, footer, nav, aside)
10
- * - Section context mapping (hero, content, sidebar, footer)
11
- * - Bidirectional parent-child refs
12
- * - Configurable max depth (default: 8)
13
- */
14
-
15
- // Constants
16
- const MAX_DEPTH = 8;
17
- const LANDMARK_TAGS = ['header', 'main', 'footer', 'nav', 'aside', 'section', 'article'];
18
- const HEADING_TAGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
19
-
20
- // Section detection thresholds (ratios of page height/width)
21
- const HERO_THRESHOLD = 0.15; // Top 15% of page is considered hero area
22
- const FOOTER_THRESHOLD = 0.85; // Bottom 15% of page is considered footer area
23
- const SIDEBAR_MAX_WIDTH = 400; // Max width in px for sidebar detection
24
- const Y_POSITION_TOLERANCE = 5; // Tolerance in px for heading Y-position matching
25
-
26
- /**
27
- * Extract DOM tree hierarchy from page
28
- * @param {import('playwright').Page} page - Playwright page
29
- * @param {Object} options - Configuration options
30
- * @param {number} [options.maxDepth=8] - Maximum traversal depth
31
- * @param {boolean} [options.includeHidden=false] - Include hidden elements (useful for accessibility audits)
32
- * @returns {Promise<Object>} DOMHierarchy with root, landmarks, headingTree, stats
33
- */
34
- export async function extractDOMHierarchy(page, options = {}) {
35
- const { maxDepth = MAX_DEPTH, includeHidden = false } = options;
36
- const startTime = Date.now();
37
-
38
- const result = await page.evaluate(({ maxDepth, includeHidden, LANDMARK_TAGS, HEADING_TAGS, HERO_THRESHOLD, FOOTER_THRESHOLD, SIDEBAR_MAX_WIDTH }) => {
39
- // Page dimensions for section context
40
- const pageHeight = Math.max(
41
- document.body.scrollHeight,
42
- document.documentElement.scrollHeight
43
- );
44
- const pageWidth = document.documentElement.clientWidth;
45
-
46
- /**
47
- * Detect semantic role of element
48
- * Priority: ARIA role > semantic tag > class patterns
49
- */
50
- function detectRole(el) {
51
- const tag = el.tagName.toLowerCase();
52
- const ariaRole = el.getAttribute('role');
53
-
54
- // ARIA role takes precedence
55
- if (ariaRole) return ariaRole;
56
-
57
- // W3C landmarks - check nesting context
58
- if (LANDMARK_TAGS.includes(tag)) {
59
- const isTopLevel = !el.closest('main, section, article, aside');
60
- if (tag === 'header' || tag === 'footer') {
61
- return isTopLevel ? `${tag}-landmark` : `${tag}-section`;
62
- }
63
- return tag;
64
- }
65
-
66
- // Headings
67
- if (HEADING_TAGS.includes(tag)) {
68
- return `heading-${tag.slice(1)}`;
69
- }
70
-
71
- // Content containers via class patterns
72
- if (tag === 'div' || tag === 'span') {
73
- const cls = (el.className || '').toString().toLowerCase();
74
- if (cls.includes('container')) return 'container';
75
- if (cls.includes('wrapper')) return 'wrapper';
76
- if (cls.includes('card')) return 'card';
77
- if (cls.includes('grid')) return 'grid';
78
- if (cls.includes('hero')) return 'hero';
79
- }
80
-
81
- return null;
82
- }
83
-
84
- /**
85
- * Detect section context based on semantic tags (priority) and position
86
- */
87
- function detectSectionContext(el, yPos) {
88
- // Semantic tags have priority (per validation)
89
- const tag = el.tagName.toLowerCase();
90
- if (tag === 'header' || el.closest('header')) return 'header';
91
- if (tag === 'footer' || el.closest('footer')) return 'footer';
92
- if (tag === 'aside' || el.closest('aside')) return 'sidebar';
93
- if (tag === 'nav' || el.closest('nav')) return 'nav';
94
-
95
- // Position-based fallback (when no semantic tag found)
96
- const yRatio = yPos / pageHeight;
97
- if (yRatio < HERO_THRESHOLD) return 'hero';
98
- if (yRatio > FOOTER_THRESHOLD) return 'footer';
99
-
100
- // Check for narrow fixed/sticky elements (sidebar pattern)
101
- const computed = window.getComputedStyle(el);
102
- const rect = el.getBoundingClientRect();
103
- if ((computed.position === 'fixed' || computed.position === 'sticky') && rect.width < SIDEBAR_MAX_WIDTH) {
104
- return 'sidebar';
105
- }
106
-
107
- return 'content';
108
- }
109
-
110
- /**
111
- * PreOrder DOM traversal
112
- */
113
- function traverseDOM(el, depth, parentId, path) {
114
- if (depth > maxDepth) return null;
115
-
116
- const rect = el.getBoundingClientRect();
117
- // Skip hidden elements unless includeHidden
118
- if (!includeHidden && (rect.width === 0 && rect.height === 0)) return null;
119
-
120
- const id = path.join('-');
121
- const computed = window.getComputedStyle(el);
122
- const yPos = rect.y + window.scrollY;
123
-
124
- const node = {
125
- id,
126
- tagName: el.tagName.toLowerCase(),
127
- depth,
128
- role: detectRole(el),
129
- section: detectSectionContext(el, yPos),
130
- attributes: {
131
- id: el.id || null,
132
- className: el.className ? el.className.toString().split(' ').slice(0, 3).join(' ') : null,
133
- role: el.getAttribute('role')
134
- },
135
- dimensions: {
136
- width: Math.round(rect.width),
137
- height: Math.round(rect.height),
138
- x: Math.round(rect.x),
139
- y: Math.round(yPos)
140
- },
141
- layout: {
142
- display: computed.display,
143
- position: computed.position !== 'static' ? computed.position : undefined,
144
- flexDirection: computed.flexDirection !== 'row' ? computed.flexDirection : undefined,
145
- gridTemplateColumns: computed.gridTemplateColumns !== 'none' ? computed.gridTemplateColumns : undefined
146
- },
147
- children: [],
148
- parentId
149
- };
150
-
151
- // Recurse children (PreOrder)
152
- let childIdx = 0;
153
- for (const child of el.children) {
154
- const childNode = traverseDOM(child, depth + 1, id, [...path, childIdx]);
155
- if (childNode) {
156
- node.children.push(childNode);
157
- childIdx++;
158
- }
159
- }
160
-
161
- return node;
162
- }
163
-
164
- /**
165
- * Build landmarks map from traversed tree
166
- */
167
- function buildLandmarksMap(root) {
168
- const landmarks = {
169
- header: null,
170
- main: null,
171
- footer: null,
172
- nav: [],
173
- aside: []
174
- };
175
-
176
- function walk(node) {
177
- if (!node) return;
178
-
179
- switch (node.role) {
180
- case 'header-landmark': landmarks.header = node; break;
181
- case 'main': landmarks.main = node; break;
182
- case 'footer-landmark': landmarks.footer = node; break;
183
- case 'nav': landmarks.nav.push(node); break;
184
- case 'aside': landmarks.aside.push(node); break;
185
- }
186
-
187
- node.children.forEach(walk);
188
- }
189
-
190
- walk(root);
191
- return landmarks;
192
- }
193
-
194
- /**
195
- * Build heading tree with section context and text
196
- */
197
- function buildHeadingTree(root) {
198
- const headings = [];
199
-
200
- function walk(node, sectionContext) {
201
- if (!node) return;
202
-
203
- // Update section context based on landmarks
204
- let ctx = sectionContext;
205
- if (node.role === 'header-landmark') ctx = 'header';
206
- else if (node.role === 'main') ctx = 'content';
207
- else if (node.role === 'footer-landmark') ctx = 'footer';
208
- else if (node.role === 'aside') ctx = 'sidebar';
209
- else if (node.role === 'hero') ctx = 'hero';
210
-
211
- // Use node's detected section as fallback
212
- if (!ctx) ctx = node.section || 'content';
213
-
214
- // Collect headings
215
- if (node.role?.startsWith('heading-')) {
216
- headings.push({
217
- level: parseInt(node.role.slice(-1)),
218
- section: ctx,
219
- nodeId: node.id,
220
- y: node.dimensions.y,
221
- fontSize: null, // Set separately for perf
222
- text: null // Set separately for perf
223
- });
224
- }
225
-
226
- node.children.forEach(c => walk(c, ctx));
227
- }
228
-
229
- walk(root, null);
230
- return headings.sort((a, b) => a.y - b.y);
231
- }
232
-
233
- // Execute traversal
234
- const root = traverseDOM(document.body, 0, null, [0]);
235
- const landmarks = buildLandmarksMap(root);
236
- const headingTree = buildHeadingTree(root);
237
-
238
- // Calculate stats
239
- let totalNodes = 0, maxActualDepth = 0;
240
- function countNodes(n) {
241
- if (!n) return;
242
- totalNodes++;
243
- maxActualDepth = Math.max(maxActualDepth, n.depth);
244
- n.children.forEach(countNodes);
245
- }
246
- countNodes(root);
247
-
248
- return {
249
- root,
250
- landmarks,
251
- headingTree,
252
- stats: {
253
- totalNodes,
254
- maxDepth: maxActualDepth,
255
- landmarkCount: [landmarks.header, landmarks.main, landmarks.footer].filter(Boolean).length +
256
- landmarks.nav.length + landmarks.aside.length,
257
- pageHeight,
258
- pageWidth
259
- }
260
- };
261
- }, { maxDepth, includeHidden, LANDMARK_TAGS, HEADING_TAGS, HERO_THRESHOLD, FOOTER_THRESHOLD, SIDEBAR_MAX_WIDTH });
262
-
263
- // Extract heading text and fontSize separately (reduces main traversal complexity)
264
- const headingData = await page.evaluate(({ headingTree, yTolerance }) => {
265
- return headingTree.map(h => {
266
- // Find heading by its position (nodeId is path-based, harder to query)
267
- const headings = document.querySelectorAll(`h${h.level}`);
268
- for (const el of headings) {
269
- const rect = el.getBoundingClientRect();
270
- const yPos = Math.round(rect.y + window.scrollY);
271
- // Match by Y position (within tolerance)
272
- if (Math.abs(yPos - h.y) < yTolerance) {
273
- const computed = window.getComputedStyle(el);
274
- return {
275
- ...h,
276
- text: el.textContent?.trim().slice(0, 60) || null,
277
- fontSize: parseFloat(computed.fontSize) || null
278
- };
279
- }
280
- }
281
- return h;
282
- });
283
- }, { headingTree: result.headingTree, yTolerance: Y_POSITION_TOLERANCE });
284
-
285
- result.headingTree = headingData;
286
-
287
- // Performance tracking
288
- const duration = Date.now() - startTime;
289
- if (duration > 500) {
290
- console.error(`[WARN] DOM hierarchy extraction took ${duration}ms (>500ms target)`);
291
- }
292
-
293
- result.stats.extractionTimeMs = duration;
294
-
295
- return result;
296
- }
297
-
298
- export { MAX_DEPTH, LANDMARK_TAGS, HEADING_TAGS };