From e0ebee5aa9b3646de722f139f1d8d15591df538e Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Sun, 02 Jul 2023 20:08:29 +0000
Subject: [PATCH] various polish
---
quartz/components/Footer.tsx | 2
quartz/components/Header.tsx | 1
quartz/components/PageList.tsx | 5
quartz/cfg.ts | 11 +
quartz/components/Head.tsx | 19 -
quartz/path.ts | 13 +
quartz/components/styles/graph.scss | 5
quartz/components/styles/listPage.scss | 29 ++-
quartz/components/scripts/plausible.inline.ts | 3
quartz/bootstrap-cli.mjs | 2
quartz/components/scripts/graph.inline.ts | 17 +
quartz/plugins/transformers/links.ts | 5
quartz/plugins/index.ts | 72 +++-----
quartz/components/pages/Content.tsx | 4
quartz/components/scripts/darkmode.inline.ts | 5
quartz/plugins/emitters/contentIndex.ts | 1
quartz/components/renderPage.tsx | 43 +++-
quartz/processors/emit.ts | 78 ++++++++
quartz.config.ts | 36 ++-
quartz/components/ArticleTitle.tsx | 5
quartz/styles/base.scss | 86 ++++++--
quartz/resources.tsx | 8
quartz/components/styles/search.scss | 3
quartz/plugins/transformers/gfm.ts | 5
quartz/components/scripts/popover.inline.ts | 32 +++
quartz/components/TagList.tsx | 23 +-
quartz/components/styles/backlinks.scss | 6
quartz/components/types.ts | 2
quartz/components/styles/popover.scss | 2
quartz/components/styles/footer.scss | 2
30 files changed, 337 insertions(+), 188 deletions(-)
diff --git a/quartz.config.ts b/quartz.config.ts
index f58bc32..58c1d9c 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -4,12 +4,7 @@
const sharedPageComponents = {
head: Component.Head(),
- header: [
- Component.PageTitle(),
- Component.Spacer(),
- Component.Search(),
- Component.Darkmode()
- ],
+ header: [],
footer: Component.Footer({
authorName: "Jacky",
links: {
@@ -25,11 +20,15 @@
Component.ReadingTime(),
Component.TagList(),
],
- left: [],
+ left: [
+ Component.PageTitle(),
+ Component.Search(),
+ Component.TableOfContents(),
+ Component.Darkmode()
+ ],
right: [
Component.Graph(),
- Component.TableOfContents(),
- Component.Backlinks()
+ Component.Backlinks(),
],
}
@@ -37,7 +36,11 @@
beforeBody: [
Component.ArticleTitle()
],
- left: [],
+ left: [
+ Component.PageTitle(),
+ Component.Search(),
+ Component.Darkmode()
+ ],
right: [],
}
@@ -46,6 +49,9 @@
pageTitle: "🪴 Quartz 4.0",
enableSPA: true,
enablePopovers: true,
+ analytics: {
+ provider: 'plausible',
+ },
canonicalUrl: "quartz.jzhao.xyz",
ignorePatterns: ["private", "templates"],
theme: {
@@ -102,16 +108,16 @@
...contentPageLayout,
pageBody: Component.Content(),
}),
- Plugin.TagPage({
- ...sharedPageComponents,
- ...listPageLayout,
- pageBody: Component.TagContent(),
- }),
Plugin.FolderPage({
...sharedPageComponents,
...listPageLayout,
pageBody: Component.FolderContent(),
}),
+ Plugin.TagPage({
+ ...sharedPageComponents,
+ ...listPageLayout,
+ pageBody: Component.TagContent(),
+ }),
Plugin.ContentIndex({
enableSiteMap: true,
enableRSS: true,
diff --git a/quartz/bootstrap-cli.mjs b/quartz/bootstrap-cli.mjs
index 2824f8e..3f71b17 100755
--- a/quartz/bootstrap-cli.mjs
+++ b/quartz/bootstrap-cli.mjs
@@ -64,7 +64,7 @@
packages: "external",
plugins: [
sassPlugin({
- type: 'css-text'
+ type: 'css-text',
}),
{
name: 'inline-script-loader',
diff --git a/quartz/cfg.ts b/quartz/cfg.ts
index 1c9ece8..49698ab 100644
--- a/quartz/cfg.ts
+++ b/quartz/cfg.ts
@@ -2,12 +2,23 @@
import { PluginTypes } from "./plugins/types"
import { Theme } from "./theme"
+export type Analytics = null
+ | {
+ provider: 'plausible'
+ }
+ | {
+ provider: 'google',
+ tagId: string
+ }
+
export interface GlobalConfiguration {
pageTitle: string,
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
enableSPA: boolean,
/** Whether to display Wikipedia-style popovers when hovering over links */
enablePopovers: boolean,
+ /** Analytics mode */
+ analytics: Analytics
/** Glob patterns to not search */
ignorePatterns: string[],
/** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.
diff --git a/quartz/components/ArticleTitle.tsx b/quartz/components/ArticleTitle.tsx
index c25769e..b8d58c6 100644
--- a/quartz/components/ArticleTitle.tsx
+++ b/quartz/components/ArticleTitle.tsx
@@ -2,9 +2,8 @@
function ArticleTitle({ fileData }: QuartzComponentProps) {
const title = fileData.frontmatter?.title
- const displayTitle = fileData.slug === "index" ? undefined : title
- if (displayTitle) {
- return <h1 class="article-title">{displayTitle}</h1>
+ if (title) {
+ return <h1 class="article-title">{title}</h1>
} else {
return null
}
diff --git a/quartz/components/Footer.tsx b/quartz/components/Footer.tsx
index 4229f9d..5fc6d64 100644
--- a/quartz/components/Footer.tsx
+++ b/quartz/components/Footer.tsx
@@ -14,7 +14,7 @@
return <>
<hr />
<footer>
- <p>Made by {name} using <a>Quartz</a>, © {year}</p>
+ <p>Made by {name} using <a href="https://quartz.jzhao.xyz/">Quartz</a>, © {year}</p>
<ul>{Object.entries(links).map(([text, link]) => <li>
<a href={link}>{text}</a>
</li>)}</ul>
diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx
index a0b62b7..f8439a0 100644
--- a/quartz/components/Head.tsx
+++ b/quartz/components/Head.tsx
@@ -2,15 +2,7 @@
import { JSResourceToScriptElement } from "../resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
-interface Options {
- prefetchContentIndex: boolean
-}
-
-const defaultOptions: Options = {
- prefetchContentIndex: true
-}
-
-export default ((opts?: Options) => {
+export default (() => {
function Head({ fileData, externalResources }: QuartzComponentProps) {
const slug = fileData.slug!
const title = fileData.frontmatter?.title ?? "Untitled"
@@ -20,10 +12,6 @@
const iconPath = baseDir + "/static/icon.png"
const ogImagePath = baseDir + "/static/og-image.png"
- const prefetchContentIndex = opts?.prefetchContentIndex ?? defaultOptions.prefetchContentIndex
- const contentIndexPath = baseDir + "/static/contentIndex.json"
- const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
-
return <head>
<title>{title}</title>
<meta charSet="utf-8" />
@@ -36,9 +24,8 @@
<link rel="icon" href={iconPath} />
<meta name="description" content={description} />
<meta name="generator" content="Quartz" />
- <link rel="preconnect" href="https://fonts.googleapis.com" />
- <link rel="preconnect" href="https://fonts.gstatic.com" />
- {prefetchContentIndex && <script spa-preserve>{contentIndexScript}</script>}
+ <link rel="preconnect" href="https://fonts.googleapis.com"/>
+ <link rel="preconnect" href="https://fonts.gstatic.com"/>
{css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
{js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
</head>
diff --git a/quartz/components/Header.tsx b/quartz/components/Header.tsx
index 06ae88b..0f13ca2 100644
--- a/quartz/components/Header.tsx
+++ b/quartz/components/Header.tsx
@@ -12,6 +12,7 @@
flex-direction: row;
align-items: center;
margin: 2em 0;
+ gap: 1.5rem;
}
header h1 {
diff --git a/quartz/components/PageList.tsx b/quartz/components/PageList.tsx
index e5d8dfb..3c39bee 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">
+ return <ul class="section-ul popover-hint">
{allFiles.sort(byDateAndAlphabetical).map(page => {
const title = page.frontmatter?.title
const pageSlug = page.slug!
@@ -36,9 +36,8 @@
<div class="desc">
<h3><a href={stripIndex(relativeToRoot(slug, pageSlug))} class="internal">{title}</a></h3>
</div>
- <div class="spacer"></div>
<ul class="tags">
- {tags.map(tag => <li><a href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
+ {tags.map(tag => <li><a class="internal" href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
</ul>
</div>
</li>
diff --git a/quartz/components/TagList.tsx b/quartz/components/TagList.tsx
index 65286a5..366889b 100644
--- a/quartz/components/TagList.tsx
+++ b/quartz/components/TagList.tsx
@@ -11,7 +11,7 @@
const display = `#${tag}`
const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
return <li>
- <a href={linkDest}>{display}</a>
+ <a href={linkDest} class="internal">{display}</a>
</li>
})}</ul>
} else {
@@ -25,17 +25,18 @@
display: flex;
padding-left: 0;
gap: 0.4rem;
+}
+
+.tags > li {
+ display: inline-block;
+ margin: 0;
+ overflow-wrap: normal;
+}
- & > li {
- display: inline-block;
- margin: 0;
-
- & > a {
- border-radius: 8px;
- border: var(--lightgray) 1px solid;
- padding: 0.2rem 0.5rem;
- }
- }
+.tags > li > a {
+ border-radius: 8px;
+ background-color: var(--highlight);
+ padding: 0.2rem 0.5rem;
}
`
diff --git a/quartz/components/pages/Content.tsx b/quartz/components/pages/Content.tsx
index 7856d6e..d233845 100644
--- a/quartz/components/pages/Content.tsx
+++ b/quartz/components/pages/Content.tsx
@@ -5,7 +5,7 @@
function Content({ tree }: QuartzComponentProps) {
// @ts-ignore (preact makes it angry)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
- return <article>{content}</article>
+ return <article class="popover-hint">{content}</article>
}
-export default (() => Content) satisfies QuartzComponentConstructor
\ No newline at end of file
+export default (() => Content) satisfies QuartzComponentConstructor
diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx
index 0e0f4c0..c70f092 100644
--- a/quartz/components/renderPage.tsx
+++ b/quartz/components/renderPage.tsx
@@ -17,10 +17,15 @@
export function pageResources(slug: string, staticResources: StaticResources): StaticResources {
const baseDir = resolveToRoot(slug)
+
+ const contentIndexPath = baseDir + "/static/contentIndex.json"
+ const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
+
return {
css: [baseDir + "/index.css", ...staticResources.css],
js: [
{ src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
+ { loadTime: "afterDOMReady", contentType: "inline", spaPreserve: true, script: contentIndexScript },
...staticResources.js,
{ src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
]
@@ -32,28 +37,40 @@
const Header = HeaderConstructor()
const Body = BodyConstructor()
+ const LeftComponent =
+ <div class="left">
+ <div class="left-inner">
+ {left.map(BodyComponent => <BodyComponent {...componentData} />)}
+ </div>
+ </div>
+
+ const RightComponent =
+ <div class="right">
+ <div class="right-inner">
+ {right.map(BodyComponent => <BodyComponent {...componentData} />)}
+ </div>
+ </div>
+
const doc = <html>
<Head {...componentData} />
<body data-slug={slug}>
<div id="quartz-root" class="page">
- <Header {...componentData} >
- {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
- </Header>
- <div class="popover-hint">
- {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
+ <div class="page-header">
+ <Header {...componentData} >
+ {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
+ </Header>
+ <div class="popover-hint">
+ {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
+ </div>
</div>
<Body {...componentData}>
- <div class="left">
- {left.map(BodyComponent => <BodyComponent {...componentData} />)}
- </div>
- <div class="center popover-hint">
+ {LeftComponent}
+ <div class="center">
<Content {...componentData} />
+ <Footer {...componentData} />
</div>
- <div class="right">
- {right.map(BodyComponent => <BodyComponent {...componentData} />)}
- </div>
+ {RightComponent}
</Body>
- <Footer {...componentData} />
</div>
</body>
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
diff --git a/quartz/components/scripts/darkmode.inline.ts b/quartz/components/scripts/darkmode.inline.ts
index f00a873..594bd3a 100644
--- a/quartz/components/scripts/darkmode.inline.ts
+++ b/quartz/components/scripts/darkmode.inline.ts
@@ -2,7 +2,7 @@
const currentTheme = localStorage.getItem('theme') ?? userPref
document.documentElement.setAttribute('saved-theme', currentTheme)
-window.addEventListener('DOMContentLoaded', () => {
+document.addEventListener("nav", () => {
const switchTheme = (e: any) => {
if (e.target.checked) {
document.documentElement.setAttribute('saved-theme', 'dark')
@@ -16,7 +16,8 @@
// Darkmode toggle
const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
- toggleSwitch.addEventListener('change', switchTheme, false)
+ toggleSwitch.removeEventListener('change', switchTheme)
+ toggleSwitch.addEventListener('change', switchTheme)
if (currentTheme === 'dark') {
toggleSwitch.checked = true
}
diff --git a/quartz/components/scripts/graph.inline.ts b/quartz/components/scripts/graph.inline.ts
index 27e9a81..169b8c4 100644
--- a/quartz/components/scripts/graph.inline.ts
+++ b/quartz/components/scripts/graph.inline.ts
@@ -266,9 +266,9 @@
})
}
-function renderGlobalGraph() {
+async function renderGlobalGraph() {
const slug = document.body.dataset["slug"]!
- renderGraph("global-graph-container", slug)
+ await renderGraph("global-graph-container", slug)
const container = document.getElementById("global-graph-outer")
container?.classList.add("active")
@@ -293,7 +293,14 @@
containerIcon?.addEventListener("click", renderGlobalGraph)
})
-window.addEventListener('resize', async () => {
- const slug = document.body.dataset["slug"]!
- await renderGraph("graph-container", slug)
+let resizeEventDebounce: number | undefined = undefined
+window.addEventListener('resize', () => {
+ if (resizeEventDebounce) {
+ clearTimeout(resizeEventDebounce)
+ }
+
+ resizeEventDebounce = window.setTimeout(async () => {
+ const slug = document.body.dataset["slug"]!
+ await renderGraph("graph-container", slug)
+ }, 50)
})
diff --git a/quartz/components/scripts/plausible.inline.ts b/quartz/components/scripts/plausible.inline.ts
new file mode 100644
index 0000000..60817c2
--- /dev/null
+++ b/quartz/components/scripts/plausible.inline.ts
@@ -0,0 +1,3 @@
+import Plausible from 'plausible-tracker'
+const { trackPageview } = Plausible()
+document.addEventListener("nav", () => trackPageview())
diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts
index 655831d..b388995 100644
--- a/quartz/components/scripts/popover.inline.ts
+++ b/quartz/components/scripts/popover.inline.ts
@@ -1,5 +1,24 @@
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
+// from micromorph/src/utils.ts
+// https://github.com/natemoo-re/micromorph/blob/main/src/utils.ts#L5
+export function normalizeRelativeURLs(
+ el: Element | Document,
+ base: string | URL
+) {
+ const update = (el: Element, attr: string, base: string | URL) => {
+ el.setAttribute(attr, new URL(el.getAttribute(attr)!, base).pathname)
+ }
+
+ el.querySelectorAll('[href^="./"], [href^="../"]').forEach((item) =>
+ update(item, 'href', base)
+ )
+
+ el.querySelectorAll('[src^="./"], [src^="../"]').forEach((item) =>
+ update(item, 'src', base)
+ )
+}
+
document.addEventListener("nav", () => {
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
const p = new DOMParser()
@@ -41,6 +60,7 @@
if (!contents) return
const html = p.parseFromString(contents, "text/html")
+ normalizeRelativeURLs(html, targetUrl)
const elts = [...html.getElementsByClassName("popover-hint")]
if (elts.length === 0) return
@@ -54,11 +74,13 @@
setPosition(popoverElement)
link.appendChild(popoverElement)
link.dataset.fetchedPopover = "true"
-
- 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' })
+
+ 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' })
+ }
}
})
}
diff --git a/quartz/components/styles/backlinks.scss b/quartz/components/styles/backlinks.scss
index 3344a7b..80baefc 100644
--- a/quartz/components/styles/backlinks.scss
+++ b/quartz/components/styles/backlinks.scss
@@ -7,13 +7,9 @@
& > ul {
list-style: none;
padding: 0;
- margin: 0;
+ margin: 0.5rem 0;
& > li {
- margin: 0.5rem 0;
- padding: 0.25rem 1rem;
- border: var(--lightgray) 1px solid;
- border-radius: 5px;
& > a {
background-color: transparent;
}
diff --git a/quartz/components/styles/footer.scss b/quartz/components/styles/footer.scss
index d104e50..16df545 100644
--- a/quartz/components/styles/footer.scss
+++ b/quartz/components/styles/footer.scss
@@ -1,6 +1,8 @@
footer {
text-align: left;
opacity: 0.8;
+ margin-bottom: 4rem;
+
& ul {
list-style: none;
margin: 0;
diff --git a/quartz/components/styles/graph.scss b/quartz/components/styles/graph.scss
index 244f2e4..4533a84 100644
--- a/quartz/components/styles/graph.scss
+++ b/quartz/components/styles/graph.scss
@@ -11,6 +11,7 @@
height: 250px;
margin: 0.5em 0;
position: relative;
+ overflow: hidden;
& > #global-graph-icon {
color: var(--dark);
@@ -30,10 +31,6 @@
background-color: var(--lightgray);
}
}
-
- & > #graph-container > svg {
- margin-bottom: -5px;
- }
}
& > #global-graph-outer {
diff --git a/quartz/components/styles/listPage.scss b/quartz/components/styles/listPage.scss
index a5d0a91..1823815 100644
--- a/quartz/components/styles/listPage.scss
+++ b/quartz/components/styles/listPage.scss
@@ -8,29 +8,36 @@
margin-bottom: 1em;
& > .section {
- display: flex;
- align-items: center;
+ display: grid;
+ grid-template-columns: 6em 3fr 1fr;
@media all and (max-width: 600px) {
- & .tags {
+ & > .tags {
display: none;
}
}
- & h3 > a {
- font-weight: 700;
- margin: 0;
- background-color: transparent;
+ & > .tags {
+ justify-self: end;
+ margin-left: 1rem;
}
- & p {
+ & > .desc a {
+ background-color: transparent;
+ }
+
+ & > .meta {
margin: 0;
- padding-right: 1em;
flex-basis: 6em;
+ opacity: 0.6;
}
}
+}
- & .meta {
- opacity: 0.6;
+// modifications in popover context
+.popover .section {
+ grid-template-columns: 6em 1fr !important;
+ & > .tags {
+ display: none;
}
}
diff --git a/quartz/components/styles/popover.scss b/quartz/components/styles/popover.scss
index 5ae09fe..80bdfad 100644
--- a/quartz/components/styles/popover.scss
+++ b/quartz/components/styles/popover.scss
@@ -24,7 +24,7 @@
height: 20rem;
padding: 0 1rem 1rem 1rem;
font-weight: initial;
- line-height: initial;
+ line-height: normal;
font-size: initial;
font-family: var(--bodyFont);
border: 1px solid var(--gray);
diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss
index 32d5744..cbf982a 100644
--- a/quartz/components/styles/search.scss
+++ b/quartz/components/styles/search.scss
@@ -1,8 +1,7 @@
.search {
min-width: 5rem;
- max-width: 12rem;
+ max-width: 14rem;
flex-grow: 0.3;
- margin: 0 1.5rem;
& > #search-icon {
background-color: var(--lightgray);
diff --git a/quartz/components/types.ts b/quartz/components/types.ts
index c7584b6..d1c153d 100644
--- a/quartz/components/types.ts
+++ b/quartz/components/types.ts
@@ -8,7 +8,7 @@
externalResources: StaticResources
fileData: QuartzPluginData
cfg: GlobalConfiguration
- children: QuartzComponent[] | JSX.Element[]
+ children: (QuartzComponent | JSX.Element)[]
tree: Node<QuartzPluginData>
allFiles: QuartzPluginData[]
}
diff --git a/quartz/path.ts b/quartz/path.ts
index 4755687..81cdb3a 100644
--- a/quartz/path.ts
+++ b/quartz/path.ts
@@ -5,7 +5,17 @@
return s.replace(/\s/g, '-')
}
+// on the client, 'index' isn't ever rendered so we should clean it up
+export function clientSideSlug(fp: string): string {
+ if (fp.endsWith("index")) {
+ fp = fp.slice(0, -"index".length)
+ }
+
+ return fp
+}
+
export function trimPathSuffix(fp: string): string {
+ fp = clientSideSlug(fp)
let [cleanPath, anchor] = fp.split("#", 2)
anchor = anchor === undefined ? "" : "#" + anchor
@@ -27,9 +37,6 @@
// resolve /a/b/c to ../../
export function resolveToRoot(slug: string): string {
let fp = trimPathSuffix(slug)
- if (fp.endsWith("index")) {
- fp = fp.slice(0, -"index".length)
- }
if (fp === "") {
return "."
diff --git a/quartz/plugins/emitters/contentIndex.ts b/quartz/plugins/emitters/contentIndex.ts
index f75334a..cf42756 100644
--- a/quartz/plugins/emitters/contentIndex.ts
+++ b/quartz/plugins/emitters/contentIndex.ts
@@ -36,7 +36,6 @@
const base = cfg.canonicalUrl ?? ""
const root = `https://${base}`
- // TODO: ogimage
const createURLEntry = (slug: string, content: ContentDetails): string => `<items>
<title>${content.title}</title>
<link>${root}/${slug}</link>
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index 8815811..c55e4dd 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -1,29 +1,17 @@
import { GlobalConfiguration } from '../cfg'
import { QuartzComponent } from '../components/types'
import { StaticResources } from '../resources'
-import { googleFontHref, joinStyles } from '../theme'
+import { joinStyles } from '../theme'
import { EmitCallback, PluginTypes } from './types'
import styles from '../styles/base.scss'
-// @ts-ignore
-import spaRouterScript from '../components/scripts/spa.inline'
-// @ts-ignore
-import popoverScript from '../components/scripts/popover.inline'
-import popoverStyle from '../components/styles/popover.scss'
-
export type ComponentResources = {
css: string[],
beforeDOMLoaded: string[],
afterDOMLoaded: string[]
}
-function joinScripts(scripts: string[]): string {
- // wrap with iife to prevent scope collision
- return scripts.map(script => `(function () {${script}})();`).join("\n")
-}
-
-export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
- const fps: string[] = []
+export function getComponentResources(plugins: PluginTypes): ComponentResources {
const allComponents: Set<QuartzComponent> = new Set()
for (const emitter of plugins.emitters) {
const components = emitter.getQuartzComponents()
@@ -50,41 +38,35 @@
componentResources.afterDOMLoaded.push(afterDOMLoaded)
}
}
-
- if (cfg.enablePopovers) {
- componentResources.afterDOMLoaded.push(popoverScript)
- componentResources.css.push(popoverStyle)
- }
- if (cfg.enableSPA) {
- componentResources.afterDOMLoaded.push(spaRouterScript)
- } else {
- componentResources.afterDOMLoaded.push(`
- window.spaNavigate = (url, _) => window.location.assign(url)
- const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
- document.dispatchEvent(event)`
- )
- }
+ return componentResources
+}
- emit({
- slug: "index",
- ext: ".css",
- content: joinStyles(cfg.theme, styles, ...componentResources.css)
- })
- emit({
- slug: "prescript",
- ext: ".js",
- content: joinScripts(componentResources.beforeDOMLoaded)
- })
- emit({
- slug: "postscript",
- ext: ".js",
- content: joinScripts(componentResources.afterDOMLoaded)
- })
+function joinScripts(scripts: string[]): string {
+ // wrap with iife to prevent scope collision
+ return scripts.map(script => `(function () {${script}})();`).join("\n")
+}
- fps.push("index.css", "prescript.js", "postscript.js")
- resources.css.push(googleFontHref(cfg.theme))
+export async function emitComponentResources(cfg: GlobalConfiguration, res: ComponentResources, emit: EmitCallback): Promise<string[]> {
+ const fps = await Promise.all([
+ emit({
+ slug: "index",
+ ext: ".css",
+ content: joinStyles(cfg.theme, styles, ...res.css)
+ }),
+ emit({
+ slug: "prescript",
+ ext: ".js",
+ content: joinScripts(res.beforeDOMLoaded)
+ }),
+ emit({
+ slug: "postscript",
+ ext: ".js",
+ content: joinScripts(res.afterDOMLoaded)
+ })
+ ])
return fps
+
}
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
diff --git a/quartz/plugins/transformers/gfm.ts b/quartz/plugins/transformers/gfm.ts
index 54f8ca6..f966e58 100644
--- a/quartz/plugins/transformers/gfm.ts
+++ b/quartz/plugins/transformers/gfm.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
import remarkGfm from "remark-gfm"
import smartypants from 'remark-smartypants'
import { QuartzTransformerPlugin } from "../types"
@@ -20,14 +19,14 @@
return {
name: "GitHubFlavoredMarkdown",
markdownPlugins() {
- return opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
+ return opts.enableSmartyPants ? [remarkGfm, smartypants] : [remarkGfm]
},
htmlPlugins() {
if (opts.linkHeadings) {
return [rehypeSlug, [rehypeAutolinkHeadings, {
behavior: 'append', content: {
type: 'text',
- value: ' §'
+ value: ' §',
}
}]]
} else {
diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts
index 1391452..b8a800a 100644
--- a/quartz/plugins/transformers/links.ts
+++ b/quartz/plugins/transformers/links.ts
@@ -1,5 +1,5 @@
import { QuartzTransformerPlugin } from "../types"
-import { relativeToRoot, slugify, trimPathSuffix } from "../../path"
+import { clientSideSlug, relativeToRoot, slugify, trimPathSuffix } from "../../path"
import path from "path"
import { visit } from 'unist-util-visit'
import isAbsoluteUrl from "is-absolute-url"
@@ -27,7 +27,7 @@
htmlPlugins() {
return [() => {
return (tree, file) => {
- const curSlug = file.data.slug!
+ const curSlug = clientSideSlug(file.data.slug!)
const transformLink = (target: string) => {
const targetSlug = slugify(decodeURI(target).trim())
if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
@@ -49,7 +49,6 @@
let dest = node.properties.href
node.properties.className = isAbsoluteUrl(dest) ? "external" : "internal"
-
// don't process external links or intra-document anchors
if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
node.properties.href = transformLink(dest)
diff --git a/quartz/processors/emit.ts b/quartz/processors/emit.ts
index e1438fa..7150f5e 100644
--- a/quartz/processors/emit.ts
+++ b/quartz/processors/emit.ts
@@ -1,13 +1,69 @@
import path from "path"
import fs from "fs"
-import { QuartzConfig } from "../cfg"
+import { GlobalConfiguration, QuartzConfig } from "../cfg"
import { PerfTimer } from "../perf"
-import { emitComponentResources, getStaticResourcesFromPlugins } from "../plugins"
+import { ComponentResources, emitComponentResources, getComponentResources, getStaticResourcesFromPlugins } from "../plugins"
import { EmitCallback } from "../plugins/types"
import { ProcessedContent } from "../plugins/vfile"
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'
+// @ts-ignore
+import plausibleScript from '../components/scripts/plausible.inline'
+// @ts-ignore
+import popoverScript from '../components/scripts/popover.inline'
+import popoverStyle from '../components/styles/popover.scss'
+import { StaticResources } from "../resources"
+
+function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: StaticResources, componentResources: ComponentResources) {
+ // font and other resources
+ staticResources.css.push(googleFontHref(cfg.theme))
+
+ // popovers
+ if (cfg.enablePopovers) {
+ componentResources.afterDOMLoaded.push(popoverScript)
+ componentResources.css.push(popoverStyle)
+ }
+
+ if (cfg.analytics?.provider === "google") {
+ const tagId = cfg.analytics.tagId
+ staticResources.js.push({
+ src: `https://www.googletagmanager.com/gtag/js?id=${tagId}`,
+ contentType: 'external',
+ loadTime: 'afterDOMReady',
+ })
+ componentResources.afterDOMLoaded.push(`
+ window.dataLayer = window.dataLayer || [];
+ function gtag() { dataLayer.push(arguments); }
+ gtag(\`js\`, new Date());
+ gtag(\`config\`, \`${tagId}\`, { send_page_view: false });
+
+ document.addEventListener(\`nav\`, () => {
+ gtag(\`event\`, \`page_view\`, {
+ page_title: document.title,
+ page_location: location.href,
+ });
+ });`
+ )
+ } else if (cfg.analytics?.provider === "plausible") {
+ componentResources.afterDOMLoaded.push(plausibleScript)
+ }
+
+ // spa
+ if (cfg.enableSPA) {
+ componentResources.afterDOMLoaded.push(spaRouterScript)
+ } else {
+ componentResources.afterDOMLoaded.push(`
+ window.spaNavigate = (url, _) => window.location.assign(url)
+ const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
+ document.dispatchEvent(event)`
+ )
+ }
+}
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
const perf = new PerfTimer()
@@ -19,9 +75,25 @@
return pathToPage
}
+ // initialize from plugins
const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
- emitComponentResources(cfg.configuration, staticResources, cfg.plugins, emit)
+ // 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
+ addGlobalPageResources(cfg.configuration, staticResources, componentResources)
+
+ // emit in one go
+ const emittedResources = await emitComponentResources(cfg.configuration, componentResources, emit)
+ if (verbose) {
+ for (const file of emittedResources) {
+ console.log(`[emit:Resources] ${file}`)
+ }
+ }
+
+ // emitter plugins
let emittedFiles = 0
for (const emitter of cfg.plugins.emitters) {
try {
diff --git a/quartz/resources.tsx b/quartz/resources.tsx
index 78ae10b..3780751 100644
--- a/quartz/resources.tsx
+++ b/quartz/resources.tsx
@@ -3,7 +3,8 @@
export type JSResource = {
loadTime: 'beforeDOMReady' | 'afterDOMReady'
- moduleType?: 'module'
+ moduleType?: 'module',
+ spaPreserve?: boolean
} & ({
src: string
contentType: 'external'
@@ -14,11 +15,12 @@
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
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={preserve} />
+ 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={preserve}>{content}</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 45e6370..db0299b 100644
--- a/quartz/styles/base.scss
+++ b/quartz/styles/base.scss
@@ -11,6 +11,9 @@
box-sizing: border-box;
background-color: var(--light);
font-family: var(--bodyFont);
+ --pageWidth: 800px;
+ --sidePanelWidth: 400px;
+ --topSpacing: 6rem;
}
.text-highlight {
@@ -27,7 +30,7 @@
a {
font-weight: 600;
text-decoration: none;
- transition: all 0.2s ease;
+ transition: color 0.2s ease;
color: var(--secondary);
&:hover {
@@ -43,34 +46,48 @@
}
.page {
- margin: 6rem 35vw 6rem 20vw;
- max-width: 1000px;
- position: relative;
+ & > .page-header {
+ max-width: var(--pageWidth);
+ margin: var(--topSpacing) auto 0 auto;
+ }
- & .left, & .right {
- position: fixed;
- height: 100vh;
- overflow-y: scroll;
- box-sizing: border-box;
+ & > #quartz-body {
+ width: 100%;
display: flex;
- flex-direction: column;
- top: 0;
- gap: 2rem;
- padding: 6rem;
- }
-
- & .left {
- left: 0;
- padding-left: 10vw;
- width: 20vw;
- }
- & .right {
- right: 0;
- padding-right: 10vw;
- width: 35vw;
- }
+ & .left, & .right {
+ flex: 1;
+ width: calc(calc(100vw - var(--pageWidth)) / 2);
+ }
+ & .left-inner, & .right-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ top: 0;
+ width: var(--sidePanelWidth);
+ margin-top: calc(var(--topSpacing));
+ box-sizing: border-box;
+ padding: 0 4rem;
+ position: fixed;
+ }
+
+ & .left-inner {
+ left: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
+ }
+
+ & .right-inner {
+ right: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
+ }
+
+ & .center {
+ width: var(--pageWidth);
+ margin: 0 auto;
+ }
+ }
+}
+
+.page {
@media all and (max-width: 1200px) {
margin: 25px 5vw;
& .left, & .right {
@@ -89,9 +106,26 @@
& > h1 {
font-size: 2rem;
}
+
+ // darkmode diagrams
+ & svg {
+ stroke: var(--dark);
+ }
+
+ & ul:has(input[type='checkbox']) {
+ list-style-type: none;
+ padding-left: 0;
+ }
}
}
+input[type="checkbox"] {
+ transform: translateY(2px);
+ color: var(--secondary);
+ border-color: var(--lightgray);
+ background-color: var(--light);
+}
+
blockquote {
margin: 1rem 0;
border-left: 3px solid var(--secondary);
@@ -120,7 +154,7 @@
}
h1, h2, h3, h4, h5, h6 {
- &[id] > a {
+ &[id] > a[href^="#"] {
margin: 0 0.5rem;
opacity: 0;
transition: opacity 0.2s ease;
--
Gitblit v1.10.0