@spilki/widget 1.0.32 → 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.
@@ -1,4 +1,4 @@
1
- const F = `
1
+ const R = `
2
2
  <style>
3
3
  :host {
4
4
  all: initial;
@@ -71,11 +71,11 @@ const F = `
71
71
  <span class="badge" hidden aria-hidden="true">0</span>
72
72
  </button>
73
73
  `;
74
- function R(r) {
74
+ function K(r) {
75
75
  const e = document.createElement("div");
76
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 = F;
78
+ t.innerHTML = R;
79
79
  const s = t.querySelector("button"), i = t.querySelector(".badge");
80
80
  return s.addEventListener("click", () => r.onClick()), {
81
81
  element: e,
@@ -85,81 +85,81 @@ function R(r) {
85
85
  destroy() {
86
86
  e.remove();
87
87
  },
88
- setOpen(a) {
89
- s.setAttribute("aria-expanded", String(a)), s.setAttribute("aria-label", a ? "Close chat" : "Open chat");
88
+ setOpen(l) {
89
+ s.setAttribute("aria-expanded", String(l)), s.setAttribute("aria-label", l ? "Close chat" : "Open chat");
90
90
  },
91
- setBadge(a) {
92
- const n = Math.max(0, a), l = s.getAttribute("aria-expanded") === "true";
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", l || s.setAttribute("aria-label", "Open chat");
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), l || s.setAttribute("aria-label", `Open chat, ${n} unread`);
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 K = "https://api.spilki.app", T = {
101
+ const H = "https://api.spilki.app", I = {
102
102
  welcome: "Hi! I'm your assistant.",
103
103
  placeholder: "Type a message…",
104
104
  sendLabel: "Send",
105
- typing: "Assistant is typing",
106
- thinking: "Assistant is thinking",
107
- searching: "Searching",
108
- executingTool: "Working on it",
105
+ typing: "Assistant is typing",
106
+ thinking: "Assistant is thinking",
107
+ searching: "Searching",
108
+ executingTool: "Working on it",
109
109
  offline: "Unable to connect. Please try again later.",
110
110
  title: "Spilki Assistant"
111
- }, k = {
112
- apiBase: K,
111
+ }, w = {
112
+ apiBase: H,
113
113
  position: "bottom-right",
114
114
  theme: "auto",
115
115
  color: "#6366f1",
116
- welcome: T.welcome,
116
+ welcome: I.welcome,
117
117
  persist: !0,
118
118
  sound: !0,
119
- i18n: T
119
+ i18n: I
120
120
  };
121
- function H(r) {
122
- var t, s, i, o, a, n, l, m;
123
- const e = { ...T, ...(t = r.i18n) != null ? t : {} };
121
+ function z(r) {
122
+ var t, s, i, o, l, n, a, g;
123
+ const e = { ...I, ...(t = r.i18n) != null ? t : {} };
124
124
  return {
125
- ...k,
125
+ ...w,
126
126
  ...r,
127
- apiBase: (s = r.apiBase) != null ? s : k.apiBase,
127
+ apiBase: (s = r.apiBase) != null ? s : w.apiBase,
128
128
  i18n: e,
129
129
  welcome: (i = r.welcome) != null ? i : e.welcome,
130
- position: (o = r.position) != null ? o : k.position,
131
- theme: (a = r.theme) != null ? a : k.theme,
132
- color: (n = r.color) != null ? n : k.color,
133
- persist: (l = r.persist) != null ? l : k.persist,
134
- sound: (m = r.sound) != null ? m : k.sound
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 z(r = "msg") {
137
+ function _(r = "msg") {
138
138
  return typeof crypto != "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${r}-${Math.random().toString(16).slice(2)}`;
139
139
  }
140
- function _() {
140
+ function G() {
141
141
  var r, e;
142
142
  return (e = (r = window.matchMedia) == null ? void 0 : r.call(window, "(prefers-color-scheme: dark)").matches) != null ? e : !1;
143
143
  }
144
144
  function P(r) {
145
- return r === "light" || r === "dark" ? r : _() ? "dark" : "light";
145
+ return r === "light" || r === "dark" ? r : G() ? "dark" : "light";
146
146
  }
147
- function E(r, e = 30) {
147
+ function S(r, e = 30) {
148
148
  return r.slice(-e);
149
149
  }
150
- function G(r) {
150
+ function q(r) {
151
151
  if (!r || r.length === 0) return;
152
152
  const e = window.location.origin;
153
153
  r.includes(e) || console.warn(
154
154
  `SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`
155
155
  );
156
156
  }
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 = (l) => String(l).padStart(2, "0"), i = `${s(e.getHours())}:${s(e.getMinutes())}`, o = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(), a = o - 864e5, n = new Date(e.getFullYear(), e.getMonth(), e.getDate()).getTime();
160
- return n === o ? `Today at ${i}` : n === a ? `Yesterday at ${i}` : `${q[e.getMonth()]} ${e.getDate()}, ${i}`;
157
+ const Y = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
158
+ function V(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}` : `${Y[e.getMonth()]} ${e.getDate()}, ${i}`;
161
161
  }
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:.5rem;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}.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}', j = 24 * 60 * 60 * 1e3, J = 2 * 60 * 1e3, X = 200, Z = [
162
+ const J = ':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, X = 2 * 60 * 1e3, Z = 200, Q = [
163
163
  "😀",
164
164
  "😂",
165
165
  "🤣",
@@ -211,10 +211,10 @@ const V = ':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-t
211
211
  "☕",
212
212
  "🍕"
213
213
  ];
214
- function v(r) {
214
+ function x(r) {
215
215
  return r.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
216
216
  }
217
- class Q {
217
+ class ee {
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 Q {
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 = v(e.i18n.title), s = v(e.i18n.typing), i = v(e.i18n.offline), o = v(e.i18n.placeholder), a = v(e.i18n.sendLabel);
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>${V}</style>
234
+ <style>${J}</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>
@@ -258,7 +258,7 @@ class Q {
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="${a}">
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 Q {
278
278
  this.options.onClose();
279
279
  }
280
280
  }, this.handleShadowKeydown = (n) => {
281
- const l = n;
282
- if (l.key === "Escape") {
281
+ const a = n;
282
+ if (a.key === "Escape") {
283
283
  if (this.emojiPickerOpen) {
284
- l.preventDefault(), this.closeEmojiPicker();
284
+ a.preventDefault(), this.closeEmojiPicker();
285
285
  return;
286
286
  }
287
287
  this.options.onClose();
288
288
  }
289
- l.key === "Tab" && this.trapFocus(l);
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 l = n.composedPath();
297
- l.includes(this.emojiPickerEl) || l.includes(this.emojiButton) || this.closeEmojiPicker();
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 Q {
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), o = s.messages[s.messages.length - 1].ts, a = this.createSeparatorElement(o, i);
333
- this.messagesEl.appendChild(i), this.messagesEl.appendChild(a);
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,7 +395,7 @@ class Q {
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 = Y(e);
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 = V(e);
399
399
  const i = () => {
400
400
  const o = t.classList.toggle("expanded");
401
401
  s.setAttribute("aria-expanded", String(o)), s.setAttribute(
@@ -404,8 +404,8 @@ class Q {
404
404
  );
405
405
  };
406
406
  return s.addEventListener("click", i), s.addEventListener("keydown", (o) => {
407
- const a = o;
408
- (a.key === "Enter" || a.key === " ") && (a.preventDefault(), i());
407
+ const l = o;
408
+ (l.key === "Enter" || l.key === " ") && (l.preventDefault(), i());
409
409
  }), s;
410
410
  }
411
411
  createHistoryContainer(e) {
@@ -431,13 +431,13 @@ class Q {
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 <= J && 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 <= X && 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 < j) {
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");
@@ -448,7 +448,7 @@ class Q {
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 - j ? "Yesterday" : this.dateSeparatorFormat.format(t);
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 Q {
458
458
  return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
459
459
  }
460
460
  isScrolledUp() {
461
- return this.distanceFromBottom() > X;
461
+ return this.distanceFromBottom() > Z;
462
462
  }
463
463
  distanceFromBottom() {
464
464
  return this.messagesEl.scrollHeight - this.messagesEl.scrollTop - this.messagesEl.clientHeight;
@@ -505,7 +505,7 @@ class Q {
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(), Z.forEach((e) => {
508
+ this.emojiPickerEl.replaceChildren(), Q.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,8 +513,8 @@ class Q {
513
513
  });
514
514
  }
515
515
  insertEmojiAtCursor(e) {
516
- var a, n;
517
- const t = this.input.value, s = (a = this.input.selectionStart) != null ? a : t.length, i = (n = this.input.selectionEnd) != null ? n : t.length;
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
519
  const o = s + e.length;
520
520
  this.input.setSelectionRange(o, o), this.input.dispatchEvent(new Event("input", { bubbles: !0 })), this.input.focus();
@@ -533,14 +533,40 @@ class Q {
533
533
  e.shiftKey && i === t ? (e.preventDefault(), s.focus()) : !e.shiftKey && i === s && (e.preventDefault(), t.focus());
534
534
  }
535
535
  }
536
- const y = 1500, D = 8e3;
537
- class ee {
536
+ const v = 1500, L = 8e3;
537
+ class te {
538
538
  constructor(e, t) {
539
- this.sessionId = null, this.currentKind = null, this.stopped = !1, this.backoff = y, this.connectPromise = null, this.options = e, this.handlers = t;
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 ee {
559
585
  }
560
586
  }
561
587
  async doConnect() {
562
- var a, n;
588
+ var l, n;
563
589
  this.stopped = !1;
564
- const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/session`, t = (n = (a = this.sessionId) != null ? a : this.options.sessionId) != null ? n : void 0, s = {
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: {
@@ -578,14 +605,15 @@ class ee {
578
605
  if (!i.ok)
579
606
  throw new Error(`SpilkiWidget: connect failed (${i.status})`);
580
607
  const o = await i.json();
581
- return this.sessionId = o.sessionId, this.options.sessionId = o.sessionId, this.backoff = y, await this.startTransport(o), o;
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 a, n;
611
+ var l, n, a;
585
612
  const s = {
586
- sessionId: (n = (a = this.sessionId) != null ? a : this.options.sessionId) != null ? 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");
@@ -606,7 +634,7 @@ class ee {
606
634
  }
607
635
  // #1: accept resetBackoff param; #8 #9: clear retryTimer
608
636
  stop(e = !0) {
609
- this.stopped = !0, e && (this.backoff = y), this.retryTimer && (clearTimeout(this.retryTimer), this.retryTimer = void 0), this.ws && (this.wsOnOpen && this.ws.removeEventListener("open", this.wsOnOpen), this.wsOnMessage && this.ws.removeEventListener("message", this.wsOnMessage), this.wsOnClose && this.ws.removeEventListener("close", this.wsOnClose), this.wsOnError && this.ws.removeEventListener("error", this.wsOnError), this.ws.close(), this.ws = void 0), this.wsOnOpen = this.wsOnMessage = this.wsOnClose = this.wsOnError = void 0, this.sseAbort && (this.sseAbort.abort(), this.sseAbort = void 0), this.pollTimer && (clearTimeout(this.pollTimer), this.pollTimer = void 0), this.currentKind = null;
637
+ this.stopped = !0, e && (this.backoff = v), this.retryTimer && (clearTimeout(this.retryTimer), this.retryTimer = void 0), this.ws && (this.wsOnOpen && this.ws.removeEventListener("open", this.wsOnOpen), this.wsOnMessage && this.ws.removeEventListener("message", this.wsOnMessage), this.wsOnClose && this.ws.removeEventListener("close", this.wsOnClose), this.wsOnError && this.ws.removeEventListener("error", this.wsOnError), this.ws.close(), this.ws = void 0), this.wsOnOpen = this.wsOnMessage = this.wsOnClose = this.wsOnError = void 0, this.sseAbort && (this.sseAbort.abort(), this.sseAbort = void 0), this.pollTimer && (clearTimeout(this.pollTimer), this.pollTimer = void 0), this.currentKind = null;
610
638
  }
611
639
  async startTransport(e) {
612
640
  if (this.stopped) return;
@@ -631,7 +659,7 @@ class ee {
631
659
  i.close();
632
660
  return;
633
661
  }
634
- this.currentKind = "ws", this.handlers.onOpen("ws"), this.backoff = y, t();
662
+ this.currentKind = "ws", this.handlers.onOpen("ws"), this.backoff = v, t();
635
663
  }, this.wsOnMessage = (o) => this.handleIncoming(o.data), this.wsOnClose = () => {
636
664
  this.stopped || this.retryFallback("ws");
637
665
  }, this.wsOnError = () => {
@@ -661,31 +689,31 @@ class ee {
661
689
  i.abort();
662
690
  return;
663
691
  }
664
- this.currentKind = "sse", this.handlers.onOpen("sse"), this.backoff = y, t();
665
- const a = o.body.getReader(), n = new TextDecoder();
666
- let l = "";
667
- const m = () => {
668
- a.read().then(({ done: g, value: h }) => {
669
- var f;
670
- if (g || this.stopped) {
692
+ this.currentKind = "sse", this.handlers.onOpen("sse"), this.backoff = v, t();
693
+ const l = o.body.getReader(), n = new TextDecoder();
694
+ let a = "";
695
+ const g = () => {
696
+ l.read().then(({ done: u, value: d }) => {
697
+ var m;
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
- l += n.decode(h, { stream: !0 });
675
- const d = l.split(`
702
+ a += n.decode(d, { stream: !0 });
703
+ const h = a.split(`
676
704
 
677
705
  `);
678
- l = (f = d.pop()) != null ? f : "";
679
- for (const b of d)
680
- for (const p of b.split(`
706
+ a = (m = h.pop()) != null ? m : "";
707
+ for (const f of h)
708
+ for (const p of f.split(`
681
709
  `))
682
710
  p.startsWith("data:") && this.handleIncoming(p.slice(5).trim());
683
- m();
684
- }).catch((g) => {
685
- i.signal.aborted || (this.handlers.onError(g), this.stopped || this.retryFallback("sse"));
711
+ g();
712
+ }).catch((u) => {
713
+ i.signal.aborted || (this.handlers.onError(u), this.stopped || this.retryFallback("sse"));
686
714
  });
687
715
  };
688
- m();
716
+ g();
689
717
  }).catch((o) => {
690
718
  i.signal.aborted || s(o);
691
719
  });
@@ -693,7 +721,7 @@ class ee {
693
721
  }
694
722
  // #15: add auth header to poll fetch
695
723
  async startPoll(e) {
696
- this.currentKind = "poll", this.handlers.onOpen("poll"), this.backoff = y;
724
+ this.currentKind = "poll", this.handlers.onOpen("poll"), this.backoff = v;
697
725
  const t = async () => {
698
726
  if (!this.stopped)
699
727
  try {
@@ -704,9 +732,9 @@ class ee {
704
732
  });
705
733
  if (!s.ok) throw new Error(`Poll failed ${s.status}`);
706
734
  const i = await s.json();
707
- E(i).forEach((o) => this.handlers.onMessage(o)), this.backoff = y;
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, D);
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 ee {
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, D), this.retryTimer = window.setTimeout(() => {
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 ee {
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" && se(e.payload) && this.handlers.onAgentEvent(e.payload);
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" && ie(e.payload) && this.handlers.onAgentEvent(e.payload);
739
767
  return;
740
768
  }
741
769
  this.handlers.onMessage(e);
742
770
  }
743
771
  }
744
- const te = /* @__PURE__ */ new Set(["TYPING", "THINKING", "SEARCHING", "EXECUTING_TOOL"]);
745
- function se(r) {
772
+ const se = /* @__PURE__ */ new Set(["TYPING", "THINKING", "SEARCHING", "EXECUTING_TOOL"]);
773
+ function ie(r) {
746
774
  if (typeof r != "object" || r === null) return !1;
747
775
  const e = r;
748
- return te.has(e.eventType) && typeof e.active == "boolean";
776
+ return se.has(e.eventType) && typeof e.active == "boolean";
749
777
  }
750
- const L = 30 * 60 * 1e3, ie = 3e4, S = 30;
751
- class re {
778
+ const U = 30 * 60 * 1e3, re = 3e4, A = 30;
779
+ class oe {
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 re {
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
- }, ie));
814
+ }, re));
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 re {
802
830
  addMessage(e) {
803
831
  var s, i;
804
832
  const t = {
805
- id: (s = e.id) != null ? s : z("msg"),
833
+ id: (s = e.id) != null ? s : _("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((o) => o.id === t.id) ? null : (this.state.messages = E([...this.state.messages, t], S), this.persistMessages(), this.touchActivity(), this.emit(), t);
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 = E(e, S), this.persistMessages(), this.emit();
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 re {
902
930
  }
903
931
  isSessionExpired() {
904
932
  const e = this.lastActivityTs;
905
- return e === 0 ? !1 : Date.now() - e >= L;
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 re {
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 >= L ? (t.push(s), s = { startTs: e[i].ts, messages: [e[i]] }) : s.messages.push(e[i]);
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,13 +958,13 @@ class re {
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) ? E(t, S) : [];
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(r) {
967
+ function ne(r) {
940
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(
@@ -947,24 +975,24 @@ function oe(r) {
947
975
  return s.from(t, "base64").toString("utf8");
948
976
  throw new Error("SpilkiWidget: no base64 decoder available");
949
977
  }
950
- function ne(r) {
978
+ function ae(r) {
951
979
  if (!r) return null;
952
980
  const e = r.split(".");
953
981
  if (e.length < 2) return null;
954
982
  try {
955
- const t = oe(e[1]);
983
+ const t = ne(e[1]);
956
984
  return JSON.parse(t);
957
985
  } catch (t) {
958
986
  return console.error("SpilkiWidget: unable to parse JWT", t), null;
959
987
  }
960
988
  }
961
- function ae(r) {
962
- const e = ne(r);
989
+ function le(r) {
990
+ const e = ae(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 le = {
995
+ const ce = {
968
996
  onOpen() {
969
997
  },
970
998
  onClose() {
@@ -976,7 +1004,7 @@ const le = {
976
1004
  onTransportChange() {
977
1005
  }
978
1006
  };
979
- async function ce(r, e, t) {
1007
+ async function de(r, e, t) {
980
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 },
@@ -993,16 +1021,16 @@ async function he(r, e) {
993
1021
  if (!s.ok) throw new Error(`SpilkiWidget: refresh failed (${s.status})`);
994
1022
  return (await s.json()).accessToken;
995
1023
  }
996
- function de(r) {
1024
+ function pe(r) {
997
1025
  let e = !1, t = null;
998
1026
  const s = [], i = () => {
999
1027
  var n;
1000
1028
  if (!r) return null;
1001
1029
  if (t) return t;
1002
1030
  try {
1003
- const l = (n = window.AudioContext) != null ? n : window.webkitAudioContext;
1004
- if (!l) return null;
1005
- t = new l();
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
  }
@@ -1015,51 +1043,51 @@ function de(r) {
1015
1043
  });
1016
1044
  } catch {
1017
1045
  }
1018
- }, a = (n) => {
1019
- const l = () => {
1020
- o(), window.removeEventListener(n, l, !0);
1046
+ }, l = (n) => {
1047
+ const a = () => {
1048
+ o(), window.removeEventListener(n, a, !0);
1021
1049
  };
1022
- s.push({ type: n, listener: l }), window.addEventListener(n, l, { capture: !0, passive: !0 });
1050
+ s.push({ type: n, listener: a }), window.addEventListener(n, a, { capture: !0, passive: !0 });
1023
1051
  };
1024
- return a("pointerdown"), a("keydown"), {
1052
+ return l("pointerdown"), l("keydown"), {
1025
1053
  markUserInteraction: o,
1026
1054
  play() {
1027
1055
  if (!r || !e) return;
1028
1056
  const n = i();
1029
1057
  if (!n || n.state !== "running") return;
1030
- const l = n.createGain();
1031
- l.gain.value = 0.15, l.connect(n.destination);
1032
- const m = (h, d, f) => {
1033
- const b = n.createOscillator(), p = n.createGain();
1034
- b.type = "sine", b.frequency.setValueAtTime(h, d), p.gain.setValueAtTime(1e-4, d), p.gain.exponentialRampToValueAtTime(1, d + 0.012), p.gain.exponentialRampToValueAtTime(1e-4, d + f), b.connect(p), p.connect(l), b.start(d), b.stop(d + f + 0.02);
1035
- }, g = n.currentTime;
1036
- m(880, g, 0.08), m(1175, g + 0.09, 0.08);
1058
+ const a = n.createGain();
1059
+ a.gain.value = 0.15, a.connect(n.destination);
1060
+ const g = (d, h, m) => {
1061
+ const f = n.createOscillator(), p = n.createGain();
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);
1063
+ }, u = n.currentTime;
1064
+ g(880, u, 0.08), g(1175, u + 0.09, 0.08);
1037
1065
  },
1038
1066
  destroy() {
1039
- s.forEach(({ type: n, listener: l }) => {
1040
- window.removeEventListener(n, l, !0);
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 U(r) {
1047
- var A, I, O, M;
1074
+ function $(r) {
1075
+ var O, M, B, j;
1048
1076
  if (!r.org)
1049
1077
  throw new Error("SpilkiWidget: org is required");
1050
- const e = H(r);
1051
- G(e.allowedOriginsHint);
1052
- const t = { ...le, ...(A = e.hooks) != null ? A : {} }, s = new re(e.org, { persist: e.persist }), i = de(e.sound);
1053
- let o = (I = s.accessToken) != null ? I : void 0, a = null;
1078
+ const e = z(r);
1079
+ q(e.allowedOriginsHint);
1080
+ const t = { ...ce, ...(O = e.hooks) != null ? O : {} }, s = new oe(e.org, { persist: e.persist }), i = pe(e.sound);
1081
+ let o = (M = s.accessToken) != null ? M : void 0, l = null;
1054
1082
  const n = () => {
1055
- g.setBadge(s.countUnread());
1056
- }, l = () => {
1057
- s.markRead(), g.setBadge(0);
1058
- }, m = async () => a || (a = (async () => {
1083
+ u.setBadge(s.countUnread());
1084
+ }, a = () => {
1085
+ s.markRead(), u.setBadge(0);
1086
+ }, g = async () => l || (l = (async () => {
1059
1087
  try {
1060
- if (o && ae(o))
1088
+ if (o && le(o))
1061
1089
  try {
1062
- o = await he(e.apiBase, o), s.persistAccessToken(o), d.setAccessToken(o);
1090
+ o = await he(e.apiBase, o), s.persistAccessToken(o), h.setAccessToken(o);
1063
1091
  return;
1064
1092
  } catch {
1065
1093
  o = void 0, s.clearAccessToken();
@@ -1067,32 +1095,32 @@ function U(r) {
1067
1095
  if (!o) {
1068
1096
  if (!r.installationToken) throw new Error("SpilkiWidget: missing installationToken");
1069
1097
  if (!r.org) throw new Error("SpilkiWidget: missing org");
1070
- o = await ce(e.apiBase, r.installationToken, r.org), s.persistAccessToken(o), d.setAccessToken(o);
1098
+ o = await de(e.apiBase, r.installationToken, r.org), s.persistAccessToken(o), h.setAccessToken(o);
1071
1099
  }
1072
1100
  } finally {
1073
- a = null;
1101
+ l = null;
1074
1102
  }
1075
- })(), a), g = R({
1103
+ })(), l), u = K({
1076
1104
  color: e.color,
1077
- position: (O = e.position) != null ? O : "bottom-right",
1105
+ position: (B = e.position) != null ? B : "bottom-right",
1078
1106
  onClick: () => {
1079
- s.snapshot.isOpen ? (s.close(), t.onClose()) : (i.markUserInteraction(), s.open(), l(), t.onOpen());
1107
+ s.snapshot.isOpen ? (s.close(), t.onClose()) : (i.markUserInteraction(), s.open(), a(), t.onOpen());
1080
1108
  }
1081
- }), h = new Q({
1109
+ }), d = new ee({
1082
1110
  color: e.color,
1083
1111
  theme: P(e.theme),
1084
- position: (M = e.position) != null ? M : "bottom-right",
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
- const u = s.addMessage({ author: "user", text: c });
1091
- u && (h.appendMessage(u), m().then(() => d.send(c, u.id)).catch((w) => {
1092
- t.onError(w), s.setConnected(!1), h.setOffline(!0);
1118
+ const k = s.addMessage({ author: "user", text: c });
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
- }), d = new ee(
1123
+ }), h = new te(
1096
1124
  {
1097
1125
  apiBase: e.apiBase,
1098
1126
  accessToken: o,
@@ -1101,11 +1129,11 @@ function U(r) {
1101
1129
  },
1102
1130
  {
1103
1131
  onOpen(c) {
1104
- C.transport = c, t.onTransportChange(c), s.setConnected(!0), h.setOffline(!1);
1132
+ C.transport = c, t.onTransportChange(c), s.setConnected(!0), d.setOffline(!1);
1105
1133
  },
1106
1134
  onMessage(c) {
1107
- const u = s.addMessage(c);
1108
- u && (h.appendMessage(u), !s.snapshot.isOpen && u.author === "bot" && (n(), i.play()), t.onMessage(u));
1135
+ const b = c.suggestedReplies, y = s.addMessage(c);
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,53 +1142,56 @@ function U(r) {
1114
1142
  s.handleAgentEvent(c.eventType, c.active);
1115
1143
  },
1116
1144
  onError(c) {
1117
- t.onError(c), s.setConnected(!1), s.snapshot.isOpen && h.setOffline(!0);
1145
+ t.onError(c), s.setConnected(!1), s.snapshot.isOpen && d.setOffline(!0);
1118
1146
  }
1119
1147
  }
1120
1148
  );
1121
- g.mount(), h.mount();
1122
- const f = s.snapshot.messages, b = s.isSessionExpired(), p = s.getConversationGroups();
1123
- if (f.length === 0 && e.welcome) {
1149
+ u.mount(), d.mount();
1150
+ const m = s.snapshot.messages, f = s.isSessionExpired(), p = s.getConversationGroups();
1151
+ if (m.length === 0 && e.welcome) {
1124
1152
  const c = {
1125
1153
  id: "welcome",
1126
1154
  author: "bot",
1127
1155
  text: e.welcome,
1128
1156
  ts: Date.now()
1129
1157
  };
1130
- s.addMessage(c), h.appendMessage(c);
1131
- } else b && f.length > 0 ? h.renderWithConversations(p, []) : p.length > 1 ? h.renderWithConversations(p.slice(0, -1), p[p.length - 1].messages) : h.updateMessages(f);
1132
- let x = !1;
1133
- const $ = s.subscribe(() => {
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;
1161
+ const W = s.subscribe(() => {
1134
1162
  const c = s.snapshot;
1135
- if (g.setOpen(c.isOpen), h.setAgentActivity(c.agentActivity, e.i18n), h.setOffline(!c.isConnected), h.updateTheme(P(e.theme)), c.isOpen) {
1136
- if (!x) {
1137
- const u = s.countUnread() > 0, w = s.lastReadTs;
1138
- l(), u && h.scrollToFirstUnread(w);
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) {
1165
+ const k = s.countUnread() > 0, b = s.lastReadTs;
1166
+ a(), k && d.scrollToFirstUnread(b);
1139
1167
  }
1140
- x = !0, h.show();
1168
+ T = !0, d.show();
1141
1169
  } else
1142
- x = !1, h.hide();
1143
- }), W = f.length === 0 || b;
1144
- n(), m().then(
1145
- () => d.connect().then((c) => {
1146
- var w, B;
1147
- e.persist && s.persistSession(c.sessionId), s.setConnected(!0), t.onTransportChange((w = d.kind) != null ? w : "ws");
1148
- const u = (B = c.suggestedReplies) != null ? B : [];
1149
- u.length > 0 && W && h.setSuggestedReplies(u);
1170
+ T = !1, d.hide();
1171
+ }), F = m.length === 0 || f;
1172
+ n(), g().then(
1173
+ () => h.connect().then((c) => {
1174
+ var b, y;
1175
+ e.persist && s.persistSession(c.sessionId), s.setConnected(!0), t.onTransportChange((b = h.kind) != null ? b : "ws");
1176
+ const k = (y = c.suggestedReplies) != null ? y : [];
1177
+ k.length > 0 && F && d.setSuggestedReplies(k);
1150
1178
  })
1151
1179
  ).catch((c) => {
1152
- t.onError(c), s.setConnected(!1), h.setOffline(!0);
1180
+ t.onError(c), s.setConnected(!1), d.setOffline(!0);
1153
1181
  });
1154
1182
  const C = {
1155
1183
  transport: null,
1156
1184
  open() {
1157
- i.markUserInteraction(), s.open(), l(), t.onOpen();
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
- $(), d.stop(), i.destroy(), g.destroy(), h.destroy();
1194
+ W(), h.stop(), i.destroy(), u.destroy(), d.destroy(), E === C && (E = null);
1164
1195
  }
1165
1196
  };
1166
1197
  return C;
@@ -1177,7 +1208,7 @@ function N() {
1177
1208
  console.error("SpilkiWidget: data-org and is required for auto init");
1178
1209
  return;
1179
1210
  }
1180
- U({
1211
+ E = $({
1181
1212
  org: t,
1182
1213
  installationToken: e.installationToken,
1183
1214
  apiBase: e.apiBase,
@@ -1185,7 +1216,10 @@ function N() {
1185
1216
  theme: (i = e.theme) != null ? i : void 0
1186
1217
  });
1187
1218
  }
1188
- typeof window != "undefined" && (window.SpilkiWidget = window.SpilkiWidget || {}, window.SpilkiWidget.init = (r) => U(r));
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
+ });
1189
1223
  N();
1190
1224
  N();
1191
1225
  //# sourceMappingURL=bootstrap.es.js.map