mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-22 05:55:42 -05:00
chore: deleted redundant files
This commit is contained in:
parent
08c99940ba
commit
751def054a
File diff suppressed because it is too large
Load Diff
@ -1,937 +0,0 @@
|
|||||||
# Quartz v5 Plugin Management & Update System
|
|
||||||
|
|
||||||
## Design Principle
|
|
||||||
|
|
||||||
**Separate what the user configures from what upstream ships.** User customization lives in files that upstream never touches. This makes both plugin management and framework updates conflict-free by design.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Problem Statement](#problem-statement)
|
|
||||||
- [Architecture Overview](#architecture-overview)
|
|
||||||
- [File Ownership Model](#file-ownership-model)
|
|
||||||
- [quartz.config.yaml — The Source of Truth](#quartzconfigyaml--the-source-of-truth)
|
|
||||||
- [Plugin Manifest](#plugin-manifest)
|
|
||||||
16: #RZ|- [Thin Template File](#thin-template-file)
|
|
||||||
- [Plugin Ordering & Dependencies](#plugin-ordering--dependencies)
|
|
||||||
- [Layout System](#layout-system)
|
|
||||||
- [JSON Schema](#json-schema)
|
|
||||||
- [CLI Commands](#cli-commands)
|
|
||||||
- [Quartz Update Mechanism](#quartz-update-mechanism)
|
|
||||||
- [Quartz Syncer Integration](#quartz-syncer-integration)
|
|
||||||
- [Migration Path](#migration-path)
|
|
||||||
- [Implementation Plan](#implementation-plan)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Problem Statement
|
|
||||||
|
|
||||||
### Current Pain Points
|
|
||||||
|
|
||||||
Users must manually edit multiple files to use a plugin:
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
- **No auto-enable**: `npx quartz plugin add` installs but the user must manually edit TypeScript files to activate.
|
|
||||||
- **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.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
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
├── .quartz/plugins/ # CLI-managed — git clones of plugins
|
|
||||||
├── content/ # User content
|
|
||||||
└── package.json
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Ownership Model
|
|
||||||
|
|
||||||
| File | Owner | Tracked by upstream? | User edits? | Updated by `npx quartz update`? |
|
|
||||||
| ---------------------------- | -------- | --------------------- | ------------------------- | ------------------------------- | ---------------------- | --- |
|
|
||||||
| `quartz/` | Upstream | Yes | No (power users only) | 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 |
|
|
||||||
| `.quartz/plugins/` | **CLI** | No (`.gitignore`d) | No | No |
|
|
||||||
| `content/` | **User** | Yes (user's fork) | Yes | No |
|
|
||||||
|
|
||||||
The key insight: **upstream never ships or modifies `quartz.config.yaml`**. It ships `quartz.config.default.yaml` as a seed/reference. On `npx quartz create`, the default is copied to `quartz.config.yaml`. On `npx quartz update`, only framework code and the default file are updated — the user's config is untouched.
|
|
||||||
|
|
||||||
This is analogous to the `.env.example` / `.env` pattern.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## quartz.config.yaml — The Source of Truth
|
|
||||||
|
|
||||||
This file contains all user configuration: site settings, plugin declarations, plugin options, layout positions, and ordering.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json
|
|
||||||
|
|
||||||
configuration:
|
|
||||||
pageTitle: My Quartz Site
|
|
||||||
pageTitleSuffix: ""
|
|
||||||
enableSPA: true
|
|
||||||
enablePopovers: true
|
|
||||||
analytics:
|
|
||||||
provider: plausible
|
|
||||||
locale: en-US
|
|
||||||
baseUrl: example.com
|
|
||||||
ignorePatterns:
|
|
||||||
- private
|
|
||||||
- templates
|
|
||||||
- .obsidian
|
|
||||||
defaultDateType: modified
|
|
||||||
theme:
|
|
||||||
fontOrigin: googleFonts
|
|
||||||
cdnCaching: true
|
|
||||||
typography:
|
|
||||||
header: Schibsted Grotesk
|
|
||||||
body: Source Sans Pro
|
|
||||||
code: IBM Plex Mono
|
|
||||||
colors:
|
|
||||||
lightMode:
|
|
||||||
light: "#faf8f8"
|
|
||||||
lightgray: "#e5e5e5"
|
|
||||||
gray: "#b8b8b8"
|
|
||||||
darkgray: "#4e4e4e"
|
|
||||||
dark: "#2b2b2b"
|
|
||||||
secondary: "#284b63"
|
|
||||||
tertiary: "#84a59d"
|
|
||||||
highlight: "rgba(143, 159, 169, 0.15)"
|
|
||||||
textHighlight: "#fff23688"
|
|
||||||
darkMode:
|
|
||||||
light: "#161618"
|
|
||||||
lightgray: "#393639"
|
|
||||||
gray: "#646464"
|
|
||||||
darkgray: "#d4d4d4"
|
|
||||||
dark: "#ebebec"
|
|
||||||
secondary: "#7b97aa"
|
|
||||||
tertiary: "#84a59d"
|
|
||||||
highlight: "rgba(143, 159, 169, 0.15)"
|
|
||||||
textHighlight: "#b3aa0288"
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- source: github:quartz-community/created-modified-date
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
priority:
|
|
||||||
- frontmatter
|
|
||||||
- git
|
|
||||||
- filesystem
|
|
||||||
order: 10
|
|
||||||
|
|
||||||
- source: github:quartz-community/syntax-highlighting
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
theme:
|
|
||||||
light: github-light
|
|
||||||
dark: github-dark
|
|
||||||
keepBackground: false
|
|
||||||
order: 20
|
|
||||||
|
|
||||||
- source: github:quartz-community/obsidian-flavored-markdown
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
enableInHtmlEmbed: false
|
|
||||||
enableCheckbox: true
|
|
||||||
order: 30
|
|
||||||
|
|
||||||
- source: github:quartz-community/github-flavored-markdown
|
|
||||||
enabled: true
|
|
||||||
order: 40
|
|
||||||
|
|
||||||
- source: github:quartz-community/table-of-contents
|
|
||||||
enabled: true
|
|
||||||
order: 50
|
|
||||||
|
|
||||||
- source: github:quartz-community/crawl-links
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
markdownLinkResolution: shortest
|
|
||||||
order: 60
|
|
||||||
|
|
||||||
- source: github:quartz-community/description
|
|
||||||
enabled: true
|
|
||||||
order: 70
|
|
||||||
|
|
||||||
- source: github:quartz-community/latex
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
renderEngine: katex
|
|
||||||
order: 80
|
|
||||||
|
|
||||||
- source: github:quartz-community/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/canvas-page
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
- source: github:quartz-community/content-page
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
- source: github:quartz-community/folder-page
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
- source: github:quartz-community/tag-page
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
- source: github:quartz-community/explorer
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 50
|
|
||||||
|
|
||||||
- source: github:quartz-community/graph
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: right
|
|
||||||
priority: 10
|
|
||||||
|
|
||||||
- source: github:quartz-community/search
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 20
|
|
||||||
group: toolbar
|
|
||||||
groupOptions:
|
|
||||||
grow: true
|
|
||||||
|
|
||||||
- source: github:quartz-community/backlinks
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: right
|
|
||||||
priority: 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/page-title
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 10
|
|
||||||
|
|
||||||
- source: github:quartz-community/darkmode
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 30
|
|
||||||
group: toolbar
|
|
||||||
|
|
||||||
- source: github:quartz-community/reader-mode
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 35
|
|
||||||
group: toolbar
|
|
||||||
|
|
||||||
- source: github:quartz-community/spacer
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 15
|
|
||||||
display: mobile-only
|
|
||||||
|
|
||||||
- source: github:quartz-community/breadcrumbs
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: beforeBody
|
|
||||||
priority: 5
|
|
||||||
condition: not-index
|
|
||||||
|
|
||||||
- source: github:quartz-community/comments
|
|
||||||
enabled: false
|
|
||||||
options:
|
|
||||||
provider: giscus
|
|
||||||
options: {}
|
|
||||||
layout:
|
|
||||||
position: afterBody
|
|
||||||
priority: 10
|
|
||||||
|
|
||||||
- source: github:quartz-community/footer
|
|
||||||
enabled: true
|
|
||||||
options:
|
|
||||||
links:
|
|
||||||
GitHub: https://github.com/jackyzha0/quartz
|
|
||||||
Discord Community: https://discord.gg/cRFFHYye7t
|
|
||||||
|
|
||||||
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: []
|
|
||||||
canvas: {}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plugin Entry Fields
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `source` | Yes | Plugin source identifier (e.g., `"github:quartz-community/explorer"`) |
|
|
||||||
| `enabled` | Yes | Whether the plugin is active |
|
|
||||||
| `options` | No | Plugin-specific configuration (object, validated against plugin's `configSchema`) |
|
|
||||||
| `order` | No | Numeric execution order (lower = runs first). Primarily relevant for transformers. Defaults to the plugin's `defaultOrder` from its manifest, or `50` if unspecified. |
|
|
||||||
| `layout` | No | Layout configuration for component-providing plugins (see [Layout System](#layout-system)) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plugin Manifest
|
|
||||||
|
|
||||||
Each plugin declares metadata in its `package.json` under a `quartz` field, or in a dedicated `quartz-plugin.json` file at the plugin root.
|
|
||||||
|
|
||||||
### Standard Plugin Manifest
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"quartz": {
|
|
||||||
"name": "obsidian-flavored-markdown",
|
|
||||||
"displayName": "Obsidian Flavored Markdown",
|
|
||||||
"category": "transformer",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"quartzVersion": ">=5.0.0",
|
|
||||||
"dependencies": [],
|
|
||||||
"defaultOrder": 30,
|
|
||||||
"defaultEnabled": true,
|
|
||||||
"defaultOptions": {
|
|
||||||
"enableInHtmlEmbed": false,
|
|
||||||
"enableCheckbox": true
|
|
||||||
},
|
|
||||||
"configSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enableInHtmlEmbed": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Parse Obsidian-flavored markdown inside HTML embed tags",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableCheckbox": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Enable interactive checkboxes with custom task characters",
|
|
||||||
"default": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Component-Providing Plugin Manifest
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"quartz": {
|
|
||||||
"name": "explorer",
|
|
||||||
"displayName": "Explorer",
|
|
||||||
"category": "emitter",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"quartzVersion": ">=5.0.0",
|
|
||||||
"dependencies": [],
|
|
||||||
"defaultEnabled": true,
|
|
||||||
"defaultOptions": {},
|
|
||||||
"components": {
|
|
||||||
"Explorer": {
|
|
||||||
"displayName": "Explorer",
|
|
||||||
"description": "File tree navigator sidebar",
|
|
||||||
"defaultPosition": "left",
|
|
||||||
"defaultPriority": 50
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manifest Fields
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
| ---------------- | -------- | -------------------------------------------------------------------------------------- |
|
|
||||||
| `name` | Yes | Unique plugin identifier |
|
|
||||||
| `displayName` | Yes | Human-readable name |
|
|
||||||
| `category` | Yes | Plugin type: `"transformer"`, `"filter"`, `"emitter"`, or `"pageType"` |
|
|
||||||
| `version` | Yes | Semver version string |
|
|
||||||
| `quartzVersion` | No | Minimum compatible Quartz version (semver range) |
|
|
||||||
| `dependencies` | No | Array of plugin sources this plugin requires |
|
|
||||||
| `defaultOrder` | No | Default numeric execution order (0-100 convention). Defaults to `50`. |
|
|
||||||
| `defaultEnabled` | No | Whether the plugin is enabled by default on install. Defaults to `true`. |
|
|
||||||
| `defaultOptions` | No | Default options applied when no user options are specified |
|
|
||||||
| `configSchema` | No | JSON Schema for the plugin's `options` object. Used for validation and TUI generation. |
|
|
||||||
| `components` | No | Components provided by this plugin, with layout defaults |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Thin Template Files
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
1. Reads `quartz.config.yaml` (falls back to `quartz.plugins.json` for backward compatibility)
|
|
||||||
2. Resolves all plugin sources (git clone if not already installed)
|
|
||||||
3. Reads each plugin's manifest for category, dependencies, default options
|
|
||||||
4. Merges user options over plugin defaults
|
|
||||||
5. Validates dependencies and ordering (see [Plugin Ordering & Dependencies](#plugin-ordering--dependencies))
|
|
||||||
6. Instantiates plugin factories with resolved options
|
|
||||||
7. Assembles the `QuartzConfig` object
|
|
||||||
8. Returns the fully resolved config
|
|
||||||
|
|
||||||
### What `loadQuartzLayout()` Does
|
|
||||||
|
|
||||||
1. Reads `quartz.config.yaml` (falls back to `quartz.plugins.json` for backward compatibility)
|
|
||||||
2. Finds all enabled plugins with `layout` declarations
|
|
||||||
3. Groups components by `position` (`left`, `right`, `beforeBody`, `afterBody`)
|
|
||||||
4. Sorts within each position by `priority`
|
|
||||||
5. Applies display modifiers (`display`, `condition`)
|
|
||||||
6. Resolves `group` references into Flex wrappers
|
|
||||||
7. Applies `byPageType` overrides
|
|
||||||
8. Assembles the `FullPageLayout` object
|
|
||||||
9. Falls back to upstream defaults for structural positions (`head`, `header`, `footer`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plugin Ordering & Dependencies
|
|
||||||
|
|
||||||
### Numeric Order
|
|
||||||
|
|
||||||
Plugins within each category are sorted by their `order` value (lower = runs first). The convention is 0-100, with the default being `50`.
|
|
||||||
|
|
||||||
```
|
|
||||||
order: 10 → CreatedModifiedDate (runs first among transformers)
|
|
||||||
order: 20 → SyntaxHighlighting
|
|
||||||
order: 30 → ObsidianFlavoredMarkdown
|
|
||||||
order: 40 → GitHubFlavoredMarkdown
|
|
||||||
order: 50 → TableOfContents (default order)
|
|
||||||
order: 60 → CrawlLinks
|
|
||||||
order: 70 → Description
|
|
||||||
order: 80 → Latex (runs last among transformers)
|
|
||||||
```
|
|
||||||
|
|
||||||
Order values only affect execution within the same plugin category (transformers sort independently from filters, etc.).
|
|
||||||
|
|
||||||
### Dependency Declaration
|
|
||||||
|
|
||||||
Plugins declare dependencies in their manifest:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"quartz": {
|
|
||||||
"dependencies": ["github:quartz-community/crawl-links"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dependency Validation
|
|
||||||
|
|
||||||
At config load time, the resolver performs the following checks:
|
|
||||||
|
|
||||||
1. **Missing dependency**: If plugin A depends on plugin B, but B is not installed → **error** with message: `Plugin "A" requires "B". Run: npx quartz plugin add B`
|
|
||||||
2. **Disabled dependency**: If plugin A depends on plugin B, but B has `"enabled": false` → **warning**: `Plugin "A" depends on "B" which is disabled. "A" may not function correctly.`
|
|
||||||
3. **Order violation**: If plugin A depends on plugin B, but A has a _lower_ order than B (meaning A would run before its dependency) → **error**: `Plugin "A" (order: 20) depends on "B" (order: 50), but "A" is configured to run first. Either increase "A"'s order above 50 or decrease "B"'s order below 20.`
|
|
||||||
4. **Circular dependency**: If A depends on B and B depends on A → **error**: `Circular dependency detected: A → B → A`
|
|
||||||
|
|
||||||
These checks run at build time. The CLI also validates after `plugin add` / `plugin config` operations and reports issues immediately.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Layout System
|
|
||||||
|
|
||||||
### Position-Based Component Placement
|
|
||||||
|
|
||||||
Each component-providing plugin can declare a `layout` field in `quartz.config.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- source: github:quartz-community/explorer
|
|
||||||
enabled: true
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 50
|
|
||||||
```
|
|
||||||
|
|
||||||
Available positions map to the `FullPageLayout` structure:
|
|
||||||
|
|
||||||
| Position | Description |
|
|
||||||
| ------------ | ---------------------- |
|
|
||||||
| `left` | Left sidebar |
|
|
||||||
| `right` | Right sidebar |
|
|
||||||
| `beforeBody` | Above the main content |
|
|
||||||
| `afterBody` | Below the main content |
|
|
||||||
|
|
||||||
The structural positions `head`, `header` (page-level header), and `footer` are handled by specific plugins (Head is built-in, Footer is a plugin with a dedicated role) and don't use the generic position system.
|
|
||||||
|
|
||||||
### Display Modifiers
|
|
||||||
|
|
||||||
Components can be restricted to specific viewport sizes:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 15
|
|
||||||
display: mobile-only
|
|
||||||
```
|
|
||||||
|
|
||||||
| Value | Effect |
|
|
||||||
| ----------------- | ----------------------------------------------------- |
|
|
||||||
| `"all"` (default) | Shown on all viewports |
|
|
||||||
| `"mobile-only"` | Only shown on mobile (adds `mobile-only` CSS class) |
|
|
||||||
| `"desktop-only"` | Only shown on desktop (adds `desktop-only` CSS class) |
|
|
||||||
|
|
||||||
At build time, the layout loader wraps components with `display` modifiers using the existing `MobileOnly` / `DesktopOnly` higher-order components.
|
|
||||||
|
|
||||||
### Conditional Rendering
|
|
||||||
|
|
||||||
Components can declare a condition for when they should render:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- source: github:quartz-community/breadcrumbs
|
|
||||||
layout:
|
|
||||||
position: beforeBody
|
|
||||||
priority: 5
|
|
||||||
condition: not-index
|
|
||||||
```
|
|
||||||
|
|
||||||
Conditions are named presets that cover common use cases:
|
|
||||||
|
|
||||||
| Condition | Behavior |
|
|
||||||
| ----------------- | ------------------------------------------------ |
|
|
||||||
| `"not-index"` | Hidden on the site index page |
|
|
||||||
| `"has-tags"` | Only shown when the page has tags |
|
|
||||||
| `"has-backlinks"` | Only shown when the page has backlinks |
|
|
||||||
| `"has-toc"` | Only shown when the page has a table of contents |
|
|
||||||
|
|
||||||
At build time, the layout loader wraps components with conditions using the existing `ConditionalRender` higher-order component with the appropriate predicate function.
|
|
||||||
|
|
||||||
Plugins can declare additional named conditions in their manifest. The condition registry is extensible.
|
|
||||||
|
|
||||||
### Component Grouping (Flex)
|
|
||||||
|
|
||||||
Multiple components can be grouped into a flex container using the `group` field:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- source: github:quartz-community/search
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 20
|
|
||||||
group: toolbar
|
|
||||||
groupOptions:
|
|
||||||
grow: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Groups are configured in the top-level `layout.groups` section:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
layout:
|
|
||||||
groups:
|
|
||||||
toolbar:
|
|
||||||
direction: row
|
|
||||||
gap: "0.5rem"
|
|
||||||
```
|
|
||||||
|
|
||||||
All components sharing the same `group` name within the same `position` are collected and wrapped in a `Flex` component. The `groupOptions` on each component control its flex behavior within the group (`grow`, `shrink`, `basis`, `order`, `align`).
|
|
||||||
|
|
||||||
### Per-Page-Type Overrides
|
|
||||||
|
|
||||||
The default layout applies to all page types. Overrides for specific page types are declared in `layout.byPageType`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
layout:
|
|
||||||
byPageType:
|
|
||||||
folder:
|
|
||||||
exclude:
|
|
||||||
- reader-mode
|
|
||||||
- table-of-contents
|
|
||||||
positions:
|
|
||||||
right: []
|
|
||||||
tag:
|
|
||||||
exclude:
|
|
||||||
- reader-mode
|
|
||||||
positions:
|
|
||||||
right: []
|
|
||||||
"404":
|
|
||||||
positions:
|
|
||||||
beforeBody: []
|
|
||||||
left: []
|
|
||||||
right: []
|
|
||||||
```
|
|
||||||
|
|
||||||
| Field | Description |
|
|
||||||
| ----------- | ------------------------------------------------------------------------------- |
|
|
||||||
| `exclude` | Array of plugin names to hide for this page type |
|
|
||||||
| `positions` | Override specific positions. An empty array `[]` clears that position entirely. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## JSON Schema
|
|
||||||
|
|
||||||
The JSON Schema (`quartz-plugins.schema.json`) is **standardized for the structure** of `quartz.config.yaml` while **leaving room for plugin-specific configuration**.
|
|
||||||
|
|
||||||
### Schema Design
|
|
||||||
|
|
||||||
The schema defines:
|
|
||||||
|
|
||||||
- The shape of a plugin entry (`source`, `enabled`, `options`, `order`, `layout`)
|
|
||||||
- The shape of `configuration` (site settings, theme, analytics)
|
|
||||||
- The shape of `layout` (groups, byPageType overrides)
|
|
||||||
- Valid values for enums (`position`, `display`, `condition` presets)
|
|
||||||
|
|
||||||
The `options` field on each plugin entry is typed as `"type": "object"` with no additional constraints at the schema level. Individual plugins define their own options schema via `configSchema` in their manifest. Validation tooling can merge the base schema with plugin-specific schemas for full validation.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["configuration", "plugins"],
|
|
||||||
"properties": {
|
|
||||||
"configuration": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Global site configuration",
|
|
||||||
"required": ["pageTitle", "enableSPA", "locale", "theme"],
|
|
||||||
"properties": {
|
|
||||||
"pageTitle": { "type": "string" },
|
|
||||||
"enableSPA": { "type": "boolean" },
|
|
||||||
"locale": { "type": "string" },
|
|
||||||
"theme": { "type": "object" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["source", "enabled"],
|
|
||||||
"properties": {
|
|
||||||
"source": { "type": "string" },
|
|
||||||
"enabled": { "type": "boolean" },
|
|
||||||
"options": { "type": "object" },
|
|
||||||
"order": { "type": "number", "minimum": 0 },
|
|
||||||
"layout": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"position": { "enum": ["left", "right", "beforeBody", "afterBody"] },
|
|
||||||
"priority": { "type": "number" },
|
|
||||||
"display": { "enum": ["all", "mobile-only", "desktop-only"] },
|
|
||||||
"condition": { "type": "string" },
|
|
||||||
"group": { "type": "string" },
|
|
||||||
"groupOptions": { "type": "object" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"layout": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"groups": { "type": "object" },
|
|
||||||
"byPageType": { "type": "object" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This schema ships with Quartz (under `quartz/plugins/`). The YAML config file references the schema via a `# yaml-language-server: $schema=...` header comment, which provides editor autocompletion and validation when using a YAML language server (e.g., the Red Hat YAML extension in VS Code).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CLI Commands
|
|
||||||
|
|
||||||
### Plugin Management
|
|
||||||
|
|
||||||
```
|
|
||||||
npx quartz plugin add <source> Install plugin and add to quartz.config.yaml
|
|
||||||
npx quartz plugin remove <name> Remove from quartz.config.yaml and uninstall
|
|
||||||
npx quartz plugin enable <name> Set enabled: true in quartz.config.yaml
|
|
||||||
npx quartz plugin disable <name> Set enabled: false in quartz.config.yaml
|
|
||||||
npx quartz plugin update [name] Fetch latest and rebuild (specific or all)
|
|
||||||
npx quartz plugin check Check for available updates without applying
|
|
||||||
npx quartz plugin list Show all plugins with status and version info
|
|
||||||
npx quartz plugin config <name> Show or edit plugin options
|
|
||||||
npx quartz plugin restore Reinstall all plugins from quartz.lock.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Framework Management
|
|
||||||
|
|
||||||
```
|
|
||||||
npx quartz update Update Quartz framework from upstream
|
|
||||||
npx quartz migrate Convert old config to quartz.config.yaml (one-time)
|
|
||||||
npx quartz create Create new Quartz site (copies default YAML)
|
|
||||||
```
|
|
||||||
|
|
||||||
### `npx quartz plugin add` Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Parse source (e.g., "github:quartz-community/explorer")
|
|
||||||
2. Clone to .quartz/plugins/<name>/, run npm install + npm run build
|
|
||||||
3. Read plugin manifest → get category, defaultOptions, defaultOrder, dependencies, components
|
|
||||||
4. Check dependencies → if missing, prompt user and auto-install
|
|
||||||
5. Validate order against existing plugins and dependencies
|
|
||||||
6. Add entry to quartz.config.yaml:
|
|
||||||
- source: as provided
|
|
||||||
- enabled: manifest.defaultEnabled (default: true)
|
|
||||||
- options: manifest.defaultOptions (default: {})
|
|
||||||
- order: manifest.defaultOrder (default: 50, for transformers)
|
|
||||||
- layout: from manifest.components defaults (if component-providing)
|
|
||||||
7. Update quartz.lock.json with commit hash + metadata
|
|
||||||
8. Print summary: "Installed <name> as <category>. Active on next build."
|
|
||||||
```
|
|
||||||
|
|
||||||
### `npx quartz plugin check` Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
1. For each plugin in quartz.lock.json:
|
|
||||||
a. git ls-remote to get latest commit on tracked branch
|
|
||||||
b. Compare to locked commit hash
|
|
||||||
c. If different → fetch commit message for display
|
|
||||||
2. Print table:
|
|
||||||
|
|
||||||
Plugin Installed Available Status
|
|
||||||
obsidian-flavored-markdown abc1234 def5678 3 commits behind
|
|
||||||
explorer — — up to date
|
|
||||||
graph 111aaaa 222bbbb 1 commit behind
|
|
||||||
|
|
||||||
3. Exit without changes
|
|
||||||
```
|
|
||||||
|
|
||||||
### `npx quartz plugin config` Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
# Show current config
|
|
||||||
npx quartz plugin config obsidian-flavored-markdown
|
|
||||||
→ Prints current options from quartz.config.yaml
|
|
||||||
|
|
||||||
# Set a value
|
|
||||||
npx quartz plugin config obsidian-flavored-markdown --set enableCheckbox=true
|
|
||||||
→ Updates quartz.config.yaml, validates against configSchema
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quartz Update Mechanism
|
|
||||||
|
|
||||||
### Current Problem
|
|
||||||
|
|
||||||
`npx quartz update` runs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git pull --no-rebase --autostash -s recursive -X ours upstream v5
|
|
||||||
```
|
|
||||||
|
|
||||||
This causes:
|
|
||||||
|
|
||||||
- Merge conflicts in `quartz.config.ts` and `quartz.layout.ts` (the only files users typically edit)
|
|
||||||
- `-X ours` silently drops upstream improvements to those files
|
|
||||||
- Users unfamiliar with git resolution get stuck
|
|
||||||
|
|
||||||
### Solution: Conflict-Free by Design
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Show current version
|
|
||||||
2. Stash content folder (existing behavior)
|
|
||||||
3. git fetch upstream
|
|
||||||
4. git merge upstream/v5 (no -X ours needed — no conflicts expected)
|
|
||||||
5. Restore content folder
|
|
||||||
6. npm install (update dependencies for new framework version)
|
|
||||||
7. npx quartz plugin restore (rebuild all plugins against new Quartz version)
|
|
||||||
8. Validate: check all plugin manifests' quartzVersion against new version
|
|
||||||
9. Report results:
|
|
||||||
- "Updated Quartz from v5.1.0 to v5.2.0"
|
|
||||||
- "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 `quartz.ts` file is a thin template (no user modifications), `git merge` applies cleanly.
|
|
||||||
|
|
||||||
### Edge Case: Power Users
|
|
||||||
|
|
||||||
Users who modify framework internals (files under `quartz/components/`, `quartz/styles/`, etc.) may still encounter merge conflicts. This is expected and acceptable — these users are sufficiently versed in development to resolve conflicts manually.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quartz Syncer Integration
|
|
||||||
|
|
||||||
With all user configuration in a single YAML file, Quartz Syncer (Obsidian plugin using isomorphic-git with LightningFS) can fully manage Quartz configuration:
|
|
||||||
|
|
||||||
| Operation | Implementation |
|
|
||||||
| ------------------------- | ---------------------------------------------------------------------------------------- |
|
|
||||||
| **Read plugin list** | Parse `quartz.config.yaml` |
|
|
||||||
| **Enable/disable plugin** | Toggle `enabled` field |
|
|
||||||
| **Change plugin options** | Edit `options` object |
|
|
||||||
| **Reorder plugins** | Change `order` values |
|
|
||||||
| **Rearrange layout** | Change `layout.position` and `layout.priority` |
|
|
||||||
| **Change site config** | Edit `configuration` object (pageTitle, theme, etc.) |
|
|
||||||
| **Validate options** | Fetch plugin's `configSchema` from manifest |
|
|
||||||
| **Commit + push** | Existing isomorphic-git flow |
|
|
||||||
| **Add plugin** | Append to `plugins` array, commit, push. Plugin install happens on next build or via CI. |
|
|
||||||
|
|
||||||
No TypeScript parsing. No AST manipulation. Plain YAML read/write → git commit → push.
|
|
||||||
|
|
||||||
This directly addresses Quartz Syncer Issue #35 ("Manage Quartz configuration from Obsidian").
|
|
||||||
|
|
||||||
### Plugin Installation from Quartz Syncer
|
|
||||||
|
|
||||||
When Quartz Syncer adds a plugin entry to `quartz.config.yaml`, the actual git clone and build happens on the next `npx quartz build` (or `npx quartz plugin restore`). The build process detects plugins declared in the config but not yet installed and installs them automatically.
|
|
||||||
|
|
||||||
Alternatively, if the user's Quartz site has CI (e.g., GitHub Actions), the CI pipeline handles `plugin restore` as part of the build.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Migration Path
|
|
||||||
|
|
||||||
### For Existing Users
|
|
||||||
|
|
||||||
A `npx quartz migrate` command handles the one-time conversion:
|
|
||||||
|
|
||||||
1. Imports current `quartz.config.ts` at runtime (no AST parsing needed — just `import()`)
|
|
||||||
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 `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 the consolidated `quartz.ts` wrapper
|
|
||||||
6. Prints migration summary
|
|
||||||
|
|
||||||
### For Plugin Authors
|
|
||||||
|
|
||||||
Plugins need to add a `quartz` field to their `package.json` or ship a `quartz-plugin.json`:
|
|
||||||
|
|
||||||
- `category` — already exists in current `PluginManifest`
|
|
||||||
- `dependencies` — **new field**
|
|
||||||
- `defaultOrder` — **new field**
|
|
||||||
- `defaultEnabled` — **new field**
|
|
||||||
- `defaultOptions` — **new field**
|
|
||||||
- `configSchema` — already exists (optional, now more important)
|
|
||||||
- `components` — already partially exists, extended with layout defaults
|
|
||||||
|
|
||||||
Plugins without the extended manifest fields still load with sensible defaults (`defaultOrder: 50`, `defaultEnabled: true`, no dependencies).
|
|
||||||
|
|
||||||
### Backward Compatibility
|
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
### Phase 1: Foundation
|
|
||||||
|
|
||||||
1. **Extended PluginManifest type** — Add `dependencies`, `defaultOrder`, `defaultEnabled`, `defaultOptions`, and `components` layout defaults to `PluginManifest` in `quartz/plugins/loader/types.ts`
|
|
||||||
2. **`quartz.config.yaml` type and loader** — Create `config-loader.ts` with `loadQuartzConfig()` and `loadQuartzLayout()` functions that read the YAML, resolve plugins, validate dependencies/ordering, and return the assembled config and layout objects
|
|
||||||
3. **JSON Schema** — Create `quartz-plugins.schema.json` with the standardized structure
|
|
||||||
4. **`quartz.config.default.yaml`** — Create the upstream default configuration file
|
|
||||||
|
|
||||||
### Phase 2: Layout Resolution
|
|
||||||
|
|
||||||
5. **Layout resolver** — Implement the logic that converts YAML layout declarations (position, priority, display, condition, group) into the `FullPageLayout` object, including Flex grouping, MobileOnly/DesktopOnly wrapping, and ConditionalRender wrapping
|
|
||||||
6. **Condition registry** — Implement the named condition preset system (`not-index`, `has-tags`, etc.)
|
|
||||||
7. **Spacer plugin** — Extract the current `Spacer` component into a minimal plugin
|
|
||||||
|
|
||||||
### Phase 3: CLI
|
|
||||||
|
|
||||||
8. **`npx quartz plugin add` refactor** — Update to write to `quartz.config.yaml` instead of just cloning + lockfile
|
|
||||||
9. **`npx quartz plugin remove/enable/disable/config`** — Implement YAML-editing CLI commands
|
|
||||||
10. **`npx quartz plugin check`** — Implement update checking without applying
|
|
||||||
11. **`npx quartz migrate`** — Implement the one-time migration command
|
|
||||||
12. **`npx quartz update` refactor** — Remove `-X ours`, add plugin compatibility validation
|
|
||||||
13. **`npx quartz create` update** — Copy `quartz.config.default.yaml` → `quartz.config.yaml` on new site creation
|
|
||||||
|
|
||||||
### Phase 4: Thin Templates
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
17. **Update existing plugin manifests** — Add `quartz` metadata to all `quartz-community/*` plugins
|
|
||||||
18. **Plugin authoring documentation** — Document the manifest format, config schema, and layout declarations
|
|
||||||
19. **User documentation** — Document `quartz.config.yaml`, CLI commands, and migration
|
|
||||||
@ -1,537 +0,0 @@
|
|||||||
# Quartz Syncer v5 Integration Notes
|
|
||||||
|
|
||||||
Notes and decisions for implementing Quartz v5 plugin management support in Quartz Syncer (Obsidian plugin). This document captures the architectural changes that affect Syncer and the specific integration points.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Overview of Changes](#overview-of-changes)
|
|
||||||
- [File Ownership Model](#file-ownership-model)
|
|
||||||
- [quartz.config.yaml — The Syncer's Interface](#quartzconfigyaml--the-syncers-interface)
|
|
||||||
- [Operations Syncer Can Perform](#operations-syncer-can-perform)
|
|
||||||
- [Plugin Installation Lifecycle](#plugin-installation-lifecycle)
|
|
||||||
- [Schema Validation](#schema-validation)
|
|
||||||
- [Layout System](#layout-system)
|
|
||||||
- [Plugin Manifest (package.json quartz field)](#plugin-manifest-packagejson-quartz-field)
|
|
||||||
- [Migration Considerations](#migration-considerations)
|
|
||||||
- [Edge Cases and Gotchas](#edge-cases-and-gotchas)
|
|
||||||
- [Syncer Issue #35 Resolution](#syncer-issue-35-resolution)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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. 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) 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)
|
|
||||||
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
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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`. |
|
|
||||||
| 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. |
|
|
||||||
|
|
||||||
**Key insight:** Syncer only needs to read/write `quartz.config.yaml`. Everything else is handled by the CLI on the build side.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## quartz.config.yaml — The Syncer's Interface
|
|
||||||
|
|
||||||
### Top-Level Structure
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json
|
|
||||||
configuration:
|
|
||||||
# GlobalConfiguration fields
|
|
||||||
pageTitle: My Quartz Site
|
|
||||||
enableSPA: true
|
|
||||||
# ...
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
# Array of plugin entries
|
|
||||||
- source: "github:quartz-community/explorer"
|
|
||||||
enabled: true
|
|
||||||
options: {}
|
|
||||||
order: 50
|
|
||||||
|
|
||||||
layout:
|
|
||||||
# Global layout overrides
|
|
||||||
groups: {}
|
|
||||||
byPageType: {}
|
|
||||||
```
|
|
||||||
|
|
||||||
### configuration Object
|
|
||||||
|
|
||||||
Maps directly to Quartz's `GlobalConfiguration` type. Fields Syncer should support editing:
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
| ----------------- | -------- | ---------------------------------------- |
|
|
||||||
| `pageTitle` | string | Site title |
|
|
||||||
| `pageTitleSuffix` | string | Appended to page titles in `<title>` |
|
|
||||||
| `enableSPA` | boolean | Single-page app navigation |
|
|
||||||
| `enablePopovers` | boolean | Link preview popovers |
|
|
||||||
| `analytics` | object | Analytics provider config |
|
|
||||||
| `locale` | string | Locale string (e.g., `"en-US"`) |
|
|
||||||
| `baseUrl` | string | Base URL for deployment |
|
|
||||||
| `ignorePatterns` | string[] | Glob patterns to ignore |
|
|
||||||
| `defaultDateType` | string | `"created"`, `"modified"`, `"published"` |
|
|
||||||
| `theme` | object | Colors, typography |
|
|
||||||
|
|
||||||
### plugins Array
|
|
||||||
|
|
||||||
Each entry:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- source: "github:quartz-community/explorer"
|
|
||||||
enabled: true
|
|
||||||
options: {}
|
|
||||||
order: 50
|
|
||||||
layout:
|
|
||||||
position: left
|
|
||||||
priority: 50
|
|
||||||
display: all
|
|
||||||
condition: null
|
|
||||||
group: null
|
|
||||||
groupOptions: null
|
|
||||||
```
|
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
|
||||||
| --------- | ------- | -------- | ---------------------------------------------------------- |
|
|
||||||
| `source` | string | Yes | Plugin identifier (`github:org/repo` or npm package name) |
|
|
||||||
| `enabled` | boolean | Yes | Whether the plugin is active |
|
|
||||||
| `options` | object | No | Plugin-specific configuration (passed to factory function) |
|
|
||||||
| `order` | number | No | Execution order within its category (lower = earlier) |
|
|
||||||
| `layout` | object | No | Only for component-providing plugins |
|
|
||||||
|
|
||||||
### layout Object (Global)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
layout:
|
|
||||||
groups:
|
|
||||||
toolbar:
|
|
||||||
direction: row
|
|
||||||
gap: "0.5rem"
|
|
||||||
byPageType:
|
|
||||||
folder:
|
|
||||||
exclude:
|
|
||||||
- reader-mode
|
|
||||||
positions:
|
|
||||||
right: []
|
|
||||||
"404":
|
|
||||||
positions:
|
|
||||||
beforeBody: []
|
|
||||||
left: []
|
|
||||||
right: []
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Operations Syncer Can Perform
|
|
||||||
|
|
||||||
### Read Operations
|
|
||||||
|
|
||||||
| Operation | How |
|
|
||||||
| ----------------------- | ----------------------------------------------------------- |
|
|
||||||
| List all plugins | Read `plugins` array |
|
|
||||||
| Get plugin status | Check `enabled` field |
|
|
||||||
| Get plugin options | Read `options` field |
|
|
||||||
| Get site configuration | Read `configuration` object |
|
|
||||||
| Get layout arrangement | Read `layout` fields on plugin entries + global `layout` |
|
|
||||||
| Check installed version | Read `quartz.lock.json` for commit hashes |
|
|
||||||
| Get plugin metadata | Read `.quartz/plugins/{name}/package.json` → `quartz` field |
|
|
||||||
|
|
||||||
### Write Operations
|
|
||||||
|
|
||||||
| Operation | How |
|
|
||||||
| --------------------- | ---------------------------------------------------------- |
|
|
||||||
| Enable plugin | Set `plugins[i].enabled = true` |
|
|
||||||
| Disable plugin | Set `plugins[i].enabled = false` |
|
|
||||||
| Change plugin options | Update `plugins[i].options` |
|
|
||||||
| Reorder plugins | Change `plugins[i].order` values |
|
|
||||||
| Rearrange layout | Change `plugins[i].layout.position` and `.priority` |
|
|
||||||
| Change site config | Update fields in `configuration` |
|
|
||||||
| Add plugin | Append new entry to `plugins` array with source + defaults |
|
|
||||||
| Remove plugin | Remove entry from `plugins` array |
|
|
||||||
|
|
||||||
### Add Plugin Flow (Syncer-initiated)
|
|
||||||
|
|
||||||
1. Syncer appends a new plugin entry to `quartz.config.yaml`:
|
|
||||||
```yaml
|
|
||||||
- source: "github:quartz-community/some-plugin"
|
|
||||||
enabled: true
|
|
||||||
options: {}
|
|
||||||
order: 50
|
|
||||||
```
|
|
||||||
2. Syncer commits and pushes the change
|
|
||||||
3. On next `npx quartz build` (or CI), the build process detects the plugin is declared but not installed
|
|
||||||
4. Build automatically runs `npx quartz plugin restore` to install missing plugins
|
|
||||||
5. Alternatively, user runs `npx quartz plugin restore` manually
|
|
||||||
|
|
||||||
**Important:** Syncer does NOT need to handle git clone or npm install. It only edits the YAML config.
|
|
||||||
|
|
||||||
### Remove Plugin Flow (Syncer-initiated)
|
|
||||||
|
|
||||||
1. Syncer removes the plugin entry from `plugins` array
|
|
||||||
2. Syncer commits and pushes
|
|
||||||
3. On next build, the orphaned plugin clone in `.quartz/plugins/` is harmless (ignored)
|
|
||||||
4. User can run `npx quartz plugin remove <name>` to clean up the clone
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Schema Validation
|
|
||||||
|
|
||||||
The JSON Schema is at `quartz/plugins/quartz-plugins.schema.json`. Syncer can use this for:
|
|
||||||
|
|
||||||
1. **Client-side validation** before committing changes
|
|
||||||
2. **Autocomplete hints** for configuration fields
|
|
||||||
3. **Type checking** for plugin options
|
|
||||||
|
|
||||||
The `quartz.config.yaml` file references the schema via a YAML language server comment on the first line:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json
|
|
||||||
```
|
|
||||||
|
|
||||||
This provides IDE autocompletion and validation when using editors that support the YAML language server (VS Code with YAML extension, IntelliJ, etc.). Syncer should preserve this comment when writing the file.
|
|
||||||
|
|
||||||
### Validation Points
|
|
||||||
|
|
||||||
- `configuration` fields have defined types and constraints
|
|
||||||
- `plugins[].source` must be a non-empty string
|
|
||||||
- `plugins[].enabled` must be boolean
|
|
||||||
- `plugins[].order` must be a number
|
|
||||||
- `plugins[].layout.position` must be one of: `"head"`, `"header"`, `"beforeBody"`, `"left"`, `"right"`, `"afterBody"`, `"footer"`
|
|
||||||
- `plugins[].layout.display` must be one of: `"all"`, `"mobile"`, `"desktop"`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Layout System
|
|
||||||
|
|
||||||
### Positions
|
|
||||||
|
|
||||||
Components can be placed in these layout positions:
|
|
||||||
|
|
||||||
| Position | Description |
|
|
||||||
| ------------ | -------------------------- |
|
|
||||||
| `head` | HTML `<head>` metadata |
|
|
||||||
| `header` | Top header area |
|
|
||||||
| `beforeBody` | Between header and content |
|
|
||||||
| `left` | Left sidebar |
|
|
||||||
| `right` | Right sidebar |
|
|
||||||
| `afterBody` | Between content and footer |
|
|
||||||
| `footer` | Bottom footer area |
|
|
||||||
|
|
||||||
### Priority
|
|
||||||
|
|
||||||
Within a position, components are sorted by `priority` (ascending). Lower numbers appear first/higher.
|
|
||||||
|
|
||||||
### Display Modifiers
|
|
||||||
|
|
||||||
| Value | Effect |
|
|
||||||
| ----------- | -------------------------- |
|
|
||||||
| `"all"` | Show on all viewport sizes |
|
|
||||||
| `"mobile"` | Show only on mobile |
|
|
||||||
| `"desktop"` | Show only on desktop |
|
|
||||||
|
|
||||||
### Conditions
|
|
||||||
|
|
||||||
Named presets that control when a component renders:
|
|
||||||
|
|
||||||
| Condition | Effect |
|
|
||||||
| ------------- | ----------------------------------------------- |
|
|
||||||
| `"not-index"` | Hide on the root index page |
|
|
||||||
| `"has-tags"` | Only show when the page has tags |
|
|
||||||
| `"has-toc"` | Only show when the page has a table of contents |
|
|
||||||
| `"not-tag"` | Hide on tag listing pages |
|
|
||||||
|
|
||||||
### Groups
|
|
||||||
|
|
||||||
Components can be grouped into flex containers:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Per-plugin layout entry
|
|
||||||
layout:
|
|
||||||
group: toolbar
|
|
||||||
groupOptions:
|
|
||||||
grow: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Global group definitions:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
layout:
|
|
||||||
groups:
|
|
||||||
toolbar:
|
|
||||||
direction: row
|
|
||||||
gap: "0.5rem"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Per-Page-Type Overrides
|
|
||||||
|
|
||||||
The global `layout.byPageType` object can override layout for specific page types:
|
|
||||||
`content` — standard markdown content pages
|
|
||||||
`folder` — folder listing pages
|
|
||||||
`tag` — tag listing pages
|
|
||||||
`canvas` — Obsidian canvas pages
|
|
||||||
`bases` — Obsidian Bases database pages
|
|
||||||
`404` — not found page
|
|
||||||
|
|
||||||
Each can `exclude` specific plugins or override `positions` entirely.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plugin Manifest (package.json quartz field)
|
|
||||||
|
|
||||||
Each plugin's `package.json` contains a `quartz` field with metadata. Syncer can read this for display purposes (plugin names, descriptions, categories) but should NOT modify it.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"quartz": {
|
|
||||||
"name": "explorer",
|
|
||||||
"displayName": "Explorer",
|
|
||||||
"category": "emitter",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"quartzVersion": ">=5.0.0",
|
|
||||||
"dependencies": [],
|
|
||||||
"defaultOrder": 50,
|
|
||||||
"defaultEnabled": true,
|
|
||||||
"defaultOptions": {},
|
|
||||||
"components": {
|
|
||||||
"Explorer": {
|
|
||||||
"displayName": "Explorer",
|
|
||||||
"defaultPosition": "left",
|
|
||||||
"defaultPriority": 50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note:** Plugin manifests remain JSON (they live in `package.json`). Only the user-facing config file is YAML.
|
|
||||||
|
|
||||||
### Categories
|
|
||||||
|
|
||||||
| Category | Description |
|
|
||||||
| ------------- | ----------------------------------------------- |
|
|
||||||
| `transformer` | Processes markdown/HTML during build |
|
|
||||||
| `filter` | Filters which pages to include/exclude |
|
|
||||||
| `emitter` | Generates output files and provides components |
|
|
||||||
| `pageType` | Defines how specific content types are rendered |
|
|
||||||
|
|
||||||
### Using Manifest Defaults
|
|
||||||
|
|
||||||
When adding a new plugin via Syncer, use the manifest's defaults to populate the initial entry:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Pseudo-code for Syncer
|
|
||||||
const manifest = readPackageJson(pluginPath).quartz
|
|
||||||
const entry = {
|
|
||||||
source: `github:quartz-community/${manifest.name}`,
|
|
||||||
enabled: manifest.defaultEnabled,
|
|
||||||
options: manifest.defaultOptions || {},
|
|
||||||
order: manifest.defaultOrder || 50,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add layout if plugin has components
|
|
||||||
if (manifest.components) {
|
|
||||||
const [componentName, componentMeta] = Object.entries(manifest.components)[0]
|
|
||||||
entry.layout = {
|
|
||||||
position: componentMeta.defaultPosition,
|
|
||||||
priority: componentMeta.defaultPriority,
|
|
||||||
display: "all",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Migration Considerations
|
|
||||||
|
|
||||||
### Detecting v5 vs v4
|
|
||||||
|
|
||||||
- **v5 (new system):** `quartz.config.yaml` exists at repository root (legacy fallback: `quartz.plugins.json`)
|
|
||||||
- **v4 (old system):** Neither `quartz.config.yaml` nor `quartz.plugins.json` exists; `quartz.config.ts` contains full configuration
|
|
||||||
|
|
||||||
Syncer should check for `quartz.config.yaml` first, then fall back to `quartz.plugins.json` for legacy v5 installs, to determine which mode to operate in.
|
|
||||||
|
|
||||||
### Recommending Migration
|
|
||||||
|
|
||||||
If Syncer detects v4 mode (no config file), it should prompt the user:
|
|
||||||
|
|
||||||
> "Your Quartz site uses the v4 configuration format. Run `npx quartz migrate` to enable plugin management from Obsidian."
|
|
||||||
|
|
||||||
Syncer should NOT attempt to run the migration itself — it requires Node.js/tsx to extract TypeScript config values.
|
|
||||||
|
|
||||||
### Post-Migration
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edge Cases and Gotchas
|
|
||||||
|
|
||||||
### 1. Plugin Not Yet Installed
|
|
||||||
|
|
||||||
A plugin can be declared in `quartz.config.yaml` but not yet installed (no clone in `.quartz/plugins/`). This is normal — the build process handles installation. Syncer should:
|
|
||||||
|
|
||||||
- Show the plugin in the list
|
|
||||||
- Mark it as "pending install" if the lockfile doesn't have it
|
|
||||||
- Allow enabling/disabling/configuring it regardless
|
|
||||||
|
|
||||||
### 2. Multiple Components per Plugin
|
|
||||||
|
|
||||||
Some plugins provide multiple components (though currently none do). The `components` object in the manifest is a map, not a single entry. Syncer should handle this gracefully.
|
|
||||||
|
|
||||||
### 3. Dependency Validation
|
|
||||||
|
|
||||||
Plugins can declare `dependencies` on other plugins. When disabling a plugin via Syncer, warn the user if other enabled plugins depend on it. The dependency is declared by plugin `name` (e.g., `"content-index"`).
|
|
||||||
|
|
||||||
### 4. Order Conflicts
|
|
||||||
|
|
||||||
Multiple plugins can have the same `order` value. This is fine — they'll be sorted stably. Syncer does not need to enforce unique order values.
|
|
||||||
|
|
||||||
### 5. YAML Comments
|
|
||||||
|
|
||||||
`quartz.config.yaml` is YAML, which natively supports comments. Users can add comments to annotate their configuration, and Syncer should preserve them when possible. The YAML parser (`yaml` npm package v2.x) supports comment roundtrip preservation via `keepSourceTokens: true`.
|
|
||||||
|
|
||||||
### 6. Schema Reference
|
|
||||||
|
|
||||||
The first line of `quartz.config.yaml` should contain the YAML language server schema reference comment:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# yaml-language-server: $schema=./quartz/plugins/quartz-plugins.schema.json
|
|
||||||
```
|
|
||||||
|
|
||||||
This comment should be preserved as-is when editing. It's a relative path to the schema file in the Quartz installation and provides IDE autocompletion.
|
|
||||||
|
|
||||||
### 7. quartz.lock.json Format
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"explorer": {
|
|
||||||
"source": "github:quartz-community/explorer",
|
|
||||||
"commit": "abc1234",
|
|
||||||
"resolved": "https://github.com/quartz-community/explorer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is CLI-managed. Syncer should only read it for display (showing installed versions), never write to it.
|
|
||||||
|
|
||||||
> **Note:** The lock file remains JSON. Only the user-facing config is YAML.
|
|
||||||
|
|
||||||
### 8. Configuration Field Types
|
|
||||||
|
|
||||||
Some `configuration` fields have complex types:
|
|
||||||
|
|
||||||
- `theme.colors` has `lightMode` and `darkMode` sub-objects with specific color fields
|
|
||||||
- `theme.typography` has `header`, `body`, `code` font family strings
|
|
||||||
- `analytics` has a `provider` field that determines the shape of the rest of the object
|
|
||||||
|
|
||||||
Refer to the JSON Schema for exact types and constraints.
|
|
||||||
|
|
||||||
### 9. Legacy JSON Fallback
|
|
||||||
|
|
||||||
The Quartz CLI includes backward compatibility for `quartz.plugins.json`. If Syncer encounters a site with the legacy JSON config instead of `quartz.config.yaml`, it should:
|
|
||||||
|
|
||||||
1. Read/write the JSON file normally (standard `JSON.parse` / `JSON.stringify`)
|
|
||||||
2. Optionally suggest migration: "Consider running `npx quartz migrate` to upgrade to the YAML config format"
|
|
||||||
|
|
||||||
The CLI's `plugin-data.js` handles this fallback automatically — it checks for `quartz.config.yaml` first, then falls back to `quartz.plugins.json`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Syncer Issue #35 Resolution
|
|
||||||
|
|
||||||
This architecture directly addresses [Quartz Syncer Issue #35](https://github.com/saberzero1/quartz-syncer/issues/35) — "Manage Quartz configuration from Obsidian."
|
|
||||||
|
|
||||||
### What Syncer Can Now Do
|
|
||||||
|
|
||||||
1. **Plugin Management UI** — List, enable/disable, configure, reorder plugins
|
|
||||||
2. **Layout Editor** — Drag-and-drop component arrangement (positions + priorities)
|
|
||||||
3. **Site Settings** — Edit pageTitle, theme colors, analytics, etc.
|
|
||||||
4. **Plugin Discovery** — Read manifests to show available plugins with descriptions
|
|
||||||
5. **Validation** — Use JSON Schema to validate changes before committing
|
|
||||||
|
|
||||||
### Minimal Implementation Path
|
|
||||||
|
|
||||||
1. Read `quartz.config.yaml` on sync
|
|
||||||
2. Parse YAML (using the `yaml` npm package)
|
|
||||||
3. Present a settings UI in Obsidian
|
|
||||||
4. On save: write updated YAML, commit, push
|
|
||||||
5. Let the CI/build pipeline handle the rest
|
|
||||||
|
|
||||||
No TypeScript parsing. No AST manipulation. No Node.js subprocess. Just YAML.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Reference: All 40 Default Plugins
|
|
||||||
|
|
||||||
| Plugin | Category | Default Enabled | Has Component | Default Position |
|
|
||||||
| -------------------------- | ----------- | --------------- | ------------- | ---------------- |
|
|
||||||
| note-properties | transformer | ✅ | ✅ | beforeBody |
|
|
||||||
| created-modified-date | transformer | ✅ | ❌ | - |
|
|
||||||
| syntax-highlighting | transformer | ✅ | ❌ | - |
|
|
||||||
| obsidian-flavored-markdown | transformer | ✅ | ❌ | - |
|
|
||||||
| github-flavored-markdown | transformer | ✅ | ❌ | - |
|
|
||||||
| table-of-contents | transformer | ✅ | ✅ | right |
|
|
||||||
| crawl-links | transformer | ✅ | ❌ | - |
|
|
||||||
| description | transformer | ✅ | ❌ | - |
|
|
||||||
| latex | transformer | ✅ | ❌ | - |
|
|
||||||
| citations | transformer | ❌ | ❌ | - |
|
|
||||||
| hard-line-breaks | transformer | ❌ | ❌ | - |
|
|
||||||
| ox-hugo | transformer | ❌ | ❌ | - |
|
|
||||||
| roam | transformer | ❌ | ❌ | - |
|
|
||||||
| remove-draft | filter | ✅ | ❌ | - |
|
|
||||||
| explicit-publish | filter | ❌ | ❌ | - |
|
|
||||||
| alias-redirects | emitter | ✅ | ❌ | - |
|
|
||||||
| content-index | emitter | ✅ | ❌ | - |
|
|
||||||
| favicon | emitter | ✅ | ❌ | - |
|
|
||||||
| og-image | emitter | ✅ | ❌ | - |
|
|
||||||
| cname | emitter | ✅ | ❌ | - |
|
|
||||||
| canvas-page | pageType | ✅ | ❌ | - |
|
|
||||||
| content-page | pageType | ✅ | ❌ | - |
|
|
||||||
| bases-page | pageType | ✅ | ❌ | - |
|
|
||||||
| folder-page | pageType | ✅ | ❌ | - |
|
|
||||||
| tag-page | pageType | ✅ | ❌ | - |
|
|
||||||
| explorer | emitter | ✅ | ✅ | left |
|
|
||||||
| graph | emitter | ✅ | ✅ | right |
|
|
||||||
| search | emitter | ✅ | ✅ | left |
|
|
||||||
| backlinks | emitter | ✅ | ✅ | right |
|
|
||||||
| article-title | emitter | ✅ | ✅ | beforeBody |
|
|
||||||
| content-meta | emitter | ✅ | ✅ | beforeBody |
|
|
||||||
| tag-list | emitter | ✅ | ✅ | beforeBody |
|
|
||||||
| page-title | emitter | ✅ | ✅ | left |
|
|
||||||
| darkmode | emitter | ✅ | ✅ | left |
|
|
||||||
| reader-mode | emitter | ✅ | ✅ | left |
|
|
||||||
| breadcrumbs | emitter | ✅ | ✅ | beforeBody |
|
|
||||||
| comments | emitter | ❌ | ✅ | afterBody |
|
|
||||||
| footer | emitter | ✅ | ✅ | footer |
|
|
||||||
| recent-notes | emitter | ❌ | ❌ | - |
|
|
||||||
| spacer | component | ✅ | ✅ | left |
|
|
||||||
Loading…
Reference in New Issue
Block a user