sonance-brand-mcp 1.2.5 → 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 (189) 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 +142 -0
  11. package/dist/assets/components/alert-dialog.tsx +143 -0
  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 +70 -0
  15. package/dist/assets/components/aspect-ratio.tsx +8 -0
  16. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  17. package/dist/assets/components/autocomplete.tsx +3 -3
  18. package/dist/assets/components/avatar.stories.tsx +5 -5
  19. package/dist/assets/components/avatar.tsx +67 -23
  20. package/dist/assets/components/badge.stories.tsx +10 -10
  21. package/dist/assets/components/badge.tsx +3 -3
  22. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  23. package/dist/assets/components/breadcrumbs.tsx +13 -8
  24. package/dist/assets/components/button.stories.tsx +74 -74
  25. package/dist/assets/components/button.tsx +2 -0
  26. package/dist/assets/components/calendar.stories.tsx +11 -11
  27. package/dist/assets/components/calendar.tsx +4 -4
  28. package/dist/assets/components/card.stories.tsx +22 -22
  29. package/dist/assets/components/card.tsx +7 -3
  30. package/dist/assets/components/carousel.stories.tsx +158 -0
  31. package/dist/assets/components/carousel.tsx +264 -0
  32. package/dist/assets/components/chart.stories.tsx +376 -0
  33. package/dist/assets/components/chart.tsx +384 -0
  34. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  35. package/dist/assets/components/checkbox-group.tsx +3 -3
  36. package/dist/assets/components/checkbox.stories.tsx +23 -20
  37. package/dist/assets/components/checkbox.tsx +13 -6
  38. package/dist/assets/components/code.stories.tsx +24 -24
  39. package/dist/assets/components/code.tsx +22 -27
  40. package/dist/assets/components/collapsible.stories.tsx +128 -0
  41. package/dist/assets/components/collapsible.tsx +10 -0
  42. package/dist/assets/components/command.stories.tsx +183 -0
  43. package/dist/assets/components/command.tsx +171 -0
  44. package/dist/assets/components/context-menu.stories.tsx +159 -0
  45. package/dist/assets/components/context-menu.tsx +214 -0
  46. package/dist/assets/components/date-input.stories.tsx +9 -9
  47. package/dist/assets/components/date-input.tsx +2 -2
  48. package/dist/assets/components/date-picker.stories.tsx +9 -9
  49. package/dist/assets/components/date-picker.tsx +3 -3
  50. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  51. package/dist/assets/components/date-range-picker.tsx +3 -3
  52. package/dist/assets/components/dialog.stories.tsx +40 -40
  53. package/dist/assets/components/dialog.tsx +8 -12
  54. package/dist/assets/components/divider.stories.tsx +30 -30
  55. package/dist/assets/components/divider.tsx +34 -35
  56. package/dist/assets/components/drawer.stories.tsx +32 -31
  57. package/dist/assets/components/drawer.tsx +7 -6
  58. package/dist/assets/components/dropdown-menu.tsx +213 -0
  59. package/dist/assets/components/dropdown.stories.tsx +12 -12
  60. package/dist/assets/components/dropdown.tsx +5 -5
  61. package/dist/assets/components/form.stories.tsx +30 -29
  62. package/dist/assets/components/form.tsx +5 -5
  63. package/dist/assets/components/hover-card.stories.tsx +115 -0
  64. package/dist/assets/components/hover-card.tsx +35 -0
  65. package/dist/assets/components/image.stories.tsx +48 -25
  66. package/dist/assets/components/image.tsx +8 -5
  67. package/dist/assets/components/input-otp.stories.tsx +15 -15
  68. package/dist/assets/components/input-otp.tsx +5 -5
  69. package/dist/assets/components/input.stories.tsx +30 -25
  70. package/dist/assets/components/input.tsx +7 -4
  71. package/dist/assets/components/kbd.stories.tsx +34 -34
  72. package/dist/assets/components/kbd.tsx +9 -9
  73. package/dist/assets/components/link.stories.tsx +36 -36
  74. package/dist/assets/components/link.tsx +4 -0
  75. package/dist/assets/components/listbox.stories.tsx +5 -5
  76. package/dist/assets/components/listbox.tsx +4 -4
  77. package/dist/assets/components/menubar.stories.tsx +208 -0
  78. package/dist/assets/components/menubar.tsx +247 -0
  79. package/dist/assets/components/navbar.stories.tsx +24 -24
  80. package/dist/assets/components/navbar.tsx +8 -14
  81. package/dist/assets/components/navigation-menu.stories.tsx +239 -0
  82. package/dist/assets/components/navigation-menu.tsx +135 -0
  83. package/dist/assets/components/number-input.stories.tsx +11 -11
  84. package/dist/assets/components/number-input.tsx +3 -3
  85. package/dist/assets/components/pagination.stories.tsx +13 -13
  86. package/dist/assets/components/pagination.tsx +6 -6
  87. package/dist/assets/components/popover.stories.tsx +35 -35
  88. package/dist/assets/components/popover.tsx +98 -15
  89. package/dist/assets/components/progress.stories.tsx +5 -5
  90. package/dist/assets/components/progress.tsx +5 -5
  91. package/dist/assets/components/radio-group.stories.tsx +7 -7
  92. package/dist/assets/components/radio-group.tsx +3 -3
  93. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  94. package/dist/assets/components/range-calendar.tsx +3 -3
  95. package/dist/assets/components/resizable.stories.tsx +197 -0
  96. package/dist/assets/components/resizable.tsx +47 -0
  97. package/dist/assets/components/scroll-area.stories.tsx +123 -0
  98. package/dist/assets/components/scroll-area.tsx +48 -0
  99. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  100. package/dist/assets/components/scroll-shadow.tsx +31 -9
  101. package/dist/assets/components/select.stories.tsx +20 -19
  102. package/dist/assets/components/select.tsx +10 -6
  103. package/dist/assets/components/separator.tsx +32 -0
  104. package/dist/assets/components/sheet.tsx +137 -0
  105. package/dist/assets/components/sidebar.stories.tsx +351 -0
  106. package/dist/assets/components/sidebar.tsx +757 -0
  107. package/dist/assets/components/skeleton.stories.tsx +3 -3
  108. package/dist/assets/components/skeleton.tsx +2 -2
  109. package/dist/assets/components/slider.stories.tsx +6 -6
  110. package/dist/assets/components/slider.tsx +3 -3
  111. package/dist/assets/components/spacer.stories.tsx +11 -11
  112. package/dist/assets/components/spacer.tsx +2 -2
  113. package/dist/assets/components/spinner.stories.tsx +8 -8
  114. package/dist/assets/components/spinner.tsx +5 -5
  115. package/dist/assets/components/switch.stories.tsx +24 -20
  116. package/dist/assets/components/switch.tsx +14 -6
  117. package/dist/assets/components/table.stories.tsx +7 -7
  118. package/dist/assets/components/table.tsx +8 -8
  119. package/dist/assets/components/tabs.stories.tsx +37 -37
  120. package/dist/assets/components/tabs.tsx +3 -3
  121. package/dist/assets/components/textarea.stories.tsx +13 -12
  122. package/dist/assets/components/textarea.tsx +3 -3
  123. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  124. package/dist/assets/components/theme-toggle.tsx +2 -2
  125. package/dist/assets/components/time-input.stories.tsx +16 -16
  126. package/dist/assets/components/time-input.tsx +2 -2
  127. package/dist/assets/components/toast.stories.tsx +8 -5
  128. package/dist/assets/components/toast.tsx +6 -6
  129. package/dist/assets/components/toggle-group.stories.tsx +153 -0
  130. package/dist/assets/components/toggle-group.tsx +61 -0
  131. package/dist/assets/components/toggle.stories.tsx +77 -0
  132. package/dist/assets/components/toggle.tsx +46 -0
  133. package/dist/assets/components/tooltip.stories.tsx +49 -27
  134. package/dist/assets/components/tooltip.tsx +23 -90
  135. package/dist/assets/components/user.stories.tsx +23 -23
  136. package/dist/assets/components/user.tsx +7 -4
  137. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  138. package/dist/assets/dev-tools/index.ts +10 -0
  139. package/dist/assets/globals.css +39 -0
  140. package/dist/assets/logos/40th-anniversary/Sonance_40_Logo_CMYK_BEAM_BLUE_40_AND_BEAM_DARK.png +0 -0
  141. package/dist/assets/logos/Sonance logo dark mode.png +0 -0
  142. package/dist/assets/logos/Sonance logo light mode.png +0 -0
  143. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png +0 -0
  144. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png +0 -0
  145. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_White_RGB_05162025.png +0 -0
  146. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png +0 -0
  147. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png +0 -0
  148. package/dist/assets/logos/james/James_Logo_Black_CMYK.png +0 -0
  149. package/dist/assets/logos/james/James_Logo_Black_RGB.png +0 -0
  150. package/dist/assets/logos/james/James_Logo_LtGray_CMYK.png +0 -0
  151. package/dist/assets/logos/james/James_Logo_LtGray_RGB.png +0 -0
  152. package/dist/assets/logos/james/James_Logo_Polished_RGB.png +0 -0
  153. package/dist/assets/logos/james/James_Logo_Reverse_CMYK.png +0 -0
  154. package/dist/assets/logos/james/James_Logo_Reverse_RGB.png +0 -0
  155. package/dist/assets/logos/james/James_Logo_White_CMYK.png +0 -0
  156. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Dark_RGB.png +0 -0
  157. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Light_RGB.png +0 -0
  158. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Dark_RGB.png +0 -0
  159. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Light_RGB.png +0 -0
  160. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Reverse_RGB.png +0 -0
  161. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Black_RGB.png +0 -0
  162. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Reverse_RGB.png +0 -0
  163. package/dist/assets/logos/sonance/Sonance_Logo_2C_Dark_RGB.png +0 -0
  164. package/dist/assets/logos/sonance/Sonance_Logo_2C_Light_RGB.png +0 -0
  165. package/dist/assets/logos/sonance/Sonance_Logo_2C_Reverse_RGB.png +0 -0
  166. package/dist/assets/logos/sonance/Sonance_Logo_Black_RGB.png +0 -0
  167. package/dist/assets/logos/sonance/Sonance_Logo_Grayscale_RGB.png +0 -0
  168. package/dist/assets/logos/sonance/Sonance_Logo_Reverse_RGB.png +0 -0
  169. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Dark_CMYK.png +0 -0
  170. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Light_CMYK.png +0 -0
  171. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Dark_RGB.png +0 -0
  172. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Light_RGB.png +0 -0
  173. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Reverse_RGB.png +0 -0
  174. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Black_RGB.png +0 -0
  175. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Grayscale_RGB.png +0 -0
  176. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Reverse_RGB.png +0 -0
  177. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Dark.png +0 -0
  178. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Light.png +0 -0
  179. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Dark.png +0 -0
  180. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Light.png +0 -0
  181. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png +0 -0
  182. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png +0 -0
  183. package/dist/assets/logos/trufig/TrufigLogo_Black.png +0 -0
  184. package/dist/assets/logos/trufig/TrufigLogo_Light.png +0 -0
  185. package/dist/assets/logos/trufig/TrufigWatermark_Black.png +0 -0
  186. package/dist/assets/logos/trufig/TrufigWatermark_Light.png +0 -0
  187. package/dist/assets/styles/brand-overrides.css +37 -0
  188. package/dist/index.js +2055 -15
  189. 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) {
@@ -306,6 +522,76 @@ function getAssetPath(assetType) {
306
522
  }
307
523
  }
