Hugo PaperMod 安装

I. 主题文件

Hugo PaperMod - GitHub

II. 主题安装

2.1 首次安装(2选1)

参考👉 Installation · hugo-PaperMod Wiki

1
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

2.2 更换旧主题(2选1)

2.2.1 删除旧主题子模块

先删除当前的主题子模块引用。假设当前主题是 old-theme,你可以执行以下命令:

1
2
3
4
git submodule deinit -f themes/old-theme
rm -rf .git/modules/themes/old-theme
git rm -f themes/old-theme
git commit -m "Remove old theme"

上述命令将会移除 Git 子模块引用,并且在版本控制中提交该变动。

2.2.2 添加新主题子模块

接下来,你可以添加新的主题子模块。如 PaperMod 主题,可以执行:

1
2
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
git submodule update --init --recursive

2.2.3 更新网站配置文件

在你的 config.toml 或 config.yaml 中修改 theme 配置项为新的主题名称。例如:

1
theme = "PaperMod"

2.3 主题更新

在 Hugo 站点根目录下运行:

1
git submodule update --remote --merge

📢 注意:
如果是从其他地方 copy | clone 过来的站点,有可能遇到(参考👉 从零开始搭建个人博客网站系列 - 知乎 ):

1
WARN  found no layout file for "html" for kind "home": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

这个时候需要重新 clone 一下主题(参考👉 Installation · adityatelange/hugo-PaperMod Wiki ):

1
git submodule update --init --recursive

2.4 查看版本

1
2
cd themes/PaperMod/
git describe --tags

III. 主题配置

3.1 站点目录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.(site root)
├── assets
│   └── css
│       └── extended
│           └── blank.css
├── content
│   ├── posts
│   │   ├── _index.md
│   │   ├── emoji-support.md
│   │   ├── markdown-syntax.md
│   │   └── math-typesetting.md
│   ├── archives.md
│   └── search.md
├── i18n
│   └── en.yaml
├── layouts
│   ├── _default
│   │   ├── _markup
│   │   |   └── render-link.html
│   │   ├── archives.html
│   │   └── terms.html
│   └── partials
│       └── toc.html
├── static
│   ├── android-chrome-192x192.png
│   ├── android-chrome-512x512.png
│   ├── apple-touch-icon.png
│   ├── favicon-16x16.png
│   ├── favicon-32x32.png
│   ├── favicon.ico
│   └── papermod-cover.png
├── themes
|   └── PaperMod
├── hugo.toml.bak
└── config.yml

3.2 站点配置

3.2.1 备份旧配置

1
hugo.toml.bak

3.2.2 配置新站点

官方示例 配置 config.yml,内容如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
baseURL: "https://examplesite.com/"
title: ExampleSite
paginate: 5
theme: PaperMod

enableRobotsTXT: true
buildDrafts: false
buildFuture: false
buildExpired: false

minify:
  disableXML: true
  minifyOutput: true

params:
  env: production # to enable google analytics, opengraph, twitter-cards and schema.
  title: ExampleSite
  description: "ExampleSite description"
  keywords: [Blog, Portfolio, PaperMod]
  author: Me
  # author: ["Me", "You"] # multiple authors
  images: ["<link or path of image for opengraph, twitter-cards>"]
  DateFormat: "January 2, 2006"
  defaultTheme: auto # dark, light
  disableThemeToggle: false

  ShowReadingTime: true
  ShowShareButtons: true
  ShowPostNavLinks: true
  ShowBreadCrumbs: true
  ShowCodeCopyButtons: false
  ShowWordCount: true
  ShowRssButtonInSectionTermList: true
  UseHugoToc: true
  disableSpecial1stPost: false
  disableScrollToTop: false
  comments: false
  hidemeta: false
  hideSummary: false
  showtoc: false
  tocopen: false

  assets:
    # disableHLJS: true # to disable highlight.js
    # disableFingerprinting: true
    favicon: "<link / abs url>"
    favicon16x16: "<link / abs url>"
    favicon32x32: "<link / abs url>"
    apple_touch_icon: "<link / abs url>"
    safari_pinned_tab: "<link / abs url>"

  label:
    text: "Home"
    icon: /apple-touch-icon.png
    iconHeight: 35

  # profile-mode
  profileMode:
    enabled: false # needs to be explicitly set
    title: ExampleSite
    subtitle: "This is subtitle"
    imageUrl: "<img location>"
    imageWidth: 120
    imageHeight: 120
    imageTitle: my image
    buttons:
      - name: Posts
        url: posts
      - name: Tags
        url: tags

  # home-info mode
  homeInfoParams:
    Title: "Hi there \U0001F44B"
    Content: Welcome to my blog

  socialIcons:
    - name: x
      url: "https://x.com/"
    - name: stackoverflow
      url: "https://stackoverflow.com"
    - name: github
      url: "https://github.com/"

  analytics:
    google:
      SiteVerificationTag: "XYZabc"
    bing:
      SiteVerificationTag: "XYZabc"
    yandex:
      SiteVerificationTag: "XYZabc"

  cover:
    hidden: true # hide everywhere but not in structured data
    hiddenInList: true # hide on list pages and home
    hiddenInSingle: true # hide on single page

  editPost:
    URL: "https://github.com/<path_to_repo>/content"
    Text: "Suggest Changes" # edit text
    appendFilePath: true # to append file path to Edit link

  # for search
  # https://fusejs.io/api/options.html
  fuseOpts:
    isCaseSensitive: false
    shouldSort: true
    location: 0
    distance: 1000
    threshold: 0.4
    minMatchCharLength: 0
    limit: 10 # refer: https://www.fusejs.io/api/methods.html#search
    keys: ["title", "permalink", "summary", "content"]
