给 Hugo 博客添加 mermaid 短代码支持
首先,看下效果,就是下面这样(RSS 订阅用户可能看不到效果,请点击查看原文):
{{< mermaid >}}
sequenceDiagram
participant Alice
participant Bob
Alice->>John: Hello John, how are you?
loop Healthcheck
John->John: Fight against hypochondria
end
Note right of John: Rational thoughts prevail...
John-->Alice: Great!
John->Bob: How about you?
Bob-->John: Jolly good!
{{< /mermaid >}}
源代码取自 https://learn.netlify.app/en/shortcodes/mermaid/ , 如下:
sequenceDiagram
participant Alice
participant Bob
Alice->>John: Hello John, how are you?
loop Healthcheck
John->John: Fight against hypochondria
end
Note right of John: Rational thoughts prevail...
John-->Alice: Great!
John->Bob: How about you?
Bob-->John: Jolly good!
短代码使用方法
{{</* mermaid */>}}
// 中间写mermaid
{{</* /mermaid */>}}
看上去好像不太直观啊,没错,官方也考虑到了这一点,给送上了 mermaid-live-editor
https://mermaid-js.github.io/mermaid-live-editor
使用 mermaid-live-editor,可以实时预览效果,如果不知道下手,里面还有快速demo供使用。
集成到 Hugo
目的: 最简化集成, 不需要增加 Hugo 配置, 不需要在 markdown front matter 里增加任何选项.
增加 shortcode 模板
如果用 mermaid 官方的集成方法,会带来一个问题: 官方集成方法会导致我们的每个文章页面都会加载 mermaid.js ,但是 我们写博客的时候,并不是每个页面都需要加载 mermaid 这个 js, 只在文章里面包含 mermaid 代码时我们才需要加载。因此这里我们用 js 动态加载 mermaid.
首先,这个 js 我们也懒得放在主题里了,直接用 CDN 的:
去 https://www.jsdelivr.com/package/npm/mermaid 我们点开 dist 目录,然后找到完整版的js文件(注意不要看错了,选了 core 那个):
https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js
新增加 shortcode, 目前我用的主题是 terminal, 因此在 themes/terminal 下新建:
layouts/shortcodes/mermaid.html
文件,内容如下:
<!-- https://github.com/matcornic/hugo-theme-learn/blob/master/layouts/shortcodes/mermaid.html -->
<pre class="mermaid" align="{{ if .Get "align" }}{{ .Get "align" }}{{ else }}center{{ end }}">{{ safeHTML .Inner }}</pre>
这个 shortcode 代码基本上取自这里, 但是老灯这里把外层的 div
改成了 pre
关于 .Inner
是啥, 可以参考这个文档
{{ $_hugo_config := `{ "version": 1 }` }}
我也一并移除了,这个是 shortcodes-with-markdown 才需要的,而我们这里面的内容都是直接给 js 处理的,并不需要 markdown 处理。 参考这里
加载 mermaid js
网上大部分的模板都不够自动, 很多是需要在配置里选择是否加载 mermaid 的, 这里老灯写了个根据文章内容动态加载的 js .
修改模板的 footer.html 文件,增加 js:
const loadScript = (url, onloadFunction) => {
var newScript = document.createElement("script");
newScript.onerror = (oError) => {
throw new URIError("The script " + oError.target.src + " didn't load correctly.");
};
if (onloadFunction) { newScript.onload = onloadFunction; }
document.head.insertAdjacentElement('beforeend', newScript);
newScript.src = url;
}
// mermaid loader by ttys3.dev
const loadMermaidOnNeed = () => {
if (document.querySelectorAll('.mermaid').length > 0) {
loadScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js', () => {
document.head.insertAdjacentHTML("beforeend", `<style>.mermaid svg { background-color: #dadcd8 !important; }</style>`);
// default, dark, neutral, forest
mermaid.initialize({ startOnLoad: false, securityLevel: "strict", theme: "neutral" });
// https://github.com/mermaid-js/mermaid/blob/e6e94ad57ea06ef8627bf91ddfbd88f5082952bf/src/mermaid.js#L153
// mermaid.contentLoaded();
mermaid.init();
console.log("mermaid init done");
})
}
}
window.addEventListener('load', function(event) {
// load mermaid
loadMermaidOnNeed();
});
如果要调试问题,可以在配置里增加 logLevel: 1,
theme: "neutral"
是指定主题,可以从 default, dark, neutral, forest
四个里面选。 如果不太满意默认的,也可以自行 DIY, 参考 https://mermaid-js.github.io/mermaid/#/theming
startOnLoad
选项其实在我们这里并没有什么用。 如果调用 mermaid.contentLoaded()
渲染的话,需要设置 startOnLoad
为 true
. 但是其实 contentLoaded
就是判断了一下 startOnLoad
然后调用了 mermaid.init()
, 因此这里老灯直接调用 mermaid.init()
渲染了。
最后,偷懒的可以直接用老灯这个修改版的terminal
主题 https://github.com/ttys3/hugo-theme-terminal/tree/ttys3
Refs
https://code.luasoftware.com/tutorials/hugo/how-to-escape-shortcode-in-hugo/
https://github.com/mermaid-js/mermaid
https://github.com/mermaid-js/mermaid-live-editor
https://github.com/matcornic/hugo-theme-learn
https://github.com/matcornic/hugo-theme-learn/blob/master/layouts/shortcodes/mermaid.html
https://mermaid-js.github.io/mermaid/#/Setup?id=startonload
https://mermaid-js.github.io/mermaid/#/Setup?id=securitylevel
strict: (default) tags in text are encoded, click functionality is disabeled loose: tags in text are allowed, click functionality is enabled antiscript: html tags in text are allowed, (only script element is removed), click functionality is enabled