sonance-brand-mcp 1.3.1 → 1.3.2

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.
Files changed (136) hide show
  1. package/dist/assets/api/sonance-analyze/route.ts +1116 -0
  2. package/dist/assets/api/sonance-assets/route.ts +113 -0
  3. package/dist/assets/api/sonance-components/route.ts +41 -0
  4. package/dist/assets/api/sonance-inject-id/route.ts +363 -0
  5. package/dist/assets/api/sonance-save-logo/route.ts +426 -0
  6. package/dist/assets/api/sonance-theme/route.ts +106 -0
  7. package/dist/assets/brand-system.ts +1265 -0
  8. package/dist/assets/components/accordion.stories.tsx +26 -26
  9. package/dist/assets/components/accordion.tsx +3 -3
  10. package/dist/assets/components/alert-dialog.stories.tsx +7 -7
  11. package/dist/assets/components/alert-dialog.tsx +2 -1
  12. package/dist/assets/components/alert.stories.tsx +3 -3
  13. package/dist/assets/components/alert.tsx +4 -3
  14. package/dist/assets/components/aspect-ratio.stories.tsx +4 -1
  15. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  16. package/dist/assets/components/autocomplete.tsx +3 -3
  17. package/dist/assets/components/avatar.stories.tsx +5 -5
  18. package/dist/assets/components/avatar.tsx +4 -4
  19. package/dist/assets/components/badge.stories.tsx +10 -10
  20. package/dist/assets/components/badge.tsx +3 -3
  21. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  22. package/dist/assets/components/breadcrumbs.tsx +13 -8
  23. package/dist/assets/components/button.stories.tsx +74 -74
  24. package/dist/assets/components/button.tsx +2 -0
  25. package/dist/assets/components/calendar.stories.tsx +11 -11
  26. package/dist/assets/components/calendar.tsx +4 -4
  27. package/dist/assets/components/card.stories.tsx +22 -22
  28. package/dist/assets/components/card.tsx +7 -3
  29. package/dist/assets/components/carousel.stories.tsx +6 -6
  30. package/dist/assets/components/carousel.tsx +10 -8
  31. package/dist/assets/components/chart.tsx +5 -5
  32. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  33. package/dist/assets/components/checkbox-group.tsx +3 -3
  34. package/dist/assets/components/checkbox.stories.tsx +23 -20
  35. package/dist/assets/components/checkbox.tsx +13 -16
  36. package/dist/assets/components/code.stories.tsx +24 -24
  37. package/dist/assets/components/code.tsx +7 -14
  38. package/dist/assets/components/collapsible.stories.tsx +3 -3
  39. package/dist/assets/components/command.stories.tsx +14 -14
  40. package/dist/assets/components/command.tsx +4 -3
  41. package/dist/assets/components/context-menu.stories.tsx +1 -1
  42. package/dist/assets/components/context-menu.tsx +3 -7
  43. package/dist/assets/components/date-input.stories.tsx +9 -9
  44. package/dist/assets/components/date-input.tsx +2 -2
  45. package/dist/assets/components/date-picker.stories.tsx +9 -9
  46. package/dist/assets/components/date-picker.tsx +3 -3
  47. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  48. package/dist/assets/components/date-range-picker.tsx +3 -3
  49. package/dist/assets/components/dialog.stories.tsx +40 -40
  50. package/dist/assets/components/dialog.tsx +8 -12
  51. package/dist/assets/components/divider.stories.tsx +30 -30
  52. package/dist/assets/components/divider.tsx +4 -8
  53. package/dist/assets/components/drawer.stories.tsx +32 -31
  54. package/dist/assets/components/drawer.tsx +7 -6
  55. package/dist/assets/components/dropdown-menu.tsx +3 -7
  56. package/dist/assets/components/dropdown.stories.tsx +12 -12
  57. package/dist/assets/components/dropdown.tsx +5 -5
  58. package/dist/assets/components/form.stories.tsx +30 -29
  59. package/dist/assets/components/form.tsx +5 -5
  60. package/dist/assets/components/hover-card.stories.tsx +12 -10
  61. package/dist/assets/components/hover-card.tsx +1 -1
  62. package/dist/assets/components/image.stories.tsx +48 -25
  63. package/dist/assets/components/image.tsx +8 -5
  64. package/dist/assets/components/input-otp.stories.tsx +15 -15
  65. package/dist/assets/components/input-otp.tsx +5 -5
  66. package/dist/assets/components/input.stories.tsx +30 -25
  67. package/dist/assets/components/input.tsx +7 -4
  68. package/dist/assets/components/kbd.stories.tsx +34 -34
  69. package/dist/assets/components/kbd.tsx +5 -5
  70. package/dist/assets/components/link.stories.tsx +36 -36
  71. package/dist/assets/components/link.tsx +4 -0
  72. package/dist/assets/components/listbox.stories.tsx +5 -5
  73. package/dist/assets/components/listbox.tsx +4 -4
  74. package/dist/assets/components/menubar.tsx +3 -7
  75. package/dist/assets/components/navbar.stories.tsx +24 -24
  76. package/dist/assets/components/navbar.tsx +8 -14
  77. package/dist/assets/components/navigation-menu.stories.tsx +11 -9
  78. package/dist/assets/components/navigation-menu.tsx +1 -1
  79. package/dist/assets/components/number-input.stories.tsx +11 -11
  80. package/dist/assets/components/number-input.tsx +3 -3
  81. package/dist/assets/components/pagination.stories.tsx +13 -13
  82. package/dist/assets/components/pagination.tsx +6 -6
  83. package/dist/assets/components/popover.stories.tsx +35 -35
  84. package/dist/assets/components/popover.tsx +98 -15
  85. package/dist/assets/components/progress.stories.tsx +5 -5
  86. package/dist/assets/components/progress.tsx +5 -5
  87. package/dist/assets/components/radio-group.stories.tsx +7 -7
  88. package/dist/assets/components/radio-group.tsx +3 -3
  89. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  90. package/dist/assets/components/range-calendar.tsx +3 -3
  91. package/dist/assets/components/resizable.stories.tsx +23 -23
  92. package/dist/assets/components/resizable.tsx +1 -1
  93. package/dist/assets/components/scroll-area.stories.tsx +15 -15
  94. package/dist/assets/components/scroll-area.tsx +1 -1
  95. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  96. package/dist/assets/components/scroll-shadow.tsx +2 -2
  97. package/dist/assets/components/select.stories.tsx +20 -19
  98. package/dist/assets/components/select.tsx +10 -6
  99. package/dist/assets/components/separator.tsx +1 -1
  100. package/dist/assets/components/sheet.tsx +3 -7
  101. package/dist/assets/components/sidebar.stories.tsx +30 -30
  102. package/dist/assets/components/sidebar.tsx +24 -27
  103. package/dist/assets/components/skeleton.stories.tsx +3 -3
  104. package/dist/assets/components/skeleton.tsx +2 -2
  105. package/dist/assets/components/slider.stories.tsx +6 -6
  106. package/dist/assets/components/slider.tsx +3 -3
  107. package/dist/assets/components/spacer.stories.tsx +11 -11
  108. package/dist/assets/components/spacer.tsx +2 -2
  109. package/dist/assets/components/spinner.stories.tsx +8 -8
  110. package/dist/assets/components/spinner.tsx +5 -5
  111. package/dist/assets/components/switch.stories.tsx +24 -20
  112. package/dist/assets/components/switch.tsx +14 -6
  113. package/dist/assets/components/table.stories.tsx +7 -7
  114. package/dist/assets/components/table.tsx +8 -8
  115. package/dist/assets/components/tabs.stories.tsx +37 -37
  116. package/dist/assets/components/tabs.tsx +3 -3
  117. package/dist/assets/components/textarea.stories.tsx +13 -12
  118. package/dist/assets/components/textarea.tsx +3 -3
  119. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  120. package/dist/assets/components/theme-toggle.tsx +2 -2
  121. package/dist/assets/components/time-input.stories.tsx +16 -16
  122. package/dist/assets/components/time-input.tsx +2 -2
  123. package/dist/assets/components/toast.stories.tsx +8 -5
  124. package/dist/assets/components/toast.tsx +6 -6
  125. package/dist/assets/components/toggle-group.tsx +1 -1
  126. package/dist/assets/components/toggle.tsx +1 -1
  127. package/dist/assets/components/tooltip.stories.tsx +49 -27
  128. package/dist/assets/components/tooltip.tsx +1 -1
  129. package/dist/assets/components/user.stories.tsx +23 -23
  130. package/dist/assets/components/user.tsx +7 -4
  131. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  132. package/dist/assets/dev-tools/index.ts +10 -0
  133. package/dist/assets/globals.css +9 -0
  134. package/dist/assets/styles/brand-overrides.css +37 -0
  135. package/dist/index.js +1776 -7
  136. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -274,6 +274,222 @@ const BUNDLED_ASSETS = path.join(__dirname, "assets");
274
274
  const IS_BUNDLED = fs.existsSync(BUNDLED_ASSETS);
275
275
  // Development paths (when running from source)
276
276
  const DEV_PROJECT_ROOT = path.resolve(__dirname, "../..");
