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
|
@@ -369,10 +369,29 @@ function compareComponents(prevList, newResults) {
|
|
|
369
369
|
return { added, removed, modified };
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
/**
|
|
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
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1090
|
-
const
|
|
1091
|
-
const
|
|
1092
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
2782
|
-
if (
|
|
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`;
|