@agentdao/core
Version:
Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration
334 lines (333 loc) • 19.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LiveChatWidget = LiveChatWidget;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const theme_1 = require("./theme");
function LiveChatWidget({ config, className, style }) {
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
const [isMinimized, setIsMinimized] = (0, react_1.useState)(false);
const [messages, setMessages] = (0, react_1.useState)([]);
const [inputValue, setInputValue] = (0, react_1.useState)('');
const [isTyping, setIsTyping] = (0, react_1.useState)(false);
const [isConnected, setIsConnected] = (0, react_1.useState)(false);
const messagesEndRef = (0, react_1.useRef)(null);
const inputRef = (0, react_1.useRef)(null);
const theme = (0, theme_1.getTheme)(config.theme || 'auto');
(0, react_1.useEffect)(() => {
// Inject CSS variables
const styleElement = document.createElement('style');
styleElement.textContent = `
.agentdao-chat-widget {
${(0, theme_1.generateCSSVariables)(theme)}
}
`;
document.head.appendChild(styleElement);
// Add welcome message
if (config.welcomeMessage) {
setMessages([{
id: 'welcome',
text: config.welcomeMessage,
sender: 'agent',
timestamp: new Date(),
status: 'read'
}]);
}
return () => {
document.head.removeChild(styleElement);
};
}, [theme, config.welcomeMessage]);
(0, react_1.useEffect)(() => {
// Auto-scroll to bottom when new messages arrive
if (config.autoScroll !== false) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [messages, config.autoScroll]);
(0, react_1.useEffect)(() => {
// Focus input when chat opens
if (isOpen && !isMinimized) {
inputRef.current?.focus();
}
}, [isOpen, isMinimized]);
const handleSendMessage = async () => {
if (!inputValue.trim() || isTyping)
return;
const userMessage = {
id: Date.now().toString(),
text: inputValue.trim(),
sender: 'user',
timestamp: new Date(),
status: 'sending'
};
setMessages(prev => [...prev, userMessage]);
setInputValue('');
setIsTyping(true);
try {
// Call real AI API instead of mock response
const response = await fetch('/api/skills/live-chat-widget', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: userMessage.text,
config: {
agentName: config.agentName,
ai: {
model: 'gpt-3.5-turbo',
maxTokens: 500,
temperature: 0.7
}
},
conversationHistory: messages.slice(-5) // Send last 5 messages for context
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to get AI response');
}
const data = await response.json();
const agentMessage = {
id: (Date.now() + 1).toString(),
text: data.response,
sender: 'agent',
timestamp: new Date(),
status: 'sent'
};
setMessages(prev => [...prev, agentMessage]);
// Update user message status
setMessages(prev => prev.map(msg => msg.id === userMessage.id ? { ...msg, status: 'sent' } : msg));
if (config.onMessageReceived) {
config.onMessageReceived(agentMessage);
}
}
catch (error) {
console.error('Live chat widget error:', error);
// Show error message to user
const errorMessage = {
id: (Date.now() + 1).toString(),
text: error instanceof Error ? error.message : 'Sorry, I encountered an error. Please try again.',
sender: 'agent',
timestamp: new Date(),
status: 'error'
};
setMessages(prev => [...prev, errorMessage]);
// Update user message status to error
setMessages(prev => prev.map(msg => msg.id === userMessage.id ? { ...msg, status: 'error' } : msg));
}
finally {
setIsTyping(false);
}
if (config.onMessageSent) {
config.onMessageSent(userMessage);
}
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
const getPositionStyles = () => {
const baseStyles = {
position: 'fixed',
zIndex: 9999,
...style
};
switch (config.position) {
case 'bottom-right':
return { ...baseStyles, bottom: '20px', right: '20px' };
case 'bottom-left':
return { ...baseStyles, bottom: '20px', left: '20px' };
case 'top-right':
return { ...baseStyles, top: '20px', right: '20px' };
case 'top-left':
return { ...baseStyles, top: '20px', left: '20px' };
default:
return { ...baseStyles, bottom: '20px', right: '20px' };
}
};
const formatTime = (date) => {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
};
return ((0, jsx_runtime_1.jsxs)("div", { className: `agentdao-chat-widget ${className || ''}`, style: getPositionStyles(), children: [!isOpen && ((0, jsx_runtime_1.jsxs)("button", { onClick: () => {
setIsOpen(true);
setIsMinimized(false);
if (config.onChatStarted) {
config.onChatStarted();
}
}, style: {
width: '60px',
height: '60px',
borderRadius: '50%',
backgroundColor: theme.colors.primary,
color: 'white',
border: 'none',
cursor: 'pointer',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
fontSize: '24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
}, children: ["\uD83D\uDCAC", messages.length > 1 && ((0, jsx_runtime_1.jsx)("div", { style: {
position: 'absolute',
top: '-5px',
right: '-5px',
backgroundColor: theme.colors.error,
color: 'white',
borderRadius: '50%',
width: '20px',
height: '20px',
fontSize: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}, children: messages.length - 1 }))] })), isOpen && ((0, jsx_runtime_1.jsxs)("div", { style: {
width: '350px',
height: isMinimized ? '60px' : '500px',
backgroundColor: theme.colors.surface,
borderRadius: theme.borderRadius.lg,
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1)',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
transition: 'height 0.3s ease',
}, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
backgroundColor: theme.colors.primary,
color: 'white',
padding: theme.spacing.md,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
cursor: 'pointer',
}, onClick: () => setIsMinimized(!isMinimized), children: [(0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: theme.spacing.sm }, children: [config.showAvatar && ((0, jsx_runtime_1.jsx)("div", { style: {
width: '32px',
height: '32px',
borderRadius: '50%',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '16px',
}, children: "\uD83E\uDD16" })), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: {
fontWeight: 'bold',
fontSize: theme.typography.fontSize.sm,
fontFamily: theme.typography.fontFamily
}, children: config.agentName }), (0, jsx_runtime_1.jsx)("div", { style: {
fontSize: theme.typography.fontSize.xs,
opacity: 0.8,
fontFamily: theme.typography.fontFamily
}, children: isConnected ? 'Online' : 'Connecting...' })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: theme.spacing.xs }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: (e) => {
e.stopPropagation();
setIsMinimized(!isMinimized);
}, style: {
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
fontSize: '16px',
padding: '4px',
}, children: isMinimized ? '□' : '−' }), (0, jsx_runtime_1.jsx)("button", { onClick: (e) => {
e.stopPropagation();
setIsOpen(false);
if (config.onChatEnded) {
config.onChatEnded();
}
}, style: {
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer',
fontSize: '16px',
padding: '4px',
}, children: "\u00D7" })] })] }), !isMinimized && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: {
flex: 1,
overflow: 'auto',
padding: theme.spacing.md,
display: 'flex',
flexDirection: 'column',
gap: theme.spacing.sm,
}, children: [messages.slice(-(config.maxMessages || 50)).map((message) => ((0, jsx_runtime_1.jsx)("div", { style: {
display: 'flex',
justifyContent: message.sender === 'user' ? 'flex-end' : 'flex-start',
}, children: (0, jsx_runtime_1.jsxs)("div", { style: {
maxWidth: '80%',
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
borderRadius: theme.borderRadius.md,
backgroundColor: message.sender === 'user'
? theme.colors.primary
: theme.colors.background,
color: message.sender === 'user'
? 'white'
: theme.colors.text,
fontSize: theme.typography.fontSize.sm,
fontFamily: theme.typography.fontFamily,
wordWrap: 'break-word',
}, children: [(0, jsx_runtime_1.jsx)("div", { children: message.text }), (0, jsx_runtime_1.jsxs)("div", { style: {
fontSize: theme.typography.fontSize.xs,
opacity: 0.7,
marginTop: '4px',
textAlign: message.sender === 'user' ? 'right' : 'left',
}, children: [formatTime(message.timestamp), message.status === 'sending' && ' • Sending...', message.status === 'sent' && ' • Sent', message.status === 'delivered' && ' • Delivered', message.status === 'read' && ' • Read'] })] }) }, message.id))), isTyping && config.showTypingIndicator && ((0, jsx_runtime_1.jsx)("div", { style: { display: 'flex', justifyContent: 'flex-start' }, children: (0, jsx_runtime_1.jsx)("div", { style: {
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
borderRadius: theme.borderRadius.md,
backgroundColor: theme.colors.background,
fontSize: theme.typography.fontSize.sm,
fontFamily: theme.typography.fontFamily,
}, children: (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '4px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
width: '8px',
height: '8px',
borderRadius: '50%',
backgroundColor: theme.colors.textSecondary,
animation: 'typing 1.4s infinite ease-in-out',
} }), (0, jsx_runtime_1.jsx)("div", { style: {
width: '8px',
height: '8px',
borderRadius: '50%',
backgroundColor: theme.colors.textSecondary,
animation: 'typing 1.4s infinite ease-in-out 0.2s',
} }), (0, jsx_runtime_1.jsx)("div", { style: {
width: '8px',
height: '8px',
borderRadius: '50%',
backgroundColor: theme.colors.textSecondary,
animation: 'typing 1.4s infinite ease-in-out 0.4s',
} })] }) }) })), (0, jsx_runtime_1.jsx)("div", { ref: messagesEndRef })] }), (0, jsx_runtime_1.jsx)("div", { style: {
padding: theme.spacing.md,
borderTop: `1px solid ${theme.colors.border}`,
backgroundColor: theme.colors.surface,
}, children: (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: theme.spacing.sm }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: handleKeyPress, placeholder: "Type your message...", disabled: isTyping, style: {
flex: 1,
padding: theme.spacing.sm,
border: `1px solid ${theme.colors.border}`,
borderRadius: theme.borderRadius.sm,
backgroundColor: theme.colors.background,
color: theme.colors.text,
fontSize: theme.typography.fontSize.sm,
fontFamily: theme.typography.fontFamily,
outline: 'none',
} }), (0, jsx_runtime_1.jsx)("button", { onClick: handleSendMessage, disabled: !inputValue.trim() || isTyping, style: {
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
border: 'none',
borderRadius: theme.borderRadius.sm,
backgroundColor: inputValue.trim() && !isTyping
? theme.colors.primary
: theme.colors.secondary,
color: 'white',
cursor: inputValue.trim() && !isTyping ? 'pointer' : 'not-allowed',
fontSize: theme.typography.fontSize.sm,
fontFamily: theme.typography.fontFamily,
}, children: "Send" })] }) })] }))] })), (0, jsx_runtime_1.jsx)("style", { children: `
@keyframes typing {
0%, 60%, 100% {
transform: translateY(0);
opacity: 0.4;
}
30% {
transform: translateY(-10px);
opacity: 1;
}
}
` })] }));
}