308
524
  }
525
+ // ============================================
526
+ // LOGO PATH RESOLUTION
527
+ // ============================================
528
+ /**
529
+ * Resolve a logo path to an absolute file path
530
+ * Handles both bundled and dev mode, with path sanitization
531
+ */
532
+ function resolveLogoPath(logoPath) {
533
+ // Sanitize path to prevent directory traversal
534
+ const sanitized = logoPath
535
+ .replace(/^\/+/, '') // Remove leading slashes
536
+ .replace(/\.{2,}/g, '') // Remove .. sequences
537
+ .replace(/\/+/g, '/'); // Normalize multiple slashes
538
+ // Remove "logos/" prefix if present (manifest has /logos/xxx format)
539
+ const normalizedPath = sanitized.replace(/^logos\//, '');
540
+ if (IS_BUNDLED) {
541
+ // In bundled mode, logos are in dist/assets/logos/
542
+ const logosDir = path.join(BUNDLED_ASSETS, "logos");
543
+ return path.join(logosDir, normalizedPath);
544
+ }
545
+ else {
546
+ // In dev mode, logos are in public/logos/
547
+ const logosDir = path.join(DEV_PROJECT_ROOT, "public/logos");
548
+ return path.join(logosDir, normalizedPath);
549
+ }
550
+ }
551
+ /**
552
+ * Get MIME type for image file based on extension
553
+ */
554
+ function getImageMimeType(filePath) {
555
+ const ext = path.extname(filePath).toLowerCase();
556
+ switch (ext) {
557
+ case '.png': return 'image/png';
558
+ case '.jpg':
559
+ case '.jpeg': return 'image/jpeg';
560
+ case '.svg': return 'image/svg+xml';
561
+ case '.gif': return 'image/gif';
562
+ case '.webp': return 'image/webp';
563
+ default: return 'application/octet-stream';
564
+ }
565
+ }
566
+ /**
567
+ * Embed a logo as base64 image content for MCP responses
568
+ * Returns an MCP-compatible image content block or null if logo not found
569
+ */
570
+ async function embedLogo(logoPath) {
571
+ try {
572
+ const resolvedPath = resolveLogoPath(logoPath);
573
+ if (!resolvedPath)
574
+ return null;
575
+ // Check if file exists
576
+ if (!fs.existsSync(resolvedPath)) {
577
+ console.error(`Logo not found: ${resolvedPath}`);
578
+ return null;
579
+ }
580
+ // Read file as base64
581
+ const fileBuffer = fs.readFileSync(resolvedPath);
582
+ const base64Data = fileBuffer.toString('base64');
583
+ const mimeType = getImageMimeType(resolvedPath);
584
+ return {
585
+ type: "image",
586
+ data: base64Data,
587
+ mimeType,
588
+ };
589
+ }
590
+ catch (error) {
591
+ console.error(`Error embedding logo: ${error}`);
592
+ return null;
593
+ }
594
+ }
309
595
  /**
310
596
  * Detect output type from user's request/prompt
311
597
  */
@@ -466,6 +752,610 @@ function getBrandColorsForDocsMulti(brand = "sonance") {
466
752
  black: hexToFormats("#000000"),
467
753
  };
468
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
+ };
469
1359
  /**
470
1360
  * Get regex patterns to detect brand colors in code
471
1361
  */
