@cognitiondesk/widget 1.2.1 → 1.2.3

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 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 = `
1
+ import { jsx as P } from "react/jsx-runtime";
2
+ import { forwardRef as I, useRef as E, useImperativeHandle as N, useEffect as x } from "react";
3
+ const j = "https://mounaji-backendv3.onrender.com", z = `
4
4
  :host {
5
5
  all: initial;
6
6
  font-family: system-ui, sans-serif;
@@ -246,84 +246,181 @@ const P = "https://mounaji-backendv3.onrender.com", B = `
246
246
  font-size: 16px;
247
247
  }
248
248
  }
249
- `, x = {
249
+ `, w = {
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 z() {
254
+ function B() {
255
255
  return "cd_" + Math.random().toString(36).slice(2) + Date.now().toString(36);
256
256
  }
257
257
  let D = class {
258
+ /**
259
+ * @param {Object} config
260
+ * @param {string} config.apiKey - Required. Your CognitionDesk API key.
261
+ * @param {string} [config.widgetId] - Widget ID from the dashboard. Enables remote config.
262
+ * @param {string} [config.assistantId] - Assistant ID (overrides server value if set).
263
+ * @param {string} [config.backendUrl] - Custom backend URL.
264
+ * @param {string} [config.theme] - 'light' | 'dark' | 'auto'
265
+ * @param {string} [config.primaryColor] - Hex color for buttons / header.
266
+ * @param {string} [config.botName] - Display name shown in the header.
267
+ * @param {string} [config.botEmoji] - Emoji shown as avatar.
268
+ * @param {string} [config.welcomeMessage] - First message shown to the user.
269
+ * @param {string} [config.placeholder] - Textarea placeholder text.
270
+ * @param {string} [config.position] - 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
271
+ * @param {boolean} [config.streaming] - Enable streaming responses (default: true).
272
+ * @param {Object} [config.overrideSettings]
273
+ * Explicit code-level overrides. Any key listed here takes precedence over
274
+ * the server-side dashboard config. Use this when you want to manage
275
+ * specific settings from code rather than the CognitionDesk dashboard.
276
+ *
277
+ * Example — let the dashboard control everything except color:
278
+ * overrideSettings: { primaryColor: '#e11d48' }
279
+ *
280
+ * Example — full code control (dashboard settings ignored):
281
+ * overrideSettings: {
282
+ * primaryColor: '#e11d48', botName: 'Aria', welcomeMessage: 'Hi!'
283
+ * }
284
+ *
285
+ * When omitted (default), ALL settings come from the dashboard.
286
+ */
258
287
  constructor(t = {}) {
259
- var e, a, s, r, n;
288
+ var e, i, s, a, n;
260
289
  if (!t.apiKey) throw new Error("[CognitionDesk] apiKey is required");
261
- this._cfg = {
290
+ this._overrides = new Set(
291
+ t.overrideSettings ? Object.keys(t.overrideSettings) : []
292
+ ), this._cfg = {
262
293
  apiKey: t.apiKey,
263
294
  widgetId: t.widgetId || null,
264
295
  assistantId: t.assistantId || null,
265
- backendUrl: t.backendUrl || P,
296
+ backendUrl: t.backendUrl || j,
266
297
  primaryColor: t.primaryColor || "#2563eb",
267
298
  theme: t.theme || "light",
268
- // 'light' | 'dark' | 'auto'
269
299
  botName: t.botName || "AI Assistant",
270
300
  botEmoji: t.botEmoji || "🤖",
271
301
  welcomeMessage: t.welcomeMessage || "Hello! How can I help you today?",
272
302
  placeholder: t.placeholder || "Type a message…",
273
303
  position: t.position || "bottom-right",
274
304
  streaming: t.streaming !== !1,
275
- // default: true
276
- // Rate limiting — can be overridden by inline config or merged from server config
277
305
  rateLimiting: {
278
306
  enabled: ((e = t.rateLimiting) == null ? void 0 : e.enabled) !== !1,
279
- maxMessagesPerSession: ((a = t.rateLimiting) == null ? void 0 : a.maxMessagesPerSession) ?? 0,
307
+ maxMessagesPerSession: ((i = t.rateLimiting) == null ? void 0 : i.maxMessagesPerSession) ?? 0,
280
308
  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.",
309
+ limitReachedMessage: ((a = t.rateLimiting) == null ? void 0 : a.limitReachedMessage) || "You've reached the message limit for this session.",
282
310
  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;
311
+ },
312
+ // Store overrides for re-application after each config refresh
313
+ overrideSettings: t.overrideSettings || null
314
+ }, t.overrideSettings && this._applyOverrides(t.overrideSettings), this._sessionId = B(), 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, this._refreshPending = !1;
285
315
  }
286
316
  // ── Public API ──────────────────────────────────────────────────────────────
287
317
  /**
288
318
  * Mount the widget.
289
- * If `widgetId` was provided, fetches the server-side config first (async).
290
- * Returns a Promise so callers can await full initialisation.
319
+ * If `widgetId` is set, fetches server config first (async).
320
+ * Returns a Promise so callers can `await widget.mount()`.
291
321
  */
292
322
  mount(t) {
293
323
  const e = () => {
294
- const a = t || document.body;
295
- this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""), a.appendChild(this._container), this._shadow = this._container.attachShadow({ mode: "open" });
324
+ const i = t || document.body;
325
+ this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""), i.appendChild(this._container), this._shadow = this._container.attachShadow({ mode: "open" });
296
326
  const s = document.createElement("style");
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);
327
+ s.textContent = z, this._shadow.appendChild(s), this._buildDOM(), this._applyConfig(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
298
328
  };
299
- return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => e()).catch(() => e()) : (e(), Promise.resolve(this));
329
+ return this._cfg.widgetId ? this._fetchWidgetConfig().then((i) => {
330
+ i !== !1 && e();
331
+ }).catch(() => e()) : (e(), Promise.resolve(this));
332
+ }
333
+ /**
334
+ * Programmatically update settings at runtime.
335
+ * Merges `newSettings` into `overrideSettings` and re-applies to the live DOM.
336
+ * Use this to change colors, bot name, etc. without remounting.
337
+ *
338
+ * widget.updateSettings({ primaryColor: '#dc2626', botName: 'Support Bot' });
339
+ */
340
+ updateSettings(t) {
341
+ if (!t || typeof t != "object") return;
342
+ const e = { ...this._cfg.overrideSettings || {}, ...t };
343
+ this._cfg.overrideSettings = e, this._overrides = new Set(Object.keys(e)), this._applyOverrides(e), this._applyConfig();
344
+ }
345
+ /**
346
+ * Fetch fresh config from the dashboard right now and apply it.
347
+ * Useful after the user changes settings in the CognitionDesk dashboard
348
+ * and wants them reflected immediately without reloading the page.
349
+ */
350
+ async refreshConfig() {
351
+ if (!this._cfg.widgetId) return;
352
+ await this._fetchWidgetConfig() === !1 ? this.unmount() : this._applyConfig();
353
+ }
354
+ // ── Config internals ────────────────────────────────────────────────────────
355
+ /**
356
+ * Returns true if `key` is explicitly controlled by `overrideSettings`.
357
+ * Those keys are immune to server config overwrites.
358
+ */
359
+ _userSet(t) {
360
+ return this._overrides.has(t);
300
361
  }
301
362
  /**
302
363
  * Fetch public widget config from the platform and merge into this._cfg.
303
- * Only fields not already overridden by the constructor are applied.
364
+ * Server values apply to any key NOT present in `overrideSettings`.
365
+ * After merging, overrides are re-applied so they always win.
304
366
  */
305
367
  async _fetchWidgetConfig() {
306
368
  var e;
307
369
  const t = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
308
370
  try {
309
- const a = await fetch(t);
310
- if (!a.ok) return;
311
- const { config: s } = await a.json();
371
+ const i = await fetch(t);
372
+ if (i.status === 404 || i.status === 403) return !1;
373
+ if (!i.ok) return !0;
374
+ const { config: s } = await i.json();
312
375
  if (!s) return;
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 });
376
+ const a = (n, o) => {
377
+ o != null && !this._userSet(n) && (this._cfg[n] = o);
378
+ };
379
+ return a("botName", s.botName), a("welcomeMessage", s.welcomeMessage), a("primaryColor", s.primaryColor), a("secondaryColor", s.secondaryColor), a("placeholder", s.placeholder), a("theme", s.theme), a("position", s.position), a("style", s.style), a("size", s.size), 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 }), this._cfg.overrideSettings && this._applyOverrides(this._cfg.overrideSettings), !0;
314
380
  } catch {
381
+ return !0;
315
382
  }
316
383
  }
317
- // eslint-disable-next-line no-unused-vars
318
- _userSet(t) {
319
- return !1;
384
+ /**
385
+ * Write override values into this._cfg (flat fields only; rateLimiting is merged).
386
+ */
387
+ _applyOverrides(t) {
388
+ for (const [e, i] of Object.entries(t))
389
+ e === "rateLimiting" && typeof i == "object" ? this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...i } : this._cfg[e] = i;
390
+ }
391
+ /**
392
+ * Update live DOM elements to reflect the current this._cfg.
393
+ * Safe to call before or after mounting.
394
+ */
395
+ _applyConfig() {
396
+ var s, a, n, o, d, c, l;
397
+ const t = this._cfg.theme === "dark" || this._cfg.theme === "auto" && ((s = window.matchMedia) == null ? void 0 : s.call(window, "(prefers-color-scheme: dark)").matches);
398
+ (a = this._root) == null || a.classList.toggle("cd-dark", t), (n = this._root) == null || n.style.setProperty("--cd-primary", this._cfg.primaryColor), (o = this._panel) == null || o.style.setProperty("--cd-primary", this._cfg.primaryColor), (d = this._toggleBtn) == null || d.style.setProperty("--cd-primary", this._cfg.primaryColor);
399
+ const e = (c = this._shadow) == null ? void 0 : c.querySelector(".cd-header-avatar");
400
+ e && (e.textContent = this._cfg.botEmoji);
401
+ const i = (l = this._shadow) == null ? void 0 : l.querySelector(".cd-header-name");
402
+ i && (i.textContent = this._cfg.botName), this._textarea && (this._textarea.placeholder = this._cfg.placeholder);
403
+ }
404
+ /**
405
+ * Silently refresh config from server in the background.
406
+ * Called each time the panel opens so dashboard changes
407
+ * are reflected without reloading the page.
408
+ * Uses a debounce flag to avoid concurrent fetches.
409
+ */
410
+ _silentRefresh() {
411
+ !this._cfg.widgetId || this._refreshPending || (this._refreshPending = !0, this._fetchWidgetConfig().then((t) => {
412
+ t === !1 ? this.unmount() : this._applyConfig();
413
+ }).catch(() => {
414
+ }).finally(() => {
415
+ this._refreshPending = !1;
416
+ }));
320
417
  }
321
418
  unmount() {
322
419
  this._unbindViewportMetrics(), this._container && (this._container.remove(), this._container = null);
323
420
  }
324
421
  open() {
325
422
  var t, e;
326
- this._open = !0, this._syncViewportMetrics(), (t = this._panel) == null || t.classList.add("open"), (e = this._textarea) == null || e.focus();
423
+ this._open = !0, this._syncViewportMetrics(), (t = this._panel) == null || t.classList.add("open"), (e = this._textarea) == null || e.focus(), this._silentRefresh();
327
424
  }
328
425
  close() {
329
426
  var t;
@@ -340,9 +437,9 @@ let D = class {
340
437
  const t = document.createElement("div");
341
438
  t.className = "cd-root";
342
439
  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;
344
- const a = document.createElement("div");
345
- a.className = "cd-panel", this._panel = a;
440
+ e.className = "cd-widget-btn", e.innerHTML = w.chat, e.setAttribute("aria-label", "Open chat"), e.style.setProperty("--cd-primary", this._cfg.primaryColor), this._toggleBtn = e;
441
+ const i = document.createElement("div");
442
+ i.className = "cd-panel", this._panel = i;
346
443
  const s = document.createElement("div");
347
444
  s.className = "cd-header", s.innerHTML = `
348
445
  <div class="cd-header-avatar">${this._cfg.botEmoji}</div>
@@ -353,22 +450,22 @@ let D = class {
353
450
  </div>
354
451
  </div>
355
452
  `;
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;
453
+ const a = document.createElement("button");
454
+ a.className = "cd-close-btn", a.innerHTML = w.close, a.setAttribute("aria-label", "Close chat"), s.appendChild(a), this._closeBtn = a;
358
455
  const n = document.createElement("div");
359
456
  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;
457
+ const o = document.createElement("div");
458
+ o.className = "cd-input-area";
459
+ const d = document.createElement("textarea");
460
+ d.className = "cd-textarea", d.rows = 1, d.placeholder = this._cfg.placeholder, this._textarea = d;
364
461
  const c = document.createElement("button");
365
- c.className = "cd-send-btn", c.innerHTML = x.send, c.setAttribute("aria-label", "Send"), this._sendBtn = c, d.appendChild(o), d.appendChild(c);
462
+ c.className = "cd-send-btn", c.innerHTML = w.send, c.setAttribute("aria-label", "Send"), this._sendBtn = c, o.appendChild(d), o.appendChild(c);
366
463
  const l = document.createElement("div");
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;
464
+ l.className = "cd-powered", l.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>', i.appendChild(s), i.appendChild(n), i.appendChild(o), i.appendChild(l), t.appendChild(e), t.appendChild(i), this._shadow.appendChild(t), this._root = t;
368
465
  }
369
466
  _applyTheme() {
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);
467
+ var e, i, s;
468
+ (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")), (i = this._root) == null || i.style.setProperty("--cd-primary", this._cfg.primaryColor), (s = this._panel) == null || s.style.setProperty("--cd-primary", this._cfg.primaryColor);
372
469
  }
373
470
  // ── Events ──────────────────────────────────────────────────────────────────
374
471
  _bindEvents() {
@@ -389,12 +486,12 @@ let D = class {
389
486
  _syncViewportMetrics() {
390
487
  const t = this._root || this._container;
391
488
  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`);
489
+ const e = window.visualViewport, i = e ? e.height : window.innerHeight;
490
+ t.style.setProperty("--cd-viewport-height", `${Math.round(i)}px`);
394
491
  }
395
492
  // ── Chat ────────────────────────────────────────────────────────────────────
396
493
  async _sendMessage() {
397
- var a;
494
+ var i;
398
495
  const t = this._textarea.value.trim();
399
496
  if (!t || this._loading) return;
400
497
  const e = this._cfg.rateLimiting;
@@ -414,13 +511,13 @@ let D = class {
414
511
  if (this._cfg.streaming)
415
512
  await this._callApiStream();
416
513
  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);
514
+ const s = this._showTyping(), a = await this._callApi();
515
+ this._removeTyping(s), this._messages.push({ role: "assistant", content: a }), this._appendMessage("assistant", a, !0);
419
516
  }
420
517
  } 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);
