@cognitiondesk/widget 1.2.0 → 1.2.2

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,174 @@ 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
329
  return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => e()).catch(() => e()) : (e(), Promise.resolve(this));
300
330
  }
331
+ /**
332
+ * Programmatically update settings at runtime.
333
+ * Merges `newSettings` into `overrideSettings` and re-applies to the live DOM.
334
+ * Use this to change colors, bot name, etc. without remounting.
335
+ *
336
+ * widget.updateSettings({ primaryColor: '#dc2626', botName: 'Support Bot' });
337
+ */
338
+ updateSettings(t) {
339
+ if (!t || typeof t != "object") return;
340
+ const e = { ...this._cfg.overrideSettings || {}, ...t };
341
+ this._cfg.overrideSettings = e, this._overrides = new Set(Object.keys(e)), this._applyOverrides(e), this._applyConfig();
342
+ }
343
+ /**
344
+ * Fetch fresh config from the dashboard right now and apply it.
345
+ * Useful after the user changes settings in the CognitionDesk dashboard
346
+ * and wants them reflected immediately without reloading the page.
347
+ */
348
+ async refreshConfig() {
349
+ this._cfg.widgetId && (await this._fetchWidgetConfig(), this._applyConfig());
350
+ }
351
+ // ── Config internals ────────────────────────────────────────────────────────
352
+ /**
353
+ * Returns true if `key` is explicitly controlled by `overrideSettings`.
354
+ * Those keys are immune to server config overwrites.
355
+ */
356
+ _userSet(t) {
357
+ return this._overrides.has(t);
358
+ }
301
359
  /**
302
360
  * Fetch public widget config from the platform and merge into this._cfg.
303
- * Only fields not already overridden by the constructor are applied.
361
+ * Server values apply to any key NOT present in `overrideSettings`.
362
+ * After merging, overrides are re-applied so they always win.
304
363
  */
305
364
  async _fetchWidgetConfig() {
306
365
  var e;
307
366
  const t = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
308
367
  try {
309
- const a = await fetch(t);
310
- if (!a.ok) return;
311
- const { config: s } = await a.json();
368
+ const i = await fetch(t);
369
+ if (!i.ok) return;
370
+ const { config: s } = await i.json();
312
371
  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 });
372
+ const a = (n, o) => {
373
+ o != null && !this._userSet(n) && (this._cfg[n] = o);
374
+ };
375
+ 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);
314
376
  } catch {
315
377
  }
316
378
  }
317
- // eslint-disable-next-line no-unused-vars
318
- _userSet(t) {
319
- return !1;
379
+ /**
380
+ * Write override values into this._cfg (flat fields only; rateLimiting is merged).
381
+ */
382
+ _applyOverrides(t) {
383
+ for (const [e, i] of Object.entries(t))
384
+ e === "rateLimiting" && typeof i == "object" ? this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...i } : this._cfg[e] = i;
385
+ }
386
+ /**
387
+ * Update live DOM elements to reflect the current this._cfg.
388
+ * Safe to call before or after mounting.
389
+ */
390
+ _applyConfig() {
391
+ var s, a, n, o, d, c, l;
392
+ const t = this._cfg.theme === "dark" || this._cfg.theme === "auto" && ((s = window.matchMedia) == null ? void 0 : s.call(window, "(prefers-color-scheme: dark)").matches);
393
+ (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);
394
+ const e = (c = this._shadow) == null ? void 0 : c.querySelector(".cd-header-avatar");
395
+ e && (e.textContent = this._cfg.botEmoji);
396
+ const i = (l = this._shadow) == null ? void 0 : l.querySelector(".cd-header-name");
397
+ i && (i.textContent = this._cfg.botName), this._textarea && (this._textarea.placeholder = this._cfg.placeholder);
398
+ }
399
+ /**
400
+ * Silently refresh config from server in the background.
401
+ * Called each time the panel opens so dashboard changes
402
+ * are reflected without reloading the page.
403
+ * Uses a debounce flag to avoid concurrent fetches.
404
+ */
405
+ _silentRefresh() {
406
+ !this._cfg.widgetId || this._refreshPending || (this._refreshPending = !0, this._fetchWidgetConfig().then(() => this._applyConfig()).catch(() => {
407
+ }).finally(() => {
408
+ this._refreshPending = !1;
409
+ }));
320
410
  }
