import { SimpleAudio } from '@gpustack/core-ui'; import { FullMarkdown } from '@gpustack/core-ui/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` height: max-content; width: max-content; `; const ThumbImgWrapper = styled.div.attrs({ className: 'custom-scrollbar-horizontal' })` display: flex; justify-content: flex-start; overflow-x: auto; flex-direction: column; padding: 0px; gap: 8px; &.has-content { padding-inline: 8px; padding-block: 8px 0; } `; 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++) { const 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 handleDeleteAudio = () => { updateMessage?.({ role: data.role, content: data.content, uid: data.uid, audio: [], imgs: data.imgs || [] }); }; 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 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} > 0) })} > {data.audio && data.audio.length > 0 && ( )} <> {data.role === Roles.User ? ( ) : ( <> {showMarkdown ? ( <>
) : ( )} )}
); }; useImperativeHandle(ref, () => ({ handleSaveEdit, handleCancelEdit, setEditContent })); return <>{editable ? renderEditMode() : renderReadOnlyMode()}; } ); export default MessageBody;