import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' import supersub from 'remark-supersub' import remarkBreaks from 'remark-breaks' import { cn } from '@/lib/utils' import { CodeBlock } from '@/components/ui/codeblock' import { MemoizedReactMarkdown } from '@/components/markdown' import { LearnMore } from './learn-more' import { ChatMessageModel } from '@/lib/bots/bing/types' import { useEffect } from 'react' import { TurnCounter } from './turn-counter' export interface ChatMessageProps { message: ChatMessageModel } export function ChatMessage({ message, ...props }: ChatMessageProps) { useEffect(() => { if (document.body.scrollHeight - window.innerHeight - window.scrollY - 200 < 0) { window.scrollBy(0, 200) } }, [message.text]) return message.text ? ( <div className={cn('text-message', message.author)} {...props} > <div className="text-message-content"> <MemoizedReactMarkdown linkTarget="_blank" className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0" remarkPlugins={[remarkGfm, remarkMath, supersub, remarkBreaks]} components={{ img(obj) { try { const uri = new URL(obj.src!) const w = uri.searchParams.get('w') const h = uri.searchParams.get('h') if (w && h) { uri.searchParams.delete('w') uri.searchParams.delete('h') return <a style={{ float: 'left', maxWidth: '50%' }} href={uri.toString()} target="_blank" rel="noopener noreferrer"><img src={obj.src} alt={obj.alt} width={w!} height={h!}/></a> } } catch (e) { } return <img src={obj.src} alt={obj.alt} title={obj.title} /> }, p({ children }) { return <p className="mb-2">{children}</p> }, code({ node, inline, className, children, ...props }) { if (children.length) { if (children[0] == '▍') { return ( <span className="mt-1 animate-pulse cursor-default">▍</span> ) } children[0] = (children[0] as string).replace('`▍`', '▍') } const match = /language-(\w+)/.exec(className || '') if (inline) { return ( <code className={className} {...props}> {children} </code> ) } return ( <CodeBlock key={Math.random()} language={(match && match[1]) || ''} value={String(children).replace(/\n$/, '')} {...props} /> ) } }} > {message.text} </MemoizedReactMarkdown> </div> <div className="text-message-footer"> {message.author === 'bot' && <LearnMore sourceAttributions={message.sourceAttributions} />} {message.author === 'bot' && <TurnCounter throttling={message.throttling} />} </div> </div> ) : null }