| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import { useCallback } from 'react'; |
| | import { Toast, Modal } from '@douyinfe/semi-ui'; |
| | import { useTranslation } from 'react-i18next'; |
| | import { getTextContent } from '../../helpers'; |
| | import { ERROR_MESSAGES } from '../../constants/playground.constants'; |
| |
|
| | export const useMessageActions = ( |
| | message, |
| | setMessage, |
| | onMessageSend, |
| | saveMessages, |
| | ) => { |
| | const { t } = useTranslation(); |
| |
|
| | |
| | const handleMessageCopy = useCallback( |
| | (targetMessage) => { |
| | const textToCopy = getTextContent(targetMessage); |
| |
|
| | if (!textToCopy) { |
| | Toast.warning({ |
| | content: t(ERROR_MESSAGES.NO_TEXT_CONTENT), |
| | duration: 2, |
| | }); |
| | return; |
| | } |
| |
|
| | const copyToClipboard = async (text) => { |
| | if (navigator.clipboard?.writeText) { |
| | try { |
| | await navigator.clipboard.writeText(text); |
| | Toast.success({ |
| | content: t('消息已复制到剪贴板'), |
| | duration: 2, |
| | }); |
| | } catch (err) { |
| | console.error('Clipboard API 复制失败:', err); |
| | fallbackCopy(text); |
| | } |
| | } else { |
| | fallbackCopy(text); |
| | } |
| | }; |
| |
|
| | const fallbackCopy = (text) => { |
| | try { |
| | const textArea = document.createElement('textarea'); |
| | textArea.value = text; |
| | textArea.style.cssText = ` |
| | position: fixed; |
| | top: -9999px; |
| | left: -9999px; |
| | opacity: 0; |
| | pointer-events: none; |
| | z-index: -1; |
| | `; |
| | textArea.setAttribute('readonly', ''); |
| |
|
| | document.body.appendChild(textArea); |
| | textArea.select(); |
| | textArea.setSelectionRange(0, text.length); |
| |
|
| | const successful = document.execCommand('copy'); |
| | document.body.removeChild(textArea); |
| |
|
| | if (successful) { |
| | Toast.success({ |
| | content: t('消息已复制到剪贴板'), |
| | duration: 2, |
| | }); |
| | } else { |
| | throw new Error('execCommand copy failed'); |
| | } |
| | } catch (err) { |
| | console.error('回退复制方案也失败:', err); |
| |
|
| | let errorMessage = t(ERROR_MESSAGES.COPY_FAILED); |
| | if ( |
| | window.location.protocol === 'http:' && |
| | window.location.hostname !== 'localhost' |
| | ) { |
| | errorMessage = t(ERROR_MESSAGES.COPY_HTTPS_REQUIRED); |
| | } else if (!navigator.clipboard && !document.execCommand) { |
| | errorMessage = t(ERROR_MESSAGES.BROWSER_NOT_SUPPORTED); |
| | } |
| |
|
| | Toast.error({ |
| | content: errorMessage, |
| | duration: 4, |
| | }); |
| | } |
| | }; |
| |
|
| | copyToClipboard(textToCopy); |
| | }, |
| | [t], |
| | ); |
| |
|
| | |
| | const handleMessageReset = useCallback( |
| | (targetMessage) => { |
| | setMessage((prevMessages) => { |
| | |
| | let messageIndex = prevMessages.findIndex( |
| | (msg) => msg === targetMessage, |
| | ); |
| |
|
| | |
| | if (messageIndex === -1) { |
| | messageIndex = prevMessages.findIndex( |
| | (msg) => msg.id === targetMessage.id, |
| | ); |
| | } |
| |
|
| | if (messageIndex === -1) return prevMessages; |
| |
|
| | if (targetMessage.role === 'user') { |
| | const newMessages = prevMessages.slice(0, messageIndex); |
| | const contentToSend = getTextContent(targetMessage); |
| |
|
| | setTimeout(() => { |
| | onMessageSend(contentToSend); |
| | }, 100); |
| |
|
| | return newMessages; |
| | } else if ( |
| | targetMessage.role === 'assistant' || |
| | targetMessage.role === 'system' |
| | ) { |
| | let userMessageIndex = messageIndex - 1; |
| | while ( |
| | userMessageIndex >= 0 && |
| | prevMessages[userMessageIndex].role !== 'user' |
| | ) { |
| | userMessageIndex--; |
| | } |
| |
|
| | if (userMessageIndex >= 0) { |
| | const userMessage = prevMessages[userMessageIndex]; |
| | const newMessages = prevMessages.slice(0, userMessageIndex); |
| | const contentToSend = getTextContent(userMessage); |
| |
|
| | setTimeout(() => { |
| | onMessageSend(contentToSend); |
| | }, 100); |
| |
|
| | return newMessages; |
| | } |
| | } |
| |
|
| | return prevMessages; |
| | }); |
| | }, |
| | [setMessage, onMessageSend], |
| | ); |
| |
|
| | |
| | const handleMessageDelete = useCallback( |
| | (targetMessage) => { |
| | Modal.confirm({ |
| | title: t('确认删除'), |
| | content: t('确定要删除这条消息吗?'), |
| | okText: t('确定'), |
| | cancelText: t('取消'), |
| | okButtonProps: { |
| | type: 'danger', |
| | }, |
| | onOk: () => { |
| | setMessage((prevMessages) => { |
| | |
| | let messageIndex = prevMessages.findIndex( |
| | (msg) => msg === targetMessage, |
| | ); |
| |
|
| | |
| | if (messageIndex === -1) { |
| | messageIndex = prevMessages.findIndex( |
| | (msg) => msg.id === targetMessage.id, |
| | ); |
| | } |
| |
|
| | if (messageIndex === -1) return prevMessages; |
| |
|
| | let updatedMessages; |
| | if ( |
| | targetMessage.role === 'user' && |
| | messageIndex < prevMessages.length - 1 |
| | ) { |
| | const nextMessage = prevMessages[messageIndex + 1]; |
| | if (nextMessage.role === 'assistant') { |
| | Toast.success({ |
| | content: t('已删除消息及其回复'), |
| | duration: 2, |
| | }); |
| | updatedMessages = prevMessages.filter( |
| | (_, index) => |
| | index !== messageIndex && index !== messageIndex + 1, |
| | ); |
| | } else { |
| | Toast.success({ |
| | content: t('消息已删除'), |
| | duration: 2, |
| | }); |
| | updatedMessages = prevMessages.filter( |
| | (msg) => msg.id !== targetMessage.id, |
| | ); |
| | } |
| | } else { |
| | Toast.success({ |
| | content: t('消息已删除'), |
| | duration: 2, |
| | }); |
| | updatedMessages = prevMessages.filter( |
| | (msg) => msg.id !== targetMessage.id, |
| | ); |
| | } |
| |
|
| | |
| | setTimeout(() => saveMessages(updatedMessages), 0); |
| | return updatedMessages; |
| | }); |
| | }, |
| | }); |
| | }, |
| | [setMessage, t, saveMessages], |
| | ); |
| |
|
| | |
| | const handleRoleToggle = useCallback( |
| | (targetMessage) => { |
| | if ( |
| | !(targetMessage.role === 'assistant' || targetMessage.role === 'system') |
| | ) { |
| | return; |
| | } |
| |
|
| | const newRole = |
| | targetMessage.role === 'assistant' ? 'system' : 'assistant'; |
| |
|
| | setMessage((prevMessages) => { |
| | const updatedMessages = prevMessages.map((msg) => { |
| | if ( |
| | msg.id === targetMessage.id && |
| | (msg.role === 'assistant' || msg.role === 'system') |
| | ) { |
| | return { ...msg, role: newRole }; |
| | } |
| | return msg; |
| | }); |
| |
|
| | |
| | setTimeout(() => saveMessages(updatedMessages), 0); |
| | return updatedMessages; |
| | }); |
| |
|
| | Toast.success({ |
| | content: t( |
| | `已切换为${newRole === 'system' ? 'System' : 'Assistant'}角色`, |
| | ), |
| | duration: 2, |
| | }); |
| | }, |
| | [setMessage, t, saveMessages], |
| | ); |
| |
|
| | return { |
| | handleMessageCopy, |
| | handleMessageReset, |
| | handleMessageDelete, |
| | handleRoleToggle, |
| | }; |
| | }; |
| |
|