UNPKG

@aichatkit/ui

Version:

Composable chat UI components for AI powered apps

952 lines (941 loc) 35 kB
// src/components/button/button.tsx import { Slot } from "@radix-ui/react-slot"; import { cva } from "class-variance-authority"; import { forwardRef } from "react"; // src/utils/cn.ts import { clsx } from "clsx"; import { twMerge } from "tailwind-merge"; function cn(...inputs) { return twMerge(clsx(inputs)); } // src/components/button/button.tsx import { jsx } from "react/jsx-runtime"; var buttonVariants = cva( "inline-flex items-center whitespace-nowrap rounded-lg font-medium text-sm transition-colors disabled:pointer-events-none disabled:[&_svg]:fill-icon-disabled", { variants: { variant: { primary: "justify-center bg-button-surface-primary text-text-button-primary ring-offset-primary-200 hover:bg-primary-400 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary-200 focus-visible:ring-offset-0 active:bg-primary-600 disabled:bg-button-surface-disabled disabled:text-text-disabled dark:active:bg-primary-400 dark:focus-visible:ring-primary-700 dark:hover:bg-primary-600 [&_svg]:fill-icon-button-primary", secondary: "[&_svg]:icon-fill-text-button-secondary justify-center text-text-button-secondary outline outline-1 outline-button-border-primary hover:bg-primary-25 focus-visible:ring-2 focus-visible:ring-primary-200 focus-visible:ring-primary-200 active:bg-primary-50 disabled:text-text-disabled disabled:outline-border-disabled dark:active:bg-primary-850 dark:focus-visible:ring-primary-700 dark:hover:bg-primary-900", ghost: "justify-center text-text-action hover:bg-primary-25 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary-200 focus-visible:ring-offset-0 active:bg-primary-50 disabled:text-text-disabled dark:active:bg-primary-850 dark:active:text-primary-50 dark:focus-visible:ring-primary-700 dark:hover:bg-primary-900 dark:hover:text-primary-50", warning: "justify-center bg-warning-400 text-neutral-0 hover:bg-warning focus-visible:ring-2 focus-visible:ring-warning-300 active:bg-warning-400 active:outline-error-600 disabled:cursor-not-allowed disabled:text-text-disabled dark:active:bg-error-600 dark:active:outline-error-400 dark:focus-visible:ring-warning-700 dark:hover:bg-warning-700", warning_outline: "justify-center text-warning outline outline-1 outline-button-border-warning hover:bg-warning-100 focus-visible:ring-2 focus-visible:ring-warning-300 active:bg-warning-400 active:outline-warning-600 disabled:cursor-not-allowed disabled:text-text-disabled dark:active:bg-warning-600 dark:active:outline-warning-400 dark:focus-visible:ring-warning-700 dark:hover:bg-warning-700", destructive: "justify-center bg-button-border-error text-neutral-0 hover:bg-error-400 focus-visible:ring-2 focus-visible:ring-error-300 active:bg-error-400 active:outline-error-600 disabled:cursor-not-allowed disabled:text-text-disabled dark:active:bg-error-600 dark:active:outline-error-400 dark:focus-visible:ring-error-700 dark:hover:bg-error-700", destructive_outline: "justify-center text-button-text-error outline outline-1 outline-button-border-error hover:bg-error-100 focus-visible:ring-2 focus-visible:ring-error-300 active:bg-error-400 active:outline-error-600 disabled:cursor-not-allowed disabled:text-text-disabled dark:active:bg-error-600 dark:active:outline-error-400 dark:focus-visible:ring-error-700 dark:hover:bg-error-700", input: "flex h-8 justify-between rounded-lg border border-form-border-primary bg-form-surface-primary p-2 text-left text-sm ring-none ring-inset transition-colors file:font-medium file:text-sm placeholder:text-text-secondary hover:bg-neutral-25 focus-visible:border-border-action focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:border-form-border-disabled disabled:bg-form-surface-disabled disabled:text-text-disabled dark:focus-visible:ring-primary-700 dark:hover:bg-form-surface-disabled dark:hover:bg-neutral-800 focus-visible:[&_svg]:fill-icon-tertiary disabled:[&_svg]:fill-icon-disabled" }, size: { default: "h-8 px-4", icon: "h-8 px-1.5", sm: "h-6 px-3 text-xs", lg: "h-10 px-5" } }, defaultVariants: { variant: "primary", size: "default" } } ); var Button = forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props } ); } ); Button.displayName = "Button"; // src/components/input/input.tsx import { forwardRef as forwardRef2 } from "react"; import { jsx as jsx2, jsxs } from "react/jsx-runtime"; var Input = forwardRef2( ({ className, type, ...props }, ref) => { return /* @__PURE__ */ jsx2( "input", { type, className: cn( "flex h-8 w-full rounded-lg border border-form-border-primary bg-form-surface-primary p-2 text-sm ring-none ring-inset transition-colors file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-text-secondary hover:bg-neutral-25 focus-visible:border-border-action focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:border-form-border-disabled disabled:bg-form-surface-disabled disabled:text-text-disabled dark:focus-visible:ring-primary-700 dark:hover:bg-form-surface-disabled dark:hover:bg-neutral-800 focus-visible:[&_svg]:fill-icon-tertiary disabled:[&_svg]:fill-icon-disabled", className ), ref, ...props } ); } ); Input.displayName = "Input"; var DefaultHypermodeInputArea = ({ input, setInput, loading, onSubmit, inputPlaceholder, sendButtonIcon, inputAreaClassName }) => /* @__PURE__ */ jsx2( "form", { onSubmit, className: cn("border-hypermode-border border-t p-4", inputAreaClassName), children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [ /* @__PURE__ */ jsx2( Input, { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: inputPlaceholder, className: "w-full rounded-md border border-hypermode-border bg-hypermode-input px-4 py-2 pr-10 text-white placeholder-neutral-500 focus:border-hypermode-accent focus:ring-hypermode-accent" } ), /* @__PURE__ */ jsx2( Button, { type: "submit", disabled: loading || !input.trim(), className: "-translate-y-1/2 absolute top-1/2 right-2 transform rounded-md p-1.5 text-hypermode-accent hover:bg-hypermode-hover disabled:cursor-not-allowed disabled:opacity-50", children: typeof sendButtonIcon === "string" ? sendButtonIcon : sendButtonIcon } ) ] }) } ); var DefaultStandardInputArea = ({ input, setInput, loading, onSubmit, inputPlaceholder, sendButtonIcon, inputAreaClassName }) => /* @__PURE__ */ jsx2( "form", { onSubmit, className: cn( "border-border-primary border-t bg-white p-4", inputAreaClassName ), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx2( Input, { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: inputPlaceholder, className: "flex-1" } ), /* @__PURE__ */ jsx2(Button, { type: "submit", disabled: loading, children: typeof sendButtonIcon === "string" ? sendButtonIcon : sendButtonIcon }) ] }) } ); // src/components/chat/chat-bubble.tsx import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime"; function ChatBubble({ content, role, timestamp, avatar, className, bubbleClassName, hypermodeStyle = false, ...props }) { const defaultAvatar = role === "assistant" ? /* @__PURE__ */ jsx3("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-hypermode-accent text-sm text-white", children: "A" }) : /* @__PURE__ */ jsx3("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-hypermode-accent-light text-sm text-white", children: "U" }); const safeTimestamp = timestamp ? timestamp instanceof Date ? timestamp.toISOString() : String(timestamp) : void 0; if (hypermodeStyle) { return /* @__PURE__ */ jsxs2("div", { className: cn("flex items-start gap-3", className), ...props, children: [ avatar || defaultAvatar, /* @__PURE__ */ jsxs2("div", { className: "flex max-w-[85%] flex-col", children: [ /* @__PURE__ */ jsx3( "div", { className: cn( "whitespace-pre-wrap break-words rounded-lg px-4 py-2", role === "user" ? "bg-hypermode-accent text-white" : "bg-hypermode-card text-white", bubbleClassName ), children: content } ), safeTimestamp && /* @__PURE__ */ jsx3("span", { className: "mt-1 ml-1 text-neutral-500 text-xs", children: safeTimestamp }) ] }) ] }); } return /* @__PURE__ */ jsx3( "div", { className: cn( "flex", role === "user" ? "justify-end" : "justify-start", className ), ...props, children: /* @__PURE__ */ jsxs2( "div", { className: cn( "relative max-w-[85%] px-4 py-2", role === "user" ? "rounded-[1.15rem] rounded-br-none bg-primary-500 text-text-inverse" : "rounded-[1.15rem] rounded-bl-none bg-neutral-750 text-text-primary", "whitespace-pre-wrap break-words", bubbleClassName ), children: [ content, safeTimestamp && /* @__PURE__ */ jsx3("span", { className: "mt-1 block text-xs opacity-70", children: safeTimestamp }) ] } ) } ); } ChatBubble.displayName = "ChatBubble"; // src/components/chat/chat-interface.tsx import { useEffect, useRef, useState } from "react"; // src/components/chat/loading-indicator.tsx import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime"; function LoadingDot({ delay, className, ...props }) { return /* @__PURE__ */ jsx4( "div", { className: cn("h-2 w-2 rounded-full bg-neutral-600", className), style: { animation: `loadingDotBounce 0.6s infinite ease-in-out ${delay}s` }, ...props } ); } function LoadingIndicator({ dots = 3, className, dotClassName, ...props }) { return /* @__PURE__ */ jsx4("div", { className: cn("flex space-x-1", className), ...props, children: Array.from({ length: dots }).map((_, index) => /* @__PURE__ */ jsx4(LoadingDot, { delay: index * 0.2, className: dotClassName }, index)) }); } function LoadingChatBubble({ className, bubbleClassName, indicatorClassName, hypermodeStyle = false, avatar, ...props }) { const defaultAvatar = /* @__PURE__ */ jsx4("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-hypermode-accent text-sm text-white", children: "A" }); if (hypermodeStyle) { return /* @__PURE__ */ jsxs3( "div", { className: cn("flex items-start gap-3", className), style: { animation: "fadeIn 0.3s ease-out" }, ...props, children: [ avatar || defaultAvatar, /* @__PURE__ */ jsx4( "div", { className: cn( "rounded-lg bg-hypermode-card px-4 py-3", bubbleClassName ), children: /* @__PURE__ */ jsx4( LoadingIndicator, { className: indicatorClassName, dotClassName: "bg-neutral-500" } ) } ) ] } ); } return /* @__PURE__ */ jsx4( "div", { className: cn("flex justify-start", className), style: { animation: "fadeIn 0.3s ease-out" }, ...props, children: /* @__PURE__ */ jsx4( "div", { className: cn( "relative max-w-[85%] rounded-[1.15rem] rounded-bl-none bg-neutral-100 px-4 py-3 text-text-primary", bubbleClassName ), children: /* @__PURE__ */ jsx4(LoadingIndicator, { className: indicatorClassName }) } ) } ); } LoadingChatBubble.displayName = "LoadingChatBubble"; // src/components/avatar/avatar.tsx import { jsx as jsx5 } from "react/jsx-runtime"; function Avatar({ initial, role = "assistant", className, size = "md", hypermodeStyle = false, ...props }) { const sizeClasses = { sm: "w-6 h-6 text-xs", md: "w-8 h-8 text-sm", lg: "w-10 h-10 text-base" }; if (hypermodeStyle) { return /* @__PURE__ */ jsx5( "div", { className: cn( "flex items-center justify-center rounded-full text-white", sizeClasses[size], role === "user" ? "bg-hypermode-accent-light" : "bg-hypermode-accent", className ), ...props, children: initial } ); } return /* @__PURE__ */ jsx5( "div", { className: cn( "flex items-center justify-center rounded-full text-white", sizeClasses[size], role === "user" ? "bg-primary-300" : "bg-primary-500", className ), ...props, children: initial } ); } Avatar.displayName = "Avatar"; // src/components/chat/sidebar.tsx import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime"; function ChatSidebar({ title = "Chat", conversations = [], currentConversationId, onCreateConversation, onDeleteConversation, onSelectConversation, username = "User", className, headerClassName, conversationsClassName, profileClassName, headerContent, profileContent, newConversationIcon = "+", deleteConversationIcon = "\xD7", settingsIcon, hypermodeStyle = false }) { const defaultHypermodeHeader = /* @__PURE__ */ jsxs4( "div", { className: cn( "flex items-center justify-between border-hypermode-border border-b p-3", headerClassName ), children: [ /* @__PURE__ */ jsx6("div", { className: "font-semibold text-lg", children: title }), settingsIcon && /* @__PURE__ */ jsx6( Button, { variant: "ghost", size: "icon", className: "text-neutral-400 hover:bg-hypermode-hover hover:text-white", children: settingsIcon } ) ] } ); const defaultHypermodeProfile = /* @__PURE__ */ jsxs4( "div", { className: cn( "flex items-center justify-between border-hypermode-border border-t p-3", profileClassName ), children: [ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx6( Avatar, { initial: username.charAt(0), role: "user", hypermodeStyle: true } ), /* @__PURE__ */ jsx6("div", { className: "text-sm", children: username }) ] }), settingsIcon && /* @__PURE__ */ jsx6( Button, { variant: "ghost", size: "icon", className: "text-neutral-400 hover:bg-hypermode-hover hover:text-white", children: settingsIcon } ) ] } ); if (hypermodeStyle) { return /* @__PURE__ */ jsxs4( "div", { className: cn( "flex h-full w-64 flex-col border-hypermode-border border-r bg-hypermode-card", className ), children: [ headerContent || defaultHypermodeHeader, /* @__PURE__ */ jsx6("div", { className: "p-3", children: /* @__PURE__ */ jsxs4( Button, { onClick: onCreateConversation, className: "flex w-full items-center justify-center gap-2 bg-hypermode-accent text-white hover:bg-hypermode-accent-light", children: [ typeof newConversationIcon === "string" ? newConversationIcon : newConversationIcon, /* @__PURE__ */ jsx6("span", { children: "New Conversation" }) ] } ) }), /* @__PURE__ */ jsxs4("div", { className: cn("flex-1 overflow-y-auto", conversationsClassName), children: [ /* @__PURE__ */ jsx6("div", { className: "hypermode-sidebar-title", children: "Conversations" }), /* @__PURE__ */ jsx6("div", { className: "space-y-1 px-2", children: conversations.map((conv) => /* @__PURE__ */ jsxs4("div", { className: "group flex items-center", children: [ /* @__PURE__ */ jsx6( "button", { onClick: () => onSelectConversation(conv.id), className: cn( "hypermode-sidebar-item flex-1", conv.id === currentConversationId ? "active" : "" ), children: conv.title } ), /* @__PURE__ */ jsx6( Button, { variant: "ghost", size: "icon", onClick: (e) => { e.stopPropagation(); onDeleteConversation(conv.id); }, className: "p-1 text-neutral-400 opacity-0 hover:text-white group-hover:opacity-100", "aria-label": "Delete conversation", children: typeof deleteConversationIcon === "string" ? deleteConversationIcon : deleteConversationIcon } ) ] }, conv.id)) }) ] }), profileContent || defaultHypermodeProfile ] } ); } return /* @__PURE__ */ jsxs4( "div", { className: cn( "h-full w-64 border-border-primary border-r bg-modal-surface-body-primary p-4", className ), children: [ /* @__PURE__ */ jsxs4( Button, { onClick: onCreateConversation, className: "mb-4 flex w-full items-center justify-center gap-2", children: [ typeof newConversationIcon === "string" ? newConversationIcon : newConversationIcon, "New Conversation" ] } ), /* @__PURE__ */ jsx6("div", { className: "flex-1 space-y-2 overflow-y-auto", children: conversations.map((conv) => /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx6( "button", { onClick: () => onSelectConversation(conv.id), className: cn( "flex-1 rounded-lg p-2 text-left transition-colors", conv.id === currentConversationId ? "bg-primary-300 text-text-inverse" : "hover:bg-neutral-100" ), children: conv.title } ), /* @__PURE__ */ jsx6( Button, { variant: "ghost", size: "icon", onClick: (e) => { e.stopPropagation(); onDeleteConversation(conv.id); }, className: "opacity-50 hover:opacity-100", children: typeof deleteConversationIcon === "string" ? deleteConversationIcon : deleteConversationIcon } ) ] }, conv.id)) }), profileContent || /* @__PURE__ */ jsxs4("div", { className: "mt-4 flex items-center gap-2 border-border-primary border-t pt-4", children: [ /* @__PURE__ */ jsx6(Avatar, { initial: username.charAt(0), role: "user" }), /* @__PURE__ */ jsx6("div", { className: "font-medium text-sm", children: username }) ] }) ] } ); } ChatSidebar.displayName = "ChatSidebar"; // src/components/chat/chat-interface.tsx import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime"; function ChatInterface({ networkAdapter, storageAdapter, showSidebar = true, showInputArea = true, className, chatAreaClassName, sidebarClassName, inputAreaClassName, headerClassName, headerContent, sidebarHeaderContent, userProfileContent, inputAreaContent, username = "User", inputPlaceholder = "Type a message...", sendButtonIcon = "\u2192", newConversationIcon = "+", deleteConversationIcon = "\xD7", hypermodeStyle = false, userAvatar, assistantAvatar }) { const [conversations, setConversations] = useState([]); const [currentConversationId, setCurrentConversationId] = useState(""); const [input, setInput] = useState(""); const [loading, setLoading] = useState(false); const [initialized, setInitialized] = useState(false); const messagesEndRef = useRef(null); const currentConversation = conversations.find( (c) => c.id === currentConversationId ); const messages = (currentConversation == null ? void 0 : currentConversation.messages) || []; useEffect(() => { if (initialized) return; const loadConversations = async () => { if (storageAdapter) { try { if ("syncAllConversationsWithBackend" in storageAdapter) { console.log("Syncing all conversations with backend on app load..."); await storageAdapter.syncAllConversationsWithBackend(); } const loadedConversations = await storageAdapter.getAllConversations(); if (loadedConversations.length > 0) { const uniqueConversations = loadedConversations.filter( (conv, index, arr) => arr.findIndex((c) => c.id === conv.id) === index ); setConversations(uniqueConversations); let activeId = ""; if ("getActiveConversationId" in storageAdapter) { activeId = storageAdapter.getActiveConversationId(); } if (activeId && uniqueConversations.some((c) => c.id === activeId)) { setCurrentConversationId(activeId); } else { setCurrentConversationId(uniqueConversations[0].id); if ("setActiveConversationId" in storageAdapter) { ; storageAdapter.setActiveConversationId( uniqueConversations[0].id ); } } } } catch (error) { console.error("Error loading conversations:", error); } finally { setInitialized(true); } } else { setInitialized(true); } }; loadConversations(); }, [storageAdapter, initialized]); useEffect(() => { if (storageAdapter && "setActiveConversationId" in storageAdapter && currentConversationId) { ; storageAdapter.setActiveConversationId(currentConversationId); const syncCurrentConversation = async () => { if (currentConversationId && storageAdapter) { try { console.log( `Syncing conversation ${currentConversationId} with backend...` ); const latestMessages = await storageAdapter.getConversationHistory( currentConversationId ); setConversations( (prev) => prev.map( (conv) => conv.id === currentConversationId ? { ...conv, messages: latestMessages } : conv ) ); } catch (error) { console.error( `Failed to sync conversation ${currentConversationId}:`, error ); } } }; syncCurrentConversation(); } }, [currentConversationId, storageAdapter]); const scrollToBottom = () => { var _a; (_a = messagesEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" }); }; useEffect(scrollToBottom, [messages]); const createNewConversation = async () => { if (!networkAdapter || !storageAdapter) { console.error("Missing required adapters"); return; } try { const agentId = await networkAdapter.startChatAgent(); const conversationId = `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const newConversation = { id: conversationId, title: `Conversation ${conversations.length + 1}`, messages: [] }; await storageAdapter.saveConversation(newConversation); await storageAdapter.setConversationAgent(conversationId, agentId); setConversations((prev) => { if (prev.some((c) => c.id === conversationId)) { return prev; } return [...prev, newConversation]; }); setCurrentConversationId(conversationId); return newConversation; } catch (error) { console.error("Error creating new conversation:", error); } }; const deleteConversation = async (id) => { if (!storageAdapter) return; try { if (networkAdapter) { const agentId = await storageAdapter.getConversationAgent(id); if (agentId) { await networkAdapter.stopChatAgent(agentId); } } await storageAdapter.deleteConversation(id); setConversations((prev) => prev.filter((conv) => conv.id !== id)); if (id === currentConversationId) { const remainingConvs = conversations.filter((conv) => conv.id !== id); if (remainingConvs.length > 0) { setCurrentConversationId(remainingConvs[0].id); } else { setCurrentConversationId(""); } } } catch (error) { console.error("Error deleting conversation:", error); } }; const sendMessage = async (messageText) => { if (!messageText.trim() || !networkAdapter || !storageAdapter || !currentConversationId) return; const userMessage = { id: Date.now().toString(), content: messageText, role: "user", timestamp: (/* @__PURE__ */ new Date()).toString() }; setConversations( (prev) => prev.map( (conv) => conv.id === currentConversationId ? { ...conv, messages: [...conv.messages, userMessage] } : conv ) ); await storageAdapter.addMessage(currentConversationId, userMessage); setLoading(true); try { const agentId = await storageAdapter.getConversationAgent( currentConversationId ); if (!agentId) { throw new Error("No agent found for conversation"); } const responseMessage = await networkAdapter.sendMessage( agentId, messageText ); setConversations( (prev) => prev.map( (conv) => conv.id === currentConversationId ? { ...conv, messages: [...conv.messages, responseMessage] } : conv ) ); await storageAdapter.addMessage(currentConversationId, responseMessage); } catch (error) { console.error("Error sending message:", error); const errorMessage = { id: Date.now().toString(), content: "Sorry, there was an error sending your message.", role: "assistant", timestamp: (/* @__PURE__ */ new Date()).toString() }; setConversations( (prev) => prev.map( (conv) => conv.id === currentConversationId ? { ...conv, messages: [...conv.messages, errorMessage] } : conv ) ); await storageAdapter.addMessage(currentConversationId, errorMessage); } finally { setLoading(false); } }; const handleSubmit = async (e) => { e.preventDefault(); await sendMessage(input); setInput(""); }; const inputAreaContext = { input, setInput, loading, sendMessage, currentConversationId, inputPlaceholder, sendButtonIcon }; const EmptyState = () => /* @__PURE__ */ jsxs5("div", { className: "flex flex-1 flex-col items-center justify-center p-8 text-center", children: [ /* @__PURE__ */ jsx7("div", { className: "mb-4 text-neutral-400", children: hypermodeStyle ? /* @__PURE__ */ jsx7("div", { className: "mb-4 text-6xl", children: "\u{1F4AC}" }) : /* @__PURE__ */ jsx7("div", { className: "mb-4 text-6xl", children: "\u{1F4AC}" }) }), /* @__PURE__ */ jsx7( "h2", { className: cn( "mb-2 font-medium text-xl", hypermodeStyle ? "text-white" : "text-text-primary" ), children: "Welcome to Chat" } ), /* @__PURE__ */ jsx7( "p", { className: cn( "mb-6 max-w-md", hypermodeStyle ? "text-neutral-400" : "text-text-secondary" ), children: "Start a new conversation to begin chatting with the AI assistant." } ), /* @__PURE__ */ jsxs5( Button, { onClick: createNewConversation, className: cn( "flex items-center gap-2", hypermodeStyle ? "bg-hypermode-accent hover:bg-hypermode-accent-light" : "" ), children: [ typeof newConversationIcon === "string" ? newConversationIcon : newConversationIcon, "Start New Conversation" ] } ) ] }); const defaultHypermodeHeader = /* @__PURE__ */ jsx7("div", { className: "flex items-center justify-between border-hypermode-border border-b p-3", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center", children: [ assistantAvatar || /* @__PURE__ */ jsx7("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-hypermode-accent text-sm text-white", children: "A" }), /* @__PURE__ */ jsx7("span", { className: "ml-2 font-medium", children: "Assistant" }) ] }) }); const renderDefaultInputArea = () => { if (hypermodeStyle) { return /* @__PURE__ */ jsx7( DefaultHypermodeInputArea, { input, setInput, loading, onSubmit: handleSubmit, inputPlaceholder, sendButtonIcon, inputAreaClassName } ); } else { return /* @__PURE__ */ jsx7( DefaultStandardInputArea, { input, setInput, loading, onSubmit: handleSubmit, inputPlaceholder, sendButtonIcon, inputAreaClassName } ); } }; if (hypermodeStyle) { return /* @__PURE__ */ jsxs5("div", { className: cn("flex h-full bg-hypermode-bg text-white", className), children: [ showSidebar && /* @__PURE__ */ jsx7( ChatSidebar, { conversations, currentConversationId, onCreateConversation: createNewConversation, onDeleteConversation: deleteConversation, onSelectConversation: setCurrentConversationId, username, className: sidebarClassName, headerContent: sidebarHeaderContent, profileContent: userProfileContent, newConversationIcon, deleteConversationIcon, hypermodeStyle: true } ), /* @__PURE__ */ jsxs5( "div", { className: cn( "flex flex-1 flex-col bg-hypermode-bg", chatAreaClassName ), children: [ headerContent || defaultHypermodeHeader, !currentConversationId ? /* @__PURE__ */ jsx7(EmptyState, {}) : /* @__PURE__ */ jsxs5(Fragment, { children: [ /* @__PURE__ */ jsxs5("div", { className: cn("flex-1 space-y-6 overflow-y-auto p-4"), children: [ messages.map((message) => /* @__PURE__ */ jsx7( ChatBubble, { content: message.content, role: message.role, timestamp: message.timestamp, hypermodeStyle: true, avatar: message.role === "user" ? userAvatar : assistantAvatar }, message.id )), loading && /* @__PURE__ */ jsx7( LoadingChatBubble, { hypermodeStyle: true, avatar: assistantAvatar } ), /* @__PURE__ */ jsx7("div", { ref: messagesEndRef }) ] }), showInputArea && (inputAreaContent ? ( // Handle both ReactNode and function types typeof inputAreaContent === "function" ? inputAreaContent(inputAreaContext) : inputAreaContent ) : renderDefaultInputArea()) ] }) ] } ) ] }); } return /* @__PURE__ */ jsxs5("div", { className: cn("flex h-full bg-white", className), children: [ showSidebar && /* @__PURE__ */ jsx7( ChatSidebar, { conversations, currentConversationId, onCreateConversation: createNewConversation, onDeleteConversation: deleteConversation, onSelectConversation: setCurrentConversationId, username, className: sidebarClassName, headerContent: sidebarHeaderContent, profileContent: userProfileContent, newConversationIcon, deleteConversationIcon, hypermodeStyle: false } ), /* @__PURE__ */ jsxs5("div", { className: cn("flex flex-1 flex-col bg-white", chatAreaClassName), children: [ /* @__PURE__ */ jsx7("div", { className: cn("bg-white p-4 shadow-xs", headerClassName), children: headerContent }), !currentConversationId ? /* @__PURE__ */ jsx7(EmptyState, {}) : /* @__PURE__ */ jsxs5(Fragment, { children: [ /* @__PURE__ */ jsxs5("div", { className: "flex-1 space-y-4 overflow-y-auto bg-white p-4", children: [ messages.map((message) => /* @__PURE__ */ jsx7( ChatBubble, { content: message.content, role: message.role, timestamp: message.timestamp, avatar: message.role === "user" ? userAvatar : assistantAvatar }, message.id )), loading && /* @__PURE__ */ jsx7(LoadingChatBubble, {}), /* @__PURE__ */ jsx7("div", { ref: messagesEndRef }) ] }), showInputArea && (inputAreaContent ? ( // Handle both ReactNode and function types typeof inputAreaContent === "function" ? inputAreaContent(inputAreaContext) : inputAreaContent ) : renderDefaultInputArea()) ] }) ] }) ] }); } ChatInterface.displayName = "ChatInterface"; // src/index.ts var CHATKIT_CSS_PATH = "./styles/base.css"; export { Avatar, Button, CHATKIT_CSS_PATH, ChatBubble, ChatInterface, ChatSidebar, DefaultHypermodeInputArea, DefaultStandardInputArea, Input, LoadingChatBubble, LoadingDot, LoadingIndicator, cn };