This commit is contained in:
Ryan Kes 2024-08-12 18:23:40 +02:00
commit 5eef85ccdc
39 changed files with 272 additions and 159 deletions

View file

@ -26,7 +26,7 @@ jobs:
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v4 uses: actions/cache@v4
@ -59,7 +59,7 @@ jobs:
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20
- name: Get package version - name: Get package version
run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
- name: Create release tag - name: Create release tag

1
.node-version Normal file
View file

@ -0,0 +1 @@
v20.9.0

View file

@ -61,6 +61,8 @@ jobs:
with: with:
fetch-depth: 0 # Fetch all history for git info fetch-depth: 0 # Fetch all history for git info
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Dependencies - name: Install Dependencies
run: npm ci run: npm ci
- name: Build Quartz - name: Build Quartz
@ -187,7 +189,7 @@ stages:
- build - build
- deploy - deploy
image: node:18 image: node:20
cache: # Cache modules in between jobs cache: # Cache modules in between jobs
key: $CI_COMMIT_REF_SLUG key: $CI_COMMIT_REF_SLUG
paths: paths:

View file

@ -6,7 +6,7 @@ Quartz is a fast, batteries-included static-site generator that transforms Markd
## 🪴 Get Started ## 🪴 Get Started
Quartz requires **at least [Node](https://nodejs.org/) v18.14** and `npm` v9.3.1 to function correctly. Ensure you have this installed on your machine before continuing. Quartz requires **at least [Node](https://nodejs.org/) v20** and `npm` v9.3.1 to function correctly. Ensure you have this installed on your machine before continuing.
Then, in your terminal of choice, enter the following commands line by line: Then, in your terminal of choice, enter the following commands line by line:

View file

@ -12,6 +12,7 @@ This plugin adds LaTeX support to Quartz. See [[features/Latex|Latex]] for more
This plugin accepts the following configuration options: This plugin accepts the following configuration options:
- `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/) or `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html). Defaults to KaTeX. - `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/) or `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html). Defaults to KaTeX.
- `customMacros`: custom macros for all LaTeX blocks. It takes the form of a key-value pair where the key is a new command name and the value is the expansion of the macro. For example: `{"\\R": "\\mathbb{R}"}`
## API ## API

View file

