mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-31 00:34:05 -06:00
Merge branch 'jackyzha0:v4' into excalidraw-embed
This commit is contained in:
commit
c7aa0e49c1
@ -21,3 +21,7 @@ This will start a local web server to run your Quartz on your computer. Open a w
|
|||||||
> - `--serve`: run a local hot-reloading server to preview your Quartz
|
> - `--serve`: run a local hot-reloading server to preview your Quartz
|
||||||
> - `--port`: what port to run the local preview server on
|
> - `--port`: what port to run the local preview server on
|
||||||
> - `--concurrency`: how many threads to use to parse notes
|
> - `--concurrency`: how many threads to use to parse notes
|
||||||
|
|
||||||
|
> [!warning] Not to be used for production
|
||||||
|
> Serve mode is intended for local previews only.
|
||||||
|
> For production workloads, see the page on [[hosting]].
|
||||||
|
|||||||
28
docs/features/Roam Research compatibility.md
Normal file
28
docs/features/Roam Research compatibility.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: "Roam Research Compatibility"
|
||||||
|
tags:
|
||||||
|
- feature/transformer
|
||||||
|
---
|
||||||
|
|
||||||
|
[Roam Research](https://roamresearch.com) is a note-taking tool that organizes your knowledge graph in a unique and interconnected way.
|
||||||
|
|
||||||
|
Quartz supports transforming the special Markdown syntax from Roam Research (like `{{[[components]]}}` and other formatting) into
|
||||||
|
regular Markdown via the [[RoamFlavoredMarkdown]] plugin.
|
||||||
|
|
||||||
|
```typescript title="quartz.config.ts"
|
||||||
|
plugins: {
|
||||||
|
transformers: [
|
||||||
|
// ...
|
||||||
|
Plugin.RoamFlavoredMarkdown(),
|
||||||
|
Plugin.ObsidianFlavoredMarkdown(),
|
||||||
|
// ...
|
||||||
|
],
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!warning]
|
||||||
|
> As seen above placement of `Plugin.RoamFlavoredMarkdown()` within `quartz.config.ts` is very important. It must come before `Plugin.ObsidianFlavoredMarkdown()`.
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
This functionality is provided by the [[RoamFlavoredMarkdown]] plugin. See the plugin page for customization options.
|
||||||
26
docs/plugins/RoamFlavoredMarkdown.md
Normal file
26
docs/plugins/RoamFlavoredMarkdown.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: RoamFlavoredMarkdown
|
||||||
|
tags:
|
||||||
|
- plugin/transformer
|
||||||
|
---
|
||||||
|
|
||||||
|
This plugin provides support for [Roam Research](https://roamresearch.com) compatibility. See [[Roam Research Compatibility]] for more information.
|
||||||
|
|
||||||
|
> [!note]
|
||||||
|
> For information on how to add, remove or configure plugins, see the [[Configuration#Plugins|Configuration]] page.
|
||||||
|
|
||||||
|
This plugin accepts the following configuration options:
|
||||||
|
|
||||||
|
- `orComponent`: If `true` (default), converts Roam `{{ or:ONE|TWO|THREE }}` shortcodes into HTML Dropdown options.
|
||||||
|
- `TODOComponent`: If `true` (default), converts Roam `{{[[TODO]]}}` shortcodes into HTML check boxes.
|
||||||
|
- `DONEComponent`: If `true` (default), converts Roam `{{[[DONE]]}}` shortcodes into checked HTML check boxes.
|
||||||
|
- `videoComponent`: If `true` (default), converts Roam `{{[[video]]:URL}}` shortcodes into embeded HTML video.
|
||||||
|
- `audioComponent`: If `true` (default), converts Roam `{{[[audio]]:URL}}` shortcodes into embeded HTML audio.
|
||||||
|
- `pdfComponent`: If `true` (default), converts Roam `{{[[pdf]]:URL}}` shortcodes into embeded HTML PDF viewer.
|
||||||
|
- `blockquoteComponent`: If `true` (default), converts Roam `{{[[>]]}}` shortcodes into Quartz blockquotes.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
- Category: Transformer
|
||||||
|
- Function name: `Plugin.RoamFlavoredMarkdown()`.
|
||||||
|
- Source: [`quartz/plugins/transformers/roam.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz/plugins/transformers/roam.ts).
|
||||||
67
package-lock.json
generated
67
package-lock.json
generated
@ -23,7 +23,7 @@
|
|||||||
"github-slugger": "^2.0.0",
|
"github-slugger": "^2.0.0",
|
||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"hast-util-to-html": "^9.0.1",
|
"hast-util-to-html": "^9.0.2",
|
||||||
"hast-util-to-jsx-runtime": "^2.3.0",
|
"hast-util-to-jsx-runtime": "^2.3.0",
|
||||||
"hast-util-to-string": "^3.0.0",
|
"hast-util-to-string": "^3.0.0",
|
||||||
"is-absolute-url": "^4.0.1",
|
"is-absolute-url": "^4.0.1",
|
||||||
@ -33,9 +33,9 @@
|
|||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
"pixi.js": "^8.3.3",
|
"pixi.js": "^8.3.4",
|
||||||
"preact": "^10.23.2",
|
"preact": "^10.24.0",
|
||||||
"preact-render-to-string": "^6.5.9",
|
"preact-render-to-string": "^6.5.11",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pretty-time": "^1.1.0",
|
"pretty-time": "^1.1.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
@ -63,7 +63,7 @@
|
|||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"vfile": "^6.0.2",
|
"vfile": "^6.0.3",
|
||||||
"workerpool": "^9.1.3",
|
"workerpool": "^9.1.3",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
@ -76,15 +76,15 @@
|
|||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/hast": "^3.0.4",
|
"@types/hast": "^3.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^22.5.0",
|
"@types/node": "^22.5.5",
|
||||||
"@types/pretty-time": "^1.1.5",
|
"@types/pretty-time": "^1.1.5",
|
||||||
"@types/source-map-support": "^0.5.10",
|
"@types/source-map-support": "^0.5.10",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/ws": "^8.5.12",
|
||||||
"@types/yargs": "^17.0.33",
|
"@types/yargs": "^17.0.33",
|
||||||
"esbuild": "^0.19.9",
|
"esbuild": "^0.19.9",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"tsx": "^4.18.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.6.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "20 || >=22",
|
"node": "20 || >=22",
|
||||||
@ -1263,9 +1263,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.5.0",
|
"version": "22.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
|
||||||
"integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==",
|
"integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.19.2"
|
"undici-types": "~6.19.2"
|
||||||
@ -2831,15 +2831,14 @@
|
|||||||
"integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
|
"integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
|
||||||
},
|
},
|
||||||
"node_modules/hast-util-to-html": {
|
"node_modules/hast-util-to-html": {
|
||||||
"version": "9.0.1",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.2.tgz",
|
||||||
"integrity": "sha512-hZOofyZANbyWo+9RP75xIDV/gq+OUKx+T46IlwERnKmfpwp81XBFbT9mi26ws+SJchA4RVUQwIBJpqEOBhMzEQ==",
|
"integrity": "sha512-RP5wNpj5nm1Z8cloDv4Sl4RS8jH5HYa0v93YB6Wb4poEzgMo/dAAL0KcT4974dCjcNG5pkLqTImeFHHCwwfY3g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/hast": "^3.0.0",
|
"@types/hast": "^3.0.0",
|
||||||
"@types/unist": "^3.0.0",
|
"@types/unist": "^3.0.0",
|
||||||
"ccount": "^2.0.0",
|
"ccount": "^2.0.0",
|
||||||
"comma-separated-tokens": "^2.0.0",
|
"comma-separated-tokens": "^2.0.0",
|
||||||
"hast-util-raw": "^9.0.0",
|
|
||||||
"hast-util-whitespace": "^3.0.0",
|
"hast-util-whitespace": "^3.0.0",
|
||||||
"html-void-elements": "^3.0.0",
|
"html-void-elements": "^3.0.0",
|
||||||
"mdast-util-to-hast": "^13.0.0",
|
"mdast-util-to-hast": "^13.0.0",
|
||||||
@ -4840,10 +4839,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pixi.js": {
|
"node_modules/pixi.js": {
|
||||||
"version": "8.3.3",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.3.4.tgz",
|
||||||
"integrity": "sha512-dpucBKAqEm0K51MQKlXvyIJ40bcxniP82uz4ZPEQejGtPp0P+vueuG5DyArHCkC48mkVE2FEDvyYvBa45/JlQg==",
|
"integrity": "sha512-b5qdoHMQy79JjTiOOAH/fDiK9dLKGAoxfBwkHIdsK5XKNxsFuII2MBbktvR9pVaAmTDobDkMPDoIBFKYYpDeOg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pixi/colord": "^2.9.6",
|
"@pixi/colord": "^2.9.6",
|
||||||
"@types/css-font-loading-module": "^0.0.12",
|
"@types/css-font-loading-module": "^0.0.12",
|
||||||
@ -4857,18 +4855,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/preact": {
|
"node_modules/preact": {
|
||||||
"version": "10.23.2",
|
"version": "10.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.24.0.tgz",
|
||||||
"integrity": "sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==",
|
"integrity": "sha512-aK8Cf+jkfyuZ0ZZRG9FbYqwmEiGQ4y/PUO4SuTWoyWL244nZZh7bd5h2APd4rSNDYTBNghg1L+5iJN3Skxtbsw==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/preact"
|
"url": "https://opencollective.com/preact"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/preact-render-to-string": {
|
"node_modules/preact-render-to-string": {
|
||||||
"version": "6.5.9",
|
"version": "6.5.11",
|
||||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz",
|
||||||
"integrity": "sha512-Fn9R89h6qrQeSRmsH2O2fWzqpVwsJgEL9UTly5nGEV2ldhVuG+9JhXdNJ6zreIkOZcBT20+AOMwlG1x72znJ+g==",
|
"integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"preact": ">=10"
|
"preact": ">=10"
|
||||||
}
|
}
|
||||||
@ -5911,9 +5909,9 @@
|
|||||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.18.0",
|
"version": "4.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz",
|
||||||
"integrity": "sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==",
|
"integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.23.0",
|
"esbuild": "~0.23.0",
|
||||||
@ -6337,9 +6335,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.5.4",
|
"version": "5.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@ -6551,12 +6549,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vfile": {
|
"node_modules/vfile": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||||
"integrity": "sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==",
|
"integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/unist": "^3.0.0",
|
"@types/unist": "^3.0.0",
|
||||||
"unist-util-stringify-position": "^4.0.0",
|
|
||||||
"vfile-message": "^4.0.0"
|
"vfile-message": "^4.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
|
|||||||
16
package.json
16
package.json
@ -49,7 +49,7 @@
|
|||||||
"github-slugger": "^2.0.0",
|
"github-slugger": "^2.0.0",
|
||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"hast-util-to-html": "^9.0.1",
|
"hast-util-to-html": "^9.0.2",
|
||||||
"hast-util-to-jsx-runtime": "^2.3.0",
|
"hast-util-to-jsx-runtime": "^2.3.0",
|
||||||
"hast-util-to-string": "^3.0.0",
|
"hast-util-to-string": "^3.0.0",
|
||||||
"is-absolute-url": "^4.0.1",
|
"is-absolute-url": "^4.0.1",
|
||||||
@ -59,9 +59,9 @@
|
|||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
"pixi.js": "^8.3.3",
|
"pixi.js": "^8.3.4",
|
||||||
"preact": "^10.23.2",
|
"preact": "^10.24.0",
|
||||||
"preact-render-to-string": "^6.5.9",
|
"preact-render-to-string": "^6.5.11",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"pretty-time": "^1.1.0",
|
"pretty-time": "^1.1.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
@ -89,7 +89,7 @@
|
|||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"vfile": "^6.0.2",
|
"vfile": "^6.0.3",
|
||||||
"workerpool": "^9.1.3",
|
"workerpool": "^9.1.3",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
@ -99,14 +99,14 @@
|
|||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/hast": "^3.0.4",
|
"@types/hast": "^3.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^22.5.0",
|
"@types/node": "^22.5.5",
|
||||||
"@types/pretty-time": "^1.1.5",
|
"@types/pretty-time": "^1.1.5",
|
||||||
"@types/source-map-support": "^0.5.10",
|
"@types/source-map-support": "^0.5.10",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/ws": "^8.5.12",
|
||||||
"@types/yargs": "^17.0.33",
|
"@types/yargs": "^17.0.33",
|
||||||
"esbuild": "^0.19.9",
|
"esbuild": "^0.19.9",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"tsx": "^4.18.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ type BuildData = {
|
|||||||
type FileEvent = "add" | "change" | "delete"
|
type FileEvent = "add" | "change" | "delete"
|
||||||
|
|
||||||
function newBuildId() {
|
function newBuildId() {
|
||||||
return new Date().toISOString()
|
return Math.random().toString(36).substring(2, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
||||||
@ -162,17 +162,19 @@ async function partialRebuildFromEntrypoint(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildStart = new Date().getTime()
|
const buildId = newBuildId()
|
||||||
buildData.lastBuildMs = buildStart
|
ctx.buildId = buildId
|
||||||
|
buildData.lastBuildMs = new Date().getTime()
|
||||||
const release = await mut.acquire()
|
const release = await mut.acquire()
|
||||||
if (buildData.lastBuildMs > buildStart) {
|
|
||||||
|
// if there's another build after us, release and let them do it
|
||||||
|
if (ctx.buildId !== buildId) {
|
||||||
release()
|
release()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const perf = new PerfTimer()
|
const perf = new PerfTimer()
|
||||||
console.log(chalk.yellow("Detected change, rebuilding..."))
|
console.log(chalk.yellow("Detected change, rebuilding..."))
|
||||||
ctx.buildId = newBuildId()
|
|
||||||
|
|
||||||
// UPDATE DEP GRAPH
|
// UPDATE DEP GRAPH
|
||||||
const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath
|
const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath
|
||||||
@ -357,19 +359,19 @@ async function rebuildFromEntrypoint(
|
|||||||
toRemove.add(filePath)
|
toRemove.add(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildStart = new Date().getTime()
|
const buildId = newBuildId()
|
||||||
buildData.lastBuildMs = buildStart
|
ctx.buildId = buildId
|
||||||
|
buildData.lastBuildMs = new Date().getTime()
|
||||||
const release = await mut.acquire()
|
const release = await mut.acquire()
|
||||||
|
|
||||||
// there's another build after us, release and let them do it
|
// there's another build after us, release and let them do it
|
||||||
if (buildData.lastBuildMs > buildStart) {
|
if (ctx.buildId !== buildId) {
|
||||||
release()
|
release()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const perf = new PerfTimer()
|
const perf = new PerfTimer()
|
||||||
console.log(chalk.yellow("Detected change, rebuilding..."))
|
console.log(chalk.yellow("Detected change, rebuilding..."))
|
||||||
ctx.buildId = newBuildId()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
|
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
|
||||||
@ -405,10 +407,10 @@ async function rebuildFromEntrypoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
release()
|
|
||||||
clientRefresh()
|
clientRefresh()
|
||||||
toRebuild.clear()
|
toRebuild.clear()
|
||||||
toRemove.clear()
|
toRemove.clear()
|
||||||
|
release()
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async (argv: Argv, mut: Mutex, clientRefresh: () => void) => {
|
export default async (argv: Argv, mut: Mutex, clientRefresh: () => void) => {
|
||||||
|
|||||||
@ -147,7 +147,7 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso
|
|||||||
} else if (cfg.analytics?.provider === "cabin") {
|
} else if (cfg.analytics?.provider === "cabin") {
|
||||||
componentResources.afterDOMLoaded.push(`
|
componentResources.afterDOMLoaded.push(`
|
||||||
const cabinScript = document.createElement("script")
|
const cabinScript = document.createElement("script")
|
||||||
cabinScript.src = "${cfg.analytics.host ?? "https://scripts.cabin.dev"}/cabin.js"
|
cabinScript.src = "${cfg.analytics.host ?? "https://scripts.withcabin.com"}/hello.js"
|
||||||
cabinScript.defer = true
|
cabinScript.defer = true
|
||||||
cabinScript.async = true
|
cabinScript.async = true
|
||||||
document.head.appendChild(cabinScript)
|
document.head.appendChild(cabinScript)
|
||||||
|
|||||||
@ -10,3 +10,4 @@ export { OxHugoFlavouredMarkdown } from "./oxhugofm"
|
|||||||
export { SyntaxHighlighting } from "./syntax"
|
export { SyntaxHighlighting } from "./syntax"
|
||||||
export { TableOfContents } from "./toc"
|
export { TableOfContents } from "./toc"
|
||||||
export { HardLineBreaks } from "./linebreaks"
|
export { HardLineBreaks } from "./linebreaks"
|
||||||
|
export { RoamFlavoredMarkdown } from "./roam"
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
|
|||||||
properties: {
|
properties: {
|
||||||
"aria-hidden": "true",
|
"aria-hidden": "true",
|
||||||
class: "external-icon",
|
class: "external-icon",
|
||||||
|
style: "max-width:0.8em;max-height:0.8em",
|
||||||
viewBox: "0 0 512 512",
|
viewBox: "0 0 512 512",
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -329,8 +329,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
replacements.push([
|
replacements.push([
|
||||||
tagRegex,
|
tagRegex,
|
||||||
(_value: string, tag: string) => {
|
(_value: string, tag: string) => {
|
||||||
// Check if the tag only includes numbers
|
// Check if the tag only includes numbers and slashes
|
||||||
if (/^\d+$/.test(tag)) {
|
if (/^[\/\d]+$/.test(tag)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
224
quartz/plugins/transformers/roam.ts
Normal file
224
quartz/plugins/transformers/roam.ts
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
|
import { PluggableList } from "unified"
|
||||||
|
import { SKIP, visit } from "unist-util-visit"
|
||||||
|
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||||
|
import { Root, Html, Paragraph, Text, Link, Parent } from "mdast"
|
||||||
|
import { Node } from "unist"
|
||||||
|
import { VFile } from "vfile"
|
||||||
|
import { BuildVisitor } from "unist-util-visit"
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
orComponent: boolean
|
||||||
|
TODOComponent: boolean
|
||||||
|
DONEComponent: boolean
|
||||||
|
videoComponent: boolean
|
||||||
|
audioComponent: boolean
|
||||||
|
pdfComponent: boolean
|
||||||
|
blockquoteComponent: boolean
|
||||||
|
tableComponent: boolean
|
||||||
|
attributeComponent: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOptions: Options = {
|
||||||
|
orComponent: true,
|
||||||
|
TODOComponent: true,
|
||||||
|
DONEComponent: true,
|
||||||
|
videoComponent: true,
|
||||||
|
audioComponent: true,
|
||||||
|
pdfComponent: true,
|
||||||
|
blockquoteComponent: true,
|
||||||
|
tableComponent: true,
|
||||||
|
attributeComponent: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const orRegex = new RegExp(/{{or:(.*?)}}/, "g")
|
||||||
|
const TODORegex = new RegExp(/{{.*?\bTODO\b.*?}}/, "g")
|
||||||
|
const DONERegex = new RegExp(/{{.*?\bDONE\b.*?}}/, "g")
|
||||||
|
const videoRegex = new RegExp(/{{.*?\[\[video\]\].*?\:(.*?)}}/, "g")
|
||||||
|
const youtubeRegex = new RegExp(
|
||||||
|
/{{.*?\[\[video\]\].*?(https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?)}}/,
|
||||||
|
"g",
|
||||||
|
)
|
||||||
|
|
||||||
|
// const multimediaRegex = new RegExp(/{{.*?\b(video|audio)\b.*?\:(.*?)}}/, "g")
|
||||||
|
|
||||||
|
const audioRegex = new RegExp(/{{.*?\[\[audio\]\].*?\:(.*?)}}/, "g")
|
||||||
|
const pdfRegex = new RegExp(/{{.*?\[\[pdf\]\].*?\:(.*?)}}/, "g")
|
||||||
|
const blockquoteRegex = new RegExp(/(\[\[>\]\])\s*(.*)/, "g")
|
||||||
|
const roamHighlightRegex = new RegExp(/\^\^(.+)\^\^/, "g")
|
||||||
|
const roamItalicRegex = new RegExp(/__(.+)__/, "g")
|
||||||
|
const tableRegex = new RegExp(/- {{.*?\btable\b.*?}}/, "g") /* TODO */
|
||||||
|
const attributeRegex = new RegExp(/\b\w+(?:\s+\w+)*::/, "g") /* TODO */
|
||||||
|
|
||||||
|
function isSpecialEmbed(node: Paragraph): boolean {
|
||||||
|
if (node.children.length !== 2) return false
|
||||||
|
|
||||||
|
const [textNode, linkNode] = node.children
|
||||||
|
return (
|
||||||
|
textNode.type === "text" &&
|
||||||
|
textNode.value.startsWith("{{[[") &&
|
||||||
|
linkNode.type === "link" &&
|
||||||
|
linkNode.children[0].type === "text" &&
|
||||||
|
linkNode.children[0].value.endsWith("}}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformSpecialEmbed(node: Paragraph, opts: Options): Html | null {
|
||||||
|
const [textNode, linkNode] = node.children as [Text, Link]
|
||||||
|
const embedType = textNode.value.match(/\{\{\[\[(.*?)\]\]:/)?.[1]?.toLowerCase()
|
||||||
|
const url = linkNode.url.slice(0, -2) // Remove the trailing '}}'
|
||||||
|
|
||||||
|
switch (embedType) {
|
||||||
|
case "audio":
|
||||||
|
return opts.audioComponent
|
||||||
|
? {
|
||||||
|
type: "html",
|
||||||
|
value: `<audio controls>
|
||||||
|
<source src="${url}" type="audio/mpeg">
|
||||||
|
<source src="${url}" type="audio/ogg">
|
||||||
|
Your browser does not support the audio tag.
|
||||||
|
</audio>`,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
case "video":
|
||||||
|
if (!opts.videoComponent) return null
|
||||||
|
// Check if it's a YouTube video
|
||||||
|
const youtubeMatch = url.match(
|
||||||
|
/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)/,
|
||||||
|
)
|
||||||
|
if (youtubeMatch) {
|
||||||
|
const videoId = youtubeMatch[1].split("&")[0] // Remove additional parameters
|
||||||
|
const playlistMatch = url.match(/[?&]list=([^#\&\?]*)/)
|
||||||
|
const playlistId = playlistMatch ? playlistMatch[1] : null
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "html",
|
||||||
|
value: `<iframe
|
||||||
|
class="external-embed youtube"
|
||||||
|
width="600px"
|
||||||
|
height="350px"
|
||||||
|
src="https://www.youtube.com/embed/${videoId}${playlistId ? `?list=${playlistId}` : ""}"
|
||||||
|
frameborder="0"
|
||||||
|
allow="fullscreen"
|
||||||
|
></iframe>`,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: "html",
|
||||||
|
value: `<video controls>
|
||||||
|
<source src="${url}" type="video/mp4">
|
||||||
|
<source src="${url}" type="video/webm">
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "pdf":
|
||||||
|
return opts.pdfComponent
|
||||||
|
? {
|
||||||
|
type: "html",
|
||||||
|
value: `<embed src="${url}" type="application/pdf" width="100%" height="600px" />`,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RoamFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (
|
||||||
|
userOpts,
|
||||||
|
) => {
|
||||||
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "RoamFlavoredMarkdown",
|
||||||
|
markdownPlugins() {
|
||||||
|
const plugins: PluggableList = []
|
||||||
|
|
||||||
|
plugins.push(() => {
|
||||||
|
return (tree: Root, file: VFile) => {
|
||||||
|
const replacements: [RegExp, ReplaceFunction][] = []
|
||||||
|
|
||||||
|
// Handle special embeds (audio, video, PDF)
|
||||||
|
if (opts.audioComponent || opts.videoComponent || opts.pdfComponent) {
|
||||||
|
visit(tree, "paragraph", ((node: Paragraph, index: number, parent: Parent | null) => {
|
||||||
|
if (isSpecialEmbed(node)) {
|
||||||
|
const transformedNode = transformSpecialEmbed(node, opts)
|
||||||
|
if (transformedNode && parent) {
|
||||||
|
parent.children[index] = transformedNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as BuildVisitor<Root, "paragraph">)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roam italic syntax
|
||||||
|
replacements.push([
|
||||||
|
roamItalicRegex,
|
||||||
|
(_value: string, match: string) => ({
|
||||||
|
type: "emphasis",
|
||||||
|
children: [{ type: "text", value: match }],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Roam highlight syntax
|
||||||
|
replacements.push([
|
||||||
|
roamHighlightRegex,
|
||||||
|
(_value: string, inner: string) => ({
|
||||||
|
type: "html",
|
||||||
|
value: `<span class="text-highlight">${inner}</span>`,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (opts.orComponent) {
|
||||||
|
replacements.push([
|
||||||
|
orRegex,
|
||||||
|
(match: string) => {
|
||||||
|
const matchResult = match.match(/{{or:(.*?)}}/)
|
||||||
|
if (matchResult === null) {
|
||||||
|
return { type: "html", value: "" }
|
||||||
|
}
|
||||||
|
const optionsString: string = matchResult[1]
|
||||||
|
const options: string[] = optionsString.split("|")
|
||||||
|
const selectHtml: string = `<select>${options.map((option: string) => `<option value="${option}">${option}</option>`).join("")}</select>`
|
||||||
|
return { type: "html", value: selectHtml }
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.TODOComponent) {
|
||||||
|
replacements.push([
|
||||||
|
TODORegex,
|
||||||
|
() => ({
|
||||||
|
type: "html",
|
||||||
|
value: `<input type="checkbox" disabled>`,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.DONEComponent) {
|
||||||
|
replacements.push([
|
||||||
|
DONERegex,
|
||||||
|
() => ({
|
||||||
|
type: "html",
|
||||||
|
value: `<input type="checkbox" checked disabled>`,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.blockquoteComponent) {
|
||||||
|
replacements.push([
|
||||||
|
blockquoteRegex,
|
||||||
|
(_match: string, _marker: string, content: string) => ({
|
||||||
|
type: "html",
|
||||||
|
value: `<blockquote>${content.trim()}</blockquote>`,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
mdastFindReplace(tree, replacements)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return plugins
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user