@champpaba/claude-agent-kit 3.2.0 → 3.4.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.
- package/.claude/CHANGELOG.md +129 -0
- package/.claude/CLAUDE.md +57 -2
- package/.claude/agents/02-uxui-frontend.md +99 -16
- package/.claude/agents/07-ux-tester.md +307 -108
- package/.claude/commands/cdev.md +506 -496
- package/.claude/commands/csetup.md +985 -1062
- package/.claude/commands/cstatus.md +62 -53
- package/.claude/commands/cview.md +49 -50
- package/.claude/commands/designsetup.md +958 -1910
- package/.claude/commands/extract.md +480 -743
- package/.claude/commands/pageplan.md +155 -153
- package/.claude/commands/pstatus.md +322 -254
- package/.claude/lib/README.md +8 -0
- package/.claude/lib/design-validator.md +330 -0
- package/package.json +1 -1
|
@@ -38,49 +38,33 @@ Extract ALL design data from a website and save to `design-system/extracted/{sit
|
|
|
38
38
|
|
|
39
39
|
## 🔍 STEP 0: Parse Input & Setup
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
header: "Re-extract?",
|
|
69
|
-
multiSelect: false,
|
|
70
|
-
options: [
|
|
71
|
-
{ label: "Yes, re-extract", description: "Overwrite previous data" },
|
|
72
|
-
{ label: "No, cancel", description: "Keep existing data" }
|
|
73
|
-
]
|
|
74
|
-
}]
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (response.answers["Re-extract?"] === "No, cancel") {
|
|
78
|
-
return output('Extraction cancelled. Existing data preserved.');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Create directories
|
|
83
|
-
Bash: mkdir -p design-system/extracted/${siteName}/screenshots
|
|
41
|
+
### 0.1: Validate and Normalize URL
|
|
42
|
+
|
|
43
|
+
1. Check if URL argument is provided
|
|
44
|
+
- If missing, return error: "URL required. Usage: /extract https://airbnb.com"
|
|
45
|
+
2. Trim whitespace from URL
|
|
46
|
+
3. Add "https://" prefix if URL doesn't start with "http://" or "https://"
|
|
47
|
+
4. Parse hostname and auto-detect site name:
|
|
48
|
+
- Remove "www." prefix if present
|
|
49
|
+
- Remove top-level domain (TLD) to get clean site name
|
|
50
|
+
- Example: "www.airbnb.com" → "airbnb"
|
|
51
|
+
|
|
52
|
+
### 0.2: Check for Existing Extraction
|
|
53
|
+
|
|
54
|
+
1. Build path: `design-system/extracted/{siteName}/data.yaml`
|
|
55
|
+
2. If file exists:
|
|
56
|
+
- Read existing YAML file
|
|
57
|
+
- Extract `meta.extracted_at` field
|
|
58
|
+
- Ask user via AskUserQuestion:
|
|
59
|
+
- Question: "Site '{siteName}' was already extracted on {extractedDate}. Re-extract?"
|
|
60
|
+
- Options: "Yes, re-extract" (overwrite) or "No, cancel" (keep existing)
|
|
61
|
+
- If user chooses "No, cancel", exit with message: "Extraction cancelled. Existing data preserved."
|
|
62
|
+
|
|
63
|
+
### 0.3: Create Output Directories
|
|
64
|
+
|
|
65
|
+
Use Bash to create directory structure:
|
|
66
|
+
```bash
|
|
67
|
+
mkdir -p design-system/extracted/{siteName}/screenshots
|
|
84
68
|
```
|
|
85
69
|
|
|
86
70
|
**Report:**
|
|
@@ -98,42 +82,27 @@ Bash: mkdir -p design-system/extracted/${siteName}/screenshots
|
|
|
98
82
|
|
|
99
83
|
## STEP 1: Navigate & Wait
|
|
100
84
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
await sleep(5000);
|
|
123
|
-
}
|
|
124
|
-
} catch {
|
|
125
|
-
await sleep(5000);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Verify loaded
|
|
129
|
-
const readyState = await mcp__chrome-devtools__evaluate_script({
|
|
130
|
-
function: '() => document.readyState'
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
if (readyState !== 'complete') {
|
|
134
|
-
await sleep(3000);
|
|
135
|
-
}
|
|
136
|
-
```
|
|
85
|
+
### 1.1: Navigate to URL
|
|
86
|
+
|
|
87
|
+
Use Chrome DevTools to navigate to the target URL:
|
|
88
|
+
- Tool: `mcp__chrome-devtools__navigate_page`
|
|
89
|
+
- Parameter: `url` (from Step 0)
|
|
90
|
+
|
|
91
|
+
### 1.2: Smart Wait for Page Load
|
|
92
|
+
|
|
93
|
+
1. Take DOM snapshot (verbose: false) to analyze page structure
|
|
94
|
+
2. From snapshot, find heading elements (filter lines containing `[heading]`)
|
|
95
|
+
3. If headings found:
|
|
96
|
+
- Extract text from first heading
|
|
97
|
+
- Use Chrome DevTools wait_for to wait for that text (timeout: 15000ms)
|
|
98
|
+
- This ensures the main content is loaded
|
|
99
|
+
4. If no headings found or wait fails:
|
|
100
|
+
- Fallback to sleep 5000ms
|
|
101
|
+
|
|
102
|
+
### 1.3: Verify Document Ready
|
|
103
|
+
|
|
104
|
+
1. Evaluate script to check document.readyState
|
|
105
|
+
2. If not "complete", sleep additional 3000ms to ensure full page load
|
|
137
106
|
|
|
138
107
|
**Report:**
|
|
139
108
|
```
|
|
@@ -146,432 +115,230 @@ if (readyState !== 'complete') {
|
|
|
146
115
|
|
|
147
116
|
## STEP 2: Extract CSS Data (17 Sections in Parallel)
|
|
148
117
|
|
|
149
|
-
Run all extraction
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const rgbToHex = (rgb) => {
|
|
190
|
-
const match = rgb.match(/\\d+/g);
|
|
191
|
-
if (!match) return rgb;
|
|
192
|
-
const hex = match.slice(0, 3).map(x => {
|
|
193
|
-
const h = parseInt(x).toString(16);
|
|
194
|
-
return h.length === 1 ? '0' + h : h;
|
|
195
|
-
});
|
|
196
|
-
return '#' + hex.join('').toUpperCase();
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Detect usage based on element context
|
|
200
|
-
const detectUsage = (el, type) => {
|
|
201
|
-
const tag = el.tagName.toLowerCase();
|
|
202
|
-
const classes = el.className || '';
|
|
203
|
-
|
|
204
|
-
if (type === 'background') {
|
|
205
|
-
if (tag === 'button' || classes.includes('btn') || classes.includes('button')) return 'button-bg';
|
|
206
|
-
if (tag === 'nav' || classes.includes('nav') || classes.includes('header')) return 'nav-bg';
|
|
207
|
-
if (classes.includes('card') || classes.includes('box')) return 'card-bg';
|
|
208
|
-
if (classes.includes('hero') || classes.includes('banner')) return 'hero-bg';
|
|
209
|
-
if (tag === 'body' || classes.includes('main')) return 'page-bg';
|
|
210
|
-
return 'surface';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (type === 'text') {
|
|
214
|
-
if (tag.match(/^h[1-6]$/)) return 'heading';
|
|
215
|
-
if (tag === 'a') return 'link';
|
|
216
|
-
if (tag === 'button') return 'button-text';
|
|
217
|
-
if (classes.includes('muted') || classes.includes('secondary')) return 'muted-text';
|
|
218
|
-
return 'body-text';
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (type === 'border') {
|
|
222
|
-
if (tag === 'input' || tag === 'textarea') return 'input-border';
|
|
223
|
-
if (classes.includes('card')) return 'card-border';
|
|
224
|
-
return 'divider';
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return 'general';
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
allElements.forEach(el => {
|
|
231
|
-
const s = window.getComputedStyle(el);
|
|
232
|
-
|
|
233
|
-
if (s.backgroundColor && s.backgroundColor !== 'rgba(0, 0, 0, 0)') {
|
|
234
|
-
const hex = rgbToHex(s.backgroundColor);
|
|
235
|
-
if (!colorMap.backgrounds.has(hex)) {
|
|
236
|
-
colorMap.backgrounds.set(hex, {
|
|
237
|
-
rgb: s.backgroundColor,
|
|
238
|
-
hex: hex,
|
|
239
|
-
usage: detectUsage(el, 'background'),
|
|
240
|
-
count: 1
|
|
241
|
-
});
|
|
242
|
-
} else {
|
|
243
|
-
colorMap.backgrounds.get(hex).count++;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (s.color) {
|
|
248
|
-
const hex = rgbToHex(s.color);
|
|
249
|
-
if (!colorMap.texts.has(hex)) {
|
|
250
|
-
colorMap.texts.set(hex, {
|
|
251
|
-
rgb: s.color,
|
|
252
|
-
hex: hex,
|
|
253
|
-
usage: detectUsage(el, 'text'),
|
|
254
|
-
count: 1
|
|
255
|
-
});
|
|
256
|
-
} else {
|
|
257
|
-
colorMap.texts.get(hex).count++;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (s.borderColor && s.borderColor !== 'rgba(0, 0, 0, 0)') {
|
|
262
|
-
const hex = rgbToHex(s.borderColor);
|
|
263
|
-
if (!colorMap.borders.has(hex)) {
|
|
264
|
-
colorMap.borders.set(hex, {
|
|
265
|
-
rgb: s.borderColor,
|
|
266
|
-
hex: hex,
|
|
267
|
-
usage: detectUsage(el, 'border'),
|
|
268
|
-
count: 1
|
|
269
|
-
});
|
|
270
|
-
} else {
|
|
271
|
-
colorMap.borders.get(hex).count++;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Sort by count (most used first)
|
|
277
|
-
const sortByCount = (map) => Array.from(map.values())
|
|
278
|
-
.sort((a, b) => b.count - a.count)
|
|
279
|
-
.slice(0, 20);
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
backgrounds: sortByCount(colorMap.backgrounds),
|
|
283
|
-
texts: sortByCount(colorMap.texts),
|
|
284
|
-
borders: sortByCount(colorMap.borders)
|
|
285
|
-
};
|
|
286
|
-
}`
|
|
287
|
-
});
|
|
288
|
-
}
|
|
118
|
+
Run all extraction evaluations in parallel for speed. Use Chrome DevTools `evaluate_script` for each extraction function below.
|
|
119
|
+
|
|
120
|
+
**Parallel Execution Strategy:**
|
|
121
|
+
- Execute all 8 extraction functions concurrently
|
|
122
|
+
- Collect results: colors, typography, shadows, spacing, buttons, cards, inputs, animations
|
|
123
|
+
- Non-critical failures should not block other extractions (use fallback empty arrays)
|
|
124
|
+
|
|
125
|
+
### 2.1: Extract Colors
|
|
126
|
+
|
|
127
|
+
Use Chrome DevTools to evaluate script that:
|
|
128
|
+
|
|
129
|
+
1. **Query all elements**: `document.querySelectorAll('*')`
|
|
130
|
+
2. **For each element**, extract using `window.getComputedStyle()`:
|
|
131
|
+
- Background color (skip transparent: `rgba(0, 0, 0, 0)`)
|
|
132
|
+
- Text color
|
|
133
|
+
- Border color (skip transparent)
|
|
134
|
+
3. **Convert RGB to HEX**: Parse RGB values and convert to uppercase hex format
|
|
135
|
+
4. **Detect usage context** based on element tag/class:
|
|
136
|
+
- Background usage: button-bg, nav-bg, card-bg, hero-bg, page-bg, surface
|
|
137
|
+
- Text usage: heading, link, button-text, muted-text, body-text
|
|
138
|
+
- Border usage: input-border, card-border, divider
|
|
139
|
+
5. **Count frequency** of each color (how many times used)
|
|
140
|
+
6. **Sort by count** (most used first) and take top 20 per category
|
|
141
|
+
|
|
142
|
+
**Output format:**
|
|
143
|
+
```yaml
|
|
144
|
+
colors:
|
|
145
|
+
backgrounds:
|
|
146
|
+
- hex: "#FFFFFF"
|
|
147
|
+
rgb: "rgb(255, 255, 255)"
|
|
148
|
+
usage: "page-bg"
|
|
149
|
+
count: 45
|
|
150
|
+
texts:
|
|
151
|
+
- hex: "#000000"
|
|
152
|
+
usage: "body-text"
|
|
153
|
+
count: 32
|
|
154
|
+
borders:
|
|
155
|
+
- hex: "#E5E7EB"
|
|
156
|
+
usage: "divider"
|
|
157
|
+
count: 12
|
|
289
158
|
```
|
|
290
159
|
|
|
291
160
|
### 2.2: Extract Typography
|
|
292
161
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// Body text
|
|
329
|
-
Array.from(document.querySelectorAll('p, div, span')).slice(0, 20).forEach(el => {
|
|
330
|
-
const s = window.getComputedStyle(el);
|
|
331
|
-
if (el.textContent.trim().length > 20) {
|
|
332
|
-
typography.body.push({
|
|
333
|
-
fontSize: s.fontSize,
|
|
334
|
-
fontWeight: s.fontWeight,
|
|
335
|
-
lineHeight: s.lineHeight,
|
|
336
|
-
fontFamily: s.fontFamily,
|
|
337
|
-
color: s.color
|
|
338
|
-
});
|
|
339
|
-
fonts.add(s.fontFamily);
|
|
340
|
-
weights.add(s.fontWeight);
|
|
341
|
-
sizes.add(s.fontSize);
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
...typography,
|
|
347
|
-
allFonts: Array.from(fonts),
|
|
348
|
-
allWeights: Array.from(weights).sort((a, b) => parseInt(a) - parseInt(b)),
|
|
349
|
-
allSizes: Array.from(sizes).sort((a, b) => parseFloat(a) - parseFloat(b))
|
|
350
|
-
};
|
|
351
|
-
}`
|
|
352
|
-
});
|
|
353
|
-
}
|
|
162
|
+
Use Chrome DevTools to evaluate script that:
|
|
163
|
+
|
|
164
|
+
1. **Extract heading styles** (h1, h2, h3):
|
|
165
|
+
- Query first 3 instances of each heading tag
|
|
166
|
+
- For each, extract using `window.getComputedStyle()`:
|
|
167
|
+
- Sample text (first 50 characters)
|
|
168
|
+
- fontSize, fontWeight, fontFamily
|
|
169
|
+
- lineHeight, letterSpacing, textTransform
|
|
170
|
+
- color
|
|
171
|
+
2. **Extract body text styles** (p, div, span):
|
|
172
|
+
- Query first 20 elements with text content > 20 characters
|
|
173
|
+
- Extract: fontSize, fontWeight, lineHeight, fontFamily, color
|
|
174
|
+
3. **Collect unique values**:
|
|
175
|
+
- All font families used
|
|
176
|
+
- All font weights (sorted numerically)
|
|
177
|
+
- All font sizes (sorted by value)
|
|
178
|
+
|
|
179
|
+
**Output format:**
|
|
180
|
+
```yaml
|
|
181
|
+
typography:
|
|
182
|
+
h1:
|
|
183
|
+
- text: "Welcome to our site"
|
|
184
|
+
fontSize: "48px"
|
|
185
|
+
fontWeight: "700"
|
|
186
|
+
fontFamily: "Inter, sans-serif"
|
|
187
|
+
h2:
|
|
188
|
+
- fontSize: "32px"
|
|
189
|
+
fontWeight: "600"
|
|
190
|
+
body:
|
|
191
|
+
- fontSize: "16px"
|
|
192
|
+
fontWeight: "400"
|
|
193
|
+
lineHeight: "1.5"
|
|
194
|
+
allFonts: ["Inter", "Roboto"]
|
|
195
|
+
allWeights: ["400", "500", "600", "700"]
|
|
196
|
+
allSizes: ["14px", "16px", "24px", "32px", "48px"]
|
|
354
197
|
```
|
|
355
198
|
|
|
356
199
|
### 2.3: Extract Shadows & Effects
|
|
357
200
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
shadows: Array.from(effects.shadows).slice(0, 15),
|
|
384
|
-
borderRadii: Array.from(effects.borderRadii).slice(0, 15),
|
|
385
|
-
borderWidths: Array.from(effects.borderWidths).slice(0, 10)
|
|
386
|
-
};
|
|
387
|
-
}`
|
|
388
|
-
});
|
|
389
|
-
}
|
|
201
|
+
Use Chrome DevTools to evaluate script that:
|
|
202
|
+
|
|
203
|
+
1. **Query all elements**: `document.querySelectorAll('*')`
|
|
204
|
+
2. **For each element**, extract using `window.getComputedStyle()`:
|
|
205
|
+
- boxShadow (skip "none")
|
|
206
|
+
- borderRadius (skip "0px")
|
|
207
|
+
- borderWidth (skip "0px")
|
|
208
|
+
3. **Collect unique values** using Set to avoid duplicates
|
|
209
|
+
4. **Limit results**:
|
|
210
|
+
- Top 15 unique box shadows
|
|
211
|
+
- Top 15 unique border radii
|
|
212
|
+
- Top 10 unique border widths
|
|
213
|
+
|
|
214
|
+
**Output format:**
|
|
215
|
+
```yaml
|
|
216
|
+
shadows:
|
|
217
|
+
- "0 1px 3px rgba(0, 0, 0, 0.1)"
|
|
218
|
+
- "0 4px 6px rgba(0, 0, 0, 0.1)"
|
|
219
|
+
borderRadii:
|
|
220
|
+
- "4px"
|
|
221
|
+
- "8px"
|
|
222
|
+
- "12px"
|
|
223
|
+
borderWidths:
|
|
224
|
+
- "1px"
|
|
225
|
+
- "2px"
|
|
390
226
|
```
|
|
391
227
|
|
|
392
228
|
### 2.4: Extract Spacing
|
|
393
229
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Detect grid pattern
|
|
424
|
-
const allValues = [
|
|
425
|
-
...spacing.paddings,
|
|
426
|
-
...spacing.margins,
|
|
427
|
-
...spacing.gaps
|
|
428
|
-
]
|
|
429
|
-
.map(v => parseFloat(v))
|
|
430
|
-
.filter(v => !isNaN(v) && v > 0)
|
|
431
|
-
.sort((a, b) => a - b);
|
|
432
|
-
|
|
433
|
-
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
|
|
434
|
-
const gridBase = allValues.length > 1
|
|
435
|
-
? allValues.reduce((acc, val) => gcd(acc, val), allValues[0])
|
|
436
|
-
: 8;
|
|
437
|
-
|
|
438
|
-
return {
|
|
439
|
-
paddings: Array.from(spacing.paddings).slice(0, 20),
|
|
440
|
-
margins: Array.from(spacing.margins).slice(0, 20),
|
|
441
|
-
gaps: Array.from(spacing.gaps).slice(0, 10),
|
|
442
|
-
detectedGrid: Math.round(gridBase) || 8,
|
|
443
|
-
commonValues: [...new Set(allValues)].slice(0, 15)
|
|
444
|
-
};
|
|
445
|
-
}`
|
|
446
|
-
});
|
|
447
|
-
}
|
|
230
|
+
Use Chrome DevTools to evaluate script that:
|
|
231
|
+
|
|
232
|
+
1. **Query first 100 elements** for spacing analysis
|
|
233
|
+
2. **For each element**, extract using `window.getComputedStyle()`:
|
|
234
|
+
- Padding (all sides: top, right, bottom, left, shorthand) - skip "0px"
|
|
235
|
+
- Margin (top, bottom only) - skip "0px" and "auto"
|
|
236
|
+
- Gap (flexbox/grid) - skip "normal" and "0px"
|
|
237
|
+
3. **Detect spacing grid pattern**:
|
|
238
|
+
- Parse all spacing values to numbers
|
|
239
|
+
- Calculate Greatest Common Divisor (GCD) to find base unit
|
|
240
|
+
- Common pattern: 4px or 8px base grid
|
|
241
|
+
- Fallback to 8px if pattern unclear
|
|
242
|
+
4. **Limit results**:
|
|
243
|
+
- Top 20 unique padding values
|
|
244
|
+
- Top 20 unique margin values
|
|
245
|
+
- Top 10 unique gap values
|
|
246
|
+
- Top 15 most common spacing values overall
|
|
247
|
+
|
|
248
|
+
**Output format:**
|
|
249
|
+
```yaml
|
|
250
|
+
spacing:
|
|
251
|
+
detectedGrid: 8
|
|
252
|
+
paddings: ["8px", "16px", "24px", "32px"]
|
|
253
|
+
margins: ["8px", "16px", "24px"]
|
|
254
|
+
gaps: ["8px", "16px"]
|
|
255
|
+
commonValues: [8, 16, 24, 32, 40, 48]
|
|
448
256
|
```
|
|
449
257
|
|
|
450
|
-
### 2.5
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
};
|
|
503
|
-
});
|
|
504
|
-
}`
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async function extractInputs() {
|
|
509
|
-
return await mcp__chrome-devtools__evaluate_script({
|
|
510
|
-
function: `() => {
|
|
511
|
-
return Array.from(document.querySelectorAll('input[type="text"], input[type="email"], input[type="password"], textarea'))
|
|
512
|
-
.slice(0, 5)
|
|
513
|
-
.map((input, i) => {
|
|
514
|
-
input.setAttribute('data-extract-id', 'input-' + i);
|
|
515
|
-
const s = window.getComputedStyle(input);
|
|
516
|
-
return {
|
|
517
|
-
id: 'input-' + i,
|
|
518
|
-
type: input.type || 'textarea',
|
|
519
|
-
height: s.height,
|
|
520
|
-
padding: s.padding,
|
|
521
|
-
border: s.border,
|
|
522
|
-
borderRadius: s.borderRadius,
|
|
523
|
-
fontSize: s.fontSize,
|
|
524
|
-
backgroundColor: s.backgroundColor,
|
|
525
|
-
transition: s.transition
|
|
526
|
-
};
|
|
527
|
-
});
|
|
528
|
-
}`
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
```
|
|
258
|
+
### 2.5: Extract Buttons
|
|
259
|
+
|
|
260
|
+
Use Chrome DevTools to evaluate script that:
|
|
261
|
+
|
|
262
|
+
1. **Query button elements** with selectors:
|
|
263
|
+
- `button`, `a[role="button"]`
|
|
264
|
+
- `.btn`, `[class*="button"]`, `[class*="Button"]`
|
|
265
|
+
2. **Take first 10 buttons** found
|
|
266
|
+
3. **For each button**:
|
|
267
|
+
- Add `data-extract-id` attribute (e.g., "button-0", "button-1")
|
|
268
|
+
- Extract using `window.getComputedStyle()`:
|
|
269
|
+
- Text content (first 30 characters)
|
|
270
|
+
- backgroundColor, color, padding
|
|
271
|
+
- border, borderRadius
|
|
272
|
+
- fontSize, fontWeight
|
|
273
|
+
- boxShadow, transition
|
|
274
|
+
|
|
275
|
+
**Why add data-extract-id**: Enables later re-querying for hover/focus state extraction.
|
|
276
|
+
|
|
277
|
+
### 2.6: Extract Cards
|
|
278
|
+
|
|
279
|
+
Use Chrome DevTools to evaluate script that:
|
|
280
|
+
|
|
281
|
+
1. **Query card-like elements** with selectors:
|
|
282
|
+
- `[class*="card"]`, `[class*="Card"]`
|
|
283
|
+
- `article`, `section`
|
|
284
|
+
- `[class*="box"]`, `[class*="Box"]`
|
|
285
|
+
2. **Take first 10 cards** found
|
|
286
|
+
3. **For each card**:
|
|
287
|
+
- Add `data-extract-id` attribute (e.g., "card-0", "card-1")
|
|
288
|
+
- Extract className for reference
|
|
289
|
+
- Extract using `window.getComputedStyle()`:
|
|
290
|
+
- backgroundColor, padding
|
|
291
|
+
- border, borderRadius
|
|
292
|
+
- boxShadow, transition
|
|
293
|
+
|
|
294
|
+
### 2.7: Extract Input Fields
|
|
295
|
+
|
|
296
|
+
Use Chrome DevTools to evaluate script that:
|
|
297
|
+
|
|
298
|
+
1. **Query input elements** with selectors:
|
|
299
|
+
- `input[type="text"]`, `input[type="email"]`, `input[type="password"]`
|
|
300
|
+
- `textarea`
|
|
301
|
+
2. **Take first 5 inputs** found
|
|
302
|
+
3. **For each input**:
|
|
303
|
+
- Add `data-extract-id` attribute (e.g., "input-0", "input-1")
|
|
304
|
+
- Extract type (text/email/password/textarea)
|
|
305
|
+
- Extract using `window.getComputedStyle()`:
|
|
306
|
+
- height, padding
|
|
307
|
+
- border, borderRadius
|
|
308
|
+
- fontSize, backgroundColor
|
|
309
|
+
- transition
|
|
532
310
|
|
|
533
311
|
### 2.8: Extract Animations
|
|
534
312
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
transition: s.transition,
|
|
565
|
-
transitionDuration: s.transitionDuration,
|
|
566
|
-
transitionTimingFunction: s.transitionTimingFunction
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
return { keyframes, transitions };
|
|
572
|
-
}`
|
|
573
|
-
});
|
|
574
|
-
}
|
|
313
|
+
Use Chrome DevTools to evaluate script that:
|
|
314
|
+
|
|
315
|
+
1. **Extract CSS @keyframes animations**:
|
|
316
|
+
- Loop through all document.styleSheets
|
|
317
|
+
- For each stylesheet, check cssRules
|
|
318
|
+
- Find rules with type `CSSRule.KEYFRAMES_RULE`
|
|
319
|
+
- Extract: animation name and full CSS text
|
|
320
|
+
- Handle CORS errors gracefully (skip external stylesheets)
|
|
321
|
+
|
|
322
|
+
2. **Extract CSS transitions**:
|
|
323
|
+
- Query first 50 elements
|
|
324
|
+
- For each, extract using `window.getComputedStyle()`:
|
|
325
|
+
- transition property
|
|
326
|
+
- transitionDuration
|
|
327
|
+
- transitionTimingFunction
|
|
328
|
+
- Skip default value: "all 0s ease 0s"
|
|
329
|
+
- Record element className or tagName for reference
|
|
330
|
+
|
|
331
|
+
**Output format:**
|
|
332
|
+
```yaml
|
|
333
|
+
animations:
|
|
334
|
+
keyframes:
|
|
335
|
+
- name: "fadeIn"
|
|
336
|
+
css: "@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }"
|
|
337
|
+
transitions:
|
|
338
|
+
- selector: "button"
|
|
339
|
+
transition: "all 0.3s ease"
|
|
340
|
+
transitionDuration: "0.3s"
|
|
341
|
+
transitionTimingFunction: "ease"
|
|
575
342
|
```
|
|
576
343
|
|
|
577
344
|
**Report:**
|
|
@@ -595,318 +362,275 @@ async function extractAnimations() {
|
|
|
595
362
|
|
|
596
363
|
## STEP 3: Extract Component Animations (Interactive States)
|
|
597
364
|
|
|
598
|
-
For each component, capture
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const hoverStyle = await mcp__chrome-devtools__evaluate_script({
|
|
652
|
-
function: `() => {
|
|
653
|
-
const el = document.querySelector('[data-extract-id="${btnId}"]');
|
|
654
|
-
if (!el) return null;
|
|
655
|
-
const s = window.getComputedStyle(el);
|
|
656
|
-
return {
|
|
657
|
-
background: s.backgroundColor,
|
|
658
|
-
color: s.color,
|
|
659
|
-
boxShadow: s.boxShadow,
|
|
660
|
-
transform: s.transform
|
|
661
|
-
};
|
|
662
|
-
}`
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
// Remove hover
|
|
666
|
-
await mcp__chrome-devtools__evaluate_script({
|
|
667
|
-
function: `() => {
|
|
668
|
-
const el = document.querySelector('[data-extract-id="${btnId}"]');
|
|
669
|
-
if (el) el.dispatchEvent(new MouseEvent('mouseleave'));
|
|
670
|
-
}`
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
// Generate description
|
|
674
|
-
const changes = [];
|
|
675
|
-
if (defaultStyle.boxShadow !== hoverStyle.boxShadow) changes.push('Shadow changes');
|
|
676
|
-
if (defaultStyle.transform !== hoverStyle.transform) changes.push('Transform changes');
|
|
677
|
-
if (defaultStyle.background !== hoverStyle.background) changes.push('Background changes');
|
|
678
|
-
|
|
679
|
-
componentAnimations[btnId] = {
|
|
680
|
-
type: 'button',
|
|
681
|
-
states: { default: defaultStyle, hover: hoverStyle },
|
|
682
|
-
transition: buttons[i].transition,
|
|
683
|
-
description: changes.length > 0 ? changes.join(' + ') : 'No visible changes'
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Repeat for cards and inputs...
|
|
365
|
+
For each component type, capture default and hover states to understand animations.
|
|
366
|
+
|
|
367
|
+
### 3.1: Button Hover States
|
|
368
|
+
|
|
369
|
+
For the **first 3 buttons** (to limit execution time):
|
|
370
|
+
|
|
371
|
+
1. **Find element by data-extract-id** (e.g., "button-0")
|
|
372
|
+
2. **Scroll element into view** (block: center) for visibility
|
|
373
|
+
3. **Wait 500ms** for scroll animation
|
|
374
|
+
4. **Take screenshot** of default state
|
|
375
|
+
- Save to: `design-system/extracted/{siteName}/screenshots/{btnId}-default.png`
|
|
376
|
+
5. **Capture default computed styles**:
|
|
377
|
+
- backgroundColor, color, boxShadow, transform
|
|
378
|
+
6. **Trigger hover state**:
|
|
379
|
+
- Dispatch `MouseEvent('mouseenter', { bubbles: true })` to element
|
|
380
|
+
7. **Wait 500ms** for transition to complete
|
|
381
|
+
8. **Take screenshot** of hover state
|
|
382
|
+
- Save to: `design-system/extracted/{siteName}/screenshots/{btnId}-hover.png`
|
|
383
|
+
9. **Capture hover computed styles**:
|
|
384
|
+
- backgroundColor, color, boxShadow, transform
|
|
385
|
+
10. **Remove hover state**:
|
|
386
|
+
- Dispatch `MouseEvent('mouseleave')` to element
|
|
387
|
+
11. **Compare states** and generate description:
|
|
388
|
+
- If boxShadow changed → "Shadow changes"
|
|
389
|
+
- If transform changed → "Transform changes"
|
|
390
|
+
- If background changed → "Background changes"
|
|
391
|
+
- Join changes with " + " or return "No visible changes"
|
|
392
|
+
|
|
393
|
+
### 3.2: Card Hover States
|
|
394
|
+
|
|
395
|
+
Repeat same process for **first 3 cards** with `data-extract-id="card-{i}"`.
|
|
396
|
+
|
|
397
|
+
### 3.3: Input Focus States
|
|
398
|
+
|
|
399
|
+
Similar process for **first 3 inputs** but use:
|
|
400
|
+
- Focus event instead of hover: `dispatchEvent(new FocusEvent('focus'))`
|
|
401
|
+
- Blur event to remove: `dispatchEvent(new FocusEvent('blur'))`
|
|
402
|
+
- Screenshot names: `{inputId}-default.png`, `{inputId}-focus.png`
|
|
403
|
+
|
|
404
|
+
**Store results** in componentAnimations object with structure:
|
|
405
|
+
```yaml
|
|
406
|
+
componentAnimations:
|
|
407
|
+
button-0:
|
|
408
|
+
type: "button"
|
|
409
|
+
description: "Shadow changes + Background changes"
|
|
410
|
+
transition: "all 0.3s ease"
|
|
411
|
+
states:
|
|
412
|
+
default:
|
|
413
|
+
background: "rgb(59, 130, 246)"
|
|
414
|
+
boxShadow: "none"
|
|
415
|
+
hover:
|
|
416
|
+
background: "rgb(37, 99, 235)"
|
|
417
|
+
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)"
|
|
688
418
|
```
|
|
689
419
|
|
|
690
420
|
---
|
|
691
421
|
|
|
692
422
|
## STEP 4: Full-Page Screenshot
|
|
693
423
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
await mcp__chrome-devtools__take_screenshot({
|
|
699
|
-
fullPage: true,
|
|
700
|
-
format: 'png',
|
|
701
|
-
filePath: `design-system/extracted/${siteName}/screenshots/full-page.png`
|
|
702
|
-
});
|
|
703
|
-
} catch {
|
|
704
|
-
await mcp__chrome-devtools__take_screenshot({
|
|
705
|
-
fullPage: false,
|
|
706
|
-
format: 'png',
|
|
707
|
-
filePath: `design-system/extracted/${siteName}/screenshots/viewport.png`
|
|
708
|
-
});
|
|
709
|
-
}
|
|
424
|
+
### 4.1: Ensure Screenshot Directory Exists
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
mkdir -p design-system/extracted/{siteName}/screenshots
|
|
710
428
|
```
|
|
711
429
|
|
|
430
|
+
### 4.2: Capture Full-Page Screenshot
|
|
431
|
+
|
|
432
|
+
Use Chrome DevTools to take screenshot:
|
|
433
|
+
|
|
434
|
+
1. **First attempt**: Full-page screenshot
|
|
435
|
+
- Tool: `mcp__chrome-devtools__take_screenshot`
|
|
436
|
+
- Parameters: `fullPage: true`, `format: 'png'`
|
|
437
|
+
- Save to: `design-system/extracted/{siteName}/screenshots/full-page.png`
|
|
438
|
+
|
|
439
|
+
2. **If full-page fails**: Fallback to viewport-only
|
|
440
|
+
- Parameters: `fullPage: false`, `format: 'png'`
|
|
441
|
+
- Save to: `design-system/extracted/{siteName}/screenshots/viewport.png`
|
|
442
|
+
|
|
443
|
+
**Why fallback**: Some sites have infinite scroll or very long pages that cause full-page screenshots to fail.
|
|
444
|
+
|
|
712
445
|
---
|
|
713
446
|
|
|
714
447
|
## STEP 5: AI Psychology Analysis
|
|
715
448
|
|
|
716
|
-
|
|
449
|
+
### 5.1: Determine Screenshot Path
|
|
717
450
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
: `design-system/extracted/${siteName}/screenshots/viewport.png`;
|
|
451
|
+
Check which screenshot exists:
|
|
452
|
+
- Prefer: `design-system/extracted/{siteName}/screenshots/full-page.png`
|
|
453
|
+
- Fallback: `design-system/extracted/{siteName}/screenshots/viewport.png`
|
|
722
454
|
|
|
723
|
-
|
|
455
|
+
### 5.2: Read Screenshot
|
|
724
456
|
|
|
725
|
-
|
|
726
|
-
You are a UX/UI design psychologist.
|
|
457
|
+
Use Read tool to load the screenshot image for visual analysis.
|
|
727
458
|
|
|
728
|
-
|
|
459
|
+
### 5.3: Generate Psychology Analysis Prompt
|
|
729
460
|
|
|
730
|
-
|
|
461
|
+
Create analysis request with:
|
|
731
462
|
|
|
732
|
-
|
|
733
|
-
-
|
|
734
|
-
-
|
|
463
|
+
**Context provided:**
|
|
464
|
+
- The screenshot (visual attachment)
|
|
465
|
+
- Extracted CSS colors data (JSON formatted)
|
|
466
|
+
- Extracted typography fonts (JSON formatted)
|
|
735
467
|
|
|
736
|
-
|
|
468
|
+
**Request UX/UI psychology insights in YAML format covering:**
|
|
737
469
|
|
|
738
|
-
|
|
739
|
-
psychology:
|
|
740
|
-
style_classification: # e.g., Neo-Brutalism, Minimalist, Modern SaaS
|
|
470
|
+
1. **style_classification**: Design style (Neo-Brutalism, Minimalist, Glassmorphism, Modern SaaS, etc.)
|
|
741
471
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
- emotion: Adventure
|
|
746
|
-
reason: "Vibrant colors suggest excitement"
|
|
472
|
+
2. **emotions_evoked**: List of emotions with reasons
|
|
473
|
+
- emotion: What feeling the design triggers
|
|
474
|
+
- reason: Specific design elements causing this emotion
|
|
747
475
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
key_principles:
|
|
769
|
-
- "Photography is hero"
|
|
770
|
-
- "Consistency builds trust"
|
|
771
|
-
- "Subtle brand presence"
|
|
772
|
-
\`\`\`
|
|
773
|
-
|
|
774
|
-
Be specific with examples from the visual.
|
|
775
|
-
`;
|
|
776
|
-
|
|
777
|
-
const psychologyYaml = await LLM({
|
|
778
|
-
prompt: analysisPrompt,
|
|
779
|
-
images: [screenshot]
|
|
780
|
-
});
|
|
781
|
-
```
|
|
476
|
+
3. **target_audience**: Who this design appeals to
|
|
477
|
+
- primary: Main user demographic (description, age_range, tech_savvy)
|
|
478
|
+
- secondary: Secondary users (if applicable)
|
|
479
|
+
|
|
480
|
+
4. **visual_principles**: Key design patterns observed
|
|
481
|
+
- name: Principle name
|
|
482
|
+
- description: How it's applied
|
|
483
|
+
|
|
484
|
+
5. **why_it_works**: Strategic design decisions
|
|
485
|
+
- List of business/psychological reasons the design is effective
|
|
486
|
+
|
|
487
|
+
6. **design_philosophy**: Underlying beliefs
|
|
488
|
+
- core_belief: Central design philosophy
|
|
489
|
+
- key_principles: List of guiding principles
|
|
490
|
+
|
|
491
|
+
**Instruction**: Be specific with examples from the visual.
|
|
492
|
+
|
|
493
|
+
### 5.4: Extract YAML Response
|
|
494
|
+
|
|
495
|
+
Parse the LLM response to extract the YAML block (between triple backticks).
|
|
782
496
|
|
|
783
497
|
---
|
|
784
498
|
|
|
785
499
|
## STEP 6: Generate data.yaml (17 Sections + Psychology)
|
|
786
500
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
501
|
+
### 6.1: Calculate Coverage Metrics
|
|
502
|
+
|
|
503
|
+
Count how many of the 17 standard sections were successfully detected:
|
|
504
|
+
- Overview, Color Palette, Typography, Spacing System, Component Styles
|
|
505
|
+
- Shadows/Elevation, Animations/Transitions, Border Radius, Border Styles
|
|
506
|
+
- Layout Patterns, etc.
|
|
791
507
|
|
|
508
|
+
Calculate percentage: `(detectedSections / 17) * 100`
|
|
509
|
+
|
|
510
|
+
### 6.2: Build YAML Structure
|
|
511
|
+
|
|
512
|
+
Construct comprehensive YAML file with these sections:
|
|
513
|
+
|
|
514
|
+
**Header Comments:**
|
|
515
|
+
```yaml
|
|
516
|
+
# Design Extraction: {siteName}
|
|
517
|
+
# Extracted: {ISO timestamp}
|
|
518
|
+
# URL: {url}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Meta Section:**
|
|
522
|
+
```yaml
|
|
792
523
|
meta:
|
|
793
|
-
site_name:
|
|
794
|
-
url:
|
|
795
|
-
extracted_at:
|
|
524
|
+
site_name: {siteName}
|
|
525
|
+
url: {url}
|
|
526
|
+
extracted_at: {ISO timestamp}
|
|
796
527
|
extractor_version: "2.1.0"
|
|
797
528
|
coverage:
|
|
798
529
|
total_sections: 17
|
|
799
|
-
detected_sections:
|
|
800
|
-
percentage:
|
|
801
|
-
|
|
802
|
-
# ============================================
|
|
803
|
-
# PSYCHOLOGY & ANALYSIS
|
|
804
|
-
# ============================================
|
|
805
|
-
|
|
806
|
-
${psychologyYaml}
|
|
530
|
+
detected_sections: {count}
|
|
531
|
+
percentage: {percentage}
|
|
532
|
+
```
|
|
807
533
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
# ============================================
|
|
534
|
+
**Psychology Section:**
|
|
535
|
+
Insert the psychology YAML from Step 5.4
|
|
811
536
|
|
|
537
|
+
**Design Tokens Sections:**
|
|
538
|
+
```yaml
|
|
812
539
|
sections:
|
|
813
540
|
overview:
|
|
814
541
|
detected: true
|
|
815
|
-
style:
|
|
542
|
+
style: {from psychology.style_classification}
|
|
816
543
|
tech_stack: Framework-agnostic
|
|
817
544
|
|
|
818
545
|
color_palette:
|
|
819
546
|
detected: true
|
|
820
547
|
primary:
|
|
821
|
-
|
|
822
|
-
rgb:
|
|
823
|
-
usage:
|
|
824
|
-
|
|
548
|
+
- hex: {top 5 background colors}
|
|
549
|
+
rgb: {rgb value}
|
|
550
|
+
usage: {usage context}
|
|
825
551
|
text_colors:
|
|
826
|
-
|
|
827
|
-
usage:
|
|
828
|
-
|
|
552
|
+
- hex: {top 5 text colors}
|
|
553
|
+
usage: {usage context}
|
|
829
554
|
border_colors:
|
|
830
|
-
|
|
831
|
-
usage:
|
|
555
|
+
- hex: {top 3 border colors}
|
|
556
|
+
usage: {usage context}
|
|
832
557
|
|
|
833
558
|
typography:
|
|
834
559
|
detected: true
|
|
835
|
-
fonts:
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
sizes: [${typography.allSizes.join(', ')}]
|
|
560
|
+
fonts: [{top 3 font families}]
|
|
561
|
+
weights: [{all weights, sorted}]
|
|
562
|
+
sizes: [{all sizes, sorted}]
|
|
839
563
|
|
|
840
564
|
spacing_system:
|
|
841
565
|
detected: true
|
|
842
|
-
grid_base:
|
|
843
|
-
common_values: [
|
|
566
|
+
grid_base: {detectedGrid}
|
|
567
|
+
common_values: [{spacing values}]
|
|
844
568
|
|
|
845
569
|
component_styles:
|
|
846
570
|
detected: true
|
|
847
571
|
buttons:
|
|
848
|
-
|
|
849
|
-
text:
|
|
850
|
-
backgroundColor:
|
|
851
|
-
color:
|
|
852
|
-
padding:
|
|
853
|
-
borderRadius:
|
|
854
|
-
transition:
|
|
855
|
-
hover_animation:
|
|
856
|
-
|
|
572
|
+
- id: {button-0}
|
|
573
|
+
text: {button text}
|
|
574
|
+
backgroundColor: {color}
|
|
575
|
+
color: {text color}
|
|
576
|
+
padding: {padding}
|
|
577
|
+
borderRadius: {radius}
|
|
578
|
+
transition: {transition}
|
|
579
|
+
hover_animation: {description from Step 3}
|
|
857
580
|
cards:
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
borderRadius: "${card.borderRadius}"
|
|
862
|
-
boxShadow: "${card.boxShadow}"
|
|
863
|
-
hover_animation: "${componentAnimations[card.id]?.description || 'none'}"`).join('\n')}
|
|
581
|
+
- {similar structure}
|
|
582
|
+
inputs:
|
|
583
|
+
- {similar structure}
|
|
864
584
|
|
|
865
585
|
shadows_elevation:
|
|
866
586
|
detected: true
|
|
867
|
-
values:
|
|
868
|
-
${shadows.shadows.slice(0, 5).map(s => ` - "${s}"`).join('\n')}
|
|
587
|
+
values: [{top 5 shadow values}]
|
|
869
588
|
|
|
870
589
|
animations_transitions:
|
|
871
590
|
detected: true
|
|
872
591
|
keyframes:
|
|
873
|
-
|
|
592
|
+
- name: {animation name}
|
|
874
593
|
transitions:
|
|
875
|
-
|
|
876
|
-
timing:
|
|
594
|
+
- duration: {duration}
|
|
595
|
+
timing: {timing function}
|
|
877
596
|
|
|
878
597
|
border_radius:
|
|
879
598
|
detected: true
|
|
880
|
-
values: [
|
|
599
|
+
values: [{top 8 radius values}]
|
|
881
600
|
|
|
882
601
|
border_styles:
|
|
883
602
|
detected: true
|
|
884
|
-
widths: [
|
|
603
|
+
widths: [{border widths}]
|
|
885
604
|
|
|
886
605
|
layout_patterns:
|
|
887
606
|
detected: true
|
|
888
607
|
container_width: "1280px"
|
|
889
608
|
grid_columns: 12
|
|
609
|
+
```
|
|
890
610
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
# ============================================
|
|
894
|
-
|
|
611
|
+
**Component Animations (Detailed):**
|
|
612
|
+
```yaml
|
|
895
613
|
animations:
|
|
896
|
-
|
|
897
|
-
type: "
|
|
898
|
-
description:
|
|
899
|
-
transition:
|
|
614
|
+
button-0:
|
|
615
|
+
type: "button"
|
|
616
|
+
description: {from Step 3}
|
|
617
|
+
transition: {transition value}
|
|
900
618
|
states:
|
|
901
619
|
default:
|
|
902
|
-
background:
|
|
903
|
-
boxShadow:
|
|
620
|
+
background: {color}
|
|
621
|
+
boxShadow: {shadow}
|
|
904
622
|
hover:
|
|
905
|
-
background:
|
|
906
|
-
boxShadow:
|
|
907
|
-
|
|
623
|
+
background: {color}
|
|
624
|
+
boxShadow: {shadow}
|
|
625
|
+
card-0:
|
|
626
|
+
{similar structure}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### 6.3: Write File
|
|
908
630
|
|
|
909
|
-
Write
|
|
631
|
+
Use Write tool to save the YAML content to:
|
|
632
|
+
```
|
|
633
|
+
design-system/extracted/{siteName}/data.yaml
|
|
910
634
|
```
|
|
911
635
|
|
|
912
636
|
---
|
|
@@ -965,29 +689,42 @@ Write(`design-system/extracted/${siteName}/data.yaml`, yamlContent);
|
|
|
965
689
|
|
|
966
690
|
## Error Handling
|
|
967
691
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
990
|
-
|
|
692
|
+
### Critical Errors (Stop Execution)
|
|
693
|
+
|
|
694
|
+
**Navigation failures** - If Chrome DevTools navigation fails:
|
|
695
|
+
1. Catch the error from `mcp__chrome-devtools__navigate_page`
|
|
696
|
+
2. Return error message:
|
|
697
|
+
```
|
|
698
|
+
❌ Failed to load URL: {url}
|
|
699
|
+
|
|
700
|
+
Error: {error.message}
|
|
701
|
+
|
|
702
|
+
Check:
|
|
703
|
+
- Is the URL accessible?
|
|
704
|
+
- Is Chrome DevTools MCP running?
|
|
705
|
+
```
|
|
706
|
+
3. Stop execution (cannot proceed without page loaded)
|
|
707
|
+
|
|
708
|
+
### Non-Critical Errors (Continue with Fallbacks)
|
|
709
|
+
|
|
710
|
+
**Extraction failures** - If individual extraction steps fail:
|
|
711
|
+
1. Log warning message (e.g., "Color extraction failed: {error.message}")
|
|
712
|
+
2. Use fallback empty data:
|
|
713
|
+
- Colors: `{ backgrounds: [], texts: [], borders: [] }`
|
|
714
|
+
- Typography: `{ h1: [], h2: [], h3: [], body: [], allFonts: [], allWeights: [], allSizes: [] }`
|
|
715
|
+
- Shadows: `{ shadows: [], borderRadii: [], borderWidths: [] }`
|
|
716
|
+
- Components: `[]` (empty array)
|
|
717
|
+
3. Continue with other extractions (parallel execution means one failure doesn't block others)
|
|
718
|
+
4. Final YAML will mark section as `detected: false` if no data extracted
|
|
719
|
+
|
|
720
|
+
**Screenshot failures**:
|
|
721
|
+
- Full-page screenshot fails → Fallback to viewport screenshot
|
|
722
|
+
- Component screenshot fails → Skip that component, continue with others
|
|
723
|
+
- Psychology analysis screenshot missing → Use viewport screenshot as fallback
|
|
724
|
+
|
|
725
|
+
**YAML generation**:
|
|
726
|
+
- Missing data sections → Mark `detected: false` in YAML
|
|
727
|
+
- Invalid data → Use empty defaults, note in coverage percentage
|
|
991
728
|
|
|
992
729
|
---
|
|
993
730
|
|