AI Message Bubble
Bulle de discussion pour les messages assistant, utilisateur, outil et systeme utilisant les surfaces du systeme de design actuel. Installez AI Message Bubble depuis le registre VLLNT UI avec la CLI shadcn.
Quand utiliser ceci dans une application IA
Use AI Message Bubble to render each turn in a conversation. Role variants (user, assistant, tool, system) keep an LLM transcript readable and consistent with the rest of your design system.
Parcourir tous les composants pour agents IABulle de discussion pour les messages assistant, utilisateur, outil et systeme utilisant les surfaces du systeme de design actuel. Fait partie de la famille ai dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.
Installation
Ajoutez AI Message Bubble a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/ai-message-bubble.jsonSource
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";
import { Avatar, AvatarFallback } from "../avatar/avatar";
import { Badge } from "../badge/badge";
const bubbleVariants = cva(
"rounded-2xl border px-4 py-3 shadow-sm transition-colors",
{
defaultVariants: {
messageRole: "assistant",
},
variants: {
messageRole: {
assistant: "border-border bg-card text-card-foreground",
system: "border-border/80 bg-muted/60 text-foreground",
tool: "border-border bg-muted/40 text-foreground",
user: "border-primary/20 bg-primary/10 text-foreground",
},
},
},
);
function AIMessageMeta({
author,
isUser,
status,
timestamp,
}: {
author?: string;
isUser: boolean;
status?: string;
timestamp?: string;
}) {
return (
<div
className={cn(
"flex flex-wrap items-center gap-2 text-xs text-muted-foreground",
isUser ? "justify-end" : "justify-start",
)}
>
{author ? (
<span className="font-medium text-foreground">{author}</span>
) : null}
{timestamp ? <span>{timestamp}</span> : null}
{status ? (
<Badge
className="rounded-full px-2 py-0 text-[10px]"
variant="secondary"
>
{status}
</Badge>
) : null}
</div>
);
}
export type AIMessageBubbleProps = React.ComponentPropsWithoutRef<"div"> &
VariantProps<typeof bubbleVariants> & {
/** Optional short label describing the speaker. */
author?: string;
/** Bubble body content. */
children: React.ReactNode;
/** Optional status badge for the message. */
status?: string;
/** Optional timestamp or relative time label. */
timestamp?: string;
};
const AIMessageBubble = ({
author,
children,
className,
messageRole = "assistant",
ref,
status,
timestamp,
...props
}: AIMessageBubbleProps & { ref?: React.Ref<HTMLDivElement> }) => {
const resolvedMessageRole = messageRole ?? "assistant";
const isUser = resolvedMessageRole === "user";
const fallbackLabel = (author ?? resolvedMessageRole).charAt(0).toUpperCase();
return (
<div
className={cn("flex w-full", isUser ? "justify-end" : "justify-start")}
>
<div
className={cn(
"flex w-full max-w-3xl gap-3",
isUser ? "flex-row-reverse text-right" : "flex-row",
)}
>
<Avatar className="mt-0.5 size-8 border border-border/70">
<AvatarFallback className="bg-muted text-[11px] font-medium uppercase text-muted-foreground">
{fallbackLabel}
</AvatarFallback>
</Avatar>
<div
className={cn(
"min-w-0 space-y-2",
isUser ? "items-end" : "items-start",
)}
>
<AIMessageMeta
author={author}
isUser={isUser}
status={status}
timestamp={timestamp}
/>
<div
className={cn(
bubbleVariants({ messageRole: resolvedMessageRole }),
className,
)}
ref={ref}
{...props}
>
<div className="text-sm leading-6 whitespace-pre-wrap">
{children}
</div>
</div>
</div>
</div>
</div>
);
};
AIMessageBubble.displayName = "AIMessageBubble";
export { AIMessageBubble };
Stories
Explorez chaque variante et etat dans le Storybook interactif :
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.