// Adapted from agentui (https://github.com/ashish200729/agentui), components/agents/message.tsx + message-context.tsx // Copyright (c) 2026 Saurabh Chauhan. MIT License, see ./LICENSE-agentui.md // 改造:motion.article 的弹簧入场换成 tw-animate 的 animate-in(duration-base / ease-pop,motion-reduce 关闭); // 头像底色、字号(text-label / text-meta)、圆角换语义 token;新增 from="system" 行(居中提示,用于压缩/中止等系统消息); // MessageTyping 三点动画用 animate-pulse + 具名时长的 animation-delay。 import { type ComponentPropsWithRef, createContext, useContext } from "react"; import { cn } from "~/lib/utils.ts"; export type MessageFrom = "user" | "assistant" | "system"; export type MessageSide = "start" | "end"; export const MessageSideContext = createContext(undefined); const MessageContext = createContext<{ from: MessageFrom }>({ from: "assistant" }); export interface MessageProps extends ComponentPropsWithRef<"article"> { from: MessageFrom; /** 挂载时播一次入场(新到达的消息用;回放历史时不播) */ animateIn?: boolean; } const FROM_LABEL: Record = { user: "用户消息", assistant: "智能体消息", system: "系统消息" }; export function Message({ from, animateIn = false, className, children, ...props }: MessageProps) { return (
{children}
); } export function MessageGroup({ spacing = "default", className, ...props }: ComponentPropsWithRef<"div"> & { spacing?: "compact" | "default" }) { return (
); } export function MessageAvatar({ placeholder = false, className, ...props }: ComponentPropsWithRef<"div"> & { placeholder?: boolean }) { return (
); } export function MessageContent({ className, ...props }: ComponentPropsWithRef<"div">) { const { from } = useContext(MessageContext); return (
); } export function MessageHeader({ className, ...props }: ComponentPropsWithRef<"div">) { const { from } = useContext(MessageContext); return (
); } export function MessageFooter({ className, ...props }: ComponentPropsWithRef<"div">) { const { from } = useContext(MessageContext); return (
); } /** 居中的系统提示条(上下文压缩、续跑等) */ export function MessageMarker({ className, ...props }: ComponentPropsWithRef<"div">) { return (
); } const DOT_DELAY = ["", "[animation-delay:var(--duration-micro)]", "[animation-delay:var(--duration-layout)]"]; export function MessageTyping({ label = "正在回复", className, ...props }: ComponentPropsWithRef<"span"> & { label?: string }) { return ( {label} {DOT_DELAY.map((d, i) => ( ))} ); }