vibe-design-system 2.8.23 → 2.8.24
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
CHANGED
|
@@ -431,6 +431,75 @@ function extractCvaVariants(content) {
|
|
|
431
431
|
return Object.keys(result).length > 0 ? result : null;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
// Common HTML/React props that are noise for the agent — skip them in cursor rules
|
|
435
|
+
const PROPS_SKIP = new Set([
|
|
436
|
+
"className","style","id","key","ref","children","asChild","slot",
|
|
437
|
+
"tabIndex","role","aria-label","aria-describedby","aria-hidden","aria-labelledby",
|
|
438
|
+
"data-testid","htmlFor","draggable","title","lang","dir","onClick","onChange",
|
|
439
|
+
"onSubmit","onBlur","onFocus","onKeyDown","onKeyUp","onMouseEnter","onMouseLeave",
|
|
440
|
+
]);
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Extract TypeScript prop types from a component file.
|
|
444
|
+
* Looks for interface *Props* { ... } or type *Props* = { ... }
|
|
445
|
+
* Returns Array<{ name, type, required }> or null.
|
|
446
|
+
*/
|
|
447
|
+
function extractTSProps(content) {
|
|
448
|
+
// Find the start of a props interface or type alias
|
|
449
|
+
const startRe = /(?:interface|type)\s+\w*[Pp]rops\w*[^{]*\{/;
|
|
450
|
+
const sm = startRe.exec(content);
|
|
451
|
+
if (!sm) return null;
|
|
452
|
+
|
|
453
|
+
// Walk forward counting braces to find the matching closing }
|
|
454
|
+
const blockStart = sm.index + sm[0].length;
|
|
455
|
+
let depth = 1;
|
|
456
|
+
let i = blockStart;
|
|
457
|
+
while (i < content.length && depth > 0) {
|
|
458
|
+
if (content[i] === "{") depth++;
|
|
459
|
+
if (content[i] === "}") depth--;
|
|
460
|
+
i++;
|
|
461
|
+
}
|
|
462
|
+
const propsBlock = content.slice(blockStart, i - 1);
|
|
463
|
+
|
|
464
|
+
const props = [];
|
|
465
|
+
// Match top-level props: optional leading whitespace (1-6 chars), prop name, optional ?, colon, type
|
|
466
|
+
const propRe = /^[ \t]{1,6}([\w]+)(\?)?:\s*(.+?)[ \t]*;?[ \t]*$/gm;
|
|
467
|
+
let pm;
|
|
468
|
+
while ((pm = propRe.exec(propsBlock)) !== null) {
|
|
469
|
+
const propName = pm[1].trim();
|
|
470
|
+
if (PROPS_SKIP.has(propName)) continue;
|
|
471
|
+
// Skip comment lines that accidentally match (e.g. "// foo:")
|
|
472
|
+
if (propsBlock.slice(pm.index).match(/^[ \t]*\/\//)) continue;
|
|
473
|
+
|
|
474
|
+
const optional = pm[2] === "?";
|
|
475
|
+
let type = pm[3]
|
|
476
|
+
.replace(/\s*\/\/.*$/, "") // strip inline // comments (with any space before //)
|
|
477
|
+
.trim() // remove surrounding whitespace
|
|
478
|
+
.replace(/;$/, "") // strip trailing semicolon (now truly at the end)
|
|
479
|
+
.trim();
|
|
480
|
+
|
|
481
|
+
// Skip multi-line types (unbalanced braces — the rest is on the next line)
|
|
482
|
+
const openBraces = (type.match(/\{/g) || []).length;
|
|
483
|
+
const closeBraces = (type.match(/\}/g) || []).length;
|
|
484
|
+
if (openBraces !== closeBraces) continue;
|
|
485
|
+
|
|
486
|
+
// Simplify verbose React type names
|
|
487
|
+
type = type.replace(/React\.ReactNode/g, "ReactNode");
|
|
488
|
+
type = type.replace(/React\.FC[^;,\n]*/g, "FC");
|
|
489
|
+
type = type.replace(/React\.CSSProperties/g, "CSSProperties");
|
|
490
|
+
type = type.replace(/React\.RefObject<[^>]+>/g, "RefObject");
|
|
491
|
+
type = type.replace(/React\.MouseEvent[^;,\n]*/g, "MouseEvent");
|
|
492
|
+
type = type.replace(/React\.ChangeEvent[^;,\n]*/g, "ChangeEvent");
|
|
493
|
+
type = type.replace(/React\.KeyboardEvent[^;,\n]*/g, "KeyboardEvent");
|
|
494
|
+
|
|
495
|
+
// Truncate very long types
|
|
496
|
+
if (type.length > 50) type = type.slice(0, 47) + "...";
|
|
497
|
+
|
|
498
|
+
props.push({ name: propName, type, required: !optional });
|
|
499
|
+
}
|
|
500
|
+
return props.length > 0 ? props : null;
|
|
501
|
+
}
|
|
502
|
+
|
|
434
503
|
function getAllComponentFiles(dir, baseDir = dir) {
|
|
435
504
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
436
505
|
const files = [];
|
|
@@ -2209,9 +2278,10 @@ function scan() {
|
|
|
2209
2278
|
const tokens = extractTailwindTokens(content);
|
|
2210
2279
|
const tier = inferTier(rel, content);
|
|
2211
2280
|
const variants = (tier === "primitive") ? extractCvaVariants(content) : null;
|
|
2281
|
+
const props = (tier === "component" || tier === "feature") ? extractTSProps(content) : null;
|
|
2212
2282
|
const lineCount = content.split("\n").length;
|
|
2213
2283
|
const localImportCount = (content.match(/from\s+['"]\.\.\?\//g) || []).length;
|
|
2214
|
-
results.push({ file: rel, name, group, category, description, tokens, tier, lines: lineCount, localImports: localImportCount, ...(variants ? { variants } : {}) });
|
|
2284
|
+
results.push({ file: rel, name, group, category, description, tokens, tier, lines: lineCount, localImports: localImportCount, ...(variants ? { variants } : {}), ...(props ? { props } : {}) });
|
|
2215
2285
|
}
|
|
2216
2286
|
if (PAGES_DIR && fs.existsSync(PAGES_DIR)) {
|
|
2217
2287
|
const pageFiles = getAllComponentFiles(PAGES_DIR);
|
|
@@ -2569,6 +2569,20 @@ function writeCursorRules(components, foundations) {
|
|
|
2569
2569
|
lines.push(``);
|
|
2570
2570
|
}
|
|
2571
2571
|
|
|
2572
|
+
// Format a compact props summary for cursor rules (required first, up to 6 props)
|
|
2573
|
+
const formatProps = (props) => {
|
|
2574
|
+
if (!Array.isArray(props) || props.length === 0) return null;
|
|
2575
|
+
const sorted = [...props].sort((a, b) => {
|
|
2576
|
+
if (a.required && !b.required) return -1;
|
|
2577
|
+
if (!a.required && b.required) return 1;
|
|
2578
|
+
return a.name.localeCompare(b.name);
|
|
2579
|
+
});
|
|
2580
|
+
const shown = sorted.slice(0, 6);
|
|
2581
|
+
const parts = shown.map(p => p.required ? `${p.name} (${p.type}, required)` : `${p.name} (${p.type})`);
|
|
2582
|
+
const suffix = props.length > 6 ? ` *(+${props.length - 6} more)*` : "";
|
|
2583
|
+
return ` props: ${parts.join(" · ")}${suffix}`;
|
|
2584
|
+
};
|
|
2585
|
+
|
|
2572
2586
|
// Tier 2 — Components
|
|
2573
2587
|
if (byTier.component.length > 0) {
|
|
2574
2588
|
lines.push(`## 🔷 Components — Reusable domain components`);
|
|
@@ -2578,6 +2592,8 @@ function writeCursorRules(components, foundations) {
|
|
|
2578
2592
|
let entry = `- **${c.name}** \`${c.file}\``;
|
|
2579
2593
|
if (c.description) entry += ` — ${c.description}`;
|
|
2580
2594
|
lines.push(entry);
|
|
2595
|
+
const propsLine = formatProps(c.props);
|
|
2596
|
+
if (propsLine) lines.push(propsLine);
|
|
2581
2597
|
}
|
|
2582
2598
|
lines.push(``);
|
|
2583
2599
|
lines.push(`---`);
|
|
@@ -2591,6 +2607,8 @@ function writeCursorRules(components, foundations) {
|
|
|
2591
2607
|
lines.push(``);
|
|
2592
2608
|
for (const c of byTier.feature.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2593
2609
|
lines.push(`- **${c.name}** \`${c.file}\``);
|
|
2610
|
+
const propsLine = formatProps(c.props);
|
|
2611
|
+
if (propsLine) lines.push(propsLine);
|
|
2594
2612
|
}
|
|
2595
2613
|
lines.push(``);
|
|
2596
2614
|
lines.push(`---`);
|