"use client"; /** * MessageBubble 消息气泡(容器) * * * * 组成(从左到右): * - AI:[Avatar] [Content] [占位 spacer] * - 用户:[占位 spacer] [Content] [Avatar] */ import { useUserSelector } from "@/stores/user/user-context"; import type { ChatAction, CommercialAction, PaymentGuidance } from "@/data/schemas/chat"; import { MessageAvatar } from "./message-avatar"; import { MessageContent } from "./message-content"; import styles from "./chat-area.module.css"; export interface MessageBubbleProps { characterId: string; displayMessageId: string; remoteMessageId?: string; content: string; imageUrl?: string | null; imagePaywalled?: boolean; audioUrl?: string | null; isFromAI: boolean; locked?: boolean | null; lockReason?: string | null; lockedPrivate?: boolean | null; privateMessageHint?: string | null; commercialAction?: CommercialAction | null; chatAction?: ChatAction | null; paymentGuidance?: PaymentGuidance | null; isUnlockingMessage?: boolean; onUnlockPrivateMessage?: ChatMessageAction; onUnlockVoiceMessage?: ChatMessageAction; onUnlockImageMessage?: ChatMessageAction; onOpenImage?: (displayMessageId: string) => void; onUserAvatarClick?: () => void; onCharacterAvatarClick?: () => void; onCommercialAction?: (action: CommercialAction) => void; onCommercialActionDismiss?: (action: CommercialAction) => void; onChatActionViewed?: (action: ChatAction) => void; onChatAction?: (action: ChatAction) => void | Promise; onChatActionDismiss?: (action: ChatAction) => void; } type ChatMessageAction = ( displayMessageId: string, remoteMessageId?: string, ) => void; export function MessageBubble({ characterId, displayMessageId, remoteMessageId, content, imageUrl, imagePaywalled, audioUrl, isFromAI, locked, lockReason, lockedPrivate, privateMessageHint, commercialAction, chatAction, paymentGuidance, isUnlockingMessage, onUnlockPrivateMessage, onUnlockVoiceMessage, onUnlockImageMessage, onOpenImage, onUserAvatarClick, onCharacterAvatarClick, onCommercialAction, onCommercialActionDismiss, onChatActionViewed, onChatAction, onChatActionDismiss, }: MessageBubbleProps) { const avatarUrl = useUserSelector((state) => state.context.avatarUrl); if (isFromAI) { return (