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
JavaScript
'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*/];
}