mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-27 23:04:05 -06:00
Merge branch 'v4' into open-graph
This commit is contained in:
commit
21408bda4b
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -1,11 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
production-dependencies:
|
||||
applies-to: "version-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
88
.github/workflows/docker-build-push.yaml
vendored
Normal file
88
.github/workflows/docker-build-push.yaml
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
name: Docker build & push image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [v4]
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
branches: [v4]
|
||||
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@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Inject slug/short variables
|
||||
uses: rlespinasse/github-slug-action@v4.4.1
|
||||
- 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@v3.7.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
|
||||
@ -1,4 +1,4 @@
|
||||
FROM node:20-slim as builder
|
||||
FROM node:20-slim AS builder
|
||||
WORKDIR /usr/src/app
|
||||
COPY package.json .
|
||||
COPY package-lock.json* .
|
||||
|
||||
@ -21,3 +21,7 @@ This will start a local web server to run your Quartz on your computer. Open a w
|
||||
> - `--serve`: run a local hot-reloading server to preview your Quartz
|
||||
> - `--port`: what port to run the local preview server on
|
||||
> - `--concurrency`: how many threads to use to parse notes
|
||||
|
||||
> [!warning] Not to be used for production
|
||||
> Serve mode is intended for local previews only.
|
||||
> For production workloads, see the page on [[hosting]].
|
||||
|
||||
@ -21,6 +21,7 @@ const config: QuartzConfig = {
|
||||
This part of the configuration concerns anything that can affect the whole site. The following is a list breaking down all the things you can configure:
|
||||
|
||||
- `pageTitle`: title of the site. This is also used when generating the [[RSS Feed]] for your site.
|
||||
- `pageTitleSuffix`: a string added to the end of the page title. This only applies to the browser tab title, not the title shown at the top of the page.
|
||||
- `enableSPA`: whether to enable [[SPA Routing]] on your site.
|
||||
- `enablePopovers`: whether to enable [[popover previews]] on your site.
|
||||
- `analytics`: what to use for analytics on your site. Values can be
|
||||
@ -32,6 +33,7 @@ This part of the configuration concerns anything that can affect the whole site.
|
||||
- `{ provider: 'posthog', apiKey: '<your-posthog-project-apiKey>', host: '<your-posthog-host>' }`: use [Posthog](https://posthog.com/);
|
||||
- `{ provider: 'tinylytics', siteId: '<your-site-id>' }`: use [Tinylytics](https://tinylytics.app/);
|
||||
- `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com);
|
||||
- `{provider: 'clarity', projectId: '<your-clarity-id-code' }`: use [Microsoft clarity](https://clarity.microsoft.com/). The project id can be found on top of the overview page.
|
||||
- `locale`: used for [[i18n]] and date formatting
|
||||
- `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes.
|
||||
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
|
||||
@ -101,7 +103,7 @@ transformers: [
|
||||
]
|
||||
```
|
||||
|
||||
Some plugins are included by default in the[ `quartz.config.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz.config.ts), but there are more available.
|
||||
Some plugins are included by default in the [`quartz.config.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz.config.ts), but there are more available.
|
||||
|
||||
You can see a list of all plugins and their configuration options [[tags/plugin|here]].
|
||||
|
||||
|
||||
28
docs/features/Roam Research compatibility.md
Normal file
28
docs/features/Roam Research compatibility.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: "Roam Research Compatibility"
|
||||
tags:
|
||||
- feature/transformer
|
||||
---
|
||||
|
||||
[Roam Research](https://roamresearch.com) is a note-taking tool that organizes your knowledge graph in a unique and interconnected way.
|
||||
|
||||
Quartz supports transforming the special Markdown syntax from Roam Research (like `{{[[components]]}}` and other formatting) into
|
||||
regular Markdown via the [[RoamFlavoredMarkdown]] plugin.
|
||||
|
||||
```typescript title="quartz.config.ts"
|
||||
plugins: {
|
||||
transformers: [
|
||||
// ...
|
||||
Plugin.RoamFlavoredMarkdown(),
|
||||
Plugin.ObsidianFlavoredMarkdown(),
|
||||
// ...
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> As seen above placement of `Plugin.RoamFlavoredMarkdown()` within `quartz.config.ts` is very important. It must come before `Plugin.ObsidianFlavoredMarkdown()`.
|
||||
|
||||
## Customization
|
||||
|
||||
This functionality is provided by the [[RoamFlavoredMarkdown]] plugin. See the plugin page for customization options.
|
||||
@ -63,6 +63,18 @@ type Options = {
|
||||
category: string
|
||||
categoryId: string
|
||||
|
||||
// Url to folder with custom themes
|
||||
// defaults to 'https://${cfg.baseUrl}/static/giscus'
|
||||
themeUrl?: string
|
||||
|
||||
// filename for light theme .css file
|
||||
// defaults to 'light'
|
||||
lightTheme?: string
|
||||
|
||||
// filename for dark theme .css file
|
||||
// defaults to 'dark'
|
||||
darkTheme?: string
|
||||
|
||||
// how to map pages -> discussions
|
||||
// defaults to 'url'
|
||||
mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
|
||||
@ -81,3 +93,24 @@ type Options = {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom CSS theme
|
||||
|
||||
Quartz supports custom theme for Giscus. To use a custom CSS theme, place the `.css` file inside the `quartz/static` folder and set the configuration values.
|
||||
|
||||
For example, if you have a light theme `light-theme.css`, a dark theme `dark-theme.css`, and your Quartz site is hosted at `https://example.com/`:
|
||||
|
||||
```ts
|
||||
afterBody: [
|
||||
Component.Comments({
|
||||
provider: 'giscus',
|
||||
options: {
|
||||
// Other options
|
||||
|
||||
themeUrl: "https://example.com/static/giscus", // corresponds to quartz/static/giscus/
|
||||
lightTheme: "light-theme", // corresponds to light-theme.css in quartz/static/giscus/
|
||||
darkTheme: "dark-theme", // corresponds to dark-theme.css quartz/static/giscus/
|
||||
}
|
||||
}),
|
||||
],
|
||||
```
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB |
BIN
docs/images/quartz-layout-desktop.png
Normal file
BIN
docs/images/quartz-layout-desktop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/images/quartz-layout-mobile.png
Normal file
BIN
docs/images/quartz-layout-mobile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/images/quartz-layout-tablet.png
Normal file
BIN
docs/images/quartz-layout-tablet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@ -13,15 +13,19 @@ export interface FullPageLayout {
|
||||
beforeBody: QuartzComponent[] // laid out vertically
|
||||
pageBody: QuartzComponent // single component
|
||||
afterBody: QuartzComponent[] // laid out vertically
|
||||
left: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
||||
right: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
||||
left: QuartzComponent[] // vertical on desktop and tablet, horizontal on mobile
|
||||
right: QuartzComponent[] // vertical on desktop, horizontal on tablet and mobile
|
||||
footer: QuartzComponent // single component
|
||||
}
|
||||
```
|
||||
|
||||
These correspond to following parts of the page:
|
||||
|
||||
![[quartz layout.png|800]]
|
||||
| Layout | Preview |
|
||||
| ------------------------------- | ----------------------------------- |
|
||||
| Desktop (width > 1200px) | ![[quartz-layout-desktop.png\|800]] |
|
||||
| Tablet (800px < width < 1200px) | ![[quartz-layout-tablet.png\|800]] |
|
||||
| Mobile (width < 800px) | ![[quartz-layout-mobile.png\|800]] |
|
||||
|
||||
> [!note]
|
||||
> There are two additional layout fields that are _not_ shown in the above diagram.
|
||||
@ -33,6 +37,23 @@ Quartz **components**, like plugins, can take in additional properties as config
|
||||
|
||||
See [a list of all the components](component.md) for all available components along with their configuration options. You can also checkout the guide on [[creating components]] if you're interested in further customizing the behaviour of Quartz.
|
||||
|
||||
### Layout breakpoints
|
||||
|
||||
Quartz has different layouts depending on the width the screen viewing the website.
|
||||
|
||||
The breakpoints for layouts can be configured in `variables.scss`.
|
||||
|
||||
- `mobile`: screen width below this size will use mobile layout.
|
||||
- `desktop`: screen width above this size will use desktop layout.
|
||||
- Screen width between `mobile` and `desktop` width will use the tablet layout.
|
||||
|
||||
```scss
|
||||
$breakpoints: (
|
||||
mobile: 800px,
|
||||
desktop: 1200px,
|
||||
);
|
||||
```
|
||||
|
||||
### Style
|
||||
|
||||
Most meaningful style changes like colour scheme and font can be done simply through the [[configuration#General Configuration|general configuration]] options. However, if you'd like to make more involved style changes, you can do this by writing your own styles. Quartz 4, like Quartz 3, uses [Sass](https://sass-lang.com/guide/) for styling.
|
||||
|
||||
26
docs/plugins/RoamFlavoredMarkdown.md
Normal file
26
docs/plugins/RoamFlavoredMarkdown.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: RoamFlavoredMarkdown
|
||||
tags:
|
||||
- plugin/transformer
|
||||
---
|
||||
|
||||
This plugin provides support for [Roam Research](https://roamresearch.com) compatibility. See [[Roam Research Compatibility]] for more information.
|
||||
|
||||
> [!note]
|
||||
> For information on how to add, remove or configure plugins, see the [[Configuration#Plugins|Configuration]] page.
|
||||
|
||||
This plugin accepts the following configuration options:
|
||||
|
||||
- `orComponent`: If `true` (default), converts Roam `{{ or:ONE|TWO|THREE }}` shortcodes into HTML Dropdown options.
|
||||
- `TODOComponent`: If `true` (default), converts Roam `{{[[TODO]]}}` shortcodes into HTML check boxes.
|
||||
- `DONEComponent`: If `true` (default), converts Roam `{{[[DONE]]}}` shortcodes into checked HTML check boxes.
|
||||
- `videoComponent`: If `true` (default), converts Roam `{{[[video]]:URL}}` shortcodes into embeded HTML video.
|
||||
- `audioComponent`: If `true` (default), converts Roam `{{[[audio]]:URL}}` shortcodes into embeded HTML audio.
|
||||
- `pdfComponent`: If `true` (default), converts Roam `{{[[pdf]]:URL}}` shortcodes into embeded HTML PDF viewer.
|
||||
- `blockquoteComponent`: If `true` (default), converts Roam `{{[[>]]}}` shortcodes into Quartz blockquotes.
|
||||
|
||||
## API
|
||||
|
||||
- Category: Transformer
|
||||
- Function name: `Plugin.RoamFlavoredMarkdown()`.
|
||||
- Source: [`quartz/plugins/transformers/roam.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/roam.ts).
|
||||
@ -20,12 +20,14 @@ Want to see what Quartz can do? Here are some cool community gardens:
|
||||
- [Sideny's 3D Artist's Handbook](https://sidney-eliot.github.io/3d-artists-handbook/)
|
||||
- [Brandon Boswell's Garden](https://brandonkboswell.com)
|
||||
- [Scaling Synthesis - A hypertext research notebook](https://scalingsynthesis.com/)
|
||||
- [Simon's Second Brain: Crafted, Curated, Connected, Compounded](https://brain.ssp.sh/)
|
||||
- [Data Engineering Vault: A Second Brain Knowledge Network](https://vault.ssp.sh/)
|
||||
- [Data Dictionary 🧠](https://glossary.airbyte.com/)
|
||||
- [sspaeti.com's Second Brain](https://brain.sspaeti.com/)
|
||||
- [🪴Aster's notebook](https://notes.asterhu.com)
|
||||
- [Gatekeeper Wiki](https://www.gatekeeper.wiki)
|
||||
- [Ellie's Notes](https://ellie.wtf)
|
||||
- [🥷🏻🌳🍃 Computer Science & Thinkering Garden](https://notes.yxy.ninja)
|
||||
- [Eledah's Crystalline](https://blog.eledah.ir/)
|
||||
- [🌓 Projects & Privacy - FOSS, tech, law](https://be-far.com)
|
||||
|
||||
If you want to see your own on here, submit a [Pull Request adding yourself to this file](https://github.com/jackyzha0/quartz/blob/v4/docs/showcase.md)!
|
||||
|
||||
1193
package-lock.json
generated
1193
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
@ -2,7 +2,7 @@
|
||||
"name": "@jackyzha0/quartz",
|
||||
"description": "🌱 publish your digital garden and notes as a website",
|
||||
"private": true,
|
||||
"version": "4.3.1",
|
||||
"version": "4.4.0",
|
||||
"type": "module",
|
||||
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
|
||||
"license": "MIT",
|
||||
@ -36,40 +36,40 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@napi-rs/simple-git": "0.1.19",
|
||||
"@tweenjs/tween.js": "^25.0.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"chalk": "^5.3.0",
|
||||
"chokidar": "^3.6.0",
|
||||
"chokidar": "^4.0.1",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"d3": "^7.9.0",
|
||||
"esbuild-sass-plugin": "^2.16.1",
|
||||
"esbuild-sass-plugin": "^3.3.1",
|
||||
"flexsearch": "0.7.43",
|
||||
"github-slugger": "^2.0.0",
|
||||
"globby": "^14.0.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hast-util-to-html": "^9.0.1",
|
||||
"hast-util-to-jsx-runtime": "^2.3.0",
|
||||
"hast-util-to-string": "^3.0.0",
|
||||
"hast-util-to-html": "^9.0.3",
|
||||
"hast-util-to-jsx-runtime": "^2.3.2",
|
||||
"hast-util-to-string": "^3.0.1",
|
||||
"is-absolute-url": "^4.0.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lightningcss": "^1.26.0",
|
||||
"lightningcss": "^1.27.0",
|
||||
"mdast-util-find-and-replace": "^3.0.1",
|
||||
"mdast-util-to-hast": "^13.2.0",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"micromorph": "^0.4.5",
|
||||
"pixi.js": "^8.3.3",
|
||||
"preact": "^10.23.2",
|
||||
"preact-render-to-string": "^6.5.9",
|
||||
"pixi.js": "^8.5.1",
|
||||
"preact": "^10.24.3",
|
||||
"preact-render-to-string": "^6.5.11",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"pretty-time": "^1.1.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-citation": "^2.1.1",
|
||||
"rehype-citation": "^2.2.0",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"rehype-mathjax": "^6.0.0",
|
||||
"rehype-pretty-code": "^0.13.2",
|
||||
"rehype-pretty-code": "^0.14.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark": "^15.0.1",
|
||||
@ -78,21 +78,21 @@
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.0",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"remark-smartypants": "^3.0.2",
|
||||
"rfdc": "^1.4.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"satori": "^0.10.14",
|
||||
"serve-handler": "^6.1.5",
|
||||
"serve-handler": "^6.1.6",
|
||||
"sharp": "^0.33.5",
|
||||
"shiki": "^1.12.1",
|
||||
"shiki": "^1.22.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"to-vfile": "^8.0.0",
|
||||
"toml": "^3.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.2",
|
||||
"workerpool": "^9.1.3",
|
||||
"vfile": "^6.0.3",
|
||||
"workerpool": "^9.2.0",
|
||||
"ws": "^8.18.0",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
@ -101,14 +101,14 @@
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^22.5.0",
|
||||
"@types/node": "^22.7.7",
|
||||
"@types/pretty-time": "^1.1.5",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"esbuild": "^0.19.9",
|
||||
"esbuild": "^0.24.0",
|
||||
"prettier": "^3.3.3",
|
||||
"tsx": "^4.18.0",
|
||||
"typescript": "^5.5.4"
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import * as Plugin from "./quartz/plugins"
|
||||
const config: QuartzConfig = {
|
||||
configuration: {
|
||||
pageTitle: "🪴 Quartz 4.0",
|
||||
pageTitleSuffix: "",
|
||||
enableSPA: true,
|
||||
enablePopovers: true,
|
||||
analytics: {
|
||||
|
||||
@ -39,7 +39,7 @@ type BuildData = {
|
||||
type FileEvent = "add" | "change" | "delete"
|
||||
|
||||
function newBuildId() {
|
||||
return new Date().toISOString()
|
||||
return Math.random().toString(36).substring(2, 8)
|
||||
}
|
||||
|
||||
async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
||||
@ -162,17 +162,19 @@ async function partialRebuildFromEntrypoint(
|
||||
return
|
||||
}
|
||||
|
||||
const buildStart = new Date().getTime()
|
||||
buildData.lastBuildMs = buildStart
|
||||
const buildId = newBuildId()
|
||||
ctx.buildId = buildId
|
||||
buildData.lastBuildMs = new Date().getTime()
|
||||
const release = await mut.acquire()
|
||||
if (buildData.lastBuildMs > buildStart) {
|
||||
|
||||
// if there's another build after us, release and let them do it
|
||||
if (ctx.buildId !== buildId) {
|
||||
release()
|
||||
return
|
||||
}
|
||||
|
||||
const perf = new PerfTimer()
|
||||
console.log(chalk.yellow("Detected change, rebuilding..."))
|
||||
ctx.buildId = newBuildId()
|
||||
|
||||
// UPDATE DEP GRAPH
|
||||
const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath
|
||||
@ -357,19 +359,19 @@ async function rebuildFromEntrypoint(
|
||||
toRemove.add(filePath)
|
||||
}
|
||||
|
||||
const buildStart = new Date().getTime()
|
||||
buildData.lastBuildMs = buildStart
|
||||
const buildId = newBuildId()
|
||||
ctx.buildId = buildId
|
||||
buildData.lastBuildMs = new Date().getTime()
|
||||
const release = await mut.acquire()
|
||||
|
||||
// there's another build after us, release and let them do it
|
||||
if (buildData.lastBuildMs > buildStart) {
|
||||
if (ctx.buildId !== buildId) {
|
||||
release()
|
||||
return
|
||||
}
|
||||
|
||||
const perf = new PerfTimer()
|
||||
console.log(chalk.yellow("Detected change, rebuilding..."))
|
||||
ctx.buildId = newBuildId()
|
||||
|
||||
try {
|
||||
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
|
||||
@ -405,10 +407,10 @@ async function rebuildFromEntrypoint(
|
||||
}
|
||||
}
|
||||
|
||||
release()
|
||||
clientRefresh()
|
||||
toRebuild.clear()
|
||||
toRemove.clear()
|
||||
release()
|
||||
}
|
||||
|
||||
export default async (argv: Argv, mut: Mutex, clientRefresh: () => void) => {
|
||||
|
||||
@ -39,9 +39,14 @@ export type Analytics =
|
||||
provider: "cabin"
|
||||
host?: string
|
||||
}
|
||||
| {
|
||||
provider: "clarity"
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
export interface GlobalConfiguration {
|
||||
pageTitle: string
|
||||
pageTitleSuffix?: string
|
||||
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
|
||||
enableSPA: boolean
|
||||
/** Whether to display Wikipedia-style popovers when hovering over links */
|
||||
|
||||
@ -466,7 +466,25 @@ export async function handleUpdate(argv) {
|
||||
|
||||
await popContentFolder(contentFolder)
|
||||
console.log("Ensuring dependencies are up to date")
|
||||
const res = spawnSync("npm", ["i"], { stdio: "inherit" })
|
||||
|
||||
/*
|
||||
On Windows, if the command `npm` is really `npm.cmd', this call fails
|
||||
as it will be unable to find `npm`. This is often the case on systems
|
||||
where `npm` is installed via a package manager.
|
||||
|
||||
This means `npx quartz update` will not actually update dependencies
|
||||
on Windows, without a manual `npm i` from the caller.
|
||||
|
||||
However, by spawning a shell, we are able to call `npm.cmd`.
|
||||
See: https://nodejs.org/api/child_process.html#spawning-bat-and-cmd-files-on-windows
|
||||
*/
|
||||
|
||||
const opts = { stdio: "inherit" }
|
||||
if (process.platform === "win32") {
|
||||
opts.shell = true
|
||||
}
|
||||
|
||||
const res = spawnSync("npm", ["i"], opts)
|
||||
if (res.status === 0) {
|
||||
console.log(chalk.green("Done!"))
|
||||
} else {
|
||||
|
||||
@ -10,6 +10,9 @@ type Options = {
|
||||
repoId: string
|
||||
category: string
|
||||
categoryId: string
|
||||
themeUrl?: string
|
||||
lightTheme?: string
|
||||
darkTheme?: string
|
||||
mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
|
||||
strict?: boolean
|
||||
reactionsEnabled?: boolean
|
||||
@ -34,6 +37,11 @@ export default ((opts: Options) => {
|
||||
data-strict={boolToStringBool(opts.options.strict ?? true)}
|
||||
data-reactions-enabled={boolToStringBool(opts.options.reactionsEnabled ?? true)}
|
||||
data-input-position={opts.options.inputPosition ?? "bottom"}
|
||||
data-light-theme={opts.options.lightTheme ?? "light"}
|
||||
data-dark-theme={opts.options.darkTheme ?? "dark"}
|
||||
data-theme-url={
|
||||
opts.options.themeUrl ?? `https://${cfg.baseUrl ?? "example.com"}/static/giscus`
|
||||
}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export default (() => {
|
||||
let fontsPromise: Promise<SatoriOptions["fonts"]>
|
||||
|
||||
let fullOptions: SocialImageOptions
|
||||
function Head({ cfg, fileData, externalResources, ctx }: QuartzComponentProps) {
|
||||
const Head: QuartzComponent = ({ cfg, fileData, externalResources, ctx }: QuartzComponentProps) => {
|
||||
// Initialize options if not set
|
||||
if (!fullOptions) {
|
||||
if (typeof cfg.generateSocialImages !== "boolean") {
|
||||
@ -75,6 +75,11 @@ export default (() => {
|
||||
// Get file description (priority: frontmatter > fileData > default)
|
||||
const fdDescription =
|
||||
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
|
||||
const titleSuffix = cfg.pageTitleSuffix ?? ""
|
||||
const title =
|
||||
(fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title) + titleSuffix
|
||||
const description =
|
||||
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
|
||||
let description = ""
|
||||
if (fdDescription) {
|
||||
description = unescapeHTML(fdDescription)
|
||||
|
||||
@ -2,22 +2,25 @@ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } fro
|
||||
import path from "path"
|
||||
|
||||
import style from "../styles/listPage.scss"
|
||||
import { PageList, SortFn } from "../PageList"
|
||||
import { stripSlashes, simplifySlug } from "../../util/path"
|
||||
import { byDateAndAlphabetical, PageList, SortFn } from "../PageList"
|
||||
import { stripSlashes, simplifySlug, joinSegments, FullSlug } from "../../util/path"
|
||||
import { Root } from "hast"
|
||||
import { htmlToJsx } from "../../util/jsx"
|
||||
import { i18n } from "../../i18n"
|
||||
import { QuartzPluginData } from "../../plugins/vfile"
|
||||
|
||||
interface FolderContentOptions {
|
||||
/**
|
||||
* Whether to display number of folders
|
||||
*/
|
||||
showFolderCount: boolean
|
||||
showSubfolders: boolean
|
||||
sort?: SortFn
|
||||
}
|
||||
|
||||
const defaultOptions: FolderContentOptions = {
|
||||
showFolderCount: true,
|
||||
showSubfolders: true,
|
||||
}
|
||||
|
||||
export default ((opts?: Partial<FolderContentOptions>) => {
|
||||
@ -26,14 +29,47 @@ export default ((opts?: Partial<FolderContentOptions>) => {
|
||||
const FolderContent: QuartzComponent = (props: QuartzComponentProps) => {
|
||||
const { tree, fileData, allFiles, cfg } = props
|
||||
const folderSlug = stripSlashes(simplifySlug(fileData.slug!))
|
||||
const allPagesInFolder = allFiles.filter((file) => {
|
||||
const folderParts = folderSlug.split(path.posix.sep)
|
||||
|
||||
const allPagesInFolder: QuartzPluginData[] = []
|
||||
const allPagesInSubfolders: Map<FullSlug, QuartzPluginData[]> = new Map()
|
||||
|
||||
allFiles.forEach((file) => {
|
||||
const fileSlug = stripSlashes(simplifySlug(file.slug!))
|
||||
const prefixed = fileSlug.startsWith(folderSlug) && fileSlug !== folderSlug
|
||||
const folderParts = folderSlug.split(path.posix.sep)
|
||||
const fileParts = fileSlug.split(path.posix.sep)
|
||||
const isDirectChild = fileParts.length === folderParts.length + 1
|
||||
return prefixed && isDirectChild
|
||||
|
||||
if (!prefixed) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isDirectChild) {
|
||||
allPagesInFolder.push(file)
|
||||
} else if (options.showSubfolders) {
|
||||
const subfolderSlug = joinSegments(
|
||||
...fileParts.slice(0, folderParts.length + 1),
|
||||
) as FullSlug
|
||||
const pagesInFolder = allPagesInSubfolders.get(subfolderSlug) || []
|
||||
allPagesInSubfolders.set(subfolderSlug, [...pagesInFolder, file])
|
||||
}
|
||||
})
|
||||
|
||||
allPagesInSubfolders.forEach((files, subfolderSlug) => {
|
||||
const hasIndex = allPagesInFolder.some(
|
||||
(file) => subfolderSlug === stripSlashes(simplifySlug(file.slug!)),
|
||||
)
|
||||
if (!hasIndex) {
|
||||
const subfolderDates = files.sort(byDateAndAlphabetical(cfg))[0].dates
|
||||
const subfolderTitle = subfolderSlug.split(path.posix.sep).at(-1)!
|
||||
allPagesInFolder.push({
|
||||
slug: subfolderSlug,
|
||||
dates: subfolderDates,
|
||||
frontmatter: { title: subfolderTitle, tags: ["folder"] },
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
|
||||
const classes = ["popover-hint", ...cssClasses].join(" ")
|
||||
const listProps = {
|
||||
|
||||
@ -242,8 +242,8 @@ export function renderPage(
|
||||
</div>
|
||||
</div>
|
||||
{RightComponent}
|
||||
<Footer {...componentData} />
|
||||
</Body>
|
||||
<Footer {...componentData} />
|
||||
</div>
|
||||
</body>
|
||||
{pageResources.js
|
||||
|
||||
@ -13,7 +13,7 @@ const changeTheme = (e: CustomEventMap["themechange"]) => {
|
||||
{
|
||||
giscus: {
|
||||
setConfig: {
|
||||
theme: theme,
|
||||
theme: getThemeUrl(getThemeName(theme)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -21,12 +21,36 @@ const changeTheme = (e: CustomEventMap["themechange"]) => {
|
||||
)
|
||||
}
|
||||
|
||||
const getThemeName = (theme: string) => {
|
||||
if (theme !== "dark" && theme !== "light") {
|
||||
return theme
|
||||
}
|
||||
const giscusContainer = document.querySelector(".giscus") as GiscusElement
|
||||
if (!giscusContainer) {
|
||||
return theme
|
||||
}
|
||||
const darkGiscus = giscusContainer.dataset.darkTheme ?? "dark"
|
||||
const lightGiscus = giscusContainer.dataset.lightTheme ?? "light"
|
||||
return theme === "dark" ? darkGiscus : lightGiscus
|
||||
}
|
||||
|
||||
const getThemeUrl = (theme: string) => {
|
||||
const giscusContainer = document.querySelector(".giscus") as GiscusElement
|
||||
if (!giscusContainer) {
|
||||
return `https://giscus.app/themes/${theme}.css`
|
||||
}
|
||||
return `${giscusContainer.dataset.themeUrl ?? "https://giscus.app/themes"}/${theme}.css`
|
||||
}
|
||||
|
||||
type GiscusElement = Omit<HTMLElement, "dataset"> & {
|
||||
dataset: DOMStringMap & {
|
||||
repo: `${string}/${string}`
|
||||
repoId: string
|
||||
category: string
|
||||
categoryId: string
|
||||
themeUrl: string
|
||||
lightTheme: string
|
||||
darkTheme: string
|
||||
mapping: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
|
||||
strict: string
|
||||
reactionsEnabled: string
|
||||
@ -57,7 +81,7 @@ document.addEventListener("nav", () => {
|
||||
|
||||
const theme = document.documentElement.getAttribute("saved-theme")
|
||||
if (theme) {
|
||||
giscusScript.setAttribute("data-theme", theme)
|
||||
giscusScript.setAttribute("data-theme", getThemeUrl(getThemeName(theme)))
|
||||
}
|
||||
|
||||
giscusContainer.appendChild(giscusScript)
|
||||
|
||||
@ -25,7 +25,6 @@ function toggleExplorer(this: HTMLElement) {
|
||||
if (!content) return
|
||||
|
||||
content.classList.toggle("collapsed")
|
||||
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
||||
}
|
||||
|
||||
function toggleFolder(evt: MouseEvent) {
|
||||
|
||||
@ -550,6 +550,19 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
||||
addToVisited(simplifySlug(slug))
|
||||
await renderGraph("graph-container", slug)
|
||||
|
||||
// Function to re-render the graph when the theme changes
|
||||
const handleThemeChange = () => {
|
||||
renderGraph("graph-container", slug)
|
||||
}
|
||||
|
||||
// event listener for theme change
|
||||
document.addEventListener("themechange", handleThemeChange)
|
||||
|
||||
// cleanup for the event listener
|
||||
window.addCleanup(() => {
|
||||
document.removeEventListener("themechange", handleThemeChange)
|
||||
})
|
||||
|
||||
const container = document.getElementById("global-graph-outer")
|
||||
const sidebar = container?.closest(".sidebar") as HTMLElement
|
||||
|
||||
|
||||
@ -23,7 +23,6 @@ function toggleToc(this: HTMLElement) {
|
||||
const content = this.nextElementSibling as HTMLElement | undefined
|
||||
if (!content) return
|
||||
content.classList.toggle("collapsed")
|
||||
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
||||
}
|
||||
|
||||
function setupToc() {
|
||||
@ -32,7 +31,6 @@ function setupToc() {
|
||||
const collapsed = toc.classList.contains("collapsed")
|
||||
const content = toc.nextElementSibling as HTMLElement | undefined
|
||||
if (!content) return
|
||||
content.style.maxHeight = collapsed ? "0px" : content.scrollHeight + "px"
|
||||
toc.addEventListener("click", toggleToc)
|
||||
window.addCleanup(() => toc.removeEventListener("click", toggleToc))
|
||||
}
|
||||
|
||||
@ -1,5 +1,19 @@
|
||||
@use "../../styles/variables.scss" as *;
|
||||
|
||||
.backlinks {
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
/*&:after {
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
background: linear-gradient(transparent 0px, var(--light));
|
||||
}*/
|
||||
|
||||
& > h3 {
|
||||
font-size: 1rem;
|
||||
@ -17,4 +31,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .overflow {
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
height: auto;
|
||||
@media all and not ($desktop) {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,28 @@
|
||||
@use "../../styles/variables.scss" as *;
|
||||
|
||||
.explorer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: hidden;
|
||||
&.desktop-only {
|
||||
@media all and not ($mobile) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
/*&:after {
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
background: linear-gradient(transparent 0px, var(--light));
|
||||
}*/
|
||||
}
|
||||
|
||||
button#explorer {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
@ -44,7 +67,8 @@ button#explorer {
|
||||
#explorer-content {
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
max-height: 100%;
|
||||
transition:
|
||||
max-height 0.35s ease,
|
||||
visibility 0s linear 0s;
|
||||
@ -52,16 +76,13 @@ button#explorer {
|
||||
visibility: visible;
|
||||
|
||||
&.collapsed {
|
||||
max-height: 0;
|
||||
transition:
|
||||
max-height 0.35s ease,
|
||||
visibility 0s linear 0.35s;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&.collapsed > .overflow::after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& ul {
|
||||
list-style: none;
|
||||
margin: 0.08rem 0;
|
||||
@ -76,6 +97,9 @@ button#explorer {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
> #explorer-ul {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
height: 80vh;
|
||||
width: 80vw;
|
||||
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
@media all and not ($desktop) {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ li.section-li {
|
||||
display: grid;
|
||||
grid-template-columns: fit-content(8em) 3fr 1fr;
|
||||
|
||||
@media all and (max-width: $mobileBreakpoint) {
|
||||
@media all and ($mobile) {
|
||||
& > .tags {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
opacity 0.3s ease,
|
||||
visibility 0.3s ease;
|
||||
|
||||
@media all and (max-width: $mobileBreakpoint) {
|
||||
@media all and ($mobile) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,9 @@
|
||||
.search {
|
||||
min-width: fit-content;
|
||||
max-width: 14rem;
|
||||
flex-grow: 0.3;
|
||||
@media all and ($mobile) {
|
||||
flex-grow: 0.3;
|
||||
}
|
||||
|
||||
& > .search-button {
|
||||
background-color: var(--lightgray);
|
||||
@ -62,7 +64,7 @@
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
@media all and not ($desktop) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
@ -104,7 +106,7 @@
|
||||
flex: 0 0 min(30%, 450px);
|
||||
}
|
||||
|
||||
@media all and (min-width: $tabletBreakpoint) {
|
||||
@media all and not ($tablet) {
|
||||
&[data-preview] {
|
||||
& .result-card > p.preview {
|
||||
display: none;
|
||||
@ -130,7 +132,7 @@
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
@media all and (max-width: $tabletBreakpoint) {
|
||||
@media all and ($tablet) {
|
||||
& > #preview-container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@ -1,3 +1,20 @@
|
||||
@use "../../styles/variables.scss" as *;
|
||||
|
||||
.toc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.desktop-only {
|
||||
max-height: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and not ($mobile) {
|
||||
.toc {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
button#toc {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
@ -28,17 +45,19 @@ button#toc {
|
||||
#toc-content {
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
max-height: 100%;
|
||||
transition:
|
||||
max-height 0.5s ease,
|
||||
max-height 0.35s ease,
|
||||
visibility 0s linear 0s;
|
||||
position: relative;
|
||||
visibility: visible;
|
||||
|
||||
&.collapsed {
|
||||
max-height: 0;
|
||||
transition:
|
||||
max-height 0.5s ease,
|
||||
visibility 0s linear 0.5s;
|
||||
max-height 0.35s ease,
|
||||
visibility 0s linear 0.35s;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@ -61,6 +80,10 @@ button#toc {
|
||||
}
|
||||
}
|
||||
}
|
||||
> ul.overflow {
|
||||
max-height: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@for $i from 0 through 6 {
|
||||
& .depth-#{$i} {
|
||||
|
||||
@ -19,6 +19,7 @@ import pt from "./locales/pt-BR"
|
||||
import hu from "./locales/hu-HU"
|
||||
import fa from "./locales/fa-IR"
|
||||
import pl from "./locales/pl-PL"
|
||||
import cs from "./locales/cs-CZ"
|
||||
|
||||
export const TRANSLATIONS = {
|
||||
"en-US": enUs,
|
||||
@ -62,6 +63,7 @@ export const TRANSLATIONS = {
|
||||
"hu-HU": hu,
|
||||
"fa-IR": fa,
|
||||
"pl-PL": pl,
|
||||
"cs-CZ": cs,
|
||||
} as const
|
||||
|
||||
export const defaultTranslation = "en-US"
|
||||
|
||||
84
quartz/i18n/locales/cs-CZ.ts
Normal file
84
quartz/i18n/locales/cs-CZ.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { Translation } from "./definition"
|
||||
|
||||
export default {
|
||||
propertyDefaults: {
|
||||
title: "Bez názvu",
|
||||
description: "Nebyl uveden žádný popis",
|
||||
},
|
||||
components: {
|
||||
callout: {
|
||||
note: "Poznámka",
|
||||
abstract: "Abstract",
|
||||
info: "Info",
|
||||
todo: "Todo",
|
||||
tip: "Tip",
|
||||
success: "Úspěch",
|
||||
question: "Otázka",
|
||||
warning: "Upozornění",
|
||||
failure: "Chyba",
|
||||
danger: "Nebezpečí",
|
||||
bug: "Bug",
|
||||
example: "Příklad",
|
||||
quote: "Citace",
|
||||
},
|
||||
backlinks: {
|
||||
title: "Příchozí odkazy",
|
||||
noBacklinksFound: "Nenalezeny žádné příchozí odkazy",
|
||||
},
|
||||
themeToggle: {
|
||||
lightMode: "Světlý režim",
|
||||
darkMode: "Tmavý režim",
|
||||
},
|
||||
explorer: {
|
||||
title: "Procházet",
|
||||
},
|
||||
footer: {
|
||||
createdWith: "Vytvořeno pomocí",
|
||||
},
|
||||
graph: {
|
||||
title: "Graf",
|
||||
},
|
||||
recentNotes: {
|
||||
title: "Nejnovější poznámky",
|
||||
seeRemainingMore: ({ remaining }) => `Zobraz ${remaining} dalších →`,
|
||||
},
|
||||
transcludes: {
|
||||
transcludeOf: ({ targetSlug }) => `Zobrazení ${targetSlug}`,
|
||||
linkToOriginal: "Odkaz na původní dokument",
|
||||
},
|
||||
search: {
|
||||
title: "Hledat",
|
||||
searchBarPlaceholder: "Hledejte něco",
|
||||
},
|
||||
tableOfContents: {
|
||||
title: "Obsah",
|
||||
},
|
||||
contentMeta: {
|
||||
readingTime: ({ minutes }) => `${minutes} min čtení`,
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
rss: {
|
||||
recentNotes: "Nejnovější poznámky",
|
||||
lastFewNotes: ({ count }) => `Posledních ${count} poznámek`,
|
||||
},
|
||||
error: {
|
||||
title: "Nenalezeno",
|
||||
notFound: "Tato stránka je buď soukromá, nebo neexistuje.",
|
||||
home: "Návrat na domovskou stránku",
|
||||
},
|
||||
folderContent: {
|
||||
folder: "Složka",
|
||||
itemsUnderFolder: ({ count }) =>
|
||||
count === 1 ? "1 položka v této složce." : `${count} položek v této složce.`,
|
||||
},
|
||||
tagContent: {
|
||||
tag: "Tag",
|
||||
tagIndex: "Rejstřík tagů",
|
||||
itemsUnderTag: ({ count }) =>
|
||||
count === 1 ? "1 položka s tímto tagem." : `${count} položek s tímto tagem.`,
|
||||
showingFirst: ({ count }) => `Zobrazují se první ${count} tagy.`,
|
||||
totalTags: ({ count }) => `Nalezeno celkem ${count} tagů.`,
|
||||
},
|
||||
},
|
||||
} as const satisfies Translation
|
||||
@ -147,11 +147,20 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso
|
||||
} else if (cfg.analytics?.provider === "cabin") {
|
||||
componentResources.afterDOMLoaded.push(`
|
||||
const cabinScript = document.createElement("script")
|
||||
cabinScript.src = "${cfg.analytics.host ?? "https://scripts.cabin.dev"}/cabin.js"
|
||||
cabinScript.src = "${cfg.analytics.host ?? "https://scripts.withcabin.com"}/hello.js"
|
||||
cabinScript.defer = true
|
||||
cabinScript.async = true
|
||||
document.head.appendChild(cabinScript)
|
||||
`)
|
||||
} else if (cfg.analytics?.provider === "clarity") {
|
||||
componentResources.afterDOMLoaded.push(`
|
||||
const clarityScript = document.createElement("script")
|
||||
clarityScript.innerHTML= \`(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||
})(window, document, "clarity", "script", "${cfg.analytics.projectId}");\`
|
||||
document.head.appendChild(clarityScript)
|
||||
`)
|
||||
}
|
||||
|
||||
if (cfg.enableSPA) {
|
||||
|
||||
@ -76,12 +76,11 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (user
|
||||
|
||||
const folders: Set<SimpleSlug> = new Set(
|
||||
allFiles.flatMap((data) => {
|
||||
const slug = data.slug
|
||||
const folderName = path.dirname(slug ?? "") as SimpleSlug
|
||||
if (slug && folderName !== "." && folderName !== "tags") {
|
||||
return [folderName]
|
||||
}
|
||||
return []
|
||||
return data.slug
|
||||
? _getFolders(data.slug).filter(
|
||||
(folderName) => folderName !== "." && folderName !== "tags",
|
||||
)
|
||||
: []
|
||||
}),
|
||||
)
|
||||
|
||||
@ -133,3 +132,14 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (user
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function _getFolders(slug: FullSlug): SimpleSlug[] {
|
||||
var folderName = path.dirname(slug ?? "") as SimpleSlug
|
||||
const parentFolderNames = [folderName]
|
||||
|
||||
while (folderName !== ".") {
|
||||
folderName = path.dirname(folderName ?? "") as SimpleSlug
|
||||
parentFolderNames.push(folderName)
|
||||
}
|
||||
return parentFolderNames
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ import { QuartzFilterPlugin } from "../types"
|
||||
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
|
||||
name: "RemoveDrafts",
|
||||
shouldPublish(_ctx, [_tree, vfile]) {
|
||||
const draftFlag: boolean = vfile.data?.frontmatter?.draft || false
|
||||
const draftFlag: boolean =
|
||||
vfile.data?.frontmatter?.draft === true || vfile.data?.frontmatter?.draft === "true"
|
||||
return !draftFlag
|
||||
},
|
||||
})
|
||||
|
||||
@ -3,6 +3,6 @@ import { QuartzFilterPlugin } from "../types"
|
||||
export const ExplicitPublish: QuartzFilterPlugin = () => ({
|
||||
name: "ExplicitPublish",
|
||||
shouldPublish(_ctx, [_tree, vfile]) {
|
||||
return vfile.data?.frontmatter?.publish ?? false
|
||||
return vfile.data?.frontmatter?.publish === true || vfile.data?.frontmatter?.publish === "true"
|
||||
},
|
||||
})
|
||||
|
||||
@ -21,7 +21,7 @@ export const Citations: QuartzTransformerPlugin<Partial<Options>> = (userOpts) =
|
||||
const opts = { ...defaultOptions, ...userOpts }
|
||||
return {
|
||||
name: "Citations",
|
||||
htmlPlugins() {
|
||||
htmlPlugins(ctx) {
|
||||
const plugins: PluggableList = []
|
||||
|
||||
// Add rehype-citation to the list of plugins
|
||||
@ -31,6 +31,8 @@ export const Citations: QuartzTransformerPlugin<Partial<Options>> = (userOpts) =
|
||||
bibliography: opts.bibliographyFile,
|
||||
suppressBibliography: opts.suppressBibliography,
|
||||
linkCitations: opts.linkCitations,
|
||||
csl: opts.csl,
|
||||
lang: ctx.cfg.configuration.locale ?? "en-US",
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
@ -88,8 +88,8 @@ declare module "vfile" {
|
||||
tags: string[]
|
||||
aliases: string[]
|
||||
description: string
|
||||
publish: boolean
|
||||
draft: boolean
|
||||
publish: boolean | string
|
||||
draft: boolean | string
|
||||
lang: string
|
||||
enableToc: string
|
||||
cssclasses: string[]
|
||||
|
||||
@ -10,3 +10,4 @@ export { OxHugoFlavouredMarkdown } from "./oxhugofm"
|
||||
export { SyntaxHighlighting } from "./syntax"
|
||||
export { TableOfContents } from "./toc"
|
||||
export { HardLineBreaks } from "./linebreaks"
|
||||
export { RoamFlavoredMarkdown } from "./roam"
|
||||
|
||||
@ -67,6 +67,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
|
||||
properties: {
|
||||
"aria-hidden": "true",
|
||||
class: "external-icon",
|
||||
style: "max-width:0.8em;max-height:0.8em",
|
||||
viewBox: "0 0 512 512",
|
||||
},
|
||||
children: [
|
||||
|
||||
@ -119,7 +119,7 @@ export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/g)
|
||||
const highlightRegex = new RegExp(/==([^=]+)==/g)
|
||||
const commentRegex = new RegExp(/%%[\s\S]*?%%/g)
|
||||
// from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
|
||||
const calloutRegex = new RegExp(/^\[\!(\w+)\|?(.+?)?\]([+-]?)/)
|
||||
const 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
|
||||
// #(...) -> capturing group, tag itself must start with #
|
||||
@ -324,8 +324,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
||||
replacements.push([
|
||||
tagRegex,
|
||||
(_value: string, tag: string) => {
|
||||
// Check if the tag only includes numbers
|
||||
if (/^\d+$/.test(tag)) {
|
||||
// Check if the tag only includes numbers and slashes
|
||||
if (/^[\/\d]+$/.test(tag)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -430,7 +430,9 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
value: useDefaultTitle ? capitalize(typeString) : titleContent + " ",
|
||||
value: useDefaultTitle
|
||||
? capitalize(typeString).replace(/-/g, " ")
|
||||
: titleContent + " ",
|
||||
},
|
||||
...restOfTitle,
|
||||
],
|
||||
|
||||
224
quartz/plugins/transformers/roam.ts
Normal file
224
quartz/plugins/transformers/roam.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import { QuartzTransformerPlugin } from "../types"
|
||||
import { PluggableList } from "unified"
|
||||
import { SKIP, visit } from "unist-util-visit"
|
||||
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||
import { Root, Html, Paragraph, Text, Link, Parent } from "mdast"
|
||||
import { Node } from "unist"
|
||||
import { VFile } from "vfile"
|
||||
import { BuildVisitor } from "unist-util-visit"
|
||||
|
||||
export interface Options {
|
||||
orComponent: boolean
|
||||
TODOComponent: boolean
|
||||
DONEComponent: boolean
|
||||
videoComponent: boolean
|
||||
audioComponent: boolean
|
||||
pdfComponent: boolean
|
||||
blockquoteComponent: boolean
|
||||
tableComponent: boolean
|
||||
attributeComponent: boolean
|
||||
}
|
||||
|
||||
const defaultOptions: Options = {
|
||||
orComponent: true,
|
||||
TODOComponent: true,
|
||||
DONEComponent: true,
|
||||
videoComponent: true,
|
||||
audioComponent: true,
|
||||
pdfComponent: true,
|
||||
blockquoteComponent: true,
|
||||
tableComponent: true,
|
||||
attributeComponent: true,
|
||||
}
|
||||
|
||||
const orRegex = new RegExp(/{{or:(.*?)}}/, "g")
|
||||
const TODORegex = new RegExp(/{{.*?\bTODO\b.*?}}/, "g")
|
||||
const DONERegex = new RegExp(/{{.*?\bDONE\b.*?}}/, "g")
|
||||
const videoRegex = new RegExp(/{{.*?\[\[video\]\].*?\:(.*?)}}/, "g")
|
||||
const youtubeRegex = new RegExp(
|
||||
/{{.*?\[\[video\]\].*?(https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?)}}/,
|
||||
"g",
|
||||
)
|
||||
|
||||
// const multimediaRegex = new RegExp(/{{.*?\b(video|audio)\b.*?\:(.*?)}}/, "g")
|
||||
|
||||
const audioRegex = new RegExp(/{{.*?\[\[audio\]\].*?\:(.*?)}}/, "g")
|
||||
const pdfRegex = new RegExp(/{{.*?\[\[pdf\]\].*?\:(.*?)}}/, "g")
|
||||
const blockquoteRegex = new RegExp(/(\[\[>\]\])\s*(.*)/, "g")
|
||||
const roamHighlightRegex = new RegExp(/\^\^(.+)\^\^/, "g")
|
||||
const roamItalicRegex = new RegExp(/__(.+)__/, "g")
|
||||
const tableRegex = new RegExp(/- {{.*?\btable\b.*?}}/, "g") /* TODO */
|
||||
const attributeRegex = new RegExp(/\b\w+(?:\s+\w+)*::/, "g") /* TODO */
|
||||
|
||||
function isSpecialEmbed(node: Paragraph): boolean {
|
||||
if (node.children.length !== 2) return false
|
||||
|
||||
const [textNode, linkNode] = node.children
|
||||
return (
|
||||
textNode.type === "text" &&
|
||||
textNode.value.startsWith("{{[[") &&
|
||||
linkNode.type === "link" &&
|
||||
linkNode.children[0].type === "text" &&
|
||||
linkNode.children[0].value.endsWith("}}")
|
||||
)
|
||||
}
|
||||
|
||||
function transformSpecialEmbed(node: Paragraph, opts: Options): Html | null {
|
||||
const [textNode, linkNode] = node.children as [Text, Link]
|
||||
const embedType = textNode.value.match(/\{\{\[\[(.*?)\]\]:/)?.[1]?.toLowerCase()
|
||||
const url = linkNode.url.slice(0, -2) // Remove the trailing '}}'
|
||||
|
||||
switch (embedType) {
|
||||
case "audio":
|
||||
return opts.audioComponent
|
||||
? {
|
||||
type: "html",
|
||||
value: `<audio controls>
|
||||
<source src="${url}" type="audio/mpeg">
|
||||
<source src="${url}" type="audio/ogg">
|
||||
Your browser does not support the audio tag.
|
||||
</audio>`,
|
||||
}
|
||||
: null
|
||||
case "video":
|
||||
if (!opts.videoComponent) return null
|
||||
// Check if it's a YouTube video
|
||||
const youtubeMatch = url.match(
|
||||
/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)/,
|
||||
)
|
||||
if (youtubeMatch) {
|
||||
const videoId = youtubeMatch[1].split("&")[0] // Remove additional parameters
|
||||
const playlistMatch = url.match(/[?&]list=([^#\&\?]*)/)
|
||||
const playlistId = playlistMatch ? playlistMatch[1] : null
|
||||
|
||||
return {
|
||||
type: "html",
|
||||
value: `<iframe
|
||||
class="external-embed youtube"
|
||||
width="600px"
|
||||
height="350px"
|
||||
src="https://www.youtube.com/embed/${videoId}${playlistId ? `?list=${playlistId}` : ""}"
|
||||
frameborder="0"
|
||||
allow="fullscreen"
|
||||
></iframe>`,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
type: "html",
|
||||
value: `<video controls>
|
||||
<source src="${url}" type="video/mp4">
|
||||
<source src="${url}" type="video/webm">
|
||||
Your browser does not support the video tag.
|
||||
</video>`,
|
||||
}
|
||||
}
|
||||
case "pdf":
|
||||
return opts.pdfComponent
|
||||
? {
|
||||
type: "html",
|
||||
value: `<embed src="${url}" type="application/pdf" width="100%" height="600px" />`,
|
||||
}
|
||||
: null
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const RoamFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (
|
||||
userOpts,
|
||||
) => {
|
||||
const opts = { ...defaultOptions, ...userOpts }
|
||||
|
||||
return {
|
||||
name: "RoamFlavoredMarkdown",
|
||||
markdownPlugins() {
|
||||
const plugins: PluggableList = []
|
||||
|
||||
plugins.push(() => {
|
||||
return (tree: Root, file: VFile) => {
|
||||
const replacements: [RegExp, ReplaceFunction][] = []
|
||||
|
||||
// Handle special embeds (audio, video, PDF)
|
||||
if (opts.audioComponent || opts.videoComponent || opts.pdfComponent) {
|
||||
visit(tree, "paragraph", ((node: Paragraph, index: number, parent: Parent | null) => {
|
||||
if (isSpecialEmbed(node)) {
|
||||
const transformedNode = transformSpecialEmbed(node, opts)
|
||||
if (transformedNode && parent) {
|
||||
parent.children[index] = transformedNode
|
||||
}
|
||||
}
|
||||
}) as BuildVisitor<Root, "paragraph">)
|
||||
}
|
||||
|
||||
// Roam italic syntax
|
||||
replacements.push([
|
||||
roamItalicRegex,
|
||||
(_value: string, match: string) => ({
|
||||
type: "emphasis",
|
||||
children: [{ type: "text", value: match }],
|
||||
}),
|
||||
])
|
||||
|
||||
// Roam highlight syntax
|
||||
replacements.push([
|
||||
roamHighlightRegex,
|
||||
(_value: string, inner: string) => ({
|
||||
type: "html",
|
||||
value: `<span class="text-highlight">${inner}</span>`,
|
||||
}),
|
||||
])
|
||||
|
||||
if (opts.orComponent) {
|
||||
replacements.push([
|
||||
orRegex,
|
||||
(match: string) => {
|
||||
const matchResult = match.match(/{{or:(.*?)}}/)
|
||||
if (matchResult === null) {
|
||||
return { type: "html", value: "" }
|
||||
}
|
||||
const optionsString: string = matchResult[1]
|
||||
const options: string[] = optionsString.split("|")
|
||||
const selectHtml: string = `<select>${options.map((option: string) => `<option value="${option}">${option}</option>`).join("")}</select>`
|
||||
return { type: "html", value: selectHtml }
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
if (opts.TODOComponent) {
|
||||
replacements.push([
|
||||
TODORegex,
|
||||
() => ({
|
||||
type: "html",
|
||||
value: `<input type="checkbox" disabled>`,
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
if (opts.DONEComponent) {
|
||||
replacements.push([
|
||||
DONERegex,
|
||||
() => ({
|
||||
type: "html",
|
||||
value: `<input type="checkbox" checked disabled>`,
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
if (opts.blockquoteComponent) {
|
||||
replacements.push([
|
||||
blockquoteRegex,
|
||||
(_match: string, _marker: string, content: string) => ({
|
||||
type: "html",
|
||||
value: `<blockquote>${content.trim()}</blockquote>`,
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
mdastFindReplace(tree, replacements)
|
||||
}
|
||||
})
|
||||
|
||||
return plugins
|
||||
},
|
||||
}
|
||||
}
|
||||
99
quartz/static/giscus/dark.css
Normal file
99
quartz/static/giscus/dark.css
Normal file
@ -0,0 +1,99 @@
|
||||
/*! MIT License
|
||||
* Copyright (c) 2018 GitHub Inc.
|
||||
* https://github.com/primer/primitives/blob/main/LICENSE
|
||||
*/
|
||||
|
||||
main {
|
||||
--color-prettylights-syntax-comment: #8b949e;
|
||||
--color-prettylights-syntax-constant: #79c0ff;
|
||||
--color-prettylights-syntax-entity: #d2a8ff;
|
||||
--color-prettylights-syntax-storage-modifier-import: #c9d1d9;
|
||||
--color-prettylights-syntax-entity-tag: #7ee787;
|
||||
--color-prettylights-syntax-keyword: #ff7b72;
|
||||
--color-prettylights-syntax-string: #a5d6ff;
|
||||
--color-prettylights-syntax-variable: #ffa657;
|
||||
--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
|
||||
--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
|
||||
--color-prettylights-syntax-invalid-illegal-bg: #8e1519;
|
||||
--color-prettylights-syntax-carriage-return-text: #f0f6fc;
|
||||
--color-prettylights-syntax-carriage-return-bg: #b62324;
|
||||
--color-prettylights-syntax-string-regexp: #7ee787;
|
||||
--color-prettylights-syntax-markup-list: #f2cc60;
|
||||
--color-prettylights-syntax-markup-heading: #1f6feb;
|
||||
--color-prettylights-syntax-markup-italic: #c9d1d9;
|
||||
--color-prettylights-syntax-markup-bold: #c9d1d9;
|
||||
--color-prettylights-syntax-markup-deleted-text: #ffdcd7;
|
||||
--color-prettylights-syntax-markup-deleted-bg: #67060c;
|
||||
--color-prettylights-syntax-markup-inserted-text: #aff5b4;
|
||||
--color-prettylights-syntax-markup-inserted-bg: #033a16;
|
||||
--color-prettylights-syntax-markup-changed-text: #ffdfb6;
|
||||
--color-prettylights-syntax-markup-changed-bg: #5a1e02;
|
||||
--color-prettylights-syntax-markup-ignored-text: #c9d1d9;
|
||||
--color-prettylights-syntax-markup-ignored-bg: #1158c7;
|
||||
--color-prettylights-syntax-meta-diff-range: #d2a8ff;
|
||||
--color-prettylights-syntax-brackethighlighter-angle: #8b949e;
|
||||
--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
|
||||
--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
|
||||
--color-btn-text: #d4d4d4; /* --darkgray */
|
||||
--color-btn-bg: #161618; /* --light */
|
||||
--color-btn-border: rgb(240, 246, 252 / 10%); /* --dark */
|
||||
--color-btn-shadow: 0 0 transparent;
|
||||
--color-btn-inset-shadow: 0 0 transparent;
|
||||
--color-btn-hover-bg: #30363d;
|
||||
--color-btn-hover-border: #8b949e;
|
||||
--color-btn-active-bg: hsl(212deg 12% 18% / 100%);
|
||||
--color-btn-active-border: #6e7681;
|
||||
--color-btn-selected-bg: #161b22;
|
||||
--color-btn-primary-text: #fff;
|
||||
--color-btn-primary-bg: #84a59d; /* --tertiary */
|
||||
--color-btn-primary-border: rgb(240, 246, 252 / 10%); /* --dark */
|
||||
--color-btn-primary-shadow: 0 0 transparent;
|
||||
--color-btn-primary-inset-shadow: 0 0 transparent;
|
||||
--color-btn-primary-hover-bg: #7b97aa; /* --secondary */
|
||||
--color-btn-primary-hover-border: rgb(240, 246, 252 / 10%); /* --dark */
|
||||
--color-btn-primary-selected-bg: #7b97aa; /* --secondary */
|
||||
--color-btn-primary-selected-shadow: 0 0 transparent;
|
||||
--color-btn-primary-disabled-text: rgba(33, 32, 32, 0.5);
|
||||
--color-btn-primary-disabled-bg: rgb(35 134 54 / 60%);
|
||||
--color-btn-primary-disabled-border: rgb(240 246 252 / 10%);
|
||||
--color-action-list-item-default-hover-bg: rgb(177 186 196 / 12%);
|
||||
--color-segmented-control-bg: rgb(110 118 129 / 10%);
|
||||
--color-segmented-control-button-bg: #0d1117;
|
||||
--color-segmented-control-button-selected-border: #6e7681;
|
||||
--color-fg-default: #ebebec; /* --dark */
|
||||
--color-fg-muted: #d4d4d4; /* --darkgray */
|
||||
--color-fg-subtle: #d4d4d4; /* --darkgray */
|
||||
--color-canvas-default: #0d1117;
|
||||
--color-canvas-overlay: #161b22;
|
||||
--color-canvas-inset: #010409;
|
||||
--color-canvas-subtle: #161b22;
|
||||
--color-border-default: #30363d;
|
||||
--color-border-muted: #21262d;
|
||||
--color-neutral-muted: rgb(110 118 129 / 40%);
|
||||
--color-accent-fg: #2f81f7;
|
||||
--color-accent-emphasis: #1f6feb;
|
||||
--color-accent-muted: rgb(56 139 253 / 40%);
|
||||
--color-accent-subtle: rgb(56 139 253 / 10%);
|
||||
--color-success-fg: #3fb950;
|
||||
--color-attention-fg: #d29922;
|
||||
--color-attention-muted: rgb(187 128 9 / 40%);
|
||||
--color-attention-subtle: rgb(187 128 9 / 15%);
|
||||
--color-danger-fg: #f85149;
|
||||
--color-danger-muted: rgb(248 81 73 / 40%);
|
||||
--color-danger-subtle: rgb(248 81 73 / 10%);
|
||||
--color-primer-shadow-inset: 0 0 transparent;
|
||||
--color-scale-gray-7: #21262d;
|
||||
--color-scale-blue-8: #0c2d6b;
|
||||
|
||||
/*! Extensions from @primer/css/alerts/flash.scss */
|
||||
--color-social-reaction-bg-hover: var(--color-scale-gray-7);
|
||||
--color-social-reaction-bg-reacted-hover: var(--color-scale-blue-8);
|
||||
}
|
||||
|
||||
main .pagination-loader-container {
|
||||
background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line-dark.svg");
|
||||
}
|
||||
|
||||
main .gsc-loading-image {
|
||||
background-image: url("https://github.githubassets.com/images/mona-loading-dark.gif");
|
||||
}
|
||||
99
quartz/static/giscus/light.css
Normal file
99
quartz/static/giscus/light.css
Normal file
@ -0,0 +1,99 @@
|
||||
/*! MIT License
|
||||
* Copyright (c) 2018 GitHub Inc.
|
||||
* https://github.com/primer/primitives/blob/main/LICENSE
|
||||
*/
|
||||
|
||||
main {
|
||||
--color-prettylights-syntax-comment: #6e7781;
|
||||
--color-prettylights-syntax-constant: #0550ae;
|
||||
--color-prettylights-syntax-entity: #8250df;
|
||||
--color-prettylights-syntax-storage-modifier-import: #24292f;
|
||||
--color-prettylights-syntax-entity-tag: #116329;
|
||||
--color-prettylights-syntax-keyword: #cf222e;
|
||||
--color-prettylights-syntax-string: #0a3069;
|
||||
--color-prettylights-syntax-variable: #953800;
|
||||
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
|
||||
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
|
||||
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
|
||||
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
|
||||
--color-prettylights-syntax-carriage-return-bg: #cf222e;
|
||||
--color-prettylights-syntax-string-regexp: #116329;
|
||||
--color-prettylights-syntax-markup-list: #3b2300;
|
||||
--color-prettylights-syntax-markup-heading: #0550ae;
|
||||
--color-prettylights-syntax-markup-italic: #24292f;
|
||||
--color-prettylights-syntax-markup-bold: #24292f;
|
||||
--color-prettylights-syntax-markup-deleted-text: #82071e;
|
||||
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
|
||||
--color-prettylights-syntax-markup-inserted-text: #116329;
|
||||
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
|
||||
--color-prettylights-syntax-markup-changed-text: #953800;
|
||||
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
|
||||
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
|
||||
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
|
||||
--color-prettylights-syntax-meta-diff-range: #8250df;
|
||||
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
|
||||
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
|
||||
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
|
||||
--color-btn-text: #4e4e4e; /* --darkgray */
|
||||
--color-btn-bg: #faf8f8; /* --light */
|
||||
--color-btn-border: rgb(43, 43, 43 / 15%); /* --dark */
|
||||
--color-btn-shadow: 0 1px 0 rgb(31 35 40 / 4%);
|
||||
--color-btn-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 25%);
|
||||
--color-btn-hover-bg: #f3f4f6;
|
||||
--color-btn-hover-border: rgb(43, 43, 43 / 15%); /* --dark */
|
||||
--color-btn-active-bg: hsl(220deg 14% 93% / 100%);
|
||||
--color-btn-active-border: rgb(31 35 40 / 15%);
|
||||
--color-btn-selected-bg: hsl(220deg 14% 94% / 100%);
|
||||
--color-btn-primary-text: #fff;
|
||||
--color-btn-primary-bg: #84a59d; /* --tertiary */
|
||||
--color-btn-primary-border: rgb(43, 43, 43 / 15%); /* --dark */
|
||||
--color-btn-primary-shadow: 0 1px 0 rgb(31 35 40 / 10%);
|
||||
--color-btn-primary-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 3%);
|
||||
--color-btn-primary-hover-bg: #284b63; /* --secondary */
|
||||
--color-btn-primary-hover-border: rgb(43, 43, 43 / 15%); /* --dark */
|
||||
--color-btn-primary-selected-bg: #284b63; /* --secondary */
|
||||
--color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%);
|
||||
--color-btn-primary-disabled-text: rgb(255 255 255 / 80%);
|
||||
--color-btn-primary-disabled-bg: #94d3a2;
|
||||
--color-btn-primary-disabled-border: rgb(31 35 40 / 15%);
|
||||
--color-action-list-item-default-hover-bg: rgb(208 215 222 / 32%);
|
||||
--color-segmented-control-bg: #eaeef2;
|
||||
--color-segmented-control-button-bg: #fff;
|
||||
--color-segmented-control-button-selected-border: #8c959f;
|
||||
--color-fg-default: #2b2b2b; /* --dark */
|
||||
--color-fg-muted: #4e4e4e; /* --darkgray */
|
||||
--color-fg-subtle: #4e4e4e; /* --darkgray */
|
||||
--color-canvas-default: #fff;
|
||||
--color-canvas-overlay: #fff;
|
||||
--color-canvas-inset: #f6f8fa;
|
||||
--color-canvas-subtle: #f6f8fa;
|
||||
--color-border-default: #d0d7de;
|
||||
--color-border-muted: hsl(210deg 18% 87% / 100%);
|
||||
--color-neutral-muted: rgb(175 184 193 / 20%);
|
||||
--color-accent-fg: #0969da;
|
||||
--color-accent-emphasis: #0969da;
|
||||
--color-accent-muted: rgb(84 174 255 / 40%);
|
||||
--color-accent-subtle: #ddf4ff;
|
||||
--color-success-fg: #1a7f37;
|
||||
--color-attention-fg: #9a6700;
|
||||
--color-attention-muted: rgb(212 167 44 / 40%);
|
||||
--color-attention-subtle: #fff8c5;
|
||||
--color-danger-fg: #d1242f;
|
||||
--color-danger-muted: rgb(255 129 130 / 40%);
|
||||
--color-danger-subtle: #ffebe9;
|
||||
--color-primer-shadow-inset: inset 0 1px 0 rgb(208 215 222 / 20%);
|
||||
--color-scale-gray-1: #eaeef2;
|
||||
--color-scale-blue-1: #b6e3ff;
|
||||
|
||||
/*! Extensions from @primer/css/alerts/flash.scss */
|
||||
--color-social-reaction-bg-hover: var(--color-scale-gray-1);
|
||||
--color-social-reaction-bg-reacted-hover: var(--color-scale-blue-1);
|
||||
}
|
||||
|
||||
main .pagination-loader-container {
|
||||
background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line.svg");
|
||||
}
|
||||
|
||||
main .gsc-loading-image {
|
||||
background-image: url("https://github.githubassets.com/images/mona-loading-default.gif");
|
||||
}
|
||||
@ -12,7 +12,6 @@ html {
|
||||
body,
|
||||
section {
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--light);
|
||||
font-family: var(--bodyFont);
|
||||
@ -109,25 +108,21 @@ a {
|
||||
|
||||
.desktop-only {
|
||||
display: initial;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
@media all and ($mobile) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-only {
|
||||
display: none;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
@media all and ($mobile) {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
max-width: $pageWidth;
|
||||
}
|
||||
|
||||
max-width: calc(#{map-get($breakpoints, desktop)} + 300px);
|
||||
margin: 0 auto;
|
||||
& article {
|
||||
& > h1 {
|
||||
font-size: 2rem;
|
||||
@ -155,79 +150,120 @@ a {
|
||||
}
|
||||
|
||||
& > #quartz-body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-columns: #{map-get($desktopGrid, templateColumns)};
|
||||
grid-template-rows: #{map-get($desktopGrid, templateRows)};
|
||||
column-gap: #{map-get($desktopGrid, columnGap)};
|
||||
row-gap: #{map-get($desktopGrid, rowGap)};
|
||||
grid-template-areas: #{map-get($desktopGrid, templateAreas)};
|
||||
@media all and ($tablet) {
|
||||
grid-template-columns: #{map-get($tabletGrid, templateColumns)};
|
||||
grid-template-rows: #{map-get($tabletGrid, templateRows)};
|
||||
column-gap: #{map-get($tabletGrid, columnGap)};
|
||||
row-gap: #{map-get($tabletGrid, rowGap)};
|
||||
grid-template-areas: #{map-get($tabletGrid, templateAreas)};
|
||||
}
|
||||
@media all and ($mobile) {
|
||||
grid-template-columns: #{map-get($mobileGrid, templateColumns)};
|
||||
grid-template-rows: #{map-get($mobileGrid, templateRows)};
|
||||
column-gap: #{map-get($mobileGrid, columnGap)};
|
||||
row-gap: #{map-get($mobileGrid, rowGap)};
|
||||
grid-template-areas: #{map-get($mobileGrid, templateAreas)};
|
||||
}
|
||||
|
||||
@media all and not ($desktop) {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
@media all and ($mobile) {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
& .sidebar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
top: 0;
|
||||
width: $sidePanelWidth;
|
||||
margin-top: $topSpacing;
|
||||
box-sizing: border-box;
|
||||
padding: 0 4rem;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
position: initial;
|
||||
flex-direction: row;
|
||||
padding: 0;
|
||||
width: initial;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
padding: $topSpacing 2rem 2rem 2rem;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
& .sidebar.left {
|
||||
left: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
z-index: 1;
|
||||
grid-area: grid-sidebar-left;
|
||||
flex-direction: column;
|
||||
@media all and ($mobile) {
|
||||
gap: 0;
|
||||
align-items: center;
|
||||
position: initial;
|
||||
display: flex;
|
||||
height: unset;
|
||||
flex-direction: row;
|
||||
padding: 0;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
& .sidebar.right {
|
||||
right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
|
||||
flex-wrap: wrap;
|
||||
& > * {
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
grid-area: grid-sidebar-right;
|
||||
margin-right: 0;
|
||||
flex-direction: column;
|
||||
@media all and ($mobile) {
|
||||
margin-left: inherit;
|
||||
margin-right: inherit;
|
||||
}
|
||||
@media all and not ($desktop) {
|
||||
position: initial;
|
||||
height: unset;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
padding: 0;
|
||||
& > * {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
& > .toc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .page-header,
|
||||
& .page-footer {
|
||||
width: $pageWidth;
|
||||
margin-top: 1rem;
|
||||
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
width: initial;
|
||||
& .page-header,
|
||||
& .page-footer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
& .page-header {
|
||||
margin: $topSpacing auto 0 auto;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
margin-top: 2rem;
|
||||
& .page-header {
|
||||
grid-area: grid-header;
|
||||
margin: $topSpacing 0 0 0;
|
||||
@media all and ($mobile) {
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .center,
|
||||
& footer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: $pageWidth;
|
||||
@media all and (max-width: $fullPageWidth) {
|
||||
width: initial;
|
||||
& .center > article {
|
||||
grid-area: grid-center;
|
||||
}
|
||||
|
||||
& footer {
|
||||
grid-area: grid-footer;
|
||||
}
|
||||
|
||||
& .center,
|
||||
& footer {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@media all and ($tablet) {
|
||||
margin-right: 0;
|
||||
}
|
||||
@media all and ($mobile) {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
& footer {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -383,7 +419,7 @@ pre {
|
||||
counter-increment: line 0;
|
||||
display: grid;
|
||||
padding: 0.5rem 0;
|
||||
overflow-x: scroll;
|
||||
overflow-x: auto;
|
||||
|
||||
& [data-highlighted-chars] {
|
||||
background-color: var(--highlight);
|
||||
@ -502,12 +538,14 @@ video {
|
||||
}
|
||||
|
||||
div:has(> .overflow) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
ul.overflow,
|
||||
ol.overflow {
|
||||
max-height: 400;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
// clearfix
|
||||
@ -517,8 +555,7 @@ ol.overflow {
|
||||
& > li:last-of-type {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
/*&:after {
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
width: 100%;
|
||||
@ -529,7 +566,7 @@ ol.overflow {
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
background: linear-gradient(transparent 0px, var(--light));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
.transclude {
|
||||
|
||||
@ -1,9 +1,56 @@
|
||||
$pageWidth: 750px;
|
||||
$mobileBreakpoint: 600px;
|
||||
$tabletBreakpoint: 1000px;
|
||||
$sidePanelWidth: 380px;
|
||||
/**
|
||||
* Layout breakpoints
|
||||
* $mobile: screen width below this value will use mobile styles
|
||||
* $desktop: screen width above this value will use desktop styles
|
||||
* Screen width between $mobile and $desktop width will use the tablet layout.
|
||||
* assuming mobile < desktop
|
||||
*/
|
||||
$breakpoints: (
|
||||
mobile: 800px,
|
||||
desktop: 1200px,
|
||||
);
|
||||
|
||||
$mobile: "(max-width: #{map-get($breakpoints, mobile)})";
|
||||
$tablet: "(min-width: #{map-get($breakpoints, mobile)}) and (max-width: #{map-get($breakpoints, desktop)})";
|
||||
$desktop: "(min-width: #{map-get($breakpoints, desktop)})";
|
||||
|
||||
$pageWidth: #{map-get($breakpoints, mobile)};
|
||||
$sidePanelWidth: 320px; //380px;
|
||||
$topSpacing: 6rem;
|
||||
$fullPageWidth: $pageWidth + 2 * $sidePanelWidth;
|
||||
$boldWeight: 700;
|
||||
$semiBoldWeight: 600;
|
||||
$normalWeight: 400;
|
||||
|
||||
$mobileGrid: (
|
||||
templateRows: "auto auto auto auto auto",
|
||||
templateColumns: "auto",
|
||||
rowGap: "5px",
|
||||
columnGap: "5px",
|
||||
templateAreas:
|
||||
'"grid-sidebar-left"\
|
||||
"grid-header"\
|
||||
"grid-center"\
|
||||
"grid-sidebar-right"\
|
||||
"grid-footer"',
|
||||
);
|
||||
$tabletGrid: (
|
||||
templateRows: "auto auto auto auto",
|
||||
templateColumns: "#{$sidePanelWidth} auto",
|
||||
rowGap: "5px",
|
||||
columnGap: "5px",
|
||||
templateAreas:
|
||||
'"grid-sidebar-left grid-header"\
|
||||
"grid-sidebar-left grid-center"\
|
||||
"grid-sidebar-left grid-sidebar-right"\
|
||||
"grid-sidebar-left grid-footer"',
|
||||
);
|
||||
$desktopGrid: (
|
||||
templateRows: "auto auto auto",
|
||||
templateColumns: "#{$sidePanelWidth} auto #{$sidePanelWidth}",
|
||||
rowGap: "5px",
|
||||
columnGap: "5px",
|
||||
templateAreas:
|
||||
'"grid-sidebar-left grid-header grid-sidebar-right"\
|
||||
"grid-sidebar-left grid-center grid-sidebar-right"\
|
||||
"grid-sidebar-left grid-footer grid-sidebar-right"',
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user