277
+ /**
278
+ * Run the installer for DevTools Plugin (copies files to user project)
279
+ */
280
+ function runDevToolsInstaller() {
281
+ console.log("");
282
+ console.log(" ┌─────────────────────────────────────────────────┐");
283
+ console.log(" │ │");
284
+ console.log(" │ 🎨 Sonance DevTools Plugin - Installer │");
285
+ console.log(" │ │");
286
+ console.log(" └─────────────────────────────────────────────────┘");
287
+ console.log("");
288
+ const targetDir = process.cwd();
289
+ // Paths
290
+ const libDir = path.join(targetDir, "src/lib");
291
+ const devToolsDir = path.join(targetDir, "src/components/dev-tools");
292
+ const stylesDir = path.join(targetDir, "src/styles");
293
+ const apiThemeDir = path.join(targetDir, "src/app/api/sonance-theme");
294
+ const apiComponentsDir = path.join(targetDir, "src/app/api/sonance-components");
295
+ const apiSaveLogoDir = path.join(targetDir, "src/app/api/sonance-save-logo");
296
+ const apiAssetsDir = path.join(targetDir, "src/app/api/sonance-assets");
297
+ const apiInjectIdDir = path.join(targetDir, "src/app/api/sonance-inject-id");
298
+ const apiAnalyzeDir = path.join(targetDir, "src/app/api/sonance-analyze");
299
+ const themeDir = path.join(targetDir, "src/theme");
300
+ // Source resolution
301
+ let sourceBrandSystem;
302
+ let sourceDevTools;
303
+ let sourceBrandOverridesCss;
304
+ let sourceApiTheme;
305
+ let sourceApiComponents;
306
+ let sourceApiSaveLogo;
307
+ let sourceApiAssets;
308
+ let sourceApiInjectId;
309
+ let sourceApiAnalyze;
310
+ if (IS_BUNDLED) {
311
+ sourceBrandSystem = path.join(BUNDLED_ASSETS, "brand-system.ts");
312
+ sourceDevTools = path.join(BUNDLED_ASSETS, "dev-tools");
313
+ sourceBrandOverridesCss = path.join(BUNDLED_ASSETS, "styles/brand-overrides.css");
314
+ sourceApiTheme = path.join(BUNDLED_ASSETS, "api/sonance-theme/route.ts");
315
+ sourceApiComponents = path.join(BUNDLED_ASSETS, "api/sonance-components/route.ts");
316
+ sourceApiSaveLogo = path.join(BUNDLED_ASSETS, "api/sonance-save-logo/route.ts");
317
+ sourceApiAssets = path.join(BUNDLED_ASSETS, "api/sonance-assets/route.ts");
318
+ sourceApiInjectId = path.join(BUNDLED_ASSETS, "api/sonance-inject-id/route.ts");
319
+ sourceApiAnalyze = path.join(BUNDLED_ASSETS, "api/sonance-analyze/route.ts");
320
+ }
321
+ else {
322
+ sourceBrandSystem = path.join(DEV_PROJECT_ROOT, "src/lib/brand-system.ts");
323
+ sourceDevTools = path.join(DEV_PROJECT_ROOT, "src/components/dev-tools");
324
+ sourceBrandOverridesCss = path.join(DEV_PROJECT_ROOT, "src/styles/brand-overrides.css");
325
+ sourceApiTheme = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-theme/route.ts");
326
+ sourceApiComponents = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-components/route.ts");
327
+ sourceApiSaveLogo = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-save-logo/route.ts");
328
+ sourceApiAssets = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-assets/route.ts");
329
+ sourceApiInjectId = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-inject-id/route.ts");
330
+ sourceApiAnalyze = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-analyze/route.ts");
331
+ }
332
+ // Verify sources exist
333
+ if (!fs.existsSync(sourceBrandSystem)) {
334
+ console.error(" ❌ Error: Could not find brand-system.ts source file.");
335
+ console.error(` Path: ${sourceBrandSystem}`);
336
+ process.exit(1);
337
+ }
338
+ if (!fs.existsSync(sourceDevTools)) {
339
+ console.error(" ❌ Error: Could not find dev-tools source directory.");
340
+ console.error(` Path: ${sourceDevTools}`);
341
+ process.exit(1);
342
+ }
343
+ if (!fs.existsSync(sourceApiTheme)) {
344
+ console.error(" ❌ Error: Could not find sonance-theme API route source file.");
345
+ console.error(` Path: ${sourceApiTheme}`);
346
+ process.exit(1);
347
+ }
348
+ if (!fs.existsSync(sourceApiComponents)) {
349
+ console.error(" ❌ Error: Could not find sonance-components API route source file.");
350
+ console.error(` Path: ${sourceApiComponents}`);
351
+ process.exit(1);
352
+ }
353
+ if (!fs.existsSync(sourceApiSaveLogo)) {
354
+ console.error(" ❌ Error: Could not find sonance-save-logo API route source file.");
355
+ console.error(` Path: ${sourceApiSaveLogo}`);
356
+ process.exit(1);
357
+ }
358
+ if (!fs.existsSync(sourceApiAssets)) {
359
+ console.error(" ❌ Error: Could not find sonance-assets API route source file.");
360
+ console.error(` Path: ${sourceApiAssets}`);
361
+ process.exit(1);
362
+ }
363
+ if (!fs.existsSync(sourceApiInjectId)) {
364
+ console.error(" ❌ Error: Could not find sonance-inject-id API route source file.");
365
+ console.error(` Path: ${sourceApiInjectId}`);
366
+ process.exit(1);
367
+ }
368
+ if (!fs.existsSync(sourceApiAnalyze)) {
369
+ console.error(" ❌ Error: Could not find sonance-analyze API route source file.");
370
+ console.error(` Path: ${sourceApiAnalyze}`);
371
+ process.exit(1);
372
+ }
373
+ if (!fs.existsSync(sourceBrandOverridesCss)) {
374
+ console.error(" ❌ Error: Could not find brand-overrides.css source file.");
375
+ console.error(` Path: ${sourceBrandOverridesCss}`);
376
+ process.exit(1);
377
+ }
378
+ console.log(" 📂 Installing files...");
379
+ // 1. Install brand-system.ts
380
+ if (!fs.existsSync(libDir)) {
381
+ fs.mkdirSync(libDir, { recursive: true });
382
+ }
383
+ fs.copyFileSync(sourceBrandSystem, path.join(libDir, "brand-system.ts"));
384
+ console.log(" ✓ Created src/lib/brand-system.ts");
385
+ // 2. Install DevTools components
386
+ if (!fs.existsSync(devToolsDir)) {
387
+ fs.mkdirSync(devToolsDir, { recursive: true });
388
+ }
389
+ // Copy directory contents
390
+ const entries = fs.readdirSync(sourceDevTools, { withFileTypes: true });
391
+ for (const entry of entries) {
392
+ if (entry.isFile()) {
393
+ fs.copyFileSync(path.join(sourceDevTools, entry.name), path.join(devToolsDir, entry.name));
394
+ }
395
+ }
396
+ console.log(" ✓ Created src/components/dev-tools/");
397
+ // 3. Install API route for saving theme
398
+ if (!fs.existsSync(apiThemeDir)) {
399
+ fs.mkdirSync(apiThemeDir, { recursive: true });
400
+ }
401
+ fs.copyFileSync(sourceApiTheme, path.join(apiThemeDir, "route.ts"));
402
+ console.log(" ✓ Created src/app/api/sonance-theme/route.ts");
403
+ // 4. Install API route for component detection
404
+ if (!fs.existsSync(apiComponentsDir)) {
405
+ fs.mkdirSync(apiComponentsDir, { recursive: true });
406
+ }
407
+ fs.copyFileSync(sourceApiComponents, path.join(apiComponentsDir, "route.ts"));
408
+ console.log(" ✓ Created src/app/api/sonance-components/route.ts");
409
+ // 5. Install API route for saving logo configuration
410
+ if (!fs.existsSync(apiSaveLogoDir)) {
411
+ fs.mkdirSync(apiSaveLogoDir, { recursive: true });
412
+ }
413
+ fs.copyFileSync(sourceApiSaveLogo, path.join(apiSaveLogoDir, "route.ts"));
414
+ console.log(" ✓ Created src/app/api/sonance-save-logo/route.ts");
415
+ // 7. Install API route for listing logo assets
416
+ if (!fs.existsSync(apiAssetsDir)) {
417
+ fs.mkdirSync(apiAssetsDir, { recursive: true });
418
+ }
419
+ fs.copyFileSync(sourceApiAssets, path.join(apiAssetsDir, "route.ts"));
420
+ console.log(" ✓ Created src/app/api/sonance-assets/route.ts");
421
+ // 8. Install API route for auto-fixing logo IDs
422
+ if (!fs.existsSync(apiInjectIdDir)) {
423
+ fs.mkdirSync(apiInjectIdDir, { recursive: true });
424
+ }
425
+ fs.copyFileSync(sourceApiInjectId, path.join(apiInjectIdDir, "route.ts"));
426
+ console.log(" ✓ Created src/app/api/sonance-inject-id/route.ts");
427
+ // 9. Install API route for project analysis
428
+ if (!fs.existsSync(apiAnalyzeDir)) {
429
+ fs.mkdirSync(apiAnalyzeDir, { recursive: true });
430
+ }
431
+ fs.copyFileSync(sourceApiAnalyze, path.join(apiAnalyzeDir, "route.ts"));
432
+ console.log(" ✓ Created src/app/api/sonance-analyze/route.ts");
433
+ // 11. Install brand-overrides.css for production logo sizing
434
+ if (!fs.existsSync(stylesDir)) {
435
+ fs.mkdirSync(stylesDir, { recursive: true });
436
+ }
437
+ fs.copyFileSync(sourceBrandOverridesCss, path.join(stylesDir, "brand-overrides.css"));
438
+ console.log(" ✓ Created src/styles/brand-overrides.css");
439
+ // 12. Create theme directory with initial files
440
+ if (!fs.existsSync(themeDir)) {
441
+ fs.mkdirSync(themeDir, { recursive: true });
442
+ // Create initial theme CSS
443
+ const initialCss = `/**
444
+ * Sonance Theme - Auto-generated by DevTools
445
+ * Do not edit manually - use the DevTools plugin to make changes.
446
+ */
447
+
448
+ :root {
449
+ /* Use DevTools to customize and apply your theme */
450
+ }
451
+ `;
452
+ fs.writeFileSync(path.join(themeDir, "sonance-theme.css"), initialCss, "utf-8");
453
+ // Create initial config
454
+ const initialConfig = {
455
+ baseColor: "#333F48",
456
+ accentColor: "#00A3E1",
457
+ radius: "sm",
458
+ headingWeight: 600,
459
+ bodyWeight: 400,
460
+ typographyScale: "default",
461
+ spacing: "default"
462
+ };
463
+ fs.writeFileSync(path.join(themeDir, "sonance-config.json"), JSON.stringify(initialConfig, null, 2), "utf-8");
464
+ console.log(" ✓ Created src/theme/ with initial files");
465
+ }
466
+ console.log("");
467
+ console.log(" ✅ Sonance DevTools installed successfully!");
468
+ console.log("");
469
+ console.log(" ┌──────────────────────────────────────────────────────────────────┐");
470
+ console.log(" │ Next steps: │");
471
+ console.log(" │ │");
472
+ console.log(" │ 1. Add to your globals.css: │");
473
+ console.log(" │ @import \"../styles/brand-overrides.css\"; │");
474
+ console.log(" │ @import \"../theme/sonance-theme.css\"; │");
475
+ console.log(" │ │");
476
+ console.log(" │ 2. Add to your layout.tsx: │");
477
+ console.log(" │ import { SonanceDevTools } from '@/components/dev-tools'; │");
478
+ console.log(" │ │");
479
+ console.log(" │ 3. Add to your render function: │");
480
+ console.log(" │ {process.env.NODE_ENV === 'development' && <SonanceDevTools />}│");
481
+ console.log(" │ │");
482
+ console.log(" │ 4. Apply CSS variables to your logo components: │");
483
+ console.log(" │ style={{ transform: `scale(var(--sonance-logo-scale, 1))` }} │");
484
+ console.log(" │ │");
485
+ console.log(" └──────────────────────────────────────────────────────────────────┘");
486
+ console.log("");
487
+ }
488
+ // Check for install-devtools command
489
+ if (process.argv.includes("install-devtools") || process.argv.includes("--install-devtools")) {
490
+ runDevToolsInstaller();
491
+ process.exit(0);
492
+ }
277
493
  // Resolve paths based on environment
