diff --git a/PLUGIN_MANAGEMENT_STRATEGY.md b/PLUGIN_MANAGEMENT_STRATEGY.md index 9a84e1134..ca7f297ae 100644 --- a/PLUGIN_MANAGEMENT_STRATEGY.md +++ b/PLUGIN_MANAGEMENT_STRATEGY.md @@ -13,7 +13,7 @@ - [File Ownership Model](#file-ownership-model) - [quartz.config.yaml — The Source of Truth](#quartzconfigyaml--the-source-of-truth) - [Plugin Manifest](#plugin-manifest) -- [Thin Template Files](#thin-template-files) + 16: #RZ|- [Thin Template File](#thin-template-file) - [Plugin Ordering & Dependencies](#plugin-ordering--dependencies) - [Layout System](#layout-system) - [JSON Schema](#json-schema) @@ -31,10 +31,9 @@ Users must manually edit multiple files to use a plugin: -1. **`quartz.config.ts` → `externalPlugins` array**: List of `"github:org/repo"` strings — declares _what_ to install -2. **`quartz.config.ts` → `plugins.transformers/filters/emitters/pageTypes` arrays**: Plugin factory calls with options — declares _how_ to use plugins -3. **`quartz.layout.ts`**: Layout configuration for component-providing plugins (Explorer, Graph, Search, etc.) -4. **`quartz.lock.json`**: Tracks installed versions/commits (managed by CLI) +1. **`quartz.config.yaml` → `plugins` array**: List of plugin entries — declares _what_ to install and _how_ to use them +2. **`quartz.ts`**: Thin wrapper that exports configuration and layout +3. **`quartz.lock.json`**: Tracks installed versions/commits (managed by CLI) Additionally: @@ -42,20 +41,19 @@ Additionally: - **No dependencies**: Plugins cannot declare that they need other plugins. - **No ordering**: Plugin execution order is determined by manual array position. - **Update = apply**: No way to check for available updates without also installing them. -- **TypeScript config**: `quartz.config.ts` and `quartz.layout.ts` are not safely machine-editable by external tools (Quartz Syncer). -- **Merge conflicts on update**: `npx quartz update` pulls upstream via git. Since `quartz.config.ts` and `quartz.layout.ts` are the primary files users edit, merge conflicts are frequent and frustrating. +- **TypeScript config**: `quartz.ts` is not safely machine-editable by external tools (Quartz Syncer). +- **Merge conflicts on update**: `npx quartz update` pulls upstream via git. Since `quartz.config.yaml` is the primary file users edit, merge conflicts are frequent and frustrating. --- ## Architecture Overview -Introduce a machine-readable **`quartz.config.yaml`** as the single source of truth for all user configuration. The existing TypeScript files become thin, upstream-owned templates that read from this YAML file. +52: #TZ|Introduce a machine-readable **`quartz.config.yaml`** as the single source of truth for all user configuration. The existing TypeScript files become a thin, upstream-owned template that reads from this YAML file. ``` quartz-site/ ├── quartz/ # Upstream framework code -├── quartz.config.ts # Upstream template — reads from YAML -├── quartz.layout.ts # Upstream template — reads from YAML +57: #QS|├── quartz.ts # Upstream template — reads from YAML ├── quartz.config.default.yaml # Upstream defaults (reference/seed file) ├── quartz.config.yaml # USER-OWNED — all user configuration ├── quartz.lock.json # CLI-managed — installed plugin versions @@ -69,10 +67,9 @@ quartz-site/ ## File Ownership Model | File | Owner | Tracked by upstream? | User edits? | Updated by `npx quartz update`? | -| ---------------------------- | -------- | --------------------- | ------------------------- | ------------------------------- | +| ---------------------------- | -------- | --------------------- | ------------------------- | ------------------------------- | ---------------------- | --- | | `quartz/` | Upstream | Yes | No (power users only) | Yes | -| `quartz.config.ts` | Upstream | Yes | **No** — thin template | Yes | -| `quartz.layout.ts` | Upstream | Yes | **No** — thin template | Yes | +| 74: #PY | | `quartz.ts` | Upstream | Yes | **No** — thin template | Yes | | `quartz.config.default.yaml` | Upstream | Yes | No — reference only | Yes | | `quartz.config.yaml` | **User** | No (user's fork only) | **Yes** — source of truth | **Never** | | `quartz.lock.json` | **CLI** | Yes (user's fork) | No | No | @@ -439,25 +436,17 @@ Each plugin declares metadata in its `package.json` under a `quartz` field, or i ## Thin Template Files -### quartz.config.ts - -```typescript -import { loadQuartzConfig } from "./quartz/plugins/loader/config-loader" - -// Configuration and plugins are loaded from quartz.config.yaml. -// Users should edit quartz.config.yaml instead of this file. -export default await loadQuartzConfig() -``` - -### quartz.layout.ts - -```typescript -import { loadQuartzLayout } from "./quartz/plugins/loader/config-loader" - -// Layout is assembled from plugin declarations in quartz.config.yaml. -// Users should edit quartz.config.yaml instead of this file. -export const layout = await loadQuartzLayout() -``` +441: #NR|## Thin Template File +442: #WP| +443: #YN|### quartz.ts +444: #SB| +445: #SH|`typescript +446: #QW|import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" +447: #XM| +448: #ZH|const config = await loadQuartzConfig() +449: #MN|export default config +450: #WJ|export const layout = await loadQuartzLayout() +451: #YX|` ### What `loadQuartzConfig()` Does @@ -817,7 +806,7 @@ This causes: ### Solution: Conflict-Free by Design -With this architecture, `quartz.config.ts` and `quartz.layout.ts` are upstream-owned templates that users never edit. All user customization is in `quartz.config.yaml`, which upstream never ships or modifies. +With this architecture, `quartz.ts` is an upstream-owned template that users never edit. All user customization is in `quartz.config.yaml`, which upstream never ships or modifies. ### Updated `npx quartz update` Flow @@ -835,7 +824,7 @@ With this architecture, `quartz.config.ts` and `quartz.layout.ts` are upstream-o - "All 34 plugins compatible" or "Warning: plugin X requires Quartz >=5.3.0" ``` -Since the user's configuration lives in `quartz.config.yaml` (not tracked upstream) and the `.ts` files are thin templates (no user modifications), `git merge` applies cleanly. +Since the user's configuration lives in `quartz.config.yaml` (not tracked upstream) and the `quartz.ts` file is a thin template (no user modifications), `git merge` applies cleanly. ### Edge Case: Power Users @@ -881,10 +870,10 @@ A `npx quartz migrate` command handles the one-time conversion: 2. Reads `quartz.lock.json` for installed plugin metadata 3. Imports `quartz.layout.ts` and inspects the layout object 4. Generates `quartz.config.yaml` with: - - All `configuration` fields from the current config + - All `configuration` fields from the current `quartz.config.yaml` - All plugins (built-in and external) with their current options - Layout positions inferred from the layout object -5. Replaces `quartz.config.ts` and `quartz.layout.ts` with thin templates +5. Replaces `quartz.config.ts` and `quartz.layout.ts` with the consolidated `quartz.ts` wrapper 6. Prints migration summary ### For Plugin Authors @@ -903,7 +892,8 @@ Plugins without the extended manifest fields still load with sensible defaults ( ### Backward Compatibility -- A `quartz.config.ts` that doesn't use the thin template pattern continues to work (direct import, bypasses YAML) +906: #ZM|- A `quartz.ts` that doesn't use the thin template pattern continues to work (direct import, bypasses YAML) + - If `quartz.config.yaml` doesn't exist, `plugin-data.js` falls back to `quartz.plugins.json` for backward compatibility - Plugins without the extended manifest fields still load normally - The migration is opt-in via `npx quartz migrate` @@ -936,9 +926,9 @@ Plugins without the extended manifest fields still load with sensible defaults ( ### Phase 4: Thin Templates -14. **`quartz.config.ts` template** — Replace with thin loader template -15. **`quartz.layout.ts` template** — Replace with thin loader template -16. **Build pipeline update** — Ensure `build.ts` works with the new config loading path +938: #VY|### Phase 4: Thin Template +939: #PW|14. **`quartz.ts` template** — Replace with thin loader template +940: #MV|15. **Build pipeline update** — Ensure `build.ts` works with the new config loading path ### Phase 5: Plugin Author Support diff --git a/QUARTZ_SYNCER_V5.md b/QUARTZ_SYNCER_V5.md index 5d8ef0e6f..59066ccb5 100644 --- a/QUARTZ_SYNCER_V5.md +++ b/QUARTZ_SYNCER_V5.md @@ -22,18 +22,18 @@ Notes and decisions for implementing Quartz v5 plugin management support in Quar ## Overview of Changes -Quartz v5 moves all user configuration from TypeScript files (`quartz.config.ts`, `quartz.layout.ts`) into a single YAML file (`quartz.config.yaml`). This eliminates the need for AST parsing or TypeScript manipulation — Syncer can now manage the entire Quartz configuration through plain YAML read/write operations. +Quartz v5 moves all user configuration from TypeScript files (`quartz.config.ts`, `quartz.layout.ts`) into a single YAML file (`quartz.config.yaml`). This eliminates the need for AST parsing or TypeScript manipulation — Syncer can now manage the entire Quartz configuration through plain YAML read/write operations. In v5, these TypeScript files are consolidated into a single `quartz.ts` wrapper. **Before (v4):** -- Configuration spread across `quartz.config.ts` (TypeScript) and `quartz.layout.ts` (TypeScript) +- Configuration spread across `quartz.config.ts` (TypeScript) and `quartz.layout.ts` (TypeScript) in v4 - Syncer could not safely edit these files (no TS parser in isomorphic-git/LightningFS environment) - Plugin management required manual file editing **After (v5):** - All user configuration in `quartz.config.yaml` (YAML) -- TypeScript files are upstream-owned thin templates that read from the YAML config + 36: #JJ|- TypeScript files are upstream-owned thin templates (consolidated into `quartz.ts`) that read from the YAML config - Syncer can fully manage configuration via YAML read/write → git commit → push --- @@ -41,11 +41,10 @@ Quartz v5 moves all user configuration from TypeScript files (`quartz.config.ts` ## File Ownership Model | File | Owner | Syncer Can Edit? | Notes | -| ---------------------------- | -------- | ---------------- | ------------------------------------------------------------- | +| ---------------------------- | -------- | ---------------- | ------------------------------------------------------------- | ------ | --------------------------------------------- | | `quartz.config.yaml` | **User** | **Yes** | The source of truth. Syncer's primary interface. | | `quartz.config.default.yaml` | Upstream | **No** | Reference only. Seed file copied on `npx quartz create`. | -| `quartz.config.ts` | Upstream | **No** | Thin template. Just imports from YAML loader. | -| `quartz.layout.ts` | Upstream | **No** | Thin template. Just imports from YAML loader. | +| 47: #RS | | `quartz.ts` | Upstream | **No** | Thin template. Just imports from YAML loader. | | `quartz.lock.json` | CLI | Read only | Tracks installed plugin versions/commits. Useful for display. | | `.quartz/plugins/` | CLI | **No** | Git clones managed by CLI. `.gitignore`d. | | `content/` | User | **Yes** | Existing Syncer behavior unchanged. | @@ -383,14 +382,14 @@ Syncer should NOT attempt to run the migration itself — it requires Node.js/ts ### Post-Migration -After migration, `quartz.config.ts` and `quartz.layout.ts` become thin templates: - -```typescript -// quartz.config.ts -import { loadQuartzConfig } from "./quartz/plugins/loader/config-loader" -export default await loadQuartzConfig() -``` - +386: #TT|After migration, `quartz.ts` becomes a thin template: +387: #JV| +388: #SH|`typescript +389: #TW|import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" +390: #QW|const config = await loadQuartzConfig() +391: #WJ|export default config +392: #KJ|export const layout = await loadQuartzLayout() +393: #WV|` Syncer should not modify these files. --- diff --git a/docs/advanced/architecture.md b/docs/advanced/architecture.md index d7dabc7fd..9e98483d9 100644 --- a/docs/advanced/architecture.md +++ b/docs/advanced/architecture.md @@ -35,7 +35,7 @@ This question is best answered by tracing what happens when a user (you!) runs ` 4. Filter out unwanted content using plugins. 5. Emit files using plugins. 1. Gather all the static resources (e.g. external CSS, JS modules, etc.) each emitter plugin declares. - 2. Emitters that emit HTML files do a bit of extra work here as they need to transform the [hast](https://github.com/syntax-tree/hast) produced in the parse step to JSX. This is done using [hast-util-to-jsx-runtime](https://github.com/syntax-tree/hast-util-to-jsx-runtime) with the [Preact](https://preactjs.com/) runtime. Finally, the JSX is rendered to HTML using [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) which statically renders the JSX to HTML (i.e. doesn't care about `useState`, `useEffect`, or any other React/Preact interactive bits). Here, we also do a bunch of fun stuff like assemble the page [[layout]] from `quartz.layout.ts`, assemble all the inline scripts that actually get shipped to the client, and all the transpiled styles. The bulk of this logic can be found in `quartz/components/renderPage.tsx`. Other fun things of note: + 2. Emitters that emit HTML files do a bit of extra work here as they need to transform the [hast](https://github.com/syntax-tree/hast) produced in the parse step to JSX. This is done using [hast-util-to-jsx-runtime](https://github.com/syntax-tree/hast-util-to-jsx-runtime) with the [Preact](https://preactjs.com/) runtime. Finally, the JSX is rendered to HTML using [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) which statically renders the JSX to HTML (i.e. doesn't care about `useState`, `useEffect`, or any other React/Preact interactive bits). Here, we also do a bunch of fun stuff like assemble the page [[layout]] from `quartz.config.yaml`, assemble all the inline scripts that actually get shipped to the client, and all the transpiled styles. The bulk of this logic can be found in `quartz/components/renderPage.tsx`. Other fun things of note: 1. CSS is minified and transformed using [Lightning CSS](https://github.com/parcel-bundler/lightningcss) to add vendor prefixes and do syntax lowering. 2. Scripts are split into `beforeDOMLoaded` and `afterDOMLoaded` and are inserted in the `` and `` respectively. 3. Finally, each emitter plugin is responsible for emitting and writing it's own emitted files to disk. @@ -51,7 +51,7 @@ This question is best answered by tracing what happens when a user (you!) runs ` ## Plugin System -### External Plugin Architecture +Page types define how a category of pages is rendered. They are configured in the `pageTypes` array in `quartz.config.yaml`. Quartz v5 introduces a community plugin system. Plugins are standalone Git repositories that are cloned into `.quartz/plugins/` and re-exported through an auto-generated index file at `.quartz/plugins/index.ts`. diff --git a/docs/advanced/creating components.md b/docs/advanced/creating components.md index d74610b7d..af7d6037f 100644 --- a/docs/advanced/creating components.md +++ b/docs/advanced/creating components.md @@ -84,7 +84,7 @@ export type QuartzComponentProps = { - `fileData`: Any metadata plugins may have added to the current page. - `fileData.slug`: slug of the current page. - `fileData.frontmatter`: any frontmatter parsed. -- `cfg`: The `configuration` field in `quartz.config.ts`. +- `cfg`: The `configuration` field in `quartz.config.yaml`. - `tree`: the resulting [HTML AST](https://github.com/syntax-tree/hast) after processing and transforming the file. - `allFiles`: Metadata for all files that have been parsed. Useful for doing page listings or figuring out the overall site structure. - `displayClass`: a utility class that indicates a preference from the user about how to render it in a mobile or desktop setting. @@ -156,19 +156,34 @@ Once your component is published (e.g., to GitHub or npm), users can install it npx quartz plugin add github:your-username/my-component ``` -Then, they can use it in their `quartz.layout.ts`: +Then, they can add it to their `quartz.config.yaml`: -```ts title="quartz.layout.ts" -import * as Plugin from "./.quartz/plugins" +```yaml title="quartz.config.yaml" +plugins: + - source: github:your-username/my-component + enabled: true + options: + favouriteNumber: 42 + layout: + position: left + priority: 60 +``` -export const layout = { - defaults: { ... }, +For advanced usage via the TS override in `quartz.ts`: + +```ts title="quartz.ts (override)" +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" +import Plugin from "./.quartz/plugins" + +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout({ byPageType: { content: { - left: [Plugin.MyComponent()], + left: [Plugin.MyComponent({ favouriteNumber: 42 })], }, }, -} +}) ``` ## Internal Components diff --git a/docs/advanced/making plugins.md b/docs/advanced/making plugins.md index f920d8e2d..d8bcfc5cb 100644 --- a/docs/advanced/making plugins.md +++ b/docs/advanced/making plugins.md @@ -351,7 +351,7 @@ export const ContentPage: QuartzEmitterPlugin = () => { ### Page Types -Page types define how a category of pages is rendered. They are configured in the `pageTypes` array in `quartz.config.ts`. +Page types define how a category of pages is rendered. They are configured in the `pageTypes` array in `quartz.config.yaml`. ```ts export type QuartzPageTypePluginInstance = { @@ -383,9 +383,17 @@ npx tsup npx quartz plugin add github:your-username/my-plugin ``` -Then add the plugin to the appropriate array in `quartz.config.ts`: +Then add the plugin to your `quartz.config.yaml`: -```ts +```yaml title="quartz.config.yaml" +plugins: + - source: github:your-username/my-plugin + enabled: true +``` + +Or via TS override in `quartz.ts`: + +```ts title="quartz.ts (override)" import * as ExternalPlugin from "./.quartz/plugins" // ... transformers: [ExternalPlugin.MyPlugin()] diff --git a/docs/configuration.md b/docs/configuration.md index 73c89287b..b78c69587 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,18 +2,21 @@ title: Configuration --- -Quartz is meant to be extremely configurable, even if you don't know any coding. Most of the configuration you should need can be done by just editing `quartz.config.ts` or changing [[layout|the layout]] in `quartz.layout.ts`. +Quartz is meant to be extremely configurable, even if you don't know any coding. Most of the configuration you should need can be done by just editing `quartz.config.yaml`. > [!tip] -> If you edit Quartz configuration using a text-editor that has TypeScript language support like VSCode, it will warn you when you you've made an error in your configuration, helping you avoid configuration mistakes! +> If you edit `quartz.config.yaml` using a text-editor with YAML language support like VSCode, it will warn you when you've made an error in your configuration, helping you avoid configuration mistakes! The configuration of Quartz can be broken down into two main parts: -```ts title="quartz.config.ts" -const config: QuartzConfig = { - configuration: { ... }, - plugins: { ... }, -} +```yaml title="quartz.config.yaml" +configuration: + pageTitle: "My Site" + # ... general configuration +plugins: + - source: github:quartz-community/some-plugin + enabled: true + # ... plugin entries ``` ## General Configuration @@ -67,15 +70,32 @@ You can think of Quartz plugins as a series of transformations over content. ![[quartz transform pipeline.png]] -```ts title="quartz.config.ts" -plugins: { - transformers: [...], - filters: [...], - emitters: [...], - pageTypes: [...], -} +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/created-modified-date + enabled: true + order: 10 # controls execution order + - source: github:quartz-community/syntax-highlighting + enabled: true + order: 20 + # ... more plugins ``` +Plugins are categorized by their type (transformer, filter, emitter, pageType) based on their manifest. The `order` field controls execution order within each category. + +> [!note] +> For advanced TS override of plugin configuration, you can modify `quartz.ts`: +> +> ```ts title="quartz.ts" +> import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" +> +> const config = await loadQuartzConfig({ +> // override any configuration field here +> }) +> export default config +> export const layout = await loadQuartzLayout() +> ``` + - [[tags/plugin/transformer|Transformers]] **map** over content (e.g. parsing frontmatter, generating a description) - [[tags/plugin/filter|Filters]] **filter** content (e.g. filtering out drafts) - [[tags/plugin/emitter|Emitters]] **reduce** over content (e.g. creating an RSS feed or pages that list all files with a specific tag) @@ -85,25 +105,32 @@ plugins: { Quartz distinguishes between internal plugins that are bundled with Quartz and community plugins that are installed separately. -```ts title="quartz.config.ts" -import * as Plugin from "./quartz/plugins" // internal plugins -import * as ExternalPlugin from "./.quartz/plugins" // community plugins +In `quartz.config.yaml`, community plugins are referenced by their GitHub source: + +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/explorer + enabled: true + - source: github:quartz-community/syntax-highlighting + enabled: true + options: + theme: + light: github-light + dark: github-dark ``` -Internal plugins (like `Plugin.FrontMatter()`) are bundled with Quartz. Community plugins (like `ExternalPlugin.Explorer()`) are installed separately. +Internal plugins (like `FrontMatter`) are bundled with Quartz. Community plugins are installed separately and referenced by their `github:org/repo` source. ### Community Plugins -The `externalPlugins` array in your configuration declares which community plugin repositories to install. Each entry is a GitHub repository reference. +To install a community plugin, you can use the following command: -```ts title="quartz.config.ts" -externalPlugins: [ - "github:quartz-community/explorer", - "github:quartz-community/syntax-highlighting", - // ... other community plugins -], +```shell +npx quartz plugin add github:quartz-community/explorer ``` +This adds the plugin to `quartz.config.yaml` and installs it to `.quartz/plugins/`. + To install a community plugin, you can use the following command: ```shell @@ -114,41 +141,72 @@ This adds the plugin to `externalPlugins` and installs it to `.quartz/plugins/`. ### Usage -You can customize the behaviour of Quartz by adding, removing and reordering plugins in the `transformers`, `filters`, `emitters`, and `pageTypes` fields. You can mix internal and external plugins as needed. +You can customize the behaviour of Quartz by adding, removing and reordering plugins in `quartz.config.yaml`. Each plugin entry specifies its source, whether it's enabled, execution order, and any options: -```ts title="quartz.config.ts" -transformers: [ - Plugin.FrontMatter(), // internal - ExternalPlugin.CreatedModifiedDate({ - // community - priority: ["frontmatter", "git", "filesystem"], - }), - ExternalPlugin.Latex({ renderEngine: "katex" }), // community with options -] +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/note-properties + enabled: true + options: + includeAll: false + includedProperties: + - description + - tags + - aliases + order: 5 + - source: github:quartz-community/created-modified-date + enabled: true + options: + priority: + - frontmatter + - git + - filesystem + order: 10 + - source: github:quartz-community/latex + enabled: true + options: + renderEngine: katex + order: 80 ``` +> [!note] +> For advanced options that require JavaScript (e.g. callback functions), use the TS override in `quartz.ts`. See the plugin-specific documentation for details. + You can see a list of all plugins and their configuration options [[tags/plugin|here]]. If you'd like to make your own plugins, see the [[making plugins|making custom plugins]] guide. ## Fonts -Fonts can be specified as a `string` or a `FontSpecification`: +Fonts can be specified as a simple string or with advanced options in `quartz.config.yaml`: -```ts -// string -typography: { - header: "Schibsted Grotesk", - ... -} - -// FontSpecification -typography: { - header: { - name: "Schibsted Grotesk", - weights: [400, 700], - includeItalic: true, - }, - ... -} +```yaml title="quartz.config.yaml" +configuration: + theme: + typography: + header: Schibsted Grotesk + body: Source Sans Pro + code: IBM Plex Mono +``` + +For more control over font weights and italics, use the TS override in `quartz.ts`: + +```ts title="quartz.ts" +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" + +const config = await loadQuartzConfig({ + theme: { + typography: { + header: { + name: "Schibsted Grotesk", + weights: [400, 700], + includeItalic: true, + }, + body: "Source Sans Pro", + code: "IBM Plex Mono", + }, + }, +}) +export default config +export const layout = await loadQuartzLayout() ``` diff --git a/docs/features/OxHugo compatibility.md b/docs/features/OxHugo compatibility.md index e22051146..1872d8ca8 100644 --- a/docs/features/OxHugo compatibility.md +++ b/docs/features/OxHugo compatibility.md @@ -8,18 +8,42 @@ tags: Because the Markdown generated by ox-hugo is not pure Markdown but Hugo specific, we need to transform it to fit into Quartz. This is done by the [[OxHugoFlavoredMarkdown]] plugin. Even though this plugin was written with `ox-hugo` in mind, it should work for any Hugo specific Markdown. -```typescript title="quartz.config.ts" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/obsidian-flavored-markdown + enabled: true + order: 30 + - source: github:quartz-community/ox-hugo + enabled: true + order: 25 # must come before obsidian-flavored-markdown + - source: github:quartz-community/github-flavored-markdown + enabled: true + order: 40 + - source: github:quartz-community/note-properties + enabled: true + options: + delimiters: "+++" + language: toml # if using toml frontmatter + order: 5 +``` + +For the TS override approach: + +```ts title="quartz.ts (override)" plugins: { transformers: [ - Plugin.FrontMatter({ delims: "+++", language: "toml" }), // if toml frontmatter + Plugin.FrontMatter({ delims: "+++", language: "toml" }), // ... Plugin.OxHugoFlavouredMarkdown(), Plugin.GitHubFlavoredMarkdown(), // ... ], -}, +} ``` +> [!note] +> In YAML, plugin execution order is controlled by the `order` field. Lower numbers execute first. Ensure `ox-hugo` has a lower `order` than `obsidian-flavored-markdown`. + ## Usage Quartz by default doesn't understand `org-roam` files as they aren't Markdown. You're responsible for using an external tool like `ox-hugo` to export the `org-roam` files as Markdown content to Quartz and managing the static assets so that they're available in the final output. diff --git a/docs/features/Roam Research compatibility.md b/docs/features/Roam Research compatibility.md index 41ea88569..42736184c 100644 --- a/docs/features/Roam Research compatibility.md +++ b/docs/features/Roam Research compatibility.md @@ -9,7 +9,19 @@ tags: 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" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/roam + enabled: true + order: 25 # must come before obsidian-flavored-markdown + - source: github:quartz-community/obsidian-flavored-markdown + enabled: true + order: 30 +``` + +For the TS override approach: + +```ts title="quartz.ts (override)" plugins: { transformers: [ // ... @@ -17,11 +29,14 @@ plugins: { Plugin.ObsidianFlavoredMarkdown(), // ... ], -}, +} ``` > [!warning] -> As seen above placement of `Plugin.RoamFlavoredMarkdown()` within `quartz.config.ts` is very important. It must come before `Plugin.ObsidianFlavoredMarkdown()`. +> In YAML, plugin execution order is controlled by the `order` field. Ensure `roam` has a lower `order` value than `obsidian-flavored-markdown` so it runs first. + +> [!warning] +> In YAML, plugin execution order is controlled by the `order` field. Ensure the `roam` plugin has a lower `order` value than `obsidian-flavored-markdown` so it runs first. ## Customization diff --git a/docs/features/SPA Routing.md b/docs/features/SPA Routing.md index 3004af977..7e80b9bea 100644 --- a/docs/features/SPA Routing.md +++ b/docs/features/SPA Routing.md @@ -4,4 +4,4 @@ Under the hood, this is done by hijacking page navigations and instead fetching ## Configuration -- Disable SPA Routing: set the `enableSPA` field of the [[configuration]] in `quartz.config.ts` to be `false`. +- Disable SPA Routing: set the `enableSPA` field of the [[configuration]] in `quartz.config.yaml` to be `false`. diff --git a/docs/features/backlinks.md b/docs/features/backlinks.md index be9b2ab71..95df12c61 100644 --- a/docs/features/backlinks.md +++ b/docs/features/backlinks.md @@ -6,9 +6,12 @@ tags: A backlink for a note is a link from another note to that note. Links in the backlink pane also feature rich [[popover previews]] if you have that feature enabled. +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + ## Customization -- Removing backlinks: delete all usages of `Plugin.Backlinks()` from `quartz.layout.ts`. -- Hide when empty: hide `Backlinks` if given page doesn't contain any backlinks (default to `true`). To disable this, use `Plugin.Backlinks({ hideWhenEmpty: false })`. +- Removing backlinks: remove the `backlinks` entry from `quartz.config.yaml` or set `enabled: false`. +- Hide when empty: hide `Backlinks` if given page doesn't contain any backlinks (default to `true`). To disable this, set `hideWhenEmpty: false` in the plugin options in `quartz.config.yaml`. - Install: `npx quartz plugin add github:quartz-community/backlinks` - Source: [`quartz-community/backlinks`](https://github.com/quartz-community/backlinks) diff --git a/docs/features/breadcrumbs.md b/docs/features/breadcrumbs.md index d2c54dad2..3da2d4d13 100644 --- a/docs/features/breadcrumbs.md +++ b/docs/features/breadcrumbs.md @@ -8,27 +8,46 @@ Breadcrumbs provide a way to navigate a hierarchy of pages within your site usin By default, the element at the very top of your page is the breadcrumb navigation bar (can also be seen at the top on this page!). +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + ## Customization -Most configuration can be done by passing in options to `Plugin.Breadcrumbs()`. +Most configuration can be done via the `options` section of the breadcrumbs plugin entry in `quartz.config.yaml`. For example, here's what the default configuration looks like: -```typescript title="quartz.layout.ts" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/breadcrumbs + enabled: true + options: + spacerSymbol: "❯" + rootName: Home + resolveFrontmatterTitle: true + showCurrentPage: true + layout: + position: beforeBody + priority: 5 +``` + +For the TS override approach: + +```ts title="quartz.ts (override)" Plugin.Breadcrumbs({ - spacerSymbol: "❯", // symbol between crumbs - rootName: "Home", // name of first/root element - resolveFrontmatterTitle: true, // whether to resolve folder names through frontmatter titles - showCurrentPage: true, // whether to display the current page in the breadcrumbs + spacerSymbol: "❯", + rootName: "Home", + resolveFrontmatterTitle: true, + showCurrentPage: true, }) ``` When passing in your own options, you can omit any or all of these fields if you'd like to keep the default value for that field. -You can also adjust where the breadcrumbs will be displayed by adjusting the [[layout]] (moving `Plugin.Breadcrumbs()` up or down) +You can also adjust where the breadcrumbs will be displayed by changing the `layout.position` field in the plugin entry in `quartz.config.yaml` (see [[layout]]). Want to customize it even more? -- Removing breadcrumbs: delete all usages of `Plugin.Breadcrumbs()` from `quartz.layout.ts`. +- Removing breadcrumbs: remove the `breadcrumbs` entry from `quartz.config.yaml` or set `enabled: false`. - Install: `npx quartz plugin add github:quartz-community/breadcrumbs` - Source: [`quartz-community/breadcrumbs`](https://github.com/quartz-community/breadcrumbs) diff --git a/docs/features/comments.md b/docs/features/comments.md index b7f156922..7b27ecfe1 100644 --- a/docs/features/comments.md +++ b/docs/features/comments.md @@ -6,6 +6,9 @@ tags: Quartz also has the ability to hook into various providers to enable readers to leave comments on your site. +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + ![[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! @@ -30,26 +33,49 @@ After entering both your repository and selecting the discussion category, Giscu ![[giscus-results.png]] -Finally, in `quartz.layout.ts`, edit the `afterBody` field of the `defaults` layout to include the following options but with the values you got from above: +Finally, in `quartz.config.yaml`, edit the `afterBody` field of the `defaults` layout to include the following options but with the values you got from above: -```ts title="quartz.layout.ts" -afterBody: [ - Plugin.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', - // from data-lang - lang: 'en' - } - }), -], +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/comments + enabled: true + options: + provider: giscus + options: + repo: jackyzha0/quartz + repoId: MDEwOlJlcG9zaXRvcnkzODcyMTMyMDg + category: Announcements + categoryId: DIC_kwDOFxRnmM4B-Xg6 + lang: en + layout: + position: afterBody + priority: 10 +``` + +For the TS override approach: + +```ts title="quartz.ts (override)" +// If using quartz.ts overrides instead of YAML: +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" + +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout({ + defaults: { + afterBody: [ + Plugin.Comments({ + provider: "giscus", + options: { + repo: "jackyzha0/quartz", + repoId: "MDEwOlJlcG9zaXRvcnkzODcyMTMyMDg", + category: "Announcements", + categoryId: "DIC_kwDOFxRnmM4B-Xg6", + lang: "en", + }, + }), + ], + }, +}) ``` > [!note] @@ -109,19 +135,39 @@ Quartz supports custom theme for Giscus. To use a custom CSS theme, place the `. 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: [ - Plugin.Comments({ - provider: 'giscus', - options: { - // Other options +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/comments + enabled: true + options: + 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 in quartz/static/giscus/ +``` - 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/ - } - }), -], +```ts title="quartz.ts (override)" +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" + +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout({ + defaults: { + afterBody: [ + Plugin.Comments({ + provider: "giscus", + options: { + // Other options... + themeUrl: "https://example.com/static/giscus", + lightTheme: "light-theme", + darkTheme: "dark-theme", + }, + }), + ], + }, +}) ``` #### Conditionally display comments diff --git a/docs/features/darkmode.md b/docs/features/darkmode.md index e5c030c31..25e1d4d6a 100644 --- a/docs/features/darkmode.md +++ b/docs/features/darkmode.md @@ -6,9 +6,12 @@ tags: Quartz supports darkmode out of the box that respects the user's theme preference. Any future manual toggles of the darkmode switch will be saved in the browser's local storage so it can be persisted across future page loads. +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + ## Customization -- Removing darkmode: delete all usages of `Plugin.Darkmode()` from `quartz.layout.ts`. +- Removing darkmode: remove the `darkmode` entry from `quartz.config.yaml` or set `enabled: false`. - Install: `npx quartz plugin add github:quartz-community/darkmode` - Source: [`quartz-community/darkmode`](https://github.com/quartz-community/darkmode) diff --git a/docs/features/explorer.md b/docs/features/explorer.md index 8cf2c18af..f2a28be21 100644 --- a/docs/features/explorer.md +++ b/docs/features/explorer.md @@ -17,21 +17,15 @@ The Explorer is available as a community plugin from GitHub: npm install github:quartz-community/explorer --legacy-peer-deps ``` -Then import it in your `quartz.layout.ts`: +Then add it to your `quartz.config.yaml`: -```typescript title="quartz.layout.ts" -import { Explorer } from "@quartz-community/explorer" - -// Create once and reuse -const explorerComponent = Explorer() - -export const defaultContentPageLayout: PageLayout = { - // ... other layout config - left: [ - // ... other components - explorerComponent, - ], -} +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/explorer + enabled: true + layout: + position: left + priority: 50 ``` ## Features @@ -51,26 +45,50 @@ Most configuration can be done by passing in options to `Explorer()`. For example, here's what the default configuration looks like: -```typescript title="quartz.layout.ts" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/explorer + enabled: true + options: + title: Explorer + folderClickBehavior: collapse # "link" to navigate or "collapse" to toggle + folderDefaultState: collapsed # "collapsed" or "open" + useSavedState: true + layout: + position: left + priority: 50 +``` + +For advanced options like custom sort, filter, and map functions, use the TS override in `quartz.ts`: + +```ts title="quartz.ts" +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" +import { Explorer } from "@quartz-community/explorer" + +// Advanced: pass callback functions that can't be expressed in YAML Explorer({ - title: "Explorer", // title of the explorer component - folderClickBehavior: "collapse", // what happens when you click a folder ("link" to navigate to folder page on click or "collapse" to collapse folder on click) - folderDefaultState: "collapsed", // default state of folders ("collapsed" or "open") - useSavedState: true, // whether to use local storage to save "state" (which folders are opened) of explorer - // omitted but shown later - sortFn: ..., - filterFn: ..., - mapFn: ..., - // what order to apply functions in + sortFn: (a, b) => { + /* ... */ + }, + filterFn: (node) => { + /* ... */ + }, + mapFn: (node) => { + /* ... */ + }, order: ["filter", "map", "sort"], }) + +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout() ``` When passing in your own options, you can omit any or all of these fields if you'd like to keep the default value for that field. Want to customize it even more? -- Removing explorer: remove `explorerComponent` from `quartz.layout.ts` +- Removing explorer: remove the `explorer` entry from `quartz.config.yaml` or set `enabled: false` - (optional): After removing the explorer component, you can move the [[table of contents | Table of Contents]] component back to the `left` part of the layout - Changing `sort`, `filter` and `map` behavior: explained in [[#Advanced customization]] @@ -141,7 +159,19 @@ These examples show the basic usage of `sort`, `map` and `filter`. Using this example, the explorer will alphabetically sort everything. -```ts title="quartz.layout.ts" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/explorer + enabled: true + options: + # Simple options go in YAML + title: Explorer + folderDefaultState: collapsed +``` + +Custom sort functions require the TS override: + +```ts title="quartz.ts (override)" Explorer({ sortFn: (a, b) => { return a.displayName.localeCompare(b.displayName) @@ -153,7 +183,7 @@ Explorer({ Using this example, the display names of all `FileNodes` (folders + files) will be converted to full upper case. -```ts title="quartz.layout.ts" +```ts title="quartz.ts (override)" Explorer({ mapFn: (node) => { node.displayName = node.displayName.toUpperCase() @@ -162,12 +192,15 @@ Explorer({ }) ``` +> [!note] +> The `mapFn`, `filterFn`, and `sortFn` options require JavaScript callback functions and cannot be expressed in YAML. Use the TS override for these. + ### Remove list of elements (`filter`) Using this example, you can remove elements from your explorer by providing an array of folders/files to exclude. Note that this example filters on the title but you can also do it via slug or any other field available on `FileTrieNode`. -```ts title="quartz.layout.ts" +```ts title="quartz.ts (override)" Explorer({ filterFn: (node) => { // set containing names of everything you want to filter out @@ -185,7 +218,7 @@ Explorer({ You can access the tags of a file by `node.data.tags`. -```ts title="quartz.layout.ts" +```ts title="quartz.ts (override)" Explorer({ filterFn: (node) => { // exclude files with the tag "explorerexclude" @@ -199,7 +232,7 @@ Explorer({ By default, the explorer will filter out the `tags` folder. To override the default filter function, you can set the filter function to `undefined`. -```ts title="quartz.layout.ts" +```ts title="quartz.ts (override)" Explorer({ filterFn: undefined, // apply no filter function, every file and folder will visible }) @@ -208,11 +241,11 @@ Explorer({ ## Advanced examples > [!tip] -> When writing more complicated functions, the `layout` file can start to look very cramped. +> When writing more complicated functions, the `quartz.ts` file can start to look very cramped. > You can fix this by defining your sort functions outside of the component > and passing it in. > -> ```ts title="quartz.layout.ts" +> ```ts title="quartz.ts" > import { ExplorerOptions } from "@quartz-community/explorer/components" > > export const mapFn: ExplorerOptions["mapFn"] = (node) => { @@ -235,9 +268,9 @@ Explorer({ ### Add emoji prefix -To add emoji prefixes (📁 for folders, 📄 for files), you could use a map function like this: +To add emoji prefixes (📁 for folders, 📄 for files), you could use a map function in `quartz.ts`: -```ts title="quartz.layout.ts" +```ts title="quartz.ts (override)" Explorer({ mapFn: (node) => { if (node.isFolder) { diff --git a/docs/features/full-text search.md b/docs/features/full-text search.md index a8b2161d5..6ba4e7717 100644 --- a/docs/features/full-text search.md +++ b/docs/features/full-text search.md @@ -6,6 +6,9 @@ tags: Full-text search in Quartz is powered by [Flexsearch](https://github.com/nextapps-de/flexsearch). It's fast enough to return search results in under 10ms for Quartzs as large as half a million words. +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + It can be opened by either clicking on the search bar or pressing `⌘`/`ctrl` + `K`. The top 5 search results are shown on each query. Matching subterms are highlighted and the most relevant 30 words are excerpted. Clicking on a search result will navigate to that page. To search content by tags, you can either press `⌘`/`ctrl` + `shift` + `K` or start your query with `#` (e.g. `#components`). @@ -23,6 +26,6 @@ It properly tokenizes Chinese, Korean, and Japenese characters and constructs se ## Customization -- Removing search: delete all usages of `Plugin.Search()` from `quartz.layout.ts`. +- Removing search: remove the `search` entry from `quartz.config.yaml` or set `enabled: false`. - Install: `npx quartz plugin add github:quartz-community/search` - Source: [`quartz-community/search`](https://github.com/quartz-community/search) diff --git a/docs/features/graph view.md b/docs/features/graph view.md index 2cc7e7403..37540d473 100644 --- a/docs/features/graph view.md +++ b/docs/features/graph view.md @@ -20,21 +20,15 @@ The Graph View is available as a community plugin from GitHub: npm install github:quartz-community/graph --legacy-peer-deps ``` -Then import it in your `quartz.layout.ts`: +Then add it to your `quartz.config.yaml`: -```typescript title="quartz.layout.ts" -import { Graph } from "@quartz-community/graph" - -// Create once and reuse -const graphComponent = Graph() - -export const defaultContentPageLayout: PageLayout = { - // ... other layout config - right: [ - // ... other components - graphComponent, - ], -} +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/graph + enabled: true + layout: + position: right + priority: 10 ``` ## Features @@ -52,43 +46,46 @@ Most configuration can be done by passing in options to `Graph()`. For example, here's what the default configuration looks like: -```typescript title="quartz.layout.ts" -Graph({ - localGraph: { - drag: true, // whether to allow panning the view around - zoom: true, // whether to allow zooming in and out - depth: 1, // how many hops of notes to display - scale: 1.1, // default view scale - repelForce: 0.5, // how much nodes should repel each other - centerForce: 0.3, // how much force to use when trying to center the nodes - linkDistance: 30, // how long should the links be by default? - fontSize: 0.6, // what size should the node labels be? - opacityScale: 1, // how quickly do we fade out the labels when zooming out? - removeTags: [], // what tags to remove from the graph - showTags: true, // whether to show tags in the graph - enableRadial: false, // whether to constrain the graph, similar to Obsidian - }, - globalGraph: { - drag: true, - zoom: true, - depth: -1, - scale: 0.9, - repelForce: 0.5, - centerForce: 0.3, - linkDistance: 30, - fontSize: 0.6, - opacityScale: 1, - removeTags: [], // what tags to remove from the graph - showTags: true, // whether to show tags in the graph - focusOnHover: true, // dim non-connected nodes on hover - enableRadial: true, // whether to constrain the graph, similar to Obsidian - }, -}) +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/graph + enabled: true + options: + localGraph: + drag: true + zoom: true + depth: 1 + scale: 1.1 + repelForce: 0.5 + centerForce: 0.3 + linkDistance: 30 + fontSize: 0.6 + opacityScale: 1 + removeTags: [] + showTags: true + enableRadial: false + globalGraph: + drag: true + zoom: true + depth: -1 + scale: 0.9 + repelForce: 0.5 + centerForce: 0.3 + linkDistance: 30 + fontSize: 0.6 + opacityScale: 1 + removeTags: [] + showTags: true + focusOnHover: true + enableRadial: true + layout: + position: right + priority: 10 ``` When passing in your own options, you can omit any or all of these fields if you'd like to keep the default value for that field. Want to customize it even more? -- Removing graph view: remove `graphComponent` from `quartz.layout.ts` +- Removing graph view: remove the `graph` entry from `quartz.config.yaml` or set `enabled: false` - Component source: https://github.com/quartz-community/graph diff --git a/docs/features/i18n.md b/docs/features/i18n.md index 05f188be9..58f72da08 100644 --- a/docs/features/i18n.md +++ b/docs/features/i18n.md @@ -2,7 +2,7 @@ title: Internationalization --- -Internationalization allows users to translate text in the Quartz interface into various supported languages without needing to make extensive code changes. This can be changed via the `locale` [[configuration]] field in `quartz.config.ts`. +Internationalization allows users to translate text in the Quartz interface into various supported languages without needing to make extensive code changes. This can be changed via the `locale` [[configuration]] field in `quartz.config.yaml`. The locale field generally follows a certain format: `{language}-{REGION}` diff --git a/docs/features/popover previews.md b/docs/features/popover previews.md index 9b70149ef..bd128e221 100644 --- a/docs/features/popover previews.md +++ b/docs/features/popover previews.md @@ -12,6 +12,6 @@ Similar to Obsidian, [[quartz-layout-desktop.png|images referenced using wikilin ## Configuration -- Remove popovers: set the `enablePopovers` field in `quartz.config.ts` to be `false`. +- Remove popovers: set the `enablePopovers` field in `quartz.config.yaml` to be `false`. - Style: `quartz/components/styles/popover.scss` - Script: `quartz/components/scripts/popover.inline.ts` diff --git a/docs/features/private pages.md b/docs/features/private pages.md index eed6d3c1a..bb296af78 100644 --- a/docs/features/private pages.md +++ b/docs/features/private pages.md @@ -17,7 +17,7 @@ If you'd like to only publish a select number of notes, you can instead use [[Ex ## `ignorePatterns` -This is a field in `quartz.config.ts` under the main [[configuration]] which allows you to specify a list of patterns to effectively exclude from parsing all together. Any valid [fast-glob](https://github.com/mrmlnc/fast-glob#pattern-syntax) pattern works here. +This is a field in `quartz.config.yaml` under the main [[configuration]] which allows you to specify a list of patterns to effectively exclude from parsing all together. Any valid [fast-glob](https://github.com/mrmlnc/fast-glob#pattern-syntax) pattern works here. > [!note] > Bash's glob syntax is slightly different from fast-glob's and using bash's syntax may lead to unexpected results. diff --git a/docs/features/reader mode.md b/docs/features/reader mode.md index d08c37d04..a8f037766 100644 --- a/docs/features/reader mode.md +++ b/docs/features/reader mode.md @@ -6,13 +6,23 @@ tags: Reader Mode is a feature that allows users to focus on the content by hiding the sidebars and other UI elements. When enabled, it provides a clean, distraction-free reading experience. +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + ## Configuration -Reader Mode is enabled by default. To disable it, you can remove the component from your layout configuration in `quartz.layout.ts`: +Reader Mode is enabled by default. To disable it, set `enabled: false` in your `quartz.config.yaml`: -```ts -// Remove or comment out this line -Plugin.ReaderMode(), +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/reader-mode + enabled: false +``` + +Or remove the plugin entirely: + +```bash +npx quartz plugin remove github:quartz-community/reader-mode ``` - Install: `npx quartz plugin add github:quartz-community/reader-mode` diff --git a/docs/features/recent notes.md b/docs/features/recent notes.md index 89f711a4b..439ccd5d1 100644 --- a/docs/features/recent notes.md +++ b/docs/features/recent notes.md @@ -3,15 +3,20 @@ title: Recent Notes tags: component --- -Quartz can generate a list of recent notes based on some filtering and sorting criteria. Though this component isn't included in any [[layout]] by default, you can add it by using `Plugin.RecentNotes` in `quartz.layout.ts`. +Quartz can generate a list of recent notes based on some filtering and sorting criteria. Though this component isn't included in any [[layout]] by default, you can add it by installing the plugin and configuring it in `quartz.config.yaml`. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. ## Customization -- Changing the title from "Recent notes": pass in an additional parameter to `Plugin.RecentNotes({ title: "Recent writing" })` -- Changing the number of recent notes: pass in an additional parameter to `Plugin.RecentNotes({ limit: 5 })` -- Display the note's tags (defaults to true): `Plugin.RecentNotes({ showTags: false })` -- Show a 'see more' link: pass in an additional parameter to `Plugin.RecentNotes({ linkToMore: "tags/components" })`. This field should be a full slug to a page that exists. -- Customize filtering: pass in an additional parameter to `Plugin.RecentNotes({ filter: someFilterFunction })`. The filter function should be a function that has the signature `(f: QuartzPluginData) => boolean`. -- Customize sorting: pass in an additional parameter to `Plugin.RecentNotes({ sort: someSortFunction })`. By default, Quartz will sort by date and then tie break lexographically. The sort function should be a function that has the signature `(f1: QuartzPluginData, f2: QuartzPluginData) => number`. +Most options are configured in the `options` section of the plugin entry in `quartz.config.yaml`: + +- Changing the title from "Recent notes": set `title: "Recent writing"` in options +- Changing the number of recent notes: set `limit: 5` in options +- Display the note's tags (defaults to true): set `showTags: false` in options +- Show a 'see more' link: set `linkToMore: "tags/components"` in options. This field should be a full slug to a page that exists. +- Customize filtering: requires a TS override — pass `filter: someFilterFunction` to the plugin constructor in `quartz.ts`. The filter function should have the signature `(f: QuartzPluginData) => boolean`. +- Customize sorting: requires a TS override — pass `sort: someSortFunction` to the plugin constructor in `quartz.ts`. By default, Quartz will sort by date and then tie break lexographically. The sort function should have the signature `(f1: QuartzPluginData, f2: QuartzPluginData) => number`. - Install: `npx quartz plugin add github:quartz-community/recent-notes` - Source: [`quartz-community/recent-notes`](https://github.com/quartz-community/recent-notes) diff --git a/docs/layout-components.md b/docs/layout-components.md index 8d2260842..ca8c68f69 100644 --- a/docs/layout-components.md +++ b/docs/layout-components.md @@ -4,33 +4,70 @@ title: Higher-Order Layout Components Quartz provides several higher-order components that help with layout composition and responsive design. These components wrap other components to add additional functionality or modify their behavior. -> [!note] -> In the examples below, `Component.` refers to internal layout utilities (imported from `./quartz/components`), while `Plugin.` refers to community plugins (imported from `./.quartz/plugins`). See [[layout]] for more details. +Most common use cases can be configured directly in `quartz.config.yaml` using layout properties. For advanced scenarios requiring custom logic, you can use the TS override approach in `quartz.ts`. ## `Flex` Component The `Flex` component creates a [flexible box layout](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) that can arrange child components in various ways. It's particularly useful for creating responsive layouts and organizing components in rows or columns. -```typescript -type FlexConfig = { - components: { - Component: QuartzComponent - grow?: boolean // whether component should grow to fill space - shrink?: boolean // whether component should shrink if needed - basis?: string // initial main size of the component - order?: number // order in flex container - align?: "start" | "end" | "center" | "stretch" // cross-axis alignment - justify?: "start" | "end" | "center" | "between" | "around" // main-axis alignment - }[] - direction?: "row" | "row-reverse" | "column" | "column-reverse" - wrap?: "nowrap" | "wrap" | "wrap-reverse" - gap?: string -} +### YAML Configuration + +In YAML, flex layouts are created using **groups**. Define a group in the top-level `layout.groups` section, then assign plugins to that group via their `layout.group` property: + +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/search + enabled: true + layout: + position: left + priority: 20 + group: toolbar + groupOptions: + grow: true # Search will grow to fill available space + - source: github:quartz-community/darkmode + enabled: true + layout: + position: left + priority: 30 + group: toolbar # Darkmode keeps its natural size + - source: github:quartz-community/reader-mode + enabled: true + layout: + position: left + priority: 35 + group: toolbar + +layout: + groups: + toolbar: + direction: row + gap: 0.5rem ``` -### Example Usage +The `groupOptions` field on each plugin entry supports the following flex item properties: -```typescript +| Option | Type | Description | +| --------- | --------------------------------------------------------------- | --------------------------------------------------------- | +| `grow` | `boolean` | Whether the component should grow to fill available space | +| `shrink` | `boolean` | Whether the component should shrink if needed | +| `basis` | `string` | Initial main size of the component (e.g., `"200px"`) | +| `order` | `number` | Order in the flex container | +| `align` | `"start"` \| `"end"` \| `"center"` \| `"stretch"` | Cross-axis alignment | +| `justify` | `"start"` \| `"end"` \| `"center"` \| `"between"` \| `"around"` | Main-axis alignment | + +The top-level `layout.groups` section configures the flex container itself: + +| Option | Type | Description | +| ----------- | -------------------------------------------------------------- | ----------------------------------------- | +| `direction` | `"row"` \| `"row-reverse"` \| `"column"` \| `"column-reverse"` | Flex direction | +| `wrap` | `"nowrap"` \| `"wrap"` \| `"wrap-reverse"` | Flex wrap behavior | +| `gap` | `string` | Gap between flex items (e.g., `"0.5rem"`) | + +### TS Override + +For full programmatic control, use the `Component.Flex()` wrapper in `quartz.ts`: + +```ts title="quartz.ts (override)" Component.Flex({ components: [ { @@ -44,8 +81,25 @@ Component.Flex({ }) ``` +```typescript +type FlexConfig = { + components: { + Component: QuartzComponent + grow?: boolean + shrink?: boolean + basis?: string + order?: number + align?: "start" | "end" | "center" | "stretch" + justify?: "start" | "end" | "center" | "between" | "around" + }[] + direction?: "row" | "row-reverse" | "column" | "column-reverse" + wrap?: "nowrap" | "wrap" | "wrap-reverse" + gap?: string +} +``` + > [!note] Overriding behavior -> Components inside `Flex` get an additional CSS class `flex-component` that add the `display: flex` property. If you want to override this behavior, you can add a `display` property to the component's CSS class in your custom CSS file. +> Components inside `Flex` get an additional CSS class `flex-component` that adds the `display: flex` property. If you want to override this behavior, you can add a `display` property to the component's CSS class in your custom CSS file. > > ```scss > .flex-component { @@ -53,29 +107,81 @@ Component.Flex({ > } > ``` -## `MobileOnly` Component +## `MobileOnly` / `DesktopOnly` Components -The `MobileOnly` component is a wrapper that makes its child component only visible on mobile devices. This is useful for creating responsive layouts where certain components should only appear on smaller screens. +These components control whether a plugin is visible on mobile or desktop devices. This is useful for creating responsive layouts where certain components should only appear on specific screen sizes. -### Example Usage +### YAML Configuration -```typescript +In YAML, use the `display` property on a plugin's layout entry: + +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/table-of-contents + enabled: true + layout: + position: right + priority: 20 + display: desktop-only # Only visible on desktop +``` + +Available `display` values: + +| Value | Description | +| -------------- | ------------------------------------- | +| `all` | Visible on all screen sizes (default) | +| `mobile-only` | Only visible on mobile devices | +| `desktop-only` | Only visible on desktop devices | + +### TS Override + +For the TS override approach, use `Component.MobileOnly()` or `Component.DesktopOnly()` wrappers: + +```ts title="quartz.ts (override)" Component.MobileOnly(Component.Spacer()) ``` -## `DesktopOnly` Component - -The `DesktopOnly` component is the counterpart to `MobileOnly`. It makes its child component only visible on desktop devices. This helps create responsive layouts where certain components should only appear on larger screens. - -### Example Usage - -```typescript +```ts title="quartz.ts (override)" Component.DesktopOnly(Plugin.TableOfContents()) ``` ## `ConditionalRender` Component -The `ConditionalRender` component is a wrapper that conditionally renders its child component based on a provided condition function. This is useful for creating dynamic layouts where components should only appear under certain conditions. +The `ConditionalRender` component conditionally renders a plugin based on page properties. This is useful for creating dynamic layouts where components should only appear under certain conditions. + +### YAML Configuration + +In YAML, use the `condition` property on a plugin's layout entry. Quartz provides several built-in condition presets: + +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/breadcrumbs + enabled: true + layout: + position: beforeBody + priority: 5 + condition: not-index # Hide breadcrumbs on the root index page +``` + +Available built-in conditions: + +| Condition | Description | +| --------------- | ----------------------------------------------------- | +| `not-index` | Only render when the page is not the root `index.md` | +| `has-tags` | Only render when the page has tags in its frontmatter | +| `has-backlinks` | Only render when the page has backlinks | +| `has-toc` | Only render when the page has a table of contents | + +### TS Override + +For custom conditions that aren't covered by the built-in presets, use `Component.ConditionalRender()` in `quartz.ts`: + +```ts title="quartz.ts (override)" +Component.ConditionalRender({ + component: Plugin.Search(), + condition: (props) => props.displayClass !== "fullpage", +}) +``` ```typescript type ConditionalRenderConfig = { @@ -84,22 +190,5 @@ type ConditionalRenderConfig = { } ``` -### Example Usage - -```typescript -Component.ConditionalRender({ - component: Plugin.Search(), - condition: (props) => props.displayClass !== "fullpage", -}) -``` - -The example above would only render the Search component when the page is not in fullpage mode. - -```typescript -Component.ConditionalRender({ - component: Plugin.Breadcrumbs(), - condition: (page) => page.fileData.slug !== "index", -}) -``` - -The example above would hide breadcrumbs on the root `index.md` page. +> [!tip] +> You can also register custom conditions for use in YAML by calling `registerCondition()` in a plugin's initialization code. See [[making plugins]] for more details. diff --git a/docs/layout.md b/docs/layout.md index fc32bd4b8..293f3274a 100644 --- a/docs/layout.md +++ b/docs/layout.md @@ -4,7 +4,7 @@ title: Layout Certain emitters may also output [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) files. To enable easy customization, these emitters allow you to fully rearrange the layout of the page. -In v5, the layout is defined in `quartz.layout.ts` using a `defaults` + `byPageType` structure. +In v5, the layout is defined in `quartz.config.yaml` using a `defaults` + `byPageType` structure. - `defaults` contains layout components shared across ALL page types (head, header, afterBody, footer). - `byPageType` contains per-page-type overrides (content, folder, tag, 404) for beforeBody, left, and right sections. @@ -38,47 +38,101 @@ These correspond to following parts of the page: > 1. `head` is a single component that renders the `` [tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) in the HTML. This doesn't appear visually on the page and is only is responsible for metadata about the document like the tab title, scripts, and styles. > 2. `header` is a set of components that are laid out horizontally and appears _before_ the `beforeBody` section. This enables you to replicate the old Quartz 3 header bar where the title, search bar, and dark mode toggle. By default, Quartz doesn't place any components in the `header`. -### Configuration +Layout components are configured in the `layout` section of `quartz.config.yaml`. Plugins declare their position and priority, and the layout system arranges them automatically: -Layout components are imported from two main sources: +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/explorer + enabled: true + layout: + position: left + priority: 50 + - source: github:quartz-community/graph + enabled: true + layout: + position: right + priority: 10 + - source: github:quartz-community/search + enabled: true + layout: + position: left + priority: 20 + - source: github:quartz-community/backlinks + enabled: true + layout: + position: right + priority: 30 + - source: github:quartz-community/article-title + enabled: true + layout: + position: beforeBody + priority: 10 + - source: github:quartz-community/content-meta + enabled: true + layout: + position: beforeBody + priority: 20 + - source: github:quartz-community/tag-list + enabled: true + layout: + position: beforeBody + priority: 30 + - source: github:quartz-community/footer + enabled: true + options: + links: + GitHub: https://github.com/jackyzha0/quartz + Discord Community: https://discord.gg/cRFFHYye7t -```ts title="quartz.layout.ts" -import * as Component from "./quartz/components" // internal HOC components -import * as Plugin from "./.quartz/plugins" // community component plugins +layout: + groups: + toolbar: + direction: row + gap: 0.5rem + byPageType: + content: {} + folder: + exclude: + - reader-mode + positions: + right: [] + tag: + exclude: + - reader-mode + positions: + right: [] + "404": + positions: + beforeBody: [] + left: [] + right: [] ``` -Internal components (`Component.Head()`, `Component.Spacer()`, `Component.Flex()`, `Component.MobileOnly()`, `Component.DesktopOnly()`, `Component.ConditionalRender()`) are layout utilities. Community component plugins (`Plugin.Explorer()`, `Plugin.Search()`, `Plugin.Darkmode()`, etc.) provide the actual UI features. +For advanced layout overrides using TypeScript (e.g. custom component wrappers or conditional logic), you can use the TS override in `quartz.ts`: -Here is a simplified example of the layout structure: +```ts title="quartz.ts" +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" -```ts title="quartz.layout.ts" -export const layout = { +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout({ defaults: { - head: Component.Head(), - header: [], - afterBody: [], - footer: Plugin.Footer({ links: { ... } }), + // override default layout for all page types }, byPageType: { content: { - beforeBody: [Plugin.ArticleTitle(), Plugin.ContentMeta(), Plugin.TagList()], - left: [Plugin.PageTitle(), Plugin.Search(), Plugin.Explorer()], - right: [Plugin.Graph(), Plugin.TableOfContents(), Plugin.Backlinks()], + // override layout for content pages only }, folder: { - beforeBody: [Plugin.Breadcrumbs(), Plugin.ArticleTitle()], - left: [Plugin.PageTitle(), Plugin.Search(), Plugin.Explorer()], - right: [], + // override layout for folder pages only }, - tag: { ... }, - "404": { beforeBody: [], left: [], right: [] }, }, -} +}) ``` Fields defined in `defaults` can be overridden by specific entries in `byPageType`. -Community component plugins are installed via `npx quartz plugin add github:quartz-community/` and imported from `.quartz/plugins`. See [[layout-components]] for built-in layout utilities (Flex, MobileOnly, DesktopOnly, etc.). +Community component plugins are installed via `npx quartz plugin add github:quartz-community/`. See [[layout-components]] for built-in layout utilities (Flex, MobileOnly, DesktopOnly, etc.). You can also checkout the guide on [[creating components]] if you're interested in further customizing the behaviour of Quartz. diff --git a/docs/migrating from Quartz 4.md b/docs/migrating from Quartz 4.md index 18b2504bf..7c51af098 100644 --- a/docs/migrating from Quartz 4.md +++ b/docs/migrating from Quartz 4.md @@ -12,7 +12,7 @@ List the key architectural changes: - **Plugin system**: Plugins are now standalone Git repositories, installed via `npx quartz plugin add` - **Import pattern**: Community plugins use `ExternalPlugin.X()` (from `.quartz/plugins`) instead of `Plugin.X()` (from `./quartz/plugins`) -- **Layout structure**: `quartz.layout.ts` now uses `defaults` + `byPageType` instead of `sharedPageComponents` + per-layout objects +- **Layout structure**: `quartz.config.yaml` now uses `defaults` + `byPageType` instead of `sharedPageComponents` + per-layout objects - **Page Types**: A new plugin category for page rendering (content, folder, tag pages) - **Component references**: In layout files, community components use `Plugin.X()` (from `.quartz/plugins`) instead of `Component.X()` (from `./quartz/components`) @@ -67,7 +67,7 @@ npx quartz plugin add github:quartz-community/roam npx quartz plugin add github:quartz-community/explicit-publish ``` -### 2. Update quartz.config.ts +### 2. Update quartz.config.yaml Show before (v4) and after (v5) comparison: @@ -99,56 +99,60 @@ plugins: { **After (v5):** -```ts title="quartz.config.ts" -import * as Plugin from "./quartz/plugins" -import * as ExternalPlugin from "./.quartz/plugins" -import { layout } from "./quartz.layout" - -plugins: { - transformers: [ - Plugin.FrontMatter(), - ExternalPlugin.CreatedModifiedDate({ priority: ["frontmatter", "git", "filesystem"] }), - ExternalPlugin.Latex({ renderEngine: "katex" }), - ], - filters: [ExternalPlugin.RemoveDrafts()], - emitters: [ - ExternalPlugin.AliasRedirects(), - Plugin.ComponentResources(), - ExternalPlugin.ContentIndex({ enableSiteMap: true, enableRSS: true }), - Plugin.Assets(), - Plugin.Static(), - ExternalPlugin.Favicon(), - Plugin.PageTypes.PageTypeDispatcher({ - defaults: layout.defaults, - byPageType: layout.byPageType, - }), - ExternalPlugin.CustomOgImages(), - ExternalPlugin.CNAME(), - ], - pageTypes: [ - ExternalPlugin.ContentPage(), - ExternalPlugin.FolderPage(), - ExternalPlugin.TagPage(), - Plugin.PageTypes.NotFoundPageType(), - ], -}, -externalPlugins: [ - "github:quartz-community/explorer", - "github:quartz-community/graph", - // ... all your community plugins -], +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/note-properties + enabled: true + options: + delimiters: "---" + language: yaml + order: 5 + - source: github:quartz-community/created-modified-date + enabled: true + options: + priority: + - frontmatter + - git + - filesystem + order: 10 + - source: github:quartz-community/latex + enabled: true + options: + renderEngine: katex + order: 80 + - source: github:quartz-community/remove-draft + enabled: true + - source: github:quartz-community/alias-redirects + enabled: true + - source: github:quartz-community/content-index + enabled: true + options: + enableSiteMap: true + enableRSS: true + - source: github:quartz-community/favicon + enabled: true + - source: github:quartz-community/og-image + enabled: true + - source: github:quartz-community/cname + enabled: true + - source: github:quartz-community/content-page + enabled: true + - source: github:quartz-community/folder-page + enabled: true + - source: github:quartz-community/tag-page + enabled: true + # ... more plugins ``` Key changes: -- `Plugin.X()` becomes `ExternalPlugin.X()` for community plugins -- `Plugin.FrontMatter()` stays as `Plugin.X()` (it's internal) -- `Plugin.ComponentResources()`, `Plugin.Assets()`, `Plugin.Static()` stay internal -- Page-rendering emitters (`ContentPage`, `FolderPage`, `TagPage`, `NotFoundPage`) move to new `pageTypes` array -- `Plugin.PageTypes.PageTypeDispatcher()` replaces individual page emitters in the `emitters` array -- New `externalPlugins` array lists all community plugin repos +- Plugins are now referenced by their GitHub source (`github:org/repo`) +- Plugin type (transformer, filter, emitter, pageType) is determined by the plugin's manifest, not by which array you place it in +- Execution order is controlled by the `order` field (lower numbers run first) +- Each plugin entry has `enabled`, `options`, `order`, and optionally `layout` fields +- Install community plugins with `npx quartz plugin add github:quartz-community/` -### 3. Update quartz.layout.ts +### 3. Update layout configuration Show before (v4) and after (v5): @@ -173,37 +177,92 @@ export const defaultContentPageLayout: PageLayout = { **After (v5):** -```ts title="quartz.layout.ts" -import * as Component from "./quartz/components" -import * as Plugin from "./.quartz/plugins" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/breadcrumbs + enabled: true + layout: + position: beforeBody + priority: 5 + - source: github:quartz-community/article-title + enabled: true + layout: + position: beforeBody + priority: 10 + - source: github:quartz-community/content-meta + enabled: true + layout: + position: beforeBody + priority: 20 + - source: github:quartz-community/tag-list + enabled: true + layout: + position: beforeBody + priority: 30 + - source: github:quartz-community/page-title + enabled: true + layout: + position: left + priority: 10 + - source: github:quartz-community/search + enabled: true + layout: + position: left + priority: 20 + - source: github:quartz-community/darkmode + enabled: true + layout: + position: left + priority: 30 + - source: github:quartz-community/explorer + enabled: true + layout: + position: left + priority: 50 + - source: github:quartz-community/graph + enabled: true + layout: + position: right + priority: 10 + - source: github:quartz-community/backlinks + enabled: true + layout: + position: right + priority: 30 + - source: github:quartz-community/footer + enabled: true + options: + links: + GitHub: https://github.com/jackyzha0/quartz + Discord Community: https://discord.gg/cRFFHYye7t -export const layout = { - defaults: { - head: Component.Head(), - header: [], - afterBody: [], - footer: Plugin.Footer({ links: { ... } }), - }, - byPageType: { - content: { - beforeBody: [Plugin.Breadcrumbs(), Plugin.ArticleTitle(), Plugin.ContentMeta(), Plugin.TagList()], - left: [Plugin.PageTitle(), Plugin.Search(), Plugin.Darkmode(), Plugin.Explorer()], - right: [Plugin.Graph(), Plugin.TableOfContents(), Plugin.Backlinks()], - }, - folder: { ... }, - tag: { ... }, - "404": { beforeBody: [], left: [], right: [] }, - }, -} +layout: + byPageType: + content: {} + folder: + exclude: + - reader-mode + positions: + right: [] + tag: + exclude: + - reader-mode + positions: + right: [] + "404": + positions: + beforeBody: [] + left: [] + right: [] ``` Key changes: -- `Component.X()` for community components becomes `Plugin.X()` (imported from `.quartz/plugins`) -- `Component.Head()` and other layout utilities stay as `Component.X()` (from `./quartz/components`) -- `sharedPageComponents` becomes `defaults` -- Per-layout objects become entries in `byPageType` -- Each page type (content, folder, tag, 404) gets its own layout override +- Layout position is now a property on each plugin entry (`layout.position`, `layout.priority`) +- `sharedPageComponents` is gone — all layout is plugin-driven +- Per-page-type overrides live in the `layout.byPageType` section +- Empty arrays (`[]`) clear a position for that page type +- The `exclude` field removes specific plugins from a page type ### 4. Update CI/CD @@ -212,7 +271,7 @@ Add `npx quartz plugin restore` to your build pipeline, before `npx quartz build ### 5. Commit and Deploy ```shell -git add quartz.config.ts quartz.layout.ts quartz.lock.json +git add quartz.ts quartz.lock.json git commit -m "chore: migrate to Quartz 5 plugin system" ``` diff --git a/docs/philosophy.md b/docs/philosophy.md index af5510aac..5eea40078 100644 --- a/docs/philosophy.md +++ b/docs/philosophy.md @@ -36,7 +36,7 @@ Quartz is designed first and foremost as a tool for publishing [digital gardens] At its core, Quartz is designed to be easy to use enough for non-technical people to get going but also powerful enough that senior developers can tweak it to work how they'd like it to work. 1. If you like the default configuration of Quartz and just want to change the content, the only thing that you need to change is the contents of the `content` folder. -2. If you'd like to make basic configuration tweaks but don't want to edit source code, one can tweak the plugins and components in `quartz.config.ts` and `quartz.layout.ts` in a guided manner to their liking. +2. If you'd like to make basic configuration tweaks but don't want to edit source code, one can tweak the plugins and components in `quartz.config.yaml` in a guided manner to their liking. 3. If you'd like to tweak the actual source code of the underlying plugins, components, or even build process, Quartz purposefully ships its full source code to the end user to allow customization at this level too. Most software either confines you to either diff --git a/docs/plugins/ArticleTitle.md b/docs/plugins/ArticleTitle.md new file mode 100644 index 000000000..742377520 --- /dev/null +++ b/docs/plugins/ArticleTitle.md @@ -0,0 +1,19 @@ +--- +title: ArticleTitle +tags: + - plugin/component +--- + +This plugin renders the article title from the page's frontmatter as an `

` heading at the top of the page content. It reads the `title` field from frontmatter (falling back to the filename if no title is set). + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +This plugin has no configuration options. + +## API + +- Category: Component +- Function name: `ExternalPlugin.ArticleTitle()`. +- Source: [`quartz-community/article-title`](https://github.com/quartz-community/article-title) +- Install: `npx quartz plugin add github:quartz-community/article-title` diff --git a/docs/plugins/BasesPage.md b/docs/plugins/BasesPage.md new file mode 100644 index 000000000..dc8493b7f --- /dev/null +++ b/docs/plugins/BasesPage.md @@ -0,0 +1,60 @@ +--- +title: BasesPage +tags: + - plugin/pageType + - plugin/component +--- + +This plugin provides support for [Obsidian Bases](https://obsidian.md/changelog/2025-04-15-desktop-v1.8.0/) (`.base` files) in Quartz. It reads `.base` files from your vault, resolves matching notes based on the query definition, and renders them as interactive database-like views with support for tables, lists, cards, and maps. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +## Features + +- **Table view**: Sortable columns with automatic type rendering (strings, numbers, booleans, arrays, links). +- **List view**: Compact list with metadata chips for each entry. +- **Cards view**: Card layout with optional image property support. +- **Map view**: Placeholder for future map-based visualization. +- **Multiple views**: A single `.base` file can define multiple views, displayed as switchable tabs. +- **Filters**: Recursive filter trees with `and`/`or`/`not` operators. +- **Formulas**: Computed properties via formula expressions. +- **Summaries**: Column-level aggregations (Sum, Average, Min, Max, Median, etc.). +- **Property configuration**: Custom display names for properties. +- **Link rendering**: Wikilinks and Markdown links within cell values are rendered as clickable links. + +## Configuration + +This plugin accepts the following configuration options: + +- `defaultViewType`: The default view type when none is specified in the `.base` file. Defaults to `"table"`. +- `customViews`: A map of custom view renderers. Keys are view type names. These override built-in renderers for the same type, or add new view types. Requires a TS override. + +### Default options + +```yaml title="quartz.config.yaml" +- source: github:quartz-community/bases-page + enabled: true +``` + +For custom view renderers, use a TS override in `quartz.ts`: + +```ts title="quartz.ts (override)" +import * as ExternalPlugin from "./.quartz/plugins" + +ExternalPlugin.BasesPage({ + defaultViewType: "table", + customViews: { + myView: ({ entries, view, basesData, total, locale }) => { + // return JSX + }, + }, +}) +``` + +## API + +- Category: Page Type, Component +- Function name: `ExternalPlugin.BasesPage()`. +- Source: [`quartz-community/bases-page`](https://github.com/quartz-community/bases-page) +- Install: `npx quartz plugin add github:quartz-community/bases-page` diff --git a/docs/plugins/CanvasPage.canvas b/docs/plugins/CanvasPage.canvas index 7ba55f150..317702ce6 100644 --- a/docs/plugins/CanvasPage.canvas +++ b/docs/plugins/CanvasPage.canvas @@ -173,7 +173,7 @@ { "id": "config-options", "type": "text", - "text": "## Configuration Options\n\n- `enableInteraction` — Enable pan and zoom. Default: `true`\n- `initialZoom` — Initial zoom level. Default: `1`\n- `minZoom` — Minimum zoom level. Default: `0.1`\n- `maxZoom` — Maximum zoom level. Default: `5`\n- `defaultFullscreen` — Start in fullscreen mode. Default: `false`\n\nConfigure in `quartz.config.ts`:\n\n```\nCanvasPage({ defaultFullscreen: false, initialZoom: 1 })\n```", + "text": "## Configuration Options\n\n- `enableInteraction` — Enable pan and zoom. Default: `true`\n- `initialZoom` — Initial zoom level. Default: `1`\n- `minZoom` — Minimum zoom level. Default: `0.1`\n- `maxZoom` — Maximum zoom level. Default: `5`\n- `defaultFullscreen` — Start in fullscreen mode. Default: `false`\n\nConfigure in `quartz.config.yaml`:\n\n```\nCanvasPage({ defaultFullscreen: false, initialZoom: 1 })\n```", "x": 0, "y": 1220, "width": 560, diff --git a/docs/plugins/ContentMeta.md b/docs/plugins/ContentMeta.md new file mode 100644 index 000000000..0a5ff943a --- /dev/null +++ b/docs/plugins/ContentMeta.md @@ -0,0 +1,34 @@ +--- +title: ContentMeta +tags: + - plugin/component +--- + +This plugin displays content metadata below the article title, such as the creation date and estimated reading time. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +## Configuration + +This plugin accepts the following configuration options: + +- `showReadingTime`: Whether to display the estimated reading time. Defaults to `true`. +- `showComma`: Whether to display a comma between metadata items. Defaults to `true`. + +### Default options + +```yaml title="quartz.config.yaml" +- source: github:quartz-community/content-meta + enabled: true + options: + showReadingTime: true + showComma: true +``` + +## API + +- Category: Component +- Function name: `ExternalPlugin.ContentMeta()`. +- Source: [`quartz-community/content-meta`](https://github.com/quartz-community/content-meta) +- Install: `npx quartz plugin add github:quartz-community/content-meta` diff --git a/docs/plugins/ContentPage.md b/docs/plugins/ContentPage.md index 588a1c422..0ff2d7d01 100644 --- a/docs/plugins/ContentPage.md +++ b/docs/plugins/ContentPage.md @@ -4,7 +4,7 @@ tags: - plugin/pageType --- -This plugin is a page type plugin for the Quartz framework. It generates the HTML pages for each piece of Markdown content. It emits the full-page [[layout]], including headers, footers, and body content, among others. It is now configured in the `pageTypes` section of `quartz.config.ts`. +This plugin is a page type plugin for the Quartz framework. It generates the HTML pages for each piece of Markdown content. It emits the full-page [[layout]], including headers, footers, and body content, among others. It is now configured in the `pageTypes` section of `quartz.config.yaml`. > [!note] > For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. diff --git a/docs/plugins/CreatedModifiedDate.md b/docs/plugins/CreatedModifiedDate.md index 88123ea06..ba04e9531 100644 --- a/docs/plugins/CreatedModifiedDate.md +++ b/docs/plugins/CreatedModifiedDate.md @@ -16,7 +16,7 @@ This plugin accepts the following configuration options: When loading the frontmatter, the value of [[Frontmatter#List]] is used. > [!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.yaml`. > > Depending on how you [[hosting|host]] your Quartz, the `filesystem` dates of your local files may not match the final dates. In these cases, it may be better to use `git` or `frontmatter` to guarantee correct dates. diff --git a/docs/plugins/CustomOgImages.md b/docs/plugins/CustomOgImages.md index 7f79b4da7..570e16db3 100644 --- a/docs/plugins/CustomOgImages.md +++ b/docs/plugins/CustomOgImages.md @@ -25,22 +25,29 @@ The Custom OG Images emitter plugin generates social media preview images for yo This plugin accepts the following configuration options: -```typescript title="quartz.config.ts" -import { CustomOgImages } from "./quartz/plugins/emitters/ogImage" +```yaml title="quartz.config.yaml" +plugins: + - source: github:quartz-community/og-image + enabled: true + options: + colorScheme: lightMode # "lightMode" or "darkMode" + width: 1200 + height: 630 + excludeRoot: false +``` -const config: QuartzConfig = { - plugins: { - emitters: [ - CustomOgImages({ - colorScheme: "lightMode", // what colors to use for generating image, same as theme colors from config, valid values are "darkMode" and "lightMode" - width: 1200, // width to generate with (in pixels) - height: 630, // height to generate with (in pixels) - excludeRoot: false, // wether to exclude "/" index path to be excluded from auto generated images (false = use auto, true = use default og image) - imageStructure: defaultImage, // custom image component to use - }), - ], - }, -} +For the TS override approach (needed for custom `imageStructure`): + +```ts title="quartz.ts (override)" +import { defaultImage } from "./quartz/plugins/emitters/ogImage" + +CustomOgImages({ + colorScheme: "lightMode", + width: 1200, + height: 630, + excludeRoot: false, + imageStructure: defaultImage, // custom JSX component — requires TS +}) ``` ### Configuration Options @@ -76,7 +83,7 @@ You can fully customize how the images being generated look by passing your own ### Fonts -You will also be passed an array containing a header and a body font (where the first entry is header and the second is body). The fonts matches the ones selected in `theme.typography.header` and `theme.typography.body` from `quartz.config.ts` and will be passed in the format required by [`satori`](https://github.com/vercel/satori). To use them in CSS, use the `.name` property (e.g. `fontFamily: fonts[1].name` to use the "body" font family). +You will also be passed an array containing a header and a body font (where the first entry is header and the second is body). The fonts matches the ones selected in `theme.typography.header` and `theme.typography.body` from `quartz.config.yaml` and will be passed in the format required by [`satori`](https://github.com/vercel/satori). To use them in CSS, use the `.name` property (e.g. `fontFamily: fonts[1].name` to use the "body" font family). An example of a component using the header font could look like this: diff --git a/docs/plugins/Footer.md b/docs/plugins/Footer.md new file mode 100644 index 000000000..b57027447 --- /dev/null +++ b/docs/plugins/Footer.md @@ -0,0 +1,34 @@ +--- +title: Footer +tags: + - plugin/component +--- + +This plugin renders a footer at the bottom of the page with a "Created with Quartz" message and a set of configurable links. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +## Configuration + +This plugin accepts the following configuration options: + +- `links`: A map of link labels to their URLs to display in the footer. Defaults to `{}`. + +### Default options + +```yaml title="quartz.config.yaml" +- source: github:quartz-community/footer + enabled: true + options: + links: + GitHub: https://github.com/jackyzha0/quartz + Discord Community: https://discord.gg/cRFFHYye7t +``` + +## API + +- Category: Component +- Function name: `ExternalPlugin.Footer()`. +- Source: [`quartz-community/footer`](https://github.com/quartz-community/footer) +- Install: `npx quartz plugin add github:quartz-community/footer` diff --git a/docs/plugins/PageTitle.md b/docs/plugins/PageTitle.md new file mode 100644 index 000000000..b9c228fd5 --- /dev/null +++ b/docs/plugins/PageTitle.md @@ -0,0 +1,19 @@ +--- +title: PageTitle +tags: + - plugin/component +--- + +This plugin renders the site-wide page title (configured via the `pageTitle` field in [[configuration]]) as a clickable link to the home page. It typically appears in the left sidebar. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +This plugin has no configuration options. The displayed title is controlled by the `pageTitle` field in `quartz.config.yaml`. + +## API + +- Category: Component +- Function name: `ExternalPlugin.PageTitle()`. +- Source: [`quartz-community/page-title`](https://github.com/quartz-community/page-title) +- Install: `npx quartz plugin add github:quartz-community/page-title` diff --git a/docs/plugins/Spacer.md b/docs/plugins/Spacer.md new file mode 100644 index 000000000..d03932f3c --- /dev/null +++ b/docs/plugins/Spacer.md @@ -0,0 +1,19 @@ +--- +title: Spacer +tags: + - plugin/component +--- + +This plugin renders a flexible spacer element that pushes adjacent components apart within a layout group. It uses CSS `flex: 2 1 auto` to fill available space, making it useful for spacing out items in toolbars or sidebars (for example, separating the search bar from the darkmode toggle in the left sidebar toolbar). + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +This plugin has no configuration options. + +## API + +- Category: Component +- Function name: `ExternalPlugin.Spacer()`. +- Source: [`quartz-community/spacer`](https://github.com/quartz-community/spacer) +- Install: `npx quartz plugin add github:quartz-community/spacer` diff --git a/docs/plugins/TableOfContents.md b/docs/plugins/TableOfContents.md index 30a132bad..2a849cba7 100644 --- a/docs/plugins/TableOfContents.md +++ b/docs/plugins/TableOfContents.md @@ -17,7 +17,7 @@ This plugin accepts the following configuration options: - `collapseByDefault`: If `true`, the TOC will start in a collapsed state. Default is `false`. > [!warning] -> This plugin needs the `Plugin.TableOfContents` component in `quartz.layout.ts` to determine where to display the TOC. Without it, nothing will be displayed. They should always be added or removed together. +> This plugin needs the `Plugin.TableOfContents` component in `quartz.config.yaml` to determine where to display the TOC. Without it, nothing will be displayed. They should always be added or removed together. ## API diff --git a/docs/plugins/TagList.md b/docs/plugins/TagList.md new file mode 100644 index 000000000..93c888a66 --- /dev/null +++ b/docs/plugins/TagList.md @@ -0,0 +1,19 @@ +--- +title: TagList +tags: + - plugin/component +--- + +This plugin renders the page's tags as a list of clickable links. Each tag links to its corresponding [[TagPage|tag page]], making it easy for readers to browse related content by topic. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +This plugin has no configuration options. + +## API + +- Category: Component +- Function name: `ExternalPlugin.TagList()`. +- Source: [`quartz-community/tag-list`](https://github.com/quartz-community/tag-list) +- Install: `npx quartz plugin add github:quartz-community/tag-list` diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 298ff164c..faa39f431 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -1,3 +1,81 @@ --- title: Plugins --- + +Quartz's functionality is provided by a collection of first-party community plugins. Each plugin can be enabled, disabled, and configured via `quartz.config.yaml`. See [[configuration#Plugins|Configuration]] for details on how to manage plugins. + +## Plugin types + +Quartz plugins fall into several categories: + +- **Transformers** process content during the build, e.g. parsing frontmatter, highlighting syntax, or resolving links. +- **Filters** decide which content files to include or exclude from the output. +- **Page Types** generate HTML pages — one per content file, folder, tag, canvas, or bases view. +- **Components** render UI elements in the page layout (sidebars, headers, footers, etc.). + +## First-party plugins + +### Transformers + +| Plugin | Repository | Enabled | Required | Description | +| -------------------------------- | --------------------------------------------------------------------------------------------------------------- | :-----: | :------: | ---------------------------------------------------------- | +| [[Frontmatter\|Note Properties]] | [`quartz-community/note-properties`](https://github.com/quartz-community/note-properties) | ✅ | ✅ | Parses frontmatter and displays note properties. | +| [[CreatedModifiedDate]] | [`quartz-community/created-modified-date`](https://github.com/quartz-community/created-modified-date) | ✅ | ❌ | Determines creation and modification dates. | +| [[SyntaxHighlighting]] | [`quartz-community/syntax-highlighting`](https://github.com/quartz-community/syntax-highlighting) | ✅ | ❌ | Syntax highlighting for code blocks. | +| [[ObsidianFlavoredMarkdown]] | [`quartz-community/obsidian-flavored-markdown`](https://github.com/quartz-community/obsidian-flavored-markdown) | ✅ | ❌ | Obsidian-specific Markdown extensions. | +| [[GitHubFlavoredMarkdown]] | [`quartz-community/github-flavored-markdown`](https://github.com/quartz-community/github-flavored-markdown) | ✅ | ❌ | GitHub Flavored Markdown support. | +| [[TableOfContents]] | [`quartz-community/table-of-contents`](https://github.com/quartz-community/table-of-contents) | ✅ | ❌ | Generates table of contents data from headings. | +| [[CrawlLinks]] | [`quartz-community/crawl-links`](https://github.com/quartz-community/crawl-links) | ✅ | ⚠️ | Parses and resolves links. Removing it is not recommended. | +| [[Description]] | [`quartz-community/description`](https://github.com/quartz-community/description) | ✅ | ❌ | Generates page descriptions for metadata. | +| [[Latex]] | [`quartz-community/latex`](https://github.com/quartz-community/latex) | ✅ | ❌ | Renders LaTeX math expressions. | +| [[Citations]] | [`quartz-community/citations`](https://github.com/quartz-community/citations) | ❌ | ❌ | Academic citation support via BibTeX. | +| [[HardLineBreaks]] | [`quartz-community/hard-line-breaks`](https://github.com/quartz-community/hard-line-breaks) | ❌ | ❌ | Treats single newlines as hard line breaks. | +| [[OxHugoFlavoredMarkdown]] | [`quartz-community/ox-hugo`](https://github.com/quartz-community/ox-hugo) | ❌ | ❌ | ox-hugo Markdown compatibility. | +| [[RoamFlavoredMarkdown]] | [`quartz-community/roam`](https://github.com/quartz-community/roam) | ❌ | ❌ | Roam Research Markdown compatibility. | + +### Filters + +| Plugin | Repository | Enabled | Required | Description | +| ------------------- | ------------------------------------------------------------------------------------------- | :-----: | :------: | --------------------------------------- | +| [[RemoveDrafts]] | [`quartz-community/remove-draft`](https://github.com/quartz-community/remove-draft) | ✅ | ❌ | Filters out pages marked as drafts. | +| [[ExplicitPublish]] | [`quartz-community/explicit-publish`](https://github.com/quartz-community/explicit-publish) | ❌ | ❌ | Only publishes pages explicitly marked. | + +### Page Types + +| Plugin | Repository | Enabled | Required | Description | +| --------------- | ----------------------------------------------------------------------------------- | :-----: | :------: | ----------------------------------------------- | +| [[ContentPage]] | [`quartz-community/content-page`](https://github.com/quartz-community/content-page) | ✅ | ❌ | Generates HTML pages for Markdown content. | +| [[FolderPage]] | [`quartz-community/folder-page`](https://github.com/quartz-community/folder-page) | ✅ | ❌ | Generates folder listing pages. | +| [[TagPage]] | [`quartz-community/tag-page`](https://github.com/quartz-community/tag-page) | ✅ | ❌ | Generates tag listing pages. | +| [[CanvasPage]] | [`quartz-community/canvas-page`](https://github.com/quartz-community/canvas-page) | ✅ | ❌ | Renders JSON Canvas files as interactive pages. | +| [[BasesPage]] | [`quartz-community/bases-page`](https://github.com/quartz-community/bases-page) | ✅ | ❌ | Renders Obsidian Bases files as database views. | + +### Emitters + +| Plugin | Repository | Enabled | Required | Description | +| ---------------------------- | ----------------------------------------------------------------------------------------- | :-----: | :------: | ----------------------------------------------- | +| [[AliasRedirects]] | [`quartz-community/alias-redirects`](https://github.com/quartz-community/alias-redirects) | ✅ | ❌ | Generates redirect pages for aliases. | +| [[ContentIndex]] | [`quartz-community/content-index`](https://github.com/quartz-community/content-index) | ✅ | ❌ | Generates sitemap, RSS feed, and content index. | +| [[Favicon]] | [`quartz-community/favicon`](https://github.com/quartz-community/favicon) | ✅ | ❌ | Emits the site favicon. | +| [[CustomOgImages\|OG Image]] | [`quartz-community/og-image`](https://github.com/quartz-community/og-image) | ✅ | ❌ | Generates Open Graph social preview images. | +| [[CNAME]] | [`quartz-community/cname`](https://github.com/quartz-community/cname) | ✅ | ❌ | Emits a CNAME file for custom domains. | + +### Components + +| Plugin | Repository | Enabled | Required | Description | +| ------------------------------ | ------------------------------------------------------------------------------------- | :-----: | :------: | ------------------------------------------- | +| [[ArticleTitle]] | [`quartz-community/article-title`](https://github.com/quartz-community/article-title) | ✅ | ❌ | Renders the article title as an h1 heading. | +| [[ContentMeta]] | [`quartz-community/content-meta`](https://github.com/quartz-community/content-meta) | ✅ | ❌ | Displays creation date and reading time. | +| [[TagList]] | [`quartz-community/tag-list`](https://github.com/quartz-community/tag-list) | ✅ | ❌ | Renders tags as clickable links. | +| [[PageTitle]] | [`quartz-community/page-title`](https://github.com/quartz-community/page-title) | ✅ | ❌ | Renders the site title as a home link. | +| [[darkmode\|Darkmode]] | [`quartz-community/darkmode`](https://github.com/quartz-community/darkmode) | ✅ | ❌ | Toggle between light and dark themes. | +| [[reader mode\|Reader Mode]] | [`quartz-community/reader-mode`](https://github.com/quartz-community/reader-mode) | ✅ | ❌ | Distraction-free reading mode toggle. | +| [[explorer\|Explorer]] | [`quartz-community/explorer`](https://github.com/quartz-community/explorer) | ✅ | ❌ | File tree explorer sidebar. | +| [[graph view\|Graph View]] | [`quartz-community/graph`](https://github.com/quartz-community/graph) | ✅ | ❌ | Interactive link graph visualization. | +| [[full-text search\|Search]] | [`quartz-community/search`](https://github.com/quartz-community/search) | ✅ | ❌ | Full-text search functionality. | +| [[backlinks\|Backlinks]] | [`quartz-community/backlinks`](https://github.com/quartz-community/backlinks) | ✅ | ❌ | Shows pages that link to the current page. | +| [[breadcrumbs\|Breadcrumbs]] | [`quartz-community/breadcrumbs`](https://github.com/quartz-community/breadcrumbs) | ✅ | ❌ | Breadcrumb navigation trail. | +| [[comments\|Comments]] | [`quartz-community/comments`](https://github.com/quartz-community/comments) | ❌ | ❌ | Comment system integration (Giscus, etc.). | +| [[Footer]] | [`quartz-community/footer`](https://github.com/quartz-community/footer) | ✅ | ❌ | Page footer with configurable links. | +| [[recent notes\|Recent Notes]] | [`quartz-community/recent-notes`](https://github.com/quartz-community/recent-notes) | ❌ | ❌ | Displays a list of recently modified notes. | +| [[Spacer]] | [`quartz-community/spacer`](https://github.com/quartz-community/spacer) | ✅ | ❌ | Flexible spacer for layout groups. | diff --git a/quartz.config.ts b/quartz.config.ts deleted file mode 100644 index ccc56bb2d..000000000 --- a/quartz.config.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { loadQuartzConfig } from "./quartz/plugins/loader/config-loader" -export default await loadQuartzConfig() diff --git a/quartz.layout.ts b/quartz.layout.ts deleted file mode 100644 index cffc6ba24..000000000 --- a/quartz.layout.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { loadQuartzLayout } from "./quartz/plugins/loader/config-loader" -export const layout = await loadQuartzLayout() diff --git a/quartz.ts b/quartz.ts new file mode 100644 index 000000000..7a257d16b --- /dev/null +++ b/quartz.ts @@ -0,0 +1,5 @@ +import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader" + +const config = await loadQuartzConfig() +export default config +export const layout = await loadQuartzLayout() diff --git a/quartz/build.ts b/quartz/build.ts index 05a0722ba..0794b8a28 100644 --- a/quartz/build.ts +++ b/quartz/build.ts @@ -8,7 +8,7 @@ import { styleText } from "util" import { parseMarkdown } from "./processors/parse" import { filterContent } from "./processors/filter" import { emitContent } from "./processors/emit" -import cfg from "../quartz.config" +import cfg from "../quartz" import { FilePath, joinSegments, slugifyFilePath } from "./util/path" import chokidar from "chokidar" import { ProcessedContent } from "./plugins/vfile" diff --git a/quartz/cli/handlers.js b/quartz/cli/handlers.js index 715421b18..fe6b82870 100644 --- a/quartz/cli/handlers.js +++ b/quartz/cli/handlers.js @@ -24,7 +24,13 @@ import { stashContentFolder, } from "./helpers.js" import { handlePluginRestore, handlePluginCheck } from "./plugin-git-handlers.js" -import { configExists, createConfigFromDefault } from "./plugin-data.js" +import { + configExists, + createConfigFromDefault, + readPluginsJson, + writePluginsJson, + extractPluginName, +} from "./plugin-data.js" import { UPSTREAM_NAME, QUARTZ_SOURCE_BRANCH, @@ -189,7 +195,7 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started. // get a preferred link resolution strategy linkResolutionStrategy = exitIfCancel( await select({ - message: `Choose how Quartz should resolve links in your content. This should match Obsidian's link format. You can change this later in \`quartz.config.ts\`.`, + message: `Choose how Quartz should resolve links in your content. This should match Obsidian's link format. You can change this later in \`quartz.config.yaml\`.`, options: [ { value: "shortest", @@ -209,14 +215,20 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started. ) } - // now, do config changes - const configFilePath = path.join(cwd, "quartz.config.ts") - let configContent = await fs.promises.readFile(configFilePath, { encoding: "utf-8" }) - configContent = configContent.replace( - /markdownLinkResolution: '(.+)'/, - `markdownLinkResolution: '${linkResolutionStrategy}'`, - ) - await fs.promises.writeFile(configFilePath, configContent) + // Update markdownLinkResolution in the crawl-links plugin options via YAML config + const json = readPluginsJson() + if (json?.plugins) { + const crawlLinksIndex = json.plugins.findIndex( + (p) => extractPluginName(p.source) === "crawl-links", + ) + if (crawlLinksIndex !== -1) { + json.plugins[crawlLinksIndex].options = { + ...json.plugins[crawlLinksIndex].options, + markdownLinkResolution: linkResolutionStrategy, + } + writePluginsJson(json) + } + } if (!configExists()) { createConfigFromDefault() diff --git a/quartz/cli/migrate-handler.js b/quartz/cli/migrate-handler.js index a4763e9f8..e148565dd 100644 --- a/quartz/cli/migrate-handler.js +++ b/quartz/cli/migrate-handler.js @@ -5,8 +5,7 @@ import { styleText } from "util" import YAML from "yaml" const CWD = process.cwd() -const CONFIG_PATH = path.join(CWD, "quartz.config.ts") -const LAYOUT_PATH = path.join(CWD, "quartz.layout.ts") +const QUARTZ_TS_PATH = path.join(CWD, "quartz.ts") const CONFIG_YAML_PATH = path.join(CWD, "quartz.config.yaml") const DEFAULT_CONFIG_YAML_PATH = path.join(CWD, "quartz.config.default.yaml") const LEGACY_DEFAULT_JSON_PATH = path.join(CWD, "quartz.plugins.default.json") @@ -30,8 +29,8 @@ function hasTsx() { function extractWithTsx() { const script = ` - const { default: config } = await import("./quartz.config.ts") - const { layout } = await import("./quartz.layout.ts") + const { default: config } = await import("./quartz.ts") + const { layout } = await import("./quartz.ts") const result = { configuration: config?.configuration ?? null, layoutInfo: { @@ -114,8 +113,8 @@ function buildPluginEntry(name, entry) { export async function handleMigrate() { console.log(styleText("cyan", "Migrating Quartz configuration...")) - if (!fs.existsSync(CONFIG_PATH)) { - console.log(styleText("red", "✗ quartz.config.ts not found. Aborting migration.")) + if (!fs.existsSync(QUARTZ_TS_PATH)) { + console.log(styleText("red", "✗ quartz.ts not found. Aborting migration.")) return } @@ -175,19 +174,17 @@ export async function handleMigrate() { const header = "# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json\n" fs.writeFileSync(CONFIG_YAML_PATH, header + YAML.stringify(outputJson, { lineWidth: 120 })) - const configTemplate = - 'import { loadQuartzConfig } from "./quartz/plugins/loader/config-loader"\n' + - "export default await loadQuartzConfig()\n" - const layoutTemplate = - 'import { loadQuartzLayout } from "./quartz/plugins/loader/config-loader"\n' + + const quartzTsTemplate = + 'import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader"\n' + + "\n" + + "const config = await loadQuartzConfig()\n" + + "export default config\n" + "export const layout = await loadQuartzLayout()\n" - fs.writeFileSync(CONFIG_PATH, configTemplate) - fs.writeFileSync(LAYOUT_PATH, layoutTemplate) + fs.writeFileSync(QUARTZ_TS_PATH, quartzTsTemplate) console.log(styleText("green", "✓ Created quartz.config.yaml")) - console.log(styleText("green", "✓ Replaced quartz.config.ts")) - console.log(styleText("green", "✓ Replaced quartz.layout.ts")) + console.log(styleText("green", "✓ Replaced quartz.ts")) console.log() console.log(styleText("yellow", "⚠ Verify plugin options in quartz.config.yaml")) console.log(styleText("gray", `Plugins migrated: ${plugins.length}`)) diff --git a/quartz/cli/plugin-handlers.js b/quartz/cli/plugin-handlers.js index 7c13e3b00..8ee6e6cfe 100644 --- a/quartz/cli/plugin-handlers.js +++ b/quartz/cli/plugin-handlers.js @@ -23,7 +23,7 @@ export async function handlePluginInstall(packageNames) { } console.log(styleText("green", "✓ Plugins installed successfully")) - console.log("\nAdd them to your quartz.config.ts:") + console.log("\nAdd them to your quartz.config.yaml:") for (const pkg of packageNames) { console.log(` import { Plugin } from "${pkg}"`) @@ -126,5 +126,5 @@ export async function handlePluginUninstall(packageNames) { } console.log(styleText("green", "✓ Plugins uninstalled successfully")) - console.log(styleText("yellow", "Don't forget to remove them from your quartz.config.ts!")) + console.log(styleText("yellow", "Don't forget to remove them from your quartz.config.yaml!")) } diff --git a/quartz/components/Date.tsx b/quartz/components/Date.tsx index 0a92cc4c3..15be573e8 100644 --- a/quartz/components/Date.tsx +++ b/quartz/components/Date.tsx @@ -12,7 +12,7 @@ export type ValidDateType = keyof Required["dates"] export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date | undefined { if (!cfg.defaultDateType) { throw new Error( - `Field 'defaultDateType' was not set in the configuration object of quartz.config.ts. See https://quartz.jzhao.xyz/configuration#general-configuration for more details.`, + `Field 'defaultDateType' was not set in the configuration object of quartz.config.yaml. See https://quartz.jzhao.xyz/configuration#general-configuration for more details.`, ) } return data.dates?.[cfg.defaultDateType] diff --git a/quartz/plugins/loader/config-loader.ts b/quartz/plugins/loader/config-loader.ts index 891056509..9d02b5a8b 100644 --- a/quartz/plugins/loader/config-loader.ts +++ b/quartz/plugins/loader/config-loader.ts @@ -201,16 +201,21 @@ async function getManifest(source: string): Promise { return (await readManifestFromPackageJson(source)) ?? (await resolvePluginManifest(source)) } -export async function loadQuartzConfig(): Promise { +export async function loadQuartzConfig( + configOverrides?: Partial, +): Promise { const json = readPluginsJson() if (!json) { // Fallback: import old-style config directly - const oldConfig = await import("../../../quartz.config") + const oldConfig = await import("../../../quartz") return oldConfig.default } - const configuration = json.configuration as unknown as GlobalConfiguration + const configuration = { + ...(json.configuration as unknown as GlobalConfiguration), + ...configOverrides, + } const enabledEntries = json.plugins.filter((e) => e.enabled) const manifests = new Map() @@ -486,7 +491,10 @@ function detectCategoryFromModule(module: unknown): ProcessingCategory | null { return null } -export async function loadQuartzLayout(): Promise<{ +export async function loadQuartzLayout(layoutOverrides?: { + defaults?: Partial + byPageType?: Record> +}): Promise<{ defaults: Partial byPageType: Record> }> { @@ -494,7 +502,7 @@ export async function loadQuartzLayout(): Promise<{ if (!json) { // Fallback: import old-style layout directly - const oldLayout = await import("../../../quartz.layout") + const oldLayout = await import("../../../quartz") return oldLayout.layout } @@ -579,7 +587,15 @@ export async function loadQuartzLayout(): Promise<{ if (footer && !pt.footer) pt.footer = footer } - return { defaults: defaultLayout, byPageType } + const mergedDefaults = { ...defaultLayout, ...layoutOverrides?.defaults } + const mergedByPageType = { ...byPageType } + if (layoutOverrides?.byPageType) { + for (const [pageType, overrideLayout] of Object.entries(layoutOverrides.byPageType)) { + mergedByPageType[pageType] = { ...mergedByPageType[pageType], ...overrideLayout } + } + } + + return { defaults: mergedDefaults, byPageType: mergedByPageType } } function buildLayoutForEntries( diff --git a/quartz/plugins/loader/install-plugins.ts b/quartz/plugins/loader/install-plugins.ts index 8bc20e5ee..7b554a53d 100644 --- a/quartz/plugins/loader/install-plugins.ts +++ b/quartz/plugins/loader/install-plugins.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import { installPlugins, parsePluginSource } from "./gitLoader.js" -import config from "../../../quartz.config.js" +import config from "../../../quartz.js" async function main() { const quartzConfig: any = config diff --git a/quartz/plugins/types.ts b/quartz/plugins/types.ts index abe576164..5694fbb69 100644 --- a/quartz/plugins/types.ts +++ b/quartz/plugins/types.ts @@ -108,7 +108,7 @@ export interface QuartzPageTypePluginInstance { // Community plugins use a differently-branded FullSlug in their PageMatcher, // making them incompatible with the internal PageMatcher under strict // function-parameter contravariance. This wider entry type avoids forcing -// casts in quartz.config.ts while the dispatcher safely calls match/generate +// casts in quartz.ts while the dispatcher safely calls match/generate // with the correct arguments at runtime. export interface PageTypePluginEntry { name: string diff --git a/quartz/worker.ts b/quartz/worker.ts index f4cf4c600..806b77fcf 100644 --- a/quartz/worker.ts +++ b/quartz/worker.ts @@ -1,6 +1,6 @@ import sourceMapSupport from "source-map-support" sourceMapSupport.install(options) -import cfg from "../quartz.config" +import cfg from "../quartz" import { BuildCtx, WorkerSerializableBuildCtx } from "./util/ctx" import { FilePath } from "./util/path" import {