diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index 797685adf..82fdf826b 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -24,14 +24,20 @@ const contextWindowWords = 30 const numSearchResults = 8 const numTagResults = 5 -const tokenizeTerm = (term: string) => - term - .split(/\s+/) - .filter((t) => t !== "") - .sort((a, b) => b.length - a.length) +const tokenizeTerm = (term: string) => { + const tokens = term.split(/\s+/).filter((t) => t.trim() !== "") + + const tokenLen = tokens.length + if (tokenLen > 1) { + for (let i = 1; i < tokenLen; i++) { + tokens.push(tokens.slice(0, i + 1).join(" ")) + } + } + + return tokens.sort((a, b) => b.length - a.length) // always highlight longest terms first +} function highlight(searchTerm: string, text: string, trim?: boolean) { - // try to highlight longest tokens first const tokenizedTerms = tokenizeTerm(searchTerm) let tokenizedText = text.split(/\s+/).filter((t) => t !== "") @@ -69,7 +75,6 @@ function highlight(searchTerm: string, text: string, trim?: boolean) { } return tok }) - .slice(startIndex, endIndex + 1) .join(" ") return `${startIndex === 0 ? "" : "..."}${slice}${ @@ -89,29 +94,32 @@ function highlightHTML(searchTerm: string, el: HTMLElement) { return span } - const highlightTextNodes = (node: Node) => { + const highlightTextNodes = (node: Node, term: string) => { if (node.nodeType === Node.TEXT_NODE) { - let nodeText = node.nodeValue || "" - tokenizedTerms.forEach((term) => { - const regex = new RegExp(term.toLowerCase(), "gi") - const matches = nodeText.match(regex) - const spanContainer = document.createElement("span") - let lastIndex = 0 - matches?.forEach((match) => { - const matchIndex = nodeText.indexOf(match, lastIndex) - spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex, matchIndex))) - spanContainer.appendChild(createHighlightSpan(match)) - lastIndex = matchIndex + match.length - }) - spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex))) - node.parentNode?.replaceChild(spanContainer, node) - }) + const nodeText = node.nodeValue ?? "" + const regex = new RegExp(term.toLowerCase(), "gi") + const matches = nodeText.match(regex) + if (!matches || matches.length === 0) return + const spanContainer = document.createElement("span") + let lastIndex = 0 + for (const match of matches) { + const matchIndex = nodeText.indexOf(match, lastIndex) + spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex, matchIndex))) + spanContainer.appendChild(createHighlightSpan(match)) + lastIndex = matchIndex + match.length + } + spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex))) + node.parentNode?.replaceChild(spanContainer, node) } else if (node.nodeType === Node.ELEMENT_NODE) { - Array.from(node.childNodes).forEach(highlightTextNodes) + if ((node as HTMLElement).classList.contains("highlight")) return + Array.from(node.childNodes).forEach((child) => highlightTextNodes(child, term)) } } - highlightTextNodes(html.body) + for (const term of tokenizedTerms) { + highlightTextNodes(html.body, term) + } + return html.body } @@ -137,13 +145,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { let previewInner: HTMLDivElement | undefined = undefined const results = document.createElement("div") results.id = "results-container" - results.style.flexBasis = enablePreview ? "30%" : "100%" + results.style.flexBasis = enablePreview ? "min(30%, 450px)" : "100%" appendLayout(results) if (enablePreview) { preview = document.createElement("div") preview.id = "preview-container" - preview.style.flexBasis = "70%" + preview.style.flexBasis = "100%" appendLayout(preview) } diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss index 11e7c4e14..0a763ec55 100644 --- a/quartz/components/styles/search.scss +++ b/quartz/components/styles/search.scss @@ -54,7 +54,7 @@ } & > #search-space { - width: 75%; + width: 65%; margin-top: 12vh; margin-left: auto; margin-right: auto; @@ -85,7 +85,6 @@ & > #search-layout { display: flex; flex-direction: row; - justify-content: space-between; opacity: 0; border: 1px solid var(--lightgray); @@ -106,7 +105,7 @@ } } - @media all and (max-width: $mobileBreakpoint) { + @media all and (max-width: $tabletBreakpoint) { display: block; & > *:not(#results-container) { display: none !important; @@ -119,8 +118,8 @@ } & .highlight { - color: var(--secondary); - font-weight: 700; + background: color-mix(in srgb, var(--tertiary) 60%, transparent); + border-radius: 5px; } & > #preview-container { @@ -129,8 +128,10 @@ overflow: hidden; & .preview-inner { + margin: 0 auto; padding: 1em; height: 100%; + width: 100%; box-sizing: border-box; overflow-y: auto; font-family: inherit; diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index fc1db9581..0fa7a5567 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -26,7 +26,7 @@ section { } ::selection { - background: color-mix(in srgb, var(--tertiary) 75%, transparent); + background: color-mix(in srgb, var(--tertiary) 60%, transparent); color: var(--darkgray); } diff --git a/quartz/styles/variables.scss b/quartz/styles/variables.scss index 30004aa7b..db43f8ed9 100644 --- a/quartz/styles/variables.scss +++ b/quartz/styles/variables.scss @@ -1,6 +1,6 @@ $pageWidth: 750px; $mobileBreakpoint: 600px; -$tabletBreakpoint: 1200px; +$tabletBreakpoint: 1000px; $sidePanelWidth: 380px; $topSpacing: 6rem; $fullPageWidth: $pageWidth + 2 * $sidePanelWidth;