menu:
  main:
    - identifier: categories
      name: categories
      url: /categories/
      weight: 10
    - identifier: tags
      name: tags
      url: /tags/
      weight: 20
    - identifier: example
      name: example.org
      url: https://example.org
      weight: 30
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
pygmentsUseClasses: true
markup:
  highlight:
    noClasses: false
    # anchorLineNos: true
    # codeFences: true
    # guessSyntax: true
    # lineNos: true
    # style: monokai

3.3 模式选择

参考👉 Features · hugo-PaperMod Wiki

Hugo PaperMod 有:常规模式(Regular Mode)、首页信息模式(Home-Info Mode)、个人资料模式(Profile Mode) 三种模式可供选择。

这里选择“个人资料模式(Profile Mode)”模式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
params:
  profileMode:
    enabled: true
    title: "<Title>" # optional default will be site title
    subtitle: "This is subtitle"
    imageUrl: "<image link>" # optional
    imageTitle: "<title of image as alt>" # optional
    imageWidth: 120 # custom size
    imageHeight: 120 # custom size
    buttons:
      - name: Archive
        url: "/archive"
      - name: Github
        url: "https://github.com/"

  socialIcons: # optional
    - name: "<platform>"
      url: "<link>"
    - name: "<platform 2>"
      url: "<link2>"

💡 Tips:
请为 imageUrl 设定一个值,如:imageUrl: "apple-touch-icon.png" 这里 apple-touch-icon.png 须为实际存在的图像文件,文件默认存放在站点 ../static 目录下。

3.4 自定义导航

3.4.1 增加导航菜单

这里修改站点配置文件,增加 archivessearch 导航菜单,并调整显示顺序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
menu:
  main:
    - identifier: archives
      name: archives
      url: archives/
      weight: 10
    - identifier: categories
      name: categories
      url: categories/
      weight: 20
    - identifier: tags
      name: tags
      url: tags/
      weight: 30
    - identifier: search
      name: search
      url: search/
      weight: 40

3.4.2 创建菜单页面

为新增加的导航菜单创建页面。在站点 ../content/ 目录增加两个文件。

1
2
3
4
.(site root)
└── content
    ├── archives.md
    └── search.md

archives.md 文件内容如下:

1
2
3
4
5
6
---
title: "存档"
layout: "archives"
url: "/archives"
summary: "archives"
---

search.md 文件内容如下:

1
2
3
4
5
---
title: "搜索"
layout: "search"
placeholder: 输入关键字,然后回车 ...
---

3.5 中文翻译

💡 Tips:
为了展示中文效果,建议在站点 ../content/posts/ 目录下至少发布三篇文章,每篇文章元数据包括文章的“分类”及“标签”,如下:

1
2
3
4
5
6
.(site root)
└── content
    └── posts
        ├── emoji-support.md
        ├── markdown-syntax.md
        └── math-typesetting.md

3.5.1 导航菜单

直接在站点配置文件 config.yml 中修改,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
menu:
  main:
    - identifier: archives
      name: 归档
      url: archives/
      weight: 10
    - identifier: categories
      name: 分类
      url: categories/
      weight: 20
    - identifier: tags
      name: 标签
      url: tags/
      weight: 30
    - identifier: search
      name: 搜索
      url: search/
      weight: 40

3.5.2 页面元素

参考👉 PaperMod 主题配置 - 修改 html 模板

1、首先汉化的是分类、标签页中文显示。创建 ../layouts/_default/terms.html 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{{- define "main" }}

{{- if .Title }}
<header class="page-header">
    {{- if eq .Title "Categories" }}
    <h1>{{ "分类" }}</h1>
    {{- end }}
    {{- if eq .Title "Tags" }}
        <h1>{{ "标签" }}</h1>
        <!-- <h1>🔖{{ .Title }}</h1> -->
    {{- end }}
    <!-- <h1>{{ .Title }}</h1> -->
    {{- if .Description }}
    <div class="post-description">
        {{ .Description }}
    </div>
    {{- end }}
</header>
{{- end }}

<!-- 原始 -->

<ul class="terms-tags">
    {{- $type := .Type }}
    {{- range $key, $value := .Data.Terms.Alphabetical }}
    {{- $name := .Name }}
    {{- $count := .Count }}
    {{- with $.Site.GetPage (printf "/%s/%s" $type $name) }}
    <li>
        <a href="{{ .Permalink }}">{{ .Name }} <sup><strong><sup>{{ $count }}</sup></strong></sup> </a>
    </li>
    {{- end }}
    {{- end }}
</ul>

{{- end }}{{/* end main */ -}}

2、汉化文章页 <<PREVNEXT>> 等页面元素,修改站点配置文件 config.yml,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 语言设置
defaultContentLanguage: "zh"

