From 82173bafbeb199e4336d65d7968b561af45b77c2 Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Wed, 3 Sep 2025 08:36:59 +0530 Subject: [PATCH 1/4] add keyboard shortcut and enhance input styling --- theme/js/osl-search.js | 327 ++++++++++++++++++++++++++++------------- 1 file changed, 227 insertions(+), 100 deletions(-) diff --git a/theme/js/osl-search.js b/theme/js/osl-search.js index c38d4898..f1eeff92 100644 --- a/theme/js/osl-search.js +++ b/theme/js/osl-search.js @@ -4,17 +4,17 @@ */ (function () { - 'use strict'; + "use strict"; - const INDEX_URL = '/search/search_index.json'; + const INDEX_URL = "/search/search_index.json"; const MAX_RESULTS = 8; const MIN_QUERY_LEN = 2; // Internal state (built once) - let _docs = null; // array of { title, text, location } - let _byRef = null; // Map(location -> doc) - let _idx = null; // lunr.Index - let _building = null; // build promise (avoid duplicate builds) + let _docs = null; // array of { title, text, location } + let _byRef = null; // Map(location -> doc) + let _idx = null; // lunr.Index + let _building = null; // build promise (avoid duplicate builds) // --- Utilities ----------------------------------------------------------- @@ -23,11 +23,11 @@ // keep full URLs as-is if (/^([a-z]+:)?\/\//i.test(href)) return href; // force site-rooted path - return '/' + href.replace(/^\/+/, ''); + return "/" + href.replace(/^\/+/, ""); } function injectBaseStylesOnce() { - if (document.getElementById('osl-search-styles')) return; + if (document.getElementById("osl-search-styles")) return; const css = ` .osl-search-panel { position: absolute; @@ -71,9 +71,60 @@ opacity: .8; } .osl-search-mark { background: rgba(255, 208, 0, .35); border-radius: .2rem; } + + /* Enhanced focus and outline styles */ + #mkdocs-search, + #mkdocs-search-mobile { + outline: 1px solid #e0e0e0; + outline-offset: 2px; + transition: all 0.2s ease; + border: 1px solid var(--border-color, rgba(255,255,255,.12)); + border-radius: 6px; + } + + #mkdocs-search:hover, + #mkdocs-search-mobile:hover { + border-color: var(--md-primary-fg-color, #1976d2); + } + + #mkdocs-search:focus, + #mkdocs-search-mobile:focus { + outline: 2px solid var(--md-primary-fg-color, #1976d2); + border-color: var(--md-primary-fg-color, #1976d2); + box-shadow: 0 0 0 4px var(--md-primary-fg-color-transparent, rgba(25, 118, 210, 0.1)); + } + + /* Search container styles */ + .search-container { + position: relative; + display: inline-block; + } + + .search-container:focus-within .search-hint { + opacity: 1; + } + + /* Add keyboard shortcut hint */ + .search-hint { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + font-size: 12px; + opacity: 0.6; + pointer-events: none; + transition: opacity 0.2s ease; + } + + /* Hide hint when input has content */ + .search-container input:not(:placeholder-shown) + .search-hint, + .search-container input:focus + .search-hint { + opacity: 0; + visibility: hidden; + } `; - const style = document.createElement('style'); - style.id = 'osl-search-styles'; + const style = document.createElement("style"); + style.id = "osl-search-styles"; style.textContent = css; document.head.appendChild(style); } @@ -81,62 +132,69 @@ async function ensureLunrReady() { if (window.lunr) return; await new Promise((resolve) => { - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', resolve, { once: true }); + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", resolve, { once: true }); } else { setTimeout(resolve, 0); } }); - if (!window.lunr) throw new Error('Lunr failed to load'); + if (!window.lunr) throw new Error("Lunr failed to load"); } async function fetchIndexJSON() { - const res = await fetch(INDEX_URL, { credentials: 'same-origin' }); + const res = await fetch(INDEX_URL, { credentials: "same-origin" }); if (!res.ok) throw new Error(`Failed to fetch ${INDEX_URL}: ${res.status}`); const json = await res.json(); // MkDocs "search" plugin typically returns { docs: [...] } - return Array.isArray(json) ? json : (json.docs || []); + return Array.isArray(json) ? json : json.docs || []; } function normalizeDocs(arr) { - return arr.map(d => ({ - title: d.title || '', - text: d.text || '', - location: d.location || '' + return arr.map((d) => ({ + title: d.title || "", + text: d.text || "", + location: d.location || "", })); } function buildSnippet(text, rawQuery) { - if (!text) return ''; - const q = (rawQuery || '').trim(); + if (!text) return ""; + const q = (rawQuery || "").trim(); const MAX = 160; - if (!q) return text.slice(0, MAX) + (text.length > MAX ? '…' : ''); + if (!q) return text.slice(0, MAX) + (text.length > MAX ? "…" : ""); // find first occurrence of any term (basic, case-insensitive) const terms = q.split(/\s+/).filter(Boolean); - let hit = -1, termUsed = ''; + let hit = -1, + termUsed = ""; for (const t of terms) { const idx = text.toLowerCase().indexOf(t.toLowerCase()); - if (idx !== -1 && (hit === -1 || idx < hit)) { hit = idx; termUsed = t; } + if (idx !== -1 && (hit === -1 || idx < hit)) { + hit = idx; + termUsed = t; + } } if (hit === -1) { - return text.slice(0, MAX) + (text.length > MAX ? '…' : ''); + return text.slice(0, MAX) + (text.length > MAX ? "…" : ""); } const start = Math.max(0, hit - 40); const end = Math.min(text.length, hit + 120); - let snip = (start > 0 ? '…' : '') + text.slice(start, end) + (end < text.length ? '…' : ''); + let snip = + (start > 0 ? "…" : "") + + text.slice(start, end) + + (end < text.length ? "…" : ""); // simple highlight for all terms - terms.forEach(t => { + terms.forEach((t) => { if (!t) return; - const re = new RegExp(`(${escapeRegExp(t)})`, 'ig'); + const re = new RegExp(`(${escapeRegExp(t)})`, "ig"); snip = snip.replace(re, '$1'); }); return snip; } function escapeRegExp(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } async function ensureIndex() { @@ -148,20 +206,20 @@ const raw = await fetchIndexJSON(); _docs = normalizeDocs(raw); - _byRef = new Map(_docs.map(d => [d.location, d])); + _byRef = new Map(_docs.map((d) => [d.location, d])); // Build index - const hasMulti = typeof lunr.multiLanguage === 'function'; + const hasMulti = typeof lunr.multiLanguage === "function"; _idx = lunr(function () { if (hasMulti) { // Adjust languages if you only need some of them - this.use(lunr.multiLanguage('en', 'es', 'pt')); + this.use(lunr.multiLanguage("en", "es", "pt")); } - this.ref('location'); - this.field('title', { boost: 10 }); - this.field('text'); + this.ref("location"); + this.field("title", { boost: 10 }); + this.field("text"); - _docs.forEach(doc => this.add(doc)); + _docs.forEach((doc) => this.add(doc)); }); return _idx; @@ -180,27 +238,38 @@ // Keep it simple; allow prefix matches let q = query.trim(); // Improve small queries a bit: foo -> foo* - if (!/[~^*]/.test(q)) q = q.split(/\s+/).map(t => t + '*').join(' '); + if (!/[~^*]/.test(q)) + q = q + .split(/\s+/) + .map((t) => t + "*") + .join(" "); let hits = []; try { hits = _idx.search(q); } catch (e) { // fallback: plain search without wildcard if syntax error - try { hits = _idx.search(query); } catch (_e) { hits = []; } + try { + hits = _idx.search(query); + } catch (_e) { + hits = []; + } } - return hits.slice(0, MAX_RESULTS).map(h => { - const doc = _byRef.get(h.ref); - return doc ? { doc, score: h.score } : null; - }).filter(Boolean); + return hits + .slice(0, MAX_RESULTS) + .map((h) => { + const doc = _byRef.get(h.ref); + return doc ? { doc, score: h.score } : null; + }) + .filter(Boolean); } // --- UI (panel) --------------------------------------------------------- function mkPanel() { - const div = document.createElement('div'); - div.className = 'osl-search-panel'; - div.setAttribute('role', 'listbox'); - div.style.display = 'none'; + const div = document.createElement("div"); + div.className = "osl-search-panel"; + div.setAttribute("role", "listbox"); + div.style.display = "none"; document.body.appendChild(div); return div; } @@ -216,26 +285,29 @@ } function renderResults(panel, items, rawQuery) { - panel.innerHTML = ''; + panel.innerHTML = ""; if (!items.length) { - const empty = document.createElement('div'); - empty.className = 'osl-search-empty'; - empty.textContent = (rawQuery && rawQuery.length >= MIN_QUERY_LEN) ? 'No results' : 'Type to search…'; + const empty = document.createElement("div"); + empty.className = "osl-search-empty"; + empty.textContent = + rawQuery && rawQuery.length >= MIN_QUERY_LEN + ? "No results" + : "Type to search…"; panel.appendChild(empty); return; } items.forEach(({ doc }, idx) => { - const a = document.createElement('a'); - a.className = 'osl-search-item'; + const a = document.createElement("a"); + a.className = "osl-search-item"; a.href = toAbsolute(doc.location); - const title = document.createElement('div'); - title.className = 'osl-search-title'; - title.innerHTML = doc.title || '(untitled)'; + const title = document.createElement("div"); + title.className = "osl-search-title"; + title.innerHTML = doc.title || "(untitled)"; - const snip = document.createElement('p'); - snip.className = 'osl-search-snippet'; - snip.innerHTML = buildSnippet(doc.text || '', rawQuery); + const snip = document.createElement("p"); + snip.className = "osl-search-snippet"; + snip.innerHTML = buildSnippet(doc.text || "", rawQuery); a.appendChild(title); a.appendChild(snip); @@ -245,29 +317,30 @@ } function activateItem(panel, nextIndex) { - const items = Array.from(panel.querySelectorAll('.osl-search-item')); + const items = Array.from(panel.querySelectorAll(".osl-search-item")); if (!items.length) return -1; - items.forEach(el => el.classList.remove('is-active')); + items.forEach((el) => el.classList.remove("is-active")); const idx = Math.max(0, Math.min(nextIndex, items.length - 1)); - items[idx].classList.add('is-active'); - items[idx].scrollIntoView({ block: 'nearest' }); + items[idx].classList.add("is-active"); + items[idx].scrollIntoView({ block: "nearest" }); return idx; } function currentActiveIndex(panel) { - const active = panel.querySelector('.osl-search-item.is-active'); + const active = panel.querySelector(".osl-search-item.is-active"); return active ? parseInt(active.dataset.index, 10) : -1; } function navigateActive(panel) { - const active = panel.querySelector('.osl-search-item.is-active') || - panel.querySelector('.osl-search-item'); + const active = + panel.querySelector(".osl-search-item.is-active") || + panel.querySelector(".osl-search-item"); if (active) window.location.assign(active.href); } function closePanel(panel) { - panel.style.display = 'none'; - panel.innerHTML = ''; + panel.style.display = "none"; + panel.innerHTML = ""; } // --- Per-input wiring ---------------------------------------------------- @@ -276,26 +349,37 @@ if (!inputEl || inputEl.__oslWired__) return; inputEl.__oslWired__ = true; + // Add ARIA attributes + inputEl.setAttribute("role", "searchbox"); + inputEl.setAttribute("aria-label", "Search"); + inputEl.setAttribute("aria-expanded", "false"); + injectBaseStylesOnce(); const panel = mkPanel(); - let lastQuery = ''; + let lastQuery = ""; let lastActive = -1; function openPanel() { positionPanel(panel, inputEl); - panel.style.display = 'block'; + panel.style.display = "block"; + inputEl.setAttribute("aria-expanded", "true"); } function updatePosition() { - if (panel.style.display !== 'none') positionPanel(panel, inputEl); + if (panel.style.display !== "none") positionPanel(panel, inputEl); } + function closePanel(panel) { + panel.style.display = "none"; + panel.innerHTML = ""; + inputEl.setAttribute("aria-expanded", "false"); + } // Debounce to keep it snappy let t = null; function onInput() { clearTimeout(t); t = setTimeout(async () => { - const q = inputEl.value || ''; + const q = inputEl.value || ""; lastQuery = q; if (q.trim().length < MIN_QUERY_LEN) { renderResults(panel, [], q); @@ -310,7 +394,7 @@ openPanel(); lastActive = activateItem(panel, 0); } catch (e) { - console.warn('[OSL Search] failed:', e); + console.warn("[OSL Search] failed:", e); renderResults(panel, [], q); openPanel(); } @@ -318,22 +402,25 @@ } function onKey(e) { - if (panel.style.display === 'none' && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { + if ( + panel.style.display === "none" && + (e.key === "ArrowDown" || e.key === "ArrowUp") + ) { openPanel(); } switch (e.key) { - case 'ArrowDown': + case "ArrowDown": e.preventDefault(); lastActive = activateItem(panel, currentActiveIndex(panel) + 1); break; - case 'ArrowUp': + case "ArrowUp": e.preventDefault(); lastActive = activateItem(panel, currentActiveIndex(panel) - 1); break; - case 'Enter': + case "Enter": // If the panel is open and we have results, navigate the active item - if (panel.style.display !== 'none') { - const anyItem = panel.querySelector('.osl-search-item'); + if (panel.style.display !== "none") { + const anyItem = panel.querySelector(".osl-search-item"); if (anyItem) { e.preventDefault(); navigateActive(panel); @@ -341,16 +428,16 @@ } } break; - case 'Escape': + case "Escape": closePanel(panel); break; } } function onFocus() { - if ((inputEl.value || '').trim().length >= 0) { + if ((inputEl.value || "").trim().length >= 0) { updatePosition(); - panel.style.display = 'block'; + panel.style.display = "block"; } } @@ -362,8 +449,8 @@ } // Clicks inside panel: follow links, also set active on hover - panel.addEventListener('mousemove', (e) => { - const a = e.target.closest('.osl-search-item'); + panel.addEventListener("mousemove", (e) => { + const a = e.target.closest(".osl-search-item"); if (a && panel.contains(a)) { const idx = parseInt(a.dataset.index, 10); if (!Number.isNaN(idx)) { @@ -371,8 +458,8 @@ } } }); - panel.addEventListener('click', (e) => { - const a = e.target.closest('a.osl-search-item'); + panel.addEventListener("click", (e) => { + const a = e.target.closest("a.osl-search-item"); if (!a) return; e.preventDefault(); window.location.assign(a.href); @@ -380,21 +467,36 @@ }); // Outside click closes panel - document.addEventListener('click', (e) => { + document.addEventListener("click", (e) => { if (e.target === inputEl) return; if (panel.contains(e.target)) return; if (!panel.contains(e.target)) closePanel(panel); }); // Reposition on scroll/resize - window.addEventListener('scroll', updatePosition, { passive: true }); - window.addEventListener('resize', updatePosition); - - inputEl.setAttribute('autocomplete', 'off'); - inputEl.addEventListener('input', onInput); - inputEl.addEventListener('keydown', onKey); - inputEl.addEventListener('focus', onFocus); - inputEl.addEventListener('blur', onBlur); + window.addEventListener("scroll", updatePosition, { passive: true }); + window.addEventListener("resize", updatePosition); + + inputEl.setAttribute("autocomplete", "off"); + inputEl.addEventListener("input", onInput); + inputEl.addEventListener("keydown", onKey); + inputEl.addEventListener("focus", onFocus); + inputEl.addEventListener("blur", onBlur); + } + + function setupKeyboardShortcuts() { + document.addEventListener("keydown", (e) => { + // Check for Ctrl+K or Cmd+K + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { + e.preventDefault(); + const searchInput = + document.getElementById("mkdocs-search") || + document.getElementById("mkdocs-search-mobile"); + if (searchInput) { + searchInput.focus(); + } + } + }); } // Public initializer (used by theme.js) @@ -404,11 +506,36 @@ }; // Optional: auto-wire known fields if present - document.addEventListener('DOMContentLoaded', () => { - const desktop = document.getElementById('mkdocs-search'); - const mobile = document.getElementById('mkdocs-search-mobile'); - if (desktop) wireInput(desktop); - if (mobile) wireInput(mobile); - }); + document.addEventListener("DOMContentLoaded", () => { + const desktop = document.getElementById("mkdocs-search"); + const mobile = document.getElementById("mkdocs-search-mobile"); + + if (desktop) { + // Wrap search input in container + const container = document.createElement("div"); + container.className = "search-container"; + desktop.parentNode.insertBefore(container, desktop); + container.appendChild(desktop); + + wireInput(desktop); + + // Add keyboard shortcut hint inside container + const hint = document.createElement("span"); + hint.className = "search-hint"; + hint.textContent = navigator.platform.includes("Mac") ? "⌘K" : "Ctrl+K"; + container.appendChild(hint); + } + + if (mobile) { + // Wrap mobile search input in container + const container = document.createElement("div"); + container.className = "search-container"; + mobile.parentNode.insertBefore(container, mobile); + container.appendChild(mobile); + wireInput(mobile); + } + + setupKeyboardShortcuts(); + }); })(); From a266b5b4d82eba6bc82582904685b57de3b528b0 Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Wed, 3 Sep 2025 09:08:31 +0530 Subject: [PATCH 2/4] chore: apply pre-commit linting changes --- theme/js/osl-search.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/theme/js/osl-search.js b/theme/js/osl-search.js index f1eeff92..ed5f0a6a 100644 --- a/theme/js/osl-search.js +++ b/theme/js/osl-search.js @@ -81,12 +81,12 @@ border: 1px solid var(--border-color, rgba(255,255,255,.12)); border-radius: 6px; } - + #mkdocs-search:hover, #mkdocs-search-mobile:hover { border-color: var(--md-primary-fg-color, #1976d2); } - + #mkdocs-search:focus, #mkdocs-search-mobile:focus { outline: 2px solid var(--md-primary-fg-color, #1976d2); @@ -99,7 +99,7 @@ position: relative; display: inline-block; } - + .search-container:focus-within .search-hint { opacity: 1; } From 9f794d1769193562060a2d8d078e8d477f34fe3f Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Wed, 3 Sep 2025 09:58:54 +0530 Subject: [PATCH 3/4] chore: linting works and fixed formatting --- theme/js/osl-search.js | 216 ++++++++++++++++++++--------------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/theme/js/osl-search.js b/theme/js/osl-search.js index ed5f0a6a..295cf048 100644 --- a/theme/js/osl-search.js +++ b/theme/js/osl-search.js @@ -4,17 +4,17 @@ */ (function () { - "use strict"; + 'use strict'; - const INDEX_URL = "/search/search_index.json"; + const INDEX_URL = '/search/search_index.json'; const MAX_RESULTS = 8; const MIN_QUERY_LEN = 2; // Internal state (built once) - let _docs = null; // array of { title, text, location } - let _byRef = null; // Map(location -> doc) - let _idx = null; // lunr.Index - let _building = null; // build promise (avoid duplicate builds) + let _docs = null; // array of { title, text, location } + let _byRef = null; // Map(location -> doc) + let _idx = null; // lunr.Index + let _building = null; // build promise (avoid duplicate builds) // --- Utilities ----------------------------------------------------------- @@ -23,11 +23,11 @@ // keep full URLs as-is if (/^([a-z]+:)?\/\//i.test(href)) return href; // force site-rooted path - return "/" + href.replace(/^\/+/, ""); + return '/' + href.replace(/^\/+/, ''); } function injectBaseStylesOnce() { - if (document.getElementById("osl-search-styles")) return; + if (document.getElementById('osl-search-styles')) return; const css = ` .osl-search-panel { position: absolute; @@ -123,8 +123,8 @@ visibility: hidden; } `; - const style = document.createElement("style"); - style.id = "osl-search-styles"; + const style = document.createElement('style'); + style.id = 'osl-search-styles'; style.textContent = css; document.head.appendChild(style); } @@ -132,17 +132,17 @@ async function ensureLunrReady() { if (window.lunr) return; await new Promise((resolve) => { - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", resolve, { once: true }); + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', resolve, { once: true }); } else { setTimeout(resolve, 0); } }); - if (!window.lunr) throw new Error("Lunr failed to load"); + if (!window.lunr) throw new Error('Lunr failed to load'); } async function fetchIndexJSON() { - const res = await fetch(INDEX_URL, { credentials: "same-origin" }); + const res = await fetch(INDEX_URL, { credentials: 'same-origin' }); if (!res.ok) throw new Error(`Failed to fetch ${INDEX_URL}: ${res.status}`); const json = await res.json(); // MkDocs "search" plugin typically returns { docs: [...] } @@ -151,22 +151,22 @@ function normalizeDocs(arr) { return arr.map((d) => ({ - title: d.title || "", - text: d.text || "", - location: d.location || "", + title: d.title || '', + text: d.text || '', + location: d.location || '', })); } function buildSnippet(text, rawQuery) { - if (!text) return ""; - const q = (rawQuery || "").trim(); + if (!text) return ''; + const q = (rawQuery || '').trim(); const MAX = 160; - if (!q) return text.slice(0, MAX) + (text.length > MAX ? "…" : ""); + if (!q) return text.slice(0, MAX) + (text.length > MAX ? '…' : ''); // find first occurrence of any term (basic, case-insensitive) const terms = q.split(/\s+/).filter(Boolean); let hit = -1, - termUsed = ""; + termUsed = ''; for (const t of terms) { const idx = text.toLowerCase().indexOf(t.toLowerCase()); if (idx !== -1 && (hit === -1 || idx < hit)) { @@ -175,26 +175,26 @@ } } if (hit === -1) { - return text.slice(0, MAX) + (text.length > MAX ? "…" : ""); + return text.slice(0, MAX) + (text.length > MAX ? '…' : ''); } const start = Math.max(0, hit - 40); const end = Math.min(text.length, hit + 120); let snip = - (start > 0 ? "…" : "") + + (start > 0 ? '…' : '') + text.slice(start, end) + - (end < text.length ? "…" : ""); + (end < text.length ? '…' : ''); // simple highlight for all terms terms.forEach((t) => { if (!t) return; - const re = new RegExp(`(${escapeRegExp(t)})`, "ig"); + const re = new RegExp(`(${escapeRegExp(t)})`, 'ig'); snip = snip.replace(re, '$1'); }); return snip; } function escapeRegExp(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } async function ensureIndex() { @@ -209,15 +209,15 @@ _byRef = new Map(_docs.map((d) => [d.location, d])); // Build index - const hasMulti = typeof lunr.multiLanguage === "function"; + const hasMulti = typeof lunr.multiLanguage === 'function'; _idx = lunr(function () { if (hasMulti) { // Adjust languages if you only need some of them - this.use(lunr.multiLanguage("en", "es", "pt")); + this.use(lunr.multiLanguage('en', 'es', 'pt')); } - this.ref("location"); - this.field("title", { boost: 10 }); - this.field("text"); + this.ref('location'); + this.field('title', { boost: 10 }); + this.field('text'); _docs.forEach((doc) => this.add(doc)); }); @@ -241,8 +241,8 @@ if (!/[~^*]/.test(q)) q = q .split(/\s+/) - .map((t) => t + "*") - .join(" "); + .map((t) => t + '*') + .join(' '); let hits = []; try { hits = _idx.search(q); @@ -266,10 +266,10 @@ // --- UI (panel) --------------------------------------------------------- function mkPanel() { - const div = document.createElement("div"); - div.className = "osl-search-panel"; - div.setAttribute("role", "listbox"); - div.style.display = "none"; + const div = document.createElement('div'); + div.className = 'osl-search-panel'; + div.setAttribute('role', 'listbox'); + div.style.display = 'none'; document.body.appendChild(div); return div; } @@ -285,29 +285,29 @@ } function renderResults(panel, items, rawQuery) { - panel.innerHTML = ""; + panel.innerHTML = ''; if (!items.length) { - const empty = document.createElement("div"); - empty.className = "osl-search-empty"; + const empty = document.createElement('div'); + empty.className = 'osl-search-empty'; empty.textContent = rawQuery && rawQuery.length >= MIN_QUERY_LEN - ? "No results" - : "Type to search…"; + ? 'No results' + : 'Type to search…'; panel.appendChild(empty); return; } items.forEach(({ doc }, idx) => { - const a = document.createElement("a"); - a.className = "osl-search-item"; + const a = document.createElement('a'); + a.className = 'osl-search-item'; a.href = toAbsolute(doc.location); - const title = document.createElement("div"); - title.className = "osl-search-title"; - title.innerHTML = doc.title || "(untitled)"; + const title = document.createElement('div'); + title.className = 'osl-search-title'; + title.innerHTML = doc.title || '(untitled)'; - const snip = document.createElement("p"); - snip.className = "osl-search-snippet"; - snip.innerHTML = buildSnippet(doc.text || "", rawQuery); + const snip = document.createElement('p'); + snip.className = 'osl-search-snippet'; + snip.innerHTML = buildSnippet(doc.text || '', rawQuery); a.appendChild(title); a.appendChild(snip); @@ -317,30 +317,30 @@ } function activateItem(panel, nextIndex) { - const items = Array.from(panel.querySelectorAll(".osl-search-item")); + const items = Array.from(panel.querySelectorAll('.osl-search-item')); if (!items.length) return -1; - items.forEach((el) => el.classList.remove("is-active")); + items.forEach((el) => el.classList.remove('is-active')); const idx = Math.max(0, Math.min(nextIndex, items.length - 1)); - items[idx].classList.add("is-active"); - items[idx].scrollIntoView({ block: "nearest" }); + items[idx].classList.add('is-active'); + items[idx].scrollIntoView({ block: 'nearest' }); return idx; } function currentActiveIndex(panel) { - const active = panel.querySelector(".osl-search-item.is-active"); + const active = panel.querySelector('.osl-search-item.is-active'); return active ? parseInt(active.dataset.index, 10) : -1; } function navigateActive(panel) { const active = - panel.querySelector(".osl-search-item.is-active") || - panel.querySelector(".osl-search-item"); + panel.querySelector('.osl-search-item.is-active') || + panel.querySelector('.osl-search-item'); if (active) window.location.assign(active.href); } function closePanel(panel) { - panel.style.display = "none"; - panel.innerHTML = ""; + panel.style.display = 'none'; + panel.innerHTML = ''; } // --- Per-input wiring ---------------------------------------------------- @@ -350,36 +350,36 @@ inputEl.__oslWired__ = true; // Add ARIA attributes - inputEl.setAttribute("role", "searchbox"); - inputEl.setAttribute("aria-label", "Search"); - inputEl.setAttribute("aria-expanded", "false"); + inputEl.setAttribute('role', 'searchbox'); + inputEl.setAttribute('aria-label', 'Search'); + inputEl.setAttribute('aria-expanded', 'false'); injectBaseStylesOnce(); const panel = mkPanel(); - let lastQuery = ""; + let lastQuery = ''; let lastActive = -1; function openPanel() { positionPanel(panel, inputEl); - panel.style.display = "block"; - inputEl.setAttribute("aria-expanded", "true"); + panel.style.display = 'block'; + inputEl.setAttribute('aria-expanded', 'true'); } function updatePosition() { - if (panel.style.display !== "none") positionPanel(panel, inputEl); + if (panel.style.display !== 'none') positionPanel(panel, inputEl); } function closePanel(panel) { - panel.style.display = "none"; - panel.innerHTML = ""; - inputEl.setAttribute("aria-expanded", "false"); + panel.style.display = 'none'; + panel.innerHTML = ''; + inputEl.setAttribute('aria-expanded', 'false'); } // Debounce to keep it snappy let t = null; function onInput() { clearTimeout(t); t = setTimeout(async () => { - const q = inputEl.value || ""; + const q = inputEl.value || ''; lastQuery = q; if (q.trim().length < MIN_QUERY_LEN) { renderResults(panel, [], q); @@ -394,7 +394,7 @@ openPanel(); lastActive = activateItem(panel, 0); } catch (e) { - console.warn("[OSL Search] failed:", e); + console.warn('[OSL Search] failed:', e); renderResults(panel, [], q); openPanel(); } @@ -403,24 +403,24 @@ function onKey(e) { if ( - panel.style.display === "none" && - (e.key === "ArrowDown" || e.key === "ArrowUp") + panel.style.display === 'none' && + (e.key === 'ArrowDown' || e.key === 'ArrowUp') ) { openPanel(); } switch (e.key) { - case "ArrowDown": + case 'ArrowDown': e.preventDefault(); lastActive = activateItem(panel, currentActiveIndex(panel) + 1); break; - case "ArrowUp": + case 'ArrowUp': e.preventDefault(); lastActive = activateItem(panel, currentActiveIndex(panel) - 1); break; - case "Enter": + case 'Enter': // If the panel is open and we have results, navigate the active item - if (panel.style.display !== "none") { - const anyItem = panel.querySelector(".osl-search-item"); + if (panel.style.display !== 'none') { + const anyItem = panel.querySelector('.osl-search-item'); if (anyItem) { e.preventDefault(); navigateActive(panel); @@ -428,16 +428,16 @@ } } break; - case "Escape": + case 'Escape': closePanel(panel); break; } } function onFocus() { - if ((inputEl.value || "").trim().length >= 0) { + if ((inputEl.value || '').trim().length >= 0) { updatePosition(); - panel.style.display = "block"; + panel.style.display = 'block'; } } @@ -449,8 +449,8 @@ } // Clicks inside panel: follow links, also set active on hover - panel.addEventListener("mousemove", (e) => { - const a = e.target.closest(".osl-search-item"); + panel.addEventListener('mousemove', (e) => { + const a = e.target.closest('.osl-search-item'); if (a && panel.contains(a)) { const idx = parseInt(a.dataset.index, 10); if (!Number.isNaN(idx)) { @@ -458,8 +458,8 @@ } } }); - panel.addEventListener("click", (e) => { - const a = e.target.closest("a.osl-search-item"); + panel.addEventListener('click', (e) => { + const a = e.target.closest('a.osl-search-item'); if (!a) return; e.preventDefault(); window.location.assign(a.href); @@ -467,31 +467,31 @@ }); // Outside click closes panel - document.addEventListener("click", (e) => { + document.addEventListener('click', (e) => { if (e.target === inputEl) return; if (panel.contains(e.target)) return; if (!panel.contains(e.target)) closePanel(panel); }); // Reposition on scroll/resize - window.addEventListener("scroll", updatePosition, { passive: true }); - window.addEventListener("resize", updatePosition); - - inputEl.setAttribute("autocomplete", "off"); - inputEl.addEventListener("input", onInput); - inputEl.addEventListener("keydown", onKey); - inputEl.addEventListener("focus", onFocus); - inputEl.addEventListener("blur", onBlur); + window.addEventListener('scroll', updatePosition, { passive: true }); + window.addEventListener('resize', updatePosition); + + inputEl.setAttribute('autocomplete', 'off'); + inputEl.addEventListener('input', onInput); + inputEl.addEventListener('keydown', onKey); + inputEl.addEventListener('focus', onFocus); + inputEl.addEventListener('blur', onBlur); } function setupKeyboardShortcuts() { - document.addEventListener("keydown", (e) => { + document.addEventListener('keydown', (e) => { // Check for Ctrl+K or Cmd+K - if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); const searchInput = - document.getElementById("mkdocs-search") || - document.getElementById("mkdocs-search-mobile"); + document.getElementById('mkdocs-search') || + document.getElementById('mkdocs-search-mobile'); if (searchInput) { searchInput.focus(); } @@ -506,30 +506,30 @@ }; // Optional: auto-wire known fields if present - document.addEventListener("DOMContentLoaded", () => { - const desktop = document.getElementById("mkdocs-search"); - const mobile = document.getElementById("mkdocs-search-mobile"); + document.addEventListener('DOMContentLoaded', () => { + const desktop = document.getElementById('mkdocs-search'); + const mobile = document.getElementById('mkdocs-search-mobile'); if (desktop) { // Wrap search input in container - const container = document.createElement("div"); - container.className = "search-container"; + const container = document.createElement('div'); + container.className = 'search-container'; desktop.parentNode.insertBefore(container, desktop); container.appendChild(desktop); wireInput(desktop); // Add keyboard shortcut hint inside container - const hint = document.createElement("span"); - hint.className = "search-hint"; - hint.textContent = navigator.platform.includes("Mac") ? "⌘K" : "Ctrl+K"; + const hint = document.createElement('span'); + hint.className = 'search-hint'; + hint.textContent = navigator.platform.includes('Mac') ? '⌘K' : 'Ctrl+K'; container.appendChild(hint); } if (mobile) { // Wrap mobile search input in container - const container = document.createElement("div"); - container.className = "search-container"; + const container = document.createElement('div'); + container.className = 'search-container'; mobile.parentNode.insertBefore(container, mobile); container.appendChild(mobile); From 980ca921a2a8b164cb478cf4d5604e5ef99acce7 Mon Sep 17 00:00:00 2001 From: Aaqilyousuf Date: Wed, 3 Sep 2025 10:30:03 +0530 Subject: [PATCH 4/4] chore: precommit error fixed --- theme/js/osl-search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/js/osl-search.js b/theme/js/osl-search.js index 295cf048..c694135d 100644 --- a/theme/js/osl-search.js +++ b/theme/js/osl-search.js @@ -72,7 +72,7 @@ } .osl-search-mark { background: rgba(255, 208, 0, .35); border-radius: .2rem; } - /* Enhanced focus and outline styles */ + /* Added outline and keyboard focus enhancements */ #mkdocs-search, #mkdocs-search-mobile { outline: 1px solid #e0e0e0;