@thesage/mcp 0.6.0 → 0.8.1

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/index.mjs CHANGED
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ COMPONENT_CATEGORIES,
4
+ COMPONENT_REGISTRY,
5
+ getAllComponentNames,
6
+ getComponent,
7
+ getComponentCount,
8
+ getComponentsByCategory,
9
+ searchComponents
10
+ } from "./chunk-L4SOXQCS.mjs";
2
11
 
3
12
  // src/index.ts
4
13
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -7,2198 +16,10 @@ import {
7
16
  CallToolRequestSchema,
8
17
  ListToolsRequestSchema
9
18
  } from "@modelcontextprotocol/sdk/types.js";
10
-
11
- // src/registry.ts
12
- var COMPONENT_CATEGORIES = {
13
- actions: {
14
- label: "Actions",
15
- description: "Interactive elements that trigger behaviors",
16
- count: 5
17
- },
18
- forms: {
19
- label: "Forms",
20
- description: "Input controls for data collection",
21
- count: 18
22
- },
23
- navigation: {
24
- label: "Navigation",
25
- description: "Moving through content and hierarchy",
26
- count: 10
27
- },
28
- overlays: {
29
- label: "Overlays",
30
- description: "Contextual content that appears above the main UI",
31
- count: 11
32
- },
33
- feedback: {
34
- label: "Feedback",
35
- description: "Communicating system state and user action results",
36
- count: 7
37
- },
38
- "data-display": {
39
- label: "Data Display",
40
- description: "Presenting information in structured formats",
41
- count: 16
42
- },
43
- layout: {
44
- label: "Layout",
45
- description: "Spatial organization and structural elements",
46
- count: 17
47
- },
48
- backgrounds: {
49
- label: "Backgrounds",
50
- description: "Animated background effects and decorative elements",
51
- count: 3
52
- },
53
- cursor: {
54
- label: "Cursor",
55
- description: "Custom cursor effects and interactions",
56
- count: 2
57
- },
58
- motion: {
59
- label: "Motion",
60
- description: "Animation components and motion effects",
61
- count: 1
62
- },
63
- blocks: {
64
- label: "Blocks",
65
- description: "Composed page sections and layouts",
66
- count: 2
67
- }
68
- };
69
- var COMPONENT_REGISTRY = {
70
- // ============================================================================
71
- // ACTIONS (3)
72
- // ============================================================================
73
- button: {
74
- name: "Button",
75
- category: "actions",
76
- description: "Primary interaction element with multiple variants for different use cases",
77
- keywords: ["button", "click", "action", "submit", "cta", "interactive"],
78
- useCases: [
79
- "Form submission",
80
- "Navigation triggers",
81
- "Action confirmation",
82
- "Call-to-action elements"
83
- ],
84
- dependencies: ["@radix-ui/react-slot"],
85
- radixPrimitive: "@radix-ui/react-slot",
86
- props: {
87
- variant: { type: "'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'", default: "'default'", description: "Visual style variant" },
88
- size: { type: "'sm' | 'default' | 'lg' | 'icon'", default: "'default'", description: "Size variant" },
89
- disabled: { type: "boolean", default: "false", description: "Disable interaction" },
90
- asChild: { type: "boolean", default: "false", description: "Render as child element via Radix Slot" }
91
- },
92
- example: `<Button variant="outline" size="sm" onClick={handleClick}>Click Me</Button>`
93
- },
94
- toggle: {
95
- name: "Toggle",
96
- category: "actions",
97
- description: "Binary state toggle with pressed/unpressed states",
98
- keywords: ["toggle", "switch", "binary", "on-off", "state"],
99
- useCases: [
100
- "Toolbar buttons",
101
- "Feature toggles",
102
- "View mode switches",
103
- "Filter activation"
104
- ],
105
- dependencies: ["@radix-ui/react-toggle"],
106
- radixPrimitive: "@radix-ui/react-toggle",
107
- props: {
108
- pressed: { type: "boolean", description: "Controlled pressed state" },
109
- onPressedChange: { type: "(pressed: boolean) => void", description: "Callback on press change" },
110
- variant: { type: "'default' | 'outline'", default: "'default'", description: "Visual variant" },
111
- size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" },
112
- disabled: { type: "boolean", default: "false", description: "Disable interaction" }
113
- },
114
- example: `<Toggle pressed={isBold} onPressedChange={setIsBold}><Bold className="h-4 w-4" /></Toggle>`
115
- },
116
- "toggle-group": {
117
- name: "ToggleGroup",
118
- category: "actions",
119
- description: "Multiple toggles with single or multi-select modes",
120
- keywords: ["toggle", "group", "selection", "multi-select", "options"],
121
- useCases: [
122
- "Text formatting toolbars",
123
- "View mode selection",
124
- "Filter groups",
125
- "Option selection"
126
- ],
127
- dependencies: ["@radix-ui/react-toggle-group"],
128
- radixPrimitive: "@radix-ui/react-toggle-group",
129
- props: {
130
- type: { type: "'single' | 'multiple'", description: "Selection mode", required: true },
131
- value: { type: "string | string[]", description: "Selected value(s)" },
132
- onValueChange: { type: "(value) => void", description: "Callback on value change" },
133
- variant: { type: "'default' | 'outline'", default: "'default'", description: "Visual variant" },
134
- size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" }
135
- },
136
- subComponents: ["ToggleGroupItem"],
137
- example: `<ToggleGroup type="single" value={align} onValueChange={setAlign}>
138
- <ToggleGroupItem value="left">Left</ToggleGroupItem>
139
- <ToggleGroupItem value="center">Center</ToggleGroupItem>
140
- </ToggleGroup>`
141
- },
142
- // ============================================================================
143
- // FORMS (11)
144
- // ============================================================================
145
- checkbox: {
146
- name: "Checkbox",
147
- category: "forms",
148
- description: "Boolean selection control for single or grouped choices",
149
- keywords: ["checkbox", "selection", "boolean", "multi-select", "form"],
150
- useCases: [
151
- "Multi-select options",
152
- "Terms acceptance",
153
- "Feature preferences",
154
- "Bulk actions"
155
- ],
156
- dependencies: ["@radix-ui/react-checkbox"],
157
- radixPrimitive: "@radix-ui/react-checkbox",
158
- props: {
159
- checked: { type: "boolean | 'indeterminate'", description: "Checked state" },
160
- onCheckedChange: { type: "(checked: boolean) => void", description: "Callback on check change" },
161
- disabled: { type: "boolean", default: "false", description: "Disable interaction" }
162
- },
163
- example: `<div className="flex items-center gap-2">
164
- <Checkbox id="terms" checked={accepted} onCheckedChange={setAccepted} />
165
- <Label htmlFor="terms">Accept terms</Label>
166
- </div>`
167
- },
168
- combobox: {
169
- name: "Combobox",
170
- category: "forms",
171
- description: "Searchable select component with autocomplete functionality",
172
- keywords: ["combobox", "autocomplete", "search", "select", "dropdown", "filter"],
173
- useCases: [
174
- "Country/state selection",
175
- "Tag selection",
176
- "User mention",
177
- "Large option lists"
178
- ],
179
- dependencies: ["cmdk", "@radix-ui/react-popover"],
180
- radixPrimitive: "@radix-ui/react-popover",
181
- props: {
182
- options: { type: "{ value: string; label: string }[]", description: "List of options", required: true },
183
- value: { type: "string", description: "Selected value" },
184
- onValueChange: { type: "(value: string) => void", description: "Callback on selection" },
185
- placeholder: { type: "string", description: "Trigger placeholder text" },
186
- searchPlaceholder: { type: "string", description: "Search input placeholder" },
187
- emptyText: { type: "string", description: "Text when no results found" }
188
- },
189
- example: `<Combobox
190
- options={[{ value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }]}
191
- value={framework}
192
- onValueChange={setFramework}
193
- placeholder="Select framework"
194
- />`
195
- },
196
- form: {
197
- name: "Form",
198
- category: "forms",
199
- description: "Form wrapper with react-hook-form and zod validation integration",
200
- keywords: ["form", "validation", "zod", "react-hook-form", "schema"],
201
- useCases: [
202
- "User registration",
203
- "Settings forms",
204
- "Data entry",
205
- "Multi-step forms"
206
- ],
207
- dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
208
- props: {
209
- "...form": { type: "UseFormReturn", description: "Spread react-hook-form useForm() return value", required: true }
210
- },
211
- subComponents: ["FormControl", "FormDescription", "FormField", "FormItem", "FormLabel", "FormMessage"],
212
- example: `<Form {...form}>
213
- <form onSubmit={form.handleSubmit(onSubmit)}>
214
- <FormField control={form.control} name="email" render={({ field }) => (
215
- <FormItem>
216
- <FormLabel>Email</FormLabel>
217
- <FormControl><Input {...field} /></FormControl>
218
- <FormMessage />
219
- </FormItem>
220
- )} />
221
- <Button type="submit">Submit</Button>
222
- </form>
223
- </Form>`
224
- },
225
- input: {
226
- name: "Input",
227
- category: "forms",
228
- description: "Text input field supporting various types (text, email, password, number)",
229
- keywords: ["input", "text", "field", "form", "email", "password"],
230
- useCases: [
231
- "Text entry",
232
- "Email addresses",
233
- "Passwords",
234
- "Numeric input"
235
- ],
236
- dependencies: [],
237
- props: {
238
- type: { type: "string", default: "'text'", description: "HTML input type" },
239
- placeholder: { type: "string", description: "Placeholder text" },
240
- disabled: { type: "boolean", default: "false", description: "Disable interaction" }
241
- },
242
- example: `<Input type="email" placeholder="Enter email" />`
243
- },
244
- "input-otp": {
245
- name: "InputOTP",
246
- category: "forms",
247
- description: "One-time password input with individual character slots",
248
- keywords: ["otp", "verification", "security", "authentication", "2fa", "code"],
249
- useCases: [
250
- "Two-factor authentication",
251
- "Email verification",
252
- "Phone verification",
253
- "Security codes"
254
- ],
255
- dependencies: ["input-otp"],
256
- props: {
257
- maxLength: { type: "number", default: "6", description: "Maximum number of characters" },
258
- value: { type: "string", description: "Controlled input value" },
259
- onChange: { type: "(value: string) => void", description: "Callback on value change" }
260
- },
261
- subComponents: ["InputOTPGroup", "InputOTPSlot", "InputOTPSeparator"],
262
- example: `<InputOTP maxLength={6}>
263
- <InputOTPGroup>
264
- <InputOTPSlot index={0} />
265
- <InputOTPSlot index={1} />
266
- <InputOTPSlot index={2} />
267
- </InputOTPGroup>
268
- <InputOTPSeparator />
269
- <InputOTPGroup>
270
- <InputOTPSlot index={3} />
271
- <InputOTPSlot index={4} />
272
- <InputOTPSlot index={5} />
273
- </InputOTPGroup>
274
- </InputOTP>`
275
- },
276
- label: {
277
- name: "Label",
278
- category: "forms",
279
- description: "Form field labels with proper accessibility associations",
280
- keywords: ["label", "form", "accessibility", "field-label"],
281
- useCases: [
282
- "Form field labels",
283
- "Input descriptions",
284
- "Accessible forms"
285
- ],
286
- dependencies: ["@radix-ui/react-label"],
287
- radixPrimitive: "@radix-ui/react-label",
288
- props: {
289
- htmlFor: { type: "string", description: "ID of the associated form control" }
290
- },
291
- example: `<Label htmlFor="email">Email Address</Label>
292
- <Input id="email" type="email" />`
293
- },
294
- "radio-group": {
295
- name: "RadioGroup",
296
- category: "forms",
297
- description: "Exclusive selection control for choosing one option from multiple",
298
- keywords: ["radio", "selection", "exclusive", "single-select", "options"],
299
- useCases: [
300
- "Single option selection",
301
- "Survey questions",
302
- "Payment methods",
303
- "Shipping options"
304
- ],
305
- dependencies: ["@radix-ui/react-radio-group"],
306
- radixPrimitive: "@radix-ui/react-radio-group",
307
- props: {
308
- value: { type: "string", description: "Controlled selected value" },
309
- onValueChange: { type: "(value: string) => void", description: "Callback on selection" },
310
- disabled: { type: "boolean", default: "false", description: "Disable interaction" }
311
- },
312
- subComponents: ["RadioGroupItem"],
313
- example: `<RadioGroup value={plan} onValueChange={setPlan}>
314
- <div className="flex items-center gap-2">
315
- <RadioGroupItem value="free" id="free" />
316
- <Label htmlFor="free">Free</Label>
317
- </div>
318
- </RadioGroup>`
319
- },
320
- select: {
321
- name: "Select",
322
- category: "forms",
323
- description: "Dropdown selection component for choosing from a list of options",
324
- keywords: ["select", "dropdown", "options", "picker", "choice"],
325
- useCases: [
326
- "Option selection",
327
- "Category filtering",
328
- "Settings choice",
329
- "Data sorting"
330
- ],
331
- dependencies: ["@radix-ui/react-select"],
332
- radixPrimitive: "@radix-ui/react-select",
333
- props: {
334
- value: { type: "string", description: "Controlled selected value" },
335
- onValueChange: { type: "(value: string) => void", description: "Callback on selection" },
336
- defaultValue: { type: "string", description: "Default selected value" }
337
- },
338
- subComponents: ["SelectTrigger", "SelectValue", "SelectContent", "SelectItem", "SelectGroup", "SelectLabel", "SelectSeparator"],
339
- example: `<Select value={theme} onValueChange={setTheme}>
340
- <SelectTrigger className="w-[180px]">
341
- <SelectValue placeholder="Select theme" />
342
- </SelectTrigger>
343
- <SelectContent>
344
- <SelectItem value="light">Light</SelectItem>
345
- <SelectItem value="dark">Dark</SelectItem>
346
- </SelectContent>
347
- </Select>`
348
- },
349
- slider: {
350
- name: "Slider",
351
- category: "forms",
352
- description: "Numeric input control via dragging for range selection",
353
- keywords: ["slider", "range", "numeric", "volume", "adjustment"],
354
- useCases: [
355
- "Volume control",
356
- "Price ranges",
357
- "Numeric settings",
358
- "Zoom level"
359
- ],
360
- dependencies: ["@radix-ui/react-slider"],
361
- radixPrimitive: "@radix-ui/react-slider",
362
- props: {
363
- value: { type: "number[]", description: "Current value(s)" },
364
- onValueChange: { type: "(value: number[]) => void", description: "Callback on value change" },
365
- min: { type: "number", default: "0", description: "Minimum value" },
366
- max: { type: "number", default: "100", description: "Maximum value" },
367
- step: { type: "number", default: "1", description: "Step increment" },
368
- disabled: { type: "boolean", default: "false", description: "Disable interaction" }
369
- },
370
- example: `<Slider value={[volume]} onValueChange={(v) => setVolume(v[0])} max={100} step={1} />`
371
- },
372
- switch: {
373
- name: "Switch",
374
- category: "forms",
375
- description: "Toggle switch for binary state changes in forms",
376
- keywords: ["switch", "toggle", "boolean", "on-off", "settings"],
377
- useCases: [
378
- "Feature toggles",
379
- "Notification settings",
380
- "Privacy options",
381
- "Mode switches"
382
- ],
383
- dependencies: ["@radix-ui/react-switch"],
384
- radixPrimitive: "@radix-ui/react-switch",
385
- props: {
386
- checked: { type: "boolean", description: "Controlled checked state" },
387
- onCheckedChange: { type: "(checked: boolean) => void", description: "Callback on toggle" },
388
- size: { type: "'sm' | 'md' | 'lg'", default: "'md'", description: "Size variant" },
389
- disabled: { type: "boolean", default: "false", description: "Disable interaction" },
390
- label: { type: "string", description: "Optional label text" }
391
- },
392
- example: `<Switch checked={enabled} onCheckedChange={setEnabled} size="md" />`
393
- },
394
- textarea: {
395
- name: "Textarea",
396
- category: "forms",
397
- description: "Multi-line text input for longer content",
398
- keywords: ["textarea", "text", "multiline", "input", "comment", "description"],
399
- useCases: [
400
- "Comment fields",
401
- "Message composition",
402
- "Descriptions",
403
- "Long-form text"
404
- ],
405
- dependencies: [],
406
- props: {
407
- placeholder: { type: "string", description: "Placeholder text" },
408
- disabled: { type: "boolean", default: "false", description: "Disable interaction" },
409
- rows: { type: "number", description: "Number of visible rows" }
410
- },
411
- example: `<Textarea placeholder="Write your message..." rows={4} />`
412
- },
413
- // ============================================================================
414
- // NAVIGATION (6)
415
- // ============================================================================
416
- breadcrumb: {
417
- name: "Breadcrumb",
418
- category: "navigation",
419
- description: "Hierarchical location indicator showing navigation path",
420
- keywords: ["breadcrumb", "navigation", "path", "hierarchy", "location"],
421
- useCases: [
422
- "Page hierarchy",
423
- "Multi-level navigation",
424
- "Location context",
425
- "Back navigation"
426
- ],
427
- dependencies: [],
428
- subComponents: ["BreadcrumbList", "BreadcrumbItem", "BreadcrumbLink", "BreadcrumbPage", "BreadcrumbSeparator", "BreadcrumbEllipsis"],
429
- example: `<Breadcrumb>
430
- <BreadcrumbList>
431
- <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>
432
- <BreadcrumbSeparator />
433
- <BreadcrumbItem><BreadcrumbLink href="/docs">Docs</BreadcrumbLink></BreadcrumbItem>
434
- <BreadcrumbSeparator />
435
- <BreadcrumbItem><BreadcrumbPage>Current</BreadcrumbPage></BreadcrumbItem>
436
- </BreadcrumbList>
437
- </Breadcrumb>`
438
- },
439
- command: {
440
- name: "Command",
441
- category: "navigation",
442
- description: "Command palette interface for searchable actions and navigation",
443
- keywords: ["command", "palette", "search", "shortcuts", "keyboard", "cmdk"],
444
- useCases: [
445
- "Quick navigation",
446
- "Action search",
447
- "Keyboard shortcuts",
448
- "Power user features"
449
- ],
450
- dependencies: ["cmdk"],
451
- subComponents: ["CommandInput", "CommandList", "CommandEmpty", "CommandGroup", "CommandItem", "CommandSeparator", "CommandShortcut", "CommandDialog"],
452
- example: `<Command>
453
- <CommandInput placeholder="Type a command..." />
454
- <CommandList>
455
- <CommandEmpty>No results found.</CommandEmpty>
456
- <CommandGroup heading="Actions">
457
- <CommandItem>Search</CommandItem>
458
- <CommandItem>Settings</CommandItem>
459
- </CommandGroup>
460
- </CommandList>
461
- </Command>`
462
- },
463
- menubar: {
464
- name: "Menubar",
465
- category: "navigation",
466
- description: "Desktop-style horizontal menu bar with dropdown menus",
467
- keywords: ["menubar", "menu", "navigation", "desktop", "toolbar"],
468
- useCases: [
469
- "Application menus",
470
- "Desktop-style navigation",
471
- "Action menus",
472
- "Editor toolbars"
473
- ],
474
- dependencies: ["@radix-ui/react-menubar"],
475
- radixPrimitive: "@radix-ui/react-menubar",
476
- subComponents: ["MenubarMenu", "MenubarTrigger", "MenubarContent", "MenubarItem", "MenubarSeparator", "MenubarLabel", "MenubarCheckboxItem", "MenubarRadioGroup", "MenubarRadioItem", "MenubarSub", "MenubarSubTrigger", "MenubarSubContent", "MenubarShortcut"],
477
- example: `<Menubar>
478
- <MenubarMenu>
479
- <MenubarTrigger>File</MenubarTrigger>
480
- <MenubarContent>
481
- <MenubarItem>New <MenubarShortcut>\u2318N</MenubarShortcut></MenubarItem>
482
- <MenubarItem>Open <MenubarShortcut>\u2318O</MenubarShortcut></MenubarItem>
483
- <MenubarSeparator />
484
- <MenubarItem>Exit</MenubarItem>
485
- </MenubarContent>
486
- </MenubarMenu>
487
- </Menubar>`
488
- },
489
- "navigation-menu": {
490
- name: "NavigationMenu",
491
- category: "navigation",
492
- description: "Complex header navigation with nested dropdown menus",
493
- keywords: ["navigation", "menu", "header", "navbar", "dropdown", "mega-menu"],
494
- useCases: [
495
- "Site navigation",
496
- "Header menus",
497
- "Mega menus",
498
- "Multi-level navigation"
499
- ],
500
- dependencies: ["@radix-ui/react-navigation-menu"],
501
- radixPrimitive: "@radix-ui/react-navigation-menu",
502
- subComponents: ["NavigationMenuList", "NavigationMenuItem", "NavigationMenuTrigger", "NavigationMenuContent", "NavigationMenuLink", "NavigationMenuIndicator", "NavigationMenuViewport"],
503
- example: `<NavigationMenu>
504
- <NavigationMenuList>
505
- <NavigationMenuItem>
506
- <NavigationMenuTrigger>Getting Started</NavigationMenuTrigger>
507
- <NavigationMenuContent>
508
- <NavigationMenuLink href="/docs">Documentation</NavigationMenuLink>
509
- </NavigationMenuContent>
510
- </NavigationMenuItem>
511
- </NavigationMenuList>
512
- </NavigationMenu>`
513
- },
514
- pagination: {
515
- name: "Pagination",
516
- category: "navigation",
517
- description: "Multi-page navigation control for paginated content",
518
- keywords: ["pagination", "pages", "navigation", "paging", "next", "previous"],
519
- useCases: [
520
- "Table pagination",
521
- "Search results",
522
- "Content lists",
523
- "Multi-page forms"
524
- ],
525
- dependencies: [],
526
- subComponents: ["PaginationContent", "PaginationItem", "PaginationLink", "PaginationPrevious", "PaginationNext", "PaginationEllipsis"],
527
- example: `<Pagination>
528
- <PaginationContent>
529
- <PaginationItem><PaginationPrevious href="#" /></PaginationItem>
530
- <PaginationItem><PaginationLink href="#">1</PaginationLink></PaginationItem>
531
- <PaginationItem><PaginationLink href="#" isActive>2</PaginationLink></PaginationItem>
532
- <PaginationItem><PaginationLink href="#">3</PaginationLink></PaginationItem>
533
- <PaginationItem><PaginationNext href="#" /></PaginationItem>
534
- </PaginationContent>
535
- </Pagination>`
536
- },
537
- tabs: {
538
- name: "Tabs",
539
- category: "navigation",
540
- description: "Tabbed interface for organizing content into switchable panels",
541
- keywords: ["tabs", "tabbed", "navigation", "panels", "switching"],
542
- useCases: [
543
- "Settings panels",
544
- "Content organization",
545
- "Multi-view interfaces",
546
- "Dashboard sections"
547
- ],
548
- dependencies: ["@radix-ui/react-tabs"],
549
- radixPrimitive: "@radix-ui/react-tabs",
550
- props: {
551
- defaultValue: { type: "string", description: "Default active tab value" },
552
- value: { type: "string", description: "Controlled active tab value" },
553
- onValueChange: { type: "(value: string) => void", description: "Callback on tab change" }
554
- },
555
- subComponents: ["TabsList", "TabsTrigger", "TabsContent"],
556
- example: `<Tabs defaultValue="account">
557
- <TabsList>
558
- <TabsTrigger value="account">Account</TabsTrigger>
559
- <TabsTrigger value="password">Password</TabsTrigger>
560
- </TabsList>
561
- <TabsContent value="account">Account settings here.</TabsContent>
562
- <TabsContent value="password">Password settings here.</TabsContent>
563
- </Tabs>`
564
- },
565
- // ============================================================================
566
- // OVERLAYS (9)
567
- // ============================================================================
568
- "alert-dialog": {
569
- name: "AlertDialog",
570
- category: "overlays",
571
- description: "Modal dialog for critical confirmations with cancel/confirm actions",
572
- keywords: ["alert", "dialog", "modal", "confirmation", "warning", "destructive"],
573
- useCases: [
574
- "Delete confirmations",
575
- "Destructive actions",
576
- "Critical warnings",
577
- "Irreversible operations"
578
- ],
579
- dependencies: ["@radix-ui/react-alert-dialog"],
580
- radixPrimitive: "@radix-ui/react-alert-dialog",
581
- props: {
582
- open: { type: "boolean", description: "Controlled open state" },
583
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
584
- },
585
- subComponents: ["AlertDialogTrigger", "AlertDialogContent", "AlertDialogHeader", "AlertDialogTitle", "AlertDialogDescription", "AlertDialogFooter", "AlertDialogAction", "AlertDialogCancel"],
586
- example: `<AlertDialog>
587
- <AlertDialogTrigger asChild><Button variant="destructive">Delete</Button></AlertDialogTrigger>
588
- <AlertDialogContent>
589
- <AlertDialogHeader>
590
- <AlertDialogTitle>Are you sure?</AlertDialogTitle>
591
- <AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
592
- </AlertDialogHeader>
593
- <AlertDialogFooter>
594
- <AlertDialogCancel>Cancel</AlertDialogCancel>
595
- <AlertDialogAction>Delete</AlertDialogAction>
596
- </AlertDialogFooter>
597
- </AlertDialogContent>
598
- </AlertDialog>`
599
- },
600
- "context-menu": {
601
- name: "ContextMenu",
602
- category: "overlays",
603
- description: "Right-click context menu for contextual actions",
604
- keywords: ["context-menu", "right-click", "menu", "actions", "contextual"],
605
- useCases: [
606
- "Right-click actions",
607
- "Contextual operations",
608
- "File operations",
609
- "Item actions"
610
- ],
611
- dependencies: ["@radix-ui/react-context-menu"],
612
- radixPrimitive: "@radix-ui/react-context-menu",
613
- subComponents: ["ContextMenuTrigger", "ContextMenuContent", "ContextMenuItem", "ContextMenuCheckboxItem", "ContextMenuRadioItem", "ContextMenuLabel", "ContextMenuSeparator", "ContextMenuShortcut", "ContextMenuSub", "ContextMenuSubTrigger", "ContextMenuSubContent", "ContextMenuRadioGroup"],
614
- example: `<ContextMenu>
615
- <ContextMenuTrigger>Right click here</ContextMenuTrigger>
616
- <ContextMenuContent>
617
- <ContextMenuItem>Edit</ContextMenuItem>
618
- <ContextMenuItem>Duplicate</ContextMenuItem>
619
- <ContextMenuSeparator />
620
- <ContextMenuItem>Delete</ContextMenuItem>
621
- </ContextMenuContent>
622
- </ContextMenu>`
623
- },
624
- dialog: {
625
- name: "Dialog",
626
- category: "overlays",
627
- description: "Modal dialog for focused interactions and forms",
628
- keywords: ["dialog", "modal", "popup", "overlay", "form"],
629
- useCases: [
630
- "Form modals",
631
- "Detail views",
632
- "Focused tasks",
633
- "User input"
634
- ],
635
- dependencies: ["@radix-ui/react-dialog"],
636
- radixPrimitive: "@radix-ui/react-dialog",
637
- props: {
638
- open: { type: "boolean", description: "Controlled open state" },
639
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
640
- },
641
- subComponents: ["DialogTrigger", "DialogContent", "DialogHeader", "DialogTitle", "DialogDescription", "DialogFooter", "DialogClose"],
642
- example: `<Dialog>
643
- <DialogTrigger asChild><Button>Open</Button></DialogTrigger>
644
- <DialogContent>
645
- <DialogHeader>
646
- <DialogTitle>Title</DialogTitle>
647
- <DialogDescription>Description here.</DialogDescription>
648
- </DialogHeader>
649
- <DialogFooter>
650
- <Button variant="outline">Cancel</Button>
651
- <Button>Confirm</Button>
652
- </DialogFooter>
653
- </DialogContent>
654
- </Dialog>`
655
- },
656
- drawer: {
657
- name: "Drawer",
658
- category: "overlays",
659
- description: "Mobile-friendly bottom drawer that slides up from screen bottom",
660
- keywords: ["drawer", "bottom-sheet", "mobile", "slide-up", "panel"],
661
- useCases: [
662
- "Mobile actions",
663
- "Mobile forms",
664
- "Bottom sheets",
665
- "Mobile menus"
666
- ],
667
- dependencies: ["vaul"],
668
- props: {
669
- open: { type: "boolean", description: "Controlled open state" },
670
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
671
- },
672
- subComponents: ["DrawerTrigger", "DrawerContent", "DrawerHeader", "DrawerTitle", "DrawerDescription", "DrawerFooter", "DrawerClose"],
673
- example: `<Drawer>
674
- <DrawerTrigger asChild><Button>Open Drawer</Button></DrawerTrigger>
675
- <DrawerContent>
676
- <DrawerHeader>
677
- <DrawerTitle>Settings</DrawerTitle>
678
- <DrawerDescription>Adjust preferences.</DrawerDescription>
679
- </DrawerHeader>
680
- <div className="p-4">Content here.</div>
681
- <DrawerFooter>
682
- <Button>Save</Button>
683
- <DrawerClose asChild><Button variant="outline">Cancel</Button></DrawerClose>
684
- </DrawerFooter>
685
- </DrawerContent>
686
- </Drawer>`
687
- },
688
- "dropdown-menu": {
689
- name: "DropdownMenu",
690
- category: "overlays",
691
- description: "Dropdown menu for actions and navigation options",
692
- keywords: ["dropdown", "menu", "actions", "options", "popover"],
693
- useCases: [
694
- "Action menus",
695
- "User menus",
696
- "Item options",
697
- "Overflow menus"
698
- ],
699
- dependencies: ["@radix-ui/react-dropdown-menu"],
700
- radixPrimitive: "@radix-ui/react-dropdown-menu",
701
- props: {
702
- open: { type: "boolean", description: "Controlled open state" },
703
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
704
- },
705
- subComponents: ["DropdownMenuTrigger", "DropdownMenuContent", "DropdownMenuItem", "DropdownMenuLabel", "DropdownMenuSeparator", "DropdownMenuCheckboxItem", "DropdownMenuRadioGroup", "DropdownMenuRadioItem", "DropdownMenuSub", "DropdownMenuSubTrigger", "DropdownMenuSubContent"],
706
- example: `<DropdownMenu>
707
- <DropdownMenuTrigger asChild><Button variant="ghost">Options</Button></DropdownMenuTrigger>
708
- <DropdownMenuContent>
709
- <DropdownMenuLabel>Actions</DropdownMenuLabel>
710
- <DropdownMenuSeparator />
711
- <DropdownMenuItem>Edit</DropdownMenuItem>
712
- <DropdownMenuItem>Delete</DropdownMenuItem>
713
- </DropdownMenuContent>
714
- </DropdownMenu>`
715
- },
716
- "hover-card": {
717
- name: "HoverCard",
718
- category: "overlays",
719
- description: "Rich preview card that appears on hover",
720
- keywords: ["hover-card", "preview", "popover", "tooltip", "hover"],
721
- useCases: [
722
- "User previews",
723
- "Link previews",
724
- "Rich tooltips",
725
- "Additional context"
726
- ],
727
- dependencies: ["@radix-ui/react-hover-card"],
728
- radixPrimitive: "@radix-ui/react-hover-card",
729
- props: {
730
- openDelay: { type: "number", default: "700", description: "Delay in ms before opening" },
731
- closeDelay: { type: "number", default: "300", description: "Delay in ms before closing" }
732
- },
733
- subComponents: ["HoverCardTrigger", "HoverCardContent"],
734
- example: `<HoverCard>
735
- <HoverCardTrigger asChild><Link href="#">@username</Link></HoverCardTrigger>
736
- <HoverCardContent className="w-80">
737
- <div className="flex gap-4">
738
- <Avatar />
739
- <div><p className="text-sm font-semibold">Username</p><p className="text-sm text-muted-foreground">Bio here.</p></div>
740
- </div>
741
- </HoverCardContent>
742
- </HoverCard>`
743
- },
744
- popover: {
745
- name: "Popover",
746
- category: "overlays",
747
- description: "Floating content panel anchored to a trigger element",
748
- keywords: ["popover", "floating", "tooltip", "overlay", "panel"],
749
- useCases: [
750
- "Additional info",
751
- "Form helpers",
752
- "Contextual content",
753
- "Inline editors"
754
- ],
755
- dependencies: ["@radix-ui/react-popover"],
756
- radixPrimitive: "@radix-ui/react-popover",
757
- props: {
758
- open: { type: "boolean", description: "Controlled open state" },
759
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
760
- },
761
- subComponents: ["PopoverTrigger", "PopoverContent", "PopoverAnchor"],
762
- example: `<Popover>
763
- <PopoverTrigger asChild><Button variant="outline">Open</Button></PopoverTrigger>
764
- <PopoverContent className="w-80">Content here.</PopoverContent>
765
- </Popover>`
766
- },
767
- sheet: {
768
- name: "Sheet",
769
- category: "overlays",
770
- description: "Slide-in panel from screen edges for supplementary content",
771
- keywords: ["sheet", "sidebar", "slide-in", "panel", "drawer"],
772
- useCases: [
773
- "Mobile navigation",
774
- "Sidebar panels",
775
- "Settings panels",
776
- "Detail views"
777
- ],
778
- dependencies: ["@radix-ui/react-dialog"],
779
- radixPrimitive: "@radix-ui/react-dialog",
780
- props: {
781
- open: { type: "boolean", description: "Controlled open state" },
782
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
783
- },
784
- subComponents: ["SheetTrigger", "SheetContent", "SheetHeader", "SheetTitle", "SheetDescription", "SheetClose"],
785
- example: `<Sheet>
786
- <SheetTrigger asChild><Button>Open</Button></SheetTrigger>
787
- <SheetContent side="right">
788
- <SheetHeader><SheetTitle>Settings</SheetTitle></SheetHeader>
789
- <div className="p-4">Content here.</div>
790
- </SheetContent>
791
- </Sheet>`
792
- },
793
- tooltip: {
794
- name: "Tooltip",
795
- category: "overlays",
796
- description: "Contextual hints and additional information on hover",
797
- keywords: ["tooltip", "hint", "help", "hover", "info"],
798
- useCases: [
799
- "Icon explanations",
800
- "Additional context",
801
- "Help text",
802
- "Keyboard shortcuts"
803
- ],
804
- dependencies: ["@radix-ui/react-tooltip"],
805
- radixPrimitive: "@radix-ui/react-tooltip",
806
- subComponents: ["TooltipTrigger", "TooltipContent", "TooltipProvider"],
807
- example: `<Tooltip>
808
- <TooltipTrigger asChild><Button variant="ghost" size="icon"><Info /></Button></TooltipTrigger>
809
- <TooltipContent><p>Helpful information</p></TooltipContent>
810
- </Tooltip>`
811
- },
812
- // ============================================================================
813
- // FEEDBACK (5)
814
- // ============================================================================
815
- alert: {
816
- name: "Alert",
817
- category: "feedback",
818
- description: "Prominent message component for important information",
819
- keywords: ["alert", "message", "notification", "warning", "info", "error", "success"],
820
- useCases: [
821
- "Status messages",
822
- "Warnings",
823
- "Errors",
824
- "Success confirmations"
825
- ],
826
- dependencies: [],
827
- props: {
828
- variant: { type: "'default' | 'destructive'", default: "'default'", description: "Visual variant" }
829
- },
830
- subComponents: ["AlertTitle", "AlertDescription"],
831
- example: `<Alert variant="destructive">
832
- <AlertTitle>Error</AlertTitle>
833
- <AlertDescription>Your session has expired.</AlertDescription>
834
- </Alert>`
835
- },
836
- progress: {
837
- name: "Progress",
838
- category: "feedback",
839
- description: "Visual indicator for progress and completion status",
840
- keywords: ["progress", "loading", "bar", "percentage", "completion"],
841
- useCases: [
842
- "Upload progress",
843
- "Task completion",
844
- "Loading states",
845
- "Multi-step forms"
846
- ],
847
- dependencies: ["@radix-ui/react-progress"],
848
- radixPrimitive: "@radix-ui/react-progress",
849
- props: {
850
- value: { type: "number", default: "0", description: "Progress value (0-100)" },
851
- max: { type: "number", default: "100", description: "Maximum value" }
852
- },
853
- example: `<Progress value={60} />`
854
- },
855
- skeleton: {
856
- name: "Skeleton",
857
- category: "feedback",
858
- description: "Loading placeholder that mimics content structure",
859
- keywords: ["skeleton", "loading", "placeholder", "shimmer", "spinner"],
860
- useCases: [
861
- "Content loading",
862
- "Data fetching",
863
- "Initial load",
864
- "Lazy loading"
865
- ],
866
- dependencies: [],
867
- props: {
868
- variant: { type: "'default' | 'circular' | 'rectangular' | 'text'", default: "'default'", description: "Shape variant" },
869
- width: { type: "string", default: "'100%'", description: "Width (CSS value)" },
870
- height: { type: "string", default: "'20px'", description: "Height (CSS value)" }
871
- },
872
- example: `<div className="space-y-2">
873
- <Skeleton className="h-12 w-12 rounded-full" />
874
- <Skeleton className="h-4 w-[250px]" />
875
- <Skeleton className="h-4 w-[200px]" />
876
- </div>`
877
- },
878
- sonner: {
879
- name: "Sonner",
880
- category: "feedback",
881
- description: "Toast notification system with queuing and positioning. Use Toaster component in layout, call toast() to trigger.",
882
- keywords: ["toast", "notification", "sonner", "message", "alert"],
883
- useCases: [
884
- "Success messages",
885
- "Error notifications",
886
- "Action feedback",
887
- "System messages"
888
- ],
889
- dependencies: ["sonner"],
890
- subComponents: ["Toaster"],
891
- example: `// In layout: <Toaster />
892
- // To trigger:
893
- import { toast } from 'sonner'
894
- toast.success('Saved successfully')
895
- toast.error('Something went wrong')
896
- toast('Default notification')`
897
- },
898
- toast: {
899
- name: "Toast",
900
- category: "feedback",
901
- description: "Temporary notification messages that appear briefly",
902
- keywords: ["toast", "notification", "message", "snackbar", "alert"],
903
- useCases: [
904
- "Quick feedback",
905
- "Status updates",
906
- "Non-critical notifications",
907
- "Confirmation messages"
908
- ],
909
- dependencies: ["@radix-ui/react-toast"],
910
- radixPrimitive: "@radix-ui/react-toast",
911
- subComponents: ["ToastAction", "ToastClose", "ToastDescription", "ToastProvider", "ToastTitle", "ToastViewport"],
912
- example: `// Prefer using Sonner (Toaster + toast()) for new projects.
913
- // This is the Radix-based alternative.
914
- import { useToast } from '@thesage/ui'
915
- const { toast } = useToast()
916
- toast({ title: 'Success', description: 'Item saved.' })`
917
- },
918
- // ============================================================================
919
- // DATA DISPLAY (6)
920
- // ============================================================================
921
- avatar: {
922
- name: "Avatar",
923
- category: "data-display",
924
- description: "User profile image with fallback initials",
925
- keywords: ["avatar", "profile", "image", "user", "picture"],
926
- useCases: [
927
- "User profiles",
928
- "Comment authors",
929
- "Team members",
930
- "Chat participants"
931
- ],
932
- dependencies: ["@radix-ui/react-avatar"],
933
- radixPrimitive: "@radix-ui/react-avatar",
934
- subComponents: ["AvatarImage", "AvatarFallback"],
935
- example: `<Avatar>
936
- <AvatarImage src="/avatar.jpg" alt="User" />
937
- <AvatarFallback>JD</AvatarFallback>
938
- </Avatar>`
939
- },
940
- badge: {
941
- name: "Badge",
942
- category: "data-display",
943
- description: "Status indicators and labels for categorization",
944
- keywords: ["badge", "tag", "label", "status", "chip", "pill"],
945
- useCases: [
946
- "Status indicators",
947
- "Tags",
948
- "Categories",
949
- "Notification counts"
950
- ],
951
- dependencies: [],
952
- props: {
953
- variant: { type: "'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' | 'error' | 'info'", default: "'default'", description: "Visual variant" },
954
- size: { type: "'sm' | 'md' | 'lg'", default: "'md'", description: "Size variant" },
955
- dot: { type: "boolean", default: "false", description: "Show animated indicator dot" }
956
- },
957
- example: `<Badge variant="success" dot>Active</Badge>`
958
- },
959
- calendar: {
960
- name: "Calendar",
961
- category: "data-display",
962
- description: "Date selection calendar with month/year navigation",
963
- keywords: ["calendar", "date", "picker", "month", "day"],
964
- useCases: [
965
- "Date selection",
966
- "Event scheduling",
967
- "Booking systems",
968
- "Date ranges"
969
- ],
970
- dependencies: ["react-day-picker", "date-fns"],
971
- props: {
972
- mode: { type: "'single' | 'multiple' | 'range'", default: "'single'", description: "Selection mode" },
973
- selected: { type: "Date | Date[] | DateRange", description: "Selected date(s)" },
974
- onSelect: { type: "(date) => void", description: "Callback on date selection" },
975
- showOutsideDays: { type: "boolean", default: "true", description: "Show days from adjacent months" },
976
- disabled: { type: "Matcher | Matcher[]", description: "Dates to disable" }
977
- },
978
- example: `<Calendar mode="single" selected={date} onSelect={setDate} />`
979
- },
980
- card: {
981
- name: "Card",
982
- category: "data-display",
983
- description: "Container for grouping related content with optional header and footer",
984
- keywords: ["card", "container", "box", "panel", "content"],
985
- useCases: [
986
- "Content grouping",
987
- "Product cards",
988
- "Information panels",
989
- "Dashboard widgets"
990
- ],
991
- dependencies: [],
992
- props: {
993
- variant: { type: "'default' | 'glass' | 'outline'", default: "'default'", description: "Visual variant" },
994
- hoverEffect: { type: "boolean", default: "false", description: "Enable hover lift and shadow" }
995
- },
996
- subComponents: ["CardHeader", "CardTitle", "CardDescription", "CardContent", "CardFooter"],
997
- example: `<Card>
998
- <CardHeader>
999
- <CardTitle>Notifications</CardTitle>
1000
- <CardDescription>You have 3 unread messages.</CardDescription>
1001
- </CardHeader>
1002
- <CardContent><p>Content here.</p></CardContent>
1003
- <CardFooter><Button>View All</Button></CardFooter>
1004
- </Card>`
1005
- },
1006
- "data-table": {
1007
- name: "DataTable",
1008
- category: "data-display",
1009
- description: "Enhanced table with sorting, filtering, and pagination",
1010
- keywords: ["table", "data", "grid", "sorting", "filtering", "pagination", "tanstack"],
1011
- useCases: [
1012
- "Data grids",
1013
- "Admin tables",
1014
- "Reports",
1015
- "List management"
1016
- ],
1017
- dependencies: ["@tanstack/react-table"],
1018
- props: {
1019
- columns: { type: "ColumnDef<TData, TValue>[]", description: "Column definitions from @tanstack/react-table", required: true },
1020
- data: { type: "TData[]", description: "Array of row data", required: true }
1021
- },
1022
- example: `import { DataTable } from '@thesage/ui/tables'
1023
- const columns = [
1024
- { accessorKey: 'name', header: 'Name' },
1025
- { accessorKey: 'email', header: 'Email' },
1026
- ]
1027
- <DataTable columns={columns} data={users} />`
1028
- },
1029
- table: {
1030
- name: "Table",
1031
- category: "data-display",
1032
- description: "Basic table component for tabular data display",
1033
- keywords: ["table", "data", "rows", "columns", "grid"],
1034
- useCases: [
1035
- "Simple tables",
1036
- "Data display",
1037
- "Comparison tables",
1038
- "Pricing tables"
1039
- ],
1040
- dependencies: [],
1041
- subComponents: ["TableHeader", "TableBody", "TableFooter", "TableRow", "TableHead", "TableCell", "TableCaption"],
1042
- example: `<Table>
1043
- <TableHeader>
1044
- <TableRow>
1045
- <TableHead>Name</TableHead>
1046
- <TableHead>Status</TableHead>
1047
- </TableRow>
1048
- </TableHeader>
1049
- <TableBody>
1050
- <TableRow>
1051
- <TableCell>John</TableCell>
1052
- <TableCell>Active</TableCell>
1053
- </TableRow>
1054
- </TableBody>
1055
- </Table>`
1056
- },
1057
- heading: {
1058
- name: "Heading",
1059
- category: "data-display",
1060
- description: "Semantic heading with automatic token-based styling and responsive sizes",
1061
- keywords: ["heading", "title", "h1", "h2", "h3", "h4", "h5", "h6", "typography"],
1062
- useCases: [
1063
- "Page titles",
1064
- "Section headings",
1065
- "Content hierarchy",
1066
- "Semantic HTML structure"
1067
- ],
1068
- dependencies: [],
1069
- props: {
1070
- level: { type: "1 | 2 | 3 | 4 | 5 | 6", default: "2", description: "Heading level (renders h1-h6)" },
1071
- as: { type: "'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'", description: "Override rendered HTML element" }
1072
- },
1073
- example: `<Heading level={1}>Page Title</Heading>
1074
- <Heading level={2}>Section Title</Heading>`
1075
- },
1076
- text: {
1077
- name: "Text",
1078
- category: "data-display",
1079
- description: "Semantic text component with variants for primary, secondary, and muted styles",
1080
- keywords: ["text", "paragraph", "body", "typography", "content", "p", "span"],
1081
- useCases: [
1082
- "Body text",
1083
- "Descriptions",
1084
- "Helper text",
1085
- "Labels and captions"
1086
- ],
1087
- dependencies: [],
1088
- props: {
1089
- variant: { type: "'default' | 'secondary' | 'muted' | 'lead'", default: "'default'", description: "Text style variant" },
1090
- as: { type: "'p' | 'span' | 'div'", default: "'p'", description: "HTML element to render" },
1091
- size: { type: "'sm' | 'base' | 'lg'", default: "'base'", description: "Font size" }
1092
- },
1093
- example: `<Text variant="lead">Important introductory text.</Text>
1094
- <Text variant="muted" size="sm">Helper text below an input.</Text>`
1095
- },
1096
- code: {
1097
- name: "Code",
1098
- category: "data-display",
1099
- description: "Code display with syntax highlighting for inline and block code",
1100
- keywords: ["code", "syntax", "highlighting", "programming", "snippet", "pre"],
1101
- useCases: [
1102
- "Code snippets",
1103
- "API documentation",
1104
- "Technical content",
1105
- "Inline code references"
1106
- ],
1107
- dependencies: [],
1108
- props: {
1109
- inline: { type: "boolean", default: "false", description: "Render as inline code (true) or block (false)" },
1110
- showCopy: { type: "boolean", default: "true", description: "Show copy button for block code" }
1111
- },
1112
- example: `// Inline
1113
- <Code inline>npm install @thesage/ui</Code>
1114
-
1115
- // Block
1116
- <Code>{\`const x = 1;
1117
- const y = 2;\`}</Code>`
1118
- },
1119
- "collapsible-code-block": {
1120
- name: "CollapsibleCodeBlock",
1121
- category: "data-display",
1122
- description: "Expandable code block with syntax highlighting, preview mode, and copy functionality",
1123
- keywords: ["code", "collapsible", "expandable", "syntax", "copy", "preview"],
1124
- useCases: [
1125
- "Long code examples",
1126
- "Documentation code blocks",
1127
- "Tutorial code snippets",
1128
- "API examples"
1129
- ],
1130
- dependencies: ["@thesage/tokens"],
1131
- props: {
1132
- code: { type: "string", description: "Source code string", required: true },
1133
- language: { type: "'tsx' | 'typescript' | 'javascript' | 'jsx' | 'css' | 'html' | 'json' | 'bash'", default: "'tsx'", description: "Language for syntax highlighting" },
1134
- title: { type: "string", description: "Optional title above the code block" },
1135
- maxLines: { type: "number", default: "10", description: "Lines to show before collapsing" },
1136
- defaultExpanded: { type: "boolean", default: "false", description: "Start in expanded state" }
1137
- },
1138
- example: `<CollapsibleCodeBlock
1139
- code="const Button = () => <button>Click</button>"
1140
- language="tsx"
1141
- title="Button.tsx"
1142
- maxLines={5}
1143
- />`
1144
- },
1145
- "description-list": {
1146
- name: "DescriptionList",
1147
- category: "data-display",
1148
- description: "Key-value pair list for displaying labeled data in row or column layout",
1149
- keywords: ["description", "list", "key-value", "definition", "dl", "dt", "dd"],
1150
- useCases: [
1151
- "Product specifications",
1152
- "User profile details",
1153
- "Metadata display",
1154
- "Settings summaries"
1155
- ],
1156
- dependencies: [],
1157
- props: {
1158
- items: { type: "{ label: string; value: ReactNode }[]", description: "Array of label-value pairs", required: true },
1159
- layout: { type: "'row' | 'column'", default: "'row'", description: "Layout direction for label/value pairs" }
1160
- },
1161
- example: `<DescriptionList items={[
1162
- { label: 'Name', value: 'John Doe' },
1163
- { label: 'Email', value: 'john@example.com' },
1164
- { label: 'Role', value: <Badge>Admin</Badge> },
1165
- ]} />`
1166
- },
1167
- // ============================================================================
1168
- // LAYOUT (8)
1169
- // ============================================================================
1170
- accordion: {
1171
- name: "Accordion",
1172
- category: "layout",
1173
- description: "Collapsible content sections with expand/collapse functionality",
1174
- keywords: ["accordion", "collapsible", "expandable", "faq", "sections"],
1175
- useCases: [
1176
- "FAQ sections",
1177
- "Content organization",
1178
- "Settings panels",
1179
- "Information disclosure"
1180
- ],
1181
- dependencies: ["@radix-ui/react-accordion"],
1182
- radixPrimitive: "@radix-ui/react-accordion",
1183
- props: {
1184
- type: { type: "'single' | 'multiple'", description: "Allow single or multiple open items", required: true },
1185
- collapsible: { type: "boolean", default: "false", description: 'Allow closing all items (type="single" only)' }
1186
- },
1187
- subComponents: ["AccordionItem", "AccordionTrigger", "AccordionContent"],
1188
- example: `<Accordion type="single" collapsible>
1189
- <AccordionItem value="item-1">
1190
- <AccordionTrigger>Is it accessible?</AccordionTrigger>
1191
- <AccordionContent>Yes. It follows WAI-ARIA patterns.</AccordionContent>
1192
- </AccordionItem>
1193
- </Accordion>`
1194
- },
1195
- "aspect-ratio": {
1196
- name: "AspectRatio",
1197
- category: "layout",
1198
- description: "Container that maintains specific width-to-height ratio",
1199
- keywords: ["aspect-ratio", "ratio", "responsive", "media", "image"],
1200
- useCases: [
1201
- "Responsive images",
1202
- "Video embeds",
1203
- "Media containers",
1204
- "Card images"
1205
- ],
1206
- dependencies: ["@radix-ui/react-aspect-ratio"],
1207
- radixPrimitive: "@radix-ui/react-aspect-ratio",
1208
- props: {
1209
- ratio: { type: "number", default: "1", description: "Aspect ratio as a number (e.g. 16/9)" }
1210
- },
1211
- example: `<AspectRatio ratio={16 / 9}>
1212
- <img src="/image.jpg" alt="Photo" className="rounded-md object-cover w-full h-full" />
1213
- </AspectRatio>`
1214
- },
1215
- carousel: {
1216
- name: "Carousel",
1217
- category: "layout",
1218
- description: "Scrollable content slider with navigation controls",
1219
- keywords: ["carousel", "slider", "slideshow", "gallery", "swipe"],
1220
- useCases: [
1221
- "Image galleries",
1222
- "Product showcases",
1223
- "Content sliders",
1224
- "Testimonials"
1225
- ],
1226
- dependencies: ["embla-carousel-react"],
1227
- props: {
1228
- orientation: { type: "'horizontal' | 'vertical'", default: "'horizontal'", description: "Scroll direction" },
1229
- opts: { type: "CarouselOptions", description: "Embla Carousel options (loop, align, etc.)" },
1230
- plugins: { type: "CarouselPlugin", description: "Embla Carousel plugins (autoplay, etc.)" },
1231
- setApi: { type: "(api: CarouselApi) => void", description: "Callback to get carousel API ref" }
1232
- },
1233
- subComponents: ["CarouselContent", "CarouselItem", "CarouselPrevious", "CarouselNext"],
1234
- example: `<Carousel>
1235
- <CarouselContent>
1236
- <CarouselItem>Slide 1</CarouselItem>
1237
- <CarouselItem>Slide 2</CarouselItem>
1238
- <CarouselItem>Slide 3</CarouselItem>
1239
- </CarouselContent>
1240
- <CarouselPrevious />
1241
- <CarouselNext />
1242
- </Carousel>`
1243
- },
1244
- collapsible: {
1245
- name: "Collapsible",
1246
- category: "layout",
1247
- description: "Simple show/hide content container",
1248
- keywords: ["collapsible", "expandable", "toggle", "show-hide", "disclosure"],
1249
- useCases: [
1250
- "Sidebar sections",
1251
- "Content reveals",
1252
- "Advanced options",
1253
- "Details disclosure"
1254
- ],
1255
- dependencies: ["@radix-ui/react-collapsible"],
1256
- radixPrimitive: "@radix-ui/react-collapsible",
1257
- props: {
1258
- open: { type: "boolean", description: "Controlled open state" },
1259
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" },
1260
- defaultOpen: { type: "boolean", default: "false", description: "Default open state" }
1261
- },
1262
- subComponents: ["CollapsibleTrigger", "CollapsibleContent"],
1263
- example: `<Collapsible>
1264
- <CollapsibleTrigger asChild><Button variant="ghost">Toggle</Button></CollapsibleTrigger>
1265
- <CollapsibleContent>
1266
- <p>Hidden content revealed on click.</p>
1267
- </CollapsibleContent>
1268
- </Collapsible>`
1269
- },
1270
- "date-picker": {
1271
- name: "DatePicker",
1272
- category: "layout",
1273
- description: "Calendar date picker combined with popover trigger",
1274
- keywords: ["date-picker", "calendar", "date", "popover", "input"],
1275
- useCases: [
1276
- "Date selection",
1277
- "Form dates",
1278
- "Scheduling",
1279
- "Filters"
1280
- ],
1281
- dependencies: ["react-day-picker", "date-fns", "@radix-ui/react-popover"],
1282
- props: {
1283
- date: { type: "Date", description: "Selected date" },
1284
- onDateChange: { type: "(date: Date | undefined) => void", description: "Callback on date change" },
1285
- placeholder: { type: "string", default: "'Pick a date'", description: "Placeholder text" },
1286
- disabled: { type: "boolean", default: "false", description: "Disable the date picker" }
1287
- },
1288
- example: `<DatePicker date={date} onDateChange={setDate} placeholder="Select date" />`
1289
- },
1290
- resizable: {
1291
- name: "Resizable",
1292
- category: "layout",
1293
- description: "User-resizable panel containers with drag handles",
1294
- keywords: ["resizable", "panels", "split", "drag", "resize"],
1295
- useCases: [
1296
- "Split views",
1297
- "IDE panels",
1298
- "Dashboard layouts",
1299
- "Adjustable sidebars"
1300
- ],
1301
- dependencies: ["react-resizable-panels"],
1302
- props: {
1303
- direction: { type: "'horizontal' | 'vertical'", default: "'horizontal'", description: "Panel layout direction" }
1304
- },
1305
- subComponents: ["ResizablePanelGroup", "ResizablePanel", "ResizableHandle"],
1306
- example: `<ResizablePanelGroup direction="horizontal">
1307
- <ResizablePanel defaultSize={50}>Left panel</ResizablePanel>
1308
- <ResizableHandle />
1309
- <ResizablePanel defaultSize={50}>Right panel</ResizablePanel>
1310
- </ResizablePanelGroup>`
1311
- },
1312
- "scroll-area": {
1313
- name: "ScrollArea",
1314
- category: "layout",
1315
- description: "Custom scrollbar styling for overflow content",
1316
- keywords: ["scroll", "scrollbar", "overflow", "custom-scroll"],
1317
- useCases: [
1318
- "Content overflow",
1319
- "Scrollable lists",
1320
- "Chat windows",
1321
- "Code displays"
1322
- ],
1323
- dependencies: ["@radix-ui/react-scroll-area"],
1324
- radixPrimitive: "@radix-ui/react-scroll-area",
1325
- subComponents: ["ScrollBar"],
1326
- example: `<ScrollArea className="h-[200px] w-full rounded-md border p-4">
1327
- <div>Long scrollable content here...</div>
1328
- </ScrollArea>`
1329
- },
1330
- separator: {
1331
- name: "Separator",
1332
- category: "layout",
1333
- description: "Visual divider for separating content sections",
1334
- keywords: ["separator", "divider", "line", "hr", "border"],
1335
- useCases: [
1336
- "Content separation",
1337
- "Menu dividers",
1338
- "Section breaks",
1339
- "Visual hierarchy"
1340
- ],
1341
- dependencies: ["@radix-ui/react-separator"],
1342
- radixPrimitive: "@radix-ui/react-separator",
1343
- props: {
1344
- orientation: { type: "'horizontal' | 'vertical'", default: "'horizontal'", description: "Orientation" }
1345
- },
1346
- example: `<Separator orientation="horizontal" />`
1347
- },
1348
- grid: {
1349
- name: "Grid",
1350
- category: "layout",
1351
- description: "Responsive CSS grid with column and gap configuration",
1352
- keywords: ["grid", "layout", "columns", "responsive", "css-grid"],
1353
- useCases: [
1354
- "Card grids",
1355
- "Gallery layouts",
1356
- "Dashboard layouts",
1357
- "Responsive content grids"
1358
- ],
1359
- dependencies: [],
1360
- props: {
1361
- columns: { type: "number | { sm?: number; md?: number; lg?: number }", default: "3", description: "Number of grid columns (responsive object supported)" },
1362
- gap: { type: "number | string", default: "4", description: "Gap between grid items (Tailwind spacing scale)" }
1363
- },
1364
- example: `<Grid columns={{ sm: 1, md: 2, lg: 3 }} gap={4}>
1365
- <Card>Item 1</Card>
1366
- <Card>Item 2</Card>
1367
- <Card>Item 3</Card>
1368
- </Grid>`
1369
- },
1370
- container: {
1371
- name: "Container",
1372
- category: "layout",
1373
- description: "Content wrapper with consistent max-width and padding variants",
1374
- keywords: ["container", "wrapper", "max-width", "centered", "layout"],
1375
- useCases: [
1376
- "Page content wrapper",
1377
- "Centered layouts",
1378
- "Responsive widths",
1379
- "Content alignment"
1380
- ],
1381
- dependencies: [],
1382
- props: {
1383
- size: { type: "'sm' | 'md' | 'lg' | 'xl' | 'full'", default: "'lg'", description: "Max-width variant" },
1384
- padding: { type: "boolean", default: "true", description: "Apply horizontal padding" }
1385
- },
1386
- example: `<Container size="lg">
1387
- <Heading level={1}>Page Title</Heading>
1388
- <Text>Content within max-width container.</Text>
1389
- </Container>`
1390
- },
1391
- stack: {
1392
- name: "Stack",
1393
- category: "layout",
1394
- description: "Flexbox layout for vertical or horizontal stacking with gap control",
1395
- keywords: ["stack", "flex", "vertical", "horizontal", "spacing", "layout"],
1396
- useCases: [
1397
- "Vertical layouts",
1398
- "Horizontal layouts",
1399
- "Form layouts",
1400
- "Button groups"
1401
- ],
1402
- dependencies: [],
1403
- props: {
1404
- direction: { type: "'vertical' | 'horizontal'", default: "'vertical'", description: "Stack direction" },
1405
- gap: { type: "number | string", default: "4", description: "Gap between items (Tailwind spacing scale)" },
1406
- align: { type: "'start' | 'center' | 'end' | 'stretch'", default: "'stretch'", description: "Cross-axis alignment" },
1407
- justify: { type: "'start' | 'center' | 'end' | 'between'", description: "Main-axis alignment" }
1408
- },
1409
- example: `<Stack direction="horizontal" gap={2} align="center">
1410
- <Button>Save</Button>
1411
- <Button variant="outline">Cancel</Button>
1412
- </Stack>`
1413
- },
1414
- sidebar: {
1415
- name: "Sidebar",
1416
- category: "layout",
1417
- description: "Navigation sidebar with header, content, footer sections and mobile overlay",
1418
- keywords: ["sidebar", "navigation", "panel", "drawer", "menu"],
1419
- useCases: [
1420
- "App navigation",
1421
- "Dashboard sidebars",
1422
- "Settings panels",
1423
- "Mobile menus"
1424
- ],
1425
- dependencies: ["@radix-ui/react-slot"],
1426
- radixPrimitive: "@radix-ui/react-slot",
1427
- props: {
1428
- isOpen: { type: "boolean", default: "true", description: "Whether sidebar is visible/expanded" }
1429
- },
1430
- subComponents: ["SidebarHeader", "SidebarContent", "SidebarFooter", "SidebarItem"],
1431
- example: `<Sidebar>
1432
- <SidebarHeader><h2>My App</h2></SidebarHeader>
1433
- <SidebarContent>
1434
- <SidebarItem icon={<Home />} isActive>Dashboard</SidebarItem>
1435
- <SidebarItem icon={<Settings />}>Settings</SidebarItem>
1436
- </SidebarContent>
1437
- <SidebarFooter>
1438
- <SidebarItem icon={<LogOut />}>Logout</SidebarItem>
1439
- </SidebarFooter>
1440
- </Sidebar>`
1441
- },
1442
- header: {
1443
- name: "Header",
1444
- category: "layout",
1445
- description: "Page header with sticky positioning, glass morphism, and mobile menu",
1446
- keywords: ["header", "navbar", "navigation", "sticky", "mobile-menu"],
1447
- useCases: [
1448
- "Site headers",
1449
- "App navigation bars",
1450
- "Sticky headers",
1451
- "Mobile-friendly navigation"
1452
- ],
1453
- dependencies: ["lucide-react"],
1454
- props: {
1455
- logo: { type: "ReactNode", description: "Logo element for header" },
1456
- links: { type: "{ label: string; href: string }[]", description: "Navigation links" },
1457
- sticky: { type: "boolean", default: "true", description: "Stick to top on scroll" },
1458
- transparent: { type: "boolean", default: "false", description: "Transparent background (for hero sections)" }
1459
- },
1460
- example: `<Header
1461
- logo={<Brand />}
1462
- links={[
1463
- { label: 'Home', href: '/' },
1464
- { label: 'About', href: '/about' },
1465
- ]}
1466
- />`
1467
- },
1468
- footer: {
1469
- name: "Footer",
1470
- category: "layout",
1471
- description: "Page footer with multi-column layout, social links, and copyright",
1472
- keywords: ["footer", "navigation", "links", "copyright", "social"],
1473
- useCases: [
1474
- "Site footers",
1475
- "Navigation sections",
1476
- "Contact information",
1477
- "Copyright notices"
1478
- ],
1479
- dependencies: [],
1480
- props: {
1481
- columns: { type: "{ title: string; links: { label: string; href: string }[] }[]", description: "Footer navigation columns" },
1482
- copyright: { type: "string", description: "Copyright text" },
1483
- socialLinks: { type: "{ icon: ReactNode; href: string }[]", description: "Social media links" }
1484
- },
1485
- example: `<Footer
1486
- columns={[{ title: 'Product', links: [{ label: 'Docs', href: '/docs' }] }]}
1487
- copyright="\xA9 2026 My Company"
1488
- />`
1489
- },
1490
- "customizer-panel": {
1491
- name: "CustomizerPanel",
1492
- category: "layout",
1493
- description: "Floating panel for theme, mode, and motion customization. Reads from ThemeProvider context.",
1494
- keywords: ["customizer", "theme", "settings", "preferences", "dark-mode"],
1495
- useCases: [
1496
- "Theme selection",
1497
- "Dark mode toggle",
1498
- "Motion preferences",
1499
- "User experience customization"
1500
- ],
1501
- dependencies: ["lucide-react", "@thesage/tokens"],
1502
- example: `// Place in your layout. It reads theme state from ThemeProvider.
1503
- <CustomizerPanel />`
1504
- },
1505
- "page-layout": {
1506
- name: "PageLayout",
1507
- category: "layout",
1508
- description: "Flexible page layout with header, nav stacks, breadcrumbs, and footer",
1509
- keywords: ["layout", "page", "template", "structure", "swiss-grid"],
1510
- useCases: [
1511
- "Page structure",
1512
- "Content layouts",
1513
- "Documentation pages",
1514
- "Dashboard layouts"
1515
- ],
1516
- dependencies: [],
1517
- props: {
1518
- header: { type: "ReactNode", description: "Header element" },
1519
- footer: { type: "ReactNode", description: "Footer element" },
1520
- sidebar: { type: "ReactNode", description: "Optional sidebar" },
1521
- breadcrumbs: { type: "ReactNode", description: "Breadcrumb navigation" }
1522
- },
1523
- example: `<PageLayout
1524
- header={<Header />}
1525
- footer={<Footer />}
1526
- >
1527
- <main>Page content</main>
1528
- </PageLayout>`
1529
- },
1530
- "page-template": {
1531
- name: "PageTemplate",
1532
- category: "layout",
1533
- description: "Opinionated page template with Swiss Grid design and customizer",
1534
- keywords: ["template", "page", "swiss-grid", "layout", "documentation"],
1535
- useCases: [
1536
- "Blog pages",
1537
- "Documentation pages",
1538
- "Standard app pages",
1539
- "Content-focused layouts"
1540
- ],
1541
- dependencies: [],
1542
- props: {
1543
- title: { type: "string", description: "Page title" },
1544
- description: { type: "string", description: "Page description" },
1545
- showCustomizer: { type: "boolean", default: "true", description: "Show customizer panel" }
1546
- },
1547
- example: `<PageTemplate title="Documentation" description="Learn the design system.">
1548
- <div>Content here.</div>
1549
- </PageTemplate>`
1550
- },
1551
- // ============================================================================
1552
- // ACTIONS - Additional (2 more)
1553
- // ============================================================================
1554
- link: {
1555
- name: "Link",
1556
- category: "actions",
1557
- description: "Styled anchor element with theme-aware colors and hover states",
1558
- keywords: ["link", "anchor", "href", "navigation", "a", "url"],
1559
- useCases: [
1560
- "Text links",
1561
- "Navigation links",
1562
- "External references",
1563
- "Inline actions"
1564
- ],
1565
- dependencies: [],
1566
- props: {
1567
- href: { type: "string", description: "Link URL", required: true },
1568
- variant: { type: "'default' | 'inline'", default: "'default'", description: "Default for standalone links, inline for text links" },
1569
- hoverEffect: { type: "boolean", default: "true", description: "Enable hover effect" }
1570
- },
1571
- example: `<Link href="/about" variant="inline">Learn More</Link>`
1572
- },
1573
- magnetic: {
1574
- name: "Magnetic",
1575
- category: "actions",
1576
- description: "Magnetic hover effect that attracts elements toward cursor",
1577
- keywords: ["magnetic", "hover", "effect", "cursor", "animation", "interactive"],
1578
- useCases: [
1579
- "Interactive buttons",
1580
- "Hover effects",
1581
- "Playful interactions",
1582
- "Cursor attraction"
1583
- ],
1584
- dependencies: ["framer-motion"],
1585
- props: {
1586
- children: { type: "ReactNode", description: "Element to apply magnetic effect to", required: true },
1587
- strength: { type: "number", default: "0.5", description: "Magnetic pull strength (0-1)" }
1588
- },
1589
- example: `<Magnetic strength={0.5}>
1590
- <Button>Hover me</Button>
1591
- </Magnetic>`
1592
- },
1593
- // ============================================================================
1594
- // FORMS - Additional (7 more)
1595
- // ============================================================================
1596
- "search-bar": {
1597
- name: "SearchBar",
1598
- category: "forms",
1599
- description: "Search input with icon, clear button, and keyboard shortcuts",
1600
- keywords: ["search", "input", "find", "query", "filter", "bar"],
1601
- useCases: [
1602
- "Site search",
1603
- "Content filtering",
1604
- "Command palette trigger",
1605
- "Data filtering"
1606
- ],
1607
- dependencies: ["lucide-react"],
1608
- props: {
1609
- value: { type: "string", description: "Controlled search value" },
1610
- onChange: { type: "(value: string) => void", description: "Callback on value change" },
1611
- placeholder: { type: "string", default: "'Search...'", description: "Placeholder text" },
1612
- onClear: { type: "() => void", description: "Callback on clear button click" }
1613
- },
1614
- example: `<SearchBar value={query} onChange={setQuery} placeholder="Search components..." />`
1615
- },
1616
- "filter-button": {
1617
- name: "FilterButton",
1618
- category: "forms",
1619
- description: "Button for filtering content with active/inactive states",
1620
- keywords: ["filter", "button", "toggle", "category", "selection"],
1621
- useCases: [
1622
- "Category filters",
1623
- "Tag selection",
1624
- "Content filtering",
1625
- "Quick filters"
1626
- ],
1627
- dependencies: [],
1628
- props: {
1629
- active: { type: "boolean", default: "false", description: "Whether filter is active" },
1630
- onClick: { type: "() => void", description: "Click handler" },
1631
- children: { type: "ReactNode", description: "Filter label", required: true }
1632
- },
1633
- example: `<FilterButton active={category === 'all'} onClick={() => setCategory('all')}>All</FilterButton>`
1634
- },
1635
- "theme-switcher": {
1636
- name: "ThemeSwitcher",
1637
- category: "forms",
1638
- description: "Multi-theme selector for switching between Studio, Terra, and Volt. Reads/writes to ThemeProvider context.",
1639
- keywords: ["theme", "switcher", "selector", "studio", "terra", "volt"],
1640
- useCases: [
1641
- "Theme selection",
1642
- "Brand customization",
1643
- "User preferences",
1644
- "Design switching"
1645
- ],
1646
- dependencies: [],
1647
- example: `// Reads from ThemeProvider context, no props needed.
1648
- <ThemeSwitcher />`
1649
- },
1650
- "theme-toggle": {
1651
- name: "ThemeToggle",
1652
- category: "forms",
1653
- description: "Light/dark mode toggle with smooth transitions. Reads/writes to ThemeProvider context.",
1654
- keywords: ["theme", "toggle", "dark-mode", "light-mode", "mode"],
1655
- useCases: [
1656
- "Dark mode switch",
1657
- "Light mode switch",
1658
- "Theme mode control",
1659
- "Accessibility preference"
1660
- ],
1661
- dependencies: ["lucide-react"],
1662
- example: `// Reads from ThemeProvider context, no props needed.
1663
- <ThemeToggle />`
1664
- },
1665
- "color-picker": {
1666
- name: "ColorPicker",
1667
- category: "forms",
1668
- description: "Color selection input with preset swatches and custom color support",
1669
- keywords: ["color", "picker", "palette", "swatch", "hex", "customization"],
1670
- useCases: [
1671
- "Brand color selection",
1672
- "Theme customization",
1673
- "Design tools",
1674
- "User preferences"
1675
- ],
1676
- dependencies: [],
1677
- props: {
1678
- value: { type: "string", description: "Selected color (hex string)" },
1679
- onChange: { type: "(color: string) => void", description: "Callback on color change" },
1680
- presets: { type: "string[]", description: "Preset color swatches (hex values)" }
1681
- },
1682
- example: `<ColorPicker value={color} onChange={setColor} presets={['#ef4444', '#3b82f6', '#22c55e']} />`
1683
- },
1684
- "drag-drop": {
1685
- name: "DragDrop",
1686
- category: "forms",
1687
- description: "Drag and drop file upload zone with preview support",
1688
- keywords: ["drag", "drop", "upload", "file", "dropzone", "input"],
1689
- useCases: [
1690
- "File uploads",
1691
- "Image uploads",
1692
- "Document uploads",
1693
- "Bulk imports"
1694
- ],
1695
- dependencies: [],
1696
- props: {
1697
- onDrop: { type: "(files: File[]) => void", description: "Callback when files are dropped", required: true },
1698
- accept: { type: "string", description: 'Accepted file types (e.g. "image/*")' },
1699
- maxSize: { type: "number", description: "Max file size in bytes" },
1700
- multiple: { type: "boolean", default: "true", description: "Allow multiple files" }
1701
- },
1702
- example: `<DragDrop onDrop={(files) => handleUpload(files)} accept="image/*" maxSize={5242880} />`
1703
- },
1704
- "text-field": {
1705
- name: "TextField",
1706
- category: "forms",
1707
- description: "Complete text input with label, helper text, and error states",
1708
- keywords: ["text", "field", "input", "label", "form", "validation"],
1709
- useCases: [
1710
- "Form fields",
1711
- "Labeled inputs",
1712
- "Validated inputs",
1713
- "Complete form controls"
1714
- ],
1715
- dependencies: [],
1716
- props: {
1717
- label: { type: "string", description: "Field label text" },
1718
- helperText: { type: "string", description: "Helper text below input" },
1719
- error: { type: "string", description: "Error message (shows error state when set)" },
1720
- required: { type: "boolean", default: "false", description: "Mark as required" }
1721
- },
1722
- example: `<TextField label="Email" helperText="We'll never share your email." error={errors.email} required />`
1723
- },
1724
- // ============================================================================
1725
- // NAVIGATION - Additional (4 more)
1726
- // ============================================================================
1727
- "nav-link": {
1728
- name: "NavLink",
1729
- category: "navigation",
1730
- description: "Navigation link with active state indicators and variants",
1731
- keywords: ["nav", "link", "navigation", "active", "menu", "item"],
1732
- useCases: [
1733
- "Navigation menus",
1734
- "Sidebar links",
1735
- "Header navigation",
1736
- "Active page indicators"
1737
- ],
1738
- dependencies: [],
1739
- props: {
1740
- href: { type: "string", description: "Link URL", required: true },
1741
- isActive: { type: "boolean", default: "false", description: "Active state indicator" },
1742
- variant: { type: "'default' | 'subtle'", default: "'default'", description: "Visual variant" }
1743
- },
1744
- example: `<NavLink href="/dashboard" isActive>Dashboard</NavLink>`
1745
- },
1746
- "secondary-nav": {
1747
- name: "SecondaryNav",
1748
- category: "navigation",
1749
- description: "Horizontal secondary navigation bar for section switching",
1750
- keywords: ["secondary", "navigation", "tabs", "sections", "subnav"],
1751
- useCases: [
1752
- "Section navigation",
1753
- "Page subsections",
1754
- "Tab-like navigation",
1755
- "Category switching"
1756
- ],
1757
- dependencies: [],
1758
- props: {
1759
- items: { type: "{ label: string; href: string; isActive?: boolean }[]", description: "Navigation items", required: true }
1760
- },
1761
- example: `<SecondaryNav items={[
1762
- { label: 'Overview', href: '/overview', isActive: true },
1763
- { label: 'Settings', href: '/settings' },
1764
- ]} />`
1765
- },
1766
- "tertiary-nav": {
1767
- name: "TertiaryNav",
1768
- category: "navigation",
1769
- description: "Third-level navigation for deep content hierarchies",
1770
- keywords: ["tertiary", "navigation", "deep", "hierarchy", "subnav"],
1771
- useCases: [
1772
- "Deep navigation",
1773
- "Documentation sections",
1774
- "Multi-level content",
1775
- "Nested categories"
1776
- ],
1777
- dependencies: [],
1778
- props: {
1779
- items: { type: "{ label: string; href: string; isActive?: boolean }[]", description: "Navigation items", required: true }
1780
- },
1781
- example: `<TertiaryNav items={[
1782
- { label: 'Props', href: '#props', isActive: true },
1783
- { label: 'Examples', href: '#examples' },
1784
- ]} />`
1785
- },
1786
- breadcrumbs: {
1787
- name: "Breadcrumbs",
1788
- category: "navigation",
1789
- description: "Breadcrumb navigation with home icon and variants",
1790
- keywords: ["breadcrumbs", "navigation", "path", "trail", "hierarchy"],
1791
- useCases: [
1792
- "Page location",
1793
- "Navigation trail",
1794
- "Hierarchical navigation",
1795
- "Back navigation"
1796
- ],
1797
- dependencies: ["lucide-react"],
1798
- props: {
1799
- items: { type: "{ label: string; href?: string }[]", description: "Breadcrumb trail items (last item is current page)", required: true },
1800
- showHome: { type: "boolean", default: "true", description: "Show home icon as first item" }
1801
- },
1802
- example: `<Breadcrumbs items={[
1803
- { label: 'Home', href: '/' },
1804
- { label: 'Components', href: '/components' },
1805
- { label: 'Button' },
1806
- ]} />`
1807
- },
1808
- // ============================================================================
1809
- // OVERLAYS - Additional (2 more)
1810
- // ============================================================================
1811
- modal: {
1812
- name: "Modal",
1813
- category: "overlays",
1814
- description: "Simple modal wrapper around Dialog with common patterns",
1815
- keywords: ["modal", "dialog", "popup", "overlay", "window"],
1816
- useCases: [
1817
- "Simple modals",
1818
- "Confirmation dialogs",
1819
- "Form modals",
1820
- "Content overlays"
1821
- ],
1822
- dependencies: ["@radix-ui/react-dialog"],
1823
- radixPrimitive: "@radix-ui/react-dialog",
1824
- props: {
1825
- open: { type: "boolean", description: "Controlled open state" },
1826
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" },
1827
- title: { type: "string", description: "Modal title" },
1828
- description: { type: "string", description: "Modal description" }
1829
- },
1830
- example: `<Modal open={isOpen} onOpenChange={setIsOpen} title="Confirm" description="Are you sure?">
1831
- <div className="flex gap-2 justify-end">
1832
- <Button variant="outline" onClick={() => setIsOpen(false)}>Cancel</Button>
1833
- <Button onClick={handleConfirm}>Confirm</Button>
1834
- </div>
1835
- </Modal>`
1836
- },
1837
- dropdown: {
1838
- name: "Dropdown",
1839
- category: "overlays",
1840
- description: "Simple dropdown wrapper for common dropdown patterns",
1841
- keywords: ["dropdown", "menu", "select", "options", "popover"],
1842
- useCases: [
1843
- "Action menus",
1844
- "User menus",
1845
- "Quick selections",
1846
- "Option lists"
1847
- ],
1848
- dependencies: ["@radix-ui/react-dropdown-menu"],
1849
- radixPrimitive: "@radix-ui/react-dropdown-menu",
1850
- props: {
1851
- open: { type: "boolean", description: "Controlled open state" },
1852
- onOpenChange: { type: "(open: boolean) => void", description: "Callback on open/close" }
1853
- },
1854
- example: `// For most cases, use DropdownMenu directly. This is a simplified wrapper.`
1855
- },
1856
- // ============================================================================
1857
- // FEEDBACK - Additional (2 more)
1858
- // ============================================================================
1859
- spinner: {
1860
- name: "Spinner",
1861
- category: "feedback",
1862
- description: "Animated loading spinner with size variants",
1863
- keywords: ["spinner", "loading", "loader", "progress", "waiting"],
1864
- useCases: [
1865
- "Loading states",
1866
- "Button loading",
1867
- "Data fetching",
1868
- "Async operations"
1869
- ],
1870
- dependencies: [],
1871
- props: {
1872
- size: { type: "'xs' | 'sm' | 'md' | 'lg' | 'xl'", default: "'md'", description: "Spinner size" },
1873
- variant: { type: "'primary' | 'secondary' | 'inherit'", default: "'primary'", description: "Color variant" }
1874
- },
1875
- example: `<Spinner size="md" />
1876
- <Button disabled><Spinner size="xs" variant="inherit" /> Loading...</Button>`
1877
- },
1878
- "progress-bar": {
1879
- name: "ProgressBar",
1880
- category: "feedback",
1881
- description: "Horizontal progress bar with percentage display",
1882
- keywords: ["progress", "bar", "loading", "percentage", "completion"],
1883
- useCases: [
1884
- "File uploads",
1885
- "Task progress",
1886
- "Loading indicators",
1887
- "Step completion"
1888
- ],
1889
- dependencies: [],
1890
- props: {
1891
- value: { type: "number", default: "0", description: "Progress value (0-100)" },
1892
- variant: { type: "'primary' | 'success' | 'warning' | 'error' | 'info'", default: "'primary'", description: "Color variant" },
1893
- size: { type: "'sm' | 'md' | 'lg'", default: "'md'", description: "Bar height" },
1894
- showLabel: { type: "boolean", default: "false", description: "Show percentage label" }
1895
- },
1896
- example: `<ProgressBar value={75} variant="success" showLabel />`
1897
- },
1898
- // ============================================================================
1899
- // DATA DISPLAY - Additional (5 more)
1900
- // ============================================================================
1901
- brand: {
1902
- name: "Brand",
1903
- category: "data-display",
1904
- description: "Theme-aware brand/logo component with size variants and link support",
1905
- keywords: ["brand", "logo", "identity", "header", "company"],
1906
- useCases: [
1907
- "Site logos",
1908
- "Header branding",
1909
- "Footer branding",
1910
- "App identity"
1911
- ],
1912
- dependencies: [],
1913
- props: {
1914
- variant: { type: "'default' | 'mark'", default: "'default'", description: "Full logo or mark only" },
1915
- size: { type: "'sm' | 'md' | 'lg'", default: "'md'", description: "Logo size" },
1916
- href: { type: "string", description: "Optional link URL (wraps in anchor)" }
1917
- },
1918
- example: `<Brand size="md" href="/" />`
1919
- },
1920
- "aspect-image": {
1921
- name: "AspectImage",
1922
- category: "data-display",
1923
- description: "Image with configurable aspect ratio, rounded corners, and captions",
1924
- keywords: ["image", "aspect", "ratio", "figure", "caption", "media"],
1925
- useCases: [
1926
- "Gallery images",
1927
- "Article images",
1928
- "Product images",
1929
- "Thumbnails with captions"
1930
- ],
1931
- dependencies: [],
1932
- props: {
1933
- src: { type: "string", description: "Image source URL", required: true },
1934
- alt: { type: "string", description: "Alt text for accessibility", required: true },
1935
- ratio: { type: "number", default: "16/9", description: "Aspect ratio" },
1936
- rounded: { type: "'none' | 'sm' | 'md' | 'lg' | 'full'", default: "'md'", description: "Border radius" },
1937
- caption: { type: "string", description: "Optional caption text below image" }
1938
- },
1939
- example: `<AspectImage src="/photo.jpg" alt="Team photo" ratio={4/3} caption="Our team" />`
1940
- },
1941
- "variable-weight-text": {
1942
- name: "VariableWeightText",
1943
- category: "data-display",
1944
- description: "Animated text with breathing font-weight effect using variable fonts",
1945
- keywords: ["variable", "font", "weight", "animation", "breathing", "motion"],
1946
- useCases: [
1947
- "Hero text",
1948
- "Emphasis text",
1949
- "Attention grabbing",
1950
- "Variable font showcase"
1951
- ],
1952
- dependencies: ["framer-motion"],
1953
- props: {
1954
- children: { type: "string", description: "Text content", required: true },
1955
- speed: { type: "number", default: "2", description: "Animation speed in seconds" },
1956
- minWeight: { type: "number", default: "100", description: "Minimum font weight" },
1957
- maxWeight: { type: "number", default: "900", description: "Maximum font weight" }
1958
- },
1959
- example: `<VariableWeightText speed={2} minWeight={200} maxWeight={800}>Design</VariableWeightText>`
1960
- },
1961
- typewriter: {
1962
- name: "Typewriter",
1963
- category: "data-display",
1964
- description: "Typewriter text animation with cursor and loop support",
1965
- keywords: ["typewriter", "typing", "animation", "cursor", "text", "effect"],
1966
- useCases: [
1967
- "Hero taglines",
1968
- "Terminal effects",
1969
- "Dynamic headings",
1970
- "Attention text"
1971
- ],
1972
- dependencies: ["framer-motion"],
1973
- props: {
1974
- words: { type: "string[]", description: "Words to cycle through", required: true },
1975
- speed: { type: "number", default: "100", description: "Typing speed in ms per character" },
1976
- loop: { type: "boolean", default: "true", description: "Loop through words continuously" },
1977
- cursor: { type: "boolean", default: "true", description: "Show blinking cursor" }
1978
- },
1979
- example: `<Typewriter words={['Developer', 'Designer', 'Creator']} speed={80} />`
1980
- },
1981
- "github-icon": {
1982
- name: "GitHubIcon",
1983
- category: "data-display",
1984
- description: "GitHub logo icon that inherits text color for theme support",
1985
- keywords: ["github", "icon", "social", "logo", "svg"],
1986
- useCases: [
1987
- "Social links",
1988
- "Footer icons",
1989
- "Repository links",
1990
- "Open source badges"
1991
- ],
1992
- dependencies: [],
1993
- props: {
1994
- size: { type: "number", default: "24", description: "Icon size in pixels" }
1995
- },
1996
- example: `<a href="https://github.com/you"><GitHubIcon size={20} /></a>`
1997
- },
1998
- // ============================================================================
1999
- // SPECIALTY - Backgrounds (3)
2000
- // ============================================================================
2001
- "warp-background": {
2002
- name: "WarpBackground",
2003
- category: "backgrounds",
2004
- description: "Animated warp speed star field background effect",
2005
- keywords: ["warp", "stars", "background", "animation", "space", "effect"],
2006
- useCases: [
2007
- "Hero backgrounds",
2008
- "Landing pages",
2009
- "Loading screens",
2010
- "Sci-fi themes"
2011
- ],
2012
- dependencies: ["framer-motion"],
2013
- props: {
2014
- speed: { type: "number", default: "1", description: "Warp speed multiplier" },
2015
- density: { type: "number", default: "200", description: "Number of stars" },
2016
- color: { type: "string", default: "'white'", description: "Star color" }
2017
- },
2018
- example: `<WarpBackground speed={1.5} density={300}>
2019
- <div className="relative z-10">Content over stars</div>
2020
- </WarpBackground>`
2021
- },
2022
- "faulty-terminal": {
2023
- name: "FaultyTerminal",
2024
- category: "backgrounds",
2025
- description: "Glitchy terminal background with flickering and scan lines",
2026
- keywords: ["terminal", "glitch", "background", "retro", "crt", "effect"],
2027
- useCases: [
2028
- "Retro themes",
2029
- "Hacker aesthetics",
2030
- "Error pages",
2031
- "Terminal UIs"
2032
- ],
2033
- dependencies: [],
2034
- props: {
2035
- children: { type: "ReactNode", description: "Content to render inside terminal" }
2036
- },
2037
- example: `<FaultyTerminal>
2038
- <p>$ system initializing...</p>
2039
- </FaultyTerminal>`
2040
- },
2041
- "orb-background": {
2042
- name: "OrbBackground",
2043
- category: "backgrounds",
2044
- description: "Animated floating orb with gradient blur effect",
2045
- keywords: ["orb", "gradient", "background", "animation", "blur", "ambient"],
2046
- useCases: [
2047
- "Landing pages",
2048
- "Hero sections",
2049
- "Ambient backgrounds",
2050
- "Modern aesthetics"
2051
- ],
2052
- dependencies: ["framer-motion"],
2053
- props: {
2054
- color: { type: "string", description: "Primary orb color" },
2055
- size: { type: "number", default: "400", description: "Orb diameter in pixels" }
2056
- },
2057
- example: `<OrbBackground color="var(--color-primary)" size={500}>
2058
- <div className="relative z-10">Content</div>
2059
- </OrbBackground>`
2060
- },
2061
- // ============================================================================
2062
- // SPECIALTY - Cursor (2)
2063
- // ============================================================================
2064
- "splash-cursor": {
2065
- name: "SplashCursor",
2066
- category: "cursor",
2067
- description: "Custom cursor with splash/ripple effect on click. WebGL-based fluid simulation.",
2068
- keywords: ["cursor", "splash", "ripple", "click", "effect", "interactive"],
2069
- useCases: [
2070
- "Interactive experiences",
2071
- "Creative portfolios",
2072
- "Playful interfaces",
2073
- "Click feedback"
2074
- ],
2075
- dependencies: [],
2076
- example: `// Place once in layout. Replaces default cursor globally.
2077
- <SplashCursor />`
2078
- },
2079
- "target-cursor": {
2080
- name: "TargetCursor",
2081
- category: "cursor",
2082
- description: "Custom cursor with target/crosshair appearance",
2083
- keywords: ["cursor", "target", "crosshair", "pointer", "custom"],
2084
- useCases: [
2085
- "Gaming interfaces",
2086
- "Precision tools",
2087
- "Interactive elements",
2088
- "Custom pointers"
2089
- ],
2090
- dependencies: [],
2091
- example: `// Place once in layout. Replaces default cursor globally.
2092
- <TargetCursor />`
2093
- },
2094
- // ============================================================================
2095
- // SPECIALTY - Motion (1)
2096
- // ============================================================================
2097
- "animated-beam": {
2098
- name: "AnimatedBeam",
2099
- category: "motion",
2100
- description: "Animated beam/line connecting two elements",
2101
- keywords: ["beam", "animation", "connection", "line", "flow", "motion"],
2102
- useCases: [
2103
- "Connecting elements",
2104
- "Data flow visualization",
2105
- "Architecture diagrams",
2106
- "Interactive connections"
2107
- ],
2108
- dependencies: ["framer-motion"],
2109
- props: {
2110
- fromRef: { type: "RefObject<HTMLElement>", description: "Ref to source element", required: true },
2111
- toRef: { type: "RefObject<HTMLElement>", description: "Ref to target element", required: true },
2112
- duration: { type: "number", default: "3", description: "Animation duration in seconds" }
2113
- },
2114
- example: `const fromRef = useRef(null)
2115
- const toRef = useRef(null)
2116
- <div ref={fromRef}>Source</div>
2117
- <div ref={toRef}>Target</div>
2118
- <AnimatedBeam fromRef={fromRef} toRef={toRef} />`
2119
- },
2120
- // ============================================================================
2121
- // SPECIALTY - Blocks (2)
2122
- // ============================================================================
2123
- hero: {
2124
- name: "Hero",
2125
- category: "blocks",
2126
- description: "Full-width hero section with title, subtitle, and CTA",
2127
- keywords: ["hero", "banner", "header", "landing", "cta", "section"],
2128
- useCases: [
2129
- "Landing pages",
2130
- "Page headers",
2131
- "Marketing sections",
2132
- "Feature highlights"
2133
- ],
2134
- dependencies: [],
2135
- props: {
2136
- title: { type: "string", description: "Main heading text", required: true },
2137
- subtitle: { type: "string", description: "Subtitle or tagline" },
2138
- cta: { type: "ReactNode", description: "Call-to-action element (Button, etc.)" },
2139
- background: { type: "ReactNode", description: "Optional background element (WarpBackground, OrbBackground, etc.)" }
2140
- },
2141
- example: `<Hero
2142
- title="Build Something Beautiful"
2143
- subtitle="A design system that brings joy."
2144
- cta={<Button size="lg">Get Started</Button>}
2145
- />`
2146
- },
2147
- "open-graph-card": {
2148
- name: "OpenGraphCard",
2149
- category: "blocks",
2150
- description: "Social media preview card for Open Graph metadata. Use in opengraph-image.tsx.",
2151
- keywords: ["open-graph", "social", "preview", "card", "meta", "share"],
2152
- useCases: [
2153
- "Social sharing previews",
2154
- "Link previews",
2155
- "Meta card generation",
2156
- "Marketing previews"
2157
- ],
2158
- dependencies: [],
2159
- props: {
2160
- title: { type: "string", default: "'Sage Design Engine'", description: "Main title text" },
2161
- description: { type: "string", description: "Subtitle text" },
2162
- variant: { type: "'primary' | 'secondary' | 'accent' | 'sage' | 'emerald' | 'gradient'", default: "'sage'", description: "Visual style variant" },
2163
- icon: { type: "ReactNode", description: "Custom logo or icon element" },
2164
- gradient: { type: "{ type: string; angle: number; colors: string[] }", description: 'Custom gradient config (variant="gradient")' },
2165
- primaryColor: { type: "string", description: "Override primary color (hex)" },
2166
- secondaryColor: { type: "string", description: "Override secondary color (hex)" },
2167
- accentColor: { type: "string", description: "Override accent color (hex)" }
2168
- },
2169
- example: `// In opengraph-image.tsx:
2170
- export default function OGImage() {
2171
- return <OpenGraphCard title="My Page" description="A great description" variant="primary" />
2172
- }`
2173
- }
2174
- };
2175
- function getComponentsByCategory(category) {
2176
- return Object.values(COMPONENT_REGISTRY).filter(
2177
- (component) => component.category === category
2178
- );
2179
- }
2180
- function searchComponents(query) {
2181
- const lowerQuery = query.toLowerCase();
2182
- return Object.values(COMPONENT_REGISTRY).filter((component) => {
2183
- return component.name.toLowerCase().includes(lowerQuery) || component.description.toLowerCase().includes(lowerQuery) || component.keywords.some((keyword) => keyword.toLowerCase().includes(lowerQuery)) || component.useCases.some((useCase) => useCase.toLowerCase().includes(lowerQuery));
2184
- });
2185
- }
2186
- function getComponent(name) {
2187
- const kebabName = name.toLowerCase().replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
2188
- return COMPONENT_REGISTRY[kebabName];
2189
- }
2190
- function getAllComponentNames() {
2191
- return Object.keys(COMPONENT_REGISTRY);
2192
- }
2193
- function getComponentCount() {
2194
- return Object.keys(COMPONENT_REGISTRY).length;
2195
- }
2196
-
2197
- // src/index.ts
2198
19
  var server = new Server(
2199
20
  {
2200
21
  name: "sds-mcp-server",
2201
- version: "0.1.0"
22
+ version: "0.8.0"
2202
23
  },
2203
24
  {
2204
25
  capabilities: {
@@ -2274,6 +95,65 @@ var TOOLS = [
2274
95
  },
2275
96
  required: ["name"]
2276
97
  }
98
+ },
99
+ {
100
+ name: "get_app_shell",
101
+ description: "Returns a complete, ready-to-use app shell with ThemeProvider, TooltipProvider, Toaster, tailwind.config, postcss.config, and globals.css import. Use this when scaffolding a new project with SDE.",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ framework: {
106
+ type: "string",
107
+ enum: ["nextjs", "vite"],
108
+ description: 'Target framework. Defaults to "vite".'
109
+ },
110
+ theme: {
111
+ type: "string",
112
+ enum: ["studio", "terra", "volt"],
113
+ description: 'Default theme. Defaults to "studio".'
114
+ }
115
+ }
116
+ }
117
+ },
118
+ {
119
+ name: "get_examples",
120
+ description: "Get usage examples for a specific component, including common patterns, compound component usage, and integration with other SDE components.",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {
124
+ name: {
125
+ type: "string",
126
+ description: 'Component name (e.g., "Button", "Dialog", "Card")'
127
+ }
128
+ },
129
+ required: ["name"]
130
+ }
131
+ },
132
+ {
133
+ name: "get_audit_checklist",
134
+ description: "Returns a post-generation checklist to verify SDE component usage is correct. Checks: provider wrapping, CSS variable usage (no hardcoded colors), accessibility attributes, motion preference respect, and import correctness.",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {}
138
+ }
139
+ },
140
+ {
141
+ name: "eject_component",
142
+ description: "Copy a component's source code into your local project for full customization. Returns step-by-step instructions to eject the component with rewritten imports.",
143
+ inputSchema: {
144
+ type: "object",
145
+ properties: {
146
+ name: {
147
+ type: "string",
148
+ description: 'Component name (e.g., "Button", "Card", "Dialog")'
149
+ },
150
+ targetDir: {
151
+ type: "string",
152
+ description: 'Target directory relative to project root. Defaults to "src/components/ui"'
153
+ }
154
+ },
155
+ required: ["name"]
156
+ }
2277
157
  }
