'use client'; /* eslint-disable @next/next/no-img-element */ import React, { MutableRefObject } from 'react'; import { cn } from '@/lib/utils'; import { BookCopy, Disc3, Volume2, StopCircle, Layers3, Plus, CornerDownRight, } from 'lucide-react'; import Markdown, { MarkdownToJSX, RuleType } from 'markdown-to-jsx'; import Copy from './MessageActions/Copy'; import Rewrite from './MessageActions/Rewrite'; import MessageSources from './MessageSources'; import SearchImages from './SearchImages'; import SearchVideos from './SearchVideos'; import { useSpeech } from 'react-text-to-speech'; import ThinkBox from './ThinkBox'; import { useChat, Section } from '@/lib/hooks/useChat'; import { useTranslation } from '@/lib/localization/context'; import Citation from './MessageRenderer/Citation'; import AssistantSteps from './AssistantSteps'; import { ResearchBlock } from '@/lib/types'; import Renderer from './Widgets/Renderer'; import CodeBlock from './MessageRenderer/CodeBlock'; const ThinkTagProcessor = ({ children, thinkingEnded, }: { children: React.ReactNode; thinkingEnded: boolean; }) => { return ( ); }; const MessageBox = ({ section, sectionIndex, dividerRef, isLast, }: { section: Section; sectionIndex: number; dividerRef?: MutableRefObject; isLast: boolean; }) => { const { loading, sendMessage, rewrite, messages, researchEnded, chatHistory, } = useChat(); const { t } = useTranslation(); const parsedMessage = section.parsedTextBlocks.join('\n\n'); const speechMessage = section.speechMessage || ''; const thinkingEnded = section.thinkingEnded; const sourceBlocks = section.message.responseBlocks.filter( (block): block is typeof block & { type: 'source' } => block.type === 'source', ); const sources = sourceBlocks.flatMap((block) => block.data); const hasContent = section.parsedTextBlocks.some( (t) => typeof t === 'string' && t.trim().length > 0, ); const { speechStatus, start, stop } = useSpeech({ text: speechMessage }); const markdownOverrides: MarkdownToJSX.Options = { renderRule(next, node, renderChildren, state) { if (node.type === RuleType.codeInline) { return `\`${node.text}\``; } if (node.type === RuleType.codeBlock) { return ( {node.text} ); } return next(); }, overrides: { think: { component: ThinkTagProcessor, props: { thinkingEnded: thinkingEnded, }, }, citation: { component: Citation, }, }, }; const isSummaryArticle = section.message.query.startsWith('Summary: ') && section.message.articleTitle; const summaryUrl = isSummaryArticle ? section.message.query.slice(9) : undefined; return (
{isSummaryArticle ? (

{section.message.articleTitle}

{summaryUrl}
) : (

{section.message.query}

)}
{sources.length > 0 && (

{t('chat.sources')}

)} {section.message.responseBlocks .filter( (block): block is ResearchBlock => block.type === 'research' && block.data.subSteps.length > 0, ) .map((researchBlock) => (
))} {isLast && loading && !researchEnded && !section.message.responseBlocks.some( (b) => b.type === 'research' && b.data.subSteps.length > 0, ) && (
{t('chat.brainstorming')}
)} {section.widgets.length > 0 && }
{sources.length > 0 && (

{t('chat.answer')}

)} {!hasContent && sources.length > 0 && isLast && loading && (

{t('chat.formingAnswer')}

)} {!hasContent && sources.length > 0 && isLast && !loading && (

{t('chat.answerFailed')}

)} {hasContent && ( <> {parsedMessage} {loading && isLast ? null : (
)} {isLast && section.suggestions && section.suggestions.length > 0 && hasContent && !loading && (

{t('chat.related')}

{section.suggestions.map( (suggestion: string, i: number) => (
), )}
)} )}
{hasContent && (
)}
); }; export default MessageBox;