# 单语言,必须在此处,以下设置之前
languages:
  zh:
    # RFC 5646 语言标记, Hugo 使用此值填充内置 RSS 模板中的语言元素和内置别名模板中 html 元素的 lang 属性
    languageCode: "zh-CN"
    # 语言名称,通常在渲染语言切换器时使用
    languageName: "中文"

3.5.3 Posts 页

汉化 exampleSite/posts/ 页面 Posts 显示为“文章”。在站点 ../content/posts/ 下创建 _index.md 文件,内容如下:

1
2
3
---
title: "文章"
---

3.6 文章目录(大纲)

参考👉 Hugo侧边目录 | 3rd’s Blog

1、在站点配置文件 config.yml 中启用目录(大纲),如下:

1
2
3
4
params:
  # ...
  showtoc: true  # 显示目录
  tocopen: true  # 自动展开目录

2、PaperMod 主题默认将文章目录放在顶部,不方便阅读时跳转,修改为侧边目录。创建 toc.htmltoc.css 两个文件,目录结构如下:

1
2
3
4
5
6
7
8
.(site root)
├── assets
│   └── css
│       └── extended
│           └── toc.css
└── layouts
    └── partials
        └── toc.html

toc.html 内容如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
{{- $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content -}}
{{- $has_headers := ge (len $headers) 1 -}}
{{- if $has_headers -}}
<aside id="toc-container" class="toc-container wide">
    <div class="toc">
        <details {{if (.Param "TocOpen") }} open{{ end }}>
            <summary accesskey="c" title="(Alt + C)">
                <span class="details">{{- i18n "toc" | default "Table of Contents" }}</span>
            </summary>

            <div class="inner">
                {{- $largest := 6 -}}
                {{- range $headers -}}
                {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}}
                {{- $headerLevel := len (seq $headerLevel) -}}
                {{- if lt $headerLevel $largest -}}
                {{- $largest = $headerLevel -}}
                {{- end -}}
                {{- end -}}

                {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}}

                {{- $.Scratch.Set "bareul" slice -}}
                <ul>
                    {{- range seq (sub $firstHeaderLevel $largest) -}}
                    <ul>
                        {{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}}
                        {{- end -}}
                        {{- range $i, $header := $headers -}}
                        {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}}
                        {{- $headerLevel := len (seq $headerLevel) -}}

                        {{/* get id="xyz" */}}
                        {{- $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }}

                        {{- /* strip id="" to leave xyz, no way to get regex capturing groups in hugo */ -}}
                        {{- $cleanedID := replace (replace $id "id=\"" "") "\"" "" }}
                        {{- $header := replaceRE "<h[1-6].*?>((.|\n])+?)</h[1-6]>" "$1" $header -}}

                        {{- if ne $i 0 -}}
                        {{- $prevHeaderLevel := index (findRE "[1-6]" (index $headers (sub $i 1)) 1) 0 -}}
                        {{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}}
                        {{- if gt $headerLevel $prevHeaderLevel -}}
                        {{- range seq $prevHeaderLevel (sub $headerLevel 1) -}}
                        <ul>
                            {{/* the first should not be recorded */}}
                            {{- if ne $prevHeaderLevel . -}}
                            {{- $.Scratch.Add "bareul" . -}}
                            {{- end -}}
                            {{- end -}}
                            {{- else -}}
                            </li>
                            {{- if lt $headerLevel $prevHeaderLevel -}}
                            {{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}}
                            {{- if in ($.Scratch.Get "bareul") . -}}
                        </ul>
                        {{/* manually do pop item */}}
                        {{- $tmp := $.Scratch.Get "bareul" -}}
                        {{- $.Scratch.Delete "bareul" -}}
                        {{- $.Scratch.Set "bareul" slice}}
                        {{- range seq (sub (len $tmp) 1) -}}
                        {{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}}
                        {{- end -}}
                        {{- else -}}
                    </ul>
                    </li>
                    {{- end -}}
                    {{- end -}}
                    {{- end -}}
                    {{- end }}
                    <li>
                        <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a>
                        {{- else }}
                    <li>
                        <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a>
                        {{- end -}}
                        {{- end -}}
                        <!-- {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} -->
                        {{- $firstHeaderLevel := $largest }}
                        {{- $lastHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers (sub (len $headers) 1)) 1) 0)) }}
                    </li>
                    {{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}}
                    {{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) }}
                </ul>
                {{- else }}
                </ul>
                </li>
                {{- end -}}
                {{- end }}
                </ul>
            </div>
        </details>
    </div>