2278
158
  ];
2279
159
  function formatComponentList(components) {
@@ -2463,6 +343,243 @@ function formatInstallationInstructions(component) {
2463
343
  `;
2464
344
  return output;
2465
345
  }
346
+ function generateAppShell(framework, theme) {
347
+ if (framework === "nextjs") {
348
+ return `# Next.js App Router Setup with Sage Design Engine
349
+
350
+ ## 1. Install dependencies
351
+
352
+ \`\`\`bash
353
+ pnpm add @thesage/ui
354
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
355
+ \`\`\`
356
+
357
+ ## 2. app/layout.tsx
358
+
359
+ \`\`\`tsx
360
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
361
+ import '@thesage/ui/globals.css'
362
+
363
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
364
+ return (
365
+ <html lang="en" suppressHydrationWarning>
366
+ <body>
367
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
368
+ <TooltipProvider delayDuration={300}>
369
+ {children}
370
+ <Toaster position="bottom-right" />
371
+ </TooltipProvider>
372
+ </ThemeProvider>
373
+ </body>
374
+ </html>
375
+ )
376
+ }
377
+ \`\`\`
378
+
379
+ ## 3. tailwind.config.js
380
+
381
+ \`\`\`js
382
+ /** @type {import('tailwindcss').Config} */
383
+ module.exports = {
384
+ content: [
385
+ './app/**/*.{ts,tsx}',
386
+ './components/**/*.{ts,tsx}',
387
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
388
+ ],
389
+ darkMode: 'class',
390
+ theme: { extend: {} },
391
+ plugins: [require('tailwindcss-animate')],
392
+ }
393
+ \`\`\`
394
+
395
+ ## 4. postcss.config.js
396
+
397
+ \`\`\`js
398
+ module.exports = {
399
+ plugins: { tailwindcss: {}, autoprefixer: {} },
400
+ }
401
+ \`\`\`
402
+
403
+ ## 5. app/page.tsx (starter)
404
+
405
+ \`\`\`tsx
406
+ import { Button, Card, Heading, Text } from '@thesage/ui'
407
+
408
+ export default function Home() {
409
+ return (
410
+ <main className="min-h-screen bg-background p-8">
411
+ <Card className="mx-auto max-w-md p-6">
412
+ <Heading as="h1" size="lg">Welcome</Heading>
413
+ <Text className="mt-2">Your app is ready.</Text>
414
+ <Button className="mt-4">Get Started</Button>
415
+ </Card>
416
+ </main>
417
+ )
418
+ }
419
+ \`\`\``;
420
+ }
421
+ return `# Vite + React Setup with Sage Design Engine
422
+
423
+ ## 1. Install dependencies
424
+
425
+ \`\`\`bash
426
+ pnpm add @thesage/ui
427
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
428
+ \`\`\`
429
+
430
+ ## 2. src/main.tsx
431
+
432
+ \`\`\`tsx
433
+ import React from 'react'
434
+ import ReactDOM from 'react-dom/client'
435
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
436
+ import '@thesage/ui/globals.css'
437
+ import App from './App'
438
+
439
+ ReactDOM.createRoot(document.getElementById('root')!).render(
440
+ <React.StrictMode>
441
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
442
+ <TooltipProvider delayDuration={300}>
443
+ <App />
444
+ <Toaster position="bottom-right" />
445
+ </TooltipProvider>
446
+ </ThemeProvider>
447
+ </React.StrictMode>
448
+ )
449
+ \`\`\`
450
+
451
+ ## 3. tailwind.config.js
452
+
453
+ \`\`\`js
454
+ /** @type {import('tailwindcss').Config} */
455
+ export default {
456
+ content: [
457
+ './index.html',
458
+ './src/**/*.{ts,tsx}',
459
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
460
+ ],
461
+ darkMode: 'class',
462
+ theme: { extend: {} },
463
+ plugins: [require('tailwindcss-animate')],
464
+ }
465
+ \`\`\`
466
+
467
+ ## 4. postcss.config.js
468
+
469
+ \`\`\`js
470
+ export default {
471
+ plugins: { tailwindcss: {}, autoprefixer: {} },
472
+ }
473
+ \`\`\`
474
+
475
+ ## 5. src/App.tsx (starter)
476
+
477
+ \`\`\`tsx
478
+ import { Button, Card, Heading, Text, ThemeToggle, ThemeSwitcher } from '@thesage/ui'
479
+
480
+ export default function App() {
481
+ return (
482
+ <div className="min-h-screen bg-background p-8">
483
+ <div className="mx-auto max-w-2xl space-y-8">
484
+ <div className="flex items-center justify-between">
485
+ <Heading as="h1" size="xl">My App</Heading>
486
+ <div className="flex gap-2">
487
+ <ThemeSwitcher />
488
+ <ThemeToggle />
489
+ </div>
490
+ </div>
491
+ <Card className="p-6">
492
+ <Text>Welcome to your new app. Start building!</Text>
493
+ <Button className="mt-4">Get Started</Button>
494
+ </Card>
495
+ </div>
496
+ </div>
497
+ )
498
+ }
499
+ \`\`\``;
500
+ }
501
+ function generateAuditChecklist() {
502
+ return `## SDE Usage Audit Checklist
503
+
504
+ ### Provider Wrapping
505
+ - [ ] ThemeProvider wraps the entire app
506
+ - [ ] TooltipProvider wraps any area using Tooltip components
507
+ - [ ] <Toaster /> is rendered at app root (required for toast notifications)
508
+ - [ ] '@thesage/ui/globals.css' is imported at the top level
509
+
510
+ ### Styling
511
+ - [ ] No hardcoded colors (no bg-white, text-black, bg-blue-500, text-gray-900)
512
+ - [ ] All colors use CSS variables (bg-background, text-foreground, bg-primary, border-border, etc.)
513
+ - [ ] className merging uses cn() utility, not string concatenation
514
+ - [ ] Dark mode works correctly via ThemeProvider (no manual dark: class management needed)
515
+ - [ ] tailwind.config.js content array includes: './node_modules/@thesage/ui/dist/**/*.{js,mjs}'
516
+
517
+ ### Accessibility
518
+ - [ ] All interactive elements are keyboard-navigable
519
+ - [ ] Dialogs trap focus and return focus on close
520
+ - [ ] Form inputs have associated Label components
521
+ - [ ] Animated components use useMotionPreference hook
522
+ - [ ] AlertDialog used (not Dialog) for destructive confirmations
523
+ - [ ] Images have alt text
524
+
525
+ ### Imports
526
+ - [ ] Components imported from '@thesage/ui' (not relative paths to node_modules)
527
+ - [ ] Heavy features use subpath imports:
528
+ - @thesage/ui/forms (react-hook-form + zod)
529
+ - @thesage/ui/dates (date-fns + react-day-picker)
530
+ - @thesage/ui/tables (@tanstack/react-table)
531
+ - @thesage/ui/dnd (@dnd-kit)
532
+ - [ ] No duplicate imports (same component from both @thesage/ui and a local file)
533
+ - [ ] Peer dependencies installed for subpath imports that need them
534
+
535
+ ### Component Usage
536
+ - [ ] Compound components use correct structure (e.g., Dialog needs DialogTrigger + DialogContent)
537
+ - [ ] Sheet used for desktop side panels, Drawer for mobile bottom sheets
538
+ - [ ] Combobox used (not Select) when searchable dropdown is needed
539
+ - [ ] Switch for instant toggles, Checkbox for form submission
540
+ - [ ] Toast/Sonner for transient notifications, Alert for persistent messages`;
541
+ }
542
+ function generateEjectInstructions(component, targetDir) {
543
+ const srcPath = `node_modules/@thesage/ui/src/components/${component.category}/${component.name}.tsx`;
544
+ const destPath = `${targetDir}/${component.name}.tsx`;
545
+ return `## Eject: ${component.name}
546
+
547
+ **Step 1:** Copy the source file:
548
+ \`\`\`bash
549
+ mkdir -p ${targetDir}
550
+ cp ${srcPath} ${destPath}
551
+ \`\`\`
552
+
553
+ **Step 2:** Rewrite imports in the copied file:
554
+ - Change \`from '../../lib/utils'\` \u2192 \`from '@/lib/utils'\`
555
+ - Change \`from '../actions/Button'\` \u2192 \`from '@thesage/ui'\` (keep using package for non-ejected deps)
556
+ - Change \`from '../../hooks/useMotionPreference'\` \u2192 \`from '@thesage/ui/hooks'\`
557
+
558
+ **Step 3:** Update your app imports:
559
+ \`\`\`tsx
560
+ // Before:
561
+ import { ${component.name} } from '@thesage/ui'
562
+ // After:
563
+ import { ${component.name} } from '@/${targetDir}/${component.name}'
564
+ \`\`\`
565
+
566
+ **Step 4:** Ensure \`cn()\` utility exists locally:
567
+ \`\`\`tsx
568
+ // src/lib/utils.ts
569
+ import { clsx, type ClassValue } from 'clsx'
570
+ import { twMerge } from 'tailwind-merge'
571
+ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }
572
+ \`\`\`
573
+
574
+ **Step 5:** Install clsx + tailwind-merge if not already present:
575
+ \`\`\`bash
576
+ pnpm add clsx tailwind-merge
577
+ \`\`\`
578
+
579
+ You now own this component. Modify it freely.
580
+
581
+ **Note:** The ejected component still works with SDE themes and CSS variables. You get full control over the markup and styling while staying in the SDE ecosystem.`;
582
+ }
2466
583
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2467
584
  return { tools: TOOLS };
