Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slots for Messages and Message components #3

Merged
merged 4 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions src/SessionMessages/MessageActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { SessionsContext } from '@/SessionsContext';
import { Slot } from '@radix-ui/react-slot';
import { cn, IconButton } from 'reablocks';
import {
FC,
PropsWithChildren,
ReactElement,
ReactNode,
useContext
} from 'react';
import CopyIcon from '@/assets/copy.svg?react';
import ThumbsDownIcon from '@/assets/thumbs-down.svg?react';
import ThumbUpIcon from '@/assets/thumbs-up.svg?react';
import RefreshIcon from '@/assets/refresh.svg?react';

export interface MessageActionsProps extends PropsWithChildren {
/**
* Question to be copied
*/
question: string;

/**
* Response to be copied
*/
response?: string;

/**
* Icon to show for copy.
*/
copyIcon?: ReactElement;

/**
* Icon to show for thumbs up.
*/
thumbsUpIcon?: ReactElement;

/**
* Icon to show for thumbs down.
*/
thumbsDownIcon?: ReactElement;

/**
* Icon to show for refresh.
*/
refreshIcon?: ReactElement;

/**
* Callback function to handle copying.
*/
onCopy?: () => void;

/**
* Callback function to handle upvoting.
*/
onUpvote?: () => void;

/**
* Callback function to handle downvoting.
*/
onDownvote?: () => void;

/**
* Callback function to handle refreshing.
*/
onRefresh?: () => void;

/**
* Children to render as MessageAction slot.
*/
children?: ReactNode;
}

export const MessageActions: FC<MessageActionsProps> = ({
children,
...props
}) => {
const { theme } = useContext(SessionsContext);
const {
question,
response,
copyIcon = <CopyIcon />,
thumbsUpIcon = <ThumbUpIcon />,
thumbsDownIcon = <ThumbsDownIcon />,
refreshIcon = <RefreshIcon />,
onCopy,
onUpvote,
onDownvote,
onRefresh
} = props;
const Comp = children ? Slot : 'div';

const handleCopy = (text: string) => {
navigator.clipboard
.writeText(text)
.then(() => {
console.log('Text copied to clipboard');
})
.catch(err => {
console.error('Could not copy text: ', err);
});
};

return (
(copyIcon || thumbsDownIcon || thumbsUpIcon || refreshIcon) && (
<Comp className={cn(theme.messages.message.footer.base)}>
{children || (
<>
{copyIcon && (
<IconButton
variant="text"
disablePadding
title="Copy question and response"
className={cn(theme.messages.message.footer.copy)}
onClick={
onCopy ? onCopy : () => handleCopy(`${question}\n${response}`)
}
>
{copyIcon}
</IconButton>
)}
{thumbsUpIcon && (
<IconButton
variant="text"
disablePadding
title="Upvote"
className={cn(theme.messages.message.footer.upvote)}
onClick={onUpvote}
>
{thumbsUpIcon}
</IconButton>
)}
{thumbsDownIcon && (
<IconButton
variant="text"
disablePadding
title="Downvote"
className={cn(theme.messages.message.footer.downvote)}
onClick={onDownvote}
>
{thumbsDownIcon}
</IconButton>
)}
{refreshIcon && (
<IconButton
variant="text"
disablePadding
title="Refresh"
className={cn(theme.messages.message.footer.refresh)}
onClick={onRefresh}
>
{refreshIcon}
</IconButton>
)}
</>
)}
</Comp>
)
);
};
6 changes: 3 additions & 3 deletions src/SessionMessages/MessageFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { SessionsContext } from '@/SessionsContext';
import { Ellipsis, cn } from 'reablocks';
import FileIcon from '@/assets/file.svg?react';

interface MessageFileProps extends ConversationFile {
/**
export interface MessageFileProps extends ConversationFile {
/**
* Icon to show for delete.
*/
fileIcon?: ReactElement;
fileIcon?: ReactElement;

/**
* Limit for the name.
Expand Down
39 changes: 39 additions & 0 deletions src/SessionMessages/MessageFiles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { SessionsContext } from '@/SessionsContext';
import { ConversationFile } from '@/types';
import { cn } from 'reablocks';
import { FC, PropsWithChildren, ReactNode, useContext } from 'react';
import { MessageFile } from './MessageFile';
import { Slot } from '@radix-ui/react-slot';

interface MessageFilesProps extends PropsWithChildren {
/**
* Files to render.
*/
files: ConversationFile[];

/**
* Children to render as MessageFile slot.
*/
children?: ReactNode;
}

