diff --git a/frontend/src/components/chatPanel/ChatWindow.jsx b/frontend/src/components/chatPanel/ChatWindow.jsx index c1b2e4e..c676819 100644 --- a/frontend/src/components/chatPanel/ChatWindow.jsx +++ b/frontend/src/components/chatPanel/ChatWindow.jsx @@ -73,7 +73,7 @@ const transformPage = (rawMsgs) => { continue; // преамбулу как сообщение не рендерим } if (type !== 'user') sawAi = true; - bubbles.push({ text: m.content, sender: type === 'user' ? 'user' : 'ai' }); + bubbles.push({ text: m.content, sender: type === 'user' ? 'user' : 'ai', timestamp: m.timestamp || null }); } return { bubbles, leadingMetas }; }; @@ -523,7 +523,10 @@ const ChatWindow = ({ onNavigateToDoc, isActive = true, activeChatId: propActive setChats((prev) => { const found = prev.find((c) => c.id === activeChatId); if (!found) return prev; - const newMessages = [...(found.messages || []), { text, sender: 'user', clientMsgId }]; + const newMessages = [ + ...(found.messages || []), + { text, sender: 'user', clientMsgId, timestamp: new Date().toISOString() }, + ]; const updatedChat = { ...found, id: conversationId, diff --git a/frontend/src/components/chatPanel/Message.jsx b/frontend/src/components/chatPanel/Message.jsx index 3d51494..8e3302c 100644 --- a/frontend/src/components/chatPanel/Message.jsx +++ b/frontend/src/components/chatPanel/Message.jsx @@ -468,53 +468,100 @@ function getMarkdownComponents(onNavigateToDoc) { }; } -const Message = ({ text, sender, toolCalls, toolCallsRunId, preparing, conversationId, onNavigateToDoc }) => { +/** Форматирует timestamp: если < 24ч — относительное время, иначе — дата. */ +const formatTimestamp = (ts) => { + if (!ts) return null; + const date = new Date(ts); + if (isNaN(date)) return null; + const diffMs = Date.now() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMs < 0) return null; + if (diffMin < 1) return '< 1 мин.'; + if (diffMin < 60) return `${diffMin} мин. назад`; + const diffH = Math.floor(diffMin / 60); + if (diffH < 24) return `${diffH} ч. назад`; + return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' }); +}; + +const formatFullDatetime = (ts) => { + if (!ts) return null; + const date = new Date(ts); + if (isNaN(date)) return null; + return date.toLocaleString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }); +}; + +const Message = ({ text, sender, toolCalls, toolCallsRunId, preparing, conversationId, onNavigateToDoc, timestamp }) => { const { t } = useTranslation('chat'); const [showSource, setShowSource] = useState(false); const messageClass = `message ${sender}`; const hasToolCalls = toolCalls && toolCalls.length > 0; const showPreparing = preparing && sender === 'ai'; + const timeLabel = formatTimestamp(timestamp); + const timeTitle = formatFullDatetime(timestamp); - const messageContent = ( + // Пузырь — только контент сообщения, без футера + const bubble = (
{text}
+ ) : (
+ {text}
- ) : (
-