</aside>
<script>
    let activeElement;
    let elements;
    
    document.addEventListener('DOMContentLoaded', function (event) {
        checkTocPosition();
    
        elements = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]');
        if (elements.length > 0) {
            // Make the first header active
            activeElement = elements[0];
            const id = encodeURI(activeElement.getAttribute('id')).toLowerCase();
            document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active');
        }
    
        // Add event listener for the "back to top" link
        const topLink = document.getElementById('top-link');
        if (topLink) {
            topLink.addEventListener('click', (event) => {
                // Prevent the default action
                event.preventDefault();
    
                // Smooth scroll to the top
                window.scrollTo({ top: 0, behavior: 'smooth' });
            });
        }
    }, false);
    
    window.addEventListener('resize', function(event) {
        checkTocPosition();
    }, false);
    
    window.addEventListener('scroll', () => {
        // Get the current scroll position
        const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
    
        // Check if the scroll position is at the top of the page
        if (scrollPosition === 0) {
            return;
        }
    
        // Ensure elements is a valid NodeList
        if (elements && elements.length > 0) {
            // Check if there is an object in the top half of the screen or keep the last item active
            activeElement = Array.from(elements).find((element) => {
                if ((getOffsetTop(element) - scrollPosition) > 0 && 
                    (getOffsetTop(element) - scrollPosition) < window.innerHeight / 2) {
                    return element;
                }
            }) || activeElement;
    
            elements.forEach(element => {
                const id = encodeURI(element.getAttribute('id')).toLowerCase();
                const tocLink = document.querySelector(`.inner ul li a[href="#${id}"]`);
                if (element === activeElement){
                    tocLink.classList.add('active');
    
                    // Ensure the active element is in view within the .inner container
                    const tocContainer = document.querySelector('.toc .inner');
                    const linkOffsetTop = tocLink.offsetTop;
                    const containerHeight = tocContainer.clientHeight;
                    const linkHeight = tocLink.clientHeight;
    
                    // Calculate the scroll position to center the active link
                    const scrollPosition = linkOffsetTop - (containerHeight / 2) + (linkHeight / 2);
                    tocContainer.scrollTo({ top: scrollPosition, behavior: 'smooth' });
                } else {
                    tocLink.classList.remove('active');
                }
            });
        }
    }, false);
    
    const main = parseInt(getComputedStyle(document.body).getPropertyValue('--article-width'), 10);
    const toc = parseInt(getComputedStyle(document.body).getPropertyValue('--toc-width'), 10);
    const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 10);
    
    function checkTocPosition() {
        const width = document.body.scrollWidth;
    
        if (width - main - (toc * 2) - (gap * 4) > 0) {
            document.getElementById("toc-container").classList.add("wide");
        } else {
            document.getElementById("toc-container").classList.remove("wide");
        }
    }
    
    function getOffsetTop(element) {
        if (!element.getClientRects().length) {
            return 0;
        }
        let rect = element.getBoundingClientRect();
        let win = element.ownerDocument.defaultView;
        return rect.top + win.pageYOffset;   
    }
    
</script>
{{- end }}

toc.css 内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
:root {
    --nav-width: 1380px;
    --article-width: 650px;
    --toc-width: 300px;
}

.toc {
    margin: 0 2px 40px 2px;
    border: 1px solid var(--border);
    background: var(--entry);
    border-radius: var(--radius);
    padding: 0.4em;
}

.toc-container.wide {
    position: absolute;
    height: 100%;
    border-right: 1px solid var(--border);
    left: calc((var(--toc-width) + var(--gap)) * -1);
    top: calc(var(--gap) * 2);
    width: var(--toc-width);
}

.wide .toc {
    position: sticky;
    top: var(--gap);
    border: unset;
    background: unset;
    border-radius: unset;
    width: 100%;
    margin: 0 2px 40px 2px;
}

.toc details summary {
    cursor: zoom-in;
    margin-inline-start: 20px;
    padding: 12px 0;
}

.toc details[open] summary {
    font-weight: 500;
}

.toc-container.wide .toc .inner {
    margin: 0;
}

.active {
    font-size: 110%;
    font-weight: 600;
}

.toc ul {
    list-style-type: circle;
}

.toc .inner {
    margin: 0 0 0 20px;
    padding: 0px 15px 15px 20px;
    font-size: 16px;

    /*目录显示高度*/
    max-height: 83vh;
    overflow-y: auto;
}

.toc .inner::-webkit-scrollbar-thumb {  /*滚动条*/
    background: var(--border);
    border: 7px solid var(--theme);
    border-radius: var(--radius);
}

.toc li ul {
    margin-inline-start: calc(var(--gap) * 0.5);
    list-style-type: none;
}

.toc li {
    list-style: none;
    font-size: 0.95rem;
    padding-bottom: 5px;
}

.toc li a:hover {
    color: var(--secondary);
}

3.7 新标签打开链接

参考👉 设置以新标签打开链接 - Dvel’s Blog

创建 ../layouts/_default/_markup/render-link.html 文件,内容如下:

1
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>{{ .Text | safeHTML }}</a>

该方法自动为所有文章内的链接加上了 target="_blank" rel="noopener"

但是站内链接也都加上了 target="_blank" rel="noopener"

站内链接建议写 path

1
2
3
[title](/foo/bar/)
↓ 会被简单解析为: ↓
<a href="/foo/bar/">title</a>

3.8 Waline 评论系统