321
411
  unmount() {
322
412
  this._unbindViewportMetrics(), this._container && (this._container.remove(), this._container = null);
323
413
  }
324
414
  open() {
325
415
  var t, e;
326
- this._open = !0, this._syncViewportMetrics(), (t = this._panel) == null || t.classList.add("open"), (e = this._textarea) == null || e.focus();
416
+ this._open = !0, this._syncViewportMetrics(), (t = this._panel) == null || t.classList.add("open"), (e = this._textarea) == null || e.focus(), this._silentRefresh();
327
417
  }
328
418
  close() {
329
419
  var t;
@@ -340,9 +430,9 @@ let D = class {
340
430
  const t = document.createElement("div");
341
431
  t.className = "cd-root";
342
432
  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;
433
+ 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;
434
+ const i = document.createElement("div");
435
+ i.className = "cd-panel", this._panel = i;
346
436
  const s = document.createElement("div");
347
437
  s.className = "cd-header", s.innerHTML = `
348
438
  <div class="cd-header-avatar">${this._cfg.botEmoji}</div>
@@ -353,22 +443,22 @@ let D = class {
353
443
  </div>
354
444
  </div>
355
445
  `;
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;
446
+ const a = document.createElement("button");
447
+ a.className = "cd-close-btn", a.innerHTML = w.close, a.setAttribute("aria-label", "Close chat"), s.appendChild(a), this._closeBtn = a;
358
448
  const n = document.createElement("div");
359
449
  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;
450
+ const o = document.createElement("div");
451
+ o.className = "cd-input-area";
452
+ const d = document.createElement("textarea");
453
+ d.className = "cd-textarea", d.rows = 1, d.placeholder = this._cfg.placeholder, this._textarea = d;
364
454
  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);
455
+ c.className = "cd-send-btn", c.innerHTML = w.send, c.setAttribute("aria-label", "Send"), this._sendBtn = c, o.appendChild(d), o.appendChild(c);
366
456
  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;
457
+ 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
458
  }
369
459
  _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);
460
+ var e, i, s;
461
+ (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
462
  }
373
463
  // ── Events ──────────────────────────────────────────────────────────────────
374
464
  _bindEvents() {
@@ -389,12 +479,12 @@ let D = class {
389
479
  _syncViewportMetrics() {
390
480
  const t = this._root || this._container;
391
481
  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`);
482
+ const e = window.visualViewport, i = e ? e.height : window.innerHeight;
483
+ t.style.setProperty("--cd-viewport-height", `${Math.round(i)}px`);
394
484
  }
395
485
  // ── Chat ────────────────────────────────────────────────────────────────────
396
486
  async _sendMessage() {
397
- var a;
487
+ var i;
398
488
  const t = this._textarea.value.trim();
399
489
  if (!t || this._loading) return;
400
490
  const e = this._cfg.rateLimiting;
@@ -414,13 +504,13 @@ let D = class {
414
504
  if (this._cfg.streaming)
415
505
  await this._callApiStream();
416
506
  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);
507
+ const s = this._showTyping(), a = await this._callApi();
508
+ this._removeTyping(s), this._messages.push({ role: "assistant", content: a }), this._appendMessage("assistant", a, !0);
419
509
  }
420
510
  } 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);
511
+ if (s.status === 429 || (i = s.message) != null && i.includes("429")) {
512
+ const a = s.rateLimitMessage || (e == null ? void 0 : e.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment.";
513
+ this._appendMessage("error", a, !1);
424
514
  } else
425
515
  this._appendMessage("error", "Sorry, something went wrong. Please try again.", !1);
426
516
  console.error("[CognitionDesk]", s);
@@ -448,56 +538,56 @@ let D = class {
448
538
  body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
449
539
  });
450
540
  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;
541
+ const d = await e.json().catch(() => ({})), c = new Error(d.message || `HTTP ${e.status}`);
542
+ throw c.status = e.status, c.rateLimitMessage = d.message, c;
453
543
  }
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;
544
+ const i = document.createElement("div");
545
+ i.className = "cd-msg assistant", i.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(i), this._scrollToBottom();
546
+ const s = e.body.getReader(), a = new TextDecoder();
547
+ let n = "", o = !1;
458
548
  try {
459
549
  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(`
550
+ const { done: d, value: c } = await s.read();
551
+ if (d) break;
552
+ const l = a.decode(c, { stream: !0 });
553
+ for (const f of l.split(`
464
554
  `)) {
465
- if (!g.startsWith("data: ")) continue;
466
- const m = g.slice(6).trim();
555
+ if (!f.startsWith("data: ")) continue;
556
+ const m = f.slice(6).trim();
467
557
  if (m === "[DONE]") break;
468
- let h;
558
+ let p;
469
559
  try {
470
- h = JSON.parse(m);
560
+ p = JSON.parse(m);
471
561
  } catch {
472
562
  continue;
473
563
  }
474
- h.type === "content" && h.data && (d || (a.innerHTML = "", d = !0), n += h.data, a.innerHTML = this._renderMarkdown(n), this._scrollToBottom());
564
+ p.type === "content" && p.data && (o || (i.innerHTML = "", o = !0), n += p.data, i.innerHTML = this._renderMarkdown(n), this._scrollToBottom());
475
565
  }
476
566
  }
477
567
  } finally {
478
568
  s.releaseLock();
479
569
  }
480
- d || (a.innerHTML = this._renderMarkdown("No response")), n && this._messages.push({ role: "assistant", content: n });
570
+ o || (i.innerHTML = this._renderMarkdown("No response")), n && this._messages.push({ role: "assistant", content: n });
481
571
  }
