sandia-chatbot-widget
Version:
Production-ready Sandia AI Chatbot Widget v3.0 with advanced themes, animations, notifications, and enterprise features
1,382 lines • 78.4 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { useRef, useState, useEffect } from '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 = useRef(false);
var _k = useState(null), subscriptionError = _k[0], setSubscriptionError = _k[1];
var _l = 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
useEffect(function () {
setIsClient(true);
}, []);
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 (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*/];
}
else if (response.status === 429) {
this.addMessage(errorData.message || 'Rate limit exceeded. Please wait before sending another message.', 'assistant', true);
return [2 /*return*/];
}
else if (response.status === 403) {
this.addMessage(errorData.message || 'Access denied. This chatbot may be restricted.', 'assistant', true);
return [2 /*return*/];
}
throw new Error(errorData.message || "HTTP error! status: ".concat(response.status));
case 4: return [4 /*yield*/, response.json()];
case 5:
data = _a.sent();
// Update conversation ID if provided
if (data.conversationId) {
this.conversationId = data.conversationId;
}
// Add bot response
this.addMessage(data.response || data.message || 'Sorry, I could not process your request.', 'assistant');
// Play notification sound
if (this.styles.soundEnabled) {
this.playSound('messageReceived');
}
// Show notification if chat is closed
if (!this.isOpen && this.styles.notificationsEnabled) {
this.showNotification('New message', data.response || data.message);
this.updateBadge(this.unreadCount + 1);
}
return [3 /*break*/, 8];
case 6:
error_3 = _a.sent();
this.hideTypingIndicator();
console.error('[Sandia Widget v3.0] Message sending failed:', error_3);
this.addMessage('Sorry, I encountered an error. Please try again.', 'assistant', true);
if (this.onError) {
this.onError(error_3);
}
return [3 /*break*/, 8];
case 7:
this.isLoading = false;
return [7 /*endfinally*/];
case 8: return [2 /*return*/];
}
});
});
};
SandiaVanillaChatbot.prototype.addMessage = function (content, role, isError) {
var message = {
id: Date.now().toString(),
content: content,
role: role,
timestamp: new Date()
};
this.messages.push(message);
// Limit message history
if (this.styles.maxMessages && this.messages.length > this.styles.maxMessages) {
this.messages = this.messages.slice(-this.styles.maxMessages);
}
// Save to localStorage if persistence enabled
if (this.styles.messagePersistence) {
this.saveMessageHistory();
}
// Render message
var container = document.getElementById('sandia-messages-container');
if (container) {
// Remove welcome message if exists
var welcome = container.querySelector('[style*="text-align: center"]');
if (welcome)
welcome.remove();
var messageElement = this.createMessage(message, isError);
container.appendChild(messageElement);
this.scrollToBottom();
}
// Trigger callback
if (this.onMessage) {
this.onMessage({
content: message.content,
role: message.role,
timestamp: message.timestamp
});
}
};
SandiaVanillaChatbot.prototype.createMessage = function (message, isError) {
var _this = this;
var isUser = message.role === 'user';
var messageDiv = document.createElement('div');
messageDiv.className = "sandia-message sandia-message-".concat(isUser ? 'user' : 'assistant');
messageDiv.style.cssText = "\n display: flex;\n ".concat(isUser ? 'justify-content: flex-end' : 'justify-content: flex-start', ";\n margin-bottom: ").concat(this.styles.showTimestamps ? '8px' : '4px', ";\n animation: sandia-message-in 0.3s ease;\n ");
var bubble = document.createElement('div');
bubble.className = 'sandia-message-bubble';
bubble.style.cssText = "\n max-width: 80%;\n padding: 12px 16px;\n border-radius: 18px;\n ".concat(isUser ?
"background: ".concat(this.styles.primaryColor, "; color: white; border-bottom-right-radius: 4px;") :
"background: ".concat(this.styles.darkMode ? '#374151' : '#f1f3f4', "; color: ").concat(this.styles.textColor, "; border-bottom-left-radius: 4px;"), "\n ").concat(isError ? 'border: 1px solid #ef4444;' : '', "\n word-wrap: break-word;\n line-height: 1.4;\n position: relative;\n box-shadow: 0 1px 2px rgba(0,0,0,0.1);\n ");
// Add message content with basic markdown parsing
bubble.innerHTML = this.parseMessageContent(message.content);
// Add copy button for assistant messages
if (!isUser && !isError) {
var copyBtn_1 = document.createElement('button');
copyBtn_1.style.cssText = "\n position: absolute;\n top: -8px;\n right: -8px;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background: ".concat(this.styles.primaryColor, ";\n color: white;\n cursor: pointer;\n font-size: 12px;\n display: none;\n align-items: center;\n justify-content: center;\n opacity: 0.8;\n transition: all 0.2s ease;\n ");
copyBtn_1.innerHTML = '📋';
copyBtn_1.addEventListener('click', function () { return _this.copyToClipboard(message.content); });
bubble.addEventListener('mouseenter', function () {
copyBtn_1.style.display = 'flex';
});
bubble.addEventListener('mouseleave', function () {
copyBtn_1.style.display = 'none';
});
bubble.appendChild(copyBtn_1);
}
messageDiv.appendChild(bubble);
// Add timestamp if enabled
if (this.styles.showTimestamps) {
var timestamp = document.createElement('div');
timestamp.style.cssText = "\n font-size: 11px;\n color: ".concat(this.styles.darkMode ? '#6b7280' : '#9ca3af', ";\n margin-top: 4px;\n ").concat(isUser ? 'text-align: right' : 'text-align: left', ";\n ");
timestamp.textContent = this.formatTime(message.timestamp);
messageDiv.appendChild(timestamp);
}
return messageDiv;
};
// Helper methods
SandiaVanillaChatbot.prototype.getBubbleSize = function () {
switch (this.styles.size) {
case 'sm': return { width: '48px', height: '48px', fontSize: '20px' };
case 'lg': return { width: '64px', height: '64px', fontSize: '28px' };
default: return { width: '56px', height: '56px', fontSize: '24px' };
}
};
SandiaVanillaChatbot.prototype.getWindowSize = function () {
var isMobile = window.innerWidth <= 768;
if (isMobile && this.styles.mobileFullScreen) {
return { width: '100vw', height: '100vh' };
}
return { width: '380px', height: '600px' };
};
SandiaVanillaChatbot.prototype.setPosition = function () {
if (!this.container)
return;
var position = this.styles.position || 'bottom-right';
var offset = '24px';
this.container.style.top = 'auto';
this.container.style.bottom = 'auto';
this.container.style.left = 'auto';
this.container.style.right = 'auto';
switch (position) {
case 'bottom-left':
this.container.style.bottom = offset;
this.container.style.left = offset;
break;
case 'top-right':
this.container.style.top = offset;
this.container.style.right = offset;
break;
case 'top-left':
this.container.style.top = offset;
this.container.style.left = offset;
break;
default:
this.container.style.bottom = offset;
this.container.style.right = offset;
break;
}
};
SandiaVanillaChatbot.prototype.getChatWindowPosition = function () {
var position = this.styles.position || 'bottom-right';
var isMobile = window.innerWidth <= 768;
if (isMobile && this.styles.mobileFullScreen) {
return 'top: 0; left: 0;';
}
switch (position) {
case 'bottom-left':
return 'bottom: 70px; left: 0;';
case 'top-right':
return 'top: 70px; right: 0;';
case 'top-left':
return 'top: 70px; left: 0;';
default:
return 'bottom: 70px; right: 0;';
}
};
SandiaVanillaChatbot.prototype.getThemeStyles = function () {
var primary = this.styles.primaryColor;
switch (this.styles.theme) {
case 'gradient':
return {
background: "linear-gradient(135deg, ".concat(primary, " 0%, ").concat(this.adjustColor(primary, 30), " 100%)"),
border: 'none'
};
case 'classic':
return {
background: primary,
border: "2px solid ".concat(this.adjustColor(primary, -20))
};
case 'minimal':
return {
background: this.styles.darkMode ? '#374151' : '#f3f4f6',
border: "1px solid ".concat(primary)
};
default: // modern
return {
background: primary,
border: 'none'
};
}
};
SandiaVanillaChatbot.prototype.getShadowStyle = function () {
var primary = this.styles.primaryColor;
switch (this.styles.shadowStyle) {
case 'hard':
return '0 4px 12px rgba(0,0,0,0.3)';
case 'glow':
return "0 0 20px ".concat(primary, "40, 0 0 40px ").concat(primary, "20");
case 'none':
return 'none';
default: // soft
return '0 4px 20px rgba(0,0,0,0.15)';
}
};
SandiaVanillaChatbot.prototype.adjustColor = function (color, percent) {
var num = parseInt(color.replace("#", ""), 16);
var amt = Math.round(2.55 * percent);
var R = (num >> 16) + amt;
var G = (num >> 8 & 0x00FF) + amt;
var B = (num & 0x0000FF) + amt;
return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
(G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
(B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
};
SandiaVanillaChatbot.prototype.formatTime = function (date) {
var now = new Date();
var diff = now.getTime() - date.getTime();
var minutes = Math.floor(diff / 60000);
if (minutes < 1)
return 'Just now';
if (minutes < 60)
return "".concat(minutes, "m ago");
if (minutes < 1440)
return "".concat(Math.floor(minutes / 60), "h ago");
return date.toLocaleDateString();
};
SandiaVanillaChatbot.prototype.parseMessageContent = function (content) {
return content
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
};
SandiaVanillaChatbot.prototype.scrollToBottom = function () {
var container = document.getElementById('sandia-messages-container');
if (container) {
container.scrollTop = container.scrollHeight;
}
};
SandiaVanillaChatbot.prototype.copyToClipboard = function (text) {
var _this = this;
navigator.clipboard.writeText(text).then(function () {
_this.showToast('Message copied to clipboard');
});
};
SandiaVanillaChatbot.prototype.showToast = function (message) {
var toast = document.createElement('div');
toast.style.cssText = "\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 8px 16px;\n border-radius: 20px;\n font-size: 12px;\n z-index: 10002;\n animation: sandia-toast-in 0.3s ease;\n ";
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(function () {
toast.remove();
}, 2000);
};
SandiaVanillaChatbot.prototype.playSound = function (type) {
if (!this.styles.soundEnabled)
return;
try {
var audio = new Audio(this.sounds[type]);
audio.volume = 0.3;
audio.play().catch(function () { }); // Ignore audio play errors
}
catch (error) {
// Ignore audio errors
}
};
SandiaVanillaChatbot.prototype.showNotification = function (title, body) {
if (!this.styles.notificationsEnabled || this.notificationPermission !== 'granted')
return;
try {
new Notification(title, {
body: body.substring(0, 100) + (body.length > 100 ? '...' : ''),
icon: '/favicon.ico',
tag: 'sandia-chatbot'
});
}
catch (error) {
// Ignore notification errors
}
};
SandiaVanillaChatbot.prototype.updateBadge = function (count) {
this.unreadCount = count;
var badge = document.getElementById('sandia-unread-badge');
if (badge) {
if (count > 0) {
badge.style.display = 'flex';
badge.textContent = count > 99 ? '99+' : count.toString();
}
else {
badge.style.display = 'none';
}
}
};
SandiaVanillaChatbot.prototype.loadMessageHistory = function () {
try {
var stored = localStorage.getItem("sandia-chatbot-".concat(this.chatbotId));
if (stored) {
var data = JSON.parse(stored);
this.messages = data.messages || [];
this.conversationId = data.conversationId || null;
}
}
catch (error) {
// Ignore localStorage errors
}
};
SandiaVanillaChatbot.prototype.saveMessageHistory = function () {
try {
localStorage.setItem("sandia-chatbot-".concat(this.chatbotId), JSON.stringify({
messages: this.messages.slice(-50), // Keep last 50 messages
conversationId: this.conversationId,
timestamp: Date.now()
}));
}
catch (error) {
// Ignore localStorage errors
}
};
SandiaVanillaChatbot.prototype.createPulseRings = function (bubble) {
for (var i = 0; i < 3; i++) {
var ring = document.createElement('div');
ring.className = "sandia-pulse-ring-".concat(i);
ring.style.cssText = "\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n border: 2px solid ".concat(this.styles.primaryColor, ";\n border-radius: 50%;\n animation: sandia-pulse 2s infinite ").concat(i * 0.5, "s;\n pointer-events: none;\n ");
bubble.appendChild(ring);
}
};
SandiaVanillaChatbot.prototype.showTypingIndicator = function () {
var container = document.getElementById('sandia-messages-container');
if (!container)
return;
var indicator = document.createElement('div');
indicator.id = 'sandia-typing-indicator';
indicator.className = 'sandia-message sandia-message-assistant';
indicator.style.cssText = "\n display: flex;\n justify-content: flex-start;\n margin-bottom: 8px;\n animation: sandia-message-in 0.3s ease;\n ";
var bubble = document.createElement('div');
bubble.style.cssText = "\n background: ".concat(this.styles.darkMode ? '#374151' : '#f1f3f4', ";\n color: ").concat(this.styles.textColor, ";\n padding: 12px 16px;\n border-radius: 18px 18px 18px 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n ");
for (var i = 0; i < 3; i++) {
var dot = document.createElement('div');
dot.style.cssText = "\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: ".concat(this.styles.darkMode ? '#9ca3af' : '#6b7280', ";\n animation: sandia-dot-pulse 1.4s infinite ").concat(i * 0.2, "s;\n ");
bubble.appendChild(dot);
}
indicator.appendChild(bubble);
container.appendChild(indicator);
this.scrollToBottom();
};
SandiaVanillaChatbot.prototype.hideTypingIndicator = function () {
var indicator = document.getElementById('sandia-typing-indicator');
if (indicator) {
indicator.remove();
}
};
SandiaVanillaChatbot.prototype.sendQuickReply = function (reply) {
var input = document.getElementById('sandia-message-input');
if (input) {
input.value = reply;
this.sendMessage();
}
};
SandiaVanillaChatbot.prototype.renderMessages = function (container) {
var _this = this;
this.messages.forEach(function (message) {
var messageElement = _this.createMessage(message);
container.appendChild(messageElement);
});
this.scrollToBottom();
};
SandiaVanillaChatbot.prototype.renderBlockedState = function () {
this.container = document.createElement('div');
this.container.id = 'sandia-chatbot-blocked';
this.container.style.cssText = "\n position: fixed;\n z-index: 9999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n ";
this.setPosition();
var buttonSize = this.getBubbleSize();
this.container.innerHTML = "\n <div style=\"\n width: ".concat(buttonSize.width, ";\n height: ").concat(buttonSize.height, ";\n background: #6b7280;\n border: none;\n border-radius: ").concat(this.styles.borderRadius, "%;\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 20px rgba(0,0,0,0.15);\n font-size: ").concat(buttonSize.fontSize, ";\n opacity: 0.6;\n cursor: not-allowed;\n position: relative;\n \">\n \u26A0\uFE0F\n </div>\n ");
document.body.appendChild(this.container);
};
SandiaVanillaChatbot.prototype.setupAutoMinimize = function () {
var _this = this;
var checkInactivity = function () {
if (_this.isOpen && !_this.isMinimized) {
var inactiveTime = Date.now() - _this.lastActivity;
var autoMinimizeMs = (_this.styles.autoMinimize || 5) * 60 * 1000;
if (inactiveTime > autoMinimizeMs) {
_this.minimizeChat();
}
}
};
setInterval(checkInactivity, 30000); // Check every 30 seconds
};
SandiaVanillaChatbot.prototype.exposeAPI = function () {
var _this = this;
window.SandiaChatbotAPI = {
open: function () { return _this.openChat(); },
close: function () { return _this.closeChat(); },
minimize: function () { return _this.minimizeChat(); },
restore: function () { return _this.restoreChat(); },
sendMessage: function (message) {
var input = document.getElementById('sandia-message-input');
if (input) {
input.value = message;
_this.sendMessage();
}
},
updateConfig: function (config) {
_this.styles = __assign(__assign({}, _this.styles), config);
},
updateBadge: function (count) { return _this.updateBadge(count); },
resetUnreadCount: function () { return _this.updateBadge(0); },
getUnreadCount: function () { return _this.unreadCount; },
destroy: function () { return _this.destroy(); },
getConfig: function () { return (__assign({}, _this.styles)); },
getMessages: function () { return __spreadArray([], _this.messages, true); },
isOpen: function () { return _this.isOpen; },
isMinimized: function () { return _this.isMinimized; }
};
};
SandiaVanillaChatbot.prototype.addEnhancedStyles = function () {
if (document.getElementById('sandia-widget-styles-v3'))
return;
var style = document.createElement('style');
style.id = 'sandia-widget-styles-v3';
style.textContent = "\n @keyframes sandia-pulse {\n 0%, 80%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }\n 40% { opacity: 0.3; transform: translate(-50%, -50%) scale(1.3); }\n }\n \n @keyframes sandia-dot-pulse {\n 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }\n 40% { opacity: 1; transform: scale(1); }\n }\n \n @keyframes sandia-message-in {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n @keyframes sandia-badge-initial {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n }\n \n @keyframes sandia-online-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n \n @keyframes sandia-notification-in {\n from { opacity: 0; transform: translateY(-20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n @keyframes sandia-toast-in {\n from { opacity: 0; transform: translate(-50%, -10px); }\n to { opacity: 1; transform: translate(-50%, 0); }\n }\n \n .sandia-widget-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n \n .sandia-scroll-container {\n scrollbar-width: thin;\n scrollbar-color: ".concat(this.styles.darkMode ? '#4b5563 #1f2937' : '#cbd5e1 #f1f5f9', ";\n }\n \n .sandia-scroll-container::-webkit-scrollbar {\n width: 6px;\n }\n \n .sandia-scroll-container::-webkit-scrollbar-track {\n background: ").concat(this.styles.darkMode ? '#1f2937' : '#f1f5f9', ";\n border-radius: 3px;\n }\n \n .sandia-scroll-container::-webkit-scrollbar-thumb {\n background: ").concat(this.styles.darkMode ? '#4b5563' : '#cbd5e1', ";\n border-radius: 3px;\n }\n \n .sandia-scroll-container::-webkit-scrollbar-thumb:hover {\n background: ").concat(this.styles.darkMode ? '#6b7280' : '#94a3b8', ";\n }\n \n @media (max-width: 768px) {\n .sandia-chat-window {\n border-radius: ").concat(this.styles.mobileFullScreen ? '0 !important' : '16px', ";\n ").concat(this.styles.mobileFullScreen ? 'position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important;' : '', "\n }\n \n .sandia-message-bubble {\n max-width: 85% !important;\n }\n \n .sandia-input-container {\n padding: 16px !important;\n }\n \n #sandia-message-input {\n font-size: 16px !important; /* Prevent zoom on iOS */\n }\n }\n \n @media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n }\n \n @media (prefers-color-scheme: dark) {\n .sandia-chat-window {\n border-color: #374151;\n }\n }\n \n @media print {\n .sandia-widget-container {\n display: none !important;\n }\n }\n ");
document.head.appendChild(style);
};
// Public methods
SandiaVanillaChatbot.prototype.toggleChat = function () {
if (this.isOpen) {
this.closeChat();
}
else {
this.openChat();
}
};
SandiaVanillaChatbot.prototype.openChat = function () {
var window = document.getElementById('sandia-chat-window');
var bubble = document.getElementById('sandia-chat-bubble');
var input = document.getElementById('sandia-message-input');
if (window && bubble) {
this.isOpen = true;
this.isMinimized = false;
this.lastActivity = Date.now();
window.style.display = 'flex';
bubble.innerHTML = bubble.innerHTML.replace(this.styles.customIcon || '💬', '✕');
// Focus input
setTimeout(function () {
if (input)
input.focus();
}, 100);
// Reset unread count
this.updateBadge(0);
// Play sound
if (this.styles.soundEnabled) {
this.playSound('messageClick');
}
if (this.onOpen)
this.onOpen();
}
};
SandiaVanillaChatbot.prototype.closeChat = function () {
var window = document.getElementById('sandia-chat-window');
var bubble = document.getElementById('sandia-chat-bubble');
if (window && bubble) {
this.isOpen = false;
this.isMinimized = false;
window.style.display = 'none';
bubble.innerHTML = bubble.innerHTML.replace('✕', this.styles.customIcon || '💬');
if (this.onClose)
this.onClose();
}
};
SandiaVanillaChatbot.prototype.minimizeChat = function () {
var window = document.getElementById('sandia-chat-window');
if (window && this.isOpen) {
this.isMinimized = true;
window.style.display = 'none';
if (this.onMinimize)
this.onMinimize();
}
};
SandiaVanillaChatbot.prototype.restoreChat = function () {
if (this.isMinimized) {
this.isMinimized = false;
this.openChat();
if (this.onRestore)
this.onRestore();
}
};
SandiaVanillaChatbot.prototype.destroy = function () {
if (this.container) {
this.container.remove();
}
var styles = document.getElementById('sandia-widget-styles-v3');
if (styles) {
styles.remove();
}
delete window.SandiaChatbotAPI;
};
return SandiaVanillaChatbot;
}());
function initSandiaChatbot(config) {
var finalConfig = config || window.SandiaChatbot;
if (!finalConfig || !finalConfig.chatbotId) {
console.error('Sandia Chatbot v3.0: Missing chatbotId in configuration');
return;
}
new SandiaVanillaChatbot(finalConfig);
}
// Auto-initialize if window.SandiaChatbot is available
if (typeof window !== 'undefined' && window.SandiaChatbot) {
document.addEventListener('DOMContentLoaded', function () {
initSandiaChatbot();
});
}
export { SandiaWidget, initSandiaChatbot };
//# sourceMappingURL=index.esm.js.map