'use client'; import { Brain, Search, FileText, ChevronDown, ChevronUp, BookSearch, } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { useEffect, useState } from 'react'; import { ResearchBlock, ResearchBlockSubStep } from '@/lib/types'; import { useChat } from '@/lib/hooks/useChat'; import { useTranslation } from '@/lib/localization/context'; const getStepIcon = (step: ResearchBlockSubStep) => { if (step.type === 'reasoning') { return ; } else if (step.type === 'searching' || step.type === 'upload_searching') { return ; } else if ( step.type === 'search_results' || step.type === 'upload_search_results' ) { return ; } else if (step.type === 'reading') { return ; } return null; }; const getStepTitle = ( step: ResearchBlockSubStep, isStreaming: boolean, t: (key: string) => string, ): string => { if (step.type === 'reasoning') { return isStreaming && !step.reasoning ? t('chat.brainstorming') : t('chat.thinking'); } else if (step.type === 'searching') { const n = step.searching.length; return t('chat.searchingQueries').replace('{count}', String(n)).replace('{plural}', n === 1 ? t('chat.query') : t('chat.queries')); } else if (step.type === 'search_results') { const n = step.reading.length; return t('chat.foundResults').replace('{count}', String(n)).replace('{plural}', n === 1 ? t('chat.result') : t('chat.results')); } else if (step.type === 'reading') { const n = step.reading.length; return t('chat.readingSources').replace('{count}', String(n)).replace('{plural}', n === 1 ? t('chat.source') : t('chat.sources')); } else if (step.type === 'upload_searching') { return t('chat.scanningDocs'); } else if (step.type === 'upload_search_results') { const n = step.results.length; return t('chat.readingDocs').replace('{count}', String(n)).replace('{plural}', n === 1 ? t('chat.document') : t('chat.documents')); } return t('chat.processing'); }; const AssistantSteps = ({ block, status, isLast, }: { block: ResearchBlock; status: 'answering' | 'completed' | 'error'; isLast: boolean; }) => { const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState( isLast && status === 'answering' ? true : false, ); const { researchEnded, loading } = useChat(); useEffect(() => { if (researchEnded && isLast) { setIsExpanded(false); } else if (status === 'answering' && isLast) { setIsExpanded(true); } }, [researchEnded, status]); if (!block || block.data.subSteps.length === 0) return null; return (
{isExpanded && (
{block.data.subSteps.map((step, index) => { const isLastStep = index === block.data.subSteps.length - 1; const isStreaming = loading && isLastStep && !researchEnded; return (
{getStepIcon(step)}
{index < block.data.subSteps.length - 1 && (
)}
{getStepTitle(step, isStreaming, t)} {step.type === 'reasoning' && ( <> {step.reasoning && (

{step.reasoning}

)} {isStreaming && !step.reasoning && (
)} )} {step.type === 'searching' && step.searching.length > 0 && (
{step.searching.map((query, idx) => ( {query} ))}
)} {(step.type === 'search_results' || step.type === 'reading') && step.reading.length > 0 && (
{step.reading.slice(0, 4).map((result, idx) => { const url = typeof result.metadata?.url === 'string' ? result.metadata.url : ''; const title = String(result.metadata?.title ?? 'Untitled'); let domain = ''; try { if (url) domain = new URL(url).hostname; } catch { /* invalid url */ } const faviconUrl = domain ? `https://s2.googleusercontent.com/s2/favicons?domain=${domain}&sz=128` : ''; return ( {faviconUrl && ( { e.currentTarget.style.display = 'none'; }} /> )} {title} ); })}
)} {step.type === 'upload_searching' && step.queries.length > 0 && (
{step.queries.map((query, idx) => ( {query} ))}
)} {step.type === 'upload_search_results' && step.results.length > 0 && (
{step.results.slice(0, 4).map((result, idx) => { const raw = result.metadata?.title ?? result.metadata?.fileName; const title = typeof raw === 'string' ? raw : 'Untitled document'; return (

{title}

); })}
)}
); })}
)}
); }; export default AssistantSteps;