import React, { useState, useEffect, useRef } from 'react'; import { useChat } from '../hooks/useChat'; export interface VantaAgentProps { agentId: string; type?: 'floating' | 'embedded' | 'fullpage'; position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; defaultOpen?: boolean; apiKey?: string; apiBaseUrl?: string; triggerIcon?: React.ReactNode | string; // Support custom node or image URL theme?: { primaryColor?: string; backgroundColor?: string; textColor?: string; }; } export const VantaAgent: React.FC = ({ agentId, type = 'floating', position = 'bottom-right', defaultOpen = false, apiKey, apiBaseUrl, triggerIcon, theme }) => { const [isOpen, setIsOpen] = useState(defaultOpen); const { messages, sendMessage, isLoading, agentConfig, isConfigLoading } = useChat({ agentId, apiKey, apiBaseUrl }); useEffect(() => { console.log("AGENT CONFIG LOADED:", agentConfig); }, [agentConfig]); const [inputValue, setInputValue] = useState(''); const [isExpanded, setIsExpanded] = useState(false); const messagesEndRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (inputValue.trim()) { sendMessage(inputValue); setInputValue(''); } }; const toggleChat = () => setIsOpen(!isOpen); const toggleExpand = () => setIsExpanded(!isExpanded); // Merge backend config with user theme, prioritizing Backend Config as source of truth const primaryColor = agentConfig?.primaryColor || theme?.primaryColor || '#00E5FF'; const bgColor = agentConfig?.backgroundColor || theme?.backgroundColor || 'rgba(15, 23, 42, 0.85)'; const textColor = agentConfig?.textColor || theme?.textColor || '#F8FAFC'; const agentName = agentConfig?.name || 'Vanta Agent'; const agentAvatar = agentConfig?.avatar; // CSS Variables for dynamic theming const cssVars = { '--v-primary': primaryColor, '--v-bg': bgColor, '--v-text': textColor } as React.CSSProperties; // Static CSS rules for high compression (Pre-minified) const staticCss = `.v-container{font-family:"-apple-system","BlinkMacSystemFont","SF Pro Text","Segoe UI","Inter",sans-serif;-webkit-font-smoothing:antialiased;z-index:9999}.v-container.floating{position:fixed}.v-container.floating.bottom{bottom:24px}.v-container.floating.top{top:24px}.v-container.floating.right{right:24px}.v-container.floating.left{left:24px}.v-container.fullpage{width:100%;height:100%;min-height:500px}.v-window{background-color:var(--v-bg);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(255,255,255,0.1);color:var(--v-text);border-radius:24px;box-shadow:0 20px 40px rgba(0,0,0,0.3);display:flex;flex-direction:column;overflow:hidden;transition:opacity .3s cubic-bezier(0.32,0.72,0,1),transform .4s cubic-bezier(0.32,0.72,0,1)}.v-window.floating{width:380px;height:650px;max-height:calc(100vh - 120px);margin-bottom:84px}.v-window.floating.expanded{width:85vw;height:85vh}.v-window.fullpage{width:100%;height:100%}.v-window.closed{opacity:0;transform:scale(0.85) translateY(40px);pointer-events:none;position:absolute;bottom:84px;right:0}.v-window.open{opacity:1;transform:scale(1) translateY(0)}.v-window.transform-br{transform-origin:bottom right}.v-window.transform-tr{transform-origin:top right}.v-header{background:linear-gradient(135deg,color-mix(in srgb,var(--v-primary) 40%,transparent) 0%,transparent 100%);border-bottom:1px solid rgba(255,255,255,0.05);padding:20px;display:flex;justify-content:space-between;align-items:center;font-weight:600}.v-mac-buttons{display:flex;align-items:center;gap:8px;margin-right:16px;padding:4px 0}.v-mac-btn{width:12px;height:12px;border-radius:50%;border:none;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.v-mac-btn svg{opacity:0;transition:opacity .2s}.v-mac-buttons:hover .v-mac-btn svg{opacity:1}.v-mac-close{background-color:#ff5f56;color:#4c0000}.v-mac-close:hover{background-color:#ff4b4d}.v-mac-min{background-color:#ffbd2e;color:#995b00}.v-mac-min:hover{background-color:#ffa726}.v-mac-exp{background-color:#27c93f;color:#004d00}.v-mac-exp:hover{background-color:#1db833}.v-profile{display:flex;align-items:center;gap:12px;flex:1;justify-content:flex-end}.v-avatar,.v-empty-avatar{width:30px;height:30px;border-radius:50%;object-fit:cover}.v-empty-avatar{width:64px;height:64px}.v-avatar-fallback{width:30px;height:30px;border-radius:50%;background:var(--v-primary);display:flex;align-items:center;justify-content:center;font-weight:700}.v-name{font-size:15px;font-weight:600;letter-spacing:-.3px}.v-messages{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:16px}@keyframes v-shimmer{0%{background-position:-400px 0}100%{background-position:400px 0}}.v-skeleton{animation:v-shimmer 1.5s infinite linear;background:linear-gradient(to right,rgba(255,255,255,0.05) 4%,rgba(255,255,255,0.1) 25%,rgba(255,255,255,0.05) 36%);background-size:800px 100%;border-radius:20px}.v-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;opacity:.7;gap:16px}.v-empty-fallback{width:64px;height:64px;border-radius:50%;background:var(--v-primary);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:24px;color:#fff}.v-empty-title{font-size:16px;font-weight:500}.v-empty-desc{font-size:13px;opacity:.7;text-align:center;max-width:80%}.v-msg{padding:12px 16px;max-width:85%;line-height:1.5;display:flex;flex-direction:column;gap:8px}.v-msg.user{align-self:flex-end;background-color:var(--v-primary);color:#fff;border-radius:20px 20px 4px 20px;box-shadow:0 4px 12px color-mix(in srgb,var(--v-primary) 40%,transparent)}.v-msg.assistant{align-self:flex-start;background-color:rgba(255,255,255,0.05);color:var(--v-text);border-radius:20px 20px 20px 4px;border:1px solid rgba(255,255,255,0.1)}.v-msg-label{font-size:11px;font-weight:600;opacity:.5;margin-bottom:-4px;display:flex;align-items:center;gap:6px}.v-msg-label-avatar{width:14px;height:14px;border-radius:50%;object-fit:cover}.v-msg-label-fallback{width:14px;height:14px;border-radius:50%;background:var(--v-primary);display:flex;align-items:center;justify-content:center;font-size:8px;color:#fff}.v-msg-content{white-space:pre-wrap}.v-loading{align-self:flex-start;display:flex;gap:6px;padding:16px 20px;background-color:rgba(255,255,255,0.08);border-radius:20px 20px 20px 4px;border:1px solid rgba(255,255,255,0.1)}@keyframes v-bounce{0%,100%,80%{transform:scale(0);opacity:.5}40%{transform:scale(1);opacity:1}}.v-loading-dot{width:8px;height:8px;border-radius:50%;background-color:var(--v-primary);animation:v-bounce 1.4s infinite ease-in-out both}.v-form{padding:20px;border-top:1px solid rgba(255,255,255,0.05);display:flex;gap:12px;background:rgba(0,0,0,0.2);position:relative;z-index:10}.v-input{flex:1;padding:12px 16px;background-color:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:24px;outline:none;color:var(--v-text);transition:border-color .2s;min-width:0}.v-input:focus{border-color:var(--v-primary)}.v-send{background-color:var(--v-primary);color:#fff;border:none;border-radius:24px;padding:0 20px;font-weight:600;transition:opacity .2s,transform .2s;white-space:nowrap;flex-shrink:0}.v-send:not(:disabled){cursor:pointer}.v-send:not(:disabled):hover{transform:scale(1.05)}.v-send:disabled{cursor:not-allowed;opacity:.5}.v-trigger{width:64px;height:64px;border-radius:50%;background:linear-gradient(135deg,var(--v-primary),#0055ff);border:none;cursor:pointer;box-shadow:0 8px 24px color-mix(in srgb,var(--v-primary) 60%,transparent);display:flex;align-items:center;justify-content:center;position:absolute;bottom:0;right:0;color:#fff;transition:transform .2s ease,box-shadow .2s ease;z-index:100}.v-trigger:hover{transform:scale(1.05)}`; // Dynamic Classes const cContainer = `v-container ${type} ${position.split('-').join(' ')}`; const cWindow = `v-window ${type} ${isOpen ? 'open' : 'closed'} ${position.includes('bottom') ? 'transform-br' : 'transform-tr'} ${isExpanded ? 'expanded' : ''}`; return (
{agentAvatar ? ( {agentName} ) : (
V
)} {agentName}
{isConfigLoading ? (
) : messages.length === 0 ? (
{agentAvatar ? ( {agentName} ) : (
V
)}
How can I help you today?
{agentConfig?.description || 'I am your virtual assistant, ready to answer your questions.'}
) : ( messages.filter(msg => msg.content.trim() !== '').map((msg, idx) => (
{msg.role === 'assistant' && (
{agentAvatar ? ( {agentName} ) : (
V
)} {agentName}
)}
{msg.content}
)) )} {isLoading && (
)}
setInputValue(e.target.value)} placeholder="Type your message..." className="v-input" />
{type === 'floating' && ( )}
); };