vibe-design-system 2.8.63 → 2.8.64

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.63",
3
+ "version": "2.8.64",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "homepage": "https://vibedesign.tech",
6
6
  "repository": {
@@ -1991,6 +1991,44 @@ function buildStoryFileContent(comp) {
1991
1991
  return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName], defaultArgLines, lucideImports, iconPropNames, needReact, variantMap);
1992
1992
  }
1993
1993
 
1994
+ // Detect if the component uses @tanstack/react-query hooks → needs QueryClientProvider in story.
1995
+ // Checks direct imports AND one level deep into local hook files (e.g. @/hooks/use-crypto.ts).
1996
+ const needsQueryClient = (() => {
1997
+ if (!source.length) return false;
1998
+ // Direct usage in this file
1999
+ if (/from\s+['"]@tanstack\/react-query['"]/m.test(source) ||
2000
+ /\b(useQuery|useMutation|useQueryClient|useInfiniteQuery|useSuspenseQuery)\b/.test(source)) {
2001
+ return true;
2002
+ }
2003
+ // One level deep: check imported hook files (e.g. @/hooks/*, ../hooks/*, ./hooks/*)
2004
+ // Derive the @/ alias source root from COMPONENTS_REL_DIR:
2005
+ // "src/components" → srcRoot = "src"
2006
+ // "client/src/components" → srcRoot = "client/src"
2007
+ const atAliasRoot = path.dirname(COMPONENTS_REL_DIR); // e.g. "src" or "client/src"
2008
+ const hookImportRe = /from\s+['"]([^'"]*\/hooks\/[^'"]+)['"]/g;
2009
+ let m;
2010
+ while ((m = hookImportRe.exec(source)) !== null) {
2011
+ try {
2012
+ let rel = m[1];
2013
+ // Resolve @ alias using derived source root
2014
+ if (rel.startsWith("@/")) rel = atAliasRoot + "/" + rel.slice(2);
2015
+ const exts = [".ts", ".tsx", ".js", ".jsx"];
2016
+ for (const ext of exts) {
2017
+ const full = path.join(PROJECT_ROOT, rel + ext);
2018
+ if (fs.existsSync(full)) {
2019
+ const hookSrc = fs.readFileSync(full, "utf-8");
2020
+ if (/from\s+['"]@tanstack\/react-query['"]/m.test(hookSrc) ||
2021
+ /\b(useQuery|useMutation|useQueryClient|useInfiniteQuery)\b/.test(hookSrc)) {
2022
+ return true;
2023
+ }
2024
+ break;
2025
+ }
2026
+ }
2027
+ } catch { /* ignore */ }
2028
+ }
2029
+ return false;
2030
+ })();
2031
+
1994
2032
  const lines = [];
1995
2033
  lines.push(`// @vds-regenerate — VDS auto-generated. Remove this line to prevent overwrite.`);
1996
2034
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
@@ -1998,11 +2036,17 @@ function buildStoryFileContent(comp) {
1998
2036
  if (lucideImports.length > 0) {
1999
2037
  lines.push(`import { ${lucideImports.join(", ")} } from "lucide-react";`);
2000
2038
  }
2039
+ if (needsQueryClient) {
2040
+ lines.push(`import { QueryClient, QueryClientProvider } from "@tanstack/react-query";`);
2041
+ }
2001
2042
 
2002
2043
  // Dynamic import + catch so "Failed to fetch dynamically imported module" shows a fallback instead of breaking Storybook
2003
2044
  const loadFallback = `() => React.createElement('div', { style: { padding: 16, color: '#666', fontSize: 14 } }, 'Component could not be loaded (import error).')`;
2004
2045
  const getDefault = `(m) => ({ default: m["${componentName}"] ?? m.default })`;
2005
2046
  lines.push(`const ComponentRef = React.lazy(() => import(/* @vite-ignore */ "${importPath}").then(${getDefault}).catch(() => ({ default: ${loadFallback} })));`);
2047
+ if (needsQueryClient) {
2048
+ lines.push(`const _queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity } } });`);
2049
+ }
2006
2050
  lines.push("");
2007
2051
 
2008
2052
  const safeDefaultsObj = componentName && SAFE_WRAPPER_DEFAULTS[componentName];
@@ -2018,6 +2062,10 @@ function buildStoryFileContent(comp) {
2018
2062
  if (profile !== "SECTION") lines.push(` tags: ["autodocs"],`);
2019
2063
  // Center small components (VARIANT, WRAPPER, CONFIGURED, SAFE) to prevent vertical stretching
2020
2064
  if (profile !== "SECTION") lines.push(` parameters: { layout: "centered" },`);
2065
+ // Wrap with QueryClientProvider for components that use @tanstack/react-query hooks
2066
+ if (needsQueryClient) {
2067
+ lines.push(` decorators: [(Story: any) => React.createElement(QueryClientProvider, { client: _queryClient }, React.createElement(Story))],`);
2068
+ }
2021
2069
 
2022
2070
  // Build argTypes from extracted TypeScript props + icon-specific overrides
2023
2071
  const argTypeEntries = [];
@@ -2161,7 +2209,7 @@ function buildStoryFileContent(comp) {
2161
2209
  lines.push(` </section>`);
2162
2210
  lines.push(` );`);
2163
2211
  lines.push(` return (`);
2164
- lines.push(` <div style={{ padding: 40, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh" }}>`);
2212
+ lines.push(` <div style={{ padding: 40, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`);
2165
2213
  lines.push(` <h2 style={{ fontSize: 22, fontWeight: 700, margin: "0 0 6px" }}>Design Tokens</h2>`);
2166
2214
  lines.push(` <p style={{ fontSize: 13, color: "#6b7280", margin: "0 0 32px" }}>Tailwind utilities used in <code style={{ background: "#f3f4f6", padding: "1px 6px", borderRadius: 4, fontSize: 12 }}>${componentName}</code> — resolved to project values.</p>`);
2167
2215
  lines.push(` {colorTokens.length > 0 && section("Color", (`);
@@ -2230,7 +2278,7 @@ function buildStoryFileContent(comp) {
2230
2278
  lines.push(` <p style={{ margin: "0 0 12px", fontSize: 11, fontWeight: 700, color: "#6b7280", textTransform: "uppercase" as any, letterSpacing: "0.08em" }}>{label}</p>`);
2231
2279
  lines.push(` );`);
2232
2280
  lines.push(` return (`);
2233
- lines.push(` <div style={{ padding: 40, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh" }}>`);
2281
+ lines.push(` <div style={{ padding: 40, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`);
2234
2282
  lines.push(` <div style={{ marginBottom: 32 }}>`);
2235
2283
  lines.push(` <h2 style={{ fontSize: 22, fontWeight: 700, margin: "0 0 6px" }}>Usage</h2>`);
2236
2284
  lines.push(` <p style={{ fontSize: 13, color: "#6b7280", margin: 0 }}>`);
@@ -2596,7 +2644,7 @@ function writeFoundationsStories(foundations) {
2596
2644
  "",
2597
2645
  "export const Default: Story = {",
2598
2646
  " render: () => (",
2599
- " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, maxWidth: 860, color: \"#111\", background: \"#fff\", minHeight: \"100vh\" }}>",
2647
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, color: \"#111\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\" }}>",
2600
2648
  " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\" }}>Typography</h2>",
2601
2649
  " <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 32px\" }}>Type scale, font families, and weights</p>",
2602
2650
  "",
@@ -2752,7 +2800,7 @@ function writeFoundationsStories(foundations) {
2752
2800
  "",
2753
2801
  "export const Default: Story = {",
2754
2802
  " render: () => (",
2755
- " <div style={{ display: \"flex\", gap: 24, flexWrap: \"wrap\", padding: 32, background: \"#fff\", minHeight: \"100vh\" }}>",
2803
+ " <div style={{ display: \"flex\", gap: 24, flexWrap: \"wrap\", padding: 32, background: \"#fff\", minHeight: \"100vh\", width: \"100%\" }}>",
2756
2804
  " {assets.length === 0 ? <p style={{ color: '#888', fontFamily: 'monospace' }}>No brand assets found — add images to public/ or client/public/</p> : assets.map((a, i) => (",
2757
2805
  " <div key={i} style={{ textAlign: \"center\", background: '#f8f8f8', borderRadius: 8, padding: 12, minWidth: 120 }}>",
2758
2806
  " <img src={a.url || (\"/\" + String(a.path || \"\").replace(/^public\\//, \"\"))} style={{ maxHeight: 100, maxWidth: 180, objectFit: \"contain\", display: 'block', margin: '0 auto' }} alt={a.name || \"\"} />",
@@ -2849,7 +2897,7 @@ function writeFoundationsStories(foundations) {
2849
2897
  " ];",
2850
2898
  " const TILE_SIZES = [16, 20, 24];",
2851
2899
  " return (",
2852
- " <div style={{ padding: 32, fontFamily: \"system-ui,sans-serif\", background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
2900
+ " <div style={{ padding: 32, fontFamily: \"system-ui,sans-serif\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
2853
2901
  " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\" }}>Icons</h2>",
2854
2902
  " <div style={{ marginBottom: 28 }}>",
2855
2903
  " <p style={{ margin: 0, marginBottom: 4, fontSize: 13, color: \"#6b7280\" }}>",
@@ -2992,7 +3040,7 @@ function writeFoundationsStories(foundations) {
2992
3040
  " );",
2993
3041
  " };",
2994
3042
  " return (",
2995
- " <div style={{ padding: 24, fontFamily: \"sans-serif\", maxWidth: 900, background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
3043
+ " <div style={{ padding: 24, fontFamily: \"sans-serif\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
2996
3044
  "",
2997
3045
  " {/* ── Breakpoints ── */}",
2998
3046
  " <section style={{ marginBottom: 40 }}>",
@@ -3148,7 +3196,7 @@ function writeFoundationsStories(foundations) {
3148
3196
  "",
3149
3197
  "export const Default: Story = {",
3150
3198
  " render: () => (",
3151
- " <div style={{ padding: 32, fontFamily: \"system-ui, sans-serif\", background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
3199
+ " <div style={{ padding: 32, fontFamily: \"system-ui, sans-serif\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
3152
3200
  " <h2 style={{ marginBottom: 8 }}>Used Button variants in app code</h2>",
3153
3201
  " <p style={{ marginBottom: 16, color: \"#888\", fontSize: 13 }}>Based on &lt;Button ... /&gt; usages in src/ (ignores lowercase &lt;button&gt;).</p>",
3154
3202
  " <table style={{ borderCollapse: \"collapse\", width: \"100%\", fontSize: 13 }}>",
@@ -3225,7 +3273,7 @@ function writeFoundationsStories(foundations) {
3225
3273
  "",
3226
3274
  "export const Default: Story = {",
3227
3275
  " render: () => (",
3228
- " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, maxWidth: 760, color: \"#111\", background: \"#fff\", minHeight: \"100vh\" }}>",
3276
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, color: \"#111\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\" }}>",
3229
3277
  " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\" }}>Spacing Scale</h2>",
3230
3278
  " <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 24px\" }}>Base unit: 0.25rem = 4px · built on an 8px grid system</p>",
3231
3279
  " {usedSpacing.length > 0 && (",
@@ -3335,7 +3383,7 @@ function writeFoundationsStories(foundations) {
3335
3383
  "",
3336
3384
  "export const Default: Story = {",
3337
3385
  " render: () => (",
3338
- " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
3386
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
3339
3387
  " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\", color: \"#111\" }}>Elevation & Shadows</h2>",
3340
3388
  " <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 32px\" }}>Depth is communicated through layered shadow levels</p>",
3341
3389
  " <h3 style={{ fontSize: 15, fontWeight: 600, margin: \"0 0 14px\", color: \"#374151\" }}>Shadow Scale</h3>",
@@ -3453,7 +3501,7 @@ function writeFoundationsStories(foundations) {
3453
3501
  "",
3454
3502
  "export const Default: Story = {",
3455
3503
  " render: () => (",
3456
- " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, maxWidth: 800, background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
3504
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
3457
3505
  " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\" }}>Border & Radius</h2>",
3458
3506
  " <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 32px\" }}>Corner radius scale — from sharp edges to fully rounded</p>",
3459
3507
  " {usedRadius.length > 0 && (",
@@ -3584,7 +3632,7 @@ function writeFoundationsStories(foundations) {
3584
3632
  " );",
3585
3633
  "",
3586
3634
  " return (",
3587
- " <div style={{ padding: 40, fontFamily: \"system-ui,sans-serif\", background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
3635
+ " <div style={{ padding: 40, fontFamily: \"system-ui,sans-serif\", background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
3588
3636
  " <h2 style={{ fontSize: 22, fontWeight: 700, margin: \"0 0 4px\" }}>Motion & Interaction</h2>",
3589
3637
  " <p style={{ fontSize: 13, color: \"#6b7280\", margin: \"0 0 40px\" }}>Consistent timing and easing for smooth, intentional motion.</p>",
3590
3638
  "",
@@ -3753,7 +3801,7 @@ function writeComponentSuggestionsStory(componentSuggestions) {
3753
3801
  "",
3754
3802
  "export const Default: Story = {",
3755
3803
  " render: () => (",
3756
- " <div style={{ padding: 32, fontFamily: 'system-ui, sans-serif', background: '#fff', minHeight: '100vh', color: '#111' }}>",
3804
+ " <div style={{ padding: 32, fontFamily: 'system-ui, sans-serif', background: '#fff', minHeight: '100vh', width: '100%', color: '#111' }}>",
3757
3805
  " <h2 style={{ marginBottom: 8, fontSize: 24, fontWeight: 700, color: '#1a202c' }}>Component Suggestions</h2>",
3758
3806
  " <p style={{ color: '#718096', marginBottom: 32, fontSize: 14 }}>Bu pattern'ler birden fazla dosyada tekrar ediyor. Her biri ayrı bir component olabilir. <strong>Copy Prompt</strong> butonuna tıklayıp Cursor veya Superflex'e yapıştır.</p>",
3759
3807
  " <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>",
@@ -3782,23 +3830,49 @@ function writeComponentInventoryStory(components, foundations) {
3782
3830
  "Forms and Input", "Status Indicators", "Navigation", "Overlays and Layering",
3783
3831
  "Loading", "Messaging", "Images and Icons", "Layout and Structure", "Text and Data Display",
3784
3832
  ];
3785
- const categorized = {}; // category → [{ name, group, tokenCount, propCount }]
3833
+ const categorized = {}; // category → [{ name, group, tokenCount, propCount, colorSwatches }]
3834
+ const foundColors = (foundations && foundations.colors) ? foundations.colors : {};
3786
3835
 
3787
3836
  for (const comp of components) {
3788
3837
  const g = comp.group || "Components";
3789
3838
  const gLower = g.toLowerCase();
3790
- const isShadcnGroup = gLower === "shadcn" || gLower === "ui";
3791
3839
  const tokens = Array.isArray(comp.tokens) ? comp.tokens : [];
3792
3840
  const props = Array.isArray(comp.props) ? comp.props : [];
3793
3841
  const semantic = classifyComponent(comp.name, tokens, props);
3794
3842
  const category = semantic || g;
3795
3843
 
3844
+ // Resolve color tokens to hex swatches from foundations.colors (max 6)
3845
+ const colorSwatches = tokens
3846
+ .filter(t => !/:/.test(t) &&
3847
+ /^(bg|text|border|ring|from|to|fill|stroke)-/.test(t) &&
3848
+ // Exclude border-side shorthands (b, t, l, r, x, y) and alignment/size text-* tokens
3849
+ !/^border-[btlrxy]$/.test(t) &&
3850
+ !/^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))
3851
+ .slice(0, 6)
3852
+ .map(token => {
3853
+ const m = token.match(/^(?:bg|text|border|ring|from|to|fill|stroke)-(.+)$/);
3854
+ let key = m ? m[1] : null;
3855
+ // Strip opacity modifier: "muted/20" → "muted", "primary/5" → "primary"
3856
+ if (key) key = key.replace(/\/\d+$/, "");
3857
+ const entry = key ? foundColors[key] : null;
3858
+ const hex = entry?.hex && /^#[0-9a-fA-F]{3,8}$/.test(entry.hex) ? entry.hex : null;
3859
+ return { token, hex };
3860
+ })
3861
+ .filter(s => s.hex); // only swatches with resolved hex
3862
+
3863
+ // Representative token fingerprint (spacing + typography, max 4, no color)
3864
+ const tokenFingerprint = tokens
3865
+ .filter(t => !/:/.test(t) && !/^(bg|text|border|ring|from|to|fill|stroke)-/.test(t))
3866
+ .slice(0, 4);
3867
+
3796
3868
  if (!categorized[category]) categorized[category] = [];
3797
3869
  categorized[category].push({
3798
- name: comp.name,
3799
- group: g,
3800
- tokenCount: tokens.filter(t => !/:/.test(t)).length,
3801
- propCount: props.length,
3870
+ name: comp.name,
3871
+ group: g,
3872
+ tokenCount: tokens.filter(t => !/:/.test(t)).length,
3873
+ propCount: props.length,
3874
+ colorSwatches,
3875
+ tokenFingerprint,
3802
3876
  });
3803
3877
  }
3804
3878
 
@@ -3828,13 +3902,13 @@ function writeComponentInventoryStory(components, foundations) {
3828
3902
  `export default meta;`,
3829
3903
  `type Story = StoryObj;`,
3830
3904
  ``,
3831
- `const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number }[] }[] = ${JSON.stringify(inventoryData)};`,
3905
+ `const inventoryData: { category: string; components: { name: string; group: string; tokenCount: number; propCount: number; colorSwatches: { token: string; hex: string }[]; tokenFingerprint: string[] }[] }[] = ${JSON.stringify(inventoryData)};`,
3832
3906
  `const totalComponents = ${totalComponents};`,
3833
3907
  `const uniqueTokens = ${uniqueTokens};`,
3834
3908
  ``,
3835
3909
  `export const Default: Story = {`,
3836
3910
  ` render: () => (`,
3837
- ` <div style={{ padding: 32, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh" }}>`,
3911
+ ` <div style={{ padding: 32, background: "#fff", fontFamily: "system-ui,sans-serif", color: "#111", minHeight: "100vh", width: "100%" }}>`,
3838
3912
  ` {/* Header */}`,
3839
3913
  ` <div style={{ marginBottom: 40 }}>`,
3840
3914
  ` <h2 style={{ fontSize: 28, fontWeight: 700, margin: "0 0 8px" }}>Component Inventory</h2>`,
@@ -3859,26 +3933,51 @@ function writeComponentInventoryStory(components, foundations) {
3859
3933
  ` <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: "#111" }}>{group.category}</h3>`,
3860
3934
  ` <span style={{ fontSize: 12, background: "#e0e7ff", color: "#3730a3", padding: "2px 9px", borderRadius: 12, fontWeight: 600 }}>{group.components.length}</span>`,
3861
3935
  ` </div>`,
3862
- ` <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(190px, 1fr))", gap: 10 }}>`,
3936
+ ` <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(210px, 1fr))", gap: 12 }}>`,
3863
3937
  ` {group.components.map(comp => (`,
3864
- ` <div key={comp.name} style={{ border: "1px solid #e5e7eb", borderRadius: 10, padding: "14px 16px", background: "#fafafa" }}>`,
3865
- ` <div style={{ fontWeight: 700, fontSize: 14, color: "#111", marginBottom: 8 }}>{comp.name}</div>`,
3866
- ` <div style={{ display: "flex", gap: 5, flexWrap: "wrap" as any }}>`,
3867
- ` {comp.propCount > 0 && (`,
3868
- ` <span style={{ fontSize: 10, background: "#f3f4f6", color: "#6b7280", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>`,
3869
- ` {comp.propCount} props`,
3870
- ` </span>`,
3871
- ` )}`,
3872
- ` {comp.tokenCount > 0 && (`,
3873
- ` <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>`,
3874
- ` {comp.tokenCount} tokens`,
3875
- ` </span>`,
3876
- ` )}`,
3877
- ` {comp.group && comp.group !== "shadcn" && comp.group !== "UI" && comp.group !== "Components" && (`,
3878
- ` <span style={{ fontSize: 10, background: "#f0fdf4", color: "#166534", padding: "2px 7px", borderRadius: 10, border: "1px solid #bbf7d0" }}>`,
3879
- ` {comp.group}`,
3880
- ` </span>`,
3938
+ ` <div key={comp.name} style={{ border: "1px solid #e5e7eb", borderRadius: 10, overflow: "hidden", background: "#fff", display: "flex", flexDirection: "column" as any }}>`,
3939
+ ` {/* Color swatch strip project-resolved colors from foundations */}`,
3940
+ ` {comp.colorSwatches.length > 0 ? (`,
3941
+ ` <div style={{ display: "flex", height: 8 }}>`,
3942
+ ` {comp.colorSwatches.map((s: any) => (`,
3943
+ ` <div key={s.token} title={s.token} style={{ flex: 1, background: s.hex }} />`,
3944
+ ` ))}`,
3945
+ ` </div>`,
3946
+ ` ) : (`,
3947
+ ` <div style={{ height: 4, background: "#f3f4f6" }} />`,
3948
+ ` )}`,
3949
+ ` <div style={{ padding: "12px 14px", flexGrow: 1 }}>`,
3950
+ ` <div style={{ fontWeight: 700, fontSize: 13, color: "#111", marginBottom: 6 }}>{comp.name}</div>`,
3951
+ ` {/* Token fingerprint pills */}`,
3952
+ ` {comp.tokenFingerprint.length > 0 && (`,
3953
+ ` <div style={{ display: "flex", gap: 4, flexWrap: "wrap" as any, marginBottom: 8 }}>`,
3954
+ ` {comp.tokenFingerprint.map((t: string) => (`,
3955
+ ` <code key={t} style={{ fontSize: 9, background: "#f3f4f6", color: "#6b7280", padding: "1px 5px", borderRadius: 4, border: "1px solid #e5e7eb" }}>{t}</code>`,
3956
+ ` ))}`,
3957
+ ` </div>`,
3881
3958
  ` )}`,
3959
+ ` <div style={{ display: "flex", gap: 5, flexWrap: "wrap" as any }}>`,
3960
+ ` {comp.propCount > 0 && (`,
3961
+ ` <span style={{ fontSize: 10, background: "#f3f4f6", color: "#6b7280", padding: "2px 7px", borderRadius: 10, border: "1px solid #e5e7eb" }}>`,
3962
+ ` {comp.propCount} props`,
3963
+ ` </span>`,
3964
+ ` )}`,
3965
+ ` {comp.tokenCount > 0 && (`,
3966
+ ` <span style={{ fontSize: 10, background: "#eff6ff", color: "#1d4ed8", padding: "2px 7px", borderRadius: 10, border: "1px solid #dbeafe" }}>`,
3967
+ ` {comp.tokenCount} tokens`,
3968
+ ` </span>`,
3969
+ ` )}`,
3970
+ ` {comp.colorSwatches.length > 0 && (`,
3971
+ ` <span style={{ fontSize: 10, background: "#fef9c3", color: "#713f12", padding: "2px 7px", borderRadius: 10, border: "1px solid #fde68a" }}>`,
3972
+ ` {comp.colorSwatches.length} colors`,
3973
+ ` </span>`,
3974
+ ` )}`,
3975
+ ` {comp.group && comp.group !== "shadcn" && comp.group !== "UI" && comp.group !== "Components" && (`,
3976
+ ` <span style={{ fontSize: 10, background: "#f0fdf4", color: "#166534", padding: "2px 7px", borderRadius: 10, border: "1px solid #bbf7d0" }}>`,
3977
+ ` {comp.group}`,
3978
+ ` </span>`,
3979
+ ` )}`,
3980
+ ` </div>`,
3882
3981
  ` </div>`,
3883
3982
  ` </div>`,
3884
3983
  ` ))}`,
@@ -3894,6 +3993,46 @@ function writeComponentInventoryStory(components, foundations) {
3894
3993
  console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "ComponentInventory.stories.tsx")));
3895
3994
  }
3896
3995
 
3996
+ /**
3997
+ * Patches .storybook/preview.tsx so that Foundations stories bypass app-specific
3998
+ * provider decorators (SidebarProvider, etc.) that constrain child width in flex layout.
3999
+ * Uses the context.title to detect "Foundations/" prefix at runtime.
4000
+ */
4001
+ function patchPreviewTsxFoundations() {
4002
+ const candidates = [
4003
+ path.join(PROJECT_ROOT, ".storybook", "preview.tsx"),
4004
+ path.join(PROJECT_ROOT, ".storybook", "preview.ts"),
4005
+ path.join(PROJECT_ROOT, ".storybook", "preview.jsx"),
4006
+ path.join(PROJECT_ROOT, ".storybook", "preview.js"),
4007
+ ];
4008
+ const previewPath = candidates.find(p => fs.existsSync(p));
4009
+ if (!previewPath) return;
4010
+
4011
+ let content;
4012
+ try { content = fs.readFileSync(previewPath, "utf-8"); } catch { return; }
4013
+
4014
+ // Already patched?
4015
+ if (content.includes('startsWith("Foundations")')) return;
4016
+
4017
+ // Match: const withProviders = (Story: any) => React.createElement(AnyProvider, null, React.createElement(Story));
4018
+ const re = /const withProviders = \(Story(?:: any)?\) => (React\.createElement\([^;]+\));/;
4019
+ const match = content.match(re);
4020
+ if (!match) return; // Pattern not found — nothing to patch
4021
+
4022
+ const providerExpr = match[1]; // e.g. React.createElement(SidebarProvider, null, React.createElement(Story))
4023
+ const patched =
4024
+ `const withProviders = (Story: any, context: any) => ` +
4025
+ `context?.title?.startsWith("Foundations") ? React.createElement(Story) : ${providerExpr};`;
4026
+
4027
+ content = content.replace(re, patched);
4028
+ try {
4029
+ fs.writeFileSync(previewPath, content, "utf-8");
4030
+ console.log("[VDS] Patched .storybook/preview.tsx — Foundations stories now bypass app providers");
4031
+ } catch (e) {
4032
+ console.warn("[VDS] Could not patch preview.tsx:", e.message);
4033
+ }
4034
+ }
4035
+
3897
4036
  function writeChangelogStory(changelog) {
3898
4037
  const foundationsDir = path.join(STORIES_DIR, "foundations");
3899
4038
  ensureDir(foundationsDir);
@@ -3910,7 +4049,7 @@ function writeChangelogStory(changelog) {
3910
4049
  "",
3911
4050
  "export const Default: Story = {",
3912
4051
  " render: () => (",
3913
- " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#fff\", minHeight: \"100vh\", color: \"#111\" }}>",
4052
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#fff\", minHeight: \"100vh\", width: \"100%\", color: \"#111\" }}>",
3914
4053
  " <h2>CHANGELOG</h2>",
3915
4054
  " {changelog.length === 0 ? <p>No changes recorded yet.</p> : changelog.map((entry) => (",
3916
4055
  " <div key={entry.version}>",
@@ -4168,6 +4307,7 @@ function main() {
4168
4307
  }
4169
4308
  writeComponentInventoryStory(components, foundations);
4170
4309
  writeChangelogStory(data.changelog);
4310
+ patchPreviewTsxFoundations();
4171
4311
  try {
4172
4312
  const fd = path.join(STORIES_DIR, "foundations");
4173
4313
  if (fs.existsSync(fd)) {