({
chatId,
message,
vote,
isLoading,
setMessages,
regenerate,
isReadonly,
requiresScrollPadding,
}: {
chatId: string;
message: ChatMessage;
vote: Vote | undefined;
isLoading: boolean;
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
isReadonly: boolean;
requiresScrollPadding: boolean;
})
| 24 | // The AI SDK provides proper discriminated unions for tool calls |
| 25 | |
| 26 | const PurePreviewMessage = ({ |
| 27 | chatId, |
| 28 | message, |
| 29 | vote, |
| 30 | isLoading, |
| 31 | setMessages, |
| 32 | regenerate, |
| 33 | isReadonly, |
| 34 | requiresScrollPadding, |
| 35 | }: { |
| 36 | chatId: string; |
| 37 | message: ChatMessage; |
| 38 | vote: Vote | undefined; |
| 39 | isLoading: boolean; |
| 40 | setMessages: UseChatHelpers<ChatMessage>['setMessages']; |
| 41 | regenerate: UseChatHelpers<ChatMessage>['regenerate']; |
| 42 | isReadonly: boolean; |
| 43 | requiresScrollPadding: boolean; |
| 44 | }) => { |
| 45 | const [mode, setMode] = useState<'view' | 'edit'>('view'); |
| 46 | |
| 47 | const attachmentsFromMessage = message.parts.filter( |
| 48 | (part) => part.type === 'file', |
| 49 | ); |
| 50 | |
| 51 | useDataStream(); |
| 52 | |
| 53 | return ( |
| 54 | <AnimatePresence> |
| 55 | <motion.div |
| 56 | data-testid={`message-${message.role}`} |
| 57 | className="w-full mx-auto max-w-3xl px-4 group/message" |
| 58 | initial={{ y: 5, opacity: 0 }} |
| 59 | animate={{ y: 0, opacity: 1 }} |
| 60 | data-role={message.role} |
| 61 | > |
| 62 | <div |
| 63 | className={cn( |
| 64 | 'flex gap-4 w-full group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl', |
| 65 | { |
| 66 | 'w-full': mode === 'edit', |
| 67 | 'group-data-[role=user]/message:w-fit': mode !== 'edit', |
| 68 | }, |
| 69 | )} |
| 70 | > |
| 71 | {message.role === 'assistant' && ( |
| 72 | <div className="size-8 flex items-center rounded-full justify-center ring-1 shrink-0 ring-border bg-background"> |
| 73 | <div className="translate-y-px"> |
| 74 | <SparklesIcon size={14} /> |
| 75 | </div> |
| 76 | </div> |
| 77 | )} |
| 78 | |
| 79 | <div |
| 80 | className={cn('flex flex-col gap-4 w-full', { |
| 81 | 'min-h-96': message.role === 'assistant' && requiresScrollPadding, |
| 82 | })} |
| 83 | > |
nothing calls this directly
no test coverage detected
searching dependent graphs…