@spilki/widget 1.0.28 → 1.0.29

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/widget.es.js CHANGED
@@ -1,4 +1,4 @@
1
- const W = `
1
+ const F = `
2
2
  <style>
3
3
  :host {
4
4
  all: initial;
@@ -14,6 +14,7 @@ const W = `
14
14
  }
15
15
  button {
16
16
  all: unset;
17
+ position: relative;
17
18
  display: inline-flex;
18
19
  align-items: center;
19
20
  justify-content: center;
@@ -42,6 +43,24 @@ const W = `
42
43
  width: 100%;
43
44
  height: 100%;
44
45
  }
46
+ .badge {
47
+ position: absolute;
48
+ top: -4px;
49
+ right: -4px;
50
+ min-width: 20px;
51
+ height: 20px;
52
+ border-radius: 999px;
53
+ background: #ef4444;
54
+ color: #fff;
55
+ font: 600 11px/20px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
56
+ text-align: center;
57
+ padding: 0 6px;
58
+ box-sizing: border-box;
59
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
60
+ }
61
+ .badge[hidden] {
62
+ display: none;
63
+ }
45
64
  </style>
46
65
  <button type="button" aria-label="Open chat">
47
66
  <span class="icon" aria-hidden="true">
@@ -49,14 +68,15 @@ const W = `
49
68
  <path d="M5 6a3 3 0 0 1 3-3h16a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H14l-5 6v-6H8a3 3 0 0 1-3-3V6Z" fill="currentColor"/>
50
69
  </svg>
51
70
  </span>
71
+ <span class="badge" hidden aria-hidden="true">0</span>
52
72
  </button>
53
73
  `;
