Compare commits

..

13 Commits

Author SHA1 Message Date
ada106a499 Remove obsolete Tokyo Night theme import 2026-07-13 10:02:27 +01:00
6bb9c584ef changes 2026-07-13 09:53:19 +01:00
dd132a8778 fix 2026-06-02 11:44:08 +01:00
saberzero1
afe98e6b1a fix: excalidraw panning on mobile and .jxl support
Some checks failed
Build and Test / build-and-test (macos-latest) (push) Has been cancelled
Build and Test / build-and-test (ubuntu-latest) (push) Has been cancelled
Build and Test / build-and-test (windows-latest) (push) Has been cancelled
Build and Test / publish-tag (push) Has been cancelled
Deploy v5 Preview / Deploy v5 to Cloudflare Pages (push) Has been cancelled
Docker build & push image / build (push) Has been cancelled
2026-05-27 20:56:47 +02:00
saberzero1
f15aa0032a fix: allow updating when repository is generated from template 2026-05-27 20:43:56 +02:00
saberzero1
9e4d4518bd fix: anchor links without alias follow obsidian reading mode 2026-05-27 00:33:38 +02:00
saberzero1
3138a88bb1 chore: updated layouts to include table of contents 2026-05-27 00:18:06 +02:00
saberzero1
db5aca2b8d fix: apply default position when installing plugins 2026-05-27 00:17:45 +02:00
saberzero1
dd1b36c148 fix: recent notes default date type 2026-05-26 22:26:21 +02:00
saberzero1
4579e49465 test: regression tests for junctions 2026-05-26 21:15:36 +02:00
saberzero1
882d3b3f50 fix: fall back to junctions on windows 2026-05-26 21:15:20 +02:00
saberzero1
667d487a0f fix: re-render graph on mode change 2026-05-26 19:47:54 +02:00
saberzero1
36b3542371 fix: block link casing 2026-05-26 18:16:22 +02:00
25 changed files with 21848 additions and 423 deletions

View File

@@ -1,54 +0,0 @@
name: Build Preview Deployment
on:
pull_request:
types: [opened, synchronize]
workflow_dispatch:
jobs:
build-preview:
if: ${{ github.repository == 'jackyzha0/quartz' }}
runs-on: ubuntu-latest
name: Build Preview
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 22
- name: Cache dependencies
uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache Quartz plugins
uses: actions/cache@v5
with:
path: .quartz/plugins
key: ${{ runner.os }}-plugins-${{ hashFiles('quartz.lock.json') }}
restore-keys: |
${{ runner.os }}-plugins-
- run: npm ci
- name: Install Quartz plugins
run: npx quartz plugin install
- name: Check types and style
run: npm run check
- name: Build Quartz
run: npx quartz build -d docs -v
- name: Upload build artifact
uses: actions/upload-artifact@v6
with:
name: preview-build
path: public

View File

