vibe-design-system 2.8.65 → 2.8.66
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
|
@@ -3950,6 +3950,58 @@ function buildInventoryPreviewHtml(compName, category, colorSwatches, tokens) {
|
|
|
3950
3950
|
* Shows all project components classified by Atlassian-style functional category.
|
|
3951
3951
|
* Every value is derived from vds-output.json — no placeholder content.
|
|
3952
3952
|
*/
|
|
3953
|
+
/**
|
|
3954
|
+
* Single-pass JSX usage scan: returns a Set of component names (PascalCase-normalised)
|
|
3955
|
+
* that actually appear as <Tag in the project's source files (excluding ui/ definitions
|
|
3956
|
+
* and story files). This is used to distinguish "used in project" from "installed only".
|
|
3957
|
+
*/
|
|
3958
|
+
function buildComponentUsageSet(componentNames, projectRoot) {
|
|
3959
|
+
// Pre-compute pascal names for each component
|
|
3960
|
+
const patterns = componentNames.map(name => {
|
|
3961
|
+
const pascal = name
|
|
3962
|
+
.replace(/[-\s]+(.)/g, (_, c) => c.toUpperCase())
|
|
3963
|
+
.replace(/^(.)/, c => c.toUpperCase());
|
|
3964
|
+
return { name, pascal };
|
|
3965
|
+
});
|
|
3966
|
+
|
|
3967
|
+
const usedNames = new Set();
|
|
3968
|
+
|
|
3969
|
+
// Resolve the shadcn ui directory path to exclude component definition files
|
|
3970
|
+
// (e.g. dialog.tsx contains <DialogClose> which would falsely register Dialog as "used")
|
|
3971
|
+
const uiDefDir = path.resolve(path.join(projectRoot, COMPONENTS_REL_DIR));
|
|
3972
|
+
|
|
3973
|
+
function walkDir(dir) {
|
|
3974
|
+
let entries;
|
|
3975
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
3976
|
+
for (const entry of entries) {
|
|
3977
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
3978
|
+
const full = path.join(dir, entry.name);
|
|
3979
|
+
if (entry.isDirectory()) {
|
|
3980
|
+
// Skip: story dirs, storybook config, AND the shadcn/ui component definitions dir
|
|
3981
|
+
if (["stories", ".storybook"].includes(entry.name)) continue;
|
|
3982
|
+
if (path.resolve(full) === uiDefDir) continue; // shadcn ui/ — skip definitions
|
|
3983
|
+
walkDir(full);
|
|
3984
|
+
} else if (/\.(tsx|jsx)$/.test(entry.name) && !/\.stories\./.test(entry.name)) {
|
|
3985
|
+
let src;
|
|
3986
|
+
try { src = fs.readFileSync(full, "utf-8"); } catch { continue; }
|
|
3987
|
+
for (const { name, pascal } of patterns) {
|
|
3988
|
+
if (!usedNames.has(name) && src.includes(`<${pascal}`)) {
|
|
3989
|
+
usedNames.add(name);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
// Walk common source roots (excluding the component definition directories themselves)
|
|
3997
|
+
const srcRoots = ["src", "client/src", "app", "pages"]
|
|
3998
|
+
.map(d => path.join(projectRoot, d))
|
|
3999
|
+
.filter(d => fs.existsSync(d));
|
|
4000
|
+
for (const root of srcRoots) walkDir(root);
|
|
4001
|
+
|
|
4002
|
+
return usedNames;
|
|
4003
|
+
}
|
|
4004
|
+
|
|
3953
4005
|
function writeComponentInventoryStory(components, foundations) {
|
|
3954
4006
|
if (!Array.isArray(components) || components.length === 0) return;
|
|
3955
4007
|
const foundationsDir = path.join(STORIES_DIR, "foundations");
|
|
@@ -3963,6 +4015,10 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
3963
4015
|
const categorized = {}; // category → [{ name, group, tokenCount, propCount, colorSwatches }]
|
|
3964
4016
|
const foundColors = (foundations && foundations.colors) ? foundations.colors : {};
|
|
3965
4017
|
|
|
4018
|
+
// Determine which components are actually used as JSX in the project source
|
|
4019
|
+
const componentNames = components.map(c => c.name);
|
|
4020
|
+
const usedSet = buildComponentUsageSet(componentNames, PROJECT_ROOT);
|
|
4021
|
+
|
|
3966
4022
|
for (const comp of components) {
|
|
3967
4023
|
const g = comp.group || "Components";
|
|
3968
4024
|
const gLower = g.toLowerCase();
|
|
@@ -3993,6 +4049,9 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
3993
4049
|
// Visual preview HTML (computed at generation time using project's resolved colors)
|
|
3994
4050
|
const previewHtml = buildInventoryPreviewHtml(comp.name, category, colorSwatches, tokens.filter(t => !/:/.test(t)));
|
|
3995
4051
|
|
|
4052
|
+
// Whether this component is actually used as a JSX tag anywhere in the project source
|
|
4053
|
+
const active = usedSet.has(comp.name);
|
|
4054
|
+
|
|
3996
4055
|
if (!categorized[category]) categorized[category] = [];
|
|
3997
4056
|
categorized[category].push({
|
|
3998
4057
|
name: comp.name,
|
|
@@ -4001,6 +4060,7 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
4001
4060
|
propCount: props.length,
|
|
4002
4061
|
colorSwatches,
|
|
4003
4062
|
previewHtml,
|
|
4063
|
+
active,
|
|
4004
4064
|
});
|
|
4005
4065
|
}
|
|
4006
4066
|
|
|
@@ -4015,10 +4075,15 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
4015
4075
|
|
|
4016
4076
|
const inventoryData = sortedCategories.map(cat => ({
|
|
4017
4077
|
category: cat,
|
|
4018
|
-
|
|
4078
|
+
// Sort: active first, then alphabetical within each group
|
|
4079
|
+
components: categorized[cat].sort((a, b) => {
|
|
4080
|
+
if (a.active !== b.active) return a.active ? -1 : 1;
|
|
4081
|
+
return a.name.localeCompare(b.name);
|
|
4082
|
+
}),
|
|
4019
4083
|
}));
|
|
4020
4084
|
|
|
4021
4085
|
const totalComponents = components.length;
|
|
4086
|
+
const activeComponents = [...usedSet].length;
|
|
4022
4087
|
const uniqueTokens = [...new Set(
|
|
4023
4088
|
components.flatMap(c => Array.isArray(c.tokens) ? c.tokens.filter(t => !/:/.test(t)) : [])
|
|
4024
4089
|
)].length;
|
|
@@ -4030,77 +4095,85 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
4030
4095
|
`export default meta;`,
|
|
4031
4096
|
`type Story = StoryObj;`,
|
|
4032
4097
|
``,
|
|
4033
|
-
`const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number; colorSwatches: { token: string; hex: string }[]; previewHtml: string }[] }[] = ${JSON.stringify(inventoryData)};`,
|
|
4098
|
+
`const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number; colorSwatches: { token: string; hex: string }[]; previewHtml: string; active: boolean }[] }[] = ${JSON.stringify(inventoryData)};`,
|
|
4034
4099
|
`const totalComponents = ${totalComponents};`,
|
|
4100
|
+
`const activeComponents = ${activeComponents};`,
|
|
4035
4101
|
`const uniqueTokens = ${uniqueTokens};`,
|
|
4036
4102
|
``,
|
|
4037
4103
|
`export const Default: Story = {`,
|
|
4038
|
-
` render: () =>
|
|
4104
|
+
` render: () => {`,
|
|
4105
|
+
` const [showUnused, setShowUnused] = (window as any).__vdsShowUnused !== undefined`,
|
|
4106
|
+
` ? [(window as any).__vdsShowUnused, (v: boolean) => { (window as any).__vdsShowUnused = v; }]`,
|
|
4107
|
+
` : [false, () => {}];`,
|
|
4108
|
+
` const [expanded, setExpanded] = React.useState(false);`,
|
|
4109
|
+
` const Card = ({ comp }: { comp: any }) => (`,
|
|
4110
|
+
` <div style={{ border: \`1px solid \${comp.active ? "#e5e7eb" : "#f0f0f0"}\`, borderRadius: 10, overflow: "hidden", background: comp.active ? "#fff" : "#fafafa", display: "flex", flexDirection: "column" as any, boxShadow: comp.active ? "0 1px 3px rgba(0,0,0,0.05)" : "none", opacity: comp.active ? 1 : 0.55 }}>`,
|
|
4111
|
+
` <div style={{ background: comp.active ? "#f8f9fa" : "#f5f5f5", borderBottom: "1px solid #f0f0f0", minHeight: 64, display: "flex", alignItems: "center", justifyContent: "center" }}`,
|
|
4112
|
+
` dangerouslySetInnerHTML={{ __html: comp.previewHtml }}`,
|
|
4113
|
+
` />`,
|
|
4114
|
+
` <div style={{ padding: "10px 12px" }}>`,
|
|
4115
|
+
` <div style={{ display: "flex", alignItems: "center", gap: 5, marginBottom: 5 }}>`,
|
|
4116
|
+
` <span style={{ fontWeight: 700, fontSize: 13, color: comp.active ? "#111" : "#9ca3af" }}>{comp.name}</span>`,
|
|
4117
|
+
` {comp.active && <span style={{ fontSize: 9, background: "#dcfce7", color: "#15803d", padding: "1px 5px", borderRadius: 4, fontWeight: 700, letterSpacing: "0.05em" }}>USED</span>}`,
|
|
4118
|
+
` </div>`,
|
|
4119
|
+
` <div style={{ display: "flex", gap: 4, flexWrap: "wrap" as any }}>`,
|
|
4120
|
+
` {comp.tokenCount > 0 && (`,
|
|
4121
|
+
` <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>{comp.tokenCount} tokens</span>`,
|
|
4122
|
+
` )}`,
|
|
4123
|
+
` {comp.colorSwatches.length > 0 && (`,
|
|
4124
|
+
` <span style={{ fontSize: 10, background: "#fef9c3", color: "#713f12", padding: "2px 7px", borderRadius: 10, border: "1px solid #fde68a" }}>{comp.colorSwatches.length} colors</span>`,
|
|
4125
|
+
` )}`,
|
|
4126
|
+
` {!comp.active && (`,
|
|
4127
|
+
` <span style={{ fontSize: 10, background: "#f3f4f6", color: "#9ca3af", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>installed</span>`,
|
|
4128
|
+
` )}`,
|
|
4129
|
+
` </div>`,
|
|
4130
|
+
` </div>`,
|
|
4131
|
+
` </div>`,
|
|
4132
|
+
` );`,
|
|
4133
|
+
` return (`,
|
|
4039
4134
|
` <div style={{ padding: 32, background: "#f8f9fa", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`,
|
|
4040
4135
|
` {/* Header */}`,
|
|
4041
4136
|
` <div style={{ marginBottom: 40 }}>`,
|
|
4042
4137
|
` <h2 style={{ fontSize: 28, fontWeight: 700, margin: "0 0 8px" }}>Component Inventory</h2>`,
|
|
4043
|
-
` <p style={{ fontSize: 14, color: "#6b7280", margin: "0 0 20px" }}>
|
|
4044
|
-
` <div style={{ display: "flex", gap: 12, flexWrap: "wrap" as any }}>`,
|
|
4138
|
+
` <p style={{ fontSize: 14, color: "#6b7280", margin: "0 0 20px" }}>Components actually used in this project. <strong style={{ color: "#111" }}>USED</strong> = appears as a JSX tag in source files.</p>`,
|
|
4139
|
+
` <div style={{ display: "flex", gap: 12, flexWrap: "wrap" as any, marginBottom: 20 }}>`,
|
|
4045
4140
|
` {[`,
|
|
4046
|
-
` { value:
|
|
4047
|
-
` { value:
|
|
4048
|
-
` { value: uniqueTokens.toString(), label: "Unique Tokens" },`,
|
|
4141
|
+
` { value: activeComponents.toString(), label: "Used in Project", bg: "#f0fdf4", fg: "#15803d", border: "#bbf7d0" },`,
|
|
4142
|
+
` { value: (totalComponents - activeComponents).toString(), label: "Installed Only", bg: "#f9fafb", fg: "#6b7280", border: "#e5e7eb" },`,
|
|
4143
|
+
` { value: uniqueTokens.toString(), label: "Unique Tokens", bg: "#eff6ff", fg: "#1d4ed8", border: "#dbeafe" },`,
|
|
4049
4144
|
` ].map(stat => (`,
|
|
4050
|
-
` <div key={stat.label} style={{ padding: "12px 20px", background:
|
|
4051
|
-
` <div style={{ fontSize: 26, fontWeight: 800, color:
|
|
4052
|
-
` <div style={{ fontSize: 12, color:
|
|
4145
|
+
` <div key={stat.label} style={{ padding: "12px 20px", background: stat.bg, borderRadius: 10, textAlign: "center" as any, minWidth: 110, border: \`1px solid \${stat.border}\` }}>`,
|
|
4146
|
+
` <div style={{ fontSize: 26, fontWeight: 800, color: stat.fg, lineHeight: 1 }}>{stat.value}</div>`,
|
|
4147
|
+
` <div style={{ fontSize: 12, color: stat.fg, marginTop: 4, opacity: 0.8 }}>{stat.label}</div>`,
|
|
4053
4148
|
` </div>`,
|
|
4054
4149
|
` ))}`,
|
|
4055
4150
|
` </div>`,
|
|
4151
|
+
` <button onClick={() => setExpanded(!expanded)} style={{ fontSize: 12, color: "#6b7280", background: "transparent", border: "1px solid #e5e7eb", borderRadius: 6, padding: "5px 12px", cursor: "pointer" }}>`,
|
|
4152
|
+
` {expanded ? "Hide" : "Show"} installed-only components`,
|
|
4153
|
+
` </button>`,
|
|
4056
4154
|
` </div>`,
|
|
4057
|
-
` {/* Category groups */}`,
|
|
4058
|
-
` {inventoryData.map(group =>
|
|
4155
|
+
` {/* Category groups — active components first */}`,
|
|
4156
|
+
` {inventoryData.map(group => {`,
|
|
4157
|
+
` const active = group.components.filter((c: any) => c.active);`,
|
|
4158
|
+
` const inactive = group.components.filter((c: any) => !c.active);`,
|
|
4159
|
+
` if (active.length === 0 && !expanded) return null;`,
|
|
4160
|
+
` return (`,
|
|
4059
4161
|
` <div key={group.category} style={{ marginBottom: 44 }}>`,
|
|
4060
4162
|
` <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, paddingBottom: 10, borderBottom: "2px solid #e5e7eb" }}>`,
|
|
4061
4163
|
` <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#111" }}>{group.category}</h3>`,
|
|
4062
|
-
` <span style={{ fontSize: 12, background: "#
|
|
4164
|
+
` <span style={{ fontSize: 12, background: "#dcfce7", color: "#15803d", padding: "2px 9px", borderRadius: 12, fontWeight: 600 }}>{active.length} used</span>`,
|
|
4165
|
+
` {inactive.length > 0 && <span style={{ fontSize: 12, background: "#f3f4f6", color: "#9ca3af", padding: "2px 9px", borderRadius: 12 }}>{inactive.length} installed</span>}`,
|
|
4063
4166
|
` </div>`,
|
|
4064
4167
|
` <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 12 }}>`,
|
|
4065
|
-
` {
|
|
4066
|
-
`
|
|
4067
|
-
` {/* Visual preview — rendered from project tokens at generation time */}`,
|
|
4068
|
-
` <div style={{ background: "#f8f9fa", borderBottom: "1px solid #f0f0f0", minHeight: 64, display: "flex", alignItems: "center", justifyContent: "center" }}`,
|
|
4069
|
-
` dangerouslySetInnerHTML={{ __html: comp.previewHtml }}`,
|
|
4070
|
-
` />`,
|
|
4071
|
-
` {/* Info strip */}`,
|
|
4072
|
-
` <div style={{ padding: "10px 12px" }}>`,
|
|
4073
|
-
` <div style={{ fontWeight: 700, fontSize: 13, color: "#111", marginBottom: 6 }}>{comp.name}</div>`,
|
|
4074
|
-
` <div style={{ display: "flex", gap: 4, flexWrap: "wrap" as any }}>`,
|
|
4075
|
-
` {comp.tokenCount > 0 && (`,
|
|
4076
|
-
` <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>`,
|
|
4077
|
-
` {comp.tokenCount} tokens`,
|
|
4078
|
-
` </span>`,
|
|
4079
|
-
` )}`,
|
|
4080
|
-
` {comp.colorSwatches.length > 0 && (`,
|
|
4081
|
-
` <span style={{ fontSize: 10, background: "#fef9c3", color: "#713f12", padding: "2px 7px", borderRadius: 10, border: "1px solid #fde68a" }}>`,
|
|
4082
|
-
` {comp.colorSwatches.length} colors`,
|
|
4083
|
-
` </span>`,
|
|
4084
|
-
` )}`,
|
|
4085
|
-
` {comp.propCount > 0 && (`,
|
|
4086
|
-
` <span style={{ fontSize: 10, background: "#f3f4f6", color: "#6b7280", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>`,
|
|
4087
|
-
` {comp.propCount} props`,
|
|
4088
|
-
` </span>`,
|
|
4089
|
-
` )}`,
|
|
4090
|
-
` {comp.group && comp.group !== "shadcn" && comp.group !== "UI" && comp.group !== "Components" && (`,
|
|
4091
|
-
` <span style={{ fontSize: 10, background: "#f0fdf4", color: "#166534", padding: "2px 7px", borderRadius: 10, border: "1px solid #bbf7d0" }}>`,
|
|
4092
|
-
` {comp.group}`,
|
|
4093
|
-
` </span>`,
|
|
4094
|
-
` )}`,
|
|
4095
|
-
` </div>`,
|
|
4096
|
-
` </div>`,
|
|
4097
|
-
` </div>`,
|
|
4098
|
-
` ))}`,
|
|
4168
|
+
` {active.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
|
|
4169
|
+
` {expanded && inactive.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
|
|
4099
4170
|
` </div>`,
|
|
4100
4171
|
` </div>`,
|
|
4101
|
-
`
|
|
4172
|
+
` );`,
|
|
4173
|
+
` })}`,
|
|
4102
4174
|
` </div>`,
|
|
4103
|
-
`
|
|
4175
|
+
` );`,
|
|
4176
|
+
` },`,
|
|
4104
4177
|
`};`,
|
|
4105
4178
|
].join("\n");
|
|
4106
4179
|
|
|
@@ -4473,21 +4546,32 @@ function main() {
|
|
|
4473
4546
|
}
|
|
4474
4547
|
}
|
|
4475
4548
|
|
|
4549
|
+
// Auto-detect which shadcn/ui components are actually used as JSX in the project source.
|
|
4550
|
+
// This replaces the manual `includeComponents` list — VDS now self-discovers which
|
|
4551
|
+
// shadcn primitives need stories instead of requiring users to maintain a whitelist.
|
|
4552
|
+
const allCompNames = components.map(c => c.name);
|
|
4553
|
+
const autoUsedSet = buildComponentUsageSet(allCompNames, PROJECT_ROOT);
|
|
4554
|
+
console.log(`[VDS] Auto-detected ${autoUsedSet.size} components with active JSX usage`);
|
|
4555
|
+
|
|
4476
4556
|
let writtenCount = 0;
|
|
4477
4557
|
for (const comp of components) {
|
|
4478
4558
|
const componentName = toSafeComponentName(comp.name, comp.file);
|
|
4479
|
-
// Skip unclassified and shadcn/ui primitives (UI group) — they're documented at ui.shadcn.com
|
|
4480
|
-
// Only project-specific module groups (Circles, Finance, Projects, Time, …) get stories
|
|
4481
4559
|
const g = comp.group || "Components";
|
|
4482
|
-
|
|
4483
|
-
|
|
4560
|
+
const gLower = g.toLowerCase();
|
|
4561
|
+
|
|
4562
|
+
// For shadcn/ui primitives: only generate a story if the component is actually
|
|
4563
|
+
// used as a JSX tag (<Button />, <Badge />, etc.) somewhere in the project source.
|
|
4564
|
+
// Manual overrides: includeGroups (all from group) and includeComponents (named list).
|
|
4484
4565
|
const includeGroups = VDS_CONFIG.includeGroups || [];
|
|
4485
4566
|
const includeComponents = VDS_CONFIG.includeComponents || [];
|
|
4486
|
-
|
|
4567
|
+
|
|
4487
4568
|
if (g === "Uncategorized" || gLower === "ui" || gLower === "shadcn") {
|
|
4488
4569
|
const groupIncluded = includeGroups.some((ig) => ig.toLowerCase() === gLower);
|
|
4489
4570
|
const compIncluded = includeComponents.includes(componentName);
|
|
4490
|
-
|
|
4571
|
+
// Auto-detection: is the component used as a JSX tag in the project?
|
|
4572
|
+
const autoUsed = autoUsedSet.has(comp.name) || autoUsedSet.has(componentName);
|
|
4573
|
+
|
|
4574
|
+
if (!groupIncluded && !compIncluded && !autoUsed) {
|
|
4491
4575
|
// Clean up leftover story files for components that are now in the skip group
|
|
4492
4576
|
const oldStory = path.join(STORIES_DIR, `${componentName}.stories.tsx`);
|
|
4493
4577
|
if (fs.existsSync(oldStory)) {
|
|
@@ -4495,12 +4579,15 @@ function main() {
|
|
|
4495
4579
|
const firstLine = fs.readFileSync(oldStory, "utf-8").split("\n")[0] || "";
|
|
4496
4580
|
if (firstLine.includes("@vds-regenerate")) {
|
|
4497
4581
|
fs.unlinkSync(oldStory);
|
|
4498
|
-
console.log(`[VDS] Removed shadcn
|
|
4582
|
+
console.log(`[VDS] Removed unused shadcn story: ${componentName}.stories.tsx`);
|
|
4499
4583
|
}
|
|
4500
4584
|
} catch { /* ignore */ }
|
|
4501
4585
|
}
|
|
4502
4586
|
continue;
|
|
4503
4587
|
}
|
|
4588
|
+
if (autoUsed && !compIncluded) {
|
|
4589
|
+
console.log(`[VDS] Auto-including ${componentName} (active JSX usage detected)`);
|
|
4590
|
+
}
|
|
4504
4591
|
}
|
|
4505
4592
|
if (onlyName && componentName !== onlyName) continue;
|
|
4506
4593
|
|