vibe-design-system 2.8.65 → 2.8.67
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
|
@@ -2505,6 +2505,7 @@ function writeFoundationsStories(foundations) {
|
|
|
2505
2505
|
.slice(0, 60); // cap to avoid extremely long stories
|
|
2506
2506
|
|
|
2507
2507
|
const colorsContent = [
|
|
2508
|
+
"import React from \"react\";",
|
|
2508
2509
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
2509
2510
|
"",
|
|
2510
2511
|
"const meta = { title: \"Foundations/Colors\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -2630,6 +2631,7 @@ function writeFoundationsStories(foundations) {
|
|
|
2630
2631
|
const sansFamily = typo.fontSans || typo.tailwindSans || typo.body || "system-ui, sans-serif";
|
|
2631
2632
|
|
|
2632
2633
|
const typoContent = [
|
|
2634
|
+
"import React from \"react\";",
|
|
2633
2635
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
2634
2636
|
"",
|
|
2635
2637
|
"const meta = { title: \"Foundations/Typography\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -2790,6 +2792,7 @@ function writeFoundationsStories(foundations) {
|
|
|
2790
2792
|
|
|
2791
2793
|
const brandContent =
|
|
2792
2794
|
[
|
|
2795
|
+
"import React from \"react\";",
|
|
2793
2796
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
2794
2797
|
"",
|
|
2795
2798
|
"const meta = { title: \"Foundations/Brand\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -2823,6 +2826,7 @@ function writeFoundationsStories(foundations) {
|
|
|
2823
2826
|
|
|
2824
2827
|
const iconsContent = [
|
|
2825
2828
|
"import { useState } from \"react\";",
|
|
2829
|
+
"import React from \"react\";",
|
|
2826
2830
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
2827
2831
|
"import * as Lucide from \"lucide-react\";",
|
|
2828
2832
|
"",
|
|
@@ -2997,6 +3001,7 @@ function writeFoundationsStories(foundations) {
|
|
|
2997
3001
|
const containerCount = gridSystem.containerCount || 0;
|
|
2998
3002
|
|
|
2999
3003
|
const gridContent = [
|
|
3004
|
+
"import React from \"react\";",
|
|
3000
3005
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3001
3006
|
"",
|
|
3002
3007
|
"const meta = { title: \"Foundations/Grid\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -3186,6 +3191,7 @@ function writeFoundationsStories(foundations) {
|
|
|
3186
3191
|
}));
|
|
3187
3192
|
const buttonsContent =
|
|
3188
3193
|
[
|
|
3194
|
+
"import React from \"react\";",
|
|
3189
3195
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3190
3196
|
"",
|
|
3191
3197
|
"const meta = { title: \"Foundations/Button Usage\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -3261,6 +3267,7 @@ function writeFoundationsStories(foundations) {
|
|
|
3261
3267
|
|
|
3262
3268
|
const usedSpacing = (foundations?.tokenUsage?.spacing || []).slice(0, 16);
|
|
3263
3269
|
const spacingContent = [
|
|
3270
|
+
"import React from \"react\";",
|
|
3264
3271
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3265
3272
|
"",
|
|
3266
3273
|
"const meta = { title: \"Foundations/Spacing & Layout\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -3368,6 +3375,7 @@ function writeFoundationsStories(foundations) {
|
|
|
3368
3375
|
const zIndexSemantics = { "z-0": "Base", "z-10": "Low / hover", "z-20": "Dropdown", "z-30": "Sticky", "z-40": "Fixed", "z-50": "Modal / overlay" };
|
|
3369
3376
|
|
|
3370
3377
|
const elevationContent = [
|
|
3378
|
+
"import React from \"react\";",
|
|
3371
3379
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3372
3380
|
"",
|
|
3373
3381
|
"const meta = { title: \"Foundations/Elevation & Shadows\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -3490,6 +3498,7 @@ function writeFoundationsStories(foundations) {
|
|
|
3490
3498
|
const usedRadius = (foundations?.tokenUsage?.radius || []).slice(0, 12);
|
|
3491
3499
|
|
|
3492
3500
|
const borderContent = [
|
|
3501
|
+
"import React from \"react\";",
|
|
3493
3502
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3494
3503
|
"",
|
|
3495
3504
|
"const meta = { title: \"Foundations/Border & Radius\", parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -3593,6 +3602,7 @@ function writeFoundationsStories(foundations) {
|
|
|
3593
3602
|
const motionChips = (foundations?.tokenUsage?.animations || []).slice(0, 24);
|
|
3594
3603
|
|
|
3595
3604
|
const motionContent = [
|
|
3605
|
+
"import React from \"react\";",
|
|
3596
3606
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3597
3607
|
"import { useState, useEffect } from \"react\";",
|
|
3598
3608
|
"",
|
|
@@ -3743,6 +3753,7 @@ function writeComponentSuggestionsStory(componentSuggestions) {
|
|
|
3743
3753
|
}));
|
|
3744
3754
|
const content =
|
|
3745
3755
|
[
|
|
3756
|
+
"import React from \"react\";",
|
|
3746
3757
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
3747
3758
|
"import { useState } from \"react\";",
|
|
3748
3759
|
"",
|
|
@@ -3950,6 +3961,58 @@ function buildInventoryPreviewHtml(compName, category, colorSwatches, tokens) {
|
|
|
3950
3961
|
* Shows all project components classified by Atlassian-style functional category.
|
|
3951
3962
|
* Every value is derived from vds-output.json — no placeholder content.
|
|
3952
3963
|
*/
|
|
3964
|
+
/**
|
|
3965
|
+
* Single-pass JSX usage scan: returns a Set of component names (PascalCase-normalised)
|
|
3966
|
+
* that actually appear as <Tag in the project's source files (excluding ui/ definitions
|
|
3967
|
+
* and story files). This is used to distinguish "used in project" from "installed only".
|
|
3968
|
+
*/
|
|
3969
|
+
function buildComponentUsageSet(componentNames, projectRoot) {
|
|
3970
|
+
// Pre-compute pascal names for each component
|
|
3971
|
+
const patterns = componentNames.map(name => {
|
|
3972
|
+
const pascal = name
|
|
3973
|
+
.replace(/[-\s]+(.)/g, (_, c) => c.toUpperCase())
|
|
3974
|
+
.replace(/^(.)/, c => c.toUpperCase());
|
|
3975
|
+
return { name, pascal };
|
|
3976
|
+
});
|
|
3977
|
+
|
|
3978
|
+
const usedNames = new Set();
|
|
3979
|
+
|
|
3980
|
+
// Resolve the shadcn ui directory path to exclude component definition files
|
|
3981
|
+
// (e.g. dialog.tsx contains <DialogClose> which would falsely register Dialog as "used")
|
|
3982
|
+
const uiDefDir = path.resolve(path.join(projectRoot, COMPONENTS_REL_DIR));
|
|
3983
|
+
|
|
3984
|
+
function walkDir(dir) {
|
|
3985
|
+
let entries;
|
|
3986
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
3987
|
+
for (const entry of entries) {
|
|
3988
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
3989
|
+
const full = path.join(dir, entry.name);
|
|
3990
|
+
if (entry.isDirectory()) {
|
|
3991
|
+
// Skip: story dirs, storybook config, AND the shadcn/ui component definitions dir
|
|
3992
|
+
if (["stories", ".storybook"].includes(entry.name)) continue;
|
|
3993
|
+
if (path.resolve(full) === uiDefDir) continue; // shadcn ui/ — skip definitions
|
|
3994
|
+
walkDir(full);
|
|
3995
|
+
} else if (/\.(tsx|jsx)$/.test(entry.name) && !/\.stories\./.test(entry.name)) {
|
|
3996
|
+
let src;
|
|
3997
|
+
try { src = fs.readFileSync(full, "utf-8"); } catch { continue; }
|
|
3998
|
+
for (const { name, pascal } of patterns) {
|
|
3999
|
+
if (!usedNames.has(name) && src.includes(`<${pascal}`)) {
|
|
4000
|
+
usedNames.add(name);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
// Walk common source roots (excluding the component definition directories themselves)
|
|
4008
|
+
const srcRoots = ["src", "client/src", "app", "pages"]
|
|
4009
|
+
.map(d => path.join(projectRoot, d))
|
|
4010
|
+
.filter(d => fs.existsSync(d));
|
|
4011
|
+
for (const root of srcRoots) walkDir(root);
|
|
4012
|
+
|
|
4013
|
+
return usedNames;
|
|
4014
|
+
}
|
|
4015
|
+
|
|
3953
4016
|
function writeComponentInventoryStory(components, foundations) {
|
|
3954
4017
|
if (!Array.isArray(components) || components.length === 0) return;
|
|
3955
4018
|
const foundationsDir = path.join(STORIES_DIR, "foundations");
|
|
@@ -3963,6 +4026,10 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
3963
4026
|
const categorized = {}; // category → [{ name, group, tokenCount, propCount, colorSwatches }]
|
|
3964
4027
|
const foundColors = (foundations && foundations.colors) ? foundations.colors : {};
|
|
3965
4028
|
|
|
4029
|
+
// Determine which components are actually used as JSX in the project source
|
|
4030
|
+
const componentNames = components.map(c => c.name);
|
|
4031
|
+
const usedSet = buildComponentUsageSet(componentNames, PROJECT_ROOT);
|
|
4032
|
+
|
|
3966
4033
|
for (const comp of components) {
|
|
3967
4034
|
const g = comp.group || "Components";
|
|
3968
4035
|
const gLower = g.toLowerCase();
|
|
@@ -3993,6 +4060,9 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
3993
4060
|
// Visual preview HTML (computed at generation time using project's resolved colors)
|
|
3994
4061
|
const previewHtml = buildInventoryPreviewHtml(comp.name, category, colorSwatches, tokens.filter(t => !/:/.test(t)));
|
|
3995
4062
|
|
|
4063
|
+
// Whether this component is actually used as a JSX tag anywhere in the project source
|
|
4064
|
+
const active = usedSet.has(comp.name);
|
|
4065
|
+
|
|
3996
4066
|
if (!categorized[category]) categorized[category] = [];
|
|
3997
4067
|
categorized[category].push({
|
|
3998
4068
|
name: comp.name,
|
|
@@ -4001,6 +4071,7 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
4001
4071
|
propCount: props.length,
|
|
4002
4072
|
colorSwatches,
|
|
4003
4073
|
previewHtml,
|
|
4074
|
+
active,
|
|
4004
4075
|
});
|
|
4005
4076
|
}
|
|
4006
4077
|
|
|
@@ -4015,92 +4086,106 @@ function writeComponentInventoryStory(components, foundations) {
|
|
|
4015
4086
|
|
|
4016
4087
|
const inventoryData = sortedCategories.map(cat => ({
|
|
4017
4088
|
category: cat,
|
|
4018
|
-
|
|
4089
|
+
// Sort: active first, then alphabetical within each group
|
|
4090
|
+
components: categorized[cat].sort((a, b) => {
|
|
4091
|
+
if (a.active !== b.active) return a.active ? -1 : 1;
|
|
4092
|
+
return a.name.localeCompare(b.name);
|
|
4093
|
+
}),
|
|
4019
4094
|
}));
|
|
4020
4095
|
|
|
4021
4096
|
const totalComponents = components.length;
|
|
4097
|
+
const activeComponents = [...usedSet].length;
|
|
4022
4098
|
const uniqueTokens = [...new Set(
|
|
4023
4099
|
components.flatMap(c => Array.isArray(c.tokens) ? c.tokens.filter(t => !/:/.test(t)) : [])
|
|
4024
4100
|
)].length;
|
|
4025
4101
|
|
|
4026
4102
|
const content = [
|
|
4103
|
+
`import React from "react";`,
|
|
4027
4104
|
`import type { Meta, StoryObj } from "@storybook/react";`,
|
|
4028
4105
|
``,
|
|
4029
4106
|
`const meta = { title: "Foundations/Component Inventory", parameters: { layout: "fullscreen" } } satisfies Meta;`,
|
|
4030
4107
|
`export default meta;`,
|
|
4031
4108
|
`type Story = StoryObj;`,
|
|
4032
4109
|
``,
|
|
4033
|
-
`const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number; colorSwatches: { token: string; hex: string }[]; previewHtml: string }[] }[] = ${JSON.stringify(inventoryData)};`,
|
|
4110
|
+
`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
4111
|
`const totalComponents = ${totalComponents};`,
|
|
4112
|
+
`const activeComponents = ${activeComponents};`,
|
|
4035
4113
|
`const uniqueTokens = ${uniqueTokens};`,
|
|
4036
4114
|
``,
|
|
4037
4115
|
`export const Default: Story = {`,
|
|
4038
|
-
` render: () =>
|
|
4116
|
+
` render: () => {`,
|
|
4117
|
+
` const [showUnused, setShowUnused] = (window as any).__vdsShowUnused !== undefined`,
|
|
4118
|
+
` ? [(window as any).__vdsShowUnused, (v: boolean) => { (window as any).__vdsShowUnused = v; }]`,
|
|
4119
|
+
` : [false, () => {}];`,
|
|
4120
|
+
` const [expanded, setExpanded] = React.useState(false);`,
|
|
4121
|
+
` const Card = ({ comp }: { comp: any }) => (`,
|
|
4122
|
+
` <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 }}>`,
|
|
4123
|
+
` <div style={{ background: comp.active ? "#f8f9fa" : "#f5f5f5", borderBottom: "1px solid #f0f0f0", minHeight: 64, display: "flex", alignItems: "center", justifyContent: "center" }}`,
|
|
4124
|
+
` dangerouslySetInnerHTML={{ __html: comp.previewHtml }}`,
|
|
4125
|
+
` />`,
|
|
4126
|
+
` <div style={{ padding: "10px 12px" }}>`,
|
|
4127
|
+
` <div style={{ display: "flex", alignItems: "center", gap: 5, marginBottom: 5 }}>`,
|
|
4128
|
+
` <span style={{ fontWeight: 700, fontSize: 13, color: comp.active ? "#111" : "#9ca3af" }}>{comp.name}</span>`,
|
|
4129
|
+
` {comp.active && <span style={{ fontSize: 9, background: "#dcfce7", color: "#15803d", padding: "1px 5px", borderRadius: 4, fontWeight: 700, letterSpacing: "0.05em" }}>USED</span>}`,
|
|
4130
|
+
` </div>`,
|
|
4131
|
+
` <div style={{ display: "flex", gap: 4, flexWrap: "wrap" as any }}>`,
|
|
4132
|
+
` {comp.tokenCount > 0 && (`,
|
|
4133
|
+
` <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>{comp.tokenCount} tokens</span>`,
|
|
4134
|
+
` )}`,
|
|
4135
|
+
` {comp.colorSwatches.length > 0 && (`,
|
|
4136
|
+
` <span style={{ fontSize: 10, background: "#fef9c3", color: "#713f12", padding: "2px 7px", borderRadius: 10, border: "1px solid #fde68a" }}>{comp.colorSwatches.length} colors</span>`,
|
|
4137
|
+
` )}`,
|
|
4138
|
+
` {!comp.active && (`,
|
|
4139
|
+
` <span style={{ fontSize: 10, background: "#f3f4f6", color: "#9ca3af", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>installed</span>`,
|
|
4140
|
+
` )}`,
|
|
4141
|
+
` </div>`,
|
|
4142
|
+
` </div>`,
|
|
4143
|
+
` </div>`,
|
|
4144
|
+
` );`,
|
|
4145
|
+
` return (`,
|
|
4039
4146
|
` <div style={{ padding: 32, background: "#f8f9fa", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`,
|
|
4040
4147
|
` {/* Header */}`,
|
|
4041
4148
|
` <div style={{ marginBottom: 40 }}>`,
|
|
4042
4149
|
` <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 }}>`,
|
|
4150
|
+
` <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>`,
|
|
4151
|
+
` <div style={{ display: "flex", gap: 12, flexWrap: "wrap" as any, marginBottom: 20 }}>`,
|
|
4045
4152
|
` {[`,
|
|
4046
|
-
` { value:
|
|
4047
|
-
` { value:
|
|
4048
|
-
` { value: uniqueTokens.toString(), label: "Unique Tokens" },`,
|
|
4153
|
+
` { value: activeComponents.toString(), label: "Used in Project", bg: "#f0fdf4", fg: "#15803d", border: "#bbf7d0" },`,
|
|
4154
|
+
` { value: (totalComponents - activeComponents).toString(), label: "Installed Only", bg: "#f9fafb", fg: "#6b7280", border: "#e5e7eb" },`,
|
|
4155
|
+
` { value: uniqueTokens.toString(), label: "Unique Tokens", bg: "#eff6ff", fg: "#1d4ed8", border: "#dbeafe" },`,
|
|
4049
4156
|
` ].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:
|
|
4157
|
+
` <div key={stat.label} style={{ padding: "12px 20px", background: stat.bg, borderRadius: 10, textAlign: "center" as any, minWidth: 110, border: \`1px solid \${stat.border}\` }}>`,
|
|
4158
|
+
` <div style={{ fontSize: 26, fontWeight: 800, color: stat.fg, lineHeight: 1 }}>{stat.value}</div>`,
|
|
4159
|
+
` <div style={{ fontSize: 12, color: stat.fg, marginTop: 4, opacity: 0.8 }}>{stat.label}</div>`,
|
|
4053
4160
|
` </div>`,
|
|
4054
4161
|
` ))}`,
|
|
4055
4162
|
` </div>`,
|
|
4163
|
+
` <button onClick={() => setExpanded(!expanded)} style={{ fontSize: 12, color: "#6b7280", background: "transparent", border: "1px solid #e5e7eb", borderRadius: 6, padding: "5px 12px", cursor: "pointer" }}>`,
|
|
4164
|
+
` {expanded ? "Hide" : "Show"} installed-only components`,
|
|
4165
|
+
` </button>`,
|
|
4056
4166
|
` </div>`,
|
|
4057
|
-
` {/* Category groups */}`,
|
|
4058
|
-
` {inventoryData.map(group =>
|
|
4167
|
+
` {/* Category groups — active components first */}`,
|
|
4168
|
+
` {inventoryData.map(group => {`,
|
|
4169
|
+
` const active = group.components.filter((c: any) => c.active);`,
|
|
4170
|
+
` const inactive = group.components.filter((c: any) => !c.active);`,
|
|
4171
|
+
` if (active.length === 0 && !expanded) return null;`,
|
|
4172
|
+
` return (`,
|
|
4059
4173
|
` <div key={group.category} style={{ marginBottom: 44 }}>`,
|
|
4060
4174
|
` <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, paddingBottom: 10, borderBottom: "2px solid #e5e7eb" }}>`,
|
|
4061
4175
|
` <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#111" }}>{group.category}</h3>`,
|
|
4062
|
-
` <span style={{ fontSize: 12, background: "#
|
|
4176
|
+
` <span style={{ fontSize: 12, background: "#dcfce7", color: "#15803d", padding: "2px 9px", borderRadius: 12, fontWeight: 600 }}>{active.length} used</span>`,
|
|
4177
|
+
` {inactive.length > 0 && <span style={{ fontSize: 12, background: "#f3f4f6", color: "#9ca3af", padding: "2px 9px", borderRadius: 12 }}>{inactive.length} installed</span>}`,
|
|
4063
4178
|
` </div>`,
|
|
4064
4179
|
` <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
|
-
` ))}`,
|
|
4180
|
+
` {active.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
|
|
4181
|
+
` {expanded && inactive.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
|
|
4099
4182
|
` </div>`,
|
|
4100
4183
|
` </div>`,
|
|
4101
|
-
`
|
|
4184
|
+
` );`,
|
|
4185
|
+
` })}`,
|
|
4102
4186
|
` </div>`,
|
|
4103
|
-
`
|
|
4187
|
+
` );`,
|
|
4188
|
+
` },`,
|
|
4104
4189
|
`};`,
|
|
4105
4190
|
].join("\n");
|
|
4106
4191
|
|
|
@@ -4154,6 +4239,7 @@ function writeChangelogStory(changelog) {
|
|
|
4154
4239
|
const entries = Array.isArray(changelog) ? changelog.map((e) => ({ version: e.version, date: e.date, changes: e.changes || [] })) : [];
|
|
4155
4240
|
const content =
|
|
4156
4241
|
[
|
|
4242
|
+
"import React from \"react\";",
|
|
4157
4243
|
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
4158
4244
|
"",
|
|
4159
4245
|
"const meta = { title: \"Foundations/Changelog\", tags: [\"autodocs\"], parameters: { layout: \"fullscreen\" } } satisfies Meta;",
|
|
@@ -4473,21 +4559,32 @@ function main() {
|
|
|
4473
4559
|
}
|
|
4474
4560
|
}
|
|
4475
4561
|
|
|
4562
|
+
// Auto-detect which shadcn/ui components are actually used as JSX in the project source.
|
|
4563
|
+
// This replaces the manual `includeComponents` list — VDS now self-discovers which
|
|
4564
|
+
// shadcn primitives need stories instead of requiring users to maintain a whitelist.
|
|
4565
|
+
const allCompNames = components.map(c => c.name);
|
|
4566
|
+
const autoUsedSet = buildComponentUsageSet(allCompNames, PROJECT_ROOT);
|
|
4567
|
+
console.log(`[VDS] Auto-detected ${autoUsedSet.size} components with active JSX usage`);
|
|
4568
|
+
|
|
4476
4569
|
let writtenCount = 0;
|
|
4477
4570
|
for (const comp of components) {
|
|
4478
4571
|
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
4572
|
const g = comp.group || "Components";
|
|
4482
|
-
|
|
4483
|
-
|
|
4573
|
+
const gLower = g.toLowerCase();
|
|
4574
|
+
|
|
4575
|
+
// For shadcn/ui primitives: only generate a story if the component is actually
|
|
4576
|
+
// used as a JSX tag (<Button />, <Badge />, etc.) somewhere in the project source.
|
|
4577
|
+
// Manual overrides: includeGroups (all from group) and includeComponents (named list).
|
|
4484
4578
|
const includeGroups = VDS_CONFIG.includeGroups || [];
|
|
4485
4579
|
const includeComponents = VDS_CONFIG.includeComponents || [];
|
|
4486
|
-
|
|
4580
|
+
|
|
4487
4581
|
if (g === "Uncategorized" || gLower === "ui" || gLower === "shadcn") {
|
|
4488
4582
|
const groupIncluded = includeGroups.some((ig) => ig.toLowerCase() === gLower);
|
|
4489
4583
|
const compIncluded = includeComponents.includes(componentName);
|
|
4490
|
-
|
|
4584
|
+
// Auto-detection: is the component used as a JSX tag in the project?
|
|
4585
|
+
const autoUsed = autoUsedSet.has(comp.name) || autoUsedSet.has(componentName);
|
|
4586
|
+
|
|
4587
|
+
if (!groupIncluded && !compIncluded && !autoUsed) {
|
|
4491
4588
|
// Clean up leftover story files for components that are now in the skip group
|
|
4492
4589
|
const oldStory = path.join(STORIES_DIR, `${componentName}.stories.tsx`);
|
|
4493
4590
|
if (fs.existsSync(oldStory)) {
|
|
@@ -4495,12 +4592,15 @@ function main() {
|
|
|
4495
4592
|
const firstLine = fs.readFileSync(oldStory, "utf-8").split("\n")[0] || "";
|
|
4496
4593
|
if (firstLine.includes("@vds-regenerate")) {
|
|
4497
4594
|
fs.unlinkSync(oldStory);
|
|
4498
|
-
console.log(`[VDS] Removed shadcn
|
|
4595
|
+
console.log(`[VDS] Removed unused shadcn story: ${componentName}.stories.tsx`);
|
|
4499
4596
|
}
|
|
4500
4597
|
} catch { /* ignore */ }
|
|
4501
4598
|
}
|
|
4502
4599
|
continue;
|
|
4503
4600
|
}
|
|
4601
|
+
if (autoUsed && !compIncluded) {
|
|
4602
|
+
console.log(`[VDS] Auto-including ${componentName} (active JSX usage detected)`);
|
|
4603
|
+
}
|
|
4504
4604
|
}
|
|
4505
4605
|
if (onlyName && componentName !== onlyName) continue;
|
|
4506
4606
|
|