278
494
  function getAssetPath(assetType) {
279
495
  if (IS_BUNDLED) {
@@ -536,6 +752,610 @@ function getBrandColorsForDocsMulti(brand = "sonance") {
536
752
  black: hexToFormats("#000000"),
537
753
  };
538
754
  }
755
+ // ============================================
756
+ // COMPONENT CATEGORIES
757
+ // ============================================
758
+ /**
759
+ * Component categories matching the sidebar navigation
760
+ * Used for category-based retrieval and feature-to-component mapping
761
+ */
762
+ const COMPONENT_CATEGORIES = {
763
+ forms: [
764
+ "button", "input", "textarea", "checkbox", "checkbox-group", "radio-group",
765
+ "switch", "select", "slider", "number-input", "autocomplete", "listbox",
766
+ "form", "input-otp", "calendar", "date-input", "date-picker",
767
+ "date-range-picker", "time-input", "range-calendar", "command", "toggle", "toggle-group"
768
+ ],
769
+ navigation: [
770
+ "breadcrumbs", "dropdown", "link", "navbar", "pagination", "tabs",
771
+ "context-menu", "dropdown-menu", "menubar", "navigation-menu"
772
+ ],
773
+ feedback: ["alert", "badge", "toast", "progress", "spinner", "skeleton"],
774
+ overlays: ["dialog", "drawer", "tooltip", "popover", "alert-dialog", "hover-card"],
775
+ "data-display": [
776
+ "avatar", "user", "accordion", "table", "card", "image",
777
+ "aspect-ratio", "carousel", "chart", "collapsible", "scroll-area"
778
+ ],
779
+ layout: ["resizable", "sidebar"],
780
+ utilities: ["code", "kbd", "divider", "spacer", "scroll-shadow"]
781
+ };
782
+ /**
783
+ * Maps app features to required component categories/components
784
+ */
785
+ const FEATURE_TO_COMPONENTS = {
786
+ auth: ["button", "input", "form", "card", "alert"],
787
+ sidebar: ["sidebar", "button", "tooltip", "dropdown-menu", "separator"],
788
+ "dark-mode": ["switch", "dropdown-menu"], // Theme toggle components
789
+ tables: ["table", "pagination", "button", "badge", "dropdown-menu"],
790
+ forms: ["input", "textarea", "checkbox", "radio-group", "select", "button", "form", "alert"],
791
+ charts: ["chart", "card", "badge"],
792
+ dashboard: ["card", "chart", "table", "badge", "button", "tabs"],
793
+ navigation: ["navbar", "breadcrumbs", "tabs", "dropdown-menu", "link"]
794
+ };
795
+ /**
796
+ * Page layout templates for different app types
797
+ */
798
+ const PAGE_LAYOUTS = {
799
+ dashboard: {
800
+ description: "Data overview with metrics, charts, and quick actions",
801
+ structure: `## Page Structure
802
+ 1. **Header** - Page title, breadcrumbs, primary action button
803
+ 2. **Metrics Row** - 3-4 stat cards showing KPIs
804
+ 3. **Main Content** - Split into primary and secondary columns
805
+ - Primary (2/3): Main chart or data table
806
+ - Secondary (1/3): Activity feed, quick links
807
+ 4. **Footer** (optional) - Pagination or additional actions`,
808
+ components: ["card", "chart", "table", "badge", "button", "dropdown-menu", "tabs"],
809
+ gridLayout: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6",
810
+ spacing: "py-8 px-6 lg:py-12 lg:px-8",
811
+ template: `// Dashboard page template
812
+ export default function DashboardPage() {
813
+ return (
814
+ <div className="min-h-screen bg-background">
815
+ {/* Header */}
816
+ <header className="border-b border-border px-6 py-4">
817
+ <div className="flex items-center justify-between">
818
+ <div>
819
+ <h1 className="text-2xl font-light text-foreground">Dashboard</h1>
820
+ <p className="text-sm text-muted-foreground">Overview of your activity</p>
821
+ </div>
822
+ <Button>New Report</Button>
823
+ </div>
824
+ </header>
825
+
826
+ {/* Main content */}
827
+ <main className="p-6 lg:p-8 space-y-8">
828
+ {/* Metrics row */}
829
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
830
+ <Card className="p-6">
831
+ <p className="text-sm text-muted-foreground">Total Revenue</p>
832
+ <p className="text-3xl font-light">$45,231</p>
833
+ </Card>
834
+ {/* Add more metric cards */}
835
+ </div>
836
+
837
+ {/* Main content grid */}
838
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
839
+ <div className="lg:col-span-2">
840
+ <Card className="p-6">
841
+ <h2 className="text-lg font-medium mb-4">Analytics</h2>
842
+ {/* Chart component here */}
843
+ </Card>
844
+ </div>
845
+ <div>
846
+ <Card className="p-6">
847
+ <h2 className="text-lg font-medium mb-4">Recent Activity</h2>
848
+ {/* Activity list here */}
849
+ </Card>
850
+ </div>
851
+ </div>
852
+ </main>
853
+ </div>
854
+ );
855
+ }`
856
+ },
857
+ settings: {
858
+ description: "Configuration page with grouped settings sections",
859
+ structure: `## Page Structure
860
+ 1. **Header** - Page title, save button
861
+ 2. **Navigation** - Tabs or sidebar for setting categories
862
+ 3. **Content Area** - Form sections with dividers
863
+ 4. **Footer** - Save/Cancel buttons (sticky on mobile)`,
864
+ components: ["tabs", "input", "switch", "select", "button", "card", "divider", "form"],
865
+ gridLayout: "max-w-4xl mx-auto",
866
+ spacing: "py-8 px-6",
867
+ template: `// Settings page template
868
+ export default function SettingsPage() {
869
+ return (
870
+ <div className="min-h-screen bg-background">
871
+ <div className="max-w-4xl mx-auto py-8 px-6">
872
+ {/* Header */}
873
+ <div className="mb-8">
874
+ <h1 className="text-2xl font-light text-foreground">Settings</h1>
875
+ <p className="text-sm text-muted-foreground">Manage your preferences</p>
876
+ </div>
877
+
878
+ {/* Settings tabs */}
879
+ <Tabs defaultValue="general" className="space-y-8">
880
+ <TabsList>
881
+ <TabsTrigger value="general">General</TabsTrigger>
882
+ <TabsTrigger value="notifications">Notifications</TabsTrigger>
883
+ <TabsTrigger value="security">Security</TabsTrigger>
884
+ </TabsList>
885
+
886
+ <TabsContent value="general" className="space-y-6">
887
+ <Card className="p-6">
888
+ <h2 className="text-lg font-medium mb-4">Profile</h2>
889
+ <div className="space-y-4">
890
+ <div>
891
+ <Label htmlFor="name">Name</Label>
892
+ <Input id="name" placeholder="Enter your name" />
893
+ </div>
894
+ <div>
895
+ <Label htmlFor="email">Email</Label>
896
+ <Input id="email" type="email" placeholder="Enter your email" />
897
+ </div>
898
+ </div>
899
+ </Card>
900
+ </TabsContent>
901
+ </Tabs>
902
+
903
+ {/* Footer actions */}
904
+ <div className="mt-8 flex justify-end gap-4">
905
+ <Button variant="outline">Cancel</Button>
906
+ <Button>Save Changes</Button>
907
+ </div>
908
+ </div>
909
+ </div>
910
+ );
911
+ }`
912
+ },
913
+ landing: {
914
+ description: "Marketing landing page with hero, features, and CTA sections",
915
+ structure: `## Page Structure
916
+ 1. **Hero Section** - Large headline, subtext, CTA buttons
917
+ 2. **Features Section** - 3-4 feature cards or grid
918
+ 3. **Social Proof** - Testimonials or logos
919
+ 4. **CTA Section** - Final call to action
920
+ 5. **Footer** - Links, legal`,
921
+ components: ["button", "card", "badge", "image", "divider"],
922
+ gridLayout: "max-w-7xl mx-auto",
923
+ spacing: "py-20 px-6 lg:py-24 lg:px-8",
924
+ template: `// Landing page template
925
+ export default function LandingPage() {
926
+ return (
927
+ <div className="min-h-screen bg-background">
928
+ {/* Hero */}
929
+ <section className="py-20 px-6 lg:py-32 lg:px-8 text-center">
930
+ <div className="max-w-4xl mx-auto">
931
+ <Badge className="mb-4">New Release</Badge>
932
+ <h1 className="text-4xl lg:text-6xl font-light text-foreground mb-6">
933
+ Designed to Disappear
934
+ </h1>
935
+ <p className="text-xl text-muted-foreground mb-8 max-w-2xl mx-auto">
936
+ Premium audio solutions that blend seamlessly into your architecture.
937
+ </p>
938
+ <div className="flex gap-4 justify-center">
939
+ <Button size="lg">Get Started</Button>
940
+ <Button size="lg" variant="outline">Learn More</Button>
941
+ </div>
942
+ </div>
943
+ </section>
944
+
945
+ {/* Features */}
946
+ <section className="py-20 px-6 lg:py-24 lg:px-8 bg-muted/50">
947
+ <div className="max-w-7xl mx-auto">
948
+ <h2 className="text-3xl font-light text-center mb-12">Features</h2>
949
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
950
+ <Card className="p-6">
951
+ <h3 className="text-lg font-medium mb-2">Feature One</h3>
952
+ <p className="text-muted-foreground">Description of the feature.</p>
953
+ </Card>
954
+ {/* More feature cards */}
955
+ </div>
956
+ </div>
957
+ </section>
958
+
959
+ {/* CTA */}
960
+ <section className="py-20 px-6 lg:py-24 lg:px-8 text-center">
961
+ <div className="max-w-2xl mx-auto">
962
+ <h2 className="text-3xl font-light mb-4">Ready to get started?</h2>
963
+ <p className="text-muted-foreground mb-8">Join thousands of satisfied customers.</p>
964
+ <Button size="lg">Start Free Trial</Button>
965
+ </div>
966
+ </section>
967
+ </div>
968
+ );
969
+ }`
970
+ },
971
+ auth: {
972
+ description: "Authentication page with centered form card",
973
+ structure: `## Page Structure
974
+ 1. **Centered Container** - Max-width card
975
+ 2. **Logo** - Brand logo at top
976
+ 3. **Form** - Email, password, submit
977
+ 4. **Links** - Forgot password, sign up`,
978
+ components: ["card", "input", "button", "form", "alert", "divider"],
979
+ gridLayout: "min-h-screen flex items-center justify-center",
980
+ spacing: "p-6",
981
+ template: `// Auth page template
982
+ export default function AuthPage() {
983
+ return (
984
+ <div className="min-h-screen flex items-center justify-center bg-background p-6">
985
+ <Card className="w-full max-w-md p-8">
986
+ {/* Logo */}
987
+ <div className="text-center mb-8">
988
+ <Image src="/logo.svg" alt="Logo" width={120} height={40} className="mx-auto" />
989
+ </div>
990
+
991
+ <h1 className="text-2xl font-light text-center mb-2">Welcome back</h1>
992
+ <p className="text-sm text-muted-foreground text-center mb-8">
993
+ Sign in to your account
994
+ </p>
995
+
996
+ <form className="space-y-4">
997
+ <div>
998
+ <Label htmlFor="email">Email</Label>
999
+ <Input id="email" type="email" placeholder="name@example.com" />
1000
+ </div>
1001
+ <div>
1002
+ <Label htmlFor="password">Password</Label>
1003
+ <Input id="password" type="password" placeholder="Enter your password" />
1004
+ </div>
1005
+ <Button className="w-full">Sign In</Button>
1006
+ </form>
1007
+
1008
+ <div className="mt-6 text-center text-sm">
1009
+ <a href="#" className="text-primary hover:underline">Forgot password?</a>
1010
+ </div>
1011
+
1012
+ <Divider className="my-6" />
1013
+
1014
+ <p className="text-center text-sm text-muted-foreground">
1015
+ Don't have an account?{" "}
1016
+ <a href="#" className="text-primary hover:underline">Sign up</a>
1017
+ </p>
1018
+ </Card>
1019
+ </div>
1020
+ );
1021
+ }`
1022
+ },
1023
+ list: {
1024
+ description: "List/table page with filters and pagination",
1025
+ structure: `## Page Structure
1026
+ 1. **Header** - Title, primary action
1027
+ 2. **Filters** - Search, dropdown filters
1028
+ 3. **Table/List** - Data display
1029
+ 4. **Pagination** - Page controls`,
1030
+ components: ["table", "input", "select", "button", "badge", "pagination", "dropdown-menu"],
1031
+ gridLayout: "max-w-7xl mx-auto",
1032
+ spacing: "py-8 px-6",
1033
+ template: `// List page template
1034
+ export default function ListPage() {
1035
+ return (
1036
+ <div className="min-h-screen bg-background">
1037
+ <div className="max-w-7xl mx-auto py-8 px-6">
1038
+ {/* Header */}
1039
+ <div className="flex items-center justify-between mb-8">
1040
+ <div>
1041
+ <h1 className="text-2xl font-light text-foreground">Users</h1>
1042
+ <p className="text-sm text-muted-foreground">Manage your team members</p>
1043
+ </div>
1044
+ <Button>Add User</Button>
1045
+ </div>
1046
+
1047
+ {/* Filters */}
1048
+ <div className="flex gap-4 mb-6">
1049
+ <Input placeholder="Search users..." className="max-w-sm" />
1050
+ <Select>
1051
+ <SelectTrigger className="w-40">
1052
+ <SelectValue placeholder="Role" />
1053
+ </SelectTrigger>
1054
+ <SelectContent>
1055
+ <SelectItem value="all">All Roles</SelectItem>
1056
+ <SelectItem value="admin">Admin</SelectItem>
1057
+ <SelectItem value="user">User</SelectItem>
1058
+ </SelectContent>
1059
+ </Select>
1060
+ </div>
1061
+
1062
+ {/* Table */}
1063
+ <Card>
1064
+ <Table>
1065
+ <TableHeader>
1066
+ <TableRow>
1067
+ <TableHead>Name</TableHead>
1068
+ <TableHead>Email</TableHead>
1069
+ <TableHead>Role</TableHead>
1070
+ <TableHead>Status</TableHead>
1071
+ <TableHead></TableHead>
1072
+ </TableRow>
1073
+ </TableHeader>
1074
+ <TableBody>
1075
+ {/* Table rows */}
1076
+ </TableBody>
1077
+ </Table>
1078
+ </Card>
1079
+
1080
+ {/* Pagination */}
1081
+ <div className="mt-6 flex justify-center">
1082
+ <Pagination>
1083
+ {/* Pagination controls */}
1084
+ </Pagination>
1085
+ </div>
1086
+ </div>
1087
+ </div>
1088
+ );
1089
+ }`
1090
+ },
1091
+ detail: {
1092
+ description: "Detail/profile page with header and content sections",
1093
+ structure: `## Page Structure
1094
+ 1. **Header** - Back button, title, actions
1095
+ 2. **Main Content** - Primary information
1096
+ 3. **Sidebar** - Related info, metadata`,
1097
+ components: ["card", "button", "badge", "tabs", "avatar", "divider"],
1098
+ gridLayout: "max-w-7xl mx-auto",
1099
+ spacing: "py-8 px-6",
1100
+ template: `// Detail page template
1101
+ export default function DetailPage() {
1102
+ return (
1103
+ <div className="min-h-screen bg-background">
1104
+ <div className="max-w-7xl mx-auto py-8 px-6">
1105
+ {/* Header */}
1106
+ <div className="mb-8">
1107
+ <Button variant="ghost" size="sm" className="mb-4">
1108
+ ← Back
1109
+ </Button>
1110
+ <div className="flex items-start justify-between">
1111
+ <div className="flex items-center gap-4">
1112
+ <Avatar className="h-16 w-16" />
1113
+ <div>
1114
+ <h1 className="text-2xl font-light text-foreground">Item Name</h1>
1115
+ <p className="text-sm text-muted-foreground">Description or subtitle</p>
1116
+ </div>
1117
+ </div>
1118
+ <div className="flex gap-2">
1119
+ <Button variant="outline">Edit</Button>
1120
+ <Button>Action</Button>
1121
+ </div>
1122
+ </div>
1123
+ </div>
1124
+
1125
+ {/* Content grid */}
1126
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
1127
+ {/* Main content */}
1128
+ <div className="lg:col-span-2 space-y-6">
1129
+ <Card className="p-6">
1130
+ <h2 className="text-lg font-medium mb-4">Details</h2>
1131
+ {/* Detail content */}
1132
+ </Card>
1133
+ </div>
1134
+
1135
+ {/* Sidebar */}
1136
+ <div className="space-y-6">
1137
+ <Card className="p-6">
1138
+ <h2 className="text-lg font-medium mb-4">Metadata</h2>
1139
+ {/* Sidebar content */}
1140
+ </Card>
1141
+ </div>
1142
+ </div>
1143
+ </div>
1144
+ </div>
1145
+ );
1146
+ }`
1147
+ }
1148
+ };
1149
+ /**
1150
+ * Detect the component type from a description
1151
+ */
1152
+ function detectComponentType(description) {
1153
+ const lower = description.toLowerCase();
1154
+ if (/button|input|form|checkbox|radio|switch|select|slider|date|time|calendar|toggle|textarea|autocomplete/i.test(lower))
1155
+ return "forms";
1156
+ if (/nav|menu|tab|breadcrumb|pagination|link|dropdown/i.test(lower))
1157
+ return "navigation";
1158
+ if (/alert|badge|toast|notification|progress|spinner|loading|skeleton/i.test(lower))
1159
+ return "feedback";
1160
+ if (/modal|dialog|drawer|tooltip|popover|overlay|sheet/i.test(lower))
1161
+ return "overlays";
1162
+ if (/card|table|avatar|user|accordion|list|chart|carousel|image|data/i.test(lower))
1163
+ return "data-display";
1164
+ if (/sidebar|layout|grid|container|resizable|panel|split/i.test(lower))
1165
+ return "layout";
1166
+ if (/code|kbd|divider|spacer|utility/i.test(lower))
1167
+ return "utilities";
1168
+ return "general";
1169
+ }
1170
+ /**
1171
+ * Get component-type-specific implementation guidance
1172
+ */
1173
+ const COMPONENT_TYPE_GUIDANCE = {
1174
+ forms: `## Forms Component Implementation
1175
+
1176
+ ### Key Rules
1177
+ - Always include label associations (htmlFor/id)
1178
+ - Support disabled, error, and loading states
1179
+ - Use ring-based focus indicators: \`focus-visible:ring-2 focus-visible:ring-primary\`
1180
+ - Maintain consistent sizing: h-10 for inputs, px-6 py-3 for buttons
1181
+ - Include aria-invalid and aria-describedby for error states
1182
+
1183
+ ### Required States
1184
+ - Default, Hover, Focus, Disabled, Error, Loading
1185
+
1186
+ ### Common Patterns
1187
+ \`\`\`tsx
1188
+ // Button with proper states
1189
+ <button className="bg-primary text-primary-foreground px-6 py-3 rounded-sm font-medium
1190
+ hover:bg-primary/90 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
1191
+ disabled:opacity-50 disabled:cursor-not-allowed transition-colors">
1192
+
1193
+ // Input with error state
1194
+ <input className="h-10 px-4 border border-input rounded-sm bg-background
1195
+ focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
1196
+ aria-[invalid=true]:border-destructive" />
1197
+ \`\`\`
1198
+
1199
+ ### Anti-Patterns
1200
+ - Missing focus states (CRITICAL)
1201
+ - No error state styling
1202
+ - Inconsistent heights across form elements
1203
+ - Using font-bold (use font-medium)`,
1204
+ navigation: `## Navigation Component Implementation
1205
+
1206
+ ### Key Rules
1207
+ - Clear active state indication: bg-primary or border-b-2
1208
+ - Keyboard navigation support: arrow keys, Home/End
1209
+ - ARIA roles: navigation, menubar, menu, menuitem
1210
+ - Consistent icon sizing: h-4 w-4 with text
1211
+
1212
+ ### Required States
1213
+ - Default, Hover, Active, Disabled
1214
+
1215
+ ### Common Patterns
1216
+ \`\`\`tsx
1217
+ // Nav item with active state
1218
+ <a className="px-4 py-2 text-sm font-medium text-muted-foreground
1219
+ hover:text-foreground hover:bg-muted rounded-sm transition-colors
1220
+ data-[active=true]:text-foreground data-[active=true]:bg-muted">
1221
+
1222
+ // Tab with indicator
1223
+ <button className="px-4 py-2 text-sm font-medium border-b-2 border-transparent
1224
+ hover:border-muted data-[state=active]:border-primary transition-colors">
1225
+ \`\`\`
1226
+
1227
+ ### Anti-Patterns
1228
+ - Missing aria-current for active items
1229
+ - No keyboard navigation
1230
+ - Inconsistent spacing between items`,
1231
+ feedback: `## Feedback Component Implementation
1232
+
1233
+ ### Key Rules
1234
+ - Use semantic colors: destructive for errors, success for positive
1235
+ - Include appropriate icons (checkmark, X, info, warning)
1236
+ - Support dismissible variants where appropriate
1237
+ - Consider auto-dismiss for toasts
1238
+
1239
+ ### Common Patterns
1240
+ \`\`\`tsx
1241
+ // Alert with variants
1242
+ <div role="alert" className="p-4 rounded-sm border
1243
+ data-[variant=error]:bg-destructive/10 data-[variant=error]:border-destructive
1244
+ data-[variant=success]:bg-green-500/10 data-[variant=success]:border-green-500">
1245
+
1246
+ // Badge
1247
+ <span className="px-2 py-0.5 text-xs font-medium rounded-sm
1248
+ bg-primary text-primary-foreground">
1249
+ \`\`\`
1250
+
1251
+ ### Anti-Patterns
1252
+ - Missing role="alert" for alerts
1253
+ - No icon to reinforce message type
1254
+ - Overly large badges`,
1255
+ overlays: `## Overlay Component Implementation
1256
+
1257
+ ### Key Rules
1258
+ - Focus trap within modals
1259
+ - Close on Escape key
1260
+ - Backdrop click to close (optional)
1261
+ - Proper z-index management
1262
+ - Animate in/out smoothly
1263
+
1264
+ ### Common Patterns
1265
+ \`\`\`tsx
1266
+ // Dialog backdrop
1267
+ <div className="fixed inset-0 bg-black/50 z-50" />
1268
+
1269
+ // Dialog content
1270
+ <div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
1271
+ bg-background border border-border rounded-sm shadow-lg p-6 z-50
1272
+ animate-in fade-in zoom-in-95">
1273
+ \`\`\`
1274
+
1275
+ ### Anti-Patterns
1276
+ - Missing focus trap
1277
+ - No Escape key handler
1278
+ - Forgetting to portal the overlay`,
1279
+ "data-display": `## Data Display Component Implementation
1280
+
1281
+ ### Key Rules
1282
+ - Handle empty states gracefully
1283
+ - Support loading skeletons
1284
+ - Consider responsive behavior
1285
+ - Maintain data hierarchy through typography
1286
+
1287
+ ### Common Patterns
1288
+ \`\`\`tsx
1289
+ // Card
1290
+ <div className="bg-card border border-border rounded-sm p-6 shadow-sm">
1291
+
1292
+ // Table
1293
+ <table className="w-full">
1294
+ <thead className="border-b border-border">
1295
+ <tr>
1296
+ <th className="text-left py-3 px-4 text-sm font-medium text-muted-foreground">
1297
+ \`\`\`
1298
+
1299
+ ### Anti-Patterns
1300
+ - No empty state handling
1301
+ - Missing loading states
1302
+ - Cramped spacing in tables`,
1303
+ layout: `## Layout Component Implementation
1304
+
1305
+ ### Key Rules
1306
+ - Use CSS Grid or Flexbox appropriately
1307
+ - Support responsive breakpoints
1308
+ - Consider collapsible/expandable states
1309
+ - Use semantic HTML (aside, main, nav)
1310
+
1311
+ ### Common Patterns
1312
+ \`\`\`tsx
1313
+ // Sidebar layout
1314
+ <div className="flex min-h-screen">
1315
+ <aside className="w-64 border-r border-border bg-background">
1316
+ <main className="flex-1 p-6">
1317
+ </div>
1318
+
1319
+ // Resizable panel
1320
+ <div className="flex" style={{ resize: 'horizontal' }}>
1321
+ \`\`\`
1322
+
1323
+ ### Anti-Patterns
1324
+ - Fixed widths without responsiveness
1325
+ - Missing semantic landmarks
1326
+ - No mobile consideration`,
1327
+ utilities: `## Utility Component Implementation
1328
+
1329
+ ### Key Rules
1330
+ - Keep utilities simple and focused
1331
+ - Provide sensible defaults
1332
+ - Support customization via props/className
1333
+
1334
+ ### Common Patterns
1335
+ \`\`\`tsx
1336
+ // Divider
1337
+ <hr className="border-t border-border my-6" />
1338
+
1339
+ // Spacer
1340
+ <div className="h-8" aria-hidden="true" />
1341
+
1342
+ // Kbd
1343
+ <kbd className="px-2 py-1 text-xs font-mono bg-muted rounded-sm border border-border">
1344
+ \`\`\``,
1345
+ general: `## General Component Implementation
1346
+
1347
+ ### Key Rules
1348
+ - Follow brand guidelines for colors and typography
1349
+ - Use semantic CSS variables
1350
+ - Support light and dark mode
1351
+ - Include hover and focus states
1352
+
1353
+ ### Quick Reference
1354
+ - Primary: bg-primary text-primary-foreground
1355
+ - Border radius: rounded-sm (2px)
1356
+ - Font weights: font-light (300), font-medium (500)
1357
+ - Spacing: generous (py-20 for sections)`
1358
+ };
539
1359
  /**
540
1360
  * Get regex patterns to detect brand colors in code
541
1361
  */
@@ -824,6 +1644,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
824
1644
  type: "string",
825
1645
  description: "What the user wants to design (e.g., 'hero section', 'pricing card', 'navigation bar', 'proposal PDF', 'email template')",
826
1646
  },
