vibe-design-system 2.8.56 → 2.8.59

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/bin/init.js CHANGED
@@ -15,7 +15,7 @@
15
15
  import fs from "fs";
16
16
  import path from "path";
17
17
  import { fileURLToPath } from "url";
18
- import { spawnSync } from "child_process";
18
+ import { spawnSync, spawn } from "child_process";
19
19
 
20
20
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
21
  const INSTALLER_ROOT = path.join(__dirname, "..");
@@ -278,8 +278,9 @@ export default config;
278
278
  // vite (default) & remix
279
279
  // For fullstack projects (client/src, frontend/src, etc.), add staticDirs for public assets
280
280
  const frontendDir = srcPrefix.split("/")[0]; // e.g. "client" from "client/src"
281
+ // Map public/ to "/" so components can load /images/... /figmaAssets/... etc. without prefix
281
282
  const staticDirsLine = srcPrefix !== "src"
282
- ? `\n staticDirs: [{ from: "../${frontendDir}/public", to: "/${frontendDir}" }],`
283
+ ? `\n staticDirs: ["../${frontendDir}/public"],`
283
284
  : "";
284
285
 
285
286
  return `import type { StorybookConfig } from "@storybook/react-vite";
@@ -610,12 +611,11 @@ function ensureVdsConfig(projectRoot, srcPrefix) {
610
611
  const configPath = path.join(projectRoot, "vds.config.js");
611
612
  if (fs.existsSync(configPath)) return;
612
613
 
613
- // Detect shadcn/ui: if components/ui/ directory exists, add includeGroups by default
614
- const uiDir = path.join(projectRoot, srcPrefix || "src", "components", "ui");
615
- const hasShadcn = fs.existsSync(uiDir);
616
- const includeGroupsLine = hasShadcn
617
- ? ` includeGroups: ["shadcn", "UI", "ui"], // Auto-detected shadcn/ui: generate stories for UI components\n`
618
- : ` // includeGroups: ["shadcn", "UI"], // Uncomment to generate stories for shadcn/ui primitives\n`;
614
+ // includeGroups is intentionally OFF by default.
615
+ // shadcn/ui base components (Button, Avatar, Badge…) are generic library primitives —
616
+ // most projects only want their custom sections and page components in Storybook.
617
+ // To include shadcn/ui components: add includeGroups: ["shadcn", "UI", "ui"] to vds.config.js.
618
+ const includeGroupsLine = ` // includeGroups: ["shadcn", "UI"], // Uncomment to add shadcn/ui primitive stories\n`;
619
619
 
620
620
  const content = `/**
621
621
  * VDS Configuration — auto-created by VDS installer
@@ -715,8 +715,18 @@ runStorybookAdapt(projectRoot);
715
715
 
716
716
  // ADIM 8
717
717
  console.log("\n✅ VDS kuruldu!");
718
- console.log("→ npm run storybook — design system'ı aç (localhost:6006)");
719
- console.log("→ npm run storybook:bg — Storybook'u arka planda başlat (Cursor kapansa bile çalışır)");
720
718
  console.log("→ npm run vds:stories — tarama + story + provider (tek komut)");
721
719
  console.log("→ npm run vds:watch — otomatik güncelleme (watch modu)");
722
720
  console.log("\nNot: Storybook başlarken (Node 24+) DEP0190 uyarısı çıkarsa Storybook kaynaklıdır, güvenle yok sayabilirsiniz.\n");
721
+
722
+ // ADIM 10 — Storybook'u otomatik başlat
723
+ console.log("🚀 Storybook başlatılıyor → http://localhost:6006\n");
724
+ const sbProc = spawn("npm", ["run", "storybook"], {
725
+ stdio: "inherit",
726
+ cwd: projectRoot,
727
+ shell: process.platform === "win32",
728
+ });
729
+ sbProc.on("error", (err) => {
730
+ console.error("[VDS] Storybook başlatılamadı:", err.message);
731
+ console.log("→ Manuel başlatmak için: npm run storybook");
732
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.8.56",
3
+ "version": "2.8.59",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "homepage": "https://vibedesign.tech",
6
6
  "repository": {
@@ -246,10 +246,9 @@ const RECIPES = {
246
246
  )`,
247
247
  },
