@spavn/ui 0.0.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.
Files changed (399) hide show
  1. package/cli/commands/add.d.ts +5 -0
  2. package/cli/commands/add.d.ts.map +1 -0
  3. package/cli/commands/add.js +104 -0
  4. package/cli/commands/add.js.map +1 -0
  5. package/cli/commands/init.d.ts +5 -0
  6. package/cli/commands/init.d.ts.map +1 -0
  7. package/cli/commands/init.js +129 -0
  8. package/cli/commands/init.js.map +1 -0
  9. package/cli/index.d.ts +3 -0
  10. package/cli/index.d.ts.map +1 -0
  11. package/cli/index.js +27 -0
  12. package/cli/index.js.map +1 -0
  13. package/cli/registry.d.ts +12 -0
  14. package/cli/registry.d.ts.map +1 -0
  15. package/cli/registry.js +1112 -0
  16. package/cli/registry.js.map +1 -0
  17. package/dist/index.js +7492 -0
  18. package/dist/index.umd.cjs +122 -0
  19. package/dist/style.css +1 -0
  20. package/mcp-server/index.d.ts +3 -0
  21. package/mcp-server/index.d.ts.map +1 -0
  22. package/mcp-server/index.js +1266 -0
  23. package/mcp-server/index.js.map +1 -0
  24. package/package.json +91 -0
  25. package/src/index.ts +432 -0
  26. package/src/lib/accordion/Accordion.test.ts +109 -0
  27. package/src/lib/accordion/Accordion.vue +11 -0
  28. package/src/lib/accordion/AccordionContent.vue +23 -0
  29. package/src/lib/accordion/AccordionItem.vue +16 -0
  30. package/src/lib/accordion/AccordionTrigger.vue +37 -0
  31. package/src/lib/accordion/index.ts +4 -0
  32. package/src/lib/alert/Alert.test.ts +144 -0
  33. package/src/lib/alert/Alert.vue +17 -0
  34. package/src/lib/alert/AlertDescription.vue +15 -0
  35. package/src/lib/alert/AlertTitle.vue +15 -0
  36. package/src/lib/alert/variants.test.ts +47 -0
  37. package/src/lib/alert/variants.ts +19 -0
  38. package/src/lib/alert-dialog/AlertDialog.vue +11 -0
  39. package/src/lib/alert-dialog/AlertDialogAction.vue +23 -0
  40. package/src/lib/alert-dialog/AlertDialogCancel.vue +24 -0
  41. package/src/lib/alert-dialog/AlertDialogContent.vue +33 -0
  42. package/src/lib/alert-dialog/AlertDialogDescription.vue +16 -0
  43. package/src/lib/alert-dialog/AlertDialogFooter.vue +17 -0
  44. package/src/lib/alert-dialog/AlertDialogHeader.vue +17 -0
  45. package/src/lib/alert-dialog/AlertDialogOverlay.vue +21 -0
  46. package/src/lib/alert-dialog/AlertDialogPortal.vue +11 -0
  47. package/src/lib/alert-dialog/AlertDialogTitle.vue +16 -0
  48. package/src/lib/alert-dialog/AlertDialogTrigger.vue +11 -0
  49. package/src/lib/alert-dialog/index.ts +11 -0
  50. package/src/lib/aspect-ratio/AspectRatio.vue +11 -0
  51. package/src/lib/aspect-ratio/index.ts +1 -0
  52. package/src/lib/avatar/Avatar.test.ts +58 -0
  53. package/src/lib/avatar/Avatar.vue +20 -0
  54. package/src/lib/avatar/AvatarFallback.vue +20 -0
  55. package/src/lib/avatar/AvatarImage.vue +10 -0
  56. package/src/lib/avatar/index.ts +3 -0
  57. package/src/lib/badge/Badge.test.ts +77 -0
  58. package/src/lib/badge/Badge.vue +20 -0
  59. package/src/lib/badge/index.ts +2 -0
  60. package/src/lib/badge/variants.test.ts +73 -0
  61. package/src/lib/badge/variants.ts +26 -0
  62. package/src/lib/breadcrumb/Breadcrumb.vue +13 -0
  63. package/src/lib/breadcrumb/BreadcrumbEllipsis.vue +35 -0
  64. package/src/lib/breadcrumb/BreadcrumbItem.vue +15 -0
  65. package/src/lib/breadcrumb/BreadcrumbLink.vue +21 -0
  66. package/src/lib/breadcrumb/BreadcrumbList.vue +22 -0
  67. package/src/lib/breadcrumb/BreadcrumbPage.vue +20 -0
  68. package/src/lib/breadcrumb/BreadcrumbSeparator.vue +34 -0
  69. package/src/lib/breadcrumb/index.ts +7 -0
  70. package/src/lib/button/Button.test.ts +84 -0
  71. package/src/lib/button/Button.vue +63 -0
  72. package/src/lib/button/index.ts +2 -0
  73. package/src/lib/button/variants.test.ts +128 -0
  74. package/src/lib/button/variants.ts +66 -0
  75. package/src/lib/button-group/ButtonGroup.vue +25 -0
  76. package/src/lib/button-group/index.ts +1 -0
  77. package/src/lib/calendar/Calendar.vue +58 -0
  78. package/src/lib/calendar/CalendarCell.vue +22 -0
  79. package/src/lib/calendar/CalendarCellTrigger.vue +28 -0
  80. package/src/lib/calendar/CalendarGrid.vue +16 -0
  81. package/src/lib/calendar/CalendarGridBody.vue +11 -0
  82. package/src/lib/calendar/CalendarGridHead.vue +11 -0
  83. package/src/lib/calendar/CalendarGridRow.vue +16 -0
  84. package/src/lib/calendar/CalendarHeadCell.vue +16 -0
  85. package/src/lib/calendar/CalendarHeader.vue +16 -0
  86. package/src/lib/calendar/CalendarHeading.vue +16 -0
  87. package/src/lib/calendar/CalendarNext.vue +37 -0
  88. package/src/lib/calendar/CalendarPrev.vue +37 -0
  89. package/src/lib/calendar/index.ts +12 -0
  90. package/src/lib/card/Card.test.ts +202 -0
  91. package/src/lib/card/Card.vue +36 -0
  92. package/src/lib/card/CardContent.vue +15 -0
  93. package/src/lib/card/CardDescription.vue +15 -0
  94. package/src/lib/card/CardFooter.vue +16 -0
  95. package/src/lib/card/CardHeader.vue +15 -0
  96. package/src/lib/card/CardTitle.vue +15 -0
  97. package/src/lib/card/index.ts +6 -0
  98. package/src/lib/carousel/Carousel.vue +73 -0
  99. package/src/lib/carousel/CarouselContent.vue +55 -0
  100. package/src/lib/carousel/CarouselItem.vue +25 -0
  101. package/src/lib/carousel/CarouselNext.vue +40 -0
  102. package/src/lib/carousel/CarouselPrevious.vue +40 -0
  103. package/src/lib/carousel/index.ts +6 -0
  104. package/src/lib/carousel/useCarousel.ts +24 -0
  105. package/src/lib/checkbox/Checkbox.test.ts +45 -0
  106. package/src/lib/checkbox/Checkbox.vue +39 -0
  107. package/src/lib/code-preview/CodePreview.vue +95 -0
  108. package/src/lib/collapsible/Collapsible.test.ts +95 -0
  109. package/src/lib/collapsible/Collapsible.vue +11 -0
  110. package/src/lib/collapsible/CollapsibleContent.vue +21 -0
  111. package/src/lib/collapsible/CollapsibleTrigger.vue +11 -0
  112. package/src/lib/collapsible/index.ts +3 -0
  113. package/src/lib/command/Command.vue +21 -0
  114. package/src/lib/command/CommandDialog.vue +25 -0
  115. package/src/lib/command/CommandEmpty.vue +16 -0
  116. package/src/lib/command/CommandGroup.vue +21 -0
  117. package/src/lib/command/CommandInput.vue +35 -0
  118. package/src/lib/command/CommandItem.vue +23 -0
  119. package/src/lib/command/CommandLabel.vue +16 -0
  120. package/src/lib/command/CommandList.vue +16 -0
  121. package/src/lib/command/CommandSeparator.vue +14 -0
  122. package/src/lib/command/index.ts +9 -0
  123. package/src/lib/context-menu/ContextMenu.vue +11 -0
  124. package/src/lib/context-menu/ContextMenuCheckboxItem.vue +45 -0
  125. package/src/lib/context-menu/ContextMenuContent.vue +31 -0
  126. package/src/lib/context-menu/ContextMenuGroup.vue +11 -0
  127. package/src/lib/context-menu/ContextMenuItem.vue +24 -0
  128. package/src/lib/context-menu/ContextMenuLabel.vue +16 -0
  129. package/src/lib/context-menu/ContextMenuRadioGroup.vue +11 -0
  130. package/src/lib/context-menu/ContextMenuRadioItem.vue +44 -0
  131. package/src/lib/context-menu/ContextMenuSeparator.vue +14 -0
  132. package/src/lib/context-menu/ContextMenuShortcut.vue +11 -0
  133. package/src/lib/context-menu/ContextMenuSub.vue +11 -0
  134. package/src/lib/context-menu/ContextMenuSubContent.vue +28 -0
  135. package/src/lib/context-menu/ContextMenuSubTrigger.vue +36 -0
  136. package/src/lib/context-menu/ContextMenuTrigger.vue +11 -0
  137. package/src/lib/context-menu/index.ts +14 -0
  138. package/src/lib/data-table/DataTable.vue +226 -0
  139. package/src/lib/date-range-picker/DateRangePicker.vue +201 -0
  140. package/src/lib/date-time-picker/DateTimePicker.vue +159 -0
  141. package/src/lib/dialog/Dialog.test.ts +87 -0
  142. package/src/lib/dialog/Dialog.vue +14 -0
  143. package/src/lib/dialog/DialogClose.vue +11 -0
  144. package/src/lib/dialog/DialogContent.vue +56 -0
  145. package/src/lib/dialog/DialogDescription.vue +15 -0
  146. package/src/lib/dialog/DialogFooter.vue +17 -0
  147. package/src/lib/dialog/DialogHeader.vue +17 -0
  148. package/src/lib/dialog/DialogOverlay.vue +20 -0
  149. package/src/lib/dialog/DialogPortal.vue +11 -0
  150. package/src/lib/dialog/DialogTitle.vue +15 -0
  151. package/src/lib/dialog/DialogTrigger.vue +11 -0
  152. package/src/lib/dialog/index.ts +10 -0
  153. package/src/lib/direction/Direction.vue +13 -0
  154. package/src/lib/drawer/Drawer.vue +11 -0
  155. package/src/lib/drawer/DrawerClose.vue +11 -0
  156. package/src/lib/drawer/DrawerContent.vue +31 -0
  157. package/src/lib/drawer/DrawerDescription.vue +16 -0
  158. package/src/lib/drawer/DrawerFooter.vue +15 -0
  159. package/src/lib/drawer/DrawerHeader.vue +15 -0
  160. package/src/lib/drawer/DrawerOverlay.vue +21 -0
  161. package/src/lib/drawer/DrawerTitle.vue +16 -0
  162. package/src/lib/drawer/DrawerTrigger.vue +11 -0
  163. package/src/lib/drawer/index.ts +9 -0
  164. package/src/lib/dropdown-menu/DropdownMenu.test.ts +146 -0
  165. package/src/lib/dropdown-menu/DropdownMenu.vue +11 -0
  166. package/src/lib/dropdown-menu/DropdownMenuCheckboxItem.vue +45 -0
  167. package/src/lib/dropdown-menu/DropdownMenuContent.vue +31 -0
  168. package/src/lib/dropdown-menu/DropdownMenuGroup.vue +11 -0
  169. package/src/lib/dropdown-menu/DropdownMenuItem.vue +24 -0
  170. package/src/lib/dropdown-menu/DropdownMenuLabel.vue +16 -0
  171. package/src/lib/dropdown-menu/DropdownMenuRadioGroup.vue +11 -0
  172. package/src/lib/dropdown-menu/DropdownMenuRadioItem.vue +44 -0
  173. package/src/lib/dropdown-menu/DropdownMenuSeparator.vue +14 -0
  174. package/src/lib/dropdown-menu/DropdownMenuShortcut.vue +11 -0
  175. package/src/lib/dropdown-menu/DropdownMenuSub.vue +11 -0
  176. package/src/lib/dropdown-menu/DropdownMenuSubContent.vue +27 -0
  177. package/src/lib/dropdown-menu/DropdownMenuSubTrigger.vue +36 -0
  178. package/src/lib/dropdown-menu/DropdownMenuTrigger.vue +11 -0
  179. package/src/lib/dropdown-menu/index.ts +14 -0
  180. package/src/lib/empty/Empty.vue +11 -0
  181. package/src/lib/empty/EmptyDescription.vue +11 -0
  182. package/src/lib/empty/EmptyIcon.vue +8 -0
  183. package/src/lib/empty/EmptyTitle.vue +11 -0
  184. package/src/lib/empty/index.ts +4 -0
  185. package/src/lib/feature-card/FeatureCard.vue +177 -0
  186. package/src/lib/feature-card/README.md +139 -0
  187. package/src/lib/feature-card/index.ts +1 -0
  188. package/src/lib/field/Field.vue +15 -0
  189. package/src/lib/field/FieldDescription.vue +15 -0
  190. package/src/lib/field/FieldError.vue +15 -0
  191. package/src/lib/field/FieldLabel.vue +24 -0
  192. package/src/lib/field/index.ts +4 -0
  193. package/src/lib/hover-card/HoverCard.vue +11 -0
  194. package/src/lib/hover-card/HoverCardContent.vue +31 -0
  195. package/src/lib/hover-card/HoverCardTrigger.vue +11 -0
  196. package/src/lib/hover-card/index.ts +3 -0
  197. package/src/lib/icon/Icon.vue +33 -0
  198. package/src/lib/input/Input.test.ts +71 -0
  199. package/src/lib/input/Input.vue +85 -0
  200. package/src/lib/input/index.ts +1 -0
  201. package/src/lib/input-group/InputGroup.vue +24 -0
  202. package/src/lib/input-group/InputGroupAddon.vue +25 -0
  203. package/src/lib/input-group/InputGroupInput.vue +38 -0
  204. package/src/lib/input-group/index.ts +3 -0
  205. package/src/lib/input-otp/InputOTP.vue +16 -0
  206. package/src/lib/input-otp/InputOTPGroup.vue +11 -0
  207. package/src/lib/input-otp/InputOTPSeparator.vue +8 -0
  208. package/src/lib/input-otp/InputOTPSlot.vue +20 -0
  209. package/src/lib/input-otp/index.ts +4 -0
  210. package/src/lib/kbd/Kbd.vue +11 -0
  211. package/src/lib/kbd/index.ts +1 -0
  212. package/src/lib/label/Label.test.ts +38 -0
  213. package/src/lib/label/Label.vue +20 -0
  214. package/src/lib/label/index.ts +1 -0
  215. package/src/lib/layout/AppFooter.vue +18 -0
  216. package/src/lib/layout/AppHeader.vue +22 -0
  217. package/src/lib/layout/AppLayout.vue +13 -0
  218. package/src/lib/layout/AppMain.vue +22 -0
  219. package/src/lib/layout/AppSidebar.vue +50 -0
  220. package/src/lib/menubar/Menubar.vue +21 -0
  221. package/src/lib/menubar/MenubarCheckboxItem.vue +45 -0
  222. package/src/lib/menubar/MenubarContent.vue +30 -0
  223. package/src/lib/menubar/MenubarGroup.vue +11 -0
  224. package/src/lib/menubar/MenubarItem.vue +24 -0
  225. package/src/lib/menubar/MenubarLabel.vue +16 -0
  226. package/src/lib/menubar/MenubarMenu.vue +11 -0
  227. package/src/lib/menubar/MenubarRadioGroup.vue +11 -0
  228. package/src/lib/menubar/MenubarRadioItem.vue +44 -0
  229. package/src/lib/menubar/MenubarSeparator.vue +14 -0
  230. package/src/lib/menubar/MenubarShortcut.vue +11 -0
  231. package/src/lib/menubar/MenubarSub.vue +11 -0
  232. package/src/lib/menubar/MenubarSubContent.vue +26 -0
  233. package/src/lib/menubar/MenubarSubTrigger.vue +36 -0
  234. package/src/lib/menubar/MenubarTrigger.vue +21 -0
  235. package/src/lib/menubar/index.ts +15 -0
  236. package/src/lib/modal/Modal.test.ts +81 -0
  237. package/src/lib/modal/Modal.vue +12 -0
  238. package/src/lib/modal/ModalClose.vue +9 -0
  239. package/src/lib/modal/ModalContent.vue +32 -0
  240. package/src/lib/modal/ModalTrigger.vue +11 -0
  241. package/src/lib/multi-select/MultiSelect.vue +186 -0
  242. package/src/lib/multi-select/MultiSelectItem.vue +47 -0
  243. package/src/lib/native-select/NativeSelect.vue +41 -0
  244. package/src/lib/native-select/index.ts +1 -0
  245. package/src/lib/navigation-menu/NavigationMenu.vue +23 -0
  246. package/src/lib/navigation-menu/NavigationMenuContent.vue +21 -0
  247. package/src/lib/navigation-menu/NavigationMenuIndicator.vue +21 -0
  248. package/src/lib/navigation-menu/NavigationMenuItem.vue +11 -0
  249. package/src/lib/navigation-menu/NavigationMenuLink.vue +23 -0
  250. package/src/lib/navigation-menu/NavigationMenuList.vue +21 -0
  251. package/src/lib/navigation-menu/NavigationMenuTrigger.vue +36 -0
  252. package/src/lib/navigation-menu/NavigationMenuViewport.vue +24 -0
  253. package/src/lib/navigation-menu/index.ts +8 -0
  254. package/src/lib/pagination/Pagination.vue +16 -0
  255. package/src/lib/pagination/PaginationContent.vue +16 -0
  256. package/src/lib/pagination/PaginationEllipsis.vue +27 -0
  257. package/src/lib/pagination/PaginationFirst.vue +25 -0
  258. package/src/lib/pagination/PaginationItem.vue +11 -0
  259. package/src/lib/pagination/PaginationLast.vue +25 -0
  260. package/src/lib/pagination/PaginationLink.vue +31 -0
  261. package/src/lib/pagination/PaginationNext.vue +24 -0
  262. package/src/lib/pagination/PaginationPrev.vue +24 -0
  263. package/src/lib/pagination/index.ts +9 -0
  264. package/src/lib/popover/Popover.test.ts +97 -0
  265. package/src/lib/popover/Popover.vue +11 -0
  266. package/src/lib/popover/PopoverContent.vue +31 -0
  267. package/src/lib/popover/PopoverTrigger.vue +11 -0
  268. package/src/lib/popover/index.ts +3 -0
  269. package/src/lib/progress/Progress.test.ts +59 -0
  270. package/src/lib/progress/Progress.vue +41 -0
  271. package/src/lib/progress/index.ts +1 -0
  272. package/src/lib/radio-group/RadioGroup.test.ts +45 -0
  273. package/src/lib/radio-group/RadioGroup.vue +16 -0
  274. package/src/lib/radio-group/RadioGroupItem.vue +35 -0
  275. package/src/lib/radio-group/index.ts +2 -0
  276. package/src/lib/resizable/ResizableHandle.vue +38 -0
  277. package/src/lib/resizable/ResizablePanel.vue +11 -0
  278. package/src/lib/resizable/ResizablePanelGroup.vue +16 -0
  279. package/src/lib/resizable/index.ts +3 -0
  280. package/src/lib/scroll-area/ScrollArea.vue +29 -0
  281. package/src/lib/scroll-area/ScrollBar.vue +26 -0
  282. package/src/lib/scroll-area/index.ts +2 -0
  283. package/src/lib/select/Select.vue +11 -0
  284. package/src/lib/select/SelectContent.vue +48 -0
  285. package/src/lib/select/SelectGroup.vue +11 -0
  286. package/src/lib/select/SelectItem.vue +41 -0
  287. package/src/lib/select/SelectLabel.vue +16 -0
  288. package/src/lib/select/SelectScrollDownButton.vue +29 -0
  289. package/src/lib/select/SelectScrollUpButton.vue +29 -0
  290. package/src/lib/select/SelectSeparator.vue +14 -0
  291. package/src/lib/select/SelectTrigger.vue +37 -0
  292. package/src/lib/select/SelectValue.vue +11 -0
  293. package/src/lib/select/index.ts +10 -0
  294. package/src/lib/separator/Separator.test.ts +47 -0
  295. package/src/lib/separator/Separator.vue +23 -0
  296. package/src/lib/separator/index.ts +1 -0
  297. package/src/lib/sheet/Sheet.test.ts +118 -0
  298. package/src/lib/sheet/Sheet.vue +11 -0
  299. package/src/lib/sheet/SheetClose.vue +11 -0
  300. package/src/lib/sheet/SheetContent.vue +68 -0
  301. package/src/lib/sheet/SheetDescription.vue +16 -0
  302. package/src/lib/sheet/SheetFooter.vue +15 -0
  303. package/src/lib/sheet/SheetHeader.vue +15 -0
  304. package/src/lib/sheet/SheetOverlay.vue +21 -0
  305. package/src/lib/sheet/SheetTitle.vue +16 -0
  306. package/src/lib/sheet/SheetTrigger.vue +11 -0
  307. package/src/lib/sheet/index.ts +9 -0
  308. package/src/lib/sidebar/Sidebar.vue +19 -0
  309. package/src/lib/sidebar/SidebarContent.vue +11 -0
  310. package/src/lib/sidebar/SidebarFooter.vue +11 -0
  311. package/src/lib/sidebar/SidebarGroup.vue +11 -0
  312. package/src/lib/sidebar/SidebarGroupLabel.vue +11 -0
  313. package/src/lib/sidebar/SidebarHeader.vue +11 -0
  314. package/src/lib/sidebar/SidebarInset.vue +11 -0
  315. package/src/lib/sidebar/SidebarMenu.vue +11 -0
  316. package/src/lib/sidebar/SidebarMenuButton.vue +20 -0
  317. package/src/lib/sidebar/SidebarMenuItem.vue +9 -0
  318. package/src/lib/sidebar/SidebarProvider.vue +23 -0
  319. package/src/lib/sidebar/SidebarSeparator.vue +9 -0
  320. package/src/lib/sidebar/SidebarTrigger.vue +24 -0
  321. package/src/lib/sidebar/context.ts +8 -0
  322. package/src/lib/sidebar/useSidebar.ts +10 -0
  323. package/src/lib/skeleton/Skeleton.test.ts +36 -0
  324. package/src/lib/skeleton/Skeleton.vue +9 -0
  325. package/src/lib/skeleton/index.ts +1 -0
  326. package/src/lib/slider/Slider.test.ts +63 -0
  327. package/src/lib/slider/Slider.vue +67 -0
  328. package/src/lib/slider/index.ts +1 -0
  329. package/src/lib/sonner/Toaster.vue +29 -0
  330. package/src/lib/spinner/Spinner.vue +34 -0
  331. package/src/lib/spinner/index.ts +1 -0
  332. package/src/lib/stats-card/StatsCard.vue +179 -0
  333. package/src/lib/stats-card/index.ts +2 -0
  334. package/src/lib/styles.ts +133 -0
  335. package/src/lib/switch/Switch.test.ts +52 -0
  336. package/src/lib/switch/Switch.vue +43 -0
  337. package/src/lib/table/Table.test.ts +150 -0
  338. package/src/lib/table/Table.vue +13 -0
  339. package/src/lib/table/TableBody.vue +11 -0
  340. package/src/lib/table/TableCaption.vue +11 -0
  341. package/src/lib/table/TableCell.vue +11 -0
  342. package/src/lib/table/TableFooter.vue +11 -0
  343. package/src/lib/table/TableHead.vue +11 -0
  344. package/src/lib/table/TableHeader.vue +11 -0
  345. package/src/lib/table/TableRow.vue +11 -0
  346. package/src/lib/table/index.ts +8 -0
  347. package/src/lib/tabs/Tabs.test.ts +150 -0
  348. package/src/lib/tabs/Tabs.vue +11 -0
  349. package/src/lib/tabs/TabsContent.vue +21 -0
  350. package/src/lib/tabs/TabsList.vue +21 -0
  351. package/src/lib/tabs/TabsTrigger.vue +21 -0
  352. package/src/lib/tabs/index.ts +4 -0
  353. package/src/lib/textarea/Textarea.test.ts +41 -0
  354. package/src/lib/textarea/Textarea.vue +36 -0
  355. package/src/lib/theme-selector/README.md +154 -0
  356. package/src/lib/theme-selector/ThemeSelector.vue +279 -0
  357. package/src/lib/theme-selector/index.ts +2 -0
  358. package/src/lib/time-picker/TimePicker.vue +162 -0
  359. package/src/lib/time-picker/TimePickerSegment.vue +176 -0
  360. package/src/lib/toast/Toast.test.ts +80 -0
  361. package/src/lib/toast/Toast.vue +56 -0
  362. package/src/lib/toast/ToastAction.vue +23 -0
  363. package/src/lib/toast/ToastClose.vue +38 -0
  364. package/src/lib/toast/ToastDescription.vue +12 -0
  365. package/src/lib/toast/ToastProvider.ts +65 -0
  366. package/src/lib/toast/ToastTitle.vue +12 -0
  367. package/src/lib/toast/ToastViewport.vue +18 -0
  368. package/src/lib/toast/Toaster.vue +50 -0
  369. package/src/lib/toast/index.ts +7 -0
  370. package/src/lib/toggle/Toggle.vue +21 -0
  371. package/src/lib/toggle/index.ts +2 -0
  372. package/src/lib/toggle/variants.test.ts +87 -0
  373. package/src/lib/toggle/variants.ts +21 -0
  374. package/src/lib/toggle-group/ToggleGroup.vue +24 -0
  375. package/src/lib/toggle-group/ToggleGroupItem.vue +33 -0
  376. package/src/lib/toggle-group/index.ts +2 -0
  377. package/src/lib/tooltip/Tooltip.test.ts +87 -0
  378. package/src/lib/tooltip/Tooltip.vue +11 -0
  379. package/src/lib/tooltip/TooltipContent.vue +30 -0
  380. package/src/lib/tooltip/TooltipProvider.vue +11 -0
  381. package/src/lib/tooltip/TooltipTrigger.vue +11 -0
  382. package/src/lib/tooltip/index.ts +4 -0
  383. package/src/lib/typography/TypographyBlockquote.vue +11 -0
  384. package/src/lib/typography/TypographyH1.vue +11 -0
  385. package/src/lib/typography/TypographyH2.vue +11 -0
  386. package/src/lib/typography/TypographyH3.vue +11 -0
  387. package/src/lib/typography/TypographyH4.vue +11 -0
  388. package/src/lib/typography/TypographyInlineCode.vue +11 -0
  389. package/src/lib/typography/TypographyLarge.vue +11 -0
  390. package/src/lib/typography/TypographyLead.vue +11 -0
  391. package/src/lib/typography/TypographyMuted.vue +11 -0
  392. package/src/lib/typography/TypographyP.vue +11 -0
  393. package/src/lib/typography/TypographySmall.vue +11 -0
  394. package/src/lib/typography/index.ts +11 -0
  395. package/src/lib/utils.test.ts +45 -0
  396. package/src/lib/utils.ts +14 -0
  397. package/src/lib/variants.ts +45 -0
  398. package/src/theme.css +203 -0
  399. package/src/vite-env.d.ts +6 -0
