Skip to content

Commit ea4ae99

Browse files
committed
Quartz sync: May 26, 2025, 10:43 PM
1 parent 7c1e009 commit ea4ae99

File tree

4 files changed

+177
-177
lines changed

4 files changed

+177
-177
lines changed

quartz.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const config: QuartzConfig = {
8080
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
8181
Plugin.Description(),
8282
Plugin.Latex({ renderEngine: "katex" }),
83-
Plugin.MarkmapTransformer(),
83+
Plugin.Markmap(),
8484
],
8585
filters: [
8686
Plugin.RemoveDrafts(),

quartz/components/scripts/markmap.inline.ts

Lines changed: 168 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -5,206 +5,206 @@ import { registerEscapeHandler } from "./util"
55
import { mouseEnterHandler, clearActivePopover } from './popover.inline'
66

77
const externalIcon = `
8-
<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em; margin-left:0.2em;" viewBox="0 0 512 512">
9-
<path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"/>
10-
</svg>`.trim()
8+
<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em; margin-left:0.2em;" viewBox="0 0 512 512">
9+
<path
10+
d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"/>
11+
</svg>`
1112

1213
const fullIcon = `
13-
<svg xmlns="http://www.w3.org/2000/svg" height="15" viewBox="0 -960 960 960" width="15" aria-hidden="true">
14-
<path stroke="none" fill="currentColor" fill-rule="evenodd"
15-
d="M120-120v-320h80v184l504-504H520v-80h320v320h-80v-184L256-200h184v80H120Z"/>
16-
</svg>`
14+
<svg xmlns="http://www.w3.org/2000/svg" height="15" viewBox="0 -960 960 960" width="15" aria-hidden="true">
15+
<path stroke="none" fill="currentColor" fill-rule="evenodd"
16+
d="M120-120v-320h80v184l504-504H520v-80h320v320h-80v-184L256-200h184v80H120Z"/>
17+
</svg>`
1718

1819
const closeIcon = `
1920
<svg xmlns="http://www.w3.org/2000/svg" height="15" viewBox="0 -960 960 960" width="15" aria-hidden="true">
20-
<path stroke="none" fill="currentColor" fill-rule="evenodd"
21+
<path stroke="none" fill="currentColor" fill-rule="evenodd"
2122
d="m136-80-56-56 264-264H160v-80h320v320h-80v-184L136-80Zm344-400v-320h80v184l264-264 56 56-264 264h184v80H480Z"/>
2223
</svg>`
2324

2425
const exitIcon = `
2526
<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" >
26-
<path stroke="none" fill="currentColor" fill-rule="evenodd"
27-
d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>
28-
</svg>
29-
`
27+
<path stroke="none" fill="currentColor" fill-rule="evenodd"
28+
d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>
29+
</svg>`
3030

3131
function renderGlobalMarkmap() {
32-
const transformer = new Transformer()
33-
const { styles } = transformer.getAssets();
34-
if (styles)
35-
loadCSS(styles)
36-
37-
const jsonOptions = {
38-
colorFreezeLevel: 2,
39-
spacingHorizontal: 100,
40-
spacingVertical: 10,
41-
}
42-
const markmapOptions = deriveOptions(jsonOptions);
43-
markmapOptions.scrollForPan = false
32+
const transformer = new Transformer()
33+
const { styles } = transformer.getAssets();
34+
if (styles)
35+
loadCSS(styles)
36+
37+
const jsonOptions = {
38+
colorFreezeLevel: 2,
39+
spacingHorizontal: 100,
40+
spacingVertical: 10,
41+
}
42+
const markmapOptions = deriveOptions(jsonOptions);
43+
markmapOptions.scrollForPan = false
44+
45+
const container = document.querySelector(".global-markmap-outer") as HTMLElement
46+
const containerInner = container.querySelector('.global-markmap-container') as HTMLElement
47+
const svg = container.querySelector("#global-markmap") as SVGSVGElement
48+
const toolbarEl = container.querySelector("#global-markmap-toolbar") as HTMLElement
49+
50+
if (!container || !svg || !toolbarEl) {
51+
console.warn("❌ Markmap container or elements not found.")
52+
return
53+
}
54+
55+
svg.innerHTML = ""
56+
toolbarEl.innerHTML = ""
57+
58+
const raw = container.dataset.markmap
59+
if (!raw) {
60+
return
61+
}
62+
63+
const data = JSON.parse(decodeURIComponent(raw))
64+
const mm = Markmap.create(svg, markmapOptions, data)
65+
const toolbar = Toolbar.create(mm)
66+
const mmToolbar = toolbar.render()
67+
toolbarEl.append(mmToolbar)
68+
mm.fit()
69+
70+
registerEscapeHandler(container, hideGlobalMarkmap)
71+
setupMarkmapPopoverSupport()
72+
73+
svg.querySelectorAll<HTMLAnchorElement>('a[href^="http"]').forEach(a => {
74+
a.classList.add('external')
75+
})
76+
77+
svg.querySelectorAll<HTMLAnchorElement>('a.external').forEach(a => {
78+
a.insertAdjacentHTML('beforeend', externalIcon)
79+
})
80+
81+
let isToggled = containerInner.classList.contains('fullscreen') ? true : false;
82+
const customToolbar = document.createElement("div");
83+
customToolbar.className = "mm-toolbar-item";
84+
customToolbar.title = "Toggle fullscreen";
85+
customToolbar.innerHTML = isToggled ? closeIcon : fullIcon
86+
87+
const customExit = document.createElement("div");
88+
customExit.className = "mm-toolbar-item";
89+
customExit.title = "Exit";
90+
customExit.innerHTML = exitIcon
91+
92+
mmToolbar.append(customToolbar);
93+
mmToolbar.append(customExit);
94+
95+
container.classList.add("active")
96+
97+
customToolbar.addEventListener('click', () => {
98+
containerInner.classList.toggle('fullscreen')
99+
isToggled = !isToggled;
100+
customToolbar.innerHTML = isToggled ? closeIcon : fullIcon;
101+
})
102+
103+
customExit.addEventListener("click", () => {
104+
const escEvent = new KeyboardEvent("keydown", {
105+
key: "Escape",
106+
code: "Escape",
107+
keyCode: 27,
108+
which: 27,
109+
bubbles: true,
110+
});
111+
document.dispatchEvent(escEvent);
112+
});
44113

45-
const container = document.querySelector(".global-markmap-outer") as HTMLElement
46-
const containerInner = container.querySelector('.global-markmap-container') as HTMLElement
47-
const svg = container.querySelector("#global-markmap") as SVGSVGElement
48-
const toolbarEl = container.querySelector("#global-markmap-toolbar") as HTMLElement
114+
renderMermaidInMarkmap(svg)
115+
}
49116

50-
if (!container || !svg || !toolbarEl) {
51-
console.warn("❌ Markmap container or elements not found.")
52-
return
53-
}
117+
function hideGlobalMarkmap() {
118+
const container = document.querySelector(".global-markmap-outer") as HTMLElement
119+
container?.classList.remove("active")
120+
}
54121

55-
svg.innerHTML = ""
56-
toolbarEl.innerHTML = ""
122+
function toggleGlobalMarkmap() {
123+
const container = document.querySelector(".global-markmap-outer") as HTMLElement
124+
if (container?.classList.contains("active")) {
125+
hideGlobalMarkmap()
126+
} else {
127+
renderGlobalMarkmap()
128+
}
129+
}
57130

58-
const raw = container.dataset.markmap
59-
if (!raw) {
60-
return
61-
}
131+
function setupMarkmapPopoverSupport() {
132+
const markmapLinks = document.querySelectorAll(".markmap .markmap-foreign a.internal") as NodeListOf<HTMLAnchorElement>
133+
for (const link of markmapLinks) {
134+
if (link.dataset.noPopover === "true") continue
135+
136+
link.addEventListener("mouseenter", mouseEnterHandler)
137+
link.addEventListener("mouseleave", clearActivePopover)
138+
window.addCleanup?.(() => {
139+
link.removeEventListener("mouseenter", mouseEnterHandler)
140+
link.removeEventListener("mouseleave", clearActivePopover)
141+
})
142+
}
143+
}
62144

63-
const data = JSON.parse(decodeURIComponent(raw))
64-
const mm = Markmap.create(svg, markmapOptions, data)
65-
const toolbar = Toolbar.create(mm)
66-
const mmToolbar = toolbar.render()
67-
toolbarEl.append(mmToolbar)
68-
mm.fit()
145+
function renderMermaidInMarkmap(svg: SVGSVGElement) {
146+
requestAnimationFrame(async () => {
147+
const codeBlocks = svg.querySelectorAll("foreignObject code.language-mermaid")
69148

70-
registerEscapeHandler(container, hideGlobalMarkmap)
71-
setupMarkmapPopoverSupport()
149+
if (codeBlocks.length === 0) return
72150

73-
svg.querySelectorAll<HTMLAnchorElement>('a[href^="http"]').forEach(a => {
74-
a.classList.add('external')
75-
})
151+
const { default: mermaid } = await import(
152+
"https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.4.0/mermaid.esm.min.mjs"
153+
)
76154

77-
svg.querySelectorAll<HTMLAnchorElement>('a.external').forEach(a => {
78-
a.insertAdjacentHTML('beforeend', externalIcon)
155+
mermaid.initialize({
156+
startOnLoad: false,
157+
theme: document.documentElement.getAttribute("saved-theme") === "dark" ? "dark" : "base",
158+
securityLevel: "loose",
79159
})
80160

81-
let isToggled = containerInner.classList.contains('fullscreen') ? true : false;
82-
const customToolbar = document.createElement("div");
83-
customToolbar.className = "mm-toolbar-item";
84-
customToolbar.title = "Toggle fullscreen";
85-
customToolbar.innerHTML = isToggled ? closeIcon : fullIcon
161+
for (const code of codeBlocks) {
162+
const graph = code.textContent?.trim()
163+
if (!graph) continue
86164

87-
const customExit = document.createElement("div");
88-
customExit.className = "mm-toolbar-item";
89-
customExit.title = "Exit";
90-
customExit.innerHTML = exitIcon
165+
const pre = code.closest("pre")
166+
const div = code.closest("div")
167+
if (!div || !pre) continue
91168

92-
mmToolbar.append(customToolbar);
93-
mmToolbar.append(customExit);
169+
pre.remove()
94170

95-
container.classList.add("active")
171+
const tempDiv = document.createElement("div")
172+
tempDiv.style.visibility = "hidden"
173+
tempDiv.style.position = "absolute"
174+
tempDiv.style.top = "-9999px"
175+
document.body.appendChild(tempDiv)
96176

97-
customToolbar.addEventListener('click', () => {
98-
containerInner.classList.toggle('fullscreen')
99-
isToggled = !isToggled;
100-
customToolbar.innerHTML = isToggled ? closeIcon : fullIcon;
101-
})
177+
const mermaidDiv = document.createElement("div")
178+
mermaidDiv.className = "mermaid"
179+
mermaidDiv.textContent = graph
180+
tempDiv.appendChild(mermaidDiv)
102181

103-
customExit.addEventListener("click", () => {
104-
const escEvent = new KeyboardEvent("keydown", {
105-
key: "Escape",
106-
code: "Escape",
107-
keyCode: 27,
108-
which: 27,
109-
bubbles: true,
110-
});
111-
document.dispatchEvent(escEvent);
112-
});
182+
try {
183+
await mermaid.run({ nodes: [mermaidDiv] })
184+
const renderedSvg = mermaidDiv.querySelector("svg")
185+
if (!renderedSvg) continue
113186

114-
renderMermaidInMarkmap(svg)
115-
}
187+
const importedSvg = renderedSvg.cloneNode(true) as SVGSVGElement
188+
div.appendChild(importedSvg)
116189

117-
function hideGlobalMarkmap() {
118-
const container = document.querySelector(".global-markmap-outer") as HTMLElement
119-
container?.classList.remove("active")
120-
}
121-
122-
function toggleGlobalMarkmap() {
123-
const container = document.querySelector(".global-markmap-outer") as HTMLElement
124-
if (container?.classList.contains("active")) {
125-
hideGlobalMarkmap()
126-
} else {
127-
renderGlobalMarkmap()
190+
tempDiv.remove()
191+
} catch (err) {
192+
console.error("❌ Mermaid render failed:", err)
193+
}
128194
}
129-
}
130-
131-
function setupMarkmapPopoverSupport() {
132-
const markmapLinks = document.querySelectorAll(".markmap .markmap-foreign a.internal") as NodeListOf<HTMLAnchorElement>
133-
for (const link of markmapLinks) {
134-
if (link.dataset.noPopover === "true") continue
135-
136-
link.addEventListener("mouseenter", mouseEnterHandler)
137-
link.addEventListener("mouseleave", clearActivePopover)
138-
window.addCleanup?.(() => {
139-
link.removeEventListener("mouseenter", mouseEnterHandler)
140-
link.removeEventListener("mouseleave", clearActivePopover)
141-
})
142-
}
143-
}
144-
145-
function renderMermaidInMarkmap(svg: SVGSVGElement) {
146-
requestAnimationFrame(async () => {
147-
const codeBlocks = svg.querySelectorAll("foreignObject code.language-mermaid")
148-
149-
if (codeBlocks.length === 0) return
150-
151-
const { default: mermaid } = await import(
152-
"https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.4.0/mermaid.esm.min.mjs"
153-
)
154-
155-
mermaid.initialize({
156-
startOnLoad: false,
157-
theme: document.documentElement.getAttribute("saved-theme") === "dark" ? "dark" : "base",
158-
securityLevel: "loose",
159-
})
160-
161-
for (const code of codeBlocks) {
162-
const graph = code.textContent?.trim()
163-
if (!graph) continue
164-
165-
const pre = code.closest("pre")
166-
const div = code.closest("div")
167-
if (!div || !pre) continue
168-
169-
pre.remove()
170-
171-
const tempDiv = document.createElement("div")
172-
tempDiv.style.visibility = "hidden"
173-
tempDiv.style.position = "absolute"
174-
tempDiv.style.top = "-9999px"
175-
document.body.appendChild(tempDiv)
176-
177-
const mermaidDiv = document.createElement("div")
178-
mermaidDiv.className = "mermaid"
179-
mermaidDiv.textContent = graph
180-
tempDiv.appendChild(mermaidDiv)
181-
182-
try {
183-
await mermaid.run({ nodes: [mermaidDiv] })
184-
const renderedSvg = mermaidDiv.querySelector("svg")
185-
if (!renderedSvg) continue
186-
187-
const importedSvg = renderedSvg.cloneNode(true) as SVGSVGElement
188-
div.appendChild(importedSvg)
189-
190-
tempDiv.remove()
191-
} catch (err) {
192-
console.error("❌ Mermaid render failed:", err)
193-
}
194-
}
195-
})
195+
})
196196
}
197197

198198
window.addEventListener("DOMContentLoaded", () => {
199-
const btn = document.getElementById("show-markmap")
200-
if (btn) {
201-
btn.addEventListener("click", renderGlobalMarkmap)
199+
const btn = document.getElementById("show-markmap")
200+
if (btn) {
201+
btn.addEventListener("click", renderGlobalMarkmap)
202+
}
203+
204+
document.addEventListener("keydown", (e) => {
205+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "m" && !e.shiftKey) {
206+
e.preventDefault()
207+
toggleGlobalMarkmap()
202208
}
203-
204-
document.addEventListener("keydown", (e) => {
205-
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "m" && !e.shiftKey) {
206-
e.preventDefault()
207-
toggleGlobalMarkmap()
208-
}
209-
})
209+
})
210210
})

quartz/plugins/transformers/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ export { SyntaxHighlighting } from "./syntax"
1111
export { TableOfContents } from "./toc"
1212
export { HardLineBreaks } from "./linebreaks"
1313
export { RoamFlavoredMarkdown } from "./roam"
14-
export { MarkmapTransformer } from "./markmap"
14+
export { Markmap } from "./markmap"

0 commit comments

Comments
 (0)