vibe-design-system 2.8.24 → 2.8.26

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.24",
3
+ "version": "2.8.26",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "homepage": "https://vibedesign.tech",
6
6
  "repository": {
@@ -369,10 +369,29 @@ function compareComponents(prevList, newResults) {
369
369
  return { added, removed, modified };
370
370
  }
371
371
 
372
- /** Path-based classification: ui/ → shadcn, components root → Components, else Uncategorized. */
372
+ /**
373
+ * Path-based classification: derive Storybook group from the first folder segment.
374
+ * Examples:
375
+ * ui/button.tsx → group: "UI"
376
+ * circles/CircleCard.tsx → group: "Circles"
377
+ * time/TimeDashboard.tsx → group: "Time"
378
+ * time-resources/planning/… → group: "Time Resources"
379
+ * settings/UserProfile.tsx → group: "Settings"
380
+ */
373
381
  function classifyByPath(rel) {
374
- if (rel.startsWith("ui/")) return { group: "shadcn", category: "UI" };
375
- return { group: "Components", category: "Components" };
382
+ const normalized = rel.replace(/\\/g, "/");
383
+ const firstSegment = normalized.split("/")[0] || "";
384
+
385
+ // ui/ → always "UI" (shadcn primitives)
386
+ if (firstSegment === "ui") return { group: "UI", category: null };
387
+
388
+ // Convert kebab-case folder to Title Case (time-resources → Time Resources)
389
+ const group = firstSegment
390
+ .split("-")
391
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
392
+ .join(" ") || "Components";
393
+
394
+ return { group, category: null };
376
395
  }
377
396
 
378
397
  /**
@@ -382,16 +401,22 @@ function classifyByPath(rel) {
382
401
  * feature → medium, contains state / multiple sub-parts
383
402
  * page → full page/view — reference only, not for direct reuse
384
403
  */
404
+ // Semantic filename keywords that indicate a view-level (feature) component
405
+ // even if the file is small (data fetched via context/hooks, not passed as props)
406
+ const FEATURE_FILENAME_KEYWORDS = /Dashboard|Profile|Hub|Overview|Planning|Timesheet|Pipeline|Workload|Schedule|Portfolio|Chart|Board|Reports?|Analytics|Settings$/i;
407
+
385
408
  function inferTier(rel, content) {
386
409
  const normalized = rel.replace(/\\/g, "/");
387
410
  if (normalized.startsWith("ui/") || normalized.includes("/ui/")) return "primitive";
388
411
 
412
+ const filename = normalized.split("/").pop()?.replace(/\.(tsx|jsx|ts|js)$/, "") || "";
389
413
  const lines = content.split("\n").length;
390
414
  // Count relative imports (../ ./) AND path-alias imports (@/components/) as local dependencies
391
415
  const localImports = (content.match(/from\s+['"](?:\.\.?\/?|@\/components\/)/g) || []).length;
392
416
 
393
417
  if (lines >= 400 || localImports >= 12) return "page";
394
- if (lines >= 200 || localImports >= 7) return "feature";
418
+ // Semantic keyword: treat as feature even if small (fetches data via context/hooks)
419
+ if (lines >= 200 || localImports >= 7 || FEATURE_FILENAME_KEYWORDS.test(filename)) return "feature";
395
420
  return "component";
396
421
  }
397
422
 
@@ -742,6 +742,12 @@ function buildDefaultArgsForRequiredProps(props, usageFromPages = null, componen
742
742
  continue;
743
743
  }
744
744
  if (!required) continue;
745
+ // Function/callback types must be caught before name-based type checks (e.g. "Task" in "(task: Task) => void")
746
+ if (isFunctionType(type)) {
747
+ argLines.push(` ${name}: () => {},`);
748
+ added.add(name);
749
+ continue;
750
+ }
745
751
  if (/LucideIcon|lucide-react/.test(type)) {
746
752
  const iconName = LUCIDE_ICON_DEFAULT;
747
753
  lucideImports.add(iconName);
@@ -1071,6 +1077,52 @@ function buildRecipeStoryContent(comp, componentName, importPath, title, source,
1071
1077
  return lines.join("\n");
1072
1078
  }
1073
1079
 
1080
+ /**
1081
+ * Map a single extracted TypeScript prop to a Storybook argType entry string.
1082
+ * Returns something like: `{ control: "text", description: "string" }`
1083
+ */
1084
+ function buildArgTypeEntry(prop) {
1085
+ const t = (prop.type || "").trim();
1086
+ const name = prop.name;
1087
+
1088
+ // Function / callback — disable control widget, show as action
1089
+ if (/=>\s*(void|any|never|Promise)/.test(t) || /^\([^)]*\)\s*=>/.test(t)) {
1090
+ const safe = t.replace(/"/g, '\\"').slice(0, 60);
1091
+ return `{ action: "${name}", control: false, description: "${safe}" }`;
1092
+ }
1093
+ // Boolean
1094
+ if (/^boolean/.test(t)) {
1095
+ return `{ control: "boolean", description: "boolean" }`;
1096
+ }
1097
+ // Number
1098
+ if (/^number/.test(t)) {
1099
+ return `{ control: "number", description: "number" }`;
1100
+ }
1101
+ // String union literals: "a" | "b" | "c" → select control
1102
+ if (/^"[^"]+"(\s*\|\s*"[^"]+")+/.test(t)) {
1103
+ const opts = (t.match(/"([^"]+)"/g) || []).map(s => s.replace(/"/g, ""));
1104
+ return `{ control: "select", options: ${JSON.stringify(opts)}, description: ${JSON.stringify(t)} }`;
1105
+ }
1106
+ // Plain string (including union with null/undefined)
1107
+ if (/^string/.test(t) || t === "string | null" || t === "string | undefined" || t === "string | null | undefined") {
1108
+ return `{ control: "text", description: "string" }`;
1109
+ }
1110
+ // ReactNode
1111
+ if (/ReactNode/.test(t)) {
1112
+ return `{ control: "text", description: "ReactNode" }`;
1113
+ }
1114
+ // Arrays → object control
1115
+ if (t.endsWith("[]") || /^Array</.test(t)) {
1116
+ return `{ control: "object", description: ${JSON.stringify(t.slice(0, 60))} }`;
1117
+ }
1118
+ // Inline object / any
1119
+ if (t === "any" || t.startsWith("{")) {
1120
+ return `{ control: "object", description: ${JSON.stringify(t.slice(0, 60))} }`;
1121
+ }
1122
+ // Imported / custom named types (Circle, Task, etc.) — object control so user can inspect
1123
+ return `{ control: "object", description: ${JSON.stringify(t.slice(0, 60))} }`;
1124
+ }
1125
+
1074
1126
  function buildStoryFileContent(comp) {
1075
1127
  const componentName = toSafeComponentName(comp.name, comp.file);
1076
1128
  const fileNoExt = comp.file.replace(/\.(tsx|jsx)$/, "");
@@ -1086,10 +1138,11 @@ function buildStoryFileContent(comp) {
1086
1138
  }
1087
1139
  if (!importPath.startsWith(".")) importPath = "./" + importPath;
1088
1140
 
1089
- const group = comp.group || "Components";
1090
- const category = comp.category || null;
1091
- const titleParts = [group, category, componentName].filter(Boolean);
1092
- const title = titleParts.join("/");
1141
+ // Normalize legacy "shadcn" group "UI", everything else kept as-is
1142
+ const rawGroup = comp.group || "Components";
1143
+ const group = rawGroup === "shadcn" ? "UI" : rawGroup;
1144
+ // Title: "Module/ComponentName" (category intentionally dropped — folder is the context)
1145
+ const title = `${group}/${componentName}`;
1093
1146
 
1094
1147
  const props = Array.isArray(comp.props) ? comp.props : [];
1095
1148
  const variantProp = props.find((p) => p.name === "variant");
@@ -1173,13 +1226,27 @@ function buildStoryFileContent(comp) {
1173
1226
  lines.push(` title: ${JSON.stringify(title)},`);
1174
1227
  lines.push(` component: ComponentRef,`);
1175
1228
  lines.push(` tags: ["autodocs"],`);
1229
+
1230
+ // Build argTypes from extracted TypeScript props + icon-specific overrides
1231
+ const argTypeEntries = [];
1232
+ for (const p of effectiveProps) {
1233
+ if (!p || !p.name) continue;
1234
+ if (p.name === "children") continue; // Storybook infers children from JSX
1235
+ if (iconPropNames && iconPropNames.includes(p.name)) continue; // handled below
1236
+ const entry = buildArgTypeEntry(p);
1237
+ argTypeEntries.push(` ${p.name}: ${entry},`);
1238
+ }
1176
1239
  if (iconPropNames && iconPropNames.length > 0) {
1177
- lines.push(` argTypes: {`);
1178
1240
  for (const name of iconPropNames) {
1179
- lines.push(` ${name}: { control: false },`);
1241
+ argTypeEntries.push(` ${name}: { control: false },`);
1180
1242
  }
1243
+ }
1244
+ if (argTypeEntries.length > 0) {
1245
+ lines.push(` argTypes: {`);
1246
+ for (const entry of argTypeEntries) lines.push(entry);
1181
1247
  lines.push(` },`);
1182
1248
  }
1249
+
1183
1250
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
1184
1251
  lines.push("");
1185
1252
  lines.push(`export default meta;`);
@@ -2778,9 +2845,8 @@ function main() {
2778
2845
 
2779
2846
  for (const comp of components) {
2780
2847
  const componentName = toSafeComponentName(comp.name, comp.file);
2781
- // Şu ana kadar shadcn grubunu atlıyorduk; Button için istisna tanıyalım
2782
- if ((comp.group === "shadcn" || comp.group === "Uncategorized") && componentName !== "Button") continue;
2783
- if (comp.group !== "Components" && componentName !== "Button") continue;
2848
+ // Skip only truly unclassified components; all module groups (UI, Circles, Time, …) are welcome
2849
+ if (comp.group === "Uncategorized") continue;
2784
2850
  if (onlyName && componentName !== onlyName) continue;
2785
2851
 
2786
2852
  const storyFileName = `${componentName}.stories.tsx`;