"use client"; /** * ChatArea 消息列表区 * * 原始 Dart: lib/ui/chat/widgets/chat_area.dart(153 行) * * 组成(自上而下): * - AI 披露横幅 * - 日期分隔条 * - 消息气泡(AI 左 / 用户右) * - AI 正在回复时的加载动画气泡 * * 滚动行为: * - 新消息到达时自动滚到底部 * - 用户向上滚动时不强制拉回(保持阅读上下文) */ import { useEffect, useRef } from "react"; import type { UiMessage } from "@/models/chat/ui-message"; import { AiDisclosureBanner } from "./ai-disclosure-banner"; import { DateHeader } from "./date-header"; import { LottieMessageBubble } from "./lottie-message-bubble"; import { MessageBubble } from "./message-bubble"; import styles from "./chat-area.module.css"; export interface ChatAreaProps { messages: readonly UiMessage[]; isReplyingAI: boolean; isGuest: boolean; } export function ChatArea({ messages, isReplyingAI }: ChatAreaProps) { const scrollRef = useRef(null); const prevLengthRef = useRef(messages.length); // 新消息 → 滚到底部 useEffect(() => { if (messages.length !== prevLengthRef.current) { scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth", }); prevLengthRef.current = messages.length; } }, [messages.length]); return (
{renderMessagesWithDateHeaders(messages)} {isReplyingAI && }
); } /** 渲染消息列表(按日期分组插入分隔条) */ function renderMessagesWithDateHeaders(messages: readonly UiMessage[]) { const items: Array<{ type: "date"; date: string; key: string } | { type: "msg"; message: UiMessage; key: string }> = []; messages.forEach((m, i) => { if (i === 0 || m.date !== messages[i - 1].date) { items.push({ type: "date", date: m.date, key: `d-${i}-${m.date}` }); } items.push({ type: "msg", message: m, key: `m-${i}` }); }); return items.map((item) => item.type === "date" ? ( ) : ( ), ); }