加载中...

加载中...

本文既是 matery 主题内容 tag 插件的完整使用教程,也是 自动化测试靶场。主题原生注册的 tag 见 themes/matery/scripts/tags/(index.js 统一注册)。自动化测试会断言下方标注的渲染标记。


1. 便签 note

用途:文章内的彩色提示块 —— 提醒、警告、成功反馈、补充说明。

学习路径:最小示例 → 六种颜色 → 隐藏图标 → 可折叠 → 嵌套组件 → 完整参数 → 边界与坑

1.1 最小示例

效果(页面实际渲染):

这是一条最简提示。结构就是:开始标签 + 内容 + 结束标签。

对应源码:

{% note %}
这是一条最简提示。结构就是:开始标签 + 内容 + 结束标签。
{% endnote %}

不写任何颜色关键字时,得到灰边框的普通提示块。

1.2 六种颜色

效果:

default — 普通提示

primary — 重点强调

info — 补充说明

success — 操作成功

warning — 注意事项

danger — 严重警告

对应源码:

{% note default %}**default** — 普通提示{% endnote %}
{% note primary %}**primary** — 重点强调{% endnote %}
{% note info %}**info** — 补充说明{% endnote %}
{% note success %}**success** — 操作成功{% endnote %}
{% note warning %}**warning** — 注意事项{% endnote %}
{% note danger %}**danger** — 严重警告{% endnote %}

颜色对照表:

关键字图标(Font Awesome 码)语义底色来源
defaultf0a9(右箭头)普通提示--text-color 8% 混合
primaryf055(加号)重点强调--tagsP-purple-color 12%
infof05a(信息)补充说明--info 8%
successf058(对勾)操作成功--success 8%
warningf06a(感叹)注意事项--warning 8%
dangerf056(叉)严重警告--danger 8%

底色用 color-mix() 与卡片底色混合,所以明暗模式自动适配,无需为暗色单独写一套。

1.3 隐藏图标(no-icon)

效果:

没有左侧图标,但保留警告色边框。

对应源码:

{% note warning no-icon %}没有左侧图标,但保留警告色边框。{% endnote %}

位置可互换(两个都是关键字,顺序无关):

{% note no-icon warning %}与上一例完全等价{% endnote %}
{% note danger no-icon %}与上一例完全等价{% endnote %}

1.4 可折叠(details)

在关键字之后写标题,就会生成可折叠块(<details><summary>):

效果:

点击展开:静态站为什么够用

静态站没有服务端,但用 构建时快照 可以获得大部分动态能力。

代价

  • 内容更新需重新构建
  • 搜索是本地索引,不是全文检索服务

对应源码:

{% note info 点击展开:静态站为什么够用 %}
静态站没有服务端,但用 **构建时快照** 可以获得大部分动态能力。

### 代价
- 内容更新需重新构建
- 搜索是本地索引,不是全文检索服务
{% endnote %}

标题和正文都走 Markdown 渲染 —— 标题里可以加粗、正文里可以有列表/代码/小标题。

1.5 嵌套其它组件

正文走 Markdown 渲染,所以内部可以再放任何 tag:

效果:

组合示例

这一块内部放了 标签 和 粗体,还有代码 console.log(1)。

二级便签

便签可以嵌套便签,内层用内层的颜色。

对应源码:

{% note success 组合示例 %}
这一块内部放了 {% label primary@标签 %} 和 **粗体**,还有代码 `console.log(1)`。

{% note warning 二级便签 %}
便签可以嵌套便签,内层用内层的颜色。
{% endnote %}
{% endnote %}

1.6 完整参数

参数按空格切分。开头最多取 2 个关键字作 CSS 类,剩余参数全部拼成折叠标题。

位置参数必填默认说明
1颜色关键字❌无default / primary / info / success / warning / danger
2no-icon❌无隐藏左侧图标;与颜色位置可互换
2+折叠标题❌无非关键字参数全部合并为 <summary>,走 Markdown
内容正文❌空走 Markdown,可嵌套其它 tag

全部可选值:

关键字类型说明
default颜色灰
primary颜色紫
info颜色蓝
success颜色绿
warning颜色橙
danger颜色红
no-icon修饰符不是颜色,只隐藏图标

别名:subnote 与 note 注册的是同一个函数,行为与样式完全相同(不是"次级")。

1.7 边界与坑

情况行为
首词不是关键字(如 提示内容)不加任何颜色类,该词变折叠标题 → 得到无颜色可折叠块,不是普通块
写 3 个颜色关键字只取前 2 个 → class="note info success",第 3 个变折叠标题
颜色写错(Warning 大写)大小写敏感 → 视为非关键字,变折叠标题且无颜色
内容为空输出空 div,不报错
忘记结束标签构建报 unclosed tag
想普通块但写了标题做不到 —— 有非关键字参数就会折叠。把文字挪到正文里
与 ::: 容器混用产物不同:tag 是 <div class="note primary">,容器是 <div class="note">(CSS 已用属性选择器区分)

1.8 小结

  • 最常用:note info / note warning 两三个词就够
  • 想折叠:在颜色后直接写标题
  • 想纯色块无图标:加 no-icon
  • 想嵌套:正文里继续写 tag(记得成对闭合)

2. 时间线 timeline

用途:按时间顺序展示版本记录、项目里程碑、个人成长历程 —— 竖线 + 圆点串起一串带日期的节点。

学习路径:最小示例 → 颜色与无标题 → 完整参数 → 进阶 → 边界与坑

2.1 最小示例

一个标题 + 一个节点,就是完整的时间线。

效果(页面实际渲染):

里程碑

2026-08

  • 新增内容 tag 测试页

对应源码:

{% timeline 里程碑,green %}
<!-- timeline 2026-08 -->
- 新增内容 tag 测试页
<!-- endtimeline -->
{% endtimeline %}

2.2 常用变体

变体 1:不要 headline(完全不给参数;注意不要只写标题,否则会渲染出 class="timeline undefined")

效果:

2026-07

  • 只有节点,没有整体标题

对应源码:

{% timeline %}
<!-- timeline 2026-07 -->
- 只有节点,没有整体标题
<!-- endtimeline -->
{% endtimeline %}

变体 2:换颜色(blue / pink / red / purple / orange / green)

效果:

蓝色主题

2026-01

  • 蓝

红色主题

2026-02

  • 红

对应源码:

{% timeline 蓝色主题,blue %}
<!-- timeline 2026-01 -->
- 蓝
<!-- endtimeline -->
{% endtimeline %}

{% timeline 红色主题,red %}
<!-- timeline 2026-02 -->
- 红
<!-- endtimeline -->
{% endtimeline %}

2.3 完整参数

参数先空格 join 再 split(','),因此只有两个位置参数。

位置参数必填默认说明
1标题❌无整体 headline,Markdown 渲染;省略则无 headline
2颜色❌无直接拼进 class 名(不校验合法性)
子块<!-- timeline 标题 --> … <!-- endtimeline -->❌—每个事件节点,标题与正文均走 Markdown

全部可选值(6 色)

颜色竖线 / 圆点色
blue蓝
pink粉
red红
purple紫
orange橙
green绿
省略用 .timeline 默认色(不追加颜色类)

2.4 进阶:节点里的 Markdown 与其它 tag

节点标题和正文都走 Markdown 渲染,所以能放粗体、链接,甚至嵌 {% label %}。

效果:

主题功能演进记录

2026-08

  • 新增内容 tag 测试页(note/timeline/tabs/label 等完整教程)
  • 相册数据重构(galleries.yml 三种形式)
  • 统一 lightGallery 图像查看库 新增

2026-07

  • 评论区架构优化(comment: waline) 优化
  • 修复 href=/ MIME 样式错误 修复

2020

  • 创建博客,开始 matery 深度定制

对应源码:

{% timeline 主题功能演进记录,green %}
<!-- timeline 2026-08 -->
- 新增内容 tag 测试页(note/timeline/tabs/label 等完整教程)
- 相册数据重构(galleries.yml 三种形式)
- 统一 lightGallery 图像查看库 {% label success@新增 %}
<!-- endtimeline -->
<!-- timeline 2026-07 -->
- 评论区架构优化(comment: waline) {% label info@优化 %}
- 修复 href=/ MIME 样式错误 {% label warning@修复 %}
<!-- endtimeline -->
<!-- timeline 2020 -->
- 创建博客,开始 matery 深度定制
<!-- endtimeline -->
{% endtimeline %}