@ -9,6 +9,7 @@ Want to see what Quartz can do? Here are some cool community gardens:
- [Socratica Toolbox](https://toolbox.socratica.info/) - [Socratica Toolbox](https://toolbox.socratica.info/)
- [Morrowind Modding Wiki](https://morrowind-modding.github.io/) - [Morrowind Modding Wiki](https://morrowind-modding.github.io/)
- [Aaron Pham's Garden](https://aarnphm.xyz/) - [Aaron Pham's Garden](https://aarnphm.xyz/)
- [Pelayo Arbues' Notes](https://pelayoarbues.com/)
- [Stanford CME 302 Numerical Linear Algebra](https://ericdarve.github.io/NLA/) - [Stanford CME 302 Numerical Linear Algebra](https://ericdarve.github.io/NLA/)
- [A Pattern Language - Christopher Alexander (Architecture)](https://patternlanguage.cc/) - [A Pattern Language - Christopher Alexander (Architecture)](https://patternlanguage.cc/)
- [oldwinter の数字花园](https://garden.oldwinter.top/) - [oldwinter の数字花园](https://garden.oldwinter.top/)
@ -23,5 +24,7 @@ Want to see what Quartz can do? Here are some cool community gardens:
- [sspaeti.com's Second Brain](https://brain.sspaeti.com/) - [sspaeti.com's Second Brain](https://brain.sspaeti.com/)
- [🪴Aster's notebook](https://notes.asterhu.com) - [🪴Aster's notebook](https://notes.asterhu.com)
- [Gatekeeper Wiki](https://www.gatekeeper.wiki) - [Gatekeeper Wiki](https://www.gatekeeper.wiki)
- [Ellie's Notes](https://ellie.wtf)
- [🥷🏻🌳🍃 Computer Science & Thinkering Garden](https://notes.yxy.ninja)
If you want to see your own on here, submit a [Pull Request adding yourself to this file](https://github.com/jackyzha0/quartz/blob/v4/docs/showcase.md)! If you want to see your own on here, submit a [Pull Request adding yourself to this file](https://github.com/jackyzha0/quartz/blob/v4/docs/showcase.md)!

4
globals.d.ts vendored
View file

@ -4,6 +4,10 @@ export declare global {
type: K, type: K,
listener: (this: Document, ev: CustomEventMap[K]) => void, listener: (this: Document, ev: CustomEventMap[K]) => void,
): void ): void
removeEventListener<K extends keyof CustomEventMap>(
type: K,
listener: (this: Document, ev: CustomEventMap[K]) => void,
): void
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K] | UIEvent): void dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K] | UIEvent): void
} }
interface Window { interface Window {

79
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "@jackyzha0/quartz", "name": "@jackyzha0/quartz",
"version": "4.2.4", "version": "4.3.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@jackyzha0/quartz", "name": "@jackyzha0/quartz",
"version": "4.2.4", "version": "4.3.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@clack/prompts": "^0.7.0", "@clack/prompts": "^0.7.0",
@ -85,7 +85,7 @@
"typescript": "^5.5.4" "typescript": "^5.5.4"
}, },
"engines": { "engines": {
"node": ">=18.14", "node": "20 || >=22",
"npm": ">=9.3.1" "npm": ">=9.3.1"
} }
}, },
@ -2434,9 +2434,9 @@
"integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==" "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg=="
}, },
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.1.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
"dependencies": { "dependencies": {
"cross-spawn": "^7.0.0", "cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1" "signal-exit": "^4.0.1"
@ -2516,21 +2516,22 @@
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
}, },
"node_modules/glob": { "node_modules/glob": {
"version": "10.3.10", "version": "11.0.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
"dependencies": { "dependencies": {
"foreground-child": "^3.1.0", "foreground-child": "^3.1.0",
"jackspeak": "^2.3.5", "jackspeak": "^4.0.1",
"minimatch": "^9.0.1", "minimatch": "^10.0.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "minipass": "^7.1.2",
"path-scurry": "^1.10.1" "package-json-from-dist": "^1.0.0",
"path-scurry": "^2.0.0"
}, },
"bin": { "bin": {
"glob": "dist/esm/bin.mjs" "glob": "dist/esm/bin.mjs"
}, },
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": "20 || >=22"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
@ -3176,14 +3177,14 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
}, },
"node_modules/jackspeak": { "node_modules/jackspeak": {
"version": "2.3.6", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz",
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==",
"dependencies": { "dependencies": {
"@isaacs/cliui": "^8.0.2" "@isaacs/cliui": "^8.0.2"
}, },
"engines": { "engines": {
"node": ">=14" "node": "20 || >=22"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
@ -3480,11 +3481,11 @@
} }
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "10.1.0", "version": "11.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz",
"integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==",
"engines": { "engines": {
"node": "14 || >=16.14" "node": "20 || >=22"
} }
}, },
"node_modules/markdown-table": { "node_modules/markdown-table": {
@ -4532,23 +4533,23 @@
} }
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "9.0.3", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
"dependencies": { "dependencies": {
"brace-expansion": "^2.0.1" "brace-expansion": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": "20 || >=22"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/minipass": { "node_modules/minipass": {
"version": "7.0.4", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
@ -4626,6 +4627,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/package-json-from-dist": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
},
"node_modules/parse-entities": { "node_modules/parse-entities": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
@ -4702,15 +4708,15 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
}, },
"node_modules/path-scurry": { "node_modules/path-scurry": {
"version": "1.10.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
"dependencies": { "dependencies": {
"lru-cache": "^9.1.1 || ^10.0.0", "lru-cache": "^11.0.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" "minipass": "^7.1.2"
}, },
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": "20 || >=22"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
@ -5321,7 +5327,8 @@
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.8.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.8.tgz",
"integrity": "sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw==", "integrity": "sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw==",
"dependencies": { "dependencies": {
"glob": "^10.3.7" "glob": "^11.0.0",
"package-json-from-dist": "^1.0.0"
}, },
"bin": { "bin": {
"rimraf": "dist/esm/bin.mjs" "rimraf": "dist/esm/bin.mjs"

View file

@ -2,7 +2,7 @@
"name": "@jackyzha0/quartz", "name": "@jackyzha0/quartz",
"description": "🌱 publish your digital garden and notes as a website", "description": "🌱 publish your digital garden and notes as a website",
"private": true, "private": true,
"version": "4.2.4", "version": "4.3.0",
"type": "module", "type": "module",
"author": "jackyzha0 <j.zhao2k19@gmail.com>", "author": "jackyzha0 <j.zhao2k19@gmail.com>",
"license": "MIT", "license": "MIT",
@ -21,7 +21,7 @@
}, },
"engines": { "engines": {
"npm": ">=9.3.1", "npm": ">=9.3.1",
"node": ">=18.14" "node": "20 || >=22"
}, },
"keywords": [ "keywords": [
"site generator", "site generator",

View file

@ -38,8 +38,13 @@ type BuildData = {
type FileEvent = "add" | "change" | "delete" type FileEvent = "add" | "change" | "delete"
function newBuildId() {
return new Date().toISOString()
}
async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) { async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
const ctx: BuildCtx = { const ctx: BuildCtx = {
buildId: newBuildId(),
argv, argv,
cfg, cfg,
allSlugs: [], allSlugs: [],
@ -167,6 +172,7 @@ async function partialRebuildFromEntrypoint(
const perf = new PerfTimer() const perf = new PerfTimer()
console.log(chalk.yellow("Detected change, rebuilding...")) console.log(chalk.yellow("Detected change, rebuilding..."))
ctx.buildId = newBuildId()
// UPDATE DEP GRAPH // UPDATE DEP GRAPH
const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath
@ -363,14 +369,10 @@ async function rebuildFromEntrypoint(
const perf = new PerfTimer() const perf = new PerfTimer()
console.log(chalk.yellow("Detected change, rebuilding...")) console.log(chalk.yellow("Detected change, rebuilding..."))
ctx.buildId = newBuildId()
try { try {
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp)) const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
const trackedSlugs = [...new Set([...contentMap.keys(), ...toRebuild, ...trackedAssets])]
.filter((fp) => !toRemove.has(fp))
.map((fp) => slugifyFilePath(path.posix.relative(argv.directory, fp) as FilePath))
ctx.allSlugs = [...new Set([...initialSlugs, ...trackedSlugs])]
const parsedContent = await parseMarkdown(ctx, filesToRebuild) const parsedContent = await parseMarkdown(ctx, filesToRebuild)
for (const content of parsedContent) { for (const content of parsedContent) {
const [_tree, vfile] = content const [_tree, vfile] = content
@ -384,6 +386,13 @@ async function rebuildFromEntrypoint(
const parsedFiles = [...contentMap.values()] const parsedFiles = [...contentMap.values()]
const filteredContent = filterContent(ctx, parsedFiles) const filteredContent = filterContent(ctx, parsedFiles)
// re-update slugs
const trackedSlugs = [...new Set([...contentMap.keys(), ...toRebuild, ...trackedAssets])]
.filter((fp) => !toRemove.has(fp))
.map((fp) => slugifyFilePath(path.posix.relative(argv.directory, fp) as FilePath))
ctx.allSlugs = [...new Set([...initialSlugs, ...trackedSlugs])]
// TODO: we can probably traverse the link graph to figure out what's safe to delete here // TODO: we can probably traverse the link graph to figure out what's safe to delete here
// instead of just deleting everything // instead of just deleting everything
await rimraf(path.join(argv.output, ".*"), { glob: true }) await rimraf(path.join(argv.output, ".*"), { glob: true })

View file

@ -1,4 +1,7 @@
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
// @ts-ignore
import script from "./scripts/comments.inline"
type Options = { type Options = {
provider: "giscus" provider: "giscus"
@ -19,49 +22,23 @@ function boolToStringBool(b: boolean): string {
} }
export default ((opts: Options) => { export default ((opts: Options) => {
const Comments: QuartzComponent = (_props: QuartzComponentProps) => <div class="giscus"></div> const Comments: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
return (
<div
class={classNames(displayClass, "giscus")}
data-repo={opts.options.repo}
data-repo-id={opts.options.repoId}
data-category={opts.options.category}
data-category-id={opts.options.categoryId}
data-mapping={opts.options.mapping ?? "url"}
data-strict={boolToStringBool(opts.options.strict ?? true)}
data-reactions-enabled={boolToStringBool(opts.options.reactionsEnabled ?? true)}
data-input-position={opts.options.inputPosition ?? "bottom"}
></div>
)
}
Comments.afterDOMLoaded = ` Comments.afterDOMLoaded = script
const changeTheme = (e) => {
const theme = e.detail.theme
const iframe = document.querySelector('iframe.giscus-frame')
if (!iframe) {
return
}
iframe.contentWindow.postMessage({
giscus: {
setConfig: {
theme: theme
}
}
}, 'https://giscus.app')
}
document.addEventListener("nav", () => {
const giscusContainer = document.querySelector(".giscus")
const giscusScript = document.createElement("script")
giscusScript.src = "https://giscus.app/client.js"
giscusScript.async = true
giscusScript.crossOrigin = "anonymous"
giscusScript.setAttribute("data-loading", "lazy")
giscusScript.setAttribute("data-emit-metadata", "0")
giscusScript.setAttribute("data-repo", "${opts.options.repo}")
giscusScript.setAttribute("data-repo-id", "${opts.options.repoId}")
giscusScript.setAttribute("data-category", "${opts.options.category}")
giscusScript.setAttribute("data-category-id", "${opts.options.categoryId}")
giscusScript.setAttribute("data-mapping", "${opts.options.mapping ?? "url"}")
giscusScript.setAttribute("data-strict", "${boolToStringBool(opts.options.strict ?? true)}")
giscusScript.setAttribute("data-reactions-enabled", "${boolToStringBool(opts.options.reactionsEnabled ?? true)}")
giscusScript.setAttribute("data-input-position", "${opts.options.inputPosition ?? "bottom"}")
const theme = document.documentElement.getAttribute("saved-theme")
giscusScript.setAttribute("data-theme", theme)
giscusContainer.appendChild(giscusScript)
document.addEventListener("themechange", changeTheme)
window.addCleanup(() => document.removeEventListener("themechange", changeTheme))
})`
return Comments return Comments
}) satisfies QuartzComponentConstructor<Options> }) satisfies QuartzComponentConstructor<Options>

View file

@ -44,12 +44,9 @@ export default ((userOpts?: Partial<Options>) => {
// memoized // memoized
let fileTree: FileNode let fileTree: FileNode
let jsonTree: string let jsonTree: string
let lastBuildId: string = ""
function constructFileTree(allFiles: QuartzPluginData[]) { function constructFileTree(allFiles: QuartzPluginData[]) {
if (fileTree) {
return
}
// Construct tree from allFiles // Construct tree from allFiles
fileTree = new FileNode("") fileTree = new FileNode("")
allFiles.forEach((file) => fileTree.add(file)) allFiles.forEach((file) => fileTree.add(file))
@ -76,12 +73,17 @@ export default ((userOpts?: Partial<Options>) => {
} }
const Explorer: QuartzComponent = ({ const Explorer: QuartzComponent = ({
ctx,
cfg, cfg,
allFiles, allFiles,
displayClass, displayClass,
fileData, fileData,
}: QuartzComponentProps) => { }: QuartzComponentProps) => {
constructFileTree(allFiles) if (ctx.buildId !== lastBuildId) {
lastBuildId = ctx.buildId
constructFileTree(allFiles)
}
return ( return (
<div class={classNames(displayClass, "explorer")}> <div class={classNames(displayClass, "explorer")}>
<button <button
@ -91,8 +93,10 @@ export default ((userOpts?: Partial<Options>) => {
data-collapsed={opts.folderDefaultState} data-collapsed={opts.folderDefaultState}
data-savestate={opts.useSavedState} data-savestate={opts.useSavedState}
data-tree={jsonTree} data-tree={jsonTree}
aria-controls="explorer-content"
aria-expanded={opts.folderDefaultState === "open"}
> >
<h1>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h1> <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="14" width="14"

View file

@ -7,14 +7,15 @@ const PageTitle: QuartzComponent = ({ fileData, cfg, displayClass }: QuartzCompo
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
const baseDir = pathToRoot(fileData.slug!) const baseDir = pathToRoot(fileData.slug!)
return ( return (
<h1 class={classNames(displayClass, "page-title")}> <h2 class={classNames(displayClass, "page-title")}>
<a href={baseDir}>{title}</a> <a href={baseDir}>{title}</a>
</h1> </h2>
) )
} }
PageTitle.css = ` PageTitle.css = `
.page-title { .page-title {
font-size: 1.75rem;
margin: 0; margin: 0;
} }
` `

View file

@ -19,24 +19,16 @@ export default ((userOpts?: Partial<SearchOptions>) => {
const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder
return ( return (
<div class={classNames(displayClass, "search")}> <div class={classNames(displayClass, "search")}>
<div id="search-icon"> <button class="search-button" id="search-button">
<p>{i18n(cfg.locale).components.search.title}</p> <p>{i18n(cfg.locale).components.search.title}</p>
<div></div> <svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7">
<svg <title>Search</title>
tabIndex={0}
aria-labelledby="title desc"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 19.9 19.7"
>
<title id="title">Search</title>
<desc id="desc">Search</desc>
<g class="search-path" fill="none"> <g class="search-path" fill="none">
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" /> <path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
<circle cx="8" cy="8" r="7" /> <circle cx="8" cy="8" r="7" />
</g> </g>
</svg> </svg>
</div> </button>
<div id="search-container"> <div id="search-container">
<div id="search-space"> <div id="search-space">
<input <input

View file

@ -26,7 +26,13 @@ const TableOfContents: QuartzComponent = ({
return ( return (
<div class={classNames(displayClass, "toc")}> <div class={classNames(displayClass, "toc")}>
<button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}> <button
type="button"
id="toc"
class={fileData.collapseToc ? "collapsed" : ""}
aria-controls="toc-content"
aria-expanded={!fileData.collapseToc}
>
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3> <h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View file

@ -0,0 +1,67 @@
const changeTheme = (e: CustomEventMap["themechange"]) => {
const theme = e.detail.theme
const iframe = document.querySelector("iframe.giscus-frame") as HTMLIFrameElement
if (!iframe) {
return
}
if (!iframe.contentWindow) {
return
}
iframe.contentWindow.postMessage(
{
giscus: {
setConfig: {
theme: theme,
},
},
},
"https://giscus.app",
)
}
type GiscusElement = Omit<HTMLElement, "dataset"> & {
dataset: DOMStringMap & {
repo: `${string}/${string}`
repoId: string
category: string
categoryId: string
mapping: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
strict: string
reactionsEnabled: string
inputPosition: "top" | "bottom"
}
}
document.addEventListener("nav", () => {
const giscusContainer = document.querySelector(".giscus") as GiscusElement
if (!giscusContainer) {
return
}
const giscusScript = document.createElement("script")
giscusScript.src = "https://giscus.app/client.js"
giscusScript.async = true
giscusScript.crossOrigin = "anonymous"
giscusScript.setAttribute("data-loading", "lazy")
giscusScript.setAttribute("data-emit-metadata", "0")
giscusScript.setAttribute("data-repo", giscusContainer.dataset.repo)
giscusScript.setAttribute("data-repo-id", giscusContainer.dataset.repoId)
giscusScript.setAttribute("data-category", giscusContainer.dataset.category)
giscusScript.setAttribute("data-category-id", giscusContainer.dataset.categoryId)
giscusScript.setAttribute("data-mapping", giscusContainer.dataset.mapping)
giscusScript.setAttribute("data-strict", giscusContainer.dataset.strict)
giscusScript.setAttribute("data-reactions-enabled", giscusContainer.dataset.reactionsEnabled)
giscusScript.setAttribute("data-input-position", giscusContainer.dataset.inputPosition)
const theme = document.documentElement.getAttribute("saved-theme")
if (theme) {
giscusScript.setAttribute("data-theme", theme)
}
giscusContainer.appendChild(giscusScript)
document.addEventListener("themechange", changeTheme)
window.addCleanup(() => document.removeEventListener("themechange", changeTheme))
})

View file

@ -17,6 +17,10 @@ const observer = new IntersectionObserver((entries) => {
function toggleExplorer(this: HTMLElement) { function toggleExplorer(this: HTMLElement) {
this.classList.toggle("collapsed") this.classList.toggle("collapsed")
this.setAttribute(
"aria-expanded",
this.getAttribute("aria-expanded") === "true" ? "false" : "true",
)
const content = this.nextElementSibling as MaybeHTMLElement const content = this.nextElementSibling as MaybeHTMLElement
if (!content) return if (!content) return

View file

@ -102,7 +102,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
const graphData: { nodes: NodeData[]; links: LinkData[] } = { const graphData: { nodes: NodeData[]; links: LinkData[] } = {
nodes: [...neighbourhood].map((url) => { nodes: [...neighbourhood].map((url) => {
const text = url.startsWith("tags/") ? "#" + url.substring(5) : data.get(url)?.title ?? url const text = url.startsWith("tags/") ? "#" + url.substring(5) : (data.get(url)?.title ?? url)
return { return {
id: url, id: url,
text: text, text: text,

View file

@ -148,7 +148,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const data = await fetchData const data = await fetchData
const container = document.getElementById("search-container") const container = document.getElementById("search-container")
const sidebar = container?.closest(".sidebar") as HTMLElement const sidebar = container?.closest(".sidebar") as HTMLElement
const searchIcon = document.getElementById("search-icon") const searchButton = document.getElementById("search-button")
const searchBar = document.getElementById("search-bar") as HTMLInputElement | null const searchBar = document.getElementById("search-bar") as HTMLInputElement | null
const searchLayout = document.getElementById("search-layout") const searchLayout = document.getElementById("search-layout")
const idDataMap = Object.keys(data) as FullSlug[] const idDataMap = Object.keys(data) as FullSlug[]
@ -191,6 +191,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
} }
searchType = "basic" // reset search type after closing searchType = "basic" // reset search type after closing
searchButton?.focus()
} }
function showSearch(searchTypeNew: SearchType) { function showSearch(searchTypeNew: SearchType) {
@ -458,8 +460,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
document.addEventListener("keydown", shortcutHandler) document.addEventListener("keydown", shortcutHandler)
window.addCleanup(() => document.removeEventListener("keydown", shortcutHandler)) window.addCleanup(() => document.removeEventListener("keydown", shortcutHandler))
searchIcon?.addEventListener("click", () => showSearch("basic")) searchButton?.addEventListener("click", () => showSearch("basic"))
window.addCleanup(() => searchIcon?.removeEventListener("click", () => showSearch("basic"))) window.addCleanup(() => searchButton?.removeEventListener("click", () => showSearch("basic")))
searchBar?.addEventListener("input", onType) searchBar?.addEventListener("input", onType)
window.addCleanup(() => searchBar?.removeEventListener("input", onType)) window.addCleanup(() => searchBar?.removeEventListener("input", onType))

View file

@ -16,6 +16,10 @@ const observer = new IntersectionObserver((entries) => {
function toggleToc(this: HTMLElement) { function toggleToc(this: HTMLElement) {
this.classList.toggle("collapsed") this.classList.toggle("collapsed")
this.setAttribute(
"aria-expanded",
this.getAttribute("aria-expanded") === "true" ? "false" : "true",
)
const content = this.nextElementSibling as HTMLElement | undefined const content = this.nextElementSibling as HTMLElement | undefined
if (!content) return if (!content) return
content.classList.toggle("collapsed") content.classList.toggle("collapsed")

View file

@ -3,6 +3,7 @@ export function registerEscapeHandler(outsideContainer: HTMLElement | null, cb:
function click(this: HTMLElement, e: HTMLElementEventMap["click"]) { function click(this: HTMLElement, e: HTMLElementEventMap["click"]) {
if (e.target !== this) return if (e.target !== this) return
e.preventDefault() e.preventDefault()
e.stopPropagation()
cb() cb()
} }

View file

@ -1,7 +1,6 @@
@use "../../styles/variables.scss" as *; @use "../../styles/variables.scss" as *;
button#explorer { button#explorer {
all: unset;
background-color: transparent; background-color: transparent;
border: none; border: none;
text-align: left; text-align: left;
@ -11,7 +10,7 @@ button#explorer {
display: flex; display: flex;
align-items: center; align-items: center;
& h1 { & h2 {
font-size: 1rem; font-size: 1rem;
display: inline-block; display: inline-block;
margin: 0; margin: 0;
@ -46,8 +45,18 @@ button#explorer {
list-style: none; list-style: none;
overflow: hidden; overflow: hidden;
max-height: none; max-height: none;
transition: max-height 0.35s ease; transition:
max-height 0.35s ease,
visibility 0s linear 0s;
margin-top: 0.5rem; margin-top: 0.5rem;
visibility: visible;
&.collapsed {
transition:
max-height 0.35s ease,
visibility 0s linear 0.35s;
visibility: hidden;
}
&.collapsed > .overflow::after { &.collapsed > .overflow::after {
opacity: 0; opacity: 0;

View file

@ -5,18 +5,21 @@
max-width: 14rem; max-width: 14rem;
flex-grow: 0.3; flex-grow: 0.3;
& > #search-icon { & > .search-button {
background-color: var(--lightgray); background-color: var(--lightgray);
border: none;
border-radius: 4px; border-radius: 4px;
font-family: inherit;
font-size: inherit;
height: 2rem; height: 2rem;
padding: 0;
display: flex; display: flex;
align-items: center; align-items: center;
text-align: inherit;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
width: 100%;
& > div { justify-content: space-between;
flex-grow: 1;
}
& > p { & > p {
display: inline; display: inline;

View file

@ -29,8 +29,18 @@ button#toc {
list-style: none; list-style: none;
overflow: hidden; overflow: hidden;
max-height: none; max-height: none;
transition: max-height 0.5s ease; transition:
max-height 0.5s ease,
visibility 0s linear 0s;
position: relative; position: relative;
visibility: visible;
&.collapsed {
transition:
max-height 0.5s ease,
visibility 0s linear 0.5s;
visibility: hidden;
}
&.collapsed > .overflow::after { &.collapsed > .overflow::after {
opacity: 0; opacity: 0;

View file

@ -17,7 +17,7 @@ const defaultOptions: Options = {
csl: "apa", csl: "apa",
} }
export const Citations: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => { export const Citations: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "Citations", name: "Citations",
@ -38,7 +38,7 @@ export const Citations: QuartzTransformerPlugin<Partial<Options> | undefined> =
// using https://github.com/syntax-tree/unist-util-visit as they're just anochor links // using https://github.com/syntax-tree/unist-util-visit as they're just anochor links
plugins.push(() => { plugins.push(() => {
return (tree, _file) => { return (tree, _file) => {
visit(tree, "element", (node, index, parent) => { visit(tree, "element", (node, _index, _parent) => {
if (node.tagName === "a" && node.properties?.href?.startsWith("#bib")) { if (node.tagName === "a" && node.properties?.href?.startsWith("#bib")) {
node.properties["data-no-popover"] = true node.properties["data-no-popover"] = true
} }

View file

@ -18,7 +18,7 @@ const urlRegex = new RegExp(
"g", "g",
) )
export const Description: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => { export const Description: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "Description", name: "Description",

View file

@ -40,7 +40,7 @@ function coerceToArray(input: string | string[]): string[] | undefined {
.map((tag: string | number) => tag.toString()) .map((tag: string | number) => tag.toString())
} }
export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => { export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "FrontMatter", name: "FrontMatter",

View file

@ -14,9 +14,7 @@ const defaultOptions: Options = {
linkHeadings: true, linkHeadings: true,
} }
export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = ( export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "GitHubFlavoredMarkdown", name: "GitHubFlavoredMarkdown",

View file

@ -27,9 +27,7 @@ function coerceDate(fp: string, d: any): Date {
} }
type MaybeDate = undefined | string | number type MaybeDate = undefined | string | number
export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | undefined> = ( export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "CreatedModifiedDate", name: "CreatedModifiedDate",

View file

@ -5,10 +5,16 @@ import { QuartzTransformerPlugin } from "../types"
interface Options { interface Options {
renderEngine: "katex" | "mathjax" renderEngine: "katex" | "mathjax"
customMacros: MacroType
} }
export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => { interface MacroType {
[key: string]: string
}
export const Latex: QuartzTransformerPlugin<Partial<Options>> = (opts) => {
const engine = opts?.renderEngine ?? "katex" const engine = opts?.renderEngine ?? "katex"
const macros = opts?.customMacros ?? {}
return { return {
name: "Latex", name: "Latex",
markdownPlugins() { markdownPlugins() {
@ -16,9 +22,9 @@ export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => {
}, },
htmlPlugins() { htmlPlugins() {
if (engine === "katex") { if (engine === "katex") {
return [[rehypeKatex, { output: "html" }]] return [[rehypeKatex, { output: "html", macros }]]
} else { } else {
return [rehypeMathjax] return [[rehypeMathjax, { macros }]]
} }
}, },
externalResources() { externalResources() {

View file

@ -8,7 +8,6 @@ import {
simplifySlug, simplifySlug,
splitAnchor, splitAnchor,
transformLink, transformLink,
joinSegments,
} from "../../util/path" } from "../../util/path"
import path from "path" import path from "path"
import { visit } from "unist-util-visit" import { visit } from "unist-util-visit"
@ -33,7 +32,7 @@ const defaultOptions: Options = {
externalLinkIcon: true, externalLinkIcon: true,
} }
export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => { export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "LinkProcessing", name: "LinkProcessing",

View file

@ -136,9 +136,7 @@ const wikilinkImageEmbedRegex = new RegExp(
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/, /^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
) )
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = ( export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
const mdastToHtml = (ast: PhrasingContent | Paragraph) => { const mdastToHtml = (ast: PhrasingContent | Paragraph) => {
@ -263,7 +261,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
} else if ([".pdf"].includes(ext)) { } else if ([".pdf"].includes(ext)) {
return { return {
type: "html", type: "html",
value: `<iframe src="${url}"></iframe>`, value: `<iframe src="${url}" class="pdf"></iframe>`,
} }
} else { } else {
const block = anchor const block = anchor
@ -616,11 +614,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
// YouTube video (with optional playlist) // YouTube video (with optional playlist)
node.tagName = "iframe" node.tagName = "iframe"
node.properties = { node.properties = {
class: "external-embed", class: "external-embed youtube",
allow: "fullscreen", allow: "fullscreen",
frameborder: 0, frameborder: 0,
width: "600px", width: "600px",
height: "350px",
src: playlistId src: playlistId
? `https://www.youtube.com/embed/${videoId}?list=${playlistId}` ? `https://www.youtube.com/embed/${videoId}?list=${playlistId}`
: `https://www.youtube.com/embed/${videoId}`, : `https://www.youtube.com/embed/${videoId}`,
@ -629,11 +626,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
// YouTube playlist only. // YouTube playlist only.
node.tagName = "iframe" node.tagName = "iframe"
node.properties = { node.properties = {
class: "external-embed", class: "external-embed youtube",
allow: "fullscreen", allow: "fullscreen",
frameborder: 0, frameborder: 0,
width: "600px", width: "600px",
height: "350px",
src: `https://www.youtube.com/embed/videoseries?list=${playlistId}`, src: `https://www.youtube.com/embed/videoseries?list=${playlistId}`,
} }
} }

View file

@ -47,9 +47,7 @@ const quartzLatexRegex = new RegExp(/\$\$[\s\S]*?\$\$|\$.*?\$/, "g")
* markdown to make it compatible with quartz but the list of changes applied it * markdown to make it compatible with quartz but the list of changes applied it
* is not exhaustive. * is not exhaustive.
* */ * */
export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = ( export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "OxHugoFlavouredMarkdown", name: "OxHugoFlavouredMarkdown",

View file

@ -19,10 +19,8 @@ const defaultOptions: Options = {
keepBackground: false, keepBackground: false,
} }
export const SyntaxHighlighting: QuartzTransformerPlugin<Options> = ( export const SyntaxHighlighting: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts?: Partial<Options>, const opts: CodeOptions = { ...defaultOptions, ...userOpts }
) => {
const opts: Partial<CodeOptions> = { ...defaultOptions, ...userOpts }
return { return {
name: "SyntaxHighlighting", name: "SyntaxHighlighting",

View file

@ -25,9 +25,7 @@ interface TocEntry {
} }
const slugAnchor = new Slugger() const slugAnchor = new Slugger()
export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefined> = ( export const TableOfContents: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
return { return {
name: "TableOfContents", name: "TableOfContents",

View file

@ -143,7 +143,7 @@ export async function parseMarkdown(ctx: BuildCtx, fps: FilePath[]): Promise<Pro
const childPromises: WorkerPromise<ProcessedContent[]>[] = [] const childPromises: WorkerPromise<ProcessedContent[]>[] = []
for (const chunk of chunks(fps, CHUNK_SIZE)) { for (const chunk of chunks(fps, CHUNK_SIZE)) {
childPromises.push(pool.exec("parseFiles", [argv, chunk, ctx.allSlugs])) childPromises.push(pool.exec("parseFiles", [ctx.buildId, argv, chunk, ctx.allSlugs]))
} }
const results: ProcessedContent[][] = await WorkerPromise.all(childPromises).catch((err) => { const results: ProcessedContent[][] = await WorkerPromise.all(childPromises).catch((err) => {

View file

@ -541,3 +541,11 @@ ol.overflow {
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
} }
.external-embed.youtube,
iframe.pdf {
aspect-ratio: 16 / 9;
height: 100%;
width: 100%;
border-radius: 5px;
}

View file

@ -14,6 +14,7 @@ export interface Argv {
} }
export interface BuildCtx { export interface BuildCtx {
buildId: string
argv: Argv argv: Argv
cfg: QuartzConfig cfg: QuartzConfig
allSlugs: FullSlug[] allSlugs: FullSlug[]

View file

@ -7,8 +7,14 @@ import { createFileParser, createProcessor } from "./processors/parse"
import { options } from "./util/sourcemap" import { options } from "./util/sourcemap"
// only called from worker thread // only called from worker thread
export async function parseFiles(argv: Argv, fps: FilePath[], allSlugs: FullSlug[]) { export async function parseFiles(
buildId: string,
argv: Argv,
fps: FilePath[],
allSlugs: FullSlug[],
) {
const ctx: BuildCtx = { const ctx: BuildCtx = {
buildId,
cfg, cfg,
argv, argv,
allSlugs, allSlugs,