@locdo.tech/botiq-chat-sdk 0.6.0 → 0.8.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 A(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 j = {
286
331
  messages: [],
287
332
  hasMore: !1
288
333
  };
289
- function k(e) {
334
+ function M(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 N(e, t, n, r, i, a) {
295
340
  try {
296
341
  let o = new URLSearchParams({
297
342
  sessionId: n,
@@ -306,26 +351,112 @@ 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 j;
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(M) : [],
313
358
  hasMore: c.hasMore === !0,
314
359
  escalated: c.escalated === !0
315
360
  };
316
361
  } catch {
317
- return O;
362
+ return j;
318
363
  }
319
364
  }
320
- function j(e, t, n, r, i) {
321
- return A(e, t, n, void 0, r, i);
365
+ function P(e, t, n, r, i) {
366
+ return N(e, t, n, void 0, r, i);
322
367
  }
323
- function ne(e, t, n, r, i, a) {
324
- return A(e, t, n, r, i, a);
368
+ function ee(e, t, n, r, i, a) {
369
+ return N(e, t, n, r, i, a);
370
+ }
371
+ //#endregion
372
+ //#region src/core/stream.ts
373
+ async function te(e) {
374
+ if (typeof ReadableStream > "u") return A(e.apiUrl, e.apiKey, e.sessionId, e.message, e.history, e.strings, e.visitorId, e.attachments);
375
+ let t = D.getDefinitions(), n = {
376
+ sessionId: e.sessionId,
377
+ message: e.message,
378
+ history: e.history,
379
+ ...t.length ? { availableActions: t } : {},
380
+ ...e.visitorId ? { visitorId: e.visitorId } : {},
381
+ ...e.attachments?.length ? { attachments: e.attachments } : {}
382
+ }, r;
383
+ try {
384
+ r = await fetch(`${e.apiUrl}/widget/chat/stream`, {
385
+ method: "POST",
386
+ headers: {
387
+ "Content-Type": "application/json",
388
+ "X-Api-Key": e.apiKey,
389
+ ...e.visitorId ? { "X-Visitor-Id": e.visitorId } : {}
390
+ },
391
+ referrerPolicy: "no-referrer-when-downgrade",
392
+ body: JSON.stringify(n)
393
+ });
394
+ } catch {
395
+ return A(e.apiUrl, e.apiKey, e.sessionId, e.message, e.history, e.strings, e.visitorId, e.attachments);
396
+ }
397
+ if (!r.ok) return r.status === 401 ? {
398
+ reply: e.strings.errorAuth,
399
+ error: !0
400
+ } : r.status === 403 ? {
401
+ reply: e.strings.errorForbidden,
402
+ error: !0
403
+ } : r.status === 429 ? {
404
+ reply: e.strings.errorQuota,
405
+ error: !0
406
+ } : A(e.apiUrl, e.apiKey, e.sessionId, e.message, e.history, e.strings, e.visitorId, e.attachments);
407
+ if (!r.body) return {
408
+ reply: e.strings.errorMessage,
409
+ error: !0
410
+ };
411
+ try {
412
+ return await ne(r.body, e);
413
+ } catch {
414
+ return {
415
+ reply: e.strings.errorMessage,
416
+ error: !0
417
+ };
418
+ }
419
+ }
420
+ async function ne(e, t) {
421
+ let n = e.getReader(), r = new TextDecoder(), i = "", a = "", o = null;
422
+ outer: for (;;) {
423
+ let { done: e, value: s } = await n.read();
424
+ if (e) break;
425
+ i += r.decode(s, { stream: !0 });
426
+ let c = i.split("\n");
427
+ i = c.pop() ?? "";
428
+ for (let e of c) {
429
+ if (!e.startsWith("data: ")) continue;
430
+ let n = e.slice(6).trim();
431
+ if (!n) continue;
432
+ let r;
433
+ try {
434
+ r = JSON.parse(n);
435
+ } catch {
436
+ continue;
437
+ }
438
+ if (r.type === "token") {
439
+ let e = String(r.delta ?? "");
440
+ e && (a += e, t.onToken(e));
441
+ } else if (r.type === "done") {
442
+ o = r;
443
+ break outer;
444
+ } else if (r.type === "error") return {
445
+ reply: t.strings.errorMessage,
446
+ error: !0
447
+ };
448
+ }
449
+ }
450
+ if (!o) throw Error("SSE stream ended without done event");
451
+ return {
452
+ reply: a || t.strings.errorMessage,
453
+ pageAction: o.pageAction,
454
+ blocks: o.blocks
455
+ };
325
456
  }
326
457
  //#endregion
327
458
  //#region src/core/state.ts
328
- var M = {
459
+ var F = {
329
460
  messages: [],
330
461
  isLoading: !1,
331
462
  isOpen: !1,
@@ -334,26 +465,107 @@ var M = {
334
465
  unreadCount: 0,
335
466
  conversations: [],
336
467
  currentSessionId: "",
337
- escalated: !1
338
- }, N = /* @__PURE__ */ new Set();
339
- function P() {
340
- return M;
468
+ escalated: !1,
469
+ pendingAttachment: null,
470
+ isUploading: !1,
471
+ uploadError: null
472
+ }, I = /* @__PURE__ */ new Set();
473
+ function L() {
474
+ return F;
341
475
  }
342
- function F(e) {
343
- Object.assign(M, e);
476
+ function R(e) {
477
+ Object.assign(F, e);
344
478
  let t = {
345
- ...M,
346
- messages: [...M.messages],
347
- conversations: [...M.conversations]
479
+ ...F,
480
+ messages: [...F.messages],
481
+ conversations: [...F.conversations]
348
482
  };
349
- N.forEach((e) => e(t));
483
+ I.forEach((e) => e(t));
350
484
  }
351
485
  function re(e) {
352
- return N.add(e), () => N.delete(e);
486
+ return I.add(e), () => I.delete(e);
487
+ }
488
+ //#endregion
489
+ //#region src/core/upload.ts
490
+ var z = new Set([
491
+ "image/jpeg",
492
+ "image/png",
493
+ "image/webp"
494
+ ]), B = 25 * 1024 * 1024, ie = 5 * 1024 * 1024, V = 1600, ae = .85, oe = .65;
495
+ function se(e) {
496
+ return z.has(e);
497
+ }
498
+ function ce(e) {
499
+ return se(e.type) ? e.size > B ? {
500
+ ok: !1,
501
+ error: "Image too large (max 25 MB before compression)."
502
+ } : { ok: !0 } : {
503
+ ok: !1,
504
+ error: "Unsupported image type. Please use JPEG, PNG, or WebP."
505
+ };
506
+ }
507
+ function le(e, t) {
508
+ let n = Math.max(e, t);
509
+ if (n <= V) return {
510
+ width: e,
511
+ height: t
512
+ };
513
+ let r = V / n;
514
+ return {
515
+ width: Math.floor(e * r),
516
+ height: Math.floor(t * r)
517
+ };
518
+ }
519
+ function ue(e) {
520
+ let t = ce(e);
521
+ return t.ok ? new Promise((t, n) => {
522
+ let r = new Image(), i = URL.createObjectURL(e);
523
+ r.onload = () => {
524
+ URL.revokeObjectURL(i);
525
+ let { width: a, height: o } = le(r.naturalWidth, r.naturalHeight), s = document.createElement("canvas");
526
+ s.width = a, s.height = o;
527
+ let c = s.getContext("2d");
528
+ if (!c) {
529
+ n(/* @__PURE__ */ Error("Canvas 2D context not available"));
530
+ return;
531
+ }
532
+ c.drawImage(r, 0, 0, a, o);
533
+ let l = e.type === "image/webp" ? "image/webp" : "image/jpeg";
534
+ s.toBlob((e) => {
535
+ if (!e) {
536
+ n(/* @__PURE__ */ Error("Canvas toBlob returned null"));
537
+ return;
538
+ }
539
+ if (e.size <= ie) {
540
+ t({
541
+ blob: e,
542
+ mimeType: l
543
+ });
544
+ return;
545
+ }
546
+ s.toBlob((e) => {
547
+ if (!e) {
548
+ n(/* @__PURE__ */ Error("Canvas toBlob (fallback quality) returned null"));
549
+ return;
550
+ }
551
+ if (e.size > ie) {
552
+ n(/* @__PURE__ */ Error("Image too large after compression (> 5 MB). Try a smaller image."));
553
+ return;
554
+ }
555
+ t({
556
+ blob: e,
557
+ mimeType: l
558
+ });
559
+ }, l, oe);
560
+ }, l, ae);
561
+ }, r.onerror = () => {
562
+ URL.revokeObjectURL(i), n(/* @__PURE__ */ Error("Failed to load image"));
563
+ }, r.src = i;
564
+ }) : Promise.reject(Error(t.error));
353
565
  }
354
566
  //#endregion
355
567
  //#region src/core/styles.ts
356
- var I = {
568
+ var de = {
357
569
  inter: {
358
570
  url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
359
571
  family: "'Inter', system-ui, -apple-system, sans-serif"
@@ -375,21 +587,21 @@ var I = {
375
587
  family: "'Playfair Display', Georgia, serif"
376
588
  }
377
589
  };
378
- function L(e, t) {
590
+ function H(e, t) {
379
591
  if (!t) return e;
380
592
  let n = t.angle ?? 135;
381
593
  return t.type === "linear" ? `linear-gradient(${n}deg, ${t.from}, ${t.to})` : `radial-gradient(circle, ${t.from}, ${t.to})`;
382
594
  }
383
- function R(e) {
595
+ function fe(e) {
384
596
  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
597
  }
386
- function ie(e) {
598
+ function pe(e) {
387
599
  let t = e?.bubbleOpen ?? "none", n = e?.typingIndicator ?? "dots-bounce";
388
600
  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
601
  }
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; }" : "";
602
+ var me = "\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", he = "\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";
603
+ function ge(e) {
604
+ let { colors: t, layout: n, font: r, gradient: i, animation: a, customCSS: o, whiteLabel: s } = e, c = de[r] ?? de.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), _ = t.primary.length === 4 ? "#" + t.primary[1].repeat(2) + t.primary[2].repeat(2) + t.primary[3].repeat(2) : t.primary, v = s ? ".botiq-badge { display: none !important; }" : "";
393
605
  return `
394
606
  @import url('${c.url}');
395
607
 
@@ -407,6 +619,7 @@ function oe(e) {
407
619
  --color-bg: ${t.background};
408
620
  --color-text: ${t.text};
409
621
  --color-input-bg: ${t.inputBackground};
622
+ --hover-bg: ${_}1A;
410
623
  --gray-50: #1A1A1A;
411
624
  --gray-100: #222222;
412
625
  --gray-200: #2D2D2D;
@@ -564,12 +777,17 @@ function oe(e) {
564
777
  border: none;
565
778
  color: var(--gray-400);
566
779
  font: inherit;
567
- font-size: 12px;
568
- padding: 10px;
780
+ font-size: 11px;
781
+ padding: 8px 10px 10px;
569
782
  cursor: pointer;
570
783
  transition: color .15s;
784
+ display: flex;
785
+ flex-direction: column;
786
+ align-items: center;
787
+ gap: 3px;
571
788
  }
572
789
 
790
+ .nav-btn svg { width: 18px; height: 18px; fill: currentColor; }
573
791
  .nav-btn:hover { color: var(--color-text); }
574
792
  .nav-btn.active { color: var(--color-primary); }
575
793
 
@@ -577,10 +795,11 @@ function oe(e) {
577
795
  .msg-list { flex: 1; overflow-y: auto; }
578
796
 
579
797
  .msg-list-item {
580
- display: grid;
798
+ display: flex;
799
+ align-items: flex-start;
800
+ gap: 10px;
581
801
  width: 100%;
582
802
  text-align: left;
583
- gap: 2px;
584
803
  padding: 12px 16px;
585
804
  background: transparent;
586
805
  border: none;
@@ -589,7 +808,27 @@ function oe(e) {
589
808
  font: inherit;
590
809
  }
591
810
 
592
- .msg-list-item:hover { background: var(--gray-50); }
811
+ .msg-list-item:hover { background: var(--hover-bg); }
812
+
813
+ .msg-list-avatar {
814
+ flex-shrink: 0;
815
+ width: 36px;
816
+ height: 36px;
817
+ border-radius: 50%;
818
+ overflow: hidden;
819
+ background: var(--color-primary);
820
+ display: flex;
821
+ align-items: center;
822
+ justify-content: center;
823
+ }
824
+
825
+ .msg-list-body {
826
+ flex: 1;
827
+ min-width: 0;
828
+ display: flex;
829
+ flex-direction: column;
830
+ gap: 2px;
831
+ }
593
832
 
594
833
  .msg-list-title {
595
834
  color: var(--color-text);
@@ -607,7 +846,7 @@ function oe(e) {
607
846
 
608
847
  .msg-list-time { color: var(--gray-400); font-size: 10px; }
609
848
 
610
- .msg-list-badge { color: #F97316; font-size: 10px; }
849
+ .msg-list-badge { color: var(--color-primary); font-size: 10px; }
611
850
 
612
851
  .msg-list-empty {
613
852
  padding: 24px;
@@ -630,33 +869,124 @@ function oe(e) {
630
869
 
631
870
  .msg-list-new:hover { background: var(--color-primary); color: #fff; }
632
871
 
633
- /* ── Home panel ─────────────────────────── */
634
- .home-greeting {
635
- font-size: 20px;
636
- font-weight: 600;
637
- color: var(--color-text);
638
- padding: 24px 18px 8px;
872
+ /* ── Home panel hero banner ─────────────── */
873
+ .home-hero {
874
+ background: var(--color-primary);
875
+ padding: 22px 18px 20px;
876
+ flex-shrink: 0;
877
+ }
878
+
879
+ .home-hero-row {
880
+ display: flex;
881
+ align-items: center;
882
+ gap: 12px;
883
+ margin-bottom: 14px;
884
+ }
885
+
886
+ .home-hero-avatar {
887
+ width: 48px;
888
+ height: 48px;
889
+ border-radius: 50%;
890
+ overflow: hidden;
891
+ flex-shrink: 0;
892
+ background: rgba(255,255,255,.2);
639
893
  }
640
894
 
895
+ .home-hero-name {
896
+ font-weight: 700;
897
+ font-size: 15px;
898
+ color: #fff;
899
+ }
900
+
901
+ .home-hero-status {
902
+ font-size: 12px;
903
+ color: rgba(255,255,255,.8);
904
+ margin-top: 2px;
905
+ }
906
+
907
+ .home-hero-greeting {
908
+ font-size: 15px;
909
+ line-height: 1.5;
910
+ color: rgba(255,255,255,.92);
911
+ }
912
+
913
+ /* ── Home panel CTA ─────────────────────── */
641
914
  .home-start {
642
- margin: 8px 18px;
915
+ margin: 14px 18px;
643
916
  padding: 14px;
644
917
  border-radius: 12px;
645
- border: 1px solid var(--gray-200);
646
- background: var(--gray-50);
647
- color: var(--color-text);
648
- text-align: left;
918
+ border: 1.5px solid var(--color-primary);
919
+ background: transparent;
920
+ color: var(--color-primary);
921
+ text-align: center;
649
922
  cursor: pointer;
650
923
  font: inherit;
651
924
  font-size: 14px;
652
- transition: background .15s, border-color .15s;
925
+ font-weight: 600;
926
+ width: calc(100% - 36px);
927
+ transition: background .15s, color .15s;
928
+ }
929
+
930
+ .home-start:hover { background: var(--color-primary); color: #fff; }
931
+
932
+ /* ── Home recent conversations ──────────────── */
933
+ .home-recent-label {
934
+ margin: 16px 18px 6px;
935
+ font-size: 11px;
936
+ font-weight: 600;
937
+ text-transform: uppercase;
938
+ letter-spacing: .06em;
939
+ color: var(--gray-400);
940
+ }
941
+
942
+ .home-recent-item {
943
+ display: flex;
944
+ align-items: center;
945
+ gap: 10px;
946
+ width: 100%;
947
+ padding: 10px 18px;
948
+ background: transparent;
949
+ border: none;
950
+ border-bottom: 1px solid var(--gray-100);
951
+ cursor: pointer;
952
+ font: inherit;
953
+ text-align: left;
954
+ }
955
+
956
+ .home-recent-item:hover { background: var(--hover-bg); }
957
+
958
+ .home-recent-avatar {
959
+ flex-shrink: 0;
960
+ width: 32px;
961
+ height: 32px;
962
+ border-radius: 50%;
963
+ overflow: hidden;
964
+ background: var(--color-primary);
965
+ display: flex;
966
+ align-items: center;
967
+ justify-content: center;
968
+ }
969
+
970
+ .home-recent-body { flex: 1; min-width: 0; }
971
+
972
+ .home-recent-title {
973
+ color: var(--color-text);
974
+ font-size: 13px;
975
+ font-weight: 500;
976
+ white-space: nowrap;
977
+ overflow: hidden;
978
+ text-overflow: ellipsis;
653
979
  }
654
980
 
655
- .home-start:hover { background: var(--gray-100); border-color: var(--color-primary); }
981
+ .home-recent-preview {
982
+ color: var(--gray-400);
983
+ font-size: 12px;
984
+ white-space: nowrap;
985
+ overflow: hidden;
986
+ text-overflow: ellipsis;
987
+ }
656
988
 
657
- .home-status { margin: 8px 18px; font-size: 12px; }
658
- .home-status--online { color: #16A34A; }
659
- .home-status--offline { color: var(--gray-400); }
989
+ .home-recent-time { color: var(--gray-400); font-size: 10px; flex-shrink: 0; }
660
990
 
661
991
  /* ── Messages ───────────────────────────── */
662
992
  .messages {
@@ -910,9 +1240,11 @@ function oe(e) {
910
1240
  color: var(--color-text);
911
1241
  opacity: 0.4;
912
1242
  text-decoration: none;
913
- margin-top: 6px;
1243
+ padding: 5px 0 7px;
914
1244
  letter-spacing: 0.02em;
915
1245
  transition: opacity .15s;
1246
+ background: var(--color-bg);
1247
+ border-top: 1px solid var(--gray-100);
916
1248
  }
917
1249
  .botiq-badge:hover { opacity: 0.85; }
918
1250
  .botiq-badge-name { font-weight: 600; color: var(--color-primary); }
@@ -957,6 +1289,72 @@ function oe(e) {
957
1289
  .send-btn:disabled { background: var(--gray-200); cursor: not-allowed; filter: none; }
958
1290
  .send-btn svg { width: 18px; height: 18px; fill: #fff; }
959
1291
 
1292
+ /* ── Attach Button ──────────────────────── */
1293
+ .attach-btn {
1294
+ width: 34px; height: 38px;
1295
+ background: transparent; border: none; cursor: pointer;
1296
+ color: var(--gray-400); flex-shrink: 0;
1297
+ display: flex; align-items: center; justify-content: center;
1298
+ border-radius: 8px; transition: color .15s, background .15s;
1299
+ }
1300
+ .attach-btn:hover { color: var(--color-primary); background: rgba(249,115,22,.08); }
1301
+ .attach-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1302
+
1303
+ /* ── Attach Preview Slot ────────────────── */
1304
+ .attach-preview-slot {
1305
+ margin-bottom: 6px;
1306
+ }
1307
+
1308
+ .attach-preview {
1309
+ display: flex;
1310
+ align-items: center;
1311
+ gap: 8px;
1312
+ padding: 6px 8px;
1313
+ background: var(--gray-50);
1314
+ border: 1px solid var(--gray-200);
1315
+ border-radius: 10px;
1316
+ position: relative;
1317
+ }
1318
+
1319
+ .attach-preview-img {
1320
+ width: 48px;
1321
+ height: 48px;
1322
+ object-fit: cover;
1323
+ border-radius: 6px;
1324
+ flex-shrink: 0;
1325
+ }
1326
+
1327
+ .attach-preview-img--uploading {
1328
+ opacity: 0.5;
1329
+ }
1330
+
1331
+ .attach-preview-status {
1332
+ font-size: 12px;
1333
+ color: var(--gray-400);
1334
+ flex: 1;
1335
+ }
1336
+
1337
+ .attach-preview-error {
1338
+ font-size: 12px;
1339
+ color: #DC2626;
1340
+ flex: 1;
1341
+ }
1342
+
1343
+ .attach-preview-remove {
1344
+ width: 22px; height: 22px;
1345
+ border-radius: 50%;
1346
+ background: var(--gray-200);
1347
+ border: none;
1348
+ cursor: pointer;
1349
+ color: var(--color-text);
1350
+ font-size: 14px;
1351
+ line-height: 1;
1352
+ display: flex; align-items: center; justify-content: center;
1353
+ flex-shrink: 0;
1354
+ transition: background .15s;
1355
+ }
1356
+ .attach-preview-remove:hover { background: #DC2626; color: #fff; }
1357
+
960
1358
  .emoji-btn {
961
1359
  width: 34px; height: 38px;
962
1360
  background: transparent; border: none; cursor: pointer;
@@ -977,10 +1375,11 @@ function oe(e) {
977
1375
  }
978
1376
  .emoji-item:hover { background: var(--gray-100); }
979
1377
 
980
- ${_}
981
- ${ie(a)}
982
- ${o ? R(o) : ""}
983
- ${ae}
1378
+ ${v}
1379
+ ${pe(a)}
1380
+ ${o ? fe(o) : ""}
1381
+ ${he}
1382
+ ${me}
984
1383
  @media (max-width: 480px) {
985
1384
  :host { bottom: 0; right: 0; left: 0; }
986
1385
  .chat-window {
@@ -997,18 +1396,18 @@ ${ae}
997
1396
  }
998
1397
  //#endregion
999
1398
  //#region src/core/timeago.ts
1000
- function z(e) {
1399
+ function U(e) {
1001
1400
  return String(e).padStart(2, "0");
1002
1401
  }
1003
- function B(e, t, n) {
1402
+ function W(e, t, n) {
1004
1403
  let r = Math.max(0, Math.floor((t - e) / 1e3));
1005
1404
  if (r < 60) return n === "vi" ? "vừa xong" : "just now";
1006
1405
  let i = Math.floor(r / 60);
1007
1406
  if (i < 60) return n === "vi" ? `${i} phút` : `${i} min`;
1008
1407
  let a = new Date(e);
1009
- return `${z(a.getHours())}:${z(a.getMinutes())}`;
1408
+ return `${U(a.getHours())}:${U(a.getMinutes())}`;
1010
1409
  }
1011
- function se(e, t, n) {
1410
+ function _e(e, t, n) {
1012
1411
  let r = (e) => {
1013
1412
  let t = new Date(e);
1014
1413
  return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
@@ -1016,85 +1415,111 @@ function se(e, t, n) {
1016
1415
  if (i <= 0) return n === "vi" ? "Hôm nay" : "Today";
1017
1416
  if (i === 1) return n === "vi" ? "Hôm qua" : "Yesterday";
1018
1417
  let a = new Date(e);
1019
- return `${z(a.getDate())}/${z(a.getMonth() + 1)}/${a.getFullYear()}`;
1418
+ return `${U(a.getDate())}/${U(a.getMonth() + 1)}/${a.getFullYear()}`;
1020
1419
  }
1021
1420
  //#endregion
1022
1421
  //#region src/core/views.ts
1023
- function V(e) {
1422
+ function G(e) {
1024
1423
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1025
1424
  }
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}
1425
+ function ve(e, t, n, r = "") {
1426
+ let i = `<button class="msg-list-new" type="button">+ ${G(t.newConversation)}</button>`;
1427
+ return e.length === 0 ? `<div class="msg-list-empty">${G(t.noConversations)}</div>${i}` : `<div class="msg-list">${e.map((e) => {
1428
+ 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>` : "";
1429
+ return `<button class="msg-list-item" type="button" data-session="${G(e.sessionId)}">
1430
+ ${r ? `<div class="msg-list-avatar">${r}</div>` : ""}
1431
+ <div class="msg-list-body">
1432
+ <span class="msg-list-title">${G(e.title)}</span>
1433
+ <span class="msg-list-preview">${G(e.preview)}</span>
1434
+ <span class="msg-list-time">${G(a)}</span>${o}
1435
+ </div>
1034
1436
  </button>`;
1035
- }).join("")}</div>${r}`;
1036
- }
1037
- function le(e, t) {
1038
- let n = e.status !== "offline", r = n ? t.statusOnline : t.statusPaused;
1437
+ }).join("")}</div>${i}`;
1438
+ }
1439
+ function ye(e, t, n = [], r = "", i = Date.now()) {
1440
+ let a = e.status === "offline" ? t.statusPaused : t.statusOnline, o = G(e.design.content.greeting || t.homeGreeting), s = "";
1441
+ if (n.length > 0) {
1442
+ let e = n.slice(0, 3).map((e) => {
1443
+ let t = e.lastMessageAt ? Date.parse(e.lastMessageAt) : i, n = Number.isFinite(t) ? W(t, i, "vi") : "";
1444
+ return `<button class="home-recent-item" type="button" data-session="${G(e.sessionId)}">
1445
+ ${r ? `<div class="home-recent-avatar">${r}</div>` : ""}
1446
+ <div class="home-recent-body">
1447
+ <div class="home-recent-title">${G(e.title)}</div>
1448
+ <div class="home-recent-preview">${G(e.preview)}</div>
1449
+ </div>
1450
+ <span class="home-recent-time">${G(n)}</span>
1451
+ </button>`;
1452
+ }).join("");
1453
+ s = `<div class="home-recent-label">${G(t.recentMessages)}</div>${e}`;
1454
+ }
1039
1455
  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>
1456
+ <div class="home-hero">
1457
+ <div class="home-hero-row">
1458
+ ${r ? `<div class="home-hero-avatar">${r}</div>` : ""}
1459
+ <div>
1460
+ <div class="home-hero-name">${G(e.name)}</div>
1461
+ <div class="home-hero-status">● ${G(a)}</div>
1462
+ </div>
1463
+ </div>
1464
+ <div class="home-hero-greeting">${o}</div>
1465
+ </div>
1466
+ <button class="home-start" type="button">${G(t.startConversation)}</button>
1467
+ ${s}
1043
1468
  `;
1044
1469
  }
1045
1470
  //#endregion
1046
1471
  //#region src/core/assets.ts
1047
- var ue = "data:image/png;base64,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";
1472
+ var be = "data:image/png;base64,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";
1048
1473
  //#endregion
1049
1474
  //#region src/core/history-pager.ts
1050
- function H() {
1475
+ function K() {
1051
1476
  return {
1052
1477
  oldestCursor: null,
1053
1478
  hasMore: !1,
1054
1479
  cursorReady: !1
1055
1480
  };
1056
1481
  }
1057
- function U(e) {
1482
+ function xe(e) {
1058
1483
  return {
1059
1484
  oldestCursor: e.messages[0]?.id ?? null,
1060
1485
  hasMore: e.hasMore,
1061
1486
  cursorReady: e.messages.length > 0
1062
1487
  };
1063
1488
  }
1064
- function de(e, t) {
1489
+ function Se(e, t) {
1065
1490
  return {
1066
1491
  oldestCursor: t.messages[0]?.id ?? e.oldestCursor,
1067
1492
  hasMore: t.hasMore,
1068
1493
  cursorReady: e.cursorReady
1069
1494
  };
1070
1495
  }
1071
- function fe(e, t) {
1496
+ function Ce(e, t) {
1072
1497
  return e.cursorReady && e.hasMore && !t && e.oldestCursor !== null;
1073
1498
  }
1074
- function pe(e, t, n) {
1499
+ function we(e, t, n) {
1075
1500
  return e + (n - t);
1076
1501
  }
1077
1502
  //#endregion
1078
1503
  //#region src/core/confirm-dialog.ts
1079
- function W(e) {
1504
+ function q(e) {
1080
1505
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1081
1506
  }
1082
- function me(e, t) {
1507
+ function Te(e, t) {
1083
1508
  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
1509
  return new Promise((o) => {
1085
1510
  let s = document.createElement("div");
1086
1511
  s.className = "botiq-confirm-overlay", s.innerHTML = `
1087
1512
  <div class="botiq-confirm-dialog" role="dialog" aria-modal="true">
1088
- <div class="botiq-confirm-header">${W(t.title)}</div>
1513
+ <div class="botiq-confirm-header">${q(t.title)}</div>
1089
1514
  <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>
1515
+ <div class="botiq-confirm-reason">${q(t.reason)}</div>
1516
+ <pre class="botiq-confirm-params">${q(t.paramsDisplay)}</pre>
1092
1517
  </div>
1093
1518
  <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>
1519
+ <button class="botiq-confirm-yes" data-risk="${t.riskLevel}" type="button">${q(n)}</button>
1520
+ <button class="botiq-confirm-no" type="button">${q(r)}</button>
1096
1521
  </div>
1097
- ${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${W(i)}<span class="botiq-confirm-secs">${t.autoConfirmAfterSec}</span>${W(a)}</div>` : ""}
1522
+ ${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${q(i)}<span class="botiq-confirm-secs">${t.autoConfirmAfterSec}</span>${q(a)}</div>` : ""}
1098
1523
  </div>`, e.appendChild(s);
1099
1524
  let c = !1, l = null, u = (t) => {
1100
1525
  c || (c = !0, l && clearInterval(l), e.removeChild(s), o(t));
@@ -1109,15 +1534,15 @@ function me(e, t) {
1109
1534
  }
1110
1535
  //#endregion
1111
1536
  //#region src/core/emoji.ts
1112
- function he(e, t) {
1537
+ function Ee(e, t) {
1113
1538
  let n = e.selectionStart ?? e.value.length, r = e.selectionEnd ?? e.value.length;
1114
1539
  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
1540
  }
1116
- var G = null;
1117
- async function ge() {
1118
- return G || (G = (await import("./emoji-data-DWiYsBZI.js")).EMOJIS, G);
1541
+ var J = null;
1542
+ async function De() {
1543
+ return J || (J = (await import("./emoji-data-DWiYsBZI.js")).EMOJIS, J);
1119
1544
  }
1120
- function _e(e) {
1545
+ function Oe(e) {
1121
1546
  let t = document.createElement("button");
1122
1547
  t.type = "button", t.className = "emoji-btn", t.setAttribute("aria-label", "Emoji"), t.textContent = "🙂";
1123
1548
  let n = document.createElement("div");
@@ -1125,7 +1550,7 @@ function _e(e) {
1125
1550
  let r = !1;
1126
1551
  return t.addEventListener("click", async () => {
1127
1552
  if (!r) {
1128
- let t = await ge();
1553
+ let t = await De();
1129
1554
  for (let r of t) {
1130
1555
  let t = document.createElement("button");
1131
1556
  t.type = "button", t.className = "emoji-item", t.textContent = r, t.addEventListener("click", () => {
@@ -1141,45 +1566,95 @@ function _e(e) {
1141
1566
  };
1142
1567
  }
1143
1568
  //#endregion
1569
+ //#region src/core/blocks-renderer.ts
1570
+ function Y(e) {
1571
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1572
+ }
1573
+ function X(e) {
1574
+ return /^https?:\/\//i.test(e) ? e : "";
1575
+ }
1576
+ function ke(e) {
1577
+ let t = Y(e.label), n = Y(e.value), r = Y(e.action);
1578
+ if (e.action === "url") {
1579
+ let n = X(e.value);
1580
+ return n ? `<a class="biq-btn biq-btn--url" href="${Y(n)}" target="_blank" rel="noopener noreferrer">${t}</a>` : "";
1581
+ }
1582
+ return `<button class="biq-btn biq-btn--${r}" type="button" data-action="${r}" data-value="${n}">${t}</button>`;
1583
+ }
1584
+ function Ae(e) {
1585
+ let t = `${e.imageUrl ? (() => {
1586
+ let t = X(e.imageUrl);
1587
+ return t ? `<img class="biq-card-img" src="${Y(t)}" alt="" loading="lazy" />` : "";
1588
+ })() : ""}${`<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(ke).join("")}</div>` : ""}`;
1589
+ if (e.url) {
1590
+ let n = X(e.url);
1591
+ if (n) return `<a class="biq-card biq-card--link" href="${Y(n)}" target="_blank" rel="noopener noreferrer">${t}</a>`;
1592
+ }
1593
+ return `<div class="biq-card">${t}</div>`;
1594
+ }
1595
+ function je(e) {
1596
+ return `<div class="biq-carousel">${e.cards.map(Ae).join("")}</div>`;
1597
+ }
1598
+ function Me(e) {
1599
+ return `<div class="biq-quick-replies">${e.replies.map((e) => {
1600
+ let t = Y(e.label);
1601
+ return `<button class="biq-qr-pill" type="button" data-qr="1" data-value="${Y(e.value)}">${t}</button>`;
1602
+ }).join("")}</div>`;
1603
+ }
1604
+ function Ne(e) {
1605
+ return `<div class="biq-buttons">${e.buttons.map(ke).join("")}</div>`;
1606
+ }
1607
+ function Pe(e) {
1608
+ return !e || e.length === 0 ? "" : e.map((e) => {
1609
+ switch (e.type) {
1610
+ case "card": return Ae(e);
1611
+ case "carousel": return je(e);
1612
+ case "quick_replies": return Me(e);
1613
+ case "buttons": return Ne(e);
1614
+ default: return "";
1615
+ }
1616
+ }).join("");
1617
+ }
1618
+ //#endregion
1144
1619
  //#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>`;
1620
+ var Fe = "https://bot-q-frontend.vercel.app/", Ie = "<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>", Le = "<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>", Re = `<img src="${be}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%" />`, ze = "<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>", Be = "<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>", Ve = "<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>", He = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\"/></svg>", Ue = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z\"/></svg>";
1621
+ function We(e) {
1622
+ if (!e || e.type === "icon") return Re;
1623
+ 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
1624
  if (e.type === "initials") {
1150
1625
  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>`;
1626
+ 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
1627
  }
1153
- return e.type === "image" ? `<img src="${q(e.value)}" style="width:100%;height:100%;object-fit:cover;border-radius:50%" alt="" />` : K;
1628
+ return e.type === "image" ? `<img src="${Z(e.value)}" style="width:100%;height:100%;object-fit:cover;border-radius:50%" alt="" />` : Re;
1154
1629
  }
1155
- function q(e) {
1630
+ function Z(e) {
1156
1631
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1157
1632
  }
1158
- function J(e) {
1633
+ function Q(e) {
1159
1634
  let t = [];
1160
1635
  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
1636
  }
1162
- function we(e) {
1637
+ function Ge(e) {
1163
1638
  return /^\|[\s\-:|]+\|$/.test(e);
1164
1639
  }
1165
- function Y(e) {
1640
+ function Ke(e) {
1166
1641
  return e.replace(/^\|/, "").replace(/\|$/, "").split("|").map((e) => e.trim());
1167
1642
  }
1168
- function Te(e, t) {
1169
- return e ? `<div class="handoff-banner">⏳ ${q(t.waitingForHuman)}</div>` : "";
1643
+ function qe(e, t) {
1644
+ return e ? `<div class="handoff-banner">⏳ ${Z(t.waitingForHuman)}</div>` : "";
1170
1645
  }
1171
- function Ee(e) {
1646
+ function Je(e) {
1172
1647
  return e <= 0 ? "" : `<span class="unread-badge">${e > 9 ? "9+" : String(e)}</span>`;
1173
1648
  }
1174
- function De(e) {
1175
- let t = q(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
1649
+ function Ye(e) {
1650
+ let t = Z(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
1176
1651
  for (; r < t.length;) {
1177
1652
  let e = t[r].trim();
1178
1653
  if (e.startsWith("|") && e.endsWith("|")) {
1179
1654
  let e = [];
1180
1655
  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);
1656
+ let i = e.findIndex(Ge), a = i > 0 ? e.slice(0, i) : [], o = i >= 0 ? e.slice(i + 1) : e, s = "<table>";
1657
+ a.length > 0 && (s += "<thead>" + a.map((e) => "<tr>" + Ke(e).map((e) => `<th>${Q(e)}</th>`).join("") + "</tr>").join("") + "</thead>"), o.length > 0 && (s += "<tbody>" + o.map((e) => "<tr>" + Ke(e).map((e) => `<td>${Q(e)}</td>`).join("") + "</tr>").join("") + "</tbody>"), s += "</table>", n.push(s);
1183
1658
  continue;
1184
1659
  }
1185
1660
  if (/^-{3,}$/.test(e)) {
@@ -1189,67 +1664,67 @@ function De(e) {
1189
1664
  let i = e.match(/^(#{1,6})\s+(.*)$/);
1190
1665
  if (i) {
1191
1666
  let e = i[1].length;
1192
- n.push(`<h${e}>${J(i[2])}</h${e}>`), r++;
1667
+ n.push(`<h${e}>${Q(i[2])}</h${e}>`), r++;
1193
1668
  continue;
1194
1669
  }
1195
1670
  if (/^[-*]\s/.test(e)) {
1196
1671
  let e = [];
1197
1672
  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>");
1673
+ n.push("<ul>" + e.map((e) => `<li>${Q(e)}</li>`).join("") + "</ul>");
1199
1674
  continue;
1200
1675
  }
1201
1676
  if (/^\d+\.\s/.test(e)) {
1202
1677
  let e = [];
1203
1678
  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>");
1679
+ n.push("<ol>" + e.map((e) => `<li>${Q(e)}</li>`).join("") + "</ol>");
1205
1680
  continue;
1206
1681
  }
1207
1682
  if (e === "") {
1208
1683
  n.push("<br>"), r++;
1209
1684
  continue;
1210
1685
  }
1211
- n.push(J(e)), n.push("<br>"), r++;
1686
+ n.push(Q(e)), n.push("<br>"), r++;
1212
1687
  }
1213
1688
  for (; n.length > 0 && n[n.length - 1] === "<br>";) n.pop();
1214
1689
  return n.join("");
1215
1690
  }
1216
- function Oe(e, t) {
1217
- let { strings: n, content: r, botName: i, lang: a, now: o, contactInfo: s } = t;
1691
+ function Xe(e, t) {
1692
+ let { strings: n, content: r, botName: i, lang: a, now: o, contactInfo: s, richMessages: c } = t;
1218
1693
  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>` : "";
1694
+ let e = r.suggestionChips.length > 0 ? `<div class="chips">${r.suggestionChips.map((e) => `<button class="chip">${Z(e)}</button>`).join("")}</div>` : "";
1220
1695
  return `
1221
1696
  <div class="empty-state">
1222
- ${Se}
1223
- <span class="greeting">${q(r.greeting || n.defaultGreeting)}</span>
1697
+ ${Be}
1698
+ <span class="greeting">${Z(r.greeting || n.defaultGreeting)}</span>
1224
1699
  ${e}
1225
1700
  </div>
1226
1701
  `;
1227
1702
  }
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) {
1703
+ let l = "", u = e.messages.map((e) => {
1704
+ let t = e.ts ?? o, r = _e(t, o, a), u = r === l ? "" : `<div class="day-sep"><span>${Z(r)}</span></div>`;
1705
+ l = r;
1706
+ let d = e.role === "assistant" ? Ye(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">${Pe(e.blocks)}</div>` : "";
1707
+ return `${u}<div class="${`message ${e.role}${e.error ? " error" : ""}`}"><div class="message-bubble">${d}</div>${p}${m}${f}${h}</div>`;
1708
+ }).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>" : "";
1709
+ return d + u + f;
1710
+ }
1711
+ function Ze(e, t, n, r) {
1712
+ 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 = We(a.avatar), w = null;
1713
+ function T(e) {
1239
1714
  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>
1715
+ let w = document.createElement("style");
1716
+ w.textContent = ge(a), o.appendChild(w), s = document.createElement("button"), s.className = "bubble", s.setAttribute("aria-label", n.ariaOpenChat), s.innerHTML = `
1717
+ <span class="icon-chat">${Ie}</span>
1718
+ <span class="icon-close">${Le}</span>
1244
1719
  <span class="unread-badge-slot"></span>
1245
1720
  `, 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
1721
  <div class="chat-header">
1247
- <div class="avatar">${Ce(a.avatar)}</div>
1722
+ <div class="avatar">${We(a.avatar)}</div>
1248
1723
  <div class="header-text">
1249
- <span class="bot-name">${q(i)}</span>
1250
- <span class="bot-status">${q(n.statusOnline)}</span>
1724
+ <span class="bot-name">${Z(i)}</span>
1725
+ <span class="bot-status">${Z(t.status === "offline" ? n.statusPaused : n.statusOnline)}</span>
1251
1726
  </div>
1252
- <button class="close-btn" aria-label="${q(n.ariaCloseChat)}">×</button>
1727
+ <button class="close-btn" aria-label="${Z(n.ariaCloseChat)}">×</button>
1253
1728
  </div>
1254
1729
  <div class="chat-body">
1255
1730
  <div class="panel panel-home"></div>
@@ -1258,29 +1733,31 @@ function ke(e, t, n, r) {
1258
1733
  <div class="messages" id="messages-container" role="log" aria-live="polite" aria-atomic="false"></div>
1259
1734
  <div class="handoff-slot"></div>
1260
1735
  <div class="chat-footer">
1736
+ <div class="attach-preview-slot"></div>
1261
1737
  <div class="input-row">
1738
+ ${S ? `<button class="attach-btn" type="button" aria-label="${Z(n.imageAttach)}">${Ve}</button>` : ""}
1262
1739
  <textarea
1263
1740
  class="input"
1264
- placeholder="${q(v.placeholder || n.inputPlaceholder)}"
1741
+ placeholder="${Z(x.placeholder || n.inputPlaceholder)}"
1265
1742
  rows="1"
1266
1743
  maxlength="2000"
1267
1744
  aria-label="Message input"
1268
1745
  ></textarea>
1269
- <button class="send-btn" aria-label="${q(n.ariaSendMessage)}">
1270
- ${xe}
1746
+ <button class="send-btn" aria-label="${Z(n.ariaSendMessage)}">
1747
+ ${ze}
1271
1748
  </button>
1272
1749
  </div>
1273
- <a class="botiq-badge" href="${ve}" target="_blank" rel="noopener noreferrer">
1274
- ${q(n.poweredBy)} <span class="botiq-badge-name">BotIQ</span>
1275
- </a>
1276
1750
  </div>
1277
1751
  </div>
1278
1752
  </div>
1279
1753
  <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>
1754
+ <button class="nav-btn" data-view="home">${He}<span>${Z(n.navHome)}</span></button>
1755
+ <button class="nav-btn" data-view="messages">${Ue}<span>${Z(n.navMessages)}</span></button>
1282
1756
  </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", () => {
1757
+ <a class="botiq-badge" href="${Fe}" target="_blank" rel="noopener noreferrer">
1758
+ ${Z(n.poweredBy)} <span class="botiq-badge-name">BotIQ</span>
1759
+ </a>
1760
+ `, 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 = ye(t, n, [], C), l.querySelector(".home-start")?.addEventListener("click", () => {
1284
1761
  r.onNewConversation();
1285
1762
  }), f = c.querySelector(".nav-btn[data-view=\"home\"]"), p = c.querySelector(".nav-btn[data-view=\"messages\"]"), c.querySelectorAll(".nav-btn").forEach((e) => {
1286
1763
  e.addEventListener("click", () => {
@@ -1288,44 +1765,105 @@ function ke(e, t, n, r) {
1288
1765
  r.onNavigate(t);
1289
1766
  });
1290
1767
  }), m = c.querySelector("#messages-container"), h = c.querySelector(".handoff-slot");
1291
- let x = null;
1768
+ let T = null;
1292
1769
  m.addEventListener("scroll", () => {
1293
- m.scrollTop <= 48 && !x && (x = setTimeout(() => {
1294
- x = null;
1770
+ m.scrollTop <= 48 && !T && (T = setTimeout(() => {
1771
+ T = null;
1295
1772
  }, 150), r.onScrollTop());
1296
1773
  }), g = c.querySelector(".input"), _ = c.querySelector(".send-btn"), g.addEventListener("input", () => {
1297
1774
  g.style.height = "auto", g.style.height = Math.min(g.scrollHeight, 100) + "px";
1298
1775
  }), 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();
1776
+ e.key === "Enter" && !e.shiftKey && (e.preventDefault(), E());
1777
+ }), _.addEventListener("click", E);
1778
+ let D = c.querySelector(".input-row"), { button: O, panel: k } = Oe((e) => {
1779
+ Ee(g, e), g.focus();
1303
1780
  });
1304
- S.insertBefore(C, _), c.querySelector(".chat-footer").appendChild(w), o.appendChild(c);
1781
+ 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", () => {
1782
+ b?.click();
1783
+ }), b.addEventListener("change", () => {
1784
+ let e = b?.files?.[0];
1785
+ e && r.onAttachImage && (r.onAttachImage(e), b && (b.value = ""));
1786
+ }))), o.appendChild(c);
1305
1787
  }
1306
- function b() {
1788
+ function E() {
1307
1789
  let e = g.value.trim();
1308
- !e || _.disabled || (g.value = "", g.style.height = "auto", r.onSend(e));
1790
+ !e && !y?.querySelector(".attach-preview") || _.disabled || (g.value = "", g.style.height = "auto", r.onSend(e));
1309
1791
  }
1310
- function x(e) {
1792
+ function D(e) {
1311
1793
  let a = m.scrollHeight - m.scrollTop - m.clientHeight < 80, o = m.scrollTop, s = m.scrollHeight;
1312
- m.innerHTML = Oe(e, {
1794
+ m.innerHTML = Xe(e, {
1313
1795
  strings: n,
1314
- content: v,
1796
+ content: x,
1315
1797
  botName: i,
1316
1798
  lang: t.widgetLanguage,
1317
1799
  now: Date.now(),
1318
- contactInfo: t.contactInfo
1800
+ contactInfo: t.contactInfo,
1801
+ richMessages: t.richMessages
1319
1802
  }), m.querySelectorAll(".chip").forEach((e) => {
1320
1803
  e.addEventListener("click", () => r.onSend(e.textContent ?? ""));
1321
1804
  }), m.querySelectorAll(".retry-btn").forEach((e) => {
1322
1805
  e.addEventListener("click", () => r.onRetry());
1323
- }), a ? m.scrollTop = m.scrollHeight : m.scrollTop = pe(o, s, m.scrollHeight);
1806
+ }), m.querySelectorAll(".biq-btn[data-action=\"send\"]").forEach((e) => {
1807
+ e.addEventListener("click", () => {
1808
+ let t = e.dataset.value ?? "";
1809
+ if (t) {
1810
+ r.onSend(t);
1811
+ let n = e.closest(".biq-blocks");
1812
+ n && (n.style.pointerEvents = "none", n.style.opacity = "0.5");
1813
+ }
1814
+ });
1815
+ }), m.querySelectorAll(".biq-btn[data-action=\"page_action\"]").forEach((e) => {
1816
+ e.addEventListener("click", () => {
1817
+ let t = e.dataset.value ?? "";
1818
+ if (t && r.onBlockPageAction) {
1819
+ let n = e.closest(".biq-blocks");
1820
+ n && (n.style.pointerEvents = "none", n.style.opacity = "0.5"), r.onBlockPageAction(t);
1821
+ }
1822
+ });
1823
+ }), m.querySelectorAll(".biq-qr-pill[data-qr=\"1\"]").forEach((e) => {
1824
+ e.addEventListener("click", () => {
1825
+ let t = e.dataset.value ?? "";
1826
+ if (t) {
1827
+ r.onSend(t);
1828
+ let n = e.closest(".biq-quick-replies");
1829
+ n && (n.style.display = "none");
1830
+ }
1831
+ });
1832
+ }), a ? m.scrollTop = m.scrollHeight : m.scrollTop = we(o, s, m.scrollHeight);
1324
1833
  }
1325
- function S(e) {
1834
+ function O(e) {
1326
1835
  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) => {
1836
+ let i = s.querySelector(".unread-badge-slot");
1837
+ if (i && (i.innerHTML = Je(e.unreadCount)), _.disabled = e.isLoading || e.isUploading, y && S) if (e.pendingAttachment) {
1838
+ let t = e.pendingAttachment, i = e.isUploading, a = e.uploadError;
1839
+ y.innerHTML = `
1840
+ <div class="attach-preview">
1841
+ <img
1842
+ src="${Z(t.previewUrl)}"
1843
+ alt="Preview"
1844
+ class="attach-preview-img${i ? " attach-preview-img--uploading" : ""}"
1845
+ />
1846
+ ${i ? `<span class="attach-preview-status">${Z(n.imageUploading)}</span>` : ""}
1847
+ ${a ? `<span class="attach-preview-error">${Z(a)}</span>` : ""}
1848
+ ${i ? "" : `<button class="attach-preview-remove" type="button" aria-label="${Z(n.imageRemove)}">×</button>`}
1849
+ </div>
1850
+ `, y.querySelector(".attach-preview-remove")?.addEventListener("click", () => {
1851
+ r.onRemoveAttachment?.();
1852
+ });
1853
+ } else y.innerHTML = "";
1854
+ 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) {
1855
+ w = e.conversations;
1856
+ let i = Date.now();
1857
+ l.innerHTML = ye(t, n, e.conversations, C, i), l.querySelector(".home-start")?.addEventListener("click", () => {
1858
+ r.onNewConversation();
1859
+ }), l.querySelectorAll(".home-recent-item").forEach((e) => {
1860
+ e.addEventListener("click", () => {
1861
+ let t = e.dataset.session;
1862
+ t && r.onOpenConversation(t);
1863
+ });
1864
+ });
1865
+ }
1866
+ e.view === "conversation" && (D(e), h.innerHTML = qe(e.escalated, n)), e.view === "messages" && (u.innerHTML = ve(e.conversations, n, Date.now(), C), u.querySelectorAll(".msg-list-item").forEach((e) => {
1329
1867
  e.addEventListener("click", () => {
1330
1868
  let t = e.dataset.session;
1331
1869
  t && r.onOpenConversation(t);
@@ -1334,8 +1872,8 @@ function ke(e, t, n, r) {
1334
1872
  r.onNewConversation();
1335
1873
  }));
1336
1874
  }
1337
- function C(e) {
1338
- return me(o, {
1875
+ function k(e) {
1876
+ return Te(o, {
1339
1877
  ...e,
1340
1878
  labels: {
1341
1879
  confirm: n.confirmYes,
@@ -1346,14 +1884,14 @@ function ke(e, t, n, r) {
1346
1884
  });
1347
1885
  }
1348
1886
  return {
1349
- mount: y,
1350
- update: S,
1351
- confirmAction: C
1887
+ mount: T,
1888
+ update: O,
1889
+ confirmAction: k
1352
1890
  };
1353
1891
  }
1354
1892
  //#endregion
1355
1893
  //#region src/i18n/vi.ts
1356
- var Ae = {
1894
+ var Qe = {
1357
1895
  statusOnline: "Trực tuyến",
1358
1896
  statusPaused: "Tạm ngừng",
1359
1897
  navHome: "Trang chủ",
@@ -1385,8 +1923,16 @@ var Ae = {
1385
1923
  confirmNo: "Huỷ",
1386
1924
  confirmCountdownPrefix: "Tự động xác nhận sau ",
1387
1925
  confirmCountdownSuffix: "s…",
1388
- retry: "Thử lại"
1389
- }, X = {
1926
+ retry: "Thử lại",
1927
+ recentMessages: "Tin nhắn gần đây",
1928
+ imageAttach: "Đính kèm ảnh",
1929
+ imageRemove: "Xóa ảnh",
1930
+ imageUploading: "Đang tải ảnh…",
1931
+ imageUploadPlanError: "Gói hiện tại không hỗ trợ gửi ảnh.",
1932
+ imageUploadTooLarge: "Ảnh quá lớn (tối đa 5 MB sau khi nén).",
1933
+ imageUploadRateLimit: "Gửi quá nhanh. Vui lòng thử lại.",
1934
+ imageUploadError: "Tải ảnh thất bại. Vui lòng thử lại."
1935
+ }, $e = {
1390
1936
  statusOnline: "Online",
1391
1937
  statusPaused: "Paused",
1392
1938
  navHome: "Home",
@@ -1418,24 +1964,32 @@ var Ae = {
1418
1964
  confirmNo: "Cancel",
1419
1965
  confirmCountdownPrefix: "Auto-confirming in ",
1420
1966
  confirmCountdownSuffix: "s…",
1421
- retry: "Retry"
1967
+ retry: "Retry",
1968
+ recentMessages: "Recent messages",
1969
+ imageAttach: "Attach image",
1970
+ imageRemove: "Remove image",
1971
+ imageUploading: "Uploading image…",
1972
+ imageUploadPlanError: "Your plan does not support image sending.",
1973
+ imageUploadTooLarge: "Image too large (max 5 MB after compression).",
1974
+ imageUploadRateLimit: "Too many requests. Please try again.",
1975
+ imageUploadError: "Image upload failed. Please try again."
1422
1976
  };
1423
1977
  //#endregion
1424
1978
  //#region src/i18n/index.ts
1425
- function Z(e) {
1426
- return e === "en" ? X : Ae;
1979
+ function et(e) {
1980
+ return e === "en" ? $e : Qe;
1427
1981
  }
1428
1982
  //#endregion
1429
1983
  //#region src/core/declarative-executor.ts
1430
- var je = /^[a-zA-Z0-9_-]*$/;
1431
- function Q(e, t) {
1984
+ var tt = /^[a-zA-Z0-9_-]*$/;
1985
+ function nt(e, t) {
1432
1986
  return e.replace(/\{([a-zA-Z][a-zA-Z0-9_]*)\}/g, (e, n) => {
1433
1987
  let r = t[n], i = r == null ? "" : String(r);
1434
- if (!je.test(i)) throw Error(`Unsafe param value for "${n}"`);
1988
+ if (!tt.test(i)) throw Error(`Unsafe param value for "${n}"`);
1435
1989
  return i;
1436
1990
  });
1437
1991
  }
1438
- function Me(e, t) {
1992
+ function rt(e, t) {
1439
1993
  let n = typeof location < "u" ? location.origin : "https://placeholder.local", r, i;
1440
1994
  try {
1441
1995
  r = new URL(e, n), i = new URL(t.replace(/\{[a-zA-Z][a-zA-Z0-9_]*\}/g, "x"), n);
@@ -1445,10 +1999,10 @@ function Me(e, t) {
1445
1999
  if (!/^https?:$/.test(r.protocol)) throw Error("Navigation must be http(s)");
1446
2000
  if (r.origin !== i.origin) throw Error("Navigation origin not allowed");
1447
2001
  }
1448
- async function Ne(e, t) {
2002
+ async function it(e, t) {
1449
2003
  let n = e.config;
1450
2004
  if (e.operation === "click") {
1451
- let e = Q(String(n.selectorTemplate ?? ""), t), r = document.querySelector(e);
2005
+ let e = nt(String(n.selectorTemplate ?? ""), t), r = document.querySelector(e);
1452
2006
  if (!r) throw Error(`No element for selector: ${e}`);
1453
2007
  r.click();
1454
2008
  return;
@@ -1463,15 +2017,15 @@ async function Ne(e, t) {
1463
2017
  return;
1464
2018
  }
1465
2019
  if (e.operation === "navigate") {
1466
- let e = String(n.urlTemplate ?? ""), r = Q(e, t);
1467
- Me(r, e), location.assign(r);
2020
+ let e = String(n.urlTemplate ?? ""), r = nt(e, t);
2021
+ rt(r, e), location.assign(r);
1468
2022
  return;
1469
2023
  }
1470
2024
  throw Error(`Unknown operation: ${String(e.operation)}`);
1471
2025
  }
1472
2026
  //#endregion
1473
2027
  //#region src/core/page-action-executor.ts
1474
- async function Pe(e, t) {
2028
+ async function at(e, t) {
1475
2029
  let n = t.registry.get(e.actionName);
1476
2030
  if (!n) return {
1477
2031
  status: "unknown",
@@ -1496,26 +2050,26 @@ async function Pe(e, t) {
1496
2050
  //#endregion
1497
2051
  //#region src/builds/npm/index.ts
1498
2052
  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;
2053
+ function ot(e) {
2054
+ if (D.setDeclarativeRunner(it), 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
2055
  let t = p(e);
1502
2056
  y();
1503
2057
  let n = t.apiKey.slice(-16), r = C(), i = b(n), a = x(n, i);
1504
- F({
2058
+ R({
1505
2059
  messages: a,
1506
2060
  view: "home",
1507
2061
  currentSessionId: i
1508
2062
  });
1509
- let o = H(), s = document.createElement("div");
2063
+ let o = K(), s = document.createElement("div");
1510
2064
  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 });
2065
+ let c = () => void 0, l = !1, u = et(void 0), d = null, m = null, h = null, g = null;
2066
+ function _() {
2067
+ g ||= setTimeout(async () => {
2068
+ if (g = null, !(!L().escalated || !L().isOpen)) try {
2069
+ let e = await P(t.apiUrl, t.apiKey, L().currentSessionId, $, r);
2070
+ e.messages.length > 0 && R({ messages: e.messages.map(M) }), R({ escalated: e.escalated === !0 });
1517
2071
  } catch {} finally {
1518
- P().escalated && P().isOpen && g();
2072
+ L().escalated && L().isOpen && _();
1519
2073
  }
1520
2074
  }, 1e4);
1521
2075
  }
@@ -1525,94 +2079,132 @@ function Fe(e) {
1525
2079
  console.warn("[BotIQ] Widget not authorised on this origin — skipped mount"), s.remove();
1526
2080
  return;
1527
2081
  }
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
2082
+ h = e, u = et(e.widgetLanguage), e.pageActions?.length && D.seed(e.pageActions);
2083
+ let n = Ze(t, e, u, {
2084
+ onSend: ae,
2085
+ onToggle: le,
2086
+ onScrollTop: ne,
2087
+ onRetry: oe,
2088
+ onNavigate: E,
2089
+ onOpenConversation: j,
2090
+ onNewConversation: A,
2091
+ onAttachImage: ie,
2092
+ onRemoveAttachment: V,
2093
+ onBlockPageAction: se
1537
2094
  });
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],
2095
+ d = n, n.mount(s), c = re((e) => n.update(e)), n.update(L()), e.proactive?.enabled && (m = setTimeout(() => {
2096
+ if (L().isOpen) return;
2097
+ let t = w();
2098
+ R({
2099
+ messages: [{
2100
+ role: "assistant",
2101
+ content: e.proactive.message,
2102
+ ts: Date.now()
2103
+ }],
1547
2104
  view: "conversation",
1548
- unreadCount: P().unreadCount + 1
2105
+ currentSessionId: t,
2106
+ unreadCount: L().unreadCount + 1
1549
2107
  });
1550
- }, e.proactive.delayMs ?? 8e3)), a.length > 0 && k(i);
2108
+ }, e.proactive.delayMs ?? 8e3)), a.length > 0 && N(i);
1551
2109
  });
1552
- async function _() {
1553
- F({ conversations: await ee(t.apiUrl, t.apiKey, r) });
2110
+ let v = !1;
2111
+ async function T() {
2112
+ if (!v) {
2113
+ v = !0;
2114
+ try {
2115
+ R({ conversations: await O(t.apiUrl, t.apiKey, r) });
2116
+ } finally {
2117
+ v = !1;
2118
+ }
2119
+ }
1554
2120
  }
1555
- function v(e) {
1556
- F({ view: e }), e === "messages" && _();
2121
+ function E(e) {
2122
+ R({ view: e }), (e === "home" || e === "messages") && T();
1557
2123
  }
1558
- function T() {
1559
- F({
2124
+ function A() {
2125
+ R({
1560
2126
  view: "conversation",
1561
2127
  currentSessionId: w(),
1562
2128
  messages: [],
1563
2129
  escalated: !1
1564
- }), o = H();
2130
+ }), o = K();
1565
2131
  }
1566
- function E(e) {
1567
- F({
2132
+ function j(e) {
2133
+ R({
1568
2134
  view: "conversation",
1569
2135
  currentSessionId: e,
1570
2136
  messages: x(n, e)
1571
- }), o = H(), k(e);
2137
+ }), o = K(), N(e);
1572
2138
  }
1573
- function O(e) {
2139
+ function M(e) {
1574
2140
  let t = Date.parse(e.createdAt);
1575
2141
  return {
1576
2142
  role: e.role,
1577
2143
  content: e.content,
1578
- ...Number.isFinite(t) ? { ts: t } : {}
2144
+ ...Number.isFinite(t) ? { ts: t } : {},
2145
+ ...h?.richMessages && e.blocks?.length ? { blocks: e.blocks } : {}
1579
2146
  };
1580
2147
  }
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();
2148
+ async function N(e) {
2149
+ let n = await P(t.apiUrl, t.apiKey, e, $, r);
2150
+ L().currentSessionId === e && (n.messages.length > 0 && (o = xe(n), R({ messages: n.messages.map(M) })), R({ escalated: n.escalated === !0 }), L().escalated && _());
1584
2151
  }
1585
- async function A() {
1586
- if (fe(o, P().loadingOlder)) {
1587
- F({ loadingOlder: !0 });
2152
+ async function ne() {
2153
+ if (Ce(o, L().loadingOlder)) {
2154
+ R({ loadingOlder: !0 });
1588
2155
  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);
2156
+ let e = L().currentSessionId, n = await ee(t.apiUrl, t.apiKey, e, o.oldestCursor, $);
2157
+ n.messages.length > 0 && R({ messages: [...n.messages.map(M), ...L().messages] }), o = Se(o, n);
1591
2158
  } finally {
1592
- F({ loadingOlder: !1 });
2159
+ R({ loadingOlder: !1 });
1593
2160
  }
1594
2161
  }
1595
2162
  }
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 });
2163
+ let F = "", I = null, z = null;
2164
+ async function B(e, i, a) {
2165
+ let o = L().currentSessionId, s = x(n, o).length === 0 && L().messages.filter((e) => e.role === "user").length === 1;
2166
+ R({ isLoading: !0 });
1600
2167
  try {
1601
- let o = await te(t.apiUrl, t.apiKey, i, e, P().messages.filter((e) => !e.error), u, r), s = {
2168
+ let c = i && a ? [{
2169
+ url: i,
2170
+ mimeType: a
2171
+ }] : void 0, l = await te({
2172
+ apiUrl: t.apiUrl,
2173
+ apiKey: t.apiKey,
2174
+ sessionId: o,
2175
+ message: e,
2176
+ history: L().messages.filter((e) => !e.error && !e.streaming),
2177
+ strings: u,
2178
+ visitorId: r,
2179
+ attachments: c,
2180
+ onToken(e) {
2181
+ let t = L().messages, n = t[t.length - 1];
2182
+ n?.role === "assistant" && n.streaming ? R({ messages: [...t.slice(0, -1), {
2183
+ ...n,
2184
+ content: n.content + e
2185
+ }] }) : R({ messages: [...t, {
2186
+ role: "assistant",
2187
+ content: e,
2188
+ ts: Date.now(),
2189
+ streaming: !0
2190
+ }] });
2191
+ }
2192
+ }), f = L().messages.filter((e) => !e.streaming), p = {
1602
2193
  role: "assistant",
1603
- content: o.reply,
2194
+ content: l.reply,
1604
2195
  ts: Date.now(),
1605
- ...o.error ? { error: !0 } : {}
2196
+ ...l.error ? { error: !0 } : {},
2197
+ ...h?.richMessages && l.blocks?.length ? { blocks: l.blocks } : {}
1606
2198
  };
1607
- if (F({
1608
- messages: [...P().messages, s],
2199
+ if (R({
2200
+ messages: [...f, p],
1609
2201
  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) {
2202
+ }), l.error || S(n, o, [p]), !l.error && s && T(), l.error || P(t.apiUrl, t.apiKey, o, $, r).then((e) => {
2203
+ R({ escalated: e.escalated === !0 }), L().escalated && _();
2204
+ }), !l.error && l.pageAction && d) {
1613
2205
  let e = d;
1614
2206
  try {
1615
- let t = await Pe(o.pageAction, {
2207
+ let t = await at(l.pageAction, {
1616
2208
  registry: D,
1617
2209
  confirm: (t) => e.confirmAction(t)
1618
2210
  });
@@ -1622,7 +2214,7 @@ function Fe(e) {
1622
2214
  content: t.status === "executed" ? u.actionDone : t.status === "cancelled" ? u.actionCancelled : t.status === "unknown" ? `⚠ ${t.message ?? u.actionUnavailable}` : `⚠ ${t.message ?? u.actionError}`,
1623
2215
  ts: Date.now()
1624
2216
  };
1625
- F({ messages: [...P().messages, r] }), S(n, i, [r]);
2217
+ R({ messages: [...L().messages, r] }), S(n, o, [r]);
1626
2218
  } catch {
1627
2219
  if (!d) return;
1628
2220
  let e = {
@@ -1630,38 +2222,128 @@ function Fe(e) {
1630
2222
  content: u.actionError,
1631
2223
  ts: Date.now()
1632
2224
  };
1633
- F({ messages: [...P().messages, e] }), S(n, i, [e]);
2225
+ R({ messages: [...L().messages, e] }), S(n, o, [e]);
1634
2226
  }
1635
2227
  }
1636
2228
  } catch {
1637
- F({ isLoading: !1 });
2229
+ R({ isLoading: !1 });
1638
2230
  }
1639
2231
  }
1640
- async function I(e) {
1641
- if (P().isLoading) return;
1642
- M = e;
1643
- let t = P().currentSessionId, r = {
2232
+ async function ie(e) {
2233
+ let t = ce(e);
2234
+ if (!t.ok) {
2235
+ R({ uploadError: t.error });
2236
+ return;
2237
+ }
2238
+ R({ uploadError: null });
2239
+ try {
2240
+ let { blob: t, mimeType: n } = await ue(e), r = L().pendingAttachment;
2241
+ r && URL.revokeObjectURL(r.previewUrl), R({
2242
+ pendingAttachment: {
2243
+ blob: t,
2244
+ previewUrl: URL.createObjectURL(t),
2245
+ mimeType: n
2246
+ },
2247
+ uploadError: null
2248
+ });
2249
+ } catch {
2250
+ R({ uploadError: u.imageUploadError });
2251
+ }
2252
+ }
2253
+ function V() {
2254
+ let e = L().pendingAttachment;
2255
+ e && URL.revokeObjectURL(e.previewUrl), R({
2256
+ pendingAttachment: null,
2257
+ uploadError: null
2258
+ });
2259
+ }
2260
+ async function ae(e) {
2261
+ if (L().isLoading || L().isUploading) return;
2262
+ let i = L().pendingAttachment;
2263
+ if (!e && !i) return;
2264
+ R({ uploadError: null }), F = e, I = null, z = null;
2265
+ let a = L().currentSessionId, o = {
1644
2266
  role: "user",
1645
2267
  content: e,
1646
2268
  ts: Date.now()
1647
2269
  };
1648
- F({ messages: [...P().messages, r] }), S(n, t, [r]), await N(e);
2270
+ if (R({ messages: [...L().messages, o] }), S(n, a, [o]), i) {
2271
+ R({
2272
+ isUploading: !0,
2273
+ uploadError: null
2274
+ });
2275
+ let e = await k(t.apiUrl, t.apiKey, i.blob, r, u);
2276
+ if (R({ isUploading: !1 }), !e.ok) {
2277
+ R({ uploadError: e.error });
2278
+ let t = L().messages;
2279
+ R({ messages: t.slice(0, -1) });
2280
+ return;
2281
+ }
2282
+ I = e.url, z = i.mimeType, URL.revokeObjectURL(i.previewUrl), R({
2283
+ pendingAttachment: null,
2284
+ uploadError: null
2285
+ });
2286
+ }
2287
+ await B(e, I ?? void 0, z ?? void 0);
2288
+ }
2289
+ function oe() {
2290
+ if (L().isLoading || !F) return;
2291
+ let e = L().messages;
2292
+ R({ messages: e.length > 0 && e[e.length - 1].error ? e.slice(0, -1) : e }), B(F, I ?? void 0, z ?? void 0);
1649
2293
  }
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);
2294
+ async function se(e) {
2295
+ if (!d) return;
2296
+ if (!D.has(e)) {
2297
+ let e = {
2298
+ role: "assistant",
2299
+ content: `⚠ ${u.actionUnavailable}`,
2300
+ ts: Date.now()
2301
+ };
2302
+ R({ messages: [...L().messages, e] });
2303
+ return;
2304
+ }
2305
+ let t = d;
2306
+ try {
2307
+ let n = await at({
2308
+ actionName: e,
2309
+ params: {},
2310
+ reason: ""
2311
+ }, {
2312
+ registry: D,
2313
+ confirm: (e) => t.confirmAction(e)
2314
+ });
2315
+ if (!d) return;
2316
+ let r = {
2317
+ role: "assistant",
2318
+ content: n.status === "executed" ? u.actionDone : n.status === "cancelled" ? u.actionCancelled : n.status === "unknown" ? `⚠ ${n.message ?? u.actionUnavailable}` : `⚠ ${n.message ?? u.actionError}`,
2319
+ ts: Date.now()
2320
+ };
2321
+ R({ messages: [...L().messages, r] });
2322
+ } catch {
2323
+ if (!d) return;
2324
+ let e = {
2325
+ role: "assistant",
2326
+ content: u.actionError,
2327
+ ts: Date.now()
2328
+ };
2329
+ R({ messages: [...L().messages, e] });
2330
+ }
1654
2331
  }
1655
- function R() {
1656
- let e = !P().isOpen;
1657
- F({
2332
+ function le() {
2333
+ let e = !L().isOpen, t = L().view;
2334
+ R({
1658
2335
  isOpen: e,
1659
2336
  ...e ? { unreadCount: 0 } : {}
1660
- });
2337
+ }), e && t === "home" && T();
1661
2338
  }
1662
2339
  return () => {
1663
- l = !0, d = null, c(), m && clearTimeout(m), h &&= (clearTimeout(h), null), s.remove();
2340
+ l = !0, d = null, c(), m && clearTimeout(m), g &&= (clearTimeout(g), null);
2341
+ let e = L().pendingAttachment;
2342
+ e && URL.revokeObjectURL(e.previewUrl), R({
2343
+ pendingAttachment: null,
2344
+ uploadError: null
2345
+ }), s.remove();
1664
2346
  };
1665
2347
  }
1666
2348
  //#endregion
1667
- export { Fe as t };
2349
+ export { ot as t };