参考👉 使用自建 Waline | Razeen`s Blog

1、在站点配置文件 config.yml 中启用评论功能,如下:

1
2
3
params:
	# ...
	comments: true  # 评论功能

2、复制主题目录 ../themes/PaperMod/layouts/partials/comments.html 文件,粘贴到站点根目录 ../layouts/partials/ 下。

3、编辑 comments.html 内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
{{- /* Comments area start */ -}}
{{- /* to add comments read => https://gohugo.io/content-management/comments/ */ -}}

<head>
    <!-- ... -->
    <link
        rel="stylesheet"
        href="https://unpkg.com/@waline/client@v3/dist/waline.css"
    />
    <!-- ... -->
</head>

<body>
    <!-- ... -->
    <div id="waline"></div>
    <script type="module">
    import { init } from 'https://unpkg.com/@waline/client@v3/dist/waline.js';

    const locale = {
        nick: '昵称',
        nickError: '请填写昵称',
        mail: '邮箱',
        mailError: '请填写正确的邮件地址',
        link: '网址',
        optional: '可选',
        placeholder: '仅填写昵称即可发表回复。\n填写邮箱可收到回复提醒。\n评论区支持 Markdown 语法及预览。\n',
        sofa: '来发评论吧~',
        submit: '提交',
        like: '喜欢',
        cancelLike: '取消喜欢',
        reply: '回复',
        cancelReply: '取消回复',
        comment: '评论',
        refresh: '刷新',
        more: '加载更多...',
        preview: '预览',
        emoji: '表情',
        uploadImage: '上传图片',
        seconds: '秒前',
        minutes: '分钟前',
        hours: '小时前',
        days: '天前',
        now: '刚刚',
        uploading: '正在上传',
        login: '登录',
        logout: '退出',
        admin: '博主',
        sticky: '置顶',
        word: '字',
        wordHint: '评论字数应在 $0 到 $1 字之间!\n当前字数:$2',
        anonymous: '匿名',
        level0: '潜水',
        level1: '冒泡',
        level2: '吐槽',
        level3: '活跃',
        level4: '话痨',
        level5: '传说',
        gif: '表情包',
        gifSearchPlaceholder: '搜索表情包',
        profile: '个人资料',
        approved: '通过',
        waiting: '待审核',
        spam: '垃圾',
        unsticky: '取消置顶',
        oldest: '按倒序',
        latest: '按正序',
        hottest: '按热度',
        reactionTitle: '你认为这篇文章怎么样?',
    };

    init({
        el: '#waline',
        serverURL: 'https://waline.example.com',
        locale,
        wordLimit: 500,   //评论字数限制
        // emoji: false,     // 表情
        emoji: [
            '//unpkg.com/@waline/emojis@1.2.0/qq',
            '//unpkg.com/@waline/emojis@1.2.0/weibo',
            '//unpkg.com/@waline/emojis@1.2.0/bmoji',
        ],
        search: false,    // GIF 表情包
        reaction: false,  // 文章反应
        requiredMeta: ['nick'],
        pageSize: 10,
        imageUploader: false,
        copyright: true,
        pageview: true,
        like: false,
        dark: 'html[class="body.dark"]',
    });
    </script>
</body>

{{- /* Comments area end */ -}}

4、暗黑模式适配(参考👉 CSS 变量 | Waline )。站点目录下创建 ../assets/css/extended/comments.css 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* 日间模式 */
#waline {
  /* 字体大小 */
  --waline-font-size: 18px;

  /* 常规颜色 */
  --waline-white: #fff;
  --waline-light-grey: #999;
  --waline-dark-grey: #666;

  /* 主题色 */
  --waline-theme-color: #27ae60;
  --waline-active-color: #2ecc71;

  /* 布局颜色 */
  --waline-color: #444;
  --waline-bg-color: #fff;
  --waline-bg-color-light: #f8f8f8;
  --waline-bg-color-hover: #f0f0f0;
  --waline-border-color: #ddd;
  --waline-disable-bg-color: #f8f8f8;
  --waline-disable-color: #bbb;
  --waline-code-bg-color: #282c34;

  /* 特殊颜色 */
  --waline-bq-color: #f0f0f0;

  /* 头像 */
  --waline-avatar-size: 3.25rem;
  --waline-m-avatar-size: calc(var(--waline-avatar-size) * 9 / 13);

  /* 徽章 */
  --waline-badge-color: #3498db;
  --waline-badge-font-size: 0.775em;

  /* 信息 */
  --waline-info-bg-color: #f8f8f8;
  --waline-info-color: #999;
  --waline-info-font-size: 0.625em;

  /* 渲染选择 */
  --waline-border: 1px solid var(--waline-border-color);
  --waline-avatar-radius: 50%;
  --waline-box-shadow: none;
}

/* 暗黑模式 */
body.dark #waline {
  /* 常规颜色 */
  --waline-white: #000;              /* 这是用于表示白色的CSS变量, 在暗模式下,白色将变为黑色*/
  --waline-light-grey: #666;         /* 浅灰色的CSS变量, 在暗模式下,变为深灰色 */
  --waline-dark-grey: #999;          /* 示深灰色, 在暗模式下,变为浅灰色 */

  /* 布局颜色 */
  --waline-color: #9B9C9D;           /* 一般文本颜色 */
  --waline-bg-color: #1D1E20;        /* 背景颜色 */
  --waline-bg-color-light: #272727;  /* 较浅的背景颜色 */
  --waline-border-color: #9B9C9D;    /* 边框颜色 */
  --waline-disable-bg-color: #444;   /* 禁用状态的背景颜色 */
  --waline-disable-color: #272727;   /* 禁用状态的文本颜色 */

  /* 特殊颜色 */
  --waline-bq-color: #272727;        /* 引用块颜色 */

  /* 其他颜色 */
  --waline-info-bg-color: #272727;   /* 信息块背景颜色 */
  --waline-info-color: #666;         /* 信息块字体颜色 */
}

3.9 版权声明

参考👉 PaperMod 添加文章版权声明 | Tofuwine’s Blog

3.9.1 版权页面

站点目录下创建 ../layouts/partials/copyright.html 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<div class="pe-copyright">
    <img src="/imgs/cc/cc.svg" width="75" height="75" align="right" />
    <!-- <hr> -->
    <blockquote>
    {{ if .Param "reposted" }}
        <p><strong>本文为转载内容,原文信息如下:</strong></p>
        <p><strong>原文标题:</strong>{{- .Param "repostedTitle" -}}</p>
        <p><strong>原文作者:</strong>{{- .Param "repostedAuthor" -}}</p>
        <p><strong>原文链接:</strong><a href="{{- .Param "repostedLink" -}}" target="_blank">{{- .Param "repostedLink" -}}</a></p>
        <p><strong>版权声明:</strong>如有侵权,请<a href="mailto://{{ .Param "contactEmail" }}">联系本站</a>删除。</p>
    {{ else }}
        <p><strong>文章标题:</strong>{{ .Title }}</p>
        <p><strong>本文作者:</strong>{{ .Param "author" }}</p>
        <p><strong>本文链接:</strong><a href="{{ .Permalink }}" target="_blank">{{ .Permalink }}</a></p>
        <p><strong>版权声明:</strong>本网站所有文章除特别声明外,均采用 <a href="{{- .Param "licenseLink" -}}" target="_blank">{{- .Param "licenseName" -}}</a> 许可协议。转载请注明出处!</p>
    {{ end }}
    </blockquote>
</div>

📌 说明:
本示例版权水印文件存放路径 ../static/imgs/cc/cc.svg

3.9.2 添加样式

在站点目录下创建 ../assets/css/extended/copyright.css 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.pe-copyright {
    margin-top: 50px;  /* 上边距 */
    font-size: 14px;
    border: 3px solid #4A4A4A;
}

.pe-copyright hr {
    border-style: dashed;
    color: #e26c56;
}

.pe-copyright blockquote {
    margin: 10px 0;  /* 外边距:上下边距为 10px,左右为 0 */
    padding: 10px 10px;  /* 内边距:上下边距为 20px,左右为 10px */
}

.pe-copyright a {
    box-shadow: 0 1px;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
}

3.9.3 加入文章页

将 PaperMod 主题目录下 ../themes/PaperMod/layouts/_default/single.html 文件复制到站点目录 ../layouts/_default/single.html ,编辑 single.html 文件,在 footer 节点上添加如下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<article class="post-single">
    {{- if .Content }}
    <div class="post-content">
        ...
    </div>
    {{- end }}    

    <!-- 加入版权声明 -->
    {{ if .Param "enableCopyright" }}
    {{ partial "copyright.html" . }}
    {{ end }}

    <footer class="post-footer">
        ...
    </footer>
</article>

3.9.4 启用版权声明

在站点配置文件 config.yml 中加入以下配置:

1
2
3
params:
	# ...
	enableCopyright: true  # 启用版权声明
3.9.4.1 原创文章

原创文章需在文章 frontmatter 中添加以下参数:(以下仅为示例,请根据实际自行修改)

1
2
3
4
5
---
author: <Author>
licenseLink: "https://creativecommons.org/licenses/by-nc/4.0/"
licenseName: "CC BY-NC 4.0"
---

也可直接在站点配置文件 config.yml 中加入以下配置:

1
2
3
4
5
6
params:
	# ...
	author: <Author>
    licenseLink: "https://creativecommons.org/licenses/by-nc/4.0/deed.zh-hans"
    licenseName: "CC BY-NC-SA"
    contactEmail: <userName@example.com>

其中 author 为 Hugo-PaperMod 已有参数。

3.9.4.2 转载文章

转载文章需在文章 frontmatter 中添加以下参数:

1
2
3
4
5
6
7
---
reposted: true
repostedTitle: "修改为原文章标题"
repostedAuthor: "修改为原文章作者名"
repostedLink: "修改为原文章链接"
contactEmail: your email
---

其中 contactEmail 参数可在 hugo 配置中指定全局默认值:

1
2
3
params:
	# ...
	contactEmail: <Your Email>

3.10 代码块优化

参考👉 Hugo以及PaperMod主题的配置 | 似水

3.10.1 代码块高亮

修改站点配置文件 config.yml ,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
params:
  # ...
  assets:
    disableHLJS: true # to disable highlight.js

# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
pygmentsUseClasses: false 
markup:
  goldmark:
    renderer:
      unsafe: true
  highlight:
    noClasses: true
    anchorLineNos: false
    codeFences: true
    guessSyntax: true
    lineNos: true
    style: monokai
    lineNumbersInTable: false

    # 参数说明:https://gohugo.io/functions/transform/highlight/
    # lineNos:是否在每行开头显示数字。 默认为 false
    # hl_Lines:以空格分隔的列表,用于强调高亮代码中的行。 要强调第 2、3、4 和 7 行,请将该值设为 2-4 7。 该选项独立于行无起始选项。
    # lineNoStart=1:第一行开头要显示的数字。 如果 lineNos 为 false 则与此无关。 默认值为 1
    # anchorLineNos:是否将每个行号渲染为 HTML 锚点元素,并将周围 span 元素的 id 属性设置为行号。 如果 lineNos 为 false,则与此无关。 默认为 false
    # lineAnchors:在将行号作为 HTML 锚点元素呈现时,将此值预置到周围 span 元素的 id 属性中。 当页面包含两个或多个代码块时,这将提供唯一的 id 属性。 如果 lineNos 或 anchorLineNos 为 false,则与此无关。
    # hl_inline:是否在没有包装容器的情况下渲染突出显示的代码。默认为 false
    # codeFences:是否高亮显示有栅栏的代码块。 默认为 true
    # guessSyntax:如果 LANG 参数为空或设置为没有相应词法的语言,是否自动检测语言。 如果无法自动检测语言,则退回到纯文本词法。 默认为 false
    # lineNumbersInTable:是否在 HTML 表格的两个单元格中显示高亮显示的代码。 左侧表格单元格包含行号,右侧表格单元格包含代码。 如果 lineNos 为 false 则无关。 默认为 true
    # noClasses:是否使用内联 CSS 样式而不是外部 CSS 文件。 要使用外部 CSS 文件,请将此值设为 false,并使用 hugo gen chromastyles 命令生成 CSS 文件。 默认值为 true

3.10.2 代码块展开/折叠

1、复制主题目录 ../themes/PaperMod/layouts/partials/footer.html 文件,粘贴到站点根目录 ../layouts/partials/ 下。 编辑 footer.html,内容如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
{{- if not (.Param "hideFooter") }}
<footer class="footer">
    {{- if not site.Params.footer.hideCopyright }}
        {{- if site.Copyright }}
        <span>{{ site.Copyright | markdownify }}</span>
        {{- else }}
        <span>&copy; {{ now.Year }} <a href="{{ "" | absLangURL }}">{{ site.Title }}</a></span>
        {{- end }}
        {{- print " · "}}
    {{- end }}

    {{- with site.Params.footer.text }}
        {{ . | markdownify }}
        {{- print " · "}}
    {{- end }}

    <span>
        Powered by
        <a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
        <a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
    </span>
</footer>
{{- end }}

{{- if (not site.Params.disableScrollToTop) }}
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
        <path d="M12 6H0l6-6z" />
    </svg>
</a>
{{- end }}

{{- partial "extend_footer.html" . }}

<script>
    let menu = document.getElementById('menu')
    if (menu) {
        menu.scrollLeft = localStorage.getItem("menu-scroll-position");
        menu.onscroll = function () {
            localStorage.setItem("menu-scroll-position", menu.scrollLeft);
        }
    }

    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();
            var id = this.getAttribute("href").substr(1);
            if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
                    behavior: "smooth"
                });
            } else {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
            }
            if (id === "top") {
                history.replaceState(null, null, " ");
            } else {
                history.pushState(null, null, `#${id}`);
            }
        });
    });

