@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
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.AiChat = void 0;
14
+ exports.AiChat = exports.AiChatDisplay = void 0;
15
15
  const React = require("react");
16
16
  const react_1 = require("react");
17
17
  const styled_1 = require("@emotion/styled");
@@ -20,14 +20,14 @@ const classnames_1 = require("classnames");
20
20
  const react_2 = require("@remixicon/react");
21
21
  const Input_1 = require("../Input/Input");
22
22
  const EntryScreen_1 = require("./EntryScreen");
23
- const react_markdown_1 = require("react-markdown");
24
23
  const ScrollSnap_1 = require("../ScrollSnap/ScrollSnap");
25
24
  const SrAnnouncer_1 = require("../SrAnnouncer/SrAnnouncer");
26
25
  const VisuallyHidden_1 = require("../VisuallyHidden/VisuallyHidden");
27
26
  const Alert_1 = require("../Alert/Alert");
28
27
  const ChatTitle_1 = require("./ChatTitle");
29
- const utils_1 = require("./utils");
28
+ const AiChatContext_1 = require("./AiChatContext");
30
29
  const useScrollSnap_1 = require("../ScrollSnap/useScrollSnap");
30
+ const Markdown_1 = require("./Markdown");
31
31
  const classes = {
32
32
  root: "MitAiChat--root",
33
33
  title: "MitAiChat--title",
@@ -65,6 +65,9 @@ const MessagesContainer = (0, styled_1.default)(ScrollSnap_1.ScrollSnap)(({ exte
65
65
  padding: "14px 0",
66
66
  overflow: externalScroll ? "visible" : "auto",
67
67
  gap: "16px",
68
+ [`> .${classes.messageRowAssistant}:first-child`]: {
69
+ marginTop: "16px",
70
+ },
68
71
  }));
69
72
  const MessageRow = styled_1.default.div({
70
73
  display: "flex",
@@ -78,25 +81,45 @@ const MessageRow = styled_1.default.div({
78
81
  },
79
82
  position: "relative",
80
83
  });
81
- const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white, padding: "12px 16px" }, theme.typography.body2), { "p:first-of-type": {
84
+ const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white }, theme.typography.body2), { "p:first-of-type": {
82
85
  marginTop: 0,
83
86
  }, "p:last-of-type": {
84
87
  marginBottom: 0,
85
- }, "ol, ul": {
88
+ }, "ol,ul": {
86
89
  paddingInlineStart: "16px",
87
- marginLeft: "6px",
88
- "ol, ul": {
89
- marginLeft: 0,
90
+ margin: "12px 0 12px 4px",
91
+ }, "ol > li, ul > li": {
92
+ margin: "12px 0",
93
+ "ol,ul": {
94
+ margin: "12px 0 12px 4px",
90
95
  },
91
96
  li: {
92
- margin: "16px 0",
97
+ margin: "6px 0",
98
+ },
99
+ }, ul: {
100
+ paddingInlineStart: 0,
101
+ "> li": {
102
+ listStyleType: "none",
103
+ position: "relative",
104
+ "&::before": {
105
+ content: '"–"',
106
+ position: "absolute",
107
+ left: 0,
108
+ color: "#888",
109
+ marginRight: "8px",
110
+ },
111
+ paddingLeft: "16px",
112
+ },
113
+ }, "ol + ul": {
114
+ marginLeft: "24px",
115
+ li: {
116
+ margin: "6px 0",
93
117
  },
94
118
  }, a: {
95
119
  color: theme.custom.colors.red,
96
120
  fontWeight: "normal",
97
- }, borderRadius: "12px", [`.${classes.messageRowAssistant} &`]: {
98
- padding: "12px 16px 12px 0",
99
- }, [`.${classes.messageRowUser} &`]: {
121
+ }, borderRadius: "12px", [`.${classes.messageRowUser} &`]: {
122
+ padding: "12px 16px",
100
123
  borderRadius: "8px 0px 8px 8px",
101
124
  backgroundColor: theme.custom.colors.lightGray1,
102
125
  } })));