export const MessageFiles: FC<MessageFilesProps> = ({ files, children }) => {
const { theme } = useContext(SessionsContext);
const Comp = children ? Slot : MessageFile;

if (!files || files.length === 0) {
return null;
}

return (
files.length > 0 && (
<div className={cn(theme.messages.message.files.base)}>
{files.map((file, index) => (
<Comp key={index} {...file}>
{children}
</Comp>
))}
</div>
)
);
};
38 changes: 38 additions & 0 deletions src/SessionMessages/MessageQuestion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { SessionsContext } from '@/SessionsContext';
import { Slot } from '@radix-ui/react-slot';
import { cn } from 'reablocks';
import { FC, PropsWithChildren, ReactNode, useContext } from 'react';
import { Markdown } from '@/Markdown';
import { PluggableList } from 'react-markdown/lib';
import remarkGfm from 'remark-gfm';
import remarkYoutube from 'remark-youtube';

export interface MessageQuestionProps extends PropsWithChildren {
/**
* Question to render.
*/
question: string;

/**
* Children to render as MessageQuestion slot.
*/
children?: ReactNode;
}

export const MessageQuestion: FC<MessageQuestionProps> = ({
question,
children
}) => {
const { theme, remarkPlugins = [remarkGfm, remarkYoutube] } =
useContext(SessionsContext);
const Comp = children ? Slot : 'div';
return (
<Comp className={cn(theme.messages.message.question)}>
{children || (
<Markdown remarkPlugins={remarkPlugins as PluggableList[]}>
{question}
</Markdown>
)}
</Comp>
);
};
58 changes: 58 additions & 0 deletions src/SessionMessages/MessageResponse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { SessionsContext } from '@/SessionsContext';
import { Slot } from '@radix-ui/react-slot';
import { motion } from 'framer-motion';
import { cn } from 'reablocks';
import { FC, ReactNode, useContext } from 'react';
import { Markdown } from '@/Markdown';
import { PluggableList } from 'react-markdown/lib';
import remarkGfm from 'remark-gfm';
import remarkYoutube from 'remark-youtube';

export interface MessageResponseProps {
/**
* Response to render.
*/
response: string;

/**
* Whether the response is loading.
*/
isLoading?: boolean;

/**
* Children to render as MessageResponse slot.
*/
children?: ReactNode;
}

export const MessageResponse: FC<MessageResponseProps> = ({
response,
isLoading,
children
}) => {
const { theme, remarkPlugins = [remarkGfm, remarkYoutube] } =
useContext(SessionsContext);
const Comp = children ? Slot : 'div';
return (
<Comp className={cn(theme.messages.message.response)}>
{children || (
<>
<Markdown remarkPlugins={remarkPlugins as PluggableList[]}>
{response}
</Markdown>
{isLoading && (
<motion.div
className={cn(theme.messages.message.cursor)}
animate={{ opacity: [1, 0] }}
transition={{
duration: 0.7,
repeat: Infinity,
repeatType: 'reverse'
}}
/>
)}
</>
)}
</Comp>
);
};
2 changes: 1 addition & 1 deletion src/SessionMessages/MessageSource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ConversationSource } from '@/types';
import { SessionsContext } from '@/SessionsContext';
import { Ellipsis, cn } from 'reablocks';

interface MessageSourceProps extends ConversationSource {
export interface MessageSourceProps extends ConversationSource {
/**
* Limit for the title.
*/
Expand Down
43 changes: 43 additions & 0 deletions src/SessionMessages/MessageSources.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { SessionsContext } from '@/SessionsContext';
import { ConversationSource } from '@/types';
import { cn } from 'reablocks';
import { FC, PropsWithChildren, ReactNode, useContext } from 'react';
import { Slot } from '@radix-ui/react-slot';
import { MessageSource } from './MessageSource';

interface MessageSourcesProps extends PropsWithChildren {
/**
* Sources to render.
*/
sources: ConversationSource[];

/**
* Children to render as MessageSource slot.
*/
children?: ReactNode;
}

export const MessageSources: FC<MessageSourcesProps> = ({
sources,
children
}) => {
const { theme } = useContext(SessionsContext);
const Comp = children ? Slot : MessageSource;

if (!sources || sources.length === 0) {
return null;
}

return (
sources &&
sources.length > 0 && (
<div className={cn(theme.messages.message.sources.base)}>
{sources.map((source, index) => (
<Comp key={index} {...source}>
{children}
</Comp>
))}
</div>
)
);
};
Loading
Loading