refactor: addinging copy buttons beside each message
This commit is contained in:
parent
188c912c9a
commit
3d7635e097
2 changed files with 51 additions and 26 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
import { ReactNode, useCallback, useEffect, useMemo, useRef, memo, useState } from 'react' // Import useMemo
|
import { ReactNode, useEffect, useMemo, useRef, memo, useState } from 'react' // Import useMemo
|
||||||
import { Message } from '@/api/lightrag'
|
import { Message } from '@/api/lightrag'
|
||||||
import useTheme from '@/hooks/useTheme'
|
import useTheme from '@/hooks/useTheme'
|
||||||
import Button from '@/components/ui/Button'
|
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
|
@ -14,7 +13,7 @@ import mermaid from 'mermaid'
|
||||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
||||||
import { oneLight, oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
|
import { oneLight, oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
|
||||||
|
|
||||||
import { LoaderIcon, CopyIcon, ChevronDownIcon } from 'lucide-react'
|
import { LoaderIcon, ChevronDownIcon } from 'lucide-react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export type MessageWithError = Message & {
|
export type MessageWithError = Message & {
|
||||||
|
|
@ -69,15 +68,6 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { //
|
||||||
}
|
}
|
||||||
loadKaTeX()
|
loadKaTeX()
|
||||||
}, [])
|
}, [])
|
||||||
const handleCopyMarkdown = useCallback(async () => {
|
|
||||||
if (message.content) {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(message.content)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(t('chat.copyError'), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [message, t]) // Added t to dependency array
|
|
||||||
|
|
||||||
const mainMarkdownComponents = useMemo(() => ({
|
const mainMarkdownComponents = useMemo(() => ({
|
||||||
code: (props: any) => (
|
code: (props: any) => (
|
||||||
|
|
@ -179,17 +169,6 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { //
|
||||||
>
|
>
|
||||||
{finalDisplayContent}
|
{finalDisplayContent}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
{message.role === 'assistant' && finalDisplayContent && finalDisplayContent.length > 0 && (
|
|
||||||
<Button
|
|
||||||
onClick={handleCopyMarkdown}
|
|
||||||
className="absolute right-0 bottom-0 size-6 rounded-md opacity-20 transition-opacity hover:opacity-100"
|
|
||||||
tooltip={t('retrievePanel.chatMessage.copyTooltip')}
|
|
||||||
variant="default"
|
|
||||||
size="icon"
|
|
||||||
>
|
|
||||||
<CopyIcon className="size-4" /> {/* Explicit size */}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(() => {
|
{(() => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useDebounce } from '@/hooks/useDebounce'
|
import { useDebounce } from '@/hooks/useDebounce'
|
||||||
import QuerySettings from '@/components/retrieval/QuerySettings'
|
import QuerySettings from '@/components/retrieval/QuerySettings'
|
||||||
import { ChatMessage, MessageWithError } from '@/components/retrieval/ChatMessage'
|
import { ChatMessage, MessageWithError } from '@/components/retrieval/ChatMessage'
|
||||||
import { EraserIcon, SendIcon } from 'lucide-react'
|
import { EraserIcon, SendIcon, CopyIcon } from 'lucide-react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { QueryMode } from '@/api/lightrag'
|
import type { QueryMode } from '@/api/lightrag'
|
||||||
|
|
||||||
|
|
@ -587,6 +587,30 @@ export default function RetrievalTesting() {
|
||||||
useSettingsStore.getState().setRetrievalHistory([])
|
useSettingsStore.getState().setRetrievalHistory([])
|
||||||
}, [setMessages])
|
}, [setMessages])
|
||||||
|
|
||||||
|
// Handle copying message content
|
||||||
|
const handleCopyMessage = useCallback(async (message: MessageWithError) => {
|
||||||
|
let contentToCopy = '';
|
||||||
|
|
||||||
|
if (message.role === 'user') {
|
||||||
|
// User messages: copy original content
|
||||||
|
contentToCopy = message.content || '';
|
||||||
|
} else {
|
||||||
|
// Assistant messages: prefer processed display content, fallback to original content
|
||||||
|
const finalDisplayContent = message.displayContent !== undefined
|
||||||
|
? message.displayContent
|
||||||
|
: (message.content || '');
|
||||||
|
contentToCopy = finalDisplayContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentToCopy.trim()) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(contentToCopy)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(t('chat.copyError'), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [t])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full gap-2 px-2 pb-12 overflow-hidden">
|
<div className="flex size-full gap-2 px-2 pb-12 overflow-hidden">
|
||||||
<div className="flex grow flex-col gap-4">
|
<div className="flex grow flex-col gap-4">
|
||||||
|
|
@ -611,9 +635,31 @@ export default function RetrievalTesting() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={message.id} // Use stable ID for key
|
key={message.id} // Use stable ID for key
|
||||||
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'} items-end gap-2`}
|
||||||
>
|
>
|
||||||
{<ChatMessage message={message} />}
|
{message.role === 'user' && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleCopyMessage(message)}
|
||||||
|
className="mb-2 size-6 rounded-md opacity-60 transition-opacity hover:opacity-100 shrink-0"
|
||||||
|
tooltip={t('retrievePanel.chatMessage.copyTooltip')}
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
>
|
||||||
|
<CopyIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<ChatMessage message={message} />
|
||||||
|
{message.role === 'assistant' && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleCopyMessage(message)}
|
||||||
|
className="mb-2 size-6 rounded-md opacity-60 transition-opacity hover:opacity-100 shrink-0"
|
||||||
|
tooltip={t('retrievePanel.chatMessage.copyTooltip')}
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
>
|
||||||
|
<CopyIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue