diff --git a/src/renderer/src/pages/home/Messages/MessageImage.tsx b/src/renderer/src/pages/home/Messages/MessageImage.tsx index 9dc0566c..21550d21 100644 --- a/src/renderer/src/pages/home/Messages/MessageImage.tsx +++ b/src/renderer/src/pages/home/Messages/MessageImage.tsx @@ -36,35 +36,51 @@ const MessageImage: FC = ({ message }) => { } } - // 复制 base64 图片到剪贴板 - const onCopy = async (imageBase64: string) => { + // 复制图片到剪贴板 + const onCopy = async (type: string, image: string) => { try { - const base64Data = imageBase64.split(',')[1] - const mimeType = imageBase64.split(';')[0].split(':')[1] + switch (type) { + case 'base64': { + // 处理 base64 格式的图片 + const parts = image.split(';base64,') + if (parts.length === 2) { + const mimeType = parts[0].replace('data:', '') + const base64Data = parts[1] + const byteCharacters = atob(base64Data) + const byteArrays: Uint8Array[] = [] - const byteCharacters = atob(base64Data) - const byteArrays: Uint8Array[] = [] + for (let offset = 0; offset < byteCharacters.length; offset += 512) { + const slice = byteCharacters.slice(offset, offset + 512) + const byteNumbers = new Array(slice.length) + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i) + } + const byteArray = new Uint8Array(byteNumbers) + byteArrays.push(byteArray) + } - for (let i = 0; i < byteCharacters.length; i += 512) { - const slice = byteCharacters.slice(i, i + 512) - - const byteNumbers = new Array(slice.length) - for (let j = 0; j < slice.length; j++) { - byteNumbers[j] = slice.charCodeAt(j) + const blob = new Blob(byteArrays, { type: mimeType }) + await navigator.clipboard.write([new ClipboardItem({ [mimeType]: blob })]) + } else { + throw new Error('无效的 base64 图片格式') + } + break } + case 'url': + { + // 处理 URL 格式的图片 + const response = await fetch(image) + const blob = await response.blob() - const byteArray = new Uint8Array(byteNumbers) - byteArrays.push(byteArray) + await navigator.clipboard.write([ + new ClipboardItem({ + [blob.type]: blob + }) + ]) + } + break } - const blob = new Blob(byteArrays, { type: mimeType }) - - await navigator.clipboard.write([ - new ClipboardItem({ - [blob.type]: blob - }) - ]) - window.message.success(t('message.copy.success')) } catch (error) { console.error('复制图片失败:', error) @@ -95,7 +111,7 @@ const MessageImage: FC = ({ message }) => { - onCopy(image)} /> + onCopy(message.metadata?.generateImage?.type!, image)} /> onDownload(image, index)} /> ) diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 382436e7..6a0d8d6d 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -112,6 +112,14 @@ const MessageMenubar: FC = (props) => { let textToEdit = message.content + // 如果是包含图片的消息,添加图片的 markdown 格式 + if (message.metadata?.generateImage?.images) { + const imageMarkdown = message.metadata.generateImage.images + .map((image, index) => `![image-${index}](${image})`) + .join('\n') + textToEdit = `${textToEdit}\n\n${imageMarkdown}` + } + if (message.role === 'assistant' && message.model && isReasoningModel(message.model)) { const processedMessage = withMessageThought(clone(message)) textToEdit = processedMessage.content @@ -135,8 +143,40 @@ const MessageMenubar: FC = (props) => { }) if (editedText && editedText !== textToEdit) { - await editMessage(message.id, { content: editedText }) - resendMessage && handleResendUserMessage({ ...message, content: editedText }) + // 解析编辑后的文本,提取图片 URL + const imageRegex = /!\[image-\d+\]\((.*?)\)/g + const imageUrls: string[] = [] + let match + let content = editedText + + while ((match = imageRegex.exec(editedText)) !== null) { + imageUrls.push(match[1]) + content = content.replace(match[0], '') + } + + // 更新消息内容,保留图片信息 + await editMessage(message.id, { + content: content.trim(), + metadata: { + ...message.metadata, + generateImage: imageUrls.length > 0 ? { + type: 'url', + images: imageUrls + } : undefined + } + }) + + resendMessage && handleResendUserMessage({ + ...message, + content: content.trim(), + metadata: { + ...message.metadata, + generateImage: imageUrls.length > 0 ? { + type: 'url', + images: imageUrls + } : undefined + } + }) } }, [message, editMessage, handleResendUserMessage, t])