1647
+ component_type: {
1648
+ type: "string",
1649
+ enum: ["forms", "navigation", "feedback", "overlays", "data-display", "layout", "utilities", "general"],
1650
+ description: "Type of component for specialized guidance. Auto-detected from description if not specified. 'forms' = buttons/inputs/selects, 'navigation' = tabs/menus/breadcrumbs, 'feedback' = alerts/badges/toasts, 'overlays' = dialogs/modals/popovers, 'data-display' = cards/tables/avatars, 'layout' = sidebar/resizable/grid, 'utilities' = dividers/spacers.",
1651
+ },
827
1652
  },
828
1653
  required: ["component_description"],
829
1654
  },
@@ -923,6 +1748,111 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
923
1748
  required: ["source_format"],
924
1749
  },
925
1750
  },
1751
+ // ============================================
1752
+ // NEW APP DEVELOPMENT TOOLS
1753
+ // ============================================
1754
+ {
1755
+ name: "design_app",
1756
+ description: "USE FOR NEW APP DEVELOPMENT: Returns a complete starter kit for building a new application with Sonance, IPORT, or Blaze branding. Includes: globals.css with brand CSS variables, component library source code filtered by features needed, page layout templates, and step-by-step build instructions. Use when user asks to 'create', 'build', or 'start' a new branded application.",
1757
+ inputSchema: {
1758
+ type: "object",
1759
+ properties: {
1760
+ brand: {
1761
+ type: "string",
1762
+ enum: ["sonance", "iport", "blaze"],
1763
+ description: "Target brand for the app. Defaults to 'sonance'.",
1764
+ },
1765
+ app_type: {
1766
+ type: "string",
1767
+ enum: ["dashboard", "marketing", "saas", "internal-tool", "general"],
1768
+ description: "Type of application - affects page layout recommendations. Defaults to 'general'.",
1769
+ },
1770
+ features: {
1771
+ type: "array",
1772
+ items: {
1773
+ type: "string",
1774
+ enum: ["auth", "sidebar", "dark-mode", "tables", "forms", "charts", "navigation", "dashboard"],
1775
+ },
1776
+ description: "Features the app will have - returns only relevant components. If omitted, returns core components.",
1777
+ },
1778
+ framework: {
1779
+ type: "string",
1780
+ enum: ["nextjs", "react"],
1781
+ description: "Target framework. Defaults to 'nextjs'.",
1782
+ },
1783
+ },
1784
+ },
1785
+ },
1786
+ {
1787
+ name: "redesign_app",
1788
+ description: "USE FOR REDESIGNING EXISTING APPS: Analyzes code patterns and returns specific transformation instructions to rebrand an application to Sonance, IPORT, or Blaze. Provide existing code and get: identified brand violations with line numbers, specific fixes with before/after code, step-by-step transformation plan. Use when user asks to 'redesign', 'rebrand', 'restyle', or 'update' an existing application.",
1789
+ inputSchema: {
1790
+ type: "object",
1791
+ properties: {
1792
+ brand: {
1793
+ type: "string",
1794
+ enum: ["sonance", "iport", "blaze"],
1795
+ description: "Target brand for redesign. Defaults to 'sonance'.",
1796
+ },
1797
+ existing_code: {
1798
+ type: "string",
1799
+ description: "The existing component/page code to analyze. Tool will identify what needs changing.",
1800
+ },
1801
+ file_type: {
1802
+ type: "string",
1803
+ enum: ["globals.css", "tailwind.config", "component", "page", "layout", "unknown"],
1804
+ description: "Type of file being analyzed - helps provide targeted fixes.",
1805
+ },
1806
+ file_path: {
1807
+ type: "string",
1808
+ description: "Optional file path for context (e.g., 'src/components/Button.tsx').",
1809
+ },
1810
+ },
1811
+ required: ["existing_code"],
1812
+ },
1813
+ },
1814
+ {
1815
+ name: "analyze_for_redesign",
1816
+ description: "BATCH CODEBASE ANALYSIS: Accepts multiple file contents and returns a prioritized transformation plan for rebranding. Returns which files need changes, in what order, and what changes each needs. Use when redesigning multiple files or an entire codebase.",
1817
+ inputSchema: {
1818
+ type: "object",
1819
+ properties: {
1820
+ brand: {
1821
+ type: "string",
1822
+ enum: ["sonance", "iport", "blaze"],
1823
+ description: "Target brand for redesign. Defaults to 'sonance'.",
1824
+ },
1825
+ files: {
1826
+ type: "array",
1827
+ items: {
1828
+ type: "object",
1829
+ properties: {
1830
+ path: { type: "string", description: "File path" },
1831
+ content: { type: "string", description: "File content" },
1832
+ },
1833
+ required: ["path", "content"],
1834
+ },
1835
+ description: "Array of files to analyze",
1836
+ },
1837
+ },
1838
+ required: ["files"],
1839
+ },
1840
+ },
1841
+ {
1842
+ name: "get_components_by_category",
1843
+ description: "Returns all components in a specific category with their full source code. More efficient than get_full_library when you only need certain types of components. Categories: forms, navigation, feedback, overlays, data-display, layout, utilities.",
1844
+ inputSchema: {
1845
+ type: "object",
1846
+ properties: {
1847
+ category: {
1848
+ type: "string",
1849
+ enum: ["forms", "navigation", "feedback", "overlays", "data-display", "layout", "utilities"],
1850
+ description: "The component category to retrieve",
1851
+ },
1852
+ },
1853
+ required: ["category"],
1854
+ },
1855
+ },
926
1856
  ],
