diff --git a/assets/scripts/search.js b/assets/scripts/search.js index 64e5b2f..5670bde 100755 --- a/assets/scripts/search.js +++ b/assets/scripts/search.js @@ -2,45 +2,37 @@ let lunrIndex; let documents = []; let activeIndex = -1; let currentResults = []; -let isSyncingHeaderInput = false; -let suppressHeaderFocus = false; let searchReady = false; const searchInput = document.getElementById("search-input"); const searchBtn = document.getElementById("search-btn"); const searchForm = searchInput?.closest("form"); +const searchContainer = searchInput?.closest(".site-actions"); -const modal = document.createElement("div"); -modal.id = "search-modal"; -modal.setAttribute("aria-hidden", "true"); -modal.innerHTML = ` -
- +const searchPanel = document.createElement("section"); +searchPanel.id = "search-inline-panel"; +searchPanel.className = "search-inline-panel"; +searchPanel.setAttribute("aria-hidden", "true"); +searchPanel.setAttribute("aria-labelledby", "search-pane-title"); +searchPanel.innerHTML = ` + + + `; -document.body.appendChild(modal); -const pane = modal.querySelector(".search-modal-content"); -const paneInput = modal.querySelector("#search-pane-input"); -const modalResults = modal.querySelector("#search-modal-results"); -const paneCount = modal.querySelector("#search-pane-count"); +if (searchContainer) { + searchContainer.appendChild(searchPanel); +} + +const inlineResults = searchPanel.querySelector("#search-inline-results"); +const paneCount = searchPanel.querySelector("#search-pane-count"); function extractDocuments(node, currentPath = "") { if (node.type === "folder" && node.children) { @@ -80,7 +72,7 @@ async function initSearch() { extractDocuments(json); buildIndex(); searchReady = true; - if (isSearchOpen()) renderResults(paneInput.value); + if (isSearchOpen()) renderResults(searchInput.value); } catch (error) { console.warn("Search index could not be loaded", error); if (paneCount) paneCount.textContent = "Search is unavailable right now."; @@ -115,14 +107,14 @@ function buildSnippet(content, terms, radius = 90) { } if (index === -1) { - return content.slice(0, radius * 2) + "…"; + return content.slice(0, radius * 2) + "..."; } const start = Math.max(0, index - radius); const end = Math.min(content.length, index + matchedTerm.length + radius); - const prefix = start > 0 ? "…" : ""; - const suffix = end < content.length ? "…" : ""; + const prefix = start > 0 ? "..." : ""; + const suffix = end < content.length ? "..." : ""; return prefix + content.slice(start, end) + suffix; } @@ -179,66 +171,41 @@ function search(query) { .filter(Boolean); } -function setHeaderSearchValue(value, dispatchInput = false) { - if (!searchInput) return; +function isSearchOpen() { + return searchContainer?.classList.contains("search-inline-open"); +} - isSyncingHeaderInput = true; - searchInput.value = value; - isSyncingHeaderInput = false; +function openSearchPane(initialValue = null, focusInput = true) { + if (!searchContainer || !searchInput) return; - if (dispatchInput) { - searchInput.dispatchEvent(new Event("input", { bubbles: true })); + if (initialValue !== null) { + searchInput.value = initialValue; + } + + searchContainer.classList.add("search-inline-open"); + searchPanel.setAttribute("aria-hidden", "false"); + searchBtn?.setAttribute("aria-expanded", "true"); + renderResults(searchInput.value); + + if (focusInput) { + searchInput.focus({ preventScroll: true }); + try { + searchInput.setSelectionRange(searchInput.value.length, searchInput.value.length); + } catch (_) {} } } -function isSearchOpen() { - return modal.classList.contains("is-open"); -} - -function focusPaneInput() { - if (!isSearchOpen()) return; - paneInput.focus({ preventScroll: true }); - try { - paneInput.setSelectionRange(paneInput.value.length, paneInput.value.length); - } catch (_) {} -} - -function schedulePaneInputFocus() { - focusPaneInput(); - window.requestAnimationFrame(() => { - focusPaneInput(); - window.requestAnimationFrame(focusPaneInput); - }); -} - -function openSearchPane(initialValue = "") { - const value = initialValue || searchInput?.value || paneInput?.value || ""; - - modal.classList.add("is-open"); - modal.setAttribute("aria-hidden", "false"); - document.documentElement.classList.add("search-open"); - - paneInput.value = value; - setHeaderSearchValue(value); - renderResults(value); - - schedulePaneInputFocus(); -} - function closeSearchPane() { - modal.classList.remove("is-open"); - modal.setAttribute("aria-hidden", "true"); - document.documentElement.classList.remove("search-open"); + if (!searchContainer) return; + + searchContainer.classList.remove("search-inline-open"); + searchPanel.setAttribute("aria-hidden", "true"); + searchBtn?.setAttribute("aria-expanded", "false"); activeIndex = -1; - suppressHeaderFocus = true; - searchInput?.focus(); - window.setTimeout(() => { - suppressHeaderFocus = false; - }, 0); } function getResultItems() { - return Array.from(modalResults.querySelectorAll(".search-modal-result")); + return Array.from(inlineResults.querySelectorAll(".search-result")); } function updateActiveResult(focusActive = false) { @@ -255,7 +222,7 @@ function updateActiveResult(focusActive = false) { block: "nearest", behavior: "smooth", }); - if (focusActive) focusPaneInput(); + if (focusActive) searchInput?.focus({ preventScroll: true }); } } @@ -282,11 +249,11 @@ function renderResults(query) { const trimmed = query.trim(); currentResults = trimmed.length >= 2 ? search(trimmed) : []; activeIndex = currentResults.length ? 0 : -1; - modalResults.innerHTML = ""; + inlineResults.innerHTML = ""; if (!trimmed) { paneCount.textContent = "Start typing to search your notes."; - modalResults.innerHTML = ` + inlineResults.innerHTML = `