@mitodl/smoot-design 6.2.2 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +4 -0
  2. package/dist/bundles/remoteTutorDrawer.es.js +16894 -16636
  3. package/dist/bundles/remoteTutorDrawer.umd.js +88 -55
  4. package/dist/cjs/ai.d.ts +3 -3
  5. package/dist/cjs/ai.js +5 -1
  6. package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.js +1 -1
  7. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +1 -2
  8. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +11 -5
  9. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +1 -0
  10. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +21 -2
  11. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +6 -0
  12. package/dist/cjs/bundles/remoteTutorDrawer.js +4 -1
  13. package/dist/cjs/components/AiChat/AiChat.d.ts +3 -3
  14. package/dist/cjs/components/AiChat/AiChat.js +28 -44
  15. package/dist/cjs/components/AiChat/AiChat.stories.js +16 -2
  16. package/dist/cjs/components/AiChat/AiChatContext.d.ts +26 -0
  17. package/dist/cjs/components/AiChat/AiChatContext.js +105 -0
  18. package/dist/cjs/components/AiChat/AiChatContext.stories.d.ts +14 -0
  19. package/dist/cjs/components/AiChat/AiChatContext.stories.js +75 -0
  20. package/dist/cjs/components/AiChat/Markdown.d.ts +7 -0
  21. package/dist/cjs/components/AiChat/Markdown.js +14 -0
  22. package/dist/cjs/components/AiChat/test-utils/api.js +40 -1
  23. package/dist/cjs/components/AiChat/types.d.ts +20 -11
  24. package/dist/cjs/components/AiChat/types.js +1 -1
  25. package/dist/cjs/components/AiChat/utils.d.ts +1 -1
  26. package/dist/cjs/components/AiChat/utils.js +1 -1
  27. package/dist/cjs/components/LinkAdapter/LinkAdapter.js +1 -1
  28. package/dist/cjs/components/TabButtons/TabButtonList.js +1 -1
  29. package/dist/esm/ai.d.ts +3 -3
  30. package/dist/esm/ai.js +2 -1
  31. package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.js +1 -1
  32. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +1 -2
  33. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +11 -5
  34. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +1 -0
  35. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +20 -1
  36. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +6 -0
  37. package/dist/esm/bundles/remoteTutorDrawer.js +4 -1
  38. package/dist/esm/components/AiChat/AiChat.d.ts +3 -3
  39. package/dist/esm/components/AiChat/AiChat.js +28 -45
  40. package/dist/esm/components/AiChat/AiChat.stories.js +16 -2
  41. package/dist/esm/components/AiChat/AiChatContext.d.ts +26 -0
  42. package/dist/esm/components/AiChat/AiChatContext.js +101 -0
  43. package/dist/esm/components/AiChat/AiChatContext.stories.d.ts +14 -0
  44. package/dist/esm/components/AiChat/AiChatContext.stories.js +72 -0
  45. package/dist/esm/components/AiChat/Markdown.d.ts +7 -0
  46. package/dist/esm/components/AiChat/Markdown.js +12 -0
  47. package/dist/esm/components/AiChat/test-utils/api.js +40 -1
  48. package/dist/esm/components/AiChat/types.d.ts +20 -11
  49. package/dist/esm/components/AiChat/types.js +1 -1
  50. package/dist/esm/components/AiChat/utils.d.ts +1 -1
  51. package/dist/esm/components/AiChat/utils.js +1 -1
  52. package/dist/esm/components/LinkAdapter/LinkAdapter.js +1 -1
  53. package/dist/esm/components/TabButtons/TabButtonList.js +1 -1
  54. package/dist/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +8 -7
@@ -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;
@@ -0,0 +1,72 @@
1
+ import * as React from "react";
2
+ import { AiChatDisplay } from "./AiChat";
3
+ import { AiChatProvider, useAiChat } from "./AiChatContext";
4
+ import styled from "@emotion/styled";
5
+ import { handlers } from "./test-utils/api";
6
+ import Typography from "@mui/material/Typography";
7
+ const TEST_API_STREAMING = "http://localhost:4567/streaming";
8
+ const INITIAL_MESSAGES = [
9
+ {
10
+ content: "Hi! What are you interested in learning about?",
11
+ role: "assistant",
12
+ },
13
+ ];
14
+ const STARTERS = [
15
+ { content: "I'm interested in quantum computing" },
16
+ { content: "I want to understand global warming. " },
17
+ { content: "I am curious about AI applications for business" },
18
+ ];
19
+ const Container = styled.div({
20
+ width: "100%",
21
+ height: "400px",
22
+ position: "relative",
23
+ });
24
+ const MessageCounter = () => {
25
+ const { messages } = useAiChat();
26
+ return (React.createElement(Typography, { variant: "subtitle1" },
27
+ "Message count: ",
28
+ messages.length,
29
+ " (Provided by ",
30
+ React.createElement("code", null, "AiChatContext"),
31
+ ")"));
32
+ };
33
+ /**
34
+ * AiChatProvider provides state and functions for managing chat. The higher-level
35
+ * `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
36
+ * roughly.
37
+ *
38
+ * If you need to access chat state outside of the chat display, you can use
39
+ * `AiChatProvider` directly.
40
+ */
41
+ const meta = {
42
+ title: "smoot-design/AI/AiChatContext",
43
+ component: AiChatProvider,
44
+ parameters: {
45
+ msw: { handlers },
46
+ },
47
+ render: (args) => {
48
+ return (React.createElement(AiChatProvider, Object.assign({}, args),
49
+ React.createElement(MessageCounter, null),
50
+ React.createElement(Container, null,
51
+ React.createElement(AiChatDisplay, { entryScreenEnabled: false, conversationStarters: STARTERS, placeholder: "Type your message here", askTimTitle: "Ask TIM" }))));
52
+ },
53
+ decorators: (Story) => {
54
+ return (React.createElement(Container, null,
55
+ React.createElement(Story, null)));
56
+ },
57
+ args: {
58
+ requestOpts: { apiUrl: TEST_API_STREAMING },
59
+ initialMessages: INITIAL_MESSAGES,
60
+ },
61
+ argTypes: {
62
+ initialMessages: {
63
+ control: { type: "object", disable: true },
64
+ },
65
+ requestOpts: {
66
+ control: { type: "object", disable: true },
67
+ table: { readonly: true }, // See above
68
+ },
69
+ },
70
+ };
71
+ export default meta;
72
+ export const StreamingResponses = {};
@@ -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,11 @@ 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;
81
89
  } & RefAttributes<HTMLDivElement>;
82
- export type { RequestOpts, AiChatProps, AiChatMessage };
90
+ type AiChatProps = AiChatContextProps & AiChatDisplayProps;
91
+ 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
  /**