927
1857
  };
928
1858
  });
@@ -1222,6 +2152,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1222
2152
  if (!rawArgs.output_type) {
1223
2153
  defaultsUsed.push(`output type → ${detectedOutputType} (auto-detected)`);
1224
2154
  }
2155
+ // Smart component type detection (only for UI outputs)
2156
+ const detectedComponentType = rawArgs.component_type || (detectedOutputType === "ui" ? detectComponentType(component_description) : "general");
2157
+ if (!rawArgs.component_type && detectedOutputType === "ui") {
2158
+ defaultsUsed.push(`component type → ${detectedComponentType} (auto-detected)`);
2159
+ }
1225
2160
  // Dual-theme mode: For UI outputs without explicit theme, provide BOTH themes by default
1226
2161
  const isDualThemeMode = detectedOutputType === "ui" && !rawArgs.theme;
1227
2162
  const theme = rawArgs.theme || "light";
@@ -1603,7 +2538,8 @@ const example = "Sonance";
1603
2538
 
1604
2539
  **Brand**: ${brand.toUpperCase()}
1605
2540
  **Theme**: Light AND Dark Mode (automatically implemented)
1606
- **Output Type**: ${detectedOutputType.toUpperCase()}
2541
+ **Output Type**: ${detectedOutputType.toUpperCase()}${detectedComponentType !== "general" ? `
2542
+ **Component Type**: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}` : ""}
1607
2543
  **Logo**: ${logoPreferenceLabel}
1608
2544
  ${defaultsNotice}
1609
2545
 
@@ -1691,6 +2627,15 @@ function Logo() {
1691
2627
 
1692
2628
  ${outputTypeGuidance[detectedOutputType]}
1693
2629
 
2630
+ ${detectedComponentType !== "general" ? `---
2631
+
2632
+ ## Component Type: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}
2633
+
2634
+ ${COMPONENT_TYPE_GUIDANCE[detectedComponentType]}
2635
+
2636
+ ### Related Components in This Category
2637
+ ${COMPONENT_CATEGORIES[detectedComponentType]?.slice(0, 8).join(", ") || "See component library"}` : ""}
2638
+
1694
2639
  ## Typography (All Brands)
1695
2640
  - **Font Family**: Montserrat
1696
2641
  - **Headlines**: font-weight 300 (Light) or 500 (Medium)
@@ -1713,13 +2658,23 @@ Now design the **${component_description}** with **BOTH light and dark mode supp
1713
2658
 
1714
2659
  **Brand**: ${brand.toUpperCase()}
1715
2660
  **Theme**: ${theme.charAt(0).toUpperCase() + theme.slice(1)}
1716
- **Output Type**: ${detectedOutputType.toUpperCase()}
2661
+ **Output Type**: ${detectedOutputType.toUpperCase()}${detectedComponentType !== "general" && detectedOutputType === "ui" ? `
2662
+ **Component Type**: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}` : ""}
1717
2663
  **Logo**: ${logoPreferenceLabel}
1718
2664
  ${defaultsNotice}
1719
2665
  ${singleThemeTokens}
1720
2666
 
1721
2667
  ${outputTypeGuidance[detectedOutputType]}
1722
2668
 
2669
+ ${detectedComponentType !== "general" && detectedOutputType === "ui" ? `---
2670
+
2671
+ ## Component Type: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}
2672
+
2673
+ ${COMPONENT_TYPE_GUIDANCE[detectedComponentType]}
2674
+
2675
+ ### Related Components in This Category
2676
+ ${COMPONENT_CATEGORIES[detectedComponentType]?.slice(0, 8).join(", ") || "See component library"}` : ""}
2677
+
1723
2678
  ## Typography (All Brands)
1724
2679
  - **Font Family**: Montserrat
1725
2680
  - **Headlines**: font-weight 300 (Light) or 500 (Medium)
@@ -2039,10 +2994,148 @@ Now design the **${component_description}** following these tokens and principle
2039
2994
  tierLabel = "Rebuild Required";
2040
2995
  deliverable = false;
2041
2996
  }
