@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.
Files changed (472) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +148 -0
  3. package/components.json +20 -0
  4. package/dist/codecenter-ui.cjs +10 -0
  5. package/dist/codecenter-ui.js +17995 -0
  6. package/dist/components/ui/accordion/Accordion.vue.d.ts +28 -0
  7. package/dist/components/ui/accordion/AccordionContent.vue.d.ts +22 -0
  8. package/dist/components/ui/accordion/AccordionItem.vue.d.ts +24 -0
  9. package/dist/components/ui/accordion/AccordionTrigger.vue.d.ts +22 -0
  10. package/dist/components/ui/accordion/index.d.ts +4 -0
  11. package/dist/components/ui/alert/Alert.vue.d.ts +32 -0
  12. package/dist/components/ui/alert/AlertDescription.vue.d.ts +24 -0
  13. package/dist/components/ui/alert/AlertTitle.vue.d.ts +24 -0
  14. package/dist/components/ui/alert/index.d.ts +4 -0
  15. package/dist/components/ui/alert/variants.d.ts +5 -0
  16. package/dist/components/ui/button/Button.vue.d.ts +27 -0
  17. package/dist/components/ui/button/index.d.ts +2 -0
  18. package/dist/components/ui/button/variants.d.ts +6 -0
  19. package/dist/components/ui/button-group/ButtonGroup.vue.d.ts +25 -0
  20. package/dist/components/ui/button-group/index.d.ts +2 -0
  21. package/dist/components/ui/card/Card.vue.d.ts +21 -0
  22. package/dist/components/ui/card/CardContent.vue.d.ts +21 -0
  23. package/dist/components/ui/card/CardDescription.vue.d.ts +21 -0
  24. package/dist/components/ui/card/CardFooter.vue.d.ts +21 -0
  25. package/dist/components/ui/card/CardHeader.vue.d.ts +21 -0
  26. package/dist/components/ui/card/CardTitle.vue.d.ts +21 -0
  27. package/dist/components/ui/card/index.d.ts +6 -0
  28. package/dist/components/ui/chart/Chart.vue.d.ts +92 -0
  29. package/dist/components/ui/chart/index.d.ts +2 -0
  30. package/dist/components/ui/chat/Chat.vue.d.ts +190 -0
  31. package/dist/components/ui/chat/ChatAttachments.vue.d.ts +11 -0
  32. package/dist/components/ui/chat/ChatCodeBlock.vue.d.ts +16 -0
  33. package/dist/components/ui/chat/code-block.d.ts +27 -0
  34. package/dist/components/ui/chat/index.d.ts +6 -0
  35. package/dist/components/ui/chat/types.d.ts +15 -0
  36. package/dist/components/ui/checkbox/Checkbox.vue.d.ts +29 -0
  37. package/dist/components/ui/checkbox/index.d.ts +1 -0
  38. package/dist/components/ui/commit/Commit.vue.d.ts +62 -0
  39. package/dist/components/ui/commit/index.d.ts +2 -0
  40. package/dist/components/ui/contribution-graph/ContributionGraph.vue.d.ts +87 -0
  41. package/dist/components/ui/contribution-graph/index.d.ts +2 -0
  42. package/dist/components/ui/data-table/DataTable.vue.d.ts +109 -0
  43. package/dist/components/ui/data-table/index.d.ts +2 -0
  44. package/dist/components/ui/date-picker/DatePicker.vue.d.ts +37 -0
  45. package/dist/components/ui/date-picker/index.d.ts +2 -0
  46. package/dist/components/ui/dialog/Dialog.vue.d.ts +25 -0
  47. package/dist/components/ui/dialog/DialogClose.vue.d.ts +18 -0
  48. package/dist/components/ui/dialog/DialogContent.vue.d.ts +39 -0
  49. package/dist/components/ui/dialog/DialogDescription.vue.d.ts +22 -0
  50. package/dist/components/ui/dialog/DialogFooter.vue.d.ts +21 -0
  51. package/dist/components/ui/dialog/DialogHeader.vue.d.ts +21 -0
  52. package/dist/components/ui/dialog/DialogOverlay.vue.d.ts +22 -0
  53. package/dist/components/ui/dialog/DialogScrollContent.vue.d.ts +36 -0
  54. package/dist/components/ui/dialog/DialogTitle.vue.d.ts +22 -0
  55. package/dist/components/ui/dialog/DialogTrigger.vue.d.ts +18 -0
  56. package/dist/components/ui/dialog/index.d.ts +10 -0
  57. package/dist/components/ui/diff/DiffTool.vue.d.ts +21 -0
  58. package/dist/components/ui/diff/diff-parser.d.ts +30 -0
  59. package/dist/components/ui/diff/diff-tool.d.ts +36 -0
  60. package/dist/components/ui/diff/index.d.ts +2 -0
  61. package/dist/components/ui/dropdown-menu/DropdownMenu.vue.d.ts +24 -0
  62. package/dist/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +29 -0
  63. package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts +36 -0
  64. package/dist/components/ui/dropdown-menu/DropdownMenuGroup.vue.d.ts +18 -0
  65. package/dist/components/ui/dropdown-menu/DropdownMenuItem.vue.d.ts +26 -0
  66. package/dist/components/ui/dropdown-menu/DropdownMenuLabel.vue.d.ts +23 -0
  67. package/dist/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts +22 -0
  68. package/dist/components/ui/dropdown-menu/DropdownMenuRadioItem.vue.d.ts +27 -0
  69. package/dist/components/ui/dropdown-menu/DropdownMenuSeparator.vue.d.ts +7 -0
  70. package/dist/components/ui/dropdown-menu/DropdownMenuShortcut.vue.d.ts +21 -0
  71. package/dist/components/ui/dropdown-menu/DropdownMenuSub.vue.d.ts +22 -0
  72. package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +38 -0
  73. package/dist/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +23 -0
  74. package/dist/components/ui/dropdown-menu/DropdownMenuTrigger.vue.d.ts +18 -0
  75. package/dist/components/ui/dropdown-menu/index.d.ts +15 -0
  76. package/dist/components/ui/gauge/Gauge.vue.d.ts +62 -0
  77. package/dist/components/ui/gauge/index.d.ts +2 -0
  78. package/dist/components/ui/git-graph/GitGraph.vue.d.ts +59 -0
  79. package/dist/components/ui/git-graph/index.d.ts +2 -0
  80. package/dist/components/ui/incident-timeline/IncidentTimeline.vue.d.ts +50 -0
  81. package/dist/components/ui/incident-timeline/index.d.ts +2 -0
  82. package/dist/components/ui/input/Input.vue.d.ts +14 -0
  83. package/dist/components/ui/input/InputControl.vue.d.ts +14 -0
  84. package/dist/components/ui/input/InputFieldGroup.vue.d.ts +21 -0
  85. package/dist/components/ui/input/index.d.ts +4 -0
  86. package/dist/components/ui/input/types.d.ts +31 -0
  87. package/dist/components/ui/kpi-card/KpiCard.vue.d.ts +46 -0
  88. package/dist/components/ui/kpi-card/index.d.ts +2 -0
  89. package/dist/components/ui/kpi-line-card/KpiLineCard.vue.d.ts +66 -0
  90. package/dist/components/ui/kpi-line-card/index.d.ts +2 -0
  91. package/dist/components/ui/model-selector/ModelSelector.vue.d.ts +41 -0
  92. package/dist/components/ui/model-selector/index.d.ts +2 -0
  93. package/dist/components/ui/model-selector/types.d.ts +12 -0
  94. package/dist/components/ui/network-graph/NetworkGraph.vue.d.ts +75 -0
  95. package/dist/components/ui/network-graph/index.d.ts +2 -0
  96. package/dist/components/ui/pagination/Pagination.vue.d.ts +29 -0
  97. package/dist/components/ui/pagination/PaginationContent.vue.d.ts +29 -0
  98. package/dist/components/ui/pagination/PaginationEllipsis.vue.d.ts +22 -0
  99. package/dist/components/ui/pagination/PaginationFirst.vue.d.ts +26 -0
  100. package/dist/components/ui/pagination/PaginationItem.vue.d.ts +28 -0
  101. package/dist/components/ui/pagination/PaginationLast.vue.d.ts +26 -0
  102. package/dist/components/ui/pagination/PaginationNext.vue.d.ts +26 -0
  103. package/dist/components/ui/pagination/PaginationPrevious.vue.d.ts +26 -0
  104. package/dist/components/ui/pagination/index.d.ts +8 -0
  105. package/dist/components/ui/profile/Profile.vue.d.ts +30 -0
  106. package/dist/components/ui/profile/ProfileGroup.vue.d.ts +37 -0
  107. package/dist/components/ui/profile/index.d.ts +4 -0
  108. package/dist/components/ui/progress/Progress.vue.d.ts +36 -0
  109. package/dist/components/ui/progress/index.d.ts +2 -0
  110. package/dist/components/ui/prompt-input/PromptInput.vue.d.ts +150 -0
  111. package/dist/components/ui/prompt-input/index.d.ts +2 -0
  112. package/dist/components/ui/prompt-input/types.d.ts +61 -0
  113. package/dist/components/ui/radio-group/RadioGroup.vue.d.ts +30 -0
  114. package/dist/components/ui/radio-group/RadioGroupItem.vue.d.ts +12 -0
  115. package/dist/components/ui/radio-group/RadioGroupOption.vue.d.ts +28 -0
  116. package/dist/components/ui/radio-group/index.d.ts +3 -0
  117. package/dist/components/ui/reasoning/Reasoning.vue.d.ts +40 -0
  118. package/dist/components/ui/reasoning/index.d.ts +2 -0
  119. package/dist/components/ui/reasoning/types.d.ts +26 -0
  120. package/dist/components/ui/select/Select.vue.d.ts +28 -0
  121. package/dist/components/ui/select/SelectContent.vue.d.ts +46 -0
  122. package/dist/components/ui/select/SelectGroup.vue.d.ts +18 -0
  123. package/dist/components/ui/select/SelectItem.vue.d.ts +26 -0
  124. package/dist/components/ui/select/SelectItemText.vue.d.ts +18 -0
  125. package/dist/components/ui/select/SelectLabel.vue.d.ts +22 -0
  126. package/dist/components/ui/select/SelectScrollDownButton.vue.d.ts +22 -0
  127. package/dist/components/ui/select/SelectScrollUpButton.vue.d.ts +22 -0
  128. package/dist/components/ui/select/SelectSeparator.vue.d.ts +7 -0
  129. package/dist/components/ui/select/SelectTrigger.vue.d.ts +25 -0
  130. package/dist/components/ui/select/SelectValue.vue.d.ts +18 -0
  131. package/dist/components/ui/select/index.d.ts +11 -0
  132. package/dist/components/ui/select/search.d.ts +18 -0
  133. package/dist/components/ui/separator/Separator.vue.d.ts +6 -0
  134. package/dist/components/ui/separator/index.d.ts +2 -0
  135. package/dist/components/ui/separator/types.d.ts +7 -0
  136. package/dist/components/ui/shimmer/Shimmer.vue.d.ts +37 -0
  137. package/dist/components/ui/shimmer/index.d.ts +2 -0
  138. package/dist/components/ui/sidebar/Sidebar.vue.d.ts +24 -0
  139. package/dist/components/ui/sidebar/SidebarContent.vue.d.ts +21 -0
  140. package/dist/components/ui/sidebar/SidebarFooter.vue.d.ts +21 -0
  141. package/dist/components/ui/sidebar/SidebarGroup.vue.d.ts +21 -0
  142. package/dist/components/ui/sidebar/SidebarGroupAction.vue.d.ts +24 -0
  143. package/dist/components/ui/sidebar/SidebarGroupContent.vue.d.ts +21 -0
  144. package/dist/components/ui/sidebar/SidebarGroupLabel.vue.d.ts +24 -0
  145. package/dist/components/ui/sidebar/SidebarHeader.vue.d.ts +21 -0
  146. package/dist/components/ui/sidebar/SidebarInput.vue.d.ts +6 -0
  147. package/dist/components/ui/sidebar/SidebarInset.vue.d.ts +21 -0
  148. package/dist/components/ui/sidebar/SidebarMenu.vue.d.ts +21 -0
  149. package/dist/components/ui/sidebar/SidebarMenuAction.vue.d.ts +25 -0
  150. package/dist/components/ui/sidebar/SidebarMenuBadge.vue.d.ts +21 -0
  151. package/dist/components/ui/sidebar/SidebarMenuButton.vue.d.ts +25 -0
  152. package/dist/components/ui/sidebar/SidebarMenuButtonChild.vue.d.ts +30 -0
  153. package/dist/components/ui/sidebar/SidebarMenuItem.vue.d.ts +21 -0
  154. package/dist/components/ui/sidebar/SidebarMenuSub.vue.d.ts +21 -0
  155. package/dist/components/ui/sidebar/SidebarMenuSubButton.vue.d.ts +27 -0
  156. package/dist/components/ui/sidebar/SidebarMenuSubItem.vue.d.ts +21 -0
  157. package/dist/components/ui/sidebar/SidebarProvider.vue.d.ts +36 -0
  158. package/dist/components/ui/sidebar/SidebarRail.vue.d.ts +21 -0
  159. package/dist/components/ui/sidebar/SidebarSeparator.vue.d.ts +6 -0
  160. package/dist/components/ui/sidebar/SidebarTrigger.vue.d.ts +6 -0
  161. package/dist/components/ui/sidebar/context.d.ts +19 -0
  162. package/dist/components/ui/sidebar/index.d.ts +26 -0
  163. package/dist/components/ui/sidebar/types.d.ts +11 -0
  164. package/dist/components/ui/sidebar/variants.d.ts +6 -0
  165. package/dist/components/ui/skeleton/Skeleton.vue.d.ts +18 -0
  166. package/dist/components/ui/skeleton/index.d.ts +2 -0
  167. package/dist/components/ui/sonner/Sonner.vue.d.ts +5 -0
  168. package/dist/components/ui/sonner/index.d.ts +2 -0
  169. package/dist/components/ui/spinner/Spinner.vue.d.ts +11 -0
  170. package/dist/components/ui/spinner/index.d.ts +1 -0
  171. package/dist/components/ui/stepper/Stepper.vue.d.ts +38 -0
  172. package/dist/components/ui/stepper/StepperDescription.vue.d.ts +22 -0
  173. package/dist/components/ui/stepper/StepperIndicator.vue.d.ts +30 -0
  174. package/dist/components/ui/stepper/StepperItem.vue.d.ts +24 -0
  175. package/dist/components/ui/stepper/StepperSeparator.vue.d.ts +7 -0
  176. package/dist/components/ui/stepper/StepperTitle.vue.d.ts +22 -0
  177. package/dist/components/ui/stepper/StepperTrigger.vue.d.ts +22 -0
  178. package/dist/components/ui/stepper/index.d.ts +7 -0
  179. package/dist/components/ui/switch/Switch.vue.d.ts +12 -0
  180. package/dist/components/ui/switch/index.d.ts +1 -0
  181. package/dist/components/ui/table/Table.vue.d.ts +22 -0
  182. package/dist/components/ui/table/TableBody.vue.d.ts +21 -0
  183. package/dist/components/ui/table/TableCaption.vue.d.ts +21 -0
  184. package/dist/components/ui/table/TableCell.vue.d.ts +22 -0
  185. package/dist/components/ui/table/TableEmpty.vue.d.ts +24 -0
  186. package/dist/components/ui/table/TableFooter.vue.d.ts +21 -0
  187. package/dist/components/ui/table/TableHead.vue.d.ts +21 -0
  188. package/dist/components/ui/table/TableHeader.vue.d.ts +21 -0
  189. package/dist/components/ui/table/TableRow.vue.d.ts +21 -0
  190. package/dist/components/ui/table/index.d.ts +9 -0
  191. package/dist/components/ui/tabs/Tabs.vue.d.ts +28 -0
  192. package/dist/components/ui/tabs/TabsContent.vue.d.ts +22 -0
  193. package/dist/components/ui/tabs/TabsList.vue.d.ts +22 -0
  194. package/dist/components/ui/tabs/TabsTrigger.vue.d.ts +22 -0
  195. package/dist/components/ui/tabs/index.d.ts +4 -0
  196. package/dist/components/ui/tag/Tag.vue.d.ts +35 -0
  197. package/dist/components/ui/tag/index.d.ts +2 -0
  198. package/dist/components/ui/tag/variants.d.ts +6 -0
  199. package/dist/components/ui/textarea/Textarea.vue.d.ts +14 -0
  200. package/dist/components/ui/textarea/TextareaControl.vue.d.ts +14 -0
  201. package/dist/components/ui/textarea/TextareaFieldGroup.vue.d.ts +21 -0
  202. package/dist/components/ui/textarea/index.d.ts +4 -0
  203. package/dist/components/ui/textarea/types.d.ts +32 -0
  204. package/dist/components/ui/tool/Tool.vue.d.ts +61 -0
  205. package/dist/components/ui/tool/index.d.ts +2 -0
  206. package/dist/components/ui/tooltip/Tooltip.vue.d.ts +24 -0
  207. package/dist/components/ui/tooltip/TooltipContent.vue.d.ts +32 -0
  208. package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts +20 -0
  209. package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts +18 -0
  210. package/dist/components/ui/tooltip/index.d.ts +4 -0
  211. package/dist/docs/component-docs.d.ts +18 -0
  212. package/dist/docs/markdown.d.ts +27 -0
  213. package/dist/index.d.ts +45 -0
  214. package/dist/lib/code-highlight.d.ts +11 -0
  215. package/dist/lib/utils.d.ts +2 -0
  216. package/dist/styles.css +3 -0
  217. package/package.json +76 -0
  218. package/public/r/accordion.json +52 -0
  219. package/public/r/alert.json +51 -0
  220. package/public/r/button-group.json +31 -0
  221. package/public/r/button.json +39 -0
  222. package/public/r/card.json +61 -0
  223. package/public/r/chart.json +31 -0
  224. package/public/r/chat.json +186 -0
  225. package/public/r/checkbox.json +34 -0
  226. package/public/r/commit.json +32 -0
  227. package/public/r/contribution-graph.json +63 -0
  228. package/public/r/data-table.json +197 -0
  229. package/public/r/date-picker.json +33 -0
  230. package/public/r/dialog.json +88 -0
  231. package/public/r/diff.json +71 -0
  232. package/public/r/dropdown-menu.json +112 -0
  233. package/public/r/gauge.json +31 -0
  234. package/public/r/git-graph.json +32 -0
  235. package/public/r/incident-timeline.json +64 -0
  236. package/public/r/input.json +49 -0
  237. package/public/r/kpi-card.json +32 -0
  238. package/public/r/kpi-line-card.json +32 -0
  239. package/public/r/model-selector.json +148 -0
  240. package/public/r/network-graph.json +33 -0
  241. package/public/r/pagination.json +95 -0
  242. package/public/r/profile.json +37 -0
  243. package/public/r/progress.json +31 -0
  244. package/public/r/prompt-input.json +293 -0
  245. package/public/r/radio-group.json +45 -0
  246. package/public/r/reasoning.json +38 -0
  247. package/public/r/registry.json +2512 -0
  248. package/public/r/select.json +100 -0
  249. package/public/r/separator.json +37 -0
  250. package/public/r/shimmer.json +31 -0
  251. package/public/r/sidebar.json +221 -0
  252. package/public/r/skeleton.json +31 -0
  253. package/public/r/sonner.json +33 -0
  254. package/public/r/spinner.json +31 -0
  255. package/public/r/stepper.json +70 -0
  256. package/public/r/switch.json +33 -0
  257. package/public/r/table.json +79 -0
  258. package/public/r/tabs.json +51 -0
  259. package/public/r/tag.json +39 -0
  260. package/public/r/textarea.json +49 -0
  261. package/public/r/tool.json +32 -0
  262. package/public/r/tooltip.json +51 -0
  263. package/registry.json +2512 -0
  264. package/src/components/docs/MarkdownContent.vue +106 -0
  265. package/src/components/ui/accordion/Accordion.vue +24 -0
  266. package/src/components/ui/accordion/AccordionContent.vue +62 -0
  267. package/src/components/ui/accordion/AccordionItem.vue +23 -0
  268. package/src/components/ui/accordion/AccordionTrigger.vue +38 -0
  269. package/src/components/ui/accordion/index.ts +4 -0
  270. package/src/components/ui/alert/Alert.vue +40 -0
  271. package/src/components/ui/alert/AlertDescription.vue +24 -0
  272. package/src/components/ui/alert/AlertTitle.vue +24 -0
  273. package/src/components/ui/alert/index.ts +4 -0
  274. package/src/components/ui/alert/variants.ts +19 -0
  275. package/src/components/ui/button/Button.vue +27 -0
  276. package/src/components/ui/button/index.ts +2 -0
  277. package/src/components/ui/button/variants.ts +32 -0
  278. package/src/components/ui/button-group/ButtonGroup.vue +31 -0
  279. package/src/components/ui/button-group/index.ts +2 -0
  280. package/src/components/ui/card/Card.vue +17 -0
  281. package/src/components/ui/card/CardContent.vue +14 -0
  282. package/src/components/ui/card/CardDescription.vue +14 -0
  283. package/src/components/ui/card/CardFooter.vue +14 -0
  284. package/src/components/ui/card/CardHeader.vue +17 -0
  285. package/src/components/ui/card/CardTitle.vue +14 -0
  286. package/src/components/ui/card/index.ts +6 -0
  287. package/src/components/ui/chart/Chart.vue +1042 -0
  288. package/src/components/ui/chart/index.ts +13 -0
  289. package/src/components/ui/chat/Chat.vue +1297 -0
  290. package/src/components/ui/chat/ChatAttachments.vue +278 -0
  291. package/src/components/ui/chat/ChatCodeBlock.vue +283 -0
  292. package/src/components/ui/chat/code-block.ts +30 -0
  293. package/src/components/ui/chat/index.ts +24 -0
  294. package/src/components/ui/chat/types.ts +23 -0
  295. package/src/components/ui/checkbox/Checkbox.vue +38 -0
  296. package/src/components/ui/checkbox/index.ts +1 -0
  297. package/src/components/ui/commit/Commit.vue +423 -0
  298. package/src/components/ui/commit/index.ts +9 -0
  299. package/src/components/ui/contribution-graph/ContributionGraph.vue +719 -0
  300. package/src/components/ui/contribution-graph/index.ts +9 -0
  301. package/src/components/ui/data-table/DataTable.vue +534 -0
  302. package/src/components/ui/data-table/index.ts +9 -0
  303. package/src/components/ui/date-picker/DatePicker.vue +649 -0
  304. package/src/components/ui/date-picker/index.ts +7 -0
  305. package/src/components/ui/dialog/Dialog.vue +19 -0
  306. package/src/components/ui/dialog/DialogClose.vue +17 -0
  307. package/src/components/ui/dialog/DialogContent.vue +60 -0
  308. package/src/components/ui/dialog/DialogDescription.vue +23 -0
  309. package/src/components/ui/dialog/DialogFooter.vue +17 -0
  310. package/src/components/ui/dialog/DialogHeader.vue +17 -0
  311. package/src/components/ui/dialog/DialogOverlay.vue +23 -0
  312. package/src/components/ui/dialog/DialogScrollContent.vue +69 -0
  313. package/src/components/ui/dialog/DialogTitle.vue +23 -0
  314. package/src/components/ui/dialog/DialogTrigger.vue +17 -0
  315. package/src/components/ui/dialog/index.ts +10 -0
  316. package/src/components/ui/diff/DiffTool.vue +513 -0
  317. package/src/components/ui/diff/diff-parser.ts +423 -0
  318. package/src/components/ui/diff/diff-tool.ts +39 -0
  319. package/src/components/ui/diff/index.ts +5 -0
  320. package/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  321. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
  322. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
  323. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
  324. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
  325. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
  326. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  327. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  328. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  329. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  330. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
  331. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
  332. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  333. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  334. package/src/components/ui/dropdown-menu/index.ts +16 -0
  335. package/src/components/ui/gauge/Gauge.vue +725 -0
  336. package/src/components/ui/gauge/index.ts +9 -0
  337. package/src/components/ui/git-graph/GitGraph.vue +715 -0
  338. package/src/components/ui/git-graph/index.ts +9 -0
  339. package/src/components/ui/incident-timeline/IncidentTimeline.vue +360 -0
  340. package/src/components/ui/incident-timeline/index.ts +7 -0
  341. package/src/components/ui/input/Input.vue +159 -0
  342. package/src/components/ui/input/InputControl.vue +135 -0
  343. package/src/components/ui/input/InputFieldGroup.vue +14 -0
  344. package/src/components/ui/input/index.ts +9 -0
  345. package/src/components/ui/input/types.ts +34 -0
  346. package/src/components/ui/kpi-card/KpiCard.vue +268 -0
  347. package/src/components/ui/kpi-card/index.ts +9 -0
  348. package/src/components/ui/kpi-line-card/KpiLineCard.vue +622 -0
  349. package/src/components/ui/kpi-line-card/index.ts +11 -0
  350. package/src/components/ui/model-selector/ModelSelector.vue +328 -0
  351. package/src/components/ui/model-selector/index.ts +6 -0
  352. package/src/components/ui/model-selector/types.ts +15 -0
  353. package/src/components/ui/network-graph/NetworkGraph.vue +902 -0
  354. package/src/components/ui/network-graph/index.ts +7 -0
  355. package/src/components/ui/pagination/Pagination.vue +26 -0
  356. package/src/components/ui/pagination/PaginationContent.vue +24 -0
  357. package/src/components/ui/pagination/PaginationEllipsis.vue +27 -0
  358. package/src/components/ui/pagination/PaginationFirst.vue +33 -0
  359. package/src/components/ui/pagination/PaginationItem.vue +39 -0
  360. package/src/components/ui/pagination/PaginationLast.vue +33 -0
  361. package/src/components/ui/pagination/PaginationNext.vue +33 -0
  362. package/src/components/ui/pagination/PaginationPrevious.vue +33 -0
  363. package/src/components/ui/pagination/index.ts +8 -0
  364. package/src/components/ui/profile/Profile.vue +226 -0
  365. package/src/components/ui/profile/ProfileGroup.vue +96 -0
  366. package/src/components/ui/profile/index.ts +8 -0
  367. package/src/components/ui/progress/Progress.vue +271 -0
  368. package/src/components/ui/progress/index.ts +9 -0
  369. package/src/components/ui/prompt-input/PromptInput.vue +1094 -0
  370. package/src/components/ui/prompt-input/index.ts +14 -0
  371. package/src/components/ui/prompt-input/types.ts +78 -0
  372. package/src/components/ui/radio-group/RadioGroup.vue +36 -0
  373. package/src/components/ui/radio-group/RadioGroupItem.vue +45 -0
  374. package/src/components/ui/radio-group/RadioGroupOption.vue +80 -0
  375. package/src/components/ui/radio-group/index.ts +3 -0
  376. package/src/components/ui/reasoning/Reasoning.vue +278 -0
  377. package/src/components/ui/reasoning/index.ts +8 -0
  378. package/src/components/ui/reasoning/types.ts +29 -0
  379. package/src/components/ui/select/Select.vue +19 -0
  380. package/src/components/ui/select/SelectContent.vue +166 -0
  381. package/src/components/ui/select/SelectGroup.vue +23 -0
  382. package/src/components/ui/select/SelectItem.vue +97 -0
  383. package/src/components/ui/select/SelectItemText.vue +15 -0
  384. package/src/components/ui/select/SelectLabel.vue +17 -0
  385. package/src/components/ui/select/SelectScrollDownButton.vue +26 -0
  386. package/src/components/ui/select/SelectScrollUpButton.vue +26 -0
  387. package/src/components/ui/select/SelectSeparator.vue +19 -0
  388. package/src/components/ui/select/SelectTrigger.vue +33 -0
  389. package/src/components/ui/select/SelectValue.vue +15 -0
  390. package/src/components/ui/select/index.ts +11 -0
  391. package/src/components/ui/select/search.ts +26 -0
  392. package/src/components/ui/separator/Separator.vue +30 -0
  393. package/src/components/ui/separator/index.ts +5 -0
  394. package/src/components/ui/separator/types.ts +9 -0
  395. package/src/components/ui/shimmer/Shimmer.vue +110 -0
  396. package/src/components/ui/shimmer/index.ts +5 -0
  397. package/src/components/ui/sidebar/Sidebar.vue +142 -0
  398. package/src/components/ui/sidebar/SidebarContent.vue +18 -0
  399. package/src/components/ui/sidebar/SidebarFooter.vue +18 -0
  400. package/src/components/ui/sidebar/SidebarGroup.vue +18 -0
  401. package/src/components/ui/sidebar/SidebarGroupAction.vue +31 -0
  402. package/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  403. package/src/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
  404. package/src/components/ui/sidebar/SidebarHeader.vue +18 -0
  405. package/src/components/ui/sidebar/SidebarInput.vue +26 -0
  406. package/src/components/ui/sidebar/SidebarInset.vue +23 -0
  407. package/src/components/ui/sidebar/SidebarMenu.vue +18 -0
  408. package/src/components/ui/sidebar/SidebarMenuAction.vue +34 -0
  409. package/src/components/ui/sidebar/SidebarMenuBadge.vue +25 -0
  410. package/src/components/ui/sidebar/SidebarMenuButton.vue +37 -0
  411. package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +38 -0
  412. package/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  413. package/src/components/ui/sidebar/SidebarMenuSub.vue +18 -0
  414. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  415. package/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  416. package/src/components/ui/sidebar/SidebarProvider.vue +119 -0
  417. package/src/components/ui/sidebar/SidebarRail.vue +35 -0
  418. package/src/components/ui/sidebar/SidebarSeparator.vue +18 -0
  419. package/src/components/ui/sidebar/SidebarTrigger.vue +28 -0
  420. package/src/components/ui/sidebar/context.ts +39 -0
  421. package/src/components/ui/sidebar/index.ts +43 -0
  422. package/src/components/ui/sidebar/types.ts +13 -0
  423. package/src/components/ui/sidebar/variants.ts +25 -0
  424. package/src/components/ui/skeleton/Skeleton.vue +53 -0
  425. package/src/components/ui/skeleton/index.ts +5 -0
  426. package/src/components/ui/sonner/Sonner.vue +69 -0
  427. package/src/components/ui/sonner/index.ts +12 -0
  428. package/src/components/ui/spinner/Spinner.vue +33 -0
  429. package/src/components/ui/spinner/index.ts +1 -0
  430. package/src/components/ui/stepper/Stepper.vue +29 -0
  431. package/src/components/ui/stepper/StepperDescription.vue +30 -0
  432. package/src/components/ui/stepper/StepperIndicator.vue +50 -0
  433. package/src/components/ui/stepper/StepperItem.vue +28 -0
  434. package/src/components/ui/stepper/StepperSeparator.vue +25 -0
  435. package/src/components/ui/stepper/StepperTitle.vue +27 -0
  436. package/src/components/ui/stepper/StepperTrigger.vue +27 -0
  437. package/src/components/ui/stepper/index.ts +7 -0
  438. package/src/components/ui/switch/Switch.vue +41 -0
  439. package/src/components/ui/switch/index.ts +1 -0
  440. package/src/components/ui/table/Table.vue +23 -0
  441. package/src/components/ui/table/TableBody.vue +17 -0
  442. package/src/components/ui/table/TableCaption.vue +17 -0
  443. package/src/components/ui/table/TableCell.vue +24 -0
  444. package/src/components/ui/table/TableEmpty.vue +31 -0
  445. package/src/components/ui/table/TableFooter.vue +17 -0
  446. package/src/components/ui/table/TableHead.vue +22 -0
  447. package/src/components/ui/table/TableHeader.vue +17 -0
  448. package/src/components/ui/table/TableRow.vue +22 -0
  449. package/src/components/ui/table/index.ts +9 -0
  450. package/src/components/ui/tabs/Tabs.vue +24 -0
  451. package/src/components/ui/tabs/TabsContent.vue +22 -0
  452. package/src/components/ui/tabs/TabsList.vue +27 -0
  453. package/src/components/ui/tabs/TabsTrigger.vue +27 -0
  454. package/src/components/ui/tabs/index.ts +4 -0
  455. package/src/components/ui/tag/Tag.vue +55 -0
  456. package/src/components/ui/tag/index.ts +2 -0
  457. package/src/components/ui/tag/variants.ts +29 -0
  458. package/src/components/ui/textarea/Textarea.vue +159 -0
  459. package/src/components/ui/textarea/TextareaControl.vue +120 -0
  460. package/src/components/ui/textarea/TextareaFieldGroup.vue +14 -0
  461. package/src/components/ui/textarea/index.ts +10 -0
  462. package/src/components/ui/textarea/types.ts +35 -0
  463. package/src/components/ui/tool/Tool.vue +304 -0
  464. package/src/components/ui/tool/index.ts +7 -0
  465. package/src/components/ui/tooltip/Tooltip.vue +19 -0
  466. package/src/components/ui/tooltip/TooltipContent.vue +44 -0
  467. package/src/components/ui/tooltip/TooltipProvider.vue +14 -0
  468. package/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
  469. package/src/components/ui/tooltip/index.ts +4 -0
  470. package/src/lib/code-highlight.ts +220 -0
  471. package/src/lib/utils.ts +6 -0
  472. package/src/styles.css +684 -0
