vibe-design-system 2.5.41 → 2.5.42

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.5.41",
3
+ "version": "2.5.42",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -542,6 +542,7 @@ function extractComponentSuggestions() {
542
542
  const pageDirs = [
543
543
  { dir: PAGES_DIR, prefix: "src/pages/" },
544
544
  { dir: APP_DIR, prefix: "src/app/" },
545
+ { dir: COMPONENTS_DIR, prefix: "src/components/" },
545
546
  ];
546
547
  const allPageFiles = [];
547
548
  for (const { dir, prefix } of pageDirs) {
@@ -1195,6 +1196,64 @@ function extractFoundations() {
1195
1196
  };
1196
1197
  }
1197
1198
 
1199
+ function extractButtonUsage() {
1200
+ if (!fs.existsSync(SRC_DIR)) return null;
1201
+ const files = getAllTsxJsxInDir(SRC_DIR);
1202
+ if (!Array.isArray(files) || files.length === 0) return null;
1203
+
1204
+ const combos = new Map();
1205
+ let total = 0;
1206
+
1207
+ const extractPropLiteral = (attrs, name) => {
1208
+ if (!attrs) return null;
1209
+ // variant="primary"
1210
+ let re = new RegExp(name + '\\\\s*=\\\\s*["\']([^"\']+)["\']');
1211
+ let m = attrs.match(re);
1212
+ if (m) return m[1];
1213
+ // variant={"primary"}
1214
+ re = new RegExp(name + '\\\\s*=\\\\s*\\\\{\\\\s*["\']([^"\']+)["\']\\\\s*\\\\}');
1215
+ m = attrs.match(re);
1216
+ if (m) return m[1];
1217
+ return null;
1218
+ };
1219
+
1220
+ for (const rel of files) {
1221
+ const fullPath = path.join(SRC_DIR, rel);
1222
+ let content;
1223
+ try {
1224
+ content = fs.readFileSync(fullPath, "utf-8");
1225
+ } catch (_) {
1226
+ continue;
1227
+ }
1228
+ const re = /<Button\\b([^>]*)>/g;
1229
+ let m;
1230
+ while ((m = re.exec(content)) !== null) {
1231
+ total += 1;
1232
+ const attrs = m[1] || "";
1233
+ const variant = extractPropLiteral(attrs, "variant") || "default";
1234
+ const size = extractPropLiteral(attrs, "size") || "default";
1235
+ const asChild = /\\basChild\\b/.test(attrs);
1236
+ const key = `${variant}__${size}__${asChild ? "1" : "0"}`;
1237
+ if (!combos.has(key)) {
1238
+ combos.set(key, {
1239
+ key,
1240
+ variant,
1241
+ size,
1242
+ asChild,
1243
+ count: 0,
1244
+ });
1245
+ }
1246
+ combos.get(key).count += 1;
1247
+ }
1248
+ }
1249
+
1250
+ if (total === 0 || combos.size === 0) return null;
1251
+ return {
1252
+ total,
1253
+ combos: Array.from(combos.values()),
1254
+ };
1255
+ }
1256
+
1198
1257
  function scan() {
1199
1258
  if (!fs.existsSync(COMPONENTS_DIR)) {
1200
1259
  console.error(CLI_LOCALES[CLI_LOCALE].componentsNotFound);
@@ -1242,6 +1301,10 @@ function scan() {
1242
1301
  const foundations = extractFoundations();
1243
1302
  foundations.icons = extractLucideIconsUsed(SRC_DIR);
1244
1303
  foundations.brand = { assets: extractBrandAssets() };
1304
+ const buttonUsage = extractButtonUsage();
1305
+ if (buttonUsage) {
1306
+ foundations.buttonUsage = buttonUsage;
1307
+ }
1245
1308
  const componentSuggestions = extractComponentSuggestions();
1246
1309
  const unreleasedSectionCandidates = extractUnreleasedSectionCandidates();
1247
1310
  const output = {
@@ -478,7 +478,7 @@ const REACTNODE_PLACEHOLDER_TEXT = {
478
478
  children: "Example content",
479
479
  };
480
480
 
481
- /** Component adına göre ekstra args (toString/undefined hatalarını önlemek için; her prop güvenli değer alır). */
481
+ /** Component adına göre ekstra args (toString/dateRange/toFixed hatalarını önlemek için; her prop güvenli değer alır). */
482
482
  const COMPONENT_EXTRA_ARGS = {
483
483
  TaskEstimateInput: [
484
484
  " task: { id: \"1\", title: \"Example\", estimate: 0, description: \"\" },",
@@ -491,6 +491,22 @@ const COMPONENT_EXTRA_ARGS = {
491
491
  " step: 1,",
492
492
  " taskId: \"1\",",
493
493
  ],
494
+ TimeFilterPanel: [
495
+ " filter: { dateRange: {}, userIds: [], tags: [], billable: \"all\" },",
496
+ " onFilterChange: () => {},",
497
+ " onSaveFilter: () => {},",
498
+ " onDeleteFilter: () => {},",
499
+ " filteredCount: 0,",
500
+ " savedFilters: [],",
501
+ ],
502
+ TimeStats: [
503
+ " totalLogged: 0,",
504
+ " totalBillable: 0,",
505
+ " totalNonBillable: 0,",
506
+ " totalBilled: 0,",
507
+ " totalEstimated: 0,",
508
+ " projectTotalEstimated: 0,",
509
+ ],
494
510
  };
495
511
 
496
512
  /** Render'da args'a uygulanacak fallback (useState(estimate.toString()) gibi kullanımlar için; args/Controls undefined yapsa bile). */
@@ -501,6 +517,8 @@ const RENDER_ARGS_FALLBACKS = {
501
517
  /** Kalıcı çözüm: Storybook Docs/Controls args'ı geçmezse bile component'a güvenli props veren wrapper. Önce safeDefaults uygulanır, sonra gelen props. */
502
518
  const SAFE_WRAPPER_DEFAULTS = {
503
519
  TaskEstimateInput: `{ estimate: 0, onUpdate: () => {}, value: 0, task: { id: "1", title: "Example", estimate: 0 }, compact: false }`,
520
+ TimeFilterPanel: `{ filter: { dateRange: {}, userIds: [], tags: [], billable: "all" }, onFilterChange: () => {}, onSaveFilter: () => {}, onDeleteFilter: () => {}, filteredCount: 0, savedFilters: [] }`,
521
+ TimeStats: `{ totalLogged: 0, totalBillable: 0, totalNonBillable: 0, totalBilled: 0, totalEstimated: 0, projectTotalEstimated: 0 }`,
504
522
  };
505
523
 
506
524
  /** Recursive list of .tsx/.jsx file paths under dir (relative to dir). Index.tsx / index.tsx first for deterministic "first usage". */
@@ -1295,6 +1313,58 @@ function writeFoundationsStories(foundations) {
1295
1313
  fs.writeFileSync(path.join(foundationsDir, "Icons.stories.tsx"), iconsContent, "utf-8");
1296
1314
  console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Icons.stories.tsx")));
1297
1315
  }
1316
+
1317
+ const buttonUsage = foundations?.buttonUsage;
1318
+ if (buttonUsage && Array.isArray(buttonUsage.combos) && buttonUsage.combos.length > 0) {
1319
+ const combos = buttonUsage.combos.map((c) => ({
1320
+ key: c.key,
1321
+ variant: c.variant,
1322
+ size: c.size,
1323
+ asChild: !!c.asChild,
1324
+ count: c.count ?? 0,
1325
+ }));
1326
+ const buttonsContent =
1327
+ [
1328
+ "import type { Meta, StoryObj } from \"@storybook/react\";",
1329
+ "",
1330
+ "const meta = { title: \"Foundations/Button Usage\" } satisfies Meta;",
1331
+ "export default meta;",
1332
+ "type Story = StoryObj;",
1333
+ "",
1334
+ `const combos = ${JSON.stringify(combos)};`,
1335
+ "",
1336
+ "export const Default: Story = {",
1337
+ " render: () => (",
1338
+ " <div style={{ padding: 24, fontFamily: \"system-ui, sans-serif\" }}>",
1339
+ " <h2 style={{ marginBottom: 8 }}>Used Button variants in app code</h2>",
1340
+ " <p style={{ marginBottom: 16, color: \"#888\", fontSize: 13 }}>Based on &lt;Button ... /&gt; usages in src/ (ignores lowercase &lt;button&gt;).</p>",
1341
+ " <table style={{ borderCollapse: \"collapse\", width: \"100%\", fontSize: 13 }}>",
1342
+ " <thead>",
1343
+ " <tr>",
1344
+ " <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Variant</th>",
1345
+ " <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Size</th>",
1346
+ " <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>asChild</th>",
1347
+ " <th style={{ textAlign: \"right\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Count</th>",
1348
+ " </tr>",
1349
+ " </thead>",
1350
+ " <tbody>",
1351
+ " {combos.map((c) => (",
1352
+ " <tr key={c.key}>",
1353
+ " <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.variant}</td>",
1354
+ " <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.size}</td>",
1355
+ " <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.asChild ? \"yes\" : \"no\"}</td>",
1356
+ " <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\", textAlign: \"right\" }}>{c.count}</td>",
1357
+ " </tr>",
1358
+ " ))}",
1359
+ " </tbody>",
1360
+ " </table>",
1361
+ " </div>",
1362
+ " ),",
1363
+ "};",
1364
+ ].join("\n");
1365
+ fs.writeFileSync(path.join(foundationsDir, "Buttons.stories.tsx"), buttonsContent, "utf-8");
1366
+ console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Buttons.stories.tsx")));
1367
+ }
1298
1368
  }
1299
1369
 
1300
1370
  function writeComponentSuggestionsStory(componentSuggestions) {
@@ -1323,7 +1393,7 @@ function writeComponentSuggestionsStory(componentSuggestions) {
1323
1393
  " render: () => (",
1324
1394
  " <div style={{ padding: 24, fontFamily: \"system-ui, sans-serif\" }}>",
1325
1395
  " <h2 style={{ marginBottom: 16 }}>Visual section candidates (component suggestions)</h2>",
1326
- " <p style={{ color: \"#888\", marginBottom: 24 }}>From src/pages and src/app: own className, 2+ child elements, pattern repeated 2+ times. Not yet in src/components. Consider extracting as components.</p>",
1396
+ " <p style={{ color: \"#888\", marginBottom: 24 }}>From src/pages, src/app ve src/components: kendi className kümeleri, 2+ child element, 2+ tekrar. Aynı yapı birden fazla dosyada geçiyorsa ayrı component olarak düşünün.</p>",
1327
1397
  " <div style={{ display: \"flex\", flexDirection: \"column\", gap: 24 }}>",
1328
1398
  " {suggestions.map((s, i) => (",
1329
1399
  " <div key={i} style={{ border: \"1px solid #333\", borderRadius: 8, padding: 16, background: \"#111\" }}>",