@sendystack/widget 0.1.3 → 0.1.4

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
@@ -8,7 +8,7 @@ async function u(e, t) {
8
8
  whiteLabel: !!n.whiteLabel
9
9
  };
10
10
  }
11
- function ee(e) {
11
+ function d(e) {
12
12
  let n = e.position === "left" ? "left" : "right", r = document.createElement("style");
13
13
  r.textContent = `
14
14
  #${t} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
@@ -67,8 +67,8 @@ function ee(e) {
67
67
  .ssk-history-item.active{background:rgba(226,112,15,.1);}
68
68
  .ssk-history-title{display:block;font-size:13.5px;font-weight:600;color:#1b2733;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
69
69
  .ssk-history-time{display:block;font-size:11px;color:#9aa3ad;margin-top:2px;}
70
- .ssk-msg{display:flex;max-width:86%;}
71
- .ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;}
70
+ .ssk-msg{display:flex;flex-direction:column;align-items:flex-start;max-width:86%;}
71
+ .ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;align-items:flex-end;}
72
72
  .ssk-bubble-text{padding:10px 13px;border-radius:15px;font-size:14px;line-height:1.5;word-wrap:break-word;}
73
73
  .ssk-msg.bot .ssk-bubble-text{background:#fff;border:1px solid rgba(27,39,51,.08);border-bottom-left-radius:5px;
74
74
  box-shadow:0 5px 14px rgba(27,39,51,.05);color:#1b2733;}
@@ -76,6 +76,18 @@ function ee(e) {
76
76
  .ssk-bubble-text a{color:${e.accent};font-weight:700;text-decoration:underline;}
77
77
  .ssk-msg.user .ssk-bubble-text a{color:#fff;}
78
78
  .ssk-bubble-text p{margin:0 0 7px;} .ssk-bubble-text p:last-child{margin:0;}
79
+ .ssk-sources{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px;}
80
+ .ssk-source-pill{display:inline-flex;align-items:center;padding:5px 11px;border-radius:999px;
81
+ background:rgba(27,39,51,.05);color:${e.accent};font-size:12px;font-weight:600;
82
+ text-decoration:none;border:1px solid rgba(27,39,51,.08);}
83
+ .ssk-source-pill:hover{background:rgba(27,39,51,.08);}
84
+ .ssk-action-btn{margin-top:7px;border:none;border-radius:12px;padding:9px 15px;font-size:13px;font-weight:700;
85
+ cursor:pointer;color:#fff;background:linear-gradient(135deg,${e.accent2},${e.accent});
86
+ box-shadow:0 6px 16px rgba(0,0,0,.15);transition:transform .15s;}
87
+ .ssk-action-btn:hover{transform:translateY(-1px);}
88
+ .ssk-action-btn:disabled{cursor:default;transform:none;}
89
+ .ssk-action-btn.ssk-action-done{opacity:.75;}
90
+ .ssk-action-btn.ssk-action-missing{background:rgba(27,39,51,.08);color:#6b7280;box-shadow:none;}
79
91
  .ssk-typing{padding:0 14px 10px;flex-shrink:0;}
80
92
  .ssk-typing[hidden]{display:none;}
81
93
  .ssk-dots{display:inline-flex;gap:5px;padding:10px 13px;background:#fff;border:1px solid rgba(27,39,51,.08);
@@ -97,47 +109,78 @@ function ee(e) {
97
109
  .ssk-foot a{color:inherit;text-decoration:underline;}
98
110
  `, document.head.appendChild(r);
99
111
  }
100
- function d(e) {
112
+ function f(e) {
101
113
  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>`;
102
114
  }
103
- function f(e, t) {
115
+ function p(e) {
116
+ let t = document.createElement("button");
117
+ t.type = "button", t.className = "ssk-action-btn", t.textContent = e.label;
118
+ let n = !e.confirmRequired;
119
+ return t.onclick = () => {
120
+ let r = e.selector ? document.querySelector(e.selector) : null;
121
+ if (!r) {
122
+ if (e.fallbackUrl) {
123
+ location.href = e.fallbackUrl;
124
+ return;
125
+ }
126
+ t.textContent = "Couldn't find that on this page", t.classList.add("ssk-action-missing"), t.disabled = !0;
127
+ return;
128
+ }
129
+ if (!n) {
130
+ n = !0, t.textContent = `Click to confirm: ${e.label}`;
131
+ return;
132
+ }
133
+ r.click(), t.textContent = "Done ✓", t.disabled = !0, t.classList.add("ssk-action-done");
134
+ }, t;
135
+ }
136
+ function m(e, t) {
104
137
  let n = document.createElement("div");
105
138
  n.className = `ssk-msg ${t.role}`;
106
139
  let r = document.createElement("div");
107
- r.className = "ssk-bubble-text", r.innerHTML = d(t.text), n.appendChild(r), e.appendChild(n), e.scrollTop = e.scrollHeight;
140
+ if (r.className = "ssk-bubble-text", r.innerHTML = f(t.text), n.appendChild(r), t.sources?.length) {
141
+ let e = document.createElement("div");
142
+ e.className = "ssk-sources";
143
+ for (let n of t.sources) {
144
+ let t = document.createElement("a");
145
+ t.className = "ssk-source-pill", t.href = n.url, t.textContent = n.title || n.url, e.appendChild(t);
146
+ }
147
+ n.appendChild(e);
148
+ }
149
+ for (let e of t.actions || []) n.appendChild(p(e));
150
+ e.appendChild(n), e.scrollTop = e.scrollHeight;
108
151
  }
109
- function p(e) {
152
+ function h(e) {
110
153
  try {
111
154
  return JSON.parse(localStorage.getItem(n(e)) || "[]");
112
155
  } catch {
113
156
  return [];
114
157
  }
115
158
  }
116
- function m(e, t) {
159
+ function g(e, t) {
117
160
  try {
118
161
  localStorage.setItem(n(e), JSON.stringify(t.slice(0, 40)));
119
162
  } catch {}
120
163
  }
121
- function h(e) {
164
+ function _(e) {
122
165
  try {
123
166
  return localStorage.getItem(r(e));
124
167
  } catch {
125
168
  return null;
126
169
  }
127
170
  }
128
- function g(e, t) {
171
+ function v(e, t) {
129
172
  try {
130
173
  localStorage.setItem(r(e), t);
131
174
  } catch {}
132
175
  }
133
- function _() {
176
+ function y() {
134
177
  return `s_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
135
178
  }
136
- function v(e) {
179
+ function b(e) {
137
180
  let t = e.find((e) => e.role === "user");
138
181
  return t ? t.text.slice(0, 60) : "New conversation";
139
182
  }
140
- function y(e) {
183
+ function x(e) {
141
184
  let t = new Date(e), n = /* @__PURE__ */ new Date();
142
185
  return t.toDateString() === n.toDateString() ? t.toLocaleTimeString(void 0, {
143
186
  hour: "numeric",
@@ -147,7 +190,7 @@ function y(e) {
147
190
  day: "numeric"
148
191
  });
149
192
  }
150
- function b() {
193
+ function S() {
151
194
  let e = document.body.cloneNode(!0);
152
195
  e.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${t}`).forEach((e) => e.remove());
153
196
  let n = (e.textContent || "").replace(/\s+/g, " ").trim();
@@ -156,8 +199,8 @@ function b() {
156
199
  text: n
157
200
  };
158
201
  }
159
- async function x(e, t) {
160
- let { title: n, text: r } = b();
202
+ async function C(e, t) {
203
+ let { title: n, text: r } = S();
161
204
  r.length < 40 || await fetch(`${e}/ingest`, {
162
205
  method: "POST",
163
206
  headers: { "Content-Type": "application/json" },
@@ -171,7 +214,7 @@ async function x(e, t) {
171
214
  })
172
215
  }).catch(() => {});
173
216
  }
174
- async function S(e, t) {
217
+ async function ee(e, t) {
175
218
  let n = !1;
176
219
  try {
177
220
  n = !!sessionStorage.getItem(i(t)), sessionStorage.setItem(i(t), "1");
@@ -182,45 +225,45 @@ async function S(e, t) {
182
225
  body: JSON.stringify({ embedToken: t })
183
226
  }).catch(() => {});
184
227
  }
185
- async function C(n) {
228
+ async function w(n) {
186
229
  if (typeof document > "u" || document.getElementById(t)) return;
187
230
  let r = (n.token || "").trim();
188
231
  if (!r) {
189
232
  console.error("[Sendystack] init() called without a token");
190
233
  return;
191
234
  }
192
- let i = n.apiBase || "https://us-central1-sendystack-fab32.cloudfunctions.net", d;
235
+ let i = n.apiBase || "https://us-central1-sendystack-fab32.cloudfunctions.net", f;
193
236
  try {
194
- d = await u(i, r);
237
+ f = await u(i, r);
195
238
  } catch (e) {
196
239
  console.error("[Sendystack] failed to load widget config:", e);
197
240
  return;
198
241
  }
199
- ee(d);
200
- let b = document.createElement("div");
201
- b.id = t;
202
- let C = p(r), w = C.length === 0, T = h(r), E = C.find((e) => e.id === T);
242
+ d(f);
243
+ let p = document.createElement("div");
244
+ p.id = t;
245
+ let S = h(r), w = S.length === 0, T = _(r), E = S.find((e) => e.id === T);
203
246
  E || (E = {
204
- id: _(),
247
+ id: y(),
205
248
  title: "New conversation",
206
249
  messages: [],
207
250
  updatedAt: Date.now()
208
- }, C.unshift(E), T = E.id, g(r, T), m(r, C));
251
+ }, S.unshift(E), T = E.id, v(r, T), g(r, S));
209
252
  let D = document.createElement("div");
210
253
  D.className = "ssk-body";
211
254
  function O() {
212
255
  D.innerHTML = "";
213
256
  let e = E.messages.length ? E.messages : [{
214
257
  role: "bot",
215
- text: d.welcomeMessage
258
+ text: f.welcomeMessage
216
259
  }];
217
- for (let t of e) f(D, t);
260
+ for (let t of e) m(D, t);
218
261
  }
219
262
  O();
220
263
  let k = document.createElement("span");
221
264
  k.className = "ssk-avatar", k.innerHTML = `<img src="${e}" alt="" />`;
222
265
  let A = document.createElement("div");
223
- A.className = "ssk-id-text", A.innerHTML = `<strong>${d.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;
266
+ A.className = "ssk-id-text", A.innerHTML = `<strong>${f.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;
224
267
  let j = document.createElement("div");
225
268
  j.className = "ssk-id", j.append(k, A);
226
269
  let M = document.createElement("button");
@@ -244,15 +287,15 @@ async function C(n) {
244
287
  let V = document.createElement("div");
245
288
  V.className = "ssk-history", V.append(z, B);
246
289
  function H() {
247
- if (B.innerHTML = "", !C.length) {
290
+ if (B.innerHTML = "", !S.length) {
248
291
  let e = document.createElement("div");
249
292
  e.className = "ssk-history-empty", e.textContent = "No past conversations yet.", B.appendChild(e);
250
293
  return;
251
294
  }
252
- for (let e of C) {
295
+ for (let e of S) {
253
296
  let t = document.createElement("button");
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");
297
+ t.className = `ssk-history-item${e.id === T ? " active" : ""}`, t.innerHTML = `<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${x(e.updatedAt)}</span>`, t.onclick = () => {
298
+ E = e, T = e.id, v(r, T), O(), H(), V.classList.remove("shown");
256
299
  }, B.appendChild(t);
257
300
  }
258
301
  }
@@ -260,11 +303,11 @@ async function C(n) {
260
303
  H(), V.classList.add("shown");
261
304
  }, L.onclick = () => V.classList.remove("shown"), N.onclick = () => {
262
305
  E.messages.length && (E = {
263
- id: _(),
306
+ id: y(),
264
307
  title: "New conversation",
265
308
  messages: [],
266
309
  updatedAt: Date.now()
267
- }, C.unshift(E), T = E.id, g(r, T), m(r, C), O(), V.classList.remove("shown"));
310
+ }, S.unshift(E), T = E.id, v(r, T), g(r, S), O(), V.classList.remove("shown"));
268
311
  };
269
312
  let U = document.createElement("div");
270
313
  U.className = "ssk-typing", U.hidden = !0, U.innerHTML = "<span class=\"ssk-dots\"><i></i><i></i><i></i></span>";
@@ -275,22 +318,22 @@ async function C(n) {
275
318
  let K = document.createElement("div");
276
319
  K.className = "ssk-inputrow", K.append(W, G);
277
320
  let q = document.createElement("div");
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";
321
+ q.className = "ssk-foot", q.textContent = f.whiteLabel ? "AI can make mistakes — confirm important details." : "AI can make mistakes — confirm important details. · Powered by Sendystack";
279
322
  let J = document.createElement("div");
280
323
  J.className = "ssk-panel", J.append(I, D, U, K, q, V);
281
324
  let Y = document.createElement("button");
282
325
  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>`;
283
326
  let X = document.createElement("div");
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(() => {
285
- b.classList.contains("open") || X.classList.add("shown"), setTimeout(() => X.classList.remove("shown"), 6e3);
327
+ X.className = "ssk-greeting", X.textContent = f.welcomeMessage, p.append(J, X, Y), document.body.appendChild(p), Y.onclick = () => p.classList.toggle("open"), P.onclick = () => p.classList.remove("open"), w && setTimeout(() => {
328
+ p.classList.contains("open") || X.classList.add("shown"), setTimeout(() => X.classList.remove("shown"), 6e3);
286
329
  }, 2500);
287
330
  function Z(e) {
288
331
  U.hidden = !e, e && (D.scrollTop = D.scrollHeight);
289
332
  }
290
333
  function Q() {
291
- E.updatedAt = Date.now(), E.title = v(E.messages);
292
- let e = C.findIndex((e) => e.id === E.id);
293
- e >= 0 && C.splice(e, 1), C.unshift(E), m(r, C);
334
+ E.updatedAt = Date.now(), E.title = b(E.messages);
335
+ let e = S.findIndex((e) => e.id === E.id);
336
+ e >= 0 && S.splice(e, 1), S.unshift(E), g(r, S);
294
337
  }
295
338
  async function $() {
296
339
  let e = W.value.trim();
@@ -300,7 +343,7 @@ async function C(n) {
300
343
  role: "user",
301
344
  text: e
302
345
  };
303
- E.messages.push(t), f(D, t), Q(), G.disabled = !0, Z(!0);
346
+ E.messages.push(t), m(D, t), Q(), G.disabled = !0, Z(!0);
304
347
  try {
305
348
  let t = await (await fetch(`${i}/answer`, {
306
349
  method: "POST",
@@ -311,30 +354,32 @@ async function C(n) {
311
354
  })
312
355
  })).json(), n = {
313
356
  role: "bot",
314
- text: t.ok ? t.reply : t.message || "Sorry, I couldn't process that right now."
357
+ text: t.ok ? t.reply : t.message || "Sorry, I couldn't process that right now.",
358
+ sources: Array.isArray(t.sources) && t.sources.length ? t.sources : void 0,
359
+ actions: Array.isArray(t.actions) && t.actions.length ? t.actions : void 0
315
360
  };
316
- E.messages.push(n), f(D, n);
361
+ E.messages.push(n), m(D, n);
317
362
  } catch {
318
363
  let e = {
319
364
  role: "bot",
320
365
  text: "I couldn't reach the server. Please try again."
321
366
  };
322
- E.messages.push(e), f(D, e);
367
+ E.messages.push(e), m(D, e);
323
368
  } finally {
324
369
  Z(!1), G.disabled = !1, Q();
325
370
  }
326
371
  }
327
372
  G.onclick = $, W.addEventListener("keydown", (e) => {
328
373
  e.key === "Enter" && !e.shiftKey && (e.preventDefault(), $());
329
- }), x(i, r), S(i, r);
374
+ }), C(i, r), ee(i, r);
330
375
  }
331
- function w() {
376
+ function T() {
332
377
  let e = document.currentScript || Array.from(document.getElementsByTagName("script")).find((e) => /widget(\.global)?\.js/.test(e.src)), t = e?.dataset.token;
333
- t && C({
378
+ t && w({
334
379
  token: t,
335
380
  apiBase: e?.dataset.apiBase
336
381
  });
337
382
  }
338
- typeof document < "u" && w();
383
+ typeof document < "u" && T();
339
384
  //#endregion
340
- export { C as init };
385
+ export { w as init };
@@ -55,8 +55,8 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
55
55
  .ssk-history-item.active{background:rgba(226,112,15,.1);}
56
56
  .ssk-history-title{display:block;font-size:13.5px;font-weight:600;color:#1b2733;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
57
57
  .ssk-history-time{display:block;font-size:11px;color:#9aa3ad;margin-top:2px;}
58
- .ssk-msg{display:flex;max-width:86%;}
59
- .ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;}
58
+ .ssk-msg{display:flex;flex-direction:column;align-items:flex-start;max-width:86%;}
59
+ .ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;align-items:flex-end;}
60
60
  .ssk-bubble-text{padding:10px 13px;border-radius:15px;font-size:14px;line-height:1.5;word-wrap:break-word;}
61
61
  .ssk-msg.bot .ssk-bubble-text{background:#fff;border:1px solid rgba(27,39,51,.08);border-bottom-left-radius:5px;
62
62
  box-shadow:0 5px 14px rgba(27,39,51,.05);color:#1b2733;}
@@ -64,6 +64,18 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
64
64
  .ssk-bubble-text a{color:${e.accent};font-weight:700;text-decoration:underline;}
65
65
  .ssk-msg.user .ssk-bubble-text a{color:#fff;}
66
66
  .ssk-bubble-text p{margin:0 0 7px;} .ssk-bubble-text p:last-child{margin:0;}
67
+ .ssk-sources{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px;}
68
+ .ssk-source-pill{display:inline-flex;align-items:center;padding:5px 11px;border-radius:999px;
69
+ background:rgba(27,39,51,.05);color:${e.accent};font-size:12px;font-weight:600;
70
+ text-decoration:none;border:1px solid rgba(27,39,51,.08);}
71
+ .ssk-source-pill:hover{background:rgba(27,39,51,.08);}
72
+ .ssk-action-btn{margin-top:7px;border:none;border-radius:12px;padding:9px 15px;font-size:13px;font-weight:700;
73
+ cursor:pointer;color:#fff;background:linear-gradient(135deg,${e.accent2},${e.accent});
74
+ box-shadow:0 6px 16px rgba(0,0,0,.15);transition:transform .15s;}
75
+ .ssk-action-btn:hover{transform:translateY(-1px);}
76
+ .ssk-action-btn:disabled{cursor:default;transform:none;}
77
+ .ssk-action-btn.ssk-action-done{opacity:.75;}
78
+ .ssk-action-btn.ssk-action-missing{background:rgba(27,39,51,.08);color:#6b7280;box-shadow:none;}
67
79
  .ssk-typing{padding:0 14px 10px;flex-shrink:0;}
68
80
  .ssk-typing[hidden]{display:none;}
69
81
  .ssk-dots{display:inline-flex;gap:5px;padding:10px 13px;background:#fff;border:1px solid rgba(27,39,51,.08);
@@ -83,4 +95,4 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
83
95
  .ssk-sendbtn:disabled{opacity:.6;cursor:default;transform:none;}
84
96
  .ssk-foot{text-align:center;font-size:10.5px;color:#9aa3ad;padding:0 8px 9px;background:#fff;flex-shrink:0;}
85
97
  .ssk-foot a{color:inherit;text-decoration:underline;}
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})({});
98
+ `,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){let t=document.createElement(`button`);t.type=`button`,t.className=`ssk-action-btn`,t.textContent=e.label;let n=!e.confirmRequired;return t.onclick=()=>{let r=e.selector?document.querySelector(e.selector):null;if(!r){if(e.fallbackUrl){location.href=e.fallbackUrl;return}t.textContent=`Couldn't find that on this page`,t.classList.add(`ssk-action-missing`),t.disabled=!0;return}if(!n){n=!0,t.textContent=`Click to confirm: ${e.label}`;return}r.click(),t.textContent=`Done ✓`,t.disabled=!0,t.classList.add(`ssk-action-done`)},t}function _(e,t){let n=document.createElement(`div`);n.className=`ssk-msg ${t.role}`;let r=document.createElement(`div`);if(r.className=`ssk-bubble-text`,r.innerHTML=h(t.text),n.appendChild(r),t.sources?.length){let e=document.createElement(`div`);e.className=`ssk-sources`;for(let n of t.sources){let t=document.createElement(`a`);t.className=`ssk-source-pill`,t.href=n.url,t.textContent=n.title||n.url,e.appendChild(t)}n.appendChild(e)}for(let e of t.actions||[])n.appendChild(g(e));e.appendChild(n),e.scrollTop=e.scrollHeight}function v(e){try{return JSON.parse(localStorage.getItem(a(e))||`[]`)}catch{return[]}}function y(e,t){try{localStorage.setItem(a(e),JSON.stringify(t.slice(0,i)))}catch{}}function b(e){try{return localStorage.getItem(o(e))}catch{return null}}function x(e,t){try{localStorage.setItem(o(e),t)}catch{}}function S(){return`s_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}function ee(e){let t=e.find(e=>e.role===`user`);return t?t.text.slice(0,60):`New conversation`}function te(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 w(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 T(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 E(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=v(i),g=h.length===0,C=b(i),E=h.find(e=>e.id===C);E||(E={id:S(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),C=E.id,x(i,C),y(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)_(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===C?` active`:``}`,t.innerHTML=`<span class="ssk-history-title">${e.title}</span><span class="ssk-history-time">${te(e.updatedAt)}</span>`,t.onclick=()=>{E=e,C=e.id,x(i,C),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:S(),title:`New conversation`,messages:[],updatedAt:Date.now()},h.unshift(E),C=E.id,x(i,C),y(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`),g&&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=ee(E.messages);let e=h.findIndex(e=>e.id===E.id);e>=0&&h.splice(e,1),h.unshift(E),y(i,h)}async function $(){let e=W.value.trim();if(!e)return;W.value=``;let t={role:`user`,text:e};E.messages.push(t),_(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.`,sources:Array.isArray(t.sources)&&t.sources.length?t.sources:void 0,actions:Array.isArray(t.actions)&&t.actions.length?t.actions:void 0};E.messages.push(n),_(D,n)}catch{let e={role:`bot`,text:`I couldn't reach the server. Please try again.`};E.messages.push(e),_(D,e)}finally{Z(!1),G.disabled=!1,Q()}}G.onclick=$,W.addEventListener(`keydown`,e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),$())}),w(a,i),T(a,i)}function D(){let e=document.currentScript||Array.from(document.getElementsByTagName(`script`)).find(e=>/widget(\.global)?\.js/.test(e.src)),t=e?.dataset.token;t&&E({token:t,apiBase:e?.dataset.apiBase})}return typeof document<`u`&&D(),e.init=E,e})({});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendystack/widget",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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",