UNPKG

sandia-chatbot-widget

Version:

Production-ready Sandia AI Chatbot Widget v3.0 with advanced themes, animations, notifications, and enterprise features

858 lines (845 loc) 78.5 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); /****************************************************************************** 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, Iterator */ 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 = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["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; }; var SandiaWidget = function (_a) { var _b, _c, _d, _e; var chatbotId = _a.chatbotId, shareToken = _a.shareToken, apiUrl = _a.apiUrl, _f = _a.styles, styles = _f === void 0 ? {} : _f, _g = _a.chatConfig, chatConfig = _g === void 0 ? {} : _g, onReady = _a.onReady, onOpen = _a.onOpen, onClose = _a.onClose, onMinimize = _a.onMinimize, onRestore = _a.onRestore, onMessage = _a.onMessage, onTyping = _a.onTyping, onError = _a.onError, onSubscriptionError = _a.onSubscriptionError, onFileUpload = _a.onFileUpload, onVoiceStart = _a.onVoiceStart, onVoiceEnd = _a.onVoiceEnd, metadata = _a.metadata, _h = _a.debug, debug = _h === void 0 ? false : _h, _j = _a.version, version = _j === void 0 ? '3.0' : _j, // Legacy props position = _a.position, primaryColor = _a.primaryColor, size = _a.size; var initialized = react.useRef(false); var _k = react.useState(null), subscriptionError = _k[0], setSubscriptionError = _k[1]; var _l = react.useState(false), isClient = _l[0], setIsClient = _l[1]; // Default API URL to Sandia's service var finalApiUrl = apiUrl || 'https://www.sandia.chat'; // Merge legacy props with new styles for backward compatibility var mergedStyles = __assign(__assign(__assign(__assign({ // v3.0 defaults position: 'bottom-right', primaryColor: '#006FEE', backgroundColor: '#ffffff', textColor: '#000000', borderRadius: 50, size: 'md', theme: 'modern', shadowStyle: 'soft', animation: 'smooth', showPulse: true, showBadge: true, customIcon: '💬', greeting: 'Hi! How can I help you today?', darkMode: false, showAvatar: true, showTimestamps: true, soundEnabled: true, notificationsEnabled: true, allowFileUpload: true, showTypingIndicator: true, messagePersistence: true, maxMessages: 100, autoMinimize: 5, brandingEnabled: true, mobileFullScreen: true }, styles), (position && { position: position })), (primaryColor && { primaryColor: primaryColor })), (size && { size: size })); // Enhanced chat configuration with defaults var mergedChatConfig = __assign({ quickReplies: ['Hello', 'How can you help?', 'Tell me more'], voiceEnabled: false, voiceLanguage: 'en-US', allowedFileTypes: ['image/*', '.pdf', '.txt', '.doc', '.docx'], maxFileSize: 10, messagesPerMinute: 10, trackingEnabled: true }, chatConfig); // Detect client-side rendering react.useEffect(function () { setIsClient(true); }, []); react.useEffect(function () { // Only run on client-side if (!isClient || initialized.current) return; initialized.current = true; // Validate access before initializing validateAccess(); }, [chatbotId, finalApiUrl, shareToken, isClient]); var validateAccess = function () { return __awaiter(void 0, void 0, void 0, function () { var headers, response, error, error_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 7, , 8]); headers = { 'Content-Type': 'application/json' }; if (shareToken) { headers['x-share-token'] = shareToken; } // Add version header for API compatibility if (version) { headers['x-widget-version'] = version; } return [4 /*yield*/, fetch("".concat(finalApiUrl, "/api/chatbot/public/").concat(chatbotId, "/config"), { method: 'GET', headers: headers, mode: 'cors', // Explicitly enable CORS credentials: 'omit' // Don't send credentials for public API })]; case 1: response = _b.sent(); if (!!response.ok) return [3 /*break*/, 6]; error = void 0; _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); return [4 /*yield*/, response.json()]; case 3: error = _b.sent(); return [3 /*break*/, 5]; case 4: _b.sent(); error = { message: 'Network error' }; return [3 /*break*/, 5]; case 5: if (response.status === 402) { // Subscription error setSubscriptionError(error.message || 'This chatbot has reached its usage limits.'); if (onSubscriptionError) { onSubscriptionError({ message: error.message || 'Subscription limit reached', upgradeRequired: error.upgradeRequired }); } return [2 /*return*/]; } if (response.status === 403 && !shareToken) { // If access denied and no share token provided, show a helpful error console.warn('[Sandia Widget] Chatbot requires authentication or is not public. Consider providing a shareToken.'); setSubscriptionError('This chatbot requires authentication. Please contact the owner for access.'); return [2 /*return*/]; } throw new Error(error.message || "HTTP ".concat(response.status, ": ").concat(response.statusText)); case 6: // Access validated, proceed with initialization initializeChatbot(); return [3 /*break*/, 8]; case 7: error_1 = _b.sent(); console.error('[Sandia Widget] Access validation failed:', error_1); // Handle CORS and network errors gracefully if (error_1 instanceof TypeError && error_1.message.includes('fetch')) { console.warn('[Sandia Widget] Network error - possibly CORS related. Falling back to basic initialization.'); // Try to initialize anyway for development/testing initializeChatbot(); return [2 /*return*/]; } // For other errors, show user-friendly message setSubscriptionError('Unable to connect to chatbot service. Please try again later.'); if (onError) { onError(error_1); } return [3 /*break*/, 8]; case 8: return [2 /*return*/]; } }); }); }; var initializeChatbot = function () { // Configure the enhanced global chatbot object with v3.0 features window.SandiaChatbot = { chatbotId: chatbotId, shareToken: shareToken, apiUrl: finalApiUrl, version: version, styles: mergedStyles, chatConfig: mergedChatConfig, // Enhanced event handlers onReady: function () { if (debug) console.log('[Sandia Widget v3.0] Chatbot ready'); if (onReady) onReady(); }, onOpen: function () { if (debug) console.log('[Sandia Widget v3.0] Chatbot opened'); if (onOpen) onOpen(); }, onClose: function () { if (debug) console.log('[Sandia Widget v3.0] Chatbot closed'); if (onClose) onClose(); }, onMinimize: function () { if (debug) console.log('[Sandia Widget v3.0] Chatbot minimized'); if (onMinimize) onMinimize(); }, onRestore: function () { if (debug) console.log('[Sandia Widget v3.0] Chatbot restored'); if (onRestore) onRestore(); }, onMessage: function (message) { var enhancedMessage = __assign(__assign({}, message), { timestamp: message.timestamp || new Date() }); if (debug) console.log('[Sandia Widget v3.0] Message:', enhancedMessage); if (onMessage) onMessage(enhancedMessage); }, onTyping: function (isTyping) { if (debug) console.log('[Sandia Widget v3.0] Typing:', isTyping); if (onTyping) onTyping(isTyping); }, onError: function (error) { if (debug) console.error('[Sandia Widget v3.0] Error:', error); if (onError) onError(error); }, onSubscriptionError: function (error) { if (debug) console.warn('[Sandia Widget v3.0] Subscription Error:', error); if (onSubscriptionError) onSubscriptionError(error); }, onFileUpload: function (file) { if (debug) console.log('[Sandia Widget v3.0] File Upload:', file.name); if (onFileUpload) onFileUpload(file); }, onVoiceStart: function () { if (debug) console.log('[Sandia Widget v3.0] Voice recording started'); if (onVoiceStart) onVoiceStart(); }, onVoiceEnd: function (transcript) { if (debug) console.log('[Sandia Widget v3.0] Voice recording ended:', transcript); if (onVoiceEnd) onVoiceEnd(transcript); }, metadata: metadata, debug: debug, }; // Load the enhanced embed script from Sandia's CDN var script = document.createElement('script'); script.src = "".concat(finalApiUrl, "/embed.js?v=").concat(version); script.async = true; script.onload = function () { if (debug) console.log('[Sandia Widget v3.0] Enhanced embed script loaded'); }; script.onerror = function (error) { console.error('[Sandia Widget v3.0] Failed to load embed script:', error); if (onError) onError(new Error('Failed to load chatbot')); }; document.head.appendChild(script); return function () { // Enhanced cleanup if (script.parentNode) { document.head.removeChild(script); } delete window.SandiaChatbot; // Clear any persistent data if needed if (mergedStyles.messagePersistence === false) { try { localStorage.removeItem("sandia-chatbot-".concat(chatbotId)); } catch (e) { // Ignore localStorage errors } } }; }; // Don't render anything on server-side if (!isClient) { return null; } // Enhanced subscription error state with v3.0 styling if (subscriptionError) { return (jsxRuntime.jsx("div", { style: { position: 'fixed', bottom: ((_b = mergedStyles.position) === null || _b === void 0 ? void 0 : _b.includes('bottom')) ? '24px' : 'auto', top: ((_c = mergedStyles.position) === null || _c === void 0 ? void 0 : _c.includes('top')) ? '24px' : 'auto', right: ((_d = mergedStyles.position) === null || _d === void 0 ? void 0 : _d.includes('right')) ? '24px' : 'auto', left: ((_e = mergedStyles.position) === null || _e === void 0 ? void 0 : _e.includes('left')) ? '24px' : 'auto', width: '60px', height: '60px', background: mergedStyles.darkMode ? '#374151' : '#6b7280', borderRadius: "".concat(mergedStyles.borderRadius, "%"), display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: mergedStyles.shadowStyle === 'hard' ? '0 4px 12px rgba(0,0,0,0.3)' : mergedStyles.shadowStyle === 'glow' ? "0 0 20px ".concat(mergedStyles.primaryColor, "40") : mergedStyles.shadowStyle === 'none' ? 'none' : '0 4px 20px rgba(0,0,0,0.15)', // soft shadow (default) fontSize: '24px', opacity: 0.6, cursor: 'not-allowed', zIndex: 9999, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', transition: 'all 0.3s ease', animation: mergedStyles.animation === 'bouncy' ? 'pulse 2s infinite' : mergedStyles.animation === 'fade' ? 'fadeIn 0.5s ease' : 'none' }, children: "\u26A0\uFE0F" })); } return null; // Widget is handled by the enhanced embed script }; var DEFAULT_STYLES = { position: 'bottom-right', primaryColor: '#006FEE', backgroundColor: '#ffffff', textColor: '#000000', borderRadius: 50, size: 'md', theme: 'modern', shadowStyle: 'soft', animation: 'smooth', showPulse: true, showBadge: true, customIcon: '💬', greeting: 'Hi! How can I help you today?', darkMode: false, showAvatar: true, showTimestamps: true, soundEnabled: true, notificationsEnabled: true, allowFileUpload: true, showTypingIndicator: true, messagePersistence: true, maxMessages: 100, autoMinimize: 5, brandingEnabled: true, mobileFullScreen: true }; var DEFAULT_CHAT_CONFIG = { quickReplies: ['Hello', 'How can you help?', 'Tell me more'], voiceEnabled: false, voiceLanguage: 'en-US', allowedFileTypes: ['image/*', '.pdf', '.txt', '.doc', '.docx'], maxFileSize: 10, messagesPerMinute: 10, trackingEnabled: true }; var SandiaVanillaChatbot = /** @class */ (function () { function SandiaVanillaChatbot(config) { this.container = null; this.isOpen = false; this.isMinimized = false; this.messages = []; this.isLoading = false; this.conversationId = null; this.unreadCount = 0; this.lastActivity = Date.now(); this.typingTimeout = null; this.autoMinimizeTimeout = null; this.notificationPermission = 'default'; // Sound effects (data URLs for production use) this.sounds = { messageReceived: 'data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBT2a3Aw==', messageClick: 'data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBT2Z3Aw==' }; this.chatbotId = config.chatbotId; this.shareToken = config.shareToken; this.apiUrl = config.apiUrl || 'https://www.sandia.chat'; this.styles = __assign(__assign({}, DEFAULT_STYLES), config.styles); this.chatConfig = __assign(__assign({}, DEFAULT_CHAT_CONFIG), config.chatConfig); this.debug = config.debug || false; this.version = config.version || '3.0'; this.metadata = config.metadata; // Bind event handlers this.onReady = config.onReady; this.onOpen = config.onOpen; this.onClose = config.onClose; this.onMinimize = config.onMinimize; this.onRestore = config.onRestore; this.onMessage = config.onMessage; this.onTyping = config.onTyping; this.onError = config.onError; this.onSubscriptionError = config.onSubscriptionError; this.onFileUpload = config.onFileUpload; this.onVoiceStart = config.onVoiceStart; this.onVoiceEnd = config.onVoiceEnd; this.init(); } SandiaVanillaChatbot.prototype.init = function () { return __awaiter(this, void 0, void 0, function () { var _a, hasAccess, error_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 4, , 5]); if (!(this.styles.notificationsEnabled && 'Notification' in window)) return [3 /*break*/, 2]; _a = this; return [4 /*yield*/, Notification.requestPermission()]; case 1: _a.notificationPermission = _b.sent(); _b.label = 2; case 2: // Load message history if (this.styles.messagePersistence) { this.loadMessageHistory(); } return [4 /*yield*/, this.validateAccess()]; case 3: hasAccess = _b.sent(); if (!hasAccess) { this.renderBlockedState(); return [2 /*return*/]; } // Create widget this.createWidget(); this.addEnhancedStyles(); // Setup auto-minimize if (this.styles.autoMinimize && this.styles.autoMinimize > 0) { this.setupAutoMinimize(); } // Expose API this.exposeAPI(); if (this.onReady) this.onReady(); if (this.debug) console.log('[Sandia Widget v3.0] Initialized successfully'); return [3 /*break*/, 5]; case 4: error_1 = _b.sent(); console.error('[Sandia Widget v3.0] Initialization failed:', error_1); if (this.onError) this.onError(error_1); return [3 /*break*/, 5]; case 5: return [2 /*return*/]; } }); }); }; SandiaVanillaChatbot.prototype.validateAccess = function () { return __awaiter(this, void 0, void 0, function () { var headers, response, error, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 4, , 5]); headers = { 'Content-Type': 'application/json' }; if (this.shareToken) { headers['x-share-token'] = this.shareToken; } if (this.version) { headers['x-widget-version'] = this.version; } return [4 /*yield*/, fetch("".concat(this.apiUrl, "/api/chatbot/public/").concat(this.chatbotId, "/config"), { method: 'GET', headers: headers })]; case 1: response = _a.sent(); if (!!response.ok) return [3 /*break*/, 3]; return [4 /*yield*/, response.json()]; case 2: error = _a.sent(); if (response.status === 402) { this.handleSubscriptionError(error); return [2 /*return*/, false]; } if (response.status === 403 && !this.shareToken) { console.warn('[Sandia Widget v3.0] Chatbot may require authentication or is not public.'); return [2 /*return*/, true]; // Still try to render for testing } throw new Error(error.message || 'Access denied'); case 3: return [2 /*return*/, true]; case 4: error_2 = _a.sent(); console.error('[Sandia Widget v3.0] Access validation failed:', error_2); return [2 /*return*/, true]; // Still try to render for development/testing case 5: return [2 /*return*/]; } }); }); }; SandiaVanillaChatbot.prototype.handleSubscriptionError = function (error) { console.warn('[Sandia Widget v3.0] Subscription limit reached:', error); if (this.onSubscriptionError) { this.onSubscriptionError({ message: error.message || 'Subscription limit reached', upgradeRequired: error.upgradeRequired }); } this.showSubscriptionNotification(error); }; SandiaVanillaChatbot.prototype.showSubscriptionNotification = function (error) { var existing = document.getElementById('sandia-subscription-notification'); if (existing) existing.remove(); var notification = document.createElement('div'); notification.id = 'sandia-subscription-notification'; var position = this.styles.position || 'bottom-right'; notification.style.cssText = "\n position: fixed;\n ".concat(position.includes('bottom') ? 'bottom: 20px' : 'top: 20px', ";\n ").concat(position.includes('right') ? 'right: 20px' : 'left: 20px', ";\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n color: white;\n padding: 16px 20px;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.1);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n max-width: 320px;\n z-index: 10001;\n cursor: pointer;\n transition: all 0.3s ease;\n animation: sandia-notification-in 0.5s ease;\n "); notification.innerHTML = "\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <div style=\"\n width: 40px;\n height: 40px;\n background: rgba(255,255,255,0.2);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n \">\u26A0\uFE0F</div>\n <div style=\"flex: 1;\">\n <div style=\"font-weight: 600; margin-bottom: 4px;\">\n Chatbot Temporarily Unavailable\n </div>\n <div style=\"font-size: 12px; opacity: 0.9; line-height: 1.4;\">\n ".concat(error.message || 'This chatbot has reached its usage limits. Please try again later.', "\n </div>\n </div>\n </div>\n <div style=\"\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid rgba(255,255,255,0.2);\n font-size: 11px;\n opacity: 0.8;\n text-align: center;\n \">\n Powered by Sandia AI\n </div>\n "); setTimeout(function () { if (notification.parentNode) { notification.remove(); } }, 10000); notification.addEventListener('click', function () { notification.remove(); }); document.body.appendChild(notification); }; SandiaVanillaChatbot.prototype.createWidget = function () { var _this = this; this.container = document.createElement('div'); this.container.id = 'sandia-chatbot-widget-v3'; this.container.className = 'sandia-widget-container'; this.container.style.cssText = "\n position: fixed;\n z-index: 9999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n color: ".concat(this.styles.textColor, ";\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n "); this.setPosition(); this.createChatBubble(); this.createChatWindow(); document.body.appendChild(this.container); // Trigger initial animation requestAnimationFrame(function () { var _a; (_a = _this.container) === null || _a === void 0 ? void 0 : _a.classList.add('sandia-widget-loaded'); }); }; SandiaVanillaChatbot.prototype.createChatBubble = function () { var _this = this; var _a; var bubbleSize = this.getBubbleSize(); var themeStyles = this.getThemeStyles(); var bubble = document.createElement('div'); bubble.id = 'sandia-chat-bubble'; bubble.className = "sandia-chat-bubble sandia-theme-".concat(this.styles.theme, " sandia-shadow-").concat(this.styles.shadowStyle); bubble.style.cssText = "\n width: ".concat(bubbleSize.width, ";\n height: ").concat(bubbleSize.height, ";\n border-radius: ").concat(this.styles.borderRadius, "%;\n background: ").concat(themeStyles.background, ";\n color: white;\n border: ").concat(themeStyles.border, ";\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: ").concat(this.getShadowStyle(), ";\n transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n position: relative;\n overflow: hidden;\n outline: none;\n font-size: ").concat(bubbleSize.fontSize, ";\n will-change: transform;\n "); // Add ARIA attributes bubble.setAttribute('role', 'button'); bubble.setAttribute('tabindex', '0'); bubble.setAttribute('aria-label', 'Open chat assistant'); // Create pulse rings for animation if (this.styles.showPulse && this.styles.animation !== 'none') { this.createPulseRings(bubble); } // Create bubble icon/avatar var icon = document.createElement('div'); icon.className = 'sandia-bubble-icon'; icon.style.cssText = "\n position: relative;\n z-index: 2;\n transition: transform 0.3s ease;\n "; if (this.styles.showAvatar && this.styles.customAvatar) { var avatar = document.createElement('img'); avatar.src = this.styles.customAvatar; avatar.style.cssText = "\n width: ".concat(parseInt(bubbleSize.width) * 0.6, "px;\n height: ").concat(parseInt(bubbleSize.height) * 0.6, "px;\n border-radius: 50%;\n object-fit: cover;\n "); icon.appendChild(avatar); } else { icon.textContent = this.styles.customIcon || '💬'; } bubble.appendChild(icon); // Create badge for unread count if (this.styles.showBadge) { var badge = document.createElement('div'); badge.id = 'sandia-unread-badge'; badge.className = 'sandia-badge'; badge.style.cssText = "\n position: absolute;\n top: -6px;\n right: -6px;\n background: #ef4444;\n color: white;\n border-radius: 50%;\n min-width: 20px;\n height: 20px;\n display: none;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n font-weight: 600;\n border: 2px solid ".concat(this.styles.backgroundColor, ";\n animation: sandia-badge-initial 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);\n "); bubble.appendChild(badge); } // Add event listeners bubble.addEventListener('click', function () { return _this.toggleChat(); }); bubble.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _this.toggleChat(); } }); // Hover effects bubble.addEventListener('mouseenter', function () { if (_this.styles.animation !== 'none') { icon.style.transform = 'scale(1.1) rotate(5deg)'; } }); bubble.addEventListener('mouseleave', function () { icon.style.transform = 'scale(1) rotate(0deg)'; }); (_a = this.container) === null || _a === void 0 ? void 0 : _a.appendChild(bubble); }; SandiaVanillaChatbot.prototype.createChatWindow = function () { var _a; var windowSize = this.getWindowSize(); this.getThemeStyles(); var window = document.createElement('div'); window.id = 'sandia-chat-window'; window.className = "sandia-chat-window sandia-theme-".concat(this.styles.theme); window.style.cssText = "\n position: absolute;\n ".concat(this.getChatWindowPosition(), ";\n width: ").concat(windowSize.width, ";\n height: ").concat(windowSize.height, ";\n background: ").concat(this.styles.backgroundColor, ";\n border-radius: 16px;\n box-shadow: ").concat(this.getShadowStyle(), ";\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: ").concat(this.styles.darkMode ? '1px solid #374151' : '1px solid #e5e7eb', ";\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n "); // Create header this.createChatHeader(window); // Create messages container this.createMessagesContainer(window); // Create input container this.createInputContainer(window); (_a = this.container) === null || _a === void 0 ? void 0 : _a.appendChild(window); }; SandiaVanillaChatbot.prototype.createChatHeader = function (window) { var _this = this; var header = document.createElement('div'); header.className = 'sandia-chat-header'; header.style.cssText = "\n padding: 20px;\n background: linear-gradient(135deg, ".concat(this.styles.primaryColor, " 0%, ").concat(this.adjustColor(this.styles.primaryColor, -20), " 100%);\n color: white;\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: relative;\n overflow: hidden;\n "); // Header content var headerContent = document.createElement('div'); headerContent.style.cssText = "\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n "; // Avatar/Icon if (this.styles.showAvatar) { var avatar = document.createElement('div'); avatar.style.cssText = "\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.2);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n "; avatar.textContent = this.styles.customIcon || '💬'; headerContent.appendChild(avatar); } // Title and status var titleContainer = document.createElement('div'); titleContainer.innerHTML = "\n <h3 style=\"margin: 0; font-size: 16px; font-weight: 600;\">Chat Assistant</h3>\n <div style=\"display: flex; align-items: center; gap: 6px; margin-top: 2px;\">\n <div style=\"\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #10b981;\n animation: sandia-online-pulse 2s infinite;\n \"></div>\n <span style=\"font-size: 12px; opacity: 0.9;\">Online now</span>\n </div>\n "; headerContent.appendChild(titleContainer); header.appendChild(headerContent); // Header buttons var headerButtons = document.createElement('div'); headerButtons.style.cssText = "\n display: flex;\n gap: 8px;\n "; // Minimize button var minimizeBtn = this.createHeaderButton('−', 'Minimize chat', function () { return _this.minimizeChat(); }); headerButtons.appendChild(minimizeBtn); // Close button var closeBtn = this.createHeaderButton('✕', 'Close chat', function () { return _this.closeChat(); }); headerButtons.appendChild(closeBtn); header.appendChild(headerButtons); window.appendChild(header); }; SandiaVanillaChatbot.prototype.createHeaderButton = function (icon, label, onClick) { var button = document.createElement('button'); button.style.cssText = "\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(255, 255, 255, 0.1);\n color: white;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n transition: all 0.2s ease;\n outline: none;\n "; button.textContent = icon; button.setAttribute('aria-label', label); button.addEventListener('click', onClick); button.addEventListener('mouseenter', function () { button.style.background = 'rgba(255, 255, 255, 0.2)'; }); button.addEventListener('mouseleave', function () { button.style.background = 'rgba(255, 255, 255, 0.1)'; }); return button; }; SandiaVanillaChatbot.prototype.createMessagesContainer = function (window) { var _this = this; var container = document.createElement('div'); container.id = 'sandia-messages-container'; container.className = 'sandia-scroll-container'; container.style.cssText = "\n flex: 1;\n padding: 20px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 16px;\n background: ".concat(this.styles.darkMode ? 'linear-gradient(to bottom, #1f2937 0%, #111827 100%)' : 'linear-gradient(to bottom, #f9fafb 0%, #ffffff 100%)', ";\n "); // Add scroll listener for last seen tracking container.addEventListener('scroll', function () { _this.lastActivity = Date.now(); }); // Show welcome message if no messages if (this.messages.length === 0) { this.showWelcomeMessage(container); } else { this.renderMessages(container); } window.appendChild(container); }; SandiaVanillaChatbot.prototype.showWelcomeMessage = function (container) { var _this = this; var welcome = document.createElement('div'); welcome.style.cssText = "\n text-align: center;\n padding: 40px 20px;\n color: ".concat(this.styles.darkMode ? '#9ca3af' : '#6b7280', ";\n "); welcome.innerHTML = "\n <div style=\"\n width: 80px;\n height: 80px;\n margin: 0 auto 16px;\n border-radius: 50%;\n background: linear-gradient(135deg, ".concat(this.styles.primaryColor, " 0%, ").concat(this.adjustColor(this.styles.primaryColor, -20), " 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n color: white;\n box-shadow: 0 8px 32px ").concat(this.styles.primaryColor, "40;\n \">\n ").concat(this.styles.customIcon, "\n </div>\n <h4 style=\"margin: 0 0 8px; font-size: 18px; font-weight: 600; color: ").concat(this.styles.textColor, ";\">\n Welcome to Chat Assistant\n </h4>\n <p style=\"margin: 0 0 20px; font-size: 14px; line-height: 1.5;\">\n ").concat(this.styles.greeting, "\n </p>\n "); // Add quick actions if (this.chatConfig.quickReplies && this.chatConfig.quickReplies.length > 0) { var quickActions_1 = document.createElement('div'); quickActions_1.style.cssText = "\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n "; this.chatConfig.quickReplies.slice(0, 3).forEach(function (reply) { var button = document.createElement('button'); button.style.cssText = "\n padding: 8px 16px;\n background: ".concat(_this.styles.primaryColor, ";\n color: white;\n border: none;\n border-radius: 20px;\n cursor: pointer;\n font-size: 12px;\n transition: all 0.2s ease;\n outline: none;\n "); button.textContent = reply; button.addEventListener('click', function () { return _this.sendQuickReply(reply); }); quickActions_1.appendChild(button); }); welcome.appendChild(quickActions_1); } container.appendChild(welcome); }; SandiaVanillaChatbot.prototype.createInputContainer = function (window) { var _this = this; var container = document.createElement('div'); container.className = 'sandia-input-container'; container.style.cssText = "\n padding: 20px;\n border-top: 1px solid ".concat(this.styles.darkMode ? '#374151' : '#e5e7eb', ";\n background: ").concat(this.styles.backgroundColor, ";\n "); // Create input wrapper var inputWrapper = document.createElement('div'); inputWrapper.style.cssText = "\n display: flex;\n align-items: end;\n gap: 12px;\n position: relative;\n "; // Create textarea var textarea = document.createElement('textarea'); textarea.id = 'sandia-message-input'; textarea.placeholder = 'Type your message...'; textarea.style.cssText = "\n flex: 1;\n min-height: 44px;\n max-height: 120px;\n padding: 12px 16px;\n border: 2px solid ".concat(this.styles.darkMode ? '#374151' : '#e5e7eb', ";\n border-radius: 24px;\n resize: none;\n outline: none;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.4;\n background: ").concat(this.styles.darkMode ? '#1f2937' : '#ffffff', ";\n color: ").concat(this.styles.textColor, ";\n transition: all 0.2s ease;\n "); // Auto-resize functionality textarea.addEventListener('input', function () { textarea.style.height = 'auto'; textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px'; // Trigger typing indicator if (_this.onTyping) _this.onTyping(true); if (_this.typingTimeout) clearTimeout(_this.typingTimeout); _this.typingTimeout = setTimeout(function () { if (_this.onTyping) _this.onTyping(false); }, 1000); }); // Focus styles textarea.addEventListener('focus', function () { textarea.style.borderColor = _this.styles.primaryColor; }); textarea.addEventListener('blur', function () { textarea.style.borderColor = _this.styles.darkMode ? '#374151' : '#e5e7eb'; }); // Send on Enter (not Shift+Enter) textarea.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); _this.sendMessage(); } }); inputWrapper.appendChild(textarea); // Create send button var sendButton = document.createElement('button'); sendButton.id = 'sandia-send-button'; sendButton.style.cssText = "\n width: 44px;\n height: 44px;\n border: none;\n border-radius: 50%;\n background: ".concat(this.styles.primaryColor, ";\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n transition: all 0.2s ease;\n outline: none;\n box-shadow: 0 2px 8px ").concat(this.styles.primaryColor, "40;\n "); sendButton.innerHTML = '➤'; sendButton.setAttribute('aria-label', 'Send message'); sendButton.addEventListener('click', function () { return _this.sendMessage(); }); // Send button hover effects sendButton.addEventListener('mouseenter', function () { sendButton.style.transform = 'scale(1.05)'; sendButton.style.boxShadow = "0 4px 16px ".concat(_this.styles.primaryColor, "60"); }); sendButton.addEventListener('mouseleave', function () { sendButton.style.transform = 'scale(1)'; sendButton.style.boxShadow = "0 2px 8px ".concat(_this.styles.primaryColor, "40"); }); inputWrapper.appendChild(sendButton); container.appendChild(inputWrapper); // Add "Powered by Sandia" footer if branding enabled if (this.styles.brandingEnabled) { var footer = document.createElement('div'); footer.style.cssText = "\n margin-top: 12px;\n text-align: center;\n font-size: 11px;\n color: ".concat(this.styles.darkMode ? '#6b7280' : '#9ca3af', ";\n opacity: 0.8;\n "); footer.innerHTML = "\n <a href=\"https://sandia.chat\" target=\"_blank\" style=\"\n color: inherit;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n \">\n <span>\u2728</span>\n Powered by Sandia AI\n </a>\n "; container.appendChild(footer); } window.appendChild(container); }; // Continue with remaining methods... SandiaVanillaChatbot.prototype.sendMessage = function () { return __awaiter(this, void 0, void 0, function () { var input, content, headers, requestBody, response, errorData, data, error_3; return __generator(this, function (_a) { switch (_a.label) { case 0: input = document.getElementById('sandia-message-input'); if (!input || this.isLoading) return [2 /*return*/]; content = input.value.trim(); if (!content) return [2 /*return*/]; this.isLoading = true; this.lastActivity = Date.now(); // Clear typing timeout if (this.typingTimeout) { clearTimeout(this.typingTimeout); if (this.onTyping) this.onTyping(false); } // Add user message this.addMessage(content, 'user'); input.value = ''; input.style.height = 'auto'; // Show typing indicator if (this.styles.showTypingIndicator) { this.showTypingIndicator(); } _a.label = 1; case 1: _a.trys.push([1, 6, 7, 8]); headers = { 'Content-Type': 'application/json', }; if (this.shareToken) { headers['x-share-token'] = this.shareToken; } if (this.version) { headers['x-widget-version'] = this.version; } requestBody = { message: content, timestamp: new Date().toISOString(), metadata: __assign({ conversationId: this.conversationId, userAgent: navigator.userAgent }, this.metadata) }; return [4 /*yield*/, fetch("".concat(this.apiUrl, "/api/chatbot/public/").concat(this.chatbotId), { method: 'POST', headers: headers, body: JSON.stringify(requestBody) })]; case 2: response = _a.sent(); this.hideTypingIndicator(); if (!!response.ok) return [3 /*break*/, 4]; return [4 /*yield*/, response.json()]; case 3: errorData = _a.sent(); if (response.status === 402) { this.handleSubscriptionError(errorData); return [2 /*return*/]; }