@cognitiondesk/widget 1.1.0 β 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react.es.js +171 -133
- package/dist/react.umd.cjs +4 -4
- package/dist/widget.es.js +106 -80
- package/dist/widget.umd.cjs +4 -4
- package/package.json +1 -1
- package/src/react.jsx +12 -0
- package/src/widget.js +57 -5
package/dist/react.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
const
|
|
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
|
-
`,
|
|
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
|
|
254
|
+
function z() {
|
|
255
255
|
return "cd_" + Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
256
256
|
}
|
|
257
|
-
let
|
|
258
|
-
constructor(
|
|
259
|
-
|
|
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:
|
|
262
|
-
widgetId:
|
|
263
|
-
assistantId:
|
|
264
|
-
backendUrl:
|
|
265
|
-
primaryColor:
|
|
266
|
-
theme:
|
|
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:
|
|
269
|
-
botEmoji:
|
|
270
|
-
welcomeMessage:
|
|
271
|
-
placeholder:
|
|
272
|
-
position:
|
|
273
|
-
streaming:
|
|
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
|
-
|
|
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(
|
|
284
|
-
const
|
|
285
|
-
const a =
|
|
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 =
|
|
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(() =>
|
|
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
|
|
298
|
-
const
|
|
306
|
+
var e;
|
|
307
|
+
const t = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
|
|
299
308
|
try {
|
|
300
|
-
const a = await fetch(
|
|
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), (
|
|
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(
|
|
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
|
|
317
|
-
this._open = !0, this._syncViewportMetrics(), (
|
|
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
|
|
321
|
-
this._open = !1, (
|
|
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
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
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
|
|
348
|
-
|
|
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-
|
|
353
|
-
const d = document.createElement("
|
|
354
|
-
d.className = "cd-
|
|
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 =
|
|
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(
|
|
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
|
|
362
|
-
(this._cfg.theme === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : this._cfg.theme) === "dark" && ((
|
|
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", (
|
|
367
|
-
|
|
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
|
|
382
|
-
if (!
|
|
383
|
-
const
|
|
384
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
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 (!
|
|
425
|
-
const
|
|
426
|
-
throw
|
|
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 =
|
|
431
|
-
let
|
|
456
|
+
const s = e.body.getReader(), r = new TextDecoder();
|
|
457
|
+
let n = "", d = !1;
|
|
432
458
|
try {
|
|
433
459
|
for (; ; ) {
|
|
434
|
-
const { done:
|
|
435
|
-
if (
|
|
436
|
-
const l =
|
|
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
|
|
441
|
-
if (
|
|
466
|
+
const m = g.slice(6).trim();
|
|
467
|
+
if (m === "[DONE]") break;
|
|
442
468
|
let h;
|
|
443
469
|
try {
|
|
444
|
-
h = JSON.parse(
|
|
470
|
+
h = JSON.parse(m);
|
|
445
471
|
} catch {
|
|
446
472
|
continue;
|
|
447
473
|
}
|
|
448
|
-
h.type === "content" && h.data && (
|
|
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
|
-
|
|
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,
|
|
459
|
-
const
|
|
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 (!
|
|
465
|
-
const
|
|
466
|
-
throw
|
|
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
|
|
469
|
-
return a.response || a.message || a.content || ((
|
|
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(
|
|
498
|
+
_appendMessage(t, e, a = !0) {
|
|
473
499
|
const s = document.createElement("div");
|
|
474
|
-
s.className = `cd-msg ${
|
|
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(
|
|
478
|
-
if (!
|
|
479
|
-
let
|
|
480
|
-
return
|
|
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
|
-
),
|
|
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
|
-
),
|
|
512
|
+
), e = e.replace(/\n/g, "<br>"), e;
|
|
487
513
|
}
|
|
488
514
|
_showTyping() {
|
|
489
|
-
const
|
|
490
|
-
return
|
|
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(
|
|
493
|
-
|
|
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(
|
|
499
|
-
return String(
|
|
524
|
+
_escHtml(t) {
|
|
525
|
+
return String(t).replace(/[&<>"']/g, (e) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[e]);
|
|
500
526
|
}
|
|
501
527
|
};
|
|
502
|
-
const
|
|
528
|
+
const V = N(function(t, e) {
|
|
503
529
|
const {
|
|
504
530
|
apiKey: a,
|
|
505
531
|
widgetId: s,
|
|
506
|
-
assistantId:
|
|
507
|
-
theme:
|
|
508
|
-
primaryColor:
|
|
509
|
-
botName:
|
|
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:
|
|
539
|
+
defaultOpen: m = !1,
|
|
514
540
|
streaming: h = !0,
|
|
515
|
-
backendUrl:
|
|
516
|
-
|
|
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
|
-
} =
|
|
519
|
-
return
|
|
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
|
-
}), []),
|
|
567
|
+
}), []), C(() => {
|
|
537
568
|
if (!a) {
|
|
538
569
|
console.error("[CognitionDesk] apiKey prop is required");
|
|
539
570
|
return;
|
|
540
571
|
}
|
|
541
|
-
const i = new
|
|
572
|
+
const i = new D({
|
|
542
573
|
apiKey: a,
|
|
543
574
|
widgetId: s,
|
|
544
|
-
assistantId:
|
|
545
|
-
theme:
|
|
546
|
-
primaryColor:
|
|
547
|
-
botName:
|
|
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
|
-
...
|
|
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 (
|
|
555
|
-
const _ = i.open.bind(i),
|
|
592
|
+
if (f || u) {
|
|
593
|
+
const _ = i.open.bind(i), T = i.close.bind(i);
|
|
556
594
|
i.open = (...b) => {
|
|
557
|
-
_(...b),
|
|
595
|
+
_(...b), f == null || f();
|
|
558
596
|
}, i.close = (...b) => {
|
|
559
|
-
|
|
597
|
+
T(...b), u == null || u();
|
|
560
598
|
};
|
|
561
599
|
}
|
|
562
|
-
return Promise.resolve(i.mount(
|
|
563
|
-
p.current = i,
|
|
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,
|
|
605
|
+
}, [a, s, r]), C(() => {
|
|
568
606
|
var _;
|
|
569
607
|
const i = p.current;
|
|
570
|
-
i && (i._cfg.theme =
|
|
571
|
-
}, [
|
|
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
|
-
|
|
575
|
-
|
|
612
|
+
V as CognitionDeskWidget,
|
|
613
|
+
D as CognitionDeskWidgetCore
|
|
576
614
|
};
|
package/dist/react.umd.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(
|
|
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
|
|
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
|
|
256
|
-
`)){if(!
|
|
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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[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"})});
|
package/dist/widget.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const u = "https://mounaji-backendv3.onrender.com",
|
|
1
|
+
const u = "https://mounaji-backendv3.onrender.com", _ = `
|
|
2
2
|
:host {
|
|
3
3
|
all: initial;
|
|
4
4
|
font-family: system-ui, sans-serif;
|
|
@@ -244,16 +244,17 @@ const u = "https://mounaji-backendv3.onrender.com", b = `
|
|
|
244
244
|
font-size: 16px;
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
`,
|
|
247
|
+
`, p = {
|
|
248
248
|
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>',
|
|
249
249
|
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>',
|
|
250
250
|
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>'
|
|
251
251
|
};
|
|
252
|
-
function
|
|
252
|
+
function b() {
|
|
253
253
|
return "cd_" + Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
254
254
|
}
|
|
255
|
-
class
|
|
255
|
+
class f {
|
|
256
256
|
constructor(e = {}) {
|
|
257
|
+
var t, a, s, i, r;
|
|
257
258
|
if (!e.apiKey) throw new Error("[CognitionDesk] apiKey is required");
|
|
258
259
|
this._cfg = {
|
|
259
260
|
apiKey: e.apiKey,
|
|
@@ -268,9 +269,17 @@ class m {
|
|
|
268
269
|
welcomeMessage: e.welcomeMessage || "Hello! How can I help you today?",
|
|
269
270
|
placeholder: e.placeholder || "Type a messageβ¦",
|
|
270
271
|
position: e.position || "bottom-right",
|
|
271
|
-
streaming: e.streaming !== !1
|
|
272
|
+
streaming: e.streaming !== !1,
|
|
272
273
|
// default: true
|
|
273
|
-
|
|
274
|
+
// Rate limiting β can be overridden by inline config or merged from server config
|
|
275
|
+
rateLimiting: {
|
|
276
|
+
enabled: ((t = e.rateLimiting) == null ? void 0 : t.enabled) !== !1,
|
|
277
|
+
maxMessagesPerSession: ((a = e.rateLimiting) == null ? void 0 : a.maxMessagesPerSession) ?? 0,
|
|
278
|
+
maxMessagesPerMinute: ((s = e.rateLimiting) == null ? void 0 : s.maxMessagesPerMinute) ?? 0,
|
|
279
|
+
limitReachedMessage: ((i = e.rateLimiting) == null ? void 0 : i.limitReachedMessage) || "You've reached the message limit for this session.",
|
|
280
|
+
rateLimitMessage: ((r = e.rateLimiting) == null ? void 0 : r.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment."
|
|
281
|
+
}
|
|
282
|
+
}, 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;
|
|
274
283
|
}
|
|
275
284
|
// ββ Public API ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
276
285
|
/**
|
|
@@ -280,10 +289,10 @@ class m {
|
|
|
280
289
|
*/
|
|
281
290
|
mount(e) {
|
|
282
291
|
const t = () => {
|
|
283
|
-
const
|
|
284
|
-
this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""),
|
|
285
|
-
const
|
|
286
|
-
|
|
292
|
+
const a = e || document.body;
|
|
293
|
+
this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""), a.appendChild(this._container), this._shadow = this._container.attachShadow({ mode: "open" });
|
|
294
|
+
const s = document.createElement("style");
|
|
295
|
+
s.textContent = _, this._shadow.appendChild(s), this._buildDOM(), this._applyTheme(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
|
|
287
296
|
};
|
|
288
297
|
return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => t()).catch(() => t()) : (t(), Promise.resolve(this));
|
|
289
298
|
}
|
|
@@ -295,11 +304,11 @@ class m {
|
|
|
295
304
|
var t;
|
|
296
305
|
const e = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
|
|
297
306
|
try {
|
|
298
|
-
const
|
|
299
|
-
if (!
|
|
300
|
-
const { config:
|
|
301
|
-
if (!
|
|
302
|
-
|
|
307
|
+
const a = await fetch(e);
|
|
308
|
+
if (!a.ok) return;
|
|
309
|
+
const { config: s } = await a.json();
|
|
310
|
+
if (!s) return;
|
|
311
|
+
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), s.rateLimiting && (this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...s.rateLimiting });
|
|
303
312
|
} catch {
|
|
304
313
|
}
|
|
305
314
|
}
|
|
@@ -329,11 +338,11 @@ class m {
|
|
|
329
338
|
const e = document.createElement("div");
|
|
330
339
|
e.className = "cd-root";
|
|
331
340
|
const t = document.createElement("button");
|
|
332
|
-
t.className = "cd-widget-btn", t.innerHTML =
|
|
333
|
-
const s = document.createElement("div");
|
|
334
|
-
s.className = "cd-panel", this._panel = s;
|
|
341
|
+
t.className = "cd-widget-btn", t.innerHTML = p.chat, t.setAttribute("aria-label", "Open chat"), t.style.setProperty("--cd-primary", this._cfg.primaryColor), this._toggleBtn = t;
|
|
335
342
|
const a = document.createElement("div");
|
|
336
|
-
a.className = "cd-
|
|
343
|
+
a.className = "cd-panel", this._panel = a;
|
|
344
|
+
const s = document.createElement("div");
|
|
345
|
+
s.className = "cd-header", s.innerHTML = `
|
|
337
346
|
<div class="cd-header-avatar">${this._cfg.botEmoji}</div>
|
|
338
347
|
<div class="cd-header-info">
|
|
339
348
|
<div class="cd-header-name">${this._escHtml(this._cfg.botName)}</div>
|
|
@@ -342,22 +351,22 @@ class m {
|
|
|
342
351
|
</div>
|
|
343
352
|
</div>
|
|
344
353
|
`;
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
354
|
+
const i = document.createElement("button");
|
|
355
|
+
i.className = "cd-close-btn", i.innerHTML = p.close, i.setAttribute("aria-label", "Close chat"), s.appendChild(i), this._closeBtn = i;
|
|
356
|
+
const r = document.createElement("div");
|
|
357
|
+
r.className = "cd-messages", r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), this._messagesEl = r;
|
|
358
|
+
const o = document.createElement("div");
|
|
359
|
+
o.className = "cd-input-area";
|
|
360
|
+
const n = document.createElement("textarea");
|
|
361
|
+
n.className = "cd-textarea", n.rows = 1, n.placeholder = this._cfg.placeholder, this._textarea = n;
|
|
353
362
|
const d = document.createElement("button");
|
|
354
|
-
d.className = "cd-send-btn", d.innerHTML =
|
|
363
|
+
d.className = "cd-send-btn", d.innerHTML = p.send, d.setAttribute("aria-label", "Send"), this._sendBtn = d, o.appendChild(n), o.appendChild(d);
|
|
355
364
|
const l = document.createElement("div");
|
|
356
|
-
l.className = "cd-powered", l.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',
|
|
365
|
+
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(o), a.appendChild(l), e.appendChild(t), e.appendChild(a), this._shadow.appendChild(e), this._root = e;
|
|
357
366
|
}
|
|
358
367
|
_applyTheme() {
|
|
359
|
-
var t,
|
|
360
|
-
(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")), (
|
|
368
|
+
var t, a, s;
|
|
369
|
+
(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);
|
|
361
370
|
}
|
|
362
371
|
// ββ Events ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
363
372
|
_bindEvents() {
|
|
@@ -378,27 +387,44 @@ class m {
|
|
|
378
387
|
_syncViewportMetrics() {
|
|
379
388
|
const e = this._root || this._container;
|
|
380
389
|
if (!e) return;
|
|
381
|
-
const t = window.visualViewport,
|
|
382
|
-
e.style.setProperty("--cd-viewport-height", `${Math.round(
|
|
390
|
+
const t = window.visualViewport, a = t ? t.height : window.innerHeight;
|
|
391
|
+
e.style.setProperty("--cd-viewport-height", `${Math.round(a)}px`);
|
|
383
392
|
}
|
|
384
393
|
// ββ Chat ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
385
394
|
async _sendMessage() {
|
|
395
|
+
var a;
|
|
386
396
|
const e = this._textarea.value.trim();
|
|
387
|
-
if (!
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
} finally {
|
|
399
|
-
this._loading = !1, this._sendBtn.disabled = !1, this._textarea.focus();
|
|
397
|
+
if (!e || this._loading) return;
|
|
398
|
+
const t = this._cfg.rateLimiting;
|
|
399
|
+
if (t != null && t.enabled) {
|
|
400
|
+
const s = Date.now();
|
|
401
|
+
if (s - this._minuteStart >= 6e4 && (this._minuteCount = 0, this._minuteStart = s), t.maxMessagesPerSession > 0 && this._messageCount >= t.maxMessagesPerSession) {
|
|
402
|
+
this._appendMessage("error", t.limitReachedMessage, !1), this._textarea.disabled = !0, this._sendBtn.disabled = !0;
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (t.maxMessagesPerMinute > 0 && this._minuteCount >= t.maxMessagesPerMinute) {
|
|
406
|
+
this._appendMessage("error", t.rateLimitMessage, !1);
|
|
407
|
+
return;
|
|
400
408
|
}
|
|
401
409
|
}
|
|
410
|
+
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), this._messageCount += 1, this._minuteCount += 1;
|
|
411
|
+
try {
|
|
412
|
+
if (this._cfg.streaming)
|
|
413
|
+
await this._callApiStream();
|
|
414
|
+
else {
|
|
415
|
+
const s = this._showTyping(), i = await this._callApi();
|
|
416
|
+
this._removeTyping(s), this._messages.push({ role: "assistant", content: i }), this._appendMessage("assistant", i, !0);
|
|
417
|
+
}
|
|
418
|
+
} catch (s) {
|
|
419
|
+
if (s.status === 429 || (a = s.message) != null && a.includes("429")) {
|
|
420
|
+
const i = s.rateLimitMessage || (t == null ? void 0 : t.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment.";
|
|
421
|
+
this._appendMessage("error", i, !1);
|
|
422
|
+
} else
|
|
423
|
+
this._appendMessage("error", "Sorry, something went wrong. Please try again.", !1);
|
|
424
|
+
console.error("[CognitionDesk]", s);
|
|
425
|
+
} finally {
|
|
426
|
+
this._loading = !1, (t == null ? void 0 : t.maxMessagesPerSession) > 0 && this._messageCount >= t.maxMessagesPerSession || (this._sendBtn.disabled = !1), this._textarea.focus();
|
|
427
|
+
}
|
|
402
428
|
}
|
|
403
429
|
_buildRequestBody() {
|
|
404
430
|
return {
|
|
@@ -420,56 +446,56 @@ class m {
|
|
|
420
446
|
body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
|
|
421
447
|
});
|
|
422
448
|
if (!t.ok) {
|
|
423
|
-
const
|
|
424
|
-
throw
|
|
449
|
+
const n = await t.json().catch(() => ({})), d = new Error(n.message || `HTTP ${t.status}`);
|
|
450
|
+
throw d.status = t.status, d.rateLimitMessage = n.message, d;
|
|
425
451
|
}
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
let
|
|
452
|
+
const a = document.createElement("div");
|
|
453
|
+
a.className = "cd-msg assistant", a.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(a), this._scrollToBottom();
|
|
454
|
+
const s = t.body.getReader(), i = new TextDecoder();
|
|
455
|
+
let r = "", o = !1;
|
|
430
456
|
try {
|
|
431
457
|
for (; ; ) {
|
|
432
|
-
const { done:
|
|
433
|
-
if (
|
|
434
|
-
const l =
|
|
458
|
+
const { done: n, value: d } = await s.read();
|
|
459
|
+
if (n) break;
|
|
460
|
+
const l = i.decode(d, { stream: !0 });
|
|
435
461
|
for (const g of l.split(`
|
|
436
462
|
`)) {
|
|
437
463
|
if (!g.startsWith("data: ")) continue;
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
440
|
-
let
|
|
464
|
+
const m = g.slice(6).trim();
|
|
465
|
+
if (m === "[DONE]") break;
|
|
466
|
+
let h;
|
|
441
467
|
try {
|
|
442
|
-
|
|
468
|
+
h = JSON.parse(m);
|
|
443
469
|
} catch {
|
|
444
470
|
continue;
|
|
445
471
|
}
|
|
446
|
-
|
|
472
|
+
h.type === "content" && h.data && (o || (a.innerHTML = "", o = !0), r += h.data, a.innerHTML = this._renderMarkdown(r), this._scrollToBottom());
|
|
447
473
|
}
|
|
448
474
|
}
|
|
449
475
|
} finally {
|
|
450
|
-
|
|
476
|
+
s.releaseLock();
|
|
451
477
|
}
|
|
452
|
-
|
|
478
|
+
o || (a.innerHTML = this._renderMarkdown("No response")), r && this._messages.push({ role: "assistant", content: r });
|
|
453
479
|
}
|
|
454
480
|
/** Non-streaming fallback path */
|
|
455
481
|
async _callApi() {
|
|
456
|
-
var
|
|
482
|
+
var s, i, r;
|
|
457
483
|
const e = `${this._cfg.backendUrl}/chat-apiKeyAuth/chat`, t = await fetch(e, {
|
|
458
484
|
method: "POST",
|
|
459
485
|
headers: { "Content-Type": "application/json", "x-api-key": this._cfg.apiKey },
|
|
460
486
|
body: JSON.stringify(this._buildRequestBody())
|
|
461
487
|
});
|
|
462
488
|
if (!t.ok) {
|
|
463
|
-
const
|
|
464
|
-
throw
|
|
489
|
+
const o = await t.json().catch(() => ({})), n = new Error(o.message || `HTTP ${t.status}`);
|
|
490
|
+
throw n.status = t.status, n.rateLimitMessage = o.message, n;
|
|
465
491
|
}
|
|
466
|
-
const
|
|
467
|
-
return
|
|
492
|
+
const a = await t.json();
|
|
493
|
+
return a.response || a.message || a.content || ((r = (i = (s = a.choices) == null ? void 0 : s[0]) == null ? void 0 : i.message) == null ? void 0 : r.content) || "No response";
|
|
468
494
|
}
|
|
469
495
|
// ββ DOM helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
470
|
-
_appendMessage(e, t,
|
|
471
|
-
const
|
|
472
|
-
|
|
496
|
+
_appendMessage(e, t, a = !0) {
|
|
497
|
+
const s = document.createElement("div");
|
|
498
|
+
s.className = `cd-msg ${e}`, a && e !== "user" ? s.innerHTML = this._renderMarkdown(t) : s.textContent = t, this._messagesEl.appendChild(s), this._scrollToBottom();
|
|
473
499
|
}
|
|
474
500
|
/** Minimal, safe markdown β HTML renderer (no dependencies). */
|
|
475
501
|
_renderMarkdown(e) {
|
|
@@ -477,7 +503,7 @@ class m {
|
|
|
477
503
|
let t = this._escHtml(e);
|
|
478
504
|
return t = t.replace(
|
|
479
505
|
/```[\w]*\n?([\s\S]*?)```/g,
|
|
480
|
-
(
|
|
506
|
+
(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>`
|
|
481
507
|
), 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(
|
|
482
508
|
/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
|
|
483
509
|
'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'
|
|
@@ -497,8 +523,8 @@ class m {
|
|
|
497
523
|
return String(e).replace(/[&<>"']/g, (t) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[t]);
|
|
498
524
|
}
|
|
499
525
|
}
|
|
500
|
-
function
|
|
501
|
-
const e = new
|
|
526
|
+
function x(c) {
|
|
527
|
+
const e = new f(c);
|
|
502
528
|
return e.mount(), e;
|
|
503
529
|
}
|
|
504
530
|
if (typeof document < "u") {
|
|
@@ -506,9 +532,9 @@ if (typeof document < "u") {
|
|
|
506
532
|
document.querySelectorAll("script[data-api-key]").forEach((e) => {
|
|
507
533
|
const t = e.getAttribute("data-api-key");
|
|
508
534
|
if (!t) return;
|
|
509
|
-
const
|
|
535
|
+
const a = e.getAttribute("data-widget-id") || void 0, s = e.getAttribute("data-streaming"), i = new f({
|
|
510
536
|
apiKey: t,
|
|
511
|
-
widgetId:
|
|
537
|
+
widgetId: a,
|
|
512
538
|
assistantId: e.getAttribute("data-assistant-id") || void 0,
|
|
513
539
|
theme: e.getAttribute("data-theme") || "light",
|
|
514
540
|
primaryColor: e.getAttribute("data-primary-color") || "#2563eb",
|
|
@@ -517,15 +543,15 @@ if (typeof document < "u") {
|
|
|
517
543
|
welcomeMessage: e.getAttribute("data-welcome-message") || void 0,
|
|
518
544
|
placeholder: e.getAttribute("data-placeholder") || void 0,
|
|
519
545
|
backendUrl: e.getAttribute("data-backend-url") || void 0,
|
|
520
|
-
streaming:
|
|
546
|
+
streaming: s === null ? !0 : s !== "false"
|
|
521
547
|
});
|
|
522
|
-
Promise.resolve(
|
|
548
|
+
Promise.resolve(i.mount());
|
|
523
549
|
});
|
|
524
550
|
};
|
|
525
551
|
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", c) : c();
|
|
526
552
|
}
|
|
527
553
|
export {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
554
|
+
f as CognitionDeskWidget,
|
|
555
|
+
f as default,
|
|
556
|
+
x as init
|
|
531
557
|
};
|
package/dist/widget.umd.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(c,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(c=typeof globalThis<"u"?globalThis:c||self,l(c.CognitionDeskWidget={}))})(this,function(c){"use strict";const l="https://mounaji-backendv3.onrender.com",b=`
|
|
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
|
-
`,
|
|
247
|
+
`,f={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 w(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}class g{constructor(e={}){var t,a,s,i,n;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||l,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,rateLimiting:{enabled:((t=e.rateLimiting)==null?void 0:t.enabled)!==!1,maxMessagesPerSession:((a=e.rateLimiting)==null?void 0:a.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=e.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((i=e.rateLimiting)==null?void 0:i.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((n=e.rateLimiting)==null?void 0:n.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."}},this._sessionId=w(),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(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=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)};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),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting})}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=f.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=`
|
|
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
|
|
256
|
-
`)){if(!u.startsWith("data: "))continue;const
|
|
255
|
+
`;const i=document.createElement("button");i.className="cd-close-btn",i.innerHTML=f.close,i.setAttribute("aria-label","Close chat"),s.appendChild(i),this._closeBtn=i;const n=document.createElement("div");n.className="cd-messages",n.setAttribute("role","log"),n.setAttribute("aria-live","polite"),this._messagesEl=n;const o=document.createElement("div");o.className="cd-input-area";const r=document.createElement("textarea");r.className="cd-textarea",r.rows=1,r.placeholder=this._cfg.placeholder,this._textarea=r;const d=document.createElement("button");d.className="cd-send-btn",d.innerHTML=f.send,d.setAttribute("aria-label","Send"),this._sendBtn=d,o.appendChild(r),o.appendChild(d);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(o),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(){var a;const e=this._textarea.value.trim();if(!e||this._loading)return;const t=this._cfg.rateLimiting;if(t!=null&&t.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),t.maxMessagesPerSession>0&&this._messageCount>=t.maxMessagesPerSession){this._appendMessage("error",t.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(t.maxMessagesPerMinute>0&&this._minuteCount>=t.maxMessagesPerMinute){this._appendMessage("error",t.rateLimitMessage,!1);return}}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),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),i=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:i}),this._appendMessage("assistant",i,!0)}}catch(s){if(s.status===429||(a=s.message)!=null&&a.includes("429")){const i=s.rateLimitMessage||(t==null?void 0:t.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",i,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(t==null?void 0:t.maxMessagesPerSession)>0&&this._messageCount>=t.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 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 r=await t.json().catch(()=>({})),d=new Error(r.message||`HTTP ${t.status}`);throw d.status=t.status,d.rateLimitMessage=r.message,d}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(),i=new TextDecoder;let n="",o=!1;try{for(;;){const{done:r,value:d}=await s.read();if(r)break;const p=i.decode(d,{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&&(o||(a.innerHTML="",o=!0),n+=m.data,a.innerHTML=this._renderMarkdown(n),this._scrollToBottom())}}}finally{s.releaseLock()}o||(a.innerHTML=this._renderMarkdown("No response")),n&&this._messages.push({role:"assistant",content:n})}async _callApi(){var s,i,n;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 o=await t.json().catch(()=>({})),r=new Error(o.message||`HTTP ${t.status}`);throw r.status=t.status,r.rateLimitMessage=o.message,r}const a=await t.json();return a.response||a.message||a.content||((n=(i=(s=a.choices)==null?void 0:s[0])==null?void 0:i.message)==null?void 0:n.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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}}function x(h){const e=new g(h);return e.mount(),e}if(typeof document<"u"){const h=()=>{document.querySelectorAll("script[data-api-key]").forEach(e=>{const t=e.getAttribute("data-api-key");if(!t)return;const a=e.getAttribute("data-widget-id")||void 0,s=e.getAttribute("data-streaming"),i=new g({apiKey:t,widgetId:a,assistantId:e.getAttribute("data-assistant-id")||void 0,theme:e.getAttribute("data-theme")||"light",primaryColor:e.getAttribute("data-primary-color")||"#2563eb",botName:e.getAttribute("data-bot-name")||"AI Assistant",botEmoji:e.getAttribute("data-bot-emoji")||void 0,welcomeMessage:e.getAttribute("data-welcome-message")||void 0,placeholder:e.getAttribute("data-placeholder")||void 0,backendUrl:e.getAttribute("data-backend-url")||void 0,streaming:s===null?!0:s!=="false"});Promise.resolve(i.mount())})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",h):h()}c.CognitionDeskWidget=g,c.default=g,c.init=x,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cognitiondesk/widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Embed a CognitionDesk AI chat widget into any website. Authenticate with an API key from your CognitionDesk dashboard.",
|
|
5
5
|
"keywords": ["chat", "widget", "ai", "cognitiondesk", "chatbot", "react", "streaming", "mounaji"],
|
|
6
6
|
"license": "MIT",
|
package/src/react.jsx
CHANGED
|
@@ -42,6 +42,11 @@ export const CognitionDeskWidget = forwardRef(function CognitionDeskWidget(props
|
|
|
42
42
|
defaultOpen = false,
|
|
43
43
|
streaming = true,
|
|
44
44
|
backendUrl,
|
|
45
|
+
// Rate limiting β 0 means unlimited
|
|
46
|
+
maxMessagesPerSession = 0,
|
|
47
|
+
maxMessagesPerMinute = 0,
|
|
48
|
+
limitReachedMessage,
|
|
49
|
+
rateLimitMessage,
|
|
45
50
|
onOpen,
|
|
46
51
|
onClose,
|
|
47
52
|
} = props;
|
|
@@ -75,6 +80,13 @@ export const CognitionDeskWidget = forwardRef(function CognitionDeskWidget(props
|
|
|
75
80
|
placeholder,
|
|
76
81
|
streaming,
|
|
77
82
|
...(backendUrl ? { backendUrl } : {}),
|
|
83
|
+
rateLimiting: {
|
|
84
|
+
enabled: true,
|
|
85
|
+
maxMessagesPerSession,
|
|
86
|
+
maxMessagesPerMinute,
|
|
87
|
+
...(limitReachedMessage ? { limitReachedMessage } : {}),
|
|
88
|
+
...(rateLimitMessage ? { rateLimitMessage } : {}),
|
|
89
|
+
},
|
|
78
90
|
});
|
|
79
91
|
|
|
80
92
|
// Patch open/close to fire callbacks
|
package/src/widget.js
CHANGED
|
@@ -287,9 +287,20 @@ export default class CognitionDeskWidget {
|
|
|
287
287
|
placeholder: config.placeholder || 'Type a messageβ¦',
|
|
288
288
|
position: config.position || 'bottom-right',
|
|
289
289
|
streaming: config.streaming !== false, // default: true
|
|
290
|
+
// Rate limiting β can be overridden by inline config or merged from server config
|
|
291
|
+
rateLimiting: {
|
|
292
|
+
enabled: config.rateLimiting?.enabled !== false,
|
|
293
|
+
maxMessagesPerSession: config.rateLimiting?.maxMessagesPerSession ?? 0,
|
|
294
|
+
maxMessagesPerMinute: config.rateLimiting?.maxMessagesPerMinute ?? 0,
|
|
295
|
+
limitReachedMessage: config.rateLimiting?.limitReachedMessage || "You've reached the message limit for this session.",
|
|
296
|
+
rateLimitMessage: config.rateLimiting?.rateLimitMessage || "You're sending messages too quickly. Please wait a moment.",
|
|
297
|
+
},
|
|
290
298
|
};
|
|
291
299
|
|
|
292
|
-
this._sessionId
|
|
300
|
+
this._sessionId = generateSessionId();
|
|
301
|
+
this._messageCount = 0; // total sent this session
|
|
302
|
+
this._minuteCount = 0; // sent in current minute window
|
|
303
|
+
this._minuteStart = Date.now(); // start of current minute window
|
|
293
304
|
this._messages = []; // { role: 'user'|'assistant', content: string }[]
|
|
294
305
|
this._open = false;
|
|
295
306
|
this._loading = false;
|
|
@@ -360,6 +371,10 @@ export default class CognitionDeskWidget {
|
|
|
360
371
|
if (config.placeholder && !this._userSet('placeholder')) this._cfg.placeholder = config.placeholder;
|
|
361
372
|
if (config.assistantId && !this._cfg.assistantId) this._cfg.assistantId = config.assistantId;
|
|
362
373
|
if (config.avatar?.value && !this._userSet('botEmoji')) this._cfg.botEmoji = config.avatar.value;
|
|
374
|
+
// Merge rate limiting from server config (server is authoritative)
|
|
375
|
+
if (config.rateLimiting) {
|
|
376
|
+
this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...config.rateLimiting };
|
|
377
|
+
}
|
|
363
378
|
} catch {
|
|
364
379
|
// Silently ignore β widget falls back to inline config
|
|
365
380
|
}
|
|
@@ -560,6 +575,27 @@ export default class CognitionDeskWidget {
|
|
|
560
575
|
const text = this._textarea.value.trim();
|
|
561
576
|
if (!text || this._loading) return;
|
|
562
577
|
|
|
578
|
+
// ββ Client-side rate limit check (mirrors server enforcement) ββ
|
|
579
|
+
const rl = this._cfg.rateLimiting;
|
|
580
|
+
if (rl?.enabled) {
|
|
581
|
+
const now = Date.now();
|
|
582
|
+
// Reset minute window
|
|
583
|
+
if (now - this._minuteStart >= 60_000) {
|
|
584
|
+
this._minuteCount = 0;
|
|
585
|
+
this._minuteStart = now;
|
|
586
|
+
}
|
|
587
|
+
if (rl.maxMessagesPerSession > 0 && this._messageCount >= rl.maxMessagesPerSession) {
|
|
588
|
+
this._appendMessage('error', rl.limitReachedMessage, false);
|
|
589
|
+
this._textarea.disabled = true;
|
|
590
|
+
this._sendBtn.disabled = true;
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (rl.maxMessagesPerMinute > 0 && this._minuteCount >= rl.maxMessagesPerMinute) {
|
|
594
|
+
this._appendMessage('error', rl.rateLimitMessage, false);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
563
599
|
this._textarea.value = '';
|
|
564
600
|
this._textarea.style.height = 'auto';
|
|
565
601
|
this._loading = true;
|
|
@@ -567,6 +603,8 @@ export default class CognitionDeskWidget {
|
|
|
567
603
|
|
|
568
604
|
this._messages.push({ role: 'user', content: text });
|
|
569
605
|
this._appendMessage('user', text, false);
|
|
606
|
+
this._messageCount += 1;
|
|
607
|
+
this._minuteCount += 1;
|
|
570
608
|
|
|
571
609
|
try {
|
|
572
610
|
if (this._cfg.streaming) {
|
|
@@ -579,11 +617,19 @@ export default class CognitionDeskWidget {
|
|
|
579
617
|
this._appendMessage('assistant', response, true);
|
|
580
618
|
}
|
|
581
619
|
} catch (err) {
|
|
582
|
-
|
|
620
|
+
// Handle server-side 429 rate limit response
|
|
621
|
+
if (err.status === 429 || err.message?.includes('429')) {
|
|
622
|
+
const msg = err.rateLimitMessage || rl?.rateLimitMessage || "You're sending messages too quickly. Please wait a moment.";
|
|
623
|
+
this._appendMessage('error', msg, false);
|
|
624
|
+
} else {
|
|
625
|
+
this._appendMessage('error', 'Sorry, something went wrong. Please try again.', false);
|
|
626
|
+
}
|
|
583
627
|
console.error('[CognitionDesk]', err);
|
|
584
628
|
} finally {
|
|
585
629
|
this._loading = false;
|
|
586
|
-
this.
|
|
630
|
+
if (!(rl?.maxMessagesPerSession > 0 && this._messageCount >= rl.maxMessagesPerSession)) {
|
|
631
|
+
this._sendBtn.disabled = false;
|
|
632
|
+
}
|
|
587
633
|
this._textarea.focus();
|
|
588
634
|
}
|
|
589
635
|
}
|
|
@@ -613,7 +659,10 @@ export default class CognitionDeskWidget {
|
|
|
613
659
|
|
|
614
660
|
if (!res.ok) {
|
|
615
661
|
const errData = await res.json().catch(() => ({}));
|
|
616
|
-
|
|
662
|
+
const err = new Error(errData.message || `HTTP ${res.status}`);
|
|
663
|
+
err.status = res.status;
|
|
664
|
+
err.rateLimitMessage = errData.message;
|
|
665
|
+
throw err;
|
|
617
666
|
}
|
|
618
667
|
|
|
619
668
|
// Create an empty assistant bubble to stream into
|
|
@@ -673,7 +722,10 @@ export default class CognitionDeskWidget {
|
|
|
673
722
|
|
|
674
723
|
if (!res.ok) {
|
|
675
724
|
const errData = await res.json().catch(() => ({}));
|
|
676
|
-
|
|
725
|
+
const err = new Error(errData.message || `HTTP ${res.status}`);
|
|
726
|
+
err.status = res.status;
|
|
727
|
+
err.rateLimitMessage = errData.message;
|
|
728
|
+
throw err;
|
|
677
729
|
}
|
|
678
730
|
|
|
679
731
|
const data = await res.json();
|