@@ -1,83 +0,0 @@
name: Build and Test
on:
pull_request:
branches:
- v5
push:
branches:
- v5
workflow_dispatch:
jobs:
build-and-test:
if: ${{ github.repository == 'jackyzha0/quartz' }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
- name: Cache dependencies
uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache Quartz plugins
uses: actions/cache@v5
with:
path: .quartz/plugins
key: ${{ runner.os }}-plugins-${{ hashFiles('quartz.lock.json') }}
restore-keys: |
${{ runner.os }}-plugins-
- run: npm ci
- name: Install Quartz plugins
run: npx quartz plugin install
- name: Check types and style
run: npm run check
- name: Test
run: npm test
- name: Ensure Quartz builds, check bundle info
run: npx quartz build --bundleInfo -d docs
publish-tag:
if: ${{ github.repository == 'jackyzha0/quartz' && github.ref == 'refs/heads/v5' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
- name: Get package version
run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
- name: Create release tag
uses: pkgdeps/git-tag-action@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_repo: ${{ github.repository }}
version: ${{ env.PACKAGE_VERSION }}
git_commit_sha: ${{ github.sha }}
git_tag_prefix: "v"

View File

@@ -1,37 +0,0 @@
name: Upload Preview Deployment
on:
workflow_run:
workflows: ["Build Preview Deployment"]
types:
- completed
permissions:
actions: read
deployments: write
contents: read
pull-requests: write
jobs:
deploy-preview:
if: ${{ github.repository == 'jackyzha0/quartz' && github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
name: Deploy Preview to Cloudflare Pages
steps:
- name: Download build artifact
uses: actions/download-artifact@v7
id: preview-build-artifact
with:
name: preview-build
path: build
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Deploy to Cloudflare Pages
uses: AdrianGonz97/refined-cf-pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
githubToken: ${{ secrets.GITHUB_TOKEN }}
projectName: quartz
deploymentName: Branch Preview
directory: ${{ steps.preview-build-artifact.outputs.download-path }}

View File

@@ -1,64 +0,0 @@
name: Deploy v5 Preview
on:
push:
branches:
- v5
workflow_dispatch:
permissions:
contents: read
deployments: write
jobs:
deploy-v5:
if: ${{ github.repository == 'jackyzha0/quartz' }}
runs-on: ubuntu-latest
name: Deploy v5 to Cloudflare Pages
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
- name: Cache dependencies
uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache Quartz plugins
uses: actions/cache@v5
with:
path: .quartz/plugins
key: ${{ runner.os }}-plugins-${{ hashFiles('quartz.lock.json') }}
restore-keys: |
${{ runner.os }}-plugins-
- run: npm ci
- name: Install Quartz plugins
run: npx quartz plugin install
- name: Check types and style
run: npm run check
- name: Build Quartz
run: npx quartz build -d docs -v
- name: Deploy to Cloudflare Pages
uses: AdrianGonz97/refined-cf-pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
githubToken: ${{ secrets.GITHUB_TOKEN }}
projectName: quartz
deploymentName: v5 Branch Preview
branch: v5
directory: public

View File

@@ -1,88 +0,0 @@
name: Docker build & push image
on:
push:
branches: [v5]
tags: ["v*"]
pull_request:
branches: [v5]
paths:
- .github/workflows/docker-build-push.yaml
- quartz/**
workflow_dispatch:
jobs:
build:
if: ${{ github.repository == 'jackyzha0/quartz' }} # Comment this out if you want to publish your own images on a fork!
runs-on: ubuntu-latest
steps:
- name: Set lowercase repository owner environment variable
run: |
echo "OWNER_LOWERCASE=${OWNER,,}" >> ${GITHUB_ENV}
env:
OWNER: "${{ github.repository_owner }}"
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v5.4.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
driver-opts: |
image=moby/buildkit:master
network=host
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v4.0.0
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata tags and labels on PRs
if: github.event_name == 'pull_request'
id: meta-pr
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ env.OWNER_LOWERCASE }}/quartz
tags: |
type=raw,value=sha-${{ env.GITHUB_SHA_SHORT }}
labels: |
org.opencontainers.image.source="https://github.com/${{ github.repository_owner }}/quartz"
- name: Extract metadata tags and labels for main, release or tag
if: github.event_name != 'pull_request'
id: meta
uses: docker/metadata-action@v5
with:
flavor: |
latest=auto
images: ghcr.io/${{ env.OWNER_LOWERCASE }}/quartz
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
type=raw,value=sha-${{ env.GITHUB_SHA_SHORT }}
labels: |
maintainer=${{ github.repository_owner }}
org.opencontainers.image.source="https://github.com/${{ github.repository_owner }}/quartz"
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
push: ${{ github.event_name != 'pull_request' }}
build-args: |
GIT_SHA=${{ env.GITHUB_SHA }}
DOCKER_LABEL=sha-${{ env.GITHUB_SHA_SHORT }}
tags: ${{ steps.meta.outputs.tags || steps.meta-pr.outputs.tags }}
labels: ${{ steps.meta.outputs.labels || steps.meta-pr.outputs.labels }}
cache-from: type=gha
cache-to: type=gha

View File

@@ -1,17 +0,0 @@
# Quartz v5
> “[One] who works with the door open gets all kinds of interruptions, but [they] also occasionally gets clues as to what the world is and what might be important.” — Richard Hamming
Quartz is a set of tools that helps you publish your [digital garden](https://jzhao.xyz/posts/networked-thought) and notes as a website for free.
🔗 Read the documentation and get started: https://quartz.jzhao.xyz/
[Join the Discord Community](https://discord.gg/cRFFHYye7t)
## Sponsors
<p align="center">
<a href="https://github.com/sponsors/jackyzha0">
<img src="https://cdn.jsdelivr.net/gh/jackyzha0/jackyzha0/sponsorkit/sponsors.svg" />
</a>
</p>

1
content Symbolic link
View File

@@ -0,0 +1 @@
/home/zaine/master-folder/vault-master

View File

2
package-lock.json generated
View File

@@ -1857,7 +1857,7 @@
},
"node_modules/@quartz-community/utils": {
"version": "0.1.0",
"resolved": "git+ssh://git@github.com/quartz-community/utils.git#7a43501ac1c728e8a7bfa75db35e8791bfa1f41c",
"resolved": "git+ssh://git@github.com/quartz-community/utils.git#e09d8b0bbccb46073b23c00affaf3e40a2c3b0c4",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -70,6 +70,9 @@ plugins:
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
@@ -142,7 +145,7 @@ plugins:
enabled: true
layout:
position: right
priority: 30
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:

308
quartz.config.yaml Normal file
View File

@@ -0,0 +1,308 @@
# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json
configuration:
pageTitle: Z Notes
pageTitleSuffix: ""
enableSPA: true
enablePopovers: true
analytics:
provider: plausible
locale: en-US
baseUrl: notes.zainezq.com
ignorePatterns:
- private
- templates
- .obsidian
theme:
fontOrigin: googleFonts
cdnCaching: true
typography:
title:
name: Cormorant Garamond
weights:
- 600
- 700
includeItalic: true
header:
name: Cormorant Garamond
weights:
- 500
- 600
- 700
includeItalic: true
body:
name: Source Serif 4
weights:
- 400
- 600
- 700
includeItalic: true
code: IBM Plex Mono
colors:
lightMode:
light: "#f4eedf"
lightgray: "#d8ccb3"
gray: "#a79575"
darkgray: "#43382c"
dark: "#241c16"
secondary: "#7a2632"
tertiary: "#365c48"
highlight: "rgba(171, 137, 73, 0.18)"
textHighlight: "#d7b95f66"
darkMode:
light: "#1b1713"
lightgray: "#393026"
gray: "#74634d"
darkgray: "#d8cbb3"
dark: "#f0e5cf"
secondary: "#c59a59"
tertiary: "#78947b"
highlight: "rgba(197, 154, 89, 0.17)"
textHighlight: "#936f3466"
plugins:
- source: github:quartz-community/created-modified-date
enabled: true
options:
defaultDateType: modified
priority:
- frontmatter
- git
- filesystem
order: 10
- source: github:quartz-community/syntax-highlighting
enabled: true
options:
theme:
light: github-light
dark: github-dark
keepBackground: false
order: 20
- source: github:quartz-community/obsidian-flavored-markdown
enabled: true
options:
comments: true
highlight: true
wikilinks: true
callouts: true
mermaid: true
parseTags: true
parseArrows: true
parseBlockReferences: true
enableInHtmlEmbed: false
enableYouTubeEmbed: true
enableVideoEmbed: true
enableCheckbox: true
order: 30
- source: github:quartz-community/github-flavored-markdown
enabled: true
order: 40
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
markdownLinkResolution: shortest
order: 60
- source: github:quartz-community/description
enabled: true
order: 70
- source: github:quartz-community/latex
enabled: true
options:
renderEngine: katex
order: 80
- source: github:quartz-community/citations
enabled: false
order: 85
- source: github:quartz-community/hard-line-breaks
enabled: true
order: 90
- source: github:quartz-community/ox-hugo
enabled: false
order: 91
- source: github:quartz-community/roam
enabled: false
order: 92
- source: github:quartz-community/remove-draft
enabled: true
- source: github:quartz-community/explicit-publish
enabled: false
- source: github:quartz-community/unlisted-pages
enabled: true
options: {}
order: 45
- source: github:quartz-community/encrypted-pages
enabled: true
options:
iterations: 600000
passwordField: password
unlistWhenEncrypted: false
outputPath: static/encryptedContentIndex.json
- source: github:quartz-community/stacked-pages
enabled: false
layout:
position: afterBody
priority: 50
display: all
- source: github:quartz-community/alias-redirects
enabled: true
- source: github:quartz-community/content-index
enabled: true
options:
enableSiteMap: true
enableRSS: true
- source: github:quartz-community/favicon
enabled: true
- source: github:quartz-community/og-image
enabled: true
- source: github:quartz-community/cname
enabled: true
- source: github:quartz-community/canvas-page
enabled: true
- source: github:quartz-community/content-page
enabled: true
- source: github:quartz-community/folder-page
enabled: true
- source: github:quartz-community/tag-page
enabled: true
- source: github:quartz-community/explorer
enabled: true
layout:
position: left
priority: 50
- source: github:quartz-community/graph
enabled: true
layout:
position: right
priority: 10
- source: github:quartz-community/search
enabled: true
layout:
position: left
priority: 20
group: toolbar
groupOptions:
grow: true
- source: github:quartz-community/backlinks
enabled: true
layout:
position: right
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:
position: beforeBody
priority: 10
- source: github:quartz-community/content-meta
enabled: true
layout:
position: beforeBody
priority: 20
- source: github:quartz-community/tag-list
enabled: false
layout:
position: beforeBody
priority: 30
- source: github:quartz-community/page-title
enabled: true
layout:
position: left
priority: 10
- source: github:quartz-community/darkmode
enabled: true
layout:
position: left
priority: 30
group: toolbar
- source: github:quartz-community/reader-mode
enabled: true
layout:
position: left
priority: 35
group: toolbar
- source: github:quartz-community/breadcrumbs
enabled: true
layout:
position: beforeBody
priority: 5
condition: not-index
- source: github:quartz-community/comments
enabled: false
options:
provider: giscus
options: {}
layout:
position: afterBody
priority: 10
- source: github:quartz-community/footer
enabled: false
options:
links:
GitHub: https://github.com/jackyzha0/quartz
Discord Community: https://discord.gg/cRFFHYye7t
- source: github:quartz-community/recent-notes
enabled: false
- source: github:quartz-community/spacer
enabled: true
options: {}
order: 25
layout:
position: left
priority: 25
display: mobile-only
- source: github:quartz-community/bases-page
enabled: true
options: {}
order: 50
- source: github:quartz-community/note-properties
enabled: true
options:
includeAll: false
includedProperties:
- description
- tags
- aliases
excludedProperties: []
hidePropertiesView: false
delimiters: ---
language: yaml
order: 5
layout:
position: beforeBody
priority: 15
display: all
- source: github:quartz-community/obsidian-plugin-excalidraw
enabled: true
options:
enableInteraction: true
darkMode: auto
exportPadding: 20
order: 50
layout:
groups:
toolbar:
priority: 35
direction: row
gap: 0.5rem
byPageType:
"404":
positions:
beforeBody: []
left: []
right: []
content: {}
folder:
exclude:
- reader-mode
positions:
right: []
tag:
exclude:
- reader-mode
positions:
right: []
canvas: {}
bases: {}

View File

@@ -5,199 +5,193 @@
"source": "github:quartz-community/alias-redirects",
"resolved": "https://github.com/quartz-community/alias-redirects.git",
"commit": "c46bc82fb9c93612902de6ab6880d28d7789b471",
"installedAt": "2026-05-24T15:36:16.431Z"
"installedAt": "2026-06-02T10:09:02.257Z"
},
"article-title": {
"source": "github:quartz-community/article-title",
"resolved": "https://github.com/quartz-community/article-title.git",
"commit": "5c5f7c702c4f56c95cee881d5440ec21a8970ba8",
"installedAt": "2026-05-24T15:36:16.464Z"
"installedAt": "2026-06-02T10:09:05.862Z"
},
"backlinks": {
"source": "github:quartz-community/backlinks",
"resolved": "https://github.com/quartz-community/backlinks.git",
"commit": "51c8954108ec68a0293dc7924af703ae59c78f5a",
"installedAt": "2026-05-24T15:36:16.408Z"
"installedAt": "2026-06-02T10:09:05.496Z"
},
"bases-page": {
"source": "github:quartz-community/bases-page",
"resolved": "https://github.com/quartz-community/bases-page.git",
"commit": "077ebacd657a86bc92adc4ec392a238cacbfc298",
"installedAt": "2026-05-24T19:26:20.456Z"
"commit": "ec5711f91e40b1e8477a101f6e2604926e4f0222",
"installedAt": "2026-06-02T10:09:07.857Z"
},
"breadcrumbs": {
"source": "github:quartz-community/breadcrumbs",
"resolved": "https://github.com/quartz-community/breadcrumbs.git",
"commit": "2f7ec9a7ef80ad5128d18205548f1132a2cf150a",
"installedAt": "2026-05-24T15:36:16.467Z"
"installedAt": "2026-06-02T10:09:06.591Z"
},
"canvas-page": {
"source": "github:quartz-community/canvas-page",
"resolved": "https://github.com/quartz-community/canvas-page.git",
"commit": "2e6d05ce087cf9b6de4dc9fa72590a3047170daa",
"installedAt": "2026-05-22T23:14:25.465Z"
"installedAt": "2026-06-02T10:09:04.070Z"
},
"citations": {
"source": "github:quartz-community/citations",
"resolved": "https://github.com/quartz-community/citations.git",
"commit": "2ad133a123c5b15b7c41d4fb684d422622113081",
"installedAt": "2026-05-24T15:36:16.430Z"
"installedAt": "2026-06-02T10:09:00.409Z"
},
"cname": {
"source": "github:quartz-community/cname",
"resolved": "https://github.com/quartz-community/cname.git",
"commit": "ec4c81c91da13a6f6c2cbe1656daf5c14575dab3",
"installedAt": "2026-05-24T15:36:16.443Z"
"installedAt": "2026-06-02T10:09:03.684Z"
},
"comments": {
"source": "github:quartz-community/comments",
"resolved": "https://github.com/quartz-community/comments.git",
"commit": "278115200a9bacfc0ee4695b200a5843d15517f3",
"installedAt": "2026-05-24T15:36:16.462Z"
"installedAt": "2026-06-02T10:09:07.067Z"
},
"content-index": {
"source": "github:quartz-community/content-index",
"resolved": "https://github.com/quartz-community/content-index.git",
"commit": "34621a71940977a6af6b20abd5dc552d81181b6b",
"installedAt": "2026-05-24T15:36:16.460Z"
"installedAt": "2026-06-02T10:09:03.662Z"
},
"content-meta": {
"source": "github:quartz-community/content-meta",
"resolved": "https://github.com/quartz-community/content-meta.git",
"commit": "a148b4104e9fb6323a6459b2f13c46f9a92da487",
"installedAt": "2026-05-24T15:36:16.441Z"
"installedAt": "2026-06-02T10:09:05.770Z"
},
"content-page": {
"source": "github:quartz-community/content-page",
"resolved": "https://github.com/quartz-community/content-page.git",
"commit": "977d15f083f5b5de4b299c8f016e4e61fd422716",
"installedAt": "2026-05-24T15:36:16.471Z"
"installedAt": "2026-06-02T10:09:04.385Z"
},
"crawl-links": {
"source": "github:quartz-community/crawl-links",
"resolved": "https://github.com/quartz-community/crawl-links.git",
"commit": "533a2213b673c3ea2a67f6e160f4e6a1262e8a6d",
"installedAt": "2026-05-24T15:36:16.458Z"
"commit": "69497e22046d62ee601415ad832bcde89a70a544",
"installedAt": "2026-06-02T10:08:59.136Z"
},
"created-modified-date": {
"source": "github:quartz-community/created-modified-date",
"resolved": "https://github.com/quartz-community/created-modified-date.git",
"commit": "c003199fb842969d43ee9e0f54120a85e588260e",
"installedAt": "2026-05-24T15:36:16.447Z"
"installedAt": "2026-06-02T10:08:58.401Z"
},
"darkmode": {
"source": "github:quartz-community/darkmode",
"resolved": "https://github.com/quartz-community/darkmode.git",
"commit": "dedd9606147e9d995a4376b0d03e469e39e94f85",
"installedAt": "2026-05-24T15:36:16.475Z"
"installedAt": "2026-06-02T10:09:06.499Z"
},
"description": {
"source": "github:quartz-community/description",
"resolved": "https://github.com/quartz-community/description.git",
"commit": "026e63bd46d5f7feed58373f999ed98bf8248a26",
"installedAt": "2026-05-24T15:36:16.445Z"
"installedAt": "2026-06-02T10:08:59.685Z"
},
"encrypted-pages": {
"source": "github:quartz-community/encrypted-pages",
"resolved": "https://github.com/quartz-community/encrypted-pages.git",
"commit": "e0e08e7afa40b97909fb24fd7509ddaa10116425",
"installedAt": "2026-05-24T15:36:17.007Z"
"installedAt": "2026-06-02T10:09:04.270Z"
},
"explicit-publish": {
"source": "github:quartz-community/explicit-publish",
"resolved": "https://github.com/quartz-community/explicit-publish.git",
"commit": "4afb630a1cc9034267beb93c928e81d0fa2f4d4b",
"installedAt": "2026-05-24T15:36:16.960Z"
"installedAt": "2026-06-02T10:09:01.004Z"
},
"explorer": {
"source": "github:quartz-community/explorer",
"resolved": "https://github.com/quartz-community/explorer.git",
"commit": "69389c917af8f8c74372c70b600ef4652f20c8f2",
"installedAt": "2026-05-24T15:36:16.853Z"
"installedAt": "2026-06-02T10:09:04.912Z"
},
"favicon": {
"source": "github:quartz-community/favicon",
"resolved": "https://github.com/quartz-community/favicon.git",
"commit": "85842d5c15f937a3d1a02c45accee27118146d73",
"installedAt": "2026-05-24T15:36:16.980Z"
"installedAt": "2026-06-02T10:09:02.895Z"
},
"folder-page": {
"source": "github:quartz-community/folder-page",
"resolved": "https://github.com/quartz-community/folder-page.git",
"commit": "a538b8e548a0215568942a3d94b042010a86d66b",
"installedAt": "2026-05-24T15:36:16.992Z"
"installedAt": "2026-06-02T10:09:04.449Z"
},
"footer": {
"source": "github:quartz-community/footer",
"resolved": "https://github.com/quartz-community/footer.git",
"commit": "9e3f6d734d886eac0d41841c4dc8862b377fcaf4",
"installedAt": "2026-05-25T15:27:47.250Z"
"installedAt": "2026-06-02T10:09:08.268Z"
},
"github-flavored-markdown": {
"source": "github:quartz-community/github-flavored-markdown",
"resolved": "https://github.com/quartz-community/github-flavored-markdown.git",
"commit": "3eabbaa252ce175665ab3f62e1af25948a83e8b6",
"installedAt": "2026-05-24T15:36:17.005Z"
"installedAt": "2026-06-02T10:08:58.485Z"
},
"graph": {
"source": "github:quartz-community/graph",
"resolved": "https://github.com/quartz-community/graph.git",
"commit": "27eb4dd71ca10c8bd204cf1537da4448811c15f3",
"installedAt": "2026-05-24T15:36:16.989Z"
"commit": "67ac4993907618a0ea5bd33726f792c572cddbad",
"installedAt": "2026-06-02T10:09:05.149Z"
},
"hard-line-breaks": {
"source": "github:quartz-community/hard-line-breaks",
"resolved": "https://github.com/quartz-community/hard-line-breaks.git",
"commit": "0d448f38a24c568c5539ff2cd57d813293430ccd",
"installedAt": "2026-05-24T15:36:17.005Z"
"installedAt": "2026-06-02T10:09:02.386Z"
},
"latex": {
"source": "github:quartz-community/latex",
"resolved": "https://github.com/quartz-community/latex.git",
"commit": "3dcfedcb5ae5e28b71248a72665ccb881e82c185",
"installedAt": "2026-05-24T15:36:16.994Z"
"installedAt": "2026-06-02T10:09:00.189Z"
},
"note-properties": {
"source": "github:quartz-community/note-properties",
"resolved": "https://github.com/quartz-community/note-properties.git",
"commit": "cccd275468e93ea59115e755d4bb8599192c45b9",
"installedAt": "2026-05-24T15:36:16.987Z"
"commit": "874712a9910299b906606c885ea1fe9602114ca7",
"installedAt": "2026-06-02T10:09:07.794Z"
},
"obsidian-flavored-markdown": {
"source": "github:quartz-community/obsidian-flavored-markdown",
"resolved": "https://github.com/quartz-community/obsidian-flavored-markdown.git",
"commit": "186d6bd990a1b88ab35c4f66ea4f257f1e7b4a51",
"installedAt": "2026-05-25T23:55:00.345Z"
"commit": "7f61d4bcf7537d056528f6cecaf5c35b39f74fe8",
"installedAt": "2026-06-02T10:08:58.452Z"
},
"obsidian-plugin-excalidraw": {
"source": "github:quartz-community/obsidian-plugin-excalidraw",
"resolved": "https://github.com/quartz-community/obsidian-plugin-excalidraw.git",
"commit": "9c5ff6bd6d2ee27814aeaf98ac4ddfd025a985fa",
"installedAt": "2026-05-25T17:40:01.376Z"
},
"obsidian-plugin-ttrpg-tools-maps": {
"source": "github:quartz-community/obsidian-plugin-ttrpg-tools-maps",
"resolved": "https://github.com/quartz-community/obsidian-plugin-ttrpg-tools-maps.git",
"commit": "c43314724a7606dd18b90972c457f593ccded5f5",
"installedAt": "2026-05-24T15:36:16.983Z"
"commit": "554ae8af7ac4d7fdff8d78e65bfa9b4dd53cbb87",
"installedAt": "2026-06-02T10:09:08.523Z"
},
"og-image": {
"source": "github:quartz-community/og-image",
"resolved": "https://github.com/quartz-community/og-image.git",
"commit": "52ea59e2b40dde77abf7fd88eddecf161e671085",
"installedAt": "2026-05-24T15:36:16.999Z"
"installedAt": "2026-06-02T10:09:03.371Z"
},
"ox-hugo": {
"source": "github:quartz-community/ox-hugo",
"resolved": "https://github.com/quartz-community/ox-hugo.git",
"commit": "832871da9ab33d52f904456c1a5e14f1b2057d83",
"installedAt": "2026-05-24T15:36:16.990Z"
"installedAt": "2026-06-02T10:09:00.391Z"
},
"page-title": {
"source": "github:quartz-community/page-title",
"resolved": "https://github.com/quartz-community/page-title.git",
"commit": "a53cf02ce7faca400bcf3cfeed47eb860329e534",
"installedAt": "2026-05-24T15:36:16.998Z"
"installedAt": "2026-06-02T10:09:06.329Z"
},
"quartz-themes": {
"source": {
@@ -206,81 +200,81 @@
"subdir": "plugin"
},
"resolved": "https://github.com/saberzero1/quartz-themes.git",
"commit": "b82021d7866c11353a0cc55b75ac3198d988d7d7",
"commit": "0da7355c49aa68cb375726913f41c64ac1f11733",
"subdir": "plugin",
"installedAt": "2026-05-25T15:05:52.311Z"
"installedAt": "2026-06-02T10:10:13.217Z"
},
"reader-mode": {
"source": "github:quartz-community/reader-mode",
"resolved": "https://github.com/quartz-community/reader-mode.git",
"commit": "02e36d3720e10c2087abce23cf0d2e94d53d27f1",
"installedAt": "2026-05-24T15:36:17.428Z"
"installedAt": "2026-06-02T10:09:06.501Z"
},
"recent-notes": {
"source": "github:quartz-community/recent-notes",
"resolved": "https://github.com/quartz-community/recent-notes.git",
"commit": "74cb2588deae80fd7c13bf5fdeedebffe547d214",
"installedAt": "2026-05-24T15:36:17.428Z"
"commit": "4505adba9c213967ddf94ea5fc05039245fd9fe9",
"installedAt": "2026-06-02T10:09:07.065Z"
},
"remove-draft": {
"source": "github:quartz-community/remove-draft",
"resolved": "https://github.com/quartz-community/remove-draft.git",
"commit": "0a51a4c6abb3c86961a9bbf46f4538da87fcf9f8",
"installedAt": "2026-05-24T15:36:17.446Z"
"installedAt": "2026-06-02T10:09:01.042Z"
},
"roam": {
"source": "github:quartz-community/roam",
"resolved": "https://github.com/quartz-community/roam.git",
"commit": "b2aad70fdf548d07b8fd28df9edbdba7f256f166",
"installedAt": "2026-05-24T15:36:17.448Z"
"installedAt": "2026-06-02T10:09:00.837Z"
},
"search": {
"source": "github:quartz-community/search",
"resolved": "https://github.com/quartz-community/search.git",
"commit": "e0ebef9f80b23a015a9252d4ab98b889b08dc8b7",
"installedAt": "2026-05-24T15:36:17.442Z"
"installedAt": "2026-06-02T10:09:05.207Z"
},
"spacer": {
"source": "github:quartz-community/spacer",
"resolved": "https://github.com/quartz-community/spacer.git",
"commit": "64b135dcfb78c4911f8c42d942872d645cd2a456",
"installedAt": "2026-05-24T15:36:17.428Z"
"installedAt": "2026-06-02T10:09:07.232Z"
},
"stacked-pages": {
"source": "github:quartz-community/stacked-pages",
"resolved": "https://github.com/quartz-community/stacked-pages.git",
"commit": "9d67b8dedab5c27db98afd26dfa773e5003b0446",
"installedAt": "2026-05-24T15:36:17.444Z"
"installedAt": "2026-06-02T10:09:01.687Z"
},
"syntax-highlighting": {
"source": "github:quartz-community/syntax-highlighting",
"resolved": "https://github.com/quartz-community/syntax-highlighting.git",
"commit": "5bfdc2c3f42d3d0326c4e777eb575f3fb68d51fb",
"installedAt": "2026-05-24T15:36:17.446Z"
"installedAt": "2026-06-02T10:08:59.657Z"
},
"table-of-contents": {
"source": "github:quartz-community/table-of-contents",
"resolved": "https://github.com/quartz-community/table-of-contents.git",
"commit": "4c25c048a7f788cb156dad0050d80c7ed88692f9",
"installedAt": "2026-05-24T15:36:17.439Z"
"installedAt": "2026-06-02T10:08:59.138Z"
},
"tag-list": {
"source": "github:quartz-community/tag-list",
"resolved": "https://github.com/quartz-community/tag-list.git",
"commit": "0d18b40d26b3499823d46740c35e8ee874ba0ca0",
"installedAt": "2026-05-24T15:36:17.439Z"
"installedAt": "2026-06-02T10:09:05.834Z"
},
"tag-page": {
"source": "github:quartz-community/tag-page",
"resolved": "https://github.com/quartz-community/tag-page.git",
"commit": "82f8a6d5e774de5e075978651b6ca81f2ccb6812",
"installedAt": "2026-05-24T15:36:17.446Z"
"installedAt": "2026-06-02T10:09:04.768Z"
},
"unlisted-pages": {
"source": "github:quartz-community/unlisted-pages",
"resolved": "https://github.com/quartz-community/unlisted-pages.git",
"commit": "be42781fb08a60e9f9268682d457b289094d2838",
"installedAt": "2026-05-24T15:36:17.406Z"
"installedAt": "2026-06-02T10:09:01.461Z"
}
}
}

View File

@@ -22,6 +22,7 @@ import {
gitPull,
popContentFolder,
stashContentFolder,
symlinkOrCopy,
} from "./helpers.js"
import {
handlePluginRestore,
@@ -210,7 +211,7 @@ export async function handleCreate(argv) {
preserveTimestamps: true,
})
} else if (setupStrategy === "symlink") {
await fs.promises.symlink(originalFolder, contentFolder, "dir")
await symlinkOrCopy(originalFolder, contentFolder)
}
} else if (setupStrategy === "new") {
await fs.promises.writeFile(

View File

@@ -3,6 +3,7 @@ import { styleText } from "util"
import { contentCacheFolder } from "./constants.js"
import { spawnSync } from "child_process"
import fs from "fs"
import path from "path"
export function escapePath(fp) {
return fp
@@ -33,7 +34,7 @@ export async function stashContentFolder(contentFolder) {
}
export function gitPull(origin, branch) {
const flags = ["--no-rebase", "--autostash", "--no-edit"]
const flags = ["--no-rebase", "--autostash", "--no-edit", "--allow-unrelated-histories"]
const out = spawnSync("git", ["pull", ...flags, origin, branch], { stdio: "inherit" })
if (out.stderr) {
throw new Error(styleText("red", `Error while pulling updates: ${out.stderr}`))
@@ -52,3 +53,57 @@ export async function popContentFolder(contentFolder) {
})
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
}
/**
* Create a directory symlink with Windows fallback.
*
* On Windows, creating symlinks requires Developer Mode or admin privileges.
* When that fails (EPERM), we try a junction first (no elevation needed),
* then fall back to a recursive copy as a last resort.
*
* @param {string} target Symlink target (may be relative)
* @param {string} linkPath Path where the link is created
*/
export function symlinkOrCopySync(target, linkPath) {
try {
fs.symlinkSync(target, linkPath, "dir")
} catch (err) {
if (err.code === "EEXIST") return
if (err.code === "EPERM" && process.platform === "win32") {
try {
fs.symlinkSync(target, linkPath, "junction")
return
} catch {
const resolvedTarget = path.resolve(path.dirname(linkPath), target)
fs.cpSync(resolvedTarget, linkPath, { recursive: true })
return
}
}
throw err
}
}
/**
* Async version of {@link symlinkOrCopySync}.
*
* @param {string} target Symlink target (may be relative)
* @param {string} linkPath Path where the link is created
*/
export async function symlinkOrCopy(target, linkPath) {
try {
await fs.promises.symlink(target, linkPath, "dir")
} catch (err) {
if (err.code === "EEXIST") return
if (err.code === "EPERM" && process.platform === "win32") {
try {
await fs.promises.symlink(target, linkPath, "junction")
return
} catch {
const resolvedTarget = path.resolve(path.dirname(linkPath), target)
await fs.promises.cp(resolvedTarget, linkPath, { recursive: true })
return
}
}
throw err
}
}

237
quartz/cli/helpers.test.js Normal file
View File

@@ -0,0 +1,237 @@
import test, { describe, beforeEach, afterEach, mock } from "node:test"
import assert from "node:assert"
import fs from "fs"
import path from "path"
import os from "os"
import { symlinkOrCopySync, symlinkOrCopy } from "./helpers.js"
function makeTmpDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), "quartz-symlink-test-"))
}
function makeTarget(tmpDir) {
const target = path.join(tmpDir, "target")
fs.mkdirSync(target)
fs.writeFileSync(path.join(target, "marker.txt"), "hello")
return target
}
describe("symlinkOrCopySync", () => {
let tmpDir
beforeEach(() => {
tmpDir = makeTmpDir()
})
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true })
})
test("creates a symlink on success", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
symlinkOrCopySync(target, linkPath)
const stat = fs.lstatSync(linkPath)
assert.ok(stat.isSymbolicLink())
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
})
test("silently succeeds when link already exists (EEXIST)", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
symlinkOrCopySync(target, linkPath)
assert.doesNotThrow(() => symlinkOrCopySync(target, linkPath))
})
test("re-throws non-EPERM errors", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "nonexistent-parent", "link")
assert.throws(
() => symlinkOrCopySync(target, linkPath),
(err) => {
return err.code === "ENOENT"
},
)
})
test("falls back to junction on Windows EPERM", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
let callCount = 0
const originalSymlinkSync = fs.symlinkSync
mock.method(fs, "symlinkSync", (t, lp, type) => {
callCount++
if (callCount === 1 && type === "dir") {
const err = new Error("EPERM: operation not permitted, symlink")
err.code = "EPERM"
err.errno = -4048
err.syscall = "symlink"
throw err
}
return originalSymlinkSync(t, lp, type)
})
try {
symlinkOrCopySync(target, linkPath)
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
assert.strictEqual(callCount, 2)
} finally {
fs.symlinkSync.mock.restore()
if (originalPlatform) {
Object.defineProperty(process, "platform", originalPlatform)
}
}
})
test("falls back to copy when both symlink and junction fail on Windows", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
const originalSymlinkSync = fs.symlinkSync
mock.method(fs, "symlinkSync", (_t, _lp, _type) => {
const err = new Error("EPERM: operation not permitted, symlink")
err.code = "EPERM"
err.errno = -4048
err.syscall = "symlink"
throw err
})
try {
symlinkOrCopySync(target, linkPath)
const stat = fs.lstatSync(linkPath)
assert.ok(stat.isDirectory(), "fallback should produce a real directory, not a symlink")
assert.strictEqual(fs.readFileSync(path.join(linkPath, "marker.txt"), "utf-8"), "hello")
} finally {
fs.symlinkSync.mock.restore()
if (originalPlatform) {
Object.defineProperty(process, "platform", originalPlatform)
}
}
})
test("does not fall back on EPERM when not on Windows", () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
Object.defineProperty(process, "platform", { value: "linux", configurable: true })
const originalSymlinkSync = fs.symlinkSync
mock.method(fs, "symlinkSync", (_t, _lp, _type) => {
const err = new Error("EPERM: operation not permitted, symlink")
err.code = "EPERM"
throw err
})
try {
assert.throws(
() => symlinkOrCopySync(target, linkPath),
(err) => err.code === "EPERM",
)
} finally {
fs.symlinkSync.mock.restore()
if (originalPlatform) {
Object.defineProperty(process, "platform", originalPlatform)
}
}
})
})
describe("symlinkOrCopy", () => {
let tmpDir
beforeEach(() => {
tmpDir = makeTmpDir()
})
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true })
})
test("creates a symlink on success", async () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
await symlinkOrCopy(target, linkPath)
const stat = fs.lstatSync(linkPath)
assert.ok(stat.isSymbolicLink())
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
})
test("silently succeeds when link already exists (EEXIST)", async () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
await symlinkOrCopy(target, linkPath)
await assert.doesNotReject(() => symlinkOrCopy(target, linkPath))
})
test("falls back to copy when both symlink and junction fail on Windows", async () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
const originalSymlink = fs.promises.symlink
mock.method(fs.promises, "symlink", async (_t, _lp, _type) => {
const err = new Error("EPERM: operation not permitted, symlink")
err.code = "EPERM"
err.errno = -4048
err.syscall = "symlink"
throw err
})
try {
await symlinkOrCopy(target, linkPath)
const stat = fs.lstatSync(linkPath)
assert.ok(stat.isDirectory(), "fallback should produce a real directory, not a symlink")
assert.strictEqual(fs.readFileSync(path.join(linkPath, "marker.txt"), "utf-8"), "hello")
} finally {
fs.promises.symlink.mock.restore()
if (originalPlatform) {
Object.defineProperty(process, "platform", originalPlatform)
}
}
})
test("does not fall back on EPERM when not on Windows", async () => {
const target = makeTarget(tmpDir)
const linkPath = path.join(tmpDir, "link")
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
Object.defineProperty(process, "platform", { value: "linux", configurable: true })
mock.method(fs.promises, "symlink", async (_t, _lp, _type) => {
const err = new Error("EPERM: operation not permitted, symlink")
err.code = "EPERM"
throw err
})
try {
await assert.rejects(
() => symlinkOrCopy(target, linkPath),
(err) => err.code === "EPERM",
)
} finally {
fs.promises.symlink.mock.restore()
if (originalPlatform) {
Object.defineProperty(process, "platform", originalPlatform)
}
}
})
})