</script>

{{- if (not site.Params.disableScrollToTop) }}
<script>
    var mybutton = document.getElementById("top-link");
    window.onscroll = function () {
        if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
            mybutton.style.visibility = "visible";
            mybutton.style.opacity = "1";
        } else {
            mybutton.style.visibility = "hidden";
            mybutton.style.opacity = "0";
        }
    };

</script>
{{- end }}

{{- if (not site.Params.disableThemeToggle) }}
<script>
    document.getElementById("theme-toggle").addEventListener("click", () => {
        if (document.body.className.includes("dark")) {
            document.body.classList.remove('dark');
            localStorage.setItem("pref-theme", 'light');
        } else {
            document.body.classList.add('dark');
            localStorage.setItem("pref-theme", 'dark');
        }
    })

</script>
{{- end }}

{{- if (and (eq .Kind "page") (ne .Layout "archives") (ne .Layout "search") (.Param "ShowCodeCopyButtons")) }}
<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const copybutton = document.createElement('button');
        copybutton.classList.add('copy-code');
        copybutton.innerHTML = '{{- i18n "code_copy" | default "copy" }}';

        function copyingDone() {
            copybutton.innerHTML = '{{- i18n "code_copied" | default "copied!" }}';
            setTimeout(() => {
                copybutton.innerHTML = '{{- i18n "code_copy" | default "copy" }}';
            }, 2000);
        }

        copybutton.addEventListener('click', (cb) => {
            if ('clipboard' in navigator) {
                // 不包含样式的span的内容拼接起来,也是代码块的内容
                let x = codeblock.getElementsByTagName("span");
                let noLineNumContent = "";
                for (i = 0; i < x.length; i++) {
                    if (x[i].style.display || x[i].style.color);
                    else noLineNumContent += x[i].textContent;
                }
                navigator.clipboard.writeText(noLineNumContent);
                copyingDone();
                return;
            }

            const range = document.createRange();
            range.selectNodeContents(codeblock);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            try {
                document.execCommand('copy');
                copyingDone();
            } catch (e) { };
            selection.removeRange(range);
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(copybutton);
        } else if (container.parentNode.firstChild == container) {
            // td containing LineNos
        } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
            // table containing LineNos and code
            codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
        } else {
            // code blocks not having highlight as parent class
            codeblock.parentNode.appendChild(copybutton);
        }
    });
