@locdo.tech/botiq-chat-sdk 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sdk/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as e } from "./npm-D7UnWA_n.js";
1
+ import { t as e } from "./npm-DfSKhYCq.js";
2
2
  export { e as init };
@@ -65,8 +65,10 @@ async function l(e, n) {
65
65
  try {
66
66
  let r = await fetch(`${n}/widget/meta`, {
67
67
  headers: { "X-Api-Key": e },
68
+ referrerPolicy: "no-referrer-when-downgrade",
68
69
  signal: AbortSignal.timeout(5e3)
69
70
  });
71
+ if (r.status === 401 || r.status === 403) return null;
70
72
  if (!r.ok) return t;
71
73
  let i = await r.json();
72
74
  if (!i?.design?.colors || !i?.design?.layout || !i?.design?.content || !c(i.design)) return t;
@@ -121,6 +123,7 @@ async function _(e, t, n, r, i, a) {
121
123
  "Content-Type": "application/json",
122
124
  "X-Api-Key": t
123
125
  },
126
+ referrerPolicy: "no-referrer-when-downgrade",
124
127
  body: JSON.stringify({
125
128
  sessionId: n,
126
129
  message: r,
@@ -354,21 +357,60 @@ function D(e) {
354
357
 
355
358
  .message { display: flex; flex-direction: column; max-width: 82%; }
356
359
  .message.user { align-self: flex-end; align-items: flex-end; }
357
- .message.assistant { align-self: flex-start; align-items: flex-start; }
360
+ .message.assistant { align-self: flex-start; align-items: flex-start; max-width: 92%; }
358
361
 
359
362
  .message-bubble {
360
363
  padding: 10px 14px;
361
364
  border-radius: var(--radius-msg);
362
365
  font-size: 14px;
363
366
  line-height: 1.5;
364
- white-space: pre-wrap;
367
+ white-space: normal;
365
368
  word-break: break-word;
369
+ overflow-x: auto;
370
+ }
371
+
372
+ /* ── Markdown inline elements ───────────────────────── */
373
+ .message-bubble strong { font-weight: 600; }
374
+ .message-bubble em { font-style: italic; }
375
+ .message-bubble a { color: var(--color-primary); text-decoration: underline; word-break: break-all; }
376
+ .message-bubble code {
377
+ font-family: monospace;
378
+ font-size: 12px;
379
+ padding: 1px 5px;
380
+ border-radius: 4px;
381
+ background: rgba(255,255,255,.1);
382
+ white-space: pre-wrap;
383
+ }
384
+
385
+ /* ── Markdown tables ─────────────────────────────────── */
386
+ .message-bubble table {
387
+ border-collapse: collapse;
388
+ min-width: 100%;
389
+ font-size: 12.5px;
390
+ display: block;
391
+ overflow-x: auto;
392
+ margin: 4px 0;
393
+ }
394
+ .message-bubble th,
395
+ .message-bubble td {
396
+ border: 1px solid rgba(255,255,255,.15);
397
+ padding: 5px 10px;
398
+ text-align: left;
366
399
  }
400
+ .message-bubble th { background: rgba(255,255,255,.08); font-weight: 600; white-space: nowrap; }
401
+ .message-bubble td { white-space: normal; }
402
+ .message-bubble tr:nth-child(even) td { background: rgba(255,255,255,.04); }
403
+
404
+ /* ── Markdown lists ──────────────────────────────────── */
405
+ .message-bubble ul,
406
+ .message-bubble ol { padding-left: 18px; margin: 2px 0; }
407
+ .message-bubble li { margin: 2px 0; }
367
408
 
368
409
  .message.user .message-bubble {
369
410
  background: ${h};
370
411
  color: #fff;
371
412
  border-bottom-right-radius: 4px;
413
+ white-space: pre-wrap;
372
414
  }
373
415
 
374
416
  .message.assistant .message-bubble {
@@ -538,13 +580,51 @@ function F(e) {
538
580
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
539
581
  }
540
582
  function I(e) {
541
- let t = F(e).replace(/\x00/g, ""), n = [];
542
- return t = t.replace(/`([^`]+)`/g, (e, t) => (n.push(`<code>${t}</code>`), `\x00CODE${n.length - 1}\x00`)), t = t.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>"), t = t.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), t = t.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>"), t = t.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>"), t = t.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1"), t = t.replace(/\n/g, "<br>"), t = t.replace(/\x00CODE(\d+)\x00/g, (e, t) => n[Number(t)]), t;
583
+ let t = [];
584
+ return e = e.replace(/`([^`]+)`/g, (e, n) => (t.push(`<code>${n}</code>`), `\x00CODE${t.length - 1}\x00`)), e = e.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>"), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>"), e = e.replace(/\[([^\]]+)\]\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, (e, t, n) => /^https?:\/\//.test(n) ? `<a href="${n}" target="_blank" rel="noopener noreferrer">${t}</a>` : t), e = e.replace(/\x00CODE(\d+)\x00/g, (e, n) => t[Number(n)]), e;
585
+ }
586
+ function L(e) {
587
+ return /^\|[\s\-:|]+\|$/.test(e);
588
+ }
589
+ function R(e) {
590
+ return e.replace(/^\|/, "").replace(/\|$/, "").split("|").map((e) => e.trim());
591
+ }
592
+ function z(e) {
593
+ let t = F(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
594
+ for (; r < t.length;) {
595
+ let e = t[r].trim();
596
+ if (e.startsWith("|") && e.endsWith("|")) {
597
+ let e = [];
598
+ for (; r < t.length && t[r].trim().startsWith("|") && t[r].trim().endsWith("|");) e.push(t[r].trim()), r++;
599
+ let i = e.findIndex(L), a = i > 0 ? e.slice(0, i) : [], o = i >= 0 ? e.slice(i + 1) : e, s = "<table>";
600
+ a.length > 0 && (s += "<thead>" + a.map((e) => "<tr>" + R(e).map((e) => `<th>${I(e)}</th>`).join("") + "</tr>").join("") + "</thead>"), o.length > 0 && (s += "<tbody>" + o.map((e) => "<tr>" + R(e).map((e) => `<td>${I(e)}</td>`).join("") + "</tr>").join("") + "</tbody>"), s += "</table>", n.push(s);
601
+ continue;
602
+ }
603
+ if (/^[-*]\s/.test(e)) {
604
+ let e = [];
605
+ for (; r < t.length && /^\s*[-*]\s/.test(t[r]);) e.push(t[r].trim().replace(/^[-*]\s+/, "")), r++;
606
+ n.push("<ul>" + e.map((e) => `<li>${I(e)}</li>`).join("") + "</ul>");
607
+ continue;
608
+ }
609
+ if (/^\d+\.\s/.test(e)) {
610
+ let e = [];
611
+ for (; r < t.length && /^\s*\d+\.\s/.test(t[r]);) e.push(t[r].trim().replace(/^\d+\.\s+/, "")), r++;
612
+ n.push("<ol>" + e.map((e) => `<li>${I(e)}</li>`).join("") + "</ol>");
613
+ continue;
614
+ }
615
+ if (e === "") {
616
+ n.push("<br>"), r++;
617
+ continue;
618
+ }
619
+ n.push(I(e)), n.push("<br>"), r++;
620
+ }
621
+ for (; n.length > 0 && n[n.length - 1] === "<br>";) n.pop();
622
+ return n.join("");
543
623
  }
544
- function L(e, t, n, r, i) {
624
+ function B(e, t, n, r, i) {
545
625
  let { name: a, design: o } = t, s, c, l, u, d, f, { content: p } = o;
546
626
  function m(e) {
547
- e.setAttribute("data-position", o.layout.position), s = e.attachShadow({ mode: "open" });
627
+ e.setAttribute("data-position", o.layout.position), s = e.attachShadow({ mode: "closed" });
548
628
  let t = document.createElement("style");
549
629
  t.textContent = D(o), s.appendChild(t), c = document.createElement("button"), c.className = "bubble", c.setAttribute("aria-label", n.ariaOpenChat), c.innerHTML = `
550
630
  <span class="icon-chat">${k}</span>
@@ -558,7 +638,7 @@ function L(e, t, n, r, i) {
558
638
  </div>
559
639
  <button class="close-btn" aria-label="${F(n.ariaCloseChat)}">×</button>
560
640
  </div>
561
- <div class="messages" id="messages-container"></div>
641
+ <div class="messages" id="messages-container" role="log" aria-live="polite" aria-atomic="false"></div>
562
642
  <div class="chat-footer">
563
643
  <div class="input-row">
564
644
  <textarea
@@ -602,7 +682,7 @@ function L(e, t, n, r, i) {
602
682
  }
603
683
  let t = e.messages.map((e) => `
604
684
  <div class="message ${e.role}">
605
- <div class="message-bubble">${e.role === "assistant" ? I(e.content) : F(e.content)}</div>
685
+ <div class="message-bubble">${e.role === "assistant" ? z(e.content) : F(e.content)}</div>
606
686
  </div>
607
687
  `).join(""), i = e.isLoading ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "";
608
688
  u.innerHTML = t + i, u.scrollTop = u.scrollHeight;
@@ -617,7 +697,7 @@ function L(e, t, n, r, i) {
617
697
  }
618
698
  //#endregion
619
699
  //#region src/i18n/vi.ts
620
- var R = {
700
+ var V = {
621
701
  statusOnline: "Trực tuyến",
622
702
  inputPlaceholder: "Nhắn tin...",
623
703
  defaultGreeting: "Xin chào! Tôi có thể giúp gì cho bạn?",
@@ -633,7 +713,7 @@ var R = {
633
713
  ariaOpenChat: "Mở khung chat",
634
714
  ariaCloseChat: "Đóng khung chat",
635
715
  ariaSendMessage: "Gửi tin nhắn"
636
- }, z = {
716
+ }, H = {
637
717
  statusOnline: "Online",
638
718
  inputPlaceholder: "Type a message...",
639
719
  defaultGreeting: "Hello! How can I help you?",
@@ -652,22 +732,26 @@ var R = {
652
732
  };
653
733
  //#endregion
654
734
  //#region src/i18n/index.ts
655
- function B(e) {
656
- return e === "en" ? z : R;
735
+ function U(e) {
736
+ return e === "en" ? H : V;
657
737
  }
658
738
  //#endregion
659
739
  //#region src/builds/npm/index.ts
660
- function V(e) {
740
+ function W(e) {
661
741
  if (!e.apiKey) return console.warn("[BotIQ] apiKey is required"), () => void 0;
662
742
  let t = u(e), n = m();
663
743
  x({ messages: h(n) });
664
744
  let r = document.createElement("div");
665
745
  document.body.appendChild(r);
666
- let i = () => void 0, a = !1, o = B(void 0);
746
+ let i = () => void 0, a = !1, o = U(void 0);
667
747
  l(t.apiKey, t.apiUrl).then((e) => {
668
748
  if (a) return;
669
- o = B(e.widgetLanguage);
670
- let n = L(t, e, o, s, c);
749
+ if (e === null) {
750
+ console.warn("[BotIQ] Widget not authorised on this origin — skipped mount"), r.remove();
751
+ return;
752
+ }
753
+ o = U(e.widgetLanguage);
754
+ let n = B(t, e, o, s, c);
671
755
  n.mount(r), i = S((e) => n.update(e)), n.update(b());
672
756
  });
673
757
  function s(e) {
@@ -701,4 +785,4 @@ function V(e) {
701
785
  };
702
786
  }
703
787
  //#endregion
704
- export { V as t };
788
+ export { W as t };
package/dist/sdk/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./npm-D7UnWA_n.js";
1
+ import { t as e } from "./npm-DfSKhYCq.js";
2
2
  import { useEffect as t } from "react";
3
3
  //#region src/builds/npm/react.tsx
4
4
  function n(n) {
package/dist/sdk/vue.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./npm-D7UnWA_n.js";
1
+ import { t as e } from "./npm-DfSKhYCq.js";
2
2
  import { defineComponent as t, onMounted as n, onUnmounted as r } from "vue";
3
3
  //#region src/builds/npm/vue.ts
4
4
  var i = t({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locdo.tech/botiq-chat-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "BotIQ chat widget SDK — embed AI chatbot into any website with vanilla JS, React, or Vue.",
5
5
  "keywords": [
6
6
  "botiq",
@@ -69,15 +69,32 @@
69
69
  "preview": "vite preview",
70
70
  "test": "vitest run",
71
71
  "test:watch": "vitest",
72
- "prepublishOnly": "npm run build:sdk"
72
+ "test:bundle": "npm run build:cdn && vitest run test/bundle",
73
+ "test:browser": "playwright test",
74
+ "test:browser:ui": "playwright test --ui",
75
+ "test:browser:chromium": "playwright test --project=chromium",
76
+ "pretest:browser": "npm run build:cdn && cpx \"test/browser/fixtures/**\" dist/test-fixtures",
77
+ "pretest:browser:chromium": "npm run pretest:browser",
78
+ "test:wordpress": "playwright test --config=playwright.wordpress.config.ts",
79
+ "pretest:wordpress": "npm run build:cdn",
80
+ "test:wordpress:up": "docker compose -f test/wordpress/docker-compose.wp.yml up -d",
81
+ "test:wordpress:down": "docker compose -f test/wordpress/docker-compose.wp.yml down -v",
82
+ "test:wordpress:logs": "docker compose -f test/wordpress/docker-compose.wp.yml logs --tail=100",
83
+ "prepublishOnly": "npm run build:sdk",
84
+ "rollback:cdn": "bash scripts/rollback/cdn-rollback.sh"
73
85
  },
74
86
  "devDependencies": {
87
+ "@playwright/test": "^1.60.0",
88
+ "@types/node": "^25.9.1",
75
89
  "@types/react": "^19.2.14",
76
90
  "@types/react-dom": "^19.2.3",
77
91
  "@vitest/coverage-v8": "^4.1.6",
92
+ "cpx2": "^9.0.0",
93
+ "gzip-size": "^7.0.0",
78
94
  "jsdom": "^29.1.1",
79
95
  "react": "^19.2.6",
80
96
  "react-dom": "^19.2.6",
97
+ "serve": "^14.2.6",
81
98
  "typescript": "~6.0.2",
82
99
  "vite": "^8.0.12",
83
100
  "vite-plugin-dts": "^5.0.0",