Skip to content

Commit 1d8303b

Browse files
committed
Quartz sync: May 23, 2025, 12:10 PM
1 parent 9fdc028 commit 1d8303b

File tree

4 files changed

+137
-118
lines changed

4 files changed

+137
-118
lines changed

quartz.layout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ export const defaultListPageLayout: PageLayout = {
109109
Component: Component.Search(),
110110
grow: true,
111111
},
112+
{ Component: Component.DesktopOnly(Component.ReaderMode()) },
112113
{ Component: Component.Darkmode() },
113114
{ Component: Component.MarkmapViewer() },
114-
{ Component: Component.DesktopOnly(Component.ReaderMode()) },
115115
],
116116
}),
117117
Component.Explorer({

quartz/components/MarkmapViewer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ function replacement(currentPath: FullSlug) {
4343
return `<img src="../../../${currentPath}/../attachments/${link}" alt="${displayText || link}" />`
4444
}
4545
const safeLink = `quartz-${link.trim().replace(/\s+/g, "-")}`
46-
return `<a href="/${safeLink}" class="inner">${displayText || link}</a>`
46+
return `<a href="/${safeLink}" class="internal">${displayText || link}</a>`
4747
} else if (tag) {
48-
return `<a href="/tags/${tag}" class="hash-link">#${tag}</a>`
48+
return `<a href="/tags/${tag}" class="internal">#${tag}</a>`
4949
}
5050
}
5151
}

quartz/components/scripts/markmap.inline.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Markmap, deriveOptions, loadCSS } from "markmap-view"
22
import { Transformer } from "markmap-lib"
33
import { Toolbar } from "markmap-toolbar"
44
import { registerEscapeHandler } from "./util"
5+
import { mouseEnterHandler, clearActivePopover } from './popover.inline'
56

