@cloudflare/ai-search-snippet 0.0.37 → 0.0.38

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