@kroonen-ai/librebot-widget 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/icons.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/librebot.esm.js +986 -11
- package/dist/librebot.esm.js.map +1 -1
- package/dist/librebot.js +1 -1
- package/dist/librebot.js.map +1 -1
- package/dist/translations.d.ts +27 -0
- package/dist/types.d.ts +51 -0
- package/dist/widget.d.ts +18 -0
- package/package.json +1 -1
package/dist/librebot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"librebot.js","sources":["../src/styles.ts","../src/icons.ts","../src/widget.ts","../src/embed.ts"],"sourcesContent":["export const getStyles = (primaryColor: string = '#14b8a6') => `\n .librebot-widget {\n --lb-primary: ${primaryColor};\n --lb-primary-hover: color-mix(in srgb, ${primaryColor} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;\n font-size: 14px;\n line-height: 1.5;\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: var(--lb-primary);\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s, box-shadow 0.2s;\n z-index: 999998;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);\n }\n\n .librebot-fab svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-fab.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n`;\n","export const chatIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path></svg>`;\n\nexport const closeIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>`;\n\nexport const sendIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line><polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon></svg>`;\n\nexport const botIcon = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>`;\n","import { LibreBotConfig, Message, ChatResponse } from './types';\nimport { getStyles } from './styles';\nimport { chatIcon, closeIcon, sendIcon } from './icons';\n\nexport class LibreBotWidget {\n private config!: Required<LibreBotConfig>;\n private container: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private messagesContainer: HTMLDivElement | null = null;\n private input: HTMLInputElement | null = null;\n private isOpen = false;\n private messages: Message[] = [];\n private isLoading = false;\n private sessionId: string | null = null;\n\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://librebot.io/api',\n position: 'bottom-right',\n theme: 'dark',\n primaryColor: '#14b8a6',\n title: 'Libre Bot',\n subtitle: 'AI Documentation Assistant',\n placeholder: 'Ask a question...',\n welcomeMessage: 'Hi! I can help you find answers in the documentation. What would you like to know?',\n streaming: true,\n whiteLabel: false,\n autoLoadConfig: true,\n };\n\n constructor(config: LibreBotConfig) {\n if (!config.apiKey) {\n console.error('LibreBot: API key is required');\n return;\n }\n\n this.config = { ...this.defaultConfig, ...config } as Required<LibreBotConfig>;\n\n // Auto-load config from server if enabled and no custom settings provided\n if (this.config.autoLoadConfig) {\n this.loadRemoteConfig().then(() => this.init());\n } else {\n this.init();\n }\n }\n\n private async loadRemoteConfig(): Promise<void> {\n try {\n const response = await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);\n if (response.ok) {\n const remoteConfig = await response.json();\n // Merge remote config with local config (local overrides remote)\n this.config = {\n ...this.defaultConfig,\n ...remoteConfig,\n ...this.getProvidedConfig(),\n apiKey: this.config.apiKey,\n apiUrl: this.config.apiUrl,\n autoLoadConfig: this.config.autoLoadConfig,\n } as Required<LibreBotConfig>;\n }\n } catch (error) {\n console.warn('LibreBot: Could not load remote config, using defaults', error);\n }\n }\n\n private getProvidedConfig(): Partial<LibreBotConfig> {\n // Return only the config options that were explicitly provided (not default values)\n const provided: Partial<LibreBotConfig> = {};\n const userConfig = this.config;\n const defaults = this.defaultConfig;\n\n if (userConfig.position !== defaults.position) provided.position = userConfig.position;\n if (userConfig.theme !== defaults.theme) provided.theme = userConfig.theme;\n if (userConfig.primaryColor !== defaults.primaryColor) provided.primaryColor = userConfig.primaryColor;\n if (userConfig.title !== defaults.title) provided.title = userConfig.title;\n if (userConfig.subtitle !== defaults.subtitle) provided.subtitle = userConfig.subtitle;\n if (userConfig.placeholder !== defaults.placeholder) provided.placeholder = userConfig.placeholder;\n if (userConfig.welcomeMessage !== defaults.welcomeMessage) provided.welcomeMessage = userConfig.welcomeMessage;\n if (userConfig.streaming !== defaults.streaming) provided.streaming = userConfig.streaming;\n if (userConfig.whiteLabel !== defaults.whiteLabel) provided.whiteLabel = userConfig.whiteLabel;\n\n return provided;\n }\n\n private init(): void {\n // Load sessionId from localStorage for persistent memory\n this.loadSession();\n\n // Inject styles\n this.injectStyles();\n\n // Create widget container\n this.createWidget();\n\n // Add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n }\n\n private getSessionKey(): string {\n return `librebot_session_${this.config.apiKey}`;\n }\n\n private loadSession(): void {\n try {\n const stored = localStorage.getItem(this.getSessionKey());\n if (stored) {\n this.sessionId = stored;\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n private saveSession(sessionId: string): void {\n try {\n this.sessionId = sessionId;\n localStorage.setItem(this.getSessionKey(), sessionId);\n } catch {\n // localStorage may not be available\n }\n }\n\n private injectStyles(): void {\n const styleId = 'librebot-styles';\n if (document.getElementById(styleId)) return;\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = getStyles(this.config.primaryColor);\n document.head.appendChild(style);\n }\n\n private createWidget(): void {\n // Create container\n this.container = document.createElement('div');\n this.container.className = `librebot-widget ${this.config.theme === 'light' ? 'light' : ''}`;\n\n // Create FAB button\n const fab = document.createElement('button');\n fab.className = `librebot-fab ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n fab.innerHTML = chatIcon;\n fab.onclick = () => this.toggle();\n\n // Create modal\n this.modal = document.createElement('div');\n this.modal.className = `librebot-modal ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n this.modal.innerHTML = this.getModalHTML();\n\n // Add to container\n this.container.appendChild(fab);\n this.container.appendChild(this.modal);\n document.body.appendChild(this.container);\n\n // Get references\n this.messagesContainer = this.modal.querySelector('.librebot-messages');\n this.input = this.modal.querySelector('.librebot-input');\n\n // Add event listeners\n const closeBtn = this.modal.querySelector('.librebot-close');\n closeBtn?.addEventListener('click', () => this.close());\n\n const sendBtn = this.modal.querySelector('.librebot-send');\n sendBtn?.addEventListener('click', () => this.sendMessage());\n\n this.input?.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.sendMessage();\n }\n });\n\n // Auto theme detection\n if (this.config.theme === 'auto') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: light)');\n this.updateTheme(mediaQuery.matches);\n mediaQuery.addEventListener('change', (e) => this.updateTheme(e.matches));\n }\n }\n\n private getModalHTML(): string {\n const poweredByHtml = this.config.whiteLabel\n ? ''\n : `<div class=\"librebot-powered\">\n Powered by <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>`;\n\n return `\n <div class=\"librebot-header\">\n <div class=\"librebot-header-content\">\n <div class=\"librebot-avatar\">${chatIcon}</div>\n <div>\n <h3 class=\"librebot-title\">${this.config.title}</h3>\n <p class=\"librebot-subtitle\">${this.config.subtitle}</p>\n </div>\n </div>\n <button class=\"librebot-close\">${closeIcon}</button>\n </div>\n <div class=\"librebot-messages\"></div>\n <div class=\"librebot-input-area\">\n <input type=\"text\" class=\"librebot-input\" placeholder=\"${this.config.placeholder}\" />\n <button class=\"librebot-send\">${sendIcon}</button>\n </div>\n ${poweredByHtml}\n `;\n }\n\n private updateTheme(isLight: boolean): void {\n if (isLight) {\n this.container?.classList.add('light');\n } else {\n this.container?.classList.remove('light');\n }\n }\n\n public toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n public open(): void {\n this.isOpen = true;\n this.modal?.classList.add('open');\n this.input?.focus();\n }\n\n public close(): void {\n this.isOpen = false;\n this.modal?.classList.remove('open');\n }\n\n private addMessage(role: 'user' | 'assistant', content: string): void {\n const message: Message = {\n id: `msg-${Date.now()}`,\n role,\n content,\n timestamp: new Date(),\n };\n\n this.messages.push(message);\n this.renderMessage(message);\n }\n\n private renderMessage(message: Message): void {\n if (!this.messagesContainer) return;\n\n const el = document.createElement('div');\n el.className = `librebot-message ${message.role}`;\n el.innerHTML = this.formatMessage(message.content);\n\n this.messagesContainer.appendChild(el);\n this.scrollToBottom();\n }\n\n private formatMessage(content: string): string {\n // Escape HTML to prevent XSS\n const escapeHtml = (text: string): string => {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n };\n\n // First, extract code blocks to protect them from other formatting\n // Regex handles optional space/newline after ``` and optional language identifier\n const codeBlocks: string[] = [];\n let processed = content.replace(/```\\s*(\\w*)\\s*\\n?([\\s\\S]*?)```/g, (_, lang, code) => {\n const escaped = escapeHtml(code.trim());\n const langClass = lang ? ` data-language=\"${escapeHtml(lang)}\"` : '';\n codeBlocks.push(`<pre class=\"librebot-code-block\"${langClass}><code>${escaped}</code></pre>`);\n return `%%CODEBLOCK${codeBlocks.length - 1}%%`;\n });\n\n // Extract inline code\n const inlineCodes: string[] = [];\n processed = processed.replace(/`([^`]+)`/g, (_, code) => {\n inlineCodes.push(`<code class=\"librebot-inline-code\">${escapeHtml(code)}</code>`);\n return `%%INLINECODE${inlineCodes.length - 1}%%`;\n });\n\n // Now escape remaining HTML\n processed = escapeHtml(processed);\n\n // Headers (## and ###)\n processed = processed.replace(/^### (.+)$/gm, '<h4 class=\"librebot-h4\">$1</h4>');\n processed = processed.replace(/^## (.+)$/gm, '<h3 class=\"librebot-h3\">$1</h3>');\n processed = processed.replace(/^# (.+)$/gm, '<h2 class=\"librebot-h2\">$1</h2>');\n\n // Bold and italic\n processed = processed.replace(/\\*\\*\\*([^*]+)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n processed = processed.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\n processed = processed.replace(/_([^_]+)_/g, '<em>$1</em>');\n\n // Links [text](url)\n processed = processed.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"librebot-link\">$1</a>');\n\n // Unordered lists\n processed = processed.replace(/^[\\*\\-] (.+)$/gm, '<li class=\"librebot-li\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ul class=\"librebot-ul\">${match}</ul>`;\n });\n\n // Ordered lists\n processed = processed.replace(/^\\d+\\. (.+)$/gm, '<li class=\"librebot-li-ordered\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li-ordered\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ol class=\"librebot-ol\">${match}</ol>`;\n });\n\n // Horizontal rule\n processed = processed.replace(/^---$/gm, '<hr class=\"librebot-hr\">');\n\n // Blockquotes\n processed = processed.replace(/^> (.+)$/gm, '<blockquote class=\"librebot-blockquote\">$1</blockquote>');\n\n // Convert newlines to <br> (but not inside lists/blockquotes)\n processed = processed.replace(/\\n/g, '<br>');\n\n // Clean up extra <br> after block elements\n processed = processed.replace(/<\\/(pre|ul|ol|blockquote|h[2-4])><br>/g, '</$1>');\n processed = processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g, '<$1');\n\n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n processed = processed.replace(`%%CODEBLOCK${i}%%`, block);\n });\n\n // Restore inline code\n inlineCodes.forEach((code, i) => {\n processed = processed.replace(`%%INLINECODE${i}%%`, code);\n });\n\n return processed;\n }\n\n private showTyping(): HTMLDivElement {\n const typing = document.createElement('div');\n typing.className = 'librebot-typing';\n typing.innerHTML = '<span></span><span></span><span></span>';\n this.messagesContainer?.appendChild(typing);\n this.scrollToBottom();\n return typing;\n }\n\n private scrollToBottom(): void {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n }\n\n private async sendMessage(): Promise<void> {\n if (!this.input || this.isLoading) return;\n\n const content = this.input.value.trim();\n if (!content) return;\n\n // Add user message\n this.addMessage('user', content);\n this.input.value = '';\n\n // Show typing indicator\n this.isLoading = true;\n const typing = this.showTyping();\n\n try {\n if (this.config.streaming) {\n await this.callStreamingAPI(content, typing);\n } else {\n const response = await this.callAPI(content);\n typing.remove();\n\n if (response.error) {\n this.addMessage('assistant', `Sorry, I encountered an error: ${response.error}`);\n } else {\n this.addMessage('assistant', response.response);\n }\n }\n } catch (error) {\n typing.remove();\n this.addMessage('assistant', 'Sorry, I had trouble connecting. Please try again.');\n console.error('LibreBot error:', error);\n } finally {\n this.isLoading = false;\n }\n }\n\n private async callStreamingAPI(message: string, typing: HTMLDivElement): Promise<void> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n stream: true,\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n typing.remove();\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n this.addMessage('assistant', `Sorry, I encountered an error: ${error.error || 'Request failed'}`);\n return;\n }\n\n // Remove typing indicator and create streaming message element\n typing.remove();\n const messageEl = document.createElement('div');\n messageEl.className = 'librebot-message assistant';\n this.messagesContainer?.appendChild(messageEl);\n\n const reader = response.body?.getReader();\n if (!reader) {\n this.addMessage('assistant', 'Sorry, streaming is not supported.');\n return;\n }\n\n const decoder = new TextDecoder();\n let fullContent = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n const lines = chunk.split('\\n').filter((line) => line.trim() !== '');\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n if (parsed.content) {\n fullContent += parsed.content;\n messageEl.innerHTML = this.formatMessage(fullContent);\n this.scrollToBottom();\n }\n // Save sessionId when received from server\n if (parsed.sessionId) {\n this.saveSession(parsed.sessionId);\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream reading error:', error);\n }\n\n // Add to messages array\n this.messages.push({\n id: `msg-${Date.now()}`,\n role: 'assistant',\n content: fullContent,\n timestamp: new Date(),\n });\n }\n\n private async callAPI(message: string): Promise<ChatResponse> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n return { response: '', error: error.error || 'Request failed' };\n }\n\n const data = await response.json();\n // Save sessionId when received from server\n if (data.sessionId) {\n this.saveSession(data.sessionId);\n }\n return data;\n }\n\n private getConversationContext(): Array<{ role: string; content: string }> {\n // Get last 10 messages for context\n return this.messages.slice(-10).map((m) => ({\n role: m.role,\n content: m.content,\n }));\n }\n\n public destroy(): void {\n this.container?.remove();\n document.getElementById('librebot-styles')?.remove();\n }\n\n public clearSession(): void {\n try {\n this.sessionId = null;\n this.messages = [];\n localStorage.removeItem(this.getSessionKey());\n if (this.messagesContainer) {\n this.messagesContainer.innerHTML = '';\n }\n // Re-add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n } catch {\n // localStorage may not be available\n }\n }\n}\n","import { LibreBotWidget } from './widget';\nimport { LibreBotConfig } from './types';\n\n// Auto-initialize from script tag\n(function () {\n // Find the script tag - document.currentScript may be null with defer\n const script = document.currentScript as HTMLScriptElement\n || document.querySelector('script[data-key][src*=\"librebot\"]') as HTMLScriptElement;\n\n if (!script) {\n console.warn('LibreBot: Could not find script tag');\n return;\n }\n\n // Get config from data attributes\n const config: LibreBotConfig = {\n apiKey: script.dataset.key || script.dataset.apiKey || '',\n apiUrl: script.dataset.apiUrl || 'https://api.librebot.io',\n position: (script.dataset.position as 'bottom-right' | 'bottom-left') || 'bottom-right',\n theme: (script.dataset.theme as 'light' | 'dark' | 'auto') || 'dark',\n primaryColor: script.dataset.color || '#14b8a6',\n title: script.dataset.title || 'Libre Bot',\n subtitle: script.dataset.subtitle || 'AI Documentation Assistant',\n placeholder: script.dataset.placeholder || 'Ask a question...',\n welcomeMessage: script.dataset.welcome || 'Hi! I can help you find answers in the documentation. What would you like to know?',\n streaming: script.dataset.streaming !== 'false', // default true\n };\n\n if (!config.apiKey) {\n console.error('LibreBot: Missing data-key attribute');\n return;\n }\n\n // Initialize when DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n (window as any).LibreBot = new LibreBotWidget(config);\n });\n } else {\n (window as any).LibreBot = new LibreBotWidget(config);\n }\n})();\n\n// Export for manual initialization\nexport { LibreBotWidget, LibreBotConfig };\n"],"names":["chatIcon","LibreBotWidget","constructor","config","this","container","modal","messagesContainer","input","isOpen","messages","isLoading","sessionId","defaultConfig","apiUrl","position","theme","primaryColor","title","subtitle","placeholder","welcomeMessage","streaming","whiteLabel","autoLoadConfig","apiKey","loadRemoteConfig","then","init","console","error","response","fetch","ok","remoteConfig","json","getProvidedConfig","warn","provided","userConfig","defaults","loadSession","injectStyles","createWidget","addMessage","getSessionKey","stored","localStorage","getItem","saveSession","setItem","styleId","document","getElementById","style","createElement","id","textContent","getStyles","head","appendChild","className","fab","innerHTML","onclick","toggle","getModalHTML","body","querySelector","closeBtn","addEventListener","close","sendBtn","sendMessage","e","key","shiftKey","preventDefault","mediaQuery","window","matchMedia","updateTheme","matches","poweredByHtml","isLight","classList","add","remove","open","focus","role","content","message","Date","now","timestamp","push","renderMessage","el","formatMessage","scrollToBottom","escapeHtml","text","replace","codeBlocks","processed","_","lang","code","escaped","trim","langClass","length","inlineCodes","match","forEach","block","i","showTyping","typing","scrollTop","scrollHeight","value","callStreamingAPI","callAPI","method","headers","Authorization","JSON","stringify","context","getConversationContext","stream","catch","messageEl","reader","getReader","decoder","TextDecoder","fullContent","done","read","lines","decode","split","filter","line","startsWith","data","slice","parsed","parse","map","m","destroy","clearSession","removeItem","script","currentScript","dataset","color","welcome","readyState","LibreBot"],"mappings":"sCAAO,MCAMA,EAAW,yNCIXC,EAyBX,WAAAC,CAAYC,GAvBJC,KAAAC,UAAmC,KACnCD,KAAAE,MAA+B,KAC/BF,KAAAG,kBAA2C,KAC3CH,KAAAI,MAAiC,KACjCJ,KAAAK,QAAS,EACTL,KAAAM,SAAsB,GACtBN,KAAAO,WAAY,EACZP,KAAAQ,UAA2B,KAE3BR,KAAAS,cAA0D,CAChEC,OAAQ,0BACRC,SAAU,eACVC,MAAO,OACPC,aAAc,UACdC,MAAO,YACPC,SAAU,6BACVC,YAAa,oBACbC,eAAgB,qFAChBC,WAAW,EACXC,YAAY,EACZC,gBAAgB,GAIXrB,EAAOsB,QAKZrB,KAAKD,OAAS,IAAKC,KAAKS,iBAAkBV,GAGtCC,KAAKD,OAAOqB,eACdpB,KAAKsB,mBAAmBC,KAAK,IAAMvB,KAAKwB,QAExCxB,KAAKwB,QAVLC,QAAQC,MAAM,gCAYlB,CAEQ,sBAAMJ,GACZ,IACE,MAAMK,QAAiBC,MAAM,GAAG5B,KAAKD,OAAOW,4BAA4BV,KAAKD,OAAOsB,UACpF,GAAIM,EAASE,GAAI,CACf,MAAMC,QAAqBH,EAASI,OAEpC/B,KAAKD,OAAS,IACTC,KAAKS,iBACLqB,KACA9B,KAAKgC,oBACRX,OAAQrB,KAAKD,OAAOsB,OACpBX,OAAQV,KAAKD,OAAOW,OACpBU,eAAgBpB,KAAKD,OAAOqB,eAEhC,CACF,CAAE,MAAOM,GACPD,QAAQQ,KAAK,yDAA0DP,EACzE,CACF,CAEQ,iBAAAM,GAEN,MAAME,EAAoC,CAAA,EACpCC,EAAanC,KAAKD,OAClBqC,EAAWpC,KAAKS,cAYtB,OAVI0B,EAAWxB,WAAayB,EAASzB,WAAUuB,EAASvB,SAAWwB,EAAWxB,UAC1EwB,EAAWvB,QAAUwB,EAASxB,QAAOsB,EAAStB,MAAQuB,EAAWvB,OACjEuB,EAAWtB,eAAiBuB,EAASvB,eAAcqB,EAASrB,aAAesB,EAAWtB,cACtFsB,EAAWrB,QAAUsB,EAAStB,QAAOoB,EAASpB,MAAQqB,EAAWrB,OACjEqB,EAAWpB,WAAaqB,EAASrB,WAAUmB,EAASnB,SAAWoB,EAAWpB,UAC1EoB,EAAWnB,cAAgBoB,EAASpB,cAAakB,EAASlB,YAAcmB,EAAWnB,aACnFmB,EAAWlB,iBAAmBmB,EAASnB,iBAAgBiB,EAASjB,eAAiBkB,EAAWlB,gBAC5FkB,EAAWjB,YAAckB,EAASlB,YAAWgB,EAAShB,UAAYiB,EAAWjB,WAC7EiB,EAAWhB,aAAeiB,EAASjB,aAAYe,EAASf,WAAagB,EAAWhB,YAE7Ee,CACT,CAEQ,IAAAV,GAENxB,KAAKqC,cAGLrC,KAAKsC,eAGLtC,KAAKuC,eAGDvC,KAAKD,OAAOkB,gBACdjB,KAAKwC,WAAW,YAAaxC,KAAKD,OAAOkB,eAE7C,CAEQ,aAAAwB,GACN,MAAO,oBAAoBzC,KAAKD,OAAOsB,QACzC,CAEQ,WAAAgB,GACN,IACE,MAAMK,EAASC,aAAaC,QAAQ5C,KAAKyC,iBACrCC,IACF1C,KAAKQ,UAAYkC,EAErB,CAAE,MAEF,CACF,CAEQ,WAAAG,CAAYrC,GAClB,IACER,KAAKQ,UAAYA,EACjBmC,aAAaG,QAAQ9C,KAAKyC,gBAAiBjC,EAC7C,CAAE,MAEF,CACF,CAEQ,YAAA8B,GACN,MAAMS,EAAU,kBAChB,GAAIC,SAASC,eAAeF,GAAU,OAEtC,MAAMG,EAAQF,SAASG,cAAc,SACrCD,EAAME,GAAKL,EACXG,EAAMG,YFlIe,EAACxC,EAAuB,YAAc,6CAE3CA,kDACyBA,wvTE+HrByC,CAAUtD,KAAKD,OAAOc,cAC1CmC,SAASO,KAAKC,YAAYN,EAC5B,CAEQ,YAAAX,GAENvC,KAAKC,UAAY+C,SAASG,cAAc,OACxCnD,KAAKC,UAAUwD,UAAY,oBAAyC,UAAtBzD,KAAKD,OAAOa,MAAoB,QAAU,IAGxF,MAAM8C,EAAMV,SAASG,cAAc,UACnCO,EAAID,UAAY,iBAAyC,gBAAzBzD,KAAKD,OAAOY,SAA6B,OAAS,IAClF+C,EAAIC,UAAY/D,EAChB8D,EAAIE,QAAU,IAAM5D,KAAK6D,SAGzB7D,KAAKE,MAAQ8C,SAASG,cAAc,OACpCnD,KAAKE,MAAMuD,UAAY,mBAA2C,gBAAzBzD,KAAKD,OAAOY,SAA6B,OAAS,IAC3FX,KAAKE,MAAMyD,UAAY3D,KAAK8D,eAG5B9D,KAAKC,UAAUuD,YAAYE,GAC3B1D,KAAKC,UAAUuD,YAAYxD,KAAKE,OAChC8C,SAASe,KAAKP,YAAYxD,KAAKC,WAG/BD,KAAKG,kBAAoBH,KAAKE,MAAM8D,cAAc,sBAClDhE,KAAKI,MAAQJ,KAAKE,MAAM8D,cAAc,mBAGtC,MAAMC,EAAWjE,KAAKE,MAAM8D,cAAc,mBAC1CC,GAAUC,iBAAiB,QAAS,IAAMlE,KAAKmE,SAE/C,MAAMC,EAAUpE,KAAKE,MAAM8D,cAAc,kBAWzC,GAVAI,GAASF,iBAAiB,QAAS,IAAMlE,KAAKqE,eAE9CrE,KAAKI,OAAO8D,iBAAiB,WAAaI,IAC1B,UAAVA,EAAEC,KAAoBD,EAAEE,WAC1BF,EAAEG,iBACFzE,KAAKqE,iBAKiB,SAAtBrE,KAAKD,OAAOa,MAAkB,CAChC,MAAM8D,EAAaC,OAAOC,WAAW,iCACrC5E,KAAK6E,YAAYH,EAAWI,SAC5BJ,EAAWR,iBAAiB,SAAWI,GAAMtE,KAAK6E,YAAYP,EAAEQ,SAClE,CACF,CAEQ,YAAAhB,GACN,MAAMiB,EAAgB/E,KAAKD,OAAOoB,WAC9B,GACA,+HAIJ,MAAO,gIAG8BvB,oEAEAI,KAAKD,OAAOe,wDACVd,KAAKD,OAAOgB,+dAOUf,KAAKD,OAAOiB,4TAGrE+D,SAEN,CAEQ,WAAAF,CAAYG,GACdA,EACFhF,KAAKC,WAAWgF,UAAUC,IAAI,SAE9BlF,KAAKC,WAAWgF,UAAUE,OAAO,QAErC,CAEO,MAAAtB,GACL7D,KAAKK,OAASL,KAAKmE,QAAUnE,KAAKoF,MACpC,CAEO,IAAAA,GACLpF,KAAKK,QAAS,EACdL,KAAKE,OAAO+E,UAAUC,IAAI,QAC1BlF,KAAKI,OAAOiF,OACd,CAEO,KAAAlB,GACLnE,KAAKK,QAAS,EACdL,KAAKE,OAAO+E,UAAUE,OAAO,OAC/B,CAEQ,UAAA3C,CAAW8C,EAA4BC,GAC7C,MAAMC,EAAmB,CACvBpC,GAAI,OAAOqC,KAAKC,QAChBJ,OACAC,UACAI,UAAW,IAAIF,MAGjBzF,KAAKM,SAASsF,KAAKJ,GACnBxF,KAAK6F,cAAcL,EACrB,CAEQ,aAAAK,CAAcL,GACpB,IAAKxF,KAAKG,kBAAmB,OAE7B,MAAM2F,EAAK9C,SAASG,cAAc,OAClC2C,EAAGrC,UAAY,oBAAoB+B,EAAQF,OAC3CQ,EAAGnC,UAAY3D,KAAK+F,cAAcP,EAAQD,SAE1CvF,KAAKG,kBAAkBqD,YAAYsC,GACnC9F,KAAKgG,gBACP,CAEQ,aAAAD,CAAcR,GAEpB,MAAMU,EAAcC,GACXA,EACJC,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,UAKbC,EAAuB,GAC7B,IAAIC,EAAYd,EAAQY,QAAQ,kCAAmC,CAACG,EAAGC,EAAMC,KAC3E,MAAMC,EAAUR,EAAWO,EAAKE,QAC1BC,EAAYJ,EAAO,mBAAmBN,EAAWM,MAAW,GAElE,OADAH,EAAWR,KAAK,mCAAmCe,WAAmBF,kBAC/D,cAAcL,EAAWQ,OAAS,QAI3C,MAAMC,EAAwB,GA0D9B,OAzDAR,EAAYA,EAAUF,QAAQ,aAAc,CAACG,EAAGE,KAC9CK,EAAYjB,KAAK,sCAAsCK,EAAWO,aAC3D,eAAeK,EAAYD,OAAS,QAI7CP,EAAYJ,EAAWI,GAGvBA,EAAYA,EAAUF,QAAQ,eAAgB,mCAC9CE,EAAYA,EAAUF,QAAQ,cAAe,mCAC7CE,EAAYA,EAAUF,QAAQ,aAAc,mCAG5CE,EAAYA,EAAUF,QAAQ,uBAAwB,gCACtDE,EAAYA,EAAUF,QAAQ,mBAAoB,uBAClDE,EAAYA,EAAUF,QAAQ,eAAgB,eAC9CE,EAAYA,EAAUF,QAAQ,aAAc,eAG5CE,EAAYA,EAAUF,QAAQ,2BAA4B,uFAG1DE,EAAYA,EAAUF,QAAQ,kBAAmB,mCACjDE,EAAYA,EAAUF,QAAQ,0CAA4CW,GACjE,2BAA2BA,UAIpCT,EAAYA,EAAUF,QAAQ,iBAAkB,2CAChDE,EAAYA,EAAUF,QAAQ,kDAAoDW,GACzE,2BAA2BA,UAIpCT,EAAYA,EAAUF,QAAQ,UAAW,4BAGzCE,EAAYA,EAAUF,QAAQ,gBAAiB,2DAG/CE,EAAYA,EAAUF,QAAQ,MAAO,QAGrCE,EAAYA,EAAUF,QAAQ,yCAA0C,SACxEE,EAAYA,EAAUF,QAAQ,sCAAuC,OAGrEC,EAAWW,QAAQ,CAACC,EAAOC,KACzBZ,EAAYA,EAAUF,QAAQ,cAAcc,MAAOD,KAIrDH,EAAYE,QAAQ,CAACP,EAAMS,KACzBZ,EAAYA,EAAUF,QAAQ,eAAec,MAAOT,KAG/CH,CACT,CAEQ,UAAAa,GACN,MAAMC,EAASnE,SAASG,cAAc,OAKtC,OAJAgE,EAAO1D,UAAY,kBACnB0D,EAAOxD,UAAY,0CACnB3D,KAAKG,mBAAmBqD,YAAY2D,GACpCnH,KAAKgG,iBACEmB,CACT,CAEQ,cAAAnB,GACFhG,KAAKG,oBACPH,KAAKG,kBAAkBiH,UAAYpH,KAAKG,kBAAkBkH,aAE9D,CAEQ,iBAAMhD,GACZ,IAAKrE,KAAKI,OAASJ,KAAKO,UAAW,OAEnC,MAAMgF,EAAUvF,KAAKI,MAAMkH,MAAMZ,OACjC,IAAKnB,EAAS,OAGdvF,KAAKwC,WAAW,OAAQ+C,GACxBvF,KAAKI,MAAMkH,MAAQ,GAGnBtH,KAAKO,WAAY,EACjB,MAAM4G,EAASnH,KAAKkH,aAEpB,IACE,GAAIlH,KAAKD,OAAOmB,gBACRlB,KAAKuH,iBAAiBhC,EAAS4B,OAChC,CACL,MAAMxF,QAAiB3B,KAAKwH,QAAQjC,GACpC4B,EAAOhC,SAEHxD,EAASD,MACX1B,KAAKwC,WAAW,YAAa,kCAAkCb,EAASD,SAExE1B,KAAKwC,WAAW,YAAab,EAASA,SAE1C,CACF,CAAE,MAAOD,GACPyF,EAAOhC,SACPnF,KAAKwC,WAAW,YAAa,sDAC7Bf,QAAQC,MAAM,kBAAmBA,EACnC,SACE1B,KAAKO,WAAY,CACnB,CACF,CAEQ,sBAAMgH,CAAiB/B,EAAiB2B,GAC9C,MAAMxF,QAAiBC,MAAM,GAAG5B,KAAKD,OAAOW,cAAe,CACzD+G,OAAQ,OACRC,QAAS,CACP,eAAgB,mBAChBC,cAAe,UAAU3H,KAAKD,OAAOsB,UAEvC0C,KAAM6D,KAAKC,UAAU,CACnBrC,UACAsC,QAAS9H,KAAK+H,yBACdC,QAAQ,EACRxH,UAAWR,KAAKQ,cAIpB,IAAKmB,EAASE,GAAI,CAChBsF,EAAOhC,SACP,MAAMzD,QAAcC,EAASI,OAAOkG,MAAM,MAASvG,MAAO,oBAE1D,YADA1B,KAAKwC,WAAW,YAAa,kCAAkCd,EAAMA,OAAS,mBAEhF,CAGAyF,EAAOhC,SACP,MAAM+C,EAAYlF,SAASG,cAAc,OACzC+E,EAAUzE,UAAY,6BACtBzD,KAAKG,mBAAmBqD,YAAY0E,GAEpC,MAAMC,EAASxG,EAASoC,MAAMqE,YAC9B,IAAKD,EAEH,YADAnI,KAAKwC,WAAW,YAAa,sCAI/B,MAAM6F,EAAU,IAAIC,YACpB,IAAIC,EAAc,GAElB,IACE,OAAa,CACX,MAAMC,KAAEA,EAAIlB,MAAEA,SAAgBa,EAAOM,OACrC,GAAID,EAAM,MAEV,MACME,EADQL,EAAQM,OAAOrB,EAAO,CAAEU,QAAQ,IAC1BY,MAAM,MAAMC,OAAQC,GAAyB,KAAhBA,EAAKpC,QAEtD,IAAK,MAAMoC,KAAQJ,EACjB,GAAII,EAAKC,WAAW,UAAW,CAC7B,MAAMC,EAAOF,EAAKG,MAAM,GACxB,GAAa,WAATD,EAAmB,SAEvB,IACE,MAAME,EAAStB,KAAKuB,MAAMH,GACtBE,EAAO3D,UACTgD,GAAeW,EAAO3D,QACtB2C,EAAUvE,UAAY3D,KAAK+F,cAAcwC,GACzCvI,KAAKgG,kBAGHkD,EAAO1I,WACTR,KAAK6C,YAAYqG,EAAO1I,UAE5B,CAAE,MAEF,CACF,CAEJ,CACF,CAAE,MAAOkB,GACPD,QAAQC,MAAM,wBAAyBA,EACzC,CAGA1B,KAAKM,SAASsF,KAAK,CACjBxC,GAAI,OAAOqC,KAAKC,QAChBJ,KAAM,YACNC,QAASgD,EACT5C,UAAW,IAAIF,MAEnB,CAEQ,aAAM+B,CAAQhC,GACpB,MAAM7D,QAAiBC,MAAM,GAAG5B,KAAKD,OAAOW,cAAe,CACzD+G,OAAQ,OACRC,QAAS,CACP,eAAgB,mBAChBC,cAAe,UAAU3H,KAAKD,OAAOsB,UAEvC0C,KAAM6D,KAAKC,UAAU,CACnBrC,UACAsC,QAAS9H,KAAK+H,yBACdvH,UAAWR,KAAKQ,cAIpB,IAAKmB,EAASE,GAAI,CAEhB,MAAO,CAAEF,SAAU,GAAID,aADHC,EAASI,OAAOkG,MAAM,MAASvG,MAAO,qBACtBA,OAAS,iBAC/C,CAEA,MAAMsH,QAAarH,EAASI,OAK5B,OAHIiH,EAAKxI,WACPR,KAAK6C,YAAYmG,EAAKxI,WAEjBwI,CACT,CAEQ,sBAAAjB,GAEN,OAAO/H,KAAKM,SAAS2I,OAAM,IAAKG,IAAKC,IAAC,CACpC/D,KAAM+D,EAAE/D,KACRC,QAAS8D,EAAE9D,UAEf,CAEO,OAAA+D,GACLtJ,KAAKC,WAAWkF,SAChBnC,SAASC,eAAe,oBAAoBkC,QAC9C,CAEO,YAAAoE,GACL,IACEvJ,KAAKQ,UAAY,KACjBR,KAAKM,SAAW,GAChBqC,aAAa6G,WAAWxJ,KAAKyC,iBACzBzC,KAAKG,oBACPH,KAAKG,kBAAkBwD,UAAY,IAGjC3D,KAAKD,OAAOkB,gBACdjB,KAAKwC,WAAW,YAAaxC,KAAKD,OAAOkB,eAE7C,CAAE,MAEF,CACF,SCvgBF,WAEE,MAAMwI,EAASzG,SAAS0G,eACnB1G,SAASgB,cAAc,qCAE5B,IAAKyF,EAEH,YADAhI,QAAQQ,KAAK,uCAKf,MAAMlC,EAAyB,CAC7BsB,OAAQoI,EAAOE,QAAQpF,KAAOkF,EAAOE,QAAQtI,QAAU,GACvDX,OAAQ+I,EAAOE,QAAQjJ,QAAU,0BACjCC,SAAW8I,EAAOE,QAAQhJ,UAA+C,eACzEC,MAAQ6I,EAAOE,QAAQ/I,OAAuC,OAC9DC,aAAc4I,EAAOE,QAAQC,OAAS,UACtC9I,MAAO2I,EAAOE,QAAQ7I,OAAS,YAC/BC,SAAU0I,EAAOE,QAAQ5I,UAAY,6BACrCC,YAAayI,EAAOE,QAAQ3I,aAAe,oBAC3CC,eAAgBwI,EAAOE,QAAQE,SAAW,qFAC1C3I,UAAwC,UAA7BuI,EAAOE,QAAQzI,WAGvBnB,EAAOsB,OAMgB,YAAxB2B,SAAS8G,WACX9G,SAASkB,iBAAiB,mBAAoB,KAC3CS,OAAeoF,SAAW,IAAIlK,EAAeE,KAG/C4E,OAAeoF,SAAW,IAAIlK,EAAeE,GAV9C0B,QAAQC,MAAM,uCAYjB,CArCD"}
|
|
1
|
+
{"version":3,"file":"librebot.js","sources":["../src/styles.ts","../src/icons.ts","../src/translations.ts","../src/widget.ts","../src/embed.ts"],"sourcesContent":["export const getStyles = (primaryColor: string = '#14b8a6') => `\n .librebot-widget {\n --lb-primary: ${primaryColor};\n --lb-primary-hover: color-mix(in srgb, ${primaryColor} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;\n font-size: 14px;\n line-height: 1.5;\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: var(--lb-primary);\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s, box-shadow 0.2s;\n z-index: 999998;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);\n }\n\n .librebot-fab svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-fab.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-chat-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-booking-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n\n /* RTL Support */\n .librebot-widget.rtl {\n direction: rtl;\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-message.user {\n align-self: flex-start;\n border-bottom-right-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-message.assistant {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-typing {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-header-content {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-input {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-input-area {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-ul,\n .librebot-widget.rtl .librebot-ol {\n padding-left: 0;\n padding-right: 20px;\n }\n\n .librebot-widget.rtl .librebot-blockquote {\n border-left: none;\n border-right: 3px solid var(--lb-primary);\n border-radius: 6px 0 0 6px;\n }\n\n /* Booking Mode Styles */\n .librebot-booking {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-booking-header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-booking-back {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-booking-back:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-booking-back svg {\n width: 20px;\n height: 20px;\n }\n\n .librebot-booking-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n }\n\n .librebot-booking-content {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n }\n\n .librebot-booking-section {\n margin-bottom: 20px;\n }\n\n .librebot-booking-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--lb-text);\n margin-bottom: 8px;\n }\n\n .librebot-date-input {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n cursor: pointer;\n }\n\n .librebot-date-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-slots {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n }\n\n .librebot-slot {\n padding: 10px 8px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: center;\n }\n\n .librebot-slot:hover {\n border-color: var(--lb-primary);\n }\n\n .librebot-slot.selected {\n background: var(--lb-primary);\n border-color: var(--lb-primary);\n color: white;\n }\n\n .librebot-slot:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-slots-loading,\n .librebot-slots-empty {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n font-size: 13px;\n }\n\n .librebot-form-field {\n margin-bottom: 16px;\n }\n\n .librebot-form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-text);\n margin-bottom: 6px;\n }\n\n .librebot-form-label .required {\n color: #ef4444;\n margin-left: 2px;\n }\n\n .librebot-form-input,\n .librebot-form-textarea,\n .librebot-form-select {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n font-family: inherit;\n }\n\n .librebot-form-input:focus,\n .librebot-form-textarea:focus,\n .librebot-form-select:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-form-textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n .librebot-booking-submit {\n width: 100%;\n padding: 12px;\n border: none;\n border-radius: 8px;\n background: var(--lb-primary);\n color: white;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.2s;\n margin-top: 12px;\n }\n\n .librebot-booking-submit:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-booking-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Booking Success */\n .librebot-booking-success {\n text-align: center;\n padding: 24px 16px;\n }\n\n .librebot-booking-success-icon {\n width: 60px;\n height: 60px;\n margin: 0 auto 16px;\n background: var(--lb-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-booking-success-icon svg {\n width: 32px;\n height: 32px;\n fill: white;\n }\n\n .librebot-booking-success h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0 0 16px;\n }\n\n .librebot-booking-details {\n background: var(--lb-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n text-align: left;\n }\n\n .librebot-booking-detail {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--lb-border);\n font-size: 13px;\n }\n\n .librebot-booking-detail:last-child {\n border-bottom: none;\n }\n\n .librebot-booking-detail-label {\n color: var(--lb-text-secondary);\n }\n\n .librebot-booking-detail-value {\n color: var(--lb-text);\n font-weight: 500;\n }\n\n .librebot-meeting-link {\n display: inline-block;\n padding: 12px 24px;\n background: var(--lb-primary);\n color: white;\n text-decoration: none;\n border-radius: 8px;\n font-weight: 600;\n margin-bottom: 16px;\n transition: background 0.2s;\n }\n\n .librebot-meeting-link:hover {\n background: var(--lb-primary-hover);\n }\n\n /* RTL Booking Styles */\n .librebot-widget.rtl .librebot-booking-header {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-form-label .required {\n margin-left: 0;\n margin-right: 2px;\n }\n\n .librebot-widget.rtl .librebot-form-input,\n .librebot-widget.rtl .librebot-form-textarea {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-booking-detail {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-booking-details {\n text-align: right;\n }\n`;\n","export const chatIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path></svg>`;\n\nexport const closeIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>`;\n\nexport const sendIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line><polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon></svg>`;\n\nexport const botIcon = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>`;\n\nexport const backIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"></polyline></svg>`;\n\nexport const calendarIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line></svg>`;\n\nexport const checkIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"></polyline></svg>`;\n","export type SupportedLang = 'en' | 'fr' | 'de' | 'nl' | 'es' | 'pt' | 'ko' | 'ja' | 'zh' | 'hi' | 'ar';\n\nexport interface WidgetTranslations {\n placeholder: string;\n welcomeMessage: string;\n errorPrefix: string;\n connectionError: string;\n streamingNotSupported: string;\n poweredBy: string;\n // Booking translations\n bookingTitle: string;\n selectDate: string;\n selectTime: string;\n bookAppointment: string;\n noSlotsAvailable: string;\n loadingSlots: string;\n bookingSuccess: string;\n bookingError: string;\n backToChat: string;\n yourDetails: string;\n confirmBooking: string;\n meetingWith: string;\n duration: string;\n minutes: string;\n joinMeeting: string;\n}\n\nexport const WIDGET_TRANSLATIONS: Record<SupportedLang, WidgetTranslations> = {\n en: {\n placeholder: 'Ask a question...',\n welcomeMessage: 'Hi! I can help you find answers in the documentation. What would you like to know?',\n errorPrefix: 'Sorry, I encountered an error:',\n connectionError: 'Sorry, I had trouble connecting. Please try again.',\n streamingNotSupported: 'Sorry, streaming is not supported.',\n poweredBy: 'Powered by',\n bookingTitle: 'Book an Appointment',\n selectDate: 'Select a date',\n selectTime: 'Select a time',\n bookAppointment: 'Book Appointment',\n noSlotsAvailable: 'No time slots available for this date',\n loadingSlots: 'Loading available times...',\n bookingSuccess: 'Your appointment has been confirmed!',\n bookingError: 'Failed to book appointment. Please try again.',\n backToChat: 'Back to chat',\n yourDetails: 'Your Details',\n confirmBooking: 'Confirm Booking',\n meetingWith: 'Meeting',\n duration: 'Duration',\n minutes: 'minutes',\n joinMeeting: 'Join Meeting',\n },\n fr: {\n placeholder: 'Posez une question...',\n welcomeMessage: 'Bonjour ! Je peux vous aider à trouver des réponses dans la documentation. Que souhaitez-vous savoir ?',\n errorPrefix: 'Désolé, j\\'ai rencontré une erreur :',\n connectionError: 'Désolé, j\\'ai eu du mal à me connecter. Veuillez réessayer.',\n streamingNotSupported: 'Désolé, le streaming n\\'est pas pris en charge.',\n poweredBy: 'Propulsé par',\n bookingTitle: 'Prendre rendez-vous',\n selectDate: 'Choisir une date',\n selectTime: 'Choisir une heure',\n bookAppointment: 'Prendre rendez-vous',\n noSlotsAvailable: 'Aucun créneau disponible pour cette date',\n loadingSlots: 'Chargement des horaires...',\n bookingSuccess: 'Votre rendez-vous a été confirmé !',\n bookingError: 'Échec de la réservation. Veuillez réessayer.',\n backToChat: 'Retour au chat',\n yourDetails: 'Vos informations',\n confirmBooking: 'Confirmer la réservation',\n meetingWith: 'Réunion',\n duration: 'Durée',\n minutes: 'minutes',\n joinMeeting: 'Rejoindre la réunion',\n },\n de: {\n placeholder: 'Stellen Sie eine Frage...',\n welcomeMessage: 'Hallo! Ich kann Ihnen helfen, Antworten in der Dokumentation zu finden. Was möchten Sie wissen?',\n errorPrefix: 'Entschuldigung, ein Fehler ist aufgetreten:',\n connectionError: 'Entschuldigung, ich hatte Verbindungsprobleme. Bitte versuchen Sie es erneut.',\n streamingNotSupported: 'Entschuldigung, Streaming wird nicht unterstützt.',\n poweredBy: 'Betrieben von',\n bookingTitle: 'Termin buchen',\n selectDate: 'Datum auswählen',\n selectTime: 'Uhrzeit auswählen',\n bookAppointment: 'Termin buchen',\n noSlotsAvailable: 'Keine Termine für dieses Datum verfügbar',\n loadingSlots: 'Verfügbare Zeiten werden geladen...',\n bookingSuccess: 'Ihr Termin wurde bestätigt!',\n bookingError: 'Terminbuchung fehlgeschlagen. Bitte versuchen Sie es erneut.',\n backToChat: 'Zurück zum Chat',\n yourDetails: 'Ihre Angaben',\n confirmBooking: 'Buchung bestätigen',\n meetingWith: 'Meeting',\n duration: 'Dauer',\n minutes: 'Minuten',\n joinMeeting: 'An Meeting teilnehmen',\n },\n nl: {\n placeholder: 'Stel een vraag...',\n welcomeMessage: 'Hallo! Ik kan je helpen antwoorden te vinden in de documentatie. Wat wil je weten?',\n errorPrefix: 'Sorry, er is een fout opgetreden:',\n connectionError: 'Sorry, ik had moeite met verbinden. Probeer het opnieuw.',\n streamingNotSupported: 'Sorry, streaming wordt niet ondersteund.',\n poweredBy: 'Mogelijk gemaakt door',\n bookingTitle: 'Afspraak maken',\n selectDate: 'Selecteer een datum',\n selectTime: 'Selecteer een tijd',\n bookAppointment: 'Afspraak maken',\n noSlotsAvailable: 'Geen tijdsloten beschikbaar voor deze datum',\n loadingSlots: 'Beschikbare tijden laden...',\n bookingSuccess: 'Je afspraak is bevestigd!',\n bookingError: 'Afspraak maken mislukt. Probeer het opnieuw.',\n backToChat: 'Terug naar chat',\n yourDetails: 'Je gegevens',\n confirmBooking: 'Boeking bevestigen',\n meetingWith: 'Vergadering',\n duration: 'Duur',\n minutes: 'minuten',\n joinMeeting: 'Deelnemen aan vergadering',\n },\n es: {\n placeholder: 'Haz una pregunta...',\n welcomeMessage: '¡Hola! Puedo ayudarte a encontrar respuestas en la documentación. ¿Qué te gustaría saber?',\n errorPrefix: 'Lo siento, encontré un error:',\n connectionError: 'Lo siento, tuve problemas para conectarme. Por favor, inténtalo de nuevo.',\n streamingNotSupported: 'Lo siento, el streaming no está soportado.',\n poweredBy: 'Desarrollado por',\n bookingTitle: 'Reservar cita',\n selectDate: 'Seleccionar fecha',\n selectTime: 'Seleccionar hora',\n bookAppointment: 'Reservar cita',\n noSlotsAvailable: 'No hay horarios disponibles para esta fecha',\n loadingSlots: 'Cargando horarios disponibles...',\n bookingSuccess: '¡Tu cita ha sido confirmada!',\n bookingError: 'Error al reservar la cita. Por favor, inténtalo de nuevo.',\n backToChat: 'Volver al chat',\n yourDetails: 'Tus datos',\n confirmBooking: 'Confirmar reserva',\n meetingWith: 'Reunión',\n duration: 'Duración',\n minutes: 'minutos',\n joinMeeting: 'Unirse a la reunión',\n },\n pt: {\n placeholder: 'Faça uma pergunta...',\n welcomeMessage: 'Olá! Posso ajudá-lo a encontrar respostas na documentação. O que você gostaria de saber?',\n errorPrefix: 'Desculpe, encontrei um erro:',\n connectionError: 'Desculpe, tive problemas para conectar. Por favor, tente novamente.',\n streamingNotSupported: 'Desculpe, streaming não é suportado.',\n poweredBy: 'Desenvolvido por',\n bookingTitle: 'Agendar consulta',\n selectDate: 'Selecionar data',\n selectTime: 'Selecionar horário',\n bookAppointment: 'Agendar consulta',\n noSlotsAvailable: 'Nenhum horário disponível para esta data',\n loadingSlots: 'Carregando horários disponíveis...',\n bookingSuccess: 'Seu agendamento foi confirmado!',\n bookingError: 'Falha ao agendar. Por favor, tente novamente.',\n backToChat: 'Voltar ao chat',\n yourDetails: 'Seus dados',\n confirmBooking: 'Confirmar agendamento',\n meetingWith: 'Reunião',\n duration: 'Duração',\n minutes: 'minutos',\n joinMeeting: 'Entrar na reunião',\n },\n ko: {\n placeholder: '질문하세요...',\n welcomeMessage: '안녕하세요! 문서에서 답변을 찾는 데 도움을 드릴 수 있습니다. 무엇을 알고 싶으신가요?',\n errorPrefix: '죄송합니다. 오류가 발생했습니다:',\n connectionError: '죄송합니다. 연결에 문제가 있습니다. 다시 시도해 주세요.',\n streamingNotSupported: '죄송합니다. 스트리밍이 지원되지 않습니다.',\n poweredBy: '제공:',\n bookingTitle: '예약하기',\n selectDate: '날짜 선택',\n selectTime: '시간 선택',\n bookAppointment: '예약하기',\n noSlotsAvailable: '선택한 날짜에 가능한 시간이 없습니다',\n loadingSlots: '가능한 시간 로딩 중...',\n bookingSuccess: '예약이 확정되었습니다!',\n bookingError: '예약에 실패했습니다. 다시 시도해 주세요.',\n backToChat: '채팅으로 돌아가기',\n yourDetails: '정보 입력',\n confirmBooking: '예약 확정',\n meetingWith: '미팅',\n duration: '소요 시간',\n minutes: '분',\n joinMeeting: '미팅 참가',\n },\n ja: {\n placeholder: '質問してください...',\n welcomeMessage: 'こんにちは!ドキュメントから回答を見つけるお手伝いができます。何をお知りになりたいですか?',\n errorPrefix: '申し訳ありません。エラーが発生しました:',\n connectionError: '申し訳ありません。接続に問題がありました。もう一度お試しください。',\n streamingNotSupported: '申し訳ありません。ストリーミングはサポートされていません。',\n poweredBy: '提供:',\n bookingTitle: '予約する',\n selectDate: '日付を選択',\n selectTime: '時間を選択',\n bookAppointment: '予約する',\n noSlotsAvailable: 'この日は空き時間がありません',\n loadingSlots: '空き時間を読み込み中...',\n bookingSuccess: 'ご予約が確定しました!',\n bookingError: '予約に失敗しました。もう一度お試しください。',\n backToChat: 'チャットに戻る',\n yourDetails: 'お客様情報',\n confirmBooking: '予約を確定',\n meetingWith: 'ミーティング',\n duration: '所要時間',\n minutes: '分',\n joinMeeting: 'ミーティングに参加',\n },\n zh: {\n placeholder: '请提问...',\n welcomeMessage: '您好!我可以帮助您在文档中查找答案。您想了解什么?',\n errorPrefix: '抱歉,遇到了错误:',\n connectionError: '抱歉,连接出现问题。请重试。',\n streamingNotSupported: '抱歉,不支持流式传输。',\n poweredBy: '由以下提供支持:',\n bookingTitle: '预约',\n selectDate: '选择日期',\n selectTime: '选择时间',\n bookAppointment: '预约',\n noSlotsAvailable: '该日期没有可用时间段',\n loadingSlots: '加载可用时间...',\n bookingSuccess: '您的预约已确认!',\n bookingError: '预约失败。请重试。',\n backToChat: '返回聊天',\n yourDetails: '您的信息',\n confirmBooking: '确认预约',\n meetingWith: '会议',\n duration: '时长',\n minutes: '分钟',\n joinMeeting: '加入会议',\n },\n hi: {\n placeholder: 'कोई प्रश्न पूछें...',\n welcomeMessage: 'नमस्ते! मैं आपको दस्तावेज़ों में उत्तर खोजने में मदद कर सकता हूं। आप क्या जानना चाहते हैं?',\n errorPrefix: 'क्षमा करें, एक त्रुटि हुई:',\n connectionError: 'क्षमा करें, कनेक्ट करने में समस्या हुई। कृपया पुनः प्रयास करें।',\n streamingNotSupported: 'क्षमा करें, स्ट्रीमिंग समर्थित नहीं है।',\n poweredBy: 'द्वारा संचालित',\n bookingTitle: 'अपॉइंटमेंट बुक करें',\n selectDate: 'तिथि चुनें',\n selectTime: 'समय चुनें',\n bookAppointment: 'अपॉइंटमेंट बुक करें',\n noSlotsAvailable: 'इस तिथि के लिए कोई समय उपलब्ध नहीं है',\n loadingSlots: 'उपलब्ध समय लोड हो रहा है...',\n bookingSuccess: 'आपकी अपॉइंटमेंट की पुष्टि हो गई है!',\n bookingError: 'बुकिंग विफल। कृपया पुनः प्रयास करें।',\n backToChat: 'चैट पर वापस जाएं',\n yourDetails: 'आपका विवरण',\n confirmBooking: 'बुकिंग की पुष्टि करें',\n meetingWith: 'मीटिंग',\n duration: 'अवधि',\n minutes: 'मिनट',\n joinMeeting: 'मीटिंग में शामिल हों',\n },\n ar: {\n placeholder: 'اطرح سؤالاً...',\n welcomeMessage: 'مرحباً! يمكنني مساعدتك في العثور على إجابات في الوثائق. ماذا تريد أن تعرف؟',\n errorPrefix: 'عذراً، حدث خطأ:',\n connectionError: 'عذراً، واجهت مشكلة في الاتصال. يرجى المحاولة مرة أخرى.',\n streamingNotSupported: 'عذراً، البث غير مدعوم.',\n poweredBy: 'مدعوم من',\n bookingTitle: 'حجز موعد',\n selectDate: 'اختر التاريخ',\n selectTime: 'اختر الوقت',\n bookAppointment: 'حجز موعد',\n noSlotsAvailable: 'لا توجد أوقات متاحة لهذا التاريخ',\n loadingSlots: 'جاري تحميل الأوقات المتاحة...',\n bookingSuccess: 'تم تأكيد موعدك!',\n bookingError: 'فشل الحجز. يرجى المحاولة مرة أخرى.',\n backToChat: 'العودة إلى المحادثة',\n yourDetails: 'بياناتك',\n confirmBooking: 'تأكيد الحجز',\n meetingWith: 'اجتماع',\n duration: 'المدة',\n minutes: 'دقيقة',\n joinMeeting: 'انضم إلى الاجتماع',\n },\n};\n\nexport const RTL_LANGUAGES: SupportedLang[] = ['ar'];\n\nexport function detectLanguage(): SupportedLang {\n // 1. Check document's lang attribute\n const htmlLang = document.documentElement.lang?.toLowerCase().split('-')[0];\n if (htmlLang && htmlLang in WIDGET_TRANSLATIONS) {\n return htmlLang as SupportedLang;\n }\n\n // 2. Check navigator.language\n const navLang = navigator.language?.toLowerCase().split('-')[0];\n if (navLang && navLang in WIDGET_TRANSLATIONS) {\n return navLang as SupportedLang;\n }\n\n // 3. Default to English\n return 'en';\n}\n","import { LibreBotConfig, Message, ChatResponse, BookingConfig, TimeSlot, BookingResult } from './types';\nimport { getStyles } from './styles';\nimport { chatIcon, closeIcon, sendIcon, backIcon, checkIcon } from './icons';\nimport {\n SupportedLang,\n WidgetTranslations,\n WIDGET_TRANSLATIONS,\n RTL_LANGUAGES,\n detectLanguage\n} from './translations';\n\ntype WidgetMode = 'chat' | 'booking';\n\nexport class LibreBotWidget {\n private config!: Required<LibreBotConfig>;\n private container: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private messagesContainer: HTMLDivElement | null = null;\n private input: HTMLInputElement | null = null;\n private isOpen = false;\n private messages: Message[] = [];\n private isLoading = false;\n private sessionId: string | null = null;\n private translations: WidgetTranslations = WIDGET_TRANSLATIONS.en;\n private isRtl = false;\n\n // Booking mode state\n private mode: WidgetMode = 'chat';\n private bookingConfig: BookingConfig | null = null;\n private selectedDate: string | null = null;\n private selectedSlot: TimeSlot | null = null;\n private availableSlots: TimeSlot[] = [];\n private bookingContainer: HTMLDivElement | null = null;\n private chatContainer: HTMLDivElement | null = null;\n private inputArea: HTMLDivElement | null = null;\n\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://librebot.io/api',\n position: 'bottom-right',\n theme: 'dark',\n primaryColor: '#14b8a6',\n title: 'Libre Bot',\n subtitle: 'AI Documentation Assistant',\n placeholder: '', // Will be set from translations\n welcomeMessage: '', // Will be set from translations\n streaming: true,\n whiteLabel: false,\n autoLoadConfig: true,\n lang: 'en',\n };\n\n constructor(config: LibreBotConfig) {\n if (!config.apiKey) {\n console.error('LibreBot: API key is required');\n return;\n }\n\n // Detect language from config or host page\n const detectedLang = config.lang || detectLanguage();\n this.translations = WIDGET_TRANSLATIONS[detectedLang] || WIDGET_TRANSLATIONS.en;\n this.isRtl = RTL_LANGUAGES.includes(detectedLang);\n\n // Apply defaults with translations\n const configWithTranslations: Partial<LibreBotConfig> = {\n ...config,\n lang: detectedLang,\n placeholder: config.placeholder || this.translations.placeholder,\n welcomeMessage: config.welcomeMessage || this.translations.welcomeMessage,\n };\n\n this.config = { ...this.defaultConfig, ...configWithTranslations } as Required<LibreBotConfig>;\n\n // Auto-load config from server if enabled and no custom settings provided\n if (this.config.autoLoadConfig) {\n this.loadRemoteConfig().then(() => this.init());\n } else {\n this.init();\n }\n }\n\n private async loadRemoteConfig(): Promise<void> {\n try {\n const response = await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);\n if (response.ok) {\n const remoteConfig = await response.json();\n // Merge remote config with local config (local overrides remote)\n this.config = {\n ...this.defaultConfig,\n ...remoteConfig,\n ...this.getProvidedConfig(),\n apiKey: this.config.apiKey,\n apiUrl: this.config.apiUrl,\n autoLoadConfig: this.config.autoLoadConfig,\n } as Required<LibreBotConfig>;\n }\n } catch (error) {\n console.warn('LibreBot: Could not load remote config, using defaults', error);\n }\n }\n\n private getProvidedConfig(): Partial<LibreBotConfig> {\n // Return only the config options that were explicitly provided (not default values)\n const provided: Partial<LibreBotConfig> = {};\n const userConfig = this.config;\n const defaults = this.defaultConfig;\n\n if (userConfig.position !== defaults.position) provided.position = userConfig.position;\n if (userConfig.theme !== defaults.theme) provided.theme = userConfig.theme;\n if (userConfig.primaryColor !== defaults.primaryColor) provided.primaryColor = userConfig.primaryColor;\n if (userConfig.title !== defaults.title) provided.title = userConfig.title;\n if (userConfig.subtitle !== defaults.subtitle) provided.subtitle = userConfig.subtitle;\n if (userConfig.placeholder !== defaults.placeholder) provided.placeholder = userConfig.placeholder;\n if (userConfig.welcomeMessage !== defaults.welcomeMessage) provided.welcomeMessage = userConfig.welcomeMessage;\n if (userConfig.streaming !== defaults.streaming) provided.streaming = userConfig.streaming;\n if (userConfig.whiteLabel !== defaults.whiteLabel) provided.whiteLabel = userConfig.whiteLabel;\n\n return provided;\n }\n\n private init(): void {\n // Load sessionId from localStorage for persistent memory\n this.loadSession();\n\n // Inject styles\n this.injectStyles();\n\n // Create widget container\n this.createWidget();\n\n // Add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n }\n\n private getSessionKey(): string {\n return `librebot_session_${this.config.apiKey}`;\n }\n\n private loadSession(): void {\n try {\n const stored = localStorage.getItem(this.getSessionKey());\n if (stored) {\n this.sessionId = stored;\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n private saveSession(sessionId: string): void {\n try {\n this.sessionId = sessionId;\n localStorage.setItem(this.getSessionKey(), sessionId);\n } catch {\n // localStorage may not be available\n }\n }\n\n private injectStyles(): void {\n const styleId = 'librebot-styles';\n if (document.getElementById(styleId)) return;\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = getStyles(this.config.primaryColor);\n document.head.appendChild(style);\n }\n\n private createWidget(): void {\n // Create container\n this.container = document.createElement('div');\n let className = 'librebot-widget';\n if (this.config.theme === 'light') className += ' light';\n if (this.isRtl) className += ' rtl';\n this.container.className = className;\n\n // Create FAB button\n const fab = document.createElement('button');\n fab.className = `librebot-fab ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n fab.innerHTML = chatIcon;\n fab.onclick = () => this.toggle();\n\n // Create modal\n this.modal = document.createElement('div');\n this.modal.className = `librebot-modal ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n this.modal.innerHTML = this.getModalHTML();\n\n // Add to container\n this.container.appendChild(fab);\n this.container.appendChild(this.modal);\n document.body.appendChild(this.container);\n\n // Get references\n this.messagesContainer = this.modal.querySelector('.librebot-messages');\n this.input = this.modal.querySelector('.librebot-input');\n this.chatContainer = this.modal.querySelector('.librebot-chat-container');\n this.bookingContainer = this.modal.querySelector('.librebot-booking-container');\n this.inputArea = this.modal.querySelector('.librebot-input-area');\n\n // Add event listeners\n const closeBtn = this.modal.querySelector('.librebot-close');\n closeBtn?.addEventListener('click', () => this.close());\n\n const sendBtn = this.modal.querySelector('.librebot-send');\n sendBtn?.addEventListener('click', () => this.sendMessage());\n\n this.input?.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.sendMessage();\n }\n });\n\n // Auto theme detection\n if (this.config.theme === 'auto') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: light)');\n this.updateTheme(mediaQuery.matches);\n mediaQuery.addEventListener('change', (e) => this.updateTheme(e.matches));\n }\n }\n\n private getModalHTML(): string {\n const poweredByHtml = this.config.whiteLabel\n ? ''\n : `<div class=\"librebot-powered\">\n ${this.translations.poweredBy} <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>`;\n\n return `\n <div class=\"librebot-header\">\n <div class=\"librebot-header-content\">\n <div class=\"librebot-avatar\">${chatIcon}</div>\n <div>\n <h3 class=\"librebot-title\">${this.config.title}</h3>\n <p class=\"librebot-subtitle\">${this.config.subtitle}</p>\n </div>\n </div>\n <button class=\"librebot-close\">${closeIcon}</button>\n </div>\n <div class=\"librebot-chat-container\">\n <div class=\"librebot-messages\"></div>\n </div>\n <div class=\"librebot-booking-container\" style=\"display: none;\"></div>\n <div class=\"librebot-input-area\">\n <input type=\"text\" class=\"librebot-input\" placeholder=\"${this.config.placeholder}\" />\n <button class=\"librebot-send\">${sendIcon}</button>\n </div>\n ${poweredByHtml}\n `;\n }\n\n private updateTheme(isLight: boolean): void {\n if (isLight) {\n this.container?.classList.add('light');\n } else {\n this.container?.classList.remove('light');\n }\n }\n\n public toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n public open(): void {\n this.isOpen = true;\n this.modal?.classList.add('open');\n this.input?.focus();\n }\n\n public close(): void {\n this.isOpen = false;\n this.modal?.classList.remove('open');\n }\n\n private addMessage(role: 'user' | 'assistant', content: string): void {\n const message: Message = {\n id: `msg-${Date.now()}`,\n role,\n content,\n timestamp: new Date(),\n };\n\n this.messages.push(message);\n this.renderMessage(message);\n }\n\n private renderMessage(message: Message): void {\n if (!this.messagesContainer) return;\n\n const el = document.createElement('div');\n el.className = `librebot-message ${message.role}`;\n el.innerHTML = this.formatMessage(message.content);\n\n this.messagesContainer.appendChild(el);\n this.scrollToBottom();\n }\n\n private formatMessage(content: string): string {\n // Escape HTML to prevent XSS\n const escapeHtml = (text: string): string => {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n };\n\n // First, extract code blocks to protect them from other formatting\n // Regex handles optional space/newline after ``` and optional language identifier\n const codeBlocks: string[] = [];\n let processed = content.replace(/```\\s*(\\w*)\\s*\\n?([\\s\\S]*?)```/g, (_, lang, code) => {\n const escaped = escapeHtml(code.trim());\n const langClass = lang ? ` data-language=\"${escapeHtml(lang)}\"` : '';\n codeBlocks.push(`<pre class=\"librebot-code-block\"${langClass}><code>${escaped}</code></pre>`);\n return `%%CODEBLOCK${codeBlocks.length - 1}%%`;\n });\n\n // Extract inline code\n const inlineCodes: string[] = [];\n processed = processed.replace(/`([^`]+)`/g, (_, code) => {\n inlineCodes.push(`<code class=\"librebot-inline-code\">${escapeHtml(code)}</code>`);\n return `%%INLINECODE${inlineCodes.length - 1}%%`;\n });\n\n // Now escape remaining HTML\n processed = escapeHtml(processed);\n\n // Headers (## and ###)\n processed = processed.replace(/^### (.+)$/gm, '<h4 class=\"librebot-h4\">$1</h4>');\n processed = processed.replace(/^## (.+)$/gm, '<h3 class=\"librebot-h3\">$1</h3>');\n processed = processed.replace(/^# (.+)$/gm, '<h2 class=\"librebot-h2\">$1</h2>');\n\n // Bold and italic\n processed = processed.replace(/\\*\\*\\*([^*]+)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n processed = processed.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\n processed = processed.replace(/_([^_]+)_/g, '<em>$1</em>');\n\n // Links [text](url)\n processed = processed.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"librebot-link\">$1</a>');\n\n // Unordered lists\n processed = processed.replace(/^[\\*\\-] (.+)$/gm, '<li class=\"librebot-li\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ul class=\"librebot-ul\">${match}</ul>`;\n });\n\n // Ordered lists\n processed = processed.replace(/^\\d+\\. (.+)$/gm, '<li class=\"librebot-li-ordered\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li-ordered\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ol class=\"librebot-ol\">${match}</ol>`;\n });\n\n // Horizontal rule\n processed = processed.replace(/^---$/gm, '<hr class=\"librebot-hr\">');\n\n // Blockquotes\n processed = processed.replace(/^> (.+)$/gm, '<blockquote class=\"librebot-blockquote\">$1</blockquote>');\n\n // Convert newlines to <br> (but not inside lists/blockquotes)\n processed = processed.replace(/\\n/g, '<br>');\n\n // Clean up extra <br> after block elements\n processed = processed.replace(/<\\/(pre|ul|ol|blockquote|h[2-4])><br>/g, '</$1>');\n processed = processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g, '<$1');\n\n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n processed = processed.replace(`%%CODEBLOCK${i}%%`, block);\n });\n\n // Restore inline code\n inlineCodes.forEach((code, i) => {\n processed = processed.replace(`%%INLINECODE${i}%%`, code);\n });\n\n return processed;\n }\n\n private showTyping(): HTMLDivElement {\n const typing = document.createElement('div');\n typing.className = 'librebot-typing';\n typing.innerHTML = '<span></span><span></span><span></span>';\n this.messagesContainer?.appendChild(typing);\n this.scrollToBottom();\n return typing;\n }\n\n private scrollToBottom(): void {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n }\n\n private async sendMessage(): Promise<void> {\n if (!this.input || this.isLoading) return;\n\n const content = this.input.value.trim();\n if (!content) return;\n\n // Add user message\n this.addMessage('user', content);\n this.input.value = '';\n\n // Show typing indicator\n this.isLoading = true;\n const typing = this.showTyping();\n\n try {\n if (this.config.streaming) {\n await this.callStreamingAPI(content, typing);\n } else {\n const response = await this.callAPI(content);\n typing.remove();\n\n if (response.error) {\n this.addMessage('assistant', `${this.translations.errorPrefix} ${response.error}`);\n } else {\n this.addMessage('assistant', response.response);\n // Check for booking intent\n this.handleBookingIntent(response);\n }\n }\n } catch (error) {\n typing.remove();\n this.addMessage('assistant', this.translations.connectionError);\n console.error('LibreBot error:', error);\n } finally {\n this.isLoading = false;\n }\n }\n\n private async callStreamingAPI(message: string, typing: HTMLDivElement): Promise<void> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n stream: true,\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n typing.remove();\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n this.addMessage('assistant', `${this.translations.errorPrefix} ${error.error || 'Request failed'}`);\n return;\n }\n\n // Remove typing indicator and create streaming message element\n typing.remove();\n const messageEl = document.createElement('div');\n messageEl.className = 'librebot-message assistant';\n this.messagesContainer?.appendChild(messageEl);\n\n const reader = response.body?.getReader();\n if (!reader) {\n this.addMessage('assistant', this.translations.streamingNotSupported);\n return;\n }\n\n const decoder = new TextDecoder();\n let fullContent = '';\n let bookingIntentData: ChatResponse | null = null;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n const lines = chunk.split('\\n').filter((line) => line.trim() !== '');\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n if (parsed.content) {\n fullContent += parsed.content;\n messageEl.innerHTML = this.formatMessage(fullContent);\n this.scrollToBottom();\n }\n // Save sessionId when received from server\n if (parsed.sessionId) {\n this.saveSession(parsed.sessionId);\n }\n // Check for booking intent in metadata\n if (parsed.booking_intent && parsed.booking_config) {\n bookingIntentData = parsed;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream reading error:', error);\n }\n\n // Add to messages array\n this.messages.push({\n id: `msg-${Date.now()}`,\n role: 'assistant',\n content: fullContent,\n timestamp: new Date(),\n });\n\n // Handle booking intent if detected\n if (bookingIntentData) {\n this.handleBookingIntent(bookingIntentData);\n }\n }\n\n private async callAPI(message: string): Promise<ChatResponse> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n return { response: '', error: error.error || 'Request failed' };\n }\n\n const data = await response.json();\n // Save sessionId when received from server\n if (data.sessionId) {\n this.saveSession(data.sessionId);\n }\n return data;\n }\n\n private getConversationContext(): Array<{ role: string; content: string }> {\n // Get last 10 messages for context\n return this.messages.slice(-10).map((m) => ({\n role: m.role,\n content: m.content,\n }));\n }\n\n public destroy(): void {\n this.container?.remove();\n document.getElementById('librebot-styles')?.remove();\n }\n\n public clearSession(): void {\n try {\n this.sessionId = null;\n this.messages = [];\n localStorage.removeItem(this.getSessionKey());\n if (this.messagesContainer) {\n this.messagesContainer.innerHTML = '';\n }\n // Re-add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n // Booking Mode Methods\n\n private switchToBookingMode(config: BookingConfig): void {\n this.bookingConfig = config;\n this.mode = 'booking';\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Hide chat, show booking\n if (this.chatContainer) this.chatContainer.style.display = 'none';\n if (this.inputArea) this.inputArea.style.display = 'none';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'flex';\n this.renderBookingUI();\n }\n }\n\n private switchToChatMode(): void {\n this.mode = 'chat';\n this.bookingConfig = null;\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Show chat, hide booking\n if (this.chatContainer) this.chatContainer.style.display = 'flex';\n if (this.inputArea) this.inputArea.style.display = 'flex';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'none';\n this.bookingContainer.innerHTML = '';\n }\n }\n\n private renderBookingUI(): void {\n if (!this.bookingContainer || !this.bookingConfig) return;\n\n const minDate = new Date();\n minDate.setDate(minDate.getDate() + 1);\n const maxDate = new Date();\n maxDate.setDate(maxDate.getDate() + (this.bookingConfig.advance_booking_days || 30));\n\n const formFieldsHtml = this.bookingConfig.form_fields.map(field => {\n const requiredMark = field.required ? '<span class=\"required\">*</span>' : '';\n const requiredAttr = field.required ? 'required' : '';\n\n if (field.type === 'textarea') {\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <textarea\n class=\"librebot-form-textarea\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n ></textarea>\n </div>\n `;\n }\n\n if (field.type === 'select' && field.options) {\n const optionsHtml = field.options.map(opt =>\n `<option value=\"${opt}\">${opt}</option>`\n ).join('');\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <select\n class=\"librebot-form-select\"\n name=\"${field.name}\"\n ${requiredAttr}\n >\n <option value=\"\">${field.placeholder || 'Select...'}</option>\n ${optionsHtml}\n </select>\n </div>\n `;\n }\n\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <input\n type=\"${field.type}\"\n class=\"librebot-form-input\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n />\n </div>\n `;\n }).join('');\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectDate}</label>\n <input\n type=\"date\"\n class=\"librebot-date-input\"\n min=\"${minDate.toISOString().split('T')[0]}\"\n max=\"${maxDate.toISOString().split('T')[0]}\"\n />\n </div>\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectTime}</label>\n <div class=\"librebot-slots-container\">\n <div class=\"librebot-slots-empty\">${this.translations.selectDate}</div>\n </div>\n </div>\n <div class=\"librebot-booking-section librebot-form-section\" style=\"display: none;\">\n <label class=\"librebot-booking-label\">${this.translations.yourDetails}</label>\n <form class=\"librebot-booking-form\">\n ${formFieldsHtml}\n <button type=\"submit\" class=\"librebot-booking-submit\" disabled>\n ${this.translations.confirmBooking}\n </button>\n </form>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const dateInput = this.bookingContainer.querySelector('.librebot-date-input') as HTMLInputElement;\n dateInput?.addEventListener('change', (e) => {\n const target = e.target as HTMLInputElement;\n this.selectedDate = target.value;\n this.selectedSlot = null;\n this.loadAvailableSlots(target.value);\n });\n\n const form = this.bookingContainer.querySelector('.librebot-booking-form') as HTMLFormElement;\n form?.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submitBooking(form);\n });\n }\n\n private async loadAvailableSlots(date: string): Promise<void> {\n const slotsContainer = this.bookingContainer?.querySelector('.librebot-slots-container');\n if (!slotsContainer) return;\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots-loading\">${this.translations.loadingSlots}</div>`;\n\n try {\n const response = await fetch(\n `${this.config.apiUrl}/bookings/slots?date=${date}&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,\n {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to load slots');\n }\n\n const data = await response.json();\n this.availableSlots = data.slots || [];\n\n if (this.availableSlots.length === 0) {\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.noSlotsAvailable}</div>`;\n return;\n }\n\n const slotsHtml = this.availableSlots.map(slot => {\n const disabled = !slot.available ? 'disabled' : '';\n return `\n <button\n type=\"button\"\n class=\"librebot-slot\"\n data-start=\"${slot.start}\"\n data-end=\"${slot.end}\"\n ${disabled}\n >${slot.start}</button>\n `;\n }).join('');\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots\">${slotsHtml}</div>`;\n\n // Add click handlers to slot buttons\n const slotButtons = slotsContainer.querySelectorAll('.librebot-slot');\n slotButtons.forEach(btn => {\n btn.addEventListener('click', () => {\n const start = btn.getAttribute('data-start') || '';\n const end = btn.getAttribute('data-end') || '';\n this.selectSlot({ start, end, available: true }, btn);\n });\n });\n } catch (err) {\n console.error('Failed to load slots:', err);\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.bookingError}</div>`;\n }\n }\n\n private selectSlot(slot: TimeSlot, buttonEl: Element): void {\n this.selectedSlot = slot;\n\n // Update button styles\n const allSlots = this.bookingContainer?.querySelectorAll('.librebot-slot');\n allSlots?.forEach(el => el.classList.remove('selected'));\n buttonEl.classList.add('selected');\n\n // Show form section\n const formSection = this.bookingContainer?.querySelector('.librebot-form-section') as HTMLElement;\n if (formSection) {\n formSection.style.display = 'block';\n }\n\n // Enable submit button\n const submitBtn = this.bookingContainer?.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = false;\n }\n\n // Scroll to form\n formSection?.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n\n private async submitBooking(form: HTMLFormElement): Promise<void> {\n if (!this.selectedDate || !this.selectedSlot || !this.bookingConfig) return;\n\n const submitBtn = form.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = true;\n submitBtn.textContent = '...';\n }\n\n // Collect form data\n const formData = new FormData(form);\n const formDataObj: Record<string, string> = {};\n formData.forEach((value, key) => {\n formDataObj[key] = value.toString();\n });\n\n try {\n const response = await fetch(`${this.config.apiUrl}/bookings/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n date: this.selectedDate,\n slot_start: this.selectedSlot.start,\n slot_end: this.selectedSlot.end,\n form_data: formDataObj,\n session_id: this.sessionId,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n }),\n });\n\n const result: BookingResult = await response.json();\n\n if (result.success && result.booking) {\n this.renderBookingSuccess(result.booking);\n } else {\n throw new Error(result.error || 'Booking failed');\n }\n } catch (err) {\n console.error('Booking submission failed:', err);\n if (submitBtn) {\n submitBtn.disabled = false;\n submitBtn.textContent = this.translations.confirmBooking;\n }\n alert(this.translations.bookingError);\n }\n }\n\n private renderBookingSuccess(booking: NonNullable<BookingResult['booking']>): void {\n if (!this.bookingContainer) return;\n\n // Format the date and time\n const startDate = new Date(booking.scheduled_start);\n const formattedDate = startDate.toLocaleDateString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n const formattedTime = startDate.toLocaleTimeString(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n });\n\n const meetingLinkHtml = booking.meeting_link\n ? `<a href=\"${booking.meeting_link}\" target=\"_blank\" class=\"librebot-meeting-link\">${this.translations.joinMeeting}</a>`\n : '';\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-success\">\n <div class=\"librebot-booking-success-icon\">${checkIcon}</div>\n <h3>${this.translations.bookingSuccess}</h3>\n <div class=\"librebot-booking-details\">\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectDate}</span>\n <span class=\"librebot-booking-detail-value\">${formattedDate}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectTime}</span>\n <span class=\"librebot-booking-detail-value\">${formattedTime}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.duration}</span>\n <span class=\"librebot-booking-detail-value\">${booking.duration_minutes} ${this.translations.minutes}</span>\n </div>\n </div>\n ${meetingLinkHtml}\n <button class=\"librebot-booking-submit\" type=\"button\">${this.translations.backToChat}</button>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const chatBtn = this.bookingContainer.querySelector('.librebot-booking-submit');\n chatBtn?.addEventListener('click', () => this.switchToChatMode());\n }\n\n private handleBookingIntent(data: ChatResponse): void {\n if (data.booking_intent && data.booking_config) {\n // Add a brief delay so user can see the AI response before switching\n setTimeout(() => {\n this.switchToBookingMode(data.booking_config!);\n }, 1500);\n }\n }\n}\n","import { LibreBotWidget } from './widget';\nimport { LibreBotConfig } from './types';\nimport { SupportedLang } from './translations';\n\n// Auto-initialize from script tag\n(async function () {\n // Find the script tag - document.currentScript may be null with defer\n const script = document.currentScript as HTMLScriptElement\n || document.querySelector('script[data-key][src*=\"librebot\"]') as HTMLScriptElement;\n\n if (!script) {\n console.warn('LibreBot: Could not find script tag');\n return;\n }\n\n const apiKey = script.dataset.key || script.dataset.apiKey || '';\n const apiUrl = script.dataset.apiUrl || 'https://api.librebot.io';\n\n if (!apiKey) {\n console.error('LibreBot: Missing data-key attribute');\n return;\n }\n\n // Fetch remote config from dashboard settings\n let remoteConfig: Partial<LibreBotConfig> = {};\n try {\n const response = await fetch(`${apiUrl}/api/widget-config?key=${encodeURIComponent(apiKey)}`);\n if (response.ok) {\n remoteConfig = await response.json();\n }\n } catch (e) {\n // Silently fail - use defaults/data attributes\n }\n\n // Build config: remote settings as base, data attributes as overrides\n const config: LibreBotConfig = {\n apiKey,\n apiUrl,\n // Remote config as defaults, data attributes override\n position: (script.dataset.position as 'bottom-right' | 'bottom-left') || remoteConfig.position || 'bottom-right',\n theme: (script.dataset.theme as 'light' | 'dark' | 'auto') || remoteConfig.theme || 'dark',\n primaryColor: script.dataset.color || remoteConfig.primaryColor || '#14b8a6',\n title: script.dataset.title || remoteConfig.title || 'Libre Bot',\n subtitle: script.dataset.subtitle || remoteConfig.subtitle || 'AI Documentation Assistant',\n placeholder: script.dataset.placeholder || remoteConfig.placeholder || undefined, // Let language detection handle default\n welcomeMessage: script.dataset.welcome || remoteConfig.welcomeMessage || undefined, // Let language detection handle default\n streaming: script.dataset.streaming !== 'false', // default true\n whiteLabel: remoteConfig.whiteLabel || false,\n lang: (script.dataset.lang as SupportedLang) || remoteConfig.lang, // Allow explicit language override\n };\n\n // Initialize when DOM is ready\n const initWidget = () => {\n (window as any).LibreBot = new LibreBotWidget(config);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initWidget);\n } else {\n initWidget();\n }\n})();\n\n// Export for manual initialization\nexport { LibreBotWidget, LibreBotConfig };\n"],"names":["chatIcon","backIcon","WIDGET_TRANSLATIONS","en","placeholder","welcomeMessage","errorPrefix","connectionError","streamingNotSupported","poweredBy","bookingTitle","selectDate","selectTime","bookAppointment","noSlotsAvailable","loadingSlots","bookingSuccess","bookingError","backToChat","yourDetails","confirmBooking","meetingWith","duration","minutes","joinMeeting","fr","de","nl","es","pt","ko","ja","zh","hi","ar","RTL_LANGUAGES","LibreBotWidget","constructor","config","this","container","modal","messagesContainer","input","isOpen","messages","isLoading","sessionId","translations","isRtl","mode","bookingConfig","selectedDate","selectedSlot","availableSlots","bookingContainer","chatContainer","inputArea","defaultConfig","apiUrl","position","theme","primaryColor","title","subtitle","streaming","whiteLabel","autoLoadConfig","lang","apiKey","console","error","detectedLang","htmlLang","document","documentElement","toLowerCase","split","navLang","navigator","language","detectLanguage","includes","configWithTranslations","loadRemoteConfig","then","init","response","fetch","ok","remoteConfig","json","getProvidedConfig","warn","provided","userConfig","defaults","loadSession","injectStyles","createWidget","addMessage","getSessionKey","stored","localStorage","getItem","saveSession","setItem","styleId","getElementById","style","createElement","id","textContent","getStyles","head","appendChild","className","fab","innerHTML","onclick","toggle","getModalHTML","body","querySelector","closeBtn","addEventListener","close","sendBtn","sendMessage","e","key","shiftKey","preventDefault","mediaQuery","window","matchMedia","updateTheme","matches","poweredByHtml","isLight","classList","add","remove","open","focus","role","content","message","Date","now","timestamp","push","renderMessage","el","formatMessage","scrollToBottom","escapeHtml","text","replace","codeBlocks","processed","_","code","escaped","trim","langClass","length","inlineCodes","match","forEach","block","i","showTyping","typing","scrollTop","scrollHeight","value","callStreamingAPI","callAPI","handleBookingIntent","method","headers","Authorization","JSON","stringify","context","getConversationContext","stream","catch","messageEl","reader","getReader","decoder","TextDecoder","fullContent","bookingIntentData","done","read","lines","decode","filter","line","startsWith","data","slice","parsed","parse","booking_intent","booking_config","map","m","destroy","clearSession","removeItem","switchToBookingMode","display","renderBookingUI","switchToChatMode","minDate","setDate","getDate","maxDate","advance_booking_days","formFieldsHtml","form_fields","field","requiredMark","required","requiredAttr","type","label","name","options","optionsHtml","opt","join","toISOString","backBtn","dateInput","target","loadAvailableSlots","form","submitBooking","date","slotsContainer","Intl","DateTimeFormat","resolvedOptions","timeZone","Error","slots","slotsHtml","slot","disabled","available","start","end","querySelectorAll","btn","getAttribute","selectSlot","err","buttonEl","allSlots","formSection","submitBtn","scrollIntoView","behavior","formData","FormData","formDataObj","toString","slot_start","slot_end","form_data","session_id","timezone","result","success","booking","renderBookingSuccess","alert","startDate","scheduled_start","formattedDate","toLocaleDateString","undefined","weekday","year","month","day","formattedTime","toLocaleTimeString","hour","minute","meetingLinkHtml","meeting_link","duration_minutes","chatBtn","setTimeout","script","currentScript","dataset","encodeURIComponent","color","welcome","initWidget","LibreBot","readyState"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type SupportedLang = 'en' | 'fr' | 'de' | 'nl' | 'es' | 'pt' | 'ko' | 'ja' | 'zh' | 'hi' | 'ar';
|
|
2
|
+
export interface WidgetTranslations {
|
|
3
|
+
placeholder: string;
|
|
4
|
+
welcomeMessage: string;
|
|
5
|
+
errorPrefix: string;
|
|
6
|
+
connectionError: string;
|
|
7
|
+
streamingNotSupported: string;
|
|
8
|
+
poweredBy: string;
|
|
9
|
+
bookingTitle: string;
|
|
10
|
+
selectDate: string;
|
|
11
|
+
selectTime: string;
|
|
12
|
+
bookAppointment: string;
|
|
13
|
+
noSlotsAvailable: string;
|
|
14
|
+
loadingSlots: string;
|
|
15
|
+
bookingSuccess: string;
|
|
16
|
+
bookingError: string;
|
|
17
|
+
backToChat: string;
|
|
18
|
+
yourDetails: string;
|
|
19
|
+
confirmBooking: string;
|
|
20
|
+
meetingWith: string;
|
|
21
|
+
duration: string;
|
|
22
|
+
minutes: string;
|
|
23
|
+
joinMeeting: string;
|
|
24
|
+
}
|
|
25
|
+
export declare const WIDGET_TRANSLATIONS: Record<SupportedLang, WidgetTranslations>;
|
|
26
|
+
export declare const RTL_LANGUAGES: SupportedLang[];
|
|
27
|
+
export declare function detectLanguage(): SupportedLang;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SupportedLang } from './translations';
|
|
1
2
|
export interface LibreBotConfig {
|
|
2
3
|
apiKey: string;
|
|
3
4
|
apiUrl?: string;
|
|
@@ -11,6 +12,7 @@ export interface LibreBotConfig {
|
|
|
11
12
|
streaming?: boolean;
|
|
12
13
|
whiteLabel?: boolean;
|
|
13
14
|
autoLoadConfig?: boolean;
|
|
15
|
+
lang?: SupportedLang;
|
|
14
16
|
}
|
|
15
17
|
export interface Message {
|
|
16
18
|
id: string;
|
|
@@ -31,4 +33,53 @@ export interface ChatResponse {
|
|
|
31
33
|
limit: number;
|
|
32
34
|
};
|
|
33
35
|
error?: string;
|
|
36
|
+
booking_intent?: boolean;
|
|
37
|
+
booking_config?: BookingConfig;
|
|
38
|
+
}
|
|
39
|
+
export interface BookingFormField {
|
|
40
|
+
name: string;
|
|
41
|
+
label: string;
|
|
42
|
+
type: 'text' | 'email' | 'tel' | 'textarea' | 'select';
|
|
43
|
+
required: boolean;
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
options?: string[];
|
|
46
|
+
}
|
|
47
|
+
export interface BookingConfig {
|
|
48
|
+
is_enabled: boolean;
|
|
49
|
+
form_fields: BookingFormField[];
|
|
50
|
+
default_duration_minutes: number;
|
|
51
|
+
buffer_minutes: number;
|
|
52
|
+
timezone: string;
|
|
53
|
+
availability_schedule: Record<string, {
|
|
54
|
+
start: string;
|
|
55
|
+
end: string;
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
advance_booking_days: number;
|
|
59
|
+
}
|
|
60
|
+
export interface TimeSlot {
|
|
61
|
+
start: string;
|
|
62
|
+
end: string;
|
|
63
|
+
available: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface BookingSubmission {
|
|
66
|
+
date: string;
|
|
67
|
+
slot_start: string;
|
|
68
|
+
slot_end: string;
|
|
69
|
+
form_data: Record<string, string>;
|
|
70
|
+
session_id?: string;
|
|
71
|
+
timezone?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface BookingResult {
|
|
74
|
+
success: boolean;
|
|
75
|
+
booking?: {
|
|
76
|
+
id: string;
|
|
77
|
+
scheduled_start: string;
|
|
78
|
+
scheduled_end: string;
|
|
79
|
+
duration_minutes: number;
|
|
80
|
+
timezone: string;
|
|
81
|
+
meeting_link?: string;
|
|
82
|
+
status: string;
|
|
83
|
+
};
|
|
84
|
+
error?: string;
|
|
34
85
|
}
|
package/dist/widget.d.ts
CHANGED
|
@@ -9,6 +9,16 @@ export declare class LibreBotWidget {
|
|
|
9
9
|
private messages;
|
|
10
10
|
private isLoading;
|
|
11
11
|
private sessionId;
|
|
12
|
+
private translations;
|
|
13
|
+
private isRtl;
|
|
14
|
+
private mode;
|
|
15
|
+
private bookingConfig;
|
|
16
|
+
private selectedDate;
|
|
17
|
+
private selectedSlot;
|
|
18
|
+
private availableSlots;
|
|
19
|
+
private bookingContainer;
|
|
20
|
+
private chatContainer;
|
|
21
|
+
private inputArea;
|
|
12
22
|
private defaultConfig;
|
|
13
23
|
constructor(config: LibreBotConfig);
|
|
14
24
|
private loadRemoteConfig;
|
|
@@ -35,4 +45,12 @@ export declare class LibreBotWidget {
|
|
|
35
45
|
private getConversationContext;
|
|
36
46
|
destroy(): void;
|
|
37
47
|
clearSession(): void;
|
|
48
|
+
private switchToBookingMode;
|
|
49
|
+
private switchToChatMode;
|
|
50
|
+
private renderBookingUI;
|
|
51
|
+
private loadAvailableSlots;
|
|
52
|
+
private selectSlot;
|
|
53
|
+
private submitBooking;
|
|
54
|
+
private renderBookingSuccess;
|
|
55
|
+
private handleBookingIntent;
|
|
38
56
|
}
|