2.5 边界与坑

情况行为
只写标题、不写颜色输出 class="timeline undefined" → 无匹配类,回退默认色(建议颜色必写)
颜色拼错(如 grey)类名照写但无 CSS → 回退默认色,不报错
<!--timeline 标题-->(timeline 后无空格)正则不匹配 → 该节点连同内容整体消失
--> 后不换行开标签要求 -->\n,不匹配 → 同上
标题里含英文逗号被 split(',') 截断,逗号后内容丢失
完全不给参数无 headline、颜色类为空,只渲染子节点
没有任何 <!-- timeline --> 子块只渲染 headline(或空容器)
颜色名写两个单词(light blue)被空格拆开,只保留第一个词,其余丢弃

2.6 小结

  • 最常用:{% timeline 标题,颜色 %} + 若干 <!-- timeline 日期 --> … <!-- endtimeline -->
  • 不想要 headline:一个参数都别写(只写标题会得到 undefined 类名)
  • 换主题色:第 2 个参数写颜色名,写错不报错、只回退默认色
  • 节点能放什么:Markdown 全支持,可以嵌 label / button 等内联 tag

测试断言:页面含 timeline 标记 + 3 个时间节点。


3. 标签页 tabs

用途:一块区域放多份内容,点击标题切换 —— 适合对比、分步骤、多方案并列展示。

学习路径:最小示例 → 激活序号 / 图标 → 完整参数 → 进阶 → 边界与坑

3.1 最小示例

两个标签,默认激活第一个。

效果(页面实际渲染):

这里是第一个标签的内容。

对应源码:

{% tabs 最小示例组,1 %}
<!-- tab 第一个 -->
这里是第一个标签的内容。
<!-- endtab -->
<!-- tab 第二个 -->
这里是第二个标签的内容。
<!-- endtab %}
{% endtabs %}

3.2 常用变体

变体 1:默认激活第二个(第 2 个参数从 1 起算)

效果:

打开时不是它。

打开时默认显示这一页。

对应源码:

{% tabs 激活第二项,2 %}
<!-- tab 第一个 -->
打开时不是它。
<!-- endtab -->
<!-- tab 第二个 -->
打开时默认显示这一页。
<!-- endtab %}
{% endtabs %}

变体 2:给标题加图标(标题 @类型:图标名)

效果:

  • 功能全面
  • 配置灵活
  • 配置项多,有学习成本

对应源码:

{% tabs 带图标,1 %}
<!-- tab 优点 @fas:star -->
- 功能全面
- 配置灵活
<!-- endtab -->
<!-- tab 缺点 @fas:exclamation-triangle -->
- 配置项多,有学习成本
<!-- endtab -->
<!-- tab 关于 @fab:github -->
- 源码在 GitHub
<!-- endtab %}
{% endtabs %}

3.3 完整参数

位置参数必填默认说明
1组名✅—生成容器 DOM id(转小写、空格→-);同页必须全局唯一
2激活序号❌0(= 第一个激活)1 起算;写 0 或省略 = 第一个激活;超范围 = 全部不激活
子块<!-- tab 标题 @类型:图标名 --> … <!-- endtab -->❌自动命名每个标签;tab 后必须有空格
内容标签正文❌空走 Markdown 渲染,可嵌套内联 tag

图标前缀(@ 之后写 类型:图标名)

写法实际 class
fas:home / solid:homefas fa-home
far:home / regular:homefar fa-home
fal:home / light:homefal fa-home
fad:home / duotone:homefad fa-home
fab:githubfab fa-github
未知类型(如 xxx:home)回退 far
不写图标不输出 <i>

3.4 进阶:标签内容承载完整 Markdown 块

效果:

  • 功能全面:内容 tag 覆盖 10+ 插件
  • 配置灵活:每个功能可独立开关
  • 性能优化:懒加载 + 占位防 CLS
// 内容里照样能放代码块
const tag = 'tabs'
  • 配置项多,上手有学习成本
  • 部分功能依赖外部 CDN
  • 技术博客:代码 / 教程 / 对比
  • 个人博客:相册 / 项目 / 历程

对应源码:

{% tabs 主题功能对比,1 %}
<!-- tab 优点 -->
- 功能全面:内容 tag 覆盖 10+ 插件
- 配置灵活:每个功能可独立开关
- 性能优化:懒加载 + 占位防 CLS

```js
// 内容里照样能放代码块
const tag = 'tabs'
```
<!-- endtab -->
<!-- tab 缺点 -->
- 配置项多,上手有学习成本
- 部分功能依赖外部 CDN
<!-- endtab -->
<!-- tab 适用场景 -->
- 技术博客:代码 / 教程 / 对比
- 个人博客:相册 / 项目 / 历程
<!-- endtab -->
{% endtabs %}

3.5 边界与坑

情况行为
缺组名log.error("[Tabs] Missing required tab name") → 返回空串,整块消失
同页两个同名组DOM id 冲突,点一组会连带切换另一组
激活序号 > tab 数(或负数)没有一个 tab 带 active → 首屏空白
激活序号写 0等价省略(第一个激活)
<!--tab 标题-->(tab 后无空格)正则不匹配 → 该 tab 及内容丢失
标题与图标都为空自动用「组名 + 序号」作标题
组名含中文/特殊字符id 原样保留(HTML5 合法,CSS 选择器需转义)
配置 tag_tabs.cdn / tag_tabs.layoutcdn 默认 https://fastly.jsdelivr.net/npm/;layout 默认 ['post','page'],当前渲染逻辑未实际读取(仅兼容保留)

3.6 小结

  • 最常用:{% tabs 组名 %} 里塞若干 <!-- tab 标题 --> … <!-- endtab -->
  • 想默认展开第 N 个:第 2 个参数写 N(1 起算,0/省略都是第一个)
  • 想加图标:标题后加 @fas:star(类型:名字)
  • 组名必须全页唯一,否则点击会串组

测试断言:页面含 nav-tabs 标记 + 3 个标签。


4. 行内标签 label

用途:正文里的小色块,给关键词、状态、分类打标记 —— 不打断句子节奏。

学习路径:最小示例 → 六色 → 完整参数 → 进阶 → 边界与坑

4.1 最小示例

效果(页面实际渲染):

主要

对应源码:

{% label primary@主要 %}

★ 参数顺序反直觉:颜色在 @ 前,文字在 @ 后(源码 split('@'),classes=args[0]、text=args[1])。

4.2 常用变体:六种颜色

效果:

默认 主要 成功 信息 警告 危险

对应源码:

{% label default@默认 %} {% label primary@主要 %} {% label success@成功 %} {% label info@信息 %} {% label warning@警告 %} {% label danger@危险 %}

4.3 完整参数

位置参数必填默认说明
1类型❌default6 种语义色之一,拼成 label-<类型> 类名
2文字✅空标签文案;为空时 log.warn 并输出空标签

全部可选值(6 色)

类型语义
default默认灰
primary主色(紫/主题色)
success成功(绿)
info信息(蓝)
warning警告(橙)
danger危险(红)

4.4 进阶:嵌进句子表达状态

效果:

部署状态:已发布 灰度状态:待观察 回滚状态:已下线 —— 标签与文字混排时不换行、不破坏行高。

对应源码:

部署状态:{% label success@已发布 %} 灰度状态:{% label warning@待观察 %} 回滚状态:{% label danger@已下线 %} —— 标签与文字混排时不换行、不破坏行高。

4.5 边界与坑

情况行为
只写类型不写文字log.warn("Label text must be defined!") → 输出空 <span>
完全没有 @整串被当作类型,文字为空 → 空标签 + warn
文字里含 @只保留第一个 @ 与第二个 @ 之间的内容,其余丢弃
类型拼错 / 写中文(如 成功)类名照写(label-成功),CSS 无匹配 → 无样式
文字含 Markdown(**粗**)不渲染,原样显示星号
文字含 HTML原样插入(源码不转义),可放 <b> 等内联标签
同一行多个 label空格分隔即可,各自独立

4.6 小结

  • 最常用:{% label success@完成 %}
  • 记顺序:颜色 @ 文字(写成 文字@颜色 会得到无样式标签)
  • 想多色并排:一行写多个,空格分隔
  • 别指望 Markdown:文字原样输出,要粗体就自己写 <b>

