import React from 'react'; import type { Message, Part } from '@opencode-ai/sdk/v2'; import ChatMessage from './ChatMessage'; import { PermissionCard } from './PermissionCard'; import { QuestionCard } from './QuestionCard'; import type { PermissionRequest } from '@/types/permission'; import type { QuestionRequest } from '@/types/question'; import type { AnimationHandlers, ContentChangeReason } from '@/hooks/useChatScrollManager'; import { filterSyntheticParts } from '@/lib/messages/synthetic'; import { useTurnGrouping } from './hooks/useTurnGrouping'; interface MessageListProps { messages: { info: Message; parts: Part[] }[]; permissions: PermissionRequest[]; questions: QuestionRequest[]; onMessageContentChange: (reason?: ContentChangeReason) => void; getAnimationHandlers: (messageId: string) => AnimationHandlers; hasMoreAbove: boolean; isLoadingOlder: boolean; onLoadOlder: () => void; scrollToBottom?: (options?: { instant?: boolean; force?: boolean }) => void; pendingAnchorId?: string | null; } const MessageList: React.FC = ({ messages, permissions, questions, onMessageContentChange, getAnimationHandlers, hasMoreAbove, isLoadingOlder, onLoadOlder, scrollToBottom, pendingAnchorId, }) => { React.useEffect(() => { if (permissions.length === 0 && questions.length === 0) { return; } onMessageContentChange('permission'); }, [permissions, questions, onMessageContentChange]); const displayMessages = React.useMemo(() => { const seenIds = new Set(); return messages .filter((message) => { const messageId = message.info?.id; if (typeof messageId === 'string') { if (seenIds.has(messageId)) { return false; } seenIds.add(messageId); } return true; }) .map((message) => ({ ...message, parts: filterSyntheticParts(message.parts), })); }, [messages]); const { getContextForMessage } = useTurnGrouping(displayMessages); return (
{hasMoreAbove && (
{isLoadingOlder ? ( Loading… ) : ( )}
)}
{displayMessages.map((message, index) => ( 0 ? displayMessages[index - 1] : undefined} nextMessage={index < displayMessages.length - 1 ? displayMessages[index + 1] : undefined} onContentChange={onMessageContentChange} animationHandlers={getAnimationHandlers(message.info.id)} scrollToBottom={scrollToBottom} isPendingAnchor={pendingAnchorId === message.info.id} turnGroupingContext={getContextForMessage(message.info.id)} /> ))}
{(questions.length > 0 || permissions.length > 0) && (
{questions.map((question) => ( ))} {permissions.map((permission) => ( ))}
)}
); }; export default React.memo(MessageList);