@speakeasy-api/moonshine 2.0.0-alpha.1 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -23
  3. package/package.json +33 -50
  4. package/scripts/generate-utility-docs.js +324 -0
  5. package/src/assets/icons/external/github.svg +3 -0
  6. package/src/assets/icons/external/maven.svg +152 -0
  7. package/src/assets/icons/external/npm.svg +4 -0
  8. package/src/assets/icons/external/nuget.svg +5 -0
  9. package/src/assets/icons/external/packagist.svg +1 -0
  10. package/src/assets/icons/external/pypi.svg +182 -0
  11. package/src/assets/icons/external/rubygems.svg +14 -0
  12. package/src/assets/icons/external/terraform.svg +1 -0
  13. package/src/assets/icons/languages/csharp.svg +1 -0
  14. package/src/assets/icons/languages/go.svg +1 -0
  15. package/src/assets/icons/languages/java.svg +1 -0
  16. package/src/assets/icons/languages/json.svg +2 -0
  17. package/src/assets/icons/languages/php.svg +1 -0
  18. package/src/assets/icons/languages/postman.svg +3 -0
  19. package/src/assets/icons/languages/python.svg +1 -0
  20. package/src/assets/icons/languages/ruby.svg +1 -0
  21. package/src/assets/icons/languages/swift.svg +1 -0
  22. package/src/assets/icons/languages/terraform.svg +1 -0
  23. package/src/assets/icons/languages/typescript.svg +1 -0
  24. package/src/assets/icons/languages/unity.svg +1 -0
  25. package/src/base.css +12 -12
  26. package/src/components/AIChat/AIChatContainer.tsx +71 -0
  27. package/src/components/AIChat/AIChatMessage.tsx +135 -0
  28. package/src/components/AIChat/AIChatMessageComposer.tsx +175 -0
  29. package/src/components/AIChat/AIChatMessageList.tsx +34 -0
  30. package/src/components/AIChat/AIChatModelSelector.tsx +159 -0
  31. package/src/components/AIChat/componentsTypes.ts +36 -0
  32. package/src/components/AIChat/context.ts +15 -0
  33. package/src/components/AIChat/index.ts +12 -0
  34. package/src/components/AIChat/parts/AIChatMessageFilePart.tsx +129 -0
  35. package/src/components/AIChat/parts/AIChatMessageReasoningPart.tsx +23 -0
  36. package/src/components/AIChat/parts/AIChatMessageSourcePart.tsx +58 -0
  37. package/src/components/AIChat/parts/AIChatMessageTextPart.tsx +33 -0
  38. package/src/components/AIChat/parts/AIChatMessageToolInvocationPart.tsx +53 -0
  39. package/src/components/AIChat/parts/AIChatMessageToolPart.tsx +395 -0
  40. package/src/components/AIChat/parts/AIChatMessageToolResultPart.tsx +46 -0
  41. package/src/components/AIChat/toolCallApproval.ts +61 -0
  42. package/src/components/AIChat/types.ts +97 -0
  43. package/src/components/ActionBar/index.tsx +184 -0
  44. package/src/components/Alert/index.tsx +118 -0
  45. package/src/components/Alert/types.ts +12 -0
  46. package/src/components/AppLayout/context.tsx +31 -0
  47. package/src/components/AppLayout/index.tsx +550 -0
  48. package/src/components/AppLayout/provider.tsx +40 -0
  49. package/src/components/AppLayout/useAppLayoutKeys.ts +26 -0
  50. package/src/components/Badge/index.tsx +227 -0
  51. package/src/components/Button/index.tsx +531 -0
  52. package/src/components/Card/index.tsx +193 -0
  53. package/src/components/CodeEditorLayout/index.tsx +394 -0
  54. package/src/components/CodeEditorLayout/styles.module.css +8 -0
  55. package/src/components/CodeHighlight/Pre.tsx +63 -0
  56. package/src/components/CodePlayground/index.tsx +411 -0
  57. package/src/components/CodeSnippet/codeSnippet.css +97 -0
  58. package/src/components/CodeSnippet/index.tsx +224 -0
  59. package/src/components/Combobox/index.tsx +193 -0
  60. package/src/components/Command/index.tsx +152 -0
  61. package/src/components/Container/index.tsx +31 -0
  62. package/src/components/ContextDropdown/index.tsx +150 -0
  63. package/src/components/Dialog/index.tsx +123 -0
  64. package/src/components/DragNDrop/DragNDropArea.tsx +30 -0
  65. package/src/components/DragNDrop/DragOverlay.tsx +4 -0
  66. package/src/components/DragNDrop/Draggable.tsx +97 -0
  67. package/src/components/DragNDrop/Droppable.tsx +51 -0
  68. package/src/components/Dropdown/index.tsx +201 -0
  69. package/src/components/ExternalPill/index.tsx +58 -0
  70. package/src/components/Facepile/index.tsx +309 -0
  71. package/src/components/GradientCircle/gradientCircle.css +34 -0
  72. package/src/components/GradientCircle/index.tsx +143 -0
  73. package/src/components/Grid/index.tsx +150 -0
  74. package/src/components/Heading/index.tsx +54 -0
  75. package/src/components/HighlightedText/index.tsx +152 -0
  76. package/src/components/Icon/customIcons/createCustomLucideIcon.ts +25 -0
  77. package/src/components/Icon/customIcons/gems.ts +26 -0
  78. package/{dist/go-CiWl_aXI.mjs → src/components/Icon/customIcons/go.ts} +21 -19
  79. package/src/components/Icon/customIcons/index.ts +11 -0
  80. package/{dist/maven-DhmnGXoB.mjs → src/components/Icon/customIcons/maven.ts} +17 -15
  81. package/src/components/Icon/customIcons/npm.ts +19 -0
  82. package/{dist/nuget-5a2icRS2.mjs → src/components/Icon/customIcons/nuget.ts} +17 -15
  83. package/src/components/Icon/customIcons/packagist.ts +124 -0
  84. package/{dist/pypi-DsuRYjdK.mjs → src/components/Icon/customIcons/pypi.ts} +16 -14
  85. package/src/components/Icon/index.tsx +83 -0
  86. package/src/components/Icon/isIconName.ts +10 -0
  87. package/src/components/Icon/names.ts +14 -0
  88. package/src/components/IconButton/index.tsx +51 -0
  89. package/src/components/Input/index.tsx +98 -0
  90. package/src/components/KeyHint/index.tsx +118 -0
  91. package/src/components/LanguageIndicator/index.tsx +68 -0
  92. package/src/components/Link/index.tsx +153 -0
  93. package/src/components/LoggedInUserMenu/index.tsx +116 -0
  94. package/src/components/Logo/Animated.tsx +191 -0
  95. package/src/components/Logo/index.tsx +17 -0
  96. package/src/components/Logo/speakeasy-logo.riv +0 -0
  97. package/src/components/Logo/svgs/index.tsx +126 -0
  98. package/src/components/Modal/index.tsx +104 -0
  99. package/src/components/PageHeader/index.tsx +227 -0
  100. package/src/components/PageHeader/styles.module.css +27 -0
  101. package/src/components/Popover/index.tsx +35 -0
  102. package/src/components/PromptInput/index.tsx +372 -0
  103. package/src/components/PullRequestLink/index.tsx +64 -0
  104. package/src/components/ResizablePanel/index.tsx +119 -0
  105. package/src/components/Score/index.module.css +32 -0
  106. package/src/components/Score/index.tsx +268 -0
  107. package/src/components/ScrollArea/index.tsx +48 -0
  108. package/src/components/SegmentedButton/index.module.css +19 -0
  109. package/src/components/SegmentedButton/index.tsx +101 -0
  110. package/src/components/Select/index.tsx +159 -0
  111. package/src/components/Separator/index.tsx +23 -0
  112. package/src/components/Skeleton/index.tsx +61 -0
  113. package/src/components/Skeleton/skeleton.css +52 -0
  114. package/src/components/Stack/index.tsx +137 -0
  115. package/src/components/Subnav/index.tsx +315 -0
  116. package/src/components/Switch/index.tsx +29 -0
  117. package/src/components/Table/context/context.tsx +19 -0
  118. package/src/components/Table/context/tableProvider.tsx +39 -0
  119. package/src/components/Table/index.tsx +707 -0
  120. package/src/components/Table/styles.module.css +25 -0
  121. package/src/components/Tabs/index.tsx +87 -0
  122. package/src/components/TargetLanguageIcon/index.tsx +84 -0
  123. package/src/components/Text/index.tsx +59 -0
  124. package/src/components/ThemeSwitcher/index.tsx +118 -0
  125. package/src/components/Timeline/index.tsx +290 -0
  126. package/src/components/Tooltip/index.tsx +41 -0
  127. package/src/components/UserAvatar/index.tsx +87 -0
  128. package/src/components/UserAvatar/sizeMap.ts +12 -0
  129. package/src/components/Wizard/index.tsx +208 -0
  130. package/src/components/Wizard/types.ts +17 -0
  131. package/src/components/WorkspaceSelector/CreateOrg.tsx +95 -0
  132. package/src/components/WorkspaceSelector/CreateWorkspace.tsx +196 -0
  133. package/src/components/WorkspaceSelector/OrgList.tsx +115 -0
  134. package/src/components/WorkspaceSelector/OrgSelector.tsx +207 -0
  135. package/src/components/WorkspaceSelector/RecentWorkspaces.tsx +83 -0
  136. package/src/components/WorkspaceSelector/ScrollingList.tsx +84 -0
  137. package/src/components/WorkspaceSelector/SearchBox.tsx +40 -0
  138. package/src/components/WorkspaceSelector/WorkspaceItem.tsx +37 -0
  139. package/src/components/WorkspaceSelector/WorkspaceList.tsx +107 -0
  140. package/src/components/WorkspaceSelector/index.tsx +400 -0
  141. package/src/components/WorkspaceSelector/styles.css +74 -0
  142. package/src/components/__beta__/CLIWizard/index.tsx +357 -0
  143. package/src/components/__beta__/CLIWizard/terminal-command.tsx +108 -0
  144. package/src/components/__beta__/CLIWizard/terminal.tsx +83 -0
  145. package/src/components/__beta__/README.md +3 -0
  146. package/src/components/index.mdx +38 -0
  147. package/src/context/ConfigContext.tsx +43 -0
  148. package/src/context/ModalContext.tsx +118 -0
  149. package/src/context/theme.ts +1 -0
  150. package/src/hooks/useAppLayout.ts +10 -0
  151. package/src/hooks/useConfig.ts +10 -0
  152. package/src/hooks/useIsMounted.ts +13 -0
  153. package/src/hooks/useModal.tsx +10 -0
  154. package/src/hooks/useTailwindBreakpoint.ts +47 -0
  155. package/src/hooks/useTheme.ts +13 -0
  156. package/src/index.ts +234 -0
  157. package/src/lib/assert.ts +9 -0
  158. package/src/lib/codeUtils.ts +177 -0
  159. package/src/lib/debounce.ts +9 -0
  160. package/src/lib/responsiveMappers.ts +69 -0
  161. package/src/lib/responsiveUtils.ts +23 -0
  162. package/src/lib/storybookUtils.tsx +26 -0
  163. package/src/lib/typeUtils.ts +109 -0
  164. package/src/lib/utils.ts +85 -0
  165. package/src/styles/codeSyntax.css +59 -0
  166. package/src/styles/globals.css +51 -0
  167. package/src/types.ts +200 -0
  168. package/src/utilities.css +347 -6
  169. package/src/vite-env.d.ts +6 -0
  170. package/types/utilities.d.ts +43 -1
  171. package/dist/components/AIChat/AIChatContainer.d.ts +0 -25
  172. package/dist/components/AIChat/AIChatMessage.d.ts +0 -19
  173. package/dist/components/AIChat/AIChatMessageComposer.d.ts +0 -22
  174. package/dist/components/AIChat/AIChatMessageList.d.ts +0 -6
  175. package/dist/components/AIChat/AIChatModelSelector.d.ts +0 -14
  176. package/dist/components/AIChat/componentsTypes.d.ts +0 -11
  177. package/dist/components/AIChat/context.d.ts +0 -3
  178. package/dist/components/AIChat/index.d.ts +0 -12
  179. package/dist/components/AIChat/parts/AIChatMessageFilePart.d.ts +0 -7
  180. package/dist/components/AIChat/parts/AIChatMessageReasoningPart.d.ts +0 -5
  181. package/dist/components/AIChat/parts/AIChatMessageSourcePart.d.ts +0 -9
  182. package/dist/components/AIChat/parts/AIChatMessageTextPart.d.ts +0 -5
  183. package/dist/components/AIChat/parts/AIChatMessageToolInvocationPart.d.ts +0 -6
  184. package/dist/components/AIChat/parts/AIChatMessageToolPart.d.ts +0 -33
  185. package/dist/components/AIChat/parts/AIChatMessageToolResultPart.d.ts +0 -5
  186. package/dist/components/AIChat/toolCallApproval.d.ts +0 -15
  187. package/dist/components/AIChat/types.d.ts +0 -78
  188. package/dist/components/ActionBar/index.d.ts +0 -36
  189. package/dist/components/Alert/index.d.ts +0 -18
  190. package/dist/components/Alert/types.d.ts +0 -4
  191. package/dist/components/Badge/index.d.ts +0 -10
  192. package/dist/components/Button/index.d.ts +0 -11
  193. package/dist/components/Card/index.d.ts +0 -47
  194. package/dist/components/CodeEditorLayout/index.d.ts +0 -101
  195. package/dist/components/CodePlayground/index.d.ts +0 -108
  196. package/dist/components/CodePlayground/lineNumbers.d.ts +0 -2
  197. package/dist/components/CodePlayground/tokenTransitions.d.ts +0 -2
  198. package/dist/components/CodePlayground/wordWrap.d.ts +0 -2
  199. package/dist/components/CodeSnippet/index.d.ts +0 -50
  200. package/dist/components/Combobox/index.d.ts +0 -35
  201. package/dist/components/Command/index.d.ts +0 -80
  202. package/dist/components/Container/index.d.ts +0 -9
  203. package/dist/components/ContextDropdown/index.d.ts +0 -7
  204. package/dist/components/ContextDropdown/provider.d.ts +0 -22
  205. package/dist/components/ContextDropdown/useModal.d.ts +0 -11
  206. package/dist/components/Dialog/index.d.ts +0 -19
  207. package/dist/components/DragNDrop/DragNDropArea.d.ts +0 -8
  208. package/dist/components/DragNDrop/DragOverlay.d.ts +0 -1
  209. package/dist/components/DragNDrop/Draggable.d.ts +0 -29
  210. package/dist/components/DragNDrop/Droppable.d.ts +0 -28
  211. package/dist/components/Dropdown/index.d.ts +0 -27
  212. package/dist/components/ExternalPill/index.d.ts +0 -12
  213. package/dist/components/Facepile/index.d.ts +0 -16
  214. package/dist/components/GradientCircle/index.d.ts +0 -10
  215. package/dist/components/Grid/index.d.ts +0 -80
  216. package/dist/components/Heading/index.d.ts +0 -12
  217. package/dist/components/HighlightedText/index.d.ts +0 -19
  218. package/dist/components/Icon/customIcons/createCustomLucideIcon.d.ts +0 -3
  219. package/dist/components/Icon/customIcons/gems.d.ts +0 -2
  220. package/dist/components/Icon/customIcons/go.d.ts +0 -2
  221. package/dist/components/Icon/customIcons/index.d.ts +0 -10
  222. package/dist/components/Icon/customIcons/maven.d.ts +0 -2
  223. package/dist/components/Icon/customIcons/npm.d.ts +0 -2
  224. package/dist/components/Icon/customIcons/nuget.d.ts +0 -2
  225. package/dist/components/Icon/customIcons/packagist.d.ts +0 -2
  226. package/dist/components/Icon/customIcons/pypi.d.ts +0 -2
  227. package/dist/components/Icon/index.d.ts +0 -10
  228. package/dist/components/Icon/isIconName.d.ts +0 -2
  229. package/dist/components/Icon/names.d.ts +0 -6
  230. package/dist/components/Input/index.d.ts +0 -8
  231. package/dist/components/KeyHint/index.d.ts +0 -16
  232. package/dist/components/LanguageIndicator/index.d.ts +0 -7
  233. package/dist/components/Link/index.d.ts +0 -19
  234. package/dist/components/LoggedInUserMenu/index.d.ts +0 -17
  235. package/dist/components/Logo/Animated.d.ts +0 -7
  236. package/dist/components/Logo/index.d.ts +0 -7
  237. package/dist/components/Logo/svgs/index.d.ts +0 -6
  238. package/dist/components/Navbar/Slim.d.ts +0 -33
  239. package/dist/components/Navbar/index.d.ts +0 -15
  240. package/dist/components/PageHeader/index.d.ts +0 -45
  241. package/dist/components/Popover/index.d.ts +0 -8
  242. package/dist/components/PromptInput/index.d.ts +0 -55
  243. package/dist/components/PullRequestLink/index.d.ts +0 -10
  244. package/dist/components/ResizablePanel/index.d.ts +0 -26
  245. package/dist/components/Score/index.d.ts +0 -37
  246. package/dist/components/ScrollArea/index.d.ts +0 -5
  247. package/dist/components/Select/index.d.ts +0 -13
  248. package/dist/components/Separator/index.d.ts +0 -6
  249. package/dist/components/Skeleton/index.d.ts +0 -27
  250. package/dist/components/Stack/index.d.ts +0 -33
  251. package/dist/components/Subnav/index.d.ts +0 -12
  252. package/dist/components/Switch/index.d.ts +0 -4
  253. package/dist/components/Table/context/context.d.ts +0 -8
  254. package/dist/components/Table/context/tableProvider.d.ts +0 -6
  255. package/dist/components/Table/index.d.ts +0 -94
  256. package/dist/components/Tabs/index.d.ts +0 -21
  257. package/dist/components/TargetLanguageIcon/index.d.ts +0 -7
  258. package/dist/components/Text/index.d.ts +0 -19
  259. package/dist/components/ThemeSwitcher/index.d.ts +0 -5
  260. package/dist/components/Tooltip/index.d.ts +0 -8
  261. package/dist/components/UserAvatar/index.d.ts +0 -9
  262. package/dist/components/UserAvatar/sizeMap.d.ts +0 -3
  263. package/dist/components/Wizard/index.d.ts +0 -19
  264. package/dist/components/Wizard/types.d.ts +0 -15
  265. package/dist/components/WorkspaceSelector/CreateOrg.d.ts +0 -6
  266. package/dist/components/WorkspaceSelector/CreateWorkspace.d.ts +0 -17
  267. package/dist/components/WorkspaceSelector/OrgList.d.ts +0 -11
  268. package/dist/components/WorkspaceSelector/OrgSelector.d.ts +0 -13
  269. package/dist/components/WorkspaceSelector/RecentWorkspaces.d.ts +0 -11
  270. package/dist/components/WorkspaceSelector/ScrollingList.d.ts +0 -21
  271. package/dist/components/WorkspaceSelector/SearchBox.d.ts +0 -9
  272. package/dist/components/WorkspaceSelector/WorkspaceItem.d.ts +0 -9
  273. package/dist/components/WorkspaceSelector/WorkspaceList.d.ts +0 -10
  274. package/dist/components/WorkspaceSelector/index.d.ts +0 -34
  275. package/dist/components/__beta__/CLIWizard/index.d.ts +0 -21
  276. package/dist/components/__beta__/CLIWizard/terminal-command.d.ts +0 -19
  277. package/dist/components/__beta__/CLIWizard/terminal.d.ts +0 -26
  278. package/dist/context/ConfigContext.d.ts +0 -18
  279. package/dist/context/theme.d.ts +0 -1
  280. package/dist/createCustomLucideIcon-YlrRX5h9.mjs +0 -19
  281. package/dist/createCustomLucideIcon-YlrRX5h9.mjs.map +0 -1
  282. package/dist/gems-BcsO9cXq.mjs +0 -24
  283. package/dist/gems-BcsO9cXq.mjs.map +0 -1
  284. package/dist/github-kgjMtfE7.mjs +0 -11
  285. package/dist/github-kgjMtfE7.mjs.map +0 -1
  286. package/dist/go-CiWl_aXI.mjs.map +0 -1
  287. package/dist/hooks/useConfig.d.ts +0 -2
  288. package/dist/hooks/useIsMounted.d.ts +0 -1
  289. package/dist/hooks/useTailwindBreakpoint.d.ts +0 -3
  290. package/dist/hooks/useTheme.d.ts +0 -6
  291. package/dist/index-COXZ9O-g.mjs +0 -50882
  292. package/dist/index-COXZ9O-g.mjs.map +0 -1
  293. package/dist/index.d.ts +0 -73
  294. package/dist/lib/assert.d.ts +0 -2
  295. package/dist/lib/codeUtils.d.ts +0 -35
  296. package/dist/lib/debounce.d.ts +0 -1
  297. package/dist/lib/responsiveMappers.d.ts +0 -10
  298. package/dist/lib/responsiveUtils.d.ts +0 -3
  299. package/dist/lib/storybookUtils.d.ts +0 -5
  300. package/dist/lib/typeUtils.d.ts +0 -24
  301. package/dist/lib/utils.d.ts +0 -23
  302. package/dist/lucide-icons-BDw0imyx.mjs +0 -28054
  303. package/dist/lucide-icons-BDw0imyx.mjs.map +0 -1
  304. package/dist/maven-DhmnGXoB.mjs.map +0 -1
  305. package/dist/maven-W_nkSDNW.mjs +0 -107
  306. package/dist/maven-W_nkSDNW.mjs.map +0 -1
  307. package/dist/moonshine.es.js +0 -114
  308. package/dist/moonshine.es.js.map +0 -1
  309. package/dist/npm-BWTcVvFH.mjs +0 -11
  310. package/dist/npm-BWTcVvFH.mjs.map +0 -1
  311. package/dist/npm-CvQ4GKW4.mjs +0 -17
  312. package/dist/npm-CvQ4GKW4.mjs.map +0 -1
  313. package/dist/nuget-5a2icRS2.mjs.map +0 -1
  314. package/dist/nuget-CV5HU1JR.mjs +0 -11
  315. package/dist/nuget-CV5HU1JR.mjs.map +0 -1
  316. package/dist/packagist-CET6q9hi.mjs +0 -118
  317. package/dist/packagist-CET6q9hi.mjs.map +0 -1
  318. package/dist/packagist-D01fn9N_.mjs +0 -11
  319. package/dist/packagist-D01fn9N_.mjs.map +0 -1
  320. package/dist/pypi-DLh6kIJe.mjs +0 -11
  321. package/dist/pypi-DLh6kIJe.mjs.map +0 -1
  322. package/dist/pypi-DsuRYjdK.mjs.map +0 -1
  323. package/dist/rubygems-DeiNjcDV.mjs +0 -11
  324. package/dist/rubygems-DeiNjcDV.mjs.map +0 -1
  325. package/dist/speakeasy-logo-ByBTXLWb.mjs +0 -5
  326. package/dist/speakeasy-logo-ByBTXLWb.mjs.map +0 -1
  327. package/dist/style.css +0 -1
  328. package/dist/terraform-C4aktQ0o.mjs +0 -11
  329. package/dist/terraform-C4aktQ0o.mjs.map +0 -1
  330. package/dist/types.d.ts +0 -80
