@locdo.tech/botiq-chat-sdk 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/{npm-XnNJWLXh.js → npm-DbbHOVFV.js} +562 -239
- package/dist/sdk/react.js +1 -1
- package/dist/sdk/vue.js +1 -1
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./npm-
|
|
1
|
+
import { t as e } from "./npm-DbbHOVFV.js";
|
|
2
2
|
export { e as init };
|
|
@@ -90,19 +90,23 @@ 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;
|
|
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;
|
|
94
94
|
return !a.design?.colors || !a.design?.layout || !a.design?.content || !l(a.design) ? {
|
|
95
95
|
name: o,
|
|
96
96
|
design: t,
|
|
97
97
|
widgetLanguage: s,
|
|
98
98
|
...c ? { pageActions: c } : {},
|
|
99
|
-
...u ? { contactInfo: u } : {}
|
|
99
|
+
...u ? { contactInfo: u } : {},
|
|
100
|
+
...f ? { proactive: f } : {},
|
|
101
|
+
...p ? { status: p } : {}
|
|
100
102
|
} : {
|
|
101
103
|
name: o,
|
|
102
104
|
design: a.design,
|
|
103
105
|
widgetLanguage: s,
|
|
104
106
|
...c ? { pageActions: c } : {},
|
|
105
|
-
...u ? { contactInfo: u } : {}
|
|
107
|
+
...u ? { contactInfo: u } : {},
|
|
108
|
+
...f ? { proactive: f } : {},
|
|
109
|
+
...p ? { status: p } : {}
|
|
106
110
|
};
|
|
107
111
|
}
|
|
108
112
|
function p(t) {
|
|
@@ -113,14 +117,14 @@ function p(t) {
|
|
|
113
117
|
}
|
|
114
118
|
//#endregion
|
|
115
119
|
//#region src/core/session.ts
|
|
116
|
-
var m = "botiq:sessionId", h = "botiq:sessionId:", g = "botiq:history:", _ = 10;
|
|
117
|
-
function
|
|
120
|
+
var m = "botiq:sessionId", h = "botiq:sessionId:", g = "botiq:history:", _ = "botiq:visitorId", v = 10;
|
|
121
|
+
function y() {
|
|
118
122
|
try {
|
|
119
123
|
let e = localStorage.getItem(m);
|
|
120
124
|
localStorage.removeItem(m), e && localStorage.removeItem(g + e);
|
|
121
125
|
} catch {}
|
|
122
126
|
}
|
|
123
|
-
function
|
|
127
|
+
function b(e) {
|
|
124
128
|
try {
|
|
125
129
|
let t = h + e, n = localStorage.getItem(t);
|
|
126
130
|
return n || (n = crypto.randomUUID(), localStorage.setItem(t, n)), n;
|
|
@@ -128,7 +132,7 @@ function y(e) {
|
|
|
128
132
|
return crypto.randomUUID();
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
|
-
function
|
|
135
|
+
function x(e, t) {
|
|
132
136
|
try {
|
|
133
137
|
let n = localStorage.getItem(`${g}${e}:${t}`);
|
|
134
138
|
return n ? JSON.parse(n) : [];
|
|
@@ -136,27 +140,38 @@ function b(e, t) {
|
|
|
136
140
|
return [];
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
|
-
function
|
|
143
|
+
function S(e, t, n) {
|
|
140
144
|
try {
|
|
141
|
-
let r = [...
|
|
145
|
+
let r = [...x(e, t), ...n].slice(-v);
|
|
142
146
|
localStorage.setItem(`${g}${e}:${t}`, JSON.stringify(r));
|
|
143
147
|
} catch {}
|
|
144
148
|
}
|
|
149
|
+
function C() {
|
|
150
|
+
try {
|
|
151
|
+
let e = localStorage.getItem(_);
|
|
152
|
+
return e || (e = crypto.randomUUID(), localStorage.setItem(_, e)), e;
|
|
153
|
+
} catch {
|
|
154
|
+
return crypto.randomUUID();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function w() {
|
|
158
|
+
return crypto.randomUUID();
|
|
159
|
+
}
|
|
145
160
|
//#endregion
|
|
146
161
|
//#region src/core/action-registry.ts
|
|
147
|
-
var
|
|
162
|
+
var T = /^[a-z][a-z0-9_]{2,40}$/, E = new Set([
|
|
148
163
|
"low",
|
|
149
164
|
"medium",
|
|
150
165
|
"high"
|
|
151
|
-
]),
|
|
166
|
+
]), D = new class {
|
|
152
167
|
actions = /* @__PURE__ */ new Map();
|
|
153
168
|
declarativeRunner;
|
|
154
169
|
constructor(e) {
|
|
155
170
|
this.declarativeRunner = e;
|
|
156
171
|
}
|
|
157
172
|
validate(e) {
|
|
158
|
-
if (!
|
|
159
|
-
if (!
|
|
173
|
+
if (!T.test(e.name)) throw Error(`Invalid action name: "${e.name}"`);
|
|
174
|
+
if (!E.has(e.riskLevel)) throw Error(`Invalid riskLevel: ${e.riskLevel}`);
|
|
160
175
|
if (typeof e.description != "string" || e.description.length < 5) throw Error(`description ≥5 chars required for ${e.name}`);
|
|
161
176
|
}
|
|
162
177
|
seed(e) {
|
|
@@ -208,39 +223,57 @@ var S = /^[a-z][a-z0-9_]{2,40}$/, C = new Set([
|
|
|
208
223
|
}();
|
|
209
224
|
//#endregion
|
|
210
225
|
//#region src/core/api.ts
|
|
211
|
-
async function ee(e, t, n
|
|
226
|
+
async function ee(e, t, n) {
|
|
212
227
|
try {
|
|
213
|
-
let
|
|
228
|
+
let r = new URLSearchParams({ visitorId: n }), i = await fetch(`${e}/widget/conversations?${r.toString()}`, {
|
|
229
|
+
method: "GET",
|
|
230
|
+
headers: {
|
|
231
|
+
"X-Api-Key": t,
|
|
232
|
+
"X-Visitor-Id": n
|
|
233
|
+
},
|
|
234
|
+
referrerPolicy: "no-referrer-when-downgrade"
|
|
235
|
+
});
|
|
236
|
+
if (!i.ok) return [];
|
|
237
|
+
let a = await i.json();
|
|
238
|
+
return Array.isArray(a.conversations) ? a.conversations : [];
|
|
239
|
+
} catch {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function te(e, t, n, r, i, a, o) {
|
|
244
|
+
try {
|
|
245
|
+
let s = D.getDefinitions(), c = await fetch(`${e}/widget/chat`, {
|
|
214
246
|
method: "POST",
|
|
215
247
|
headers: {
|
|
216
248
|
"Content-Type": "application/json",
|
|
217
|
-
"X-Api-Key": t
|
|
249
|
+
"X-Api-Key": t,
|
|
250
|
+
...o ? { "X-Visitor-Id": o } : {}
|
|
218
251
|
},
|
|
219
252
|
referrerPolicy: "no-referrer-when-downgrade",
|
|
220
253
|
body: JSON.stringify({
|
|
221
254
|
sessionId: n,
|
|
222
255
|
message: r,
|
|
223
256
|
history: i,
|
|
224
|
-
...
|
|
257
|
+
...s.length > 0 ? { availableActions: s } : {}
|
|
225
258
|
})
|
|
226
259
|
});
|
|
227
|
-
if (!
|
|
260
|
+
if (!c.ok) return c.status === 401 ? {
|
|
228
261
|
reply: a.errorAuth,
|
|
229
262
|
error: !0
|
|
230
|
-
} :
|
|
263
|
+
} : c.status === 403 ? {
|
|
231
264
|
reply: a.errorForbidden,
|
|
232
265
|
error: !0
|
|
233
|
-
} :
|
|
266
|
+
} : c.status === 429 ? {
|
|
234
267
|
reply: a.errorQuota,
|
|
235
268
|
error: !0
|
|
236
269
|
} : {
|
|
237
270
|
reply: a.errorGeneric,
|
|
238
271
|
error: !0
|
|
239
272
|
};
|
|
240
|
-
let
|
|
273
|
+
let l = await c.json();
|
|
241
274
|
return {
|
|
242
|
-
reply:
|
|
243
|
-
pageAction:
|
|
275
|
+
reply: l.reply || a.errorMessage,
|
|
276
|
+
pageAction: l.pageAction
|
|
244
277
|
};
|
|
245
278
|
} catch {
|
|
246
279
|
return {
|
|
@@ -249,68 +282,78 @@ async function ee(e, t, n, r, i, a) {
|
|
|
249
282
|
};
|
|
250
283
|
}
|
|
251
284
|
}
|
|
252
|
-
var
|
|
285
|
+
var O = {
|
|
253
286
|
messages: [],
|
|
254
287
|
hasMore: !1
|
|
255
288
|
};
|
|
256
|
-
function
|
|
289
|
+
function k(e) {
|
|
257
290
|
if (!e || typeof e != "object") return !1;
|
|
258
291
|
let t = e;
|
|
259
292
|
return typeof t.id == "string" && (t.role === "user" || t.role === "assistant") && typeof t.content == "string";
|
|
260
293
|
}
|
|
261
|
-
async function
|
|
294
|
+
async function A(e, t, n, r, i, a) {
|
|
262
295
|
try {
|
|
263
|
-
let
|
|
296
|
+
let o = new URLSearchParams({
|
|
264
297
|
sessionId: n,
|
|
265
298
|
limit: String(i)
|
|
266
299
|
});
|
|
267
|
-
r &&
|
|
268
|
-
let
|
|
300
|
+
r && o.set("before", r);
|
|
301
|
+
let s = await fetch(`${e}/widget/messages?${o.toString()}`, {
|
|
269
302
|
method: "GET",
|
|
270
|
-
headers: {
|
|
303
|
+
headers: {
|
|
304
|
+
"X-Api-Key": t,
|
|
305
|
+
...a ? { "X-Visitor-Id": a } : {}
|
|
306
|
+
},
|
|
271
307
|
referrerPolicy: "no-referrer-when-downgrade"
|
|
272
308
|
});
|
|
273
|
-
if (!
|
|
274
|
-
let
|
|
309
|
+
if (!s.ok) return O;
|
|
310
|
+
let c = await s.json();
|
|
275
311
|
return {
|
|
276
|
-
messages: Array.isArray(
|
|
277
|
-
hasMore:
|
|
312
|
+
messages: Array.isArray(c.messages) ? c.messages.filter(k) : [],
|
|
313
|
+
hasMore: c.hasMore === !0,
|
|
314
|
+
escalated: c.escalated === !0
|
|
278
315
|
};
|
|
279
316
|
} catch {
|
|
280
|
-
return
|
|
317
|
+
return O;
|
|
281
318
|
}
|
|
282
319
|
}
|
|
283
|
-
function
|
|
284
|
-
return
|
|
320
|
+
function j(e, t, n, r, i) {
|
|
321
|
+
return A(e, t, n, void 0, r, i);
|
|
285
322
|
}
|
|
286
|
-
function
|
|
287
|
-
return
|
|
323
|
+
function ne(e, t, n, r, i, a) {
|
|
324
|
+
return A(e, t, n, r, i, a);
|
|
288
325
|
}
|
|
289
326
|
//#endregion
|
|
290
327
|
//#region src/core/state.ts
|
|
291
|
-
var
|
|
328
|
+
var M = {
|
|
292
329
|
messages: [],
|
|
293
330
|
isLoading: !1,
|
|
294
331
|
isOpen: !1,
|
|
295
|
-
loadingOlder: !1
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
332
|
+
loadingOlder: !1,
|
|
333
|
+
view: "home",
|
|
334
|
+
unreadCount: 0,
|
|
335
|
+
conversations: [],
|
|
336
|
+
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);
|
|
302
344
|
let t = {
|
|
303
|
-
...
|
|
304
|
-
messages: [...
|
|
345
|
+
...M,
|
|
346
|
+
messages: [...M.messages],
|
|
347
|
+
conversations: [...M.conversations]
|
|
305
348
|
};
|
|
306
|
-
|
|
349
|
+
N.forEach((e) => e(t));
|
|
307
350
|
}
|
|
308
|
-
function
|
|
309
|
-
return
|
|
351
|
+
function re(e) {
|
|
352
|
+
return N.add(e), () => N.delete(e);
|
|
310
353
|
}
|
|
311
354
|
//#endregion
|
|
312
355
|
//#region src/core/styles.ts
|
|
313
|
-
var
|
|
356
|
+
var I = {
|
|
314
357
|
inter: {
|
|
315
358
|
url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
|
|
316
359
|
family: "'Inter', system-ui, -apple-system, sans-serif"
|
|
@@ -332,21 +375,21 @@ var P = {
|
|
|
332
375
|
family: "'Playfair Display', Georgia, serif"
|
|
333
376
|
}
|
|
334
377
|
};
|
|
335
|
-
function
|
|
378
|
+
function L(e, t) {
|
|
336
379
|
if (!t) return e;
|
|
337
380
|
let n = t.angle ?? 135;
|
|
338
381
|
return t.type === "linear" ? `linear-gradient(${n}deg, ${t.from}, ${t.to})` : `radial-gradient(circle, ${t.from}, ${t.to})`;
|
|
339
382
|
}
|
|
340
|
-
function
|
|
383
|
+
function R(e) {
|
|
341
384
|
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, "");
|
|
342
385
|
}
|
|
343
|
-
function
|
|
386
|
+
function ie(e) {
|
|
344
387
|
let t = e?.bubbleOpen ?? "none", n = e?.typingIndicator ?? "dots-bounce";
|
|
345
388
|
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");
|
|
346
389
|
}
|
|
347
|
-
var
|
|
348
|
-
function
|
|
349
|
-
let { colors: t, layout: n, font: r, gradient: i, animation: a, customCSS: o, whiteLabel: s } = e, c =
|
|
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; }" : "";
|
|
350
393
|
return `
|
|
351
394
|
@import url('${c.url}');
|
|
352
395
|
|
|
@@ -393,6 +436,7 @@ function ae(e) {
|
|
|
393
436
|
box-shadow: var(--shadow-md);
|
|
394
437
|
transition: transform .2s ease, box-shadow .2s ease;
|
|
395
438
|
outline: none;
|
|
439
|
+
position: relative;
|
|
396
440
|
}
|
|
397
441
|
|
|
398
442
|
.bubble:hover { transform: scale(1.08); box-shadow: var(--shadow-lg); }
|
|
@@ -404,6 +448,24 @@ function ae(e) {
|
|
|
404
448
|
.bubble.open .icon-chat { opacity: 0; }
|
|
405
449
|
.bubble.open .icon-close { opacity: 1; }
|
|
406
450
|
|
|
451
|
+
.unread-badge {
|
|
452
|
+
position: absolute;
|
|
453
|
+
top: -2px;
|
|
454
|
+
right: -2px;
|
|
455
|
+
min-width: 18px;
|
|
456
|
+
height: 18px;
|
|
457
|
+
padding: 0 5px;
|
|
458
|
+
border-radius: 9px;
|
|
459
|
+
background: #EF4444;
|
|
460
|
+
color: #fff;
|
|
461
|
+
font-size: 11px;
|
|
462
|
+
font-weight: 600;
|
|
463
|
+
display: flex;
|
|
464
|
+
align-items: center;
|
|
465
|
+
justify-content: center;
|
|
466
|
+
pointer-events: none;
|
|
467
|
+
}
|
|
468
|
+
|
|
407
469
|
/* ── Chat Window ────────────────────────── */
|
|
408
470
|
.chat-window {
|
|
409
471
|
position: absolute;
|
|
@@ -482,6 +544,120 @@ function ae(e) {
|
|
|
482
544
|
}
|
|
483
545
|
.close-btn:hover { background: rgba(255,255,255,.25); }
|
|
484
546
|
|
|
547
|
+
/* ── Chat body + panels ─────────────────── */
|
|
548
|
+
.chat-body { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
|
549
|
+
|
|
550
|
+
.panel { display: none; flex: 1; flex-direction: column; overflow: hidden; }
|
|
551
|
+
.panel.active { display: flex; }
|
|
552
|
+
|
|
553
|
+
/* ── Bottom nav ─────────────────────────── */
|
|
554
|
+
.bottom-nav {
|
|
555
|
+
display: flex;
|
|
556
|
+
border-top: 1px solid var(--gray-100);
|
|
557
|
+
flex-shrink: 0;
|
|
558
|
+
background: var(--color-bg);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.nav-btn {
|
|
562
|
+
flex: 1;
|
|
563
|
+
background: transparent;
|
|
564
|
+
border: none;
|
|
565
|
+
color: var(--gray-400);
|
|
566
|
+
font: inherit;
|
|
567
|
+
font-size: 12px;
|
|
568
|
+
padding: 10px;
|
|
569
|
+
cursor: pointer;
|
|
570
|
+
transition: color .15s;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.nav-btn:hover { color: var(--color-text); }
|
|
574
|
+
.nav-btn.active { color: var(--color-primary); }
|
|
575
|
+
|
|
576
|
+
/* ── Messages list panel ────────────────── */
|
|
577
|
+
.msg-list { flex: 1; overflow-y: auto; }
|
|
578
|
+
|
|
579
|
+
.msg-list-item {
|
|
580
|
+
display: grid;
|
|
581
|
+
width: 100%;
|
|
582
|
+
text-align: left;
|
|
583
|
+
gap: 2px;
|
|
584
|
+
padding: 12px 16px;
|
|
585
|
+
background: transparent;
|
|
586
|
+
border: none;
|
|
587
|
+
border-bottom: 1px solid var(--gray-100);
|
|
588
|
+
cursor: pointer;
|
|
589
|
+
font: inherit;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.msg-list-item:hover { background: var(--gray-50); }
|
|
593
|
+
|
|
594
|
+
.msg-list-title {
|
|
595
|
+
color: var(--color-text);
|
|
596
|
+
font-weight: 600;
|
|
597
|
+
font-size: 14px;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.msg-list-preview {
|
|
601
|
+
color: var(--gray-400);
|
|
602
|
+
font-size: 12px;
|
|
603
|
+
white-space: nowrap;
|
|
604
|
+
overflow: hidden;
|
|
605
|
+
text-overflow: ellipsis;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.msg-list-time { color: var(--gray-400); font-size: 10px; }
|
|
609
|
+
|
|
610
|
+
.msg-list-badge { color: #F97316; font-size: 10px; }
|
|
611
|
+
|
|
612
|
+
.msg-list-empty {
|
|
613
|
+
padding: 24px;
|
|
614
|
+
text-align: center;
|
|
615
|
+
color: var(--gray-400);
|
|
616
|
+
font-size: 13px;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.msg-list-new {
|
|
620
|
+
margin: 10px 16px;
|
|
621
|
+
padding: 10px;
|
|
622
|
+
border-radius: 10px;
|
|
623
|
+
border: 1.5px solid var(--color-primary);
|
|
624
|
+
background: transparent;
|
|
625
|
+
color: var(--color-primary);
|
|
626
|
+
cursor: pointer;
|
|
627
|
+
font: inherit;
|
|
628
|
+
width: calc(100% - 32px);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.msg-list-new:hover { background: var(--color-primary); color: #fff; }
|
|
632
|
+
|
|
633
|
+
/* ── Home panel ─────────────────────────── */
|
|
634
|
+
.home-greeting {
|
|
635
|
+
font-size: 20px;
|
|
636
|
+
font-weight: 600;
|
|
637
|
+
color: var(--color-text);
|
|
638
|
+
padding: 24px 18px 8px;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.home-start {
|
|
642
|
+
margin: 8px 18px;
|
|
643
|
+
padding: 14px;
|
|
644
|
+
border-radius: 12px;
|
|
645
|
+
border: 1px solid var(--gray-200);
|
|
646
|
+
background: var(--gray-50);
|
|
647
|
+
color: var(--color-text);
|
|
648
|
+
text-align: left;
|
|
649
|
+
cursor: pointer;
|
|
650
|
+
font: inherit;
|
|
651
|
+
font-size: 14px;
|
|
652
|
+
transition: background .15s, border-color .15s;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.home-start:hover { background: var(--gray-100); border-color: var(--color-primary); }
|
|
656
|
+
|
|
657
|
+
.home-status { margin: 8px 18px; font-size: 12px; }
|
|
658
|
+
.home-status--online { color: #16A34A; }
|
|
659
|
+
.home-status--offline { color: var(--gray-400); }
|
|
660
|
+
|
|
485
661
|
/* ── Messages ───────────────────────────── */
|
|
486
662
|
.messages {
|
|
487
663
|
flex: 1;
|
|
@@ -703,6 +879,17 @@ function ae(e) {
|
|
|
703
879
|
color: #fff;
|
|
704
880
|
}
|
|
705
881
|
|
|
882
|
+
/* ── Handoff Banner ─────────────────────── */
|
|
883
|
+
.handoff-banner {
|
|
884
|
+
margin: 6px 14px;
|
|
885
|
+
padding: 6px 10px;
|
|
886
|
+
border-radius: 8px;
|
|
887
|
+
background: rgba(249,115,22,.12);
|
|
888
|
+
color: var(--color-primary);
|
|
889
|
+
font-size: 12px;
|
|
890
|
+
text-align: center;
|
|
891
|
+
}
|
|
892
|
+
|
|
706
893
|
/* ── Footer / Input ─────────────────────── */
|
|
707
894
|
.chat-footer {
|
|
708
895
|
border-top: 1px solid var(--gray-100);
|
|
@@ -791,9 +978,9 @@ function ae(e) {
|
|
|
791
978
|
.emoji-item:hover { background: var(--gray-100); }
|
|
792
979
|
|
|
793
980
|
${_}
|
|
794
|
-
${
|
|
795
|
-
${o ?
|
|
796
|
-
${
|
|
981
|
+
${ie(a)}
|
|
982
|
+
${o ? R(o) : ""}
|
|
983
|
+
${ae}
|
|
797
984
|
@media (max-width: 480px) {
|
|
798
985
|
:host { bottom: 0; right: 0; left: 0; }
|
|
799
986
|
.chat-window {
|
|
@@ -809,58 +996,105 @@ ${ie}
|
|
|
809
996
|
`;
|
|
810
997
|
}
|
|
811
998
|
//#endregion
|
|
999
|
+
//#region src/core/timeago.ts
|
|
1000
|
+
function z(e) {
|
|
1001
|
+
return String(e).padStart(2, "0");
|
|
1002
|
+
}
|
|
1003
|
+
function B(e, t, n) {
|
|
1004
|
+
let r = Math.max(0, Math.floor((t - e) / 1e3));
|
|
1005
|
+
if (r < 60) return n === "vi" ? "vừa xong" : "just now";
|
|
1006
|
+
let i = Math.floor(r / 60);
|
|
1007
|
+
if (i < 60) return n === "vi" ? `${i} phút` : `${i} min`;
|
|
1008
|
+
let a = new Date(e);
|
|
1009
|
+
return `${z(a.getHours())}:${z(a.getMinutes())}`;
|
|
1010
|
+
}
|
|
1011
|
+
function se(e, t, n) {
|
|
1012
|
+
let r = (e) => {
|
|
1013
|
+
let t = new Date(e);
|
|
1014
|
+
return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
|
|
1015
|
+
}, i = Math.round((r(t) - r(e)) / 864e5);
|
|
1016
|
+
if (i <= 0) return n === "vi" ? "Hôm nay" : "Today";
|
|
1017
|
+
if (i === 1) return n === "vi" ? "Hôm qua" : "Yesterday";
|
|
1018
|
+
let a = new Date(e);
|
|
1019
|
+
return `${z(a.getDate())}/${z(a.getMonth() + 1)}/${a.getFullYear()}`;
|
|
1020
|
+
}
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region src/core/views.ts
|
|
1023
|
+
function V(e) {
|
|
1024
|
+
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1025
|
+
}
|
|
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}
|
|
1034
|
+
</button>`;
|
|
1035
|
+
}).join("")}</div>${r}`;
|
|
1036
|
+
}
|
|
1037
|
+
function le(e, t) {
|
|
1038
|
+
let n = e.status !== "offline", r = n ? t.statusOnline : t.statusPaused;
|
|
1039
|
+
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>
|
|
1043
|
+
`;
|
|
1044
|
+
}
|
|
1045
|
+
//#endregion
|
|
812
1046
|
//#region src/core/assets.ts
|
|
813
|
-
var
|
|
1047
|
+
var ue = "data:image/png;base64,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";
|
|
814
1048
|
//#endregion
|
|
815
1049
|
//#region src/core/history-pager.ts
|
|
816
|
-
function
|
|
1050
|
+
function H() {
|
|
817
1051
|
return {
|
|
818
1052
|
oldestCursor: null,
|
|
819
1053
|
hasMore: !1,
|
|
820
1054
|
cursorReady: !1
|
|
821
1055
|
};
|
|
822
1056
|
}
|
|
823
|
-
function
|
|
1057
|
+
function U(e) {
|
|
824
1058
|
return {
|
|
825
1059
|
oldestCursor: e.messages[0]?.id ?? null,
|
|
826
1060
|
hasMore: e.hasMore,
|
|
827
1061
|
cursorReady: e.messages.length > 0
|
|
828
1062
|
};
|
|
829
1063
|
}
|
|
830
|
-
function
|
|
1064
|
+
function de(e, t) {
|
|
831
1065
|
return {
|
|
832
1066
|
oldestCursor: t.messages[0]?.id ?? e.oldestCursor,
|
|
833
1067
|
hasMore: t.hasMore,
|
|
834
1068
|
cursorReady: e.cursorReady
|
|
835
1069
|
};
|
|
836
1070
|
}
|
|
837
|
-
function
|
|
1071
|
+
function fe(e, t) {
|
|
838
1072
|
return e.cursorReady && e.hasMore && !t && e.oldestCursor !== null;
|
|
839
1073
|
}
|
|
840
|
-
function
|
|
1074
|
+
function pe(e, t, n) {
|
|
841
1075
|
return e + (n - t);
|
|
842
1076
|
}
|
|
843
1077
|
//#endregion
|
|
844
1078
|
//#region src/core/confirm-dialog.ts
|
|
845
|
-
function
|
|
1079
|
+
function W(e) {
|
|
846
1080
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
847
1081
|
}
|
|
848
|
-
function
|
|
1082
|
+
function me(e, t) {
|
|
849
1083
|
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…";
|
|
850
1084
|
return new Promise((o) => {
|
|
851
1085
|
let s = document.createElement("div");
|
|
852
1086
|
s.className = "botiq-confirm-overlay", s.innerHTML = `
|
|
853
1087
|
<div class="botiq-confirm-dialog" role="dialog" aria-modal="true">
|
|
854
|
-
<div class="botiq-confirm-header">${
|
|
1088
|
+
<div class="botiq-confirm-header">${W(t.title)}</div>
|
|
855
1089
|
<div class="botiq-confirm-body">
|
|
856
|
-
<div class="botiq-confirm-reason">${
|
|
857
|
-
<pre class="botiq-confirm-params">${
|
|
1090
|
+
<div class="botiq-confirm-reason">${W(t.reason)}</div>
|
|
1091
|
+
<pre class="botiq-confirm-params">${W(t.paramsDisplay)}</pre>
|
|
858
1092
|
</div>
|
|
859
1093
|
<div class="botiq-confirm-actions">
|
|
860
|
-
<button class="botiq-confirm-yes" data-risk="${t.riskLevel}" type="button">${
|
|
861
|
-
<button class="botiq-confirm-no" type="button">${
|
|
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>
|
|
862
1096
|
</div>
|
|
863
|
-
${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${
|
|
1097
|
+
${t.autoConfirmAfterSec ? `<div class="botiq-confirm-countdown">${W(i)}<span class="botiq-confirm-secs">${t.autoConfirmAfterSec}</span>${W(a)}</div>` : ""}
|
|
864
1098
|
</div>`, e.appendChild(s);
|
|
865
1099
|
let c = !1, l = null, u = (t) => {
|
|
866
1100
|
c || (c = !0, l && clearInterval(l), e.removeChild(s), o(t));
|
|
@@ -874,39 +1108,16 @@ function B(e, t) {
|
|
|
874
1108
|
});
|
|
875
1109
|
}
|
|
876
1110
|
//#endregion
|
|
877
|
-
//#region src/core/timeago.ts
|
|
878
|
-
function V(e) {
|
|
879
|
-
return String(e).padStart(2, "0");
|
|
880
|
-
}
|
|
881
|
-
function H(e, t, n) {
|
|
882
|
-
let r = Math.max(0, Math.floor((t - e) / 1e3));
|
|
883
|
-
if (r < 60) return n === "vi" ? "vừa xong" : "just now";
|
|
884
|
-
let i = Math.floor(r / 60);
|
|
885
|
-
if (i < 60) return n === "vi" ? `${i} phút` : `${i} min`;
|
|
886
|
-
let a = new Date(e);
|
|
887
|
-
return `${V(a.getHours())}:${V(a.getMinutes())}`;
|
|
888
|
-
}
|
|
889
|
-
function U(e, t, n) {
|
|
890
|
-
let r = (e) => {
|
|
891
|
-
let t = new Date(e);
|
|
892
|
-
return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
|
|
893
|
-
}, i = Math.round((r(t) - r(e)) / 864e5);
|
|
894
|
-
if (i <= 0) return n === "vi" ? "Hôm nay" : "Today";
|
|
895
|
-
if (i === 1) return n === "vi" ? "Hôm qua" : "Yesterday";
|
|
896
|
-
let a = new Date(e);
|
|
897
|
-
return `${V(a.getDate())}/${V(a.getMonth() + 1)}/${a.getFullYear()}`;
|
|
898
|
-
}
|
|
899
|
-
//#endregion
|
|
900
1111
|
//#region src/core/emoji.ts
|
|
901
|
-
function
|
|
1112
|
+
function he(e, t) {
|
|
902
1113
|
let n = e.selectionStart ?? e.value.length, r = e.selectionEnd ?? e.value.length;
|
|
903
1114
|
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 }));
|
|
904
1115
|
}
|
|
905
1116
|
var G = null;
|
|
906
|
-
async function
|
|
1117
|
+
async function ge() {
|
|
907
1118
|
return G || (G = (await import("./emoji-data-DWiYsBZI.js")).EMOJIS, G);
|
|
908
1119
|
}
|
|
909
|
-
function
|
|
1120
|
+
function _e(e) {
|
|
910
1121
|
let t = document.createElement("button");
|
|
911
1122
|
t.type = "button", t.className = "emoji-btn", t.setAttribute("aria-label", "Emoji"), t.textContent = "🙂";
|
|
912
1123
|
let n = document.createElement("div");
|
|
@@ -914,7 +1125,7 @@ function ue(e) {
|
|
|
914
1125
|
let r = !1;
|
|
915
1126
|
return t.addEventListener("click", async () => {
|
|
916
1127
|
if (!r) {
|
|
917
|
-
let t = await
|
|
1128
|
+
let t = await ge();
|
|
918
1129
|
for (let r of t) {
|
|
919
1130
|
let t = document.createElement("button");
|
|
920
1131
|
t.type = "button", t.className = "emoji-item", t.textContent = r, t.addEventListener("click", () => {
|
|
@@ -931,8 +1142,8 @@ function ue(e) {
|
|
|
931
1142
|
}
|
|
932
1143
|
//#endregion
|
|
933
1144
|
//#region src/core/ui.ts
|
|
934
|
-
var
|
|
935
|
-
function
|
|
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) {
|
|
936
1147
|
if (!e || e.type === "icon") return K;
|
|
937
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>`;
|
|
938
1149
|
if (e.type === "initials") {
|
|
@@ -948,20 +1159,26 @@ function J(e) {
|
|
|
948
1159
|
let t = [];
|
|
949
1160
|
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;
|
|
950
1161
|
}
|
|
951
|
-
function
|
|
1162
|
+
function we(e) {
|
|
952
1163
|
return /^\|[\s\-:|]+\|$/.test(e);
|
|
953
1164
|
}
|
|
954
1165
|
function Y(e) {
|
|
955
1166
|
return e.replace(/^\|/, "").replace(/\|$/, "").split("|").map((e) => e.trim());
|
|
956
1167
|
}
|
|
957
|
-
function
|
|
1168
|
+
function Te(e, t) {
|
|
1169
|
+
return e ? `<div class="handoff-banner">⏳ ${q(t.waitingForHuman)}</div>` : "";
|
|
1170
|
+
}
|
|
1171
|
+
function Ee(e) {
|
|
1172
|
+
return e <= 0 ? "" : `<span class="unread-badge">${e > 9 ? "9+" : String(e)}</span>`;
|
|
1173
|
+
}
|
|
1174
|
+
function De(e) {
|
|
958
1175
|
let t = q(e).replace(/\x00/g, "").split("\n"), n = [], r = 0;
|
|
959
1176
|
for (; r < t.length;) {
|
|
960
1177
|
let e = t[r].trim();
|
|
961
1178
|
if (e.startsWith("|") && e.endsWith("|")) {
|
|
962
1179
|
let e = [];
|
|
963
1180
|
for (; r < t.length && t[r].trim().startsWith("|") && t[r].trim().endsWith("|");) e.push(t[r].trim()), r++;
|
|
964
|
-
let i = e.findIndex(
|
|
1181
|
+
let i = e.findIndex(we), a = i > 0 ? e.slice(0, i) : [], o = i >= 0 ? e.slice(i + 1) : e, s = "<table>";
|
|
965
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);
|
|
966
1183
|
continue;
|
|
967
1184
|
}
|
|
@@ -996,101 +1213,129 @@ function ve(e) {
|
|
|
996
1213
|
for (; n.length > 0 && n[n.length - 1] === "<br>";) n.pop();
|
|
997
1214
|
return n.join("");
|
|
998
1215
|
}
|
|
999
|
-
function
|
|
1216
|
+
function Oe(e, t) {
|
|
1000
1217
|
let { strings: n, content: r, botName: i, lang: a, now: o, contactInfo: s } = t;
|
|
1001
1218
|
if (e.messages.length === 0 && !e.isLoading) {
|
|
1002
1219
|
let e = r.suggestionChips.length > 0 ? `<div class="chips">${r.suggestionChips.map((e) => `<button class="chip">${q(e)}</button>`).join("")}</div>` : "";
|
|
1003
1220
|
return `
|
|
1004
1221
|
<div class="empty-state">
|
|
1005
|
-
${
|
|
1222
|
+
${Se}
|
|
1006
1223
|
<span class="greeting">${q(r.greeting || n.defaultGreeting)}</span>
|
|
1007
1224
|
${e}
|
|
1008
1225
|
</div>
|
|
1009
1226
|
`;
|
|
1010
1227
|
}
|
|
1011
1228
|
let c = "", l = e.messages.map((e) => {
|
|
1012
|
-
let t = e.ts ?? o, r =
|
|
1229
|
+
let t = e.ts ?? o, r = se(t, o, a), l = r === c ? "" : `<div class="day-sep"><span>${q(r)}</span></div>`;
|
|
1013
1230
|
c = r;
|
|
1014
|
-
let u = e.role === "assistant" ?
|
|
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>` : "";
|
|
1015
1232
|
return `${l}<div class="${`message ${e.role}${e.error ? " error" : ""}`}"><div class="message-bubble">${u}</div>${f}${p}${d}</div>`;
|
|
1016
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>" : "";
|
|
1017
1234
|
return u + l + d;
|
|
1018
1235
|
}
|
|
1019
|
-
function
|
|
1020
|
-
let { name:
|
|
1021
|
-
function
|
|
1022
|
-
e.setAttribute("data-position",
|
|
1023
|
-
let
|
|
1024
|
-
|
|
1025
|
-
<span class="icon-chat">${
|
|
1026
|
-
<span class="icon-close">${
|
|
1027
|
-
|
|
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) {
|
|
1239
|
+
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>
|
|
1244
|
+
<span class="unread-badge-slot"></span>
|
|
1245
|
+
`, 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 = `
|
|
1028
1246
|
<div class="chat-header">
|
|
1029
|
-
<div class="avatar">${
|
|
1247
|
+
<div class="avatar">${Ce(a.avatar)}</div>
|
|
1030
1248
|
<div class="header-text">
|
|
1031
|
-
<span class="bot-name">${q(
|
|
1249
|
+
<span class="bot-name">${q(i)}</span>
|
|
1032
1250
|
<span class="bot-status">${q(n.statusOnline)}</span>
|
|
1033
1251
|
</div>
|
|
1034
1252
|
<button class="close-btn" aria-label="${q(n.ariaCloseChat)}">×</button>
|
|
1035
1253
|
</div>
|
|
1036
|
-
<div class="
|
|
1037
|
-
|
|
1038
|
-
<div class="
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1254
|
+
<div class="chat-body">
|
|
1255
|
+
<div class="panel panel-home"></div>
|
|
1256
|
+
<div class="panel panel-messages"></div>
|
|
1257
|
+
<div class="panel panel-conversation">
|
|
1258
|
+
<div class="messages" id="messages-container" role="log" aria-live="polite" aria-atomic="false"></div>
|
|
1259
|
+
<div class="handoff-slot"></div>
|
|
1260
|
+
<div class="chat-footer">
|
|
1261
|
+
<div class="input-row">
|
|
1262
|
+
<textarea
|
|
1263
|
+
class="input"
|
|
1264
|
+
placeholder="${q(v.placeholder || n.inputPlaceholder)}"
|
|
1265
|
+
rows="1"
|
|
1266
|
+
maxlength="2000"
|
|
1267
|
+
aria-label="Message input"
|
|
1268
|
+
></textarea>
|
|
1269
|
+
<button class="send-btn" aria-label="${q(n.ariaSendMessage)}">
|
|
1270
|
+
${xe}
|
|
1271
|
+
</button>
|
|
1272
|
+
</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
|
+
</div>
|
|
1049
1277
|
</div>
|
|
1050
|
-
<a class="botiq-badge" href="${de}" target="_blank" rel="noopener noreferrer">
|
|
1051
|
-
${q(n.poweredBy)} <span class="botiq-badge-name">BotIQ</span>
|
|
1052
|
-
</a>
|
|
1053
1278
|
</div>
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
}),
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1279
|
+
<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>
|
|
1282
|
+
</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", () => {
|
|
1284
|
+
r.onNewConversation();
|
|
1285
|
+
}), f = c.querySelector(".nav-btn[data-view=\"home\"]"), p = c.querySelector(".nav-btn[data-view=\"messages\"]"), c.querySelectorAll(".nav-btn").forEach((e) => {
|
|
1286
|
+
e.addEventListener("click", () => {
|
|
1287
|
+
let t = e.dataset.view;
|
|
1288
|
+
r.onNavigate(t);
|
|
1289
|
+
});
|
|
1290
|
+
}), m = c.querySelector("#messages-container"), h = c.querySelector(".handoff-slot");
|
|
1291
|
+
let x = null;
|
|
1292
|
+
m.addEventListener("scroll", () => {
|
|
1293
|
+
m.scrollTop <= 48 && !x && (x = setTimeout(() => {
|
|
1294
|
+
x = null;
|
|
1295
|
+
}, 150), r.onScrollTop());
|
|
1296
|
+
}), g = c.querySelector(".input"), _ = c.querySelector(".send-btn"), g.addEventListener("input", () => {
|
|
1297
|
+
g.style.height = "auto", g.style.height = Math.min(g.scrollHeight, 100) + "px";
|
|
1298
|
+
}), 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();
|
|
1067
1303
|
});
|
|
1068
|
-
|
|
1304
|
+
S.insertBefore(C, _), c.querySelector(".chat-footer").appendChild(w), o.appendChild(c);
|
|
1069
1305
|
}
|
|
1070
|
-
function
|
|
1071
|
-
let e =
|
|
1072
|
-
!e ||
|
|
1306
|
+
function b() {
|
|
1307
|
+
let e = g.value.trim();
|
|
1308
|
+
!e || _.disabled || (g.value = "", g.style.height = "auto", r.onSend(e));
|
|
1073
1309
|
}
|
|
1074
|
-
function
|
|
1075
|
-
let
|
|
1076
|
-
|
|
1310
|
+
function x(e) {
|
|
1311
|
+
let a = m.scrollHeight - m.scrollTop - m.clientHeight < 80, o = m.scrollTop, s = m.scrollHeight;
|
|
1312
|
+
m.innerHTML = Oe(e, {
|
|
1077
1313
|
strings: n,
|
|
1078
|
-
content:
|
|
1079
|
-
botName:
|
|
1314
|
+
content: v,
|
|
1315
|
+
botName: i,
|
|
1080
1316
|
lang: t.widgetLanguage,
|
|
1081
1317
|
now: Date.now(),
|
|
1082
1318
|
contactInfo: t.contactInfo
|
|
1083
|
-
}),
|
|
1084
|
-
e.addEventListener("click", () => r(e.textContent ?? ""));
|
|
1085
|
-
}),
|
|
1086
|
-
e.addEventListener("click", () =>
|
|
1087
|
-
}),
|
|
1319
|
+
}), m.querySelectorAll(".chip").forEach((e) => {
|
|
1320
|
+
e.addEventListener("click", () => r.onSend(e.textContent ?? ""));
|
|
1321
|
+
}), m.querySelectorAll(".retry-btn").forEach((e) => {
|
|
1322
|
+
e.addEventListener("click", () => r.onRetry());
|
|
1323
|
+
}), a ? m.scrollTop = m.scrollHeight : m.scrollTop = pe(o, s, m.scrollHeight);
|
|
1088
1324
|
}
|
|
1089
|
-
function
|
|
1090
|
-
e.isOpen ? (
|
|
1325
|
+
function S(e) {
|
|
1326
|
+
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) => {
|
|
1329
|
+
e.addEventListener("click", () => {
|
|
1330
|
+
let t = e.dataset.session;
|
|
1331
|
+
t && r.onOpenConversation(t);
|
|
1332
|
+
});
|
|
1333
|
+
}), u.querySelector(".msg-list-new")?.addEventListener("click", () => {
|
|
1334
|
+
r.onNewConversation();
|
|
1335
|
+
}));
|
|
1091
1336
|
}
|
|
1092
|
-
function
|
|
1093
|
-
return
|
|
1337
|
+
function C(e) {
|
|
1338
|
+
return me(o, {
|
|
1094
1339
|
...e,
|
|
1095
1340
|
labels: {
|
|
1096
1341
|
confirm: n.confirmYes,
|
|
@@ -1101,15 +1346,23 @@ function be(e, t, n, r, i, a, o) {
|
|
|
1101
1346
|
});
|
|
1102
1347
|
}
|
|
1103
1348
|
return {
|
|
1104
|
-
mount:
|
|
1105
|
-
update:
|
|
1106
|
-
confirmAction:
|
|
1349
|
+
mount: y,
|
|
1350
|
+
update: S,
|
|
1351
|
+
confirmAction: C
|
|
1107
1352
|
};
|
|
1108
1353
|
}
|
|
1109
1354
|
//#endregion
|
|
1110
1355
|
//#region src/i18n/vi.ts
|
|
1111
|
-
var
|
|
1356
|
+
var Ae = {
|
|
1112
1357
|
statusOnline: "Trực tuyến",
|
|
1358
|
+
statusPaused: "Tạm ngừng",
|
|
1359
|
+
navHome: "Trang chủ",
|
|
1360
|
+
navMessages: "Tin nhắn",
|
|
1361
|
+
homeGreeting: "Xin chào 👋",
|
|
1362
|
+
startConversation: "Bắt đầu trò chuyện",
|
|
1363
|
+
newConversation: "Cuộc trò chuyện mới",
|
|
1364
|
+
noConversations: "Chưa có cuộc trò chuyện nào",
|
|
1365
|
+
waitingForHuman: "Đang chờ nhân viên…",
|
|
1113
1366
|
inputPlaceholder: "Nhắn tin...",
|
|
1114
1367
|
defaultGreeting: "Xin chào! Tôi có thể giúp gì cho bạn?",
|
|
1115
1368
|
errorMessage: "Xin lỗi, không thể kết nối. Vui lòng thử lại.",
|
|
@@ -1133,8 +1386,16 @@ var xe = {
|
|
|
1133
1386
|
confirmCountdownPrefix: "Tự động xác nhận sau ",
|
|
1134
1387
|
confirmCountdownSuffix: "s…",
|
|
1135
1388
|
retry: "Thử lại"
|
|
1136
|
-
},
|
|
1389
|
+
}, X = {
|
|
1137
1390
|
statusOnline: "Online",
|
|
1391
|
+
statusPaused: "Paused",
|
|
1392
|
+
navHome: "Home",
|
|
1393
|
+
navMessages: "Messages",
|
|
1394
|
+
homeGreeting: "Hi there 👋",
|
|
1395
|
+
startConversation: "Start a conversation",
|
|
1396
|
+
newConversation: "New conversation",
|
|
1397
|
+
noConversations: "No conversations yet",
|
|
1398
|
+
waitingForHuman: "Waiting for a teammate…",
|
|
1138
1399
|
inputPlaceholder: "Type a message...",
|
|
1139
1400
|
defaultGreeting: "Hello! How can I help you?",
|
|
1140
1401
|
errorMessage: "Sorry, something went wrong. Please try again.",
|
|
@@ -1161,20 +1422,20 @@ var xe = {
|
|
|
1161
1422
|
};
|
|
1162
1423
|
//#endregion
|
|
1163
1424
|
//#region src/i18n/index.ts
|
|
1164
|
-
function
|
|
1165
|
-
return e === "en" ?
|
|
1425
|
+
function Z(e) {
|
|
1426
|
+
return e === "en" ? X : Ae;
|
|
1166
1427
|
}
|
|
1167
1428
|
//#endregion
|
|
1168
1429
|
//#region src/core/declarative-executor.ts
|
|
1169
|
-
var
|
|
1430
|
+
var je = /^[a-zA-Z0-9_-]*$/;
|
|
1170
1431
|
function Q(e, t) {
|
|
1171
1432
|
return e.replace(/\{([a-zA-Z][a-zA-Z0-9_]*)\}/g, (e, n) => {
|
|
1172
1433
|
let r = t[n], i = r == null ? "" : String(r);
|
|
1173
|
-
if (!
|
|
1434
|
+
if (!je.test(i)) throw Error(`Unsafe param value for "${n}"`);
|
|
1174
1435
|
return i;
|
|
1175
1436
|
});
|
|
1176
1437
|
}
|
|
1177
|
-
function
|
|
1438
|
+
function Me(e, t) {
|
|
1178
1439
|
let n = typeof location < "u" ? location.origin : "https://placeholder.local", r, i;
|
|
1179
1440
|
try {
|
|
1180
1441
|
r = new URL(e, n), i = new URL(t.replace(/\{[a-zA-Z][a-zA-Z0-9_]*\}/g, "x"), n);
|
|
@@ -1184,7 +1445,7 @@ function Ce(e, t) {
|
|
|
1184
1445
|
if (!/^https?:$/.test(r.protocol)) throw Error("Navigation must be http(s)");
|
|
1185
1446
|
if (r.origin !== i.origin) throw Error("Navigation origin not allowed");
|
|
1186
1447
|
}
|
|
1187
|
-
async function
|
|
1448
|
+
async function Ne(e, t) {
|
|
1188
1449
|
let n = e.config;
|
|
1189
1450
|
if (e.operation === "click") {
|
|
1190
1451
|
let e = Q(String(n.selectorTemplate ?? ""), t), r = document.querySelector(e);
|
|
@@ -1203,14 +1464,14 @@ async function we(e, t) {
|
|
|
1203
1464
|
}
|
|
1204
1465
|
if (e.operation === "navigate") {
|
|
1205
1466
|
let e = String(n.urlTemplate ?? ""), r = Q(e, t);
|
|
1206
|
-
|
|
1467
|
+
Me(r, e), location.assign(r);
|
|
1207
1468
|
return;
|
|
1208
1469
|
}
|
|
1209
1470
|
throw Error(`Unknown operation: ${String(e.operation)}`);
|
|
1210
1471
|
}
|
|
1211
1472
|
//#endregion
|
|
1212
1473
|
//#region src/core/page-action-executor.ts
|
|
1213
|
-
async function
|
|
1474
|
+
async function Pe(e, t) {
|
|
1214
1475
|
let n = t.registry.get(e.actionName);
|
|
1215
1476
|
if (!n) return {
|
|
1216
1477
|
status: "unknown",
|
|
@@ -1235,26 +1496,81 @@ async function Te(e, t) {
|
|
|
1235
1496
|
//#endregion
|
|
1236
1497
|
//#region src/builds/npm/index.ts
|
|
1237
1498
|
var $ = 10;
|
|
1238
|
-
function
|
|
1239
|
-
if (
|
|
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;
|
|
1240
1501
|
let t = p(e);
|
|
1241
|
-
|
|
1242
|
-
let n = t.apiKey.slice(-16), r =
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1502
|
+
y();
|
|
1503
|
+
let n = t.apiKey.slice(-16), r = C(), i = b(n), a = x(n, i);
|
|
1504
|
+
F({
|
|
1505
|
+
messages: a,
|
|
1506
|
+
view: "home",
|
|
1507
|
+
currentSessionId: i
|
|
1508
|
+
});
|
|
1509
|
+
let o = H(), s = document.createElement("div");
|
|
1510
|
+
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 });
|
|
1517
|
+
} catch {} finally {
|
|
1518
|
+
P().escalated && P().isOpen && g();
|
|
1519
|
+
}
|
|
1520
|
+
}, 1e4);
|
|
1521
|
+
}
|
|
1247
1522
|
f(t.apiKey, t.apiUrl).then((e) => {
|
|
1248
|
-
if (
|
|
1523
|
+
if (l) return;
|
|
1249
1524
|
if (e === null) {
|
|
1250
|
-
console.warn("[BotIQ] Widget not authorised on this origin — skipped mount"),
|
|
1525
|
+
console.warn("[BotIQ] Widget not authorised on this origin — skipped mount"), s.remove();
|
|
1251
1526
|
return;
|
|
1252
1527
|
}
|
|
1253
|
-
|
|
1254
|
-
let n =
|
|
1255
|
-
|
|
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
|
|
1537
|
+
});
|
|
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],
|
|
1547
|
+
view: "conversation",
|
|
1548
|
+
unreadCount: P().unreadCount + 1
|
|
1549
|
+
});
|
|
1550
|
+
}, e.proactive.delayMs ?? 8e3)), a.length > 0 && k(i);
|
|
1256
1551
|
});
|
|
1257
|
-
function
|
|
1552
|
+
async function _() {
|
|
1553
|
+
F({ conversations: await ee(t.apiUrl, t.apiKey, r) });
|
|
1554
|
+
}
|
|
1555
|
+
function v(e) {
|
|
1556
|
+
F({ view: e }), e === "messages" && _();
|
|
1557
|
+
}
|
|
1558
|
+
function T() {
|
|
1559
|
+
F({
|
|
1560
|
+
view: "conversation",
|
|
1561
|
+
currentSessionId: w(),
|
|
1562
|
+
messages: [],
|
|
1563
|
+
escalated: !1
|
|
1564
|
+
}), o = H();
|
|
1565
|
+
}
|
|
1566
|
+
function E(e) {
|
|
1567
|
+
F({
|
|
1568
|
+
view: "conversation",
|
|
1569
|
+
currentSessionId: e,
|
|
1570
|
+
messages: x(n, e)
|
|
1571
|
+
}), o = H(), k(e);
|
|
1572
|
+
}
|
|
1573
|
+
function O(e) {
|
|
1258
1574
|
let t = Date.parse(e.createdAt);
|
|
1259
1575
|
return {
|
|
1260
1576
|
role: e.role,
|
|
@@ -1262,83 +1578,90 @@ function Ee(e) {
|
|
|
1262
1578
|
...Number.isFinite(t) ? { ts: t } : {}
|
|
1263
1579
|
};
|
|
1264
1580
|
}
|
|
1265
|
-
async function
|
|
1266
|
-
let
|
|
1267
|
-
|
|
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();
|
|
1268
1584
|
}
|
|
1269
|
-
async function
|
|
1270
|
-
if (
|
|
1271
|
-
|
|
1585
|
+
async function A() {
|
|
1586
|
+
if (fe(o, P().loadingOlder)) {
|
|
1587
|
+
F({ loadingOlder: !0 });
|
|
1272
1588
|
try {
|
|
1273
|
-
let e = await
|
|
1274
|
-
|
|
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);
|
|
1275
1591
|
} finally {
|
|
1276
|
-
|
|
1592
|
+
F({ loadingOlder: !1 });
|
|
1277
1593
|
}
|
|
1278
1594
|
}
|
|
1279
1595
|
}
|
|
1280
|
-
let
|
|
1281
|
-
async function
|
|
1282
|
-
|
|
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 });
|
|
1283
1600
|
try {
|
|
1284
|
-
let
|
|
1601
|
+
let o = await te(t.apiUrl, t.apiKey, i, e, P().messages.filter((e) => !e.error), u, r), s = {
|
|
1285
1602
|
role: "assistant",
|
|
1286
|
-
content:
|
|
1603
|
+
content: o.reply,
|
|
1287
1604
|
ts: Date.now(),
|
|
1288
|
-
...
|
|
1605
|
+
...o.error ? { error: !0 } : {}
|
|
1289
1606
|
};
|
|
1290
|
-
if (
|
|
1291
|
-
messages: [...
|
|
1607
|
+
if (F({
|
|
1608
|
+
messages: [...P().messages, s],
|
|
1292
1609
|
isLoading: !1
|
|
1293
|
-
}),
|
|
1294
|
-
|
|
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) {
|
|
1613
|
+
let e = d;
|
|
1295
1614
|
try {
|
|
1296
|
-
let t = await
|
|
1297
|
-
registry:
|
|
1615
|
+
let t = await Pe(o.pageAction, {
|
|
1616
|
+
registry: D,
|
|
1298
1617
|
confirm: (t) => e.confirmAction(t)
|
|
1299
1618
|
});
|
|
1300
|
-
if (!
|
|
1301
|
-
let
|
|
1619
|
+
if (!d) return;
|
|
1620
|
+
let r = {
|
|
1302
1621
|
role: "assistant",
|
|
1303
|
-
content: t.status === "executed" ?
|
|
1622
|
+
content: t.status === "executed" ? u.actionDone : t.status === "cancelled" ? u.actionCancelled : t.status === "unknown" ? `⚠ ${t.message ?? u.actionUnavailable}` : `⚠ ${t.message ?? u.actionError}`,
|
|
1304
1623
|
ts: Date.now()
|
|
1305
1624
|
};
|
|
1306
|
-
|
|
1625
|
+
F({ messages: [...P().messages, r] }), S(n, i, [r]);
|
|
1307
1626
|
} catch {
|
|
1308
|
-
if (!
|
|
1627
|
+
if (!d) return;
|
|
1309
1628
|
let e = {
|
|
1310
1629
|
role: "assistant",
|
|
1311
|
-
content:
|
|
1630
|
+
content: u.actionError,
|
|
1312
1631
|
ts: Date.now()
|
|
1313
1632
|
};
|
|
1314
|
-
|
|
1633
|
+
F({ messages: [...P().messages, e] }), S(n, i, [e]);
|
|
1315
1634
|
}
|
|
1316
1635
|
}
|
|
1317
1636
|
} catch {
|
|
1318
|
-
|
|
1637
|
+
F({ isLoading: !1 });
|
|
1319
1638
|
}
|
|
1320
1639
|
}
|
|
1321
|
-
async function
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
let t = {
|
|
1640
|
+
async function I(e) {
|
|
1641
|
+
if (P().isLoading) return;
|
|
1642
|
+
M = e;
|
|
1643
|
+
let t = P().currentSessionId, r = {
|
|
1325
1644
|
role: "user",
|
|
1326
1645
|
content: e,
|
|
1327
1646
|
ts: Date.now()
|
|
1328
1647
|
};
|
|
1329
|
-
|
|
1648
|
+
F({ messages: [...P().messages, r] }), S(n, t, [r]), await N(e);
|
|
1330
1649
|
}
|
|
1331
|
-
function
|
|
1332
|
-
if (
|
|
1333
|
-
let e =
|
|
1334
|
-
|
|
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);
|
|
1335
1654
|
}
|
|
1336
|
-
function
|
|
1337
|
-
|
|
1655
|
+
function R() {
|
|
1656
|
+
let e = !P().isOpen;
|
|
1657
|
+
F({
|
|
1658
|
+
isOpen: e,
|
|
1659
|
+
...e ? { unreadCount: 0 } : {}
|
|
1660
|
+
});
|
|
1338
1661
|
}
|
|
1339
1662
|
return () => {
|
|
1340
|
-
|
|
1663
|
+
l = !0, d = null, c(), m && clearTimeout(m), h &&= (clearTimeout(h), null), s.remove();
|
|
1341
1664
|
};
|
|
1342
1665
|
}
|
|
1343
1666
|
//#endregion
|
|
1344
|
-
export {
|
|
1667
|
+
export { Fe as t };
|
package/dist/sdk/react.js
CHANGED
package/dist/sdk/vue.js
CHANGED