@cloudflare/ai-search-snippet 0.0.38 → 0.0.39
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 +186 -4
- package/dist/search-snippet.es.js +819 -399
- package/dist/search-snippet.es.js.map +1 -1
- package/dist/search-snippet.umd.js +120 -95
- package/dist/search-snippet.umd.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var a = (
|
|
4
|
-
const
|
|
1
|
+
var W = Object.defineProperty;
|
|
2
|
+
var J = (l, i, e) => i in l ? W(l, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[i] = e;
|
|
3
|
+
var a = (l, i, e) => J(l, typeof i != "symbol" ? i + "" : i, e);
|
|
4
|
+
const T = {
|
|
5
5
|
// Shared
|
|
6
6
|
loadingAriaLabel: "Loading",
|
|
7
7
|
errorPrefix: "Error:",
|
|
@@ -76,15 +76,15 @@ const L = {
|
|
|
76
76
|
"Hunting down answers..."
|
|
77
77
|
]
|
|
78
78
|
};
|
|
79
|
-
function
|
|
80
|
-
if (!
|
|
81
|
-
return
|
|
82
|
-
const i = { ...
|
|
83
|
-
for (const e of Object.keys(
|
|
84
|
-
const t =
|
|
79
|
+
function g(l) {
|
|
80
|
+
if (!l || typeof l != "object")
|
|
81
|
+
return T;
|
|
82
|
+
const i = { ...T };
|
|
83
|
+
for (const e of Object.keys(l)) {
|
|
84
|
+
const t = l[e];
|
|
85
85
|
if (t != null) {
|
|
86
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 =
|
|
87
|
+
Array.isArray(t) && t.length > 0 && (i.loadingMessages = t.filter((s) => typeof s == "string"), i.loadingMessages.length === 0 && (i.loadingMessages = T.loadingMessages));
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
90
90
|
typeof t == "string" && (i[e] = t);
|
|
@@ -92,13 +92,13 @@ function d(n) {
|
|
|
92
92
|
}
|
|
93
93
|
return i;
|
|
94
94
|
}
|
|
95
|
-
function
|
|
96
|
-
return
|
|
95
|
+
function y(l, i = {}) {
|
|
96
|
+
return l.replace(/\{(\w+)\}/g, (e, t) => Object.hasOwn(i, t) ? String(i[t]) : e);
|
|
97
97
|
}
|
|
98
|
-
function k(
|
|
99
|
-
if (!
|
|
98
|
+
function k(l, i) {
|
|
99
|
+
if (!l) return null;
|
|
100
100
|
try {
|
|
101
|
-
const e = JSON.parse(
|
|
101
|
+
const e = JSON.parse(l);
|
|
102
102
|
if (e === null || typeof e != "object" || Array.isArray(e))
|
|
103
103
|
throw new Error("translations must be a JSON object");
|
|
104
104
|
return e;
|
|
@@ -106,41 +106,41 @@ function k(n, i) {
|
|
|
106
106
|
return console.error(`${i}: invalid translations attribute`, e), null;
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
const
|
|
110
|
-
function
|
|
109
|
+
const A = 2500;
|
|
110
|
+
function _(l, i) {
|
|
111
111
|
let e;
|
|
112
112
|
function t(...s) {
|
|
113
113
|
clearTimeout(e), e = setTimeout(() => {
|
|
114
|
-
|
|
114
|
+
l(...s);
|
|
115
115
|
}, i);
|
|
116
116
|
}
|
|
117
117
|
return t.cancel = () => clearTimeout(e), t;
|
|
118
118
|
}
|
|
119
|
-
function l
|
|
119
|
+
function c(l) {
|
|
120
120
|
const i = document.createElement("div");
|
|
121
|
-
return i.textContent =
|
|
121
|
+
return i.textContent = l, i.innerHTML;
|
|
122
122
|
}
|
|
123
|
-
function
|
|
123
|
+
function V(l) {
|
|
124
124
|
try {
|
|
125
|
-
return decodeURI(
|
|
125
|
+
return decodeURI(l);
|
|
126
126
|
} catch {
|
|
127
|
-
return
|
|
127
|
+
return l;
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
function
|
|
131
|
-
return new DOMParser().parseFromString(
|
|
130
|
+
function I(l) {
|
|
131
|
+
return new DOMParser().parseFromString(l, "text/html").documentElement.textContent || "";
|
|
132
132
|
}
|
|
133
|
-
function
|
|
134
|
-
const e =
|
|
133
|
+
function X(l, i) {
|
|
134
|
+
const e = g(i), t = new Date(l), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime();
|
|
135
135
|
if (r < 6e4)
|
|
136
136
|
return e.justNow;
|
|
137
137
|
if (r < 36e5) {
|
|
138
|
-
const
|
|
139
|
-
return
|
|
138
|
+
const n = Math.floor(r / 6e4), o = n === 1 ? e.minuteAgo : e.minutesAgo;
|
|
139
|
+
return y(o, { n });
|
|
140
140
|
}
|
|
141
141
|
if (r < 864e5) {
|
|
142
|
-
const
|
|
143
|
-
return
|
|
142
|
+
const n = Math.floor(r / 36e5), o = n === 1 ? e.hourAgo : e.hoursAgo;
|
|
143
|
+
return y(o, { n });
|
|
144
144
|
}
|
|
145
145
|
return t.toLocaleString(void 0, {
|
|
146
146
|
month: "short",
|
|
@@ -149,88 +149,131 @@ function J(n, i) {
|
|
|
149
149
|
minute: "2-digit"
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
|
-
function j(
|
|
153
|
-
return new Date(
|
|
152
|
+
function j(l) {
|
|
153
|
+
return new Date(l).toLocaleDateString(void 0, {
|
|
154
154
|
month: "short",
|
|
155
155
|
day: "numeric"
|
|
156
156
|
});
|
|
157
157
|
}
|
|
158
|
-
function
|
|
159
|
-
return `${
|
|
158
|
+
function R(l = "id") {
|
|
159
|
+
return `${l}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
160
160
|
}
|
|
161
|
-
function
|
|
162
|
-
return
|
|
161
|
+
function m(l, i) {
|
|
162
|
+
return l !== null ? l : i;
|
|
163
163
|
}
|
|
164
|
-
function
|
|
165
|
-
return
|
|
164
|
+
function v(l, i) {
|
|
165
|
+
return l === null ? i : l === "true" || l === "";
|
|
166
166
|
}
|
|
167
|
-
function
|
|
168
|
-
if (
|
|
169
|
-
const e = Number.parseInt(
|
|
167
|
+
function w(l, i) {
|
|
168
|
+
if (l === null) return i;
|
|
169
|
+
const e = Number.parseInt(l, 10);
|
|
170
170
|
return Number.isNaN(e) ? i : e;
|
|
171
171
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
172
|
+
function F(l, i) {
|
|
173
|
+
if (!(l === null || l === ""))
|
|
174
|
+
try {
|
|
175
|
+
const e = JSON.parse(l);
|
|
176
|
+
if (e === null || typeof e != "object" || Array.isArray(e))
|
|
177
|
+
throw new Error("chat-query-rewrite must be a JSON object");
|
|
178
|
+
const t = e, s = {};
|
|
179
|
+
return typeof t.enabled == "boolean" && (s.enabled = t.enabled), typeof t.model == "string" && (s.model = t.model), typeof t.rewritePrompt == "string" && (s.rewritePrompt = t.rewritePrompt), s;
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error(`${i}: invalid chat-query-rewrite attribute`, e);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function b(l, i) {
|
|
186
|
+
return new CustomEvent(l, {
|
|
174
187
|
detail: i,
|
|
175
188
|
bubbles: !0,
|
|
176
189
|
composed: !0,
|
|
177
190
|
cancelable: !0
|
|
178
191
|
});
|
|
179
192
|
}
|
|
180
|
-
function S(
|
|
181
|
-
if (!
|
|
193
|
+
function S(l) {
|
|
194
|
+
if (!l)
|
|
182
195
|
throw new Error("API URL is required");
|
|
183
|
-
return new
|
|
196
|
+
return new ae(l);
|
|
197
|
+
}
|
|
198
|
+
const Z = "@cf/meta/llama-3.3-70b-instruct-fp8-fast", ee = `You rewrite a multi-turn chat into a single standalone search query for a retrieval system.
|
|
199
|
+
|
|
200
|
+
Inputs: the full conversation in \`messages\`. The final user message is the one to answer; earlier messages are context only.
|
|
201
|
+
|
|
202
|
+
Rules:
|
|
203
|
+
- Output ONLY the rewritten query as plain text. No preamble, no quotes, no markdown, no explanation.
|
|
204
|
+
- Resolve pronouns and references (it, that, they, the second one, the previous one, etc.) using prior turns.
|
|
205
|
+
- Inline any entities, names, versions, products, or constraints from earlier turns that the final message depends on.
|
|
206
|
+
- Preserve the user's original language and terminology. Do not translate.
|
|
207
|
+
- Do not invent facts, sources, dates, or details not present in the conversation.
|
|
208
|
+
- If the final user message is already fully self-contained, return it unchanged (modulo trivial cleanup).
|
|
209
|
+
- Drop greetings, thanks, and meta questions about the assistant itself; keep only the information need.
|
|
210
|
+
- Keep it concise — a search query, not a sentence. Aim for under 200 characters when possible.
|
|
211
|
+
|
|
212
|
+
Return only the rewritten query.`;
|
|
213
|
+
function te(l) {
|
|
214
|
+
let i = "message";
|
|
215
|
+
const e = [];
|
|
216
|
+
for (const t of l.split(`
|
|
217
|
+
`)) {
|
|
218
|
+
const s = t.endsWith("\r") ? t.slice(0, -1) : t;
|
|
219
|
+
if (s === "" || s.startsWith(":"))
|
|
220
|
+
continue;
|
|
221
|
+
const r = s.indexOf(":"), n = r === -1 ? s : s.slice(0, r);
|
|
222
|
+
let o = r === -1 ? "" : s.slice(r + 1);
|
|
223
|
+
o.startsWith(" ") && (o = o.slice(1)), n === "event" ? i = o : n === "data" && e.push(o);
|
|
224
|
+
}
|
|
225
|
+
return e.length === 0 ? null : { event: i, data: e.join(`
|
|
226
|
+
`) };
|
|
184
227
|
}
|
|
185
|
-
function C(
|
|
186
|
-
return
|
|
228
|
+
function C(l) {
|
|
229
|
+
return l !== null && typeof l == "object" && !Array.isArray(l);
|
|
187
230
|
}
|
|
188
|
-
function
|
|
231
|
+
function K(...l) {
|
|
189
232
|
const i = {};
|
|
190
|
-
for (const e of
|
|
233
|
+
for (const e of l)
|
|
191
234
|
if (e)
|
|
192
235
|
for (const [t, s] of Object.entries(e)) {
|
|
193
236
|
const r = i[t];
|
|
194
|
-
C(r) && C(s) ? i[t] =
|
|
237
|
+
C(r) && C(s) ? i[t] = K(r, s) : i[t] = s;
|
|
195
238
|
}
|
|
196
239
|
return i;
|
|
197
240
|
}
|
|
198
|
-
function
|
|
241
|
+
function se(l, i) {
|
|
199
242
|
if (!C(i))
|
|
200
|
-
return
|
|
243
|
+
return l;
|
|
201
244
|
const e = new URLSearchParams();
|
|
202
|
-
for (const [h,
|
|
203
|
-
|
|
245
|
+
for (const [h, d] of Object.entries(i))
|
|
246
|
+
d != null && e.append(h, String(d));
|
|
204
247
|
const t = e.toString();
|
|
205
248
|
if (!t)
|
|
206
|
-
return
|
|
207
|
-
const s =
|
|
208
|
-
return `${r}${
|
|
249
|
+
return l;
|
|
250
|
+
const s = l.indexOf("#"), r = s === -1 ? l : l.slice(0, s), n = s === -1 ? "" : l.slice(s), o = r.includes("?") ? "&" : "?";
|
|
251
|
+
return `${r}${o}${t}${n}`;
|
|
209
252
|
}
|
|
210
|
-
function
|
|
211
|
-
if (!C(
|
|
253
|
+
function ie(l) {
|
|
254
|
+
if (!C(l))
|
|
212
255
|
return {};
|
|
213
256
|
const i = {};
|
|
214
|
-
for (const [e, t] of Object.entries(
|
|
257
|
+
for (const [e, t] of Object.entries(l))
|
|
215
258
|
t != null && (i[e] = String(t));
|
|
216
259
|
return i;
|
|
217
260
|
}
|
|
218
|
-
function
|
|
219
|
-
return C(
|
|
261
|
+
function re(l) {
|
|
262
|
+
return C(l) ? l : void 0;
|
|
220
263
|
}
|
|
221
|
-
class
|
|
264
|
+
class ae {
|
|
222
265
|
constructor(i) {
|
|
223
266
|
a(this, "activeRequests", /* @__PURE__ */ new Map());
|
|
224
267
|
a(this, "baseUrl");
|
|
225
268
|
this.baseUrl = i.replace(/\/$/, "");
|
|
226
269
|
}
|
|
227
270
|
request(i, e, t, s) {
|
|
228
|
-
const r = e === "search" ? "snippet-search" : "snippet-chat-completions",
|
|
229
|
-
return fetch(
|
|
271
|
+
const r = e === "search" ? "snippet-search" : "snippet-chat-completions", n = se(`${this.baseUrl}/${e}`, s?.queryParams);
|
|
272
|
+
return fetch(n, {
|
|
230
273
|
method: "POST",
|
|
231
|
-
body: JSON.stringify(
|
|
274
|
+
body: JSON.stringify(K(re(s?.body), i)),
|
|
232
275
|
headers: {
|
|
233
|
-
...
|
|
276
|
+
...ie(s?.headers),
|
|
234
277
|
"Content-Type": "application/json",
|
|
235
278
|
Accept: i.stream ? "text/event-stream" : "application/json",
|
|
236
279
|
"cf-ai-search-source": r
|
|
@@ -245,7 +288,7 @@ class Q {
|
|
|
245
288
|
const t = this.generateRequestId(), s = new AbortController(), r = e.signal || s.signal;
|
|
246
289
|
this.registerRequest(t, s);
|
|
247
290
|
try {
|
|
248
|
-
const
|
|
291
|
+
const n = await this.request(
|
|
249
292
|
{
|
|
250
293
|
messages: [{ role: "user", content: i }],
|
|
251
294
|
stream: !1,
|
|
@@ -260,18 +303,18 @@ class Q {
|
|
|
260
303
|
r,
|
|
261
304
|
e.request
|
|
262
305
|
);
|
|
263
|
-
if (!
|
|
264
|
-
throw new Error(`HTTP error! status: ${
|
|
265
|
-
if (!
|
|
306
|
+
if (!n.ok)
|
|
307
|
+
throw new Error(`HTTP error! status: ${n.status}`);
|
|
308
|
+
if (!n.body)
|
|
266
309
|
throw new Error("Response body is empty");
|
|
267
|
-
const
|
|
268
|
-
if (
|
|
269
|
-
return
|
|
310
|
+
const o = await n.json();
|
|
311
|
+
if (o.success && o.result)
|
|
312
|
+
return o.result.chunks.map(
|
|
270
313
|
(h) => ({
|
|
271
314
|
type: "result",
|
|
272
315
|
id: h.id,
|
|
273
|
-
title:
|
|
274
|
-
description: h.item.metadata?.description ?
|
|
316
|
+
title: I(h.item.metadata?.title),
|
|
317
|
+
description: h.item.metadata?.description ? I(h.item.metadata?.description) : "",
|
|
275
318
|
timestamp: h.item.timestamp ?? void 0,
|
|
276
319
|
url: h.item.key,
|
|
277
320
|
image: h.item.metadata?.image || void 0,
|
|
@@ -281,7 +324,7 @@ class Q {
|
|
|
281
324
|
}
|
|
282
325
|
})
|
|
283
326
|
);
|
|
284
|
-
throw
|
|
327
|
+
throw o.success === !1 ? new Error(o.error) : new Error("Unknown error");
|
|
285
328
|
} finally {
|
|
286
329
|
this.unregisterRequest(t);
|
|
287
330
|
}
|
|
@@ -289,7 +332,7 @@ class Q {
|
|
|
289
332
|
async *searchStream(i, e = {}) {
|
|
290
333
|
const t = this.generateRequestId(), s = new AbortController(), r = e.signal || s.signal;
|
|
291
334
|
this.registerRequest(t, s);
|
|
292
|
-
const
|
|
335
|
+
const n = await this.request(
|
|
293
336
|
{
|
|
294
337
|
messages: [{ role: "user", content: i }],
|
|
295
338
|
stream: !0,
|
|
@@ -301,47 +344,111 @@ class Q {
|
|
|
301
344
|
r,
|
|
302
345
|
e.request
|
|
303
346
|
);
|
|
304
|
-
if (!
|
|
305
|
-
throw new Error(`HTTP error! status: ${
|
|
306
|
-
if (!
|
|
347
|
+
if (!n.ok)
|
|
348
|
+
throw new Error(`HTTP error! status: ${n.status}`);
|
|
349
|
+
if (!n.body)
|
|
307
350
|
throw new Error("Response body is empty");
|
|
308
|
-
let
|
|
309
|
-
const h =
|
|
351
|
+
let o = "";
|
|
352
|
+
const h = n.body.getReader(), d = new TextDecoder();
|
|
310
353
|
for (; ; ) {
|
|
311
|
-
const { done:
|
|
312
|
-
if (
|
|
354
|
+
const { done: u, value: f } = await h.read();
|
|
355
|
+
if (u)
|
|
313
356
|
break;
|
|
314
|
-
const
|
|
315
|
-
|
|
357
|
+
const L = d.decode(f, { stream: !0 });
|
|
358
|
+
o += L;
|
|
316
359
|
}
|
|
317
360
|
yield {
|
|
318
361
|
type: "result",
|
|
319
362
|
id: "",
|
|
320
363
|
title: "",
|
|
321
|
-
description:
|
|
364
|
+
description: o.replaceAll("data: ", "").trim().split(`
|
|
322
365
|
|
|
323
|
-
`).map((
|
|
366
|
+
`).map((u) => JSON.parse(u)).map((u) => u.response).join(""),
|
|
324
367
|
url: "",
|
|
325
368
|
metadata: {}
|
|
326
369
|
};
|
|
327
370
|
}
|
|
328
371
|
async *chat(i, e) {
|
|
329
|
-
const t = new AbortController(), s = e?.signal || t.signal, r =
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
372
|
+
const t = new AbortController(), s = e?.signal || t.signal, r = e?.stream ?? !0, o = { messages: [...e?.history ?? [], { role: "user", content: i }], stream: r };
|
|
373
|
+
if (e?.queryRewrite) {
|
|
374
|
+
const d = typeof e.queryRewrite == "object" ? e.queryRewrite : {};
|
|
375
|
+
o.ai_search_options = {
|
|
376
|
+
query_rewrite: {
|
|
377
|
+
enabled: !0,
|
|
378
|
+
model: d.model ?? Z,
|
|
379
|
+
rewrite_prompt: d.rewritePrompt ?? ee
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
const h = await this.request(o, "chat/completions", s);
|
|
384
|
+
if (!h.ok)
|
|
385
|
+
throw new Error(`HTTP error! status: ${h.status}`);
|
|
386
|
+
if (!h.body)
|
|
340
387
|
throw new Error("Response body is empty");
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
388
|
+
if (!r) {
|
|
389
|
+
yield {
|
|
390
|
+
type: "text",
|
|
391
|
+
message: (await h.json()).choices.map((p) => p.message.content).join("")
|
|
392
|
+
};
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
yield* this.parseChatStream(h.body);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Consume an SSE stream from the chat/completions endpoint and yield one
|
|
399
|
+
* ChatTextResponse per non-empty content delta. Discards `event: chunks`
|
|
400
|
+
* (RAG sources) and the `[DONE]` sentinel; tolerates malformed individual
|
|
401
|
+
* frames without aborting the whole stream.
|
|
402
|
+
*/
|
|
403
|
+
async *parseChatStream(i) {
|
|
404
|
+
const e = i.getReader(), t = new TextDecoder();
|
|
405
|
+
let s = "";
|
|
406
|
+
const r = (n) => {
|
|
407
|
+
const o = te(n);
|
|
408
|
+
if (!o || o.event === "chunks")
|
|
409
|
+
return null;
|
|
410
|
+
if (o.data === "[DONE]")
|
|
411
|
+
return "done";
|
|
412
|
+
try {
|
|
413
|
+
const d = JSON.parse(o.data).choices?.[0]?.delta?.content;
|
|
414
|
+
if (typeof d == "string" && d.length > 0)
|
|
415
|
+
return { type: "text", message: d };
|
|
416
|
+
} catch (h) {
|
|
417
|
+
console.error("AISearchClient: failed to parse SSE chat chunk", h);
|
|
418
|
+
}
|
|
419
|
+
return null;
|
|
344
420
|
};
|
|
421
|
+
try {
|
|
422
|
+
for (; ; ) {
|
|
423
|
+
const { done: n, value: o } = await e.read();
|
|
424
|
+
if (n)
|
|
425
|
+
break;
|
|
426
|
+
s += t.decode(o, { stream: !0 });
|
|
427
|
+
let h = s.indexOf(`
|
|
428
|
+
|
|
429
|
+
`);
|
|
430
|
+
for (; h !== -1; ) {
|
|
431
|
+
const d = s.slice(0, h);
|
|
432
|
+
s = s.slice(h + 2);
|
|
433
|
+
const p = r(d);
|
|
434
|
+
if (p === "done")
|
|
435
|
+
return;
|
|
436
|
+
p && (yield p), h = s.indexOf(`
|
|
437
|
+
|
|
438
|
+
`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (s += t.decode(), s.trim().length > 0) {
|
|
442
|
+
const n = r(s);
|
|
443
|
+
n && n !== "done" && (yield n);
|
|
444
|
+
}
|
|
445
|
+
} catch (n) {
|
|
446
|
+
if (n.name === "AbortError")
|
|
447
|
+
return;
|
|
448
|
+
throw n;
|
|
449
|
+
} finally {
|
|
450
|
+
e.releaseLock();
|
|
451
|
+
}
|
|
345
452
|
}
|
|
346
453
|
/**
|
|
347
454
|
* Cancels an active request by ID
|
|
@@ -380,10 +487,141 @@ class Q {
|
|
|
380
487
|
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
381
488
|
}
|
|
382
489
|
}
|
|
383
|
-
const
|
|
490
|
+
const ne = "0.0.39", oe = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.w3.org/2000/svg" aria-label="Cloudflare" role="img">
|
|
384
491
|
<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"/>
|
|
385
492
|
<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"/>
|
|
386
|
-
</svg>`,
|
|
493
|
+
</svg>`, le = "https://workers.cloudflare.com/product/ai-search", E = `Powered by <a href="${le}" target="_blank" rel="noopener noreferrer">Cloudflare AI Search ${oe}</a>`, ce = 2e4, he = 20, de = "/stats";
|
|
494
|
+
function z() {
|
|
495
|
+
return typeof document < "u" && typeof window < "u";
|
|
496
|
+
}
|
|
497
|
+
class Q {
|
|
498
|
+
constructor(i, e = {}) {
|
|
499
|
+
a(this, "baseUrl");
|
|
500
|
+
a(this, "endpoint");
|
|
501
|
+
a(this, "snippetVersion");
|
|
502
|
+
a(this, "flushIntervalMs");
|
|
503
|
+
a(this, "maxBufferSize");
|
|
504
|
+
a(this, "buffer", []);
|
|
505
|
+
a(this, "flushTimer", null);
|
|
506
|
+
a(this, "destroyed", !1);
|
|
507
|
+
a(this, "boundUnloadHandler");
|
|
508
|
+
a(this, "boundVisibilityHandler");
|
|
509
|
+
this.baseUrl = i.replace(/\/$/, ""), this.endpoint = e.endpoint ?? de, this.snippetVersion = e.snippetVersion ?? ne, this.flushIntervalMs = e.flushIntervalMs ?? ce, this.maxBufferSize = Math.max(1, e.maxBufferSize ?? he), this.boundUnloadHandler = () => this.flushBeacon(), this.boundVisibilityHandler = () => {
|
|
510
|
+
typeof document < "u" && document.visibilityState === "hidden" && this.flushBeacon();
|
|
511
|
+
}, z() && (window.addEventListener("pagehide", this.boundUnloadHandler), document.addEventListener("visibilitychange", this.boundVisibilityHandler));
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Record a completed search (no click).
|
|
515
|
+
*/
|
|
516
|
+
trackSearch(i, e) {
|
|
517
|
+
this.track({
|
|
518
|
+
inputQuery: i,
|
|
519
|
+
snippetVersion: this.snippetVersion,
|
|
520
|
+
totalResult: e
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Record a click on a specific result.
|
|
525
|
+
*/
|
|
526
|
+
trackClick(i, e, t, s) {
|
|
527
|
+
this.track({
|
|
528
|
+
inputQuery: i,
|
|
529
|
+
snippetVersion: this.snippetVersion,
|
|
530
|
+
totalResult: e,
|
|
531
|
+
clickedResultId: t,
|
|
532
|
+
clickPosition: s,
|
|
533
|
+
clickViewMore: !1
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Record a click on the "See more" link.
|
|
538
|
+
*/
|
|
539
|
+
trackViewMore(i, e) {
|
|
540
|
+
this.track({
|
|
541
|
+
inputQuery: i,
|
|
542
|
+
snippetVersion: this.snippetVersion,
|
|
543
|
+
totalResult: e,
|
|
544
|
+
clickViewMore: !0
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Buffer a pre-built event. Higher-level `track*` helpers call this.
|
|
549
|
+
*/
|
|
550
|
+
track(i) {
|
|
551
|
+
if (!this.destroyed) {
|
|
552
|
+
if (this.buffer.push(i), this.buffer.length >= this.maxBufferSize) {
|
|
553
|
+
this.flush();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
this.scheduleFlush();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Force an immediate flush using `fetch` with `keepalive: true`.
|
|
561
|
+
* Returns synchronously; network errors are swallowed.
|
|
562
|
+
*/
|
|
563
|
+
flush() {
|
|
564
|
+
const i = this.drainBuffer();
|
|
565
|
+
if (i.length === 0)
|
|
566
|
+
return;
|
|
567
|
+
const e = JSON.stringify({ events: i });
|
|
568
|
+
fetch(this.buildUrl(), {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body: e,
|
|
572
|
+
keepalive: !0
|
|
573
|
+
}).catch((t) => {
|
|
574
|
+
console.log(t);
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Flush path optimized for page-unload. Prefers `navigator.sendBeacon`
|
|
579
|
+
* when available; falls back to `fetch({ keepalive: true })`.
|
|
580
|
+
*/
|
|
581
|
+
flushBeacon() {
|
|
582
|
+
const i = this.drainBuffer();
|
|
583
|
+
if (i.length === 0)
|
|
584
|
+
return;
|
|
585
|
+
const e = JSON.stringify({ events: i }), t = this.buildUrl();
|
|
586
|
+
if (typeof navigator < "u" && typeof navigator.sendBeacon == "function")
|
|
587
|
+
try {
|
|
588
|
+
const s = new Blob([e], { type: "application/json" });
|
|
589
|
+
if (navigator.sendBeacon(t, s))
|
|
590
|
+
return;
|
|
591
|
+
} catch {
|
|
592
|
+
}
|
|
593
|
+
typeof fetch < "u" && fetch(t, {
|
|
594
|
+
method: "POST",
|
|
595
|
+
headers: { "Content-Type": "application/json" },
|
|
596
|
+
body: e,
|
|
597
|
+
keepalive: !0
|
|
598
|
+
}).catch(() => {
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Remove unload listeners, clear timers, and flush anything still buffered.
|
|
603
|
+
* Call from the host component's `disconnectedCallback`.
|
|
604
|
+
*/
|
|
605
|
+
destroy() {
|
|
606
|
+
this.destroyed || (this.destroyed = !0, this.flushTimer !== null && (clearTimeout(this.flushTimer), this.flushTimer = null), z() && (window.removeEventListener("pagehide", this.boundUnloadHandler), document.removeEventListener("visibilitychange", this.boundVisibilityHandler)), this.flushBeacon());
|
|
607
|
+
}
|
|
608
|
+
scheduleFlush() {
|
|
609
|
+
this.flushTimer !== null || this.destroyed || (this.flushTimer = setTimeout(() => {
|
|
610
|
+
this.flushTimer = null, this.flush();
|
|
611
|
+
}, this.flushIntervalMs));
|
|
612
|
+
}
|
|
613
|
+
drainBuffer() {
|
|
614
|
+
if (this.flushTimer !== null && (clearTimeout(this.flushTimer), this.flushTimer = null), this.buffer.length === 0)
|
|
615
|
+
return [];
|
|
616
|
+
const i = this.buffer;
|
|
617
|
+
return this.buffer = [], i;
|
|
618
|
+
}
|
|
619
|
+
buildUrl() {
|
|
620
|
+
const i = this.endpoint.startsWith("/") ? this.endpoint : `/${this.endpoint}`;
|
|
621
|
+
return `${this.baseUrl}${i}`;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
const G = `
|
|
387
625
|
/* Chat container */
|
|
388
626
|
.chat-container {
|
|
389
627
|
display: flex;
|
|
@@ -426,6 +664,7 @@ const ee = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.
|
|
|
426
664
|
gap: var(--search-snippet-spacing-sm);
|
|
427
665
|
max-width: 85%;
|
|
428
666
|
animation: slideIn var(--search-snippet-animation-duration) ease-out;
|
|
667
|
+
animation-fill-mode: both;
|
|
429
668
|
}
|
|
430
669
|
|
|
431
670
|
@keyframes slideIn {
|
|
@@ -496,6 +735,9 @@ const ee = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.
|
|
|
496
735
|
border-radius: var(--search-snippet-border-radius);
|
|
497
736
|
word-wrap: break-word;
|
|
498
737
|
overflow-wrap: break-word;
|
|
738
|
+
/* Isolate layout cost of bubble height changes during streaming so
|
|
739
|
+
siblings don't reflow. */
|
|
740
|
+
contain: layout style;
|
|
499
741
|
}
|
|
500
742
|
|
|
501
743
|
.chat-message-user .chat-message-bubble {
|
|
@@ -1135,55 +1377,55 @@ const ee = `<svg width="32" height="10" viewBox="0 0 412 186" xmlns="http://www.
|
|
|
1135
1377
|
text-align: center;
|
|
1136
1378
|
}
|
|
1137
1379
|
`;
|
|
1138
|
-
function
|
|
1139
|
-
let i =
|
|
1140
|
-
i =
|
|
1380
|
+
function pe(l) {
|
|
1381
|
+
let i = l;
|
|
1382
|
+
i = ue(i), i = i.replace(/```([\s\S]*?)```/g, (n, o) => `<pre><code>${o.trim()}</code></pre>`);
|
|
1141
1383
|
const e = i.split(`
|
|
1142
1384
|
`), t = [];
|
|
1143
1385
|
let s = !1, r = "";
|
|
1144
|
-
for (let
|
|
1145
|
-
const
|
|
1386
|
+
for (let n = 0; n < e.length; n++) {
|
|
1387
|
+
const o = e[n], h = o.match(/^(#{1,6})\s+(.+)$/);
|
|
1146
1388
|
if (h) {
|
|
1147
|
-
const
|
|
1148
|
-
t.push(`<h${
|
|
1389
|
+
const u = h[1].length, f = h[2];
|
|
1390
|
+
t.push(`<h${u}>${x(f)}</h${u}>`);
|
|
1149
1391
|
continue;
|
|
1150
1392
|
}
|
|
1151
|
-
if (
|
|
1393
|
+
if (o.match(/^---+$/)) {
|
|
1152
1394
|
t.push("<hr />");
|
|
1153
1395
|
continue;
|
|
1154
1396
|
}
|
|
1155
|
-
if (
|
|
1156
|
-
const
|
|
1157
|
-
t.push(`<blockquote>${x(
|
|
1397
|
+
if (o.match(/^>\s+/)) {
|
|
1398
|
+
const u = o.replace(/^>\s+/, "");
|
|
1399
|
+
t.push(`<blockquote>${x(u)}</blockquote>`);
|
|
1158
1400
|
continue;
|
|
1159
1401
|
}
|
|
1160
|
-
const
|
|
1161
|
-
if (
|
|
1162
|
-
(!s || r !== "ul") && (s && t.push(`</${r}>`), t.push("<ul>"), s = !0, r = "ul"), t.push(`<li>${x(
|
|
1402
|
+
const d = o.match(/^[-*]\s+(.+)$/);
|
|
1403
|
+
if (d) {
|
|
1404
|
+
(!s || r !== "ul") && (s && t.push(`</${r}>`), t.push("<ul>"), s = !0, r = "ul"), t.push(`<li>${x(d[1])}</li>`);
|
|
1163
1405
|
continue;
|
|
1164
1406
|
}
|
|
1165
|
-
const
|
|
1166
|
-
if (
|
|
1167
|
-
(!s || r !== "ol") && (s && t.push(`</${r}>`), t.push("<ol>"), s = !0, r = "ol"), t.push(`<li>${x(
|
|
1407
|
+
const p = o.match(/^\d+\.\s+(.+)$/);
|
|
1408
|
+
if (p) {
|
|
1409
|
+
(!s || r !== "ol") && (s && t.push(`</${r}>`), t.push("<ol>"), s = !0, r = "ol"), t.push(`<li>${x(p[1])}</li>`);
|
|
1168
1410
|
continue;
|
|
1169
1411
|
}
|
|
1170
|
-
if (s && (t.push(`</${r}>`), s = !1, r = ""),
|
|
1412
|
+
if (s && (t.push(`</${r}>`), s = !1, r = ""), o.trim() === "") {
|
|
1171
1413
|
t.push("<br />");
|
|
1172
1414
|
continue;
|
|
1173
1415
|
}
|
|
1174
|
-
t.push(`<p>${x(
|
|
1416
|
+
t.push(`<p>${x(o)}</p>`);
|
|
1175
1417
|
}
|
|
1176
1418
|
return s && t.push(`</${r}>`), t.join(`
|
|
1177
1419
|
`);
|
|
1178
1420
|
}
|
|
1179
|
-
function x(
|
|
1180
|
-
let i =
|
|
1421
|
+
function x(l) {
|
|
1422
|
+
let i = l;
|
|
1181
1423
|
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(
|
|
1182
1424
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
1183
1425
|
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
1184
1426
|
), i;
|
|
1185
1427
|
}
|
|
1186
|
-
function
|
|
1428
|
+
function ue(l) {
|
|
1187
1429
|
const i = {
|
|
1188
1430
|
"&": "&",
|
|
1189
1431
|
"<": "<",
|
|
@@ -1191,9 +1433,10 @@ function ie(n) {
|
|
|
1191
1433
|
'"': """,
|
|
1192
1434
|
"'": "'"
|
|
1193
1435
|
};
|
|
1194
|
-
return
|
|
1436
|
+
return l.replace(/[&<>"']/g, (e) => i[e] || e);
|
|
1195
1437
|
}
|
|
1196
|
-
|
|
1438
|
+
const ge = 64;
|
|
1439
|
+
class Y {
|
|
1197
1440
|
constructor(i, e, t) {
|
|
1198
1441
|
a(this, "container");
|
|
1199
1442
|
a(this, "client");
|
|
@@ -1207,11 +1450,12 @@ class K {
|
|
|
1207
1450
|
a(this, "currentStreamingMessageId", null);
|
|
1208
1451
|
a(this, "loadingMessageInterval", null);
|
|
1209
1452
|
a(this, "loadingMessageIndex", 0);
|
|
1453
|
+
a(this, "pendingScrollFrame", null);
|
|
1210
1454
|
// Event handler references for cleanup
|
|
1211
1455
|
a(this, "handleInputResize", null);
|
|
1212
1456
|
a(this, "handleInputKeydown", null);
|
|
1213
1457
|
a(this, "handleSendClick", null);
|
|
1214
|
-
this.container = i, this.client = e, this.props = t, this.translations =
|
|
1458
|
+
this.container = i, this.client = e, this.props = t, this.translations = g(t.translations), this.render(), this.attachEventListeners();
|
|
1215
1459
|
}
|
|
1216
1460
|
/**
|
|
1217
1461
|
* Render the chat interface
|
|
@@ -1227,13 +1471,13 @@ class K {
|
|
|
1227
1471
|
<div class="chat-input-wrapper">
|
|
1228
1472
|
<textarea
|
|
1229
1473
|
class="chat-input"
|
|
1230
|
-
placeholder="${
|
|
1231
|
-
aria-label="${
|
|
1474
|
+
placeholder="${c(this.props.placeholder || i.chatPlaceholder)}"
|
|
1475
|
+
aria-label="${c(i.chatInputAriaLabel)}"
|
|
1232
1476
|
style="height: 40px;"
|
|
1233
1477
|
rows="1"
|
|
1234
1478
|
></textarea>
|
|
1235
|
-
<button class="button chat-send-button" aria-label="${
|
|
1236
|
-
<span>${
|
|
1479
|
+
<button class="button chat-send-button" aria-label="${c(i.sendButtonAriaLabel)}">
|
|
1480
|
+
<span>${c(i.sendButtonLabel)}</span>
|
|
1237
1481
|
</button>
|
|
1238
1482
|
</div>
|
|
1239
1483
|
</div>
|
|
@@ -1247,9 +1491,9 @@ class K {
|
|
|
1247
1491
|
<svg class="chat-empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1248
1492
|
<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
1493
|
</svg>
|
|
1250
|
-
<div class="chat-empty-title">${
|
|
1494
|
+
<div class="chat-empty-title">${c(i.chatEmptyTitle)}</div>
|
|
1251
1495
|
<div class="chat-empty-description">
|
|
1252
|
-
${
|
|
1496
|
+
${c(i.chatEmptyDescription)}
|
|
1253
1497
|
</div>
|
|
1254
1498
|
</div>
|
|
1255
1499
|
`;
|
|
@@ -1279,40 +1523,43 @@ class K {
|
|
|
1279
1523
|
* Send a message
|
|
1280
1524
|
*/
|
|
1281
1525
|
async sendMessage(i) {
|
|
1282
|
-
const e = {
|
|
1283
|
-
|
|
1526
|
+
const e = this.messages.map((o) => ({
|
|
1527
|
+
role: o.role,
|
|
1528
|
+
content: o.content
|
|
1529
|
+
})), t = this.resolveQueryRewriteOption(e), s = {
|
|
1530
|
+
id: R("msg"),
|
|
1284
1531
|
role: "user",
|
|
1285
1532
|
content: i,
|
|
1286
1533
|
timestamp: Date.now()
|
|
1287
1534
|
};
|
|
1288
|
-
this.addMessage(
|
|
1289
|
-
const
|
|
1290
|
-
id:
|
|
1535
|
+
this.addMessage(s), this.renderMessages(!0), this.setStreamingState(!0);
|
|
1536
|
+
const r = R("msg"), n = {
|
|
1537
|
+
id: r,
|
|
1291
1538
|
role: "assistant",
|
|
1292
1539
|
content: "",
|
|
1293
1540
|
timestamp: Date.now()
|
|
1294
1541
|
};
|
|
1295
|
-
this.addMessage(
|
|
1542
|
+
this.addMessage(n), this.currentStreamingMessageId = r, this.renderMessages(!0);
|
|
1296
1543
|
try {
|
|
1297
|
-
const
|
|
1298
|
-
let
|
|
1299
|
-
for await (const
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
1302
|
-
else if (
|
|
1544
|
+
const o = this.client.chat(i, { history: e, queryRewrite: t });
|
|
1545
|
+
let h = "";
|
|
1546
|
+
for await (const p of o)
|
|
1547
|
+
if (p.type === "text" && p.message)
|
|
1548
|
+
h += p.message, this.updateStreamingMessage(r, h);
|
|
1549
|
+
else if (p.type === "error") {
|
|
1303
1550
|
this.showErrorInMessage(
|
|
1304
|
-
|
|
1305
|
-
|
|
1551
|
+
r,
|
|
1552
|
+
p.message || this.translations.unknownError
|
|
1306
1553
|
);
|
|
1307
1554
|
break;
|
|
1308
1555
|
}
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
} catch (
|
|
1312
|
-
this.showErrorInMessage(
|
|
1313
|
-
|
|
1556
|
+
const d = this.messages.findIndex((p) => p.id === r);
|
|
1557
|
+
d !== -1 && (this.messages[d].content = h), this.container.dispatchEvent(b("message", { message: n }));
|
|
1558
|
+
} catch (o) {
|
|
1559
|
+
this.showErrorInMessage(r, o.message), this.container.dispatchEvent(
|
|
1560
|
+
b("error", {
|
|
1314
1561
|
error: {
|
|
1315
|
-
message:
|
|
1562
|
+
message: o.message,
|
|
1316
1563
|
code: "CHAT_ERROR"
|
|
1317
1564
|
}
|
|
1318
1565
|
})
|
|
@@ -1321,6 +1568,16 @@ class K {
|
|
|
1321
1568
|
this.setStreamingState(!1), this.renderMessages(), this.currentStreamingMessageId = null;
|
|
1322
1569
|
}
|
|
1323
1570
|
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Resolve `chatQueryRewrite` from props into the value passed to
|
|
1573
|
+
* `AISearchClient.chat`. Query rewriting is gated on having at least one
|
|
1574
|
+
* prior turn — there is nothing to rewrite against on the first message.
|
|
1575
|
+
* `chatQueryRewrite.enabled === false` opts out unconditionally.
|
|
1576
|
+
*/
|
|
1577
|
+
resolveQueryRewriteOption(i) {
|
|
1578
|
+
const e = this.props.chatQueryRewrite;
|
|
1579
|
+
return e?.enabled === !1 || i.length === 0 ? !1 : e && (e.model !== void 0 || e.rewritePrompt !== void 0) ? { model: e.model, rewritePrompt: e.rewritePrompt } : !0;
|
|
1580
|
+
}
|
|
1324
1581
|
/**
|
|
1325
1582
|
* Add a message to the chat
|
|
1326
1583
|
*/
|
|
@@ -1328,11 +1585,43 @@ class K {
|
|
|
1328
1585
|
this.messages.push(i), this.renderMessages();
|
|
1329
1586
|
}
|
|
1330
1587
|
/**
|
|
1331
|
-
* Update streaming message content
|
|
1588
|
+
* Update streaming message content.
|
|
1589
|
+
*
|
|
1590
|
+
* During streaming this performs a surgical DOM update on the streaming
|
|
1591
|
+
* bubble only — no full message-list re-render. Content is written as plain
|
|
1592
|
+
* text (escaped via textContent) to avoid markdown structural flips that
|
|
1593
|
+
* cause height jumps. Markdown is applied once the stream completes via the
|
|
1594
|
+
* final `renderMessages()` call in `sendMessage`'s `finally` block.
|
|
1332
1595
|
*/
|
|
1333
1596
|
updateStreamingMessage(i, e) {
|
|
1334
1597
|
const t = this.messages.findIndex((s) => s.id === i);
|
|
1335
|
-
t !== -1 && (this.messages[t].content = e, this.renderMessages(!0));
|
|
1598
|
+
t !== -1 && (this.messages[t].content = e, this.updateStreamingMessageDOM(i, e) || this.renderMessages(!0));
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Surgically update the streaming bubble's text node. Returns true on
|
|
1602
|
+
* success, false if the target nodes weren't found (caller should fall back
|
|
1603
|
+
* to a full re-render).
|
|
1604
|
+
*/
|
|
1605
|
+
updateStreamingMessageDOM(i, e) {
|
|
1606
|
+
if (!this.messagesContainer)
|
|
1607
|
+
return !1;
|
|
1608
|
+
const t = this.messagesContainer.querySelector(
|
|
1609
|
+
`[data-message-id="${CSS.escape(i)}"]`
|
|
1610
|
+
);
|
|
1611
|
+
if (!t)
|
|
1612
|
+
return !1;
|
|
1613
|
+
const s = t.querySelector(".chat-message-bubble");
|
|
1614
|
+
if (!s)
|
|
1615
|
+
return !1;
|
|
1616
|
+
const r = this.isNearBottom();
|
|
1617
|
+
let n = s.querySelector(".chat-message-text");
|
|
1618
|
+
if (!n) {
|
|
1619
|
+
const o = document.createElement("div");
|
|
1620
|
+
o.className = "chat-message-text";
|
|
1621
|
+
const h = s.querySelector(".chat-streaming");
|
|
1622
|
+
h ? s.insertBefore(o, h) : s.appendChild(o), n = o;
|
|
1623
|
+
}
|
|
1624
|
+
return n.textContent = e, r && this.scheduleScrollToBottom(), !0;
|
|
1336
1625
|
}
|
|
1337
1626
|
/**
|
|
1338
1627
|
* Show error in message
|
|
@@ -1342,7 +1631,9 @@ class K {
|
|
|
1342
1631
|
t !== -1 && (this.messages[t].content = `${this.translations.errorPrefix} ${e}`, this.renderMessages());
|
|
1343
1632
|
}
|
|
1344
1633
|
/**
|
|
1345
|
-
* Render all messages
|
|
1634
|
+
* Render all messages. Forces a scroll to bottom regardless of current
|
|
1635
|
+
* scroll position; called only on full-list mutations (add, clear, set,
|
|
1636
|
+
* end-of-stream final render, prop changes), not on per-token updates.
|
|
1346
1637
|
*/
|
|
1347
1638
|
renderMessages(i = !1) {
|
|
1348
1639
|
if (!this.messagesContainer) return;
|
|
@@ -1353,49 +1644,79 @@ class K {
|
|
|
1353
1644
|
const e = this.messages.map(
|
|
1354
1645
|
(t) => this.renderMessage(t, i && t.id === this.currentStreamingMessageId)
|
|
1355
1646
|
).join("");
|
|
1356
|
-
this.messagesContainer.innerHTML = e, this.
|
|
1647
|
+
this.messagesContainer.innerHTML = e, this.scheduleScrollToBottom();
|
|
1357
1648
|
}
|
|
1358
1649
|
/**
|
|
1359
1650
|
* Render a single message
|
|
1360
1651
|
*/
|
|
1361
1652
|
renderMessage(i, e = !1) {
|
|
1362
|
-
const t = this.translations, s = `chat-message-${i.role}`, r = i.role === "user" ? t.userAvatar : t.assistantAvatar,
|
|
1653
|
+
const t = this.translations, s = `chat-message-${i.role}`, r = i.role === "user" ? t.userAvatar : t.assistantAvatar, n = t.loadingMessages[this.loadingMessageIndex] ?? "", o = i.content ? e ? c(i.content) : pe(i.content) : "";
|
|
1363
1654
|
return `
|
|
1364
|
-
<div class="chat-message ${s}">
|
|
1365
|
-
<div class="chat-message-avatar">${
|
|
1655
|
+
<div class="chat-message ${s}" data-message-id="${c(i.id)}">
|
|
1656
|
+
<div class="chat-message-avatar">${c(r)}</div>
|
|
1366
1657
|
<div class="chat-message-content">
|
|
1367
1658
|
<div class="chat-message-bubble">
|
|
1368
|
-
${
|
|
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">${
|
|
1659
|
+
${o ? `<div class="chat-message-text">${o}</div>` : ""}
|
|
1660
|
+
${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">${c(n)}</span></div>` : ""}
|
|
1370
1661
|
</div>
|
|
1371
1662
|
<div class="chat-message-metadata">
|
|
1372
|
-
<span class="chat-message-time">${
|
|
1663
|
+
<span class="chat-message-time">${c(X(i.timestamp, this.translations))}</span>
|
|
1373
1664
|
</div>
|
|
1374
1665
|
</div>
|
|
1375
1666
|
</div>
|
|
1376
1667
|
`;
|
|
1377
1668
|
}
|
|
1378
1669
|
/**
|
|
1379
|
-
*
|
|
1670
|
+
* True when the user is within `BOTTOM_FOLLOW_THRESHOLD_PX` of the bottom
|
|
1671
|
+
* of the messages list. Used to decide whether to auto-follow the stream
|
|
1672
|
+
* or leave the user where they scrolled.
|
|
1380
1673
|
*/
|
|
1381
|
-
|
|
1382
|
-
this.messagesContainer
|
|
1383
|
-
|
|
1384
|
-
|
|
1674
|
+
isNearBottom() {
|
|
1675
|
+
const i = this.messagesContainer;
|
|
1676
|
+
return i ? i.scrollHeight - i.scrollTop - i.clientHeight < ge : !0;
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Schedule a single scroll-to-bottom on the next animation frame. Multiple
|
|
1680
|
+
* calls within the same frame are coalesced into one DOM write. The "should
|
|
1681
|
+
* I scroll?" decision is gated by callers — `updateStreamingMessageDOM`
|
|
1682
|
+
* only calls this when the user is near the bottom; full re-renders always
|
|
1683
|
+
* call it.
|
|
1684
|
+
*/
|
|
1685
|
+
scheduleScrollToBottom() {
|
|
1686
|
+
this.messagesContainer && this.pendingScrollFrame === null && (this.pendingScrollFrame = requestAnimationFrame(() => {
|
|
1687
|
+
this.pendingScrollFrame = null;
|
|
1688
|
+
const i = this.messagesContainer;
|
|
1689
|
+
i && (i.scrollTop = i.scrollHeight);
|
|
1690
|
+
}));
|
|
1385
1691
|
}
|
|
1386
1692
|
/**
|
|
1387
1693
|
* Set streaming state
|
|
1388
1694
|
*/
|
|
1389
1695
|
setStreamingState(i) {
|
|
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>${
|
|
1696
|
+
this.isStreaming = i, this.inputElement && (this.inputElement.disabled = i), this.sendButton && (this.sendButton.disabled = i, this.sendButton.innerHTML = i ? '<div class="loading"></div>' : `<span>${c(this.translations.sendButtonLabel)}</span>`), i ? this.startLoadingMessages() : this.clearLoadingMessages();
|
|
1391
1697
|
}
|
|
1392
1698
|
startLoadingMessages() {
|
|
1393
1699
|
this.clearLoadingMessages();
|
|
1394
1700
|
const i = this.translations.loadingMessages;
|
|
1395
1701
|
this.loadingMessageIndex = Math.floor(Math.random() * i.length), this.loadingMessageInterval = setInterval(() => {
|
|
1396
1702
|
const e = this.translations.loadingMessages;
|
|
1397
|
-
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length, this.isStreaming
|
|
1398
|
-
|
|
1703
|
+
if (this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length, !this.isStreaming) return;
|
|
1704
|
+
this.updateLoadingTextDOM(e[this.loadingMessageIndex] ?? "") || this.renderMessages(!0);
|
|
1705
|
+
}, A);
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Update the rotating loading-text label inside the currently-streaming
|
|
1709
|
+
* bubble only. Returns true on success, false if the target wasn't found.
|
|
1710
|
+
*/
|
|
1711
|
+
updateLoadingTextDOM(i) {
|
|
1712
|
+
if (!this.messagesContainer || !this.currentStreamingMessageId)
|
|
1713
|
+
return !1;
|
|
1714
|
+
const e = this.messagesContainer.querySelector(
|
|
1715
|
+
`[data-message-id="${CSS.escape(this.currentStreamingMessageId)}"]`
|
|
1716
|
+
);
|
|
1717
|
+
if (!e) return !1;
|
|
1718
|
+
const t = e.querySelector(".chat-streaming .loading-text");
|
|
1719
|
+
return t ? (t.textContent = i, !0) : !1;
|
|
1399
1720
|
}
|
|
1400
1721
|
clearLoadingMessages() {
|
|
1401
1722
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
@@ -1426,7 +1747,7 @@ class K {
|
|
|
1426
1747
|
* newly rendered elements.
|
|
1427
1748
|
*/
|
|
1428
1749
|
setProps(i) {
|
|
1429
|
-
this.props = i, this.translations =
|
|
1750
|
+
this.props = i, this.translations = g(i.translations), this.detachEventListeners(), this.render(), this.attachEventListeners(), this.renderMessages(this.isStreaming), this.isStreaming && this.setStreamingState(!0);
|
|
1430
1751
|
}
|
|
1431
1752
|
detachEventListeners() {
|
|
1432
1753
|
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;
|
|
@@ -1435,11 +1756,11 @@ class K {
|
|
|
1435
1756
|
* Destroy and cleanup
|
|
1436
1757
|
*/
|
|
1437
1758
|
destroy() {
|
|
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;
|
|
1759
|
+
this.clearLoadingMessages(), this.pendingScrollFrame !== null && (cancelAnimationFrame(this.pendingScrollFrame), this.pendingScrollFrame = null), 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;
|
|
1439
1760
|
}
|
|
1440
1761
|
}
|
|
1441
|
-
const
|
|
1442
|
-
class
|
|
1762
|
+
const $ = "chat-bubble-snippet";
|
|
1763
|
+
class me extends HTMLElement {
|
|
1443
1764
|
constructor() {
|
|
1444
1765
|
super();
|
|
1445
1766
|
a(this, "shadow");
|
|
@@ -1449,7 +1770,8 @@ class re extends HTMLElement {
|
|
|
1449
1770
|
a(this, "isExpanded", !1);
|
|
1450
1771
|
a(this, "isMinimized", !1);
|
|
1451
1772
|
a(this, "translationsOverride", null);
|
|
1452
|
-
a(this, "resolvedTranslations",
|
|
1773
|
+
a(this, "resolvedTranslations", g(null));
|
|
1774
|
+
a(this, "chatQueryRewriteOverride", null);
|
|
1453
1775
|
// Event handler references for cleanup
|
|
1454
1776
|
a(this, "handleBubbleClick", null);
|
|
1455
1777
|
a(this, "handleCloseClick", null);
|
|
@@ -1458,10 +1780,17 @@ class re extends HTMLElement {
|
|
|
1458
1780
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
1459
1781
|
}
|
|
1460
1782
|
static get observedAttributes() {
|
|
1461
|
-
return [
|
|
1783
|
+
return [
|
|
1784
|
+
"api-url",
|
|
1785
|
+
"placeholder",
|
|
1786
|
+
"theme",
|
|
1787
|
+
"hide-branding",
|
|
1788
|
+
"translations",
|
|
1789
|
+
"chat-query-rewrite"
|
|
1790
|
+
];
|
|
1462
1791
|
}
|
|
1463
1792
|
connectedCallback() {
|
|
1464
|
-
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.dispatchEvent(
|
|
1793
|
+
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.dispatchEvent(b("ready", void 0));
|
|
1465
1794
|
}
|
|
1466
1795
|
disconnectedCallback() {
|
|
1467
1796
|
this.cleanup();
|
|
@@ -1475,22 +1804,32 @@ class re extends HTMLElement {
|
|
|
1475
1804
|
get translations() {
|
|
1476
1805
|
return this.translationsOverride;
|
|
1477
1806
|
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Override AI Search query rewriting on subsequent chat turns. Setting
|
|
1809
|
+
* `null` falls back to parsing the `chat-query-rewrite` attribute.
|
|
1810
|
+
*/
|
|
1811
|
+
get chatQueryRewrite() {
|
|
1812
|
+
return this.chatQueryRewriteOverride;
|
|
1813
|
+
}
|
|
1814
|
+
set chatQueryRewrite(e) {
|
|
1815
|
+
this.chatQueryRewriteOverride = e ?? null, this.chatView && this.chatView.setProps(this.getProps());
|
|
1816
|
+
}
|
|
1478
1817
|
/**
|
|
1479
1818
|
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
1480
1819
|
*/
|
|
1481
1820
|
set translations(e) {
|
|
1482
|
-
this.translationsOverride = e ?? null, this.resolvedTranslations =
|
|
1821
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = g(this.translationsOverride), this.isConnected && this.rerenderAfterTranslationsChange();
|
|
1483
1822
|
}
|
|
1484
1823
|
syncTranslationsFromAttribute() {
|
|
1485
1824
|
if (this.translationsOverride) {
|
|
1486
|
-
this.resolvedTranslations =
|
|
1825
|
+
this.resolvedTranslations = g(this.translationsOverride);
|
|
1487
1826
|
return;
|
|
1488
1827
|
}
|
|
1489
1828
|
const e = k(
|
|
1490
1829
|
this.getAttribute("translations"),
|
|
1491
1830
|
"ChatBubbleSnippet"
|
|
1492
1831
|
);
|
|
1493
|
-
this.resolvedTranslations =
|
|
1832
|
+
this.resolvedTranslations = g(e);
|
|
1494
1833
|
}
|
|
1495
1834
|
rerenderAfterTranslationsChange() {
|
|
1496
1835
|
const e = this.isExpanded, t = this.isMinimized;
|
|
@@ -1499,20 +1838,27 @@ class re extends HTMLElement {
|
|
|
1499
1838
|
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
1839
|
const r = this.shadow.querySelector(".chat-window");
|
|
1501
1840
|
if (this.chatView && s && r) {
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1841
|
+
const n = r.querySelector(".chat-content");
|
|
1842
|
+
n ? r.replaceChild(s, n) : r.appendChild(s), this.chatView.setProps(this.getProps());
|
|
1504
1843
|
} else e && this.initializeChatView();
|
|
1505
1844
|
}
|
|
1506
1845
|
getProps() {
|
|
1507
1846
|
const e = this.resolvedTranslations;
|
|
1508
1847
|
return {
|
|
1509
|
-
apiUrl:
|
|
1510
|
-
placeholder:
|
|
1511
|
-
theme:
|
|
1512
|
-
hideBranding:
|
|
1513
|
-
translations: this.translationsOverride ?? void 0
|
|
1848
|
+
apiUrl: m(this.getAttribute("api-url"), ""),
|
|
1849
|
+
placeholder: m(this.getAttribute("placeholder"), e.chatPlaceholder),
|
|
1850
|
+
theme: m(this.getAttribute("theme"), "auto"),
|
|
1851
|
+
hideBranding: v(this.getAttribute("hide-branding"), !1),
|
|
1852
|
+
translations: this.translationsOverride ?? void 0,
|
|
1853
|
+
chatQueryRewrite: this.resolveChatQueryRewrite()
|
|
1514
1854
|
};
|
|
1515
1855
|
}
|
|
1856
|
+
resolveChatQueryRewrite() {
|
|
1857
|
+
return this.chatQueryRewriteOverride !== null ? this.chatQueryRewriteOverride : F(
|
|
1858
|
+
this.getAttribute("chat-query-rewrite"),
|
|
1859
|
+
"ChatBubbleSnippet"
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1516
1862
|
initializeClient() {
|
|
1517
1863
|
const e = this.getProps();
|
|
1518
1864
|
if (!e.apiUrl) {
|
|
@@ -1528,7 +1874,7 @@ class re extends HTMLElement {
|
|
|
1528
1874
|
render() {
|
|
1529
1875
|
const e = document.createElement("style");
|
|
1530
1876
|
e.textContent = `${M}
|
|
1531
|
-
${
|
|
1877
|
+
${G}
|
|
1532
1878
|
${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();
|
|
1533
1879
|
}
|
|
1534
1880
|
getBubbleStyles() {
|
|
@@ -1675,7 +2021,7 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1675
2021
|
getBaseHTML() {
|
|
1676
2022
|
const e = this.getProps(), t = this.resolvedTranslations, s = e.hideBranding ? "" : `<div class="powered-by">${E}</div>`;
|
|
1677
2023
|
return `
|
|
1678
|
-
<button class="bubble-button" aria-label="${
|
|
2024
|
+
<button class="bubble-button" aria-label="${c(t.openChatAriaLabel)}">
|
|
1679
2025
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1680
2026
|
<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>
|
|
1681
2027
|
</svg>
|
|
@@ -1686,21 +2032,21 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1686
2032
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1687
2033
|
<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>
|
|
1688
2034
|
</svg>
|
|
1689
|
-
<span>${
|
|
2035
|
+
<span>${c(t.chatTitle)}</span>
|
|
1690
2036
|
</div>
|
|
1691
2037
|
<div class="chat-header-actions">
|
|
1692
|
-
<button class="icon-button clear-button" aria-label="${
|
|
2038
|
+
<button class="icon-button clear-button" aria-label="${c(t.clearHistoryAriaLabel)}">
|
|
1693
2039
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1694
2040
|
<polyline points="3 6 5 6 21 6"></polyline>
|
|
1695
2041
|
<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>
|
|
1696
2042
|
</svg>
|
|
1697
2043
|
</button>
|
|
1698
|
-
<button class="icon-button minimize-button" aria-label="${
|
|
2044
|
+
<button class="icon-button minimize-button" aria-label="${c(t.minimizeAriaLabel)}">
|
|
1699
2045
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1700
2046
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
1701
2047
|
</svg>
|
|
1702
2048
|
</button>
|
|
1703
|
-
<button class="icon-button close-button" aria-label="${
|
|
2049
|
+
<button class="icon-button close-button" aria-label="${c(t.closeAriaLabel)}">
|
|
1704
2050
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1705
2051
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
1706
2052
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
@@ -1744,13 +2090,13 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1744
2090
|
const s = this.resolvedTranslations;
|
|
1745
2091
|
e.innerHTML = `
|
|
1746
2092
|
<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);">
|
|
1747
|
-
<strong>${
|
|
2093
|
+
<strong>${c(s.errorPrefix)}</strong> ${c(s.missingApiUrlError)}
|
|
1748
2094
|
</div>
|
|
1749
2095
|
`;
|
|
1750
2096
|
return;
|
|
1751
2097
|
}
|
|
1752
2098
|
const t = this.getProps();
|
|
1753
|
-
this.chatView = new
|
|
2099
|
+
this.chatView = new Y(e, this.client, t);
|
|
1754
2100
|
}
|
|
1755
2101
|
updateTheme(e) {
|
|
1756
2102
|
(e === "light" || e === "dark" ? e : null) === null && this.hasAttribute("theme") && this.getAttribute("theme") !== "auto" && this.removeAttribute("theme");
|
|
@@ -1769,9 +2115,9 @@ ${this.getBubbleStyles()}`, this.container = document.createElement("div"), this
|
|
|
1769
2115
|
return this.chatView?.getMessages() || [];
|
|
1770
2116
|
}
|
|
1771
2117
|
}
|
|
1772
|
-
customElements.get(
|
|
1773
|
-
const
|
|
1774
|
-
class
|
|
2118
|
+
customElements.get($) || customElements.define($, me);
|
|
2119
|
+
const q = "chat-page-snippet", B = "chat-page-sessions";
|
|
2120
|
+
class ve extends HTMLElement {
|
|
1775
2121
|
constructor() {
|
|
1776
2122
|
super();
|
|
1777
2123
|
a(this, "shadow");
|
|
@@ -1782,7 +2128,8 @@ class ae extends HTMLElement {
|
|
|
1782
2128
|
a(this, "currentSessionId", null);
|
|
1783
2129
|
a(this, "sidebarCollapsed", !1);
|
|
1784
2130
|
a(this, "translationsOverride", null);
|
|
1785
|
-
a(this, "resolvedTranslations",
|
|
2131
|
+
a(this, "resolvedTranslations", g(null));
|
|
2132
|
+
a(this, "chatQueryRewriteOverride", null);
|
|
1786
2133
|
// Event handler references for cleanup
|
|
1787
2134
|
a(this, "handleClearClick", null);
|
|
1788
2135
|
a(this, "handleNewChatClick", null);
|
|
@@ -1792,10 +2139,17 @@ class ae extends HTMLElement {
|
|
|
1792
2139
|
this.shadow = this.attachShadow({ mode: "open" }), this.loadSessions();
|
|
1793
2140
|
}
|
|
1794
2141
|
static get observedAttributes() {
|
|
1795
|
-
return [
|
|
2142
|
+
return [
|
|
2143
|
+
"api-url",
|
|
2144
|
+
"placeholder",
|
|
2145
|
+
"theme",
|
|
2146
|
+
"hide-branding",
|
|
2147
|
+
"translations",
|
|
2148
|
+
"chat-query-rewrite"
|
|
2149
|
+
];
|
|
1796
2150
|
}
|
|
1797
2151
|
connectedCallback() {
|
|
1798
|
-
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.setupView(), this.dispatchEvent(
|
|
2152
|
+
this.syncTranslationsFromAttribute(), this.render(), this.initializeClient(), this.setupView(), this.dispatchEvent(b("ready", void 0));
|
|
1799
2153
|
}
|
|
1800
2154
|
disconnectedCallback() {
|
|
1801
2155
|
this.saveCurrentSession(), this.cleanup();
|
|
@@ -1809,19 +2163,29 @@ class ae extends HTMLElement {
|
|
|
1809
2163
|
get translations() {
|
|
1810
2164
|
return this.translationsOverride;
|
|
1811
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Override AI Search query rewriting on subsequent chat turns. Setting
|
|
2168
|
+
* `null` falls back to parsing the `chat-query-rewrite` attribute.
|
|
2169
|
+
*/
|
|
2170
|
+
get chatQueryRewrite() {
|
|
2171
|
+
return this.chatQueryRewriteOverride;
|
|
2172
|
+
}
|
|
2173
|
+
set chatQueryRewrite(e) {
|
|
2174
|
+
this.chatQueryRewriteOverride = e ?? null, this.chatView && this.chatView.setProps(this.getProps());
|
|
2175
|
+
}
|
|
1812
2176
|
/**
|
|
1813
2177
|
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
1814
2178
|
*/
|
|
1815
2179
|
set translations(e) {
|
|
1816
|
-
this.translationsOverride = e ?? null, this.resolvedTranslations =
|
|
2180
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = g(this.translationsOverride), this.refreshDefaultSessionTitles(), this.isConnected && this.rerenderAfterTranslationsChange();
|
|
1817
2181
|
}
|
|
1818
2182
|
syncTranslationsFromAttribute() {
|
|
1819
2183
|
if (this.translationsOverride) {
|
|
1820
|
-
this.resolvedTranslations =
|
|
2184
|
+
this.resolvedTranslations = g(this.translationsOverride), this.refreshDefaultSessionTitles();
|
|
1821
2185
|
return;
|
|
1822
2186
|
}
|
|
1823
2187
|
const e = k(this.getAttribute("translations"), "ChatPageSnippet");
|
|
1824
|
-
this.resolvedTranslations =
|
|
2188
|
+
this.resolvedTranslations = g(e), this.refreshDefaultSessionTitles();
|
|
1825
2189
|
}
|
|
1826
2190
|
/**
|
|
1827
2191
|
* Replace the stored title of any still-default-titled session with the
|
|
@@ -1851,13 +2215,20 @@ class ae extends HTMLElement {
|
|
|
1851
2215
|
getProps() {
|
|
1852
2216
|
const e = this.resolvedTranslations;
|
|
1853
2217
|
return {
|
|
1854
|
-
apiUrl:
|
|
1855
|
-
placeholder:
|
|
1856
|
-
theme:
|
|
1857
|
-
hideBranding:
|
|
1858
|
-
translations: this.translationsOverride ?? void 0
|
|
2218
|
+
apiUrl: m(this.getAttribute("api-url"), ""),
|
|
2219
|
+
placeholder: m(this.getAttribute("placeholder"), e.chatPlaceholder),
|
|
2220
|
+
theme: m(this.getAttribute("theme"), "auto"),
|
|
2221
|
+
hideBranding: v(this.getAttribute("hide-branding"), !1),
|
|
2222
|
+
translations: this.translationsOverride ?? void 0,
|
|
2223
|
+
chatQueryRewrite: this.resolveChatQueryRewrite()
|
|
1859
2224
|
};
|
|
1860
2225
|
}
|
|
2226
|
+
resolveChatQueryRewrite() {
|
|
2227
|
+
return this.chatQueryRewriteOverride !== null ? this.chatQueryRewriteOverride : F(
|
|
2228
|
+
this.getAttribute("chat-query-rewrite"),
|
|
2229
|
+
"ChatPageSnippet"
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
1861
2232
|
initializeClient() {
|
|
1862
2233
|
const e = this.getProps();
|
|
1863
2234
|
if (!e.apiUrl) {
|
|
@@ -1873,7 +2244,7 @@ class ae extends HTMLElement {
|
|
|
1873
2244
|
render() {
|
|
1874
2245
|
const e = document.createElement("style");
|
|
1875
2246
|
e.textContent = `${M}
|
|
1876
|
-
${
|
|
2247
|
+
${G}
|
|
1877
2248
|
${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();
|
|
1878
2249
|
}
|
|
1879
2250
|
getPageStyles() {
|
|
@@ -2157,13 +2528,13 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2157
2528
|
return `
|
|
2158
2529
|
<div class="chat-sidebar">
|
|
2159
2530
|
<div class="sidebar-header">
|
|
2160
|
-
<span class="sidebar-title">${
|
|
2531
|
+
<span class="sidebar-title">${c(t.historyTitle)}</span>
|
|
2161
2532
|
</div>
|
|
2162
2533
|
<button class="new-chat-button">
|
|
2163
2534
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2164
2535
|
<path d="M12 5v14M5 12h14"></path>
|
|
2165
2536
|
</svg>
|
|
2166
|
-
${
|
|
2537
|
+
${c(t.newChatButton)}
|
|
2167
2538
|
</button>
|
|
2168
2539
|
<div class="chat-list"></div>
|
|
2169
2540
|
${s}
|
|
@@ -2171,7 +2542,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2171
2542
|
<div class="chat-main">
|
|
2172
2543
|
<div class="chat-page-header">
|
|
2173
2544
|
<div class="chat-page-header-left">
|
|
2174
|
-
<button class="toggle-sidebar-button" title="${
|
|
2545
|
+
<button class="toggle-sidebar-button" title="${c(t.toggleSidebarTitle)}">
|
|
2175
2546
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2176
2547
|
<path d="M3 12h18M3 6h18M3 18h18"></path>
|
|
2177
2548
|
</svg>
|
|
@@ -2180,7 +2551,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2180
2551
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2181
2552
|
<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>
|
|
2182
2553
|
</svg>
|
|
2183
|
-
<span>${
|
|
2554
|
+
<span>${c(t.chatTitle)}</span>
|
|
2184
2555
|
</div>
|
|
2185
2556
|
</div>
|
|
2186
2557
|
<div class="chat-page-header-actions">
|
|
@@ -2188,7 +2559,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2188
2559
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2189
2560
|
<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>
|
|
2190
2561
|
</svg>
|
|
2191
|
-
${
|
|
2562
|
+
${c(t.clearChatButton)}
|
|
2192
2563
|
</button>
|
|
2193
2564
|
</div>
|
|
2194
2565
|
</div>
|
|
@@ -2200,11 +2571,11 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2200
2571
|
}
|
|
2201
2572
|
attachEventListeners() {
|
|
2202
2573
|
const e = this.shadow.querySelector(".clear-button"), t = this.shadow.querySelector(".new-chat-button"), s = this.shadow.querySelector(".toggle-sidebar-button"), r = this.shadow.querySelector(".chat-list");
|
|
2203
|
-
this.handleClearClick = () => this.clearCurrentChat(), this.handleNewChatClick = () => this.createNewChat(), this.handleToggleSidebarClick = () => this.toggleSidebar(), this.handleChatListClick = (
|
|
2574
|
+
this.handleClearClick = () => this.clearCurrentChat(), this.handleNewChatClick = () => this.createNewChat(), this.handleToggleSidebarClick = () => this.toggleSidebar(), this.handleChatListClick = (n) => this.onChatListClick(n), e?.addEventListener("click", this.handleClearClick), t?.addEventListener("click", this.handleNewChatClick), s?.addEventListener("click", this.handleToggleSidebarClick), r?.addEventListener("click", this.handleChatListClick);
|
|
2204
2575
|
}
|
|
2205
2576
|
removeEventListeners() {
|
|
2206
|
-
const e = this.shadow.querySelector(".clear-button"), t = this.shadow.querySelector(".new-chat-button"), s = this.shadow.querySelector(".toggle-sidebar-button"), r = this.shadow.querySelector(".chat-list"),
|
|
2207
|
-
this.handleClearClick && e?.removeEventListener("click", this.handleClearClick), this.handleNewChatClick && t?.removeEventListener("click", this.handleNewChatClick), this.handleToggleSidebarClick && s?.removeEventListener("click", this.handleToggleSidebarClick), this.handleChatListClick && r?.removeEventListener("click", this.handleChatListClick), this.handleMessageEvent &&
|
|
2577
|
+
const e = this.shadow.querySelector(".clear-button"), t = this.shadow.querySelector(".new-chat-button"), s = this.shadow.querySelector(".toggle-sidebar-button"), r = this.shadow.querySelector(".chat-list"), n = this.shadow.querySelector(".container");
|
|
2578
|
+
this.handleClearClick && e?.removeEventListener("click", this.handleClearClick), this.handleNewChatClick && t?.removeEventListener("click", this.handleNewChatClick), this.handleToggleSidebarClick && s?.removeEventListener("click", this.handleToggleSidebarClick), this.handleChatListClick && r?.removeEventListener("click", this.handleChatListClick), this.handleMessageEvent && n && n.removeEventListener("message", this.handleMessageEvent), this.handleClearClick = null, this.handleNewChatClick = null, this.handleToggleSidebarClick = null, this.handleChatListClick = null, this.handleMessageEvent = null;
|
|
2208
2579
|
}
|
|
2209
2580
|
setupView() {
|
|
2210
2581
|
const e = this.shadow.querySelector(".container");
|
|
@@ -2213,7 +2584,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2213
2584
|
const s = this.resolvedTranslations;
|
|
2214
2585
|
e.innerHTML = `
|
|
2215
2586
|
<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);">
|
|
2216
|
-
<strong>${
|
|
2587
|
+
<strong>${c(s.errorPrefix)}</strong> ${c(s.missingApiUrlError)}
|
|
2217
2588
|
</div>
|
|
2218
2589
|
`;
|
|
2219
2590
|
}
|
|
@@ -2221,7 +2592,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2221
2592
|
}
|
|
2222
2593
|
if (!e) return;
|
|
2223
2594
|
const t = this.getProps();
|
|
2224
|
-
if (this.chatView = new
|
|
2595
|
+
if (this.chatView = new Y(e, this.client, t), this.sessions.length === 0)
|
|
2225
2596
|
this.createNewChat();
|
|
2226
2597
|
else {
|
|
2227
2598
|
const s = this.sessions[0];
|
|
@@ -2236,7 +2607,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2236
2607
|
}
|
|
2237
2608
|
loadSessions() {
|
|
2238
2609
|
try {
|
|
2239
|
-
const e = localStorage.getItem(
|
|
2610
|
+
const e = localStorage.getItem(B);
|
|
2240
2611
|
e && (this.sessions = JSON.parse(e), this.sessions.sort((t, s) => s.updatedAt - t.updatedAt));
|
|
2241
2612
|
} catch (e) {
|
|
2242
2613
|
console.error("Failed to load chat sessions:", e);
|
|
@@ -2244,7 +2615,7 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2244
2615
|
}
|
|
2245
2616
|
saveSessions() {
|
|
2246
2617
|
try {
|
|
2247
|
-
localStorage.setItem(
|
|
2618
|
+
localStorage.setItem(B, JSON.stringify(this.sessions));
|
|
2248
2619
|
} catch (e) {
|
|
2249
2620
|
console.error("Failed to save chat sessions:", e);
|
|
2250
2621
|
}
|
|
@@ -2295,14 +2666,14 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2295
2666
|
const t = e.target, s = t.closest(".chat-list-item-delete");
|
|
2296
2667
|
if (s) {
|
|
2297
2668
|
e.stopPropagation();
|
|
2298
|
-
const
|
|
2299
|
-
|
|
2669
|
+
const n = s.getAttribute("data-session-id");
|
|
2670
|
+
n && this.deleteSession(n);
|
|
2300
2671
|
return;
|
|
2301
2672
|
}
|
|
2302
2673
|
const r = t.closest(".chat-list-item");
|
|
2303
2674
|
if (r) {
|
|
2304
|
-
const
|
|
2305
|
-
|
|
2675
|
+
const n = r.getAttribute("data-session-id");
|
|
2676
|
+
n && this.switchToSession(n);
|
|
2306
2677
|
}
|
|
2307
2678
|
}
|
|
2308
2679
|
renderChatList() {
|
|
@@ -2332,8 +2703,8 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2332
2703
|
`;
|
|
2333
2704
|
}
|
|
2334
2705
|
formatDate(e) {
|
|
2335
|
-
const t = new Date(e), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime(),
|
|
2336
|
-
return
|
|
2706
|
+
const t = new Date(e), r = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), n = Math.floor(r / (1e3 * 60 * 60 * 24));
|
|
2707
|
+
return n === 0 ? t.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" }) : n === 1 ? this.resolvedTranslations.yesterday : n < 7 ? t.toLocaleDateString(void 0, { weekday: "long" }) : t.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
2337
2708
|
}
|
|
2338
2709
|
escapeHTML(e) {
|
|
2339
2710
|
const t = document.createElement("div");
|
|
@@ -2362,8 +2733,8 @@ ${this.getPageStyles()}`, this.container = document.createElement("div"), this.c
|
|
|
2362
2733
|
return this.sessions.find((e) => e.id === this.currentSessionId) || null;
|
|
2363
2734
|
}
|
|
2364
2735
|
}
|
|
2365
|
-
customElements.get(
|
|
2366
|
-
const
|
|
2736
|
+
customElements.get(q) || customElements.define(q, ve);
|
|
2737
|
+
const fe = `
|
|
2367
2738
|
/* Search view states */
|
|
2368
2739
|
.search-view {
|
|
2369
2740
|
transition: var(--search-snippet-transition-slow);
|
|
@@ -2733,12 +3104,13 @@ a.search-result-item:focus-visible {
|
|
|
2733
3104
|
border-radius: 2px;
|
|
2734
3105
|
font-weight: var(--search-snippet-font-weight-medium);
|
|
2735
3106
|
}
|
|
2736
|
-
`,
|
|
2737
|
-
class
|
|
3107
|
+
`, O = "search-bar-snippet", H = 10, N = 50;
|
|
3108
|
+
class be extends HTMLElement {
|
|
2738
3109
|
constructor() {
|
|
2739
3110
|
super();
|
|
2740
3111
|
a(this, "shadow");
|
|
2741
3112
|
a(this, "client", null);
|
|
3113
|
+
a(this, "stats", null);
|
|
2742
3114
|
a(this, "container", null);
|
|
2743
3115
|
a(this, "inputElement", null);
|
|
2744
3116
|
a(this, "resultsContainer", null);
|
|
@@ -2748,12 +3120,18 @@ class oe extends HTMLElement {
|
|
|
2748
3120
|
a(this, "loadingMessageInterval", null);
|
|
2749
3121
|
a(this, "loadingMessageIndex", 0);
|
|
2750
3122
|
a(this, "translationsOverride", null);
|
|
2751
|
-
a(this, "resolvedTranslations",
|
|
3123
|
+
a(this, "resolvedTranslations", g(null));
|
|
3124
|
+
// Analytics context: captures the most recently completed search so that
|
|
3125
|
+
// click / view-more events report the same query + total.
|
|
3126
|
+
a(this, "lastSearchQuery", "");
|
|
3127
|
+
a(this, "lastSearchTotal", 0);
|
|
2752
3128
|
// Event handler references for cleanup
|
|
2753
3129
|
a(this, "handleInputChange", null);
|
|
2754
3130
|
a(this, "handleInputKeydownEnter", null);
|
|
2755
3131
|
a(this, "handleInputKeydownEscape", null);
|
|
2756
3132
|
a(this, "handleSearchButtonClick", null);
|
|
3133
|
+
a(this, "handleResultClick", null);
|
|
3134
|
+
a(this, "handleSeeMoreClick", null);
|
|
2757
3135
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
2758
3136
|
}
|
|
2759
3137
|
static get observedAttributes() {
|
|
@@ -2769,18 +3147,19 @@ class oe extends HTMLElement {
|
|
|
2769
3147
|
"show-date",
|
|
2770
3148
|
"hide-thumbnails",
|
|
2771
3149
|
"see-more",
|
|
3150
|
+
"disable-analytics",
|
|
2772
3151
|
"request-options",
|
|
2773
3152
|
"translations"
|
|
2774
3153
|
];
|
|
2775
3154
|
}
|
|
2776
3155
|
connectedCallback() {
|
|
2777
|
-
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.dispatchEvent(
|
|
3156
|
+
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.dispatchEvent(b("ready", void 0));
|
|
2778
3157
|
}
|
|
2779
3158
|
disconnectedCallback() {
|
|
2780
3159
|
this.cleanup();
|
|
2781
3160
|
}
|
|
2782
3161
|
attributeChangedCallback(e, t, s) {
|
|
2783
|
-
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
3162
|
+
t !== s && (e === "api-url" || e === "disable-analytics" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
2784
3163
|
}
|
|
2785
3164
|
/**
|
|
2786
3165
|
* Get the current translations object. Mirrors the property getter.
|
|
@@ -2792,7 +3171,7 @@ class oe extends HTMLElement {
|
|
|
2792
3171
|
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
2793
3172
|
*/
|
|
2794
3173
|
set translations(e) {
|
|
2795
|
-
this.translationsOverride = e ?? null, this.resolvedTranslations =
|
|
3174
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = g(this.translationsOverride), this.isConnected && this.rerender();
|
|
2796
3175
|
}
|
|
2797
3176
|
/**
|
|
2798
3177
|
* Re-render preserving the current query and re-running the search so
|
|
@@ -2804,35 +3183,36 @@ class oe extends HTMLElement {
|
|
|
2804
3183
|
}
|
|
2805
3184
|
syncTranslationsFromAttribute() {
|
|
2806
3185
|
if (this.translationsOverride) {
|
|
2807
|
-
this.resolvedTranslations =
|
|
3186
|
+
this.resolvedTranslations = g(this.translationsOverride);
|
|
2808
3187
|
return;
|
|
2809
3188
|
}
|
|
2810
3189
|
const e = k(
|
|
2811
3190
|
this.getAttribute("translations"),
|
|
2812
3191
|
"SearchBarSnippet"
|
|
2813
3192
|
);
|
|
2814
|
-
this.resolvedTranslations =
|
|
3193
|
+
this.resolvedTranslations = g(e);
|
|
2815
3194
|
}
|
|
2816
3195
|
getProps() {
|
|
2817
3196
|
const e = this.resolvedTranslations;
|
|
2818
3197
|
return {
|
|
2819
|
-
apiUrl:
|
|
2820
|
-
placeholder:
|
|
2821
|
-
maxResults:
|
|
3198
|
+
apiUrl: m(this.getAttribute("api-url"), ""),
|
|
3199
|
+
placeholder: m(this.getAttribute("placeholder"), e.placeholder),
|
|
3200
|
+
maxResults: w(
|
|
2822
3201
|
this.getAttribute("max-results"),
|
|
2823
|
-
|
|
3202
|
+
N
|
|
2824
3203
|
),
|
|
2825
|
-
maxRenderResults:
|
|
3204
|
+
maxRenderResults: w(
|
|
2826
3205
|
this.getAttribute("max-render-results"),
|
|
2827
|
-
|
|
3206
|
+
H
|
|
2828
3207
|
),
|
|
2829
|
-
debounceMs:
|
|
2830
|
-
theme:
|
|
2831
|
-
hideBranding:
|
|
2832
|
-
showUrl:
|
|
2833
|
-
showDate:
|
|
2834
|
-
hideThumbnails:
|
|
2835
|
-
seeMore:
|
|
3208
|
+
debounceMs: w(this.getAttribute("debounce-ms"), 300),
|
|
3209
|
+
theme: m(this.getAttribute("theme"), "auto"),
|
|
3210
|
+
hideBranding: v(this.getAttribute("hide-branding"), !1),
|
|
3211
|
+
showUrl: v(this.getAttribute("show-url"), !1),
|
|
3212
|
+
showDate: v(this.getAttribute("show-date"), !1),
|
|
3213
|
+
hideThumbnails: v(this.getAttribute("hide-thumbnails"), !1),
|
|
3214
|
+
seeMore: m(this.getAttribute("see-more"), ""),
|
|
3215
|
+
disableAnalytics: v(this.getAttribute("disable-analytics"), !1),
|
|
2836
3216
|
translations: this.translationsOverride ?? void 0
|
|
2837
3217
|
};
|
|
2838
3218
|
}
|
|
@@ -2852,24 +3232,27 @@ class oe extends HTMLElement {
|
|
|
2852
3232
|
initializeClient() {
|
|
2853
3233
|
const e = this.getProps();
|
|
2854
3234
|
if (!e.apiUrl) {
|
|
2855
|
-
console.error("SearchBarSnippet: api-url attribute is required"), this.client = null, this.showMissingApiUrlError();
|
|
3235
|
+
console.error("SearchBarSnippet: api-url attribute is required"), this.client = null, this.destroyStatsClient(), this.showMissingApiUrlError();
|
|
2856
3236
|
return;
|
|
2857
3237
|
}
|
|
2858
3238
|
try {
|
|
2859
|
-
this.client = S(e.apiUrl);
|
|
3239
|
+
this.client = S(e.apiUrl), this.destroyStatsClient(), e.disableAnalytics || (this.stats = new Q(e.apiUrl));
|
|
2860
3240
|
} catch (t) {
|
|
2861
3241
|
console.error("SearchBarSnippet:", t);
|
|
2862
3242
|
}
|
|
2863
3243
|
}
|
|
3244
|
+
destroyStatsClient() {
|
|
3245
|
+
this.stats && (this.stats.destroy(), this.stats = null);
|
|
3246
|
+
}
|
|
2864
3247
|
render() {
|
|
2865
|
-
const e = this.getProps(), t = this.resolvedTranslations, s = (
|
|
2866
|
-
this.debouncedSearch =
|
|
3248
|
+
const e = this.getProps(), t = this.resolvedTranslations, s = (n) => this.performSearch(n);
|
|
3249
|
+
this.debouncedSearch = _(
|
|
2867
3250
|
s,
|
|
2868
3251
|
e.debounceMs || 400
|
|
2869
3252
|
);
|
|
2870
3253
|
const r = document.createElement("style");
|
|
2871
3254
|
r.textContent = `${M}
|
|
2872
|
-
${
|
|
3255
|
+
${fe}`, this.container = document.createElement("div"), this.container.className = "container", this.container.innerHTML = `
|
|
2873
3256
|
<div class="search-view">
|
|
2874
3257
|
<div class="search-input-wrapper">
|
|
2875
3258
|
<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>
|
|
@@ -2877,12 +3260,12 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
2877
3260
|
type="text"
|
|
2878
3261
|
name="search-input"
|
|
2879
3262
|
class="search-input"
|
|
2880
|
-
placeholder="${
|
|
2881
|
-
aria-label="${
|
|
3263
|
+
placeholder="${c(e.placeholder || t.placeholder)}"
|
|
3264
|
+
aria-label="${c(t.searchInputAriaLabel)}"
|
|
2882
3265
|
autocomplete="off"
|
|
2883
3266
|
/>
|
|
2884
|
-
<button class="button search-submit-button" aria-label="${
|
|
2885
|
-
<span>${
|
|
3267
|
+
<button class="button search-submit-button" aria-label="${c(t.searchButtonLabel)}">
|
|
3268
|
+
<span>${c(t.searchButtonLabel)}</span>
|
|
2886
3269
|
</button>
|
|
2887
3270
|
</div>
|
|
2888
3271
|
<div class="search-content">
|
|
@@ -2918,10 +3301,10 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
2918
3301
|
try {
|
|
2919
3302
|
const t = this.getProps(), s = await this.client.search(e, {
|
|
2920
3303
|
signal: this.currentSearchController.signal,
|
|
2921
|
-
maxResults: t.maxResults ||
|
|
3304
|
+
maxResults: t.maxResults || N,
|
|
2922
3305
|
request: this.getRequestOptions()
|
|
2923
|
-
}), r = s.slice(0, t.maxRenderResults ||
|
|
2924
|
-
this.displayResults(r, e, s.length);
|
|
3306
|
+
}), r = s.slice(0, t.maxRenderResults || H);
|
|
3307
|
+
this.lastSearchQuery = e, this.lastSearchTotal = s.length, this.stats?.trackSearch(e, s.length), this.displayResults(r, e, s.length);
|
|
2925
3308
|
} catch (t) {
|
|
2926
3309
|
if (t.name === "AbortError")
|
|
2927
3310
|
return;
|
|
@@ -2936,39 +3319,39 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
2936
3319
|
this.showNoResultsState(t);
|
|
2937
3320
|
return;
|
|
2938
3321
|
}
|
|
2939
|
-
const r = this.getProps(),
|
|
3322
|
+
const r = this.getProps(), n = this.resolvedTranslations, o = r.hideBranding ? "" : `<div class="powered-by-inline">${E}</div>`, h = s > e.length, d = h ? y(n.resultsCountOverflow, { n: e.length, total: s }) : y(s === 1 ? n.resultsCount : n.resultsCountPlural, {
|
|
2940
3323
|
n: s
|
|
2941
|
-
}),
|
|
2942
|
-
<a href="${
|
|
2943
|
-
<span>${
|
|
3324
|
+
}), p = r.seeMore && h ? `<div class="search-footer">
|
|
3325
|
+
<a href="${c(r.seeMore + encodeURIComponent(t))}" class="search-see-more">
|
|
3326
|
+
<span>${c(n.seeMoreResults)}</span>
|
|
2944
3327
|
<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>
|
|
2945
3328
|
</a>
|
|
2946
|
-
</div>` : "",
|
|
3329
|
+
</div>` : "", u = `
|
|
2947
3330
|
<div class="search-header">
|
|
2948
3331
|
<div class="search-count">
|
|
2949
|
-
${
|
|
3332
|
+
${c(d)}
|
|
2950
3333
|
</div>
|
|
2951
|
-
${
|
|
3334
|
+
${o}
|
|
2952
3335
|
</div>
|
|
2953
3336
|
<div class="search-results">
|
|
2954
|
-
${e.map((
|
|
3337
|
+
${e.map((f, L) => this.renderResult(f, L)).join("")}
|
|
2955
3338
|
</div>
|
|
2956
|
-
${
|
|
3339
|
+
${p}
|
|
2957
3340
|
`;
|
|
2958
|
-
this.resultsContainer.innerHTML =
|
|
3341
|
+
this.resultsContainer.innerHTML = u, this.attachResultHandlers();
|
|
2959
3342
|
}
|
|
2960
|
-
renderResult(e) {
|
|
2961
|
-
const
|
|
2962
|
-
${
|
|
2963
|
-
${
|
|
3343
|
+
renderResult(e, t) {
|
|
3344
|
+
const s = this.getProps(), r = s.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), n = e.url ? c(e.url) : "#", o = e.url ? c(V(e.url)) : "", h = s.showDate && e.timestamp !== void 0 ? `<div class="search-result-date">${c(j(e.timestamp))}</div>` : "", d = s.showUrl && e.url || h ? `<div class="search-result-metadata">
|
|
3345
|
+
${s.showUrl && e.url ? `<span class="search-result-url">${o}</span>` : '<span class="search-result-url search-result-url-empty"></span>'}
|
|
3346
|
+
${h}
|
|
2964
3347
|
</div>` : "";
|
|
2965
3348
|
return `
|
|
2966
|
-
<a href="${
|
|
2967
|
-
${
|
|
3349
|
+
<a href="${n}" class="search-result-item" data-index="${t}" data-result-id="${c(e.id || "")}">
|
|
3350
|
+
${r}
|
|
2968
3351
|
<div class="search-result-content">
|
|
2969
|
-
<div class="search-result-title">${
|
|
2970
|
-
<div class="search-result-snippet">${
|
|
2971
|
-
${
|
|
3352
|
+
<div class="search-result-title">${c(e.title || "")}</div>
|
|
3353
|
+
<div class="search-result-snippet">${c(e.description || "")}</div>
|
|
3354
|
+
${d}
|
|
2972
3355
|
</div>
|
|
2973
3356
|
</a>
|
|
2974
3357
|
`;
|
|
@@ -2981,8 +3364,8 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
2981
3364
|
<div class="search-result-image-placeholder" style="display: none;">${s}</div>
|
|
2982
3365
|
<img
|
|
2983
3366
|
class="search-result-image"
|
|
2984
|
-
src="${
|
|
2985
|
-
alt="${
|
|
3367
|
+
src="${c(e)}"
|
|
3368
|
+
alt="${c(t)}"
|
|
2986
3369
|
loading="lazy"
|
|
2987
3370
|
/>
|
|
2988
3371
|
</div>
|
|
@@ -2993,25 +3376,36 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
2993
3376
|
`;
|
|
2994
3377
|
}
|
|
2995
3378
|
attachResultHandlers() {
|
|
2996
|
-
|
|
3379
|
+
this.detachResultTrackingHandlers();
|
|
3380
|
+
const e = this.resultsContainer;
|
|
2997
3381
|
if (!e) return;
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3382
|
+
this.handleResultClick = (r) => {
|
|
3383
|
+
const o = r.target?.closest(".search-result-item");
|
|
3384
|
+
if (!o) return;
|
|
3385
|
+
o.getAttribute("href") === "#" && r.preventDefault();
|
|
3386
|
+
const d = o.getAttribute("data-index"), p = o.getAttribute("data-result-id") ?? "", u = d !== null ? Number.parseInt(d, 10) : Number.NaN;
|
|
3387
|
+
!Number.isNaN(u) && p && this.stats?.trackClick(this.lastSearchQuery, this.lastSearchTotal, p, u);
|
|
3388
|
+
}, e.addEventListener("click", this.handleResultClick);
|
|
3389
|
+
const t = e.querySelector(".search-see-more");
|
|
3390
|
+
t && (this.handleSeeMoreClick = () => {
|
|
3391
|
+
this.stats?.trackViewMore(this.lastSearchQuery, this.lastSearchTotal);
|
|
3392
|
+
}, t.addEventListener("click", this.handleSeeMoreClick)), this.container?.querySelectorAll(".search-result-image")?.forEach((r) => {
|
|
3393
|
+
r.addEventListener("load", () => {
|
|
3394
|
+
r.classList.add("loaded"), r.closest(".search-result-image-container")?.querySelector(".search-result-image-loading")?.remove();
|
|
3395
|
+
}), r.addEventListener("error", () => {
|
|
3396
|
+
const n = r.closest(".search-result-image-container");
|
|
3397
|
+
n?.querySelector(".search-result-image-loading")?.remove();
|
|
3398
|
+
const o = n?.querySelector(
|
|
3009
3399
|
".search-result-image-placeholder"
|
|
3010
3400
|
);
|
|
3011
|
-
o && (o.style.display = "flex"),
|
|
3401
|
+
o && (o.style.display = "flex"), r.style.display = "none";
|
|
3012
3402
|
});
|
|
3013
3403
|
});
|
|
3014
3404
|
}
|
|
3405
|
+
detachResultTrackingHandlers() {
|
|
3406
|
+
const e = this.resultsContainer;
|
|
3407
|
+
e && this.handleResultClick && e.removeEventListener("click", this.handleResultClick), this.handleResultClick = null, this.handleSeeMoreClick = null;
|
|
3408
|
+
}
|
|
3015
3409
|
showLoadingState() {
|
|
3016
3410
|
if (!this.resultsContainer) return;
|
|
3017
3411
|
this.clearLoadingInterval();
|
|
@@ -3020,8 +3414,8 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3020
3414
|
const t = this.resolvedTranslations;
|
|
3021
3415
|
this.resultsContainer.innerHTML = `
|
|
3022
3416
|
<div class="search-loading">
|
|
3023
|
-
<div class="loading" aria-label="${
|
|
3024
|
-
<div class="loading-text loading-text-animate">${
|
|
3417
|
+
<div class="loading" aria-label="${c(t.loadingAriaLabel)}"></div>
|
|
3418
|
+
<div class="loading-text loading-text-animate">${c(e[this.loadingMessageIndex])}</div>
|
|
3025
3419
|
</div>
|
|
3026
3420
|
`, this.startLoadingInterval();
|
|
3027
3421
|
}
|
|
@@ -3031,7 +3425,7 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3031
3425
|
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length;
|
|
3032
3426
|
const t = this.resultsContainer?.querySelector(".loading-text");
|
|
3033
3427
|
t && (t.classList.remove("loading-text-animate"), t.offsetWidth, t.textContent = e[this.loadingMessageIndex], t.classList.add("loading-text-animate"));
|
|
3034
|
-
},
|
|
3428
|
+
}, A);
|
|
3035
3429
|
}
|
|
3036
3430
|
clearLoadingInterval() {
|
|
3037
3431
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
@@ -3045,9 +3439,9 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3045
3439
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3046
3440
|
<path d="m21 21-4.35-4.35"></path>
|
|
3047
3441
|
</svg>
|
|
3048
|
-
<div class="search-empty-title">${
|
|
3442
|
+
<div class="search-empty-title">${c(e.emptyStateTitle)}</div>
|
|
3049
3443
|
<div class="search-empty-description">
|
|
3050
|
-
${
|
|
3444
|
+
${c(e.emptyStateDescription)}
|
|
3051
3445
|
</div>
|
|
3052
3446
|
</div>
|
|
3053
3447
|
`;
|
|
@@ -3061,9 +3455,9 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3061
3455
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3062
3456
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
3063
3457
|
</svg>
|
|
3064
|
-
<div class="search-empty-title">${
|
|
3458
|
+
<div class="search-empty-title">${c(t.noResultsTitle)}</div>
|
|
3065
3459
|
<div class="search-empty-description">
|
|
3066
|
-
${
|
|
3460
|
+
${c(y(t.noResultsDescription, { query: e }))}
|
|
3067
3461
|
</div>
|
|
3068
3462
|
</div>
|
|
3069
3463
|
`;
|
|
@@ -3073,7 +3467,7 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3073
3467
|
const t = this.resolvedTranslations;
|
|
3074
3468
|
this.resultsContainer.innerHTML = `
|
|
3075
3469
|
<div class="error">
|
|
3076
|
-
<strong>${
|
|
3470
|
+
<strong>${c(t.errorPrefix)}</strong> ${c(e)}
|
|
3077
3471
|
</div>
|
|
3078
3472
|
`;
|
|
3079
3473
|
}
|
|
@@ -3085,15 +3479,15 @@ ${ne}`, this.container = document.createElement("div"), this.container.className
|
|
|
3085
3479
|
t === "auto" ? this.removeAttribute("theme") : this.setAttribute("theme", t);
|
|
3086
3480
|
}
|
|
3087
3481
|
cleanup() {
|
|
3088
|
-
this.clearLoadingInterval(), this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.client && this.client.cancelAllRequests(), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydownEnter && this.inputElement.removeEventListener("keydown", this.handleInputKeydownEnter), this.handleInputKeydownEscape && window.removeEventListener("keydown", this.handleInputKeydownEscape)), this.searchButton && this.handleSearchButtonClick && this.searchButton.removeEventListener("click", this.handleSearchButtonClick), this.handleInputChange = null, this.handleInputKeydownEnter = null, this.handleInputKeydownEscape = null, this.handleSearchButtonClick = null;
|
|
3482
|
+
this.clearLoadingInterval(), this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.client && this.client.cancelAllRequests(), this.destroyStatsClient(), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydownEnter && this.inputElement.removeEventListener("keydown", this.handleInputKeydownEnter), this.handleInputKeydownEscape && window.removeEventListener("keydown", this.handleInputKeydownEscape)), this.searchButton && this.handleSearchButtonClick && this.searchButton.removeEventListener("click", this.handleSearchButtonClick), this.detachResultTrackingHandlers(), this.handleInputChange = null, this.handleInputKeydownEnter = null, this.handleInputKeydownEscape = null, this.handleSearchButtonClick = null;
|
|
3089
3483
|
}
|
|
3090
3484
|
// Public API
|
|
3091
3485
|
async search(e) {
|
|
3092
3486
|
await this.performSearch(e);
|
|
3093
3487
|
}
|
|
3094
3488
|
}
|
|
3095
|
-
customElements.get(
|
|
3096
|
-
const
|
|
3489
|
+
customElements.get(O) || customElements.define(O, be);
|
|
3490
|
+
const ye = `
|
|
3097
3491
|
/* Modal backdrop */
|
|
3098
3492
|
.modal-backdrop {
|
|
3099
3493
|
position: fixed;
|
|
@@ -3530,12 +3924,13 @@ a.modal-result-item:focus-visible {
|
|
|
3530
3924
|
.modal-container.open {
|
|
3531
3925
|
animation: modal-slide-in var(--search-snippet-transition) ease-out;
|
|
3532
3926
|
}
|
|
3533
|
-
`,
|
|
3534
|
-
class
|
|
3927
|
+
`, P = "search-modal-snippet", D = 10, U = 50;
|
|
3928
|
+
class we extends HTMLElement {
|
|
3535
3929
|
constructor() {
|
|
3536
3930
|
super();
|
|
3537
3931
|
a(this, "shadow");
|
|
3538
3932
|
a(this, "client", null);
|
|
3933
|
+
a(this, "stats", null);
|
|
3539
3934
|
a(this, "backdrop", null);
|
|
3540
3935
|
a(this, "modal", null);
|
|
3541
3936
|
a(this, "inputElement", null);
|
|
@@ -3549,12 +3944,17 @@ class ce extends HTMLElement {
|
|
|
3549
3944
|
a(this, "loadingMessageInterval", null);
|
|
3550
3945
|
a(this, "loadingMessageIndex", 0);
|
|
3551
3946
|
a(this, "translationsOverride", null);
|
|
3552
|
-
a(this, "resolvedTranslations",
|
|
3947
|
+
a(this, "resolvedTranslations", g(null));
|
|
3948
|
+
// Analytics context: populated by performSearch so click/view-more events
|
|
3949
|
+
// can reuse the query and total from the most recent result set.
|
|
3950
|
+
a(this, "lastSearchQuery", "");
|
|
3951
|
+
a(this, "lastSearchTotal", 0);
|
|
3553
3952
|
// Event handler references for cleanup
|
|
3554
3953
|
a(this, "handleGlobalKeydown", null);
|
|
3555
3954
|
a(this, "handleInputChange", null);
|
|
3556
3955
|
a(this, "handleInputKeydown", null);
|
|
3557
3956
|
a(this, "handleBackdropClick", null);
|
|
3957
|
+
a(this, "handleResultsContainerClick", null);
|
|
3558
3958
|
// Scroll lock state
|
|
3559
3959
|
a(this, "savedBodyStyles", null);
|
|
3560
3960
|
a(this, "savedHtmlOverflow", null);
|
|
@@ -3575,18 +3975,19 @@ class ce extends HTMLElement {
|
|
|
3575
3975
|
"show-date",
|
|
3576
3976
|
"hide-thumbnails",
|
|
3577
3977
|
"see-more",
|
|
3978
|
+
"disable-analytics",
|
|
3578
3979
|
"request-options",
|
|
3579
3980
|
"translations"
|
|
3580
3981
|
];
|
|
3581
3982
|
}
|
|
3582
3983
|
connectedCallback() {
|
|
3583
|
-
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.attachGlobalKeyboardShortcut(), this.dispatchEvent(
|
|
3984
|
+
this.syncTranslationsFromAttribute(), this.initializeClient(), this.render(), this.attachGlobalKeyboardShortcut(), this.dispatchEvent(b("ready", void 0));
|
|
3584
3985
|
}
|
|
3585
3986
|
disconnectedCallback() {
|
|
3586
3987
|
this.cleanup();
|
|
3587
3988
|
}
|
|
3588
3989
|
attributeChangedCallback(e, t, s) {
|
|
3589
|
-
t !== s && (e === "api-url" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
3990
|
+
t !== s && (e === "api-url" || e === "disable-analytics" ? this.initializeClient() : e === "theme" ? this.updateTheme(s) : e === "translations" && (this.syncTranslationsFromAttribute(), this.isConnected && this.rerender()));
|
|
3590
3991
|
}
|
|
3591
3992
|
/**
|
|
3592
3993
|
* Get the current translations object.
|
|
@@ -3598,7 +3999,7 @@ class ce extends HTMLElement {
|
|
|
3598
3999
|
* Override any user-facing string. Omitted keys fall back to English defaults.
|
|
3599
4000
|
*/
|
|
3600
4001
|
set translations(e) {
|
|
3601
|
-
this.translationsOverride = e ?? null, this.resolvedTranslations =
|
|
4002
|
+
this.translationsOverride = e ?? null, this.resolvedTranslations = g(this.translationsOverride), this.isConnected && this.rerender();
|
|
3602
4003
|
}
|
|
3603
4004
|
/**
|
|
3604
4005
|
* Re-render while preserving open state and the current query. Results are
|
|
@@ -3616,37 +4017,38 @@ class ce extends HTMLElement {
|
|
|
3616
4017
|
}
|
|
3617
4018
|
syncTranslationsFromAttribute() {
|
|
3618
4019
|
if (this.translationsOverride) {
|
|
3619
|
-
this.resolvedTranslations =
|
|
4020
|
+
this.resolvedTranslations = g(this.translationsOverride);
|
|
3620
4021
|
return;
|
|
3621
4022
|
}
|
|
3622
4023
|
const e = k(
|
|
3623
4024
|
this.getAttribute("translations"),
|
|
3624
4025
|
"SearchModalSnippet"
|
|
3625
4026
|
);
|
|
3626
|
-
this.resolvedTranslations =
|
|
4027
|
+
this.resolvedTranslations = g(e);
|
|
3627
4028
|
}
|
|
3628
4029
|
getProps() {
|
|
3629
4030
|
const e = this.resolvedTranslations;
|
|
3630
4031
|
return {
|
|
3631
|
-
apiUrl:
|
|
3632
|
-
placeholder:
|
|
3633
|
-
maxResults:
|
|
4032
|
+
apiUrl: m(this.getAttribute("api-url"), ""),
|
|
4033
|
+
placeholder: m(this.getAttribute("placeholder"), e.placeholder),
|
|
4034
|
+
maxResults: w(
|
|
3634
4035
|
this.getAttribute("max-results"),
|
|
3635
|
-
|
|
4036
|
+
U
|
|
3636
4037
|
),
|
|
3637
|
-
maxRenderResults:
|
|
4038
|
+
maxRenderResults: w(
|
|
3638
4039
|
this.getAttribute("max-render-results"),
|
|
3639
4040
|
D
|
|
3640
4041
|
),
|
|
3641
|
-
debounceMs:
|
|
3642
|
-
theme:
|
|
3643
|
-
shortcut:
|
|
4042
|
+
debounceMs: w(this.getAttribute("debounce-ms"), 300),
|
|
4043
|
+
theme: m(this.getAttribute("theme"), "auto"),
|
|
4044
|
+
shortcut: m(this.getAttribute("shortcut"), "k"),
|
|
3644
4045
|
useMetaKey: this.getAttribute("use-meta-key") !== "false",
|
|
3645
|
-
hideBranding:
|
|
3646
|
-
showUrl:
|
|
3647
|
-
showDate:
|
|
3648
|
-
hideThumbnails:
|
|
3649
|
-
seeMore:
|
|
4046
|
+
hideBranding: v(this.getAttribute("hide-branding"), !1),
|
|
4047
|
+
showUrl: v(this.getAttribute("show-url"), !1),
|
|
4048
|
+
showDate: v(this.getAttribute("show-date"), !1),
|
|
4049
|
+
hideThumbnails: v(this.getAttribute("hide-thumbnails"), !1),
|
|
4050
|
+
seeMore: m(this.getAttribute("see-more"), ""),
|
|
4051
|
+
disableAnalytics: v(this.getAttribute("disable-analytics"), !1),
|
|
3650
4052
|
translations: this.translationsOverride ?? void 0
|
|
3651
4053
|
};
|
|
3652
4054
|
}
|
|
@@ -3666,26 +4068,29 @@ class ce extends HTMLElement {
|
|
|
3666
4068
|
initializeClient() {
|
|
3667
4069
|
const e = this.getProps();
|
|
3668
4070
|
if (!e.apiUrl) {
|
|
3669
|
-
console.error("SearchModalSnippet: api-url attribute is required"), this.client = null, this.showMissingApiUrlError();
|
|
4071
|
+
console.error("SearchModalSnippet: api-url attribute is required"), this.client = null, this.destroyStatsClient(), this.showMissingApiUrlError();
|
|
3670
4072
|
return;
|
|
3671
4073
|
}
|
|
3672
4074
|
try {
|
|
3673
|
-
this.client = S(e.apiUrl);
|
|
4075
|
+
this.client = S(e.apiUrl), this.destroyStatsClient(), e.disableAnalytics || (this.stats = new Q(e.apiUrl));
|
|
3674
4076
|
} catch (t) {
|
|
3675
4077
|
console.error("SearchModalSnippet:", t);
|
|
3676
4078
|
}
|
|
3677
4079
|
}
|
|
4080
|
+
destroyStatsClient() {
|
|
4081
|
+
this.stats && (this.stats.destroy(), this.stats = null);
|
|
4082
|
+
}
|
|
3678
4083
|
render() {
|
|
3679
4084
|
const e = this.getProps(), t = this.resolvedTranslations, s = (h) => this.performSearch(h);
|
|
3680
|
-
this.debouncedSearch =
|
|
4085
|
+
this.debouncedSearch = _(
|
|
3681
4086
|
s,
|
|
3682
4087
|
e.debounceMs || 300
|
|
3683
4088
|
);
|
|
3684
4089
|
const r = document.createElement("style");
|
|
3685
4090
|
r.textContent = `${M}
|
|
3686
|
-
${
|
|
3687
|
-
const
|
|
3688
|
-
|
|
4091
|
+
${ye}`;
|
|
4092
|
+
const n = e.hideBranding ? "" : `<div class="powered-by-inline">${E}</div>`, o = document.createElement("div");
|
|
4093
|
+
o.innerHTML = `
|
|
3689
4094
|
<div class="modal-backdrop" role="presentation"></div>
|
|
3690
4095
|
<div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
3691
4096
|
<div class="modal-header">
|
|
@@ -3695,8 +4100,8 @@ ${le}`;
|
|
|
3695
4100
|
<input
|
|
3696
4101
|
type="text"
|
|
3697
4102
|
class="modal-search-input"
|
|
3698
|
-
placeholder="${
|
|
3699
|
-
aria-label="${
|
|
4103
|
+
placeholder="${c(e.placeholder || t.placeholder)}"
|
|
4104
|
+
aria-label="${c(t.searchButtonLabel)}"
|
|
3700
4105
|
aria-autocomplete="list"
|
|
3701
4106
|
aria-controls="modal-results-list"
|
|
3702
4107
|
aria-expanded="false"
|
|
@@ -3705,7 +4110,7 @@ ${le}`;
|
|
|
3705
4110
|
/>
|
|
3706
4111
|
</div>
|
|
3707
4112
|
<div class="modal-content">
|
|
3708
|
-
<div class="modal-results" id="modal-results-list" role="listbox" aria-label="${
|
|
4113
|
+
<div class="modal-results" id="modal-results-list" role="listbox" aria-label="${c(t.searchResultsAriaLabel)}">
|
|
3709
4114
|
${this.renderEmptyState()}
|
|
3710
4115
|
</div>
|
|
3711
4116
|
</div>
|
|
@@ -3714,21 +4119,21 @@ ${le}`;
|
|
|
3714
4119
|
<div class="modal-footer-hint">
|
|
3715
4120
|
<kbd class="modal-kbd">↑</kbd>
|
|
3716
4121
|
<kbd class="modal-kbd">↓</kbd>
|
|
3717
|
-
<span>${
|
|
4122
|
+
<span>${c(t.navigateHint)}</span>
|
|
3718
4123
|
</div>
|
|
3719
4124
|
<div class="modal-footer-hint">
|
|
3720
4125
|
<kbd class="modal-kbd">↵</kbd>
|
|
3721
|
-
<span>${
|
|
4126
|
+
<span>${c(t.selectHint)}</span>
|
|
3722
4127
|
</div>
|
|
3723
4128
|
<div class="modal-footer-hint">
|
|
3724
4129
|
<kbd class="modal-kbd">Esc</kbd>
|
|
3725
|
-
<span>${
|
|
4130
|
+
<span>${c(t.closeHint)}</span>
|
|
3726
4131
|
</div>
|
|
3727
4132
|
</div>
|
|
3728
|
-
${
|
|
4133
|
+
${n}
|
|
3729
4134
|
</div>
|
|
3730
4135
|
</div>
|
|
3731
|
-
`, this.shadow.innerHTML = "", this.shadow.appendChild(r), this.shadow.appendChild(
|
|
4136
|
+
`, this.shadow.innerHTML = "", this.shadow.appendChild(r), this.shadow.appendChild(o), 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();
|
|
3732
4137
|
}
|
|
3733
4138
|
attachGlobalKeyboardShortcut() {
|
|
3734
4139
|
const e = this.getProps(), t = e.shortcut?.toLowerCase() || "k";
|
|
@@ -3778,7 +4183,7 @@ ${le}`;
|
|
|
3778
4183
|
}
|
|
3779
4184
|
const e = this.results[this.activeIndex];
|
|
3780
4185
|
this.dispatchEvent(
|
|
3781
|
-
|
|
4186
|
+
b("result-select", {
|
|
3782
4187
|
result: e,
|
|
3783
4188
|
index: this.activeIndex
|
|
3784
4189
|
})
|
|
@@ -3797,10 +4202,10 @@ ${le}`;
|
|
|
3797
4202
|
try {
|
|
3798
4203
|
const t = this.getProps(), s = await this.client.search(e, {
|
|
3799
4204
|
signal: this.currentSearchController.signal,
|
|
3800
|
-
maxResults: t.maxResults ||
|
|
4205
|
+
maxResults: t.maxResults || U,
|
|
3801
4206
|
request: this.getRequestOptions()
|
|
3802
4207
|
});
|
|
3803
|
-
this.results = s.slice(0, t.maxRenderResults || D), this.activeIndex = this.results.length > 0 ? 0 : -1, this.displayResults(this.results, e, s.length);
|
|
4208
|
+
this.results = s.slice(0, t.maxRenderResults || D), this.activeIndex = this.results.length > 0 ? 0 : -1, this.lastSearchQuery = e, this.lastSearchTotal = s.length, this.stats?.trackSearch(e, s.length), this.displayResults(this.results, e, s.length);
|
|
3804
4209
|
} catch (t) {
|
|
3805
4210
|
if (t.name === "AbortError")
|
|
3806
4211
|
return;
|
|
@@ -3815,35 +4220,36 @@ ${le}`;
|
|
|
3815
4220
|
this.showNoResultsState(t);
|
|
3816
4221
|
return;
|
|
3817
4222
|
}
|
|
3818
|
-
const r = this.getProps(),
|
|
4223
|
+
const r = this.getProps(), n = this.resolvedTranslations, o = e.map((u, f) => this.renderResult(u, f)).join(""), h = s > e.length, d = h ? y(n.resultsCountOverflow, { n: e.length, total: s }) : y(s === 1 ? n.modalResultsCount : n.modalResultsCountPlural, {
|
|
3819
4224
|
n: s
|
|
3820
|
-
}),
|
|
3821
|
-
<span>${
|
|
4225
|
+
}), p = r.seeMore && h ? `<a href="${c(r.seeMore + encodeURIComponent(t))}" class="modal-see-more">
|
|
4226
|
+
<span>${c(n.seeMoreResults)}</span>
|
|
3822
4227
|
<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>
|
|
3823
4228
|
</a>` : "";
|
|
3824
|
-
this.resultsContainer.innerHTML =
|
|
4229
|
+
this.resultsContainer.innerHTML = o + p, this.footerCount && (this.footerCount.textContent = d), this.inputElement && this.inputElement.setAttribute("aria-expanded", "true"), this.attachResultHandlers(), this.updateActiveResult();
|
|
3825
4230
|
}
|
|
3826
4231
|
renderResult(e, t) {
|
|
3827
|
-
const s = this.getProps(), r = s.hideThumbnails ? "" : this.renderResultImage(e.image, e.title),
|
|
3828
|
-
${s.showUrl && e.url ? `<span class="modal-result-url">${
|
|
4232
|
+
const s = this.getProps(), r = s.hideThumbnails ? "" : this.renderResultImage(e.image, e.title), n = e.url ? c(e.url) : "#", o = e.url ? c(V(e.url)) : "", h = s.showDate && e.timestamp !== void 0 ? `<div class="modal-result-date">${c(j(e.timestamp))}</div>` : "", d = s.showUrl && e.url || h ? `<div class="modal-result-metadata">
|
|
4233
|
+
${s.showUrl && e.url ? `<span class="modal-result-url">${o}</span>` : '<span class="modal-result-url modal-result-url-empty"></span>'}
|
|
3829
4234
|
${h}
|
|
3830
4235
|
</div>` : "";
|
|
3831
4236
|
return `
|
|
3832
4237
|
<a
|
|
3833
|
-
href="${
|
|
4238
|
+
href="${n}"
|
|
3834
4239
|
class="modal-result-item${t === this.activeIndex ? " active" : ""}"
|
|
3835
4240
|
role="option"
|
|
3836
4241
|
id="result-${t}"
|
|
3837
4242
|
aria-selected="${t === this.activeIndex}"
|
|
3838
4243
|
tabindex="-1"
|
|
3839
4244
|
data-index="${t}"
|
|
3840
|
-
data-
|
|
4245
|
+
data-result-id="${c(e.id || "")}"
|
|
4246
|
+
data-url="${c(e.url || "")}"
|
|
3841
4247
|
>
|
|
3842
4248
|
${r}
|
|
3843
4249
|
<div class="modal-result-content">
|
|
3844
|
-
<div class="modal-result-title">${
|
|
3845
|
-
${e.description ? `<div class="modal-result-description">${
|
|
3846
|
-
${
|
|
4250
|
+
<div class="modal-result-title">${c(e.title || "")}</div>
|
|
4251
|
+
${e.description ? `<div class="modal-result-description">${c(e.description)}</div>` : ""}
|
|
4252
|
+
${d}
|
|
3847
4253
|
</div>
|
|
3848
4254
|
</a>
|
|
3849
4255
|
`;
|
|
@@ -3856,8 +4262,8 @@ ${le}`;
|
|
|
3856
4262
|
<div class="modal-result-image-placeholder" style="display: none;">${s}</div>
|
|
3857
4263
|
<img
|
|
3858
4264
|
class="modal-result-image"
|
|
3859
|
-
src="${
|
|
3860
|
-
alt="${
|
|
4265
|
+
src="${c(e)}"
|
|
4266
|
+
alt="${c(t)}"
|
|
3861
4267
|
loading="lazy"
|
|
3862
4268
|
/>
|
|
3863
4269
|
</div>
|
|
@@ -3868,27 +4274,40 @@ ${le}`;
|
|
|
3868
4274
|
`;
|
|
3869
4275
|
}
|
|
3870
4276
|
attachResultHandlers() {
|
|
3871
|
-
|
|
4277
|
+
this.detachResultsContainerClick();
|
|
4278
|
+
const e = this.resultsContainer;
|
|
3872
4279
|
if (!e) return;
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
4280
|
+
this.handleResultsContainerClick = (r) => {
|
|
4281
|
+
const n = r.target;
|
|
4282
|
+
if (!n) return;
|
|
4283
|
+
const o = n.closest(".modal-result-item");
|
|
4284
|
+
if (o) {
|
|
4285
|
+
o.getAttribute("href") === "#" && r.preventDefault();
|
|
4286
|
+
const p = o.getAttribute("data-index"), u = o.getAttribute("data-result-id") ?? "", f = p !== null ? Number.parseInt(p, 10) : Number.NaN;
|
|
4287
|
+
!Number.isNaN(f) && u && this.stats?.trackClick(this.lastSearchQuery, this.lastSearchTotal, u, f);
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
n.closest(".modal-see-more") && this.stats?.trackViewMore(this.lastSearchQuery, this.lastSearchTotal);
|
|
4291
|
+
}, e.addEventListener("click", this.handleResultsContainerClick), e.querySelectorAll(".modal-result-item").forEach((r, n) => {
|
|
4292
|
+
r.addEventListener("mouseenter", () => {
|
|
4293
|
+
this.activeIndex = n, this.updateActiveResult();
|
|
3878
4294
|
});
|
|
3879
|
-
}),
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
}),
|
|
3883
|
-
const
|
|
3884
|
-
|
|
3885
|
-
const o =
|
|
4295
|
+
}), e.querySelectorAll(".modal-result-image").forEach((r) => {
|
|
4296
|
+
r.addEventListener("load", () => {
|
|
4297
|
+
r.classList.add("loaded"), r.closest(".modal-result-image-container")?.querySelector(".modal-result-image-loading")?.remove();
|
|
4298
|
+
}), r.addEventListener("error", () => {
|
|
4299
|
+
const n = r.closest(".modal-result-image-container");
|
|
4300
|
+
n?.querySelector(".modal-result-image-loading")?.remove();
|
|
4301
|
+
const o = n?.querySelector(
|
|
3886
4302
|
".modal-result-image-placeholder"
|
|
3887
4303
|
);
|
|
3888
|
-
o && (o.style.display = "flex"),
|
|
4304
|
+
o && (o.style.display = "flex"), r.style.display = "none";
|
|
3889
4305
|
});
|
|
3890
4306
|
});
|
|
3891
4307
|
}
|
|
4308
|
+
detachResultsContainerClick() {
|
|
4309
|
+
this.resultsContainer && this.handleResultsContainerClick && this.resultsContainer.removeEventListener("click", this.handleResultsContainerClick), this.handleResultsContainerClick = null;
|
|
4310
|
+
}
|
|
3892
4311
|
renderEmptyState() {
|
|
3893
4312
|
const e = this.resolvedTranslations;
|
|
3894
4313
|
return `
|
|
@@ -3897,7 +4316,7 @@ ${le}`;
|
|
|
3897
4316
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3898
4317
|
<path d="m21 21-4.35-4.35"></path>
|
|
3899
4318
|
</svg>
|
|
3900
|
-
<div class="modal-empty-description">${
|
|
4319
|
+
<div class="modal-empty-description">${c(e.modalEmptyStateDescription)}</div>
|
|
3901
4320
|
</div>
|
|
3902
4321
|
`;
|
|
3903
4322
|
}
|
|
@@ -3910,8 +4329,8 @@ ${le}`;
|
|
|
3910
4329
|
const e = this.resolvedTranslations.loadingMessages, t = this.resolvedTranslations;
|
|
3911
4330
|
this.loadingMessageIndex = Math.floor(Math.random() * e.length), this.resultsContainer.innerHTML = `
|
|
3912
4331
|
<div class="modal-loading">
|
|
3913
|
-
<div class="loading" aria-label="${
|
|
3914
|
-
<div class="loading-text loading-text-animate">${
|
|
4332
|
+
<div class="loading" aria-label="${c(t.loadingAriaLabel)}"></div>
|
|
4333
|
+
<div class="loading-text loading-text-animate">${c(e[this.loadingMessageIndex])}</div>
|
|
3915
4334
|
</div>
|
|
3916
4335
|
`, this.footerCount && (this.footerCount.textContent = e[this.loadingMessageIndex]), this.startLoadingInterval();
|
|
3917
4336
|
}
|
|
@@ -3921,7 +4340,7 @@ ${le}`;
|
|
|
3921
4340
|
this.loadingMessageIndex = (this.loadingMessageIndex + 1) % e.length;
|
|
3922
4341
|
const t = this.resultsContainer?.querySelector(".loading-text");
|
|
3923
4342
|
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
|
-
},
|
|
4343
|
+
}, A);
|
|
3925
4344
|
}
|
|
3926
4345
|
clearLoadingInterval() {
|
|
3927
4346
|
this.loadingMessageInterval && (clearInterval(this.loadingMessageInterval), this.loadingMessageInterval = null);
|
|
@@ -3935,8 +4354,8 @@ ${le}`;
|
|
|
3935
4354
|
<circle cx="11" cy="11" r="8"></circle>
|
|
3936
4355
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
3937
4356
|
</svg>
|
|
3938
|
-
<div class="modal-empty-title">${
|
|
3939
|
-
<div class="modal-empty-description">${
|
|
4357
|
+
<div class="modal-empty-title">${c(t.modalNoResultsTitle)}</div>
|
|
4358
|
+
<div class="modal-empty-description">${c(y(t.modalNoResultsDescription, { query: e }))}</div>
|
|
3940
4359
|
</div>
|
|
3941
4360
|
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountZero), this.inputElement && this.inputElement.setAttribute("aria-expanded", "false");
|
|
3942
4361
|
}
|
|
@@ -3945,7 +4364,7 @@ ${le}`;
|
|
|
3945
4364
|
const t = this.resolvedTranslations;
|
|
3946
4365
|
this.resultsContainer.innerHTML = `
|
|
3947
4366
|
<div class="error">
|
|
3948
|
-
<strong>${
|
|
4367
|
+
<strong>${c(t.errorPrefix)}</strong> ${c(e)}
|
|
3949
4368
|
</div>
|
|
3950
4369
|
`, this.footerCount && (this.footerCount.textContent = t.modalResultsCountError);
|
|
3951
4370
|
}
|
|
@@ -3972,7 +4391,7 @@ ${le}`;
|
|
|
3972
4391
|
document.documentElement.style.overflow = this.savedHtmlOverflow || "", document.body.style.overflow = this.savedBodyStyles.overflow, document.body.style.position = this.savedBodyStyles.position, document.body.style.top = this.savedBodyStyles.top, document.body.style.width = this.savedBodyStyles.width, document.body.style.scrollbarGutter = this.savedBodyStyles.scrollbarGutter || "", window.scrollTo(0, e), this.savedBodyStyles = null, this.savedHtmlOverflow = null;
|
|
3973
4392
|
}
|
|
3974
4393
|
cleanup() {
|
|
3975
|
-
this.clearLoadingInterval(), this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.handleGlobalKeydown && (document.removeEventListener("keydown", this.handleGlobalKeydown), this.handleGlobalKeydown = null), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.backdrop && this.handleBackdropClick && this.backdrop.removeEventListener("click", this.handleBackdropClick), this.handleInputChange = null, this.handleInputKeydown = null, this.handleBackdropClick = null, this.client && this.client.cancelAllRequests();
|
|
4394
|
+
this.clearLoadingInterval(), this.currentSearchController && (this.currentSearchController.abort(), this.currentSearchController = null), this.handleGlobalKeydown && (document.removeEventListener("keydown", this.handleGlobalKeydown), this.handleGlobalKeydown = null), this.inputElement && (this.handleInputChange && this.inputElement.removeEventListener("input", this.handleInputChange), this.handleInputKeydown && this.inputElement.removeEventListener("keydown", this.handleInputKeydown)), this.backdrop && this.handleBackdropClick && this.backdrop.removeEventListener("click", this.handleBackdropClick), this.detachResultsContainerClick(), this.handleInputChange = null, this.handleInputKeydown = null, this.handleBackdropClick = null, this.destroyStatsClient(), this.client && this.client.cancelAllRequests();
|
|
3976
4395
|
}
|
|
3977
4396
|
// Public API
|
|
3978
4397
|
/**
|
|
@@ -3983,13 +4402,13 @@ ${le}`;
|
|
|
3983
4402
|
requestAnimationFrame(() => {
|
|
3984
4403
|
this.inputElement?.focus();
|
|
3985
4404
|
});
|
|
3986
|
-
}), this.lockBodyScroll(), this.dispatchEvent(
|
|
4405
|
+
}), this.lockBodyScroll(), this.dispatchEvent(b("open", void 0)));
|
|
3987
4406
|
}
|
|
3988
4407
|
/**
|
|
3989
4408
|
* Close the search modal
|
|
3990
4409
|
*/
|
|
3991
4410
|
close() {
|
|
3992
|
-
this.isOpen && (this.isOpen = !1, this.backdrop?.classList.remove("open"), this.modal?.classList.remove("open"), this.inputElement && (this.inputElement.value = ""), this.results = [], this.activeIndex = -1, this.showEmptyState(), this.unlockBodyScroll(), this.dispatchEvent(
|
|
4411
|
+
this.isOpen && (this.isOpen = !1, this.backdrop?.classList.remove("open"), this.modal?.classList.remove("open"), this.inputElement && (this.inputElement.value = ""), this.results = [], this.activeIndex = -1, this.showEmptyState(), this.unlockBodyScroll(), this.dispatchEvent(b("close", void 0)));
|
|
3993
4412
|
}
|
|
3994
4413
|
/**
|
|
3995
4414
|
* Toggle the search modal open/closed
|
|
@@ -4016,15 +4435,16 @@ ${le}`;
|
|
|
4016
4435
|
return this.isOpen;
|
|
4017
4436
|
}
|
|
4018
4437
|
}
|
|
4019
|
-
customElements.get(
|
|
4438
|
+
customElements.get(P) || customElements.define(P, we);
|
|
4020
4439
|
export {
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4440
|
+
ae as AISearchClient,
|
|
4441
|
+
me as ChatBubbleSnippet,
|
|
4442
|
+
ve as ChatPageSnippet,
|
|
4443
|
+
T as DEFAULT_TRANSLATIONS,
|
|
4444
|
+
be as SearchBarSnippet,
|
|
4445
|
+
we as SearchModalSnippet,
|
|
4446
|
+
Q as StatsClient,
|
|
4447
|
+
be as default,
|
|
4448
|
+
g as mergeTranslations
|
|
4029
4449
|
};
|
|
4030
4450
|
//# sourceMappingURL=search-snippet.es.js.map
|