@@ -545,6 +1435,50 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
545
1435
  catch (e) {
546
1436
  // Components directory might not exist
547
1437
  }
1438
+ // Add logo resources
1439
+ try {
1440
+ let logoList;
1441
+ if (IS_BUNDLED) {
1442
+ const manifestPath = getAssetPath("logos");
1443
+ logoList = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1444
+ }
1445
+ else {
1446
+ // Scan directory in development
1447
+ const logosDir = getAssetPath("logos");
1448
+ function listLogoFiles(dir, prefix = "") {
1449
+ let results = [];
1450
+ if (!fs.existsSync(dir))
1451
+ return results;
1452
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
1453
+ for (const entry of entries) {
1454
+ if (entry.isDirectory()) {
1455
+ results = [...results, ...listLogoFiles(path.join(dir, entry.name), `${prefix}${entry.name}/`)];
1456
+ }
1457
+ else if (/\.(png|svg|jpg|jpeg)$/i.test(entry.name)) {
1458
+ results.push(`/logos/${prefix}${entry.name}`);
1459
+ }
1460
+ }
1461
+ return results;
1462
+ }
1463
+ logoList = listLogoFiles(logosDir);
1464
+ }
1465
+ for (const logoPath of logoList) {
1466
+ // Convert /logos/sonance/file.png to sonance/file.png for URI
1467
+ const uriPath = logoPath.replace(/^\/logos\//, '');
1468
+ const fileName = path.basename(logoPath, path.extname(logoPath));
1469
+ const folder = path.dirname(uriPath);
1470
+ const mimeType = getImageMimeType(logoPath);
1471
+ resources.push({
1472
+ uri: `sonance://logo/${uriPath}`,
1473
+ name: `${fileName} Logo`,
1474
+ description: `${folder !== '.' ? folder + ' - ' : ''}Brand logo image file`,
1475
+ mimeType: mimeType,
1476
+ });
1477
+ }
1478
+ }
1479
+ catch (e) {
1480
+ // Logo resources might not be available
1481
+ }
548
1482
  return { resources };
549
1483
  });
