mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-27 23:04:05 -06:00
merge: branch 'v4' of github.com:jackyzha0/quartz into open-graph
This commit is contained in:
commit
04230a8970
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@ -9,3 +9,12 @@ updates:
|
|||||||
applies-to: "version-updates"
|
applies-to: "version-updates"
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- "*"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
groups:
|
||||||
|
ci-dependencies:
|
||||||
|
applies-to: "version-updates"
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
|||||||
2
.github/workflows/docker-build-push.yaml
vendored
2
.github/workflows/docker-build-push.yaml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
- name: Inject slug/short variables
|
- name: Inject slug/short variables
|
||||||
uses: rlespinasse/github-slug-action@v4.4.1
|
uses: rlespinasse/github-slug-action@v5.0.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
|
|||||||
@ -27,7 +27,7 @@ The following sections will go into detail for what methods can be implemented f
|
|||||||
- `cfg`: The full Quartz [[configuration]]
|
- `cfg`: The full Quartz [[configuration]]
|
||||||
- `allSlugs`: a list of all the valid content slugs (see [[paths]] for more information on what a `ServerSlug` is)
|
- `allSlugs`: a list of all the valid content slugs (see [[paths]] for more information on what a `ServerSlug` is)
|
||||||
- `StaticResources` is defined in `quartz/resources.tsx`. It consists of
|
- `StaticResources` is defined in `quartz/resources.tsx`. It consists of
|
||||||
- `css`: a list of URLs for stylesheets that should be loaded
|
- `css`: a list of CSS style definitions that should be loaded. A CSS style is described with the `CSSResource` type which is also defined in `quartz/resources.tsx`. It accepts either a source URL or the inline content of the stylesheet.
|
||||||
- `js`: a list of scripts that should be loaded. A script is described with the `JSResource` type which is also defined in `quartz/resources.tsx`. It allows you to define a load time (either before or after the DOM has been loaded), whether it should be a module, and either the source URL or the inline content of the script.
|
- `js`: a list of scripts that should be loaded. A script is described with the `JSResource` type which is also defined in `quartz/resources.tsx`. It allows you to define a load time (either before or after the DOM has been loaded), whether it should be a module, and either the source URL or the inline content of the script.
|
||||||
|
|
||||||
## Transformers
|
## Transformers
|
||||||
@ -85,8 +85,10 @@ export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => {
|
|||||||
if (engine === "katex") {
|
if (engine === "katex") {
|
||||||
return {
|
return {
|
||||||
css: [
|
css: [
|
||||||
// base css
|
{
|
||||||
"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",
|
// base css
|
||||||
|
content: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
js: [
|
js: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -114,3 +114,14 @@ afterBody: [
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Conditionally display comments
|
||||||
|
|
||||||
|
Quartz can conditionally display the comment box based on a field `comments` in the frontmatter. By default, all pages will display comments, to disable it for a specific page, set `comments` to `false`.
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
title: Comments disabled here!
|
||||||
|
comments: false
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|||||||
@ -11,9 +11,13 @@ This plugin adds LaTeX support to Quartz. See [[features/Latex|Latex]] for more
|
|||||||
|
|
||||||
This plugin accepts the following configuration options:
|
This plugin accepts the following configuration options:
|
||||||
|
|
||||||
- `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/) or `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html). Defaults to KaTeX.
|
- `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/), `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html), or `"typst"` for [Typst](https://typst.app/) (a new way to compose LaTeX equation). Defaults to KaTeX.
|
||||||
- `customMacros`: custom macros for all LaTeX blocks. It takes the form of a key-value pair where the key is a new command name and the value is the expansion of the macro. For example: `{"\\R": "\\mathbb{R}"}`
|
- `customMacros`: custom macros for all LaTeX blocks. It takes the form of a key-value pair where the key is a new command name and the value is the expansion of the macro. For example: `{"\\R": "\\mathbb{R}"}`
|
||||||
|
|
||||||
|
> [!note] Typst support
|
||||||
|
>
|
||||||
|
> Currently, typst doesn't support inline-math
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
- Category: Transformer
|
- Category: Transformer
|
||||||
|
|||||||
1113
package-lock.json
generated
1113
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -36,7 +36,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^0.7.0",
|
"@clack/prompts": "^0.7.0",
|
||||||
"@floating-ui/dom": "^1.6.11",
|
"@floating-ui/dom": "^1.6.12",
|
||||||
|
"@myriaddreamin/rehype-typst": "^0.5.0-rc7",
|
||||||
"@napi-rs/simple-git": "0.1.19",
|
"@napi-rs/simple-git": "0.1.19",
|
||||||
"@tweenjs/tween.js": "^25.0.0",
|
"@tweenjs/tween.js": "^25.0.0",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
@ -54,10 +55,11 @@
|
|||||||
"hast-util-to-string": "^3.0.1",
|
"hast-util-to-string": "^3.0.1",
|
||||||
"is-absolute-url": "^4.0.1",
|
"is-absolute-url": "^4.0.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lightningcss": "^1.27.0",
|
"lightningcss": "^1.28.1",
|
||||||
"mdast-util-find-and-replace": "^3.0.1",
|
"mdast-util-find-and-replace": "^3.0.1",
|
||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
|
"mermaid": "^11.4.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
"pixi.js": "^8.5.2",
|
"pixi.js": "^8.5.2",
|
||||||
"preact": "^10.24.3",
|
"preact": "^10.24.3",
|
||||||
@ -66,7 +68,7 @@
|
|||||||
"pretty-time": "^1.1.0",
|
"pretty-time": "^1.1.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"rehype-autolink-headings": "^7.1.0",
|
"rehype-autolink-headings": "^7.1.0",
|
||||||
"rehype-citation": "^2.2.1",
|
"rehype-citation": "^2.2.2",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
"rehype-mathjax": "^6.0.0",
|
"rehype-mathjax": "^6.0.0",
|
||||||
"rehype-pretty-code": "^0.14.0",
|
"rehype-pretty-code": "^0.14.0",
|
||||||
@ -101,10 +103,10 @@
|
|||||||
"@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.8.1",
|
"@types/node": "^22.9.0",
|
||||||
"@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.13",
|
||||||
"@types/yargs": "^17.0.33",
|
"@types/yargs": "^17.0.33",
|
||||||
"esbuild": "^0.24.0",
|
"esbuild": "^0.24.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { WebSocketServer } from "ws"
|
|||||||
import { randomUUID } from "crypto"
|
import { randomUUID } from "crypto"
|
||||||
import { Mutex } from "async-mutex"
|
import { Mutex } from "async-mutex"
|
||||||
import { CreateArgv } from "./args.js"
|
import { CreateArgv } from "./args.js"
|
||||||
|
import { globby } from "globby"
|
||||||
import {
|
import {
|
||||||
exitIfCancel,
|
exitIfCancel,
|
||||||
escapePath,
|
escapePath,
|
||||||
@ -44,7 +45,7 @@ export async function handleCreate(argv) {
|
|||||||
let linkResolutionStrategy = argv.links?.toLowerCase()
|
let linkResolutionStrategy = argv.links?.toLowerCase()
|
||||||
const sourceDirectory = argv.source
|
const sourceDirectory = argv.source
|
||||||
|
|
||||||
// If all cmd arguments were provided, check if theyre valid
|
// If all cmd arguments were provided, check if they're valid
|
||||||
if (setupStrategy && linkResolutionStrategy) {
|
if (setupStrategy && linkResolutionStrategy) {
|
||||||
// If setup isn't, "new", source argument is required
|
// If setup isn't, "new", source argument is required
|
||||||
if (setupStrategy !== "new") {
|
if (setupStrategy !== "new") {
|
||||||
@ -236,6 +237,11 @@ export async function handleBuild(argv) {
|
|||||||
type: "css-text",
|
type: "css-text",
|
||||||
cssImports: true,
|
cssImports: true,
|
||||||
}),
|
}),
|
||||||
|
sassPlugin({
|
||||||
|
filter: /\.inline\.scss$/,
|
||||||
|
type: "css",
|
||||||
|
cssImports: true,
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
name: "inline-script-loader",
|
name: "inline-script-loader",
|
||||||
setup(build) {
|
setup(build) {
|
||||||
@ -285,8 +291,8 @@ export async function handleBuild(argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cleanupBuild) {
|
if (cleanupBuild) {
|
||||||
await cleanupBuild()
|
|
||||||
console.log(chalk.yellow("Detected a source code change, doing a hard rebuild..."))
|
console.log(chalk.yellow("Detected a source code change, doing a hard rebuild..."))
|
||||||
|
await cleanupBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await ctx.rebuild().catch((err) => {
|
const result = await ctx.rebuild().catch((err) => {
|
||||||
@ -427,13 +433,12 @@ export async function handleBuild(argv) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
console.log("hint: exit with ctrl+c")
|
console.log("hint: exit with ctrl+c")
|
||||||
|
const paths = await globby(["**/*.ts", "**/*.tsx", "**/*.scss", "package.json"])
|
||||||
chokidar
|
chokidar
|
||||||
.watch(["**/*.ts", "**/*.tsx", "**/*.scss", "package.json"], {
|
.watch(paths, { ignoreInitial: true })
|
||||||
ignoreInitial: true,
|
.on("add", () => build(clientRefresh))
|
||||||
})
|
.on("change", () => build(clientRefresh))
|
||||||
.on("all", async () => {
|
.on("unlink", () => build(clientRefresh))
|
||||||
build(clientRefresh)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
await build(() => {})
|
await build(() => {})
|
||||||
ctx.dispose()
|
ctx.dispose()
|
||||||
|
|||||||
@ -25,7 +25,14 @@ function boolToStringBool(b: boolean): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ((opts: Options) => {
|
export default ((opts: Options) => {
|
||||||
const Comments: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
const Comments: QuartzComponent = ({ displayClass, fileData, cfg }: QuartzComponentProps) => {
|
||||||
|
// check if comments should be displayed according to frontmatter
|
||||||
|
const commentsFlag: boolean =
|
||||||
|
fileData.frontmatter?.comments === true || fileData.frontmatter?.comments === "true"
|
||||||
|
if (!commentsFlag) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={classNames(displayClass, "giscus")}
|
class={classNames(displayClass, "giscus")}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { i18n } from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import { FullSlug, joinSegments, pathToRoot } from "../util/path"
|
import { FullSlug, joinSegments, pathToRoot } from "../util/path"
|
||||||
import { JSResourceToScriptElement } from "../util/resources"
|
import { CSSResourceToStyleElement, JSResourceToScriptElement } from "../util/resources"
|
||||||
import { googleFontHref } from "../util/theme"
|
import { googleFontHref } from "../util/theme"
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
import satori, { SatoriOptions } from "satori"
|
import satori, { SatoriOptions } from "satori"
|
||||||
@ -198,9 +198,7 @@ export default (() => {
|
|||||||
<link rel="icon" href={iconPath} />
|
<link rel="icon" href={iconPath} />
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<meta name="generator" content="Quartz" />
|
<meta name="generator" content="Quartz" />
|
||||||
{css.map((href) => (
|
{css.map((resource) => CSSResourceToStyleElement(resource, true))}
|
||||||
<link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />
|
|
||||||
))}
|
|
||||||
{js
|
{js
|
||||||
.filter((resource) => resource.loadTime === "beforeDOMReady")
|
.filter((resource) => resource.loadTime === "beforeDOMReady")
|
||||||
.map((res) => JSResourceToScriptElement(res, true))}
|
.map((res) => JSResourceToScriptElement(res, true))}
|
||||||
|
|||||||
@ -33,7 +33,6 @@ TagList.css = `
|
|||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-self: end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-li > .section > .tags {
|
.section-li > .section > .tags {
|
||||||
|
|||||||
@ -29,7 +29,12 @@ export function pageResources(
|
|||||||
const contentIndexScript = `const fetchData = fetch("${contentIndexPath}").then(data => data.json())`
|
const contentIndexScript = `const fetchData = fetch("${contentIndexPath}").then(data => data.json())`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
css: [joinSegments(baseDir, "index.css"), ...staticResources.css],
|
css: [
|
||||||
|
{
|
||||||
|
content: joinSegments(baseDir, "index.css"),
|
||||||
|
},
|
||||||
|
...staticResources.css,
|
||||||
|
],
|
||||||
js: [
|
js: [
|
||||||
{
|
{
|
||||||
src: joinSegments(baseDir, "prescript.js"),
|
src: joinSegments(baseDir, "prescript.js"),
|
||||||
|
|||||||
@ -8,7 +8,9 @@ document.addEventListener("nav", () => {
|
|||||||
for (let i = 0; i < els.length; i++) {
|
for (let i = 0; i < els.length; i++) {
|
||||||
const codeBlock = els[i].getElementsByTagName("code")[0]
|
const codeBlock = els[i].getElementsByTagName("code")[0]
|
||||||
if (codeBlock) {
|
if (codeBlock) {
|
||||||
const source = codeBlock.innerText.replace(/\n\n/g, "\n")
|
const source = (
|
||||||
|
codeBlock.dataset.clipboard ? JSON.parse(codeBlock.dataset.clipboard) : codeBlock.innerText
|
||||||
|
).replace(/\n\n/g, "\n")
|
||||||
const button = document.createElement("button")
|
const button = document.createElement("button")
|
||||||
button.className = "clipboard-button"
|
button.className = "clipboard-button"
|
||||||
button.type = "button"
|
button.type = "button"
|
||||||
|
|||||||
@ -580,7 +580,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
function hideGlobalGraph() {
|
function hideGlobalGraph() {
|
||||||
container?.classList.remove("active")
|
container?.classList.remove("active")
|
||||||
if (sidebar) {
|
if (sidebar) {
|
||||||
sidebar.style.zIndex = "unset"
|
sidebar.style.zIndex = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
242
quartz/components/scripts/mermaid.inline.ts
Normal file
242
quartz/components/scripts/mermaid.inline.ts
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
import { removeAllChildren } from "./util"
|
||||||
|
import mermaid from "mermaid"
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
class DiagramPanZoom {
|
||||||
|
private isDragging = false
|
||||||
|
private startPan: Position = { x: 0, y: 0 }
|
||||||
|
private currentPan: Position = { x: 0, y: 0 }
|
||||||
|
private scale = 1
|
||||||
|
private readonly MIN_SCALE = 0.5
|
||||||
|
private readonly MAX_SCALE = 3
|
||||||
|
private readonly ZOOM_SENSITIVITY = 0.001
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private container: HTMLElement,
|
||||||
|
private content: HTMLElement,
|
||||||
|
) {
|
||||||
|
this.setupEventListeners()
|
||||||
|
this.setupNavigationControls()
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupEventListeners() {
|
||||||
|
// Mouse drag events
|
||||||
|
this.container.addEventListener("mousedown", this.onMouseDown.bind(this))
|
||||||
|
document.addEventListener("mousemove", this.onMouseMove.bind(this))
|
||||||
|
document.addEventListener("mouseup", this.onMouseUp.bind(this))
|
||||||
|
|
||||||
|
// Wheel zoom events
|
||||||
|
this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false })
|
||||||
|
|
||||||
|
// Reset on window resize
|
||||||
|
window.addEventListener("resize", this.resetTransform.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupNavigationControls() {
|
||||||
|
const controls = document.createElement("div")
|
||||||
|
controls.className = "mermaid-controls"
|
||||||
|
|
||||||
|
// Zoom controls
|
||||||
|
const zoomIn = this.createButton("+", () => this.zoom(0.1))
|
||||||
|
const zoomOut = this.createButton("-", () => this.zoom(-0.1))
|
||||||
|
const resetBtn = this.createButton("Reset", () => this.resetTransform())
|
||||||
|
|
||||||
|
controls.appendChild(zoomOut)
|
||||||
|
controls.appendChild(resetBtn)
|
||||||
|
controls.appendChild(zoomIn)
|
||||||
|
|
||||||
|
this.container.appendChild(controls)
|
||||||
|
}
|
||||||
|
|
||||||
|
private createButton(text: string, onClick: () => void): HTMLButtonElement {
|
||||||
|
const button = document.createElement("button")
|
||||||
|
button.textContent = text
|
||||||
|
button.className = "mermaid-control-button"
|
||||||
|
button.addEventListener("click", onClick)
|
||||||
|
window.addCleanup(() => button.removeEventListener("click", onClick))
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMouseDown(e: MouseEvent) {
|
||||||
|
if (e.button !== 0) return // Only handle left click
|
||||||
|
this.isDragging = true
|
||||||
|
this.startPan = { x: e.clientX - this.currentPan.x, y: e.clientY - this.currentPan.y }
|
||||||
|
this.container.style.cursor = "grabbing"
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMouseMove(e: MouseEvent) {
|
||||||
|
if (!this.isDragging) return
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
this.currentPan = {
|
||||||
|
x: e.clientX - this.startPan.x,
|
||||||
|
y: e.clientY - this.startPan.y,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTransform()
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMouseUp() {
|
||||||
|
this.isDragging = false
|
||||||
|
this.container.style.cursor = "grab"
|
||||||
|
}
|
||||||
|
|
||||||
|
private onWheel(e: WheelEvent) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const delta = -e.deltaY * this.ZOOM_SENSITIVITY
|
||||||
|
const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE)
|
||||||
|
|
||||||
|
// Calculate mouse position relative to content
|
||||||
|
const rect = this.content.getBoundingClientRect()
|
||||||
|
const mouseX = e.clientX - rect.left
|
||||||
|
const mouseY = e.clientY - rect.top
|
||||||
|
|
||||||
|
// Adjust pan to zoom around mouse position
|
||||||
|
const scaleDiff = newScale - this.scale
|
||||||
|
this.currentPan.x -= mouseX * scaleDiff
|
||||||
|
this.currentPan.y -= mouseY * scaleDiff
|
||||||
|
|
||||||
|
this.scale = newScale
|
||||||
|
this.updateTransform()
|
||||||
|
}
|
||||||
|
|
||||||
|
private zoom(delta: number) {
|
||||||
|
const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE)
|
||||||
|
|
||||||
|
// Zoom around center
|
||||||
|
const rect = this.content.getBoundingClientRect()
|
||||||
|
const centerX = rect.width / 2
|
||||||
|
const centerY = rect.height / 2
|
||||||
|
|
||||||
|
const scaleDiff = newScale - this.scale
|
||||||
|
this.currentPan.x -= centerX * scaleDiff
|
||||||
|
this.currentPan.y -= centerY * scaleDiff
|
||||||
|
|
||||||
|
this.scale = newScale
|
||||||
|
this.updateTransform()
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateTransform() {
|
||||||
|
this.content.style.transform = `translate(${this.currentPan.x}px, ${this.currentPan.y}px) scale(${this.scale})`
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetTransform() {
|
||||||
|
this.scale = 1
|
||||||
|
this.currentPan = { x: 0, y: 0 }
|
||||||
|
this.updateTransform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssVars = [
|
||||||
|
"--secondary",
|
||||||
|
"--tertiary",
|
||||||
|
"--gray",
|
||||||
|
"--light",
|
||||||
|
"--lightgray",
|
||||||
|
"--highlight",
|
||||||
|
"--dark",
|
||||||
|
"--darkgray",
|
||||||
|
"--codeFont",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
document.addEventListener("nav", async () => {
|
||||||
|
const center = document.querySelector(".center") as HTMLElement
|
||||||
|
const nodes = center.querySelectorAll("code.mermaid") as NodeListOf<HTMLElement>
|
||||||
|
if (nodes.length === 0) return
|
||||||
|
|
||||||
|
const computedStyleMap = cssVars.reduce(
|
||||||
|
(acc, key) => {
|
||||||
|
acc[key] = getComputedStyle(document.documentElement).getPropertyValue(key)
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<(typeof cssVars)[number], string>,
|
||||||
|
)
|
||||||
|
|
||||||
|
const darkMode = document.documentElement.getAttribute("saved-theme") === "dark"
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
securityLevel: "loose",
|
||||||
|
theme: darkMode ? "dark" : "base",
|
||||||
|
themeVariables: {
|
||||||
|
fontFamily: computedStyleMap["--codeFont"],
|
||||||
|
primaryColor: computedStyleMap["--light"],
|
||||||
|
primaryTextColor: computedStyleMap["--darkgray"],
|
||||||
|
primaryBorderColor: computedStyleMap["--tertiary"],
|
||||||
|
lineColor: computedStyleMap["--darkgray"],
|
||||||
|
secondaryColor: computedStyleMap["--secondary"],
|
||||||
|
tertiaryColor: computedStyleMap["--tertiary"],
|
||||||
|
clusterBkg: computedStyleMap["--light"],
|
||||||
|
edgeLabelBackground: computedStyleMap["--highlight"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await mermaid.run({ nodes })
|
||||||
|
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const codeBlock = nodes[i] as HTMLElement
|
||||||
|
const pre = codeBlock.parentElement as HTMLPreElement
|
||||||
|
const clipboardBtn = pre.querySelector(".clipboard-button") as HTMLButtonElement
|
||||||
|
const expandBtn = pre.querySelector(".expand-button") as HTMLButtonElement
|
||||||
|
|
||||||
|
const clipboardStyle = window.getComputedStyle(clipboardBtn)
|
||||||
|
const clipboardWidth =
|
||||||
|
clipboardBtn.offsetWidth +
|
||||||
|
parseFloat(clipboardStyle.marginLeft || "0") +
|
||||||
|
parseFloat(clipboardStyle.marginRight || "0")
|
||||||
|
|
||||||
|
// Set expand button position
|
||||||
|
expandBtn.style.right = `calc(${clipboardWidth}px + 0.3rem)`
|
||||||
|
pre.prepend(expandBtn)
|
||||||
|
|
||||||
|
// query popup container
|
||||||
|
const popupContainer = pre.querySelector("#mermaid-container") as HTMLElement
|
||||||
|
if (!popupContainer) return
|
||||||
|
|
||||||
|
let panZoom: DiagramPanZoom | null = null
|
||||||
|
|
||||||
|
function showMermaid() {
|
||||||
|
const container = popupContainer.querySelector("#mermaid-space") as HTMLElement
|
||||||
|
const content = popupContainer.querySelector(".mermaid-content") as HTMLElement
|
||||||
|
if (!content) return
|
||||||
|
removeAllChildren(content)
|
||||||
|
|
||||||
|
// Clone the mermaid content
|
||||||
|
const mermaidContent = codeBlock.querySelector("svg")!.cloneNode(true) as SVGElement
|
||||||
|
content.appendChild(mermaidContent)
|
||||||
|
|
||||||
|
// Show container
|
||||||
|
popupContainer.classList.add("active")
|
||||||
|
container.style.cursor = "grab"
|
||||||
|
|
||||||
|
// Initialize pan-zoom after showing the popup
|
||||||
|
panZoom = new DiagramPanZoom(container, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideMermaid() {
|
||||||
|
popupContainer.classList.remove("active")
|
||||||
|
panZoom = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEscape(e: any) {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
hideMermaid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeBtn = popupContainer.querySelector(".close-button") as HTMLButtonElement
|
||||||
|
|
||||||
|
closeBtn.addEventListener("click", hideMermaid)
|
||||||
|
expandBtn.addEventListener("click", showMermaid)
|
||||||
|
document.addEventListener("keydown", handleEscape)
|
||||||
|
|
||||||
|
window.addCleanup(() => {
|
||||||
|
closeBtn.removeEventListener("click", hideMermaid)
|
||||||
|
expandBtn.removeEventListener("click", showMermaid)
|
||||||
|
document.removeEventListener("keydown", handleEscape)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -178,7 +178,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
searchBar.value = "" // clear the input when we dismiss the search
|
searchBar.value = "" // clear the input when we dismiss the search
|
||||||
}
|
}
|
||||||
if (sidebar) {
|
if (sidebar) {
|
||||||
sidebar.style.zIndex = "unset"
|
sidebar.style.zIndex = ""
|
||||||
}
|
}
|
||||||
if (results) {
|
if (results) {
|
||||||
removeAllChildren(results)
|
removeAllChildren(results)
|
||||||
|
|||||||
163
quartz/components/styles/mermaid.inline.scss
Normal file
163
quartz/components/styles/mermaid.inline.scss
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
.expand-button {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
float: right;
|
||||||
|
padding: 0.4rem;
|
||||||
|
margin: 0.3rem;
|
||||||
|
right: 0; // NOTE: right will be set in mermaid.inline.ts
|
||||||
|
color: var(--gray);
|
||||||
|
border-color: var(--dark);
|
||||||
|
background-color: var(--light);
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
fill: var(--light);
|
||||||
|
filter: contrast(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
border-color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
&:hover > .expand-button {
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mermaid-container {
|
||||||
|
position: fixed;
|
||||||
|
contain: layout;
|
||||||
|
z-index: 999;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: none;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > #mermaid-space {
|
||||||
|
display: grid;
|
||||||
|
width: 90%;
|
||||||
|
height: 90vh;
|
||||||
|
margin: 5vh auto;
|
||||||
|
background: var(--light);
|
||||||
|
box-shadow:
|
||||||
|
0 14px 50px rgba(27, 33, 48, 0.12),
|
||||||
|
0 10px 30px rgba(27, 33, 48, 0.16);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > .mermaid-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: 1px solid var(--lightgray);
|
||||||
|
background: var(--light);
|
||||||
|
z-index: 2;
|
||||||
|
max-height: fit-content;
|
||||||
|
|
||||||
|
& > .close-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--darkgray);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--lightgray);
|
||||||
|
color: var(--dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .mermaid-content {
|
||||||
|
padding: 2rem;
|
||||||
|
position: relative;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transition: transform 0.1s ease;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 200px;
|
||||||
|
min-width: 200px;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
max-width: none;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .mermaid-controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--light);
|
||||||
|
border: 1px solid var(--lightgray);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.mermaid-control-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
border: 1px solid var(--lightgray);
|
||||||
|
background: var(--light);
|
||||||
|
color: var(--dark);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: var(--bodyFont);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--lightgray);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style the reset button differently
|
||||||
|
&:nth-child(2) {
|
||||||
|
width: auto;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -98,6 +98,7 @@ declare module "vfile" {
|
|||||||
lang: string
|
lang: string
|
||||||
enableToc: string
|
enableToc: string
|
||||||
cssclasses: string[]
|
cssclasses: string[]
|
||||||
|
comments: boolean | string
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,20 @@
|
|||||||
import remarkMath from "remark-math"
|
import remarkMath from "remark-math"
|
||||||
import rehypeKatex from "rehype-katex"
|
import rehypeKatex from "rehype-katex"
|
||||||
import rehypeMathjax from "rehype-mathjax/svg"
|
import rehypeMathjax from "rehype-mathjax/svg"
|
||||||
|
//@ts-ignore
|
||||||
|
import rehypeTypst from "@myriaddreamin/rehype-typst"
|
||||||
import { QuartzTransformerPlugin } from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
|
import { KatexOptions } from "katex"
|
||||||
|
import { Options as MathjaxOptions } from "rehype-mathjax/svg"
|
||||||
|
//@ts-ignore
|
||||||
|
import { Options as TypstOptions } from "@myriaddreamin/rehype-typst"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
renderEngine: "katex" | "mathjax"
|
renderEngine: "katex" | "mathjax" | "typst"
|
||||||
customMacros: MacroType
|
customMacros: MacroType
|
||||||
|
katexOptions: Omit<KatexOptions, "macros" | "output">
|
||||||
|
mathJaxOptions: Omit<MathjaxOptions, "macros">
|
||||||
|
typstOptions: TypstOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MacroType {
|
interface MacroType {
|
||||||
@ -21,30 +30,37 @@ export const Latex: QuartzTransformerPlugin<Partial<Options>> = (opts) => {
|
|||||||
return [remarkMath]
|
return [remarkMath]
|
||||||
},
|
},
|
||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
if (engine === "katex") {
|
switch (engine) {
|
||||||
return [[rehypeKatex, { output: "html", macros }]]
|
case "katex": {
|
||||||
} else {
|
return [[rehypeKatex, { output: "html", macros, ...(opts?.katexOptions ?? {}) }]]
|
||||||
return [[rehypeMathjax, { macros }]]
|
}
|
||||||
|
case "typst": {
|
||||||
|
return [[rehypeTypst, opts?.typstOptions ?? {}]]
|
||||||
|
}
|
||||||
|
case "mathjax": {
|
||||||
|
return [[rehypeMathjax, { macros, ...(opts?.mathJaxOptions ?? {}) }]]
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return [[rehypeMathjax, { macros, ...(opts?.mathJaxOptions ?? {}) }]]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
externalResources() {
|
externalResources() {
|
||||||
if (engine === "katex") {
|
switch (engine) {
|
||||||
return {
|
case "katex":
|
||||||
css: [
|
return {
|
||||||
// base css
|
css: [{ content: "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" }],
|
||||||
"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",
|
js: [
|
||||||
],
|
{
|
||||||
js: [
|
// fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md
|
||||||
{
|
src: "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js",
|
||||||
// fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md
|
loadTime: "afterDOMReady",
|
||||||
src: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/copy-tex.min.js",
|
contentType: "external",
|
||||||
loadTime: "afterDOMReady",
|
},
|
||||||
contentType: "external",
|
],
|
||||||
},
|
}
|
||||||
],
|
default:
|
||||||
}
|
return { css: [], js: [] }
|
||||||
} else {
|
|
||||||
return {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,14 @@ import rehypeRaw from "rehype-raw"
|
|||||||
import { SKIP, visit } from "unist-util-visit"
|
import { SKIP, visit } from "unist-util-visit"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { splitAnchor } from "../../util/path"
|
import { splitAnchor } from "../../util/path"
|
||||||
import { JSResource } from "../../util/resources"
|
import { JSResource, CSSResource } from "../../util/resources"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import calloutScript from "../../components/scripts/callout.inline.ts"
|
import calloutScript from "../../components/scripts/callout.inline.ts"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
|
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
|
||||||
|
// @ts-ignore
|
||||||
|
import mermaidExtensionScript from "../../components/scripts/mermaid.inline.ts"
|
||||||
|
import mermaidStyle from "../../components/styles/mermaid.inline.scss"
|
||||||
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
|
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
|
||||||
import { toHast } from "mdast-util-to-hast"
|
import { toHast } from "mdast-util-to-hast"
|
||||||
import { toHtml } from "hast-util-to-html"
|
import { toHtml } from "hast-util-to-html"
|
||||||
@ -114,7 +117,7 @@ export const wikilinkRegex = new RegExp(
|
|||||||
export const tableRegex = new RegExp(/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/gm)
|
export const tableRegex = new RegExp(/^\|([^\n])+\|\n(\|)( ?:?-{3,}:? ?\|)+\n(\|([^\n])+\|\n?)+/gm)
|
||||||
|
|
||||||
// matches any wikilink, only used for escaping wikilinks inside tables
|
// matches any wikilink, only used for escaping wikilinks inside tables
|
||||||
export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\])/g)
|
export const tableWikilinkRegex = new RegExp(/(!?\[\[[^\]]*?\]\]|\[\^[^\]]*?\])/g)
|
||||||
|
|
||||||
const highlightRegex = new RegExp(/==([^=]+)==/g)
|
const highlightRegex = new RegExp(/==([^=]+)==/g)
|
||||||
const commentRegex = new RegExp(/%%[\s\S]*?%%/g)
|
const commentRegex = new RegExp(/%%[\s\S]*?%%/g)
|
||||||
@ -279,6 +282,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
|
|
||||||
// internal link
|
// internal link
|
||||||
const url = fp + anchor
|
const url = fp + anchor
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "link",
|
type: "link",
|
||||||
url,
|
url,
|
||||||
@ -515,6 +519,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
node.data = {
|
node.data = {
|
||||||
hProperties: {
|
hProperties: {
|
||||||
className: ["mermaid"],
|
className: ["mermaid"],
|
||||||
|
"data-clipboard": JSON.stringify(node.value),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,10 +664,138 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.mermaid) {
|
||||||
|
plugins.push(() => {
|
||||||
|
return (tree: HtmlRoot, _file) => {
|
||||||
|
visit(tree, "element", (node: Element, _idx, parent) => {
|
||||||
|
if (
|
||||||
|
node.tagName === "code" &&
|
||||||
|
((node.properties?.className ?? []) as string[])?.includes("mermaid")
|
||||||
|
) {
|
||||||
|
parent!.children = [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "button",
|
||||||
|
properties: {
|
||||||
|
className: ["expand-button"],
|
||||||
|
"aria-label": "Expand mermaid diagram",
|
||||||
|
"aria-hidden": "true",
|
||||||
|
"data-view-component": true,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "svg",
|
||||||
|
properties: {
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
viewBox: "0 0 16 16",
|
||||||
|
fill: "currentColor",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "path",
|
||||||
|
properties: {
|
||||||
|
fillRule: "evenodd",
|
||||||
|
d: "M3.72 3.72a.75.75 0 011.06 1.06L2.56 7h10.88l-2.22-2.22a.75.75 0 011.06-1.06l3.5 3.5a.75.75 0 010 1.06l-3.5 3.5a.75.75 0 11-1.06-1.06l2.22-2.22H2.56l2.22 2.22a.75.75 0 11-1.06 1.06l-3.5-3.5a.75.75 0 010-1.06l3.5-3.5z",
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
node,
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "div",
|
||||||
|
properties: { id: "mermaid-container" },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "div",
|
||||||
|
properties: { id: "mermaid-space" },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "div",
|
||||||
|
properties: { className: ["mermaid-header"] },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "button",
|
||||||
|
properties: {
|
||||||
|
className: ["close-button"],
|
||||||
|
"aria-label": "close button",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "svg",
|
||||||
|
properties: {
|
||||||
|
"aria-hidden": "true",
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
"stroke-width": "2",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"stroke-linejoin": "round",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "line",
|
||||||
|
properties: {
|
||||||
|
x1: 18,
|
||||||
|
y1: 6,
|
||||||
|
x2: 6,
|
||||||
|
y2: 18,
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "line",
|
||||||
|
properties: {
|
||||||
|
x1: 6,
|
||||||
|
y1: 6,
|
||||||
|
x2: 18,
|
||||||
|
y2: 18,
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "div",
|
||||||
|
properties: { className: ["mermaid-content"] },
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return plugins
|
return plugins
|
||||||
},
|
},
|
||||||
externalResources() {
|
externalResources() {
|
||||||
const js: JSResource[] = []
|
const js: JSResource[] = []
|
||||||
|
const css: CSSResource[] = []
|
||||||
|
|
||||||
if (opts.enableCheckbox) {
|
if (opts.enableCheckbox) {
|
||||||
js.push({
|
js.push({
|
||||||
@ -682,32 +815,18 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
|
|
||||||
if (opts.mermaid) {
|
if (opts.mermaid) {
|
||||||
js.push({
|
js.push({
|
||||||
script: `
|
script: mermaidExtensionScript,
|
||||||
let mermaidImport = undefined
|
|
||||||
document.addEventListener('nav', async () => {
|
|
||||||
if (document.querySelector("code.mermaid")) {
|
|
||||||
mermaidImport ||= await import('https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs')
|
|
||||||
const mermaid = mermaidImport.default
|
|
||||||
const darkMode = document.documentElement.getAttribute('saved-theme') === 'dark'
|
|
||||||
mermaid.initialize({
|
|
||||||
startOnLoad: false,
|
|
||||||
securityLevel: 'loose',
|
|
||||||
theme: darkMode ? 'dark' : 'default'
|
|
||||||
})
|
|
||||||
|
|
||||||
await mermaid.run({
|
|
||||||
querySelector: '.mermaid'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
loadTime: "afterDOMReady",
|
loadTime: "afterDOMReady",
|
||||||
moduleType: "module",
|
moduleType: "module",
|
||||||
contentType: "inline",
|
contentType: "inline",
|
||||||
})
|
})
|
||||||
|
css.push({
|
||||||
|
content: mermaidStyle,
|
||||||
|
inline: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return { js }
|
return { js, css }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,12 @@ export type JSResource = {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export type CSSResource = {
|
||||||
|
content: string
|
||||||
|
inline?: boolean
|
||||||
|
spaPreserve?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
|
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
|
||||||
const scriptType = resource.moduleType ?? "application/javascript"
|
const scriptType = resource.moduleType ?? "application/javascript"
|
||||||
const spaPreserve = preserve ?? resource.spaPreserve
|
const spaPreserve = preserve ?? resource.spaPreserve
|
||||||
@ -36,7 +42,24 @@ export function JSResourceToScriptElement(resource: JSResource, preserve?: boole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CSSResourceToStyleElement(resource: CSSResource, preserve?: boolean): JSX.Element {
|
||||||
|
const spaPreserve = preserve ?? resource.spaPreserve
|
||||||
|
if (resource.inline ?? false) {
|
||||||
|
return <style>{resource.content}</style>
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<link
|
||||||
|
key={resource.content}
|
||||||
|
href={resource.content}
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
spa-preserve={spaPreserve}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface StaticResources {
|
export interface StaticResources {
|
||||||
css: string[]
|
css: CSSResource[]
|
||||||
js: JSResource[]
|
js: JSResource[]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user