@@ -0,0 +1,1297 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ computed,
4
+ nextTick,
5
+ onBeforeUnmount,
6
+ onMounted,
7
+ ref,
8
+ watch,
9
+ } from "vue"
10
+ import type {
11
+ Component,
12
+ ComponentPublicInstance,
13
+ HTMLAttributes,
14
+ StyleValue,
15
+ } from "vue"
16
+ import {
17
+ BookmarkIcon,
18
+ ChevronLeftIcon,
19
+ ChevronRightIcon,
20
+ CopyIcon,
21
+ DownloadIcon,
22
+ GitBranchIcon,
23
+ MoreHorizontalIcon,
24
+ PencilIcon,
25
+ RotateCcwIcon,
26
+ } from "@lucide/vue"
27
+ import { Button } from "@/components/ui/button"
28
+ import {
29
+ DropdownMenu,
30
+ DropdownMenuContent,
31
+ DropdownMenuGroup,
32
+ DropdownMenuItem,
33
+ DropdownMenuTrigger,
34
+ } from "@/components/ui/dropdown-menu"
35
+ import { Spinner } from "@/components/ui/spinner"
36
+ import { cn } from "@/lib/utils"
37
+ import ChatAttachments from "./ChatAttachments.vue"
38
+ import ChatCodeBlock from "./ChatCodeBlock.vue"
39
+ import type {
40
+ ChatActionKey,
41
+ ChatActionVisibility,
42
+ ChatAttachment,
43
+ ChatSide,
44
+ ChatStatus,
45
+ ChatVariant,
46
+ } from "./types"
47
+
48
+ export type ChatProfile =
49
+ | string
50
+ | {
51
+ alt?: string
52
+ label?: string
53
+ src?: string
54
+ }
55
+
56
+ export type ChatMessage =
57
+ | string
58
+ | {
59
+ actionsVisibility?: ChatActionVisibility
60
+ attachments?: ChatAttachment[]
61
+ content: string
62
+ id?: number | string
63
+ name?: string
64
+ profile?: ChatProfile
65
+ regenerate?: boolean
66
+ regenerating?: boolean
67
+ status?: ChatStatus
68
+ version?: number
69
+ versions?: string[]
70
+ }
71
+
72
+ export interface ChatResolvedMessage {
73
+ actionsVisibility: ChatActionVisibility
74
+ attachments: ChatAttachment[]
75
+ content: string
76
+ id: number | string
77
+ key: string
78
+ name?: string
79
+ profile?: ChatProfile
80
+ regenerate: boolean
81
+ regenerating: boolean
82
+ side: ChatSide
83
+ status: ChatStatus
84
+ version: number
85
+ versionCount: number
86
+ versions: string[]
87
+ variant: ChatVariant
88
+ }
89
+
90
+ export interface ChatActionSlotProps {
91
+ message: ChatResolvedMessage
92
+ side: ChatSide
93
+ status: ChatStatus
94
+ }
95
+
96
+ export interface ChatAttachmentSlotProps extends ChatActionSlotProps {
97
+ attachments: ChatAttachment[]
98
+ }
99
+
100
+ export interface ChatContentSlotProps extends ChatActionSlotProps {
101
+ attachments: ChatAttachment[]
102
+ content: string
103
+ isStreaming: boolean
104
+ }
105
+
106
+ export interface ChatMessageSlotProps extends ChatContentSlotProps {
107
+ canCollapse: boolean
108
+ isCollapsed: boolean
109
+ toggleExpanded: () => void
110
+ }
111
+
112
+ type ChatActionSlotName = "left-actions" | "message-actions" | "right-actions"
113
+ type ChatContentPart =
114
+ | {
115
+ content: string
116
+ key: string
117
+ type: "text"
118
+ }
119
+ | {
120
+ content: string
121
+ key: string
122
+ language?: string
123
+ type: "code"
124
+ }
125
+ type ChatSingleActionKey = Exclude<ChatActionKey, "pagination">
126
+
127
+ const actionIcons: Record<ChatSingleActionKey, Component> = {
128
+ bookmark: BookmarkIcon,
129
+ branch: GitBranchIcon,
130
+ copy: CopyIcon,
131
+ download: DownloadIcon,
132
+ edit: PencilIcon,
133
+ regenerate: RotateCcwIcon,
134
+ }
135
+
136
+ const props = withDefaults(
137
+ defineProps<{
138
+ actionItems?: ChatActionKey[]
139
+ actionMenuThreshold?: number
140
+ actionsLabel?: string
141
+ ariaLive?: "assertive" | "off" | "polite"
142
+ ariaLabel?: string
143
+ autoScroll?: boolean
144
+ autoScrollBehavior?: ScrollBehavior
145
+ bookmarkAction?: boolean
146
+ bookmarkLabel?: string
147
+ branchAction?: boolean
148
+ branchLabel?: string
149
+ class?: HTMLAttributes["class"]
150
+ collapseLabel?: string
151
+ collapsible?: boolean
152
+ codeBlockCopiedLabel?: string
153
+ codeBlockCloseLabel?: string
154
+ codeBlockCopyLabel?: string
155
+ codeBlockExpandLabel?: string
156
+ codeBlockHighlight?: boolean
157
+ codeBlockLabel?: string
158
+ codeBlockLineNumbers?: boolean
159
+ codeBlockMaxVisibleLines?: number
160
+ codeBlocks?: boolean
161
+ copyAction?: boolean
162
+ copyLabel?: string
163
+ downloadAction?: boolean
164
+ downloadLabel?: string
165
+ editAction?: boolean
166
+ editLabel?: string
167
+ expandLabel?: string
168
+ left?: ChatMessage | ChatMessage[]
169
+ leftActionItems?: ChatActionKey[]
170
+ leftName?: string
171
+ leftProfile?: ChatProfile
172
+ leftVariant?: ChatVariant
173
+ moreActionsLabel?: string
174
+ nextVersionLabel?: string
175
+ paginationAction?: boolean
176
+ previousVersionLabel?: string
177
+ regenerateAction?: boolean
178
+ regenerateLabel?: string
179
+ regeneratingLabel?: string
180
+ right?: ChatMessage | ChatMessage[]
181
+ rightActionItems?: ChatActionKey[]
182
+ rightName?: string
183
+ rightProfile?: ChatProfile
184
+ rightVariant?: ChatVariant
185
+ }>(),
186
+ {
187
+ actionItems: () => ["pagination", "regenerate"],
188
+ actionMenuThreshold: 3,
189
+ actionsLabel: "Message actions",
190
+ ariaLive: "polite",
191
+ ariaLabel: "Chat messages",
192
+ autoScroll: false,
193
+ autoScrollBehavior: "smooth",
194
+ bookmarkLabel: "북마크",
195
+ branchLabel: "브랜치",
196
+ collapseLabel: "간단히",
197
+ collapsible: true,
198
+ codeBlockCopiedLabel: "복사됨",
199
+ codeBlockCloseLabel: "닫기",
200
+ codeBlockCopyLabel: "코드 복사",
201
+ codeBlockExpandLabel: "크게 보기",
202
+ codeBlockHighlight: true,
203
+ codeBlockLabel: "Code",
204
+ codeBlockLineNumbers: true,
205
+ codeBlockMaxVisibleLines: 20,
206
+ codeBlocks: true,
207
+ copyLabel: "복사",
208
+ downloadLabel: "다운로드",
209
+ editLabel: "수정",
210
+ expandLabel: "더보기",
211
+ leftVariant: "bubble",
212
+ moreActionsLabel: "더 많은 작업",
213
+ nextVersionLabel: "다음 응답",
214
+ previousVersionLabel: "이전 응답",
215
+ regenerateLabel: "재생성",
216
+ regeneratingLabel: "재생성 중",
217
+ rightVariant: "bubble",
218
+ },
219
+ )
220
+
221
+ const emit = defineEmits<{
222
+ action: [action: ChatSingleActionKey, message: ChatResolvedMessage]
223
+ bookmark: [message: ChatResolvedMessage]
224
+ branch: [message: ChatResolvedMessage]
225
+ codeCopy: [message: ChatResolvedMessage, code: string, language?: string]
226
+ copy: [message: ChatResolvedMessage]
227
+ download: [message: ChatResolvedMessage]
228
+ edit: [message: ChatResolvedMessage]
229
+ paginate: [message: ChatResolvedMessage, version: number]
230
+ regenerate: [message: ChatResolvedMessage]
231
+ }>()
232
+
233
+ const rootRef = ref<HTMLElement | null>(null)
234
+ const slots = defineSlots<{
235
+ attachments?: (props: ChatAttachmentSlotProps) => unknown
236
+ content?: (props: ChatContentSlotProps) => unknown
237
+ "left-actions"?: (props: ChatActionSlotProps) => unknown
238
+ message?: (props: ChatMessageSlotProps) => unknown
239
+ "message-actions"?: (props: ChatActionSlotProps) => unknown
240
+ "right-actions"?: (props: ChatActionSlotProps) => unknown
241
+ }>()
242
+ const collapsedHeightByKey = ref<Record<string, number>>({})
243
+ const overflowByKey = ref<Record<string, boolean>>({})
244
+ const expandedKeys = ref(new Set<string>())
245
+ const expandedHeightByKey = ref<Record<string, number>>({})
246
+ const contentRefs = new Map<string, HTMLElement>()
247
+ const observedContentElements = new Set<HTMLElement>()
248
+ let resizeObserver: ResizeObserver | undefined
249
+ let pendingMeasure = false
250
+ let pendingAutoScroll = false
251
+
252
+ function toMessageList(value?: ChatMessage | ChatMessage[]) {
253
+ if (!value) return []
254
+ return Array.isArray(value) ? value : [value]
255
+ }
256
+
257
+ function normalizeMessage(
258
+ value: ChatMessage,
259
+ side: ChatSide,
260
+ index: number,
261
+ ): ChatResolvedMessage {
262
+ const sideName = side === "left" ? props.leftName : props.rightName
263
+ const sideProfile = side === "left" ? props.leftProfile : props.rightProfile
264
+ const sideVariant = side === "left" ? props.leftVariant : props.rightVariant
265
+
266
+ if (typeof value === "string") {
267
+ return {
268
+ actionsVisibility: "always",
269
+ attachments: [],
270
+ content: value,
271
+ id: `${side}-${index}`,
272
+ key: `${side}-${index}`,
273
+ name: sideName,
274
+ profile: sideProfile,
275
+ regenerate: false,
276
+ regenerating: false,
277
+ side,
278
+ status: "complete",
279
+ version: 0,
280
+ versionCount: 1,
281
+ versions: [value],
282
+ variant: sideVariant,
283
+ }
284
+ }
285
+
286
+ const id = value.id ?? `${side}-${index}`
287
+ const versions = normalizeVersions(value.content, value.versions)
288
+ const version = normalizeVersion(value.version, versions.length)
289
+
290
+ return {
291
+ actionsVisibility: value.actionsVisibility ?? "always",
292
+ attachments: value.attachments ?? [],
293
+ content: versions[version] ?? value.content,
294
+ id,
295
+ key: `${side}-${id}`,
296
+ name: value.name ?? sideName,
297
+ profile: value.profile ?? sideProfile,
298
+ regenerate: value.regenerate ?? false,
299
+ regenerating: value.regenerating ?? false,
300
+ side,
301
+ status: normalizeStatus(value.status),
302
+ version,
303
+ versionCount: versions.length,
304
+ versions,
305
+ variant: sideVariant,
306
+ }
307
+ }
308
+
309
+ function normalizeVersions(content: string, versions?: string[]) {
310
+ if (!versions || versions.length === 0) return [content]
311
+
312
+ return versions
313
+ }
314
+
315
+ function normalizeStatus(status: ChatStatus | undefined): ChatStatus {
316
+ return status ?? "complete"
317
+ }
318
+
319
+ function normalizeVersion(version: number | undefined, versionCount: number) {
320
+ const fallbackVersion = versionCount - 1
321
+ const nextVersion = version ?? fallbackVersion
322
+
323
+ if (!Number.isFinite(nextVersion)) return fallbackVersion
324
+
325
+ return Math.min(Math.max(Math.trunc(nextVersion), 0), versionCount - 1)
326
+ }
327
+
328
+ const messages = computed(() => {
329
+ const leftMessages = toMessageList(props.left).map((message, index) =>
330
+ normalizeMessage(message, "left", index),
331
+ )
332
+ const rightMessages = toMessageList(props.right).map((message, index) =>
333
+ normalizeMessage(message, "right", index),
334
+ )
335
+ const rows: ChatResolvedMessage[] = []
336
+ const maxLength = Math.max(leftMessages.length, rightMessages.length)
337
+
338
+ for (let index = 0; index < maxLength; index += 1) {
339
+ if (leftMessages[index]) rows.push(leftMessages[index])
340
+ if (rightMessages[index]) rows.push(rightMessages[index])
341
+ }
342
+
343
+ return rows
344
+ })
345
+
346
+ function profileSrc(profile?: ChatProfile) {
347
+ if (!profile || typeof profile === "string") return undefined
348
+ return profile.src
349
+ }
350
+
351
+ function profileLabel(profile?: ChatProfile) {
352
+ if (!profile) return undefined
353
+ if (typeof profile === "string") return profile
354
+ return profile.label
355
+ }
356
+
357
+ function profileAlt(message: ChatResolvedMessage) {
358
+ if (!message.profile || typeof message.profile === "string") {
359
+ return message.name ?? "Profile"
360
+ }
361
+
362
+ return message.profile.alt ?? message.name ?? "Profile"
363
+ }
364
+
365
+ function hasProfile(message: ChatResolvedMessage) {
366
+ return Boolean(message.profile)
367
+ }
368
+
369
+ function canCollapse(message: ChatResolvedMessage) {
370
+ return !isStreaming(message) &&
371
+ props.collapsible &&
372
+ !hasCodeBlock(message) &&
373
+ Boolean(overflowByKey.value[message.key])
374
+ }
375
+
376
+ function isMessageCollapsed(message: ChatResolvedMessage) {
377
+ return canCollapse(message) && !expandedKeys.value.has(message.key)
378
+ }
379
+
380
+ function toggleExpanded(key: string) {
381
+ const nextExpandedKeys = new Set(expandedKeys.value)
382
+
383
+ if (nextExpandedKeys.has(key)) {
384
+ nextExpandedKeys.delete(key)
385
+ } else {
386
+ nextExpandedKeys.add(key)
387
+ }
388
+
389
+ expandedKeys.value = nextExpandedKeys
390
+ }
391
+
392
+ function setContentRef(
393
+ key: string,
394
+ element: ComponentPublicInstance | Element | null,
395
+ ) {
396
+ const currentElement = contentRefs.get(key)
397
+
398
+ if (!element) {
399
+ if (currentElement) unobserveContentElement(currentElement)
400
+ contentRefs.delete(key)
401
+ return
402
+ }
403
+
404
+ if (element instanceof HTMLElement) {
405
+ if (currentElement && currentElement !== element) {
406
+ unobserveContentElement(currentElement)
407
+ }
408
+
409
+ contentRefs.set(key, element)
410
+ observeContentElement(element)
411
+ measureOverflowSoon()
412
+ return
413
+ }
414
+
415
+ if (currentElement) unobserveContentElement(currentElement)
416
+ contentRefs.delete(key)
417
+ }
418
+
419
+ function observeContentElement(element: HTMLElement) {
420
+ if (!resizeObserver || observedContentElements.has(element)) return
421
+
422
+ resizeObserver.observe(element)
423
+ observedContentElements.add(element)
424
+ }
425
+
426
+ function unobserveContentElement(element: HTMLElement) {
427
+ resizeObserver?.unobserve(element)
428
+ observedContentElements.delete(element)
429
+ }
430
+
431
+ function getLineHeight(element: HTMLElement) {
432
+ const styles = window.getComputedStyle(element)
433
+ const lineHeight = Number.parseFloat(styles.lineHeight)
434
+
435
+ if (Number.isFinite(lineHeight)) return lineHeight
436
+
437
+ const fontSize = Number.parseFloat(styles.fontSize)
438
+
439
+ return Number.isFinite(fontSize) ? fontSize * 1.5 : 21
440
+ }
441
+
442
+ function measureOverflow() {
443
+ if (!props.collapsible) {
444
+ if (Object.keys(overflowByKey.value).length > 0) {
445
+ overflowByKey.value = {}
446
+ }
447
+ if (Object.keys(collapsedHeightByKey.value).length > 0) {
448
+ collapsedHeightByKey.value = {}
449
+ }
450
+ if (Object.keys(expandedHeightByKey.value).length > 0) {
451
+ expandedHeightByKey.value = {}
452
+ }
453
+ return
454
+ }
455
+
456
+ const nextCollapsedHeightByKey: Record<string, number> = {}
457
+ const nextExpandedHeightByKey: Record<string, number> = {}
458
+ const nextOverflowByKey: Record<string, boolean> = {}
459
+ const activeKeys = new Set(messages.value.map((message) => message.key))
460
+
461
+ for (const [key, element] of contentRefs) {
462
+ if (!activeKeys.has(key)) {
463
+ unobserveContentElement(element)
464
+ contentRefs.delete(key)
465
+ continue
466
+ }
467
+
468
+ const maxHeight = getLineHeight(element) * 4
469
+ nextCollapsedHeightByKey[key] = maxHeight
470
+ nextExpandedHeightByKey[key] = element.scrollHeight
471
+ nextOverflowByKey[key] = element.scrollHeight > maxHeight + 1
472
+ }
473
+
474
+ if (!isSameNumberMap(collapsedHeightByKey.value, nextCollapsedHeightByKey)) {
475
+ collapsedHeightByKey.value = nextCollapsedHeightByKey
476
+ }
477
+ if (!isSameNumberMap(expandedHeightByKey.value, nextExpandedHeightByKey)) {
478
+ expandedHeightByKey.value = nextExpandedHeightByKey
479
+ }
480
+ if (!isSameOverflowMap(overflowByKey.value, nextOverflowByKey)) {
481
+ overflowByKey.value = nextOverflowByKey
482
+ }
483
+ }
484
+
485
+ function isSameNumberMap(
486
+ current: Record<string, number>,
487
+ next: Record<string, number>,
488
+ ) {
489
+ const currentKeys = Object.keys(current)
490
+ const nextKeys = Object.keys(next)
491
+
492
+ if (currentKeys.length !== nextKeys.length) return false
493
+
494
+ return nextKeys.every((key) => current[key] === next[key])
495
+ }
496
+
497
+ function isSameOverflowMap(
498
+ current: Record<string, boolean>,
499
+ next: Record<string, boolean>,
500
+ ) {
501
+ const currentKeys = Object.keys(current)
502
+ const nextKeys = Object.keys(next)
503
+
504
+ if (currentKeys.length !== nextKeys.length) return false
505
+
506
+ return nextKeys.every((key) => current[key] === next[key])
507
+ }
508
+
509
+ function measureOverflowSoon() {
510
+ if (pendingMeasure) return
511
+
512
+ pendingMeasure = true
513
+
514
+ void nextTick(() => {
515
+ pendingMeasure = false
516
+ measureOverflow()
517
+ })
518
+ }
519
+
520
+ function autoScrollSoon() {
521
+ if (!props.autoScroll || !messages.value.some(isStreaming)) return
522
+ if (pendingAutoScroll) return
523
+
524
+ pendingAutoScroll = true
525
+
526
+ const scroll = () => {
527
+ pendingAutoScroll = false
528
+ const targetElement = rootRef.value?.lastElementChild
529
+
530
+ if (targetElement instanceof HTMLElement) {
531
+ targetElement.scrollIntoView({
532
+ block: "end",
533
+ behavior: props.autoScrollBehavior,
534
+ })
535
+ }
536
+ }
537
+
538
+ if (typeof requestAnimationFrame === "function") {
539
+ requestAnimationFrame(scroll)
540
+ } else {
541
+ window.setTimeout(scroll, 0)
542
+ }
543
+ }
544
+
545
+ function messageRowClass(message: ChatResolvedMessage) {
546
+ return cn(
547
+ "flex w-full items-start gap-2",
548
+ message.side === "right" &&
549
+ message.variant === "bubble" &&
550
+ "justify-end",
551
+ message.actionsVisibility === "hover" && "group/chat-actions",
552
+ )
553
+ }
554
+
555
+ function messageBodyClass(message: ChatResolvedMessage) {
556
+ return cn(
557
+ "relative flex min-w-0 flex-col gap-1",
558
+ message.variant === "plain" || hasCodeBlock(message)
559
+ ? "w-full flex-1"
560
+ : "max-w-[78%]",
561
+ message.side === "right" ? "order-1 items-end" : "items-start",
562
+ )
563
+ }
564
+
565
+ function bubbleClass(message: ChatResolvedMessage) {
566
+ return cn(
567
+ "relative min-w-0 max-w-full text-sm leading-relaxed break-words",
568
+ message.variant === "plain"
569
+ ? "w-full text-foreground"
570
+ : "rounded-2xl bg-secondary px-4 py-2 text-secondary-foreground",
571
+ message.variant === "bubble" &&
572
+ message.side === "right" &&
573
+ "bg-primary text-primary-foreground",
574
+ )
575
+ }
576
+
577
+ function fadeClass(message: ChatResolvedMessage) {
578
+ return cn(
579
+ "absolute inset-x-0 bottom-0 flex h-16 items-end justify-center pb-2 transition-opacity duration-200 ease-out",
580
+ message.variant === "plain"
581
+ ? "bg-gradient-to-b from-transparent via-white/90 to-white"
582
+ : message.side === "right"
583
+ ? "bg-gradient-to-b from-transparent via-primary/90 to-primary"
584
+ : "bg-gradient-to-b from-transparent via-secondary/90 to-secondary",
585
+ )
586
+ }
587
+
588
+ function contentClass(message: ChatResolvedMessage) {
589
+ return cn(
590
+ "overflow-hidden transition-[max-height] duration-300 ease-in-out motion-reduce:transition-none",
591
+ !canCollapse(message) && "overflow-visible",
592
+ )
593
+ }
594
+
595
+ function contentStyle(message: ChatResolvedMessage): StyleValue | undefined {
596
+ if (!canCollapse(message)) return undefined
597
+
598
+ const height = isMessageCollapsed(message)
599
+ ? collapsedHeightByKey.value[message.key]
600
+ : expandedHeightByKey.value[message.key]
601
+
602
+ if (!height) return undefined
603
+
604
+ return {
605
+ maxHeight: `${height}px`,
606
+ }
607
+ }
608
+
609
+ function toggleButtonLabel(message: ChatResolvedMessage) {
610
+ return isMessageCollapsed(message) ? props.expandLabel : props.collapseLabel
611
+ }
612
+
613
+ function toggleArrow(message: ChatResolvedMessage) {
614
+ return isMessageCollapsed(message) ? "↓" : "↑"
615
+ }
616
+
617
+ function toggleButtonClass(message: ChatResolvedMessage) {
618
+ return cn(
619
+ "inline-flex items-center gap-1 border-0 bg-transparent p-0 text-xs font-medium shadow-none transition-colors hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
620
+ message.variant === "plain"
621
+ ? "text-muted-foreground hover:text-foreground"
622
+ : message.side === "right"
623
+ ? "text-primary-foreground/85 hover:text-primary-foreground"
624
+ : "text-secondary-foreground/70 hover:text-secondary-foreground",
625
+ !isMessageCollapsed(message) && "mt-2",
626
+ )
627
+ }
628
+
629
+ function isStreaming(message: ChatResolvedMessage) {
630
+ return message.status === "streaming"
631
+ }
632
+
633
+ function streamingIndicatorClass(message: ChatResolvedMessage) {
634
+ return cn(
635
+ "mt-2 inline-flex items-center text-current/70",
636
+ message.variant === "plain" && "text-muted-foreground",
637
+ )
638
+ }
639
+
640
+ function actionSlotProps(message: ChatResolvedMessage): ChatActionSlotProps {
641
+ return {
642
+ message,
643
+ side: message.side,
644
+ status: message.status,
645
+ }
646
+ }
647
+
648
+ function attachmentSlotProps(message: ChatResolvedMessage): ChatAttachmentSlotProps {
649
+ return {
650
+ ...actionSlotProps(message),
651
+ attachments: message.attachments,
652
+ }
653
+ }
654
+
655
+ function contentSlotProps(message: ChatResolvedMessage): ChatContentSlotProps {
656
+ return {
657
+ ...actionSlotProps(message),
658
+ attachments: message.attachments,
659
+ content: message.content,
660
+ isStreaming: isStreaming(message),
661
+ }
662
+ }
663
+
664
+ function messageSlotProps(message: ChatResolvedMessage): ChatMessageSlotProps {
665
+ return {
666
+ ...contentSlotProps(message),
667
+ canCollapse: canCollapse(message),
668
+ isCollapsed: isMessageCollapsed(message),
669
+ toggleExpanded: () => toggleExpanded(message.key),
670
+ }
671
+ }
672
+
673
+ function shouldRenderCodeBlocks(message: ChatResolvedMessage) {
674
+ return props.codeBlocks && message.side === "left"
675
+ }
676
+
677
+ function hasCodeBlock(message: ChatResolvedMessage) {
678
+ return shouldRenderCodeBlocks(message) &&
679
+ /```([^\n\r`]*)\r?\n?([\s\S]*?)```/.test(message.content)
680
+ }
681
+
682
+ function contentParts(message: ChatResolvedMessage): ChatContentPart[] {
683
+ if (!shouldRenderCodeBlocks(message)) {
684
+ return [
685
+ {
686
+ content: message.content,
687
+ key: `${message.key}-text`,
688
+ type: "text",
689
+ },
690
+ ]
691
+ }
692
+
693
+ const parts: ChatContentPart[] = []
694
+ const fencePattern = /```([^\n\r`]*)\r?\n?([\s\S]*?)```/g
695
+ let lastIndex = 0
696
+ let index = 0
697
+ let match: RegExpExecArray | null
698
+
699
+ const pushText = (content: string) => {
700
+ const normalizedContent = content.replace(/^(?:\r?\n)+|(?:\r?\n)+$/g, "")
701
+
702
+ if (!normalizedContent.trim()) return
703
+
704
+ parts.push({
705
+ content: normalizedContent,
706
+ key: `${message.key}-text-${index}`,
707
+ type: "text",
708
+ })
709
+ index += 1
710
+ }
711
+
712
+ while ((match = fencePattern.exec(message.content)) !== null) {
713
+ pushText(message.content.slice(lastIndex, match.index))
714
+
715
+ const language = normalizeCodeLanguage(match[1])
716
+ const code = (match[2] ?? "").replace(/^(?:\r?\n)+|(?:\r?\n)+$/g, "")
717
+
718
+ parts.push({
719
+ content: code,
720
+ key: `${message.key}-code-${index}`,
721
+ language,
722
+ type: "code",
723
+ })
724
+ index += 1
725
+ lastIndex = fencePattern.lastIndex
726
+ }
727
+
728
+ pushText(message.content.slice(lastIndex))
729
+
730
+ if (parts.length === 0) {
731
+ return [
732
+ {
733
+ content: message.content,
734
+ key: `${message.key}-text`,
735
+ type: "text",
736
+ },
737
+ ]
738
+ }
739
+
740
+ return parts
741
+ }
742
+
743
+ function normalizeCodeLanguage(info?: string) {
744
+ return info?.trim().split(/\s+/)[0] || undefined
745
+ }
746
+
747
+ function handleCodeCopy(message: ChatResolvedMessage, part: ChatContentPart) {
748
+ if (part.type !== "code") return
749
+
750
+ emit("codeCopy", message, part.content, part.language)
751
+ }
752
+
753
+ function sideActionSlotName(message: ChatResolvedMessage): ChatActionSlotName {
754
+ return message.side === "left" ? "left-actions" : "right-actions"
755
+ }
756
+
757
+ function hasActions(message: ChatResolvedMessage) {
758
+ return Boolean(
759
+ messageActionItems(message).length > 0 ||
760
+ slots[sideActionSlotName(message)] ||
761
+ slots["message-actions"],
762
+ )
763
+ }
764
+
765
+ function hasAttachments(message: ChatResolvedMessage) {
766
+ return message.attachments.length > 0
767
+ }
768
+
769
+ function actionClass(message: ChatResolvedMessage) {
770
+ return cn(
771
+ "flex w-full items-center px-1 pt-1",
772
+ message.side === "right" ? "justify-end" : "justify-start",
773
+ message.actionsVisibility === "hover" &&
774
+ "absolute inset-x-0 top-full pointer-events-none pt-2 opacity-0 transition-opacity duration-150 ease-out group-hover/chat-actions:pointer-events-auto group-hover/chat-actions:opacity-100 group-focus-within/chat-actions:pointer-events-auto group-focus-within/chat-actions:opacity-100",
775
+ )
776
+ }
777
+
778
+ function canRegenerate(message: ChatResolvedMessage) {
779
+ return isActionEnabled(message, "regenerate") &&
780
+ (message.regenerate || message.regenerating)
781
+ }
782
+
783
+ function hasResponseControls(message: ChatResolvedMessage) {
784
+ return canPaginate(message) || canRegenerate(message)
785
+ }
786
+
787
+ function canPaginate(message: ChatResolvedMessage) {
788
+ return isActionEnabled(message, "pagination") && message.versionCount > 1
789
+ }
790
+
791
+ function paginationStatus(message: ChatResolvedMessage) {
792
+ return `${message.version + 1} / ${message.versionCount}`
793
+ }
794
+
795
+ function handlePaginate(message: ChatResolvedMessage, version: number) {
796
+ if (message.regenerating) return
797
+ if (version < 0 || version >= message.versionCount) return
798
+ if (version === message.version) return
799
+
800
+ emit("paginate", message, version)
801
+ }
802
+
803
+ function configuredActionItems(message: ChatResolvedMessage) {
804
+ const items = message.side === "left"
805
+ ? props.leftActionItems ?? props.actionItems
806
+ : props.rightActionItems ?? props.actionItems
807
+
808
+ return withActionFlags(items)
809
+ }
810
+
811
+ function isActionEnabled(message: ChatResolvedMessage, action: ChatActionKey) {
812
+ return configuredActionItems(message).includes(action)
813
+ }
814
+
815
+ function withActionFlags(items: ChatActionKey[]) {
816
+ const nextItems = [...items]
817
+
818
+ applyActionFlag(nextItems, "pagination", props.paginationAction)
819
+ applyActionFlag(nextItems, "regenerate", props.regenerateAction)
820
+ applyActionFlag(nextItems, "copy", props.copyAction)
821
+ applyActionFlag(nextItems, "bookmark", props.bookmarkAction)
822
+ applyActionFlag(nextItems, "branch", props.branchAction)
823
+ applyActionFlag(nextItems, "download", props.downloadAction)
824
+ applyActionFlag(nextItems, "edit", props.editAction)
825
+
826
+ return nextItems
827
+ }
828
+
829
+ function applyActionFlag(
830
+ items: ChatActionKey[],
831
+ action: ChatActionKey,
832
+ enabled: boolean | undefined,
833
+ ) {
834
+ const index = items.indexOf(action)
835
+
836
+ if (enabled === true && index === -1) {
837
+ items.push(action)
838
+ }
839
+ }
840
+
841
+ function messageActionItems(message: ChatResolvedMessage): ChatActionKey[] {
842
+ return configuredActionItems(message).filter((action) => {
843
+ if (action === "pagination") return message.versionCount > 1
844
+ if (action === "regenerate") return message.regenerate || message.regenerating
845
+
846
+ return true
847
+ })
848
+ }
849
+
850
+ function singleActionItems(message: ChatResolvedMessage): ChatSingleActionKey[] {
851
+ return messageActionItems(message).filter(
852
+ (action): action is ChatSingleActionKey => action !== "pagination",
853
+ )
854
+ }
855
+
856
+ function shouldUseActionMenu(message: ChatResolvedMessage) {
857
+ const threshold = Math.max(1, Math.trunc(props.actionMenuThreshold))
858
+
859
+ return messageActionItems(message).length >= threshold
860
+ }
861
+
862
+ function isPrimaryInlineAction(action: ChatSingleActionKey) {
863
+ return action === "regenerate" || action === "copy"
864
+ }
865
+
866
+ function inlineSingleActionItems(message: ChatResolvedMessage) {
867
+ const actions = singleActionItems(message)
868
+
869
+ if (!shouldUseActionMenu(message)) return actions
870
+
871
+ return actions.filter(isPrimaryInlineAction)
872
+ }
873
+
874
+ function menuActionItems(message: ChatResolvedMessage) {
875
+ if (!shouldUseActionMenu(message)) return []
876
+
877
+ return singleActionItems(message).filter(
878
+ (action) => !isPrimaryInlineAction(action),
879
+ )
880
+ }
881
+
882
+ function hasActionMenu(message: ChatResolvedMessage) {
883
+ return menuActionItems(message).length > 0
884
+ }
885
+
886
+ function actionLabel(action: ChatSingleActionKey, message: ChatResolvedMessage) {
887
+ if (action === "regenerate") return regenerateButtonLabel(message)
888
+ if (action === "copy") return props.copyLabel
889
+ if (action === "bookmark") return props.bookmarkLabel
890
+ if (action === "branch") return props.branchLabel
891
+ if (action === "download") return props.downloadLabel
892
+
893
+ return props.editLabel
894
+ }
895
+
896
+ function actionSlot(action: ChatSingleActionKey) {
897
+ return `chat-${action}`
898
+ }
899
+
900
+ function isSingleActionDisabled(action: ChatSingleActionKey, message: ChatResolvedMessage) {
901
+ return action === "regenerate" && message.regenerating
902
+ }
903
+
904
+ async function copyMessage(message: ChatResolvedMessage) {
905
+ try {
906
+ if (typeof navigator !== "undefined") {
907
+ await navigator.clipboard?.writeText(message.content)
908
+ }
909
+ } catch {
910
+ // Clipboard support depends on browser permissions; the action event still fires.
911
+ }
912
+
913
+ emit("copy", message)
914
+ emit("action", "copy", message)
915
+ }
916
+
917
+ function handleSingleAction(action: ChatSingleActionKey, message: ChatResolvedMessage) {
918
+ if (isSingleActionDisabled(action, message)) return
919
+
920
+ if (action === "regenerate") {
921
+ handleRegenerate(message)
922
+ return
923
+ }
924
+
925
+ if (action === "copy") {
926
+ void copyMessage(message)
927
+ return
928
+ }
929
+
930
+ if (action === "bookmark") emit("bookmark", message)
931
+ if (action === "branch") emit("branch", message)
932
+ if (action === "download") emit("download", message)
933
+ if (action === "edit") emit("edit", message)
934
+
935
+ emit("action", action, message)
936
+ }
937
+
938
+ function regenerateButtonLabel(message: ChatResolvedMessage) {
939
+ return message.regenerating ? props.regeneratingLabel : props.regenerateLabel
940
+ }
941
+
942
+ function actionToolbarLabel(message: ChatResolvedMessage) {
943
+ const details = messageActionItems(message)
944
+ .map((action) =>
945
+ action === "pagination"
946
+ ? paginationStatus(message)
947
+ : actionLabel(action, message),
948
+ )
949
+ .join(", ")
950
+
951
+ return details ? `${props.actionsLabel}, ${details}` : props.actionsLabel
952
+ }
953
+
954
+ function handleRegenerate(message: ChatResolvedMessage) {
955
+ if (message.regenerating) return
956
+
957
+ emit("regenerate", message)
958
+ emit("action", "regenerate", message)
959
+ }
960
+
961
+ function actionToolbarClass(message: ChatResolvedMessage) {
962
+ return cn(
963
+ "inline-flex max-w-full shrink-0 items-center rounded-md text-muted-foreground",
964
+ "[&>button]:relative [&>button]:rounded-none [&>button]:shadow-none",
965
+ "[&>button:first-child]:rounded-l-md [&>button:last-child]:rounded-r-md",
966
+ "[&>button:not(:first-child)]:-ml-px [&>button:focus-visible]:z-10",
967
+ "[&>[data-slot=button]]:relative [&>[data-slot=button]]:rounded-none [&>[data-slot=button]]:shadow-none",
968
+ "[&>[data-slot=button]:first-child]:rounded-l-md [&>[data-slot=button]:last-child]:rounded-r-md",
969
+ "[&>[data-slot=button]:not(:first-child)]:-ml-px [&>[data-slot=button]:focus-visible]:z-10",
970
+ "[&>[data-slot=chat-pagination-status]]:relative [&>[data-slot=chat-pagination-status]]:rounded-none",
971
+ "[&>[data-slot=chat-pagination-status]:first-child]:rounded-l-md [&>[data-slot=chat-pagination-status]:last-child]:rounded-r-md",
972
+ "[&>[data-slot=chat-pagination-status]:not(:first-child)]:-ml-px",
973
+ message.side === "right" && "order-2",
974
+ )
975
+ }
976
+
977
+ function actionButtonClass() {
978
+ return "text-muted-foreground hover:text-foreground"
979
+ }
980
+
981
+ function actionStatusClass() {
982
+ return cn(
983
+ "inline-flex h-8 min-w-12 items-center justify-center whitespace-nowrap border border-input bg-background px-2 text-xs font-medium text-muted-foreground shadow-none",
984
+ )
985
+ }
986
+
987
+ watch(messages, () => {
988
+ measureOverflowSoon()
989
+ autoScrollSoon()
990
+ }, { flush: "post" })
991
+ watch(() => props.collapsible, () => measureOverflowSoon(), { flush: "post" })
992
+
993
+ onMounted(() => {
994
+ measureOverflowSoon()
995
+ autoScrollSoon()
996
+
997
+ if (typeof ResizeObserver !== "undefined" && rootRef.value) {
998
+ resizeObserver = new ResizeObserver(() => measureOverflowSoon())
999
+ resizeObserver.observe(rootRef.value)
1000
+ contentRefs.forEach((element) => observeContentElement(element))
1001
+ }
1002
+ })
1003
+
1004
+ onBeforeUnmount(() => {
1005
+ resizeObserver?.disconnect()
1006
+ })
1007
+ </script>
1008
+
1009
+ <template>
1010
+ <section
1011
+ ref="rootRef"
1012
+ data-slot="chat"
1013
+ :aria-live="props.ariaLive"
1014
+ :aria-label="props.ariaLabel"
1015
+ :class="cn('flex w-full flex-col gap-4 rounded-lg bg-background p-4 text-foreground', props.class)"
1016
+ role="log"
1017
+ >
1018
+ <div
1019
+ v-for="message in messages"
1020
+ :key="message.key"
1021
+ data-slot="chat-message"
1022
+ :data-side="message.side"
1023
+ :data-status="message.status"
1024
+ :data-variant="message.variant"
1025
+ :class="messageRowClass(message)"
1026
+ >
1027
+ <slot
1028
+ v-if="slots.message"
1029
+ name="message"
1030
+ v-bind="messageSlotProps(message)"
1031
+ />
1032
+
1033
+ <template v-else>
1034
+ <div
1035
+ v-if="hasProfile(message)"
1036
+ data-slot="chat-profile"
1037
+ :class="
1038
+ cn(
1039
+ 'flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground',
1040
+ message.side === 'right' && 'order-2 bg-primary text-primary-foreground',
1041
+ )
1042
+ "
1043
+ >
1044
+ <img
1045
+ v-if="profileSrc(message.profile)"
1046
+ :alt="profileAlt(message)"
1047
+ class="size-full rounded-full object-cover"
1048
+ :src="profileSrc(message.profile)"
1049
+ />
1050
+ <span v-else>{{ profileLabel(message.profile) }}</span>
1051
+ </div>
1052
+
1053
+ <div
1054
+ :class="messageBodyClass(message)"
1055
+ >
1056
+ <span
1057
+ v-if="message.name"
1058
+ data-slot="chat-name"
1059
+ class="px-1 text-xs font-medium text-muted-foreground"
1060
+ >
1061
+ {{ message.name }}
1062
+ </span>
1063
+ <slot
1064
+ v-if="hasAttachments(message) || slots.attachments"
1065
+ name="attachments"
1066
+ v-bind="attachmentSlotProps(message)"
1067
+ >
1068
+ <ChatAttachments
1069
+ v-if="hasAttachments(message)"
1070
+ :attachments="message.attachments"
1071
+ :side="message.side"
1072
+ />
1073
+ </slot>
1074
+ <div
1075
+ data-slot="chat-bubble"
1076
+ :data-collapsed="isMessageCollapsed(message) ? '' : undefined"
1077
+ :data-status="message.status"
1078
+ :aria-busy="isStreaming(message) ? 'true' : undefined"
1079
+ :class="bubbleClass(message)"
1080
+ >
1081
+ <div
1082
+ data-slot="chat-content-frame"
1083
+ class="relative"
1084
+ >
1085
+ <div
1086
+ :ref="(element) => setContentRef(message.key, element)"
1087
+ data-slot="chat-content"
1088
+ :data-status="message.status"
1089
+ :class="contentClass(message)"
1090
+ :style="contentStyle(message)"
1091
+ >
1092
+ <slot
1093
+ name="content"
1094
+ v-bind="contentSlotProps(message)"
1095
+ >
1096
+ <div
1097
+ data-slot="chat-default-content"
1098
+ class="flex min-w-0 flex-col gap-2"
1099
+ >
1100
+ <template
1101
+ v-for="part in contentParts(message)"
1102
+ :key="part.key"
1103
+ >
1104
+ <p
1105
+ v-if="part.type === 'text'"
1106
+ data-slot="chat-text"
1107
+ class="m-0 whitespace-pre-wrap"
1108
+ >
1109
+ {{ part.content }}
1110
+ </p>
1111
+ <ChatCodeBlock
1112
+ v-else
1113
+ :code="part.content"
1114
+ :close-label="props.codeBlockCloseLabel"
1115
+ :copied-label="props.codeBlockCopiedLabel"
1116
+ :copy-label="props.codeBlockCopyLabel"
1117
+ :expand-label="props.codeBlockExpandLabel"
1118
+ :highlight="props.codeBlockHighlight"
1119
+ :label="props.codeBlockLabel"
1120
+ :language="part.language"
1121
+ :line-numbers="props.codeBlockLineNumbers"
1122
+ :max-visible-lines="props.codeBlockMaxVisibleLines"
1123
+ @copy="handleCodeCopy(message, part)"
1124
+ />
1125
+ </template>
1126
+ </div>
1127
+ </slot>
1128
+ <span
1129
+ v-if="isStreaming(message)"
1130
+ data-slot="chat-streaming-indicator"
1131
+ :class="streamingIndicatorClass(message)"
1132
+ >
1133
+ <Spinner decorative />
1134
+ </span>
1135
+ </div>
1136
+ <div
1137
+ v-if="isMessageCollapsed(message)"
1138
+ data-slot="chat-fade"
1139
+ :class="fadeClass(message)"
1140
+ >
1141
+ <button
1142
+ data-slot="chat-expand"
1143
+ type="button"
1144
+ :aria-expanded="false"
1145
+ :aria-label="props.expandLabel"
1146
+ :class="toggleButtonClass(message)"
1147
+ @click="toggleExpanded(message.key)"
1148
+ >
1149
+ <span aria-hidden="true">{{ toggleArrow(message) }}</span>
1150
+ <span>{{ props.expandLabel }}</span>
1151
+ </button>
1152
+ </div>
1153
+ </div>
1154
+ <div
1155
+ v-if="canCollapse(message) && !isMessageCollapsed(message)"
1156
+ data-slot="chat-collapse"
1157
+ class="flex justify-center"
1158
+ >
1159
+ <button
1160
+ data-slot="chat-expand"
1161
+ type="button"
1162
+ :aria-expanded="true"
1163
+ :aria-label="props.collapseLabel"
1164
+ :class="toggleButtonClass(message)"
1165
+ @click="toggleExpanded(message.key)"
1166
+ >
1167
+ <span aria-hidden="true">{{ toggleArrow(message) }}</span>
1168
+ <span>{{ toggleButtonLabel(message) }}</span>
1169
+ </button>
1170
+ </div>
1171
+ </div>
1172
+ <div
1173
+ v-if="hasActions(message)"
1174
+ data-slot="chat-actions"
1175
+ :data-actions-visibility="message.actionsVisibility"
1176
+ :class="actionClass(message)"
1177
+ >
1178
+ <div
1179
+ data-slot="chat-action-toolbar"
1180
+ :data-response-controls="hasResponseControls(message) ? '' : undefined"
1181
+ :aria-label="actionToolbarLabel(message)"
1182
+ :class="actionToolbarClass(message)"
1183
+ role="toolbar"
1184
+ >
1185
+ <template v-if="canPaginate(message)">
1186
+ <Button
1187
+ data-slot="chat-pagination-prev"
1188
+ type="button"
1189
+ variant="outline"
1190
+ size="icon-sm"
1191
+ :class="actionButtonClass()"
1192
+ :aria-label="props.previousVersionLabel"
1193
+ :title="props.previousVersionLabel"
1194
+ :disabled="message.regenerating || message.version <= 0"
1195
+ @click="handlePaginate(message, message.version - 1)"
1196
+ >
1197
+ <ChevronLeftIcon />
1198
+ </Button>
1199
+ <span
1200
+ data-slot="chat-pagination-status"
1201
+ data-action="pagination-status"
1202
+ :class="actionStatusClass()"
1203
+ >
1204
+ {{ paginationStatus(message) }}
1205
+ </span>
1206
+ <Button
1207
+ data-slot="chat-pagination-next"
1208
+ type="button"
1209
+ variant="outline"
1210
+ size="icon-sm"
1211
+ :class="actionButtonClass()"
1212
+ :aria-label="props.nextVersionLabel"
1213
+ :title="props.nextVersionLabel"
1214
+ :disabled="message.regenerating || message.version >= message.versionCount - 1"
1215
+ @click="handlePaginate(message, message.version + 1)"
1216
+ >
1217
+ <ChevronRightIcon />
1218
+ </Button>
1219
+ </template>
1220
+ <Button
1221
+ v-for="action in inlineSingleActionItems(message)"
1222
+ :key="action"
1223
+ :data-slot="actionSlot(action)"
1224
+ :data-action="action"
1225
+ type="button"
1226
+ variant="outline"
1227
+ size="icon-sm"
1228
+ :class="actionButtonClass()"
1229
+ :aria-label="actionLabel(action, message)"
1230
+ :title="actionLabel(action, message)"
1231
+ :disabled="isSingleActionDisabled(action, message)"
1232
+ @click="handleSingleAction(action, message)"
1233
+ >
1234
+ <Spinner
1235
+ v-if="action === 'regenerate' && message.regenerating"
1236
+ decorative
1237
+ />
1238
+ <component
1239
+ :is="actionIcons[action]"
1240
+ v-else
1241
+ />
1242
+ <span class="sr-only">{{ actionLabel(action, message) }}</span>
1243
+ </Button>
1244
+ <template v-if="hasActionMenu(message)">
1245
+ <DropdownMenu>
1246
+ <DropdownMenuTrigger as-child>
1247
+ <Button
1248
+ data-slot="chat-action-menu-trigger"
1249
+ type="button"
1250
+ variant="outline"
1251
+ size="icon-sm"
1252
+ :class="actionButtonClass()"
1253
+ :aria-label="props.moreActionsLabel"
1254
+ :title="props.moreActionsLabel"
1255
+ >
1256
+ <MoreHorizontalIcon />
1257
+ <span class="sr-only">{{ props.moreActionsLabel }}</span>
1258
+ </Button>
1259
+ </DropdownMenuTrigger>
1260
+ <DropdownMenuContent
1261
+ :align="message.side === 'right' ? 'end' : 'start'"
1262
+ class="min-w-44"
1263
+ >
1264
+ <DropdownMenuGroup>
1265
+ <DropdownMenuItem
1266
+ v-for="action in menuActionItems(message)"
1267
+ :key="action"
1268
+ :data-action="action"
1269
+ :disabled="isSingleActionDisabled(action, message)"
1270
+ @click="handleSingleAction(action, message)"
1271
+ >
1272
+ <component
1273
+ :is="actionIcons[action]"
1274
+ />
1275
+ {{ actionLabel(action, message) }}
1276
+ </DropdownMenuItem>
1277
+ </DropdownMenuGroup>
1278
+ </DropdownMenuContent>
1279
+ </DropdownMenu>
1280
+ </template>
1281
+ <slot
1282
+ v-if="slots[sideActionSlotName(message)]"
1283
+ :name="sideActionSlotName(message)"
1284
+ v-bind="actionSlotProps(message)"
1285
+ />
1286
+ <slot
1287
+ v-else
1288
+ name="message-actions"
1289
+ v-bind="actionSlotProps(message)"
1290
+ />
1291
+ </div>
1292
+ </div>
1293
+ </div>
1294
+ </template>
1295
+ </div>
1296
+ </section>
1297
+ </template>