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.
Files changed (59) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.js +202 -0
  3. package/dist/extractors/components.d.ts +11 -0
  4. package/dist/extractors/components.js +455 -0
  5. package/dist/extractors/framework.d.ts +4 -0
  6. package/dist/extractors/framework.js +126 -0
  7. package/dist/extractors/tokens/computed.d.ts +7 -0
  8. package/dist/extractors/tokens/computed.js +249 -0
  9. package/dist/extractors/tokens/css.d.ts +3 -0
  10. package/dist/extractors/tokens/css.js +510 -0
  11. package/dist/extractors/tokens/http-css.d.ts +14 -0
  12. package/dist/extractors/tokens/http-css.js +1689 -0
  13. package/dist/extractors/tokens/tailwind.d.ts +3 -0
  14. package/dist/extractors/tokens/tailwind.js +353 -0
  15. package/dist/extractors/tokens/tokens-file.d.ts +3 -0
  16. package/dist/extractors/tokens/tokens-file.js +229 -0
  17. package/dist/extractors/ultra/animations.d.ts +21 -0
  18. package/dist/extractors/ultra/animations.js +530 -0
  19. package/dist/extractors/ultra/components-dom.d.ts +13 -0
  20. package/dist/extractors/ultra/components-dom.js +152 -0
  21. package/dist/extractors/ultra/interactions.d.ts +14 -0
  22. package/dist/extractors/ultra/interactions.js +225 -0
  23. package/dist/extractors/ultra/layout.d.ts +14 -0
  24. package/dist/extractors/ultra/layout.js +126 -0
  25. package/dist/extractors/ultra/pages.d.ts +16 -0
  26. package/dist/extractors/ultra/pages.js +231 -0
  27. package/dist/font-resolver.d.ts +10 -0
  28. package/dist/font-resolver.js +280 -0
  29. package/dist/modes/dir.d.ts +6 -0
  30. package/dist/modes/dir.js +213 -0
  31. package/dist/modes/repo.d.ts +6 -0
  32. package/dist/modes/repo.js +76 -0
  33. package/dist/modes/ultra.d.ts +22 -0
  34. package/dist/modes/ultra.js +285 -0
  35. package/dist/modes/url.d.ts +14 -0
  36. package/dist/modes/url.js +161 -0
  37. package/dist/normalizer.d.ts +11 -0
  38. package/dist/normalizer.js +867 -0
  39. package/dist/screenshot.d.ts +9 -0
  40. package/dist/screenshot.js +94 -0
  41. package/dist/types-ultra.d.ts +157 -0
  42. package/dist/types-ultra.js +4 -0
  43. package/dist/types.d.ts +182 -0
  44. package/dist/types.js +4 -0
  45. package/dist/writers/animations-md.d.ts +17 -0
  46. package/dist/writers/animations-md.js +313 -0
  47. package/dist/writers/components-md.d.ts +8 -0
  48. package/dist/writers/components-md.js +151 -0
  49. package/dist/writers/design-md.d.ts +7 -0
  50. package/dist/writers/design-md.js +704 -0
  51. package/dist/writers/interactions-md.d.ts +8 -0
  52. package/dist/writers/interactions-md.js +146 -0
  53. package/dist/writers/layout-md.d.ts +8 -0
  54. package/dist/writers/layout-md.js +120 -0
  55. package/dist/writers/skill.d.ts +12 -0
  56. package/dist/writers/skill.js +1006 -0
  57. package/dist/writers/tokens-json.d.ts +11 -0
  58. package/dist/writers/tokens-json.js +164 -0
  59. package/package.json +78 -0
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.capturePageScreenshots = capturePageScreenshots;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Ultra mode — Page & Section Screenshots
41
+ *
42
+ * 1. Crawl up to `maxPages` internal links from the origin URL
43
+ * 2. Take a full-page screenshot for each (screens/pages/[slug].png)
44
+ * 3. Detect major sections (section, article, main > div, height > 300px)
45
+ * and clip one screenshot per section (screens/sections/[page]-section-N.png)
46
+ *
47
+ * Requires Playwright (optional peer dependency).
48
+ */
49
+ async function capturePageScreenshots(originUrl, skillDir, maxPages) {
50
+ let playwright;
51
+ try {
52
+ playwright = require('playwright');
53
+ }
54
+ catch {
55
+ return { pages: [], sections: [] };
56
+ }
57
+ const pagesDir = path.join(skillDir, 'screens', 'pages');
58
+ const sectionsDir = path.join(skillDir, 'screens', 'sections');
59
+ fs.mkdirSync(pagesDir, { recursive: true });
60
+ fs.mkdirSync(sectionsDir, { recursive: true });
61
+ const pages = [];
62
+ const sections = [];
63
+ const browser = await playwright.chromium.launch({ headless: true });
64
+ const context = await browser.newContext({
65
+ viewport: { width: 1440, height: 900 },
66
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
67
+ });
68
+ try {
69
+ const origin = new URL(originUrl).origin;
70
+ const visited = new Set();
71
+ const queue = [originUrl];
72
+ while (queue.length > 0 && visited.size < maxPages) {
73
+ const url = queue.shift();
74
+ const normalized = normalizeUrl(url);
75
+ if (visited.has(normalized))
76
+ continue;
77
+ visited.add(normalized);
78
+ const slug = urlToSlug(url, origin);
79
+ const pageFile = path.join(pagesDir, `${slug}.png`);
80
+ try {
81
+ const page = await context.newPage();
82
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 25000 });
83
+ await page.waitForTimeout(1500);
84
+ // Full-page screenshot
85
+ await page.screenshot({ path: pageFile, fullPage: true });
86
+ const title = await page.title().catch(() => slug);
87
+ pages.push({
88
+ url,
89
+ slug,
90
+ filePath: `screens/pages/${slug}.png`,
91
+ title: title || slug,
92
+ });
93
+ // Section screenshots
94
+ const sectionData = await page.evaluate(() => {
95
+ const SECTION_SELECTORS = [
96
+ 'section',
97
+ 'article',
98
+ 'header',
99
+ 'footer',
100
+ 'nav',
101
+ 'main > div',
102
+ 'main > section',
103
+ '[class*="section"]',
104
+ '[class*="hero"]',
105
+ '[class*="features"]',
106
+ '[class*="pricing"]',
107
+ '[class*="testimonial"]',
108
+ '[class*="faq"]',
109
+ '[class*="cta"]',
110
+ ];
111
+ const candidates = [];
112
+ for (const sel of SECTION_SELECTORS) {
113
+ const els = document.querySelectorAll(sel);
114
+ els.forEach((el) => {
115
+ const rect = el.getBoundingClientRect();
116
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
117
+ // Must be wide (>= 60% viewport) and tall (>= 200px)
118
+ if (rect.width >= window.innerWidth * 0.6 && rect.height >= 200) {
119
+ candidates.push({
120
+ selector: sel,
121
+ rect: {
122
+ x: Math.max(0, rect.left),
123
+ y: Math.max(0, rect.top + scrollTop),
124
+ width: Math.min(rect.width, 1440),
125
+ height: Math.min(rect.height, 1200),
126
+ },
127
+ });
128
+ }
129
+ });
130
+ }
131
+ // Deduplicate: remove sections whose top is within 50px of another
132
+ const deduped = [];
133
+ for (const c of candidates) {
134
+ const overlap = deduped.some((d) => Math.abs(d.rect.y - c.rect.y) < 50);
135
+ if (!overlap)
136
+ deduped.push(c);
137
+ }
138
+ return deduped.slice(0, 10);
139
+ });
140
+ for (let i = 0; i < sectionData.length; i++) {
141
+ const sec = sectionData[i];
142
+ const secFile = `${slug}-section-${i + 1}.png`;
143
+ const secPath = path.join(sectionsDir, secFile);
144
+ try {
145
+ await page.screenshot({
146
+ path: secPath,
147
+ clip: {
148
+ x: sec.rect.x,
149
+ y: sec.rect.y,
150
+ width: sec.rect.width,
151
+ height: sec.rect.height,
152
+ },
153
+ });
154
+ sections.push({
155
+ page: slug,
156
+ index: i + 1,
157
+ filePath: `screens/sections/${secFile}`,
158
+ selector: sec.selector,
159
+ height: Math.round(sec.rect.height),
160
+ width: Math.round(sec.rect.width),
161
+ });
162
+ }
163
+ catch {
164
+ // Section clip failed — skip
165
+ }
166
+ }
167
+ // Discover internal links for queue
168
+ if (visited.size < maxPages) {
169
+ const links = await page.evaluate((origin) => {
170
+ return Array.from(document.querySelectorAll('a[href]'))
171
+ .map((a) => a.href)
172
+ .filter((href) => {
173
+ try {
174
+ const u = new URL(href);
175
+ return (u.origin === origin &&
176
+ !u.pathname.match(/\.(pdf|zip|png|jpg|svg|ico|css|js|xml|json|txt)$/i) &&
177
+ !u.hash);
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ })
183
+ .slice(0, 20);
184
+ }, origin);
185
+ for (const link of links) {
186
+ const norm = normalizeUrl(link);
187
+ if (!visited.has(norm) && !queue.includes(link)) {
188
+ queue.push(link);
189
+ }
190
+ }
191
+ }
192
+ await page.close();
193
+ }
194
+ catch (err) {
195
+ // Page failed — continue with next
196
+ try {
197
+ await context.pages()[0]?.close();
198
+ }
199
+ catch { }
200
+ }
201
+ }
202
+ }
203
+ finally {
204
+ await browser.close().catch(() => { });
205
+ }
206
+ return { pages, sections };
207
+ }
208
+ function normalizeUrl(url) {
209
+ try {
210
+ const u = new URL(url);
211
+ return `${u.origin}${u.pathname}`.replace(/\/$/, '');
212
+ }
213
+ catch {
214
+ return url;
215
+ }
216
+ }
217
+ function urlToSlug(url, origin) {
218
+ try {
219
+ const u = new URL(url);
220
+ const rel = u.pathname.replace(/^\//, '').replace(/\/$/, '') || 'home';
221
+ return rel
222
+ .replace(/[^a-zA-Z0-9/]/g, '-')
223
+ .replace(/\//g, '--')
224
+ .replace(/-{2,}/g, '-')
225
+ .slice(0, 60) || 'home';
226
+ }
227
+ catch {
228
+ return 'home';
229
+ }
230
+ }
231
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1,10 @@
1
+ import { FontSource } from './types';
2
+ /**
3
+ * Fetch font files from Google Fonts API and bundle them into the skill directory.
4
+ * Downloads ALL available weights (non-italic) for each font family.
5
+ */
6
+ export declare function bundleFonts(fontSources: FontSource[], fontFamilies: string[], skillDir: string, _usedWeights?: Set<string>): Promise<{
7
+ updatedSources: FontSource[];
8
+ bundledCount: number;
9
+ }>;
10
+ //# sourceMappingURL=font-resolver.d.ts.map
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.bundleFonts = bundleFonts;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Resolve font families via the Google Fonts API.
41
+ *
42
+ * Downloads real .ttf files for each font family needed by the project.
43
+ * No local fonts-main/ directory required.
44
+ */
45
+ const GOOGLE_FONTS_API_KEY = 'AIzaSyCETey82fDURE2zp-MPF2lb_R-9PeAcPjY';
46
+ const GOOGLE_FONTS_API = `https://www.googleapis.com/webfonts/v1/webfonts`;
47
+ /** Returns true for fonts that cannot or should not be downloaded (system, generic, icon). */
48
+ function isSkippableFont(family) {
49
+ if (!family)
50
+ return true;
51
+ // Generic CSS families
52
+ if (/^(sans-serif|serif|monospace|cursive|fantasy|system-ui|ui-sans-serif|ui-serif|ui-monospace|inherit|initial|unset)$/i.test(family))
53
+ return true;
54
+ // System fonts (OS-level, not on Google Fonts)
55
+ if (/^(-apple-system|blinkmacsystemfont|\.sf\s*(pro|compact)|sf\s*(pro|compact)|segoe\s*ui|helvetica\s*neue|helvetica|arial)/i.test(family))
56
+ return true;
57
+ // Proprietary fonts (Google internal, etc.)
58
+ if (/^(google\s*sans|product\s*sans|roboto\s*flex)/i.test(family))
59
+ return false; // These ARE on Google Fonts
60
+ // Icon/symbol fonts
61
+ if (/^(apple\s*(icons?|legacy|sf\s*symbols?)|material\s*(icons?|symbols?)|font\s*awesome|fontawesome|glyphicons?|ionicons?)/i.test(family))
62
+ return true;
63
+ if (/^apple\s*icons?\s*\d+/i.test(family))
64
+ return true;
65
+ return false;
66
+ }
67
+ /**
68
+ * Query Google Fonts API for a specific font family.
69
+ * Returns download URLs for each weight variant (TTF format).
70
+ */
71
+ async function fetchFontMetadata(family) {
72
+ // No capability param — default returns real .ttf URLs
73
+ const url = `${GOOGLE_FONTS_API}?key=${GOOGLE_FONTS_API_KEY}&family=${encodeURIComponent(family)}`;
74
+ try {
75
+ const res = await fetch(url);
76
+ if (!res.ok)
77
+ return [];
78
+ const data = await res.json();
79
+ if (!data.items || data.items.length === 0)
80
+ return [];
81
+ const item = data.items[0];
82
+ const files = [];
83
+ // item.files is { "regular": "url", "700": "url", "italic": "url", ... }
84
+ if (item.files) {
85
+ for (const [variant, fileUrl] of Object.entries(item.files)) {
86
+ // Skip italic variants to keep package small
87
+ if (/italic/i.test(variant))
88
+ continue;
89
+ const weight = variantToWeight(variant);
90
+ const safeName = family.replace(/\s+/g, '') + '-' + weightToLabel(variant) + '.ttf';
91
+ files.push({
92
+ family: item.family || family,
93
+ weight,
94
+ url: fileUrl,
95
+ fileName: safeName,
96
+ });
97
+ }
98
+ }
99
+ return files;
100
+ }
101
+ catch {
102
+ return [];
103
+ }
104
+ }
105
+ function variantToWeight(variant) {
106
+ const map = {
107
+ '100': '100', '200': '200', '300': '300',
108
+ 'regular': '400', '400': '400',
109
+ '500': '500', '600': '600',
110
+ '700': '700', '800': '800', '900': '900',
111
+ };
112
+ return map[variant] || '400';
113
+ }
114
+ function weightToLabel(variant) {
115
+ const map = {
116
+ '100': 'Thin',
117
+ '200': 'ExtraLight',
118
+ '300': 'Light',
119
+ 'regular': 'Regular',
120
+ '400': 'Regular',
121
+ '500': 'Medium',
122
+ '600': 'SemiBold',
123
+ '700': 'Bold',
124
+ '800': 'ExtraBold',
125
+ '900': 'Black',
126
+ };
127
+ return map[variant] || variant;
128
+ }
129
+ /**
130
+ * Download a font file from a URL and save it locally.
131
+ * Accepts TTF, OTF, and WOFF2 formats.
132
+ */
133
+ async function downloadFont(url, destPath) {
134
+ try {
135
+ const res = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
136
+ if (!res.ok)
137
+ return false;
138
+ const buffer = Buffer.from(await res.arrayBuffer());
139
+ if (buffer.length < 4)
140
+ return false;
141
+ const magic = buffer.readUInt32BE(0);
142
+ // TTF: 0x00010000 or 0x74727565 ('true')
143
+ // OTF: 0x4F54544F ('OTTO')
144
+ // WOFF2: 0x774F4632
145
+ // WOFF: 0x774F4646
146
+ const validMagics = [0x00010000, 0x74727565, 0x4F54544F, 0x774F4632, 0x774F4646];
147
+ if (!validMagics.includes(magic))
148
+ return false;
149
+ fs.writeFileSync(destPath, buffer);
150
+ return true;
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ }
156
+ /**
157
+ * Detect the font format from magic bytes.
158
+ */
159
+ function detectFontFormat(buffer) {
160
+ if (buffer.length < 4)
161
+ return 'truetype';
162
+ const magic = buffer.readUInt32BE(0);
163
+ if (magic === 0x774F4632)
164
+ return 'woff2';
165
+ if (magic === 0x774F4646)
166
+ return 'woff';
167
+ if (magic === 0x4F54544F)
168
+ return 'opentype';
169
+ return 'truetype';
170
+ }
171
+ /**
172
+ * Try to download a font directly from its source URL (for custom/proprietary fonts
173
+ * not available on Google Fonts API). Returns the local file path or null.
174
+ */
175
+ async function downloadFontFromSource(src, fontsOutputDir) {
176
+ if (!src.src || !src.src.startsWith('http'))
177
+ return null;
178
+ // Build a clean filename from family + weight + format
179
+ const ext = src.src.includes('.woff2') ? 'woff2'
180
+ : src.src.includes('.woff') ? 'woff'
181
+ : src.src.includes('.otf') ? 'otf'
182
+ : 'ttf';
183
+ const weightLabel = src.weight === '400' || src.weight === 'regular' ? 'Regular' : (src.weight || 'Regular');
184
+ const safeName = (src.family || 'Font').replace(/\s+/g, '') + '-' + weightLabel + '.' + ext;
185
+ const destPath = path.join(fontsOutputDir, safeName);
186
+ if (!fs.existsSync(destPath)) {
187
+ const ok = await downloadFont(src.src, destPath);
188
+ if (!ok)
189
+ return null;
190
+ }
191
+ // Detect actual format from downloaded bytes
192
+ let format = src.format || ext;
193
+ try {
194
+ const buf = fs.readFileSync(destPath);
195
+ format = detectFontFormat(buf);
196
+ }
197
+ catch { }
198
+ return {
199
+ family: src.family,
200
+ src: `fonts/${safeName}`,
201
+ format,
202
+ weight: src.weight,
203
+ };
204
+ }
205
+ /**
206
+ * Fetch font files from Google Fonts API and bundle them into the skill directory.
207
+ * Downloads ALL available weights (non-italic) for each font family.
208
+ */
209
+ async function bundleFonts(fontSources, fontFamilies, skillDir, _usedWeights) {
210
+ const fontsOutputDir = path.join(skillDir, 'fonts');
211
+ let bundledCount = 0;
212
+ const updatedSources = [];
213
+ const bundledFamilies = new Set();
214
+ // Collect all unique font families — skip generics, system fonts, and icon fonts
215
+ const allFamilies = new Set();
216
+ for (const src of fontSources) {
217
+ if (!isSkippableFont(src.family))
218
+ allFamilies.add(src.family);
219
+ }
220
+ for (const f of fontFamilies) {
221
+ if (f && !isSkippableFont(f))
222
+ allFamilies.add(f);
223
+ }
224
+ for (const family of allFamilies) {
225
+ // Try Google Fonts API first
226
+ const fontFiles = await fetchFontMetadata(family);
227
+ if (fontFiles.length > 0) {
228
+ // Create fonts/ directory if needed
229
+ if (!fs.existsSync(fontsOutputDir)) {
230
+ fs.mkdirSync(fontsOutputDir, { recursive: true });
231
+ }
232
+ // Download ALL available weights (non-italic already filtered)
233
+ for (const font of fontFiles) {
234
+ const destPath = path.join(fontsOutputDir, font.fileName);
235
+ if (!fs.existsSync(destPath)) {
236
+ const ok = await downloadFont(font.url, destPath);
237
+ if (!ok)
238
+ continue;
239
+ bundledCount++;
240
+ }
241
+ updatedSources.push({
242
+ family: font.family,
243
+ src: `fonts/${font.fileName}`,
244
+ format: 'truetype',
245
+ weight: font.weight,
246
+ });
247
+ }
248
+ bundledFamilies.add(family);
249
+ }
250
+ else {
251
+ // Not on Google Fonts — try downloading directly from fontSources URLs
252
+ // (handles custom/proprietary fonts like "delight", or self-hosted woff2)
253
+ const sourcesForFamily = fontSources.filter(s => s.family === family);
254
+ if (sourcesForFamily.length > 0) {
255
+ if (!fs.existsSync(fontsOutputDir)) {
256
+ fs.mkdirSync(fontsOutputDir, { recursive: true });
257
+ }
258
+ let downloadedAny = false;
259
+ for (const src of sourcesForFamily) {
260
+ const downloaded = await downloadFontFromSource(src, fontsOutputDir);
261
+ if (downloaded) {
262
+ updatedSources.push(downloaded);
263
+ bundledCount++;
264
+ downloadedAny = true;
265
+ }
266
+ }
267
+ if (downloadedAny)
268
+ bundledFamilies.add(family);
269
+ }
270
+ }
271
+ }
272
+ // Keep remote sources for fonts we couldn't download at all
273
+ for (const src of fontSources) {
274
+ if (!bundledFamilies.has(src.family)) {
275
+ updatedSources.push(src);
276
+ }
277
+ }
278
+ return { updatedSources, bundledCount };
279
+ }
280
+ //# sourceMappingURL=font-resolver.js.map
@@ -0,0 +1,6 @@
1
+ import { DesignProfile } from '../types';
2
+ /**
3
+ * DIR mode: scan a local project directory and extract design tokens.
4
+ */
5
+ export declare function runDirMode(projectDir: string, nameOverride?: string): Promise<DesignProfile>;
6
+ //# sourceMappingURL=dir.d.ts.map