feat: implement copy message (#13)
This commit is contained in:
commit
cd340320b7
5 changed files with 86 additions and 40 deletions
|
|
@ -20,7 +20,6 @@ if (!process.env.OPENAI_API_KEY) {
|
|||
export const POST = auth(async function POST(req: Request) {
|
||||
const json = await req.json()
|
||||
// @ts-ignore
|
||||
console.log(req.auth) // todo fix types
|
||||
const messages = json.messages.map((m: any) => ({
|
||||
content: m.content,
|
||||
role: m.role
|
||||
|
|
|
|||
38
components/chat-message-actions.tsx
Normal file
38
components/chat-message-actions.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { IconCheck, IconCopy } from '@/components/ui/icons'
|
||||
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { type Message } from 'ai-connector'
|
||||
|
||||
interface ChatMessageActionsProps extends React.ComponentProps<'div'> {
|
||||
message: Message
|
||||
}
|
||||
|
||||
export function ChatMessageActions({
|
||||
message,
|
||||
className,
|
||||
...props
|
||||
}: ChatMessageActionsProps) {
|
||||
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-end w-full md:absolute md:-top-2 md:-right-10 md:opacity-0 group-hover:opacity-100 transition-opacity',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyToClipboard(message.content)}
|
||||
>
|
||||
{isCopied ? <IconCheck /> : <IconCopy />}
|
||||
<span className="sr-only">Copy message</span>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import { cn } from '@/lib/utils'
|
|||
import { CodeBlock } from '@/components/ui/codeblock'
|
||||
import { MemoizedReactMarkdown } from '@/components/markdown'
|
||||
import { IconOpenAI, IconUser } from '@/components/ui/icons'
|
||||
import { ChatMessageActions } from '@/components/chat-message-actions'
|
||||
|
||||
export interface ChatMessageProps {
|
||||
message: Message
|
||||
|
|
@ -13,7 +14,10 @@ export interface ChatMessageProps {
|
|||
|
||||
export function ChatMessage({ message, ...props }: ChatMessageProps) {
|
||||
return (
|
||||
<div className={cn('relative mb-4 flex items-start md:-ml-12')} {...props}>
|
||||
<div
|
||||
className={cn('relative mb-4 flex items-start group md:-ml-12')}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow',
|
||||
|
|
@ -24,7 +28,8 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
|
|||
>
|
||||
{message.role === 'user' ? <IconUser /> : <IconOpenAI />}
|
||||
</div>
|
||||
<div className="ml-4 px-1">
|
||||
<div className="ml-4 px-1 space-y-2 flex-1">
|
||||
<div>
|
||||
<MemoizedReactMarkdown
|
||||
className="prose leading-6 dark:prose-invert prose-p:leading-[1.8rem] prose-pre:rounded-md prose-pre:bg-[#282c34]"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
|
|
@ -36,7 +41,9 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
|
|||
if (children.length) {
|
||||
if (children[0] == '▍') {
|
||||
return (
|
||||
<span className="mt-1 animate-pulse cursor-default">▍</span>
|
||||
<span className="mt-1 animate-pulse cursor-default">
|
||||
▍
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +70,8 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
|
|||
{message.content}
|
||||
</MemoizedReactMarkdown>
|
||||
</div>
|
||||
<ChatMessageActions message={message} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FC, memo } from 'react'
|
|||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
||||
import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
|
||||
|
||||
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-cliipboard'
|
||||
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
|
||||
import { IconCheck, IconCopy, IconDownload } from '@/components/ui/icons'
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
|
||||
export interface useCopyToClipboardProps {
|
||||
timeout?: number
|
||||
|
|
@ -9,7 +9,7 @@ export interface useCopyToClipboardProps {
|
|||
export function useCopyToClipboard({
|
||||
timeout = 2000
|
||||
}: useCopyToClipboardProps) {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false)
|
||||
const [isCopied, setIsCopied] = React.useState<Boolean>(false)
|
||||
|
||||
const copyToClipboard = (value: string) => {
|
||||
if (
|
||||
Loading…
Add table
Add a link
Reference in a new issue