@cloudflare/ai-search-snippet 0.0.36 → 0.0.38
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/main.d.ts +231 -11
- package/dist/search-snippet.es.js +651 -325
- package/dist/search-snippet.es.js.map +1 -1
- package/dist/search-snippet.umd.js +117 -125
- package/dist/search-snippet.umd.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,19 +1,113 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var a = (n, i, e) =>
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
var G = Object.defineProperty;
|
|
2
|
+
var Y = (n, i, e) => i in n ? G(n, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[i] = e;
|
|
3
|
+
var a = (n, i, e) => Y(n, typeof i != "symbol" ? i + "" : i, e);
|
|
4
|
+
const L = {
|
|
5
|
+
// Shared
|
|
6
|
+
loadingAriaLabel: "Loading",
|
|
7
|
+
errorPrefix: "Error:",
|
|
8
|
+
missingApiUrlError: "The api-url attribute is required. Please provide a valid API URL.",
|
|
9
|
+
poweredBy: "Powered by",
|
|
10
|
+
poweredByLinkLabel: "Cloudflare AI Search",
|
|
11
|
+
// Search (shared between bar and modal)
|
|
12
|
+
placeholder: "Search...",
|
|
13
|
+
searchButtonLabel: "Search",
|
|
14
|
+
searchInputAriaLabel: "Search input",
|
|
15
|
+
searchResultsAriaLabel: "Search results",
|
|
16
|
+
emptyStateTitle: "Start Searching",
|
|
17
|
+
emptyStateDescription: "Enter a query to search for results",
|
|
18
|
+
modalEmptyStateDescription: "Start typing to search",
|
|
19
|
+
noResultsTitle: "No Results Found",
|
|
20
|
+
noResultsDescription: 'No results found for "{query}"',
|
|
21
|
+
modalNoResultsTitle: "No results found",
|
|
22
|
+
modalNoResultsDescription: 'No results for "{query}"',
|
|
23
|
+
resultsCount: "Found {n} result",
|
|
24
|
+
resultsCountPlural: "Found {n} results",
|
|
25
|
+
resultsCountOverflow: "Showing {n} of {total} results",
|
|
26
|
+
modalResultsCount: "{n} result",
|
|
27
|
+
modalResultsCountPlural: "{n} results",
|
|
28
|
+
modalResultsCountZero: "0 results",
|
|
29
|
+
modalResultsCountError: "Error",
|
|
30
|
+
seeMoreResults: "See more results",
|
|
31
|
+
// Modal-only
|
|
32
|
+
navigateHint: "Navigate",
|
|
33
|
+
selectHint: "Select",
|
|
34
|
+
closeHint: "Close",
|
|
35
|
+
// Chat (shared between bubble, page, and chat view)
|
|
36
|
+
chatTitle: "Chat",
|
|
37
|
+
chatPlaceholder: "Type a message...",
|
|
38
|
+
chatInputAriaLabel: "Chat message input",
|
|
39
|
+
sendButtonLabel: "Send",
|
|
40
|
+
sendButtonAriaLabel: "Send message",
|
|
41
|
+
chatEmptyTitle: "Start a Conversation",
|
|
42
|
+
chatEmptyDescription: "Send a message to begin chatting",
|
|
43
|
+
userAvatar: "U",
|
|
44
|
+
assistantAvatar: "AI",
|
|
45
|
+
unknownError: "Unknown error",
|
|
46
|
+
// Chat bubble
|
|
47
|
+
openChatAriaLabel: "Open chat",
|
|
48
|
+
clearHistoryAriaLabel: "Clear history",
|
|
49
|
+
minimizeAriaLabel: "Minimize",
|
|
50
|
+
closeAriaLabel: "Close",
|
|
51
|
+
// Chat page
|
|
52
|
+
historyTitle: "History",
|
|
53
|
+
newChatButton: "New Chat",
|
|
54
|
+
clearChatButton: "Clear Chat",
|
|
55
|
+
toggleSidebarTitle: "Toggle sidebar",
|
|
56
|
+
deleteChatTitle: "Delete chat",
|
|
57
|
+
noChatsYet: "No chats yet",
|
|
58
|
+
yesterday: "Yesterday",
|
|
59
|
+
// Relative timestamps (formatTimestamp)
|
|
60
|
+
justNow: "Just now",
|
|
61
|
+
minuteAgo: "{n} minute ago",
|
|
62
|
+
minutesAgo: "{n} minutes ago",
|
|
63
|
+
hourAgo: "{n} hour ago",
|
|
64
|
+
hoursAgo: "{n} hours ago",
|
|
65
|
+
// Cycling loading messages
|
|
66
|
+
loadingMessages: [
|
|
67
|
+
"Searching...",
|
|
68
|
+
"Digging through results...",
|
|
69
|
+
"Scanning the knowledge base...",
|
|
70
|
+
"Finding the best matches...",
|
|
71
|
+
"Sifting through the data...",
|
|
72
|
+
"Almost there...",
|
|
73
|
+
"Looking far and wide...",
|
|
74
|
+
"Connecting the dots...",
|
|
75
|
+
"Rummaging through pages...",
|
|
76
|
+
"Hunting down answers..."
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
function d(n) {
|
|
80
|
+
if (!n || typeof n != "object")
|
|
81
|
+
return L;
|
|
82
|
+
const i = { ...L };
|
|
83
|
+
for (const e of Object.keys(n)) {
|
|
84
|
+
const t = n[e];
|
|
85
|
+
if (t != null) {
|
|
86
|
+
if (e === "loadingMessages") {
|
|
87
|
+
Array.isArray(t) && t.length > 0 && (i.loadingMessages = t.filter((s) => typeof s == "string"), i.loadingMessages.length === 0 && (i.loadingMessages = L.loadingMessages));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
typeof t == "string" && (i[e] = t);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return i;
|
|
94
|
+
}
|
|
95
|
+
function b(n, i = {}) {
|
|
96
|
+
return n.replace(/\{(\w+)\}/g, (e, t) => Object.hasOwn(i, t) ? String(i[t]) : e);
|
|
97
|
+
}
|
|
98
|
+
function k(n, i) {
|
|
99
|
+
if (!n) return null;
|
|
100
|
+
try {
|
|
101
|
+
const e = JSON.parse(n);
|
|
102
|
+
if (e === null || typeof e != "object" || Array.isArray(e))
|
|
103
|
+
throw new Error("translations must be a JSON object");
|
|
104
|
+
return e;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return console.error(`${i}: invalid translations attribute`, e), null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const T = 2500;
|
|
110
|
+
function P(n, i) {
|
|
17
111
|
let e;
|
|
18
112
|
function t(...s) {
|
|
19
113
|
clearTimeout(e), e = setTimeout(() => {
|
|
@@ -22,49 +116,49 @@ function $(n, i) {
|
|
|
22
116
|
}
|
|
23
117
|
return t.cancel = () => clearTimeout(e), t;
|
|
24
118
|
}
|
|
25
|
-
function
|
|
119
|
+
function l(n) {
|
|
26
120
|
const i = document.createElement("div");
|
|
27
121
|
return i.textContent = n, i.innerHTML;
|
|
28
122
|
}
|
|
29
|
-
function
|
|
123
|
+
function U(n) {
|
|
30
124
|
try {
|
|
31
125
|
return decodeURI(n);
|
|
32
126
|
} catch {
|
|
33
127
|
return n;
|
|
34
128
|
}
|
|
35
129
|
}
|
|
36
|
-
function
|
|
130
|
+
function A(n) {
|
|
37
131
|
return new DOMParser().parseFromString(n, "text/html").documentElement.textContent || "";
|
|
38
132
|
}
|
|
39
|
-
function
|
|
40
|
-
const i = new Date(n),
|
|
41
|
-
if (
|
|
42
|
-
return
|
|
43
|
-
if (
|
|
44
|
-
const
|
|
45
|
-
return
|
|
133
|
+
function J(n, i) {
|
|
134
|
+
const e = d(i), t = new Date(n), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime();
|
|
135
|
+
if (r < 6e4)
|
|
136
|
+
return e.justNow;
|
|
137
|
+
if (r < 36e5) {
|
|
138
|
+
const o = Math.floor(r / 6e4), c = o === 1 ? e.minuteAgo : e.minutesAgo;
|
|
139
|
+
return b(c, { n: o });
|
|
46
140
|
}
|
|
47
|
-
if (
|
|
48
|
-
const
|
|
49
|
-
return
|
|
141
|
+
if (r < 864e5) {
|
|
142
|
+
const o = Math.floor(r / 36e5), c = o === 1 ? e.hourAgo : e.hoursAgo;
|
|
143
|
+
return b(c, { n: o });
|
|
50
144
|
}
|
|
51
|
-
return
|
|
145
|
+
return t.toLocaleString(void 0, {
|
|
52
146
|
month: "short",
|
|
53
147
|
day: "numeric",
|
|
54
148
|
hour: "2-digit",
|
|
55
149
|
minute: "2-digit"
|
|
56
150
|
});
|
|
57
151
|
}
|
|
58
|
-
function
|
|
152
|
+
function j(n) {
|
|
59
153
|
return new Date(n).toLocaleDateString(void 0, {
|
|
60
154
|
month: "short",
|
|
61
155
|
day: "numeric"
|
|
62
156
|
});
|
|
63
157
|
}
|
|
64
|
-
function
|
|
158
|
+
function I(n = "id") {
|
|
65
159
|
return `${n}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
66
160
|
}
|
|
67
|
-
function
|
|
161
|
+
function p(n, i) {
|
|
68
162
|
return n !== null ? n : i;
|
|
69
163
|
}
|
|
70
164
|
function m(n, i) {
|
|
@@ -83,61 +177,62 @@ function v(n, i) {
|
|
|
83
177
|
cancelable: !0
|
|
84
178
|
});
|
|
85
179
|
}
|
|
86
|
-
function
|
|
180
|
+
function S(n) {
|
|
87
181
|
if (!n)
|
|
88
182
|
throw new Error("API URL is required");
|
|
89
|
-
return new
|
|
183
|
+
return new Q(n);
|
|
90
184
|
}
|
|
91
|
-
function
|
|
185
|
+
function C(n) {
|
|
92
186
|
return n !== null && typeof n == "object" && !Array.isArray(n);
|
|
93
187
|
}
|
|
94
|
-
function
|
|
188
|
+
function _(...n) {
|
|
95
189
|
const i = {};
|
|
96
190
|
for (const e of n)
|
|
97
191
|
if (e)
|
|
98
192
|
for (const [t, s] of Object.entries(e)) {
|
|
99
193
|
const r = i[t];
|
|
100
|
-
|
|
194
|
+
C(r) && C(s) ? i[t] = _(r, s) : i[t] = s;
|
|
101
195
|
}
|
|
102
196
|
return i;
|
|
103
197
|
}
|
|
104
|
-
function
|
|
105
|
-
if (!
|
|
198
|
+
function W(n, i) {
|
|
199
|
+
if (!C(i))
|
|
106
200
|
return n;
|
|
107
201
|
const e = new URLSearchParams();
|
|
108
|
-
for (const [
|
|
109
|
-
|
|
202
|
+
for (const [h, u] of Object.entries(i))
|
|
203
|
+
u != null && e.append(h, String(u));
|
|
110
204
|
const t = e.toString();
|
|
111
205
|
if (!t)
|
|
112
206
|
return n;
|
|
113
207
|
const s = n.indexOf("#"), r = s === -1 ? n : n.slice(0, s), o = s === -1 ? "" : n.slice(s), c = r.includes("?") ? "&" : "?";
|
|
114
208
|
return `${r}${c}${t}${o}`;
|
|
115
209
|
}
|
|
116
|
-
function
|
|
117
|
-
if (!
|
|
210
|
+
function Z(n) {
|
|
211
|
+
if (!C(n))
|
|
118
212
|
return {};
|
|
119
213
|
const i = {};
|
|
120
214
|
for (const [e, t] of Object.entries(n))
|
|
121
215
|
t != null && (i[e] = String(t));
|
|
122
216
|
return i;
|
|
123
217
|
}
|
|
124
|
-
function
|
|
125
|
-
return
|
|
218
|
+
function X(n) {
|
|
219
|
+
return C(n) ? n : void 0;
|
|
126
220
|
}
|
|
127
|
-
class
|
|
221
|
+
class Q {
|
|
128
222
|
constructor(i) {
|
|
129
223
|
a(this, "activeRequests", /* @__PURE__ */ new Map());
|
|
130
224
|
a(this, "baseUrl");
|
|
131
225
|
this.baseUrl = i.replace(/\/$/, "");
|
|
132
226
|
}
|
|
133
227
|
request(i, e, t, s) {
|
|
134
|
-
const r = e === "search" ? "snippet-search" : "snippet-chat-completions", o =
|
|
228
|
+
const r = e === "search" ? "snippet-search" : "snippet-chat-completions", o = W(`${this.baseUrl}/${e}`, s?.queryParams);
|
|
135
229
|
return fetch(o, {
|
|
136
230
|
method: "POST",
|
|
137
|
-
body: JSON.stringify(
|
|
231
|
+
body: JSON.stringify(_(X(s?.body), i)),
|
|
138
232
|
headers: {
|
|
139
|
-
...
|
|
233
|
+
...Z(s?.headers),
|
|
140
234
|
"Content-Type": "application/json",
|
|
235
|
+
Accept: i.stream ? "text/event-stream" : "application/json",
|
|
141
236
|
"cf-ai-search-source": r
|
|
142
237
|
},
|
|
143
238
|
signal: t
|
|
@@ -172,17 +267,17 @@ class K {
|
|
|
172
267
|
const c = await o.json();
|
|
173
268
|
if (c.success && c.result)
|
|
174
269
|
return c.result.chunks.map(
|
|
175
|
-
(
|
|
270
|
+
(h) => ({
|
|
176
271
|
type: "result",
|
|
177
|
-
id:
|
|
178
|
-
title:
|
|
179
|
-
description:
|
|
180
|
-
timestamp:
|
|
181
|
-
url:
|
|
182
|
-
image:
|
|
272
|
+
id: h.id,
|
|
273
|
+
title: A(h.item.metadata?.title),
|
|
274
|
+
description: h.item.metadata?.description ? A(h.item.metadata?.description) : "",
|
|
275
|
+
timestamp: h.item.timestamp ?? void 0,
|
|
276
|
+
url: h.item.key,
|
|
277
|
+
image: h.item.metadata?.image || void 0,
|
|
183
278
|
metadata: {
|
|
184
|
-
...
|
|
185
|
-
instance_id:
|
|
279
|
+
...h.item.metadata,
|
|
280
|
+
instance_id: h.instance_id
|
|
186
281
|
}
|
|
187
282
|
})
|
|
188
283
|
);
|
|
@@ -211,13 +306,13 @@ class K {
|
|
|
211
306
|
if (!o.body)
|
|
212
307
|
throw new Error("Response body is empty");
|
|
213
308
|
let c = "";
|
|
214
|
-
const
|
|
309
|
+
const h = o.body.getReader(), u = new TextDecoder();
|
|
215
310
|
for (; ; ) {
|
|
216
|
-
const { done:
|
|
217
|
-
if (
|
|
311
|
+
const { done: g, value: w } = await h.read();
|
|
312
|
+
if (g)
|
|
218
313
|
break;
|
|
219
|
-
const
|
|
220
|
-
c +=
|
|
314
|
+
const F = u.decode(w, { stream: !0 });
|
|
315
|
+
c += F;
|
|
221
316
|
}
|
|
222
317
|
yield {
|
|
223
318
|
type: "result",
|
|
@@ -225,7 +320,7 @@ class K {
|
|
|
225
320
|
title: "",
|
|
226
321
|
description: c.replaceAll("data: ", "").trim().split(`
|
|
227
322
|
|
|
228
|
-
`).map((
|
|
323
|
+
`).map((g) => JSON.parse(g)).map((g) => g.response).join(""),
|
|
229
324
|
url: "",
|
|
230
325
|
metadata: {}
|
|
231
326
|
};
|
|
@@ -285,10 +380,10 @@ class K {
|
|
|
285
380
|
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
286
381
|
}
|
|
287
382
|
}
|
|
288
|
-
const
|
|
383
|
+
const ee = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w3.org/2000/svg" aria-label="Cloudflare" role="img">
|
|
289
384
|
<path fill="#f38020" d="m280.8395,183.31456c11,-26 -4,-38 -19,-38l-148,-2c-4,0 -4,-6 1,-7l150,-2c17,-1 37,-15 43,-33c0,0 10,-21 9,-24a97,97 0 0 0 -187,-11c-38,-25 -78,9 -69,46c-48,3 -65,46 -60,72c0,1 1,2 3,2l274,0c1,0 3,-1 3,-3z"/>
|
|
290
385
|
<path fill="#faae40" d="m330.8395,81.31456c-4,0 -6,-1 -7,1l-5,21c-5,16 3,30 20,31l32,2c4,0 4,6 -1,7l-33,1c-36,4 -46,39 -46,39c0,2 0,3 2,3l113,0l3,-2a81,81 0 0 0 -78,-103"/>
|
|
291
|
-
</svg>`,
|
|
386
|
+
</svg>`, te = "https://workers.cloudflare.com/product/ai-search", E = `Powered by <a href="${te}" target="_blank" rel="noopener noreferrer">Cloudflare AI Search ${ee}</a>`, V = `
|
|
292
387
|
/* Chat container */
|
|
293
388
|
.chat-container {
|
|
294
389
|
display: flex;
|
|
@@ -605,7 +700,7 @@ const G = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w
|
|
|
605
700
|
.chat-message-bubble a:hover {
|
|
606
701
|
text-decoration: none;
|
|
607
702
|
}
|
|
608
|
-
`,
|
|
703
|
+
`, M = `
|
|
609
704
|
:host {
|
|
610
705
|
/* Colors - Light Mode */
|
|
611
706
|
--search-snippet-primary-color: #2563eb;
|
|
@@ -1040,17 +1135,17 @@ const G = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w
|
|
|
1040
1135
|
text-align: center;
|
|
1041
1136
|
}
|
|
1042
1137
|
`;
|
|
1043
|
-
function
|
|
1138
|
+
function se(n) {
|
|
1044
1139
|
let i = n;
|
|
1045
|
-
i =
|
|
1140
|
+
i = ie(i), i = i.replace(/```([\s\S]*?)```/g, (o, c) => `<pre><code>${c.trim()}</code></pre>`);
|
|
1046
1141
|
const e = i.split(`
|
|
1047
1142
|
`), t = [];
|
|
1048
1143
|
let s = !1, r = "";
|
|
1049
1144
|
for (let o = 0; o < e.length; o++) {
|
|
1050
|
-
const c = e[o],
|
|
1051
|
-
if (
|
|
1052
|
-
const
|
|
1053
|
-
t.push(`<h${
|
|
1145
|
+
const c = e[o], h = c.match(/^(#{1,6})\s+(.+)$/);
|
|
1146
|
+
if (h) {
|
|
1147
|
+
const g = h[1].length, w = h[2];
|
|
1148
|
+
t.push(`<h${g}>${x(w)}</h${g}>`);
|
|
1054
1149
|
continue;
|
|
1055
1150
|
}
|
|
1056
1151
|
if (c.match(/^---+$/)) {
|
|
@@ -1058,37 +1153,37 @@ function Y(n) {
|
|
|
1058
1153
|
continue;
|
|
1059
1154
|
}
|
|
1060
1155
|
if (c.match(/^>\s+/)) {
|
|
1061
|
-
const
|
|
1062
|
-
t.push(`<blockquote>${
|
|
1156
|
+
const g = c.replace(/^>\s+/, "");
|
|
1157
|
+
t.push(`<blockquote>${x(g)}</blockquote>`);
|
|
1063
1158
|
continue;
|
|
1064
1159
|
}
|
|
1065
|
-
const
|
|
1066
|
-
if (
|
|
1067
|
-
(!s || r !== "ul") && (s && t.push(`</${r}>`), t.push("<ul>"), s = !0, r = "ul"), t.push(`<li>${
|
|
1160
|
+
const u = c.match(/^[-*]\s+(.+)$/);
|
|
1161
|
+
if (u) {
|
|
1162
|
+
(!s || r !== "ul") && (s && t.push(`</${r}>`), t.push("<ul>"), s = !0, r = "ul"), t.push(`<li>${x(u[1])}</li>`);
|
|
1068
1163
|
continue;
|
|
1069
1164
|
}
|
|
1070
|
-
const
|
|
1071
|
-
if (
|
|
1072
|
-
(!s || r !== "ol") && (s && t.push(`</${r}>`), t.push("<ol>"), s = !0, r = "ol"), t.push(`<li>${f
|
|
1165
|
+
const f = c.match(/^\d+\.\s+(.+)$/);
|
|
1166
|
+
if (f) {
|
|
1167
|
+
(!s || r !== "ol") && (s && t.push(`</${r}>`), t.push("<ol>"), s = !0, r = "ol"), t.push(`<li>${x(f[1])}</li>`);
|
|
1073
1168
|
continue;
|
|
1074
1169
|
}
|
|
1075
1170
|
if (s && (t.push(`</${r}>`), s = !1, r = ""), c.trim() === "") {
|
|
1076
1171
|
t.push("<br />");
|
|
1077
1172
|
continue;
|
|
1078
1173
|
}
|
|
1079
|
-
t.push(`<p>${
|
|
1174
|
+
t.push(`<p>${x(c)}</p>`);
|
|
1080
1175
|
}
|
|
1081
1176
|
return s && t.push(`</${r}>`), t.join(`
|
|
1082
1177
|
`);
|
|
1083
1178
|
}
|
|
1084
|
-
function
|
|
1179
|
+
function x(n) {
|
|
1085
1180
|
let i = n;
|
|
1086
1181
|
return i = i.replace(/`([^`]+)`/g, "<code>$1</code>"), i = i.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>"), i = i.replace(/___(.+?)___/g, "<strong><em>$1</em></strong>"), i = i.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), i = i.replace(/__(.+?)__/g, "<strong>$1</strong>"), i = i.replace(/\*(.+?)\*/g, "<em>$1</em>"), i = i.replace(/_(.+?)_/g, "<em>$1</em>"), i = i.replace(
|
|
1087
1182
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
1088
1183
|
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
1089
1184
|
), i;
|
|
1090
1185
|
}
|
|
1091
|
-
function
|
|
1186
|
+
function ie(n) {
|
|
1092
1187
|
const i = {
|
|
1093
1188
|
"&": "&",
|
|
1094
1189
|
"<": "<",
|
|
@@ -1098,11 +1193,12 @@ function J(n) {
|
|
|
1098
1193
|
};
|
|
1099
1194
|
return n.replace(/[&<>"']/g, (e) => i[e] || e);
|
|
1100
1195
|
}
|
|
1101
|
-
class
|
|
1196
|
+
class K {
|
|
1102
1197
|
constructor(i, e, t) {
|
|
1103
1198
|
a(this, "container");
|
|
1104
1199
|
a(this, "client");
|
|
1105
1200
|
a(this, "props");
|
|
1201
|
+
a(this, "translations");
|
|
1106
1202
|
a(this, "inputElement", null);
|
|
1107
1203
|
a(this, "messagesContainer", null);
|
|
1108
1204
|
a(this, "sendButton", null);
|
|
@@ -1115,42 +1211,49 @@ class N {
|
|
|
1115
1211
|
a(this, "handleInputResize", null);
|
|
1116
1212
|
a(this, "handleInputKeydown", null);
|
|
1117
1213
|
a(this, "handleSendClick", null);
|
|
1118
|
-
this.container = i, this.client = e, this.props = t, this.render(), this.attachEventListeners();
|
|
1214
|
+
this.container = i, this.client = e, this.props = t, this.translations = d(t.translations), this.render(), this.attachEventListeners();
|
|
1119
1215
|
}
|
|
1120
1216
|
/**
|
|
1121
1217
|
* Render the chat interface
|
|
1122
1218
|
*/
|
|
1123
1219
|
render() {
|
|
1220
|
+
const i = this.translations;
|
|
1124
1221
|
this.container.innerHTML = `
|
|
1125
1222
|
<div class="chat-container">
|
|
1126
1223
|
<div class="chat-messages">
|
|
1127
|
-
|
|
1128
|
-
<svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1129
|
-
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1130
|
-
</svg>
|
|
1131
|
-
<div class="chat-empty-title">Start a Conversation</div>
|
|
1132
|
-
<div class="chat-empty-description">
|
|
1133
|
-
Send a message to begin chatting
|
|
1134
|
-
</div>
|
|
1135
|
-
</div>
|
|
1224
|
+
${this.renderEmptyStateHTML()}
|
|
1136
1225
|
</div>
|
|
1137
1226
|
<div class="chat-input-area">
|
|
1138
1227
|
<div class="chat-input-wrapper">
|
|
1139
1228
|
<textarea
|
|
1140
1229
|
class="chat-input"
|
|
1141
|
-
placeholder="${
|
|
1142
|
-
aria-label="
|
|
1230
|
+
placeholder="${l(this.props.placeholder || i.chatPlaceholder)}"
|
|
1231
|
+
aria-label="${l(i.chatInputAriaLabel)}"
|
|
1143
1232
|
style="height: 40px;"
|
|
1144
1233
|
rows="1"
|
|
1145
1234
|
></textarea>
|
|
1146
|
-
<button class="button chat-send-button" aria-label="
|
|
1147
|
-
<span
|
|
1235
|
+
<button class="button chat-send-button" aria-label="${l(i.sendButtonAriaLabel)}">
|
|
1236
|
+
<span>${l(i.sendButtonLabel)}</span>
|
|
1148
1237
|
</button>
|
|
1149
1238
|
</div>
|
|
1150
1239
|
</div>
|
|
1151
1240
|
</div>
|
|
1152
1241
|
`, this.messagesContainer = this.container.querySelector(".chat-messages"), this.inputElement = this.container.querySelector(".chat-input"), this.sendButton = this.container.querySelector(".chat-send-button");
|
|
1153
1242
|
}
|
|
1243
|
+
renderEmptyStateHTML() {
|
|
1244
|
+
const i = this.translations;
|
|
1245
|
+
return `
|
|
1246
|
+
<div class="chat-empty">
|
|
1247
|
+
<svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1248
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1249
|
+
</svg>
|
|
1250
|
+
<div class="chat-empty-title">${l(i.chatEmptyTitle)}</div>
|
|
1251
|
+
<div class="chat-empty-description">
|
|
1252
|
+
${l(i.chatEmptyDescription)}
|
|
1253
|
+
</div>
|
|
1254
|
+
</div>
|
|
1255
|
+
`;
|
|
1256
|
+
}
|
|
1154
1257
|
/**
|
|
1155
1258
|
* Attach event listeners
|
|
1156
1259
|
*/
|
|
@@ -1177,13 +1280,13 @@ class N {
|
|
|
1177
1280
|
*/
|
|
1178
1281
|
async sendMessage(i) {
|
|
1179
1282
|
const e = {
|
|
1180
|
-
id:
|
|
1283
|
+
id: I("msg"),
|
|
1181
1284
|
role: "user",
|
|
1182
1285
|
content: i,
|
|
1183
1286
|
timestamp: Date.now()
|
|
1184
1287
|
};
|
|
1185
1288
|
this.addMessage(e), this.renderMessages(!0), this.setStreamingState(!0);
|
|
1186
|
-
const t =
|
|
1289
|
+
const t = I("msg"), s = {
|
|
1187
1290
|
id: t,
|
|
1188
1291
|
role: "assistant",
|
|
1189
1292
|
content: "",
|
|
@@ -1193,14 +1296,17 @@ class N {
|
|
|
1193
1296
|
try {
|
|
1194
1297
|
const r = this.client.chat(i);
|
|
1195
1298
|
let o = "";
|
|
1196
|
-
for await (const
|
|
1197
|
-
if (
|
|
1198
|
-
o +=
|
|
1199
|
-
else if (
|
|
1200
|
-
this.showErrorInMessage(
|
|
1299
|
+
for await (const h of r)
|
|
1300
|
+
if (h.type === "text" && h.message)
|
|
1301
|
+
o += h.message, this.updateStreamingMessage(t, o);
|
|
1302
|
+
else if (h.type === "error") {
|
|
1303
|
+
this.showErrorInMessage(
|
|
1304
|
+
t,
|
|
1305
|
+
h.message || this.translations.unknownError
|
|
1306
|
+
);
|
|
1201
1307
|
break;
|
|
1202
1308
|
}
|
|
1203
|
-
const c = this.messages.findIndex((
|
|
1309
|
+
const c = this.messages.findIndex((h) => h.id === t);
|
|
1204
1310
|
c !== -1 && (this.messages[c].content = o), this.container.dispatchEvent(v("message", { message: s }));
|
|
1205
1311
|
} catch (r) {
|
|
1206
1312
|
this.showErrorInMessage(t, r.message), this.container.dispatchEvent(
|
|
@@ -1233,7 +1339,7 @@ class N {
|
|
|
1233
1339
|
*/
|
|
1234
1340
|
showErrorInMessage(i, e) {
|
|
1235
1341
|
const t = this.messages.findIndex((s) => s.id === i);
|
|
1236
|
-
t !== -1 && (this.messages[t].content =
|
|
1342
|
+
t !== -1 && (this.messages[t].content = `${this.translations.errorPrefix} ${e}`, this.renderMessages());
|
|
1237
1343
|
}
|
|
1238
1344
|
/**
|
|
1239
1345
|
* Render all messages
|
|
@@ -1241,17 +1347,7 @@ class N {
|
|
|
1241
1347
|
renderMessages(i = !1) {
|
|
1242
1348
|
if (!this.messagesContainer) return;
|
|
1243
1349
|
if (this.messages.length === 0) {
|
|
1244
|
-
this.messagesContainer.innerHTML =
|
|
1245
|
-
<div class="chat-empty">
|
|
1246
|
-
<svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1247
|
-
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1248
|
-
</svg>
|
|
1249
|
-
<div class="chat-empty-title">Start a Conversation</div>
|
|
1250
|
-
<div class="chat-empty-description">
|
|
1251
|
-
Send a message to begin chatting
|
|
1252
|
-
</div>
|
|
1253
|
-
</div>
|
|
1254
|
-
`;
|
|
1350
|
+
this.messagesContainer.innerHTML = this.renderEmptyStateHTML();
|
|
1255
1351
|
return;
|
|
1256
1352
|
}
|
|
1257
1353
|
const e = this.messages.map(
|
|
@@ -1263,17 +1359,17 @@ class N {
|
|
|
1263
1359
|
* Render a single message
|
|
1264
1360
|
*/
|
|
1265
1361
|
renderMessage(i, e = !1) {
|
|
1266
|
-
const t = `chat-message-${i.role}`,
|
|
1362
|
+
const t = this.translations, s = `chat-message-${i.role}`, r = i.role === "user" ? t.userAvatar : t.assistantAvatar, o = t.loadingMessages[this.loadingMessageIndex] ?? "";
|
|
1267
1363
|
return `
|
|
1268
|
-
<div class="chat-message ${
|
|
1269
|
-
<div class="chat-message-avatar">${
|
|
1364
|
+
<div class="chat-message ${s}">
|
|
1365
|
+
<div class="chat-message-avatar">${l(r)}</div>
|
|
1270
1366
|
<div class="chat-message-content">
|
|
1271
1367
|
<div class="chat-message-bubble">
|
|
1272
|
-
${i.content ? `<div class="chat-message-text">${
|
|
1273
|
-
${e ? `<div class="chat-streaming"><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span><span class="loading-text">${
|
|
1368
|
+
${i.content ? `<div class="chat-message-text">${se(i.content)}</div>` : ""}
|
|
1369
|
+
${e ? `<div class="chat-streaming"><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span><span class="chat-streaming-dot"></span><span class="loading-text">${l(o)}</span></div>` : ""}
|
|
1274
1370
|
</div>
|
|
1275
1371
|
<div class="chat-message-metadata">
|
|
1276
|
-
<span class="chat-message-time">${
|
|
1372
|
+
<span class="chat-message-time">${l(J(i.timestamp, this.translations))}</span>
|
|
1277
1373
|
</div>
|
|
1278
1374
|
</div>
|
|
1279
1375
|
</div>
|
|
@@ -1291,12 +1387,15 @@ class N {
|
|
|
1291
1387
|
* Set streaming state
|
|
1292
1388
|
*/
|
|
1293
1389
|
setStreamingState(i) {
|
|
1294
|
-
this.isStreaming = i, this.inputElement && (this.inputElement.disabled = i), this.sendButton && (this.sendButton.disabled = i, this.sendButton.innerHTML = i ? '<div class="loading"></div>' :
|
|
1390
|
+
this.isStreaming = i, this.inputElement && (this.inputElement.disabled = i), this.sendButton && (this.sendButton.disabled = i, this.sendButton.innerHTML = i ? '<div class="loading"></div>' : `<span>${l(this.translations.sendButtonLabel)}</span>`), i ? this.startLoadingMessages() : this.clearLoadingMessages();
|
|
1295
1391
|
}
|
|
1296
1392
|
startLoadingMessages() {
|
|
1297
|
-
this.
|
|
1298
|
-
|
|
1299
|
-
|
|
1393
|
+
this.clearLoadingMessages();
|
|
1394
|
+
const i = this.translations.loadingMessages;
|
|
1395
|
+
this.loadingMessageIndex = Math.floor(Math.random() * i.length), this.loadingMessageInterval = setInterval(() => {
|
|
1396
|
+
const e = this.translations.loadingMessages;
|
|
1397
|
+
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length, this.isStreaming && this.renderMessages(!0);
|
|
1398
|
+
}, T);
|
|
1300
1399
|
}
|
|
1301
1400
|
clearLoadingMessages() {
|
|
1302
1401
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
@@ -1319,6 +1418,19 @@ class N {
|
|
|
1319
1418
|
setMessages(i) {
|
|
1320
1419
|
this.messages = [...i], this.renderMessages();
|
|
1321
1420
|
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Update the props (e.g. placeholder / translations) and re-render.
|
|
1423
|
+
*
|
|
1424
|
+
* Safe to call at any time: existing messages and streaming state are
|
|
1425
|
+
* preserved; listeners on the old DOM are removed and re-attached to the
|
|
1426
|
+
* newly rendered elements.
|
|
1427
|
+
*/
|
|
1428
|
+
setProps(i) {
|
|
1429
|
+
this.props = i, this.translations = d(i.translations), this.detachEventListeners(), this.render(), this.attachEventListeners(), this.renderMessages(this.isStreaming), this.isStreaming && this.setStreamingState(!0);
|
|
1430
|
+
}
|
|
1431
|
+
detachEventListeners() {
|
|
1432
|
+
this.inputElement && (this.handleInputResize && this.inputElement.removeEventListener("input", this.handleInputResize), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.sendButton && this.handleSendClick && this.sendButton.removeEventListener("click", this.handleSendClick), this.handleInputResize = null, this.handleInputKeydown = null, this.handleSendClick = null;
|
|
1433
|
+
}
|
|
1322
1434
|
/**
|
|
1323
1435
|
* Destroy and cleanup
|
|
1324
1436
|
*/
|
|
@@ -1326,8 +1438,8 @@ class N {
|
|
|
1326
1438
|
this.clearLoadingMessages(), this.isStreaming && this.client.cancelAllRequests(), this.inputElement && (this.handleInputResize && this.inputElement.removeEventListener("input", this.handleInputResize), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.sendButton && this.handleSendClick && this.sendButton.removeEventListener("click", this.handleSendClick), this.handleInputResize = null, this.handleInputKeydown = null, this.handleSendClick = null;
|
|
1327
1439
|
}
|
|
1328
1440
|
}
|
|
1329
|
-
const
|
|
1330
|
-
class
|
|
1441
|
+
const z = "chat-bubble-snippet";
|
|
1442
|
+
class re extends HTMLElement {
|
|
1331
1443
|
constructor() {
|
|
1332
1444
|
super();
|
|
1333
1445
|
a(this, "shadow");
|
|
@@ -1336,6 +1448,8 @@ class W extends HTMLElement {
|
|
|
1336
1448
|
a(this, "container", null);
|
|
1337
1449
|
a(this, "isExpanded", !1);
|
|
1338
1450
|
a(this, "isMinimized", !1);
|
|
1451
|
+
a(this, "translationsOverride", null);
|
|
1452
|
+
a(this, "resolvedTranslations", d(null));
|
|
1339
1453
|
// Event handler references for cleanup
|
|
1340
1454
|
a(this, "handleBubbleClick", null);
|
|
1341
1455
|
a(this, "handleCloseClick", null);
|
|
@@ -1344,23 +1458,59 @@ class W extends HTMLElement {
|
|
|
1344
1458
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
1345
1459
|
}
|
|
1346
1460
|
static get observedAttributes() {
|
|
1347
|
-
return ["api-url", "placeholder", "theme", "hide-branding"];
|
|
1461
|
+
return ["api-url", "placeholder", "theme", "hide-branding", "translations"];
|
|
1348
1462
|
}
|
|
1349
1463
|
connectedCallback() {
|
|
1350
|
-
this.render(), this.initializeClient(), this.dispatchEvent(v("ready", void 0));
|
|
1464
|
+
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.dispatchEvent(v("ready", void 0));
|
|
1351
1465
|
}
|
|
1352
1466
|
disconnectedCallback() {
|
|
1353
1467
|
this.cleanup();
|
|
1354
1468
|
}
|
|
1355
1469
|
attributeChangedCallback(e, t, s) {
|
|
1356
|
-
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme"
|
|
1470
|
+
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerenderAfterTranslationsChange()));
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Get the current translations object.
|
|
1474
|
+
*/
|
|
1475
|
+
get translations() {
|
|
1476
|
+
return this.translationsOverride;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
1480
|
+
*/
|
|
1481
|
+
set translations(e) {
|
|
1482
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = d(this.translationsOverride), this.isConnected && this.rerenderAfterTranslationsChange();
|
|
1483
|
+
}
|
|
1484
|
+
syncTranslationsFromAttribute() {
|
|
1485
|
+
if (this.translationsOverride) {
|
|
1486
|
+
this.resolvedTranslations = d(this.translationsOverride);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
const e = k(
|
|
1490
|
+
this.getAttribute("translations"),
|
|
1491
|
+
"ChatBubbleSnippet"
|
|
1492
|
+
);
|
|
1493
|
+
this.resolvedTranslations = d(e);
|
|
1494
|
+
}
|
|
1495
|
+
rerenderAfterTranslationsChange() {
|
|
1496
|
+
const e = this.isExpanded, t = this.isMinimized;
|
|
1497
|
+
this.removeEventListeners();
|
|
1498
|
+
const s = this.chatView ? this.shadow.querySelector(".chat-content") : null;
|
|
1499
|
+
s?.parentNode && s.parentNode.removeChild(s), this.render(), e && (this.shadow.querySelector(".bubble-button")?.classList.add("hidden"), this.shadow.querySelector(".chat-window")?.classList.add("expanded"), t && this.shadow.querySelector(".chat-window")?.classList.add("minimized"));
|
|
1500
|
+
const r = this.shadow.querySelector(".chat-window");
|
|
1501
|
+
if (this.chatView && s && r) {
|
|
1502
|
+
const o = r.querySelector(".chat-content");
|
|
1503
|
+
o ? r.replaceChild(s, o) : r.appendChild(s), this.chatView.setProps(this.getProps());
|
|
1504
|
+
} else e && this.initializeChatView();
|
|
1357
1505
|
}
|
|
1358
1506
|
getProps() {
|
|
1507
|
+
const e = this.resolvedTranslations;
|
|
1359
1508
|
return {
|
|
1360
|
-
apiUrl:
|
|
1361
|
-
placeholder:
|
|
1362
|
-
theme:
|
|
1363
|
-
hideBranding: m(this.getAttribute("hide-branding"), !1)
|
|
1509
|
+
apiUrl: p(this.getAttribute("api-url"), ""),
|
|
1510
|
+
placeholder: p(this.getAttribute("placeholder"), e.chatPlaceholder),
|
|
1511
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
1512
|
+
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
1513
|
+
translations: this.translationsOverride ?? void 0
|
|
1364
1514
|
};
|
|
1365
1515
|
}
|
|
1366
1516
|
initializeClient() {
|
|
@@ -1370,15 +1520,15 @@ class W extends HTMLElement {
|
|
|
1370
1520
|
return;
|
|
1371
1521
|
}
|
|
1372
1522
|
try {
|
|
1373
|
-
this.client =
|
|
1523
|
+
this.client = S(e.apiUrl);
|
|
1374
1524
|
} catch (t) {
|
|
1375
1525
|
console.error("ChatBubbleSnippet:", t);
|
|
1376
1526
|
}
|
|
1377
1527
|
}
|
|
1378
1528
|
render() {
|
|
1379
1529
|
const e = document.createElement("style");
|
|
1380
|
-
e.textContent = `${
|
|
1381
|
-
${
|
|
1530
|
+
e.textContent = `${M}
|
|
1531
|
+
${V}
|
|
1382
1532
|
${this.getBubbleStyles()}`, this.container = document.createElement("div"), this.container.className = "chat-bubble-widget", this.container.innerHTML = this.getBaseHTML(), this.shadow.innerHTML = "", this.shadow.appendChild(e), this.shadow.appendChild(this.container), this.attachEventListeners();
|
|
1383
1533
|
}
|
|
1384
1534
|
getBubbleStyles() {
|
|
@@ -1523,8 +1673,9 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1523
1673
|
`;
|
|
1524
1674
|
}
|
|
1525
1675
|
getBaseHTML() {
|
|
1676
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = e.hideBranding ? "" : `<div class="powered-by">${E}</div>`;
|
|
1526
1677
|
return `
|
|
1527
|
-
<button class="bubble-button" aria-label="
|
|
1678
|
+
<button class="bubble-button" aria-label="${l(t.openChatAriaLabel)}">
|
|
1528
1679
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1529
1680
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1530
1681
|
</svg>
|
|
@@ -1535,21 +1686,21 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1535
1686
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1536
1687
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1537
1688
|
</svg>
|
|
1538
|
-
<span
|
|
1689
|
+
<span>${l(t.chatTitle)}</span>
|
|
1539
1690
|
</div>
|
|
1540
1691
|
<div class="chat-header-actions">
|
|
1541
|
-
<button class="icon-button clear-button" aria-label="
|
|
1692
|
+
<button class="icon-button clear-button" aria-label="${l(t.clearHistoryAriaLabel)}">
|
|
1542
1693
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1543
1694
|
<polyline points="3 6 5 6 21 6"></polyline>
|
|
1544
1695
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
1545
1696
|
</svg>
|
|
1546
1697
|
</button>
|
|
1547
|
-
<button class="icon-button minimize-button" aria-label="
|
|
1698
|
+
<button class="icon-button minimize-button" aria-label="${l(t.minimizeAriaLabel)}">
|
|
1548
1699
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1549
1700
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
1550
1701
|
</svg>
|
|
1551
1702
|
</button>
|
|
1552
|
-
<button class="icon-button close-button" aria-label="
|
|
1703
|
+
<button class="icon-button close-button" aria-label="${l(t.closeAriaLabel)}">
|
|
1553
1704
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1554
1705
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
1555
1706
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
@@ -1558,7 +1709,7 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1558
1709
|
</div>
|
|
1559
1710
|
</div>
|
|
1560
1711
|
<div class="chat-content"></div>
|
|
1561
|
-
${
|
|
1712
|
+
${s}
|
|
1562
1713
|
</div>
|
|
1563
1714
|
`;
|
|
1564
1715
|
}
|
|
@@ -1590,15 +1741,16 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1590
1741
|
const e = this.shadow.querySelector(".chat-content");
|
|
1591
1742
|
if (!e) return;
|
|
1592
1743
|
if (!this.client) {
|
|
1744
|
+
const s = this.resolvedTranslations;
|
|
1593
1745
|
e.innerHTML = `
|
|
1594
1746
|
<div style="padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);">
|
|
1595
|
-
<strong
|
|
1747
|
+
<strong>${l(s.errorPrefix)}</strong> ${l(s.missingApiUrlError)}
|
|
1596
1748
|
</div>
|
|
1597
1749
|
`;
|
|
1598
1750
|
return;
|
|
1599
1751
|
}
|
|
1600
1752
|
const t = this.getProps();
|
|
1601
|
-
this.chatView = new
|
|
1753
|
+
this.chatView = new K(e, this.client, t);
|
|
1602
1754
|
}
|
|
1603
1755
|
updateTheme(e) {
|
|
1604
1756
|
(e === "light" || e === "dark" ? e : null) === null && this.hasAttribute("theme") && this.getAttribute("theme") !== "auto" && this.removeAttribute("theme");
|
|
@@ -1617,9 +1769,9 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1617
1769
|
return this.chatView?.getMessages() || [];
|
|
1618
1770
|
}
|
|
1619
1771
|
}
|
|
1620
|
-
customElements.get(
|
|
1621
|
-
const
|
|
1622
|
-
class
|
|
1772
|
+
customElements.get(z) || customElements.define(z, re);
|
|
1773
|
+
const $ = "chat-page-snippet", R = "chat-page-sessions";
|
|
1774
|
+
class ae extends HTMLElement {
|
|
1623
1775
|
constructor() {
|
|
1624
1776
|
super();
|
|
1625
1777
|
a(this, "shadow");
|
|
@@ -1629,6 +1781,8 @@ class X extends HTMLElement {
|
|
|
1629
1781
|
a(this, "sessions", []);
|
|
1630
1782
|
a(this, "currentSessionId", null);
|
|
1631
1783
|
a(this, "sidebarCollapsed", !1);
|
|
1784
|
+
a(this, "translationsOverride", null);
|
|
1785
|
+
a(this, "resolvedTranslations", d(null));
|
|
1632
1786
|
// Event handler references for cleanup
|
|
1633
1787
|
a(this, "handleClearClick", null);
|
|
1634
1788
|
a(this, "handleNewChatClick", null);
|
|
@@ -1638,23 +1792,70 @@ class X extends HTMLElement {
|
|
|
1638
1792
|
this.shadow = this.attachShadow({ mode: "open" }), this.loadSessions();
|
|
1639
1793
|
}
|
|
1640
1794
|
static get observedAttributes() {
|
|
1641
|
-
return ["api-url", "placeholder", "theme", "hide-branding"];
|
|
1795
|
+
return ["api-url", "placeholder", "theme", "hide-branding", "translations"];
|
|
1642
1796
|
}
|
|
1643
1797
|
connectedCallback() {
|
|
1644
|
-
this.render(), this.initializeClient(), this.setupView(), this.dispatchEvent(v("ready", void 0));
|
|
1798
|
+
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.setupView(), this.dispatchEvent(v("ready", void 0));
|
|
1645
1799
|
}
|
|
1646
1800
|
disconnectedCallback() {
|
|
1647
1801
|
this.saveCurrentSession(), this.cleanup();
|
|
1648
1802
|
}
|
|
1649
1803
|
attributeChangedCallback(e, t, s) {
|
|
1650
|
-
t !== s && (e === "api-url" ? (this.initializeClient(), this.setupView()) : e === "theme"
|
|
1804
|
+
t !== s && (e === "api-url" ? (this.initializeClient(), this.setupView()) : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerenderAfterTranslationsChange()));
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Get the current translations object.
|
|
1808
|
+
*/
|
|
1809
|
+
get translations() {
|
|
1810
|
+
return this.translationsOverride;
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
1814
|
+
*/
|
|
1815
|
+
set translations(e) {
|
|
1816
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = d(this.translationsOverride), this.refreshDefaultSessionTitles(), this.isConnected && this.rerenderAfterTranslationsChange();
|
|
1817
|
+
}
|
|
1818
|
+
syncTranslationsFromAttribute() {
|
|
1819
|
+
if (this.translationsOverride) {
|
|
1820
|
+
this.resolvedTranslations = d(this.translationsOverride), this.refreshDefaultSessionTitles();
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
const e = k(this.getAttribute("translations"), "ChatPageSnippet");
|
|
1824
|
+
this.resolvedTranslations = d(e), this.refreshDefaultSessionTitles();
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Replace the stored title of any still-default-titled session with the
|
|
1828
|
+
* current `newChatButton` translation, so the sidebar reflects the active
|
|
1829
|
+
* language after a translations change.
|
|
1830
|
+
*/
|
|
1831
|
+
refreshDefaultSessionTitles() {
|
|
1832
|
+
if (this.sessions.length === 0) return;
|
|
1833
|
+
const e = this.resolvedTranslations.newChatButton;
|
|
1834
|
+
let t = !1;
|
|
1835
|
+
for (const s of this.sessions)
|
|
1836
|
+
s.titleIsDefault && s.title !== e && (s.title = e, t = !0);
|
|
1837
|
+
t && this.saveSessions();
|
|
1838
|
+
}
|
|
1839
|
+
rerenderAfterTranslationsChange() {
|
|
1840
|
+
this.removeEventListeners();
|
|
1841
|
+
const e = this.chatView ? this.shadow.querySelector(".container") : null;
|
|
1842
|
+
e?.parentNode && e.parentNode.removeChild(e), this.render(), this.attachEventListeners(), this.renderChatList();
|
|
1843
|
+
const t = this.shadow.querySelector(".chat-page-content");
|
|
1844
|
+
if (this.chatView && e && t) {
|
|
1845
|
+
const s = t.querySelector(".container");
|
|
1846
|
+
s ? t.replaceChild(e, s) : t.appendChild(e), this.chatView.setProps(this.getProps()), this.handleMessageEvent = () => {
|
|
1847
|
+
this.saveCurrentSession(), this.updateSessionTitle(), this.renderChatList();
|
|
1848
|
+
}, e.addEventListener("message", this.handleMessageEvent);
|
|
1849
|
+
}
|
|
1651
1850
|
}
|
|
1652
1851
|
getProps() {
|
|
1852
|
+
const e = this.resolvedTranslations;
|
|
1653
1853
|
return {
|
|
1654
|
-
apiUrl:
|
|
1655
|
-
placeholder:
|
|
1656
|
-
theme:
|
|
1657
|
-
hideBranding: m(this.getAttribute("hide-branding"), !1)
|
|
1854
|
+
apiUrl: p(this.getAttribute("api-url"), ""),
|
|
1855
|
+
placeholder: p(this.getAttribute("placeholder"), e.chatPlaceholder),
|
|
1856
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
1857
|
+
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
1858
|
+
translations: this.translationsOverride ?? void 0
|
|
1658
1859
|
};
|
|
1659
1860
|
}
|
|
1660
1861
|
initializeClient() {
|
|
@@ -1664,15 +1865,15 @@ class X extends HTMLElement {
|
|
|
1664
1865
|
return;
|
|
1665
1866
|
}
|
|
1666
1867
|
try {
|
|
1667
|
-
this.client =
|
|
1868
|
+
this.client = S(e.apiUrl);
|
|
1668
1869
|
} catch (t) {
|
|
1669
1870
|
console.error("ChatPageSnippet:", t);
|
|
1670
1871
|
}
|
|
1671
1872
|
}
|
|
1672
1873
|
render() {
|
|
1673
1874
|
const e = document.createElement("style");
|
|
1674
|
-
e.textContent = `${
|
|
1675
|
-
${
|
|
1875
|
+
e.textContent = `${M}
|
|
1876
|
+
${V}
|
|
1676
1877
|
${this.getPageStyles()}`, this.container = document.createElement("div"), this.container.className = "chat-page-container", this.container.innerHTML = this.getBaseHTML(), this.shadow.innerHTML = "", this.shadow.appendChild(e), this.shadow.appendChild(this.container), this.attachEventListeners();
|
|
1677
1878
|
}
|
|
1678
1879
|
getPageStyles() {
|
|
@@ -1952,24 +2153,25 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1952
2153
|
`;
|
|
1953
2154
|
}
|
|
1954
2155
|
getBaseHTML() {
|
|
2156
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = e.hideBranding ? "" : `<div class="powered-by">${E}</div>`;
|
|
1955
2157
|
return `
|
|
1956
2158
|
<div class="chat-sidebar">
|
|
1957
2159
|
<div class="sidebar-header">
|
|
1958
|
-
<span class="sidebar-title"
|
|
2160
|
+
<span class="sidebar-title">${l(t.historyTitle)}</span>
|
|
1959
2161
|
</div>
|
|
1960
2162
|
<button class="new-chat-button">
|
|
1961
2163
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1962
2164
|
<path d="M12 5v14M5 12h14"></path>
|
|
1963
2165
|
</svg>
|
|
1964
|
-
|
|
2166
|
+
${l(t.newChatButton)}
|
|
1965
2167
|
</button>
|
|
1966
2168
|
<div class="chat-list"></div>
|
|
1967
|
-
${
|
|
2169
|
+
${s}
|
|
1968
2170
|
</div>
|
|
1969
2171
|
<div class="chat-main">
|
|
1970
2172
|
<div class="chat-page-header">
|
|
1971
2173
|
<div class="chat-page-header-left">
|
|
1972
|
-
<button class="toggle-sidebar-button" title="
|
|
2174
|
+
<button class="toggle-sidebar-button" title="${l(t.toggleSidebarTitle)}">
|
|
1973
2175
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1974
2176
|
<path d="M3 12h18M3 6h18M3 18h18"></path>
|
|
1975
2177
|
</svg>
|
|
@@ -1978,7 +2180,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1978
2180
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1979
2181
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
1980
2182
|
</svg>
|
|
1981
|
-
<span
|
|
2183
|
+
<span>${l(t.chatTitle)}</span>
|
|
1982
2184
|
</div>
|
|
1983
2185
|
</div>
|
|
1984
2186
|
<div class="chat-page-header-actions">
|
|
@@ -1986,7 +2188,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1986
2188
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1987
2189
|
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
1988
2190
|
</svg>
|
|
1989
|
-
|
|
2191
|
+
${l(t.clearChatButton)}
|
|
1990
2192
|
</button>
|
|
1991
2193
|
</div>
|
|
1992
2194
|
</div>
|
|
@@ -2007,16 +2209,19 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2007
2209
|
setupView() {
|
|
2008
2210
|
const e = this.shadow.querySelector(".container");
|
|
2009
2211
|
if (!this.client) {
|
|
2010
|
-
|
|
2212
|
+
if (e) {
|
|
2213
|
+
const s = this.resolvedTranslations;
|
|
2214
|
+
e.innerHTML = `
|
|
2011
2215
|
<div style="padding: 16px; color: var(--search-snippet-error-color, #ef4444); font-family: var(--search-snippet-font-family, sans-serif); font-size: var(--search-snippet-font-size-base, 14px);">
|
|
2012
|
-
<strong
|
|
2216
|
+
<strong>${l(s.errorPrefix)}</strong> ${l(s.missingApiUrlError)}
|
|
2013
2217
|
</div>
|
|
2014
|
-
|
|
2218
|
+
`;
|
|
2219
|
+
}
|
|
2015
2220
|
return;
|
|
2016
2221
|
}
|
|
2017
2222
|
if (!e) return;
|
|
2018
2223
|
const t = this.getProps();
|
|
2019
|
-
if (this.chatView = new
|
|
2224
|
+
if (this.chatView = new K(e, this.client, t), this.sessions.length === 0)
|
|
2020
2225
|
this.createNewChat();
|
|
2021
2226
|
else {
|
|
2022
2227
|
const s = this.sessions[0];
|
|
@@ -2031,7 +2236,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2031
2236
|
}
|
|
2032
2237
|
loadSessions() {
|
|
2033
2238
|
try {
|
|
2034
|
-
const e = localStorage.getItem(
|
|
2239
|
+
const e = localStorage.getItem(R);
|
|
2035
2240
|
e && (this.sessions = JSON.parse(e), this.sessions.sort((t, s) => s.updatedAt - t.updatedAt));
|
|
2036
2241
|
} catch (e) {
|
|
2037
2242
|
console.error("Failed to load chat sessions:", e);
|
|
@@ -2039,7 +2244,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2039
2244
|
}
|
|
2040
2245
|
saveSessions() {
|
|
2041
2246
|
try {
|
|
2042
|
-
localStorage.setItem(
|
|
2247
|
+
localStorage.setItem(R, JSON.stringify(this.sessions));
|
|
2043
2248
|
} catch (e) {
|
|
2044
2249
|
console.error("Failed to save chat sessions:", e);
|
|
2045
2250
|
}
|
|
@@ -2051,20 +2256,20 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2051
2256
|
}
|
|
2052
2257
|
updateSessionTitle() {
|
|
2053
2258
|
if (!this.currentSessionId) return;
|
|
2054
|
-
const e = this.sessions.find((
|
|
2055
|
-
if (e
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
}
|
|
2259
|
+
const e = this.sessions.find((r) => r.id === this.currentSessionId);
|
|
2260
|
+
if (!e || e.messages.length === 0 || !(e.titleIsDefault ?? e.title === this.resolvedTranslations.newChatButton)) return;
|
|
2261
|
+
const s = e.messages.find((r) => r.role === "user");
|
|
2262
|
+
s && (e.title = s.content.slice(0, 50) + (s.content.length > 50 ? "..." : ""), e.titleIsDefault = !1, this.saveSessions());
|
|
2059
2263
|
}
|
|
2060
2264
|
createNewChat() {
|
|
2061
2265
|
this.saveCurrentSession();
|
|
2062
2266
|
const e = {
|
|
2063
2267
|
id: this.generateSessionId(),
|
|
2064
|
-
title:
|
|
2268
|
+
title: this.resolvedTranslations.newChatButton,
|
|
2065
2269
|
messages: [],
|
|
2066
2270
|
createdAt: Date.now(),
|
|
2067
|
-
updatedAt: Date.now()
|
|
2271
|
+
updatedAt: Date.now(),
|
|
2272
|
+
titleIsDefault: !0
|
|
2068
2273
|
};
|
|
2069
2274
|
this.sessions.unshift(e), this.currentSessionId = e.id, this.saveSessions(), this.chatView?.clearMessages(), this.renderChatList();
|
|
2070
2275
|
}
|
|
@@ -2081,7 +2286,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2081
2286
|
clearCurrentChat() {
|
|
2082
2287
|
if (!this.currentSessionId) return;
|
|
2083
2288
|
const e = this.sessions.find((t) => t.id === this.currentSessionId);
|
|
2084
|
-
e && (e.messages = [], e.title =
|
|
2289
|
+
e && (e.messages = [], e.title = this.resolvedTranslations.newChatButton, e.titleIsDefault = !0, e.updatedAt = Date.now(), this.saveSessions()), this.chatView?.clearMessages(), this.renderChatList();
|
|
2085
2290
|
}
|
|
2086
2291
|
toggleSidebar() {
|
|
2087
2292
|
this.sidebarCollapsed = !this.sidebarCollapsed, this.shadow.querySelector(".chat-sidebar")?.classList.toggle("collapsed", this.sidebarCollapsed);
|
|
@@ -2102,23 +2307,23 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2102
2307
|
}
|
|
2103
2308
|
renderChatList() {
|
|
2104
2309
|
const e = this.shadow.querySelector(".chat-list");
|
|
2105
|
-
if (e)
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
e.innerHTML = this.sessions.map((t) => this.renderChatListItem(t)).join("");
|
|
2310
|
+
if (!e) return;
|
|
2311
|
+
const t = this.resolvedTranslations;
|
|
2312
|
+
if (this.sessions.length === 0) {
|
|
2313
|
+
e.innerHTML = `<div class="chat-list-empty">${this.escapeHTML(t.noChatsYet)}</div>`;
|
|
2314
|
+
return;
|
|
2111
2315
|
}
|
|
2316
|
+
e.innerHTML = this.sessions.map((s) => this.renderChatListItem(s)).join("");
|
|
2112
2317
|
}
|
|
2113
2318
|
renderChatListItem(e) {
|
|
2114
|
-
const t = e.id === this.currentSessionId, s = this.formatDate(e.updatedAt);
|
|
2319
|
+
const t = e.id === this.currentSessionId, s = this.formatDate(e.updatedAt), r = this.resolvedTranslations.deleteChatTitle;
|
|
2115
2320
|
return `
|
|
2116
2321
|
<div class="chat-list-item ${t ? "active" : ""}" data-session-id="${e.id}">
|
|
2117
2322
|
<div class="chat-list-item-content">
|
|
2118
2323
|
<div class="chat-list-item-title">${this.escapeHTML(e.title)}</div>
|
|
2119
2324
|
<div class="chat-list-item-date">${s}</div>
|
|
2120
2325
|
</div>
|
|
2121
|
-
<button class="chat-list-item-delete" data-session-id="${e.id}" title="
|
|
2326
|
+
<button class="chat-list-item-delete" data-session-id="${e.id}" title="${this.escapeHTML(r)}">
|
|
2122
2327
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2123
2328
|
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
2124
2329
|
</svg>
|
|
@@ -2128,7 +2333,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2128
2333
|
}
|
|
2129
2334
|
formatDate(e) {
|
|
2130
2335
|
const t = new Date(e), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), o = Math.floor(r / (1e3 * 60 * 60 * 24));
|
|
2131
|
-
return o === 0 ? t.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" }) : o === 1 ?
|
|
2336
|
+
return o === 0 ? t.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" }) : o === 1 ? this.resolvedTranslations.yesterday : o < 7 ? t.toLocaleDateString(void 0, { weekday: "long" }) : t.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
2132
2337
|
}
|
|
2133
2338
|
escapeHTML(e) {
|
|
2134
2339
|
const t = document.createElement("div");
|
|
@@ -2157,8 +2362,8 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2157
2362
|
return this.sessions.find((e) => e.id === this.currentSessionId) || null;
|
|
2158
2363
|
}
|
|
2159
2364
|
}
|
|
2160
|
-
customElements.get(
|
|
2161
|
-
const
|
|
2365
|
+
customElements.get($) || customElements.define($, ae);
|
|
2366
|
+
const ne = `
|
|
2162
2367
|
/* Search view states */
|
|
2163
2368
|
.search-view {
|
|
2164
2369
|
transition: var(--search-snippet-transition-slow);
|
|
@@ -2528,8 +2733,8 @@ a.search-result-item:focus-visible {
|
|
|
2528
2733
|
border-radius: 2px;
|
|
2529
2734
|
font-weight: var(--search-snippet-font-weight-medium);
|
|
2530
2735
|
}
|
|
2531
|
-
`,
|
|
2532
|
-
class
|
|
2736
|
+
`, q = "search-bar-snippet", B = 10, H = 50;
|
|
2737
|
+
class oe extends HTMLElement {
|
|
2533
2738
|
constructor() {
|
|
2534
2739
|
super();
|
|
2535
2740
|
a(this, "shadow");
|
|
@@ -2542,6 +2747,8 @@ class te extends HTMLElement {
|
|
|
2542
2747
|
a(this, "currentSearchController", null);
|
|
2543
2748
|
a(this, "loadingMessageInterval", null);
|
|
2544
2749
|
a(this, "loadingMessageIndex", 0);
|
|
2750
|
+
a(this, "translationsOverride", null);
|
|
2751
|
+
a(this, "resolvedTranslations", d(null));
|
|
2545
2752
|
// Event handler references for cleanup
|
|
2546
2753
|
a(this, "handleInputChange", null);
|
|
2547
2754
|
a(this, "handleInputKeydownEnter", null);
|
|
@@ -2554,6 +2761,7 @@ class te extends HTMLElement {
|
|
|
2554
2761
|
"api-url",
|
|
2555
2762
|
"placeholder",
|
|
2556
2763
|
"max-results",
|
|
2764
|
+
"max-render-results",
|
|
2557
2765
|
"debounce-ms",
|
|
2558
2766
|
"theme",
|
|
2559
2767
|
"hide-branding",
|
|
@@ -2561,30 +2769,71 @@ class te extends HTMLElement {
|
|
|
2561
2769
|
"show-date",
|
|
2562
2770
|
"hide-thumbnails",
|
|
2563
2771
|
"see-more",
|
|
2564
|
-
"request-options"
|
|
2772
|
+
"request-options",
|
|
2773
|
+
"translations"
|
|
2565
2774
|
];
|
|
2566
2775
|
}
|
|
2567
2776
|
connectedCallback() {
|
|
2568
|
-
this.initializeClient(), this.render(), this.dispatchEvent(v("ready", void 0));
|
|
2777
|
+
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.dispatchEvent(v("ready", void 0));
|
|
2569
2778
|
}
|
|
2570
2779
|
disconnectedCallback() {
|
|
2571
2780
|
this.cleanup();
|
|
2572
2781
|
}
|
|
2573
2782
|
attributeChangedCallback(e, t, s) {
|
|
2574
|
-
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme"
|
|
2783
|
+
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Get the current translations object. Mirrors the property getter.
|
|
2787
|
+
*/
|
|
2788
|
+
get translations() {
|
|
2789
|
+
return this.translationsOverride;
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
2793
|
+
*/
|
|
2794
|
+
set translations(e) {
|
|
2795
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = d(this.translationsOverride), this.isConnected && this.rerender();
|
|
2796
|
+
}
|
|
2797
|
+
/**
|
|
2798
|
+
* Re-render preserving the current query and re-running the search so
|
|
2799
|
+
* results remain visible after a translations change at runtime.
|
|
2800
|
+
*/
|
|
2801
|
+
rerender() {
|
|
2802
|
+
const e = this.inputElement?.value ?? "";
|
|
2803
|
+
this.render(), e && this.inputElement && (this.inputElement.value = e, e.trim().length > 0 && this.performSearch(e.trim()));
|
|
2804
|
+
}
|
|
2805
|
+
syncTranslationsFromAttribute() {
|
|
2806
|
+
if (this.translationsOverride) {
|
|
2807
|
+
this.resolvedTranslations = d(this.translationsOverride);
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
const e = k(
|
|
2811
|
+
this.getAttribute("translations"),
|
|
2812
|
+
"SearchBarSnippet"
|
|
2813
|
+
);
|
|
2814
|
+
this.resolvedTranslations = d(e);
|
|
2575
2815
|
}
|
|
2576
2816
|
getProps() {
|
|
2817
|
+
const e = this.resolvedTranslations;
|
|
2577
2818
|
return {
|
|
2578
|
-
apiUrl:
|
|
2579
|
-
placeholder:
|
|
2580
|
-
maxResults: y(
|
|
2819
|
+
apiUrl: p(this.getAttribute("api-url"), ""),
|
|
2820
|
+
placeholder: p(this.getAttribute("placeholder"), e.placeholder),
|
|
2821
|
+
maxResults: y(
|
|
2822
|
+
this.getAttribute("max-results"),
|
|
2823
|
+
H
|
|
2824
|
+
),
|
|
2825
|
+
maxRenderResults: y(
|
|
2826
|
+
this.getAttribute("max-render-results"),
|
|
2827
|
+
B
|
|
2828
|
+
),
|
|
2581
2829
|
debounceMs: y(this.getAttribute("debounce-ms"), 300),
|
|
2582
|
-
theme:
|
|
2830
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
2583
2831
|
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
2584
2832
|
showUrl: m(this.getAttribute("show-url"), !1),
|
|
2585
2833
|
showDate: m(this.getAttribute("show-date"), !1),
|
|
2586
2834
|
hideThumbnails: m(this.getAttribute("hide-thumbnails"), !1),
|
|
2587
|
-
seeMore:
|
|
2835
|
+
seeMore: p(this.getAttribute("see-more"), ""),
|
|
2836
|
+
translations: this.translationsOverride ?? void 0
|
|
2588
2837
|
};
|
|
2589
2838
|
}
|
|
2590
2839
|
getRequestOptions() {
|
|
@@ -2607,20 +2856,20 @@ class te extends HTMLElement {
|
|
|
2607
2856
|
return;
|
|
2608
2857
|
}
|
|
2609
2858
|
try {
|
|
2610
|
-
this.client =
|
|
2859
|
+
this.client = S(e.apiUrl);
|
|
2611
2860
|
} catch (t) {
|
|
2612
2861
|
console.error("SearchBarSnippet:", t);
|
|
2613
2862
|
}
|
|
2614
2863
|
}
|
|
2615
2864
|
render() {
|
|
2616
|
-
const e = this.getProps(), t = (
|
|
2617
|
-
this.debouncedSearch =
|
|
2618
|
-
|
|
2865
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = (o) => this.performSearch(o);
|
|
2866
|
+
this.debouncedSearch = P(
|
|
2867
|
+
s,
|
|
2619
2868
|
e.debounceMs || 400
|
|
2620
2869
|
);
|
|
2621
|
-
const
|
|
2622
|
-
|
|
2623
|
-
${
|
|
2870
|
+
const r = document.createElement("style");
|
|
2871
|
+
r.textContent = `${M}
|
|
2872
|
+
${ne}`, this.container = document.createElement("div"), this.container.className = "container", this.container.innerHTML = `
|
|
2624
2873
|
<div class="search-view">
|
|
2625
2874
|
<div class="search-input-wrapper">
|
|
2626
2875
|
<svg xmlns="http://www.w3.org/2000/svg" class="search-icon" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
|
|
@@ -2628,12 +2877,12 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2628
2877
|
type="text"
|
|
2629
2878
|
name="search-input"
|
|
2630
2879
|
class="search-input"
|
|
2631
|
-
placeholder="${
|
|
2632
|
-
aria-label="
|
|
2880
|
+
placeholder="${l(e.placeholder || t.placeholder)}"
|
|
2881
|
+
aria-label="${l(t.searchInputAriaLabel)}"
|
|
2633
2882
|
autocomplete="off"
|
|
2634
2883
|
/>
|
|
2635
|
-
<button class="button search-submit-button" aria-label="
|
|
2636
|
-
<span
|
|
2884
|
+
<button class="button search-submit-button" aria-label="${l(t.searchButtonLabel)}">
|
|
2885
|
+
<span>${l(t.searchButtonLabel)}</span>
|
|
2637
2886
|
</button>
|
|
2638
2887
|
</div>
|
|
2639
2888
|
<div class="search-content">
|
|
@@ -2642,7 +2891,7 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2642
2891
|
</div>
|
|
2643
2892
|
</div>
|
|
2644
2893
|
</div>
|
|
2645
|
-
`, this.shadow.innerHTML = "", this.shadow.appendChild(
|
|
2894
|
+
`, this.shadow.innerHTML = "", this.shadow.appendChild(r), this.shadow.appendChild(this.container), this.inputElement = this.container.querySelector(".search-input"), this.resultsContainer = this.container.querySelector(".search-results-wrapper"), this.searchButton = this.container.querySelector(".search-submit-button"), this.attachEventListeners(), this.client || this.showMissingApiUrlError();
|
|
2646
2895
|
}
|
|
2647
2896
|
attachEventListeners() {
|
|
2648
2897
|
this.inputElement && (this.handleInputChange = (e) => {
|
|
@@ -2667,12 +2916,12 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2667
2916
|
}
|
|
2668
2917
|
this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.currentSearchController = new AbortController(), this.showLoadingState();
|
|
2669
2918
|
try {
|
|
2670
|
-
const t = await this.client.search(e, {
|
|
2919
|
+
const t = this.getProps(), s = await this.client.search(e, {
|
|
2671
2920
|
signal: this.currentSearchController.signal,
|
|
2672
|
-
maxResults:
|
|
2921
|
+
maxResults: t.maxResults || H,
|
|
2673
2922
|
request: this.getRequestOptions()
|
|
2674
|
-
}),
|
|
2675
|
-
this.displayResults(r, e,
|
|
2923
|
+
}), r = s.slice(0, t.maxRenderResults || B);
|
|
2924
|
+
this.displayResults(r, e, s.length);
|
|
2676
2925
|
} catch (t) {
|
|
2677
2926
|
if (t.name === "AbortError")
|
|
2678
2927
|
return;
|
|
@@ -2687,37 +2936,39 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2687
2936
|
this.showNoResultsState(t);
|
|
2688
2937
|
return;
|
|
2689
2938
|
}
|
|
2690
|
-
const r = this.getProps(), o = r.hideBranding ? "" : `<div class="powered-by-inline">${
|
|
2691
|
-
|
|
2692
|
-
|
|
2939
|
+
const r = this.getProps(), o = this.resolvedTranslations, c = r.hideBranding ? "" : `<div class="powered-by-inline">${E}</div>`, h = s > e.length, u = h ? b(o.resultsCountOverflow, { n: e.length, total: s }) : b(s === 1 ? o.resultsCount : o.resultsCountPlural, {
|
|
2940
|
+
n: s
|
|
2941
|
+
}), f = r.seeMore && h ? `<div class="search-footer">
|
|
2942
|
+
<a href="${l(r.seeMore + encodeURIComponent(t))}" class="search-see-more">
|
|
2943
|
+
<span>${l(o.seeMoreResults)}</span>
|
|
2693
2944
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
|
2694
2945
|
</a>
|
|
2695
|
-
</div>` : "",
|
|
2946
|
+
</div>` : "", g = `
|
|
2696
2947
|
<div class="search-header">
|
|
2697
2948
|
<div class="search-count">
|
|
2698
|
-
${l}
|
|
2949
|
+
${l(u)}
|
|
2699
2950
|
</div>
|
|
2700
|
-
${
|
|
2951
|
+
${c}
|
|
2701
2952
|
</div>
|
|
2702
2953
|
<div class="search-results">
|
|
2703
|
-
${e.map((
|
|
2954
|
+
${e.map((w) => this.renderResult(w)).join("")}
|
|
2704
2955
|
</div>
|
|
2705
|
-
${
|
|
2956
|
+
${f}
|
|
2706
2957
|
`;
|
|
2707
|
-
this.resultsContainer.innerHTML =
|
|
2958
|
+
this.resultsContainer.innerHTML = g, this.attachResultHandlers();
|
|
2708
2959
|
}
|
|
2709
2960
|
renderResult(e) {
|
|
2710
|
-
const t = this.getProps(), s = t.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), r = e.url ?
|
|
2961
|
+
const t = this.getProps(), s = t.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), r = e.url ? l(e.url) : "#", o = e.url ? l(U(e.url)) : "", c = t.showDate && e.timestamp !== void 0 ? `<div class="search-result-date">${l(j(e.timestamp))}</div>` : "", h = t.showUrl && e.url || c ? `<div class="search-result-metadata">
|
|
2711
2962
|
${t.showUrl && e.url ? `<span class="search-result-url">${o}</span>` : '<span class="search-result-url search-result-url-empty"></span>'}
|
|
2712
2963
|
${c}
|
|
2713
2964
|
</div>` : "";
|
|
2714
2965
|
return `
|
|
2715
|
-
<a href="${r}" class="search-result-item" data-result-id="${
|
|
2966
|
+
<a href="${r}" class="search-result-item" data-result-id="${l(e.url || "")}">
|
|
2716
2967
|
${s}
|
|
2717
2968
|
<div class="search-result-content">
|
|
2718
|
-
<div class="search-result-title">${
|
|
2719
|
-
<div class="search-result-snippet">${
|
|
2720
|
-
${
|
|
2969
|
+
<div class="search-result-title">${l(e.title || "")}</div>
|
|
2970
|
+
<div class="search-result-snippet">${l(e.description || "")}</div>
|
|
2971
|
+
${h}
|
|
2721
2972
|
</div>
|
|
2722
2973
|
</a>
|
|
2723
2974
|
`;
|
|
@@ -2730,8 +2981,8 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2730
2981
|
<div class="search-result-image-placeholder" style="display: none;">${s}</div>
|
|
2731
2982
|
<img
|
|
2732
2983
|
class="search-result-image"
|
|
2733
|
-
src="${
|
|
2734
|
-
alt="${
|
|
2984
|
+
src="${l(e)}"
|
|
2985
|
+
alt="${l(t)}"
|
|
2735
2986
|
loading="lazy"
|
|
2736
2987
|
/>
|
|
2737
2988
|
</div>
|
|
@@ -2762,60 +3013,72 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2762
3013
|
});
|
|
2763
3014
|
}
|
|
2764
3015
|
showLoadingState() {
|
|
2765
|
-
|
|
3016
|
+
if (!this.resultsContainer) return;
|
|
3017
|
+
this.clearLoadingInterval();
|
|
3018
|
+
const e = this.resolvedTranslations.loadingMessages;
|
|
3019
|
+
this.loadingMessageIndex = Math.floor(Math.random() * e.length);
|
|
3020
|
+
const t = this.resolvedTranslations;
|
|
3021
|
+
this.resultsContainer.innerHTML = `
|
|
2766
3022
|
<div class="search-loading">
|
|
2767
|
-
<div class="loading" aria-label="
|
|
2768
|
-
<div class="loading-text loading-text-animate">${
|
|
3023
|
+
<div class="loading" aria-label="${l(t.loadingAriaLabel)}"></div>
|
|
3024
|
+
<div class="loading-text loading-text-animate">${l(e[this.loadingMessageIndex])}</div>
|
|
2769
3025
|
</div>
|
|
2770
|
-
`, this.startLoadingInterval()
|
|
3026
|
+
`, this.startLoadingInterval();
|
|
2771
3027
|
}
|
|
2772
3028
|
startLoadingInterval() {
|
|
2773
3029
|
this.loadingMessageInterval = setInterval(() => {
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3030
|
+
const e = this.resolvedTranslations.loadingMessages;
|
|
3031
|
+
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length;
|
|
3032
|
+
const t = this.resultsContainer?.querySelector(".loading-text");
|
|
3033
|
+
t && (t.classList.remove("loading-text-animate"), t.offsetWidth, t.textContent = e[this.loadingMessageIndex], t.classList.add("loading-text-animate"));
|
|
3034
|
+
}, T);
|
|
2778
3035
|
}
|
|
2779
3036
|
clearLoadingInterval() {
|
|
2780
3037
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
2781
3038
|
}
|
|
2782
3039
|
showEmptyState() {
|
|
2783
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3040
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3041
|
+
const e = this.resolvedTranslations;
|
|
3042
|
+
this.resultsContainer.innerHTML = `
|
|
2784
3043
|
<div class="search-empty">
|
|
2785
3044
|
<svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2786
3045
|
<circle cx="11" cy="11" r="8"></circle>
|
|
2787
3046
|
<path d="m21 21-4.35-4.35"></path>
|
|
2788
3047
|
</svg>
|
|
2789
|
-
<div class="search-empty-title"
|
|
3048
|
+
<div class="search-empty-title">${l(e.emptyStateTitle)}</div>
|
|
2790
3049
|
<div class="search-empty-description">
|
|
2791
|
-
|
|
3050
|
+
${l(e.emptyStateDescription)}
|
|
2792
3051
|
</div>
|
|
2793
3052
|
</div>
|
|
2794
|
-
|
|
3053
|
+
`;
|
|
2795
3054
|
}
|
|
2796
3055
|
showNoResultsState(e) {
|
|
2797
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3056
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3057
|
+
const t = this.resolvedTranslations;
|
|
3058
|
+
this.resultsContainer.innerHTML = `
|
|
2798
3059
|
<div class="search-empty">
|
|
2799
3060
|
<svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2800
3061
|
<circle cx="11" cy="11" r="8"></circle>
|
|
2801
3062
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
2802
3063
|
</svg>
|
|
2803
|
-
<div class="search-empty-title"
|
|
3064
|
+
<div class="search-empty-title">${l(t.noResultsTitle)}</div>
|
|
2804
3065
|
<div class="search-empty-description">
|
|
2805
|
-
|
|
3066
|
+
${l(b(t.noResultsDescription, { query: e }))}
|
|
2806
3067
|
</div>
|
|
2807
3068
|
</div>
|
|
2808
|
-
|
|
3069
|
+
`;
|
|
2809
3070
|
}
|
|
2810
3071
|
showErrorState(e) {
|
|
2811
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3072
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3073
|
+
const t = this.resolvedTranslations;
|
|
3074
|
+
this.resultsContainer.innerHTML = `
|
|
2812
3075
|
<div class="error">
|
|
2813
|
-
<strong
|
|
3076
|
+
<strong>${l(t.errorPrefix)}</strong> ${l(e)}
|
|
2814
3077
|
</div>
|
|
2815
|
-
|
|
3078
|
+
`;
|
|
2816
3079
|
}
|
|
2817
3080
|
showMissingApiUrlError() {
|
|
2818
|
-
this.resultsContainer && this.showErrorState(
|
|
3081
|
+
this.resultsContainer && this.showErrorState(this.resolvedTranslations.missingApiUrlError);
|
|
2819
3082
|
}
|
|
2820
3083
|
updateTheme(e) {
|
|
2821
3084
|
const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
|
|
@@ -2829,8 +3092,8 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2829
3092
|
await this.performSearch(e);
|
|
2830
3093
|
}
|
|
2831
3094
|
}
|
|
2832
|
-
customElements.get(
|
|
2833
|
-
const
|
|
3095
|
+
customElements.get(q) || customElements.define(q, oe);
|
|
3096
|
+
const le = `
|
|
2834
3097
|
/* Modal backdrop */
|
|
2835
3098
|
.modal-backdrop {
|
|
2836
3099
|
position: fixed;
|
|
@@ -3267,8 +3530,8 @@ a.modal-result-item:focus-visible {
|
|
|
3267
3530
|
.modal-container.open {
|
|
3268
3531
|
animation: modal-slide-in var(--search-snippet-transition) ease-out;
|
|
3269
3532
|
}
|
|
3270
|
-
`,
|
|
3271
|
-
class
|
|
3533
|
+
`, O = "search-modal-snippet", D = 10, N = 50;
|
|
3534
|
+
class ce extends HTMLElement {
|
|
3272
3535
|
constructor() {
|
|
3273
3536
|
super();
|
|
3274
3537
|
a(this, "shadow");
|
|
@@ -3285,6 +3548,8 @@ class ae extends HTMLElement {
|
|
|
3285
3548
|
a(this, "currentSearchController", null);
|
|
3286
3549
|
a(this, "loadingMessageInterval", null);
|
|
3287
3550
|
a(this, "loadingMessageIndex", 0);
|
|
3551
|
+
a(this, "translationsOverride", null);
|
|
3552
|
+
a(this, "resolvedTranslations", d(null));
|
|
3288
3553
|
// Event handler references for cleanup
|
|
3289
3554
|
a(this, "handleGlobalKeydown", null);
|
|
3290
3555
|
a(this, "handleInputChange", null);
|
|
@@ -3300,6 +3565,7 @@ class ae extends HTMLElement {
|
|
|
3300
3565
|
"api-url",
|
|
3301
3566
|
"placeholder",
|
|
3302
3567
|
"max-results",
|
|
3568
|
+
"max-render-results",
|
|
3303
3569
|
"theme",
|
|
3304
3570
|
"shortcut",
|
|
3305
3571
|
"use-meta-key",
|
|
@@ -3309,32 +3575,79 @@ class ae extends HTMLElement {
|
|
|
3309
3575
|
"show-date",
|
|
3310
3576
|
"hide-thumbnails",
|
|
3311
3577
|
"see-more",
|
|
3312
|
-
"request-options"
|
|
3578
|
+
"request-options",
|
|
3579
|
+
"translations"
|
|
3313
3580
|
];
|
|
3314
3581
|
}
|
|
3315
3582
|
connectedCallback() {
|
|
3316
|
-
this.initializeClient(), this.render(), this.attachGlobalKeyboardShortcut(), this.dispatchEvent(v("ready", void 0));
|
|
3583
|
+
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.attachGlobalKeyboardShortcut(), this.dispatchEvent(v("ready", void 0));
|
|
3317
3584
|
}
|
|
3318
3585
|
disconnectedCallback() {
|
|
3319
3586
|
this.cleanup();
|
|
3320
3587
|
}
|
|
3321
3588
|
attributeChangedCallback(e, t, s) {
|
|
3322
|
-
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme"
|
|
3589
|
+
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
3590
|
+
}
|
|
3591
|
+
/**
|
|
3592
|
+
* Get the current translations object.
|
|
3593
|
+
*/
|
|
3594
|
+
get translations() {
|
|
3595
|
+
return this.translationsOverride;
|
|
3596
|
+
}
|
|
3597
|
+
/**
|
|
3598
|
+
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
3599
|
+
*/
|
|
3600
|
+
set translations(e) {
|
|
3601
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = d(this.translationsOverride), this.isConnected && this.rerender();
|
|
3602
|
+
}
|
|
3603
|
+
/**
|
|
3604
|
+
* Re-render while preserving open state and the current query. Results are
|
|
3605
|
+
* re-fetched so the list reflects the updated translation strings around
|
|
3606
|
+
* them (counts, footer hints, etc.). Selection resets to none — the same
|
|
3607
|
+
* behavior as the immediate post-search state.
|
|
3608
|
+
*/
|
|
3609
|
+
rerender() {
|
|
3610
|
+
const e = this.isOpen, t = this.inputElement?.value ?? "";
|
|
3611
|
+
this.isOpen = !1, this.render(), e && (this.isOpen = !0, this.backdrop?.classList.add("open"), this.modal?.classList.add("open"), requestAnimationFrame(() => {
|
|
3612
|
+
requestAnimationFrame(() => {
|
|
3613
|
+
this.inputElement?.focus();
|
|
3614
|
+
});
|
|
3615
|
+
})), t && this.inputElement && (this.inputElement.value = t, t.trim().length > 0 && this.performSearch(t.trim()));
|
|
3616
|
+
}
|
|
3617
|
+
syncTranslationsFromAttribute() {
|
|
3618
|
+
if (this.translationsOverride) {
|
|
3619
|
+
this.resolvedTranslations = d(this.translationsOverride);
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
const e = k(
|
|
3623
|
+
this.getAttribute("translations"),
|
|
3624
|
+
"SearchModalSnippet"
|
|
3625
|
+
);
|
|
3626
|
+
this.resolvedTranslations = d(e);
|
|
3323
3627
|
}
|
|
3324
3628
|
getProps() {
|
|
3629
|
+
const e = this.resolvedTranslations;
|
|
3325
3630
|
return {
|
|
3326
|
-
apiUrl:
|
|
3327
|
-
placeholder:
|
|
3328
|
-
maxResults: y(
|
|
3631
|
+
apiUrl: p(this.getAttribute("api-url"), ""),
|
|
3632
|
+
placeholder: p(this.getAttribute("placeholder"), e.placeholder),
|
|
3633
|
+
maxResults: y(
|
|
3634
|
+
this.getAttribute("max-results"),
|
|
3635
|
+
N
|
|
3636
|
+
),
|
|
3637
|
+
maxRenderResults: y(
|
|
3638
|
+
this.getAttribute("max-render-results"),
|
|
3639
|
+
D
|
|
3640
|
+
),
|
|
3329
3641
|
debounceMs: y(this.getAttribute("debounce-ms"), 300),
|
|
3330
|
-
theme:
|
|
3331
|
-
shortcut:
|
|
3642
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
3643
|
+
shortcut: p(this.getAttribute("shortcut"), "k"),
|
|
3332
3644
|
useMetaKey: this.getAttribute("use-meta-key") !== "false",
|
|
3333
3645
|
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
3334
3646
|
showUrl: m(this.getAttribute("show-url"), !1),
|
|
3335
3647
|
showDate: m(this.getAttribute("show-date"), !1),
|
|
3336
3648
|
hideThumbnails: m(this.getAttribute("hide-thumbnails"), !1),
|
|
3337
|
-
seeMore:
|
|
3649
|
+
seeMore: p(this.getAttribute("see-more"), ""),
|
|
3650
|
+
translations: this.translationsOverride ?? void 0
|
|
3338
3651
|
};
|
|
3339
3652
|
}
|
|
3340
3653
|
getRequestOptions() {
|
|
@@ -3357,22 +3670,22 @@ class ae extends HTMLElement {
|
|
|
3357
3670
|
return;
|
|
3358
3671
|
}
|
|
3359
3672
|
try {
|
|
3360
|
-
this.client =
|
|
3673
|
+
this.client = S(e.apiUrl);
|
|
3361
3674
|
} catch (t) {
|
|
3362
3675
|
console.error("SearchModalSnippet:", t);
|
|
3363
3676
|
}
|
|
3364
3677
|
}
|
|
3365
3678
|
render() {
|
|
3366
|
-
const e = this.getProps(), t = (
|
|
3367
|
-
this.debouncedSearch =
|
|
3368
|
-
|
|
3679
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = (h) => this.performSearch(h);
|
|
3680
|
+
this.debouncedSearch = P(
|
|
3681
|
+
s,
|
|
3369
3682
|
e.debounceMs || 300
|
|
3370
3683
|
);
|
|
3371
|
-
const
|
|
3372
|
-
|
|
3373
|
-
${
|
|
3374
|
-
const
|
|
3375
|
-
|
|
3684
|
+
const r = document.createElement("style");
|
|
3685
|
+
r.textContent = `${M}
|
|
3686
|
+
${le}`;
|
|
3687
|
+
const o = e.hideBranding ? "" : `<div class="powered-by-inline">${E}</div>`, c = document.createElement("div");
|
|
3688
|
+
c.innerHTML = `
|
|
3376
3689
|
<div class="modal-backdrop" role="presentation"></div>
|
|
3377
3690
|
<div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
3378
3691
|
<div class="modal-header">
|
|
@@ -3382,8 +3695,8 @@ ${se}`;
|
|
|
3382
3695
|
<input
|
|
3383
3696
|
type="text"
|
|
3384
3697
|
class="modal-search-input"
|
|
3385
|
-
placeholder="${
|
|
3386
|
-
aria-label="
|
|
3698
|
+
placeholder="${l(e.placeholder || t.placeholder)}"
|
|
3699
|
+
aria-label="${l(t.searchButtonLabel)}"
|
|
3387
3700
|
aria-autocomplete="list"
|
|
3388
3701
|
aria-controls="modal-results-list"
|
|
3389
3702
|
aria-expanded="false"
|
|
@@ -3392,7 +3705,7 @@ ${se}`;
|
|
|
3392
3705
|
/>
|
|
3393
3706
|
</div>
|
|
3394
3707
|
<div class="modal-content">
|
|
3395
|
-
<div class="modal-results" id="modal-results-list" role="listbox" aria-label="
|
|
3708
|
+
<div class="modal-results" id="modal-results-list" role="listbox" aria-label="${l(t.searchResultsAriaLabel)}">
|
|
3396
3709
|
${this.renderEmptyState()}
|
|
3397
3710
|
</div>
|
|
3398
3711
|
</div>
|
|
@@ -3401,21 +3714,21 @@ ${se}`;
|
|
|
3401
3714
|
<div class="modal-footer-hint">
|
|
3402
3715
|
<kbd class="modal-kbd">↑</kbd>
|
|
3403
3716
|
<kbd class="modal-kbd">↓</kbd>
|
|
3404
|
-
<span
|
|
3717
|
+
<span>${l(t.navigateHint)}</span>
|
|
3405
3718
|
</div>
|
|
3406
3719
|
<div class="modal-footer-hint">
|
|
3407
3720
|
<kbd class="modal-kbd">↵</kbd>
|
|
3408
|
-
<span
|
|
3721
|
+
<span>${l(t.selectHint)}</span>
|
|
3409
3722
|
</div>
|
|
3410
3723
|
<div class="modal-footer-hint">
|
|
3411
3724
|
<kbd class="modal-kbd">Esc</kbd>
|
|
3412
|
-
<span
|
|
3725
|
+
<span>${l(t.closeHint)}</span>
|
|
3413
3726
|
</div>
|
|
3414
3727
|
</div>
|
|
3415
|
-
${
|
|
3728
|
+
${o}
|
|
3416
3729
|
</div>
|
|
3417
3730
|
</div>
|
|
3418
|
-
`, this.shadow.innerHTML = "", this.shadow.appendChild(
|
|
3731
|
+
`, this.shadow.innerHTML = "", this.shadow.appendChild(r), this.shadow.appendChild(c), this.backdrop = this.shadow.querySelector(".modal-backdrop"), this.modal = this.shadow.querySelector(".modal-container"), this.inputElement = this.shadow.querySelector(".modal-search-input"), this.resultsContainer = this.shadow.querySelector(".modal-results"), this.footerCount = this.shadow.querySelector(".modal-results-count"), this.attachEventListeners(), this.client || this.showMissingApiUrlError();
|
|
3419
3732
|
}
|
|
3420
3733
|
attachGlobalKeyboardShortcut() {
|
|
3421
3734
|
const e = this.getProps(), t = e.shortcut?.toLowerCase() || "k";
|
|
@@ -3482,12 +3795,12 @@ ${se}`;
|
|
|
3482
3795
|
}
|
|
3483
3796
|
this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.currentSearchController = new AbortController(), this.showLoadingState();
|
|
3484
3797
|
try {
|
|
3485
|
-
const t = await this.client.search(e, {
|
|
3798
|
+
const t = this.getProps(), s = await this.client.search(e, {
|
|
3486
3799
|
signal: this.currentSearchController.signal,
|
|
3487
|
-
maxResults:
|
|
3800
|
+
maxResults: t.maxResults || N,
|
|
3488
3801
|
request: this.getRequestOptions()
|
|
3489
|
-
})
|
|
3490
|
-
this.results =
|
|
3802
|
+
});
|
|
3803
|
+
this.results = s.slice(0, t.maxRenderResults || D), this.activeIndex = this.results.length > 0 ? 0 : -1, this.displayResults(this.results, e, s.length);
|
|
3491
3804
|
} catch (t) {
|
|
3492
3805
|
if (t.name === "AbortError")
|
|
3493
3806
|
return;
|
|
@@ -3502,16 +3815,18 @@ ${se}`;
|
|
|
3502
3815
|
this.showNoResultsState(t);
|
|
3503
3816
|
return;
|
|
3504
3817
|
}
|
|
3505
|
-
const r = this.getProps(), o = e.map((
|
|
3506
|
-
|
|
3818
|
+
const r = this.getProps(), o = this.resolvedTranslations, c = e.map((g, w) => this.renderResult(g, w)).join(""), h = s > e.length, u = h ? b(o.resultsCountOverflow, { n: e.length, total: s }) : b(s === 1 ? o.modalResultsCount : o.modalResultsCountPlural, {
|
|
3819
|
+
n: s
|
|
3820
|
+
}), f = r.seeMore && h ? `<a href="${l(r.seeMore + encodeURIComponent(t))}" class="modal-see-more">
|
|
3821
|
+
<span>${l(o.seeMoreResults)}</span>
|
|
3507
3822
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
|
3508
3823
|
</a>` : "";
|
|
3509
|
-
this.resultsContainer.innerHTML =
|
|
3824
|
+
this.resultsContainer.innerHTML = c + f, this.footerCount && (this.footerCount.textContent = u), this.inputElement && this.inputElement.setAttribute("aria-expanded", "true"), this.attachResultHandlers(), this.updateActiveResult();
|
|
3510
3825
|
}
|
|
3511
3826
|
renderResult(e, t) {
|
|
3512
|
-
const s = this.getProps(), r = s.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), o = e.url ?
|
|
3827
|
+
const s = this.getProps(), r = s.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), o = e.url ? l(e.url) : "#", c = e.url ? l(U(e.url)) : "", h = s.showDate && e.timestamp !== void 0 ? `<div class="modal-result-date">${l(j(e.timestamp))}</div>` : "", u = s.showUrl && e.url || h ? `<div class="modal-result-metadata">
|
|
3513
3828
|
${s.showUrl && e.url ? `<span class="modal-result-url">${c}</span>` : '<span class="modal-result-url modal-result-url-empty"></span>'}
|
|
3514
|
-
${
|
|
3829
|
+
${h}
|
|
3515
3830
|
</div>` : "";
|
|
3516
3831
|
return `
|
|
3517
3832
|
<a
|
|
@@ -3522,13 +3837,13 @@ ${se}`;
|
|
|
3522
3837
|
aria-selected="${t === this.activeIndex}"
|
|
3523
3838
|
tabindex="-1"
|
|
3524
3839
|
data-index="${t}"
|
|
3525
|
-
data-url="${
|
|
3840
|
+
data-url="${l(e.url || "")}"
|
|
3526
3841
|
>
|
|
3527
3842
|
${r}
|
|
3528
3843
|
<div class="modal-result-content">
|
|
3529
|
-
<div class="modal-result-title">${
|
|
3530
|
-
${e.description ? `<div class="modal-result-description">${
|
|
3531
|
-
${
|
|
3844
|
+
<div class="modal-result-title">${l(e.title || "")}</div>
|
|
3845
|
+
${e.description ? `<div class="modal-result-description">${l(e.description)}</div>` : ""}
|
|
3846
|
+
${u}
|
|
3532
3847
|
</div>
|
|
3533
3848
|
</a>
|
|
3534
3849
|
`;
|
|
@@ -3541,8 +3856,8 @@ ${se}`;
|
|
|
3541
3856
|
<div class="modal-result-image-placeholder" style="display: none;">${s}</div>
|
|
3542
3857
|
<img
|
|
3543
3858
|
class="modal-result-image"
|
|
3544
|
-
src="${
|
|
3545
|
-
alt="${
|
|
3859
|
+
src="${l(e)}"
|
|
3860
|
+
alt="${l(t)}"
|
|
3546
3861
|
loading="lazy"
|
|
3547
3862
|
/>
|
|
3548
3863
|
</div>
|
|
@@ -3575,13 +3890,14 @@ ${se}`;
|
|
|
3575
3890
|
});
|
|
3576
3891
|
}
|
|
3577
3892
|
renderEmptyState() {
|
|
3893
|
+
const e = this.resolvedTranslations;
|
|
3578
3894
|
return `
|
|
3579
3895
|
<div class="modal-empty">
|
|
3580
3896
|
<svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3581
3897
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3582
3898
|
<path d="m21 21-4.35-4.35"></path>
|
|
3583
3899
|
</svg>
|
|
3584
|
-
<div class="modal-empty-description"
|
|
3900
|
+
<div class="modal-empty-description">${l(e.modalEmptyStateDescription)}</div>
|
|
3585
3901
|
</div>
|
|
3586
3902
|
`;
|
|
3587
3903
|
}
|
|
@@ -3589,44 +3905,52 @@ ${se}`;
|
|
|
3589
3905
|
this.clearLoadingInterval(), this.resultsContainer && (this.resultsContainer.innerHTML = this.renderEmptyState(), this.footerCount && (this.footerCount.textContent = ""), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false"));
|
|
3590
3906
|
}
|
|
3591
3907
|
showLoadingState() {
|
|
3592
|
-
|
|
3908
|
+
if (!this.resultsContainer) return;
|
|
3909
|
+
this.clearLoadingInterval();
|
|
3910
|
+
const e = this.resolvedTranslations.loadingMessages, t = this.resolvedTranslations;
|
|
3911
|
+
this.loadingMessageIndex = Math.floor(Math.random() * e.length), this.resultsContainer.innerHTML = `
|
|
3593
3912
|
<div class="modal-loading">
|
|
3594
|
-
<div class="loading" aria-label="
|
|
3595
|
-
<div class="loading-text loading-text-animate">${
|
|
3913
|
+
<div class="loading" aria-label="${l(t.loadingAriaLabel)}"></div>
|
|
3914
|
+
<div class="loading-text loading-text-animate">${l(e[this.loadingMessageIndex])}</div>
|
|
3596
3915
|
</div>
|
|
3597
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3916
|
+
`, this.footerCount && (this.footerCount.textContent = e[this.loadingMessageIndex]), this.startLoadingInterval();
|
|
3598
3917
|
}
|
|
3599
3918
|
startLoadingInterval() {
|
|
3600
3919
|
this.loadingMessageInterval = setInterval(() => {
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3920
|
+
const e = this.resolvedTranslations.loadingMessages;
|
|
3921
|
+
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length;
|
|
3922
|
+
const t = this.resultsContainer?.querySelector(".loading-text");
|
|
3923
|
+
t && (t.classList.remove("loading-text-animate"), t.offsetWidth, t.textContent = e[this.loadingMessageIndex], t.classList.add("loading-text-animate")), this.footerCount && (this.footerCount.textContent = e[this.loadingMessageIndex]);
|
|
3924
|
+
}, T);
|
|
3605
3925
|
}
|
|
3606
3926
|
clearLoadingInterval() {
|
|
3607
3927
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
3608
3928
|
}
|
|
3609
3929
|
showNoResultsState(e) {
|
|
3610
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3930
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3931
|
+
const t = this.resolvedTranslations;
|
|
3932
|
+
this.resultsContainer.innerHTML = `
|
|
3611
3933
|
<div class="modal-empty">
|
|
3612
3934
|
<svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3613
3935
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3614
3936
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
3615
3937
|
</svg>
|
|
3616
|
-
<div class="modal-empty-title"
|
|
3617
|
-
<div class="modal-empty-description"
|
|
3938
|
+
<div class="modal-empty-title">${l(t.modalNoResultsTitle)}</div>
|
|
3939
|
+
<div class="modal-empty-description">${l(b(t.modalNoResultsDescription, { query: e }))}</div>
|
|
3618
3940
|
</div>
|
|
3619
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3941
|
+
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountZero), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false");
|
|
3620
3942
|
}
|
|
3621
3943
|
showErrorState(e) {
|
|
3622
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3944
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3945
|
+
const t = this.resolvedTranslations;
|
|
3946
|
+
this.resultsContainer.innerHTML = `
|
|
3623
3947
|
<div class="error">
|
|
3624
|
-
<strong
|
|
3948
|
+
<strong>${l(t.errorPrefix)}</strong> ${l(e)}
|
|
3625
3949
|
</div>
|
|
3626
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3950
|
+
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountError);
|
|
3627
3951
|
}
|
|
3628
3952
|
showMissingApiUrlError() {
|
|
3629
|
-
this.resultsContainer && this.showErrorState(
|
|
3953
|
+
this.resultsContainer && this.showErrorState(this.resolvedTranslations.missingApiUrlError);
|
|
3630
3954
|
}
|
|
3631
3955
|
updateTheme(e) {
|
|
3632
3956
|
const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
|
|
@@ -3692,13 +4016,15 @@ ${se}`;
|
|
|
3692
4016
|
return this.isOpen;
|
|
3693
4017
|
}
|
|
3694
4018
|
}
|
|
3695
|
-
customElements.get(
|
|
4019
|
+
customElements.get(O) || customElements.define(O, ce);
|
|
3696
4020
|
export {
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
4021
|
+
Q as AISearchClient,
|
|
4022
|
+
re as ChatBubbleSnippet,
|
|
4023
|
+
ae as ChatPageSnippet,
|
|
4024
|
+
L as DEFAULT_TRANSLATIONS,
|
|
4025
|
+
oe as SearchBarSnippet,
|
|
4026
|
+
ce as SearchModalSnippet,
|
|
4027
|
+
oe as default,
|
|
4028
|
+
d as mergeTranslations
|
|
3703
4029
|
};
|
|
3704
4030
|
//# sourceMappingURL=search-snippet.es.js.map
|