UNPKG

aspirechat

Version:

A highly customizable React chatbot component with extensive configuration options

525 lines (504 loc) 27.3 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'; import { v4 } from 'uuid'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function useChat(options) { var _a; if (options === void 0) { options = {}; } var _b = options.initialMessages, initialMessages = _b === void 0 ? [] : _b, persistKey = options.persistKey, _c = options.flows, flows = _c === void 0 ? {} : _c, _d = options.initialFlow, initialFlow = _d === void 0 ? null : _d, _e = options.typingDelay, typingDelay = _e === void 0 ? 1000 : _e, _f = options.initiallyOpen, initiallyOpen = _f === void 0 ? false : _f; var _g = useState({ messages: initialMessages, isTyping: false, isOpen: initiallyOpen, isMinimized: false, currentFlow: initialFlow, availableOptions: (initialFlow && ((_a = flows[initialFlow]) === null || _a === void 0 ? void 0 : _a.options)) || [], }), state = _g[0], setState = _g[1]; // Track if component is mounted var isMounted = useRef(true); useEffect(function () { return function () { isMounted.current = false; }; }, []); // Load persisted messages if available useEffect(function () { if (persistKey) { try { var savedMessages_1 = localStorage.getItem(persistKey); if (savedMessages_1) { setState(function (prev) { return (__assign(__assign({}, prev), { messages: JSON.parse(savedMessages_1) })); }); } } catch (error) { console.error("Error loading persisted chat messages:", error); } } }, [persistKey]); // Save messages to localStorage when they change useEffect(function () { if (persistKey && state.messages.length > 0) { try { localStorage.setItem(persistKey, JSON.stringify(state.messages)); } catch (error) { console.error("Error persisting chat messages:", error); } } }, [state.messages, persistKey]); // Initialize first flow if provided useEffect(function () { if (initialFlow && flows[initialFlow] && state.messages.length === 0) { startFlow(initialFlow); } }, [initialFlow, flows]); var addMessage = useCallback(function (message) { var newMessage = __assign(__assign({}, message), { id: v4(), timestamp: Date.now() }); setState(function (prev) { return (__assign(__assign({}, prev), { messages: __spreadArray(__spreadArray([], prev.messages, true), [newMessage], false) })); }); return newMessage; }, []); var updateMessage = useCallback(function (id, updates) { setState(function (prev) { return (__assign(__assign({}, prev), { messages: prev.messages.map(function (msg) { return msg.id === id ? __assign(__assign({}, msg), updates) : msg; }) })); }); }, []); var clearMessages = useCallback(function () { setState(function (prev) { var _a; return (__assign(__assign({}, prev), { messages: [], availableOptions: prev.currentFlow ? ((_a = flows[prev.currentFlow]) === null || _a === void 0 ? void 0 : _a.options) || [] : [] })); }); if (persistKey) { localStorage.removeItem(persistKey); } }, [persistKey, flows]); // Start a specific flow var startFlow = useCallback(function (flowId) { var flow = flows[flowId]; if (!flow) return; // Show typing indicator setState(function (prev) { return (__assign(__assign({}, prev), { isTyping: true, currentFlow: flowId })); }); // Add each message in the flow with a delay var delay = 0; flow.messages.forEach(function (message) { setTimeout(function () { if (isMounted.current) { addMessage(message); // After the last message, set available options and turn off typing if (message === flow.messages[flow.messages.length - 1]) { setState(function (prev) { return (__assign(__assign({}, prev), { isTyping: false, availableOptions: flow.options || [] })); }); } } }, delay); delay += typingDelay; }); }, [flows, typingDelay, addMessage]); // Handle button clicks var handleButtonClick = useCallback(function (option) { // Add user's selection as a message addMessage({ text: option.text, type: "user", }); // If there's a next flow, start it if (option.nextFlow && flows[option.nextFlow]) { startFlow(option.nextFlow); } // Otherwise, just add the button value as a bot response else if (option.value) { // Show typing indicator setState(function (prev) { return (__assign(__assign({}, prev), { isTyping: true, availableOptions: [] })); }); setTimeout(function () { if (isMounted.current) { addMessage({ text: option.value, type: "bot", }); setState(function (prev) { return (__assign(__assign({}, prev), { isTyping: false })); }); } }, typingDelay); } }, [addMessage, flows, startFlow, typingDelay]); var toggleChat = useCallback(function () { setState(function (prev) { return (__assign(__assign({}, prev), { isOpen: !prev.isOpen })); }); }, []); var openChat = useCallback(function () { setState(function (prev) { return (__assign(__assign({}, prev), { isOpen: true })); }); }, []); var closeChat = useCallback(function () { setState(function (prev) { return (__assign(__assign({}, prev), { isOpen: false })); }); }, []); // Add controls for minimizing the chat var minimizeChat = useCallback(function () { setState(function (prev) { return (__assign(__assign({}, prev), { isMinimized: true })); }); }, []); var maximizeChat = useCallback(function () { setState(function (prev) { return (__assign(__assign({}, prev), { isMinimized: false })); }); }, []); return { messages: state.messages, isTyping: state.isTyping, isOpen: state.isOpen, isMinimized: state.isMinimized, availableOptions: state.availableOptions, currentFlow: state.currentFlow, addMessage: addMessage, updateMessage: updateMessage, clearMessages: clearMessages, toggleChat: toggleChat, openChat: openChat, closeChat: closeChat, minimizeChat: minimizeChat, maximizeChat: maximizeChat, startFlow: startFlow, handleButtonClick: handleButtonClick, }; } var ChatContext = createContext(null); var ChatContextProvider = function (_a) { var children = _a.children, _b = _a.initialConfig, initialConfig = _b === void 0 ? {} : _b, _c = _a.initialOptions, initialOptions = _c === void 0 ? {} : _c; var chat = useChat(__assign({ initialMessages: initialConfig.initialMessages || [], persistKey: initialConfig.persistKey, flows: initialConfig.flows || {}, initialFlow: initialConfig.initialFlow, typingDelay: initialConfig.typingIndicatorTimeout || 1000, initiallyOpen: initialConfig.initiallyOpen || false }, initialOptions)); // We need to implement a proper removeMessage function var removeMessage = useCallback(function (id) { // We can use updateMessage to "remove" by filtering out the message in the UI // Real removal would require modifying the useChat hook chat.updateMessage(id, { text: "", type: "system" }); }, [chat]); // Implement setIsTyping to properly change typing state var setIsTyping = useCallback(function (typing) { if (typing === chat.isTyping) return; // No change needed // Since we don't have direct access to setState in useChat, // we'll use a workaround for setting typing state if (typing) { // Simulate typing start if we need to var placeholderMsg = { text: "", type: "bot", isTyping: true, }; // This will trigger the typing indicator indirectly var tempMsg_1 = chat.addMessage(placeholderMsg); // Clean up after a delay - this is optional if (!chat.isTyping) { setTimeout(function () { // Remove the temporary typing message or replace it with an empty message chat.updateMessage(tempMsg_1.id, { text: "", type: "system" }); }, initialConfig.typingIndicatorTimeout || 1000); } } // For turning off typing, we don't need to do anything special // as the typing indicator is controlled by message state in components }, [chat, initialConfig.typingIndicatorTimeout]); // Create a value object that fully matches ChatContextType var contextValue = { messages: chat.messages, isTyping: chat.isTyping, isOpen: chat.isOpen, isMinimized: chat.isMinimized, currentFlow: chat.currentFlow, availableOptions: chat.availableOptions, config: initialConfig, // Methods from useChat addMessage: chat.addMessage, updateMessage: chat.updateMessage, clearMessages: chat.clearMessages, toggleChat: chat.toggleChat, openChat: chat.openChat, closeChat: chat.closeChat, minimizeChat: chat.minimizeChat, maximizeChat: chat.maximizeChat, startFlow: chat.startFlow, handleButtonClick: chat.handleButtonClick, // Our custom implementations removeMessage: removeMessage, setIsOpen: function (open) { if (open) chat.openChat(); else chat.closeChat(); }, setIsMinimized: function (minimized) { if (minimized) chat.minimizeChat(); else chat.maximizeChat(); }, setIsTyping: setIsTyping, }; return (jsx(ChatContext.Provider, { value: contextValue, children: children })); }; var useGlobalChat = function () { var context = useContext(ChatContext); if (!context) { throw new Error("useGlobalChat must be used within a ChatContextProvider"); } return context; }; var ChatHeader = function (_a) { var _b = _a.title, title = _b === void 0 ? "Chat Support" : _b, logo = _a.logo; var _c = useGlobalChat(), minimizeChat = _c.minimizeChat, closeChat = _c.closeChat, isMinimized = _c.isMinimized, maximizeChat = _c.maximizeChat; return (jsxs("div", { className: "flex items-center p-3 bg-blue-500 text-white", role: "heading", "aria-level": 1, children: [logo && jsx("img", { src: logo, alt: "Chat Logo", className: "h-6 w-auto mr-2" }), jsx("h3", { className: "text-base font-semibold flex-1", children: title }), jsxs("div", { className: "flex gap-2", children: [isMinimized ? (jsx("button", { onClick: maximizeChat, className: "p-1 hover:bg-blue-600 rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-300", "aria-label": "Maximize chat", children: jsx("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: jsx("polyline", { points: "5 12 12 5 19 12" }) }) })) : (jsx("button", { onClick: minimizeChat, className: "p-1 hover:bg-blue-600 rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-300", "aria-label": "Minimize chat", children: jsx("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: jsx("polyline", { points: "5 9 12 16 19 9" }) }) })), jsx("button", { onClick: closeChat, className: "p-1 hover:bg-blue-600 rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-300", "aria-label": "Close chat", children: 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: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] })] })); }; /** * Creates a bot response message object */ function createChatbotResponse(text, metadata) { return { text: text, type: "bot", metadata: metadata, }; } /** * Formats a timestamp according to the provided format */ function formatTimestamp(timestamp, format) { if (format === void 0) { format = "HH:mm"; } var date = new Date(timestamp); if (format === "HH:mm") { return "".concat(String(date.getHours()).padStart(2, "0"), ":").concat(String(date.getMinutes()).padStart(2, "0")); } if (format === "HH:mm:ss") { return "".concat(String(date.getHours()).padStart(2, "0"), ":").concat(String(date.getMinutes()).padStart(2, "0"), ":").concat(String(date.getSeconds()).padStart(2, "0")); } return date.toLocaleString(); } var processResponse = function (response, messageText) { return __awaiter(void 0, void 0, void 0, function () { var result; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(typeof response === "function")) return [3 /*break*/, 3]; result = response(messageText); if (!(result instanceof Promise)) return [3 /*break*/, 2]; return [4 /*yield*/, result]; case 1: return [2 /*return*/, _a.sent()]; case 2: return [2 /*return*/, result]; case 3: return [2 /*return*/, response]; } }); }); }; var ChatMessage = function (_a) { var message = _a.message; var isUser = message.type === "user"; return (jsxs("div", { className: "flex flex-col mb-3 max-w-[80%] ".concat(isUser ? "items-end self-end" : "items-start self-start"), role: isUser ? "complementary" : "region", "aria-label": "".concat(isUser ? "User" : "Bot", " message"), children: [jsx("div", { className: "rounded-2xl px-4 py-2 ".concat(isUser ? "bg-blue-500 text-white" : "bg-gray-100 text-gray-800"), children: message.html ? (jsx("div", { dangerouslySetInnerHTML: { __html: message.text }, "aria-live": "polite" })) : (jsx("p", { children: message.text })) }), jsx("div", { className: "text-xs text-gray-400 mt-1", "aria-hidden": "true", children: jsx("time", { dateTime: new Date(message.timestamp).toISOString(), children: formatTimestamp(message.timestamp) }) })] })); }; var ChatButtons = function (_a) { var options = _a.options, onButtonClick = _a.onButtonClick; if (!options || options.length === 0) { return null; } return (jsx("div", { className: "w-full flex flex-wrap gap-2 p-3 border-t border-gray-200", role: "group", "aria-label": "Chat options", children: options.map(function (option) { return (jsx("button", { className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-full text-sm font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500", onClick: function () { return onButtonClick(option); }, type: "button", "aria-label": option.text, children: option.text }, option.id)); }) })); }; var ChatInput = function () { var _a = useGlobalChat(), availableOptions = _a.availableOptions, handleButtonClick = _a.handleButtonClick, isTyping = _a.isTyping; // Don't show options while typing if (isTyping) { return (jsx("div", { className: "border-t border-gray-200 p-4 flex items-center justify-center chat-typing-indicator", "aria-live": "polite", "aria-label": "Bot is typing", children: jsxs("div", { className: "flex space-x-2 items-center", children: [jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full", style: { animationDelay: "0ms" } }), jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full", style: { animationDelay: "150ms" } }), jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full", style: { animationDelay: "300ms" } })] }) })); } return (jsx(ChatButtons, { options: availableOptions, onButtonClick: handleButtonClick })); }; var ChatToggleButton = function (_a) { var _b = _a.config, config = _b === void 0 ? {} : _b; var toggleChat = useGlobalChat().toggleChat; // Style variables based on position var positionClasses = (function () { var _a; var position = ((_a = config === null || config === void 0 ? void 0 : config.position) === null || _a === void 0 ? void 0 : _a.placement) || "bottom-right"; switch (position) { case "top-left": return "top-5 left-5"; case "top-right": return "top-5 right-5"; case "bottom-left": return "top-5 left-5"; case "bottom-right": default: return "bottom-5 right-5"; } })(); return (jsx("button", { onClick: toggleChat, className: "fixed ".concat(positionClasses, " z-50 w-14 h-14 bg-blue-500 hover:bg-blue-600 text-white rounded-full flex items-center justify-center shadow-lg transition-transform duration-300 ease-in-out hover:scale-105 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"), "aria-label": "Toggle chat", children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }) })); }; var Chatbot = function (_a) { var _b = _a.config, config = _b === void 0 ? {} : _b; // Use the global chat context instead of direct hook var _c = useGlobalChat(), isOpen = _c.isOpen, isMinimized = _c.isMinimized, messages = _c.messages; var messagesEndRef = useRef(null); // Auto scroll to bottom when messages change useEffect(function () { if (!config.disableAutoScroll && messagesEndRef.current && !isMinimized) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [messages, isMinimized, config.disableAutoScroll]); if (!isOpen) { return jsx(ChatToggleButton, { config: config }); } // Style variables based on position var positionClasses = (function () { var _a; var position = ((_a = config.position) === null || _a === void 0 ? void 0 : _a.placement) || "bottom-right"; switch (position) { case "top-left": return "top-5 left-5"; case "top-right": return "top-5 right-5"; case "bottom-left": return "bottom-5 left-5"; case "bottom-right": default: return "bottom-5 right-5"; } })(); return (jsxs("div", { className: "fixed ".concat(positionClasses, " z-50 bg-white rounded-lg shadow-xl overflow-hidden flex flex-col aspire-chat-container"), style: { width: config.width || "350px", maxWidth: "90vw", maxHeight: config.maxHeight || "600px", minHeight: isMinimized ? "60px" : "300px", }, role: "dialog", "aria-label": "Chat window", children: [jsx(ChatHeader, { title: config.headerTitle, logo: config.logo }), !isMinimized && (jsxs(Fragment, { children: [jsxs("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-3", role: "log", "aria-live": "polite", children: [messages.map(function (message) { return (jsx(ChatMessage, { message: message }, message.id)); }), jsx("div", { ref: messagesEndRef })] }), jsx(ChatInput, {})] }))] })); }; var defaultTheme = { // Main colors primary: "#4F46E5", // Indigo-600 secondary: "#6366F1", // Indigo-500 background: "#FFFFFF", textPrimary: "#1F2937", // Gray-800 textSecondary: "#4B5563", // Gray-600 // Message bubbles userBubbleBackground: "#4F46E5", // Indigo-600 userBubbleText: "#FFFFFF", botBubbleBackground: "#F3F4F6", // Gray-100 botBubbleText: "#1F2937", // Gray-800 systemMessageColor: "#9CA3AF", // Gray-400 // Header headerBackground: "#4F46E5", // Indigo-600 headerText: "#FFFFFF", // Input inputBackground: "#FFFFFF", inputFieldBackground: "#F9FAFB", // Gray-50 inputTextColor: "#1F2937", // Gray-800 inputBorderColor: "#E5E7EB", // Gray-200 // Misc borderColor: "#E5E7EB", // Gray-200 timestampColor: "#9CA3AF", // Gray-400 errorColor: "#EF4444", // Red-500 successColor: "#10B981", // Green-500 // Typography fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif', fontSize: "14px", fontSizeSmall: "12px", fontSizeLarge: "16px", // Spacing spacing: { unit: 4, tiny: "4px", small: "8px", medium: "16px", large: "24px", extraLarge: "32px", }, // Border radius borderRadius: { small: "4px", medium: "8px", large: "16px", circle: "50%", }, // Box shadow boxShadow: { light: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)", medium: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)", heavy: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)", }, // Animations animations: { transitionDuration: "200ms", easing: "ease-in-out", }, }; var darkTheme = __assign(__assign({}, defaultTheme), { // Main colors primary: "#6366F1", secondary: "#818CF8", background: "#1F2937", textPrimary: "#F9FAFB", textSecondary: "#E5E7EB", // Message bubbles userBubbleBackground: "#6366F1", userBubbleText: "#FFFFFF", botBubbleBackground: "#374151", botBubbleText: "#F9FAFB", // Header headerBackground: "#6366F1", headerText: "#FFFFFF", // Input inputBackground: "#1F2937", inputFieldBackground: "#374151", inputTextColor: "#F9FAFB", inputBorderColor: "#4B5563", // Misc borderColor: "#4B5563", timestampColor: "#9CA3AF" }); export { ChatButtons, ChatContextProvider, ChatHeader, ChatInput, ChatMessage, ChatToggleButton, Chatbot, createChatbotResponse, darkTheme, defaultTheme, formatTimestamp, processResponse, useChat, useGlobalChat }; //# sourceMappingURL=index.esm.js.map