2042
- // Build gaps list from failed checks
2043
- const gaps = checkResults
2997
+ // Fix suggestions for common issues by check key
2998
+ const fixSuggestions = {
2999
+ // UI fixes
3000
+ usesSemantic: `**Fix: Use Semantic Colors**
3001
+ \`\`\`jsx
3002
+ // Instead of: bg-[#343d46] or bg-blue-500
3003
+ // Use: bg-primary or bg-background
3004
+ <div className="bg-primary text-primary-foreground">
3005
+ <div className="bg-background text-foreground">
3006
+ \`\`\``,
3007
+ noHardcodedColors: `**Fix: Replace Hardcoded Hex Colors**
3008
+ \`\`\`jsx
3009
+ // Instead of: className="bg-[#343d46] text-[#ffffff]"
3010
+ // Use CSS variables: className="bg-primary text-primary-foreground"
3011
+ // Or in CSS: var(--primary), var(--foreground)
3012
+ \`\`\``,
3013
+ usesCorrectRadius: `**Fix: Use Sharp Corners**
3014
+ \`\`\`jsx
3015
+ // Instead of: rounded-lg, rounded-xl, rounded-full
3016
+ // Use: rounded-sm (2px) or rounded-none
3017
+ <button className="rounded-sm bg-primary px-6 py-3">
3018
+ \`\`\``,
3019
+ usesMontserrat: `**Fix: Configure Montserrat Font**
3020
+ \`\`\`jsx
3021
+ // In globals.css or layout:
3022
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;700&display=swap');
3023
+
3024
+ // In Tailwind config:
3025
+ fontFamily: { sans: ['Montserrat', 'sans-serif'] }
3026
+ \`\`\``,
3027
+ noFontBold: `**Fix: Use Lighter Font Weights**
3028
+ \`\`\`jsx
3029
+ // Instead of: font-bold (700)
3030
+ // Use: font-light (300), font-normal (400), or font-medium (500)
3031
+ <h1 className="font-light text-4xl">Heading</h1>
3032
+ <h2 className="font-medium text-2xl">Subheading</h2>
3033
+ \`\`\``,
3034
+ hasDarkMode: `**Fix: Add Dark Mode Support**
3035
+ \`\`\`jsx
3036
+ // Use Tailwind dark: prefix
3037
+ <div className="bg-white dark:bg-sonance-charcoal text-gray-900 dark:text-white">
3038
+
3039
+ // Or use CSS variables that change with theme
3040
+ <div className="bg-background text-foreground">
3041
+ \`\`\``,
3042
+ hasGenerousSpacing: `**Fix: Add Generous Spacing**
3043
+ \`\`\`jsx
3044
+ // For sections: py-20 or py-24 (80-96px)
3045
+ <section className="py-20 px-6 lg:py-24 lg:px-8">
3046
+
3047
+ // For buttons: px-6 py-3
3048
+ <button className="px-6 py-3">
3049
+
3050
+ // For cards: p-6 or p-8
3051
+ <div className="p-6 lg:p-8">
3052
+ \`\`\``,
3053
+ hasHoverStates: `**Fix: Add Hover States**
3054
+ \`\`\`jsx
3055
+ <button className="bg-primary hover:bg-primary/90 transition-colors">
3056
+ <a className="text-primary hover:text-primary/80 hover:underline">
3057
+ \`\`\``,
3058
+ hasFocusStates: `**Fix: Add Focus States**
3059
+ \`\`\`jsx
3060
+ <button className="focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2">
3061
+ <input className="focus-visible:ring-2 focus-visible:ring-primary focus-visible:border-primary">
3062
+ \`\`\``,
3063
+ hasAccessibility: `**Fix: Add Accessibility Attributes**
3064
+ \`\`\`jsx
3065
+ <button aria-label="Close dialog">
3066
+ <img alt="Product image description" />
3067
+ <nav role="navigation" aria-label="Main menu">
3068
+ \`\`\``,
3069
+ hasTransitions: `**Fix: Add Smooth Transitions**
3070
+ \`\`\`jsx
3071
+ // For color changes
3072
+ <button className="transition-colors duration-200">
3073
+
3074
+ // For all properties
3075
+ <div className="transition-all duration-300 ease-out">
3076
+ \`\`\``,
3077
+ // Doc fixes
3078
+ usesPrimaryColor: `**Fix: Use ${evalPalette.name} Primary Color**
3079
+ \`\`\`python
3080
+ # python-docx
3081
+ from docx.shared import RGBColor
3082
+ CHARCOAL = RGBColor(0x34, 0x3D, 0x46)
3083
+ run.font.color.rgb = CHARCOAL
3084
+
3085
+ # ReportLab
3086
+ from reportlab.lib.colors import HexColor
3087
+ CHARCOAL = HexColor('#343d46')
3088
+ \`\`\``,
3089
+ hasFontConfig: `**Fix: Configure Montserrat Font**
3090
+ \`\`\`python
3091
+ # python-docx
3092
+ run.font.name = 'Montserrat'
3093
+ # Or fallback
3094
+ run.font.name = 'Arial'
3095
+
3096
+ # ReportLab
3097
+ from reportlab.pdfbase.ttfonts import TTFont
3098
+ pdfmetrics.registerFont(TTFont('Montserrat', 'Montserrat-Regular.ttf'))
3099
+ \`\`\``,
3100
+ usesProperWeights: `**Fix: Use Correct Font Weights**
3101
+ \`\`\`python
3102
+ # DON'T use bold=True for headings
3103
+ # Instead, use medium weight or increase font size
3104
+
3105
+ # python-docx
3106
+ run.font.bold = False # Use font size for hierarchy instead
3107
+ heading_run.font.size = Pt(24) # Large size = visual weight
3108
+ \`\`\``,
3109
+ // Marketing fixes
3110
+ usesInlineColors: `**Fix: Use Inline Brand Colors**
3111
+ \`\`\`html
3112
+ <td style="background-color: #343d46; color: #ffffff;">
3113
+ <a style="color: #00A3E1;">
3114
+ \`\`\``,
3115
+ hasFallbackFonts: `**Fix: Add Font Fallbacks**
3116
+ \`\`\`html
3117
+ style="font-family: 'Montserrat', Arial, Helvetica, sans-serif;"
3118
+ \`\`\``,
3119
+ usesTableLayout: `**Fix: Use Table-Based Layout**
3120
+ \`\`\`html
3121
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="600">
3122
+ <tr>
3123
+ <td align="center" style="padding: 20px;">
3124
+ Content here
3125
+ </td>
3126
+ </tr>
3127
+ </table>
3128
+ \`\`\``,
3129
+ };
3130
+ // Build gaps list from failed checks with fix suggestions
3131
+ const gapsWithFixes = checkResults
2044
3132
  .filter(c => !c.passed)
2045
- .map(c => c.description);
3133
+ .map(c => ({
3134
+ description: c.description,
3135
+ key: c.check,
3136
+ fix: fixSuggestions[c.check] || null
3137
+ }));
3138
+ const gaps = gapsWithFixes.map(g => g.description);
2046
3139
  // Group results by category
2047
3140
  const byCategory = {};
2048
3141
  for (const result of checkResults) {
@@ -2085,7 +3178,8 @@ ${categoryScores.map(c => `| ${c.category} | ${c.earned} | ${c.max} | ${c.checkM
2085
3178
 
2086
3179
  ${gaps.length > 0 ? `## Gaps to Address
2087
3180
 
2088
- ${gaps.map((gap, i) => `${i + 1}. ${gap}`).join('\n')}
3181
+ ${gapsWithFixes.map((gap, i) => `### ${i + 1}. ${gap.description}
3182
+ ${gap.fix ? `\n${gap.fix}\n` : ""}`).join('\n')}
2089
3183
 
2090
3184
  ---` : "## No major gaps identified — excellent work!"}
2091
3185
 
@@ -2093,7 +3187,9 @@ ${gaps.map((gap, i) => `${i + 1}. ${gap}`).join('\n')}
2093
3187
 
2094
3188
  ${deliverable
2095
3189
  ? "This output meets Sonance brand standards. Consider polish improvements for Tier 5."
2096
- : `Iterate on the gaps above to reach Tier 4 (85+). Current gap: ${85 - totalScore} points.`}
3190
+ : `Iterate on the gaps above to reach Tier 4 (85+). Current gap: ${85 - totalScore} points.
3191
+
3192
+ > **Tip**: Use \`design_component\` or \`redesign_app\` tools for comprehensive guidance on implementing these fixes.`}
2097
3193
  `;
2098
3194
  return {
2099
3195
  content: [{ type: "text", text: evalResponse }],
@@ -2943,6 +4039,679 @@ ${rebrandArgs.source_format === "word" ? `
2943
4039
  content: rebrandContentBlocks,
2944
4040
  };
2945
4041
  }
