@kroonen-ai/librebot-widget 1.0.2 → 1.1.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.
@@ -178,7 +178,154 @@ const getStyles = (primaryColor = '#14b8a6') => `
178
178
  border-bottom-left-radius: 4px;
179
179
  }
180
180
 
181
- .librebot-message code {
181
+ /* Inline code */
182
+ .librebot-inline-code {
183
+ background: rgba(0, 0, 0, 0.3);
184
+ padding: 2px 6px;
185
+ border-radius: 4px;
186
+ font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;
187
+ font-size: 12px;
188
+ color: #f0f0f0;
189
+ }
190
+
191
+ .librebot-widget.light .librebot-inline-code {
192
+ background: rgba(0, 0, 0, 0.08);
193
+ color: #1a1a1a;
194
+ }
195
+
196
+ /* Code blocks */
197
+ .librebot-code-block {
198
+ background: #0d1117;
199
+ padding: 12px 14px;
200
+ border-radius: 8px;
201
+ overflow-x: auto;
202
+ margin: 10px 0;
203
+ border: 1px solid rgba(255, 255, 255, 0.1);
204
+ position: relative;
205
+ }
206
+
207
+ .librebot-widget.light .librebot-code-block {
208
+ background: #f6f8fa;
209
+ border-color: rgba(0, 0, 0, 0.1);
210
+ }
211
+
212
+ .librebot-code-block code {
213
+ font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;
214
+ font-size: 12px;
215
+ line-height: 1.5;
216
+ color: #e6edf3;
217
+ background: none;
218
+ padding: 0;
219
+ white-space: pre;
220
+ display: block;
221
+ }
222
+
223
+ .librebot-widget.light .librebot-code-block code {
224
+ color: #24292f;
225
+ }
226
+
227
+ .librebot-code-block[data-language]::before {
228
+ content: attr(data-language);
229
+ position: absolute;
230
+ top: 6px;
231
+ right: 10px;
232
+ font-size: 10px;
233
+ color: rgba(255, 255, 255, 0.4);
234
+ text-transform: uppercase;
235
+ font-family: 'Roboto Mono', monospace;
236
+ }
237
+
238
+ .librebot-widget.light .librebot-code-block[data-language]::before {
239
+ color: rgba(0, 0, 0, 0.3);
240
+ }
241
+
242
+ /* Headers */
243
+ .librebot-h2 {
244
+ font-size: 16px;
245
+ font-weight: 600;
246
+ margin: 12px 0 8px 0;
247
+ color: var(--lb-text);
248
+ }
249
+
250
+ .librebot-h3 {
251
+ font-size: 14px;
252
+ font-weight: 600;
253
+ margin: 10px 0 6px 0;
254
+ color: var(--lb-text);
255
+ }
256
+
257
+ .librebot-h4 {
258
+ font-size: 13px;
259
+ font-weight: 600;
260
+ margin: 8px 0 4px 0;
261
+ color: var(--lb-text);
262
+ }
263
+
264
+ /* Lists */
265
+ .librebot-ul, .librebot-ol {
266
+ margin: 8px 0;
267
+ padding-left: 20px;
268
+ }
269
+
270
+ .librebot-ul {
271
+ list-style-type: disc;
272
+ }
273
+
274
+ .librebot-ol {
275
+ list-style-type: decimal;
276
+ }
277
+
278
+ .librebot-li, .librebot-li-ordered {
279
+ margin: 4px 0;
280
+ padding-left: 4px;
281
+ line-height: 1.5;
282
+ }
283
+
284
+ /* Links */
285
+ .librebot-link {
286
+ color: var(--lb-primary);
287
+ text-decoration: none;
288
+ border-bottom: 1px solid transparent;
289
+ transition: border-color 0.2s;
290
+ }
291
+
292
+ .librebot-link:hover {
293
+ border-bottom-color: var(--lb-primary);
294
+ }
295
+
296
+ /* Blockquotes */
297
+ .librebot-blockquote {
298
+ border-left: 3px solid var(--lb-primary);
299
+ margin: 8px 0;
300
+ padding: 4px 12px;
301
+ color: var(--lb-text-secondary);
302
+ font-style: italic;
303
+ background: rgba(0, 0, 0, 0.1);
304
+ border-radius: 0 6px 6px 0;
305
+ }
306
+
307
+ .librebot-widget.light .librebot-blockquote {
308
+ background: rgba(0, 0, 0, 0.04);
309
+ }
310
+
311
+ /* Horizontal rule */
312
+ .librebot-hr {
313
+ border: none;
314
+ border-top: 1px solid var(--lb-border);
315
+ margin: 12px 0;
316
+ }
317
+
318
+ /* Strong and emphasis */
319
+ .librebot-message strong {
320
+ font-weight: 600;
321
+ }
322
+
323
+ .librebot-message em {
324
+ font-style: italic;
325
+ }
326
+
327
+ /* Legacy support for old code elements */
328
+ .librebot-message code:not(.librebot-inline-code) {
182
329
  background: rgba(0, 0, 0, 0.2);
183
330
  padding: 2px 6px;
184
331
  border-radius: 4px;
@@ -186,7 +333,7 @@ const getStyles = (primaryColor = '#14b8a6') => `
186
333
  font-size: 13px;
