import { useState, useRef, useEffect } from 'react'; import { Box, Stack, Typography, IconButton, Tooltip, TextField } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import EditIcon from '@mui/icons-material/Edit'; import CheckIcon from '@mui/icons-material/Check'; import type { UIMessage } from 'ai'; import type { MessageMeta } from '@/types/agent'; interface UserMessageProps { message: UIMessage; isLastTurn?: boolean; onUndoTurn?: () => void; onEditAndRegenerate?: (messageId: string, newText: string) => void | Promise; isProcessing?: boolean; } function extractText(message: UIMessage): string { return message.parts .filter((p): p is Extract => p.type === 'text') .map(p => p.text) .join(''); } export default function UserMessage({ message, isLastTurn = false, onUndoTurn, onEditAndRegenerate, isProcessing = false, }: UserMessageProps) { const showUndo = isLastTurn && !isProcessing && !!onUndoTurn; const showEdit = !isProcessing && !!onEditAndRegenerate; const text = extractText(message); const meta = message.metadata as MessageMeta | undefined; const timeStr = meta?.createdAt ? new Date(meta.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : null; const [isEditing, setIsEditing] = useState(false); const [editText, setEditText] = useState(text); const inputRef = useRef(null); useEffect(() => { if (isEditing && inputRef.current) { inputRef.current.focus(); inputRef.current.selectionStart = inputRef.current.value.length; } }, [isEditing]); const handleStartEdit = () => { setEditText(text); setIsEditing(true); }; const handleConfirmEdit = () => { const trimmed = editText.trim(); if (!trimmed || trimmed === text) { setIsEditing(false); return; } setIsEditing(false); onEditAndRegenerate?.(message.id, trimmed); }; const handleCancelEdit = () => { setIsEditing(false); setEditText(text); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleConfirmEdit(); } else if (e.key === 'Escape') { handleCancelEdit(); } }; return ( {!isEditing && (showUndo || showEdit) && ( {showEdit && ( )} {showUndo && ( )} )} {isEditing ? ( setEditText(e.target.value)} onKeyDown={handleKeyDown} variant="outlined" size="small" sx={{ '& .MuiOutlinedInput-root': { fontFamily: 'inherit', fontSize: '0.925rem', lineHeight: 1.65, color: 'var(--text)', '& fieldset': { borderColor: 'var(--accent-yellow)', borderWidth: 1.5 }, '&:hover fieldset': { borderColor: 'var(--accent-yellow)' }, '&.Mui-focused fieldset': { borderColor: 'var(--accent-yellow)' }, }, }} /> ) : ( {text} )} {timeStr && !isEditing && ( {timeStr} )} ); }