vibe-design-system 2.8.66 → 2.8.68

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.8.66",
3
+ "version": "2.8.68",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "homepage": "https://vibedesign.tech",
6
6
  "repository": {
@@ -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
  "",
@@ -4007,132 +4018,236 @@ function writeComponentInventoryStory(components, foundations) {
4007
4018
  const foundationsDir = path.join(STORIES_DIR, "foundations");
4008
4019
  ensureDir(foundationsDir);
4009
4020
 
4010
- // Build per-component data
4011
4021
  const CATEGORY_ORDER = [
4012
4022
  "Forms and Input", "Status Indicators", "Navigation", "Overlays and Layering",
4013
4023
  "Loading", "Messaging", "Images and Icons", "Layout and Structure", "Text and Data Display",
4014
4024
  ];
4015
- const categorized = {}; // category → [{ name, group, tokenCount, propCount, colorSwatches }]
4025
+ const categorized = {};
4016
4026
  const foundColors = (foundations && foundations.colors) ? foundations.colors : {};
4017
4027
 
4018
- // Determine which components are actually used as JSX in the project source
4019
4028
  const componentNames = components.map(c => c.name);
4020
4029
  const usedSet = buildComponentUsageSet(componentNames, PROJECT_ROOT);
4021
4030
 
4031
+ // ── Build import info for every component ──────────────────────────────────
4032
+ const atRoot = path.dirname(COMPONENTS_REL_DIR); // "client/src" or "src"
4033
+ const compBase = path.basename(COMPONENTS_REL_DIR); // "components"
4034
+
4035
+ function toPascalLocal(name) {
4036
+ return name.replace(/[-\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, c => c.toUpperCase());
4037
+ }
4038
+
4039
+ const importInfoMap = {}; // comp.name → { identifier, importAlias, isDefault }
4040
+ const seenAliases = new Set();
4041
+ const orderedImports = []; // deduplicated
4042
+
4022
4043
  for (const comp of components) {
4023
- const g = comp.group || "Components";
4024
- const gLower = g.toLowerCase();
4044
+ if (!comp.file) continue;
4045
+ const identifier = toPascalLocal(comp.name);
4046
+ const fileNoExt = comp.file.replace(/\.(tsx|ts|jsx|js)$/, '');
4047
+ const posixNoExt = fileNoExt.replace(/\\/g, '/');
4048
+
4049
+ // Derive @/ alias
4050
+ const absFromRoot = path.join(PROJECT_ROOT, comp.file);
4051
+ const isProjectRelative = fs.existsSync(absFromRoot);
4052
+ let importAlias;
4053
+ if (isProjectRelative) {
4054
+ const rel = path.posix.relative(atRoot, posixNoExt);
4055
+ importAlias = rel.startsWith('..') ? `@/${compBase}/${posixNoExt}` : `@/${rel}`;
4056
+ } else {
4057
+ importAlias = `@/${compBase}/${posixNoExt}`;
4058
+ }
4059
+
4060
+ // Detect default vs named export
4061
+ let isDefault = false;
4062
+ try {
4063
+ const srcPath = isProjectRelative
4064
+ ? absFromRoot
4065
+ : path.join(PROJECT_ROOT, COMPONENTS_REL_DIR, comp.file);
4066
+ isDefault = /export\s+default\b/.test(fs.readFileSync(srcPath, 'utf-8'));
4067
+ } catch { /* keep false */ }
4068
+
4069
+ importInfoMap[comp.name] = { identifier, importAlias, isDefault };
4070
+
4071
+ if (!seenAliases.has(importAlias)) {
4072
+ seenAliases.add(importAlias);
4073
+ orderedImports.push({ identifier, importAlias, isDefault });
4074
+ }
4075
+ }
4076
+
4077
+ // Does the project use @tanstack/react-query?
4078
+ let needsQC = false;
4079
+ try {
4080
+ const pkg = JSON.parse(fs.readFileSync(path.join(PROJECT_ROOT, 'package.json'), 'utf-8'));
4081
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
4082
+ needsQC = '@tanstack/react-query' in deps;
4083
+ } catch { /* ignore */ }
4084
+
4085
+ // Preview scale per category
4086
+ const COMPACT_CATS = new Set(["Status Indicators"]);
4087
+ const MEDIUM_CATS = new Set(["Forms and Input", "Images and Icons", "Loading"]);
4088
+
4089
+ // ── Per-component metadata ─────────────────────────────────────────────────
4090
+ for (const comp of components) {
4091
+ const g = comp.group || "Components";
4025
4092
  const tokens = Array.isArray(comp.tokens) ? comp.tokens : [];
4026
4093
  const props = Array.isArray(comp.props) ? comp.props : [];
4027
4094
  const semantic = classifyComponent(comp.name, tokens, props);
4028
4095
  const category = semantic || g;
4029
4096
 
4030
- // Resolve color tokens to hex swatches from foundations.colors (max 6)
4031
4097
  const colorSwatches = tokens
4032
4098
  .filter(t => !/:/.test(t) &&
4033
4099
  /^(bg|text|border|ring|from|to|fill|stroke)-/.test(t) &&
4034
- // Exclude border-side shorthands (b, t, l, r, x, y) and alignment/size text-* tokens
4035
4100
  !/^border-[btlrxy]$/.test(t) &&
4036
4101
  !/^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl|\d|left|right|center|justify|start|end|wrap|nowrap|balance|pretty|clip|ellipsis)/.test(t))
4037
4102
  .slice(0, 6)
4038
4103
  .map(token => {
4039
4104
  const m = token.match(/^(?:bg|text|border|ring|from|to|fill|stroke)-(.+)$/);
4040
4105
  let key = m ? m[1] : null;
4041
- // Strip opacity modifier: "muted/20" → "muted", "primary/5" → "primary"
4042
4106
  if (key) key = key.replace(/\/\d+$/, "");
4043
4107
  const entry = key ? foundColors[key] : null;
4044
4108
  const hex = entry?.hex && /^#[0-9a-fA-F]{3,8}$/.test(entry.hex) ? entry.hex : null;
4045
4109
  return { token, hex };
4046
4110
  })
4047
- .filter(s => s.hex); // only swatches with resolved hex
4111
+ .filter(s => s.hex);
4048
4112
 
4049
- // Visual preview HTML (computed at generation time using project's resolved colors)
4050
- const previewHtml = buildInventoryPreviewHtml(comp.name, category, colorSwatches, tokens.filter(t => !/:/.test(t)));
4051
-
4052
- // Whether this component is actually used as a JSX tag anywhere in the project source
4053
- const active = usedSet.has(comp.name);
4113
+ const previewHtml = buildInventoryPreviewHtml(comp.name, category, colorSwatches, tokens.filter(t => !/:/.test(t)));
4114
+ const active = usedSet.has(comp.name);
4115
+ const previewScale = COMPACT_CATS.has(category) ? 1.0 : MEDIUM_CATS.has(category) ? 0.65 : 0.3;
4054
4116
 
4055
4117
  if (!categorized[category]) categorized[category] = [];
4056
4118
  categorized[category].push({
4057
- name: comp.name,
4058
- group: g,
4059
- tokenCount: tokens.filter(t => !/:/.test(t)).length,
4060
- propCount: props.length,
4061
- colorSwatches,
4062
- previewHtml,
4063
- active,
4119
+ name: comp.name, group: g,
4120
+ tokenCount: tokens.filter(t => !/:/.test(t)).length,
4121
+ propCount: props.length,
4122
+ colorSwatches, previewHtml, previewScale, active,
4064
4123
  });
4065
4124
  }
4066
4125
 
4067
4126
  const sortedCategories = Object.keys(categorized).sort((a, b) => {
4068
- const ai = CATEGORY_ORDER.indexOf(a);
4069
- const bi = CATEGORY_ORDER.indexOf(b);
4127
+ const ai = CATEGORY_ORDER.indexOf(a), bi = CATEGORY_ORDER.indexOf(b);
4070
4128
  if (ai >= 0 && bi >= 0) return ai - bi;
4071
- if (ai >= 0) return -1;
4072
- if (bi >= 0) return 1;
4073
- return a.localeCompare(b);
4129
+ return (ai >= 0) ? -1 : (bi >= 0) ? 1 : a.localeCompare(b);
4074
4130
  });
4075
4131
 
4076
4132
  const inventoryData = sortedCategories.map(cat => ({
4077
4133
  category: cat,
4078
- // Sort: active first, then alphabetical within each group
4079
4134
  components: categorized[cat].sort((a, b) => {
4080
4135
  if (a.active !== b.active) return a.active ? -1 : 1;
4081
4136
  return a.name.localeCompare(b.name);
4082
4137
  }),
4083
4138
  }));
4084
4139
 
4085
- const totalComponents = components.length;
4140
+ const totalComponents = components.length;
4086
4141
  const activeComponents = [...usedSet].length;
4087
- const uniqueTokens = [...new Set(
4142
+ const uniqueTokens = [...new Set(
4088
4143
  components.flatMap(c => Array.isArray(c.tokens) ? c.tokens.filter(t => !/:/.test(t)) : [])
4089
4144
  )].length;
4090
4145
 
4146
+ // ── Build import lines ─────────────────────────────────────────────────────
4147
+ const importLines = orderedImports.map(imp =>
4148
+ imp.isDefault
4149
+ ? `import ${imp.identifier} from "${imp.importAlias}";`
4150
+ : `import { ${imp.identifier} } from "${imp.importAlias}";`
4151
+ );
4152
+
4153
+ // ── PREVIEWS map entries ───────────────────────────────────────────────────
4154
+ const TEXT_CHILD_NAMES = new Set(['button','badge','toggle','label','link','chip','tag','pill']);
4155
+ const previewEntries = components
4156
+ .filter(comp => importInfoMap[comp.name])
4157
+ .map(comp => {
4158
+ const { identifier } = importInfoMap[comp.name];
4159
+ const hasText = TEXT_CHILD_NAMES.has(identifier.toLowerCase());
4160
+ const call = hasText
4161
+ ? `() => React.createElement(${identifier} as any, null, "${comp.name}")`
4162
+ : `() => React.createElement(${identifier} as any)`;
4163
+ return ` ${JSON.stringify(comp.name)}: ${call}`;
4164
+ });
4165
+
4166
+ // ── Generate story file ────────────────────────────────────────────────────
4091
4167
  const content = [
4168
+ `import React from "react";`,
4092
4169
  `import type { Meta, StoryObj } from "@storybook/react";`,
4170
+ ...(needsQC ? [`import { QueryClient, QueryClientProvider } from "@tanstack/react-query";`] : []),
4171
+ ...importLines,
4093
4172
  ``,
4094
4173
  `const meta = { title: "Foundations/Component Inventory", parameters: { layout: "fullscreen" } } satisfies Meta;`,
4095
4174
  `export default meta;`,
4096
4175
  `type Story = StoryObj;`,
4097
4176
  ``,
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)};`,
4177
+ // ErrorBoundary catches broken component renders and falls back to shape HTML
4178
+ `class _EB extends React.Component<{ children: React.ReactNode; fallback: React.ReactNode }, { err: boolean }> {`,
4179
+ ` state = { err: false };`,
4180
+ ` static getDerivedStateFromError() { return { err: true }; }`,
4181
+ ` componentDidCatch() {}`,
4182
+ ` render() { return (this.state.err ? this.props.fallback : this.props.children) as React.ReactElement; }`,
4183
+ `}`,
4184
+ // Provider wrapper — QueryClientProvider if project uses react-query, otherwise fragment
4185
+ ...(needsQC ? [
4186
+ `const _qc = new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity } } });`,
4187
+ `const _PW = ({ children }: { children: React.ReactNode }) => React.createElement(QueryClientProvider, { client: _qc }, children) as React.ReactElement;`,
4188
+ ] : [
4189
+ `const _PW = ({ children }: { children: React.ReactNode }) => React.createElement(React.Fragment, null, children) as React.ReactElement;`,
4190
+ ]),
4191
+ // PREVIEWS: each component renders via its real import; ErrorBoundary catches failures
4192
+ `const PREVIEWS: Record<string, () => React.ReactElement> = {`,
4193
+ ...previewEntries.map(e => e + ','),
4194
+ `};`,
4195
+ ``,
4196
+ // Inventory data (metadata only — visual rendering is done at runtime via PREVIEWS)
4197
+ `const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number; colorSwatches: { token: string; hex: string }[]; previewHtml: string; previewScale: number; active: boolean }[] }[] = ${JSON.stringify(inventoryData)};`,
4099
4198
  `const totalComponents = ${totalComponents};`,
4100
4199
  `const activeComponents = ${activeComponents};`,
4101
4200
  `const uniqueTokens = ${uniqueTokens};`,
4102
4201
  ``,
4202
+ // CardPreview: renders the actual component at the right scale, falls back to shape HTML
4203
+ `const CardPreview = ({ comp }: { comp: any }) => {`,
4204
+ ` const s: number = comp.previewScale || 0.4;`,
4205
+ ` const centered = s >= 0.9;`,
4206
+ ` const fn = PREVIEWS[comp.name];`,
4207
+ ` const shapeHtml = (`,
4208
+ ` <div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}`,
4209
+ ` dangerouslySetInnerHTML={{ __html: comp.previewHtml }} />`,
4210
+ ` );`,
4211
+ ` const wPct = (+(100 / s).toFixed(1)) + "%";`,
4212
+ ` const innerStyle: React.CSSProperties = centered`,
4213
+ ` ? { position: "absolute", top: "50%", left: "50%",`,
4214
+ ` transform: "translate(-50%, -50%) scale(" + s + ")",`,
4215
+ ` transformOrigin: "center center", pointerEvents: "none" }`,
4216
+ ` : { position: "absolute", top: 0, left: 0, width: wPct,`,
4217
+ ` transform: "scale(" + s + ")",`,
4218
+ ` transformOrigin: "top left", pointerEvents: "none" };`,
4219
+ ` return (`,
4220
+ ` <div style={{ height: 96, overflow: "hidden", position: "relative", background: "#fff" }}>`,
4221
+ ` <_EB fallback={shapeHtml}>`,
4222
+ ` {fn ? <_PW><div style={innerStyle}>{fn()}</div></_PW> : shapeHtml}`,
4223
+ ` </_EB>`,
4224
+ ` </div>`,
4225
+ ` );`,
4226
+ `};`,
4227
+ `const Card = ({ comp }: { comp: any }) => (`,
4228
+ ` <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 }}>`,
4229
+ ` <div style={{ background: comp.active ? "#f8f9fa" : "#f5f5f5", borderBottom: "1px solid #f0f0f0" }}>`,
4230
+ ` <CardPreview comp={comp} />`,
4231
+ ` </div>`,
4232
+ ` <div style={{ padding: "10px 12px" }}>`,
4233
+ ` <div style={{ display: "flex", alignItems: "center", gap: 5, marginBottom: 5 }}>`,
4234
+ ` <span style={{ fontWeight: 700, fontSize: 13, color: comp.active ? "#111" : "#9ca3af" }}>{comp.name}</span>`,
4235
+ ` {comp.active && <span style={{ fontSize: 9, background: "#dcfce7", color: "#15803d", padding: "1px 5px", borderRadius: 4, fontWeight: 700, letterSpacing: "0.05em" }}>USED</span>}`,
4236
+ ` </div>`,
4237
+ ` <div style={{ display: "flex", gap: 4, flexWrap: "wrap" as any }}>`,
4238
+ ` {comp.tokenCount > 0 && <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>{comp.tokenCount} tokens</span>}`,
4239
+ ` {comp.colorSwatches.length > 0 && <span style={{ fontSize: 10, background: "#fef9c3", color: "#713f12", padding: "2px 7px", borderRadius: 10, border: "1px solid #fde68a" }}>{comp.colorSwatches.length} colors</span>}`,
4240
+ ` {!comp.active && <span style={{ fontSize: 10, background: "#f3f4f6", color: "#9ca3af", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>installed</span>}`,
4241
+ ` </div>`,
4242
+ ` </div>`,
4243
+ ` </div>`,
4244
+ `);`,
4245
+ ``,
4103
4246
  `export const Default: Story = {`,
4104
4247
  ` 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
4248
  ` 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
4249
  ` return (`,
4134
4250
  ` <div style={{ padding: 32, background: "#f8f9fa", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`,
4135
- ` {/* Header */}`,
4136
4251
  ` <div style={{ marginBottom: 40 }}>`,
4137
4252
  ` <h2 style={{ fontSize: 28, fontWeight: 700, margin: "0 0 8px" }}>Component Inventory</h2>`,
4138
4253
  ` <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>`,
@@ -4142,7 +4257,7 @@ function writeComponentInventoryStory(components, foundations) {
4142
4257
  ` { value: (totalComponents - activeComponents).toString(), label: "Installed Only", bg: "#f9fafb", fg: "#6b7280", border: "#e5e7eb" },`,
4143
4258
  ` { value: uniqueTokens.toString(), label: "Unique Tokens", bg: "#eff6ff", fg: "#1d4ed8", border: "#dbeafe" },`,
4144
4259
  ` ].map(stat => (`,
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}\` }}>`,
4260
+ ` <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
4261
  ` <div style={{ fontSize: 26, fontWeight: 800, color: stat.fg, lineHeight: 1 }}>{stat.value}</div>`,
4147
4262
  ` <div style={{ fontSize: 12, color: stat.fg, marginTop: 4, opacity: 0.8 }}>{stat.label}</div>`,
4148
4263
  ` </div>`,
@@ -4152,23 +4267,22 @@ function writeComponentInventoryStory(components, foundations) {
4152
4267
  ` {expanded ? "Hide" : "Show"} installed-only components`,
4153
4268
  ` </button>`,
4154
4269
  ` </div>`,
4155
- ` {/* Category groups — active components first */}`,
4156
4270
  ` {inventoryData.map(group => {`,
4157
4271
  ` const active = group.components.filter((c: any) => c.active);`,
4158
4272
  ` const inactive = group.components.filter((c: any) => !c.active);`,
4159
4273
  ` if (active.length === 0 && !expanded) return null;`,
4160
4274
  ` return (`,
4161
- ` <div key={group.category} style={{ marginBottom: 44 }}>`,
4162
- ` <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, paddingBottom: 10, borderBottom: "2px solid #e5e7eb" }}>`,
4163
- ` <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#111" }}>{group.category}</h3>`,
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>}`,
4166
- ` </div>`,
4167
- ` <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 12 }}>`,
4168
- ` {active.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
4169
- ` {expanded && inactive.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
4275
+ ` <div key={group.category} style={{ marginBottom: 44 }}>`,
4276
+ ` <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, paddingBottom: 10, borderBottom: "2px solid #e5e7eb" }}>`,
4277
+ ` <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#111" }}>{group.category}</h3>`,
4278
+ ` <span style={{ fontSize: 12, background: "#dcfce7", color: "#15803d", padding: "2px 9px", borderRadius: 12, fontWeight: 600 }}>{active.length} used</span>`,
4279
+ ` {inactive.length > 0 && <span style={{ fontSize: 12, background: "#f3f4f6", color: "#9ca3af", padding: "2px 9px", borderRadius: 12 }}>{inactive.length} installed</span>}`,
4280
+ ` </div>`,
4281
+ ` <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 12 }}>`,
4282
+ ` {active.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
4283
+ ` {expanded && inactive.map((comp: any) => <Card key={comp.name} comp={comp} />)}`,
4284
+ ` </div>`,
4170
4285
  ` </div>`,
4171
- ` </div>`,
4172
4286
  ` );`,
4173
4287
  ` })}`,
4174
4288
  ` </div>`,
@@ -4227,6 +4341,7 @@ function writeChangelogStory(changelog) {
4227
4341
  const entries = Array.isArray(changelog) ? changelog.map((e) => ({ version: e.version, date: e.date, changes: e.changes || [] })) : [];
4228
4342
  const content =
4229
4343
  [
4344
+ "import React from \"react\";",
4230
4345
  "import type { Meta, StoryObj } from \"@storybook/react\";",
4231
4346
  "",
4232
4347
  "const meta = { title: \"Foundations/Changelog\", tags: [\"autodocs\"], parameters: { layout: \"fullscreen\" } } satisfies Meta;",