diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2724ae9f4..cc3c8070a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - name: Cache dependencies uses: actions/cache@v4 @@ -59,7 +59,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - name: Get package version run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV - name: Create release tag diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..805b5a4e0 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +v20.9.0 diff --git a/docs/configuration.md b/docs/configuration.md index 556668210..7f9eee8a5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -52,6 +52,7 @@ This part of the configuration concerns anything that can affect the whole site. - `secondary`: link colour, current [[graph view|graph]] node - `tertiary`: hover states and visited [[graph view|graph]] nodes - `highlight`: internal link background, highlighted text, [[syntax highlighting|highlighted lines of code]] + - `textHighlight`: markdown highlighted text background ## Plugins diff --git a/docs/features/comments.md b/docs/features/comments.md new file mode 100644 index 000000000..92ea754b1 --- /dev/null +++ b/docs/features/comments.md @@ -0,0 +1,83 @@ +--- +title: Comments +tags: + - component +--- + +Quartz also has the ability to hook into various providers to enable readers to leave comments on your site. + +![[giscus-example.png]] + +As of today, only [Giscus](https://giscus.app/) is supported out of the box but PRs to support other providers are welcome! + +## Providers + +### Giscus + +First, make sure that the [[setting up your GitHub repository|GitHub]] repository you are using for your Quartz meets the following requirements: + +1. The **repository is [public](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/setting-repository-visibility#making-a-repository-public)**, otherwise visitors will not be able to view the discussion. +2. The **[giscus](https://github.com/apps/giscus) app is installed**, otherwise visitors will not be able to comment and react. +3. The **Discussions feature is turned on** by [enabling it for your repository](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/enabling-or-disabling-github-discussions-for-a-repository). + +Then, use the [Giscus site](https://giscus.app/#repository) to figure out what your `repoId` and `categoryId` should be. Make sure you select `Announcements` for the Discussion category. + +![[giscus-repo.png]] + +![[giscus-discussion.png]] + +After entering both your repository and selecting the discussion category, Giscus will compute some IDs that you'll need to provide back to Quartz. You won't need to manually add the script yourself as Quartz will handle that part for you but will need these values in the next step! + +![[giscus-results.png]] + +Finally, in `quartz.layout.ts`, edit the `afterBody` field of `sharedPageComponents` to include the following options but with the values you got from above: + +```ts title="quartz.layout.ts" +afterBody: [ + Component.Comments({ + provider: 'giscus', + options: { + // from data-repo + repo: 'jackyzha0/quartz', + // from data-repo-id + repoId: 'MDEwOlJlcG9zaXRvcnkzODcyMTMyMDg', + // from data-category + category: 'Announcements', + // from data-category-id + categoryId: 'DIC_kwDOFxRnmM4B-Xg6', + } + }), +], +``` + +### Customization + +Quartz also exposes a few of the other Giscus options as well and you can provide them the same way `repo`, `repoId`, `category`, and `categoryId` are provided. + +```ts +type Options = { + provider: "giscus" + options: { + repo: `${string}/${string}` + repoId: string + category: string + categoryId: string + + // how to map pages -> discussions + // defaults to 'url' + mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname" + + // use strict title matching + // defaults to true + strict?: boolean + + // whether to enable reactions for the main post + // defaults to true + reactionsEnabled?: boolean + + // where to put the comment input box relative to the comments + // defaults to 'bottom' + inputPosition?: "top" | "bottom" + } +} +``` diff --git a/docs/features/folder and tag listings.md b/docs/features/folder and tag listings.md index d330f1479..3190709d3 100644 --- a/docs/features/folder and tag listings.md +++ b/docs/features/folder and tag listings.md @@ -30,4 +30,4 @@ As with folder listings, you can also provide a description and title for a tag ## Customization -The folder listings are a functionality of the [[FolderPage]] plugin, the tag listings of the [[TagPage]] plugin. See the plugin pages for customization options. +Quartz allows you to define a custom sort ordering for content on both page types. The folder listings are a functionality of the [[FolderPage]] plugin, the tag listings of the [[TagPage]] plugin. See the plugin pages for customization options. diff --git a/docs/features/upcoming features.md b/docs/features/upcoming features.md index 76adda00e..11d9cbdb9 100644 --- a/docs/features/upcoming features.md +++ b/docs/features/upcoming features.md @@ -2,22 +2,12 @@ draft: true --- -## high priority backlog - -- static dead link detection -- block links: https://help.obsidian.md/Linking+notes+and+files/Internal+links#Link+to+a+block+in+a+note -- note/header/block transcludes: https://help.obsidian.md/Linking+notes+and+files/Embedding+files -- docker support - ## misc backlog -- breadcrumbs component +- static dead link detection - cursor chat extension - https://giscus.app/ extension - sidenotes? https://github.com/capnfabs/paperesque - direct match in search using double quotes - https://help.obsidian.md/Advanced+topics/Using+Obsidian+URI -- audio/video embed styling - Canvas -- parse all images in page: use this for page lists if applicable? -- CV mode? with print stylesheet diff --git a/docs/hosting.md b/docs/hosting.md index 128c0d4fd..6067512d6 100644 --- a/docs/hosting.md +++ b/docs/hosting.md @@ -58,6 +58,8 @@ jobs: with: fetch-depth: 0 # Fetch all history for git info - uses: actions/setup-node@v4 + with: + node-version: 22 - name: Install Dependencies run: npm ci - name: Build Quartz @@ -180,7 +182,7 @@ stages: - build - deploy -image: node:18 +image: node:20 cache: # Cache modules in between jobs key: $CI_COMMIT_REF_SLUG paths: diff --git a/docs/images/giscus-discussion.png b/docs/images/giscus-discussion.png new file mode 100644 index 000000000..939af624f Binary files /dev/null and b/docs/images/giscus-discussion.png differ diff --git a/docs/images/giscus-example.png b/docs/images/giscus-example.png new file mode 100644 index 000000000..f59f52ba1 Binary files /dev/null and b/docs/images/giscus-example.png differ diff --git a/docs/images/giscus-repo.png b/docs/images/giscus-repo.png new file mode 100644 index 000000000..bfabc5692 Binary files /dev/null and b/docs/images/giscus-repo.png differ diff --git a/docs/images/giscus-results.png b/docs/images/giscus-results.png new file mode 100644 index 000000000..b25c75158 Binary files /dev/null and b/docs/images/giscus-results.png differ diff --git a/docs/images/quartz layout.png b/docs/images/quartz layout.png index 03435f7d5..71ef3ac71 100644 Binary files a/docs/images/quartz layout.png and b/docs/images/quartz layout.png differ diff --git a/docs/index.md b/docs/index.md index c6e5243e6..89f3bf8af 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,7 +6,7 @@ Quartz is a fast, batteries-included static-site generator that transforms Markd ## 🪴 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: @@ -30,7 +30,7 @@ If you prefer instructions in a video format you can try following Nicole van de ## 🔧 Features -- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]] and [many more](./features) right out of the box +- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]], [[comments]] and [many more](./features) right out of the box - Hot-reload for both configuration and content - Simple JSX layouts and [[creating components|page components]] - [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes diff --git a/docs/layout.md b/docs/layout.md index 8822c73ac..28023d526 100644 --- a/docs/layout.md +++ b/docs/layout.md @@ -12,6 +12,7 @@ export interface FullPageLayout { header: QuartzComponent[] // laid out horizontally beforeBody: QuartzComponent[] // laid out vertically pageBody: QuartzComponent // single component + afterBody: QuartzComponent[] // laid out vertically left: QuartzComponent[] // vertical on desktop, horizontal on mobile right: QuartzComponent[] // vertical on desktop, horizontal on mobile footer: QuartzComponent // single component diff --git a/docs/plugins/CreatedModifiedDate.md b/docs/plugins/CreatedModifiedDate.md index ef846ccff..7e21782ed 100644 --- a/docs/plugins/CreatedModifiedDate.md +++ b/docs/plugins/CreatedModifiedDate.md @@ -10,7 +10,7 @@ This plugin determines the created, modified, and published dates for a document This plugin accepts the following configuration options: -- `priority`: The data sources to consult for date information. Highest priority first. Possible values are `"frontmatter"`, `"git"`, and `"filesystem"`. Defaults to `"frontmatter", "git", "filesystem"]`. +- `priority`: The data sources to consult for date information. Highest priority first. Possible values are `"frontmatter"`, `"git"`, and `"filesystem"`. Defaults to `["frontmatter", "git", "filesystem"]`. > [!warning] If you rely on `git` for dates, make sure `defaultDateType` is set to `modified` in `quartz.config.ts`. > diff --git a/docs/plugins/FolderPage.md b/docs/plugins/FolderPage.md index acf083093..f006dab71 100644 --- a/docs/plugins/FolderPage.md +++ b/docs/plugins/FolderPage.md @@ -10,10 +10,12 @@ Example: [[advanced/|Advanced]] > [!note] For information on how to add, remove or configure plugins, see the [[Configuration#Plugins|Configuration]] page. -This plugin has no configuration options. - The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `FolderContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/FolderContent.tsx`). +This plugin accepts the following configuration options: + +- `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number{:ts}` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order. + ## API - Category: Emitter diff --git a/docs/plugins/Latex.md b/docs/plugins/Latex.md index 5dd3facf2..8373c78a3 100644 --- a/docs/plugins/Latex.md +++ b/docs/plugins/Latex.md @@ -11,6 +11,7 @@ This plugin adds LaTeX support to Quartz. See [[features/Latex|Latex]] for more 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. +- `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 diff --git a/docs/plugins/TagPage.md b/docs/plugins/TagPage.md index 2ad6d8e52..4c900fa6f 100644 --- a/docs/plugins/TagPage.md +++ b/docs/plugins/TagPage.md @@ -8,10 +8,12 @@ This plugin emits dedicated pages for each tag used in the content. See [[folder > [!note] For information on how to add, remove or configure plugins, see the [[Configuration#Plugins|Configuration]] page. -This plugin has no configuration options. - The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `TagContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/TagContent.tsx`). +This plugin accepts the following configuration options: + +- `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number{:ts}` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order. + ## API - Category: Emitter diff --git a/docs/showcase.md b/docs/showcase.md index 9cff3a5f7..5fd71336a 100644 --- a/docs/showcase.md +++ b/docs/showcase.md @@ -7,27 +7,24 @@ Want to see what Quartz can do? Here are some cool community gardens: - [Quartz Documentation (this site!)](https://quartz.jzhao.xyz/) - [Jacky Zhao's Garden](https://jzhao.xyz/) - [Socratica Toolbox](https://toolbox.socratica.info/) -- [oldwinter の数字花园](https://garden.oldwinter.top/) +- [Morrowind Modding Wiki](https://morrowind-modding.github.io/) - [Aaron Pham's Garden](https://aarnphm.xyz/) -- [The Quantum Garden](https://quantumgardener.blog/) -- [Abhijeet's Math Wiki](https://abhmul.github.io/quartz/Math-Wiki/) -- [Matt Dunn's Second Brain](https://mattdunn.info/) -- [Pelayo Arbues' Notes](https://pelayoarbues.github.io/) -- [Vince Imbat's Talahardin](https://vinceimbat.com/) +- [Pelayo Arbues' Notes](https://pelayoarbues.com/) +- [Stanford CME 302 Numerical Linear Algebra](https://ericdarve.github.io/NLA/) +- [A Pattern Language - Christopher Alexander (Architecture)](https://patternlanguage.cc/) +- [oldwinter の数字花园](https://garden.oldwinter.top/) +- [Eilleen's Everything Notebook](https://quartz.eilleeenz.com/) - [🧠🌳 Chad's Mind Garden](https://www.chadly.net/) - [Pedro MC Fernandes's Topo da Mente](https://www.pmcf.xyz/topo-da-mente/) - [Mau Camargo's Notkesto](https://notes.camargomau.com/) -- [Caicai's Novels](https://imoko.cc/blog/caicai/) -- [🌊 Collapsed Wave](https://collapsedwave.com/) - [Sideny's 3D Artist's Handbook](https://sidney-eliot.github.io/3d-artists-handbook/) -- [Mike's AI Garden 🤖🪴](https://mwalton.me/) - [Brandon Boswell's Garden](https://brandonkboswell.com) - [Scaling Synthesis - A hypertext research notebook](https://scalingsynthesis.com/) - [Data Dictionary 🧠](https://glossary.airbyte.com/) - [sspaeti.com's Second Brain](https://brain.sspaeti.com/) - [🪴Aster's notebook](https://notes.asterhu.com) -- [🥷🏻🌳🍃 Computer Science & Thinkering Garden](https://notes.yxy.ninja) -- [A Pattern Language - Christopher Alexander (Architecture)](https://patternlanguage.cc/) - [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)! diff --git a/package-lock.json b/package-lock.json index 40d2c43c4..bc04b624a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jackyzha0/quartz", - "version": "4.2.3", + "version": "4.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jackyzha0/quartz", - "version": "4.2.3", + "version": "4.3.0", "license": "MIT", "bin": { "quartz": "quartz/bootstrap-cli.mjs" @@ -83,7 +83,7 @@ "yargs": "^17.7.2" }, "engines": { - "node": ">=18.14", + "node": "20 || >=22", "npm": ">=9.3.1" } }, @@ -1243,7 +1243,7 @@ "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.13.0" } }, "node_modules/@types/pretty-time": { @@ -1268,9 +1268,9 @@ "dev": true }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -3322,7 +3322,7 @@ "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3674,7 +3674,7 @@ "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", "dev": true, "engines": { - "node": "14 || >=16.14" + "node": "20 || >=22" } }, "node_modules/markdown-table": { @@ -4688,7 +4688,7 @@ "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4727,7 +4727,7 @@ "integrity": "sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==", "dev": true, "dependencies": { - "@types/nlcst": "^1.0.0" + "@types/nlcst": "^2.0.0" }, "funding": { "type": "opencollective", @@ -4801,15 +4801,23 @@ "integrity": "sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==", "dev": true, "dependencies": { - "nlcst-to-string": "^3.0.0", - "unist-util-modify-children": "^3.0.0", - "unist-util-visit-children": "^2.0.0" + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse-latin/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, "node_modules/parse-numeric-range": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", @@ -4921,9 +4929,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -5297,12 +5305,13 @@ "integrity": "sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw==", "dev": true, "dependencies": { - "retext": "^8.1.0", - "retext-smartypants": "^5.2.0", + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", "unist-util-visit": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=16.0.0" } }, "node_modules/remark-stringify": { @@ -5376,10 +5385,10 @@ "integrity": "sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==", "dev": true, "dependencies": { - "@types/nlcst": "^1.0.0", - "retext-latin": "^3.0.0", - "retext-stringify": "^3.0.0", - "unified": "^10.0.0" + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", @@ -5446,10 +5455,9 @@ "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", @@ -5572,10 +5580,9 @@ "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", @@ -5723,10 +5730,9 @@ "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", @@ -5769,13 +5775,14 @@ "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", "dev": true, "dependencies": { - "glob": "^10.3.7" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6303,9 +6310,9 @@ "dev": true }, "node_modules/tsx": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.0.tgz", - "integrity": "sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ==", + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.2.tgz", + "integrity": "sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==", "dev": true, "dependencies": { "esbuild": "~0.21.5", @@ -6741,9 +6748,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true }, "node_modules/unherit": { @@ -6820,7 +6827,7 @@ "integrity": "sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", + "@types/unist": "^3.0.0", "array-iterate": "^2.0.0" }, "funding": { @@ -6895,7 +6902,7 @@ "integrity": "sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", diff --git a/package.json b/package.json index 266bc2116..22a293887 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@jackyzha0/quartz", "description": "🌱 publish your digital garden and notes as a website", "private": true, - "version": "4.2.3", + "version": "4.3.0", "type": "module", "author": "jackyzha0 ", "license": "MIT", @@ -25,7 +25,7 @@ }, "engines": { "npm": ">=9.3.1", - "node": ">=18.14" + "node": "20 || >=22" }, "keywords": [ "site generator", diff --git a/quartz.layout.ts b/quartz.layout.ts index 70ae3eac4..2879eef8e 100644 --- a/quartz.layout.ts +++ b/quartz.layout.ts @@ -6,6 +6,7 @@ export const sharedPageComponents: SharedLayout = { head: Component.Head(), // header: [Component.PageTitle(), Component.Search(), Component.Darkmode()], header: [], + afterBody: [], footer: Component.Footer({ links: { About: "/About", diff --git a/quartz/build.ts b/quartz/build.ts index d43cf35b7..e47be9b6e 100644 --- a/quartz/build.ts +++ b/quartz/build.ts @@ -38,8 +38,13 @@ type BuildData = { type FileEvent = "add" | "change" | "delete" +function newBuildId() { + return new Date().toISOString() +} + async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) { const ctx: BuildCtx = { + buildId: newBuildId(), argv, cfg, allSlugs: [], @@ -187,6 +192,7 @@ async function partialRebuildFromEntrypoint( const perf = new PerfTimer() console.log(chalk.yellow("Detected change, rebuilding...")) + ctx.buildId = newBuildId() // UPDATE DEP GRAPH const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath @@ -412,6 +418,8 @@ async function rebuildFromEntrypoint( const perf = new PerfTimer() console.log(chalk.yellow("Detected change, rebuilding...")) + ctx.buildId = newBuildId() + try { const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp)) @@ -437,6 +445,13 @@ async function rebuildFromEntrypoint( const parsedFiles = [...contentMap.values()] 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 // instead of just deleting everything await rimraf(path.join(argv.output, ".*"), {glob: true}) diff --git a/quartz/cfg.ts b/quartz/cfg.ts index fd01e7098..5135d5c23 100644 --- a/quartz/cfg.ts +++ b/quartz/cfg.ts @@ -77,10 +77,11 @@ export interface FullPageLayout { header: QuartzComponent[] beforeBody: QuartzComponent[] pageBody: QuartzComponent + afterBody: QuartzComponent[] left: QuartzComponent[] right: QuartzComponent[] footer: QuartzComponent } export type PageLayout = Pick -export type SharedLayout = Pick +export type SharedLayout = Pick diff --git a/quartz/components/Comments.tsx b/quartz/components/Comments.tsx new file mode 100644 index 000000000..8e4494026 --- /dev/null +++ b/quartz/components/Comments.tsx @@ -0,0 +1,44 @@ +import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { classNames } from "../util/lang" +// @ts-ignore +import script from "./scripts/comments.inline" + +type Options = { + provider: "giscus" + options: { + repo: `${string}/${string}` + repoId: string + category: string + categoryId: string + mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname" + strict?: boolean + reactionsEnabled?: boolean + inputPosition?: "top" | "bottom" + } +} + +function boolToStringBool(b: boolean): string { + return b ? "1" : "0" +} + +export default ((opts: Options) => { + const Comments: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => { + return ( +
+ ) + } + + Comments.afterDOMLoaded = script + + return Comments +}) satisfies QuartzComponentConstructor diff --git a/quartz/components/Explorer.tsx b/quartz/components/Explorer.tsx index 54154e48b..250798d38 100644 --- a/quartz/components/Explorer.tsx +++ b/quartz/components/Explorer.tsx @@ -48,12 +48,9 @@ export default ((userOpts?: Partial) => { // memoized let fileTree: FileNode let jsonTree: string + let lastBuildId: string = "" function constructFileTree(allFiles: QuartzPluginData[]) { - if (fileTree) { - return - } - // Construct tree from allFiles fileTree = new FileNode("") allFiles.forEach((file) => fileTree.add(file)) @@ -82,12 +79,17 @@ export default ((userOpts?: Partial) => { } const Explorer: QuartzComponent = ({ + ctx, cfg, allFiles, displayClass, fileData, }: QuartzComponentProps) => { - constructFileTree(allFiles) + if (ctx.buildId !== lastBuildId) { + lastBuildId = ctx.buildId + constructFileTree(allFiles) + } + return (
+
) => { const classes = ["popover-hint", ...cssClasses].join(" ") const listProps = { ...props, + sort: options.sort, allFiles: allPagesInFolder, } diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx index c83e973bf..d953bd126 100644 --- a/quartz/components/pages/TagContent.tsx +++ b/quartz/components/pages/TagContent.tsx @@ -96,18 +96,17 @@ const TagContent: QuartzComponent = (props: QuartzComponentProps) => {

-
- ) - })} + ) + })} + - - ) - } else { - const pages = allPagesWithTag(tag) - const listProps = { - ...props, - allFiles: pages, - } + ) + } else { + const pages = allPagesWithTag(tag) + const listProps = { + ...props, + allFiles: pages, + } return (
@@ -122,10 +121,10 @@ const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
- - ) + ) + } } -} -TagContent.css = style + PageList.css -export default (() => TagContent) satisfies QuartzComponentConstructor + TagContent.css = style + PageList.css + return TagContent +}) satisfies QuartzComponentConstructor diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx index 9a033e44e..7d4b3856b 100644 --- a/quartz/components/renderPage.tsx +++ b/quartz/components/renderPage.tsx @@ -20,6 +20,7 @@ interface RenderComponents { header: QuartzComponent[] beforeBody: QuartzComponent[] pageBody: QuartzComponent + afterBody: QuartzComponent[] left: QuartzComponent[] right: QuartzComponent[] footer: QuartzComponent @@ -217,6 +218,7 @@ export function renderPage( header, beforeBody, pageBody: Content, + afterBody, left, right, footer: Footer, @@ -265,6 +267,12 @@ export function renderPage( +
+ {RightComponent} diff --git a/quartz/components/scripts/comments.inline.ts b/quartz/components/scripts/comments.inline.ts new file mode 100644 index 000000000..4ab29f087 --- /dev/null +++ b/quartz/components/scripts/comments.inline.ts @@ -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 & { + 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)) +}) diff --git a/quartz/components/scripts/explorer.inline.ts b/quartz/components/scripts/explorer.inline.ts index ac945312e..c989a28d7 100644 --- a/quartz/components/scripts/explorer.inline.ts +++ b/quartz/components/scripts/explorer.inline.ts @@ -17,6 +17,10 @@ const observer = new IntersectionObserver((entries) => { function toggleExplorer(this: HTMLElement) { this.classList.toggle("collapsed") + this.setAttribute( + "aria-expanded", + this.getAttribute("aria-expanded") === "true" ? "false" : "true", + ) const content = this.nextElementSibling as MaybeHTMLElement if (!content) return diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts index 3afb02b31..72d7eaa31 100644 --- a/quartz/components/scripts/popover.inline.ts +++ b/quartz/components/scripts/popover.inline.ts @@ -33,7 +33,7 @@ async function mouseEnterHandler( thisUrl.hash = "" thisUrl.search = "" const targetUrl = new URL(link.href) - const hash = targetUrl.hash + const hash = decodeURIComponent(targetUrl.hash) targetUrl.hash = "" targetUrl.search = "" diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index c868208f8..3e0437dc1 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -209,6 +209,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { } searchType = "basic" // reset search type after closing + + searchButton?.focus() } function showSearch(searchTypeNew: SearchType) { diff --git a/quartz/components/scripts/toc.inline.ts b/quartz/components/scripts/toc.inline.ts index 087cea51c..8c89fe370 100644 --- a/quartz/components/scripts/toc.inline.ts +++ b/quartz/components/scripts/toc.inline.ts @@ -16,6 +16,10 @@ const observer = new IntersectionObserver((entries) => { function toggleToc(this: HTMLElement) { this.classList.toggle("collapsed") + this.setAttribute( + "aria-expanded", + this.getAttribute("aria-expanded") === "true" ? "false" : "true", + ) const content = this.nextElementSibling as HTMLElement | undefined if (!content) return content.classList.toggle("collapsed") diff --git a/quartz/components/scripts/util.ts b/quartz/components/scripts/util.ts index be4b81692..a8945b7a7 100644 --- a/quartz/components/scripts/util.ts +++ b/quartz/components/scripts/util.ts @@ -6,6 +6,7 @@ export function registerEscapeHandler( function click(this: HTMLElement, e: HTMLElementEventMap["click"]) { if (e.target !== this) return e.preventDefault() + e.stopPropagation() cb() } diff --git a/quartz/components/styles/explorer.scss b/quartz/components/styles/explorer.scss index 55ea8aa88..2f94b158c 100644 --- a/quartz/components/styles/explorer.scss +++ b/quartz/components/styles/explorer.scss @@ -1,7 +1,6 @@ @use "../../styles/variables.scss" as *; button#explorer { - all: unset; background-color: transparent; border: none; text-align: left; @@ -11,7 +10,7 @@ button#explorer { display: flex; align-items: center; - & h1 { + & h2 { font-size: 1rem; display: inline-block; margin: 0; @@ -46,8 +45,18 @@ button#explorer { list-style: none; overflow: hidden; max-height: none; - transition: max-height 0.35s ease; + transition: + max-height 0.35s ease, + visibility 0s linear 0s; margin-top: 0.5rem; + visibility: visible; + + &.collapsed { + transition: + max-height 0.35s ease, + visibility 0s linear 0.35s; + visibility: hidden; + } &.collapsed > .overflow::after { opacity: 0; diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss index fb36803c0..3aadcfba5 100644 --- a/quartz/components/styles/search.scss +++ b/quartz/components/styles/search.scss @@ -5,18 +5,21 @@ max-width: 14rem; flex-grow: 0.3; - & > #search-icon { + & > .search-button { background-color: var(--lightgray); + border: none; border-radius: 4px; + font-family: inherit; + font-size: inherit; height: 2rem; + padding: 0; display: flex; align-items: center; + text-align: inherit; cursor: pointer; white-space: nowrap; - - & > div { - flex-grow: 1; - } + width: 100%; + justify-content: space-between; & > p { display: inline; diff --git a/quartz/components/styles/toc.scss b/quartz/components/styles/toc.scss index 27ff62a40..6845812f5 100644 --- a/quartz/components/styles/toc.scss +++ b/quartz/components/styles/toc.scss @@ -29,8 +29,18 @@ button#toc { list-style: none; overflow: hidden; max-height: none; - transition: max-height 0.5s ease; + transition: + max-height 0.5s ease, + visibility 0s linear 0s; position: relative; + visibility: visible; + + &.collapsed { + transition: + max-height 0.5s ease, + visibility 0s linear 0.5s; + visibility: hidden; + } &.collapsed > .overflow::after { opacity: 0; diff --git a/quartz/i18n/index.ts b/quartz/i18n/index.ts index 0ec91c94a..6aaddbe9d 100644 --- a/quartz/i18n/index.ts +++ b/quartz/i18n/index.ts @@ -20,8 +20,8 @@ import fa from "./locales/fa-IR" import pl from "./locales/pl-PL" export const TRANSLATIONS = { - "en-US": en, - "en-GB": en, + "en-US": enUs, + "en-GB": enGb, "fr-FR": fr, "it-IT": it, "ja-JP": ja, diff --git a/quartz/plugins/emitters/folderPage.tsx b/quartz/plugins/emitters/folderPage.tsx index 02cfdfc9f..57f581bf9 100644 --- a/quartz/plugins/emitters/folderPage.tsx +++ b/quartz/plugins/emitters/folderPage.tsx @@ -30,7 +30,7 @@ export const FolderPage: QuartzEmitterPlugin> = ( const opts: FullPageLayout = { ...sharedPageComponents, ...defaultListPageLayout, - pageBody: FolderContent(), + pageBody: FolderContent({ sort: userOpts?.sort }), ...userOpts, } diff --git a/quartz/plugins/emitters/tagPage.tsx b/quartz/plugins/emitters/tagPage.tsx index 6c1eb22df..35795c66a 100644 --- a/quartz/plugins/emitters/tagPage.tsx +++ b/quartz/plugins/emitters/tagPage.tsx @@ -27,7 +27,7 @@ export const TagPage: QuartzEmitterPlugin> = ( const opts: FullPageLayout = { ...sharedPageComponents, ...defaultListPageLayout, - pageBody: TagContent(), + pageBody: TagContent({ sort: userOpts?.sort }), ...userOpts, } diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts index adc53ef66..7bfa445e5 100644 --- a/quartz/plugins/index.ts +++ b/quartz/plugins/index.ts @@ -30,10 +30,10 @@ export function getStaticResourcesFromPlugins(ctx: BuildCtx) { loadTime: "afterDOMReady", contentType: "inline", script: ` - const socket = new WebSocket('${wsUrl}') - // reload(true) ensures resources like images and scripts are fetched again in firefox - socket.addEventListener('message', () => document.location.reload(true)) - `, + const socket = new WebSocket('${wsUrl}') + // reload(true) ensures resources like images and scripts are fetched again in firefox + socket.addEventListener('message', () => document.location.reload(true)) + `, }) } diff --git a/quartz/plugins/transformers/latex.ts b/quartz/plugins/transformers/latex.ts index 626b188a8..7d4fcbe1d 100644 --- a/quartz/plugins/transformers/latex.ts +++ b/quartz/plugins/transformers/latex.ts @@ -5,10 +5,16 @@ import {QuartzTransformerPlugin} from "../types" interface Options { renderEngine: "katex" | "mathjax" + customMacros: MacroType } -export const Latex: QuartzTransformerPlugin = (opts?: Options) => { +interface MacroType { + [key: string]: string +} + +export const Latex: QuartzTransformerPlugin> = (opts) => { const engine = opts?.renderEngine ?? "katex" + const macros = opts?.customMacros ?? {} return { name: "Latex", markdownPlugins() { @@ -18,7 +24,7 @@ export const Latex: QuartzTransformerPlugin = (opts?: Options) => { if (engine === "katex") { return [[rehypeKatex, {output: "html"}]] } else { - return [rehypeMathjax] + return [[rehypeMathjax, { macros }]] } }, externalResources() { diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts index 0f1eb644f..aae535cfe 100644 --- a/quartz/plugins/transformers/links.ts +++ b/quartz/plugins/transformers/links.ts @@ -8,7 +8,6 @@ import { simplifySlug, splitAnchor, transformLink, - joinSegments, } from "../../util/path" import path from "path" import {visit} from "unist-util-visit" @@ -68,6 +67,7 @@ export const CrawlLinks: QuartzTransformerPlugin< type: "element", tagName: "svg", properties: { + "aria-hidden": "true", class: "external-icon", viewBox: "0 0 512 512", }, diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 4eed3d4ad..379e1712f 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -109,7 +109,7 @@ function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping { export const externalLinkRegex = /^https?:\/\//i -export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/, "g") +export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/g) // !? -> optional embedding // \[\[ -> open brace @@ -117,33 +117,28 @@ export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/, "g") // (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link) // (\\?\|[^\[\]\#]+)? -> optional escape \ then | then one or more non-special characters (alias) export const wikilinkRegex = new RegExp( - /!?\[\[([^\[\]\|\#\\]+)?(#+[^\[\]\|\#\\]+)?(\\?\|[^\[\]\#]+)?\]\]/, - "g", + /!?\[\[([^\[\]\|\#\\]+)?(#+[^\[\]\|\#\\]+)?(\\?\|[^\[\]\#]+)?\]\]/g, ) // ^\|([^\n])+\|\n(\|) -> matches the header row // ( ?:?-{3,}:? ?\|)+ -> matches the header row separator // (\|([^\n])+\|\n)+ -> matches the body rows -export const tableRegex = new RegExp( - /^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/, - "gm", -) +export const tableRegex = new RegExp(/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/gm) // matches any wikilink, only used for escaping wikilinks inside tables -export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/, "g") +export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/g) -const highlightRegex = new RegExp(/==([^=]+)==/, "g") -const commentRegex = new RegExp(/%%[\s\S]*?%%/, "g") +const highlightRegex = new RegExp(/==([^=]+)==/g) +const commentRegex = new RegExp(/%%[\s\S]*?%%/g) // from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts const calloutRegex = new RegExp(/^\[\!(\w+)\|?(.+?)?\]([+-]?)/) -const calloutLineRegex = new RegExp(/^> *\[\!\w+\|?.*?\][+-]?.*$/, "gm") +const calloutLineRegex = new RegExp(/^> *\[\!\w+\|?.*?\][+-]?.*$/gm) // (?:^| ) -> non-capturing group, tag should start be separated by a space or be the start of the line // #(...) -> capturing group, tag itself must start with # // (?:[-_\p{L}\d\p{Z}])+ -> non-capturing group, non-empty string of (Unicode-aware) alpha-numeric characters and symbols, hyphens and/or underscores // (?:\/[-_\p{L}\d\p{Z}]+)*) -> non-capturing group, matches an arbitrary number of tag strings separated by "/" const tagRegex = new RegExp( - /(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/, - "gu", + /(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/gu, ) const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/, "g") const ytLinkRegex = @@ -199,8 +194,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin< // replace all wikilinks inside a table first src = src.replace(tableRegex, (value) => { // escape all aliases and headers in wikilinks inside a table - return value.replace(tableWikilinkRegex, (value, ...capture) => { - const [raw]: (string | undefined)[] = capture + return value.replace(tableWikilinkRegex, (_value, raw) => { + // const [raw]: (string | undefined)[] = capture let escaped = raw ?? "" escaped = escaped.replace("#", "\\#") // escape pipe characters if they are not already escaped @@ -305,7 +300,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin< } else if ([".pdf"].includes(ext)) { return { type: "html", - value: ``, + value: ``, } } else { const block = anchor @@ -689,11 +684,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin< // YouTube video (with optional playlist) node.tagName = "iframe" node.properties = { - class: "external-embed", + class: "external-embed youtube", allow: "fullscreen", frameborder: 0, width: "600px", - height: "350px", src: playlistId ? `https://www.youtube.com/embed/${videoId}?list=${playlistId}` : `https://www.youtube.com/embed/${videoId}`, @@ -702,11 +696,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin< // YouTube playlist only. node.tagName = "iframe" node.properties = { - class: "external-embed", + class: "external-embed youtube", allow: "fullscreen", frameborder: 0, width: "600px", - height: "350px", src: `https://www.youtube.com/embed/videoseries?list=${playlistId}`, } } diff --git a/quartz/processors/parse.ts b/quartz/processors/parse.ts index 55e27f610..e2aaee368 100644 --- a/quartz/processors/parse.ts +++ b/quartz/processors/parse.ts @@ -158,7 +158,7 @@ export async function parseMarkdown( const childPromises: WorkerPromise[] = [] 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( diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index 0c16462c7..f445b95a7 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -20,11 +20,10 @@ section { } .text-highlight { - background-color: #fff23688; + background-color: var(--textHighlight); padding: 0 0.1rem; border-radius: 5px; } - ::selection { background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0)); color: var(--darkgray); @@ -181,11 +180,19 @@ a { } } - & .page-header { + & .page-header, + & .page-footer { width: $pageWidth; - margin: $topSpacing auto 0 auto; + margin-top: 1rem; + @media all and (max-width: $fullPageWidth) { width: initial; + } + } + + & .page-header { + margin: $topSpacing auto 0 auto; + @media all and (max-width: $fullPageWidth) { margin-top: 2rem; } } diff --git a/quartz/util/ctx.ts b/quartz/util/ctx.ts index 607f3f689..40fbaf6f0 100644 --- a/quartz/util/ctx.ts +++ b/quartz/util/ctx.ts @@ -14,6 +14,7 @@ export interface Argv { } export interface BuildCtx { + buildId: string argv: Argv cfg: QuartzConfig allSlugs: FullSlug[] diff --git a/quartz/util/theme.ts b/quartz/util/theme.ts index 3bb77e1f5..9dac24b15 100644 --- a/quartz/util/theme.ts +++ b/quartz/util/theme.ts @@ -7,6 +7,7 @@ export interface ColorScheme { secondary: string tertiary: string highlight: string + textHighlight: string } interface Colors { @@ -49,6 +50,7 @@ ${stylesheet.join("\n\n")} --secondary: ${theme.colors.lightMode.secondary}; --tertiary: ${theme.colors.lightMode.tertiary}; --highlight: ${theme.colors.lightMode.highlight}; + --textHighlight: ${theme.colors.lightMode.textHighlight}; --headerFont: "${theme.typography.header}", ${DEFAULT_SANS_SERIF}; --bodyFont: "${theme.typography.body}", ${DEFAULT_SANS_SERIF}; @@ -64,6 +66,7 @@ ${stylesheet.join("\n\n")} --secondary: ${theme.colors.darkMode.secondary}; --tertiary: ${theme.colors.darkMode.tertiary}; --highlight: ${theme.colors.darkMode.highlight}; + --textHighlight: ${theme.colors.darkMode.textHighlight}; } ` } diff --git a/quartz/worker.ts b/quartz/worker.ts index d413ae240..888eeb991 100644 --- a/quartz/worker.ts +++ b/quartz/worker.ts @@ -13,6 +13,7 @@ export async function parseFiles( allSlugs: FullSlug[], ) { const ctx: BuildCtx = { + buildId, cfg, argv, allSlugs,