@cognitiondesk/widget 1.1.0 β†’ 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.es.js CHANGED
@@ -1,6 +1,6 @@
1
- import { jsx as C } from "react/jsx-runtime";
2
- import { forwardRef as H, useRef as y, useImperativeHandle as T, useEffect as k } from "react";
3
- const L = "https://mounaji-backendv3.onrender.com", N = `
1
+ import { jsx as S } from "react/jsx-runtime";
2
+ import { forwardRef as N, useRef as M, useImperativeHandle as I, useEffect as C } from "react";
3
+ const P = "https://mounaji-backendv3.onrender.com", B = `
4
4
  :host {
5
5
  all: initial;
6
6
  font-family: system-ui, sans-serif;
@@ -246,33 +246,42 @@ const L = "https://mounaji-backendv3.onrender.com", N = `
246
246
  font-size: 16px;
247
247
  }
248
248
  }
249
- `, w = {
249
+ `, x = {
250
250
  chat: '<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"/></svg>',
251
251
  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 x1="6" y1="6" x2="18" y2="18"/></svg>',
252
252
  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"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'
253
253
  };
254
- function I() {
254
+ function z() {
255
255
  return "cd_" + Math.random().toString(36).slice(2) + Date.now().toString(36);
256
256
  }
257
- let S = class {
258
- constructor(e = {}) {
259
- if (!e.apiKey) throw new Error("[CognitionDesk] apiKey is required");
257
+ let D = class {
258
+ constructor(t = {}) {
259
+ var e, a, s, r, n;
260
+ if (!t.apiKey) throw new Error("[CognitionDesk] apiKey is required");
260
261
  this._cfg = {
261
- apiKey: e.apiKey,
262
- widgetId: e.widgetId || null,
263
- assistantId: e.assistantId || null,
264
- backendUrl: e.backendUrl || L,
265
- primaryColor: e.primaryColor || "#2563eb",
266
- theme: e.theme || "light",
262
+ apiKey: t.apiKey,
263
+ widgetId: t.widgetId || null,
264
+ assistantId: t.assistantId || null,
265
+ backendUrl: t.backendUrl || P,
266
+ primaryColor: t.primaryColor || "#2563eb",
267
+ theme: t.theme || "light",
267
268
  // 'light' | 'dark' | 'auto'
268
- botName: e.botName || "AI Assistant",
269
- botEmoji: e.botEmoji || "πŸ€–",
270
- welcomeMessage: e.welcomeMessage || "Hello! How can I help you today?",
271
- placeholder: e.placeholder || "Type a message…",
272
- position: e.position || "bottom-right",
273
- streaming: e.streaming !== !1
269
+ botName: t.botName || "AI Assistant",
270
+ botEmoji: t.botEmoji || "πŸ€–",
271
+ welcomeMessage: t.welcomeMessage || "Hello! How can I help you today?",
272
+ placeholder: t.placeholder || "Type a message…",
273
+ position: t.position || "bottom-right",
274
+ streaming: t.streaming !== !1,
274
275
  // default: true
275
- }, this._sessionId = I(), this._messages = [], this._open = !1, this._loading = !1, this._container = null, this._shadow = null, this._panel = null, this._messagesEl = null, this._textarea = null, this._sendBtn = null, this._viewportHandler = null;
276
+ // Rate limiting β€” can be overridden by inline config or merged from server config
277
+ rateLimiting: {
278
+ enabled: ((e = t.rateLimiting) == null ? void 0 : e.enabled) !== !1,
279
+ maxMessagesPerSession: ((a = t.rateLimiting) == null ? void 0 : a.maxMessagesPerSession) ?? 0,
280
+ maxMessagesPerMinute: ((s = t.rateLimiting) == null ? void 0 : s.maxMessagesPerMinute) ?? 0,
281
+ limitReachedMessage: ((r = t.rateLimiting) == null ? void 0 : r.limitReachedMessage) || "You've reached the message limit for this session.",
282
+ rateLimitMessage: ((n = t.rateLimiting) == null ? void 0 : n.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment."
283
+ }
284
+ }, this._sessionId = z(), this._messageCount = 0, this._minuteCount = 0, this._minuteStart = Date.now(), this._messages = [], this._open = !1, this._loading = !1, this._container = null, this._shadow = null, this._panel = null, this._messagesEl = null, this._textarea = null, this._sendBtn = null, this._viewportHandler = null;
276
285
  }
277
286
  // ── Public API ──────────────────────────────────────────────────────────────
278
287
  /**
@@ -280,45 +289,45 @@ let S = class {
280
289
  * If `widgetId` was provided, fetches the server-side config first (async).
281
290
  * Returns a Promise so callers can await full initialisation.
282
291
  */
283
- mount(e) {
284
- const t = () => {
285
- const a = e || document.body;
292
+ mount(t) {
293
+ const e = () => {
294
+ const a = t || document.body;
286
295
  this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""), a.appendChild(this._container), this._shadow = this._container.attachShadow({ mode: "open" });
287
296
  const s = document.createElement("style");
288
- s.textContent = N, this._shadow.appendChild(s), this._buildDOM(), this._applyTheme(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
297
+ s.textContent = B, this._shadow.appendChild(s), this._buildDOM(), this._applyTheme(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
289
298
  };
290
- return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => t()).catch(() => t()) : (t(), Promise.resolve(this));
299
+ return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => e()).catch(() => e()) : (e(), Promise.resolve(this));
291
300
  }
292
301
  /**
293
302
  * Fetch public widget config from the platform and merge into this._cfg.
294
303
  * Only fields not already overridden by the constructor are applied.
295
304
  */
296
305
  async _fetchWidgetConfig() {
297
- var t;
298
- const e = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
306
+ var e;
307
+ const t = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
299
308
  try {
300
- const a = await fetch(e);
309
+ const a = await fetch(t);
301
310
  if (!a.ok) return;
302
311
  const { config: s } = await a.json();
303
312
  if (!s) return;
304
- s.botName && !this._userSet("botName") && (this._cfg.botName = s.botName), s.welcomeMessage && !this._userSet("welcomeMessage") && (this._cfg.welcomeMessage = s.welcomeMessage), s.primaryColor && !this._userSet("primaryColor") && (this._cfg.primaryColor = s.primaryColor), s.placeholder && !this._userSet("placeholder") && (this._cfg.placeholder = s.placeholder), s.assistantId && !this._cfg.assistantId && (this._cfg.assistantId = s.assistantId), (t = s.avatar) != null && t.value && !this._userSet("botEmoji") && (this._cfg.botEmoji = s.avatar.value);
313
+ s.botName && !this._userSet("botName") && (this._cfg.botName = s.botName), s.welcomeMessage && !this._userSet("welcomeMessage") && (this._cfg.welcomeMessage = s.welcomeMessage), s.primaryColor && !this._userSet("primaryColor") && (this._cfg.primaryColor = s.primaryColor), s.placeholder && !this._userSet("placeholder") && (this._cfg.placeholder = s.placeholder), s.assistantId && !this._cfg.assistantId && (this._cfg.assistantId = s.assistantId), (e = s.avatar) != null && e.value && !this._userSet("botEmoji") && (this._cfg.botEmoji = s.avatar.value), s.rateLimiting && (this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...s.rateLimiting });
305
314
  } catch {
306
315
  }
307
316
  }
308
317
  // eslint-disable-next-line no-unused-vars
309
- _userSet(e) {
318
+ _userSet(t) {
310
319
  return !1;
311
320
  }
312
321
  unmount() {
313
322
  this._unbindViewportMetrics(), this._container && (this._container.remove(), this._container = null);
314
323
  }
315
324
  open() {
316
- var e, t;
317
- this._open = !0, this._syncViewportMetrics(), (e = this._panel) == null || e.classList.add("open"), (t = this._textarea) == null || t.focus();
325
+ var t, e;
326
+ this._open = !0, this._syncViewportMetrics(), (t = this._panel) == null || t.classList.add("open"), (e = this._textarea) == null || e.focus();
318
327
  }
319
328
  close() {
320
- var e;
321
- this._open = !1, (e = this._panel) == null || e.classList.remove("open");
329
+ var t;
330
+ this._open = !1, (t = this._panel) == null || t.classList.remove("open");
322
331
  }
323
332
  toggle() {
324
333
  this._open ? this.close() : this.open();
@@ -328,10 +337,10 @@ let S = class {
328
337
  }
329
338
  // ── DOM ─────────────────────────────────────────────────────────────────────
330
339
  _buildDOM() {
331
- const e = document.createElement("div");
332
- e.className = "cd-root";
333
- const t = document.createElement("button");
334
- t.className = "cd-widget-btn", t.innerHTML = w.chat, t.setAttribute("aria-label", "Open chat"), t.style.setProperty("--cd-primary", this._cfg.primaryColor), this._toggleBtn = t;
340
+ const t = document.createElement("div");
341
+ t.className = "cd-root";
342
+ const e = document.createElement("button");
343
+ e.className = "cd-widget-btn", e.innerHTML = x.chat, e.setAttribute("aria-label", "Open chat"), e.style.setProperty("--cd-primary", this._cfg.primaryColor), this._toggleBtn = e;
335
344
  const a = document.createElement("div");
336
345
  a.className = "cd-panel", this._panel = a;
337
346
  const s = document.createElement("div");
@@ -344,27 +353,27 @@ let S = class {
344
353
  </div>
345
354
  </div>
346
355
  `;
347
- const o = document.createElement("button");
348
- o.className = "cd-close-btn", o.innerHTML = w.close, o.setAttribute("aria-label", "Close chat"), s.appendChild(o), this._closeBtn = o;
349
- const r = document.createElement("div");
350
- r.className = "cd-messages", r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), this._messagesEl = r;
356
+ const r = document.createElement("button");
357
+ r.className = "cd-close-btn", r.innerHTML = x.close, r.setAttribute("aria-label", "Close chat"), s.appendChild(r), this._closeBtn = r;
351
358
  const n = document.createElement("div");
352
- n.className = "cd-input-area";
353
- const d = document.createElement("textarea");
354
- d.className = "cd-textarea", d.rows = 1, d.placeholder = this._cfg.placeholder, this._textarea = d;
359
+ n.className = "cd-messages", n.setAttribute("role", "log"), n.setAttribute("aria-live", "polite"), this._messagesEl = n;
360
+ const d = document.createElement("div");
361
+ d.className = "cd-input-area";
362
+ const o = document.createElement("textarea");
363
+ o.className = "cd-textarea", o.rows = 1, o.placeholder = this._cfg.placeholder, this._textarea = o;
355
364
  const c = document.createElement("button");
356
- c.className = "cd-send-btn", c.innerHTML = w.send, c.setAttribute("aria-label", "Send"), this._sendBtn = c, n.appendChild(d), n.appendChild(c);
365
+ c.className = "cd-send-btn", c.innerHTML = x.send, c.setAttribute("aria-label", "Send"), this._sendBtn = c, d.appendChild(o), d.appendChild(c);
357
366
  const l = document.createElement("div");
358
- l.className = "cd-powered", l.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>', a.appendChild(s), a.appendChild(r), a.appendChild(n), a.appendChild(l), e.appendChild(t), e.appendChild(a), this._shadow.appendChild(e), this._root = e;
367
+ l.className = "cd-powered", l.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>', a.appendChild(s), a.appendChild(n), a.appendChild(d), a.appendChild(l), t.appendChild(e), t.appendChild(a), this._shadow.appendChild(t), this._root = t;
359
368
  }
