@jeanmemory/react
Version:
React SDK for Jean Memory - Build personalized AI chatbots in 5 lines of code
69 lines (68 loc) • 12.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.JeanChat = JeanChat;
const jsx_runtime_1 = require("react/jsx-runtime");
/**
* Jean Memory React SDK - Chat Component
* Complete chat interface with authentication
*/
const react_1 = require("react");
const provider_1 = require("./provider");
const lucide_react_1 = require("lucide-react");
function JeanChat({ className = '', showHeader = true, placeholder = 'Type your message...', style }) {
const agent = (0, provider_1.useJean)();
const [input, setInput] = (0, react_1.useState)('');
const messagesEndRef = (0, react_1.useRef)(null);
const [isDark, setIsDark] = (0, react_1.useState)(false);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
(0, react_1.useEffect)(() => {
scrollToBottom();
}, [agent.messages]);
(0, react_1.useEffect)(() => {
if (typeof window !== 'undefined') {
const storedTheme = localStorage.getItem('jean-chat-theme');
if (storedTheme === 'dark') {
setIsDark(true);
}
}
}, []);
(0, react_1.useEffect)(() => {
if (isDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('jean-chat-theme', 'dark');
}
else {
document.documentElement.classList.remove('dark');
localStorage.setItem('jean-chat-theme', 'light');
}
}, [isDark]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim() || agent.isLoading)
return;
const message = input.trim();
setInput('');
try {
await agent.sendMessage(message);
}
catch (err) {
console.error('Failed to send message:', err);
}
};
if (!agent.isAuthenticated) {
return ((0, jsx_runtime_1.jsx)("div", { className: `flex items-center justify-center h-full ${className} bg-white dark:bg-gray-950`, style: style, children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-8 max-w-sm w-full", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 mx-auto mb-6 rounded-full bg-gray-50 dark:bg-gray-900 flex items-center justify-center border border-gray-100 dark:border-gray-800", children: (0, jsx_runtime_1.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 2L13.09 8.26L20 9L13.09 15.74L12 22L10.91 15.74L4 9L10.91 8.26L12 2Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-400 dark:text-gray-500" }) }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-medium text-gray-900 dark:text-white mb-2", children: "Connect to Jean Memory" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-500 dark:text-gray-400 text-sm mb-8 leading-relaxed", children: "Sign in to access your personalized AI assistant with persistent memory." }), (0, jsx_runtime_1.jsxs)("button", { onClick: agent.signIn, className: "inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-medium text-sm rounded-lg hover:bg-gray-800 dark:hover:bg-gray-100 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-900 dark:focus:ring-gray-300 focus:ring-offset-2", children: [(0, jsx_runtime_1.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 2L13.09 8.26L20 9L13.09 15.74L12 22L10.91 15.74L4 9L10.91 8.26L12 2Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }), (0, jsx_runtime_1.jsx)("span", { children: "Sign In" })] })] }) }));
}
return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-col h-full bg-white dark:bg-gray-950 ${className}`, style: style, children: [showHeader && ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center px-5 py-4 border-b border-gray-100 dark:border-gray-800", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-900 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Bot, { size: 16, className: "text-gray-600 dark:text-gray-400" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-medium text-gray-900 dark:text-white text-sm", children: "Jean Memory" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-500 dark:text-gray-500", children: agent.user?.email })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-1", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setIsDark(!isDark), className: "p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-900 rounded-lg transition-colors", children: isDark ? (0, jsx_runtime_1.jsx)(lucide_react_1.Sun, { size: 16 }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Moon, { size: 16 }) }), (0, jsx_runtime_1.jsx)("button", { onClick: () => window.location.reload(), disabled: agent.messages.length === 0, className: "p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-900 rounded-lg disabled:opacity-40 disabled:cursor-not-allowed transition-colors", title: "Clear Conversation", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 16 }) }), (0, jsx_runtime_1.jsx)("button", { onClick: agent.signOut, className: "p-2 text-gray-400 hover:text-red-500 hover:bg-gray-50 dark:hover:bg-gray-900 rounded-lg transition-colors", title: "Sign Out", children: (0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { size: 16 }) })] })] })), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto px-4 py-6", children: agent.messages.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center h-full text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-16 h-16 rounded-full bg-gray-50 dark:bg-gray-900 flex items-center justify-center mb-4", children: (0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-400 dark:text-gray-500", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 2L13.09 8.26L20 9L13.09 15.74L12 22L10.91 15.74L4 9L10.91 8.26L12 2Z" }) }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-base font-medium text-gray-900 dark:text-white mb-2", children: "How can I help you today?" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-gray-400 max-w-xs", children: "I have access to your personal context and can assist with a wide range of topics." })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6 max-w-3xl mx-auto", children: [agent.messages.map((message) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-start gap-3 ${message.role === 'user' ? 'flex-row-reverse' : ''}`, children: [(0, jsx_runtime_1.jsx)("div", { className: "w-7 h-7 rounded-full bg-gray-100 dark:bg-gray-900 flex items-center justify-center flex-shrink-0", children: message.role === 'user' ? ((0, jsx_runtime_1.jsx)(lucide_react_1.User, { size: 14, className: "text-gray-600 dark:text-gray-400" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Bot, { size: 14, className: "text-gray-600 dark:text-gray-400" })) }), (0, jsx_runtime_1.jsxs)("div", { className: `flex-1 max-w-[85%] ${message.role === 'user' ? 'text-right' : ''}`, children: [(0, jsx_runtime_1.jsx)("div", { className: `inline-block px-4 py-3 rounded-xl ${message.role === 'user'
? 'bg-gray-900 dark:bg-white text-white dark:text-gray-900'
: 'bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-100 dark:border-gray-800'}`, children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm whitespace-pre-wrap leading-relaxed", children: message.content }) }), (0, jsx_runtime_1.jsx)("div", { className: `mt-1.5 px-1 ${message.role === 'user' ? 'text-right' : 'text-left'}`, children: (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: message.timestamp.toLocaleTimeString([], {
hour: 'numeric',
minute: '2-digit'
}) }) })] })] }, message.id))), agent.isLoading && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-3 max-w-3xl mx-auto", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-7 h-7 rounded-full bg-gray-100 dark:bg-gray-900 flex items-center justify-center flex-shrink-0", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Bot, { size: 14, className: "text-gray-600 dark:text-gray-400" }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-4 py-3 rounded-xl border border-gray-100 dark:border-gray-800", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-1", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-1.5 h-1.5 bg-gray-400 dark:bg-gray-500 rounded-full animate-bounce" }), (0, jsx_runtime_1.jsx)("div", { className: "w-1.5 h-1.5 bg-gray-400 dark:bg-gray-500 rounded-full animate-bounce", style: { animationDelay: '0.1s' } }), (0, jsx_runtime_1.jsx)("div", { className: "w-1.5 h-1.5 bg-gray-400 dark:bg-gray-500 rounded-full animate-bounce", style: { animationDelay: '0.2s' } })] }) })] })), (0, jsx_runtime_1.jsx)("div", { ref: messagesEndRef })] })) }), (0, jsx_runtime_1.jsxs)("div", { className: "border-t border-gray-100 dark:border-gray-800 p-4", children: [agent.error && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50 rounded-lg flex items-start space-x-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-red-500 dark:text-red-400 mt-0.5", children: (0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1", children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-600 dark:text-red-400 text-sm", children: agent.error }) }), (0, jsx_runtime_1.jsx)("button", { onClick: () => window.location.reload(), className: "p-1 text-red-500 hover:bg-red-100 dark:hover:bg-red-900/50 rounded", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 14 }) })] })), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "relative max-w-3xl mx-auto", children: [(0, jsx_runtime_1.jsx)("textarea", { value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
}, placeholder: placeholder, disabled: agent.isLoading, className: "w-full px-4 py-3 pr-12 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-xl focus:outline-none focus:ring-1 focus:ring-gray-300 dark:focus:ring-gray-600 focus:border-transparent disabled:opacity-50 resize-none text-sm text-gray-900 dark:text-gray-100", rows: 1 }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: agent.isLoading || !input.trim(), className: "absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 bg-gray-900 dark:bg-white text-white dark:text-gray-900 rounded-lg hover:bg-gray-800 dark:hover:bg-gray-100 disabled:opacity-40 disabled:cursor-not-allowed flex items-center justify-center transition-colors", children: agent.isLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "w-3 h-3 border-2 border-current border-t-transparent rounded-full animate-spin" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Send, { size: 14 })) })] })] })] }));
}
;