Compare commits

..

263 Commits

Author SHA1 Message Date
MarcRez33
01fc26d2c0
fix: Reload graph after a theme change (closes #1380) (#1383)
* FIX: Reload graph after a theme change

* FIX: Reload graph after a theme change - comment updated

* FIX: Reload graph after a theme change - comment updated

* FIX: Reload graph after a theme change

* fix: Reload graph after a theme change
2024-08-31 12:25:30 -07:00
bfahrenfort
84a9be65ce
Add Projects & Privacy to showcase.md (#1381) 2024-08-31 11:43:49 -07:00
Jacky Zhao
6715079a89 fix: only apply z-index on left sidebar 2024-08-28 12:50:25 -07:00
dependabot[bot]
d613a3d2f2
chore(deps): bump unified from 11.0.4 to 11.0.5 (#1373)
Bumps [unified](https://github.com/unifiedjs/unified) from 11.0.4 to 11.0.5.
- [Release notes](https://github.com/unifiedjs/unified/releases)
- [Changelog](https://github.com/unifiedjs/unified/blob/main/changelog.md)
- [Commits](https://github.com/unifiedjs/unified/compare/11.0.4...11.0.5)

---
updated-dependencies:
- dependency-name: unified
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 14:13:09 -07:00
dependabot[bot]
9b75faafea
chore(deps-dev): bump @types/yargs from 17.0.32 to 17.0.33 (#1374)
Bumps [@types/yargs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/yargs) from 17.0.32 to 17.0.33.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/yargs)

---
updated-dependencies:
- dependency-name: "@types/yargs"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 14:13:00 -07:00
dependabot[bot]
5a26b582ed
chore(deps-dev): bump @types/node from 22.1.0 to 22.5.0 (#1375)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.1.0 to 22.5.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 14:12:51 -07:00
dependabot[bot]
4e2aea8a5a
chore(deps): bump @napi-rs/simple-git from 0.1.17 to 0.1.19 (#1376)
Bumps [@napi-rs/simple-git](https://github.com/Brooooooklyn/simple-git) from 0.1.17 to 0.1.19.
- [Release notes](https://github.com/Brooooooklyn/simple-git/releases)
- [Commits](https://github.com/Brooooooklyn/simple-git/compare/v0.1.17...v0.1.19)

---
updated-dependencies:
- dependency-name: "@napi-rs/simple-git"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 14:12:45 -07:00
dependabot[bot]
40f039983c
chore(deps-dev): bump tsx from 4.17.0 to 4.18.0 (#1377)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.17.0 to 4.18.0.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.17.0...v4.18.0)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 14:12:35 -07:00
Iker Larrea
3b988aec61
fix: Popover z-index issue on left sidebar (#1230) 2024-08-25 19:33:34 -07:00
Aaron Pham
bca74623a3
perf(graph): canvas implementation (#1328)
* perf(graph): initial canvas layout

include nodes and links drawn

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix(graph): update persistent for nodeGfx

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* chore(graph): add canvas element to avoid rerendering glitch

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix(spa): only render graph once in global

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix(graph): change svg as button

render global graph on toggle

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix(graph): fix anchor position and zIndex behaviour

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* chore(graph): increase linkDistance

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* refactor

* fmt

* pkg

---------

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-08-25 00:33:14 -07:00
Andrew
4c9e860150
a11y(darkmode): use a button for the theme toggle (#1335)
* Use a `<button>` for theme toggle

* docs: Adds back Xinyang's cs garden to showcase (#1323)

adding back my garden which was deleted from the cleanup showcase

* feat(toc,explorer): add accessibility for toggle (#1327)

* Restore focus highlight on explorer toggle button.

Remove `unset: all` declaration causing `outline`
property to be unset. This allows the default
browser focus highlight to be shown.

* Fix semantics of expandable sections (explorer, toc).

This adds the appropriate aria attributes for the [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-image-description/#javascriptandcsssourcecode) and uses `visibility: hidden` to remove the hidden elements from the focus order without disrupting the animations. Further work is needed on the tree view nodes.

* Run prettier for SCSS files.

* feat: custom global latex macros (closes #1325)

* chore: ts fixes

* docs: recommend at least node 20 in gh

* fix: unmemoize explorer on rebuild (closes #1077)

* fix: pass buildId to worker

* Fix theme button DOM hierarchy and styles

* Restore functionality of theme button

* `aria-label` on theme svgs so their accessible labels are included in button content

---------

Co-authored-by: Xinyang Yu <47915643+xy-241@users.noreply.github.com>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-08-24 13:33:30 -07:00
Danny van Tol
46b63b68bf
fix: Link visibility in exampanded TOC while collapseByDefault is true (#1371) 2024-08-24 12:56:17 -07:00
Andrew
c4cd84dcc8
fix: Add a wrapper element to dates in PageList. (#1345)
* Add a wrapper span to dates in PageList.

This means there is a placeholder when date is not specified, so the values in grid-template-columns always line up correctly.

* Use a <div> instead -- better practice to stick to block elements.
2024-08-24 10:13:55 -07:00
Aaron Pham
2be9c096a1
chore(features): remove implemented (#1350) 2024-08-24 10:13:14 -07:00
Tony Jackson
f913332862
docs: Update Gitlab CI runner tag to use Gitlab runner (#1365)
Co-authored-by: Tony Jackson <tony.aaj.jackson@protonmail.com>
2024-08-23 18:55:03 -07:00
Elijah Kennedy
647c125525
docs: added permalink description to supported frontmatter doc (#1369) 2024-08-23 18:35:52 -07:00
Eledah
3b74453fe6
docs: RTL showcase (#1362) 2024-08-20 09:28:52 -07:00
dependabot[bot]
6b499ed90c
chore(deps): bump @floating-ui/dom from 1.6.8 to 1.6.10 (#1358)
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.6.8 to 1.6.10.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.6.10/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:18:00 -07:00
dependabot[bot]
437d65c847
chore(deps): bump lightningcss from 1.25.1 to 1.26.0 (#1359)
Bumps [lightningcss](https://github.com/parcel-bundler/lightningcss) from 1.25.1 to 1.26.0.
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.25.1...v1.26.0)

---
updated-dependencies:
- dependency-name: lightningcss
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:15:43 -07:00
dependabot[bot]
7c5709b660
chore(deps): bump preact-render-to-string from 6.5.7 to 6.5.9 (#1360)
Bumps [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) from 6.5.7 to 6.5.9.
- [Release notes](https://github.com/preactjs/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/preactjs/preact-render-to-string/compare/v6.5.7...v6.5.9)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:15:34 -07:00
dependabot[bot]
21e1921822
chore(deps): bump @napi-rs/simple-git from 0.1.16 to 0.1.17 (#1357)
Bumps [@napi-rs/simple-git](https://github.com/Brooooooklyn/simple-git) from 0.1.16 to 0.1.17.
- [Release notes](https://github.com/Brooooooklyn/simple-git/releases)
- [Commits](https://github.com/Brooooooklyn/simple-git/compare/v0.1.16...v0.1.17)

---
updated-dependencies:
- dependency-name: "@napi-rs/simple-git"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:14:45 -07:00
dependabot[bot]
e47c29d2fd
chore(deps): bump rehype-katex from 7.0.0 to 7.0.1 (#1356)
Bumps [rehype-katex](https://github.com/remarkjs/remark-math) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/remarkjs/remark-math/releases)
- [Commits](https://github.com/remarkjs/remark-math/compare/rehype-katex@7.0.0...rehype-katex@7.0.1)

---
updated-dependencies:
- dependency-name: rehype-katex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 14:14:29 -07:00
dependabot[bot]
a5f2f874f7
chore(deps-dev): bump tsx from 4.16.2 to 4.17.0 (#1340)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.16.2 to 4.17.0.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.16.2...v4.17.0)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 05:07:05 -04:00
dependabot[bot]
eb23cbe8da
chore(deps): bump rehype-citation from 2.0.0 to 2.1.1 (#1341)
Bumps [rehype-citation](https://github.com/timlrx/rehype-citation) from 2.0.0 to 2.1.1.
- [Release notes](https://github.com/timlrx/rehype-citation/releases)
- [Commits](https://github.com/timlrx/rehype-citation/compare/v2.0.0...v2.1.1)

---
updated-dependencies:
- dependency-name: rehype-citation
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 04:25:07 -04:00
dependabot[bot]
cb68069d45
chore(deps-dev): bump typescript from 5.5.3 to 5.5.4 (#1343)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.5.3 to 5.5.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.5.3...v5.5.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 04:24:48 -04:00
Andrew
d27c292736
feat: Mark the external link icon as aria-hidden (#1346) 2024-08-12 20:06:19 -07:00
dependabot[bot]
0ee103a514
chore(deps): bump shiki from 1.10.3 to 1.12.1 (#1344)
Bumps [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) from 1.10.3 to 1.12.1.
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Changelog](https://github.com/shikijs/shiki/blob/main/CHANGELOG.md)
- [Commits](https://github.com/shikijs/shiki/commits/v1.12.1/packages/shiki)

---
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-12 13:38:15 -07:00
dependabot[bot]
c5f0b69a52
chore(deps): bump preact from 10.22.1 to 10.23.2 (#1342)
Bumps [preact](https://github.com/preactjs/preact) from 10.22.1 to 10.23.2.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.22.1...10.23.2)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-12 13:37:54 -07:00
Karthi Fair Hawn
323167a001
docs(hosting): missing key with: (#1334) 2024-08-10 08:32:51 -07:00
Andrew
3b5ed813f5
feat(search): keyboard-accessible search button (#1331)
* Use a `<button>` for search

* Fix search button styles to match preexisting styling

* Remove additional native button properties.

* Invoke search button on click or keyboard.

* Reorganize search button DOM hierarchy

* Restore focus to the search button when exiting the search overlay

* Run prettier on Search.tsx
2024-08-09 18:46:50 -07:00
Jacky Zhao
195fc5134c fix: pass buildId to worker 2024-08-08 19:33:27 -07:00
Jacky Zhao
e89c395f7c fix: unmemoize explorer on rebuild (closes #1077) 2024-08-08 19:17:20 -07:00
Jacky Zhao
2db735a150 docs: recommend at least node 20 in gh 2024-08-08 18:38:17 -07:00
Jacky Zhao
39eebca3cf chore: ts fixes 2024-08-08 18:28:13 -07:00
Jacky Zhao
9acaa1c8ac feat: custom global latex macros (closes #1325) 2024-08-08 18:19:45 -07:00
Andrew
27a41abb62
feat(toc,explorer): add accessibility for toggle (#1327)
* Restore focus highlight on explorer toggle button.

Remove `unset: all` declaration causing `outline`
property to be unset. This allows the default
browser focus highlight to be shown.

* Fix semantics of expandable sections (explorer, toc).

This adds the appropriate aria attributes for the [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-image-description/#javascriptandcsssourcecode) and uses `visibility: hidden` to remove the hidden elements from the focus order without disrupting the animations. Further work is needed on the tree view nodes.

* Run prettier for SCSS files.
2024-08-08 18:07:47 -07:00
Xinyang Yu
12904ab796
docs: Adds back Xinyang's cs garden to showcase (#1323)
adding back my garden which was deleted from the cleanup showcase
2024-08-08 18:00:20 -07:00
Jacky Zhao
4bbcc0c50a pkg: minor bump for breaking nodejs bump :) 2024-08-05 19:33:23 -07:00
Jacky Zhao
3938904cd0 fix: embed pdf aspect ratio (closes #1310) 2024-08-05 19:31:54 -07:00
Jacky Zhao
407fad384c fix: only one h1 on a page (closes #1269) 2024-08-05 19:18:48 -07:00
Jacky Zhao
ca3943b500 fix: responsive youtube embed (closes #1167) 2024-08-05 19:14:14 -07:00
dependabot[bot]
6c4ed249ba
chore(deps): bump rimraf from 5.0.7 to 6.0.1 (#1277)
Bumps [rimraf](https://github.com/isaacs/rimraf) from 5.0.7 to 6.0.1.
- [Changelog](https://github.com/isaacs/rimraf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/rimraf/compare/v5.0.7...v6.0.1)

---
updated-dependencies:
- dependency-name: rimraf
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 18:54:10 -07:00
Jacky Zhao
563ab4aaaf docs: update node version in hosting docs 2024-08-05 18:49:20 -07:00
Jacky Zhao
1c2d542138 build: add .node-version 2024-08-05 18:48:29 -07:00
Pelayo Arbués
e864740df7
docs: Adds back Pelayo Arbues blog to showcase (#1314)
Co-authored-by: Pelayo Arbues <pgarbues@idealista.com>
2024-08-05 18:44:34 -07:00
Ellie Huxtable
efed544df1
docs: Add "Ellie's Notes" to the showcase (#1315)
Thank you for Quartz! I really love using it. Just adding my notes to the showcase 😊
2024-08-05 18:43:57 -07:00
Jacky Zhao
3d156b8497 deps(dev): bump nodejs in ci 2024-08-05 18:43:05 -07:00
Jacky Zhao
38361aaf48 deps: change min required nodejs to v20 (breaking) 2024-08-05 18:41:46 -07:00
dependabot[bot]
f3e07fd51c
chore(deps-dev): bump prettier from 3.3.2 to 3.3.3 (#1293)
* chore(deps-dev): bump prettier from 3.3.2 to 3.3.3

Bumps [prettier](https://github.com/prettier/prettier) from 3.3.2 to 3.3.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.2...3.3.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* make prettier happy

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-08-05 18:38:07 -07:00
dependabot[bot]
d79911fa79
chore(deps): bump workerpool from 9.1.2 to 9.1.3 (#1318)
Bumps [workerpool](https://github.com/josdejong/workerpool) from 9.1.2 to 9.1.3.
- [Changelog](https://github.com/josdejong/workerpool/blob/master/HISTORY.md)
- [Commits](https://github.com/josdejong/workerpool/compare/v9.1.2...v9.1.3)

---
updated-dependencies:
- dependency-name: workerpool
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 18:46:00 -04:00
dependabot[bot]
963c7c8654
chore(deps-dev): bump @types/node from 20.14.11 to 22.1.0 (#1319)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.14.11 to 22.1.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 16:49:37 -04:00
dependabot[bot]
3728929ee6
chore(deps): bump preact-render-to-string from 6.5.5 to 6.5.7 (#1317)
Bumps [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) from 6.5.5 to 6.5.7.
- [Release notes](https://github.com/preactjs/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/preactjs/preact-render-to-string/compare/v6.5.5...v6.5.7)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 16:49:19 -04:00
Aaron Pham
1224c7d32f
refactor(comments): move script to files (#1308)
* refactor(comments): move script to files

for LSP, treesitter, and the whole galore.

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix(type): support removeEventListener with CustomEventMap

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix: parse bool to string first

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* chore: address comments and test on branch

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* revert: remove comments section from main quartz pages

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

---------

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
2024-08-05 15:17:11 -04:00
dependabot[bot]
bf1c9d1791
chore(deps): bump globby from 14.0.1 to 14.0.2 (#1301)
Bumps [globby](https://github.com/sindresorhus/globby) from 14.0.1 to 14.0.2.
- [Release notes](https://github.com/sindresorhus/globby/releases)
- [Commits](https://github.com/sindresorhus/globby/compare/v14.0.1...v14.0.2)

---
updated-dependencies:
- dependency-name: globby
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-02 14:48:54 -04:00
Jacky Zhao
6264f5685c
fix: comments on spa should work (closes #1296) (#1298)
* fix comments on spa

* fix giscus
2024-07-30 01:13:13 -07:00
Jacky Zhao
e1a9661be7 docs: cleanup showcase 2024-07-29 16:33:50 -07:00
dependabot[bot]
bc95332fce
chore(deps-dev): bump @types/ws from 8.5.11 to 8.5.12 (#1300)
Bumps [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) from 8.5.11 to 8.5.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

---
updated-dependencies:
- dependency-name: "@types/ws"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-29 13:14:34 -07:00
dependabot[bot]
740172acb6
chore(deps): bump vfile from 6.0.1 to 6.0.2 (#1299)
Bumps [vfile](https://github.com/vfile/vfile) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/vfile/vfile/releases)
- [Changelog](https://github.com/vfile/vfile/blob/main/changelog.md)
- [Commits](https://github.com/vfile/vfile/compare/6.0.1...6.0.2)

---
updated-dependencies:
- dependency-name: vfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-29 13:14:25 -07:00
dependabot[bot]
34fde07cf8
chore(deps-dev): bump tsx from 4.16.0 to 4.16.2 (#1292)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.16.0 to 4.16.2.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.16.0...v4.16.2)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 15:30:44 -07:00
dependabot[bot]
e688eeeaff
chore(deps-dev): bump @types/node from 20.12.5 to 20.14.11 (#1291)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.12.5 to 20.14.11.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 15:30:35 -07:00
dependabot[bot]
5749fbbd75
chore(deps): bump @floating-ui/dom from 1.6.5 to 1.6.8 (#1290)
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.6.5 to 1.6.8.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.6.8/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 15:30:28 -07:00
Jacky Zhao
5f10df4d05 pkg 2024-07-21 17:49:28 -07:00
Jacky Zhao
03f23e5054 feat: comments (giscus) 2024-07-20 23:05:45 -07:00
Jacky Zhao
b9ee44aad7 i18n: disambiguate en-us and en-gb 2024-07-20 20:24:17 -07:00
dependabot[bot]
87f2b0c327
chore(deps): bump lightningcss from 1.24.1 to 1.25.1 (#1276)
Bumps [lightningcss](https://github.com/parcel-bundler/lightningcss) from 1.24.1 to 1.25.1.
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.24.1...v1.25.1)

---
updated-dependencies:
- dependency-name: lightningcss
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-19 10:57:28 -07:00
dependabot[bot]
805d9e3226
chore(deps): bump ws and @types/ws (#1280)
Bumps [ws](https://github.com/websockets/ws) and [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws). These dependencies needed to be updated together.

Updates `ws` from 8.17.1 to 8.18.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.17.1...8.18.0)

Updates `@types/ws` from 8.5.10 to 8.5.11
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/ws"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-19 10:02:06 -07:00
dependabot[bot]
5fcba1bfaf
chore(deps): bump mdast-util-to-hast from 13.1.0 to 13.2.0 (#1279)
Bumps [mdast-util-to-hast](https://github.com/syntax-tree/mdast-util-to-hast) from 13.1.0 to 13.2.0.
- [Release notes](https://github.com/syntax-tree/mdast-util-to-hast/releases)
- [Commits](https://github.com/syntax-tree/mdast-util-to-hast/compare/13.1.0...13.2.0)

---
updated-dependencies:
- dependency-name: mdast-util-to-hast
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-19 10:02:03 -07:00
dependabot[bot]
942c35183a
chore(deps): bump preact from 10.22.0 to 10.22.1 (#1278)
Bumps [preact](https://github.com/preactjs/preact) from 10.22.0 to 10.22.1.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.22.0...10.22.1)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-19 10:01:55 -07:00
sou7
b37c408985
Fix CreatedModifiedDate.md (#1281)
Fixed a broken parentheses correspondence for code fragments that appear in the documentation.
2024-07-16 09:08:58 -07:00
Emile Bangma
f37dbe1a59
fix(translusion): block reference not being recognized. (#1274) 2024-07-14 03:06:52 -07:00
Jacky Zhao
247625c4f5 feat(layout): add afterBody 2024-07-09 19:09:31 -07:00
Jacky Zhao
4b407e786f chore: format 2024-07-09 18:08:21 -07:00
Jacky Zhao
9cabf2b416 chore: update features 2024-07-09 18:07:00 -07:00
Jacky Zhao
965425d54d docs + chore: cleanup custom sort ordering for folder + tag listings, add docs 2024-07-09 17:55:19 -07:00
Cao Mingjun
ea92ed4f45
feat: Allow custom sorting of FolderPage and TagPage (#1250) 2024-07-09 17:42:33 -07:00
dependabot[bot]
596e06ab0e
chore(deps-dev): bump typescript from 5.4.5 to 5.5.3 (#1254)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.4.5 to 5.5.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.5...v5.5.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 17:28:49 -07:00
Alex Nordstrom
c95f1d2336
feat: add alias/metadata to transclude tag (#1229) 2024-07-09 17:26:32 -07:00
Peter
b7793bd856
feat(style): Add textHighlight theme setting (#1242)
* Add textHighlight theme setting

* update docs to include textHighlight

* Remove errant `S`
2024-07-09 17:21:00 -07:00
dependabot[bot]
ef375d265d
chore(deps): bump rfdc from 1.3.1 to 1.4.1 (#1235)
Bumps [rfdc](https://github.com/davidmarkclements/rfdc) from 1.3.1 to 1.4.1.
- [Release notes](https://github.com/davidmarkclements/rfdc/releases)
- [Commits](https://github.com/davidmarkclements/rfdc/compare/v1.3.1...1.4.1)

---
updated-dependencies:
- dependency-name: rfdc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 17:20:37 -07:00
Jacky Zhao
2154d36d99 chore: use regex flag instead of string in regexp ctor 2024-07-09 17:20:13 -07:00
dependabot[bot]
3eda53cac7
chore(deps): bump remark-smartypants from 2.1.0 to 3.0.2 (#1263)
Bumps [remark-smartypants](https://github.com/silvenon/remark-smartypants) from 2.1.0 to 3.0.2.
- [Release notes](https://github.com/silvenon/remark-smartypants/releases)
- [Commits](https://github.com/silvenon/remark-smartypants/compare/v2.1.0...v3.0.2)

---
updated-dependencies:
- dependency-name: remark-smartypants
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 17:16:02 -07:00
Cao Mingjun
4eeacb7fbf
Fix Non-English Anchor Popover Positioning Issue and Update Type Hint (#1252)
- [Major] Changed `hash` passed to `querySelector` to `decodeURIComponent(hash)` to fix the issue where non-English anchors were not correctly positioning the popover content to the corresponding title.
- [Minor] Updated the type hint from `HTMLLinkElement` to `HTMLAnchorElement` as the passed element is an `<a>` element, not a `<link>` element (reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement).
2024-07-08 13:34:43 -07:00
dependabot[bot]
6245935c8c
chore(deps): bump shiki from 1.6.0 to 1.10.3 (#1264)
Bumps [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) from 1.6.0 to 1.10.3.
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Changelog](https://github.com/shikijs/shiki/blob/main/CHANGELOG.md)
- [Commits](https://github.com/shikijs/shiki/commits/v1.10.3/packages/shiki)

---
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 13:34:04 -07:00
MassiveJuice
e20dee2850
Fix: Table overflow-wrap: anywhere breaks words on mobile (#1259)
Closes #1258
2024-07-05 09:25:29 -07:00
dependabot[bot]
af1fdaac0a
chore(deps-dev): bump tsx from 4.11.2 to 4.16.0 (#1256)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.11.2 to 4.16.0.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.11.2...v4.16.0)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-04 22:47:20 -04:00
Peter
5926d3f803
docs: replace .gitlab-ci.yml example with more reliable and faster ci job (#1243)
* replace .gitlab-ci.yml example with more reliable and faster ci job

* literally removing 1 space, inside a code block, in docs, just to make prettier not cry
2024-06-30 21:12:48 -07:00
Illia Pyshniak
e8277d017e
i18n: Update uk-UA.ts (#1245)
Update the Ukrainian translation of Quartz according to the latest Ukrainian translation of Obsidian.
2024-06-30 21:11:21 -07:00
Christopher Tee
b053d354b6
deps: Bump Github Action versions (#1247) 2024-06-30 21:05:42 -07:00
L01D
dbe12c0d34
i18n: Improving Spanish & adding Catalan and British English (#1240)
* Create en-GB

* Rename en-GB to en-GB.ts

* Update es-ES.ts

* Update es-ES.ts

* Create ca-ES.ts

* Update es-ES.ts

* Update index.ts

* Update index.ts

* Update index.ts

* Update es-ES.ts
2024-06-30 21:03:45 -07:00
Seohyun Kim
21e75acc8d
fix: "draft" true or false in frontmatter still removes from publishing #1244 (#1249)
* fix: draft bug #1244

* update: contents in folder before creating PR

* Update draft.ts
2024-06-30 20:59:08 -07:00
Sean Missingham
f7bd2137ec
Permit Manual (and Scripted) Trigger of CI Job (#1251) 2024-06-30 20:58:34 -07:00
John Bowdre
3faf2ff6f5
feat(analytics): Cabin analytics support (#1221)
* add cabin analytics

* fix formatting
2024-06-18 13:38:45 -07:00
Jacky Zhao
1d94e9c303 css: use fit-content (closes #1194) 2024-06-17 22:13:31 -07:00
Jacky Zhao
48e16c943a chore(deps-dev): bump prettier 2024-06-17 21:46:43 -07:00
Jacky Zhao
cc5913b75c fix(ci): only publish tag on v4 origin 2024-06-17 21:45:58 -07:00
Jacky Zhao
265faef4e8 fix: properly compute relative path for explorer (closes #1055, #1066) 2024-06-17 21:43:32 -07:00
Jacky Zhao
541b470cfc fix: overflow fade for good (closes #1218) 2024-06-17 21:33:53 -07:00
dependabot[bot]
0a3be96dd6
chore(deps): bump ws from 8.17.0 to 8.17.1 (#1213)
Bumps [ws](https://github.com/websockets/ws) from 8.17.0 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.17.0...8.17.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 20:37:04 -07:00
dependabot[bot]
3cb9392a7a
chore(deps): bump preact-render-to-string from 6.5.4 to 6.5.5 (#1214)
Bumps [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) from 6.5.4 to 6.5.5.
- [Release notes](https://github.com/preactjs/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/preactjs/preact-render-to-string/compare/v6.5.4...v6.5.5)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 20:34:45 -07:00
dependabot[bot]
569ee74633
chore(deps): bump workerpool from 9.1.1 to 9.1.2 (#1215)
Bumps [workerpool](https://github.com/josdejong/workerpool) from 9.1.1 to 9.1.2.
- [Changelog](https://github.com/josdejong/workerpool/blob/master/HISTORY.md)
- [Commits](https://github.com/josdejong/workerpool/compare/v9.1.1...v9.1.2)

---
updated-dependencies:
- dependency-name: workerpool
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 20:33:00 -07:00
Paul Trotter
42640bceb5
Check link isExternal before adding target="_blank" (#1211)
Fixes #1186 openLinksInNewTab opens ALL links in new tabs. Fixed to reflect documented behavior here: https://quartz.jzhao.xyz/plugins/CrawlLinks
2024-06-16 22:33:28 -07:00
Emile Bangma
3e14b2b89b
fix(wikilinks): pdf page linking (#1207) 2024-06-14 09:17:46 -07:00
Emile Bangma
81d00fc9c0
.callout-content support (#1188)
* .callout-content support

* Use BlockContent | FootnoteContent for callout body

* Update quartz/plugins/transformers/ofm.ts

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* Refactor

* Combine child selectors

* Fix multiple callout members

* Empty check

* Replace splice

---------

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-06-13 16:02:00 -07:00
Callum Barker
a12d76afdb
fix: fix explorer view gradient positioning on mobile (fixes #906) (#1206) 2024-06-13 12:47:22 -07:00
x4dr
19e127f6ad
fix DOMLoaded in code examples (#1204) 2024-06-13 09:38:48 -07:00
Leo Lazou
0472daa003
homepage coloured as visited in the Graph (#1128)
simplifies slug from FullSlug to SimpleSlug before storing it in the visited pages list in memory
this leads to "index" page and "folder/index", "tags/tag/index" being stored a "/", "folder/" and "tags/tag/" respectively in the list of visited pages.
this ensures that the homepage is rightfully coloured as a visited page in the "color" function of the graph
2024-06-12 09:39:49 -07:00
dependabot[bot]
63d51a8cc5
chore(deps): bump preact-render-to-string from 6.4.2 to 6.5.4 (#1198)
Bumps [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) from 6.4.2 to 6.5.4.
- [Release notes](https://github.com/preactjs/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/preactjs/preact-render-to-string/compare/v6.4.2...v6.5.4)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 09:34:36 -07:00
dependabot[bot]
9032717486
chore(deps): bump preact from 10.20.1 to 10.22.0 (#1195)
Bumps [preact](https://github.com/preactjs/preact) from 10.20.1 to 10.22.0.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.20.1...10.22.0)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 09:31:50 -07:00
dependabot[bot]
3968b850c2
chore(deps): bump @floating-ui/dom from 1.6.3 to 1.6.5 (#1196)
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.6.3 to 1.6.5.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.6.5/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 13:57:08 -07:00
dependabot[bot]
688c5484a9
chore(deps-dev): bump tsx from 4.11.0 to 4.11.2 (#1183)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.11.0 to 4.11.2.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.11.0...v4.11.2)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 18:16:34 -04:00
dependabot[bot]
09038f1604
chore(deps): bump rehype-pretty-code from 0.13.0 to 0.13.2 (#1184)
Bumps [rehype-pretty-code](https://github.com/rehype-pretty/rehype-pretty-code/tree/HEAD/packages/core) from 0.13.0 to 0.13.2.
- [Release notes](https://github.com/rehype-pretty/rehype-pretty-code/releases)
- [Changelog](https://github.com/rehype-pretty/rehype-pretty-code/blob/master/packages/core/CHANGELOG.md)
- [Commits](https://github.com/rehype-pretty/rehype-pretty-code/commits/rehype-pretty-code@0.13.2/packages/core)

---
updated-dependencies:
- dependency-name: rehype-pretty-code
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 18:16:18 -04:00
Leo Lazou
244801af65
idea: Tags appear as hollow circles on the graph (#1129)
* Tags appear as hollow circles on the graph

Added a few lines to make tags appear as hollow circles on the graph, as opposed to pages which are plain circles, for better visual separation.

* Applied Prettier code style
2024-05-30 10:42:22 -07:00
Jacky Zhao
73a5ec87f1 docs: fix link to path tests (closes #1163) 2024-05-29 13:04:58 -07:00
Jacky Zhao
520acbbf6f docs: inline code syntax highlighting (closes #1162) 2024-05-29 13:04:03 -07:00
Dinu Blanovschi
0b9f79e1b7
feat(graph): obsidianLikeFocusOnHover (#1017)
* feat(graph): obsidianLikeFocusOnHover

* fix: prettier

* fix: remove option from config

* fix: for when opacityOld < 0.2

* fix: prettier
2024-05-29 12:53:23 -07:00
RunTheBot
94fbf5b066
fix: Reorder Unified.js plugins to fix #1132 (#1139)
* Reorder Unified.js to fix #1132

* moved latex farther down for bette luck
2024-05-29 12:52:53 -07:00
dependabot[bot]
3e0e06ff8a
chore(deps): bump ws from 8.16.0 to 8.17.0 (#1169)
Bumps [ws](https://github.com/websockets/ws) from 8.16.0 to 8.17.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.16.0...8.17.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-29 12:49:55 -07:00
dependabot[bot]
e57984dafc
chore(deps-dev): bump tsx from 4.9.3 to 4.11.0 (#1168)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.9.3 to 4.11.0.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.9.3...v4.11.0)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-29 12:48:55 -07:00
Alex Nordstrom
9ff4626d25
fix: change callout metadata regex to include non-letter characters (#1174)
* fix: change callout metadata regex to include non-letter characters

* fix: make metadata regex non-greedy

This allows for users to have callouts such as
> [!NOTE|left foo-bar 123] a ]+ title with square brackets [s] a
> Contents
2024-05-29 12:48:39 -07:00
Max
a99e854d1e
docs: Update showcase.md (#1176)
Added Gatekeeper Wiki.
2024-05-29 12:47:45 -07:00
Alex Nordstrom
77d6d9623f
feat: add callout metadata parsing (#1172) 2024-05-28 15:23:28 -07:00
James Bennion-Pedley
9c726efa33
feat(i18n): homepage link for 404 pages (#1117)
* Add homepage link with internationalization

* Construct pathname from baseUrl config value

* More robust URL manipulation

* Add Farsi (#1133)

* Fix bad rebase
2024-05-22 16:44:54 -04:00
Yohann Bacha
81a4e20236
feat: ability to hide tags in the recent notes component (#1147)
* feat: ability to hide tags in the recent notes component

* docs: recent notes custom parameters in a table

* docs: revert recent notes doc to bullet points

* fix: linter issues

* Update docs/features/recent notes.md

---------

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-05-21 09:50:58 -07:00
dependabot[bot]
cf1b3f270b
chore(deps): bump shiki from 1.2.3 to 1.6.0 (#1149)
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:11:16 -07:00
dependabot[bot]
a655cec9f6
chore(deps): bump rimraf from 5.0.5 to 5.0.7 (#1150)
updated-dependencies:
- dependency-name: rimraf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:11:08 -07:00
Bartłomiej Garbiec
a97e72f219
i18n: add Polish translation (#1148)
* Create pl-PL.ts

* add pl-PL to index.ts

* import pl-PL in index.ts
2024-05-20 12:36:32 -07:00
dependabot[bot]
e3cfe1f22f
chore(deps-dev): bump typescript from 5.4.3 to 5.4.5 (#1092)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.4.3 to 5.4.5.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.3...v5.4.5)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 23:21:01 -07:00
dependabot[bot]
535af55ec8
chore(deps): bump hast-util-to-html from 9.0.0 to 9.0.1 (#1137)
Bumps [hast-util-to-html](https://github.com/syntax-tree/hast-util-to-html) from 9.0.0 to 9.0.1.
- [Release notes](https://github.com/syntax-tree/hast-util-to-html/releases)
- [Commits](https://github.com/syntax-tree/hast-util-to-html/compare/9.0.0...9.0.1)

---
updated-dependencies:
- dependency-name: hast-util-to-html
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 14:25:42 -07:00
Eledah
3c4d54352d
feat(i18n): add Farsi (#1133)
* Add fa-IR translation via upload

* Add files via upload

* Ran npm run format
2024-05-13 09:12:06 -07:00
dependabot[bot]
67f3614f3d
chore(deps-dev): bump tsx from 4.7.1 to 4.9.3 (#1120)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.7.1 to 4.9.3.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.7.1...v4.9.3)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-12 19:05:27 -07:00
zenodotus280
438ea6d73b
docs(showcase): add patternlanguage.cc (#1135)
A self-contained hypertextual catalog of architectural ideas and aesthetic inquiry.
2024-05-12 19:04:21 -07:00
John Bowdre
d03fdc235a
feat(analytics): Tinylytics support (#1118)
* add tinylytics support

* fix formatting

* add trailing semicolon for consistency
2024-05-06 09:30:21 -07:00
Emmanuel Ferdman
aee9145691
fix: update link to hosting page (#1054)
* fix: update link to hosting page

* chore: update correct path with using alias

---------

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
2024-04-27 21:07:26 -07:00
dependabot[bot]
a37c7775e7
chore(deps): bump preact-render-to-string from 6.4.0 to 6.4.2 (#1094)
Bumps [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) from 6.4.0 to 6.4.2.
- [Release notes](https://github.com/preactjs/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/preactjs/preact-render-to-string/compare/6.4.0...v6.4.2)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-27 21:04:38 -07:00
iacore
e763e1969e
Allow pnpm quartz (#1078)
Co-authored-by: iacore <noreply+gpg-stub@1a-insec.net>
2024-04-19 13:02:49 -07:00
Lucas-BRT
6a019dae13
fix: unnecessery 'm' letter removed in pt-BR (#1100) 2024-04-16 18:02:59 -07:00
dependabot[bot]
4d73b8289d
chore(deps): bump workerpool from 9.1.0 to 9.1.1 (#1073)
Bumps [workerpool](https://github.com/josdejong/workerpool) from 9.1.0 to 9.1.1.
- [Changelog](https://github.com/josdejong/workerpool/blob/master/HISTORY.md)
- [Commits](https://github.com/josdejong/workerpool/compare/v9.1.0...v9.1.1)

---
updated-dependencies:
- dependency-name: workerpool
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-12 02:33:05 -04:00
dependabot[bot]
8010093df7
chore(deps-dev): bump @types/node from 20.11.29 to 20.12.5 (#1074)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.29 to 20.12.5.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-12 02:30:21 -04:00
kwyuan
1f032f538b
feat(analytics): PostHog support (#1072) 2024-04-08 18:43:09 -07:00
dependabot[bot]
83bdcd58e6
chore(deps): bump d3 from 7.8.5 to 7.9.0 (#1047)
Bumps [d3](https://github.com/d3/d3) from 7.8.5 to 7.9.0.
- [Release notes](https://github.com/d3/d3/releases)
- [Changelog](https://github.com/d3/d3/blob/main/CHANGES.md)
- [Commits](https://github.com/d3/d3/compare/v7.8.5...v7.9.0)

---
updated-dependencies:
- dependency-name: d3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 16:02:00 -04:00
Race Williams
dd82ab8d79
fix: broken doc links on /plugins/* (#1053)
* Update Assets.md

* expand fix for every /plugins page
2024-04-03 13:30:41 -07:00
dependabot[bot]
561dafce5f
chore(deps): bump shiki from 1.2.0 to 1.2.3 (#1048)
Bumps [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) from 1.2.0 to 1.2.3.
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Changelog](https://github.com/shikijs/shiki/blob/main/CHANGELOG.md)
- [Commits](https://github.com/shikijs/shiki/commits/v1.2.3/packages/shiki)

---
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 23:32:36 -04:00
Emile Bangma
5ec61468d5
fix(wikilinks): proper escaping of pipe character in wikilinks inside tables (#1040) 2024-03-31 09:44:50 -07:00
Hydrophobefireman
aa4f5294a3
fix: do not render <p> inside FolderContent article (#1044)
it can lead to nested <p>'s which is actually [invalid html](https://www.w3.org/TR/html401/struct/text.html#h-9.3.1:~:text=The%20P%20element%20represents%20a%20paragraph.%20It%20cannot%20contain%20block%2Dlevel%20elements%20(including%20P%20itself).)
2024-03-31 09:44:20 -07:00
dependabot[bot]
fafe50b0c5
chore(deps): bump preact from 10.19.6 to 10.20.1 (#1035)
Bumps [preact](https://github.com/preactjs/preact) from 10.19.6 to 10.20.1.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.19.6...10.20.1)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 04:47:15 -04:00
dependabot[bot]
8f13a38b5a
chore(deps-dev): bump typescript from 5.4.2 to 5.4.3 (#1036)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.4.2 to 5.4.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.2...v5.4.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 04:46:52 -04:00
Emile Bangma
d75928ad5c
fix(assets): pdf page linking support. (#1025)
* github-slugger pdf workaround

* Skip sluggifying on pdf file extension

* Account for pdf files without anchor

* Address feedback
2024-03-24 16:23:25 -07:00
Jacky Zhao
0a2b52f618 simpler katex fix 2024-03-24 15:50:38 -07:00
Emile Bangma
8437d9da72
fix(style): LaTex/KaTeX overflow (#1027)
* LaTex/KaTeX overflow fix

* prettier

* Add !important modifier

* Added overflow-x override

* Refactor without !important

* Refactor scss notation

* Formatting scss
2024-03-24 15:43:36 -07:00
Jorge Marcelo Risco
70d86ff096
i18n: pt-BR translation (#1024)
* i18n: pt-br

* i18n: pt-br translation

---------

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-03-24 09:40:01 -07:00
Kun-Szabó Kristóf
6efc4dd724
i18n: add Hungarian translations (#1023)
* add Hungarian translations

* typo: _ instead of - in hu-HU

* run prettier

* revert prettier messing up tsconfig

* Update hu-HU.ts
2024-03-24 09:35:07 -07:00
Xinyang Yu
85a737b4ee
docs: Update showcase.md (#1031) 2024-03-24 09:33:53 -07:00
dependabot[bot]
de6f469011
chore(deps-dev): bump @types/node from 20.11.25 to 20.11.29 (#1010)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.25 to 20.11.29.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 18:54:12 -07:00
dependabot[bot]
668640d641
chore(deps): bump shiki from 1.1.7 to 1.2.0 (#1011)
Bumps [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) from 1.1.7 to 1.2.0.
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Changelog](https://github.com/shikijs/shiki/blob/main/CHANGELOG.md)
- [Commits](https://github.com/shikijs/shiki/commits/v1.2.0/packages/shiki)

---
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 18:54:01 -07:00
dependabot[bot]
8007ec0f82
chore(deps): bump lightningcss from 1.24.0 to 1.24.1 (#1012)
Bumps [lightningcss](https://github.com/parcel-bundler/lightningcss) from 1.24.0 to 1.24.1.
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.24.0...v1.24.1)

---
updated-dependencies:
- dependency-name: lightningcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 18:53:19 -07:00
Emile Bangma
7e22c38f8e
fix(wikilinks): handle wikilinks inside tables seperately from other wikilinks (#1005)
* fix(wikilinks): handle wikilinks inside tables seperately from other wikilinks

* Prettier

* Cleaned up duplicate code

* Remove test logging

* Refactored and fixed for non-aliased wikilinks inside table

* Updated naming and comments

* Updated comment of wikilink regex

* Updated regex to match previous formatting

* Match table even if EOF is immediately after the table.

* Update quartz/plugins/transformers/ofm.ts

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* Change table escape replace to non-regex version

* Prettier

* Prettier

---------

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-03-17 18:16:04 -07:00
Jacky Zhao
daa8796554 fix: format 2024-03-17 18:15:42 -07:00
Jacky Zhao
91f0a2abb2 feat: support rich descriptions in tag listing page (closes #908) 2024-03-17 18:00:04 -07:00
makondratev
38d9d52137
feat(search): add search by title/content index and tag at the same time (#978)
* feat(search): add search by title/content index and tag at the same time

* fix(search): set search type to basic and remove tag from term for proper highlightning and scroll when searched by tag and title/content index

* fix(search): use indexOf to find space so it is easier to read

* fix(search): trim trailing whitespaces before splitting

* fix(search): set limit to 10000 for combined search mode (to make filter by tag more accurate)
2024-03-17 17:48:00 -07:00
Denis Bezykornov
253497cad4
docs: add config for Caddy server (#1002) 2024-03-16 10:16:58 -07:00
Emile Bangma
4691369abf
fix(wikilinks): only escape alias in wikilinks inside tables (#1000) 2024-03-16 09:23:08 -04:00
Aaron Pham
7164857f6e
chore(ofm): remove unused (#999)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-15 18:17:42 -07:00
dependabot[bot]
47024022e8
chore(deps-dev): bump @types/node from 20.11.24 to 20.11.25 (#990)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.24 to 20.11.25.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-15 18:29:14 -04:00
Mara-Li
b98e4be665
feat(i18n): Add French translation for reading time (#998)
Signed-off-by: Mara-Li <lili.simonetti@outlook.fr>
2024-03-15 18:28:31 -04:00
catcodeme
8be51a0504
fix: wikiLink in table (#993)
* fix: wikiLink in table

- update regexp to make '\' to group in alias
- handle alias using block_id

* style: format with prettier

* style: add comment for block_ref(without alias) in table

---------

Co-authored-by: hulinjiang <hulinjiang@58.com>
2024-03-14 23:25:01 -07:00
Linus Sehn
92cc23dc45
feat(plugin): citations (#984)
* feat: add rehype-citations

* feat: add citations transformer plugin

* feat: add rehype-rewrite

* feat: add csl option and add no-popover to citation links

* revert: add rehype-rewrite

04b2692 'feat: add rehype-rewrite'

* feat: use existing package for html manipulation

* fix: remove `console.log()`
2024-03-13 03:59:37 -04:00
dependabot[bot]
097abc3cda
chore(deps): bump async-mutex from 0.4.1 to 0.5.0 (#991)
Bumps [async-mutex](https://github.com/DirtyHairy/async-mutex) from 0.4.1 to 0.5.0.
- [Changelog](https://github.com/DirtyHairy/async-mutex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DirtyHairy/async-mutex/compare/v0.4.1...v0.5.0)

---
updated-dependencies:
- dependency-name: async-mutex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 13:41:48 -07:00
dependabot[bot]
a00324ddfd
chore(deps-dev): bump typescript from 5.3.3 to 5.4.2 (#989)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.3.3 to 5.4.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 13:41:41 -07:00
Mara-Li
9fff6d7d0d
fix: spelling error (#987)
I really don't know why I translated this like that into "pas trouvé", and it bugged me a lot. I finally fixed it…

Signed-off-by: Mara-Li <lili.simonetti@outlook.fr>
2024-03-11 09:46:53 -07:00
Matt Vogel
0f5a9d7b66
feat: separated content meta (#929)
to allow for CSS styling
2024-03-10 09:57:10 -07:00
kabirgh
b4236e5142
feat(perf:fast-rebuilds): Stop mutating resources param in ComponentResources emitter (#977)
* Stop mutating resources param in ComponentResources emitter

* Add done rebuilding log for fast rebuilds

* Move google font loading to Head component

* Simplify code and fix comment
2024-03-09 16:42:23 -08:00
Emile Bangma
6e0c102970
fix(transclusion): prevent duplicate transclusion if multiple transclusions are present. (#982) 2024-03-09 16:14:31 -08:00
Emile Bangma
94a54698ab
fix(resources): Use full path to font when cdnCache is false (#976) 2024-03-09 11:59:55 -05:00
Emile Bangma
2e9a0c21db
fix(description): first sentence no longer repeats until max length (#981) 2024-03-09 08:43:40 -08:00
Aaron Pham
b30a200bd4
fix(i18n): make sure to use correct fileData for manual localization (#975)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-08 09:14:22 -08:00
Emile Bangma
6d59aa8201
fix(description): counts characters instead of words (#972)
* fix(description): make sure description counts characters instead of words

* ref: removed duplicate ternary
2024-03-08 04:04:44 -05:00
Aaron Pham
141dd3b51f
fix(description): make sure to we join space correctly (#970)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-06 19:45:02 -08:00
Tyler Funk
3d4a94dda3
feat(analytics): Goatcounter support (#956)
* Add options to support goatcounter analytics

* goatcounter: support self-hosted

* Add to configuration docs for goatcounter settings

* use https instead of protocol-relative link for goatcounter js
2024-03-06 19:44:34 -08:00
Jacky Zhao
ba6c7a73d1 fix: remove extra # from tag content 2024-03-06 19:00:37 -08:00
Aaron Pham
f44e4d25e6
fix(tag): remove hash on main page (#969)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-06 18:24:50 -08:00
Matt Vogel
001c166825
fix(tag): move hash to sass styling only (#930) 2024-03-06 20:25:39 -05:00
Emile Bangma
e13cafe070
feat: support youtube playlist iframe (#968)
* feat: support youtube playlist iframe

* chore: updated Youtube embed documentation to include playlists
2024-03-06 09:45:31 -08:00
Jacky Zhao
0ca8a2ac7c chore: transclude subsection without dynamic regex construction 2024-03-05 22:17:58 -08:00
dependabot[bot]
a506cedd7a
chore(deps-dev): bump @types/node from 20.11.19 to 20.11.24 (#958)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.19 to 20.11.24.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 21:59:37 -08:00
Emile Bangma
5163504517
fix: transclude all subsections for embedded call (closes #963) (#964) 2024-03-06 00:53:35 -05:00
Aaron Pham
73a890ab12
revert: "fix(callout): reorder the plugins to render latex on callout… (#965)
This reverts commit 018c6358c4.
2024-03-05 19:37:28 -08:00
dependabot[bot]
83ab39c7bd
chore(deps): bump shiki from 1.1.6 to 1.1.7 (#959)
Bumps [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) from 1.1.6 to 1.1.7.
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Changelog](https://github.com/shikijs/shiki/blob/main/CHANGELOG.md)
- [Commits](https://github.com/shikijs/shiki/commits/v1.1.7/packages/shiki)

---
updated-dependencies:
- dependency-name: shiki
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 12:55:44 -08:00
dependabot[bot]
059dd1260e
chore(deps): bump preact-render-to-string from 6.3.1 to 6.4.0 (#960)
Bumps [preact-render-to-string](https://github.com/developit/preact-render-to-string) from 6.3.1 to 6.4.0.
- [Release notes](https://github.com/developit/preact-render-to-string/releases)
- [Changelog](https://github.com/preactjs/preact-render-to-string/blob/main/CHANGELOG.md)
- [Commits](https://github.com/developit/preact-render-to-string/compare/v6.3.1...6.4.0)

---
updated-dependencies:
- dependency-name: preact-render-to-string
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 12:55:02 -08:00
dependabot[bot]
a13d8e84b2
chore(deps): bump lightningcss from 1.23.0 to 1.24.0 (#961)
Bumps [lightningcss](https://github.com/parcel-bundler/lightningcss) from 1.23.0 to 1.24.0.
- [Release notes](https://github.com/parcel-bundler/lightningcss/releases)
- [Commits](https://github.com/parcel-bundler/lightningcss/compare/v1.23.0...v1.24.0)

---
updated-dependencies:
- dependency-name: lightningcss
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 12:54:21 -08:00
Aaron Pham
cec3662c74
feat(graph): focusOnHover (#954)
by default, globalGraph will enable focusOnHover, similar to Obsidian.

---------

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-04 15:09:20 -05:00
Emile Bangma
bcb5b2df09
feat(frontmatter): configure max length for description (#946)
* Sentence length check

* Replace external links with domain name.

* Updated documentation.

* Updated replacement values.

* Updated Regex based on feedback.

* Check description for undefined

* Updated external url transform regex.

* Updated formatting
2024-03-04 09:52:28 -08:00
sventec
bd05950c2d
fix(docs): correct ExplicitPublish as filters instead of transformers (#953) 2024-03-03 19:40:42 -05:00
Jacky Zhao
2a7e61ae2a feat: support transcluding codeblocks and blockquotes (closes #940) 2024-03-03 12:31:55 -08:00
dependabot[bot]
566f3cf9f8
chore(deps): bump remark-smartypants from 2.0.0 to 2.1.0 (#755)
Bumps [remark-smartypants](https://github.com/silvenon/remark-smartypants) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/silvenon/remark-smartypants/releases)
- [Commits](https://github.com/silvenon/remark-smartypants/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: remark-smartypants
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-03 11:27:54 -08:00
Aaron Pham
018c6358c4
fix(callout): reorder the plugins to render latex on callout title (closes #952) (#934)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-03-03 10:39:29 -08:00
HakuGuen
1c42b6365c
feat(i18n): add Vietnamese translation (#950) 2024-02-29 19:14:20 -05:00
kon-foo
f200a0be22
fix: correct umami host for self-hosted (#939)
* fixed umami script path for self-hosted version

* Update quartz/plugins/emitters/componentResources.ts

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

---------

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
2024-02-27 11:05:28 -05:00
kon-foo
b9dee0775c
docs: Clarifications in the Explorer Docs (#938)
add example to filter by tags.
2024-02-26 13:55:47 -08:00
dependabot[bot]
66a5855fad
chore(deps): bump chokidar from 3.5.3 to 3.6.0 (#937)
Bumps [chokidar](https://github.com/paulmillr/chokidar) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/paulmillr/chokidar/releases)
- [Commits](https://github.com/paulmillr/chokidar/compare/3.5.3...3.6.0)

---
updated-dependencies:
- dependency-name: chokidar
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 13:54:15 -08:00
dependabot[bot]
4957eaa2d0
chore(deps): bump preact from 10.19.5 to 10.19.6 (#935)
Bumps [preact](https://github.com/preactjs/preact) from 10.19.5 to 10.19.6.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.19.5...10.19.6)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 16:17:42 -05:00
Aaron Pham
6b90d03ca6
chore(type): export attribute for theme key (#933)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-26 09:53:45 -08:00
Aaron Pham
d6e79d1ea6
chore(types): update correct annotations for pages (#928) 2024-02-25 16:58:21 -08:00
Aaron Pham
2f10da7766
docs: fix tag page oops (#925) 2024-02-25 09:00:26 -08:00
Jacky Zhao
67647d9167 ci: also checkout and install node before tagging 2024-02-23 19:08:39 -08:00
Jacky Zhao
b88d3d292b ci: fix typo in runs-on 2024-02-23 19:05:26 -08:00
Jacky Zhao
c53fd5b56f ci: tag as a separate step 2024-02-23 19:04:38 -08:00
Jacky Zhao
d0c0daa4aa ci: fix autotag 2024-02-23 19:00:47 -08:00
Jacky Zhao
ea7122dd5a pkg: bump to 4.2.3 2024-02-23 18:52:28 -08:00
Jacky Zhao
2c74b05d1b fix(ci): autotag 2024-02-23 18:48:25 -08:00
kabirgh
a6417c447a
fix(fast rebuild): handle added an deleted markdown correctly (#921)
* Handle added files correctly

* Handle deletes properly

* addGraph renamed to mergeGraph
2024-02-23 18:40:42 -08:00
Jacky Zhao
6be1ed1ea2 docs(latex): mhchem 2024-02-23 17:45:41 -08:00
Eiko Wagenknecht
1929241a62
docs: update plugin documentation (#888)
* docs: first few plugins documented

* docs: move plugin info

* docs: move plugin docs to tag based system

* docs: update latex example code snippet

* docs: fix spelling of latex in title

* docs: add missing linebreak

* docs: remove plugin tag from feature pages

* docs: shorten titles

* docs: refine wording

* docs: move plugin details for frontmatter

* docs: add features/* tags

* docs: update latex example

* docs: make references more explicit

* docs: add stubs for the remaining plugins

* docs: more descriptions

* docs: fix feature tags

* docs: descriptions

* docs: new plugin pages

* docs: update configuration page

* docs: more plugin work

* docs: run prettier

* docs: remove comments in config file and add link to docs

* docs: minor fixes

* docs: run prettier

* docs: spelling

* docs: update docs/plugins/AliasRedirects.md

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* docs: update docs/plugins/Assets.md

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* docs: update docs/plugins/CNAME.md

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* docs: update docs/plugins/Static.md

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* docs: update docs

* docs: update docs/features/Mermaid diagrams.md

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* docs: update docs/plugins/RemoveDrafts.md

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* docs: update docs/plugins/Assets.md

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* docs: update docs/configuration.md

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* docs: update docs/configuration.md

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* docs: update docs/configuration.md

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* docs: some updates

* docs: work in review comments

---------

Signed-off-by: Eiko Wagenknecht <git@eiko-wagenknecht.de>
Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-02-23 12:07:53 -08:00
Jacky Zhao
421718958f fix(callouts): use user provided title instead of canonical for default title 2024-02-23 11:20:35 -08:00
Jacky Zhao
be9b6b3a1e fix(docs): make docs accurate to callout behaviour (closes #920) 2024-02-23 09:32:22 -08:00
KylinDC
fb66ae2838
deps(highlighting): migrate to shiki as shikiji has been archived (#918) 2024-02-22 21:56:26 -08:00
Aaron Pham
129e878b29
chore(img): return targetUrl as given href (#916)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-22 21:55:35 -08:00
Aaron Pham
96c7076fb5
feat(popover): add support for PDF (#913)
* feat(popover): add support for PDF

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>

* chore: split pdf by ';'

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>

* fix: remove unnecessary check

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>

---------

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-22 22:16:40 -05:00
Aaron Pham
345c347a56
chore: passing additional buildCtx to componentData (#914)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-22 17:51:07 -08:00
Aster Hu
916aedce40
docs: Add Aster's notebook to showcase.md (#912) 2024-02-21 19:04:36 -08:00
kon-foo
7dd596ebce
docs: Fix in explorer.md (#911) 2024-02-21 08:18:44 -08:00
Eiko Wagenknecht
1c3f3d03e1
fix(toc): correct type for minEntries param (#909) 2024-02-20 09:06:53 -08:00
Eiko Wagenknecht
3b266ee7d0
fix: add space and missing dot for listing pages (#907) 2024-02-20 09:45:10 -05:00
JONG HWAN KIM
fc5fa48bf1
feat(i18n): change itemsUnderFolder, itemsUnderTag translation of ko-KR (#905)
* feat(i18n): add Korean

* feat(i18n): add Korean

* feat(i18n): change itemsUnderFolder, itemsUnderTag translation of ko-KR
2024-02-19 22:36:54 -08:00
Eiko Wagenknecht
b6cf3df84f
fix: correctly parse falsy js as title (#900) 2024-02-19 13:49:07 -08:00
dependabot[bot]
779c501d9e
chore(deps): bump preact from 10.19.4 to 10.19.5 (#898)
Bumps [preact](https://github.com/preactjs/preact) from 10.19.4 to 10.19.5.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.19.4...10.19.5)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 13:47:07 -08:00
dependabot[bot]
f1619620d5
chore(deps): bump globby from 14.0.0 to 14.0.1 (#897)
Bumps [globby](https://github.com/sindresorhus/globby) from 14.0.0 to 14.0.1.
- [Release notes](https://github.com/sindresorhus/globby/releases)
- [Commits](https://github.com/sindresorhus/globby/compare/v14.0.0...v14.0.1)

---
updated-dependencies:
- dependency-name: globby
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 13:47:00 -08:00
dependabot[bot]
637e336cda
chore(deps-dev): bump @types/node from 20.11.16 to 20.11.19 (#899)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.16 to 20.11.19.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 13:46:27 -08:00
kon-foo
0493942c79
fix: remove assets via globs to avoid volume mount lock (#877)
* Fix docker volume lock issue by altering asset cleanup method
Modified build process to prevent the deletion of the output directory.

* Add fsOps utility for filesystem operations

* Use cleanDirectory in build process to fix volume lock issue

* applied prettier

* handle ENOENT error when output dir does not exist

* remove native function in favor of rimraf

* use path.join to concatenate paths
2024-02-19 11:04:27 -08:00
kabirgh
a67a8d7aa9
feat: implement getDependencyGraph for TagPage (#872)
* feat: implement getDependencyGraph for TagPage

* Only add file to dg if it has at least 1 tag
2024-02-19 13:58:15 -05:00
KylinDC
e85ea49000
feat(i18n): add Simplified Chinese (#896) 2024-02-19 13:31:09 -05:00
kon-foo
3e09b05468
docs: add self-hosting section (#883)
* Add Self-Hosting section
Add Nginx section

* run prettier
2024-02-19 12:50:40 -05:00
Leonardo Ledda
d9e8ffc78c
feat(i18n): Add Italian (#893)
Signed-off-by: Leonardo Ledda <leonardoledda@gmail.com>
2024-02-19 12:50:01 -05:00
Eiko Wagenknecht
efd46f84de
fix(frontmatter): delimiters parameter was not passed (#885)
* fix: delimiters parameter was not passed

Signed-off-by: Eiko Wagenknecht <git@eiko-wagenknecht.de>

* fix: remove unneeded undefined

---------

Signed-off-by: Eiko Wagenknecht <git@eiko-wagenknecht.de>
2024-02-19 00:08:36 -08:00
s-crypt
739c2e2cc8
perf(cdn): CDNJS instead of JSDelivr (#891) 2024-02-18 20:26:04 -08:00
JONG HWAN KIM
b1a105371b
feat(i18n): add Korean (#889)
* feat(i18n): add Korean

* feat(i18n): add Korean
2024-02-18 17:37:59 -05:00
makondratev
8c5c5f9130
feat(i18n): add Russian (#886) 2024-02-18 13:54:37 -05:00
Jacky Zhao
aa24a62ae7 fix(breadcrumbs): calculate trailing slash for tag hierarchies (closes #873) 2024-02-17 11:12:35 -08:00
Jacky Zhao
a6690c6503 fix(style): bold should use semibold 2024-02-17 10:57:59 -08:00
Jacky Zhao
06e3f8b93d fix(style): introduce semiBoldWeight and various improvements to reduce CLS 2024-02-17 10:34:51 -08:00
Silviu Lorenț
fa2ea2896f
feat: add user-defined config for syntax highlighting plugin (#869)
* feat: add user-defined options to syntax highlighting plugin

* feat: add default syntax highlighting config to `quartz.config.ts`

* chore: refactor according to @aarnphm's review

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>

* chore: run Prettier on `quartz/plugins/transformers/syntax.ts`

* Update quartz/plugins/transformers/syntax.ts

Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

* Update syntax.ts

---------

Co-authored-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
2024-02-17 10:23:45 -08:00
kabirgh
5af707ea20
fix/feat(fast rebuild): re-render transclusions in normal and fastRebuild mode (#842)
* Re-render transclusions in normal watch mode

* Include transclusions in ContentPage getDependencyGraph

* Address PR comments
2024-02-17 09:45:01 -08:00
kabirgh
823d952922
feat: implement getDependencyGraph for AliasRedirects emitter (#860) 2024-02-15 19:50:48 -05:00
kabirgh
78a408c96a
feat: implement getDependencyGraph for FolderPage (#849) 2024-02-15 19:50:33 -05:00
David Fischer
6c8023463d
Add support for image popovers (#854)
* feat(popover): Add support for images

* fix: run prettier

* feat(popover): use switch logic for content types & adjust styles

* feat(popover): Add content type data tag for popover-inner class
2024-02-14 15:41:13 -05:00
Aaron Bull Schaefer
2041341d9f
docs: workaround for shallow clones on Cloudflare Pages (#868)
Rather than recommend a different hosting provider, Cloudflare Pages
users that prioritize the `git` method for their `CreatedModifiedDate`
configuration can preface the build command with a means of fetching the
required repository history.

See:
- https://gohugo.io/methods/page/gitinfo/#hosting-considerations
2024-02-14 09:41:44 -08:00
Aaron Pham
21c6bbf302
chore(types): add additional hint for LSP support (#864)
Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2024-02-13 23:53:44 -05:00
Jacky Zhao
b87a701ff7 fix: base.com not being resolved properly with joinSegments 2024-02-13 01:27:27 -08:00
Lin
880a9511b6
fix: incorrect link resolution for transclusion in root index file (#853)
Co-authored-by: Lauréline Nevin <laureline.nevin@unicaen.fr>
2024-02-13 03:11:16 -05:00
dependabot[bot]
a31e3f9458
chore(deps): bump @floating-ui/dom from 1.6.1 to 1.6.3 (#857)
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.6.1 to 1.6.3.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.6.3/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-13 00:21:45 -05:00
dependabot[bot]
2c06e68ba6
chore(deps): bump preact from 10.19.3 to 10.19.4 (#858)
Bumps [preact](https://github.com/preactjs/preact) from 10.19.3 to 10.19.4.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.19.3...10.19.4)

---
updated-dependencies:
- dependency-name: preact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-13 00:21:30 -05:00
dependabot[bot]
4a28d0e5d1
chore(deps-dev): bump tsx from 4.7.0 to 4.7.1 (#859)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.7.0 to 4.7.1.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/develop/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.7.0...v4.7.1)

---
updated-dependencies:
- dependency-name: tsx
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-13 00:21:13 -05:00
Jacky Zhao
a7325eadc1 fix(analytics): umami custom host should be a string (closes #852) 2024-02-12 09:01:05 -08:00
Emile Bangma
5dc4f21a4b
feat(i18n): localize the min read string for the nl-NL locale (#850)
* Update min read translation

* Added nl_BE to Dutch

Added Flemish (nl_BE) to point to nl.

* Removed period to match other translations
2024-02-12 08:58:00 -08:00
Jacky Zhao
76f295620c feat: add transclude-src to transclude 'link to original' 2024-02-12 08:52:00 -08:00
kabirgh
226891b9b1
fix(fast rebuild): call only required emitters, don't always copy assets (#845)
* fix(fast rebuild): call only required emitters, don't always copy assets

* Type function
2024-02-11 12:20:44 -08:00
Jacky Zhao
389f2e8bee fix(ofm): allow diacretic marks in tag regex (closes #830) 2024-02-11 12:12:01 -08:00
dependabot[bot]
998198cffb
chore(deps): bump esbuild-sass-plugin from 2.16.0 to 2.16.1 (#778)
Bumps [esbuild-sass-plugin](https://github.com/glromeo/esbuild-sass-plugin) from 2.16.0 to 2.16.1.
- [Release notes](https://github.com/glromeo/esbuild-sass-plugin/releases)
- [Commits](https://github.com/glromeo/esbuild-sass-plugin/compare/v2.16.0...v2.16.1)

---
updated-dependencies:
- dependency-name: esbuild-sass-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 11:27:16 -08:00
Jacky Zhao
4a6a44950f fix(breadcrumbs): folder index by full path rather than folder name (closes #676) 2024-02-11 11:26:24 -08:00
Silviu Lorenț
2b39bd93f3
feat(i18n): localize the min read string for the ro-RO locale (#847)
* feat(i18n): localize `min read` string for `ro-RO` locale

* chore: run Prettier on `quartz/i18n/locales/ro-RO.ts`
2024-02-11 11:23:58 -08:00
Neel Shah
b5295e0f26
fix: breadcrumbs displayName issue for file names ending with index (#839) 2024-02-11 11:08:12 -08:00
Jacky Zhao
ab0e20b4d0 chore: refactor out and export endsWith 2024-02-11 10:57:24 -08:00
Silviu Lorenț
af5f5abad4
docs: add documentation for Umami analytics integration (#846) 2024-02-11 10:51:10 -08:00
Alq
3518ca9e2a
feat(i18n): localize the min read string (#838)
* feat(i18n): localize the min read string fixes #825

* chore: format
2024-02-11 10:43:08 -08:00
162 changed files with 5094 additions and 1693 deletions

View File

@ -7,6 +7,7 @@ on:
push:
branches:
- v4
workflow_dispatch:
jobs:
build-and-test:
@ -18,17 +19,17 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Cache dependencies
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -46,8 +47,26 @@ jobs:
- name: Ensure Quartz builds, check bundle info
run: npx quartz build --bundleInfo
- name: Create release tag
uses: Klemensas/action-autotag@stable
publish-tag:
if: ${{ github.repository == 'jackyzha0/quartz' && github.ref == 'refs/heads/v4' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
tag_prefix: "v"
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Get package version
run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
- name: Create release tag
uses: pkgdeps/git-tag-action@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_repo: ${{ github.repository }}
version: ${{ env.PACKAGE_VERSION }}
git_commit_sha: ${{ github.sha }}
git_tag_prefix: "v"

1
.node-version Normal file
View File

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

View File

@ -129,11 +129,11 @@ export default (() => {
return <button id="btn">Click me</button>
}
YourComponent.beforeDOM = `
YourComponent.beforeDOMLoaded = `
console.log("hello from before the page loads!")
`
YourComponent.afterDOM = `
YourComponent.afterDOMLoaded = `
document.getElementById('btn').onclick = () => {
alert('button clicked!')
}
@ -180,7 +180,7 @@ export default (() => {
return <button id="btn">Click me</button>
}
YourComponent.afterDOM = script
YourComponent.afterDOMLoaded = script
return YourComponent
}) satisfies QuartzComponentConstructor
```

View File

@ -53,12 +53,12 @@ All transformer plugins must define at least a `name` field to register the plug
Normally for both `remark` and `rehype`, you can find existing plugins that you can use to . If you'd like to create your own `remark` or `rehype` plugin, checkout the [guide to creating a plugin](https://unifiedjs.com/learn/guide/create-a-plugin/) using `unified` (the underlying AST parser and transformer library).
A good example of a transformer plugin that borrows from the `remark` and `rehype` ecosystems is the [[Latex]] plugin:
A good example of a transformer plugin that borrows from the `remark` and `rehype` ecosystems is the [[plugins/Latex|Latex]] plugin:
```ts title="quartz/plugins/transformers/latex.ts"
import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import rehypeMathjax from "rehype-mathjax/svg.js"
import rehypeMathjax from "rehype-mathjax/svg"
import { QuartzTransformerPlugin } from "../types"
interface Options {
@ -84,10 +84,14 @@ export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => {
externalResources() {
if (engine === "katex") {
return {
css: ["https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css"],
css: [
// base css
"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",
],
js: [
{
src: "https://cdn.jsdelivr.net/npm/katex@0.16.7/dist/contrib/copy-tex.min.js",
// fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md
src: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/copy-tex.min.js",
loadTime: "afterDOMReady",
contentType: "external",
},
@ -256,11 +260,11 @@ export const ContentPage: QuartzEmitterPlugin = () => {
...defaultContentPageLayout,
pageBody: Content(),
}
const { head, header, beforeBody, pageBody, left, right, footer } = layout
const { head, header, beforeBody, pageBody, afterBody, left, right, footer } = layout
return {
name: "ContentPage",
getQuartzComponents() {
return [head, ...header, ...beforeBody, pageBody, ...left, ...right, footer]
return [head, ...header, ...beforeBody, pageBody, ...afterBody, ...left, ...right, footer]
},
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
const cfg = ctx.cfg.configuration

View File

@ -48,4 +48,4 @@ Here are the main types of slugs with a rough description of each type of path:
- `SimpleSlug`: cannot be relative and shouldn't have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path.
- `RelativeURL`: must start with `.` or `..` to indicate it's a relative URL. Shouldn't have `/index` as an ending or a file extension but can contain a trailing slash.
To get a clearer picture of how these relate to each other, take a look at the path tests in `quartz/path.test.ts`.
To get a clearer picture of how these relate to each other, take a look at the path tests in `quartz/util/path.test.ts`.

View File

@ -29,6 +29,7 @@ Some common frontmatter fields that are natively supported by Quartz:
- `title`: Title of the page. If it isn't provided, Quartz will use the name of the file as the title.
- `description`: Description of the page used for link previews.
- `permalink`: A custom URL for the page that will remain constant even if the path to the file changes.
- `aliases`: Other names for this note. This is a list of strings.
- `tags`: Tags for this note.
- `draft`: Whether to publish the page or not. This is one way to make [[private pages|pages private]] in Quartz.
@ -38,3 +39,7 @@ Some common frontmatter fields that are natively supported by Quartz:
When your Quartz is at a point you're happy with, you can save your changes to GitHub.
First, make sure you've [[setting up your GitHub repository|already setup your GitHub repository]] and then do `npx quartz sync`.
## Customization
Frontmatter parsing for `title`, `tags`, `aliases` and `cssclasses` is a functionality of the [[Frontmatter]] plugin, `date` is handled by the [[CreatedModifiedDate]] plugin and `description` by the [[Description]] plugin. See the plugin pages for customization options.

View File

@ -25,16 +25,21 @@ This part of the configuration concerns anything that can affect the whole site.
- `enablePopovers`: whether to enable [[popover previews]] on your site.
- `analytics`: what to use for analytics on your site. Values can be
- `null`: don't use analytics;
- `{ provider: 'plausible' }`: use [Plausible](https://plausible.io/), a privacy-friendly alternative to Google Analytics; or
- `{ provider: 'google', tagId: <your-google-tag> }`: use Google Analytics
- `{ provider: 'google', tagId: '<your-google-tag>' }`: use Google Analytics;
- `{ provider: 'plausible' }` (managed) or `{ provider: 'plausible', host: '<your-plausible-host>' }` (self-hosted): use [Plausible](https://plausible.io/);
- `{ provider: 'umami', host: '<your-umami-host>', websiteId: '<your-umami-website-id>' }`: use [Umami](https://umami.is/);
- `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id' }` (managed) or `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id', host: 'my-goatcounter-domain.com', scriptSrc: 'https://my-url.to/counter.js' }` (self-hosted) use [GoatCounter](https://goatcounter.com);
- `{ provider: 'posthog', apiKey: '<your-posthog-project-apiKey>', host: '<your-posthog-host>' }`: use [Posthog](https://posthog.com/);
- `{ provider: 'tinylytics', siteId: '<your-site-id>' }`: use [Tinylytics](https://tinylytics.app/);
- `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com);
- `locale`: used for [[i18n]] and date formatting
- `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes.
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
- Note that Quartz 4 will avoid using this as much as possible and use relative URLs whenever it can to make sure your site works no matter _where_ you end up actually deploying it.
- `ignorePatterns`: a list of [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details.
- `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings.
- `theme`: configure how the site looks.
- `cdnCaching`: Whether to use Google CDN to cache the fonts (generally will be faster). Disable this if you want Quartz to be self-contained. Default to `true`
- `cdnCaching`: If `true` (default), use Google CDN to cache the fonts. This will generally will be faster. Disable (`false`) this if you want Quartz to download the fonts to be self-contained.
- `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here.
- `header`: Font to use for headers
- `code`: Font for inline and block quotes.
@ -48,6 +53,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
@ -55,7 +61,7 @@ You can think of Quartz plugins as a series of transformations over content.
![[quartz transform pipeline.png]]
```ts
```ts title="quartz.config.ts"
plugins: {
transformers: [...],
filters: [...],
@ -63,22 +69,40 @@ plugins: {
}
```
- [[making plugins#Transformers|Transformers]] **map** over content (e.g. parsing frontmatter, generating a description)
- [[making plugins#Filters|Filters]] **filter** content (e.g. filtering out drafts)
- [[making plugins#Emitters|Emitters]] **reduce** over content (e.g. creating an RSS feed or pages that list all files with a specific tag)
- [[tags/plugin/transformer|Transformers]] **map** over content (e.g. parsing frontmatter, generating a description)
- [[tags/plugin/filter|Filters]] **filter** content (e.g. filtering out drafts)
- [[tags/plugin/emitter|Emitters]] **reduce** over content (e.g. creating an RSS feed or pages that list all files with a specific tag)
By adding, removing, and reordering plugins from the `tranformers`, `filters`, and `emitters` fields, you can customize the behaviour of Quartz.
You can customize the behaviour of Quartz by adding, removing and reordering plugins in the `transformers`, `filters` and `emitters` fields.
> [!note]
> Each node is modified by every transformer _in order_. Some transformers are position-sensitive so you may need to take special note of whether it needs come before or after any other particular plugins.
> Each node is modified by every transformer _in order_. Some transformers are position sensitive, so you may need to pay particular attention to whether they need to come before or after certain other plugins.
Additionally, plugins may also have their own configuration settings that you can pass in. For example, the [[Latex]] plugin allows you to pass in a field specifying the `renderEngine` to choose between Katex and MathJax.
You should take care to add the plugin to the right entry corresponding to its plugin type. For example, to add the [[ExplicitPublish]] plugin (a [[tags/plugin/filter|Filter]]), you would add the following line:
```ts
```ts title="quartz.config.ts"
filters: [
...
Plugin.ExplicitPublish(),
...
],
```
To remove a plugin, you should remove all occurrences of it in the `quartz.config.ts`.
To customize plugins further, some plugins may also have their own configuration settings that you can pass in. If you do not pass in a configuration, the plugin will use its default settings.
For example, the [[plugins/Latex|Latex]] plugin allows you to pass in a field specifying the `renderEngine` to choose between Katex and MathJax.
```ts title="quartz.config.ts"
transformers: [
Plugin.FrontMatter(), // uses default options
Plugin.Latex({ renderEngine: "katex" }), // specify some options
Plugin.FrontMatter(), // use default options
Plugin.Latex({ renderEngine: "katex" }), // set some custom options
]
```
If you'd like to make your own plugins, read the guide on [[making plugins]] for more information.
Some plugins are included by default in the[ `quartz.config.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz.config.ts), but there are more available.
You can see a list of all plugins and their configuration options [[tags/plugin|here]].
If you'd like to make your own plugins, see the [[making plugins|making custom plugins]] guide.

View File

@ -1,6 +1,7 @@
---
title: LaTeX
tags:
- plugin/transformer
- feature/transformer
---
Quartz uses [Katex](https://katex.org/) by default to typeset both inline and block math expressions at build time.
@ -38,6 +39,20 @@ a & b & c
\end{bmatrix}
$$
$$
\begin{array}{rll}
E \psi &= H\psi & \text{Expanding the Hamiltonian Operator} \\
&= -\frac{\hbar^2}{2m}\frac{\partial^2}{\partial x^2} \psi + \frac{1}{2}m\omega x^2 \psi & \text{Using the ansatz $\psi(x) = e^{-kx^2}f(x)$, hoping to cancel the $x^2$ term} \\
&= -\frac{\hbar^2}{2m} [4k^2x^2f(x)+2(-2kx)f'(x) + f''(x)]e^{-kx^2} + \frac{1}{2}m\omega x^2 f(x)e^{-kx^2} &\text{Removing the $e^{-kx^2}$ term from both sides} \\
& \Downarrow \\
Ef(x) &= -\frac{\hbar^2}{2m} [4k^2x^2f(x)-4kxf'(x) + f''(x)] + \frac{1}{2}m\omega x^2 f(x) & \text{Choosing $k=\frac{im}{2}\sqrt{\frac{\omega}{\hbar}}$ to cancel the $x^2$ term, via $-\frac{\hbar^2}{2m}4k^2=\frac{1}{2}m \omega$} \\
&= -\frac{\hbar^2}{2m} [-4kxf'(x) + f''(x)] \\
\end{array}
$$
> [!warn]
> Due to limitations in the [underlying parsing library](https://github.com/remarkjs/remark-math), block math in Quartz requires the `$$` delimiters to be on newlines like above.
### Inline Math
Similarly, inline math can be rendered by delimiting math expression with a single `$`. For example, `$e^{i\pi} = -1$` produces $e^{i\pi} = -1$
@ -53,11 +68,15 @@ For example:
- Incorrect: `I have $1 and you have $2` produces I have $1 and you have $2
- Correct: `I have \$1 and you have \$2` produces I have \$1 and you have \$2
## MathJax
### Using mhchem
In `quartz.config.ts`, you can configure Quartz to use [MathJax SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html) by replacing `Plugin.Latex({ renderEngine: 'katex' })` with `Plugin.Latex({ renderEngine: 'mathjax' })`
Add the following import to the top of `quartz/plugins/transformers/latex.ts` (before all the other
imports):
```ts title="quartz/plugins/transformers/latex.ts"
import "katex/contrib/mhchem"
```
## Customization
- Removing Latex support: remove all instances of `Plugin.Latex()` from `quartz.config.ts`.
- Plugin: `quartz/plugins/transformers/latex.ts`
Latex parsing is a functionality of the [[plugins/Latex|Latex]] plugin. See the plugin page for customization options.

View File

@ -1,9 +1,15 @@
---
title: "Mermaid Diagrams"
tags:
- feature/transformer
---
Quartz supports Mermaid which allows you to add diagrams and charts to your notes. Mermaid supports a range of diagrams, such as [flow charts](https://mermaid.js.org/syntax/flowchart.html), [sequence diagrams](https://mermaid.js.org/syntax/sequenceDiagram.html), and [timelines](https://mermaid.js.org/syntax/timeline.html). This is enabled as a part of [[Obsidian compatibility]] and can be configured and enabled/disabled from that plugin.
By default, Quartz will render Mermaid diagrams to match the site theme.
> [!warning]
> Wondering why Mermaid diagrams may not be showing up even if you have them enabled? You may need to reorder your plugins so that `Plugin.ObsidianFlavoredMarkdown()` is _after_ `Plugin.SyntaxHighlighting()`.
> Wondering why Mermaid diagrams may not be showing up even if you have them enabled? You may need to reorder your plugins so that [[ObsidianFlavoredMarkdown]] is _after_ [[SyntaxHighlighting]].
## Syntax

View File

@ -1,33 +1,17 @@
---
title: "Obsidian Compatibility"
tags:
- plugin/transformer
- feature/transformer
---
Quartz was originally designed as a tool to publish Obsidian vaults as websites. Even as the scope of Quartz has widened over time, it hasn't lost the ability to seamlessly interoperate with Obsidian.
By default, Quartz ships with `Plugin.ObsidianFlavoredMarkdown` which is a transformer plugin that adds support for [Obsidian Flavored Markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown). This includes support for features like [[wikilinks]] and [[Mermaid diagrams]].
By default, Quartz ships with the [[ObsidianFlavoredMarkdown]] plugin, which is a transformer plugin that adds support for [Obsidian Flavored Markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown). This includes support for features like [[wikilinks]] and [[Mermaid diagrams]].
It also ships with support for [frontmatter parsing](https://help.obsidian.md/Editing+and+formatting/Properties) with the same fields that Obsidian uses through the `Plugin.FrontMatter` transformer plugin.
It also ships with support for [frontmatter parsing](https://help.obsidian.md/Editing+and+formatting/Properties) with the same fields that Obsidian uses through the [[Frontmatter]] transformer plugin.
Finally, Quartz also provides `Plugin.CrawlLinks` which allows you to customize Quartz's link resolution behaviour to match Obsidian.
Finally, Quartz also provides [[CrawlLinks]] plugin, which allows you to customize Quartz's link resolution behaviour to match Obsidian.
## Configuration
- Frontmatter parsing:
- Disabling: remove all instances of `Plugin.FrontMatter()` from `quartz.config.ts`.
- Customize default values for frontmatter: edit `quartz/plugins/transformers/frontmatter.ts`
- Obsidian Flavored Markdown:
- Disabling: remove all instances of `Plugin.ObsidianFlavoredMarkdown()` from `quartz.config.ts`
- Customizing features: `Plugin.ObsidianFlavoredMarkdown` has several other options to toggle on and off:
- `comments`: whether to enable `%%` style Obsidian comments. Defaults to `true`
- `highlight`: whether to enable `==` style highlights. Defaults to `true`
- `wikilinks`: whether to enable turning [[wikilinks]] into regular links. Defaults to `true`
- `callouts`: whether to enable [[callouts]]. Defaults to `true`
- `mermaid`: whether to enable [[Mermaid diagrams]]. Defaults to `true`
- `parseTags`: whether to try and parse tags in the content body. Defaults to `true`
- `parseArrows`: whether to try and parse arrows in the content body. Defaults to `true`.
- `enableInHtmlEmbed`: whether to try and parse Obsidian flavoured markdown in raw HTML. Defaults to `false`
- `enableYouTubeEmbed`: whether to enable embedded YouTube videos using external image Markdown syntax. Defaults to `false`
- Link resolution behaviour:
- Disabling: remove all instances of `Plugin.CrawlLinks()` from `quartz.config.ts`
- Changing link resolution preference: set `markdownLinkResolution` to one of `absolute`, `relative` or `shortest`
This functionality is provided by the [[ObsidianFlavoredMarkdown]], [[Frontmatter]] and [[CrawlLinks]] plugins. See the plugin pages for customization options.

View File

@ -1,11 +1,12 @@
---
title: "OxHugo Compatibility"
tags:
- plugin/transformer
- feature/transformer
---
[org-roam](https://www.orgroam.com/) is a plain-text personal knowledge management system for [emacs](https://en.wikipedia.org/wiki/Emacs). [ox-hugo](https://github.com/kaushalmodi/ox-hugo) is org exporter backend that exports `org-mode` files to [Hugo](https://gohugo.io/) compatible Markdown.
Because the Markdown generated by ox-hugo is not pure Markdown but Hugo specific, we need to transform it to fit into Quartz. This is done by `Plugin.OxHugoFlavouredMarkdown`. Even though this [[making plugins|plugin]] was written with `ox-hugo` in mind, it should work for any Hugo specific Markdown.
Because the Markdown generated by ox-hugo is not pure Markdown but Hugo specific, we need to transform it to fit into Quartz. This is done by the [[OxHugoFlavoredMarkdown]] plugin. Even though this plugin was written with `ox-hugo` in mind, it should work for any Hugo specific Markdown.
```typescript title="quartz.config.ts"
plugins: {
@ -25,15 +26,4 @@ Quartz by default doesn't understand `org-roam` files as they aren't Markdown. Y
## Configuration
- Link resolution
- `wikilinks`: Whether to replace `{{ relref }}` with Quartz [[wikilinks]]
- `removePredefinedAnchor`: Whether to remove [pre-defined anchor set by ox-hugo](https://ox-hugo.scripter.co/doc/anchors/).
- Image handling
- `replaceFigureWithMdImg`: Whether to replace `<figure/>` with `![]()`
- Formatting
- `removeHugoShortcode`: Whether to remove hugo shortcode syntax (`{{}}`)
- `replaceOrgLatex`: Whether to replace org-mode formatting for latex fragments with what `Plugin.Latex` supports.
> [!warning]
>
> While you can use `Plugin.OxHugoFlavoredMarkdown` and `Plugin.ObsidianFlavoredMarkdown` together, it's not recommended because it might mutate the file in unexpected ways. Use with caution.
This functionality is provided by the [[OxHugoFlavoredMarkdown]] plugin. See the plugin page for customization options.

View File

@ -1,7 +1,5 @@
Quartz creates an RSS feed for all the content on your site by generating an `index.xml` file that RSS readers can subscribe to. Because of the RSS spec, this requires the `baseUrl` property in your [[configuration]] to be set properly for RSS readers to pick it up properly.
Quartz emits an RSS feed for all the content on your site by generating an `index.xml` file that RSS readers can subscribe to. Because of the RSS spec, this requires the `baseUrl` property in your [[configuration]] to be set properly for RSS readers to pick it up properly.
## Configuration
- Remove RSS feed: set the `enableRSS` field of `Plugin.ContentIndex` in `quartz.config.ts` to be `false`.
- Change number of entries: set the `rssLimit` field of `Plugin.ContentIndex` to be the desired value. It defaults to latest 10 items.
- Use rich HTML output in RSS: set `rssFullHtml` field of `Plugin.ContentIndex` to be `true`.
This functionality is provided by the [[ContentIndex]] plugin. See the plugin page for customization options.

View File

@ -1,7 +1,7 @@
---
title: Callouts
tags:
- plugin/transformer
- feature/transformer
---
Quartz supports the same Admonition-callout syntax as Obsidian.
@ -19,12 +19,13 @@ This includes
See [documentation on supported types and syntax here](https://help.obsidian.md/Editing+and+formatting/Callouts).
> [!warning]
> Wondering why callouts may not be showing up even if you have them enabled? You may need to reorder your plugins so that `Plugin.ObsidianFlavoredMarkdown()` is _after_ `Plugin.SyntaxHighlighting()`.
> Wondering why callouts may not be showing up even if you have them enabled? You may need to reorder your plugins so that [[ObsidianFlavoredMarkdown]] is _after_ [[SyntaxHighlighting]].
## Customization
- Disable callouts: simply pass `callouts: false` to the plugin: `Plugin.ObsidianFlavoredMarkdown({ callouts: false })`
- Editing icons: `quartz/styles/callouts.scss`
The callouts are a functionality of the [[ObsidianFlavoredMarkdown]] plugin. See the plugin page for how to enable or disable them.
You can edit the icons by customizing `quartz/styles/callouts.scss`.
### Add custom callouts
@ -55,50 +56,41 @@ By default, custom callouts are handled by applying the `note` style. To make fa
> >
> > > [!example] You can even use multiple layers of nesting.
> [!EXAMPLE] Examples
>
> Aliases: example
> [!note]
> Aliases: "note"
> [!note] Notes
>
> Aliases: note
> [!abstract]
> Aliases: "abstract", "summary", "tldr"
> [!abstract] Summaries
>
> Aliases: abstract, summary, tldr
> [!info]
> Aliases: "info"
> [!info] Info
>
> Aliases: info, todo
> [!todo]
> Aliases: "todo"
> [!tip] Hint
>
> Aliases: tip, hint, important
> [!tip]
> Aliases: "tip", "hint", "important"
> [!success] Success
>
> Aliases: success, check, done
> [!success]
> Aliases: "success", "check", "done"
> [!question] Question
>
> Aliases: question, help, faq
> [!question]
> Aliases: "question", "help", "faq"
> [!warning] Warning
>
> Aliases: warning, caution, attention
> [!warning]
> Aliases: "warning", "attention", "caution"
> [!failure] Failure
>
> Aliases: failure, fail, missing
> [!failure]
> Aliases: "failure", "missing", "fail"
> [!danger] Error
>
> Aliases: danger, error
> [!danger]
> Aliases: "danger", "error"
> [!bug] Bug
>
> Aliases: bug
> [!bug]
> Aliases: "bug"
> [!quote] Quote
>
> Aliases: quote, cite
> [!example]
> Aliases: "example"
> [!quote]
> Aliases: "quote", "cite"

83
docs/features/comments.md Normal file
View File

@ -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"
}
}
```

View File

@ -42,7 +42,7 @@ When passing in your own options, you can omit any or all of these fields if you
Want to customize it even more?
- Removing table of contents: remove `Component.Explorer()` from `quartz.layout.ts`
- Removing explorer: remove `Component.Explorer()` from `quartz.layout.ts`
- (optional): After removing the explorer component, you can move the [[table of contents | Table of Contents]] component back to the `left` part of the layout
- Changing `sort`, `filter` and `map` behavior: explained in [[#Advanced customization]]
- Component:
@ -61,7 +61,7 @@ export class FileNode {
children: FileNode[] // children of current node
name: string // last part of slug
displayName: string // what actually should be displayed in the explorer
file: QuartzPluginData | null // set if node is a file, see `QuartzPluginData` for more detail
file: QuartzPluginData | null // if node is a file, this is the file's metadata. See `QuartzPluginData` for more detail
depth: number // depth of current node
... // rest of implementation
@ -167,6 +167,19 @@ Component.Explorer({
You can customize this by changing the entries of the `omit` set. Simply add all folder or file names you want to remove.
### Remove files by tag
You can access the frontmatter of a file by `node.file?.frontmatter?`. This allows you to filter out files based on their frontmatter, for example by their tags.
```ts title="quartz.layout.ts"
Component.Explorer({
filterFn: (node) => {
// exclude files with the tag "explorerexclude"
return node.file?.frontmatter?.tags?.includes("explorerexclude") !== true
},
})
```
### Show every element in explorer
To override the default filter function that removes the `tags` folder from the explorer, you can set the filter function to `undefined`.

View File

@ -1,18 +1,20 @@
---
title: Folder and Tag Listings
tags:
- plugin/emitter
- feature/emitter
---
Quartz creates listing pages for any folders and tags you have.
Quartz emits listing pages for any folders and tags you have.
## Folder Listings
Quartz will generate an index page for all the pages under that folder. This includes any content that is multiple levels deep.
Additionally, Quartz will also generate pages for subfolders. Say you have a note in a nested folder `content/abc/def/note.md`. Then, Quartz would generate a page for all the notes under `abc` _and_ a page for all the notes under `abc/def`.
Additionally, Quartz will also generate pages for subfolders. Say you have a note in a nested folder `content/abc/def/note.md`. Then Quartz would generate a page for all the notes under `abc` _and_ a page for all the notes under `abc/def`.
By default, Quartz will title the page `Folder: <name of folder>` and no description. You can override this by creating an `index.md` file in the folder with the `title` [[authoring content#Syntax|frontmatter]] field. Any content you write in this file will also be used in the description of the folder.
You can link to the folder listing by referencing its name, plus a trailing slash, like this: `[[advanced/]]` (results in [[advanced/]]).
By default, Quartz will title the page `Folder: <folder name>` and no description. You can override this by creating an `index.md` file in the folder with the `title` [[authoring content#Syntax|frontmatter]] field. Any content you write in this file will also be used in the folder description.
For example, for the folder `content/posts`, you can add another file `content/posts/index.md` to add a specific description for it.
@ -20,13 +22,12 @@ For example, for the folder `content/posts`, you can add another file `content/p
Quartz will also create an index page for each unique tag in your vault and render a list of all notes with that tag.
Quartz also supports tag hierarchies as well (e.g. `plugin/emitter`) and will also render a separate tag page for each layer of the tag hierarchy. It will also create a default global tag index page at `/tags` that displays a list of all the tags in your Quartz.
Quartz also supports tag hierarchies as well (e.g. `plugin/emitter`) and will also render a separate tag page for each level of the tag hierarchy. It will also create a default global tag index page at `/tags` that displays a list of all the tags in your Quartz.
Like folder listings, you can also provide a description and title for a tag page by creating a file for each tag. For example, if you wanted to create a custom description for the #component tag, you would create a file at `content/tags/component.md` with a title and description.
You can link to the tag listing by referencing its name with a `tag/` prefix, like this: `[[tags/plugin]]` (results in [[tags/plugin]]).
As with folder listings, you can also provide a description and title for a tag page by creating a file for each tag. For example, if you wanted to create a custom description for the #component tag, you would create a file at `content/tags/component.md` with a title and description.
## Customization
The layout for both the folder and content pages can be customized. By default, they use the `defaultListPageLayout` in `quartz.layouts.ts`. If you'd like to make more involved changes to the layout and don't mind editing some [[creating components|Quartz components]], you can take a look at `quartz/components/pages/FolderContent.tsx` and `quartz/components/pages/TagContent.tsx` respectively.
- Removing folder listings: remove `Plugin.FolderPage()` from `emitters` in `quartz.config.ts`
- Removing tag listings: remove `Plugin.TagPage()` from `emitters` in `quartz.config.ts`
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.

View File

@ -8,6 +8,8 @@ By default, Quartz only fetches previews for pages inside your vault due to [COR
When [[creating components|creating your own components]], you can include this `popover-hint` class to also include it in the popover.
Similar to Obsidian, [[quartz layout.png|images referenced using wikilinks]] can also be viewed as popups.
## Configuration
- Remove popovers: set the `enablePopovers` field in `quartz.config.ts` to be `false`.

View File

@ -1,16 +1,16 @@
---
title: Private Pages
tags:
- plugin/filter
- feature/filter
---
There may be some notes you want to avoid publishing as a website. Quartz supports this through two mechanisms which can be used in conjunction:
## Filter Plugins
[[making plugins#Filters|Filter plugins]] are plugins that filter out content based off of certain criteria. By default, Quartz uses the `Plugin.RemoveDrafts` plugin which filters out any note that has `draft: true` in the frontmatter.
[[making plugins#Filters|Filter plugins]] are plugins that filter out content based off of certain criteria. By default, Quartz uses the [[RemoveDrafts]] plugin which filters out any note that has `draft: true` in the frontmatter.
If you'd like to only publish a select number of notes, you can instead use `Plugin.ExplicitPublish` which will filter out all notes except for any that have `publish: true` in the frontmatter.
If you'd like to only publish a select number of notes, you can instead use [[ExplicitPublish]] which will filter out all notes except for any that have `publish: true` in the frontmatter.
> [!warning]
> Regardless of the filter plugin used, **all non-markdown files will be emitted and available publically in the final build.** This includes files such as images, voice recordings, PDFs, etc. One way to prevent this and still be able to embed local images is to create a folder specifically for public media and add the following two patterns to the ignorePatterns array.

View File

@ -9,6 +9,7 @@ Quartz can generate a list of recent notes based on some filtering and sorting c
- Changing the title from "Recent notes": pass in an additional parameter to `Component.RecentNotes({ title: "Recent writing" })`
- Changing the number of recent notes: pass in an additional parameter to `Component.RecentNotes({ limit: 5 })`
- Display the note's tags (defaults to true): `Component.RecentNotes({ showTags: false })`
- Show a 'see more' link: pass in an additional parameter to `Component.RecentNotes({ linkToMore: "tags/components" })`. This field should be a full slug to a page that exists.
- Customize filtering: pass in an additional parameter to `Component.RecentNotes({ filter: someFilterFunction })`. The filter function should be a function that has the signature `(f: QuartzPluginData) => boolean`.
- Customize sorting: pass in an additional parameter to `Component.RecentNotes({ sort: someSortFunction })`. By default, Quartz will sort by date and then tie break lexographically. The sort function should be a function that has the signature `(f1: QuartzPluginData, f2: QuartzPluginData) => number`. See `byDateAndAlphabetical` in `quartz/components/PageList.tsx` for an example.

View File

@ -1,7 +1,7 @@
---
title: Syntax Highlighting
tags:
- plugin/transformer
- feature/transformer
---
Syntax highlighting in Quartz is completely done at build-time. This means that Quartz only ships pre-calculated CSS to highlight the right words so there is no heavy client-side bundle that does the syntax highlighting.
@ -95,6 +95,16 @@ const [age, setAge] = useState(50)
const [name, setName] = useState("Taylor")
```
### Inline Highlighting
Append {:lang} to the end of inline code to highlight it like a regular code block.
```
This is an array `[1, 2, 3]{:js}` of numbers 1 through 3.
```
This is an array `[1, 2, 3]{:js}` of numbers 1 through 3.
### Line numbers
Syntax highlighting has line numbers configured automatically. If you want to start line numbers at a specific number, use `showLineNumbers{number}`:
@ -130,6 +140,4 @@ const [name, setName] = useState('Taylor');
## Customization
- Removing syntax highlighting: delete all usages of `Plugin.SyntaxHighlighting()` from `quartz.config.ts`.
- Style: By default, Quartz uses derivatives of the GitHub light and dark themes. You can customize the colours in the `quartz/styles/syntax.scss` file.
- Plugin: `quartz/plugins/transformers/syntax.ts`
Syntax highlighting is a functionality of the [[SyntaxHighlighting]] plugin. See the plugin page for customization options.

View File

@ -2,25 +2,17 @@
title: "Table of Contents"
tags:
- component
- plugin/transformer
- feature/transformer
---
Quartz can automatically generate a table of contents from a list of headings on each page. It will also show you your current scroll position on the site by marking headings you've scrolled through with a different colour.
Quartz can automatically generate a table of contents (TOC) from a list of headings on each page. It will also show you your current scrolling position on the page by highlighting headings you've scrolled through with a different color.
By default, it will show all headers from H1 (`# Title`) all the way to H3 (`### Title`) and will only show the table of contents if there is more than 1 header on the page.
You can also hide the table of contents on a page by adding `enableToc: false` to the frontmatter for that page.
You can hide the TOC on a page by adding `enableToc: false` to the frontmatter for that page.
> [!info]
> This feature requires both `Plugin.TableOfContents` in your `quartz.config.ts` and `Component.TableOfContents` in your `quartz.layout.ts` to function correctly.
By default, the TOC shows all headings from H1 (`# Title`) to H3 (`### Title`) and is only displayed if there is more than one heading on the page.
## Customization
- Removing table of contents: remove all instances of `Plugin.TableOfContents()` from `quartz.config.ts`. and `Component.TableOfContents()` from `quartz.layout.ts`
- Changing the max depth: pass in a parameter to `Plugin.TableOfContents({ maxDepth: 4 })`
- Changing the minimum number of entries in the Table of Contents before it renders: pass in a parameter to `Plugin.TableOfContents({ minEntries: 3 })`
- Collapse the table of content by default: pass in a parameter to `Plugin.TableOfContents({ collapseByDefault: true })`
- Component: `quartz/components/TableOfContents.tsx`
- Style:
- Modern (default): `quartz/components/styles/toc.scss`
- Legacy Quartz 3 style: `quartz/components/styles/legacyToc.scss`
- Script: `quartz/components/scripts/toc.inline.ts`
The table of contents is a functionality of the [[TableOfContents]] plugin. See the plugin page for more customization options.
It also needs the `TableOfContents` component, which is displayed in the right sidebar by default. You can change this by customizing the [[layout]]. The TOC component can be configured with the `layout` parameter, which can either be `modern` (default) or `legacy`.

View File

@ -2,22 +2,11 @@
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

View File

@ -4,7 +4,7 @@ title: Wikilinks
Wikilinks were pioneered by earlier internet wikis to make it easier to write links across pages without needing to write Markdown or HTML links each time.
Quartz supports Wikilinks by default and these links are resolved by Quartz using `Plugin.CrawlLinks`. See the [Obsidian Help page on Internal Links](https://help.obsidian.md/Linking+notes+and+files/Internal+links) for more information on Wikilink syntax.
Quartz supports Wikilinks by default and these links are resolved by Quartz using the [[CrawlLinks]] plugin. See the [Obsidian Help page on Internal Links](https://help.obsidian.md/Linking+notes+and+files/Internal+links) for more information on Wikilink syntax.
This is enabled as a part of [[Obsidian compatibility]] and can be configured and enabled/disabled from that plugin.

View File

@ -30,7 +30,7 @@ Press "Save and deploy" and Cloudflare should have a deployed version of your si
To add a custom domain, check out [Cloudflare's documentation](https://developers.cloudflare.com/pages/platform/custom-domains/).
> [!warning]
> Cloudflare Pages only allows shallow `git` clones so if you rely on `git` for timestamps, it is recommended you either add dates to your frontmatter (see [[authoring content#Syntax]]) or use another hosting provider.
> Cloudflare Pages performs a shallow clone by default, so if you rely on `git` for timestamps, it is recommended that you add `git fetch --unshallow &&` to the beginning of the build command (e.g., `git fetch --unshallow && npx quartz build`).
## GitHub Pages
@ -57,18 +57,18 @@ jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for git info
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18.14
node-version: 22
- name: Install Dependencies
run: npm ci
- name: Build Quartz
run: npx quartz build
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
with:
path: public
@ -81,7 +81,7 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4
```
Then:
@ -182,37 +182,33 @@ Using `docs.example.com` is an example of a subdomain. They're a simple way of c
## GitLab Pages
In your local Quartz, create a new file `.gitlab-ci.yaml`.
In your local Quartz, create a new file `.gitlab-ci.yml`.
```yaml title=".gitlab-ci.yaml"
```yaml title=".gitlab-ci.yml"
stages:
- build
- deploy
variables:
NODE_VERSION: "18.14"
image: node:20
cache: # Cache modules in between jobs
key: $CI_COMMIT_REF_SLUG
paths:
- .npm/
build:
stage: build
rules:
- if: '$CI_COMMIT_REF_NAME == "v4"'
before_script:
- apt-get update -q && apt-get install -y nodejs npm
- npm install -g n
- n $NODE_VERSION
- hash -r
- npm ci
- npm ci --cache .npm --prefer-offline
script:
- npx quartz build
artifacts:
paths:
- public
cache:
paths:
- ~/.npm/
key: "${CI_COMMIT_REF_SLUG}-node-${CI_COMMIT_REF_NAME}"
tags:
- docker
- gitlab-org-docker
pages:
stage: deploy
@ -228,3 +224,43 @@ pages:
When `.gitlab-ci.yaml` is committed, GitLab will build and deploy the website as a GitLab Page. You can find the url under `Deploy > Pages` in the sidebar.
By default, the page is private and only visible when logged in to a GitLab account with access to the repository but can be opened in the settings under `Deploy` -> `Pages`.
## Self-Hosting
Copy the `public` directory to your web server and configure it to serve the files. You can use any web server to host your site. Since Quartz generates links that do not include the `.html` extension, you need to let your web server know how to deal with it.
### Using Nginx
Here's an example of how to do this with Nginx:
```nginx title="nginx.conf"
server {
listen 80;
server_name example.com;
root /path/to/quartz/public;
index index.html;
error_page 404 /404.html;
location / {
try_files $uri $uri.html $uri/ =404;
}
}
```
### Using Caddy
Here's and example of how to do this with Caddy:
```caddy title="Caddyfile"
example.com {
root * /path/to/quartz/public
try_files {path} {path}.html {path}/ =404
file_server
encode gzip
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

BIN
docs/images/giscus-repo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -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:
@ -31,7 +31,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]], [[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

View File

@ -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

View File

@ -0,0 +1,37 @@
---
title: AliasRedirects
tags:
- plugin/emitter
---
This plugin emits HTML redirect pages for aliases and permalinks defined in the frontmatter of content files.
For example, A `foo.md` has the following frontmatter
```md title="foo.md"
---
title: "Foo"
alias:
- "bar"
---
```
The target `host.me/bar` will be redirected to `host.me/foo`
Note that these are permanent redirect.
The emitter supports the following aliases:
- `aliases`
- `alias`
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.AliasRedirects()`.
- Source: [`quartz/plugins/emitters/aliases.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/aliases.ts).

20
docs/plugins/Assets.md Normal file
View File

@ -0,0 +1,20 @@
---
title: Assets
tags:
- plugin/emitter
---
This plugin emits all non-Markdown static assets in your content folder (like images, videos, HTML, etc). The plugin respects the `ignorePatterns` in the global [[configuration]].
Note that all static assets will then be accessible through its path on your generated site, i.e: `host.me/path/to/static.pdf`
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.Assets()`.
- Source: [`quartz/plugins/emitters/assets.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/assets.ts).

22
docs/plugins/CNAME.md Normal file
View File

@ -0,0 +1,22 @@
---
title: CNAME
tags:
- plugin/emitter
---
This plugin emits a `CNAME` record that points your subdomain to the default domain of your site.
If you want to use a custom domain name like `quartz.example.com` for the site, then this is needed.
See [[hosting|Hosting]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.CNAME()`.
- Source: [`quartz/plugins/emitters/cname.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/cname.ts).

View File

@ -0,0 +1,18 @@
---
title: ComponentResources
tags:
- plugin/emitter
---
This plugin manages and emits the static resources required for the Quartz framework. This includes CSS stylesheets and JavaScript scripts that enhance the functionality and aesthetics of the generated site. See also the `cdnCaching` option in the `theme` section of the [[configuration]].
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.ComponentResources()`.
- Source: [`quartz/plugins/emitters/componentResources.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/componentResources.ts).

View File

@ -0,0 +1,26 @@
---
title: ContentIndex
tags:
- plugin/emitter
---
This plugin emits both RSS and an XML sitemap for your site. The [[RSS Feed]] allows users to subscribe to content on your site and the sitemap allows search engines to better index your site. The plugin also emits a `contentIndex.json` file which is used by dynamic frontend components like search and graph.
This plugin emits a comprehensive index of the site's content, generating additional resources such as a sitemap, an RSS feed, and a
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `enableSiteMap`: If `true` (default), generates a sitemap XML file (`sitemap.xml`) listing all site URLs for search engines in content discovery.
- `enableRSS`: If `true` (default), produces an RSS feed (`index.xml`) with recent content updates.
- `rssLimit`: Defines the maximum number of entries to include in the RSS feed, helping to focus on the most recent or relevant content. Defaults to `10`.
- `rssFullHtml`: If `true`, the RSS feed includes full HTML content. Otherwise it includes just summaries.
- `includeEmptyFiles`: If `true` (default), content files with no body text are included in the generated index and resources.
## API
- Category: Emitter
- Function name: `Plugin.ContentIndex()`.
- Source: [`quartz/plugins/emitters/contentIndex.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/contentIndex.ts).

View File

@ -0,0 +1,18 @@
---
title: ContentPage
tags:
- plugin/emitter
---
This plugin is a core component of the Quartz framework. It generates the HTML pages for each piece of Markdown content. It emits the full-page [[layout]], including headers, footers, and body content, among others.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.ContentPage()`.
- Source: [`quartz/plugins/emitters/contentPage.tsx`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/contentPage.tsx).

View File

@ -0,0 +1,30 @@
---
title: CrawlLinks
tags:
- plugin/transformer
---
This plugin parses links and processes them to point to the right places. It is also needed for embedded links (like images). See [[Obsidian compatibility]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `markdownLinkResolution`: Sets the strategy for resolving Markdown paths, can be `"absolute"` (default), `"relative"` or `"shortest"`. You should use the same setting here as in [[Obsidian compatibility|Obsidian]].
- `absolute`: Path relative to the root of the content folder.
- `relative`: Path relative to the file you are linking from.
- `shortest`: Name of the file. If this isn't enough to identify the file, use the full absolute path.
- `prettyLinks`: If `true` (default), simplifies links by removing folder paths, making them more user friendly (e.g. `folder/deeply/nested/note` becomes `note`).
- `openLinksInNewTab`: If `true`, configures external links to open in a new tab. Defaults to `false`.
- `lazyLoad`: If `true`, adds lazy loading to resource elements (`img`, `video`, etc.) to improve page load performance. Defaults to `false`.
- `externalLinkIcon`: Adds an icon next to external links when `true` (default) to visually distinguishing them from internal links.
> [!warning]
> Removing this plugin is _not_ recommended and will likely break the page.
## API
- Category: Transformer
- Function name: `Plugin.CrawlLinks()`.
- Source: [`quartz/plugins/transformers/links.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/links.ts).

View File

@ -0,0 +1,25 @@
---
title: "CreatedModifiedDate"
tags:
- plugin/transformer
---
This plugin determines the created, modified, and published dates for a document using three potential data sources: frontmatter metadata, Git history, and the filesystem. See [[authoring content#Syntax]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
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"]`.
> [!warning]
> If you rely on `git` for dates, make sure `defaultDateType` is set to `modified` in `quartz.config.ts`.
>
> Depending on how you [[hosting|host]] your Quartz, the `filesystem` dates of your local files may not match the final dates. In these cases, it may be better to use `git` or `frontmatter` to guarantee correct dates.
## API
- Category: Transformer
- Function name: `Plugin.CreatedModifiedDate()`.
- Source: [`quartz/plugins/transformers/lastmod.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/lastmod.ts).

View File

@ -0,0 +1,23 @@
---
title: Description
tags:
- plugin/transformer
---
This plugin generates descriptions that are used as metadata for the HTML `head`, the [[RSS Feed]] and in [[folder and tag listings]] if there is no main body content, the description is used as the text between the title and the listing.
If the frontmatter contains a `description` property, it is used (see [[authoring content#Syntax]]). Otherwise, the plugin will do its best to use the first few sentences of the content to reach the target description length.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `descriptionLength`: the maximum length of the generated description. Default is 150 characters. The cut off happens after the first _sentence_ that ends after the given length.
- `replaceExternalLinks`: If `true` (default), replace external links with their domain and path in the description (e.g. `https://domain.tld/some_page/another_page?query=hello&target=world` is replaced with `domain.tld/some_page/another_page`).
## API
- Category: Transformer
- Function name: `Plugin.Description()`.
- Source: [`quartz/plugins/transformers/description.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/description.ts).

View File

@ -0,0 +1,18 @@
---
title: ExplicitPublish
tags:
- plugin/filter
---
This plugin filters content based on an explicit `publish` flag in the frontmatter, allowing only content that is explicitly marked for publication to pass through. It's the opt-in version of [[RemoveDrafts]]. See [[private pages]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Filter
- Function name: `Plugin.ExplicitPublish()`.
- Source: [`quartz/plugins/filters/explicit.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/filters/explicit.ts).

View File

@ -0,0 +1,24 @@
---
title: FolderPage
tags:
- plugin/emitter
---
This plugin generates index pages for folders, creating a listing page for each folder that contains multiple content files. See [[folder and tag listings]] for more information.
Example: [[advanced/|Advanced]]
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
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
- Function name: `Plugin.FolderPage()`.
- Source: [`quartz/plugins/emitters/folderPage.tsx`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/folderPage.tsx).

View File

@ -0,0 +1,24 @@
---
title: "Frontmatter"
tags:
- plugin/transformer
---
This plugin parses the frontmatter of the page using the [gray-matter](https://github.com/jonschlinkert/gray-matter) library. See [[authoring content#Syntax]], [[Obsidian compatibility]] and [[OxHugo compatibility]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `delimiters`: the delimiters to use for the frontmatter. Can have one value (e.g. `"---"`) or separate values for opening and closing delimiters (e.g. `["---", "~~~"]`). Defaults to `"---"`.
- `language`: the language to use for parsing the frontmatter. Can be `yaml` (default) or `toml`.
> [!warning]
> This plugin must not be removed, otherwise Quartz will break.
## API
- Category: Transformer
- Function name: `Plugin.Frontmatter()`.
- Source: [`quartz/plugins/transformers/frontmatter.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/frontmatter.ts).

View File

@ -0,0 +1,23 @@
---
title: GitHubFlavoredMarkdown
tags:
- plugin/transformer
---
This plugin enhances Markdown processing to support GitHub Flavored Markdown (GFM) which adds features like autolink literals, footnotes, strikethrough, tables and tasklists.
In addition, this plugin adds optional features for typographic refinement (such as converting straight quotes to curly quotes, dashes to en-dashes/em-dashes, and ellipses) and automatic heading links as a symbol that appears next to the heading on hover.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `enableSmartyPants`: When true, enables typographic enhancements. Default is true.
- `linkHeadings`: When true, automatically adds links to headings. Default is true.
## API
- Category: Transformer
- Function name: `Plugin.GitHubFlavoredMarkdown()`.
- Source: [`quartz/plugins/transformers/gfm.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/gfm.ts).

View File

@ -0,0 +1,18 @@
---
title: HardLineBreaks
tags:
- plugin/transformer
---
This plugin automatically converts single line breaks in Markdown text into hard line breaks in the HTML output. This plugin is not enabled by default as this doesn't follow the semantics of actual Markdown but you may enable it if you'd like parity with [[Obsidian compatibility|Obsidian]].
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Transformer
- Function name: `Plugin.HardLineBreaks()`.
- Source: [`quartz/plugins/transformers/linebreaks.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/linebreaks.ts).

21
docs/plugins/Latex.md Normal file
View File

@ -0,0 +1,21 @@
---
title: "Latex"
tags:
- plugin/transformer
---
This plugin adds LaTeX support to Quartz. See [[features/Latex|Latex]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
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
- Category: Transformer
- Function name: `Plugin.Latex()`.
- Source: [`quartz/plugins/transformers/latex.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/latex.ts).

View File

@ -0,0 +1,18 @@
---
title: NotFoundPage
tags:
- plugin/emitter
---
This plugin emits a 404 (Not Found) page for broken or non-existent URLs.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.NotFoundPage()`.
- Source: [`quartz/plugins/emitters/404.tsx`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/404.tsx).

View File

@ -0,0 +1,34 @@
---
title: ObsidianFlavoredMarkdown
tags:
- plugin/transformer
---
This plugin provides support for [[Obsidian compatibility]].
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `comments`: If `true` (default), enables parsing of `%%` style Obsidian comment blocks.
- `highlight`: If `true` (default), enables parsing of `==` style highlights within content.
- `wikilinks`:If `true` (default), turns [[wikilinks]] into regular links.
- `callouts`: If `true` (default), adds support for [[callouts|callout]] blocks for emphasizing content.
- `mermaid`: If `true` (default), enables [[Mermaid diagrams|Mermaid diagram]] rendering within Markdown files.
- `parseTags`: If `true` (default), parses and links tags within the content.
- `parseArrows`: If `true` (default), transforms arrow symbols into their HTML character equivalents.
- `parseBlockReferences`: If `true` (default), handles block references, linking to specific content blocks.
- `enableInHtmlEmbed`: If `true`, allows embedding of content directly within HTML. Defaults to `false`.
- `enableYouTubeEmbed`: If `true` (default), enables the embedding of YouTube videos and playlists using external image Markdown syntax.
- `enableVideoEmbed`: If `true` (default), enables the embedding of video files.
- `enableCheckbox`: If `true`, adds support for interactive checkboxes in content. Defaults to `false`.
> [!warning]
> Don't remove this plugin if you're using [[Obsidian compatibility|Obsidian]] to author the content!
## API
- Category: Transformer
- Function name: `Plugin.ObsidianFlavoredMarkdown()`.
- Source: [`quartz/plugins/transformers/toc.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/toc.ts).

View File

@ -0,0 +1,29 @@
---
title: OxHugoFlavoredMarkdown
tags:
- plugin/transformer
---
This plugin provides support for [ox-hugo](https://github.com/kaushalmodi/ox-hugo) compatibility. See [[OxHugo compatibility]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `wikilinks`: If `true` (default), converts Hugo `{{ relref }}` shortcodes to Quartz [[wikilinks]].
- `removePredefinedAnchor`: If `true` (default), strips predefined anchors from headings.
- `removeHugoShortcode`: If `true` (default), removes Hugo shortcode syntax (`{{}}`) from the content.
- `replaceFigureWithMdImg`: If `true` (default), replaces `<figure/>` with `![]()`.
- `replaceOrgLatex`: If `true` (default), converts Org-mode [[features/Latex|Latex]] fragments to Quartz-compatible LaTeX wrapped in `$` (for inline) and `$$` (for block equations).
> [!warning]
> While you can use this together with [[ObsidianFlavoredMarkdown]], it's not recommended because it might mutate the file in unexpected ways. Use with caution.
>
> If you use `toml` frontmatter, make sure to configure the [[Frontmatter]] plugin accordingly. See [[OxHugo compatibility]] for an example.
## API
- Category: Transformer
- Function name: `Plugin.OxHugoFlavoredMarkdown()`.
- Source: [`quartz/plugins/transformers/oxhugofm.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/oxhugofm.ts).

View File

@ -0,0 +1,18 @@
---
title: RemoveDrafts
tags:
- plugin/filter
---
This plugin filters out content from your vault, so that only finalized content is made available. This prevents [[private pages]] from being published. By default, it filters out all pages with `draft: true` in the frontmatter and leaves all other pages intact.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Filter
- Function name: `Plugin.RemoveDrafts()`.
- Source: [`quartz/plugins/filters/draft.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/filters/draft.ts).

21
docs/plugins/Static.md Normal file
View File

@ -0,0 +1,21 @@
---
title: Static
tags:
- plugin/emitter
---
This plugin emits all static resources needed by Quartz. This is used, for example, for fonts and images that need a stable position, such as banners and icons. The plugin respects the `ignorePatterns` in the global [[configuration]].
> [!important]
> This is different from [[Assets]]. The resources from the [[Static]] plugin are located under `quartz/static`, whereas [[Assets]] renders all static resources under `content` and is used for images, videos, audio, etc. that are directly referenced by your markdown content.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin has no configuration options.
## API
- Category: Emitter
- Function name: `Plugin.Static()`.
- Source: [`quartz/plugins/emitters/static.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/static.ts).

View File

@ -0,0 +1,23 @@
---
title: "SyntaxHighlighting"
tags:
- plugin/transformer
---
This plugin is used to add syntax highlighting to code blocks in Quartz. See [[syntax highlighting]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `theme`: a separate id of one of the [themes bundled with Shikiji](https://shikiji.netlify.app/themes). One for light mode and one for dark mode. Defaults to `theme: { light: "github-light", dark: "github-dark" }`.
- `keepBackground`: If set to `true`, the background of the Shikiji theme will be used. With `false` (default) the Quartz theme color for background will be used instead.
In addition, you can further override the colours in the `quartz/styles/syntax.scss` file.
## API
- Category: Transformer
- Function name: `Plugin.SyntaxHighlighting()`.
- Source: [`quartz/plugins/transformers/syntax.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/syntax.ts).

View File

@ -0,0 +1,26 @@
---
title: TableOfContents
tags:
- plugin/transformer
---
This plugin generates a table of contents (TOC) for Markdown documents. See [[table of contents]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
This plugin accepts the following configuration options:
- `maxDepth`: Limits the depth of headings included in the TOC, ranging from `1` (top level headings only) to `6` (all heading levels). Default is `3`.
- `minEntries`: The minimum number of heading entries required for the TOC to be displayed. Default is `1`.
- `showByDefault`: If `true` (default), the TOC should be displayed by default. Can be overridden by frontmatter settings.
- `collapseByDefault`: If `true`, the TOC will start in a collapsed state. Default is `false`.
> [!warning]
> This plugin needs the `Component.TableOfContents` component in `quartz.layout.ts` to determine where to display the TOC. Without it, nothing will be displayed. They should always be added or removed together.
## API
- Category: Transformer
- Function name: `Plugin.TableOfContents()`.
- Source: [`quartz/plugins/transformers/toc.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/toc.ts).

22
docs/plugins/TagPage.md Normal file
View File

@ -0,0 +1,22 @@
---
title: TagPage
tags:
- plugin/emitter
---
This plugin emits dedicated pages for each tag used in the content. See [[folder and tag listings]] for more information.
> [!note]
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
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
- Function name: `Plugin.TagPage()`.
- Source: [`quartz/plugins/emitters/tagPage.tsx`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/emitters/tagPage.tsx).

3
docs/plugins/index.md Normal file
View File

@ -0,0 +1,3 @@
---
title: Plugins
---

View File

@ -7,23 +7,26 @@ 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)
- [Gatekeeper Wiki](https://www.gatekeeper.wiki)
- [Ellie's Notes](https://ellie.wtf)
- [🥷🏻🌳🍃 Computer Science & Thinkering Garden](https://notes.yxy.ninja)
- [Eledah's Crystalline](https://blog.eledah.ir/)
- [🌓 Projects & Privacy - FOSS, tech, law](https://be-far.com)
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)!

3
docs/tags/plugin.md Normal file
View File

@ -0,0 +1,3 @@
---
title: Plugins
---

4
globals.d.ts vendored
View File

@ -4,6 +4,10 @@ export declare global {
type: K,
listener: (this: Document, ev: CustomEventMap[K]) => 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
}
interface Window {

1744
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
"name": "@jackyzha0/quartz",
"description": "🌱 publish your digital garden and notes as a website",
"private": true,
"version": "4.2.2",
"version": "4.3.1",
"type": "module",
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
"license": "MIT",
@ -12,6 +12,7 @@
"url": "https://github.com/jackyzha0/quartz.git"
},
"scripts": {
"quartz": "./quartz/bootstrap-cli.mjs",
"docs": "npx quartz build --serve -d docs",
"check": "tsc --noEmit && npx prettier . --check",
"format": "npx prettier . --write",
@ -20,7 +21,7 @@
},
"engines": {
"npm": ">=9.3.1",
"node": ">=18.14"
"node": "20 || >=22"
},
"keywords": [
"site generator",
@ -35,37 +36,40 @@
},
"dependencies": {
"@clack/prompts": "^0.7.0",
"@floating-ui/dom": "^1.6.1",
"@napi-rs/simple-git": "0.1.16",
"async-mutex": "^0.4.1",
"@floating-ui/dom": "^1.6.10",
"@napi-rs/simple-git": "0.1.19",
"@tweenjs/tween.js": "^25.0.0",
"async-mutex": "^0.5.0",
"chalk": "^5.3.0",
"chokidar": "^3.5.3",
"chokidar": "^3.6.0",
"cli-spinner": "^0.2.10",
"d3": "^7.8.5",
"esbuild-sass-plugin": "^2.16.0",
"d3": "^7.9.0",
"esbuild-sass-plugin": "^2.16.1",
"flexsearch": "0.7.43",
"github-slugger": "^2.0.0",
"globby": "^14.0.0",
"globby": "^14.0.2",
"gray-matter": "^4.0.3",
"hast-util-to-html": "^9.0.0",
"hast-util-to-html": "^9.0.1",
"hast-util-to-jsx-runtime": "^2.3.0",
"hast-util-to-string": "^3.0.0",
"is-absolute-url": "^4.0.1",
"js-yaml": "^4.1.0",
"lightningcss": "^1.23.0",
"lightningcss": "^1.26.0",
"mdast-util-find-and-replace": "^3.0.1",
"mdast-util-to-hast": "^13.1.0",
"mdast-util-to-hast": "^13.2.0",
"mdast-util-to-string": "^4.0.0",
"micromorph": "^0.4.5",
"preact": "^10.19.3",
"preact-render-to-string": "^6.3.1",
"pixi.js": "^8.3.3",
"preact": "^10.23.2",
"preact-render-to-string": "^6.5.9",
"pretty-bytes": "^6.1.1",
"pretty-time": "^1.1.0",
"reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-katex": "^7.0.0",
"rehype-citation": "^2.1.1",
"rehype-katex": "^7.0.1",
"rehype-mathjax": "^6.0.0",
"rehype-pretty-code": "^0.12.6",
"rehype-pretty-code": "^0.13.2",
"rehype-raw": "^7.0.0",
"rehype-slug": "^6.0.0",
"remark": "^15.0.1",
@ -75,19 +79,19 @@
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
"remark-smartypants": "^2.0.0",
"rfdc": "^1.3.1",
"rimraf": "^5.0.5",
"remark-smartypants": "^3.0.2",
"rfdc": "^1.4.1",
"rimraf": "^6.0.1",
"serve-handler": "^6.1.5",
"shikiji": "^0.10.2",
"shiki": "^1.12.1",
"source-map-support": "^0.5.21",
"to-vfile": "^8.0.0",
"toml": "^3.0.0",
"unified": "^11.0.4",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.1",
"workerpool": "^9.1.0",
"ws": "^8.15.1",
"vfile": "^6.0.2",
"workerpool": "^9.1.3",
"ws": "^8.18.0",
"yargs": "^17.7.2"
},
"devDependencies": {
@ -95,14 +99,14 @@
"@types/d3": "^7.4.3",
"@types/hast": "^3.0.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.11.16",
"@types/node": "^22.5.0",
"@types/pretty-time": "^1.1.5",
"@types/source-map-support": "^0.5.10",
"@types/ws": "^8.5.10",
"@types/yargs": "^17.0.32",
"@types/ws": "^8.5.12",
"@types/yargs": "^17.0.33",
"esbuild": "^0.19.9",
"prettier": "^3.2.4",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
"prettier": "^3.3.3",
"tsx": "^4.18.0",
"typescript": "^5.5.4"
}
}

View File

@ -1,6 +1,11 @@
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
/**
* Quartz 4.0 Configuration
*
* See https://quartz.jzhao.xyz/configuration for more information.
*/
const config: QuartzConfig = {
configuration: {
pageTitle: "🪴 Quartz 4.0",
@ -14,6 +19,7 @@ const config: QuartzConfig = {
ignorePatterns: ["private", "templates", ".obsidian"],
defaultDateType: "created",
theme: {
fontOrigin: "googleFonts",
cdnCaching: true,
typography: {
header: "Schibsted Grotesk",
@ -30,6 +36,7 @@ const config: QuartzConfig = {
secondary: "#284b63",
tertiary: "#84a59d",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#fff23688",
},
darkMode: {
light: "#161618",
@ -40,6 +47,7 @@ const config: QuartzConfig = {
secondary: "#7b97aa",
tertiary: "#84a59d",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#b3aa0288",
},
},
},
@ -48,22 +56,26 @@ const config: QuartzConfig = {
transformers: [
Plugin.FrontMatter(),
Plugin.CreatedModifiedDate({
// you can add 'git' here for last modified from Git
// if you do rely on git for dates, ensure defaultDateType is 'modified'
priority: ["frontmatter", "filesystem"],
}),
Plugin.Latex({ renderEngine: "katex" }),
Plugin.SyntaxHighlighting(),
Plugin.SyntaxHighlighting({
theme: {
light: "github-light",
dark: "github-dark",
},
keepBackground: false,
}),
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
Plugin.GitHubFlavoredMarkdown(),
Plugin.TableOfContents(),
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
Plugin.Description(),
Plugin.Latex({ renderEngine: "katex" }),
],
filters: [Plugin.RemoveDrafts()],
emitters: [
Plugin.AliasRedirects(),
Plugin.ComponentResources({ fontOrigin: "googleFonts" }),
Plugin.ComponentResources(),
Plugin.ContentPage(),
Plugin.FolderPage(),
Plugin.TagPage(),

View File

@ -5,6 +5,7 @@ import * as Component from "./quartz/components"
export const sharedPageComponents: SharedLayout = {
head: Component.Head(),
header: [],
afterBody: [],
footer: Component.Footer({
links: {
GitHub: "https://github.com/jackyzha0/quartz",

View File

@ -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: [],
@ -60,7 +65,7 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
const release = await mut.acquire()
perf.addEvent("clean")
await rimraf(output)
await rimraf(path.join(output, "*"), { glob: true })
console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`)
perf.addEvent("glob")
@ -167,6 +172,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
@ -185,9 +191,14 @@ async function partialRebuildFromEntrypoint(
const emitterGraph =
(await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
// emmiter may not define a dependency graph. nothing to update if so
if (emitterGraph) {
dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp)
const existingGraph = dependencies[emitter.name]
if (existingGraph !== null) {
existingGraph.mergeGraph(emitterGraph)
} else {
// might be the first time we're adding a mardown file
dependencies[emitter.name] = emitterGraph
}
}
}
break
@ -203,8 +214,9 @@ async function partialRebuildFromEntrypoint(
const emitterGraph =
(await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
// emmiter may not define a dependency graph. nothing to update if so
if (emitterGraph) {
// only update the graph if the emitter plugin uses the changed file
// eg. Assets plugin ignores md files, so we skip updating the graph
if (emitterGraph?.hasNode(fp)) {
// merge the new dependencies into the dep graph
dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp)
}
@ -223,7 +235,6 @@ async function partialRebuildFromEntrypoint(
// EMIT
perf.addEvent("rebuild")
let emittedFiles = 0
const destinationsToDelete = new Set<FilePath>()
for (const emitter of cfg.plugins.emitters) {
const depGraph = dependencies[emitter.name]
@ -263,11 +274,6 @@ async function partialRebuildFromEntrypoint(
// and supply [a.md, b.md] to the emitter
const upstreams = [...depGraph.getLeafNodeAncestors(fp)] as FilePath[]
if (action === "delete" && upstreams.length === 1) {
// if there's only one upstream, the destination is solely dependent on this file
destinationsToDelete.add(upstreams[0])
}
const upstreamContent = upstreams
// filter out non-markdown files
.filter((file) => contentMap.has(file))
@ -290,14 +296,26 @@ async function partialRebuildFromEntrypoint(
console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`)
// CLEANUP
// delete files that are solely dependent on this file
await rimraf([...destinationsToDelete])
const destinationsToDelete = new Set<FilePath>()
for (const file of toRemove) {
// remove from cache
contentMap.delete(file)
// remove the node from dependency graphs
Object.values(dependencies).forEach((depGraph) => depGraph?.removeNode(file))
Object.values(dependencies).forEach((depGraph) => {
// remove the node from dependency graphs
depGraph?.removeNode(file)
// remove any orphan nodes. eg if a.md is deleted, a.html is orphaned and should be removed
const orphanNodes = depGraph?.removeOrphanNodes()
orphanNodes?.forEach((node) => {
// only delete files that are in the output directory
if (node.startsWith(argv.output)) {
destinationsToDelete.add(node)
}
})
})
}
await rimraf([...destinationsToDelete])
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
toRemove.clear()
release()
@ -351,14 +369,10 @@ 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))
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)
for (const content of parsedContent) {
const [_tree, vfile] = content
@ -372,9 +386,16 @@ 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(argv.output)
await rimraf(path.join(argv.output, ".*"), { glob: true })
await emitContent(ctx, filteredContent)
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
} catch (err) {

View File

@ -19,6 +19,25 @@ export type Analytics =
websiteId: string
host?: string
}
| {
provider: "goatcounter"
websiteId: string
host?: string
scriptSrc?: string
}
| {
provider: "posthog"
apiKey: string
host?: string
}
| {
provider: "tinylytics"
siteId: string
}
| {
provider: "cabin"
host?: string
}
export interface GlobalConfiguration {
pageTitle: string
@ -58,10 +77,11 @@ export interface FullPageLayout {
header: QuartzComponent[]
beforeBody: QuartzComponent[]
pageBody: QuartzComponent
afterBody: QuartzComponent[]
left: QuartzComponent[]
right: QuartzComponent[]
footer: QuartzComponent
}
export type PageLayout = Pick<FullPageLayout, "beforeBody" | "left" | "right">
export type SharedLayout = Pick<FullPageLayout, "head" | "header" | "footer">
export type SharedLayout = Pick<FullPageLayout, "head" | "header" | "footer" | "afterBody">

View File

@ -1,7 +1,7 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) {
const ArticleTitle: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
const title = fileData.frontmatter?.title
if (title) {
return <h1 class={classNames(displayClass, "article-title")}>{title}</h1>

View File

@ -1,10 +1,15 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/backlinks.scss"
import { resolveRelative, simplifySlug } from "../util/path"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"
function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) {
const Backlinks: QuartzComponent = ({
fileData,
allFiles,
displayClass,
cfg,
}: QuartzComponentProps) => {
const slug = simplifySlug(fileData.slug!)
const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
return (

View File

@ -1,9 +1,9 @@
// @ts-ignore
import clipboardScript from "./scripts/clipboard.inline"
import clipboardStyle from "./styles/clipboard.scss"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
function Body({ children }: QuartzComponentProps) {
const Body: QuartzComponent = ({ children }: QuartzComponentProps) => {
return <div id="quartz-body">{children}</div>
}

View File

@ -1,6 +1,6 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import breadcrumbsStyle from "./styles/breadcrumbs.scss"
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
import { FullSlug, SimpleSlug, joinSegments, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile"
import { classNames } from "../util/lang"
@ -54,7 +54,11 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
// computed index of folder name to its associated file data
let folderIndex: Map<string, QuartzPluginData> | undefined
function Breadcrumbs({ fileData, allFiles, displayClass }: QuartzComponentProps) {
const Breadcrumbs: QuartzComponent = ({
fileData,
allFiles,
displayClass,
}: QuartzComponentProps) => {
// Hide crumbs on root if enabled
if (options.hideOnRoot && fileData.slug === "index") {
return <></>
@ -68,13 +72,9 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
folderIndex = new Map()
// construct the index for the first time
for (const file of allFiles) {
if (file.slug?.endsWith("index")) {
const folderParts = file.slug?.split("/")
// 2nd last to exclude the /index
const folderName = folderParts?.at(-2)
if (folderName) {
folderIndex.set(folderName, file)
}
const folderParts = file.slug?.split("/")
if (folderParts?.at(-1) === "index") {
folderIndex.set(folderParts.slice(0, -1).join("/"), file)
}
}
}
@ -82,13 +82,17 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
// Split slug into hierarchy/parts
const slugParts = fileData.slug?.split("/")
if (slugParts) {
// is tag breadcrumb?
const isTagPath = slugParts[0] === "tags"
// full path until current part
let currentPath = ""
for (let i = 0; i < slugParts.length - 1; i++) {
let curPathSegment = slugParts[i]
// Try to resolve frontmatter folder title
const currentFile = folderIndex?.get(curPathSegment)
const currentFile = folderIndex?.get(slugParts.slice(0, i + 1).join("/"))
if (currentFile) {
const title = currentFile.frontmatter!.title
if (title !== "index") {
@ -97,10 +101,15 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
}
// Add current slug to full path
currentPath += slugParts[i] + "/"
currentPath = joinSegments(currentPath, slugParts[i])
const includeTrailingSlash = !isTagPath || i < 1
// Format and add current crumb
const crumb = formatCrumb(curPathSegment, fileData.slug!, currentPath as SimpleSlug)
const crumb = formatCrumb(
curPathSegment,
fileData.slug!,
(currentPath + (includeTrailingSlash ? "/" : "")) as SimpleSlug,
)
crumbs.push(crumb)
}
@ -125,5 +134,6 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
)
}
Breadcrumbs.css = breadcrumbsStyle
return Breadcrumbs
}) satisfies QuartzComponentConstructor

View File

@ -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 (
<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 = script
return Comments
}) satisfies QuartzComponentConstructor<Options>

View File

@ -2,16 +2,21 @@ import { formatDate, getDate } from "./Date"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import readingTime from "reading-time"
import { classNames } from "../util/lang"
import { i18n } from "../i18n"
import { JSX } from "preact"
import style from "./styles/contentMeta.scss"
interface ContentMetaOptions {
/**
* Whether to display reading time
*/
showReadingTime: boolean
showComma: boolean
}
const defaultOptions: ContentMetaOptions = {
showReadingTime: true,
showComma: true,
}
export default ((opts?: Partial<ContentMetaOptions>) => {
@ -22,7 +27,7 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
const text = fileData.text
if (text) {
const segments: string[] = []
const segments: (string | JSX.Element)[] = []
if (fileData.dates) {
segments.push(formatDate(getDate(cfg, fileData)!, cfg.locale))
@ -30,21 +35,26 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
// Display reading time if enabled
if (options.showReadingTime) {
const { text: timeTaken, words: _words } = readingTime(text)
segments.push(timeTaken)
const { minutes, words: _words } = readingTime(text)
const displayedTime = i18n(cfg.locale).components.contentMeta.readingTime({
minutes: Math.ceil(minutes),
})
segments.push(displayedTime)
}
return <p class={classNames(displayClass, "content-meta")}>{segments.join(", ")}</p>
const segmentsElements = segments.map((segment) => <span>{segment}</span>)
return (
<p show-comma={options.showComma} class={classNames(displayClass, "content-meta")}>
{segmentsElements}
</p>
)
} else {
return null
}
}
ContentMetadata.css = `
.content-meta {
margin-top: 0;
color: var(--gray);
}
`
ContentMetadata.css = style
return ContentMetadata
}) satisfies QuartzComponentConstructor

View File

@ -3,47 +3,44 @@
// see: https://v8.dev/features/modules#defer
import darkmodeScript from "./scripts/darkmode.inline"
import styles from "./styles/darkmode.scss"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"
function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
const Darkmode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
return (
<div class={classNames(displayClass, "darkmode")}>
<input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
<label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
id="dayIcon"
x="0px"
y="0px"
viewBox="0 0 35 35"
style="enable-background:new 0 0 35 35"
xmlSpace="preserve"
>
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</title>
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
</svg>
</label>
<label id="toggle-label-dark" for="darkmode-toggle" tabIndex={-1}>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
id="nightIcon"
x="0px"
y="0px"
viewBox="0 0 100 100"
style="enable-background:new 0 0 100 100"
xmlSpace="preserve"
>
<title>{i18n(cfg.locale).components.themeToggle.lightMode}</title>
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
</svg>
</label>
</div>
<button class={classNames(displayClass, "darkmode")} id="darkmode">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
id="dayIcon"
x="0px"
y="0px"
viewBox="0 0 35 35"
style="enable-background:new 0 0 35 35"
xmlSpace="preserve"
aria-label={i18n(cfg.locale).components.themeToggle.darkMode}
>
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</title>
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
id="nightIcon"
x="0px"
y="0px"
viewBox="0 0 100 100"
style="enable-background:new 0 0 100 100"
xmlSpace="preserve"
aria-label={i18n(cfg.locale).components.themeToggle.lightMode}
>
<title>{i18n(cfg.locale).components.themeToggle.lightMode}</title>
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
</svg>
</button>
)
}

View File

@ -3,7 +3,7 @@ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } fro
export default ((component?: QuartzComponent) => {
if (component) {
const Component = component
function DesktopOnly(props: QuartzComponentProps) {
const DesktopOnly: QuartzComponent = (props: QuartzComponentProps) => {
return <Component displayClass="desktop-only" {...props} />
}

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import explorerStyle from "./styles/explorer.scss"
// @ts-ignore
@ -44,12 +44,9 @@ export default ((userOpts?: Partial<Options>) => {
// 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))
@ -75,8 +72,18 @@ export default ((userOpts?: Partial<Options>) => {
jsonTree = JSON.stringify(folders)
}
function Explorer({ cfg, allFiles, displayClass, fileData }: QuartzComponentProps) {
constructFileTree(allFiles)
const Explorer: QuartzComponent = ({
ctx,
cfg,
allFiles,
displayClass,
fileData,
}: QuartzComponentProps) => {
if (ctx.buildId !== lastBuildId) {
lastBuildId = ctx.buildId
constructFileTree(allFiles)
}
return (
<div class={classNames(displayClass, "explorer")}>
<button
@ -86,8 +93,10 @@ export default ((userOpts?: Partial<Options>) => {
data-collapsed={opts.folderDefaultState}
data-savestate={opts.useSavedState}
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
xmlns="http://www.w3.org/2000/svg"
width="14"

View File

@ -168,10 +168,8 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro
const isDefaultOpen = opts.folderDefaultState === "open"
// Calculate current folderPath
let folderPath = ""
if (node.name !== "") {
folderPath = joinSegments(fullPath ?? "", node.name)
}
const folderPath = node.name !== "" ? joinSegments(fullPath ?? "", node.name) : ""
const href = resolveRelative(fileData.slug!, folderPath as SimpleSlug) + "/"
return (
<>
@ -205,11 +203,7 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro
{/* render <a> tag if folderBehavior is "link", otherwise render <button> with collapse click event */}
<div key={node.name} data-folderpath={folderPath}>
{folderBehavior === "link" ? (
<a
href={resolveRelative(fileData.slug!, folderPath as SimpleSlug)}
data-for={node.name}
class="folder-title"
>
<a href={href} data-for={node.name} class="folder-title">
{node.displayName}
</a>
) : (

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/footer.scss"
import { version } from "../../package.json"
import { i18n } from "../i18n"
@ -8,12 +8,11 @@ interface Options {
}
export default ((opts?: Options) => {
function Footer({ displayClass, cfg }: QuartzComponentProps) {
const Footer: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
const year = new Date().getFullYear()
const links = opts?.links ?? []
return (
<footer class={`${displayClass ?? ""}`}>
<hr />
<p>
{i18n(cfg.locale).components.footer.createdWith}{" "}
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
// @ts-ignore
import script from "./scripts/graph.inline"
import style from "./styles/graph.scss"
@ -17,6 +17,7 @@ export interface D3Config {
opacityScale: number
removeTags: string[]
showTags: boolean
focusOnHover?: boolean
}
interface GraphOptions {
@ -37,6 +38,7 @@ const defaultOptions: GraphOptions = {
opacityScale: 1,
showTags: true,
removeTags: [],
focusOnHover: false,
},
globalGraph: {
drag: true,
@ -50,11 +52,12 @@ const defaultOptions: GraphOptions = {
opacityScale: 1,
showTags: true,
removeTags: [],
focusOnHover: true,
},
}
export default ((opts?: GraphOptions) => {
function Graph({ displayClass, cfg }: QuartzComponentProps) {
const Graph: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
return (
@ -62,31 +65,32 @@ export default ((opts?: GraphOptions) => {
<h3>{i18n(cfg.locale).components.graph.title}</h3>
<div class="graph-outer">
<div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
<svg
version="1.1"
id="global-graph-icon"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 55 55"
fill="currentColor"
xmlSpace="preserve"
>
<path
d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"
/>
</svg>
<button id="global-graph-icon" aria-label="Global Graph">
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 55 55"
fill="currentColor"
xmlSpace="preserve"
>
<path
d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"
/>
</svg>
</button>
</div>
<div id="global-graph-outer">
<div id="global-graph-container" data-cfg={JSON.stringify(globalGraph)}></div>

View File

@ -1,10 +1,11 @@
import { i18n } from "../i18n"
import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path"
import { FullSlug, joinSegments, pathToRoot } from "../util/path"
import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { googleFontHref } from "../util/theme"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
export default (() => {
function Head({ cfg, fileData, externalResources }: QuartzComponentProps) {
const Head: QuartzComponent = ({ cfg, fileData, externalResources }: QuartzComponentProps) => {
const title = fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
const description =
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
@ -21,6 +22,13 @@ export default (() => {
<head>
<title>{title}</title>
<meta charSet="utf-8" />
{cfg.theme.cdnCaching && cfg.theme.fontOrigin === "googleFonts" && (
<>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link rel="stylesheet" href={googleFontHref(cfg.theme)} />
</>
)}
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
@ -30,12 +38,6 @@ export default (() => {
<link rel="icon" href={iconPath} />
<meta name="description" content={description} />
<meta name="generator" content="Quartz" />
{cfg.theme.cdnCaching && (
<>
<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 />
))}

View File

@ -1,6 +1,6 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
function Header({ children }: QuartzComponentProps) {
const Header: QuartzComponent = ({ children }: QuartzComponentProps) => {
return children.length > 0 ? <header>{children}</header> : null
}

View File

@ -3,7 +3,7 @@ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } fro
export default ((component?: QuartzComponent) => {
if (component) {
const Component = component
function MobileOnly(props: QuartzComponentProps) {
const MobileOnly: QuartzComponent = (props: QuartzComponentProps) => {
return <Component displayClass="mobile-only" {...props} />
}

View File

@ -1,12 +1,12 @@
import { FullSlug, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile"
import { Date, getDate } from "./Date"
import { QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentProps } from "./types"
import { GlobalConfiguration } from "../cfg"
export function byDateAndAlphabetical(
cfg: GlobalConfiguration,
): (f1: QuartzPluginData, f2: QuartzPluginData) => number {
export type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number
export function byDateAndAlphabetical(cfg: GlobalConfiguration): SortFn {
return (f1, f2) => {
if (f1.dates && f2.dates) {
// sort descending
@ -27,10 +27,12 @@ export function byDateAndAlphabetical(
type Props = {
limit?: number
sort?: SortFn
} & QuartzComponentProps
export function PageList({ cfg, fileData, allFiles, limit }: Props) {
let list = allFiles.sort(byDateAndAlphabetical(cfg))
export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort }: Props) => {
const sorter = sort ?? byDateAndAlphabetical(cfg)
let list = allFiles.sort(sorter)
if (limit) {
list = list.slice(0, limit)
}
@ -44,11 +46,13 @@ export function PageList({ cfg, fileData, allFiles, limit }: Props) {
return (
<li class="section-li">
<div class="section">
{page.dates && (
<p class="meta">
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
</p>
)}
<div>
{page.dates && (
<p class="meta">
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
</p>
)}
</div>
<div class="desc">
<h3>
<a href={resolveRelative(fileData.slug!, page.slug!)} class="internal">
@ -63,7 +67,7 @@ export function PageList({ cfg, fileData, allFiles, limit }: Props) {
class="internal tag-link"
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
>
#{tag}
{tag}
</a>
</li>
))}

View File

@ -1,20 +1,21 @@
import { pathToRoot } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
import { i18n } from "../i18n"
function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) {
const PageTitle: QuartzComponent = ({ fileData, cfg, displayClass }: QuartzComponentProps) => {
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
const baseDir = pathToRoot(fileData.slug!)
return (
<h1 class={classNames(displayClass, "page-title")}>
<h2 class={classNames(displayClass, "page-title")}>
<a href={baseDir}>{title}</a>
</h1>
</h2>
)
}
PageTitle.css = `
.page-title {
font-size: 1.75rem;
margin: 0;
}
`

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile"
import { byDateAndAlphabetical } from "./PageList"
@ -12,6 +12,7 @@ interface Options {
title?: string
limit: number
linkToMore: SimpleSlug | false
showTags: boolean
filter: (f: QuartzPluginData) => boolean
sort: (f1: QuartzPluginData, f2: QuartzPluginData) => number
}
@ -19,12 +20,18 @@ interface Options {
const defaultOptions = (cfg: GlobalConfiguration): Options => ({
limit: 3,
linkToMore: false,
showTags: true,
filter: () => true,
sort: byDateAndAlphabetical(cfg),
})
export default ((userOpts?: Partial<Options>) => {
function RecentNotes({ allFiles, fileData, displayClass, cfg }: QuartzComponentProps) {
const RecentNotes: QuartzComponent = ({
allFiles,
fileData,
displayClass,
cfg,
}: QuartzComponentProps) => {
const opts = { ...defaultOptions(cfg), ...userOpts }
const pages = allFiles.filter(opts.filter).sort(opts.sort)
const remaining = Math.max(0, pages.length - opts.limit)
@ -51,18 +58,20 @@ export default ((userOpts?: Partial<Options>) => {
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
</p>
)}
<ul class="tags">
{tags.map((tag) => (
<li>
<a
class="internal tag-link"
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
>
#{tag}
</a>
</li>
))}
</ul>
{opts.showTags && (
<ul class="tags">
{tags.map((tag) => (
<li>
<a
class="internal tag-link"
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
>
{tag}
</a>
</li>
))}
</ul>
)}
</div>
</li>
)

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/search.scss"
// @ts-ignore
import script from "./scripts/search.inline"
@ -14,29 +14,21 @@ const defaultOptions: SearchOptions = {
}
export default ((userOpts?: Partial<SearchOptions>) => {
function Search({ displayClass, cfg }: QuartzComponentProps) {
const Search: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
const opts = { ...defaultOptions, ...userOpts }
const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder
return (
<div class={classNames(displayClass, "search")}>
<div id="search-icon">
<button class="search-button" id="search-button">
<p>{i18n(cfg.locale).components.search.title}</p>
<div></div>
<svg
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>
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7">
<title>Search</title>
<g class="search-path" fill="none">
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
<circle cx="8" cy="8" r="7" />
</g>
</svg>
</div>
</button>
<div id="search-container">
<div id="search-space">
<input

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import legacyStyle from "./styles/legacyToc.scss"
import modernStyle from "./styles/toc.scss"
import { classNames } from "../util/lang"
@ -15,14 +15,24 @@ const defaultOptions: Options = {
layout: "modern",
}
function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps) {
const TableOfContents: QuartzComponent = ({
fileData,
displayClass,
cfg,
}: QuartzComponentProps) => {
if (!fileData.toc) {
return null
}
return (
<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>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -39,7 +49,7 @@ function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps)
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div id="toc-content">
<div id="toc-content" class={fileData.collapseToc ? "collapsed" : ""}>
<ul class="overflow">
{fileData.toc.map((tocEntry) => (
<li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
@ -56,7 +66,7 @@ function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps)
TableOfContents.css = modernStyle
TableOfContents.afterDOMLoaded = script
function LegacyTableOfContents({ fileData, cfg }: QuartzComponentProps) {
const LegacyTableOfContents: QuartzComponent = ({ fileData, cfg }: QuartzComponentProps) => {
if (!fileData.toc) {
return null
}

View File

@ -1,20 +1,19 @@
import { pathToRoot, slugTag } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function TagList({ fileData, displayClass }: QuartzComponentProps) {
const TagList: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
const tags = fileData.frontmatter?.tags
const baseDir = pathToRoot(fileData.slug!)
if (tags && tags.length > 0) {
return (
<ul class={classNames(displayClass, "tags")}>
{tags.map((tag) => {
const display = `#${tag}`
const linkDest = baseDir + `/tags/${slugTag(tag)}`
return (
<li>
<a href={linkDest} class="internal tag-link">
{display}
{tag}
</a>
</li>
)

View File

@ -19,6 +19,7 @@ import DesktopOnly from "./DesktopOnly"
import MobileOnly from "./MobileOnly"
import RecentNotes from "./RecentNotes"
import Breadcrumbs from "./Breadcrumbs"
import Comments from "./Comments"
export {
ArticleTitle,
@ -42,4 +43,5 @@ export {
RecentNotes,
NotFound,
Breadcrumbs,
Comments,
}

View File

@ -1,11 +1,16 @@
import { i18n } from "../../i18n"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
const NotFound: QuartzComponent = ({ cfg }: QuartzComponentProps) => {
// If baseUrl contains a pathname after the domain, use this as the home link
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
const baseDir = url.pathname
function NotFound({ cfg }: QuartzComponentProps) {
return (
<article class="popover-hint">
<h1>404</h1>
<p>{i18n(cfg.locale).pages.error.notFound}</p>
<a href={baseDir}>{i18n(cfg.locale).pages.error.home}</a>
</article>
)
}

View File

@ -1,7 +1,7 @@
import { htmlToJsx } from "../../util/jsx"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
function Content({ fileData, tree }: QuartzComponentProps) {
const Content: QuartzComponent = ({ fileData, tree }: QuartzComponentProps) => {
const content = htmlToJsx(fileData.filePath!, tree)
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
const classString = ["popover-hint", ...classes].join(" ")

View File

@ -1,9 +1,9 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
import path from "path"
import style from "../styles/listPage.scss"
import { PageList } from "../PageList"
import { _stripSlashes, simplifySlug } from "../../util/path"
import { PageList, SortFn } from "../PageList"
import { stripSlashes, simplifySlug } from "../../util/path"
import { Root } from "hast"
import { htmlToJsx } from "../../util/jsx"
import { i18n } from "../../i18n"
@ -13,6 +13,7 @@ interface FolderContentOptions {
* Whether to display number of folders
*/
showFolderCount: boolean
sort?: SortFn
}
const defaultOptions: FolderContentOptions = {
@ -22,11 +23,11 @@ const defaultOptions: FolderContentOptions = {
export default ((opts?: Partial<FolderContentOptions>) => {
const options: FolderContentOptions = { ...defaultOptions, ...opts }
function FolderContent(props: QuartzComponentProps) {
const FolderContent: QuartzComponent = (props: QuartzComponentProps) => {
const { tree, fileData, allFiles, cfg } = props
const folderSlug = _stripSlashes(simplifySlug(fileData.slug!))
const folderSlug = stripSlashes(simplifySlug(fileData.slug!))
const allPagesInFolder = allFiles.filter((file) => {
const fileSlug = _stripSlashes(simplifySlug(file.slug!))
const fileSlug = stripSlashes(simplifySlug(file.slug!))
const prefixed = fileSlug.startsWith(folderSlug) && fileSlug !== folderSlug
const folderParts = folderSlug.split(path.posix.sep)
const fileParts = fileSlug.split(path.posix.sep)
@ -37,6 +38,7 @@ export default ((opts?: Partial<FolderContentOptions>) => {
const classes = ["popover-hint", ...cssClasses].join(" ")
const listProps = {
...props,
sort: options.sort,
allFiles: allPagesInFolder,
}
@ -47,9 +49,7 @@ export default ((opts?: Partial<FolderContentOptions>) => {
return (
<div class={classes}>
<article>
<p>{content}</p>
</article>
<article>{content}</article>
<div class="page-listing">
{options.showFolderCount && (
<p>

View File

@ -1,104 +1,127 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
import style from "../styles/listPage.scss"
import { PageList } from "../PageList"
import { PageList, SortFn } from "../PageList"
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
import { QuartzPluginData } from "../../plugins/vfile"
import { Root } from "hast"
import { htmlToJsx } from "../../util/jsx"
import { i18n } from "../../i18n"
const numPages = 10
function TagContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles, cfg } = props
const slug = fileData.slug
if (!(slug?.startsWith("tags/") || slug === "tags")) {
throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`)
}
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
const allPagesWithTag = (tag: string) =>
allFiles.filter((file) =>
(file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag),
)
const content =
(tree as Root).children.length === 0
? fileData.description
: htmlToJsx(fileData.filePath!, tree)
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
const classes = ["popover-hint", ...cssClasses].join(" ")
if (tag === "/") {
const tags = [
...new Set(
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
),
].sort((a, b) => a.localeCompare(b))
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
for (const tag of tags) {
tagItemMap.set(tag, allPagesWithTag(tag))
}
return (
<div class={classes}>
<article>
<p>{content}</p>
</article>
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p>
<div>
{tags.map((tag) => {
const pages = tagItemMap.get(tag)!
const listProps = {
...props,
allFiles: pages,
}
const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`)[0]
const content = contentPage?.description
return (
<div>
<h2>
<a class="internal tag-link" href={`../tags/${tag}`}>
#{tag}
</a>
</h2>
{content && <p>{content}</p>}
<div class="page-listing">
<p>
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
{pages.length > numPages && (
<span>
{i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })}
</span>
)}
</p>
<PageList limit={numPages} {...listProps} />
</div>
</div>
)
})}
</div>
</div>
)
} else {
const pages = allPagesWithTag(tag)
const listProps = {
...props,
allFiles: pages,
}
return (
<div class={classes}>
<article>{content}</article>
<div class="page-listing">
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
<div>
<PageList {...listProps} />
</div>
</div>
</div>
)
}
interface TagContentOptions {
sort?: SortFn
numPages: number
}
TagContent.css = style + PageList.css
export default (() => TagContent) satisfies QuartzComponentConstructor
const defaultOptions: TagContentOptions = {
numPages: 10,
}
export default ((opts?: Partial<TagContentOptions>) => {
const options: TagContentOptions = { ...defaultOptions, ...opts }
const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
const { tree, fileData, allFiles, cfg } = props
const slug = fileData.slug
if (!(slug?.startsWith("tags/") || slug === "tags")) {
throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`)
}
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
const allPagesWithTag = (tag: string) =>
allFiles.filter((file) =>
(file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag),
)
const content =
(tree as Root).children.length === 0
? fileData.description
: htmlToJsx(fileData.filePath!, tree)
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
const classes = ["popover-hint", ...cssClasses].join(" ")
if (tag === "/") {
const tags = [
...new Set(
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
),
].sort((a, b) => a.localeCompare(b))
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
for (const tag of tags) {
tagItemMap.set(tag, allPagesWithTag(tag))
}
return (
<div class={classes}>
<article>
<p>{content}</p>
</article>
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p>
<div>
{tags.map((tag) => {
const pages = tagItemMap.get(tag)!
const listProps = {
...props,
allFiles: pages,
}
const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0)
const root = contentPage?.htmlAst
const content =
!root || root?.children.length === 0
? contentPage?.description
: htmlToJsx(contentPage.filePath!, root)
return (
<div>
<h2>
<a class="internal tag-link" href={`../tags/${tag}`}>
{tag}
</a>
</h2>
{content && <p>{content}</p>}
<div class="page-listing">
<p>
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
{pages.length > options.numPages && (
<>
{" "}
<span>
{i18n(cfg.locale).pages.tagContent.showingFirst({
count: options.numPages,
})}
</span>
</>
)}
</p>
<PageList limit={options.numPages} {...listProps} sort={opts?.sort} />
</div>
</div>
)
})}
</div>
</div>
)
} else {
const pages = allPagesWithTag(tag)
const listProps = {
...props,
allFiles: pages,
}
return (
<div class={classes}>
<article>{content}</article>
<div class="page-listing">
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
<div>
<PageList {...listProps} />
</div>
</div>
</div>
)
}
}
TagContent.css = style + PageList.css
return TagContent
}) satisfies QuartzComponentConstructor

View File

@ -3,10 +3,9 @@ import { QuartzComponent, QuartzComponentProps } from "./types"
import HeaderConstructor from "./Header"
import BodyConstructor from "./Body"
import { JSResourceToScriptElement, StaticResources } from "../util/resources"
import { FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../util/path"
import { clone, FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../util/path"
import { visit } from "unist-util-visit"
import { Root, Element, ElementContent } from "hast"
import { QuartzPluginData } from "../plugins/vfile"
import { GlobalConfiguration } from "../cfg"
import { i18n } from "../i18n"
@ -15,11 +14,13 @@ interface RenderComponents {
header: QuartzComponent[]
beforeBody: QuartzComponent[]
pageBody: QuartzComponent
afterBody: QuartzComponent[]
left: QuartzComponent[]
right: QuartzComponent[]
footer: QuartzComponent
}
const headerRegex = new RegExp(/h[1-6]/)
export function pageResources(
baseDir: FullSlug | RelativeURL,
staticResources: StaticResources,
@ -52,18 +53,6 @@ export function pageResources(
}
}
let pageIndex: Map<FullSlug, QuartzPluginData> | undefined = undefined
function getOrComputeFileIndex(allFiles: QuartzPluginData[]): Map<FullSlug, QuartzPluginData> {
if (!pageIndex) {
pageIndex = new Map()
for (const file of allFiles) {
pageIndex.set(file.slug!, file)
}
}
return pageIndex
}
export function renderPage(
cfg: GlobalConfiguration,
slug: FullSlug,
@ -71,14 +60,18 @@ export function renderPage(
components: RenderComponents,
pageResources: StaticResources,
): string {
// make a deep copy of the tree so we don't remove the transclusion references
// for the file cached in contentMap in build.ts
const root = clone(componentData.tree) as Root
// process transcludes in componentData
visit(componentData.tree as Root, "element", (node, _index, _parent) => {
visit(root, "element", (node, _index, _parent) => {
if (node.tagName === "blockquote") {
const classNames = (node.properties?.className ?? []) as string[]
if (classNames.includes("transclude")) {
const inner = node.children[0] as Element
const transcludeTarget = inner.properties["data-slug"] as FullSlug
const page = getOrComputeFileIndex(componentData.allFiles).get(transcludeTarget)
const page = componentData.allFiles.find((f) => f.slug === transcludeTarget)
if (!page) {
return
}
@ -103,7 +96,7 @@ export function renderPage(
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
children: [
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
],
@ -114,18 +107,24 @@ export function renderPage(
// header transclude
blockRef = blockRef.slice(1)
let startIdx = undefined
let startDepth = undefined
let endIdx = undefined
for (const [i, el] of page.htmlAst.children.entries()) {
if (el.type === "element" && el.tagName.match(/h[1-6]/)) {
if (endIdx) {
break
}
// skip non-headers
if (!(el.type === "element" && el.tagName.match(headerRegex))) continue
const depth = Number(el.tagName.substring(1))
if (startIdx !== undefined) {
endIdx = i
} else if (el.properties?.id === blockRef) {
// lookin for our blockref
if (startIdx === undefined || startDepth === undefined) {
// skip until we find the blockref that matches
if (el.properties?.id === blockRef) {
startIdx = i
startDepth = depth
}
} else if (depth <= startDepth) {
// looking for new header that is same level or higher
endIdx = i
break
}
}
@ -140,7 +139,7 @@ export function renderPage(
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
children: [
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
],
@ -170,7 +169,7 @@ export function renderPage(
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
children: [
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
],
@ -181,11 +180,15 @@ export function renderPage(
}
})
// set componentData.tree to the edited html that has transclusions rendered
componentData.tree = root
const {
head: Head,
header,
beforeBody,
pageBody: Content,
afterBody,
left,
right,
footer: Footer,
@ -209,14 +212,11 @@ export function renderPage(
</div>
)
const lang = componentData.frontmatter?.lang ?? cfg.locale?.split("-")[0] ?? "en"
const lang = componentData.fileData.frontmatter?.lang ?? cfg.locale?.split("-")[0] ?? "en"
const doc = (
<html lang={lang}>
<Head {...componentData} />
<body
data-slug={slug}
data-enable-preview={componentData.fileData.frontmatter?.preview ?? true}
>
<body data-slug={slug}>
<div id="quartz-root" class="page">
<Body {...componentData}>
{LeftComponent}
@ -234,6 +234,12 @@ export function renderPage(
</div>
</div>
<Content {...componentData} />
<hr />
<div class="page-footer">
{afterBody.map((BodyComponent) => (
<BodyComponent {...componentData} />
))}
</div>
</div>
{RightComponent}
</Body>

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

@ -11,7 +11,8 @@ const emitThemeChangeEvent = (theme: "light" | "dark") => {
document.addEventListener("nav", () => {
const switchTheme = (e: Event) => {
const newTheme = (e.target as HTMLInputElement)?.checked ? "dark" : "light"
const newTheme =
document.documentElement.getAttribute("saved-theme") === "dark" ? "light" : "dark"
document.documentElement.setAttribute("saved-theme", newTheme)
localStorage.setItem("theme", newTheme)
emitThemeChangeEvent(newTheme)
@ -21,17 +22,13 @@ document.addEventListener("nav", () => {
const newTheme = e.matches ? "dark" : "light"
document.documentElement.setAttribute("saved-theme", newTheme)
localStorage.setItem("theme", newTheme)
toggleSwitch.checked = e.matches
emitThemeChangeEvent(newTheme)
}
// Darkmode toggle
const toggleSwitch = document.querySelector("#darkmode-toggle") as HTMLInputElement
toggleSwitch.addEventListener("change", switchTheme)
window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme))
if (currentTheme === "dark") {
toggleSwitch.checked = true
}
const themeButton = document.querySelector("#darkmode") as HTMLButtonElement
themeButton.addEventListener("click", switchTheme)
window.addCleanup(() => themeButton.removeEventListener("click", switchTheme))
// Listen for changes in prefers-color-scheme
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")

View File

@ -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

View File

@ -1,19 +1,56 @@
import type { ContentDetails, ContentIndex } from "../../plugins/emitters/contentIndex"
import * as d3 from "d3"
import type { ContentDetails } from "../../plugins/emitters/contentIndex"
import {
SimulationNodeDatum,
SimulationLinkDatum,
Simulation,
forceSimulation,
forceManyBody,
forceCenter,
forceLink,
forceCollide,
zoomIdentity,
select,
drag,
zoom,
} from "d3"
import { Text, Graphics, Application, Container, Circle } from "pixi.js"
import { Group as TweenGroup, Tween as Tweened } from "@tweenjs/tween.js"
import { registerEscapeHandler, removeAllChildren } from "./util"
import { FullSlug, SimpleSlug, getFullSlug, resolveRelative, simplifySlug } from "../../util/path"
import { D3Config } from "../Graph"
type GraphicsInfo = {
color: string
gfx: Graphics
alpha: number
active: boolean
}
type NodeData = {
id: SimpleSlug
text: string
tags: string[]
} & d3.SimulationNodeDatum
} & SimulationNodeDatum
type LinkData = {
type SimpleLinkData = {
source: SimpleSlug
target: SimpleSlug
}
type LinkData = {
source: NodeData
target: NodeData
} & SimulationLinkDatum<NodeData>
type LinkRenderData = GraphicsInfo & {
simulationData: LinkData
}
type NodeRenderData = GraphicsInfo & {
simulationData: NodeData
label: Text
}
const localStorageKey = "graph-visited"
function getVisited(): Set<SimpleSlug> {
return new Set(JSON.parse(localStorage.getItem(localStorageKey) ?? "[]"))
@ -25,6 +62,11 @@ function addToVisited(slug: SimpleSlug) {
localStorage.setItem(localStorageKey, JSON.stringify([...visited]))
}
type TweenNode = {
update: (time: number) => void
stop: () => void
}
async function renderGraph(container: string, fullSlug: FullSlug) {
const slug = simplifySlug(fullSlug)
const visited = getVisited()
@ -44,7 +86,8 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
opacityScale,
removeTags,
showTags,
} = JSON.parse(graph.dataset["cfg"]!)
focusOnHover,
} = JSON.parse(graph.dataset["cfg"]!) as D3Config
const data: Map<SimpleSlug, ContentDetails> = new Map(
Object.entries<ContentDetails>(await fetchData).map(([k, v]) => [
@ -52,10 +95,11 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
v,
]),
)
const links: LinkData[] = []
const links: SimpleLinkData[] = []
const tags: SimpleSlug[] = []
const validLinks = new Set(data.keys())
const tweens = new Map<string, TweenNode>()
for (const [source, details] of data.entries()) {
const outgoing = details.links ?? []
@ -99,232 +143,459 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
if (showTags) tags.forEach((tag) => neighbourhood.add(tag))
}
const nodes = [...neighbourhood].map((url) => {
const text = url.startsWith("tags/") ? "#" + url.substring(5) : (data.get(url)?.title ?? url)
return {
id: url,
text,
tags: data.get(url)?.tags ?? [],
}
})
const graphData: { nodes: NodeData[]; links: LinkData[] } = {
nodes: [...neighbourhood].map((url) => {
const text = url.startsWith("tags/") ? "#" + url.substring(5) : data.get(url)?.title ?? url
return {
id: url,
text: text,
tags: data.get(url)?.tags ?? [],
}
}),
links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target)),
nodes,
links: links
.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target))
.map((l) => ({
source: nodes.find((n) => n.id === l.source)!,
target: nodes.find((n) => n.id === l.target)!,
})),
}
const simulation: d3.Simulation<NodeData, LinkData> = d3
.forceSimulation(graphData.nodes)
.force("charge", d3.forceManyBody().strength(-100 * repelForce))
.force(
"link",
d3
.forceLink(graphData.links)
.id((d: any) => d.id)
.distance(linkDistance),
)
.force("center", d3.forceCenter().strength(centerForce))
// we virtualize the simulation and use pixi to actually render it
const simulation: Simulation<NodeData, LinkData> = forceSimulation<NodeData>(graphData.nodes)
.force("charge", forceManyBody().strength(-100 * repelForce))
.force("center", forceCenter().strength(centerForce))
.force("link", forceLink(graphData.links).distance(linkDistance))
.force("collide", forceCollide<NodeData>((n) => nodeRadius(n)).iterations(3))
const height = Math.max(graph.offsetHeight, 250)
const width = graph.offsetWidth
const height = Math.max(graph.offsetHeight, 250)
const svg = d3
.select<HTMLElement, NodeData>("#" + container)
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2 / scale, -height / 2 / scale, width / scale, height / scale])
// draw links between nodes
const link = svg
.append("g")
.selectAll("line")
.data(graphData.links)
.join("line")
.attr("class", "link")
.attr("stroke", "var(--lightgray)")
.attr("stroke-width", 1)
// svg groups
const graphNode = svg.append("g").selectAll("g").data(graphData.nodes).enter().append("g")
// precompute style prop strings as pixi doesn't support css variables
const cssVars = [
"--secondary",
"--tertiary",
"--gray",
"--light",
"--lightgray",
"--dark",
"--darkgray",
"--bodyFont",
] as const
const computedStyleMap = cssVars.reduce(
(acc, key) => {
acc[key] = getComputedStyle(document.documentElement).getPropertyValue(key)
return acc
},
{} as Record<(typeof cssVars)[number], string>,
)
// calculate color
const color = (d: NodeData) => {
const isCurrent = d.id === slug
if (isCurrent) {
return "var(--secondary)"
return computedStyleMap["--secondary"]
} else if (visited.has(d.id) || d.id.startsWith("tags/")) {
return "var(--tertiary)"
return computedStyleMap["--tertiary"]
} else {
return "var(--gray)"
return computedStyleMap["--gray"]
}
}
const drag = (simulation: d3.Simulation<NodeData, LinkData>) => {
function dragstarted(event: any, d: NodeData) {
if (!event.active) simulation.alphaTarget(1).restart()
d.fx = d.x
d.fy = d.y
}
function dragged(event: any, d: NodeData) {
d.fx = event.x
d.fy = event.y
}
function dragended(event: any, d: NodeData) {
if (!event.active) simulation.alphaTarget(0)
d.fx = null
d.fy = null
}
const noop = () => {}
return d3
.drag<Element, NodeData>()
.on("start", enableDrag ? dragstarted : noop)
.on("drag", enableDrag ? dragged : noop)
.on("end", enableDrag ? dragended : noop)
}
function nodeRadius(d: NodeData) {
const numLinks = links.filter((l: any) => l.source.id === d.id || l.target.id === d.id).length
const numLinks = graphData.links.filter(
(l) => l.source.id === d.id || l.target.id === d.id,
).length
return 2 + Math.sqrt(numLinks)
}
// draw individual nodes
const node = graphNode
.append("circle")
.attr("class", "node")
.attr("id", (d) => d.id)
.attr("r", nodeRadius)
.attr("fill", color)
.style("cursor", "pointer")
.on("click", (_, d) => {
const targ = resolveRelative(fullSlug, d.id)
window.spaNavigate(new URL(targ, window.location.toString()))
let hoveredNodeId: string | null = null
let hoveredNeighbours: Set<string> = new Set()
const linkRenderData: LinkRenderData[] = []
const nodeRenderData: NodeRenderData[] = []
function updateHoverInfo(newHoveredId: string | null) {
hoveredNodeId = newHoveredId
if (newHoveredId === null) {
hoveredNeighbours = new Set()
for (const n of nodeRenderData) {
n.active = false
}
for (const l of linkRenderData) {
l.active = false
}
} else {
hoveredNeighbours = new Set()
for (const l of linkRenderData) {
const linkData = l.simulationData
if (linkData.source.id === newHoveredId || linkData.target.id === newHoveredId) {
hoveredNeighbours.add(linkData.source.id)
hoveredNeighbours.add(linkData.target.id)
}
l.active = linkData.source.id === newHoveredId || linkData.target.id === newHoveredId
}
for (const n of nodeRenderData) {
n.active = hoveredNeighbours.has(n.simulationData.id)
}
}
}
let dragStartTime = 0
let dragging = false
function renderLinks() {
tweens.get("link")?.stop()
const tweenGroup = new TweenGroup()
for (const l of linkRenderData) {
let alpha = 1
// if we are hovering over a node, we want to highlight the immediate neighbours
// with full alpha and the rest with default alpha
if (hoveredNodeId) {
alpha = l.active ? 1 : 0.2
}
l.color = l.active ? computedStyleMap["--gray"] : computedStyleMap["--lightgray"]
tweenGroup.add(new Tweened<LinkRenderData>(l).to({ alpha }, 200))
}
tweenGroup.getAll().forEach((tw) => tw.start())
tweens.set("link", {
update: tweenGroup.update.bind(tweenGroup),
stop() {
tweenGroup.getAll().forEach((tw) => tw.stop())
},
})
.on("mouseover", function (_, d) {
const neighbours: SimpleSlug[] = data.get(slug)?.links ?? []
const neighbourNodes = d3
.selectAll<HTMLElement, NodeData>(".node")
.filter((d) => neighbours.includes(d.id))
const currentId = d.id
const linkNodes = d3
.selectAll(".link")
.filter((d: any) => d.source.id === currentId || d.target.id === currentId)
}
// highlight neighbour nodes
neighbourNodes.transition().duration(200).attr("fill", color)
function renderLabels() {
tweens.get("label")?.stop()
const tweenGroup = new TweenGroup()
// highlight links
linkNodes.transition().duration(200).attr("stroke", "var(--gray)").attr("stroke-width", 1)
const defaultScale = 1 / scale
const activeScale = defaultScale * 1.1
for (const n of nodeRenderData) {
const nodeId = n.simulationData.id
const bigFont = fontSize * 1.5
if (hoveredNodeId === nodeId) {
tweenGroup.add(
new Tweened<Text>(n.label).to(
{
alpha: 1,
scale: { x: activeScale, y: activeScale },
},
100,
),
)
} else {
tweenGroup.add(
new Tweened<Text>(n.label).to(
{
alpha: n.label.alpha,
scale: { x: defaultScale, y: defaultScale },
},
100,
),
)
}
}
// show text for self
const parent = this.parentNode as HTMLElement
d3.select<HTMLElement, NodeData>(parent)
.raise()
.select("text")
.transition()
.duration(200)
.attr("opacityOld", d3.select(parent).select("text").style("opacity"))
.style("opacity", 1)
.style("font-size", bigFont + "em")
tweenGroup.getAll().forEach((tw) => tw.start())
tweens.set("label", {
update: tweenGroup.update.bind(tweenGroup),
stop() {
tweenGroup.getAll().forEach((tw) => tw.stop())
},
})
.on("mouseleave", function (_, d) {
const currentId = d.id
const linkNodes = d3
.selectAll(".link")
.filter((d: any) => d.source.id === currentId || d.target.id === currentId)
}
linkNodes.transition().duration(200).attr("stroke", "var(--lightgray)")
function renderNodes() {
tweens.get("hover")?.stop()
const parent = this.parentNode as HTMLElement
d3.select<HTMLElement, NodeData>(parent)
.select("text")
.transition()
.duration(200)
.style("opacity", d3.select(parent).select("text").attr("opacityOld"))
.style("font-size", fontSize + "em")
const tweenGroup = new TweenGroup()
for (const n of nodeRenderData) {
let alpha = 1
// if we are hovering over a node, we want to highlight the immediate neighbours
if (hoveredNodeId !== null && focusOnHover) {
alpha = n.active ? 1 : 0.2
}
tweenGroup.add(new Tweened<Graphics>(n.gfx, tweenGroup).to({ alpha }, 200))
}
tweenGroup.getAll().forEach((tw) => tw.start())
tweens.set("hover", {
update: tweenGroup.update.bind(tweenGroup),
stop() {
tweenGroup.getAll().forEach((tw) => tw.stop())
},
})
// @ts-ignore
.call(drag(simulation))
}
// draw labels
const labels = graphNode
.append("text")
.attr("dx", 0)
.attr("dy", (d) => -nodeRadius(d) + "px")
.attr("text-anchor", "middle")
.text((d) => d.text)
.style("opacity", (opacityScale - 1) / 3.75)
.style("pointer-events", "none")
.style("font-size", fontSize + "em")
.raise()
// @ts-ignore
.call(drag(simulation))
function renderPixiFromD3() {
renderNodes()
renderLinks()
renderLabels()
}
tweens.forEach((tween) => tween.stop())
tweens.clear()
const app = new Application()
await app.init({
width,
height,
antialias: true,
autoStart: false,
autoDensity: true,
backgroundAlpha: 0,
preference: "webgpu",
resolution: window.devicePixelRatio,
eventMode: "static",
})
graph.appendChild(app.canvas)
const stage = app.stage
stage.interactive = false
const labelsContainer = new Container<Text>({ zIndex: 3 })
const nodesContainer = new Container<Graphics>({ zIndex: 2 })
const linkContainer = new Container<Graphics>({ zIndex: 1 })
stage.addChild(nodesContainer, labelsContainer, linkContainer)
for (const n of graphData.nodes) {
const nodeId = n.id
const label = new Text({
interactive: false,
eventMode: "none",
text: n.text,
alpha: 0,
anchor: { x: 0.5, y: 1.2 },
style: {
fontSize: fontSize * 15,
fill: computedStyleMap["--dark"],
fontFamily: computedStyleMap["--bodyFont"],
},
resolution: window.devicePixelRatio * 4,
})
label.scale.set(1 / scale)
let oldLabelOpacity = 0
const isTagNode = nodeId.startsWith("tags/")
const gfx = new Graphics({
interactive: true,
label: nodeId,
eventMode: "static",
hitArea: new Circle(0, 0, nodeRadius(n)),
cursor: "pointer",
})
.circle(0, 0, nodeRadius(n))
.fill({ color: isTagNode ? computedStyleMap["--light"] : color(n) })
.stroke({ width: isTagNode ? 2 : 0, color: color(n) })
.on("pointerover", (e) => {
updateHoverInfo(e.target.label)
oldLabelOpacity = label.alpha
if (!dragging) {
renderPixiFromD3()
}
})
.on("pointerleave", () => {
updateHoverInfo(null)
label.alpha = oldLabelOpacity
if (!dragging) {
renderPixiFromD3()
}
})
nodesContainer.addChild(gfx)
labelsContainer.addChild(label)
const nodeRenderDatum: NodeRenderData = {
simulationData: n,
gfx,
label,
color: color(n),
alpha: 1,
active: false,
}
nodeRenderData.push(nodeRenderDatum)
}
for (const l of graphData.links) {
const gfx = new Graphics({ interactive: false, eventMode: "none" })
linkContainer.addChild(gfx)
const linkRenderDatum: LinkRenderData = {
simulationData: l,
gfx,
color: computedStyleMap["--lightgray"],
alpha: 1,
active: false,
}
linkRenderData.push(linkRenderDatum)
}
let currentTransform = zoomIdentity
if (enableDrag) {
select<HTMLCanvasElement, NodeData | undefined>(app.canvas).call(
drag<HTMLCanvasElement, NodeData | undefined>()
.container(() => app.canvas)
.subject(() => graphData.nodes.find((n) => n.id === hoveredNodeId))
.on("start", function dragstarted(event) {
if (!event.active) simulation.alphaTarget(1).restart()
event.subject.fx = event.subject.x
event.subject.fy = event.subject.y
event.subject.__initialDragPos = {
x: event.subject.x,
y: event.subject.y,
fx: event.subject.fx,
fy: event.subject.fy,
}
dragStartTime = Date.now()
dragging = true
})
.on("drag", function dragged(event) {
const initPos = event.subject.__initialDragPos
event.subject.fx = initPos.x + (event.x - initPos.x) / currentTransform.k
event.subject.fy = initPos.y + (event.y - initPos.y) / currentTransform.k
})
.on("end", function dragended(event) {
if (!event.active) simulation.alphaTarget(0)
event.subject.fx = null
event.subject.fy = null
dragging = false
// if the time between mousedown and mouseup is short, we consider it a click
if (Date.now() - dragStartTime < 500) {
const node = graphData.nodes.find((n) => n.id === event.subject.id) as NodeData
const targ = resolveRelative(fullSlug, node.id)
window.spaNavigate(new URL(targ, window.location.toString()))
}
}),
)
} else {
for (const node of nodeRenderData) {
node.gfx.on("click", () => {
const targ = resolveRelative(fullSlug, node.simulationData.id)
window.spaNavigate(new URL(targ, window.location.toString()))
})
}
}
// set panning
if (enableZoom) {
svg.call(
d3
.zoom<SVGSVGElement, NodeData>()
select<HTMLCanvasElement, NodeData>(app.canvas).call(
zoom<HTMLCanvasElement, NodeData>()
.extent([
[0, 0],
[width, height],
])
.scaleExtent([0.25, 4])
.on("zoom", ({ transform }) => {
link.attr("transform", transform)
node.attr("transform", transform)
currentTransform = transform
stage.scale.set(transform.k, transform.k)
stage.position.set(transform.x, transform.y)
// zoom adjusts opacity of labels too
const scale = transform.k * opacityScale
const scaledOpacity = Math.max((scale - 1) / 3.75, 0)
labels.attr("transform", transform).style("opacity", scaledOpacity)
let scaleOpacity = Math.max((scale - 1) / 3.75, 0)
const activeNodes = nodeRenderData.filter((n) => n.active).flatMap((n) => n.label)
for (const label of labelsContainer.children) {
if (!activeNodes.includes(label)) {
label.alpha = scaleOpacity
}
}
}),
)
}
// progress the simulation
simulation.on("tick", () => {
link
.attr("x1", (d: any) => d.source.x)
.attr("y1", (d: any) => d.source.y)
.attr("x2", (d: any) => d.target.x)
.attr("y2", (d: any) => d.target.y)
node.attr("cx", (d: any) => d.x).attr("cy", (d: any) => d.y)
labels.attr("x", (d: any) => d.x).attr("y", (d: any) => d.y)
})
}
function renderGlobalGraph() {
const slug = getFullSlug(window)
const container = document.getElementById("global-graph-outer")
const sidebar = container?.closest(".sidebar") as HTMLElement
container?.classList.add("active")
if (sidebar) {
sidebar.style.zIndex = "1"
}
renderGraph("global-graph-container", slug)
function hideGlobalGraph() {
container?.classList.remove("active")
const graph = document.getElementById("global-graph-container")
if (sidebar) {
sidebar.style.zIndex = "unset"
function animate(time: number) {
for (const n of nodeRenderData) {
const { x, y } = n.simulationData
if (!x || !y) continue
n.gfx.position.set(x + width / 2, y + height / 2)
if (n.label) {
n.label.position.set(x + width / 2, y + height / 2)
}
}
if (!graph) return
removeAllChildren(graph)
for (const l of linkRenderData) {
const linkData = l.simulationData
l.gfx.clear()
l.gfx.moveTo(linkData.source.x! + width / 2, linkData.source.y! + height / 2)
l.gfx
.lineTo(linkData.target.x! + width / 2, linkData.target.y! + height / 2)
.stroke({ alpha: l.alpha, width: 1, color: l.color })
}
tweens.forEach((t) => t.update(time))
app.renderer.render(stage)
requestAnimationFrame(animate)
}
registerEscapeHandler(container, hideGlobalGraph)
const graphAnimationFrameHandle = requestAnimationFrame(animate)
window.addCleanup(() => cancelAnimationFrame(graphAnimationFrameHandle))
}
document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const slug = e.detail.url
addToVisited(slug)
addToVisited(simplifySlug(slug))
await renderGraph("graph-container", slug)
// Function to re-render the graph when the theme changes
const handleThemeChange = () => {
renderGraph("graph-container", slug)
}
// event listener for theme change
document.addEventListener("themechange", handleThemeChange)
// cleanup for the event listener
window.addCleanup(() => {
document.removeEventListener("themechange", handleThemeChange)
})
const container = document.getElementById("global-graph-outer")
const sidebar = container?.closest(".sidebar") as HTMLElement
function renderGlobalGraph() {
const slug = getFullSlug(window)
container?.classList.add("active")
if (sidebar) {
sidebar.style.zIndex = "1"
}
renderGraph("global-graph-container", slug)
registerEscapeHandler(container, hideGlobalGraph)
}
function hideGlobalGraph() {
container?.classList.remove("active")
if (sidebar) {
sidebar.style.zIndex = "unset"
}
}
async function shortcutHandler(e: HTMLElementEventMap["keydown"]) {
if (e.key === "g" && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
e.preventDefault()
const globalGraphOpen = container?.classList.contains("active")
globalGraphOpen ? hideGlobalGraph() : renderGlobalGraph()
}
}
const containerIcon = document.getElementById("global-graph-icon")
containerIcon?.addEventListener("click", renderGlobalGraph)
window.addCleanup(() => containerIcon?.removeEventListener("click", renderGlobalGraph))
document.addEventListener("keydown", shortcutHandler)
window.addCleanup(() => document.removeEventListener("keydown", shortcutHandler))
})

View File

@ -3,7 +3,7 @@ import { normalizeRelativeURLs } from "../../util/path"
const p = new DOMParser()
async function mouseEnterHandler(
this: HTMLLinkElement,
this: HTMLAnchorElement,
{ clientX, clientY }: { clientX: number; clientY: number },
) {
const link = this
@ -33,39 +33,59 @@ 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 = ""
const contents = await fetch(`${targetUrl}`)
.then((res) => res.text())
.catch((err) => {
console.error(err)
})
const response = await fetch(`${targetUrl}`).catch((err) => {
console.error(err)
})
// bailout if another popover exists
if (hasAlreadyBeenFetched()) {
return
}
if (!contents) return
const html = p.parseFromString(contents, "text/html")
normalizeRelativeURLs(html, targetUrl)
let elts: Element[]
if (html.body.dataset.enablePreview === "false") {
const noPreview = document.createElement("div")
noPreview.innerHTML = `<p>Preview is disabled for this page.</p>`
elts = [noPreview]
} else elts = [...html.getElementsByClassName("popover-hint")]
if (elts.length === 0) return
if (!response) return
const [contentType] = response.headers.get("Content-Type")!.split(";")
const [contentTypeCategory, typeInfo] = contentType.split("/")
const popoverElement = document.createElement("div")
popoverElement.classList.add("popover")
const popoverInner = document.createElement("div")
popoverInner.classList.add("popover-inner")
popoverElement.appendChild(popoverInner)
elts.forEach((elt) => popoverInner.appendChild(elt))
popoverInner.dataset.contentType = contentType ?? undefined
switch (contentTypeCategory) {
case "image":
const img = document.createElement("img")
img.src = targetUrl.toString()
img.alt = targetUrl.pathname
popoverInner.appendChild(img)
break
case "application":
switch (typeInfo) {
case "pdf":
const pdf = document.createElement("iframe")
pdf.src = targetUrl.toString()
popoverInner.appendChild(pdf)
break
default:
break
}
break
default:
const contents = await response.text()
const html = p.parseFromString(contents, "text/html")
normalizeRelativeURLs(html, targetUrl)
const elts = [...html.getElementsByClassName("popover-hint")]
if (elts.length === 0) return
elts.forEach((elt) => popoverInner.appendChild(elt))
}
setPosition(popoverElement)
link.appendChild(popoverElement)
@ -80,7 +100,7 @@ async function mouseEnterHandler(
}
document.addEventListener("nav", () => {
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
for (const link of links) {
link.addEventListener("mouseenter", mouseEnterHandler)
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))

View File

@ -21,6 +21,7 @@ let index = new FlexSearch.Document<Item>({
encode: encoder,
document: {
id: "id",
tag: "tags",
index: [
{
field: "title",
@ -147,7 +148,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const data = await fetchData
const container = document.getElementById("search-container")
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 searchLayout = document.getElementById("search-layout")
const idDataMap = Object.keys(data) as FullSlug[]
@ -190,6 +191,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
}
searchType = "basic" // reset search type after closing
searchButton?.focus()
}
function showSearch(searchTypeNew: SearchType) {
@ -371,11 +374,6 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
throw new Error(`Could not fetch ${targetUrl}`)
}
const html = p.parseFromString(contents ?? "", "text/html")
if (html.body.dataset.enablePreview === "false") {
const noPreview = document.createElement("div")
noPreview.innerHTML = `<p>Preview is disabled for this page.</p>`
return [noPreview]
}
normalizeRelativeURLs(html, targetUrl)
return [...html.getElementsByClassName("popover-hint")]
})
@ -410,11 +408,33 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
let searchResults: FlexSearch.SimpleDocumentSearchResultSetUnit[]
if (searchType === "tags") {
searchResults = await index.searchAsync({
query: currentSearchTerm.substring(1),
limit: numSearchResults,
index: ["tags"],
})
currentSearchTerm = currentSearchTerm.substring(1).trim()
const separatorIndex = currentSearchTerm.indexOf(" ")
if (separatorIndex != -1) {
// search by title and content index and then filter by tag (implemented in flexsearch)
const tag = currentSearchTerm.substring(0, separatorIndex)
const query = currentSearchTerm.substring(separatorIndex + 1).trim()
searchResults = await index.searchAsync({
query: query,
// return at least 10000 documents, so it is enough to filter them by tag (implemented in flexsearch)
limit: Math.max(numSearchResults, 10000),
index: ["title", "content"],
tag: tag,
})
for (let searchResult of searchResults) {
searchResult.result = searchResult.result.slice(0, numSearchResults)
}
// set search type to basic and remove tag from term for proper highlightning and scroll
searchType = "basic"
currentSearchTerm = query
} else {
// default search by tags index
searchResults = await index.searchAsync({
query: currentSearchTerm,
limit: numSearchResults,
index: ["tags"],
})
}
} else if (searchType === "basic") {
searchResults = await index.searchAsync({
query: currentSearchTerm,
@ -440,8 +460,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
document.addEventListener("keydown", shortcutHandler)
window.addCleanup(() => document.removeEventListener("keydown", shortcutHandler))
searchIcon?.addEventListener("click", () => showSearch("basic"))
window.addCleanup(() => searchIcon?.removeEventListener("click", () => showSearch("basic")))
searchButton?.addEventListener("click", () => showSearch("basic"))
window.addCleanup(() => searchButton?.removeEventListener("click", () => showSearch("basic")))
searchBar?.addEventListener("input", onType)
window.addCleanup(() => searchBar?.removeEventListener("input", onType))

View File

@ -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")

View File

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

View File

@ -0,0 +1,14 @@
.content-meta {
margin-top: 0;
color: var(--gray);
&[show-comma="true"] {
> span:not(:last-child) {
margin-right: 8px;
&::after {
content: ",";
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More