mirror of
https://github.com/ZetaKebab/quartz.git
synced 2025-01-14 22:18:43 +00:00
Compare commits
No commits in common. "34fde07cf8ee7c65dd48a35b35d3fb1c801e1634" and "688c5484a9d9146214d4259745b70dc0dd0fedc4" have entirely different histories.
34fde07cf8
...
688c5484a9
15
.github/workflows/ci.yaml
vendored
15
.github/workflows/ci.yaml
vendored
@ -7,7 +7,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- v4
|
- v4
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
build-and-test:
|
||||||
@ -19,17 +18,17 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
@ -48,22 +47,22 @@ jobs:
|
|||||||
run: npx quartz build --bundleInfo
|
run: npx quartz build --bundleInfo
|
||||||
|
|
||||||
publish-tag:
|
publish-tag:
|
||||||
if: ${{ github.repository == 'jackyzha0/quartz' && github.ref == 'refs/heads/v4' }}
|
if: ${{ github.repository == 'jackyzha0/quartz' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- name: Get package version
|
- name: Get package version
|
||||||
run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
|
run: node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
|
||||||
- name: Create release tag
|
- name: Create release tag
|
||||||
uses: pkgdeps/git-tag-action@v3
|
uses: pkgdeps/git-tag-action@v2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
github_repo: ${{ github.repository }}
|
github_repo: ${{ github.repository }}
|
||||||
|
@ -129,11 +129,11 @@ export default (() => {
|
|||||||
return <button id="btn">Click me</button>
|
return <button id="btn">Click me</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
YourComponent.beforeDOMLoaded = `
|
YourComponent.beforeDOM = `
|
||||||
console.log("hello from before the page loads!")
|
console.log("hello from before the page loads!")
|
||||||
`
|
`
|
||||||
|
|
||||||
YourComponent.afterDOMLoaded = `
|
YourComponent.afterDOM = `
|
||||||
document.getElementById('btn').onclick = () => {
|
document.getElementById('btn').onclick = () => {
|
||||||
alert('button clicked!')
|
alert('button clicked!')
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ export default (() => {
|
|||||||
return <button id="btn">Click me</button>
|
return <button id="btn">Click me</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
YourComponent.afterDOMLoaded = script
|
YourComponent.afterDOM = script
|
||||||
return YourComponent
|
return YourComponent
|
||||||
}) satisfies QuartzComponentConstructor
|
}) satisfies QuartzComponentConstructor
|
||||||
```
|
```
|
||||||
|
@ -260,11 +260,11 @@ export const ContentPage: QuartzEmitterPlugin = () => {
|
|||||||
...defaultContentPageLayout,
|
...defaultContentPageLayout,
|
||||||
pageBody: Content(),
|
pageBody: Content(),
|
||||||
}
|
}
|
||||||
const { head, header, beforeBody, pageBody, afterBody, left, right, footer } = layout
|
const { head, header, beforeBody, pageBody, left, right, footer } = layout
|
||||||
return {
|
return {
|
||||||
name: "ContentPage",
|
name: "ContentPage",
|
||||||
getQuartzComponents() {
|
getQuartzComponents() {
|
||||||
return [head, ...header, ...beforeBody, pageBody, ...afterBody, ...left, ...right, footer]
|
return [head, ...header, ...beforeBody, pageBody, ...left, ...right, footer]
|
||||||
},
|
},
|
||||||
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
|
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
|
||||||
const cfg = ctx.cfg.configuration
|
const cfg = ctx.cfg.configuration
|
||||||
|
@ -31,7 +31,6 @@ This part of the configuration concerns anything that can affect the whole site.
|
|||||||
- `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id' }` (managed) or `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id', host: 'my-goatcounter-domain.com', scriptSrc: 'https://my-url.to/counter.js' }` (self-hosted) use [GoatCounter](https://goatcounter.com);
|
- `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id' }` (managed) or `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id', host: 'my-goatcounter-domain.com', scriptSrc: 'https://my-url.to/counter.js' }` (self-hosted) use [GoatCounter](https://goatcounter.com);
|
||||||
- `{ provider: 'posthog', apiKey: '<your-posthog-project-apiKey>', host: '<your-posthog-host>' }`: use [Posthog](https://posthog.com/);
|
- `{ provider: '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: 'tinylytics', siteId: '<your-site-id>' }`: use [Tinylytics](https://tinylytics.app/);
|
||||||
- `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com);
|
|
||||||
- `locale`: used for [[i18n]] and date formatting
|
- `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.
|
- `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes.
|
||||||
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
|
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
|
||||||
@ -53,7 +52,6 @@ This part of the configuration concerns anything that can affect the whole site.
|
|||||||
- `secondary`: link colour, current [[graph view|graph]] node
|
- `secondary`: link colour, current [[graph view|graph]] node
|
||||||
- `tertiary`: hover states and visited [[graph view|graph]] nodes
|
- `tertiary`: hover states and visited [[graph view|graph]] nodes
|
||||||
- `highlight`: internal link background, highlighted text, [[syntax highlighting|highlighted lines of code]]
|
- `highlight`: internal link background, highlighted text, [[syntax highlighting|highlighted lines of code]]
|
||||||
- `textHighlight`: markdown highlighted text background
|
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
---
|
|
||||||
title: Comments
|
|
||||||
tags:
|
|
||||||
- component
|
|
||||||
---
|
|
||||||
|
|
||||||
Quartz also has the ability to hook into various providers to enable readers to leave comments on your site.
|
|
||||||
|
|
||||||
![[giscus-example.png]]
|
|
||||||
|
|
||||||
As of today, only [Giscus](https://giscus.app/) is supported out of the box but PRs to support other providers are welcome!
|
|
||||||
|
|
||||||
## Providers
|
|
||||||
|
|
||||||
### Giscus
|
|
||||||
|
|
||||||
First, make sure that the [[setting up your GitHub repository|GitHub]] repository you are using for your Quartz meets the following requirements:
|
|
||||||
|
|
||||||
1. The **repository is [public](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/setting-repository-visibility#making-a-repository-public)**, otherwise visitors will not be able to view the discussion.
|
|
||||||
2. The **[giscus](https://github.com/apps/giscus) app is installed**, otherwise visitors will not be able to comment and react.
|
|
||||||
3. The **Discussions feature is turned on** by [enabling it for your repository](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/enabling-or-disabling-github-discussions-for-a-repository).
|
|
||||||
|
|
||||||
Then, use the [Giscus site](https://giscus.app/#repository) to figure out what your `repoId` and `categoryId` should be. Make sure you select `Announcements` for the Discussion category.
|
|
||||||
|
|
||||||
![[giscus-repo.png]]
|
|
||||||
|
|
||||||
![[giscus-discussion.png]]
|
|
||||||
|
|
||||||
After entering both your repository and selecting the discussion category, Giscus will compute some IDs that you'll need to provide back to Quartz. You won't need to manually add the script yourself as Quartz will handle that part for you but will need these values in the next step!
|
|
||||||
|
|
||||||
![[giscus-results.png]]
|
|
||||||
|
|
||||||
Finally, in `quartz.layout.ts`, edit the `afterBody` field of `sharedPageComponents` to include the following options but with the values you got from above:
|
|
||||||
|
|
||||||
```ts title="quartz.layout.ts"
|
|
||||||
afterBody: [
|
|
||||||
Component.Comments({
|
|
||||||
provider: 'giscus',
|
|
||||||
options: {
|
|
||||||
// from data-repo
|
|
||||||
repo: 'jackyzha0/quartz',
|
|
||||||
// from data-repo-id
|
|
||||||
repoId: 'MDEwOlJlcG9zaXRvcnkzODcyMTMyMDg',
|
|
||||||
// from data-category
|
|
||||||
category: 'Announcements',
|
|
||||||
// from data-category-id
|
|
||||||
categoryId: 'DIC_kwDOFxRnmM4B-Xg6',
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customization
|
|
||||||
|
|
||||||
Quartz also exposes a few of the other Giscus options as well and you can provide them the same way `repo`, `repoId`, `category`, and `categoryId` are provided.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type Options = {
|
|
||||||
provider: "giscus"
|
|
||||||
options: {
|
|
||||||
repo: `${string}/${string}`
|
|
||||||
repoId: string
|
|
||||||
category: string
|
|
||||||
categoryId: string
|
|
||||||
|
|
||||||
// how to map pages -> discussions
|
|
||||||
// defaults to 'url'
|
|
||||||
mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
|
|
||||||
|
|
||||||
// use strict title matching
|
|
||||||
// defaults to true
|
|
||||||
strict?: boolean
|
|
||||||
|
|
||||||
// whether to enable reactions for the main post
|
|
||||||
// defaults to true
|
|
||||||
reactionsEnabled?: boolean
|
|
||||||
|
|
||||||
// where to put the comment input box relative to the comments
|
|
||||||
// defaults to 'bottom'
|
|
||||||
inputPosition?: "top" | "bottom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
@ -30,4 +30,4 @@ As with folder listings, you can also provide a description and title for a tag
|
|||||||
|
|
||||||
## Customization
|
## Customization
|
||||||
|
|
||||||
Quartz allows you to define a custom sort ordering for content on both page types. The folder listings are a functionality of the [[FolderPage]] plugin, the tag listings of the [[TagPage]] plugin. See the plugin pages for customization options.
|
The folder listings are a functionality of the [[FolderPage]] plugin, the tag listings of the [[TagPage]] plugin. See the plugin pages for customization options.
|
||||||
|
@ -2,12 +2,22 @@
|
|||||||
draft: true
|
draft: true
|
||||||
---
|
---
|
||||||
|
|
||||||
## misc backlog
|
## high priority backlog
|
||||||
|
|
||||||
- static dead link detection
|
- static dead link detection
|
||||||
|
- block links: https://help.obsidian.md/Linking+notes+and+files/Internal+links#Link+to+a+block+in+a+note
|
||||||
|
- note/header/block transcludes: https://help.obsidian.md/Linking+notes+and+files/Embedding+files
|
||||||
|
- docker support
|
||||||
|
|
||||||
|
## misc backlog
|
||||||
|
|
||||||
|
- breadcrumbs component
|
||||||
- cursor chat extension
|
- cursor chat extension
|
||||||
- https://giscus.app/ extension
|
- https://giscus.app/ extension
|
||||||
- sidenotes? https://github.com/capnfabs/paperesque
|
- sidenotes? https://github.com/capnfabs/paperesque
|
||||||
- direct match in search using double quotes
|
- direct match in search using double quotes
|
||||||
- https://help.obsidian.md/Advanced+topics/Using+Obsidian+URI
|
- https://help.obsidian.md/Advanced+topics/Using+Obsidian+URI
|
||||||
|
- audio/video embed styling
|
||||||
- Canvas
|
- Canvas
|
||||||
|
- parse all images in page: use this for page lists if applicable?
|
||||||
|
- CV mode? with print stylesheet
|
||||||
|
@ -57,16 +57,18 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Fetch all history for git info
|
fetch-depth: 0 # Fetch all history for git info
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18.14
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Build Quartz
|
- name: Build Quartz
|
||||||
run: npx quartz build
|
run: npx quartz build
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v2
|
||||||
with:
|
with:
|
||||||
path: public
|
path: public
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v2
|
||||||
```
|
```
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
@ -180,31 +182,35 @@ Using `docs.example.com` is an example of a subdomain. They're a simple way of c
|
|||||||
|
|
||||||
## GitLab Pages
|
## GitLab Pages
|
||||||
|
|
||||||
In your local Quartz, create a new file `.gitlab-ci.yml`.
|
In your local Quartz, create a new file `.gitlab-ci.yaml`.
|
||||||
|
|
||||||
```yaml title=".gitlab-ci.yml"
|
```yaml title=".gitlab-ci.yaml"
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
image: node:18
|
variables:
|
||||||
cache: # Cache modules in between jobs
|
NODE_VERSION: "18.14"
|
||||||
key: $CI_COMMIT_REF_SLUG
|
|
||||||
paths:
|
|
||||||
- .npm/
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_REF_NAME == "v4"'
|
- if: '$CI_COMMIT_REF_NAME == "v4"'
|
||||||
before_script:
|
before_script:
|
||||||
|
- apt-get update -q && apt-get install -y nodejs npm
|
||||||
|
- npm install -g n
|
||||||
|
- n $NODE_VERSION
|
||||||
- hash -r
|
- hash -r
|
||||||
- npm ci --cache .npm --prefer-offline
|
- npm ci
|
||||||
script:
|
script:
|
||||||
- npx quartz build
|
- npx quartz build
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- ~/.npm/
|
||||||
|
key: "${CI_COMMIT_REF_SLUG}-node-${CI_COMMIT_REF_NAME}"
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
Before Width: | Height: | Size: 572 KiB |
Binary file not shown.
Before Width: | Height: | Size: 108 KiB |
Binary file not shown.
Before Width: | Height: | Size: 171 KiB |
Binary file not shown.
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 55 KiB |
@ -31,7 +31,7 @@ If you prefer instructions in a video format you can try following Nicole van de
|
|||||||
|
|
||||||
## 🔧 Features
|
## 🔧 Features
|
||||||
|
|
||||||
- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]], [[comments]] and [many more](./features) right out of the box
|
- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]] and [many more](./features) right out of the box
|
||||||
- Hot-reload for both configuration and content
|
- Hot-reload for both configuration and content
|
||||||
- Simple JSX layouts and [[creating components|page components]]
|
- Simple JSX layouts and [[creating components|page components]]
|
||||||
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
||||||
|
@ -12,7 +12,6 @@ export interface FullPageLayout {
|
|||||||
header: QuartzComponent[] // laid out horizontally
|
header: QuartzComponent[] // laid out horizontally
|
||||||
beforeBody: QuartzComponent[] // laid out vertically
|
beforeBody: QuartzComponent[] // laid out vertically
|
||||||
pageBody: QuartzComponent // single component
|
pageBody: QuartzComponent // single component
|
||||||
afterBody: QuartzComponent[] // laid out vertically
|
|
||||||
left: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
left: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
||||||
right: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
right: QuartzComponent[] // vertical on desktop, horizontal on mobile
|
||||||
footer: QuartzComponent // single component
|
footer: QuartzComponent // single component
|
||||||
|
@ -11,7 +11,7 @@ This plugin determines the created, modified, and published dates for a document
|
|||||||
|
|
||||||
This plugin accepts the following configuration options:
|
This plugin accepts the following configuration options:
|
||||||
|
|
||||||
- `priority`: The data sources to consult for date information. Highest priority first. Possible values are `"frontmatter"`, `"git"`, and `"filesystem"`. Defaults to `["frontmatter", "git", "filesystem"]`.
|
- `priority`: The data sources to consult for date information. Highest priority first. Possible values are `"frontmatter"`, `"git"`, and `"filesystem"`. Defaults to `"frontmatter", "git", "filesystem"]`.
|
||||||
|
|
||||||
> [!warning]
|
> [!warning]
|
||||||
> If you rely on `git` for dates, make sure `defaultDateType` is set to `modified` in `quartz.config.ts`.
|
> If you rely on `git` for dates, make sure `defaultDateType` is set to `modified` in `quartz.config.ts`.
|
||||||
|
@ -11,12 +11,10 @@ Example: [[advanced/|Advanced]]
|
|||||||
> [!note]
|
> [!note]
|
||||||
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
|
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
|
||||||
|
|
||||||
|
This plugin has no configuration options.
|
||||||
|
|
||||||
The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `FolderContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/FolderContent.tsx`).
|
The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `FolderContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/FolderContent.tsx`).
|
||||||
|
|
||||||
This plugin accepts the following configuration options:
|
|
||||||
|
|
||||||
- `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number{:ts}` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order.
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
- Category: Emitter
|
- Category: Emitter
|
||||||
|
@ -9,12 +9,10 @@ This plugin emits dedicated pages for each tag used in the content. See [[folder
|
|||||||
> [!note]
|
> [!note]
|
||||||
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
|
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
|
||||||
|
|
||||||
|
This plugin has no configuration options.
|
||||||
|
|
||||||
The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `TagContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/TagContent.tsx`).
|
The pages are displayed using the `defaultListPageLayout` in `quartz.layouts.ts`. For the content, the `TagContent` component is used. If you want to modify the layout, you must edit it directly (`quartz/components/pages/TagContent.tsx`).
|
||||||
|
|
||||||
This plugin accepts the following configuration options:
|
|
||||||
|
|
||||||
- `sort`: A function of type `(f1: QuartzPluginData, f2: QuartzPluginData) => number{:ts}` used to sort entries. Defaults to sorting by date and tie-breaking on lexographical order.
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
- Category: Emitter
|
- Category: Emitter
|
||||||
|
764
package-lock.json
generated
764
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@ -2,7 +2,7 @@
|
|||||||
"name": "@jackyzha0/quartz",
|
"name": "@jackyzha0/quartz",
|
||||||
"description": "🌱 publish your digital garden and notes as a website",
|
"description": "🌱 publish your digital garden and notes as a website",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "4.2.4",
|
"version": "4.2.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
|
"author": "jackyzha0 <j.zhao2k19@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^0.7.0",
|
"@clack/prompts": "^0.7.0",
|
||||||
"@floating-ui/dom": "^1.6.8",
|
"@floating-ui/dom": "^1.6.3",
|
||||||
"@napi-rs/simple-git": "0.1.16",
|
"@napi-rs/simple-git": "0.1.16",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
@ -53,13 +53,13 @@
|
|||||||
"hast-util-to-string": "^3.0.0",
|
"hast-util-to-string": "^3.0.0",
|
||||||
"is-absolute-url": "^4.0.1",
|
"is-absolute-url": "^4.0.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lightningcss": "^1.25.1",
|
"lightningcss": "^1.24.1",
|
||||||
"mdast-util-find-and-replace": "^3.0.1",
|
"mdast-util-find-and-replace": "^3.0.1",
|
||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.1.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
"preact": "^10.22.1",
|
"preact": "^10.20.1",
|
||||||
"preact-render-to-string": "^6.5.5",
|
"preact-render-to-string": "^6.4.2",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pretty-time": "^1.1.0",
|
"pretty-time": "^1.1.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
@ -77,19 +77,19 @@
|
|||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.1.0",
|
"remark-rehype": "^11.1.0",
|
||||||
"remark-smartypants": "^3.0.2",
|
"remark-smartypants": "^2.1.0",
|
||||||
"rfdc": "^1.4.1",
|
"rfdc": "^1.3.1",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^5.0.7",
|
||||||
"serve-handler": "^6.1.5",
|
"serve-handler": "^6.1.5",
|
||||||
"shiki": "^1.10.3",
|
"shiki": "^1.6.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"to-vfile": "^8.0.0",
|
"to-vfile": "^8.0.0",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"unified": "^11.0.4",
|
"unified": "^11.0.4",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"vfile": "^6.0.1",
|
"vfile": "^6.0.1",
|
||||||
"workerpool": "^9.1.2",
|
"workerpool": "^9.1.1",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.17.0",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -97,14 +97,14 @@
|
|||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/hast": "^3.0.4",
|
"@types/hast": "^3.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^20.14.11",
|
"@types/node": "^20.12.5",
|
||||||
"@types/pretty-time": "^1.1.5",
|
"@types/pretty-time": "^1.1.5",
|
||||||
"@types/source-map-support": "^0.5.10",
|
"@types/source-map-support": "^0.5.10",
|
||||||
"@types/ws": "^8.5.11",
|
"@types/ws": "^8.5.10",
|
||||||
"@types/yargs": "^17.0.32",
|
"@types/yargs": "^17.0.32",
|
||||||
"esbuild": "^0.19.9",
|
"esbuild": "^0.19.9",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.2.4",
|
||||||
"tsx": "^4.16.2",
|
"tsx": "^4.11.2",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ const config: QuartzConfig = {
|
|||||||
secondary: "#284b63",
|
secondary: "#284b63",
|
||||||
tertiary: "#84a59d",
|
tertiary: "#84a59d",
|
||||||
highlight: "rgba(143, 159, 169, 0.15)",
|
highlight: "rgba(143, 159, 169, 0.15)",
|
||||||
textHighlight: "#fff23688",
|
|
||||||
},
|
},
|
||||||
darkMode: {
|
darkMode: {
|
||||||
light: "#161618",
|
light: "#161618",
|
||||||
@ -47,7 +46,6 @@ const config: QuartzConfig = {
|
|||||||
secondary: "#7b97aa",
|
secondary: "#7b97aa",
|
||||||
tertiary: "#84a59d",
|
tertiary: "#84a59d",
|
||||||
highlight: "rgba(143, 159, 169, 0.15)",
|
highlight: "rgba(143, 159, 169, 0.15)",
|
||||||
textHighlight: "#b3aa0288",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,6 @@ import * as Component from "./quartz/components"
|
|||||||
export const sharedPageComponents: SharedLayout = {
|
export const sharedPageComponents: SharedLayout = {
|
||||||
head: Component.Head(),
|
head: Component.Head(),
|
||||||
header: [],
|
header: [],
|
||||||
afterBody: [],
|
|
||||||
footer: Component.Footer({
|
footer: Component.Footer({
|
||||||
links: {
|
links: {
|
||||||
GitHub: "https://github.com/jackyzha0/quartz",
|
GitHub: "https://github.com/jackyzha0/quartz",
|
||||||
|
@ -34,10 +34,6 @@ export type Analytics =
|
|||||||
provider: "tinylytics"
|
provider: "tinylytics"
|
||||||
siteId: string
|
siteId: string
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
provider: "cabin"
|
|
||||||
host?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GlobalConfiguration {
|
export interface GlobalConfiguration {
|
||||||
pageTitle: string
|
pageTitle: string
|
||||||
@ -77,11 +73,10 @@ export interface FullPageLayout {
|
|||||||
header: QuartzComponent[]
|
header: QuartzComponent[]
|
||||||
beforeBody: QuartzComponent[]
|
beforeBody: QuartzComponent[]
|
||||||
pageBody: QuartzComponent
|
pageBody: QuartzComponent
|
||||||
afterBody: QuartzComponent[]
|
|
||||||
left: QuartzComponent[]
|
left: QuartzComponent[]
|
||||||
right: QuartzComponent[]
|
right: QuartzComponent[]
|
||||||
footer: QuartzComponent
|
footer: QuartzComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PageLayout = Pick<FullPageLayout, "beforeBody" | "left" | "right">
|
export type PageLayout = Pick<FullPageLayout, "beforeBody" | "left" | "right">
|
||||||
export type SharedLayout = Pick<FullPageLayout, "head" | "header" | "footer" | "afterBody">
|
export type SharedLayout = Pick<FullPageLayout, "head" | "header" | "footer">
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
|
|
||||||
type Options = {
|
|
||||||
provider: "giscus"
|
|
||||||
options: {
|
|
||||||
repo: `${string}/${string}`
|
|
||||||
repoId: string
|
|
||||||
category: string
|
|
||||||
categoryId: string
|
|
||||||
mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname"
|
|
||||||
strict?: boolean
|
|
||||||
reactionsEnabled?: boolean
|
|
||||||
inputPosition?: "top" | "bottom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function boolToStringBool(b: boolean): string {
|
|
||||||
return b ? "1" : "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ((opts: Options) => {
|
|
||||||
const Comments: QuartzComponent = (_props: QuartzComponentProps) => <div class="giscus"></div>
|
|
||||||
Comments.afterDOMLoaded = `
|
|
||||||
const giscusScript = document.createElement("script")
|
|
||||||
giscusScript.src = "https://giscus.app/client.js"
|
|
||||||
giscusScript.async = true
|
|
||||||
giscusScript.crossOrigin = "anonymous"
|
|
||||||
giscusScript.setAttribute("data-loading", "lazy")
|
|
||||||
giscusScript.setAttribute("data-emit-metadata", "0")
|
|
||||||
giscusScript.setAttribute("data-repo", "${opts.options.repo}")
|
|
||||||
giscusScript.setAttribute("data-repo-id", "${opts.options.repoId}")
|
|
||||||
giscusScript.setAttribute("data-category", "${opts.options.category}")
|
|
||||||
giscusScript.setAttribute("data-category-id", "${opts.options.categoryId}")
|
|
||||||
giscusScript.setAttribute("data-mapping", "${opts.options.mapping ?? "url"}")
|
|
||||||
giscusScript.setAttribute("data-strict", "${boolToStringBool(opts.options.strict ?? true)}")
|
|
||||||
giscusScript.setAttribute("data-reactions-enabled", "${boolToStringBool(opts.options.reactionsEnabled ?? true)}")
|
|
||||||
giscusScript.setAttribute("data-input-position", "${opts.options.inputPosition ?? "bottom"}")
|
|
||||||
|
|
||||||
const theme = document.documentElement.getAttribute("saved-theme")
|
|
||||||
giscusScript.setAttribute("data-theme", theme)
|
|
||||||
document.head.appendChild(giscusScript)
|
|
||||||
|
|
||||||
const changeTheme = (e) => {
|
|
||||||
const theme = e.detail.theme
|
|
||||||
const iframe = document.querySelector('iframe.giscus-frame')
|
|
||||||
if (!iframe) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe.contentWindow.postMessage({
|
|
||||||
giscus: {
|
|
||||||
setConfig: {
|
|
||||||
theme: theme
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 'https://giscus.app')
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
|
||||||
document.addEventListener("themechange", changeTheme)
|
|
||||||
window.addCleanup(() => document.removeEventListener("themechange", changeTheme))
|
|
||||||
})
|
|
||||||
`
|
|
||||||
|
|
||||||
return Comments
|
|
||||||
}) satisfies QuartzComponentConstructor<Options>
|
|
@ -168,8 +168,10 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro
|
|||||||
const isDefaultOpen = opts.folderDefaultState === "open"
|
const isDefaultOpen = opts.folderDefaultState === "open"
|
||||||
|
|
||||||
// Calculate current folderPath
|
// Calculate current folderPath
|
||||||
const folderPath = node.name !== "" ? joinSegments(fullPath ?? "", node.name) : ""
|
let folderPath = ""
|
||||||
const href = resolveRelative(fileData.slug!, folderPath as SimpleSlug) + "/"
|
if (node.name !== "") {
|
||||||
|
folderPath = joinSegments(fullPath ?? "", node.name)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -203,7 +205,11 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro
|
|||||||
{/* render <a> tag if folderBehavior is "link", otherwise render <button> with collapse click event */}
|
{/* render <a> tag if folderBehavior is "link", otherwise render <button> with collapse click event */}
|
||||||
<div key={node.name} data-folderpath={folderPath}>
|
<div key={node.name} data-folderpath={folderPath}>
|
||||||
{folderBehavior === "link" ? (
|
{folderBehavior === "link" ? (
|
||||||
<a href={href} data-for={node.name} class="folder-title">
|
<a
|
||||||
|
href={resolveRelative(fileData.slug!, folderPath as SimpleSlug)}
|
||||||
|
data-for={node.name}
|
||||||
|
class="folder-title"
|
||||||
|
>
|
||||||
{node.displayName}
|
{node.displayName}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
|
@ -13,6 +13,7 @@ export default ((opts?: Options) => {
|
|||||||
const links = opts?.links ?? []
|
const links = opts?.links ?? []
|
||||||
return (
|
return (
|
||||||
<footer class={`${displayClass ?? ""}`}>
|
<footer class={`${displayClass ?? ""}`}>
|
||||||
|
<hr />
|
||||||
<p>
|
<p>
|
||||||
{i18n(cfg.locale).components.footer.createdWith}{" "}
|
{i18n(cfg.locale).components.footer.createdWith}{" "}
|
||||||
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
|
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
|
||||||
|
@ -4,9 +4,9 @@ import { Date, getDate } from "./Date"
|
|||||||
import { QuartzComponent, QuartzComponentProps } from "./types"
|
import { QuartzComponent, QuartzComponentProps } from "./types"
|
||||||
import { GlobalConfiguration } from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
|
|
||||||
export type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
export function byDateAndAlphabetical(
|
||||||
|
cfg: GlobalConfiguration,
|
||||||
export function byDateAndAlphabetical(cfg: GlobalConfiguration): SortFn {
|
): (f1: QuartzPluginData, f2: QuartzPluginData) => number {
|
||||||
return (f1, f2) => {
|
return (f1, f2) => {
|
||||||
if (f1.dates && f2.dates) {
|
if (f1.dates && f2.dates) {
|
||||||
// sort descending
|
// sort descending
|
||||||
@ -27,12 +27,10 @@ export function byDateAndAlphabetical(cfg: GlobalConfiguration): SortFn {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
limit?: number
|
limit?: number
|
||||||
sort?: SortFn
|
|
||||||
} & QuartzComponentProps
|
} & QuartzComponentProps
|
||||||
|
|
||||||
export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort }: Props) => {
|
export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit }: Props) => {
|
||||||
const sorter = sort ?? byDateAndAlphabetical(cfg)
|
let list = allFiles.sort(byDateAndAlphabetical(cfg))
|
||||||
let list = allFiles.sort(sorter)
|
|
||||||
if (limit) {
|
if (limit) {
|
||||||
list = list.slice(0, limit)
|
list = list.slice(0, limit)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import DesktopOnly from "./DesktopOnly"
|
|||||||
import MobileOnly from "./MobileOnly"
|
import MobileOnly from "./MobileOnly"
|
||||||
import RecentNotes from "./RecentNotes"
|
import RecentNotes from "./RecentNotes"
|
||||||
import Breadcrumbs from "./Breadcrumbs"
|
import Breadcrumbs from "./Breadcrumbs"
|
||||||
import Comments from "./Comments"
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ArticleTitle,
|
ArticleTitle,
|
||||||
@ -43,5 +42,4 @@ export {
|
|||||||
RecentNotes,
|
RecentNotes,
|
||||||
NotFound,
|
NotFound,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
Comments,
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } fro
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
import style from "../styles/listPage.scss"
|
import style from "../styles/listPage.scss"
|
||||||
import { PageList, SortFn } from "../PageList"
|
import { PageList } from "../PageList"
|
||||||
import { stripSlashes, simplifySlug } from "../../util/path"
|
import { stripSlashes, simplifySlug } from "../../util/path"
|
||||||
import { Root } from "hast"
|
import { Root } from "hast"
|
||||||
import { htmlToJsx } from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
@ -13,7 +13,6 @@ interface FolderContentOptions {
|
|||||||
* Whether to display number of folders
|
* Whether to display number of folders
|
||||||
*/
|
*/
|
||||||
showFolderCount: boolean
|
showFolderCount: boolean
|
||||||
sort?: SortFn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: FolderContentOptions = {
|
const defaultOptions: FolderContentOptions = {
|
||||||
@ -38,7 +37,6 @@ export default ((opts?: Partial<FolderContentOptions>) => {
|
|||||||
const classes = ["popover-hint", ...cssClasses].join(" ")
|
const classes = ["popover-hint", ...cssClasses].join(" ")
|
||||||
const listProps = {
|
const listProps = {
|
||||||
...props,
|
...props,
|
||||||
sort: options.sort,
|
|
||||||
allFiles: allPagesInFolder,
|
allFiles: allPagesInFolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,127 +1,113 @@
|
|||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
import style from "../styles/listPage.scss"
|
import style from "../styles/listPage.scss"
|
||||||
import { PageList, SortFn } from "../PageList"
|
import { PageList } from "../PageList"
|
||||||
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
|
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
|
||||||
import { QuartzPluginData } from "../../plugins/vfile"
|
import { QuartzPluginData } from "../../plugins/vfile"
|
||||||
import { Root } from "hast"
|
import { Root } from "hast"
|
||||||
import { htmlToJsx } from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import { i18n } from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
interface TagContentOptions {
|
const numPages = 10
|
||||||
sort?: SortFn
|
const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
|
||||||
numPages: number
|
const { tree, fileData, allFiles, cfg } = props
|
||||||
}
|
const slug = fileData.slug
|
||||||
|
|
||||||
const defaultOptions: TagContentOptions = {
|
if (!(slug?.startsWith("tags/") || slug === "tags")) {
|
||||||
numPages: 10,
|
throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`)
|
||||||
}
|
|
||||||
|
|
||||||
export default ((opts?: Partial<TagContentOptions>) => {
|
|
||||||
const options: TagContentOptions = { ...defaultOptions, ...opts }
|
|
||||||
|
|
||||||
const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
|
|
||||||
const { tree, fileData, allFiles, cfg } = props
|
|
||||||
const slug = fileData.slug
|
|
||||||
|
|
||||||
if (!(slug?.startsWith("tags/") || slug === "tags")) {
|
|
||||||
throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
|
|
||||||
const allPagesWithTag = (tag: string) =>
|
|
||||||
allFiles.filter((file) =>
|
|
||||||
(file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag),
|
|
||||||
)
|
|
||||||
|
|
||||||
const content =
|
|
||||||
(tree as Root).children.length === 0
|
|
||||||
? fileData.description
|
|
||||||
: htmlToJsx(fileData.filePath!, tree)
|
|
||||||
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
|
|
||||||
const classes = ["popover-hint", ...cssClasses].join(" ")
|
|
||||||
if (tag === "/") {
|
|
||||||
const tags = [
|
|
||||||
...new Set(
|
|
||||||
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
|
|
||||||
),
|
|
||||||
].sort((a, b) => a.localeCompare(b))
|
|
||||||
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
|
|
||||||
for (const tag of tags) {
|
|
||||||
tagItemMap.set(tag, allPagesWithTag(tag))
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div class={classes}>
|
|
||||||
<article>
|
|
||||||
<p>{content}</p>
|
|
||||||
</article>
|
|
||||||
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p>
|
|
||||||
<div>
|
|
||||||
{tags.map((tag) => {
|
|
||||||
const pages = tagItemMap.get(tag)!
|
|
||||||
const listProps = {
|
|
||||||
...props,
|
|
||||||
allFiles: pages,
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0)
|
|
||||||
|
|
||||||
const root = contentPage?.htmlAst
|
|
||||||
const content =
|
|
||||||
!root || root?.children.length === 0
|
|
||||||
? contentPage?.description
|
|
||||||
: htmlToJsx(contentPage.filePath!, root)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>
|
|
||||||
<a class="internal tag-link" href={`../tags/${tag}`}>
|
|
||||||
{tag}
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
{content && <p>{content}</p>}
|
|
||||||
<div class="page-listing">
|
|
||||||
<p>
|
|
||||||
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
|
|
||||||
{pages.length > options.numPages && (
|
|
||||||
<>
|
|
||||||
{" "}
|
|
||||||
<span>
|
|
||||||
{i18n(cfg.locale).pages.tagContent.showingFirst({
|
|
||||||
count: options.numPages,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<PageList limit={options.numPages} {...listProps} sort={opts?.sort} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const pages = allPagesWithTag(tag)
|
|
||||||
const listProps = {
|
|
||||||
...props,
|
|
||||||
allFiles: pages,
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class={classes}>
|
|
||||||
<article>{content}</article>
|
|
||||||
<div class="page-listing">
|
|
||||||
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
|
|
||||||
<div>
|
|
||||||
<PageList {...listProps} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TagContent.css = style + PageList.css
|
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
|
||||||
return TagContent
|
const allPagesWithTag = (tag: string) =>
|
||||||
}) satisfies QuartzComponentConstructor
|
allFiles.filter((file) =>
|
||||||
|
(file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag),
|
||||||
|
)
|
||||||
|
|
||||||
|
const content =
|
||||||
|
(tree as Root).children.length === 0
|
||||||
|
? fileData.description
|
||||||
|
: htmlToJsx(fileData.filePath!, tree)
|
||||||
|
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
|
||||||
|
const classes = ["popover-hint", ...cssClasses].join(" ")
|
||||||
|
if (tag === "/") {
|
||||||
|
const tags = [
|
||||||
|
...new Set(
|
||||||
|
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
|
||||||
|
),
|
||||||
|
].sort((a, b) => a.localeCompare(b))
|
||||||
|
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
|
||||||
|
for (const tag of tags) {
|
||||||
|
tagItemMap.set(tag, allPagesWithTag(tag))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class={classes}>
|
||||||
|
<article>
|
||||||
|
<p>{content}</p>
|
||||||
|
</article>
|
||||||
|
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p>
|
||||||
|
<div>
|
||||||
|
{tags.map((tag) => {
|
||||||
|
const pages = tagItemMap.get(tag)!
|
||||||
|
const listProps = {
|
||||||
|
...props,
|
||||||
|
allFiles: pages,
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0)
|
||||||
|
|
||||||
|
const root = contentPage?.htmlAst
|
||||||
|
const content =
|
||||||
|
!root || root?.children.length === 0
|
||||||
|
? contentPage?.description
|
||||||
|
: htmlToJsx(contentPage.filePath!, root)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
<a class="internal tag-link" href={`../tags/${tag}`}>
|
||||||
|
{tag}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
{content && <p>{content}</p>}
|
||||||
|
<div class="page-listing">
|
||||||
|
<p>
|
||||||
|
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
|
||||||
|
{pages.length > numPages && (
|
||||||
|
<>
|
||||||
|
{" "}
|
||||||
|
<span>
|
||||||
|
{i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<PageList limit={numPages} {...listProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const pages = allPagesWithTag(tag)
|
||||||
|
const listProps = {
|
||||||
|
...props,
|
||||||
|
allFiles: pages,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={classes}>
|
||||||
|
<article>{content}</article>
|
||||||
|
<div class="page-listing">
|
||||||
|
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
|
||||||
|
<div>
|
||||||
|
<PageList {...listProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TagContent.css = style + PageList.css
|
||||||
|
export default (() => TagContent) satisfies QuartzComponentConstructor
|
||||||
|
@ -14,7 +14,6 @@ interface RenderComponents {
|
|||||||
header: QuartzComponent[]
|
header: QuartzComponent[]
|
||||||
beforeBody: QuartzComponent[]
|
beforeBody: QuartzComponent[]
|
||||||
pageBody: QuartzComponent
|
pageBody: QuartzComponent
|
||||||
afterBody: QuartzComponent[]
|
|
||||||
left: QuartzComponent[]
|
left: QuartzComponent[]
|
||||||
right: QuartzComponent[]
|
right: QuartzComponent[]
|
||||||
footer: QuartzComponent
|
footer: QuartzComponent
|
||||||
@ -188,7 +187,6 @@ export function renderPage(
|
|||||||
header,
|
header,
|
||||||
beforeBody,
|
beforeBody,
|
||||||
pageBody: Content,
|
pageBody: Content,
|
||||||
afterBody,
|
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
footer: Footer,
|
footer: Footer,
|
||||||
@ -234,12 +232,6 @@ export function renderPage(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Content {...componentData} />
|
<Content {...componentData} />
|
||||||
<hr />
|
|
||||||
<div class="page-footer">
|
|
||||||
{afterBody.map((BodyComponent) => (
|
|
||||||
<BodyComponent {...componentData} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{RightComponent}
|
{RightComponent}
|
||||||
</Body>
|
</Body>
|
||||||
|
@ -361,7 +361,7 @@ function renderGlobalGraph() {
|
|||||||
|
|
||||||
document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
||||||
const slug = e.detail.url
|
const slug = e.detail.url
|
||||||
addToVisited(simplifySlug(slug))
|
addToVisited(slug)
|
||||||
await renderGraph("graph-container", slug)
|
await renderGraph("graph-container", slug)
|
||||||
|
|
||||||
const containerIcon = document.getElementById("global-graph-icon")
|
const containerIcon = document.getElementById("global-graph-icon")
|
||||||
|
@ -3,7 +3,7 @@ import { normalizeRelativeURLs } from "../../util/path"
|
|||||||
|
|
||||||
const p = new DOMParser()
|
const p = new DOMParser()
|
||||||
async function mouseEnterHandler(
|
async function mouseEnterHandler(
|
||||||
this: HTMLAnchorElement,
|
this: HTMLLinkElement,
|
||||||
{ clientX, clientY }: { clientX: number; clientY: number },
|
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||||
) {
|
) {
|
||||||
const link = this
|
const link = this
|
||||||
@ -33,7 +33,7 @@ async function mouseEnterHandler(
|
|||||||
thisUrl.hash = ""
|
thisUrl.hash = ""
|
||||||
thisUrl.search = ""
|
thisUrl.search = ""
|
||||||
const targetUrl = new URL(link.href)
|
const targetUrl = new URL(link.href)
|
||||||
const hash = decodeURIComponent(targetUrl.hash)
|
const hash = targetUrl.hash
|
||||||
targetUrl.hash = ""
|
targetUrl.hash = ""
|
||||||
targetUrl.search = ""
|
targetUrl.search = ""
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ async function mouseEnterHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
document.addEventListener("nav", () => {
|
||||||
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
|
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
link.addEventListener("mouseenter", mouseEnterHandler)
|
link.addEventListener("mouseenter", mouseEnterHandler)
|
||||||
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
|
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
|
||||||
|
@ -11,7 +11,7 @@ li.section-li {
|
|||||||
|
|
||||||
& > .section {
|
& > .section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: fit-content(8em) 3fr 1fr;
|
grid-template-columns: 6em 3fr 1fr;
|
||||||
|
|
||||||
@media all and (max-width: $mobileBreakpoint) {
|
@media all and (max-width: $mobileBreakpoint) {
|
||||||
& > .tags {
|
& > .tags {
|
||||||
@ -24,7 +24,8 @@ li.section-li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
& > .meta {
|
& > .meta {
|
||||||
margin: 0 1em 0 0;
|
margin: 0;
|
||||||
|
flex-basis: 6em;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,8 +33,7 @@ li.section-li {
|
|||||||
|
|
||||||
// modifications in popover context
|
// modifications in popover context
|
||||||
.popover .section {
|
.popover .section {
|
||||||
grid-template-columns: fit-content(8em) 1fr !important;
|
grid-template-columns: 6em 1fr !important;
|
||||||
|
|
||||||
& > .tags {
|
& > .tags {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { Translation, CalloutTranslation } from "./locales/definition"
|
import { Translation, CalloutTranslation } from "./locales/definition"
|
||||||
import enUs from "./locales/en-US"
|
import en from "./locales/en-US"
|
||||||
import enGb from "./locales/en-GB"
|
|
||||||
import fr from "./locales/fr-FR"
|
import fr from "./locales/fr-FR"
|
||||||
import it from "./locales/it-IT"
|
import it from "./locales/it-IT"
|
||||||
import ja from "./locales/ja-JP"
|
import ja from "./locales/ja-JP"
|
||||||
import de from "./locales/de-DE"
|
import de from "./locales/de-DE"
|
||||||
import nl from "./locales/nl-NL"
|
import nl from "./locales/nl-NL"
|
||||||
import ro from "./locales/ro-RO"
|
import ro from "./locales/ro-RO"
|
||||||
import ca from "./locales/ca-ES"
|
|
||||||
import es from "./locales/es-ES"
|
import es from "./locales/es-ES"
|
||||||
import ar from "./locales/ar-SA"
|
import ar from "./locales/ar-SA"
|
||||||
import uk from "./locales/uk-UA"
|
import uk from "./locales/uk-UA"
|
||||||
@ -21,8 +19,7 @@ import fa from "./locales/fa-IR"
|
|||||||
import pl from "./locales/pl-PL"
|
import pl from "./locales/pl-PL"
|
||||||
|
|
||||||
export const TRANSLATIONS = {
|
export const TRANSLATIONS = {
|
||||||
"en-US": enUs,
|
"en-US": en,
|
||||||
"en-GB": enGb,
|
|
||||||
"fr-FR": fr,
|
"fr-FR": fr,
|
||||||
"it-IT": it,
|
"it-IT": it,
|
||||||
"ja-JP": ja,
|
"ja-JP": ja,
|
||||||
@ -31,7 +28,6 @@ export const TRANSLATIONS = {
|
|||||||
"nl-BE": nl,
|
"nl-BE": nl,
|
||||||
"ro-RO": ro,
|
"ro-RO": ro,
|
||||||
"ro-MD": ro,
|
"ro-MD": ro,
|
||||||
"ca-ES": ca,
|
|
||||||
"es-ES": es,
|
"es-ES": es,
|
||||||
"ar-SA": ar,
|
"ar-SA": ar,
|
||||||
"ar-AE": ar,
|
"ar-AE": ar,
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
import { Translation } from "./definition"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
propertyDefaults: {
|
|
||||||
title: "Sense títol",
|
|
||||||
description: "Sense descripció",
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
callout: {
|
|
||||||
note: "Nota",
|
|
||||||
abstract: "Resum",
|
|
||||||
info: "Informació",
|
|
||||||
todo: "Per fer",
|
|
||||||
tip: "Consell",
|
|
||||||
success: "Èxit",
|
|
||||||
question: "Pregunta",
|
|
||||||
warning: "Advertència",
|
|
||||||
failure: "Fall",
|
|
||||||
danger: "Perill",
|
|
||||||
bug: "Error",
|
|
||||||
example: "Exemple",
|
|
||||||
quote: "Cita",
|
|
||||||
},
|
|
||||||
backlinks: {
|
|
||||||
title: "Retroenllaç",
|
|
||||||
noBacklinksFound: "No s'han trobat retroenllaços",
|
|
||||||
},
|
|
||||||
themeToggle: {
|
|
||||||
lightMode: "Mode clar",
|
|
||||||
darkMode: "Mode fosc",
|
|
||||||
},
|
|
||||||
explorer: {
|
|
||||||
title: "Explorador",
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
createdWith: "Creat amb",
|
|
||||||
},
|
|
||||||
graph: {
|
|
||||||
title: "Vista Gràfica",
|
|
||||||
},
|
|
||||||
recentNotes: {
|
|
||||||
title: "Notes Recents",
|
|
||||||
seeRemainingMore: ({ remaining }) => `Vegi ${remaining} més →`,
|
|
||||||
},
|
|
||||||
transcludes: {
|
|
||||||
transcludeOf: ({ targetSlug }) => `Transcluit de ${targetSlug}`,
|
|
||||||
linkToOriginal: "Enllaç a l'original",
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
title: "Cercar",
|
|
||||||
searchBarPlaceholder: "Cerca alguna cosa",
|
|
||||||
},
|
|
||||||
tableOfContents: {
|
|
||||||
title: "Taula de Continguts",
|
|
||||||
},
|
|
||||||
contentMeta: {
|
|
||||||
readingTime: ({ minutes }) => `Es llegeix en ${minutes} min`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pages: {
|
|
||||||
rss: {
|
|
||||||
recentNotes: "Notes recents",
|
|
||||||
lastFewNotes: ({ count }) => `Últimes ${count} notes`,
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
title: "No s'ha trobat.",
|
|
||||||
notFound: "Aquesta pàgina és privada o no existeix.",
|
|
||||||
home: "Torna a la pàgina principal",
|
|
||||||
},
|
|
||||||
folderContent: {
|
|
||||||
folder: "Carpeta",
|
|
||||||
itemsUnderFolder: ({ count }) =>
|
|
||||||
count === 1 ? "1 article en aquesta carpeta." : `${count} articles en esta carpeta.`,
|
|
||||||
},
|
|
||||||
tagContent: {
|
|
||||||
tag: "Etiqueta",
|
|
||||||
tagIndex: "índex d'Etiquetes",
|
|
||||||
itemsUnderTag: ({ count }) =>
|
|
||||||
count === 1 ? "1 article amb aquesta etiqueta." : `${count} article amb aquesta etiqueta.`,
|
|
||||||
showingFirst: ({ count }) => `Mostrant les primeres ${count} etiquetes.`,
|
|
||||||
totalTags: ({ count }) => `S'han trobat ${count} etiquetes en total.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const satisfies Translation
|
|
@ -1,84 +0,0 @@
|
|||||||
import { Translation } from "./definition"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
propertyDefaults: {
|
|
||||||
title: "Untitled",
|
|
||||||
description: "No description provided",
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
callout: {
|
|
||||||
note: "Note",
|
|
||||||
abstract: "Abstract",
|
|
||||||
info: "Info",
|
|
||||||
todo: "To-Do",
|
|
||||||
tip: "Tip",
|
|
||||||
success: "Success",
|
|
||||||
question: "Question",
|
|
||||||
warning: "Warning",
|
|
||||||
failure: "Failure",
|
|
||||||
danger: "Danger",
|
|
||||||
bug: "Bug",
|
|
||||||
example: "Example",
|
|
||||||
quote: "Quote",
|
|
||||||
},
|
|
||||||
backlinks: {
|
|
||||||
title: "Backlinks",
|
|
||||||
noBacklinksFound: "No backlinks found",
|
|
||||||
},
|
|
||||||
themeToggle: {
|
|
||||||
lightMode: "Light mode",
|
|
||||||
darkMode: "Dark mode",
|
|
||||||
},
|
|
||||||
explorer: {
|
|
||||||
title: "Explorer",
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
createdWith: "Created with",
|
|
||||||
},
|
|
||||||
graph: {
|
|
||||||
title: "Graph View",
|
|
||||||
},
|
|
||||||
recentNotes: {
|
|
||||||
title: "Recent Notes",
|
|
||||||
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
|
||||||
},
|
|
||||||
transcludes: {
|
|
||||||
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
|
||||||
linkToOriginal: "Link to original",
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
title: "Search",
|
|
||||||
searchBarPlaceholder: "Search for something",
|
|
||||||
},
|
|
||||||
tableOfContents: {
|
|
||||||
title: "Table of Contents",
|
|
||||||
},
|
|
||||||
contentMeta: {
|
|
||||||
readingTime: ({ minutes }) => `${minutes} min read`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pages: {
|
|
||||||
rss: {
|
|
||||||
recentNotes: "Recent notes",
|
|
||||||
lastFewNotes: ({ count }) => `Last ${count} notes`,
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
title: "Not Found",
|
|
||||||
notFound: "Either this page is private or doesn't exist.",
|
|
||||||
home: "Return to Homepage",
|
|
||||||
},
|
|
||||||
folderContent: {
|
|
||||||
folder: "Folder",
|
|
||||||
itemsUnderFolder: ({ count }) =>
|
|
||||||
count === 1 ? "1 item under this folder." : `${count} items under this folder.`,
|
|
||||||
},
|
|
||||||
tagContent: {
|
|
||||||
tag: "Tag",
|
|
||||||
tagIndex: "Tag Index",
|
|
||||||
itemsUnderTag: ({ count }) =>
|
|
||||||
count === 1 ? "1 item with this tag." : `${count} items with this tag.`,
|
|
||||||
showingFirst: ({ count }) => `Showing first ${count} tags.`,
|
|
||||||
totalTags: ({ count }) => `Found ${count} total tags.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const satisfies Translation
|
|
@ -22,8 +22,8 @@ export default {
|
|||||||
quote: "Cita",
|
quote: "Cita",
|
||||||
},
|
},
|
||||||
backlinks: {
|
backlinks: {
|
||||||
title: "Retroenlaces",
|
title: "Enlaces de Retroceso",
|
||||||
noBacklinksFound: "No se han encontrado retroenlaces",
|
noBacklinksFound: "No se han encontrado enlaces traseros",
|
||||||
},
|
},
|
||||||
themeToggle: {
|
themeToggle: {
|
||||||
lightMode: "Modo claro",
|
lightMode: "Modo claro",
|
||||||
@ -54,18 +54,18 @@ export default {
|
|||||||
title: "Tabla de Contenidos",
|
title: "Tabla de Contenidos",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({ minutes }) => `Se lee en ${minutes} min`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Notas recientes",
|
recentNotes: "Notas recientes",
|
||||||
lastFewNotes: ({ count }) => `Últimas ${count} notas`,
|
lastFewNotes: ({ count }) => `Últimás ${count} notas`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "No se ha encontrado.",
|
title: "No se encontró.",
|
||||||
notFound: "Esta página es privada o no existe.",
|
notFound: "Esta página es privada o no existe.",
|
||||||
home: "Regresa a la página principal",
|
home: "Regresar a la página principal",
|
||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Carpeta",
|
folder: "Carpeta",
|
||||||
@ -78,7 +78,7 @@ export default {
|
|||||||
itemsUnderTag: ({ count }) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 artículo con esta etiqueta." : `${count} artículos con esta etiqueta.`,
|
count === 1 ? "1 artículo con esta etiqueta." : `${count} artículos con esta etiqueta.`,
|
||||||
showingFirst: ({ count }) => `Mostrando las primeras ${count} etiquetas.`,
|
showingFirst: ({ count }) => `Mostrando las primeras ${count} etiquetas.`,
|
||||||
totalTags: ({ count }) => `Se han encontrado ${count} etiquetas en total.`,
|
totalTags: ({ count }) => `Se encontraron ${count} etiquetas en total.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
@ -54,7 +54,7 @@ export default {
|
|||||||
title: "Зміст",
|
title: "Зміст",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({ minutes }) => `${minutes} хв читання`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
@ -68,17 +68,17 @@ export default {
|
|||||||
home: "Повернутися на головну сторінку",
|
home: "Повернутися на головну сторінку",
|
||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Тека",
|
folder: "Папка",
|
||||||
itemsUnderFolder: ({ count }) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1 ? "У цій теці 1 елемент." : `Елементів у цій теці: ${count}.`,
|
count === 1 ? "У цій папці 1 елемент." : `Елементів у цій папці: ${count}.`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Мітка",
|
tag: "Тег",
|
||||||
tagIndex: "Індекс мітки",
|
tagIndex: "Індекс тегу",
|
||||||
itemsUnderTag: ({ count }) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 елемент з цією міткою." : `Елементів з цією міткою: ${count}.`,
|
count === 1 ? "1 елемент з цим тегом." : `Елементів з цим тегом: ${count}.`,
|
||||||
showingFirst: ({ count }) => `Показ перших ${count} міток.`,
|
showingFirst: ({ count }) => `Показ перших ${count} тегів.`,
|
||||||
totalTags: ({ count }) => `Всього знайдено міток: ${count}.`,
|
totalTags: ({ count }) => `Всього знайдено тегів: ${count}.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
@ -144,14 +144,6 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso
|
|||||||
tinylyticsScript.defer = true
|
tinylyticsScript.defer = true
|
||||||
document.head.appendChild(tinylyticsScript)
|
document.head.appendChild(tinylyticsScript)
|
||||||
`)
|
`)
|
||||||
} 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.defer = true
|
|
||||||
cabinScript.async = true
|
|
||||||
document.head.appendChild(cabinScript)
|
|
||||||
`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg.enableSPA) {
|
if (cfg.enableSPA) {
|
||||||
|
@ -59,25 +59,14 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
|
|||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "ContentPage",
|
name: "ContentPage",
|
||||||
getQuartzComponents() {
|
getQuartzComponents() {
|
||||||
return [
|
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||||
Head,
|
|
||||||
Header,
|
|
||||||
Body,
|
|
||||||
...header,
|
|
||||||
...beforeBody,
|
|
||||||
pageBody,
|
|
||||||
...afterBody,
|
|
||||||
...left,
|
|
||||||
...right,
|
|
||||||
Footer,
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
async getDependencyGraph(ctx, content, _resources) {
|
async getDependencyGraph(ctx, content, _resources) {
|
||||||
const graph = new DepGraph<FilePath>()
|
const graph = new DepGraph<FilePath>()
|
||||||
|
@ -3,7 +3,7 @@ import { QuartzComponentProps } from "../../components/types"
|
|||||||
import HeaderConstructor from "../../components/Header"
|
import HeaderConstructor from "../../components/Header"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import { pageResources, renderPage } from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile"
|
import { ProcessedContent, defaultProcessedContent } from "../vfile"
|
||||||
import { FullPageLayout } from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {
|
import {
|
||||||
@ -21,37 +21,22 @@ import { write } from "./helpers"
|
|||||||
import { i18n } from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
interface FolderPageOptions extends FullPageLayout {
|
export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||||
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (userOpts) => {
|
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...defaultListPageLayout,
|
...defaultListPageLayout,
|
||||||
pageBody: FolderContent({ sort: userOpts?.sort }),
|
pageBody: FolderContent(),
|
||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "FolderPage",
|
name: "FolderPage",
|
||||||
getQuartzComponents() {
|
getQuartzComponents() {
|
||||||
return [
|
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||||
Head,
|
|
||||||
Header,
|
|
||||||
Body,
|
|
||||||
...header,
|
|
||||||
...beforeBody,
|
|
||||||
pageBody,
|
|
||||||
...afterBody,
|
|
||||||
...left,
|
|
||||||
...right,
|
|
||||||
Footer,
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
async getDependencyGraph(_ctx, content, _resources) {
|
async getDependencyGraph(_ctx, content, _resources) {
|
||||||
// Example graph:
|
// Example graph:
|
||||||
|
@ -3,7 +3,7 @@ import { QuartzComponentProps } from "../../components/types"
|
|||||||
import HeaderConstructor from "../../components/Header"
|
import HeaderConstructor from "../../components/Header"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import { pageResources, renderPage } from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile"
|
import { ProcessedContent, defaultProcessedContent } from "../vfile"
|
||||||
import { FullPageLayout } from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import {
|
import {
|
||||||
FilePath,
|
FilePath,
|
||||||
@ -18,37 +18,22 @@ import { write } from "./helpers"
|
|||||||
import { i18n } from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
interface TagPageOptions extends FullPageLayout {
|
export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||||
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TagPage: QuartzEmitterPlugin<Partial<TagPageOptions>> = (userOpts) => {
|
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...defaultListPageLayout,
|
...defaultListPageLayout,
|
||||||
pageBody: TagContent({ sort: userOpts?.sort }),
|
pageBody: TagContent(),
|
||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "TagPage",
|
name: "TagPage",
|
||||||
getQuartzComponents() {
|
getQuartzComponents() {
|
||||||
return [
|
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||||
Head,
|
|
||||||
Header,
|
|
||||||
Body,
|
|
||||||
...header,
|
|
||||||
...beforeBody,
|
|
||||||
pageBody,
|
|
||||||
...afterBody,
|
|
||||||
...left,
|
|
||||||
...right,
|
|
||||||
Footer,
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
async getDependencyGraph(ctx, content, _resources) {
|
async getDependencyGraph(ctx, content, _resources) {
|
||||||
const graph = new DepGraph<FilePath>()
|
const graph = new DepGraph<FilePath>()
|
||||||
|
@ -3,7 +3,7 @@ import { QuartzFilterPlugin } from "../types"
|
|||||||
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
|
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
|
||||||
name: "RemoveDrafts",
|
name: "RemoveDrafts",
|
||||||
shouldPublish(_ctx, [_tree, vfile]) {
|
shouldPublish(_ctx, [_tree, vfile]) {
|
||||||
const draftFlag: boolean = vfile.data?.frontmatter?.draft || false
|
const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false
|
||||||
return !draftFlag
|
return !draftFlag
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -93,7 +93,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
|
|||||||
}
|
}
|
||||||
node.properties.className = classes
|
node.properties.className = classes
|
||||||
|
|
||||||
if (isExternal && opts.openLinksInNewTab) {
|
if (opts.openLinksInNewTab) {
|
||||||
node.properties.target = "_blank"
|
node.properties.target = "_blank"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ import { QuartzTransformerPlugin } from "../types"
|
|||||||
import { Root, Html, BlockContent, DefinitionContent, Paragraph, Code } from "mdast"
|
import { Root, Html, BlockContent, DefinitionContent, Paragraph, Code } from "mdast"
|
||||||
import { Element, Literal, Root as HtmlRoot } from "hast"
|
import { Element, Literal, Root as HtmlRoot } from "hast"
|
||||||
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||||
|
import { slug as slugAnchor } from "github-slugger"
|
||||||
import rehypeRaw from "rehype-raw"
|
import rehypeRaw from "rehype-raw"
|
||||||
import { SKIP, visit } from "unist-util-visit"
|
import { SKIP, visit } from "unist-util-visit"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { splitAnchor } from "../../util/path"
|
|
||||||
import { JSResource } from "../../util/resources"
|
import { JSResource } from "../../util/resources"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import calloutScript from "../../components/scripts/callout.inline.ts"
|
import calloutScript from "../../components/scripts/callout.inline.ts"
|
||||||
@ -97,7 +97,7 @@ function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping {
|
|||||||
|
|
||||||
export const externalLinkRegex = /^https?:\/\//i
|
export const externalLinkRegex = /^https?:\/\//i
|
||||||
|
|
||||||
export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/g)
|
export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/, "g")
|
||||||
|
|
||||||
// !? -> optional embedding
|
// !? -> optional embedding
|
||||||
// \[\[ -> open brace
|
// \[\[ -> open brace
|
||||||
@ -105,30 +105,35 @@ export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/g)
|
|||||||
// (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
|
// (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
|
||||||
// (\\?\|[^\[\]\#]+)? -> optional escape \ then | then one or more non-special characters (alias)
|
// (\\?\|[^\[\]\#]+)? -> optional escape \ then | then one or more non-special characters (alias)
|
||||||
export const wikilinkRegex = new RegExp(
|
export const wikilinkRegex = new RegExp(
|
||||||
/!?\[\[([^\[\]\|\#\\]+)?(#+[^\[\]\|\#\\]+)?(\\?\|[^\[\]\#]+)?\]\]/g,
|
/!?\[\[([^\[\]\|\#\\]+)?(#+[^\[\]\|\#\\]+)?(\\?\|[^\[\]\#]+)?\]\]/,
|
||||||
|
"g",
|
||||||
)
|
)
|
||||||
|
|
||||||
// ^\|([^\n])+\|\n(\|) -> matches the header row
|
// ^\|([^\n])+\|\n(\|) -> matches the header row
|
||||||
// ( ?:?-{3,}:? ?\|)+ -> matches the header row separator
|
// ( ?:?-{3,}:? ?\|)+ -> matches the header row separator
|
||||||
// (\|([^\n])+\|\n)+ -> matches the body rows
|
// (\|([^\n])+\|\n)+ -> matches the body rows
|
||||||
export const tableRegex = new RegExp(/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/gm)
|
export const tableRegex = new RegExp(
|
||||||
|
/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/,
|
||||||
|
"gm",
|
||||||
|
)
|
||||||
|
|
||||||
// matches any wikilink, only used for escaping wikilinks inside tables
|
// matches any wikilink, only used for escaping wikilinks inside tables
|
||||||
export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/g)
|
export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/, "g")
|
||||||
|
|
||||||
const highlightRegex = new RegExp(/==([^=]+)==/g)
|
const highlightRegex = new RegExp(/==([^=]+)==/, "g")
|
||||||
const commentRegex = new RegExp(/%%[\s\S]*?%%/g)
|
const commentRegex = new RegExp(/%%[\s\S]*?%%/, "g")
|
||||||
// from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
|
// 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)
|
const calloutLineRegex = new RegExp(/^> *\[\!\w+\|?.*?\][+-]?.*$/, "gm")
|
||||||
// (?:^| ) -> non-capturing group, tag should start be separated by a space or be the start of the line
|
// (?:^| ) -> 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 #
|
// #(...) -> capturing group, tag itself must start with #
|
||||||
// (?:[-_\p{L}\d\p{Z}])+ -> non-capturing group, non-empty string of (Unicode-aware) alpha-numeric characters and symbols, hyphens and/or underscores
|
// (?:[-_\p{L}\d\p{Z}])+ -> non-capturing group, non-empty string of (Unicode-aware) alpha-numeric characters and symbols, hyphens and/or underscores
|
||||||
// (?:\/[-_\p{L}\d\p{Z}]+)*) -> non-capturing group, matches an arbitrary number of tag strings separated by "/"
|
// (?:\/[-_\p{L}\d\p{Z}]+)*) -> non-capturing group, matches an arbitrary number of tag strings separated by "/"
|
||||||
const tagRegex = new RegExp(
|
const tagRegex = new RegExp(
|
||||||
/(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/gu,
|
/(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/,
|
||||||
|
"gu",
|
||||||
)
|
)
|
||||||
const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/g)
|
const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/, "g")
|
||||||
const ytLinkRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
const ytLinkRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||||
const ytPlaylistLinkRegex = /[?&]list=([^#?&]*)/
|
const ytPlaylistLinkRegex = /[?&]list=([^#?&]*)/
|
||||||
const videoExtensionRegex = new RegExp(/\.(mp4|webm|ogg|avi|mov|flv|wmv|mkv|mpg|mpeg|3gp|m4v)$/)
|
const videoExtensionRegex = new RegExp(/\.(mp4|webm|ogg|avi|mov|flv|wmv|mkv|mpg|mpeg|3gp|m4v)$/)
|
||||||
@ -179,8 +184,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
// replace all wikilinks inside a table first
|
// replace all wikilinks inside a table first
|
||||||
src = src.replace(tableRegex, (value) => {
|
src = src.replace(tableRegex, (value) => {
|
||||||
// escape all aliases and headers in wikilinks inside a table
|
// escape all aliases and headers in wikilinks inside a table
|
||||||
return value.replace(tableWikilinkRegex, (_value, raw) => {
|
return value.replace(tableWikilinkRegex, (value, ...capture) => {
|
||||||
// const [raw]: (string | undefined)[] = capture
|
const [raw]: (string | undefined)[] = capture
|
||||||
let escaped = raw ?? ""
|
let escaped = raw ?? ""
|
||||||
escaped = escaped.replace("#", "\\#")
|
escaped = escaped.replace("#", "\\#")
|
||||||
// escape pipe characters if they are not already escaped
|
// escape pipe characters if they are not already escaped
|
||||||
@ -194,9 +199,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
src = src.replace(wikilinkRegex, (value, ...capture) => {
|
src = src.replace(wikilinkRegex, (value, ...capture) => {
|
||||||
const [rawFp, rawHeader, rawAlias]: (string | undefined)[] = capture
|
const [rawFp, rawHeader, rawAlias]: (string | undefined)[] = capture
|
||||||
|
|
||||||
const [fp, anchor] = splitAnchor(`${rawFp ?? ""}${rawHeader ?? ""}`)
|
const fp = rawFp ?? ""
|
||||||
const blockRef = Boolean(rawHeader?.match(/^#?\^/)) ? "^" : ""
|
const anchor = rawHeader?.trim().replace(/^#+/, "")
|
||||||
const displayAnchor = anchor ? `#${blockRef}${anchor.trim().replace(/^#+/, "")}` : ""
|
const blockRef = Boolean(anchor?.startsWith("^")) ? "^" : ""
|
||||||
|
const displayAnchor = anchor ? `#${blockRef}${slugAnchor(anchor)}` : ""
|
||||||
const displayAlias = rawAlias ?? rawHeader?.replace("#", "|") ?? ""
|
const displayAlias = rawAlias ?? rawHeader?.replace("#", "|") ?? ""
|
||||||
const embedDisplay = value.startsWith("!") ? "!" : ""
|
const embedDisplay = value.startsWith("!") ? "!" : ""
|
||||||
|
|
||||||
@ -270,7 +276,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
return {
|
return {
|
||||||
type: "html",
|
type: "html",
|
||||||
data: { hProperties: { transclude: true } },
|
data: { hProperties: { transclude: true } },
|
||||||
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}" data-embed-alias="${alias}"><a href="${
|
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${
|
||||||
url + anchor
|
url + anchor
|
||||||
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
||||||
}
|
}
|
||||||
@ -408,8 +414,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// find first line and callout content
|
// find first line
|
||||||
const [firstChild, ...calloutContent] = node.children
|
const firstChild = node.children[0]
|
||||||
if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
|
if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -486,21 +492,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
"data-callout-metadata": calloutMetaData,
|
"data-callout-metadata": calloutMetaData,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add callout-content class to callout body if it has one.
|
|
||||||
if (calloutContent.length > 0) {
|
|
||||||
const contentData: BlockContent | DefinitionContent = {
|
|
||||||
data: {
|
|
||||||
hProperties: {
|
|
||||||
className: "callout-content",
|
|
||||||
},
|
|
||||||
hName: "div",
|
|
||||||
},
|
|
||||||
type: "blockquote",
|
|
||||||
children: [...calloutContent],
|
|
||||||
}
|
|
||||||
node.children = [node.children[0], contentData]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,11 @@ section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-highlight {
|
.text-highlight {
|
||||||
background-color: var(--textHighlight);
|
background-color: #fff23688;
|
||||||
padding: 0 0.1rem;
|
padding: 0 0.1rem;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0));
|
background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0));
|
||||||
color: var(--darkgray);
|
color: var(--darkgray);
|
||||||
@ -42,20 +43,8 @@ ul,
|
|||||||
.math {
|
.math {
|
||||||
color: var(--darkgray);
|
color: var(--darkgray);
|
||||||
fill: var(--darkgray);
|
fill: var(--darkgray);
|
||||||
hyphens: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
text,
|
|
||||||
a,
|
|
||||||
li,
|
|
||||||
ol,
|
|
||||||
ul,
|
|
||||||
.katex,
|
|
||||||
.math {
|
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
/* tr and td removed from list of selectors for overflow-wrap, allowing them to use default 'normal' property value */
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.math {
|
.math {
|
||||||
@ -201,19 +190,11 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .page-header,
|
|
||||||
& .page-footer {
|
|
||||||
width: $pageWidth;
|
|
||||||
margin-top: 1rem;
|
|
||||||
|
|
||||||
@media all and (max-width: $fullPageWidth) {
|
|
||||||
width: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .page-header {
|
& .page-header {
|
||||||
|
width: $pageWidth;
|
||||||
margin: $topSpacing auto 0 auto;
|
margin: $topSpacing auto 0 auto;
|
||||||
@media all and (max-width: $fullPageWidth) {
|
@media all and (max-width: $fullPageWidth) {
|
||||||
|
width: initial;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,10 +481,6 @@ video {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
div:has(> .overflow) {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.overflow,
|
ul.overflow,
|
||||||
ol.overflow {
|
ol.overflow {
|
||||||
max-height: 400;
|
max-height: 400;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
transition: max-height 0.3s ease;
|
transition: max-height 0.3s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
& > .callout-content > :first-child {
|
& > *:nth-child(2) {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ export interface ColorScheme {
|
|||||||
secondary: string
|
secondary: string
|
||||||
tertiary: string
|
tertiary: string
|
||||||
highlight: string
|
highlight: string
|
||||||
textHighlight: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Colors {
|
interface Colors {
|
||||||
@ -50,7 +49,6 @@ ${stylesheet.join("\n\n")}
|
|||||||
--secondary: ${theme.colors.lightMode.secondary};
|
--secondary: ${theme.colors.lightMode.secondary};
|
||||||
--tertiary: ${theme.colors.lightMode.tertiary};
|
--tertiary: ${theme.colors.lightMode.tertiary};
|
||||||
--highlight: ${theme.colors.lightMode.highlight};
|
--highlight: ${theme.colors.lightMode.highlight};
|
||||||
--textHighlight: ${theme.colors.lightMode.textHighlight};
|
|
||||||
|
|
||||||
--headerFont: "${theme.typography.header}", ${DEFAULT_SANS_SERIF};
|
--headerFont: "${theme.typography.header}", ${DEFAULT_SANS_SERIF};
|
||||||
--bodyFont: "${theme.typography.body}", ${DEFAULT_SANS_SERIF};
|
--bodyFont: "${theme.typography.body}", ${DEFAULT_SANS_SERIF};
|
||||||
@ -66,7 +64,6 @@ ${stylesheet.join("\n\n")}
|
|||||||
--secondary: ${theme.colors.darkMode.secondary};
|
--secondary: ${theme.colors.darkMode.secondary};
|
||||||
--tertiary: ${theme.colors.darkMode.tertiary};
|
--tertiary: ${theme.colors.darkMode.tertiary};
|
||||||
--highlight: ${theme.colors.darkMode.highlight};
|
--highlight: ${theme.colors.darkMode.highlight};
|
||||||
--textHighlight: ${theme.colors.darkMode.textHighlight};
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "preact"
|
"jsxImportSource": "preact",
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.tsx", "./package.json"],
|
"include": ["**/*.ts", "**/*.tsx", "./package.json"],
|
||||||
"exclude": ["build/**/*.d.ts"]
|
"exclude": ["build/**/*.d.ts"],
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user