skillui 1.1.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/dist/cli.d.ts +3 -0
- package/dist/cli.js +202 -0
- package/dist/extractors/components.d.ts +11 -0
- package/dist/extractors/components.js +455 -0
- package/dist/extractors/framework.d.ts +4 -0
- package/dist/extractors/framework.js +126 -0
- package/dist/extractors/tokens/computed.d.ts +7 -0
- package/dist/extractors/tokens/computed.js +249 -0
- package/dist/extractors/tokens/css.d.ts +3 -0
- package/dist/extractors/tokens/css.js +510 -0
- package/dist/extractors/tokens/http-css.d.ts +14 -0
- package/dist/extractors/tokens/http-css.js +1689 -0
- package/dist/extractors/tokens/tailwind.d.ts +3 -0
- package/dist/extractors/tokens/tailwind.js +353 -0
- package/dist/extractors/tokens/tokens-file.d.ts +3 -0
- package/dist/extractors/tokens/tokens-file.js +229 -0
- package/dist/extractors/ultra/animations.d.ts +21 -0
- package/dist/extractors/ultra/animations.js +530 -0
- package/dist/extractors/ultra/components-dom.d.ts +13 -0
- package/dist/extractors/ultra/components-dom.js +152 -0
- package/dist/extractors/ultra/interactions.d.ts +14 -0
- package/dist/extractors/ultra/interactions.js +225 -0
- package/dist/extractors/ultra/layout.d.ts +14 -0
- package/dist/extractors/ultra/layout.js +126 -0
- package/dist/extractors/ultra/pages.d.ts +16 -0
- package/dist/extractors/ultra/pages.js +231 -0
- package/dist/font-resolver.d.ts +10 -0
- package/dist/font-resolver.js +280 -0
- package/dist/modes/dir.d.ts +6 -0
- package/dist/modes/dir.js +213 -0
- package/dist/modes/repo.d.ts +6 -0
- package/dist/modes/repo.js +76 -0
- package/dist/modes/ultra.d.ts +22 -0
- package/dist/modes/ultra.js +285 -0
- package/dist/modes/url.d.ts +14 -0
- package/dist/modes/url.js +161 -0
- package/dist/normalizer.d.ts +11 -0
- package/dist/normalizer.js +867 -0
- package/dist/screenshot.d.ts +9 -0
- package/dist/screenshot.js +94 -0
- package/dist/types-ultra.d.ts +157 -0
- package/dist/types-ultra.js +4 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.js +4 -0
- package/dist/writers/animations-md.d.ts +17 -0
- package/dist/writers/animations-md.js +313 -0
- package/dist/writers/components-md.d.ts +8 -0
- package/dist/writers/components-md.js +151 -0
- package/dist/writers/design-md.d.ts +7 -0
- package/dist/writers/design-md.js +704 -0
- package/dist/writers/interactions-md.d.ts +8 -0
- package/dist/writers/interactions-md.js +146 -0
- package/dist/writers/layout-md.d.ts +8 -0
- package/dist/writers/layout-md.js +120 -0
- package/dist/writers/skill.d.ts +12 -0
- package/dist/writers/skill.js +1006 -0
- package/dist/writers/tokens-json.d.ts +11 -0
- package/dist/writers/tokens-json.js +164 -0
- package/package.json +78 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateAnimationsMd = generateAnimationsMd;
|
|
4
|
+
/**
|
|
5
|
+
* Generate references/ANIMATIONS.md
|
|
6
|
+
*
|
|
7
|
+
* Cinematic-quality animation documentation:
|
|
8
|
+
* - Technology stack (GSAP, Lottie, Three.js, etc.)
|
|
9
|
+
* - Scroll journey (7 screenshots from top to bottom)
|
|
10
|
+
* - Every CSS @keyframe with full code + usage context
|
|
11
|
+
* - Scroll-triggered animation patterns
|
|
12
|
+
* - Video background specifications
|
|
13
|
+
* - Canvas / WebGL indicators
|
|
14
|
+
* - CSS animation variables / tokens
|
|
15
|
+
* - Implementation guide (how to recreate the motion design)
|
|
16
|
+
*/
|
|
17
|
+
function generateAnimationsMd(anim, profile) {
|
|
18
|
+
let md = `# Animation Reference\n\n`;
|
|
19
|
+
md += `> Cinematic motion design extracted from live DOM. Follow these specs exactly to recreate the experience.\n\n`;
|
|
20
|
+
// ── Technology Stack ────────────────────────────────────────────────
|
|
21
|
+
md += `## Motion Technology Stack\n\n`;
|
|
22
|
+
if (anim.libraries.length === 0 && anim.canvasCount === 0 && !anim.webglDetected) {
|
|
23
|
+
md += `Pure CSS animations — no external animation libraries detected.\n\n`;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
md += `| Library | Type | Notes |\n`;
|
|
27
|
+
md += `|---------|------|-------|\n`;
|
|
28
|
+
for (const lib of anim.libraries) {
|
|
29
|
+
const ver = lib.version ? ` v${lib.version}` : '';
|
|
30
|
+
const cdn = lib.cdn ? ` ([CDN](${lib.cdn}))` : '';
|
|
31
|
+
md += `| **${lib.name}${ver}** | ${lib.type} | ${cdn} |\n`;
|
|
32
|
+
}
|
|
33
|
+
if (anim.canvasCount > 0) {
|
|
34
|
+
const webglLabel = anim.webglDetected ? 'WebGL/3D' : '2D Canvas';
|
|
35
|
+
md += `| Canvas (${anim.canvasCount} elements) | ${webglLabel} | ${anim.webglDetected ? 'WebGL context detected — likely Three.js or custom shader' : '2D canvas rendering'} |\n`;
|
|
36
|
+
}
|
|
37
|
+
if (anim.lottieCount > 0) {
|
|
38
|
+
md += `| Lottie (${anim.lottieCount} players) | vector | JSON-based vector animations |\n`;
|
|
39
|
+
}
|
|
40
|
+
md += `\n`;
|
|
41
|
+
}
|
|
42
|
+
// ── Scroll Journey ──────────────────────────────────────────────────
|
|
43
|
+
if (anim.scrollFrames.length > 0) {
|
|
44
|
+
md += `## Scroll Journey\n\n`;
|
|
45
|
+
md += `The page is **${Math.round(anim.scrollFrames[0]?.pageHeight || 0).toLocaleString()}px** tall. `;
|
|
46
|
+
md += `Each frame below shows what the user sees at that scroll depth.\n\n`;
|
|
47
|
+
md += `> **Use these screenshots to understand WHAT animates, WHEN it animates, and HOW it moves.**\n\n`;
|
|
48
|
+
for (const frame of anim.scrollFrames) {
|
|
49
|
+
const sectionLabel = getScrollLabel(frame.scrollPercent);
|
|
50
|
+
md += `### ${frame.scrollPercent}% — ${sectionLabel}\n`;
|
|
51
|
+
md += `Scroll position: ${frame.scrollY.toLocaleString()}px\n\n`;
|
|
52
|
+
md += `\n\n`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ── Video Backgrounds ────────────────────────────────────────────────
|
|
56
|
+
if (anim.videos.length > 0) {
|
|
57
|
+
md += `## Video Elements\n\n`;
|
|
58
|
+
md += `| # | Role | Autoplay | Loop | Muted | Size | First Frame |\n`;
|
|
59
|
+
md += `|---|------|----------|------|-------|------|-------------|\n`;
|
|
60
|
+
for (const v of anim.videos) {
|
|
61
|
+
const size = v.width && v.height ? `${v.width}×${v.height}` : '—';
|
|
62
|
+
const frame = v.firstFramePath ? `[view](../${v.firstFramePath})` : '—';
|
|
63
|
+
md += `| ${v.index} | ${v.role} | ${v.autoplay ? '✓' : '—'} | ${v.loop ? '✓' : '—'} | ${v.muted ? '✓' : '—'} | ${size} | ${frame} |\n`;
|
|
64
|
+
}
|
|
65
|
+
md += `\n`;
|
|
66
|
+
for (const v of anim.videos) {
|
|
67
|
+
if (v.firstFramePath) {
|
|
68
|
+
md += `**Video ${v.index} first frame:**\n\n`;
|
|
69
|
+
md += `\n\n`;
|
|
70
|
+
}
|
|
71
|
+
if (v.src) {
|
|
72
|
+
md += `- **Source:** \`${v.src.slice(0, 120)}\`\n`;
|
|
73
|
+
}
|
|
74
|
+
if (v.poster)
|
|
75
|
+
md += `- **Poster:** \`${v.poster.slice(0, 120)}\`\n`;
|
|
76
|
+
}
|
|
77
|
+
md += `\n`;
|
|
78
|
+
}
|
|
79
|
+
// ── Scroll Animation Patterns ────────────────────────────────────────
|
|
80
|
+
if (anim.scrollPatterns.length > 0) {
|
|
81
|
+
md += `## Scroll Animation Patterns\n\n`;
|
|
82
|
+
md += `| Pattern | Library | Element Count | Duration | Delay | Easing |\n`;
|
|
83
|
+
md += `|---------|---------|---------------|----------|-------|--------|\n`;
|
|
84
|
+
for (const p of anim.scrollPatterns) {
|
|
85
|
+
md += `| ${p.animationType} | ${p.library} | ${p.count} | ${p.duration || '—'} | ${p.delay || '—'} | ${p.easing || '—'} |\n`;
|
|
86
|
+
}
|
|
87
|
+
md += `\n`;
|
|
88
|
+
// Implementation guide per library
|
|
89
|
+
const libsUsed = [...new Set(anim.scrollPatterns.map(p => p.library))];
|
|
90
|
+
for (const lib of libsUsed) {
|
|
91
|
+
const patterns = anim.scrollPatterns.filter(p => p.library === lib);
|
|
92
|
+
md += `### ${lib} Implementation\n\n`;
|
|
93
|
+
if (lib === 'AOS') {
|
|
94
|
+
md += `\`\`\`html\n<!-- Add to <head> -->\n<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">\n\n`;
|
|
95
|
+
md += `<!-- Add before </body> -->\n<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>\n<script>AOS.init({ once: true, offset: 80 });</script>\n\`\`\`\n\n`;
|
|
96
|
+
for (const p of patterns.slice(0, 5)) {
|
|
97
|
+
const dur = p.duration ? ` data-aos-duration="${p.duration}"` : '';
|
|
98
|
+
const del = p.delay ? ` data-aos-delay="${p.delay}"` : '';
|
|
99
|
+
md += `\`\`\`html\n<div data-aos="${p.animationType}"${dur}${del}>...</div>\n\`\`\`\n\n`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (lib.includes('GSAP')) {
|
|
103
|
+
md += `\`\`\`javascript\n// GSAP ScrollTrigger\ngsap.registerPlugin(ScrollTrigger);\n\n`;
|
|
104
|
+
md += `gsap.from('.element', {\n opacity: 0,\n y: 60,\n duration: 0.8,\n ease: 'power2.out',\n scrollTrigger: {\n trigger: '.element',\n start: 'top 80%',\n end: 'bottom 20%',\n }\n});\n\`\`\`\n\n`;
|
|
105
|
+
}
|
|
106
|
+
else if (lib === 'CSS + IntersectionObserver') {
|
|
107
|
+
md += `\`\`\`css\n.animate-on-scroll {\n opacity: 0;\n transform: translateY(40px);\n animation: fadeSlideUp 0.6s ease-out forwards;\n animation-play-state: paused;\n}\n.animate-on-scroll.visible {\n animation-play-state: running;\n}\n\`\`\`\n\n`;
|
|
108
|
+
md += `\`\`\`javascript\nconst observer = new IntersectionObserver((entries) => {\n entries.forEach(e => {\n if (e.isIntersecting) e.target.classList.add('visible');\n });\n}, { threshold: 0.1 });\ndocument.querySelectorAll('.animate-on-scroll').forEach(el => observer.observe(el));\n\`\`\`\n\n`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ── CSS Keyframes ─────────────────────────────────────────────────────
|
|
113
|
+
if (anim.keyframes.length > 0) {
|
|
114
|
+
md += `## CSS Keyframes (${anim.keyframes.length} extracted)\n\n`;
|
|
115
|
+
// Sort: most-used keyframes first
|
|
116
|
+
const sorted = [...anim.keyframes].sort((a, b) => (b.usedBy.length) - (a.usedBy.length));
|
|
117
|
+
for (const kf of sorted) {
|
|
118
|
+
md += `### \`@keyframes ${kf.name}\`\n\n`;
|
|
119
|
+
// Meta row
|
|
120
|
+
const meta = [];
|
|
121
|
+
if (kf.animDuration)
|
|
122
|
+
meta.push(`Duration: \`${kf.animDuration}\``);
|
|
123
|
+
if (kf.animEasing)
|
|
124
|
+
meta.push(`Easing: \`${kf.animEasing}\``);
|
|
125
|
+
if (kf.animDelay)
|
|
126
|
+
meta.push(`Delay: \`${kf.animDelay}\``);
|
|
127
|
+
if (kf.animIteration)
|
|
128
|
+
meta.push(`Iteration: \`${kf.animIteration}\``);
|
|
129
|
+
if (kf.animFillMode)
|
|
130
|
+
meta.push(`Fill: \`${kf.animFillMode}\``);
|
|
131
|
+
if (meta.length > 0)
|
|
132
|
+
md += meta.join(' · ') + `\n\n`;
|
|
133
|
+
if (kf.usedBy.length > 0) {
|
|
134
|
+
md += `Used by: ${kf.usedBy.slice(0, 4).map(s => `\`${s}\``).join(', ')}\n\n`;
|
|
135
|
+
}
|
|
136
|
+
// Full keyframe code
|
|
137
|
+
md += `\`\`\`css\n@keyframes ${kf.name} {\n`;
|
|
138
|
+
for (const stop of kf.stops) {
|
|
139
|
+
md += ` ${stop.stop} {\n`;
|
|
140
|
+
for (const [prop, val] of Object.entries(stop.properties)) {
|
|
141
|
+
if (val && val !== 'initial') {
|
|
142
|
+
md += ` ${prop}: ${val};\n`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
md += ` }\n`;
|
|
146
|
+
}
|
|
147
|
+
md += `}\n\`\`\`\n\n`;
|
|
148
|
+
// Detect animation type and describe what it does
|
|
149
|
+
const description = describeKeyframe(kf);
|
|
150
|
+
if (description)
|
|
151
|
+
md += `> ${description}\n\n`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ── Animation CSS Variables ───────────────────────────────────────────
|
|
155
|
+
if (anim.animationVars.length > 0) {
|
|
156
|
+
md += `## Motion Tokens (CSS Variables)\n\n`;
|
|
157
|
+
const byCategory = {};
|
|
158
|
+
for (const v of anim.animationVars) {
|
|
159
|
+
if (!byCategory[v.category])
|
|
160
|
+
byCategory[v.category] = [];
|
|
161
|
+
byCategory[v.category].push(v);
|
|
162
|
+
}
|
|
163
|
+
const catOrder = ['duration', 'easing', 'delay', 'animation', 'other'];
|
|
164
|
+
for (const cat of catOrder) {
|
|
165
|
+
const vars = byCategory[cat];
|
|
166
|
+
if (!vars?.length)
|
|
167
|
+
continue;
|
|
168
|
+
md += `### ${cat.charAt(0).toUpperCase() + cat.slice(1)} Tokens\n\n`;
|
|
169
|
+
md += `\`\`\`css\n`;
|
|
170
|
+
for (const v of vars) {
|
|
171
|
+
md += `${v.name}: ${v.value};\n`;
|
|
172
|
+
}
|
|
173
|
+
md += `\`\`\`\n\n`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ── Global Transitions ────────────────────────────────────────────────
|
|
177
|
+
if (anim.globalTransitions.length > 0) {
|
|
178
|
+
md += `## Global Transition Declarations\n\n`;
|
|
179
|
+
md += `These \`transition\` values were extracted from CSS rules across the site:\n\n`;
|
|
180
|
+
md += `\`\`\`css\n`;
|
|
181
|
+
const uniqueT = [...new Set(anim.globalTransitions)].slice(0, 12);
|
|
182
|
+
for (const t of uniqueT) {
|
|
183
|
+
md += `transition: ${t};\n`;
|
|
184
|
+
}
|
|
185
|
+
md += `\`\`\`\n\n`;
|
|
186
|
+
}
|
|
187
|
+
// ── Implementation Guide ──────────────────────────────────────────────
|
|
188
|
+
md += `## How to Recreate This Motion Design\n\n`;
|
|
189
|
+
// Step 1: Libraries
|
|
190
|
+
if (anim.libraries.length > 0) {
|
|
191
|
+
md += `### Step 1 — Install Dependencies\n\n\`\`\`bash\n`;
|
|
192
|
+
for (const lib of anim.libraries) {
|
|
193
|
+
const pkg = libraryToPackage(lib.name);
|
|
194
|
+
if (pkg)
|
|
195
|
+
md += `npm install ${pkg}\n`;
|
|
196
|
+
}
|
|
197
|
+
md += `\`\`\`\n\n`;
|
|
198
|
+
}
|
|
199
|
+
// Step 2: Scroll animations
|
|
200
|
+
md += `### Step 2 — Scroll-Reveal Pattern\n\n`;
|
|
201
|
+
md += `Elements that animate into view follow this pattern:\n\n`;
|
|
202
|
+
md += `\`\`\`css\n/* Initial hidden state */\n.reveal {\n opacity: 0;\n transform: translateY(40px);\n`;
|
|
203
|
+
const mainDuration = anim.animationVars.find(v => v.category === 'duration')?.value
|
|
204
|
+
|| anim.globalTransitions[0]?.match(/\d+\.?\d*(?:ms|s)/)?.[0]
|
|
205
|
+
|| '0.6s';
|
|
206
|
+
const mainEase = anim.animationVars.find(v => v.category === 'easing')?.value
|
|
207
|
+
|| 'cubic-bezier(0.4, 0, 0.2, 1)';
|
|
208
|
+
md += ` transition: opacity ${mainDuration} ${mainEase},\n transform ${mainDuration} ${mainEase};\n}\n.reveal.visible {\n opacity: 1;\n transform: translateY(0);\n}\n\`\`\`\n\n`;
|
|
209
|
+
// Step 3: Key animation recommendations
|
|
210
|
+
md += `### Step 3 — Key Motion Principles\n\n`;
|
|
211
|
+
if (anim.webglDetected) {
|
|
212
|
+
md += `- **WebGL/3D layer detected** — product visualizations use Three.js or custom WebGL. Use \`<canvas>\` with Three.js for 3D product renders\n`;
|
|
213
|
+
}
|
|
214
|
+
if (anim.videos.filter(v => v.role === 'background').length > 0) {
|
|
215
|
+
md += `- **Video backgrounds** — use \`<video autoplay loop muted playsinline>\` for background videos. Always include a poster image fallback\n`;
|
|
216
|
+
}
|
|
217
|
+
if (anim.libraries.some(l => l.name === 'GSAP' || l.name === 'ScrollTrigger')) {
|
|
218
|
+
md += `- **GSAP ScrollTrigger** — scroll-linked animations (product rotation, parallax) use \`ScrollTrigger.scrub\` for frame-perfect scroll sync\n`;
|
|
219
|
+
}
|
|
220
|
+
if (anim.canvasCount > 0) {
|
|
221
|
+
md += `- **Canvas elements (${anim.canvasCount})** — animated via requestAnimationFrame loop. Use canvas for particle effects, gradient animations, and WebGL scenes\n`;
|
|
222
|
+
}
|
|
223
|
+
// Extract main transition duration for reference
|
|
224
|
+
const durations = anim.animationVars.filter(v => v.category === 'duration').map(v => v.value);
|
|
225
|
+
const globalDurations = anim.globalTransitions
|
|
226
|
+
.join(' ')
|
|
227
|
+
.match(/\d+\.?\d*(?:ms|s)/g)
|
|
228
|
+
?.slice(0, 5) || [];
|
|
229
|
+
const allDurations = [...new Set([...durations, ...globalDurations])];
|
|
230
|
+
if (allDurations.length > 0) {
|
|
231
|
+
md += `- **Duration scale:** ${allDurations.map(d => `\`${d}\``).join(' · ')} — use these values, never invent new durations\n`;
|
|
232
|
+
}
|
|
233
|
+
md += `- **Always add** \`@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }\`\n`;
|
|
234
|
+
md += `\n`;
|
|
235
|
+
// Step 4: Scroll journey reference
|
|
236
|
+
if (anim.scrollFrames.length > 0) {
|
|
237
|
+
md += `### Step 4 — Scroll Journey Reference\n\n`;
|
|
238
|
+
md += `Match what happens at each scroll position:\n\n`;
|
|
239
|
+
for (const f of anim.scrollFrames) {
|
|
240
|
+
md += `- **${f.scrollPercent}%** (\`${f.scrollY}px\`) → \`${f.filePath}\`\n`;
|
|
241
|
+
}
|
|
242
|
+
md += `\n`;
|
|
243
|
+
}
|
|
244
|
+
return md;
|
|
245
|
+
}
|
|
246
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
247
|
+
function getScrollLabel(pct) {
|
|
248
|
+
if (pct === 0)
|
|
249
|
+
return 'Top / Hero';
|
|
250
|
+
if (pct <= 20)
|
|
251
|
+
return 'Opening Section';
|
|
252
|
+
if (pct <= 40)
|
|
253
|
+
return 'First Feature Section';
|
|
254
|
+
if (pct <= 60)
|
|
255
|
+
return 'Mid-Page';
|
|
256
|
+
if (pct <= 80)
|
|
257
|
+
return 'Lower Content';
|
|
258
|
+
if (pct < 100)
|
|
259
|
+
return 'Near Footer';
|
|
260
|
+
return 'Bottom / Footer';
|
|
261
|
+
}
|
|
262
|
+
function describeKeyframe(kf) {
|
|
263
|
+
const allProps = kf.stops.flatMap(s => Object.keys(s.properties));
|
|
264
|
+
const uniq = [...new Set(allProps)];
|
|
265
|
+
const has = (p) => uniq.some(u => u.includes(p));
|
|
266
|
+
const descriptions = [];
|
|
267
|
+
if (has('opacity') && has('transform'))
|
|
268
|
+
descriptions.push('Fade + motion enter animation');
|
|
269
|
+
else if (has('opacity'))
|
|
270
|
+
descriptions.push('Opacity fade');
|
|
271
|
+
else if (has('transform'))
|
|
272
|
+
descriptions.push('Transform/motion animation');
|
|
273
|
+
if (has('background'))
|
|
274
|
+
descriptions.push('Background color/gradient shift');
|
|
275
|
+
if (has('clip-path') || has('clipPath'))
|
|
276
|
+
descriptions.push('Clip-path reveal');
|
|
277
|
+
if (has('filter'))
|
|
278
|
+
descriptions.push('Filter effect (blur/brightness)');
|
|
279
|
+
if (has('stroke'))
|
|
280
|
+
descriptions.push('SVG stroke animation');
|
|
281
|
+
if (has('width') || has('height') || has('max-height') || has('max-width'))
|
|
282
|
+
descriptions.push('Dimension expand/collapse');
|
|
283
|
+
if (has('border'))
|
|
284
|
+
descriptions.push('Border animation');
|
|
285
|
+
if (has('box-shadow'))
|
|
286
|
+
descriptions.push('Shadow pulse/glow effect');
|
|
287
|
+
if (has('background-position'))
|
|
288
|
+
descriptions.push('Background position (shimmer/scroll)');
|
|
289
|
+
if (has('color'))
|
|
290
|
+
descriptions.push('Text color shift');
|
|
291
|
+
return descriptions.slice(0, 2).join(' · ');
|
|
292
|
+
}
|
|
293
|
+
function libraryToPackage(name) {
|
|
294
|
+
const map = {
|
|
295
|
+
'GSAP': 'gsap',
|
|
296
|
+
'ScrollTrigger': 'gsap',
|
|
297
|
+
'ScrollSmoother': 'gsap',
|
|
298
|
+
'Lottie': 'lottie-web',
|
|
299
|
+
'Bodymovin (Lottie)': 'lottie-web',
|
|
300
|
+
'Three.js': 'three',
|
|
301
|
+
'PixiJS': 'pixi.js',
|
|
302
|
+
'Framer Motion': 'framer-motion',
|
|
303
|
+
'Motion One / Framer Motion': 'motion',
|
|
304
|
+
'AOS (Animate On Scroll)': 'aos',
|
|
305
|
+
'AOS': 'aos',
|
|
306
|
+
'Anime.js': 'animejs',
|
|
307
|
+
'Velocity.js': 'velocity-animate',
|
|
308
|
+
'Matter.js (Physics)': 'matter-js',
|
|
309
|
+
'Locomotive Scroll': 'locomotive-scroll',
|
|
310
|
+
};
|
|
311
|
+
return map[name] || '';
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=animations-md.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DOMComponent } from '../types-ultra';
|
|
2
|
+
import { DesignProfile } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Generate references/COMPONENTS.md
|
|
5
|
+
* Documents all detected repeated DOM components with HTML snippets.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateComponentsMd(domComponents: DOMComponent[], profile: DesignProfile): string;
|
|
8
|
+
//# sourceMappingURL=components-md.d.ts.map
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateComponentsMd = generateComponentsMd;
|
|
4
|
+
/**
|
|
5
|
+
* Generate references/COMPONENTS.md
|
|
6
|
+
* Documents all detected repeated DOM components with HTML snippets.
|
|
7
|
+
*/
|
|
8
|
+
function generateComponentsMd(domComponents, profile) {
|
|
9
|
+
let md = `# Component Reference\n\n`;
|
|
10
|
+
md += `> Repeated DOM patterns detected by structural analysis. Each component appeared 3+ times.\n\n`;
|
|
11
|
+
if (domComponents.length === 0) {
|
|
12
|
+
md += `No repeated components detected (Playwright required).\n`;
|
|
13
|
+
return md;
|
|
14
|
+
}
|
|
15
|
+
// ── Overview ────────────────────────────────────────────────────────
|
|
16
|
+
md += `## Detected Components\n\n`;
|
|
17
|
+
md += `| Component | Category | Instances | Key Classes |\n`;
|
|
18
|
+
md += `|-----------|----------|-----------|-------------|\n`;
|
|
19
|
+
for (const c of domComponents) {
|
|
20
|
+
const classes = c.commonClasses.slice(0, 3).map(cl => `\`.${cl}\``).join(', ');
|
|
21
|
+
md += `| **${c.name}** | ${c.category} | ${c.instances}× | ${classes} |\n`;
|
|
22
|
+
}
|
|
23
|
+
md += `\n`;
|
|
24
|
+
// ── Category Groups ─────────────────────────────────────────────────
|
|
25
|
+
const byCategory = groupBy(domComponents, c => c.category);
|
|
26
|
+
const categoryOrder = [
|
|
27
|
+
'card', 'list-item', 'nav-item', 'button', 'badge', 'form-field', 'unknown'
|
|
28
|
+
];
|
|
29
|
+
const accent = profile.colors.find(c => c.role === 'accent');
|
|
30
|
+
const bg = profile.colors.find(c => c.role === 'background');
|
|
31
|
+
const surface = profile.colors.find(c => c.role === 'surface');
|
|
32
|
+
const border = profile.colors.find(c => c.role === 'border');
|
|
33
|
+
const textPrimary = profile.colors.find(c => c.role === 'text-primary');
|
|
34
|
+
const commonRadius = profile.borderRadius.filter(r => !r.includes('9999'))[Math.floor(profile.borderRadius.length / 2)] || '8px';
|
|
35
|
+
for (const category of categoryOrder) {
|
|
36
|
+
const comps = byCategory[category];
|
|
37
|
+
if (!comps?.length)
|
|
38
|
+
continue;
|
|
39
|
+
md += `## ${formatCategory(category)}\n\n`;
|
|
40
|
+
for (const comp of comps) {
|
|
41
|
+
md += `### ${comp.name}\n\n`;
|
|
42
|
+
md += `**Instances found:** ${comp.instances}\n\n`;
|
|
43
|
+
if (comp.commonClasses.length > 0) {
|
|
44
|
+
md += `**CSS classes:** ${comp.commonClasses.map(c => `\`.${c}\``).join(' ')}\n\n`;
|
|
45
|
+
}
|
|
46
|
+
// HTML snippet
|
|
47
|
+
md += `**HTML structure:**\n\n`;
|
|
48
|
+
md += `\`\`\`html\n`;
|
|
49
|
+
md += `${comp.htmlSnippet}\n`;
|
|
50
|
+
md += `\`\`\`\n\n`;
|
|
51
|
+
// Suggested base CSS from design tokens
|
|
52
|
+
const suggestedCss = buildSuggestedCss(comp, {
|
|
53
|
+
accent, bg, surface, border, textPrimary, commonRadius, profile
|
|
54
|
+
});
|
|
55
|
+
if (suggestedCss) {
|
|
56
|
+
md += `**Base styles (from design tokens):**\n\n`;
|
|
57
|
+
md += `\`\`\`css\n`;
|
|
58
|
+
md += suggestedCss;
|
|
59
|
+
md += `\`\`\`\n\n`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ── Rules ───────────────────────────────────────────────────────────
|
|
64
|
+
md += `## Component Rules\n\n`;
|
|
65
|
+
md += `- Match class names exactly from the patterns above\n`;
|
|
66
|
+
md += `- Each component instance must be visually identical to others of its type\n`;
|
|
67
|
+
md += `- Do not add extra wrappers or change the DOM structure\n`;
|
|
68
|
+
if (border) {
|
|
69
|
+
md += `- Use \`${border.hex}\` for all dividers within components\n`;
|
|
70
|
+
}
|
|
71
|
+
if (accent) {
|
|
72
|
+
md += `- Use \`${accent.hex}\` for all interactive/active states\n`;
|
|
73
|
+
}
|
|
74
|
+
md += `\n`;
|
|
75
|
+
return md;
|
|
76
|
+
}
|
|
77
|
+
function buildSuggestedCss(comp, tokens) {
|
|
78
|
+
const { accent, surface, border, textPrimary, commonRadius, profile } = tokens;
|
|
79
|
+
const sp = profile.spacing;
|
|
80
|
+
const pad = sp.base * 2;
|
|
81
|
+
const lines = [];
|
|
82
|
+
const mainClass = comp.commonClasses[0] || comp.name.toLowerCase().replace(/\s+/g, '-');
|
|
83
|
+
lines.push(`.${mainClass} {`);
|
|
84
|
+
switch (comp.category) {
|
|
85
|
+
case 'card':
|
|
86
|
+
if (surface)
|
|
87
|
+
lines.push(` background: ${surface.hex};`);
|
|
88
|
+
if (border)
|
|
89
|
+
lines.push(` border: 1px solid ${border.hex};`);
|
|
90
|
+
lines.push(` border-radius: ${commonRadius};`);
|
|
91
|
+
lines.push(` padding: ${pad}px;`);
|
|
92
|
+
break;
|
|
93
|
+
case 'button':
|
|
94
|
+
if (accent)
|
|
95
|
+
lines.push(` background: ${accent.hex};`);
|
|
96
|
+
if (textPrimary)
|
|
97
|
+
lines.push(` color: ${textPrimary.hex};`);
|
|
98
|
+
lines.push(` border-radius: ${commonRadius};`);
|
|
99
|
+
lines.push(` padding: ${sp.base}px ${pad}px;`);
|
|
100
|
+
lines.push(` cursor: pointer;`);
|
|
101
|
+
break;
|
|
102
|
+
case 'badge':
|
|
103
|
+
if (surface)
|
|
104
|
+
lines.push(` background: ${surface.hex};`);
|
|
105
|
+
if (border)
|
|
106
|
+
lines.push(` border: 1px solid ${border.hex};`);
|
|
107
|
+
lines.push(` border-radius: ${commonRadius};`);
|
|
108
|
+
lines.push(` padding: ${Math.round(sp.base * 0.5)}px ${sp.base}px;`);
|
|
109
|
+
lines.push(` font-size: 12px;`);
|
|
110
|
+
break;
|
|
111
|
+
case 'nav-item':
|
|
112
|
+
lines.push(` padding: ${sp.base}px ${pad}px;`);
|
|
113
|
+
lines.push(` cursor: pointer;`);
|
|
114
|
+
if (accent)
|
|
115
|
+
lines.push(` /* active: color: ${accent.hex}; */`);
|
|
116
|
+
break;
|
|
117
|
+
case 'list-item':
|
|
118
|
+
lines.push(` padding: ${sp.base}px 0;`);
|
|
119
|
+
if (border)
|
|
120
|
+
lines.push(` border-bottom: 1px solid ${border.hex};`);
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
if (surface)
|
|
124
|
+
lines.push(` background: ${surface.hex};`);
|
|
125
|
+
lines.push(` padding: ${sp.base}px;`);
|
|
126
|
+
}
|
|
127
|
+
lines.push(`}`);
|
|
128
|
+
return lines.join('\n');
|
|
129
|
+
}
|
|
130
|
+
function formatCategory(cat) {
|
|
131
|
+
const map = {
|
|
132
|
+
card: 'Cards',
|
|
133
|
+
'list-item': 'List Items',
|
|
134
|
+
'nav-item': 'Navigation Items',
|
|
135
|
+
button: 'Buttons',
|
|
136
|
+
badge: 'Badges & Chips',
|
|
137
|
+
'form-field': 'Form Fields',
|
|
138
|
+
unknown: 'Other Components',
|
|
139
|
+
};
|
|
140
|
+
return map[cat] || cat;
|
|
141
|
+
}
|
|
142
|
+
function groupBy(arr, key) {
|
|
143
|
+
return arr.reduce((acc, item) => {
|
|
144
|
+
const k = key(item);
|
|
145
|
+
if (!acc[k])
|
|
146
|
+
acc[k] = [];
|
|
147
|
+
acc[k].push(item);
|
|
148
|
+
return acc;
|
|
149
|
+
}, {});
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=components-md.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DesignProfile } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Generate DESIGN.md in Google Stitch / awesome-design-md format.
|
|
4
|
+
* Pure template-driven — no AI.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateDesignMd(profile: DesignProfile, screenshotPath?: string | null): string;
|
|
7
|
+
//# sourceMappingURL=design-md.d.ts.map
|