4042
+ // ============================================
4043
+ // NEW APP DEVELOPMENT TOOLS
4044
+ // ============================================
4045
+ case "design_app": {
4046
+ const designAppArgs = args;
4047
+ const brand = designAppArgs.brand || "sonance";
4048
+ const appType = designAppArgs.app_type || "general";
4049
+ const features = designAppArgs.features || ["forms", "navigation"]; // Core features by default
4050
+ const framework = designAppArgs.framework || "nextjs";
4051
+ const palette = BRAND_PALETTES[brand];
4052
+ // Determine which components to include based on features
4053
+ const neededComponents = new Set();
4054
+ // Always include core components
4055
+ neededComponents.add("button");
4056
+ neededComponents.add("card");
4057
+ for (const feature of features) {
4058
+ const featureComponents = FEATURE_TO_COMPONENTS[feature] || [];
4059
+ for (const comp of featureComponents) {
4060
+ neededComponents.add(comp);
4061
+ }
4062
+ }
4063
+ // Add app-type specific components
4064
+ if (appType === "dashboard") {
4065
+ ["card", "chart", "table", "badge", "tabs"].forEach(c => neededComponents.add(c));
4066
+ }
4067
+ else if (appType === "marketing") {
4068
+ ["button", "card", "badge", "image"].forEach(c => neededComponents.add(c));
4069
+ }
4070
+ // Read the components
4071
+ const componentsDir = getAssetPath("components");
4072
+ const componentSources = [];
4073
+ for (const compName of neededComponents) {
4074
+ const filePath = path.join(componentsDir, `${compName}.tsx`);
4075
+ if (fs.existsSync(filePath)) {
4076
+ try {
4077
+ const source = fs.readFileSync(filePath, "utf-8");
4078
+ componentSources.push({ name: compName, source });
4079
+ }
4080
+ catch {
4081
+ // Skip if can't read
4082
+ }
4083
+ }
4084
+ }
4085
+ // Read CSS theme
4086
+ let cssTheme = "";
4087
+ try {
4088
+ cssTheme = fs.readFileSync(getAssetPath("css"), "utf-8");
4089
+ }
4090
+ catch {
4091
+ cssTheme = "/* Could not load CSS theme */";
4092
+ }
4093
+ // Read utils
4094
+ let utils = "";
4095
+ try {
4096
+ utils = fs.readFileSync(getAssetPath("utils"), "utf-8");
4097
+ }
4098
+ catch {
4099
+ utils = "/* Could not load utils */";
4100
+ }
4101
+ // Get page layout template
4102
+ const layout = PAGE_LAYOUTS[appType] || PAGE_LAYOUTS.dashboard;
4103
+ // Build the response
4104
+ const setupInstructions = framework === "nextjs" ? `
4105
+ ## Setup Instructions (Next.js)
4106
+
4107
+ 1. Create a new Next.js project:
4108
+ \`\`\`bash
4109
+ npx create-next-app@latest my-${brand}-app --typescript --tailwind --eslint --app
4110
+ cd my-${brand}-app
4111
+ \`\`\`
4112
+
4113
+ 2. Install dependencies:
4114
+ \`\`\`bash
4115
+ npm install clsx tailwind-merge class-variance-authority lucide-react
4116
+ npm install @radix-ui/react-slot @radix-ui/react-dialog @radix-ui/react-dropdown-menu
4117
+ \`\`\`
4118
+
4119
+ 3. Copy the globals.css content below to \`src/app/globals.css\`
4120
+ 4. Copy the utils to \`src/lib/utils.ts\`
4121
+ 5. Create \`src/components/ui/\` and add the component files
4122
+ ` : `
4123
+ ## Setup Instructions (React + Vite)
4124
+
4125
+ 1. Create a new React project:
4126
+ \`\`\`bash
4127
+ npm create vite@latest my-${brand}-app -- --template react-ts
4128
+ cd my-${brand}-app
4129
+ npm install
4130
+ \`\`\`
4131
+
4132
+ 2. Install Tailwind CSS:
4133
+ \`\`\`bash
4134
+ npm install -D tailwindcss postcss autoprefixer
4135
+ npx tailwindcss init -p
4136
+ \`\`\`
4137
+
4138
+ 3. Install dependencies:
4139
+ \`\`\`bash
4140
+ npm install clsx tailwind-merge class-variance-authority lucide-react
4141
+ npm install @radix-ui/react-slot @radix-ui/react-dialog
4142
+ \`\`\`
4143
+
4144
+ 4. Copy the CSS and component files as described below
4145
+ `;
4146
+ const response = `# ${palette.name} App Starter Kit
4147
+
4148
+ **App Type:** ${appType}
4149
+ **Framework:** ${framework}
4150
+ **Brand:** ${palette.name}
4151
+ **Features:** ${features.join(", ")}
4152
+ **Components Included:** ${componentSources.length}
4153
+
4154
+ ---
4155
+
4156
+ ${setupInstructions}
4157
+
4158
+ ---
4159
+
4160
+ ## Global Styles (copy to globals.css)
4161
+
4162
+ \`\`\`css
4163
+ ${cssTheme}
4164
+ \`\`\`
4165
+
4166
+ ---
4167
+
4168
+ ## Utility Functions (copy to lib/utils.ts)
4169
+
4170
+ \`\`\`typescript
4171
+ ${utils}
4172
+ \`\`\`
4173
+
4174
+ ---
4175
+
4176
+ ## Required Components (${componentSources.length} total)
4177
+
4178
+ ${componentSources.map(c => `### ${c.name}.tsx
4179
+
4180
+ \`\`\`tsx
4181
+ ${c.source}
4182
+ \`\`\``).join("\n\n")}
4183
+
4184
+ ---
4185
+
4186
+ ## Page Template: ${appType}
4187
+
4188
+ ${layout.description}
4189
+
4190
+ ${layout.structure}
4191
+
4192
+ **Recommended Layout Classes:**
4193
+ - Grid: \`${layout.gridLayout}\`
4194
+ - Spacing: \`${layout.spacing}\`
4195
+
4196
+ \`\`\`tsx
4197
+ ${layout.template}
4198
+ \`\`\`
4199
+
4200
+ ---
4201
+
4202
+ ## Quick Reference
4203
+
4204
+ ### ${palette.name} Brand Colors
4205
+ - **Primary:** ${palette.primary} (bg-primary, text-primary)
4206
+ - **Secondary:** ${palette.secondary}
4207
+ - **Accent:** ${palette.accent}
4208
+ - **Preferred Theme:** ${palette.preferredTheme}
4209
+
4210
+ ### Typography
4211
+ - Font: Montserrat
4212
+ - Headlines: font-light (300) or font-medium (500)
4213
+ - Body: font-normal (400)
4214
+ - **Never use font-bold (700) for headlines**
4215
+
4216
+ ### Spacing
4217
+ - Section padding: py-20 or py-24
4218
+ - Component gaps: gap-4 to gap-8
4219
+ - Card padding: p-6
4220
+
4221
+ ### Border Radius
4222
+ - Use rounded-sm (2px) or rounded-none
4223
+ - **Never use rounded-lg or rounded-xl**
4224
+
4225
+ ---
4226
+
4227
+ Now you have everything needed to build a ${palette.name}-branded ${appType} application!
4228
+ `;
4229
+ // Embed logos
4230
+ const logoMap = {
4231
+ sonance: {
4232
+ light: "/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png",
4233
+ dark: "/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png",
4234
+ },
4235
+ iport: {
4236
+ light: "/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png",
4237
+ dark: "/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png",
4238
+ },
4239
+ blaze: {
4240
+ light: "/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png",
4241
+ dark: "/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png",
4242
+ },
4243
+ };
4244
+ const contentBlocks = [];
4245
+ const lightLogo = await embedLogo(logoMap[brand].light);
4246
+ const darkLogo = await embedLogo(logoMap[brand].dark);
4247
+ if (lightLogo) {
4248
+ contentBlocks.push({ type: "text", text: `## Logo for Light Backgrounds\n**Path:** \`${logoMap[brand].light}\`` });
4249
+ contentBlocks.push(lightLogo);
4250
+ }
4251
+ if (darkLogo) {
4252
+ contentBlocks.push({ type: "text", text: `## Logo for Dark Backgrounds\n**Path:** \`${logoMap[brand].dark}\`` });
4253
+ contentBlocks.push(darkLogo);
4254
+ }
4255
+ contentBlocks.push({ type: "text", text: response });
4256
+ return {
4257
+ content: contentBlocks,
4258
+ };
4259
+ }
4260
+ case "redesign_app": {
4261
+ const redesignArgs = args;
4262
+ if (!redesignArgs.existing_code) {
4263
+ return {
4264
+ content: [{ type: "text", text: "Error: existing_code is required" }],
4265
+ isError: true,
4266
+ };
4267
+ }
4268
+ const brand = redesignArgs.brand || "sonance";
4269
+ const code = redesignArgs.existing_code;
4270
+ const fileType = redesignArgs.file_type || "component";
4271
+ const filePath = redesignArgs.file_path || "unknown file";
4272
+ const palette = BRAND_PALETTES[brand];
4273
+ const violations = [];
4274
+ const lines = code.split("\n");
4275
+ // Check for hardcoded colors
4276
+ const hardcodedColorPatterns = [
4277
+ { pattern: /bg-\[#[0-9a-fA-F]{6}\]/g, fix: "bg-primary or bg-secondary" },
4278
+ { pattern: /text-\[#[0-9a-fA-F]{6}\]/g, fix: "text-foreground or text-primary" },
4279
+ { pattern: /border-\[#[0-9a-fA-F]{6}\]/g, fix: "border-border or border-primary" },
4280
+ { pattern: /#[0-9a-fA-F]{6}/g, fix: "Use CSS variable: var(--primary), var(--secondary), etc." },
4281
+ ];
4282
+ // Check for non-brand Tailwind colors
4283
+ const nonBrandColors = [
4284
+ { pattern: /bg-(blue|red|green|yellow|purple|pink|indigo|orange)-\d{2,3}/g, fix: "bg-primary or bg-accent" },
4285
+ { pattern: /text-(blue|red|green|yellow|purple|pink|indigo|orange)-\d{2,3}/g, fix: "text-foreground or text-primary" },
4286
+ { pattern: /border-(blue|red|green|yellow|purple|pink|indigo|orange)-\d{2,3}/g, fix: "border-border" },
4287
+ ];
4288
+ // Check for wrong border radius
4289
+ const radiusPatterns = [
4290
+ { pattern: /rounded-(lg|xl|2xl|3xl|full)/g, fix: "rounded-sm (2px) or rounded-none" },
4291
+ ];
4292
+ // Check for wrong font weights
4293
+ const fontPatterns = [
4294
+ { pattern: /font-bold/g, fix: "font-medium (500) or font-light (300) for headlines" },
4295
+ ];
4296
+ // Check for missing dark mode
4297
+ const hasDarkMode = /dark:|\.dark\s|data-theme/i.test(code);
4298
+ // Check for missing hover states
4299
+ const hasHoverStates = /hover:/i.test(code);
4300
+ const hasFocusStates = /focus:|focus-visible:/i.test(code);
4301
+ // Scan each line
4302
+ lines.forEach((line, index) => {
4303
+ const lineNum = index + 1;
4304
+ for (const { pattern, fix } of hardcodedColorPatterns) {
4305
+ const matches = line.match(pattern);
4306
+ if (matches) {
4307
+ for (const match of matches) {
4308
+ violations.push({
4309
+ type: "hardcoded_color",
4310
+ severity: "critical",
4311
+ line: lineNum,
4312
+ match,
4313
+ fix,
4314
+ });
4315
+ }
4316
+ }
4317
+ }
4318
+ for (const { pattern, fix } of nonBrandColors) {
4319
+ const matches = line.match(pattern);
4320
+ if (matches) {
4321
+ for (const match of matches) {
4322
+ violations.push({
4323
+ type: "non_brand_color",
4324
+ severity: "critical",
4325
+ line: lineNum,
4326
+ match,
4327
+ fix,
4328
+ });
4329
+ }
4330
+ }
4331
+ }
4332
+ for (const { pattern, fix } of radiusPatterns) {
4333
+ const matches = line.match(pattern);
4334
+ if (matches) {
4335
+ for (const match of matches) {
4336
+ violations.push({
4337
+ type: "wrong_radius",
4338
+ severity: "critical",
4339
+ line: lineNum,
4340
+ match,
4341
+ fix,
4342
+ });
4343
+ }
4344
+ }
4345
+ }
4346
+ for (const { pattern, fix } of fontPatterns) {
4347
+ const matches = line.match(pattern);
4348
+ if (matches) {
4349
+ for (const match of matches) {
4350
+ violations.push({
4351
+ type: "wrong_font",
4352
+ severity: "critical",
4353
+ line: lineNum,
4354
+ match,
4355
+ fix,
4356
+ });
4357
+ }
4358
+ }
4359
+ }
4360
+ });
4361
+ // Add warnings
4362
+ if (!hasDarkMode) {
4363
+ violations.push({
4364
+ type: "missing_dark_mode",
4365
+ severity: "warning",
4366
+ match: "No dark mode support",
4367
+ fix: "Add dark: variants for theme switching",
4368
+ });
4369
+ }
4370
+ if (!hasHoverStates) {
4371
+ violations.push({
4372
+ type: "missing_hover",
4373
+ severity: "warning",
4374
+ match: "No hover states found",
4375
+ fix: "Add hover: variants to interactive elements",
4376
+ });
4377
+ }
4378
+ if (!hasFocusStates) {
4379
+ violations.push({
4380
+ type: "missing_focus",
4381
+ severity: "warning",
4382
+ match: "No focus states found",
4383
+ fix: "Add focus-visible:ring-2 focus-visible:ring-primary to interactive elements",
4384
+ });
4385
+ }
4386
+ // Group violations
4387
+ const criticalViolations = violations.filter(v => v.severity === "critical");
4388
+ const warnings = violations.filter(v => v.severity === "warning");
4389
+ // Generate transformation code
4390
+ let transformedCode = code;
4391
+ // Apply fixes
4392
+ transformedCode = transformedCode
4393
+ .replace(/rounded-(lg|xl|2xl|3xl)/g, "rounded-sm")
4394
+ .replace(/rounded-full/g, "rounded-sm")
4395
+ .replace(/font-bold/g, "font-medium")
4396
+ .replace(/bg-blue-\d{2,3}/g, "bg-primary")
4397
+ .replace(/bg-red-\d{2,3}/g, "bg-destructive")
4398
+ .replace(/bg-green-\d{2,3}/g, "bg-primary")
4399
+ .replace(/text-blue-\d{2,3}/g, "text-primary")
4400
+ .replace(/text-gray-900/g, "text-foreground")
4401
+ .replace(/text-gray-500/g, "text-muted-foreground")
4402
+ .replace(/border-gray-\d{2,3}/g, "border-border");
4403
+ const response = `# Redesign Analysis: ${filePath}
4404
+
4405
+ **Target Brand:** ${palette.name}
4406
+ **File Type:** ${fileType}
4407
+ **Issues Found:** ${violations.length} (${criticalViolations.length} critical, ${warnings.length} warnings)
4408
+
4409
+ ---
4410
+
4411
+ ## Detected Issues
4412
+
4413
+ ### Critical (Must Fix)
4414
+
4415
+ ${criticalViolations.length > 0 ? criticalViolations.map(v => `
4416
+ **${v.type.replace(/_/g, " ")}**
4417
+ - Line ${v.line}: \`${v.match}\`
4418
+ - Fix: ${v.fix}
4419
+ `).join("\n") : "No critical issues found."}
4420
+
4421
+ ### Warnings
4422
+
4423
+ ${warnings.length > 0 ? warnings.map(v => `
4424
+ - **${v.type.replace(/_/g, " ")}**: ${v.match}
4425
+ - Fix: ${v.fix}
4426
+ `).join("\n") : "No warnings."}
4427
+
4428
+ ---
4429
+
4430
+ ## Transformation Code
4431
+
4432
+ ### Before
4433
+ \`\`\`tsx
4434
+ ${code.substring(0, 500)}${code.length > 500 ? "\n// ... (truncated)" : ""}
4435
+ \`\`\`
4436
+
4437
+ ### After
4438
+ \`\`\`tsx
4439
+ ${transformedCode.substring(0, 500)}${transformedCode.length > 500 ? "\n// ... (truncated)" : ""}
4440
+ \`\`\`
4441
+
4442
+ ---
4443
+
4444
+ ## Required CSS Variables
4445
+
4446
+ Add these to your globals.css if not present:
4447
+
4448
+ \`\`\`css
4449
+ :root {
4450
+ --background: #ffffff;
4451
+ --foreground: ${palette.primary};
4452
+ --primary: ${palette.primary};
4453
+ --primary-foreground: #ffffff;
4454
+ --secondary: ${palette.secondary};
4455
+ --secondary-foreground: ${palette.primary};
4456
+ --muted: ${palette.secondary};
4457
+ --muted-foreground: #64748b;
4458
+ --accent: ${palette.accent};
4459
+ --accent-foreground: #ffffff;
4460
+ --destructive: #ef4444;
4461
+ --border: ${palette.secondary};
4462
+ --input: ${palette.secondary};
4463
+ --ring: ${palette.accent};
4464
+ --radius: 0.125rem; /* 2px - sharp corners */
4465
+ }
4466
+
4467
+ .dark {
4468
+ --background: ${palette.preferredTheme === "dark" ? palette.primary : "#1a1a1a"};
4469
+ --foreground: #ffffff;
4470
+ --primary: ${palette.accent};
4471
+ --primary-foreground: ${palette.primary};
4472
+ --muted: #333333;
4473
+ --border: rgba(255, 255, 255, 0.1);
4474
+ }
4475
+ \`\`\`
4476
+
4477
+ ---
4478
+
4479
+ ## Step-by-Step Transformation Plan
4480
+
4481
+ ### Step 1: Update globals.css
4482
+ Copy the CSS variables above to your globals.css file.
4483
+
4484
+ ### Step 2: Find and Replace Colors
4485
+ Use your editor's find/replace to apply these changes:
4486
+
4487
+ | Find | Replace |
4488
+ |------|---------|
4489
+ | \`bg-blue-*\` | \`bg-primary\` |
4490
+ | \`bg-gray-100\` | \`bg-muted\` |
4491
+ | \`text-gray-900\` | \`text-foreground\` |
4492
+ | \`text-gray-500\` | \`text-muted-foreground\` |
4493
+ | \`border-gray-*\` | \`border-border\` |
4494
+
4495
+ ### Step 3: Fix Border Radius
4496
+ | Find | Replace |
4497
+ |------|---------|
4498
+ | \`rounded-lg\` | \`rounded-sm\` |
4499
+ | \`rounded-xl\` | \`rounded-sm\` |
4500
+ | \`rounded-2xl\` | \`rounded-sm\` |
4501
+
4502
+ ### Step 4: Fix Font Weights
4503
+ | Find | Replace |
4504
+ |------|---------|
4505
+ | \`font-bold\` (in headlines) | \`font-medium\` or \`font-light\` |
4506
+
4507
+ ### Step 5: Add Dark Mode
4508
+ Add \`dark:\` variants to your classes:
4509
+ \`\`\`tsx
4510
+ className="bg-white dark:bg-background text-foreground dark:text-white"
4511
+ \`\`\`
4512
+
4513
+ ### Step 6: Add Hover/Focus States
4514
+ \`\`\`tsx
4515
+ className="hover:bg-primary/90 focus-visible:ring-2 focus-visible:ring-primary"
4516
+ \`\`\`
4517
+
4518
+ ---
4519
+
4520
+ ## Verification Checklist
4521
+
4522
+ - [ ] All hardcoded colors replaced with semantic variables
4523
+ - [ ] Border radius is rounded-sm or rounded-none
4524
+ - [ ] Font weights are 300, 400, or 500 (no 700/bold for headlines)
4525
+ - [ ] Dark mode support added
4526
+ - [ ] Hover states on interactive elements
4527
+ - [ ] Focus states for accessibility
4528
+ `;
4529
+ return {
4530
+ content: [{ type: "text", text: response }],
4531
+ };
4532
+ }
4533
+ case "analyze_for_redesign": {
4534
+ const analyzeArgs = args;
4535
+ if (!analyzeArgs.files || analyzeArgs.files.length === 0) {
4536
+ return {
4537
+ content: [{ type: "text", text: "Error: files array is required with at least one file" }],
4538
+ isError: true,
4539
+ };
4540
+ }
4541
+ const brand = analyzeArgs.brand || "sonance";
4542
+ const palette = BRAND_PALETTES[brand];
4543
+ const files = analyzeArgs.files;
4544
+ const analyses = [];
4545
+ for (const file of files) {
4546
+ const issues = [];
4547
+ const content = file.content;
4548
+ // Check for various issues
4549
+ if (/#[0-9a-fA-F]{6}/.test(content)) {
4550
+ issues.push("hardcoded colors");
4551
+ }
4552
+ if (/bg-(blue|red|green|yellow|purple|pink|indigo|orange)-\d{2,3}/.test(content)) {
4553
+ issues.push("non-brand Tailwind colors");
4554
+ }
4555
+ if (/rounded-(lg|xl|2xl|3xl|full)/.test(content)) {
4556
+ issues.push("wrong border radius");
4557
+ }
4558
+ if (/font-bold/.test(content)) {
4559
+ issues.push("wrong font weight");
4560
+ }
4561
+ if (!/dark:/.test(content) && /className/.test(content)) {
4562
+ issues.push("missing dark mode");
4563
+ }
4564
+ // Determine priority based on file path and issues
4565
+ let priority = "low";
4566
+ if (file.path.includes("globals.css") || file.path.includes("tailwind.config")) {
4567
+ priority = "critical";
4568
+ }
4569
+ else if (issues.length >= 3) {
4570
+ priority = "high";
4571
+ }
4572
+ else if (issues.length >= 1) {
4573
+ priority = "medium";
4574
+ }
4575
+ analyses.push({
4576
+ path: file.path,
4577
+ priority,
4578
+ issues: issues.length,
4579
+ issueTypes: issues,
4580
+ });
4581
+ }
4582
+ // Sort by priority
4583
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
4584
+ analyses.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
4585
+ const totalIssues = analyses.reduce((sum, a) => sum + a.issues, 0);
4586
+ const filesNeedingChanges = analyses.filter(a => a.issues > 0).length;
4587
+ const response = `# Codebase Redesign Plan: ${palette.name}
4588
+
4589
+ ## Summary
4590
+
4591
+ - **Files Analyzed:** ${files.length}
4592
+ - **Files Needing Changes:** ${filesNeedingChanges}
4593
+ - **Total Issues:** ${totalIssues}
4594
+ - **Target Brand:** ${palette.name}
4595
+
4596
+ ---
4597
+
4598
+ ## Priority Order
4599
+
4600
+ ${analyses.filter(a => a.issues > 0).map((a, i) => `
4601
+ ### ${i + 1}. ${a.path} (${a.priority.toUpperCase()})
4602
+
4603
+ **Issues Found:** ${a.issues}
4604
+ ${a.issueTypes.map(t => `- ${t}`).join("\n")}
4605
+ **Action:** ${a.priority === "critical" ? "Update first - affects entire app" : a.priority === "high" ? "Update after critical files" : "Update when convenient"}
4606
+ `).join("\n")}
4607
+
4608
+ ---
4609
+
4610
+ ## Recommended Workflow
4611
+
4612
+ ### Phase 1: Foundation (Critical)
4613
+ ${analyses.filter(a => a.priority === "critical").map(a => `1. Update \`${a.path}\``).join("\n") || "No critical files identified."}
4614
+
4615
+ ### Phase 2: Components (High Priority)
4616
+ ${analyses.filter(a => a.priority === "high").map(a => `- Update \`${a.path}\` (${a.issues} issues)`).join("\n") || "No high priority files."}
4617
+
4618
+ ### Phase 3: Polish (Medium/Low)
4619
+ ${analyses.filter(a => a.priority === "medium" || a.priority === "low").map(a => `- Update \`${a.path}\``).join("\n") || "No remaining files."}
4620
+
4621
+ ---
4622
+
4623
+ ## Global Search/Replace Commands
4624
+
4625
+ Run these across your codebase:
4626
+
4627
+ \`\`\`bash
4628
+ # Fix border radius
4629
+ find . -name "*.tsx" -exec sed -i 's/rounded-lg/rounded-sm/g' {} \\;
4630
+ find . -name "*.tsx" -exec sed -i 's/rounded-xl/rounded-sm/g' {} \\;
4631
+
4632
+ # Fix font weights
4633
+ find . -name "*.tsx" -exec sed -i 's/font-bold/font-medium/g' {} \\;
4634
+
4635
+ # Fix common color patterns
4636
+ find . -name "*.tsx" -exec sed -i 's/bg-blue-500/bg-primary/g' {} \\;
4637
+ find . -name "*.tsx" -exec sed -i 's/text-gray-900/text-foreground/g' {} \\;
4638
+ \`\`\`
4639
+
4640
+ ---
4641
+
4642
+ ## CSS Variables Needed
4643
+
4644
+ Ensure your globals.css includes:
4645
+
4646
+ \`\`\`css
4647
+ :root {
4648
+ --primary: ${palette.primary};
4649
+ --secondary: ${palette.secondary};
4650
+ --accent: ${palette.accent};
4651
+ --background: #ffffff;
4652
+ --foreground: ${palette.primary};
4653
+ --border: ${palette.secondary};
4654
+ --radius: 0.125rem;
4655
+ }
4656
+ \`\`\`
4657
+
4658
+ ---
4659
+
4660
+ ## Next Steps
4661
+
4662
+ 1. Start with \`globals.css\` or \`tailwind.config\` to set up brand colors
4663
+ 2. Work through high-priority component files
4664
+ 3. Test each component after updating
4665
+ 4. Verify dark mode works correctly
4666
+ 5. Run \`evaluate_design\` on key components to verify brand compliance
4667
+
4668
+ Use \`redesign_app\` tool on individual files for detailed transformation instructions.
4669
+ `;
4670
+ return {
4671
+ content: [{ type: "text", text: response }],
4672
+ };
4673
+ }
4674
+ case "get_components_by_category": {
4675
+ const categoryArgs = args;
4676
+ const category = categoryArgs.category;
4677
+ if (!category || !COMPONENT_CATEGORIES[category]) {
4678
+ return {
4679
+ content: [{
4680
+ type: "text",
4681
+ text: `Error: Invalid category '${category}'. Available categories: ${Object.keys(COMPONENT_CATEGORIES).join(", ")}`,
4682
+ }],
4683
+ isError: true,
4684
+ };
4685
+ }
4686
+ const components = COMPONENT_CATEGORIES[category];
4687
+ const componentsDir = getAssetPath("components");
4688
+ let response = `# ${category.charAt(0).toUpperCase() + category.slice(1)} Components (${components.length} components)\n\n`;
4689
+ response += `Use these components for ${category === "forms" ? "building forms and capturing user input" :
4690
+ category === "navigation" ? "navigation and menus" :
4691
+ category === "feedback" ? "user feedback and notifications" :
4692
+ category === "overlays" ? "modals, dialogs, and overlays" :
4693
+ category === "data-display" ? "displaying data and content" :
4694
+ category === "layout" ? "page structure and layouts" :
4695
+ "utility purposes"}.\n\n---\n\n`;
4696
+ for (const compName of components) {
4697
+ const filePath = path.join(componentsDir, `${compName}.tsx`);
4698
+ if (fs.existsSync(filePath)) {
4699
+ try {
4700
+ const content = fs.readFileSync(filePath, "utf-8");
4701
+ response += `## ${compName}\n\nFile: \`components/ui/${compName}.tsx\`\n\n\`\`\`tsx\n${content}\n\`\`\`\n\n---\n\n`;
4702
+ }
4703
+ catch {
4704
+ response += `## ${compName}\n\n*Could not read component file*\n\n---\n\n`;
4705
+ }
4706
+ }
4707
+ else {
4708
+ response += `## ${compName}\n\n*Component file not found*\n\n---\n\n`;
4709
+ }
4710
+ }
4711
+ return {
4712
+ content: [{ type: "text", text: response }],
4713
+ };
4714
+ }
2946
4715
  default:
2947
4716
  return {
2948
4717
  content: [{ type: "text", text: `Unknown tool: ${name}` }],