67
const externalIcon = `
78
<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">
@@ -108,6 +109,7 @@ function renderGlobalMarkmap() {
108109
});
109110

110111
registerEscapeHandler(container, hideGlobalMarkmap)
112+
setupMarkmapPopoverSupport()
111113
}
112114

113115
function hideGlobalMarkmap() {
@@ -124,6 +126,21 @@ function toggleGlobalMarkmap() {
124126
}
125127
}
126128

129+
function setupMarkmapPopoverSupport() {
130+
const markmapLinks = document.querySelectorAll(".markmap .markmap-foreign a.internal") as NodeListOf<HTMLAnchorElement>
131+
console.log("markmap links", markmapLinks)
132+
for (const link of markmapLinks) {
133+
if (link.dataset.noPopover === "true") continue
134+
135+
link.addEventListener("mouseenter", mouseEnterHandler)
136+
link.addEventListener("mouseleave", clearActivePopover)
137+
window.addCleanup?.(() => {
138+
link.removeEventListener("mouseenter", mouseEnterHandler)
139+
link.removeEventListener("mouseleave", clearActivePopover)
140+
})
141+
}
142+
}
143+
127144
window.addEventListener("DOMContentLoaded", () => {
128145
const btn = document.getElementById("show-markmap")
129146
if (btn) {

quartz/components/scripts/popover.inline.ts

Lines changed: 117 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -6,128 +6,130 @@ const p = new DOMParser()
66
let activeAnchor: HTMLAnchorElement | null = null
77

88
async function mouseEnterHandler(
9-
this: HTMLAnchorElement,
10-
{ clientX, clientY }: { clientX: number; clientY: number },
9+
this: HTMLAnchorElement,
10+
{ clientX, clientY }: { clientX: number; clientY: number },
1111
) {
12-
const link = (activeAnchor = this)
13-
if (link.dataset.noPopover === "true") {
14-
return
15-
}
16-
17-
async function setPosition(popoverElement: HTMLElement) {
18-
const { x, y } = await computePosition(link, popoverElement, {
19-
strategy: "fixed",
20-
middleware: [inline({ x: clientX, y: clientY }), shift(), flip()],
21-
})
22-
Object.assign(popoverElement.style, {
23-
transform: `translate(${x.toFixed()}px, ${y.toFixed()}px)`,
24-
})
25-
}
26-
27-
function showPopover(popoverElement: HTMLElement) {
28-
clearActivePopover()
29-
popoverElement.classList.add("active-popover")
30-
setPosition(popoverElement as HTMLElement)
31-
32-
if (hash !== "") {
33-
const targetAnchor = `#popover-internal-${hash.slice(1)}`
34-
const heading = popoverInner.querySelector(targetAnchor) as HTMLElement | null
35-
if (heading) {
36-
// leave ~12px of buffer when scrolling to a heading
37-
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
38-
}
12+
const link = (activeAnchor = this)
13+
if (link.dataset.noPopover === "true") {
14+
return
15+
}
16+
17+
async function setPosition(popoverElement: HTMLElement) {
18+
const { x, y } = await computePosition(link, popoverElement, {
19+
strategy: "fixed",
20+
middleware: [inline({ x: clientX, y: clientY }), shift(), flip()],
21+
})
22+
Object.assign(popoverElement.style, {
23+
transform: `translate(${x.toFixed()}px, ${y.toFixed()}px)`,
24+
})
25+
}
26+
27+
function showPopover(popoverElement: HTMLElement) {
28+
clearActivePopover()
29+
popoverElement.classList.add("active-popover")
30+
setPosition(popoverElement as HTMLElement)
31+
32+
if (hash !== "") {
33+
const targetAnchor = `#popover-internal-${hash.slice(1)}`
34+
const heading = popoverInner.querySelector(targetAnchor) as HTMLElement | null
35+
if (heading) {
36+
// leave ~12px of buffer when scrolling to a heading
37+
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
38+
}
39+
}
40+
}
41+
42+
const targetUrl = new URL(link.href)
43+
const hash = decodeURIComponent(targetUrl.hash)
44+
targetUrl.hash = ""
45+
targetUrl.search = ""
46+
const popoverId = `popover-${link.pathname}`
47+
const prevPopoverElement = document.getElementById(popoverId)
48+
49+
// dont refetch if there's already a popover
50+
if (!!document.getElementById(popoverId)) {
51+
showPopover(prevPopoverElement as HTMLElement)
52+
return
3953
}
40-
}
41-
42-
const targetUrl = new URL(link.href)
43-
const hash = decodeURIComponent(targetUrl.hash)
44-
targetUrl.hash = ""
45-
targetUrl.search = ""
46-
const popoverId = `popover-${link.pathname}`
47-
const prevPopoverElement = document.getElementById(popoverId)
48-
49-
// dont refetch if there's already a popover
50-
if (!!document.getElementById(popoverId)) {
51-
showPopover(prevPopoverElement as HTMLElement)
52-
return
53-
}
54-
55-
const response = await fetchCanonical(targetUrl).catch((err) => {
56-
console.error(err)
57-
})
58-
59-
if (!response) return
60-
const [contentType] = response.headers.get("Content-Type")!.split(";")
61-
const [contentTypeCategory, typeInfo] = contentType.split("/")
62-
63-
const popoverElement = document.createElement("div")
64-
popoverElement.id = popoverId
65-
popoverElement.classList.add("popover")
66-
const popoverInner = document.createElement("div")
67-
popoverInner.classList.add("popover-inner")
68-
popoverInner.dataset.contentType = contentType ?? undefined
69-
popoverElement.appendChild(popoverInner)
70-
71-
switch (contentTypeCategory) {
72-
case "image":
73-
const img = document.createElement("img")
74-
img.src = targetUrl.toString()
75-
img.alt = targetUrl.pathname
76-
77-
popoverInner.appendChild(img)
78-
break
79-
case "application":
80-
switch (typeInfo) {
81-
case "pdf":
82-
const pdf = document.createElement("iframe")
83-
pdf.src = targetUrl.toString()
84-
popoverInner.appendChild(pdf)
85-
break
54+
55+
const response = await fetchCanonical(targetUrl).catch((err) => {
56+
console.error(err)
57+
})
58+
59+
if (!response) return
60+
const [contentType] = response.headers.get("Content-Type")!.split(";")
61+
const [contentTypeCategory, typeInfo] = contentType.split("/")
62+
63+
const popoverElement = document.createElement("div")
64+
popoverElement.id = popoverId
65+
popoverElement.classList.add("popover")
66+
const popoverInner = document.createElement("div")
67+
popoverInner.classList.add("popover-inner")
68+
popoverInner.dataset.contentType = contentType ?? undefined
69+
popoverElement.appendChild(popoverInner)
70+
71+
switch (contentTypeCategory) {
72+
case "image":
73+
const img = document.createElement("img")
74+
img.src = targetUrl.toString()
75+
img.alt = targetUrl.pathname
76+
77+
popoverInner.appendChild(img)
78+
break
79+
case "application":
80+
switch (typeInfo) {
81+
case "pdf":
82+
const pdf = document.createElement("iframe")
83+
pdf.src = targetUrl.toString()
84+
popoverInner.appendChild(pdf)
85+
break
86+
default:
87+
break
88+
}
89+
break
8690
default:
87-
break
88-
}
89-
break
90-
default:
91-
const contents = await response.text()
92-
const html = p.parseFromString(contents, "text/html")
93-
normalizeRelativeURLs(html, targetUrl)
94-
// prepend all IDs inside popovers to prevent duplicates
95-
html.querySelectorAll("[id]").forEach((el) => {
96-
const targetID = `popover-internal-${el.id}`
97-
el.id = targetID
98-
})
99-
const elts = [...html.getElementsByClassName("popover-hint")]
100-
if (elts.length === 0) return
101-
102-
elts.forEach((elt) => popoverInner.appendChild(elt))
103-
}
104-
105-
if (!!document.getElementById(popoverId)) {
106-
return
107-
}
108-
109-
document.body.appendChild(popoverElement)
110-
if (activeAnchor !== this) {
111-
return
112-
}
113-
114-
showPopover(popoverElement)
91+
const contents = await response.text()
92+
const html = p.parseFromString(contents, "text/html")
93+
normalizeRelativeURLs(html, targetUrl)
94+
// prepend all IDs inside popovers to prevent duplicates
95+
html.querySelectorAll("[id]").forEach((el) => {
96+
const targetID = `popover-internal-${el.id}`
97+
el.id = targetID
98+
})
99+
const elts = [...html.getElementsByClassName("popover-hint")]
100+
if (elts.length === 0) return
101+
102+
elts.forEach((elt) => popoverInner.appendChild(elt))
103+
}
104+
105+
if (!!document.getElementById(popoverId)) {
106+
return
107+
}
108+
109+
document.body.appendChild(popoverElement)
110+
if (activeAnchor !== this) {
111+
return
112+
}
113+
114+
showPopover(popoverElement)
115115
}
116116