2468
585
  });
@@ -2555,8 +672,8 @@ Try searching for:
2555
672
  };
2556
673
  }
2557
674
  case "get_component": {
2558
- const name2 = args?.name;
2559
- if (!name2) {
675
+ const componentName = args?.name;
676
+ if (!componentName) {
2560
677
  return {
2561
678
  content: [
2562
679
  {
@@ -2567,14 +684,14 @@ Try searching for:
2567
684
  isError: true
2568
685
  };
2569
686
  }
2570
- const component = getComponent(name2);
687
+ const component = getComponent(componentName);
2571
688
  if (!component) {
2572
689
  const allNames = getAllComponentNames();
2573
690
  return {
2574
691
  content: [
2575
692
  {
2576
693
  type: "text",
2577
- text: `Component "${name2}" not found.
694
+ text: `Component "${componentName}" not found.
2578
695
 
2579
696
  Available components:
2580
697
  ${allNames.join(", ")}`
@@ -2593,8 +710,8 @@ ${allNames.join(", ")}`
2593
710
  };
2594
711
  }
2595
712
  case "install_component": {
2596
- const name2 = args?.name;
2597
- if (!name2) {
713
+ const componentName = args?.name;
714
+ if (!componentName) {
2598
715
  return {
2599
716
  content: [
2600
717
  {
@@ -2605,13 +722,13 @@ ${allNames.join(", ")}`
2605
722
  isError: true
2606
723
  };
2607
724
  }
2608
- const component = getComponent(name2);
725
+ const component = getComponent(componentName);
2609
726
  if (!component) {
2610
727
  return {
2611
728
  content: [
2612
729
  {
2613
730
  type: "text",
2614
- text: `Component "${name2}" not found. Use search_components to find available components.`
731
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
2615
732
  }
2616
733
  ],
2617
734
  isError: true
@@ -2626,6 +743,150 @@ ${allNames.join(", ")}`
2626
743
  ]
2627
744
  };
2628
745
  }
746
+ case "get_app_shell": {
747
+ const framework = args?.framework || "vite";
748
+ const theme = args?.theme || "studio";
749
+ return {
750
+ content: [
751
+ {
752
+ type: "text",
753
+ text: generateAppShell(framework, theme)
754
+ }
755
+ ]
756
+ };
757
+ }
758
+ case "get_examples": {
759
+ const componentName = args?.name;
760
+ if (!componentName) {
761
+ return {
762
+ content: [
763
+ {
764
+ type: "text",
765
+ text: "Error: name parameter is required"
766
+ }
767
+ ],
768
+ isError: true
769
+ };
770
+ }
771
+ const component = getComponent(componentName);
772
+ if (!component) {
773
+ return {
774
+ content: [
775
+ {
776
+ type: "text",
777
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
778
+ }
779
+ ],
780
+ isError: true
781
+ };
782
+ }
783
+ let output = `## ${component.name} Examples
784
+
785
+ `;
786
+ if (component.example) {
787
+ output += `### Basic Usage
788
+
789
+ `;
790
+ output += `\`\`\`tsx
791
+ ${component.example}
792
+ \`\`\`
793
+
794
+ `;
795
+ }
796
+ const importParts = [component.name];
797
+ if (component.subComponents) {
798
+ importParts.push(...component.subComponents);
799
+ }
800
+ output += `### Import
801
+
802
+ `;
803
+ output += `\`\`\`tsx
804
+ import { ${importParts.join(", ")} } from '@thesage/ui'
805
+ \`\`\`
806
+
807
+ `;
808
+ if (component.useCases.length > 0) {
809
+ output += `### Common Use Cases
810
+
811
+ `;
812
+ component.useCases.forEach((useCase) => {
813
+ output += `- ${useCase}
814
+ `;
815
+ });
816
+ output += "\n";
817
+ }
818
+ if (component.props && Object.keys(component.props).length > 0) {
819
+ output += `### Key Props
820
+
821
+ `;
822
+ Object.entries(component.props).forEach(([propName, prop]) => {
823
+ const defaultStr = prop.default ? ` (default: ${prop.default})` : "";
824
+ output += `- **${propName}**: \`${prop.type}\`${defaultStr} \u2014 ${prop.description}
825
+ `;
826
+ });
827
+ output += "\n";
828
+ }
829
+ output += `
830
+ Full API reference: https://thesage.dev/llms-full.txt
831
+ `;
832
+ return {
833
+ content: [
834
+ {
835
+ type: "text",
836
+ text: output
837
+ }
838
+ ]
839
+ };
840
+ }
841
+ case "get_audit_checklist": {
842
+ return {
843
+ content: [
844
+ {
845
+ type: "text",
846
+ text: generateAuditChecklist()
847
+ }
848
+ ]
849
+ };
850
+ }
851
+ case "eject_component": {
852
+ const componentName = args?.name;
853
+ const targetDir = args?.targetDir || "src/components/ui";
854
+ if (!componentName) {
855
+ return {
856
+ content: [
857
+ {
858
+ type: "text",
859
+ text: "Error: name parameter is required"
860
+ }
861
+ ],
862
+ isError: true
863
+ };
864
+ }
865
+ const component = getComponent(componentName);
866
+ if (!component) {
867
+ const allNames = getAllComponentNames();
868
+ return {
869
+ content: [
870
+ {
871
+ type: "text",
872
+ text: `Component "${componentName}" not found.
873
+
874
+ Available components:
875
+ ${allNames.join(", ")}`
876
+ }
877
+ ],
878
+ isError: true
879
+ };
880
+ }
881
+ return {
882
+ content: [
883
+ {
884
+ type: "text",
885
+ text: generateEjectInstructions(component, targetDir)
886
+ }
887
+ ]
888
+ };
889
+ }
2629
890
  default:
2630
891
  return {
2631
892
  content: [
@@ -2652,8 +913,9 @@ ${allNames.join(", ")}`
2652
913
  async function main() {
2653
914
  const transport = new StdioServerTransport();
2654
915
  await server.connect(transport);
2655
- console.error("Sage UI MCP Server running");
916
+ console.error("Sage UI MCP Server v0.8.0 running");
2656
917
  console.error(`Components available: ${getComponentCount()}`);
918
+ console.error(`Tools available: ${TOOLS.length}`);
2657
919
  }
2658
920
  main().catch((error) => {
2659
921
  console.error("Fatal error:", error);