import { ArrowDown, Bot, ChevronDown, Clock3, Copy, Download, Sparkles, UserRound } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import clsx from 'clsx' import { MESSAGE_COLLAPSE_AT, buildListModeMessages, copyTextWithFallback, downloadTextFile, formatElapsed, } from './chatHistoryUtils' function ExpandableText({ text = '', threshold = MESSAGE_COLLAPSE_AT, expandLabel, collapseLabel, buttonClassName = 'text-white hover:text-white/80' }) { const shouldCollapse = text.length > threshold const [expanded, setExpanded] = useState(false) const contentRef = useRef(null) const [maxHeight, setMaxHeight] = useState('none') useEffect(() => { setExpanded(false) }, [text]) const visibleText = shouldCollapse && !expanded ? `${text.slice(0, threshold)}...` : text useEffect(() => { if (!contentRef.current) return setMaxHeight(`${contentRef.current.scrollHeight}px`) }, [expanded, visibleText]) return (
{visibleText}
{shouldCollapse && ( )}
) } function RequestMessages({ item, t, messages }) { const requestMessages = Array.isArray(messages) && messages.length > 0 ? messages : [{ role: 'user', content: item?.user_input || t('chatHistory.emptyUserInput') }] return (
{requestMessages.map((message, index) => { const role = message.role || 'user' const isUser = role === 'user' const isAssistant = role === 'assistant' const isTool = role === 'tool' const label = isUser ? t('chatHistory.role.user') : (isAssistant ? t('chatHistory.role.assistant') : (isTool ? t('chatHistory.role.tool') : t('chatHistory.role.system'))) return (
{isUser ? : }
{label}
{message.content || t('chatHistory.emptyUserInput')}
) })}
) } function PromptTextActions({ text, filename, copyTitle, downloadTitle, t, onMessage, buttonClassName }) { const handleCopy = async () => { try { await copyTextWithFallback(text) onMessage?.('success', t('chatHistory.copySuccess')) } catch { onMessage?.('error', t('chatHistory.copyFailed')) } } const handleDownload = () => { try { downloadTextFile(filename, text) onMessage?.('success', t('chatHistory.downloadSuccess')) } catch { onMessage?.('error', t('chatHistory.downloadFailed')) } } return (
) } function MergedPromptView({ item, t, onMessage }) { const merged = item?.final_prompt || '' return (
{t('chatHistory.mergedInput')}
) } function HistoryTextView({ item, t, onMessage }) { const historyText = (item?.history_text || '').trim() if (!historyText) return null return (
HISTORY
) } function MetaGrid({ selectedItem, t }) { return (
{t('chatHistory.metaTitle')}
{t('chatHistory.metaAccount')}
{selectedItem.account_id || t('chatHistory.metaUnknown')}
{t('chatHistory.metaElapsed')}
{formatElapsed(selectedItem.elapsed_ms, t)}
{t('chatHistory.metaSurface')}
{selectedItem.surface || t('chatHistory.metaUnknown')}
{t('chatHistory.metaModel')}
{selectedItem.model || t('chatHistory.metaUnknown')}
{t('chatHistory.metaStatusCode')}
{selectedItem.status_code || '-'}
{t('chatHistory.metaStream')}
{selectedItem.stream ? t('chatHistory.streamMode') : t('chatHistory.nonStreamMode')}
{t('chatHistory.metaCaller')}
{selectedItem.caller_id || t('chatHistory.metaUnknown')}
) } export default function DetailConversation({ selectedItem, t, viewMode, detailScrollRef, assistantStartRef, bottomButtonClassName, onMessage }) { if (!selectedItem) return null const listModeState = viewMode === 'list' ? buildListModeMessages(selectedItem, t) : null const showHistoryAtTop = viewMode !== 'list' || !listModeState?.historyMerged return ( <> {showHistoryAtTop && } {viewMode === 'list' ? : }
{(selectedItem.reasoning_content || '').trim() && (
{t('chatHistory.reasoningTrace')}
{selectedItem.reasoning_content}
)}
{selectedItem.status === 'error' ? {selectedItem.error || t('chatHistory.failedOutput')} : (selectedItem.content || t('chatHistory.emptyAssistantOutput'))}
) }