360
369
  _applyTheme() {
361
- var t, a, s;
362
- (this._cfg.theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : this._cfg.theme) === "dark" && ((t = this._root) == null || t.classList.add("cd-dark")), (a = this._root) == null || a.style.setProperty("--cd-primary", this._cfg.primaryColor), (s = this._panel) == null || s.style.setProperty("--cd-primary", this._cfg.primaryColor);
370
+ var e, a, s;
371
+ (this._cfg.theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : this._cfg.theme) === "dark" && ((e = this._root) == null || e.classList.add("cd-dark")), (a = this._root) == null || a.style.setProperty("--cd-primary", this._cfg.primaryColor), (s = this._panel) == null || s.style.setProperty("--cd-primary", this._cfg.primaryColor);
363
372
  }
364
373
  // ── Events ──────────────────────────────────────────────────────────────────
365
374
  _bindEvents() {
366
- this._toggleBtn.addEventListener("click", () => this.toggle()), this._closeBtn.addEventListener("click", () => this.close()), this._sendBtn.addEventListener("click", () => this._sendMessage()), this._textarea.addEventListener("keydown", (e) => {
367
- e.key === "Enter" && !e.shiftKey && (e.preventDefault(), this._sendMessage());
375
+ this._toggleBtn.addEventListener("click", () => this.toggle()), this._closeBtn.addEventListener("click", () => this.close()), this._sendBtn.addEventListener("click", () => this._sendMessage()), this._textarea.addEventListener("keydown", (t) => {
376
+ t.key === "Enter" && !t.shiftKey && (t.preventDefault(), this._sendMessage());
368
377
  }), this._textarea.addEventListener("input", () => {
369
378
  this._textarea.style.height = "auto", this._textarea.style.height = Math.min(this._textarea.scrollHeight, 120) + "px";
370
379
  }), this._textarea.addEventListener("focus", () => {
@@ -378,28 +387,45 @@ let S = class {
378
387
  this._viewportHandler && (window.removeEventListener("resize", this._viewportHandler), window.removeEventListener("orientationchange", this._viewportHandler), window.visualViewport && (window.visualViewport.removeEventListener("resize", this._viewportHandler), window.visualViewport.removeEventListener("scroll", this._viewportHandler)), this._viewportHandler = null);
379
388
  }
380
389
  _syncViewportMetrics() {
381
- const e = this._root || this._container;
382
- if (!e) return;
383
- const t = window.visualViewport, a = t ? t.height : window.innerHeight;
384
- e.style.setProperty("--cd-viewport-height", `${Math.round(a)}px`);
390
+ const t = this._root || this._container;
391
+ if (!t) return;
392
+ const e = window.visualViewport, a = e ? e.height : window.innerHeight;
393
+ t.style.setProperty("--cd-viewport-height", `${Math.round(a)}px`);
385
394
  }
386
395
  // ── Chat ────────────────────────────────────────────────────────────────────
387
396
  async _sendMessage() {
388
- const e = this._textarea.value.trim();
389
- if (!(!e || this._loading)) {
390
- this._textarea.value = "", this._textarea.style.height = "auto", this._loading = !0, this._sendBtn.disabled = !0, this._messages.push({ role: "user", content: e }), this._appendMessage("user", e, !1);
391
- try {
392
- if (this._cfg.streaming)
393
- await this._callApiStream();
394
- else {
395
- const t = this._showTyping(), a = await this._callApi();
396
- this._removeTyping(t), this._messages.push({ role: "assistant", content: a }), this._appendMessage("assistant", a, !0);
397
- }
398
- } catch (t) {
399
- this._appendMessage("error", "Sorry, something went wrong. Please try again.", !1), console.error("[CognitionDesk]", t);
400
- } finally {
401
- this._loading = !1, this._sendBtn.disabled = !1, this._textarea.focus();
397
+ var a;
398
+ const t = this._textarea.value.trim();
399
+ if (!t || this._loading) return;
400
+ const e = this._cfg.rateLimiting;
401
+ if (e != null && e.enabled) {
402
+ const s = Date.now();
403
+ if (s - this._minuteStart >= 6e4 && (this._minuteCount = 0, this._minuteStart = s), e.maxMessagesPerSession > 0 && this._messageCount >= e.maxMessagesPerSession) {
404
+ this._appendMessage("error", e.limitReachedMessage, !1), this._textarea.disabled = !0, this._sendBtn.disabled = !0;
405
+ return;
402
406
  }
407
+ if (e.maxMessagesPerMinute > 0 && this._minuteCount >= e.maxMessagesPerMinute) {
408
+ this._appendMessage("error", e.rateLimitMessage, !1);
409
+ return;
410
+ }
411
+ }
412
+ this._textarea.value = "", this._textarea.style.height = "auto", this._loading = !0, this._sendBtn.disabled = !0, this._messages.push({ role: "user", content: t }), this._appendMessage("user", t, !1), this._messageCount += 1, this._minuteCount += 1;
413
+ try {
414
+ if (this._cfg.streaming)
415
+ await this._callApiStream();
416
+ else {
417
+ const s = this._showTyping(), r = await this._callApi();
418
+ this._removeTyping(s), this._messages.push({ role: "assistant", content: r }), this._appendMessage("assistant", r, !0);
419
+ }
420
+ } catch (s) {
421
+ if (s.status === 429 || (a = s.message) != null && a.includes("429")) {
422
+ const r = s.rateLimitMessage || (e == null ? void 0 : e.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment.";
423
+ this._appendMessage("error", r, !1);
424
+ } else
425
+ this._appendMessage("error", "Sorry, something went wrong. Please try again.", !1);
426
+ console.error("[CognitionDesk]", s);
427
+ } finally {
428
+ this._loading = !1, (e == null ? void 0 : e.maxMessagesPerSession) > 0 && this._messageCount >= e.maxMessagesPerSession || (this._sendBtn.disabled = !1), this._textarea.focus();
403
429
  }
404
430
  }
405
431
  _buildRequestBody() {
@@ -416,107 +442,112 @@ let S = class {
416
442
  }
417
443
  /** Streaming path β€” uses SSE endpoint */
418
444
  async _callApiStream() {
419
- const e = `${this._cfg.backendUrl}/chat-apiKeyAuth/stream`, t = await fetch(e, {
445
+ const t = `${this._cfg.backendUrl}/chat-apiKeyAuth/stream`, e = await fetch(t, {
420
446
  method: "POST",
421
447
  headers: { "Content-Type": "application/json", "x-api-key": this._cfg.apiKey },
422
448
  body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
423
449
  });
424
- if (!t.ok) {
425
- const d = await t.json().catch(() => ({}));
426
- throw new Error(d.message || `HTTP ${t.status}`);
450
+ if (!e.ok) {
451
+ const o = await e.json().catch(() => ({})), c = new Error(o.message || `HTTP ${e.status}`);
452
+ throw c.status = e.status, c.rateLimitMessage = o.message, c;
427
453
  }
428
454
  const a = document.createElement("div");
429
455
  a.className = "cd-msg assistant", a.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(a), this._scrollToBottom();
430
- const s = t.body.getReader(), o = new TextDecoder();
431
- let r = "", n = !1;
456
+ const s = e.body.getReader(), r = new TextDecoder();
457
+ let n = "", d = !1;
432
458
  try {
433
459
  for (; ; ) {
434
- const { done: d, value: c } = await s.read();
435
- if (d) break;
436
- const l = o.decode(c, { stream: !0 });
460
+ const { done: o, value: c } = await s.read();
461
+ if (o) break;
462
+ const l = r.decode(c, { stream: !0 });
437
463
  for (const g of l.split(`
438
464
  `)) {
439
465
  if (!g.startsWith("data: ")) continue;
440
- const f = g.slice(6).trim();
441
- if (f === "[DONE]") break;
466
+ const m = g.slice(6).trim();
467
+ if (m === "[DONE]") break;
442
468
  let h;
443
469
  try {
444
- h = JSON.parse(f);
470
+ h = JSON.parse(m);
445
471
  } catch {
446
472
  continue;
447
473
  }
448
- h.type === "content" && h.data && (n || (a.innerHTML = "", n = !0), r += h.data, a.innerHTML = this._renderMarkdown(r), this._scrollToBottom());
474
+ h.type === "content" && h.data && (d || (a.innerHTML = "", d = !0), n += h.data, a.innerHTML = this._renderMarkdown(n), this._scrollToBottom());
449
475
  }
450
476
  }
451
477
  } finally {
452
478
  s.releaseLock();
453
479
  }
454
- n || (a.innerHTML = this._renderMarkdown("No response")), r && this._messages.push({ role: "assistant", content: r });
480
+ d || (a.innerHTML = this._renderMarkdown("No response")), n && this._messages.push({ role: "assistant", content: n });
455
481
  }
456
482
  /** Non-streaming fallback path */
457
483
  async _callApi() {
458
- var s, o, r;
459
- const e = `${this._cfg.backendUrl}/chat-apiKeyAuth/chat`, t = await fetch(e, {
484
+ var s, r, n;
485
+ const t = `${this._cfg.backendUrl}/chat-apiKeyAuth/chat`, e = await fetch(t, {
460
486
  method: "POST",
461
487
  headers: { "Content-Type": "application/json", "x-api-key": this._cfg.apiKey },
462
488
  body: JSON.stringify(this._buildRequestBody())
463
489
  });
464
- if (!t.ok) {
465
- const n = await t.json().catch(() => ({}));
466
- throw new Error(n.message || `HTTP ${t.status}`);
490
+ if (!e.ok) {
491
+ const d = await e.json().catch(() => ({})), o = new Error(d.message || `HTTP ${e.status}`);
492
+ throw o.status = e.status, o.rateLimitMessage = d.message, o;
467
493
  }
468
- const a = await t.json();
469
- return a.response || a.message || a.content || ((r = (o = (s = a.choices) == null ? void 0 : s[0]) == null ? void 0 : o.message) == null ? void 0 : r.content) || "No response";
494
+ const a = await e.json();
495
+ return a.response || a.message || a.content || ((n = (r = (s = a.choices) == null ? void 0 : s[0]) == null ? void 0 : r.message) == null ? void 0 : n.content) || "No response";
470
496
  }
471
497
  // ── DOM helpers ─────────────────────────────────────────────────────────────
472
- _appendMessage(e, t, a = !0) {
498
+ _appendMessage(t, e, a = !0) {
473
499
  const s = document.createElement("div");
474
- s.className = `cd-msg ${e}`, a && e !== "user" ? s.innerHTML = this._renderMarkdown(t) : s.textContent = t, this._messagesEl.appendChild(s), this._scrollToBottom();
500
+ s.className = `cd-msg ${t}`, a && t !== "user" ? s.innerHTML = this._renderMarkdown(e) : s.textContent = e, this._messagesEl.appendChild(s), this._scrollToBottom();
475
501
  }
476
502
  /** Minimal, safe markdown β†’ HTML renderer (no dependencies). */
477
- _renderMarkdown(e) {
478
- if (!e) return "";
479
- let t = this._escHtml(e);
480
- return t = t.replace(
503
+ _renderMarkdown(t) {
504
+ if (!t) return "";
505
+ let e = this._escHtml(t);
506
+ return e = e.replace(
481
507
  /```[\w]*\n?([\s\S]*?)```/g,
482
508
  (a, s) => `<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`
483
- ), t = t.replace(/`([^`]+)`/g, '<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'), t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), t = t.replace(/__([^_]+)__/g, "<strong>$1</strong>"), t = t.replace(/\*([^*]+)\*/g, "<em>$1</em>"), t = t.replace(/_([^_]+)_/g, "<em>$1</em>"), t = t.replace(
509
+ ), e = e.replace(/`([^`]+)`/g, '<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'), e = e.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), e = e.replace(/__([^_]+)__/g, "<strong>$1</strong>"), e = e.replace(/\*([^*]+)\*/g, "<em>$1</em>"), e = e.replace(/_([^_]+)_/g, "<em>$1</em>"), e = e.replace(
484
510
  /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
485
511
  '<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'
486
- ), t = t.replace(/\n/g, "<br>"), t;
512
+ ), e = e.replace(/\n/g, "<br>"), e;
487
513
  }
488
514
  _showTyping() {
489
- const e = document.createElement("div");
490
- return e.className = "cd-msg assistant", e.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(e), this._scrollToBottom(), e;
515
+ const t = document.createElement("div");
516
+ return t.className = "cd-msg assistant", t.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(t), this._scrollToBottom(), t;
491
517
  }
492
- _removeTyping(e) {
493
- e == null || e.remove();
518
+ _removeTyping(t) {
519
+ t == null || t.remove();
494
520
  }
495
521
  _scrollToBottom() {
496
522
  this._messagesEl && (this._messagesEl.scrollTop = this._messagesEl.scrollHeight);
497
523
  }
498
- _escHtml(e) {
499
- return String(e).replace(/[&<>"']/g, (t) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[t]);
524
+ _escHtml(t) {
525
+ return String(t).replace(/[&<>"']/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[e]);
500
526
  }
501
527
  };
502
- const D = H(function(e, t) {
528
+ const V = N(function(t, e) {
503
529
  const {
504
530
  apiKey: a,
505
531
  widgetId: s,
506
- assistantId: o,
507
- theme: r = "light",
508
- primaryColor: n = "#2563eb",
509
- botName: d = "AI Assistant",
532
+ assistantId: r,
533
+ theme: n = "light",
534
+ primaryColor: d = "#2563eb",
535
+ botName: o = "AI Assistant",
510
536
  botEmoji: c = "πŸ€–",
511
537
  welcomeMessage: l = "Hello! How can I help you today?",
512
538
  placeholder: g = "Type a message…",
513
- defaultOpen: f = !1,
539
+ defaultOpen: m = !1,
514
540
  streaming: h = !0,
515
- backendUrl: x,
516
- onOpen: m,
541
+ backendUrl: w,
542
+ // Rate limiting β€” 0 means unlimited
543
+ maxMessagesPerSession: L = 0,
544
+ maxMessagesPerMinute: H = 0,
545
+ limitReachedMessage: v,
546
+ rateLimitMessage: y,
547
+ onOpen: f,
517
548
  onClose: u
518
- } = e, p = y(null), v = y(null);
519
- return T(t, () => ({
549
+ } = t, p = M(null), k = M(null);
550
+ return I(e, () => ({
520
551
  open: () => {
521
552
  var i;
522
553
  return (i = p.current) == null ? void 0 : i.open();
@@ -533,44 +564,51 @@ const D = H(function(e, t) {
533
564
  var i;
534
565
  return (i = p.current) == null ? void 0 : i.clearHistory();
535
566
  }
536
- }), []), k(() => {
567
+ }), []), C(() => {
537
568
  if (!a) {
538
569
  console.error("[CognitionDesk] apiKey prop is required");
539
570
  return;
540
571
  }
541
- const i = new S({
572
+ const i = new D({
542
573
  apiKey: a,
543
574
  widgetId: s,
544
- assistantId: o,
545
- theme: r,
546
- primaryColor: n,
547
- botName: d,
575
+ assistantId: r,
576
+ theme: n,
577
+ primaryColor: d,
578
+ botName: o,
548
579
  botEmoji: c,
549
580
  welcomeMessage: l,
550
581
  placeholder: g,
551
582
  streaming: h,
552
- ...x ? { backendUrl: x } : {}
583
+ ...w ? { backendUrl: w } : {},
584
+ rateLimiting: {
585
+ enabled: !0,
586
+ maxMessagesPerSession: L,
587
+ maxMessagesPerMinute: H,
588
+ ...v ? { limitReachedMessage: v } : {},
589
+ ...y ? { rateLimitMessage: y } : {}
590
+ }
553
591
  });
554
- if (m || u) {
555
- const _ = i.open.bind(i), M = i.close.bind(i);
592
+ if (f || u) {
593
+ const _ = i.open.bind(i), T = i.close.bind(i);
556
594
  i.open = (...b) => {
557
- _(...b), m == null || m();
595
+ _(...b), f == null || f();
558
596
  }, i.close = (...b) => {
559
- M(...b), u == null || u();
597
+ T(...b), u == null || u();
560
598
  };
561
599
  }
562
- return Promise.resolve(i.mount(v.current)).then(() => {
563
- p.current = i, f && i.open();
600
+ return Promise.resolve(i.mount(k.current)).then(() => {
601
+ p.current = i, m && i.open();
564
602
  }), () => {
565
603
  i.unmount(), p.current = null;
566
604
  };
567
- }, [a, s, o]), k(() => {
605
+ }, [a, s, r]), C(() => {
568
606
  var _;
569
607
  const i = p.current;
570
- i && (i._cfg.theme = r, i._cfg.primaryColor = n, (_ = i._applyTheme) == null || _.call(i));
571
- }, [r, n]), /* @__PURE__ */ C("div", { ref: v, "data-cognitiondesk-root": "" });
608
+ i && (i._cfg.theme = n, i._cfg.primaryColor = d, (_ = i._applyTheme) == null || _.call(i));
609
+ }, [n, d]), /* @__PURE__ */ S("div", { ref: k, "data-cognitiondesk-root": "" });
572
610
  });
573
611
  export {
574
- D as CognitionDeskWidget,
575
- S as CognitionDeskWidgetCore
612
+ V as CognitionDeskWidget,
613
+ D as CognitionDeskWidgetCore
576
614
  };
@@ -1,4 +1,4 @@
1
- (function(c,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],u):(c=typeof globalThis<"u"?globalThis:c||self,u(c.CognitionDeskWidgetReact={},c.jsxRuntime,c.React))})(this,function(c,u,g){"use strict";const C="https://mounaji-backendv3.onrender.com",H=`
1
+ (function(l,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],f):(l=typeof globalThis<"u"?globalThis:l||self,f(l.CognitionDeskWidgetReact={},l.jsxRuntime,l.React))})(this,function(l,f,g){"use strict";const H="https://mounaji-backendv3.onrender.com",T=`
2
2
  :host {
3
3
  all: initial;
4
4
  font-family: system-ui, sans-serif;
@@ -244,7 +244,7 @@
244
244
  font-size: 16px;
245
245
  }
246
246
  }
247
- `,v={chat:'<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"/></svg>',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 x1="6" y1="6" x2="18" y2="18"/></svg>',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"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function T(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}let k=class{constructor(e={}){if(!e.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._cfg={apiKey:e.apiKey,widgetId:e.widgetId||null,assistantId:e.assistantId||null,backendUrl:e.backendUrl||C,primaryColor:e.primaryColor||"#2563eb",theme:e.theme||"light",botName:e.botName||"AI Assistant",botEmoji:e.botEmoji||"πŸ€–",welcomeMessage:e.welcomeMessage||"Hello! How can I help you today?",placeholder:e.placeholder||"Type a message…",position:e.position||"bottom-right",streaming:e.streaming!==!1},this._sessionId=T(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null}mount(e){const t=()=>{const a=e||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),a.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=H,this._shadow.appendChild(s),this._buildDOM(),this._applyTheme(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(()=>t()).catch(()=>t()):(t(),Promise.resolve(this))}async _fetchWidgetConfig(){var t;const e=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const a=await fetch(e);if(!a.ok)return;const{config:s}=await a.json();if(!s)return;s.botName&&!this._userSet("botName")&&(this._cfg.botName=s.botName),s.welcomeMessage&&!this._userSet("welcomeMessage")&&(this._cfg.welcomeMessage=s.welcomeMessage),s.primaryColor&&!this._userSet("primaryColor")&&(this._cfg.primaryColor=s.primaryColor),s.placeholder&&!this._userSet("placeholder")&&(this._cfg.placeholder=s.placeholder),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(t=s.avatar)!=null&&t.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value)}catch{}}_userSet(e){return!1}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var e,t;this._open=!0,this._syncViewportMetrics(),(e=this._panel)==null||e.classList.add("open"),(t=this._textarea)==null||t.focus()}close(){var e;this._open=!1,(e=this._panel)==null||e.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const e=document.createElement("div");e.className="cd-root";const t=document.createElement("button");t.className="cd-widget-btn",t.innerHTML=v.chat,t.setAttribute("aria-label","Open chat"),t.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=t;const a=document.createElement("div");a.className="cd-panel",this._panel=a;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
247
+ `,v={chat:'<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"/></svg>',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 x1="6" y1="6" x2="18" y2="18"/></svg>',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"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function S(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}let k=class{constructor(t={}){var e,a,s,r,n;if(!t.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._cfg={apiKey:t.apiKey,widgetId:t.widgetId||null,assistantId:t.assistantId||null,backendUrl:t.backendUrl||H,primaryColor:t.primaryColor||"#2563eb",theme:t.theme||"light",botName:t.botName||"AI Assistant",botEmoji:t.botEmoji||"πŸ€–",welcomeMessage:t.welcomeMessage||"Hello! How can I help you today?",placeholder:t.placeholder||"Type a message…",position:t.position||"bottom-right",streaming:t.streaming!==!1,rateLimiting:{enabled:((e=t.rateLimiting)==null?void 0:e.enabled)!==!1,maxMessagesPerSession:((a=t.rateLimiting)==null?void 0:a.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=t.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((r=t.rateLimiting)==null?void 0:r.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((n=t.rateLimiting)==null?void 0:n.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."}},this._sessionId=S(),this._messageCount=0,this._minuteCount=0,this._minuteStart=Date.now(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null}mount(t){const e=()=>{const a=t||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),a.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=T,this._shadow.appendChild(s),this._buildDOM(),this._applyTheme(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(()=>e()).catch(()=>e()):(e(),Promise.resolve(this))}async _fetchWidgetConfig(){var e;const t=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const a=await fetch(t);if(!a.ok)return;const{config:s}=await a.json();if(!s)return;s.botName&&!this._userSet("botName")&&(this._cfg.botName=s.botName),s.welcomeMessage&&!this._userSet("welcomeMessage")&&(this._cfg.welcomeMessage=s.welcomeMessage),s.primaryColor&&!this._userSet("primaryColor")&&(this._cfg.primaryColor=s.primaryColor),s.placeholder&&!this._userSet("placeholder")&&(this._cfg.placeholder=s.placeholder),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(e=s.avatar)!=null&&e.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting})}catch{}}_userSet(t){return!1}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var t,e;this._open=!0,this._syncViewportMetrics(),(t=this._panel)==null||t.classList.add("open"),(e=this._textarea)==null||e.focus()}close(){var t;this._open=!1,(t=this._panel)==null||t.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const t=document.createElement("div");t.className="cd-root";const e=document.createElement("button");e.className="cd-widget-btn",e.innerHTML=v.chat,e.setAttribute("aria-label","Open chat"),e.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=e;const a=document.createElement("div");a.className="cd-panel",this._panel=a;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
248
248
  <div class="cd-header-avatar">${this._cfg.botEmoji}</div>
249
249
  <div class="cd-header-info">
250
250
  <div class="cd-header-name">${this._escHtml(this._cfg.botName)}</div>
@@ -252,5 +252,5 @@
252
252
  <span class="cd-status-dot"></span> Online
253
253
  </div>
254
254
  </div>
255
- `;const o=document.createElement("button");o.className="cd-close-btn",o.innerHTML=v.close,o.setAttribute("aria-label","Close chat"),s.appendChild(o),this._closeBtn=o;const r=document.createElement("div");r.className="cd-messages",r.setAttribute("role","log"),r.setAttribute("aria-live","polite"),this._messagesEl=r;const n=document.createElement("div");n.className="cd-input-area";const d=document.createElement("textarea");d.className="cd-textarea",d.rows=1,d.placeholder=this._cfg.placeholder,this._textarea=d;const l=document.createElement("button");l.className="cd-send-btn",l.innerHTML=v.send,l.setAttribute("aria-label","Send"),this._sendBtn=l,n.appendChild(d),n.appendChild(l);const p=document.createElement("div");p.className="cd-powered",p.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',a.appendChild(s),a.appendChild(r),a.appendChild(n),a.appendChild(p),e.appendChild(t),e.appendChild(a),this._shadow.appendChild(e),this._root=e}_applyTheme(){var t,a,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((t=this._root)==null||t.classList.add("cd-dark")),(a=this._root)==null||a.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const e=this._root||this._container;if(!e)return;const t=window.visualViewport,a=t?t.height:window.innerHeight;e.style.setProperty("--cd-viewport-height",`${Math.round(a)}px`)}async _sendMessage(){const e=this._textarea.value.trim();if(!(!e||this._loading)){this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:e}),this._appendMessage("user",e,!1);try{if(this._cfg.streaming)await this._callApiStream();else{const t=this._showTyping(),a=await this._callApi();this._removeTyping(t),this._messages.push({role:"assistant",content:a}),this._appendMessage("assistant",a,!0)}}catch(t){this._appendMessage("error","Sorry, something went wrong. Please try again.",!1),console.error("[CognitionDesk]",t)}finally{this._loading=!1,this._sendBtn.disabled=!1,this._textarea.focus()}}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!t.ok){const d=await t.json().catch(()=>({}));throw new Error(d.message||`HTTP ${t.status}`)}const a=document.createElement("div");a.className="cd-msg assistant",a.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(a),this._scrollToBottom();const s=t.body.getReader(),o=new TextDecoder;let r="",n=!1;try{for(;;){const{done:d,value:l}=await s.read();if(d)break;const p=o.decode(l,{stream:!0});for(const m of p.split(`
256
- `)){if(!m.startsWith("data: "))continue;const _=m.slice(6).trim();if(_==="[DONE]")break;let f;try{f=JSON.parse(_)}catch{continue}f.type==="content"&&f.data&&(n||(a.innerHTML="",n=!0),r+=f.data,a.innerHTML=this._renderMarkdown(r),this._scrollToBottom())}}}finally{s.releaseLock()}n||(a.innerHTML=this._renderMarkdown("No response")),r&&this._messages.push({role:"assistant",content:r})}async _callApi(){var s,o,r;const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!t.ok){const n=await t.json().catch(()=>({}));throw new Error(n.message||`HTTP ${t.status}`)}const a=await t.json();return a.response||a.message||a.content||((r=(o=(s=a.choices)==null?void 0:s[0])==null?void 0:o.message)==null?void 0:r.content)||"No response"}_appendMessage(e,t,a=!0){const s=document.createElement("div");s.className=`cd-msg ${e}`,a&&e!=="user"?s.innerHTML=this._renderMarkdown(t):s.textContent=t,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(e){if(!e)return"";let t=this._escHtml(e);return t=t.replace(/```[\w]*\n?([\s\S]*?)```/g,(a,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),t=t.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),t=t.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replace(/__([^_]+)__/g,"<strong>$1</strong>"),t=t.replace(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replace(/_([^_]+)_/g,"<em>$1</em>"),t=t.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),t=t.replace(/\n/g,"<br>"),t}_showTyping(){const e=document.createElement("div");return e.className="cd-msg assistant",e.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(e),this._scrollToBottom(),e}_removeTyping(e){e==null||e.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}};const L=g.forwardRef(function(e,t){const{apiKey:a,widgetId:s,assistantId:o,theme:r="light",primaryColor:n="#2563eb",botName:d="AI Assistant",botEmoji:l="πŸ€–",welcomeMessage:p="Hello! How can I help you today?",placeholder:m="Type a message…",defaultOpen:_=!1,streaming:f=!0,backendUrl:E,onOpen:b,onClose:x}=e,h=g.useRef(null),M=g.useRef(null);return g.useImperativeHandle(t,()=>({open:()=>{var i;return(i=h.current)==null?void 0:i.open()},close:()=>{var i;return(i=h.current)==null?void 0:i.close()},toggle:()=>{var i;return(i=h.current)==null?void 0:i.toggle()},clearHistory:()=>{var i;return(i=h.current)==null?void 0:i.clearHistory()}}),[]),g.useEffect(()=>{if(!a){console.error("[CognitionDesk] apiKey prop is required");return}const i=new k({apiKey:a,widgetId:s,assistantId:o,theme:r,primaryColor:n,botName:d,botEmoji:l,welcomeMessage:p,placeholder:m,streaming:f,...E?{backendUrl:E}:{}});if(b||x){const w=i.open.bind(i),I=i.close.bind(i);i.open=(...y)=>{w(...y),b==null||b()},i.close=(...y)=>{I(...y),x==null||x()}}return Promise.resolve(i.mount(M.current)).then(()=>{h.current=i,_&&i.open()}),()=>{i.unmount(),h.current=null}},[a,s,o]),g.useEffect(()=>{var w;const i=h.current;i&&(i._cfg.theme=r,i._cfg.primaryColor=n,(w=i._applyTheme)==null||w.call(i))},[r,n]),u.jsx("div",{ref:M,"data-cognitiondesk-root":""})});c.CognitionDeskWidget=L,c.CognitionDeskWidgetCore=k,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
255
+ `;const r=document.createElement("button");r.className="cd-close-btn",r.innerHTML=v.close,r.setAttribute("aria-label","Close chat"),s.appendChild(r),this._closeBtn=r;const n=document.createElement("div");n.className="cd-messages",n.setAttribute("role","log"),n.setAttribute("aria-live","polite"),this._messagesEl=n;const d=document.createElement("div");d.className="cd-input-area";const o=document.createElement("textarea");o.className="cd-textarea",o.rows=1,o.placeholder=this._cfg.placeholder,this._textarea=o;const c=document.createElement("button");c.className="cd-send-btn",c.innerHTML=v.send,c.setAttribute("aria-label","Send"),this._sendBtn=c,d.appendChild(o),d.appendChild(c);const p=document.createElement("div");p.className="cd-powered",p.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',a.appendChild(s),a.appendChild(n),a.appendChild(d),a.appendChild(p),t.appendChild(e),t.appendChild(a),this._shadow.appendChild(t),this._root=t}_applyTheme(){var e,a,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((e=this._root)==null||e.classList.add("cd-dark")),(a=this._root)==null||a.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const t=this._root||this._container;if(!t)return;const e=window.visualViewport,a=e?e.height:window.innerHeight;t.style.setProperty("--cd-viewport-height",`${Math.round(a)}px`)}async _sendMessage(){var a;const t=this._textarea.value.trim();if(!t||this._loading)return;const e=this._cfg.rateLimiting;if(e!=null&&e.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),e.maxMessagesPerSession>0&&this._messageCount>=e.maxMessagesPerSession){this._appendMessage("error",e.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(e.maxMessagesPerMinute>0&&this._minuteCount>=e.maxMessagesPerMinute){this._appendMessage("error",e.rateLimitMessage,!1);return}}this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:t}),this._appendMessage("user",t,!1),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),r=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:r}),this._appendMessage("assistant",r,!0)}}catch(s){if(s.status===429||(a=s.message)!=null&&a.includes("429")){const r=s.rateLimitMessage||(e==null?void 0:e.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",r,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(e==null?void 0:e.maxMessagesPerSession)>0&&this._messageCount>=e.maxMessagesPerSession||(this._sendBtn.disabled=!1),this._textarea.focus()}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!e.ok){const o=await e.json().catch(()=>({})),c=new Error(o.message||`HTTP ${e.status}`);throw c.status=e.status,c.rateLimitMessage=o.message,c}const a=document.createElement("div");a.className="cd-msg assistant",a.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(a),this._scrollToBottom();const s=e.body.getReader(),r=new TextDecoder;let n="",d=!1;try{for(;;){const{done:o,value:c}=await s.read();if(o)break;const p=r.decode(c,{stream:!0});for(const u of p.split(`
256
+ `)){if(!u.startsWith("data: "))continue;const _=u.slice(6).trim();if(_==="[DONE]")break;let m;try{m=JSON.parse(_)}catch{continue}m.type==="content"&&m.data&&(d||(a.innerHTML="",d=!0),n+=m.data,a.innerHTML=this._renderMarkdown(n),this._scrollToBottom())}}}finally{s.releaseLock()}d||(a.innerHTML=this._renderMarkdown("No response")),n&&this._messages.push({role:"assistant",content:n})}async _callApi(){var s,r,n;const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!e.ok){const d=await e.json().catch(()=>({})),o=new Error(d.message||`HTTP ${e.status}`);throw o.status=e.status,o.rateLimitMessage=d.message,o}const a=await e.json();return a.response||a.message||a.content||((n=(r=(s=a.choices)==null?void 0:s[0])==null?void 0:r.message)==null?void 0:n.content)||"No response"}_appendMessage(t,e,a=!0){const s=document.createElement("div");s.className=`cd-msg ${t}`,a&&t!=="user"?s.innerHTML=this._renderMarkdown(e):s.textContent=e,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(t){if(!t)return"";let e=this._escHtml(t);return e=e.replace(/```[\w]*\n?([\s\S]*?)```/g,(a,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),e=e.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),e=e.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__([^_]+)__/g,"<strong>$1</strong>"),e=e.replace(/\*([^*]+)\*/g,"<em>$1</em>"),e=e.replace(/_([^_]+)_/g,"<em>$1</em>"),e=e.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),e=e.replace(/\n/g,"<br>"),e}_showTyping(){const t=document.createElement("div");return t.className="cd-msg assistant",t.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(t),this._scrollToBottom(),t}_removeTyping(t){t==null||t.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(t){return String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}};const N=g.forwardRef(function(t,e){const{apiKey:a,widgetId:s,assistantId:r,theme:n="light",primaryColor:d="#2563eb",botName:o="AI Assistant",botEmoji:c="πŸ€–",welcomeMessage:p="Hello! How can I help you today?",placeholder:u="Type a message…",defaultOpen:_=!1,streaming:m=!0,backendUrl:M,maxMessagesPerSession:P=0,maxMessagesPerMinute:j=0,limitReachedMessage:C,rateLimitMessage:E,onOpen:b,onClose:x}=t,h=g.useRef(null),L=g.useRef(null);return g.useImperativeHandle(e,()=>({open:()=>{var i;return(i=h.current)==null?void 0:i.open()},close:()=>{var i;return(i=h.current)==null?void 0:i.close()},toggle:()=>{var i;return(i=h.current)==null?void 0:i.toggle()},clearHistory:()=>{var i;return(i=h.current)==null?void 0:i.clearHistory()}}),[]),g.useEffect(()=>{if(!a){console.error("[CognitionDesk] apiKey prop is required");return}const i=new k({apiKey:a,widgetId:s,assistantId:r,theme:n,primaryColor:d,botName:o,botEmoji:c,welcomeMessage:p,placeholder:u,streaming:m,...M?{backendUrl:M}:{},rateLimiting:{enabled:!0,maxMessagesPerSession:P,maxMessagesPerMinute:j,...C?{limitReachedMessage:C}:{},...E?{rateLimitMessage:E}:{}}});if(b||x){const w=i.open.bind(i),B=i.close.bind(i);i.open=(...y)=>{w(...y),b==null||b()},i.close=(...y)=>{B(...y),x==null||x()}}return Promise.resolve(i.mount(L.current)).then(()=>{h.current=i,_&&i.open()}),()=>{i.unmount(),h.current=null}},[a,s,r]),g.useEffect(()=>{var w;const i=h.current;i&&(i._cfg.theme=n,i._cfg.primaryColor=d,(w=i._applyTheme)==null||w.call(i))},[n,d]),f.jsx("div",{ref:L,"data-cognitiondesk-root":""})});l.CognitionDeskWidget=N,l.CognitionDeskWidgetCore=k,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});