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
@@ -0,0 +1,789 @@
1
+ # Design Clone Code Standards & Guidelines
2
+
3
+ **Version:** 2.1.0
4
+ **Last Updated:** February 23, 2026
5
+
6
+ ## Overview
7
+
8
+ This document establishes code quality standards, architectural patterns, and development guidelines for the Design Clone project. All contributions must follow these standards to maintain consistency, readability, and long-term maintainability.
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ 1. [JavaScript/Node.js Standards](#javascriptnodejs-standards)
15
+ 2. [Python Standards](#python-standards)
16
+ 3. [Project Structure](#project-structure)
17
+ 4. [Error Handling](#error-handling)
18
+ 5. [Testing Standards](#testing-standards)
19
+ 6. [Documentation Standards](#documentation-standards)
20
+ 7. [Performance Guidelines](#performance-guidelines)
21
+ 8. [Security Standards](#security-standards)
22
+
23
+ ---
24
+
25
+ ## JavaScript/Node.js Standards
26
+
27
+ ### Code Style
28
+
29
+ **File Format:**
30
+ ```javascript
31
+ /**
32
+ * Brief description of what this file does.
33
+ * @module moduleName
34
+ */
35
+
36
+ 'use strict';
37
+
38
+ // Imports (group: external, relative)
39
+ const path = require('path');
40
+ const fs = require('fs').promises;
41
+ const { parseUrl } = require('../utils/url-parser');
42
+
43
+ // Constants
44
+ const DEFAULT_TIMEOUT = 5000;
45
+ const SCREENSHOT_WIDTH = 1920;
46
+
47
+ // Types/Interfaces (JSDoc style)
48
+ /**
49
+ * @typedef {Object} ScreenshotOptions
50
+ * @property {string} url - Target URL
51
+ * @property {string} output - Output directory
52
+ * @property {number} timeout - Operation timeout in ms
53
+ */
54
+
55
+ // Main exports
56
+ module.exports = {
57
+ captureScreenshots,
58
+ filterCSS,
59
+ validateOutput,
60
+ };
61
+
62
+ // Implementation
63
+ async function captureScreenshots(url, options) {
64
+ // ...
65
+ }
66
+ ```
67
+
68
+ **Naming Conventions:**
69
+ ```javascript
70
+ // Constants: UPPER_SNAKE_CASE
71
+ const MAX_RETRIES = 3;
72
+ const DEFAULT_VIEWPORT_WIDTH = 1920;
73
+
74
+ // Functions: camelCase
75
+ function extractDesignTokens() {}
76
+ const validateUrl = () => {};
77
+
78
+ // Classes: PascalCase
79
+ class PlaywrightBrowser {
80
+ constructor() {}
81
+ }
82
+
83
+ // Private methods: _leadingUnderscore or #privateField
84
+ class BrowserPool {
85
+ #browsers = [];
86
+ _initializeBrowser() {}
87
+ }
88
+
89
+ // Variables: camelCase
90
+ let currentState = null;
91
+ const outputDirectory = './output';
92
+ ```
93
+
94
+ **Formatting Rules:**
95
+ - Line length: 100 characters maximum
96
+ - Indentation: 2 spaces
97
+ - Semicolons: Required
98
+ - Quotes: Single quotes for strings
99
+ - Arrow functions: Use for callbacks
100
+ - Async/await: Prefer over .then() chains
101
+
102
+ ```javascript
103
+ // Good
104
+ const results = await Promise.all(
105
+ screenshots.map(async (shot) => {
106
+ return await processScreenshot(shot);
107
+ })
108
+ );
109
+
110
+ // Bad
111
+ const results = await Promise.all(screenshots.map(ss => {
112
+ return processScreenshot(ss);
113
+ }));
114
+ ```
115
+
116
+ ### Function Structure
117
+
118
+ **Pattern:**
119
+ ```javascript
120
+ /**
121
+ * Brief one-line description.
122
+ *
123
+ * Detailed explanation if needed. Describe the algorithm or key behavior.
124
+ *
125
+ * @param {string} inputPath - Description of param
126
+ * @param {Object} options - Configuration object
127
+ * @param {boolean} options.verbose - Enable verbose logging
128
+ * @param {number} options.timeout - Timeout in milliseconds
129
+ * @returns {Promise<Object>} Returns processed data
130
+ * @throws {Error} When input is invalid
131
+ *
132
+ * @example
133
+ * const result = await processFile('path/to/file.html', { verbose: true });
134
+ */
135
+ async function processFile(inputPath, options = {}) {
136
+ // 1. Input validation
137
+ if (!inputPath || typeof inputPath !== 'string') {
138
+ throw new Error('inputPath must be non-empty string');
139
+ }
140
+
141
+ // 2. Setup/initialization
142
+ const config = {
143
+ timeout: 5000,
144
+ ...options,
145
+ };
146
+
147
+ // 3. Main logic
148
+ try {
149
+ const data = await readFile(inputPath);
150
+ const processed = transform(data);
151
+ return processed;
152
+ } catch (error) {
153
+ // 4. Error handling
154
+ if (error.code === 'ENOENT') {
155
+ throw new Error(`File not found: ${inputPath}`);
156
+ }
157
+ throw error;
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Control Flow
163
+
164
+ **Conditionals:**
165
+ ```javascript
166
+ // Good: Early return
167
+ if (!value) return null;
168
+ if (typeof x !== 'string') throw new Error('Must be string');
169
+
170
+ // Guard clauses
171
+ function validate(input) {
172
+ if (!input) return false;
173
+ if (!input.trim()) return false;
174
+ if (input.length > 1000) return false;
175
+ return true;
176
+ }
177
+
178
+ // Ternary for simple logic
179
+ const status = isActive ? 'running' : 'stopped';
180
+
181
+ // Avoid nested ternary
182
+ // Bad: status === 'pending' ? 'waiting' : status === 'done' ? 'complete' : 'error'
183
+ // Good: Use switch or if-else
184
+ ```
185
+
186
+ **Loops & Iteration:**
187
+ ```javascript
188
+ // Prefer forEach for side effects
189
+ items.forEach((item) => {
190
+ console.log(item);
191
+ });
192
+
193
+ // Prefer map for transformations
194
+ const doubled = numbers.map((n) => n * 2);
195
+
196
+ // Prefer filter for selection
197
+ const active = users.filter((u) => u.isActive);
198
+
199
+ // Use for-of for breaking early
200
+ for (const item of items) {
201
+ if (shouldStop(item)) break;
202
+ processItem(item);
203
+ }
204
+
205
+ // Avoid traditional for loops unless necessary
206
+ // Bad: for (let i = 0; i < items.length; i++)
207
+ // Good: for (const item of items)
208
+ ```
209
+
210
+ ### Error Handling
211
+
212
+ **Pattern:**
213
+ ```javascript
214
+ /**
215
+ * Executes operation with retry logic.
216
+ *
217
+ * @param {Function} operation - Async function to retry
218
+ * @param {number} maxRetries - Maximum retry attempts (default: 3)
219
+ * @returns {Promise<*>} Result of successful operation
220
+ */
221
+ async function executeWithRetry(operation, maxRetries = 3) {
222
+ let lastError;
223
+
224
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
225
+ try {
226
+ return await operation();
227
+ } catch (error) {
228
+ lastError = error;
229
+
230
+ if (attempt < maxRetries) {
231
+ const delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
232
+ console.warn(
233
+ `Attempt ${attempt} failed: ${error.message}. Retrying in ${delay}ms...`
234
+ );
235
+ await sleep(delay);
236
+ }
237
+ }
238
+ }
239
+
240
+ throw new Error(
241
+ `Operation failed after ${maxRetries} attempts: ${lastError.message}`
242
+ );
243
+ }
244
+ ```
245
+
246
+ **Custom Errors:**
247
+ ```javascript
248
+ class ValidationError extends Error {
249
+ constructor(message, context = {}) {
250
+ super(message);
251
+ this.name = 'ValidationError';
252
+ this.context = context;
253
+ }
254
+ }
255
+
256
+ class TimeoutError extends Error {
257
+ constructor(operation, timeout) {
258
+ super(`${operation} exceeded timeout of ${timeout}ms`);
259
+ this.name = 'TimeoutError';
260
+ this.timeout = timeout;
261
+ }
262
+ }
263
+
264
+ // Usage
265
+ if (!isValid(input)) {
266
+ throw new ValidationError('Invalid input format', { input, expected });
267
+ }
268
+ ```
269
+
270
+ ### Async/Promises
271
+
272
+ **Always use async/await:**
273
+ ```javascript
274
+ // Good
275
+ async function fetchData() {
276
+ const response = await fetch(url);
277
+ const data = await response.json();
278
+ return data;
279
+ }
280
+
281
+ // Avoid bare promises
282
+ // Bad: function fetchData() { return fetch(url).then(...) }
283
+ ```
284
+
285
+ **Parallel operations:**
286
+ ```javascript
287
+ // Good: Parallel with Promise.all
288
+ const [screenshots, css, html] = await Promise.all([
289
+ captureScreenshots(url),
290
+ extractCSS(url),
291
+ extractHTML(url),
292
+ ]);
293
+
294
+ // Sequential when dependent
295
+ const tokens = await extractDesignTokens(url);
296
+ const css = await generateCSS(tokens);
297
+ ```
298
+
299
+ **Timeout handling:**
300
+ ```javascript
301
+ async function withTimeout(promise, timeoutMs) {
302
+ return Promise.race([
303
+ promise,
304
+ new Promise((_, reject) =>
305
+ setTimeout(() => reject(new TimeoutError('Operation', timeoutMs)), timeoutMs)
306
+ ),
307
+ ]);
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Python Standards
314
+
315
+ ### Code Style
316
+
317
+ **File Format:**
318
+ ```python
319
+ #!/usr/bin/env python3
320
+ """
321
+ Module docstring: Brief description of module purpose.
322
+
323
+ This module handles [description of main responsibility].
324
+ """
325
+
326
+ import sys
327
+ import json
328
+ from pathlib import Path
329
+ from typing import Dict, List, Optional
330
+
331
+ # Constants
332
+ DEFAULT_TIMEOUT = 5000
333
+ FIGMA_API_BASE = 'https://api.figma.com/v1'
334
+
335
+ # Helper functions
336
+ def validate_token(token: str) -> bool:
337
+ """Check if token format is valid."""
338
+ return bool(token and len(token) > 10)
339
+
340
+ # Main classes
341
+ class FigmaClient:
342
+ """Figma REST API client."""
343
+
344
+ def __init__(self, access_token: str):
345
+ """Initialize with access token."""
346
+ if not validate_token(access_token):
347
+ raise ValueError('Invalid access token format')
348
+ self.token = access_token
349
+ ```
350
+
351
+ **Naming Conventions:**
352
+ ```python
353
+ # Constants: UPPER_SNAKE_CASE
354
+ MAX_RETRIES = 3
355
+ DEFAULT_TIMEOUT = 5000
356
+
357
+ # Functions & methods: snake_case
358
+ def extract_design_tokens(file_path):
359
+ pass
360
+
361
+ def validate_figma_url(url):
362
+ pass
363
+
364
+ # Classes: PascalCase
365
+ class FigmaClient:
366
+ pass
367
+
368
+ class DesignTokenExtractor:
369
+ pass
370
+
371
+ # Private: Leading underscore
372
+ def _internal_helper():
373
+ pass
374
+
375
+ class MyClass:
376
+ def _private_method(self):
377
+ pass
378
+ ```
379
+
380
+ **Type Hints (Required):**
381
+ ```python
382
+ from typing import Dict, List, Optional, Tuple
383
+
384
+ def process_tokens(
385
+ data: Dict[str, any],
386
+ output_path: str,
387
+ verbose: bool = False
388
+ ) -> Dict[str, List[str]]:
389
+ """Process design tokens."""
390
+ return {'colors': [], 'fonts': []}
391
+
392
+ def extract_color(rgba: Dict[str, float]) -> str:
393
+ """Convert RGBA dict to hex color."""
394
+ pass
395
+ ```
396
+
397
+ **Formatting Rules:**
398
+ - Line length: 100 characters
399
+ - Indentation: 4 spaces
400
+ - Docstrings: Google style
401
+ - Type hints: All function signatures
402
+ - Blank lines: 2 between top-level definitions, 1 within class
403
+
404
+ ### Function Structure
405
+
406
+ **Pattern (condensed):**
407
+ ```python
408
+ def extract_design_tokens(figma_data: Dict, output_dir: str) -> Dict:
409
+ """Extract colors, typography, spacing from Figma file."""
410
+ if not isinstance(figma_data, dict):
411
+ raise ValueError('figma_data must be dictionary')
412
+
413
+ tokens = {'colors': {}, 'typography': {}, 'spacing': {}}
414
+ tokens['colors'] = _extract_colors(figma_data)
415
+ return tokens
416
+
417
+ def _extract_colors(data: Dict) -> Dict[str, str]:
418
+ """Extract color palette from design."""
419
+ colors = {}
420
+ # Implementation
421
+ return colors
422
+ ```
423
+
424
+ ### Error Handling
425
+
426
+ **Pattern:**
427
+ ```python
428
+ class FigmaError(Exception):
429
+ """Base exception for Figma operations."""
430
+ pass
431
+
432
+ class FigmaAuthError(FigmaError):
433
+ """Raised when authentication fails."""
434
+ pass
435
+
436
+ class FigmaAPIError(FigmaError):
437
+ """Raised when API request fails."""
438
+ def __init__(self, message: str, status_code: int = None):
439
+ super().__init__(message)
440
+ self.status_code = status_code
441
+
442
+ def authenticate(token: str) -> bool:
443
+ """Authenticate with Figma API."""
444
+ try:
445
+ response = requests.get(
446
+ 'https://api.figma.com/v1/me',
447
+ headers={'X-Figma-Token': token}
448
+ )
449
+ response.raise_for_status()
450
+ return True
451
+ except requests.exceptions.ConnectionError as e:
452
+ raise FigmaError(f'Network error: {e}')
453
+ except requests.exceptions.HTTPError as e:
454
+ if e.response.status_code == 401:
455
+ raise FigmaAuthError('Invalid token')
456
+ raise FigmaAPIError(str(e), e.response.status_code)
457
+ ```
458
+
459
+ ---
460
+
461
+ ## Project Structure
462
+
463
+ ### Directory Organization
464
+
465
+ ```
466
+ design-clone/
467
+ ├── bin/
468
+ │ ├── cli.js # Main CLI entry
469
+ │ ├── commands/
470
+ │ │ ├── clone-site.js # Multi-page command
471
+ │ │ ├── init.js # Setup command
472
+ │ │ └── verify.js # Verification command
473
+ │ └── utils/
474
+ │ ├── copy.js # Utility functions
475
+ │ └── validate.js
476
+
477
+ ├── src/
478
+ │ ├── core/ # Core extraction engines (12 semantic subdirectories)
479
+ │ │ ├── capture/ # Screenshot pipeline (7 modules)
480
+ │ │ ├── css/ # CSS processing (7 modules)
481
+ │ │ ├── html/ # HTML extraction (5 modules)
482
+ │ │ ├── animation/ # Animation & hover states (5 modules)
483
+ │ │ ├── discovery/ # Page discovery (6 modules)
484
+ │ │ ├── detection/ # Framework detection (3 modules)
485
+ │ │ ├── dimension/ # DOM analysis (6 modules)
486
+ │ │ ├── section/ # Section detection (5 modules)
487
+ │ │ ├── media/ # Asset extraction (5 modules)
488
+ │ │ ├── page-prep/ # Page readiness (3 modules)
489
+ │ │ ├── content/ # Content analysis (2 modules)
490
+ │ │ ├── links/ # URL rewriting (2 modules)
491
+ │ │ └── tests/ # Core tests (2 modules)
492
+ │ │
493
+ │ ├── figma/ # Figma-to-code pipeline
494
+ │ │ ├── parse-url.js # URL parsing
495
+ │ │ ├── figma-client.py # API client
496
+ │ │ ├── extract-figma.py # Token extraction
497
+ │ │ ├── generate-css.py # BEM generation
498
+ │ │ └── generate-tailwind.py # Tailwind generation
499
+ │ │
500
+ │ ├── ai/ # AI analysis
501
+ │ │ ├── analyze-structure.py
502
+ │ │ ├── extract-design-tokens.py
503
+ │ │ └── prompts/
504
+ │ │ ├── structure_analysis.py
505
+ │ │ ├── design_tokens.py
506
+ │ │ └── ux_audit.py
507
+ │ │
508
+ │ ├── verification/ # Quality checks
509
+ │ │ ├── verify-menu.js
510
+ │ │ ├── verify-layout.js
511
+ │ │ └── [other verifications]
512
+ │ │
513
+ │ ├── post-process/ # Asset processing
514
+ │ │ ├── fetch-images.js
515
+ │ │ ├── inject-icons.js
516
+ │ │ └── enhance-assets.js
517
+ │ │
518
+ │ ├── utils/ # Shared utilities
519
+ │ │ ├── browser.js
520
+ │ │ ├── env.js / env.py
521
+ │ │ ├── helpers.js
522
+ │ │ ├── playwright.js
523
+ │ │ └── log.js # Centralized logging (Feb 23)
524
+ │ │
525
+ │ └── route-discoverers/ # Framework detection
526
+ │ ├── base-discoverer.js
527
+ │ ├── react-discoverer.js
528
+ │ └── [other frameworks]
529
+
530
+ ├── tests/ # Test files
531
+ │ ├── unit/
532
+ │ ├── integration/
533
+ │ └── fixtures/
534
+
535
+ ├── templates/ # HTML/CSS templates
536
+ │ ├── base.html
537
+ │ └── base.css
538
+
539
+ ├── docs/ # Documentation
540
+ └── prd/ # Product requirements
541
+ ```
542
+
543
+ ### Module Dependencies
544
+
545
+ **Guidelines:**
546
+ - Minimize circular dependencies
547
+ - Organize imports: external → local
548
+ - Separate concerns across modules
549
+ - Use dependency injection for testability
550
+ - Import directly from module files using relative paths
551
+ - Use centralized logging (src/utils/log.js) instead of console.log
552
+
553
+ **Companion Module Pattern (Feb 23):**
554
+
555
+ When a module grows beyond 150-200 LOC with repetitive logic, decompose using companion modules:
556
+ - `module.js` - Main orchestrator (stable API)
557
+ - `module-feature.js` - Specific feature extraction
558
+ - `module-output.js` - Output formatting
559
+ - `module-mappings.js` - Mapping tables/rules
560
+
561
+ **Examples:**
562
+ - `semantic-enhancer.js` → `semantic-enhancer-page.js`, `semantic-enhancer-mappings.js`
563
+ - `dimension-extractor.js` → `dimension-extractor-card-detector.js`, `dimension-output.js`, `dimension-output-ai-summary.js`
564
+
565
+ **Example Dependency Structure:**
566
+ ```
567
+ CLI Layer
568
+
569
+ Workflow Layer (clone, clone-px, clone-site, figma-to-code)
570
+
571
+ Core Engines (capture/, css/, discovery/, detection/, etc. via direct imports)
572
+
573
+ Utilities (browser, env, helpers, log, playwright)
574
+ ```
575
+
576
+ ---
577
+
578
+ ## Error Handling
579
+
580
+ ### Error Classification
581
+
582
+ **User Errors (exit code 1):**
583
+ ```javascript
584
+ // Invalid input
585
+ if (!isValidUrl(url)) {
586
+ console.error(`Error: Invalid URL format: ${url}`);
587
+ process.exit(1);
588
+ }
589
+
590
+ // Missing configuration
591
+ if (!process.env.FIGMA_ACCESS_TOKEN) {
592
+ console.error('Error: FIGMA_ACCESS_TOKEN environment variable not set');
593
+ process.exit(1);
594
+ }
595
+ ```
596
+
597
+ **System Errors (exit code 2):**
598
+ ```javascript
599
+ // Browser crash
600
+ if (!browser || !browser.isConnected()) {
601
+ console.error('Error: Browser disconnected unexpectedly');
602
+ process.exit(2);
603
+ }
604
+
605
+ // Disk I/O failure
606
+ catch (error) {
607
+ if (error.code === 'ENOSPC') {
608
+ console.error('Error: Disk space exhausted');
609
+ process.exit(2);
610
+ }
611
+ }
612
+ ```
613
+
614
+ **Unknown Errors (exit code 3):**
615
+ ```javascript
616
+ process.on('unhandledRejection', (reason) => {
617
+ console.error('Unhandled rejection:', reason);
618
+ process.exit(3);
619
+ });
620
+ ```
621
+
622
+ ### Error Messages
623
+
624
+ **Format:**
625
+ ```
626
+ Error: [Brief description]
627
+ Context: [Relevant details]
628
+ Suggestion: [How to fix]
629
+
630
+ Example:
631
+ Error: Invalid Figma URL format
632
+ Context: URL must contain /design/ or /file/ path
633
+ Suggestion: Use: figma.com/design/{file_key}/...
634
+ ```
635
+
636
+ ---
637
+
638
+ ## Testing Standards
639
+
640
+ ### Test Structure
641
+
642
+ **File naming:** `src/core/capture/screenshot.js` → `tests/unit/screenshot.test.js`
643
+
644
+ **Template (JavaScript):**
645
+ ```javascript
646
+ describe('ScreenshotCapture', () => {
647
+ it('should capture all three viewports', async () => {
648
+ const screenshots = await captureViewports('https://example.com');
649
+ expect(screenshots).toHaveLength(3);
650
+ });
651
+ });
652
+ ```
653
+
654
+ **Template (Python):**
655
+ ```python
656
+ def test_extract_colors(self):
657
+ tokens = extract_design_tokens(sample_data)
658
+ assert 'colors' in tokens
659
+ ```
660
+
661
+ **Coverage:** Minimum 85% statement coverage, 100% for critical paths, integration tests for workflows
662
+
663
+ ---
664
+
665
+ ## Documentation Standards
666
+
667
+ ### Code Comments
668
+
669
+ **Use comments for WHY, not WHAT:**
670
+ ```javascript
671
+ // Bad
672
+ // Increment counter
673
+ counter++;
674
+
675
+ // Good
676
+ // Reset counter for next batch processing cycle
677
+ counter++;
678
+
679
+ // Good: Explaining non-obvious logic
680
+ // Playwright requires minimum 100ms delay between viewport changes
681
+ // to avoid race conditions in CSS media query evaluation
682
+ await page.waitForTimeout(100);
683
+ ```
684
+
685
+ **Block comments for complex algorithms:**
686
+ ```python
687
+ def extract_colors(nodes):
688
+ """
689
+ Extract unique colors from Figma nodes.
690
+
691
+ Algorithm:
692
+ 1. Traverse all nodes recursively
693
+ 2. For each node, check fills and strokes
694
+ 3. Convert RGBA to hex, normalize to #RRGGBB
695
+ 4. Deduplicate using case-insensitive matching
696
+ 5. Categorize by brightness (light/dark)
697
+ 6. Assign semantic names (primary, secondary, etc.)
698
+ """
699
+ colors = {}
700
+ # Implementation...
701
+ return colors
702
+ ```
703
+
704
+ ### JSDoc/Docstrings
705
+
706
+ **Required for all public APIs:**
707
+ ```javascript
708
+ /**
709
+ * Extract CSS rules used in HTML.
710
+ *
711
+ * Analyzes HTML for all used selectors and matches them against
712
+ * CSS rules. Removes declarations that don't match any selector
713
+ * while preserving media queries and keyframes.
714
+ *
715
+ * @param {string} html - HTML content
716
+ * @param {string} css - CSS stylesheet
717
+ * @param {Object} options - Configuration
718
+ * @param {boolean} options.preserveAnimations - Keep @keyframes (default: true)
719
+ * @param {boolean} options.verbose - Log removals (default: false)
720
+ * @returns {string} Filtered CSS
721
+ * @throws {Error} If HTML or CSS is invalid
722
+ */
723
+ function filterUnusedCSS(html, css, options = {}) {}
724
+ ```
725
+
726
+ ### README Structure
727
+
728
+ Each module with public APIs should have:
729
+ - One-line description
730
+ - Key functions/classes
731
+ - Usage examples
732
+ - Error handling
733
+ - Configuration options
734
+
735
+ ---
736
+
737
+ ## Performance Guidelines
738
+
739
+ **Rules:**
740
+ - Profile before optimizing (use `console.time()`)
741
+ - Use `Promise.all()` for parallel work, sequence only when dependent
742
+ - Stream large files, batch writes with `fs.promises`
743
+ - Release large objects: `obj = null`
744
+
745
+ **Target Performance:**
746
+ - Screenshot: 3-5s per viewport
747
+ - CSS filtering: 1-2s
748
+ - Asset extraction: 5-10s per 10 images
749
+ - Full workflow: <120s
750
+
751
+ ---
752
+
753
+ ## Security Standards
754
+
755
+ **Input Validation:** Always validate external input (URLs, tokens, files)
756
+
757
+ ```javascript
758
+ function validateUrl(url) {
759
+ if (typeof url !== 'string' || !url.trim()) throw new Error('Invalid URL');
760
+ const parsed = new URL(url);
761
+ if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('HTTP only');
762
+ return url;
763
+ }
764
+ ```
765
+
766
+ **Credentials:** Use environment variables only, never log tokens
767
+
768
+ ```javascript
769
+ const token = process.env.FIGMA_ACCESS_TOKEN;
770
+ if (!token) throw new Error('FIGMA_ACCESS_TOKEN required');
771
+ console.log('Using token:', 'REDACTED'); // Good
772
+ ```
773
+
774
+ **HTML Sanitization:** Remove `<script>`, event handlers, `javascript:` URLs
775
+
776
+ ---
777
+
778
+ ## Code Review Standards
779
+
780
+ Before submitting code:
781
+ - Code follows naming conventions & project structure
782
+ - Functions have JSDoc/docstring comments
783
+ - Error handling is comprehensive
784
+ - No credentials or secrets
785
+ - Tests added/updated (maintain 85%+ coverage)
786
+ - No console.log in production
787
+ - Documentation updated
788
+
789
+ **References:** [Google JS Style Guide](https://google.github.io/styleguide/tsguide.html), [PEP 8](https://pep8.org), OWASP Top 10