File size: 3,267 Bytes
59d634d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
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
}
|