@locdo.tech/botiq-chat-sdk 0.6.0 → 0.7.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.
@@ -90,23 +90,24 @@ async function f(e, r) {
90
90
  } catch {
91
91
  return n;
92
92
  }
93
- let o = typeof a.name == "string" && a.name.length > 0 ? a.name : n.name, s = a.widgetLanguage === "en" ? "en" : "vi", c = Array.isArray(a.pageActions) ? a.pageActions : void 0, u = typeof a.contactInfo == "string" ? a.contactInfo : void 0, f = a.proactive && typeof a.proactive == "object" ? a.proactive : void 0, p = a.status === "offline" ? "offline" : a.status === "online" ? "online" : void 0;
93
+ let o = typeof a.name == "string" && a.name.length > 0 ? a.name : n.name, s = a.widgetLanguage === "en" ? "en" : "vi", c = Array.isArray(a.pageActions) ? a.pageActions : void 0, u = typeof a.contactInfo == "string" ? a.contactInfo : void 0, f = a.proactive && typeof a.proactive == "object" ? a.proactive : void 0, p = a.status === "offline" ? "offline" : a.status === "online" ? "online" : void 0, m = a.imageInput === !0 ? !0 : void 0, h = a.richMessages === !0 ? !0 : void 0, g = {
94
+ ...c ? { pageActions: c } : {},
95
+ ...u ? { contactInfo: u } : {},
96
+ ...f ? { proactive: f } : {},
97
+ ...p ? { status: p } : {},
98
+ ...m ? { imageInput: m } : {},
99
+ ...h ? { richMessages: h } : {}
100
+ };
94
101
  return !a.design?.colors || !a.design?.layout || !a.design?.content || !l(a.design) ? {
95
102
  name: o,
96
103
  design: t,
97
104
  widgetLanguage: s,
98
- ...c ? { pageActions: c } : {},
99
- ...u ? { contactInfo: u } : {},
100
- ...f ? { proactive: f } : {},
101
- ...p ? { status: p } : {}
105
+ ...g
102
106
  } : {
103
107
  name: o,
104
108
  design: a.design,
105
109
  widgetLanguage: s,
106
- ...c ? { pageActions: c } : {},
107
- ...u ? { contactInfo: u } : {},
108
- ...f ? { proactive: f } : {},
109
- ...p ? { status: p } : {}
110
+ ...g
110
111
  };
111
112
  }
