| import { MarkdownRenderer } from '../markdown/MarkdownRenderer' |
| import { MessageActionBar, type MessageBranchAction } from './MessageActionBar' |
| import { InlineImageGallery } from './InlineImageGallery' |
|
|
| type Props = { |
| content: string |
| isStreaming?: boolean |
| branchAction?: MessageBranchAction |
| } |
|
|
| export function AssistantMessage({ content, isStreaming, branchAction }: Props) { |
| if (!content.trim()) return null |
|
|
| const documentLayout = shouldUseDocumentLayout(content) |
|
|
| return ( |
| <div className="group mb-5 flex justify-start"> |
| <div |
| data-message-shell="assistant" |
| data-layout={documentLayout ? 'document' : 'bubble'} |
| className={`flex min-w-0 flex-col items-start gap-2 ${ |
| documentLayout |
| ? 'w-full max-w-full' |
| : 'w-full max-w-[88%] sm:max-w-[80%] lg:max-w-[72%]' |
| }`} |
| > |
| <div className={`rounded-[20px] rounded-tl-[8px] border border-[var(--color-border)]/60 bg-[var(--color-surface)] px-4 py-3 text-sm text-[var(--color-text-primary)] shadow-sm ${ |
| documentLayout ? 'w-full' : 'max-w-full' |
| }`}> |
| <MarkdownRenderer content={content} variant={documentLayout ? 'document' : 'default'} /> |
| {!isStreaming && <InlineImageGallery text={content} />} |
| {isStreaming && ( |
| <span className="ml-0.5 inline-block h-4 w-0.5 animate-shimmer bg-[var(--color-brand)] align-text-bottom" /> |
| )} |
| </div> |
| |
| <MessageActionBar |
| copyText={isStreaming ? undefined : content} |
| copyLabel="Copy reply" |
| branchAction={branchAction} |
| align="start" |
| /> |
| </div> |
| </div> |
| ) |
| } |
|
|
| function shouldUseDocumentLayout(content: string) { |
| const normalized = content.trim() |
| if (!normalized) return false |
|
|
| if (/```/.test(normalized)) return true |
| if (/^\s{0,3}(#{1,6}\s|[-*+]\s|\d+\.\s|>\s|\|.+\|)/m.test(normalized)) return true |
|
|
| const paragraphs = normalized |
| .split(/\n\s*\n/) |
| .map((chunk) => chunk.trim()) |
| .filter(Boolean) |
|
|
| return paragraphs.length >= 2 || normalized.split('\n').filter((line) => line.trim()).length >= 8 |
| } |
|
|