</script>

<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const unfoldbtn = document.createElement('button');
        unfoldbtn.classList.add('unfoldbtn');
        unfoldbtn.innerHTML = '展开';

        unfoldbtn.addEventListener('click', (cb) => {
            if (container.firstChild.firstChild.classList.contains('unfold')) {
                container.firstChild.firstChild.classList.remove('unfold');
                unfoldbtn.innerHTML = '展开';
            } else {
                container.firstChild.firstChild.classList.add('unfold');
                unfoldbtn.innerHTML = '折叠';
            }
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(unfoldbtn);
        }
    });
</script>

{{- end }}

2、复制主题目录 ../themes/PaperMod/assets/css/extended/blank.css 文件,粘贴到站点根目录 ../assets/css/extended/ 下。 编辑 blank.css,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
This is just a placeholder blank stylesheet so as to support adding custom styles budled with theme's default styles

Read https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#bundling-custom-css-with-themes-assets for more info
*/

.highlight:hover {
    .unfoldbtn {
        display: block;
    }
}

.unfoldbtn {
    display: none;
    position: absolute;
    top: 4px;
    right: 18px;
    color: rgba(255, 255, 255, .8);
    background: rgba(78, 78, 78, .8);
    border-radius: var(--radius);
    padding: 0 5px;
    font-size: 14px;
    user-select: none;
}