187
334
  }
188
335
 
189
- .librebot-message pre {
336
+ .librebot-message pre:not(.librebot-code-block) {
190
337
  background: rgba(0, 0, 0, 0.3);
191
338
  padding: 10px;
192
339
  border-radius: 8px;
@@ -194,7 +341,7 @@ const getStyles = (primaryColor = '#14b8a6') => `
194
341
  margin: 8px 0;
195
342
  }
196
343
 
197
- .librebot-message pre code {
344
+ .librebot-message pre:not(.librebot-code-block) code {
198
345
  background: none;
199
346
  padding: 0;
200
347
  }
@@ -336,7 +483,7 @@ class LibreBotWidget {
336
483
  this.messages = [];
337
484
  this.isLoading = false;
338
485
  this.defaultConfig = {
339
- apiUrl: 'https://api.librebot.io',
486
+ apiUrl: 'https://librebot.io/api',
340
487
  position: 'bottom-right',
341
488
  theme: 'dark',
342
489
  primaryColor: '#14b8a6',
@@ -345,13 +492,66 @@ class LibreBotWidget {
345
492
  placeholder: 'Ask a question...',
346
493
  welcomeMessage: 'Hi! I can help you find answers in the documentation. What would you like to know?',
347
494
  streaming: true,
495
+ whiteLabel: false,
496
+ autoLoadConfig: true,
348
497
  };
349
498
  if (!config.apiKey) {
350
499
  console.error('LibreBot: API key is required');
351
500
  return;
352
501
  }
353
502
  this.config = { ...this.defaultConfig, ...config };
354
- this.init();
503
+ // Auto-load config from server if enabled and no custom settings provided
504
+ if (this.config.autoLoadConfig) {
505
+ this.loadRemoteConfig().then(() => this.init());
506
+ }
507
+ else {
508
+ this.init();
509
+ }
510
+ }
511
+ async loadRemoteConfig() {
512
+ try {
513
+ const response = await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);
514
+ if (response.ok) {
515
+ const remoteConfig = await response.json();
516
+ // Merge remote config with local config (local overrides remote)
517
+ this.config = {
518
+ ...this.defaultConfig,
519
+ ...remoteConfig,
520
+ ...this.getProvidedConfig(),
521
+ apiKey: this.config.apiKey,
522
+ apiUrl: this.config.apiUrl,
523
+ autoLoadConfig: this.config.autoLoadConfig,
524
+ };
525
+ }
526
+ }
527
+ catch (error) {
528
+ console.warn('LibreBot: Could not load remote config, using defaults', error);
529
+ }
530
+ }
531
+ getProvidedConfig() {
532
+ // Return only the config options that were explicitly provided (not default values)
533
+ const provided = {};
534
+ const userConfig = this.config;
535
+ const defaults = this.defaultConfig;
536
+ if (userConfig.position !== defaults.position)
537
+ provided.position = userConfig.position;
538
+ if (userConfig.theme !== defaults.theme)
539
+ provided.theme = userConfig.theme;
540
+ if (userConfig.primaryColor !== defaults.primaryColor)
541
+ provided.primaryColor = userConfig.primaryColor;
542
+ if (userConfig.title !== defaults.title)
543
+ provided.title = userConfig.title;
544
+ if (userConfig.subtitle !== defaults.subtitle)
545
+ provided.subtitle = userConfig.subtitle;
546
+ if (userConfig.placeholder !== defaults.placeholder)
547
+ provided.placeholder = userConfig.placeholder;
548
+ if (userConfig.welcomeMessage !== defaults.welcomeMessage)
549
+ provided.welcomeMessage = userConfig.welcomeMessage;
550
+ if (userConfig.streaming !== defaults.streaming)
551
+ provided.streaming = userConfig.streaming;
552
+ if (userConfig.whiteLabel !== defaults.whiteLabel)
553
+ provided.whiteLabel = userConfig.whiteLabel;
554
+ return provided;
355
555
  }
356
556
  init() {
357
557
  // Inject styles
@@ -411,6 +611,11 @@ class LibreBotWidget {
411
611
  }
412
612
  }
413
613
  getModalHTML() {
614
+ const poweredByHtml = this.config.whiteLabel
615
+ ? ''
616
+ : `<div class="librebot-powered">
617
+ Powered by <a href="https://librebot.io" target="_blank">Libre Bot</a>
618
+ </div>`;
414
619
  return `
415
620
  <div class="librebot-header">
416
621
  <div class="librebot-header-content">
@@ -427,9 +632,7 @@ class LibreBotWidget {
427
632
  <input type="text" class="librebot-input" placeholder="${this.config.placeholder}" />
428
633
  <button class="librebot-send">${sendIcon}</button>
429
634
  </div>
430
- <div class="librebot-powered">
431
- Powered by <a href="https://librebot.io" target="_blank">Libre Bot</a>
432
- </div>
635
+ ${poweredByHtml}
433
636
  `;
434
637
  }
435
638
  updateTheme(isLight) {
@@ -472,11 +675,70 @@ class LibreBotWidget {
472
675
  this.scrollToBottom();
473
676
  }
474
677
  formatMessage(content) {
475
- // Basic markdown-like formatting
476
- return content
477
- .replace(/`([^`]+)`/g, '<code>$1</code>')
478
- .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
479
- .replace(/\n/g, '<br>');
678
+ // Escape HTML to prevent XSS
679
+ const escapeHtml = (text) => {
680
+ return text
681
+ .replace(/&/g, '&amp;')
682
+ .replace(/</g, '&lt;')
683
+ .replace(/>/g, '&gt;')
684
+ .replace(/"/g, '&quot;')
685
+ .replace(/'/g, '&#039;');
686
+ };
687
+ // First, extract code blocks to protect them from other formatting
688
+ const codeBlocks = [];
689
+ let processed = content.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
690
+ const escaped = escapeHtml(code.trim());
691
+ const langClass = lang ? ` data-language="${escapeHtml(lang)}"` : '';
692
+ codeBlocks.push(`<pre class="librebot-code-block"${langClass}><code>${escaped}</code></pre>`);
693
+ return `%%CODEBLOCK${codeBlocks.length - 1}%%`;
694
+ });
695
+ // Extract inline code
696
+ const inlineCodes = [];
697
+ processed = processed.replace(/`([^`]+)`/g, (_, code) => {
698
+ inlineCodes.push(`<code class="librebot-inline-code">${escapeHtml(code)}</code>`);
699
+ return `%%INLINECODE${inlineCodes.length - 1}%%`;
700
+ });
701
+ // Now escape remaining HTML
702
+ processed = escapeHtml(processed);
703
+ // Headers (## and ###)
704
+ processed = processed.replace(/^### (.+)$/gm, '<h4 class="librebot-h4">$1</h4>');
705
+ processed = processed.replace(/^## (.+)$/gm, '<h3 class="librebot-h3">$1</h3>');
706
+ processed = processed.replace(/^# (.+)$/gm, '<h2 class="librebot-h2">$1</h2>');
707
+ // Bold and italic
708
+ processed = processed.replace(/\*\*\*([^*]+)\*\*\*/g, '<strong><em>$1</em></strong>');
709
+ processed = processed.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
710
+ processed = processed.replace(/\*([^*]+)\*/g, '<em>$1</em>');
711
+ processed = processed.replace(/_([^_]+)_/g, '<em>$1</em>');
712
+ // Links [text](url)
713
+ processed = processed.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" class="librebot-link">$1</a>');
714
+ // Unordered lists
715
+ processed = processed.replace(/^[\*\-] (.+)$/gm, '<li class="librebot-li">$1</li>');
716
+ processed = processed.replace(/(<li class="librebot-li">.*<\/li>\n?)+/g, (match) => {
717
+ return `<ul class="librebot-ul">${match}</ul>`;
718
+ });
719
+ // Ordered lists
720
+ processed = processed.replace(/^\d+\. (.+)$/gm, '<li class="librebot-li-ordered">$1</li>');
721
+ processed = processed.replace(/(<li class="librebot-li-ordered">.*<\/li>\n?)+/g, (match) => {
722
+ return `<ol class="librebot-ol">${match}</ol>`;
723
+ });
724
+ // Horizontal rule
725
+ processed = processed.replace(/^---$/gm, '<hr class="librebot-hr">');
726
+ // Blockquotes
727
+ processed = processed.replace(/^&gt; (.+)$/gm, '<blockquote class="librebot-blockquote">$1</blockquote>');
728
+ // Convert newlines to <br> (but not inside lists/blockquotes)
729
+ processed = processed.replace(/\n/g, '<br>');
730
+ // Clean up extra <br> after block elements
731
+ processed = processed.replace(/<\/(pre|ul|ol|blockquote|h[2-4])><br>/g, '</$1>');
732
+ processed = processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g, '<$1');
733
+ // Restore code blocks
734
+ codeBlocks.forEach((block, i) => {
735
+ processed = processed.replace(`%%CODEBLOCK${i}%%`, block);
736
+ });
737
+ // Restore inline code
738
+ inlineCodes.forEach((code, i) => {
739
+ processed = processed.replace(`%%INLINECODE${i}%%`, code);
740
+ });
741
+ return processed;
480
742
  }
481
743
  showTyping() {
482
744
  const typing = document.createElement('div');
@@ -528,7 +790,7 @@ class LibreBotWidget {
528
790
  }
529
791
  }
530
792
  async callStreamingAPI(message, typing) {
531
- const response = await fetch(`${this.config.apiUrl}/api/chat`, {
793
+ const response = await fetch(`${this.config.apiUrl}/chat`, {
532
794
  method: 'POST',
533
795
  headers: {
534
796
  'Content-Type': 'application/json',
@@ -597,7 +859,7 @@ class LibreBotWidget {
597
859
  });
598
860
  }
599
861
  async callAPI(message) {
600
- const response = await fetch(`${this.config.apiUrl}/api/chat`, {
862
+ const response = await fetch(`${this.config.apiUrl}/chat`, {
601
863
  method: 'POST',
602
864
  headers: {
603
865
  'Content-Type': 'application/json',
@@ -1 +1 @@
1
- {"version":3,"file":"librebot.esm.js","sources":["../src/styles.ts","../src/icons.ts","../src/widget.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 .librebot-message 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 {\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 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\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://api.librebot.io',\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 };\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 this.init();\n }\n\n private init(): void {\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 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 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 <div class=\"librebot-powered\">\n Powered by <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>\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 // Basic markdown-like formatting\n return content\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\n/g, '<br>');\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}/api/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 }),\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 } 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}/api/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 }),\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 return response.json();\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"librebot.esm.js","sources":["../src/styles.ts","../src/icons.ts","../src/widget.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\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 // 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 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n };\n\n // First, extract code blocks to protect them from other formatting\n const codeBlocks: string[] = [];\n let processed = content.replace(/```(\\w*)\\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(/^&gt; (.+)$/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 }),\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 } 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 }),\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 return response.json();\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"],"names":[],"mappings":"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"}
package/dist/librebot.js CHANGED
@@ -1,2 +1,2 @@
1
- var LibreBot=function(e){"use strict";const n='<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>';class t{constructor(e){this.container=null,this.modal=null,this.messagesContainer=null,this.input=null,this.isOpen=!1,this.messages=[],this.isLoading=!1,this.defaultConfig={apiUrl:"https://api.librebot.io",position:"bottom-right",theme:"dark",primaryColor:"#14b8a6",title:"Libre Bot",subtitle:"AI Documentation Assistant",placeholder:"Ask a question...",welcomeMessage:"Hi! I can help you find answers in the documentation. What would you like to know?",streaming:!0},e.apiKey?(this.config={...this.defaultConfig,...e},this.init()):console.error("LibreBot: API key is required")}init(){this.injectStyles(),this.createWidget(),this.config.welcomeMessage&&this.addMessage("assistant",this.config.welcomeMessage)}injectStyles(){const e="librebot-styles";if(document.getElementById(e))return;const n=document.createElement("style");n.id=e,n.textContent=((e="#14b8a6")=>`\n .librebot-widget {\n --lb-primary: ${e};\n --lb-primary-hover: color-mix(in srgb, ${e} 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 .librebot-message 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 {\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 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`)(this.config.primaryColor),document.head.appendChild(n)}createWidget(){this.container=document.createElement("div"),this.container.className="librebot-widget "+("light"===this.config.theme?"light":"");const e=document.createElement("button");e.className="librebot-fab "+("bottom-left"===this.config.position?"left":""),e.innerHTML=n,e.onclick=()=>this.toggle(),this.modal=document.createElement("div"),this.modal.className="librebot-modal "+("bottom-left"===this.config.position?"left":""),this.modal.innerHTML=this.getModalHTML(),this.container.appendChild(e),this.container.appendChild(this.modal),document.body.appendChild(this.container),this.messagesContainer=this.modal.querySelector(".librebot-messages"),this.input=this.modal.querySelector(".librebot-input");const t=this.modal.querySelector(".librebot-close");t?.addEventListener("click",()=>this.close());const o=this.modal.querySelector(".librebot-send");if(o?.addEventListener("click",()=>this.sendMessage()),this.input?.addEventListener("keypress",e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage())}),"auto"===this.config.theme){const e=window.matchMedia("(prefers-color-scheme: light)");this.updateTheme(e.matches),e.addEventListener("change",e=>this.updateTheme(e.matches))}}getModalHTML(){return`\n <div class="librebot-header">\n <div class="librebot-header-content">\n <div class="librebot-avatar">${n}</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"><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></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"><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></button>\n </div>\n <div class="librebot-powered">\n Powered by <a href="https://librebot.io" target="_blank">Libre Bot</a>\n </div>\n `}updateTheme(e){e?this.container?.classList.add("light"):this.container?.classList.remove("light")}toggle(){this.isOpen?this.close():this.open()}open(){this.isOpen=!0,this.modal?.classList.add("open"),this.input?.focus()}close(){this.isOpen=!1,this.modal?.classList.remove("open")}addMessage(e,n){const t={id:`msg-${Date.now()}`,role:e,content:n,timestamp:new Date};this.messages.push(t),this.renderMessage(t)}renderMessage(e){if(!this.messagesContainer)return;const n=document.createElement("div");n.className=`librebot-message ${e.role}`,n.innerHTML=this.formatMessage(e.content),this.messagesContainer.appendChild(n),this.scrollToBottom()}formatMessage(e){return e.replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/\n/g,"<br>")}showTyping(){const e=document.createElement("div");return e.className="librebot-typing",e.innerHTML="<span></span><span></span><span></span>",this.messagesContainer?.appendChild(e),this.scrollToBottom(),e}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}async sendMessage(){if(!this.input||this.isLoading)return;const e=this.input.value.trim();if(!e)return;this.addMessage("user",e),this.input.value="",this.isLoading=!0;const n=this.showTyping();try{if(this.config.streaming)await this.callStreamingAPI(e,n);else{const t=await this.callAPI(e);n.remove(),t.error?this.addMessage("assistant",`Sorry, I encountered an error: ${t.error}`):this.addMessage("assistant",t.response)}}catch(e){n.remove(),this.addMessage("assistant","Sorry, I had trouble connecting. Please try again."),console.error("LibreBot error:",e)}finally{this.isLoading=!1}}async callStreamingAPI(e,n){const t=await fetch(`${this.config.apiUrl}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext(),stream:!0})});if(!t.ok){n.remove();const e=await t.json().catch(()=>({error:"Request failed"}));return void this.addMessage("assistant",`Sorry, I encountered an error: ${e.error||"Request failed"}`)}n.remove();const o=document.createElement("div");o.className="librebot-message assistant",this.messagesContainer?.appendChild(o);const i=t.body?.getReader();if(!i)return void this.addMessage("assistant","Sorry, streaming is not supported.");const r=new TextDecoder;let s="";try{for(;;){const{done:e,value:n}=await i.read();if(e)break;const t=r.decode(n,{stream:!0}).split("\n").filter(e=>""!==e.trim());for(const e of t)if(e.startsWith("data: ")){const n=e.slice(6);if("[DONE]"===n)continue;try{const e=JSON.parse(n);e.content&&(s+=e.content,o.innerHTML=this.formatMessage(s),this.scrollToBottom())}catch{}}}}catch(e){console.error("Stream reading error:",e)}this.messages.push({id:`msg-${Date.now()}`,role:"assistant",content:s,timestamp:new Date})}async callAPI(e){const n=await fetch(`${this.config.apiUrl}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext()})});if(!n.ok){return{response:"",error:(await n.json().catch(()=>({error:"Request failed"}))).error||"Request failed"}}return n.json()}getConversationContext(){return this.messages.slice(-10).map(e=>({role:e.role,content:e.content}))}destroy(){this.container?.remove(),document.getElementById("librebot-styles")?.remove()}}return function(){const e=document.currentScript||document.querySelector('script[data-key][src*="librebot"]');if(!e)return void console.warn("LibreBot: Could not find script tag");const n={apiKey:e.dataset.key||e.dataset.apiKey||"",apiUrl:e.dataset.apiUrl||"https://api.librebot.io",position:e.dataset.position||"bottom-right",theme:e.dataset.theme||"dark",primaryColor:e.dataset.color||"#14b8a6",title:e.dataset.title||"Libre Bot",subtitle:e.dataset.subtitle||"AI Documentation Assistant",placeholder:e.dataset.placeholder||"Ask a question...",welcomeMessage:e.dataset.welcome||"Hi! I can help you find answers in the documentation. What would you like to know?",streaming:"false"!==e.dataset.streaming};n.apiKey?"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{window.LibreBot=new t(n)}):window.LibreBot=new t(n):console.error("LibreBot: Missing data-key attribute")}(),e.LibreBotWidget=t,e}({});
1
+ var LibreBot=function(e){"use strict";const n='<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>';class t{constructor(e){this.container=null,this.modal=null,this.messagesContainer=null,this.input=null,this.isOpen=!1,this.messages=[],this.isLoading=!1,this.defaultConfig={apiUrl:"https://librebot.io/api",position:"bottom-right",theme:"dark",primaryColor:"#14b8a6",title:"Libre Bot",subtitle:"AI Documentation Assistant",placeholder:"Ask a question...",welcomeMessage:"Hi! I can help you find answers in the documentation. What would you like to know?",streaming:!0,whiteLabel:!1,autoLoadConfig:!0},e.apiKey?(this.config={...this.defaultConfig,...e},this.config.autoLoadConfig?this.loadRemoteConfig().then(()=>this.init()):this.init()):console.error("LibreBot: API key is required")}async loadRemoteConfig(){try{const e=await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);if(e.ok){const n=await e.json();this.config={...this.defaultConfig,...n,...this.getProvidedConfig(),apiKey:this.config.apiKey,apiUrl:this.config.apiUrl,autoLoadConfig:this.config.autoLoadConfig}}}catch(e){console.warn("LibreBot: Could not load remote config, using defaults",e)}}getProvidedConfig(){const e={},n=this.config,t=this.defaultConfig;return n.position!==t.position&&(e.position=n.position),n.theme!==t.theme&&(e.theme=n.theme),n.primaryColor!==t.primaryColor&&(e.primaryColor=n.primaryColor),n.title!==t.title&&(e.title=n.title),n.subtitle!==t.subtitle&&(e.subtitle=n.subtitle),n.placeholder!==t.placeholder&&(e.placeholder=n.placeholder),n.welcomeMessage!==t.welcomeMessage&&(e.welcomeMessage=n.welcomeMessage),n.streaming!==t.streaming&&(e.streaming=n.streaming),n.whiteLabel!==t.whiteLabel&&(e.whiteLabel=n.whiteLabel),e}init(){this.injectStyles(),this.createWidget(),this.config.welcomeMessage&&this.addMessage("assistant",this.config.welcomeMessage)}injectStyles(){const e="librebot-styles";if(document.getElementById(e))return;const n=document.createElement("style");n.id=e,n.textContent=((e="#14b8a6")=>`\n .librebot-widget {\n --lb-primary: ${e};\n --lb-primary-hover: color-mix(in srgb, ${e} 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`)(this.config.primaryColor),document.head.appendChild(n)}createWidget(){this.container=document.createElement("div"),this.container.className="librebot-widget "+("light"===this.config.theme?"light":"");const e=document.createElement("button");e.className="librebot-fab "+("bottom-left"===this.config.position?"left":""),e.innerHTML=n,e.onclick=()=>this.toggle(),this.modal=document.createElement("div"),this.modal.className="librebot-modal "+("bottom-left"===this.config.position?"left":""),this.modal.innerHTML=this.getModalHTML(),this.container.appendChild(e),this.container.appendChild(this.modal),document.body.appendChild(this.container),this.messagesContainer=this.modal.querySelector(".librebot-messages"),this.input=this.modal.querySelector(".librebot-input");const t=this.modal.querySelector(".librebot-close");t?.addEventListener("click",()=>this.close());const o=this.modal.querySelector(".librebot-send");if(o?.addEventListener("click",()=>this.sendMessage()),this.input?.addEventListener("keypress",e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage())}),"auto"===this.config.theme){const e=window.matchMedia("(prefers-color-scheme: light)");this.updateTheme(e.matches),e.addEventListener("change",e=>this.updateTheme(e.matches))}}getModalHTML(){const e=this.config.whiteLabel?"":'<div class="librebot-powered">\n Powered by <a href="https://librebot.io" target="_blank">Libre Bot</a>\n </div>';return`\n <div class="librebot-header">\n <div class="librebot-header-content">\n <div class="librebot-avatar">${n}</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"><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></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"><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></button>\n </div>\n ${e}\n `}updateTheme(e){e?this.container?.classList.add("light"):this.container?.classList.remove("light")}toggle(){this.isOpen?this.close():this.open()}open(){this.isOpen=!0,this.modal?.classList.add("open"),this.input?.focus()}close(){this.isOpen=!1,this.modal?.classList.remove("open")}addMessage(e,n){const t={id:`msg-${Date.now()}`,role:e,content:n,timestamp:new Date};this.messages.push(t),this.renderMessage(t)}renderMessage(e){if(!this.messagesContainer)return;const n=document.createElement("div");n.className=`librebot-message ${e.role}`,n.innerHTML=this.formatMessage(e.content),this.messagesContainer.appendChild(n),this.scrollToBottom()}formatMessage(e){const n=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),t=[];let o=e.replace(/```(\w*)\n?([\s\S]*?)```/g,(e,o,i)=>{const r=n(i.trim()),a=o?` data-language="${n(o)}"`:"";return t.push(`<pre class="librebot-code-block"${a}><code>${r}</code></pre>`),`%%CODEBLOCK${t.length-1}%%`});const i=[];return o=o.replace(/`([^`]+)`/g,(e,t)=>(i.push(`<code class="librebot-inline-code">${n(t)}</code>`),`%%INLINECODE${i.length-1}%%`)),o=n(o),o=o.replace(/^### (.+)$/gm,'<h4 class="librebot-h4">$1</h4>'),o=o.replace(/^## (.+)$/gm,'<h3 class="librebot-h3">$1</h3>'),o=o.replace(/^# (.+)$/gm,'<h2 class="librebot-h2">$1</h2>'),o=o.replace(/\*\*\*([^*]+)\*\*\*/g,"<strong><em>$1</em></strong>"),o=o.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),o=o.replace(/\*([^*]+)\*/g,"<em>$1</em>"),o=o.replace(/_([^_]+)_/g,"<em>$1</em>"),o=o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer" class="librebot-link">$1</a>'),o=o.replace(/^[\*\-] (.+)$/gm,'<li class="librebot-li">$1</li>'),o=o.replace(/(<li class="librebot-li">.*<\/li>\n?)+/g,e=>`<ul class="librebot-ul">${e}</ul>`),o=o.replace(/^\d+\. (.+)$/gm,'<li class="librebot-li-ordered">$1</li>'),o=o.replace(/(<li class="librebot-li-ordered">.*<\/li>\n?)+/g,e=>`<ol class="librebot-ol">${e}</ol>`),o=o.replace(/^---$/gm,'<hr class="librebot-hr">'),o=o.replace(/^&gt; (.+)$/gm,'<blockquote class="librebot-blockquote">$1</blockquote>'),o=o.replace(/\n/g,"<br>"),o=o.replace(/<\/(pre|ul|ol|blockquote|h[2-4])><br>/g,"</$1>"),o=o.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g,"<$1"),t.forEach((e,n)=>{o=o.replace(`%%CODEBLOCK${n}%%`,e)}),i.forEach((e,n)=>{o=o.replace(`%%INLINECODE${n}%%`,e)}),o}showTyping(){const e=document.createElement("div");return e.className="librebot-typing",e.innerHTML="<span></span><span></span><span></span>",this.messagesContainer?.appendChild(e),this.scrollToBottom(),e}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}async sendMessage(){if(!this.input||this.isLoading)return;const e=this.input.value.trim();if(!e)return;this.addMessage("user",e),this.input.value="",this.isLoading=!0;const n=this.showTyping();try{if(this.config.streaming)await this.callStreamingAPI(e,n);else{const t=await this.callAPI(e);n.remove(),t.error?this.addMessage("assistant",`Sorry, I encountered an error: ${t.error}`):this.addMessage("assistant",t.response)}}catch(e){n.remove(),this.addMessage("assistant","Sorry, I had trouble connecting. Please try again."),console.error("LibreBot error:",e)}finally{this.isLoading=!1}}async callStreamingAPI(e,n){const t=await fetch(`${this.config.apiUrl}/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext(),stream:!0})});if(!t.ok){n.remove();const e=await t.json().catch(()=>({error:"Request failed"}));return void this.addMessage("assistant",`Sorry, I encountered an error: ${e.error||"Request failed"}`)}n.remove();const o=document.createElement("div");o.className="librebot-message assistant",this.messagesContainer?.appendChild(o);const i=t.body?.getReader();if(!i)return void this.addMessage("assistant","Sorry, streaming is not supported.");const r=new TextDecoder;let a="";try{for(;;){const{done:e,value:n}=await i.read();if(e)break;const t=r.decode(n,{stream:!0}).split("\n").filter(e=>""!==e.trim());for(const e of t)if(e.startsWith("data: ")){const n=e.slice(6);if("[DONE]"===n)continue;try{const e=JSON.parse(n);e.content&&(a+=e.content,o.innerHTML=this.formatMessage(a),this.scrollToBottom())}catch{}}}}catch(e){console.error("Stream reading error:",e)}this.messages.push({id:`msg-${Date.now()}`,role:"assistant",content:a,timestamp:new Date})}async callAPI(e){const n=await fetch(`${this.config.apiUrl}/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext()})});if(!n.ok){return{response:"",error:(await n.json().catch(()=>({error:"Request failed"}))).error||"Request failed"}}return n.json()}getConversationContext(){return this.messages.slice(-10).map(e=>({role:e.role,content:e.content}))}destroy(){this.container?.remove(),document.getElementById("librebot-styles")?.remove()}}return function(){const e=document.currentScript||document.querySelector('script[data-key][src*="librebot"]');if(!e)return void console.warn("LibreBot: Could not find script tag");const n={apiKey:e.dataset.key||e.dataset.apiKey||"",apiUrl:e.dataset.apiUrl||"https://api.librebot.io",position:e.dataset.position||"bottom-right",theme:e.dataset.theme||"dark",primaryColor:e.dataset.color||"#14b8a6",title:e.dataset.title||"Libre Bot",subtitle:e.dataset.subtitle||"AI Documentation Assistant",placeholder:e.dataset.placeholder||"Ask a question...",welcomeMessage:e.dataset.welcome||"Hi! I can help you find answers in the documentation. What would you like to know?",streaming:"false"!==e.dataset.streaming};n.apiKey?"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{window.LibreBot=new t(n)}):window.LibreBot=new t(n):console.error("LibreBot: Missing data-key attribute")}(),e.LibreBotWidget=t,e}({});
2
2
  //# sourceMappingURL=librebot.js.map
@@ -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 .librebot-message 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 {\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 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\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://api.librebot.io',\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 };\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 this.init();\n }\n\n private init(): void {\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 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 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 <div class=\"librebot-powered\">\n Powered by <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>\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 // Basic markdown-like formatting\n return content\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\n/g, '<br>');\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}/api/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 }),\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 } 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}/api/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 }),\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 return response.json();\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","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","defaultConfig","apiUrl","position","theme","primaryColor","title","subtitle","placeholder","welcomeMessage","streaming","apiKey","init","console","error","injectStyles","createWidget","addMessage","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","isLight","classList","add","remove","open","focus","role","content","message","Date","now","timestamp","push","renderMessage","el","formatMessage","scrollToBottom","replace","showTyping","typing","scrollTop","scrollHeight","value","trim","callStreamingAPI","response","callAPI","fetch","method","headers","Authorization","JSON","stringify","context","getConversationContext","stream","ok","json","catch","messageEl","reader","getReader","decoder","TextDecoder","fullContent","done","read","lines","decode","split","filter","line","startsWith","data","slice","parsed","parse","map","m","destroy","script","currentScript","warn","dataset","color","welcome","readyState","LibreBot"],"mappings":"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"}
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\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 // 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 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n };\n\n // First, extract code blocks to protect them from other formatting\n const codeBlocks: string[] = [];\n let processed = content.replace(/```(\\w*)\\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(/^&gt; (.+)$/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 }),\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 } 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 }),\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 return response.json();\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","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","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","injectStyles","createWidget","addMessage","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","script","currentScript","dataset","color","welcome","readyState","LibreBot"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface LibreBotConfig {
9
9
  placeholder?: string;
10
10
  welcomeMessage?: string;
11
11
  streaming?: boolean;
12
+ whiteLabel?: boolean;
13
+ autoLoadConfig?: boolean;
12
14
  }
13
15
  export interface Message {
14
16
  id: string;
package/dist/widget.d.ts CHANGED
@@ -10,6 +10,8 @@ export declare class LibreBotWidget {
10
10
  private isLoading;
11
11
  private defaultConfig;
12
12
  constructor(config: LibreBotConfig);
13
+ private loadRemoteConfig;
14
+ private getProvidedConfig;
13
15
  private init;
14
16
  private injectStyles;
15
17
  private createWidget;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kroonen-ai/librebot-widget",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Libre Bot - AI documentation assistant widget",
6
6
  "main": "dist/librebot.js",