View File

@@ -20,6 +20,7 @@ import {
resolveLockfileName,
getNameOverrides,
} from "./plugin-data.js"
import { symlinkOrCopySync } from "./helpers.js"
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
@@ -128,12 +129,7 @@ function needsBuild(pluginDir) {
* share a single copy of packages like unified, vfile, rehype-raw, etc.
*/
function trySymlink(target, linkPath) {
try {
fs.symlinkSync(target, linkPath, "dir")
} catch (err) {
if (err.code === "EEXIST") return
throw err
}
symlinkOrCopySync(target, linkPath)
}
function linkPeerPlugins(pluginDir) {
@@ -621,7 +617,7 @@ export async function handlePluginInstallUnified({
}
console.log(styleText("cyan", `→ Linking ${name} from ${resolvedPath}...`))
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
fs.symlinkSync(resolvedPath, pluginDir, "dir")
symlinkOrCopySync(resolvedPath, pluginDir)
lockfile.plugins[name] = {
source: entry.source,
resolved: resolvedPath,
@@ -810,7 +806,7 @@ export async function handlePluginInstallUnified({
continue
}
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
fs.symlinkSync(entry.resolved, pluginDir, "dir")
symlinkOrCopySync(entry.resolved, pluginDir)
console.log(styleText("green", `${name} restored (local symlink)`))
restoredPlugins.push({ name, pluginDir })
installed++
@@ -1045,7 +1041,7 @@ export async function handlePluginInstallUnified({
continue
}
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
fs.symlinkSync(entry.resolved, pluginDir, "dir")
symlinkOrCopySync(entry.resolved, pluginDir)
console.log(styleText("green", `${name} (local) linked`))
pluginsToBuild.push({ name, pluginDir })
installed++
@@ -1222,7 +1218,7 @@ export async function handlePluginAdd(
}
console.log(styleText("cyan", `→ Adding ${name} from local path ${resolvedPath}...`))
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
fs.symlinkSync(resolvedPath, pluginDir, "dir")
symlinkOrCopySync(resolvedPath, pluginDir)
lockfile.plugins[name] = {
source,
resolved: resolvedPath,
@@ -1315,13 +1311,13 @@ export async function handlePluginAdd(
}
if (manifest?.components) {
const layoutPositions = new Set(["left", "right", "beforeBody", "afterBody"])
const firstComponentKey = Object.keys(manifest.components)[0]
const comp = manifest.components[firstComponentKey]
if (comp?.defaultPosition) {
if (comp?.defaultPosition && layoutPositions.has(comp.defaultPosition)) {
newEntry.layout = {
position: comp.defaultPosition,
priority: comp.defaultPriority ?? 50,
display: "all",
}
}
}

View File

@@ -72,6 +72,9 @@ plugins:
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
@@ -156,7 +159,7 @@ plugins:
enabled: true
layout:
position: right
priority: 30
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:

View File

@@ -72,6 +72,9 @@ plugins:
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
@@ -161,7 +164,7 @@ plugins:
enabled: true
layout:
position: right
priority: 30
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:

View File

@@ -82,6 +82,9 @@ plugins:
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
@@ -172,7 +175,7 @@ plugins:
enabled: true
layout:
position: right
priority: 30
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:

View File

@@ -82,6 +82,9 @@ plugins:
- source: github:quartz-community/table-of-contents
enabled: true
order: 50
layout:
position: right
priority: 30
- source: github:quartz-community/crawl-links
enabled: true
options:
@@ -172,7 +175,7 @@ plugins:
enabled: true
layout:
position: right
priority: 30
priority: 50
- source: github:quartz-community/article-title
enabled: true
layout:

View File

@@ -1,3 +1,428 @@
@use "./variables.scss" as *;
@use "sass:map";
// put your custom CSS here!
// Old Library — a source-controlled, reading-first theme.
:root {
--paper-shadow: rgba(66, 43, 23, 0.09);
--paper-rule: rgba(122, 38, 50, 0.28);
--paper-grain: rgba(74, 51, 29, 0.026);
--panel: rgba(255, 252, 242, 0.48);
--code-surface: rgba(91, 67, 39, 0.075);
--focus-ring: #9b7137;
}
:root[saved-theme="dark"] {
--paper-shadow: rgba(0, 0, 0, 0.34);
--paper-rule: rgba(197, 154, 89, 0.28);
--paper-grain: rgba(238, 219, 180, 0.018);
--panel: rgba(65, 51, 37, 0.32);
--code-surface: rgba(238, 219, 180, 0.055);
--focus-ring: #d1aa6b;
}
html {
font-size: 17px;
}
body {
line-height: 1.72;
letter-spacing: 0.003em;
background-image:
repeating-linear-gradient(0deg, transparent 0, transparent 3px, var(--paper-grain) 4px),
radial-gradient(circle at 18% 12%, var(--paper-grain) 0 1px, transparent 1.5px),
radial-gradient(circle at 78% 62%, var(--paper-grain) 0 1px, transparent 1.5px);
background-size:
auto,
31px 29px,
37px 41px;
}
::selection {
background: color-mix(in srgb, var(--secondary) 24%, transparent);
}
:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 3px;
border-radius: 2px;
}
.page {
max-width: 1390px;
}
.page > #quartz-body {
box-sizing: border-box;
grid-template-columns: minmax(220px, 255px) minmax(0, 780px) minmax(205px, 250px);
column-gap: clamp(1.5rem, 3vw, 3.25rem);
justify-content: center;
}
.page > #quartz-body .sidebar {
gap: 1rem;
padding: 4.25rem 0.6rem 2rem;
scrollbar-width: thin;
}
.page > #quartz-body .center {
box-sizing: border-box;
position: relative;
padding: 0 clamp(0.25rem, 1.25vw, 1rem) 3rem;
}
.page > #quartz-body .center::before {
content: "";
position: absolute;
inset: 3.3rem -1.2rem 1.5rem;
z-index: -1;
border-inline: 1px solid color-mix(in srgb, var(--lightgray) 65%, transparent);
box-shadow: 0 1.1rem 2.8rem -2.2rem var(--paper-shadow);
pointer-events: none;
}
article {
font-size: 1.02rem;
hanging-punctuation: first last;
}
article > p,
article > ul,
article > ol,
article > blockquote {
max-width: 70ch;
}
article p {
margin: 0.85rem 0 1.15rem;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--dark);
font-family: var(--headerFont);
line-height: 1.13;
letter-spacing: -0.018em;
text-wrap: balance;
}
.article-title {
margin: 0.35rem 0 0.35rem;
font-size: clamp(2.45rem, 5vw, 4rem);
font-weight: 600;
line-height: 0.98;
}
article h2 {
margin-top: 2.7rem;
padding-bottom: 0.28rem;
border-bottom: 1px solid var(--paper-rule);
font-size: 2rem;
}
article h3 {
margin-top: 2.1rem;
font-size: 1.55rem;
font-style: italic;
}
article h4 {
margin-top: 1.8rem;
font-size: 1.22rem;
letter-spacing: 0.025em;
text-transform: uppercase;
}
a {
text-decoration-color: color-mix(in srgb, var(--secondary) 48%, transparent);
text-decoration-thickness: 1px;
text-underline-offset: 0.16em;
}
article a:not(.internal):not(.tag-link) {
text-decoration-line: underline;
}
a.internal {
border-radius: 2px;
background: linear-gradient(to top, var(--highlight) 42%, transparent 42%);
padding-inline: 0.08em;
}
hr {
height: 1px;
margin: 2.8rem auto;
border: 0;
background: linear-gradient(90deg, transparent, var(--gray) 18%, var(--gray) 82%, transparent);
}
blockquote {
position: relative;
margin: 1.8rem 0;
padding: 0.2rem 1.4rem 0.2rem 1.65rem;
border-left: 3px double var(--secondary);
color: var(--darkgray);
font-style: italic;
background: linear-gradient(90deg, var(--highlight), transparent 78%);
}
blockquote::before {
content: "";
position: absolute;
top: -0.55rem;
left: 0.35rem;
color: var(--secondary);
font-family: var(--headerFont);
font-size: 2.5rem;
opacity: 0.45;
}
pre,
code {
font-size: 0.86em;
}
pre {
border: 1px solid var(--lightgray);
border-radius: 3px;
background: var(--code-surface) !important;
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--tertiary) 65%, transparent);
}
:not(pre) > code {
border: 1px solid color-mix(in srgb, var(--lightgray) 72%, transparent);
border-radius: 2px;
background: var(--code-surface);
padding: 0.08em 0.3em;
}
table {
box-sizing: border-box;
width: 100%;
border-collapse: collapse;
font-size: 0.92rem;
}
th {
border-bottom: 2px double var(--gray);
color: var(--dark);
font-family: var(--headerFont);
font-size: 1rem;
letter-spacing: 0.035em;
text-align: left;
}
td {
border-bottom: 1px solid color-mix(in srgb, var(--lightgray) 74%, transparent);
}
tr:nth-child(even) td {
background: var(--panel);
}
.callout {
border-width: 1px !important;
border-radius: 3px !important;
background: var(--panel) !important;
box-shadow: 0 0.4rem 1.3rem -1rem var(--paper-shadow);
}
.callout-title {
font-family: var(--headerFont);
letter-spacing: 0.025em;
}
input[type="checkbox"] {
accent-color: var(--tertiary);
}
.page-title a {
color: var(--dark);
font-family: var(--titleFont);
font-size: 1.75rem;
font-variant: small-caps;
font-weight: 700;
letter-spacing: 0.075em;
}
.page-title a::after {
content: " · commonplace";
color: var(--gray);
font-size: 0.62rem;
font-variant: normal;
font-weight: 600;
letter-spacing: 0.13em;
text-transform: uppercase;
}
.content-meta,
.breadcrumbs,
.tags {
color: var(--gray);
font-size: 0.76rem;
letter-spacing: 0.075em;
text-transform: uppercase;
}
.breadcrumbs {
padding-bottom: 0.55rem;
border-bottom: 1px solid color-mix(in srgb, var(--lightgray) 72%, transparent);
}
.sidebar > *:not(.page-title):not(.spacer) {
padding-block: 0.75rem;
border-top: 1px solid color-mix(in srgb, var(--lightgray) 70%, transparent);
}
.sidebar h3,
.sidebar h4,
.explorer .folder-title,
.toc .toc-header {
font-family: var(--headerFont);
letter-spacing: 0.045em;
}
.sidebar ul,
.sidebar ol,
.explorer-content {
font-size: 0.83rem;
line-height: 1.42;
}
.search > .search-button {
border: 1px solid var(--lightgray);
border-radius: 2px;
background: var(--panel);
}
.graph,
.backlinks,
.toc {
opacity: 0.88;
transition: opacity 160ms ease;
}
.graph:hover,
.backlinks:hover,
.toc:hover {
opacity: 1;
}
.popover {
border: 1px solid var(--gray);
border-radius: 3px;
background-color: var(--light);
box-shadow: 0 1rem 2.2rem var(--paper-shadow);
}
.tag-link {
border: 1px solid color-mix(in srgb, var(--secondary) 35%, var(--lightgray));
border-radius: 999px;
background: var(--panel) !important;
padding: 0.08rem 0.48rem !important;
font-size: 0.76rem;
letter-spacing: 0.04em;
}
img {
border-radius: 2px;
}
article > img,
article p > img {
box-shadow: 0 0.6rem 1.6rem -1rem var(--paper-shadow);
}
@media all and ($tablet) {
.page > #quartz-body {
grid-template-columns: minmax(210px, 245px) minmax(0, 1fr);
column-gap: 2rem;
}
.page > #quartz-body .sidebar {
padding-top: 3.25rem;
}
.page > #quartz-body .center::before {
right: -0.5rem;
}
}
@media all and ($mobile) {
html {
font-size: 16px;
}
.page > #quartz-body {
display: grid;
grid-template-columns: minmax(0, 1fr);
padding-inline: clamp(1rem, 5vw, 1.5rem);
}
.page > #quartz-body .sidebar.left {
align-items: center;
gap: 0.55rem;
padding-top: 1rem;
border-bottom: 1px double var(--paper-rule);
}
.page > #quartz-body .sidebar.right {
gap: 1rem;
padding-top: 1.5rem;
}
.page > #quartz-body .sidebar > * {
border-top: 0;
}
.page > #quartz-body .center {
padding-inline: 0;
}
.page > #quartz-body .center::before {
display: none;
}
.page-title a {
font-size: 1.45rem;
}
.page-title a::after {
display: none;
}
.article-title {
font-size: clamp(2.25rem, 12vw, 3.35rem);
}
article h2 {
font-size: 1.7rem;
}
article {
overflow-wrap: anywhere;
}
article table {
display: block;
max-width: 100%;
overflow-x: auto;
}
pre {
max-width: calc(100vw - 2rem);
overflow-x: auto;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}