482
572
  /** Non-streaming fallback path */
483
573
  async _callApi() {
484
- var s, r, n;
574
+ var s, a, n;
485
575
  const t = `${this._cfg.backendUrl}/chat-apiKeyAuth/chat`, e = await fetch(t, {
486
576
  method: "POST",
487
577
  headers: { "Content-Type": "application/json", "x-api-key": this._cfg.apiKey },
488
578
  body: JSON.stringify(this._buildRequestBody())
489
579
  });
490
580
  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;
581
+ const o = await e.json().catch(() => ({})), d = new Error(o.message || `HTTP ${e.status}`);
582
+ throw d.status = e.status, d.rateLimitMessage = o.message, d;
493
583
  }
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";
584
+ const i = await e.json();
585
+ 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
586
  }
497
587
  // ── DOM helpers ─────────────────────────────────────────────────────────────
498
- _appendMessage(t, e, a = !0) {
588
+ _appendMessage(t, e, i = !0) {
499
589
  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();
590
+ s.className = `cd-msg ${t}`, i && t !== "user" ? s.innerHTML = this._renderMarkdown(e) : s.textContent = e, this._messagesEl.appendChild(s), this._scrollToBottom();
501
591
  }
502
592
  /** Minimal, safe markdown → HTML renderer (no dependencies). */
503
593
  _renderMarkdown(t) {
@@ -505,7 +595,7 @@ let D = class {
505
595
  let e = this._escHtml(t);
506
596
  return e = e.replace(
507
597
  /```[\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>`
598
+ (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
599
  ), 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
600
  /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
511
601
  '<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'
@@ -525,88 +615,96 @@ let D = class {
525
615
  return String(t).replace(/[&<>"']/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[e]);
526
616
  }
527
617
  };
528
- const V = N(function(t, e) {
618
+ const V = I(function(t, e) {
529
619
  const {
530
- apiKey: a,
620
+ apiKey: i,
531
621
  widgetId: s,
532
- assistantId: r,
622
+ assistantId: a,
533
623
  theme: n = "light",
534
- primaryColor: d = "#2563eb",
535
- botName: o = "AI Assistant",
624
+ primaryColor: o = "#2563eb",
625
+ botName: d = "AI Assistant",
536
626
  botEmoji: c = "🤖",
537
627
  welcomeMessage: l = "Hello! How can I help you today?",
538
- placeholder: g = "Type a message…",
628
+ placeholder: f = "Type a message…",
539
629
  defaultOpen: m = !1,
540
- streaming: h = !0,
541
- backendUrl: w,
630
+ streaming: p = !0,
631
+ backendUrl: y,
542
632
  // Rate limiting — 0 means unlimited
543
- maxMessagesPerSession: L = 0,
633
+ maxMessagesPerSession: S = 0,
544
634
  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, () => ({
635
+ limitReachedMessage: k,
636
+ rateLimitMessage: M,
637
+ onOpen: u,
638
+ onClose: _,
639
+ // Fields listed here are owned by code and won't be overwritten by
640
+ // dashboard config refreshes. Pass an object whose keys match widget
641
+ // config fields, e.g. { primaryColor: '#7c3aed', botName: 'Aria' }.
642
+ overrideSettings: g
643
+ } = t, h = E(null), C = E(null);
644
+ return N(e, () => ({
551
645
  open: () => {
552
- var i;
553
- return (i = p.current) == null ? void 0 : i.open();
646
+ var r;
647
+ return (r = h.current) == null ? void 0 : r.open();
554
648
  },
555
649
  close: () => {
556
- var i;
557
- return (i = p.current) == null ? void 0 : i.close();
650
+ var r;
651
+ return (r = h.current) == null ? void 0 : r.close();
558
652
  },
559
653
  toggle: () => {
560
- var i;
561
- return (i = p.current) == null ? void 0 : i.toggle();
654
+ var r;
655
+ return (r = h.current) == null ? void 0 : r.toggle();
562
656
  },
563
657
  clearHistory: () => {
564
- var i;
565
- return (i = p.current) == null ? void 0 : i.clearHistory();
658
+ var r;
659
+ return (r = h.current) == null ? void 0 : r.clearHistory();
566
660
  }
567
- }), []), C(() => {
568
- if (!a) {
661
+ }), []), x(() => {
662
+ if (!i) {
569
663
  console.error("[CognitionDesk] apiKey prop is required");
570
664
  return;
571
665
  }
572
- const i = new D({
573
- apiKey: a,
666
+ const r = new D({
667
+ apiKey: i,
574
668
  widgetId: s,
575
- assistantId: r,
669
+ assistantId: a,
576
670
  theme: n,
577
- primaryColor: d,
578
- botName: o,
671
+ primaryColor: o,
672
+ botName: d,
579
673
  botEmoji: c,
580
674
  welcomeMessage: l,
581
- placeholder: g,
582
- streaming: h,
583
- ...w ? { backendUrl: w } : {},
675
+ placeholder: f,
676
+ streaming: p,
677
+ ...y ? { backendUrl: y } : {},
678
+ ...g ? { overrideSettings: g } : {},
584
679
  rateLimiting: {
585
680
  enabled: !0,
586
- maxMessagesPerSession: L,
681
+ maxMessagesPerSession: S,
587
682
  maxMessagesPerMinute: H,
588
- ...v ? { limitReachedMessage: v } : {},
589
- ...y ? { rateLimitMessage: y } : {}
683
+ ...k ? { limitReachedMessage: k } : {},
684
+ ...M ? { rateLimitMessage: M } : {}
590
685
  }
591
686
  });
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();
687
+ if (u || _) {
688
+ const b = r.open.bind(r), T = r.close.bind(r);
689
+ r.open = (...v) => {
690
+ b(...v), u == null || u();
691
+ }, r.close = (...v) => {
692
+ T(...v), _ == null || _();
598
693
  };
599
694
  }
600
- return Promise.resolve(i.mount(k.current)).then(() => {
601
- p.current = i, m && i.open();
695
+ return Promise.resolve(r.mount(C.current)).then(() => {
696
+ h.current = r, m && r.open();
602
697
  }), () => {
603
- i.unmount(), p.current = null;
698
+ r.unmount(), h.current = null;
604
699
  };
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": "" });
700
+ }, [i, s, a]), x(() => {
701
+ var b;
702
+ const r = h.current;
703
+ r && (r._cfg.theme = n, r._cfg.primaryColor = o, (b = r._applyConfig) == null || b.call(r));
704
+ }, [n, o]), x(() => {
705
+ const r = h.current;
706
+ !r || !g || r.updateSettings(g);
707
+ }, [g]), /* @__PURE__ */ P("div", { ref: C, "data-cognitiondesk-root": "" });
610
708
  });
611
709
  export {
612
710
  V as CognitionDeskWidget,