| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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, |
| }; |
| }; |
|
|