54
- function L(r) {
74
+ function R(r) {
55
75
  const e = document.createElement("div");
56
76
  e.setAttribute("part", "bubble-root"), e.setAttribute("data-position", r.position), e.style.setProperty("--spilki-accent", r.color);
57
77
  const t = e.attachShadow({ mode: "open" });
58
- t.innerHTML = W;
59
- const s = t.querySelector("button");
78
+ t.innerHTML = F;
79
+ const s = t.querySelector("button"), i = t.querySelector(".badge");
60
80
  return s.addEventListener("click", () => r.onClick()), {
61
81
  element: e,
62
82
  mount() {
@@ -65,109 +85,227 @@ function L(r) {
65
85
  destroy() {
66
86
  e.remove();
67
87
  },
68
- setOpen(o) {
69
- s.setAttribute("aria-expanded", String(o)), s.setAttribute("aria-label", o ? "Close chat" : "Open chat");
88
+ setOpen(a) {
89
+ s.setAttribute("aria-expanded", String(a)), s.setAttribute("aria-label", a ? "Close chat" : "Open chat");
90
+ },
91
+ setBadge(a) {
92
+ const n = Math.max(0, a), l = s.getAttribute("aria-expanded") === "true";
93
+ if (n === 0) {
94
+ i.toggleAttribute("hidden", !0), i.textContent = "0", l || s.setAttribute("aria-label", "Open chat");
95
+ return;
96
+ }
97
+ i.toggleAttribute("hidden", !1), i.textContent = n > 99 ? "99+" : String(n), l || s.setAttribute("aria-label", `Open chat, ${n} unread`);
70
98
  }
71
99
  };
72
100
  }
73
- const B = "https://api.spilki.app", S = {
101
+ const K = "https://api.spilki.app", T = {
74
102
  welcome: "Hi! I'm your assistant.",
75
103
  placeholder: "Type a message…",
76
104
  sendLabel: "Send",
77
105
  typing: "Assistant is typing…",
78
106
  offline: "Unable to connect. Please try again later.",
79
107
  title: "Spilki Assistant"
80
- }, g = {
81
- apiBase: B,
108
+ }, k = {
109
+ apiBase: K,
82
110
  position: "bottom-right",
83
111
  theme: "auto",
84
112
  color: "#6366f1",
85
- welcome: S.welcome,
113
+ welcome: T.welcome,
86
114
  persist: !0,
87
- i18n: S
115
+ sound: !0,
116
+ i18n: T
88
117
  };
89
- function K(r) {
90
- var t, s, i, o, c, a, n;
91
- const e = { ...S, ...(t = r.i18n) != null ? t : {} };
118
+ function N(r) {
119
+ var t, s, i, o, a, n, l, g;
120
+ const e = { ...T, ...(t = r.i18n) != null ? t : {} };
92
121
  return {
93
- ...g,
122
+ ...k,
94
123
  ...r,
95
- apiBase: (s = r.apiBase) != null ? s : g.apiBase,
124
+ apiBase: (s = r.apiBase) != null ? s : k.apiBase,
96
125
  i18n: e,
97
126
  welcome: (i = r.welcome) != null ? i : e.welcome,
98
- position: (o = r.position) != null ? o : g.position,
99
- theme: (c = r.theme) != null ? c : g.theme,
100
- color: (a = r.color) != null ? a : g.color,
101
- persist: (n = r.persist) != null ? n : g.persist
127
+ position: (o = r.position) != null ? o : k.position,
128
+ theme: (a = r.theme) != null ? a : k.theme,
129
+ color: (n = r.color) != null ? n : k.color,
130
+ persist: (l = r.persist) != null ? l : k.persist,
131
+ sound: (g = r.sound) != null ? g : k.sound
102
132
  };
103
133
  }
104
- function P(r = "msg") {
134
+ function H(r = "msg") {
105
135
  return typeof crypto != "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${r}-${Math.random().toString(16).slice(2)}`;
106
136
  }
107
- function D() {
137
+ function z() {
108
138
  var r, e;
109
139
  return (e = (r = window.matchMedia) == null ? void 0 : r.call(window, "(prefers-color-scheme: dark)").matches) != null ? e : !1;
110
140
  }
111
- function C(r) {
112
- return r === "light" || r === "dark" ? r : D() ? "dark" : "light";
141
+ function j(r) {
142
+ return r === "light" || r === "dark" ? r : z() ? "dark" : "light";
113
143
  }
114
- function y(r, e = 30) {
144
+ function x(r, e = 30) {
115
145
  return r.slice(-e);
116
146
  }
117
- function N(r) {
147
+ function q(r) {
118
148
  if (!r || r.length === 0) return;
119
149
  const e = window.location.origin;
120
150
  r.includes(e) || console.warn(
121
151
  `SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`
122
152
  );
123
153
  }
124
- const F = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
125
- function H(r) {
126
- const e = new Date(r), t = /* @__PURE__ */ new Date(), s = (n) => String(n).padStart(2, "0"), i = `${s(e.getHours())}:${s(e.getMinutes())}`, o = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(), c = o - 864e5, a = new Date(e.getFullYear(), e.getMonth(), e.getDate()).getTime();
127
- return a === o ? `Today at ${i}` : a === c ? `Yesterday at ${i}` : `${F[e.getMonth()]} ${e.getDate()}, ${i}`;
154
+ const _ = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
155
+ function J(r) {
156
+ 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();
157
+ return n === o ? `Today at ${i}` : n === a ? `Yesterday at ${i}` : `${_[e.getMonth()]} ${e.getDate()}, ${i}`;
128
158
  }
129
- const U = ':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-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{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}.message{display:flex;flex-direction:column;gap:.25rem;max-width:85%;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}.message.user{align-self:flex-end;text-align:right}.message .bubble{padding:.6rem .8rem;border-radius:1rem;background:#6366f126}.message.user .bubble{background:var(--spilki-accent);color:#fff}.message.bot .bubble{background:#94a3b826}.input-area{display:flex;align-items:flex-end;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--spilki-border)}.input-area textarea{flex:1;resize:none;min-height:2.5rem;max-height:6rem;border-radius:.75rem;border:1px solid var(--spilki-border);padding:.6rem .75rem;font-family:inherit;font-size:.95rem;background:var(--spilki-surface);color:var(--spilki-text);scrollbar-width:thin;.input-area textarea:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:-1px}scrollbar-color:rgba(148,163,184,.3) transparent}.input-area button{border:none;border-radius:999px;padding:.6rem 1.1rem;background:var(--spilki-accent);color:#fff;font-weight:600;cursor:pointer}.input-area button:focus-visible{outline:2px solid #fff;outline-offset:2px}.typing{font-size:.75rem;color:#94a3b8e6;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)}:host([data-theme="dark"]) .messages,:host([data-theme="dark"]) .input-area textarea{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)}: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:#0f172ad9}.messages::-webkit-scrollbar,.input-area textarea::-webkit-scrollbar{width:6px}.messages::-webkit-scrollbar-track,.input-area textarea::-webkit-scrollbar-track{background:transparent}.messages::-webkit-scrollbar-thumb,.input-area textarea::-webkit-scrollbar-thumb{background:#94a3b84d;border-radius:999px}.messages::-webkit-scrollbar-thumb:hover,.input-area textarea::-webkit-scrollbar-thumb:hover{background:#94a3b880}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .input-area textarea::-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{background:#ffffff59}.messages{scroll-behavior:smooth}.conversation-separator{display:flex;align-items:center;gap:.5rem;padding:.5rem 0;cursor:pointer;user-select:none;font-size:.7rem;color:#94a3b8b3;white-space:nowrap}.conversation-separator:before,.conversation-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.conversation-separator:hover{color:#94a3b8e6}.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}';
130
- function b(r) {
159
+ 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}', P = 24 * 60 * 60 * 1e3, Y = 2 * 60 * 1e3, G = 200, X = [
160
+ "😀",
161
+ "😂",
162
+ "🤣",
163
+ "😊",
164
+ "😍",
165
+ "🥰",
166
+ "😘",
167
+ "😉",
168
+ "🤔",
169
+ "😏",
170
+ "😎",
171
+ "🤗",
172
+ "😢",
173
+ "😭",
174
+ "😡",
175
+ "🤯",
176
+ "😱",
177
+ "🥳",
178
+ "😴",
179
+ "🤮",
180
+ "👍",
181
+ "👎",
182
+ "👋",
183
+ "🤝",
184
+ "🙏",
185
+ "💪",
186
+ "✌️",
187
+ "🤞",
188
+ "👏",
189
+ "🫶",
190
+ "❤️",
191
+ "🔥",
192
+ "⭐",
193
+ "💯",
194
+ "✅",
195
+ "❌",
196
+ "⚡",
197
+ "🎉",
198
+ "💰",
199
+ "📦",
200
+ "📅",
201
+ "📍",
202
+ "📞",
203
+ "💬",
204
+ "🔔",
205
+ "🔗",
206
+ "📸",
207
+ "🎵",
208
+ "☕",
209
+ "🍕"
210
+ ];
211
+ function v(r) {
131
212
  return r.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
132
213
  }
133
- class z {
214
+ class Z {
134
215
  constructor(e) {
135
- this.options = e, this.focusable = [], this.seenIds = /* @__PURE__ */ new Set(), 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" });
136
- const t = b(e.i18n.title), s = b(e.i18n.typing), i = b(e.i18n.offline), o = b(e.i18n.placeholder), c = b(e.i18n.sendLabel);
216
+ this.options = e, this.relativeTimeFormat = new Intl.RelativeTimeFormat(void 0, {
217
+ numeric: "auto"
218
+ }), this.absoluteTimeFormat = new Intl.DateTimeFormat(void 0, {
219
+ month: "short",
220
+ day: "numeric",
221
+ hour: "2-digit",
222
+ minute: "2-digit",
223
+ hour12: !1
224
+ }), this.dateSeparatorFormat = new Intl.DateTimeFormat(void 0, {
225
+ month: "short",
226
+ day: "numeric",
227
+ year: "numeric"
228
+ }), 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" });
229
+ 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);
137
230
  this.shadow.innerHTML = `
138
- <style>${U}</style>
231
+ <style>${V}</style>
139
232
  <div class="wrapper" role="dialog" aria-modal="true" aria-label="${t}">
140
233
  <header>
141
234
  <h1><span class="status-dot" aria-hidden="true"></span>${t}</h1>
142
235
  <button class="close" type="button" aria-label="Close">×</button>
143
236
  </header>
144
237
  <div class="messages" part="messages" role="log" aria-live="polite" aria-label="Chat messages"></div>
238
+ <button class="scroll-bottom" type="button" aria-label="Scroll to latest messages">
239
+ <span class="scroll-arrow" aria-hidden="true">▼</span>
240
+ <span class="scroll-count" hidden>0</span>
241
+ </button>
145
242
  <div class="typing" hidden aria-live="polite" aria-atomic="true">${s}</div>
146
243
  <div class="offline" hidden aria-live="assertive" aria-atomic="true">${i}</div>
244
+ <div class="suggested-replies" role="group" hidden aria-label="Suggested replies"></div>
245
+ <div class="emoji-picker" aria-label="Emoji picker"></div>
147
246
  <div class="input-area">
148
- <textarea rows="2" placeholder="${o}" aria-label="${o}"></textarea>
149
- <button type="button">${c}</button>
247
+ <div class="input-wrap">
248
+ <textarea rows="1" placeholder="${o}" aria-label="${o}"></textarea>
249
+ <div class="input-actions">
250
+ <button class="emoji-btn" type="button" aria-label="Insert emoji">
251
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
252
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
253
+ <path d="M8 14s1.5 2 4 2 4-2 4-2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
254
+ <circle cx="9" cy="10" r="1" fill="currentColor"/>
255
+ <circle cx="15" cy="10" r="1" fill="currentColor"/>
256
+ </svg>
257
+ </button>
258
+ <button type="button" class="send-btn" aria-label="${a}">
259
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
260
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" fill="currentColor"/>
261
+ </svg>
262
+ </button>
263
+ </div>
264
+ </div>
150
265
  </div>
151
266
  </div>
152
- `, this.host.dataset.theme = e.theme, this.host.style.setProperty("--spilki-accent", e.color), this.messagesEl = this.shadow.querySelector(".messages"), this.typingEl = this.shadow.querySelector(".typing"), this.input = this.shadow.querySelector("textarea"), this.offlineEl = this.shadow.querySelector(".offline"), this.sendButton = this.shadow.querySelector(".input-area button"), this.closeButton = this.shadow.querySelector("header .close"), this.handleCloseClick = () => this.options.onClose(), this.handleSendClick = () => this.send(), this.handleInputKeydown = (a) => {
153
- a.key === "Enter" && !a.shiftKey ? (a.preventDefault(), this.send()) : a.key === "Escape" && this.options.onClose();
154
- }, this.handleShadowKeydown = (a) => {
155
- const n = a;
156
- n.key === "Escape" && this.options.onClose(), n.key === "Tab" && this.trapFocus(n);
157
- }, this.handleFocusin = () => this.collectFocusable(), this.closeButton.addEventListener("click", this.handleCloseClick), this.sendButton.addEventListener("click", this.handleSendClick), this.input.addEventListener("keydown", this.handleInputKeydown), this.shadow.addEventListener("keydown", this.handleShadowKeydown), this.shadow.addEventListener("focusin", this.handleFocusin), this.collectFocusable();
267
+ `, this.host.dataset.theme = e.theme, this.host.style.setProperty("--spilki-accent", e.color), this.messagesEl = this.shadow.querySelector(".messages"), this.typingEl = this.shadow.querySelector(".typing"), this.input = this.shadow.querySelector("textarea"), this.offlineEl = this.shadow.querySelector(".offline"), this.sendButton = this.shadow.querySelector(".send-btn"), this.emojiButton = this.shadow.querySelector(".emoji-btn"), this.emojiPickerEl = this.shadow.querySelector(".emoji-picker"), this.scrollBottomButton = this.shadow.querySelector(".scroll-bottom"), this.scrollBottomCountEl = this.shadow.querySelector(".scroll-count"), this.closeButton = this.shadow.querySelector("header .close"), this.suggestedRepliesEl = this.shadow.querySelector(".suggested-replies"), this.handleCloseClick = () => this.options.onClose(), this.handleSendClick = () => this.send(), this.handleInputKeydown = (n) => {
268
+ if (n.key === "Enter" && !n.shiftKey)
269
+ n.preventDefault(), this.send();
270
+ else if (n.key === "Escape") {
271
+ if (this.emojiPickerOpen) {
272
+ n.stopPropagation(), this.closeEmojiPicker();
273
+ return;
274
+ }
275
+ this.options.onClose();
276
+ }
277
+ }, this.handleShadowKeydown = (n) => {
278
+ const l = n;
279
+ if (l.key === "Escape") {
280
+ if (this.emojiPickerOpen) {
281
+ l.preventDefault(), this.closeEmojiPicker();
282
+ return;
283
+ }
284
+ this.options.onClose();
285
+ }
286
+ l.key === "Tab" && this.trapFocus(l);
287
+ }, this.handleFocusin = () => this.collectFocusable(), this.handleMessagesScroll = () => this.updateScrollBottomState(), this.handleScrollBottomClick = () => {
288
+ this.scrollToBottom(), this.resetScrollUnreadCount();
289
+ }, this.handleEmojiClick = () => {
290
+ this.toggleEmojiPicker();
291
+ }, this.handleDocumentPointerDown = (n) => {
292
+ if (!this.emojiPickerOpen) return;
293
+ const l = n.composedPath();
294
+ l.includes(this.emojiPickerEl) || l.includes(this.emojiButton) || this.closeEmojiPicker();
295
+ }, 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();
158
296
  }
159
297
  mount() {
160
298
  document.body.appendChild(this.host);
161
299
  }
162
300
  // #4: remove all event listeners before removing DOM
163
301
  destroy() {
164
- this.closeButton.removeEventListener("click", this.handleCloseClick), this.sendButton.removeEventListener("click", this.handleSendClick), this.input.removeEventListener("keydown", this.handleInputKeydown), this.shadow.removeEventListener("keydown", this.handleShadowKeydown), this.shadow.removeEventListener("focusin", this.handleFocusin), this.host.remove();
302
+ this.closeButton.removeEventListener("click", this.handleCloseClick), this.sendButton.removeEventListener("click", this.handleSendClick), this.emojiButton.removeEventListener("click", this.handleEmojiClick), this.scrollBottomButton.removeEventListener("click", this.handleScrollBottomClick), this.input.removeEventListener("keydown", this.handleInputKeydown), this.messagesEl.removeEventListener("scroll", this.handleMessagesScroll), this.shadow.removeEventListener("keydown", this.handleShadowKeydown), this.shadow.removeEventListener("focusin", this.handleFocusin), document.removeEventListener("pointerdown", this.handleDocumentPointerDown, !0), this.host.remove();
165
303
  }
166
304
  show() {
167
305
  this.open || (this.open = !0, this.host.style.display = "block", this.focusInput());
168
306
  }
169
307
  hide() {
170
- this.open && (this.open = !1, this.host.style.display = "none");
308
+ this.open && (this.open = !1, this.closeEmojiPicker(), this.host.style.display = "none");
171
309
  }
172
310
  focusInput() {
173
311
  queueMicrotask(() => {
@@ -179,25 +317,43 @@ class z {
179
317
  }
180
318
  // #16: rebuild seen IDs on full re-render
181
319
  updateMessages(e) {
182
- this.messagesEl.innerHTML = "", this.seenIds.clear(), e.forEach((t) => {
183
- this.seenIds.add(t.id), this.messagesEl.appendChild(this.createMessageElement(t));
184
- }), this.messagesEl.scrollTop = this.messagesEl.scrollHeight, this.collectFocusable();
320
+ this.messagesEl.innerHTML = "", this.seenIds.clear(), this.renderedMessages.length = 0, this.lastTimelineMessage = null, e.forEach((t) => {
321
+ this.seenIds.add(t.id), this.appendTimelineMessage(t);
322
+ }), this.updateTimestampVisibility(), this.scrollToBottom(), this.resetScrollUnreadCount(), this.collectFocusable();
185
323
  }
186
324
  // Render conversation groups with collapsed history separators
187
325
  renderWithConversations(e, t) {
188
- this.messagesEl.innerHTML = "", this.seenIds.clear();
326
+ this.messagesEl.innerHTML = "", this.seenIds.clear(), this.renderedMessages.length = 0, this.lastTimelineMessage = null;
189
327
  for (const s of e) {
190
- s.messages.forEach((a) => this.seenIds.add(a.id));
191
- const i = this.createHistoryContainer(s.messages), o = s.messages[s.messages.length - 1].ts, c = this.createSeparatorElement(o, i);
192
- this.messagesEl.appendChild(i), this.messagesEl.appendChild(c);
328
+ s.messages.forEach((n) => this.seenIds.add(n.id));
329
+ const i = this.createHistoryContainer(s.messages), o = s.messages[s.messages.length - 1].ts, a = this.createSeparatorElement(o, i);
330
+ this.messagesEl.appendChild(i), this.messagesEl.appendChild(a);
193
331
  }
194
332
  t.forEach((s) => {
195
- this.seenIds.add(s.id), this.messagesEl.appendChild(this.createMessageElement(s));
196
- }), this.messagesEl.scrollTop = this.messagesEl.scrollHeight, this.collectFocusable();
333
+ this.seenIds.add(s.id), this.appendTimelineMessage(s);
334
+ }), this.updateTimestampVisibility(), this.scrollToBottom(), this.resetScrollUnreadCount(), this.collectFocusable();
197
335
  }
198
336
  // #16: deduplicate; #19: smart scroll; #21: update focus trap
199
337
  appendMessage(e) {
200
- this.seenIds.has(e.id) || (this.seenIds.add(e.id), this.messagesEl.appendChild(this.createMessageElement(e)), this.scrollToBottomIfNeeded(), this.collectFocusable());
338
+ if (this.seenIds.has(e.id)) return;
339
+ const t = this.isScrolledUp();
340
+ this.seenIds.add(e.id), this.appendTimelineMessage(e), this.updateTimestampVisibility(), t ? (this.scrollUnreadCount += 1, this.updateScrollBottomState()) : (this.scrollToBottom(), this.resetScrollUnreadCount()), this.collectFocusable();
341
+ }
342
+ setSuggestedReplies(e) {
343
+ if (e.length === 0) {
344
+ this.hideSuggestedReplies();
345
+ return;
346
+ }
347
+ this.suggestedRepliesEl.replaceChildren(), e.forEach((t) => {
348
+ const s = document.createElement("button");
349
+ s.className = "suggested-chip", s.type = "button", s.textContent = t.text, s.addEventListener("click", () => {
350
+ var i;
351
+ this.options.onSend((i = t.payload) != null ? i : t.text), this.hideSuggestedReplies();
352
+ }), this.suggestedRepliesEl.appendChild(s);
353
+ }), this.suggestedRepliesEl.toggleAttribute("hidden", !1), this.collectFocusable();
354
+ }
355
+ hideSuggestedReplies() {
356
+ this.suggestedRepliesEl.toggleAttribute("hidden", !0), this.suggestedRepliesEl.replaceChildren(), this.collectFocusable(), this.focusInput();
201
357
  }
202
358
  setTyping(e) {
203
359
  this.typingEl.toggleAttribute("hidden", !e);
@@ -210,22 +366,19 @@ class z {
210
366
  }
211
367
  send() {
212
368
  const e = this.input.value.trim();
213
- e && (this.options.onSend(e), this.clearInput());
369
+ e && (this.options.onSend(e), this.clearInput(), this.hideSuggestedReplies());
214
370
  }
215
- // #29 #34: extract shared message element creation; #25: semantic labels
216
371
  createMessageElement(e) {
217
372
  const t = document.createElement("div");
218
- t.className = `message ${e.author}`, t.setAttribute("data-author", e.author), t.setAttribute("role", "article"), t.setAttribute(
219
- "aria-label",
220
- // #25
221
- e.author === "user" ? "You" : "Assistant"
222
- );
373
+ t.className = `message ${e.author}`, t.setAttribute("data-author", e.author), t.setAttribute("role", "article"), t.setAttribute("aria-label", e.author === "user" ? "You" : "Assistant");
223
374
  const s = document.createElement("div");
224
- return s.className = "bubble", s.textContent = e.text, t.appendChild(s), t;
375
+ s.className = "bubble", s.textContent = e.text;
376
+ const i = document.createElement("span");
377
+ return i.className = "msg-time", i.textContent = this.formatMessageTimestamp(e.ts), t.appendChild(s), t.appendChild(i), { item: t, timeEl: i };
225
378
  }
226
379
  createSeparatorElement(e, t) {
227
380
  const s = document.createElement("div");
228
- 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 = H(e);
381
+ 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 = J(e);
229
382
  const i = () => {
230
383
  const o = t.classList.toggle("expanded");
231
384
  s.setAttribute("aria-expanded", String(o)), s.setAttribute(
@@ -234,27 +387,127 @@ class z {
234
387
  );
235
388
  };
236
389
  return s.addEventListener("click", i), s.addEventListener("keydown", (o) => {
237
- const c = o;
238
- (c.key === "Enter" || c.key === " ") && (c.preventDefault(), i());
390
+ const a = o;
391
+ (a.key === "Enter" || a.key === " ") && (a.preventDefault(), i());
239
392
  }), s;
240
393
  }
241
394
  createHistoryContainer(e) {
242
395
  const t = document.createElement("div");
243
- return t.className = "conversation-history", e.forEach((s) => {
244
- t.appendChild(this.createMessageElement(s));
396
+ t.className = "conversation-history";
397
+ let s = null;
398
+ return e.forEach((i) => {
399
+ this.appendDateSeparatorIfNeeded(s, i, t);
400
+ const { item: o } = this.createMessageElement(i);
401
+ t.appendChild(o), s = i;
245
402
  }), t;
246
403
  }
247
- // #19: only auto-scroll if user is near the bottom
248
- scrollToBottomIfNeeded() {
249
- const e = this.messagesEl;
250
- e.scrollHeight - e.scrollTop - e.clientHeight < 100 && (e.scrollTop = e.scrollHeight);
404
+ appendTimelineMessage(e) {
405
+ this.appendDateSeparatorIfNeeded(this.lastTimelineMessage, e, this.messagesEl);
406
+ const t = this.createMessageElement(e);
407
+ this.messagesEl.appendChild(t.item), this.renderedMessages.push({ message: e, timeEl: t.timeEl }), this.lastTimelineMessage = e;
408
+ }
409
+ appendDateSeparatorIfNeeded(e, t, s) {
410
+ if (!e || this.isSameDay(e.ts, t.ts))
411
+ return;
412
+ const i = document.createElement("div");
413
+ i.className = "date-separator", i.textContent = this.formatDateSeparator(t.ts), s.appendChild(i);
414
+ }
415
+ updateTimestampVisibility() {
416
+ for (let e = 0; e < this.renderedMessages.length; e += 1) {
417
+ const t = this.renderedMessages[e], s = this.renderedMessages[e + 1], i = !!s && s.message.author === t.message.author && s.message.ts - t.message.ts <= Y && s.message.ts >= t.message.ts;
418
+ t.timeEl.toggleAttribute("hidden", i), t.timeEl.textContent = this.formatMessageTimestamp(t.message.ts);
419
+ }
420
+ }
421
+ formatMessageTimestamp(e) {
422
+ const s = Date.now() - e;
423
+ if (s < P) {
424
+ const i = Math.max(1, Math.round(s / 6e4));
425
+ if (i < 60)
426
+ return this.relativeTimeFormat.format(-i, "minute");
427
+ const o = Math.max(1, Math.round(i / 60));
428
+ return this.relativeTimeFormat.format(-o, "hour");
429
+ }
430
+ return this.absoluteTimeFormat.format(new Date(e));
431
+ }
432
+ formatDateSeparator(e) {
433
+ const t = new Date(e), s = this.startOfDay(Date.now()), i = this.startOfDay(e);
434
+ return i === s ? "Today" : i === s - P ? "Yesterday" : this.dateSeparatorFormat.format(t);
435
+ }
436
+ isSameDay(e, t) {
437
+ return this.startOfDay(e) === this.startOfDay(t);
438
+ }
439
+ startOfDay(e) {
440
+ const t = new Date(e);
441
+ return new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime();
442
+ }
443
+ isScrolledUp() {
444
+ return this.distanceFromBottom() > G;
445
+ }
446
+ distanceFromBottom() {
447
+ return this.messagesEl.scrollHeight - this.messagesEl.scrollTop - this.messagesEl.clientHeight;
448
+ }
449
+ scrollToFirstUnread(e) {
450
+ if (e <= 0) {
451
+ this.scrollToBottom();
452
+ return;
453
+ }
454
+ const t = this.renderedMessages.find(
455
+ (i) => i.message.author === "bot" && i.message.ts > e
456
+ );
457
+ if (!t) {
458
+ this.scrollToBottom();
459
+ return;
460
+ }
461
+ const s = t.timeEl.closest(".message");
462
+ s && s.scrollIntoView({ behavior: "smooth", block: "start" });
463
+ }
464
+ scrollToBottom() {
465
+ if (typeof this.messagesEl.scrollTo == "function") {
466
+ this.messagesEl.scrollTo({ top: this.messagesEl.scrollHeight, behavior: "smooth" });
467
+ return;
468
+ }
469
+ this.messagesEl.scrollTop = this.messagesEl.scrollHeight;
470
+ }
471
+ updateScrollBottomState() {
472
+ const e = this.isScrolledUp();
473
+ this.scrollBottomButton.classList.toggle("visible", e), this.scrollBottomButton.setAttribute("aria-hidden", String(!e)), this.scrollBottomButton.tabIndex = e ? 0 : -1, e || this.resetScrollUnreadCount();
474
+ const t = this.scrollUnreadCount > 0;
475
+ this.scrollBottomCountEl.toggleAttribute("hidden", !t), this.scrollBottomCountEl.textContent = t ? this.scrollUnreadCount > 99 ? "99+" : String(this.scrollUnreadCount) : "0";
476
+ }
477
+ resetScrollUnreadCount() {
478
+ this.scrollUnreadCount !== 0 && (this.scrollUnreadCount = 0, this.updateScrollBottomState());
479
+ }
480
+ toggleEmojiPicker() {
481
+ if (this.emojiPickerOpen) {
482
+ this.closeEmojiPicker();
483
+ return;
484
+ }
485
+ this.emojiPickerOpen = !0, this.emojiPickerEl.style.display = "grid", this.emojiButton.setAttribute("aria-expanded", "true"), this.collectFocusable();
486
+ }
487
+ closeEmojiPicker() {
488
+ this.emojiPickerOpen && (this.emojiPickerOpen = !1, this.emojiPickerEl.style.display = "none", this.emojiButton.setAttribute("aria-expanded", "false"), this.collectFocusable());
489
+ }
490
+ renderEmojiPicker() {
491
+ this.emojiPickerEl.replaceChildren(), X.forEach((e) => {
492
+ const t = document.createElement("button");
493
+ t.type = "button", t.className = "emoji-option", t.textContent = e, t.setAttribute("aria-label", `Insert ${e}`), t.addEventListener("click", () => {
494
+ this.insertEmojiAtCursor(e), this.closeEmojiPicker();
495
+ }), this.emojiPickerEl.appendChild(t);
496
+ });
497
+ }
498
+ insertEmojiAtCursor(e) {
499
+ var a, n;
500
+ const t = this.input.value, s = (a = this.input.selectionStart) != null ? a : t.length, i = (n = this.input.selectionEnd) != null ? n : t.length;
501
+ this.input.value = `${t.slice(0, s)}${e}${t.slice(i)}`;
502
+ const o = s + e.length;
503
+ this.input.setSelectionRange(o, o), this.input.dispatchEvent(new Event("input", { bubbles: !0 })), this.input.focus();
251
504
  }
252
505
  collectFocusable() {
253
506
  const e = this.shadow.querySelectorAll(
254
507
  'button, textarea, [href], [tabindex]:not([tabindex="-1"])'
255
508
  );
256
509
  this.focusable.length = 0, e.forEach((t) => {
257
- t.hasAttribute("disabled") || this.focusable.push(t);
510
+ !t.hasAttribute("disabled") && !t.hasAttribute("hidden") && t.tabIndex >= 0 && this.focusable.push(t);
258
511
  });
259
512
  }
260
513
  trapFocus(e) {
@@ -263,10 +516,10 @@ class z {
263
516
  e.shiftKey && i === t ? (e.preventDefault(), s.focus()) : !e.shiftKey && i === s && (e.preventDefault(), t.focus());
264
517
  }
265
518
  }
266
- const f = 1500, I = 8e3;
267
- class J {
519
+ const w = 1500, D = 8e3;
520
+ class Q {
268
521
  constructor(e, t) {
269
- this.sessionId = null, this.currentKind = null, this.stopped = !1, this.backoff = f, this.connectPromise = null, this.options = e, this.handlers = t;
522
+ this.sessionId = null, this.currentKind = null, this.stopped = !1, this.backoff = w, this.connectPromise = null, this.options = e, this.handlers = t;
270
523
  }
271
524
  setAccessToken(e) {
272
525
  this.options.accessToken = e;
@@ -289,9 +542,9 @@ class J {
289
542
  }
290
543
  }
291
544
  async doConnect() {
292
- var c, a;
545
+ var a, n;
293
546
  this.stopped = !1;
294
- const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/session`, t = (a = (c = this.sessionId) != null ? c : this.options.sessionId) != null ? a : void 0, s = {
547
+ const e = `${this.options.apiBase.replace(/\/$/, "")}/widget/session`, t = (n = (a = this.sessionId) != null ? a : this.options.sessionId) != null ? n : void 0, s = {
295
548
  organisationId: this.options.org,
296
549
  sessionId: t,
297
550
  userAgent: typeof navigator != "undefined" ? navigator.userAgent : "",
@@ -308,12 +561,12 @@ class J {
308
561
  if (!i.ok)
309
562
  throw new Error(`SpilkiWidget: connect failed (${i.status})`);
310
563
  const o = await i.json();
311
- return this.sessionId = o.sessionId, this.options.sessionId = o.sessionId, this.backoff = f, await this.startTransport(o), o;
564
+ return this.sessionId = o.sessionId, this.options.sessionId = o.sessionId, this.backoff = w, await this.startTransport(o), o;
312
565
  }
313
566
  async send(e, t) {
314
- var c, a;
567
+ var a, n;
315
568
  const s = {
316
- sessionId: (a = (c = this.sessionId) != null ? c : this.options.sessionId) != null ? a : "",
569
+ sessionId: (n = (a = this.sessionId) != null ? a : this.options.sessionId) != null ? n : "",
317
570
  text: e,
318
571
  ...t ? { messageId: t } : {}
319
572
  };
@@ -336,7 +589,7 @@ class J {
336
589
  }
337
590
  // #1: accept resetBackoff param; #8 #9: clear retryTimer
338
591
  stop(e = !0) {
339
- this.stopped = !0, e && (this.backoff = f), 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;
592
+ this.stopped = !0, e && (this.backoff = w), 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;
340
593
  }
341
594
  async startTransport(e) {
342
595
  if (this.stopped) return;
@@ -361,7 +614,7 @@ class J {
361
614
  i.close();
362
615
  return;
363
616
  }
364
- this.currentKind = "ws", this.handlers.onOpen("ws"), this.backoff = f, t();
617
+ this.currentKind = "ws", this.handlers.onOpen("ws"), this.backoff = w, t();
365
618
  }, this.wsOnMessage = (o) => this.handleIncoming(o.data), this.wsOnClose = () => {
366
619
  this.stopped || this.retryFallback("ws");
367
620
  }, this.wsOnError = () => {
@@ -391,31 +644,31 @@ class J {
391
644
  i.abort();
392
645
  return;
393
646
  }
394
- this.currentKind = "sse", this.handlers.onOpen("sse"), this.backoff = f, t();
395
- const c = o.body.getReader(), a = new TextDecoder();
396
- let n = "";
397
- const h = () => {
398
- c.read().then(({ done: u, value: v }) => {
399
- var w;
400
- if (u || this.stopped) {
647
+ this.currentKind = "sse", this.handlers.onOpen("sse"), this.backoff = w, t();
648
+ const a = o.body.getReader(), n = new TextDecoder();
649
+ let l = "";
650
+ const g = () => {
651
+ a.read().then(({ done: m, value: d }) => {
652
+ var f;
653
+ if (m || this.stopped) {
401
654
  this.stopped || (this.handlers.onError(new Error("SpilkiWidget: SSE stream ended")), this.retryFallback("sse"));
402
655
  return;
403
656
  }
404
- n += a.decode(v, { stream: !0 });
405
- const d = n.split(`
657
+ l += n.decode(d, { stream: !0 });
658
+ const h = l.split(`
406
659
 
407
660
  `);
408
- n = (w = d.pop()) != null ? w : "";
409
- for (const k of d)
410
- for (const m of k.split(`
661
+ l = (f = h.pop()) != null ? f : "";
662
+ for (const b of h)
663
+ for (const p of b.split(`
411
664
  `))
412
- m.startsWith("data:") && this.handleIncoming(m.slice(5).trim());
413
- h();
414
- }).catch((u) => {
415
- i.signal.aborted || (this.handlers.onError(u), this.stopped || this.retryFallback("sse"));
665
+ p.startsWith("data:") && this.handleIncoming(p.slice(5).trim());
666
+ g();
667
+ }).catch((m) => {
668
+ i.signal.aborted || (this.handlers.onError(m), this.stopped || this.retryFallback("sse"));
416
669
  });
417
670
  };
418
- h();
671
+ g();
419
672
  }).catch((o) => {
420
673
  i.signal.aborted || s(o);
421
674
  });
@@ -423,7 +676,7 @@ class J {
423
676
  }
424
677
  // #15: add auth header to poll fetch
425
678
  async startPoll(e) {
426
- this.currentKind = "poll", this.handlers.onOpen("poll"), this.backoff = f;
679
+ this.currentKind = "poll", this.handlers.onOpen("poll"), this.backoff = w;
427
680
  const t = async () => {
428
681
  if (!this.stopped)
429
682
  try {
@@ -434,9 +687,9 @@ class J {
434
687
  });
435
688
  if (!s.ok) throw new Error(`Poll failed ${s.status}`);
436
689
  const i = await s.json();
437
- y(i).forEach((o) => this.handlers.onMessage(o)), this.backoff = f;
690
+ x(i).forEach((o) => this.handlers.onMessage(o)), this.backoff = w;
438
691
  } catch (s) {
439
- this.handlers.onError(s), this.backoff = Math.min(this.backoff * 1.5, I);
692
+ this.handlers.onError(s), this.backoff = Math.min(this.backoff * 1.5, D);
440
693
  } finally {
441
694
  this.stopped || (this.pollTimer = window.setTimeout(t, this.backoff));
442
695
  }
@@ -445,7 +698,7 @@ class J {
445
698
  }
446
699
  // #1: preserve backoff by calling stop(false); #8: check stopped before reconnect
447
700
  retryFallback(e) {
448
- this.stopped || (this.stop(!1), this.backoff = Math.min(this.backoff * 1.5, I), this.retryTimer = window.setTimeout(() => {
701
+ this.stopped || (this.stop(!1), this.backoff = Math.min(this.backoff * 1.5, D), this.retryTimer = window.setTimeout(() => {
449
702
  this.stopped || (this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)), this.connect().catch((t) => this.handlers.onError(t)));
450
703
  }, this.backoff));
451
704
  }
@@ -471,15 +724,15 @@ class J {
471
724
  this.handlers.onMessage(e);
472
725
  }
473
726
  }
474
- const A = 30 * 60 * 1e3, x = 30;
475
- class q {
727
+ const L = 30 * 60 * 1e3, S = 30;
728
+ class ee {
476
729
  constructor(e, t) {
477
730
  this.org = e, this.listeners = /* @__PURE__ */ new Set(), this.state = {
478
731
  isOpen: !1,
479
732
  isTyping: !1,
480
733
  isConnected: !1,
481
734
  messages: []
482
- }, this.historyKey = `spilki-history:${e}`, this.sessionKey = `spilki-session:${e}`, this.tokenKey = `spilki-token:${e}`, this.activityKey = `spilki-activity:${e}`, this.persist = t.persist, this.state.messages = this.loadMessages();
735
+ }, this.historyKey = `spilki-history:${e}`, this.sessionKey = `spilki-session:${e}`, this.tokenKey = `spilki-token:${e}`, this.activityKey = `spilki-activity:${e}`, this.lastReadKey = `spilki-lastread:${e}`, this.persist = t.persist, this.state.messages = this.loadMessages();
483
736
  }
484
737
  get snapshot() {
485
738
  return {
@@ -507,15 +760,15 @@ class q {
507
760
  addMessage(e) {
508
761
  var s, i;
509
762
  const t = {
510
- id: (s = e.id) != null ? s : P("msg"),
763
+ id: (s = e.id) != null ? s : H("msg"),
511
764
  ts: (i = e.ts) != null ? i : Date.now(),
512
765
  author: e.author,
513
766
  text: e.text
514
767
  };
515
- return this.state.messages = y([...this.state.messages, t], x), this.persistMessages(), this.touchActivity(), this.emit(), t;
768
+ return this.state.messages.some((o) => o.id === t.id) ? null : (this.state.messages = x([...this.state.messages, t], S), this.persistMessages(), this.touchActivity(), this.emit(), t);
516
769
  }
517
770
  setMessages(e) {
518
- this.state.messages = y(e, x), this.persistMessages(), this.emit();
771
+ this.state.messages = x(e, S), this.persistMessages(), this.emit();
519
772
  }
520
773
  clearMessages() {
521
774
  this.state.messages = [], this.persistMessages(), this.emit();
@@ -585,9 +838,29 @@ class q {
585
838
  console.error("SpilkiWidget: unable to set item", e);
586
839
  }
587
840
  }
841
+ get lastReadTs() {
842
+ if (!this.persist) return 0;
843
+ try {
844
+ const e = localStorage.getItem(this.lastReadKey);
845
+ return e ? Number(e) : 0;
846
+ } catch {
847
+ return 0;
848
+ }
849
+ }
850
+ markRead() {
851
+ if (this.persist)
852
+ try {
853
+ localStorage.setItem(this.lastReadKey, String(Date.now()));
854
+ } catch {
855
+ }
856
+ }
857
+ countUnread() {
858
+ const e = this.lastReadTs;
859
+ return e === 0 ? 0 : this.state.messages.filter((t) => t.author === "bot" && t.ts > e).length;
860
+ }
588
861
  isSessionExpired() {
589
862
  const e = this.lastActivityTs;
590
- return e === 0 ? !1 : Date.now() - e >= A;
863
+ return e === 0 ? !1 : Date.now() - e >= L;
591
864
  }
592
865
  getConversationGroups() {
593
866
  const e = this.state.messages;
@@ -595,7 +868,7 @@ class q {
595
868
  const t = [];
596
869
  let s = { startTs: e[0].ts, messages: [e[0]] };
597
870
  for (let i = 1; i < e.length; i++)
598
- e[i].ts - e[i - 1].ts >= A ? (t.push(s), s = { startTs: e[i].ts, messages: [e[i]] }) : s.messages.push(e[i]);
871
+ e[i].ts - e[i - 1].ts >= L ? (t.push(s), s = { startTs: e[i].ts, messages: [e[i]] }) : s.messages.push(e[i]);
599
872
  return t.push(s), t;
600
873
  }
601
874
  emit() {
@@ -615,13 +888,13 @@ class q {
615
888
  const e = localStorage.getItem(this.historyKey);
616
889
  if (!e) return [];
617
890
  const t = JSON.parse(e);
618
- return Array.isArray(t) ? y(t, x) : [];
891
+ return Array.isArray(t) ? x(t, S) : [];
619
892
  } catch (e) {
620
893
  return console.error("SpilkiWidget: unable to load messages", e), [];
621
894
  }
622
895
  }
623
896
  }
624
- function _(r) {
897
+ function te(r) {
625
898
  const e = r.replace(/-/g, "+").replace(/_/g, "/"), t = e.padEnd(e.length + (4 - e.length % 4) % 4, "=");
626
899
  if (typeof atob == "function")
627
900
  return decodeURIComponent(
@@ -632,24 +905,24 @@ function _(r) {
632
905
  return s.from(t, "base64").toString("utf8");
633
906
  throw new Error("SpilkiWidget: no base64 decoder available");
634
907
  }
635
- function j(r) {
908
+ function se(r) {
636
909
  if (!r) return null;
637
910
  const e = r.split(".");
638
911
  if (e.length < 2) return null;
639
912
  try {
640
- const t = _(e[1]);
913
+ const t = te(e[1]);
641
914
  return JSON.parse(t);
642
915
  } catch (t) {
643
916
  return console.error("SpilkiWidget: unable to parse JWT", t), null;
644
917
  }
645
918
  }
646
- function Y(r) {
647
- const e = j(r);
919
+ function ie(r) {
920
+ const e = se(r);
648
921
  if (!(e != null && e.exp) || typeof e.exp != "number") return !0;
649
922
  const t = Math.floor(Date.now() / 1e3);
650
923
  return e.exp < t + 60;
651
924
  }
652
- const R = {
925
+ const re = {
653
926
  onOpen() {
654
927
  },
655
928
  onClose() {
@@ -661,7 +934,7 @@ const R = {
661
934
  onTransportChange() {
662
935
  }
663
936
  };
664
- async function X(r, e, t) {
937
+ async function oe(r, e, t) {
665
938
  const s = `${r.replace(/\/$/, "")}/widget/install`, i = await fetch(s, {
666
939
  method: "POST",
667
940
  headers: { "Content-Type": "application/json", Origin: window.location.origin },
@@ -670,7 +943,7 @@ async function X(r, e, t) {
670
943
  if (!i.ok) throw new Error(`SpilkiWidget: install failed (${i.status})`);
671
944
  return (await i.json()).accessToken;
672
945
  }
673
- async function G(r, e) {
946
+ async function ne(r, e) {
674
947
  const t = `${r.replace(/\/$/, "")}/widget/refresh`, s = await fetch(t, {
675
948
  method: "POST",
676
949
  headers: { "X-Authorization": `Bearer ${e}`, Origin: window.location.origin }
@@ -678,112 +951,176 @@ async function G(r, e) {
678
951
  if (!s.ok) throw new Error(`SpilkiWidget: refresh failed (${s.status})`);
679
952
  return (await s.json()).accessToken;
680
953
  }
681
- function M(r) {
682
- var m, E, T, O;
954
+ function ae(r) {
955
+ let e = !1, t = null;
956
+ const s = [], i = () => {
957
+ var n;
958
+ if (!r) return null;
959
+ if (t) return t;
960
+ try {
961
+ const l = (n = window.AudioContext) != null ? n : window.webkitAudioContext;
962
+ if (!l) return null;
963
+ t = new l();
964
+ } catch {
965
+ return null;
966
+ }
967
+ return t;
968
+ }, o = () => {
969
+ e = !0;
970
+ try {
971
+ const n = i();
972
+ (n == null ? void 0 : n.state) === "suspended" && n.resume().catch(() => {
973
+ });
974
+ } catch {
975
+ }
976
+ }, a = (n) => {
977
+ const l = () => {
978
+ o(), window.removeEventListener(n, l, !0);
979
+ };
980
+ s.push({ type: n, listener: l }), window.addEventListener(n, l, { capture: !0, passive: !0 });
981
+ };
982
+ return a("pointerdown"), a("keydown"), {
983
+ markUserInteraction: o,
984
+ play() {
985
+ if (!r || !e) return;
986
+ const n = i();
987
+ if (!n || n.state !== "running") return;
988
+ const l = n.createGain();
989
+ l.gain.value = 0.15, l.connect(n.destination);
990
+ const g = (d, h, f) => {
991
+ const b = n.createOscillator(), p = n.createGain();
992
+ b.type = "sine", b.frequency.setValueAtTime(d, h), p.gain.setValueAtTime(1e-4, h), p.gain.exponentialRampToValueAtTime(1, h + 0.012), p.gain.exponentialRampToValueAtTime(1e-4, h + f), b.connect(p), p.connect(l), b.start(h), b.stop(h + f + 0.02);
993
+ }, m = n.currentTime;
994
+ g(880, m, 0.08), g(1175, m + 0.09, 0.08);
995
+ },
996
+ destroy() {
997
+ s.forEach(({ type: n, listener: l }) => {
998
+ window.removeEventListener(n, l, !0);
999
+ }), s.length = 0, t && t.close().catch(() => {
1000
+ }), t = null;
1001
+ }
1002
+ };
1003
+ }
1004
+ function U(r) {
1005
+ var O, M, I, A;
683
1006
  if (!r.org)
684
1007
  throw new Error("SpilkiWidget: org is required");
685
- const e = K(r);
686
- N(e.allowedOriginsHint);
687
- const t = { ...R, ...(m = e.hooks) != null ? m : {} }, s = new q(e.org, { persist: e.persist });
688
- let i = (E = s.accessToken) != null ? E : void 0, o = null;
689
- const c = async () => o || (o = (async () => {
1008
+ const e = N(r);
1009
+ q(e.allowedOriginsHint);
1010
+ const t = { ...re, ...(O = e.hooks) != null ? O : {} }, s = new ee(e.org, { persist: e.persist }), i = ae(e.sound);
1011
+ let o = (M = s.accessToken) != null ? M : void 0, a = null;
1012
+ const n = () => {
1013
+ m.setBadge(s.countUnread());
1014
+ }, l = () => {
1015
+ s.markRead(), m.setBadge(0);
1016
+ }, g = async () => a || (a = (async () => {
690
1017
  try {
691
- if (i && Y(i))
1018
+ if (o && ie(o))
692
1019
  try {
693
- i = await G(e.apiBase, i), s.persistAccessToken(i), h.setAccessToken(i);
1020
+ o = await ne(e.apiBase, o), s.persistAccessToken(o), h.setAccessToken(o);
694
1021
  return;
695
1022
  } catch {
696
- i = void 0, s.clearAccessToken();
1023
+ o = void 0, s.clearAccessToken();
697
1024
  }
698
- if (!i) {
1025
+ if (!o) {
699
1026
  if (!r.installationToken) throw new Error("SpilkiWidget: missing installationToken");
700
1027
  if (!r.org) throw new Error("SpilkiWidget: missing org");
701
- i = await X(e.apiBase, r.installationToken, r.org), s.persistAccessToken(i), h.setAccessToken(i);
1028
+ o = await oe(e.apiBase, r.installationToken, r.org), s.persistAccessToken(o), h.setAccessToken(o);
702
1029
  }
703
1030
  } finally {
704
- o = null;
1031
+ a = null;
705
1032
  }
706
- })(), o), a = L({
1033
+ })(), a), m = R({
707
1034
  color: e.color,
708
- position: (T = e.position) != null ? T : "bottom-right",
1035
+ position: (I = e.position) != null ? I : "bottom-right",
709
1036
  onClick: () => {
710
- s.snapshot.isOpen ? (s.close(), t.onClose()) : (s.open(), t.onOpen());
1037
+ s.snapshot.isOpen ? (s.close(), t.onClose()) : (i.markUserInteraction(), s.open(), l(), t.onOpen());
711
1038
  }
712
- }), n = new z({
1039
+ }), d = new Z({
713
1040
  color: e.color,
714
- theme: C(e.theme),
715
- position: (O = e.position) != null ? O : "bottom-right",
1041
+ theme: j(e.theme),
1042
+ position: (A = e.position) != null ? A : "bottom-right",
716
1043
  i18n: e.i18n,
717
1044
  onClose: () => {
718
1045
  s.close(), t.onClose();
719
1046
  },
720
- onSend: (l) => {
721
- const p = s.addMessage({ author: "user", text: l });
722
- n.appendMessage(p), c().then(() => h.send(l, p.id)).catch(($) => {
723
- t.onError($), s.setConnected(!1), n.setOffline(!0);
724
- });
1047
+ onSend: (c) => {
1048
+ const u = s.addMessage({ author: "user", text: c });
1049
+ u && (d.appendMessage(u), g().then(() => h.send(c, u.id)).catch((y) => {
1050
+ t.onError(y), s.setConnected(!1), d.setOffline(!0);
1051
+ }));
725
1052
  }
726
- }), h = new J(
1053
+ }), h = new Q(
727
1054
  {
728
1055
  apiBase: e.apiBase,
729
- accessToken: i,
1056
+ accessToken: o,
730
1057
  org: e.org,
731
1058
  sessionId: s.sessionId
732
1059
  },
733
1060
  {
734
- onOpen(l) {
735
- k.transport = l, t.onTransportChange(l), s.setConnected(!0), n.setOffline(!1);
1061
+ onOpen(c) {
1062
+ C.transport = c, t.onTransportChange(c), s.setConnected(!0), d.setOffline(!1);
736
1063
  },
737
- onMessage(l) {
738
- const p = s.addMessage(l);
739
- n.appendMessage(p), t.onMessage(p);
1064
+ onMessage(c) {
1065
+ const u = s.addMessage(c);
1066
+ u && (d.appendMessage(u), !s.snapshot.isOpen && u.author === "bot" && (n(), i.play()), t.onMessage(u));
740
1067
  },
741
- onTyping(l) {
742
- s.setTyping(l);
1068
+ onTyping(c) {
1069
+ s.setTyping(c);
743
1070
  },
744
- onError(l) {
745
- t.onError(l), s.setConnected(!1), s.snapshot.isOpen && n.setOffline(!0);
1071
+ onError(c) {
1072
+ t.onError(c), s.setConnected(!1), s.snapshot.isOpen && d.setOffline(!0);
746
1073
  }
747
1074
  }
748
1075
  );
749
- a.mount(), n.mount();
750
- const u = s.snapshot.messages, v = s.isSessionExpired(), d = s.getConversationGroups();
751
- if (u.length === 0 && e.welcome) {
752
- const l = {
1076
+ m.mount(), d.mount();
1077
+ const f = s.snapshot.messages, b = s.isSessionExpired(), p = s.getConversationGroups();
1078
+ if (f.length === 0 && e.welcome) {
1079
+ const c = {
753
1080
  id: "welcome",
754
1081
  author: "bot",
755
1082
  text: e.welcome,
756
1083
  ts: Date.now()
757
1084
  };
758
- s.addMessage(l), n.appendMessage(l);
759
- } else v && u.length > 0 ? n.renderWithConversations(d, []) : d.length > 1 ? n.renderWithConversations(d.slice(0, -1), d[d.length - 1].messages) : n.updateMessages(u);
760
- const w = s.subscribe(() => {
761
- const l = s.snapshot;
762
- a.setOpen(l.isOpen), n.setTyping(l.isTyping), n.setOffline(!l.isConnected), n.updateTheme(C(e.theme)), l.isOpen ? n.show() : n.hide();
763
- });
764
- c().then(
765
- () => h.connect().then((l) => {
766
- var p;
767
- e.persist && s.persistSession(l.sessionId), s.setConnected(!0), t.onTransportChange((p = h.kind) != null ? p : "ws");
1085
+ s.addMessage(c), d.appendMessage(c);
1086
+ } else b && f.length > 0 ? d.renderWithConversations(p, []) : p.length > 1 ? d.renderWithConversations(p.slice(0, -1), p[p.length - 1].messages) : d.updateMessages(f);
1087
+ let E = !1;
1088
+ const $ = s.subscribe(() => {
1089
+ const c = s.snapshot;
1090
+ if (m.setOpen(c.isOpen), d.setTyping(c.isTyping), d.setOffline(!c.isConnected), d.updateTheme(j(e.theme)), c.isOpen) {
1091
+ if (!E) {
1092
+ const u = s.countUnread() > 0, y = s.lastReadTs;
1093
+ l(), u && d.scrollToFirstUnread(y);
1094
+ }
1095
+ E = !0, d.show();
1096
+ } else
1097
+ E = !1, d.hide();
1098
+ }), W = f.length === 0 || b;
1099
+ n(), g().then(
1100
+ () => h.connect().then((c) => {
1101
+ var y, B;
1102
+ e.persist && s.persistSession(c.sessionId), s.setConnected(!0), t.onTransportChange((y = h.kind) != null ? y : "ws");
1103
+ const u = (B = c.suggestedReplies) != null ? B : [];
1104
+ u.length > 0 && W && d.setSuggestedReplies(u);
768
1105
  })
769
- ).catch((l) => {
770
- t.onError(l), s.setConnected(!1), n.setOffline(!0);
1106
+ ).catch((c) => {
1107
+ t.onError(c), s.setConnected(!1), d.setOffline(!0);
771
1108
  });
772
- const k = {
1109
+ const C = {
773
1110
  transport: null,
774
1111
  open() {
775
- s.open(), t.onOpen();
1112
+ i.markUserInteraction(), s.open(), l(), t.onOpen();
776
1113
  },
777
1114
  close() {
778
1115
  s.close(), t.onClose();
779
1116
  },
780
1117
  destroy() {
781
- w(), h.stop(), a.destroy(), n.destroy();
1118
+ $(), h.stop(), i.destroy(), m.destroy(), d.destroy();
782
1119
  }
783
1120
  };
784
- return k;
1121
+ return C;
785
1122
  }
786
- function V() {
1123
+ function le() {
787
1124
  var s, i;
788
1125
  if (typeof document == "undefined") return;
789
1126
  const r = document.currentScript;
@@ -795,7 +1132,7 @@ function V() {
795
1132
  console.error("SpilkiWidget: data-org and is required for auto init");
796
1133
  return;
797
1134
  }
798
- M({
1135
+ U({
799
1136
  org: t,
800
1137
  installationToken: e.installationToken,
801
1138
  apiBase: e.apiBase,
@@ -803,10 +1140,10 @@ function V() {
803
1140
  theme: (i = e.theme) != null ? i : void 0
804
1141
  });
805
1142
  }
806
- typeof window != "undefined" && (window.SpilkiWidget = window.SpilkiWidget || {}, window.SpilkiWidget.init = (r) => M(r));
807
- V();
1143
+ typeof window != "undefined" && (window.SpilkiWidget = window.SpilkiWidget || {}, window.SpilkiWidget.init = (r) => U(r));
1144
+ le();
808
1145
  export {
809
- V as autoInit,
810
- M as initSpilkiWidget
1146
+ le as autoInit,
1147
+ U as initSpilkiWidget
811
1148
  };
812
1149
  //# sourceMappingURL=widget.es.js.map