428
quartz/styles/custom.scssr Normal file
View File

@@ -0,0 +1,428 @@
@use "./variables.scss" as *;
@use "sass:map";
// Old Library — a source-controlled, reading-first theme.
:root {
--paper-shadow: rgba(66, 43, 23, 0.09);
--paper-rule: rgba(122, 38, 50, 0.28);
--paper-grain: rgba(74, 51, 29, 0.026);
--panel: rgba(255, 252, 242, 0.48);
--code-surface: rgba(91, 67, 39, 0.075);
--focus-ring: #9b7137;
}
:root[saved-theme="dark"] {
--paper-shadow: rgba(0, 0, 0, 0.34);
--paper-rule: rgba(197, 154, 89, 0.28);
--paper-grain: rgba(238, 219, 180, 0.018);
--panel: rgba(65, 51, 37, 0.32);
--code-surface: rgba(238, 219, 180, 0.055);
--focus-ring: #d1aa6b;
}
html {
font-size: 17px;
}
body {
line-height: 1.72;
letter-spacing: 0.003em;
background-image:
repeating-linear-gradient(0deg, transparent 0, transparent 3px, var(--paper-grain) 4px),
radial-gradient(circle at 18% 12%, var(--paper-grain) 0 1px, transparent 1.5px),
radial-gradient(circle at 78% 62%, var(--paper-grain) 0 1px, transparent 1.5px);
background-size:
auto,
31px 29px,
37px 41px;
}
::selection {
background: color-mix(in srgb, var(--secondary) 24%, transparent);
}
:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 3px;
border-radius: 2px;
}
.page {
max-width: 1390px;
}
.page > #quartz-body {
box-sizing: border-box;
grid-template-columns: minmax(220px, 255px) minmax(0, 780px) minmax(205px, 250px);
column-gap: clamp(1.5rem, 3vw, 3.25rem);
justify-content: center;
}
.page > #quartz-body .sidebar {
gap: 1rem;
padding: 4.25rem 0.6rem 2rem;
scrollbar-width: thin;
}
.page > #quartz-body .center {
box-sizing: border-box;
position: relative;
padding: 0 clamp(0.25rem, 1.25vw, 1rem) 3rem;
}
.page > #quartz-body .center::before {
content: "";
position: absolute;
inset: 3.3rem -1.2rem 1.5rem;
z-index: -1;
border-inline: 1px solid color-mix(in srgb, var(--lightgray) 65%, transparent);
box-shadow: 0 1.1rem 2.8rem -2.2rem var(--paper-shadow);
pointer-events: none;
}
article {
font-size: 1.02rem;
hanging-punctuation: first last;
}
article > p,
article > ul,
article > ol,
article > blockquote {
max-width: 70ch;
}
article p {
margin: 0.85rem 0 1.15rem;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--dark);
font-family: var(--headerFont);
line-height: 1.13;
letter-spacing: -0.018em;
text-wrap: balance;
}
.article-title {
margin: 0.35rem 0 0.35rem;
font-size: clamp(2.45rem, 5vw, 4rem);
font-weight: 600;
line-height: 0.98;
}
article h2 {
margin-top: 2.7rem;
padding-bottom: 0.28rem;
border-bottom: 1px solid var(--paper-rule);
font-size: 2rem;
}
article h3 {
margin-top: 2.1rem;
font-size: 1.55rem;
font-style: italic;
}
article h4 {
margin-top: 1.8rem;
font-size: 1.22rem;
letter-spacing: 0.025em;
text-transform: uppercase;
}
a {
text-decoration-color: color-mix(in srgb, var(--secondary) 48%, transparent);
text-decoration-thickness: 1px;
text-underline-offset: 0.16em;
}
article a:not(.internal):not(.tag-link) {
text-decoration-line: underline;
}
a.internal {
border-radius: 2px;
background: linear-gradient(to top, var(--highlight) 42%, transparent 42%);
padding-inline: 0.08em;
}
hr {
height: 1px;
margin: 2.8rem auto;
border: 0;
background: linear-gradient(90deg, transparent, var(--gray) 18%, var(--gray) 82%, transparent);
}
blockquote {
position: relative;
margin: 1.8rem 0;
padding: 0.2rem 1.4rem 0.2rem 1.65rem;
border-left: 3px double var(--secondary);
color: var(--darkgray);
font-style: italic;
background: linear-gradient(90deg, var(--highlight), transparent 78%);
}
blockquote::before {
content: "“";
position: absolute;
top: -0.55rem;
left: 0.35rem;
color: var(--secondary);
font-family: var(--headerFont);
font-size: 2.5rem;
opacity: 0.45;
}
pre,
code {
font-size: 0.86em;
}
pre {
border: 1px solid var(--lightgray);
border-radius: 3px;
background: var(--code-surface) !important;
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--tertiary) 65%, transparent);
}
:not(pre) > code {
border: 1px solid color-mix(in srgb, var(--lightgray) 72%, transparent);
border-radius: 2px;
background: var(--code-surface);
padding: 0.08em 0.3em;
}
table {
box-sizing: border-box;
width: 100%;
border-collapse: collapse;
font-size: 0.92rem;
}
th {
border-bottom: 2px double var(--gray);
color: var(--dark);
font-family: var(--headerFont);
font-size: 1rem;
letter-spacing: 0.035em;
text-align: left;
}
td {
border-bottom: 1px solid color-mix(in srgb, var(--lightgray) 74%, transparent);
}
tr:nth-child(even) td {
background: var(--panel);
}
.callout {
border-width: 1px !important;
border-radius: 3px !important;
background: var(--panel) !important;
box-shadow: 0 0.4rem 1.3rem -1rem var(--paper-shadow);
}
.callout-title {
font-family: var(--headerFont);
letter-spacing: 0.025em;
}
input[type="checkbox"] {
accent-color: var(--tertiary);
}
.page-title a {
color: var(--dark);
font-family: var(--titleFont);
font-size: 1.75rem;
font-variant: small-caps;
font-weight: 700;
letter-spacing: 0.075em;
}
.page-title a::after {
content: " · commonplace";
color: var(--gray);
font-size: 0.62rem;
font-variant: normal;
font-weight: 600;
letter-spacing: 0.13em;
text-transform: uppercase;
}
.content-meta,
.breadcrumbs,
.tags {
color: var(--gray);
font-size: 0.76rem;
letter-spacing: 0.075em;
text-transform: uppercase;
}
.breadcrumbs {
padding-bottom: 0.55rem;
border-bottom: 1px solid color-mix(in srgb, var(--lightgray) 72%, transparent);
}
.sidebar > *:not(.page-title):not(.spacer) {
padding-block: 0.75rem;
border-top: 1px solid color-mix(in srgb, var(--lightgray) 70%, transparent);
}
.sidebar h3,
.sidebar h4,
.explorer .folder-title,
.toc .toc-header {
font-family: var(--headerFont);
letter-spacing: 0.045em;
}
.sidebar ul,
.sidebar ol,
.explorer-content {
font-size: 0.83rem;
line-height: 1.42;
}
.search > .search-button {
border: 1px solid var(--lightgray);
border-radius: 2px;
background: var(--panel);
}
.graph,
.backlinks,
.toc {
opacity: 0.88;
transition: opacity 160ms ease;
}
.graph:hover,
.backlinks:hover,
.toc:hover {
opacity: 1;
}
.popover {
border: 1px solid var(--gray);
border-radius: 3px;
background-color: var(--light);
box-shadow: 0 1rem 2.2rem var(--paper-shadow);
}
.tag-link {
border: 1px solid color-mix(in srgb, var(--secondary) 35%, var(--lightgray));
border-radius: 999px;
background: var(--panel) !important;
padding: 0.08rem 0.48rem !important;
font-size: 0.76rem;
letter-spacing: 0.04em;
}
img {
border-radius: 2px;
}
article > img,
article p > img {
box-shadow: 0 0.6rem 1.6rem -1rem var(--paper-shadow);
}
@media all and ($tablet) {
.page > #quartz-body {
grid-template-columns: minmax(210px, 245px) minmax(0, 1fr);
column-gap: 2rem;
}
.page > #quartz-body .sidebar {
padding-top: 3.25rem;
}
.page > #quartz-body .center::before {
right: -0.5rem;
}
}
@media all and ($mobile) {
html {
font-size: 16px;
}
.page > #quartz-body {
display: grid;
grid-template-columns: minmax(0, 1fr);
padding-inline: clamp(1rem, 5vw, 1.5rem);
}
.page > #quartz-body .sidebar.left {
align-items: center;
gap: 0.55rem;
padding-top: 1rem;
border-bottom: 1px double var(--paper-rule);
}
.page > #quartz-body .sidebar.right {
gap: 1rem;
padding-top: 1.5rem;
}
.page > #quartz-body .sidebar > * {
border-top: 0;
}
.page > #quartz-body .center {
padding-inline: 0;
}
.page > #quartz-body .center::before {
display: none;
}
.page-title a {
font-size: 1.45rem;
}
.page-title a::after {
display: none;
}
.article-title {
font-size: clamp(2.25rem, 12vw, 3.35rem);
}
article h2 {
font-size: 1.7rem;
}
article {
overflow-wrap: anywhere;
}
article table {
display: block;
max-width: 100%;
overflow-x: auto;
}
pre {
max-width: calc(100vw - 2rem);
overflow-x: auto;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}

