Squashed commit of the following:

commit 76f2664277
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Nov 13 22:57:05 2023 -0800

    versioning: bump to v4.1.1

commit 74777118a7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Nov 13 22:51:40 2023 -0800

    feat: header and full-page transcludes (closes #557)

commit 8223465bda
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Nov 12 14:33:19 2023 -0800

    fix: make :has img selector direct

commit cf6ab9e933
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Nov 12 14:27:53 2023 -0800

    feat: option to specify npx quartz sync message (closes #583)

commit 74c63e448e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 21:13:10 2023 -0800

    fix(style): dont internal-link highlight when image (closes #581)

commit 43d638a6de
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 21:06:37 2023 -0800

    perf: compute mapping of folder name to file data for faster breadcrumbs

commit d1551872ff
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 20:46:57 2023 -0800

    fix: check if popover exists after fetching and before inserting

commit 275bea3051
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 20:46:29 2023 -0800

    style + cfg: resolve breadcrumb titles by default and change arrow character

commit bc02791734
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 20:27:51 2023 -0800

    fix: .date.getTime() based sort

commit bf603c49c2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 12:08:54 2023 -0800

    fix: sort rss feed by date

commit f67356c3d2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 12:02:34 2023 -0800

    lint: format

commit 5d666d1860
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 11:59:05 2023 -0800

    fix: normalize relative urls (closes #569)

commit 22b7cf135e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 11:41:44 2023 -0800

    types: cast in jsx.tsx to avoid @ts-ignore

commit 50a87d0d86
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 11:39:56 2023 -0800

    style: scrollable tables

commit 134b6ed582
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 10:11:31 2023 -0800

    fix: anchors links shouldnt cause reload (closes #574)

commit 99e8f5944f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 11 09:56:30 2023 -0800

    fix: trailing slash aliases (closes #577)

commit e9f4e28a2d
Author: Yes365 <ninfovores365@gmail.com>
Date:   Fri Nov 10 11:44:16 2023 +0800

    fix: adapt vercel cleanurls (#487)

    Co-authored-by: Harrison <Harrison@fanruan.com>

commit 2a6b9a9ea0
Author: Niklas Schröder <33390735+lnschroeder@users.noreply.github.com>
Date:   Tue Nov 7 18:16:48 2023 +0100

    docs: fix property name for ToC toggle (#573)

commit e806c30fa1
Author: Mau Camargo <52770775+camargomau@users.noreply.github.com>
Date:   Sun Nov 5 13:30:10 2023 -0600

    docs: Add Mau Camargo's Notkesto to showcase (#570)

commit aac7b7e97d
Author: Anson Yu <ansonyu24@gmail.com>
Date:   Sat Nov 4 17:20:16 2023 -0400

    docs: Update making plugins.md (#567)

    :)

commit 101e9946bd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 4 12:11:42 2023 -0700

    feat: add collapseByDefault option to TableOfContents (closes #566)

commit a62a97c7ab
Author: Emil Rofors <emirof@gmail.com>
Date:   Fri Nov 3 16:40:43 2023 -0700

    docs: add GitLab pages CI (#549)

    * add .gitlab-ci.yml

    * move GitLab CI to hosting.md

    * remove extra folder name

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

    * remove test from gitlab instructions

    * run prettier

    ---------

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

commit 923b72fb67
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Nov 1 10:03:45 2023 -0700

    feat: auto-tag releases (closes #560)

commit 05a1c34c6f
Author: Florence <59734957+Pydes-boop@users.noreply.github.com>
Date:   Wed Nov 1 17:57:32 2023 +0100

    docs: remove dead link (#561)

commit 06ccb89cd7
Author: Blue Rose <134471273+bluerosegarden@users.noreply.github.com>
Date:   Tue Oct 31 15:53:49 2023 -0500

    docs: clarifications about globs (#559)

    * Add note about fast-glob

    * Add warning about non-markdown files

    Also added a glob pattern to filter out all non-markdown files outside of a specified folder.

    * run npm format

    ---------

    Co-authored-by: wych <wychwitchcraft@gmail.com>

commit 01fc8e4640
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Oct 25 09:40:43 2023 -0700

    fix: disable semi-broken flexsearch cache

commit 7c01e8dde0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 22 09:54:12 2023 -0700

    feat: openLinksInNewTab option for link transformer

commit b7ae7a99db
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 21 21:12:11 2023 -0700

    fix: styling for nested popover tag in page list

commit 60b3bc34cb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 21 21:05:46 2023 -0700

    fix: catch html to jsx errors (closes #547)

commit dc834015d0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 21 20:27:49 2023 -0700

    fix(style): tag float orientation for long tags on page listing

commit 1e357ef5ac
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 21 20:09:49 2023 -0700

    fix(style): prioritize base and custom scss over component css

commit 54e722a55d
Author: freenandes <42041153+freenandes@users.noreply.github.com>
Date:   Wed Oct 18 03:43:41 2023 +0100

    docs: Update showcase.md (#540)

    changed URL

commit 86d16b12a2
Author: Thomas <65691606+NotTacoz@users.noreply.github.com>
Date:   Wed Oct 18 10:43:20 2023 +0800

    docs(explorer): Fixed small typo with extra } in explorer.md (#541)

commit ed971800c0
Author: freenandes <42041153+freenandes@users.noreply.github.com>
Date:   Tue Oct 17 16:58:28 2023 +0100

    Update showcase.md (#539)

commit af9ddadc4d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 14 13:45:56 2023 -0700

    fix(css): import base from custom instead of the other way around (#536)

commit da0a062c05
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 8 09:59:18 2023 -0700

    feat: docker support for v4 (closes #530)

commit f66d2c23ac
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 8 09:15:06 2023 -0700

    fix: ctrl+click with spa enabled

commit 3268d45a20
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Oct 5 13:48:52 2023 -0700

    css: make article relative

commit afa163f2fe
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Oct 5 13:30:06 2023 -0700

    style: styling for codeblocks without langs (#527)

commit cec4877adb
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Oct 5 18:19:56 2023 +0200

    fix(breadcrumbs): problem with folder whitespace (#522)

    * fix(breadcrumbs): problem with folder whitespace

    use slugs for folder hrefs so folder paths get resolved properly

    * feat: only use `slug` for constructing crumbs

    * fix: remove capitalization

commit cf0c090e3c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Oct 4 09:23:56 2023 -0700

    specify minimum npm version

commit c8f5dbbad3
Author: Luca Salvarani <lucasalvarani99@gmail.com>
Date:   Mon Oct 2 02:20:55 2023 +0200

    fix: Fix `Backlinks` not applying the display class (#519)

    * fix: Fix `Backlinks` not applying the display class

    Fix #518

    * fix: Apply `displayClass` to all layout components

    * refactor: Use same style

    * fix: Remove `undefined` class using coalescing operator

commit ab5efac75f
Author: bfahrenfort <59982409+bfahrenfort@users.noreply.github.com>
Date:   Sun Oct 1 11:47:22 2023 -0500

    Fix: RSS title escaping (#521)

    * Fix title escaping

    * npm run format

commit 2f99339dcf
Author: Hrishikesh Barman <geekodour@users.noreply.github.com>
Date:   Sat Sep 30 00:05:26 2023 +0530

    feat: add transformations for latex in oxhugofm (#510)

    ox-hugo currently supports the following syntax for latex equations:
    - https://orgmode.org/manual/LaTeX-fragments.html
    - https://ox-hugo.scripter.co/doc/equations

    This syntax is supported by mathjax as is mentioned in the ox-hugo documentation.

    But quartz uses remark-math which has some issues with the \( \) syntax.
    See https://github.com/remarkjs/remark-math/issues/39

    This change adds few more transformations to the OxHugoFlavouredMarkdown
    plugin, which makes a best effort conversion of this syntax into what
    the Quartz Latex transformer plugin supports.

    With these changes, the generated files show latex formatting with
    default quartz configuration.

    Sidenote on `\_` escape by ox-hugo:

    ox-hugo escapes, _ using \_, we match against it after we transform
    equations into what quartz supports($$ and $).

    This could be achieved using lookaround like regex as follows
    ```js
    (?<=(\$|\$\$)[\s\S]*) -> Positive lookbehind for $ or $$
    \\_ -> Matches \_
    (?=[\s\S]*(?:\1)) Positive lookahead for $ or $$ if matched
    const escapedUnderscoreRegex = new RegExp(/(?<=(\$|\$\$)[\s\S]*)\\_(?=[\s\S]*(?:\1))/, "g")
    ````

    But since lookahead/behind can slow things down on large files, we just
    look up all equations with $ and $$ delimiters and then try replacing \_

commit 5232d09af5
Author: ArtfulAzeria <146041757+ArtfulAzeria@users.noreply.github.com>
Date:   Fri Sep 29 20:17:48 2023 +0200

    feat: Better and more responsive tag behavior (#515)

    * fix(explorer): default sortFn implementation (#511)

    * fix: use `numeric` + `base` for localeCompare

    * docs(explorer): update default sortFn

    * fix: better and more responsive tag behavior

    * tags css moved to TagList.tsx

    * used npm run format

    * merged tag declarations

    ---------

    Co-authored-by: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>

commit 0138085c16
Author: Catchears <57631841+Catchears@users.noreply.github.com>
Date:   Fri Sep 29 17:19:10 2023 +0200

    docs: fix typo in breadcrumbs documentation (#513)

commit 0b61f6fbfd
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Sep 29 10:26:15 2023 +0200

    feat: implement breadcrumb component (#508)

    * feat: implement breadcrumbs

    * style: fix styling, move breadcrumbs to top

    * refactor: move `capitalize to `lang.ts``

    * refactor: clean breadcrumb generation

    * feat: add options to breadcrumbs

    * feat: implement `resolveFrontmatterTitle`

    * feat: add `hideOnRoot` option

    * feat(consistency): capitalize every crumb

    * style: add `flex-wrap` to parent container

    * refactor: clean `Breadcrumbs.tsx`

    * feat(accessibility): use `nav`, add aria label

    * style: improve look in popovers by adding margin

    * docs: write docs for breadcrumb component

    * refactor: collapse `if` condition for hideOnRoot

    * chore: add todo for perf optimization

    * docs: update introduction

commit d4c122646c
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Sep 28 17:39:44 2023 +0200

    fix(explorer): default sortFn implementation (#511)

    * fix: use `numeric` + `base` for localeCompare

    * docs(explorer): update default sortFn

commit d22c3c107a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Sep 25 18:15:55 2023 -0700

    fix: coerce title to string

commit 697bffdb8b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Sep 24 14:47:30 2023 -0700

    fix: treat the 0 time as invalid too

commit ea5742c328
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Sep 24 10:31:47 2023 -0700

    fix: mermaid copy source position

commit 95eec5b49d
Author: Chad Lee <git@chadly.net>
Date:   Sun Sep 24 12:27:42 2023 -0500

    add site to showcase (#504)

commit c5b9137f12
Author: Vince Imbat <96913392+vinceimbat@users.noreply.github.com>
Date:   Sat Sep 23 10:39:02 2023 +0800

    docs: Adds Vince Imbat to showcase (#501)

commit 13c8673226
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 22 10:04:37 2023 -0700

    feat: add warning for invalid date format

commit a897cc1f53
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 22 09:43:34 2023 -0700

    feat: add warning for missing home page

commit d93599364a
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Sep 22 17:20:19 2023 +0200

    docs(showcase): fix pull request redirect link (#500)

commit fa69c2a565
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Sep 21 19:35:11 2023 +0200

    fix(explorer): increase consistency, explicitly use font-family (#496)

    * fix(explorer): display name for folders without `index` file

    * docs(explorer): add section for folder display names

    * docs(explorer): fix broken wikilink

    * fix(consistency): explicitly set font + label/link fix

    Use consistent styling between folders with `folderClickBehavior: "link"` and `"collapse`

    * Update quartz/components/styles/explorer.scss

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

    * Update quartz/components/styles/explorer.scss

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

    ---------

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

commit 8eb1554b13
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Sep 21 18:54:33 2023 +0200

    fix(explorer): display names for folders without frontmatter (#494)

    * fix(explorer): display name for folders without `index` file

    * docs(explorer): add section for folder display names

commit dcdeae4e7b
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Sep 21 18:53:19 2023 +0200

    docs(explorer): update default config + new example (#493)

commit 48452231d5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 16:09:18 2023 -0700

    perf: memoize filetree computation (#490)

    * perf: memoize filetree computation

    * format

    * var -> let

commit 16d33fb771
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 16:08:54 2023 -0700

    feat: display name for folders, expand explorer a little bit (#489)

    * feat: display name for folders, expand explorer a little bit

    * update docs

commit b029eeadab
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Wed Sep 20 22:55:29 2023 +0200

    feat(explorer): improve accessibility and consistency (+ bug fix) (#488)

    * feat(consistency): use `all: unset` on button

    * style: improve accessibility and consistency for explorer

    * fix: localStorage bug with folder name changes

    * chore: bump quartz version

commit 6a9e6352e8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 13:52:45 2023 -0700

    Revert "feat: Making Quartz available offline by making it a PWA (#465)"

    This reverts commit d6301fae90.

commit 70e029d151
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 13:52:29 2023 -0700

    Revert "docs: wording changes for offline support"

    This reverts commit 52a172d1a4.

commit 0bad3ce799
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 11:58:52 2023 -0700

    docs: document enableToc

commit 52a172d1a4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 20 11:40:36 2023 -0700

    docs: wording changes for offline support

commit d6301fae90
Author: Adam Brangenberg <adambrangenberg@proton.me>
Date:   Wed Sep 20 20:38:13 2023 +0200

    feat: Making Quartz available offline by making it a PWA (#465)

    * Adding PWA and chaching for offline aviability

    * renamed workbox config to fit Quartz' scheme

    * Documenting new configuration

    * Added missig umami documentation

    * Fixed formatting so the build passes, thank you prettier :)

    * specified caching strategies to improve performance

    * formatting...

    * fixing "404 manifest.json not found" on subdirectories by adding a / to manifestpath

    * turning it into a plugin

    * Removed Workbox-cli and updated @types/node

    * Added Serviceworkercode to offline.ts

    * formatting

    * Removing workbox from docs

    * applied suggestions

    * Removed path.join for sw path

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

    * Removed path.join for manifest path

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

    * Removing path module import

    * Added absolute path to manifests start_url and manifest "import" using baseUrl

    * Adding protocol to baseurl

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

    * Adding protocol to start_url too then

    * formatting...

    * Adding fallback page

    * Documenting offline plugin

    * formatting...

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * merge suggestion

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

    * formatting...

    * Fixing manifest path, all these nits hiding the actual issues .-.

    * Offline fallback page through plugins, most things taken from 404 Plugin

    * adding Offline Plugin to config

    * formatting...

    * Turned offline off as default and removed offline.md

    ---------

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

commit 27a6087dd5
Author: rwutscher <richard.wutscher@gmail.com>
Date:   Tue Sep 19 21:26:30 2023 +0200

    fix: tag regex no longer includes purely numerical 'tags' (#485)

    * fix: tag regex no longer includes purely numerical 'tags'

    * fix: formatting

    * fix: use guard in findAndReplace() instead of expanding the regex

commit 1bf7e3d8b3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Sep 19 10:22:39 2023 -0700

    fix(nit): make defaultOptions on explorer not a function

commit cc31a40b0c
Author: David Fischer <david@konst.fish>
Date:   Tue Sep 19 18:25:51 2023 +0200

    feat: support changes in system theme (#484)

    * feat: support changes in system theme

    * fix: run prettier

    * fix: add content/.gitkeep

commit 0d3cf29226
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Mon Sep 18 23:32:00 2023 +0200

    docs: fix explorer example (#483)

commit 6a2e0b3ad3
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Sep 17 22:04:44 2023 +0200

    fix: bad visibility for last explorer item (#478)

    * fix: bad visibility for last explorer item

    * feat(explorer): add pseudo element for observer

commit e67f409ec1
Merge: af41f34b 4afb099b
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Sep 17 21:36:04 2023 +0200

    Merge pull request #479 from benschlegel/explorer-config

    feat(explorer): add config for custom sort/map/filter functions

commit 4afb099bf3
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sun Sep 17 21:32:23 2023 +0200

    docs: fix examples

commit 6914d4b40c
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sun Sep 17 21:20:09 2023 +0200

    docs: fix intra page links

commit af41f34bfd
Author: Christian Gill <gillchristiang@gmail.com>
Date:   Sun Sep 17 20:02:00 2023 +0200

    fix(slug): Handle question mark (#481)

commit 7ac772fca8
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Sep 17 19:29:20 2023 +0200

    fix: darkmode scroll bars (#480)

commit 5cc9253c41
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sun Sep 17 16:41:23 2023 +0200

    docs(explorer): write docs for new features

commit 94a04ab1c9
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sun Sep 17 15:51:08 2023 +0200

    fix(explorer): filter function in `ExplorerNode`

commit 9358f73f1c
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sun Sep 17 12:41:06 2023 +0200

    fix: display name for file nodes

commit f7029012df
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 21:58:38 2023 +0200

    feat: black magic

    add config for `order` array, which determines the order in which all passed config functions for explorer will get executed in.

    functions will now dynamically be called on `fileTree` via array accessor (e.g. fileTree["sort"].call(...)) with corresponding function from options being passed to call)

commit fea352849c
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 19:45:21 2023 +0200

    fix: create deep copy of file passed into tree

commit 3d8c470c0d
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 19:35:27 2023 +0200

    feat(explorer): implement `map` fn argument

    Add a function for mapping over all FileNodes as an option for `Explorer`

commit 31d16fbd2c
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 19:18:59 2023 +0200

    feat(explorer): integrate filter option

commit 036a33f70b
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 17:47:44 2023 +0200

    fix: use correct import for `QuartzPluginData`

commit 58aea1cb07
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 17:28:58 2023 +0200

    feat: implement filter function for explorer

commit c7d3474ba8
Author: Ben Schlegel <ben5.schlegel@gmail.com>
Date:   Sat Sep 16 12:40:19 2023 +0200

    feat(explorer): add config to support custom sort fn

commit 422ba5c365
Author: Yuto Nagata <38714187+mouse484@users.noreply.github.com>
Date:   Sat Sep 16 11:17:20 2023 +0900

    fix: umami analytics date attribute (#477)

commit 9ae6343dd0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 15 10:33:38 2023 -0700

    Revert "fix: use git dates by default, @napi/git is fast enough"

    This reverts commit 5dcb7e83fc.

commit 5dcb7e83fc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 15 09:46:06 2023 -0700

    fix: use git dates by default, @napi/git is fast enough

commit 91f9ae2d71
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Sep 15 18:39:16 2023 +0200

    feat: implement file explorer component (closes #201) (#452)

    * feat: add basic explorer structure„

    * feat: integrate new component/plugin

    * feat: add basic explorer structure

    * feat: add sort to FileNodes

    * style: improve style for explorer

    * refactor: remove unused explorer plugin

    * refactor: clean explorer structure, fix base (toc)

    * refactor: clean css, respect displayClass

    * style: add styling to chevron

    * refactor: clean up debug statements

    * refactor: remove unused import

    * fix: clicking folder icon sometimes turns invisible

    * refactor: clean css

    * feat(explorer): add config for title

    * feat: add config for folder click behavior

    * fix: `no-pointer` not being set for all elements

    new approach, have one `no-pointer` class, that removes pointer events and one `clickable` class on the svg and button (everything that can normally be clicked). then, find all children with `clickable` and toggle `no-pointer`

    * fix: bug where nested folders got incorrect height

    this fixes the bug where nested folders weren't calculating their total height correctly. done by adding class to main container of all children and calculating total

    * feat: introduce `folderDefaultState` config

    * feat: store depth for explorer nodes

    * feat: implement option for collapsed state + bug fixes

    folderBehavior: "link" still has bad styling, but major bugs with pointers fixed (not clean yet, but working)

    * fix: default folder icon rotation

    * fix: hitbox problem with folder links, fix style

    * fix: redirect url for nested folders

    * fix: inconsistent behavior with 'collapseFolders' opt

    * chore: add comments to `ExplorerNode`

    * feat: save explorer state to local storage (not clean)

    * feat: rework `getFolders()`, fix localstorage read + write

    * feat: set folder state from localStorage

    needs serious refactoring but functional (except folder icon orientation)

    * fix: folder icon orientation after local storage

    * feat: add config for `useSavedState`

    * refactor: clean `explorer.inline.ts`

    remove unused functions, comments, unused code, add types to EventHandler

    * refactor: clean explorer

    merge `isSvg` paths, remove console logs

    * refactor: add documentation, remove unused funcs

    * feat: rework folder collapse logic

    use grids instead of jank scuffed solution with calculating total heights

    * refactor: remove depth arg from insert

    * feat: restore collapse functionality to clicks

    allow folder icon + folder label to collapse folders again

    * refactor: remove `pointer-event` jank

    * feat: improve svg viewbox + remove unused props

    * feat: use css selector to toggle icon

    rework folder icon to work purely with css instead of JS manipulation

    * refactor: remove unused cfg

    * feat: move TOC to right sidebar

    * refactor: clean css

    * style: fix overflow + overflow margin

    * fix: use `resolveRelative` to resolve file paths

    * fix: `defaultFolderState` config option

    * refactor: rename import, rename `folderLi` + ul

    * fix: use `QuartzPluginData` type

    * docs: add explorer documentation

commit 14cbbdb8a2
Author: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com>
Date:   Thu Sep 14 05:55:59 2023 +0200

    feat: display tag in graph view (#466)

    * feat: tags in graph view

    * fix: revert changing graph forces

    * fix: run prettier

commit cce389c81d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 13 11:28:53 2023 -0700

    feat: note transclusion (#475)

    * basic transclude

    * feat: note transclusion

commit 4461748a85
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 13 09:43:14 2023 -0700

    fix dont show html in search when rssFullHtml is true (closes #474)

commit 6ecdcb5e24
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Sep 12 22:55:50 2023 -0700

    feat: resolve block references in obsidian markdown

commit e3b879741b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Sep 12 21:44:03 2023 -0700

    feat: rich html rss (closes #460)

commit 60a3c54339
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Sep 12 21:29:57 2023 -0700

    fix: 404 page styling for nested pages (closes #458)

commit 71d81bde1d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Sep 12 19:18:44 2023 -0700

    feat: rss limit (closes #459)

commit a19df64be8
Author: hcplantern <38579760+HCPlantern@users.noreply.github.com>
Date:   Tue Sep 12 14:00:21 2023 +0800

    fix: callout parsing (#469)

commit 4e23e67244
Author: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com>
Date:   Mon Sep 11 08:11:42 2023 +0200

    feat: plugin for remark-breaks (#467)

    * feat: plugin for remark-breaks

    * fix: update package-lock.json

    * fix: styling

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

    * Update linebreaks.ts

    * Update index.ts

    ---------

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

commit a66c239797
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Sep 10 23:07:17 2023 -0700

    ci: print bundleInfo

commit 53f1c88738
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 8 09:29:57 2023 -0700

    fix: more lenient date parsing for templates

commit 06df00b186
Author: Stefano Cecere <stefano.cecere@krur.com>
Date:   Thu Sep 7 17:13:41 2023 +0200

    typo (it's draft, not drafts) (#456)

commit 2525bfbab5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 22:24:15 2023 -0700

    fix: links to index not showing in graph (closes #450)

commit 828aa71fe3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 21:47:59 2023 -0700

    fix: escape encoding for titles in rss

commit ef1ead31dc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 21:31:01 2023 -0700

    fix: encodeuri for slugs in rss

commit 989bee5979
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 21:08:08 2023 -0700

    docs: correct field for ignorePatterns

commit 8d6029b7b8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 21:02:21 2023 -0700

    feat: 404 page emitter

commit 2d52eba413
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Sep 6 20:25:38 2023 -0700

    fix: dont transform external links

commit 6ef4246cf1
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Mon Sep 4 07:36:30 2023 +0200

    docs: update `full-text-search.md` (#447)

commit 616a7f148a
Author: Dr Kim Foale <kim@gfsc.studio>
Date:   Mon Sep 4 05:29:58 2023 +0100

    docs: Make it clearer that wikilinks go to paths not page titles (#448)

commit e8a04efaf1
Author: Adam Brangenberg <adambrangenberg@proton.me>
Date:   Mon Sep 4 06:28:57 2023 +0200

    feat(analytics): Support for Umami (#449)

commit 7e42be8e46
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Sep 3 18:32:46 2023 +0200

    feat(search): add arrow key navigation (#442)

    * feat(search): add arrow navigation

    * chore: format

    * refactor: simplify arrow navigation

    * chore: remove comment

    * feat: rework arrow navigation to work without state

    * feat: make pressing enter work with arrow navigation

    * fix: remove unused css class

    * chore: correct comment

    * refactor(search): use optional chaining

commit 8c354f6261
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Sep 3 18:06:05 2023 +0200

    fix: clipboard button visible in search (#445)

commit 505673acd7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Sep 2 18:07:26 2023 -0700

    feat: pluralize things in lists

commit 23f43045c4
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Sep 1 23:12:32 2023 +0200

    fix(search): matches getting highlighted in title (#440)

commit 90dac31216
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Sep 1 19:09:58 2023 +0200

    feat: Implement search for tags (#436)

    * Quartz sync: Aug 29, 2023, 10:17 PM

    * style: add basic style to tags in search

    * feat: add SearchType + tags to search preview

    * feat: support multiple matches

    * style(search): add style to matching tags

    * feat(search): add content to preview for tag search

    * fix: only display tags on tag search

    * feat: support basic + tag search

    * refactor: extract common `fillDocument`, format

    * feat: add hotkey to search for tags

    * chore: remove logs

    * fix: dont render empty `<ul>` if tags not present

    * fix(search-tag): make case insensitive

    * refactor: clean `hideSearch` and `showSearch`

    * feat: trim content similar to `description.ts`

    * fix(search-tag): hotkey for windows

    * perf: re-use main index for tag search

commit 2d6dc176c3
Author: Pelayo Arbués <gonzalezpelayo@gmail.com>
Date:   Thu Aug 31 21:12:06 2023 +0200

    Adds Pelayo Arbues to showcase (#435)

commit b213ba45e2
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Aug 31 20:55:04 2023 +0200

    fix: regex for matching highlights (closes #437) (#438)

    * fix:  regex for matching highlights

    * fix: regex for empty highlights

commit 5fa6fc9789
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 29 10:37:00 2023 -0700

    fix: aliasredirects not using full path, add permalink support

commit 1cc09ef76d
Author: Jeffrey Fabian <jeffrey.fabian61@gmail.com>
Date:   Tue Aug 29 13:14:54 2023 -0400

    feat: support kebab-case and nested tags in Obsidian-flavored Markdown tag-in-content parsing (#425)

    * enhancement: support kebab-case and nested tags in ofm transformer

    * update regex/capture groups to allow for (arbitrarily) nested values and tags of only -/_

    * Update quartz/plugins/transformers/ofm.ts

    ---------

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

commit c35cd422c6
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Mon Aug 28 19:00:49 2023 +0200

    fix: correct graph labels for `index.md` nodes (#431)

commit 082fdf2e80
Author: Jeremy Press <jeremypress1@gmail.com>
Date:   Sun Aug 27 20:57:19 2023 -0700

    Fix typo :) (#430)

commit b6b1dabde0
Author: Jeremy Press <jeremypress1@gmail.com>
Date:   Sun Aug 27 17:39:42 2023 -0700

    feat: support configurable ws port and remote development (#429)

    Co-authored-by: Jeremy Press <jeremy@replit.com>
    Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

commit 4b89202f7e
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Mon Aug 28 00:59:51 2023 +0200

    cleanup: rework cli to allow invoking create and build outside of cli (#428)

    * refactor: move `bootstrap-cli.mjs` tp cli

    also update reference in docs

    * refactor(cli): move build handler to `cli-functions`

    * refactor(cli): move create to handler + helpers

    * refactor(cli): extract arg definitions

    * refactor: rename handlers and helpers

    * refactor(cli): move update, await handlers

    * refactor(cli): create constants, migrate to helpers

    * refactor(cli): migrate `restore`

    * refactor(cli): migrate `sync`

    * format

    * refactor(cli): remove old imports/functions

    * refactor(cli): remove unused imports + format

    * chore: remove old log statement

    * fix: fix imports, clean duplicate code

    * fix: relative import

    * fix: simplified cacheFile path

    * fix: update cacheFile import path

    * refactor: move bootstrap-cli to quartz

    * format

    * revert: revert path to bootstrap-cli

    * ci: re-run

    * ci: fix  execution permission

commit 52ca312f41
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 27 12:27:42 2023 -0700

    fix: slugify tag on page before adding (closes #411)

commit c91e62c376
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sun Aug 27 02:19:45 2023 +0200

    Fix search bar after navigate (#424)

commit ad4145fb10
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Sat Aug 26 22:21:44 2023 +0200

    feat: support CLI arguments for `npx quartz create` (#421)

    * feat(cli): add new args for content + link resolve

    * feat(cli): validate cmd args

    * feat(cli): add chalk + error code to errors

    * feat(cli): support for setup/link via args

    * refactor(cli): use yargs choices instead of manual

    Scrap manual check if arguments are valid, use yargs "choices" field instead.

    * feat(cli): add in-dir argument+ handle errors

    add new "in-directory" argument, used if "setup" is "copy" or "symlink" to determine source. add error handling for invalid permutations of arguments or non existent path

    * feat(cli): dynamically use cli or provided args

    use "in-directory" arg as `originalFolder` if available, otherwise get it from manual cli process

    * run format

    * fix: use process.exit instead of return

    * refactor: split CommonArgv and CreateArgv

    * refactor(cli): rename create args, use ${} syntax

    * fix(cli): fix link resolution strategy arg

    * format

    * feat(consistency): allow partial cmd args

commit 74c3ebb7bd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 26 10:48:34 2023 -0700

    style: fix mulitline callout styling

commit e3265f8416
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 26 10:42:55 2023 -0700

    docs: simplify oxhugo page

commit bc543f81d9
Author: Hrishikesh Barman <geekodour@users.noreply.github.com>
Date:   Sat Aug 26 11:22:23 2023 +0530

    feat(plugins): add OxHugoFlavouredMarkdown (#419)

    * feat(plugins): add OxHugoFlavouredMarkdown

    ox-hugo is an org exporter backend that exports org files to
    hugo-compatible markdown in an opinionated way. This plugin adds some
    tweaks to the generated markdown to make it compatible with quartz but
    the list of changes applied it is not extensive.

    In the future however, we could leapfrog ox-hugo altogether and
    create a quartz site directly out of org-roam files. That way we won't
    have to do all the ritual dancing that this plugin has to perform.
    See https://github.com/k2052/org-to-markdown

    * fix: add toml to remarkFrontmatter configuration

    * docs: add docs for OxHugoFlavouredMarkdown

    * fixup! docs: add docs for OxHugoFlavouredMarkdown

commit 5c6d1e27ba
Author: Hrishikesh Barman <geekodour@users.noreply.github.com>
Date:   Fri Aug 25 22:55:46 2023 +0530

    feat(plugins): add toml support for frontmatter (#418)

    * feat(plugins): add toml support for frontmatter

    Currently frontmatter is expected to be yaml, with delimiter set to
    "---". This might not always be the case, for example ox-hugo(a hugo
    exporter for org-mode files) exports in toml format with the delimiter
    set to "+++" by default.

    With this change, the users will be able use frontmatter plugin to
    support this toml frontmatter format.

    Example usage: `Plugin.FrontMatter({delims: "+++", language: 'toml'})`

    - [0] https://ox-hugo.scripter.co/doc/org-meta-data-to-hugo-front-matter/

    * fixup! feat(plugins): add toml support for frontmatter

commit 340e3ef511
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Fri Aug 25 18:03:49 2023 +0200

    feat(consistency): Add `.obsidian` to ignorePatterns (#420)

commit 953ef29f4e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 12:31:15 2023 -0700

    format, ensure ci runs on prs

commit 94ce0883e7
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Aug 24 21:28:06 2023 +0200

    style: integrate tertiary color to text-select (#413)

commit 8cf7280614
Author: Zero King <l2dy@icloud.com>
Date:   Fri Aug 25 02:41:20 2023 +0800

    feat: reproducible build (#412)

    for sitemap, RSS and contentIndex.json.

commit c8412a5b0a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 10:03:14 2023 -0700

    format

commit fc4b8f3d3f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 09:38:00 2023 -0700

    fix: ensure recentnotes uses proper date

commit 6cd0612d40
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 09:17:43 2023 -0700

    fix: add better warning when defaultDateType is not set due to upgrade

commit 9851697b58
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 09:05:19 2023 -0700

    version bump to 4.0.10

commit c36a9f3fb7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 08:56:40 2023 -0700

    feat: add defaultDateType config

commit 98d82415dc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 24 08:31:06 2023 -0700

    fix: lock to never read when site is building

commit 9d2340e90b
Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com>
Date:   Thu Aug 24 17:14:52 2023 +0200

    docs: fix typo in `authoring content.md` (#408)

commit 8200c8d040
Author: bfahrenfort <59982409+bfahrenfort@users.noreply.github.com>
Date:   Thu Aug 24 00:57:49 2023 -0500

    Revert contentIndex to RSS 2.0 (#407)

commit 2e0e518f5d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 15:16:04 2023 -0700

    format

commit 632c27b7ec
Author: Zane Helton <me@zaaane.com>
Date:   Wed Aug 23 18:14:23 2023 -0400

    docs: update `hosting.md` with Vercel hosting instructions (#406)

    * Update hosting.md with Vercel hosting instructions

    * Update docs/hosting.md

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

    * Update docs/hosting.md

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

    * Run npm run format

    ---------

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

commit bfb416b35a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 13:10:23 2023 -0700

    fix: text wrap in popover

commit 960c1814d0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 12:23:49 2023 -0700

    docs: make incompability of trailing slashes clear

commit eed4472aee
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 12:18:50 2023 -0700

    fix: use proper full base for links.ts

commit b99eb7ebce
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 12:11:16 2023 -0700

    docs: whitespace

commit 0aaf88b852
Author: kanpov <71177577+kanpov@users.noreply.github.com>
Date:   Wed Aug 23 22:09:04 2023 +0300

    Fix #403 by moving documentation to separate directory to avoid merge conflicts (#405)

commit a1a1e7e1e0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 11:36:34 2023 -0700

    fix: builds should no accumulate on repeated changes (closes #404)

commit 3209f7c3b7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 09:19:00 2023 -0700

    deps: native addons for lightningcss

commit cde1e26129
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 09:16:44 2023 -0700

    deps: install exact

commit 1128efcf23
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 23 09:10:30 2023 -0700

    deps: esbuild and esbuild-sass-plugin

commit d2f5254995
Author: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
Date:   Wed Aug 23 12:05:01 2023 -0400

    fix(esbuild): conflict with esbuild-sass-plugin (#402)

commit 3064839c2d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 23:37:02 2023 -0700

    version bump to 4.0.9

commit b444c5c13b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 23:33:58 2023 -0700

    fix: percent-encoding for files with %, contentIndex for non-latin chars (closes #397, closes #399)

commit 36548d5986
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 22:41:50 2023 -0700

    fix: toc for cyrillic and other non-latin alphabets (closes #396)

commit 99dbe525d9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 22:27:41 2023 -0700

    fix: properly lock across source and content refresh by sharing a mutex

commit 8b63ff882a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 22:14:16 2023 -0700

    fix: tag support for non-latin alphabets (fixes #398)

commit b991cf2ee8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 22 21:30:31 2023 -0700

    fix: spa hijacks back button (closes #400)

commit bb677840fc
Author: 松浦 知也 Matsuura Tomoya <me@matsuuratomoya.com>
Date:   Wed Aug 23 01:16:55 2023 +0900

    fixed broken CJK links (#390)

commit c60b3d5e34
Author: Ikko Eltociear Ashimine <eltociear@gmail.com>
Date:   Wed Aug 23 01:16:21 2023 +0900

    fix: typo in bootstrap-cli.mjs (#394)

commit e10de3febf
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 21 17:01:18 2023 -0700

    fix: server-handler crash from filename (closes #386)

commit b69556c918
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 21 16:43:22 2023 -0700

    fix: async-mutex not exclusively locking correectly

commit ce70571072
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 21 09:14:47 2023 -0700

    docs: use canonical quartz.jzhao.xyz, update bootstrap script to point to correct hosting link

commit 8c943f47d6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 21 09:00:13 2023 -0700

    format, update default sidepanel width

commit 2774e976d2
Author: 松浦 知也 Matsuura Tomoya <me@matsuuratomoya.com>
Date:   Tue Aug 22 00:45:47 2023 +0900

    fix: opts being overriden in graph option (#384)

commit bb93ac1c83
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 23:50:19 2023 -0700

    docs: fix links to networked thought

commit 777ff51c7a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 20:48:35 2023 -0700

    format

commit 4e42d52e16
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 20:47:07 2023 -0700

    fix: ctrl + k breaking after page nav

commit d0f67d9935
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 18:41:37 2023 -0700

    move wss server start after http

commit 952d6cb3dd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 18:08:44 2023 -0700

    fix: nav event with spa off, anchor nav refresh page

commit 173ec240d2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 17:50:56 2023 -0700

    fix: jump to anchor on deployed site triggering spa refresh

commit 425c9789a4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 16:59:25 2023 -0700

    remove checkout step from instructions as v4 is the default branch

commit 7b7064ad2b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 15:38:37 2023 -0700

    fix: ensure code exists inside pre before adding clipboard

commit ca17af4ae2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 15:02:24 2023 -0700

    fix: dont show index page for folder in its own listing

commit 71471117c5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 14:34:00 2023 -0700

    fix: ci runs on v4

commit e65ea48fae
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 14:27:44 2023 -0700

    fix: add async-mutex to builds on large vaults

commit b99d4cd8ce
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 14:05:37 2023 -0700

    recent notes css fixes

commit 1bb00e72bb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 13:00:33 2023 -0700

    add docs for recent notes

commit 236130ac22
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 12:46:37 2023 -0700

    css fixes, add recent notes, more robust quartz update

commit 5adf3c67a8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 08:57:56 2023 -0700

    add engines field

commit 9d77edaf94
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 01:08:18 2023 -0700

    fix description not being used in folder and tag listings

commit 0ef1b5b522
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 00:54:13 2023 -0700

    update plausible url

commit cfb7d1232e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 00:52:49 2023 -0700

    docs: update notes for tag and folder listings

commit 03fd62496f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 20 00:02:41 2023 -0700

    docs: note about updating default branch

commit d205eb5686
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 22:19:49 2023 -0700

    docs: make setting upstream more clear, docs on npx quartz restore

commit 96a3bfeafb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 22:04:29 2023 -0700

    fix: put quotations around font

commit 95fb6ccfcb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:59:20 2023 -0700

    readme fix

commit e262482921
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:59:01 2023 -0700

    fix: string for aliases being treated as array of chars

commit eb4d3dc5b4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:55:09 2023 -0700

    css: fix scrollbars on windows

commit 90d6c1ed24
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:38:10 2023 -0700

    add git fetch to migration instructions

commit 443c182890
Merge: 791b8e2d a6236d97
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:16:31 2023 -0700

    Merge branch 'v4' of https://github.com/jackyzha0/quartz into v4

commit 791b8e2d9f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 21:16:24 2023 -0700

    add sponsors

commit a6236d97cf
Author: Matt Dunn <55315824+TheRealMattDunn@users.noreply.github.com>
Date:   Sun Aug 20 03:15:14 2023 +0100

    Adding to Showcase page (#367)

commit b1debaebff
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 18:56:45 2023 -0700

    update docs

commit 7b8017413c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 18:04:17 2023 -0700

    impl baseDir option for quartz build --serve for local testing

commit 6681f28af0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 16:55:36 2023 -0700

    fix trailing slash causing folder listing to not fetch content correctly

commit 78f4cdbe10
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 16:40:02 2023 -0700

    avoid 404 on icon for spa navigations with anchors

commit dd47be1bc6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 16:28:44 2023 -0700

    improve path resolution stability

commit c874e7e937
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 19 15:52:25 2023 -0700

    base path refactor to better support subpath hosting

commit 3201f83b70
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 18 18:24:09 2023 -0700

    v4-alpha -> v4

commit d8bec631b6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 18 18:22:38 2023 -0700

    update docs on github pages and syncing

commit 6f1f820289
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 23:39:15 2023 -0700

    fix typo in docs

commit 8bc7a50dfa
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:54:42 2023 -0700

    format

commit 569beb410b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:49:58 2023 -0700

    ensure sync includes untracked files

commit 5713d30670
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:24:41 2023 -0700

    ensure contentfolder is passed to popContentFolder

commit a130945443
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:20:15 2023 -0700

    fix when symlink targ is calculated and added npx quartz restore

commit e10f6da011
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:08:26 2023 -0700

    format

commit a7cca3242a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 21:07:40 2023 -0700

    deref symlink on quartz sync

commit 0998bc355e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 01:58:11 2023 -0700

    fix rebuild debouncing

commit 07a327e05a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 01:34:50 2023 -0700

    fix back button in spa not working between two pages that both have hash fragments

commit 58d9dc0528
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 00:55:52 2023 -0700

    format

commit 0c199975f2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 17 00:55:28 2023 -0700

    various path fixes for links to extensions, fix relative paths in links

commit 2dc0ae279c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 16 22:09:11 2023 -0700

    fix import paths

commit 2f6747b166
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 16 22:04:15 2023 -0700

    fix relative path resolution in router and link crawling

commit 232652149a
Author: Sohaib <98542228+sohaibology@users.noreply.github.com>
Date:   Mon Aug 14 20:59:47 2023 -0400

    Update hosting.md (#371)

commit 7bde99b4e2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 13 17:47:07 2023 -0700

    fix: add trailing slash to local serving

commit f1c9ca495e
Author: vintro <77507980+vintrocode@users.noreply.github.com>
Date:   Sun Aug 13 20:19:50 2023 -0400

    docs: note about existing content at same path on different branches

commit 4f4b04eeb4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 21:18:51 2023 -0700

    format docs

commit d6e73f221c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 21:16:34 2023 -0700

    fix relative path resolution logic, add more path tests

commit 6d9ffd6da5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 17:44:35 2023 -0700

    404 page styling on local

commit c89f8b1a9a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 10:33:57 2023 -0700

    fix nested callout folding

commit 8fd496bbef
Author: Sohaib <98542228+sohaibology@users.noreply.github.com>
Date:   Sat Aug 12 16:52:16 2023 -0400

    Update hosting.md (#368)

commit aed3f5fccb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 10:17:07 2023 -0700

    fmt

commit c55d54f068
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 10:16:55 2023 -0700

    enable rich text in callout title

commit 7bffc2183e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 00:24:30 2023 -0700

    include home page in search

commit 827dd91847
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 12 00:03:11 2023 -0700

    format, make search async

commit e1dd6aee86
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 23:55:17 2023 -0700

    fix wikilinks to anchors in the same document

commit 83269ac26e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 23:40:06 2023 -0700

    fix scanning for tags in content

commit ed62ece491
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 23:27:59 2023 -0700

    fix broken tag listing links to tags

commit 736c3981c4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 23:25:44 2023 -0700

    fix emit filepaths, tag emit being overriden by content

commit 79e828696a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 22:47:50 2023 -0700

    feature docs

commit 259d0a6d9a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 11 00:31:44 2023 -0700

    more documentation

commit df02ea20d7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 10 21:32:11 2023 -0700

    spacing fix

commit 21cc6a5da9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 10 21:29:11 2023 -0700

    run prettier

commit cefbca4753
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 10 21:16:07 2023 -0700

    docs on making plugins

commit ad3f7b2d5f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 9 09:18:44 2023 -0700

    format

commit ebf3263b7e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 9 09:10:40 2023 -0700

    update npx quartz update script

commit cea6834fef
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 9 00:26:33 2023 -0700

    profiling, better concurrency heuristics

commit 68ccd1d79d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 22:53:01 2023 -0700

    format

commit 49bd6bc3ff
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 22:52:49 2023 -0700

    better concurrency debugging, --concurrency flag for npx quartz build

commit e4950e06a1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 21:31:36 2023 -0700

    fix getFileExtension missing numeric extensions (e.g. mp4)

commit e21f0f9bb9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 21:28:09 2023 -0700

    change reading time to content meta

commit ee9ed4f287
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 20:36:24 2023 -0700

    fix head.tsx

commit 2706a137a0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 8 20:18:31 2023 -0700

    guide to creating components

commit 09d4eb0684
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 23:57:24 2023 -0700

    fix notes

commit 533d68e642
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 23:56:50 2023 -0700

    most of creating components, increase legibility of bold in article and callouts

commit 774a162850
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 21:51:23 2023 -0700

    format

commit 2ac5dd49da
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 21:51:06 2023 -0700

    fix regression in code block font-size boosting on safari mobile

commit 527ce6546e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 21:41:18 2023 -0700

    various css fixes, fix new image loading bug when previewing, path docs

commit d02af6a8ae
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 7 17:34:38 2023 -0700

    architecture, fix vendor prefixing

commit b4cacd5956
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 22:07:33 2023 -0700

    format

commit cd9dc6ecb5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 22:07:08 2023 -0700

    fix css transforms for mobile

commit d8d9dd22c9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 20:52:17 2023 -0700

    fix shortest path for non-md files, mobile fix

commit 075ac33474
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 19:54:11 2023 -0700

    note formatting

commit 3adc73a703
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 19:52:30 2023 -0700

    docs upgrade, ci changes

commit 028bcec62c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 6 17:09:29 2023 -0700

    mobile fixes, fix bug when linking to anchor on home, docs

commit db6054a8c1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 5 18:00:52 2023 -0700

    format, remove markdown from being procesed

commit a0d651d64d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 5 17:53:29 2023 -0700

    reverse query param hack to re-add sourcemap support

commit 1da467d214
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 5 16:43:50 2023 -0700

    non-admonition callout fix

commit 7c09627df4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 5 15:34:10 2023 -0700

    improve hot reload robustness

commit c402f0c385
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Aug 5 11:28:09 2023 -0700

    more robust error handling, config hotreload

commit 9e76b257d4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 4 22:35:21 2023 -0700

    fix mermaid initialization

commit 21a7ec2307
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 23:36:00 2023 -0700

    bump mathjax version

commit 6423f85614
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 23:28:34 2023 -0700

    fix execsync

commit 3a2eae0a16
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 23:24:34 2023 -0700

    fix fetch flags

commit 2acfb9e870
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 23:08:04 2023 -0700

    format, add upstream

commit 93986c6e7c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 22:29:46 2023 -0700

    update pull strategy

commit 4877a9c934
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 3 00:08:13 2023 -0700

    fix callout aliases not being used properly

commit 6457496b4b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 23:42:49 2023 -0700

    readme fixes, force

commit fdf1e2a41d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 23:29:28 2023 -0700

    use checkout for pulling updates

commit 663c41fa41
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 23:04:26 2023 -0700

    use posix style paths for all path ops

commit de72dd4e4a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 22:16:46 2023 -0700

    format

commit 5537ca41e0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 22:16:32 2023 -0700

    use autostash and pull

commit 558a509164
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 22:11:46 2023 -0700

    format

commit d7842e0ce7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 22:10:13 2023 -0700

    make path and globbing more platform invariant

commit 264ea3d544
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 20:59:56 2023 -0700

    add gitattributes for windows

commit 0a33ff7a82
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 20:56:31 2023 -0700

    fix test matrix for ci

commit 429f331c21
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 20:53:13 2023 -0700

    make ci also run on windows, re-add css minification

commit 9a0f20012a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 2 00:07:41 2023 -0700

    windows patches

commit c8c108c7f7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 1 23:29:58 2023 -0700

    change default strategy to be rebase

commit aaae7d46c2
Merge: a70e846b cbae88fc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 1 22:48:32 2023 -0700

    Merge branch 'v4-alpha' of https://github.com/jackyzha0/quartz into v4-alpha

commit a70e846b0a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 1 22:47:16 2023 -0700

    flag to allow ofm replace in html embed

commit cbae88fc4e
Author: Adam Brangenberg <adambrangenberg@proton.me>
Date:   Mon Jul 31 05:08:32 2023 +0100

    Removing redundant properties (#356)

commit cc79502670
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 25 23:37:24 2023 -0700

    make layouts simpler to think about

commit 45f9087f03
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 25 22:27:59 2023 -0700

    fix checkbox/tasklist styling

commit 1c1a569023
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 25 21:11:06 2023 -0700

    fix formatting

commit cee2883c08
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 25 21:10:37 2023 -0700

    nested tag support and tag index page

commit c0278a8c65
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jul 24 21:54:47 2023 -0700

    font loading options, optimize css

commit e82ba97a39
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jul 24 00:07:58 2023 -0700

    actually add processed tag to frontmatter

commit 041a4ce7bc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jul 24 00:04:01 2023 -0700

    fix watch-mode batching

commit 569ff1a801
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 21:53:34 2023 -0700

    npm i on quartz update

commit 351b4ab13b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 21:41:09 2023 -0700

    styling fixes for stacking order and overflow

commit 4811500b1b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 18:20:43 2023 -0700

    make component resources a proper emitter

commit 236ba56be1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 17:59:44 2023 -0700

    version bump, update doc

commit 7c2bb4ee4c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 17:58:35 2023 -0700

    bundleinfo flag, minify scripts

commit 8fd75ffbfd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 17:42:00 2023 -0700

    support attachments folder

commit 55a1fb8c41
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 17:09:12 2023 -0700

    format

commit 9e83af04a7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 17:07:19 2023 -0700

    refactor static and asset emission to be actual emitter plugins

commit 000eb4c3c0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 15:37:06 2023 -0700

    update feature list

commit 5599eb590e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 14:02:57 2023 -0700

    feat: process tags in content

commit ae2e3b463a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 11:49:26 2023 -0700

    improve error handling while serving

commit fd7c33c537
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 11:19:15 2023 -0700

    style fixes for search bar and title on mobile

commit 76fdb3b4d8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 11:04:20 2023 -0700

    fix styles

commit 27a5f7ef8e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 23 11:02:45 2023 -0700

    various typography and styling fixes

commit ab228748ab
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 22 17:42:13 2023 -0700

    oops actually use npm run check

commit 76fa9bbe00
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 22 17:39:10 2023 -0700

    run prettier on ci

commit 7db2eda76c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 22 17:27:41 2023 -0700

    run prettier

commit 2034b970b6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 22 17:26:03 2023 -0700

    configure prettier

commit 8dd73704e6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 22 16:06:36 2023 -0700

    hot content reload

commit b7966ff7fa
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 20 21:51:55 2023 -0700

    update features list

commit 01d7d8e554
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 19 23:03:59 2023 -0700

    fix tag pages to emit to tag/index.html to override content and folder pages

commit 83d47f7aaa
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 19 22:00:44 2023 -0700

    rename github action

commit 76c092dcf2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 19 21:59:48 2023 -0700

    add custom.scss

commit 410fc9c8d3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 19 21:59:39 2023 -0700

    quartz update and quartz sync

commit 8e0ba45789
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 16 10:39:35 2023 -0700

    add link resolution prompt to quartz create

commit f82282367e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 15 23:33:06 2023 -0700

    treat _index as index

commit a3e4c86a4c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 15 23:05:17 2023 -0700

    fix ci, disable strict path type checks by default

commit 3ac6b42e16
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 15 23:02:12 2023 -0700

    finish path refactoring, add sourcemap + better trace support

commit 906f91f8ee
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 13 00:19:35 2023 -0700

    base path refactor, more docs

commit 08f8e3b4a4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 9 19:32:24 2023 -0700

    docs + various polish

commit b90590b9f4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 8 14:36:02 2023 -0700

    polish

commit b3480bdc49
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 6 19:18:18 2023 -0700

    fix styling for bullet points

commit 9cbacca2d4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 6 18:45:38 2023 -0700

    handle dates as tags

commit 05d1ca01c3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 6 18:32:48 2023 -0700

    handle string tags

commit f7bf4038dc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 6 16:56:30 2023 -0700

    fix path parsing

commit 465804a389
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 5 00:16:06 2023 -0700

    basic docs, remove publish, add quartz create

commit 92ca787092
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 18:26:11 2023 -0700

    fix default callout state

commit fe2852ff25
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 18:08:36 2023 -0700

    update package

commit 974b0da308
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 18:02:59 2023 -0700

    folder and tag descriptions, re-enable relative pathing

commit 2a17431460
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 17:14:15 2023 -0700

    fix popover zindex

commit 38cff2d670
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 16:48:36 2023 -0700

    more visual polish, adjust colours and spacing

commit ab9da02c60
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 4 10:08:32 2023 -0700

    fix indexing causing main thread freeze, various polish

commit e0ebee5aa9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 2 13:08:29 2023 -0700

    various polish

commit 4c904d88ab
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 1 13:35:27 2023 -0700

    rss + sitemap

commit ba9f243728
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 1 00:03:01 2023 -0700

    tag and folder pages

commit 24348b24a9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jun 19 22:50:25 2023 -0700

    fix: parsing wikilinks that have codeblock anchors, scroll to anchor

commit fd5c8d17d3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jun 19 20:37:45 2023 -0700

    basic search implementation

commit c4cf0dcb02
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jun 18 10:47:07 2023 -0700

    local and global graph

commit 8bfee04c8c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jun 17 16:05:46 2023 -0700

    popovers

commit cb89cce183
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jun 17 14:36:06 2023 -0700

    basic left,right layout

commit b587782450
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jun 17 13:08:06 2023 -0700

    collapsible callout

commit 6d5491fdcb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jun 17 12:07:40 2023 -0700

    collapsible toc

commit 917d5791ac
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jun 16 19:41:59 2023 -0700

    modern toc tweaks

commit 9d2024b11c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jun 12 22:41:42 2023 -0700

    taglist, mermaid

commit 2bfe90b7e6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jun 11 23:46:38 2023 -0700

    add config to components

commit 352075ae81
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jun 11 23:26:43 2023 -0700

    refactor plugins to be functions instead of classes

commit b8c011410d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jun 9 23:06:02 2023 -0700

    toc

commit 3a29f4c86e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jun 9 19:58:58 2023 -0700

    add custom spa solution

commit 59109a8c1d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 7 22:38:45 2023 -0700

    add flamethrower router

commit 317cce9314
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 7 22:27:32 2023 -0700

    generic quartz component for layout

commit dde36fa558
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 7 10:52:53 2023 -0700

    update gh actions

commit 1cb4dadf13
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 6 21:19:00 2023 -0700

    codeblock copy

commit 0813f127a3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 6 20:58:26 2023 -0700

    fix darkmode script load

commit 4d3579ca98
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 6 19:48:37 2023 -0700

    darkmode scripts

commit 89e0311a98
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 6 00:00:38 2023 -0700

    embeds

commit 700036e84c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jun 5 22:14:17 2023 -0700

    callouts

commit 1406ee0f05
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jun 4 13:37:43 2023 -0400

    update spinners

commit 9ad89997a5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jun 4 12:35:45 2023 -0400

    multi-core builds

commit 4bdc17d4a1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jun 3 15:07:19 2023 -0400

    inline scripts

commit fcd81353f8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jun 1 19:48:38 2023 -0400

    heading linking

commit 04eeb2d10c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jun 1 19:05:14 2023 -0400

    syntax higlighting

commit 42d3a7de17
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jun 1 17:35:31 2023 -0400

    scss support

commit c1c46ad67e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jun 1 12:33:20 2023 -0400

    obsidian flavored markdown support

commit 3636c052eb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed May 31 17:41:44 2023 -0400

    link processing

commit 21c007e2fc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed May 31 17:01:23 2023 -0400

    rendering, link resolution, asset copying

commit ad6ce0d73f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 30 08:02:20 2023 -0700

    plugin integration round 2

commit a757521313
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun May 28 17:44:08 2023 -0700

    base setup

commit 7b1da7a845
Author: BSD-Yassin <103321053+BSD-Yassin@users.noreply.github.com>
Date:   Thu Apr 27 20:12:56 2023 +0200

    i18n: Update fr.toml (#313)

commit e482fa1097
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Apr 6 15:06:01 2023 -0700

    fix: graph and tooltip sometimes not showing

commit ba7a968881
Author: Mattia Ippoliti <Ippolitimattia@gmail.com>
Date:   Sat Apr 1 22:50:08 2023 +0200

    fix: padding for empty title callouts (#308)

commit db27557aa3
Author: Md Jawad Noor Asif <jawad.asif.bd@gmail.com>
Date:   Thu Mar 30 17:14:06 2023 +0600

    fix: search highlight not showing because for trailing slash (#306)

commit b7c305e002
Author: Mike Walton <walton.myke@gmail.com>
Date:   Wed Mar 22 22:56:20 2023 -0700

    adding myself to the showcase (#301)

commit 74fe4d6813
Author: Daniel Lazaro <daniel@dlazaro.ca>
Date:   Sat Mar 18 12:20:56 2023 -0400

    docs: Update link to callouts documentation (#300)

commit d6c31595b3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Mar 16 10:33:01 2023 -0700

    deps: bump hugo-obsidian

commit aa5ab03d4a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Mar 2 09:14:29 2023 -0800

    docs: update to account for github changes

commit ecba6071b8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Feb 25 13:04:15 2023 -0800

    deps: bump hugo-obsidian

commit 983efab94c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Feb 12 16:46:11 2023 -0800

    fix: recent notes partial sorting

commit 10e41743e5
Author: Dev Uni <wlwhsvkdlxh@gmail.com>
Date:   Wed Feb 8 01:38:20 2023 +0900

    fix: Bad UI due to head.html (#284)

commit bde44fadf2
Author: Simon Späti <simu@sspaeti.com>
Date:   Tue Feb 7 09:16:15 2023 +0100

    feat: Adding Twitter and Social image preview including description (#207)

commit 6885651f7b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Feb 6 12:58:34 2023 -0800

    feat: max-width for large screens

commit 7df2bb6f5e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Feb 5 12:01:49 2023 -0800

    fix: fix duplicate link click tracking

commit 11959de11c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Feb 5 11:34:39 2023 -0800

    feat: add more plausible events

commit a73aca8ed9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Feb 5 10:39:58 2023 -0800

    feat: switch from GA to Plausible for analytics

commit 93610e232b
Author: Adam Brangenberg <adambrangenberg@proton.me>
Date:   Wed Feb 1 21:34:18 2023 +0100

    feat: Remove leading slash of folders in graph view (#282)

commit 712dab5c8c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jan 31 11:00:28 2023 -0800

    docs: remove broken links from showcase

commit 77b3907b23
Author: Olivér Falvai <ofalvai@gmail.com>
Date:   Tue Jan 31 19:48:20 2023 +0100

    docs: Clarify Obsidian settings (#280)

commit 8fc63586c4
Author: herrwinfried <ozgurarslln@icloud.com>
Date:   Sun Jan 29 23:14:11 2023 +0300

    feat: Added Turkish translation (#275)

commit 24c9777a52
Author: Apoorv Khandelwal <mail@apoorvkh.com>
Date:   Sat Jan 21 10:01:05 2023 -0800

    feat: Embedding multimodal assets (#274)

commit 7a8811a184
Author: Quadrubo <71718414+Quadrubo@users.noreply.github.com>
Date:   Wed Jan 18 17:25:01 2023 +0100

    added the liveReloadPort as an option for docker (#272)

commit eb2f6aeca8
Author: chaosarium <38693485+chaosarium@users.noreply.github.com>
Date:   Mon Jan 9 17:14:11 2023 -0500

    Fix callout behaviour inconsistent with Obsidian (closes #168) (#268)

commit b78008532f
Author: Md Jawad Noor Asif <jawad.asif.bd@gmail.com>
Date:   Tue Jan 10 04:12:52 2023 +0600

    feat: Added Bangla translations (#266)

commit c5b103c85f
Author: Md Jawad Noor Asif <jawad.asif.bd@gmail.com>
Date:   Wed Jan 4 09:10:25 2023 +0600

    fix: fix unicode broken tags (#261)

commit 614a6222a1
Author: Adam Brangenberg <adambrangenberg@proton.me>
Date:   Thu Dec 29 16:43:41 2022 +0100

    refactor: General performance/style improvements (#262)

commit dc43737896
Author: chaosarium <38693485+chaosarium@users.noreply.github.com>
Date:   Sat Dec 24 12:10:59 2022 -0500

    fix edge cases link processing (#258)

    Fixes https://github.com/jackyzha0/quartz/issues/176

commit ea37486309
Author: toof <toof@toof.jp>
Date:   Sun Dec 25 00:38:49 2022 +0900

    fix: fix misspelling (#259)

commit c1b0eafce6
Author: chaosarium <38693485+chaosarium@users.noreply.github.com>
Date:   Thu Dec 22 13:34:21 2022 -0500

    feat: Added simplified Chinese translations (#257)

commit ce5df837f5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Dec 3 21:03:12 2022 -0800

    feat: latex in search results

commit 4cd6f7efdf
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Nov 30 18:00:12 2022 -0800

    fix: text highlighting

commit 5a7936e23a
Author: Apoorv Khandelwal <mail@apoorvkh.com>
Date:   Wed Nov 30 17:41:05 2022 -0800

    fix: Replacing "internal-link broken" with link to asset (#232)

commit 5fd707714f
Author: Jon Erling Hustadnes <jonerling.hustadnes@gmail.com>
Date:   Sun Nov 27 19:55:43 2022 +0100

    feat: Added Norwegian localization (#242)

commit 717a13a580
Author: Filippo Andrea Sighinolfi <83777862+Sighi-04@users.noreply.github.com>
Date:   Sun Nov 27 19:55:13 2022 +0100

    feat: Added italian localization in i18n/it.toml (#239)

commit 5f3d430699
Author: Brendan Ang <53790951+bbawj@users.noreply.github.com>
Date:   Mon Nov 28 02:53:52 2022 +0800

    feat: add support for mermaid diagrams (#244)

commit 66f3e249fe
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Nov 23 08:34:19 2022 -0800

    fix: only run docker publish on main repository

commit e374e3abd4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Nov 21 23:36:27 2022 -0800

    fix: jump to search for operand

commit f08a76a738
Author: SafEight <43656822+SafEight@users.noreply.github.com>
Date:   Mon Nov 21 21:05:46 2022 +0000

    fix: External links ending in .md don't get trimmed (#236)

    Co-authored-by: SAF <saf@saf.saf>
    fixes https://github.com/jackyzha0/quartz/issues/229

commit d80f6946c8
Author: Morgan Gallant <morgan@morgangallant.com>
Date:   Tue Nov 22 01:54:45 2022 +0900

    fix: Semantic Search: Use Operand Beta API (#235)

commit 120d104230
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Nov 20 15:14:48 2022 -0800

    update config for search

commit e9aa6ae9e7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Nov 20 15:09:58 2022 -0800

    feat: docker docs, semantic search alpha

commit c12af32a5a
Author: Apoorv Khandelwal <apoorv.khand@gmail.com>
Date:   Sun Nov 20 17:03:53 2022 -0500

    feat: Dockerfile and automated container build (#230)

commit de2b6b9a1b
Author: SafEight <43656822+SafEight@users.noreply.github.com>
Date:   Sat Nov 19 21:17:55 2022 +0000

    feat: Replace == with <mark> (#234)

    Co-authored-by: SAF <saf@saf.saf>

commit 7f9f58860d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 19 11:18:57 2022 -0800

    feat: allow enableToc to override default no TOC on a per-page basis

commit 151b9851d6
Author: jet457 <abhmul@gmail.com>
Date:   Sat Nov 19 13:10:41 2022 -0600

    docs: add Abhijeet's math-wiki to the showcase (#228)

commit d56a58044d
Author: saucecoat <43880196+saucecoat@users.noreply.github.com>
Date:   Sun Oct 30 06:08:44 2022 +0000

    Added German translation (#223)

commit 689201bfbd
Author: Conor <hzk@konor.fr>
Date:   Wed Oct 26 18:12:35 2022 +0200

    feat: Add French translation (#221)

commit 9b72edcd9c
Merge: 8704edcc 0a602eda
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Oct 25 13:14:13 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 8704edcca2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Oct 25 13:14:06 2022 -0700

    deps: bump ubuntu version (closes #218)

commit 0a602eda1b
Author: Evan Cater <evan.ecater@gmail.com>
Date:   Mon Oct 24 12:13:35 2022 -0400

    fix euler's identity (#220)

commit 72571a7588
Author: Javier Zaleta Martínez <94091554+javierzaleta@users.noreply.github.com>
Date:   Tue Oct 18 19:25:55 2022 -0500

    feat: Add Spanish translation (#217)

commit 3409a49f15
Author: Charles Chamberlain <charlesetc@users.noreply.github.com>
Date:   Sun Oct 16 12:43:43 2022 -0400

    fix: Apply monospace style to all meta in a popover (#216)

commit 666ffebe90
Author: Pavol Komlos <62595149+plundration@users.noreply.github.com>
Date:   Wed Oct 12 17:21:28 2022 +0200

    Decode the heading id from split link (#214)

commit 8ea1525df4
Author: Seth <37915796+iSaluki@users.noreply.github.com>
Date:   Mon Oct 3 19:45:54 2022 +0100

    Add SethMB Work (#203)

commit dd11d56dd9
Merge: cd7e2088 169ef442
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 23 10:17:34 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit cd7e2088d5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Sep 23 10:17:28 2022 -0700

    feat: hide TOC when no headers (closes #204)

commit 169ef442b9
Author: Simon Späti <simon@airbyte.io>
Date:   Wed Sep 14 19:05:51 2022 +0200

    Adding reference projects (#196)

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

commit 8e3042df49
Author: DongDong Chen <cdd2zju@gmail.com>
Date:   Thu Sep 15 01:05:20 2022 +0800

    add my showcase :  oldwinterの数字花园 (#192)

commit 2145e92b00
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Sep 12 11:08:07 2022 -0700

    fix: make latex rendering size more simialr to obsidian

commit e6c7a4e1e2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Sep 11 18:03:55 2022 -0700

    fix: latex rendering bugs + patch for #195

commit ca84da5b31
Author: Nikola Georgiev <42315052+nikolageorgiev2000@users.noreply.github.com>
Date:   Mon Sep 12 01:05:14 2022 +0100

    feat: Hide full path to file in Wikilinks by default (#195)

commit 0d1670adba
Merge: 5c770f96 ce55eca7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 29 14:23:19 2022 -0400

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 5c770f965a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 29 14:23:04 2022 -0400

    Update Quartz version in documentation

commit ce55eca73b
Author: Andrii Yefremov <56955307+decatetsu@users.noreply.github.com>
Date:   Mon Aug 29 21:15:18 2022 +0300

    Add Ukrainian translation (#191)

commit 591c4813ec
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Aug 28 01:09:52 2022 -0400

    deps: bump hugo-obsidian version

commit 83e7aec3c9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 24 00:45:08 2022 -0400

    fix: tag list styling

commit 25ba1159ad
Author: Youssif Shaaban Alsager <ysh-alsager@hotmail.com>
Date:   Wed Aug 24 05:32:40 2022 +0200

    feat: Add internationalization (i18n) support (#182)

commit e38eaa94d6
Author: Vincent Huang <vincenthuang75025@yahoo.com>
Date:   Sat Aug 20 20:31:06 2022 -0500

    Popover preview should show relevant heading (#180)

commit a78926ede5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 11 11:42:16 2022 -0700

    feat: link previews to page-list (closes #173)

commit 5c76d8dad9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 5 11:08:52 2022 -0700

    fix: make callout detection case-insensitive (closes #171)

commit 3dcc1f1106
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 5 11:04:01 2022 -0700

    feat: better graph scaling (closes #170)

commit ff770927fd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 4 14:50:24 2022 -0700

    style: _callouts.scss simplification (#169)

commit 7ffc907907
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Aug 3 23:46:55 2022 -0700

    fix: CJK search (closes #163)

commit 6dd4c64a4c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Aug 1 07:59:49 2022 -0700

    fix: highlights being stripped in non-semantic search mode

commit 8fc6b8e28e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 18:21:17 2022 -0700

    docs: update, re-added debounce

commit b10b23a47b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 18:02:06 2022 -0700

    docs: add documentation for Operand Search, remove debounce

commit 23380d0519
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 16:55:25 2022 -0700

    fix: title not being selected properly, bump hugo-obsidian for uri fix

commit dd047305af
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 12:33:36 2022 -0700

    deps: bump hugo-obsidian to fix bug of writing to non-existent directory during build

commit 54a8fd4a56
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 12:24:53 2022 -0700

    deps: bump hugo-obsidian to properly copy linkmap

commit 5ef9aad501
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 12:16:36 2022 -0700

    feat: add support for semantic search using operand

commit 14b89105dc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 10:54:23 2022 -0700

    refactor: move search utils to util.js

commit 93d039fe7c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 31 10:14:36 2022 -0700

    deps: bump hugo-obsidian version

commit 234c707a93
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 30 18:46:19 2022 -0700

    docs: improve scss structure and admonition styling, update docs

commit 728d8529ec
Author: Emile Bangma <ewjbangma@hotmail.com>
Date:   Sun Jul 31 02:29:26 2022 +0200

    Support Admonition callouts (#166) (closes #88)

commit e142f37e8d
Merge: d747b19e 1f3da4b8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 19 09:03:26 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit d747b19e61
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 19 09:03:19 2022 -0700

    docs: copy edits

commit 1f3da4b829
Author: Pranav M <pranavm7@outlook.com>
Date:   Mon Jul 18 11:45:36 2022 -0400

    feat: edit the clipboard button to change border colour on success (#162)

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

commit e15e39155d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jul 15 14:26:31 2022 -0700

    fix: give precedence to date created over last modified if defined (#101)

commit dff5ae0d4d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 14 13:09:21 2022 -0700

    style: improve header anchor styling

commit b2555ced61
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 14 12:02:35 2022 -0700

    feat: add description section to section/term/taxonomies, fix header margin

commit 7ccff2cf3d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 14 11:49:47 2022 -0700

    fix: styling on page-list for smaller screens

commit e0b6606d50
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 14 10:38:34 2022 -0700

    fix: make section-li scss more generic

commit d7a42a2fd7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jul 14 10:30:07 2022 -0700

    feat: improve styling for lists, fix anchor offset

commit 422b6cc25b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 13 23:51:33 2022 -0700

    feat: css typography improvements

commit 22c8981bb9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 13 23:37:54 2022 -0700

    feat: css refactor for easy font change

commit 8b2a82a96a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 13 22:27:13 2022 -0700

    fix: change / to use base url

commit 81af8c459b
Author: y1450 <107429941+y1450@users.noreply.github.com>
Date:   Thu Jul 14 00:02:11 2022 +0200

    fix: remove console log (#159)

commit ffe22689eb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 13 15:01:50 2022 -0700

    feat: use floating-ui for better popover positioning

commit c1b8fe1221
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jul 13 14:32:32 2022 -0700

    feat: restyle search icon

commit b7a619bbd7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 12 14:37:10 2022 -0700

    fix: tabsize not being respected

commit 74993d19b7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jul 5 15:42:57 2022 -0700

    docs + fix: broken partial and description of enableGitHubEdit

commit 25a4d3b6e1
Author: rphla <101242699+rphla@users.noreply.github.com>
Date:   Wed Jul 6 06:39:29 2022 +0800

    Add GitHub "edit" button (#157)

commit aaf31f419e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jul 3 11:50:13 2022 -0700

    fix: copy code block logic for non code pages

commit f54df35767
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Sun Jul 3 20:42:35 2022 +0200

    Copy to clipboard feature for code block (#152)

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

commit 015ed4cfa2
Author: Aiden Bai 白宇彤 <aiden.bai05@gmail.com>
Date:   Sat Jul 2 19:40:18 2022 -0700

    Fix `width: auto` for SPA routing (#156)

commit a8137edf24
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Jul 2 17:14:17 2022 -0700

    fix: adjust weird colours for err highlighting

commit eda370334a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jul 1 11:27:50 2022 -0700

    fix: image scaling for md-style links (closes #155)

commit d3e20b8b94
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Fri Jul 1 20:03:52 2022 +0200

    Added optional rendering of code block titles (#148)

commit 8d7a7b712f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jul 1 11:02:42 2022 -0700

    fix: non-SPA fn defs (closes #154)

commit 0896814959
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 17:35:29 2022 -0700

    docs: remove test image from hosting

commit 8b2fba895a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 17:34:05 2022 -0700

    feat: image scaling (closes #131)

commit e884f4927f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 17:17:53 2022 -0700

    fix: anchor formatting (closes #141)

commit 2b0482ae4c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 17:03:41 2022 -0700

    docs: fix page weight

commit 8a100edeb8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 16:57:36 2022 -0700

    docs: polish and update

commit 200c605142
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 16:16:06 2022 -0700

    feat: enable raw html by default (fixes #143)

commit f2078ee621
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jun 29 16:12:33 2022 -0700

    fix: prefix images with base url for non-root quartz

commit 916c51c19c
Merge: 72941965 67a7ba37
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 28 23:21:25 2022 -0700

    Merge pull request #150 from aidenybai/bump-million

commit 67a7ba37e8
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue Jun 28 21:43:28 2022 -0700

    Bump million to 1.11.3

commit 72941965ab
Merge: 34b03537 b732293f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jun 27 16:27:57 2022 -0700

    Merge pull request #146 from geoffreygarrett/hugo

commit b732293f65
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Tue Jun 28 01:21:22 2022 +0200

    fix(head.html): Adds robustness to `config.yaml` favicon definitions

    Initially assumed that `href` definitions should have `/...` as their
    pattern, and `baseURL` would always end with `/`, however the omission
    of `/` as the prefix of the former and suffix of the latter
    simultaneously, would result in broken favicon paths. Final comment:
    `..///...` is not breaking, which is worst case scenario with this fix.

commit 7070a1992a
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Tue Jun 28 01:15:33 2022 +0200

    docs(config.md): Fixed multi-favicon examples and general favicon explanation throughout

commit 997937af5a
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Tue Jun 28 00:45:48 2022 +0200

    docs(config.md): Added short explainer on favicons

commit a334b45b17
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Mon Jun 27 22:05:35 2022 +0200

    docs(content/notes/config.md): Adds documentation for the new favicon support

commit 473ea2c66f
Author: Geoffrey Garrett <g.h.garrett13@gmail.com>
Date:   Mon Jun 27 22:04:32 2022 +0200

    feat(layouts/partials/head.html): Adds general favicon support with dict and string input format

commit 34b0353797
Merge: dbd4fb75 52a185f7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jun 7 08:43:52 2022 -0700

    Merge pull request #140 from DhammaCharts/hugo

commit 52a185f73b
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Mon Jun 6 16:49:01 2022 +0100

    change enableGlobalGraph to false

commit 69c74ca6b5
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Mon Jun 6 16:48:16 2022 +0100

    minor adjustment

commit ab809249c8
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Mon Jun 6 16:42:53 2022 +0100

    Update layouts/partials/head.html

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

commit 84c75d0546
Merge: a275123b dbd4fb75
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Mon Jun 6 12:56:47 2022 +0100

    Merge branch 'hugo' into hugo

commit dbd4fb7595
Merge: 84c6e1ef a1293f82
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Jun 3 10:59:18 2022 -0700

    Merge pull request #139 from aidenybai/prerender-latex

commit a275123be2
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Thu Jun 2 08:35:28 2022 +0100

    better font behaviour

commit c88f31c364
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Thu Jun 2 08:16:02 2022 +0100

    change to object destructuring for drawGraph() arguments

commit d261655d96
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Thu Jun 2 07:49:09 2022 +0100

    remove unnecessary ternary

commit c0800a8749
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Thu Jun 2 07:45:44 2022 +0100

    change baseURL back to original

commit ac0dd50c04
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Wed Jun 1 21:30:40 2022 +0100

    uncomment window.Million

commit e809896338
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Wed Jun 1 21:22:31 2022 +0100

    increase scale

commit 19606ba63d
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Wed Jun 1 21:19:03 2022 +0100

    add www.

commit 1e237ef677
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Wed Jun 1 20:15:44 2022 +0100

    change baseURL

commit 5a1fbc9374
Author: DhammaCharts <100090806+DhammaCharts@users.noreply.github.com>
Date:   Wed Jun 1 13:49:27 2022 +0100

    Improve graph display, options and ability to have a global graph on the home page, local graphs on subpage.

commit a1293f820a
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Sun May 29 20:40:44 2022 -0700

    Prerender latex

commit 84c6e1efed
Merge: 775a1b24 8673a7bc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat May 28 23:27:54 2022 -0700

    Merge pull request #138 from aidenybai/add-footer-config

commit 8673a7bc3d
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Sat May 28 22:52:18 2022 -0700

    Add option to toggle footer

commit 775a1b2490
Merge: cbc2bea4 006b74ec
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri May 27 19:21:05 2022 -0700

    Merge pull request #137 from aidenybai/fix-non-spa-routing

commit 006b74ec6f
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 18:45:42 2022 -0700

    Fix formatting

commit 8aba612a00
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 18:42:01 2022 -0700

    Fix non-spa fallback

commit cbc2bea413
Merge: ba586adc ae240ff8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri May 27 18:32:49 2022 -0700

    Merge pull request #136 from aidenybai/custom-progress-bar-color

commit ae240ff82c
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 18:31:36 2022 -0700

    Remove redundant CSS rule

commit ba586adc76
Merge: 232bd2f0 159deabf
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri May 27 17:14:55 2022 -0700

    Merge pull request #135 from aidenybai/bump-million

commit 159deabfe1
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 16:14:17 2022 -0700

    Bump to 1.9.6

commit 44984cdaf4
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 13:27:13 2022 -0700

    Add support for progress bar

commit 683cb53cbd
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 13:19:19 2022 -0700

    Bump million to 1.9.5

commit 232bd2f016
Merge: 0293c122 e0fd9570
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri May 27 11:01:20 2022 -0700

    Merge pull request #134 from aidenybai/add-prefetching-within-graph

commit e0fd9570d7
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 09:49:28 2022 -0700

    Bump million to 1.9.4

commit bc32bbeaed
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 09:02:01 2022 -0700

    Bump milliomn to 1.9.3

commit efb6c7845f
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 08:40:00 2022 -0700

    Add prefetch to graph

commit bd316d8249
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Fri May 27 08:39:44 2022 -0700

    Bump million to 1.9.2

commit 0293c12217
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 23 22:25:13 2022 -0700

    feat: recent posts section/partial

commit 0439c163a0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri May 20 16:50:56 2022 -0400

    fix: js not executing if spa disabled

commit 0b6711c218
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat May 14 16:47:50 2022 -0400

    fix: tag boxes overlapping for content with many tags (closes #130)

commit ed9a8efd1f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu May 5 21:11:23 2022 -0400

    fix inline link highlighting, safer latex render

commit e302f6c423
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu May 5 20:35:32 2022 -0400

    fix: more generic style to match bad nesting generated by popover interp

commit b21b27d1d3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu May 5 20:30:55 2022 -0400

    fix: clean wikilinks and render latex in popover

commit 364aee36fc
Merge: cea0f3eb 8b855b52
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu May 5 01:03:09 2022 -0400

    fix: merge conf

commit cea0f3eb74
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu May 5 00:58:50 2022 -0400

    feat: contextual backlinks (closes #106)

commit 8b855b522a
Merge: b67a389b 7b3696b8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed May 4 11:40:38 2022 -0400

    Merge pull request #125 from aidenybai/fix-latex

commit 7b3696b877
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Wed May 4 08:39:25 2022 -0700

    Remove pnpm debug log

commit b4ff12ca0b
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Wed May 4 08:10:59 2022 -0700

    Fix latex

commit b67a389bea
Merge: a0997444 2b5c03c9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 13:59:02 2022 -0400

    Merge pull request #124 from aidenybai/hugo

commit 2b5c03c972
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:55:45 2022 -0700

    Remove redundant URL construction

commit aaed5dc1f1
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:54:39 2022 -0700

    Support /path root sites

commit 1a5d158fce
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:38:41 2022 -0700

    Support active node with other data at end of url

commit a09974446d
Merge: 03742621 9fc71603
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 13:21:32 2022 -0400

    Merge pull request #123 from aidenybai/fix-popover

commit 9fc71603ba
Merge: d38f9bec 3789df80
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:18:41 2022 -0700

    Merge

commit d38f9bec70
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:16:09 2022 -0700

    Rename API and generalize router API

commit 771ebd8031
Merge: e4cc625c 03742621
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 10:07:38 2022 -0700

    Merge

commit e4cc625c33
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:34:27 2022 -0700

    Add future note about init function

commit 3789df80e4
Merge: 32c79a56 03742621
Author: Aiden Bai 白宇彤 <aiden.bai05@gmail.com>
Date:   Tue May 3 09:33:00 2022 -0700

    Merge branch 'hugo' into fix-popover

commit 037426217c
Merge: 6e6dd4cb e646cdb0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 12:29:26 2022 -0400

    Merge pull request #122 from aidenybai/fix-active-graph-node

commit e646cdb0be
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:27:25 2022 -0700

    Use explicit regex for trailing slash trim

commit 8d092a3a4a
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:22:51 2022 -0700

    Remove unnecessary 'url' argument in graph.html

commit 32c79a561f
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:21:44 2022 -0700

    Remove unnecessary 'url' argument in graph.html

commit 3c660dd9b5
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:20:01 2022 -0700

    Remove unnecessary 'url' param in drawGraph

commit 4cca3c1f2d
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 09:04:15 2022 -0700

    Peg router version

commit 9d3bbd6076
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 08:53:18 2022 -0700

    Fix active node on graph

commit 9c71f07355
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 08:48:35 2022 -0700

    Enable config for testing

commit 77485b754d
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Tue May 3 08:47:42 2022 -0700

    Fix popover

commit 6e6dd4cb0b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 10:57:20 2022 -0400

    fix: trim trailing slash when calculating popover

commit 81fe2d2493
Merge: 24d08d58 321e19dc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 10:44:56 2022 -0400

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 24d08d580d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 10:43:22 2022 -0400

    cfg: make SPA optional

commit 321e19dc41
Merge: 12d33619 97607c3c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 09:48:50 2022 -0400

    Merge pull request #121 from benbohmer/patch-1

commit 12d33619a2
Merge: fc89ff26 4197ad46
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue May 3 09:47:48 2022 -0400

    Merge pull request #120 from straightupjac/fix/github-info

commit 97607c3ca5
Author: benbohmer <103453816+benbohmer@users.noreply.github.com>
Date:   Tue May 3 09:10:45 2022 +0200

    fix: keep / at end of URL to avoid redirects

    Removed strings.TrimRight "/" in line 10 to keep the trailing slash at the end of URLs in regular links. This avoids having every single internal link being a 301 redirect.

commit 4197ad460a
Author: straightupjac <jdc.jaclyn@gmail.com>
Date:   Tue May 3 01:51:15 2022 -0400

    fix github info

commit fc89ff2680
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 2 13:00:41 2022 -0400

    fix: broken semi and graph min-height

commit e9a33c04b5
Merge: 9ba0a4b3 b0e15e0c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 2 12:56:44 2022 -0400

    fmt: remove semis for good

commit b0e15e0cbc
Merge: 66304da0 f1b85fb6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 2 12:19:26 2022 -0400

    Merge pull request #118 from aidenybai/add-router

commit 9ba0a4b34f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 2 12:14:51 2022 -0400

    fmt: remove semis :)

commit f1b85fb6d9
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Mon May 2 09:10:40 2022 -0700

    Fix clarification comment

commit 66304da027
Merge: 416dc0b8 87144fca
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon May 2 12:06:57 2022 -0400

    Merge pull request #119 from aidenybai/add-prettier

    Add prettier config

commit 40d216759c
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Mon May 2 09:05:02 2022 -0700

    Expand template

commit 5c602ab16f
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Mon May 2 09:04:36 2022 -0700

    Add clarification comments

commit 87144fca21
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Mon May 2 08:57:25 2022 -0700

    Use semi: false for prettier config

commit a9523dd39b
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Sun May 1 22:08:14 2022 -0700

    Add prettier config

commit bcb166c21c
Author: Aiden Bai <aiden.bai05@gmail.com>
Date:   Sun May 1 22:06:33 2022 -0700

    Add router

commit 416dc0b85c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 30 13:13:30 2022 -0700

    fix: add update for local hugo-obsidian on make update

commit b8a660e208
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 30 13:10:12 2022 -0700

    feat: copyable header anchors (fixes #86)

commit ec86cca97b
Merge: 87b5a7a2 f7027e7e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Apr 28 15:53:57 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 87b5a7a251
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Apr 28 15:49:16 2022 -0700

    feat: show graph titles on zoom (fixes #92)

commit c8d390dbc5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Apr 28 13:45:29 2022 -0700

    fix: always hide popover on mobile (fixes #104)

commit 3c7ece5405
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Apr 28 10:48:31 2022 -0700

    fix: append trailing slash, fixes #111

commit f7027e7ecd
Merge: f05ff5e6 0cfd93c5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Apr 20 09:20:21 2022 -0700

    Merge pull request #108 from exu3/patch-1

commit 0cfd93c57c
Author: Ella <git@ella.cx>
Date:   Sun Apr 17 02:11:17 2022 -0700

    Fix another typo

commit 3f8c473678
Author: Ella <git@ella.cx>
Date:   Sun Apr 17 01:33:16 2022 -0700

    Fix typo: recomment -> recommend

commit f05ff5e62d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 23:19:33 2022 -0700

    fix: add dropshadow to popover, cleanup animation

commit 12ed9722d8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 22:43:11 2022 -0700

    fix: popover selection wrongly including line breaks

commit 887d4d4f5e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 21:40:59 2022 -0700

    deps: bump hugo -> v0.96.0

commit f9c7cdf928
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 20:44:39 2022 -0700

    fix: check for src before attempting to add popover

commit 2d55b6ac2e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 18:07:40 2022 -0700

    fix: missing whitespace chomp in link render hook

commit d5884aedb7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 14:14:19 2022 -0700

    fix: wikilink patch not applying to transformed text like apostrophes

commit 66eaa444a4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 14:08:36 2022 -0700

    fix: wikilink image relURL for images with spaces

commit 0ddc48a452
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 13:47:24 2022 -0700

    fix: wikilink-like text in code fences #95, #97

commit cd19159c53
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 12:47:28 2022 -0700

    feat: wikilink img support

commit 7808c66c4d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 09:41:13 2022 -0700

    fix: align footer links

commit a7abc6ab96
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 00:09:56 2022 -0700

    docs: make update command and clarify update steps/potential danger

commit 9509a64354
Merge: 53242b1e 3ce6944c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 00:02:48 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 53242b1e57
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Apr 5 00:02:37 2022 -0700

    add update target to Makefile

commit 3ce6944c18
Merge: e2455050 3583265f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 23:56:28 2022 -0700

    Merge pull request #93 from meleu/patch-3

commit 3cec4fd950
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 23:30:28 2022 -0700

    update screenshot

commit e245505082
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 23:25:24 2022 -0700

    feat: hide toc for short notes

commit fc4b9ded76
Merge: 3781b677 27c4761f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 23:20:43 2022 -0700

    Merge pull request #94 from meleu/patch-4

commit 27c4761fe0
Author: meleu <meleu@users.noreply.github.com>
Date:   Mon Apr 4 20:15:40 2022 -0300

    link to home goes to baseURL

commit 3583265f80
Author: meleu <meleu@users.noreply.github.com>
Date:   Mon Apr 4 17:30:23 2022 -0300

    docs: warn about possible lost of customization

commit 3781b67707
Merge: 1613511f 671fe053
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 13:08:42 2022 -0700

    Merge pull request #91 from meleu/patch-2

commit 671fe05312
Author: meleu <meleu@users.noreply.github.com>
Date:   Mon Apr 4 17:07:43 2022 -0300

    padding and border-radius matching bottom cards

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

commit 1613511f39
Merge: acab4887 575288ec
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 09:45:05 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit acab488784
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Apr 4 09:44:58 2022 -0700

    re-add obsidian file

commit ff91dcd196
Merge: a287d112 575288ec
Author: meleu <meleu@users.noreply.github.com>
Date:   Sun Apr 3 22:14:12 2022 -0300

    Merge branch 'jackyzha0:hugo' into patch-2

commit a287d11246
Author: meleu <meleu.dev@gmail.com>
Date:   Sun Apr 3 22:12:55 2022 -0300

    add a collapsible ToC

commit 575288ece9
Merge: 25b5ac43 1d9c0e4a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Apr 3 17:57:46 2022 -0700

    Merge pull request #88 from meleu/patch-2

commit 25b5ac43dd
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Apr 3 17:43:37 2022 -0700

    fix: favicon not showing on non-root domain #89

commit 1d9c0e4a44
Author: meleu <meleu.dev@gmail.com>
Date:   Sun Apr 3 16:31:29 2022 -0300

    use "enableToc: false"

commit e62d512d95
Author: meleu <meleu.dev@gmail.com>
Date:   Sun Apr 3 16:29:10 2022 -0300

    disable ToC if frontmatter has "enableToc: false"

commit 8f15c5f8c1
Author: meleu <meleu@users.noreply.github.com>
Date:   Sun Apr 3 16:22:32 2022 -0300

    disable ToC if enableToc: false

commit efeaf9b49c
Merge: 91c4e3fb 22f11711
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Apr 3 11:44:39 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 91c4e3fb3a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Apr 3 11:42:42 2022 -0700

    fix: multiline code block #87

commit 22f11711b2
Merge: 16b177ce 5c3ef884
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Apr 3 08:17:13 2022 -0700

    Merge pull request #85 from meleu/patch-1

    Ah my git was being really weird with cases :')) thank you

commit 5c3ef884c7
Author: meleu <meleu@users.noreply.github.com>
Date:   Sun Apr 3 11:19:21 2022 -0300

    duplicated file

commit 16b177ce66
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 21:04:20 2022 -0700

    README update

commit 14c6181d24
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 20:37:42 2022 -0700

    bump hugo version v0.82 -> v0.92.2

commit e6e04c03c4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 20:34:55 2022 -0700

    fix latex misrendering

commit 146e975932
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 20:21:16 2022 -0700

    bump hugo obsidian, fix backlinks for subpathed quartz, update homepage

commit c117e38899
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 20:06:31 2022 -0700

    feat: wikilinks implementation

commit 4fd983277e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 17:38:39 2022 -0700

    fix: cjk support + demo page

commit cc86136bcb
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 17:00:14 2022 -0700

    feat: basic latex support

commit 8e083d4a93
Merge: c51573ef 03b574b1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 14:53:05 2022 -0700

    Merge pull request #83 from meleu/patch-2

commit 03b574b160
Author: meleu <meleu.dev@gmail.com>
Date:   Sat Apr 2 18:51:45 2022 -0300

    cleanup

commit a469653f75
Author: meleu <meleu.dev@gmail.com>
Date:   Sat Apr 2 18:50:58 2022 -0300

    separate contact links semantically

commit c51573efa9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 13:34:26 2022 -0700

    feat: grey out broken links

commit 902d0f2a0f
Merge: 1ddd15af 9c5ecccf
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 12:59:47 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 1ddd15afc6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Apr 2 12:59:38 2022 -0700

    fix: non-unicode character in popover and search #67, #68

commit 16f8cd7100
Author: meleu <meleu@users.noreply.github.com>
Date:   Sat Apr 2 13:37:12 2022 -0300

    separate links with &ZeroWidthSpace;

commit 9c5ecccf25
Merge: 3674df48 e3cd531c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Apr 1 14:17:35 2022 -0700

    Merge pull request #82 from meleu/patch-1

commit e3cd531c53
Author: meleu <meleu@users.noreply.github.com>
Date:   Fri Apr 1 18:13:49 2022 -0300

    fix custom.scss path

commit 3674df48b8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Apr 1 10:13:01 2022 -0700

    fix pagination styling

commit 9e8c5587e4
Merge: 6605b13b 6edc9798
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Mar 31 23:16:00 2022 -0700

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 6605b13b86
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Mar 31 23:15:54 2022 -0700

    more troubleshooting, backlinks reference private page fix

commit 6edc979896
Merge: 54a68e6e fc439224
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Mar 21 09:15:35 2022 -0700

    Merge pull request #71 from siyangsun/patch-1

commit fc43922445
Author: Siyang <siyangsun2007@gmail.com>
Date:   Sun Mar 20 22:37:05 2022 -0700

    add to showcase and fix link to file

commit 54a68e6e5c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Mar 18 10:53:39 2022 -0700

    patch image

commit a6ab2f92ef
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Mar 16 17:54:24 2022 -0700

    add update

commit fda481fbb9
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Mar 15 01:12:08 2022 -0700

    fix: bump hugo-obsidian version to account for contentIndex paths on windows

commit 94e987dab5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Mar 15 00:37:56 2022 -0700

    feat: better titles for empty pages #61

commit e981c76ed4
Merge: 651bfc5c f70128a3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Mar 9 10:11:36 2022 -0800

    Merge pull request #65 from claudio4/fix-text

commit f70128a3de
Author: Claudio Yanes <me@claudio4.com>
Date:   Wed Mar 9 17:58:01 2022 +0000

    Prevent overflow of long links and words

    When a word (or any string withtout breakpoints (spaces, dashes....), making links the most common place where this becamoes an issue)  is wider than its container, the text will simply overflow any container, including the viewport. This commit fixes this behaviour by making the word-drap strategy of the browser more aggresive.

commit 651bfc5cd2
Merge: 90727099 60794201
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Mar 7 10:45:07 2022 -0800

    Merge pull request #62 from claudio4/hugo

commit 6079420178
Merge: 978d5ca1 b96c60ed
Author: Claudio Yanes <me@claudio4.com>
Date:   Mon Mar 7 18:28:14 2022 +0000

    Merge branch 'jackyzha0-hugo' into hugo

commit b96c60edfc
Merge: 978d5ca1 90727099
Author: Claudio Yanes <me@claudio4.com>
Date:   Mon Mar 7 18:27:45 2022 +0000

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into jackyzha0-hugo

commit 978d5ca1ae
Author: Claudio Yanes <me@claudio4.com>
Date:   Mon Mar 7 18:25:02 2022 +0000

    Format JS

commit 907270992d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Mar 4 23:55:07 2022 -0800

    fix: hide popover on mobile to prevent overflow

commit 6f9283e95b
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 22:27:21 2022 +0000

    Update makefile and docs

    The artifacts produced by hugo-obsidian are now expected to be placed in
    the assets/indices directory. This commit reflects this change in the
    Makefile and in the docs.

commit 0fad5570d3
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 04:14:42 2022 +0000

    Add .gitkeep to assets/indices

commit dc9b421e21
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 04:12:43 2022 +0000

    Remove unnecessary scrollbars

    The margin property can escape the parent node and move it alongside
    its child. This happens with singlePage div and the body, resulting in
    scrollbars appearing as the body has the size of the viewport but
    does not align with it. This phenomenon can be always observed
    in the vertical axis and it can also be observed in the horizontal axis
    when the viewport it’s not wide enough (mostly in mobile).

    Using paddings prevents this “extra space” from scraping and displacing
    the body.

    Also, the value 100vw does not take into account the space taken by the
    vertical scrollbar, thus making the body wider than the actual viewport,
    producing a horizontal scrollbar.

commit 8779e72c77
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 03:34:45 2022 +0000

    Add attribute property to scripts from jsdelivr

    Adding the integrity attribute protects the website (by refusing to load
    the script) against malicious modifications of the script
    in the case of jsdelivr gets hacked

commit 7f6523337c
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 03:24:32 2022 +0000

    Move popover to the end of the page

    The popover script doesn’t ever start in until the DOM has finished
    Loading, so wait for the script to be downloaded and parsed before
    Showing the content to the user makes no sense.

commit 7e0f2e4449
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 02:25:30 2022 +0000

    Fix fetchData

    The fetchData function suffer from a race condition. If the function is
    called before the promise finishes, it will result in another pair of
    HTTP request. This does not only make the function useless but
    Actually, it makes it harmful as the data might be redownloaded twice.

    Now fetchData is not a function but rather the promise by itself.
    Previous callers are expected to await the variable instead, this
    should be not concern as awaiting a promise multiple time in
    JavaScript is completely safe.

commit 1313bd9779
Author: Claudio Yanes <me@claudio4.com>
Date:   Fri Mar 4 02:07:51 2022 +0000

    Move css and js to appropriate files

    Having the CSS and JS in the html template produces pages larger
    than necessary, as each page need to contain all the js/css.
    Separating them in appropriate files allow the browser to just download
    them once and use them for all the pages. This is even more effective
    with an aggressive cache policy for the js and css, something that can
    be done without fear thanks to the implemented cache-busting.
    Also, having then in separate files allows us to use Hugo pipelines
    for minimizing the code.

commit 5234fae080
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Feb 28 08:24:29 2022 -0800

    fix backlinks not using baseurl

commit 0ee0855e1c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Feb 28 07:30:59 2022 -0800

    bump hugo-obsidian to support root

commit e06e341468
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Feb 28 07:14:55 2022 -0800

    fix: explicitly set root as current directory to fix ignore files

commit 73e526a7d5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Feb 23 12:28:25 2022 -0500

    add screenshot to readme

commit cdc4f1a840
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 22 13:36:08 2022 -0500

    fix: relink search button (move outside content load listener)

commit 714b4fcfa3
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Feb 20 21:40:10 2022 -0500

    fix links being broken for pages with spaces

commit 9c04ca0266
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Feb 17 10:49:41 2022 -0500

    rtl docs

commit 388a2bf78b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Feb 17 10:44:39 2022 -0500

    docs updates

commit f192f9a23d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 23:03:02 2022 -0500

    fix #54: root all image urls

commit 3b3e6ec3b2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 22:54:20 2022 -0500

    fix relative pathing for dynamic fetch

commit 8e85e274f6
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 19:42:45 2022 -0500

    change output to static instead of data

commit fcd5d2807d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 19:39:14 2022 -0500

    feat: dynamically fetch indices

commit 4587b13360
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 17:12:08 2022 -0500

    feat: add rtl support as part of #47

commit fb9ea8dcb8
Merge: c520db48 10f9843b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 16:52:49 2022 -0500

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit c520db4882
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 16:52:32 2022 -0500

    fix: #50, change css load order

commit 10f9843bb6
Merge: 0dc51ff3 31297b7e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 14:51:29 2022 -0500

    Merge pull request #51 from brandonkboswell/patch-1

commit 0dc51ff39c
Merge: c35086c5 fa3bc3de
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 14:50:34 2022 -0500

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit c35086c510
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Feb 15 14:50:25 2022 -0500

    visibility fix

commit 31297b7e5a
Author: Brandon Boswell <brandonkboswell@gmail.com>
Date:   Sat Feb 12 22:35:03 2022 -0500

    Added to the Showcase

commit fa3bc3de92
Merge: a271fb9d 41c443db
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Feb 11 17:24:54 2022 -0500

    Merge pull request #48 from earnestma/earne/configurable-page-toc

commit 41c443dbf0
Author: earnest ma <me@earne.link>
Date:   Fri Feb 11 17:05:38 2022 -0500

    Add disableToc parameter to not show TOC on a page

commit a271fb9d74
Merge: 9645f003 49cdca5d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 31 12:28:40 2022 -0800

    Merge pull request #46 from adube/patch-1

commit 49cdca5dfc
Author: Alexandre Dubé <adube@mapgears.com>
Date:   Mon Jan 31 15:18:26 2022 -0500

    Specify Hugo requires extended Sass/SCSS version

    Hugo needs to be installed with its "extended" Sass/SCSS version, otherwise this template does not work.

commit 9645f00317
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Jan 27 09:38:28 2022 -0800

    link fixing

commit 57ebf4c21c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 10 13:08:50 2022 -0800

    underscore fix, fix relative path being weird for graph

commit 54e3e071d1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 10 09:00:45 2022 -0800

    fix popover regex

commit d46e223831
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 10 08:51:00 2022 -0800

    revert baseurl fix

commit 6f9a29c174
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 10 08:49:29 2022 -0800

    various path fixes

commit 532bc61025
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Jan 5 19:42:13 2022 -0500

    set relativeUrls to true

commit 99aea48260
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Jan 4 11:39:22 2022 -0500

    docs update

commit 4a3766db56
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 3 16:37:24 2022 -0500

    update featurelist

commit 4e639979f8
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 3 15:36:58 2022 -0500

    fix copy selection

commit e49a1ac9db
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 3 13:22:04 2022 -0500

    made link preview optional

commit 4a3c4fdef5
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Jan 3 13:18:31 2022 -0500

    popover implementation

commit 2b432d7f0b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jan 2 20:02:47 2022 -0500

    fix flex gap

commit 7507fd2991
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Jan 2 19:49:41 2022 -0500

    fix search styling

commit ca886e4075
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Dec 28 14:28:08 2021 -0500

    fix render link for apostrophe

commit 3722e600ee
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 20:52:30 2021 -0500

    bump hugo-obsidian

commit efeaf0f4e4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 20:16:21 2021 -0500

    add pagination to section, fix graph linking

commit 1a8cdaad24
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 19:43:01 2021 -0500

    remove console.log

commit e4caa0d1d7
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 19:35:42 2021 -0500

    add taxonomy and term lists

commit a45856d788
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 17:53:33 2021 -0500

    fix last modified not working for capitalized pages

commit dbe9b338cc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 17:44:39 2021 -0500

    fix capitalization

commit 000fcdbf99
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 17:43:27 2021 -0500

    fix casing

commit 612c44d719
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 17:34:53 2021 -0500

    modify obsidian

commit e1911a58ff
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 17:28:53 2021 -0500

    enable last modified info

commit b4e2697116
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 15:59:19 2021 -0500

    content section

commit 094ab9d064
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 13:15:10 2021 -0500

    dedupe backlinks

commit 39592347cc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Dec 27 13:06:58 2021 -0500

    add graph depth config

commit 165d33810d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Dec 26 21:13:21 2021 -0500

    base tags

commit 6fbfa7170b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Dec 26 00:09:15 2021 -0500

    various font and colour fixes

commit 43837f9e2e
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Dec 25 23:45:30 2021 -0500

    add makefile, fix link padding, test capitalization

commit 2ba01c8311
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Dec 24 15:51:37 2021 -0500

    fix untitled #36

commit 114b7ca913
Merge: 5bd5642c 091be704
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Dec 24 09:48:41 2021 -0500

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 5bd5642c99
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Dec 24 09:48:22 2021 -0500

    add toLowerCase to id

commit 48d01810c4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 14:40:59 2021 -0800

    fix config setting, fix font size for h1 in article

commit 3a98c8b554
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 14:32:47 2021 -0800

    actually display site title

commit 69c86e407f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 14:21:39 2021 -0800

    update subdomain docs

commit 56d2382c28
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 14:05:27 2021 -0800

    fix relative link styling, change graph and backlinks to refer to name rather than path

commit 091be7040b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 13:37:29 2021 -0800

    Create CODE_OF_CONDUCT.md

commit 09b5522a48
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 13:22:22 2021 -0800

    Update issue templates

commit b9d7adafcc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 23 13:18:03 2021 -0800

    Create FUNDING.yml

commit afeb18212d
Merge: c64322ad bc909559
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Dec 2 20:06:18 2021 -0800

    Merge pull request #28 from juaoose/overflow

commit bc90955959
Author: Juaoose <jjrg1994@gmail.com>
Date:   Thu Dec 2 22:58:34 2021 -0500

    remove horizontal scrollbar

commit c64322ad3f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 20 22:55:53 2021 -0800

    remove bad wikilink

commit 48eb9ebc5f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Nov 20 22:53:26 2021 -0800

    better search, fix spacing support, bump hugo-obsidian

commit 82ba843e42
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Nov 15 15:54:18 2021 -0800

    search styling

commit 8ca31df3f2
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 31 09:59:38 2021 -0700

    search patch

commit df23b99951
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sat Oct 30 23:27:33 2021 -0700

    more search improvements

commit 6005a2e0a0
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Wed Oct 27 20:10:04 2021 -0700

    css fixes

commit de940d6a4b
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Oct 26 17:06:00 2021 -0700

    update graph redir

commit 806d11f874
Merge: 03bb3a3b 1fc2da4f
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Oct 26 17:03:07 2021 -0700

    Merge pull request #23 from bur3ku/hugo

commit 1fc2da4fe2
Merge: 9292de63 24776624
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Tue Oct 26 16:58:37 2021 -0700

    Merge branch 'hugo' of https://github.com/bur3ku/quartz into hugo

commit 9292de6333
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Tue Oct 26 16:58:08 2021 -0700

    remove unnecessary regex, use encodeuri for label instead of replace

commit 2477662404
Merge: a14d06aa 03bb3a3b
Author: Blake Allen <blake.edward.allen@gmail.com>
Date:   Tue Oct 26 12:46:03 2021 -0700

    Merge branch 'hugo' into hugo

commit a14d06aa3d
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Tue Oct 26 12:44:25 2021 -0700

    fix conflict fix

commit e0535dbe32
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Tue Oct 26 12:43:55 2021 -0700

    fix conflict

commit 8eca1e60f7
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Tue Oct 26 12:36:20 2021 -0700

    change %20 in node labels to whitespace, change %20 in node hrefs to hyphen

commit 03bb3a3bae
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Oct 25 15:06:29 2021 -0700

    normalize search styling

commit f7b89db8ee
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Mon Oct 25 15:00:55 2021 -0700

    search fix

commit 1835b97a7a
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:45:55 2021 -0700

    better homepage

commit f56642f13c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:32:55 2021 -0700

    forgot string lol

commit 22a9c0ddfc
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:31:09 2021 -0700

    docs updates, add search to main page, fix redir bug

commit c1c061fbea
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:17:20 2021 -0700

    bump docs

commit 6fd19069de
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:17:13 2021 -0700

    search improvements

commit 299533a4f4
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Sun Oct 24 23:17:00 2021 -0700

    bump hugo-obsidian version

commit e1366ecb61
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Fri Oct 22 18:56:26 2021 -0700

    fix accidental code

commit 776ef084c9
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Fri Oct 22 18:32:57 2021 -0700

    fix last commit

commit fc00ad5bff
Author: Blake Allen <blakesnake100@gmail.com>
Date:   Fri Oct 22 14:04:09 2021 -0700

    fix for notes with spaces not linking properly

commit 228f96e74d
Merge: ae2f7efd 071984a1
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Tue Aug 31 18:32:14 2021 -0400

    Merge pull request #14 from juaoose/hugo

    fix product typo in external hosting section

commit 071984a12d
Author: Juan José Rodríguez <juaoose@users.noreply.github.com>
Date:   Tue Aug 31 16:40:31 2021 -0500

    fix product typo in external hosting section

commit ae2f7efde0
Author: jackyzha0 <j.zhao2k19@gmail.com>
Date:   Sat Aug 28 20:58:14 2021 -0400

    update showcase

commit cb38667c1d
Merge: 1c851271 27c33f83
Author: jackyzha0 <j.zhao2k19@gmail.com>
Date:   Fri Aug 27 14:08:18 2021 -0400

    Merge branch 'hugo' of https://github.com/jackyzha0/quartz into hugo

commit 27c33f8334
Merge: 8850976d f9920f6d
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Fri Aug 13 17:45:32 2021 -0400

    Merge pull request #9 from brechtcs/template

    Execute darkmode script before first render

commit f9920f6d73
Author: Brecht Savelkoul <brecht.savelkoul@alumni.lse.ac.uk>
Date:   Fri Aug 13 22:46:00 2021 +0200

    Execute darkmode script before first render

commit 8850976d8d
Merge: 9b427faa bb6a1e8c
Author: Jacky Zhao <j.zhao2k19@gmail.com>
Date:   Thu Aug 12 23:49:05 2021 -0400

    Merge pull request #8 from SlRvb/patch-1

    Add SlRvb Site to Showcase

commit bb6a1e8c34
Author: SlRvb <54087190+SlRvb@users.noreply.github.com>
Date:   Thu Aug 12 20:46:23 2021 -0700

    Add SlRvb Site to Showcase
This commit is contained in:
Tomoya Matsuura(MacBookPro) 2023-11-25 03:08:37 +09:00
parent 042ce7f9ca
commit 7d7c6feaae
75 changed files with 2523 additions and 708 deletions

2
.gitignore vendored
View File

@ -7,3 +7,5 @@ tsconfig.tsbuildinfo
content/.obsidian/workspace.json
.quartz-cache
private/
.replit
replit.nix

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM node:20-slim as builder
WORKDIR /usr/src/app
COPY package.json .
COPY package-lock.json* .
RUN npm ci
FROM node:20-slim
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/ /usr/src/app/
COPY . .
CMD ["npx", "quartz", "build", "--serve"]

BIN
docs/images/dns records.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

38
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@jackyzha0/quartz",
"version": "4.0.10",
"version": "4.0.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@jackyzha0/quartz",
"version": "4.0.10",
"version": "4.0.11",
"license": "MIT",
"dependencies": {
"@clack/prompts": "^0.6.3",
@ -45,6 +45,7 @@
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark": "^14.0.2",
"remark-breaks": "^3.0.3",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
@ -55,6 +56,7 @@
"serve-handler": "^6.1.5",
"source-map-support": "^0.5.21",
"to-vfile": "^7.2.4",
"toml": "^3.0.0",
"unified": "^10.1.2",
"unist-util-visit": "^4.1.2",
"vfile": "^5.3.7",
@ -3809,6 +3811,19 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-newline-to-break": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-1.0.0.tgz",
"integrity": "sha512-491LcYv3gbGhhCrLoeALncQmega2xPh+m3gbsIhVsOX4sw85+ShLFPvPyibxc1Swx/6GtzxgVodq+cGa/47ULg==",
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-find-and-replace": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-phrasing": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
@ -4902,6 +4917,20 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-breaks": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-3.0.3.tgz",
"integrity": "sha512-C7VkvcUp1TPUc2eAYzsPdaUh8Xj4FSbQnYA5A9f80diApLZscTDeG7efiWP65W8hV2sEy3JuGVU0i6qr5D8Hug==",
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-newline-to-break": "^1.0.0",
"unified": "^10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-frontmatter": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz",
@ -5548,6 +5577,11 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="
},
"node_modules/tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",

View File

@ -2,7 +2,7 @@
"name": "@jackyzha0/quartz",
"description": "🌱 publish your digital garden and notes as a website",
"private": true,
"version": "4.0.10",
"version": "4.1.1",
"type": "module",
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
"license": "MIT",
@ -19,6 +19,7 @@
"profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1"
},
"engines": {
"npm": ">=9.3.1",
"node": ">=18.14"
},
"keywords": [
@ -69,6 +70,7 @@
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark": "^14.0.2",
"remark-breaks": "^3.0.3",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
@ -79,6 +81,7 @@
"serve-handler": "^6.1.5",
"source-map-support": "^0.5.21",
"to-vfile": "^7.2.4",
"toml": "^3.0.0",
"unified": "^10.1.2",
"unist-util-visit": "^4.1.2",
"vfile": "^5.3.7",

View File

@ -10,7 +10,7 @@ const config: QuartzConfig = {
provider: "plausible",
},
baseUrl: "garden.matsuuratomoya.com",
ignorePatterns: ["private", "templates"],
ignorePatterns: ["private", "templates",".obsidian"],
defaultDateType: "created",
theme: {
typography: {
@ -69,6 +69,7 @@ const config: QuartzConfig = {
}),
Plugin.Assets(),
Plugin.Static(),
Plugin.NotFoundPage(),
],
},
}

View File

@ -1,550 +1,39 @@
#!/usr/bin/env node
import { promises, readFileSync } from "fs"
import yargs from "yargs"
import path from "path"
import { hideBin } from "yargs/helpers"
import esbuild from "esbuild"
import chalk from "chalk"
import { sassPlugin } from "esbuild-sass-plugin"
import fs from "fs"
import { intro, isCancel, outro, select, text } from "@clack/prompts"
import { rimraf } from "rimraf"
import chokidar from "chokidar"
import prettyBytes from "pretty-bytes"
import { execSync, spawnSync } from "child_process"
import http from "http"
import serveHandler from "serve-handler"
import { WebSocketServer } from "ws"
import { randomUUID } from "crypto"
import { Mutex } from "async-mutex"
const ORIGIN_NAME = "origin"
const UPSTREAM_NAME = "upstream"
const QUARTZ_SOURCE_BRANCH = "v4"
const cwd = process.cwd()
const cacheDir = path.join(cwd, ".quartz-cache")
const cacheFile = "./.quartz-cache/transpiled-build.mjs"
const fp = "./quartz/build.ts"
const { version } = JSON.parse(readFileSync("./package.json").toString())
const contentCacheFolder = path.join(cacheDir, "content-cache")
const CommonArgv = {
directory: {
string: true,
alias: ["d"],
default: "content",
describe: "directory to look for content files",
},
verbose: {
boolean: true,
alias: ["v"],
default: false,
describe: "print out extra logging information",
},
}
const SyncArgv = {
...CommonArgv,
commit: {
boolean: true,
default: true,
describe: "create a git commit for your unsaved changes",
},
push: {
boolean: true,
default: true,
describe: "push updates to your Quartz fork",
},
pull: {
boolean: true,
default: true,
describe: "pull updates from your Quartz fork",
},
}
const BuildArgv = {
...CommonArgv,
output: {
string: true,
alias: ["o"],
default: "public",
describe: "output folder for files",
},
serve: {
boolean: true,
default: false,
describe: "run a local server to live-preview your Quartz",
},
baseDir: {
string: true,
default: "",
describe: "base path to serve your local server on",
},
port: {
number: true,
default: 8080,
describe: "port to serve Quartz on",
},
bundleInfo: {
boolean: true,
default: false,
describe: "show detailed bundle information",
},
concurrency: {
number: true,
describe: "how many threads to use to parse notes",
},
}
function escapePath(fp) {
return fp
.replace(/\\ /g, " ") // unescape spaces
.replace(/^".*"$/, "$1")
.replace(/^'.*"$/, "$1")
.trim()
}
function exitIfCancel(val) {
if (isCancel(val)) {
outro(chalk.red("Exiting"))
process.exit(0)
} else {
return val
}
}
async function stashContentFolder(contentFolder) {
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
await fs.promises.cp(contentFolder, contentCacheFolder, {
force: true,
recursive: true,
verbatimSymlinks: true,
preserveTimestamps: true,
})
await fs.promises.rm(contentFolder, { force: true, recursive: true })
}
async function popContentFolder(contentFolder) {
await fs.promises.rm(contentFolder, { force: true, recursive: true })
await fs.promises.cp(contentCacheFolder, contentFolder, {
force: true,
recursive: true,
verbatimSymlinks: true,
preserveTimestamps: true,
})
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
}
function gitPull(origin, branch) {
const flags = ["--no-rebase", "--autostash", "-s", "recursive", "-X", "ours", "--no-edit"]
const out = spawnSync("git", ["pull", ...flags, origin, branch], { stdio: "inherit" })
if (out.stderr) {
throw new Error(`Error while pulling updates: ${out.stderr}`)
}
}
import {
handleBuild,
handleCreate,
handleUpdate,
handleRestore,
handleSync,
} from "./cli/handlers.js"
import { CommonArgv, BuildArgv, CreateArgv, SyncArgv } from "./cli/args.js"
import { version } from "./cli/constants.js"
yargs(hideBin(process.argv))
.scriptName("quartz")
.version(version)
.usage("$0 <cmd> [args]")
.command("create", "Initialize Quartz", CommonArgv, async (argv) => {
console.log()
intro(chalk.bgGreen.black(` Quartz v${version} `))
const contentFolder = path.join(cwd, argv.directory)
const setupStrategy = exitIfCancel(
await select({
message: `Choose how to initialize the content in \`${contentFolder}\``,
options: [
{ value: "new", label: "Empty Quartz" },
{ value: "copy", label: "Copy an existing folder", hint: "overwrites `content`" },
{
value: "symlink",
label: "Symlink an existing folder",
hint: "don't select this unless you know what you are doing!",
},
],
}),
)
async function rmContentFolder() {
const contentStat = await fs.promises.lstat(contentFolder)
if (contentStat.isSymbolicLink()) {
await fs.promises.unlink(contentFolder)
} else {
await rimraf(contentFolder)
}
}
await fs.promises.unlink(path.join(contentFolder, ".gitkeep"))
if (setupStrategy === "copy" || setupStrategy === "symlink") {
const originalFolder = escapePath(
exitIfCancel(
await text({
message: "Enter the full path to existing content folder",
placeholder:
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
validate(fp) {
const fullPath = escapePath(fp)
if (!fs.existsSync(fullPath)) {
return "The given path doesn't exist"
} else if (!fs.lstatSync(fullPath).isDirectory()) {
return "The given path is not a folder"
}
},
}),
),
)
await rmContentFolder()
if (setupStrategy === "copy") {
await fs.promises.cp(originalFolder, contentFolder, {
recursive: true,
preserveTimestamps: true,
})
} else if (setupStrategy === "symlink") {
await fs.promises.symlink(originalFolder, contentFolder, "dir")
}
} else if (setupStrategy === "new") {
await fs.promises.writeFile(
path.join(contentFolder, "index.md"),
`---
title: Welcome to Quartz
---
This is a blank Quartz installation.
See the [documentation](https://quartz.jzhao.xyz) for how to get started.
`,
)
}
// get a preferred link resolution strategy
const linkResolutionStrategy = exitIfCancel(
await select({
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
options: [
{
value: "absolute",
label: "Treat links as absolute path",
hint: "for content made for Quartz 3 and Hugo",
},
{
value: "shortest",
label: "Treat links as shortest path",
hint: "for most Obsidian vaults",
},
{
value: "relative",
label: "Treat links as relative paths",
hint: "for just normal Markdown files",
},
],
}),
)
// now, do config changes
const configFilePath = path.join(cwd, "quartz.config.ts")
let configContent = await fs.promises.readFile(configFilePath, { encoding: "utf-8" })
configContent = configContent.replace(
/markdownLinkResolution: '(.+)'/,
`markdownLinkResolution: '${linkResolutionStrategy}'`,
)
await fs.promises.writeFile(configFilePath, configContent)
outro(`You're all set! Not sure what to do next? Try:
Customizing Quartz a bit more by editing \`quartz.config.ts\`
Running \`npx quartz build --serve\` to preview your Quartz locally
Hosting your Quartz online (see: https://quartz.jzhao.xyz/hosting)
`)
.command("create", "Initialize Quartz", CreateArgv, async (argv) => {
await handleCreate(argv)
})
.command("update", "Get the latest Quartz updates", CommonArgv, async (argv) => {
const contentFolder = path.join(cwd, argv.directory)
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
console.log("Backing up your content")
execSync(
`git remote show upstream || git remote add upstream https://github.com/jackyzha0/quartz.git`,
)
await stashContentFolder(contentFolder)
console.log(
"Pulling updates... you may need to resolve some `git` conflicts if you've made changes to components or plugins.",
)
gitPull(UPSTREAM_NAME, QUARTZ_SOURCE_BRANCH)
await popContentFolder(contentFolder)
console.log("Ensuring dependencies are up to date")
spawnSync("npm", ["i"], { stdio: "inherit" })
console.log(chalk.green("Done!"))
await handleUpdate(argv)
})
.command(
"restore",
"Try to restore your content folder from the cache",
CommonArgv,
async (argv) => {
const contentFolder = path.join(cwd, argv.directory)
await popContentFolder(contentFolder)
await handleRestore(argv)
},
)
.command("sync", "Sync your Quartz to and from GitHub.", SyncArgv, async (argv) => {
const contentFolder = path.join(cwd, argv.directory)
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
console.log("Backing up your content")
if (argv.commit) {
const contentStat = await fs.promises.lstat(contentFolder)
if (contentStat.isSymbolicLink()) {
const linkTarg = await fs.promises.readlink(contentFolder)
console.log(chalk.yellow("Detected symlink, trying to dereference before committing"))
// stash symlink file
await stashContentFolder(contentFolder)
// follow symlink and copy content
await fs.promises.cp(linkTarg, contentFolder, {
recursive: true,
preserveTimestamps: true,
})
}
const currentTimestamp = new Date().toLocaleString("en-US", {
dateStyle: "medium",
timeStyle: "short",
})
spawnSync("git", ["add", "."], { stdio: "inherit" })
spawnSync("git", ["commit", "-m", `Quartz sync: ${currentTimestamp}`], { stdio: "inherit" })
if (contentStat.isSymbolicLink()) {
// put symlink back
await popContentFolder(contentFolder)
}
}
await stashContentFolder(contentFolder)
if (argv.pull) {
console.log(
"Pulling updates from your repository. You may need to resolve some `git` conflicts if you've made changes to components or plugins.",
)
gitPull(ORIGIN_NAME, QUARTZ_SOURCE_BRANCH)
}
await popContentFolder(contentFolder)
if (argv.push) {
console.log("Pushing your changes")
spawnSync("git", ["push", "-f", ORIGIN_NAME, QUARTZ_SOURCE_BRANCH], { stdio: "inherit" })
}
console.log(chalk.green("Done!"))
await handleSync(argv)
})
.command("build", "Build Quartz into a bundle of static HTML files", BuildArgv, async (argv) => {
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
const ctx = await esbuild.context({
entryPoints: [fp],
outfile: path.join("quartz", cacheFile),
bundle: true,
keepNames: true,
minifyWhitespace: true,
minifySyntax: true,
platform: "node",
format: "esm",
jsx: "automatic",
jsxImportSource: "preact",
packages: "external",
metafile: true,
sourcemap: true,
sourcesContent: false,
plugins: [
sassPlugin({
type: "css-text",
cssImports: true,
}),
{
name: "inline-script-loader",
setup(build) {
build.onLoad({ filter: /\.inline\.(ts|js)$/ }, async (args) => {
let text = await promises.readFile(args.path, "utf8")
// remove default exports that we manually inserted
text = text.replace("export default", "")
text = text.replace("export", "")
const sourcefile = path.relative(path.resolve("."), args.path)
const resolveDir = path.dirname(sourcefile)
const transpiled = await esbuild.build({
stdin: {
contents: text,
loader: "ts",
resolveDir,
sourcefile,
},
write: false,
bundle: true,
platform: "browser",
format: "esm",
})
const rawMod = transpiled.outputFiles[0].text
return {
contents: rawMod,
loader: "text",
}
})
},
},
],
})
const buildMutex = new Mutex()
let lastBuildMs = 0
let cleanupBuild = null
const build = async (clientRefresh) => {
const buildStart = new Date().getTime()
lastBuildMs = buildStart
const release = await buildMutex.acquire()
if (lastBuildMs > buildStart) {
release()
return
}
if (cleanupBuild) {
await cleanupBuild()
console.log(chalk.yellow("Detected a source code change, doing a hard rebuild..."))
}
const result = await ctx.rebuild().catch((err) => {
console.error(`${chalk.red("Couldn't parse Quartz configuration:")} ${fp}`)
console.log(`Reason: ${chalk.grey(err)}`)
process.exit(1)
})
release()
if (argv.bundleInfo) {
const outputFileName = "quartz/.quartz-cache/transpiled-build.mjs"
const meta = result.metafile.outputs[outputFileName]
console.log(
`Successfully transpiled ${Object.keys(meta.inputs).length} files (${prettyBytes(
meta.bytes,
)})`,
)
console.log(await esbuild.analyzeMetafile(result.metafile, { color: true }))
}
// bypass module cache
// https://github.com/nodejs/modules/issues/307
const { default: buildQuartz } = await import(cacheFile + `?update=${randomUUID()}`)
cleanupBuild = await buildQuartz(argv, buildMutex, clientRefresh)
clientRefresh()
}
if (argv.serve) {
const connections = []
const clientRefresh = () => connections.forEach((conn) => conn.send("rebuild"))
if (argv.baseDir !== "" && !argv.baseDir.startsWith("/")) {
argv.baseDir = "/" + argv.baseDir
}
await build(clientRefresh)
const server = http.createServer(async (req, res) => {
if (argv.baseDir && !req.url?.startsWith(argv.baseDir)) {
console.log(
chalk.red(
`[404] ${req.url} (warning: link outside of site, this is likely a Quartz bug)`,
),
)
res.writeHead(404)
res.end()
return
}
// strip baseDir prefix
req.url = req.url?.slice(argv.baseDir.length)
const serve = async () => {
const release = await buildMutex.acquire()
await serveHandler(req, res, {
public: argv.output,
directoryListing: false,
headers: [
{
source: "**/*.html",
headers: [{ key: "Content-Disposition", value: "inline" }],
},
],
})
const status = res.statusCode
const statusString =
status >= 200 && status < 300 ? chalk.green(`[${status}]`) : chalk.red(`[${status}]`)
console.log(statusString + chalk.grey(` ${argv.baseDir}${req.url}`))
release()
}
const redirect = (newFp) => {
newFp = argv.baseDir + newFp
res.writeHead(302, {
Location: newFp,
})
console.log(chalk.yellow("[302]") + chalk.grey(` ${argv.baseDir}${req.url} -> ${newFp}`))
res.end()
}
let fp = req.url?.split("?")[0] ?? "/"
// handle redirects
if (fp.endsWith("/")) {
// /trailing/
// does /trailing/index.html exist? if so, serve it
const indexFp = path.posix.join(fp, "index.html")
if (fs.existsSync(path.posix.join(argv.output, indexFp))) {
req.url = fp
return serve()
}
// does /trailing.html exist? if so, redirect to /trailing
let base = fp.slice(0, -1)
if (path.extname(base) === "") {
base += ".html"
}
if (fs.existsSync(path.posix.join(argv.output, base))) {
return redirect(fp.slice(0, -1))
}
} else {
// /regular
// does /regular.html exist? if so, serve it
let base = fp
if (path.extname(base) === "") {
base += ".html"
}
if (fs.existsSync(path.posix.join(argv.output, base))) {
req.url = fp
return serve()
}
// does /regular/index.html exist? if so, redirect to /regular/
let indexFp = path.posix.join(fp, "index.html")
if (fs.existsSync(path.posix.join(argv.output, indexFp))) {
return redirect(fp + "/")
}
}
return serve()
})
server.listen(argv.port)
const wss = new WebSocketServer({ port: 3001 })
wss.on("connection", (ws) => connections.push(ws))
console.log(
chalk.cyan(
`Started a Quartz server listening at http://localhost:${argv.port}${argv.baseDir}`,
),
)
console.log("hint: exit with ctrl+c")
chokidar
.watch(["**/*.ts", "**/*.tsx", "**/*.scss", "package.json"], {
ignoreInitial: true,
})
.on("all", async () => {
build(clientRefresh)
})
} else {
await build(() => {})
ctx.dispose()
}
await handleBuild(argv)
})
.showHelpOnFail(false)
.help()

View File

@ -12,6 +12,10 @@ export type Analytics =
provider: "google"
tagId: string
}
| {
provider: "umami"
websiteId: string
}
export interface GlobalConfiguration {
pageTitle: string

103
quartz/cli/args.js Normal file
View File

@ -0,0 +1,103 @@
export const CommonArgv = {
directory: {
string: true,
alias: ["d"],
default: "content",
describe: "directory to look for content files",
},
verbose: {
boolean: true,
alias: ["v"],
default: false,
describe: "print out extra logging information",
},
}
export const CreateArgv = {
...CommonArgv,
source: {
string: true,
alias: ["s"],
describe: "source directory to copy/create symlink from",
},
strategy: {
string: true,
alias: ["X"],
choices: ["new", "copy", "symlink"],
describe: "strategy for content folder setup",
},
links: {
string: true,
alias: ["l"],
choices: ["absolute", "shortest", "relative"],
describe: "strategy to resolve links",
},
}
export const SyncArgv = {
...CommonArgv,
commit: {
boolean: true,
default: true,
describe: "create a git commit for your unsaved changes",
},
message: {
string: true,
alias: ["m"],
describe: "option to override the default Quartz commit message",
},
push: {
boolean: true,
default: true,
describe: "push updates to your Quartz fork",
},
pull: {
boolean: true,
default: true,
describe: "pull updates from your Quartz fork",
},
}
export const BuildArgv = {
...CommonArgv,
output: {
string: true,
alias: ["o"],
default: "public",
describe: "output folder for files",
},
serve: {
boolean: true,
default: false,
describe: "run a local server to live-preview your Quartz",
},
baseDir: {
string: true,
default: "",
describe: "base path to serve your local server on",
},
port: {
number: true,
default: 8080,
describe: "port to serve Quartz on",
},
wsPort: {
number: true,
default: 3001,
describe: "port to use for WebSocket-based hot-reload notifications",
},
remoteDevHost: {
string: true,
default: "",
describe: "A URL override for the websocket connection if you are not developing on localhost",
},
bundleInfo: {
boolean: true,
default: false,
describe: "show detailed bundle information",
},
concurrency: {
number: true,
describe: "how many threads to use to parse notes",
},
}

15
quartz/cli/constants.js Normal file
View File

@ -0,0 +1,15 @@
import path from "path"
import { readFileSync } from "fs"
/**
* All constants relating to helpers or handlers
*/
export const ORIGIN_NAME = "origin"
export const UPSTREAM_NAME = "upstream"
export const QUARTZ_SOURCE_BRANCH = "v4"
export const cwd = process.cwd()
export const cacheDir = path.join(cwd, ".quartz-cache")
export const cacheFile = "./quartz/.quartz-cache/transpiled-build.mjs"
export const fp = "./quartz/build.ts"
export const { version } = JSON.parse(readFileSync("./package.json").toString())
export const contentCacheFolder = path.join(cacheDir, "content-cache")

512
quartz/cli/handlers.js Normal file
View File

@ -0,0 +1,512 @@
import { promises } from "fs"
import path from "path"
import esbuild from "esbuild"
import chalk from "chalk"
import { sassPlugin } from "esbuild-sass-plugin"
import fs from "fs"
import { intro, outro, select, text } from "@clack/prompts"
import { rimraf } from "rimraf"
import chokidar from "chokidar"
import prettyBytes from "pretty-bytes"
import { execSync, spawnSync } from "child_process"
import http from "http"
import serveHandler from "serve-handler"
import { WebSocketServer } from "ws"
import { randomUUID } from "crypto"
import { Mutex } from "async-mutex"
import { CreateArgv } from "./args.js"
import {
exitIfCancel,
escapePath,
gitPull,
popContentFolder,
stashContentFolder,
} from "./helpers.js"
import {
UPSTREAM_NAME,
QUARTZ_SOURCE_BRANCH,
ORIGIN_NAME,
version,
fp,
cacheFile,
cwd,
} from "./constants.js"
/**
* Handles `npx quartz create`
* @param {*} argv arguments for `create`
*/
export async function handleCreate(argv) {
console.log()
intro(chalk.bgGreen.black(` Quartz v${version} `))
const contentFolder = path.join(cwd, argv.directory)
let setupStrategy = argv.strategy?.toLowerCase()
let linkResolutionStrategy = argv.links?.toLowerCase()
const sourceDirectory = argv.source
// If all cmd arguments were provided, check if theyre valid
if (setupStrategy && linkResolutionStrategy) {
// If setup isn't, "new", source argument is required
if (setupStrategy !== "new") {
// Error handling
if (!sourceDirectory) {
outro(
chalk.red(
`Setup strategies (arg '${chalk.yellow(
`-${CreateArgv.strategy.alias[0]}`,
)}') other than '${chalk.yellow(
"new",
)}' require content folder argument ('${chalk.yellow(
`-${CreateArgv.source.alias[0]}`,
)}') to be set`,
),
)
process.exit(1)
} else {
if (!fs.existsSync(sourceDirectory)) {
outro(
chalk.red(
`Input directory to copy/symlink 'content' from not found ('${chalk.yellow(
sourceDirectory,
)}', invalid argument "${chalk.yellow(`-${CreateArgv.source.alias[0]}`)})`,
),
)
process.exit(1)
} else if (!fs.lstatSync(sourceDirectory).isDirectory()) {
outro(
chalk.red(
`Source directory to copy/symlink 'content' from is not a directory (found file at '${chalk.yellow(
sourceDirectory,
)}', invalid argument ${chalk.yellow(`-${CreateArgv.source.alias[0]}`)}")`,
),
)
process.exit(1)
}
}
}
}
// Use cli process if cmd args werent provided
if (!setupStrategy) {
setupStrategy = exitIfCancel(
await select({
message: `Choose how to initialize the content in \`${contentFolder}\``,
options: [
{ value: "new", label: "Empty Quartz" },
{ value: "copy", label: "Copy an existing folder", hint: "overwrites `content`" },
{
value: "symlink",
label: "Symlink an existing folder",
hint: "don't select this unless you know what you are doing!",
},
],
}),
)
}
async function rmContentFolder() {
const contentStat = await fs.promises.lstat(contentFolder)
if (contentStat.isSymbolicLink()) {
await fs.promises.unlink(contentFolder)
} else {
await rimraf(contentFolder)
}
}
await fs.promises.unlink(path.join(contentFolder, ".gitkeep"))
if (setupStrategy === "copy" || setupStrategy === "symlink") {
let originalFolder = sourceDirectory
// If input directory was not passed, use cli
if (!sourceDirectory) {
originalFolder = escapePath(
exitIfCancel(
await text({
message: "Enter the full path to existing content folder",
placeholder:
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
validate(fp) {
const fullPath = escapePath(fp)
if (!fs.existsSync(fullPath)) {
return "The given path doesn't exist"
} else if (!fs.lstatSync(fullPath).isDirectory()) {
return "The given path is not a folder"
}
},
}),
),
)
}
await rmContentFolder()
if (setupStrategy === "copy") {
await fs.promises.cp(originalFolder, contentFolder, {
recursive: true,
preserveTimestamps: true,
})
} else if (setupStrategy === "symlink") {
await fs.promises.symlink(originalFolder, contentFolder, "dir")
}
} else if (setupStrategy === "new") {
await fs.promises.writeFile(
path.join(contentFolder, "index.md"),
`---
title: Welcome to Quartz
---
This is a blank Quartz installation.
See the [documentation](https://quartz.jzhao.xyz) for how to get started.
`,
)
}
// Use cli process if cmd args werent provided
if (!linkResolutionStrategy) {
// get a preferred link resolution strategy
linkResolutionStrategy = exitIfCancel(
await select({
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
options: [
{
value: "absolute",
label: "Treat links as absolute path",
hint: "for content made for Quartz 3 and Hugo",
},
{
value: "shortest",
label: "Treat links as shortest path",
hint: "for most Obsidian vaults",
},
{
value: "relative",
label: "Treat links as relative paths",
hint: "for just normal Markdown files",
},
],
}),
)
}
// now, do config changes
const configFilePath = path.join(cwd, "quartz.config.ts")
let configContent = await fs.promises.readFile(configFilePath, { encoding: "utf-8" })
configContent = configContent.replace(
/markdownLinkResolution: '(.+)'/,
`markdownLinkResolution: '${linkResolutionStrategy}'`,
)
await fs.promises.writeFile(configFilePath, configContent)
outro(`You're all set! Not sure what to do next? Try:
Customizing Quartz a bit more by editing \`quartz.config.ts\`
Running \`npx quartz build --serve\` to preview your Quartz locally
Hosting your Quartz online (see: https://quartz.jzhao.xyz/hosting)
`)
}
/**
* Handles `npx quartz build`
* @param {*} argv arguments for `build`
*/
export async function handleBuild(argv) {
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
const ctx = await esbuild.context({
entryPoints: [fp],
outfile: cacheFile,
bundle: true,
keepNames: true,
minifyWhitespace: true,
minifySyntax: true,
platform: "node",
format: "esm",
jsx: "automatic",
jsxImportSource: "preact",
packages: "external",
metafile: true,
sourcemap: true,
sourcesContent: false,
plugins: [
sassPlugin({
type: "css-text",
cssImports: true,
}),
{
name: "inline-script-loader",
setup(build) {
build.onLoad({ filter: /\.inline\.(ts|js)$/ }, async (args) => {
let text = await promises.readFile(args.path, "utf8")
// remove default exports that we manually inserted
text = text.replace("export default", "")
text = text.replace("export", "")
const sourcefile = path.relative(path.resolve("."), args.path)
const resolveDir = path.dirname(sourcefile)
const transpiled = await esbuild.build({
stdin: {
contents: text,
loader: "ts",
resolveDir,
sourcefile,
},
write: false,
bundle: true,
platform: "browser",
format: "esm",
})
const rawMod = transpiled.outputFiles[0].text
return {
contents: rawMod,
loader: "text",
}
})
},
},
],
})
const buildMutex = new Mutex()
let lastBuildMs = 0
let cleanupBuild = null
const build = async (clientRefresh) => {
const buildStart = new Date().getTime()
lastBuildMs = buildStart
const release = await buildMutex.acquire()
if (lastBuildMs > buildStart) {
release()
return
}
if (cleanupBuild) {
await cleanupBuild()
console.log(chalk.yellow("Detected a source code change, doing a hard rebuild..."))
}
const result = await ctx.rebuild().catch((err) => {
console.error(`${chalk.red("Couldn't parse Quartz configuration:")} ${fp}`)
console.log(`Reason: ${chalk.grey(err)}`)
process.exit(1)
})
release()
if (argv.bundleInfo) {
const outputFileName = "quartz/.quartz-cache/transpiled-build.mjs"
const meta = result.metafile.outputs[outputFileName]
console.log(
`Successfully transpiled ${Object.keys(meta.inputs).length} files (${prettyBytes(
meta.bytes,
)})`,
)
console.log(await esbuild.analyzeMetafile(result.metafile, { color: true }))
}
// bypass module cache
// https://github.com/nodejs/modules/issues/307
const { default: buildQuartz } = await import(`../../${cacheFile}?update=${randomUUID()}`)
// ^ this import is relative, so base "cacheFile" path can't be used
cleanupBuild = await buildQuartz(argv, buildMutex, clientRefresh)
clientRefresh()
}
if (argv.serve) {
const connections = []
const clientRefresh = () => connections.forEach((conn) => conn.send("rebuild"))
if (argv.baseDir !== "" && !argv.baseDir.startsWith("/")) {
argv.baseDir = "/" + argv.baseDir
}
await build(clientRefresh)
const server = http.createServer(async (req, res) => {
if (argv.baseDir && !req.url?.startsWith(argv.baseDir)) {
console.log(
chalk.red(
`[404] ${req.url} (warning: link outside of site, this is likely a Quartz bug)`,
),
)
res.writeHead(404)
res.end()
return
}
// strip baseDir prefix
req.url = req.url?.slice(argv.baseDir.length)
const serve = async () => {
const release = await buildMutex.acquire()
await serveHandler(req, res, {
public: argv.output,
directoryListing: false,
headers: [
{
source: "**/*.html",
headers: [{ key: "Content-Disposition", value: "inline" }],
},
],
})
const status = res.statusCode
const statusString =
status >= 200 && status < 300 ? chalk.green(`[${status}]`) : chalk.red(`[${status}]`)
console.log(statusString + chalk.grey(` ${argv.baseDir}${req.url}`))
release()
}
const redirect = (newFp) => {
newFp = argv.baseDir + newFp
res.writeHead(302, {
Location: newFp,
})
console.log(chalk.yellow("[302]") + chalk.grey(` ${argv.baseDir}${req.url} -> ${newFp}`))
res.end()
}
let fp = req.url?.split("?")[0] ?? "/"
// handle redirects
if (fp.endsWith("/")) {
// /trailing/
// does /trailing/index.html exist? if so, serve it
const indexFp = path.posix.join(fp, "index.html")
if (fs.existsSync(path.posix.join(argv.output, indexFp))) {
req.url = fp
return serve()
}
// does /trailing.html exist? if so, redirect to /trailing
let base = fp.slice(0, -1)
if (path.extname(base) === "") {
base += ".html"
}
if (fs.existsSync(path.posix.join(argv.output, base))) {
return redirect(fp.slice(0, -1))
}
} else {
// /regular
// does /regular.html exist? if so, serve it
let base = fp
if (path.extname(base) === "") {
base += ".html"
}
if (fs.existsSync(path.posix.join(argv.output, base))) {
req.url = fp
return serve()
}
// does /regular/index.html exist? if so, redirect to /regular/
let indexFp = path.posix.join(fp, "index.html")
if (fs.existsSync(path.posix.join(argv.output, indexFp))) {
return redirect(fp + "/")
}
}
return serve()
})
server.listen(argv.port)
const wss = new WebSocketServer({ port: argv.wsPort })
wss.on("connection", (ws) => connections.push(ws))
console.log(
chalk.cyan(
`Started a Quartz server listening at http://localhost:${argv.port}${argv.baseDir}`,
),
)
console.log("hint: exit with ctrl+c")
chokidar
.watch(["**/*.ts", "**/*.tsx", "**/*.scss", "package.json"], {
ignoreInitial: true,
})
.on("all", async () => {
build(clientRefresh)
})
} else {
await build(() => {})
ctx.dispose()
}
}
/**
* Handles `npx quartz update`
* @param {*} argv arguments for `update`
*/
export async function handleUpdate(argv) {
const contentFolder = path.join(cwd, argv.directory)
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
console.log("Backing up your content")
execSync(
`git remote show upstream || git remote add upstream https://github.com/jackyzha0/quartz.git`,
)
await stashContentFolder(contentFolder)
console.log(
"Pulling updates... you may need to resolve some `git` conflicts if you've made changes to components or plugins.",
)
gitPull(UPSTREAM_NAME, QUARTZ_SOURCE_BRANCH)
await popContentFolder(contentFolder)
console.log("Ensuring dependencies are up to date")
spawnSync("npm", ["i"], { stdio: "inherit" })
console.log(chalk.green("Done!"))
}
/**
* Handles `npx quartz restore`
* @param {*} argv arguments for `restore`
*/
export async function handleRestore(argv) {
const contentFolder = path.join(cwd, argv.directory)
await popContentFolder(contentFolder)
}
/**
* Handles `npx quartz sync`
* @param {*} argv arguments for `sync`
*/
export async function handleSync(argv) {
const contentFolder = path.join(cwd, argv.directory)
console.log(chalk.bgGreen.black(`\n Quartz v${version} \n`))
console.log("Backing up your content")
if (argv.commit) {
const contentStat = await fs.promises.lstat(contentFolder)
if (contentStat.isSymbolicLink()) {
const linkTarg = await fs.promises.readlink(contentFolder)
console.log(chalk.yellow("Detected symlink, trying to dereference before committing"))
// stash symlink file
await stashContentFolder(contentFolder)
// follow symlink and copy content
await fs.promises.cp(linkTarg, contentFolder, {
recursive: true,
preserveTimestamps: true,
})
}
const currentTimestamp = new Date().toLocaleString("en-US", {
dateStyle: "medium",
timeStyle: "short",
})
const commitMessage = argv.message ?? `Quartz sync: ${currentTimestamp}`
spawnSync("git", ["add", "."], { stdio: "inherit" })
spawnSync("git", ["commit", "-m", commitMessage], { stdio: "inherit" })
if (contentStat.isSymbolicLink()) {
// put symlink back
await popContentFolder(contentFolder)
}
}
await stashContentFolder(contentFolder)
if (argv.pull) {
console.log(
"Pulling updates from your repository. You may need to resolve some `git` conflicts if you've made changes to components or plugins.",
)
gitPull(ORIGIN_NAME, QUARTZ_SOURCE_BRANCH)
}
await popContentFolder(contentFolder)
if (argv.push) {
console.log("Pushing your changes")
spawnSync("git", ["push", "-f", ORIGIN_NAME, QUARTZ_SOURCE_BRANCH], { stdio: "inherit" })
}
console.log(chalk.green("Done!"))
}

52
quartz/cli/helpers.js Normal file
View File

@ -0,0 +1,52 @@
import { isCancel, outro } from "@clack/prompts"
import chalk from "chalk"
import { contentCacheFolder } from "./constants.js"
import { spawnSync } from "child_process"
import fs from "fs"
export function escapePath(fp) {
return fp
.replace(/\\ /g, " ") // unescape spaces
.replace(/^".*"$/, "$1")
.replace(/^'.*"$/, "$1")
.trim()
}
export function exitIfCancel(val) {
if (isCancel(val)) {
outro(chalk.red("Exiting"))
process.exit(0)
} else {
return val
}
}
export async function stashContentFolder(contentFolder) {
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
await fs.promises.cp(contentFolder, contentCacheFolder, {
force: true,
recursive: true,
verbatimSymlinks: true,
preserveTimestamps: true,
})
await fs.promises.rm(contentFolder, { force: true, recursive: true })
}
export function gitPull(origin, branch) {
const flags = ["--no-rebase", "--autostash", "-s", "recursive", "-X", "ours", "--no-edit"]
const out = spawnSync("git", ["pull", ...flags, origin, branch], { stdio: "inherit" })
if (out.stderr) {
throw new Error(`Error while pulling updates: ${out.stderr}`)
}
}
export async function popContentFolder(contentFolder) {
await fs.promises.rm(contentFolder, { force: true, recursive: true })
await fs.promises.cp(contentCacheFolder, contentFolder, {
force: true,
recursive: true,
verbatimSymlinks: true,
preserveTimestamps: true,
})
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
}

View File

@ -1,9 +1,9 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function ArticleTitle({ fileData }: QuartzComponentProps) {
function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) {
const title = fileData.frontmatter?.title
if (title) {
return <h1 class="article-title">{title}</h1>
return <h1 class={`article-title ${displayClass ?? ""}`}>{title}</h1>
} else {
return null
}

View File

@ -2,11 +2,11 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/backlinks.scss"
import { resolveRelative, simplifySlug } from "../util/path"
function Backlinks({ fileData, allFiles }: QuartzComponentProps) {
function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) {
const slug = simplifySlug(fileData.slug!)
const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
return (
<div class="backlinks">
<div class={`backlinks ${displayClass ?? ""}`}>
<h3>Backlinks</h3>
<ul class="overflow">
{backlinkFiles.length > 0 ? (

View File

@ -0,0 +1,116 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import breadcrumbsStyle from "./styles/breadcrumbs.scss"
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile"
type CrumbData = {
displayName: string
path: string
}
interface BreadcrumbOptions {
/**
* Symbol between crumbs
*/
spacerSymbol: string
/**
* Name of first crumb
*/
rootName: string
/**
* wether to look up frontmatter title for folders (could cause performance problems with big vaults)
*/
resolveFrontmatterTitle: boolean
/**
* Wether to display breadcrumbs on root `index.md`
*/
hideOnRoot: boolean
}
const defaultOptions: BreadcrumbOptions = {
spacerSymbol: "",
rootName: "Home",
resolveFrontmatterTitle: true,
hideOnRoot: true,
}
function formatCrumb(displayName: string, baseSlug: FullSlug, currentSlug: SimpleSlug): CrumbData {
return {
displayName: displayName.replaceAll("-", " "),
path: resolveRelative(baseSlug, currentSlug),
}
}
export default ((opts?: Partial<BreadcrumbOptions>) => {
// Merge options with defaults
const options: BreadcrumbOptions = { ...defaultOptions, ...opts }
// computed index of folder name to its associated file data
let folderIndex: Map<string, QuartzPluginData> | undefined
function Breadcrumbs({ fileData, allFiles, displayClass }: QuartzComponentProps) {
// Hide crumbs on root if enabled
if (options.hideOnRoot && fileData.slug === "index") {
return <></>
}
// Format entry for root element
const firstEntry = formatCrumb(options.rootName, fileData.slug!, "/" as SimpleSlug)
const crumbs: CrumbData[] = [firstEntry]
if (!folderIndex && options.resolveFrontmatterTitle) {
folderIndex = new Map()
// construct the index for the first time
for (const file of allFiles) {
if (file.slug?.endsWith("index")) {
const folderParts = file.filePath?.split("/")
if (folderParts) {
const folderName = folderParts[folderParts?.length - 2]
folderIndex.set(folderName, file)
}
}
}
}
// Split slug into hierarchy/parts
const slugParts = fileData.slug?.split("/")
if (slugParts) {
// 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)
if (currentFile) {
curPathSegment = currentFile.frontmatter!.title
}
// Add current slug to full path
currentPath += slugParts[i] + "/"
// Format and add current crumb
const crumb = formatCrumb(curPathSegment, fileData.slug!, currentPath as SimpleSlug)
crumbs.push(crumb)
}
// Add current file to crumb (can directly use frontmatter title)
crumbs.push({
displayName: fileData.frontmatter!.title,
path: "",
})
}
return (
<nav class={`breadcrumb-container ${displayClass ?? ""}`} aria-label="breadcrumbs">
{crumbs.map((crumb, index) => (
<div class="breadcrumb-element">
<a href={crumb.path}>{crumb.displayName}</a>
{index !== crumbs.length - 1 && <p>{` ${options.spacerSymbol} `}</p>}
</div>
))}
</nav>
)
}
Breadcrumbs.css = breadcrumbsStyle
return Breadcrumbs
}) satisfies QuartzComponentConstructor

View File

@ -3,7 +3,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import readingTime from "reading-time"
export default (() => {
function ContentMetadata({ cfg, fileData }: QuartzComponentProps) {
function ContentMetadata({ cfg, fileData, displayClass }: QuartzComponentProps) {
const text = fileData.text
if (text) {
const segments: string[] = []
@ -14,7 +14,7 @@ export default (() => {
}
segments.push(timeTaken)
return <p class="content-meta">{segments.join(", ")}</p>
return <p class={`content-meta ${displayClass ?? ""}`}>{segments.join(", ")}</p>
} else {
return null
}

View File

@ -3,11 +3,11 @@
// see: https://v8.dev/features/modules#defer
import darkmodeScript from "./scripts/darkmode.inline"
import styles from "./styles/darkmode.scss"
import { QuartzComponentConstructor } from "./types"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function Darkmode() {
function Darkmode({ displayClass }: QuartzComponentProps) {
return (
<div class="darkmode">
<div class={`darkmode ${displayClass ?? ""}`}>
<input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
<label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}>
<svg

View File

@ -0,0 +1,126 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import explorerStyle from "./styles/explorer.scss"
// @ts-ignore
import script from "./scripts/explorer.inline"
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
import { QuartzPluginData } from "../plugins/vfile"
// Options interface defined in `ExplorerNode` to avoid circular dependency
const defaultOptions = {
title: "Explorer",
folderClickBehavior: "collapse",
folderDefaultState: "collapsed",
useSavedState: true,
sortFn: (a, b) => {
// Sort order: folders first, then files. Sort folders and files alphabetically
if ((!a.file && !b.file) || (a.file && b.file)) {
// numeric: true: Whether numeric collation should be used, such that "1" < "2" < "10"
// sensitivity: "base": Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A
return a.displayName.localeCompare(b.displayName, undefined, {
numeric: true,
sensitivity: "base",
})
}
if (a.file && !b.file) {
return 1
} else {
return -1
}
},
filterFn: (node) => node.name !== "tags",
order: ["filter", "map", "sort"],
} satisfies Options
export default ((userOpts?: Partial<Options>) => {
// Parse config
const opts: Options = { ...defaultOptions, ...userOpts }
// memoized
let fileTree: FileNode
let jsonTree: string
function constructFileTree(allFiles: QuartzPluginData[]) {
if (!fileTree) {
// Construct tree from allFiles
fileTree = new FileNode("")
allFiles.forEach((file) => fileTree.add(file, 1))
/**
* Keys of this object must match corresponding function name of `FileNode`,
* while values must be the argument that will be passed to the function.
*
* e.g. entry for FileNode.sort: `sort: opts.sortFn` (value is sort function from options)
*/
const functions = {
map: opts.mapFn,
sort: opts.sortFn,
filter: opts.filterFn,
}
// Execute all functions (sort, filter, map) that were provided (if none were provided, only default "sort" is applied)
if (opts.order) {
// Order is important, use loop with index instead of order.map()
for (let i = 0; i < opts.order.length; i++) {
const functionName = opts.order[i]
if (functions[functionName]) {
// for every entry in order, call matching function in FileNode and pass matching argument
// e.g. i = 0; functionName = "filter"
// converted to: (if opts.filterFn) => fileTree.filter(opts.filterFn)
// @ts-ignore
// typescript cant statically check these dynamic references, so manually make sure reference is valid and ignore warning
fileTree[functionName].call(fileTree, functions[functionName])
}
}
}
// Get all folders of tree. Initialize with collapsed state
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
// Stringify to pass json tree as data attribute ([data-tree])
jsonTree = JSON.stringify(folders)
}
}
function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
constructFileTree(allFiles)
return (
<div class={`explorer ${displayClass ?? ""}`}>
<button
type="button"
id="explorer"
data-behavior={opts.folderClickBehavior}
data-collapsed={opts.folderDefaultState}
data-savestate={opts.useSavedState}
data-tree={jsonTree}
>
<h1>{opts.title}</h1>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="5 8 14 8"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="fold"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div id="explorer-content">
<ul class="overflow" id="explorer-ul">
<ExplorerNode node={fileTree} opts={opts} fileData={fileData} />
<li id="explorer-end" />
</ul>
</div>
</div>
)
}
Explorer.css = explorerStyle
Explorer.afterDOMLoaded = script
return Explorer
}) satisfies QuartzComponentConstructor

View File

@ -0,0 +1,224 @@
// @ts-ignore
import { QuartzPluginData } from "../plugins/vfile"
import { resolveRelative } from "../util/path"
type OrderEntries = "sort" | "filter" | "map"
export interface Options {
title: string
folderDefaultState: "collapsed" | "open"
folderClickBehavior: "collapse" | "link"
useSavedState: boolean
sortFn: (a: FileNode, b: FileNode) => number
filterFn?: (node: FileNode) => boolean
mapFn?: (node: FileNode) => void
order?: OrderEntries[]
}
type DataWrapper = {
file: QuartzPluginData
path: string[]
}
export type FolderState = {
path: string
collapsed: boolean
}
// Structure to add all files into a tree
export class FileNode {
children: FileNode[]
name: string
displayName: string
file: QuartzPluginData | null
depth: number
constructor(name: string, file?: QuartzPluginData, depth?: number) {
this.children = []
this.name = name
this.displayName = name
this.file = file ? structuredClone(file) : null
this.depth = depth ?? 0
}
private insert(file: DataWrapper) {
if (file.path.length === 1) {
if (file.path[0] !== "index.md") {
this.children.push(new FileNode(file.file.frontmatter!.title, file.file, this.depth + 1))
} else {
const title = file.file.frontmatter?.title
if (title && title !== "index" && file.path[0] === "index.md") {
this.displayName = title
}
}
} else {
const next = file.path[0]
file.path = file.path.splice(1)
for (const child of this.children) {
if (child.name === next) {
child.insert(file)
return
}
}
const newChild = new FileNode(next, undefined, this.depth + 1)
newChild.insert(file)
this.children.push(newChild)
}
}
// Add new file to tree
add(file: QuartzPluginData, splice: number = 0) {
this.insert({ file, path: file.filePath!.split("/").splice(splice) })
}
// Print tree structure (for debugging)
print(depth: number = 0) {
let folderChar = ""
if (!this.file) folderChar = "|"
console.log("-".repeat(depth), folderChar, this.name, this.depth)
this.children.forEach((e) => e.print(depth + 1))
}
/**
* Filter FileNode tree. Behaves similar to `Array.prototype.filter()`, but modifies tree in place
* @param filterFn function to filter tree with
*/
filter(filterFn: (node: FileNode) => boolean) {
this.children = this.children.filter(filterFn)
this.children.forEach((child) => child.filter(filterFn))
}
/**
* Filter FileNode tree. Behaves similar to `Array.prototype.map()`, but modifies tree in place
* @param mapFn function to use for mapping over tree
*/
map(mapFn: (node: FileNode) => void) {
mapFn(this)
this.children.forEach((child) => child.map(mapFn))
}
/**
* Get folder representation with state of tree.
* Intended to only be called on root node before changes to the tree are made
* @param collapsed default state of folders (collapsed by default or not)
* @returns array containing folder state for tree
*/
getFolderPaths(collapsed: boolean): FolderState[] {
const folderPaths: FolderState[] = []
const traverse = (node: FileNode, currentPath: string) => {
if (!node.file) {
const folderPath = currentPath + (currentPath ? "/" : "") + node.name
if (folderPath !== "") {
folderPaths.push({ path: folderPath, collapsed })
}
node.children.forEach((child) => traverse(child, folderPath))
}
}
traverse(this, "")
return folderPaths
}
// Sort order: folders first, then files. Sort folders and files alphabetically
/**
* Sorts tree according to sort/compare function
* @param sortFn compare function used for `.sort()`, also used recursively for children
*/
sort(sortFn: (a: FileNode, b: FileNode) => number) {
this.children = this.children.sort(sortFn)
this.children.forEach((e) => e.sort(sortFn))
}
}
type ExplorerNodeProps = {
node: FileNode
opts: Options
fileData: QuartzPluginData
fullPath?: string
}
export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodeProps) {
// Get options
const folderBehavior = opts.folderClickBehavior
const isDefaultOpen = opts.folderDefaultState === "open"
// Calculate current folderPath
let pathOld = fullPath ? fullPath : ""
let folderPath = ""
if (node.name !== "") {
folderPath = `${pathOld}/${node.name}`
}
return (
<li>
{node.file ? (
// Single file node
<li key={node.file.slug}>
<a href={resolveRelative(fileData.slug!, node.file.slug!)} data-for={node.file.slug}>
{node.displayName}
</a>
</li>
) : (
<div>
{node.name !== "" && (
// Node with entire folder
// Render svg button + folder name, then children
<div class="folder-container">
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="5 8 14 8"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="folder-icon"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
{/* 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={`${folderPath}`} data-for={node.name} class="folder-title">
{node.displayName}
</a>
) : (
<button class="folder-button">
<p class="folder-title">{node.displayName}</p>
</button>
)}
</div>
</div>
)}
{/* Recursively render children of folder */}
<div class={`folder-outer ${node.depth === 0 || isDefaultOpen ? "open" : ""}`}>
<ul
// Inline style for left folder paddings
style={{
paddingLeft: node.name !== "" ? "1.4rem" : "0",
}}
class="content"
data-folderul={folderPath}
>
{node.children.map((childNode, i) => (
<ExplorerNode
node={childNode}
key={i}
opts={opts}
fullPath={folderPath}
fileData={fileData}
/>
))}
</ul>
</div>
</div>
)}
</li>
)
}

View File

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

View File

@ -1,4 +1,4 @@
import { QuartzComponentConstructor } from "./types"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
// @ts-ignore
import script from "./scripts/graph.inline"
import style from "./styles/graph.scss"
@ -13,6 +13,8 @@ export interface D3Config {
linkDistance: number
fontSize: number
opacityScale: number
removeTags: string[]
showTags: boolean
}
interface GraphOptions {
@ -31,6 +33,8 @@ const defaultOptions: GraphOptions = {
linkDistance: 30,
fontSize: 0.6,
opacityScale: 1,
showTags: true,
removeTags: [],
},
globalGraph: {
drag: true,
@ -42,15 +46,17 @@ const defaultOptions: GraphOptions = {
linkDistance: 30,
fontSize: 0.6,
opacityScale: 1,
showTags: true,
removeTags: [],
},
}
export default ((opts?: GraphOptions) => {
function Graph() {
function Graph({ displayClass }: QuartzComponentProps) {
const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
return (
<div class="graph">
<div class={`graph ${displayClass ?? ""}`}>
<h3>Graph View</h3>
<div class="graph-outer">
<div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>

View File

@ -1,4 +1,4 @@
import { joinSegments, pathToRoot } from "../util/path"
import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path"
import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
@ -7,7 +7,11 @@ export default (() => {
const title = fileData.frontmatter?.title ?? "Untitled"
const description = fileData.description?.trim() ?? "No description provided"
const { css, js } = externalResources
const baseDir = pathToRoot(fileData.slug!)
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
const path = url.pathname as FullSlug
const baseDir = fileData.slug === "404" ? path : pathToRoot(fileData.slug!)
const iconPath = joinSegments(baseDir, "static/icon.png")
const ogImagePath = `https://${cfg.baseUrl}/static/og-image.png`

View File

@ -1,11 +1,11 @@
import { pathToRoot } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function PageTitle({ fileData, cfg }: QuartzComponentProps) {
function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) {
const title = cfg?.pageTitle ?? "Untitled Quartz"
const baseDir = pathToRoot(fileData.slug!)
return (
<h1 class="page-title">
<h1 class={`page-title ${displayClass ?? ""}`}>
<a href={baseDir}>{title}</a>
</h1>
)

View File

@ -23,13 +23,12 @@ const defaultOptions = (cfg: GlobalConfiguration): Options => ({
})
export default ((userOpts?: Partial<Options>) => {
function RecentNotes(props: QuartzComponentProps) {
const { allFiles, fileData, displayClass, cfg } = props
function RecentNotes({ 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)
return (
<div class={`recent-notes ${displayClass}`}>
<div class={`recent-notes ${displayClass ?? ""}`}>
<h3>{opts.title}</h3>
<ul class="recent-ul">
{pages.slice(0, opts.limit).map((page) => {

View File

@ -1,12 +1,12 @@
import { QuartzComponentConstructor } from "./types"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/search.scss"
// @ts-ignore
import script from "./scripts/search.inline"
export default (() => {
function Search() {
function Search({ displayClass }: QuartzComponentProps) {
return (
<div class="search">
<div class={`search ${displayClass ?? ""}`}>
<div id="search-icon">
<p>Search</p>
<div></div>

View File

@ -1,8 +1,7 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function Spacer({ displayClass }: QuartzComponentProps) {
const className = displayClass ? `spacer ${displayClass}` : "spacer"
return <div class={className}></div>
return <div class={`spacer ${displayClass ?? ""}`}></div>
}
export default (() => Spacer) satisfies QuartzComponentConstructor

View File

@ -19,8 +19,8 @@ function TableOfContents({ fileData, displayClass }: QuartzComponentProps) {
}
return (
<div class={`toc ${displayClass}`}>
<button type="button" id="toc">
<div class={`toc ${displayClass ?? ""}`}>
<button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}>
<h3>Table of Contents</h3>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -60,7 +60,7 @@ function LegacyTableOfContents({ fileData }: QuartzComponentProps) {
}
return (
<details id="toc" open>
<details id="toc" open={!fileData.collapseToc}>
<summary>
<h3>Table of Contents</h3>
</summary>

View File

@ -1,12 +1,12 @@
import { pathToRoot, slugTag } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function TagList({ fileData }: QuartzComponentProps) {
function TagList({ fileData, displayClass }: QuartzComponentProps) {
const tags = fileData.frontmatter?.tags
const baseDir = pathToRoot(fileData.slug!)
if (tags && tags.length > 0) {
return (
<ul class="tags">
<ul class={`tags ${displayClass ?? ""}`}>
{tags.map((tag) => {
const display = `#${tag}`
const linkDest = baseDir + `/tags/${slugTag(tag)}`
@ -32,6 +32,12 @@ TagList.css = `
padding-left: 0;
gap: 0.4rem;
margin: 1rem 0;
flex-wrap: wrap;
justify-self: end;
}
.section-li > .section > .tags {
justify-content: flex-end;
}
.tags > li {
@ -41,7 +47,7 @@ TagList.css = `
overflow-wrap: normal;
}
a.tag-link {
a.internal.tag-link {
border-radius: 8px;
background-color: var(--highlight);
padding: 0.2rem 0.4rem;

View File

@ -1,13 +1,15 @@
import ArticleTitle from "./ArticleTitle"
import Content from "./pages/Content"
import TagContent from "./pages/TagContent"
import FolderContent from "./pages/FolderContent"
import NotFound from "./pages/404"
import ArticleTitle from "./ArticleTitle"
import Darkmode from "./Darkmode"
import Head from "./Head"
import PageTitle from "./PageTitle"
import ContentMeta from "./ContentMeta"
import Spacer from "./Spacer"
import TableOfContents from "./TableOfContents"
import Explorer from "./Explorer"
import TagList from "./TagList"
import Graph from "./Graph"
import Backlinks from "./Backlinks"
@ -16,6 +18,7 @@ import Footer from "./Footer"
import DesktopOnly from "./DesktopOnly"
import MobileOnly from "./MobileOnly"
import RecentNotes from "./RecentNotes"
import Breadcrumbs from "./Breadcrumbs"
export {
ArticleTitle,
@ -28,6 +31,7 @@ export {
ContentMeta,
Spacer,
TableOfContents,
Explorer,
TagList,
Graph,
Backlinks,
@ -36,4 +40,6 @@ export {
DesktopOnly,
MobileOnly,
RecentNotes,
NotFound,
Breadcrumbs,
}

View File

@ -0,0 +1,12 @@
import { QuartzComponentConstructor } from "../types"
function NotFound() {
return (
<article class="popover-hint">
<h1>404</h1>
<p>Either this page is private or doesn't exist.</p>
</article>
)
}
export default (() => NotFound) satisfies QuartzComponentConstructor

View File

@ -1,10 +1,8 @@
import { htmlToJsx } from "../../util/jsx"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
function Content({ tree }: QuartzComponentProps) {
// @ts-ignore (preact makes it angry)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
function Content({ fileData, tree }: QuartzComponentProps) {
const content = htmlToJsx(fileData.filePath!, tree)
return <article class="popover-hint">{content}</article>
}

View File

@ -1,12 +1,12 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import path from "path"
import style from "../styles/listPage.scss"
import { PageList } from "../PageList"
import { _stripSlashes, simplifySlug } from "../../util/path"
import { Root } from "hast"
import { pluralize } from "../../util/lang"
import { htmlToJsx } from "../../util/jsx"
function FolderContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props
@ -28,15 +28,14 @@ function FolderContent(props: QuartzComponentProps) {
const content =
(tree as Root).children.length === 0
? fileData.description
: // @ts-ignore
toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
: htmlToJsx(fileData.filePath!, tree)
return (
<div class="popover-hint">
<article>
<p>{content}</p>
</article>
<p>{allPagesInFolder.length} items under this folder.</p>
<p>{pluralize(allPagesInFolder.length, "item")} under this folder.</p>
<div>
<PageList {...listProps} />
</div>

View File

@ -1,11 +1,11 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import style from "../styles/listPage.scss"
import { PageList } from "../PageList"
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
import { QuartzPluginData } from "../../plugins/vfile"
import { Root } from "hast"
import { pluralize } from "../../util/lang"
import { htmlToJsx } from "../../util/jsx"
const numPages = 10
function TagContent(props: QuartzComponentProps) {
@ -25,8 +25,7 @@ function TagContent(props: QuartzComponentProps) {
const content =
(tree as Root).children.length === 0
? fileData.description
: // @ts-ignore
toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
: htmlToJsx(fileData.filePath!, tree)
if (tag === "") {
const tags = [...new Set(allFiles.flatMap((data) => data.frontmatter?.tags ?? []))]
@ -54,13 +53,13 @@ function TagContent(props: QuartzComponentProps) {
return (
<div>
<h2>
<a class="internal tag-link" href={`./${tag}`}>
<a class="internal tag-link" href={`../tags/${tag}`}>
#{tag}
</a>
</h2>
{content && <p>{content}</p>}
<p>
{pages.length} items with this tag.{" "}
{pluralize(pages.length, "item")} with this tag.{" "}
{pages.length > numPages && `Showing first ${numPages}.`}
</p>
<PageList limit={numPages} {...listProps} />
@ -80,7 +79,7 @@ function TagContent(props: QuartzComponentProps) {
return (
<div class="popover-hint">
<article>{content}</article>
<p>{pages.length} items with this tag.</p>
<p>{pluralize(pages.length, "item")} with this tag.</p>
<div>
<PageList {...listProps} />
</div>

View File

@ -3,7 +3,9 @@ import { QuartzComponent, QuartzComponentProps } from "./types"
import HeaderConstructor from "./Header"
import BodyConstructor from "./Body"
import { JSResourceToScriptElement, StaticResources } from "../util/resources"
import { FullSlug, joinSegments, pathToRoot } from "../util/path"
import { FullSlug, RelativeURL, joinSegments } from "../util/path"
import { visit } from "unist-util-visit"
import { Root, Element, ElementContent } from "hast"
interface RenderComponents {
head: QuartzComponent
@ -15,9 +17,10 @@ interface RenderComponents {
footer: QuartzComponent
}
export function pageResources(slug: FullSlug, staticResources: StaticResources): StaticResources {
const baseDir = pathToRoot(slug)
export function pageResources(
baseDir: FullSlug | RelativeURL,
staticResources: StaticResources,
): StaticResources {
const contentIndexPath = joinSegments(baseDir, "static/contentIndex.json")
const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
@ -52,6 +55,99 @@ export function renderPage(
components: RenderComponents,
pageResources: StaticResources,
): string {
// process transcludes in componentData
visit(componentData.tree as 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
// TODO: avoid this expensive find operation and construct an index ahead of time
const page = componentData.allFiles.find((f) => f.slug === transcludeTarget)
if (!page) {
return
}
let blockRef = node.properties?.dataBlock as string | undefined
if (blockRef?.startsWith("^")) {
// block transclude
blockRef = blockRef.slice(1)
let blockNode = page.blocks?.[blockRef]
if (blockNode) {
if (blockNode.tagName === "li") {
blockNode = {
type: "element",
tagName: "ul",
children: [blockNode],
}
}
node.children = [
blockNode,
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
children: [{ type: "text", value: `Link to original` }],
},
]
}
} else if (blockRef?.startsWith("#") && page.htmlAst) {
// header transclude
blockRef = blockRef.slice(1)
let startIdx = 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
}
if (startIdx) {
endIdx = i
} else if (el.properties?.id === blockRef) {
startIdx = i
}
}
}
if (!startIdx) {
return
}
node.children = [
...(page.htmlAst.children.slice(startIdx, endIdx) as ElementContent[]),
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
children: [{ type: "text", value: `Link to original` }],
},
]
} else if (page.htmlAst) {
// page transclude
node.children = [
{
type: "element",
tagName: "h1",
children: [
{ type: "text", value: page.frontmatter?.title ?? `Transclude of ${page.slug}` },
],
},
...(page.htmlAst.children as ElementContent[]),
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal"] },
children: [{ type: "text", value: `Link to original` }],
},
]
}
}
}
})
const {
head: Head,
header,

View File

@ -20,4 +20,13 @@ document.addEventListener("nav", () => {
if (currentTheme === "dark") {
toggleSwitch.checked = true
}
// Listen for changes in prefers-color-scheme
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
colorSchemeMediaQuery.addEventListener("change", (e) => {
const newTheme = e.matches ? "dark" : "light"
document.documentElement.setAttribute("saved-theme", newTheme)
localStorage.setItem("theme", newTheme)
toggleSwitch.checked = e.matches
})
})

View File

@ -0,0 +1,164 @@
import { FolderState } from "../ExplorerNode"
// Current state of folders
let explorerState: FolderState[]
const observer = new IntersectionObserver((entries) => {
// If last element is observed, remove gradient of "overflow" class so element is visible
const explorer = document.getElementById("explorer-ul")
for (const entry of entries) {
if (entry.isIntersecting) {
explorer?.classList.add("no-background")
} else {
explorer?.classList.remove("no-background")
}
}
})
function toggleExplorer(this: HTMLElement) {
// Toggle collapsed state of entire explorer
this.classList.toggle("collapsed")
const content = this.nextElementSibling as HTMLElement
content.classList.toggle("collapsed")
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
}
function toggleFolder(evt: MouseEvent) {
evt.stopPropagation()
// Element that was clicked
const target = evt.target as HTMLElement
// Check if target was svg icon or button
const isSvg = target.nodeName === "svg"
// corresponding <ul> element relative to clicked button/folder
let childFolderContainer: HTMLElement
// <li> element of folder (stores folder-path dataset)
let currentFolderParent: HTMLElement
// Get correct relative container and toggle collapsed class
if (isSvg) {
childFolderContainer = target.parentElement?.nextSibling as HTMLElement
currentFolderParent = target.nextElementSibling as HTMLElement
childFolderContainer.classList.toggle("open")
} else {
childFolderContainer = target.parentElement?.parentElement?.nextElementSibling as HTMLElement
currentFolderParent = target.parentElement as HTMLElement
childFolderContainer.classList.toggle("open")
}
if (!childFolderContainer) return
// Collapse folder container
const isCollapsed = childFolderContainer.classList.contains("open")
setFolderState(childFolderContainer, !isCollapsed)
// Save folder state to localStorage
const clickFolderPath = currentFolderParent.dataset.folderpath as string
// Remove leading "/"
const fullFolderPath = clickFolderPath.substring(1)
toggleCollapsedByPath(explorerState, fullFolderPath)
const stringifiedFileTree = JSON.stringify(explorerState)
localStorage.setItem("fileTree", stringifiedFileTree)
}
function setupExplorer() {
// Set click handler for collapsing entire explorer
const explorer = document.getElementById("explorer")
// Get folder state from local storage
const storageTree = localStorage.getItem("fileTree")
// Convert to bool
const useSavedFolderState = explorer?.dataset.savestate === "true"
if (explorer) {
// Get config
const collapseBehavior = explorer.dataset.behavior
// Add click handlers for all folders (click handler on folder "label")
if (collapseBehavior === "collapse") {
Array.prototype.forEach.call(
document.getElementsByClassName("folder-button"),
function (item) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
},
)
}
// Add click handler to main explorer
explorer.removeEventListener("click", toggleExplorer)
explorer.addEventListener("click", toggleExplorer)
}
// Set up click handlers for each folder (click handler on folder "icon")
Array.prototype.forEach.call(document.getElementsByClassName("folder-icon"), function (item) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
})
if (storageTree && useSavedFolderState) {
// Get state from localStorage and set folder state
explorerState = JSON.parse(storageTree)
explorerState.map((folderUl) => {
// grab <li> element for matching folder path
const folderLi = document.querySelector(
`[data-folderpath='/${folderUl.path}']`,
) as HTMLElement
// Get corresponding content <ul> tag and set state
if (folderLi) {
const folderUL = folderLi.parentElement?.nextElementSibling
if (folderUL) {
setFolderState(folderUL as HTMLElement, folderUl.collapsed)
}
}
})
} else {
// If tree is not in localStorage or config is disabled, use tree passed from Explorer as dataset
explorerState = JSON.parse(explorer?.dataset.tree as string)
}
}
window.addEventListener("resize", setupExplorer)
document.addEventListener("nav", () => {
setupExplorer()
const explorerContent = document.getElementById("explorer-ul")
// select pseudo element at end of list
const lastItem = document.getElementById("explorer-end")
observer.disconnect()
observer.observe(lastItem as Element)
})
/**
* Toggles the state of a given folder
* @param folderElement <div class="folder-outer"> Element of folder (parent)
* @param collapsed if folder should be set to collapsed or not
*/
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {
if (collapsed) {
folderElement?.classList.remove("open")
} else {
folderElement?.classList.add("open")
}
}
/**
* Toggles visibility of a folder
* @param array array of FolderState (`fileTree`, either get from local storage or data attribute)
* @param path path to folder (e.g. 'advanced/more/more2')
*/
function toggleCollapsedByPath(array: FolderState[], path: string) {
const entry = array.find((item) => item.path === path)
if (entry) {
entry.collapsed = !entry.collapsed
}
}

View File

@ -42,19 +42,38 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
linkDistance,
fontSize,
opacityScale,
removeTags,
showTags,
} = JSON.parse(graph.dataset["cfg"]!)
const data = await fetchData
const links: LinkData[] = []
const tags: SimpleSlug[] = []
const validLinks = new Set(Object.keys(data).map((slug) => simplifySlug(slug as FullSlug)))
for (const [src, details] of Object.entries<ContentDetails>(data)) {
const source = simplifySlug(src as FullSlug)
const outgoing = details.links ?? []
for (const dest of outgoing) {
if (dest in data) {
if (validLinks.has(dest)) {
links.push({ source, target: dest })
}
}
if (showTags) {
const localTags = details.tags
.filter((tag) => !removeTags.includes(tag))
.map((tag) => simplifySlug(("tags/" + tag) as FullSlug))
tags.push(...localTags.filter((tag) => !tags.includes(tag)))
for (const tag of localTags) {
links.push({ source, target: tag })
}
}
}
const neighbourhood = new Set<SimpleSlug>()
@ -75,14 +94,18 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
}
} else {
Object.keys(data).forEach((id) => neighbourhood.add(simplifySlug(id as FullSlug)))
if (showTags) tags.forEach((tag) => neighbourhood.add(tag))
}
const graphData: { nodes: NodeData[]; links: LinkData[] } = {
nodes: [...neighbourhood].map((url) => ({
nodes: [...neighbourhood].map((url) => {
const text = url.startsWith("tags/") ? "#" + url.substring(5) : data[url]?.title ?? url
return {
id: url,
text: data[url]?.title ?? url,
text: text,
tags: data[url]?.tags ?? [],
})),
}
}),
links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target)),
}
@ -126,7 +149,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
const isCurrent = d.id === slug
if (isCurrent) {
return "var(--secondary)"
} else if (visited.has(d.id)) {
} else if (visited.has(d.id) || d.id.startsWith("tags/")) {
return "var(--tertiary)"
} else {
return "var(--gray)"
@ -230,9 +253,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
.attr("dx", 0)
.attr("dy", (d) => -nodeRadius(d) + "px")
.attr("text-anchor", "middle")
.text(
(d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "),
)
.text((d) => d.text)
.style("opacity", (opacityScale - 1) / 3.75)
.style("pointer-events", "none")
.style("font-size", fontSize + "em")

View File

@ -28,8 +28,11 @@ async function mouseEnterHandler(
})
}
const hasAlreadyBeenFetched = () =>
[...link.children].some((child) => child.classList.contains("popover"))
// dont refetch if there's already a popover
if ([...link.children].some((child) => child.classList.contains("popover"))) {
if (hasAlreadyBeenFetched()) {
return setPosition(link.lastChild as HTMLElement)
}
@ -49,6 +52,11 @@ async function mouseEnterHandler(
console.error(err)
})
// bailout if another popover exists
if (hasAlreadyBeenFetched()) {
return
}
if (!contents) return
const html = p.parseFromString(contents, "text/html")
normalizeRelativeURLs(html, targetUrl)

View File

@ -1,4 +1,4 @@
import { Document } from "flexsearch"
import { Document, SimpleDocumentSearchResultSetUnit } from "flexsearch"
import { ContentDetails } from "../../plugins/emitters/contentIndex"
import { registerEscapeHandler, removeAllChildren } from "./util"
import { FullSlug, resolveRelative } from "../../util/path"
@ -8,12 +8,20 @@ interface Item {
slug: FullSlug
title: string
content: string
tags: string[]
}
let index: Document<Item> | undefined = undefined
// Can be expanded with things like "term" in the future
type SearchType = "basic" | "tags"
// Current searchType
let searchType: SearchType = "basic"
const contextWindowWords = 30
const numSearchResults = 5
const numTagResults = 3
function highlight(searchTerm: string, text: string, trim?: boolean) {
// try to highlight longest tokens first
const tokenizedTerms = searchTerm
@ -74,6 +82,7 @@ document.addEventListener("nav", async (e: unknown) => {
const searchIcon = document.getElementById("search-icon")
const searchBar = document.getElementById("search-bar") as HTMLInputElement | null
const results = document.getElementById("results-container")
const resultCards = document.getElementsByClassName("result-card")
const idDataMap = Object.keys(data) as FullSlug[]
function hideSearch() {
@ -87,9 +96,12 @@ document.addEventListener("nav", async (e: unknown) => {
if (results) {
removeAllChildren(results)
}
searchType = "basic" // reset search type after closing
}
function showSearch() {
function showSearch(searchTypeNew: SearchType) {
searchType = searchTypeNew
if (sidebar) {
sidebar.style.zIndex = "1"
}
@ -98,36 +110,123 @@ document.addEventListener("nav", async (e: unknown) => {
}
function shortcutHandler(e: HTMLElementEventMap["keydown"]) {
if (e.key === "k" && (e.ctrlKey || e.metaKey)) {
if (e.key === "k" && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
e.preventDefault()
const searchBarOpen = container?.classList.contains("active")
searchBarOpen ? hideSearch() : showSearch()
searchBarOpen ? hideSearch() : showSearch("basic")
} else if (e.shiftKey && (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
// Hotkey to open tag search
e.preventDefault()
const searchBarOpen = container?.classList.contains("active")
searchBarOpen ? hideSearch() : showSearch("tags")
// add "#" prefix for tag search
if (searchBar) searchBar.value = "#"
} else if (e.key === "Enter") {
// If result has focus, navigate to that one, otherwise pick first result
if (results?.contains(document.activeElement)) {
const active = document.activeElement as HTMLInputElement
active.click()
} else {
const anchor = document.getElementsByClassName("result-card")[0] as HTMLInputElement | null
if (anchor) {
anchor.click()
anchor?.click()
}
} else if (e.key === "ArrowDown") {
e.preventDefault()
// When first pressing ArrowDown, results wont contain the active element, so focus first element
if (!results?.contains(document.activeElement)) {
const firstResult = resultCards[0] as HTMLInputElement | null
firstResult?.focus()
} else {
// If an element in results-container already has focus, focus next one
const nextResult = document.activeElement?.nextElementSibling as HTMLInputElement | null
nextResult?.focus()
}
} else if (e.key === "ArrowUp") {
e.preventDefault()
if (results?.contains(document.activeElement)) {
// If an element in results-container already has focus, focus previous one
const prevResult = document.activeElement?.previousElementSibling as HTMLInputElement | null
prevResult?.focus()
}
}
}
function trimContent(content: string) {
// works without escaping html like in `description.ts`
const sentences = content.replace(/\s+/g, " ").split(".")
let finalDesc = ""
let sentenceIdx = 0
// Roughly estimate characters by (words * 5). Matches description length in `description.ts`.
const len = contextWindowWords * 5
while (finalDesc.length < len) {
const sentence = sentences[sentenceIdx]
if (!sentence) break
finalDesc += sentence + "."
sentenceIdx++
}
// If more content would be available, indicate it by finishing with "..."
if (finalDesc.length < content.length) {
finalDesc += ".."
}
return finalDesc
}
const formatForDisplay = (term: string, id: number) => {
const slug = idDataMap[id]
return {
id,
slug,
title: highlight(term, data[slug].title ?? ""),
content: highlight(term, data[slug].content ?? "", true),
title: searchType === "tags" ? data[slug].title : highlight(term, data[slug].title ?? ""),
// if searchType is tag, display context from start of file and trim, otherwise use regular highlight
content:
searchType === "tags"
? trimContent(data[slug].content)
: highlight(term, data[slug].content ?? "", true),
tags: highlightTags(term, data[slug].tags),
}
}
const resultToHTML = ({ slug, title, content }: Item) => {
function highlightTags(term: string, tags: string[]) {
if (tags && searchType === "tags") {
// Find matching tags
const termLower = term.toLowerCase()
let matching = tags.filter((str) => str.includes(termLower))
// Substract matching from original tags, then push difference
if (matching.length > 0) {
let difference = tags.filter((x) => !matching.includes(x))
// Convert to html (cant be done later as matches/term dont get passed to `resultToHTML`)
matching = matching.map((tag) => `<li><p class="match-tag">#${tag}</p></li>`)
difference = difference.map((tag) => `<li><p>#${tag}</p></li>`)
matching.push(...difference)
}
// Only allow max of `numTagResults` in preview
if (tags.length > numTagResults) {
matching.splice(numTagResults)
}
return matching
} else {
return []
}
}
const resultToHTML = ({ slug, title, content, tags }: Item) => {
const htmlTags = tags.length > 0 ? `<ul>${tags.join("")}</ul>` : ``
const button = document.createElement("button")
button.classList.add("result-card")
button.id = slug
button.innerHTML = `<h3>${title}</h3><p>${content}</p>`
button.innerHTML = `<h3>${title}</h3>${htmlTags}<p>${content}</p>`
button.addEventListener("click", () => {
const targ = resolveRelative(currentSlug, slug)
window.spaNavigate(new URL(targ, window.location.toString()))
hideSearch()
})
return button
}
@ -147,15 +246,45 @@ document.addEventListener("nav", async (e: unknown) => {
}
async function onType(e: HTMLElementEventMap["input"]) {
const term = (e.target as HTMLInputElement).value
const searchResults = (await index?.searchAsync(term, numSearchResults)) ?? []
let term = (e.target as HTMLInputElement).value
let searchResults: SimpleDocumentSearchResultSetUnit[]
if (term.toLowerCase().startsWith("#")) {
searchType = "tags"
} else {
searchType = "basic"
}
switch (searchType) {
case "tags": {
term = term.substring(1)
searchResults =
(await index?.searchAsync({ query: term, limit: numSearchResults, index: ["tags"] })) ??
[]
break
}
case "basic":
default: {
searchResults =
(await index?.searchAsync({
query: term,
limit: numSearchResults,
index: ["title", "content"],
})) ?? []
}
}
const getByField = (field: string): number[] => {
const results = searchResults.filter((x) => x.field === field)
return results.length === 0 ? [] : ([...results[0].result] as number[])
}
// order titles ahead of content
const allIds: Set<number> = new Set([...getByField("title"), ...getByField("content")])
const allIds: Set<number> = new Set([
...getByField("title"),
...getByField("content"),
...getByField("tags"),
])
const finalResults = [...allIds].map((id) => formatForDisplay(term, id))
displayResults(finalResults)
}
@ -166,15 +295,14 @@ document.addEventListener("nav", async (e: unknown) => {
document.addEventListener("keydown", shortcutHandler)
prevShortcutHandler = shortcutHandler
searchIcon?.removeEventListener("click", showSearch)
searchIcon?.addEventListener("click", showSearch)
searchIcon?.removeEventListener("click", () => showSearch("basic"))
searchIcon?.addEventListener("click", () => showSearch("basic"))
searchBar?.removeEventListener("input", onType)
searchBar?.addEventListener("input", onType)
// setup index if it hasn't been already
if (!index) {
index = new Document({
cache: true,
charset: "latin:extra",
optimize: true,
encode: encoder,
@ -189,10 +317,27 @@ document.addEventListener("nav", async (e: unknown) => {
field: "content",
tokenize: "reverse",
},
{
field: "tags",
tokenize: "reverse",
},
],
},
})
fillDocument(index, data)
}
// register handlers
registerEscapeHandler(container, hideSearch)
})
/**
* Fills flexsearch document with data
* @param index index to fill
* @param data data to fill index with
*/
async function fillDocument(index: Document<Item, false>, data: any) {
let id = 0
for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
await index.addAsync(id, {
@ -200,11 +345,8 @@ document.addEventListener("nav", async (e: unknown) => {
slug: slug as FullSlug,
title: fileData.title,
content: fileData.content,
tags: fileData.tags,
})
id++
}
}
// register handlers
registerEscapeHandler(container, hideSearch)
})

View File

@ -1,5 +1,6 @@
import micromorph from "micromorph"
import { FullSlug, RelativeURL, getFullSlug } from "../../util/path"
import { normalizeRelativeURLs } from "./popover.inline"
// adapted from `micromorph`
// https://github.com/natemoo-re/micromorph
@ -18,8 +19,15 @@ const isLocalUrl = (href: string) => {
return false
}
const isSamePage = (url: URL): boolean => {
const sameOrigin = url.origin === window.location.origin
const samePath = url.pathname === window.location.pathname
return sameOrigin && samePath
}
const getOpts = ({ target }: Event): { url: URL; scroll?: boolean } | undefined => {
if (!isElement(target)) return
if (target.attributes.getNamedItem("target")?.value === "_blank") return
const a = target.closest("a")
if (!a) return
if ("routerIgnore" in a.dataset) return
@ -45,6 +53,8 @@ async function navigate(url: URL, isBack: boolean = false) {
if (!contents) return
const html = p.parseFromString(contents, "text/html")
normalizeRelativeURLs(html, url)
let title = html.querySelector("title")?.textContent
if (title) {
document.title = title
@ -92,8 +102,16 @@ function createRouter() {
if (typeof window !== "undefined") {
window.addEventListener("click", async (event) => {
const { url } = getOpts(event) ?? {}
if (!url) return
// dont hijack behaviour, just let browser act normally
if (!url || event.ctrlKey || event.metaKey) return
event.preventDefault()
if (isSamePage(url) && url.hash) {
const el = document.getElementById(decodeURIComponent(url.hash.substring(1)))
el?.scrollIntoView()
return
}
try {
navigate(url, false)
} catch (e) {

View File

@ -24,8 +24,9 @@ function toggleToc(this: HTMLElement) {
function setupToc() {
const toc = document.getElementById("toc")
if (toc) {
const collapsed = toc.classList.contains("collapsed")
const content = toc.nextElementSibling as HTMLElement
content.style.maxHeight = content.scrollHeight + "px"
content.style.maxHeight = collapsed ? "0px" : content.scrollHeight + "px"
toc.removeEventListener("click", toggleToc)
toc.addEventListener("click", toggleToc)
}

View File

@ -0,0 +1,22 @@
.breadcrumb-container {
margin: 0;
margin-top: 0.75rem;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.5rem;
}
.breadcrumb-element {
p {
margin: 0;
margin-left: 0.5rem;
padding: 0;
line-height: normal;
}
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}

View File

@ -10,7 +10,6 @@
background-color: var(--light);
border: 1px solid;
border-radius: 5px;
z-index: 1;
opacity: 0;
transition: 0.2s;

View File

@ -21,6 +21,14 @@
}
}
:root[saved-theme="dark"] {
color-scheme: dark;
}
:root[saved-theme="light"] {
color-scheme: light;
}
:root[saved-theme="dark"] .toggle ~ label {
& > #dayIcon {
opacity: 0;

View File

@ -0,0 +1,146 @@
button#explorer {
all: unset;
background-color: transparent;
border: none;
text-align: left;
cursor: pointer;
padding: 0;
color: var(--dark);
display: flex;
align-items: center;
& h1 {
font-size: 1rem;
display: inline-block;
margin: 0;
}
& .fold {
margin-left: 0.5rem;
transition: transform 0.3s ease;
opacity: 0.8;
}
&.collapsed .fold {
transform: rotateZ(-90deg);
}
}
.folder-outer {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-in-out;
}
.folder-outer.open {
grid-template-rows: 1fr;
}
.folder-outer > ul {
overflow: hidden;
}
#explorer-content {
list-style: none;
overflow: hidden;
max-height: none;
transition: max-height 0.35s ease;
margin-top: 0.5rem;
&.collapsed > .overflow::after {
opacity: 0;
}
& ul {
list-style: none;
margin: 0.08rem 0;
padding: 0;
transition:
max-height 0.35s ease,
transform 0.35s ease,
opacity 0.2s ease;
& li > a {
color: var(--dark);
opacity: 0.75;
pointer-events: all;
}
}
}
svg {
pointer-events: all;
& > polyline {
pointer-events: none;
}
}
.folder-container {
flex-direction: row;
display: flex;
align-items: center;
user-select: none;
& div > a {
color: var(--secondary);
font-family: var(--headerFont);
font-size: 0.95rem;
font-weight: 600;
line-height: 1.5rem;
display: inline-block;
}
& div > a:hover {
color: var(--tertiary);
}
& div > button {
color: var(--dark);
background-color: transparent;
border: none;
text-align: left;
cursor: pointer;
padding-left: 0;
padding-right: 0;
display: flex;
align-items: center;
font-family: var(--headerFont);
& p {
font-size: 0.95rem;
display: inline-block;
color: var(--secondary);
font-weight: 600;
margin: 0;
line-height: 1.5rem;
pointer-events: none;
}
}
}
.folder-icon {
margin-right: 5px;
color: var(--secondary);
cursor: pointer;
transition: transform 0.3s ease;
backface-visibility: visible;
}
div:has(> .folder-outer:not(.open)) > .folder-container > svg {
transform: rotate(-90deg);
}
.folder-icon:hover {
color: var(--tertiary);
}
.no-background::after {
background: none !important;
}
#explorer-end {
// needs height so IntersectionObserver gets triggered
height: 4px;
// remove default margin from li
margin: 0;
}

View File

@ -19,11 +19,6 @@ li.section-li {
}
}
& > .tags {
justify-self: end;
margin-left: 1rem;
}
& > .desc > h3 > a {
background-color: transparent;
}

View File

@ -130,6 +130,44 @@
margin: 0;
}
& > ul > li {
margin: 0;
display: inline-block;
white-space: nowrap;
margin: 0;
overflow-wrap: normal;
}
& > ul {
list-style: none;
display: flex;
padding-left: 0;
gap: 0.4rem;
margin: 0;
margin-top: 0.45rem;
// Offset border radius
margin-left: -2px;
overflow: hidden;
background-clip: border-box;
}
& > ul > li > p {
border-radius: 8px;
background-color: var(--highlight);
overflow: hidden;
background-clip: border-box;
padding: 0.03rem 0.4rem;
margin: 0;
color: var(--secondary);
opacity: 0.85;
}
& > ul > li > .match-tag {
color: var(--tertiary);
font-weight: bold;
opacity: 1;
}
& > p {
margin-bottom: 0;
}

View File

@ -0,0 +1,59 @@
import { QuartzEmitterPlugin } from "../types"
import { QuartzComponentProps } from "../../components/types"
import BodyConstructor from "../../components/Body"
import { pageResources, renderPage } from "../../components/renderPage"
import { FullPageLayout } from "../../cfg"
import { FilePath, FullSlug } from "../../util/path"
import { sharedPageComponents } from "../../../quartz.layout"
import { NotFound } from "../../components"
import { defaultProcessedContent } from "../vfile"
export const NotFoundPage: QuartzEmitterPlugin = () => {
const opts: FullPageLayout = {
...sharedPageComponents,
pageBody: NotFound(),
beforeBody: [],
left: [],
right: [],
}
const { head: Head, pageBody, footer: Footer } = opts
const Body = BodyConstructor()
return {
name: "404Page",
getQuartzComponents() {
return [Head, Body, pageBody, Footer]
},
async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
const cfg = ctx.cfg.configuration
const slug = "404" as FullSlug
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
const path = url.pathname as FullSlug
const externalResources = pageResources(path, resources)
const [tree, vfile] = defaultProcessedContent({
slug,
text: "Not Found",
description: "Not Found",
frontmatter: { title: "Not Found", tags: [] },
})
const componentData: QuartzComponentProps = {
fileData: vfile.data,
externalResources,
cfg,
children: [],
tree,
allFiles: [],
}
return [
await emit({
content: renderPage(slug, componentData, opts, externalResources),
slug,
ext: ".html",
}),
]
},
}
}

View File

@ -1,4 +1,4 @@
import { FilePath, FullSlug, resolveRelative, simplifySlug } from "../../util/path"
import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import path from "path"
@ -12,15 +12,25 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({
for (const [_tree, file] of content) {
const ogSlug = simplifySlug(file.data.slug!)
const dir = path.posix.relative(argv.directory, file.dirname ?? argv.directory)
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
let aliases: FullSlug[] = file.data.frontmatter?.aliases ?? file.data.frontmatter?.alias ?? []
if (typeof aliases === "string") {
aliases = [aliases]
}
for (const alias of aliases) {
const slug = path.posix.join(dir, alias) as FullSlug
const slugs: FullSlug[] = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
const permalink = file.data.frontmatter?.permalink
if (typeof permalink === "string") {
slugs.push(permalink as FullSlug)
}
for (let slug of slugs) {
// fix any slugs that have trailing slash
if (slug.endsWith("/")) {
slug = joinSegments(slug, "index") as FullSlug
}
const redirUrl = resolveRelative(slug, file.data.slug!)
const fp = await emit({
content: `

View File

@ -7,7 +7,7 @@ import spaRouterScript from "../../components/scripts/spa.inline"
import plausibleScript from "../../components/scripts/plausible.inline"
// @ts-ignore
import popoverScript from "../../components/scripts/popover.inline"
import styles from "../../styles/base.scss"
import styles from "../../styles/custom.scss"
import popoverStyle from "../../components/styles/popover.scss"
import { BuildCtx } from "../../util/ctx"
import { StaticResources } from "../../util/resources"
@ -96,6 +96,15 @@ function addGlobalPageResources(
});`)
} else if (cfg.analytics?.provider === "plausible") {
componentResources.afterDOMLoaded.push(plausibleScript)
} else if (cfg.analytics?.provider === "umami") {
componentResources.afterDOMLoaded.push(`
const umamiScript = document.createElement("script")
umamiScript.src = "https://analytics.umami.is/script.js"
umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}")
umamiScript.async = true
document.head.appendChild(umamiScript)
`)
}
if (cfg.enableSPA) {
@ -107,12 +116,18 @@ function addGlobalPageResources(
document.dispatchEvent(event)`)
}
let wsUrl = `ws://localhost:${ctx.argv.wsPort}`
if (ctx.argv.remoteDevHost) {
wsUrl = `wss://${ctx.argv.remoteDevHost}:${ctx.argv.wsPort}`
}
if (reloadScript) {
staticResources.js.push({
loadTime: "afterDOMReady",
contentType: "inline",
script: `
const socket = new WebSocket('ws://localhost:3001')
const socket = new WebSocket('${wsUrl}')
socket.addEventListener('message', () => document.location.reload())
`,
})
@ -149,7 +164,7 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
addGlobalPageResources(ctx, resources, componentResources)
const stylesheet = joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css)
const stylesheet = joinStyles(ctx.cfg.configuration.theme, ...componentResources.css, styles)
const prescript = joinScripts(componentResources.beforeDOMLoaded)
const postscript = joinScripts(componentResources.afterDOMLoaded)
const fps = await Promise.all([

View File

@ -1,7 +1,10 @@
import { Root } from "hast"
import { GlobalConfiguration } from "../../cfg"
import { getDate } from "../../components/Date"
import { escapeHTML } from "../../util/escape"
import { FilePath, FullSlug, SimpleSlug, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import { toHtml } from "hast-util-to-html"
import path from "path"
export type ContentIndex = Map<FullSlug, ContentDetails>
@ -10,6 +13,7 @@ export type ContentDetails = {
links: SimpleSlug[]
tags: string[]
content: string
richContent?: string
date?: Date
description?: string
}
@ -17,19 +21,23 @@ export type ContentDetails = {
interface Options {
enableSiteMap: boolean
enableRSS: boolean
rssLimit?: number
rssFullHtml: boolean
includeEmptyFiles: boolean
}
const defaultOptions: Options = {
enableSiteMap: true,
enableRSS: true,
rssLimit: 10,
rssFullHtml: false,
includeEmptyFiles: true,
}
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
const base = cfg.baseUrl ?? ""
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
<loc>https://${base}/${slug}</loc>
<loc>https://${base}/${encodeURI(slug)}</loc>
<lastmod>${content.date?.toISOString()}</lastmod>
</url>`
const urls = Array.from(idx)
@ -38,27 +46,42 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`
}
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex): string {
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: number): string {
const base = cfg.baseUrl ?? ""
const root = `https://${base}`
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item>
<title>${content.title}</title>
<link>${root}/${slug}</link>
<guid>${root}/${slug}</guid>
<description>${content.description}</description>
<title>${escapeHTML(content.title)}</title>
<link>${root}/${encodeURI(slug)}</link>
<guid>${root}/${encodeURI(slug)}</guid>
<description>${content.richContent ?? content.description}</description>
<pubDate>${content.date?.toUTCString()}</pubDate>
</item>`
const items = Array.from(idx)
.sort(([_, f1], [__, f2]) => {
if (f1.date && f2.date) {
return f2.date.getTime() - f1.date.getTime()
} else if (f1.date && !f2.date) {
return -1
} else if (!f1.date && f2.date) {
return 1
}
return f1.title.localeCompare(f2.title)
})
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
.slice(0, limit ?? idx.size)
.join("")
return `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>${cfg.pageTitle}</title>
<title>${escapeHTML(cfg.pageTitle)}</title>
<link>${root}</link>
<description>Recent content on ${cfg.pageTitle}</description>
<description>${!!limit ? `Last ${limit} notes` : "Recent notes"} on ${escapeHTML(
cfg.pageTitle,
)}</description>
<generator>Quartz -- quartz.jzhao.xyz</generator>
${items}
</channel>
@ -73,7 +96,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
const cfg = ctx.cfg.configuration
const emitted: FilePath[] = []
const linkIndex: ContentIndex = new Map()
for (const [_tree, file] of content) {
for (const [tree, file] of content) {
const slug = file.data.slug!
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()
if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
@ -82,6 +105,9 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
links: file.data.links ?? [],
tags: file.data.frontmatter?.tags ?? [],
content: file.data.text ?? "",
richContent: opts?.rssFullHtml
? escapeHTML(toHtml(tree as Root, { allowDangerousHtml: true }))
: undefined,
date: date,
description: file.data.description ?? "",
})
@ -101,7 +127,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
if (opts?.enableRSS) {
emitted.push(
await emit({
content: generateRSSFeed(cfg, linkIndex),
content: generateRSSFeed(cfg, linkIndex, opts.rssLimit),
slug: "index" as FullSlug,
ext: ".xml",
}),

View File

@ -4,9 +4,10 @@ import HeaderConstructor from "../../components/Header"
import BodyConstructor from "../../components/Body"
import { pageResources, renderPage } from "../../components/renderPage"
import { FullPageLayout } from "../../cfg"
import { FilePath } from "../../util/path"
import { FilePath, pathToRoot } from "../../util/path"
import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { Content } from "../../components"
import chalk from "chalk"
export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
const opts: FullPageLayout = {
@ -29,9 +30,15 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
const cfg = ctx.cfg.configuration
const fps: FilePath[] = []
const allFiles = content.map((c) => c[1].data)
let containsIndex = false
for (const [tree, file] of content) {
const slug = file.data.slug!
const externalResources = pageResources(slug, resources)
if (slug === "index") {
containsIndex = true
}
const externalResources = pageResources(pathToRoot(slug), resources)
const componentData: QuartzComponentProps = {
fileData: file.data,
externalResources,
@ -50,6 +57,15 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
fps.push(fp)
}
if (!containsIndex) {
console.log(
chalk.yellow(
`\nWarning: you seem to be missing an \`index.md\` home page file at the root of your \`${ctx.argv.directory}\` folder. This may cause errors when deploying.`,
),
)
}
return fps
},
}

View File

@ -12,6 +12,7 @@ import {
SimpleSlug,
_stripSlashes,
joinSegments,
pathToRoot,
simplifySlug,
} from "../../util/path"
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
@ -69,7 +70,7 @@ export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
for (const folder of folders) {
const slug = joinSegments(folder, "index") as FullSlug
const externalResources = pageResources(slug, resources)
const externalResources = pageResources(pathToRoot(slug), resources)
const [tree, file] = folderDescriptions[folder]
const componentData: QuartzComponentProps = {
fileData: file.data,

View File

@ -6,3 +6,4 @@ export { AliasRedirects } from "./aliases"
export { Assets } from "./assets"
export { Static } from "./static"
export { ComponentResources } from "./componentResources"
export { NotFoundPage } from "./404"

View File

@ -5,7 +5,13 @@ import BodyConstructor from "../../components/Body"
import { pageResources, renderPage } from "../../components/renderPage"
import { ProcessedContent, defaultProcessedContent } from "../vfile"
import { FullPageLayout } from "../../cfg"
import { FilePath, FullSlug, getAllSegmentPrefixes, joinSegments } from "../../util/path"
import {
FilePath,
FullSlug,
getAllSegmentPrefixes,
joinSegments,
pathToRoot,
} from "../../util/path"
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { TagContent } from "../../components"
@ -62,7 +68,7 @@ export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
for (const tag of tags) {
const slug = joinSegments("tags", tag) as FullSlug
const externalResources = pageResources(slug, resources)
const externalResources = pageResources(pathToRoot(slug), resources)
const [tree, file] = tagDescriptions[tag]
const componentData: QuartzComponentProps = {
fileData: file.data,

View File

@ -1,6 +1,7 @@
import { Root as HTMLRoot } from "hast"
import { toString } from "hast-util-to-string"
import { QuartzTransformerPlugin } from "../types"
import { escapeHTML } from "../../util/escape"
export interface Options {
descriptionLength: number
@ -10,15 +11,6 @@ const defaultOptions: Options = {
descriptionLength: 150,
}
const escapeHTML = (unsafe: string) => {
return unsafe
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;")
}
export const Description: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts }
return {

View File

@ -2,14 +2,17 @@ import matter from "gray-matter"
import remarkFrontmatter from "remark-frontmatter"
import { QuartzTransformerPlugin } from "../types"
import yaml from "js-yaml"
import toml from "toml"
import { slugTag } from "../../util/path"
export interface Options {
delims: string | string[]
language: "yaml" | "toml"
}
const defaultOptions: Options = {
delims: "---",
language: "yaml",
}
export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
@ -18,13 +21,14 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
name: "FrontMatter",
markdownPlugins() {
return [
remarkFrontmatter,
[remarkFrontmatter, ["yaml", "toml"]],
() => {
return (_, file) => {
const { data } = matter(file.value, {
...opts,
engines: {
yaml: (s) => yaml.load(s, { schema: yaml.JSON_SCHEMA }) as object,
toml: (s) => toml.parse(s) as object,
},
})
@ -33,6 +37,11 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
data.tags = data.tag
}
// coerce title to string
if (data.title) {
data.title = data.title.toString()
}
if (data.tags && !Array.isArray(data.tags)) {
data.tags = data.tags
.toString()

View File

@ -5,5 +5,7 @@ export { Latex } from "./latex"
export { Description } from "./description"
export { CrawlLinks } from "./links"
export { ObsidianFlavoredMarkdown } from "./ofm"
export { OxHugoFlavouredMarkdown } from "./oxhugofm"
export { SyntaxHighlighting } from "./syntax"
export { TableOfContents } from "./toc"
export { HardLineBreaks } from "./linebreaks"

View File

@ -2,6 +2,7 @@ import fs from "fs"
import path from "path"
import { Repository } from "@napi-rs/simple-git"
import { QuartzTransformerPlugin } from "../types"
import chalk from "chalk"
export interface Options {
priority: ("frontmatter" | "git" | "filesystem")[]
@ -11,6 +12,20 @@ const defaultOptions: Options = {
priority: ["frontmatter", "git", "filesystem"],
}
function coerceDate(fp: string, d: any): Date {
const dt = new Date(d)
const invalidDate = isNaN(dt.getTime()) || dt.getTime() === 0
if (invalidDate && d !== undefined) {
console.log(
chalk.yellow(
`\nWarning: found invalid date "${d}" in \`${fp}\`. Supported formats: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format`,
),
)
}
return invalidDate ? new Date() : dt
}
type MaybeDate = undefined | string | number
export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | undefined> = (
userOpts,
@ -27,10 +42,11 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
let modified: MaybeDate = undefined
let published: MaybeDate = undefined
const fp = path.posix.join(file.cwd, file.data.filePath as string)
const fp = file.data.filePath!
const fullFp = path.posix.join(file.cwd, fp)
for (const source of opts.priority) {
if (source === "filesystem") {
const st = await fs.promises.stat(fp)
const st = await fs.promises.stat(fullFp)
created ||= st.birthtimeMs
modified ||= st.mtimeMs
} else if (source === "frontmatter" && file.data.frontmatter) {
@ -49,9 +65,9 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
}
file.data.dates = {
created: created ? new Date(created) : new Date(),
modified: modified ? new Date(modified) : new Date(),
published: published ? new Date(published) : new Date(),
created: coerceDate(fp, created),
modified: coerceDate(fp, modified),
published: coerceDate(fp, published),
}
}
},

View File

@ -0,0 +1,11 @@
import { QuartzTransformerPlugin } from "../types"
import remarkBreaks from "remark-breaks"
export const HardLineBreaks: QuartzTransformerPlugin = () => {
return {
name: "HardLineBreaks",
markdownPlugins() {
return [remarkBreaks]
},
}
}

View File

@ -5,7 +5,6 @@ import {
SimpleSlug,
TransformOptions,
_stripSlashes,
joinSegments,
simplifySlug,
splitAnchor,
transformLink,
@ -19,11 +18,13 @@ interface Options {
markdownLinkResolution: TransformOptions["strategy"]
/** Strips folders from a link so that it looks nice */
prettyLinks: boolean
openLinksInNewTab: boolean
}
const defaultOptions: Options = {
markdownLinkResolution: "absolute",
prettyLinks: true,
openLinksInNewTab: false,
}
export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
@ -53,8 +54,13 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
node.properties.className ??= []
node.properties.className.push(isAbsoluteUrl(dest) ? "external" : "internal")
if (opts.openLinksInNewTab) {
node.properties.target = "_blank"
}
// don't process external links or intra-document anchors
if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
const isInternal = !(isAbsoluteUrl(dest) || dest.startsWith("#"))
if (isInternal) {
dest = node.properties.href = transformLink(
file.data.slug!,
dest,
@ -72,11 +78,13 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
simplifySlug(destCanonical as FullSlug),
) as SimpleSlug
outgoing.add(simple)
node.properties["data-slug"] = simple
}
// rewrite link internals if prettylinks is on
if (
opts.prettyLinks &&
isInternal &&
node.children.length === 1 &&
node.children[0].type === "text" &&
!node.children[0].value.startsWith("#")

View File

@ -1,6 +1,7 @@
import { PluggableList } from "unified"
import { QuartzTransformerPlugin } from "../types"
import { Root, HTML, BlockContent, DefinitionContent, Code, Paragraph } from "mdast"
import { Element, Literal, Root as HtmlRoot } from "hast"
import { Replace, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
import { slug as slugAnchor } from "github-slugger"
import rehypeRaw from "rehype-raw"
@ -13,6 +14,7 @@ import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html"
import { PhrasingContent } from "mdast-util-find-and-replace/lib"
import { capitalize } from "../../util/lang"
export interface Options {
comments: boolean
@ -21,6 +23,7 @@ export interface Options {
callouts: boolean
mermaid: boolean
parseTags: boolean
parseBlockReferences: boolean
enableInHtmlEmbed: boolean
}
@ -31,6 +34,7 @@ const defaultOptions: Options = {
callouts: true,
mermaid: true,
parseTags: true,
parseBlockReferences: true,
enableInHtmlEmbed: false,
}
@ -69,6 +73,8 @@ const callouts = {
const calloutMapping: Record<string, keyof typeof callouts> = {
note: "note",
abstract: "abstract",
summary: "abstract",
tldr: "abstract",
info: "info",
todo: "todo",
tip: "tip",
@ -96,11 +102,7 @@ const calloutMapping: Record<string, keyof typeof callouts> = {
function canonicalizeCallout(calloutName: string): keyof typeof callouts {
let callout = calloutName.toLowerCase() as keyof typeof calloutMapping
return calloutMapping[callout] ?? calloutName
}
const capitalize = (s: string): string => {
return s.substring(0, 1).toUpperCase() + s.substring(1)
return calloutMapping[callout] ?? "note"
}
// !? -> optional embedding
@ -109,14 +111,17 @@ const capitalize = (s: string): string => {
// (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
// (|[^\[\]\|\#]+)? -> | then one or more non-special characters (alias)
const wikilinkRegex = new RegExp(/!?\[\[([^\[\]\|\#]+)?(#[^\[\]\|\#]+)?(\|[^\[\]\|\#]+)?\]\]/, "g")
const highlightRegex = new RegExp(/==(.+)==/, "g")
const highlightRegex = new RegExp(/==([^=]+)==/, "g")
const commentRegex = new RegExp(/%%(.+)%%/, "g")
// from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
const calloutRegex = new RegExp(/^\[\!(\w+)\]([+-]?)/)
const calloutLineRegex = new RegExp(/^> *\[\!\w+\][+-]?.*$/, "gm")
// (?:^| ) -> non-capturing group, tag should start be separated by a space or be the start of the line
// #(\w+) -> tag itself is # followed by a string of alpha-numeric characters
const tagRegex = new RegExp(/(?:^| )#(\p{L}+)/, "gu")
// #(...) -> capturing group, tag itself must start with #
// (?:[-_\p{L}])+ -> non-capturing group, non-empty string of (Unicode-aware) alpha-numeric characters, hyphens and/or underscores
// (?:\/[-_\p{L}]+)*) -> non-capturing group, matches an arbitrary number of tag strings separated by "/"
const tagRegex = new RegExp(/(?:^| )#((?:[-_\p{L}\d])+(?:\/[-_\p{L}\d]+)*)/, "gu")
const blockReferenceRegex = new RegExp(/\^([A-Za-z0-9]+)$/, "g")
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (
userOpts,
@ -230,8 +235,16 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
value: `<iframe src="${url}"></iframe>`,
}
} else if (ext === "") {
// TODO: note embed
const block = anchor
return {
type: "html",
data: { hProperties: { transclude: true } },
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${
url + anchor
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
}
}
// otherwise, fall through to regular link
}
@ -383,13 +396,18 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
return (tree: Root, file) => {
const base = pathToRoot(file.data.slug!)
findAndReplace(tree, tagRegex, (_value: string, tag: string) => {
// Check if the tag only includes numbers
if (/^\d+$/.test(tag)) {
return false
}
tag = slugTag(tag)
if (file.data.frontmatter && !file.data.frontmatter.tags.includes(tag)) {
file.data.frontmatter.tags.push(tag)
}
return {
type: "link",
url: base + `/tags/${slugTag(tag)}`,
url: base + `/tags/${tag}`,
data: {
hProperties: {
className: ["tag-link"],
@ -406,11 +424,64 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
}
})
}
return plugins
},
htmlPlugins() {
return [rehypeRaw]
const plugins = [rehypeRaw]
if (opts.parseBlockReferences) {
plugins.push(() => {
const inlineTagTypes = new Set(["p", "li"])
const blockTagTypes = new Set(["blockquote"])
return (tree, file) => {
file.data.blocks = {}
file.data.htmlAst = tree
visit(tree, "element", (node, index, parent) => {
if (blockTagTypes.has(node.tagName)) {
const nextChild = parent?.children.at(index! + 2) as Element
if (nextChild && nextChild.tagName === "p") {
const text = nextChild.children.at(0) as Literal
if (text && text.value && text.type === "text") {
const matches = text.value.match(blockReferenceRegex)
if (matches && matches.length >= 1) {
parent!.children.splice(index! + 2, 1)
const block = matches[0].slice(1)
if (!Object.keys(file.data.blocks!).includes(block)) {
node.properties = {
...node.properties,
id: block,
}
file.data.blocks![block] = node
}
}
}
}
} else if (inlineTagTypes.has(node.tagName)) {
const last = node.children.at(-1) as Literal
if (last && last.value && typeof last.value === "string") {
const matches = last.value.match(blockReferenceRegex)
if (matches && matches.length >= 1) {
last.value = last.value.slice(0, -matches[0].length)
const block = matches[0].slice(1)
if (!Object.keys(file.data.blocks!).includes(block)) {
node.properties = {
...node.properties,
id: block,
}
file.data.blocks![block] = node
}
}
}
}
})
}
})
}
return plugins
},
externalResources() {
const js: JSResource[] = []
@ -449,3 +520,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
},
}
}
declare module "vfile" {
interface DataMap {
blocks: Record<string, Element>
htmlAst: HtmlRoot
}
}

View File

@ -0,0 +1,108 @@
import { QuartzTransformerPlugin } from "../types"
export interface Options {
/** Replace {{ relref }} with quartz wikilinks []() */
wikilinks: boolean
/** Remove pre-defined anchor (see https://ox-hugo.scripter.co/doc/anchors/) */
removePredefinedAnchor: boolean
/** Remove hugo shortcode syntax */
removeHugoShortcode: boolean
/** Replace <figure/> with ![]() */
replaceFigureWithMdImg: boolean
/** Replace org latex fragments with $ and $$ */
replaceOrgLatex: boolean
}
const defaultOptions: Options = {
wikilinks: true,
removePredefinedAnchor: true,
removeHugoShortcode: true,
replaceFigureWithMdImg: true,
replaceOrgLatex: true,
}
const relrefRegex = new RegExp(/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/, "g")
const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
const figureTagRegex = new RegExp(/< ?figure src="(.*)" ?>/, "g")
// \\\\\( -> matches \\(
// (.+?) -> Lazy match for capturing the equation
// \\\\\) -> matches \\)
const inlineLatexRegex = new RegExp(/\\\\\((.+?)\\\\\)/, "g")
// (?:\\begin{equation}|\\\\\(|\\\\\[) -> start of equation
// ([\s\S]*?) -> Matches the block equation
// (?:\\\\\]|\\\\\)|\\end{equation}) -> end of equation
const blockLatexRegex = new RegExp(
/(?:\\begin{equation}|\\\\\(|\\\\\[)([\s\S]*?)(?:\\\\\]|\\\\\)|\\end{equation})/,
"g",
)
// \$\$[\s\S]*?\$\$ -> Matches block equations
// \$.*?\$ -> Matches inline equations
const quartzLatexRegex = new RegExp(/\$\$[\s\S]*?\$\$|\$.*?\$/, "g")
/**
* ox-hugo is an org exporter backend that exports org files to hugo-compatible
* markdown in an opinionated way. This plugin adds some tweaks to the generated
* markdown to make it compatible with quartz but the list of changes applied it
* is not exhaustive.
* */
export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (
userOpts,
) => {
const opts = { ...defaultOptions, ...userOpts }
return {
name: "OxHugoFlavouredMarkdown",
textTransform(_ctx, src) {
if (opts.wikilinks) {
src = src.toString()
src = src.replaceAll(relrefRegex, (value, ...capture) => {
const [text, link] = capture
return `[${text}](${link})`
})
}
if (opts.removePredefinedAnchor) {
src = src.toString()
src = src.replaceAll(predefinedHeadingIdRegex, (value, ...capture) => {
const [headingText] = capture
return headingText
})
}
if (opts.removeHugoShortcode) {
src = src.toString()
src = src.replaceAll(hugoShortcodeRegex, (value, ...capture) => {
const [scContent] = capture
return scContent
})
}
if (opts.replaceFigureWithMdImg) {
src = src.toString()
src = src.replaceAll(figureTagRegex, (value, ...capture) => {
const [src] = capture
return `![](${src})`
})
}
if (opts.replaceOrgLatex) {
src = src.toString()
src = src.replaceAll(inlineLatexRegex, (value, ...capture) => {
const [eqn] = capture
return `$${eqn}$`
})
src = src.replaceAll(blockLatexRegex, (value, ...capture) => {
const [eqn] = capture
return `$$${eqn}$$`
})
// ox-hugo escapes _ as \_
src = src.replaceAll(quartzLatexRegex, (value) => {
return value.replaceAll("\\_", "_")
})
}
return src
},
}
}

View File

@ -8,12 +8,14 @@ export interface Options {
maxDepth: 1 | 2 | 3 | 4 | 5 | 6
minEntries: 1
showByDefault: boolean
collapseByDefault: boolean
}
const defaultOptions: Options = {
maxDepth: 3,
minEntries: 1,
showByDefault: true,
collapseByDefault: false,
}
interface TocEntry {
@ -54,6 +56,7 @@ export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefin
...entry,
depth: entry.depth - highestDepth,
}))
file.data.collapseToc = opts.collapseByDefault
}
}
}
@ -66,5 +69,6 @@ export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefin
declare module "vfile" {
interface DataMap {
toc: TocEntry[]
collapseToc: boolean
}
}

View File

@ -1,7 +1,6 @@
@use "./custom.scss";
@use "./variables.scss" as *;
@use "./syntax.scss";
@use "./callouts.scss";
@use "./variables.scss" as *;
html {
scroll-behavior: smooth;
@ -65,7 +64,7 @@ a {
color: var(--tertiary) !important;
}
&.internal {
&.internal:not(:has(> img)) {
text-decoration: none;
background-color: var(--highlight);
padding: 0 0.1rem;
@ -95,6 +94,8 @@ a {
}
& article {
position: relative;
& > h1 {
font-size: 2rem;
}
@ -389,23 +390,33 @@ p {
line-height: 1.6rem;
}
table {
.table-container {
overflow-x: auto;
& > table {
margin: 1rem;
padding: 1.5rem;
border-collapse: collapse;
th,
td {
min-width: 75px;
}
& > * {
line-height: 2rem;
}
}
}
th {
text-align: left;
padding: 0.4rem 1rem;
padding: 0.4rem 0.7rem;
border-bottom: 2px solid var(--gray);
}
td {
padding: 0.2rem 1rem;
padding: 0.2rem 0.7rem;
}
tr {
@ -446,7 +457,7 @@ video {
ul.overflow,
ol.overflow {
height: 300px;
max-height: 400;
overflow-y: auto;
// clearfix
@ -454,7 +465,7 @@ ol.overflow {
clear: both;
& > li:last-of-type {
margin-bottom: 50px;
margin-bottom: 30px;
}
&:after {
@ -470,3 +481,9 @@ ol.overflow {
background: linear-gradient(transparent 0px, var(--light));
}
}
.transclude {
ul {
padding-left: 1rem;
}
}

View File

@ -82,7 +82,6 @@
.callout-title {
display: flex;
align-items: center;
gap: 5px;
padding: 1rem 0;
color: var(--color);
@ -103,6 +102,8 @@
.callout-icon {
width: 18px;
height: 18px;
flex: 0 0 18px;
padding-top: 4px;
}
.callout-title-inner {

View File

@ -7,6 +7,8 @@ export interface Argv {
output: string
serve: boolean
port: number
wsPort: number
remoteDevHost?: string
concurrency?: number
}

8
quartz/util/escape.ts Normal file
View File

@ -0,0 +1,8 @@
export const escapeHTML = (unsafe: string) => {
return unsafe
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;")
}

28
quartz/util/jsx.tsx Normal file
View File

@ -0,0 +1,28 @@
import { Components, Jsx, toJsxRuntime } from "hast-util-to-jsx-runtime"
import { QuartzPluginData } from "../plugins/vfile"
import { Node, Root } from "hast"
import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
import { trace } from "./trace"
import { type FilePath } from "./path"
const customComponents: Components = {
table: (props) => (
<div class="table-container">
<table {...props} />
</div>
),
}
export function htmlToJsx(fp: FilePath, tree: Node<QuartzPluginData>) {
try {
return toJsxRuntime(tree as Root, {
Fragment,
jsx: jsx as Jsx,
jsxs: jsxs as Jsx,
elementAttributeNameCase: "html",
components: customComponents,
})
} catch (e) {
trace(`Failed to parse Markdown in \`${fp}\` into JSX`, e as Error)
}
}

11
quartz/util/lang.ts Normal file
View File

@ -0,0 +1,11 @@
export function pluralize(count: number, s: string): string {
if (count === 1) {
return `1 ${s}`
} else {
return `${count} ${s}s`
}
}
export function capitalize(s: string): string {
return s.substring(0, 1).toUpperCase() + s.substring(1)
}

View File

@ -52,7 +52,7 @@ export function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug {
let slug = withoutFileExt
.split("/")
.map((segment) => segment.replace(/\s/g, "-").replace(/%/g, "-percent")) // slugify all segments
.map((segment) => segment.replace(/\s/g, "-").replace(/%/g, "-percent").replace(/\?/g, "-q")) // slugify all segments
.join("/") // always use / as sep
.replace(/\/$/, "") // remove trailing slash
@ -123,7 +123,10 @@ export function slugTag(tag: string) {
}
export function joinSegments(...args: string[]): string {
return args.filter((segment) => segment !== "").join("/")
return args
.filter((segment) => segment !== "")
.join("/")
.replace(/\/\/+/g, "/")
}
export function getAllSegmentPrefixes(tags: string): string[] {

View File

@ -4,7 +4,7 @@ import { isMainThread } from "workerpool"
const rootFile = /.*at file:/
export function trace(msg: string, err: Error) {
const stack = err.stack
let stack = err.stack ?? ""
const lines: string[] = []
@ -12,15 +12,11 @@ export function trace(msg: string, err: Error) {
lines.push(
"\n" +
chalk.bgRed.black.bold(" ERROR ") +
"\n" +
"\n\n" +
chalk.red(` ${msg}`) +
(err.message.length > 0 ? `: ${err.message}` : ""),
)
if (!stack) {
return
}
let reachedEndOfLegibleTrace = false
for (const line of stack.split("\n").slice(1)) {
if (reachedEndOfLegibleTrace) {