550
1484
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
@@ -572,6 +1506,24 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
572
1506
  };
573
1507
  }
574
1508
  }
1509
+ // Handle logo resources
1510
+ if (uri.startsWith("sonance://logo/")) {
1511
+ const logoPath = uri.replace("sonance://logo/", "");
1512
+ const resolvedPath = resolveLogoPath(logoPath);
1513
+ if (!resolvedPath || !fs.existsSync(resolvedPath)) {
1514
+ throw new Error(`Logo not found: ${logoPath}`);
1515
+ }
1516
+ const imageBuffer = fs.readFileSync(resolvedPath);
1517
+ const base64Data = imageBuffer.toString('base64');
1518
+ const mimeType = getImageMimeType(resolvedPath);
1519
+ return {
1520
+ contents: [{
1521
+ uri,
1522
+ mimeType,
1523
+ blob: base64Data, // Use blob for binary resources
1524
+ }],
1525
+ };
1526
+ }
575
1527
  throw new Error(`Resource not found: ${uri}`);
576
1528
  });
577
1529
  // ============================================
@@ -639,6 +1591,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
639
1591
  required: [],
640
1592
  },
641
1593
  },
1594
+ {
1595
+ name: "get_logo",
1596
+ description: "Returns a brand logo image as base64-encoded data that AI can view and analyze. Use list_logos first to see available logos, then use this tool to retrieve the actual image.",
1597
+ inputSchema: {
1598
+ type: "object",
1599
+ properties: {
1600
+ logo_path: {
1601
+ type: "string",
1602
+ description: "The path to the logo from list_logos output (e.g., '/logos/sonance/Sonance_Logo_2C_Dark_RGB.png' or 'sonance/Sonance_Logo_2C_Dark_RGB.png')",
1603
+ },
1604
+ },
1605
+ required: ["logo_path"],
1606
+ },
1607
+ },
642
1608
  {
643
1609
  name: "get_full_library",
644
1610
  description: "RECOMMENDED FOR APP REDESIGNS & FULL APPLICATIONS: Returns the complete component library including brand guidelines, CSS theme, utilities, and all component source code. USE THIS TOOL when the user asks to: redesign an application, rebrand an app, build a full application, update an entire codebase to use Sonance/IPORT/Blaze brand, or any multi-file/multi-component project. This is the go-to tool for comprehensive brand implementation across an entire project.",
@@ -650,7 +1616,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
650
1616
  },