117117
function clearActivePopover() {
118-
activeAnchor = null
119-
const allPopoverElements = document.querySelectorAll(".popover")
120-
allPopoverElements.forEach((popoverElement) => popoverElement.classList.remove("active-popover"))
118+
activeAnchor = null
119+
const allPopoverElements = document.querySelectorAll(".popover")
120+
allPopoverElements.forEach((popoverElement) => popoverElement.classList.remove("active-popover"))
121121
}
122122

123123
document.addEventListener("nav", () => {
124-
const links = [...document.querySelectorAll("a.internal")] as HTMLAnchorElement[]
125-
for (const link of links) {
126-
link.addEventListener("mouseenter", mouseEnterHandler)
127-
link.addEventListener("mouseleave", clearActivePopover)
128-
window.addCleanup(() => {
129-
link.removeEventListener("mouseenter", mouseEnterHandler)
130-
link.removeEventListener("mouseleave", clearActivePopover)
131-
})
132-
}
124+
const links = [...document.querySelectorAll("a.internal")] as HTMLAnchorElement[]
125+
for (const link of links) {
126+
link.addEventListener("mouseenter", mouseEnterHandler)
127+
link.addEventListener("mouseleave", clearActivePopover)
128+
window.addCleanup(() => {
129+
link.removeEventListener("mouseenter", mouseEnterHandler)
130+
link.removeEventListener("mouseleave", clearActivePopover)
131+
})
132+
}
133133
})
134+
135+
export { mouseEnterHandler, clearActivePopover }

0 commit comments

Comments
 (0)