import { cva, VariantProps } from "class-variance-authority"; import React from "react"; import { Avatar, Button, ConversationMessageContent, IconButton, } from "@sparkle/components"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@sparkle/components/Dropdown"; import { MoreIcon } from "@sparkle/icons/app"; import { cn } from "@sparkle/lib/utils"; export const ConversationContainer = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ children, className, ...props }, ref) => { return (
{children}
); }); ConversationContainer.displayName = "ConversationContainer"; interface ConversationMessageProps extends React.HTMLAttributes, VariantProps { actions?: ConversationMessageAction[]; avatarBusy?: boolean; buttons?: React.ReactElement[]; children?: React.ReactNode; citations?: React.ReactElement[]; isDisabled?: boolean; name?: string; timestamp?: string; completionStatus?: React.ReactNode; pictureUrl?: string | React.ReactNode | null; renderName?: (name: string | null) => React.ReactNode; infoChip?: React.ReactNode; type: ConversationMessageType; } type ConversationMessageType = "user" | "agent"; interface ConversationMessageAction { icon: React.ComponentType | React.ReactNode; label: string; onClick: () => void; } const messageVariants = cva("s-flex s-w-full s-flex-col s-rounded-2xl", { variants: { type: { user: "s-bg-muted-background dark:s-bg-muted-background-night s-px-5 s-py-4 s-gap-2", agent: "s-w-full s-gap-3", }, }, defaultVariants: { type: "agent", }, }); const buttonsVariants = cva("s-flex s-justify-start s-gap-2 s-pt-2", { variants: { type: { user: "s-justify-end", agent: "s-justify-start", }, }, defaultVariants: { type: "agent", }, }); /** * Parent component for both UserMessage and AgentMessage, to ensure avatar, * side buttons and spacing are consistent between the two */ export const ConversationMessage = React.forwardRef< HTMLDivElement, ConversationMessageProps >( ( { avatarBusy = false, buttons, children, citations, isDisabled = false, name, timestamp, completionStatus, pictureUrl, renderName = (name) => {name}, infoChip, type, actions, className, ...props }, ref ) => { return (
{children}
{buttons && (
{buttons}
)}
); } ); ConversationMessage.displayName = "ConversationMessage"; interface ConversationMessageHeaderProps extends React.HTMLAttributes { actions?: ConversationMessageAction[]; avatarUrl?: string | React.ReactNode; isBusy?: boolean; isDisabled?: boolean; name?: string; timestamp?: string; completionStatus?: React.ReactNode; infoChip?: React.ReactNode; renderName: (name: string | null) => React.ReactNode; } const ConversationMessageHeader = React.forwardRef< HTMLDivElement, ConversationMessageHeaderProps >( ( { avatarUrl, isBusy, isDisabled, name = "", timestamp, infoChip, completionStatus, renderName, actions, className, ...props }, ref ) => { return (
{renderName(name)} {timestamp} {infoChip && infoChip}
{completionStatus ?? null} {actions && actions.length > 0 && ( {actions.map((action, index) => ( ))} )}
); } ); ConversationMessageHeader.displayName = "ConversationMessageHeader";