@slexn/codecenter-ui 1.0.0
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/LICENSE +34 -0
- package/README.md +148 -0
- package/components.json +20 -0
- package/dist/codecenter-ui.cjs +10 -0
- package/dist/codecenter-ui.js +17995 -0
- package/dist/components/ui/accordion/Accordion.vue.d.ts +28 -0
- package/dist/components/ui/accordion/AccordionContent.vue.d.ts +22 -0
- package/dist/components/ui/accordion/AccordionItem.vue.d.ts +24 -0
- package/dist/components/ui/accordion/AccordionTrigger.vue.d.ts +22 -0
- package/dist/components/ui/accordion/index.d.ts +4 -0
- package/dist/components/ui/alert/Alert.vue.d.ts +32 -0
- package/dist/components/ui/alert/AlertDescription.vue.d.ts +24 -0
- package/dist/components/ui/alert/AlertTitle.vue.d.ts +24 -0
- package/dist/components/ui/alert/index.d.ts +4 -0
- package/dist/components/ui/alert/variants.d.ts +5 -0
- package/dist/components/ui/button/Button.vue.d.ts +27 -0
- package/dist/components/ui/button/index.d.ts +2 -0
- package/dist/components/ui/button/variants.d.ts +6 -0
- package/dist/components/ui/button-group/ButtonGroup.vue.d.ts +25 -0
- package/dist/components/ui/button-group/index.d.ts +2 -0
- package/dist/components/ui/card/Card.vue.d.ts +21 -0
- package/dist/components/ui/card/CardContent.vue.d.ts +21 -0
- package/dist/components/ui/card/CardDescription.vue.d.ts +21 -0
- package/dist/components/ui/card/CardFooter.vue.d.ts +21 -0
- package/dist/components/ui/card/CardHeader.vue.d.ts +21 -0
- package/dist/components/ui/card/CardTitle.vue.d.ts +21 -0
- package/dist/components/ui/card/index.d.ts +6 -0
- package/dist/components/ui/chart/Chart.vue.d.ts +92 -0
- package/dist/components/ui/chart/index.d.ts +2 -0
- package/dist/components/ui/chat/Chat.vue.d.ts +190 -0
- package/dist/components/ui/chat/ChatAttachments.vue.d.ts +11 -0
- package/dist/components/ui/chat/ChatCodeBlock.vue.d.ts +16 -0
- package/dist/components/ui/chat/code-block.d.ts +27 -0
- package/dist/components/ui/chat/index.d.ts +6 -0
- package/dist/components/ui/chat/types.d.ts +15 -0
- package/dist/components/ui/checkbox/Checkbox.vue.d.ts +29 -0
- package/dist/components/ui/checkbox/index.d.ts +1 -0
- package/dist/components/ui/commit/Commit.vue.d.ts +62 -0
- package/dist/components/ui/commit/index.d.ts +2 -0
- package/dist/components/ui/contribution-graph/ContributionGraph.vue.d.ts +87 -0
- package/dist/components/ui/contribution-graph/index.d.ts +2 -0
- package/dist/components/ui/data-table/DataTable.vue.d.ts +109 -0
- package/dist/components/ui/data-table/index.d.ts +2 -0
- package/dist/components/ui/date-picker/DatePicker.vue.d.ts +37 -0
- package/dist/components/ui/date-picker/index.d.ts +2 -0
- package/dist/components/ui/dialog/Dialog.vue.d.ts +25 -0
- package/dist/components/ui/dialog/DialogClose.vue.d.ts +18 -0
- package/dist/components/ui/dialog/DialogContent.vue.d.ts +39 -0
- package/dist/components/ui/dialog/DialogDescription.vue.d.ts +22 -0
- package/dist/components/ui/dialog/DialogFooter.vue.d.ts +21 -0
- package/dist/components/ui/dialog/DialogHeader.vue.d.ts +21 -0
- package/dist/components/ui/dialog/DialogOverlay.vue.d.ts +22 -0
- package/dist/components/ui/dialog/DialogScrollContent.vue.d.ts +36 -0
- package/dist/components/ui/dialog/DialogTitle.vue.d.ts +22 -0
- package/dist/components/ui/dialog/DialogTrigger.vue.d.ts +18 -0
- package/dist/components/ui/dialog/index.d.ts +10 -0
- package/dist/components/ui/diff/DiffTool.vue.d.ts +21 -0
- package/dist/components/ui/diff/diff-parser.d.ts +30 -0
- package/dist/components/ui/diff/diff-tool.d.ts +36 -0
- package/dist/components/ui/diff/index.d.ts +2 -0
- package/dist/components/ui/dropdown-menu/DropdownMenu.vue.d.ts +24 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +29 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts +36 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuGroup.vue.d.ts +18 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuItem.vue.d.ts +26 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuLabel.vue.d.ts +23 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts +22 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuRadioItem.vue.d.ts +27 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuSeparator.vue.d.ts +7 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuShortcut.vue.d.ts +21 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuSub.vue.d.ts +22 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +38 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +23 -0
- package/dist/components/ui/dropdown-menu/DropdownMenuTrigger.vue.d.ts +18 -0
- package/dist/components/ui/dropdown-menu/index.d.ts +15 -0
- package/dist/components/ui/gauge/Gauge.vue.d.ts +62 -0
- package/dist/components/ui/gauge/index.d.ts +2 -0
- package/dist/components/ui/git-graph/GitGraph.vue.d.ts +59 -0
- package/dist/components/ui/git-graph/index.d.ts +2 -0
- package/dist/components/ui/incident-timeline/IncidentTimeline.vue.d.ts +50 -0
- package/dist/components/ui/incident-timeline/index.d.ts +2 -0
- package/dist/components/ui/input/Input.vue.d.ts +14 -0
- package/dist/components/ui/input/InputControl.vue.d.ts +14 -0
- package/dist/components/ui/input/InputFieldGroup.vue.d.ts +21 -0
- package/dist/components/ui/input/index.d.ts +4 -0
- package/dist/components/ui/input/types.d.ts +31 -0
- package/dist/components/ui/kpi-card/KpiCard.vue.d.ts +46 -0
- package/dist/components/ui/kpi-card/index.d.ts +2 -0
- package/dist/components/ui/kpi-line-card/KpiLineCard.vue.d.ts +66 -0
- package/dist/components/ui/kpi-line-card/index.d.ts +2 -0
- package/dist/components/ui/model-selector/ModelSelector.vue.d.ts +41 -0
- package/dist/components/ui/model-selector/index.d.ts +2 -0
- package/dist/components/ui/model-selector/types.d.ts +12 -0
- package/dist/components/ui/network-graph/NetworkGraph.vue.d.ts +75 -0
- package/dist/components/ui/network-graph/index.d.ts +2 -0
- package/dist/components/ui/pagination/Pagination.vue.d.ts +29 -0
- package/dist/components/ui/pagination/PaginationContent.vue.d.ts +29 -0
- package/dist/components/ui/pagination/PaginationEllipsis.vue.d.ts +22 -0
- package/dist/components/ui/pagination/PaginationFirst.vue.d.ts +26 -0
- package/dist/components/ui/pagination/PaginationItem.vue.d.ts +28 -0
- package/dist/components/ui/pagination/PaginationLast.vue.d.ts +26 -0
- package/dist/components/ui/pagination/PaginationNext.vue.d.ts +26 -0
- package/dist/components/ui/pagination/PaginationPrevious.vue.d.ts +26 -0
- package/dist/components/ui/pagination/index.d.ts +8 -0
- package/dist/components/ui/profile/Profile.vue.d.ts +30 -0
- package/dist/components/ui/profile/ProfileGroup.vue.d.ts +37 -0
- package/dist/components/ui/profile/index.d.ts +4 -0
- package/dist/components/ui/progress/Progress.vue.d.ts +36 -0
- package/dist/components/ui/progress/index.d.ts +2 -0
- package/dist/components/ui/prompt-input/PromptInput.vue.d.ts +150 -0
- package/dist/components/ui/prompt-input/index.d.ts +2 -0
- package/dist/components/ui/prompt-input/types.d.ts +61 -0
- package/dist/components/ui/radio-group/RadioGroup.vue.d.ts +30 -0
- package/dist/components/ui/radio-group/RadioGroupItem.vue.d.ts +12 -0
- package/dist/components/ui/radio-group/RadioGroupOption.vue.d.ts +28 -0
- package/dist/components/ui/radio-group/index.d.ts +3 -0
- package/dist/components/ui/reasoning/Reasoning.vue.d.ts +40 -0
- package/dist/components/ui/reasoning/index.d.ts +2 -0
- package/dist/components/ui/reasoning/types.d.ts +26 -0
- package/dist/components/ui/select/Select.vue.d.ts +28 -0
- package/dist/components/ui/select/SelectContent.vue.d.ts +46 -0
- package/dist/components/ui/select/SelectGroup.vue.d.ts +18 -0
- package/dist/components/ui/select/SelectItem.vue.d.ts +26 -0
- package/dist/components/ui/select/SelectItemText.vue.d.ts +18 -0
- package/dist/components/ui/select/SelectLabel.vue.d.ts +22 -0
- package/dist/components/ui/select/SelectScrollDownButton.vue.d.ts +22 -0
- package/dist/components/ui/select/SelectScrollUpButton.vue.d.ts +22 -0
- package/dist/components/ui/select/SelectSeparator.vue.d.ts +7 -0
- package/dist/components/ui/select/SelectTrigger.vue.d.ts +25 -0
- package/dist/components/ui/select/SelectValue.vue.d.ts +18 -0
- package/dist/components/ui/select/index.d.ts +11 -0
- package/dist/components/ui/select/search.d.ts +18 -0
- package/dist/components/ui/separator/Separator.vue.d.ts +6 -0
- package/dist/components/ui/separator/index.d.ts +2 -0
- package/dist/components/ui/separator/types.d.ts +7 -0
- package/dist/components/ui/shimmer/Shimmer.vue.d.ts +37 -0
- package/dist/components/ui/shimmer/index.d.ts +2 -0
- package/dist/components/ui/sidebar/Sidebar.vue.d.ts +24 -0
- package/dist/components/ui/sidebar/SidebarContent.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarFooter.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarGroup.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarGroupAction.vue.d.ts +24 -0
- package/dist/components/ui/sidebar/SidebarGroupContent.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarGroupLabel.vue.d.ts +24 -0
- package/dist/components/ui/sidebar/SidebarHeader.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarInput.vue.d.ts +6 -0
- package/dist/components/ui/sidebar/SidebarInset.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarMenu.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarMenuAction.vue.d.ts +25 -0
- package/dist/components/ui/sidebar/SidebarMenuBadge.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarMenuButton.vue.d.ts +25 -0
- package/dist/components/ui/sidebar/SidebarMenuButtonChild.vue.d.ts +30 -0
- package/dist/components/ui/sidebar/SidebarMenuItem.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarMenuSub.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarMenuSubButton.vue.d.ts +27 -0
- package/dist/components/ui/sidebar/SidebarMenuSubItem.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarProvider.vue.d.ts +36 -0
- package/dist/components/ui/sidebar/SidebarRail.vue.d.ts +21 -0
- package/dist/components/ui/sidebar/SidebarSeparator.vue.d.ts +6 -0
- package/dist/components/ui/sidebar/SidebarTrigger.vue.d.ts +6 -0
- package/dist/components/ui/sidebar/context.d.ts +19 -0
- package/dist/components/ui/sidebar/index.d.ts +26 -0
- package/dist/components/ui/sidebar/types.d.ts +11 -0
- package/dist/components/ui/sidebar/variants.d.ts +6 -0
- package/dist/components/ui/skeleton/Skeleton.vue.d.ts +18 -0
- package/dist/components/ui/skeleton/index.d.ts +2 -0
- package/dist/components/ui/sonner/Sonner.vue.d.ts +5 -0
- package/dist/components/ui/sonner/index.d.ts +2 -0
- package/dist/components/ui/spinner/Spinner.vue.d.ts +11 -0
- package/dist/components/ui/spinner/index.d.ts +1 -0
- package/dist/components/ui/stepper/Stepper.vue.d.ts +38 -0
- package/dist/components/ui/stepper/StepperDescription.vue.d.ts +22 -0
- package/dist/components/ui/stepper/StepperIndicator.vue.d.ts +30 -0
- package/dist/components/ui/stepper/StepperItem.vue.d.ts +24 -0
- package/dist/components/ui/stepper/StepperSeparator.vue.d.ts +7 -0
- package/dist/components/ui/stepper/StepperTitle.vue.d.ts +22 -0
- package/dist/components/ui/stepper/StepperTrigger.vue.d.ts +22 -0
- package/dist/components/ui/stepper/index.d.ts +7 -0
- package/dist/components/ui/switch/Switch.vue.d.ts +12 -0
- package/dist/components/ui/switch/index.d.ts +1 -0
- package/dist/components/ui/table/Table.vue.d.ts +22 -0
- package/dist/components/ui/table/TableBody.vue.d.ts +21 -0
- package/dist/components/ui/table/TableCaption.vue.d.ts +21 -0
- package/dist/components/ui/table/TableCell.vue.d.ts +22 -0
- package/dist/components/ui/table/TableEmpty.vue.d.ts +24 -0
- package/dist/components/ui/table/TableFooter.vue.d.ts +21 -0
- package/dist/components/ui/table/TableHead.vue.d.ts +21 -0
- package/dist/components/ui/table/TableHeader.vue.d.ts +21 -0
- package/dist/components/ui/table/TableRow.vue.d.ts +21 -0
- package/dist/components/ui/table/index.d.ts +9 -0
- package/dist/components/ui/tabs/Tabs.vue.d.ts +28 -0
- package/dist/components/ui/tabs/TabsContent.vue.d.ts +22 -0
- package/dist/components/ui/tabs/TabsList.vue.d.ts +22 -0
- package/dist/components/ui/tabs/TabsTrigger.vue.d.ts +22 -0
- package/dist/components/ui/tabs/index.d.ts +4 -0
- package/dist/components/ui/tag/Tag.vue.d.ts +35 -0
- package/dist/components/ui/tag/index.d.ts +2 -0
- package/dist/components/ui/tag/variants.d.ts +6 -0
- package/dist/components/ui/textarea/Textarea.vue.d.ts +14 -0
- package/dist/components/ui/textarea/TextareaControl.vue.d.ts +14 -0
- package/dist/components/ui/textarea/TextareaFieldGroup.vue.d.ts +21 -0
- package/dist/components/ui/textarea/index.d.ts +4 -0
- package/dist/components/ui/textarea/types.d.ts +32 -0
- package/dist/components/ui/tool/Tool.vue.d.ts +61 -0
- package/dist/components/ui/tool/index.d.ts +2 -0
- package/dist/components/ui/tooltip/Tooltip.vue.d.ts +24 -0
- package/dist/components/ui/tooltip/TooltipContent.vue.d.ts +32 -0
- package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts +20 -0
- package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts +18 -0
- package/dist/components/ui/tooltip/index.d.ts +4 -0
- package/dist/docs/component-docs.d.ts +18 -0
- package/dist/docs/markdown.d.ts +27 -0
- package/dist/index.d.ts +45 -0
- package/dist/lib/code-highlight.d.ts +11 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/styles.css +3 -0
- package/package.json +76 -0
- package/public/r/accordion.json +52 -0
- package/public/r/alert.json +51 -0
- package/public/r/button-group.json +31 -0
- package/public/r/button.json +39 -0
- package/public/r/card.json +61 -0
- package/public/r/chart.json +31 -0
- package/public/r/chat.json +186 -0
- package/public/r/checkbox.json +34 -0
- package/public/r/commit.json +32 -0
- package/public/r/contribution-graph.json +63 -0
- package/public/r/data-table.json +197 -0
- package/public/r/date-picker.json +33 -0
- package/public/r/dialog.json +88 -0
- package/public/r/diff.json +71 -0
- package/public/r/dropdown-menu.json +112 -0
- package/public/r/gauge.json +31 -0
- package/public/r/git-graph.json +32 -0
- package/public/r/incident-timeline.json +64 -0
- package/public/r/input.json +49 -0
- package/public/r/kpi-card.json +32 -0
- package/public/r/kpi-line-card.json +32 -0
- package/public/r/model-selector.json +148 -0
- package/public/r/network-graph.json +33 -0
- package/public/r/pagination.json +95 -0
- package/public/r/profile.json +37 -0
- package/public/r/progress.json +31 -0
- package/public/r/prompt-input.json +293 -0
- package/public/r/radio-group.json +45 -0
- package/public/r/reasoning.json +38 -0
- package/public/r/registry.json +2512 -0
- package/public/r/select.json +100 -0
- package/public/r/separator.json +37 -0
- package/public/r/shimmer.json +31 -0
- package/public/r/sidebar.json +221 -0
- package/public/r/skeleton.json +31 -0
- package/public/r/sonner.json +33 -0
- package/public/r/spinner.json +31 -0
- package/public/r/stepper.json +70 -0
- package/public/r/switch.json +33 -0
- package/public/r/table.json +79 -0
- package/public/r/tabs.json +51 -0
- package/public/r/tag.json +39 -0
- package/public/r/textarea.json +49 -0
- package/public/r/tool.json +32 -0
- package/public/r/tooltip.json +51 -0
- package/registry.json +2512 -0
- package/src/components/docs/MarkdownContent.vue +106 -0
- package/src/components/ui/accordion/Accordion.vue +24 -0
- package/src/components/ui/accordion/AccordionContent.vue +62 -0
- package/src/components/ui/accordion/AccordionItem.vue +23 -0
- package/src/components/ui/accordion/AccordionTrigger.vue +38 -0
- package/src/components/ui/accordion/index.ts +4 -0
- package/src/components/ui/alert/Alert.vue +40 -0
- package/src/components/ui/alert/AlertDescription.vue +24 -0
- package/src/components/ui/alert/AlertTitle.vue +24 -0
- package/src/components/ui/alert/index.ts +4 -0
- package/src/components/ui/alert/variants.ts +19 -0
- package/src/components/ui/button/Button.vue +27 -0
- package/src/components/ui/button/index.ts +2 -0
- package/src/components/ui/button/variants.ts +32 -0
- package/src/components/ui/button-group/ButtonGroup.vue +31 -0
- package/src/components/ui/button-group/index.ts +2 -0
- package/src/components/ui/card/Card.vue +17 -0
- package/src/components/ui/card/CardContent.vue +14 -0
- package/src/components/ui/card/CardDescription.vue +14 -0
- package/src/components/ui/card/CardFooter.vue +14 -0
- package/src/components/ui/card/CardHeader.vue +17 -0
- package/src/components/ui/card/CardTitle.vue +14 -0
- package/src/components/ui/card/index.ts +6 -0
- package/src/components/ui/chart/Chart.vue +1042 -0
- package/src/components/ui/chart/index.ts +13 -0
- package/src/components/ui/chat/Chat.vue +1297 -0
- package/src/components/ui/chat/ChatAttachments.vue +278 -0
- package/src/components/ui/chat/ChatCodeBlock.vue +283 -0
- package/src/components/ui/chat/code-block.ts +30 -0
- package/src/components/ui/chat/index.ts +24 -0
- package/src/components/ui/chat/types.ts +23 -0
- package/src/components/ui/checkbox/Checkbox.vue +38 -0
- package/src/components/ui/checkbox/index.ts +1 -0
- package/src/components/ui/commit/Commit.vue +423 -0
- package/src/components/ui/commit/index.ts +9 -0
- package/src/components/ui/contribution-graph/ContributionGraph.vue +719 -0
- package/src/components/ui/contribution-graph/index.ts +9 -0
- package/src/components/ui/data-table/DataTable.vue +534 -0
- package/src/components/ui/data-table/index.ts +9 -0
- package/src/components/ui/date-picker/DatePicker.vue +649 -0
- package/src/components/ui/date-picker/index.ts +7 -0
- package/src/components/ui/dialog/Dialog.vue +19 -0
- package/src/components/ui/dialog/DialogClose.vue +17 -0
- package/src/components/ui/dialog/DialogContent.vue +60 -0
- package/src/components/ui/dialog/DialogDescription.vue +23 -0
- package/src/components/ui/dialog/DialogFooter.vue +17 -0
- package/src/components/ui/dialog/DialogHeader.vue +17 -0
- package/src/components/ui/dialog/DialogOverlay.vue +23 -0
- package/src/components/ui/dialog/DialogScrollContent.vue +69 -0
- package/src/components/ui/dialog/DialogTitle.vue +23 -0
- package/src/components/ui/dialog/DialogTrigger.vue +17 -0
- package/src/components/ui/dialog/index.ts +10 -0
- package/src/components/ui/diff/DiffTool.vue +513 -0
- package/src/components/ui/diff/diff-parser.ts +423 -0
- package/src/components/ui/diff/diff-tool.ts +39 -0
- package/src/components/ui/diff/index.ts +5 -0
- package/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/src/components/ui/dropdown-menu/index.ts +16 -0
- package/src/components/ui/gauge/Gauge.vue +725 -0
- package/src/components/ui/gauge/index.ts +9 -0
- package/src/components/ui/git-graph/GitGraph.vue +715 -0
- package/src/components/ui/git-graph/index.ts +9 -0
- package/src/components/ui/incident-timeline/IncidentTimeline.vue +360 -0
- package/src/components/ui/incident-timeline/index.ts +7 -0
- package/src/components/ui/input/Input.vue +159 -0
- package/src/components/ui/input/InputControl.vue +135 -0
- package/src/components/ui/input/InputFieldGroup.vue +14 -0
- package/src/components/ui/input/index.ts +9 -0
- package/src/components/ui/input/types.ts +34 -0
- package/src/components/ui/kpi-card/KpiCard.vue +268 -0
- package/src/components/ui/kpi-card/index.ts +9 -0
- package/src/components/ui/kpi-line-card/KpiLineCard.vue +622 -0
- package/src/components/ui/kpi-line-card/index.ts +11 -0
- package/src/components/ui/model-selector/ModelSelector.vue +328 -0
- package/src/components/ui/model-selector/index.ts +6 -0
- package/src/components/ui/model-selector/types.ts +15 -0
- package/src/components/ui/network-graph/NetworkGraph.vue +902 -0
- package/src/components/ui/network-graph/index.ts +7 -0
- package/src/components/ui/pagination/Pagination.vue +26 -0
- package/src/components/ui/pagination/PaginationContent.vue +24 -0
- package/src/components/ui/pagination/PaginationEllipsis.vue +27 -0
- package/src/components/ui/pagination/PaginationFirst.vue +33 -0
- package/src/components/ui/pagination/PaginationItem.vue +39 -0
- package/src/components/ui/pagination/PaginationLast.vue +33 -0
- package/src/components/ui/pagination/PaginationNext.vue +33 -0
- package/src/components/ui/pagination/PaginationPrevious.vue +33 -0
- package/src/components/ui/pagination/index.ts +8 -0
- package/src/components/ui/profile/Profile.vue +226 -0
- package/src/components/ui/profile/ProfileGroup.vue +96 -0
- package/src/components/ui/profile/index.ts +8 -0
- package/src/components/ui/progress/Progress.vue +271 -0
- package/src/components/ui/progress/index.ts +9 -0
- package/src/components/ui/prompt-input/PromptInput.vue +1094 -0
- package/src/components/ui/prompt-input/index.ts +14 -0
- package/src/components/ui/prompt-input/types.ts +78 -0
- package/src/components/ui/radio-group/RadioGroup.vue +36 -0
- package/src/components/ui/radio-group/RadioGroupItem.vue +45 -0
- package/src/components/ui/radio-group/RadioGroupOption.vue +80 -0
- package/src/components/ui/radio-group/index.ts +3 -0
- package/src/components/ui/reasoning/Reasoning.vue +278 -0
- package/src/components/ui/reasoning/index.ts +8 -0
- package/src/components/ui/reasoning/types.ts +29 -0
- package/src/components/ui/select/Select.vue +19 -0
- package/src/components/ui/select/SelectContent.vue +166 -0
- package/src/components/ui/select/SelectGroup.vue +23 -0
- package/src/components/ui/select/SelectItem.vue +97 -0
- package/src/components/ui/select/SelectItemText.vue +15 -0
- package/src/components/ui/select/SelectLabel.vue +17 -0
- package/src/components/ui/select/SelectScrollDownButton.vue +26 -0
- package/src/components/ui/select/SelectScrollUpButton.vue +26 -0
- package/src/components/ui/select/SelectSeparator.vue +19 -0
- package/src/components/ui/select/SelectTrigger.vue +33 -0
- package/src/components/ui/select/SelectValue.vue +15 -0
- package/src/components/ui/select/index.ts +11 -0
- package/src/components/ui/select/search.ts +26 -0
- package/src/components/ui/separator/Separator.vue +30 -0
- package/src/components/ui/separator/index.ts +5 -0
- package/src/components/ui/separator/types.ts +9 -0
- package/src/components/ui/shimmer/Shimmer.vue +110 -0
- package/src/components/ui/shimmer/index.ts +5 -0
- package/src/components/ui/sidebar/Sidebar.vue +142 -0
- package/src/components/ui/sidebar/SidebarContent.vue +18 -0
- package/src/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/src/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/src/components/ui/sidebar/SidebarGroupAction.vue +31 -0
- package/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/src/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
- package/src/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/src/components/ui/sidebar/SidebarInput.vue +26 -0
- package/src/components/ui/sidebar/SidebarInset.vue +23 -0
- package/src/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/src/components/ui/sidebar/SidebarMenuAction.vue +34 -0
- package/src/components/ui/sidebar/SidebarMenuBadge.vue +25 -0
- package/src/components/ui/sidebar/SidebarMenuButton.vue +37 -0
- package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +38 -0
- package/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/src/components/ui/sidebar/SidebarMenuSub.vue +18 -0
- package/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- package/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/src/components/ui/sidebar/SidebarProvider.vue +119 -0
- package/src/components/ui/sidebar/SidebarRail.vue +35 -0
- package/src/components/ui/sidebar/SidebarSeparator.vue +18 -0
- package/src/components/ui/sidebar/SidebarTrigger.vue +28 -0
- package/src/components/ui/sidebar/context.ts +39 -0
- package/src/components/ui/sidebar/index.ts +43 -0
- package/src/components/ui/sidebar/types.ts +13 -0
- package/src/components/ui/sidebar/variants.ts +25 -0
- package/src/components/ui/skeleton/Skeleton.vue +53 -0
- package/src/components/ui/skeleton/index.ts +5 -0
- package/src/components/ui/sonner/Sonner.vue +69 -0
- package/src/components/ui/sonner/index.ts +12 -0
- package/src/components/ui/spinner/Spinner.vue +33 -0
- package/src/components/ui/spinner/index.ts +1 -0
- package/src/components/ui/stepper/Stepper.vue +29 -0
- package/src/components/ui/stepper/StepperDescription.vue +30 -0
- package/src/components/ui/stepper/StepperIndicator.vue +50 -0
- package/src/components/ui/stepper/StepperItem.vue +28 -0
- package/src/components/ui/stepper/StepperSeparator.vue +25 -0
- package/src/components/ui/stepper/StepperTitle.vue +27 -0
- package/src/components/ui/stepper/StepperTrigger.vue +27 -0
- package/src/components/ui/stepper/index.ts +7 -0
- package/src/components/ui/switch/Switch.vue +41 -0
- package/src/components/ui/switch/index.ts +1 -0
- package/src/components/ui/table/Table.vue +23 -0
- package/src/components/ui/table/TableBody.vue +17 -0
- package/src/components/ui/table/TableCaption.vue +17 -0
- package/src/components/ui/table/TableCell.vue +24 -0
- package/src/components/ui/table/TableEmpty.vue +31 -0
- package/src/components/ui/table/TableFooter.vue +17 -0
- package/src/components/ui/table/TableHead.vue +22 -0
- package/src/components/ui/table/TableHeader.vue +17 -0
- package/src/components/ui/table/TableRow.vue +22 -0
- package/src/components/ui/table/index.ts +9 -0
- package/src/components/ui/tabs/Tabs.vue +24 -0
- package/src/components/ui/tabs/TabsContent.vue +22 -0
- package/src/components/ui/tabs/TabsList.vue +27 -0
- package/src/components/ui/tabs/TabsTrigger.vue +27 -0
- package/src/components/ui/tabs/index.ts +4 -0
- package/src/components/ui/tag/Tag.vue +55 -0
- package/src/components/ui/tag/index.ts +2 -0
- package/src/components/ui/tag/variants.ts +29 -0
- package/src/components/ui/textarea/Textarea.vue +159 -0
- package/src/components/ui/textarea/TextareaControl.vue +120 -0
- package/src/components/ui/textarea/TextareaFieldGroup.vue +14 -0
- package/src/components/ui/textarea/index.ts +10 -0
- package/src/components/ui/textarea/types.ts +35 -0
- package/src/components/ui/tool/Tool.vue +304 -0
- package/src/components/ui/tool/index.ts +7 -0
- package/src/components/ui/tooltip/Tooltip.vue +19 -0
- package/src/components/ui/tooltip/TooltipContent.vue +44 -0
- package/src/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/src/components/ui/tooltip/index.ts +4 -0
- package/src/lib/code-highlight.ts +220 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +684 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-vue.com/schema/registry-item.json",
|
|
3
|
+
"name": "prompt-input",
|
|
4
|
+
"title": "Prompt Input",
|
|
5
|
+
"description": "A standalone AI prompt composer with add action, searchable model selection, model icons, toolbar slots, and submit behavior.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"@lucide/vue",
|
|
8
|
+
"@vueuse/core",
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"clsx",
|
|
11
|
+
"reka-ui",
|
|
12
|
+
"tailwind-merge"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "src/components/ui/button/Button.vue",
|
|
17
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive, type PrimitiveProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants, type ButtonVariants } from \"./variants\"\n\ninterface ButtonProps extends PrimitiveProps {\n variant?: ButtonVariants[\"variant\"]\n size?: ButtonVariants[\"size\"]\n class?: HTMLAttributes[\"class\"]\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n as: \"button\",\n})\n</script>\n\n<template>\n <Primitive\n data-slot=\"button\"\n :as=\"props.as\"\n :as-child=\"props.asChild\"\n :class=\"cn(buttonVariants({ variant: props.variant, size: props.size }), props.class)\"\n >\n <slot />\n </Primitive>\n</template>\n",
|
|
18
|
+
"type": "registry:ui",
|
|
19
|
+
"target": "components/ui/button/Button.vue"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "src/components/ui/button/variants.ts",
|
|
23
|
+
"content": "import { cva, type VariantProps } from \"class-variance-authority\"\n\nexport const buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/30\",\n outline:\n \"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-6\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>\n",
|
|
24
|
+
"type": "registry:ui",
|
|
25
|
+
"target": "components/ui/button/variants.ts"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "src/components/ui/button/index.ts",
|
|
29
|
+
"content": "export { default as Button } from \"./Button.vue\"\nexport { buttonVariants, type ButtonVariants } from \"./variants\"\n",
|
|
30
|
+
"type": "registry:ui",
|
|
31
|
+
"target": "components/ui/button/index.ts"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenu.vue",
|
|
35
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRootEmits, DropdownMenuRootProps } from \"reka-ui\"\nimport { DropdownMenuRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuRootProps>()\nconst emits = defineEmits<DropdownMenuRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <DropdownMenuRoot\n v-slot=\"slotProps\"\n data-slot=\"dropdown-menu\"\n v-bind=\"forwarded\"\n >\n <slot v-bind=\"slotProps\" />\n </DropdownMenuRoot>\n</template>\n",
|
|
36
|
+
"type": "registry:ui",
|
|
37
|
+
"target": "components/ui/dropdown-menu/DropdownMenu.vue"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuTrigger.vue",
|
|
41
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuTriggerProps } from \"reka-ui\"\nimport { DropdownMenuTrigger, useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuTriggerProps>()\n\nconst forwardedProps = useForwardProps(props)\n</script>\n\n<template>\n <DropdownMenuTrigger\n data-slot=\"dropdown-menu-trigger\"\n v-bind=\"forwardedProps\"\n >\n <slot />\n </DropdownMenuTrigger>\n</template>\n",
|
|
42
|
+
"type": "registry:ui",
|
|
43
|
+
"target": "components/ui/dropdown-menu/DropdownMenuTrigger.vue"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuContent.vue",
|
|
47
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuContentEmits, DropdownMenuContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuContent,\n DropdownMenuPortal,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(\n defineProps<DropdownMenuContentProps & { class?: HTMLAttributes[\"class\"] }>(),\n {\n sideOffset: 4,\n },\n)\nconst emits = defineEmits<DropdownMenuContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <DropdownMenuPortal>\n <DropdownMenuContent\n data-slot=\"dropdown-menu-content\"\n v-bind=\"{ ...$attrs, ...forwarded }\"\n :class=\"cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)\"\n >\n <slot />\n </DropdownMenuContent>\n </DropdownMenuPortal>\n</template>\n",
|
|
48
|
+
"type": "registry:ui",
|
|
49
|
+
"target": "components/ui/dropdown-menu/DropdownMenuContent.vue"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuGroup.vue",
|
|
53
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuGroupProps } from \"reka-ui\"\nimport { DropdownMenuGroup } from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuGroupProps>()\n</script>\n\n<template>\n <DropdownMenuGroup\n data-slot=\"dropdown-menu-group\"\n v-bind=\"props\"\n >\n <slot />\n </DropdownMenuGroup>\n</template>\n",
|
|
54
|
+
"type": "registry:ui",
|
|
55
|
+
"target": "components/ui/dropdown-menu/DropdownMenuGroup.vue"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuItem.vue",
|
|
59
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DropdownMenuItem, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<DropdownMenuItemProps & {\n class?: HTMLAttributes[\"class\"]\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}>(), {\n variant: \"default\",\n})\n\nconst delegatedProps = reactiveOmit(props, \"inset\", \"variant\", \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <DropdownMenuItem\n data-slot=\"dropdown-menu-item\"\n :data-inset=\"inset ? '' : undefined\"\n :data-variant=\"variant\"\n v-bind=\"forwardedProps\"\n :class=\"cn('focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*=text-])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4', props.class)\"\n >\n <slot />\n </DropdownMenuItem>\n</template>\n",
|
|
60
|
+
"type": "registry:ui",
|
|
61
|
+
"target": "components/ui/dropdown-menu/DropdownMenuItem.vue"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue",
|
|
65
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Check } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuCheckboxItem,\n DropdownMenuItemIndicator,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DropdownMenuCheckboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <DropdownMenuCheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n v-bind=\"forwarded\"\n :class=\"cn(\n 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n props.class,\n )\"\n >\n <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuItemIndicator>\n <slot name=\"indicator-icon\">\n <Check class=\"size-4\" />\n </slot>\n </DropdownMenuItemIndicator>\n </span>\n <slot />\n </DropdownMenuCheckboxItem>\n</template>\n",
|
|
66
|
+
"type": "registry:ui",
|
|
67
|
+
"target": "components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue",
|
|
71
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRadioGroupEmits, DropdownMenuRadioGroupProps } from \"reka-ui\"\nimport {\n DropdownMenuRadioGroup,\n useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuRadioGroupProps>()\nconst emits = defineEmits<DropdownMenuRadioGroupEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <DropdownMenuRadioGroup\n data-slot=\"dropdown-menu-radio-group\"\n v-bind=\"forwarded\"\n >\n <slot />\n </DropdownMenuRadioGroup>\n</template>\n",
|
|
72
|
+
"type": "registry:ui",
|
|
73
|
+
"target": "components/ui/dropdown-menu/DropdownMenuRadioGroup.vue"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue",
|
|
77
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuRadioItemEmits, DropdownMenuRadioItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Circle } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuItemIndicator,\n DropdownMenuRadioItem,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst emits = defineEmits<DropdownMenuRadioItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <DropdownMenuRadioItem\n data-slot=\"dropdown-menu-radio-item\"\n v-bind=\"forwarded\"\n :class=\"cn(\n 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n props.class,\n )\"\n >\n <span class=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuItemIndicator>\n <slot name=\"indicator-icon\">\n <Circle class=\"size-2 fill-current\" />\n </slot>\n </DropdownMenuItemIndicator>\n </span>\n <slot />\n </DropdownMenuRadioItem>\n</template>\n",
|
|
78
|
+
"type": "registry:ui",
|
|
79
|
+
"target": "components/ui/dropdown-menu/DropdownMenuRadioItem.vue"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuLabel.vue",
|
|
83
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DropdownMenuLabel, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <DropdownMenuLabel\n data-slot=\"dropdown-menu-label\"\n :data-inset=\"inset ? '' : undefined\"\n v-bind=\"forwardedProps\"\n :class=\"cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', props.class)\"\n >\n <slot />\n </DropdownMenuLabel>\n</template>\n",
|
|
84
|
+
"type": "registry:ui",
|
|
85
|
+
"target": "components/ui/dropdown-menu/DropdownMenuLabel.vue"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuSeparator.vue",
|
|
89
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuSeparator,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSeparatorProps & {\n class?: HTMLAttributes[\"class\"]\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n <DropdownMenuSeparator\n data-slot=\"dropdown-menu-separator\"\n v-bind=\"delegatedProps\"\n :class=\"cn('bg-border -mx-1 my-1 h-px', props.class)\"\n />\n</template>\n",
|
|
90
|
+
"type": "registry:ui",
|
|
91
|
+
"target": "components/ui/dropdown-menu/DropdownMenuSeparator.vue"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuShortcut.vue",
|
|
95
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n <span\n data-slot=\"dropdown-menu-shortcut\"\n :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n >\n <slot />\n </span>\n</template>\n",
|
|
96
|
+
"type": "registry:ui",
|
|
97
|
+
"target": "components/ui/dropdown-menu/DropdownMenuShortcut.vue"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuSub.vue",
|
|
101
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubEmits, DropdownMenuSubProps } from \"reka-ui\"\nimport {\n DropdownMenuSub,\n useForwardPropsEmits,\n} from \"reka-ui\"\n\nconst props = defineProps<DropdownMenuSubProps>()\nconst emits = defineEmits<DropdownMenuSubEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <DropdownMenuSub data-slot=\"dropdown-menu-sub\" v-bind=\"forwarded\">\n <slot />\n </DropdownMenuSub>\n</template>\n",
|
|
102
|
+
"type": "registry:ui",
|
|
103
|
+
"target": "components/ui/dropdown-menu/DropdownMenuSub.vue"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue",
|
|
107
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { ChevronRight } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuSubTrigger,\n useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes[\"class\"], inset?: boolean }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"inset\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <DropdownMenuSubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n v-bind=\"forwardedProps\"\n :data-inset=\"inset ? '' : undefined\"\n :class=\"cn(\n 'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*=text-])]:text-muted-foreground',\n props.class,\n )\"\n >\n <slot />\n <ChevronRight class=\"ml-auto size-4\" />\n </DropdownMenuSubTrigger>\n</template>\n",
|
|
108
|
+
"type": "registry:ui",
|
|
109
|
+
"target": "components/ui/dropdown-menu/DropdownMenuSubTrigger.vue"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"path": "src/components/ui/dropdown-menu/DropdownMenuSubContent.vue",
|
|
113
|
+
"content": "<script setup lang=\"ts\">\nimport type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DropdownMenuSubContent,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes[\"class\"] }>()\nconst emits = defineEmits<DropdownMenuSubContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <DropdownMenuSubContent\n data-slot=\"dropdown-menu-sub-content\"\n v-bind=\"forwarded\"\n :class=\"cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg', props.class)\"\n >\n <slot />\n </DropdownMenuSubContent>\n</template>\n",
|
|
114
|
+
"type": "registry:ui",
|
|
115
|
+
"target": "components/ui/dropdown-menu/DropdownMenuSubContent.vue"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"path": "src/components/ui/dropdown-menu/index.ts",
|
|
119
|
+
"content": "export { default as DropdownMenu } from \"./DropdownMenu.vue\"\n\nexport { default as DropdownMenuCheckboxItem } from \"./DropdownMenuCheckboxItem.vue\"\nexport { default as DropdownMenuContent } from \"./DropdownMenuContent.vue\"\nexport { default as DropdownMenuGroup } from \"./DropdownMenuGroup.vue\"\nexport { default as DropdownMenuItem } from \"./DropdownMenuItem.vue\"\nexport { default as DropdownMenuLabel } from \"./DropdownMenuLabel.vue\"\nexport { default as DropdownMenuRadioGroup } from \"./DropdownMenuRadioGroup.vue\"\nexport { default as DropdownMenuRadioItem } from \"./DropdownMenuRadioItem.vue\"\nexport { default as DropdownMenuSeparator } from \"./DropdownMenuSeparator.vue\"\nexport { default as DropdownMenuShortcut } from \"./DropdownMenuShortcut.vue\"\nexport { default as DropdownMenuSub } from \"./DropdownMenuSub.vue\"\nexport { default as DropdownMenuSubContent } from \"./DropdownMenuSubContent.vue\"\nexport { default as DropdownMenuSubTrigger } from \"./DropdownMenuSubTrigger.vue\"\nexport { default as DropdownMenuTrigger } from \"./DropdownMenuTrigger.vue\"\nexport { DropdownMenuPortal } from \"reka-ui\"\n",
|
|
120
|
+
"type": "registry:ui",
|
|
121
|
+
"target": "components/ui/dropdown-menu/index.ts"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"path": "src/components/ui/model-selector/ModelSelector.vue",
|
|
125
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, nextTick, ref, watch } from \"vue\"\nimport { Check, ChevronDown, Search, X } from \"@lucide/vue\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n} from \"@/components/ui/select\"\nimport { cn } from \"@/lib/utils\"\nimport Dialog from \"../dialog/Dialog.vue\"\nimport DialogContent from \"../dialog/DialogContent.vue\"\nimport DialogTitle from \"../dialog/DialogTitle.vue\"\nimport DialogTrigger from \"../dialog/DialogTrigger.vue\"\nimport type {\n ModelSelectorChangePayload,\n ModelSelectorMode,\n ModelSelectorOption,\n} from \"./types\"\n\ninterface ModelSelectorProps {\n class?: HTMLAttributes[\"class\"]\n contentClass?: HTMLAttributes[\"class\"]\n dialogClass?: HTMLAttributes[\"class\"]\n disabled?: boolean\n emptyText?: string\n label?: string\n modalThreshold?: number\n modalTitle?: string\n mode?: ModelSelectorMode\n modelValue?: string\n models?: ModelSelectorOption[]\n placeholder?: string\n searchPlaceholder?: string\n size?: \"default\" | \"sm\"\n triggerClass?: HTMLAttributes[\"class\"]\n}\n\nconst props = withDefaults(defineProps<ModelSelectorProps>(), {\n disabled: false,\n emptyText: \"No models found.\",\n label: \"Model\",\n modalThreshold: 8,\n modalTitle: \"Select model\",\n mode: \"auto\",\n modelValue: \"\",\n models: () => [],\n placeholder: \"Select model\",\n searchPlaceholder: \"Search models\",\n size: \"default\",\n})\n\nconst emit = defineEmits<{\n change: [payload: ModelSelectorChangePayload]\n \"update:modelValue\": [value: string]\n}>()\n\nconst dialogOpen = ref(false)\nconst searchQuery = ref(\"\")\nconst searchInputRef = ref<HTMLInputElement | null>(null)\n\nconst selectedValue = computed({\n get: () => props.modelValue,\n set: (value: string) => selectModel(value),\n})\nconst selectedModel = computed(() =>\n props.models.find((model) => model.value === props.modelValue),\n)\nconst shouldUseDialog = computed(() => {\n if (props.mode === \"dialog\") return true\n if (props.mode === \"select\") return false\n\n return props.models.length > props.modalThreshold\n})\nconst normalizedSearchQuery = computed(() => normalizeSearchText(searchQuery.value))\nconst filteredModels = computed(() => {\n if (!normalizedSearchQuery.value) return props.models\n\n return props.models.filter((model) =>\n normalizeSearchText([\n model.label,\n model.value,\n ].filter(Boolean).join(\" \")).includes(normalizedSearchQuery.value),\n )\n})\nconst hasSelection = computed(() => Boolean(selectedModel.value))\n\nwatch(dialogOpen, (open) => {\n if (!open) {\n searchQuery.value = \"\"\n return\n }\n\n void nextTick(() => searchInputRef.value?.focus())\n})\n\nfunction normalizeSearchText(value: string) {\n return value.trim().toLowerCase().replace(/\\s+/g, \" \")\n}\n\nfunction selectModel(value: string) {\n const option = props.models.find((model) => model.value === value)\n\n if (!option || option.disabled) return\n\n emit(\"update:modelValue\", value)\n emit(\"change\", {\n option,\n value,\n })\n}\n\nfunction selectDialogModel(model: ModelSelectorOption) {\n if (model.disabled) return\n\n selectModel(model.value)\n dialogOpen.value = false\n}\n\nfunction clearSearch() {\n searchQuery.value = \"\"\n void nextTick(() => searchInputRef.value?.focus())\n}\n\nfunction closeDialog() {\n dialogOpen.value = false\n}\n</script>\n\n<template>\n <div\n data-slot=\"model-selector\"\n :class=\"cn('inline-flex min-w-0', props.class)\"\n >\n <Select\n v-if=\"!shouldUseDialog\"\n v-model=\"selectedValue\"\n :disabled=\"props.disabled\"\n >\n <SelectTrigger\n :aria-label=\"props.label\"\n :size=\"props.size\"\n :class=\"cn('min-w-0 max-w-full', props.triggerClass)\"\n >\n <span\n data-slot=\"select-value\"\n :class=\"cn(\n 'inline-flex min-w-0 items-center gap-2',\n !hasSelection && 'text-muted-foreground',\n )\"\n >\n <component\n :is=\"selectedModel?.icon\"\n v-if=\"selectedModel?.icon\"\n data-slot=\"model-selector-icon\"\n />\n <span class=\"truncate\">\n {{ selectedModel?.label ?? props.placeholder }}\n </span>\n </span>\n </SelectTrigger>\n <SelectContent\n search\n :empty-text=\"props.emptyText\"\n :search-placeholder=\"props.searchPlaceholder\"\n :class=\"cn('w-72', props.contentClass)\"\n >\n <SelectItem\n v-for=\"model in props.models\"\n :key=\"model.value\"\n :disabled=\"model.disabled\"\n :text-value=\"model.label\"\n :value=\"model.value\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-2\">\n <component\n :is=\"model.icon\"\n v-if=\"model.icon\"\n data-slot=\"model-selector-icon\"\n />\n <span class=\"truncate\">{{ model.label }}</span>\n </span>\n </SelectItem>\n </SelectContent>\n </Select>\n\n <Dialog\n v-else\n v-model:open=\"dialogOpen\"\n >\n <DialogTrigger as-child>\n <button\n data-slot=\"model-selector-trigger\"\n :data-size=\"props.size\"\n :data-placeholder=\"hasSelection ? undefined : ''\"\n type=\"button\"\n :aria-label=\"props.label\"\n :disabled=\"props.disabled\"\n :class=\"cn(\n 'border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*=text-])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit min-w-0 max-w-full items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n props.triggerClass,\n )\"\n >\n <span\n data-slot=\"model-selector-value\"\n class=\"inline-flex min-w-0 items-center gap-2\"\n >\n <component\n :is=\"selectedModel?.icon\"\n v-if=\"selectedModel?.icon\"\n data-slot=\"model-selector-icon\"\n />\n <span class=\"truncate\">\n {{ selectedModel?.label ?? props.placeholder }}\n </span>\n </span>\n <ChevronDown class=\"size-4 opacity-50\" />\n </button>\n </DialogTrigger>\n\n <DialogContent\n :aria-describedby=\"undefined\"\n :show-close-button=\"false\"\n :class=\"\n cn(\n 'left-[50vw] w-[calc(100vw-4rem)] max-w-[calc(100vw-4rem)] gap-5 p-0 sm:max-w-2xl',\n props.dialogClass,\n )\n \"\n >\n <DialogTitle class=\"sr-only\">{{ props.modalTitle }}</DialogTitle>\n\n <div\n data-slot=\"model-selector-dialog-body\"\n class=\"flex min-h-0 flex-col\"\n >\n <div class=\"border-b px-4 py-3\">\n <div class=\"flex items-center gap-2\">\n <div class=\"relative min-w-0 flex-1\">\n <Search\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchQuery\"\n data-slot=\"model-selector-search\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n :placeholder=\"props.searchPlaceholder\"\n class=\"h-9 w-full rounded-md border border-input bg-transparent py-1.5 pr-9 pl-9 text-sm shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50\"\n />\n <button\n v-if=\"searchQuery\"\n data-slot=\"model-selector-search-clear\"\n type=\"button\"\n aria-label=\"Clear search\"\n class=\"absolute top-1/2 right-1.5 flex size-7 -translate-y-1/2 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\"\n @click=\"clearSearch\"\n >\n <X aria-hidden=\"true\" class=\"size-4\" />\n </button>\n </div>\n <button\n data-slot=\"model-selector-close\"\n type=\"button\"\n aria-label=\"Close\"\n class=\"flex size-9 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\"\n @click=\"closeDialog\"\n >\n <X aria-hidden=\"true\" class=\"size-4\" />\n </button>\n </div>\n </div>\n\n <div\n data-slot=\"model-selector-list\"\n role=\"listbox\"\n :aria-label=\"props.label\"\n class=\"max-h-[min(60vh,28rem)] overflow-y-auto p-2\"\n >\n <button\n v-for=\"model in filteredModels\"\n :key=\"model.value\"\n data-slot=\"model-selector-option\"\n type=\"button\"\n role=\"option\"\n :aria-selected=\"model.value === props.modelValue\"\n :disabled=\"model.disabled\"\n :class=\"cn(\n 'group flex w-full min-w-0 items-center gap-3 rounded-md px-3 py-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 aria-selected:bg-accent aria-selected:text-accent-foreground',\n )\"\n @click=\"selectDialogModel(model)\"\n >\n <component\n :is=\"model.icon\"\n v-if=\"model.icon\"\n data-slot=\"model-selector-option-icon\"\n class=\"size-4 shrink-0\"\n />\n <span\n data-slot=\"model-selector-option-label\"\n class=\"min-w-0 flex-1 truncate font-medium\"\n >\n {{ model.label }}\n </span>\n <Check\n v-if=\"model.value === props.modelValue\"\n aria-hidden=\"true\"\n class=\"size-4 shrink-0\"\n />\n </button>\n\n <div\n v-if=\"!filteredModels.length\"\n data-slot=\"model-selector-empty\"\n class=\"px-3 py-10 text-center text-sm text-muted-foreground\"\n >\n {{ props.emptyText }}\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n</template>\n",
|
|
126
|
+
"type": "registry:ui",
|
|
127
|
+
"target": "components/ui/model-selector/ModelSelector.vue"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"path": "src/components/ui/model-selector/types.ts",
|
|
131
|
+
"content": "import type { Component } from \"vue\"\n\nexport type ModelSelectorMode = \"auto\" | \"dialog\" | \"select\"\n\nexport interface ModelSelectorOption {\n disabled?: boolean\n icon?: Component\n label: string\n value: string\n}\n\nexport interface ModelSelectorChangePayload {\n option?: ModelSelectorOption\n value: string\n}\n",
|
|
132
|
+
"type": "registry:ui",
|
|
133
|
+
"target": "components/ui/model-selector/types.ts"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"path": "src/components/ui/model-selector/index.ts",
|
|
137
|
+
"content": "export { default as ModelSelector } from \"./ModelSelector.vue\"\nexport type {\n ModelSelectorChangePayload,\n ModelSelectorMode,\n ModelSelectorOption,\n} from \"./types\"\n",
|
|
138
|
+
"type": "registry:ui",
|
|
139
|
+
"target": "components/ui/model-selector/index.ts"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"path": "src/components/ui/select/Select.vue",
|
|
143
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectRootEmits, SelectRootProps } from \"reka-ui\"\nimport { SelectRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<SelectRootProps>()\nconst emits = defineEmits<SelectRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <SelectRoot\n v-slot=\"slotProps\"\n data-slot=\"select\"\n v-bind=\"forwarded\"\n >\n <slot v-bind=\"slotProps\" />\n </SelectRoot>\n</template>\n",
|
|
144
|
+
"type": "registry:ui",
|
|
145
|
+
"target": "components/ui/select/Select.vue"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"path": "src/components/ui/select/SelectTrigger.vue",
|
|
149
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectTriggerProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { ChevronDown } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SelectIcon, SelectTrigger, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(\n defineProps<SelectTriggerProps & { class?: HTMLAttributes[\"class\"], size?: \"sm\" | \"default\" }>(),\n { size: \"default\" },\n)\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"size\")\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <SelectTrigger\n data-slot=\"select-trigger\"\n :data-size=\"size\"\n v-bind=\"forwardedProps\"\n :class=\"cn(\n 'border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*=text-])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n props.class,\n )\"\n >\n <slot />\n <SelectIcon as-child>\n <ChevronDown class=\"size-4 opacity-50\" />\n </SelectIcon>\n </SelectTrigger>\n</template>\n",
|
|
150
|
+
"type": "registry:ui",
|
|
151
|
+
"target": "components/ui/select/SelectTrigger.vue"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"path": "src/components/ui/select/SelectValue.vue",
|
|
155
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectValueProps } from \"reka-ui\"\nimport { SelectValue } from \"reka-ui\"\n\nconst props = defineProps<SelectValueProps>()\n</script>\n\n<template>\n <SelectValue\n data-slot=\"select-value\"\n v-bind=\"props\"\n >\n <slot />\n </SelectValue>\n</template>\n",
|
|
156
|
+
"type": "registry:ui",
|
|
157
|
+
"target": "components/ui/select/SelectValue.vue"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"path": "src/components/ui/select/SelectContent.vue",
|
|
161
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectContentEmits, SelectContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, nextTick, provide, ref, shallowReactive } from \"vue\"\nimport { Search, X } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n SelectContent,\n SelectPortal,\n SelectViewport,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { SelectScrollDownButton, SelectScrollUpButton } from \".\"\nimport {\n normalizeSelectSearchText,\n selectSearchContextKey,\n type SelectSearchItem,\n} from \"./search\"\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(\n defineProps<SelectContentProps & {\n class?: HTMLAttributes[\"class\"]\n emptyText?: string\n search?: boolean\n searchClearLabel?: string\n searchPlaceholder?: string\n 검색?: boolean\n }>(),\n {\n emptyText: \"No results found.\",\n position: \"popper\",\n search: false,\n searchClearLabel: \"Clear search\",\n searchPlaceholder: \"Search options\",\n 검색: false,\n },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst delegatedProps = reactiveOmit(\n props,\n \"class\",\n \"emptyText\",\n \"search\",\n \"searchClearLabel\",\n \"searchPlaceholder\",\n \"검색\",\n)\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\nconst searchQuery = ref(\"\")\nconst searchInputRef = ref<HTMLInputElement | null>(null)\nconst searchItems = shallowReactive(new Map<symbol, SelectSearchItem>())\nconst isSearchEnabled = computed(() => props.search || props.검색)\nconst normalizedSearchQuery = computed(() => normalizeSelectSearchText(searchQuery.value))\nconst hasSearchQuery = computed(() => normalizedSearchQuery.value.length > 0)\nconst hasVisibleItems = computed(() => {\n if (!isSearchEnabled.value || !hasSearchQuery.value) return true\n\n return Array.from(searchItems.values()).some((item) => item.matches.value)\n})\n\nfunction hasVisibleItemsInGroup(groupId: symbol) {\n if (!isSearchEnabled.value || !hasSearchQuery.value) return true\n\n return Array.from(searchItems.values()).some(\n (item) => item.groupId === groupId && item.matches.value,\n )\n}\n\nfunction handleSearchKeydown(event: KeyboardEvent) {\n if (event.key === \"Escape\" && searchQuery.value) {\n searchQuery.value = \"\"\n event.preventDefault()\n event.stopPropagation()\n return\n }\n\n if (event.key !== \"Escape\") {\n event.stopPropagation()\n }\n}\n\nfunction clearSearch() {\n searchQuery.value = \"\"\n\n void nextTick(() => searchInputRef.value?.focus())\n}\n\nprovide(selectSearchContextKey, {\n enabled: isSearchEnabled,\n hasQuery: hasSearchQuery,\n hasVisibleItems,\n query: normalizedSearchQuery,\n hasVisibleItemsInGroup,\n registerItem: (item) => searchItems.set(item.id, item),\n unregisterItem: (id) => searchItems.delete(id),\n})\n</script>\n\n<template>\n <SelectPortal>\n <SelectContent\n data-slot=\"select-content\"\n v-bind=\"{ ...$attrs, ...forwarded }\"\n :class=\"cn(\n 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--reka-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md',\n position === 'popper'\n && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n props.class,\n )\n \"\n >\n <SelectScrollUpButton />\n <div v-if=\"isSearchEnabled\" data-slot=\"select-search\" class=\"border-b p-1\">\n <div class=\"relative\">\n <Search\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchQuery\"\n data-slot=\"select-search-input\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n :placeholder=\"props.searchPlaceholder\"\n class=\"h-8 w-full rounded-sm bg-transparent py-1.5 pr-8 pl-8 text-sm outline-none placeholder:text-muted-foreground focus:bg-accent focus:text-accent-foreground\"\n @click.stop\n @keydown=\"handleSearchKeydown\"\n @pointerdown.stop\n />\n <button\n v-if=\"searchQuery\"\n data-slot=\"select-search-clear\"\n type=\"button\"\n :aria-label=\"props.searchClearLabel\"\n class=\"absolute top-1/2 right-1 flex size-6 -translate-y-1/2 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\"\n @click.stop.prevent=\"clearSearch\"\n @keydown.stop\n @pointerdown.stop.prevent\n >\n <X aria-hidden=\"true\" class=\"size-4\" />\n </button>\n </div>\n </div>\n <SelectViewport :class=\"cn('p-1', position === 'popper' && 'h-[var(--reka-select-trigger-height)] w-full min-w-[var(--reka-select-trigger-width)] scroll-my-1')\">\n <slot />\n <div\n v-if=\"isSearchEnabled && hasSearchQuery && !hasVisibleItems\"\n data-slot=\"select-empty\"\n class=\"px-2 py-6 text-center text-sm text-muted-foreground\"\n >\n {{ props.emptyText }}\n </div>\n </SelectViewport>\n <SelectScrollDownButton />\n </SelectContent>\n </SelectPortal>\n</template>\n",
|
|
162
|
+
"type": "registry:ui",
|
|
163
|
+
"target": "components/ui/select/SelectContent.vue"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"path": "src/components/ui/select/SelectGroup.vue",
|
|
167
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectGroupProps } from \"reka-ui\"\nimport { computed, inject, provide } from \"vue\"\nimport { SelectGroup } from \"reka-ui\"\nimport { selectSearchContextKey, selectSearchGroupKey } from \"./search\"\n\nconst props = defineProps<SelectGroupProps>()\nconst groupId = Symbol(\"select-group\")\nconst searchContext = inject(selectSearchContextKey, null)\nconst isVisible = computed(() => searchContext?.hasVisibleItemsInGroup(groupId) ?? true)\n\nprovide(selectSearchGroupKey, groupId)\n</script>\n\n<template>\n <SelectGroup\n v-show=\"isVisible\"\n data-slot=\"select-group\"\n v-bind=\"props\"\n >\n <slot />\n </SelectGroup>\n</template>\n",
|
|
168
|
+
"type": "registry:ui",
|
|
169
|
+
"target": "components/ui/select/SelectGroup.vue"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"path": "src/components/ui/select/SelectItem.vue",
|
|
173
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectItemProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, inject, onBeforeUnmount } from \"vue\"\nimport { Check } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n SelectItem,\n SelectItemIndicator,\n SelectItemText,\n useForwardProps,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport {\n normalizeSelectSearchText,\n selectSearchContextKey,\n selectSearchGroupKey,\n} from \"./search\"\n\nconst props = defineProps<SelectItemProps & {\n class?: HTMLAttributes[\"class\"]\n description?: string\n descriptionClass?: HTMLAttributes[\"class\"]\n}>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"description\", \"descriptionClass\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\nconst searchContext = inject(selectSearchContextKey, null)\nconst groupId = inject(selectSearchGroupKey, undefined)\nconst itemId = Symbol(\"select-item\")\nconst itemSearchText = computed(() =>\n normalizeSelectSearchText(\n [props.textValue ?? props.value, props.description].filter(Boolean).join(\" \"),\n ),\n)\nconst matchesSearch = computed(() => {\n if (!searchContext?.enabled.value || !searchContext.hasQuery.value) return true\n\n return itemSearchText.value.includes(searchContext.query.value)\n})\nconst isVisible = computed(() => matchesSearch.value)\n\nif (searchContext) {\n searchContext.registerItem({\n groupId,\n id: itemId,\n matches: matchesSearch,\n })\n\n onBeforeUnmount(() => searchContext.unregisterItem(itemId))\n}\n</script>\n\n<template>\n <SelectItem\n v-if=\"isVisible\"\n data-slot=\"select-item\"\n v-bind=\"forwardedProps\"\n :class=\"\n cn(\n 'group focus:bg-accent focus:text-accent-foreground data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*=text-])]:text-muted-foreground relative flex w-full cursor-default items-start gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n props.class,\n )\n \"\n >\n <span class=\"absolute top-1/2 right-2 flex size-3.5 -translate-y-1/2 items-center justify-center\">\n <SelectItemIndicator>\n <slot name=\"indicator-icon\">\n <Check class=\"size-4\" />\n </slot>\n </SelectItemIndicator>\n </span>\n\n <span class=\"grid min-w-0 gap-0.5\">\n <SelectItemText>\n <span data-slot=\"select-item-label\" class=\"truncate\">\n <slot />\n </span>\n </SelectItemText>\n <span\n v-if=\"props.description || $slots.description\"\n data-slot=\"select-item-description\"\n :class=\"\n cn(\n 'text-xs leading-snug text-muted-foreground group-data-[highlighted]:text-accent-foreground/80 group-focus:text-accent-foreground/80',\n props.descriptionClass,\n )\n \"\n >\n <slot name=\"description\">\n {{ props.description }}\n </slot>\n </span>\n </span>\n </SelectItem>\n</template>\n",
|
|
174
|
+
"type": "registry:ui",
|
|
175
|
+
"target": "components/ui/select/SelectItem.vue"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"path": "src/components/ui/select/SelectItemText.vue",
|
|
179
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectItemTextProps } from \"reka-ui\"\nimport { SelectItemText } from \"reka-ui\"\n\nconst props = defineProps<SelectItemTextProps>()\n</script>\n\n<template>\n <SelectItemText\n data-slot=\"select-item-text\"\n v-bind=\"props\"\n >\n <slot />\n </SelectItemText>\n</template>\n",
|
|
180
|
+
"type": "registry:ui",
|
|
181
|
+
"target": "components/ui/select/SelectItemText.vue"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"path": "src/components/ui/select/SelectLabel.vue",
|
|
185
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectLabelProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { SelectLabel } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectLabelProps & { class?: HTMLAttributes[\"class\"] }>()\n</script>\n\n<template>\n <SelectLabel\n data-slot=\"select-label\"\n :class=\"cn('text-muted-foreground px-2 py-1.5 text-xs', props.class)\"\n >\n <slot />\n </SelectLabel>\n</template>\n",
|
|
186
|
+
"type": "registry:ui",
|
|
187
|
+
"target": "components/ui/select/SelectLabel.vue"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"path": "src/components/ui/select/SelectSeparator.vue",
|
|
191
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectSeparatorProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SelectSeparator } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n</script>\n\n<template>\n <SelectSeparator\n data-slot=\"select-separator\"\n v-bind=\"delegatedProps\"\n :class=\"cn('bg-border pointer-events-none -mx-1 my-1 h-px', props.class)\"\n />\n</template>\n",
|
|
192
|
+
"type": "registry:ui",
|
|
193
|
+
"target": "components/ui/select/SelectSeparator.vue"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"path": "src/components/ui/select/SelectScrollUpButton.vue",
|
|
197
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectScrollUpButtonProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { ChevronUp } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SelectScrollUpButton, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <SelectScrollUpButton\n data-slot=\"select-scroll-up-button\"\n v-bind=\"forwardedProps\"\n :class=\"cn('flex cursor-default items-center justify-center py-1', props.class)\"\n >\n <slot>\n <ChevronUp class=\"size-4\" />\n </slot>\n </SelectScrollUpButton>\n</template>\n",
|
|
198
|
+
"type": "registry:ui",
|
|
199
|
+
"target": "components/ui/select/SelectScrollUpButton.vue"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"path": "src/components/ui/select/SelectScrollDownButton.vue",
|
|
203
|
+
"content": "<script setup lang=\"ts\">\nimport type { SelectScrollDownButtonProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { ChevronDown } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { SelectScrollDownButton, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <SelectScrollDownButton\n data-slot=\"select-scroll-down-button\"\n v-bind=\"forwardedProps\"\n :class=\"cn('flex cursor-default items-center justify-center py-1', props.class)\"\n >\n <slot>\n <ChevronDown class=\"size-4\" />\n </slot>\n </SelectScrollDownButton>\n</template>\n",
|
|
204
|
+
"type": "registry:ui",
|
|
205
|
+
"target": "components/ui/select/SelectScrollDownButton.vue"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"path": "src/components/ui/select/search.ts",
|
|
209
|
+
"content": "import type { ComputedRef, InjectionKey } from \"vue\"\n\nexport interface SelectSearchItem {\n groupId?: symbol\n id: symbol\n matches: ComputedRef<boolean>\n}\n\nexport interface SelectSearchContext {\n enabled: ComputedRef<boolean>\n hasQuery: ComputedRef<boolean>\n hasVisibleItems: ComputedRef<boolean>\n query: ComputedRef<string>\n hasVisibleItemsInGroup: (groupId: symbol) => boolean\n registerItem: (item: SelectSearchItem) => void\n unregisterItem: (id: symbol) => void\n}\n\nexport const selectSearchContextKey: InjectionKey<SelectSearchContext> =\n Symbol(\"select-search-context\")\n\nexport const selectSearchGroupKey: InjectionKey<symbol> = Symbol(\"select-search-group\")\n\nexport function normalizeSelectSearchText(value: unknown) {\n return String(value ?? \"\").trim().toLocaleLowerCase()\n}\n",
|
|
210
|
+
"type": "registry:ui",
|
|
211
|
+
"target": "components/ui/select/search.ts"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"path": "src/components/ui/select/index.ts",
|
|
215
|
+
"content": "export { default as Select } from \"./Select.vue\"\nexport { default as SelectContent } from \"./SelectContent.vue\"\nexport { default as SelectGroup } from \"./SelectGroup.vue\"\nexport { default as SelectItem } from \"./SelectItem.vue\"\nexport { default as SelectItemText } from \"./SelectItemText.vue\"\nexport { default as SelectLabel } from \"./SelectLabel.vue\"\nexport { default as SelectScrollDownButton } from \"./SelectScrollDownButton.vue\"\nexport { default as SelectScrollUpButton } from \"./SelectScrollUpButton.vue\"\nexport { default as SelectSeparator } from \"./SelectSeparator.vue\"\nexport { default as SelectTrigger } from \"./SelectTrigger.vue\"\nexport { default as SelectValue } from \"./SelectValue.vue\"\n",
|
|
216
|
+
"type": "registry:ui",
|
|
217
|
+
"target": "components/ui/select/index.ts"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"path": "src/components/ui/dialog/Dialog.vue",
|
|
221
|
+
"content": "<script setup lang=\"ts\">\nimport type { DialogRootEmits, DialogRootProps } from \"reka-ui\"\nimport { DialogRoot, useForwardPropsEmits } from \"reka-ui\"\n\nconst props = defineProps<DialogRootProps>()\nconst emits = defineEmits<DialogRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <DialogRoot\n v-slot=\"slotProps\"\n data-slot=\"dialog\"\n v-bind=\"forwarded\"\n >\n <slot v-bind=\"slotProps\" />\n </DialogRoot>\n</template>\n",
|
|
222
|
+
"type": "registry:ui",
|
|
223
|
+
"target": "components/ui/dialog/Dialog.vue"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"path": "src/components/ui/dialog/DialogTrigger.vue",
|
|
227
|
+
"content": "<script setup lang=\"ts\">\nimport type { DialogTriggerProps } from \"reka-ui\"\nimport { DialogTrigger, useForwardProps } from \"reka-ui\"\n\nconst props = defineProps<DialogTriggerProps>()\n\nconst forwardedProps = useForwardProps(props)\n</script>\n\n<template>\n <DialogTrigger\n data-slot=\"dialog-trigger\"\n v-bind=\"forwardedProps\"\n >\n <slot />\n </DialogTrigger>\n</template>\n",
|
|
228
|
+
"type": "registry:ui",
|
|
229
|
+
"target": "components/ui/dialog/DialogTrigger.vue"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"path": "src/components/ui/dialog/DialogContent.vue",
|
|
233
|
+
"content": "<script setup lang=\"ts\">\nimport type { DialogContentEmits, DialogContentProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { X } from \"@lucide/vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n DialogClose,\n DialogContent,\n DialogPortal,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport DialogOverlay from \"./DialogOverlay.vue\"\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(\n defineProps<DialogContentProps & {\n class?: HTMLAttributes[\"class\"]\n showCloseButton?: boolean\n }>(),\n {\n showCloseButton: true,\n },\n)\nconst emits = defineEmits<DialogContentEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"showCloseButton\")\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <DialogPortal>\n <DialogOverlay />\n <DialogContent\n data-slot=\"dialog-content\"\n v-bind=\"{ ...$attrs, ...forwarded }\"\n :class=\"\n cn(\n 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border bg-background p-6 text-foreground shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg',\n props.class,\n )\n \"\n >\n <slot />\n\n <DialogClose\n v-if=\"props.showCloseButton\"\n data-slot=\"dialog-close\"\n class=\"absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background focus:outline-none disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <X aria-hidden=\"true\" />\n <span class=\"sr-only\">Close</span>\n </DialogClose>\n </DialogContent>\n </DialogPortal>\n</template>\n",
|
|
234
|
+
"type": "registry:ui",
|
|
235
|
+
"target": "components/ui/dialog/DialogContent.vue"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"path": "src/components/ui/dialog/DialogOverlay.vue",
|
|
239
|
+
"content": "<script setup lang=\"ts\">\nimport type { DialogOverlayProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogOverlay, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogOverlayProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <DialogOverlay\n data-slot=\"dialog-overlay\"\n v-bind=\"forwardedProps\"\n :class=\"cn('fixed inset-0 z-50 bg-[var(--overlay)] backdrop-blur-[4px] [-webkit-backdrop-filter:blur(4px)] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', props.class)\"\n >\n <slot />\n </DialogOverlay>\n</template>\n",
|
|
240
|
+
"type": "registry:ui",
|
|
241
|
+
"target": "components/ui/dialog/DialogOverlay.vue"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"path": "src/components/ui/dialog/DialogTitle.vue",
|
|
245
|
+
"content": "<script setup lang=\"ts\">\nimport type { DialogTitleProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { DialogTitle, useForwardProps } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<DialogTitleProps & { class?: HTMLAttributes[\"class\"] }>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <DialogTitle\n data-slot=\"dialog-title\"\n v-bind=\"forwardedProps\"\n :class=\"cn('text-lg font-semibold leading-none', props.class)\"\n >\n <slot />\n </DialogTitle>\n</template>\n",
|
|
246
|
+
"type": "registry:ui",
|
|
247
|
+
"target": "components/ui/dialog/DialogTitle.vue"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"path": "src/components/ui/tag/Tag.vue",
|
|
251
|
+
"content": "<script setup lang=\"ts\">\nimport type { Component, HTMLAttributes } from \"vue\"\nimport { XIcon } from \"@lucide/vue\"\nimport { cn } from \"@/lib/utils\"\nimport { tagVariants, type TagVariants } from \"./variants\"\n\ninterface TagProps {\n class?: HTMLAttributes[\"class\"]\n icon?: Component\n iconClass?: HTMLAttributes[\"class\"]\n labelClass?: HTMLAttributes[\"class\"]\n removable?: boolean\n removeLabel?: string\n size?: TagVariants[\"size\"]\n variant?: TagVariants[\"variant\"]\n}\n\nconst props = withDefaults(defineProps<TagProps>(), {\n removeLabel: \"Remove tag\",\n})\n\nconst emit = defineEmits<{\n (event: \"remove\"): void\n}>()\n</script>\n\n<template>\n <span\n data-slot=\"tag\"\n :class=\"cn(tagVariants({ size: props.size, variant: props.variant }), props.class)\"\n >\n <component\n :is=\"props.icon\"\n v-if=\"props.icon\"\n data-slot=\"tag-icon\"\n aria-hidden=\"true\"\n :class=\"props.iconClass\"\n />\n\n <span data-slot=\"tag-label\" :class=\"cn('min-w-0 truncate', props.labelClass)\">\n <slot />\n </span>\n\n <button\n v-if=\"props.removable\"\n data-slot=\"tag-remove\"\n type=\"button\"\n :aria-label=\"props.removeLabel\"\n class=\"-mr-1 inline-flex shrink-0 items-center justify-center rounded-full p-0.5 text-current opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50\"\n @click.stop=\"emit('remove')\"\n >\n <XIcon data-slot=\"tag-remove-icon\" aria-hidden=\"true\" />\n </button>\n </span>\n</template>\n",
|
|
252
|
+
"type": "registry:ui",
|
|
253
|
+
"target": "components/ui/tag/Tag.vue"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"path": "src/components/ui/tag/variants.ts",
|
|
257
|
+
"content": "import { cva, type VariantProps } from \"class-variance-authority\"\n\nexport const tagVariants = cva(\n \"inline-flex max-w-full shrink-0 items-center gap-1 rounded-full border font-medium whitespace-nowrap outline-none transition-[background-color,border-color,color,box-shadow] focus-within:ring-[3px] focus-within:ring-ring/50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"border-transparent bg-primary text-primary-foreground\",\n destructive:\n \"border-transparent bg-destructive text-destructive-foreground focus-within:ring-destructive/30\",\n muted: \"border-transparent bg-muted text-muted-foreground\",\n outline: \"border-border bg-background text-foreground\",\n secondary: \"border-transparent bg-secondary text-secondary-foreground\",\n },\n size: {\n default:\n \"h-7 px-2.5 text-xs [&_[data-slot=tag-icon]]:size-3.5 [&_[data-slot=tag-remove-icon]]:size-3.5\",\n lg: \"h-8 px-3 text-sm [&_[data-slot=tag-icon]]:size-4 [&_[data-slot=tag-remove-icon]]:size-4\",\n sm: \"h-6 px-2 text-xs [&_[data-slot=tag-icon]]:size-3 [&_[data-slot=tag-remove-icon]]:size-3\",\n },\n },\n defaultVariants: {\n size: \"default\",\n variant: \"default\",\n },\n },\n)\n\nexport type TagVariants = VariantProps<typeof tagVariants>\n",
|
|
258
|
+
"type": "registry:ui",
|
|
259
|
+
"target": "components/ui/tag/variants.ts"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"path": "src/components/ui/tag/index.ts",
|
|
263
|
+
"content": "export { default as Tag } from \"./Tag.vue\"\nexport { tagVariants, type TagVariants } from \"./variants\"\n",
|
|
264
|
+
"type": "registry:ui",
|
|
265
|
+
"target": "components/ui/tag/index.ts"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"path": "src/components/ui/prompt-input/PromptInput.vue",
|
|
269
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from \"vue\"\nimport {\n BotIcon,\n BrainCircuitIcon,\n FileIcon,\n PaperclipIcon,\n PlugIcon,\n PlusIcon,\n SendIcon,\n SparklesIcon,\n XIcon,\n} from \"@lucide/vue\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { ModelSelector, type ModelSelectorMode } from \"@/components/ui/model-selector\"\nimport { Tag } from \"@/components/ui/tag\"\nimport { cn } from \"@/lib/utils\"\nimport type {\n PromptInputAddAction,\n PromptInputAttachment,\n PromptInputCommand,\n PromptInputCommandData,\n PromptInputCommandPayload,\n PromptInputCommandRemovePayload,\n PromptInputModel,\n PromptInputSubmitPayload,\n} from \"./types\"\n\ninterface PromptInputProps {\n addActions?: PromptInputAddAction[]\n addLabel?: string\n allowEmpty?: boolean\n attachmentAccept?: string\n attachments?: PromptInputAttachment[]\n class?: HTMLAttributes[\"class\"]\n clearAttachmentsOnSubmit?: boolean\n clearCommandPayloadOnSubmit?: boolean\n commandEmptyText?: string\n commandLabel?: string\n commandMenuClass?: HTMLAttributes[\"class\"]\n commandPayload?: PromptInputCommandData\n commandTagRemoveLabel?: string\n commandTrigger?: string\n commands?: PromptInputCommand[]\n disabled?: boolean\n id?: string\n maxRows?: number | string\n model?: string\n modelDialogClass?: HTMLAttributes[\"class\"]\n modelEmptyText?: string\n modelLabel?: string\n modelMenuClass?: HTMLAttributes[\"class\"]\n modelModalThreshold?: number\n modelSearchPlaceholder?: string\n modelSelectorMode?: ModelSelectorMode\n modelValue?: string\n models?: PromptInputModel[]\n multipleAttachments?: boolean\n placeholder?: string\n removeAttachmentLabel?: string\n rows?: number | string\n showAdd?: boolean\n showCommands?: boolean\n showCommandTags?: boolean\n showModel?: boolean\n showSubmit?: boolean\n submitLabel?: string\n submitOnEnter?: boolean\n textareaClass?: HTMLAttributes[\"class\"]\n textareaLabel?: string\n toolbarClass?: HTMLAttributes[\"class\"]\n}\n\nconst props = withDefaults(defineProps<PromptInputProps>(), {\n addActions: () => [\n {\n icon: PaperclipIcon,\n key: \"attachment\",\n label: \"사진/파일 추가\",\n },\n {\n icon: PlugIcon,\n key: \"mcp\",\n label: \"MCP\",\n },\n ],\n addLabel: \"추가\",\n allowEmpty: false,\n clearAttachmentsOnSubmit: true,\n clearCommandPayloadOnSubmit: true,\n commandEmptyText: \"명령어가 없습니다.\",\n commandLabel: \"명령어\",\n commandTagRemoveLabel: \"{label} 명령어 취소\",\n commandTrigger: \"/\",\n commands: () => [],\n modelEmptyText: \"검색 결과가 없습니다.\",\n modelLabel: \"모델 선택\",\n modelModalThreshold: 8,\n modelSearchPlaceholder: \"모델 검색\",\n modelSelectorMode: \"dialog\",\n modelValue: \"\",\n models: () => [\n {\n icon: SparklesIcon,\n label: \"GPT-5\",\n value: \"gpt-5\",\n },\n {\n icon: BrainCircuitIcon,\n label: \"GPT-5 mini\",\n value: \"gpt-5-mini\",\n },\n {\n icon: BotIcon,\n label: \"Claude Sonnet\",\n value: \"claude-sonnet\",\n },\n {\n icon: BotIcon,\n label: \"Claude Haiku\",\n value: \"claude-haiku\",\n },\n {\n icon: SparklesIcon,\n label: \"Gemini Pro\",\n value: \"gemini-pro\",\n },\n {\n icon: BrainCircuitIcon,\n label: \"Gemini Flash\",\n value: \"gemini-flash\",\n },\n {\n icon: BotIcon,\n label: \"Llama 4\",\n value: \"llama-4\",\n },\n {\n icon: SparklesIcon,\n label: \"Mistral Large\",\n value: \"mistral-large\",\n },\n {\n icon: BrainCircuitIcon,\n label: \"DeepSeek Chat\",\n value: \"deepseek-chat\",\n },\n {\n icon: BotIcon,\n label: \"Command R\",\n value: \"command-r\",\n },\n ],\n multipleAttachments: true,\n placeholder: \"메시지를 입력하세요\",\n removeAttachmentLabel: \"첨부 삭제\",\n maxRows: 4,\n rows: 2,\n showAdd: true,\n showCommands: true,\n showCommandTags: true,\n showModel: true,\n showSubmit: true,\n submitLabel: \"전송\",\n submitOnEnter: true,\n textareaLabel: \"프롬프트 입력\",\n})\n\nconst emit = defineEmits<{\n \"attachments-change\": [attachments: PromptInputAttachment[]]\n \"command-payload-change\": [commandPayload: PromptInputCommandData]\n \"remove-attachment\": [attachment: PromptInputAttachment]\n \"update:attachments\": [attachments: PromptInputAttachment[]]\n \"update:commandPayload\": [commandPayload: PromptInputCommandData]\n \"update:model\": [value: string]\n \"update:modelValue\": [value: string]\n add: [action: PromptInputAddAction]\n \"add:attachment\": [action: PromptInputAddAction]\n \"add:mcp\": [action: PromptInputAddAction]\n command: [payload: PromptInputCommandPayload]\n \"remove-command\": [payload: PromptInputCommandRemovePayload]\n submit: [payload: PromptInputSubmitPayload]\n upload: [attachments: PromptInputAttachment[]]\n}>()\n\ninterface CommandRange {\n end: number\n query: string\n start: number\n}\n\ninterface ActiveCommandEntry {\n command: PromptInputCommand\n payload: PromptInputCommandData\n}\n\nconst generatedId = useId()\nconst commandMenuElement = ref<HTMLElement | null>(null)\nconst commandRange = ref<CommandRange | null>(null)\nconst fileInputElement = ref<HTMLInputElement | null>(null)\nconst highlightedCommandIndex = ref(0)\nconst activeCommandEntries = ref<ActiveCommandEntry[]>([])\nconst internalAttachments = ref<PromptInputAttachment[]>([])\nconst internalCommandPayload = ref<PromptInputCommandData>({})\nconst textareaElement = ref<HTMLTextAreaElement | null>(null)\nconst textareaId = computed(() => props.id ?? generatedId)\nconst promptValue = computed(() => props.modelValue ?? \"\")\nconst resolvedAttachments = computed(() =>\n props.attachments ?? internalAttachments.value,\n)\nconst resolvedCommandPayload = computed(() =>\n props.commandPayload ?? internalCommandPayload.value,\n)\nconst selectedModelValue = computed({\n get: () => props.model ?? props.models[0]?.value ?? \"\",\n set: (value: string) => {\n emit(\"update:model\", value)\n },\n})\nconst selectedModel = computed(() =>\n props.models.find((model) => model.value === selectedModelValue.value)\n ?? props.models[0],\n)\nconst filteredCommands = computed(() => {\n const query = commandRange.value?.query.trim().toLocaleLowerCase() ?? \"\"\n\n if (!query) return props.commands\n\n return props.commands.filter((command) => {\n const searchable = [\n command.label,\n command.value,\n command.description,\n ]\n .filter(Boolean)\n .join(\" \")\n .toLocaleLowerCase()\n\n return searchable.includes(query)\n })\n})\nconst commandMenuVisible = computed(() =>\n props.showCommands\n && Boolean(commandRange.value)\n && props.commands.length > 0,\n)\nconst hasPromptMessage = computed(() => promptValue.value.trim().length > 0)\nconst canSubmit = computed(() =>\n !props.disabled\n && (props.allowEmpty || hasPromptMessage.value),\n)\nconst submitButtonActive = computed(() =>\n !props.disabled\n && (props.allowEmpty || hasPromptMessage.value),\n)\nconst minRows = computed(() => normalizeRowCount(props.rows, 2))\nconst maxRows = computed(() =>\n Math.max(minRows.value, normalizeRowCount(props.maxRows, 4)),\n)\nconst objectUrlSet = new Set<string>()\n\nfunction normalizeRowCount(value: number | string | undefined, fallback: number) {\n const parsed = Number(value)\n\n if (!Number.isFinite(parsed) || parsed < 1) return fallback\n\n return Math.floor(parsed)\n}\n\nfunction resizeTextarea() {\n const textarea = textareaElement.value\n\n if (!textarea || typeof window === \"undefined\") return\n\n const style = window.getComputedStyle(textarea)\n const lineHeight = Number.parseFloat(style.lineHeight) || 24\n const padding =\n (Number.parseFloat(style.paddingTop) || 0)\n + (Number.parseFloat(style.paddingBottom) || 0)\n const border =\n (Number.parseFloat(style.borderTopWidth) || 0)\n + (Number.parseFloat(style.borderBottomWidth) || 0)\n const minHeight = (lineHeight * minRows.value) + padding + border\n const maxHeight = (lineHeight * maxRows.value) + padding + border\n\n textarea.style.height = `${minHeight}px`\n\n const nextHeight = Math.min(\n Math.max(textarea.scrollHeight, minHeight),\n maxHeight,\n )\n\n textarea.style.height = `${nextHeight}px`\n textarea.style.overflowY = textarea.scrollHeight > maxHeight ? \"auto\" : \"hidden\"\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLTextAreaElement\n\n emit(\"update:modelValue\", target.value)\n updateCommandRange(target.value, target.selectionStart ?? target.value.length)\n void nextTick(resizeTextarea)\n}\n\nfunction getCommandRange(value: string, cursor: number): CommandRange | null {\n if (!props.showCommands || !props.commands.length) return null\n\n const trigger = props.commandTrigger\n\n if (!trigger) return null\n\n const beforeCursor = value.slice(0, cursor)\n const start = beforeCursor.lastIndexOf(trigger)\n\n if (start === -1) return null\n\n const previousCharacter = start > 0 ? value[start - 1] : \"\"\n\n if (previousCharacter && !/\\s/.test(previousCharacter)) return null\n\n const query = value.slice(start + trigger.length, cursor)\n\n if (/\\s/.test(query)) return null\n\n return {\n end: cursor,\n query,\n start,\n }\n}\n\nfunction updateCommandRange(value?: string, cursor?: number) {\n const textarea = textareaElement.value\n const nextValue = value ?? textarea?.value ?? promptValue.value\n const nextCursor = cursor ?? textarea?.selectionStart ?? nextValue.length\n\n commandRange.value = getCommandRange(nextValue, nextCursor)\n\n if (!commandRange.value) {\n highlightedCommandIndex.value = 0\n return\n }\n\n highlightedCommandIndex.value = getFirstEnabledCommandIndex(filteredCommands.value)\n}\n\nfunction getFirstEnabledCommandIndex(commands = filteredCommands.value) {\n const enabledIndex = commands.findIndex((command) => !command.disabled)\n\n return enabledIndex === -1 ? 0 : enabledIndex\n}\n\nfunction moveHighlightedCommand(direction: 1 | -1) {\n const commands = filteredCommands.value\n\n if (!commands.length || commands.every((command) => command.disabled)) return\n\n let nextIndex = highlightedCommandIndex.value\n\n for (let offset = 0; offset < commands.length; offset += 1) {\n nextIndex = (nextIndex + direction + commands.length) % commands.length\n\n if (!commands[nextIndex]?.disabled) {\n highlightedCommandIndex.value = nextIndex\n scrollHighlightedCommandIntoView()\n return\n }\n }\n}\n\nfunction scrollHighlightedCommandIntoView() {\n void nextTick(() => {\n commandMenuElement.value\n ?.querySelector<HTMLElement>(\"[data-highlighted]\")\n ?.scrollIntoView({ block: \"nearest\" })\n })\n}\n\nfunction closeCommandMenu() {\n commandRange.value = null\n highlightedCommandIndex.value = 0\n}\n\nfunction emitCommandPayload(nextCommandPayload: PromptInputCommandData) {\n if (props.commandPayload === undefined) {\n internalCommandPayload.value = nextCommandPayload\n }\n\n emit(\"update:commandPayload\", nextCommandPayload)\n emit(\"command-payload-change\", nextCommandPayload)\n}\n\nfunction clearCommandPayload() {\n activeCommandEntries.value = []\n emitCommandPayload({})\n}\n\nfunction setActiveCommand(\n command: PromptInputCommand,\n commandPayloadPatch: PromptInputCommandData,\n) {\n const patchKeys = new Set(Object.keys(commandPayloadPatch))\n const nextEntry = {\n command,\n payload: { ...commandPayloadPatch },\n }\n\n activeCommandEntries.value = [\n ...activeCommandEntries.value.filter((entry) => {\n if (entry.command.value === command.value) return false\n if (!patchKeys.size) return true\n\n return !Object.keys(entry.payload).some((key) => patchKeys.has(key))\n }),\n nextEntry,\n ]\n}\n\nfunction removeCommand(entry: ActiveCommandEntry) {\n if (props.disabled) return\n\n const commandPayload = { ...resolvedCommandPayload.value }\n const nextCommandPayload = { ...commandPayload }\n\n Object.keys(entry.payload).forEach((key) => {\n delete nextCommandPayload[key]\n })\n\n activeCommandEntries.value = activeCommandEntries.value.filter(\n (currentEntry) => currentEntry !== entry,\n )\n\n const payload: PromptInputCommandRemovePayload = {\n command: entry.command,\n commandPayload,\n nextCommandPayload,\n removedPayload: { ...entry.payload },\n }\n\n emitCommandPayload(nextCommandPayload)\n emit(\"remove-command\", payload)\n entry.command.cancel?.(payload)\n focusTextarea()\n}\n\nfunction resolveCommandPayloadPatch(\n command: PromptInputCommand,\n payload: PromptInputCommandPayload,\n) {\n const commandPayloadPatch =\n typeof command.payload === \"function\"\n ? command.payload(payload)\n : command.payload\n\n return { ...(commandPayloadPatch ?? {}) }\n}\n\nfunction resolveCommandTagRemoveLabel(command: PromptInputCommand) {\n return props.commandTagRemoveLabel.replace(\"{label}\", command.label)\n}\n\nfunction selectCommand(command: PromptInputCommand) {\n if (props.disabled || command.disabled || !commandRange.value) return\n\n const textarea = textareaElement.value\n const currentValue = textarea?.value ?? promptValue.value\n const range = commandRange.value\n const insertedValue = command.insert ?? \"\"\n const nextValue =\n currentValue.slice(0, range.start)\n + insertedValue\n + currentValue.slice(range.end)\n const cursor = range.start + insertedValue.length\n const commandPayload = { ...resolvedCommandPayload.value }\n const basePayload: PromptInputCommandPayload = {\n command,\n commandPayload,\n nextCommandPayload: commandPayload,\n nextValue,\n query: range.query,\n trigger: props.commandTrigger,\n value: currentValue,\n }\n const commandPayloadPatch = resolveCommandPayloadPatch(command, basePayload)\n const nextCommandPayload = {\n ...commandPayload,\n ...commandPayloadPatch,\n }\n const payload: PromptInputCommandPayload = {\n ...basePayload,\n nextCommandPayload,\n }\n\n emit(\"update:modelValue\", nextValue)\n setActiveCommand(command, commandPayloadPatch)\n emitCommandPayload(nextCommandPayload)\n emit(\"command\", payload)\n command.action?.(payload)\n closeCommandMenu()\n\n void nextTick(() => {\n focusTextarea()\n textareaElement.value?.setSelectionRange(cursor, cursor)\n resizeTextarea()\n })\n}\n\nfunction selectHighlightedCommand() {\n const command =\n filteredCommands.value[highlightedCommandIndex.value]\n ?? filteredCommands.value[getFirstEnabledCommandIndex()]\n\n if (!command || command.disabled) return\n\n selectCommand(command)\n}\n\nfunction emitAttachments(nextAttachments: PromptInputAttachment[]) {\n if (props.attachments === undefined) {\n internalAttachments.value = nextAttachments\n }\n\n emit(\"update:attachments\", nextAttachments)\n emit(\"attachments-change\", nextAttachments)\n}\n\nfunction createAttachment(file: File): PromptInputAttachment {\n const kind = file.type.startsWith(\"image/\") ? \"image\" : \"file\"\n const previewUrl = kind === \"image\" ? URL.createObjectURL(file) : undefined\n const id =\n globalThis.crypto?.randomUUID?.()\n ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`\n\n if (previewUrl) {\n objectUrlSet.add(previewUrl)\n }\n\n return {\n file,\n id: `${file.name}-${file.size}-${file.lastModified}-${id}`,\n kind,\n name: file.name,\n previewUrl,\n size: file.size,\n type: file.type,\n }\n}\n\nfunction revokeAttachmentPreview(attachment: PromptInputAttachment) {\n if (!attachment.previewUrl || !objectUrlSet.has(attachment.previewUrl)) return\n\n URL.revokeObjectURL(attachment.previewUrl)\n objectUrlSet.delete(attachment.previewUrl)\n}\n\nfunction clearAttachments() {\n resolvedAttachments.value.forEach(revokeAttachmentPreview)\n emitAttachments([])\n}\n\nfunction formatAttachmentSize(size: number) {\n if (size < 1024) return `${size} B`\n if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`\n\n return `${(size / 1024 / 1024).toFixed(1)} MB`\n}\n\nfunction isImageAttachment(attachment: PromptInputAttachment) {\n return attachment.kind === \"image\" && Boolean(attachment.previewUrl)\n}\n\nfunction handleFileInputChange(event: Event) {\n if (props.disabled) return\n\n const input = event.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n\n if (!files.length) return\n\n const uploadedAttachments = files.map(createAttachment)\n\n emitAttachments([\n ...resolvedAttachments.value,\n ...uploadedAttachments,\n ])\n emit(\"upload\", uploadedAttachments)\n\n input.value = \"\"\n}\n\nfunction removeAttachment(attachment: PromptInputAttachment) {\n revokeAttachmentPreview(attachment)\n\n emitAttachments(\n resolvedAttachments.value.filter((item) => item.id !== attachment.id),\n )\n emit(\"remove-attachment\", attachment)\n}\n\nfunction handleAdd(action: PromptInputAddAction) {\n if (props.disabled) return\n\n emit(\"add\", action)\n\n if (action.key === \"attachment\") {\n emit(\"add:attachment\", action)\n fileInputElement.value?.click()\n return\n }\n\n if (action.key === \"mcp\") {\n emit(\"add:mcp\", action)\n }\n}\n\nfunction focusTextarea() {\n if (typeof document === \"undefined\") return\n\n document.getElementById(textareaId.value)?.focus()\n}\n\nfunction handleSurfaceClick(event: MouseEvent) {\n if (props.disabled) return\n\n event.preventDefault()\n focusTextarea()\n}\n\nfunction handleTextareaPointer() {\n void nextTick(() => updateCommandRange())\n}\n\nfunction handleTextareaKeyup(event: KeyboardEvent) {\n if (\n event.key === \"ArrowDown\"\n || event.key === \"ArrowUp\"\n || event.key === \"Enter\"\n || event.key === \"Tab\"\n || event.key === \"Escape\"\n ) {\n return\n }\n\n updateCommandRange()\n}\n\nfunction handleSubmit() {\n if (!canSubmit.value) return\n\n const attachments = [...resolvedAttachments.value]\n const commandPayload = { ...resolvedCommandPayload.value }\n\n emit(\"submit\", {\n attachments,\n commandPayload,\n model: selectedModelValue.value,\n modelOption: selectedModel.value,\n value: promptValue.value,\n })\n\n if (props.clearAttachmentsOnSubmit && attachments.length) {\n clearAttachments()\n }\n\n if (\n props.clearCommandPayloadOnSubmit\n && Object.keys(commandPayload).length\n ) {\n clearCommandPayload()\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (commandMenuVisible.value) {\n if (event.key === \"ArrowDown\") {\n event.preventDefault()\n moveHighlightedCommand(1)\n return\n }\n\n if (event.key === \"ArrowUp\") {\n event.preventDefault()\n moveHighlightedCommand(-1)\n return\n }\n\n if (event.key === \"Escape\") {\n event.preventDefault()\n closeCommandMenu()\n return\n }\n\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n event.preventDefault()\n selectHighlightedCommand()\n return\n }\n }\n\n if (\n !props.submitOnEnter\n || event.isComposing\n || event.key !== \"Enter\"\n || event.shiftKey\n || event.altKey\n || event.ctrlKey\n || event.metaKey\n ) {\n return\n }\n\n event.preventDefault()\n handleSubmit()\n}\n\nonMounted(() => {\n void nextTick(resizeTextarea)\n})\n\nonBeforeUnmount(() => {\n resolvedAttachments.value.forEach(revokeAttachmentPreview)\n})\n\nwatch(\n [promptValue, minRows, maxRows],\n () => {\n void nextTick(resizeTextarea)\n },\n { flush: \"post\" },\n)\n\nwatch(\n filteredCommands,\n (commands) => {\n if (!commandMenuVisible.value) return\n\n const currentCommand = commands[highlightedCommandIndex.value]\n\n if (currentCommand && !currentCommand.disabled) return\n\n highlightedCommandIndex.value = getFirstEnabledCommandIndex(commands)\n },\n)\n\nwatch(\n () => props.attachments,\n (nextAttachments = [], previousAttachments = []) => {\n const nextPreviewUrls = new Set(\n nextAttachments\n .map((attachment) => attachment.previewUrl)\n .filter(Boolean),\n )\n\n previousAttachments.forEach((attachment) => {\n if (\n attachment.previewUrl\n && !nextPreviewUrls.has(attachment.previewUrl)\n ) {\n revokeAttachmentPreview(attachment)\n }\n })\n },\n)\n\nwatch(\n resolvedCommandPayload,\n (nextCommandPayload) => {\n if (Object.keys(nextCommandPayload).length > 0) return\n\n activeCommandEntries.value = []\n },\n { deep: true },\n)\n</script>\n\n<template>\n <form\n data-slot=\"prompt-input\"\n :data-disabled=\"props.disabled ? '' : undefined\"\n :class=\"\n cn(\n 'border-input bg-background text-foreground relative flex w-full max-w-3xl flex-col overflow-visible rounded-lg border data-[disabled]:opacity-60',\n props.class,\n )\n \"\n @submit.prevent=\"handleSubmit\"\n >\n <input\n ref=\"fileInputElement\"\n data-slot=\"prompt-input-file-input\"\n class=\"sr-only\"\n type=\"file\"\n :accept=\"props.attachmentAccept\"\n :multiple=\"props.multipleAttachments\"\n :disabled=\"props.disabled\"\n @change=\"handleFileInputChange\"\n >\n\n <div\n v-if=\"resolvedAttachments.length\"\n data-slot=\"prompt-input-attachments\"\n class=\"flex flex-nowrap gap-2 overflow-x-auto overflow-y-hidden px-3 pt-3 pb-1\"\n @click=\"focusTextarea\"\n >\n <div\n v-for=\"attachment in resolvedAttachments\"\n :key=\"attachment.id\"\n data-slot=\"prompt-input-attachment\"\n class=\"bg-muted/70 text-foreground flex w-56 max-w-[calc(100vw-3rem)] shrink-0 items-center gap-2 overflow-hidden rounded-md px-2 py-1.5 text-xs\"\n >\n <img\n v-if=\"isImageAttachment(attachment)\"\n data-slot=\"prompt-input-attachment-preview\"\n :src=\"attachment.previewUrl\"\n :alt=\"attachment.name\"\n class=\"size-8 shrink-0 rounded-sm object-cover\"\n >\n <span\n v-else\n data-slot=\"prompt-input-attachment-icon\"\n class=\"bg-background text-muted-foreground flex size-8 shrink-0 items-center justify-center rounded-sm\"\n >\n <FileIcon />\n </span>\n <span\n data-slot=\"prompt-input-attachment-content\"\n class=\"grid min-w-0 flex-1\"\n >\n <span\n data-slot=\"prompt-input-attachment-name\"\n class=\"truncate font-medium\"\n :title=\"attachment.name\"\n >\n {{ attachment.name }}\n </span>\n <span\n data-slot=\"prompt-input-attachment-size\"\n class=\"text-muted-foreground\"\n >\n {{ formatAttachmentSize(attachment.size) }}\n </span>\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n class=\"-mr-1 size-7 rounded-sm\"\n :aria-label=\"props.removeAttachmentLabel\"\n :disabled=\"props.disabled\"\n @click.stop=\"removeAttachment(attachment)\"\n >\n <XIcon />\n </Button>\n </div>\n </div>\n\n <textarea\n ref=\"textareaElement\"\n :id=\"textareaId\"\n data-slot=\"prompt-input-textarea\"\n :class=\"\n cn(\n 'w-full resize-none border-0 bg-transparent px-4 py-3 text-sm leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed',\n props.textareaClass,\n )\n \"\n :disabled=\"props.disabled\"\n :aria-label=\"props.textareaLabel\"\n :placeholder=\"props.placeholder\"\n :rows=\"minRows\"\n :value=\"promptValue\"\n @click=\"handleTextareaPointer\"\n @focus=\"handleTextareaPointer\"\n @input=\"handleInput\"\n @keydown=\"handleKeydown\"\n @keyup=\"handleTextareaKeyup\"\n @select=\"handleTextareaPointer\"\n />\n\n <div\n v-if=\"commandMenuVisible\"\n ref=\"commandMenuElement\"\n data-slot=\"prompt-input-command-menu\"\n role=\"listbox\"\n :aria-label=\"props.commandLabel\"\n :class=\"\n cn(\n 'absolute bottom-[calc(100%+0.5rem)] left-0 z-[999] flex max-h-56 w-full flex-col overflow-y-auto rounded-[12px] border border-border bg-popover/90 p-1 text-popover-foreground shadow-lg backdrop-blur-[15px] [-webkit-backdrop-filter:blur(15px)] [&_[data-slot=prompt-input-command-icon]]:size-3.5',\n props.commandMenuClass,\n )\n \"\n @mousedown.stop\n >\n <button\n v-for=\"(command, commandIndex) in filteredCommands\"\n :key=\"command.value\"\n type=\"button\"\n data-slot=\"prompt-input-command-item\"\n :data-highlighted=\"commandIndex === highlightedCommandIndex ? '' : undefined\"\n :disabled=\"command.disabled\"\n role=\"option\"\n :aria-selected=\"commandIndex === highlightedCommandIndex\"\n :class=\"\n cn(\n 'flex w-full min-w-0 items-center gap-1.5 rounded-[8px] px-2 py-1.5 text-left text-xs outline-none transition-[background-color,color] hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground',\n )\n \"\n @click=\"selectCommand(command)\"\n @mouseenter=\"highlightedCommandIndex = commandIndex\"\n @mousedown.prevent\n >\n <component\n :is=\"command.icon\"\n v-if=\"command.icon\"\n data-slot=\"prompt-input-command-icon\"\n />\n <span\n data-slot=\"prompt-input-command-content\"\n class=\"grid min-w-0 flex-1 grid-flow-col grid-cols-[minmax(0,max-content)_minmax(0,1fr)] items-baseline gap-1.5\"\n >\n <span\n data-slot=\"prompt-input-command-label\"\n class=\"min-w-0 truncate font-medium\"\n >\n {{ command.label }}\n </span>\n <span\n v-if=\"command.description\"\n data-slot=\"prompt-input-command-description\"\n class=\"min-w-0 truncate text-[11px] leading-4 text-muted-foreground max-[520px]:hidden\"\n >\n {{ command.description }}\n </span>\n </span>\n <span\n data-slot=\"prompt-input-command-value\"\n class=\"shrink-0 text-[11px] leading-4 text-muted-foreground\"\n >\n {{ props.commandTrigger }}{{ command.value }}\n </span>\n </button>\n\n <div\n v-if=\"!filteredCommands.length\"\n data-slot=\"prompt-input-command-empty\"\n class=\"px-2 py-2.5 text-xs text-muted-foreground\"\n >\n {{ props.commandEmptyText }}\n </div>\n </div>\n\n <div\n data-slot=\"prompt-input-toolbar\"\n :class=\"\n cn(\n 'relative flex min-h-12 items-center justify-between gap-3 px-2 py-2',\n props.toolbarClass,\n )\n \"\n >\n <label\n data-slot=\"prompt-input-surface\"\n :for=\"textareaId\"\n class=\"absolute inset-0 z-10 cursor-text\"\n aria-hidden=\"true\"\n @click=\"handleSurfaceClick\"\n />\n\n <div\n data-slot=\"prompt-input-tools\"\n class=\"pointer-events-none relative z-20 flex min-w-0 flex-1 flex-wrap items-center gap-1 [&_*]:pointer-events-auto\"\n >\n <DropdownMenu v-if=\"props.showAdd && props.addActions.length\">\n <DropdownMenuTrigger as-child>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n :aria-label=\"props.addLabel\"\n :disabled=\"props.disabled\"\n >\n <PlusIcon />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" class=\"w-40\">\n <DropdownMenuItem\n v-for=\"action in props.addActions\"\n :key=\"action.key\"\n :disabled=\"action.disabled\"\n @select=\"handleAdd(action)\"\n >\n <component\n :is=\"action.icon\"\n v-if=\"action.icon\"\n data-slot=\"prompt-input-add-action-icon\"\n />\n {{ action.label }}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n\n <div\n v-if=\"props.showCommandTags && activeCommandEntries.length\"\n data-slot=\"prompt-input-command-tags\"\n class=\"flex min-w-0 flex-wrap items-center gap-1\"\n >\n <Tag\n v-for=\"entry in activeCommandEntries\"\n :key=\"entry.command.value\"\n :icon=\"entry.command.icon\"\n :remove-label=\"resolveCommandTagRemoveLabel(entry.command)\"\n :title=\"entry.command.label\"\n class=\"max-w-36\"\n removable\n size=\"sm\"\n variant=\"secondary\"\n @remove=\"removeCommand(entry)\"\n >\n {{ props.commandTrigger }}{{ entry.command.value }}\n </Tag>\n </div>\n\n <ModelSelector\n v-if=\"props.showModel && props.models.length\"\n v-model=\"selectedModelValue\"\n :disabled=\"props.disabled\"\n :content-class=\"cn('w-72', props.modelMenuClass)\"\n :dialog-class=\"props.modelDialogClass\"\n :empty-text=\"props.modelEmptyText\"\n :label=\"props.modelLabel\"\n :modal-threshold=\"props.modelModalThreshold\"\n :modal-title=\"props.modelLabel\"\n :mode=\"props.modelSelectorMode\"\n :models=\"props.models\"\n :placeholder=\"props.modelLabel\"\n :search-placeholder=\"props.modelSearchPlaceholder\"\n class=\"min-w-0\"\n size=\"sm\"\n trigger-class=\"h-8 min-w-0 max-w-52 border-transparent bg-transparent px-2 shadow-none transition-[background-color,color] hover:bg-accent hover:text-accent-foreground focus-visible:border-transparent focus-visible:bg-accent focus-visible:ring-0 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground\"\n />\n\n <slot\n name=\"tools\"\n :attachments=\"resolvedAttachments\"\n :command-payload=\"resolvedCommandPayload\"\n :disabled=\"props.disabled\"\n :model=\"selectedModelValue\"\n :model-option=\"selectedModel\"\n :value=\"promptValue\"\n />\n </div>\n\n <div\n data-slot=\"prompt-input-actions\"\n class=\"pointer-events-none relative z-20 flex shrink-0 items-center gap-1 [&_*]:pointer-events-auto\"\n >\n <slot\n name=\"actions\"\n :attachments=\"resolvedAttachments\"\n :command-payload=\"resolvedCommandPayload\"\n :disabled=\"props.disabled\"\n :model=\"selectedModelValue\"\n :model-option=\"selectedModel\"\n :submit=\"handleSubmit\"\n :value=\"promptValue\"\n />\n\n <Button\n v-if=\"props.showSubmit\"\n type=\"submit\"\n :variant=\"submitButtonActive ? 'default' : 'secondary'\"\n size=\"icon-sm\"\n :class=\"\n cn(\n 'transition-[background-color,color,opacity]',\n !submitButtonActive && 'bg-muted text-muted-foreground hover:bg-muted',\n )\n \"\n :aria-label=\"props.submitLabel\"\n :disabled=\"!canSubmit\"\n >\n <SendIcon />\n </Button>\n </div>\n </div>\n </form>\n</template>\n",
|
|
270
|
+
"type": "registry:ui",
|
|
271
|
+
"target": "components/ui/prompt-input/PromptInput.vue"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"path": "src/components/ui/prompt-input/types.ts",
|
|
275
|
+
"content": "import type { Component } from \"vue\"\n\nexport type PromptInputAddActionKey = \"attachment\" | \"mcp\" | (string & {})\n\nexport interface PromptInputAddAction {\n disabled?: boolean\n icon?: Component\n key: PromptInputAddActionKey\n label: string\n}\n\nexport type PromptInputAttachmentKind = \"file\" | \"image\"\n\nexport interface PromptInputAttachment {\n file: File\n id: string\n kind: PromptInputAttachmentKind\n name: string\n previewUrl?: string\n size: number\n type: string\n}\n\nexport type PromptInputCommandDataValue =\n | boolean\n | null\n | number\n | string\n | PromptInputCommandDataValue[]\n | { [key: string]: PromptInputCommandDataValue }\n\nexport type PromptInputCommandData = Record<string, PromptInputCommandDataValue>\n\nexport interface PromptInputCommand {\n action?: (payload: PromptInputCommandPayload) => void\n cancel?: (payload: PromptInputCommandRemovePayload) => void\n description?: string\n disabled?: boolean\n icon?: Component\n insert?: string\n label: string\n payload?:\n | PromptInputCommandData\n | ((payload: PromptInputCommandPayload) => PromptInputCommandData)\n value: string\n}\n\nexport interface PromptInputCommandPayload {\n command: PromptInputCommand\n commandPayload: PromptInputCommandData\n nextCommandPayload: PromptInputCommandData\n nextValue: string\n query: string\n trigger: string\n value: string\n}\n\nexport interface PromptInputCommandRemovePayload {\n command: PromptInputCommand\n commandPayload: PromptInputCommandData\n nextCommandPayload: PromptInputCommandData\n removedPayload: PromptInputCommandData\n}\n\nexport interface PromptInputModel {\n disabled?: boolean\n icon?: Component\n label: string\n value: string\n}\n\nexport interface PromptInputSubmitPayload {\n attachments: PromptInputAttachment[]\n commandPayload: PromptInputCommandData\n model: string\n modelOption?: PromptInputModel\n value: string\n}\n",
|
|
276
|
+
"type": "registry:ui",
|
|
277
|
+
"target": "components/ui/prompt-input/types.ts"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"path": "src/components/ui/prompt-input/index.ts",
|
|
281
|
+
"content": "export { default as PromptInput } from \"./PromptInput.vue\"\nexport type {\n PromptInputAddAction,\n PromptInputAddActionKey,\n PromptInputAttachment,\n PromptInputAttachmentKind,\n PromptInputCommand,\n PromptInputCommandData,\n PromptInputCommandDataValue,\n PromptInputCommandPayload,\n PromptInputCommandRemovePayload,\n PromptInputModel,\n PromptInputSubmitPayload,\n} from \"./types\"\n",
|
|
282
|
+
"type": "registry:ui",
|
|
283
|
+
"target": "components/ui/prompt-input/index.ts"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"path": "src/lib/utils.ts",
|
|
287
|
+
"content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
288
|
+
"type": "registry:lib",
|
|
289
|
+
"target": "lib/utils.ts"
|
|
290
|
+
}
|
|
291
|
+
],
|
|
292
|
+
"type": "registry:ui"
|
|
293
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-vue.com/schema/registry-item.json",
|
|
3
|
+
"name": "radio-group",
|
|
4
|
+
"title": "Radio Group",
|
|
5
|
+
"description": "A radio group control for single selection with labels, descriptions, horizontal layout, and disabled options.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"@vueuse/core",
|
|
8
|
+
"clsx",
|
|
9
|
+
"reka-ui",
|
|
10
|
+
"tailwind-merge"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "src/components/ui/radio-group/RadioGroup.vue",
|
|
15
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { RadioGroupRootEmits, RadioGroupRootProps } from \"reka-ui\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { RadioGroupRoot, useForwardPropsEmits } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(\n defineProps<RadioGroupRootProps & { class?: HTMLAttributes[\"class\"] }>(),\n {\n orientation: \"vertical\",\n },\n)\nconst emits = defineEmits<RadioGroupRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <RadioGroupRoot\n v-slot=\"slotProps\"\n data-slot=\"radio-group\"\n v-bind=\"forwarded\"\n :class=\"\n cn(\n props.orientation === 'horizontal'\n ? 'flex flex-wrap items-center gap-4'\n : 'grid gap-3',\n props.class,\n )\n \"\n >\n <slot v-bind=\"slotProps\" />\n </RadioGroupRoot>\n</template>\n",
|
|
16
|
+
"type": "registry:ui",
|
|
17
|
+
"target": "components/ui/radio-group/RadioGroup.vue"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "src/components/ui/radio-group/RadioGroupItem.vue",
|
|
21
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { RadioGroupItemEmits, RadioGroupItemProps } from \"reka-ui\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport {\n RadioGroupIndicator,\n RadioGroupItem,\n useForwardPropsEmits,\n} from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<\n RadioGroupItemProps & {\n class?: HTMLAttributes[\"class\"]\n indicatorClass?: HTMLAttributes[\"class\"]\n }\n>()\nconst emits = defineEmits<RadioGroupItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, \"class\", \"indicatorClass\")\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <RadioGroupItem\n data-slot=\"radio-group-item\"\n v-bind=\"forwarded\"\n :class=\"\n cn(\n 'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 relative flex size-4 shrink-0 items-center justify-center rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',\n props.class,\n )\n \"\n >\n <RadioGroupIndicator\n data-slot=\"radio-group-indicator\"\n :class=\"\n cn(\n 'flex size-full items-center justify-center after:block after:size-2 after:rounded-full after:bg-current',\n props.indicatorClass,\n )\n \"\n />\n </RadioGroupItem>\n</template>\n",
|
|
22
|
+
"type": "registry:ui",
|
|
23
|
+
"target": "components/ui/radio-group/RadioGroupItem.vue"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"path": "src/components/ui/radio-group/RadioGroupOption.vue",
|
|
27
|
+
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { RadioGroupItemProps } from \"reka-ui\"\nimport { computed, useId } from \"vue\"\nimport { reactiveOmit } from \"@vueuse/core\"\nimport { cn } from \"@/lib/utils\"\nimport RadioGroupItem from \"./RadioGroupItem.vue\"\n\nconst props = defineProps<\n RadioGroupItemProps & {\n class?: HTMLAttributes[\"class\"]\n description?: string\n descriptionClass?: HTMLAttributes[\"class\"]\n indicatorClass?: HTMLAttributes[\"class\"]\n itemClass?: HTMLAttributes[\"class\"]\n label?: string\n labelClass?: HTMLAttributes[\"class\"]\n }\n>()\n\nconst generatedId = useId()\nconst optionId = computed(() => props.id ?? generatedId)\nconst delegatedProps = reactiveOmit(\n props,\n \"class\",\n \"description\",\n \"descriptionClass\",\n \"id\",\n \"indicatorClass\",\n \"itemClass\",\n \"label\",\n \"labelClass\",\n)\n</script>\n\n<template>\n <div\n data-slot=\"radio-group-option\"\n :data-disabled=\"props.disabled ? '' : undefined\"\n :class=\"\n cn(\n 'flex items-start gap-3',\n props.disabled && 'opacity-50',\n props.class,\n )\n \"\n >\n <RadioGroupItem\n v-bind=\"delegatedProps\"\n :id=\"optionId\"\n :class=\"props.itemClass\"\n :indicator-class=\"props.indicatorClass\"\n />\n\n <div class=\"grid gap-1 leading-none\">\n <label\n v-if=\"props.label || $slots.default\"\n data-slot=\"radio-group-label\"\n :for=\"optionId\"\n :class=\"\n cn(\n 'text-sm font-medium leading-none text-foreground',\n props.disabled && 'cursor-not-allowed',\n props.labelClass,\n )\n \"\n >\n <slot>{{ props.label }}</slot>\n </label>\n\n <p\n v-if=\"props.description\"\n data-slot=\"radio-group-description\"\n :class=\"cn('text-xs leading-relaxed text-muted-foreground', props.descriptionClass)\"\n >\n {{ props.description }}\n </p>\n </div>\n </div>\n</template>\n",
|
|
28
|
+
"type": "registry:ui",
|
|
29
|
+
"target": "components/ui/radio-group/RadioGroupOption.vue"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": "src/components/ui/radio-group/index.ts",
|
|
33
|
+
"content": "export { default as RadioGroup } from \"./RadioGroup.vue\"\nexport { default as RadioGroupItem } from \"./RadioGroupItem.vue\"\nexport { default as RadioGroupOption } from \"./RadioGroupOption.vue\"\n",
|
|
34
|
+
"type": "registry:ui",
|
|
35
|
+
"target": "components/ui/radio-group/index.ts"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"path": "src/lib/utils.ts",
|
|
39
|
+
"content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
40
|
+
"type": "registry:lib",
|
|
41
|
+
"target": "lib/utils.ts"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"type": "registry:ui"
|
|
45
|
+
}
|