Compare commits

...

12 Commits

Author SHA1 Message Date
Jacky Zhao
34a8dfcd55 pkg: bump to 4.2.1 2024-02-02 01:45:28 -08:00
Jacky Zhao
44da82467e fix(style): remove redundant selector 2024-02-02 01:45:15 -08:00
Jacky Zhao
3231ce6e79 fix: search async ordering, scroll offset 2024-02-02 01:36:17 -08:00
Jacky Zhao
a0b927da4a fix: use display instead of visibility for click handling pasthrough 2024-02-02 01:24:40 -08:00
Jacky Zhao
5ab922f316 fix(revert): font aliasing 2024-02-02 01:15:10 -08:00
Jacky Zhao
d11a0e71a8 fix: font smoothing defaults 2024-02-02 01:01:04 -08:00
Jacky Zhao
2b57a68e1f fix: font weight consistency 2024-02-02 00:53:09 -08:00
Jacky Zhao
18cd58617d fix: parallelize search indexing 2024-02-02 00:53:09 -08:00
Aaron Pham
ee868b2d79 fix(search): set correct attribute on hover icon (#787)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-02 00:35:53 -08:00
Jacky Zhao
5a36e5b68d fix(style): reasonable page width for rich search preview 2024-02-02 00:29:45 -08:00
Jacky Zhao
0416c03ae6 fix: be more eager about constructing search index 2024-02-02 00:25:05 -08:00
Jacky Zhao
3b596c9311 fix: flatmap children when highlighting rich preview to avoid body 2024-02-02 00:19:19 -08:00
8 changed files with 77 additions and 74 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@jackyzha0/quartz",
"version": "4.2.0",
"version": "4.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@jackyzha0/quartz",
"version": "4.2.0",
"version": "4.2.1",
"license": "MIT",
"dependencies": {
"@clack/prompts": "^0.7.0",

View File

@@ -2,7 +2,7 @@
"name": "@jackyzha0/quartz",
"description": "🌱 publish your digital garden and notes as a website",
"private": true,
"version": "4.2.0",
"version": "4.2.1",
"type": "module",
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
"license": "MIT",

View File

@@ -15,10 +15,30 @@ interface Item {
type SearchType = "basic" | "tags"
let searchType: SearchType = "basic"
let currentSearchTerm: string = ""
let index: FlexSearch.Document<Item> | undefined = undefined
const p = new DOMParser()
const encoder = (str: string) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/)
let index = new FlexSearch.Document<Item>({
charset: "latin:extra",
encode: encoder,
document: {
id: "id",
index: [
{
field: "title",
tokenize: "forward",
},
{
field: "content",
tokenize: "forward",
},
{
field: "tags",
tokenize: "forward",
},
],
},
})
const p = new DOMParser()
const fetchContentCache: Map<FullSlug, Element[]> = new Map()
const contextWindowWords = 30
const numSearchResults = 8
@@ -81,10 +101,10 @@ function highlight(searchTerm: string, text: string, trim?: boolean) {
}`
}
function highlightHTML(searchTerm: string, innerHTML: string) {
function highlightHTML(searchTerm: string, el: HTMLElement) {
const p = new DOMParser()
const tokenizedTerms = tokenizeTerm(searchTerm)
const html = p.parseFromString(innerHTML, "text/html")
const html = p.parseFromString(el.innerHTML, "text/html")
const createHighlightSpan = (text: string) => {
const span = document.createElement("span")
@@ -168,7 +188,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
removeAllChildren(preview)
}
if (searchLayout) {
searchLayout.style.visibility = "hidden"
searchLayout.classList.remove("display-results")
}
searchType = "basic" // reset search type after closing
@@ -204,6 +224,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
if (currentHover) {
currentHover.classList.remove("focus")
currentHover.blur()
}
// If search is active, then we will render the first result and display accordingly
@@ -230,9 +251,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
: (document.activeElement as HTMLInputElement | null)
const prevResult = currentResult?.previousElementSibling as HTMLInputElement | null
currentResult?.classList.remove("focus")
await displayPreview(prevResult)
prevResult?.focus()
currentHover = prevResult
await displayPreview(prevResult)
}
} else if (e.key === "ArrowDown" || e.key === "Tab") {
e.preventDefault()
@@ -244,9 +265,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
: (document.getElementsByClassName("result-card")[0] as HTMLInputElement | null)
const secondResult = firstResult?.nextElementSibling as HTMLInputElement | null
firstResult?.classList.remove("focus")
await displayPreview(secondResult)
secondResult?.focus()
currentHover = secondResult
await displayPreview(secondResult)
} else {
// If an element in results-container already has focus, focus next one
const active = currentHover
@@ -254,9 +275,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
: (document.activeElement as HTMLInputElement | null)
active?.classList.remove("focus")
const nextResult = active?.nextElementSibling as HTMLInputElement | null
await displayPreview(nextResult)
nextResult?.focus()
currentHover = nextResult
await displayPreview(nextResult)
}
}
}
@@ -305,9 +326,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
currentHover?.classList.remove("focus")
currentHover?.blur()
const target = ev.target as HTMLInputElement
await displayPreview(target)
currentHover = target
currentHover.classList.add("focus")
await displayPreview(target)
}
async function onMouseLeave(ev: MouseEvent) {
@@ -385,12 +406,11 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
async function displayPreview(el: HTMLElement | null) {
if (!searchLayout || !enablePreview || !el || !preview) return
const slug = el.id as FullSlug
el.classList.add("focus")
const innerDiv = await fetchContent(slug).then((contents) =>
contents.flatMap((el) => [...highlightHTML(currentSearchTerm, el as HTMLElement).children]),
)
previewInner = document.createElement("div")
previewInner.classList.add("preview-inner")
const innerDiv = await fetchContent(slug).then((contents) =>
contents.map((el) => highlightHTML(currentSearchTerm, el.innerHTML)),
)
previewInner.append(...innerDiv)
preview.replaceChildren(previewInner)
@@ -398,13 +418,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const highlights = [...preview.querySelectorAll(".highlight")].sort(
(a, b) => b.innerHTML.length - a.innerHTML.length,
)
highlights[0]?.scrollIntoView()
highlights[0]?.scrollIntoView({ block: "start" })
}
async function onType(e: HTMLElementEventMap["input"]) {
if (!searchLayout || !index) return
currentSearchTerm = (e.target as HTMLInputElement).value
searchLayout.style.visibility = currentSearchTerm === "" ? "hidden" : "visible"
searchLayout.classList.toggle("display-results", currentSearchTerm !== "")
searchType = currentSearchTerm.startsWith("#") ? "tags" : "basic"
let searchResults: FlexSearch.SimpleDocumentSearchResultSetUnit[]
@@ -444,8 +464,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
searchBar?.addEventListener("input", onType)
window.addCleanup(() => searchBar?.removeEventListener("input", onType))
index ??= await fillDocument(data)
registerEscapeHandler(container, hideSearch)
await fillDocument(data)
})
/**
@@ -454,37 +474,19 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
* @param data data to fill index with
*/
async function fillDocument(data: { [key: FullSlug]: ContentDetails }) {
const index = new FlexSearch.Document<Item>({
charset: "latin:extra",
encode: encoder,
document: {
id: "id",
index: [
{
field: "title",
tokenize: "forward",
},
{
field: "content",
tokenize: "forward",
},
{
field: "tags",
tokenize: "forward",
},
],
},
})
let id = 0
const promises: Array<Promise<unknown>> = []
for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
await index.addAsync(id++, {
id,
slug: slug as FullSlug,
title: fileData.title,
content: fileData.content,
tags: fileData.tags,
})
promises.push(
index.addAsync(id++, {
id,
slug: slug as FullSlug,
title: fileData.title,
content: fileData.content,
tags: fileData.tags,
}),
)
}
return index
return await Promise.all(promises)
}

View File

@@ -1,3 +1,5 @@
@use "../../styles/variables.scss" as *;
button#explorer {
all: unset;
background-color: transparent;
@@ -85,7 +87,7 @@ svg {
color: var(--secondary);
font-family: var(--headerFont);
font-size: 0.95rem;
font-weight: 600;
font-weight: $boldWeight;
line-height: 1.5rem;
display: inline-block;
}
@@ -110,7 +112,7 @@ svg {
font-size: 0.95rem;
display: inline-block;
color: var(--secondary);
font-weight: 600;
font-weight: $boldWeight;
margin: 0;
line-height: 1.5rem;
pointer-events: none;

View File

@@ -83,11 +83,14 @@
}
& > #search-layout {
display: flex;
display: none;
flex-direction: row;
visibility: hidden;
border: 1px solid var(--lightgray);
&.display-results {
display: flex;
}
@media all and (min-width: $tabletBreakpoint) {
&[data-preview] {
& .result-card > p.preview {
@@ -128,34 +131,27 @@
& .highlight {
background: color-mix(in srgb, var(--tertiary) 60%, transparent);
border-radius: 5px;
scroll-margin-top: 2rem;
}
& > #preview-container {
display: block;
box-sizing: border-box;
overflow: hidden;
box-sizing: border-box;
font-family: inherit;
color: var(--dark);
line-height: 1.5em;
font-weight: $normalWeight;
background: var(--light);
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
overflow-y: auto;
padding: 1rem;
& .preview-inner {
margin: 0 auto;
padding: 1em;
height: 100%;
width: 100%;
box-sizing: border-box;
overflow-y: auto;
font-family: inherit;
color: var(--dark);
line-height: 1.5em;
font-weight: 400;
background: var(--light);
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
box-shadow:
0 14px 50px rgba(27, 33, 48, 0.12),
0 10px 30px rgba(27, 33, 48, 0.16);
}
a.internal {
background-color: none;
width: min($pageWidth, 100%);
}
}
@@ -202,7 +198,7 @@
padding: 0.2rem 0.4rem;
margin: 0 0.1rem;
line-height: 1.4rem;
font-weight: bold;
font-weight: $boldWeight;
color: var(--secondary);
&.match-tag {

View File

@@ -54,7 +54,7 @@ ul,
}
a {
font-weight: 600;
font-weight: $boldWeight;
text-decoration: none;
transition: color 0.2s ease;
color: var(--secondary);

View File

@@ -1,3 +1,4 @@
@use "./variables.scss" as *;
@use "sass:color";
.callout {
@@ -156,6 +157,6 @@
}
.callout-title-inner {
font-weight: 700;
font-weight: $boldWeight;
}
}

View File

@@ -4,3 +4,5 @@ $tabletBreakpoint: 1000px;
$sidePanelWidth: 380px;
$topSpacing: 6rem;
$fullPageWidth: $pageWidth + 2 * $sidePanelWidth;
$boldWeight: 700;
$normalWeight: 400;