@mitodl/smoot-design 0.0.0-1967631

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 (261) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +35 -0
  3. package/dist/bundles/remoteTutorDrawer.es.js +38609 -0
  4. package/dist/bundles/remoteTutorDrawer.es.js.map +1 -0
  5. package/dist/bundles/remoteTutorDrawer.umd.js +241 -0
  6. package/dist/bundles/remoteTutorDrawer.umd.js.map +1 -0
  7. package/dist/cjs/VERSION.d.ts +6 -0
  8. package/dist/cjs/VERSION.js +9 -0
  9. package/dist/cjs/ai.d.ts +3 -0
  10. package/dist/cjs/ai.js +9 -0
  11. package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.d.ts +9 -0
  12. package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.js +87 -0
  13. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +56 -0
  14. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +283 -0
  15. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +17 -0
  16. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +302 -0
  17. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts +1 -0
  18. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +247 -0
  19. package/dist/cjs/bundles/remoteTutorDrawer.d.ts +7 -0
  20. package/dist/cjs/bundles/remoteTutorDrawer.js +43 -0
  21. package/dist/cjs/components/AiChat/AiChat.d.ts +5 -0
  22. package/dist/cjs/components/AiChat/AiChat.js +233 -0
  23. package/dist/cjs/components/AiChat/AiChat.stories.d.ts +21 -0
  24. package/dist/cjs/components/AiChat/AiChat.stories.js +257 -0
  25. package/dist/cjs/components/AiChat/AiChat.test.d.ts +1 -0
  26. package/dist/cjs/components/AiChat/AiChat.test.js +211 -0
  27. package/dist/cjs/components/AiChat/AiChatContext.d.ts +26 -0
  28. package/dist/cjs/components/AiChat/AiChatContext.js +105 -0
  29. package/dist/cjs/components/AiChat/AiChatContext.stories.d.ts +14 -0
  30. package/dist/cjs/components/AiChat/AiChatContext.stories.js +75 -0
  31. package/dist/cjs/components/AiChat/ChatTitle.d.ts +8 -0
  32. package/dist/cjs/components/AiChat/ChatTitle.js +43 -0
  33. package/dist/cjs/components/AiChat/EntryScreen.d.ts +11 -0
  34. package/dist/cjs/components/AiChat/EntryScreen.js +123 -0
  35. package/dist/cjs/components/AiChat/Markdown.d.ts +7 -0
  36. package/dist/cjs/components/AiChat/Markdown.js +14 -0
  37. package/dist/cjs/components/AiChat/TimLogo.d.ts +5 -0
  38. package/dist/cjs/components/AiChat/TimLogo.js +15 -0
  39. package/dist/cjs/components/AiChat/test-utils/api.d.ts +2 -0
  40. package/dist/cjs/components/AiChat/test-utils/api.js +164 -0
  41. package/dist/cjs/components/AiChat/types.d.ts +91 -0
  42. package/dist/cjs/components/AiChat/types.js +3 -0
  43. package/dist/cjs/components/AiChat/utils.d.ts +9 -0
  44. package/dist/cjs/components/AiChat/utils.js +41 -0
  45. package/dist/cjs/components/Alert/Alert.d.ts +15 -0
  46. package/dist/cjs/components/Alert/Alert.js +62 -0
  47. package/dist/cjs/components/Alert/Alert.stories.d.ts +8 -0
  48. package/dist/cjs/components/Alert/Alert.stories.js +53 -0
  49. package/dist/cjs/components/Button/ActionButton.d.ts +30 -0
  50. package/dist/cjs/components/Button/ActionButton.js +73 -0
  51. package/dist/cjs/components/Button/ActionButton.stories.d.ts +15 -0
  52. package/dist/cjs/components/Button/ActionButton.stories.js +113 -0
  53. package/dist/cjs/components/Button/Button.d.ts +54 -0
  54. package/dist/cjs/components/Button/Button.js +240 -0
  55. package/dist/cjs/components/Button/Button.stories.d.ts +17 -0
  56. package/dist/cjs/components/Button/Button.stories.js +135 -0
  57. package/dist/cjs/components/Button/Button.test.d.ts +1 -0
  58. package/dist/cjs/components/Button/Button.test.js +46 -0
  59. package/dist/cjs/components/ImageAdapter/ImageAdapter.d.ts +23 -0
  60. package/dist/cjs/components/ImageAdapter/ImageAdapter.js +30 -0
  61. package/dist/cjs/components/Input/Input.d.ts +116 -0
  62. package/dist/cjs/components/Input/Input.js +237 -0
  63. package/dist/cjs/components/Input/Input.stories.d.ts +19 -0
  64. package/dist/cjs/components/Input/Input.stories.js +135 -0
  65. package/dist/cjs/components/Input/Input.test.d.ts +1 -0
  66. package/dist/cjs/components/Input/Input.test.js +32 -0
  67. package/dist/cjs/components/LinkAdapter/LinkAdapter.d.ts +23 -0
  68. package/dist/cjs/components/LinkAdapter/LinkAdapter.js +34 -0
  69. package/dist/cjs/components/ScrollSnap/ScrollSnap.d.ts +19 -0
  70. package/dist/cjs/components/ScrollSnap/ScrollSnap.js +59 -0
  71. package/dist/cjs/components/ScrollSnap/ScrollSnap.stories.d.ts +6 -0
  72. package/dist/cjs/components/ScrollSnap/ScrollSnap.stories.js +43 -0
  73. package/dist/cjs/components/ScrollSnap/useScrollSnap.d.ts +6 -0
  74. package/dist/cjs/components/ScrollSnap/useScrollSnap.js +36 -0
  75. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.d.ts +25 -0
  76. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.js +43 -0
  77. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.stories.d.ts +6 -0
  78. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.stories.js +44 -0
  79. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.test.d.ts +1 -0
  80. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.test.js +62 -0
  81. package/dist/cjs/components/TabButtons/TabButtonList.d.ts +25 -0
  82. package/dist/cjs/components/TabButtons/TabButtonList.js +97 -0
  83. package/dist/cjs/components/TabButtons/TabButtonList.stories.d.ts +24 -0
  84. package/dist/cjs/components/TabButtons/TabButtonList.stories.js +139 -0
  85. package/dist/cjs/components/TextField/TextField.d.ts +29 -0
  86. package/dist/cjs/components/TextField/TextField.js +33 -0
  87. package/dist/cjs/components/TextField/TextField.stories.d.ts +10 -0
  88. package/dist/cjs/components/TextField/TextField.stories.js +136 -0
  89. package/dist/cjs/components/TextField/TextField.test.d.ts +1 -0
  90. package/dist/cjs/components/TextField/TextField.test.js +77 -0
  91. package/dist/cjs/components/ThemeProvider/ThemeProvider.d.ts +21 -0
  92. package/dist/cjs/components/ThemeProvider/ThemeProvider.js +86 -0
  93. package/dist/cjs/components/ThemeProvider/ThemeProvider.stories.d.ts +63 -0
  94. package/dist/cjs/components/ThemeProvider/ThemeProvider.stories.js +102 -0
  95. package/dist/cjs/components/ThemeProvider/Typography.stories.d.ts +39 -0
  96. package/dist/cjs/components/ThemeProvider/Typography.stories.js +65 -0
  97. package/dist/cjs/components/ThemeProvider/breakpoints.d.ts +4 -0
  98. package/dist/cjs/components/ThemeProvider/breakpoints.js +19 -0
  99. package/dist/cjs/components/ThemeProvider/buttons.d.ts +7 -0
  100. package/dist/cjs/components/ThemeProvider/buttons.js +20 -0
  101. package/dist/cjs/components/ThemeProvider/chips.d.ts +3 -0
  102. package/dist/cjs/components/ThemeProvider/chips.js +154 -0
  103. package/dist/cjs/components/ThemeProvider/colors.d.ts +32 -0
  104. package/dist/cjs/components/ThemeProvider/colors.js +35 -0
  105. package/dist/cjs/components/ThemeProvider/typography.d.ts +18 -0
  106. package/dist/cjs/components/ThemeProvider/typography.js +173 -0
  107. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.d.ts +24 -0
  108. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.js +33 -0
  109. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.stories.d.ts +6 -0
  110. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.stories.js +13 -0
  111. package/dist/cjs/components/internal/FormHelpers/FormHelpers.d.ts +39 -0
  112. package/dist/cjs/components/internal/FormHelpers/FormHelpers.js +78 -0
  113. package/dist/cjs/components/internal/FormHelpers/FormHelpers.test.d.ts +1 -0
  114. package/dist/cjs/components/internal/FormHelpers/FormHelpers.test.js +93 -0
  115. package/dist/cjs/index.d.ts +16 -0
  116. package/dist/cjs/index.js +30 -0
  117. package/dist/cjs/jest-setup.d.ts +1 -0
  118. package/dist/cjs/jest-setup.js +18 -0
  119. package/dist/cjs/jsdom-extended.d.ts +6 -0
  120. package/dist/cjs/jsdom-extended.js +14 -0
  121. package/dist/cjs/story-utils/index.d.ts +6 -0
  122. package/dist/cjs/story-utils/index.js +17 -0
  123. package/dist/cjs/utils/composeRefs.d.ts +7 -0
  124. package/dist/cjs/utils/composeRefs.js +20 -0
  125. package/dist/cjs/utils/composeRefs.test.d.ts +1 -0
  126. package/dist/cjs/utils/composeRefs.test.js +19 -0
  127. package/dist/cjs/utils/useDevCheckStable.d.ts +8 -0
  128. package/dist/cjs/utils/useDevCheckStable.js +29 -0
  129. package/dist/cjs/utils/useInterval.d.ts +7 -0
  130. package/dist/cjs/utils/useInterval.js +25 -0
  131. package/dist/esm/VERSION.d.ts +6 -0
  132. package/dist/esm/VERSION.js +6 -0
  133. package/dist/esm/ai.d.ts +3 -0
  134. package/dist/esm/ai.js +2 -0
  135. package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.d.ts +9 -0
  136. package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.js +83 -0
  137. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +56 -0
  138. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +280 -0
  139. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +17 -0
  140. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +299 -0
  141. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts +1 -0
  142. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +245 -0
  143. package/dist/esm/bundles/remoteTutorDrawer.d.ts +7 -0
  144. package/dist/esm/bundles/remoteTutorDrawer.js +40 -0
  145. package/dist/esm/components/AiChat/AiChat.d.ts +5 -0
  146. package/dist/esm/components/AiChat/AiChat.js +229 -0
  147. package/dist/esm/components/AiChat/AiChat.stories.d.ts +21 -0
  148. package/dist/esm/components/AiChat/AiChat.stories.js +254 -0
  149. package/dist/esm/components/AiChat/AiChat.test.d.ts +1 -0
  150. package/dist/esm/components/AiChat/AiChat.test.js +209 -0
  151. package/dist/esm/components/AiChat/AiChatContext.d.ts +26 -0
  152. package/dist/esm/components/AiChat/AiChatContext.js +101 -0
  153. package/dist/esm/components/AiChat/AiChatContext.stories.d.ts +14 -0
  154. package/dist/esm/components/AiChat/AiChatContext.stories.js +72 -0
  155. package/dist/esm/components/AiChat/ChatTitle.d.ts +8 -0
  156. package/dist/esm/components/AiChat/ChatTitle.js +40 -0
  157. package/dist/esm/components/AiChat/EntryScreen.d.ts +11 -0
  158. package/dist/esm/components/AiChat/EntryScreen.js +120 -0
  159. package/dist/esm/components/AiChat/Markdown.d.ts +7 -0
  160. package/dist/esm/components/AiChat/Markdown.js +12 -0
  161. package/dist/esm/components/AiChat/TimLogo.d.ts +5 -0
  162. package/dist/esm/components/AiChat/TimLogo.js +13 -0
  163. package/dist/esm/components/AiChat/test-utils/api.d.ts +2 -0
  164. package/dist/esm/components/AiChat/test-utils/api.js +161 -0
  165. package/dist/esm/components/AiChat/types.d.ts +91 -0
  166. package/dist/esm/components/AiChat/types.js +2 -0
  167. package/dist/esm/components/AiChat/utils.d.ts +9 -0
  168. package/dist/esm/components/AiChat/utils.js +38 -0
  169. package/dist/esm/components/Alert/Alert.d.ts +15 -0
  170. package/dist/esm/components/Alert/Alert.js +59 -0
  171. package/dist/esm/components/Alert/Alert.stories.d.ts +8 -0
  172. package/dist/esm/components/Alert/Alert.stories.js +50 -0
  173. package/dist/esm/components/Button/ActionButton.d.ts +30 -0
  174. package/dist/esm/components/Button/ActionButton.js +68 -0
  175. package/dist/esm/components/Button/ActionButton.stories.d.ts +15 -0
  176. package/dist/esm/components/Button/ActionButton.stories.js +110 -0
  177. package/dist/esm/components/Button/Button.d.ts +54 -0
  178. package/dist/esm/components/Button/Button.js +232 -0
  179. package/dist/esm/components/Button/Button.stories.d.ts +17 -0
  180. package/dist/esm/components/Button/Button.stories.js +132 -0
  181. package/dist/esm/components/Button/Button.test.d.ts +1 -0
  182. package/dist/esm/components/Button/Button.test.js +44 -0
  183. package/dist/esm/components/ImageAdapter/ImageAdapter.d.ts +23 -0
  184. package/dist/esm/components/ImageAdapter/ImageAdapter.js +27 -0
  185. package/dist/esm/components/Input/Input.d.ts +116 -0
  186. package/dist/esm/components/Input/Input.js +232 -0
  187. package/dist/esm/components/Input/Input.stories.d.ts +19 -0
  188. package/dist/esm/components/Input/Input.stories.js +132 -0
  189. package/dist/esm/components/Input/Input.test.d.ts +1 -0
  190. package/dist/esm/components/Input/Input.test.js +30 -0
  191. package/dist/esm/components/LinkAdapter/LinkAdapter.d.ts +23 -0
  192. package/dist/esm/components/LinkAdapter/LinkAdapter.js +31 -0
  193. package/dist/esm/components/ScrollSnap/ScrollSnap.d.ts +19 -0
  194. package/dist/esm/components/ScrollSnap/ScrollSnap.js +56 -0
  195. package/dist/esm/components/ScrollSnap/ScrollSnap.stories.d.ts +6 -0
  196. package/dist/esm/components/ScrollSnap/ScrollSnap.stories.js +40 -0
  197. package/dist/esm/components/ScrollSnap/useScrollSnap.d.ts +6 -0
  198. package/dist/esm/components/ScrollSnap/useScrollSnap.js +33 -0
  199. package/dist/esm/components/SrAnnouncer/SrAnnouncer.d.ts +25 -0
  200. package/dist/esm/components/SrAnnouncer/SrAnnouncer.js +40 -0
  201. package/dist/esm/components/SrAnnouncer/SrAnnouncer.stories.d.ts +6 -0
  202. package/dist/esm/components/SrAnnouncer/SrAnnouncer.stories.js +41 -0
  203. package/dist/esm/components/SrAnnouncer/SrAnnouncer.test.d.ts +1 -0
  204. package/dist/esm/components/SrAnnouncer/SrAnnouncer.test.js +60 -0
  205. package/dist/esm/components/TabButtons/TabButtonList.d.ts +25 -0
  206. package/dist/esm/components/TabButtons/TabButtonList.js +92 -0
  207. package/dist/esm/components/TabButtons/TabButtonList.stories.d.ts +24 -0
  208. package/dist/esm/components/TabButtons/TabButtonList.stories.js +136 -0
  209. package/dist/esm/components/TextField/TextField.d.ts +29 -0
  210. package/dist/esm/components/TextField/TextField.js +30 -0
  211. package/dist/esm/components/TextField/TextField.stories.d.ts +10 -0
  212. package/dist/esm/components/TextField/TextField.stories.js +133 -0
  213. package/dist/esm/components/TextField/TextField.test.d.ts +1 -0
  214. package/dist/esm/components/TextField/TextField.test.js +75 -0
  215. package/dist/esm/components/ThemeProvider/ThemeProvider.d.ts +21 -0
  216. package/dist/esm/components/ThemeProvider/ThemeProvider.js +82 -0
  217. package/dist/esm/components/ThemeProvider/ThemeProvider.stories.d.ts +63 -0
  218. package/dist/esm/components/ThemeProvider/ThemeProvider.stories.js +99 -0
  219. package/dist/esm/components/ThemeProvider/Typography.stories.d.ts +39 -0
  220. package/dist/esm/components/ThemeProvider/Typography.stories.js +62 -0
  221. package/dist/esm/components/ThemeProvider/breakpoints.d.ts +4 -0
  222. package/dist/esm/components/ThemeProvider/breakpoints.js +15 -0
  223. package/dist/esm/components/ThemeProvider/buttons.d.ts +7 -0
  224. package/dist/esm/components/ThemeProvider/buttons.js +17 -0
  225. package/dist/esm/components/ThemeProvider/chips.d.ts +3 -0
  226. package/dist/esm/components/ThemeProvider/chips.js +151 -0
  227. package/dist/esm/components/ThemeProvider/colors.d.ts +32 -0
  228. package/dist/esm/components/ThemeProvider/colors.js +32 -0
  229. package/dist/esm/components/ThemeProvider/typography.d.ts +18 -0
  230. package/dist/esm/components/ThemeProvider/typography.js +167 -0
  231. package/dist/esm/components/VisuallyHidden/VisuallyHidden.d.ts +24 -0
  232. package/dist/esm/components/VisuallyHidden/VisuallyHidden.js +30 -0
  233. package/dist/esm/components/VisuallyHidden/VisuallyHidden.stories.d.ts +6 -0
  234. package/dist/esm/components/VisuallyHidden/VisuallyHidden.stories.js +10 -0
  235. package/dist/esm/components/internal/FormHelpers/FormHelpers.d.ts +39 -0
  236. package/dist/esm/components/internal/FormHelpers/FormHelpers.js +73 -0
  237. package/dist/esm/components/internal/FormHelpers/FormHelpers.test.d.ts +1 -0
  238. package/dist/esm/components/internal/FormHelpers/FormHelpers.test.js +91 -0
  239. package/dist/esm/index.d.ts +16 -0
  240. package/dist/esm/index.js +11 -0
  241. package/dist/esm/jest-setup.d.ts +1 -0
  242. package/dist/esm/jest-setup.js +16 -0
  243. package/dist/esm/jsdom-extended.d.ts +6 -0
  244. package/dist/esm/jsdom-extended.js +12 -0
  245. package/dist/esm/story-utils/index.d.ts +6 -0
  246. package/dist/esm/story-utils/index.js +13 -0
  247. package/dist/esm/utils/composeRefs.d.ts +7 -0
  248. package/dist/esm/utils/composeRefs.js +17 -0
  249. package/dist/esm/utils/composeRefs.test.d.ts +1 -0
  250. package/dist/esm/utils/composeRefs.test.js +17 -0
  251. package/dist/esm/utils/useDevCheckStable.d.ts +8 -0
  252. package/dist/esm/utils/useDevCheckStable.js +26 -0
  253. package/dist/esm/utils/useInterval.d.ts +7 -0
  254. package/dist/esm/utils/useInterval.js +22 -0
  255. package/dist/tsconfig.tsbuildinfo +1 -0
  256. package/dist/type-augmentation/TypescriptDocs.mdx +17 -0
  257. package/dist/type-augmentation/imports.d.ts +3 -0
  258. package/dist/type-augmentation/index.d.ts +3 -0
  259. package/dist/type-augmentation/theme.d.ts +105 -0
  260. package/dist/type-augmentation/typography.d.ts +54 -0
  261. package/package.json +158 -0
