@sendystack/widget 0.1.1 → 0.1.2
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 +178 -85
- package/dist/widget.global.js +21 -4
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/index.ts
|
|
2
|
-
var e = "sendystack-widget-root", t = (e) => `
|
|
3
|
-
async function
|
|
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) {
|
|
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,7 +8,7 @@ async function c(e, t) {
|
|
|
8
8
|
whiteLabel: !!n.whiteLabel
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function f(t) {
|
|
12
12
|
let n = t.position === "left" ? "left" : "right", r = document.createElement("style");
|
|
13
13
|
r.textContent = `
|
|
14
14
|
#${e} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
|
|
@@ -43,11 +43,28 @@ function l(t) {
|
|
|
43
43
|
.ssk-id-text strong{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
|
44
44
|
.ssk-status{display:flex;align-items:center;gap:5px;font-size:11px;opacity:.9;}
|
|
45
45
|
.ssk-dot{width:6px;height:6px;border-radius:50%;background:#46e08a;box-shadow:0 0 7px #46e08a;}
|
|
46
|
-
.ssk-
|
|
46
|
+
.ssk-headbtns{display:flex;align-items:center;gap:2px;flex:0 0 auto;}
|
|
47
|
+
.ssk-iconbtn,.ssk-closebtn{flex:0 0 auto;width:30px;height:30px;border:none;border-radius:8px;background:transparent;color:#fff;
|
|
47
48
|
cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.9;}
|
|
48
|
-
.ssk-closebtn:hover{background:rgba(255,255,255,.18);}
|
|
49
|
+
.ssk-iconbtn:hover,.ssk-closebtn:hover{background:rgba(255,255,255,.18);}
|
|
49
50
|
.ssk-body{flex:1;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:10px;
|
|
50
51
|
background:linear-gradient(180deg,#fff,#fbf6ef);min-height:0;}
|
|
52
|
+
.ssk-history{position:absolute;inset:0;background:#fff;display:flex;flex-direction:column;transform:translateX(-100%);
|
|
53
|
+
transition:transform .25s cubic-bezier(.16,1,.3,1);z-index:2;}
|
|
54
|
+
.ssk-history.shown{transform:translateX(0);}
|
|
55
|
+
.ssk-history-head{display:flex;align-items:center;gap:8px;padding:13px 14px;border-bottom:1px solid rgba(27,39,51,.08);flex-shrink:0;}
|
|
56
|
+
.ssk-history-head h4{margin:0;font-size:14px;font-weight:700;color:#1b2733;flex:1;}
|
|
57
|
+
.ssk-history-back{flex:0 0 auto;width:28px;height:28px;border:none;border-radius:8px;background:rgba(27,39,51,.06);
|
|
58
|
+
color:#1b2733;cursor:pointer;display:flex;align-items:center;justify-content:center;}
|
|
59
|
+
.ssk-history-back:hover{background:rgba(27,39,51,.1);}
|
|
60
|
+
.ssk-history-list{flex:1;overflow-y:auto;padding:8px;}
|
|
61
|
+
.ssk-history-empty{padding:20px 12px;text-align:center;font-size:13px;color:#9aa3ad;}
|
|
62
|
+
.ssk-history-item{display:block;width:100%;text-align:left;border:none;background:transparent;border-radius:11px;
|
|
63
|
+
padding:10px 11px;cursor:pointer;margin-bottom:2px;}
|
|
64
|
+
.ssk-history-item:hover{background:rgba(27,39,51,.05);}
|
|
65
|
+
.ssk-history-item.active{background:rgba(226,112,15,.1);}
|
|
66
|
+
.ssk-history-title{display:block;font-size:13.5px;font-weight:600;color:#1b2733;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
|
67
|
+
.ssk-history-time{display:block;font-size:11px;color:#9aa3ad;margin-top:2px;}
|
|
51
68
|
.ssk-msg{display:flex;max-width:86%;}
|
|
52
69
|
.ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;}
|
|
53
70
|
.ssk-bubble-text{padding:10px 13px;border-radius:15px;font-size:14px;line-height:1.5;word-wrap:break-word;}
|
|
@@ -78,28 +95,57 @@ function l(t) {
|
|
|
78
95
|
.ssk-foot a{color:inherit;text-decoration:underline;}
|
|
79
96
|
`, document.head.appendChild(r);
|
|
80
97
|
}
|
|
81
|
-
function
|
|
98
|
+
function p(e) {
|
|
82
99
|
return `<p>${e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").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>`;
|
|
83
100
|
}
|
|
84
|
-
function
|
|
101
|
+
function m(e, t) {
|
|
85
102
|
let n = document.createElement("div");
|
|
86
103
|
n.className = `ssk-msg ${t.role}`;
|
|
87
104
|
let r = document.createElement("div");
|
|
88
|
-
r.className = "ssk-bubble-text", r.innerHTML =
|
|
105
|
+
r.className = "ssk-bubble-text", r.innerHTML = p(t.text), n.appendChild(r), e.appendChild(n), e.scrollTop = e.scrollHeight;
|
|
89
106
|
}
|
|
90
|
-
function
|
|
107
|
+
function ee(e) {
|
|
91
108
|
try {
|
|
92
|
-
|
|
109
|
+
return JSON.parse(localStorage.getItem(t(e)) || "[]");
|
|
110
|
+
} catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function h(e, n) {
|
|
115
|
+
try {
|
|
116
|
+
localStorage.setItem(t(e), JSON.stringify(n.slice(0, 40)));
|
|
93
117
|
} catch {}
|
|
94
118
|
}
|
|
95
|
-
function
|
|
119
|
+
function g(e) {
|
|
96
120
|
try {
|
|
97
|
-
return
|
|
121
|
+
return localStorage.getItem(n(e));
|
|
98
122
|
} catch {
|
|
99
|
-
return
|
|
123
|
+
return null;
|
|
100
124
|
}
|
|
101
125
|
}
|
|
102
|
-
function
|
|
126
|
+
function _(e, t) {
|
|
127
|
+
try {
|
|
128
|
+
localStorage.setItem(n(e), t);
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
function v() {
|
|
132
|
+
return `s_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
133
|
+
}
|
|
134
|
+
function y(e) {
|
|
135
|
+
let t = e.find((e) => e.role === "user");
|
|
136
|
+
return t ? t.text.slice(0, 60) : "New conversation";
|
|
137
|
+
}
|
|
138
|
+
function te(e) {
|
|
139
|
+
let t = new Date(e), n = /* @__PURE__ */ new Date();
|
|
140
|
+
return t.toDateString() === n.toDateString() ? t.toLocaleTimeString(void 0, {
|
|
141
|
+
hour: "numeric",
|
|
142
|
+
minute: "2-digit"
|
|
143
|
+
}) : t.toLocaleDateString(void 0, {
|
|
144
|
+
month: "short",
|
|
145
|
+
day: "numeric"
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function b() {
|
|
103
149
|
let t = document.body.cloneNode(!0);
|
|
104
150
|
t.querySelectorAll(`script,style,nav,header,footer,svg,noscript,#${e}`).forEach((e) => e.remove());
|
|
105
151
|
let n = (t.textContent || "").replace(/\s+/g, " ").trim();
|
|
@@ -108,8 +154,8 @@ function m() {
|
|
|
108
154
|
text: n
|
|
109
155
|
};
|
|
110
156
|
}
|
|
111
|
-
async function
|
|
112
|
-
let { title: n, text: r } =
|
|
157
|
+
async function x(e, t) {
|
|
158
|
+
let { title: n, text: r } = b();
|
|
113
159
|
r.length < 40 || await fetch(`${e}/ingest`, {
|
|
114
160
|
method: "POST",
|
|
115
161
|
headers: { "Content-Type": "application/json" },
|
|
@@ -123,123 +169,170 @@ async function h(e, t) {
|
|
|
123
169
|
})
|
|
124
170
|
}).catch(() => {});
|
|
125
171
|
}
|
|
126
|
-
async function
|
|
127
|
-
let
|
|
172
|
+
async function S(e, t) {
|
|
173
|
+
let n = !1;
|
|
128
174
|
try {
|
|
129
|
-
|
|
175
|
+
n = !!sessionStorage.getItem(r(t)), sessionStorage.setItem(r(t), "1");
|
|
130
176
|
} catch {}
|
|
131
|
-
|
|
177
|
+
n || await fetch(`${e}/crawlSite`, {
|
|
132
178
|
method: "POST",
|
|
133
179
|
headers: { "Content-Type": "application/json" },
|
|
134
180
|
body: JSON.stringify({ embedToken: t })
|
|
135
181
|
}).catch(() => {});
|
|
136
182
|
}
|
|
137
|
-
async function
|
|
183
|
+
async function C(t) {
|
|
138
184
|
if (typeof document > "u" || document.getElementById(e)) return;
|
|
139
185
|
let n = (t.token || "").trim();
|
|
140
186
|
if (!n) {
|
|
141
187
|
console.error("[Sendystack] init() called without a token");
|
|
142
188
|
return;
|
|
143
189
|
}
|
|
144
|
-
let
|
|
190
|
+
let r = t.apiBase || "https://us-central1-sendystack-fab32.cloudfunctions.net", p;
|
|
145
191
|
try {
|
|
146
|
-
|
|
192
|
+
p = await d(r, n);
|
|
147
193
|
} catch (e) {
|
|
148
194
|
console.error("[Sendystack] failed to load widget config:", e);
|
|
149
195
|
return;
|
|
150
196
|
}
|
|
151
|
-
|
|
152
|
-
let
|
|
153
|
-
|
|
154
|
-
let
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
let D = document.createElement("button");
|
|
175
|
-
D.className = "ssk-sendbtn", D.innerHTML = o, D.setAttribute("aria-label", "Send");
|
|
176
|
-
let O = document.createElement("div");
|
|
177
|
-
O.className = "ssk-inputrow", O.append(E, D);
|
|
178
|
-
let k = document.createElement("div");
|
|
179
|
-
k.className = "ssk-foot", k.textContent = m.whiteLabel ? "AI can make mistakes — confirm important details." : "AI can make mistakes — confirm important details. · Powered by Sendystack";
|
|
197
|
+
f(p);
|
|
198
|
+
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
|
+
E || (E = {
|
|
202
|
+
id: v(),
|
|
203
|
+
title: "New conversation",
|
|
204
|
+
messages: [],
|
|
205
|
+
updatedAt: Date.now()
|
|
206
|
+
}, C.unshift(E), T = E.id, _(n, T), h(n, C));
|
|
207
|
+
let D = document.createElement("div");
|
|
208
|
+
D.className = "ssk-body";
|
|
209
|
+
function O() {
|
|
210
|
+
D.innerHTML = "";
|
|
211
|
+
let e = E.messages.length ? E.messages : [{
|
|
212
|
+
role: "bot",
|
|
213
|
+
text: p.welcomeMessage
|
|
214
|
+
}];
|
|
215
|
+
for (let t of e) m(D, t);
|
|
216
|
+
}
|
|
217
|
+
O();
|
|
218
|
+
let k = document.createElement("span");
|
|
219
|
+
k.className = "ssk-avatar", k.innerHTML = s;
|
|
180
220
|
let A = document.createElement("div");
|
|
181
|
-
A.className = "ssk-
|
|
182
|
-
let j = document.createElement("
|
|
183
|
-
j.className = "ssk-
|
|
184
|
-
let M = document.createElement("
|
|
185
|
-
M.className = "ssk-
|
|
186
|
-
let N =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
221
|
+
A.className = "ssk-id-text", A.innerHTML = `<strong>${p.assistantName}</strong><span class="ssk-status"><span class="ssk-dot"></span>Online</span>`;
|
|
222
|
+
let j = document.createElement("div");
|
|
223
|
+
j.className = "ssk-id", j.append(k, A);
|
|
224
|
+
let M = document.createElement("button");
|
|
225
|
+
M.className = "ssk-iconbtn", M.innerHTML = c, M.setAttribute("aria-label", "Chat history");
|
|
226
|
+
let N = document.createElement("button");
|
|
227
|
+
N.className = "ssk-iconbtn", N.innerHTML = l, N.setAttribute("aria-label", "New chat");
|
|
228
|
+
let P = document.createElement("button");
|
|
229
|
+
P.className = "ssk-closebtn", P.innerHTML = a, P.setAttribute("aria-label", "Close chat");
|
|
230
|
+
let F = document.createElement("div");
|
|
231
|
+
F.className = "ssk-headbtns", F.append(M, N, P);
|
|
232
|
+
let I = document.createElement("div");
|
|
233
|
+
I.className = "ssk-head", I.append(j, F);
|
|
234
|
+
let L = document.createElement("button");
|
|
235
|
+
L.className = "ssk-history-back", L.innerHTML = u, L.setAttribute("aria-label", "Back to chat");
|
|
236
|
+
let R = document.createElement("h4");
|
|
237
|
+
R.textContent = "Chat history";
|
|
238
|
+
let z = document.createElement("div");
|
|
239
|
+
z.className = "ssk-history-head", z.append(L, R);
|
|
240
|
+
let B = document.createElement("div");
|
|
241
|
+
B.className = "ssk-history-list";
|
|
242
|
+
let V = document.createElement("div");
|
|
243
|
+
V.className = "ssk-history", V.append(z, B);
|
|
244
|
+
function H() {
|
|
245
|
+
if (B.innerHTML = "", !C.length) {
|
|
246
|
+
let e = document.createElement("div");
|
|
247
|
+
e.className = "ssk-history-empty", e.textContent = "No past conversations yet.", B.appendChild(e);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
for (let e of C) {
|
|
251
|
+
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
|
+
}, B.appendChild(t);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
M.onclick = () => {
|
|
258
|
+
H(), V.classList.add("shown");
|
|
259
|
+
}, L.onclick = () => V.classList.remove("shown"), N.onclick = () => {
|
|
260
|
+
E.messages.length && (E = {
|
|
261
|
+
id: v(),
|
|
262
|
+
title: "New conversation",
|
|
263
|
+
messages: [],
|
|
264
|
+
updatedAt: Date.now()
|
|
265
|
+
}, C.unshift(E), T = E.id, _(n, T), h(n, C), O(), V.classList.remove("shown"));
|
|
266
|
+
};
|
|
267
|
+
let U = document.createElement("div");
|
|
268
|
+
U.className = "ssk-typing", U.hidden = !0, U.innerHTML = "<span class=\"ssk-dots\"><i></i><i></i><i></i></span>";
|
|
269
|
+
let W = document.createElement("textarea");
|
|
270
|
+
W.rows = 1, W.maxLength = 4e3, W.placeholder = "Ask me anything…";
|
|
271
|
+
let G = document.createElement("button");
|
|
272
|
+
G.className = "ssk-sendbtn", G.innerHTML = o, G.setAttribute("aria-label", "Send");
|
|
273
|
+
let K = document.createElement("div");
|
|
274
|
+
K.className = "ssk-inputrow", K.append(W, G);
|
|
275
|
+
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";
|
|
277
|
+
let J = document.createElement("div");
|
|
278
|
+
J.className = "ssk-panel", J.append(I, D, U, K, q, V);
|
|
279
|
+
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>`;
|
|
281
|
+
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(() => {
|
|
283
|
+
b.classList.contains("open") || X.classList.add("shown"), setTimeout(() => X.classList.remove("shown"), 6e3);
|
|
196
284
|
}, 2500);
|
|
197
|
-
function
|
|
198
|
-
|
|
285
|
+
function Z(e) {
|
|
286
|
+
U.hidden = !e, e && (D.scrollTop = D.scrollHeight);
|
|
287
|
+
}
|
|
288
|
+
function Q() {
|
|
289
|
+
E.updatedAt = Date.now(), E.title = y(E.messages);
|
|
290
|
+
let e = C.findIndex((e) => e.id === E.id);
|
|
291
|
+
e >= 0 && C.splice(e, 1), C.unshift(E), h(n, C);
|
|
199
292
|
}
|
|
200
|
-
async function
|
|
201
|
-
let e =
|
|
293
|
+
async function $() {
|
|
294
|
+
let e = W.value.trim();
|
|
202
295
|
if (!e) return;
|
|
203
|
-
|
|
296
|
+
W.value = "";
|
|
204
297
|
let t = {
|
|
205
298
|
role: "user",
|
|
206
299
|
text: e
|
|
207
300
|
};
|
|
208
|
-
|
|
301
|
+
E.messages.push(t), m(D, t), Q(), G.disabled = !0, Z(!0);
|
|
209
302
|
try {
|
|
210
|
-
let t = await (await fetch(`${
|
|
303
|
+
let t = await (await fetch(`${r}/answer`, {
|
|
211
304
|
method: "POST",
|
|
212
305
|
headers: { "Content-Type": "application/json" },
|
|
213
306
|
body: JSON.stringify({
|
|
214
307
|
embedToken: n,
|
|
215
308
|
query: e
|
|
216
309
|
})
|
|
217
|
-
})).json(),
|
|
310
|
+
})).json(), i = {
|
|
218
311
|
role: "bot",
|
|
219
312
|
text: t.ok ? t.reply : t.message || "Sorry, I couldn't process that right now."
|
|
220
313
|
};
|
|
221
|
-
|
|
314
|
+
E.messages.push(i), m(D, i);
|
|
222
315
|
} catch {
|
|
223
316
|
let e = {
|
|
224
317
|
role: "bot",
|
|
225
318
|
text: "I couldn't reach the server. Please try again."
|
|
226
319
|
};
|
|
227
|
-
|
|
320
|
+
E.messages.push(e), m(D, e);
|
|
228
321
|
} finally {
|
|
229
|
-
|
|
322
|
+
Z(!1), G.disabled = !1, Q();
|
|
230
323
|
}
|
|
231
324
|
}
|
|
232
|
-
|
|
233
|
-
e.key === "Enter" && !e.shiftKey && (e.preventDefault(),
|
|
234
|
-
}),
|
|
325
|
+
G.onclick = $, W.addEventListener("keydown", (e) => {
|
|
326
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), $());
|
|
327
|
+
}), x(r, n), S(r, n);
|
|
235
328
|
}
|
|
236
|
-
function
|
|
329
|
+
function w() {
|
|
237
330
|
let e = document.currentScript || Array.from(document.getElementsByTagName("script")).find((e) => /widget(\.global)?\.js/.test(e.src)), t = e?.dataset.token;
|
|
238
|
-
t &&
|
|
331
|
+
t && C({
|
|
239
332
|
token: t,
|
|
240
333
|
apiBase: e?.dataset.apiBase
|
|
241
334
|
});
|
|
242
335
|
}
|
|
243
|
-
typeof document < "u" &&
|
|
336
|
+
typeof document < "u" && w();
|
|
244
337
|
//#endregion
|
|
245
|
-
export {
|
|
338
|
+
export { C as init };
|
package/dist/widget.global.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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=e=>`
|
|
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
2
|
#${n} *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
|
|
3
3
|
#${n}{position:fixed;${t}:20px;bottom:20px;z-index:2147483000;}
|
|
4
4
|
.ssk-launcher{position:relative;width:60px;height:60px;border-radius:50%;border:none;cursor:pointer;color:#fff;
|
|
@@ -31,11 +31,28 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
|
|
|
31
31
|
.ssk-id-text strong{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
|
32
32
|
.ssk-status{display:flex;align-items:center;gap:5px;font-size:11px;opacity:.9;}
|
|
33
33
|
.ssk-dot{width:6px;height:6px;border-radius:50%;background:#46e08a;box-shadow:0 0 7px #46e08a;}
|
|
34
|
-
.ssk-
|
|
34
|
+
.ssk-headbtns{display:flex;align-items:center;gap:2px;flex:0 0 auto;}
|
|
35
|
+
.ssk-iconbtn,.ssk-closebtn{flex:0 0 auto;width:30px;height:30px;border:none;border-radius:8px;background:transparent;color:#fff;
|
|
35
36
|
cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.9;}
|
|
36
|
-
.ssk-closebtn:hover{background:rgba(255,255,255,.18);}
|
|
37
|
+
.ssk-iconbtn:hover,.ssk-closebtn:hover{background:rgba(255,255,255,.18);}
|
|
37
38
|
.ssk-body{flex:1;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:10px;
|
|
38
39
|
background:linear-gradient(180deg,#fff,#fbf6ef);min-height:0;}
|
|
40
|
+
.ssk-history{position:absolute;inset:0;background:#fff;display:flex;flex-direction:column;transform:translateX(-100%);
|
|
41
|
+
transition:transform .25s cubic-bezier(.16,1,.3,1);z-index:2;}
|
|
42
|
+
.ssk-history.shown{transform:translateX(0);}
|
|
43
|
+
.ssk-history-head{display:flex;align-items:center;gap:8px;padding:13px 14px;border-bottom:1px solid rgba(27,39,51,.08);flex-shrink:0;}
|
|
44
|
+
.ssk-history-head h4{margin:0;font-size:14px;font-weight:700;color:#1b2733;flex:1;}
|
|
45
|
+
.ssk-history-back{flex:0 0 auto;width:28px;height:28px;border:none;border-radius:8px;background:rgba(27,39,51,.06);
|
|
46
|
+
color:#1b2733;cursor:pointer;display:flex;align-items:center;justify-content:center;}
|
|
47
|
+
.ssk-history-back:hover{background:rgba(27,39,51,.1);}
|
|
48
|
+
.ssk-history-list{flex:1;overflow-y:auto;padding:8px;}
|
|
49
|
+
.ssk-history-empty{padding:20px 12px;text-align:center;font-size:13px;color:#9aa3ad;}
|
|
50
|
+
.ssk-history-item{display:block;width:100%;text-align:left;border:none;background:transparent;border-radius:11px;
|
|
51
|
+
padding:10px 11px;cursor:pointer;margin-bottom:2px;}
|
|
52
|
+
.ssk-history-item:hover{background:rgba(27,39,51,.05);}
|
|
53
|
+
.ssk-history-item.active{background:rgba(226,112,15,.1);}
|
|
54
|
+
.ssk-history-title{display:block;font-size:13.5px;font-weight:600;color:#1b2733;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
|
55
|
+
.ssk-history-time{display:block;font-size:11px;color:#9aa3ad;margin-top:2px;}
|
|
39
56
|
.ssk-msg{display:flex;max-width:86%;}
|
|
40
57
|
.ssk-msg.bot{align-self:flex-start;} .ssk-msg.user{align-self:flex-end;}
|
|
41
58
|
.ssk-bubble-text{padding:10px 13px;border-radius:15px;font-size:14px;line-height:1.5;word-wrap:break-word;}
|
|
@@ -64,4 +81,4 @@ var Sendystack=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`M
|
|
|
64
81
|
.ssk-sendbtn:disabled{opacity:.6;cursor:default;transform:none;}
|
|
65
82
|
.ssk-foot{text-align:center;font-size:10.5px;color:#9aa3ad;padding:0 8px 9px;background:#fff;flex-shrink:0;}
|
|
66
83
|
.ssk-foot a{color:inherit;text-decoration:underline;}
|
|
67
|
-
`,document.head.appendChild(r)}function
|
|
84
|
+
`,document.head.appendChild(r)}function h(e){return`<p>${e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).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})({});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendystack/widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|