import 'katex/dist/katex.min.css'; 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 rehypeKatex from 'rehype-katex'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; 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 Edit from '../assets/edit.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; import Sidebar from '../components/Sidebar'; import SpeakButton from '../components/TextToSpeechButton'; import { selectChunks, selectSelectedDocs, } from '../preferences/preferenceSlice'; import classes from './ConversationBubble.module.css'; import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; 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; questionNumber?: number; handleUpdatedQuestionSubmission?: ( updatedquestion?: string, updated?: boolean, index?: number, ) => void; } >(function ConversationBubble( { message, type, className, feedback, handleFeedback, sources, retryBtn, questionNumber, handleUpdatedQuestionSubmission, }, ref, ) { // const bubbleRef = useRef(null); const chunks = useSelector(selectChunks); const selectedDocs = useSelector(selectSelectedDocs); const [isLikeHovered, setIsLikeHovered] = useState(false); const [isEditClicked, setIsEditClicked] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isQuestionHovered, setIsQuestionHovered] = useState(false); const [editInputBox, setEditInputBox] = useState(''); const [isLikeClicked, setIsLikeClicked] = useState(false); const [isDislikeClicked, setIsDislikeClicked] = useState(false); const [activeTooltip, setActiveTooltip] = useState(null); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const handleEditClick = () => { setIsEditClicked(false); handleUpdatedQuestionSubmission?.(editInputBox, true, questionNumber); }; let bubble; if (type === 'QUESTION') { bubble = (
setIsQuestionHovered(true)} onMouseLeave={() => setIsQuestionHovered(false)} >
{!isEditClicked && (
{message}
)} {isEditClicked && ( setEditInputBox(e.target.value)} value={editInputBox} className="w-[85%] ml-2 mr-2 rounded-[28px] py-[12px] dark:border-[0.5px] dark:border-white dark:bg-raisin-black dark:text-white px-[18px] border-[1.5px] border-black" /> )}
Edit { setIsEditClicked(true); setEditInputBox(message); }} />
{isEditClicked && (
)}
); } 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 && ( <>
{ if(feedback===undefined){ console.log("liked") handleFeedback?.('LIKE'); setIsLikeClicked(true); setIsDislikeClicked(false); } }} onMouseEnter={() => setIsLikeHovered(true)} onMouseLeave={() => setIsLikeHovered(false)} >
{ if(feedback===undefined){ 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;