@cloudflare/ai-search-snippet 0.0.37 → 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 +650 -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,60 +177,60 @@ 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",
|
|
141
235
|
Accept: i.stream ? "text/event-stream" : "application/json",
|
|
142
236
|
"cf-ai-search-source": r
|
|
@@ -173,17 +267,17 @@ class K {
|
|
|
173
267
|
const c = await o.json();
|
|
174
268
|
if (c.success && c.result)
|
|
175
269
|
return c.result.chunks.map(
|
|
176
|
-
(
|
|
270
|
+
(h) => ({
|
|
177
271
|
type: "result",
|
|
178
|
-
id:
|
|
179
|
-
title:
|
|
180
|
-
description:
|
|
181
|
-
timestamp:
|
|
182
|
-
url:
|
|
183
|
-
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,
|
|
184
278
|
metadata: {
|
|
185
|
-
...
|
|
186
|
-
instance_id:
|
|
279
|
+
...h.item.metadata,
|
|
280
|
+
instance_id: h.instance_id
|
|
187
281
|
}
|
|
188
282
|
})
|
|
189
283
|
);
|
|
@@ -212,13 +306,13 @@ class K {
|
|
|
212
306
|
if (!o.body)
|
|
213
307
|
throw new Error("Response body is empty");
|
|
214
308
|
let c = "";
|
|
215
|
-
const
|
|
309
|
+
const h = o.body.getReader(), u = new TextDecoder();
|
|
216
310
|
for (; ; ) {
|
|
217
|
-
const { done:
|
|
218
|
-
if (
|
|
311
|
+
const { done: g, value: w } = await h.read();
|
|
312
|
+
if (g)
|
|
219
313
|
break;
|
|
220
|
-
const
|
|
221
|
-
c +=
|
|
314
|
+
const F = u.decode(w, { stream: !0 });
|
|
315
|
+
c += F;
|
|
222
316
|
}
|
|
223
317
|
yield {
|
|
224
318
|
type: "result",
|
|
@@ -226,7 +320,7 @@ class K {
|
|
|
226
320
|
title: "",
|
|
227
321
|
description: c.replaceAll("data: ", "").trim().split(`
|
|
228
322
|
|
|
229
|
-
`).map((
|
|
323
|
+
`).map((g) => JSON.parse(g)).map((g) => g.response).join(""),
|
|
230
324
|
url: "",
|
|
231
325
|
metadata: {}
|
|
232
326
|
};
|
|
@@ -286,10 +380,10 @@ class K {
|
|
|
286
380
|
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
287
381
|
}
|
|
288
382
|
}
|
|
289
|
-
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">
|
|
290
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"/>
|
|
291
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"/>
|
|
292
|
-
</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 = `
|
|
293
387
|
/* Chat container */
|
|
294
388
|
.chat-container {
|
|
295
389
|
display: flex;
|
|
@@ -606,7 +700,7 @@ const G = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w
|
|
|
606
700
|
.chat-message-bubble a:hover {
|
|
607
701
|
text-decoration: none;
|
|
608
702
|
}
|
|
609
|
-
`,
|
|
703
|
+
`, M = `
|
|
610
704
|
:host {
|
|
611
705
|
/* Colors - Light Mode */
|
|
612
706
|
--search-snippet-primary-color: #2563eb;
|
|
@@ -1041,17 +1135,17 @@ const G = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w
|
|
|
1041
1135
|
text-align: center;
|
|
1042
1136
|
}
|
|
1043
1137
|
`;
|
|
1044
|
-
function
|
|
1138
|
+
function se(n) {
|
|
1045
1139
|
let i = n;
|
|
1046
|
-
i =
|
|
1140
|
+
i = ie(i), i = i.replace(/```([\s\S]*?)```/g, (o, c) => `<pre><code>${c.trim()}</code></pre>`);
|
|
1047
1141
|
const e = i.split(`
|
|
1048
1142
|
`), t = [];
|
|
1049
1143
|
let s = !1, r = "";
|
|
1050
1144
|
for (let o = 0; o < e.length; o++) {
|
|
1051
|
-
const c = e[o],
|
|
1052
|
-
if (
|
|
1053
|
-
const
|
|
1054
|
-
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}>`);
|
|
1055
1149
|
continue;
|
|
1056
1150
|
}
|
|
1057
1151
|
if (c.match(/^---+$/)) {
|
|
@@ -1059,37 +1153,37 @@ function Y(n) {
|
|
|
1059
1153
|
continue;
|
|
1060
1154
|
}
|
|
1061
1155
|
if (c.match(/^>\s+/)) {
|
|
1062
|
-
const
|
|
1063
|
-
t.push(`<blockquote>${
|
|
1156
|
+
const g = c.replace(/^>\s+/, "");
|
|
1157
|
+
t.push(`<blockquote>${x(g)}</blockquote>`);
|
|
1064
1158
|
continue;
|
|
1065
1159
|
}
|
|
1066
|
-
const
|
|
1067
|
-
if (
|
|
1068
|
-
(!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>`);
|
|
1069
1163
|
continue;
|
|
1070
1164
|
}
|
|
1071
|
-
const
|
|
1072
|
-
if (
|
|
1073
|
-
(!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>`);
|
|
1074
1168
|
continue;
|
|
1075
1169
|
}
|
|
1076
1170
|
if (s && (t.push(`</${r}>`), s = !1, r = ""), c.trim() === "") {
|
|
1077
1171
|
t.push("<br />");
|
|
1078
1172
|
continue;
|
|
1079
1173
|
}
|
|
1080
|
-
t.push(`<p>${
|
|
1174
|
+
t.push(`<p>${x(c)}</p>`);
|
|
1081
1175
|
}
|
|
1082
1176
|
return s && t.push(`</${r}>`), t.join(`
|
|
1083
1177
|
`);
|
|
1084
1178
|
}
|
|
1085
|
-
function
|
|
1179
|
+
function x(n) {
|
|
1086
1180
|
let i = n;
|
|
1087
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(
|
|
1088
1182
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
1089
1183
|
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
1090
1184
|
), i;
|
|
1091
1185
|
}
|
|
1092
|
-
function
|
|
1186
|
+
function ie(n) {
|
|
1093
1187
|
const i = {
|
|
1094
1188
|
"&": "&",
|
|
1095
1189
|
"<": "<",
|
|
@@ -1099,11 +1193,12 @@ function J(n) {
|
|
|
1099
1193
|
};
|
|
1100
1194
|
return n.replace(/[&<>"']/g, (e) => i[e] || e);
|
|
1101
1195
|
}
|
|
1102
|
-
class
|
|
1196
|
+
class K {
|
|
1103
1197
|
constructor(i, e, t) {
|
|
1104
1198
|
a(this, "container");
|
|
1105
1199
|
a(this, "client");
|
|
1106
1200
|
a(this, "props");
|
|
1201
|
+
a(this, "translations");
|
|
1107
1202
|
a(this, "inputElement", null);
|
|
1108
1203
|
a(this, "messagesContainer", null);
|
|
1109
1204
|
a(this, "sendButton", null);
|
|
@@ -1116,42 +1211,49 @@ class N {
|
|
|
1116
1211
|
a(this, "handleInputResize", null);
|
|
1117
1212
|
a(this, "handleInputKeydown", null);
|
|
1118
1213
|
a(this, "handleSendClick", null);
|
|
1119
|
-
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();
|
|
1120
1215
|
}
|
|
1121
1216
|
/**
|
|
1122
1217
|
* Render the chat interface
|
|
1123
1218
|
*/
|
|
1124
1219
|
render() {
|
|
1220
|
+
const i = this.translations;
|
|
1125
1221
|
this.container.innerHTML = `
|
|
1126
1222
|
<div class="chat-container">
|
|
1127
1223
|
<div class="chat-messages">
|
|
1128
|
-
|
|
1129
|
-
<svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1130
|
-
<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>
|
|
1131
|
-
</svg>
|
|
1132
|
-
<div class="chat-empty-title">Start a Conversation</div>
|
|
1133
|
-
<div class="chat-empty-description">
|
|
1134
|
-
Send a message to begin chatting
|
|
1135
|
-
</div>
|
|
1136
|
-
</div>
|
|
1224
|
+
${this.renderEmptyStateHTML()}
|
|
1137
1225
|
</div>
|
|
1138
1226
|
<div class="chat-input-area">
|
|
1139
1227
|
<div class="chat-input-wrapper">
|
|
1140
1228
|
<textarea
|
|
1141
1229
|
class="chat-input"
|
|
1142
|
-
placeholder="${
|
|
1143
|
-
aria-label="
|
|
1230
|
+
placeholder="${l(this.props.placeholder || i.chatPlaceholder)}"
|
|
1231
|
+
aria-label="${l(i.chatInputAriaLabel)}"
|
|
1144
1232
|
style="height: 40px;"
|
|
1145
1233
|
rows="1"
|
|
1146
1234
|
></textarea>
|
|
1147
|
-
<button class="button chat-send-button" aria-label="
|
|
1148
|
-
<span
|
|
1235
|
+
<button class="button chat-send-button" aria-label="${l(i.sendButtonAriaLabel)}">
|
|
1236
|
+
<span>${l(i.sendButtonLabel)}</span>
|
|
1149
1237
|
</button>
|
|
1150
1238
|
</div>
|
|
1151
1239
|
</div>
|
|
1152
1240
|
</div>
|
|
1153
1241
|
`, this.messagesContainer = this.container.querySelector(".chat-messages"), this.inputElement = this.container.querySelector(".chat-input"), this.sendButton = this.container.querySelector(".chat-send-button");
|
|
1154
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
|
+
}
|
|
1155
1257
|
/**
|
|
1156
1258
|
* Attach event listeners
|
|
1157
1259
|
*/
|
|
@@ -1178,13 +1280,13 @@ class N {
|
|
|
1178
1280
|
*/
|
|
1179
1281
|
async sendMessage(i) {
|
|
1180
1282
|
const e = {
|
|
1181
|
-
id:
|
|
1283
|
+
id: I("msg"),
|
|
1182
1284
|
role: "user",
|
|
1183
1285
|
content: i,
|
|
1184
1286
|
timestamp: Date.now()
|
|
1185
1287
|
};
|
|
1186
1288
|
this.addMessage(e), this.renderMessages(!0), this.setStreamingState(!0);
|
|
1187
|
-
const t =
|
|
1289
|
+
const t = I("msg"), s = {
|
|
1188
1290
|
id: t,
|
|
1189
1291
|
role: "assistant",
|
|
1190
1292
|
content: "",
|
|
@@ -1194,14 +1296,17 @@ class N {
|
|
|
1194
1296
|
try {
|
|
1195
1297
|
const r = this.client.chat(i);
|
|
1196
1298
|
let o = "";
|
|
1197
|
-
for await (const
|
|
1198
|
-
if (
|
|
1199
|
-
o +=
|
|
1200
|
-
else if (
|
|
1201
|
-
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
|
+
);
|
|
1202
1307
|
break;
|
|
1203
1308
|
}
|
|
1204
|
-
const c = this.messages.findIndex((
|
|
1309
|
+
const c = this.messages.findIndex((h) => h.id === t);
|
|
1205
1310
|
c !== -1 && (this.messages[c].content = o), this.container.dispatchEvent(v("message", { message: s }));
|
|
1206
1311
|
} catch (r) {
|
|
1207
1312
|
this.showErrorInMessage(t, r.message), this.container.dispatchEvent(
|
|
@@ -1234,7 +1339,7 @@ class N {
|
|
|
1234
1339
|
*/
|
|
1235
1340
|
showErrorInMessage(i, e) {
|
|
1236
1341
|
const t = this.messages.findIndex((s) => s.id === i);
|
|
1237
|
-
t !== -1 && (this.messages[t].content =
|
|
1342
|
+
t !== -1 && (this.messages[t].content = `${this.translations.errorPrefix} ${e}`, this.renderMessages());
|
|
1238
1343
|
}
|
|
1239
1344
|
/**
|
|
1240
1345
|
* Render all messages
|
|
@@ -1242,17 +1347,7 @@ class N {
|
|
|
1242
1347
|
renderMessages(i = !1) {
|
|
1243
1348
|
if (!this.messagesContainer) return;
|
|
1244
1349
|
if (this.messages.length === 0) {
|
|
1245
|
-
this.messagesContainer.innerHTML =
|
|
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">Start a Conversation</div>
|
|
1251
|
-
<div class="chat-empty-description">
|
|
1252
|
-
Send a message to begin chatting
|
|
1253
|
-
</div>
|
|
1254
|
-
</div>
|
|
1255
|
-
`;
|
|
1350
|
+
this.messagesContainer.innerHTML = this.renderEmptyStateHTML();
|
|
1256
1351
|
return;
|
|
1257
1352
|
}
|
|
1258
1353
|
const e = this.messages.map(
|
|
@@ -1264,17 +1359,17 @@ class N {
|
|
|
1264
1359
|
* Render a single message
|
|
1265
1360
|
*/
|
|
1266
1361
|
renderMessage(i, e = !1) {
|
|
1267
|
-
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] ?? "";
|
|
1268
1363
|
return `
|
|
1269
|
-
<div class="chat-message ${
|
|
1270
|
-
<div class="chat-message-avatar">${
|
|
1364
|
+
<div class="chat-message ${s}">
|
|
1365
|
+
<div class="chat-message-avatar">${l(r)}</div>
|
|
1271
1366
|
<div class="chat-message-content">
|
|
1272
1367
|
<div class="chat-message-bubble">
|
|
1273
|
-
${i.content ? `<div class="chat-message-text">${
|
|
1274
|
-
${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>` : ""}
|
|
1275
1370
|
</div>
|
|
1276
1371
|
<div class="chat-message-metadata">
|
|
1277
|
-
<span class="chat-message-time">${
|
|
1372
|
+
<span class="chat-message-time">${l(J(i.timestamp, this.translations))}</span>
|
|
1278
1373
|
</div>
|
|
1279
1374
|
</div>
|
|
1280
1375
|
</div>
|
|
@@ -1292,12 +1387,15 @@ class N {
|
|
|
1292
1387
|
* Set streaming state
|
|
1293
1388
|
*/
|
|
1294
1389
|
setStreamingState(i) {
|
|
1295
|
-
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();
|
|
1296
1391
|
}
|
|
1297
1392
|
startLoadingMessages() {
|
|
1298
|
-
this.
|
|
1299
|
-
|
|
1300
|
-
|
|
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);
|
|
1301
1399
|
}
|
|
1302
1400
|
clearLoadingMessages() {
|
|
1303
1401
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
@@ -1320,6 +1418,19 @@ class N {
|
|
|
1320
1418
|
setMessages(i) {
|
|
1321
1419
|
this.messages = [...i], this.renderMessages();
|
|
1322
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
|
+
}
|
|
1323
1434
|
/**
|
|
1324
1435
|
* Destroy and cleanup
|
|
1325
1436
|
*/
|
|
@@ -1327,8 +1438,8 @@ class N {
|
|
|
1327
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;
|
|
1328
1439
|
}
|
|
1329
1440
|
}
|
|
1330
|
-
const
|
|
1331
|
-
class
|
|
1441
|
+
const z = "chat-bubble-snippet";
|
|
1442
|
+
class re extends HTMLElement {
|
|
1332
1443
|
constructor() {
|
|
1333
1444
|
super();
|
|
1334
1445
|
a(this, "shadow");
|
|
@@ -1337,6 +1448,8 @@ class W extends HTMLElement {
|
|
|
1337
1448
|
a(this, "container", null);
|
|
1338
1449
|
a(this, "isExpanded", !1);
|
|
1339
1450
|
a(this, "isMinimized", !1);
|
|
1451
|
+
a(this, "translationsOverride", null);
|
|
1452
|
+
a(this, "resolvedTranslations", d(null));
|
|
1340
1453
|
// Event handler references for cleanup
|
|
1341
1454
|
a(this, "handleBubbleClick", null);
|
|
1342
1455
|
a(this, "handleCloseClick", null);
|
|
@@ -1345,23 +1458,59 @@ class W extends HTMLElement {
|
|
|
1345
1458
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
1346
1459
|
}
|
|
1347
1460
|
static get observedAttributes() {
|
|
1348
|
-
return ["api-url", "placeholder", "theme", "hide-branding"];
|
|
1461
|
+
return ["api-url", "placeholder", "theme", "hide-branding", "translations"];
|
|
1349
1462
|
}
|
|
1350
1463
|
connectedCallback() {
|
|
1351
|
-
this.render(), this.initializeClient(), this.dispatchEvent(v("ready", void 0));
|
|
1464
|
+
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.dispatchEvent(v("ready", void 0));
|
|
1352
1465
|
}
|
|
1353
1466
|
disconnectedCallback() {
|
|
1354
1467
|
this.cleanup();
|
|
1355
1468
|
}
|
|
1356
1469
|
attributeChangedCallback(e, t, s) {
|
|
1357
|
-
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();
|
|
1358
1505
|
}
|
|
1359
1506
|
getProps() {
|
|
1507
|
+
const e = this.resolvedTranslations;
|
|
1360
1508
|
return {
|
|
1361
|
-
apiUrl:
|
|
1362
|
-
placeholder:
|
|
1363
|
-
theme:
|
|
1364
|
-
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
|
|
1365
1514
|
};
|
|
1366
1515
|
}
|
|
1367
1516
|
initializeClient() {
|
|
@@ -1371,15 +1520,15 @@ class W extends HTMLElement {
|
|
|
1371
1520
|
return;
|
|
1372
1521
|
}
|
|
1373
1522
|
try {
|
|
1374
|
-
this.client =
|
|
1523
|
+
this.client = S(e.apiUrl);
|
|
1375
1524
|
} catch (t) {
|
|
1376
1525
|
console.error("ChatBubbleSnippet:", t);
|
|
1377
1526
|
}
|
|
1378
1527
|
}
|
|
1379
1528
|
render() {
|
|
1380
1529
|
const e = document.createElement("style");
|
|
1381
|
-
e.textContent = `${
|
|
1382
|
-
${
|
|
1530
|
+
e.textContent = `${M}
|
|
1531
|
+
${V}
|
|
1383
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();
|
|
1384
1533
|
}
|
|
1385
1534
|
getBubbleStyles() {
|
|
@@ -1524,8 +1673,9 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1524
1673
|
`;
|
|
1525
1674
|
}
|
|
1526
1675
|
getBaseHTML() {
|
|
1676
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = e.hideBranding ? "" : `<div class="powered-by">${E}</div>`;
|
|
1527
1677
|
return `
|
|
1528
|
-
<button class="bubble-button" aria-label="
|
|
1678
|
+
<button class="bubble-button" aria-label="${l(t.openChatAriaLabel)}">
|
|
1529
1679
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1530
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>
|
|
1531
1681
|
</svg>
|
|
@@ -1536,21 +1686,21 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1536
1686
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1537
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>
|
|
1538
1688
|
</svg>
|
|
1539
|
-
<span
|
|
1689
|
+
<span>${l(t.chatTitle)}</span>
|
|
1540
1690
|
</div>
|
|
1541
1691
|
<div class="chat-header-actions">
|
|
1542
|
-
<button class="icon-button clear-button" aria-label="
|
|
1692
|
+
<button class="icon-button clear-button" aria-label="${l(t.clearHistoryAriaLabel)}">
|
|
1543
1693
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1544
1694
|
<polyline points="3 6 5 6 21 6"></polyline>
|
|
1545
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>
|
|
1546
1696
|
</svg>
|
|
1547
1697
|
</button>
|
|
1548
|
-
<button class="icon-button minimize-button" aria-label="
|
|
1698
|
+
<button class="icon-button minimize-button" aria-label="${l(t.minimizeAriaLabel)}">
|
|
1549
1699
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1550
1700
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
1551
1701
|
</svg>
|
|
1552
1702
|
</button>
|
|
1553
|
-
<button class="icon-button close-button" aria-label="
|
|
1703
|
+
<button class="icon-button close-button" aria-label="${l(t.closeAriaLabel)}">
|
|
1554
1704
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1555
1705
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
1556
1706
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
@@ -1559,7 +1709,7 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1559
1709
|
</div>
|
|
1560
1710
|
</div>
|
|
1561
1711
|
<div class="chat-content"></div>
|
|
1562
|
-
${
|
|
1712
|
+
${s}
|
|
1563
1713
|
</div>
|
|
1564
1714
|
`;
|
|
1565
1715
|
}
|
|
@@ -1591,15 +1741,16 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1591
1741
|
const e = this.shadow.querySelector(".chat-content");
|
|
1592
1742
|
if (!e) return;
|
|
1593
1743
|
if (!this.client) {
|
|
1744
|
+
const s = this.resolvedTranslations;
|
|
1594
1745
|
e.innerHTML = `
|
|
1595
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);">
|
|
1596
|
-
<strong
|
|
1747
|
+
<strong>${l(s.errorPrefix)}</strong> ${l(s.missingApiUrlError)}
|
|
1597
1748
|
</div>
|
|
1598
1749
|
`;
|
|
1599
1750
|
return;
|
|
1600
1751
|
}
|
|
1601
1752
|
const t = this.getProps();
|
|
1602
|
-
this.chatView = new
|
|
1753
|
+
this.chatView = new K(e, this.client, t);
|
|
1603
1754
|
}
|
|
1604
1755
|
updateTheme(e) {
|
|
1605
1756
|
(e === "light" || e === "dark" ? e : null) === null && this.hasAttribute("theme") && this.getAttribute("theme") !== "auto" && this.removeAttribute("theme");
|
|
@@ -1618,9 +1769,9 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1618
1769
|
return this.chatView?.getMessages() || [];
|
|
1619
1770
|
}
|
|
1620
1771
|
}
|
|
1621
|
-
customElements.get(
|
|
1622
|
-
const
|
|
1623
|
-
class
|
|
1772
|
+
customElements.get(z) || customElements.define(z, re);
|
|
1773
|
+
const $ = "chat-page-snippet", R = "chat-page-sessions";
|
|
1774
|
+
class ae extends HTMLElement {
|
|
1624
1775
|
constructor() {
|
|
1625
1776
|
super();
|
|
1626
1777
|
a(this, "shadow");
|
|
@@ -1630,6 +1781,8 @@ class X extends HTMLElement {
|
|
|
1630
1781
|
a(this, "sessions", []);
|
|
1631
1782
|
a(this, "currentSessionId", null);
|
|
1632
1783
|
a(this, "sidebarCollapsed", !1);
|
|
1784
|
+
a(this, "translationsOverride", null);
|
|
1785
|
+
a(this, "resolvedTranslations", d(null));
|
|
1633
1786
|
// Event handler references for cleanup
|
|
1634
1787
|
a(this, "handleClearClick", null);
|
|
1635
1788
|
a(this, "handleNewChatClick", null);
|
|
@@ -1639,23 +1792,70 @@ class X extends HTMLElement {
|
|
|
1639
1792
|
this.shadow = this.attachShadow({ mode: "open" }), this.loadSessions();
|
|
1640
1793
|
}
|
|
1641
1794
|
static get observedAttributes() {
|
|
1642
|
-
return ["api-url", "placeholder", "theme", "hide-branding"];
|
|
1795
|
+
return ["api-url", "placeholder", "theme", "hide-branding", "translations"];
|
|
1643
1796
|
}
|
|
1644
1797
|
connectedCallback() {
|
|
1645
|
-
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));
|
|
1646
1799
|
}
|
|
1647
1800
|
disconnectedCallback() {
|
|
1648
1801
|
this.saveCurrentSession(), this.cleanup();
|
|
1649
1802
|
}
|
|
1650
1803
|
attributeChangedCallback(e, t, s) {
|
|
1651
|
-
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
|
+
}
|
|
1652
1850
|
}
|
|
1653
1851
|
getProps() {
|
|
1852
|
+
const e = this.resolvedTranslations;
|
|
1654
1853
|
return {
|
|
1655
|
-
apiUrl:
|
|
1656
|
-
placeholder:
|
|
1657
|
-
theme:
|
|
1658
|
-
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
|
|
1659
1859
|
};
|
|
1660
1860
|
}
|
|
1661
1861
|
initializeClient() {
|
|
@@ -1665,15 +1865,15 @@ class X extends HTMLElement {
|
|
|
1665
1865
|
return;
|
|
1666
1866
|
}
|
|
1667
1867
|
try {
|
|
1668
|
-
this.client =
|
|
1868
|
+
this.client = S(e.apiUrl);
|
|
1669
1869
|
} catch (t) {
|
|
1670
1870
|
console.error("ChatPageSnippet:", t);
|
|
1671
1871
|
}
|
|
1672
1872
|
}
|
|
1673
1873
|
render() {
|
|
1674
1874
|
const e = document.createElement("style");
|
|
1675
|
-
e.textContent = `${
|
|
1676
|
-
${
|
|
1875
|
+
e.textContent = `${M}
|
|
1876
|
+
${V}
|
|
1677
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();
|
|
1678
1878
|
}
|
|
1679
1879
|
getPageStyles() {
|
|
@@ -1953,24 +2153,25 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1953
2153
|
`;
|
|
1954
2154
|
}
|
|
1955
2155
|
getBaseHTML() {
|
|
2156
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = e.hideBranding ? "" : `<div class="powered-by">${E}</div>`;
|
|
1956
2157
|
return `
|
|
1957
2158
|
<div class="chat-sidebar">
|
|
1958
2159
|
<div class="sidebar-header">
|
|
1959
|
-
<span class="sidebar-title"
|
|
2160
|
+
<span class="sidebar-title">${l(t.historyTitle)}</span>
|
|
1960
2161
|
</div>
|
|
1961
2162
|
<button class="new-chat-button">
|
|
1962
2163
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1963
2164
|
<path d="M12 5v14M5 12h14"></path>
|
|
1964
2165
|
</svg>
|
|
1965
|
-
|
|
2166
|
+
${l(t.newChatButton)}
|
|
1966
2167
|
</button>
|
|
1967
2168
|
<div class="chat-list"></div>
|
|
1968
|
-
${
|
|
2169
|
+
${s}
|
|
1969
2170
|
</div>
|
|
1970
2171
|
<div class="chat-main">
|
|
1971
2172
|
<div class="chat-page-header">
|
|
1972
2173
|
<div class="chat-page-header-left">
|
|
1973
|
-
<button class="toggle-sidebar-button" title="
|
|
2174
|
+
<button class="toggle-sidebar-button" title="${l(t.toggleSidebarTitle)}">
|
|
1974
2175
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1975
2176
|
<path d="M3 12h18M3 6h18M3 18h18"></path>
|
|
1976
2177
|
</svg>
|
|
@@ -1979,7 +2180,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1979
2180
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1980
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>
|
|
1981
2182
|
</svg>
|
|
1982
|
-
<span
|
|
2183
|
+
<span>${l(t.chatTitle)}</span>
|
|
1983
2184
|
</div>
|
|
1984
2185
|
</div>
|
|
1985
2186
|
<div class="chat-page-header-actions">
|
|
@@ -1987,7 +2188,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
1987
2188
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1988
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>
|
|
1989
2190
|
</svg>
|
|
1990
|
-
|
|
2191
|
+
${l(t.clearChatButton)}
|
|
1991
2192
|
</button>
|
|
1992
2193
|
</div>
|
|
1993
2194
|
</div>
|
|
@@ -2008,16 +2209,19 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2008
2209
|
setupView() {
|
|
2009
2210
|
const e = this.shadow.querySelector(".container");
|
|
2010
2211
|
if (!this.client) {
|
|
2011
|
-
|
|
2212
|
+
if (e) {
|
|
2213
|
+
const s = this.resolvedTranslations;
|
|
2214
|
+
e.innerHTML = `
|
|
2012
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);">
|
|
2013
|
-
<strong
|
|
2216
|
+
<strong>${l(s.errorPrefix)}</strong> ${l(s.missingApiUrlError)}
|
|
2014
2217
|
</div>
|
|
2015
|
-
|
|
2218
|
+
`;
|
|
2219
|
+
}
|
|
2016
2220
|
return;
|
|
2017
2221
|
}
|
|
2018
2222
|
if (!e) return;
|
|
2019
2223
|
const t = this.getProps();
|
|
2020
|
-
if (this.chatView = new
|
|
2224
|
+
if (this.chatView = new K(e, this.client, t), this.sessions.length === 0)
|
|
2021
2225
|
this.createNewChat();
|
|
2022
2226
|
else {
|
|
2023
2227
|
const s = this.sessions[0];
|
|
@@ -2032,7 +2236,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2032
2236
|
}
|
|
2033
2237
|
loadSessions() {
|
|
2034
2238
|
try {
|
|
2035
|
-
const e = localStorage.getItem(
|
|
2239
|
+
const e = localStorage.getItem(R);
|
|
2036
2240
|
e && (this.sessions = JSON.parse(e), this.sessions.sort((t, s) => s.updatedAt - t.updatedAt));
|
|
2037
2241
|
} catch (e) {
|
|
2038
2242
|
console.error("Failed to load chat sessions:", e);
|
|
@@ -2040,7 +2244,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2040
2244
|
}
|
|
2041
2245
|
saveSessions() {
|
|
2042
2246
|
try {
|
|
2043
|
-
localStorage.setItem(
|
|
2247
|
+
localStorage.setItem(R, JSON.stringify(this.sessions));
|
|
2044
2248
|
} catch (e) {
|
|
2045
2249
|
console.error("Failed to save chat sessions:", e);
|
|
2046
2250
|
}
|
|
@@ -2052,20 +2256,20 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2052
2256
|
}
|
|
2053
2257
|
updateSessionTitle() {
|
|
2054
2258
|
if (!this.currentSessionId) return;
|
|
2055
|
-
const e = this.sessions.find((
|
|
2056
|
-
if (e
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
}
|
|
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());
|
|
2060
2263
|
}
|
|
2061
2264
|
createNewChat() {
|
|
2062
2265
|
this.saveCurrentSession();
|
|
2063
2266
|
const e = {
|
|
2064
2267
|
id: this.generateSessionId(),
|
|
2065
|
-
title:
|
|
2268
|
+
title: this.resolvedTranslations.newChatButton,
|
|
2066
2269
|
messages: [],
|
|
2067
2270
|
createdAt: Date.now(),
|
|
2068
|
-
updatedAt: Date.now()
|
|
2271
|
+
updatedAt: Date.now(),
|
|
2272
|
+
titleIsDefault: !0
|
|
2069
2273
|
};
|
|
2070
2274
|
this.sessions.unshift(e), this.currentSessionId = e.id, this.saveSessions(), this.chatView?.clearMessages(), this.renderChatList();
|
|
2071
2275
|
}
|
|
@@ -2082,7 +2286,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2082
2286
|
clearCurrentChat() {
|
|
2083
2287
|
if (!this.currentSessionId) return;
|
|
2084
2288
|
const e = this.sessions.find((t) => t.id === this.currentSessionId);
|
|
2085
|
-
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();
|
|
2086
2290
|
}
|
|
2087
2291
|
toggleSidebar() {
|
|
2088
2292
|
this.sidebarCollapsed = !this.sidebarCollapsed, this.shadow.querySelector(".chat-sidebar")?.classList.toggle("collapsed", this.sidebarCollapsed);
|
|
@@ -2103,23 +2307,23 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2103
2307
|
}
|
|
2104
2308
|
renderChatList() {
|
|
2105
2309
|
const e = this.shadow.querySelector(".chat-list");
|
|
2106
|
-
if (e)
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
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;
|
|
2112
2315
|
}
|
|
2316
|
+
e.innerHTML = this.sessions.map((s) => this.renderChatListItem(s)).join("");
|
|
2113
2317
|
}
|
|
2114
2318
|
renderChatListItem(e) {
|
|
2115
|
-
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;
|
|
2116
2320
|
return `
|
|
2117
2321
|
<div class="chat-list-item ${t ? "active" : ""}" data-session-id="${e.id}">
|
|
2118
2322
|
<div class="chat-list-item-content">
|
|
2119
2323
|
<div class="chat-list-item-title">${this.escapeHTML(e.title)}</div>
|
|
2120
2324
|
<div class="chat-list-item-date">${s}</div>
|
|
2121
2325
|
</div>
|
|
2122
|
-
<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)}">
|
|
2123
2327
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2124
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>
|
|
2125
2329
|
</svg>
|
|
@@ -2129,7 +2333,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2129
2333
|
}
|
|
2130
2334
|
formatDate(e) {
|
|
2131
2335
|
const t = new Date(e), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), o = Math.floor(r / (1e3 * 60 * 60 * 24));
|
|
2132
|
-
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" });
|
|
2133
2337
|
}
|
|
2134
2338
|
escapeHTML(e) {
|
|
2135
2339
|
const t = document.createElement("div");
|
|
@@ -2158,8 +2362,8 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2158
2362
|
return this.sessions.find((e) => e.id === this.currentSessionId) || null;
|
|
2159
2363
|
}
|
|
2160
2364
|
}
|
|
2161
|
-
customElements.get(
|
|
2162
|
-
const
|
|
2365
|
+
customElements.get($) || customElements.define($, ae);
|
|
2366
|
+
const ne = `
|
|
2163
2367
|
/* Search view states */
|
|
2164
2368
|
.search-view {
|
|
2165
2369
|
transition: var(--search-snippet-transition-slow);
|
|
@@ -2529,8 +2733,8 @@ a.search-result-item:focus-visible {
|
|
|
2529
2733
|
border-radius: 2px;
|
|
2530
2734
|
font-weight: var(--search-snippet-font-weight-medium);
|
|
2531
2735
|
}
|
|
2532
|
-
`,
|
|
2533
|
-
class
|
|
2736
|
+
`, q = "search-bar-snippet", B = 10, H = 50;
|
|
2737
|
+
class oe extends HTMLElement {
|
|
2534
2738
|
constructor() {
|
|
2535
2739
|
super();
|
|
2536
2740
|
a(this, "shadow");
|
|
@@ -2543,6 +2747,8 @@ class te extends HTMLElement {
|
|
|
2543
2747
|
a(this, "currentSearchController", null);
|
|
2544
2748
|
a(this, "loadingMessageInterval", null);
|
|
2545
2749
|
a(this, "loadingMessageIndex", 0);
|
|
2750
|
+
a(this, "translationsOverride", null);
|
|
2751
|
+
a(this, "resolvedTranslations", d(null));
|
|
2546
2752
|
// Event handler references for cleanup
|
|
2547
2753
|
a(this, "handleInputChange", null);
|
|
2548
2754
|
a(this, "handleInputKeydownEnter", null);
|
|
@@ -2555,6 +2761,7 @@ class te extends HTMLElement {
|
|
|
2555
2761
|
"api-url",
|
|
2556
2762
|
"placeholder",
|
|
2557
2763
|
"max-results",
|
|
2764
|
+
"max-render-results",
|
|
2558
2765
|
"debounce-ms",
|
|
2559
2766
|
"theme",
|
|
2560
2767
|
"hide-branding",
|
|
@@ -2562,30 +2769,71 @@ class te extends HTMLElement {
|
|
|
2562
2769
|
"show-date",
|
|
2563
2770
|
"hide-thumbnails",
|
|
2564
2771
|
"see-more",
|
|
2565
|
-
"request-options"
|
|
2772
|
+
"request-options",
|
|
2773
|
+
"translations"
|
|
2566
2774
|
];
|
|
2567
2775
|
}
|
|
2568
2776
|
connectedCallback() {
|
|
2569
|
-
this.initializeClient(), this.render(), this.dispatchEvent(v("ready", void 0));
|
|
2777
|
+
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.dispatchEvent(v("ready", void 0));
|
|
2570
2778
|
}
|
|
2571
2779
|
disconnectedCallback() {
|
|
2572
2780
|
this.cleanup();
|
|
2573
2781
|
}
|
|
2574
2782
|
attributeChangedCallback(e, t, s) {
|
|
2575
|
-
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);
|
|
2576
2815
|
}
|
|
2577
2816
|
getProps() {
|
|
2817
|
+
const e = this.resolvedTranslations;
|
|
2578
2818
|
return {
|
|
2579
|
-
apiUrl:
|
|
2580
|
-
placeholder:
|
|
2581
|
-
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
|
+
),
|
|
2582
2829
|
debounceMs: y(this.getAttribute("debounce-ms"), 300),
|
|
2583
|
-
theme:
|
|
2830
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
2584
2831
|
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
2585
2832
|
showUrl: m(this.getAttribute("show-url"), !1),
|
|
2586
2833
|
showDate: m(this.getAttribute("show-date"), !1),
|
|
2587
2834
|
hideThumbnails: m(this.getAttribute("hide-thumbnails"), !1),
|
|
2588
|
-
seeMore:
|
|
2835
|
+
seeMore: p(this.getAttribute("see-more"), ""),
|
|
2836
|
+
translations: this.translationsOverride ?? void 0
|
|
2589
2837
|
};
|
|
2590
2838
|
}
|
|
2591
2839
|
getRequestOptions() {
|
|
@@ -2608,20 +2856,20 @@ class te extends HTMLElement {
|
|
|
2608
2856
|
return;
|
|
2609
2857
|
}
|
|
2610
2858
|
try {
|
|
2611
|
-
this.client =
|
|
2859
|
+
this.client = S(e.apiUrl);
|
|
2612
2860
|
} catch (t) {
|
|
2613
2861
|
console.error("SearchBarSnippet:", t);
|
|
2614
2862
|
}
|
|
2615
2863
|
}
|
|
2616
2864
|
render() {
|
|
2617
|
-
const e = this.getProps(), t = (
|
|
2618
|
-
this.debouncedSearch =
|
|
2619
|
-
|
|
2865
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = (o) => this.performSearch(o);
|
|
2866
|
+
this.debouncedSearch = P(
|
|
2867
|
+
s,
|
|
2620
2868
|
e.debounceMs || 400
|
|
2621
2869
|
);
|
|
2622
|
-
const
|
|
2623
|
-
|
|
2624
|
-
${
|
|
2870
|
+
const r = document.createElement("style");
|
|
2871
|
+
r.textContent = `${M}
|
|
2872
|
+
${ne}`, this.container = document.createElement("div"), this.container.className = "container", this.container.innerHTML = `
|
|
2625
2873
|
<div class="search-view">
|
|
2626
2874
|
<div class="search-input-wrapper">
|
|
2627
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>
|
|
@@ -2629,12 +2877,12 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2629
2877
|
type="text"
|
|
2630
2878
|
name="search-input"
|
|
2631
2879
|
class="search-input"
|
|
2632
|
-
placeholder="${
|
|
2633
|
-
aria-label="
|
|
2880
|
+
placeholder="${l(e.placeholder || t.placeholder)}"
|
|
2881
|
+
aria-label="${l(t.searchInputAriaLabel)}"
|
|
2634
2882
|
autocomplete="off"
|
|
2635
2883
|
/>
|
|
2636
|
-
<button class="button search-submit-button" aria-label="
|
|
2637
|
-
<span
|
|
2884
|
+
<button class="button search-submit-button" aria-label="${l(t.searchButtonLabel)}">
|
|
2885
|
+
<span>${l(t.searchButtonLabel)}</span>
|
|
2638
2886
|
</button>
|
|
2639
2887
|
</div>
|
|
2640
2888
|
<div class="search-content">
|
|
@@ -2643,7 +2891,7 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2643
2891
|
</div>
|
|
2644
2892
|
</div>
|
|
2645
2893
|
</div>
|
|
2646
|
-
`, 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();
|
|
2647
2895
|
}
|
|
2648
2896
|
attachEventListeners() {
|
|
2649
2897
|
this.inputElement && (this.handleInputChange = (e) => {
|
|
@@ -2668,12 +2916,12 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2668
2916
|
}
|
|
2669
2917
|
this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.currentSearchController = new AbortController(), this.showLoadingState();
|
|
2670
2918
|
try {
|
|
2671
|
-
const t = await this.client.search(e, {
|
|
2919
|
+
const t = this.getProps(), s = await this.client.search(e, {
|
|
2672
2920
|
signal: this.currentSearchController.signal,
|
|
2673
|
-
maxResults:
|
|
2921
|
+
maxResults: t.maxResults || H,
|
|
2674
2922
|
request: this.getRequestOptions()
|
|
2675
|
-
}),
|
|
2676
|
-
this.displayResults(r, e,
|
|
2923
|
+
}), r = s.slice(0, t.maxRenderResults || B);
|
|
2924
|
+
this.displayResults(r, e, s.length);
|
|
2677
2925
|
} catch (t) {
|
|
2678
2926
|
if (t.name === "AbortError")
|
|
2679
2927
|
return;
|
|
@@ -2688,37 +2936,39 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2688
2936
|
this.showNoResultsState(t);
|
|
2689
2937
|
return;
|
|
2690
2938
|
}
|
|
2691
|
-
const r = this.getProps(), o = r.hideBranding ? "" : `<div class="powered-by-inline">${
|
|
2692
|
-
|
|
2693
|
-
|
|
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>
|
|
2694
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>
|
|
2695
2945
|
</a>
|
|
2696
|
-
</div>` : "",
|
|
2946
|
+
</div>` : "", g = `
|
|
2697
2947
|
<div class="search-header">
|
|
2698
2948
|
<div class="search-count">
|
|
2699
|
-
${l}
|
|
2949
|
+
${l(u)}
|
|
2700
2950
|
</div>
|
|
2701
|
-
${
|
|
2951
|
+
${c}
|
|
2702
2952
|
</div>
|
|
2703
2953
|
<div class="search-results">
|
|
2704
|
-
${e.map((
|
|
2954
|
+
${e.map((w) => this.renderResult(w)).join("")}
|
|
2705
2955
|
</div>
|
|
2706
|
-
${
|
|
2956
|
+
${f}
|
|
2707
2957
|
`;
|
|
2708
|
-
this.resultsContainer.innerHTML =
|
|
2958
|
+
this.resultsContainer.innerHTML = g, this.attachResultHandlers();
|
|
2709
2959
|
}
|
|
2710
2960
|
renderResult(e) {
|
|
2711
|
-
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">
|
|
2712
2962
|
${t.showUrl && e.url ? `<span class="search-result-url">${o}</span>` : '<span class="search-result-url search-result-url-empty"></span>'}
|
|
2713
2963
|
${c}
|
|
2714
2964
|
</div>` : "";
|
|
2715
2965
|
return `
|
|
2716
|
-
<a href="${r}" class="search-result-item" data-result-id="${
|
|
2966
|
+
<a href="${r}" class="search-result-item" data-result-id="${l(e.url || "")}">
|
|
2717
2967
|
${s}
|
|
2718
2968
|
<div class="search-result-content">
|
|
2719
|
-
<div class="search-result-title">${
|
|
2720
|
-
<div class="search-result-snippet">${
|
|
2721
|
-
${
|
|
2969
|
+
<div class="search-result-title">${l(e.title || "")}</div>
|
|
2970
|
+
<div class="search-result-snippet">${l(e.description || "")}</div>
|
|
2971
|
+
${h}
|
|
2722
2972
|
</div>
|
|
2723
2973
|
</a>
|
|
2724
2974
|
`;
|
|
@@ -2731,8 +2981,8 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2731
2981
|
<div class="search-result-image-placeholder" style="display: none;">${s}</div>
|
|
2732
2982
|
<img
|
|
2733
2983
|
class="search-result-image"
|
|
2734
|
-
src="${
|
|
2735
|
-
alt="${
|
|
2984
|
+
src="${l(e)}"
|
|
2985
|
+
alt="${l(t)}"
|
|
2736
2986
|
loading="lazy"
|
|
2737
2987
|
/>
|
|
2738
2988
|
</div>
|
|
@@ -2763,60 +3013,72 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2763
3013
|
});
|
|
2764
3014
|
}
|
|
2765
3015
|
showLoadingState() {
|
|
2766
|
-
|
|
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 = `
|
|
2767
3022
|
<div class="search-loading">
|
|
2768
|
-
<div class="loading" aria-label="
|
|
2769
|
-
<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>
|
|
2770
3025
|
</div>
|
|
2771
|
-
`, this.startLoadingInterval()
|
|
3026
|
+
`, this.startLoadingInterval();
|
|
2772
3027
|
}
|
|
2773
3028
|
startLoadingInterval() {
|
|
2774
3029
|
this.loadingMessageInterval = setInterval(() => {
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
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);
|
|
2779
3035
|
}
|
|
2780
3036
|
clearLoadingInterval() {
|
|
2781
3037
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
2782
3038
|
}
|
|
2783
3039
|
showEmptyState() {
|
|
2784
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3040
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3041
|
+
const e = this.resolvedTranslations;
|
|
3042
|
+
this.resultsContainer.innerHTML = `
|
|
2785
3043
|
<div class="search-empty">
|
|
2786
3044
|
<svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2787
3045
|
<circle cx="11" cy="11" r="8"></circle>
|
|
2788
3046
|
<path d="m21 21-4.35-4.35"></path>
|
|
2789
3047
|
</svg>
|
|
2790
|
-
<div class="search-empty-title"
|
|
3048
|
+
<div class="search-empty-title">${l(e.emptyStateTitle)}</div>
|
|
2791
3049
|
<div class="search-empty-description">
|
|
2792
|
-
|
|
3050
|
+
${l(e.emptyStateDescription)}
|
|
2793
3051
|
</div>
|
|
2794
3052
|
</div>
|
|
2795
|
-
|
|
3053
|
+
`;
|
|
2796
3054
|
}
|
|
2797
3055
|
showNoResultsState(e) {
|
|
2798
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3056
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3057
|
+
const t = this.resolvedTranslations;
|
|
3058
|
+
this.resultsContainer.innerHTML = `
|
|
2799
3059
|
<div class="search-empty">
|
|
2800
3060
|
<svg class="search-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2801
3061
|
<circle cx="11" cy="11" r="8"></circle>
|
|
2802
3062
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
2803
3063
|
</svg>
|
|
2804
|
-
<div class="search-empty-title"
|
|
3064
|
+
<div class="search-empty-title">${l(t.noResultsTitle)}</div>
|
|
2805
3065
|
<div class="search-empty-description">
|
|
2806
|
-
|
|
3066
|
+
${l(b(t.noResultsDescription, { query: e }))}
|
|
2807
3067
|
</div>
|
|
2808
3068
|
</div>
|
|
2809
|
-
|
|
3069
|
+
`;
|
|
2810
3070
|
}
|
|
2811
3071
|
showErrorState(e) {
|
|
2812
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3072
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3073
|
+
const t = this.resolvedTranslations;
|
|
3074
|
+
this.resultsContainer.innerHTML = `
|
|
2813
3075
|
<div class="error">
|
|
2814
|
-
<strong
|
|
3076
|
+
<strong>${l(t.errorPrefix)}</strong> ${l(e)}
|
|
2815
3077
|
</div>
|
|
2816
|
-
|
|
3078
|
+
`;
|
|
2817
3079
|
}
|
|
2818
3080
|
showMissingApiUrlError() {
|
|
2819
|
-
this.resultsContainer && this.showErrorState(
|
|
3081
|
+
this.resultsContainer && this.showErrorState(this.resolvedTranslations.missingApiUrlError);
|
|
2820
3082
|
}
|
|
2821
3083
|
updateTheme(e) {
|
|
2822
3084
|
const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
|
|
@@ -2830,8 +3092,8 @@ ${Z}`, this.container = document.createElement("div"), this.container.className
|
|
|
2830
3092
|
await this.performSearch(e);
|
|
2831
3093
|
}
|
|
2832
3094
|
}
|
|
2833
|
-
customElements.get(
|
|
2834
|
-
const
|
|
3095
|
+
customElements.get(q) || customElements.define(q, oe);
|
|
3096
|
+
const le = `
|
|
2835
3097
|
/* Modal backdrop */
|
|
2836
3098
|
.modal-backdrop {
|
|
2837
3099
|
position: fixed;
|
|
@@ -3268,8 +3530,8 @@ a.modal-result-item:focus-visible {
|
|
|
3268
3530
|
.modal-container.open {
|
|
3269
3531
|
animation: modal-slide-in var(--search-snippet-transition) ease-out;
|
|
3270
3532
|
}
|
|
3271
|
-
`,
|
|
3272
|
-
class
|
|
3533
|
+
`, O = "search-modal-snippet", D = 10, N = 50;
|
|
3534
|
+
class ce extends HTMLElement {
|
|
3273
3535
|
constructor() {
|
|
3274
3536
|
super();
|
|
3275
3537
|
a(this, "shadow");
|
|
@@ -3286,6 +3548,8 @@ class ae extends HTMLElement {
|
|
|
3286
3548
|
a(this, "currentSearchController", null);
|
|
3287
3549
|
a(this, "loadingMessageInterval", null);
|
|
3288
3550
|
a(this, "loadingMessageIndex", 0);
|
|
3551
|
+
a(this, "translationsOverride", null);
|
|
3552
|
+
a(this, "resolvedTranslations", d(null));
|
|
3289
3553
|
// Event handler references for cleanup
|
|
3290
3554
|
a(this, "handleGlobalKeydown", null);
|
|
3291
3555
|
a(this, "handleInputChange", null);
|
|
@@ -3301,6 +3565,7 @@ class ae extends HTMLElement {
|
|
|
3301
3565
|
"api-url",
|
|
3302
3566
|
"placeholder",
|
|
3303
3567
|
"max-results",
|
|
3568
|
+
"max-render-results",
|
|
3304
3569
|
"theme",
|
|
3305
3570
|
"shortcut",
|
|
3306
3571
|
"use-meta-key",
|
|
@@ -3310,32 +3575,79 @@ class ae extends HTMLElement {
|
|
|
3310
3575
|
"show-date",
|
|
3311
3576
|
"hide-thumbnails",
|
|
3312
3577
|
"see-more",
|
|
3313
|
-
"request-options"
|
|
3578
|
+
"request-options",
|
|
3579
|
+
"translations"
|
|
3314
3580
|
];
|
|
3315
3581
|
}
|
|
3316
3582
|
connectedCallback() {
|
|
3317
|
-
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));
|
|
3318
3584
|
}
|
|
3319
3585
|
disconnectedCallback() {
|
|
3320
3586
|
this.cleanup();
|
|
3321
3587
|
}
|
|
3322
3588
|
attributeChangedCallback(e, t, s) {
|
|
3323
|
-
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);
|
|
3324
3627
|
}
|
|
3325
3628
|
getProps() {
|
|
3629
|
+
const e = this.resolvedTranslations;
|
|
3326
3630
|
return {
|
|
3327
|
-
apiUrl:
|
|
3328
|
-
placeholder:
|
|
3329
|
-
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
|
+
),
|
|
3330
3641
|
debounceMs: y(this.getAttribute("debounce-ms"), 300),
|
|
3331
|
-
theme:
|
|
3332
|
-
shortcut:
|
|
3642
|
+
theme: p(this.getAttribute("theme"), "auto"),
|
|
3643
|
+
shortcut: p(this.getAttribute("shortcut"), "k"),
|
|
3333
3644
|
useMetaKey: this.getAttribute("use-meta-key") !== "false",
|
|
3334
3645
|
hideBranding: m(this.getAttribute("hide-branding"), !1),
|
|
3335
3646
|
showUrl: m(this.getAttribute("show-url"), !1),
|
|
3336
3647
|
showDate: m(this.getAttribute("show-date"), !1),
|
|
3337
3648
|
hideThumbnails: m(this.getAttribute("hide-thumbnails"), !1),
|
|
3338
|
-
seeMore:
|
|
3649
|
+
seeMore: p(this.getAttribute("see-more"), ""),
|
|
3650
|
+
translations: this.translationsOverride ?? void 0
|
|
3339
3651
|
};
|
|
3340
3652
|
}
|
|
3341
3653
|
getRequestOptions() {
|
|
@@ -3358,22 +3670,22 @@ class ae extends HTMLElement {
|
|
|
3358
3670
|
return;
|
|
3359
3671
|
}
|
|
3360
3672
|
try {
|
|
3361
|
-
this.client =
|
|
3673
|
+
this.client = S(e.apiUrl);
|
|
3362
3674
|
} catch (t) {
|
|
3363
3675
|
console.error("SearchModalSnippet:", t);
|
|
3364
3676
|
}
|
|
3365
3677
|
}
|
|
3366
3678
|
render() {
|
|
3367
|
-
const e = this.getProps(), t = (
|
|
3368
|
-
this.debouncedSearch =
|
|
3369
|
-
|
|
3679
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = (h) => this.performSearch(h);
|
|
3680
|
+
this.debouncedSearch = P(
|
|
3681
|
+
s,
|
|
3370
3682
|
e.debounceMs || 300
|
|
3371
3683
|
);
|
|
3372
|
-
const
|
|
3373
|
-
|
|
3374
|
-
${
|
|
3375
|
-
const
|
|
3376
|
-
|
|
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 = `
|
|
3377
3689
|
<div class="modal-backdrop" role="presentation"></div>
|
|
3378
3690
|
<div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
3379
3691
|
<div class="modal-header">
|
|
@@ -3383,8 +3695,8 @@ ${se}`;
|
|
|
3383
3695
|
<input
|
|
3384
3696
|
type="text"
|
|
3385
3697
|
class="modal-search-input"
|
|
3386
|
-
placeholder="${
|
|
3387
|
-
aria-label="
|
|
3698
|
+
placeholder="${l(e.placeholder || t.placeholder)}"
|
|
3699
|
+
aria-label="${l(t.searchButtonLabel)}"
|
|
3388
3700
|
aria-autocomplete="list"
|
|
3389
3701
|
aria-controls="modal-results-list"
|
|
3390
3702
|
aria-expanded="false"
|
|
@@ -3393,7 +3705,7 @@ ${se}`;
|
|
|
3393
3705
|
/>
|
|
3394
3706
|
</div>
|
|
3395
3707
|
<div class="modal-content">
|
|
3396
|
-
<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)}">
|
|
3397
3709
|
${this.renderEmptyState()}
|
|
3398
3710
|
</div>
|
|
3399
3711
|
</div>
|
|
@@ -3402,21 +3714,21 @@ ${se}`;
|
|
|
3402
3714
|
<div class="modal-footer-hint">
|
|
3403
3715
|
<kbd class="modal-kbd">↑</kbd>
|
|
3404
3716
|
<kbd class="modal-kbd">↓</kbd>
|
|
3405
|
-
<span
|
|
3717
|
+
<span>${l(t.navigateHint)}</span>
|
|
3406
3718
|
</div>
|
|
3407
3719
|
<div class="modal-footer-hint">
|
|
3408
3720
|
<kbd class="modal-kbd">↵</kbd>
|
|
3409
|
-
<span
|
|
3721
|
+
<span>${l(t.selectHint)}</span>
|
|
3410
3722
|
</div>
|
|
3411
3723
|
<div class="modal-footer-hint">
|
|
3412
3724
|
<kbd class="modal-kbd">Esc</kbd>
|
|
3413
|
-
<span
|
|
3725
|
+
<span>${l(t.closeHint)}</span>
|
|
3414
3726
|
</div>
|
|
3415
3727
|
</div>
|
|
3416
|
-
${
|
|
3728
|
+
${o}
|
|
3417
3729
|
</div>
|
|
3418
3730
|
</div>
|
|
3419
|
-
`, 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();
|
|
3420
3732
|
}
|
|
3421
3733
|
attachGlobalKeyboardShortcut() {
|
|
3422
3734
|
const e = this.getProps(), t = e.shortcut?.toLowerCase() || "k";
|
|
@@ -3483,12 +3795,12 @@ ${se}`;
|
|
|
3483
3795
|
}
|
|
3484
3796
|
this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.currentSearchController = new AbortController(), this.showLoadingState();
|
|
3485
3797
|
try {
|
|
3486
|
-
const t = await this.client.search(e, {
|
|
3798
|
+
const t = this.getProps(), s = await this.client.search(e, {
|
|
3487
3799
|
signal: this.currentSearchController.signal,
|
|
3488
|
-
maxResults:
|
|
3800
|
+
maxResults: t.maxResults || N,
|
|
3489
3801
|
request: this.getRequestOptions()
|
|
3490
|
-
})
|
|
3491
|
-
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);
|
|
3492
3804
|
} catch (t) {
|
|
3493
3805
|
if (t.name === "AbortError")
|
|
3494
3806
|
return;
|
|
@@ -3503,16 +3815,18 @@ ${se}`;
|
|
|
3503
3815
|
this.showNoResultsState(t);
|
|
3504
3816
|
return;
|
|
3505
3817
|
}
|
|
3506
|
-
const r = this.getProps(), o = e.map((
|
|
3507
|
-
|
|
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>
|
|
3508
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>
|
|
3509
3823
|
</a>` : "";
|
|
3510
|
-
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();
|
|
3511
3825
|
}
|
|
3512
3826
|
renderResult(e, t) {
|
|
3513
|
-
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">
|
|
3514
3828
|
${s.showUrl && e.url ? `<span class="modal-result-url">${c}</span>` : '<span class="modal-result-url modal-result-url-empty"></span>'}
|
|
3515
|
-
${
|
|
3829
|
+
${h}
|
|
3516
3830
|
</div>` : "";
|
|
3517
3831
|
return `
|
|
3518
3832
|
<a
|
|
@@ -3523,13 +3837,13 @@ ${se}`;
|
|
|
3523
3837
|
aria-selected="${t === this.activeIndex}"
|
|
3524
3838
|
tabindex="-1"
|
|
3525
3839
|
data-index="${t}"
|
|
3526
|
-
data-url="${
|
|
3840
|
+
data-url="${l(e.url || "")}"
|
|
3527
3841
|
>
|
|
3528
3842
|
${r}
|
|
3529
3843
|
<div class="modal-result-content">
|
|
3530
|
-
<div class="modal-result-title">${
|
|
3531
|
-
${e.description ? `<div class="modal-result-description">${
|
|
3532
|
-
${
|
|
3844
|
+
<div class="modal-result-title">${l(e.title || "")}</div>
|
|
3845
|
+
${e.description ? `<div class="modal-result-description">${l(e.description)}</div>` : ""}
|
|
3846
|
+
${u}
|
|
3533
3847
|
</div>
|
|
3534
3848
|
</a>
|
|
3535
3849
|
`;
|
|
@@ -3542,8 +3856,8 @@ ${se}`;
|
|
|
3542
3856
|
<div class="modal-result-image-placeholder" style="display: none;">${s}</div>
|
|
3543
3857
|
<img
|
|
3544
3858
|
class="modal-result-image"
|
|
3545
|
-
src="${
|
|
3546
|
-
alt="${
|
|
3859
|
+
src="${l(e)}"
|
|
3860
|
+
alt="${l(t)}"
|
|
3547
3861
|
loading="lazy"
|
|
3548
3862
|
/>
|
|
3549
3863
|
</div>
|
|
@@ -3576,13 +3890,14 @@ ${se}`;
|
|
|
3576
3890
|
});
|
|
3577
3891
|
}
|
|
3578
3892
|
renderEmptyState() {
|
|
3893
|
+
const e = this.resolvedTranslations;
|
|
3579
3894
|
return `
|
|
3580
3895
|
<div class="modal-empty">
|
|
3581
3896
|
<svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3582
3897
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3583
3898
|
<path d="m21 21-4.35-4.35"></path>
|
|
3584
3899
|
</svg>
|
|
3585
|
-
<div class="modal-empty-description"
|
|
3900
|
+
<div class="modal-empty-description">${l(e.modalEmptyStateDescription)}</div>
|
|
3586
3901
|
</div>
|
|
3587
3902
|
`;
|
|
3588
3903
|
}
|
|
@@ -3590,44 +3905,52 @@ ${se}`;
|
|
|
3590
3905
|
this.clearLoadingInterval(), this.resultsContainer && (this.resultsContainer.innerHTML = this.renderEmptyState(), this.footerCount && (this.footerCount.textContent = ""), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false"));
|
|
3591
3906
|
}
|
|
3592
3907
|
showLoadingState() {
|
|
3593
|
-
|
|
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 = `
|
|
3594
3912
|
<div class="modal-loading">
|
|
3595
|
-
<div class="loading" aria-label="
|
|
3596
|
-
<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>
|
|
3597
3915
|
</div>
|
|
3598
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3916
|
+
`, this.footerCount && (this.footerCount.textContent = e[this.loadingMessageIndex]), this.startLoadingInterval();
|
|
3599
3917
|
}
|
|
3600
3918
|
startLoadingInterval() {
|
|
3601
3919
|
this.loadingMessageInterval = setInterval(() => {
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
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);
|
|
3606
3925
|
}
|
|
3607
3926
|
clearLoadingInterval() {
|
|
3608
3927
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
3609
3928
|
}
|
|
3610
3929
|
showNoResultsState(e) {
|
|
3611
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3930
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3931
|
+
const t = this.resolvedTranslations;
|
|
3932
|
+
this.resultsContainer.innerHTML = `
|
|
3612
3933
|
<div class="modal-empty">
|
|
3613
3934
|
<svg class="modal-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3614
3935
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3615
3936
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
3616
3937
|
</svg>
|
|
3617
|
-
<div class="modal-empty-title"
|
|
3618
|
-
<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>
|
|
3619
3940
|
</div>
|
|
3620
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3941
|
+
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountZero), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false");
|
|
3621
3942
|
}
|
|
3622
3943
|
showErrorState(e) {
|
|
3623
|
-
this.clearLoadingInterval(), this.resultsContainer
|
|
3944
|
+
if (this.clearLoadingInterval(), !this.resultsContainer) return;
|
|
3945
|
+
const t = this.resolvedTranslations;
|
|
3946
|
+
this.resultsContainer.innerHTML = `
|
|
3624
3947
|
<div class="error">
|
|
3625
|
-
<strong
|
|
3948
|
+
<strong>${l(t.errorPrefix)}</strong> ${l(e)}
|
|
3626
3949
|
</div>
|
|
3627
|
-
`, this.footerCount && (this.footerCount.textContent =
|
|
3950
|
+
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountError);
|
|
3628
3951
|
}
|
|
3629
3952
|
showMissingApiUrlError() {
|
|
3630
|
-
this.resultsContainer && this.showErrorState(
|
|
3953
|
+
this.resultsContainer && this.showErrorState(this.resolvedTranslations.missingApiUrlError);
|
|
3631
3954
|
}
|
|
3632
3955
|
updateTheme(e) {
|
|
3633
3956
|
const t = e === "light" || e === "dark" || e === "auto" ? e : "auto";
|
|
@@ -3693,13 +4016,15 @@ ${se}`;
|
|
|
3693
4016
|
return this.isOpen;
|
|
3694
4017
|
}
|
|
3695
4018
|
}
|
|
3696
|
-
customElements.get(
|
|
4019
|
+
customElements.get(O) || customElements.define(O, ce);
|
|
3697
4020
|
export {
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
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
|
|
3704
4029
|
};
|
|
3705
4030
|
//# sourceMappingURL=search-snippet.es.js.map
|