vibe-design-system 2.8.74 → 2.8.76
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/package.json +1 -1
- package/vds-core-template/scan.mjs +157 -20
- package/vds-core-template/story-generator.mjs +74 -24
package/package.json
CHANGED
|
@@ -522,26 +522,74 @@ function extractBrandAssets() {
|
|
|
522
522
|
function extractLucideIconsUsed(srcDir) {
|
|
523
523
|
const allFiles = getAllTsxJsxInDir(srcDir);
|
|
524
524
|
const files = allFiles.filter((rel) => !/^stories[/\\]/.test(rel.replace(/\\/g, "/")));
|
|
525
|
+
// Pass 1: collect all imported icon names + track alias renames
|
|
525
526
|
const names = new Set();
|
|
527
|
+
const aliasMap = new Map(); // localAlias → originalName (for `import { X as Y }`)
|
|
526
528
|
const importRe = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
529
|
+
const fileContents = new Map();
|
|
527
530
|
for (const rel of files) {
|
|
528
531
|
const fullPath = path.join(srcDir, rel);
|
|
529
532
|
try {
|
|
530
533
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
534
|
+
fileContents.set(rel, content);
|
|
531
535
|
let m;
|
|
532
536
|
while ((m = importRe.exec(content)) !== null) {
|
|
533
537
|
const block = m[1];
|
|
534
538
|
block.split(",").forEach((part) => {
|
|
535
539
|
const trimmed = part.trim();
|
|
536
|
-
const asMatch = trimmed.match(/^(\w+)\s+as\s
|
|
537
|
-
|
|
538
|
-
|
|
540
|
+
const asMatch = trimmed.match(/^(\w+)\s+as\s+(\w+)/);
|
|
541
|
+
if (asMatch) {
|
|
542
|
+
names.add(asMatch[1]);
|
|
543
|
+
aliasMap.set(asMatch[2], asMatch[1]); // Y → X
|
|
544
|
+
} else {
|
|
545
|
+
const name = trimmed.split(/\s+/)[0];
|
|
546
|
+
if (name && /^[A-Z][a-zA-Z0-9]*$/.test(name)) names.add(name);
|
|
547
|
+
}
|
|
539
548
|
});
|
|
540
549
|
}
|
|
541
550
|
} catch (_) {}
|
|
542
551
|
importRe.lastIndex = 0;
|
|
543
552
|
}
|
|
544
|
-
return [
|
|
553
|
+
if (names.size === 0) return [];
|
|
554
|
+
|
|
555
|
+
// Pass 2: count JSX usages per icon per component file
|
|
556
|
+
// Build lookup: all names + aliases → canonical icon name
|
|
557
|
+
const nameArr = [...names];
|
|
558
|
+
const allLocalNames = new Set([...nameArr]);
|
|
559
|
+
for (const [alias, orig] of aliasMap) if (names.has(orig)) allLocalNames.add(alias);
|
|
560
|
+
|
|
561
|
+
// usageCounts: iconName → Map<componentName, count>
|
|
562
|
+
const usageCounts = new Map();
|
|
563
|
+
for (const n of nameArr) usageCounts.set(n, new Map());
|
|
564
|
+
|
|
565
|
+
// JSX tag regex: matches <IconName, <IconName/ or <IconName>
|
|
566
|
+
const jsxTagRe = new RegExp(`<(${[...allLocalNames].join("|")})(?=[\\s/>])`, "g");
|
|
567
|
+
|
|
568
|
+
for (const [rel, content] of fileContents) {
|
|
569
|
+
const componentName = path.basename(rel).replace(/\.(tsx|jsx|ts|js)$/, "");
|
|
570
|
+
jsxTagRe.lastIndex = 0;
|
|
571
|
+
let m;
|
|
572
|
+
while ((m = jsxTagRe.exec(content)) !== null) {
|
|
573
|
+
const localName = m[1];
|
|
574
|
+
const canonName = aliasMap.has(localName) ? aliasMap.get(localName) : localName;
|
|
575
|
+
if (usageCounts.has(canonName)) {
|
|
576
|
+
const fm = usageCounts.get(canonName);
|
|
577
|
+
fm.set(componentName, (fm.get(componentName) || 0) + 1);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return nameArr
|
|
583
|
+
.map((name) => {
|
|
584
|
+
const fileMap = usageCounts.get(name) || new Map();
|
|
585
|
+
const total = [...fileMap.values()].reduce((a, b) => a + b, 0);
|
|
586
|
+
const topFiles = [...fileMap.entries()]
|
|
587
|
+
.sort((a, b) => b[1] - a[1])
|
|
588
|
+
.slice(0, 5)
|
|
589
|
+
.map(([f]) => f);
|
|
590
|
+
return { name, total, topFiles };
|
|
591
|
+
})
|
|
592
|
+
.sort((a, b) => b.total - a.total || a.name.localeCompare(b.name));
|
|
545
593
|
}
|
|
546
594
|
|
|
547
595
|
function extractVdsTags(content) {
|
|
@@ -1217,27 +1265,68 @@ function getTailwindExtendColors() {
|
|
|
1217
1265
|
}
|
|
1218
1266
|
|
|
1219
1267
|
function parseTailwindThemeFromText(raw) {
|
|
1220
|
-
const result = {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1268
|
+
const result = {
|
|
1269
|
+
shadows: {}, spacing: {}, breakpoints: {}, zIndex: {},
|
|
1270
|
+
transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {},
|
|
1271
|
+
fontSize: {}, fontWeight: {},
|
|
1272
|
+
};
|
|
1273
|
+
// Helper: extract a shallow key:value block (handles one level of nesting)
|
|
1274
|
+
function extractBlock(key) {
|
|
1275
|
+
// Match "key: {" and capture until the matching closing brace (one level deep)
|
|
1276
|
+
const re = new RegExp(key + "\\s*:\\s*\\{");
|
|
1277
|
+
const start = raw.search(re);
|
|
1278
|
+
if (start === -1) return null;
|
|
1279
|
+
const open = raw.indexOf("{", start);
|
|
1280
|
+
if (open === -1) return null;
|
|
1281
|
+
let depth = 0, i = open;
|
|
1282
|
+
while (i < raw.length) {
|
|
1283
|
+
if (raw[i] === "{") depth++;
|
|
1284
|
+
else if (raw[i] === "}") { depth--; if (depth === 0) return raw.slice(open + 1, i); }
|
|
1285
|
+
i++;
|
|
1227
1286
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
// Helper: parse simple key:"value" pairs from a block string
|
|
1290
|
+
function parseKV(block) {
|
|
1291
|
+
const out = {};
|
|
1292
|
+
if (!block) return out;
|
|
1293
|
+
const re = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
|
|
1294
|
+
let m;
|
|
1295
|
+
while ((m = re.exec(block)) !== null) out[m[1]] = m[2];
|
|
1296
|
+
return out;
|
|
1297
|
+
}
|
|
1298
|
+
// Helper: parse fontSize which can be string OR [string, {...}] (array form)
|
|
1299
|
+
function parseFontSize(block) {
|
|
1300
|
+
const out = {};
|
|
1301
|
+
if (!block) return out;
|
|
1302
|
+
// Array form: "base": ["1rem", { lineHeight: "1.5rem" }]
|
|
1303
|
+
const arrRe = /["']?([\w.-]+)["']?\s*:\s*\[\s*["']([^"']+)["']/g;
|
|
1304
|
+
let m;
|
|
1305
|
+
while ((m = arrRe.exec(block)) !== null) out[m[1]] = m[2];
|
|
1306
|
+
// Simple string form (fills in what array form missed): "xs": "0.75rem"
|
|
1307
|
+
const strRe = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
|
|
1308
|
+
while ((m = strRe.exec(block)) !== null) {
|
|
1309
|
+
if (!out[m[1]]) out[m[1]] = m[2];
|
|
1233
1310
|
}
|
|
1311
|
+
return out;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
try {
|
|
1315
|
+
result.spacing = parseKV(extractBlock("spacing"));
|
|
1316
|
+
result.breakpoints = parseKV(extractBlock("screens"));
|
|
1317
|
+
result.zIndex = parseKV(extractBlock("zIndex"));
|
|
1318
|
+
result.transitionDuration = parseKV(extractBlock("transitionDuration"));
|
|
1319
|
+
result.transitionTimingFunction = parseKV(extractBlock("transitionTimingFunction"));
|
|
1320
|
+
result.animation = parseKV(extractBlock("animation"));
|
|
1321
|
+
result.fontWeight = parseKV(extractBlock("fontWeight"));
|
|
1322
|
+
result.fontSize = parseFontSize(extractBlock("fontSize"));
|
|
1234
1323
|
} catch (_) {}
|
|
1235
1324
|
return result;
|
|
1236
1325
|
}
|
|
1237
1326
|
|
|
1238
1327
|
/** Resolve Tailwind theme for boxShadow, spacing, screens, zIndex, motion. Uses resolveConfig for .js/.mjs, text-parse for .ts. */
|
|
1239
1328
|
function getTailwindTheme() {
|
|
1240
|
-
const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
|
|
1329
|
+
const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {}, fontSize: {}, fontWeight: {} };
|
|
1241
1330
|
const twPath = path.join(PROJECT_ROOT, "tailwind.config.js");
|
|
1242
1331
|
const twPathMjs = path.join(PROJECT_ROOT, "tailwind.config.mjs");
|
|
1243
1332
|
const twPathTs = path.join(PROJECT_ROOT, "tailwind.config.ts");
|
|
@@ -1260,6 +1349,8 @@ function getTailwindTheme() {
|
|
|
1260
1349
|
transitionTimingFunction: toObj(theme.transitionTimingFunction),
|
|
1261
1350
|
animation: toObj(theme.animation),
|
|
1262
1351
|
colors: toObj(theme.colors),
|
|
1352
|
+
fontSize: toObj(theme.fontSize),
|
|
1353
|
+
fontWeight: toObj(theme.fontWeight),
|
|
1263
1354
|
};
|
|
1264
1355
|
} catch (_) {
|
|
1265
1356
|
return empty;
|
|
@@ -1306,9 +1397,14 @@ function extractFoundations() {
|
|
|
1306
1397
|
if (cssChunks.length > 0) {
|
|
1307
1398
|
const css = cssChunks.join("\n");
|
|
1308
1399
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1400
|
+
// Collect ALL :root blocks (a file may have multiple, e.g. a small one early
|
|
1401
|
+
// and the main one inside @layer base { :root { } })
|
|
1402
|
+
const rootBlocks = [];
|
|
1403
|
+
const rootGlobalRe = /:root\s*\{([\s\S]*?)\}/g;
|
|
1404
|
+
let rootM;
|
|
1405
|
+
while ((rootM = rootGlobalRe.exec(css)) !== null) { rootBlocks.push(rootM[1]); }
|
|
1406
|
+
if (rootBlocks.length > 0) {
|
|
1407
|
+
const rootVars = parseCssVarBlock(rootBlocks.join("\n"));
|
|
1312
1408
|
for (const [name, value] of Object.entries(rootVars)) {
|
|
1313
1409
|
if (/\d+\s+\d+%/.test(value) || /\d+%/.test(value)) {
|
|
1314
1410
|
const hsl = `hsl(${value})`;
|
|
@@ -1379,6 +1475,14 @@ function extractFoundations() {
|
|
|
1379
1475
|
});
|
|
1380
1476
|
if (typeScale.length > 0) typography.typeScale = typeScale;
|
|
1381
1477
|
|
|
1478
|
+
// Tailwind v4: font-weight custom props (--font-weight-*)
|
|
1479
|
+
const fontWeightVarRe = /--font-weight-([\w-]+)\s*:\s*([^;\n]+);/g;
|
|
1480
|
+
let fwvm;
|
|
1481
|
+
while ((fwvm = fontWeightVarRe.exec(css)) !== null) {
|
|
1482
|
+
if (!typography.fontWeightScale) typography.fontWeightScale = {};
|
|
1483
|
+
typography.fontWeightScale[fwvm[1]] = fwvm[2].trim();
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1382
1486
|
// Tailwind v4: extract design tokens from @layer theme { :root { ... } } or bare :root
|
|
1383
1487
|
// Variables: --spacing (base), --radius-*, --shadow-*, --ease-*, --duration-*, --animate-*
|
|
1384
1488
|
const v4ThemeVars = {};
|
|
@@ -1778,6 +1882,39 @@ function extractFoundations() {
|
|
|
1778
1882
|
const mergedDurations = { ...v4Durations, ...normalizeThemeObj(twTheme.transitionDuration) };
|
|
1779
1883
|
const mergedAnimations = { ...v4Animations, ...normalizeThemeObj(twTheme.animation) };
|
|
1780
1884
|
|
|
1885
|
+
// ── Fallback: build typeScale from twTheme.fontSize if CSS vars produced nothing ──
|
|
1886
|
+
if (!typography.typeScale || typography.typeScale.length === 0) {
|
|
1887
|
+
const fsObj = normalizeThemeObj(twTheme.fontSize);
|
|
1888
|
+
const fsEntries = Object.entries(fsObj);
|
|
1889
|
+
if (fsEntries.length > 0) {
|
|
1890
|
+
// Order: canonical Tailwind steps first, then any custom steps
|
|
1891
|
+
const ORDER = ["xs","sm","base","lg","xl","2xl","3xl","4xl","5xl","6xl","7xl","8xl","9xl"];
|
|
1892
|
+
const ordered = [
|
|
1893
|
+
...ORDER.filter((k) => fsObj[k]).map((k) => [k, fsObj[k]]),
|
|
1894
|
+
...fsEntries.filter(([k]) => !ORDER.includes(k)),
|
|
1895
|
+
];
|
|
1896
|
+
typography.typeScale = ordered.map(([step, size]) => {
|
|
1897
|
+
// fontSize can be "[size, {lineHeight: ...}]" (array serialized) or plain "1rem"
|
|
1898
|
+
// After toObj + toString it looks like "1rem,1.5rem" or just "1rem" or "[object Object]"
|
|
1899
|
+
let resolvedSize = size;
|
|
1900
|
+
// Handle tuple-ish form from resolveConfig: e.g. "0.75rem,..." or ["0.75rem",{lineHeight:"1rem"}]
|
|
1901
|
+
if (resolvedSize && resolvedSize.includes(",")) resolvedSize = resolvedSize.split(",")[0].trim();
|
|
1902
|
+
if (!resolvedSize || resolvedSize === "[object Object]") return null;
|
|
1903
|
+
const pxM = resolvedSize.match(/^([\d.]+)rem$/);
|
|
1904
|
+
const px = pxM ? Math.round(parseFloat(pxM[1]) * 16) + "px" : null;
|
|
1905
|
+
return { step, size: resolvedSize, px, lineHeight: null };
|
|
1906
|
+
}).filter(Boolean);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// ── Fallback: build fontWeightScale from twTheme.fontWeight if CSS vars produced nothing ──
|
|
1911
|
+
if (!typography.fontWeightScale || Object.keys(typography.fontWeightScale).length === 0) {
|
|
1912
|
+
const fwObj = normalizeThemeObj(twTheme.fontWeight);
|
|
1913
|
+
if (Object.keys(fwObj).length > 0) {
|
|
1914
|
+
typography.fontWeightScale = fwObj;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1781
1918
|
return {
|
|
1782
1919
|
colors: foundationsColors,
|
|
1783
1920
|
typography,
|
|
@@ -2182,9 +2182,11 @@ function buildStoryFileContent(comp) {
|
|
|
2182
2182
|
const resolvedColors = colorRaw.map(token => {
|
|
2183
2183
|
const m = token.match(/^(?:bg|text|border|ring|from|to|fill|stroke)-(.+)$/);
|
|
2184
2184
|
const key = m ? m[1] : null;
|
|
2185
|
-
|
|
2185
|
+
// Strip opacity modifier (e.g. "muted/20" → "muted") as a fallback
|
|
2186
|
+
const baseKey = key ? key.replace(/\/[\d.]+$/, "") : null;
|
|
2187
|
+
const entry = key ? (foundColors[key] || (baseKey !== key ? foundColors[baseKey] : null)) : null;
|
|
2186
2188
|
const hex = entry?.hex && /^#[0-9a-fA-F]{3,8}$/.test(entry.hex) ? entry.hex : null;
|
|
2187
|
-
return { token, hex, label: key };
|
|
2189
|
+
return { token, hex, label: baseKey || key };
|
|
2188
2190
|
});
|
|
2189
2191
|
|
|
2190
2192
|
const hasContent = resolvedColors.length > 0 || spacingRaw.length > 0 || typographyRaw.length > 0 || radiusRaw.length > 0 || animRaw.length > 0;
|
|
@@ -2605,7 +2607,32 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2605
2607
|
|
|
2606
2608
|
{
|
|
2607
2609
|
const typo = foundations?.typography || {};
|
|
2608
|
-
|
|
2610
|
+
// Tailwind v3 default type scale (canonical sizes)
|
|
2611
|
+
const TW_DEFAULT_TYPE_SCALE = [
|
|
2612
|
+
{ step: "xs", size: "0.75rem", px: "12px", lineHeight: "1rem" },
|
|
2613
|
+
{ step: "sm", size: "0.875rem", px: "14px", lineHeight: "1.25rem" },
|
|
2614
|
+
{ step: "base", size: "1rem", px: "16px", lineHeight: "1.5rem" },
|
|
2615
|
+
{ step: "lg", size: "1.125rem", px: "18px", lineHeight: "1.75rem" },
|
|
2616
|
+
{ step: "xl", size: "1.25rem", px: "20px", lineHeight: "1.75rem" },
|
|
2617
|
+
{ step: "2xl", size: "1.5rem", px: "24px", lineHeight: "2rem" },
|
|
2618
|
+
{ step: "3xl", size: "1.875rem", px: "30px", lineHeight: "2.25rem" },
|
|
2619
|
+
{ step: "4xl", size: "2.25rem", px: "36px", lineHeight: "2.5rem" },
|
|
2620
|
+
{ step: "5xl", size: "3rem", px: "48px", lineHeight: "1" },
|
|
2621
|
+
];
|
|
2622
|
+
const TW_DEFAULT_FONT_WEIGHTS = [
|
|
2623
|
+
{ token: "thin", value: "100" },
|
|
2624
|
+
{ token: "extralight", value: "200" },
|
|
2625
|
+
{ token: "light", value: "300" },
|
|
2626
|
+
{ token: "normal", value: "400" },
|
|
2627
|
+
{ token: "medium", value: "500" },
|
|
2628
|
+
{ token: "semibold", value: "600" },
|
|
2629
|
+
{ token: "bold", value: "700" },
|
|
2630
|
+
{ token: "extrabold", value: "800" },
|
|
2631
|
+
{ token: "black", value: "900" },
|
|
2632
|
+
];
|
|
2633
|
+
let typeScale = Array.isArray(typo.typeScale) ? typo.typeScale : [];
|
|
2634
|
+
const typeScaleIsDefault = typeScale.length === 0;
|
|
2635
|
+
if (typeScaleIsDefault) typeScale = TW_DEFAULT_TYPE_SCALE;
|
|
2609
2636
|
const usedTextSizes = (foundations?.tokenUsage?.textSizes || []);
|
|
2610
2637
|
const usedTextMap = Object.fromEntries(usedTextSizes.map(({ token, count }) => [token, count]));
|
|
2611
2638
|
|
|
@@ -2619,11 +2646,23 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2619
2646
|
})
|
|
2620
2647
|
.filter((r) => r.value);
|
|
2621
2648
|
|
|
2622
|
-
// Font weights
|
|
2649
|
+
// Font weights — check fontWeightScale (from twTheme.fontWeight), then legacy specific keys, then Tailwind defaults
|
|
2623
2650
|
const weightKeys = ["fontWeightNormal", "fontWeightMedium", "fontWeightSemibold", "fontWeightBold"];
|
|
2624
|
-
|
|
2651
|
+
let weightRows = weightKeys
|
|
2625
2652
|
.filter((k) => typo[k])
|
|
2626
2653
|
.map((k) => ({ token: k, value: String(typo[k]) }));
|
|
2654
|
+
if (weightRows.length === 0 && typo.fontWeightScale && typeof typo.fontWeightScale === "object") {
|
|
2655
|
+
const WGT_ORDER = ["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"];
|
|
2656
|
+
const wEntries = Object.entries(typo.fontWeightScale);
|
|
2657
|
+
wEntries.sort(([a], [b]) => {
|
|
2658
|
+
const ia = WGT_ORDER.indexOf(a), ib = WGT_ORDER.indexOf(b);
|
|
2659
|
+
if (ia >= 0 && ib >= 0) return ia - ib;
|
|
2660
|
+
return parseInt(a) - parseInt(b) || a.localeCompare(b);
|
|
2661
|
+
});
|
|
2662
|
+
weightRows = wEntries.map(([token, value]) => ({ token, value: String(value) }));
|
|
2663
|
+
}
|
|
2664
|
+
const weightRowsIsDefault = weightRows.length === 0;
|
|
2665
|
+
if (weightRowsIsDefault) weightRows = TW_DEFAULT_FONT_WEIGHTS;
|
|
2627
2666
|
|
|
2628
2667
|
// Reverse type scale for display (largest first)
|
|
2629
2668
|
const scaleDesc = [...typeScale].reverse();
|
|
@@ -2639,9 +2678,11 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2639
2678
|
"type Story = StoryObj;",
|
|
2640
2679
|
"",
|
|
2641
2680
|
`const typeScale: { step: string; size: string; px: string | null; lineHeight: string | null }[] = ${JSON.stringify(scaleDesc)};`,
|
|
2681
|
+
`const typeScaleIsDefault: boolean = ${typeScaleIsDefault};`,
|
|
2642
2682
|
`const usedTextSizes: { token: string; count: number }[] = ${JSON.stringify(usedTextSizes)};`,
|
|
2643
2683
|
`const familyRows: { token: string; value: string }[] = ${JSON.stringify(familyRows)};`,
|
|
2644
2684
|
`const weightRows: { token: string; value: string }[] = ${JSON.stringify(weightRows)};`,
|
|
2685
|
+
`const weightRowsIsDefault: boolean = ${weightRowsIsDefault};`,
|
|
2645
2686
|
`const sansFamily = ${JSON.stringify(sansFamily)};`,
|
|
2646
2687
|
"",
|
|
2647
2688
|
"export const Default: Story = {",
|
|
@@ -2664,10 +2705,8 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2664
2705
|
" </div>",
|
|
2665
2706
|
" </div>",
|
|
2666
2707
|
" )}",
|
|
2667
|
-
" {
|
|
2668
|
-
"
|
|
2669
|
-
" ) : (",
|
|
2670
|
-
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 0, marginBottom: 48, border: \"1px solid #e5e7eb\", borderRadius: 10, overflow: \"hidden\" }}>",
|
|
2708
|
+
" {typeScaleIsDefault && <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 16, display: \"inline-block\" }}>ℹ️ Tailwind defaults — no custom font sizes found in this project</p>}",
|
|
2709
|
+
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 0, marginBottom: 48, border: \"1px solid #e5e7eb\", borderRadius: 10, overflow: \"hidden\" }}>",
|
|
2671
2710
|
" {typeScale.map(({ step, size, px, lineHeight }, i) => {",
|
|
2672
2711
|
" const usage = usedTextSizes.find(u => u.token === `text-${step}`);",
|
|
2673
2712
|
" const usageCount = usage?.count;",
|
|
@@ -2701,8 +2740,7 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2701
2740
|
" </div>",
|
|
2702
2741
|
" );",
|
|
2703
2742
|
" })}",
|
|
2704
|
-
"
|
|
2705
|
-
" )}",
|
|
2743
|
+
" </div>",
|
|
2706
2744
|
"",
|
|
2707
2745
|
" {/* ── FONT FAMILIES ── */}",
|
|
2708
2746
|
" {familyRows.length > 0 && (",
|
|
@@ -2725,6 +2763,7 @@ function writeFoundationsStories(foundations, components) {
|
|
|
2725
2763
|
" )}",
|
|
2726
2764
|
"",
|
|
2727
2765
|
" {/* ── FONT WEIGHTS ── */}",
|
|
2766
|
+
" {weightRowsIsDefault && <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 12, display: \"inline-block\" }}>ℹ️ Tailwind defaults — no custom font weights found in this project</p>}",
|
|
2728
2767
|
" {weightRows.length > 0 && (",
|
|
2729
2768
|
" <>",
|
|
2730
2769
|
" <h3 style={{ fontSize: 15, fontWeight: 600, margin: \"0 0 14px\", color: \"#374151\" }}>Font Weights</h3>",
|
|
@@ -3237,6 +3276,13 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3237
3276
|
{
|
|
3238
3277
|
const spacing = foundations?.spacing || {};
|
|
3239
3278
|
const breakpoints = foundations?.breakpoints || {};
|
|
3279
|
+
// Tailwind v3 default spacing scale
|
|
3280
|
+
const TW_DEFAULT_SPACING = { "0": "0px", "px": "1px", "0.5": "0.125rem", "1": "0.25rem", "1.5": "0.375rem", "2": "0.5rem", "2.5": "0.625rem", "3": "0.75rem", "3.5": "0.875rem", "4": "1rem", "5": "1.25rem", "6": "1.5rem", "7": "1.75rem", "8": "2rem", "9": "2.25rem", "10": "2.5rem", "11": "2.75rem", "12": "3rem", "14": "3.5rem", "16": "4rem", "20": "5rem", "24": "6rem", "28": "7rem", "32": "8rem", "36": "9rem", "40": "10rem", "44": "11rem", "48": "12rem", "52": "13rem", "56": "14rem", "60": "15rem", "64": "16rem", "72": "18rem", "80": "20rem", "96": "24rem" };
|
|
3281
|
+
const TW_DEFAULT_BREAKPOINTS = { "sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px", "2xl": "1536px" };
|
|
3282
|
+
const spacingIsDefault = Object.keys(spacing).length === 0;
|
|
3283
|
+
const spacingSource = spacingIsDefault ? TW_DEFAULT_SPACING : spacing;
|
|
3284
|
+
const breakpointsIsDefault = Object.keys(breakpoints).length === 0;
|
|
3285
|
+
const breakpointsSource = breakpointsIsDefault ? TW_DEFAULT_BREAKPOINTS : breakpoints;
|
|
3240
3286
|
|
|
3241
3287
|
function remToPxNum(val) {
|
|
3242
3288
|
const m = String(val).match(/^([\d.]+)rem$/);
|
|
@@ -3246,7 +3292,7 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3246
3292
|
return -1;
|
|
3247
3293
|
}
|
|
3248
3294
|
|
|
3249
|
-
const spacingRows = Object.entries(
|
|
3295
|
+
const spacingRows = Object.entries(spacingSource)
|
|
3250
3296
|
.map(([token, value]) => {
|
|
3251
3297
|
const px = remToPxNum(value);
|
|
3252
3298
|
const label = px >= 0 ? `${value} · ${px}px` : value;
|
|
@@ -3261,7 +3307,7 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3261
3307
|
return a.token.localeCompare(b.token);
|
|
3262
3308
|
});
|
|
3263
3309
|
|
|
3264
|
-
const breakpointRows = Object.entries(
|
|
3310
|
+
const breakpointRows = Object.entries(breakpointsSource)
|
|
3265
3311
|
.map(([token, value]) => ({ token, value }))
|
|
3266
3312
|
.sort((a, b) => parseInt(a.value) - parseInt(b.value));
|
|
3267
3313
|
|
|
@@ -3275,7 +3321,9 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3275
3321
|
"type Story = StoryObj;",
|
|
3276
3322
|
"",
|
|
3277
3323
|
`const spacingTokens: { token: string; label: string; barWidth: number }[] = ${JSON.stringify(spacingRows)};`,
|
|
3324
|
+
`const spacingIsDefault: boolean = ${spacingIsDefault};`,
|
|
3278
3325
|
`const breakpointTokens: { token: string; value: string }[] = ${JSON.stringify(breakpointRows)};`,
|
|
3326
|
+
`const breakpointsIsDefault: boolean = ${breakpointsIsDefault};`,
|
|
3279
3327
|
`const usedSpacing: { token: string; count: number }[] = ${JSON.stringify(usedSpacing)};`,
|
|
3280
3328
|
"",
|
|
3281
3329
|
"export const Default: Story = {",
|
|
@@ -3295,22 +3343,20 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3295
3343
|
" </div>",
|
|
3296
3344
|
" </>",
|
|
3297
3345
|
" )}",
|
|
3298
|
-
" {
|
|
3299
|
-
"
|
|
3300
|
-
"
|
|
3301
|
-
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 3 }}>",
|
|
3302
|
-
" {spacingTokens.map(({ token, label, barWidth }) => (",
|
|
3346
|
+
" {spacingIsDefault && <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 16, display: \"inline-block\" }}>ℹ️ Tailwind defaults — no custom spacing detected in this project</p>}",
|
|
3347
|
+
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 3 }}>",
|
|
3348
|
+
" {spacingTokens.map(({ token, label, barWidth }) => (",
|
|
3303
3349
|
" <div key={token} style={{ display: \"flex\", alignItems: \"center\", gap: 10, minHeight: 24 }}>",
|
|
3304
3350
|
" <span style={{ width: 40, fontSize: 11, color: \"#9ca3af\", textAlign: \"right\", flexShrink: 0, fontVariantNumeric: \"tabular-nums\" }}>{token}</span>",
|
|
3305
3351
|
" <div style={{ width: Math.max(barWidth, 2), height: 16, background: barWidth === 0 ? \"transparent\" : \"var(--color-primary, #6366f1)\", opacity: barWidth === 0 ? 1 : 0.85, borderRadius: 3, flexShrink: 0, border: barWidth === 0 ? \"1px dashed #555\" : \"none\", boxSizing: \"border-box\" as any }} />",
|
|
3306
3352
|
" <code style={{ fontSize: 11, color: \"#6b7280\" }}>{label}</code>",
|
|
3307
3353
|
" </div>",
|
|
3308
3354
|
" ))}",
|
|
3309
|
-
"
|
|
3310
|
-
" )}",
|
|
3355
|
+
" </div>",
|
|
3311
3356
|
" {breakpointTokens.length > 0 && (",
|
|
3312
3357
|
" <>",
|
|
3313
3358
|
" <h3 style={{ fontSize: 16, fontWeight: 600, margin: \"40px 0 12px\" }}>Breakpoints</h3>",
|
|
3359
|
+
" {breakpointsIsDefault && <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 10, display: \"inline-block\" }}>ℹ️ Tailwind defaults — no custom breakpoints found in this project</p>}",
|
|
3314
3360
|
" <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fill, minmax(130px, 1fr))\", gap: 8 }}>",
|
|
3315
3361
|
" {breakpointTokens.map(({ token, value }) => (",
|
|
3316
3362
|
" <div key={token} style={{ padding: \"10px 12px\", border: \"1px solid #e5e7eb\", borderRadius: 8, background: \"#fafafa\" }}>",
|
|
@@ -3346,7 +3392,11 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3346
3392
|
.map(([token, value]) => ({ token, value }));
|
|
3347
3393
|
|
|
3348
3394
|
const zSemanticLabels = { "0": "Base layer", "10": "Low elevation", "20": "Dropdown / popover", "30": "Sticky header", "40": "Fixed overlay", "50": "Modal / dialog", "auto": "Auto" };
|
|
3349
|
-
|
|
3395
|
+
// Tailwind v3 default z-index scale
|
|
3396
|
+
const TW_DEFAULT_Z_INDEX = { "0": "0", "10": "10", "20": "20", "30": "30", "40": "40", "50": "50", "auto": "auto" };
|
|
3397
|
+
const zIndexIsDefault = Object.keys(zIndex).length === 0;
|
|
3398
|
+
const zIndexSource = zIndexIsDefault ? TW_DEFAULT_Z_INDEX : zIndex;
|
|
3399
|
+
const zIndexRows = Object.entries(zIndexSource)
|
|
3350
3400
|
.sort(([a], [b]) => {
|
|
3351
3401
|
if (a === "auto") return 1;
|
|
3352
3402
|
if (b === "auto") return -1;
|
|
@@ -3384,6 +3434,7 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3384
3434
|
"",
|
|
3385
3435
|
`const shadowTokens: { token: string; value: string }[] = ${JSON.stringify(shadowDisplayRows)};`,
|
|
3386
3436
|
`const zIndexTokens: { token: string; value: string; label: string }[] = ${JSON.stringify(zIndexRows)};`,
|
|
3437
|
+
`const zIndexIsDefault: boolean = ${zIndexIsDefault};`,
|
|
3387
3438
|
`const usedShadows: { token: string; count: number }[] = ${JSON.stringify(usedShadows)};`,
|
|
3388
3439
|
`const usedZIndex: { token: string; count: number }[] = ${JSON.stringify(usedZIndex)};`,
|
|
3389
3440
|
`const zSemantics: Record<string, string> = ${JSON.stringify(zIndexSemantics)};`,
|
|
@@ -3434,9 +3485,8 @@ function writeFoundationsStories(foundations, components) {
|
|
|
3434
3485
|
" ))}",
|
|
3435
3486
|
" </div>",
|
|
3436
3487
|
" )}",
|
|
3437
|
-
" {
|
|
3438
|
-
"
|
|
3439
|
-
" ) : zIndexTokens.length > 0 ? (",
|
|
3488
|
+
" {zIndexIsDefault && <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 14, display: \"inline-block\" }}>ℹ️ Tailwind defaults — no custom z-index found in this project</p>}",
|
|
3489
|
+
" {zIndexTokens.length > 0 ? (",
|
|
3440
3490
|
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, maxWidth: 380 }}>",
|
|
3441
3491
|
" {zIndexTokens.map(({ token, value, label }) => (",
|
|
3442
3492
|
" <div key={token} style={{ display: \"flex\", alignItems: \"center\", gap: 12, padding: \"8px 14px\", background: \"#fff\", borderRadius: 8, border: \"1px solid #e5e7eb\" }}>",
|