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