@@ -0,0 +1,175 @@
1
+ import React, { ReactNode, useEffect, useRef, useState } from 'react'
2
+ import { cn } from '../../lib/utils'
3
+ import { IconButton } from '../IconButton'
4
+ import { Icon } from '../Icon'
5
+ import { AIChatModelSelector } from './AIChatModelSelector'
6
+ import {
7
+ DefaultComponents,
8
+ FcOrClassName,
9
+ renderComponent,
10
+ } from './componentsTypes'
11
+ import { useAIChat } from './context'
12
+
13
+ export interface AIChatMessageComposerProps {
14
+ className?: string
15
+ components?: Partial<AIChatMessageComposerComponents>
16
+ }
17
+
18
+ export type AIChatMessageComposerComponents = {
19
+ submitButton: FcOrClassName<{
20
+ disabled?: boolean
21
+ type: 'submit'
22
+ }>
23
+ modelSelector: FcOrClassName<{
24
+ model: string
25
+ onModelChange: (model: string) => void
26
+ availableModels: { label: string; value: string }[]
27
+ }>
28
+ additionalActions: ReactNode
29
+ }
30
+
31
+ const defaultComponents: DefaultComponents<AIChatMessageComposerComponents> = {
32
+ submitButton: ({
33
+ disabled,
34
+ type,
35
+ className,
36
+ }: {
37
+ disabled?: boolean
38
+ type: 'submit'
39
+ className: string
40
+ }) => (
41
+ <IconButton
42
+ type={type}
43
+ disabled={disabled}
44
+ variant="secondary"
45
+ icon={<Icon name="arrow-up" className="size-4" />}
46
+ aria-label="Send message"
47
+ className={cn('h-8 w-8 rounded-[7px]', className)}
48
+ />
49
+ ),
50
+ modelSelector: ({
51
+ model,
52
+ onModelChange,
53
+ availableModels,
54
+ className,
55
+ }: {
56
+ model: string
57
+ onModelChange: (model: string) => void
58
+ availableModels: { label: string; value: string }[]
59
+ className: string
60
+ }) => (
61
+ <AIChatModelSelector
62
+ modelSelector={{ model, onModelChange, availableModels }}
63
+ /* Inner border radius = outer border radius (rounded-lg = 8px) - border width (1px) = 7px */
64
+ className={cn('rounded-[7px]', className)}
65
+ />
66
+ ),
67
+ additionalActions: null,
68
+ }
69
+
70
+ export function AIChatMessageComposer({
71
+ className,
72
+ components,
73
+ }: AIChatMessageComposerProps) {
74
+ const {
75
+ onSendMessage,
76
+ isLoading,
77
+ model,
78
+ onModelChange,
79
+ availableModels,
80
+ initialInput,
81
+ } = useAIChat()
82
+ const [message, setMessage] = useState(initialInput || '')
83
+ const [isFocused, setIsFocused] = useState(false)
84
+ const textareaRef = useRef<HTMLTextAreaElement>(null)
85
+
86
+ const adjustHeight = () => {
87
+ const textarea = textareaRef.current
88
+ if (!textarea) return
89
+ textarea.style.height = 'auto'
90
+ textarea.style.height = `${textarea.scrollHeight}px`
91
+ }
92
+
93
+ // Initialize height on mount and adjust on content change
94
+ useEffect(() => {
95
+ adjustHeight()
96
+ }, [message])
97
+
98
+ // Set initial input only once when component mounts with an initialInput
99
+ useEffect(() => {
100
+ if (initialInput) {
101
+ setMessage(initialInput)
102
+ }
103
+ }, [])
104
+
105
+ const handleSubmit = (e?: React.FormEvent) => {
106
+ e?.preventDefault()
107
+ if (message.trim() && onSendMessage && !isLoading) {
108
+ onSendMessage(message)
109
+ setMessage('')
110
+ if (textareaRef.current) {
111
+ textareaRef.current.style.height = 'auto'
112
+ }
113
+ }
114
+ }
115
+
116
+ const handleKeyDown = (e: React.KeyboardEvent) => {
117
+ if (e.key === 'Enter' && !e.shiftKey) {
118
+ e.preventDefault()
119
+ handleSubmit()
120
+ }
121
+ }
122
+
123
+ return (
124
+ <form onSubmit={handleSubmit} className={cn('mt-auto p-4', className)}>
125
+ <div className="flex flex-col">
126
+ <div
127
+ className={cn(
128
+ 'flex flex-col overflow-hidden rounded-lg border transition-shadow',
129
+ isFocused && 'ring-1 ring-offset-1'
130
+ )}
131
+ >
132
+ <textarea
133
+ ref={textareaRef}
134
+ value={message}
135
+ onChange={(e) => setMessage(e.target.value)}
136
+ onKeyDown={handleKeyDown}
137
+ onFocus={() => setIsFocused(true)}
138
+ onBlur={() => setIsFocused(false)}
139
+ placeholder="Send a message..."
140
+ rows={1}
141
+ className={cn(
142
+ 'max-h-[300px] min-h-[44px] w-full resize-none bg-transparent px-4 pt-3',
143
+ // 'text-neutral-50 placeholder:text-neutral-400',
144
+ 'focus:outline-none',
145
+ 'text-sm'
146
+ )}
147
+ disabled={isLoading}
148
+ />
149
+ <div className="flex items-center justify-between p-3 pt-2">
150
+ <div className="flex items-center gap-2">
151
+ {availableModels &&
152
+ model &&
153
+ onModelChange &&
154
+ renderComponent(
155
+ defaultComponents,
156
+ components,
157
+ 'modelSelector',
158
+ {
159
+ model,
160
+ onModelChange,
161
+ availableModels,
162
+ }
163
+ )}
164
+ {components?.additionalActions}
165
+ </div>
166
+ {renderComponent(defaultComponents, components, 'submitButton', {
167
+ disabled: !message.trim() || isLoading,
168
+ type: 'submit',
169
+ })}
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </form>
174
+ )
175
+ }
@@ -0,0 +1,34 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { cn } from '../../lib/utils'
3
+ import { AIChatMessage, AIChatMessageComponents } from './AIChatMessage'
4
+ import { useAIChat } from './context'
5
+
6
+ export interface AIChatMessageListProps {
7
+ components?: Partial<AIChatMessageComponents>
8
+ className?: string
9
+ }
10
+
11
+ export function AIChatMessageList({
12
+ components,
13
+ className,
14
+ }: AIChatMessageListProps) {
15
+ const { messages } = useAIChat()
16
+ const messagesEndRef = useRef<HTMLDivElement>(null)
17
+
18
+ useEffect(() => {
19
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
20
+ }, [messages])
21
+
22
+ return (
23
+ <ul role="log" className={cn('h-full overflow-y-auto', className)}>
24
+ {messages.map((message) => (
25
+ <AIChatMessage
26
+ key={message.id}
27
+ message={message}
28
+ components={components}
29
+ />
30
+ ))}
31
+ <div ref={messagesEndRef} />
32
+ </ul>
33
+ )
34
+ }
@@ -0,0 +1,159 @@
1
+ import { useState, useRef, useEffect } from 'react'
2
+ import { cn } from '../../lib/utils'
3
+ import { Icon } from '../Icon'
4
+ import { Popover, PopoverContent, PopoverTrigger } from '../Popover'
5
+ import { AnimatePresence, motion, Variants } from 'motion/react'
6
+ import React from 'react'
7
+
8
+ interface ModelSelectorConfig {
9
+ model: string
10
+ onModelChange: (v: string) => void
11
+ availableModels: { label: string; value: string }[]
12
+ }
13
+
14
+ interface AIChatModelSelectorProps {
15
+ modelSelector?: ModelSelectorConfig
16
+ className?: string
17
+ }
18
+
19
+ const AnimatedLabel = React.memo(function AnimatedLabel({
20
+ label,
21
+ className = '',
22
+ }: {
23
+ label: string
24
+ className?: string
25
+ }) {
26
+ const parent = {
27
+ visible: {
28
+ transition: { staggerChildren: 0.03 },
29
+ },
30
+ }
31
+
32
+ const char: Variants = {
33
+ hidden: { opacity: 0, y: 10, filter: 'blur(4px)' },
34
+ visible: {
35
+ opacity: 1,
36
+ y: 0,
37
+ filter: 'blur(0px)',
38
+ transition: { duration: 0.32, ease: [0.4, 0, 0.2, 1] },
39
+ },
40
+ exit: { opacity: 0, y: -10, filter: 'blur(4px)' },
41
+ }
42
+
43
+ return (
44
+ <motion.span
45
+ className={cn('inline-flex', className)}
46
+ variants={parent}
47
+ initial="hidden"
48
+ animate="visible"
49
+ exit="hidden"
50
+ style={{ willChange: 'transform, opacity' }}
51
+ >
52
+ {label.split('').map((c, i) => (
53
+ <motion.span
54
+ key={`${label}-${i}`}
55
+ variants={char}
56
+ className="block"
57
+ style={{ willChange: 'transform, opacity' }}
58
+ >
59
+ {c === ' ' ? '\u00A0' : c}
60
+ </motion.span>
61
+ ))}
62
+ </motion.span>
63
+ )
64
+ })
65
+
66
+ export function AIChatModelSelector({
67
+ modelSelector,
68
+ className,
69
+ }: AIChatModelSelectorProps) {
70
+ if (!modelSelector) return null
71
+ const { model, onModelChange, availableModels } = modelSelector
72
+ const [open, setOpen] = useState(false)
73
+ const current = availableModels.find((m) => m.value === model) || {
74
+ label: 'Unknown',
75
+ value: '',
76
+ }
77
+
78
+ const prevLabelRef = useRef(current.label)
79
+ const [outgoingLabel, setOutgoingLabel] = useState<string | null>(null)
80
+
81
+ useEffect(() => {
82
+ if (current.label !== prevLabelRef.current) {
83
+ const prev = prevLabelRef.current
84
+ prevLabelRef.current = current.label
85
+
86
+ setOutgoingLabel(prev)
87
+
88
+ requestAnimationFrame(() => {
89
+ setOutgoingLabel(null)
90
+ })
91
+ }
92
+ }, [current.label])
93
+
94
+ return (
95
+ <Popover open={open} onOpenChange={setOpen}>
96
+ <PopoverTrigger asChild>
97
+ <button
98
+ type="button"
99
+ className={cn(
100
+ 'flex h-8 items-center rounded-md px-1 text-xs',
101
+ className
102
+ )}
103
+ >
104
+ <motion.div
105
+ layout
106
+ transition={{ type: 'spring', stiffness: 320, damping: 50 }}
107
+ className="relative inline-flex items-center gap-1 overflow-hidden whitespace-nowrap"
108
+ >
109
+ <AnimatePresence initial={false}>
110
+ {outgoingLabel && (
111
+ <motion.div
112
+ initial={{ opacity: 1 }}
113
+ animate={{ opacity: 1 }}
114
+ exit={{ opacity: 0 }}
115
+ transition={{ duration: 0.18 }}
116
+ className="pointer-events-none absolute top-0 left-0 select-none"
117
+ >
118
+ <AnimatedLabel key={outgoingLabel} label={outgoingLabel} />
119
+ </motion.div>
120
+ )}
121
+ </AnimatePresence>
122
+
123
+ <AnimatedLabel key={current.value} label={current.label} />
124
+
125
+ <span>
126
+ <Icon name="chevron-down" className="size-3" />
127
+ </span>
128
+ </motion.div>
129
+ </button>
130
+ </PopoverTrigger>
131
+
132
+ <PopoverContent
133
+ align="start"
134
+ className="w-[220px] rounded-[7px] border p-0 shadow-lg"
135
+ side="top"
136
+ sideOffset={8}
137
+ >
138
+ <div className="flex flex-col py-1">
139
+ {availableModels.map((m) => (
140
+ <button
141
+ key={m.value}
142
+ type="button"
143
+ onClick={() => {
144
+ onModelChange(m.value)
145
+ setOpen(false)
146
+ }}
147
+ className={cn(
148
+ 'flex items-center justify-between gap-2 px-5 py-2 text-xs transition-colors'
149
+ )}
150
+ >
151
+ <span>{m.label}</span>
152
+ {m.value === model && <Icon name="check" className="size-3" />}
153
+ </button>
154
+ ))}
155
+ </div>
156
+ </PopoverContent>
157
+ </Popover>
158
+ )
159
+ }
@@ -0,0 +1,36 @@
1
+ import { createElement, FunctionComponent, ReactNode } from 'react'
2
+
3
+ type ClassName = string
4
+
5
+ export type FcOrClassName<P> = FunctionComponent<P> | ClassName
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ export type BaseComponents = Record<string, FcOrClassName<any> | ReactNode>
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ type FC = FunctionComponent<any>
11
+
12
+ export type DefaultComponents<T extends BaseComponents> = {
13
+ [key in keyof T]: T[key] extends FcOrClassName<infer P>
14
+ ? FunctionComponent<P & { className: string }>
15
+ : T[key]
16
+ }
17
+
18
+ export const renderComponent = <T extends BaseComponents, K extends keyof T>(
19
+ defaultComponents: DefaultComponents<T>,
20
+ userComponents: Partial<T> | undefined,
21
+ key: K,
22
+ props: T[K] extends FcOrClassName<infer P> ? P : never
23
+ ): ReactNode => {
24
+ const userComponent = userComponents?.[key]
25
+ if (typeof userComponent === 'function') {
26
+ const Component = userComponent as FC
27
+ return createElement(Component, props)
28
+ }
29
+
30
+ // Default component handling
31
+ const DefaultComponent = defaultComponents[key] as FC
32
+ return createElement(DefaultComponent, {
33
+ ...(props as object),
34
+ className: typeof userComponent === 'string' ? userComponent : '',
35
+ })
36
+ }
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { AIChatContextValue } from './types'
3
+
4
+ export const AIChatContext = createContext<AIChatContextValue>({
5
+ messages: [],
6
+ isLoading: false,
7
+ })
8
+
9
+ export const useAIChat = () => {
10
+ const context = useContext(AIChatContext)
11
+ if (!context) {
12
+ throw new Error('useAIChat must be used within an AIChatProvider')
13
+ }
14
+ return context
15
+ }
@@ -0,0 +1,12 @@
1
+ export * from './AIChatContainer'
2
+ export * from './AIChatMessageList'
3
+ export * from './AIChatMessage'
4
+ export * from './AIChatMessageComposer'
5
+ export * from './parts/AIChatMessageTextPart'
6
+ export * from './parts/AIChatMessageReasoningPart'
7
+ export * from './parts/AIChatMessageToolInvocationPart'
8
+ export * from './parts/AIChatMessageToolResultPart'
9
+ export * from './parts/AIChatMessageFilePart'
10
+ export * from './parts/AIChatMessageSourcePart'
11
+ export * from './types'
12
+ export * from './context'
@@ -0,0 +1,129 @@
1
+ import { cn } from '../../../lib/utils'
2
+ import { Text } from '../../Text/index'
3
+ import type { BasePartProps } from '../types'
4
+
5
+ export interface AIChatMessageFilePartProps extends BasePartProps {
6
+ mimeType: string
7
+ data: string
8
+ fileName?: string
9
+ }
10
+
11
+ export function AIChatMessageFilePart({
12
+ mimeType,
13
+ data,
14
+ fileName,
15
+ className,
16
+ }: AIChatMessageFilePartProps) {
17
+ // Determine file icon based on mime type
18
+ const getFileIcon = () => {
19
+ if (mimeType.startsWith('image/')) {
20
+ return (
21
+ <svg
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ className="h-5 w-5"
24
+ fill="none"
25
+ viewBox="0 0 24 24"
26
+ stroke="currentColor"
27
+ >
28
+ <path
29
+ strokeLinecap="round"
30
+ strokeLinejoin="round"
31
+ strokeWidth={2}
32
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
33
+ />
34
+ </svg>
35
+ )
36
+ } else if (mimeType.startsWith('video/')) {
37
+ return (
38
+ <svg
39
+ xmlns="http://www.w3.org/2000/svg"
40
+ className="h-5 w-5"
41
+ fill="none"
42
+ viewBox="0 0 24 24"
43
+ stroke="currentColor"
44
+ >
45
+ <path
46
+ strokeLinecap="round"
47
+ strokeLinejoin="round"
48
+ strokeWidth={2}
49
+ d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
50
+ />
51
+ </svg>
52
+ )
53
+ } else if (mimeType.startsWith('audio/')) {
54
+ return (
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ className="h-5 w-5"
58
+ fill="none"
59
+ viewBox="0 0 24 24"
60
+ stroke="currentColor"
61
+ >
62
+ <path
63
+ strokeLinecap="round"
64
+ strokeLinejoin="round"
65
+ strokeWidth={2}
66
+ d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"
67
+ />
68
+ </svg>
69
+ )
70
+ } else {
71
+ return (
72
+ <svg
73
+ xmlns="http://www.w3.org/2000/svg"
74
+ className="h-5 w-5"
75
+ fill="none"
76
+ viewBox="0 0 24 24"
77
+ stroke="currentColor"
78
+ >
79
+ <path
80
+ strokeLinecap="round"
81
+ strokeLinejoin="round"
82
+ strokeWidth={2}
83
+ d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
84
+ />
85
+ </svg>
86
+ )
87
+ }
88
+ }
89
+
90
+ // Create a download link for the file
91
+ const downloadUrl = `data:${mimeType};base64,${data}`
92
+
93
+ return (
94
+ <div
95
+ className={cn(
96
+ 'rounded-md border border-neutral-600 bg-neutral-800 p-3',
97
+ className
98
+ )}
99
+ >
100
+ <div className="mb-2 flex items-center gap-2">
101
+ <div className="text-neutral-400">{getFileIcon()}</div>
102
+ <Text variant="sm" className="font-medium text-neutral-50">
103
+ {fileName || 'File Attachment'}
104
+ </Text>
105
+ </div>
106
+ <a
107
+ href={downloadUrl}
108
+ download={fileName}
109
+ className="text-brand-yellow-300 hover:text-brand-yellow-200 inline-flex items-center gap-1"
110
+ >
111
+ <svg
112
+ xmlns="http://www.w3.org/2000/svg"
113
+ className="h-4 w-4"
114
+ fill="none"
115
+ viewBox="0 0 24 24"
116
+ stroke="currentColor"
117
+ >
118
+ <path
119
+ strokeLinecap="round"
120
+ strokeLinejoin="round"
121
+ strokeWidth={2}
122
+ d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
123
+ />
124
+ </svg>
125
+ <Text variant="sm">Download</Text>
126
+ </a>
127
+ </div>
128
+ )
129
+ }
@@ -0,0 +1,23 @@
1
+ import { cn } from '../../../lib/utils'
2
+ import { Text } from '../../Text'
3
+ import type { BasePartProps } from '../types'
4
+
5
+ export interface AIChatMessageReasoningPartProps extends BasePartProps {
6
+ reasoning: string
7
+ }
8
+
9
+ export function AIChatMessageReasoningPart({
10
+ reasoning,
11
+ className,
12
+ }: AIChatMessageReasoningPartProps) {
13
+ return (
14
+ <div className={cn('rounded-md bg-neutral-800 p-3', className)}>
15
+ <Text variant="sm" className="mb-1 font-medium text-neutral-50">
16
+ Reasoning:
17
+ </Text>
18
+ <Text variant="sm" className="whitespace-pre-wrap text-neutral-200">
19
+ {reasoning}
20
+ </Text>
21
+ </div>
22
+ )
23
+ }
@@ -0,0 +1,58 @@
1
+ import { cn } from '../../../lib/utils'
2
+ import { Text } from '../../Text'
3
+ import type { BasePartProps } from '../types'
4
+
5
+ export interface AIChatMessageSourcePartProps extends BasePartProps {
6
+ source: {
7
+ id: string
8
+ url: string
9
+ title?: string
10
+ }
11
+ }
12
+
13
+ export function AIChatMessageSourcePart({
14
+ source,
15
+ className,
16
+ }: AIChatMessageSourcePartProps) {
17
+ return (
18
+ <div
19
+ className={cn(
20
+ 'rounded-md border border-neutral-600 bg-neutral-800 p-3',
21
+ className
22
+ )}
23
+ >
24
+ <div className="mb-1 flex items-center gap-2">
25
+ <svg
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ className="h-4 w-4 text-neutral-400"
28
+ fill="none"
29
+ viewBox="0 0 24 24"
30
+ stroke="currentColor"
31
+ >
32
+ <path
33
+ strokeLinecap="round"
34
+ strokeLinejoin="round"
35
+ strokeWidth={2}
36
+ d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
37
+ />
38
+ </svg>
39
+ <Text variant="sm" className="font-medium text-neutral-50">
40
+ Source Reference
41
+ </Text>
42
+ </div>
43
+ <div className="mt-2">
44
+ <a
45
+ href={source.url}
46
+ target="_blank"
47
+ rel="noopener noreferrer"
48
+ className="text-brand-yellow-300 hover:text-brand-yellow-200 block hover:underline"
49
+ >
50
+ <Text variant="sm">{source.title || source.url}</Text>
51
+ </a>
52
+ <Text variant="sm" className="mt-1 block text-neutral-400">
53
+ ID: {source.id}
54
+ </Text>
55
+ </div>
56
+ </div>
57
+ )
58
+ }
@@ -0,0 +1,33 @@
1
+ import { cn } from '../../../lib/utils'
2
+ import type { BasePartProps } from '../types'
3
+ import ReactMarkdown from 'react-markdown'
4
+ import remarkGfm from 'remark-gfm'
5
+
6
+ export interface AIChatMessageTextPartProps extends BasePartProps {
7
+ text: string
8
+ }
9
+
10
+ // The line-heights here have been left as the prose defaults for now, ideally they should be adjusted to match the design system
11
+ export function AIChatMessageTextPart({
12
+ text,
13
+ className,
14
+ }: AIChatMessageTextPartProps) {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ 'prose prose-sm dark:prose-invert min-w-0 break-words',
19
+ 'prose-headings:font-light prose-headings:tracking-[0.0015em]',
20
+ 'prose-h1:text-heading-xl',
21
+ 'prose-h2:text-heading-lg',
22
+ 'prose-h3:text-heading-md',
23
+ 'prose-h4:text-heading-sm',
24
+ 'prose-h5:text-heading-xs',
25
+ 'prose-p:font-normal prose-p:tracking-[0.0025em]',
26
+ 'prose-a:text-link prose-a:font-normal prose-a:underline-offset-4 prose-a:decoration-1 prose-a:tracking-[0.0025em]',
27
+ className
28
+ )}
29
+ >
30
+ <ReactMarkdown remarkPlugins={[remarkGfm]}>{text}</ReactMarkdown>
31
+ </div>
32
+ )
33
+ }