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,92 @@
1
+ # UX Audit Prompt (Desktop Viewport)
2
+
3
+ Analyze this website screenshot for UX quality.
4
+
5
+ Evaluate these categories (score 0-100 each):
6
+
7
+ 1. VISUAL HIERARCHY
8
+ - Primary content prominence
9
+ - Clear scanning patterns (F/Z pattern)
10
+ - Call-to-action visibility
11
+ - Information grouping and prioritization
12
+ - White space utilization
13
+
14
+ 2. NAVIGATION
15
+ - Tappable area size (44x44px minimum for mobile)
16
+ - Current page indicator clarity
17
+ - Menu discoverability
18
+ - Breadcrumb/location awareness
19
+ - Navigation consistency
20
+
21
+ 3. TYPOGRAPHY
22
+ - Body text size (16px+ recommended)
23
+ - Line height (1.4-1.6 ideal)
24
+ - Contrast ratio (WCAG AA: 4.5:1 for text)
25
+ - Font hierarchy clarity
26
+ - Readability at viewport size
27
+
28
+ 4. SPACING
29
+ - Consistent padding/margins
30
+ - Element breathing room
31
+ - Touch target spacing (8px minimum between)
32
+ - Grid alignment
33
+ - Section separation
34
+
35
+ 5. INTERACTIVE ELEMENTS
36
+ - Button affordance (looks clickable)
37
+ - Link distinguishability
38
+ - Focus state visibility
39
+ - Hover state indication
40
+ - Form field clarity
41
+
42
+ 6. RESPONSIVE
43
+ - Content reflow appropriateness
44
+ - No horizontal scroll
45
+ - Image scaling quality
46
+ - Text truncation handling
47
+ - Breakpoint transitions
48
+
49
+ Return ONLY valid JSON in this exact format:
50
+ ```json
51
+ {
52
+ "viewport": "desktop",
53
+ "scores": {
54
+ "visual_hierarchy": 0,
55
+ "navigation": 0,
56
+ "typography": 0,
57
+ "spacing": 0,
58
+ "interactivity": 0,
59
+ "responsive": 0
60
+ },
61
+ "overall_ux_score": 0,
62
+ "accessibility_score": 0,
63
+ "issues": [
64
+ {
65
+ "category": "<visual_hierarchy|navigation|typography|spacing|interactivity|responsive>",
66
+ "severity": "<critical|major|minor>",
67
+ "issue": "<concise description>",
68
+ "fix": "<actionable suggestion>"
69
+ }
70
+ ],
71
+ "recommendations": ["<actionable improvement item>"]
72
+ }
73
+ ```
74
+
75
+ SEVERITY GUIDELINES:
76
+ - critical: Blocks user tasks or causes confusion (0-30 score range issues)
77
+ - major: Degrades experience significantly (31-60 score range issues)
78
+ - minor: Polish improvements (61-80 score range issues)
79
+
80
+ SCORING GUIDELINES:
81
+ - 90-100: Excellent, industry-leading UX
82
+ - 70-89: Good, meets modern standards
83
+ - 50-69: Adequate, room for improvement
84
+ - 30-49: Poor, significant issues
85
+ - 0-29: Critical, requires immediate attention
86
+
87
+ DESKTOP-SPECIFIC CHECKS:
88
+ - Maximum content width (1200-1440px ideal)
89
+ - Multi-column layout efficiency
90
+ - Hover states and micro-interactions
91
+ - Keyboard navigation support
92
+ - Large screen real estate utilization
@@ -0,0 +1,93 @@
1
+ # UX Audit Prompt (Mobile Viewport)
2
+
3
+ Analyze this website screenshot for UX quality.
4
+
5
+ Evaluate these categories (score 0-100 each):
6
+
7
+ 1. VISUAL HIERARCHY
8
+ - Primary content prominence
9
+ - Clear scanning patterns (F/Z pattern)
10
+ - Call-to-action visibility
11
+ - Information grouping and prioritization
12
+ - White space utilization
13
+
14
+ 2. NAVIGATION
15
+ - Tappable area size (44x44px minimum for mobile)
16
+ - Current page indicator clarity
17
+ - Menu discoverability
18
+ - Breadcrumb/location awareness
19
+ - Navigation consistency
20
+
21
+ 3. TYPOGRAPHY
22
+ - Body text size (16px+ recommended)
23
+ - Line height (1.4-1.6 ideal)
24
+ - Contrast ratio (WCAG AA: 4.5:1 for text)
25
+ - Font hierarchy clarity
26
+ - Readability at viewport size
27
+
28
+ 4. SPACING
29
+ - Consistent padding/margins
30
+ - Element breathing room
31
+ - Touch target spacing (8px minimum between)
32
+ - Grid alignment
33
+ - Section separation
34
+
35
+ 5. INTERACTIVE ELEMENTS
36
+ - Button affordance (looks clickable)
37
+ - Link distinguishability
38
+ - Focus state visibility
39
+ - Hover state indication
40
+ - Form field clarity
41
+
42
+ 6. RESPONSIVE
43
+ - Content reflow appropriateness
44
+ - No horizontal scroll
45
+ - Image scaling quality
46
+ - Text truncation handling
47
+ - Breakpoint transitions
48
+
49
+ Return ONLY valid JSON in this exact format:
50
+ ```json
51
+ {
52
+ "viewport": "mobile",
53
+ "scores": {
54
+ "visual_hierarchy": 0,
55
+ "navigation": 0,
56
+ "typography": 0,
57
+ "spacing": 0,
58
+ "interactivity": 0,
59
+ "responsive": 0
60
+ },
61
+ "overall_ux_score": 0,
62
+ "accessibility_score": 0,
63
+ "issues": [
64
+ {
65
+ "category": "<visual_hierarchy|navigation|typography|spacing|interactivity|responsive>",
66
+ "severity": "<critical|major|minor>",
67
+ "issue": "<concise description>",
68
+ "fix": "<actionable suggestion>"
69
+ }
70
+ ],
71
+ "recommendations": ["<actionable improvement item>"]
72
+ }
73
+ ```
74
+
75
+ SEVERITY GUIDELINES:
76
+ - critical: Blocks user tasks or causes confusion (0-30 score range issues)
77
+ - major: Degrades experience significantly (31-60 score range issues)
78
+ - minor: Polish improvements (61-80 score range issues)
79
+
80
+ SCORING GUIDELINES:
81
+ - 90-100: Excellent, industry-leading UX
82
+ - 70-89: Good, meets modern standards
83
+ - 50-69: Adequate, room for improvement
84
+ - 30-49: Poor, significant issues
85
+ - 0-29: Critical, requires immediate attention
86
+
87
+ MOBILE-SPECIFIC CHECKS:
88
+ - Touch targets minimum 44x44px
89
+ - Thumb zone accessibility
90
+ - Single-column layout efficiency
91
+ - Mobile navigation pattern (hamburger/tab bar)
92
+ - Text readable without zooming
93
+ - Forms optimized for mobile input
@@ -0,0 +1,92 @@
1
+ # UX Audit Prompt (Tablet Viewport)
2
+
3
+ Analyze this website screenshot for UX quality.
4
+
5
+ Evaluate these categories (score 0-100 each):
6
+
7
+ 1. VISUAL HIERARCHY
8
+ - Primary content prominence
9
+ - Clear scanning patterns (F/Z pattern)
10
+ - Call-to-action visibility
11
+ - Information grouping and prioritization
12
+ - White space utilization
13
+
14
+ 2. NAVIGATION
15
+ - Tappable area size (44x44px minimum for mobile)
16
+ - Current page indicator clarity
17
+ - Menu discoverability
18
+ - Breadcrumb/location awareness
19
+ - Navigation consistency
20
+
21
+ 3. TYPOGRAPHY
22
+ - Body text size (16px+ recommended)
23
+ - Line height (1.4-1.6 ideal)
24
+ - Contrast ratio (WCAG AA: 4.5:1 for text)
25
+ - Font hierarchy clarity
26
+ - Readability at viewport size
27
+
28
+ 4. SPACING
29
+ - Consistent padding/margins
30
+ - Element breathing room
31
+ - Touch target spacing (8px minimum between)
32
+ - Grid alignment
33
+ - Section separation
34
+
35
+ 5. INTERACTIVE ELEMENTS
36
+ - Button affordance (looks clickable)
37
+ - Link distinguishability
38
+ - Focus state visibility
39
+ - Hover state indication
40
+ - Form field clarity
41
+
42
+ 6. RESPONSIVE
43
+ - Content reflow appropriateness
44
+ - No horizontal scroll
45
+ - Image scaling quality
46
+ - Text truncation handling
47
+ - Breakpoint transitions
48
+
49
+ Return ONLY valid JSON in this exact format:
50
+ ```json
51
+ {
52
+ "viewport": "tablet",
53
+ "scores": {
54
+ "visual_hierarchy": 0,
55
+ "navigation": 0,
56
+ "typography": 0,
57
+ "spacing": 0,
58
+ "interactivity": 0,
59
+ "responsive": 0
60
+ },
61
+ "overall_ux_score": 0,
62
+ "accessibility_score": 0,
63
+ "issues": [
64
+ {
65
+ "category": "<visual_hierarchy|navigation|typography|spacing|interactivity|responsive>",
66
+ "severity": "<critical|major|minor>",
67
+ "issue": "<concise description>",
68
+ "fix": "<actionable suggestion>"
69
+ }
70
+ ],
71
+ "recommendations": ["<actionable improvement item>"]
72
+ }
73
+ ```
74
+
75
+ SEVERITY GUIDELINES:
76
+ - critical: Blocks user tasks or causes confusion (0-30 score range issues)
77
+ - major: Degrades experience significantly (31-60 score range issues)
78
+ - minor: Polish improvements (61-80 score range issues)
79
+
80
+ SCORING GUIDELINES:
81
+ - 90-100: Excellent, industry-leading UX
82
+ - 70-89: Good, meets modern standards
83
+ - 50-69: Adequate, room for improvement
84
+ - 30-49: Poor, significant issues
85
+ - 0-29: Critical, requires immediate attention
86
+
87
+ TABLET-SPECIFIC CHECKS:
88
+ - Two-column layout utilization
89
+ - Touch and mouse input support
90
+ - Landscape/portrait adaptability
91
+ - Sidebar vs content balance
92
+ - Split-view readiness
@@ -0,0 +1,183 @@
1
+ /**
2
+ * AST-based CSS animation extraction helpers.
3
+ *
4
+ * Single-pass css-tree AST walker that extracts @keyframes definitions,
5
+ * transition properties, and animation properties from parsed CSS.
6
+ * Used by animation-extractor.js (main module).
7
+ */
8
+
9
+ // ============================================================================
10
+ // Constants
11
+ // ============================================================================
12
+
13
+ /** CSS transition property names (standard + vendor prefixed) */
14
+ export const TRANSITION_PROPERTIES = new Set([
15
+ 'transition',
16
+ 'transition-property',
17
+ 'transition-duration',
18
+ 'transition-timing-function',
19
+ 'transition-delay',
20
+ '-webkit-transition'
21
+ ]);
22
+
23
+ /** CSS animation property names (standard + vendor prefixed) */
24
+ export const ANIMATION_PROPERTIES = new Set([
25
+ 'animation',
26
+ 'animation-name',
27
+ 'animation-duration',
28
+ 'animation-timing-function',
29
+ 'animation-delay',
30
+ 'animation-iteration-count',
31
+ 'animation-direction',
32
+ 'animation-fill-mode',
33
+ 'animation-play-state',
34
+ '-webkit-animation',
35
+ '-webkit-animation-name'
36
+ ]);
37
+
38
+ // ============================================================================
39
+ // Single-Pass AST Extraction (Performance Optimized)
40
+ // ============================================================================
41
+
42
+ /**
43
+ * Extract all animation-related data in a single AST walk.
44
+ * Optimized O(n) traversal instead of O(3n) with separate walks.
45
+ *
46
+ * @param {Object} csstree - css-tree module reference
47
+ * @param {Object} cssAst - css-tree parsed AST
48
+ * @returns {{keyframes: Object, transitions: Array, animatedElements: Array}}
49
+ */
50
+ export function extractAllFromAst(csstree, cssAst) {
51
+ const keyframes = {};
52
+ const transitions = [];
53
+ const animatedElements = [];
54
+
55
+ if (!csstree) {
56
+ return { keyframes, transitions, animatedElements };
57
+ }
58
+
59
+ csstree.walk(cssAst, {
60
+ enter(node) {
61
+ // Handle @keyframes rules
62
+ if (node.type === 'Atrule' &&
63
+ (node.name === 'keyframes' || node.name === '-webkit-keyframes')) {
64
+ const keyframeData = processKeyframeRule(csstree, node);
65
+ if (keyframeData) {
66
+ keyframes[keyframeData.name] = keyframeData.data;
67
+ }
68
+ return; // Don't descend into keyframes block
69
+ }
70
+
71
+ // Handle style rules (for transitions and animations)
72
+ if (node.type === 'Rule' && node.prelude && node.block) {
73
+ const ruleData = processStyleRule(csstree, node);
74
+ if (ruleData.transition) {
75
+ transitions.push(ruleData.transition);
76
+ }
77
+ if (ruleData.animation) {
78
+ animatedElements.push(ruleData.animation);
79
+ }
80
+ }
81
+ }
82
+ });
83
+
84
+ return { keyframes, transitions, animatedElements };
85
+ }
86
+
87
+ /**
88
+ * Process a @keyframes at-rule node.
89
+ *
90
+ * @param {Object} csstree - css-tree module reference
91
+ * @param {Object} node - css-tree Atrule node
92
+ * @returns {{name: string, data: import('./animation-extractor.js').KeyframeData}|null}
93
+ */
94
+ export function processKeyframeRule(csstree, node) {
95
+ // Get keyframe name from prelude
96
+ let name = null;
97
+
98
+ if (node.prelude) {
99
+ if (node.prelude.type === 'AtrulePrelude') {
100
+ // Walk prelude to find first Identifier
101
+ csstree.walk(node.prelude, {
102
+ visit: 'Identifier',
103
+ enter(idNode) {
104
+ if (!name) name = idNode.name;
105
+ }
106
+ });
107
+ } else if (node.prelude.type === 'Raw') {
108
+ name = node.prelude.value?.trim();
109
+ }
110
+ }
111
+
112
+ if (!name || !node.block) return null;
113
+
114
+ // Extract frames from keyframe block
115
+ const frames = [];
116
+ csstree.walk(node.block, {
117
+ visit: 'Rule',
118
+ enter(frameNode) {
119
+ const offset = csstree.generate(frameNode.prelude);
120
+ const properties = {};
121
+
122
+ if (frameNode.block) {
123
+ csstree.walk(frameNode.block, {
124
+ visit: 'Declaration',
125
+ enter(declNode) {
126
+ properties[declNode.property] = csstree.generate(declNode.value);
127
+ }
128
+ });
129
+ }
130
+
131
+ if (Object.keys(properties).length > 0) {
132
+ frames.push({ offset, properties });
133
+ }
134
+ }
135
+ });
136
+
137
+ return {
138
+ name,
139
+ data: {
140
+ frames,
141
+ raw: csstree.generate(node),
142
+ vendorPrefixed: node.name === '-webkit-keyframes'
143
+ }
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Process a style rule for transition and animation properties.
149
+ *
150
+ * @param {Object} csstree - css-tree module reference
151
+ * @param {Object} node - css-tree Rule node
152
+ * @returns {{transition: Object|null, animation: Object|null}}
153
+ */
154
+ export function processStyleRule(csstree, node) {
155
+ const selector = csstree.generate(node.prelude);
156
+ const transitionProps = {};
157
+ const animationProps = {};
158
+
159
+ // Extract declarations in a single walk
160
+ csstree.walk(node.block, {
161
+ visit: 'Declaration',
162
+ enter(declNode) {
163
+ const prop = declNode.property;
164
+ const value = csstree.generate(declNode.value);
165
+
166
+ if (TRANSITION_PROPERTIES.has(prop)) {
167
+ transitionProps[prop] = value;
168
+ }
169
+ if (ANIMATION_PROPERTIES.has(prop)) {
170
+ animationProps[prop] = value;
171
+ }
172
+ }
173
+ });
174
+
175
+ return {
176
+ transition: Object.keys(transitionProps).length > 0
177
+ ? { selector, ...transitionProps }
178
+ : null,
179
+ animation: Object.keys(animationProps).length > 0
180
+ ? { selector, ...animationProps }
181
+ : null
182
+ };
183
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * CSS animation output generators.
3
+ *
4
+ * Generates animations.css content from extracted keyframes and
5
+ * produces animation design tokens (durations, timing functions)
6
+ * for design-tokens.json. Used by animation-extractor.js (main module).
7
+ */
8
+
9
+ // ============================================================================
10
+ // Constants
11
+ // ============================================================================
12
+
13
+ /** Pattern to extract duration values (e.g., "200ms", "1.5s") */
14
+ const DURATION_PATTERN = /(\d+(?:\.\d+)?(?:ms|s))/g;
15
+
16
+ /** Pattern to extract cubic-bezier timing functions */
17
+ const CUBIC_BEZIER_PATTERN = /cubic-bezier\([^)]+\)/g;
18
+
19
+ /** Common timing function keywords to detect in shorthand values */
20
+ const TIMING_KEYWORDS = ['ease', 'linear', 'ease-in-out', 'ease-in', 'ease-out'];
21
+
22
+ // ============================================================================
23
+ // CSS Generation
24
+ // ============================================================================
25
+
26
+ /**
27
+ * Generate animations.css from extracted keyframes.
28
+ *
29
+ * @param {import('./animation-extractor.js').ExtractionResult} animationData - Result from extractAnimations()
30
+ * @returns {string} CSS string with @keyframes definitions
31
+ *
32
+ * @example
33
+ * const result = await extractAnimations(css);
34
+ * const animCss = generateAnimationsCss(result);
35
+ * fs.writeFileSync('animations.css', animCss);
36
+ */
37
+ export function generateAnimationsCss(animationData) {
38
+ const { keyframes } = animationData || {};
39
+
40
+ if (!keyframes || Object.keys(keyframes).length === 0) {
41
+ return '/* No @keyframes found */\n';
42
+ }
43
+
44
+ const lines = [
45
+ '/**',
46
+ ' * Extracted CSS Animations',
47
+ ' * Generated by design-clone animation-extractor',
48
+ ' */\n'
49
+ ];
50
+
51
+ for (const [name, data] of Object.entries(keyframes)) {
52
+ const frameCount = data.frames?.length || 0;
53
+ lines.push(`/* Keyframes: ${name} (${frameCount} frames) */`);
54
+ lines.push(data.raw);
55
+ lines.push('');
56
+ }
57
+
58
+ return lines.join('\n');
59
+ }
60
+
61
+ // ============================================================================
62
+ // Token Generation
63
+ // ============================================================================
64
+
65
+ /**
66
+ * Extract timing values from shorthand transition/animation strings.
67
+ *
68
+ * Limitations:
69
+ * - Only extracts first duration from shorthand (CSS allows multiple)
70
+ * - Timing function detection is keyword-based, may miss complex values
71
+ * - Does not resolve CSS variables (e.g., var(--duration))
72
+ *
73
+ * @param {string} shorthand - Shorthand property value
74
+ * @param {Set<string>} durations - Set to add durations to
75
+ * @param {Set<string>} timings - Set to add timing functions to
76
+ */
77
+ export function extractTimingFromShorthand(shorthand, durations, timings) {
78
+ if (!shorthand) return;
79
+
80
+ // Extract all duration values
81
+ const durationMatches = shorthand.match(DURATION_PATTERN);
82
+ if (durationMatches) {
83
+ durationMatches.forEach(d => durations.add(d));
84
+ }
85
+
86
+ // Extract cubic-bezier functions
87
+ const bezierMatches = shorthand.match(CUBIC_BEZIER_PATTERN);
88
+ if (bezierMatches) {
89
+ bezierMatches.forEach(b => timings.add(b));
90
+ }
91
+
92
+ // Check for timing keywords
93
+ const lowerShorthand = shorthand.toLowerCase();
94
+ for (const keyword of TIMING_KEYWORDS) {
95
+ if (lowerShorthand.includes(keyword)) {
96
+ timings.add(keyword);
97
+ }
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Generate animation tokens for design-tokens.json.
103
+ *
104
+ * @param {import('./animation-extractor.js').ExtractionResult} animationData - Result from extractAnimations()
105
+ * @returns {import('./animation-extractor.js').AnimationTokens} Animation tokens structure
106
+ *
107
+ * @example
108
+ * const result = await extractAnimations(css);
109
+ * const tokens = generateAnimationTokens(result);
110
+ * console.log(tokens.durations); // ['200ms', '300ms', '1s']
111
+ */
112
+ export function generateAnimationTokens(animationData) {
113
+ const { keyframes = {}, transitions = [], animatedElements = [] } = animationData || {};
114
+
115
+ const durations = new Set();
116
+ const timings = new Set();
117
+
118
+ // Extract from transitions
119
+ for (const t of transitions) {
120
+ if (t['transition-duration']) {
121
+ t['transition-duration'].split(',').forEach(d => durations.add(d.trim()));
122
+ }
123
+ if (t['transition-timing-function']) {
124
+ t['transition-timing-function'].split(',').forEach(tf => timings.add(tf.trim()));
125
+ }
126
+ if (t.transition) {
127
+ extractTimingFromShorthand(t.transition, durations, timings);
128
+ }
129
+ }
130
+
131
+ // Extract from animated elements
132
+ for (const a of animatedElements) {
133
+ if (a['animation-duration']) {
134
+ a['animation-duration'].split(',').forEach(d => durations.add(d.trim()));
135
+ }
136
+ if (a['animation-timing-function']) {
137
+ a['animation-timing-function'].split(',').forEach(tf => timings.add(tf.trim()));
138
+ }
139
+ if (a.animation) {
140
+ extractTimingFromShorthand(a.animation, durations, timings);
141
+ }
142
+ }
143
+
144
+ return {
145
+ keyframes: Object.keys(keyframes),
146
+ keyframeCount: Object.keys(keyframes).length,
147
+ transitions: transitions.length,
148
+ animatedElements: animatedElements.length,
149
+ durations: [...durations].sort(),
150
+ timingFunctions: [...timings].sort()
151
+ };
152
+ }