@@ -0,0 +1,1266 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ const components = [
6
+ // ─── Layout ──────────────────────────────────────────────────
7
+ {
8
+ name: 'AppLayout',
9
+ category: 'layout',
10
+ description: 'Root application layout shell that composes header, sidebar, main, and footer regions',
11
+ props: [],
12
+ examples: ['<AppLayout><AppHeader /><AppMain>Content</AppMain></AppLayout>']
13
+ },
14
+ {
15
+ name: 'AppHeader',
16
+ category: 'layout',
17
+ description: 'Sticky application header bar with slot-based content areas',
18
+ props: [],
19
+ examples: ['<AppHeader><h1>My App</h1></AppHeader>']
20
+ },
21
+ {
22
+ name: 'AppSidebar',
23
+ category: 'layout',
24
+ description: 'Collapsible application sidebar for primary navigation',
25
+ props: [],
26
+ examples: ['<AppSidebar>Navigation items</AppSidebar>']
27
+ },
28
+ {
29
+ name: 'AppFooter',
30
+ category: 'layout',
31
+ description: 'Application footer region pinned to the bottom of the layout',
32
+ props: [],
33
+ examples: ['<AppFooter>Footer content</AppFooter>']
34
+ },
35
+ {
36
+ name: 'AppMain',
37
+ category: 'layout',
38
+ description: 'Main content area that fills the remaining space in AppLayout',
39
+ props: [],
40
+ examples: ['<AppMain><router-view /></AppMain>']
41
+ },
42
+ {
43
+ name: 'SidebarProvider',
44
+ category: 'layout',
45
+ description: 'Context provider that manages sidebar open/collapsed state for child components',
46
+ props: ['defaultOpen', 'open'],
47
+ subComponents: ['Sidebar', 'SidebarHeader', 'SidebarContent', 'SidebarFooter', 'SidebarGroup', 'SidebarGroupLabel', 'SidebarMenu', 'SidebarMenuItem', 'SidebarMenuButton', 'SidebarTrigger', 'SidebarSeparator', 'SidebarInset'],
48
+ examples: ['<SidebarProvider><Sidebar>...</Sidebar><SidebarInset>Page</SidebarInset></SidebarProvider>']
49
+ },
50
+ {
51
+ name: 'Sidebar',
52
+ category: 'layout',
53
+ description: 'Collapsible sidebar panel that sits inside a SidebarProvider',
54
+ props: ['side', 'variant', 'collapsible'],
55
+ examples: ['<Sidebar><SidebarContent>...</SidebarContent></Sidebar>']
56
+ },
57
+ {
58
+ name: 'SidebarTrigger',
59
+ category: 'layout',
60
+ description: 'Button that toggles sidebar open/collapsed state',
61
+ props: [],
62
+ examples: ['<SidebarTrigger />']
63
+ },
64
+ {
65
+ name: 'SidebarInset',
66
+ category: 'layout',
67
+ description: 'Main content wrapper that adjusts its width when the sidebar expands or collapses',
68
+ props: [],
69
+ examples: ['<SidebarInset>Main page content</SidebarInset>']
70
+ },
71
+ // ─── Forms ───────────────────────────────────────────────────
72
+ {
73
+ name: 'Button',
74
+ category: 'forms',
75
+ description: 'Interactive button with multiple variants, sizes, and optional icon slots',
76
+ props: ['variant', 'size', 'disabled', 'asChild'],
77
+ examples: ['<Button>Click me</Button>', '<Button variant="outline" size="sm">Small</Button>']
78
+ },
79
+ {
80
+ name: 'Input',
81
+ category: 'forms',
82
+ description: 'Single-line text input with v-model support and built-in validation states',
83
+ props: ['type', 'placeholder', 'disabled', 'modelValue'],
84
+ examples: ['<Input v-model="name" placeholder="Enter name" />']
85
+ },
86
+ {
87
+ name: 'Textarea',
88
+ category: 'forms',
89
+ description: 'Multi-line text input with auto-resize support',
90
+ props: ['placeholder', 'disabled', 'modelValue', 'rows'],
91
+ examples: ['<Textarea v-model="message" placeholder="Type here..." />']
92
+ },
93
+ {
94
+ name: 'Checkbox',
95
+ category: 'forms',
96
+ description: 'Checkbox input with indeterminate state support',
97
+ props: ['modelValue', 'disabled', 'indeterminate'],
98
+ examples: ['<Checkbox v-model="checked" />']
99
+ },
100
+ {
101
+ name: 'Switch',
102
+ category: 'forms',
103
+ description: 'Toggle switch for boolean on/off values',
104
+ props: ['checked', 'disabled'],
105
+ examples: ['<Switch v-model:checked="enabled" />']
106
+ },
107
+ {
108
+ name: 'Select',
109
+ category: 'forms',
110
+ description: 'Styled dropdown select with search, groups, and keyboard navigation',
111
+ props: ['modelValue', 'disabled', 'placeholder'],
112
+ subComponents: ['SelectTrigger', 'SelectValue', 'SelectContent', 'SelectItem', 'SelectGroup', 'SelectLabel', 'SelectSeparator', 'SelectScrollUpButton', 'SelectScrollDownButton'],
113
+ examples: ['<Select v-model="value"><SelectTrigger><SelectValue placeholder="Pick..." /></SelectTrigger><SelectContent><SelectItem value="a">A</SelectItem></SelectContent></Select>']
114
+ },
115
+ {
116
+ name: 'RadioGroup',
117
+ category: 'forms',
118
+ description: 'Group of mutually exclusive radio options',
119
+ props: ['modelValue', 'disabled'],
120
+ subComponents: ['RadioGroupItem'],
121
+ examples: ['<RadioGroup v-model="choice"><RadioGroupItem value="a" /><RadioGroupItem value="b" /></RadioGroup>']
122
+ },
123
+ {
124
+ name: 'Slider',
125
+ category: 'forms',
126
+ description: 'Range slider for selecting numeric values within a min/max range',
127
+ props: ['modelValue', 'min', 'max', 'step', 'disabled'],
128
+ examples: ['<Slider v-model="value" :max="100" :step="1" />']
129
+ },
130
+ {
131
+ name: 'Label',
132
+ category: 'forms',
133
+ description: 'Accessible label element that pairs with form controls',
134
+ props: ['for'],
135
+ examples: ['<Label for="email">Email</Label>']
136
+ },
137
+ {
138
+ name: 'Progress',
139
+ category: 'forms',
140
+ description: 'Determinate or indeterminate progress bar',
141
+ props: ['modelValue', 'max'],
142
+ examples: ['<Progress :model-value="60" />']
143
+ },
144
+ {
145
+ name: 'MultiSelect',
146
+ category: 'forms',
147
+ description: 'Multi-value select with tags and search filtering',
148
+ props: ['modelValue', 'options', 'placeholder'],
149
+ subComponents: ['MultiSelectItem'],
150
+ examples: ['<MultiSelect v-model="selected" :options="items" />']
151
+ },
152
+ {
153
+ name: 'TimePicker',
154
+ category: 'forms',
155
+ description: 'Time input with segmented hours, minutes, and optional seconds',
156
+ props: ['modelValue', 'hourCycle'],
157
+ subComponents: ['TimePickerSegment'],
158
+ examples: ['<TimePicker v-model="time" />']
159
+ },
160
+ {
161
+ name: 'DateRangePicker',
162
+ category: 'forms',
163
+ description: 'Dual-calendar date range selector with presets support',
164
+ props: ['modelValue', 'placeholder'],
165
+ examples: ['<DateRangePicker v-model="range" />']
166
+ },
167
+ {
168
+ name: 'DateTimePicker',
169
+ category: 'forms',
170
+ description: 'Combined date and time picker in a single control',
171
+ props: ['modelValue'],
172
+ examples: ['<DateTimePicker v-model="datetime" />']
173
+ },
174
+ {
175
+ name: 'NativeSelect',
176
+ category: 'forms',
177
+ description: 'Styled wrapper around the native HTML select element for simple use cases',
178
+ props: ['modelValue', 'options', 'placeholder'],
179
+ examples: ['<NativeSelect v-model="val" :options="options" />']
180
+ },
181
+ {
182
+ name: 'InputGroup',
183
+ category: 'forms',
184
+ description: 'Groups an input with leading/trailing addons (icons, text, buttons)',
185
+ props: [],
186
+ subComponents: ['InputGroupAddon', 'InputGroupInput'],
187
+ examples: ['<InputGroup><InputGroupAddon>$</InputGroupAddon><InputGroupInput placeholder="Amount" /></InputGroup>']
188
+ },
189
+ {
190
+ name: 'InputOTP',
191
+ category: 'forms',
192
+ description: 'One-time-password input with individual character slots',
193
+ props: ['modelValue', 'maxLength'],
194
+ subComponents: ['InputOTPGroup', 'InputOTPSlot', 'InputOTPSeparator'],
195
+ examples: ['<InputOTP v-model="code" :max-length="6"><InputOTPGroup><InputOTPSlot :index="0" />...</InputOTPGroup></InputOTP>']
196
+ },
197
+ {
198
+ name: 'Field',
199
+ category: 'forms',
200
+ description: 'Form field wrapper that composes label, description, control, and error message',
201
+ props: ['name'],
202
+ subComponents: ['FieldLabel', 'FieldDescription', 'FieldError'],
203
+ examples: ['<Field><FieldLabel>Email</FieldLabel><Input /><FieldError>Required</FieldError></Field>']
204
+ },
205
+ {
206
+ name: 'ButtonGroup',
207
+ category: 'forms',
208
+ description: 'Visually groups related buttons with shared border radius',
209
+ props: [],
210
+ examples: ['<ButtonGroup><Button>A</Button><Button>B</Button></ButtonGroup>']
211
+ },
212
+ // ─── Data Display ────────────────────────────────────────────
213
+ {
214
+ name: 'Card',
215
+ category: 'data-display',
216
+ description: 'Elevated container for grouping related content with header and footer slots',
217
+ props: ['elevation'],
218
+ subComponents: ['CardHeader', 'CardTitle', 'CardDescription', 'CardContent', 'CardFooter'],
219
+ examples: ['<Card><CardHeader><CardTitle>Title</CardTitle></CardHeader><CardContent>Body</CardContent></Card>']
220
+ },
221
+ {
222
+ name: 'Avatar',
223
+ category: 'data-display',
224
+ description: 'User avatar with image source and text fallback',
225
+ props: [],
226
+ subComponents: ['AvatarImage', 'AvatarFallback'],
227
+ examples: ['<Avatar><AvatarImage src="/photo.jpg" /><AvatarFallback>JD</AvatarFallback></Avatar>']
228
+ },
229
+ {
230
+ name: 'Badge',
231
+ category: 'data-display',
232
+ description: 'Small status label with color variants for tags, counts, and statuses',
233
+ props: ['variant'],
234
+ examples: ['<Badge>New</Badge>', '<Badge variant="destructive">Error</Badge>']
235
+ },
236
+ {
237
+ name: 'Separator',
238
+ category: 'data-display',
239
+ description: 'Visual divider line for separating content sections',
240
+ props: ['orientation', 'decorative'],
241
+ examples: ['<Separator />', '<Separator orientation="vertical" />']
242
+ },
243
+ {
244
+ name: 'Table',
245
+ category: 'data-display',
246
+ description: 'Semantic HTML table with styled header, body, footer, and cell components',
247
+ props: [],
248
+ subComponents: ['TableHeader', 'TableBody', 'TableFooter', 'TableRow', 'TableHead', 'TableCell', 'TableCaption'],
249
+ examples: ['<Table><TableHeader><TableRow><TableHead>Name</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>Alice</TableCell></TableRow></TableBody></Table>']
250
+ },
251
+ {
252
+ name: 'DataTable',
253
+ category: 'data-display',
254
+ description: 'Full-featured data table with sorting, filtering, pagination, and column visibility',
255
+ props: ['columns', 'data', 'pagination', 'sorting'],
256
+ examples: ['<DataTable :columns="columns" :data="rows" />']
257
+ },
258
+ {
259
+ name: 'Accordion',
260
+ category: 'data-display',
261
+ description: 'Vertically stacked sections that expand/collapse to reveal content',
262
+ props: ['type', 'collapsible', 'modelValue'],
263
+ subComponents: ['AccordionItem', 'AccordionTrigger', 'AccordionContent'],
264
+ examples: ['<Accordion type="single" collapsible><AccordionItem value="a"><AccordionTrigger>Section</AccordionTrigger><AccordionContent>Details</AccordionContent></AccordionItem></Accordion>']
265
+ },
266
+ {
267
+ name: 'Collapsible',
268
+ category: 'data-display',
269
+ description: 'Simple expand/collapse container controlled by a trigger',
270
+ props: ['open'],
271
+ subComponents: ['CollapsibleTrigger', 'CollapsibleContent'],
272
+ examples: ['<Collapsible><CollapsibleTrigger>Toggle</CollapsibleTrigger><CollapsibleContent>Hidden content</CollapsibleContent></Collapsible>']
273
+ },
274
+ {
275
+ name: 'Tabs',
276
+ category: 'data-display',
277
+ description: 'Tabbed interface for switching between content panels',
278
+ props: ['modelValue', 'defaultValue'],
279
+ subComponents: ['TabsList', 'TabsTrigger', 'TabsContent'],
280
+ examples: ['<Tabs default-value="tab1"><TabsList><TabsTrigger value="tab1">Tab 1</TabsTrigger></TabsList><TabsContent value="tab1">Content</TabsContent></Tabs>']
281
+ },
282
+ {
283
+ name: 'Calendar',
284
+ category: 'data-display',
285
+ description: 'Date calendar grid with month/year navigation and date selection',
286
+ props: ['modelValue', 'locale'],
287
+ subComponents: ['CalendarCell', 'CalendarCellTrigger', 'CalendarGrid', 'CalendarGridBody', 'CalendarGridHead', 'CalendarGridRow', 'CalendarHeadCell', 'CalendarHeader', 'CalendarHeading', 'CalendarNext', 'CalendarPrev'],
288
+ examples: ['<Calendar v-model="date" />']
289
+ },
290
+ {
291
+ name: 'Carousel',
292
+ category: 'data-display',
293
+ description: 'Horizontal content carousel with previous/next navigation',
294
+ props: ['orientation', 'opts'],
295
+ subComponents: ['CarouselContent', 'CarouselItem', 'CarouselPrevious', 'CarouselNext'],
296
+ examples: ['<Carousel><CarouselContent><CarouselItem>Slide 1</CarouselItem></CarouselContent><CarouselPrevious /><CarouselNext /></Carousel>']
297
+ },
298
+ {
299
+ name: 'Pagination',
300
+ category: 'data-display',
301
+ description: 'Page navigation with first, previous, numbered, next, and last controls',
302
+ props: ['total', 'itemsPerPage', 'page'],
303
+ subComponents: ['PaginationContent', 'PaginationItem', 'PaginationLink', 'PaginationFirst', 'PaginationPrev', 'PaginationNext', 'PaginationLast', 'PaginationEllipsis'],
304
+ examples: ['<Pagination :total="100" :items-per-page="10" :page="1" />']
305
+ },
306
+ {
307
+ name: 'Skeleton',
308
+ category: 'data-display',
309
+ description: 'Placeholder loading animation that mimics content shape',
310
+ props: ['class'],
311
+ examples: ['<Skeleton class="h-4 w-[250px]" />']
312
+ },
313
+ {
314
+ name: 'Empty',
315
+ category: 'data-display',
316
+ description: 'Empty state placeholder with icon, title, and description slots',
317
+ props: [],
318
+ subComponents: ['EmptyIcon', 'EmptyTitle', 'EmptyDescription'],
319
+ examples: ['<Empty><EmptyIcon /><EmptyTitle>No results</EmptyTitle><EmptyDescription>Try a different search.</EmptyDescription></Empty>']
320
+ },
321
+ {
322
+ name: 'Kbd',
323
+ category: 'data-display',
324
+ description: 'Keyboard shortcut indicator styled as a key cap',
325
+ props: [],
326
+ examples: ['<Kbd>Ctrl+K</Kbd>']
327
+ },
328
+ {
329
+ name: 'Typography',
330
+ category: 'data-display',
331
+ description: 'Semantic typography primitives for headings, paragraphs, and text styles',
332
+ props: [],
333
+ subComponents: ['TypographyH1', 'TypographyH2', 'TypographyH3', 'TypographyH4', 'TypographyP', 'TypographyBlockquote', 'TypographyInlineCode', 'TypographyLead', 'TypographyLarge', 'TypographySmall', 'TypographyMuted'],
334
+ examples: ['<TypographyH1>Heading</TypographyH1>', '<TypographyP>Paragraph text</TypographyP>']
335
+ },
336
+ {
337
+ name: 'Alert',
338
+ category: 'data-display',
339
+ description: 'Inline alert banner with variant-based styling for info, success, warning, and error',
340
+ props: ['variant'],
341
+ subComponents: ['AlertTitle', 'AlertDescription'],
342
+ examples: ['<Alert variant="destructive"><AlertTitle>Error</AlertTitle><AlertDescription>Something went wrong.</AlertDescription></Alert>']
343
+ },
344
+ {
345
+ name: 'FeatureCard',
346
+ category: 'data-display',
347
+ description: 'Pre-composed card for showcasing a feature with icon, title, and description',
348
+ props: ['icon', 'title', 'description'],
349
+ examples: ['<FeatureCard icon="Zap" title="Fast" description="Blazing performance" />']
350
+ },
351
+ {
352
+ name: 'StatsCard',
353
+ category: 'data-display',
354
+ description: 'Pre-composed card for displaying a metric with label, value, and trend indicator',
355
+ props: ['label', 'value', 'trend'],
356
+ examples: ['<StatsCard label="Revenue" value="$12,345" trend="+12%" />']
357
+ },
358
+ // ─── Overlays ────────────────────────────────────────────────
359
+ {
360
+ name: 'Dialog',
361
+ category: 'overlays',
362
+ description: 'Modal dialog with overlay, triggered by a child element',
363
+ props: ['open'],
364
+ subComponents: ['DialogTrigger', 'DialogPortal', 'DialogOverlay', 'DialogContent', 'DialogHeader', 'DialogFooter', 'DialogTitle', 'DialogDescription', 'DialogClose'],
365
+ examples: ['<Dialog><DialogTrigger><Button>Open</Button></DialogTrigger><DialogContent><DialogHeader><DialogTitle>Title</DialogTitle></DialogHeader></DialogContent></Dialog>']
366
+ },
367
+ {
368
+ name: 'Modal',
369
+ category: 'overlays',
370
+ description: 'Convenience wrapper around Dialog with simplified API and built-in close button',
371
+ props: ['open'],
372
+ subComponents: ['ModalTrigger', 'ModalContent', 'ModalClose', 'ModalHeader', 'ModalFooter', 'ModalTitle', 'ModalDescription'],
373
+ examples: ['<Modal><ModalTrigger><Button>Open</Button></ModalTrigger><ModalContent><ModalHeader><ModalTitle>Title</ModalTitle></ModalHeader></ModalContent></Modal>']
374
+ },
375
+ {
376
+ name: 'Sheet',
377
+ category: 'overlays',
378
+ description: 'Slide-out panel from any edge of the screen',
379
+ props: ['open', 'side'],
380
+ subComponents: ['SheetTrigger', 'SheetClose', 'SheetContent', 'SheetHeader', 'SheetFooter', 'SheetTitle', 'SheetDescription'],
381
+ examples: ['<Sheet><SheetTrigger><Button>Open</Button></SheetTrigger><SheetContent><SheetHeader><SheetTitle>Panel</SheetTitle></SheetHeader></SheetContent></Sheet>']
382
+ },
383
+ {
384
+ name: 'Drawer',
385
+ category: 'overlays',
386
+ description: 'Bottom sheet drawer with drag-to-dismiss interaction',
387
+ props: ['open'],
388
+ subComponents: ['DrawerTrigger', 'DrawerClose', 'DrawerContent', 'DrawerHeader', 'DrawerFooter', 'DrawerTitle', 'DrawerDescription'],
389
+ examples: ['<Drawer><DrawerTrigger><Button>Open</Button></DrawerTrigger><DrawerContent><DrawerHeader><DrawerTitle>Drawer</DrawerTitle></DrawerHeader></DrawerContent></Drawer>']
390
+ },
391
+ {
392
+ name: 'AlertDialog',
393
+ category: 'overlays',
394
+ description: 'Confirmation dialog that requires explicit user action before proceeding',
395
+ props: ['open'],
396
+ subComponents: ['AlertDialogTrigger', 'AlertDialogPortal', 'AlertDialogOverlay', 'AlertDialogContent', 'AlertDialogHeader', 'AlertDialogFooter', 'AlertDialogTitle', 'AlertDialogDescription', 'AlertDialogAction', 'AlertDialogCancel'],
397
+ examples: ['<AlertDialog><AlertDialogTrigger><Button>Delete</Button></AlertDialogTrigger><AlertDialogContent><AlertDialogHeader><AlertDialogTitle>Confirm</AlertDialogTitle></AlertDialogHeader><AlertDialogFooter><AlertDialogCancel>Cancel</AlertDialogCancel><AlertDialogAction>Continue</AlertDialogAction></AlertDialogFooter></AlertDialogContent></AlertDialog>']
398
+ },
399
+ {
400
+ name: 'Tooltip',
401
+ category: 'overlays',
402
+ description: 'Popup hint shown on hover or focus to describe an element',
403
+ props: ['delayDuration'],
404
+ subComponents: ['TooltipProvider', 'TooltipTrigger', 'TooltipContent'],
405
+ examples: ['<TooltipProvider><Tooltip><TooltipTrigger>Hover me</TooltipTrigger><TooltipContent>Hint</TooltipContent></Tooltip></TooltipProvider>']
406
+ },
407
+ {
408
+ name: 'Popover',
409
+ category: 'overlays',
410
+ description: 'Floating content panel anchored to a trigger element',
411
+ props: ['open'],
412
+ subComponents: ['PopoverTrigger', 'PopoverContent'],
413
+ examples: ['<Popover><PopoverTrigger><Button>Open</Button></PopoverTrigger><PopoverContent>Popover body</PopoverContent></Popover>']
414
+ },
415
+ {
416
+ name: 'DropdownMenu',
417
+ category: 'overlays',
418
+ description: 'Context-aware dropdown menu with items, checkboxes, radio groups, and sub-menus',
419
+ props: [],
420
+ subComponents: ['DropdownMenuTrigger', 'DropdownMenuContent', 'DropdownMenuItem', 'DropdownMenuCheckboxItem', 'DropdownMenuRadioItem', 'DropdownMenuRadioGroup', 'DropdownMenuSub', 'DropdownMenuSubTrigger', 'DropdownMenuSubContent', 'DropdownMenuSeparator', 'DropdownMenuLabel', 'DropdownMenuGroup', 'DropdownMenuShortcut'],
421
+ examples: ['<DropdownMenu><DropdownMenuTrigger><Button>Menu</Button></DropdownMenuTrigger><DropdownMenuContent><DropdownMenuItem>Action</DropdownMenuItem></DropdownMenuContent></DropdownMenu>']
422
+ },
423
+ {
424
+ name: 'ContextMenu',
425
+ category: 'overlays',
426
+ description: 'Right-click context menu with items, checkboxes, and sub-menus',
427
+ props: [],
428
+ subComponents: ['ContextMenuTrigger', 'ContextMenuContent', 'ContextMenuItem', 'ContextMenuCheckboxItem', 'ContextMenuRadioItem', 'ContextMenuRadioGroup', 'ContextMenuSub', 'ContextMenuSubTrigger', 'ContextMenuSubContent', 'ContextMenuSeparator', 'ContextMenuLabel', 'ContextMenuGroup', 'ContextMenuShortcut'],
429
+ examples: ['<ContextMenu><ContextMenuTrigger>Right-click here</ContextMenuTrigger><ContextMenuContent><ContextMenuItem>Action</ContextMenuItem></ContextMenuContent></ContextMenu>']
430
+ },
431
+ {
432
+ name: 'HoverCard',
433
+ category: 'overlays',
434
+ description: 'Rich content card that appears when hovering over a trigger',
435
+ props: [],
436
+ subComponents: ['HoverCardTrigger', 'HoverCardContent'],
437
+ examples: ['<HoverCard><HoverCardTrigger>@user</HoverCardTrigger><HoverCardContent>User info</HoverCardContent></HoverCard>']
438
+ },
439
+ {
440
+ name: 'Command',
441
+ category: 'overlays',
442
+ description: 'Command palette / combobox for searching and selecting actions',
443
+ props: [],
444
+ subComponents: ['CommandInput', 'CommandList', 'CommandEmpty', 'CommandGroup', 'CommandItem', 'CommandSeparator', 'CommandLabel', 'CommandDialog'],
445
+ examples: ['<Command><CommandInput placeholder="Search..." /><CommandList><CommandGroup><CommandItem>Action</CommandItem></CommandGroup></CommandList></Command>']
446
+ },
447
+ {
448
+ name: 'Toast',
449
+ category: 'overlays',
450
+ description: 'Temporary notification message with title, description, and action slots',
451
+ props: ['variant'],
452
+ subComponents: ['ToastViewport', 'ToastTitle', 'ToastDescription', 'ToastAction', 'ToastClose', 'ToastToaster'],
453
+ examples: ['useToast().toast({ title: "Saved", description: "Your changes have been saved." })']
454
+ },
455
+ // ─── Navigation ──────────────────────────────────────────────
456
+ {
457
+ name: 'Breadcrumb',
458
+ category: 'navigation',
459
+ description: 'Hierarchical breadcrumb trail showing the current page path',
460
+ props: [],
461
+ subComponents: ['BreadcrumbList', 'BreadcrumbItem', 'BreadcrumbLink', 'BreadcrumbPage', 'BreadcrumbSeparator', 'BreadcrumbEllipsis'],
462
+ examples: ['<Breadcrumb><BreadcrumbList><BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem><BreadcrumbSeparator /><BreadcrumbItem><BreadcrumbPage>Current</BreadcrumbPage></BreadcrumbItem></BreadcrumbList></Breadcrumb>']
463
+ },
464
+ {
465
+ name: 'NavigationMenu',
466
+ category: 'navigation',
467
+ description: 'Horizontal navigation bar with dropdown content panels and active indicators',
468
+ props: [],
469
+ subComponents: ['NavigationMenuList', 'NavigationMenuItem', 'NavigationMenuTrigger', 'NavigationMenuContent', 'NavigationMenuLink', 'NavigationMenuViewport', 'NavigationMenuIndicator'],
470
+ examples: ['<NavigationMenu><NavigationMenuList><NavigationMenuItem><NavigationMenuTrigger>Features</NavigationMenuTrigger><NavigationMenuContent>Links...</NavigationMenuContent></NavigationMenuItem></NavigationMenuList></NavigationMenu>']
471
+ },
472
+ {
473
+ name: 'Menubar',
474
+ category: 'navigation',
475
+ description: 'Desktop-style horizontal menu bar with dropdown sub-menus',
476
+ props: [],
477
+ subComponents: ['MenubarMenu', 'MenubarTrigger', 'MenubarContent', 'MenubarItem', 'MenubarCheckboxItem', 'MenubarRadioItem', 'MenubarRadioGroup', 'MenubarSub', 'MenubarSubTrigger', 'MenubarSubContent', 'MenubarSeparator', 'MenubarLabel', 'MenubarGroup', 'MenubarShortcut'],
478
+ examples: ['<Menubar><MenubarMenu><MenubarTrigger>File</MenubarTrigger><MenubarContent><MenubarItem>New</MenubarItem></MenubarContent></MenubarMenu></Menubar>']
479
+ },
480
+ // ─── Utility ─────────────────────────────────────────────────
481
+ {
482
+ name: 'Toggle',
483
+ category: 'utility',
484
+ description: 'Two-state toggle button (pressed/not pressed) with variant styling',
485
+ props: ['pressed', 'variant', 'size', 'disabled'],
486
+ examples: ['<Toggle v-model:pressed="bold"><Bold /></Toggle>']
487
+ },
488
+ {
489
+ name: 'ToggleGroup',
490
+ category: 'utility',
491
+ description: 'Groups multiple Toggle buttons with single or multiple selection',
492
+ props: ['type', 'modelValue'],
493
+ subComponents: ['ToggleGroupItem'],
494
+ examples: ['<ToggleGroup type="single" v-model="alignment"><ToggleGroupItem value="left">Left</ToggleGroupItem></ToggleGroup>']
495
+ },
496
+ {
497
+ name: 'ScrollArea',
498
+ category: 'utility',
499
+ description: 'Custom scrollable container with styled scrollbars',
500
+ props: ['class'],
501
+ subComponents: ['ScrollBar'],
502
+ examples: ['<ScrollArea class="h-[200px]">Long content...</ScrollArea>']
503
+ },
504
+ {
505
+ name: 'Resizable',
506
+ category: 'utility',
507
+ description: 'Resizable panel layout with draggable dividers',
508
+ props: ['direction'],
509
+ subComponents: ['ResizablePanelGroup', 'ResizablePanel', 'ResizableHandle'],
510
+ examples: ['<ResizablePanelGroup direction="horizontal"><ResizablePanel>Left</ResizablePanel><ResizableHandle /><ResizablePanel>Right</ResizablePanel></ResizablePanelGroup>']
511
+ },
512
+ {
513
+ name: 'AspectRatio',
514
+ category: 'utility',
515
+ description: 'Container that maintains a fixed aspect ratio for its content',
516
+ props: ['ratio'],
517
+ examples: ['<AspectRatio :ratio="16/9"><img src="/photo.jpg" /></AspectRatio>']
518
+ },
519
+ {
520
+ name: 'Spinner',
521
+ category: 'utility',
522
+ description: 'Animated loading spinner indicator',
523
+ props: ['size'],
524
+ examples: ['<Spinner />', '<Spinner size="lg" />']
525
+ },
526
+ {
527
+ name: 'Icon',
528
+ category: 'utility',
529
+ description: 'Dynamic Lucide icon renderer that accepts an icon name string',
530
+ props: ['name', 'size', 'strokeWidth'],
531
+ examples: ['<Icon name="Home" />', '<Icon name="Settings" :size="24" />']
532
+ },
533
+ {
534
+ name: 'CodePreview',
535
+ category: 'utility',
536
+ description: 'Code block with syntax highlighting and copy-to-clipboard button',
537
+ props: ['code', 'language'],
538
+ examples: ['<CodePreview code="const x = 1" language="typescript" />']
539
+ },
540
+ {
541
+ name: 'ThemeSelector',
542
+ category: 'utility',
543
+ description: 'Dropdown selector for switching between predefined color themes',
544
+ props: ['themes', 'modelValue'],
545
+ examples: ['<ThemeSelector v-model="theme" :themes="themes" />']
546
+ },
547
+ {
548
+ name: 'Direction',
549
+ category: 'utility',
550
+ description: 'Provider that sets text direction (LTR/RTL) for child components',
551
+ props: ['dir'],
552
+ examples: ['<Direction dir="rtl">RTL content</Direction>']
553
+ },
554
+ {
555
+ name: 'Toaster',
556
+ category: 'utility',
557
+ description: 'Sonner-based toast notification container; place once at the app root',
558
+ props: ['position', 'richColors'],
559
+ examples: ['<Toaster />']
560
+ }
561
+ ];
562
+ // Theme tokens
563
+ const themeTokens = {
564
+ light: {
565
+ colors: {
566
+ background: { value: 'hsl(0 0% 96%)', hex: '#F5F5F5', description: 'Page background' },
567
+ foreground: { value: 'hsl(0 0% 9%)', hex: '#171717', description: 'Text color' },
568
+ card: { value: 'hsl(0 0% 100%)', hex: '#FFFFFF', description: 'Card background' },
569
+ primary: { value: 'hsl(0 0% 9%)', hex: '#171717', description: 'Primary buttons' },
570
+ secondary: { value: 'hsl(0 0% 93%)', hex: '#EEEEEE', description: 'Secondary buttons' },
571
+ muted: { value: 'hsl(0 0% 93%)', hex: '#EEEEEE', description: 'Muted backgrounds' },
572
+ border: { value: 'hsl(0 0% 90%)', hex: '#E5E5E5', description: 'Borders' },
573
+ destructive: { value: 'hsl(0 84% 60%)', hex: '#EF4444', description: 'Error states' },
574
+ success: { value: 'hsl(142 76% 36%)', hex: '#16A34A', description: 'Success states' }
575
+ },
576
+ radius: { default: '1rem', sm: '0.75rem', lg: '1.25rem' },
577
+ spacing: {
578
+ '0': '0px', '1': '4px', '2': '8px', '3': '12px', '4': '16px',
579
+ '5': '20px', '6': '24px', '8': '32px', '10': '40px', '12': '48px'
580
+ }
581
+ },
582
+ dark: {
583
+ colors: {
584
+ background: { value: 'hsl(0 0% 7%)', hex: '#121212', description: 'Page background' },
585
+ foreground: { value: 'hsl(0 0% 98%)', hex: '#FAFAFA', description: 'Text color' },
586
+ card: { value: 'hsl(0 0% 10%)', hex: '#1A1A1A', description: 'Card background' },
587
+ primary: { value: 'hsl(0 0% 98%)', hex: '#FAFAFA', description: 'Primary buttons' },
588
+ secondary: { value: 'hsl(0 0% 15%)', hex: '#262626', description: 'Secondary buttons' },
589
+ muted: { value: 'hsl(0 0% 15%)', hex: '#262626', description: 'Muted backgrounds' },
590
+ border: { value: 'hsl(0 0% 20%)', hex: '#333333', description: 'Borders' },
591
+ destructive: { value: 'hsl(0 72% 51%)', hex: '#EF4444', description: 'Error states' },
592
+ success: { value: 'hsl(142 71% 45%)', hex: '#22C55E', description: 'Success states' }
593
+ }
594
+ }
595
+ };
596
+ // ─── Known component names (flat set for validation) ───────────
597
+ const allComponentNames = new Set();
598
+ for (const comp of components) {
599
+ allComponentNames.add(comp.name);
600
+ if (comp.subComponents) {
601
+ for (const sub of comp.subComponents) {
602
+ allComponentNames.add(sub);
603
+ }
604
+ }
605
+ }
606
+ // Known variant values per component for validation
607
+ const knownVariants = {
608
+ Button: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],
609
+ Badge: ['default', 'secondary', 'destructive', 'outline'],
610
+ Alert: ['default', 'destructive'],
611
+ Toggle: ['default', 'outline'],
612
+ };
613
+ const knownSizes = {
614
+ Button: ['default', 'sm', 'lg', 'icon'],
615
+ Spinner: ['default', 'sm', 'lg'],
616
+ };
617
+ // ─── Tool definitions ──────────────────────────────────────────
618
+ const tools = [
619
+ {
620
+ name: 'search_components',
621
+ description: 'Search for Spavn UI components by name or category',
622
+ inputSchema: {
623
+ type: 'object',
624
+ properties: {
625
+ query: {
626
+ type: 'string',
627
+ description: 'Search term (e.g., "button", "form", "input", "overlay")'
628
+ },
629
+ category: {
630
+ type: 'string',
631
+ description: 'Filter by category',
632
+ enum: ['layout', 'forms', 'data-display', 'overlays', 'navigation', 'utility']
633
+ }
634
+ },
635
+ required: ['query']
636
+ }
637
+ },
638
+ {
639
+ name: 'get_component_api',
640
+ description: 'Get detailed API documentation for a specific component',
641
+ inputSchema: {
642
+ type: 'object',
643
+ properties: {
644
+ component: {
645
+ type: 'string',
646
+ description: 'Component name (e.g., "Button", "Dialog")'
647
+ }
648
+ },
649
+ required: ['component']
650
+ }
651
+ },
652
+ {
653
+ name: 'generate_component_code',
654
+ description: 'Generate Vue code for using a component',
655
+ inputSchema: {
656
+ type: 'object',
657
+ properties: {
658
+ component: {
659
+ type: 'string',
660
+ description: 'Component name'
661
+ },
662
+ props: {
663
+ type: 'object',
664
+ description: 'Props to set'
665
+ },
666
+ includeSlots: {
667
+ type: 'boolean',
668
+ description: 'Include slot examples'
669
+ }
670
+ },
671
+ required: ['component']
672
+ }
673
+ },
674
+ {
675
+ name: 'get_theme_tokens',
676
+ description: 'Get design tokens (colors, spacing, etc.)',
677
+ inputSchema: {
678
+ type: 'object',
679
+ properties: {
680
+ mode: {
681
+ type: 'string',
682
+ description: 'Theme mode',
683
+ enum: ['light', 'dark', 'all']
684
+ },
685
+ category: {
686
+ type: 'string',
687
+ description: 'Token category',
688
+ enum: ['colors', 'spacing', 'radius', 'all']
689
+ }
690
+ }
691
+ }
692
+ },
693
+ {
694
+ name: 'generate_theme',
695
+ description: 'Generate a custom color theme with CSS variables for light and dark modes based on a primary color',
696
+ inputSchema: {
697
+ type: 'object',
698
+ properties: {
699
+ name: {
700
+ type: 'string',
701
+ description: 'Theme name (e.g., "ocean", "forest")'
702
+ },
703
+ primary: {
704
+ type: 'string',
705
+ description: 'Primary color in hex format (e.g., "#3B82F6")'
706
+ },
707
+ background: {
708
+ type: 'string',
709
+ description: 'Optional background color in hex format'
710
+ },
711
+ mode: {
712
+ type: 'string',
713
+ description: 'Generate for light, dark, or both',
714
+ enum: ['light', 'dark', 'both']
715
+ }
716
+ },
717
+ required: ['primary']
718
+ }
719
+ },
720
+ {
721
+ name: 'validate_component_usage',
722
+ description: 'Validate a Vue template snippet for correct Spavn UI component imports and prop usage',
723
+ inputSchema: {
724
+ type: 'object',
725
+ properties: {
726
+ code: {
727
+ type: 'string',
728
+ description: 'Vue template or SFC code to validate'
729
+ }
730
+ },
731
+ required: ['code']
732
+ }
733
+ },
734
+ {
735
+ name: 'get_installation_guide',
736
+ description: 'Get installation instructions',
737
+ inputSchema: {
738
+ type: 'object',
739
+ properties: {
740
+ packageManager: {
741
+ type: 'string',
742
+ enum: ['npm', 'yarn', 'pnpm', 'bun']
743
+ },
744
+ framework: {
745
+ type: 'string',
746
+ enum: ['vite', 'nuxt', 'astro', 'vue']
747
+ }
748
+ }
749
+ }
750
+ }
751
+ ];
752
+ // ─── Tool handlers ─────────────────────────────────────────────
753
+ async function handleSearchComponents(args) {
754
+ const { query, category } = args;
755
+ let results = components.filter(c => c.name.toLowerCase().includes(query.toLowerCase()) ||
756
+ c.description.toLowerCase().includes(query.toLowerCase()) ||
757
+ c.category.toLowerCase().includes(query.toLowerCase()) ||
758
+ (c.subComponents && c.subComponents.some(s => s.toLowerCase().includes(query.toLowerCase()))));
759
+ if (category) {
760
+ results = results.filter(c => c.category === category);
761
+ }
762
+ return {
763
+ content: [
764
+ {
765
+ type: 'text',
766
+ text: JSON.stringify({
767
+ count: results.length,
768
+ components: results.map(c => ({
769
+ name: c.name,
770
+ category: c.category,
771
+ description: c.description,
772
+ subComponents: c.subComponents || [],
773
+ props: c.props
774
+ }))
775
+ }, null, 2)
776
+ }
777
+ ]
778
+ };
779
+ }
780
+ async function handleGetComponentApi(args) {
781
+ const { component } = args;
782
+ const comp = components.find(c => c.name.toLowerCase() === component.toLowerCase());
783
+ if (!comp) {
784
+ // Try to find as a sub-component
785
+ const parent = components.find(c => c.subComponents && c.subComponents.some(s => s.toLowerCase() === component.toLowerCase()));
786
+ if (parent) {
787
+ return {
788
+ content: [
789
+ {
790
+ type: 'text',
791
+ text: JSON.stringify({
792
+ name: component,
793
+ isSubComponentOf: parent.name,
794
+ category: parent.category,
795
+ description: `Sub-component of ${parent.name}. Import alongside the parent component.`,
796
+ import: `import { ${parent.name}, ${component} } from '@spavn/ui'`,
797
+ parentComponent: {
798
+ name: parent.name,
799
+ description: parent.description,
800
+ allSubComponents: parent.subComponents
801
+ }
802
+ }, null, 2)
803
+ }
804
+ ]
805
+ };
806
+ }
807
+ return {
808
+ content: [
809
+ {
810
+ type: 'text',
811
+ text: `Component "${component}" not found. Use search_components to find available components.`
812
+ }
813
+ ],
814
+ isError: true
815
+ };
816
+ }
817
+ return {
818
+ content: [
819
+ {
820
+ type: 'text',
821
+ text: JSON.stringify({
822
+ name: comp.name,
823
+ category: comp.category,
824
+ description: comp.description,
825
+ subComponents: comp.subComponents || [],
826
+ props: comp.props,
827
+ examples: comp.examples,
828
+ import: `import { ${comp.name}${comp.subComponents ? ', ' + comp.subComponents.join(', ') : ''} } from '@spavn/ui'`
829
+ }, null, 2)
830
+ }
831
+ ]
832
+ };
833
+ }
834
+ async function handleGenerateComponentCode(args) {
835
+ const { component, props = {}, includeSlots = false } = args;
836
+ const comp = components.find(c => c.name.toLowerCase() === component.toLowerCase());
837
+ if (!comp) {
838
+ return {
839
+ content: [
840
+ {
841
+ type: 'text',
842
+ text: `Component "${component}" not found.`
843
+ }
844
+ ],
845
+ isError: true
846
+ };
847
+ }
848
+ const propString = Object.entries(props)
849
+ .map(([key, value]) => {
850
+ if (typeof value === 'boolean')
851
+ return value ? key : '';
852
+ if (typeof value === 'number')
853
+ return `:${key}="${value}"`;
854
+ return `${key}="${value}"`;
855
+ })
856
+ .filter(Boolean)
857
+ .join(' ');
858
+ let code = '';
859
+ if (comp.subComponents && comp.subComponents.length > 0) {
860
+ code = `<${comp.name}>
861
+ ${comp.subComponents.includes(comp.name + 'Trigger') ? `<${comp.name}Trigger>
862
+ <Button>Open</Button>
863
+ </${comp.name}Trigger>` : ''}
864
+ ${comp.subComponents.includes(comp.name + 'Content') ? `<${comp.name}Content${propString ? ' ' + propString : ''}>
865
+ Content here
866
+ </${comp.name}Content>` : `<${comp.name}${propString ? ' ' + propString : ''} />`}
867
+ </${comp.name}>`;
868
+ }
869
+ else {
870
+ code = `<${comp.name}${propString ? ' ' + propString : ''} />`;
871
+ }
872
+ if (includeSlots) {
873
+ code = code.replace('/>', '>\n Slot content\n</' + comp.name + '>');
874
+ }
875
+ // Build import statement
876
+ const imports = [comp.name, ...(comp.subComponents || [])];
877
+ const importLine = `import { ${imports.join(', ')} } from '@spavn/ui'`;
878
+ return {
879
+ content: [
880
+ {
881
+ type: 'text',
882
+ text: `\`\`\`vue\n<script setup>\n${importLine}\n</script>\n\n<template>\n${code.trim()}\n</template>\n\`\`\``
883
+ }
884
+ ]
885
+ };
886
+ }
887
+ async function handleGetThemeTokens(args) {
888
+ const { mode = 'all', category = 'all' } = args;
889
+ let tokens = {};
890
+ if (mode === 'all' || mode === 'light') {
891
+ tokens.light = {};
892
+ if (category === 'all' || category === 'colors') {
893
+ tokens.light.colors = themeTokens.light.colors;
894
+ }
895
+ if (category === 'all' || category === 'spacing') {
896
+ tokens.light.spacing = themeTokens.light.spacing;
897
+ }
898
+ if (category === 'all' || category === 'radius') {
899
+ tokens.light.radius = themeTokens.light.radius;
900
+ }
901
+ }
902
+ if (mode === 'all' || mode === 'dark') {
903
+ tokens.dark = {};
904
+ if (category === 'all' || category === 'colors') {
905
+ tokens.dark.colors = themeTokens.dark.colors;
906
+ }
907
+ }
908
+ return {
909
+ content: [
910
+ {
911
+ type: 'text',
912
+ text: JSON.stringify(tokens, null, 2)
913
+ }
914
+ ]
915
+ };
916
+ }
917
+ // ─── Helper: hex to HSL ────────────────────────────────────────
918
+ function hexToHsl(hex) {
919
+ hex = hex.replace('#', '');
920
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
921
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
922
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
923
+ const max = Math.max(r, g, b);
924
+ const min = Math.min(r, g, b);
925
+ let h = 0;
926
+ let s = 0;
927
+ const l = (max + min) / 2;
928
+ if (max !== min) {
929
+ const d = max - min;
930
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
931
+ switch (max) {
932
+ case r:
933
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
934
+ break;
935
+ case g:
936
+ h = ((b - r) / d + 2) / 6;
937
+ break;
938
+ case b:
939
+ h = ((r - g) / d + 4) / 6;
940
+ break;
941
+ }
942
+ }
943
+ return {
944
+ h: Math.round(h * 360),
945
+ s: Math.round(s * 100),
946
+ l: Math.round(l * 100)
947
+ };
948
+ }
949
+ function hslString(h, s, l) {
950
+ return `hsl(${h} ${s}% ${l}%)`;
951
+ }
952
+ async function handleGenerateTheme(args) {
953
+ const { name = 'custom', primary, background, mode = 'both' } = args;
954
+ const pHsl = hexToHsl(primary);
955
+ // Derive a full palette from the primary color
956
+ const generateMode = (isDark) => {
957
+ const bg = background
958
+ ? hexToHsl(background)
959
+ : isDark
960
+ ? { h: pHsl.h, s: Math.round(pHsl.s * 0.15), l: 7 }
961
+ : { h: pHsl.h, s: Math.round(pHsl.s * 0.25), l: 96 };
962
+ const fg = isDark
963
+ ? { h: pHsl.h, s: 5, l: 98 }
964
+ : { h: pHsl.h, s: 5, l: 9 };
965
+ const card = isDark
966
+ ? { h: bg.h, s: bg.s, l: bg.l + 3 }
967
+ : { h: bg.h, s: bg.s, l: 100 };
968
+ const muted = isDark
969
+ ? { h: pHsl.h, s: Math.round(pHsl.s * 0.1), l: 15 }
970
+ : { h: pHsl.h, s: Math.round(pHsl.s * 0.2), l: 93 };
971
+ const border = isDark
972
+ ? { h: pHsl.h, s: Math.round(pHsl.s * 0.1), l: 20 }
973
+ : { h: pHsl.h, s: Math.round(pHsl.s * 0.15), l: 90 };
974
+ const primaryFg = isDark
975
+ ? { h: pHsl.h, s: pHsl.s, l: Math.min(pHsl.l + 40, 98) }
976
+ : { h: pHsl.h, s: pHsl.s, l: pHsl.l };
977
+ const ring = { h: pHsl.h, s: pHsl.s, l: isDark ? pHsl.l + 10 : pHsl.l };
978
+ return {
979
+ '--background': hslString(bg.h, bg.s, bg.l),
980
+ '--foreground': hslString(fg.h, fg.s, fg.l),
981
+ '--card': hslString(card.h, card.s, card.l),
982
+ '--card-foreground': hslString(fg.h, fg.s, fg.l),
983
+ '--primary': hslString(primaryFg.h, primaryFg.s, primaryFg.l),
984
+ '--primary-foreground': isDark ? hslString(pHsl.h, pHsl.s, 10) : hslString(0, 0, 100),
985
+ '--secondary': hslString(muted.h, muted.s, muted.l),
986
+ '--secondary-foreground': hslString(fg.h, fg.s, fg.l),
987
+ '--muted': hslString(muted.h, muted.s, muted.l),
988
+ '--muted-foreground': hslString(fg.h, fg.s, isDark ? 65 : 45),
989
+ '--border': hslString(border.h, border.s, border.l),
990
+ '--ring': hslString(ring.h, ring.s, ring.l),
991
+ '--destructive': isDark ? 'hsl(0 72% 51%)' : 'hsl(0 84% 60%)',
992
+ '--destructive-foreground': 'hsl(0 0% 100%)',
993
+ '--radius': '1rem',
994
+ };
995
+ };
996
+ const result = { name };
997
+ if (mode === 'both' || mode === 'light') {
998
+ result.light = generateMode(false);
999
+ }
1000
+ if (mode === 'both' || mode === 'dark') {
1001
+ result.dark = generateMode(true);
1002
+ }
1003
+ // Generate ready-to-paste CSS
1004
+ let css = `/* Theme: ${name} — generated from primary ${primary} */\n`;
1005
+ if (result.light) {
1006
+ css += ':root {\n';
1007
+ for (const [key, value] of Object.entries(result.light)) {
1008
+ css += ` ${key}: ${value};\n`;
1009
+ }
1010
+ css += '}\n\n';
1011
+ }
1012
+ if (result.dark) {
1013
+ css += '.dark {\n';
1014
+ for (const [key, value] of Object.entries(result.dark)) {
1015
+ css += ` ${key}: ${value};\n`;
1016
+ }
1017
+ css += '}\n';
1018
+ }
1019
+ return {
1020
+ content: [
1021
+ {
1022
+ type: 'text',
1023
+ text: JSON.stringify({
1024
+ ...result,
1025
+ css,
1026
+ usage: 'Add the CSS to your global stylesheet or Tailwind CSS layer. Apply .dark class on <html> for dark mode.'
1027
+ }, null, 2)
1028
+ }
1029
+ ]
1030
+ };
1031
+ }
1032
+ async function handleValidateComponentUsage(args) {
1033
+ const { code } = args;
1034
+ const issues = [];
1035
+ const suggestions = [];
1036
+ // Extract component tags used in the template
1037
+ const tagPattern = /<\/?([A-Z][A-Za-z0-9]*)/g;
1038
+ const usedTags = new Set();
1039
+ let match;
1040
+ while ((match = tagPattern.exec(code)) !== null) {
1041
+ usedTags.add(match[1]);
1042
+ }
1043
+ // Check each used tag against the known component database
1044
+ const unknownTags = [];
1045
+ const validTags = [];
1046
+ for (const tag of usedTags) {
1047
+ if (allComponentNames.has(tag)) {
1048
+ validTags.push(tag);
1049
+ }
1050
+ else {
1051
+ // Skip common HTML-like Vue components
1052
+ if (!['Button'].includes(tag) && !tag.startsWith('Router') && !tag.startsWith('Transition')) {
1053
+ unknownTags.push(tag);
1054
+ }
1055
+ }
1056
+ }
1057
+ if (unknownTags.length > 0) {
1058
+ issues.push({
1059
+ severity: 'warning',
1060
+ message: `Unknown component(s): ${unknownTags.join(', ')}. These are not exported from @spavn/ui. Check spelling or verify they are custom components.`
1061
+ });
1062
+ }
1063
+ // Check for sub-components used without their parent
1064
+ for (const tag of validTags) {
1065
+ const parent = components.find(c => c.subComponents && c.subComponents.includes(tag));
1066
+ if (parent && !usedTags.has(parent.name)) {
1067
+ issues.push({
1068
+ severity: 'error',
1069
+ message: `"${tag}" is a sub-component of "${parent.name}" but "${parent.name}" is not used in the template. Wrap it inside <${parent.name}>.`
1070
+ });
1071
+ }
1072
+ }
1073
+ // Check for incorrect variant prop values
1074
+ const variantPattern = /variant="([^"]+)"/g;
1075
+ while ((match = variantPattern.exec(code)) !== null) {
1076
+ const value = match[1];
1077
+ // Find which component this variant belongs to by looking backwards for a tag
1078
+ const beforeMatch = code.substring(0, match.index);
1079
+ const lastTag = beforeMatch.match(/<([A-Z][A-Za-z0-9]*)[^>]*$/);
1080
+ if (lastTag) {
1081
+ const compName = lastTag[1];
1082
+ const allowed = knownVariants[compName];
1083
+ if (allowed && !allowed.includes(value)) {
1084
+ issues.push({
1085
+ severity: 'error',
1086
+ message: `Invalid variant="${value}" on <${compName}>. Allowed values: ${allowed.join(', ')}.`
1087
+ });
1088
+ }
1089
+ }
1090
+ }
1091
+ // Check for incorrect size prop values
1092
+ const sizePattern = /size="([^"]+)"/g;
1093
+ while ((match = sizePattern.exec(code)) !== null) {
1094
+ const value = match[1];
1095
+ const beforeMatch = code.substring(0, match.index);
1096
+ const lastTag = beforeMatch.match(/<([A-Z][A-Za-z0-9]*)[^>]*$/);
1097
+ if (lastTag) {
1098
+ const compName = lastTag[1];
1099
+ const allowed = knownSizes[compName];
1100
+ if (allowed && !allowed.includes(value)) {
1101
+ issues.push({
1102
+ severity: 'error',
1103
+ message: `Invalid size="${value}" on <${compName}>. Allowed values: ${allowed.join(', ')}.`
1104
+ });
1105
+ }
1106
+ }
1107
+ }
1108
+ // Check if <script setup> imports are present
1109
+ const hasScriptSetup = code.includes('<script setup');
1110
+ const hasImport = code.includes("from '@spavn/ui'") || code.includes('from "@spavn/ui"');
1111
+ if (hasScriptSetup && validTags.length > 0 && !hasImport) {
1112
+ issues.push({
1113
+ severity: 'error',
1114
+ message: `Components are used but no import from '@spavn/ui' found. Add: import { ${validTags.join(', ')} } from '@spavn/ui'`
1115
+ });
1116
+ }
1117
+ // Build suggested import statement
1118
+ if (validTags.length > 0) {
1119
+ // Collect all needed imports (parents + sub-components)
1120
+ const needed = new Set();
1121
+ for (const tag of usedTags) {
1122
+ if (allComponentNames.has(tag)) {
1123
+ needed.add(tag);
1124
+ }
1125
+ }
1126
+ suggestions.push(`import { ${[...needed].sort().join(', ')} } from '@spavn/ui'`);
1127
+ }
1128
+ // Check TooltipProvider
1129
+ if (usedTags.has('Tooltip') && !usedTags.has('TooltipProvider')) {
1130
+ issues.push({
1131
+ severity: 'warning',
1132
+ message: 'Tooltip requires a <TooltipProvider> ancestor. Wrap your tooltips or add it near the app root.'
1133
+ });
1134
+ }
1135
+ // Check SidebarProvider
1136
+ if (usedTags.has('Sidebar') && !usedTags.has('SidebarProvider')) {
1137
+ issues.push({
1138
+ severity: 'warning',
1139
+ message: 'Sidebar requires a <SidebarProvider> ancestor. Wrap your sidebar layout with <SidebarProvider>.'
1140
+ });
1141
+ }
1142
+ return {
1143
+ content: [
1144
+ {
1145
+ type: 'text',
1146
+ text: JSON.stringify({
1147
+ valid: issues.filter(i => i.severity === 'error').length === 0,
1148
+ componentsDetected: [...usedTags].filter(t => allComponentNames.has(t)),
1149
+ issues,
1150
+ suggestedImport: suggestions[0] || null
1151
+ }, null, 2)
1152
+ }
1153
+ ]
1154
+ };
1155
+ }
1156
+ async function handleGetInstallationGuide(args) {
1157
+ const { packageManager = 'npm', framework = 'vite' } = args;
1158
+ const installCommands = {
1159
+ npm: 'npm install @spavn/ui',
1160
+ yarn: 'yarn add @spavn/ui',
1161
+ pnpm: 'pnpm add @spavn/ui',
1162
+ bun: 'bun add @spavn/ui'
1163
+ };
1164
+ const frameworkNotes = {
1165
+ vite: 'Standard Vite setup. Import styles in main.ts',
1166
+ nuxt: 'Add to plugins/ directory or import in app.vue',
1167
+ astro: 'Import in your layout component',
1168
+ vue: 'Standard Vue CLI setup'
1169
+ };
1170
+ return {
1171
+ content: [
1172
+ {
1173
+ type: 'text',
1174
+ text: `
1175
+ ## Installation
1176
+
1177
+ ### 1. Install the package
1178
+
1179
+ \`\`\`bash
1180
+ ${installCommands[packageManager]}
1181
+ \`\`\`
1182
+
1183
+ ### 2. Import styles
1184
+
1185
+ \`\`\`typescript
1186
+ // main.ts
1187
+ import '@spavn/ui/style.css'
1188
+ \`\`\`
1189
+
1190
+ ### 3. Start using components
1191
+
1192
+ \`\`\`vue
1193
+ <script setup>
1194
+ import { Button, Card } from '@spavn/ui'
1195
+ </script>
1196
+
1197
+ <template>
1198
+ <Button>Hello World</Button>
1199
+ </template>
1200
+ \`\`\`
1201
+
1202
+ ### Framework Notes
1203
+
1204
+ ${frameworkNotes[framework]}
1205
+
1206
+ ### Prerequisites
1207
+
1208
+ - Vue 3.3+
1209
+ - Tailwind CSS 3.4+
1210
+ - TypeScript 5.0+ (recommended)
1211
+ `
1212
+ }
1213
+ ]
1214
+ };
1215
+ }
1216
+ // ─── Create server ─────────────────────────────────────────────
1217
+ const server = new Server({
1218
+ name: 'spavn-ui-mcp',
1219
+ version: '1.1.0',
1220
+ });
1221
+ // Set up request handlers
1222
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1223
+ return { tools };
1224
+ });
1225
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1226
+ const { name, arguments: args } = request.params;
1227
+ try {
1228
+ switch (name) {
1229
+ case 'search_components':
1230
+ return await handleSearchComponents(args);
1231
+ case 'get_component_api':
1232
+ return await handleGetComponentApi(args);
1233
+ case 'generate_component_code':
1234
+ return await handleGenerateComponentCode(args);
1235
+ case 'get_theme_tokens':
1236
+ return await handleGetThemeTokens(args);
1237
+ case 'generate_theme':
1238
+ return await handleGenerateTheme(args);
1239
+ case 'validate_component_usage':
1240
+ return await handleValidateComponentUsage(args);
1241
+ case 'get_installation_guide':
1242
+ return await handleGetInstallationGuide(args);
1243
+ default:
1244
+ throw new Error(`Unknown tool: ${name}`);
1245
+ }
1246
+ }
1247
+ catch (error) {
1248
+ return {
1249
+ content: [
1250
+ {
1251
+ type: 'text',
1252
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
1253
+ }
1254
+ ],
1255
+ isError: true
1256
+ };
1257
+ }
1258
+ });
1259
+ // Start server
1260
+ async function main() {
1261
+ const transport = new StdioServerTransport();
1262
+ await server.connect(transport);
1263
+ console.error('Spavn UI MCP server running on stdio');
1264
+ }
1265
+ main().catch(console.error);
1266
+ //# sourceMappingURL=index.js.map