code {
    max-height: 13rem;
}

.unfold {
    max-height: none;
}

.copy-code {
    right: 58px;
}

3.11 查看原图

参考👉 Hugo 主题配置 | 夜云泊

1、修改站点配置文件 config.yml ,如下:

1
2
3
4
params:
	# ...
	# User-defined parameters
    fancybox: true  # 启用图片放大功能

2、创建 ../layouts/_default/_markup/render-image.html 文件,内容如下:

1
2
3
4
5
6
7
 {{if .Page.Site.Params.fancybox }}
 <div class="post-img-view">
 <a data-fancybox="gallery" href="{{ .Destination | safeURL }}">
 <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title}} title="{{ . }}"{{ end }} />
 </a>
 </div>
 {{ end }}

3、编辑 ../layouts/partials/footer.html 文件,加入以下内容:

1
2
3
4
5
{{if .Page.Site.Params.fancybox }}
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
{{ end }}

3.12 数学公式

参考👉 在Hugo PaperMod主题中加入数学支持的最简方式 - 微控圈(MCU Loop)

1、在站点目录下创建 ../layouts/partials/math.html 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.css" integrity="sha384-bYdxxUwYipFNohQlHt0bjN/LCpueqWz13HufFEV1SUatKs1cm4L6fFgCi1jT643X" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js" integrity="sha384-Qsn9KnoKISj6dI8g7p1HBlNpVx0I8p1SvlwOldgi3IorMle61nQy4zEahWYtljaz" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
<script>
    document.addEventListener("DOMContentLoaded", function() {
        renderMathInElement(document.body, {
          // customised options
          // • auto-render specific keys, e.g.:
          delimiters: [
              {left: '$$', right: '$$', display: true},
              {left: '$', right: '$', display: false}
          ],
          // • rendering keys, e.g.:
          throwOnError : false
        });
    });
</script>

2、复制主题目录 ../themes/PaperMod/layouts/partials/extend_head.html 文件,粘贴到站点根目录 ../layouts/partials/ 下。 编辑 extend_head.html,内容如下:

1
2
3
4
5
6
7
{{- /* Head custom content area start */ -}}
{{- /*     Insert any custom code (web-analytics, resources, etc.) - it will appear in the <head></head> section of every page. */ -}}
{{- /*     Can be overwritten by partial with the same name in the global layouts. */ -}}
{{ if or .Params.math .Site.Params.math }}
{{ partial "math.html" . }}
{{ end }}
{{- /* Head custom content area end */ -}}

3、通过在文章 front matter 中设置 math 属性 true/false 来按需加载数学公式资源。

1
2
3
4
5
6
---
title: 文章标题
date: 
tags: 
math: true
---

3.13 其它配置

3.13.1 不显示面包屑

在站点配置文件 config.yml 中关闭,如下:

1
2
3
params:
	# ...
	ShowBreadCrumbs: false

3.13.2 日期格式化

修改站点配置文件 config.yml ,如下:

1
2
3
4
params:
	# ...
    DateFormat: "2006-01-02"  # 日期格式化
    ShowFullTextinRSS: true   # RSS 输出全文

3.13.3 修改文章作者

修改站点配置文件 config.yml ,如下:

1
2
3
params:
	# ...
	author: <YourName>

3.13.4 表格优化

参考👉 折腾 Hugo & PaperMod 主题 - Dvel’s Blog

编辑 ../assets/css/extended/blank.css 文件,添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* GitHub 样式的表格 */
.post-content table tr {
    border: 1px solid #979da3 !important;
}
.post-content table tr:nth-child(2n),
.post-content thead {
    background-color: var(--code-bg);
}
.post-content table th {
    border: 1px solid #979da3 !important;
}
.post-content table td {
    border: 1px solid #979da3 !important;
}

IV. 参考文档

  1. 田少晗的个人博客

  2. 周鑫的个人博客

  3. 似水

  4. 夜云泊

  5. 3rd’s Blog

  6. Dvel’s Blog

  7. Tofuwine’s Blog

  8. 微控圈(MCU Loop)