diff --git a/src/client/search-init.ts b/src/client/search-init.ts index 10574eb8a..263a25cab 100644 --- a/src/client/search-init.ts +++ b/src/client/search-init.ts @@ -8,6 +8,11 @@ const input = container.querySelector("input")!; const load = () => import("observablehq:search"); input.addEventListener("focus", load, {once: true}); input.addEventListener("keydown", load, {once: true}); +input.addEventListener("focus", () => { + if (input.value) return; // only restore the query if empty + input.value = sessionStorage.getItem("search-query") ?? ""; + input.select(); +}); // Focus on meta-K and / const toggle = document.querySelector("#observablehq-sidebar-toggle")!; @@ -22,6 +27,7 @@ addEventListener("keydown", (event) => { // open until the user blurs the input. if (toggle.checked) input.focus(); else toggle.click(), input.focus(), toggle.click(); + input.value = sessionStorage.getItem("search-query") ?? ""; input.select(); event.preventDefault(); } diff --git a/src/client/search.js b/src/client/search.js index 65ca71a42..6d2e88b8a 100644 --- a/src/client/search.js +++ b/src/client/search.js @@ -31,6 +31,7 @@ const index = await fetch(import.meta.resolve("observablehq:minisearch.json")) input.addEventListener("input", () => { if (currentValue === input.value) return; currentValue = input.value; + sessionStorage.setItem("search-query", currentValue); if (!currentValue.length) { container.setAttribute("data-shortcut", shortcut); sidebar.classList.remove("observablehq-search-results"); @@ -43,22 +44,40 @@ input.addEventListener("input", () => { resultsContainer.innerHTML = results.length === 0 ? "
no results
" - : `
${results.length.toLocaleString("en-US")} result${results.length === 1 ? "" : "s"}
    ${results - .map(renderResult) - .join("")}
`; + : `
${results.length.toLocaleString("en-US")} result${ + results.length === 1 ? "" : "s" + }
    ${renderResults(results)}
`; + resultsContainer.querySelector(`.${activeClass}`)?.scrollIntoView({block: "nearest"}); }); +function renderResults(results) { + const me = document.location.href.replace(/[?#].*/, ""); + let found; + results = results.map(({id, score, title}) => { + const external = /^\w+:/.test(id); + const href = external ? id : import.meta.resolve(`..${id}`); + return { + title: String(title ?? "—"), + href, + external, + score: Math.min(5, Math.round(0.6 * score)), + active: me === href && (found = true) + }; + }); + if (!found) results[0].active = true; + return results.map(renderResult).join(""); +} + function isExternal(id) { return /^\w+:/.test(id); } -function renderResult({id, score, title}, i) { - const external = /^\w+:/.test(id); - return `
  • ${escapeText(String(title ?? "—"))}
  • `; +function renderResult({href, score, external, title, active}) { + return `
  • ${escapeText( + title + )}
  • `; } function escapeDoubleQuote(text) {