248
248
  Avatar: {
249
- imports: ["AvatarImage", "AvatarFallback"],
249
+ imports: ["AvatarFallback"],
250
250
  render: `(args) => (
251
251
  <ComponentRef {...args}>
252
- <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
253
252
  <AvatarFallback>JD</AvatarFallback>
254
253
  </ComponentRef>
255
254
  )`,
@@ -881,6 +880,58 @@ const COMPONENT_EXTRA_ARGS = {
881
880
  ...(VDS_CONFIG.componentExtraArgs ?? {}),
882
881
  };
883
882
 
883
+ // ---------------------------------------------------------------------------
884
+ // USAGE SCANNER — reads project sources to find actual component usage patterns
885
+ // e.g. <Button className="bg-[#5409da] text-white rounded-[10px]"> → generates
886
+ // a "ProjectPrimary" story with the real project styling instead of generic CVA variants.
887
+ // ---------------------------------------------------------------------------
888
+ const _SCAN_IGNORE_DIRS = new Set(["node_modules", ".next", "dist", "build", ".git", ".storybook"]);
889
+
890
+ function scanComponentUsages(componentName, projectRoot) {
891
+ const uiPathFrag = path.join("components", "ui") + path.sep;
892
+ const classNameCounts = new Map(); // className → count
893
+
894
+ function walk(dir) {
895
+ let entries;
896
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
897
+ for (const e of entries) {
898
+ if (_SCAN_IGNORE_DIRS.has(e.name)) continue;
899
+ const full = path.join(dir, e.name);
900
+ if (e.isDirectory()) { walk(full); continue; }
901
+ if (!/\.(tsx|jsx|ts|js)$/.test(e.name)) continue;
902
+ if (full.includes(uiPathFrag)) continue; // Skip ui/ directory itself
903
+ if (e.name.includes(".stories.")) continue; // Skip story files
904
+ let src;
905
+ try { src = fs.readFileSync(full, "utf-8"); } catch { continue; }
906
+ if (!src.includes(`<${componentName}`)) continue;
907
+
908
+ // Match <ComponentName ... > or <ComponentName ... />
909
+ const re = new RegExp(`<${componentName}(?:\\s+([^>]*?))?(?:\\s*/>|\\s*>)`, "g");
910
+ let m;
911
+ while ((m = re.exec(src)) !== null) {
912
+ const propsStr = m[1] || "";
913
+ // Extract className="..." — single or double quotes
914
+ const classM = propsStr.match(/className=["']([^"']+)["']/);
915
+ if (!classM) continue;
916
+ const cn = classM[1].trim();
917
+ if (!cn) continue;
918
+ // Only include usages that have project-specific (non-CVA) styling
919
+ // i.e. hex colors, arbitrary Tailwind values, custom font families
920
+ const hasCustomStyling = /bg-\[|text-\[|rounded-\[|#[0-9a-fA-F]{3,8}|\[font-family:/.test(cn);
921
+ if (!hasCustomStyling) continue;
922
+ classNameCounts.set(cn, (classNameCounts.get(cn) || 0) + 1);
923
+ }
924
+ }
925
+ }
926
+
927
+ walk(projectRoot);
928
+
929
+ return [...classNameCounts.entries()]
930
+ .sort((a, b) => b[1] - a[1])
931
+ .slice(0, 4)
932
+ .map(([className, count]) => ({ className, count }));
933
+ }
934
+
884
935
  /** Render'da args'a uygulanacak fallback (useState(estimate.toString()) gibi kullanımlar için; args/Controls undefined yapsa bile). */
885
936
  const RENDER_ARGS_FALLBACKS = {
886
937
  TaskEstimateInput: ", estimate: (args && args.estimate) ?? 0, value: (args && args.value) ?? 0, task: (args && args.task) ?? { id: \"1\", title: \"Example\", estimate: 0 }",
@@ -1867,6 +1918,28 @@ function buildStoryFileContent(comp) {
1867
1918
  lines.push(`};`);
1868
1919
  }
1869
1920
 
1921
+ // --- Project-specific usage stories ---
1922
+ // Scan actual component usages in project source. If custom className overrides found
1923
+ // (hex colors, arbitrary Tailwind values), append stories that show the REAL project styling.
1924
+ const usagePatterns = scanComponentUsages(componentName, PROJECT_ROOT);
1925
+ if (usagePatterns.length > 0) {
1926
+ const usageStoryNames = ["ProjectPrimary", "ProjectSecondary", "ProjectTertiary", "ProjectQuaternary"];
1927
+ lines.push("");
1928
+ lines.push(`// — Project-specific stories auto-detected from actual ${componentName} usage in this codebase.`);
1929
+ lines.push(`// These reflect how ${componentName} is styled in the project, not generic variants.`);
1930
+ for (let i = 0; i < usagePatterns.length; i++) {
1931
+ const { className } = usagePatterns[i];
1932
+ const sName = usageStoryNames[i];
1933
+ lines.push(`export const ${sName}: Story = {`);
1934
+ lines.push(` render: () => (`);
1935
+ lines.push(` <ComponentRef className="${className}">`);
1936
+ lines.push(` ${componentName}`);
1937
+ lines.push(` </ComponentRef>`);
1938
+ lines.push(` ),`);
1939
+ lines.push(`};`);
1940
+ }
1941
+ }
1942
+
1870
1943
  return lines.join("\n");
1871
1944
  }
1872
1945
 
@@ -2292,8 +2365,47 @@ function writeFoundationsStories(foundations) {
2292
2365
  console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Typography.stories.tsx")));
2293
2366
  }
2294
2367
 
2368
+ // Collect brand assets: prefer scan data, fallback to scanning public/ directories
2295
2369
  const brandAssets = foundations?.brand?.assets;
2296
- const assets = Array.isArray(brandAssets) ? brandAssets : [];
2370
+ let assets = Array.isArray(brandAssets) ? brandAssets : [];
2371
+
2372
+ // Also scan public/ dirs directly — scan.mjs may miss most project image assets
2373
+ {
2374
+ const publicDirs = [
2375
+ path.join(PROJECT_ROOT, "public"),
2376
+ path.join(PROJECT_ROOT, "client", "public"),
2377
+ path.join(PROJECT_ROOT, "frontend", "public"),
2378
+ path.join(PROJECT_ROOT, "src", "assets"),
2379
+ path.join(PROJECT_ROOT, "client", "src", "assets"),
2380
+ ];
2381
+ const IMAGE_EXT = /\.(svg|png|jpg|jpeg|webp|gif)$/i;
2382
+ const existingUrls = new Set(assets.map((a) => a.url || a.path || ""));
2383
+
2384
+ for (const pubDir of publicDirs) {
2385
+ if (!fs.existsSync(pubDir)) continue;
2386
+ const newAssets = [];
2387
+ const scanDir = (dir, baseUrl) => {
2388
+ let entries;
2389
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
2390
+ for (const e of entries) {
2391
+ const urlPath = baseUrl + "/" + e.name;
2392
+ if (e.isDirectory()) {
2393
+ scanDir(path.join(dir, e.name), urlPath);
2394
+ } else if (IMAGE_EXT.test(e.name) && !existingUrls.has(urlPath)) {
2395
+ newAssets.push({ name: e.name.replace(/\.[^.]+$/, ""), url: urlPath });
2396
+ }
2397
+ }
2398
+ };
2399
+ scanDir(pubDir, "");
2400
+ if (newAssets.length > 0) {
2401
+ assets = [...assets, ...newAssets];
2402
+ break; // Use first matching public dir only
2403
+ }
2404
+ }
2405
+ // Limit to 40 assets to keep story file size reasonable
2406
+ assets = assets.slice(0, 40);
2407
+ }
2408
+
2297
2409
  const brandContent =
2298
2410
  [
2299
2411
  "import type { Meta, StoryObj } from \"@storybook/react\";",
@@ -2307,10 +2419,10 @@ function writeFoundationsStories(foundations) {
2307
2419
  "export const Default: Story = {",
2308
2420
  " render: () => (",
2309
2421
  " <div style={{ display: \"flex\", gap: 24, flexWrap: \"wrap\", padding: 24 }}>",
2310
- " {assets.length === 0 ? <p>No brand assets found.</p> : assets.map((a, i) => (",
2311
- " <div key={i} style={{ textAlign: \"center\" }}>",
2312
- " <img src={\"/\" + String(a.path || \"\").replace(\"public/\", \"\")} style={{ maxHeight: 100, maxWidth: 200, objectFit: \"contain\" }} alt={a.name || \"\"} />",
2313
- " <div style={{ fontSize: 11, marginTop: 8, color: \"#666\" }}>{a.name || a.type || \"asset\"}</div>",
2422
+ " {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) => (",
2423
+ " <div key={i} style={{ textAlign: \"center\", background: '#f8f8f8', borderRadius: 8, padding: 12, minWidth: 120 }}>",
2424
+ " <img src={a.url || (\"/\" + String(a.path || \"\").replace(/^public\\//, \"\"))} style={{ maxHeight: 100, maxWidth: 180, objectFit: \"contain\", display: 'block', margin: '0 auto' }} alt={a.name || \"\"} />",
2425
+ " <div style={{ fontSize: 11, marginTop: 8, color: \"#666\", wordBreak: 'break-all' }}>{a.name || a.type || \"asset\"}</div>",
2314
2426
  " </div>",
2315
2427
  " ))}",
2316
2428
  " </div>",
@@ -3496,6 +3608,12 @@ function main() {
3496
3608
  if (!onlyName) {
3497
3609
  try {
3498
3610
  const existing = fs.readdirSync(STORIES_DIR);
3611
+ // Remove macOS copy artifacts: "Button.stories 2.tsx", "Badge.stories 3.tsx", etc.
3612
+ for (const name of existing) {
3613
+ if (/\.stories \d+\.(tsx|ts|jsx|js)$/.test(name)) {
3614
+ try { fs.unlinkSync(path.join(STORIES_DIR, name)); } catch { /* ignore */ }
3615
+ }
3616
+ }
3499
3617
  for (const name of existing) {
3500
3618
  if (name === "foundations") continue;
3501
3619
  if (