651
1617
  {
652
1618
  name: "design_component",
653
- description: "Returns brand-specific design tokens, colors, implementation rules, and logo assets for designing a component. Smart defaults: Sonance/Light/Default logo lockup if not specified. Auto-detects output type (ui/doc/marketing/tech) if not specified.",
1619
+ description: "Returns brand-specific design tokens, colors, implementation rules, and AUTO-EMBEDS the correct logo image(s) for designing a component. For UI outputs, embeds both light and dark theme logos. Smart defaults: Sonance+James+IPORT lockup as default logo. Auto-detects output type (ui/doc/marketing/tech).",
654
1620
  inputSchema: {
655
1621
  type: "object",
656
1622
  properties: {
@@ -666,8 +1632,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
666
1632
  },
667
1633
  logo_preference: {
668
1634
  type: "string",
669
- enum: ["default", "sonance", "iport", "blaze"],
670
- description: "Which logo to use. 'default' = Sonance+James+IPORT lockup. Or specify 'sonance', 'iport', or 'blaze' for individual brand logos. Defaults to 'default' if not specified.",
1635
+ enum: ["default", "sonance", "iport", "blaze", "james"],
1636
+ description: "Which logo to use. 'default' = Sonance+James+IPORT lockup. Or specify 'sonance', 'iport', 'blaze', or 'james' for individual brand logos. Defaults to 'default' if not specified.",
671
1637
  },
672
1638
  output_type: {
673
1639
  type: "string",
@@ -678,6 +1644,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
678
1644
  type: "string",
679
1645
  description: "What the user wants to design (e.g., 'hero section', 'pricing card', 'navigation bar', 'proposal PDF', 'email template')",
680
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
+ },
681
1652
  },
682
1653
  required: ["component_description"],
683
1654
  },
@@ -755,7 +1726,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
755
1726
  },