@@ -131,47 +154,32 @@ const Disclaimer = (0, styled_1.default)(Typography_1.default)(({ theme }) => ({
131
154
  marginTop: "16px",
132
155
  textAlign: "center",
133
156
  }));
134
- const AiChat = (_a) => {
157
+ const AiChatDisplay = (_a) => {
135
158
  var _b, _c;
136
- var { entryScreenTitle, entryScreenEnabled = true, conversationStarters, initialMessages: _initialMessages, askTimTitle, requestOpts, parseContent, srLoadingMessages, placeholder = "", className, scrollElement, chatId, ref } = _a, others = __rest(_a, ["entryScreenTitle", "entryScreenEnabled", "conversationStarters", "initialMessages", "askTimTitle", "requestOpts", "parseContent", "srLoadingMessages", "placeholder", "className", "scrollElement", "chatId", "ref"]) // Could contain data attributes
159
+ var { conversationStarters, askTimTitle, entryScreenEnabled = true, entryScreenTitle, srLoadingMessages, placeholder = "", className, scrollElement, ref, useMathJax = false } = _a, others = __rest(_a, ["conversationStarters", "askTimTitle", "entryScreenEnabled", "entryScreenTitle", "srLoadingMessages", "placeholder", "className", "scrollElement", "ref", "useMathJax"]) // Could contain data attributes
137
160
  ;
138
161
  const containerRef = (0, react_1.useRef)(null);
139
162
  const messagesContainerRef = (0, react_1.useRef)(null);
140
- const [showEntryScreen, setShowEntryScreen] = (0, react_1.useState)(entryScreenEnabled);
141
163
  const chatScreenRef = (0, react_1.useRef)(null);
142
- const [initialMessages, setInitialMessages] = (0, react_1.useState)();
143
164
  const promptInputRef = (0, react_1.useRef)(null);
144
- const { messages: unparsed, input, handleInputChange, handleSubmit, append, isLoading, stop, error, } = (0, utils_1.useAiChat)(requestOpts, {
145
- initialMessages,
146
- id: chatId,
147
- });
165
+ const { messages, input, handleInputChange, handleSubmit, append, isLoading, stop, error, initialMessages, status, } = (0, AiChatContext_1.useAiChat)();
148
166
  (0, useScrollSnap_1.useScrollSnap)({
149
167
  scrollElement: scrollElement || messagesContainerRef.current,
150
168
  contentElement: scrollElement ? messagesContainerRef.current : null,
151
169
  threshold: 200,
152
170
  });
153
- (0, react_1.useEffect)(() => {
154
- if (_initialMessages) {
155
- const prefix = Math.random().toString().slice(2);
156
- setInitialMessages(_initialMessages.map((m, i) => (Object.assign(Object.assign({}, m), { id: `initial-${prefix}-${i}` }))));
157
- }
158
- }, [_initialMessages]);
171
+ const [showEntryScreen, setShowEntryScreen] = (0, react_1.useState)(entryScreenEnabled);
159
172
  (0, react_1.useEffect)(() => {
160
173
  var _a, _b;
161
174
  if (!showEntryScreen) {
162
175
  (_b = (_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input")) === null || _b === void 0 ? void 0 : _b.focus();
163
176
  }
164
177
  }, [showEntryScreen]);
165
- const messages = (0, react_1.useMemo)(() => {
166
- const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
167
- return unparsed.map((m) => {
168
- if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
169
- const content = parseContent ? parseContent(m.content) : m.content;
170
- return Object.assign(Object.assign({}, m), { content });
171
- }
172
- return m;
173
- });
174
- }, [parseContent, unparsed, initialMessages]);
178
+ (0, react_1.useEffect)(() => {
179
+ if (messages.some((m) => m.role === "user" || ["submitted", "streaming"].includes(status))) {
180
+ setShowEntryScreen(false);
181
+ }
182
+ }, [messages, status]);
175
183
  const showStarters = messages.length === ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) || 0);
176
184
  const waiting = !showStarters && !error && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
177
185
  const stoppable = isLoading && ((_c = messages[messages.length - 1]) === null || _c === void 0 ? void 0 : _c.role) !== "user";
@@ -184,16 +192,7 @@ const AiChat = (_a) => {
184
192
  };
185
193
  const lastMsg = messages[messages.length - 1];
186
194
  const externalScroll = !!scrollElement;
187
- return (React.createElement(Container, { className: className, ref: containerRef,
188
- /**
189
- * Changing the `useChat` chatId seems to persist some state between
190
- * hook calls. This can cause strange effects like loading API responses
191
- * for previous chatId into new chatId.
192
- *
193
- * To avoid this, let's change the key, this will force React to make a new component
194
- * not sharing any of the old state.
195
- */
196
- key: chatId }, showEntryScreen ? (React.createElement(EntryScreen_1.EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
195
+ return (React.createElement(Container, { className: className, ref: containerRef }, showEntryScreen ? (React.createElement(EntryScreen_1.EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
197
196
  if (prompt.trim() === "") {
198
197
  return;
199
198
  }
@@ -203,13 +202,20 @@ const AiChat = (_a) => {
203
202
  React.createElement(ChatContainer, Object.assign({ className: (0, classnames_1.default)(className, classes.root), externalScroll: externalScroll }, others),
204
203
  React.createElement(ChatTitle_1.ChatTitle, { askTimTitle: askTimTitle, externalScroll: externalScroll, className: (0, classnames_1.default)(className, classes.title) }),
205
204
  React.createElement(MessagesContainer, { className: classes.messagesContainer, externalScroll: externalScroll, ref: messagesContainerRef },
206
- messages.map((m) => (React.createElement(MessageRow, { key: m.id, "data-chat-role": m.role, className: (0, classnames_1.default)(classes.messageRow, {
207
- [classes.messageRowUser]: m.role === "user",
208
- [classes.messageRowAssistant]: m.role === "assistant",
209
- }) },
210
- React.createElement(Message, { className: classes.message },
211
- React.createElement(VisuallyHidden_1.VisuallyHidden, { as: m.role === "user" ? "h5" : "h6" }, m.role === "user" ? "You said: " : "Assistant said: "),
212
- React.createElement(react_markdown_1.default, { skipHtml: true }, m.content))))),
205
+ messages.map((m, i) => {
206
+ // Our Markdown+Mathjax has issues when rendering streaming display math
207
+ // Force a re-render of the last (streaming) message when it's done loading.
208
+ const key = i === messages.length - 1 && isLoading
209
+ ? `isLoading-${m.id}`
210
+ : m.id;
211
+ return (React.createElement(MessageRow, { key: key, "data-chat-role": m.role, className: (0, classnames_1.default)(classes.messageRow, {
212
+ [classes.messageRowUser]: m.role === "user",
213
+ [classes.messageRowAssistant]: m.role === "assistant",
214
+ }) },
215
+ React.createElement(Message, { className: classes.message },
216
+ React.createElement(VisuallyHidden_1.VisuallyHidden, { as: m.role === "user" ? "h5" : "h6" }, m.role === "user" ? "You said: " : "Assistant said: "),
217
+ React.createElement(Markdown_1.default, { enableMathjax: useMathJax }, m.content))));
218
+ }),
213
219
  showStarters ? (React.createElement(StarterContainer, null, conversationStarters === null || conversationStarters === void 0 ? void 0 : conversationStarters.map((m) => (React.createElement(Starter, { className: classes.conversationStarter, key: m.content, onClick: () => {
214
220
  scrollToBottom();
215
221
  append({ role: "user", content: m.content });
@@ -244,4 +250,10 @@ const AiChat = (_a) => {
244
250
  React.createElement(Disclaimer, { variant: "body3" }, "AI-generated content may be incorrect.")),
245
251
  React.createElement(SrAnnouncer_1.SrAnnouncer, { isLoading: isLoading, loadingMessages: srLoadingMessages, message: (lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.role) === "assistant" ? lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.content : "" }))))));
246
252
  };
253
+ exports.AiChatDisplay = AiChatDisplay;
254
+ const AiChat = (_a) => {
255
+ var { requestOpts, initialMessages, chatId, parseContent } = _a, displayProps = __rest(_a, ["requestOpts", "initialMessages", "chatId", "parseContent"]);
256
+ return (React.createElement(AiChatContext_1.AiChatProvider, { requestOpts: requestOpts, chatId: chatId, initialMessages: initialMessages, parseContent: parseContent },
257
+ React.createElement(AiChatDisplay, Object.assign({}, displayProps))));
258
+ };
247
259
  exports.AiChat = AiChat;
@@ -9,10 +9,6 @@ export declare const StreamingResponses: Story;
9
9
  * to text via `parseContent`.
10
10
  */
11
11
  export declare const JsonResponses: Story;
12
- /**
13
- * This story shows the component's builtin markdown styling.
14
- */
15
- export declare const MarkdownStyling: Story;
16
12
  /**
17
13
  * Where a scrollable container exists in the including component, it can be passed in
18
14
  * for the AiChat component to use in place of its own height constrained message container
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ScrollContainer = exports.MarkdownStyling = exports.JsonResponses = exports.StreamingResponses = void 0;
3
+ exports.ScrollContainer = exports.JsonResponses = exports.StreamingResponses = void 0;
4
4
  const React = require("react");
5
5
  const AiChat_1 = require("./AiChat");
6
6
  const styled_1 = require("@emotion/styled");
7
7
  const api_1 = require("./test-utils/api");
8
8
  const react_1 = require("react");
9
+ const better_react_mathjax_1 = require("better-react-mathjax");
9
10
  const TEST_API_STREAMING = "http://localhost:4567/streaming";
10
11
  const TEST_API_JSON = "http://localhost:4567/json";
11
12
  const INITIAL_MESSAGES = [
@@ -39,8 +40,9 @@ const meta = {
39
40
  },
40
41
  render: (args) => React.createElement(AiChat_1.AiChat, Object.assign({}, args)),
41
42
  decorators: (Story, context) => {
42
- return (React.createElement(Container, null,
43
- React.createElement(Story, { key: String(context.args.entryScreenEnabled) })));
43
+ return (React.createElement(better_react_mathjax_1.MathJaxContext, null,
44
+ React.createElement(Container, null,
45
+ React.createElement(Story, { key: String(context.args.entryScreenEnabled) }))));
44
46
  },
45
47
  args: {
46
48
  entryScreenTitle: "What do you want to learn from MIT?",
@@ -77,57 +79,6 @@ exports.JsonResponses = {
77
79
  },
78
80
  },
79
81
  };
80
- const DEMO_MARKDOWN = `This shows default markdown styling. Here's are some lists:
81
- - Item 1
82
- - Item 2 - an unordered list (bullets)
83
- - Point A
84
- - Point B
85
- - Item 3 - an ordered list (numbers)
86
- 1. Item 3.1
87
- 2. Item 3.2
88
- 3. Item 3.3
89
-
90
- Sometimes, unordered lists are nested within ordered lists:
91
- 1. Item 1
92
- - Item 1.1
93
- - Item 1.2
94
- 2. Item 2
95
- - Item 2.1
96
- - Item 2.2
97
-
98
- Sometimes, ordered lists are nested within unordered lists:
99
- - Item 1
100
- 1. Item 1.1
101
- 2. Item 1.2
102
- - Item 2
103
- 1. Item 2.1
104
- 2. Item 2.2
105
-
106
- Here is a longer paragraph and **bold text** and *italic text*. Lorem ipsum dolor sit amet, consectetur adipiscing elit
107
- sed do eiusmod tempor [incididunt](https://mit.edu) ut labore et dolore magna aliqua. Ut enim ad minim veniam.
108
-
109
- And some inline code, \`\`<inline></inline>\`\` and code block:
110
- \`\`\`
111
- def f(x):
112
- print(x)
113
- \`\`\`
114
- `;
115
- /**
116
- * This story shows the component's builtin markdown styling.
117
- */
118
- exports.MarkdownStyling = {
119
- args: {
120
- requestOpts: { apiUrl: TEST_API_STREAMING },
121
- entryScreenEnabled: false,
122
- conversationStarters: [],
123
- initialMessages: [
124
- {
125
- role: "assistant",
126
- content: DEMO_MARKDOWN,
127
- },
128
- ],
129
- },
130
- };
131
82
  const ScrollComponent = (args) => {
132
83
  const ref = (0, react_1.useRef)(null);
133
84
  const [scrollElement, setScrollElement] = (0, react_1.useState)(null);
@@ -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,106 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.AiChatProvider = exports.useAiChat = void 0;
24
+ const React = require("react");
25
+ const react_1 = require("@ai-sdk/react");
26
+ const react_2 = require("react");
27
+ const retryingFetch_1 = require("../../utils/retryingFetch");
28
+ const identity = (x) => x;
29
+ const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
30
+ var _a, _b;
31
+ if (typeof (opts === null || opts === void 0 ? void 0 : opts.body) !== "string") {
32
+ console.error("Unexpected body type.");
33
+ return (0, retryingFetch_1.default)(url, opts);
34
+ }
35
+ const messages = JSON.parse(opts === null || opts === void 0 ? void 0 : opts.body).messages;
36
+ const transformBody = (_a = requestOpts.transformBody) !== null && _a !== void 0 ? _a : identity;
37
+ 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) });
38
+ return (0, retryingFetch_1.default)(url, options);
39
+ });
40
+ const AiChatContext = (0, react_2.createContext)(null);
41
+ /**
42
+ * Provides AiChatContext to its children. Within this provider, you can consume
43
+ * the AiChatContext using the `useAiChat` hook.
44
+ */
45
+ const AiChatProvider = ({ initialMessages: _initialMessages, requestOpts, chatId, parseContent, children, }) => {
46
+ const initialMessages = (0, react_2.useMemo)(() => {
47
+ var _a;
48
+ 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 : []);
49
+ }, [_initialMessages]);
50
+ const fetcher = (0, react_2.useMemo)(() => getFetcher(requestOpts), [requestOpts]);
51
+ const _a = (0, react_1.useChat)({
52
+ api: requestOpts.apiUrl,
53
+ streamProtocol: "text",
54
+ fetch: fetcher,
55
+ onFinish: (message) => {
56
+ var _a;
57
+ if (!requestOpts.onFinish)
58
+ return;
59
+ if (message.role === "assistant" || message.role === "user") {
60
+ (_a = requestOpts.onFinish) === null || _a === void 0 ? void 0 : _a.call(requestOpts, message);
61
+ }
62
+ else {
63
+ console.info("Unexpected message role.", message);
64
+ }
65
+ },
66
+ initialMessages,
67
+ id: chatId,
68
+ }), { messages: unparsed } = _a, others = __rest(_a, ["messages"]);
69
+ const messages = (0, react_2.useMemo)(() => {
70
+ const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
71
+ return unparsed.map((m) => {
72
+ if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
73
+ const content = parseContent ? parseContent(m.content) : m.content;
74
+ return Object.assign(Object.assign({}, m), { content });
75
+ }
76
+ return m;
77
+ });
78
+ }, [parseContent, unparsed, initialMessages]);
79
+ return (React.createElement(AiChatContext.Provider
80
+ /**
81
+ * Ensure that child state is reset when chatId changes.
82
+ */
83
+ , {
84
+ /**
85
+ * Ensure that child state is reset when chatId changes.
86
+ */
87
+ key: chatId, value: Object.assign({ initialMessages, messages }, others) }, children));
88
+ };
89
+ exports.AiChatProvider = AiChatProvider;
90
+ /**
91
+ * Returns the AiChatContext, which includes all results from `@ai-sdk/react`'s
92
+ * [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook as
93
+ * well as the initial messages.
94
+ *
95
+ * In addition to customizing the fetcher, using a context allows us to avoid
96
+ * this issue https://github.com/vercel/ai/issues/3266 since the caller no
97
+ * longer needs to provide the initial messages.
98
+ */
99
+ const useAiChat = () => {
100
+ const context = React.useContext(AiChatContext);
101
+ if (!context) {
102
+ throw new Error("useAiChatContext must be used within an AiChatProvider");
103
+ }
104
+ return context;
105
+ };
106
+ exports.useAiChat = useAiChat;
@@ -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;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamingResponses = void 0;
4
+ const React = require("react");
5
+ const AiChat_1 = require("./AiChat");
6
+ const AiChatContext_1 = require("./AiChatContext");
7
+ const styled_1 = require("@emotion/styled");
8
+ const api_1 = require("./test-utils/api");
9
+ const Typography_1 = require("@mui/material/Typography");
10
+ const TEST_API_STREAMING = "http://localhost:4567/streaming";
11
+ const INITIAL_MESSAGES = [
12
+ {
13
+ content: "Hi! What are you interested in learning about?",
14
+ role: "assistant",
15
+ },
16
+ ];
17
+ const STARTERS = [
18
+ { content: "I'm interested in quantum computing" },
19
+ { content: "I want to understand global warming. " },
20
+ { content: "I am curious about AI applications for business" },
21
+ ];
22
+ const Container = styled_1.default.div({
23
+ width: "100%",
24
+ height: "400px",
25
+ position: "relative",
26
+ });
27
+ const MessageCounter = () => {
28
+ const { messages } = (0, AiChatContext_1.useAiChat)();
29
+ return (React.createElement(Typography_1.default, { variant: "subtitle1" },
30
+ "Message count: ",
31
+ messages.length,
32
+ " (Provided by ",
33
+ React.createElement("code", null, "AiChatContext"),
34
+ ")"));
35
+ };
36
+ /**
37
+ * AiChatProvider provides state and functions for managing chat. The higher-level
38
+ * `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
39
+ * roughly.
40
+ *
41
+ * If you need to access chat state outside of the chat display, you can use
42
+ * `AiChatProvider` directly.
43
+ */
44
+ const meta = {
45
+ title: "smoot-design/AI/AiChatContext",
46
+ component: AiChatContext_1.AiChatProvider,
47
+ parameters: {
48
+ msw: { handlers: api_1.handlers },
49
+ },
50
+ render: (args) => {
51
+ return (React.createElement(AiChatContext_1.AiChatProvider, Object.assign({}, args),
52
+ React.createElement(MessageCounter, null),
53
+ React.createElement(Container, null,
54
+ React.createElement(AiChat_1.AiChatDisplay, { entryScreenEnabled: false, conversationStarters: STARTERS, placeholder: "Type your message here", askTimTitle: "Ask TIM" }))));
55
+ },
56
+ decorators: (Story) => {
57
+ return (React.createElement(Container, null,
58
+ React.createElement(Story, null)));
59
+ },
60
+ args: {
61
+ requestOpts: { apiUrl: TEST_API_STREAMING },
62
+ initialMessages: INITIAL_MESSAGES,
63
+ },
64
+ argTypes: {
65
+ initialMessages: {
66
+ control: { type: "object", disable: true },
67
+ },
68
+ requestOpts: {
69
+ control: { type: "object", disable: true },
70
+ table: { readonly: true }, // See above
71
+ },
72
+ },
73
+ };
74
+ exports.default = meta;
75
+ exports.StreamingResponses = {};
@@ -0,0 +1,15 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AiChat } from "./AiChat";
3
+ declare const meta: Meta<typeof AiChat>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof AiChat>;
6
+ export declare const Typical: Story;
7
+ export declare const Textual: Story;
8
+ export declare const Math: Story;
9
+ export declare const SimpleOrderedList: Story;
10
+ export declare const SimpleUnorderedList: Story;
11
+ export declare const NestedOrderedList: Story;
12
+ export declare const NestedUnorderedList: Story;
13
+ export declare const NestedOrderedUnorderedList: Story;
14
+ export declare const NestedUnorderedOrderedList: Story;
15
+ export declare const UnexpectedList: Story;