Hugo:配置Callouts

Callouts 是 Obsidian 中增强显示的工具,丰富了原始 Markdown 中具备的标签。

本文主要介绍如何将 Callouts 移植到 Hugo 中,使得本地 Obsidian 预览结果和 Hugo 渲染出来的 Callouts Block 具备一致的效果.

Todo

  • 增加可折叠
  • 修复嵌套时样式被覆盖

在 Obsidian 中 使用 Callouts 的如下,其支持除了 info 以外的其他很多类型

> [!info]
> Here's a callout block. 

其主要支持的特性有可替换标题, 可嵌套, 可折叠

> [!tip] Title-only callout
> [!faq]- Are callouts foldable? 
> Yes! In a foldable callout, the contents are hidden when the callout is collapsed.
> [!question] Can callouts be nested? 
> > [!todo] Yes!, they can. 
> > > [!example] You can even use multiple layers of nesting.

Hugo 使用默认的渲染器 goldmark.renderer 将上述 Callouts 转换为:

<blockquote>
  <p>[!tip] Title-only callout</p>
</blockquote>

<blockquote>
  <p>[!faq]- Are callouts foldable?
    Yes! In a foldable callout, the contents are hidden when the callout is collapsed.</p>
</blockquote>

<blockquote>
  <p>[!question] Can callouts be nested?</p>
  <blockquote>
    <p>[!todo] Yes!, they can.</p>
    <blockquote>
      <p>[!example] You can even use multiple layers of nesting.</p>
    </blockquote>
  </blockquote>
</blockquote>

上述例子中可以发现:

  1. blockquote 不是每一行都单独使用一个 <p> 标签,这个可以通过在这一行后加入两个空格强制让其为下一行增加 <p>标签 1
  2. blockquote 原生支持嵌套语法,不需要做调整

因此,我们主要需要关注前两个 Callouts 的移植,其在 Obsidian 渲染出来的结果如下:

<div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout">
  <div class="callout-title" dir="auto">
    <div class="callout-icon"><svg ..></svg></div>
    <div class="callout-title-inner">Title-only callout</div>
  </div>
</div>

<div data-callout-metadata="" data-callout-fold="-" data-callout="faq" class="callout is-collapsible">
  <div class="callout-title" dir="auto">
    <div class="callout-icon"><svg ..></svg></div>
    <div class="callout-title-inner">Are callouts foldable?</div>
    <div class="callout-fold"><svg ..></svg></div>
  </div>
  <div class="callout-content" style="">
    <p dir="auto">Yes! In a foldable callout, the contents are hidden when collapsed.</p>
  </div>
</div>

由于找到对应的结尾标签比较困难,因此直接考虑在 blockquote 上改变。

layouts/partials/obsidian.md 中定义 obsidian 函数进行转义2

{{- define "partials/obsidian.html" -}}
  {{- $content := .content -}}
  {{- $pageRelLink := .pageRelLink -}}
  {{- $regexCallout := `<blockquote>\n<p>\[!(.+)\](-?) (.+)?\<` -}}
  {{- $callouts := $content | findRE $regexCallout -}}
  {{- /* Convert Callouts */ -}}
  {{- range $callouts -}}
    {{- $callout := findRESubmatch $regexCallout . -}}
    {{- $type := index $callout 0 1 -}}
    {{- $isFold := index $callout 0 2 -}}
    {{- $title := index $callout 0 3 -}}
      {{- $content = replaceRE $regexCallout `<blockquote class="callout" data-callout="$1" callout-fold="$2"><p><div class="title">$3</div><` $content 1 -}}
  {{- end -}}
  {{- /* Output content */ -}}
  {{- $content | safeHTML -}}
{{- end -}}

这样,通过增加了 data-callout 字段可以进行不同类型 Callout 的区分,并且转义的过程中不动不容易正则匹配的反括号,避免过于复杂的逻辑。

layouts/_default/single.html 中将 {{.Content}} 替换为 {{- partial "partials/obsidian.html" (dict "content" .Content "pageRelLink" .Page.RelPermalink) -}} 使我们的函数进行转义。

效果:

Title-only callout

[!faq]- Are callouts foldable? Yes! In a foldable callout, the contents are hidden when the callout is collapsed.

Can callouts be nested?

Yes!, they can.

You can even use multiple layers of nesting.


Last modified on 2024-08-07