View File

@@ -0,0 +1,56 @@
# Tokyo Night
- Theme: Tokyo Night
- Author: tcmmichaelb139
## Preview
[Open live preview (Quartz)](https://quartz-themes.github.io/tokyo-night/)
[Open live preview (Publish)](https://publish.saberzero.one/tokyo-night)
## Usage (Quartz)
### GitHub Actions
```yaml
env:
THEME_NAME: tokyo-night
```
```yaml
- name: Fetch Quartz Theme
run: curl -s -S https://raw.githubusercontent.com/saberzero1/quartz-themes/master/action.sh | bash -s -- $THEME_NAME
```
### Automatic install into Quartz repository
```bash
curl -s -S -o action.sh https://raw.githubusercontent.com/saberzero1/quartz-themes/master/action.sh
./action.sh tokyo-night
```
### Manual install
Copy [\_index.scss](./_index.scss) into your Quartz repository's `quartz/styles/themes/` directory. (Create the `themes` directory if it does not exist.)
Then, add the following to your `quartz/styles/custom.scss` file after the `@use "base";` line:
```scss
@use "themes";
```
> [!IMPORTANT]
> For dark-only or light-only themes, remember to remove `Component.Darkmode()` from your `quartz.config.ts` file.
### Quartz Syncer
> [!IMPORTANT]
> This setup method is not yet available. It is currently being tested and will be released in the future.
## Usage (Publish)
Download the [publish.css](./publish.css) file and copy it into your Obsidian Publish vault.
Then, in Obsidian Publish, open the Publish modal and select the `publish.css` file and click the "Publish" button at the bottom.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff