From ab9da02c60c962128820e6874e6f07c98bc3dda7 Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Tue, 04 Jul 2023 17:08:32 +0000
Subject: [PATCH] fix indexing causing main thread freeze, various polish
---
quartz/styles/variables.scss | 5
package-lock.json | 9 +
quartz/components/index.ts | 6
quartz/components/PageList.tsx | 2
quartz/components/Head.tsx | 4
quartz/path.ts | 8 +
quartz/components/DesktopOnly.tsx | 20 ++
quartz/components/styles/graph.scss | 2
quartz/components/styles/listPage.scss | 6
quartz/components/scripts/search.inline.ts | 8
quartz/components/scripts/graph.inline.ts | 18 +-
quartz/plugins/index.ts | 26 ++-
quartz/plugins/emitters/contentIndex.ts | 7
quartz/plugins/emitters/tagPage.tsx | 3
quartz/components/renderPage.tsx | 2
quartz/processors/emit.ts | 10 +
quartz/components/TableOfContents.tsx | 2
quartz.config.ts | 4
quartz/styles/base.scss | 40 +++--
quartz/resources.tsx | 2
quartz/components/MobileOnly.tsx | 20 ++
quartz/components/styles/search.scss | 4
quartz/components/scripts/popover.inline.ts | 126 +++++++++--------
quartz/components/TagList.tsx | 1
quartz/components/Graph.tsx | 18 +-
quartz/components/ReadingTime.tsx | 3
package.json | 1
quartz/plugins/emitters/folderPage.tsx | 3
quartz/theme.ts | 8
quartz/components/pages/TagContent.tsx | 9
quartz/components/styles/popover.scss | 4
quartz/components/pages/FolderContent.tsx | 10
quartz/components/styles/darkmode.scss | 1
33 files changed, 253 insertions(+), 139 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 92e22eb..fb8f23c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"mdast-util-find-and-replace": "^2.2.2",
"mdast-util-to-string": "^3.2.0",
"micromorph": "^0.4.5",
+ "plausible-tracker": "^0.3.8",
"preact": "^10.14.1",
"preact-render-to-string": "^6.0.3",
"pretty-time": "^1.1.0",
@@ -3619,6 +3620,14 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/plausible-tracker": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/plausible-tracker/-/plausible-tracker-0.3.8.tgz",
+ "integrity": "sha512-lmOWYQ7s9KOUJ1R+YTOR3HrjdbxIS2Z4de0P/Jx2dQPteznJl2eX3tXxKClpvbfyGP59B5bbhW8ftN59HbbFSg==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/preact": {
"version": "10.15.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.15.1.tgz",
diff --git a/package.json b/package.json
index 614bb76..689548d 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"mdast-util-find-and-replace": "^2.2.2",
"mdast-util-to-string": "^3.2.0",
"micromorph": "^0.4.5",
+ "plausible-tracker": "^0.3.8",
"preact": "^10.14.1",
"preact-render-to-string": "^6.0.3",
"pretty-time": "^1.1.0",
diff --git a/quartz.config.ts b/quartz.config.ts
index 58c1d9c..18f2533 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -23,8 +23,8 @@
left: [
Component.PageTitle(),
Component.Search(),
- Component.TableOfContents(),
- Component.Darkmode()
+ Component.Darkmode(),
+ Component.DesktopOnly(Component.TableOfContents()),
],
right: [
Component.Graph(),
diff --git a/quartz/components/DesktopOnly.tsx b/quartz/components/DesktopOnly.tsx
new file mode 100644
index 0000000..a1c5dae
--- /dev/null
+++ b/quartz/components/DesktopOnly.tsx
@@ -0,0 +1,20 @@
+import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
+
+export default ((component?: QuartzComponent) => {
+ if (component) {
+ const Component = component
+ function DesktopOnly(props: QuartzComponentProps) {
+ return <div class="desktop-only">
+ <Component {...props} />
+ </div>
+ }
+
+ DesktopOnly.displayName = component.displayName
+ DesktopOnly.afterDOMLoaded = component?.afterDOMLoaded
+ DesktopOnly.beforeDOMLoaded = component?.beforeDOMLoaded
+ DesktopOnly.css = component?.css
+ return DesktopOnly
+ } else {
+ return () => <></>
+ }
+}) satisfies QuartzComponentConstructor
diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx
index 0146188..e7f1df2 100644
--- a/quartz/components/Graph.tsx
+++ b/quartz/components/Graph.tsx
@@ -25,23 +25,23 @@
drag: true,
zoom: true,
depth: 1,
- scale: 1.2,
- repelForce: 2,
- centerForce: 1,
+ scale: 1.1,
+ repelForce: 0.5,
+ centerForce: 0.3,
linkDistance: 30,
fontSize: 0.6,
- opacityScale: 3
+ opacityScale: 1
},
globalGraph: {
drag: true,
zoom: true,
depth: -1,
- scale: 1.2,
- repelForce: 1,
- centerForce: 1,
+ scale: 0.9,
+ repelForce: 0.5,
+ centerForce: 0.3,
linkDistance: 30,
- fontSize: 0.5,
- opacityScale: 3
+ fontSize: 0.6,
+ opacityScale: 1
}
}
diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx
index f8439a0..bfc7bae 100644
--- a/quartz/components/Head.tsx
+++ b/quartz/components/Head.tsx
@@ -1,10 +1,10 @@
-import { resolveToRoot } from "../path"
+import { clientSideSlug, resolveToRoot } from "../path"
import { JSResourceToScriptElement } from "../resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
export default (() => {
function Head({ fileData, externalResources }: QuartzComponentProps) {
- const slug = fileData.slug!
+ const slug = clientSideSlug(fileData.slug!)
const title = fileData.frontmatter?.title ?? "Untitled"
const description = fileData.description ?? "No description provided"
const { css, js } = externalResources
diff --git a/quartz/components/MobileOnly.tsx b/quartz/components/MobileOnly.tsx
new file mode 100644
index 0000000..b75fd76
--- /dev/null
+++ b/quartz/components/MobileOnly.tsx
@@ -0,0 +1,20 @@
+import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
+
+export default ((component?: QuartzComponent) => {
+ if (component) {
+ const Component = component
+ function MobileOnly(props: QuartzComponentProps) {
+ return <div class="mobile-only">
+ <Component {...props} />
+ </div>
+ }
+
+ MobileOnly.displayName = component.displayName
+ MobileOnly.afterDOMLoaded = component?.afterDOMLoaded
+ MobileOnly.beforeDOMLoaded = component?.beforeDOMLoaded
+ MobileOnly.css = component?.css
+ return MobileOnly
+ } else {
+ return () => <></>
+ }
+}) satisfies QuartzComponentConstructor
diff --git a/quartz/components/PageList.tsx b/quartz/components/PageList.tsx
index 3c39bee..b92720d 100644
--- a/quartz/components/PageList.tsx
+++ b/quartz/components/PageList.tsx
@@ -23,7 +23,7 @@
export function PageList({ fileData, allFiles }: QuartzComponentProps) {
const slug = fileData.slug!
- return <ul class="section-ul popover-hint">
+ return <ul class="section-ul">
{allFiles.sort(byDateAndAlphabetical).map(page => {
const title = page.frontmatter?.title
const pageSlug = page.slug!
diff --git a/quartz/components/ReadingTime.tsx b/quartz/components/ReadingTime.tsx
index cac8711..9c64289 100644
--- a/quartz/components/ReadingTime.tsx
+++ b/quartz/components/ReadingTime.tsx
@@ -3,8 +3,7 @@
function ReadingTime({ fileData }: QuartzComponentProps) {
const text = fileData.text
- const isHomePage = fileData.slug === "index"
- if (text && !isHomePage) {
+ if (text) {
const { text: timeTaken, words } = readingTime(text)
return <p class="reading-time">{words} words, {timeTaken}</p>
} else {
diff --git a/quartz/components/TableOfContents.tsx b/quartz/components/TableOfContents.tsx
index f3d90bb..a9d7b78 100644
--- a/quartz/components/TableOfContents.tsx
+++ b/quartz/components/TableOfContents.tsx
@@ -18,7 +18,7 @@
return null
}
- return <div>
+ return <div class="desktop-only">
<button type="button" id="toc">
<h3>Table of Contents</h3>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold">
diff --git a/quartz/components/TagList.tsx b/quartz/components/TagList.tsx
index 366889b..6b93719 100644
--- a/quartz/components/TagList.tsx
+++ b/quartz/components/TagList.tsx
@@ -29,6 +29,7 @@
.tags > li {
display: inline-block;
+ white-space: nowrap;
margin: 0;
overflow-wrap: normal;
}
diff --git a/quartz/components/index.ts b/quartz/components/index.ts
index ed0c668..35d5a64 100644
--- a/quartz/components/index.ts
+++ b/quartz/components/index.ts
@@ -13,6 +13,8 @@
import Backlinks from "./Backlinks"
import Search from "./Search"
import Footer from "./Footer"
+import DesktopOnly from "./DesktopOnly"
+import MobileOnly from "./MobileOnly"
export {
ArticleTitle,
@@ -29,5 +31,7 @@
Graph,
Backlinks,
Search,
- Footer
+ Footer,
+ DesktopOnly,
+ MobileOnly
}
diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx
index 4806843..445074c 100644
--- a/quartz/components/pages/FolderContent.tsx
+++ b/quartz/components/pages/FolderContent.tsx
@@ -6,7 +6,7 @@
import style from '../styles/listPage.scss'
import { PageList } from "../PageList"
-function TagContent(props: QuartzComponentProps) {
+function FolderContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props
const folderSlug = fileData.slug!
const allPagesInFolder = allFiles.filter(file => {
@@ -25,13 +25,15 @@
// @ts-ignore
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
- return <div>
+ return <div class="popover-hint">
<article>{content}</article>
+ <hr/>
+ <p>{allPagesInFolder.length} items under this folder.</p>
<div>
<PageList {...listProps} />
</div>
</div>
}
-TagContent.css = style + PageList.css
-export default (() => TagContent) satisfies QuartzComponentConstructor
+FolderContent.css = style + PageList.css
+export default (() => FolderContent) satisfies QuartzComponentConstructor
diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx
index e7e5f6d..b84ce29 100644
--- a/quartz/components/pages/TagContent.tsx
+++ b/quartz/components/pages/TagContent.tsx
@@ -3,13 +3,14 @@
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import style from '../styles/listPage.scss'
import { PageList } from "../PageList"
+import { clientSideSlug } from "../../path"
function TagContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props
const slug = fileData.slug
- if (slug?.startsWith("tags/")) {
- const tag = slug.slice("tags/".length)
+ if (slug?.startsWith("tags/")) {
+ const tag = clientSideSlug(slug.slice("tags/".length))
const allPagesWithTag = allFiles.filter(file => (file.frontmatter?.tags ?? []).includes(tag))
const listProps = {
...props,
@@ -18,8 +19,10 @@
// @ts-ignore
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
- return <div>
+ return <div class="popover-hint">
<article>{content}</article>
+ <hr/>
+ <p>{allPagesWithTag.length} items with this tag.</p>
<div>
<PageList {...listProps} />
</div>
diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx
index c70f092..4da6370 100644
--- a/quartz/components/renderPage.tsx
+++ b/quartz/components/renderPage.tsx
@@ -25,7 +25,7 @@
css: [baseDir + "/index.css", ...staticResources.css],
js: [
{ src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
- { loadTime: "afterDOMReady", contentType: "inline", spaPreserve: true, script: contentIndexScript },
+ { loadTime: "beforeDOMReady", contentType: "inline", spaPreserve: true, script: contentIndexScript },
...staticResources.js,
{ src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
]
diff --git a/quartz/components/scripts/graph.inline.ts b/quartz/components/scripts/graph.inline.ts
index 169b8c4..194a23b 100644
--- a/quartz/components/scripts/graph.inline.ts
+++ b/quartz/components/scripts/graph.inline.ts
@@ -110,12 +110,12 @@
.join("line")
.attr("class", "link")
.attr("stroke", "var(--lightgray)")
- .attr("stroke-width", 2)
+ .attr("stroke-width", 1)
// svg groups
const graphNode = svg.append("g").selectAll("g").data(graphData.nodes).enter().append("g")
- // calculate radius
+ // calculate color
const color = (d: NodeData) => {
const isCurrent = d.id === slug
if (isCurrent) {
@@ -182,7 +182,12 @@
neighbourNodes.transition().duration(200).attr("fill", color)
// highlight links
- linkNodes.transition().duration(200).attr("stroke", "var(--gray)")
+ linkNodes
+ .transition()
+ .duration(200)
+ .attr("stroke", "var(--gray)")
+ .attr("stroke-width", 1)
+
const bigFont = fontSize * 1.5
@@ -220,7 +225,7 @@
const labels = graphNode
.append("text")
.attr("dx", 0)
- .attr("dy", (d) => nodeRadius(d) + 8 + "px")
+ .attr("dy", (d) => nodeRadius(d) - 8 + "px")
.attr("text-anchor", "middle")
.text((d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "))
.style('opacity', (opacityScale - 1) / 3.75)
@@ -266,12 +271,11 @@
})
}
-async function renderGlobalGraph() {
+function renderGlobalGraph() {
const slug = document.body.dataset["slug"]!
- await renderGraph("global-graph-container", slug)
const container = document.getElementById("global-graph-outer")
container?.classList.add("active")
-
+ renderGraph("global-graph-container", slug)
function hideGlobalGraph() {
container?.classList.remove("active")
diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts
index b388995..666371b 100644
--- a/quartz/components/scripts/popover.inline.ts
+++ b/quartz/components/scripts/popover.inline.ts
@@ -19,69 +19,73 @@
)
}
+const p = new DOMParser()
+async function mouseEnterHandler(this: HTMLLinkElement, { clientX, clientY }: { clientX: number, clientY: number }) {
+ const link = this
+ async function setPosition(popoverElement: HTMLElement) {
+ const { x, y } = await computePosition(link, popoverElement, {
+ middleware: [
+ inline({ x: clientX, y: clientY }),
+ shift(),
+ flip()
+ ]
+ })
+ Object.assign(popoverElement.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ })
+ }
+
+ // dont refetch if there's already a popover
+ if ([...link.children].some(child => child.classList.contains("popover"))) {
+ return setPosition(link.lastChild as HTMLElement)
+ }
+
+ const thisUrl = new URL(document.location.href)
+ thisUrl.hash = ""
+ thisUrl.search = ""
+ const targetUrl = new URL(link.href)
+ const hash = targetUrl.hash
+ targetUrl.hash = ""
+ targetUrl.search = ""
+ // prevent hover of the same page
+ if (thisUrl.toString() === targetUrl.toString()) return
+
+ const contents = await fetch(`${targetUrl}`)
+ .then((res) => res.text())
+ .catch((err) => {
+ console.error(err)
+ })
+
+ if (!contents) return
+ const html = p.parseFromString(contents, "text/html")
+ normalizeRelativeURLs(html, targetUrl)
+ const elts = [...html.getElementsByClassName("popover-hint")]
+ if (elts.length === 0) return
+
+ const popoverElement = document.createElement("div")
+ popoverElement.classList.add("popover")
+ const popoverInner = document.createElement("div")
+ popoverInner.classList.add("popover-inner")
+ popoverElement.appendChild(popoverInner)
+ elts.forEach(elt => popoverInner.appendChild(elt))
+
+ setPosition(popoverElement)
+ link.appendChild(popoverElement)
+
+ if (hash !== "") {
+ const heading = popoverInner.querySelector(hash) as HTMLElement | null
+ if (heading) {
+ // leave ~12px of buffer when scrolling to a heading
+ popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
+ }
+ }
+}
+
document.addEventListener("nav", () => {
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
- const p = new DOMParser()
for (const link of links) {
- link.addEventListener("mouseenter", async ({ clientX, clientY }) => {
- async function setPosition(popoverElement: HTMLElement) {
- const { x, y } = await computePosition(link, popoverElement, {
- middleware: [
- inline({ x: clientX, y: clientY }),
- shift(),
- flip()
- ]
- })
- Object.assign(popoverElement.style, {
- left: `${x}px`,
- top: `${y}px`,
- })
- }
-
- if (link.dataset.fetchedPopover === "true") {
- return setPosition(link.lastChild as HTMLElement)
- }
-
- const thisUrl = new URL(document.location.href)
- thisUrl.hash = ""
- thisUrl.search = ""
- const targetUrl = new URL(link.href)
- const hash = targetUrl.hash
- targetUrl.hash = ""
- targetUrl.search = ""
- // prevent hover of the same page
- if (thisUrl.toString() === targetUrl.toString()) return
-
- const contents = await fetch(`${targetUrl}`)
- .then((res) => res.text())
- .catch((err) => {
- console.error(err)
- })
-
- if (!contents) return
- const html = p.parseFromString(contents, "text/html")
- normalizeRelativeURLs(html, targetUrl)
- const elts = [...html.getElementsByClassName("popover-hint")]
- if (elts.length === 0) return
-
- const popoverElement = document.createElement("div")
- popoverElement.classList.add("popover")
- const popoverInner = document.createElement("div")
- popoverInner.classList.add("popover-inner")
- popoverElement.appendChild(popoverInner)
- elts.forEach(elt => popoverInner.appendChild(elt))
-
- setPosition(popoverElement)
- link.appendChild(popoverElement)
- link.dataset.fetchedPopover = "true"
-
- if (hash !== "") {
- const heading = popoverInner.querySelector(hash) as HTMLElement | null
- if (heading) {
- // leave ~12px of buffer when scrolling to a heading
- popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
- }
- }
- })
+ link.removeEventListener("mouseenter", mouseEnterHandler)
+ link.addEventListener("mouseenter", mouseEnterHandler)
}
})
diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts
index 78517fe..f69d77d 100644
--- a/quartz/components/scripts/search.inline.ts
+++ b/quartz/components/scripts/search.inline.ts
@@ -11,7 +11,8 @@
const contextWindowWords = 30
function highlight(searchTerm: string, text: string, trim?: boolean) {
- const tokenizedTerms = searchTerm.split(/\s+/).filter(t => t !== "")
+ // try to highlight longest tokens first
+ const tokenizedTerms = searchTerm.split(/\s+/).filter(t => t !== "").sort((a, b) => b.length - a.length)
let tokenizedText = text
.split(/\s+/)
.filter(t => t !== "")
@@ -42,7 +43,7 @@
// see if this tok is prefixed by any search terms
for (const searchTok of tokenizedTerms) {
if (tok.toLowerCase().includes(searchTok.toLowerCase())) {
- const regex = new RegExp(searchTok, "gi")
+ const regex = new RegExp(searchTok.toLowerCase(), "gi")
return tok.replace(regex, `<span class="highlight">$&</span>`)
}
}
@@ -81,7 +82,7 @@
})
for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
- index.add({
+ await index.addAsync(slug, {
slug,
title: fileData.title,
content: fileData.content
@@ -169,7 +170,6 @@
displayResults(finalResults)
}
-
document.removeEventListener("keydown", shortcutHandler)
document.addEventListener("keydown", shortcutHandler)
searchIcon?.removeEventListener("click", showSearch)
diff --git a/quartz/components/styles/darkmode.scss b/quartz/components/styles/darkmode.scss
index 730fcd2..0edfebd 100644
--- a/quartz/components/styles/darkmode.scss
+++ b/quartz/components/styles/darkmode.scss
@@ -2,6 +2,7 @@
position: relative;
width: 20px;
height: 20px;
+ margin: 1rem;
& > .toggle {
display: none;
diff --git a/quartz/components/styles/graph.scss b/quartz/components/styles/graph.scss
index 4533a84..2200282 100644
--- a/quartz/components/styles/graph.scss
+++ b/quartz/components/styles/graph.scss
@@ -40,9 +40,9 @@
top: 0;
width: 100vw;
height: 100%;
- overflow: scroll;
backdrop-filter: blur(4px);
display: none;
+ overflow: hidden;
&.active {
display: inline-block;
diff --git a/quartz/components/styles/listPage.scss b/quartz/components/styles/listPage.scss
index 1823815..6c8e1b5 100644
--- a/quartz/components/styles/listPage.scss
+++ b/quartz/components/styles/listPage.scss
@@ -1,3 +1,5 @@
+@use "../../styles/variables.scss" as *;
+
ul.section-ul {
list-style: none;
margin-top: 2em;
@@ -11,7 +13,7 @@
display: grid;
grid-template-columns: 6em 3fr 1fr;
- @media all and (max-width: 600px) {
+ @media all and (max-width: $mobileBreakpoint) {
& > .tags {
display: none;
}
@@ -22,7 +24,7 @@
margin-left: 1rem;
}
- & > .desc a {
+ & > .desc > h3 > a {
background-color: transparent;
}
diff --git a/quartz/components/styles/popover.scss b/quartz/components/styles/popover.scss
index 80bdfad..05c6dc7 100644
--- a/quartz/components/styles/popover.scss
+++ b/quartz/components/styles/popover.scss
@@ -1,3 +1,5 @@
+@use "../../styles/variables.scss" as *;
+
@keyframes dropin {
0% {
opacity: 0;
@@ -42,7 +44,7 @@
opacity: 0;
transition: opacity 0.3s ease, visibility 0.3s ease;
- @media all and (max-width: 600px) {
+ @media all and (max-width: $mobileBreakpoint) {
display: none !important;
}
}
diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss
index cbf982a..1f0a8b5 100644
--- a/quartz/components/styles/search.scss
+++ b/quartz/components/styles/search.scss
@@ -1,3 +1,5 @@
+@use "../../styles/variables.scss" as *;
+
.search {
min-width: 5rem;
max-width: 14rem;
@@ -55,7 +57,7 @@
margin-left: auto;
margin-right: auto;
- @media all and (max-width: 1200px) {
+ @media all and (max-width: $tabletBreakpoint) {
width: 90%;
}
diff --git a/quartz/path.ts b/quartz/path.ts
index 81cdb3a..a0fb223 100644
--- a/quartz/path.ts
+++ b/quartz/path.ts
@@ -7,10 +7,16 @@
// on the client, 'index' isn't ever rendered so we should clean it up
export function clientSideSlug(fp: string): string {
+ // remove index
if (fp.endsWith("index")) {
fp = fp.slice(0, -"index".length)
}
+ // remove trailing slash
+ if (fp.endsWith("/")) {
+ fp = fp.slice(0, -1)
+ }
+
return fp
}
@@ -23,7 +29,7 @@
}
export function slugify(s: string): string {
- const [fp, anchor] = s.split("#", 2)
+ let [fp, anchor] = s.split("#", 2)
const sluggedAnchor = anchor === undefined ? "" : "#" + slugAnchor(anchor)
const withoutFileExt = fp.replace(new RegExp(path.extname(fp) + '$'), '')
const rawSlugSegments = withoutFileExt.split(path.sep)
diff --git a/quartz/plugins/emitters/contentIndex.ts b/quartz/plugins/emitters/contentIndex.ts
index cf42756..a1d8648 100644
--- a/quartz/plugins/emitters/contentIndex.ts
+++ b/quartz/plugins/emitters/contentIndex.ts
@@ -15,11 +15,13 @@
interface Options {
enableSiteMap: boolean
enableRSS: boolean
+ includeEmptyFiles: boolean
}
const defaultOptions: Options = {
enableSiteMap: true,
enableRSS: true,
+ includeEmptyFiles: false,
}
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
@@ -57,7 +59,7 @@
</rss>`
}
-export const ContentIndex: QuartzEmitterPlugin<Options> = (opts) => {
+export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
opts = { ...defaultOptions, ...opts }
return {
name: "ContentIndex",
@@ -67,6 +69,7 @@
for (const [_tree, file] of content) {
const slug = file.data.slug!
const date = file.data.dates?.modified ?? new Date()
+ if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
linkIndex.set(slug, {
title: file.data.frontmatter?.title!,
links: file.data.links ?? [],
@@ -75,6 +78,7 @@
date: date,
description: file.data.description ?? ""
})
+ }
}
if (opts?.enableSiteMap) {
@@ -106,6 +110,7 @@
return [slug, content]
})
)
+
await emit({
content: JSON.stringify(simplifiedIndex),
slug: fp,
diff --git a/quartz/plugins/emitters/folderPage.tsx b/quartz/plugins/emitters/folderPage.tsx
index ee8f0b9..1eed30d 100644
--- a/quartz/plugins/emitters/folderPage.tsx
+++ b/quartz/plugins/emitters/folderPage.tsx
@@ -6,6 +6,7 @@
import { ProcessedContent, defaultProcessedContent } from "../vfile"
import { FullPageLayout } from "../../cfg"
import path from "path"
+import { clientSideSlug } from "../../path"
export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (opts) => {
if (!opts) {
@@ -36,7 +37,7 @@
])))
for (const [tree, file] of content) {
- const slug = file.data.slug!
+ const slug = clientSideSlug(file.data.slug!)
if (folders.has(slug)) {
folderDescriptions[slug] = [tree, file]
}
diff --git a/quartz/plugins/emitters/tagPage.tsx b/quartz/plugins/emitters/tagPage.tsx
index 1f69715..0cdb7c3 100644
--- a/quartz/plugins/emitters/tagPage.tsx
+++ b/quartz/plugins/emitters/tagPage.tsx
@@ -5,6 +5,7 @@
import { pageResources, renderPage } from "../../components/renderPage"
import { ProcessedContent, defaultProcessedContent } from "../vfile"
import { FullPageLayout } from "../../cfg"
+import { clientSideSlug } from "../../path"
export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (opts) => {
if (!opts) {
@@ -30,7 +31,7 @@
])))
for (const [tree, file] of content) {
- const slug = file.data.slug!
+ const slug = clientSideSlug(file.data.slug!)
if (slug.startsWith("tags/")) {
const tag = slug.slice("tags/".length)
if (tags.has(tag)) {
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index c55e4dd..0de0283 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -20,26 +20,30 @@
}
}
- const componentResources: ComponentResources = {
- css: [],
- beforeDOMLoaded: [],
- afterDOMLoaded: []
+ const componentResources = {
+ css: new Set<string>(),
+ beforeDOMLoaded: new Set<string>(),
+ afterDOMLoaded: new Set<string>()
}
for (const component of allComponents) {
const { css, beforeDOMLoaded, afterDOMLoaded } = component
if (css) {
- componentResources.css.push(css)
+ componentResources.css.add(css)
}
if (beforeDOMLoaded) {
- componentResources.beforeDOMLoaded.push(beforeDOMLoaded)
+ componentResources.beforeDOMLoaded.add(beforeDOMLoaded)
}
if (afterDOMLoaded) {
- componentResources.afterDOMLoaded.push(afterDOMLoaded)
+ componentResources.afterDOMLoaded.add(afterDOMLoaded)
}
}
-
- return componentResources
+
+ return {
+ css: [...componentResources.css],
+ beforeDOMLoaded: [...componentResources.beforeDOMLoaded],
+ afterDOMLoaded: [...componentResources.afterDOMLoaded]
+ }
}
function joinScripts(scripts: string[]): string {
@@ -78,10 +82,10 @@
for (const transformer of plugins.transformers) {
const res = transformer.externalResources ? transformer.externalResources() : {}
if (res?.js) {
- staticResources.js = staticResources.js.concat(res.js)
+ staticResources.js.push(...res.js)
}
if (res?.css) {
- staticResources.css = staticResources.css.concat(res.css)
+ staticResources.css.push(...res.css)
}
}
diff --git a/quartz/processors/emit.ts b/quartz/processors/emit.ts
index 7150f5e..59875f5 100644
--- a/quartz/processors/emit.ts
+++ b/quartz/processors/emit.ts
@@ -8,7 +8,6 @@
import { QUARTZ, slugify } from "../path"
import { globbyStream } from "globby"
import chalk from "chalk"
-import { googleFontHref } from '../theme'
// @ts-ignore
import spaRouterScript from '../components/scripts/spa.inline'
@@ -18,9 +17,10 @@
import popoverScript from '../components/scripts/popover.inline'
import popoverStyle from '../components/styles/popover.scss'
import { StaticResources } from "../resources"
+import { QuartzLogger } from "../log"
+import { googleFontHref } from "../theme"
function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: StaticResources, componentResources: ComponentResources) {
- // font and other resources
staticResources.css.push(googleFontHref(cfg.theme))
// popovers
@@ -67,6 +67,9 @@
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
const perf = new PerfTimer()
+ const log = new QuartzLogger(verbose)
+
+ log.start(`Emitting output files`)
const emit: EmitCallback = async ({ slug, ext, content }) => {
const pathToPage = path.join(output, slug + ext)
const dir = path.dirname(pathToPage)
@@ -80,6 +83,7 @@
// component specific scripts and styles
const componentResources = getComponentResources(cfg.plugins)
+
// important that this goes *after* component scripts
// as the "nav" event gets triggered here and we should make sure
// that everyone else had the chance to register a listener for it
@@ -136,5 +140,5 @@
}
}
- console.log(`Emitted ${emittedFiles} files to \`${output}\` in ${perf.timeSince()}`)
+ log.success(`Emitted ${emittedFiles} files to \`${output}\` in ${perf.timeSince()}`)
}
diff --git a/quartz/resources.tsx b/quartz/resources.tsx
index 3780751..525210b 100644
--- a/quartz/resources.tsx
+++ b/quartz/resources.tsx
@@ -17,7 +17,7 @@
const scriptType = resource.moduleType ?? 'application/javascript'
const spaPreserve = preserve ?? resource.spaPreserve
if (resource.contentType === 'external') {
- return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={spaPreserve} />
+ return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={spaPreserve}/>
} else {
const content = resource.script
return <script key={randomUUID()} type={scriptType} spa-preserve={spaPreserve}>{content}</script>
diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss
index db0299b..e741755 100644
--- a/quartz/styles/base.scss
+++ b/quartz/styles/base.scss
@@ -1,5 +1,6 @@
-@import "./syntax.scss";
-@import "./callouts.scss";
+@use "./syntax.scss";
+@use "./callouts.scss";
+@use "./variables.scss" as *;
html {
scroll-behavior: smooth;
@@ -11,9 +12,6 @@
box-sizing: border-box;
background-color: var(--light);
font-family: var(--bodyFont);
- --pageWidth: 800px;
- --sidePanelWidth: 400px;
- --topSpacing: 6rem;
}
.text-highlight {
@@ -47,8 +45,8 @@
.page {
& > .page-header {
- max-width: var(--pageWidth);
- margin: var(--topSpacing) auto 0 auto;
+ max-width: $pageWidth;
+ margin: $topSpacing auto 0 auto;
}
& > #quartz-body {
@@ -57,7 +55,7 @@
& .left, & .right {
flex: 1;
- width: calc(calc(100vw - var(--pageWidth)) / 2);
+ width: calc(calc(100vw - $pageWidth) / 2);
}
& .left-inner, & .right-inner {
@@ -65,30 +63,44 @@
flex-direction: column;
gap: 2rem;
top: 0;
- width: var(--sidePanelWidth);
- margin-top: calc(var(--topSpacing));
+ width: $sidePanelWidth;
+ margin-top: $topSpacing;
box-sizing: border-box;
padding: 0 4rem;
position: fixed;
}
& .left-inner {
- left: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
+ left: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
}
& .right-inner {
- right: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
+ right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
}
& .center {
- width: var(--pageWidth);
+ width: $pageWidth;
margin: 0 auto;
}
}
}
+.desktop-only {
+ display: initial;
+ @media all and (max-width: ($pageWidth + 2 * $sidePanelWidth)) {
+ display: none;
+ }
+}
+
+.mobile-only {
+ display: none;
+ @media all and (max-width: ($pageWidth + 2 * $sidePanelWidth)) {
+ display: initial;
+ }
+}
+
.page {
- @media all and (max-width: 1200px) {
+ @media all and (max-width: $tabletBreakpoint) {
margin: 25px 5vw;
& .left, & .right {
padding: 0;
diff --git a/quartz/styles/variables.scss b/quartz/styles/variables.scss
new file mode 100644
index 0000000..792223b
--- /dev/null
+++ b/quartz/styles/variables.scss
@@ -0,0 +1,5 @@
+$pageWidth: 800px;
+$mobileBreakpoint: 600px;
+$tabletBreakpoint: 1200px;
+$sidePanelWidth: 400px;
+$topSpacing: 6rem;
diff --git a/quartz/theme.ts b/quartz/theme.ts
index 318f5cc..820519f 100644
--- a/quartz/theme.ts
+++ b/quartz/theme.ts
@@ -21,6 +21,8 @@
}
}
+const DEFAULT_SANS_SERIF = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif"
+const DEFAULT_MONO = "ui-monospace, SFMono-Regular, SF Mono, Menlo, monospace"
export function googleFontHref(theme: Theme) {
const { code, header, body } = theme.typography
return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap`
@@ -37,9 +39,9 @@
--tertiary: ${theme.colors.lightMode.tertiary};
--highlight: ${theme.colors.lightMode.highlight};
- --headerFont: ${theme.typography.header};
- --bodyFont: ${theme.typography.body};
- --codeFont: ${theme.typography.code};
+ --headerFont: ${theme.typography.header}, ${DEFAULT_SANS_SERIF};
+ --bodyFont: ${theme.typography.body}, ${DEFAULT_SANS_SERIF};
+ --codeFont: ${theme.typography.code}, ${DEFAULT_MONO};
}
:root[saved-theme="dark"] {
--
Gitblit v1.10.0