vibe-design-system 2.8.27 → 2.8.28
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
|
@@ -525,6 +525,61 @@ function extractTSProps(content) {
|
|
|
525
525
|
return props.length > 0 ? props : null;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Extract named UI patterns from a feature-tier file.
|
|
530
|
+
*
|
|
531
|
+
* Signal 1 — JSX block comments: {/* SECTION TITLE *\/}
|
|
532
|
+
* e.g. {/* CLUSTER 1: REVENUE & BILLING *\/} → label "CLUSTER 1: REVENUE & BILLING"
|
|
533
|
+
*
|
|
534
|
+
* Signal 2 — Nearest <h3>/<h2> text (fallback when no comments found)
|
|
535
|
+
* e.g. <h3>Unbilled WIP</h3> → label "Unbilled WIP"
|
|
536
|
+
*
|
|
537
|
+
* Returns Array<{ label: string, line: number }> or null.
|
|
538
|
+
* Safe on any JSX/TSX file — never throws.
|
|
539
|
+
*/
|
|
540
|
+
function extractInlinePatterns(content) {
|
|
541
|
+
const patterns = [];
|
|
542
|
+
const seen = new Set();
|
|
543
|
+
|
|
544
|
+
// Signal 1: JSX block comments — {/* ... */}
|
|
545
|
+
const commentRe = /\{\/\*\s*([\s\S]{3,120}?)\s*\*\/\}/g;
|
|
546
|
+
let cm;
|
|
547
|
+
while ((cm = commentRe.exec(content)) !== null) {
|
|
548
|
+
const raw = cm[1].replace(/\s+/g, " ").trim();
|
|
549
|
+
// Skip too-short, pure annotation, or code-style comments
|
|
550
|
+
if (!raw || raw.length < 5) continue;
|
|
551
|
+
if (/^(TODO|FIXME|HACK|NOTE|@|eslint|prettier|ts-ignore|type-)/i.test(raw)) continue;
|
|
552
|
+
// Skip comments that look like code (contain JSX/HTML tags)
|
|
553
|
+
if (/<[A-Za-z]/.test(raw)) continue;
|
|
554
|
+
const lineNum = content.slice(0, cm.index).split("\n").length;
|
|
555
|
+
const label = raw.slice(0, 80);
|
|
556
|
+
const key = label.toLowerCase().replace(/\s+/g, " ");
|
|
557
|
+
if (!seen.has(key)) {
|
|
558
|
+
seen.add(key);
|
|
559
|
+
patterns.push({ label, line: lineNum });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Signal 2: <h3>/<h2> text content (only if no JSX comments found)
|
|
564
|
+
if (patterns.length === 0) {
|
|
565
|
+
const headingRe = /<h[23][^>]*>\s*([^<{]{4,60}?)\s*<\/h[23]>/g;
|
|
566
|
+
let hm;
|
|
567
|
+
while ((hm = headingRe.exec(content)) !== null) {
|
|
568
|
+
const raw = hm[1].replace(/\s+/g, " ").trim();
|
|
569
|
+
if (!raw || raw.length < 4) continue;
|
|
570
|
+
const lineNum = content.slice(0, hm.index).split("\n").length;
|
|
571
|
+
const label = raw.slice(0, 80);
|
|
572
|
+
const key = label.toLowerCase();
|
|
573
|
+
if (!seen.has(key)) {
|
|
574
|
+
seen.add(key);
|
|
575
|
+
patterns.push({ label, line: lineNum });
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return patterns.length > 0 ? patterns : null;
|
|
581
|
+
}
|
|
582
|
+
|
|
528
583
|
function getAllComponentFiles(dir, baseDir = dir) {
|
|
529
584
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
530
585
|
const files = [];
|
|
@@ -2304,9 +2359,10 @@ function scan() {
|
|
|
2304
2359
|
const tier = inferTier(rel, content);
|
|
2305
2360
|
const variants = (tier === "primitive") ? extractCvaVariants(content) : null;
|
|
2306
2361
|
const props = (tier === "component" || tier === "feature") ? extractTSProps(content) : null;
|
|
2362
|
+
const patterns = (tier === "feature") ? extractInlinePatterns(content) : null;
|
|
2307
2363
|
const lineCount = content.split("\n").length;
|
|
2308
2364
|
const localImportCount = (content.match(/from\s+['"]\.\.\?\//g) || []).length;
|
|
2309
|
-
results.push({ file: rel, name, group, category, description, tokens, tier, lines: lineCount, localImports: localImportCount, ...(variants ? { variants } : {}), ...(props ? { props } : {}) });
|
|
2365
|
+
results.push({ file: rel, name, group, category, description, tokens, tier, lines: lineCount, localImports: localImportCount, ...(variants ? { variants } : {}), ...(props ? { props } : {}), ...(patterns ? { patterns } : {}) });
|
|
2310
2366
|
}
|
|
2311
2367
|
if (PAGES_DIR && fs.existsSync(PAGES_DIR)) {
|
|
2312
2368
|
const pageFiles = getAllComponentFiles(PAGES_DIR);
|
|
@@ -2669,13 +2669,17 @@ function writeCursorRules(components, foundations) {
|
|
|
2669
2669
|
|
|
2670
2670
|
// Tier 3 — Features
|
|
2671
2671
|
if (byTier.feature.length > 0) {
|
|
2672
|
-
lines.push(`## ⚙️ Feature
|
|
2673
|
-
lines.push(`>
|
|
2672
|
+
lines.push(`## ⚙️ Feature Views — Reuse existing UI patterns before building new ones`);
|
|
2673
|
+
lines.push(`> Full views with internal state. When a user story needs a new widget, check the pattern list below first.`);
|
|
2674
2674
|
lines.push(``);
|
|
2675
2675
|
for (const c of byTier.feature.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2676
2676
|
lines.push(`- **${c.name}** \`${c.file}\``);
|
|
2677
2677
|
const propsLine = formatProps(c.props);
|
|
2678
2678
|
if (propsLine) lines.push(propsLine);
|
|
2679
|
+
if (Array.isArray(c.patterns) && c.patterns.length > 0) {
|
|
2680
|
+
const patternParts = c.patterns.slice(0, 8).map(p => `"${p.label}" (line ${p.line})`);
|
|
2681
|
+
lines.push(` patterns: ${patternParts.join(" · ")}`);
|
|
2682
|
+
}
|
|
2679
2683
|
}
|
|
2680
2684
|
lines.push(``);
|
|
2681
2685
|
lines.push(`---`);
|