112
113
  function p(t) {
@@ -223,7 +224,7 @@ var T = /^[a-z][a-z0-9_]{2,40}$/, E = new Set([
223
224
  }();
224
225
  //#endregion
225
226
  //#region src/core/api.ts
226
- async function ee(e, t, n) {
227
+ async function O(e, t, n) {
227
228
  try {
228
229
  let r = new URLSearchParams({ visitorId: n }), i = await fetch(`${e}/widget/conversations?${r.toString()}`, {
229
230
  method: "GET",
@@ -240,9 +241,51 @@ async function ee(e, t, n) {
240
241
  return [];
241
242
  }
242
243
  }
243
- async function te(e, t, n, r, i, a, o) {
244
+ async function k(e, t, n, r, i) {
245
+ let a = i?.imageUploadError ?? "Image upload failed. Please try again.";
246
+ try {
247
+ let o = new FormData(), s = n.type === "image/webp" ? ".webp" : ".jpg";
248
+ o.append("file", n, `image${s}`);
249
+ let c = await fetch(`${e}/widget/upload`, {
250
+ method: "POST",
251
+ headers: {
252
+ "X-Api-Key": t,
253
+ ...r ? { "X-Visitor-Id": r } : {}
254
+ },
255
+ referrerPolicy: "no-referrer-when-downgrade",
256
+ body: o
257
+ });
258
+ if (!c.ok) return c.status === 403 ? {
259
+ ok: !1,
260
+ error: i?.imageUploadPlanError ?? a
261
+ } : c.status === 413 ? {
262
+ ok: !1,
263
+ error: i?.imageUploadTooLarge ?? a
264
+ } : c.status === 429 ? {
265
+ ok: !1,
266
+ error: i?.imageUploadRateLimit ?? a
267
+ } : {
268
+ ok: !1,
269
+ error: a
270
+ };
271
+ let l = await c.json();
272
+ return l.url ? {
273
+ ok: !0,
274
+ url: l.url
275
+ } : {
276
+ ok: !1,
277
+ error: a
278
+ };
279
+ } catch {
280
+ return {
281
+ ok: !1,
282
+ error: a
283
+ };
284
+ }
285
+ }
286
+ async function ee(e, t, n, r, i, a, o, s) {
244
287
  try {
245
- let s = D.getDefinitions(), c = await fetch(`${e}/widget/chat`, {
288
+ let c = D.getDefinitions(), l = await fetch(`${e}/widget/chat`, {
246
289
  method: "POST",
247
290
  headers: {
248
291
  "Content-Type": "application/json",
@@ -254,26 +297,28 @@ async function te(e, t, n, r, i, a, o) {
254
297
  sessionId: n,
255
298
  message: r,
256
299
  history: i,
257
- ...s.length > 0 ? { availableActions: s } : {}
300
+ ...c.length > 0 ? { availableActions: c } : {},
301
+ ...s?.length ? { attachments: s } : {}
258
302
  })
259
303
  });
260
- if (!c.ok) return c.status === 401 ? {
304
+ if (!l.ok) return l.status === 401 ? {
261
305
  reply: a.errorAuth,
262
306
  error: !0
263
- } : c.status === 403 ? {
307
+ } : l.status === 403 ? {
264
308
  reply: a.errorForbidden,
265
309
  error: !0
266
- } : c.status === 429 ? {
310
+ } : l.status === 429 ? {
267
311
  reply: a.errorQuota,
268
312
  error: !0
269
313
  } : {
270
314
  reply: a.errorGeneric,
271
315
  error: !0
272
316
  };
273
- let l = await c.json();
317
+ let u = await l.json(), d = Array.isArray(u.blocks) && u.blocks.length > 0 ? u.blocks : void 0;
274
318
  return {
275
- reply: l.reply || a.errorMessage,
276
- pageAction: l.pageAction
319
+ reply: u.reply || a.errorMessage,
320
+ pageAction: u.pageAction,
321
+ blocks: d
277
322
  };
278
323
  } catch {
279
324
  return {
@@ -282,16 +327,16 @@ async function te(e, t, n, r, i, a, o) {
282
327
  };
283
328
  }
284
329
  }
285
- var O = {
330
+ var A = {
286
331
  messages: [],
287
332
  hasMore: !1
288
333
  };
289
- function k(e) {
334
+ function te(e) {
290
335
  if (!e || typeof e != "object") return !1;
291
336
  let t = e;
292
337
  return typeof t.id == "string" && (t.role === "user" || t.role === "assistant") && typeof t.content == "string";
293
338
  }
294
- async function A(e, t, n, r, i, a) {
339
+ async function j(e, t, n, r, i, a) {
295
340
  try {
296
341
  let o = new URLSearchParams({
297
342
  sessionId: n,
@@ -306,26 +351,26 @@ async function A(e, t, n, r, i, a) {
306
351
  },
307
352
  referrerPolicy: "no-referrer-when-downgrade"
308
353
  });
309
- if (!s.ok) return O;
354
+ if (!s.ok) return A;
310
355
  let c = await s.json();
311
356
  return {
312
- messages: Array.isArray(c.messages) ? c.messages.filter(k) : [],
357
+ messages: Array.isArray(c.messages) ? c.messages.filter(te) : [],
313
358
  hasMore: c.hasMore === !0,
314
359
  escalated: c.escalated === !0
315
360
  };
316
361
  } catch {
317
- return O;
362
+ return A;
318
363
  }
319
364
  }
320
- function j(e, t, n, r, i) {
321
- return A(e, t, n, void 0, r, i);
365
+ function M(e, t, n, r, i) {
366
+ return j(e, t, n, void 0, r, i);
322
367
  }
323
368
  function ne(e, t, n, r, i, a) {
324
- return A(e, t, n, r, i, a);
369
+ return j(e, t, n, r, i, a);
325
370
  }
326
371
  //#endregion
327
372
  //#region src/core/state.ts
328
- var M = {
373
+ var N = {
329
374
  messages: [],
330
375
  isLoading: !1,
331
376
  isOpen: !1,
@@ -334,26 +379,107 @@ var M = {
334
379
  unreadCount: 0,
335
380
  conversations: [],
336
381
  currentSessionId: "",
337
- escalated: !1
338
- }, N = /* @__PURE__ */ new Set();
339
- function P() {
340
- return M;
341
- }
342
- function F(e) {
343
- Object.assign(M, e);
382
+ escalated: !1,
383
+ pendingAttachment: null,
384
+ isUploading: !1,
385
+ uploadError: null
386
+ }, P = /* @__PURE__ */ new Set();
387
+ function F() {
388
+ return N;
389
+ }
390
+ function I(e) {
391
+ Object.assign(N, e);
344
392
  let t = {
345
- ...M,
346
- messages: [...M.messages],
347
- conversations: [...M.conversations]
393
+ ...N,
394
+ messages: [...N.messages],
395
+ conversations: [...N.conversations]
348
396
  };
349
- N.forEach((e) => e(t));
397
+ P.forEach((e) => e(t));
350
398
  }
351
399
  function re(e) {
352
- return N.add(e), () => N.delete(e);
400
+ return P.add(e), () => P.delete(e);
401
+ }
402
+ //#endregion
403
+ //#region src/core/upload.ts
404
+ var L = new Set([
405
+ "image/jpeg",
406
+ "image/png",
407
+ "image/webp"
408
+ ]), R = 25 * 1024 * 1024, z = 5 * 1024 * 1024, B = 1600, ie = .85, ae = .65;
409
+ function oe(e) {
410
+ return L.has(e);
411
+ }
412
+ function se(e) {
413
+ return oe(e.type) ? e.size > R ? {
414
+ ok: !1,
415
+ error: "Image too large (max 25 MB before compression)."
416
+ } : { ok: !0 } : {
417
+ ok: !1,
418
+ error: "Unsupported image type. Please use JPEG, PNG, or WebP."
419
+ };
420
+ }
421
+ function ce(e, t) {
422
+ let n = Math.max(e, t);
423
+ if (n <= B) return {
424
+ width: e,
425
+ height: t
426
+ };
427
+ let r = B / n;
428
+ return {
429
+ width: Math.floor(e * r),
430
+ height: Math.floor(t * r)
431
+ };
432
+ }
433
+ function le(e) {
434
+ let t = se(e);
435
+ return t.ok ? new Promise((t, n) => {
436
+ let r = new Image(), i = URL.createObjectURL(e);
437
+ r.onload = () => {
438
+ URL.revokeObjectURL(i);
439
+ let { width: a, height: o } = ce(r.naturalWidth, r.naturalHeight), s = document.createElement("canvas");
440
+ s.width = a, s.height = o;
441
+ let c = s.getContext("2d");
442
+ if (!c) {
443
+ n(/* @__PURE__ */ Error("Canvas 2D context not available"));
444
+ return;
445
+ }
446
+ c.drawImage(r, 0, 0, a, o);
447
+ let l = e.type === "image/webp" ? "image/webp" : "image/jpeg";
448
+ s.toBlob((e) => {
449
+ if (!e) {
450
+ n(/* @__PURE__ */ Error("Canvas toBlob returned null"));
451
+ return;
452
+ }
453
+ if (e.size <= z) {
454
+ t({
455
+ blob: e,
456
+ mimeType: l
457
+ });
458
+ return;
459
+ }
460
+ s.toBlob((e) => {
461
+ if (!e) {
462
+ n(/* @__PURE__ */ Error("Canvas toBlob (fallback quality) returned null"));
463
+ return;
464
+ }
465
+ if (e.size > z) {
466
+ n(/* @__PURE__ */ Error("Image too large after compression (> 5 MB). Try a smaller image."));
467
+ return;
468
+ }
469
+ t({
470
+ blob: e,
471
+ mimeType: l
472
+ });
473
+ }, l, ae);
474
+ }, l, ie);
475
+ }, r.onerror = () => {
476
+ URL.revokeObjectURL(i), n(/* @__PURE__ */ Error("Failed to load image"));
477
+ }, r.src = i;
478
+ }) : Promise.reject(Error(t.error));
353
479
  }
354
480
  //#endregion
355
481
  //#region src/core/styles.ts
356
- var I = {
482
+ var V = {
357
483
  inter: {
358
484
  url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
359
485
  family: "'Inter', system-ui, -apple-system, sans-serif"
@@ -375,21 +501,21 @@ var I = {
375
501
  family: "'Playfair Display', Georgia, serif"
376
502
  }
377
503
  };
378
- function L(e, t) {
504
+ function H(e, t) {
379
505
  if (!t) return e;
380
506
  let n = t.angle ?? 135;
381
507
  return t.type === "linear" ? `linear-gradient(${n}deg, ${t.from}, ${t.to})` : `radial-gradient(circle, ${t.from}, ${t.to})`;
382
508
  }
383
- function R(e) {
509
+ function ue(e) {
384
510
  return e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\\[0-9a-fA-F]{1,6}\s?/g, "x").replace(/@import\b[^;]*;?/gi, "").replace(/url\s*\(\s*["']?\s*(?!data:)[^)]*["']?\s*\)/gi, "url(\"\")").replace(/expression\s*\(/gi, "expression_(").replace(/javascript\s*:/gi, "blocked:").replace(/-moz-binding\s*:/gi, "").replace(/\bbehavior\s*:/gi, "");
385
511
  }
386
- function ie(e) {
512
+ function de(e) {
387
513
  let t = e?.bubbleOpen ?? "none", n = e?.typingIndicator ?? "dots-bounce";
388
514
  return [t === "fade" ? "@keyframes biq-open-fade { from { opacity: 0; } to { opacity: 1; } }\n.chat-window.open { animation: biq-open-fade .22s ease forwards; }" : t === "slide-up" ? "@keyframes biq-open-slide { from { opacity: 0; transform: scale(1) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } }\n.chat-window.open { animation: biq-open-slide .25s cubic-bezier(.22,1,.36,1) forwards; }" : t === "bounce" ? "@keyframes biq-open-bounce { 0% { opacity: 0; transform: scale(.85) translateY(8px); } 60% { transform: scale(1.03) translateY(-3px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }\n.chat-window.open { animation: biq-open-bounce .35s cubic-bezier(.34,1.56,.64,1) forwards; }" : "", n === "dots-pulse" ? "@keyframes biq-pulse { 0%, 100% { opacity: 0.3; transform: scale(1); } 50% { opacity: 1; transform: scale(1.3); } }\n.typing span { animation: biq-pulse 1.2s infinite ease-in-out; }" : n === "bar" ? ".typing { gap: 3px; align-items: flex-end; }\n.typing span { width: 3px; height: 14px; border-radius: 2px; animation: biq-bar 1s infinite ease-in-out; }\n.typing span:nth-child(1) { animation-delay: 0s; }\n.typing span:nth-child(2) { animation-delay: .15s; height: 20px; }\n.typing span:nth-child(3) { animation-delay: .3s; }\n@keyframes biq-bar { 0%, 100% { transform: scaleY(.4); opacity: .5; } 50% { transform: scaleY(1); opacity: 1; } }" : ""].filter(Boolean).join("\n");
389
515
  }
390
- var ae = "\n.botiq-confirm-overlay { position:absolute; inset:0; background:rgba(0,0,0,.7);\n display:flex; align-items:center; justify-content:center; z-index:999; }\n.botiq-confirm-dialog { background:#1A1A1A; border:1px solid #2D2D2D; border-radius:12px;\n padding:16px; max-width:320px; color:#fff; font-size:14px; }\n.botiq-confirm-header { font-weight:600; margin-bottom:8px; }\n.botiq-confirm-reason { color:#9CA3AF; margin-bottom:8px; }\n.botiq-confirm-params { background:#0a0a0a; padding:8px; border-radius:6px; font-size:12px;\n max-height:120px; overflow:auto; white-space:pre-wrap; }\n.botiq-confirm-actions { display:flex; gap:8px; margin-top:12px; }\n.botiq-confirm-yes { flex:1; padding:8px 12px; border-radius:6px; border:none;\n background:#F97316; color:#fff; cursor:pointer; }\n.botiq-confirm-yes[data-risk=\"high\"] { background:#DC2626; }\n.botiq-confirm-no { flex:1; padding:8px 12px; border-radius:6px; background:transparent;\n border:1px solid #2D2D2D; color:#fff; cursor:pointer; }\n.botiq-confirm-countdown { margin-top:8px; font-size:12px; color:#9CA3AF; }\n";
391
- function oe(e) {
392
- let { colors: t, layout: n, font: r, gradient: i, animation: a, customCSS: o, whiteLabel: s } = e, c = I[r] ?? I.inter, l = n.buttonShape === "square" ? "14px" : "50%", u = n.position === "bottom-left" ? "right: auto; left: 24px;" : "right: 24px;", d = n.position === "bottom-left" ? "bottom left" : "bottom right", f = n.position === "bottom-left" ? "right: auto; left: 0;" : "right: 0;", p = n.fillHeight ? `min(${n.height}px, calc(100dvh - 48px))` : `${n.height}px`, m = L(t.primary, i), h = L(t.header, i), g = L(t.userBubble, i), _ = s ? ".botiq-badge { display: none !important; }" : "";
516
+ var fe = "\n/* ── Rich Blocks ─────────────────────────── */\n.biq-blocks {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-width: 100%;\n}\n\n/* ── Shared button style ─────────────────── */\n.biq-btn {\n display: inline-block;\n padding: 7px 14px;\n border-radius: 18px;\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: filter .15s, opacity .15s;\n text-decoration: none;\n border: 1.5px solid #F97316;\n background: transparent;\n color: #F97316;\n line-height: 1.3;\n white-space: nowrap;\n}\n.biq-btn:hover { filter: brightness(1.15); }\n.biq-btn--send { background: #F97316; color: #fff; }\n.biq-btn--send:hover { filter: brightness(0.9); }\n.biq-btn--url { background: transparent; color: #F97316; }\n.biq-btn--page_action { border-style: dashed; }\n\n/* ── Card ────────────────────────────────── */\n.biq-card {\n background: #1A1A1A;\n border: 1px solid #2D2D2D;\n border-radius: 12px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n min-width: 0;\n text-decoration: none;\n color: inherit;\n transition: border-color .15s;\n}\n.biq-card--link:hover { border-color: #F97316; }\n\n.biq-card-img {\n width: 100%;\n max-height: 160px;\n object-fit: cover;\n display: block;\n}\n\n.biq-card-title {\n font-size: 14px;\n font-weight: 600;\n color: #fff;\n padding: 10px 12px 2px;\n line-height: 1.3;\n}\n\n.biq-card-subtitle {\n font-size: 12px;\n color: #9CA3AF;\n padding: 0 12px 6px;\n line-height: 1.4;\n}\n\n.biq-card-price {\n font-size: 14px;\n font-weight: 600;\n color: #F97316;\n padding: 4px 12px 8px;\n}\n\n.biq-card-btns {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 0 10px 10px;\n}\n\n/* ── Carousel ────────────────────────────── */\n.biq-carousel {\n display: flex;\n flex-direction: row;\n gap: 10px;\n overflow-x: auto;\n padding-bottom: 4px;\n scrollbar-width: thin;\n scrollbar-color: #2D2D2D transparent;\n /* Snap cards into position on scroll */\n scroll-snap-type: x mandatory;\n}\n.biq-carousel::-webkit-scrollbar { height: 4px; }\n.biq-carousel::-webkit-scrollbar-track { background: transparent; }\n.biq-carousel::-webkit-scrollbar-thumb { background: #2D2D2D; border-radius: 2px; }\n\n.biq-carousel .biq-card {\n flex: 0 0 200px;\n scroll-snap-align: start;\n max-width: 200px;\n}\n\n/* ── Quick Replies ───────────────────────── */\n.biq-quick-replies {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 4px;\n}\n\n.biq-qr-pill {\n padding: 5px 14px;\n border-radius: 20px;\n border: 1.5px solid #F97316;\n background: transparent;\n color: #F97316;\n font: inherit;\n font-size: 13px;\n cursor: pointer;\n transition: background .15s, color .15s;\n white-space: nowrap;\n}\n.biq-qr-pill:hover { background: #F97316; color: #fff; }\n\n/* ── Buttons block ───────────────────────── */\n.biq-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 4px;\n}\n", pe = "\n.botiq-confirm-overlay { position:absolute; inset:0; background:rgba(0,0,0,.7);\n display:flex; align-items:center; justify-content:center; z-index:999; }\n.botiq-confirm-dialog { background:#1A1A1A; border:1px solid #2D2D2D; border-radius:12px;\n padding:16px; max-width:320px; color:#fff; font-size:14px; }\n.botiq-confirm-header { font-weight:600; margin-bottom:8px; }\n.botiq-confirm-reason { color:#9CA3AF; margin-bottom:8px; }\n.botiq-confirm-params { background:#0a0a0a; padding:8px; border-radius:6px; font-size:12px;\n max-height:120px; overflow:auto; white-space:pre-wrap; }\n.botiq-confirm-actions { display:flex; gap:8px; margin-top:12px; }\n.botiq-confirm-yes { flex:1; padding:8px 12px; border-radius:6px; border:none;\n background:#F97316; color:#fff; cursor:pointer; }\n.botiq-confirm-yes[data-risk=\"high\"] { background:#DC2626; }\n.botiq-confirm-no { flex:1; padding:8px 12px; border-radius:6px; background:transparent;\n border:1px solid #2D2D2D; color:#fff; cursor:pointer; }\n.botiq-confirm-countdown { margin-top:8px; font-size:12px; color:#9CA3AF; }\n";
517
+ function me(e) {
518
+ let { colors: t, layout: n, font: r, gradient: i, animation: a, customCSS: o, whiteLabel: s } = e, c = V[r] ?? V.inter, l = n.buttonShape === "square" ? "14px" : "50%", u = n.position === "bottom-left" ? "right: auto; left: 24px;" : "right: 24px;", d = n.position === "bottom-left" ? "bottom left" : "bottom right", f = n.position === "bottom-left" ? "right: auto; left: 0;" : "right: 0;", p = n.fillHeight ? `min(${n.height}px, calc(100dvh - 48px))` : `${n.height}px`, m = H(t.primary, i), h = H(t.header, i), g = H(t.userBubble, i), _ = s ? ".botiq-badge { display: none !important; }" : "";
393
519
  return `
394
520
  @import url('${c.url}');
395
521
 
@@ -577,10 +703,11 @@ function oe(e) {
577
703
  .msg-list { flex: 1; overflow-y: auto; }
578
704
 
579
705
  .msg-list-item {
580
- display: grid;
706
+ display: flex;
707
+ align-items: flex-start;
708
+ gap: 10px;
581
709
  width: 100%;
582
710
  text-align: left;
583
- gap: 2px;
584
711
  padding: 12px 16px;
585
712
  background: transparent;
586
713
  border: none;
@@ -591,6 +718,26 @@ function oe(e) {
591
718
 
592
719
  .msg-list-item:hover { background: var(--gray-50); }
593
720
 
721
+ .msg-list-avatar {
722
+ flex-shrink: 0;
723
+ width: 36px;
724
+ height: 36px;
725
+ border-radius: 50%;
726
+ overflow: hidden;
727
+ background: var(--color-primary);
728
+ display: flex;
729
+ align-items: center;
730
+ justify-content: center;
731
+ }
732
+
733
+ .msg-list-body {
734
+ flex: 1;
735
+ min-width: 0;
736
+ display: flex;
737
+ flex-direction: column;
738
+ gap: 2px;
739
+ }
740
+
594
741
  .msg-list-title {
595
742
  color: var(--color-text);
596
743
  font-weight: 600;
@@ -607,7 +754,7 @@ function oe(e) {
607
754
 
608
755
  .msg-list-time { color: var(--gray-400); font-size: 10px; }
609
756
 
610
- .msg-list-badge { color: #F97316; font-size: 10px; }
757
+ .msg-list-badge { color: var(--color-primary); font-size: 10px; }
611
758
 
612
759
  .msg-list-empty {
613
760
  padding: 24px;
@@ -642,22 +789,82 @@ function oe(e) {
642
789
  margin: 8px 18px;
643
790
  padding: 14px;
644
791
  border-radius: 12px;
645
- border: 1px solid var(--gray-200);
646
- background: var(--gray-50);
647
- color: var(--color-text);
792
+ border: 1.5px solid var(--color-primary);
793
+ background: transparent;
794
+ color: var(--color-primary);
648
795
  text-align: left;
649
796
  cursor: pointer;
650
797
  font: inherit;
651
798
  font-size: 14px;
652
- transition: background .15s, border-color .15s;
799
+ font-weight: 500;
800
+ transition: background .15s, color .15s;
653
801
  }
654
802
 
655
- .home-start:hover { background: var(--gray-100); border-color: var(--color-primary); }
803
+ .home-start:hover { background: var(--color-primary); color: #fff; }
656
804
 
657
805
  .home-status { margin: 8px 18px; font-size: 12px; }
658
806
  .home-status--online { color: #16A34A; }
659
807
  .home-status--offline { color: var(--gray-400); }
660
808
 
809
+ /* ── Home recent conversations ──────────────── */
810
+ .home-recent-label {
811
+ margin: 16px 18px 6px;
812
+ font-size: 11px;
813
+ font-weight: 600;
814
+ text-transform: uppercase;
815
+ letter-spacing: .06em;
816
+ color: var(--gray-400);
817
+ }
818
+
819
+ .home-recent-item {
820
+ display: flex;
821
+ align-items: center;
822
+ gap: 10px;
823
+ width: 100%;
824
+ padding: 10px 18px;
825
+ background: transparent;
826
+ border: none;
827
+ border-bottom: 1px solid var(--gray-100);
828
+ cursor: pointer;
829
+ font: inherit;
830
+ text-align: left;
831
+ }
832
+
833
+ .home-recent-item:hover { background: var(--gray-50); }
834
+
835
+ .home-recent-avatar {
836
+ flex-shrink: 0;
837
+ width: 32px;
838
+ height: 32px;
839
+ border-radius: 50%;
840
+ overflow: hidden;
841
+ background: var(--color-primary);
842
+ display: flex;
843
+ align-items: center;
844
+ justify-content: center;
845
+ }
846
+
847
+ .home-recent-body { flex: 1; min-width: 0; }
848
+
849
+ .home-recent-title {
850
+ color: var(--color-text);
851
+ font-size: 13px;
852
+ font-weight: 500;
853
+ white-space: nowrap;
854
+ overflow: hidden;
855
+ text-overflow: ellipsis;
856
+ }
857
+
858
+ .home-recent-preview {
859
+ color: var(--gray-400);
860
+ font-size: 12px;
861
+ white-space: nowrap;
862
+ overflow: hidden;
863
+ text-overflow: ellipsis;
864
+ }
865
+
866
+ .home-recent-time { color: var(--gray-400); font-size: 10px; flex-shrink: 0; }
867
+
661
868
  /* ── Messages ───────────────────────────── */
662
869
  .messages {
663
870
  flex: 1;
@@ -957,6 +1164,72 @@ function oe(e) {
957
1164
  .send-btn:disabled { background: var(--gray-200); cursor: not-allowed; filter: none; }
958
1165
  .send-btn svg { width: 18px; height: 18px; fill: #fff; }
959
1166
 
1167
+ /* ── Attach Button ──────────────────────── */
1168
+ .attach-btn {
1169
+ width: 34px; height: 38px;
1170
+ background: transparent; border: none; cursor: pointer;
1171
+ color: var(--gray-400); flex-shrink: 0;
1172
+ display: flex; align-items: center; justify-content: center;
1173
+ border-radius: 8px; transition: color .15s, background .15s;
1174
+ }
1175
+ .attach-btn:hover { color: var(--color-primary); background: rgba(249,115,22,.08); }
1176
+ .attach-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1177
+
1178
+ /* ── Attach Preview Slot ────────────────── */
1179
+ .attach-preview-slot {
1180
+ margin-bottom: 6px;
1181
+ }
1182
+
1183
+ .attach-preview {
1184
+ display: flex;
1185
+ align-items: center;
1186
+ gap: 8px;
1187
+ padding: 6px 8px;
1188
+ background: var(--gray-50);
1189
+ border: 1px solid var(--gray-200);
1190
+ border-radius: 10px;
1191
+ position: relative;
1192
+ }
1193
+
1194
+ .attach-preview-img {
1195
+ width: 48px;
1196
+ height: 48px;
1197
+ object-fit: cover;
1198
+ border-radius: 6px;
1199
+ flex-shrink: 0;
1200
+ }
1201
+
1202
+ .attach-preview-img--uploading {
1203
+ opacity: 0.5;
1204
+ }
1205
+
1206
+ .attach-preview-status {
1207
+ font-size: 12px;
1208
+ color: var(--gray-400);
1209
+ flex: 1;
1210
+ }
1211
+
1212
+ .attach-preview-error {
1213
+ font-size: 12px;
1214
+ color: #DC2626;
1215
+ flex: 1;
1216
+ }
1217
+
1218
+ .attach-preview-remove {
1219
+ width: 22px; height: 22px;
1220
+ border-radius: 50%;
1221
+ background: var(--gray-200);
1222
+ border: none;
1223
+ cursor: pointer;
1224
+ color: var(--color-text);
1225
+ font-size: 14px;
1226
+ line-height: 1;
1227
+ display: flex; align-items: center; justify-content: center;
1228
+ flex-shrink: 0;
1229
+ transition: background .15s;
1230
+ }
1231
+ .attach-preview-remove:hover { background: #DC2626; color: #fff; }
1232
+
960
1233
  .emoji-btn {
961
1234
  width: 34px; height: 38px;
962
1235
  background: transparent; border: none; cursor: pointer;
@@ -978,9 +1251,10 @@ function oe(e) {
978
1251
  .emoji-item:hover { background: var(--gray-100); }
979
1252
 
980
1253
  ${_}
981
- ${ie(a)}
982
- ${o ? R(o) : ""}
983
- ${ae}
1254
+ ${de(a)}
1255
+ ${o ? ue(o) : ""}
1256
+ ${pe}
1257
+ ${fe}
984
1258
  @media (max-width: 480px) {
985
1259
  :host { bottom: 0; right: 0; left: 0; }
986
1260
  .chat-window {
@@ -997,18 +1271,18 @@ ${ae}
997
1271
  }
998
1272
  //#endregion
999
1273
  //#region src/core/timeago.ts
1000
- function z(e) {
1274
+ function U(e) {
1001
1275
  return String(e).padStart(2, "0");
1002
1276
  }
1003
- function B(e, t, n) {
1277
+ function W(e, t, n) {
1004
1278
  let r = Math.max(0, Math.floor((t - e) / 1e3));
1005
1279
  if (r < 60) return n === "vi" ? "vừa xong" : "just now";
1006
1280
  let i = Math.floor(r / 60);
1007
1281
  if (i < 60) return n === "vi" ? `${i} phút` : `${i} min`;
1008
1282
  let a = new Date(e);
1009
- return `${z(a.getHours())}:${z(a.getMinutes())}`;
1283
+ return `${U(a.getHours())}:${U(a.getMinutes())}`;
1010
1284
  }
1011
- function se(e, t, n) {
1285
+ function he(e, t, n) {
1012
1286
  let r = (e) => {
1013
1287
  let t = new Date(e);
1014
1288
  return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
@@ -1016,85 +1290,103 @@ function se(e, t, n) {
1016
1290
  if (i <= 0) return n === "vi" ? "Hôm nay" : "Today";
1017
1291
  if (i === 1) return n === "vi" ? "Hôm qua" : "Yesterday";
1018
1292
  let a = new Date(e);
1019
- return `${z(a.getDate())}/${z(a.getMonth() + 1)}/${a.getFullYear()}`;
1293
+ return `${U(a.getDate())}/${U(a.getMonth() + 1)}/${a.getFullYear()}`;
1020
1294
  }
1021
1295
  //#endregion
1022
1296
  //#region src/core/views.ts
1023
- function V(e) {
1297
+ function G(e) {
1024
1298
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1025
1299
  }
1026
- function ce(e, t, n) {
1027
- let r = `<button class="msg-list-new" type="button">+ ${V(t.newConversation)}</button>`;
1028
- return e.length === 0 ? `<div class="msg-list-empty">${V(t.noConversations)}</div>${r}` : `<div class="msg-list">${e.map((e) => {
1029
- let r = e.lastMessageAt ? Date.parse(e.lastMessageAt) : n, i = Number.isFinite(r) ? B(r, n, "vi") : "", a = e.escalated ? `<span class="msg-list-badge">${V(t.waitingForHuman)}</span>` : "";
1030
- return `<button class="msg-list-item" type="button" data-session="${V(e.sessionId)}">
1031
- <span class="msg-list-title">${V(e.title)}</span>
1032
- <span class="msg-list-preview">${V(e.preview)}</span>
1033
- <span class="msg-list-time">${V(i)}</span>${a}
1300
+ function ge(e, t, n, r = "") {
1301
+ let i = `<button class="msg-list-new" type="button">+ ${G(t.newConversation)}</button>`;
1302
+ return e.length === 0 ? `<div class="msg-list-empty">${G(t.noConversations)}</div>${i}` : `<div class="msg-list">${e.map((e) => {
1303
+ let i = e.lastMessageAt ? Date.parse(e.lastMessageAt) : n, a = Number.isFinite(i) ? W(i, n, "vi") : "", o = e.escalated ? `<span class="msg-list-badge">${G(t.waitingForHuman)}</span>` : "";
1304
+ return `<button class="msg-list-item" type="button" data-session="${G(e.sessionId)}">
1305
+ ${r ? `<div class="msg-list-avatar">${r}</div>` : ""}
1306
+ <div class="msg-list-body">
1307
+ <span class="msg-list-title">${G(e.title)}</span>
1308
+ <span class="msg-list-preview">${G(e.preview)}</span>
1309
+ <span class="msg-list-time">${G(a)}</span>${o}
1310
+ </div>
1034
1311
  </button>`;
1035
- }).join("")}</div>${r}`;
1036
- }
1037
- function le(e, t) {
1038
- let n = e.status !== "offline", r = n ? t.statusOnline : t.statusPaused;
1312
+ }).join("")}</div>${i}`;
1313
+ }
1314
+ function _e(e, t, n = [], r = "", i = Date.now()) {
1315
+ let a = e.status !== "offline", o = a ? t.statusOnline : t.statusPaused, s = G(e.design.content.greeting || t.homeGreeting), c = "";
1316
+ if (n.length > 0) {
1317
+ let e = n.slice(0, 3).map((e) => {
1318
+ let t = e.lastMessageAt ? Date.parse(e.lastMessageAt) : i, n = Number.isFinite(t) ? W(t, i, "vi") : "";
1319
+ return `<button class="home-recent-item" type="button" data-session="${G(e.sessionId)}">
1320
+ ${r ? `<div class="home-recent-avatar">${r}</div>` : ""}
1321
+ <div class="home-recent-body">
1322
+ <div class="home-recent-title">${G(e.title)}</div>
1323
+ <div class="home-recent-preview">${G(e.preview)}</div>
1324
+ </div>
1325
+ <span class="home-recent-time">${G(n)}</span>
1326
+ </button>`;
1327
+ }).join("");
1328
+ c = `<div class="home-recent-label">${G(t.recentMessages)}</div>${e}`;
1329
+ }
1039
1330
  return `
1040
- <div class="home-greeting">${V(e.design.content.greeting || t.homeGreeting)}</div>
1041
- <button class="home-start" type="button">${V(t.startConversation)} ›</button>
1042
- <div class="home-status home-status--${n ? "online" : "offline"}">● ${V(r)}</div>
1331
+ <div class="home-greeting">${s}</div>
1332
+ <button class="home-start" type="button">${G(t.startConversation)} ›</button>
1333
+ ${c}
1334
+ <div class="home-status home-status--${a ? "online" : "offline"}">● ${G(o)}</div>
1043
1335
  `;
1044
1336
  }
1045
1337
  //#endregion
1046
1338
  //#region src/core/assets.ts
1047
- var ue = "data:image/png;base64,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";
1339
+ var ve = "data:image/png;base64,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";
1048
1340
  //#endregion
1049
1341
  //#region src/core/history-pager.ts
1050
- function H() {
1342
+ function K() {
1051
1343
  return {
1052
1344
  oldestCursor: null,
1053
1345
  hasMore: !1,
1054
1346
  cursorReady: !1
1055
1347
  };
1056
1348
  }
1057
- function U(e) {
1349
+ function ye(e) {
1058
1350
  return {
1059
1351
  oldestCursor: e.messages[0]?.id ?? null,
1060
1352
  hasMore: e.hasMore,
1061
1353
  cursorReady: e.messages.length > 0
1062
1354
  };
1063
1355
  }
1064
- function de(e, t) {
1356
+ function be(e, t) {
1065
1357
  return {
1066
1358
  oldestCursor: t.messages[0]?.id ?? e.oldestCursor,
1067
1359
  hasMore: t.hasMore,
1068
1360
  cursorReady: e.cursorReady
1069
1361
  };
1070
1362
  }
1071
- function fe(e, t) {
1363
+ function xe(e, t) {
1072
1364
  return e.cursorReady && e.hasMore && !t && e.oldestCursor !== null;
1073
1365
  }
1074
- function pe(e, t, n) {
1366
+ function Se(e, t, n) {
1075
1367
  return e + (n - t);
1076
1368
  }
1077
1369
  //#endregion
1078
1370
  //#region src/core/confirm-dialog.ts
1079
- function W(e) {
1371
+ function q(e) {
1080
1372
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1081
1373
  }
1082
- function me(e, t) {
1374
+ function Ce(e, t) {
1083
1375
  let n = t.labels?.confirm ?? "Đồng ý", r = t.labels?.cancel ?? "Huỷ", i = t.labels?.countdownPrefix ?? "Tự động xác nhận sau ", a = t.labels?.countdownSuffix ?? "s…";
1084
1376
  return new Promise((o) => {
1085
1377
  let s = document.createElement("div");
1086
1378
  s.className = "botiq-confirm-overlay", s.innerHTML = `
1087
1379
  <div class="botiq-confirm-dialog" role="dialog" aria-modal="true">
1088
- <div class="botiq-confirm-header">${W(t.title)}</div>
1380
+ <div class="botiq-confirm-header">${q(t.title)}</div>
1089
1381
  <div class="botiq-confirm-body">
1090
- <div class="botiq-confirm-reason">${W(t.reason)}</div>
1091
- <pre class="botiq-confirm-params">${W(t.paramsDisplay)}</pre>
1382
+ <div class="botiq-confirm-reason">${q(t.reason)}</div>
1383
+ <pre class="botiq-confirm-params">${q(t.paramsDisplay)}</pre>
1092
1384
  </div>
1093
1385
  <div class="botiq-confirm-actions">
1094
- <button class="botiq-confirm-yes" data-risk="${t.riskLevel}" type="button">${W(n)}</button>
1095
- <button class="botiq-confirm-no" type="button">${W(r)}</button>
1386
+ <button class="botiq-confirm-yes" data-risk="${t.riskLevel}" type="button">${q(n)}</button>
1387
+ <button class="botiq-confirm-no" type="button">${q(r)}</button>
1096
1388
  </div>
1097
- ${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${W(i)}<span class="botiq-confirm-secs">${t.autoConfirmAfterSec}</span>${W(a)}</div>` : ""}
1389
+ ${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${q(i)}<span class="botiq-confirm-secs">${t.autoConfirmAfterSec}</span>${q(a)}</div>` : ""}
1098
1390
  </div>`, e.appendChild(s);
1099
1391
  let c = !1, l = null, u = (t) => {
1100
1392
  c || (c = !0, l && clearInterval(l), e.removeChild(s), o(t));
@@ -1109,15 +1401,15 @@ function me(e, t) {
1109
1401
  }
1110
1402
  //#endregion
1111
1403
  //#region src/core/emoji.ts
1112
- function he(e, t) {
1404
+ function we(e, t) {
1113
1405
  let n = e.selectionStart ?? e.value.length, r = e.selectionEnd ?? e.value.length;
1114
1406
  e.value = e.value.slice(0, n) + t + e.value.slice(r), e.selectionStart = e.selectionEnd = n + t.length, e.dispatchEvent(new Event("input", { bubbles: !0 }));
1115
1407
  }
1116
- var G = null;
1117
- async function ge() {
1118
- return G || (G = (await import("./emoji-data-DWiYsBZI.js")).EMOJIS, G);
1408
+ var J = null;
1409
+ async function Te() {
1410
+ return J || (J = (await import("./emoji-data-DWiYsBZI.js")).EMOJIS, J);
1119
1411
  }
1120
- function _e(e) {
1412
+ function Ee(e) {
1121
1413
  let t = document.createElement("button");
1122
1414
  t.type = "button", t.className = "emoji-btn", t.setAttribute("aria-label", "Emoji"), t.textContent = "🙂";
1123
1415
  let n = document.createElement("div");
@@ -1125,7 +1417,7 @@ function _e(e) {
1125
1417
  let r = !1;
1126
1418
  return t.addEventListener("click", async () => {
1127
1419
  if (!r) {
1128
- let t = await ge();
1420
+ let t = await Te();
1129
1421
  for (let r of t) {
1130
1422
  let t = document.createElement("button");
1131
1423
  t.type = "button", t.className = "emoji-item", t.textContent = r, t.addEventListener("click", () => {
@@ -1141,45 +1433,95 @@ function _e(e) {
1141
1433
  };
1142
1434
  }
1143
1435
  //#endregion
1436
+ //#region src/core/blocks-renderer.ts
1437
+ function Y(e) {
1438
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1439
+ }
1440
+ function X(e) {
1441
+ return /^https?:\/\//i.test(e) ? e : "";
1442
+ }
1443
+ function De(e) {
1444
+ let t = Y(e.label), n = Y(e.value), r = Y(e.action);
1445
+ if (e.action === "url") {
1446
+ let n = X(e.value);
1447
+ return n ? `<a class="biq-btn biq-btn--url" href="${Y(n)}" target="_blank" rel="noopener noreferrer">${t}</a>` : "";
1448
+ }
1449
+ return `<button class="biq-btn biq-btn--${r}" type="button" data-action="${r}" data-value="${n}">${t}</button>`;
1450
+ }
1451
+ function Oe(e) {
1452
+ let t = `${e.imageUrl ? (() => {
1453
+ let t = X(e.imageUrl);
1454
+ return t ? `<img class="biq-card-img" src="${Y(t)}" alt="" loading="lazy" />` : "";
1455
+ })() : ""}${`<div class="biq-card-title">${Y(e.title)}</div>`}${e.subtitle ? `<div class="biq-card-subtitle">${Y(e.subtitle)}</div>` : ""}${e.price ? `<div class="biq-card-price">${Y(e.price)}</div>` : ""}${e.buttons && e.buttons.length > 0 ? `<div class="biq-card-btns">${e.buttons.map(De).join("")}</div>` : ""}`;
1456
+ if (e.url) {
1457
+ let n = X(e.url);
1458
+ if (n) return `<a class="biq-card biq-card--link" href="${Y(n)}" target="_blank" rel="noopener noreferrer">${t}</a>`;
1459
+ }
1460
+ return `<div class="biq-card">${t}</div>`;
1461
+ }
1462
+ function ke(e) {
1463
+ return `<div class="biq-carousel">${e.cards.map(Oe).join("")}</div>`;
1464
+ }
1465
+ function Ae(e) {
1466
+ return `<div class="biq-quick-replies">${e.replies.map((e) => {
1467
+ let t = Y(e.label);
1468
+ return `<button class="biq-qr-pill" type="button" data-qr="1" data-value="${Y(e.value)}">${t}</button>`;
1469
+ }).join("")}</div>`;
1470
+ }
1471
+ function je(e) {
1472
+ return `<div class="biq-buttons">${e.buttons.map(De).join("")}</div>`;
1473
+ }
1474
+ function Me(e) {
1475
+ return !e || e.length === 0 ? "" : e.map((e) => {
1476
+ switch (e.type) {
1477
+ case "card": return Oe(e);
1478
+ case "carousel": return ke(e);
1479
+ case "quick_replies": return Ae(e);
1480
+ case "buttons": return je(e);
1481
+ default: return "";
1482
+ }
1483
+ }).join("");
1484
+ }
1485
+ //#endregion
1144
1486
  //#region src/core/ui.ts
1145
- var ve = "https://bot-q-frontend.vercel.app/", ye = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2z\"/>\n</svg>", be = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n</svg>", K = `<img src="${ue}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%" />`, xe = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/>\n</svg>", Se = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2zm-2 10H6v-2h12v2zm0-3H6V7h12v2z\"/>\n</svg>";
1146
- function Ce(e) {
1147
- if (!e || e.type === "icon") return K;
1148
- if (e.type === "emoji") return `<span style="font-size:22px;line-height:1;display:flex;align-items:center;justify-content:center;width:100%;height:100%">${q(e.value)}</span>`;
1487
+ var Ne = "https://bot-q-frontend.vercel.app/", Pe = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2z\"/>\n</svg>", Fe = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n</svg>", Ie = `<img src="${ve}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%" />`, Le = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/>\n</svg>", Re = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2zm-2 10H6v-2h12v2zm0-3H6V7h12v2z\"/>\n</svg>", ze = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48\"/>\n</svg>";
1488
+ function Be(e) {
1489
+ if (!e || e.type === "icon") return Ie;
1490
+ if (e.type === "emoji") return `<span style="font-size:22px;line-height:1;display:flex;align-items:center;justify-content:center;width:100%;height:100%">${Z(e.value)}</span>`;
1149
1491
  if (e.type === "initials") {
1150
1492
  let t = e.bgColor ?? "#F97316";
1151
- return `<div style="width:100%;height:100%;border-radius:50%;background:${/^#[0-9A-Fa-f]{3,6}$/.test(t) ? t : "#F97316"};display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff">${q(e.value.slice(0, 2).toUpperCase())}</div>`;
1493
+ return `<div style="width:100%;height:100%;border-radius:50%;background:${/^#[0-9A-Fa-f]{3,6}$/.test(t) ? t : "#F97316"};display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff">${Z(e.value.slice(0, 2).toUpperCase())}</div>`;
1152
1494
  }
1153
- return e.type === "image" ? `<img src="${q(e.value)}" style="width:100%;height:100%;object-fit:cover;border-radius:50%" alt="" />` : K;
1495
+ return e.type === "image" ? `<img src="${Z(e.value)}" style="width:100%;height:100%;object-fit:cover;border-radius:50%" alt="" />` : Ie;
1154
1496
  }
1155
- function q(e) {
1497
+ function Z(e) {
1156
1498
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1157
1499
  }
1158
- function J(e) {
1500
+ function Q(e) {
1159
1501
  let t = [];
1160
1502
  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, "<del>$1</del>"), e = e.replace(/__(.+?)__/g, "<strong>$1</strong>"), e = e.replace(/(^|\s)_(?!_)(.+?)_(?!_)(?=\s|$)/g, "$1<em>$2</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;
1161
1503
  }
1162
- function we(e) {
1504
+ function Ve(e) {
1163
1505
  return /^\|[\s\-:|]+\|$/.test(e);
1164
1506
  }
1165
- function Y(e) {
1507
+ function He(e) {
1166
1508
  return e.replace(/^\|/, "").replace(/\|$/, "").split("|").map((e) => e.trim());
1167
1509
  }
1168
- function Te(e, t) {
1169
- return e ? `<div class="handoff-banner">⏳ ${q(t.waitingForHuman)}</div>` : "";
1510
+ function Ue(e, t) {
1511
+ return e ? `<div class="handoff-banner">⏳ ${Z(t.waitingForHuman)}</div>` : "";
1170
1512
  }
1171
- function Ee(e) {
1513
+ function We(e) {
1172
1514
  return e <= 0 ? "" : `<span class="unread-badge">${e > 9 ? "9+" : String(e)}</span>`;
1173
1515
  }
1174
- function De(e) {
1175
- let t = q(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
1516
+ function Ge(e) {
1517
+ let t = Z(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
1176
1518
  for (; r < t.length;) {
1177
1519
  let e = t[r].trim();
1178
1520
  if (e.startsWith("|") && e.endsWith("|")) {
1179
1521
  let e = [];
1180
1522
  for (; r < t.length && t[r].trim().startsWith("|") && t[r].trim().endsWith("|");) e.push(t[r].trim()), r++;
1181
- let i = e.findIndex(we), a = i > 0 ? e.slice(0, i) : [], o = i >= 0 ? e.slice(i + 1) : e, s = "<table>";
1182
- a.length > 0 && (s += "<thead>" + a.map((e) => "<tr>" + Y(e).map((e) => `<th>${J(e)}</th>`).join("") + "</tr>").join("") + "</thead>"), o.length > 0 && (s += "<tbody>" + o.map((e) => "<tr>" + Y(e).map((e) => `<td>${J(e)}</td>`).join("") + "</tr>").join("") + "</tbody>"), s += "</table>", n.push(s);
1523
+ let i = e.findIndex(Ve), a = i > 0 ? e.slice(0, i) : [], o = i >= 0 ? e.slice(i + 1) : e, s = "<table>";
1524
+ a.length > 0 && (s += "<thead>" + a.map((e) => "<tr>" + He(e).map((e) => `<th>${Q(e)}</th>`).join("") + "</tr>").join("") + "</thead>"), o.length > 0 && (s += "<tbody>" + o.map((e) => "<tr>" + He(e).map((e) => `<td>${Q(e)}</td>`).join("") + "</tr>").join("") + "</tbody>"), s += "</table>", n.push(s);
1183
1525
  continue;
1184
1526
  }
1185
1527
  if (/^-{3,}$/.test(e)) {
@@ -1189,67 +1531,67 @@ function De(e) {
1189
1531
  let i = e.match(/^(#{1,6})\s+(.*)$/);
1190
1532
  if (i) {
1191
1533
  let e = i[1].length;
1192
- n.push(`<h${e}>${J(i[2])}</h${e}>`), r++;
1534
+ n.push(`<h${e}>${Q(i[2])}</h${e}>`), r++;
1193
1535
  continue;
1194
1536
  }
1195
1537
  if (/^[-*]\s/.test(e)) {
1196
1538
  let e = [];
1197
1539
  for (; r < t.length && /^\s*[-*]\s/.test(t[r]);) e.push(t[r].trim().replace(/^[-*]\s+/, "")), r++;
1198
- n.push("<ul>" + e.map((e) => `<li>${J(e)}</li>`).join("") + "</ul>");
1540
+ n.push("<ul>" + e.map((e) => `<li>${Q(e)}</li>`).join("") + "</ul>");
1199
1541
  continue;
1200
1542
  }
1201
1543
  if (/^\d+\.\s/.test(e)) {
1202
1544
  let e = [];
1203
1545
  for (; r < t.length && /^\s*\d+\.\s/.test(t[r]);) e.push(t[r].trim().replace(/^\d+\.\s+/, "")), r++;
1204
- n.push("<ol>" + e.map((e) => `<li>${J(e)}</li>`).join("") + "</ol>");
1546
+ n.push("<ol>" + e.map((e) => `<li>${Q(e)}</li>`).join("") + "</ol>");
1205
1547
  continue;
1206
1548
  }
1207
1549
  if (e === "") {
1208
1550
  n.push("<br>"), r++;
1209
1551
  continue;
1210
1552
  }
1211
- n.push(J(e)), n.push("<br>"), r++;
1553
+ n.push(Q(e)), n.push("<br>"), r++;
1212
1554
  }
1213
1555
  for (; n.length > 0 && n[n.length - 1] === "<br>";) n.pop();
1214
1556
  return n.join("");
1215
1557
  }
1216
- function Oe(e, t) {
1217
- let { strings: n, content: r, botName: i, lang: a, now: o, contactInfo: s } = t;
1558
+ function Ke(e, t) {
1559
+ let { strings: n, content: r, botName: i, lang: a, now: o, contactInfo: s, richMessages: c } = t;
1218
1560
  if (e.messages.length === 0 && !e.isLoading) {
1219
- let e = r.suggestionChips.length > 0 ? `<div class="chips">${r.suggestionChips.map((e) => `<button class="chip">${q(e)}</button>`).join("")}</div>` : "";
1561
+ let e = r.suggestionChips.length > 0 ? `<div class="chips">${r.suggestionChips.map((e) => `<button class="chip">${Z(e)}</button>`).join("")}</div>` : "";
1220
1562
  return `
1221
1563
  <div class="empty-state">
1222
- ${Se}
1223
- <span class="greeting">${q(r.greeting || n.defaultGreeting)}</span>
1564
+ ${Re}
1565
+ <span class="greeting">${Z(r.greeting || n.defaultGreeting)}</span>
1224
1566
  ${e}
1225
1567
  </div>
1226
1568
  `;
1227
1569
  }
1228
- let c = "", l = e.messages.map((e) => {
1229
- let t = e.ts ?? o, r = se(t, o, a), l = r === c ? "" : `<div class="day-sep"><span>${q(r)}</span></div>`;
1230
- c = r;
1231
- let u = e.role === "assistant" ? De(e.content) : q(e.content), d = e.role === "assistant" && !e.error ? `<div class="msg-meta">${q(i)} · ${q(B(t, o, a))}</div>` : "", f = e.error ? `<button class="retry-btn" type="button">↻ ${q(n.retry)}</button>` : "", p = e.error && s ? `<div class="msg-contact">${q(s)}</div>` : "";
1232
- return `${l}<div class="${`message ${e.role}${e.error ? " error" : ""}`}"><div class="message-bubble">${u}</div>${f}${p}${d}</div>`;
1233
- }).join(""), u = e.loadingOlder ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "", d = e.isLoading ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "";
1234
- return u + l + d;
1235
- }
1236
- function ke(e, t, n, r) {
1237
- let { name: i, design: a } = t, o, s, c, l, u, d, f, p, m, h, g, _, { content: v } = a;
1238
- function y(e) {
1570
+ let l = "", u = e.messages.map((e) => {
1571
+ let t = e.ts ?? o, r = he(t, o, a), u = r === l ? "" : `<div class="day-sep"><span>${Z(r)}</span></div>`;
1572
+ l = r;
1573
+ let d = e.role === "assistant" ? Ge(e.content) : Z(e.content), f = e.role === "assistant" && !e.error ? `<div class="msg-meta">${Z(i)} · ${Z(W(t, o, a))}</div>` : "", p = e.error ? `<button class="retry-btn" type="button">↻ ${Z(n.retry)}</button>` : "", m = e.error && s ? `<div class="msg-contact">${Z(s)}</div>` : "", h = c && e.role === "assistant" && !e.error && e.blocks && e.blocks.length > 0 ? `<div class="biq-blocks">${Me(e.blocks)}</div>` : "";
1574
+ return `${u}<div class="${`message ${e.role}${e.error ? " error" : ""}`}"><div class="message-bubble">${d}</div>${p}${m}${f}${h}</div>`;
1575
+ }).join(""), d = e.loadingOlder ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "", f = e.isLoading ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "";
1576
+ return d + u + f;
1577
+ }
1578
+ function qe(e, t, n, r) {
1579
+ let { name: i, design: a } = t, o, s, c, l, u, d, f, p, m, h, g, _, v = null, y = null, b = null, { content: x } = a, S = t.imageInput === !0, C = Be(a.avatar), w = null;
1580
+ function T(e) {
1239
1581
  e.setAttribute("data-position", a.layout.position), o = e.attachShadow({ mode: "closed" });
1240
- let y = document.createElement("style");
1241
- y.textContent = oe(a), o.appendChild(y), s = document.createElement("button"), s.className = "bubble", s.setAttribute("aria-label", n.ariaOpenChat), s.innerHTML = `
1242
- <span class="icon-chat">${ye}</span>
1243
- <span class="icon-close">${be}</span>
1582
+ let w = document.createElement("style");
1583
+ w.textContent = me(a), o.appendChild(w), s = document.createElement("button"), s.className = "bubble", s.setAttribute("aria-label", n.ariaOpenChat), s.innerHTML = `
1584
+ <span class="icon-chat">${Pe}</span>
1585
+ <span class="icon-close">${Fe}</span>
1244
1586
  <span class="unread-badge-slot"></span>
1245
1587
  `, s.addEventListener("click", () => r.onToggle()), o.appendChild(s), c = document.createElement("div"), c.className = "chat-window", c.setAttribute("role", "dialog"), c.setAttribute("aria-label", `${i} chat`), c.innerHTML = `
1246
1588
  <div class="chat-header">
1247
- <div class="avatar">${Ce(a.avatar)}</div>
1589
+ <div class="avatar">${Be(a.avatar)}</div>
1248
1590
  <div class="header-text">
1249
- <span class="bot-name">${q(i)}</span>
1250
- <span class="bot-status">${q(n.statusOnline)}</span>
1591
+ <span class="bot-name">${Z(i)}</span>
1592
+ <span class="bot-status">${Z(n.statusOnline)}</span>
1251
1593
  </div>
1252
- <button class="close-btn" aria-label="${q(n.ariaCloseChat)}">×</button>
1594
+ <button class="close-btn" aria-label="${Z(n.ariaCloseChat)}">×</button>
1253
1595
  </div>
1254
1596
  <div class="chat-body">
1255
1597
  <div class="panel panel-home"></div>
@@ -1258,29 +1600,31 @@ function ke(e, t, n, r) {
1258
1600
  <div class="messages" id="messages-container" role="log" aria-live="polite" aria-atomic="false"></div>
1259
1601
  <div class="handoff-slot"></div>
1260
1602
  <div class="chat-footer">
1603
+ <div class="attach-preview-slot"></div>
1261
1604
  <div class="input-row">
1605
+ ${S ? `<button class="attach-btn" type="button" aria-label="${Z(n.imageAttach)}">${ze}</button>` : ""}
1262
1606
  <textarea
1263
1607
  class="input"
1264
- placeholder="${q(v.placeholder || n.inputPlaceholder)}"
1608
+ placeholder="${Z(x.placeholder || n.inputPlaceholder)}"
1265
1609
  rows="1"
1266
1610
  maxlength="2000"
1267
1611
  aria-label="Message input"
1268
1612
  ></textarea>
1269
- <button class="send-btn" aria-label="${q(n.ariaSendMessage)}">
1270
- ${xe}
1613
+ <button class="send-btn" aria-label="${Z(n.ariaSendMessage)}">
1614
+ ${Le}
1271
1615
  </button>
1272
1616
  </div>
1273
- <a class="botiq-badge" href="${ve}" target="_blank" rel="noopener noreferrer">
1274
- ${q(n.poweredBy)} <span class="botiq-badge-name">BotIQ</span>
1617
+ <a class="botiq-badge" href="${Ne}" target="_blank" rel="noopener noreferrer">
1618
+ ${Z(n.poweredBy)} <span class="botiq-badge-name">BotIQ</span>
1275
1619
  </a>
1276
1620
  </div>
1277
1621
  </div>
1278
1622
  </div>
1279
1623
  <div class="bottom-nav">
1280
- <button class="nav-btn" data-view="home">⌂ ${q(n.navHome)}</button>
1281
- <button class="nav-btn" data-view="messages">💬 ${q(n.navMessages)}</button>
1624
+ <button class="nav-btn" data-view="home">⌂ ${Z(n.navHome)}</button>
1625
+ <button class="nav-btn" data-view="messages">💬 ${Z(n.navMessages)}</button>
1282
1626
  </div>
1283
- `, c.querySelector(".close-btn").addEventListener("click", () => r.onToggle()), l = c.querySelector(".panel-home"), u = c.querySelector(".panel-messages"), d = c.querySelector(".panel-conversation"), l.innerHTML = le(t, n), l.querySelector(".home-start")?.addEventListener("click", () => {
1627
+ `, c.querySelector(".close-btn").addEventListener("click", () => r.onToggle()), l = c.querySelector(".panel-home"), u = c.querySelector(".panel-messages"), d = c.querySelector(".panel-conversation"), l.innerHTML = _e(t, n, [], C), l.querySelector(".home-start")?.addEventListener("click", () => {
1284
1628
  r.onNewConversation();
1285
1629
  }), f = c.querySelector(".nav-btn[data-view=\"home\"]"), p = c.querySelector(".nav-btn[data-view=\"messages\"]"), c.querySelectorAll(".nav-btn").forEach((e) => {
1286
1630
  e.addEventListener("click", () => {
@@ -1288,44 +1632,105 @@ function ke(e, t, n, r) {
1288
1632
  r.onNavigate(t);
1289
1633
  });
1290
1634
  }), m = c.querySelector("#messages-container"), h = c.querySelector(".handoff-slot");
1291
- let x = null;
1635
+ let T = null;
1292
1636
  m.addEventListener("scroll", () => {
1293
- m.scrollTop <= 48 && !x && (x = setTimeout(() => {
1294
- x = null;
1637
+ m.scrollTop <= 48 && !T && (T = setTimeout(() => {
1638
+ T = null;
1295
1639
  }, 150), r.onScrollTop());
1296
1640
  }), g = c.querySelector(".input"), _ = c.querySelector(".send-btn"), g.addEventListener("input", () => {
1297
1641
  g.style.height = "auto", g.style.height = Math.min(g.scrollHeight, 100) + "px";
1298
1642
  }), g.addEventListener("keydown", (e) => {
1299
- e.key === "Enter" && !e.shiftKey && (e.preventDefault(), b());
1300
- }), _.addEventListener("click", b);
1301
- let S = c.querySelector(".input-row"), { button: C, panel: w } = _e((e) => {
1302
- he(g, e), g.focus();
1643
+ e.key === "Enter" && !e.shiftKey && (e.preventDefault(), E());
1644
+ }), _.addEventListener("click", E);
1645
+ let D = c.querySelector(".input-row"), { button: O, panel: k } = Ee((e) => {
1646
+ we(g, e), g.focus();
1303
1647
  });
1304
- S.insertBefore(C, _), c.querySelector(".chat-footer").appendChild(w), o.appendChild(c);
1648
+ D.insertBefore(O, _), c.querySelector(".chat-footer").appendChild(k), y = c.querySelector(".attach-preview-slot"), S && (v = c.querySelector(".attach-btn"), v && (b = document.createElement("input"), b.type = "file", b.accept = "image/jpeg,image/png,image/webp", b.style.display = "none", b.setAttribute("aria-hidden", "true"), o.appendChild(b), v.addEventListener("click", () => {
1649
+ b?.click();
1650
+ }), b.addEventListener("change", () => {
1651
+ let e = b?.files?.[0];
1652
+ e && r.onAttachImage && (r.onAttachImage(e), b && (b.value = ""));
1653
+ }))), o.appendChild(c);
1305
1654
  }
1306
- function b() {
1655
+ function E() {
1307
1656
  let e = g.value.trim();
1308
- !e || _.disabled || (g.value = "", g.style.height = "auto", r.onSend(e));
1657
+ !e && !y?.querySelector(".attach-preview") || _.disabled || (g.value = "", g.style.height = "auto", r.onSend(e));
1309
1658
  }
1310
- function x(e) {
1659
+ function D(e) {
1311
1660
  let a = m.scrollHeight - m.scrollTop - m.clientHeight < 80, o = m.scrollTop, s = m.scrollHeight;
1312
- m.innerHTML = Oe(e, {
1661
+ m.innerHTML = Ke(e, {
1313
1662
  strings: n,
1314
- content: v,
1663
+ content: x,
1315
1664
  botName: i,
1316
1665
  lang: t.widgetLanguage,
1317
1666
  now: Date.now(),
1318
- contactInfo: t.contactInfo
1667
+ contactInfo: t.contactInfo,
1668
+ richMessages: t.richMessages
1319
1669
  }), m.querySelectorAll(".chip").forEach((e) => {
1320
1670
  e.addEventListener("click", () => r.onSend(e.textContent ?? ""));
1321
1671
  }), m.querySelectorAll(".retry-btn").forEach((e) => {
1322
1672
  e.addEventListener("click", () => r.onRetry());
1323
- }), a ? m.scrollTop = m.scrollHeight : m.scrollTop = pe(o, s, m.scrollHeight);
1673
+ }), m.querySelectorAll(".biq-btn[data-action=\"send\"]").forEach((e) => {
1674
+ e.addEventListener("click", () => {
1675
+ let t = e.dataset.value ?? "";
1676
+ if (t) {
1677
+ r.onSend(t);
1678
+ let n = e.closest(".biq-blocks");
1679
+ n && (n.style.pointerEvents = "none", n.style.opacity = "0.5");
1680
+ }
1681
+ });
1682
+ }), m.querySelectorAll(".biq-btn[data-action=\"page_action\"]").forEach((e) => {
1683
+ e.addEventListener("click", () => {
1684
+ let t = e.dataset.value ?? "";
1685
+ if (t && r.onBlockPageAction) {
1686
+ let n = e.closest(".biq-blocks");
1687
+ n && (n.style.pointerEvents = "none", n.style.opacity = "0.5"), r.onBlockPageAction(t);
1688
+ }
1689
+ });
1690
+ }), m.querySelectorAll(".biq-qr-pill[data-qr=\"1\"]").forEach((e) => {
1691
+ e.addEventListener("click", () => {
1692
+ let t = e.dataset.value ?? "";
1693
+ if (t) {
1694
+ r.onSend(t);
1695
+ let n = e.closest(".biq-quick-replies");
1696
+ n && (n.style.display = "none");
1697
+ }
1698
+ });
1699
+ }), a ? m.scrollTop = m.scrollHeight : m.scrollTop = Se(o, s, m.scrollHeight);
1324
1700
  }
1325
- function S(e) {
1701
+ function O(e) {
1326
1702
  e.isOpen ? (c.classList.add("open"), s.classList.add("open"), s.setAttribute("aria-label", n.ariaCloseChat), e.view === "conversation" && requestAnimationFrame(() => g.focus())) : (c.classList.remove("open"), s.classList.remove("open"), s.setAttribute("aria-label", n.ariaOpenChat));
1327
- let t = s.querySelector(".unread-badge-slot");
1328
- t && (t.innerHTML = Ee(e.unreadCount)), _.disabled = e.isLoading, c.dataset.view = e.view, l.classList.toggle("active", e.view === "home"), u.classList.toggle("active", e.view === "messages"), d.classList.toggle("active", e.view === "conversation"), f.classList.toggle("active", e.view === "home"), p.classList.toggle("active", e.view === "messages"), e.view === "conversation" && (x(e), h.innerHTML = Te(e.escalated, n)), e.view === "messages" && (u.innerHTML = ce(e.conversations, n, Date.now()), u.querySelectorAll(".msg-list-item").forEach((e) => {
1703
+ let i = s.querySelector(".unread-badge-slot");
1704
+ if (i && (i.innerHTML = We(e.unreadCount)), _.disabled = e.isLoading || e.isUploading, y && S) if (e.pendingAttachment) {
1705
+ let t = e.pendingAttachment, i = e.isUploading, a = e.uploadError;
1706
+ y.innerHTML = `
1707
+ <div class="attach-preview">
1708
+ <img
1709
+ src="${Z(t.previewUrl)}"
1710
+ alt="Preview"
1711
+ class="attach-preview-img${i ? " attach-preview-img--uploading" : ""}"
1712
+ />
1713
+ ${i ? `<span class="attach-preview-status">${Z(n.imageUploading)}</span>` : ""}
1714
+ ${a ? `<span class="attach-preview-error">${Z(a)}</span>` : ""}
1715
+ ${i ? "" : `<button class="attach-preview-remove" type="button" aria-label="${Z(n.imageRemove)}">×</button>`}
1716
+ </div>
1717
+ `, y.querySelector(".attach-preview-remove")?.addEventListener("click", () => {
1718
+ r.onRemoveAttachment?.();
1719
+ });
1720
+ } else y.innerHTML = "";
1721
+ if (c.dataset.view = e.view, l.classList.toggle("active", e.view === "home"), u.classList.toggle("active", e.view === "messages"), d.classList.toggle("active", e.view === "conversation"), f.classList.toggle("active", e.view === "home"), p.classList.toggle("active", e.view === "messages"), e.view === "home" && e.conversations !== w) {
1722
+ w = e.conversations;
1723
+ let i = Date.now();
1724
+ l.innerHTML = _e(t, n, e.conversations, C, i), l.querySelector(".home-start")?.addEventListener("click", () => {
1725
+ r.onNewConversation();
1726
+ }), l.querySelectorAll(".home-recent-item").forEach((e) => {
1727
+ e.addEventListener("click", () => {
1728
+ let t = e.dataset.session;
1729
+ t && r.onOpenConversation(t);
1730
+ });
1731
+ });
1732
+ }
1733
+ e.view === "conversation" && (D(e), h.innerHTML = Ue(e.escalated, n)), e.view === "messages" && (u.innerHTML = ge(e.conversations, n, Date.now(), C), u.querySelectorAll(".msg-list-item").forEach((e) => {
1329
1734
  e.addEventListener("click", () => {
1330
1735
  let t = e.dataset.session;
1331
1736
  t && r.onOpenConversation(t);
@@ -1334,8 +1739,8 @@ function ke(e, t, n, r) {
1334
1739
  r.onNewConversation();
1335
1740
  }));
1336
1741
  }
1337
- function C(e) {
1338
- return me(o, {
1742
+ function k(e) {
1743
+ return Ce(o, {
1339
1744
  ...e,
1340
1745
  labels: {
1341
1746
  confirm: n.confirmYes,
@@ -1346,14 +1751,14 @@ function ke(e, t, n, r) {
1346
1751
  });
1347
1752
  }
1348
1753
  return {
1349
- mount: y,
1350
- update: S,
1351
- confirmAction: C
1754
+ mount: T,
1755
+ update: O,
1756
+ confirmAction: k
1352
1757
  };
1353
1758
  }
1354
1759
  //#endregion
1355
1760
  //#region src/i18n/vi.ts
1356
- var Ae = {
1761
+ var Je = {
1357
1762
  statusOnline: "Trực tuyến",
1358
1763
  statusPaused: "Tạm ngừng",
1359
1764
  navHome: "Trang chủ",
@@ -1385,8 +1790,16 @@ var Ae = {
1385
1790
  confirmNo: "Huỷ",
1386
1791
  confirmCountdownPrefix: "Tự động xác nhận sau ",
1387
1792
  confirmCountdownSuffix: "s…",
1388
- retry: "Thử lại"
1389
- }, X = {
1793
+ retry: "Thử lại",
1794
+ recentMessages: "Tin nhắn gần đây",
1795
+ imageAttach: "Đính kèm ảnh",
1796
+ imageRemove: "Xóa ảnh",
1797
+ imageUploading: "Đang tải ảnh…",
1798
+ imageUploadPlanError: "Gói hiện tại không hỗ trợ gửi ảnh.",
1799
+ imageUploadTooLarge: "Ảnh quá lớn (tối đa 5 MB sau khi nén).",
1800
+ imageUploadRateLimit: "Gửi quá nhanh. Vui lòng thử lại.",
1801
+ imageUploadError: "Tải ảnh thất bại. Vui lòng thử lại."
1802
+ }, Ye = {
1390
1803
  statusOnline: "Online",
1391
1804
  statusPaused: "Paused",
1392
1805
  navHome: "Home",
@@ -1418,24 +1831,32 @@ var Ae = {
1418
1831
  confirmNo: "Cancel",
1419
1832
  confirmCountdownPrefix: "Auto-confirming in ",
1420
1833
  confirmCountdownSuffix: "s…",
1421
- retry: "Retry"
1834
+ retry: "Retry",
1835
+ recentMessages: "Recent messages",
1836
+ imageAttach: "Attach image",
1837
+ imageRemove: "Remove image",
1838
+ imageUploading: "Uploading image…",
1839
+ imageUploadPlanError: "Your plan does not support image sending.",
1840
+ imageUploadTooLarge: "Image too large (max 5 MB after compression).",
1841
+ imageUploadRateLimit: "Too many requests. Please try again.",
1842
+ imageUploadError: "Image upload failed. Please try again."
1422
1843
  };
1423
1844
  //#endregion
1424
1845
  //#region src/i18n/index.ts
1425
- function Z(e) {
1426
- return e === "en" ? X : Ae;
1846
+ function Xe(e) {
1847
+ return e === "en" ? Ye : Je;
1427
1848
  }
1428
1849
  //#endregion
1429
1850
  //#region src/core/declarative-executor.ts
1430
- var je = /^[a-zA-Z0-9_-]*$/;
1431
- function Q(e, t) {
1851
+ var Ze = /^[a-zA-Z0-9_-]*$/;
1852
+ function Qe(e, t) {
1432
1853
  return e.replace(/\{([a-zA-Z][a-zA-Z0-9_]*)\}/g, (e, n) => {
1433
1854
  let r = t[n], i = r == null ? "" : String(r);
1434
- if (!je.test(i)) throw Error(`Unsafe param value for "${n}"`);
1855
+ if (!Ze.test(i)) throw Error(`Unsafe param value for "${n}"`);
1435
1856
  return i;
1436
1857
  });
1437
1858
  }
1438
- function Me(e, t) {
1859
+ function $e(e, t) {
1439
1860
  let n = typeof location < "u" ? location.origin : "https://placeholder.local", r, i;
1440
1861
  try {
1441
1862
  r = new URL(e, n), i = new URL(t.replace(/\{[a-zA-Z][a-zA-Z0-9_]*\}/g, "x"), n);
@@ -1445,10 +1866,10 @@ function Me(e, t) {
1445
1866
  if (!/^https?:$/.test(r.protocol)) throw Error("Navigation must be http(s)");
1446
1867
  if (r.origin !== i.origin) throw Error("Navigation origin not allowed");
1447
1868
  }
1448
- async function Ne(e, t) {
1869
+ async function et(e, t) {
1449
1870
  let n = e.config;
1450
1871
  if (e.operation === "click") {
1451
- let e = Q(String(n.selectorTemplate ?? ""), t), r = document.querySelector(e);
1872
+ let e = Qe(String(n.selectorTemplate ?? ""), t), r = document.querySelector(e);
1452
1873
  if (!r) throw Error(`No element for selector: ${e}`);
1453
1874
  r.click();
1454
1875
  return;
@@ -1463,15 +1884,15 @@ async function Ne(e, t) {
1463
1884
  return;
1464
1885
  }
1465
1886
  if (e.operation === "navigate") {
1466
- let e = String(n.urlTemplate ?? ""), r = Q(e, t);
1467
- Me(r, e), location.assign(r);
1887
+ let e = String(n.urlTemplate ?? ""), r = Qe(e, t);
1888
+ $e(r, e), location.assign(r);
1468
1889
  return;
1469
1890
  }
1470
1891
  throw Error(`Unknown operation: ${String(e.operation)}`);
1471
1892
  }
1472
1893
  //#endregion
1473
1894
  //#region src/core/page-action-executor.ts
1474
- async function Pe(e, t) {
1895
+ async function tt(e, t) {
1475
1896
  let n = t.registry.get(e.actionName);
1476
1897
  if (!n) return {
1477
1898
  status: "unknown",
@@ -1496,26 +1917,26 @@ async function Pe(e, t) {
1496
1917
  //#endregion
1497
1918
  //#region src/builds/npm/index.ts
1498
1919
  var $ = 10;
1499
- function Fe(e) {
1500
- if (D.setDeclarativeRunner(Ne), typeof window < "u" && (window.botiq = window.botiq ?? {}, window.botiq.registerActions = (e) => D.register(e)), !e.apiKey) return console.warn("[BotIQ] apiKey is required"), () => void 0;
1920
+ function nt(e) {
1921
+ if (D.setDeclarativeRunner(et), typeof window < "u" && (window.botiq = window.botiq ?? {}, window.botiq.registerActions = (e) => D.register(e)), !e.apiKey) return console.warn("[BotIQ] apiKey is required"), () => void 0;
1501
1922
  let t = p(e);
1502
1923
  y();
1503
1924
  let n = t.apiKey.slice(-16), r = C(), i = b(n), a = x(n, i);
1504
- F({
1925
+ I({
1505
1926
  messages: a,
1506
1927
  view: "home",
1507
1928
  currentSessionId: i
1508
1929
  });
1509
- let o = H(), s = document.createElement("div");
1930
+ let o = K(), s = document.createElement("div");
1510
1931
  document.body.appendChild(s);
1511
- let c = () => void 0, l = !1, u = Z(void 0), d = null, m = null, h = null;
1512
- function g() {
1513
- h ||= setTimeout(async () => {
1514
- if (h = null, !(!P().escalated || !P().isOpen)) try {
1515
- let e = await j(t.apiUrl, t.apiKey, P().currentSessionId, $, r);
1516
- e.messages.length > 0 && F({ messages: e.messages.map(O) }), F({ escalated: e.escalated === !0 });
1932
+ let c = () => void 0, l = !1, u = Xe(void 0), d = null, m = null, h = null, g = null;
1933
+ function _() {
1934
+ g ||= setTimeout(async () => {
1935
+ if (g = null, !(!F().escalated || !F().isOpen)) try {
1936
+ let e = await M(t.apiUrl, t.apiKey, F().currentSessionId, $, r);
1937
+ e.messages.length > 0 && I({ messages: e.messages.map(j) }), I({ escalated: e.escalated === !0 });
1517
1938
  } catch {} finally {
1518
- P().escalated && P().isOpen && g();
1939
+ F().escalated && F().isOpen && _();
1519
1940
  }
1520
1941
  }, 1e4);
1521
1942
  }
@@ -1525,94 +1946,111 @@ function Fe(e) {
1525
1946
  console.warn("[BotIQ] Widget not authorised on this origin — skipped mount"), s.remove();
1526
1947
  return;
1527
1948
  }
1528
- u = Z(e.widgetLanguage), e.pageActions?.length && D.seed(e.pageActions);
1529
- let n = ke(t, e, u, {
1530
- onSend: I,
1531
- onToggle: R,
1532
- onScrollTop: A,
1533
- onRetry: L,
1534
- onNavigate: v,
1535
- onOpenConversation: E,
1536
- onNewConversation: T
1949
+ h = e, u = Xe(e.widgetLanguage), e.pageActions?.length && D.seed(e.pageActions);
1950
+ let n = qe(t, e, u, {
1951
+ onSend: oe,
1952
+ onToggle: H,
1953
+ onScrollTop: P,
1954
+ onRetry: ce,
1955
+ onNavigate: E,
1956
+ onOpenConversation: te,
1957
+ onNewConversation: A,
1958
+ onAttachImage: ie,
1959
+ onRemoveAttachment: ae,
1960
+ onBlockPageAction: V
1537
1961
  });
1538
- d = n, n.mount(s), c = re((e) => n.update(e)), n.update(P()), e.proactive?.enabled && (m = setTimeout(() => {
1539
- if (P().isOpen) return;
1540
- let t = {
1541
- role: "assistant",
1542
- content: e.proactive.message,
1543
- ts: Date.now()
1544
- };
1545
- F({
1546
- messages: [...P().messages, t],
1962
+ d = n, n.mount(s), c = re((e) => n.update(e)), n.update(F()), e.proactive?.enabled && (m = setTimeout(() => {
1963
+ if (F().isOpen) return;
1964
+ let t = w();
1965
+ I({
1966
+ messages: [{
1967
+ role: "assistant",
1968
+ content: e.proactive.message,
1969
+ ts: Date.now()
1970
+ }],
1547
1971
  view: "conversation",
1548
- unreadCount: P().unreadCount + 1
1972
+ currentSessionId: t,
1973
+ unreadCount: F().unreadCount + 1
1549
1974
  });
1550
- }, e.proactive.delayMs ?? 8e3)), a.length > 0 && k(i);
1975
+ }, e.proactive.delayMs ?? 8e3)), a.length > 0 && N(i);
1551
1976
  });
1552
- async function _() {
1553
- F({ conversations: await ee(t.apiUrl, t.apiKey, r) });
1977
+ let v = !1;
1978
+ async function T() {
1979
+ if (!v) {
1980
+ v = !0;
1981
+ try {
1982
+ I({ conversations: await O(t.apiUrl, t.apiKey, r) });
1983
+ } finally {
1984
+ v = !1;
1985
+ }
1986
+ }
1554
1987
  }
1555
- function v(e) {
1556
- F({ view: e }), e === "messages" && _();
1988
+ function E(e) {
1989
+ I({ view: e }), (e === "home" || e === "messages") && T();
1557
1990
  }
1558
- function T() {
1559
- F({
1991
+ function A() {
1992
+ I({
1560
1993
  view: "conversation",
1561
1994
  currentSessionId: w(),
1562
1995
  messages: [],
1563
1996
  escalated: !1
1564
- }), o = H();
1997
+ }), o = K();
1565
1998
  }
1566
- function E(e) {
1567
- F({
1999
+ function te(e) {
2000
+ I({
1568
2001
  view: "conversation",
1569
2002
  currentSessionId: e,
1570
2003
  messages: x(n, e)
1571
- }), o = H(), k(e);
2004
+ }), o = K(), N(e);
1572
2005
  }
1573
- function O(e) {
2006
+ function j(e) {
1574
2007
  let t = Date.parse(e.createdAt);
1575
2008
  return {
1576
2009
  role: e.role,
1577
2010
  content: e.content,
1578
- ...Number.isFinite(t) ? { ts: t } : {}
2011
+ ...Number.isFinite(t) ? { ts: t } : {},
2012
+ ...h?.richMessages && e.blocks?.length ? { blocks: e.blocks } : {}
1579
2013
  };
1580
2014
  }
1581
- async function k(e) {
1582
- let n = await j(t.apiUrl, t.apiKey, e, $, r);
1583
- n.messages.length > 0 && (o = U(n), F({ messages: n.messages.map(O) })), F({ escalated: n.escalated === !0 }), P().escalated && g();
2015
+ async function N(e) {
2016
+ let n = await M(t.apiUrl, t.apiKey, e, $, r);
2017
+ n.messages.length > 0 && (o = ye(n), I({ messages: n.messages.map(j) })), I({ escalated: n.escalated === !0 }), F().escalated && _();
1584
2018
  }
1585
- async function A() {
1586
- if (fe(o, P().loadingOlder)) {
1587
- F({ loadingOlder: !0 });
2019
+ async function P() {
2020
+ if (xe(o, F().loadingOlder)) {
2021
+ I({ loadingOlder: !0 });
1588
2022
  try {
1589
- let e = P().currentSessionId, n = await ne(t.apiUrl, t.apiKey, e, o.oldestCursor, $);
1590
- n.messages.length > 0 && F({ messages: [...n.messages.map(O), ...P().messages] }), o = de(o, n);
2023
+ let e = F().currentSessionId, n = await ne(t.apiUrl, t.apiKey, e, o.oldestCursor, $);
2024
+ n.messages.length > 0 && I({ messages: [...n.messages.map(j), ...F().messages] }), o = be(o, n);
1591
2025
  } finally {
1592
- F({ loadingOlder: !1 });
2026
+ I({ loadingOlder: !1 });
1593
2027
  }
1594
2028
  }
1595
2029
  }
1596
- let M = "";
1597
- async function N(e) {
1598
- let i = P().currentSessionId, a = x(n, i).length === 0 && P().messages.filter((e) => e.role === "user").length === 1;
1599
- F({ isLoading: !0 });
2030
+ let L = "", R = null, z = null;
2031
+ async function B(e, i, a) {
2032
+ let o = F().currentSessionId, s = x(n, o).length === 0 && F().messages.filter((e) => e.role === "user").length === 1;
2033
+ I({ isLoading: !0 });
1600
2034
  try {
1601
- let o = await te(t.apiUrl, t.apiKey, i, e, P().messages.filter((e) => !e.error), u, r), s = {
2035
+ let c = i && a ? [{
2036
+ url: i,
2037
+ mimeType: a
2038
+ }] : void 0, l = await ee(t.apiUrl, t.apiKey, o, e, F().messages.filter((e) => !e.error), u, r, c), f = {
1602
2039
  role: "assistant",
1603
- content: o.reply,
2040
+ content: l.reply,
1604
2041
  ts: Date.now(),
1605
- ...o.error ? { error: !0 } : {}
2042
+ ...l.error ? { error: !0 } : {},
2043
+ ...h?.richMessages && l.blocks?.length ? { blocks: l.blocks } : {}
1606
2044
  };
1607
- if (F({
1608
- messages: [...P().messages, s],
2045
+ if (I({
2046
+ messages: [...F().messages, f],
1609
2047
  isLoading: !1
1610
- }), o.error || S(n, i, [s]), !o.error && a && _(), o.error || j(t.apiUrl, t.apiKey, i, $, r).then((e) => {
1611
- F({ escalated: e.escalated === !0 }), P().escalated && g();
1612
- }), !o.error && o.pageAction && d) {
2048
+ }), l.error || S(n, o, [f]), !l.error && s && T(), l.error || M(t.apiUrl, t.apiKey, o, $, r).then((e) => {
2049
+ I({ escalated: e.escalated === !0 }), F().escalated && _();
2050
+ }), !l.error && l.pageAction && d) {
1613
2051
  let e = d;
1614
2052
  try {
1615
- let t = await Pe(o.pageAction, {
2053
+ let t = await tt(l.pageAction, {
1616
2054
  registry: D,
1617
2055
  confirm: (t) => e.confirmAction(t)
1618
2056
  });
@@ -1622,7 +2060,7 @@ function Fe(e) {
1622
2060
  content: t.status === "executed" ? u.actionDone : t.status === "cancelled" ? u.actionCancelled : t.status === "unknown" ? `⚠ ${t.message ?? u.actionUnavailable}` : `⚠ ${t.message ?? u.actionError}`,
1623
2061
  ts: Date.now()
1624
2062
  };
1625
- F({ messages: [...P().messages, r] }), S(n, i, [r]);
2063
+ I({ messages: [...F().messages, r] }), S(n, o, [r]);
1626
2064
  } catch {
1627
2065
  if (!d) return;
1628
2066
  let e = {
@@ -1630,38 +2068,128 @@ function Fe(e) {
1630
2068
  content: u.actionError,
1631
2069
  ts: Date.now()
1632
2070
  };
1633
- F({ messages: [...P().messages, e] }), S(n, i, [e]);
2071
+ I({ messages: [...F().messages, e] }), S(n, o, [e]);
1634
2072
  }
1635
2073
  }
1636
2074
  } catch {
1637
- F({ isLoading: !1 });
2075
+ I({ isLoading: !1 });
1638
2076
  }
1639
2077
  }
1640
- async function I(e) {
1641
- if (P().isLoading) return;
1642
- M = e;
1643
- let t = P().currentSessionId, r = {
2078
+ async function ie(e) {
2079
+ let t = se(e);
2080
+ if (!t.ok) {
2081
+ I({ uploadError: t.error });
2082
+ return;
2083
+ }
2084
+ I({ uploadError: null });
2085
+ try {
2086
+ let { blob: t, mimeType: n } = await le(e), r = F().pendingAttachment;
2087
+ r && URL.revokeObjectURL(r.previewUrl), I({
2088
+ pendingAttachment: {
2089
+ blob: t,
2090
+ previewUrl: URL.createObjectURL(t),
2091
+ mimeType: n
2092
+ },
2093
+ uploadError: null
2094
+ });
2095
+ } catch {
2096
+ I({ uploadError: u.imageUploadError });
2097
+ }
2098
+ }
2099
+ function ae() {
2100
+ let e = F().pendingAttachment;
2101
+ e && URL.revokeObjectURL(e.previewUrl), I({
2102
+ pendingAttachment: null,
2103
+ uploadError: null
2104
+ });
2105
+ }
2106
+ async function oe(e) {
2107
+ if (F().isLoading || F().isUploading) return;
2108
+ let i = F().pendingAttachment;
2109
+ if (!e && !i) return;
2110
+ I({ uploadError: null }), L = e, R = null, z = null;
2111
+ let a = F().currentSessionId, o = {
1644
2112
  role: "user",
1645
2113
  content: e,
1646
2114
  ts: Date.now()
1647
2115
  };
1648
- F({ messages: [...P().messages, r] }), S(n, t, [r]), await N(e);
2116
+ if (I({ messages: [...F().messages, o] }), S(n, a, [o]), i) {
2117
+ I({
2118
+ isUploading: !0,
2119
+ uploadError: null
2120
+ });
2121
+ let e = await k(t.apiUrl, t.apiKey, i.blob, r, u);
2122
+ if (I({ isUploading: !1 }), !e.ok) {
2123
+ I({ uploadError: e.error });
2124
+ let t = F().messages;
2125
+ I({ messages: t.slice(0, -1) });
2126
+ return;
2127
+ }
2128
+ R = e.url, z = i.mimeType, URL.revokeObjectURL(i.previewUrl), I({
2129
+ pendingAttachment: null,
2130
+ uploadError: null
2131
+ });
2132
+ }
2133
+ await B(e, R ?? void 0, z ?? void 0);
1649
2134
  }
1650
- function L() {
1651
- if (P().isLoading || !M) return;
1652
- let e = P().messages;
1653
- F({ messages: e.length > 0 && e[e.length - 1].error ? e.slice(0, -1) : e }), N(M);
2135
+ function ce() {
2136
+ if (F().isLoading || !L) return;
2137
+ let e = F().messages;
2138
+ I({ messages: e.length > 0 && e[e.length - 1].error ? e.slice(0, -1) : e }), B(L, R ?? void 0, z ?? void 0);
1654
2139
  }
1655
- function R() {
1656
- let e = !P().isOpen;
1657
- F({
2140
+ async function V(e) {
2141
+ if (!d) return;
2142
+ if (!D.has(e)) {
2143
+ let e = {
2144
+ role: "assistant",
2145
+ content: `⚠ ${u.actionUnavailable}`,
2146
+ ts: Date.now()
2147
+ };
2148
+ I({ messages: [...F().messages, e] });
2149
+ return;
2150
+ }
2151
+ let t = d;
2152
+ try {
2153
+ let n = await tt({
2154
+ actionName: e,
2155
+ params: {},
2156
+ reason: ""
2157
+ }, {
2158
+ registry: D,
2159
+ confirm: (e) => t.confirmAction(e)
2160
+ });
2161
+ if (!d) return;
2162
+ let r = {
2163
+ role: "assistant",
2164
+ content: n.status === "executed" ? u.actionDone : n.status === "cancelled" ? u.actionCancelled : n.status === "unknown" ? `⚠ ${n.message ?? u.actionUnavailable}` : `⚠ ${n.message ?? u.actionError}`,
2165
+ ts: Date.now()
2166
+ };
2167
+ I({ messages: [...F().messages, r] });
2168
+ } catch {
2169
+ if (!d) return;
2170
+ let e = {
2171
+ role: "assistant",
2172
+ content: u.actionError,
2173
+ ts: Date.now()
2174
+ };
2175
+ I({ messages: [...F().messages, e] });
2176
+ }
2177
+ }
2178
+ function H() {
2179
+ let e = !F().isOpen, t = F().view;
2180
+ I({
1658
2181
  isOpen: e,
1659
2182
  ...e ? { unreadCount: 0 } : {}
1660
- });
2183
+ }), e && t === "home" && T();
1661
2184
  }
1662
2185
  return () => {
1663
- l = !0, d = null, c(), m && clearTimeout(m), h &&= (clearTimeout(h), null), s.remove();
2186
+ l = !0, d = null, c(), m && clearTimeout(m), g &&= (clearTimeout(g), null);
2187
+ let e = F().pendingAttachment;
2188
+ e && URL.revokeObjectURL(e.previewUrl), I({
2189
+ pendingAttachment: null,
2190
+ uploadError: null
2191
+ }), s.remove();
1664
2192
  };
1665
2193
  }
1666
2194
  //#endregion
1667
- export { Fe as t };
2195
+ export { nt as t };