@farming-labs/astro-theme 0.2.38 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/astro-theme",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "description": "Astro UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
5
5
  "keywords": [
6
6
  "astro",
@@ -119,8 +119,8 @@
119
119
  },
120
120
  "dependencies": {
121
121
  "sugar-high": "^0.9.5",
122
- "@farming-labs/docs": "0.2.38",
123
- "@farming-labs/astro": "0.2.38"
122
+ "@farming-labs/astro": "0.2.39",
123
+ "@farming-labs/docs": "0.2.39"
124
124
  },
125
125
  "peerDependencies": {
126
126
  "astro": ">=4.0.0"
@@ -21,15 +21,11 @@ const { config = null } = Astro.props;
21
21
  role="combobox"
22
22
  aria-expanded="true"
23
23
  aria-controls="fd-omni-listbox"
24
- placeholder="Search documentation…"
24
+ placeholder="Search"
25
25
  autocomplete="off"
26
26
  />
27
- <kbd class="omni-kbd">⌘ K</kbd>
28
27
  <button type="button" aria-label="Close" class="omni-close-btn" id="fd-omni-close">
29
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
30
- <path d="M18 6 6 18" />
31
- <path d="m6 6 12 12" />
32
- </svg>
28
+ ESC
33
29
  </button>
34
30
  </div>
35
31
  </div>
@@ -65,27 +61,71 @@ const { config = null } = Astro.props;
65
61
  <div class="omni-footer-hints">
66
62
  <span class="omni-footer-hint">
67
63
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
68
- <polyline points="9 18 15 12 9 6" />
64
+ <path d="m9 10-5 5 5 5" />
65
+ <path d="M20 4v7a4 4 0 0 1-4 4H4" />
69
66
  </svg>
70
67
  to select
71
68
  </span>
72
69
  <span class="omni-footer-hint">
73
70
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
74
- <path d="M18 15l-6-6-6 6" />
71
+ <path d="m18 15-6-6-6 6" />
75
72
  </svg>
76
73
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
77
- <path d="M6 9l6 6 6-6" />
74
+ <path d="m6 9 6 6 6-6" />
78
75
  </svg>
79
76
  to navigate
80
77
  </span>
81
78
  <span class="omni-footer-hint omni-footer-hint-desktop">
82
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
83
- <path d="M18 6 6 18" />
84
- <path d="m6 6 12 12" />
85
- </svg>
79
+ <span class="omni-kbd-sm">ESC</span>
86
80
  to close
87
81
  </span>
88
82
  </div>
83
+ <div class="omni-footer-filter">
84
+ <span class="omni-filter-label">Filter</span>
85
+ <button
86
+ type="button"
87
+ class="omni-filter-button"
88
+ id="fd-omni-filter-button"
89
+ aria-expanded="false"
90
+ >
91
+ <span id="fd-omni-filter-value">All</span>
92
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
93
+ <path d="M6 9l6 6 6-6" />
94
+ </svg>
95
+ </button>
96
+ <div
97
+ class="omni-filter-menu"
98
+ id="fd-omni-filter-menu"
99
+ role="group"
100
+ aria-label="Search filter"
101
+ style="display: none;"
102
+ >
103
+ <button
104
+ type="button"
105
+ aria-pressed="true"
106
+ class="omni-filter-option omni-filter-option-active"
107
+ data-filter="all"
108
+ >
109
+ All
110
+ </button>
111
+ <button
112
+ type="button"
113
+ aria-pressed="false"
114
+ class="omni-filter-option"
115
+ data-filter="pages"
116
+ >
117
+ Pages
118
+ </button>
119
+ <button
120
+ type="button"
121
+ aria-pressed="false"
122
+ class="omni-filter-option"
123
+ data-filter="inside"
124
+ >
125
+ Inside pages
126
+ </button>
127
+ </div>
128
+ </div>
89
129
  </div>
90
130
  </div>
91
131
  </div>
@@ -96,6 +136,12 @@ const { config = null } = Astro.props;
96
136
  var STORAGE_KEY = "fd:omni:recents";
97
137
  var MAX_RECENTS = 8;
98
138
  var DEBOUNCE_MS = 150;
139
+ var BREADCRUMB_SEPARATOR = "\u00a0\u00a0>\u00a0\u00a0";
140
+ var FILTER_LABELS = {
141
+ all: "All",
142
+ pages: "Pages",
143
+ inside: "Inside pages"
144
+ };
99
145
 
100
146
  function getDialog() { return document.getElementById("fd-search-dialog"); }
101
147
  function getOverlay() { return document.getElementById("fd-omni-overlay"); }
@@ -109,6 +155,9 @@ const { config = null } = Astro.props;
109
155
  function getDocsItems() { return document.getElementById("fd-omni-docs-items"); }
110
156
  function getEmpty() { return document.getElementById("fd-omni-empty"); }
111
157
  function getEmptyText() { return document.getElementById("fd-omni-empty-text"); }
158
+ function getFilterButton() { return document.getElementById("fd-omni-filter-button"); }
159
+ function getFilterValue() { return document.getElementById("fd-omni-filter-value"); }
160
+ function getFilterMenu() { return document.getElementById("fd-omni-filter-menu"); }
112
161
 
113
162
  function getRecents() {
114
163
  try {
@@ -148,13 +197,115 @@ const { config = null } = Astro.props;
148
197
  }
149
198
  }
150
199
 
200
+ function breadcrumbForUrl(url) {
201
+ try {
202
+ var parsed = new URL(url, window.location.origin);
203
+ var parts = parsed.pathname.split("/").filter(Boolean).map(function (part) {
204
+ return decodeURIComponent(part)
205
+ .replace(/[-_]+/g, " ")
206
+ .replace(/\b\w/g, function (char) { return char.toUpperCase(); });
207
+ });
208
+ return parts.length ? parts.join(BREADCRUMB_SEPARATOR) : "Docs";
209
+ } catch (e) {
210
+ return "Docs";
211
+ }
212
+ }
213
+
214
+ function displayLabelForResult(result) {
215
+ if (result.section) return result.section;
216
+ var label = result.content || "";
217
+ var parts = label.split(/\s+[—–]\s+/).map(function (part) { return part.trim(); }).filter(Boolean);
218
+ return result.type === "heading" && parts.length > 1 ? parts[parts.length - 1] : label;
219
+ }
220
+
221
+ function normalizeSearchPhrase(value) {
222
+ return (value || "").toLowerCase().replace(/[?!.,;:]+$/g, "").replace(/\s+/g, " ").trim();
223
+ }
224
+
225
+ function escapeRegExp(value) {
226
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
227
+ }
228
+
229
+ function literalMatchPriority(searchQuery, value) {
230
+ var q = normalizeSearchPhrase(searchQuery);
231
+ var text = normalizeSearchPhrase(value);
232
+ if (!q || !text) return 0;
233
+ if (text === q) return 2;
234
+
235
+ var boundary = "[^\\p{L}\\p{N}]";
236
+ return new RegExp("(^|" + boundary + ")" + escapeRegExp(q) + "(?=$|" + boundary + ")", "u").test(text)
237
+ ? 1
238
+ : 0;
239
+ }
240
+
241
+ function tokenizeLiteralQuery(searchQuery) {
242
+ return Array.from(new Set(
243
+ searchQuery
244
+ .toLowerCase()
245
+ .replace(/[^\p{L}\p{N}@/_:.-]+/gu, " ")
246
+ .split(/\s+/)
247
+ .map(function (word) { return word.replace(/^[^\p{L}\p{N}@]+|[^\p{L}\p{N}]+$/gu, ""); })
248
+ .filter(function (word) { return word.length > 1; })
249
+ ));
250
+ }
251
+
252
+ function isLiteralLookupQuery(searchQuery) {
253
+ var q = normalizeSearchPhrase(searchQuery);
254
+ var words = tokenizeLiteralQuery(q);
255
+ return words.length > 0 && words.length <= 3 && words.join(" ") === q;
256
+ }
257
+
258
+ function hasDistinctSearchSection(result) {
259
+ if (result.type === "page") return false;
260
+ if (!result.section) return true;
261
+ var title = (result.content || "").split(/\s+[—–]\s+/)[0] || "";
262
+ return normalizeSearchPhrase(result.section) !== normalizeSearchPhrase(title);
263
+ }
264
+
265
+ function insideLiteralPriority(result, searchQuery) {
266
+ if (!hasDistinctSearchSection(result) || !isLiteralLookupQuery(searchQuery)) return 0;
267
+ return Math.max(
268
+ literalMatchPriority(searchQuery, result.section),
269
+ literalMatchPriority(searchQuery, result.description)
270
+ );
271
+ }
272
+
273
+ function getCurrentSearchQuery() {
274
+ var input = getInput();
275
+ return input ? input.value.trim() : "";
276
+ }
277
+
278
+ function sortResultsForFilter(results, searchQuery) {
279
+ var q = typeof searchQuery === "string" ? searchQuery : getCurrentSearchQuery();
280
+ if (!q) return results;
281
+
282
+ return results.map(function (result, index) {
283
+ return { result: result, index: index };
284
+ }).sort(function (a, b) {
285
+ var aLiteralPriority = insideLiteralPriority(a.result, q);
286
+ var bLiteralPriority = insideLiteralPriority(b.result, q);
287
+ var literalDelta = bLiteralPriority - aLiteralPriority;
288
+ if (literalDelta) return literalDelta;
289
+ if (aLiteralPriority > 0 && bLiteralPriority > 0) return a.index - b.index;
290
+
291
+ var scoreDelta = (b.result.score || 0) - (a.result.score || 0);
292
+ if (scoreDelta) return scoreDelta;
293
+
294
+ return a.index - b.index;
295
+ }).map(function (item) {
296
+ return item.result;
297
+ });
298
+ }
299
+
151
300
  function openDialog() {
152
301
  var dialog = getDialog();
153
302
  if (dialog) {
154
303
  dialog.style.display = "block";
155
304
  document.body.style.overflow = "hidden";
305
+ currentFilter = "all";
306
+ setFilterMenuOpen(false);
307
+ renderFilter();
156
308
  bindListboxDelegation();
157
- ensureListboxClickBound();
158
309
  setTimeout(function () {
159
310
  var input = getInput();
160
311
  if (input) { input.value = ""; input.focus(); }
@@ -168,6 +319,7 @@ const { config = null } = Astro.props;
168
319
  if (dialog) {
169
320
  dialog.style.display = "none";
170
321
  document.body.style.overflow = "";
322
+ setFilterMenuOpen(false);
171
323
  }
172
324
  }
173
325
 
@@ -178,26 +330,48 @@ const { config = null } = Astro.props;
178
330
  return el.textContent || el.innerText || "";
179
331
  }
180
332
 
181
- var fileIconSvg = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/></svg>';
182
- var externalSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>';
183
- var chevronSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>';
333
+ function escapeHtml(value) {
334
+ return (value || "").replace(/[&<>"']/g, function (char) {
335
+ if (char === "&") return "&amp;";
336
+ if (char === "<") return "&lt;";
337
+ if (char === ">") return "&gt;";
338
+ if (char === '"') return "&quot;";
339
+ return "&#39;";
340
+ });
341
+ }
342
+
343
+ function highlightSnippet(text, query) {
344
+ var q = (query || "").trim();
345
+ if (!q) return escapeHtml(text);
346
+ var html = "";
347
+ var lastIndex = 0;
348
+ var regex = new RegExp(escapeRegExp(q), "gi");
349
+ var match;
350
+
351
+ while ((match = regex.exec(text)) !== null) {
352
+ html += escapeHtml(text.slice(lastIndex, match.index));
353
+ html += '<mark class="omni-highlight">' + escapeHtml(match[0]) + '</mark>';
354
+ lastIndex = match.index + match[0].length;
355
+ }
356
+
357
+ return html + escapeHtml(text.slice(lastIndex));
358
+ }
184
359
 
185
360
  function renderItem(item, active, isRecent) {
186
- var label = (item.label || item.content || "").replace(/</g, "&lt;").replace(/>/g, "&gt;");
187
- var subtitle = (item.subtitle || item.description || (isRecent ? "Recently viewed" : "Page")).replace(/</g, "&lt;").replace(/>/g, "&gt;");
361
+ var label = escapeHtml(item.label || item.content || "");
362
+ var subtitle = escapeHtml(item.subtitle || (isRecent ? "Recently viewed" : breadcrumbForUrl(item.url || "")));
363
+ var rawDescription = item.description || "";
364
+ var description = highlightSnippet(rawDescription, getInput() && getInput().value || "");
188
365
  var url = item.url || "#";
189
366
  var id = (item.id || url).replace(/[^a-zA-Z0-9-_]/g, "_");
190
- var safeUrl = url.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
191
367
  var activeClass = active ? " omni-item-active" : "";
192
368
  return (
193
369
  '<div class="omni-item' + activeClass + '" data-id="' + id + '" data-url="' + url.replace(/"/g, "&quot;") + '" role="option" aria-selected="' + (active ? "true" : "false") + '" tabindex="-1">' +
194
- '<div class="omni-item-icon">' + fileIconSvg + '</div>' +
195
370
  '<div class="omni-item-text">' +
196
- '<div class="omni-item-label">' + label + '</div>' +
197
371
  '<div class="omni-item-subtitle">' + subtitle + '</div>' +
372
+ '<div class="omni-item-label">' + label + '</div>' +
373
+ (description ? '<div class="omni-item-description">' + description + '</div>' : '') +
198
374
  '</div>' +
199
- '<a href="' + safeUrl + '" class="omni-item-ext" title="Open in new tab" target="_blank" rel="noopener noreferrer" data-ext-url="' + url.replace(/"/g, "&quot;") + '">' + externalSvg + '</a>' +
200
- '<span class="omni-item-chevron" aria-hidden="true">' + chevronSvg + '</span>' +
201
375
  '</div>'
202
376
  );
203
377
  }
@@ -206,13 +380,77 @@ const { config = null } = Astro.props;
206
380
  var currentResults = [];
207
381
  var recentsList = [];
208
382
  var activeIndex = 0;
383
+ var currentFilter = "all";
384
+ var abortController = null;
385
+ var searchCache = new Map();
386
+ var currentResultsVersion = 0;
387
+ var visibleResultsCacheKey = "";
388
+ var visibleResultsCacheResults = [];
389
+
390
+ function setCurrentResults(results) {
391
+ currentResults = results;
392
+ currentResultsVersion += 1;
393
+ visibleResultsCacheKey = "";
394
+ visibleResultsCacheResults = [];
395
+ }
396
+
397
+ function visibleResults() {
398
+ var q = getCurrentSearchQuery();
399
+ var cacheKey = currentFilter + "\n" + q + "\n" + currentResultsVersion;
400
+ if (visibleResultsCacheKey === cacheKey) return visibleResultsCacheResults;
401
+
402
+ var results;
403
+ if (currentFilter === "pages") {
404
+ results = currentResults.filter(function (result) { return result.type === "page"; });
405
+ } else if (currentFilter === "inside") {
406
+ results = sortResultsForFilter(
407
+ currentResults.filter(function (result) { return result.type !== "page"; }),
408
+ q
409
+ );
410
+ } else {
411
+ results = sortResultsForFilter(currentResults, q);
412
+ }
413
+
414
+ visibleResultsCacheKey = cacheKey;
415
+ visibleResultsCacheResults = results;
416
+ return results;
417
+ }
209
418
 
210
419
  function allItems() {
211
420
  var q = (getInput() && getInput().value || "").trim();
212
- if (q && currentResults.length) return currentResults;
421
+ var results = visibleResults();
422
+ if (q && results.length) return results;
213
423
  return recentsList;
214
424
  }
215
425
 
426
+ function setFilterMenuOpen(open) {
427
+ var menu = getFilterMenu();
428
+ var button = getFilterButton();
429
+ if (menu) menu.style.display = open ? "block" : "none";
430
+ if (button) button.setAttribute("aria-expanded", open ? "true" : "false");
431
+ }
432
+
433
+ function renderFilter() {
434
+ var value = getFilterValue();
435
+ var menu = getFilterMenu();
436
+ if (value) value.textContent = FILTER_LABELS[currentFilter] || "All";
437
+ if (!menu) return;
438
+ Array.prototype.forEach.call(menu.querySelectorAll("[data-filter]"), function (option) {
439
+ var active = option.getAttribute("data-filter") === currentFilter;
440
+ option.setAttribute("aria-pressed", active ? "true" : "false");
441
+ option.classList.toggle("omni-filter-option-active", active);
442
+ });
443
+ }
444
+
445
+ function updateFilter(nextFilter) {
446
+ if (!FILTER_LABELS[nextFilter]) return;
447
+ currentFilter = nextFilter;
448
+ activeIndex = 0;
449
+ setFilterMenuOpen(false);
450
+ renderFilter();
451
+ render();
452
+ }
453
+
216
454
  function render() {
217
455
  var input = getInput();
218
456
  var q = input ? input.value.trim() : "";
@@ -223,12 +461,15 @@ const { config = null } = Astro.props;
223
461
  var docsItemsEl = getDocsItems();
224
462
  var emptyEl = getEmpty();
225
463
  var emptyText = getEmptyText();
464
+ var isLoading = !!loadingEl && loadingEl.style.display !== "none";
465
+ var results = visibleResults();
226
466
 
227
467
  recentsList = !q ? getRecents() : [];
228
468
  if (loadingEl && !q) loadingEl.style.display = "none";
469
+ renderFilter();
229
470
 
230
471
  if (!q) {
231
- currentResults = [];
472
+ if (currentResults.length) setCurrentResults([]);
232
473
  if (recentGroup) recentGroup.style.display = recentsList.length ? "block" : "none";
233
474
  if (recentItemsEl) {
234
475
  recentItemsEl.innerHTML = recentsList.map(function (r, i) {
@@ -245,18 +486,22 @@ const { config = null } = Astro.props;
245
486
  if (emptyText) emptyText.textContent = "Type to search the docs, or browse recent items.";
246
487
  } else {
247
488
  if (recentGroup) recentGroup.style.display = "none";
248
- if (docsGroup) docsGroup.style.display = currentResults.length ? "block" : "none";
489
+ if (docsGroup) docsGroup.style.display = results.length ? "block" : "none";
249
490
  if (docsItemsEl) {
250
- docsItemsEl.innerHTML = currentResults.map(function (r, i) {
491
+ docsItemsEl.innerHTML = results.map(function (r, i) {
251
492
  return renderItem(
252
- { id: r.url, label: r.content, url: r.url, subtitle: r.description || "Page" },
493
+ { id: r.url, label: displayLabelForResult(r), url: r.url, subtitle: breadcrumbForUrl(r.url), description: r.description || "" },
253
494
  i === activeIndex,
254
495
  false
255
496
  );
256
497
  }).join("");
257
498
  }
258
- if (emptyEl) emptyEl.style.display = (currentResults.length === 0 && !loadingEl.style.display) ? "none" : (currentResults.length === 0 ? "block" : "none");
259
- if (emptyText) emptyText.textContent = "No results found. Try a different query.";
499
+ if (emptyEl) emptyEl.style.display = (results.length === 0 && !isLoading) ? "block" : "none";
500
+ if (emptyText) {
501
+ emptyText.textContent = currentResults.length > 0
502
+ ? "No " + (FILTER_LABELS[currentFilter] || "all").toLowerCase() + " results found."
503
+ : "No results found. Try a different query.";
504
+ }
260
505
  }
261
506
 
262
507
  scrollActiveIntoView();
@@ -282,14 +527,6 @@ const { config = null } = Astro.props;
282
527
  var withinDialog = e.target.closest && e.target.closest("#fd-search-dialog");
283
528
  if (!withinDialog) return;
284
529
 
285
- var ext = e.target.closest && e.target.closest("a.omni-item-ext");
286
- if (ext) {
287
- e.preventDefault();
288
- e.stopPropagation();
289
- var url = ext.getAttribute("data-ext-url") || ext.getAttribute("href");
290
- navigateToUrl(url, true);
291
- return;
292
- }
293
530
  var row = e.target.closest && e.target.closest(".omni-item[data-url]");
294
531
  if (row) {
295
532
  e.preventDefault();
@@ -325,35 +562,6 @@ const { config = null } = Astro.props;
325
562
  document.addEventListener("mouseover", handleOmniItemMouseOver, true);
326
563
  }
327
564
 
328
- function ensureListboxClickBound() {
329
- var listbox = getListbox();
330
- if (!listbox || listbox._fdOmniClickBound) return;
331
- listbox._fdOmniClickBound = true;
332
- function onListboxClick(e) {
333
- var ext = e.target.closest && e.target.closest("a.omni-item-ext");
334
- if (ext) {
335
- e.preventDefault();
336
- e.stopPropagation();
337
- var url = ext.getAttribute("data-ext-url") || ext.getAttribute("href");
338
- navigateToUrl(url, true);
339
- return;
340
- }
341
- var row = e.target.closest && e.target.closest(".omni-item[data-url]");
342
- if (row) {
343
- e.preventDefault();
344
- e.stopPropagation();
345
- var url = row.getAttribute("data-url");
346
- if (!url) return;
347
- var labelEl = row.querySelector(".omni-item-label");
348
- var label = labelEl ? labelEl.textContent : url;
349
- saveRecent({ id: url, label: label, url: url });
350
- closeDialog();
351
- navigateToUrl(url, url.startsWith("http"));
352
- }
353
- }
354
- listbox.addEventListener("click", onListboxClick, true);
355
- }
356
-
357
565
  function scrollActiveIntoView() {
358
566
  var q = getInput() && getInput().value.trim();
359
567
  var container = q ? getDocsItems() : getRecentItems();
@@ -385,8 +593,10 @@ const { config = null } = Astro.props;
385
593
  var input = getInput();
386
594
  var q = (input && input.value || "").trim();
387
595
  if (getLoading()) getLoading().style.display = "none";
388
- currentResults = [];
596
+ if (abortController) abortController.abort();
597
+ setCurrentResults([]);
389
598
  activeIndex = 0;
599
+ setFilterMenuOpen(false);
390
600
  if (getDocsGroup()) getDocsGroup().style.display = "none";
391
601
  if (getDocsItems()) getDocsItems().innerHTML = "";
392
602
  if (getRecentGroup()) getRecentGroup().style.display = !q && getRecents().length ? "block" : "none";
@@ -396,19 +606,38 @@ const { config = null } = Astro.props;
396
606
 
397
607
  clearTimeout(debounceTimer);
398
608
  if (!q) return;
609
+ var requestUrl = withLang("/api/docs?query=" + encodeURIComponent(q));
610
+ if (searchCache.has(requestUrl)) {
611
+ setCurrentResults(searchCache.get(requestUrl) || []);
612
+ activeIndex = 0;
613
+ render();
614
+ return;
615
+ }
399
616
  debounceTimer = setTimeout(function () {
617
+ var controller = new AbortController();
618
+ abortController = controller;
400
619
  if (getLoading()) getLoading().style.display = "flex";
401
- fetch(withLang("/api/docs?query=" + encodeURIComponent(q)))
620
+ fetch(requestUrl, { signal: controller.signal })
402
621
  .then(function (res) { return res.ok ? res.json() : []; })
403
622
  .then(function (data) {
404
- currentResults = Array.isArray(data) ? data : [];
623
+ if (controller.signal.aborted) return;
624
+ setCurrentResults(Array.isArray(data) ? data : []);
625
+ if (searchCache.size >= 20) {
626
+ var firstKey = searchCache.keys().next().value;
627
+ if (firstKey) searchCache.delete(firstKey);
628
+ }
629
+ searchCache.set(requestUrl, currentResults);
405
630
  activeIndex = 0;
406
631
  if (getLoading()) getLoading().style.display = "none";
407
632
  render();
408
633
  })
409
634
  .catch(function () {
635
+ if (controller.signal.aborted) return;
410
636
  if (getLoading()) getLoading().style.display = "none";
411
637
  render();
638
+ })
639
+ .finally(function () {
640
+ if (abortController === controller) abortController = null;
412
641
  });
413
642
  }, DEBOUNCE_MS);
414
643
  }
@@ -419,6 +648,8 @@ const { config = null } = Astro.props;
419
648
  var content = getContent();
420
649
  var input = getInput();
421
650
  var closeBtn = document.getElementById("fd-omni-close");
651
+ var filterButton = getFilterButton();
652
+ var filterMenu = getFilterMenu();
422
653
 
423
654
  if (!dialog || !input) return;
424
655
 
@@ -433,10 +664,41 @@ const { config = null } = Astro.props;
433
664
  else if (e.key === "Enter") { e.preventDefault(); executeActive(); }
434
665
  });
435
666
 
667
+ if (filterButton && !filterButton.hasAttribute("data-fd-bound")) {
668
+ filterButton.setAttribute("data-fd-bound", "true");
669
+ filterButton.addEventListener("click", function (e) {
670
+ e.preventDefault();
671
+ e.stopPropagation();
672
+ var menu = getFilterMenu();
673
+ var isOpen = !!menu && menu.style.display !== "none";
674
+ setFilterMenuOpen(!isOpen);
675
+ });
676
+ }
677
+
678
+ if (filterMenu && !filterMenu.hasAttribute("data-fd-bound")) {
679
+ filterMenu.setAttribute("data-fd-bound", "true");
680
+ filterMenu.addEventListener("click", function (e) {
681
+ var option = e.target.closest && e.target.closest("[data-filter]");
682
+ if (!option) return;
683
+ e.preventDefault();
684
+ e.stopPropagation();
685
+ updateFilter(option.getAttribute("data-filter"));
686
+ });
687
+ }
688
+
436
689
  document.addEventListener("keydown", function (e) {
437
690
  if (e.key === "Escape") closeDialog();
438
691
  });
439
692
 
693
+ if (!document.body.hasAttribute("data-fd-omni-filter-close")) {
694
+ document.body.setAttribute("data-fd-omni-filter-close", "true");
695
+ document.addEventListener("click", function (e) {
696
+ var target = e.target;
697
+ if (target && target.closest && target.closest(".omni-footer-filter")) return;
698
+ setFilterMenuOpen(false);
699
+ });
700
+ }
701
+
440
702
  if (!document.body.hasAttribute("data-fd-cmdk-registered")) {
441
703
  document.body.setAttribute("data-fd-cmdk-registered", "true");
442
704
  document.addEventListener("keydown", function (e) {
@@ -605,6 +605,7 @@ figure.shiki:has(figcaption) > div:first-child,
605
605
  }
606
606
 
607
607
  .omni-group-label {
608
+ display: block !important;
608
609
  letter-spacing: 0.14em;
609
610
  color: color-mix(in srgb, var(--color-fd-foreground) 72%, transparent) !important;
610
611
  }
@@ -605,6 +605,7 @@ figure.shiki:has(figcaption) > div:first-child,
605
605
  }
606
606
 
607
607
  .omni-group-label {
608
+ display: block !important;
608
609
  letter-spacing: 0.14em;
609
610
  color: color-mix(in srgb, var(--color-fd-foreground) 72%, transparent) !important;
610
611
  }
package/styles/omni.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /* ═══════════════════════════════════════════════════════════════════
2
- * Omni Command Palette — core styles (clone of website/components/ui/omni-command-palette)
3
- * Uses same CSS variables as docs so it auto-adapts to every theme.
2
+ * Omni Command Palette — core styles
3
+ * Uses fumadocs CSS variables so it auto-adapts to every theme.
4
4
  * ═══════════════════════════════════════════════════════════════════ */
5
5
 
6
6
  @keyframes omni-fade-in {
@@ -22,7 +22,7 @@
22
22
  @keyframes omni-scale-in {
23
23
  from {
24
24
  opacity: 0;
25
- transform: translateX(-50%) scale(0.96) translateY(-4px);
25
+ transform: translateX(-50%) scale(0.98) translateY(-6px);
26
26
  }
27
27
  to {
28
28
  opacity: 1;
@@ -36,7 +36,7 @@
36
36
  }
37
37
  to {
38
38
  opacity: 0;
39
- transform: translateX(-50%) scale(0.96) translateY(-4px);
39
+ transform: translateX(-50%) scale(0.98) translateY(-6px);
40
40
  }
41
41
  }
42
42
 
@@ -56,39 +56,40 @@
56
56
  .omni-overlay {
57
57
  position: fixed;
58
58
  inset: 0;
59
- z-index: 100;
60
- background: rgba(0, 0, 0, 0.55);
61
- backdrop-filter: blur(4px);
59
+ z-index: 50;
60
+ background: rgba(0, 0, 0, 0.2);
61
+ backdrop-filter: blur(2px);
62
62
  animation: omni-fade-in 150ms ease-out;
63
63
  }
64
64
 
65
65
  .omni-content {
66
+ --omni-content-top: clamp(5rem, 16vh, 7rem);
66
67
  position: fixed;
67
- z-index: 101;
68
- top: 18%;
68
+ z-index: 51;
69
+ top: var(--omni-content-top);
69
70
  left: 50%;
70
71
  transform: translateX(-50%);
71
- width: min(720px, calc(100% - 32px));
72
- border-radius: var(--radius, 0.75rem);
73
- border: 1px solid var(--color-fd-border, #262626);
74
- background: var(--color-fd-popover, #0c0c0c);
75
- color: var(--color-fd-foreground, #fafafa);
76
- box-shadow:
77
- 0 24px 60px -12px rgba(0, 0, 0, 0.5),
78
- 0 0 0 1px rgba(255, 255, 255, 0.04);
72
+ display: flex;
73
+ flex-direction: column;
74
+ width: min(720px, calc(100% - 1rem));
75
+ border-radius: 0.75rem;
76
+ border: 1px solid color-mix(in srgb, var(--color-fd-border, #d4d4d4) 70%, transparent);
77
+ background: var(--color-fd-popover, #fafafa);
78
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
79
+ font-family: var(--font-sans, system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
80
+ ui-sans-serif, sans-serif);
81
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
79
82
  outline: none;
80
- overflow: hidden;
83
+ overscroll-behavior: contain;
81
84
  animation: omni-scale-in 200ms cubic-bezier(0.16, 1, 0.3, 1);
82
85
  }
83
86
 
84
87
  @media (max-width: 639px) {
85
88
  .omni-content {
86
- top: 10%;
87
- width: calc(100% - 24px);
88
- max-height: 75vh;
89
- }
90
- .omni-kbd {
91
- display: none;
89
+ --omni-content-top: 1rem;
90
+ top: var(--omni-content-top);
91
+ width: calc(100% - 1rem);
92
+ max-height: calc(100vh - 2rem);
92
93
  }
93
94
  }
94
95
 
@@ -100,52 +101,77 @@
100
101
  display: flex;
101
102
  align-items: center;
102
103
  gap: 0.5rem;
103
- padding: 0.625rem 0.875rem;
104
+ padding: 0.75rem;
104
105
  }
105
106
  .omni-search-icon {
106
107
  color: var(--color-fd-muted-foreground, #a3a3a3);
107
108
  flex-shrink: 0;
108
109
  }
110
+ .omni-search-icon svg {
111
+ width: 1.25rem;
112
+ height: 1.25rem;
113
+ }
109
114
  .omni-search-input {
115
+ width: 0;
110
116
  flex: 1;
111
117
  background: transparent;
112
118
  outline: none;
113
- font-size: 0.875rem;
114
- line-height: 1.25rem;
115
- color: var(--color-fd-foreground, #fafafa);
119
+ font-size: 1.125rem;
120
+ line-height: 1.75rem;
121
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
116
122
  border: none;
117
123
  }
118
124
  .omni-search-input::placeholder {
119
125
  color: var(--color-fd-muted-foreground, #a3a3a3);
120
126
  }
121
127
  .omni-kbd {
122
- border-radius: 0.25rem;
123
- background: var(--color-fd-muted, #262626);
124
- padding: 0.125rem 0.375rem;
125
- font-size: 10px;
128
+ border-radius: 0.375rem;
129
+ border: 1px solid var(--color-fd-border, #d4d4d4);
130
+ background: transparent;
131
+ padding: 0.375rem 0.5rem;
132
+ font-size: 0.75rem;
126
133
  color: var(--color-fd-muted-foreground, #a3a3a3);
127
- font-family: inherit;
128
- line-height: 1.4;
134
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
135
+ "Liberation Mono", "Courier New", monospace);
136
+ line-height: 1rem;
129
137
  }
130
138
  .omni-close-btn {
131
- margin-left: 0.25rem;
132
- border-radius: 0.25rem;
133
- padding: 0.25rem;
139
+ display: inline-flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ gap: 0.25rem;
143
+ min-width: 2.5rem;
144
+ border-radius: 0.375rem;
145
+ border: 1px solid var(--color-fd-border, #d4d4d4);
146
+ padding: 0.375rem 0.5rem;
134
147
  color: var(--color-fd-muted-foreground, #a3a3a3);
135
- transition: background 120ms;
148
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
149
+ "Liberation Mono", "Courier New", monospace);
150
+ font-size: 0.75rem;
151
+ line-height: 1rem;
152
+ font-weight: 500;
153
+ transition:
154
+ background 120ms,
155
+ color 120ms;
136
156
  cursor: pointer;
137
- border: none;
138
157
  background: none;
139
158
  }
140
159
  .omni-close-btn:hover {
141
- background: var(--color-fd-muted, #262626);
160
+ color: var(--color-fd-accent-foreground, var(--color-fd-foreground, #171717));
161
+ background: var(--color-fd-accent, #e5e5e5);
162
+ }
163
+ .omni-close-btn svg {
164
+ display: none;
142
165
  }
143
166
 
144
167
  /* Body / listbox */
145
168
  .omni-body {
146
- max-height: 60vh;
169
+ flex: 1 1 auto;
170
+ min-height: 0;
171
+ max-height: min(540px, calc(100vh - var(--omni-content-top, 7rem) - 8rem));
147
172
  overflow: auto;
148
- padding: 0.25rem;
173
+ overscroll-behavior: contain;
174
+ padding: 0.5rem;
149
175
  }
150
176
  .omni-body::-webkit-scrollbar {
151
177
  width: 6px;
@@ -163,36 +189,32 @@
163
189
  display: flex;
164
190
  align-items: center;
165
191
  gap: 0.5rem;
166
- padding: 0.5rem 0.75rem;
192
+ padding: 0.75rem 0.625rem;
167
193
  color: var(--color-fd-muted-foreground, #a3a3a3);
168
- font-size: 0.75rem;
194
+ font-size: 0.875rem;
169
195
  }
170
196
 
171
197
  /* Groups */
172
198
  .omni-group {
173
- padding: 0.25rem 0;
199
+ padding: 0;
174
200
  }
175
201
  .omni-group-label {
176
- padding: 0.25rem 0.75rem;
177
- font-size: 10px;
178
- text-transform: uppercase;
179
- letter-spacing: 0.06em;
180
- color: var(--color-fd-muted-foreground, #a3a3a3);
181
- font-weight: 500;
202
+ display: none;
182
203
  }
183
204
  .omni-group-items {
184
205
  display: flex;
185
206
  flex-direction: column;
207
+ gap: 0.25rem;
186
208
  }
187
209
 
188
210
  /* Items */
189
211
  .omni-item {
190
- display: flex;
212
+ position: relative;
213
+ display: block;
191
214
  width: 100%;
192
- align-items: center;
193
- gap: 0.75rem;
194
- border-radius: calc(var(--radius, 0.75rem) - 4px);
195
- padding: 0.5rem 0.75rem;
215
+ flex-shrink: 0;
216
+ border-radius: 0.5rem;
217
+ padding: 0.75rem 0.875rem;
196
218
  text-align: left;
197
219
  font-size: 0.875rem;
198
220
  line-height: 1.25rem;
@@ -204,10 +226,10 @@
204
226
  }
205
227
  .omni-item:hover,
206
228
  .omni-item-active {
207
- background: color-mix(in srgb, var(--color-fd-accent, #262626) 60%, transparent);
229
+ background: var(--color-fd-accent, rgba(209, 209, 209, 0.5));
208
230
  }
209
231
  .omni-item-active {
210
- color: var(--color-fd-accent-foreground, #fafafa);
232
+ color: var(--color-fd-accent-foreground, #171717);
211
233
  }
212
234
  .omni-item-disabled {
213
235
  opacity: 0.5;
@@ -215,51 +237,56 @@
215
237
  }
216
238
 
217
239
  .omni-item-icon {
218
- flex-shrink: 0;
219
- color: var(--color-fd-muted-foreground, #a3a3a3);
240
+ display: none;
220
241
  }
221
242
  .omni-item-active .omni-item-icon {
222
243
  color: var(--color-fd-accent-foreground, #fafafa);
223
244
  }
224
245
  .omni-item-text {
225
- flex: 1;
226
246
  min-width: 0;
227
247
  }
228
248
  .omni-item-label {
249
+ min-width: 0;
229
250
  overflow: hidden;
230
251
  text-overflow: ellipsis;
231
252
  white-space: nowrap;
253
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
254
+ font-weight: 500;
255
+ line-height: 1.35;
232
256
  }
233
257
  .omni-item-subtitle {
258
+ min-width: 0;
234
259
  overflow: hidden;
235
260
  text-overflow: ellipsis;
236
261
  white-space: nowrap;
237
262
  font-size: 0.75rem;
263
+ line-height: 1rem;
238
264
  color: var(--color-fd-muted-foreground, #a3a3a3);
239
- opacity: 0.8;
265
+ opacity: 1;
240
266
  }
241
267
  .omni-item-active .omni-item-subtitle {
242
- color: var(--color-fd-accent-foreground, #fafafa);
243
- opacity: 0.7;
268
+ color: var(--color-fd-muted-foreground, #737373);
269
+ opacity: 1;
270
+ }
271
+ .omni-item-description {
272
+ min-width: 0;
273
+ overflow: hidden;
274
+ display: -webkit-box;
275
+ -webkit-line-clamp: 3;
276
+ -webkit-box-orient: vertical;
277
+ margin-top: 0.625rem;
278
+ padding-left: 0.75rem;
279
+ border-left: 1px solid color-mix(in srgb, var(--color-fd-border, #d4d4d4) 70%, transparent);
280
+ color: color-mix(in srgb, var(--color-fd-popover-foreground, #272727) 82%, transparent);
281
+ font-size: 0.875rem;
282
+ line-height: 1.5rem;
283
+ font-weight: 400;
244
284
  }
245
285
  .omni-item-badge {
246
- color: var(--color-fd-muted-foreground, #a3a3a3);
247
- flex-shrink: 0;
286
+ display: none;
248
287
  }
249
288
  .omni-item-ext {
250
- position: relative;
251
- z-index: 2;
252
- display: inline-flex;
253
- align-items: center;
254
- justify-content: center;
255
- padding: 0.25rem;
256
- border-radius: 0.25rem;
257
- color: var(--color-fd-muted-foreground, #a3a3a3);
258
- flex-shrink: 0;
259
- transition:
260
- color 100ms,
261
- background 100ms;
262
- text-decoration: none;
289
+ display: none;
263
290
  }
264
291
  .omni-item-ext:hover {
265
292
  color: var(--color-fd-foreground, #fafafa);
@@ -281,27 +308,20 @@
281
308
  border-radius: 0.25rem;
282
309
  background: var(--color-fd-muted, #262626);
283
310
  padding: 0.125rem 0.25rem;
284
- font-family: inherit;
311
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
312
+ "Liberation Mono", "Courier New", monospace);
285
313
  }
286
314
  .omni-item-chevron {
287
- width: 0.875rem;
288
- height: 0.875rem;
289
- margin-left: 0.25rem;
290
- color: var(--color-fd-muted-foreground, #a3a3a3);
291
- opacity: 0;
292
- transition: opacity 120ms;
293
- flex-shrink: 0;
294
- }
295
- .omni-item:hover .omni-item-chevron {
296
- opacity: 1;
315
+ display: none;
297
316
  }
298
-
299
317
  /* Highlight */
300
318
  .omni-highlight {
301
- border-radius: 2px;
302
- background: color-mix(in srgb, var(--color-fd-primary, #6366f1) 30%, transparent);
303
- padding: 0 2px;
304
- color: inherit;
319
+ border-radius: 0;
320
+ background: transparent;
321
+ padding: 0;
322
+ color: var(--color-fd-primary, #ca8a04);
323
+ text-decoration: underline;
324
+ text-underline-offset: 2px;
305
325
  }
306
326
 
307
327
  /* Empty states */
@@ -311,7 +331,7 @@
311
331
  color: var(--color-fd-muted-foreground, #a3a3a3);
312
332
  }
313
333
  .omni-empty {
314
- padding: 2rem 0.75rem;
334
+ padding: 1.5rem 0.75rem;
315
335
  text-align: center;
316
336
  font-size: 0.875rem;
317
337
  color: var(--color-fd-muted-foreground, #a3a3a3);
@@ -330,30 +350,105 @@
330
350
  /* Footer */
331
351
  .omni-footer {
332
352
  border-top: 1px solid var(--color-fd-border, #262626);
353
+ background: color-mix(in srgb, var(--color-fd-secondary, #f5f5f5) 50%, transparent);
333
354
  }
334
355
  .omni-footer-inner {
335
356
  display: flex;
336
357
  align-items: center;
337
358
  justify-content: space-between;
338
- padding: 0.5rem 0.75rem;
359
+ gap: 0.75rem;
360
+ padding: 0.625rem 0.75rem;
339
361
  font-size: 0.75rem;
340
362
  color: var(--color-fd-muted-foreground, #a3a3a3);
341
363
  }
342
364
  .omni-footer-hints {
343
365
  display: flex;
344
366
  align-items: center;
345
- gap: 1rem;
367
+ flex-wrap: wrap;
368
+ gap: 0.75rem;
346
369
  }
347
370
  .omni-footer-hint {
348
371
  display: flex;
349
372
  align-items: center;
350
373
  gap: 0.25rem;
374
+ white-space: nowrap;
375
+ }
376
+ .omni-footer-hint svg {
377
+ box-sizing: border-box;
378
+ width: 1.375rem;
379
+ height: 1.375rem;
380
+ padding: 0.25rem;
381
+ border-radius: 0.375rem;
382
+ border: 1px solid var(--color-fd-border, #d4d4d4);
383
+ background: color-mix(in srgb, var(--color-fd-muted, #e5e5e5) 70%, transparent);
384
+ color: var(--color-fd-muted-foreground, #737373);
385
+ flex-shrink: 0;
351
386
  }
352
387
  .omni-footer-hint-desktop {
353
388
  display: none;
354
389
  }
390
+ .omni-footer-filter {
391
+ position: relative;
392
+ display: inline-flex;
393
+ align-items: center;
394
+ gap: 0.5rem;
395
+ margin-left: auto;
396
+ white-space: nowrap;
397
+ }
355
398
  @media (min-width: 640px) {
356
399
  .omni-footer-hint-desktop {
357
400
  display: flex;
358
401
  }
359
402
  }
403
+ .omni-filter-label {
404
+ color: var(--color-fd-muted-foreground, #a3a3a3);
405
+ }
406
+ .omni-filter-value {
407
+ display: inline-flex;
408
+ align-items: center;
409
+ gap: 0.25rem;
410
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
411
+ }
412
+ .omni-filter-button {
413
+ display: inline-flex;
414
+ align-items: center;
415
+ gap: 0.25rem;
416
+ border: 0;
417
+ background: transparent;
418
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
419
+ font: inherit;
420
+ cursor: pointer;
421
+ padding: 0;
422
+ }
423
+ .omni-filter-button:hover {
424
+ color: var(--color-fd-accent-foreground, var(--color-fd-foreground, #171717));
425
+ }
426
+ .omni-filter-menu {
427
+ position: absolute;
428
+ right: 0;
429
+ bottom: calc(100% + 0.5rem);
430
+ z-index: 1;
431
+ width: 10rem;
432
+ border: 1px solid var(--color-fd-border, #d4d4d4);
433
+ border-radius: 0.5rem;
434
+ background: var(--color-fd-popover, #fafafa);
435
+ padding: 0.25rem;
436
+ box-shadow: 0 16px 32px -20px rgba(0, 0, 0, 0.45);
437
+ }
438
+ .omni-filter-option {
439
+ display: flex;
440
+ width: 100%;
441
+ align-items: center;
442
+ border: 0;
443
+ border-radius: 0.375rem;
444
+ background: transparent;
445
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground, #272727));
446
+ cursor: pointer;
447
+ font: inherit;
448
+ padding: 0.5rem 0.625rem;
449
+ text-align: left;
450
+ }
451
+ .omni-filter-option:hover,
452
+ .omni-filter-option-active {
453
+ background: var(--color-fd-accent, rgba(209, 209, 209, 0.5));
454
+ }
@@ -840,6 +840,56 @@ code:not(pre code) {
840
840
  color: var(--color-fd-foreground) !important;
841
841
  }
842
842
 
843
+ /* ─── Omni Command Palette — pixel-border theme ──────────────────── */
844
+
845
+ .omni-content {
846
+ border-radius: 0 !important;
847
+ border: 2px solid var(--color-fd-border, hsl(0 0% 15%)) !important;
848
+ box-shadow:
849
+ 4px 4px 0 0 var(--color-fd-border, hsl(0 0% 15%)),
850
+ 0 24px 60px -12px rgba(0, 0, 0, 0.5) !important;
851
+ }
852
+
853
+ .omni-item,
854
+ .omni-kbd,
855
+ .omni-kbd-sm,
856
+ .omni-close-btn,
857
+ .omni-empty-icon {
858
+ border-radius: 0 !important;
859
+ }
860
+
861
+ .omni-kbd,
862
+ .omni-kbd-sm {
863
+ border: 1px solid var(--color-fd-border, hsl(0 0% 15%)) !important;
864
+ }
865
+
866
+ .omni-group-label,
867
+ .omni-footer-inner {
868
+ font-family: var(--fd-font-mono, ui-monospace, monospace) !important;
869
+ }
870
+
871
+ .omni-search-input {
872
+ font-family: var(--fd-font-sans, system-ui, -apple-system, sans-serif) !important;
873
+ }
874
+
875
+ .omni-group-label {
876
+ letter-spacing: 0.08em !important;
877
+ }
878
+
879
+ .omni-footer-hint svg {
880
+ border-radius: 0 !important;
881
+ }
882
+
883
+ .omni-filter-menu,
884
+ .omni-filter-option {
885
+ border-radius: 0 !important;
886
+ }
887
+
888
+ .omni-highlight {
889
+ background: color-mix(in srgb, var(--color-fd-primary, #6366f1) 25%, transparent) !important;
890
+ border-radius: 0 !important;
891
+ }
892
+
843
893
  /* ─── Feedback (pixel-border theme) ──────────────────────────────── */
844
894
 
845
895
  .fd-feedback-input,