756
1727
  {
757
1728
  name: "rebrand_document",
758
- description: "Returns universal rebranding instructions for converting ANY existing document to a specific brand (Sonance, IPORT, or Blaze). Works with any document format - provides color replacements, font substitutions, and logo placement rules that apply regardless of document structure.",
1729
+ description: "Returns universal rebranding instructions for converting ANY existing document to a specific brand (Sonance, IPORT, or Blaze) and AUTO-EMBEDS both light and dark logo variants for immediate use. Provides color replacements, font substitutions, and logo placement rules. Default: Sonance+James+IPORT lockup for Sonance brand.",
759
1730
  inputSchema: {
760
1731
  type: "object",
761
1732
  properties: {
@@ -777,6 +1748,111 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
777
1748
  required: ["source_format"],
778
1749
  },
779
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
+ },
780
1856
  ],
781
1857
  };
782
1858
  });
@@ -941,6 +2017,66 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
941
2017
  };
942
2018
  }
943
2019
  }
2020
+ case "get_logo": {
2021
+ const logoPath = args.logo_path;
2022
+ if (!logoPath) {
2023
+ return {
2024
+ content: [{ type: "text", text: "Error: logo_path is required. Use list_logos to see available logos." }],
2025
+ isError: true,
2026
+ };
2027
+ }
2028
+ try {
2029
+ const resolvedPath = resolveLogoPath(logoPath);
2030
+ if (!resolvedPath) {
2031
+ return {
2032
+ content: [{ type: "text", text: `Error: Invalid logo path: ${logoPath}` }],
2033
+ isError: true,
2034
+ };
2035
+ }
2036
+ if (!fs.existsSync(resolvedPath)) {
2037
+ // Try to help user find the right logo
2038
+ let suggestion = '\n\nUse list_logos to see available logos.';
2039
+ if (IS_BUNDLED) {
2040
+ try {
2041
+ const manifestPath = getAssetPath("logos");
2042
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
2043
+ suggestion = `\n\nAvailable logos include:\n${manifest.slice(0, 5).map((p) => ` - ${p}`).join('\n')}\n ... and ${manifest.length - 5} more. Use list_logos to see all.`;
2044
+ }
2045
+ catch {
2046
+ // Keep default suggestion
2047
+ }
2048
+ }
2049
+ return {
2050
+ content: [{ type: "text", text: `Error: Logo not found: ${logoPath}${suggestion}` }],
2051
+ isError: true,
2052
+ };
2053
+ }
2054
+ // Read and encode the image
2055
+ const imageBuffer = fs.readFileSync(resolvedPath);
2056
+ const base64Data = imageBuffer.toString('base64');
2057
+ const mimeType = getImageMimeType(resolvedPath);
2058
+ const fileName = path.basename(resolvedPath);
2059
+ return {
2060
+ content: [
2061
+ {
2062
+ type: "image",
2063
+ data: base64Data,
2064
+ mimeType: mimeType,
2065
+ },
2066
+ {
2067
+ type: "text",
2068
+ text: `**Logo:** ${fileName}\n**Path:** ${logoPath}\n**Size:** ${imageBuffer.length} bytes\n**Format:** ${mimeType}`,
2069
+ },
2070
+ ],
2071
+ };
2072
+ }
2073
+ catch (e) {
2074
+ return {
2075
+ content: [{ type: "text", text: `Error reading logo: ${e}` }],
2076
+ isError: true,
2077
+ };
2078
+ }
2079
+ }
944
2080
  case "get_full_library": {
945
2081
  try {
946
2082
  let fullLibrary = "# Sonance Component Library\n\n";
@@ -1016,6 +2152,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1016
2152
  if (!rawArgs.output_type) {
1017
2153
  defaultsUsed.push(`output type → ${detectedOutputType} (auto-detected)`);
1018
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
+ }
1019
2160
  // Dual-theme mode: For UI outputs without explicit theme, provide BOTH themes by default
1020
2161
  const isDualThemeMode = detectedOutputType === "ui" && !rawArgs.theme;
1021
2162
  const theme = rawArgs.theme || "light";
@@ -1044,11 +2185,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1044
2185
  light: "/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png",
1045
2186
  dark: "/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png",
1046
2187
  },
2188
+ james: {
2189
+ light: "/logos/james/James_Logo_Black_RGB.png",
2190
+ dark: "/logos/james/James_Logo_Reverse_RGB.png",
2191
+ },
1047
2192
  };
