@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/LICENSE +21 -0
- package/README.md +1 -268
- package/dist/react.es.js +230 -125
- package/dist/react.umd.cjs +4 -4
- package/dist/widget.es.js +144 -47
- package/dist/widget.umd.cjs +4 -4
- package/package.json +1 -1
- package/src/react.jsx +25 -2
- package/src/widget.js +204 -36
package/dist/react.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
const
|
|
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
|
-
`,
|
|
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
|
|
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,
|
|
288
|
+
var e, i, s, a, n;
|
|
260
289
|
if (!t.apiKey) throw new Error("[CognitionDesk] apiKey is required");
|
|
261
|
-
this.
|
|
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 ||
|
|
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: ((
|
|
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: ((
|
|
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
|
-
|
|
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`
|
|
290
|
-
* Returns a Promise so callers can await
|
|
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
|
|
295
|
-
this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""),
|
|
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 =
|
|
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(() =>
|
|
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
|
-
*
|
|
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
|
|
310
|
-
if (
|
|
311
|
-
|
|
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
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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 =
|
|
344
|
-
const
|
|
345
|
-
|
|
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
|
|
357
|
-
|
|
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
|
|
361
|
-
|
|
362
|
-
const
|
|
363
|
-
|
|
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 =
|
|
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>',
|
|
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,
|
|
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")), (
|
|
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,
|
|
393
|
-
t.style.setProperty("--cd-viewport-height", `${Math.round(
|
|
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
|
|
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(),
|
|
418
|
-
this._removeTyping(s), this._messages.push({ role: "assistant", content:
|
|
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 || (
|
|
422
|
-
const
|
|
423
|
-
this._appendMessage("error",
|
|
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
|
|
452
|
-
throw c.status = e.status, c.rateLimitMessage =
|
|
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
|
|
455
|
-
|
|
456
|
-
const s = e.body.getReader(),
|
|
457
|
-
let n = "",
|
|
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:
|
|
461
|
-
if (
|
|
462
|
-
const l =
|
|
463
|
-
for (const
|
|
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 (!
|
|
466
|
-
const m =
|
|
562
|
+
if (!f.startsWith("data: ")) continue;
|
|
563
|
+
const m = f.slice(6).trim();
|
|
467
564
|
if (m === "[DONE]") break;
|
|
468
|
-
let
|
|
565
|
+
let p;
|
|
469
566
|
try {
|
|
470
|
-
|
|
567
|
+
p = JSON.parse(m);
|
|
471
568
|
} catch {
|
|
472
569
|
continue;
|
|
473
570
|
}
|
|
474
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
492
|
-
throw
|
|
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
|
|
495
|
-
return
|
|
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,
|
|
595
|
+
_appendMessage(t, e, i = !0) {
|
|
499
596
|
const s = document.createElement("div");
|
|
500
|
-
s.className = `cd-msg ${t}`,
|
|
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
|
-
(
|
|
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) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[e]);
|
|
526
623
|
}
|
|
527
624
|
};
|
|
528
|
-
const V =
|
|
625
|
+
const V = I(function(t, e) {
|
|
529
626
|
const {
|
|
530
|
-
apiKey:
|
|
627
|
+
apiKey: i,
|
|
531
628
|
widgetId: s,
|
|
532
|
-
assistantId:
|
|
629
|
+
assistantId: a,
|
|
533
630
|
theme: n = "light",
|
|
534
|
-
primaryColor:
|
|
535
|
-
botName:
|
|
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:
|
|
635
|
+
placeholder: f = "Type a message…",
|
|
539
636
|
defaultOpen: m = !1,
|
|
540
|
-
streaming:
|
|
541
|
-
backendUrl:
|
|
637
|
+
streaming: p = !0,
|
|
638
|
+
backendUrl: y,
|
|
542
639
|
// Rate limiting — 0 means unlimited
|
|
543
|
-
maxMessagesPerSession:
|
|
640
|
+
maxMessagesPerSession: S = 0,
|
|
544
641
|
maxMessagesPerMinute: H = 0,
|
|
545
|
-
limitReachedMessage:
|
|
546
|
-
rateLimitMessage:
|
|
547
|
-
onOpen:
|
|
548
|
-
onClose:
|
|
549
|
-
|
|
550
|
-
|
|
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
|
|
553
|
-
return (
|
|
653
|
+
var r;
|
|
654
|
+
return (r = h.current) == null ? void 0 : r.open();
|
|
554
655
|
},
|
|
555
656
|
close: () => {
|
|
556
|
-
var
|
|
557
|
-
return (
|
|
657
|
+
var r;
|
|
658
|
+
return (r = h.current) == null ? void 0 : r.close();
|
|
558
659
|
},
|
|
559
660
|
toggle: () => {
|
|
560
|
-
var
|
|
561
|
-
return (
|
|
661
|
+
var r;
|
|
662
|
+
return (r = h.current) == null ? void 0 : r.toggle();
|
|
562
663
|
},
|
|
563
664
|
clearHistory: () => {
|
|
564
|
-
var
|
|
565
|
-
return (
|
|
665
|
+
var r;
|
|
666
|
+
return (r = h.current) == null ? void 0 : r.clearHistory();
|
|
566
667
|
}
|
|
567
|
-
}), []),
|
|
568
|
-
if (!
|
|
668
|
+
}), []), x(() => {
|
|
669
|
+
if (!i) {
|
|
569
670
|
console.error("[CognitionDesk] apiKey prop is required");
|
|
570
671
|
return;
|
|
571
672
|
}
|
|
572
|
-
const
|
|
573
|
-
apiKey:
|
|
673
|
+
const r = new D({
|
|
674
|
+
apiKey: i,
|
|
574
675
|
widgetId: s,
|
|
575
|
-
assistantId:
|
|
676
|
+
assistantId: a,
|
|
576
677
|
theme: n,
|
|
577
|
-
primaryColor:
|
|
578
|
-
botName:
|
|
678
|
+
primaryColor: o,
|
|
679
|
+
botName: d,
|
|
579
680
|
botEmoji: c,
|
|
580
681
|
welcomeMessage: l,
|
|
581
|
-
placeholder:
|
|
582
|
-
streaming:
|
|
583
|
-
...
|
|
682
|
+
placeholder: f,
|
|
683
|
+
streaming: p,
|
|
684
|
+
...y ? { backendUrl: y } : {},
|
|
685
|
+
...g ? { overrideSettings: g } : {},
|
|
584
686
|
rateLimiting: {
|
|
585
687
|
enabled: !0,
|
|
586
|
-
maxMessagesPerSession:
|
|
688
|
+
maxMessagesPerSession: S,
|
|
587
689
|
maxMessagesPerMinute: H,
|
|
588
|
-
...
|
|
589
|
-
...
|
|
690
|
+
...k ? { limitReachedMessage: k } : {},
|
|
691
|
+
...M ? { rateLimitMessage: M } : {}
|
|
590
692
|
}
|
|
591
693
|
});
|
|
592
|
-
if (
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
},
|
|
597
|
-
T(...
|
|
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(
|
|
601
|
-
|
|
702
|
+
return Promise.resolve(r.mount(C.current)).then(() => {
|
|
703
|
+
h.current = r, m && r.open();
|
|
602
704
|
}), () => {
|
|
603
|
-
|
|
705
|
+
r.unmount(), h.current = null;
|
|
604
706
|
};
|
|
605
|
-
}, [
|
|
606
|
-
var
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
}, [n,
|
|
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,
|