Skip to content

Commit cd9474f

Browse files
committed
What a rabbit hole that was!
1 parent cd165a5 commit cd9474f

File tree

3 files changed

+56
-48
lines changed

3 files changed

+56
-48
lines changed

FrontEnd/scripts/controllers/readme_controller.js

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,60 +16,44 @@ import { Controller } from '@hotwired/stimulus'
1616
import mermaid from 'mermaid'
1717

1818
export class ReadmeController extends Controller {
19-
onThemeChange(darkMode) {
20-
// If `darkMode` isn't passed, get the current value
21-
if (darkMode == undefined) {
22-
darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
23-
console.log('actually dark mode is', darkMode)
24-
}
19+
frameLoaded() {
20+
this.navigateToAnchorFromLocation()
21+
this.renderMermaidDiagrams()
22+
}
2523

26-
// Get all mermaid diagrams
27-
const mermaidDivs = document.querySelectorAll('pre[lang="mermaid"]')
24+
async renderMermaidDiagrams() {
25+
// Replace all Mermaid chart sources with rendered diagrams.
26+
const mermaidSectionElements = document.querySelectorAll('section[data-type="mermaid"]')
27+
for (const [index, mermaidSectionElement] of Array.from(mermaidSectionElements).entries()) {
28+
// No need to parse the JSON, the chart source is in a `data-plain` attribute.
29+
const mermaidDataElement = mermaidSectionElement.querySelector('[data-plain]')
30+
if (!mermaidDataElement) continue
31+
const chartDefinition = mermaidDataElement.getAttribute('data-plain')
32+
if (!chartDefinition) continue
2833

29-
for (const div of Array.from(mermaidDivs)) {
30-
// Get the diagram code
31-
const json = div.parentElement.parentElement.getAttribute('data-json')
32-
if (!json) {
33-
continue
34-
}
35-
const diagramCode = JSON.parse(json).data
36-
if (diagramCode) {
37-
try {
38-
// Clear the existing diagram
39-
div.innerHTML = diagramCode
40-
div.removeAttribute('data-processed')
41-
} catch (error) {
42-
console.error('Error re-rendering mermaid diagram:', error)
43-
}
44-
}
45-
}
34+
// Make a container with *both* light and dark charts.
35+
const chartContainer = document.createElement('div')
36+
chartContainer.classList.add('mermaid-chart')
37+
mermaidDataElement.appendChild(chartContainer)
4638

47-
mermaid.initialize({
48-
theme: darkMode ? 'dark' : undefined,
49-
nodeSpacing: 50,
50-
rankSpacing: 50,
51-
curve: 'basis',
52-
})
39+
// The documentation says not to call `initialize` more than once, but it is the
40+
// only way to switch themes and it's the only way to get this working.
41+
mermaid.initialize({ theme: 'default', nodeSpacing: 50, rankSpacing: 50, curve: 'basis' })
42+
const lightRenderResult = await mermaid.render(`mermaid-chart-light-${index}`, chartDefinition)
43+
chartContainer.insertAdjacentHTML('beforeend', lightRenderResult.svg)
5344

54-
// Rather then preprocess the HTML in Swift we just change the selector to use `lang` attribute instead of `class`
55-
mermaid.run({
56-
querySelector: 'pre[lang="mermaid"]',
57-
})
58-
}
59-
navigateToAnchorFromLocation() {
60-
// listen for changes to color scheme
61-
if (typeof window !== 'undefined') {
62-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
45+
mermaid.initialize({ theme: 'dark', nodeSpacing: 50, rankSpacing: 50, curve: 'basis' })
46+
const darkRenderResult = await mermaid.render(`mermaid-chart-dark-${index}`, chartDefinition)
47+
chartContainer.insertAdjacentHTML('beforeend', darkRenderResult.svg)
6348

64-
// Add listener for system preference changes
65-
mediaQuery.addEventListener('change', (e) => {
66-
this.onThemeChange(e.matches)
67-
})
49+
// Clean up the superfluous loading element.
50+
const loadingElement = mermaidSectionElement.querySelector('.js-render-enrichment-loader')
51+
if (!loadingElement) continue
52+
loadingElement.remove()
6853
}
54+
}
6955

70-
// setup mermaid diagrams
71-
this.onThemeChange()
72-
56+
navigateToAnchorFromLocation() {
7357
// If the browser has an anchor in the URL that may be inside the README then
7458
// we should attempt to scroll it into view once the README is loaded.
7559
const hash = window.location.hash

FrontEnd/styles/readme.scss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,30 @@
138138
.markdown-alert-caution {
139139
color: var(--mid-red);
140140
}
141+
142+
.mermaid-chart {
143+
display: flex;
144+
justify-content: center;
145+
padding: 30px 0;
146+
}
147+
148+
section[data-type='mermaid'] {
149+
pre {
150+
display: none;
151+
}
152+
153+
@media (prefers-color-scheme: light) {
154+
[id^='mermaid-chart-dark'] {
155+
display: none;
156+
}
157+
}
158+
159+
@media (prefers-color-scheme: dark) {
160+
[id^='mermaid-chart-light'] {
161+
display: none;
162+
}
163+
}
164+
}
141165
}
142166

143167
g-emoji {

Sources/App/Views/PackageController/PackageShow+View.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ extension PackageShow {
327327
.value(model.repositoryName),
328328
.readme).relativeURL(),
329329
.data(named: "controller", value: "readme"),
330-
.data(named: "action", value: "turbo:frame-load->readme#navigateToAnchorFromLocation"),
330+
.data(named: "action", value: "turbo:frame-load->readme#frameLoaded"),
331331
.div(.spinner())
332332
)
333333
}

0 commit comments

Comments
 (0)