mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
When the virtualization window slid during scroll, every visible row reconciled from scratch because the heavy chat-row subtree had no memo barriers, and because renderTranscriptItem rebuilt fresh branchAction / toolResult object literals each render that broke MessageBlock's existing memo. Wraps AssistantMessage, UserMessage, ToolCallBlock, ToolCallGroup, ToolResultBlock, and MarkdownRenderer in React.memo, and hoists the per-message branchAction and toolResult lookups into useMemo'd Maps keyed by message id. Window slides now keep referentially-stable props for unchanged rows, so reconciliation skips them and only newly entering rows pay full render cost. Tested: 722/722 desktop vitest suites pass Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { memo } from 'react'
|
|
import type { UIAttachment } from '../../types/chat'
|
|
import { AttachmentGallery } from './AttachmentGallery'
|
|
import { MessageActionBar, type MessageBranchAction } from './MessageActionBar'
|
|
|
|
type Props = {
|
|
content: string
|
|
attachments?: UIAttachment[]
|
|
branchAction?: MessageBranchAction
|
|
}
|
|
|
|
export const UserMessage = memo(function UserMessage({ content, attachments, branchAction }: Props) {
|
|
const hasText = content.trim().length > 0
|
|
|
|
return (
|
|
<div className="group mb-5 flex justify-end">
|
|
<div
|
|
data-message-shell="user"
|
|
className="flex min-w-0 w-full max-w-[82%] flex-col items-end gap-2 sm:max-w-[78%] lg:max-w-[72%]"
|
|
>
|
|
{attachments && attachments.length > 0 && (
|
|
<AttachmentGallery attachments={attachments} variant="message" />
|
|
)}
|
|
|
|
{hasText && (
|
|
<div
|
|
className="min-w-0 max-w-full bg-[var(--color-surface-user-msg)] px-4 py-3 text-sm leading-relaxed text-[var(--color-text-primary)] whitespace-pre-wrap break-words"
|
|
style={{
|
|
borderRadius: '18px 4px 18px 18px',
|
|
overflowWrap: 'anywhere',
|
|
wordBreak: 'break-word',
|
|
}}
|
|
>
|
|
{content}
|
|
</div>
|
|
)}
|
|
|
|
{hasText && (
|
|
<MessageActionBar
|
|
copyText={content}
|
|
copyLabel="Copy prompt"
|
|
branchAction={branchAction}
|
|
align="end"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|