给 Hugo 博客添加 mermaid 短代码支持

给 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() 渲染的话,需要设置 startOnLoadtrue. 但是其实 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