Files
metona-ai-desktop/src/components/chat/MessageItem.tsx
T

56 lines
2.0 KiB
TypeScript

/**
* MessageItem — 单条消息路由组件
*
* 根据消息 role 路由到对应的子组件渲染。
*
* F1 性能优化: 使用 React.memo 包裹,避免流式 delta 触发历史消息重渲染。
* 浅比较策略:message 引用变化(store 仅替换最后一条)或 isStreaming 变化时才重渲染。
* 历史消息的 message 引用在 delta 流中保持不变,因此可跳过 re-render。
*/
import { memo } from 'react';
import type { ChatMessage } from '@renderer/stores/agent-store';
import { UserMessage } from './UserMessage';
import { AssistantMessage } from './AssistantMessage';
import { SystemMessage } from './SystemMessage';
interface MessageItemProps {
message: ChatMessage;
isLast?: boolean;
isStreaming?: boolean;
}
function MessageItemImpl({ message, isLast, isStreaming }: MessageItemProps): React.JSX.Element | null {
switch (message.role) {
case 'user':
return <UserMessage message={message} />;
case 'assistant':
return (
<AssistantMessage
message={message}
isStreaming={isLast && isStreaming}
/>
);
case 'tool':
// #38 修复: 与 AgentStore.setCurrentSession 的过滤逻辑保持一致,
// 不在 UI 中渲染独立的 tool 消息卡片。
// tool 结果已包含在 assistant 消息的 toolCalls 中(见 AssistantMessage),
// 独立的 tool 消息仅用于 LLM API 上下文,不需要在前端显示为独立卡片。
return null;
case 'system':
default:
return <SystemMessage message={message} />;
}
}
/**
* F1: memo 默认浅比较 props。
* - message 是对象引用,store 更新最后一条时只替换最后一条,历史消息引用不变 → 跳过 re-render
* - isStreaming 是 boolean,流式过程中值不变(持续 true),流结束才变化一次 → 历史消息仅重渲染一次
* - isLast 是 boolean,仅在最后一条切换时变化
*/
export const MessageItem = memo(MessageItemImpl);