@sendystack/widget 0.1.2 → 0.1.3

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/index.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/index.ts
2
- var e = "sendystack-widget-root", t = (e) => `sendystack_sessions_${e}`, n = (e) => `sendystack_current_${e}`, r = (e) => `sendystack_crawled_${e}`, i = "<svg viewBox=\"0 0 24 24\" width=\"26\" height=\"26\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 5h13a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H9l-5 4v-4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2z\"/></svg>", a = "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>", o = "<svg viewBox=\"0 0 24 24\" width=\"19\" height=\"19\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 12l16-7-7 16-2-7-7-2z\"/></svg>", s = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3l2.5 5 5 .7-3.7 3.5.9 5L12 19.8 7.3 17.2l.9-5L4.5 8.7l5-.7z\"/></svg>", c = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 7v5l3 3\"/></svg>", l = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14M5 12h14\"/></svg>", u = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 18l-6-6 6-6\"/></svg>";
3
- async function d(e, t) {
2
+ var e = "https://app.sendystack.org/sendy_stack_favico.png", t = "sendystack-widget-root", n = (e) => `sendystack_sessions_${e}`, r = (e) => `sendystack_current_${e}`, i = (e) => `sendystack_crawled_${e}`, a = "<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>", o = "<svg viewBox=\"0 0 24 24\" width=\"19\" height=\"19\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 12l16-7-7 16-2-7-7-2z\"/></svg>", s = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 7v5l3 3\"/></svg>", c = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14M5 12h14\"/></svg>", l = "<svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 18l-6-6 6-6\"/></svg>";
3
+ async function u(e, t) {
4
4
  let n = await (await fetch(`${e}/embedConfig?embedToken=${encodeURIComponent(t)}`)).json();
5
5
  if (!n.ok) throw Error(n.error || "failed to load widget config");
6
6
  return {
@@ -8,37 +8,39 @@ async function d(e, t) {
8
8
  whiteLabel: !!n.whiteLabel
9
9
  };
10
10
  }
11
- function f(t) {
12
- let n = t.position === "left" ? "left" : "right", r = document.createElement("style");
11
+ function ee(e) {
12
+ let n = e.position === "left" ? "left" : "right", r = document.createElement("style");
13
13
  r.textContent = `
14
- #${e} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
15
- #${e}{position:fixed;${n}:20px;bottom:20px;z-index:2147483000;}
16
- .ssk-launcher{position:relative;width:60px;height:60px;border-radius:50%;border:none;cursor:pointer;color:#fff;
17
- background:linear-gradient(140deg,${t.accent2},${t.accent});box-shadow:0 16px 36px rgba(0,0,0,.28);
14
+ #${t} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
15
+ #${t}{position:fixed;${n}:20px;bottom:20px;z-index:2147483000;}
16
+ .ssk-launcher{position:relative;width:60px;height:60px;border-radius:50%;border:none;cursor:pointer;color:${e.accent};
17
+ background:#fff;box-shadow:0 16px 36px rgba(0,0,0,.22),0 0 0 1px rgba(27,39,51,.05);
18
18
  display:flex;align-items:center;justify-content:center;transition:transform .25s cubic-bezier(.16,1,.3,1);margin-left:auto;}
19
19
  .ssk-launcher:hover{transform:translateY(-3px) scale(1.04);}
20
20
  .ssk-launcher:active{transform:scale(.96);}
21
21
  .ssk-launcher .ssk-open{display:flex;} .ssk-launcher .ssk-x{display:none;}
22
- #${e}.open .ssk-launcher .ssk-open{display:none;} #${e}.open .ssk-launcher .ssk-x{display:flex;}
23
- .ssk-pulse{position:absolute;inset:0;border-radius:50%;background:${t.accent};opacity:.5;z-index:-1;animation:sskPulse 2.4s ease-out infinite;}
22
+ .ssk-launcher .ssk-open img{width:32px;height:32px;object-fit:contain;}
23
+ #${t}.open .ssk-launcher .ssk-open{display:none;} #${t}.open .ssk-launcher .ssk-x{display:flex;}
24
+ .ssk-pulse{position:absolute;inset:0;border-radius:50%;background:${e.accent};opacity:.5;z-index:-1;animation:sskPulse 2.4s ease-out infinite;}
24
25
  @keyframes sskPulse{0%{transform:scale(1);opacity:.5;}70%,100%{transform:scale(1.7);opacity:0;}}
25
26
  @media(prefers-reduced-motion:reduce){.ssk-pulse{animation:none;}}
26
- .ssk-greeting{position:absolute;bottom:72px;${n}:0;max-width:220px;padding:11px 14px;border-radius:14px;background:#fff;
27
+ .ssk-greeting{position:absolute;bottom:72px;${n}:0;width:max-content;max-width:min(300px,calc(100vw - 48px));padding:11px 16px;border-radius:14px;background:#fff;
27
28
  color:#1b2733;font-size:13.5px;line-height:1.4;font-weight:600;box-shadow:0 16px 38px rgba(27,39,51,.18);
28
29
  transform:translateY(8px);opacity:0;pointer-events:none;transition:opacity .3s,transform .3s;}
29
30
  .ssk-greeting.shown{opacity:1;transform:translateY(0);}
30
- #${e}.open .ssk-greeting{display:none;}
31
+ #${t}.open .ssk-greeting{display:none;}
31
32
  .ssk-panel{position:absolute;bottom:74px;${n}:0;width:368px;max-width:calc(100vw - 32px);height:560px;
32
33
  max-height:calc(100vh - 110px);background:#fff;border-radius:20px;overflow:hidden;display:flex;flex-direction:column;
33
34
  box-shadow:0 36px 80px rgba(20,20,30,.3);border:1px solid rgba(27,39,51,.08);
34
35
  transform:translateY(14px) scale(.96);opacity:0;pointer-events:none;transform-origin:bottom ${n};
35
36
  transition:transform .25s cubic-bezier(.16,1,.3,1),opacity .2s;}
36
- #${e}.open .ssk-panel{transform:translateY(0) scale(1);opacity:1;pointer-events:auto;}
37
+ #${t}.open .ssk-panel{transform:translateY(0) scale(1);opacity:1;pointer-events:auto;}
37
38
  .ssk-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px 14px;flex-shrink:0;
38
- background:linear-gradient(135deg,${t.accent2},${t.accent});color:#fff;}
39
+ background:linear-gradient(135deg,${e.accent2},${e.accent});color:#fff;}
39
40
  .ssk-id{display:flex;align-items:center;gap:9px;min-width:0;}
40
41
  .ssk-avatar{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;
41
- background:rgba(255,255,255,.22);flex:0 0 auto;}
42
+ background:#fff;flex:0 0 auto;}
43
+ .ssk-avatar img{width:22px;height:22px;object-fit:contain;}
42
44
  .ssk-id-text{min-width:0;}
43
45
  .ssk-id-text strong{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
44
46
  .ssk-status{display:flex;align-items:center;gap:5px;font-size:11px;opacity:.9;}
@@ -70,24 +72,24 @@ function f(t) {
70
72
  .ssk-bubble-text{padding:10px 13px;border-radius:15px;font-size:14px;line-height:1.5;word-wrap:break-word;}
71
73
  .ssk-msg.bot .ssk-bubble-text{background:#fff;border:1px solid rgba(27,39,51,.08);border-bottom-left-radius:5px;
72
74
  box-shadow:0 5px 14px rgba(27,39,51,.05);color:#1b2733;}
73
- .ssk-msg.user .ssk-bubble-text{background:linear-gradient(135deg,${t.accent2},${t.accent});color:#fff;border-bottom-right-radius:5px;}
74
- .ssk-bubble-text a{color:${t.accent};font-weight:700;text-decoration:underline;}
75
+ .ssk-msg.user .ssk-bubble-text{background:linear-gradient(135deg,${e.accent2},${e.accent});color:#fff;border-bottom-right-radius:5px;}
76
+ .ssk-bubble-text a{color:${e.accent};font-weight:700;text-decoration:underline;}
75
77
  .ssk-msg.user .ssk-bubble-text a{color:#fff;}
76
78
  .ssk-bubble-text p{margin:0 0 7px;} .ssk-bubble-text p:last-child{margin:0;}
77
79
  .ssk-typing{padding:0 14px 10px;flex-shrink:0;}
78
80
  .ssk-typing[hidden]{display:none;}
79
81
  .ssk-dots{display:inline-flex;gap:5px;padding:10px 13px;background:#fff;border:1px solid rgba(27,39,51,.08);
80
82
  border-radius:15px;border-bottom-left-radius:5px;}
81
- .ssk-dots i{width:7px;height:7px;border-radius:50%;background:${t.accent};animation:sskWave 1.2s ease-in-out infinite;}
83
+ .ssk-dots i{width:7px;height:7px;border-radius:50%;background:${e.accent};animation:sskWave 1.2s ease-in-out infinite;}
82
84
  .ssk-dots i:nth-child(2){animation-delay:.15s;} .ssk-dots i:nth-child(3){animation-delay:.3s;}
83
85
  @keyframes sskWave{0%,60%,100%{transform:translateY(0);opacity:.5;}30%{transform:translateY(-5px);opacity:1;}}
84
86
  .ssk-inputrow{display:flex;align-items:flex-end;gap:8px;padding:11px;border-top:1px solid rgba(27,39,51,.08);
85
87
  background:#fff;flex-shrink:0;}
86
88
  .ssk-inputrow textarea{flex:1;resize:none;max-height:90px;border:1px solid rgba(27,39,51,.14);border-radius:13px;
87
89
  padding:10px 12px;font-size:14px;line-height:1.4;color:#1b2733;outline:none;background:#fbf7f1;}
88
- .ssk-inputrow textarea:focus{border-color:${t.accent};background:#fff;}
90
+ .ssk-inputrow textarea:focus{border-color:${e.accent};background:#fff;}
89
91
  .ssk-sendbtn{width:42px;height:42px;flex:0 0 auto;border:none;border-radius:12px;cursor:pointer;color:#fff;
90
- background:linear-gradient(135deg,${t.accent2},${t.accent});display:flex;align-items:center;justify-content:center;
92
+ background:linear-gradient(135deg,${e.accent2},${e.accent});display:flex;align-items:center;justify-content:center;
91
93
  box-shadow:0 9px 20px rgba(0,0,0,.18);transition:transform .15s;}
92
94
  .ssk-sendbtn:hover{transform:translateY(-2px);}
93
95
  .ssk-sendbtn:disabled{opacity:.6;cursor:default;transform:none;}
@@ -95,47 +97,47 @@ function f(t) {
95
97
  .ssk-foot a{color:inherit;text-decoration:underline;}
96
98
  `, document.head.appendChild(r);
97
99
  }
98
- function p(e) {
100
+ function d(e) {
99
101
  return `<p>${e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener\">$1</a>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\n{2,}/g, "</p><p>").replace(/\n/g, "<br>")}</p>`;
100
102
  }
101
- function m(e, t) {
103
+ function f(e, t) {
102
104
  let n = document.createElement("div");
103
105
  n.className = `ssk-msg ${t.role}`;
104
106
  let r = document.createElement("div");
105
- r.className = "ssk-bubble-text", r.innerHTML = p(t.text), n.appendChild(r), e.appendChild(n), e.scrollTop = e.scrollHeight;
107
+ r.className = "ssk-bubble-text", r.innerHTML = d(t.text), n.appendChild(r), e.appendChild(n), e.scrollTop = e.scrollHeight;
106
108
  }
107
- function ee(e) {
109
+ function p(e) {
108
110
  try {
109
- return JSON.parse(localStorage.getItem(t(e)) || "[]");
111
+ return JSON.parse(localStorage.getItem(n(e)) || "[]");
110
112
  } catch {
111
113
  return [];
112
114
  }
113
115
  }
114
- function h(e, n) {
116
+ function m(e, t) {
115
117
  try {
116
- localStorage.setItem(t(e), JSON.stringify(n.slice(0, 40)));
118
+ localStorage.setItem(n(e), JSON.stringify(t.slice(0, 40)));
117
119
  } catch {}
118
120
  }
119
- function g(e) {
121
+ function h(e) {
120
122
  try {
121
- return localStorage.getItem(n(e));
123
+ return localStorage.getItem(r(e));
122
124
  } catch {
123
125
  return null;
124
126
  }
125
127
  }
126
- function _(e, t) {
128
+ function g(e, t) {
127
129
  try {
128
- localStorage.setItem(n(e), t);
130
+ localStorage.setItem(r(e), t);
129
131
  } catch {}
130
132
  }
131
- function v() {
133
+ function _() {
132
134
  return `s_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
133
135
  }
134
- function y(e) {
136
+ function v(e) {
135
137
  let t = e.find((e) => e.role === "user");
136
138
  return t ? t.text.slice(0, 60) : "New conversation";
137
139
  }
138
- function te(e) {
140
+ function y(e) {
139
141
  let t = new Date(e), n = /* @__PURE__ */ new Date();
140
142
  return t.toDateString() === n.toDateString() ? t.toLocaleTimeString(void 0, {
141
143
  hour: "numeric",
@@ -146,9 +148,9 @@ function te(e) {
146
148
  });
147
149
  }
148
150
  function b() {
149
- let t = document.body.cloneNode(!0);
150
- t.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${e}`).forEach((e) => e.remove());
151
- let n = (t.textContent || "").replace(/\s+/g, " ").trim();
151
+ let e = document.body.cloneNode(!0);
152
+ e.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${t}`).forEach((e) => e.remove());
153
+ let n = (e.textContent || "").replace(/\s+/g, " ").trim();
152
154
  return {
153
155
  title: document.title,
154
156
  text: n
@@ -172,7 +174,7 @@ async function x(e, t) {
172
174
  async function S(e, t) {
173
175
  let n = !1;
174
176
  try {
175
- n = !!sessionStorage.getItem(r(t)), sessionStorage.setItem(r(t), "1");
177
+ n = !!sessionStorage.getItem(i(t)), sessionStorage.setItem(i(t), "1");
176
178
  } catch {}
177
179
  n || await fetch(`${e}/crawlSite`, {
178
180
  method: "POST",
@@ -180,51 +182,51 @@ async function S(e, t) {
180
182
  body: JSON.stringify({ embedToken: t })
181
183
  }).catch(() => {});
182
184
  }
183
- async function C(t) {
184
- if (typeof document > "u" || document.getElementById(e)) return;
185
- let n = (t.token || "").trim();
186
- if (!n) {
185
+ async function C(n) {
186
+ if (typeof document > "u" || document.getElementById(t)) return;
187
+ let r = (n.token || "").trim();
188
+ if (!r) {
187
189
  console.error("[Sendystack] init() called without a token");
188
190
  return;
189
191
  }
190
- let r = t.apiBase || "https://us-central1-sendystack-fab32.cloudfunctions.net", p;
192
+ let i = n.apiBase || "https://us-central1-sendystack-fab32.cloudfunctions.net", d;
191
193
  try {
192
- p = await d(r, n);
194
+ d = await u(i, r);
193
195
  } catch (e) {
194
196
  console.error("[Sendystack] failed to load widget config:", e);
195
197
  return;
196
198
  }
197
- f(p);
199
+ ee(d);
198
200
  let b = document.createElement("div");
199
- b.id = e;
200
- let C = ee(n), w = C.length === 0, T = g(n), E = C.find((e) => e.id === T);
201
+ b.id = t;
202
+ let C = p(r), w = C.length === 0, T = h(r), E = C.find((e) => e.id === T);
201
203
  E || (E = {
202
- id: v(),
204
+ id: _(),
203
205
  title: "New conversation",
204
206
  messages: [],
205
207
  updatedAt: Date.now()
206
- }, C.unshift(E), T = E.id, _(n, T), h(n, C));
208
+ }, C.unshift(E), T = E.id, g(r, T), m(r, C));
207
209
  let D = document.createElement("div");
208
210
  D.className = "ssk-body";
209
211
  function O() {
210
212
  D.innerHTML = "";
211
213
  let e = E.messages.length ? E.messages : [{
212
214
  role: "bot",
213
- text: p.welcomeMessage
215
+ text: d.welcomeMessage
214
216
  }];
215
- for (let t of e) m(D, t);
217
+ for (let t of e) f(D, t);
216
218
  }
217
219
  O();
218
220
  let k = document.createElement("span");
219
- k.className = "ssk-avatar", k.innerHTML = s;
221
+ k.className = "ssk-avatar", k.innerHTML = `<img src="${e}" alt="" />`;
220
222
  let A = document.createElement("div");
221
- A.className = "ssk-id-text", A.innerHTML = `<strong>${p.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;
223
+ A.className = "ssk-id-text", A.innerHTML = `<strong>${d.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;
222
224
  let j = document.createElement("div");
223
225
  j.className = "ssk-id", j.append(k, A);
224
226
  let M = document.createElement("button");
225
- M.className = "ssk-iconbtn", M.innerHTML = c, M.setAttribute("aria-label", "Chat history");
227
+ M.className = "ssk-iconbtn", M.innerHTML = s, M.setAttribute("aria-label", "Chat history");
226
228
  let N = document.createElement("button");
227
- N.className = "ssk-iconbtn", N.innerHTML = l, N.setAttribute("aria-label", "New chat");
229
+ N.className = "ssk-iconbtn", N.innerHTML = c, N.setAttribute("aria-label", "New chat");
228
230
  let P = document.createElement("button");
229
231
  P.className = "ssk-closebtn", P.innerHTML = a, P.setAttribute("aria-label", "Close chat");
230
232
  let F = document.createElement("div");
@@ -232,7 +234,7 @@ async function C(t) {
232
234
  let I = document.createElement("div");
233
235
  I.className = "ssk-head", I.append(j, F);
234
236
  let L = document.createElement("button");
235
- L.className = "ssk-history-back", L.innerHTML = u, L.setAttribute("aria-label", "Back to chat");
237
+ L.className = "ssk-history-back", L.innerHTML = l, L.setAttribute("aria-label", "Back to chat");
236
238
  let R = document.createElement("h4");
237
239
  R.textContent = "Chat history";
238
240
  let z = document.createElement("div");
@@ -249,8 +251,8 @@ async function C(t) {
249
251
  }
250
252
  for (let e of C) {
251
253
  let t = document.createElement("button");
252
- t.className = `ssk-history-item${e.id === T ? " active" : ""}`, t.innerHTML = `<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${te(e.updatedAt)}</span>`, t.onclick = () => {
253
- E = e, T = e.id, _(n, T), O(), H(), V.classList.remove("shown");
254
+ t.className = `ssk-history-item${e.id === T ? " active" : ""}`, t.innerHTML = `<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${y(e.updatedAt)}</span>`, t.onclick = () => {
255
+ E = e, T = e.id, g(r, T), O(), H(), V.classList.remove("shown");
254
256
  }, B.appendChild(t);
255
257
  }
256
258
  }
@@ -258,11 +260,11 @@ async function C(t) {
258
260
  H(), V.classList.add("shown");
259
261
  }, L.onclick = () => V.classList.remove("shown"), N.onclick = () => {
260
262
  E.messages.length && (E = {
261
- id: v(),
263
+ id: _(),
262
264
  title: "New conversation",
263
265
  messages: [],
264
266
  updatedAt: Date.now()
265
- }, C.unshift(E), T = E.id, _(n, T), h(n, C), O(), V.classList.remove("shown"));
267
+ }, C.unshift(E), T = E.id, g(r, T), m(r, C), O(), V.classList.remove("shown"));
266
268
  };
267
269
  let U = document.createElement("div");
268
270
  U.className = "ssk-typing", U.hidden = !0, U.innerHTML = "<span class=\"ssk-dots\"><i></i><i></i><i></i></span>";
@@ -273,22 +275,22 @@ async function C(t) {
273
275
  let K = document.createElement("div");
274
276
  K.className = "ssk-inputrow", K.append(W, G);
275
277
  let q = document.createElement("div");
276
- q.className = "ssk-foot", q.textContent = p.whiteLabel ? "AI can make mistakes — confirm important details." : "AI can make mistakes — confirm important details. · Powered by Sendystack";
278
+ q.className = "ssk-foot", q.textContent = d.whiteLabel ? "AI can make mistakes — confirm important details." : "AI can make mistakes — confirm important details. · Powered by Sendystack";
277
279
  let J = document.createElement("div");
278
280
  J.className = "ssk-panel", J.append(I, D, U, K, q, V);
279
281
  let Y = document.createElement("button");
280
- Y.className = "ssk-launcher", Y.setAttribute("aria-label", "Open chat"), Y.innerHTML = `<span class="ssk-open">${i}</span><span class="ssk-x" style="display:none">${a}</span><span class="ssk-pulse" aria-hidden="true"></span>`;
282
+ Y.className = "ssk-launcher", Y.setAttribute("aria-label", "Open chat"), Y.innerHTML = `<span class="ssk-open"><img src="${e}" alt="" /></span><span class="ssk-x" style="display:none">${a}</span><span class="ssk-pulse" aria-hidden="true"></span>`;
281
283
  let X = document.createElement("div");
282
- X.className = "ssk-greeting", X.textContent = p.welcomeMessage, b.append(J, X, Y), document.body.appendChild(b), Y.onclick = () => b.classList.toggle("open"), P.onclick = () => b.classList.remove("open"), w && setTimeout(() => {
284
+ X.className = "ssk-greeting", X.textContent = d.welcomeMessage, b.append(J, X, Y), document.body.appendChild(b), Y.onclick = () => b.classList.toggle("open"), P.onclick = () => b.classList.remove("open"), w && setTimeout(() => {
283
285
  b.classList.contains("open") || X.classList.add("shown"), setTimeout(() => X.classList.remove("shown"), 6e3);
284
286
  }, 2500);
285
287
  function Z(e) {
286
288
  U.hidden = !e, e && (D.scrollTop = D.scrollHeight);
287
289
  }
288
290
  function Q() {
289
- E.updatedAt = Date.now(), E.title = y(E.messages);
291
+ E.updatedAt = Date.now(), E.title = v(E.messages);
290
292
  let e = C.findIndex((e) => e.id === E.id);
291
- e >= 0 && C.splice(e, 1), C.unshift(E), h(n, C);
293
+ e >= 0 && C.splice(e, 1), C.unshift(E), m(r, C);
292
294
  }
293
295
  async function $() {
294
296
  let e = W.value.trim();
@@ -298,33 +300,33 @@ async function C(t) {
298
300
  role: "user",
299
301
  text: e
300
302
  };
301
- E.messages.push(t), m(D, t), Q(), G.disabled = !0, Z(!0);
303
+ E.messages.push(t), f(D, t), Q(), G.disabled = !0, Z(!0);
302
304
  try {
303
- let t = await (await fetch(`${r}/answer`, {
305
+ let t = await (await fetch(`${i}/answer`, {
304
306
  method: "POST",
305
307
  headers: { "Content-Type": "application/json" },
306
308
  body: JSON.stringify({
307
- embedToken: n,
309
+ embedToken: r,
308
310
  query: e
309
311
  })
310
- })).json(), i = {
312
+ })).json(), n = {
311
313
  role: "bot",
312
314
  text: t.ok ? t.reply : t.message || "Sorry, I couldn't process that right now."
313
315
  };
314
- E.messages.push(i), m(D, i);
316
+ E.messages.push(n), f(D, n);
315
317
  } catch {
316
318
  let e = {
317
319
  role: "bot",
318
320
  text: "I couldn't reach the server. Please try again."
319
321
  };
320
- E.messages.push(e), m(D, e);
322
+ E.messages.push(e), f(D, e);
321
323
  } finally {
322
324
  Z(!1), G.disabled = !1, Q();
323
325
  }
324
326
  }
325
327
  G.onclick = $, W.addEventListener("keydown", (e) => {
326
328
  e.key === "Enter" && !e.shiftKey && (e.preventDefault(), $());
327
- }), x(r, n), S(r, n);
329
+ }), x(i, r), S(i, r);
328
330
  }
329
331
  function w() {
330
332
  let e = document.currentScript || Array.from(document.getElementsByTagName("script")).find((e) => /widget(\.global)?\.js/.test(e.src)), t = e?.dataset.token;
@@ -1,32 +1,34 @@
1
- var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`https://us-central1-sendystack-fab32.cloudfunctions.net`,n=`sendystack-widget-root`,r=40,i=e=>`sendystack_sessions_${e}`,a=e=>`sendystack_current_${e}`,o=e=>`sendystack_crawled_${e}`,ee=`<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h13a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H9l-5 4v-4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2z"/></svg>`,s=`<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>`,c=`<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l16-7-7 16-2-7-7-2z"/></svg>`,l=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l2.5 5 5 .7-3.7 3.5.9 5L12 19.8 7.3 17.2l.9-5L4.5 8.7l5-.7z"/></svg>`,u=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/></svg>`,d=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>`,f=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>`;async function p(e,t){let n=await(await fetch(`${e}/embedConfig?embedToken=${encodeURIComponent(t)}`)).json();if(!n.ok)throw Error(n.error||`failed to load widget config`);return{...n.appearance,whiteLabel:!!n.whiteLabel}}function m(e){let t=e.position===`left`?`left`:`right`,r=document.createElement(`style`);r.textContent=`
2
- #${n} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
3
- #${n}{position:fixed;${t}:20px;bottom:20px;z-index:2147483000;}
4
- .ssk-launcher{position:relative;width:60px;height:60px;border-radius:50%;border:none;cursor:pointer;color:#fff;
5
- background:linear-gradient(140deg,${e.accent2},${e.accent});box-shadow:0 16px 36px rgba(0,0,0,.28);
1
+ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`https://us-central1-sendystack-fab32.cloudfunctions.net`,n=`https://app.sendystack.org/sendy_stack_favico.png`,r=`sendystack-widget-root`,i=40,a=e=>`sendystack_sessions_${e}`,o=e=>`sendystack_current_${e}`,s=e=>`sendystack_crawled_${e}`,c=`<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>`,l=`<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l16-7-7 16-2-7-7-2z"/></svg>`,u=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/></svg>`,d=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>`,f=`<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>`;async function p(e,t){let n=await(await fetch(`${e}/embedConfig?embedToken=${encodeURIComponent(t)}`)).json();if(!n.ok)throw Error(n.error||`failed to load widget config`);return{...n.appearance,whiteLabel:!!n.whiteLabel}}function m(e){let t=e.position===`left`?`left`:`right`,n=document.createElement(`style`);n.textContent=`
2
+ #${r} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
3
+ #${r}{position:fixed;${t}:20px;bottom:20px;z-index:2147483000;}
4
+ .ssk-launcher{position:relative;width:60px;height:60px;border-radius:50%;border:none;cursor:pointer;color:${e.accent};
5
+ background:#fff;box-shadow:0 16px 36px rgba(0,0,0,.22),0 0 0 1px rgba(27,39,51,.05);
6
6
  display:flex;align-items:center;justify-content:center;transition:transform .25s cubic-bezier(.16,1,.3,1);margin-left:auto;}
7
7
  .ssk-launcher:hover{transform:translateY(-3px) scale(1.04);}
8
8
  .ssk-launcher:active{transform:scale(.96);}
9
9
  .ssk-launcher .ssk-open{display:flex;} .ssk-launcher .ssk-x{display:none;}
10
- #${n}.open .ssk-launcher .ssk-open{display:none;} #${n}.open .ssk-launcher .ssk-x{display:flex;}
10
+ .ssk-launcher .ssk-open img{width:32px;height:32px;object-fit:contain;}
11
+ #${r}.open .ssk-launcher .ssk-open{display:none;} #${r}.open .ssk-launcher .ssk-x{display:flex;}
11
12
  .ssk-pulse{position:absolute;inset:0;border-radius:50%;background:${e.accent};opacity:.5;z-index:-1;animation:sskPulse 2.4s ease-out infinite;}
12
13
  @keyframes sskPulse{0%{transform:scale(1);opacity:.5;}70%,100%{transform:scale(1.7);opacity:0;}}
13
14
  @media(prefers-reduced-motion:reduce){.ssk-pulse{animation:none;}}
14
- .ssk-greeting{position:absolute;bottom:72px;${t}:0;max-width:220px;padding:11px 14px;border-radius:14px;background:#fff;
15
+ .ssk-greeting{position:absolute;bottom:72px;${t}:0;width:max-content;max-width:min(300px,calc(100vw - 48px));padding:11px 16px;border-radius:14px;background:#fff;
15
16
  color:#1b2733;font-size:13.5px;line-height:1.4;font-weight:600;box-shadow:0 16px 38px rgba(27,39,51,.18);
16
17
  transform:translateY(8px);opacity:0;pointer-events:none;transition:opacity .3s,transform .3s;}
17
18
  .ssk-greeting.shown{opacity:1;transform:translateY(0);}
18
- #${n}.open .ssk-greeting{display:none;}
19
+ #${r}.open .ssk-greeting{display:none;}
19
20
  .ssk-panel{position:absolute;bottom:74px;${t}:0;width:368px;max-width:calc(100vw - 32px);height:560px;
20
21
  max-height:calc(100vh - 110px);background:#fff;border-radius:20px;overflow:hidden;display:flex;flex-direction:column;
21
22
  box-shadow:0 36px 80px rgba(20,20,30,.3);border:1px solid rgba(27,39,51,.08);
22
23
  transform:translateY(14px) scale(.96);opacity:0;pointer-events:none;transform-origin:bottom ${t};
23
24
  transition:transform .25s cubic-bezier(.16,1,.3,1),opacity .2s;}
24
- #${n}.open .ssk-panel{transform:translateY(0) scale(1);opacity:1;pointer-events:auto;}
25
+ #${r}.open .ssk-panel{transform:translateY(0) scale(1);opacity:1;pointer-events:auto;}
25
26
  .ssk-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px 14px;flex-shrink:0;
26
27
  background:linear-gradient(135deg,${e.accent2},${e.accent});color:#fff;}
27
28
  .ssk-id{display:flex;align-items:center;gap:9px;min-width:0;}
28
29
  .ssk-avatar{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;
29
- background:rgba(255,255,255,.22);flex:0 0 auto;}
30
+ background:#fff;flex:0 0 auto;}
31
+ .ssk-avatar img{width:22px;height:22px;object-fit:contain;}
30
32
  .ssk-id-text{min-width:0;}
31
33
  .ssk-id-text strong{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
32
34
  .ssk-status{display:flex;align-items:center;gap:5px;font-size:11px;opacity:.9;}
@@ -81,4 +83,4 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
81
83
  .ssk-sendbtn:disabled{opacity:.6;cursor:default;transform:none;}
82
84
  .ssk-foot{text-align:center;font-size:10.5px;color:#9aa3ad;padding:0 8px 9px;background:#fff;flex-shrink:0;}
83
85
  .ssk-foot a{color:inherit;text-decoration:underline;}
84
- `,document.head.appendChild(r)}function h(e){return`<p>${e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,`<a href="$2" target="_blank" rel="noopener">$1</a>`).replace(/\*\*([^*]+)\*\*/g,`<strong>$1</strong>`).replace(/\n{2,}/g,`</p><p>`).replace(/\n/g,`<br>`)}</p>`}function g(e,t){let n=document.createElement(`div`);n.className=`ssk-msg ${t.role}`;let r=document.createElement(`div`);r.className=`ssk-bubble-text`,r.innerHTML=h(t.text),n.appendChild(r),e.appendChild(n),e.scrollTop=e.scrollHeight}function te(e){try{return JSON.parse(localStorage.getItem(i(e))||`[]`)}catch{return[]}}function _(e,t){try{localStorage.setItem(i(e),JSON.stringify(t.slice(0,r)))}catch{}}function v(e){try{return localStorage.getItem(a(e))}catch{return null}}function y(e,t){try{localStorage.setItem(a(e),t)}catch{}}function b(){return`s_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}function x(e){let t=e.find(e=>e.role===`user`);return t?t.text.slice(0,60):`New conversation`}function S(e){let t=new Date(e),n=new Date;return t.toDateString()===n.toDateString()?t.toLocaleTimeString(void 0,{hour:`numeric`,minute:`2-digit`}):t.toLocaleDateString(void 0,{month:`short`,day:`numeric`})}function C(){let e=document.body.cloneNode(!0);e.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${n}`).forEach(e=>e.remove());let t=(e.textContent||``).replace(/\s+/g,` `).trim();return{title:document.title,text:t}}async function ne(e,t){let{title:n,text:r}=C();r.length<40||await fetch(`${e}/ingest`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:t,items:[{url:location.href,title:n,text:r}]})}).catch(()=>{})}async function w(e,t){let n=!1;try{n=!!sessionStorage.getItem(o(t)),sessionStorage.setItem(o(t),`1`)}catch{}n||await fetch(`${e}/crawlSite`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:t})}).catch(()=>{})}async function T(e){if(typeof document>`u`||document.getElementById(n))return;let r=(e.token||``).trim();if(!r){console.error(`[Sendystack] init() called without a token`);return}let i=e.apiBase||t,a;try{a=await p(i,r)}catch(e){console.error(`[Sendystack] failed to load widget config:`,e);return}m(a);let o=document.createElement(`div`);o.id=n;let h=te(r),C=h.length===0,T=v(r),E=h.find(e=>e.id===T);E||(E={id:b(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),T=E.id,y(r,T),_(r,h));let D=document.createElement(`div`);D.className=`ssk-body`;function O(){D.innerHTML=``;let e=E.messages.length?E.messages:[{role:`bot`,text:a.welcomeMessage}];for(let t of e)g(D,t)}O();let k=document.createElement(`span`);k.className=`ssk-avatar`,k.innerHTML=l;let A=document.createElement(`div`);A.className=`ssk-id-text`,A.innerHTML=`<strong>${a.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;let j=document.createElement(`div`);j.className=`ssk-id`,j.append(k,A);let M=document.createElement(`button`);M.className=`ssk-iconbtn`,M.innerHTML=u,M.setAttribute(`aria-label`,`Chat history`);let N=document.createElement(`button`);N.className=`ssk-iconbtn`,N.innerHTML=d,N.setAttribute(`aria-label`,`New chat`);let P=document.createElement(`button`);P.className=`ssk-closebtn`,P.innerHTML=s,P.setAttribute(`aria-label`,`Close chat`);let F=document.createElement(`div`);F.className=`ssk-headbtns`,F.append(M,N,P);let I=document.createElement(`div`);I.className=`ssk-head`,I.append(j,F);let L=document.createElement(`button`);L.className=`ssk-history-back`,L.innerHTML=f,L.setAttribute(`aria-label`,`Back to chat`);let R=document.createElement(`h4`);R.textContent=`Chat history`;let z=document.createElement(`div`);z.className=`ssk-history-head`,z.append(L,R);let B=document.createElement(`div`);B.className=`ssk-history-list`;let V=document.createElement(`div`);V.className=`ssk-history`,V.append(z,B);function H(){if(B.innerHTML=``,!h.length){let e=document.createElement(`div`);e.className=`ssk-history-empty`,e.textContent=`No past conversations yet.`,B.appendChild(e);return}for(let e of h){let t=document.createElement(`button`);t.className=`ssk-history-item${e.id===T?` active`:``}`,t.innerHTML=`<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${S(e.updatedAt)}</span>`,t.onclick=()=>{E=e,T=e.id,y(r,T),O(),H(),V.classList.remove(`shown`)},B.appendChild(t)}}M.onclick=()=>{H(),V.classList.add(`shown`)},L.onclick=()=>V.classList.remove(`shown`),N.onclick=()=>{E.messages.length&&(E={id:b(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),T=E.id,y(r,T),_(r,h),O(),V.classList.remove(`shown`))};let U=document.createElement(`div`);U.className=`ssk-typing`,U.hidden=!0,U.innerHTML=`<span class="ssk-dots"><i></i><i></i><i></i></span>`;let W=document.createElement(`textarea`);W.rows=1,W.maxLength=4e3,W.placeholder=`Ask me anything…`;let G=document.createElement(`button`);G.className=`ssk-sendbtn`,G.innerHTML=c,G.setAttribute(`aria-label`,`Send`);let K=document.createElement(`div`);K.className=`ssk-inputrow`,K.append(W,G);let q=document.createElement(`div`);q.className=`ssk-foot`,q.textContent=a.whiteLabel?`AI can make mistakes — confirm important details.`:`AI can make mistakes — confirm important details. · Powered by Sendystack`;let J=document.createElement(`div`);J.className=`ssk-panel`,J.append(I,D,U,K,q,V);let Y=document.createElement(`button`);Y.className=`ssk-launcher`,Y.setAttribute(`aria-label`,`Open chat`),Y.innerHTML=`<span class="ssk-open">${ee}</span><span class="ssk-x" style="display:none">${s}</span><span class="ssk-pulse" aria-hidden="true"></span>`;let X=document.createElement(`div`);X.className=`ssk-greeting`,X.textContent=a.welcomeMessage,o.append(J,X,Y),document.body.appendChild(o),Y.onclick=()=>o.classList.toggle(`open`),P.onclick=()=>o.classList.remove(`open`),C&&setTimeout(()=>{o.classList.contains(`open`)||X.classList.add(`shown`),setTimeout(()=>X.classList.remove(`shown`),6e3)},2500);function Z(e){U.hidden=!e,e&&(D.scrollTop=D.scrollHeight)}function Q(){E.updatedAt=Date.now(),E.title=x(E.messages);let e=h.findIndex(e=>e.id===E.id);e>=0&&h.splice(e,1),h.unshift(E),_(r,h)}async function $(){let e=W.value.trim();if(!e)return;W.value=``;let t={role:`user`,text:e};E.messages.push(t),g(D,t),Q(),G.disabled=!0,Z(!0);try{let t=await(await fetch(`${i}/answer`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:r,query:e})})).json(),n={role:`bot`,text:t.ok?t.reply:t.message||`Sorry, I couldn't process that right now.`};E.messages.push(n),g(D,n)}catch{let e={role:`bot`,text:`I couldn't reach the server. Please try again.`};E.messages.push(e),g(D,e)}finally{Z(!1),G.disabled=!1,Q()}}G.onclick=$,W.addEventListener(`keydown`,e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),$())}),ne(i,r),w(i,r)}function E(){let e=document.currentScript||Array.from(document.getElementsByTagName(`script`)).find(e=>/widget(\.global)?\.js/.test(e.src)),t=e?.dataset.token;t&&T({token:t,apiBase:e?.dataset.apiBase})}return typeof document<`u`&&E(),e.init=T,e})({});
86
+ `,document.head.appendChild(n)}function h(e){return`<p>${e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,`<a href="$2" target="_blank" rel="noopener">$1</a>`).replace(/\*\*([^*]+)\*\*/g,`<strong>$1</strong>`).replace(/\n{2,}/g,`</p><p>`).replace(/\n/g,`<br>`)}</p>`}function g(e,t){let n=document.createElement(`div`);n.className=`ssk-msg ${t.role}`;let r=document.createElement(`div`);r.className=`ssk-bubble-text`,r.innerHTML=h(t.text),n.appendChild(r),e.appendChild(n),e.scrollTop=e.scrollHeight}function _(e){try{return JSON.parse(localStorage.getItem(a(e))||`[]`)}catch{return[]}}function v(e,t){try{localStorage.setItem(a(e),JSON.stringify(t.slice(0,i)))}catch{}}function y(e){try{return localStorage.getItem(o(e))}catch{return null}}function b(e,t){try{localStorage.setItem(o(e),t)}catch{}}function x(){return`s_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}function S(e){let t=e.find(e=>e.role===`user`);return t?t.text.slice(0,60):`New conversation`}function ee(e){let t=new Date(e),n=new Date;return t.toDateString()===n.toDateString()?t.toLocaleTimeString(void 0,{hour:`numeric`,minute:`2-digit`}):t.toLocaleDateString(void 0,{month:`short`,day:`numeric`})}function C(){let e=document.body.cloneNode(!0);e.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${r}`).forEach(e=>e.remove());let t=(e.textContent||``).replace(/\s+/g,` `).trim();return{title:document.title,text:t}}async function te(e,t){let{title:n,text:r}=C();r.length<40||await fetch(`${e}/ingest`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:t,items:[{url:location.href,title:n,text:r}]})}).catch(()=>{})}async function w(e,t){let n=!1;try{n=!!sessionStorage.getItem(s(t)),sessionStorage.setItem(s(t),`1`)}catch{}n||await fetch(`${e}/crawlSite`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:t})}).catch(()=>{})}async function T(e){if(typeof document>`u`||document.getElementById(r))return;let i=(e.token||``).trim();if(!i){console.error(`[Sendystack] init() called without a token`);return}let a=e.apiBase||t,o;try{o=await p(a,i)}catch(e){console.error(`[Sendystack] failed to load widget config:`,e);return}m(o);let s=document.createElement(`div`);s.id=r;let h=_(i),C=h.length===0,T=y(i),E=h.find(e=>e.id===T);E||(E={id:x(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),T=E.id,b(i,T),v(i,h));let D=document.createElement(`div`);D.className=`ssk-body`;function O(){D.innerHTML=``;let e=E.messages.length?E.messages:[{role:`bot`,text:o.welcomeMessage}];for(let t of e)g(D,t)}O();let k=document.createElement(`span`);k.className=`ssk-avatar`,k.innerHTML=`<img src="${n}" alt="" />`;let A=document.createElement(`div`);A.className=`ssk-id-text`,A.innerHTML=`<strong>${o.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;let j=document.createElement(`div`);j.className=`ssk-id`,j.append(k,A);let M=document.createElement(`button`);M.className=`ssk-iconbtn`,M.innerHTML=u,M.setAttribute(`aria-label`,`Chat history`);let N=document.createElement(`button`);N.className=`ssk-iconbtn`,N.innerHTML=d,N.setAttribute(`aria-label`,`New chat`);let P=document.createElement(`button`);P.className=`ssk-closebtn`,P.innerHTML=c,P.setAttribute(`aria-label`,`Close chat`);let F=document.createElement(`div`);F.className=`ssk-headbtns`,F.append(M,N,P);let I=document.createElement(`div`);I.className=`ssk-head`,I.append(j,F);let L=document.createElement(`button`);L.className=`ssk-history-back`,L.innerHTML=f,L.setAttribute(`aria-label`,`Back to chat`);let R=document.createElement(`h4`);R.textContent=`Chat history`;let z=document.createElement(`div`);z.className=`ssk-history-head`,z.append(L,R);let B=document.createElement(`div`);B.className=`ssk-history-list`;let V=document.createElement(`div`);V.className=`ssk-history`,V.append(z,B);function H(){if(B.innerHTML=``,!h.length){let e=document.createElement(`div`);e.className=`ssk-history-empty`,e.textContent=`No past conversations yet.`,B.appendChild(e);return}for(let e of h){let t=document.createElement(`button`);t.className=`ssk-history-item${e.id===T?` active`:``}`,t.innerHTML=`<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${ee(e.updatedAt)}</span>`,t.onclick=()=>{E=e,T=e.id,b(i,T),O(),H(),V.classList.remove(`shown`)},B.appendChild(t)}}M.onclick=()=>{H(),V.classList.add(`shown`)},L.onclick=()=>V.classList.remove(`shown`),N.onclick=()=>{E.messages.length&&(E={id:x(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),T=E.id,b(i,T),v(i,h),O(),V.classList.remove(`shown`))};let U=document.createElement(`div`);U.className=`ssk-typing`,U.hidden=!0,U.innerHTML=`<span class="ssk-dots"><i></i><i></i><i></i></span>`;let W=document.createElement(`textarea`);W.rows=1,W.maxLength=4e3,W.placeholder=`Ask me anything…`;let G=document.createElement(`button`);G.className=`ssk-sendbtn`,G.innerHTML=l,G.setAttribute(`aria-label`,`Send`);let K=document.createElement(`div`);K.className=`ssk-inputrow`,K.append(W,G);let q=document.createElement(`div`);q.className=`ssk-foot`,q.textContent=o.whiteLabel?`AI can make mistakes — confirm important details.`:`AI can make mistakes — confirm important details. · Powered by Sendystack`;let J=document.createElement(`div`);J.className=`ssk-panel`,J.append(I,D,U,K,q,V);let Y=document.createElement(`button`);Y.className=`ssk-launcher`,Y.setAttribute(`aria-label`,`Open chat`),Y.innerHTML=`<span class="ssk-open"><img src="${n}" alt="" /></span><span class="ssk-x" style="display:none">${c}</span><span class="ssk-pulse" aria-hidden="true"></span>`;let X=document.createElement(`div`);X.className=`ssk-greeting`,X.textContent=o.welcomeMessage,s.append(J,X,Y),document.body.appendChild(s),Y.onclick=()=>s.classList.toggle(`open`),P.onclick=()=>s.classList.remove(`open`),C&&setTimeout(()=>{s.classList.contains(`open`)||X.classList.add(`shown`),setTimeout(()=>X.classList.remove(`shown`),6e3)},2500);function Z(e){U.hidden=!e,e&&(D.scrollTop=D.scrollHeight)}function Q(){E.updatedAt=Date.now(),E.title=S(E.messages);let e=h.findIndex(e=>e.id===E.id);e>=0&&h.splice(e,1),h.unshift(E),v(i,h)}async function $(){let e=W.value.trim();if(!e)return;W.value=``;let t={role:`user`,text:e};E.messages.push(t),g(D,t),Q(),G.disabled=!0,Z(!0);try{let t=await(await fetch(`${a}/answer`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({embedToken:i,query:e})})).json(),n={role:`bot`,text:t.ok?t.reply:t.message||`Sorry, I couldn't process that right now.`};E.messages.push(n),g(D,n)}catch{let e={role:`bot`,text:`I couldn't reach the server. Please try again.`};E.messages.push(e),g(D,e)}finally{Z(!1),G.disabled=!1,Q()}}G.onclick=$,W.addEventListener(`keydown`,e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),$())}),te(a,i),w(a,i)}function E(){let e=document.currentScript||Array.from(document.getElementsByTagName(`script`)).find(e=>/widget(\.global)?\.js/.test(e.src)),t=e?.dataset.token;t&&T({token:t,apiBase:e?.dataset.apiBase})}return typeof document<`u`&&E(),e.init=T,e})({});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendystack/widget",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Universal AI chat widget for any website -- auto-injects the chatbot, crawls your site into the knowledge base, and feeds Claude/ChatGPT/Gemini via MCP. Appearance is managed at app.sendystack.org.",
5
5
  "type": "module",
6
6
  "license": "MIT",