From 97f495956790787f0c8209b9317d23fd989a7461 Mon Sep 17 00:00:00 2001 From: taras Date: Sun, 25 Aug 2024 13:53:58 -0400 Subject: [PATCH 1/4] init commit --- stories/examples.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/stories/examples.ts b/stories/examples.ts index 2bb49b4..3a6c0f9 100644 --- a/stories/examples.ts +++ b/stories/examples.ts @@ -176,3 +176,41 @@ export const sessionsWithPartialConversation: Session[] = [ ] } ]; + +export const sessionWithMessageResponseRecommended: Session[] = [ + { + id: 'session-1', + title: 'Session with Image', + createdAt: subHours(new Date(), 1), + updatedAt: new Date(), + conversations: [ + { + id: 'conversation-1', + question: 'What are the benefits of using React?', + createdAt: new Date(), + response: + 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1024px-React-icon.svg.png', + followUpResponse: + 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1024px-React-icon.svg.png', + updatedAt: new Date() + } + ] + }, + { + id: 'session-2', + title: 'Session with Text', + createdAt: subHours(new Date(), 1), + updatedAt: new Date(), + conversations: [ + { + id: 'conversation-2', + question: 'What are the benefits of using React?', + createdAt: new Date(), + response: + 'React benefits include a declarative coding style, component-based architecture, virtual DOM, and a large community and ecosystem.', + followUpResponse: `React's state management system, which allows components to maintain their own state and re-render only when necessary, makes it easier to build responsive and interactive UIs.`, + updatedAt: new Date() + } + ] + } +]; From d3334fbf9a7b2065f28592629197f28746c27591 Mon Sep 17 00:00:00 2001 From: taras Date: Sun, 25 Aug 2024 14:49:18 -0400 Subject: [PATCH 2/4] message response recommended --- package-lock.json | 4 +- .../MessageResponseRecommended.tsx | 110 ++++++++++++++++++ .../SessionMessage/SessionMessage.tsx | 27 +++-- src/SessionMessages/SessionMessage/index.ts | 1 + src/theme.ts | 20 +++- src/types.ts | 5 + stories/Console.stories.tsx | 38 +++++- stories/examples.ts | 3 +- 8 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx diff --git a/package-lock.json b/package-lock.json index 72ae5bc..97bf6fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "reachat", - "version": "1.4.0", + "version": "1.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "reachat", - "version": "1.4.0", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { "@radix-ui/react-slot": "^1.1.0", diff --git a/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx new file mode 100644 index 0000000..d47427d --- /dev/null +++ b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx @@ -0,0 +1,110 @@ +import { ChatContext } from '@/ChatContext'; +import { Slot } from '@radix-ui/react-slot'; +import { motion } from 'framer-motion'; +import { cn } from 'reablocks'; +import { FC, PropsWithChildren, useContext } from 'react'; +import { Markdown } from '@/Markdown'; +import { PluggableList } from 'react-markdown/lib'; + +export interface MessageResponseRecommendedProps extends PropsWithChildren { + /** + * Initial response to render. + */ + response: string; + + /** + * Follow-up response to render. + */ + followUpResponse: string; + + /** + * Whether the response is loading. + */ + isLoading?: boolean; + + /** + * Function to handle clicks on the response. + */ + onClickResponse?: (response: string) => void; + + /** + * Function to handle clicks on the follow-up response. + */ + onClickFollowUpResponse?: (followUpResponse: string) => void; +} + +// Helper function to check if the URL is a valid image URL +const isImageUrl = (url: string) => { + return /\.(jpeg|jpg|gif|png|svg|webp)$/i.test(url); +}; + +export const MessageResponseRecommended: FC< + MessageResponseRecommendedProps +> = ({ + response, + followUpResponse, + isLoading, + children, + onClickResponse, + onClickFollowUpResponse +}) => { + const { theme, isCompact, remarkPlugins } = useContext(ChatContext); + const Comp = children ? Slot : 'div'; + + return ( + + {children || ( + <> +
onClickResponse?.(response)}> + {isImageUrl(response) ? ( +
+ {response} +
+ ) : ( +
+ + {response} + +
+ )} +
+
onClickFollowUpResponse?.(followUpResponse)}> + {isImageUrl(followUpResponse) ? ( +
+ {followUpResponse} +
+ ) : ( +
+ + {followUpResponse} + +
+ )} +
+ {isLoading && ( + + )} + + )} +
+ ); +}; diff --git a/src/SessionMessages/SessionMessage/SessionMessage.tsx b/src/SessionMessages/SessionMessage/SessionMessage.tsx index 60372bf..fbd019e 100644 --- a/src/SessionMessages/SessionMessage/SessionMessage.tsx +++ b/src/SessionMessages/SessionMessage/SessionMessage.tsx @@ -7,6 +7,7 @@ import { MessageQuestion } from './MessageQuestion'; import { MessageResponse } from './MessageResponse'; import { MessageSources } from './MessageSources'; import { MessageActions } from './MessageActions'; +import { MessageResponseRecommended } from './MessageResponseRecommended'; const messageVariants = { hidden: { @@ -41,17 +42,31 @@ export const SessionMessage: FC = ({ children }) => { const { theme, isLoading } = useContext(ChatContext); + const hasFollowUpResponse = !!conversation.followUpResponse; return ( {children || ( <> - - + + {hasFollowUpResponse ? ( + + ) : ( + + )} + = ({ )} - {!isLast && ( - - )} + {!isLast && } ); }; diff --git a/src/SessionMessages/SessionMessage/index.ts b/src/SessionMessages/SessionMessage/index.ts index 5eb14b0..0340978 100644 --- a/src/SessionMessages/SessionMessage/index.ts +++ b/src/SessionMessages/SessionMessage/index.ts @@ -6,3 +6,4 @@ export * from './MessageFiles'; export * from './MessageQuestion'; export * from './MessageResponse'; export * from './MessageSources'; +export * from './MessageResponseRecommended'; diff --git a/src/theme.ts b/src/theme.ts index 3c8e247..69c9bf8 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -33,6 +33,9 @@ export interface ChatTheme { cursor: string; overlay: string; expand: string; + recommended: string; + rimage: string; + markdownBorder: string; files: { base: string; file: { @@ -132,10 +135,20 @@ export const chatTheme: ChatTheme = { 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-3xl rounded-br-none text-typography border bg-gray-200 border-gray-300 text-gray-900', 'dark:bg-gray-900/60 dark:border-gray-700/50 dark:text-gray-100' ].join(' '), - response: ['relative data-[compact=false]:px-4 text-gray-900', 'dark:text-gray-100'].join(' '), - overlay: `overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-gradient-to-b after:from-transparent dark:after:to-gray-900 after:to-gray-200`, + response: [ + 'relative data-[compact=false]:px-4 text-gray-900', + 'dark:text-gray-100' + ].join(' '), + overlay: + "overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-gradient-to-b after:from-transparent dark:after:to-gray-900 after:to-gray-200", cursor: 'inline-block w-1 h-4 bg-current', expand: 'absolute bottom-1 right-1 z-10', + recommended: 'flex gap-4 justify-around', + rimage: ' max-w-[250px] max-h-[250px] object-cover cursor-pointer', + markdownBorder: [ + 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-4xl rounded-[10px] cursor-pointer text-typography border bg-gray-200 border-gray-300 text-gray-900', + 'dark:bg-gray-900/60 dark:border-gray-700/50 dark:text-gray-100 ' + ].join(' '), files: { base: 'mb-2 flex flex-wrap gap-3 ', file: { @@ -167,7 +180,8 @@ export const chatTheme: ChatTheme = { th: 'px-4 py-2 text-left font-bold border-b border-gray-500', td: 'px-4 py-2', code: 'm-2 rounded-b relative', - toolbar: 'text-xs dark:bg-gray-700/50 flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gray-200 ', + toolbar: + 'text-xs dark:bg-gray-700/50 flex items-center justify-between px-2 py-1 rounded-t sticky top-0 backdrop-blur-md bg-gray-200 ', li: 'mb-2 ml-6', ul: 'mb-4 list-disc', ol: 'mb-4 list-decimal' diff --git a/src/types.ts b/src/types.ts index 67cfe2f..3724380 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,6 +62,11 @@ export interface Conversation { * The AI's response to the user's question */ response?: string; + /** + * The AI's follow-upresponse to the user's question + */ + + followUpResponse?: string; /** * Array of sources referenced in the conversation diff --git a/stories/Console.stories.tsx b/stories/Console.stories.tsx index 785ed95..3743b71 100644 --- a/stories/Console.stories.tsx +++ b/stories/Console.stories.tsx @@ -43,7 +43,8 @@ import { fakeSessionsWithEmbeds, sessionWithSources, sessionsWithFiles, - sessionsWithPartialConversation + sessionsWithPartialConversation, + sessionWithMessageResponseRecommended } from './examples'; export default { @@ -1093,3 +1094,38 @@ export const ImageFiles = () => { ); }; + +export const FollowUpResponses = () => { + return ( +
+ + + + + + + + + + + + +
+ ); +}; diff --git a/stories/examples.ts b/stories/examples.ts index 3a6c0f9..3a42663 100644 --- a/stories/examples.ts +++ b/stories/examples.ts @@ -208,7 +208,8 @@ export const sessionWithMessageResponseRecommended: Session[] = [ createdAt: new Date(), response: 'React benefits include a declarative coding style, component-based architecture, virtual DOM, and a large community and ecosystem.', - followUpResponse: `React's state management system, which allows components to maintain their own state and re-render only when necessary, makes it easier to build responsive and interactive UIs.`, + followUpResponse: + 'React’s state management system enables components to maintain their own state and efficiently re-render only when necessary, making it easier to build responsive and interactive UIs.', updatedAt: new Date() } ] From 23e11098e2c395852c83bd98870e5bf512da3e4c Mon Sep 17 00:00:00 2001 From: taras Date: Tue, 27 Aug 2024 09:13:51 -0400 Subject: [PATCH 3/4] updated follow up logic --- .../MessageResponseRecommended.tsx | 81 +++++++------------ .../SessionMessage/SessionMessage.tsx | 17 ++-- src/theme.ts | 2 +- src/types.ts | 2 +- stories/examples.ts | 10 ++- 5 files changed, 45 insertions(+), 67 deletions(-) diff --git a/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx index d47427d..8444799 100644 --- a/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx +++ b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx @@ -8,25 +8,15 @@ import { PluggableList } from 'react-markdown/lib'; export interface MessageResponseRecommendedProps extends PropsWithChildren { /** - * Initial response to render. + * Follow-up response to render (array of values). */ - response: string; - - /** - * Follow-up response to render. - */ - followUpResponse: string; + followUpResponse: string[]; /** * Whether the response is loading. */ isLoading?: boolean; - /** - * Function to handle clicks on the response. - */ - onClickResponse?: (response: string) => void; - /** * Function to handle clicks on the follow-up response. */ @@ -40,14 +30,7 @@ const isImageUrl = (url: string) => { export const MessageResponseRecommended: FC< MessageResponseRecommendedProps -> = ({ - response, - followUpResponse, - isLoading, - children, - onClickResponse, - onClickFollowUpResponse -}) => { +> = ({ followUpResponse, isLoading, children, onClickFollowUpResponse }) => { const { theme, isCompact, remarkPlugins } = useContext(ChatContext); const Comp = children ? Slot : 'div'; @@ -58,40 +41,30 @@ export const MessageResponseRecommended: FC< > {children || ( <> -
onClickResponse?.(response)}> - {isImageUrl(response) ? ( -
- {response} -
- ) : ( -
- - {response} - -
- )} -
-
onClickFollowUpResponse?.(followUpResponse)}> - {isImageUrl(followUpResponse) ? ( -
- {followUpResponse} -
- ) : ( -
- - {followUpResponse} - -
- )} -
+ {/* Map over follow-up responses to display them individually */} + {followUpResponse.map((responseItem, index) => ( +
onClickFollowUpResponse?.(responseItem)} + > + {isImageUrl(responseItem) ? ( +
+ {responseItem} +
+ ) : ( +
+ + {responseItem} + +
+ )} +
+ ))} + {isLoading && ( = ({ children }) => { const { theme, isLoading } = useContext(ChatContext); - const hasFollowUpResponse = !!conversation.followUpResponse; + const hasFollowUpResponse = + Array.isArray(conversation.followUpResponse) && + conversation.followUpResponse.length > 0; return ( @@ -54,17 +56,16 @@ export const SessionMessage: FC = ({ files={conversation.files} /> - {hasFollowUpResponse ? ( + + + {hasFollowUpResponse && ( - ) : ( - )} diff --git a/src/theme.ts b/src/theme.ts index 69c9bf8..d35634c 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -143,7 +143,7 @@ export const chatTheme: ChatTheme = { "overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-gradient-to-b after:from-transparent dark:after:to-gray-900 after:to-gray-200", cursor: 'inline-block w-1 h-4 bg-current', expand: 'absolute bottom-1 right-1 z-10', - recommended: 'flex gap-4 justify-around', + recommended: 'flex gap-4 justify-around mt-8', rimage: ' max-w-[250px] max-h-[250px] object-cover cursor-pointer', markdownBorder: [ 'relative font-semibold mb-4 px-4 py-4 pb-2 rounded-4xl rounded-[10px] cursor-pointer text-typography border bg-gray-200 border-gray-300 text-gray-900', diff --git a/src/types.ts b/src/types.ts index 3724380..0379dbe 100644 --- a/src/types.ts +++ b/src/types.ts @@ -66,7 +66,7 @@ export interface Conversation { * The AI's follow-upresponse to the user's question */ - followUpResponse?: string; + followUpResponse?: string[]; /** * Array of sources referenced in the conversation diff --git a/stories/examples.ts b/stories/examples.ts index 3a42663..dd6e61a 100644 --- a/stories/examples.ts +++ b/stories/examples.ts @@ -190,8 +190,10 @@ export const sessionWithMessageResponseRecommended: Session[] = [ createdAt: new Date(), response: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1024px-React-icon.svg.png', - followUpResponse: + followUpResponse: [ 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1024px-React-icon.svg.png', + 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1024px-React-icon.svg.png' + ], updatedAt: new Date() } ] @@ -208,8 +210,10 @@ export const sessionWithMessageResponseRecommended: Session[] = [ createdAt: new Date(), response: 'React benefits include a declarative coding style, component-based architecture, virtual DOM, and a large community and ecosystem.', - followUpResponse: - 'React’s state management system enables components to maintain their own state and efficiently re-render only when necessary, making it easier to build responsive and interactive UIs.', + followUpResponse: [ + 'What are some downsides of React?', + 'What are alternative options to React?' + ], updatedAt: new Date() } ] From 218f88261b11e3b02c1a430831eba40cb08b3e39 Mon Sep 17 00:00:00 2001 From: taras Date: Tue, 27 Aug 2024 09:19:42 -0400 Subject: [PATCH 4/4] remove comment --- .../SessionMessage/MessageResponseRecommended.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx index 8444799..521b6e3 100644 --- a/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx +++ b/src/SessionMessages/SessionMessage/MessageResponseRecommended.tsx @@ -41,7 +41,6 @@ export const MessageResponseRecommended: FC< > {children || ( <> - {/* Map over follow-up responses to display them individually */} {followUpResponse.map((responseItem, index) => (