@mitodl/smoot-design 0.0.0-a2e4e1e → 0.0.0-bafccd3

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 (98) hide show
  1. package/README.md +4 -0
  2. package/dist/bundles/{remoteTutorDrawer.es.js → aiDrawerManager.es.js} +17612 -17240
  3. package/dist/bundles/aiDrawerManager.es.js.map +1 -0
  4. package/dist/bundles/aiDrawerManager.umd.js +245 -0
  5. package/dist/bundles/aiDrawerManager.umd.js.map +1 -0
  6. package/dist/cjs/VERSION.d.ts +12 -0
  7. package/dist/cjs/VERSION.js +15 -0
  8. package/dist/cjs/ai.d.ts +3 -3
  9. package/dist/cjs/ai.js +5 -1
  10. package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.d.ts → AiDrawer/AiDrawer.d.ts} +11 -13
  11. package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.js → AiDrawer/AiDrawer.js} +27 -41
  12. package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts → AiDrawer/AiDrawer.stories.d.ts} +4 -3
  13. package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.js → AiDrawer/AiDrawer.stories.js} +32 -51
  14. package/dist/cjs/bundles/AiDrawer/AiDrawerManager.d.ts +12 -0
  15. package/dist/cjs/bundles/AiDrawer/AiDrawerManager.js +51 -0
  16. package/dist/cjs/bundles/AiDrawer/AiDrawerManager.stories.d.ts +6 -0
  17. package/dist/cjs/bundles/AiDrawer/AiDrawerManager.stories.js +267 -0
  18. package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.js → AiDrawer/AiDrawerManager.test.js} +22 -18
  19. package/dist/cjs/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.js +1 -1
  20. package/dist/cjs/bundles/aiDrawerManager.d.ts +6 -0
  21. package/dist/cjs/bundles/aiDrawerManager.js +44 -0
  22. package/dist/cjs/components/AiChat/AiChat.d.ts +3 -3
  23. package/dist/cjs/components/AiChat/AiChat.js +65 -53
  24. package/dist/cjs/components/AiChat/AiChat.stories.d.ts +0 -4
  25. package/dist/cjs/components/AiChat/AiChat.stories.js +5 -54
  26. package/dist/cjs/components/AiChat/AiChatContext.d.ts +26 -0
  27. package/dist/cjs/components/AiChat/AiChatContext.js +106 -0
  28. package/dist/cjs/components/AiChat/AiChatContext.stories.d.ts +14 -0
  29. package/dist/cjs/components/AiChat/AiChatContext.stories.js +75 -0
  30. package/dist/cjs/components/AiChat/AiChatMarkdown.stories.d.ts +15 -0
  31. package/dist/cjs/components/AiChat/AiChatMarkdown.stories.js +282 -0
  32. package/dist/cjs/components/AiChat/Markdown.d.ts +7 -0
  33. package/dist/cjs/components/AiChat/Markdown.js +14 -0
  34. package/dist/cjs/components/AiChat/test-utils/api.js +40 -1
  35. package/dist/cjs/components/AiChat/types.d.ts +25 -11
  36. package/dist/cjs/components/AiChat/types.js +1 -1
  37. package/dist/cjs/components/AiChat/utils.d.ts +1 -1
  38. package/dist/cjs/components/AiChat/utils.js +1 -1
  39. package/dist/cjs/components/LinkAdapter/LinkAdapter.js +1 -1
  40. package/dist/cjs/components/TabButtons/TabButtonList.js +1 -1
  41. package/dist/cjs/index.d.ts +1 -0
  42. package/dist/cjs/index.js +3 -1
  43. package/dist/cjs/utils/retryingFetch.d.ts +19 -0
  44. package/dist/cjs/utils/retryingFetch.js +98 -0
  45. package/dist/cjs/utils/retryingFetch.test.js +48 -0
  46. package/dist/esm/VERSION.d.ts +12 -0
  47. package/dist/esm/VERSION.js +12 -0
  48. package/dist/esm/ai.d.ts +3 -3
  49. package/dist/esm/ai.js +2 -1
  50. package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.d.ts → AiDrawer/AiDrawer.d.ts} +11 -13
  51. package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.js → AiDrawer/AiDrawer.js} +26 -40
  52. package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts → AiDrawer/AiDrawer.stories.d.ts} +4 -3
  53. package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.js → AiDrawer/AiDrawer.stories.js} +31 -50
  54. package/dist/esm/bundles/AiDrawer/AiDrawerManager.d.ts +12 -0
  55. package/dist/esm/bundles/AiDrawer/AiDrawerManager.js +48 -0
  56. package/dist/esm/bundles/AiDrawer/AiDrawerManager.stories.d.ts +6 -0
  57. package/dist/esm/bundles/AiDrawer/AiDrawerManager.stories.js +264 -0
  58. package/dist/esm/bundles/AiDrawer/AiDrawerManager.test.d.ts +1 -0
  59. package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.js → AiDrawer/AiDrawerManager.test.js} +22 -18
  60. package/dist/esm/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.js +1 -1
  61. package/dist/esm/bundles/aiDrawerManager.d.ts +6 -0
  62. package/dist/esm/bundles/aiDrawerManager.js +41 -0
  63. package/dist/esm/components/AiChat/AiChat.d.ts +3 -3
  64. package/dist/esm/components/AiChat/AiChat.js +65 -54
  65. package/dist/esm/components/AiChat/AiChat.stories.d.ts +0 -4
  66. package/dist/esm/components/AiChat/AiChat.stories.js +4 -53
  67. package/dist/esm/components/AiChat/AiChatContext.d.ts +26 -0
  68. package/dist/esm/components/AiChat/AiChatContext.js +102 -0
  69. package/dist/esm/components/AiChat/AiChatContext.stories.d.ts +14 -0
  70. package/dist/esm/components/AiChat/AiChatContext.stories.js +72 -0
  71. package/dist/esm/components/AiChat/AiChatMarkdown.stories.d.ts +15 -0
  72. package/dist/esm/components/AiChat/AiChatMarkdown.stories.js +279 -0
  73. package/dist/esm/components/AiChat/Markdown.d.ts +7 -0
  74. package/dist/esm/components/AiChat/Markdown.js +12 -0
  75. package/dist/esm/components/AiChat/test-utils/api.js +40 -1
  76. package/dist/esm/components/AiChat/types.d.ts +25 -11
  77. package/dist/esm/components/AiChat/types.js +1 -1
  78. package/dist/esm/components/AiChat/utils.d.ts +1 -1
  79. package/dist/esm/components/AiChat/utils.js +1 -1
  80. package/dist/esm/components/LinkAdapter/LinkAdapter.js +1 -1
  81. package/dist/esm/components/TabButtons/TabButtonList.js +1 -1
  82. package/dist/esm/index.d.ts +1 -0
  83. package/dist/esm/index.js +1 -0
  84. package/dist/esm/utils/retryingFetch.d.ts +19 -0
  85. package/dist/esm/utils/retryingFetch.js +96 -0
  86. package/dist/esm/utils/retryingFetch.test.d.ts +1 -0
  87. package/dist/esm/utils/retryingFetch.test.js +46 -0
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/package.json +11 -7
  90. package/dist/bundles/remoteTutorDrawer.umd.js +0 -207
  91. package/dist/cjs/bundles/remoteTutorDrawer.d.ts +0 -7
  92. package/dist/cjs/bundles/remoteTutorDrawer.js +0 -40
  93. package/dist/esm/bundles/remoteTutorDrawer.d.ts +0 -7
  94. package/dist/esm/bundles/remoteTutorDrawer.js +0 -37
  95. /package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts → AiDrawer/AiDrawerManager.test.d.ts} +0 -0
  96. /package/dist/cjs/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.d.ts +0 -0
  97. /package/dist/{esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts → cjs/utils/retryingFetch.test.d.ts} +0 -0
  98. /package/dist/esm/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.d.ts +0 -0
