@spilki/widget 1.0.33 → 1.0.34
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/bootstrap.es.js +240 -206
- package/dist/bootstrap.es.js.map +1 -1
- package/dist/bootstrap.umd.js +7 -7
- package/dist/bootstrap.umd.js.map +1 -1
- package/dist/core/transport.d.ts +4 -1
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/widget.es.js +243 -209
- package/dist/widget.es.js.map +1 -1
- package/dist/widget.umd.js +7 -7
- package/dist/widget.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/widget.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const F = `
|
|
2
2
|
<style>
|
|
3
3
|
:host {
|
|
4
4
|
all: initial;
|
|
@@ -71,13 +71,13 @@ const W = `
|
|
|
71
71
|
<span class="badge" hidden aria-hidden="true">0</span>
|
|
72
72
|
</button>
|
|
73
73
|
`;
|
|
74
|
-
function
|
|
74
|
+
function R(r) {
|
|
75
75
|
const e = document.createElement("div");
|
|
76
|
-
e.setAttribute("part", "bubble-root"), e.setAttribute("data-position",
|
|
76
|
+
e.setAttribute("part", "bubble-root"), e.setAttribute("data-position", r.position), e.style.setProperty("--spilki-accent", r.color);
|
|
77
77
|
const t = e.attachShadow({ mode: "open" });
|
|
78
|
-
t.innerHTML =
|
|
78
|
+
t.innerHTML = F;
|
|
79
79
|
const s = t.querySelector("button"), i = t.querySelector(".badge");
|
|
80
|
-
return s.addEventListener("click", () =>
|
|
80
|
+
return s.addEventListener("click", () => r.onClick()), {
|
|
81
81
|
element: e,
|
|
82
82
|
mount() {
|
|
83
83
|
document.body.appendChild(e);
|
|
@@ -85,20 +85,20 @@ function F(o) {
|
|
|
85
85
|
destroy() {
|
|
86
86
|
e.remove();
|
|
87
87
|
},
|
|
88
|
-
setOpen(
|
|
89
|
-
s.setAttribute("aria-expanded", String(
|
|
88
|
+
setOpen(l) {
|
|
89
|
+
s.setAttribute("aria-expanded", String(l)), s.setAttribute("aria-label", l ? "Close chat" : "Open chat");
|
|
90
90
|
},
|
|
91
|
-
setBadge(
|
|
92
|
-
const n = Math.max(0,
|
|
91
|
+
setBadge(l) {
|
|
92
|
+
const n = Math.max(0, l), a = s.getAttribute("aria-expanded") === "true";
|
|
93
93
|
if (n === 0) {
|
|
94
|
-
i.toggleAttribute("hidden", !0), i.textContent = "0",
|
|
94
|
+
i.toggleAttribute("hidden", !0), i.textContent = "0", a || s.setAttribute("aria-label", "Open chat");
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
-
i.toggleAttribute("hidden", !1), i.textContent = n > 99 ? "99+" : String(n),
|
|
97
|
+
i.toggleAttribute("hidden", !1), i.textContent = n > 99 ? "99+" : String(n), a || s.setAttribute("aria-label", `Open chat, ${n} unread`);
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
-
const
|
|
101
|
+
const K = "https://api.spilki.app", I = {
|
|
102
102
|
welcome: "Hi! I'm your assistant.",
|
|
103
103
|
placeholder: "Type a message…",
|
|
104
104
|
sendLabel: "Send",
|
|
@@ -109,57 +109,57 @@ const R = "https://api.spilki.app", C = {
|
|
|
109
109
|
offline: "Unable to connect. Please try again later.",
|
|
110
110
|
title: "Spilki Assistant"
|
|
111
111
|
}, w = {
|
|
112
|
-
apiBase:
|
|
112
|
+
apiBase: K,
|
|
113
113
|
position: "bottom-right",
|
|
114
114
|
theme: "auto",
|
|
115
115
|
color: "#6366f1",
|
|
116
|
-
welcome:
|
|
116
|
+
welcome: I.welcome,
|
|
117
117
|
persist: !0,
|
|
118
118
|
sound: !0,
|
|
119
|
-
i18n:
|
|
119
|
+
i18n: I
|
|
120
120
|
};
|
|
121
|
-
function
|
|
122
|
-
var t, s, i,
|
|
123
|
-
const e = { ...
|
|
121
|
+
function H(r) {
|
|
122
|
+
var t, s, i, o, l, n, a, g;
|
|
123
|
+
const e = { ...I, ...(t = r.i18n) != null ? t : {} };
|
|
124
124
|
return {
|
|
125
125
|
...w,
|
|
126
|
-
...
|
|
127
|
-
apiBase: (s =
|
|
126
|
+
...r,
|
|
127
|
+
apiBase: (s = r.apiBase) != null ? s : w.apiBase,
|
|
128
128
|
i18n: e,
|
|
129
|
-
welcome: (i =
|
|
130
|
-
position: (
|
|
131
|
-
theme: (
|
|
132
|
-
color: (n =
|
|
133
|
-
persist: (
|
|
134
|
-
sound: (g =
|
|
129
|
+
welcome: (i = r.welcome) != null ? i : e.welcome,
|
|
130
|
+
position: (o = r.position) != null ? o : w.position,
|
|
131
|
+
theme: (l = r.theme) != null ? l : w.theme,
|
|
132
|
+
color: (n = r.color) != null ? n : w.color,
|
|
133
|
+
persist: (a = r.persist) != null ? a : w.persist,
|
|
134
|
+
sound: (g = r.sound) != null ? g : w.sound
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
|
-
function
|
|
138
|
-
return typeof crypto != "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${
|
|
137
|
+
function z(r = "msg") {
|
|
138
|
+
return typeof crypto != "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${r}-${Math.random().toString(16).slice(2)}`;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
141
|
-
var
|
|
142
|
-
return (e = (
|
|
140
|
+
function _() {
|
|
141
|
+
var r, e;
|
|
142
|
+
return (e = (r = window.matchMedia) == null ? void 0 : r.call(window, "(prefers-color-scheme: dark)").matches) != null ? e : !1;
|
|
143
143
|
}
|
|
144
|
-
function P(
|
|
145
|
-
return
|
|
144
|
+
function P(r) {
|
|
145
|
+
return r === "light" || r === "dark" ? r : _() ? "dark" : "light";
|
|
146
146
|
}
|
|
147
|
-
function
|
|
148
|
-
return
|
|
147
|
+
function S(r, e = 30) {
|
|
148
|
+
return r.slice(-e);
|
|
149
149
|
}
|
|
150
|
-
function
|
|
151
|
-
if (!
|
|
150
|
+
function G(r) {
|
|
151
|
+
if (!r || r.length === 0) return;
|
|
152
152
|
const e = window.location.origin;
|
|
153
|
-
|
|
154
|
-
`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${
|
|
153
|
+
r.includes(e) || console.warn(
|
|
154
|
+
`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
|
-
const
|
|
158
|
-
function
|
|
159
|
-
const e = new Date(
|
|
160
|
-
return n ===
|
|
157
|
+
const q = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
158
|
+
function Y(r) {
|
|
159
|
+
const e = new Date(r), t = /* @__PURE__ */ new Date(), s = (a) => String(a).padStart(2, "0"), i = `${s(e.getHours())}:${s(e.getMinutes())}`, o = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(), l = o - 864e5, n = new Date(e.getFullYear(), e.getMonth(), e.getDate()).getTime();
|
|
160
|
+
return n === o ? `Today at ${i}` : n === l ? `Yesterday at ${i}` : `${q[e.getMonth()]} ${e.getDate()}, ${i}`;
|
|
161
161
|
}
|
|
162
|
-
const
|
|
162
|
+
const V = ':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-text-light: #0f172a;--spilki-text-dark: #f8fafc;--spilki-border-light: rgba(15, 23, 42, .1);--spilki-border-dark: rgba(148, 163, 184, .25);--spilki-muted-light: #64748b;--spilki-muted-dark: #94a3b8;--spilki-shadow: 0 10px 40px rgba(15, 23, 42, .2);--spilki-radius: 16px;--spilki-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;color:inherit;font-family:var(--spilki-font)}.wrapper{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background:var(--spilki-surface);color:var(--spilki-text);border-radius:var(--spilki-radius);box-shadow:var(--spilki-shadow);border:1px solid var(--spilki-border);overflow:hidden}header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--spilki-surface);border-bottom:1px solid var(--spilki-border)}header h1{font-size:1rem;margin:0;display:flex;align-items:center;gap:.5rem}header button.close{border:none;background:transparent;color:inherit;font-size:1.25rem;cursor:pointer}header button.close:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}header .status-dot{width:10px;height:10px;border-radius:999px;background:var(--spilki-accent)}.messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem;scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent;scroll-behavior:smooth}.message{display:flex;flex-direction:column;gap:.15rem;max-width:85%;width:fit-content;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}.message.user{align-self:flex-end;align-items:flex-end}.message .bubble{padding:.6rem .8rem;border-radius:1rem;background:#6366f126}.message.user .bubble{background:var(--spilki-accent);color:#fff}.message.bot .bubble{background:#94a3b826}.msg-time{font-size:.7rem;color:var(--spilki-muted);margin-top:2px}.date-separator{display:flex;align-items:center;gap:.5rem;padding:.4rem 0;font-size:.72rem;color:var(--spilki-muted);white-space:nowrap}.date-separator:before,.date-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.scroll-bottom{position:absolute;right:1rem;bottom:88px;width:38px;height:38px;border:none;border-radius:999px;background:var(--spilki-accent);color:#fff;box-shadow:0 8px 20px #0f172a40;cursor:pointer;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .18s ease,transform .18s ease;z-index:3}.scroll-bottom.visible{opacity:1;pointer-events:auto;transform:translateY(0)}.scroll-arrow{font-size:.8rem;line-height:1}.scroll-count{position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;border-radius:999px;background:#ef4444;color:#fff;font-size:10px;line-height:16px;text-align:center;padding:0 4px}.suggested-replies{display:flex;flex-wrap:wrap;gap:.4rem;padding:.5rem 1rem;border-top:1px solid var(--spilki-border)}.suggested-chip{border:1px solid var(--spilki-accent);background:transparent;color:var(--spilki-accent);border-radius:999px;padding:.35rem .75rem;font-size:.8rem;font-family:inherit;cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background .15s,color .15s}.suggested-chip:hover{background:var(--spilki-accent);color:#fff}.suggested-chip:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.input-area{padding:.5rem .75rem}.input-wrap{display:flex;align-items:center;border:1px solid var(--spilki-border);border-radius:1.5rem;background:transparent;padding-left:0}.input-area textarea{flex:1;resize:none;min-height:2.4rem;max-height:6rem;border:0;border-radius:0;outline:0;background:transparent;box-shadow:none;-webkit-box-shadow:none;-webkit-appearance:none;appearance:none;padding:.55rem 0 .55rem 1rem;font-family:inherit;font-size:.95rem;color:var(--spilki-text);scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent}.input-actions{display:flex;align-items:center;gap:.25rem;padding:.25rem .35rem;flex-shrink:0}.input-actions button{width:32px;height:32px;display:grid;place-items:center;border:none;border-radius:50%;cursor:pointer;padding:0;transition:opacity .15s}.emoji-btn{background:#6366f126;color:var(--spilki-accent)}.emoji-btn:hover{background:#6366f140}.emoji-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:1px}.send-btn{background:var(--spilki-accent);color:#fff}.send-btn:hover{opacity:.85}.send-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.emoji-picker{position:absolute;right:1rem;bottom:80px;width:240px;max-height:180px;overflow-y:auto;display:none;grid-template-columns:repeat(10,minmax(0,1fr));gap:.25rem;padding:.5rem;background:var(--spilki-surface);border:1px solid var(--spilki-border);border-radius:12px;box-shadow:0 14px 30px #0f172a40;z-index:4}.emoji-option{width:100%;aspect-ratio:1;border:none;border-radius:8px;background:transparent;font-size:1rem;cursor:pointer}.emoji-option:hover,.emoji-option:focus-visible{background:#94a3b833}.typing{font-size:.75rem;color:var(--spilki-muted);padding:0 1rem .75rem}.typing:after{content:"";animation:dots 1.2s steps(4,end) infinite}@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}.offline{font-size:.8rem;padding:0 1rem;color:#f97316}:host([data-theme="dark"]){--spilki-surface: var(--spilki-bg-dark);--spilki-text: var(--spilki-text-dark);--spilki-border: var(--spilki-border-dark);--spilki-muted: var(--spilki-muted-dark)}:host([data-theme="dark"]) .messages,:host([data-theme="dark"]) .input-area textarea,:host([data-theme="dark"]) .emoji-picker{scrollbar-color:rgba(255,255,255,.2) transparent}:host([data-theme="light"]){--spilki-surface: var(--spilki-bg-light);--spilki-text: var(--spilki-text-light);--spilki-border: var(--spilki-border-light);--spilki-muted: var(--spilki-muted-light)}:host([data-theme="dark"]) .message .bubble{background:#94a3b81f}:host([data-theme="dark"]) .message.bot .bubble{background:#6366f126}:host([data-theme="dark"]) .input-area textarea{background:transparent}.messages::-webkit-scrollbar,.input-area textarea::-webkit-scrollbar,.emoji-picker::-webkit-scrollbar{width:6px}.messages::-webkit-scrollbar-track,.input-area textarea::-webkit-scrollbar-track,.emoji-picker::-webkit-scrollbar-track{background:transparent}.messages::-webkit-scrollbar-thumb,.input-area textarea::-webkit-scrollbar-thumb,.emoji-picker::-webkit-scrollbar-thumb{background:#94a3b84d;border-radius:999px}.messages::-webkit-scrollbar-thumb:hover,.input-area textarea::-webkit-scrollbar-thumb:hover,.emoji-picker::-webkit-scrollbar-thumb:hover{background:#94a3b880}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb{background:#fff3}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb:hover{background:#ffffff59}.conversation-separator{display:flex;align-items:center;gap:.5rem;padding:.5rem 0;cursor:pointer;user-select:none;font-size:.7rem;color:var(--spilki-muted);white-space:nowrap}.conversation-separator:before,.conversation-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.conversation-separator:hover{color:var(--spilki-text)}.conversation-separator:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}.conversation-history{display:none;flex-direction:column;gap:.5rem}.conversation-history.expanded{display:flex}', D = 24 * 60 * 60 * 1e3, J = 2 * 60 * 1e3, X = 200, Z = [
|
|
163
163
|
"😀",
|
|
164
164
|
"😂",
|
|
165
165
|
"🤣",
|
|
@@ -211,10 +211,10 @@ const Y = ':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-t
|
|
|
211
211
|
"☕",
|
|
212
212
|
"🍕"
|
|
213
213
|
];
|
|
214
|
-
function
|
|
215
|
-
return
|
|
214
|
+
function x(r) {
|
|
215
|
+
return r.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
216
216
|
}
|
|
217
|
-
class
|
|
217
|
+
class Q {
|
|
218
218
|
constructor(e) {
|
|
219
219
|
this.options = e, this.relativeTimeFormat = new Intl.RelativeTimeFormat(void 0, {
|
|
220
220
|
numeric: "auto"
|
|
@@ -229,9 +229,9 @@ class Z {
|
|
|
229
229
|
day: "numeric",
|
|
230
230
|
year: "numeric"
|
|
231
231
|
}), this.renderedMessages = [], this.focusable = [], this.seenIds = /* @__PURE__ */ new Set(), this.lastTimelineMessage = null, this.scrollUnreadCount = 0, this.emojiPickerOpen = !1, this.open = !1, this.host = document.createElement("div"), this.host.setAttribute("part", "panel-root"), this.host.style.position = "fixed", this.host.style.bottom = "96px", this.host.style[e.position === "bottom-right" ? "right" : "left"] = "24px", this.host.style.width = "360px", this.host.style.maxWidth = "calc(100vw - 32px)", this.host.style.height = "520px", this.host.style.display = "none", this.host.style.zIndex = "2147483001", this.shadow = this.host.attachShadow({ mode: "open" });
|
|
232
|
-
const t =
|
|
232
|
+
const t = x(e.i18n.title), s = x(e.i18n.typing), i = x(e.i18n.offline), o = x(e.i18n.placeholder), l = x(e.i18n.sendLabel);
|
|
233
233
|
this.shadow.innerHTML = `
|
|
234
|
-
<style>${
|
|
234
|
+
<style>${V}</style>
|
|
235
235
|
<div class="wrapper" role="dialog" aria-modal="true" aria-label="${t}">
|
|
236
236
|
<header>
|
|
237
237
|
<h1><span class="status-dot" aria-hidden="true"></span>${t}</h1>
|
|
@@ -248,7 +248,7 @@ class Z {
|
|
|
248
248
|
<div class="emoji-picker" aria-label="Emoji picker"></div>
|
|
249
249
|
<div class="input-area">
|
|
250
250
|
<div class="input-wrap">
|
|
251
|
-
<textarea rows="1" placeholder="${
|
|
251
|
+
<textarea rows="1" placeholder="${o}" aria-label="${o}"></textarea>
|
|
252
252
|
<div class="input-actions">
|
|
253
253
|
<button class="emoji-btn" type="button" aria-label="Insert emoji">
|
|
254
254
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
@@ -258,7 +258,7 @@ class Z {
|
|
|
258
258
|
<circle cx="15" cy="10" r="1" fill="currentColor"/>
|
|
259
259
|
</svg>
|
|
260
260
|
</button>
|
|
261
|
-
<button type="button" class="send-btn" aria-label="${
|
|
261
|
+
<button type="button" class="send-btn" aria-label="${l}">
|
|
262
262
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
263
263
|
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" fill="currentColor"/>
|
|
264
264
|
</svg>
|
|
@@ -278,23 +278,23 @@ class Z {
|
|
|
278
278
|
this.options.onClose();
|
|
279
279
|
}
|
|
280
280
|
}, this.handleShadowKeydown = (n) => {
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
281
|
+
const a = n;
|
|
282
|
+
if (a.key === "Escape") {
|
|
283
283
|
if (this.emojiPickerOpen) {
|
|
284
|
-
|
|
284
|
+
a.preventDefault(), this.closeEmojiPicker();
|
|
285
285
|
return;
|
|
286
286
|
}
|
|
287
287
|
this.options.onClose();
|
|
288
288
|
}
|
|
289
|
-
|
|
289
|
+
a.key === "Tab" && this.trapFocus(a);
|
|
290
290
|
}, this.handleFocusin = () => this.collectFocusable(), this.handleMessagesScroll = () => this.updateScrollBottomState(), this.handleScrollBottomClick = () => {
|
|
291
291
|
this.scrollToBottom(), this.resetScrollUnreadCount();
|
|
292
292
|
}, this.handleEmojiClick = () => {
|
|
293
293
|
this.toggleEmojiPicker();
|
|
294
294
|
}, this.handleDocumentPointerDown = (n) => {
|
|
295
295
|
if (!this.emojiPickerOpen) return;
|
|
296
|
-
const
|
|
297
|
-
|
|
296
|
+
const a = n.composedPath();
|
|
297
|
+
a.includes(this.emojiPickerEl) || a.includes(this.emojiButton) || this.closeEmojiPicker();
|
|
298
298
|
}, this.closeButton.addEventListener("click", this.handleCloseClick), this.sendButton.addEventListener("click", this.handleSendClick), this.emojiButton.addEventListener("click", this.handleEmojiClick), this.scrollBottomButton.addEventListener("click", this.handleScrollBottomClick), this.input.addEventListener("keydown", this.handleInputKeydown), this.messagesEl.addEventListener("scroll", this.handleMessagesScroll), this.shadow.addEventListener("keydown", this.handleShadowKeydown), this.shadow.addEventListener("focusin", this.handleFocusin), document.addEventListener("pointerdown", this.handleDocumentPointerDown, !0), this.renderEmojiPicker(), this.updateScrollBottomState(), this.collectFocusable();
|
|
299
299
|
}
|
|
300
300
|
mount() {
|
|
@@ -329,8 +329,8 @@ class Z {
|
|
|
329
329
|
this.messagesEl.innerHTML = "", this.seenIds.clear(), this.renderedMessages.length = 0, this.lastTimelineMessage = null;
|
|
330
330
|
for (const s of e) {
|
|
331
331
|
s.messages.forEach((n) => this.seenIds.add(n.id));
|
|
332
|
-
const i = this.createHistoryContainer(s.messages),
|
|
333
|
-
this.messagesEl.appendChild(i), this.messagesEl.appendChild(
|
|
332
|
+
const i = this.createHistoryContainer(s.messages), o = s.messages[s.messages.length - 1].ts, l = this.createSeparatorElement(o, i);
|
|
333
|
+
this.messagesEl.appendChild(i), this.messagesEl.appendChild(l);
|
|
334
334
|
}
|
|
335
335
|
t.forEach((s) => {
|
|
336
336
|
this.seenIds.add(s.id), this.appendTimelineMessage(s);
|
|
@@ -395,17 +395,17 @@ class Z {
|
|
|
395
395
|
}
|
|
396
396
|
createSeparatorElement(e, t) {
|
|
397
397
|
const s = document.createElement("div");
|
|
398
|
-
s.className = "conversation-separator", s.setAttribute("role", "button"), s.setAttribute("tabindex", "0"), s.setAttribute("aria-expanded", "false"), s.setAttribute("aria-label", "Show previous conversation"), s.textContent =
|
|
398
|
+
s.className = "conversation-separator", s.setAttribute("role", "button"), s.setAttribute("tabindex", "0"), s.setAttribute("aria-expanded", "false"), s.setAttribute("aria-label", "Show previous conversation"), s.textContent = Y(e);
|
|
399
399
|
const i = () => {
|
|
400
|
-
const
|
|
401
|
-
s.setAttribute("aria-expanded", String(
|
|
400
|
+
const o = t.classList.toggle("expanded");
|
|
401
|
+
s.setAttribute("aria-expanded", String(o)), s.setAttribute(
|
|
402
402
|
"aria-label",
|
|
403
|
-
|
|
403
|
+
o ? "Hide previous conversation" : "Show previous conversation"
|
|
404
404
|
);
|
|
405
405
|
};
|
|
406
|
-
return s.addEventListener("click", i), s.addEventListener("keydown", (
|
|
407
|
-
const
|
|
408
|
-
(
|
|
406
|
+
return s.addEventListener("click", i), s.addEventListener("keydown", (o) => {
|
|
407
|
+
const l = o;
|
|
408
|
+
(l.key === "Enter" || l.key === " ") && (l.preventDefault(), i());
|
|
409
409
|
}), s;
|
|
410
410
|
}
|
|
411
411
|
createHistoryContainer(e) {
|
|
@@ -414,8 +414,8 @@ class Z {
|
|
|
414
414
|
let s = null;
|
|
415
415
|
return e.forEach((i) => {
|
|
416
416
|
this.appendDateSeparatorIfNeeded(s, i, t);
|
|
417
|
-
const { item:
|
|
418
|
-
t.appendChild(
|
|
417
|
+
const { item: o } = this.createMessageElement(i);
|
|
418
|
+
t.appendChild(o), s = i;
|
|
419
419
|
}), t;
|
|
420
420
|
}
|
|
421
421
|
appendTimelineMessage(e) {
|
|
@@ -431,24 +431,24 @@ class Z {
|
|
|
431
431
|
}
|
|
432
432
|
updateTimestampVisibility() {
|
|
433
433
|
for (let e = 0; e < this.renderedMessages.length; e += 1) {
|
|
434
|
-
const t = this.renderedMessages[e], s = this.renderedMessages[e + 1], i = !!s && s.message.author === t.message.author && s.message.ts - t.message.ts <=
|
|
434
|
+
const t = this.renderedMessages[e], s = this.renderedMessages[e + 1], i = !!s && s.message.author === t.message.author && s.message.ts - t.message.ts <= J && s.message.ts >= t.message.ts;
|
|
435
435
|
t.timeEl.toggleAttribute("hidden", i), t.timeEl.textContent = this.formatMessageTimestamp(t.message.ts);
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
formatMessageTimestamp(e) {
|
|
439
439
|
const s = Date.now() - e;
|
|
440
|
-
if (s <
|
|
440
|
+
if (s < D) {
|
|
441
441
|
const i = Math.max(1, Math.round(s / 6e4));
|
|
442
442
|
if (i < 60)
|
|
443
443
|
return this.relativeTimeFormat.format(-i, "minute");
|
|
444
|
-
const
|
|
445
|
-
return this.relativeTimeFormat.format(-
|
|
444
|
+
const o = Math.max(1, Math.round(i / 60));
|
|
445
|
+
return this.relativeTimeFormat.format(-o, "hour");
|
|
446
446
|
}
|
|
447
447
|
return this.absoluteTimeFormat.format(new Date(e));
|
|
448
448
|
}
|
|
449
449
|
formatDateSeparator(e) {
|
|
450
450
|
const t = new Date(e), s = this.startOfDay(Date.now()), i = this.startOfDay(e);
|
|
451
|
-
return i === s ? "Today" : i === s -
|
|
451
|
+
return i === s ? "Today" : i === s - D ? "Yesterday" : this.dateSeparatorFormat.format(t);
|
|
452
452
|
}
|
|
453
453
|
isSameDay(e, t) {
|
|
454
454
|
return this.startOfDay(e) === this.startOfDay(t);
|
|
@@ -458,7 +458,7 @@ class Z {
|
|
|
458
458
|
return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
|
|
459
459
|
}
|
|
460
460
|
isScrolledUp() {
|
|
461
|
-
return this.distanceFromBottom() >
|
|
461
|
+
return this.distanceFromBottom() > X;
|
|
462
462
|
}
|
|
463
463
|
distanceFromBottom() {
|
|
464
464
|
return this.messagesEl.scrollHeight - this.messagesEl.scrollTop - this.messagesEl.clientHeight;
|
|
@@ -505,7 +505,7 @@ class Z {
|
|
|
505
505
|
this.emojiPickerOpen && (this.emojiPickerOpen = !1, this.emojiPickerEl.style.display = "none", this.emojiButton.setAttribute("aria-expanded", "false"), this.collectFocusable());
|
|
506
506
|
}
|
|
507
507
|
renderEmojiPicker() {
|
|
508
|
-
this.emojiPickerEl.replaceChildren(),
|
|
508
|
+
this.emojiPickerEl.replaceChildren(), Z.forEach((e) => {
|
|
509
509
|
const t = document.createElement("button");
|
|
510
510
|
t.type = "button", t.className = "emoji-option", t.textContent = e, t.setAttribute("aria-label", `Insert ${e}`), t.addEventListener("click", () => {
|
|
511
511
|
this.insertEmojiAtCursor(e), this.closeEmojiPicker();
|
|
@@ -513,11 +513,11 @@ class Z {
|
|
|
513
513
|
});
|
|
514
514
|
}
|
|
515
515
|
insertEmojiAtCursor(e) {
|
|
516
|
-
var
|
|
517
|
-
const t = this.input.value, s = (
|
|
516
|
+
var l, n;
|
|
517
|
+
const t = this.input.value, s = (l = this.input.selectionStart) != null ? l : t.length, i = (n = this.input.selectionEnd) != null ? n : t.length;
|
|
518
518
|
this.input.value = `${t.slice(0, s)}${e}${t.slice(i)}`;
|
|
519
|
-
const
|
|
520
|
-
this.input.setSelectionRange(
|
|
519
|
+
const o = s + e.length;
|
|
520
|
+
this.input.setSelectionRange(o, o), this.input.dispatchEvent(new Event("input", { bubbles: !0 })), this.input.focus();
|
|
521
521
|
}
|
|
522
522
|
collectFocusable() {
|
|
523
523
|
const e = this.shadow.querySelectorAll(
|
|
@@ -533,14 +533,40 @@ class Z {
|
|
|
533
533
|
e.shiftKey && i === t ? (e.preventDefault(), s.focus()) : !e.shiftKey && i === s && (e.preventDefault(), t.focus());
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
|
-
const v = 1500,
|
|
537
|
-
class
|
|
536
|
+
const v = 1500, L = 8e3;
|
|
537
|
+
class ee {
|
|
538
538
|
constructor(e, t) {
|
|
539
539
|
this.sessionId = null, this.currentKind = null, this.stopped = !1, this.backoff = v, this.connectPromise = null, this.options = e, this.handlers = t;
|
|
540
540
|
}
|
|
541
541
|
setAccessToken(e) {
|
|
542
542
|
this.options.accessToken = e;
|
|
543
543
|
}
|
|
544
|
+
setUser(e) {
|
|
545
|
+
const t = {};
|
|
546
|
+
for (const [s, i] of Object.entries(e))
|
|
547
|
+
if (typeof i == "string") {
|
|
548
|
+
const o = i.trim();
|
|
549
|
+
o && (t[s] = o);
|
|
550
|
+
}
|
|
551
|
+
this.user = t, this.sessionId && this.sendIdentify().catch(
|
|
552
|
+
(s) => this.handlers.onError(s)
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
async sendIdentify() {
|
|
556
|
+
const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/identify`, t = await fetch(e, {
|
|
557
|
+
method: "POST",
|
|
558
|
+
headers: {
|
|
559
|
+
"Content-Type": "application/json",
|
|
560
|
+
...this.options.accessToken ? { "X-Authorization": `Bearer ${this.options.accessToken}` } : {}
|
|
561
|
+
},
|
|
562
|
+
body: JSON.stringify({
|
|
563
|
+
sessionId: this.sessionId,
|
|
564
|
+
user: this.user
|
|
565
|
+
})
|
|
566
|
+
});
|
|
567
|
+
if (!t.ok)
|
|
568
|
+
throw new Error(`SpilkiWidget: identify failed (${t.status})`);
|
|
569
|
+
}
|
|
544
570
|
get kind() {
|
|
545
571
|
return this.currentKind;
|
|
546
572
|
}
|
|
@@ -559,14 +585,15 @@ class Q {
|
|
|
559
585
|
}
|
|
560
586
|
}
|
|
561
587
|
async doConnect() {
|
|
562
|
-
var
|
|
588
|
+
var l, n;
|
|
563
589
|
this.stopped = !1;
|
|
564
|
-
const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/session`, t = (n = (
|
|
590
|
+
const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/session`, t = (n = (l = this.sessionId) != null ? l : this.options.sessionId) != null ? n : void 0, s = {
|
|
565
591
|
organisationId: this.options.org,
|
|
566
592
|
sessionId: t,
|
|
567
593
|
userAgent: typeof navigator != "undefined" ? navigator.userAgent : "",
|
|
568
594
|
referrer: typeof document != "undefined" ? document.referrer : "",
|
|
569
|
-
origin: typeof window != "undefined" ? window.location.origin : ""
|
|
595
|
+
origin: typeof window != "undefined" ? window.location.origin : "",
|
|
596
|
+
...this.user ? { user: this.user } : {}
|
|
570
597
|
}, i = await fetch(e, {
|
|
571
598
|
method: "POST",
|
|
572
599
|
headers: {
|
|
@@ -577,15 +604,16 @@ class Q {
|
|
|
577
604
|
});
|
|
578
605
|
if (!i.ok)
|
|
579
606
|
throw new Error(`SpilkiWidget: connect failed (${i.status})`);
|
|
580
|
-
const
|
|
581
|
-
return this.sessionId =
|
|
607
|
+
const o = await i.json();
|
|
608
|
+
return this.sessionId = o.sessionId, this.options.sessionId = o.sessionId, this.backoff = v, await this.startTransport(o), o;
|
|
582
609
|
}
|
|
583
610
|
async send(e, t) {
|
|
584
|
-
var
|
|
611
|
+
var l, n, a;
|
|
585
612
|
const s = {
|
|
586
|
-
sessionId: (n = (
|
|
613
|
+
sessionId: (n = (l = this.sessionId) != null ? l : this.options.sessionId) != null ? n : "",
|
|
587
614
|
text: e,
|
|
588
|
-
...t ? { messageId: t } : {}
|
|
615
|
+
...t ? { messageId: t } : {},
|
|
616
|
+
...(a = this.user) != null && a.userId ? { userId: this.user.userId } : {}
|
|
589
617
|
};
|
|
590
618
|
if (!s.sessionId)
|
|
591
619
|
throw new Error("SpilkiWidget: missing session id");
|
|
@@ -593,7 +621,7 @@ class Q {
|
|
|
593
621
|
this.ws.send(JSON.stringify({ type: "message", payload: s }));
|
|
594
622
|
return;
|
|
595
623
|
}
|
|
596
|
-
const i = `${this.options.apiBase.replace(/\/$/, "")}/widget/message`,
|
|
624
|
+
const i = `${this.options.apiBase.replace(/\/$/, "")}/widget/message`, o = await fetch(i, {
|
|
597
625
|
method: "POST",
|
|
598
626
|
headers: {
|
|
599
627
|
"Content-Type": "application/json",
|
|
@@ -601,8 +629,8 @@ class Q {
|
|
|
601
629
|
},
|
|
602
630
|
body: JSON.stringify(s)
|
|
603
631
|
});
|
|
604
|
-
if (!
|
|
605
|
-
throw new Error(`SpilkiWidget: send failed (${
|
|
632
|
+
if (!o.ok)
|
|
633
|
+
throw new Error(`SpilkiWidget: send failed (${o.status})`);
|
|
606
634
|
}
|
|
607
635
|
// #1: accept resetBackoff param; #8 #9: clear retryTimer
|
|
608
636
|
stop(e = !0) {
|
|
@@ -632,7 +660,7 @@ class Q {
|
|
|
632
660
|
return;
|
|
633
661
|
}
|
|
634
662
|
this.currentKind = "ws", this.handlers.onOpen("ws"), this.backoff = v, t();
|
|
635
|
-
}, this.wsOnMessage = (
|
|
663
|
+
}, this.wsOnMessage = (o) => this.handleIncoming(o.data), this.wsOnClose = () => {
|
|
636
664
|
this.stopped || this.retryFallback("ws");
|
|
637
665
|
}, this.wsOnError = () => {
|
|
638
666
|
this.handlers.onError(new Error("SpilkiWidget: websocket error")), i.readyState !== WebSocket.OPEN && s(new Error("WebSocket failed"));
|
|
@@ -652,8 +680,8 @@ class Q {
|
|
|
652
680
|
"X-Authorization": `Bearer ${this.options.accessToken}`
|
|
653
681
|
},
|
|
654
682
|
signal: i.signal
|
|
655
|
-
}).then((
|
|
656
|
-
if (!
|
|
683
|
+
}).then((o) => {
|
|
684
|
+
if (!o.ok || !o.body) {
|
|
657
685
|
s(new Error("SSE failed"));
|
|
658
686
|
return;
|
|
659
687
|
}
|
|
@@ -662,21 +690,21 @@ class Q {
|
|
|
662
690
|
return;
|
|
663
691
|
}
|
|
664
692
|
this.currentKind = "sse", this.handlers.onOpen("sse"), this.backoff = v, t();
|
|
665
|
-
const
|
|
666
|
-
let
|
|
693
|
+
const l = o.body.getReader(), n = new TextDecoder();
|
|
694
|
+
let a = "";
|
|
667
695
|
const g = () => {
|
|
668
|
-
|
|
696
|
+
l.read().then(({ done: u, value: d }) => {
|
|
669
697
|
var m;
|
|
670
698
|
if (u || this.stopped) {
|
|
671
699
|
this.stopped || (this.handlers.onError(new Error("SpilkiWidget: SSE stream ended")), this.retryFallback("sse"));
|
|
672
700
|
return;
|
|
673
701
|
}
|
|
674
|
-
|
|
675
|
-
const
|
|
702
|
+
a += n.decode(d, { stream: !0 });
|
|
703
|
+
const h = a.split(`
|
|
676
704
|
|
|
677
705
|
`);
|
|
678
|
-
|
|
679
|
-
for (const f of
|
|
706
|
+
a = (m = h.pop()) != null ? m : "";
|
|
707
|
+
for (const f of h)
|
|
680
708
|
for (const p of f.split(`
|
|
681
709
|
`))
|
|
682
710
|
p.startsWith("data:") && this.handleIncoming(p.slice(5).trim());
|
|
@@ -686,8 +714,8 @@ class Q {
|
|
|
686
714
|
});
|
|
687
715
|
};
|
|
688
716
|
g();
|
|
689
|
-
}).catch((
|
|
690
|
-
i.signal.aborted || s(
|
|
717
|
+
}).catch((o) => {
|
|
718
|
+
i.signal.aborted || s(o);
|
|
691
719
|
});
|
|
692
720
|
});
|
|
693
721
|
}
|
|
@@ -704,9 +732,9 @@ class Q {
|
|
|
704
732
|
});
|
|
705
733
|
if (!s.ok) throw new Error(`Poll failed ${s.status}`);
|
|
706
734
|
const i = await s.json();
|
|
707
|
-
|
|
735
|
+
S(i).forEach((o) => this.handlers.onMessage(o)), this.backoff = v;
|
|
708
736
|
} catch (s) {
|
|
709
|
-
this.handlers.onError(s), this.backoff = Math.min(this.backoff * 1.5,
|
|
737
|
+
this.handlers.onError(s), this.backoff = Math.min(this.backoff * 1.5, L);
|
|
710
738
|
} finally {
|
|
711
739
|
this.stopped || (this.pollTimer = window.setTimeout(t, this.backoff));
|
|
712
740
|
}
|
|
@@ -715,7 +743,7 @@ class Q {
|
|
|
715
743
|
}
|
|
716
744
|
// #1: preserve backoff by calling stop(false); #8: check stopped before reconnect
|
|
717
745
|
retryFallback(e) {
|
|
718
|
-
this.stopped || (this.stop(!1), this.backoff = Math.min(this.backoff * 1.5,
|
|
746
|
+
this.stopped || (this.stop(!1), this.backoff = Math.min(this.backoff * 1.5, L), this.retryTimer = window.setTimeout(() => {
|
|
719
747
|
this.stopped || (this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)), this.connect().catch((t) => this.handlers.onError(t)));
|
|
720
748
|
}, this.backoff));
|
|
721
749
|
}
|
|
@@ -735,20 +763,20 @@ class Q {
|
|
|
735
763
|
dispatchIncoming(e) {
|
|
736
764
|
var t;
|
|
737
765
|
if ("type" in e) {
|
|
738
|
-
e.type === "message" ? this.handlers.onMessage(e.payload) : e.type === "typing" ? this.handlers.onTyping(!!((t = e.payload) != null && t.active)) : e.type === "AGENT_EVENT" &&
|
|
766
|
+
e.type === "message" ? this.handlers.onMessage(e.payload) : e.type === "typing" ? this.handlers.onTyping(!!((t = e.payload) != null && t.active)) : e.type === "AGENT_EVENT" && se(e.payload) && this.handlers.onAgentEvent(e.payload);
|
|
739
767
|
return;
|
|
740
768
|
}
|
|
741
769
|
this.handlers.onMessage(e);
|
|
742
770
|
}
|
|
743
771
|
}
|
|
744
|
-
const
|
|
745
|
-
function
|
|
746
|
-
if (typeof
|
|
747
|
-
const e =
|
|
748
|
-
return
|
|
772
|
+
const te = /* @__PURE__ */ new Set(["TYPING", "THINKING", "SEARCHING", "EXECUTING_TOOL"]);
|
|
773
|
+
function se(r) {
|
|
774
|
+
if (typeof r != "object" || r === null) return !1;
|
|
775
|
+
const e = r;
|
|
776
|
+
return te.has(e.eventType) && typeof e.active == "boolean";
|
|
749
777
|
}
|
|
750
|
-
const
|
|
751
|
-
class
|
|
778
|
+
const U = 30 * 60 * 1e3, ie = 3e4, A = 30;
|
|
779
|
+
class re {
|
|
752
780
|
constructor(e, t) {
|
|
753
781
|
this.org = e, this.listeners = /* @__PURE__ */ new Set(), this.activityTimer = null, this.activeEvents = /* @__PURE__ */ new Set(), this.state = {
|
|
754
782
|
isOpen: !1,
|
|
@@ -783,7 +811,7 @@ class ie {
|
|
|
783
811
|
updateDisplayedActivity() {
|
|
784
812
|
this.activityTimer && (clearTimeout(this.activityTimer), this.activityTimer = null), this.activeEvents.size > 0 && (this.activityTimer = setTimeout(() => {
|
|
785
813
|
this.activeEvents.clear(), this.state.agentActivity = null, this.activityTimer = null, this.emit();
|
|
786
|
-
},
|
|
814
|
+
}, ie));
|
|
787
815
|
const e = this.resolveTopActivity();
|
|
788
816
|
this.state.agentActivity !== e && (this.state.agentActivity = e, this.emit());
|
|
789
817
|
}
|
|
@@ -802,15 +830,15 @@ class ie {
|
|
|
802
830
|
addMessage(e) {
|
|
803
831
|
var s, i;
|
|
804
832
|
const t = {
|
|
805
|
-
id: (s = e.id) != null ? s :
|
|
833
|
+
id: (s = e.id) != null ? s : z("msg"),
|
|
806
834
|
ts: (i = e.ts) != null ? i : Date.now(),
|
|
807
835
|
author: e.author,
|
|
808
836
|
text: e.text
|
|
809
837
|
};
|
|
810
|
-
return this.state.messages.some((
|
|
838
|
+
return this.state.messages.some((o) => o.id === t.id) ? null : (this.state.messages = S([...this.state.messages, t], A), this.persistMessages(), this.touchActivity(), this.emit(), t);
|
|
811
839
|
}
|
|
812
840
|
setMessages(e) {
|
|
813
|
-
this.state.messages =
|
|
841
|
+
this.state.messages = S(e, A), this.persistMessages(), this.emit();
|
|
814
842
|
}
|
|
815
843
|
clearMessages() {
|
|
816
844
|
this.state.messages = [], this.persistMessages(), this.emit();
|
|
@@ -902,7 +930,7 @@ class ie {
|
|
|
902
930
|
}
|
|
903
931
|
isSessionExpired() {
|
|
904
932
|
const e = this.lastActivityTs;
|
|
905
|
-
return e === 0 ? !1 : Date.now() - e >=
|
|
933
|
+
return e === 0 ? !1 : Date.now() - e >= U;
|
|
906
934
|
}
|
|
907
935
|
getConversationGroups() {
|
|
908
936
|
const e = this.state.messages;
|
|
@@ -910,7 +938,7 @@ class ie {
|
|
|
910
938
|
const t = [];
|
|
911
939
|
let s = { startTs: e[0].ts, messages: [e[0]] };
|
|
912
940
|
for (let i = 1; i < e.length; i++)
|
|
913
|
-
e[i].ts - e[i - 1].ts >=
|
|
941
|
+
e[i].ts - e[i - 1].ts >= U ? (t.push(s), s = { startTs: e[i].ts, messages: [e[i]] }) : s.messages.push(e[i]);
|
|
914
942
|
return t.push(s), t;
|
|
915
943
|
}
|
|
916
944
|
emit() {
|
|
@@ -930,14 +958,14 @@ class ie {
|
|
|
930
958
|
const e = localStorage.getItem(this.historyKey);
|
|
931
959
|
if (!e) return [];
|
|
932
960
|
const t = JSON.parse(e);
|
|
933
|
-
return Array.isArray(t) ?
|
|
961
|
+
return Array.isArray(t) ? S(t, A) : [];
|
|
934
962
|
} catch (e) {
|
|
935
963
|
return console.error("SpilkiWidget: unable to load messages", e), [];
|
|
936
964
|
}
|
|
937
965
|
}
|
|
938
966
|
}
|
|
939
|
-
function oe(
|
|
940
|
-
const e =
|
|
967
|
+
function oe(r) {
|
|
968
|
+
const e = r.replace(/-/g, "+").replace(/_/g, "/"), t = e.padEnd(e.length + (4 - e.length % 4) % 4, "=");
|
|
941
969
|
if (typeof atob == "function")
|
|
942
970
|
return decodeURIComponent(
|
|
943
971
|
Array.prototype.map.call(atob(t), (i) => `%${`00${i.charCodeAt(0).toString(16)}`.slice(-2)}`).join("")
|
|
@@ -947,9 +975,9 @@ function oe(o) {
|
|
|
947
975
|
return s.from(t, "base64").toString("utf8");
|
|
948
976
|
throw new Error("SpilkiWidget: no base64 decoder available");
|
|
949
977
|
}
|
|
950
|
-
function
|
|
951
|
-
if (!
|
|
952
|
-
const e =
|
|
978
|
+
function ne(r) {
|
|
979
|
+
if (!r) return null;
|
|
980
|
+
const e = r.split(".");
|
|
953
981
|
if (e.length < 2) return null;
|
|
954
982
|
try {
|
|
955
983
|
const t = oe(e[1]);
|
|
@@ -958,13 +986,13 @@ function re(o) {
|
|
|
958
986
|
return console.error("SpilkiWidget: unable to parse JWT", t), null;
|
|
959
987
|
}
|
|
960
988
|
}
|
|
961
|
-
function
|
|
962
|
-
const e =
|
|
989
|
+
function ae(r) {
|
|
990
|
+
const e = ne(r);
|
|
963
991
|
if (!(e != null && e.exp) || typeof e.exp != "number") return !0;
|
|
964
992
|
const t = Math.floor(Date.now() / 1e3);
|
|
965
993
|
return e.exp < t + 60;
|
|
966
994
|
}
|
|
967
|
-
const
|
|
995
|
+
const le = {
|
|
968
996
|
onOpen() {
|
|
969
997
|
},
|
|
970
998
|
onClose() {
|
|
@@ -976,8 +1004,8 @@ const ae = {
|
|
|
976
1004
|
onTransportChange() {
|
|
977
1005
|
}
|
|
978
1006
|
};
|
|
979
|
-
async function
|
|
980
|
-
const s = `${
|
|
1007
|
+
async function ce(r, e, t) {
|
|
1008
|
+
const s = `${r.replace(/\/$/, "")}/widget/install`, i = await fetch(s, {
|
|
981
1009
|
method: "POST",
|
|
982
1010
|
headers: { "Content-Type": "application/json", Origin: window.location.origin },
|
|
983
1011
|
body: JSON.stringify({ token: e, organisationId: t })
|
|
@@ -985,29 +1013,29 @@ async function le(o, e, t) {
|
|
|
985
1013
|
if (!i.ok) throw new Error(`SpilkiWidget: install failed (${i.status})`);
|
|
986
1014
|
return (await i.json()).accessToken;
|
|
987
1015
|
}
|
|
988
|
-
async function
|
|
989
|
-
const t = `${
|
|
1016
|
+
async function de(r, e) {
|
|
1017
|
+
const t = `${r.replace(/\/$/, "")}/widget/refresh`, s = await fetch(t, {
|
|
990
1018
|
method: "POST",
|
|
991
1019
|
headers: { "X-Authorization": `Bearer ${e}`, Origin: window.location.origin }
|
|
992
1020
|
});
|
|
993
1021
|
if (!s.ok) throw new Error(`SpilkiWidget: refresh failed (${s.status})`);
|
|
994
1022
|
return (await s.json()).accessToken;
|
|
995
1023
|
}
|
|
996
|
-
function he(
|
|
1024
|
+
function he(r) {
|
|
997
1025
|
let e = !1, t = null;
|
|
998
1026
|
const s = [], i = () => {
|
|
999
1027
|
var n;
|
|
1000
|
-
if (!
|
|
1028
|
+
if (!r) return null;
|
|
1001
1029
|
if (t) return t;
|
|
1002
1030
|
try {
|
|
1003
|
-
const
|
|
1004
|
-
if (!
|
|
1005
|
-
t = new
|
|
1031
|
+
const a = (n = window.AudioContext) != null ? n : window.webkitAudioContext;
|
|
1032
|
+
if (!a) return null;
|
|
1033
|
+
t = new a();
|
|
1006
1034
|
} catch {
|
|
1007
1035
|
return null;
|
|
1008
1036
|
}
|
|
1009
1037
|
return t;
|
|
1010
|
-
},
|
|
1038
|
+
}, o = () => {
|
|
1011
1039
|
e = !0;
|
|
1012
1040
|
try {
|
|
1013
1041
|
const n = i();
|
|
@@ -1015,97 +1043,97 @@ function he(o) {
|
|
|
1015
1043
|
});
|
|
1016
1044
|
} catch {
|
|
1017
1045
|
}
|
|
1018
|
-
},
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1046
|
+
}, l = (n) => {
|
|
1047
|
+
const a = () => {
|
|
1048
|
+
o(), window.removeEventListener(n, a, !0);
|
|
1021
1049
|
};
|
|
1022
|
-
s.push({ type: n, listener:
|
|
1050
|
+
s.push({ type: n, listener: a }), window.addEventListener(n, a, { capture: !0, passive: !0 });
|
|
1023
1051
|
};
|
|
1024
|
-
return
|
|
1025
|
-
markUserInteraction:
|
|
1052
|
+
return l("pointerdown"), l("keydown"), {
|
|
1053
|
+
markUserInteraction: o,
|
|
1026
1054
|
play() {
|
|
1027
|
-
if (!
|
|
1055
|
+
if (!r || !e) return;
|
|
1028
1056
|
const n = i();
|
|
1029
1057
|
if (!n || n.state !== "running") return;
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
const g = (
|
|
1058
|
+
const a = n.createGain();
|
|
1059
|
+
a.gain.value = 0.15, a.connect(n.destination);
|
|
1060
|
+
const g = (d, h, m) => {
|
|
1033
1061
|
const f = n.createOscillator(), p = n.createGain();
|
|
1034
|
-
f.type = "sine", f.frequency.setValueAtTime(
|
|
1062
|
+
f.type = "sine", f.frequency.setValueAtTime(d, h), p.gain.setValueAtTime(1e-4, h), p.gain.exponentialRampToValueAtTime(1, h + 0.012), p.gain.exponentialRampToValueAtTime(1e-4, h + m), f.connect(p), p.connect(a), f.start(h), f.stop(h + m + 0.02);
|
|
1035
1063
|
}, u = n.currentTime;
|
|
1036
1064
|
g(880, u, 0.08), g(1175, u + 0.09, 0.08);
|
|
1037
1065
|
},
|
|
1038
1066
|
destroy() {
|
|
1039
|
-
s.forEach(({ type: n, listener:
|
|
1040
|
-
window.removeEventListener(n,
|
|
1067
|
+
s.forEach(({ type: n, listener: a }) => {
|
|
1068
|
+
window.removeEventListener(n, a, !0);
|
|
1041
1069
|
}), s.length = 0, t && t.close().catch(() => {
|
|
1042
1070
|
}), t = null;
|
|
1043
1071
|
}
|
|
1044
1072
|
};
|
|
1045
1073
|
}
|
|
1046
|
-
function
|
|
1047
|
-
var
|
|
1048
|
-
if (!
|
|
1074
|
+
function $(r) {
|
|
1075
|
+
var O, M, B, j;
|
|
1076
|
+
if (!r.org)
|
|
1049
1077
|
throw new Error("SpilkiWidget: org is required");
|
|
1050
|
-
const e =
|
|
1051
|
-
|
|
1052
|
-
const t = { ...
|
|
1053
|
-
let
|
|
1078
|
+
const e = H(r);
|
|
1079
|
+
G(e.allowedOriginsHint);
|
|
1080
|
+
const t = { ...le, ...(O = e.hooks) != null ? O : {} }, s = new re(e.org, { persist: e.persist }), i = he(e.sound);
|
|
1081
|
+
let o = (M = s.accessToken) != null ? M : void 0, l = null;
|
|
1054
1082
|
const n = () => {
|
|
1055
1083
|
u.setBadge(s.countUnread());
|
|
1056
|
-
},
|
|
1084
|
+
}, a = () => {
|
|
1057
1085
|
s.markRead(), u.setBadge(0);
|
|
1058
|
-
}, g = async () =>
|
|
1086
|
+
}, g = async () => l || (l = (async () => {
|
|
1059
1087
|
try {
|
|
1060
|
-
if (
|
|
1088
|
+
if (o && ae(o))
|
|
1061
1089
|
try {
|
|
1062
|
-
|
|
1090
|
+
o = await de(e.apiBase, o), s.persistAccessToken(o), h.setAccessToken(o);
|
|
1063
1091
|
return;
|
|
1064
1092
|
} catch {
|
|
1065
|
-
|
|
1093
|
+
o = void 0, s.clearAccessToken();
|
|
1066
1094
|
}
|
|
1067
|
-
if (!
|
|
1068
|
-
if (!
|
|
1069
|
-
if (!
|
|
1070
|
-
|
|
1095
|
+
if (!o) {
|
|
1096
|
+
if (!r.installationToken) throw new Error("SpilkiWidget: missing installationToken");
|
|
1097
|
+
if (!r.org) throw new Error("SpilkiWidget: missing org");
|
|
1098
|
+
o = await ce(e.apiBase, r.installationToken, r.org), s.persistAccessToken(o), h.setAccessToken(o);
|
|
1071
1099
|
}
|
|
1072
1100
|
} finally {
|
|
1073
|
-
|
|
1101
|
+
l = null;
|
|
1074
1102
|
}
|
|
1075
|
-
})(),
|
|
1103
|
+
})(), l), u = R({
|
|
1076
1104
|
color: e.color,
|
|
1077
|
-
position: (
|
|
1105
|
+
position: (B = e.position) != null ? B : "bottom-right",
|
|
1078
1106
|
onClick: () => {
|
|
1079
|
-
s.snapshot.isOpen ? (s.close(), t.onClose()) : (i.markUserInteraction(), s.open(),
|
|
1107
|
+
s.snapshot.isOpen ? (s.close(), t.onClose()) : (i.markUserInteraction(), s.open(), a(), t.onOpen());
|
|
1080
1108
|
}
|
|
1081
|
-
}),
|
|
1109
|
+
}), d = new Q({
|
|
1082
1110
|
color: e.color,
|
|
1083
1111
|
theme: P(e.theme),
|
|
1084
|
-
position: (
|
|
1112
|
+
position: (j = e.position) != null ? j : "bottom-right",
|
|
1085
1113
|
i18n: e.i18n,
|
|
1086
1114
|
onClose: () => {
|
|
1087
1115
|
s.close(), t.onClose();
|
|
1088
1116
|
},
|
|
1089
1117
|
onSend: (c) => {
|
|
1090
1118
|
const k = s.addMessage({ author: "user", text: c });
|
|
1091
|
-
k && (
|
|
1092
|
-
t.onError(b), s.setConnected(!1),
|
|
1119
|
+
k && (d.appendMessage(k), g().then(() => h.send(c, k.id)).catch((b) => {
|
|
1120
|
+
t.onError(b), s.setConnected(!1), d.setOffline(!0);
|
|
1093
1121
|
}));
|
|
1094
1122
|
}
|
|
1095
|
-
}),
|
|
1123
|
+
}), h = new ee(
|
|
1096
1124
|
{
|
|
1097
1125
|
apiBase: e.apiBase,
|
|
1098
|
-
accessToken:
|
|
1126
|
+
accessToken: o,
|
|
1099
1127
|
org: e.org,
|
|
1100
1128
|
sessionId: s.sessionId
|
|
1101
1129
|
},
|
|
1102
1130
|
{
|
|
1103
1131
|
onOpen(c) {
|
|
1104
|
-
|
|
1132
|
+
C.transport = c, t.onTransportChange(c), s.setConnected(!0), d.setOffline(!1);
|
|
1105
1133
|
},
|
|
1106
1134
|
onMessage(c) {
|
|
1107
1135
|
const b = c.suggestedReplies, y = s.addMessage(c);
|
|
1108
|
-
y && (
|
|
1136
|
+
y && (d.appendMessage(y), y.author === "bot" && b && b.length > 0 && d.setSuggestedReplies(b), !s.snapshot.isOpen && y.author === "bot" && (n(), i.play()), t.onMessage(y));
|
|
1109
1137
|
},
|
|
1110
1138
|
onTyping(c) {
|
|
1111
1139
|
s.setTyping(c);
|
|
@@ -1114,11 +1142,11 @@ function U(o) {
|
|
|
1114
1142
|
s.handleAgentEvent(c.eventType, c.active);
|
|
1115
1143
|
},
|
|
1116
1144
|
onError(c) {
|
|
1117
|
-
t.onError(c), s.setConnected(!1), s.snapshot.isOpen &&
|
|
1145
|
+
t.onError(c), s.setConnected(!1), s.snapshot.isOpen && d.setOffline(!0);
|
|
1118
1146
|
}
|
|
1119
1147
|
}
|
|
1120
1148
|
);
|
|
1121
|
-
u.mount(),
|
|
1149
|
+
u.mount(), d.mount();
|
|
1122
1150
|
const m = s.snapshot.messages, f = s.isSessionExpired(), p = s.getConversationGroups();
|
|
1123
1151
|
if (m.length === 0 && e.welcome) {
|
|
1124
1152
|
const c = {
|
|
@@ -1127,57 +1155,60 @@ function U(o) {
|
|
|
1127
1155
|
text: e.welcome,
|
|
1128
1156
|
ts: Date.now()
|
|
1129
1157
|
};
|
|
1130
|
-
s.addMessage(c),
|
|
1131
|
-
} else f && m.length > 0 ?
|
|
1132
|
-
let
|
|
1158
|
+
s.addMessage(c), d.appendMessage(c);
|
|
1159
|
+
} else f && m.length > 0 ? d.renderWithConversations(p, []) : p.length > 1 ? d.renderWithConversations(p.slice(0, -1), p[p.length - 1].messages) : d.updateMessages(m);
|
|
1160
|
+
let T = !1;
|
|
1133
1161
|
const N = s.subscribe(() => {
|
|
1134
1162
|
const c = s.snapshot;
|
|
1135
|
-
if (u.setOpen(c.isOpen),
|
|
1136
|
-
if (!
|
|
1163
|
+
if (u.setOpen(c.isOpen), d.setAgentActivity(c.agentActivity, e.i18n), d.setOffline(!c.isConnected), d.updateTheme(P(e.theme)), c.isOpen) {
|
|
1164
|
+
if (!T) {
|
|
1137
1165
|
const k = s.countUnread() > 0, b = s.lastReadTs;
|
|
1138
|
-
|
|
1166
|
+
a(), k && d.scrollToFirstUnread(b);
|
|
1139
1167
|
}
|
|
1140
|
-
|
|
1168
|
+
T = !0, d.show();
|
|
1141
1169
|
} else
|
|
1142
|
-
|
|
1143
|
-
}),
|
|
1170
|
+
T = !1, d.hide();
|
|
1171
|
+
}), W = m.length === 0 || f;
|
|
1144
1172
|
n(), g().then(
|
|
1145
|
-
() =>
|
|
1173
|
+
() => h.connect().then((c) => {
|
|
1146
1174
|
var b, y;
|
|
1147
|
-
e.persist && s.persistSession(c.sessionId), s.setConnected(!0), t.onTransportChange((b =
|
|
1175
|
+
e.persist && s.persistSession(c.sessionId), s.setConnected(!0), t.onTransportChange((b = h.kind) != null ? b : "ws");
|
|
1148
1176
|
const k = (y = c.suggestedReplies) != null ? y : [];
|
|
1149
|
-
k.length > 0 &&
|
|
1177
|
+
k.length > 0 && W && d.setSuggestedReplies(k);
|
|
1150
1178
|
})
|
|
1151
1179
|
).catch((c) => {
|
|
1152
|
-
t.onError(c), s.setConnected(!1),
|
|
1180
|
+
t.onError(c), s.setConnected(!1), d.setOffline(!0);
|
|
1153
1181
|
});
|
|
1154
|
-
const
|
|
1182
|
+
const C = {
|
|
1155
1183
|
transport: null,
|
|
1156
1184
|
open() {
|
|
1157
|
-
i.markUserInteraction(), s.open(),
|
|
1185
|
+
i.markUserInteraction(), s.open(), a(), t.onOpen();
|
|
1158
1186
|
},
|
|
1159
1187
|
close() {
|
|
1160
1188
|
s.close(), t.onClose();
|
|
1161
1189
|
},
|
|
1190
|
+
identify(c) {
|
|
1191
|
+
h.setUser(c);
|
|
1192
|
+
},
|
|
1162
1193
|
destroy() {
|
|
1163
|
-
N(),
|
|
1194
|
+
N(), h.stop(), i.destroy(), u.destroy(), d.destroy(), E === C && (E = null);
|
|
1164
1195
|
}
|
|
1165
1196
|
};
|
|
1166
|
-
return
|
|
1197
|
+
return C;
|
|
1167
1198
|
}
|
|
1168
|
-
function
|
|
1199
|
+
function pe() {
|
|
1169
1200
|
var s, i;
|
|
1170
1201
|
if (typeof document == "undefined") return;
|
|
1171
|
-
const
|
|
1172
|
-
if (!
|
|
1173
|
-
const e =
|
|
1202
|
+
const r = document.currentScript;
|
|
1203
|
+
if (!r) return;
|
|
1204
|
+
const e = r.dataset;
|
|
1174
1205
|
if (e.autoinit === "false") return;
|
|
1175
1206
|
const t = e.org;
|
|
1176
1207
|
if (!t) {
|
|
1177
1208
|
console.error("SpilkiWidget: data-org and is required for auto init");
|
|
1178
1209
|
return;
|
|
1179
1210
|
}
|
|
1180
|
-
|
|
1211
|
+
E = $({
|
|
1181
1212
|
org: t,
|
|
1182
1213
|
installationToken: e.installationToken,
|
|
1183
1214
|
apiBase: e.apiBase,
|
|
@@ -1185,10 +1216,13 @@ function de() {
|
|
|
1185
1216
|
theme: (i = e.theme) != null ? i : void 0
|
|
1186
1217
|
});
|
|
1187
1218
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1219
|
+
let E = null;
|
|
1220
|
+
typeof window != "undefined" && (window.SpilkiWidget = window.SpilkiWidget || {}, window.SpilkiWidget.init = (r) => (E = $(r), E), window.SpilkiWidget.identify = (r) => {
|
|
1221
|
+
E ? E.identify(r) : console.warn("SpilkiWidget: call init() before identify()");
|
|
1222
|
+
});
|
|
1223
|
+
pe();
|
|
1190
1224
|
export {
|
|
1191
|
-
|
|
1192
|
-
|
|
1225
|
+
pe as autoInit,
|
|
1226
|
+
$ as initSpilkiWidget
|
|
1193
1227
|
};
|
|
1194
1228
|
//# sourceMappingURL=widget.es.js.map
|