1048
2193
  const logoPath = logoMap[logoPreference]?.[theme] || logoMap.default[theme];
1049
2194
  const logoPreferenceLabel = logoPreference === "default"
1050
2195
  ? "Sonance + James + IPORT Lockup"
1051
- : `${logoPreference.charAt(0).toUpperCase() + logoPreference.slice(1)} Only`;
2196
+ : logoPreference === "james"
2197
+ ? "James Only"
2198
+ : `${logoPreference.charAt(0).toUpperCase() + logoPreference.slice(1)} Only`;
1052
2199
  // Brand-specific design tokens
1053
2200
  const brandTokens = {
1054
2201
  sonance: {
@@ -1391,7 +2538,8 @@ const example = "Sonance";
1391
2538
 
1392
2539
  **Brand**: ${brand.toUpperCase()}
1393
2540
  **Theme**: Light AND Dark Mode (automatically implemented)
1394
- **Output Type**: ${detectedOutputType.toUpperCase()}
2541
+ **Output Type**: ${detectedOutputType.toUpperCase()}${detectedComponentType !== "general" ? `
2542
+ **Component Type**: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}` : ""}
1395
2543
  **Logo**: ${logoPreferenceLabel}
1396
2544
  ${defaultsNotice}
1397
2545
 
@@ -1479,7 +2627,16 @@ function Logo() {
1479
2627
 
1480
2628
  ${outputTypeGuidance[detectedOutputType]}
1481
2629
 
1482
- ## Typography (All Brands)
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
+
2639
+ ## Typography (All Brands)
1483
2640
  - **Font Family**: Montserrat
1484
2641
  - **Headlines**: font-weight 300 (Light) or 500 (Medium)
1485
2642
  - **Body**: font-weight 400 (Regular), line-height 1.6
@@ -1501,13 +2658,23 @@ Now design the **${component_description}** with **BOTH light and dark mode supp
1501
2658
 
1502
2659
  **Brand**: ${brand.toUpperCase()}
1503
2660
  **Theme**: ${theme.charAt(0).toUpperCase() + theme.slice(1)}
1504
- **Output Type**: ${detectedOutputType.toUpperCase()}
2661
+ **Output Type**: ${detectedOutputType.toUpperCase()}${detectedComponentType !== "general" && detectedOutputType === "ui" ? `
2662
+ **Component Type**: ${detectedComponentType.charAt(0).toUpperCase() + detectedComponentType.slice(1)}` : ""}
1505
2663
  **Logo**: ${logoPreferenceLabel}
1506
2664
  ${defaultsNotice}
1507
2665
  ${singleThemeTokens}
1508
2666
 
1509
2667
  ${outputTypeGuidance[detectedOutputType]}
1510
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
+
1511
2678
  ## Typography (All Brands)
1512
2679
  - **Font Family**: Montserrat
1513
2680
  - **Headlines**: font-weight 300 (Light) or 500 (Medium)
@@ -1524,8 +2691,35 @@ ${outputTypeGuidance[detectedOutputType]}
1524
2691
 
1525
2692
  Now design the **${component_description}** following these tokens and principles.`;
1526
2693
  }
2694
+ // Embed logos in response
2695
+ const contentBlocks = [];
2696
+ if (isDualThemeMode) {
2697
+ // Dual-theme mode: embed both light and dark theme logos
2698
+ const lightLogoPath = logoMap[logoPreference]?.light || logoMap.default.light;
2699
+ const darkLogoPath = logoMap[logoPreference]?.dark || logoMap.default.dark;
2700
+ const lightLogo = await embedLogo(lightLogoPath);
2701
+ const darkLogo = await embedLogo(darkLogoPath);
2702
+ if (lightLogo) {
2703
+ contentBlocks.push({ type: "text", text: `## Logo for Light Backgrounds\n**Path:** \`${lightLogoPath}\`` });
2704
+ contentBlocks.push(lightLogo);
2705
+ }
2706
+ if (darkLogo) {
2707
+ contentBlocks.push({ type: "text", text: `## Logo for Dark Backgrounds\n**Path:** \`${darkLogoPath}\`` });
2708
+ contentBlocks.push(darkLogo);
2709
+ }
2710
+ }
2711
+ else {
2712
+ // Single-theme mode: embed the appropriate logo
2713
+ const embeddedLogo = await embedLogo(logoPath);
2714
+ if (embeddedLogo) {
2715
+ contentBlocks.push({ type: "text", text: `## Brand Logo (${theme === "light" ? "for Light Backgrounds" : "for Dark Backgrounds"})\n**Path:** \`${logoPath}\`` });
2716
+ contentBlocks.push(embeddedLogo);
2717
+ }
2718
+ }
2719
+ // Add the main response text
2720
+ contentBlocks.push({ type: "text", text: response });
1527
2721
  return {
1528
- content: [{ type: "text", text: response }],
2722
+ content: contentBlocks,
1529
2723
  };
1530
2724
  }