@@ -0,0 +1,254 @@
1
+ import * as React from "react";
2
+ import { AiChat } from "./AiChat";
3
+ import styled from "@emotion/styled";
4
+ import { handlers } from "./test-utils/api";
5
+ import { useEffect, useRef, useState } from "react";
6
+ import { MathJaxContext } from "better-react-mathjax";
7
+ const TEST_API_STREAMING = "http://localhost:4567/streaming";
8
+ const TEST_API_JSON = "http://localhost:4567/json";
9
+ const INITIAL_MESSAGES = [
10
+ {
11
+ content: "Hi! What are you interested in learning about?",
12
+ role: "assistant",
13
+ },
14
+ {
15
+ content: "I need to brush up on my Calculus",
16
+ role: "user",
17
+ },
18
+ {
19
+ content: "Great! Do you want to start with the basics, like limits and derivatives, or jump into more advanced topics like integrals and series? Let me know how I can help!",
20
+ role: "assistant",
21
+ },
22
+ ];
23
+ const STARTERS = [
24
+ { content: "I'm interested in quantum computing" },
25
+ { content: "I want to understand global warming. " },
26
+ { content: "I am curious about AI applications for business" },
27
+ ];
28
+ const Container = styled.div({
29
+ width: "100%",
30
+ height: "800px",
31
+ });
32
+ const meta = {
33
+ title: "smoot-design/AI/AiChat",
34
+ component: AiChat,
35
+ parameters: {
36
+ msw: { handlers },
37
+ },
38
+ render: (args) => React.createElement(AiChat, Object.assign({}, args)),
39
+ decorators: (Story, context) => {
40
+ return (React.createElement(MathJaxContext, null,
41
+ React.createElement(Container, null,
42
+ React.createElement(Story, { key: String(context.args.entryScreenEnabled) }))));
43
+ },
44
+ args: {
45
+ entryScreenTitle: "What do you want to learn from MIT?",
46
+ requestOpts: { apiUrl: TEST_API_STREAMING },
47
+ conversationStarters: STARTERS,
48
+ askTimTitle: "to recommend a course",
49
+ },
50
+ argTypes: {
51
+ conversationStarters: {
52
+ control: { type: "object", disable: true },
53
+ },
54
+ initialMessages: {
55
+ control: { type: "object", disable: true },
56
+ },
57
+ requestOpts: {
58
+ control: { type: "object", disable: true },
59
+ table: { readonly: true }, // See above
60
+ },
61
+ },
62
+ };
63
+ export default meta;
64
+ export const StreamingResponses = {};
65
+ /**
66
+ * Here `AiChat` is used with a non-streaming JSON API. The JSON is converted
67
+ * to text via `parseContent`.
68
+ */
69
+ export const JsonResponses = {
70
+ args: {
71
+ requestOpts: { apiUrl: TEST_API_JSON },
72
+ initialMessages: INITIAL_MESSAGES,
73
+ entryScreenEnabled: false,
74
+ parseContent: (content) => {
75
+ return JSON.parse(content).message;
76
+ },
77
+ },
78
+ };
79
+ const DEMO_MARKDOWN = `This shows default markdown styling. Here's are some lists:
80
+ - Item 1
81
+ - Item 2 - an unordered list (bullets)
82
+ - Point A
83
+ - Point B
84
+ - Item 3 - an ordered list (numbers)
85
+ 1. Item 3.1
86
+ 2. Item 3.2
87
+ 3. Item 3.3
88
+
89
+ Sometimes, unordered lists are nested within ordered lists:
90
+ 1. Item 1
91
+ - Item 1.1
92
+ - Item 1.2
93
+ 2. Item 2
94
+ - Item 2.1
95
+ - Item 2.2
96
+
97
+ Sometimes, ordered lists are nested within unordered lists:
98
+ - Item 1
99
+ 1. Item 1.1
100
+ 2. Item 1.2
101
+ - Item 2
102
+ 1. Item 2.1
103
+ 2. Item 2.2
104
+
105
+ Here is a longer paragraph and **bold text** and *italic text*. Lorem ipsum dolor sit amet, consectetur adipiscing elit
106
+ sed do eiusmod tempor [incididunt](https://mit.edu) ut labore et dolore magna aliqua. Ut enim ad minim veniam.
107
+
108
+ And some inline code, \`\`<inline></inline>\`\` and code block:
109
+ \`\`\`
110
+ def f(x):
111
+ print(x)
112
+ \`\`\`
113
+
114
+ And some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
115
+
116
+ And some block math:
117
+ \n
118
+
119
+ $$
120
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
121
+ $$
122
+
123
+ Math is rendered using MathJax only if the useMathJax prop is set to true.
124
+ `;
125
+ /**
126
+ * This story shows the component's builtin markdown styling.
127
+ */
128
+ export const MarkdownStyling = {
129
+ args: {
130
+ requestOpts: { apiUrl: TEST_API_STREAMING },
131
+ entryScreenEnabled: false,
132
+ conversationStarters: [],
133
+ initialMessages: [
134
+ {
135
+ role: "assistant",
136
+ content: DEMO_MARKDOWN,
137
+ },
138
+ ],
139
+ useMathJax: true,
140
+ },
141
+ };
142
+ const ScrollComponent = (args) => {
143
+ const ref = useRef(null);
144
+ const [scrollElement, setScrollElement] = useState(null);
145
+ useEffect(() => {
146
+ setScrollElement(ref.current);
147
+ }, []);
148
+ return (React.createElement("div", { style: {
149
+ height: "800px",
150
+ overflowY: "scroll",
151
+ border: "2px solid #e0e6ea",
152
+ position: "relative",
153
+ }, ref: ref },
154
+ React.createElement(AiChat, Object.assign({}, args, { scrollElement: scrollElement }))));
155
+ };
156
+ /**
157
+ * Where a scrollable container exists in the including component, it can be passed in
158
+ * for the AiChat component to use in place of its own height constrained message container
159
+ * overflow scroll. This is useful for cases such as use inside a modal or drawer.
160
+ */
161
+ export const ScrollContainer = {
162
+ args: {
163
+ requestOpts: { apiUrl: TEST_API_STREAMING },
164
+ entryScreenEnabled: false,
165
+ conversationStarters: [],
166
+ initialMessages: [
167
+ {
168
+ content: "Hi! What are you interested in learning about?",
169
+ role: "assistant",
170
+ },
171
+ {
172
+ content: "I'm looking for a course in computer science, but I'm not sure where to start.",
173
+ role: "user",
174
+ },
175
+ {
176
+ content: "I can help you find the right course! Could you tell me about your background? Have you done any programming before, and what are your goals?",
177
+ role: "assistant",
178
+ },
179
+ {
180
+ content: "I've done some basic Python programming, and I'm interested in AI and machine learning. I work full-time though, so I need something flexible.",
181
+ role: "user",
182
+ },
183
+ {
184
+ content: "Given your background and interests, I have a few recommendations:\n\n1. 6.0001 - Introduction to Computer Science and Programming in Python\n2. 6.0002 - Introduction to Computational Thinking and Data Science\n3. 6.036 - Introduction to Machine Learning\n\nAll these courses offer flexible online options. Would you like more details about any of these?",
185
+ role: "assistant",
186
+ },
187
+ {
188
+ content: "Yes, could you tell me more about the machine learning course? What are the prerequisites?",
189
+ role: "user",
190
+ },
191
+ {
192
+ content: "6.036 (Introduction to Machine Learning) requires:\n\n- Basic Python programming\n- Linear algebra fundamentals\n- Basic probability and statistics\n- Calculus I\n\nThe course covers:\n- Supervised learning\n- Neural networks\n- Deep learning basics\n- 12 weeks of content\n- 10-12 hours per week commitment\n\nGiven your Python background, you might need to brush up on math concepts first. Would you like me to suggest some preparatory courses?",
193
+ role: "assistant",
194
+ },
195
+ {
196
+ content: "Yes, what math courses would you recommend I take first?",
197
+ role: "user",
198
+ },
199
+ {
200
+ content: "Here are the recommended math prerequisites:\n\n1. 18.01 - Single Variable Calculus\n2. 18.06 - Linear Algebra\n3. 6.041 - Probability\n\nYou can take these online through our OpenCourseWare platform. Each course takes about 8-12 weeks. Would you like to start with any of these? I can provide more details about the course format and materials.",
201
+ role: "assistant",
202
+ },
203
+ {
204
+ content: "How long would it take to complete all the prerequisites before starting the ML course?",
205
+ role: "user",
206
+ },
207
+ {
208
+ content: "If you study part-time (10-15 hours per week):\n\n- Calculus: 12 weeks\n- Linear Algebra: 10 weeks\n- Probability: 8 weeks\n\nYou could complete them in:\n1. Sequential order: 7-8 months total\n2. Parallel study (2 courses at a time): 4-5 months\n\nMany students take Calculus and Linear Algebra together. Would you like to see a suggested study schedule?",
209
+ role: "assistant",
210
+ },
211
+ {
212
+ content: "Yes, a study schedule would be helpful. Also, are there any costs involved?",
213
+ role: "user",
214
+ },
215
+ {
216
+ content: "Here's the breakdown:\n\nCosts:\n- OpenCourseWare materials: Free\n- Optional verified certificate: $49-99 per course\n- Full ML course with certificate: $149\n\nSuggested Schedule (15 hrs/week):\nMonths 1-3:\n- Calculus (8 hrs/week)\n- Linear Algebra (7 hrs/week)\n\nMonths 4-5:\n- Probability (10 hrs/week)\n\nMonths 6-8:\n- ML course (12 hrs/week)\n\nWould you like me to help you enroll in any of these courses?",
217
+ role: "assistant",
218
+ },
219
+ {
220
+ content: "Can I start with just the Calculus course first to see how it goes?",
221
+ role: "user",
222
+ },
223
+ {
224
+ content: "Absolutely! That's a smart approach. 18.01 (Single Variable Calculus) has:\n\n- Flexible start dates\n- Self-paced learning\n- Video lectures\n- Practice problems\n- Online discussion forums\n- 24/7 access to materials\n\nYou can enroll right now and start today. Would you like the enrollment link and first week's syllabus?",
225
+ role: "assistant",
226
+ },
227
+ {
228
+ content: "Yes, please share the enrollment link. Also, will I have access to any tutoring support?",
229
+ role: "user",
230
+ },
231
+ {
232
+ content: "Yes! You'll have several support options:\n\n1. Teaching Assistants (TAs) available via:\n - Discussion forums\n - Weekly online office hours\n - Email support\n\n2. Study Groups:\n - Optional virtual study groups\n - Peer-to-peer learning\n - Weekly problem-solving sessions\n\nHere's your enrollment link: [Course Registration Link]\n\nWould you like me to connect you with a TA to discuss your study plan?",
233
+ role: "assistant",
234
+ },
235
+ {
236
+ content: "Not right now, but I might later. What happens if I find the course too difficult?",
237
+ role: "user",
238
+ },
239
+ {
240
+ content: "Don't worry! We have several options if you find the course challenging:\n\n1. You can:\n - Pause your progress anytime\n - Retake modules as needed\n - Switch to a more basic math course\n - Get extra tutoring support\n\n2. We also offer a 2-week trial period with a full refund if you decide it's not the right fit.\n\nWould you like to start with some preliminary practice problems to assess your readiness?",
241
+ role: "assistant",
242
+ },
243
+ {
244
+ content: "Yes, that would be helpful. Can you send me some practice problems?",
245
+ role: "user",
246
+ },
247
+ {
248
+ content: "I'll send you our Calculus readiness assessment. It covers:\n\n- Basic algebra\n- Functions and graphs\n- Trigonometry basics\n- Pre-calculus concepts\n\nYou can access it here: [Assessment Link]\n\nTake your time with it - there's no time limit. Once you complete it, I can provide personalized recommendations for where to start. Would you like to try it now?",
249
+ role: "assistant",
250
+ },
251
+ ],
252
+ },
253
+ render: (args) => React.createElement(ScrollComponent, Object.assign({}, args)),
254
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,209 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ // This was giving false positives
11
+ /* eslint-disable testing-library/await-async-utils */
12
+ import { render, screen, waitFor } from "@testing-library/react";
13
+ import user from "@testing-library/user-event";
14
+ import { AiChat } from "./AiChat";
15
+ import { ThemeProvider } from "../ThemeProvider/ThemeProvider";
16
+ import * as React from "react";
17
+ import { faker } from "@faker-js/faker/locale/en";
18
+ import { http, HttpResponse } from "msw";
19
+ import { setupServer } from "msw/node";
20
+ const counter = jest.fn(); // use jest.fn as counter because it resets on each test
21
+ const API_URL = "http://localhost:4567/test";
22
+ const server = setupServer(http.post(API_URL, () => __awaiter(void 0, void 0, void 0, function* () {
23
+ const count = counter.mock.calls.length;
24
+ counter();
25
+ return HttpResponse.text(`AI Response ${count}`);
26
+ })));
27
+ beforeAll(() => server.listen());
28
+ afterEach(() => server.resetHandlers());
29
+ afterAll(() => server.close());
30
+ jest.mock("react-markdown", () => {
31
+ return {
32
+ __esModule: true,
33
+ default: ({ children }) => React.createElement("div", null, children),
34
+ };
35
+ });
36
+ const msg = {
37
+ ai: (text) => `Assistant said: ${text}`,
38
+ you: (text) => `You said: ${text}`,
39
+ };
40
+ const getMessages = () => {
41
+ return Array.from(document.querySelectorAll(".MitAiChat--message"));
42
+ };
43
+ const getConversationStarters = () => {
44
+ return Array.from(document.querySelectorAll("button.MitAiChat--conversationStarter"));
45
+ };
46
+ const whenCount = (cb, count) => __awaiter(void 0, void 0, void 0, function* () {
47
+ return yield waitFor(() => {
48
+ const result = cb();
49
+ expect(result).toHaveLength(count);
50
+ return result;
51
+ });
52
+ });
53
+ describe("AiChat", () => {
54
+ beforeEach(() => {
55
+ const MockObserverInstance = {
56
+ observe: jest.fn(),
57
+ unobserve: jest.fn(),
58
+ disconnect: jest.fn(),
59
+ };
60
+ global.ResizeObserver = jest
61
+ .fn()
62
+ .mockImplementation(() => MockObserverInstance);
63
+ });
64
+ const setup = (props = {}) => {
65
+ const initialMessages = [
66
+ { role: "assistant", content: faker.lorem.sentence() },
67
+ ];
68
+ const conversationStarters = [
69
+ { content: faker.lorem.sentence() },
70
+ { content: faker.lorem.sentence() },
71
+ ];
72
+ const view = render(React.createElement(AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl: API_URL }, placeholder: "Type a message...", entryScreenEnabled: false }, props)), { wrapper: ThemeProvider });
73
+ const rerender = (newProps) => {
74
+ view.rerender(React.createElement(AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl: API_URL }, entryScreenEnabled: false }, newProps)));
75
+ };
76
+ return { initialMessages, conversationStarters, rerender };
77
+ };
78
+ test("Clicking conversation starters and sending chats", () => __awaiter(void 0, void 0, void 0, function* () {
79
+ const { initialMessages, conversationStarters } = setup();
80
+ const scrollBy = jest.spyOn(HTMLElement.prototype, "scrollBy");
81
+ const initialMessageEls = getMessages();
82
+ expect(initialMessageEls.length).toBe(1);
83
+ expect(initialMessageEls[0]).toHaveTextContent(initialMessages[0].content);
84
+ const starterEls = getConversationStarters();
85
+ expect(starterEls.length).toBe(2);
86
+ expect(starterEls[0]).toHaveTextContent(conversationStarters[0].content);
87
+ expect(starterEls[1]).toHaveTextContent(conversationStarters[1].content);
88
+ const chosen = faker.helpers.arrayElement([0, 1]);
89
+ yield user.click(starterEls[chosen]);
90
+ expect(scrollBy).toHaveBeenCalled();
91
+ scrollBy.mockReset();
92
+ const messageEls = yield whenCount(getMessages, 3);
93
+ expect(messageEls[0]).toHaveTextContent(initialMessages[0].content);
94
+ expect(messageEls[1]).toHaveTextContent(conversationStarters[chosen].content);
95
+ expect(messageEls[2]).toHaveTextContent("AI Response 0");
96
+ yield user.click(screen.getByPlaceholderText("Type a message..."));
97
+ yield user.paste("User message");
98
+ yield user.click(screen.getByRole("button", { name: "Send" }));
99
+ expect(scrollBy).toHaveBeenCalled();
100
+ const afterSending = yield whenCount(getMessages, 5);
101
+ expect(afterSending[3]).toHaveTextContent("User message");
102
+ expect(afterSending[4]).toHaveTextContent("AI Response 1");
103
+ }));
104
+ test("Messages persist if chat has same chatId", () => __awaiter(void 0, void 0, void 0, function* () {
105
+ const { rerender } = setup({ chatId: "test-123" });
106
+ const starterEls = getConversationStarters();
107
+ const chosen = faker.helpers.arrayElement([0, 1]);
108
+ yield user.click(starterEls[chosen]);
109
+ yield whenCount(getMessages, 3);
110
+ // New chat ... starters should be shown
111
+ rerender({ chatId: "test-345" });
112
+ expect(getConversationStarters().length).toBeGreaterThan(0);
113
+ yield whenCount(getMessages, 1);
114
+ // existing chat ... starters should not be shown, messages should be restored
115
+ rerender({ chatId: "test-123" });
116
+ expect(getConversationStarters().length).toBe(0);
117
+ yield whenCount(getMessages, 3);
118
+ }));
119
+ test("transformBody is called before sending requests", () => __awaiter(void 0, void 0, void 0, function* () {
120
+ const mockFetch = jest.spyOn(window, "fetch");
121
+ const fakeBody = { message: faker.lorem.sentence() };
122
+ const transformBody = jest.fn(() => fakeBody);
123
+ const { initialMessages } = setup({
124
+ requestOpts: { apiUrl: API_URL, transformBody },
125
+ });
126
+ yield user.click(screen.getByPlaceholderText("Type a message..."));
127
+ yield user.paste("User message");
128
+ yield user.click(screen.getByRole("button", { name: "Send" }));
129
+ expect(transformBody).toHaveBeenCalledWith([
130
+ expect.objectContaining(initialMessages[0]),
131
+ expect.objectContaining({ content: "User message", role: "user" }),
132
+ ]);
133
+ expect(mockFetch).toHaveBeenCalledTimes(1);
134
+ expect(mockFetch).toHaveBeenCalledWith(API_URL, expect.objectContaining({
135
+ body: JSON.stringify(fakeBody),
136
+ }));
137
+ }));
138
+ test("parseContent is called on the API-received message content", () => __awaiter(void 0, void 0, void 0, function* () {
139
+ const fakeBody = { message: faker.lorem.sentence() };
140
+ const transformBody = jest.fn(() => fakeBody);
141
+ const { initialMessages, conversationStarters } = setup({
142
+ requestOpts: { apiUrl: API_URL, transformBody },
143
+ parseContent: jest.fn((content) => `Parsed: ${content}`),
144
+ });
145
+ yield user.click(getConversationStarters()[0]);
146
+ yield whenCount(getMessages, initialMessages.length + 2);
147
+ yield user.click(screen.getByPlaceholderText("Type a message..."));
148
+ yield user.paste("User message");
149
+ yield user.click(screen.getByRole("button", { name: "Send" }));
150
+ yield whenCount(getMessages, initialMessages.length + 4);
151
+ const messagesTexts = getMessages().map((el) => el.textContent);
152
+ expect(messagesTexts).toEqual([
153
+ msg.ai(initialMessages[0].content),
154
+ msg.you(conversationStarters[0].content),
155
+ msg.ai("Parsed: AI Response 0"),
156
+ msg.you("User message"),
157
+ msg.ai("Parsed: AI Response 1"),
158
+ ]);
159
+ }));
160
+ test("Passes extra attributes to root", () => {
161
+ const fakeBody = { message: faker.lorem.sentence() };
162
+ const transformBody = jest.fn(() => fakeBody);
163
+ setup({
164
+ requestOpts: { apiUrl: API_URL, transformBody },
165
+ parseContent: jest.fn((content) => `Parsed: ${content}`),
166
+ });
167
+ expect(screen.getByTestId("ai-chat")).toBeInTheDocument();
168
+ });
169
+ test("If the API returns an error, an alert is shown", () => __awaiter(void 0, void 0, void 0, function* () {
170
+ setup();
171
+ server.use(http.post(API_URL, () => __awaiter(void 0, void 0, void 0, function* () {
172
+ return new HttpResponse(null, { status: 500 });
173
+ })));
174
+ yield user.click(getConversationStarters()[0]);
175
+ const alert = yield screen.findByRole("alert");
176
+ expect(alert).toHaveTextContent("An unexpected error has occurred");
177
+ }));
178
+ test("Shows the entry screen if entryScreenEnabled is true", () => __awaiter(void 0, void 0, void 0, function* () {
179
+ setup({
180
+ entryScreenEnabled: true,
181
+ entryScreenTitle: "Entry Screen Title",
182
+ });
183
+ yield expect(screen.getByText("Entry Screen Title")).toBeInTheDocument();
184
+ }));
185
+ test("User can submit a prompt from the entry screen", () => __awaiter(void 0, void 0, void 0, function* () {
186
+ setup({
187
+ entryScreenEnabled: true,
188
+ entryScreenTitle: "Entry Screen Title",
189
+ initialMessages: [],
190
+ conversationStarters: [],
191
+ });
192
+ yield user.click(screen.getByRole("textbox"));
193
+ yield user.paste("User message");
194
+ yield user.click(screen.getByRole("button", { name: "Send" }));
195
+ const messages = getMessages();
196
+ expect(messages[0]).toHaveTextContent("User message");
197
+ }));
198
+ test("User can click starter on the entry screen to submit a prompt", () => __awaiter(void 0, void 0, void 0, function* () {
199
+ setup({
200
+ entryScreenEnabled: true,
201
+ entryScreenTitle: "Entry Screen Title",
202
+ initialMessages: [],
203
+ conversationStarters: [{ content: "Starter 1" }],
204
+ });
205
+ yield user.click(screen.getByRole("button", { name: "Starter 1" }));
206
+ const messages = getMessages();
207
+ expect(messages[0]).toHaveTextContent("Starter 1");
208
+ }));
209
+ });
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { UseChatHelpers } from "@ai-sdk/react";
3
+ import type { AiChatMessage, AiChatContextProps } from "./types";
4
+ /**
5
+ * All of `@ai-sdk/react`'s [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat)
6
+ * results, plus the initial messages.
7
+ */
8
+ type AiChatContextResult = UseChatHelpers & {
9
+ initialMessages: AiChatMessage[] | null;
10
+ };
11
+ /**
12
+ * Provides AiChatContext to its children. Within this provider, you can consume
13
+ * the AiChatContext using the `useAiChat` hook.
14
+ */
15
+ declare const AiChatProvider: React.FC<AiChatContextProps>;
16
+ /**
17
+ * Returns the AiChatContext, which includes all results from `@ai-sdk/react`'s
18
+ * [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook as
19
+ * well as the initial messages.
20
+ *
21
+ * In addition to customizing the fetcher, using a context allows us to avoid
22
+ * this issue https://github.com/vercel/ai/issues/3266 since the caller no
23
+ * longer needs to provide the initial messages.
24
+ */
25
+ declare const useAiChat: () => AiChatContextResult;
26
+ export { useAiChat, AiChatProvider };
@@ -0,0 +1,101 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ import * as React from "react";
22
+ import { useChat } from "@ai-sdk/react";
23
+ import { useMemo, createContext } from "react";
24
+ const identity = (x) => x;
25
+ const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
26
+ var _a, _b;
27
+ if (typeof (opts === null || opts === void 0 ? void 0 : opts.body) !== "string") {
28
+ console.error("Unexpected body type.");
29
+ return window.fetch(url, opts);
30
+ }
31
+ const messages = JSON.parse(opts === null || opts === void 0 ? void 0 : opts.body).messages;
32
+ const transformBody = (_a = requestOpts.transformBody) !== null && _a !== void 0 ? _a : identity;
33
+ const options = Object.assign(Object.assign(Object.assign(Object.assign({}, opts), { body: JSON.stringify(transformBody(messages)) }), requestOpts.fetchOpts), { headers: Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.headers), { "Content-Type": "application/json" }), (_b = requestOpts.fetchOpts) === null || _b === void 0 ? void 0 : _b.headers) });
34
+ return fetch(url, options);
35
+ });
36
+ const AiChatContext = createContext(null);
37
+ /**
38
+ * Provides AiChatContext to its children. Within this provider, you can consume
39
+ * the AiChatContext using the `useAiChat` hook.
40
+ */
41
+ const AiChatProvider = ({ initialMessages: _initialMessages, requestOpts, chatId, parseContent, children, }) => {
42
+ const initialMessages = useMemo(() => {
43
+ var _a;
44
+ return ((_a = _initialMessages === null || _initialMessages === void 0 ? void 0 : _initialMessages.map((message, i) => (Object.assign(Object.assign({}, message), { id: `initial-${i}` })))) !== null && _a !== void 0 ? _a : []);
45
+ }, [_initialMessages]);
46
+ const fetcher = useMemo(() => getFetcher(requestOpts), [requestOpts]);
47
+ const _a = useChat({
48
+ api: requestOpts.apiUrl,
49
+ streamProtocol: "text",
50
+ fetch: fetcher,
51
+ onFinish: (message) => {
52
+ var _a;
53
+ if (!requestOpts.onFinish)
54
+ return;
55
+ if (message.role === "assistant" || message.role === "user") {
56
+ (_a = requestOpts.onFinish) === null || _a === void 0 ? void 0 : _a.call(requestOpts, message);
57
+ }
58
+ else {
59
+ console.info("Unexpected message role.", message);
60
+ }
61
+ },
62
+ initialMessages,
63
+ id: chatId,
64
+ }), { messages: unparsed } = _a, others = __rest(_a, ["messages"]);
65
+ const messages = useMemo(() => {
66
+ const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
67
+ return unparsed.map((m) => {
68
+ if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
69
+ const content = parseContent ? parseContent(m.content) : m.content;
70
+ return Object.assign(Object.assign({}, m), { content });
71
+ }
72
+ return m;
73
+ });
74
+ }, [parseContent, unparsed, initialMessages]);
75
+ return (React.createElement(AiChatContext.Provider
76
+ /**
77
+ * Ensure that child state is reset when chatId changes.
78
+ */
79
+ , {
80
+ /**
81
+ * Ensure that child state is reset when chatId changes.
82
+ */
83
+ key: chatId, value: Object.assign({ initialMessages, messages }, others) }, children));
84
+ };
85
+ /**
86
+ * Returns the AiChatContext, which includes all results from `@ai-sdk/react`'s
87
+ * [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook as
88
+ * well as the initial messages.
89
+ *
90
+ * In addition to customizing the fetcher, using a context allows us to avoid
91
+ * this issue https://github.com/vercel/ai/issues/3266 since the caller no
92
+ * longer needs to provide the initial messages.
93
+ */
94
+ const useAiChat = () => {
95
+ const context = React.useContext(AiChatContext);
96
+ if (!context) {
97
+ throw new Error("useAiChatContext must be used within an AiChatProvider");
98
+ }
99
+ return context;
100
+ };
101
+ export { useAiChat, AiChatProvider };
@@ -0,0 +1,14 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AiChatProvider } from "./AiChatContext";
3
+ /**
4
+ * AiChatProvider provides state and functions for managing chat. The higher-level
5
+ * `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
6
+ * roughly.
7
+ *
8
+ * If you need to access chat state outside of the chat display, you can use
9
+ * `AiChatProvider` directly.
10
+ */
11
+ declare const meta: Meta<typeof AiChatProvider>;
12
+ export default meta;
13
+ type Story = StoryObj<typeof AiChatProvider>;
14
+ export declare const StreamingResponses: Story;