import { forwardRef, useState } from 'react'; import ReactMarkdown from 'react-markdown'; import { useSelector } from 'react-redux'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; import 'katex/dist/katex.min.css'; import DocsGPT3 from '../assets/cute_docsgpt3.svg'; import Dislike from '../assets/dislike.svg?react'; import Document from '../assets/document.svg'; import Like from '../assets/like.svg?react'; import Link from '../assets/link.svg'; import Sources from '../assets/sources.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; import Sidebar from '../components/Sidebar'; import { selectChunks, selectSelectedDocs, } from '../preferences/preferenceSlice'; import classes from './ConversationBubble.module.css'; import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; import SpeakButton from '../components/TextToSpeechButton'; const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false; const ConversationBubble = forwardRef< HTMLDivElement, { message: string; type: MESSAGE_TYPE; className?: string; feedback?: FEEDBACK; handleFeedback?: (feedback: FEEDBACK) => void; sources?: { title: string; text: string; source: string }[]; retryBtn?: React.ReactElement; } >(function ConversationBubble( { message, type, className, feedback, handleFeedback, sources, retryBtn }, ref, ) { const chunks = useSelector(selectChunks); const selectedDocs = useSelector(selectSelectedDocs); const [isLikeHovered, setIsLikeHovered] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); const [isDislikeClicked, setIsDislikeClicked] = useState(false); const [activeTooltip, setActiveTooltip] = useState(null); const [isSidebarOpen, setIsSidebarOpen] = useState(false); let bubble; if (type === 'QUESTION') { bubble = (
{message}
); } else { const preprocessLaTeX = (content: string) => { // Replace block-level LaTeX delimiters \[ \] with $$ $$ const blockProcessedContent = content.replace( /\\\[(.*?)\\\]/gs, (_, equation) => `$$${equation}$$`, ); // Replace inline LaTeX delimiters \( \) with $ $ const inlineProcessedContent = blockProcessedContent.replace( /\\\((.*?)\\\)/gs, (_, equation) => `$${equation}$`, ); return inlineProcessedContent; }; bubble = (
{DisableSourceFE || type === 'ERROR' || sources?.length === 0 || sources?.some((source) => source.source === 'None') ? null : !sources && chunks !== '0' && selectedDocs ? (
} />

Sources

{Array.from({ length: 4 }).map((_, index) => (
))}
) : ( sources && (
} />

Sources

{sources?.slice(0, 3)?.map((source, index) => (
setActiveTooltip(index)} onMouseOut={() => setActiveTooltip(null)} >

{source.text}

source.source && source.source !== 'local' ? window.open( source.source, '_blank', 'noopener, noreferrer', ) : null } > Document

{source.source && source.source !== 'local' ? source.source : source.title}

{activeTooltip === index && (
setActiveTooltip(index)} onMouseOut={() => setActiveTooltip(null)} >

{source.text}

)}
))} {(sources?.length ?? 0) > 3 && (
setIsSidebarOpen(true)} >

{`View ${ sources?.length ? sources.length - 3 : 0 } more`}

)}
) )}
} />

Answer

{String(children).replace(/\n$/, '')}
) : ( {children} ); }, ul({ children }) { return (
    {children}
); }, ol({ children }) { return (
    {children}
); }, table({ children }) { return (
{children}
); }, thead({ children }) { return ( {children} ); }, tr({ children }) { return ( {children} ); }, td({ children }) { return {children}; }, th({ children }) { return {children}; }, }} > {preprocessLaTeX(message)}
{/* Add SpeakButton here */}
{type === 'ERROR' && (
{retryBtn}
)} {handleFeedback && ( <>
{ handleFeedback?.('LIKE'); setIsLikeClicked(true); setIsDislikeClicked(false); }} onMouseEnter={() => setIsLikeHovered(true)} onMouseLeave={() => setIsLikeHovered(false)} >
{ handleFeedback?.('DISLIKE'); setIsDislikeClicked(true); setIsLikeClicked(false); }} onMouseEnter={() => setIsDislikeHovered(true)} onMouseLeave={() => setIsDislikeHovered(false)} >
)}
{sources && ( { setIsSidebarOpen(state); }} > )} ); } return bubble; }); type AllSourcesProps = { sources: { title: string; text: string; source: string }[]; }; function AllSources(sources: AllSourcesProps) { return (

{`${sources.sources.length} Sources`}

{sources.sources.map((source, index) => (

{`${index + 1}. ${source.title}`}

{source.source && source.source !== 'local' ? ( Link window.open(source.source, '_blank', 'noopener, noreferrer') } > ) : null}

{source.text}

))}
); } export default ConversationBubble;