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
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Header Viewport Checks
3
+ *
4
+ * Orchestrates all per-viewport header checks using helpers.
5
+ * Separated from verify-header-helpers.js to keep each file under 200 lines.
6
+ */
7
+
8
+ import {
9
+ HEADER_SELECTORS,
10
+ findElement,
11
+ countVisibleElements,
12
+ checkHeaderPosition,
13
+ checkLogoPosition
14
+ } from './verify-header-helpers.js';
15
+
16
+ /**
17
+ * Test header at a specific viewport — runs all header checks
18
+ * @param {import('playwright').Page} page
19
+ * @param {string} viewportName
20
+ * @param {Object} VIEWPORTS - Viewport map
21
+ * @param {boolean} verbose
22
+ * @returns {Promise<Object>} Viewport result object
23
+ */
24
+ export async function testHeaderViewport(page, viewportName, VIEWPORTS, verbose = false) {
25
+ const viewport = VIEWPORTS[viewportName];
26
+ await page.setViewportSize(viewport);
27
+ await new Promise(r => setTimeout(r, 500));
28
+
29
+ const result = { viewport: viewportName, dimensions: viewport, tests: [], passed: 0, failed: 0, warnings: [] };
30
+ if (verbose) console.error(`\n📱 Testing ${viewportName} (${viewport.width}x${viewport.height})...`);
31
+
32
+ const headerResult = await findElement(page, HEADER_SELECTORS.container);
33
+ if (!headerResult) {
34
+ result.tests.push({ name: 'Header container exists', passed: false, error: 'No header container found' });
35
+ result.failed++;
36
+ if (verbose) console.error(` ✗ Header not found`);
37
+ return result;
38
+ }
39
+
40
+ result.tests.push({ name: 'Header container exists', passed: true, selector: headerResult.selector });
41
+ result.passed++;
42
+ if (verbose) console.error(` ✓ Header found: ${headerResult.selector}`);
43
+
44
+ const positionInfo = await checkHeaderPosition(page, headerResult.selector);
45
+
46
+ const logoResult = await findElement(page, HEADER_SELECTORS.logo);
47
+ if (logoResult) {
48
+ const logoPosition = await checkLogoPosition(page, logoResult.selector, viewport.width);
49
+ result.tests.push({ name: 'Logo present', passed: true, selector: logoResult.selector, position: logoPosition?.position || 'unknown' });
50
+ result.passed++;
51
+ if (verbose) console.error(` ✓ Logo found: ${logoResult.selector} (${logoPosition?.position})`);
52
+ } else {
53
+ result.tests.push({ name: 'Logo present', passed: false, error: 'No logo found' });
54
+ result.failed++;
55
+ if (verbose) console.error(` ✗ Logo not found`);
56
+ }
57
+
58
+ const navLinks = await countVisibleElements(page, HEADER_SELECTORS.navLinks);
59
+ const expectedLinks = viewportName === 'desktop' ? 2 : 0;
60
+
61
+ if (navLinks.count >= expectedLinks) {
62
+ result.tests.push({ name: 'Navigation links visible', passed: true, count: navLinks.count, selector: navLinks.selector });
63
+ result.passed++;
64
+ if (verbose) console.error(` ✓ ${navLinks.count} nav links visible`);
65
+ } else if (viewportName !== 'desktop' && navLinks.count === 0) {
66
+ result.tests.push({ name: 'Navigation links (may be in hamburger)', passed: true, count: navLinks.count, note: 'Links may be hidden in mobile menu' });
67
+ result.passed++;
68
+ if (verbose) console.error(` ✓ Nav links hidden (expected on ${viewportName})`);
69
+ } else {
70
+ result.tests.push({ name: 'Navigation links visible', passed: false, count: navLinks.count, error: `Expected at least ${expectedLinks} links on ${viewportName}` });
71
+ result.failed++;
72
+ if (verbose) console.error(` ✗ Only ${navLinks.count} nav links (expected >= ${expectedLinks})`);
73
+ }
74
+
75
+ if (viewportName === 'desktop') {
76
+ const ctaResult = await findElement(page, HEADER_SELECTORS.cta);
77
+ if (ctaResult) {
78
+ result.tests.push({ name: 'CTA button present', passed: true, selector: ctaResult.selector });
79
+ result.passed++;
80
+ if (verbose) console.error(` ✓ CTA found: ${ctaResult.selector}`);
81
+ } else {
82
+ result.warnings.push('No CTA button found in header');
83
+ if (verbose) console.error(` ⚠ No CTA button found`);
84
+ }
85
+ }
86
+
87
+ if (positionInfo) {
88
+ result.tests.push({ name: 'Header sticky/fixed behavior', passed: true, position: positionInfo.position, note: (!positionInfo.isSticky && !positionInfo.isFixed) ? 'Header uses static/relative positioning' : undefined });
89
+ result.passed++;
90
+ if (verbose) console.error(` ✓ Header position: ${positionInfo.position}`);
91
+
92
+ if ((positionInfo.isSticky || positionInfo.isFixed) && positionInfo.zIndex !== 'auto') {
93
+ const zIndexOk = positionInfo.zIndex >= 100;
94
+ result.tests.push({ name: 'Z-index layering', passed: zIndexOk, zIndex: positionInfo.zIndex, note: zIndexOk ? 'Header on top layer' : 'Z-index may be too low' });
95
+ if (zIndexOk) result.passed++;
96
+ else result.warnings.push(`Header z-index (${positionInfo.zIndex}) may be too low`);
97
+ if (verbose) console.error(` ${zIndexOk ? '✓' : '⚠'} Z-index: ${positionInfo.zIndex}`);
98
+ }
99
+
100
+ result.headerHeight = positionInfo.height;
101
+ }
102
+
103
+ return result;
104
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Header Verification Helpers
3
+ *
4
+ * Selectors, DOM query utilities, and header-specific checks extracted
5
+ * from verify-header.js to keep each file under 200 lines.
6
+ */
7
+
8
+ // Header element selectors
9
+ export const HEADER_SELECTORS = {
10
+ container: [
11
+ 'header',
12
+ '[role="banner"]',
13
+ '.header',
14
+ '#header',
15
+ '.site-header',
16
+ '.page-header',
17
+ '.masthead'
18
+ ],
19
+ logo: [
20
+ 'header img[alt*="logo" i]',
21
+ '[role="banner"] img',
22
+ '.logo img',
23
+ '.site-logo img',
24
+ '.logo',
25
+ '.site-logo',
26
+ 'header a[href="/"] img',
27
+ '.brand img',
28
+ '.navbar-brand img'
29
+ ],
30
+ nav: [
31
+ 'header nav',
32
+ 'header [role="navigation"]',
33
+ '.header-nav',
34
+ '.main-navigation',
35
+ '.primary-nav',
36
+ '.site-nav',
37
+ '.navbar-nav'
38
+ ],
39
+ cta: [
40
+ 'header button.cta',
41
+ 'header a[class*="button"]',
42
+ 'header a[class*="btn"]',
43
+ '.header-action',
44
+ '.nav-cta',
45
+ 'header .btn-primary',
46
+ 'header a[href*="contact"]',
47
+ 'header a[href*="signup"]',
48
+ 'header a[href*="login"]'
49
+ ],
50
+ navLinks: [
51
+ 'header nav a',
52
+ 'header [role="navigation"] a',
53
+ '.main-navigation a',
54
+ '.nav-item a',
55
+ '.menu-item a'
56
+ ]
57
+ };
58
+
59
+ /**
60
+ * Find first matching element from a list of selectors
61
+ * @param {import('playwright').Page} page
62
+ * @param {string[]} selectors
63
+ * @returns {Promise<{element: ElementHandle, selector: string}|null>}
64
+ */
65
+ export async function findElement(page, selectors) {
66
+ for (const selector of selectors) {
67
+ try {
68
+ const element = await page.$(selector);
69
+ if (element) return { element, selector };
70
+ } catch (err) { /* continue */ }
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Count visible elements (returns first non-zero match)
77
+ * @param {import('playwright').Page} page
78
+ * @param {string[]} selectors
79
+ * @returns {Promise<{count: number, selector: string|null}>}
80
+ */
81
+ export async function countVisibleElements(page, selectors) {
82
+ for (const selector of selectors) {
83
+ try {
84
+ const count = await page.evaluate((sel) => {
85
+ const items = document.querySelectorAll(sel);
86
+ let visible = 0;
87
+ items.forEach(item => {
88
+ const style = window.getComputedStyle(item);
89
+ const rect = item.getBoundingClientRect();
90
+ if (
91
+ style.display !== 'none' &&
92
+ style.visibility !== 'hidden' &&
93
+ style.opacity !== '0' &&
94
+ rect.width > 0 &&
95
+ rect.height > 0
96
+ ) visible++;
97
+ });
98
+ return visible;
99
+ }, selector);
100
+
101
+ if (count > 0) return { count, selector };
102
+ } catch (err) { /* continue */ }
103
+ }
104
+ return { count: 0, selector: null };
105
+ }
106
+
107
+ /**
108
+ * Check header position/stickiness properties
109
+ * @param {import('playwright').Page} page
110
+ * @param {string} headerSelector
111
+ * @returns {Promise<{position: string, isSticky: boolean, isFixed: boolean, zIndex: number|string, top: number, height: number, width: number}|null>}
112
+ */
113
+ export async function checkHeaderPosition(page, headerSelector) {
114
+ return await page.evaluate((sel) => {
115
+ const header = document.querySelector(sel);
116
+ if (!header) return null;
117
+
118
+ const style = window.getComputedStyle(header);
119
+ const rect = header.getBoundingClientRect();
120
+
121
+ return {
122
+ position: style.position,
123
+ isSticky: style.position === 'sticky',
124
+ isFixed: style.position === 'fixed',
125
+ zIndex: parseInt(style.zIndex) || 'auto',
126
+ top: rect.top,
127
+ height: rect.height,
128
+ width: rect.width
129
+ };
130
+ }, headerSelector);
131
+ }
132
+
133
+ /**
134
+ * Check logo position relative to header width
135
+ * @param {import('playwright').Page} page
136
+ * @param {string} logoSelector
137
+ * @param {number} headerWidth
138
+ * @returns {Promise<{position: string, x: number, y: number, width: number, height: number}|null>}
139
+ */
140
+ export async function checkLogoPosition(page, logoSelector, headerWidth) {
141
+ return await page.evaluate((sel, width) => {
142
+ const logo = document.querySelector(sel);
143
+ if (!logo) return null;
144
+
145
+ const rect = logo.getBoundingClientRect();
146
+ const centerThreshold = width * 0.35;
147
+
148
+ let position = 'unknown';
149
+ if (rect.left < centerThreshold) position = 'left';
150
+ else if (rect.left > width - centerThreshold) position = 'right';
151
+ else position = 'center';
152
+
153
+ return { position, x: rect.left, y: rect.top, width: rect.width, height: rect.height };
154
+ }, logoSelector, headerWidth);
155
+ }
156
+