@@ -0,0 +1,279 @@
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 { MathJaxContext } from "better-react-mathjax";
6
+ const TEST_API_STREAMING = "http://localhost:4567/streaming";
7
+ const Container = styled.div({
8
+ width: "100%",
9
+ height: "500px",
10
+ });
11
+ const meta = {
12
+ title: "smoot-design/AI/AiChat Markdown",
13
+ component: AiChat,
14
+ parameters: {
15
+ msw: { handlers },
16
+ },
17
+ render: (args) => React.createElement(AiChat, Object.assign({}, args)),
18
+ decorators: (Story, context) => {
19
+ return (React.createElement(MathJaxContext, null,
20
+ React.createElement(Container, null,
21
+ React.createElement(Story, { key: String(context.args.entryScreenEnabled) }))));
22
+ },
23
+ args: {
24
+ requestOpts: { apiUrl: TEST_API_STREAMING },
25
+ askTimTitle: "to recommend a course",
26
+ initialMessages: [
27
+ {
28
+ role: "assistant",
29
+ content: `A simple ordered list:
30
+ 1. Item 1
31
+ 2. Item 3
32
+ 3. Item 3`,
33
+ },
34
+ ],
35
+ },
36
+ argTypes: {
37
+ conversationStarters: {
38
+ control: { type: "object", disable: true },
39
+ },
40
+ initialMessages: {
41
+ control: { type: "object", disable: true },
42
+ },
43
+ requestOpts: {
44
+ control: { type: "object", disable: true },
45
+ table: { readonly: true }, // See above
46
+ },
47
+ },
48
+ };
49
+ export default meta;
50
+ export const Typical = {
51
+ args: {
52
+ requestOpts: { apiUrl: TEST_API_STREAMING },
53
+ entryScreenEnabled: false,
54
+ conversationStarters: [],
55
+ initialMessages: [
56
+ {
57
+ role: "user",
58
+ content: "What is this course about?",
59
+ },
60
+ {
61
+ role: "assistant",
62
+ content: `Let me search for the core focus of this course.
63
+
64
+ Based on the search results, this course (The Human Brain) focuses on studying how the human brain implements core perceptual and cognitive abilities of the mind. Here are the main aspects of what the course covers:
65
+
66
+ Key Focus:
67
+ - Examines how different parts of the brain carry out specific cognitive and perceptual functions
68
+ - Studies the functional organization of the human brain and how it gives rise to mental abilities
69
+ - Explores the relationship between mind and brain
70
+
71
+ Specific Topics Covered:
72
+ 1. Visual perception (color, shape, motion)
73
+ 2. Recognition of faces, places, bodies, and words
74
+ 3. Scene perception and navigation
75
+ 4. Numerical understanding
76
+ 5. Speech and music perception
77
+ 6. Language comprehension
78
+ 7. Understanding other minds (social cognition)
79
+
80
+ Main Approaches:
81
+ - Examines each mental function by:
82
+ 1. Understanding how it works in the mind (what is computed and how)
83
+ 2. Studying its brain basis (specialized machinery, information representation, timing)
84
+ - Uses multiple research methods including psychophysics, neuropsychology, fMRI, and other cognitive neuroscience techniques
85
+
86
+ Important Note:
87
+ The course emphasizes understanding principles rather than memorizing details, and takes students to the cutting edge of the field by having them read current research papers rather than textbooks. The professor notes that while it's called "The Human Brain," it focuses on cognitive neuroscience - understanding how the brain gives rise to mental functions, rather than purely biological aspects of the brain.
88
+
89
+ The course aims to help students understand both the big questions in the field and the methods used to answer them, while bringing them to the current frontiers of cognitive neuroscience research.`,
90
+ },
91
+ ],
92
+ },
93
+ };
94
+ export const Textual = {
95
+ args: {
96
+ requestOpts: { apiUrl: TEST_API_STREAMING },
97
+ entryScreenEnabled: false,
98
+ conversationStarters: [],
99
+ initialMessages: [
100
+ {
101
+ role: "assistant",
102
+ content: `Here is a longer paragraph and **bold text** and *italic text*. Lorem ipsum dolor sit amet, consectetur adipiscing elit
103
+ sed do eiusmod tempor [incididunt](https://mit.edu) ut labore et dolore magna aliqua. Ut enim ad minim veniam.
104
+
105
+ And some inline code, \`\`<inline></inline>\`\` and code block:
106
+ \`\`\`
107
+ def f(x):
108
+ print(x)
109
+ \`\`\``,
110
+ },
111
+ ],
112
+ },
113
+ };
114
+ export const Math = {
115
+ args: {
116
+ requestOpts: { apiUrl: TEST_API_STREAMING },
117
+ entryScreenEnabled: false,
118
+ conversationStarters: [],
119
+ initialMessages: [
120
+ {
121
+ role: "assistant",
122
+ content: `Some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
123
+
124
+ And some block math:
125
+ \n
126
+
127
+ $$
128
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
129
+ $$
130
+
131
+ Math is rendered using MathJax only if the \`useMathJax\` prop is set to true.`,
132
+ },
133
+ ],
134
+ useMathJax: true,
135
+ },
136
+ };
137
+ export const SimpleOrderedList = {
138
+ args: {
139
+ requestOpts: { apiUrl: TEST_API_STREAMING },
140
+ entryScreenEnabled: false,
141
+ conversationStarters: [],
142
+ initialMessages: [
143
+ {
144
+ role: "assistant",
145
+ content: `A simple ordered list:
146
+ 1. Item 1
147
+ 2. Item 2
148
+ 3. Item 3`,
149
+ },
150
+ ],
151
+ },
152
+ };
153
+ export const SimpleUnorderedList = {
154
+ args: {
155
+ requestOpts: { apiUrl: TEST_API_STREAMING },
156
+ entryScreenEnabled: false,
157
+ conversationStarters: [],
158
+ initialMessages: [
159
+ {
160
+ role: "assistant",
161
+ content: `A simple unordered list:
162
+ - Item 1
163
+ - Item 2
164
+ - Item 3`,
165
+ },
166
+ ],
167
+ },
168
+ };
169
+ export const NestedOrderedList = {
170
+ args: {
171
+ requestOpts: { apiUrl: TEST_API_STREAMING },
172
+ entryScreenEnabled: false,
173
+ conversationStarters: [],
174
+ initialMessages: [
175
+ {
176
+ role: "assistant",
177
+ content: `A nested ordered list:
178
+ 1. Item 1
179
+ 1. Item 1.1
180
+ 2. Item 1.2
181
+ 2. Item 2
182
+ 1. Item 2.1
183
+ 2. Item 2.2
184
+ 3. Item 3
185
+ 1. Item 3.1
186
+ 2. Item 3.2`,
187
+ },
188
+ ],
189
+ },
190
+ };
191
+ export const NestedUnorderedList = {
192
+ args: {
193
+ requestOpts: { apiUrl: TEST_API_STREAMING },
194
+ entryScreenEnabled: false,
195
+ conversationStarters: [],
196
+ initialMessages: [
197
+ {
198
+ role: "assistant",
199
+ content: `A nested unordered list:
200
+ - Item 1
201
+ - Item 1.1
202
+ - Item 1.2
203
+ - Item 2
204
+ - Item 2.1
205
+ - Item 2.2
206
+ - Item 3
207
+ - Item 3.1
208
+ - Item 3.2`,
209
+ },
210
+ ],
211
+ },
212
+ };
213
+ export const NestedOrderedUnorderedList = {
214
+ args: {
215
+ requestOpts: { apiUrl: TEST_API_STREAMING },
216
+ entryScreenEnabled: false,
217
+ conversationStarters: [],
218
+ initialMessages: [
219
+ {
220
+ role: "assistant",
221
+ content: `A nested ordered and unordered list:
222
+ 1. Item 1
223
+ - Item 1.1
224
+ - Item 1.2
225
+ 2. Item 2
226
+ - Item 2.1
227
+ - Item 2.2
228
+ 3. Item 3
229
+ - Item 3.1
230
+ - Item 3.2`,
231
+ },
232
+ ],
233
+ },
234
+ };
235
+ export const NestedUnorderedOrderedList = {
236
+ args: {
237
+ requestOpts: { apiUrl: TEST_API_STREAMING },
238
+ entryScreenEnabled: false,
239
+ conversationStarters: [],
240
+ initialMessages: [
241
+ {
242
+ role: "assistant",
243
+ content: `A nested unordered and ordered list:
244
+ - Item 1
245
+ 1. Item 1.1
246
+ 2. Item 1.2
247
+ - Item 2
248
+ 1. Item 2.1
249
+ 2. Item 2.2
250
+ - Item 3
251
+ 1. Item 3.1
252
+ 2. Item 3.2`,
253
+ },
254
+ ],
255
+ },
256
+ };
257
+ export const UnexpectedList = {
258
+ args: {
259
+ requestOpts: { apiUrl: TEST_API_STREAMING },
260
+ entryScreenEnabled: false,
261
+ conversationStarters: [],
262
+ initialMessages: [
263
+ {
264
+ role: "assistant",
265
+ content: `LLMs may produce lists that are semantically nested, but do not reliably indent the markdown for the nested list items,
266
+ producing sibling ordered and unordered lists. We address this by assuming that if &lt;ul&gt; immediately follows &lt;ol&gt; in the DOM, it is a
267
+ "semantic sublist" of the last list item, and we style accordingly:
268
+
269
+ 1. Item 1
270
+ - Semantic subitem 1.1
271
+ - Semantic subitem 1.2
272
+
273
+ 2. Item 2
274
+ - Semantic subitem 2.1
275
+ - Semantic subitem 2.2`,
276
+ },
277
+ ],
278
+ },
279
+ };
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ type MarkdownProps = {
3
+ children?: string;
4
+ enableMathjax?: boolean;
5
+ };
6
+ declare const Markdown: React.FC<MarkdownProps>;
7
+ export default Markdown;
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import ReactMarkdown from "react-markdown";
3
+ import rehypeMathjax from "rehype-mathjax/browser";
4
+ import remarkMath from "remark-math";
5
+ import { MathJax } from "better-react-mathjax";
6
+ const Markdown = ({ children, enableMathjax }) => {
7
+ const remarkPlugins = enableMathjax ? [remarkMath] : undefined;
8
+ const rehypePlugins = enableMathjax ? [rehypeMathjax] : undefined;
9
+ const markdown = (React.createElement(ReactMarkdown, { skipHtml: true, remarkPlugins: remarkPlugins, rehypePlugins: rehypePlugins }, children));
10
+ return enableMathjax ? React.createElement(MathJax, null, markdown) : markdown;
11
+ };
12
+ export default Markdown;
@@ -14,6 +14,15 @@ const SAMPLE_RESPONSES = [
14
14
  1. **[Machine Learning, Modeling, and Simulation Principles](https://xpro.mit.edu/courses/course-v1:xPRO+MLx1/)**: Offered by MIT xPRO, this course is part of the program "Machine Learning, Modeling, and Simulation: Engineering Problem-Solving in the Age of AI." It focuses on the principles of machine learning and how they can be applied to solve engineering problems, which is highly relevant for business applications of AI.
15
15
 
16
16
  This course is not free, but it provides a certification upon completion, which can be valuable for professionals looking to apply AI in business contexts. It covers essential concepts that can help you understand how AI can be leveraged to improve business processes and decision-making.
17
+ Here is some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
18
+
19
+ And some block math:
20
+ \n
21
+
22
+ $$
23
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
24
+ $$
25
+
17
26
  <!-- Comment! -->
18
27
  `,
19
28
  `
@@ -24,6 +33,15 @@ To understand global warming, I recommend the following resources from MIT:
24
33
  2. **[Global Warming Science](https://openlearninglibrary.mit.edu/courses/course-v1:MITx+12.340x+1T2020/about)**: Another offering of the same course by MITx, available through the Open Learning Library. It provides the same in-depth exploration of the earth's climate system.
25
34
 
26
35
  These courses are free and provide a solid foundation in understanding the scientific aspects of global warming. They are suitable for anyone interested in the topic, regardless of prior knowledge.
36
+ Here is some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
37
+
38
+ And some block math:
39
+ \n
40
+
41
+ $$
42
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
43
+ $$
44
+
27
45
  <!-- Comment! -->
28
46
  `,
29
47
  `
@@ -36,6 +54,16 @@ Here are some courses on linear algebra that you can explore:
36
54
  3. **[Quantum Information Science I, Part 1 (MITx)](https://openlearninglibrary.mit.edu/courses/course-v1:MITx+8.370.1x+1T2018/about)**: While primarily focused on quantum information science, this course requires some knowledge of linear algebra and is suitable for those interested in quantum mechanics. It is free and available through MITx.
37
55
 
38
56
  These courses provide a comprehensive introduction to linear algebra and its applications across various fields.
57
+
58
+ Here is some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
59
+
60
+ And some block math:
61
+ \n
62
+
63
+ $$
64
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
65
+ $$
66
+
39
67
  <!-- Comment! -->
40
68
  `,
41
69
  `Here are some courses on quantum computing that offer certificates:
@@ -50,7 +78,18 @@ These courses provide a comprehensive introduction to linear algebra and its app
50
78
  - **Offered by**: MIT xPRO
51
79
  - **Instructors**: Isaac Chuang, William Oliver, Peter Shor, Aram Harrow
52
80
 
53
- These courses are part of professional certificate programs, and you can choose to complete the entire program or take individual courses for certification.`,
81
+ These courses are part of professional certificate programs, and you can choose to complete the entire program or take individual courses for certification.
82
+
83
+
84
+ Here is some inline math: $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$
85
+
86
+ And some block math:
87
+ \n
88
+
89
+ $$
90
+ x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}
91
+ $$
92
+ `,
54
93
  ];
55
94
  const rand = (min, max) => {
56
95
  // min and max included
@@ -20,12 +20,24 @@ type RequestOpts = {
20
20
  fetchOpts?: RequestInit;
21
21
  onFinish?: (message: AiChatMessage) => void;
22
22
  };
23
- type AiChatProps = {
23
+ type AiChatContextProps = {
24
24
  /**
25
25
  * Changing the `chatId` will reset the chat. Changing the `chatId` to a
26
26
  * previously used value will restore the session state.
27
27
  */
28
28
  chatId?: string;
29
+ /**
30
+ * Options for making requests to the AI service.
31
+ */
32
+ requestOpts: RequestOpts;
33
+ parseContent?: (content: unknown) => string;
34
+ /**
35
+ * Initial messages to display on the chat. If not provided, the entry screen title will be used as the initial message.
36
+ */
37
+ initialMessages?: Omit<AiChatMessage, "id">[];
38
+ children?: React.ReactNode;
39
+ };
40
+ type AiChatDisplayProps = {
29
41
  /**
30
42
  * If provided, renders the "AskTIM" title motif followed by the text.
31
43
  */
@@ -44,21 +56,12 @@ type AiChatProps = {
44
56
  * Title to display on the entry screen, also the initial assistant message if not overridden by `initialMessages`.
45
57
  */
46
58
  entryScreenTitle?: string;
47
- /**
48
- * Initial messages to display on the chat. If not provided, the entry screen title will be used as the initial message.
49
- */
50
- initialMessages?: Omit<AiChatMessage, "id">[];
51
59
  /**
52
60
  * Prompt suggestions for the user, clickable on the entry screen or in the chat if the entry screen is not enabled.
53
61
  */
54
62
  conversationStarters?: {
55
63
  content: string;
56
64
  }[];
57
- /**
58
- * Options for making requests to the AI service.
59
- */
60
- requestOpts: RequestOpts;
61
- parseContent?: (content: unknown) => string;
62
65
  /**
63
66
  * A message to display while the component is in a loading state.
64
67
  *
@@ -78,5 +81,16 @@ type AiChatProps = {
78
81
  * the AiChat will scroll to the bottom as chat messages are added.
79
82
  */
80
83
  scrollElement?: HTMLElement | null;
84
+ /**
85
+ * If true, the chat will display math equations using MathJax.
86
+ * Defaults to false.
87
+ */
88
+ useMathJax?: boolean;
89
+ /**
90
+ * If true, the chat input will be autofocused on load.
91
+ * Defaults to true.
92
+ */
93
+ autofocus?: boolean;
81
94
  } & RefAttributes<HTMLDivElement>;
82
- export type { RequestOpts, AiChatProps, AiChatMessage };
95
+ type AiChatProps = AiChatContextProps & AiChatDisplayProps;
96
+ export type { RequestOpts, AiChatMessage, AiChatContextProps, AiChatDisplayProps, AiChatProps, };
@@ -1,2 +1,2 @@
1
- // Some of these are based on (compatible, but simplified / restricted) versions of ai/react types.
1
+ // Some of these are based on (compatible, but simplified / restricted) versions of @ai-sdk/react types.
2
2
  export {};
@@ -1,4 +1,4 @@
1
- import { UseChatOptions } from "ai/react";
1
+ import { UseChatOptions } from "@ai-sdk/react";
2
2
  import type { RequestOpts } from "./types";
3
3
  declare const useAiChat: (requestOpts: RequestOpts, opts: UseChatOptions) => import("@ai-sdk/react").UseChatHelpers & {
4
4
  addToolResult: ({ toolCallId, result, }: {
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { useChat } from "ai/react";
10
+ import { useChat } from "@ai-sdk/react";
11
11
  import { useMemo } from "react";
12
12
  const identity = (x) => x;
13
13
  const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
@@ -26,6 +26,6 @@ const LinkAdapter = React.forwardRef(function LinkAdapter(_a, ref) {
26
26
  var { Component } = _a, props = __rest(_a, ["Component"]);
27
27
  const theme = useTheme();
28
28
  const LinkComponent = Component !== null && Component !== void 0 ? Component : theme.custom.LinkAdapter;
29
- return React.createElement(PlainLink, Object.assign({ as: LinkComponent, ref: ref }, props));
29
+ return (React.createElement(PlainLink, Object.assign({ as: LinkComponent, ref: ref }, props), props.children));
30
30
  });
31
31
  export { LinkAdapter };
@@ -73,7 +73,7 @@ const TabButtonInner = React.forwardRef(
73
73
  TabButtonInner.displayName = "TabButtonInner";
74
74
  const TabLinkInner = React.forwardRef((props, ref) => {
75
75
  const { className } = props, others = __rest(props, ["className"]);
76
- return React.createElement(TabLinkStyled, Object.assign({}, defaultTabButtonProps, others, { ref: ref }));
76
+ return (React.createElement(TabLinkStyled, Object.assign({}, defaultTabButtonProps, others, { ref: ref }), props.children));
77
77
  });
78
78
  TabLinkInner.displayName = "TabLinkInner";
79
79
  /**
@@ -13,4 +13,5 @@ export type { TextFieldProps } from "./components/TextField/TextField";
13
13
  export { SrAnnouncer } from "./components/SrAnnouncer/SrAnnouncer";
14
14
  export type { SrAnnouncerProps } from "./components/SrAnnouncer/SrAnnouncer";
15
15
  export { TabButton, TabButtonLink, TabButtonList, } from "./components/TabButtons/TabButtonList";
16
+ export { VERSION } from "./VERSION";
16
17
  export { VisuallyHidden } from "./components/VisuallyHidden/VisuallyHidden";
package/dist/esm/index.js CHANGED
@@ -8,4 +8,5 @@ export { Input } from "./components/Input/Input";
8
8
  export { TextField } from "./components/TextField/TextField";
9
9
  export { SrAnnouncer } from "./components/SrAnnouncer/SrAnnouncer";
10
10
  export { TabButton, TabButtonLink, TabButtonList, } from "./components/TabButtons/TabButtonList";
11
+ export { VERSION } from "./VERSION";
11
12
  export { VisuallyHidden } from "./components/VisuallyHidden/VisuallyHidden";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A wrapper around fetch that retries the request on certain error codes.
3
+ *
4
+ * By default:
5
+ *
6
+ * Requests will be retried if max retries not exceeded and :
7
+ * - The status code is >= 400 AND NOT in the NO_RETRY_ERROR_CODES list,
8
+ * - OR the request promise rejected (network error)
9
+ *
10
+ * The retry delay is exponential, 1s, 2s, 4s, 8s, ... maxing at 30s.
11
+ *
12
+ * Maximum retries is 3.
13
+ *
14
+ * NOTE:
15
+ * - When NODE_ENV="test", the maximum retries is set 0 by default but can be
16
+ * set via the TEST_ENV_MAX_RETRIES environment variable.
17
+ */
18
+ declare const retryingFetch: ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & typeof globalThis.fetch;
19
+ export default retryingFetch;
@@ -0,0 +1,96 @@
1
+ const MAX_RETRIES = 3;
2
+ const NO_RETRY_ERROR_CODES = [400, 401, 403, 404, 405, 409, 422];
3
+ const BASE_RETRY_DELAY = process.env.NODE_ENV === "test" ? 10 : 1000;
4
+ // This is a workaround to ensure retryingFetch uses MSW's fetch version in tests.
5
+ // See https://github.com/jonbern/fetch-retry/issues/95#issuecomment-2613990480
6
+ const fetch = (...args) => window.fetch(...args);
7
+ // From https://github.com/jonbern/fetch-retry/blob/master/index.js
8
+ const fetchRetry = (fetch, { retryDelay, retryOn }) => {
9
+ return (input, init) => {
10
+ return new Promise((resolve, reject) => {
11
+ const wrappedFetch = (attempt) => {
12
+ const _input = typeof Request !== "undefined" && input instanceof Request
13
+ ? input.clone()
14
+ : input;
15
+ fetch(_input, init)
16
+ .then((response) => {
17
+ try {
18
+ Promise.resolve(retryOn(attempt, null, response))
19
+ .then((retryOnResponse) => {
20
+ if (retryOnResponse) {
21
+ retry(attempt, null, response);
22
+ }
23
+ else {
24
+ resolve(response);
25
+ }
26
+ })
27
+ .catch(reject);
28
+ }
29
+ catch (error) {
30
+ reject(error);
31
+ }
32
+ })
33
+ .catch((error) => {
34
+ try {
35
+ Promise.resolve(retryOn(attempt, error, null))
36
+ .then((retryOnResponse) => {
37
+ if (retryOnResponse) {
38
+ retry(attempt, error, null);
39
+ }
40
+ else {
41
+ reject(error);
42
+ }
43
+ })
44
+ .catch((error) => {
45
+ reject(error);
46
+ });
47
+ }
48
+ catch (error) {
49
+ reject(error);
50
+ }
51
+ });
52
+ };
53
+ const retry = (attempt, error, response) => {
54
+ const delay = retryDelay(attempt, error, response);
55
+ setTimeout(() => {
56
+ wrappedFetch(++attempt);
57
+ }, delay);
58
+ };
59
+ wrappedFetch(0);
60
+ });
61
+ };
62
+ };
63
+ /**
64
+ * A wrapper around fetch that retries the request on certain error codes.
65
+ *
66
+ * By default:
67
+ *
68
+ * Requests will be retried if max retries not exceeded and :
69
+ * - The status code is >= 400 AND NOT in the NO_RETRY_ERROR_CODES list,
70
+ * - OR the request promise rejected (network error)
71
+ *
72
+ * The retry delay is exponential, 1s, 2s, 4s, 8s, ... maxing at 30s.
73
+ *
74
+ * Maximum retries is 3.
75
+ *
76
+ * NOTE:
77
+ * - When NODE_ENV="test", the maximum retries is set 0 by default but can be
78
+ * set via the TEST_ENV_MAX_RETRIES environment variable.
79
+ */
80
+ const retryingFetch = fetchRetry(fetch, {
81
+ retryDelay: (attempt, _error, _response) => {
82
+ return Math.min(BASE_RETRY_DELAY * Math.pow(2, attempt), 30000);
83
+ },
84
+ retryOn: (attempt, _error, response) => {
85
+ if (attempt >= MAX_RETRIES) {
86
+ return false;
87
+ }
88
+ if (response) {
89
+ if (response.status < 400)
90
+ return false;
91
+ return !NO_RETRY_ERROR_CODES.includes(response.status);
92
+ }
93
+ return true;
94
+ },
95
+ });
96
+ export default retryingFetch;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
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
+ import retryingFetch from "./retryingFetch";
11
+ import { http, HttpResponse } from "msw";
12
+ import { setupServer } from "msw/node";
13
+ const counter = jest.fn(); // use jest.fn as counter because it resets on each test
14
+ const NETWORK_SUCCESS_URL = "http://localhost:3456/success";
15
+ const NETWORK_ERROR_URL = "http://localhost:3456/error";
16
+ const server = setupServer(http.get(NETWORK_SUCCESS_URL, (_a) => __awaiter(void 0, [_a], void 0, function* ({ request }) {
17
+ var _b;
18
+ counter();
19
+ const url = new URL(request.url);
20
+ const status = +((_b = url.searchParams.get("status")) !== null && _b !== void 0 ? _b : 200);
21
+ return HttpResponse.text(`Status ${status}`, { status });
22
+ })), http.get(NETWORK_ERROR_URL, () => __awaiter(void 0, void 0, void 0, function* () {
23
+ counter();
24
+ return HttpResponse.error();
25
+ })));
26
+ beforeAll(() => server.listen());
27
+ afterEach(() => server.resetHandlers());
28
+ afterAll(() => server.close());
29
+ describe("retryingFetch", () => {
30
+ beforeAll(() => { });
31
+ test.each([200, 201, 202, 367, 400, 401, 403])("should not retry on %s", (status) => __awaiter(void 0, void 0, void 0, function* () {
32
+ const result = yield retryingFetch(`${NETWORK_SUCCESS_URL}?status=${status}`);
33
+ expect(yield result.text()).toBe(`Status ${status}`);
34
+ expect(counter).toHaveBeenCalledTimes(1);
35
+ }));
36
+ test.each([429, 500, 501, 502, 503])("should retry on %s", (status) => __awaiter(void 0, void 0, void 0, function* () {
37
+ const result = yield retryingFetch(`${NETWORK_SUCCESS_URL}?status=${status}`);
38
+ expect(yield result.text()).toBe(`Status ${status}`);
39
+ expect(counter).toHaveBeenCalledTimes(4);
40
+ }));
41
+ test("should retry on error", () => __awaiter(void 0, void 0, void 0, function* () {
42
+ const result = yield retryingFetch(NETWORK_ERROR_URL).catch((err) => err);
43
+ expect(result).toBeInstanceOf(Error);
44
+ expect(counter).toHaveBeenCalledTimes(4);
45
+ }));
46
+ });