测试断言:页面含 class="label 标记(6 种颜色)。


5. 按钮 button

用途:文章里的行动入口 —— 跳转、下载、引导读者下一步。

学习路径:最小示例 → 四种写法 → 完整参数 → 进阶 → 边界与坑

5.1 最小示例

效果(页面实际渲染):

返回首页

对应源码:

{% button /,返回首页,home,首页 %}

★ 链接是第一个参数(源码 url=args[0]、text=args[1]),顺序写反按钮就指向文字。

5.2 常用变体

效果:

访问GitHub 关于我 纯文字按钮 Hexo

对应源码:

{% button https://github.com/appotry,访问GitHub,github,GitHub %}
{% btn /about,关于我,user-circle,关于 %}
{% button /tags,纯文字按钮,,标签 %}
{% button https://hexo.io,Hexo,fa fa-book,官网 %}

四个变体分别是:带图标的标准按钮、别名 btn、纯文字按钮(图标留空)、图标写完整 fa fa-xxx 类名。

5.3 完整参数

位置参数必填默认说明
1链接✅空URL / 相对路径;为空时 warn,仍输出 href=""
2文字❌空按钮文案,不渲染 Markdown
3图标❌空FA 类名;不以 fa 开头时自动补 fa fa-
4title❌空hover 提示;为空则不输出该属性
5+—❌—静默忽略

别名:{% btn %} 与 {% button %} 注册的是同一个函数,完全等价。

5.4 进阶:按钮放进便签里做引导

效果:

下一步

读完这篇建议继续看布局篇,或者直接去标签页找同类文章:

内容 tag 篇按标签浏览

对应源码:

{% note info 下一步 %}
读完这篇建议继续看布局篇,或者直接去标签页找同类文章:

{% button /posts/8a11fe0a/,内容 tag 篇,file-text,当前页 %}
{% button /tags,按标签浏览,tags,标签云 %}
{% endnote %}

5.5 边界与坑

情况行为
任何参数含英文逗号先 join(' ') 再 split(',') → 参数被切断,后续全部错位
图标写 home输出 <i class="fa fa-home">
图标写 fas fa-home / fa-home以 fa 开头 → 原样保留(不重复加前缀)
缺链接log.warn("URL can NOT be empty.") → 仍渲染 <a href="">(点击回当前页)
第 5 个及以后参数静默丢弃
title 为空不输出 title="" 属性
文字留空但给了图标只有图标的按钮(纯图标按钮可用)
别名 btn与 button 完全等价(同一函数注册)

5.6 小结

  • 最常用:{% button 链接,文字,图标,title %}
  • 记顺序:链接第一、文字第二(写反就跳错地方)
  • 纯文字按钮:图标位留空但逗号保留
  • 参数别带逗号:任何逗号都会把后面的参数切错位

测试断言:页面含 btn 标记(≥4 个按钮)。


6. GitHub 卡片 githubCard

用途:把仓库变成一张能点的卡片(星标 / 分支 / 简介),比裸链接更有说服力。

学习路径:最小示例 → 用户卡与尺寸 → 完整参数 → 进阶 → 边界与坑

6.1 最小示例

效果(页面实际渲染):

对应源码:

{% githubCard user:appotry repo:hexo %}

参数是 key:value(冒号),空格分隔 —— 写成 width=300 会被整条忽略。

6.2 常用变体

变体 1:只给 user(渲染用户卡,仓库区为空)

效果:

对应源码:

{% githubCard user:hexojs repo:hexo %}

变体 2:指定尺寸

效果:

对应源码:

{% githubCard user:appotry repo:hexo-theme-matery width:400 height:140 %}

6.3 完整参数

位置参数必填默认说明
任意user:名称✅—GitHub 用户名;缺失时输出红字错误块
任意repo:仓库❌—仓库名;省略则显示用户卡片
任意width:300 / width:300px❌组件自带自动去掉尾部 px
任意height:150 / height:150px❌组件自带同上
任意theme:主题❌default值为 default 时不输出该属性
任意target:目标❌blank值为 blank 时不输出该属性
任意client_id:xxx❌—OAuth client_id,用于提升 API 限额
任意client_secret:xxx❌—不支持(会泄漏到前端 HTML,源码刻意不实现)

6.4 进阶:并列展示两个仓库

效果:

对应源码:

{% githubCard user:appotry repo:hexo %}

{% githubCard user:hexojs repo:hexo %}

两个卡片之间有 Markdown 空行,各自独立成块。

6.5 边界与坑

情况行为
缺 user输出 <div style="color:red">GitHub Card配置错误:缺少user参数</div>,构建不失败
只给 user 不给 repo渲染用户卡片(仓库区为空)
值里含空格被空格切成两个 token,第二个无冒号 → 丢弃
写 width=300(等号)找不到冒号 → 参数被忽略
theme:default / target:blank属性不输出,走组件默认值
页面禁用 JS<noscript> 显示 “JavaScript is required to load GitHub Cards”
卡片加载前容器预留 min-height: 150px,防止布局抖动
同名参数写两次后者覆盖前者(按 key 存对象)

6.6 小结

  • 最常用:{% githubCard user:xxx repo:yyy %}
  • 只要用户卡:省掉 repo
  • 改尺寸:width:400 height:140(px 可带可不带)
  • 提升 API 限额:加 client_id;永远不要写 client_secret

测试断言:页面含 github-card 或仓库名链接标记。


7. Mermaid 流程图

用途:把流程、时序、类关系画成图 —— 用文本描述,构建时渲染成 SVG。

学习路径:最小示例 → 三种常用图 → 完整参数 → 进阶 → 边界与坑

⚠️ 前置条件:文章 front-matter 必须有 mermaid: true,且主题 post.mermaid.enable: true。缺 front-matter 时整段脚本不输出,图静默空白(本文已声明)。

7.1 最小示例

效果(页面实际渲染):


graph TD
    A --> B

对应源码:

{% mermaid %}
graph TD
    A --> B
{% endmermaid %}

7.2 常用变体:三种最常用的图

变体 1:流程图

效果:


graph TD
    A[构建] --> B{测试}
    B -->|通过| C[部署]
    B -->|失败| D[修复]
    D --> B
    C --> E[监控]

对应源码:

{% mermaid %}
graph TD
    A[构建] --> B{测试}
    B -->|通过| C[部署]
    B -->|失败| D[修复]
    D --> B
    C --> E[监控]
{% endmermaid %}

变体 2:时序图

效果:


sequenceDiagram
    participant U as 用户
    participant S as 服务器
    participant D as 数据库
    U->>S: 发起请求
    S->>D: 查询数据
    D-->>S: 返回结果
    S-->>U: 响应页面

对应源码:

{% mermaid %}
sequenceDiagram
    participant U as 用户
    participant S as 服务器
    participant D as 数据库
    U->>S: 发起请求
    S->>D: 查询数据
    D-->>S: 返回结果
    S-->>U: 响应页面
{% endmermaid %}

变体 3:类图

效果:


classDiagram
    class 用户 {
        +String 姓名
        +登录()
    }
    class 博客 {
        +String 标题
        +发布()
    }
    用户 --> 博客

对应源码:

{% mermaid %}
classDiagram
    class 用户 {
        +String 姓名
        +登录()
    }
    class 博客 {
        +String 标题
        +发布()
    }
    用户 --> 博客
{% endmermaid %}

7.3 完整参数

tag 参数

位置参数必填默认说明
任意任意文本❌空不是 Mermaid 配置——全部原样拼进 <code class="mermaid" …> 的 HTML 属性位

也就是说写 theme=dark 只是给 <code> 加了个 HTML 属性,不会改变图表主题;主题走下面的 post.mermaid.options.theme。

配置项(主题 _config.yml 的 post.mermaid)

配置键可选值默认说明
enabletrue / false—总开关
specifictrue / falsetruetrue 时仅 mermaid: true 的文章输出脚本
options.themedefault / forest / dark / neutral / basedefault图表主题
options.startOnLoadtrue / falsefalse主题自行控制渲染时序,不要改 true
options.securityLevelstrict / loose / antiscript / sandboxlooseHTML 标签放行程度
options.flowchart.useMaxWidthtrue / falsetrue流程图是否撑满容器

支持的图表类型:graph / flowchart、sequenceDiagram、classDiagram、stateDiagram-v2、gantt、pie、erDiagram、gitGraph、journey、mindmap、timeline、quadrantChart。

7.4 进阶:饼图(稳定输出,适合占比)

效果:


pie title 内容类型占比
    "教程" : 45
    "排障" : 30
    "随笔" : 25

对应源码:

{% mermaid %}
pie title 内容类型占比
    "教程" : 45
    "排障" : 30
    "随笔" : 25
{% endmermaid %}

7.5 边界与坑

情况行为
specific: true 但 front-matter 没写 mermaid: true主题整段脚本不输出 → 图空白,控制台无报错(最隐蔽的坑)
在 tag 里写 theme=dark只加 HTML 属性,图表主题不变
图语法错误(缩进/箭头写错)Mermaid 抛 parse error,容器留空并显示错误图
正文用 mermaid 代码围栏(三个反引号)不会自动渲染(属于另一条前端插件路径),请统一用本 tag
startOnLoad: true与主题自行渲染的时序冲突,可能双渲染/首屏闪烁
同页多张图各自独立渲染,互不影响
加密片段内的图解密后由 events.refresh() 重渲染(幂等)
本页 front-matter已设 mermaid: true,是上方示例能渲染的前提

7.6 小结

  • 最常用:{% mermaid %} 里贴原生 Mermaid 语法
  • 记前提:front-matter 必须有 mermaid: true(specific: true 时)
  • 换主题:改配置 post.mermaid.options.theme,不是写在 tag 参数里
  • 别用代码围栏:三个反引号的 mermaid 块不渲染,统一用 tag
  • 语法错了不报构建:只是那块空白,看控制台 parse error

测试断言:页面含 class="mermaid" 标记(≥3 个图表)。


8. PDF 嵌入 pdf

用途:文章里直接翻 PDF —— 手册、图纸、报告不用跳出去。

学习路径:最小示例 → 完整参数 → 进阶 → 边界与坑

8.1 最小示例

效果(页面实际渲染):

对应源码:

{% pdf /doc/Linux学习笔记.pdf %}

8.2 完整参数

位置参数必填默认说明
1URL✅—PDF 地址;没有 width/height 等其它参数

类型自动检测(按顺序匹配,先命中先返回)

顺序URL 特征类型渲染方式
1含 .pdfnormal<embed>,高 550px
2含 drive.google.comgoogledoc<iframe>,高 550px
3含 www.slideshare.netslideshare<iframe>,高 550px
4其它normal<embed>

8.3 进阶:前面加一句说明(便签 + PDF 组合)

效果:

移动端提示

手机浏览器大多不支持内联 PDF,会退化成下载或空白 —— 桌面端体验完整。

对应源码:

{% note warning 移动端提示 %}
手机浏览器大多不支持内联 PDF,会退化成下载或空白 —— 桌面端体验完整。
{% endnote %}

{% pdf /doc/Linux学习笔记.pdf %}

8.4 边界与坑

情况行为
Google Drive 直链(URL 含 .pdf)先命中第 1 条 → 按 normal embed,不会走 Google 预览
drive.google.com/viewer?url=xxx.pdf同样含 .pdf → 被判为 normal
slideshare 链接里带 .pdf被判为 normal
想改高度无参数可调,源码 3 处硬编码 550px,只能改源码或额外 CSS 覆盖
空参数输出 <embed src="">,空白容器
相对路径以站点根为基准(如 /medias_webp/docs/sample.pdf)
移动端浏览器多数不支持内联 PDF → 空白或提示下载(非 tag 问题)

8.5 小结

  • 最常用:{% pdf /path/file.pdf %}
  • 记限制:只有一个参数,高度写死 550px
  • 想走 Google 预览:URL 里不能出现 .pdf,否则被判为普通 embed
  • 站内文件:以站点根为起点写路径

测试断言:页面含 pdf 相关容器标记。


9. 插入片段 insertmd

用途:把 source/_template/ 里的 Markdown 片段插进正文 —— 一处维护、多处复用(系列目录、免责声明、广告位)。

学习路径:最小示例 → 带分隔符 → 完整参数 → 边界与坑

9.1 最小示例

效果(页面实际渲染):

系列教程

全部文章RSS订阅

对应源码:

{% insertmd '_template/系列教程.md' %}

9.2 常用变体:加分隔符

第 2 个参数是 Markdown 分隔内容,会追加在每个文件之后(含最后一个)。

效果:

系列教程

全部文章RSS订阅


对应源码:

{% insertmd '_template/系列教程.md' '---' %}

9.3 完整参数

位置参数必填默认说明
1路径✅—相对 source/(不是相对当前文章);文件或目录均可
2分隔符❌空Markdown 渲染后追加在每个文件之后(含最后一个)

行为细则

项说明
目录模式传目录则递归收集所有 .md(其它扩展名忽略),一次插入整目录
排序按完整路径 localeCompare → 第10篇 排在 第2篇 前面(字典序)
分隔符每个文件渲染后都追加一次,末尾也会多一个
渲染器自动识别 markdown-it / marked;以 disableNunjucks: true 渲染 → 片段内的 Hexo tag 语法不会执行
注册方式{ async: true, ends: false, priority: 2 }
依赖fs-extra

9.4 边界与坑

情况行为
路径不存在log.error + 返回空串 → 页面静默缺内容,构建不失败
路径写成相对文章(../)解析基准是 source/,会找不到(静默空)
分隔符含空格需引号包裹,否则只取到前两段
目录层级过深/文件过多无深度限制,大目录会明显拖慢构建
片段内嵌 tag不执行(disableNunjucks),只按纯 Markdown 渲染
单个文件渲染异常该文件返回空串,其它文件继续

9.5 小结

  • 最常用:{% insertmd '_template/xxx.md' %}(路径相对 source/)
  • 想加分隔线:第 2 参数写 '---'
  • 想批量插:传目录,按路径字典序合并
  • 片段里别写 tag:disableNunjucks 下不执行,只当纯文本

测试断言:渲染后含模板片段内容。


10. 微信对话卡片 wechat_dialog

用途:把问答、客服对话、报错排查过程变成聊天截图的样子 —— 比正文引用更直观。

学习路径:最小示例 → 多轮 + Markdown → 完整参数 → 进阶 → 边界与坑

10.1 最小示例

效果(页面实际渲染):

User

你好

Assistant

你好,有什么可以帮您?

对应源码:

{% wechat_dialog %}
User: 你好
Assistant: 你好,有什么可以帮您?
{% endwechat_dialog %}

★ 必须是 User: / Assistant: 行格式,不是行内参数 —— 写 {% wd 文字,发送者 %} 会渲染异常。

10.2 常用变体:多轮且带 Markdown

效果:

User

你好,我想了解 matery 主题

Assistant

好的,有什么可以帮您?

User

主题支持哪些功能?

Assistant

内容 tag、相册、搜索、评论等 20+ 功能

Assistant

常用清单:

  • 内容 tag(note / tabs / timeline)
  • 相册与图库
  • 本地搜索 + Pagefind

对应源码:

{% wechat_dialog %}
User: 你好,我想了解 matery 主题
Assistant: 好的,有什么可以帮您?
User: 主题支持哪些功能?
Assistant: 内容 tag、相册、搜索、评论等 **20+** 功能
Assistant: 常用清单:
- 内容 tag(note / tabs / timeline)
- 相册与图库
- 本地搜索 + Pagefind
{% endwechat_dialog %}

10.3 完整参数

位置参数必填默认说明
—无——不接受位置参数,写了也会被忽略;内容全部来自块内的角色行

块内规则

写法行为
User: 开头新消息:右侧、绿色气泡、头像 2.jpg
Assistant: 开头新消息:左侧、灰色气泡、头像 1.jpg
其它行追加到当前消息(保留换行)
消息正文用独立 new MarkdownIt() 渲染(不走 Hexo 渲染器)

10.4 进阶:别名 wd 写排障对话

效果:

User

改了样式为什么不生效?

Assistant

先确认是不是缓存:主题配置改动必须重新 clean 构建。

User

clean 了还是不变

Assistant

那看两点:

  1. 配置改的是 userConfig/_config.tmp.yml 还是构建产物 themes/matery/_config.yml
  2. 容器里 pm2 restart 不够,db.json 缓存还在

对应源码:

{% wd %}
User: 改了样式为什么不生效?
Assistant: 先确认是不是缓存:**主题配置改动必须重新 clean 构建**。
User: clean 了还是不变
Assistant: 那看两点:
1. 配置改的是 `userConfig/_config.tmp.yml` 还是构建产物 `themes/matery/_config.yml`
2. 容器里 `pm2 restart` 不够,`db.json` 缓存还在
{% endwd %}

10.5 边界与坑

情况行为
中文冒号 User:不识别(正则要求英文 :)→ 整行并入上一条消息
小写 user: / assistant:不识别(大小写敏感)→ 并入上一条
第一条角色行之前有内容currentRole 为空 → 按 assistant 气泡输出
整块没有角色行全部内容合成一条 assistant 消息
消息里写 {% note %} 等 tag不生效(不经过 Hexo tag 管线)
头像硬编码 /medias_webp/avatar/1.jpg、2.jpg,换头像需改源码
空块只输出空 .chat-container

10.6 小结

  • 最常用:每行以 User: / Assistant: 开头,其余行自动归入当前消息
  • 别名:{% wd %} 完全等价
  • 能放 Markdown:粗体、列表、行内代码都行
  • 不能放 tag:块内 {% note %} 之类不执行
  • 冒号必须英文,大小写必须严格

测试断言:页面含微信对话容器标记(.chat-container)。


11. 分组图片 groupimage

用途:多张图片按行列网格排列 —— 相册缩略、对比图、步骤截图不再一张一张竖着堆。

学习路径:最小示例 → 默认布局 → 完整参数 → 进阶 → 边界与坑

11.1 最小示例

3 张图,2-1 布局(第一行 2 张、第二行 1 张)。

效果(页面实际渲染):

封面1
封面2
封面3

对应源码:

{% groupimage 3 2-1 %}
![封面1](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面2](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面3](https://blog.17lai.site/medias_webp/cover/hexo.webp)
{% endgroupimage %}

11.2 常用变体:省略布局

不给第 2 个参数时按总数取默认布局(4 张 → 2-2)。

效果:

封面1
封面2
封面3
封面4

对应源码:

{% groupimage 4 %}
![封面1](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面2](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面3](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面4](https://blog.17lai.site/medias_webp/cover/hexo.webp)
{% endgroupimage %}

11.3 完整参数

位置参数必填默认说明
1总数✅—parseInt 解析;决定布局与每行配额
2布局❌见下表- 分隔的每行张数(3-2 = 3 张 + 2 张)
内容图片✅—标准 ![]();从渲染后 HTML 的 <img> 中提取

默认布局(未给布局或校验失败时)

总数布局总数布局
21-173-2-2
32-183-2-3
42-293-3-3
53-2103-2-2-3
63-3>10每行 3 张

别名:{% gi %} 与 {% groupimage %} 注册的是同一个函数,完全等价。

11.4 进阶:自定义三行布局

效果:

封面1
封面2
封面3
封面4
封面5

对应源码:

{% gi 5 3-1-1 %}
![封面1](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面2](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面3](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面4](https://blog.17lai.site/medias_webp/cover/hexo.webp)
![封面5](https://blog.17lai.site/medias_webp/cover/hexo.webp)
{% endgi %}

11.5 边界与坑

情况行为
sum(布局) != 总数静默回退默认布局(不报错、不提示)
图片数 < 总数后面的行分到空数组 → 出现空行/留白
图片数 > 总数超出配额的图片被丢弃
总数 = 1 或 > 10 且未给布局回退「每行 3 张」
布局写 3,2(逗号)只按 - 切分,3,2 解析为 3 → sum 不符 → 回退默认
图片写成裸 URL / 其它语法提取不到 <img> → 该图不显示(只认渲染后的 <img>)
总数写非数字parseInt → NaN → 无默认布局 → 每行 3 张

11.6 小结

  • 最常用:{% groupimage 数量 布局 %}![图](url)...{% endgroupimage %}
  • 不想算布局:只写数量,用默认布局
  • 自定义:3-2-1 这种 - 分隔写法,各段之和必须等于数量,否则静默回退
  • 图片必须用 Markdown 语法:裸 URL 提取不到

测试断言:页面含 group-image 容器标记。


12. URL 卡片 cardurl

用途:把一个链接做成带标题/描述的卡片 —— 推荐资源、引用官方文档时比裸链接体面。

学习路径:最小示例 → 带图标 → 完整参数 → 进阶 → 边界与坑

12.1 最小示例

效果(页面实际渲染):

夜法之书-hexo仓库
个人独立blog源码,基于hexo搭建

对应源码:

{% cardurl [url=https://github.com/appotry/hexo] [title=夜法之书-hexo仓库] [desc=个人独立blog源码,基于hexo搭建] %}

参数必须带方括号([key=value])。漏掉方括号 → 正则不匹配 → 参数被静默忽略,得到一张空卡片。

12.2 常用变体:加左侧图标

效果:

夜法之书
个人独立技术博客

对应源码:

{% cardurl [url=https://blog.17lai.site] [title=夜法之书] [desc=个人独立技术博客] [avatar=/medias_webp/avatar/1.jpg] %}

12.3 完整参数

位置参数必填默认说明
任意[url=...]✅—目标链接
任意[title=...]❌同 url卡片标题
任意[desc=...]❌无描述文字
任意[avatar=...]❌无左侧图标;省略则不输出 <img>

12.4 进阶:卡片列表做资源导航

效果:

博客源码
undefined
组件教程
主题全部组件的公开文档

对应源码:

{% cardurl [url=https://github.com/appotry/hexo] [title=博客源码] [desc=Hexo 主题与内容全部开源] [avatar=/medias_webp/avatar/1.jpg] %}

{% cardurl [url=https://blog.17lai.site/tutorial/] [title=组件教程] [desc=主题全部组件的公开文档] [avatar=/medias_webp/avatar/2.jpg] %}

12.5 边界与坑

情况行为
参数不带方括号正则 \[([^=]+)=(.+)\] 不匹配 → 参数被静默忽略,输出空卡片
缺 desc显示字面量 undefined(源码未判空)
缺 urlhref="undefined",点击跳到 /undefined
缺 title回退显示 url
值里含 HTML/Markdown全部 HTML 转义(XSS 防护)→ 显示为实体,不渲染
值里含 ]正则贪婪(.+)→ 取到最后一个 ] 之前
空值 [desc=].+ 要求至少 1 字符 → 该参数被忽略
键名写错([titel=...])不认识的键被忽略

12.6 小结

  • 最常用:{% cardurl [url=...] [title=...] [desc=...] %}
  • 记方括号:[key=value] 一个都不能少
  • 补 desc:不写 desc 会显示 undefined
  • 加图标:[avatar=图片URL]
  • 别塞 HTML:全部转义,只会显示成实体字符

测试断言:页面含 URL 卡片容器标记(.card-url-wrapper)。


13. 容器 admonition(markdown-it-container)

用途:用 ::: 语法写提示块 —— 不依赖 Hexo tag,纯 Markdown 层解析。

学习路径:最小示例 → 带标题 / 五种类型 → 完整参数 → 进阶 → 边界与坑

13.1 最小示例

效果(页面实际渲染):

这是一个 warning 容器——需要注意的内容!

对应源码:

::: warning
这是一个 warning 容器——需要注意的内容!
:::

13.2 常用变体

变体 1:带标题(类型后同一行写标题)

效果:

带标题的容器:类型后同一行写标题。

对应源码:

::: info 可选标题
带标题的容器:类型后同一行写标题。
:::

变体 2:五种类型并排

效果:

这是一个 note 容器——补充说明。

这是一个 info 容器——信息提示。

这是一个 attention 容器——注意警示。

这是一个 error 容器——严重错误。

对应源码:

::: note
这是一个 note 容器——补充说明。
:::

::: info
这是一个 info 容器——信息提示。
:::

::: attention
这是一个 attention 容器——注意警示。
:::

::: error
这是一个 error 容器——严重错误。
:::

13.3 完整参数

位置参数必填默认说明
1类型名✅—必须是根 _config.yml 注册过的 7 个之一
2可选标题❌无与类型同一行,渲染为容器内标题段
闭合:::✅—单独一行;缺失会把后续内容全部吞入

全部可选值(7 个注册名)

名称颜色
warning橙
attention橙(同 warning)
note绿
info蓝
tip蓝(同 info)
hint蓝(同 info)
error红

与 {% note %} tag 的区别

维度::: 容器{% note %} tag
解析层markdown-it 插件(Markdown 层)Hexo tag 插件
产物裸 <div class="warning">(无 note 类)<div class="note warning">
可折叠❌✅(剩余参数作标题)
类型扩展仅注册的 7 个6 色 + no-icon

13.4 进阶:容器里放列表和代码

效果:

  1. 先看构建日志有没有报错
  2. 再看浏览器控制台
  3. 最后确认是不是缓存
npm run clean && npm run build

对应源码:

::: tip 排查顺序
1. 先看构建日志有没有报错
2. 再看浏览器控制台
3. 最后确认是不是缓存

```bash
npm run clean && npm run build
```
:::

13.5 边界与坑

情况行为
未注册类型(::: danger)不解析 → ::: 与文字按普通段落原样输出
忘记闭合 :::后续整段内容被并入容器
同名容器嵌套markdown-it-container 不支持同名直接嵌套,需用别的内容隔开
标题写多行只有第一行识别为标题,其余进正文
样式作用域规则挂在 .post-container 后代选择器下 → wiki 页需 <main> 带该类(已修)
与 !!! admon 区别!!! 产物带 .admonition + 标题段,闭合靠两个空行

已废弃:原 {% admonition %} tag 形式已移除(tag 插件不存在);提示块现由 markdown-it-container(:::)与 markdown-it-admon(!!!,见 Markdown 语法篇)提供。

13.6 小结

  • 最常用:::: warning … :::
  • 想加标题:类型同一行往后写
  • 只有 7 个类型,写 danger 之类会原样输出
  • 和 {% note %} 二选一:要折叠就用 tag,要纯 Markdown 写法就用 :::
  • 闭合不能忘,否则后面整段被吞

测试断言:页面含 admonition 容器标记。


14. 加密片段 tag({% encrypt %})

用途:文章里某几段内容加密码 —— 每段独立密码,互不影响,解密后原地展开。

学习路径:最小示例 → 标题与简介 → 完整参数 → 进阶(组合 + wiki 整体加密)→ 边界与坑

14.1 最小示例

效果(页面实际渲染,密码 demo123):

您好, 这里需要密码.

对应源码:

{% encrypt demo123 %}
这一段在构建产物里是密文,输入密码后才展开。
{% endencrypt %}

14.2 常用变体:带提示标题与简介

效果(密码 test-secret):

私有内容

对应源码:

{% encrypt "test-secret" "私有内容" "这是一段测试加密内容" %}
**粗体测试** 和 {% note %}内部 note 标签{% endnote %} 结尾。

SECRET-SEGMENT-TEXT-MARKER
{% endencrypt %}

14.3 完整参数

位置参数必填默认说明
1密码✅—解密密码(Hexo 自动去引号)
2标题❌空加密块提示语(显示在密码框上方)
3简介❌空密码输入框占位文案
内容正文❌空被加密的 Markdown,可含内部 tag(note/timeline 等)

14.4 进阶

进阶 1:内部 tag 与 Markdown 混合渲染(解密后二次渲染,after_post_render 方案)

效果(解密后可见):

组合内容

对应源码:

{% encrypt nested-demo "组合内容" "输入 nested-demo 查看" %}
{% note success 解密后生效 %}
片段里可以正常写 `note`、`label`,以及 **Markdown**。
{% endnote %}

{% label success@已解密 %} {% label info@二次渲染 %}
{% endencrypt %}

进阶 2:Wiki 整页加密(在侧栏数据源里配一次,整站 wiki 都加密)

效果(说明):wiki 页首屏显示密码框,解密后侧栏与正文一起展开;同 wiki 其它页面自动解密(派生密钥存 localStorage)。

对应源码:

# source/_data/docs-sidebar.yml
encrypt_password: "你的密码"    # 必填,整站 wiki 加密
encrypt_message: 本文已加密      # 可选,提示语
encrypt_placeholder: 请输入密码   # 可选,输入框占位

测试断言:L2y(wiki 加密页解密)+ L8 8c(加密 wiki 可访问)。

14.5 边界与坑

情况行为
不写密码log.warn + 原样输出明文(等于没加密,最危险)
密码/标题含空格或中文标点必须用引号包裹;否则按空格切成多个参数,密码只取第一段
忘记 {% endencrypt %}构建报 unclosed tag 错误
一篇文章多个片段各自独立密码,互不影响
存储键hbe.v4.segment.<页面路径>#<片段序号> → 改文章路径或调整片段顺序,已解密状态失效(需重输)
忘记密码无法恢复(AES-GCM + PBKDF2,无后门)
源文件源 Markdown 仍是明文:仓库公开 = 内容公开,本功能只保护渲染产物
解密后组件代码块/灯箱/Mermaid/布局统一由 hbe-bundle onSuccess 重新初始化

解密后各组件的具体重初始化动作(解密前 DOM 里没有这些元素,必须补一遍):

组件重初始化动作
代码块codeWidget() 扫描新 <pre> 生成 toolbar
图片灯箱articleInit() 包装 img → lightGallery
Mermaidevents.refresh() 重渲染(幂等)
布局refreshLayout() 重排

14.6 小结

  • 最常用:{% encrypt 密码 %} … {% endencrypt %}
  • 想加提示:后面跟 "标题" "输入框占位",含空格必须引号
  • 能嵌 tag:片段内 note / label 解密后正常渲染
  • 忘记密码 = 内容丢失,别把唯一密码只存在脑子里
  • 整站 wiki 加密:配 docs-sidebar.yml 的 encrypt_password

加密单测 tools/tests/unit/unit-encrypt.test.js + unit/unit-encrypt2.test.js 覆盖:AES-GCM + PBKDF2 闭环 / 片段解密 / 内部 note 渲染 / markdown 粗体 / dk 存储无密码;解密后的运行时行为由门禁 L2y 段(加密解密刷新 + 容器完整性)验证。

测试断言:L2y(解密后组件初始化)。


15. 数学公式(MathJax / KaTeX 双通道)

用途:正文写 LaTeX 公式,构建时 SSR 成 SVG —— 不用前端 JS 也能稳定显示。

学习路径:行内 → 块级 → 完整参数(分隔符与引擎)→ 进阶 → 边界与坑

⚠️ 前置条件:文章 front-matter 需要 mathjax: true(specific: true 时),主题 post.math.engine 选数学引擎。

15.1 最小示例:行内公式

效果(页面实际渲染):质能方程 E=mc2就写在一句话中间。

对应源码:

质能方程 $E=mc^2$ 就写在一句话中间。

15.2 常用变体:块级公式

效果:

∫0∞e−x2dx=π2

对应源码:

$$
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
$$

15.3 完整参数

分隔符支持(按引擎)

写法mathjax 引擎katex 引擎
$...$ 行内✅✅
$$...$$ 块级✅✅
\(...\) 行内❌(5.x 硬编码,配置无效)✅(需独立成块)
\[...\] 块级❌✅(需独立成块)

引擎对照

项mathjax(默认)katex
生产端markdown-it-mathjax3(Node SSR 出 SVG)markdown-it-texmath
前端加载 MathJax 3.2.2只加载 KaTeX CSS(无需 JS)
产物标记<mjx-container>.katex
切换方式post.math.engine: mathjax + 保留 mathjax3post.math.engine: katex + 注释 mathjax3、启用 texmath

配置(根 _config.yml + 主题 _config.yml)

# 根 _config.yml:markdown-it-mathjax3(Node 端 SSR 输出 MathJax SVG)
markdown:
  plugins:
    - name: markdown-it-mathjax3
      options:
        tex:
          inlineMath: [['$', '$']]
          displayMath: [['$$', '$$']]
          processEscapes: true

# 主题 _config.yml:post.math 引擎(Fluid 风格,二选一)
post:
  math:
    enable: false          # 开启后文章默认可用
    specific: true         # true 时需 front-matter `math: true` 才启动
    engine: mathjax        # Options: mathjax | katex

15.4 进阶:矩阵与多行推导

效果:

A=(abcd),det(A)=ad−bc

对应源码:

$$
A = \begin{pmatrix} a & b \\ c & d \end{pmatrix},\quad
\det(A) = ad - bc
$$

15.5 边界与坑

情况行为
写 \(...\) 但用 mathjax 引擎原样输出,不渲染(inlineMath 配置在 5.x 无效)
两种引擎同时启用冲突(都注册 math_inline),只能二选一
$ 表示金额(如 $5)会被当公式起点 → 用 \$5 转义
块级 $$ 与文字同行必须独立成行,否则可能被当行内处理
specific: true 但 front-matter 没写 mathjax: true主题不注入前端脚本(模板判断的是 page.mathjax,不是 math)
katex 引擎下 \[...\]需独立成块,行内混排不生效
脑图内的公式走 KaTeX(hexo_markmap.features 含 math)

15.6 小结

  • 最常用:行内 $...$、块级 $$...$$(两种引擎都支持)
  • 记前提:front-matter 写 mathjax: true
  • 金额要转义:\$5,否则被当公式
  • 别混引擎:mathjax 与 katex 只能二选一
  • 公式进脑图:配 hexo_markmap.features: [math]

测试断言:tools/tests/rich-content-integration.test.js(L2o):公式页 mjx-container ≥ 10 + katex ≥ 1(双通道)。


16. 脑图 markmap

用途:用 Markdown 列表画出可交互思维导图 —— 缩放、折叠、点链接,适合知识结构梳理。

学习路径:最小示例 → 省略高度 → 完整参数 → 进阶 → 边界与坑

16.1 最小示例

效果(页面实际渲染):

对应源码:

{% markmap 350px %}
- 主题功能测试系列
  - 交互视觉篇
  - 内容 tag 篇
- 自动化测试
  - L1 构建验证
  - L13 单测门禁
{% endmarkmap %}

16.2 常用变体:不写高度

效果:

对应源码:

{% markmap %}
- 省略高度参数
  - 走 CSS 默认高度
{% endmarkmap %}

16.3 完整参数

位置参数必填默认说明
1高度❌空(走 CSS 默认)原样写入容器 style="height: …",必须带单位(350px)
内容列表✅—标准 Markdown 无序列表,缩进 2 空格表示层级

配置项(根 _config.yml 的 hexo_markmap)

配置键可选值默认说明
CDNjsdelivr / custom 等插件默认资源来源;本项目用 jsdelivr,再由 filters/markmap-localize.js 本地化
featuresmath / prism / zoom(数组)[]公式、代码高亮、缩放工具栏
themeauto 等插件默认脑图主题,auto 跟随明暗
hexo_markmap:
  CDN: 'jsdelivr'   # 2026-08-24: fastly.jsdelivr.net 不可达,换 cdn.jsdelivr.net
  features:
    - math          # 脑图内公式(KaTeX)
    - prism         # 代码高亮
    - zoom          # 缩放
  theme: auto

16.4 进阶:节点里放链接、行内代码与公式

效果:

对应源码:

{% markmap 320px %}
- 进阶节点
  - 链接 [Hexo](https://hexo.io)
  - 行内代码 `npm run build`
  - 公式 $a^2+b^2=c^2$
{% endmarkmap %}

16.5 边界与坑

情况行为
高度不带单位(350)style="height: 350" 非法 → 被浏览器忽略,回退 CSS 默认高度
内容不是列表只有根节点或渲染异常(必须用 - 列表)
缩进层级错乱层级关系按缩进推断,错缩进会挂错父节点
features 未含 math公式显示为原始 LaTeX 文本
未配置 hexo_markmap用插件默认 CDN,可能被墙导致空白
含 {% encrypt %} 的页面脚本改走串行 onload 加载链(见下),静态页仍用 defer
同页多个 markmap各自独立 id,互不影响

与加密片段的共存(重要):markmap 的 4 个脚本(d3 / markmap-view / markmap-toolbar / init)由 after_render:html filter 本地化。含 {% encrypt %} 的页面会走串行 onload 链加载器而非 <script defer> —— 原因是 HTML 规范规定动态插入的 <script> 忽略 defer(其 async 默认为 true),hbe 解密流程重新插入脚本时会按下载完成顺序执行,恰好与依赖链颠倒(init 1.8KB 最快先跑 → window.markmap undefined;d3 280KB 最慢最后跑 → markmap-view 报 d3 is not defined)。静态页仍用 defer(并行下载 + 首屏友好)。完整根因与排查教训见 docs/EXPERIENCE.md §58。

16.6 小结

  • 最常用:{% markmap 350px %} + 无序列表
  • 高度必须带单位,否则被忽略
  • 层级靠缩进(2 空格一级)
  • 要公式/代码高亮:配 hexo_markmap.features: [math, prism]
  • 和 encrypt 同页:加载链会切换成串行 onload,属预期行为

测试断言:tools/tests/rich-content-integration.test.js(L2o):.markmap svg 渲染。


17. 豆瓣卡片 douban

用途:嵌一本书 / 一部电影 / 一张专辑的豆瓣条目卡(封面 + 评分 + 元数据)。2026-10-19 起改为主题内置本地实现,封面在构建时下载到本地,不再依赖第三方图片代理。

学习路径:最小示例 → 电影与音乐 → 完整参数 → 进阶 → 边界与坑

17.1 最小示例

效果(页面实际渲染):

对应源码:

{% douban book 30376420 %}

17.2 常用变体

变体 1:电影

效果:

对应源码:

{% douban movie 24745500 %}

变体 2:音乐

效果:

对应源码:

{% douban music 35099703 %}

17.3 完整参数

位置参数必填默认说明
1 / 2type✅—book / movie / music 三选一
1 / 2id✅—豆瓣条目 ID,必须匹配 /^\d+$/(纯数字)
—顺序——用 isNaN 判定:数字那项当 ID、非数字那项当类型,顺序随意

配置项(根 _config.yml 的 doubanCard)

配置键可选值默认说明
cookie豆瓣 Cookie 字符串空部分条目/接口需要登录态时使用
localImagestrue / falsetrue封面是否下载到本地;false 时直连豆瓣(会 418)
imagePath目录路径source/medias_webp/douban本地封面落盘目录
imageUrlPrefixURL 前缀/medias_webp/douban渲染时拼出的图片访问前缀

本地化机制

项说明
代码themes/matery/scripts/tags/douban-card.js
依赖cheerio + superagent
缓存DoubanCard.json(按 url 去重;命中则不访问豆瓣)
封面构建时下载到 source/medias_webp/douban/{type}-{id}.jpg,已存在则跳过
Referer按类型内置豆瓣域 Referer(防盗链要求)
暗色遮罩 brightness(0.6) → 暗色 0.4;文字用 var(--white)

17.4 进阶:type 与 id 顺序互换

isNaN 判定谁是谁,所以 {% douban 30376420 book %} 与 {% douban book 30376420 %} 等价。

效果:

对应源码:

{% douban 30376420 book %}

17.5 边界与坑

情况行为
类型拼错 / ID 非数字(如 30376420a)Promise.reject → 构建失败(不是静默跳过)
顺序互换({% douban 30376420 book %})同样有效(isNaN 决定谁是 ID)
豆瓣返回 418(防盗链)构建期带类型 Referer 下载到本地,正常显示
localImages: false直接引用豆瓣原图 → 418,封面空白
缓存命中不访问豆瓣(断网也能构建)
缓存文件项目根 DoubanCard.json,按 url 去重;删除即强制刷新
抓取失败降级占位图 + 占位文案(书「见字如晤」/ 影「灯影绰约」/ 乐「乐以忘忧」),不阻塞构建
封面文件已存在跳过下载(增量构建快)

背景:豆瓣已收紧防盗链,第三方代理转发会收到 418(图片全站空白)。本地化后一次下载、长期可用。详见 docs/EXPERIENCE.md(豆瓣卡片经验)。

17.6 小结

  • 最常用:{% douban book 30376420 %}(type + 纯数字 ID)
  • 顺序随意:非数字当类型、数字当 ID
  • ID 必须纯数字,否则构建直接失败
  • 封面本地化:localImages: true(默认),别关

附:非 tag 主题功能

以下三项不属于「文章内嵌 tag 插件」范畴(§1–§17),而是主题级独立功能。分组于此以区分 tag 与非 tag 功能边界。

18. 打字机 typing(fun_features)

用途:首页 Banner 副标题逐字打出 —— 首屏的一点点仪式感(与交互视觉篇 §3 的 subtitle.typed 同源,配置聚合在 fun_features.typing)。

学习路径:最小示例 → 变体 → 完整参数 → 边界与坑

18.1 最小示例

效果(说明):首页副标题逐字打出 + 光标闪烁;本文不是首页,实际效果见站点首页(.typed-cursor 元素存在即生效)。

对应源码:

fun_features:
  typing:
    enable: true      # 打字机开关
    typeSpeed: 70     # 打印速度,数字越大越慢
    cursorChar: "_"   # 光标字符
    loop: false       # 是否循环播放

18.2 常用变体:单页关闭

在文章 front-matter 里关掉本页打字机,其余页面不受影响。

效果(说明):该页副标题直接静态显示,无逐字动画。

对应源码:

# 文章 front-matter
subtitle: false

18.3 完整参数

配置键可选值默认说明
fun_features.typing.enabletrue / false—总开关
typeSpeed数字主题默认每字间隔,越大越慢
cursorChar任意字符串\|光标字符
looptrue / falsefalse播完是否重来
index.slogan.apiURL空从接口取副标题(失败回落 text)

18.4 边界与坑

情况行为
改配置后页面不变Hexo 缓存未失效 → 必须 npm run clean && npm run build
page.subtitle: false仅本页关闭,首页不受影响
index.slogan.api 不可达自动回落 text 字段,不会空白
与 subtitle.typed 同时配同源配置,以 fun_features.typing 为准

18.5 小结

  • 最常用:fun_features.typing.enable: true
  • 想调速度:typeSpeed 调大更慢
  • 单页关掉:front-matter subtitle: false
  • 改完必须 clean 构建

测试断言:tools/tests/rich-content-integration.test.js(L2o):首页 .typed-cursor 存在。


19. 社交分享 sharejs

用途:文章底部一键分享条(微博 / 微信 / QQ / 豆瓣等),只出现在文章页。

学习路径:最小示例 → 平台子集 → 完整参数 → 备选方案

19.1 最小示例

效果(说明):文章底部出现 .social-share 分享条,按钮顺序由 sites 决定。

对应源码:

sharejs:
  enable: true
  # 支持顺序:twitter, facebook, google, qq, qzone, wechat, weibo, douban, linkedin
  sites: twitter,facebook,qq,qzone,wechat,weibo,douban

19.2 常用变体:只留国内平台

效果(说明):分享条只渲染 wechat,weibo,qq 三个按钮。

对应源码:

sharejs:
  enable: true
  sites: wechat,weibo,qq

19.3 完整参数

配置键可选值默认说明
sharejs.enabletrue / false—总开关
sharejs.sites逗号分隔平台名主题默认渲染顺序 = 书写顺序
可用平台twitter / facebook / google / qq / qzone / wechat / weibo / douban / linkedin—不在列表里的不渲染
渲染范围仅 page.layout == 'post'—首页/独立页不显示

19.4 备选方案:addthis(与 sharejs 二选一)

效果(说明):分享条换成 AddThis 的实现,样式与站点风格略有差异。

对应源码:

addthis:
  enable: false
  pubid: 613a04428f842fe1

19.5 小结

  • 最常用:sharejs.enable: true + sites 列表
  • 只在国内用:wechat,weibo,qq 足够
  • 顺序即书写顺序
  • 别同时开 addthis:二选一

测试断言:tools/tests/rich-content-integration.test.js(L2o):.social-share 组件存在。


20. Live2D 看板娘

用途:页面右下角常驻 Live2D 模型(纯静态,无 PHP 后端),可交互、可换模型。

学习路径:最小示例 → 回访守卫 → 完整参数 → 边界与坑

20.1 最小示例

效果(说明):页面右下角出现看板娘(#live2dcanvas / #waifu);首访不加载,第二次访问才出现。

对应源码:

live2dWidget:
  enable: true

20.2 常用变体:调回访门槛

效果(说明):默认 viewCount > 1 才加载;把门槛改成 > 0 则首访也加载(牺牲首屏性能)。

对应源码:

# 主题源码中的加载守卫(themes/matery 的 live2d 初始化)
# localStorage.viewCount > 1 才初始化模型

20.3 完整参数

配置键可选值默认说明
live2dWidget.enabletrue / false—看板娘总开关
模型目录静态文件source/live2d/Cubism2 / Cubism5 双运行时
明暗主题自动跟随—暗色下切换对应模型/配色
回访守卫localStorage.viewCount > 1开启首访不加载,性能保护

20.4 边界与坑

情况行为
首次访问看不到预期行为(viewCount > 1 才加载)
改了 enable 不生效必须 clean 构建(Hexo 缓存)
模型不动检查 source/live2d/ 资源是否完整(skip_render 静态目录)
移动端通常隐藏,避免遮挡内容

20.5 小结

  • 最常用:live2dWidget.enable: true
  • 验证时要刷新两次(回访守卫)
  • 模型放 source/live2d/,Cubism2/5 都支持
  • 改配置后 clean 构建

测试断言:tools/tests/rich-content-integration.test.js(L2o):设置 viewCount=3 后看板娘容器存在。


⚠️ 已知坑

坑 1:标题编号双重叠加

现象:标题渲染为「1. 1. 概述」(两个编号)。

原因:主题 CSS 自动给 h2–h6 加编号(默认开启),手写编号(如 ## 1. 概述)会叠加。

正确做法:手写编号的页面在 front-matter 加 closeAutoTocNum: true;不手写编号的页面不要加该字段。

本文已正确设置 closeAutoTocNum: true。

坑 5:改配置后必须 clean 构建

现象:修改 tag 插件相关配置(如 post.mermaid.enable、live2dWidget.enable、sharejs.enable 等)后页面不变。

原因:Hexo 缓存机制不会自动检测配置变更。

正确做法:改配置后必须 npm run clean && npm run build。


附:tag 插件速查表

插件语法关键参数是否需要 frontmatter
note{% note 颜色 %}内容{% endnote %}6 色否
timeline{% timeline 标题,颜色 %}...{% endtimeline %}标题/颜色否
tabs{% tabs 名称,序号 %}...{% endtabs %}名称/序号否
label{% label 颜色@文字 %}颜色在前文字在后否
button{% button 链接,文字,图标,title %}链接第一/文字第二否
githubCard{% githubCard user:xx repo:yy %}user/repo否
mermaid{% mermaid %}...{% endmermaid %}图表类型是(mermaid: true)
pdf{% pdf URL %}URL否
insertmd{% insertmd 'file.md' %}文件名否
wechat_dialog{% wechat_dialog %}User:/Assistant: 行{% endwechat_dialog %}User/Assistant 行否
groupimage{% groupimage 数量 布局 %}![图](url){% endgroupimage %}数量/布局否
cardurl{% cardurl [url=xxx] %}url/title/desc否
admonition::: warning 内容 :::container :::;原 {% admonition %} tag 已废弃否
encrypt{% encrypt 密码 "标题" "简介" %}...{% endencrypt %}密码/标题/简介否
公式$...$ / $$...$$mathjax3(SSR SVG)/ katex 引擎是(mathjax: true)
markmap{% markmap 350px %}列表{% endmarkmap %}高度/features(math/prism/zoom)否
douban{% douban book\|movie\|music id %}type/id 顺序可互换;封面本地化否
打字机fun_features.typingtypeSpeed/cursorChar/loop否
分享sharejs.sitestwitter/wechat/weibo 等否(仅 post 页)
Live2Dlive2dWidget.enable回访用户守卫 viewCount>1否

参考&致谢

系列教程

全部文章RSS订阅

Hexo 主题功能测试系列


Hexo主题功能测试-内容tag篇
发布于
2026年8月7日
许可协议。转载请注明来源
赏
评论
数据加载中 ...
 上一篇

阅读全文

Hexo主题功能测试-交互视觉篇
Hexo主题功能测试-交互视觉篇 Hexo主题功能测试-交互视觉篇
主题交互与视觉功能完整教程与测试:代码块增强、图片灯箱、打字机、页面特效、繁简转换、阅读进度、返回顶部、打赏弹窗、打印样式。每项含用途、完整参数、多用法示例、实现效果,供自动化测试断言。
2026-08-07
下一篇 

阅读全文

在Linux上运行OpenAI Codex Desktop完整指南
在Linux上运行OpenAI Codex Desktop完整指南 在Linux上运行OpenAI Codex Desktop完整指南
OpenAI Codex Desktop 官方仅提供 macOS 和 Windows 版本。但社区的力量是无穷的——通过 codex-desktop-linux 项目,我们可以在 Linux 上运行完整的 Codex Desktop 桌面应
2026-06-23