1531
2725
  case "evaluate_design": {
@@ -1800,10 +2994,148 @@ Now design the **${component_description}** following these tokens and principle
1800
2994
  tierLabel = "Rebuild Required";
1801
2995
  deliverable = false;
1802
2996
  }
1803
- // Build gaps list from failed checks
1804
- 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
1805
3132
  .filter(c => !c.passed)
1806
- .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);
1807
3139
  // Group results by category
1808
3140
  const byCategory = {};
1809
3141
  for (const result of checkResults) {
@@ -1846,7 +3178,8 @@ ${categoryScores.map(c => `| ${c.category} | ${c.earned} | ${c.max} | ${c.checkM
1846
3178
 
1847
3179
  ${gaps.length > 0 ? `## Gaps to Address
1848
3180
 
1849
- ${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')}
1850
3183
 
1851
3184
  ---` : "## No major gaps identified — excellent work!"}
1852
3185
 
@@ -1854,7 +3187,9 @@ ${gaps.map((gap, i) => `${i + 1}. ${gap}`).join('\n')}
1854
3187
 
1855
3188
  ${deliverable
1856
3189
  ? "This output meets Sonance brand standards. Consider polish improvements for Tier 5."
1857
- : `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.`}
1858
3193
  `;
1859
3194
  return {
1860
3195
  content: [{ type: "text", text: evalResponse }],
@@ -2668,8 +4003,713 @@ ${rebrandArgs.source_format === "word" ? `
2668
4003
 
2669
4004
  **Remember:** The goal is to make the document unmistakably ${palette.name} while preserving its original structure and content.
2670
4005
  `;
4006
+ // Embed logos for rebrand_document
4007
+ // Use the Sonance+James+IPORT lockup as default for Sonance brand
4008
+ const rebrandLogoMap = {
4009
+ sonance: {
4010
+ light: "/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png",
4011
+ dark: "/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png",
4012
+ },
4013
+ iport: {
4014
+ light: "/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png",
4015
+ dark: "/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png",
4016
+ },
4017
+ blaze: {
4018
+ light: "/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png",
4019
+ dark: "/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png",
4020
+ },
4021
+ };
4022
+ const rebrandContentBlocks = [];
4023
+ // For documents, provide both light and dark logo variants
4024
+ const lightLogoPath = rebrandLogoMap[targetBrand].light;
4025
+ const darkLogoPath = rebrandLogoMap[targetBrand].dark;
4026
+ const lightLogo = await embedLogo(lightLogoPath);
4027
+ const darkLogo = await embedLogo(darkLogoPath);
4028
+ if (lightLogo) {
4029
+ rebrandContentBlocks.push({ type: "text", text: `## Logo for Light Backgrounds\n**Use on white/light document backgrounds**\n**Path:** \`${lightLogoPath}\`` });
4030
+ rebrandContentBlocks.push(lightLogo);
4031
+ }
4032
+ if (darkLogo) {
4033
+ rebrandContentBlocks.push({ type: "text", text: `## Logo for Dark Backgrounds\n**Use on dark/colored document backgrounds**\n**Path:** \`${darkLogoPath}\`` });
4034
+ rebrandContentBlocks.push(darkLogo);
4035
+ }
4036
+ // Add the main guide text
4037
+ rebrandContentBlocks.push({ type: "text", text: rebrandGuide });
2671
4038
  return {
2672
- content: [{ type: "text", text: rebrandGuide }],
4039
+ content: rebrandContentBlocks,
4040
+ };
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 }],
2673
4713
  };
2674
4714
  }
2675
4715
  default: