diff --git a/.changeset/pretty-melons-punch.md b/.changeset/pretty-melons-punch.md
index 3b8d854eb0..fe79eb8599 100644
--- a/.changeset/pretty-melons-punch.md
+++ b/.changeset/pretty-melons-punch.md
@@ -3,4 +3,4 @@
"@twilio-paste/core": patch
---
-[Button] Add border radius 20 to size="reset" buttons which can be overridden by passing a border radius token to variant="reset" and size="reset" buttons
\ No newline at end of file
+[Button] Add border radius 20 to size="reset" buttons which can be overridden by passing a border radius token to variant="reset" and size="reset" buttons
diff --git a/cypress/integration/sitemap-vrt/constants.ts b/cypress/integration/sitemap-vrt/constants.ts
index 49e88dd27e..c7367f49f6 100644
--- a/cypress/integration/sitemap-vrt/constants.ts
+++ b/cypress/integration/sitemap-vrt/constants.ts
@@ -24,6 +24,9 @@ export const SITEMAP = [
"/components/account-switcher/",
"/components/account-switcher/api",
"/components/account-switcher/changelog",
+ "/components/ai-chat-log/",
+ "/components/ai-chat-log/api",
+ "/components/ai-chat-log/changelog",
"/components/aspect-ratio/",
"/components/aspect-ratio/api",
"/components/aspect-ratio/changelog",
diff --git a/packages/paste-core/components/ai-chat-log/CHANGELOG.md b/packages/paste-core/components/ai-chat-log/CHANGELOG.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/paste-core/components/ai-chat-log/package.json b/packages/paste-core/components/ai-chat-log/package.json
index bef2f140bd..9e6c788a0a 100644
--- a/packages/paste-core/components/ai-chat-log/package.json
+++ b/packages/paste-core/components/ai-chat-log/package.json
@@ -3,7 +3,7 @@
"version": "0.0.0",
"category": "data display",
"status": "production",
- "description": "Ai chat log.",
+ "description": "An AI Chat Log is a collection of AI Chat components for displaying conversations between a human and an AI bot.",
"author": "Twilio Inc.",
"license": "MIT",
"main:dev": "src/index.tsx",
diff --git a/packages/paste-website/package.json b/packages/paste-website/package.json
index b98320ca70..192f8f2ef3 100644
--- a/packages/paste-website/package.json
+++ b/packages/paste-website/package.json
@@ -34,6 +34,7 @@
"@tanstack/react-query": "^5.17.9",
"@tanstack/react-query-devtools": "^5.17.10",
"@twilio-paste/account-switcher": "^3.0.1",
+ "@twilio-paste/ai-chat-log": "^0.0.0",
"@twilio-paste/alert": "^14.1.0",
"@twilio-paste/alert-dialog": "^9.2.0",
"@twilio-paste/anchor": "^12.1.0",
diff --git a/packages/paste-website/src/component-examples/AIChatLogExamples.ts b/packages/paste-website/src/component-examples/AIChatLogExamples.ts
new file mode 100644
index 0000000000..fc679bd134
--- /dev/null
+++ b/packages/paste-website/src/component-examples/AIChatLogExamples.ts
@@ -0,0 +1,310 @@
+export const basicBotMessage = `
+const BasicMessage = () => {
+ return (
+
+
+ Good Bot
+
+ Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const basicHumanMessage = `
+const BasicMessage = () => {
+ return (
+
+
+ Gibby Radki
+
+ I would like some information on twilio error codes for undelivered messages
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const botWithFeedback = `
+const MessageWithFeedback = () => {
+ return (
+
+
+ Good Bot
+
+ Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.
+
+
+
+ Is this helpful?
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const botWithBodyActions = `
+const MessageWithFeedback = () => {
+ return (
+
+
+ Good Bot
+
+ Below is a list of actions that can be taken with flex wrapping supported:
+
+
+
+
+
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const botWithLoadingStopButton = `
+const MessageWithLoadingAndStop = () => {
+ return (
+
+
+
+ Good Bot
+
+
+ {}} />
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const botWithLoading = `
+const MessageWithLoading = () => {
+ return (
+
+
+
+ Good Bot
+
+
+
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const kitchenSink = `
+const AIChatLogExample = () => {
+ return (
+
+
+ Gibby Radki
+
+ Hi, I'm getting errors codes when sending an SMS.
+
+
+
+ Good Bot
+
+ Error codes can be returned from various parts of the process. What error codes are you encountering?
+
+
+
+
+
+
+
+
+ Good Bot
+
+ Error 21608 means you're trying to send a message from an unverified number. Is your number verified in your Twilio account?
+
+
+
+ Is this helpful?
+
+
+
+
+
+
+
+ Gibby Radki
+
+
+ No, how do I verify it?
+
+
+
+
+ Good Bot
+
+
+ {}} />
+
+
+
+ );
+};
+
+render(
+
+)`.trim();
+export const aiChatLoggerExample = `
+const aiChatFactory = ([ message, variant, metaLabel, meta ]) => {
+ const time = new Date(0).toLocaleString(
+ 'en-US',
+ { hour: 'numeric', minute: 'numeric', timeZone: 'UTC', hour12: true }
+ )
+
+ return {
+ variant,
+ content: (
+
+ {meta}
+
+ {message}
+
+
+ )
+ }
+};
+
+const chatTemplates = [
+ ["Hello", "user", "You said at ", "Gibby Radki"],
+ ["Hi there", "bot", "AI said at ", "Good Bot"],
+ ["Greetings", "user", "You said at ", "Gibby Radki"],
+ ["Good to meet you", "bot", "AI said at ", "Good Bot"]
+];
+
+const AIChatLoggerExample = () => {
+ const [templateIdx, setTemplateIdx] = React.useState(2);
+ const { aiChats, push, pop, clear } = useAIChatLogger(
+ aiChatFactory(chatTemplates[0]),
+ aiChatFactory(chatTemplates[1])
+ );
+ const [loading, setLoading] = React.useState(false);
+
+ const pushChat = () => {
+ const template = chatTemplates[templateIdx];
+ setTemplateIdx((idx) => ++idx % chatTemplates.length);
+ const chat = aiChatFactory(template);
+
+ if (template[1] === "bot") {
+ const id = uid(chat.content);
+ setLoading(true);
+ push({
+ id,
+ variant: template[1],
+ content: (
+
+ Good Bot
+
+
+
+
+ ),
+ });
+ setTimeout(() => {
+ pop(id);
+ setLoading(false);
+ push(chat);
+ }, 1000);
+ } else {
+ push(chat);
+ }
+ }
+
+ const popChat = () => {
+ pop();
+ setTemplateIdx((idx) => idx === 0 ? idx : --idx % chatTemplates.length);
+ }
+
+ return(
+
+
+
+
+
+
+
+
+ )
+}
+
+render();
+`.trim();
+export const avatarExample = `
+const AvatarExample = () => {
+ return (
+
+
+ Gibby Radki
+
+
+ Gibby Radki
+
+
+ );
+};
+
+render(
+
+)`.trim();
diff --git a/packages/paste-website/src/pages/components/ai-chat-log/api.mdx b/packages/paste-website/src/pages/components/ai-chat-log/api.mdx
new file mode 100644
index 0000000000..20adf134bd
--- /dev/null
+++ b/packages/paste-website/src/pages/components/ai-chat-log/api.mdx
@@ -0,0 +1,100 @@
+export const meta = {
+ title: "AI Chat Log - API",
+ package: "@twilio-paste/ai-chat-log",
+ description:
+ "An AI Chat Log is a collection of Chat components for displaying conversations between a human and an AI bot",
+ slug: "/components/ai-chat-log/api",
+};
+
+import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md"; // I don't know why this is needed but if you remove it the page fails to render
+import packageJson from "@twilio-paste/ai-chat-log/package.json";
+
+import { SidebarCategoryRoutes } from "../../../constants";
+import ComponentPageLayout from "../../../layouts/ComponentPageLayout";
+import { getFeature, getNavigationData, getComponentApi } from "../../../utils/api";
+
+export default ComponentPageLayout;
+
+export const getStaticProps = async () => {
+ const navigationData = await getNavigationData();
+ const feature = await getFeature("AI Chat Log");
+ const { componentApi, componentApiTocData } = getComponentApi("@twilio-paste/ai-chat-log");
+ return {
+ props: {
+ data: {
+ ...packageJson,
+ ...feature,
+ },
+ componentApi,
+ mdxHeadings: [...mdxHeadings, ...componentApiTocData],
+ navigationData,
+ pageHeaderData: {
+ categoryRoute: SidebarCategoryRoutes.COMPONENTS,
+ githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log",
+ storybookUrl: "/?path=/story/components-chatlog--example-chat-log",
+ },
+ },
+ };
+};
+
+## Installation
+
+```bash
+yarn add @twilio-paste/ai-chat-log - or - yarn add @twilio-paste/core
+```
+
+## Usage
+
+```jsx
+import {
+ AIChatLog,
+ AIChatMessage,
+ AIChatMessageAuthor,
+ AIChatMessageBody,
+ AIChatMessageFeedback,
+ AIChatMessageLoading,
+ AIChatMessageMeta,
+} from "@twilio-paste/ai-chat-log";
+
+export const Basic = () => {
+ return (
+
+
+ Gibby Radki
+
+ Hi, I'm getting errors codes when sending an SMS.
+
+
+
+ Good Bot
+
+ Error codes can be returned from various parts of the process. What error codes are you encountering?
+
+
+
+ Is this helpful?
+
+
+
+
+
+
+
+
+
+
+ );
+};
+```
+
+## Props
+
+
diff --git a/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx b/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx
new file mode 100644
index 0000000000..b78049f582
--- /dev/null
+++ b/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx
@@ -0,0 +1,38 @@
+export const meta = {
+ title: "AI Chat Log - Components",
+ package: "@twilio-paste/ai-chat-log",
+ description:
+ "An AI Chat Log is a collection of Chat components for displaying conversations between a human and an AI bot.",
+ slug: "/components/ai-chat-log/changelog",
+};
+
+import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md";
+import packageJson from "@twilio-paste/ai-chat-log/package.json";
+
+import { SidebarCategoryRoutes } from "../../../constants";
+import ComponentPageLayout from "../../../layouts/ComponentPageLayout";
+import { getFeature, getNavigationData } from "../../../utils/api";
+
+export default ComponentPageLayout;
+
+export const getStaticProps = async () => {
+ const navigationData = await getNavigationData();
+ const feature = await getFeature("AI Chat Log");
+ return {
+ props: {
+ data: {
+ ...packageJson,
+ ...feature,
+ },
+ navigationData,
+ mdxHeadings,
+ pageHeaderData: {
+ categoryRoute: SidebarCategoryRoutes.COMPONENTS,
+ githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log",
+ storybookUrl: "/?path=/story/components-chatlog--example-chat-log",
+ },
+ },
+ };
+};
+
+
diff --git a/packages/paste-website/src/pages/components/ai-chat-log/index.mdx b/packages/paste-website/src/pages/components/ai-chat-log/index.mdx
new file mode 100644
index 0000000000..b4a86406f7
--- /dev/null
+++ b/packages/paste-website/src/pages/components/ai-chat-log/index.mdx
@@ -0,0 +1,363 @@
+export const meta = {
+ title: "AI Chat Log - Components",
+ package: "@twilio-paste/ai-chat-log",
+ description:
+ "An AI Chat Log is a collection of AI Chat components for displaying conversations between a human and an AI bot.",
+ slug: "/components/ai-chat-log/",
+};
+
+import { uid } from "@twilio-paste/uid-library";
+import { HelpText } from "@twilio-paste/help-text";
+import { Button } from "@twilio-paste/button";
+import { ButtonGroup } from "@twilio-paste/button-group";
+import { Stack } from "@twilio-paste/stack";
+import { Paragraph } from "@twilio-paste/paragraph";
+import {
+ AIChatLog,
+ AIChatMessage,
+ AIChatMessageAuthor,
+ AIChatMessageBody,
+ AIChatMessageLoading,
+ AIChatLogger,
+ useAIChatLogger,
+ AIChatMessageActionCard,
+ AIChatMessageActionGroup,
+} from "@twilio-paste/ai-chat-log";
+import { ThumbsUpIcon } from "@twilio-paste/icons/esm/ThumbsUpIcon";
+import { ThumbsDownIcon } from "@twilio-paste/icons/esm/ThumbsDownIcon";
+import { RefreshIcon } from "@twilio-paste/icons/esm/RefreshIcon";
+import { CopyIcon } from "@twilio-paste/icons/esm/CopyIcon";
+import { LogoTwilioIcon } from "@twilio-paste/icons/esm/LogoTwilioIcon";
+import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md";
+
+import { SidebarCategoryRoutes } from "../../../constants";
+import Logo from "../../../assets/logo.svg";
+
+import {
+ basicBotMessage,
+ basicHumanMessage,
+ botWithFeedback,
+ botWithLoadingStopButton,
+ botWithLoading,
+ kitchenSink,
+ aiChatLoggerExample,
+ botWithBodyActions,
+ avatarExample,
+} from "../../../component-examples/AIChatLogExamples";
+import packageJson from "@twilio-paste/ai-chat-log/package.json";
+import ComponentPageLayout from "../../../layouts/ComponentPageLayout";
+import { getFeature, getNavigationData } from "../../../utils/api";
+
+export default ComponentPageLayout;
+
+export const getStaticProps = async () => {
+ const navigationData = await getNavigationData();
+ const feature = await getFeature("AI Chat Log");
+ return {
+ props: {
+ data: {
+ ...packageJson,
+ ...feature,
+ },
+ navigationData,
+ mdxHeadings,
+ pageHeaderData: {
+ categoryRoute: SidebarCategoryRoutes.COMPONENTS,
+ githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log",
+ storybookUrl: "/?path=/story/components-chatlog--example-chat-log",
+ },
+ },
+ };
+};
+
+
+ {`
+
+ Gibby Radki
+
+ What does the SMS delivery error code 30003 mean?
+
+
+
+ Good Bot
+
+ Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.
+
+
+`}
+
+
+## Guidelines
+
+### About AI Chat Log
+
+An AI Chat Log is a way to display conversations between a user and AI. If you are looking for a chat between 2 or more humans, please refer to [Chat Log](/components/chat-log).
+
+The AI Chat Log package includes these main components:
+
+- AIChatLog
+- AIChatMessage
+- AIChatMessageAuthor
+- AIChatMessageBody
+- AIChatMessageActionGroup
+- AIChatMessageActionCard
+- AIChatMessageLoading
+
+### Accessibility
+
+To ensure the chat is accessible, only use the AI Chat components within an `AIChatLog` component and use `AIChatMessage` to wrap `AIChatMessageBody`, `AIChatMessageActionGroup` and components together.
+
+The only other accessibility requirement is providing the `AIChatMessageActionCard` a descriptive label via the `aria-label` React prop.
+
+The AIChatLog component has `role="log"` which means that any new messages added to it are announced by assistive technology.
+
+## Examples
+
+### Basic Message
+
+A message must include the author and body. Any message text from a user or a bot must be contained within the `AIChatMessageBody` component. Due to lengthy AI responses, the chat layout is top-down.
+
+#### Bot
+
+
+ {basicBotMessage}
+
+
+#### User
+
+
+ {basicHumanMessage}
+
+
+### Message Body Sizes
+
+The `AIChatMessageBody` component has two sizes, `size="default"` and `size="fullScreen"`. The fullScreen size is used where the ChatLog is displayed in the full width of the page where larger font size is needed.
+
+
+ {`
+
+ Gibby Radki
+
+ I'm a message that should be displayed in compact elements
+
+
+
+ Gibby Radki
+
+ I'm a message that will be displayed in full screen width
+
+
+`}
+
+
+### Message with Actions
+
+Message actions can be used to provide quick responses or actions to the user.
+
+`AIChatMessageActionGroup` should be a child of `AIChatMessage` so that the text and meta information are correctly grouped together for assistive technologies. `AIChatMessageActionCard` also needs a readable `aria-label` that summarizes what the meta information says.
+
+Each item within `AIChatMessageActionGroup` should be wrapped with `AIChatMessageActionCard`. It is recommended to use reset button variants for content within `AIChatMessageActionCard`.
+
+Actions can still be added in `AIChatMessageBody` which are returned from the AI response.
+
+#### Feedback in AIChatMessageActionCard
+
+
+ {botWithFeedback}
+
+
+#### Buttons in AIChatMessageBody from AI Response
+
+
+ {botWithBodyActions}
+
+
+### Loading States
+
+Use the `AIChatMessageLoading` component to indicate that the bot is typing or processing a response. During this time **no user input should be accepted**. No new messages should be added to a chat until the AI operation is finished processing.
+
+The SkeletonLoader lengths vary on each render to give a more natural pending message body interaction.
+
+#### Loading
+
+
+ {botWithLoading}
+
+
+#### Loading with Stop Button
+
+
+ {botWithLoadingStopButton}
+
+
+### Customizing Avatar
+
+`AIChatMessageAuthor` can utilize custom icons by passing an icon to the prop `avatarIcon` or an image to the `avatarSrc` prop.
+
+
+ {avatarExample}
+
+
+### Example AI Chat Log
+
+This example combines all the separate features displayed previously into one example. It shows how all the features work together harmoniously through composition.
+
+
+ {kitchenSink}
+
+
+### useAIChatLogger hook
+
+The `useAIChatLogger` hook provides a hook-based approach to managing AI chat state. It is best used with the `` component.
+
+`useAIChatLogger` returns 4 things:
+
+- An array of `aiChats`.
+- A `push` method used to add a chat, optionally with a custom ID.
+- A `pop` method used to remove a chat, optionally via its ID.
+- A `clear` method used to remove all chats.
+
+##### AIChatLogger component
+
+The `` component handles rendering the chats it is passed via props. It handles how chats enter and leave the UI.
+
+```
+const { aiChats } = useAIChatLogger();
+return ;
+```
+
+##### Adding and removing a chat
+
+You can push or pop a chat based on an action or event. In this example it's based on a button click:
+
+
+ {aiChatLoggerExample}
+
+
+## Composition Notes
+
+Keep any generated responses from the AI contained in the `AIChatMessageBody` component. Each chat message should only have one `AIChatMessageBody` component.
diff --git a/packages/paste-website/src/pages/components/chat-composer/api.mdx b/packages/paste-website/src/pages/components/chat-composer/api.mdx
index 2ba0e32372..99fc28949b 100644
--- a/packages/paste-website/src/pages/components/chat-composer/api.mdx
+++ b/packages/paste-website/src/pages/components/chat-composer/api.mdx
@@ -1,23 +1,23 @@
export const meta = {
- title: 'Chat Composer',
- package: '@twilio-paste/chat-composer',
- description: 'A Chat Composer is an input made for users to type rich chat messages.',
- slug: '/components/chat-composer/api',
+ title: "Chat Composer",
+ package: "@twilio-paste/chat-composer",
+ description: "A Chat Composer is an input made for users to type rich chat messages.",
+ slug: "/components/chat-composer/api",
};
-import Changelog from '@twilio-paste/chat-composer/CHANGELOG.md'; // I don't know why this is needed but if you remove it the page fails to render
-import packageJson from '@twilio-paste/chat-composer/package.json';
+import Changelog from "@twilio-paste/chat-composer/CHANGELOG.md"; // I don't know why this is needed but if you remove it the page fails to render
+import packageJson from "@twilio-paste/chat-composer/package.json";
-import {SidebarCategoryRoutes} from '../../../constants';
-import ComponentPageLayout from '../../../layouts/ComponentPageLayout';
-import {getFeature, getNavigationData, getComponentApi} from '../../../utils/api';
+import { SidebarCategoryRoutes } from "../../../constants";
+import ComponentPageLayout from "../../../layouts/ComponentPageLayout";
+import { getFeature, getNavigationData, getComponentApi } from "../../../utils/api";
export default ComponentPageLayout;
export const getStaticProps = async () => {
const navigationData = await getNavigationData();
- const feature = await getFeature('Chat Composer');
- const {componentApi, componentApiTocData} = getComponentApi('@twilio-paste/chat-composer');
+ const feature = await getFeature("Chat Composer");
+ const { componentApi, componentApiTocData } = getComponentApi("@twilio-paste/chat-composer");
return {
props: {
data: {
@@ -29,8 +29,8 @@ export const getStaticProps = async () => {
navigationData,
pageHeaderData: {
categoryRoute: SidebarCategoryRoutes.COMPONENTS,
- githubUrl: 'https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/chat-composer',
- storybookUrl: '/?path=/story/components-chat-composer--default',
+ githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/chat-composer",
+ storybookUrl: "/?path=/story/components-chat-composer--default",
},
},
};
@@ -45,12 +45,12 @@ yarn add @twilio-paste/chat-composer - or - yarn add @twilio-paste/core
## Usage
```jsx
-import {ChatComposer} from '@twilio-paste/core/chat-composer';
+import { ChatComposer } from "@twilio-paste/core/chat-composer";
export const BasicChatComposer = () => (
{
throw e;
},
diff --git a/packages/paste-website/src/pages/components/chat-log/index.mdx b/packages/paste-website/src/pages/components/chat-log/index.mdx
index 6638f26bcb..d7f6729504 100644
--- a/packages/paste-website/src/pages/components/chat-log/index.mdx
+++ b/packages/paste-website/src/pages/components/chat-log/index.mdx
@@ -100,7 +100,7 @@ export const getStaticProps = async () => {
### About Chat Log
-A Chat Log is a way to display conversations between people and can include complex content like attachments. The chat can be between two or more people.
+A Chat Log is a way to display conversations between people and can include complex content like attachments. The chat can be between two or more people. If you are looking for a chat between a human and an AI please refer to [AIChatLog](/components/ai-chat-log).
The Chat Log package includes these main components:
@@ -292,7 +292,7 @@ This example combines all the separate features displayed previously into one ex
The `useChatLogger` hook provides a hook based approach to managing chat state. It is best used with the `` component.
-`useChatLogger` returns 3 things:
+`useChatLogger` returns 4 things:
- An array of `chats`.
- A `push` method used to add a chat, optionally with a custom ID
diff --git a/yarn.lock b/yarn.lock
index ef6f733466..6324d27113 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15858,6 +15858,7 @@ __metadata:
"@tanstack/react-query-devtools": ^5.17.10
"@testing-library/react": ^13.4.0
"@twilio-paste/account-switcher": ^3.0.1
+ "@twilio-paste/ai-chat-log": ^0.0.0
"@twilio-paste/alert": ^14.1.0
"@twilio-paste/alert-dialog": ^9.2.0
"@twilio-paste/anchor": ^12.1.0