518
+ if (s.status === 429 || (i = s.message) != null && i.includes("429")) {
519
+ const a = s.rateLimitMessage || (e == null ? void 0 : e.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment.";
520
+ this._appendMessage("error", a, !1);
424
521
  } else
425
522
  this._appendMessage("error", "Sorry, something went wrong. Please try again.", !1);
426
523
  console.error("[CognitionDesk]", s);
@@ -448,56 +545,56 @@ let D = class {
448
545
  body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
449
546
  });
450
547
  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;
548
+ const d = await e.json().catch(() => ({})), c = new Error(d.message || `HTTP ${e.status}`);
549
+ throw c.status = e.status, c.rateLimitMessage = d.message, c;
453
550
  }
454
- const a = document.createElement("div");
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();
456
- const s = e.body.getReader(), r = new TextDecoder();
457
- let n = "", d = !1;
551
+ const i = document.createElement("div");
552
+ i.className = "cd-msg assistant", i.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(i), this._scrollToBottom();
553
+ const s = e.body.getReader(), a = new TextDecoder();
554
+ let n = "", o = !1;
458
555
  try {
459
556
  for (; ; ) {
460
- const { done: o, value: c } = await s.read();
461
- if (o) break;
462
- const l = r.decode(c, { stream: !0 });
463
- for (const g of l.split(`
557
+ const { done: d, value: c } = await s.read();
558
+ if (d) break;
559
+ const l = a.decode(c, { stream: !0 });
560
+ for (const f of l.split(`
464
561
  `)) {
465
- if (!g.startsWith("data: ")) continue;
466
- const m = g.slice(6).trim();
562
+ if (!f.startsWith("data: ")) continue;
563
+ const m = f.slice(6).trim();
467
564
  if (m === "[DONE]") break;
468
- let h;
565
+ let p;
469
566
  try {
470
- h = JSON.parse(m);
567
+ p = JSON.parse(m);
471
568
  } catch {
472
569
  continue;
473
570
  }
474
- h.type === "content" && h.data && (d || (a.innerHTML = "", d = !0), n += h.data, a.innerHTML = this._renderMarkdown(n), this._scrollToBottom());
571
+ p.type === "content" && p.data && (o || (i.innerHTML = "", o = !0), n += p.data, i.innerHTML = this._renderMarkdown(n), this._scrollToBottom());
475
572
  }
476
573
  }
477
574
  } finally {
478
575
  s.releaseLock();
479
576
  }
480
- d || (a.innerHTML = this._renderMarkdown("No response")), n && this._messages.push({ role: "assistant", content: n });
577
+ o || (i.innerHTML = this._renderMarkdown("No response")), n && this._messages.push({ role: "assistant", content: n });
481
578
  }
482
579
  /** Non-streaming fallback path */
483
580
  async _callApi() {
484
- var s, r, n;
581
+ var s, a, n;
485
582
  const t = `${this._cfg.backendUrl}/chat-apiKeyAuth/chat`, e = await fetch(t, {
486
583
  method: "POST",
487
584
  headers: { "Content-Type": "application/json", "x-api-key": this._cfg.apiKey },
488
585
  body: JSON.stringify(this._buildRequestBody())
489
586
  });
490
587
  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;
588
+ const o = await e.json().catch(() => ({})), d = new Error(o.message || `HTTP ${e.status}`);
589
+ throw d.status = e.status, d.rateLimitMessage = o.message, d;
493
590
  }
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";
591
+ const i = await e.json();
592
+ return i.response || i.message || i.content || ((n = (a = (s = i.choices) == null ? void 0 : s[0]) == null ? void 0 : a.message) == null ? void 0 : n.content) || "No response";
496
593
  }
497
594
  // ── DOM helpers ─────────────────────────────────────────────────────────────
498
- _appendMessage(t, e, a = !0) {
595
+ _appendMessage(t, e, i = !0) {
499
596
  const s = document.createElement("div");
500
- s.className = `cd-msg ${t}`, a && t !== "user" ? s.innerHTML = this._renderMarkdown(e) : s.textContent = e, this._messagesEl.appendChild(s), this._scrollToBottom();
597
+ s.className = `cd-msg ${t}`, i && t !== "user" ? s.innerHTML = this._renderMarkdown(e) : s.textContent = e, this._messagesEl.appendChild(s), this._scrollToBottom();
501
598
  }
502
599
  /** Minimal, safe markdown → HTML renderer (no dependencies). */
503
600
  _renderMarkdown(t) {
@@ -505,7 +602,7 @@ let D = class {
505
602
  let e = this._escHtml(t);
506
603
  return e = e.replace(
507
604
  /```[\w]*\n?([\s\S]*?)```/g,
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>`
605
+ (i, 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>`
509
606
  ), 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(
510
607
  /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
511
608
  '<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'
@@ -525,88 +622,96 @@ let D = class {
525
622
  return String(t).replace(/[&<>"']/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[e]);
526
623
  }
527
624
  };
528
- const V = N(function(t, e) {
625
+ const V = I(function(t, e) {
529
626
  const {
530
- apiKey: a,
627
+ apiKey: i,
531
628
  widgetId: s,
532
- assistantId: r,
629
+ assistantId: a,
533
630
  theme: n = "light",
534
- primaryColor: d = "#2563eb",
535
- botName: o = "AI Assistant",
631
+ primaryColor: o = "#2563eb",
632
+ botName: d = "AI Assistant",
536
633
  botEmoji: c = "🤖",
537
634
  welcomeMessage: l = "Hello! How can I help you today?",
538
- placeholder: g = "Type a message…",
635
+ placeholder: f = "Type a message…",
539
636
  defaultOpen: m = !1,
540
- streaming: h = !0,
541
- backendUrl: w,
637
+ streaming: p = !0,
638
+ backendUrl: y,
542
639
  // Rate limiting — 0 means unlimited
543
- maxMessagesPerSession: L = 0,
640
+ maxMessagesPerSession: S = 0,
544
641
  maxMessagesPerMinute: H = 0,
545
- limitReachedMessage: v,
546
- rateLimitMessage: y,
547
- onOpen: f,
548
- onClose: u
549
- } = t, p = M(null), k = M(null);
550
- return I(e, () => ({
642
+ limitReachedMessage: k,
643
+ rateLimitMessage: M,
644
+ onOpen: u,
645
+ onClose: _,
646
+ // Fields listed here are owned by code and won't be overwritten by
647
+ // dashboard config refreshes. Pass an object whose keys match widget
648
+ // config fields, e.g. { primaryColor: '#7c3aed', botName: 'Aria' }.
649
+ overrideSettings: g
650
+ } = t, h = E(null), C = E(null);
651
+ return N(e, () => ({
551
652
  open: () => {
552
- var i;
553
- return (i = p.current) == null ? void 0 : i.open();
653
+ var r;
654
+ return (r = h.current) == null ? void 0 : r.open();
554
655
  },
555
656
  close: () => {
556
- var i;
557
- return (i = p.current) == null ? void 0 : i.close();
657
+ var r;
658
+ return (r = h.current) == null ? void 0 : r.close();
558
659
  },
559
660
  toggle: () => {
560
- var i;
561
- return (i = p.current) == null ? void 0 : i.toggle();
661
+ var r;
662
+ return (r = h.current) == null ? void 0 : r.toggle();
562
663
  },
563
664
  clearHistory: () => {
564
- var i;
565
- return (i = p.current) == null ? void 0 : i.clearHistory();
665
+ var r;
666
+ return (r = h.current) == null ? void 0 : r.clearHistory();
566
667
  }
567
- }), []), C(() => {
568
- if (!a) {
668
+ }), []), x(() => {
669
+ if (!i) {
569
670
  console.error("[CognitionDesk] apiKey prop is required");
570
671
  return;
571
672
  }
572
- const i = new D({
573
- apiKey: a,
673
+ const r = new D({
674
+ apiKey: i,
574
675
  widgetId: s,
575
- assistantId: r,
676
+ assistantId: a,
576
677
  theme: n,
577
- primaryColor: d,
578
- botName: o,
678
+ primaryColor: o,
679
+ botName: d,
579
680
  botEmoji: c,
580
681
  welcomeMessage: l,
581
- placeholder: g,
582
- streaming: h,
583
- ...w ? { backendUrl: w } : {},
682
+ placeholder: f,
683
+ streaming: p,
684
+ ...y ? { backendUrl: y } : {},
685
+ ...g ? { overrideSettings: g } : {},
584
686
  rateLimiting: {
585
687
  enabled: !0,
586
- maxMessagesPerSession: L,
688
+ maxMessagesPerSession: S,
587
689
  maxMessagesPerMinute: H,
588
- ...v ? { limitReachedMessage: v } : {},
589
- ...y ? { rateLimitMessage: y } : {}
690
+ ...k ? { limitReachedMessage: k } : {},
691
+ ...M ? { rateLimitMessage: M } : {}
590
692
  }
591
693
  });
592
- if (f || u) {
593
- const _ = i.open.bind(i), T = i.close.bind(i);
594
- i.open = (...b) => {
595
- _(...b), f == null || f();
596
- }, i.close = (...b) => {
597
- T(...b), u == null || u();
694
+ if (u || _) {
695
+ const b = r.open.bind(r), T = r.close.bind(r);
696
+ r.open = (...v) => {
697
+ b(...v), u == null || u();
698
+ }, r.close = (...v) => {
699
+ T(...v), _ == null || _();
598
700
  };
599
701
  }
600
- return Promise.resolve(i.mount(k.current)).then(() => {
601
- p.current = i, m && i.open();
702
+ return Promise.resolve(r.mount(C.current)).then(() => {
703
+ h.current = r, m && r.open();
602
704
  }), () => {
603
- i.unmount(), p.current = null;
705
+ r.unmount(), h.current = null;
604
706
  };
605
- }, [a, s, r]), C(() => {
606
- var _;
607
- const i = p.current;
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": "" });
707
+ }, [i, s, a]), x(() => {
708
+ var b;
709
+ const r = h.current;
710
+ r && (r._cfg.theme = n, r._cfg.primaryColor = o, (b = r._applyConfig) == null || b.call(r));
711
+ }, [n, o]), x(() => {
712
+ const r = h.current;
713
+ !r || !g || r.updateSettings(g);
714
+ }, [g]), /* @__PURE__ */ P("div", { ref: C, "data-cognitiondesk-root": "" });
610
715
  });
611
716
  export {
612
717
  V as CognitionDeskWidget,