import AudioElement from '@/components/audio-player/audio-element'; import FullMarkdown from '@/components/markdown-viewer/full-markdown'; import { Input } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; import styled from 'styled-components'; import { Roles } from '../../config'; import { MessageItem, MessageItemAction } from '../../config/types'; import ThumbImg from '../thumb-img'; import ThinkContent from './think-content'; const AudioWrapper = styled.div` padding-left: 10px; audio { padding-top: 10px; } `; interface MessageBodyProps { ref?: any; data: MessageItem; editable?: boolean; loading?: boolean; showTitle?: boolean; actions?: MessageItemAction[]; showMarkdown?: boolean; reasoningContent: { thought: string; isThinking: boolean; result: string; }; collapsed?: boolean; updateMessage?: (message: MessageItem) => void; onDelete?: () => void; } const MessageBody: React.FC = forwardRef( ( { editable, data, loading, reasoningContent, showMarkdown, collapsed, updateMessage }, ref ) => { const inputRef = useRef(null); const imgCountRef = useRef(0); const [editContent, setEditContent] = React.useState(data.content); const getPasteContent = useCallback( async (event: any) => { // @ts-ignore const clipboardData = event.clipboardData || window.clipboardData; const items = clipboardData.items; const imgPromises: Promise[] = []; for (let i = 0; i < items.length; i++) { let item = items[i]; if (item.kind === 'file' && item.type.indexOf('image') !== -1) { const file = item.getAsFile(); const imgPromise = new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function (event) { const base64String = event.target?.result as string; if (base64String) { resolve(base64String); } else { reject('Failed to convert image to base64'); } }; reader.readAsDataURL(file); }); imgPromises.push(imgPromise); } else if (item.kind === 'string') { // string } } try { const imgs = await Promise.all(imgPromises); if (imgs.length) { const list = _.map(imgs, (img: string) => { imgCountRef.current += 1; return { uid: imgCountRef.current, dataUrl: img }; }); updateMessage?.({ role: data.role, content: data.content, uid: data.uid, audio: data.audio || [], imgs: [...(data.imgs || []), ...list] }); } } catch (error) { console.error('Error processing images:', error); } }, [data, updateMessage] ); const handleOnPaste = (e: any) => { const text = e.clipboardData.getData('text'); if (!text) { e.preventDefault(); getPasteContent(e); } }; const handleDeleteImg = (uid: number | string) => { const list = _.filter(data.imgs, (item: MessageItem) => item.uid !== uid); updateMessage?.({ role: data.role, content: data.content, uid: data.uid, audio: data.audio || [], imgs: list }); }; const handleMessageChange = (e: any) => { updateMessage?.({ imgs: data.imgs || [], audio: data.audio || [], role: data.role, content: e.target.value, uid: data.uid }); }; const handleEdit = (e: any) => { setEditContent(e.target.value); }; const handleCancelEdit = () => { setEditContent(data.content); }; const handleSaveEdit = () => { updateMessage?.({ role: data.role, content: editContent, uid: data.uid, audio: data.audio || [], imgs: data.imgs }); }; const handleDeleteLastImage = useCallback(() => { if (data.imgs && data.imgs?.length > 0) { const newImgList = [...(data.imgs || [])]; const lastImage = newImgList.pop(); if (lastImage) { handleDeleteImg(lastImage.uid); } } }, [data.imgs, handleDeleteImg]); const handleKeyDown = useCallback( (event: any) => { if ( event.key === 'Backspace' && data.content === '' && data.imgs && data.imgs?.length > 0 ) { // inputref blur event.preventDefault(); handleDeleteLastImage(); } }, [data, handleDeleteLastImage] ); const handleClickWrapper = (e: any) => { e.stopPropagation(); e.preventDefault(); inputRef.current?.focus(); }; const renderReadOnlyMode = () => { return (
0) })} >
{data.audio && data.audio.length > 0 && ( )}
{data.content &&
{data.content}
}
); }; const renderEditMode = () => { return (
0) })} onClick={handleClickWrapper} >
{data.audio && data.audio.length > 0 && ( )}
<> {data.role === Roles.User ? ( ) : ( <> {showMarkdown ? ( <>
) : ( )} )}
); }; useImperativeHandle(ref, () => ({ handleSaveEdit, handleCancelEdit, setEditContent })); return <>{editable ? renderEditMode() : renderReadOnlyMode()}; } ); export default MessageBody;