加载中...

加载中...

内容 Tag 插件

本页汇总 matery 主题的 14 个内容 tag 插件:note 便签、timeline 时间线、tabs 标签页、label 行内标签、button 按钮、githubCard GitHub 卡片、mermaid 图表、pdf 嵌入、insertmd 片段插入、wechat_dialog 微信对话、groupimage 分组图片、cardurl URL 卡片、admonition 容器、encrypt 片段加密。每个插件给出用途、语法、示例、效果,示例可直接复制到文章测试。

怎么读这页:每个插件都按「效果 → 对应源码」成对展示——先看页面真实渲染,再抄下面的 Markdown 源码。参数表里的默认值全部取自 themes/matery/scripts/tags/*.js 的实际代码,不是猜的。

与「布局与页面」的行内加粗标签风格不同,本页因需容纳代码块与真实渲染示例,采用三级子标题(最小示例/常用变体/完整参数/进阶组合/边界与坑/小结)分隔。前 12 个为主题原生注册的 tag(themes/matery/scripts/tags/index.js 统一注册),admonition 由 markdown-it-container 插件提供(根 _config.yml 注册 7 种容器:warning/note/info/attention/error/tip/hint)。
注意:在 Markdown 代码块中展示 {% xxx %} 这类 tag 语法时,必须用 raw 标签包裹(raw 与 endraw 成对),否则 nunjucks 会预渲染导致构建报 unknown block tag。

基础标签插件

1. 便签 note

用途:文章内的彩色提示块,用于警示 / 成功 / 信息 / 危险等强调内容。

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

1.1 最小示例

不写任何关键字,就是一个灰边框提示块——结构只有「开始标签 + 内容 + 结束标签」。

效果:

这是一条最简提示。

对应源码:

{% note %}这是一条最简提示。{% endnote %}

1.2 六种颜色

效果:

default 默认便签——普通提示

primary 主要便签——重点强调

success 成功便签——操作完成提示

info 信息便签——补充说明

warning 警告便签——注意事项

danger 危险便签——严重警告

对应源码:

{% note default %}**default** 默认便签——普通提示{% endnote %}
{% note primary %}**primary** 主要便签——重点强调{% endnote %}
{% note success %}**success** 成功便签——操作完成提示{% endnote %}
{% note info %}**info** 信息便签——补充说明{% endnote %}
{% note warning %}**warning** 警告便签——注意事项{% endnote %}
{% note danger %}**danger** 危险便签——严重警告{% endnote %}

颜色对照:

关键字图标语义
defaultf0a9(右箭头)普通提示
primaryf055(加号)重点强调
successf058(对勾)操作成功
infof05a(信息)补充说明
warningf06a(感叹)注意事项
dangerf056(叉)严重警告

1.3 隐藏图标 no-icon

no-icon 是修饰符不是颜色,它会占用第二个关键字位置,与颜色顺序可互换。

效果:

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

对应源码:

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

1.4 可折叠

前两个关键字之后任何剩余参数都会拼成折叠标题,输出 <details><summary>。

效果:

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

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

代价

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

对应源码:

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

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

标题与正文都走 Markdown 渲染——标题里可以加粗,正文里可以放列表、代码。但别在便签正文里写 ### 小标题:它会渲染成真标题并进页面 TOC。

1.5 嵌套组合

正文走 Markdown 渲染,因此内部可以再放任何 tag。

效果:

组合示例

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

二级便签

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

对应源码:

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

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

1.6 完整参数

参数按空格切分;note.js 循环最多取 2 个关键字作 CSS 类,其余全部拼成折叠标题。

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

全部可选值:6 种颜色 + 1 个修饰符 no-icon(不是颜色,只隐藏图标)。

别名:{% subnote %} 与 {% note %} 注册的是同一个函数(index.js 中两行 tag.register 指向同一 postNote),行为与样式完全相同,不是「次级」便签。

1.7 边界与坑

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

1.8 小结

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

2. 时间线 timeline

用途:按时间顺序展示事件 / 版本记录 / 成长历程——竖线 + 圆点串起一串带日期的节点。

学习路径:最小示例 → 无标题与颜色 → 节点内 Markdown → 完整参数 → 边界与坑 → 小结

2.1 最小示例

一个标题 + 一个节点就是完整的时间线。节点用 <!-- timeline 日期 --> 与 <!-- endtimeline --> 包住。

效果:

主题功能演进记录

2026-08

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

对应源码:

{% timeline 主题功能演进记录,green %}
<!-- timeline 2026-08 -->
- 新增内容 tag 测试页(note/timeline/tabs/label 等完整教程)
- 相册数据重构(galleries.yml 三种形式)
<!-- endtimeline -->
{% endtimeline %}

2.2 不写标题

timeline 的第一个参数(逗号前)是整体标题;整个参数省略时没有总标题,直接列节点。

效果:

2026-07

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

对应源码:

{% timeline %}
<!-- timeline 2026-07 -->
- 评论区架构优化(comment: waline)
- 修复 href=/ 的 MIME 样式错误
<!-- endtimeline -->
<!-- timeline 2020 -->
- 创建博客,开始 matery 深度定制
<!-- endtimeline -->
{% endtimeline %}

2.3 颜色与节点内 Markdown

第二个参数是追加到 div.timeline 的类名(green / blue / red / orange 等由 CSS 决定),标题与节点内容都走 Markdown。

效果:

v13.27 版本记录

v13.27 发布

豆瓣卡片改为本地实现,封面构建时下载。

v13.26

CSS 变量化 + 暗色适配,涉及 style/ 与 color-schema.styl。

对应源码:

{% timeline **v13.27** 版本记录,blue %}
<!-- timeline v13.27 [发布](https://blog.17lai.site) -->
豆瓣卡片改为**本地实现**,封面构建时下载。
<!-- endtimeline -->
<!-- timeline v13.26 -->
CSS 变量化 + 暗色适配,涉及 `style/` 与 `color-schema.styl`。
<!-- endtimeline -->
{% endtimeline %}

2.4 完整参数

timeline.js 把参数 join(' ') 后按 , 切分:args[0] 是标题,args[1] 是颜色类名(不去空格,写 标题, green 会得到带前导空格的类名,虽不影响显示但不干净)。

位置参数必填默认说明
参数 1(逗号前)整体标题❌无省略则无总标题;走 Markdown
参数 2(逗号后)颜色类名❌无追加到 .timeline;可用值由主题 CSS 决定(green / blue / red / orange 等)
内容节点块✅—每个节点 = <!-- timeline 节点标题 --> + 内容 + <!-- endtimeline -->

节点标题同样走 Markdown(可加链接、加粗、行内代码)。没有节点块时只输出一个空时间线容器。

2.5 进阶/组合

  • 节点内容里可以再写 tag(如 {% label %}、{% button %}),因为节点内容走 Markdown 渲染。
  • 节点数不限;timeline.js 用正则全局匹配,按出现顺序输出。
  • 时间线不是语义化的 <time> 结构,日期只是节点标题文本,排序完全靠你写的顺序(不会自动按日期排)。

2.6 边界与坑

情况行为
节点标记后没有换行(<!-- timeline 2026 -->内容 写在同一行)正则要求 --> 后紧跟 \n → 该节点被静默丢弃
<!--timeline 2026-->(timeline 后少空格)正则要 timeline 带空格 → 不匹配,节点丢失
忘记 <!-- endtimeline -->后续内容全被吞进上一个节点,或整个节点丢失
标题里含逗号split(',') 会把后半段当颜色 → 标题被截断
颜色写错类名照样输出,只是没有对应 CSS,退化为默认色
只想要纯列表不用 timeline,直接写 Markdown 列表更轻量

2.7 小结

  • 记住两件事:节点标记必须独占一行,endtimeline 必须闭合
  • 标题可省:不想要总标题就不写参数
  • 颜色是 CSS 类名,写错不报错但没效果

3. 标签页 tabs

用途:多标签内容切换,适合分类展示 / 步骤说明 / 对比内容。

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

3.1 最小示例

第一个参数是必填的唯一名称(用于生成 DOM id),省略会构建报错并输出空串。

效果:

  • 功能全面:内容 tag 覆盖 10+ 插件
  • 配置灵活:每个功能可独立开关
  • 配置项多,上手有学习成本
  • 部分功能依赖外部 CDN
  • 技术博客:代码 / 教程 / 对比
  • 个人博客:相册 / 项目 / 历程

对应源码:

{% tabs 主题功能对比 %}
<!-- tab 优点 -->
- 功能全面:内容 tag 覆盖 10+ 插件
- 配置灵活:每个功能可独立开关
<!-- endtab -->
<!-- tab 缺点 -->
- 配置项多,上手有学习成本
- 部分功能依赖外部 CDN
<!-- endtab -->
<!-- tab 适用场景 -->
- 技术博客:代码 / 教程 / 对比
- 个人博客:相册 / 项目 / 历程
<!-- endtab -->
{% endtabs %}

3.2 指定激活页

第二个参数是激活序号,从 1 开始;省略或写 0 时激活第一个。

效果:

默认不激活我。

打开页面时默认显示我。

对应源码:

{% tabs 激活序号示例,2 %}
<!-- tab 第一个 -->
默认不激活我。
<!-- endtab -->
<!-- tab 第二个 -->
打开页面时默认显示我。
<!-- endtab -->
<!-- tab 第三个 -->
也不激活。
<!-- endtab %}
{% endtabs %}

3.3 标题加图标

标签标题里用 @图标 追加 Font Awesome 图标,@ 前是文字、@ 后是「前缀:图标名」。

效果:

@fas:info-circle → <i class="fas fa-info-circle">

@fas:download → <i class="fas fa-download">

对应源码:

{% tabs 图标示例 %}
<!-- tab 简介 @fas:info-circle -->
`@fas:info-circle` → `<i class="fas fa-info-circle">`
<!-- endtab -->
<!-- tab 下载 @fas:download -->
`@fas:download` → `<i class="fas fa-download">`
<!-- endtab -->
{% endtabs %}

3.4 完整参数

tabs.js 先 args.join(' ').split(','):args[0] = 名称,Number(args[1]) || 0 = 激活序号。

位置参数必填默认说明
1(逗号前)唯一名称✅—缺失时 hexo.log.error 并返回空串(页面什么都不显示)
2(逗号后)激活序号❌0(= 第一个)从 1 开始计数;非数字按 0 处理
内容标签块✅—每个标签 = <!-- tab 标题 --> + 内容 + <!-- endtab -->

图标前缀可选值(processIcon 的 prefixMap):fas / far / fal / fad / fab,以及别名 solid→fas、regular→far、light→fal、duotone→fad;不写前缀默认 far(regular)。

别名:subtabs、subsubtabs 注册的是同一个函数,语法与行为完全一致(只是命名上的嵌套习惯,并不会生成嵌套样式)。

3.5 进阶/组合

  • 每个标签的内容区独立走 Markdown 渲染,可以放列表、表格、代码、其他 tag。
  • DOM 结构:<div class="tag-common tabs" id="名称小写-空格转-"> + ul.nav-tabs + div.tab-content,切换靠前端 JS 加 .active。
  • 名称会被转成 id:小写 + 空格换 -;中文原样保留。

3.6 边界与坑

情况行为
不写名称[Tabs] Missing required tab name 错误日志 + 整块空输出
同一页两个 tabs 用同名id 重复 → 点击切换可能串到另一组
激活序号超出范围(如 3 个标签写 ,9)没有任何一个带 .active → 全部收起,需要手动点开
<!-- tab 标题 --> 后不换行直接写内容正则要求 --> 后是 \n → 该标签被丢弃
忘记 <!-- endtab -->后续内容吞进上一个标签
名称含逗号被 split(',') 截断,后半段当激活序号

3.7 小结

  • 必填名称,且全页唯一
  • 内容区是完整 Markdown,可以随便嵌套
  • 激活序号从 1 开始

4. 行内标签 label

用途:行内彩色小标签,用于关键词 / 状态 / 分类标注。

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

4.1 最小示例

参数顺序反直觉:颜色@文字——颜色在 @ 前,文字在 @ 后。

效果:

本站使用 Hexo 构建,主题为 matery。

对应源码:

本站使用 {% label primary@Hexo %} 构建,主题为 {% label success@matery %}。

4.2 六种颜色

效果:

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

对应源码:

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

4.3 完整参数

label.js:args.join(' ').split('@') → classes = args[0] || 'default'、text = args[1] || '',输出 <span class="label label-<classes>">。

位置参数必填默认说明
@ 前颜色❌defaultdefault / primary / success / info / warning / danger
@ 后文字✅空串纯文本(不是 Markdown,不渲染加粗/链接)

文字写空时 label.js 会 log.warn('[Matery] Label text must be defined!'),并渲染一个空 span。

4.4 边界与坑

情况行为
只写 {% label 文字 %}(没有 @)text 为空 → 渲染出空标签 + 构建警告;文字 被当成颜色类名
颜色写错(label-warningx)输出 class="label label-warningx",没有对应 CSS → 只剩基础 .label 样式
文字里含 @({% label info@a@b %})split('@')[1] 只取 a,b 被丢弃
想在标签里加粗/加链接不支持,text 原样输出
一行写多个支持,用空格分隔即可(见 4.2)

4.5 小结

  • 永远是 颜色@文字,忘了 @ 就是空标签
  • 文字不支持 Markdown
  • 6 色之外的值不报错但无样式

5. 按钮 button

用途:文章内 CTA 按钮,用于跳转 / 下载 / 操作入口。

学习路径:最小示例 → 图标与 title → 别名与纯文字 → 完整参数 → 边界与坑 → 小结

5.1 最小示例

链接在第一个参数(源码 url=args[0]、text=args[1]),与 label 的「修饰在前」相反。

效果:

返回首页

对应源码:

{% button /,返回首页 %}

5.2 图标与 title

第三个参数是 Font Awesome 图标名,第四个是 hover 提示(不是颜色)。

效果:

访问 GitHub 全部标签

对应源码:

{% button https://github.com/appotry,访问 GitHub,github,打开作者主页 %}
{% button /tags,全部标签,tags %}

5.3 别名与纯文字

{% btn %} 与 {% button %} 是同一个函数;图标留空即为纯文字按钮(注意逗号占位)。

效果:

关于我 归档

对应源码:

{% btn /about,关于我,user-circle,关于 %}
{% button /archives,归档,,无图标的纯文字按钮 %}

5.4 完整参数

button.js:args.join(' ').split(','),按位置取 4 个值。

位置参数必填默认说明
1url✅—跳转地址,可相对(/);为空时 log.warn('URL can NOT be empty.') 且 href=""
2文字❌空串按钮显示文字
3图标❌无Font Awesome 名;不以 fa 开头时自动补 fa fa-,所以 home 与 fa fa-home 等价,fas fa-home 原样保留
4title❌无hover 提示文字(不是颜色)

输出结构:<a class="theme-btn" href="..." title="..."><i class="..."></i>文字</a>。

5.5 进阶/组合

  • 按钮可以放在 note / tabs / timeline 内容里(它们都走 Markdown 渲染)。
  • 外链建议配 title 说明去向;站内跳转用相对路径,迁移域名时不失效。
  • 图标名查 Font Awesome;写错只会得到一个空 <i>。

5.6 边界与坑

情况行为
文字里含英文逗号split(',') 会把后半段当图标 → 文字被截断(中文全角逗号 , 安全)
图标写 fas fa-homestartsWith('fa') 为真 → 不再补前缀,输出 <i class="fas fa-home"> ✔
图标写 fa-home同样不补前缀 → class="fa-home" 缺少 fa 基类,图标不显示
只写 url 不写文字渲染一个空文字按钮(只剩图标或什么都没有)
url 为空(,文字)警告 + href="" 指向当前页
想要新窗口打开该 tag 不支持 target,需要手写 <a target="_blank">

5.7 小结

  • url 第一、文字第二、图标第三、title 第四
  • 图标名不带前缀最省事(github),不要写 fa-home
  • 文字里别放半角逗号

6. GitHub 卡片 githubCard

⚠️ repo 实际必填:libs/githubCards/cards/ 只有 default.html / medium.html,没有 user-only 模板。只写 user: 会请求 /cards/&s=48 → 404,卡片不显示。务必同时给 repo:。

用途:展示 GitHub 仓库信息卡片(仓库名 / 简介 / 星标 / 分支数)。

学习路径:最小示例 → 尺寸参数 → 完整参数 → 边界与坑 → 小结

6.1 最小示例

参数是 key:value 形式(冒号分隔、空格分隔多个),user 必填。

效果:

对应源码:

{% githubCard user:appotry repo:hexo %}

6.2 尺寸参数

效果:

对应源码:

{% githubCard user:appotry repo:hexo-theme-matery width:360 height:180 %}

6.3 完整参数

github-card.js 把每个参数按第一个冒号切成 key/value,再拼成 data-* 属性交给前端 JS。

参数必填默认说明
user✅—GitHub 用户名;缺失时输出红色错误提示 缺少user参数
repo❌无仓库名;省略则卡片可能显示用户信息
width❌由前端 JS 决定宽度,px 后缀会自动去掉(360px → 360)
height❌由前端 JS 决定高度,同样去 px
theme❌default(不输出该属性)值等于 default 时不写 data-theme
target❌blank(不输出该属性)值等于 blank 时不写 data-target
client_id❌无GitHub OAuth App 的 client_id,用于提升 API 限额

安全设计:不支持 client_secret——OAuth secret 出现在前端 HTML 里等于公开。需要更高限额请用 client_id 或后端代理。

6.4 进阶/组合

  • 卡片内容由前端 JS 异步拉取 GitHub API 渲染,所以构建产物里没有仓库数据,也就不会被 Hexo 的增量缓存影响。
  • 无 JS 时显示 <noscript> 提示;API 失败时前端降级为普通链接。
  • 同一页放太多卡片会撞 GitHub 未认证 API 限额(60 次/小时/IP),演示页建议 ≤2 个。

6.5 边界与坑

情况行为
缺 user构建日志 error,渲染红字配置错误提示
参数值含空格Hexo 按空格切参数 → 值被截断
写 theme:default / target:blank不输出对应属性(等于没写),不是错误
写 client_secret:xxx被忽略(代码里没有这个键),不会泄露也不会生效
用逗号分隔参数不支持,key:value 之间只能用空格

6.6 小结

  • 最小可用:user:xxx repo:yyy
  • 尺寸写数字即可,px 会自动去掉
  • 需要高限额用 client_id,永远别放 secret

媒体与内容标签

7. Mermaid 图表 mermaid

用途:渲染 Mermaid 图表(流程图 / 时序图 / 类图等)。

学习路径:front-matter 前置条件 → 最小示例(流程图)→ 时序图 → 类图 → 完整参数 → 边界与坑 → 小结

7.1 前置条件(必读)

使用前必须在文章 front-matter 声明,否则 Mermaid JS 不加载、图表不渲染:

mermaid: true

(主题 post.mermaid.enable: true 已默认开启;specific: true 模式下仅声明了 mermaid: true 的文章才加载 Mermaid JS。本页 front-matter 已声明。暗色模式自动适配。)

7.2 最小示例:流程图

效果:


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 %}

7.3 常用变体:时序图

效果:


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 %}

7.4 常用变体:类图

效果:


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

对应源码:

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

7.5 完整参数

mermaid.js 只是把标签体包成 <pre><code class="mermaid ...">,没有自己的参数——所有能力来自 Mermaid 语法本身。

位置参数必填默认说明
标签参数额外类名❌无args.join(' ') 原样拼到 class="mermaid ...",一般不用
标签体Mermaid 源码✅空首行决定图表类型

图表类型(首行关键字):graph / flowchart(流程图)、sequenceDiagram(时序图)、classDiagram(类图)、stateDiagram-v2(状态图)、erDiagram(ER 图)、gantt(甘特图)、pie(饼图)、mindmap(思维导图)。

7.6 进阶/组合

  • 图表渲染成 SVG,主题已做暗色适配,切换明暗模式无需重绘配置。
  • 大图可在首行后加 %%{init: {'theme':'neutral'}}%% 之类指令调整,但会与主题暗色适配冲突,慎用。
  • 想放别的图表(折线/柱状)用 echarts tag,不是 mermaid。

7.7 边界与坑

情况行为
忘写 front-matter mermaid: true输出 <pre><code class="mermaid"> 原文,不渲染(specific 模式不加载 JS)
语法错误Mermaid 前端报错,图表区域显示错误信息
标签体为空输出空 <pre>,无报错
忘 {% endmermaid %}构建报 unclosed tag
用 ```mermaid 代码块不生效——主题注册的是 {% mermaid %} 块标签

7.8 小结

  • 必须先 mermaid: true
  • 语法全在标签体里,tag 本身没有参数
  • 首行决定图表类型

8. PDF 嵌入 pdf

用途:文章内嵌 PDF 文件查看器。

学习路径:最小示例 → 完整参数(三种 URL 类型)→ 边界与坑 → 小结

8.1 最小示例

标签体只有一个参数:PDF 地址。下面嵌入的是站点仓库里真实存在的 /doc/Linux学习笔记.pdf(1.7 MB,浏览器按需下载)。

效果:

对应源码:

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

8.2 完整参数

pdf.js 只有一个参数,并按顺序做类型探测(第一个命中的规则生效)。

位置参数必填默认说明
1文件 URL✅—本地路径或远程地址;为空时 src=""

类型探测规则(源码顺序,先命中先返回):

顺序条件输出容器
1URL 含 .pdf<embed type="application/pdf">div.pdf-container
2含 drive.google.com<iframe> Google Docs 预览div.google-doc-container
3含 www.slideshare.net<iframe> Slidesharediv.slideshare-container
4其它回退到 <embed>div.pdf-container

三种模式的高度都固定 550px(height="550" / height:550px),宽度 100% 自适应。

8.3 进阶/组合

  • 想改高度或加封面,直接手写 <iframe>/<embed> 比改 tag 更简单(tag 没有高度参数)。
  • PDF 放在 source/doc/ 之类目录时,记得该目录不被 skip_render 拦截,否则 404。
  • 需要 PDF.js 阅读器(自带工具栏/翻页)时用 object_url 模式,主题当前未启用。

8.4 边界与坑

情况行为
Google Drive 链接但以 .pdf 结尾先命中规则 1 → 走 <embed>,拿不到 Google 预览
URL 里含 .pdf 的查询串(?file=x.pdf)仍判为 normal,符合预期
Supademo/其它分享站落到规则 4 → <embed>,多数会显示空白或下载
移动端浏览器可能不内联 PDF,显示下载按钮或空白
文件不存在构建不报错,页面里是一个 550px 的空白框

8.5 小结

  • 一个参数、固定 550px 高
  • .pdf 判断在最前面,Drive 链接别带 .pdf 后缀
  • 移动端体验无法保证,重要文档建议同时给下载链接

9. 插入片段 insertmd

用途:插入 source/ 下的 Markdown 模板片段,实现内容复用(异步渲染,支持子目录递归)。

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

9.1 最小示例

路径相对 source/ 目录。下面插入的是站点真实的 source/_template/系列教程.md(系列文章模板片段)——所以你看到的小标题和 RSS 链接来自那个文件,不是本页写的。

效果:

系列教程

全部文章RSS订阅

对应源码:

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

9.2 完整参数

insertMD/parseArgs.js + renderHtml.js:两个参数,都为位置参数。

位置参数必填默认说明
1文件路径 / 目录✅—相对 source/;传目录则递归收集所有 .md 并按路径字母序合并
2分隔符❌空串多文件合并时插在文件之间的 Markdown 文本(如 '---')

渲染细节:片段内容以 disableNunjucks: true、disableTagFilter: true 走 Markdown 渲染器,因此片段里可以再写 tag、也不会被 nunjucks 预解析破坏。

9.3 进阶/组合

  • 系列文章内链的标准做法:模板放 source/_template/<系列名>.md,各篇文章文末插一行 insertmd → 改一处,全系列同步。
  • 传目录可批量合并,例如 {% insertmd '_template/' '---' %} 会把所有模板按字母序拼起来(谨慎使用,内容量很大)。
  • 片段标题会进入页面 TOC(上面 9.1 里那个「系列教程」小标题就是插入进来的)。

9.4 边界与坑

情况行为
路径不存在构建日志 [insertmd] 错误: Path not found,输出空串(页面静默少一段)
路径写 source/_template/x.md错——路径已相对 source/,会变成 source/source/...
文件名含空格必须用引号包住('a b.md')
目录里含非 .md 文件跳过,只收 .md
片段里写了 raw / endraw 包裹语法会被 disableNunjucks: true 当纯文本 → 显示为字面量
async: true + 同页多个 async tag正常,Hexo 会等待所有 async tag

9.5 小结

  • 路径相对 source/,必填
  • 目录会递归合并,第二参数是分隔符
  • 这是「系列文章内链」的实现基础,比在每篇里硬编码链接更省事

10. 微信对话卡片 wechat_dialog

用途:渲染微信聊天界面卡片,用于对话示例 / 客服场景。

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

10.1 最小示例

没有 tag 参数,全部信息在标签体里:必须以 User: / Assistant: 开头成行书写。

效果:

User

你好,我想了解 matery 主题

Assistant

好的,有什么可以帮您?

对应源码:

{% wechat_dialog %}
User: 你好,我想了解 matery 主题
Assistant: 好的,有什么可以帮您?
{% endwechat_dialog %}

10.2 多轮与 Markdown

同一角色连续多行会合并成一条气泡;气泡内容走 Markdown(加粗/列表/代码都行)。

效果:

User

主题支持哪些功能?

Assistant

主要分三类:

  • 内容 tag(note/tabs/timeline 等)
  • 媒体嵌入(pdf/github 卡片)
  • 搜索与评论
User

有示例吗?

Assistant

有,本页每个组件都是 效果 + 源码 成对展示。

对应源码:

{% wechat_dialog %}
User: 主题支持哪些功能?
Assistant: 主要分三类:
- 内容 tag(note/tabs/timeline 等)
- 媒体嵌入(pdf/github 卡片)
- 搜索与评论
User: 有**示例**吗?
Assistant: 有,本页每个组件都是 `效果 + 源码` 成对展示。
{% endwechat_dialog %}

10.3 完整参数

wechat.js 不看 args,只解析标签体:逐行匹配 /^(User|Assistant):/。

位置参数必填默认说明
标签参数无——写了也不报错,但完全不生效
标签体User: 内容✅—右侧绿色气泡(角色判断 role.toLowerCase() === 'user')
标签体Assistant: 内容✅—左侧灰色气泡(其它角色名一律当 assistant 处理)

头像:硬编码为 /medias_webp/avatar/2.jpg(User)与 /medias_webp/avatar/1.jpg(Assistant),不可通过参数修改。

别名:{% wd %} 等价于 {% wechat_dialog %}。

10.4 进阶/组合

  • 气泡内部可以继续写 tag(如 {% label %}),因为整段内容走 Markdown 渲染。
  • 想换头像/昵称:目前只能改主题模板或 wechat.js,没有参数入口。
  • 适合「客服问答」「AI 对话示例」,不适合长文(气泡宽度按容器自适应)。

10.5 边界与坑

情况行为
写小写 user: 你好行首正则大小写敏感 → 不识别为新消息,被追加到上一条内容里
角色名写 Bot:同样不被识别,追加到上一条
角色行前有缩进(User: x)^ 锚定行首 → 不识别,追加到上一条
标签体里没有任何 User:/Assistant:输出空的 div.chat-container
User: 冒号后有多个空格冒号后内容原样保留,多余空格不影响
想自定义头像不支持,只能改代码/模板

10.6 小结

  • 只认 User: / Assistant: 两个行首前缀,大小写敏感
  • 多行自动合并成一条气泡
  • 别名 wd 更短

11. 分组图片 groupimage

用途:将多张图片按行列网格布局展示(自动分列布局)。

学习路径:最小示例 → 显式布局 → 完整参数 → 边界与坑 → 小结

11.1 最小示例

第一个参数是图片张数,图片用标准 Markdown 语法写在标签体里。不写布局时按张数查默认布局。

效果:

封面1
封面2
封面3

对应源码:

{% groupimage 3 %}
![封面1](/medias_webp/cover/hexo.webp)
![封面2](/medias_webp/cover/hexo.webp)
![封面3](/medias_webp/cover/hexo.webp)
{% endgroupimage %}

11.2 显式布局

第二个参数是每行张数,用 - 分隔;各数之和必须等于张数,否则回退到默认布局。

效果:

封面1
封面2
封面3

对应源码:

{% groupimage 3 2-1 %}
![封面1](/medias_webp/cover/hexo.webp)
![封面2](/medias_webp/cover/hexo.webp)
![封面3](/medias_webp/cover/hexo.webp)
{% endgroupimage %}

11.3 完整参数

group-image.js:total = parseInt(args[0], 10)、layout = args[1]?.split('-').map(Number)。

位置参数必填默认说明
1图片数量✅—整数 2–10;必须与实际图片数一致
2布局❌按数量查表每行张数,- 分隔(如 2-1);各数之和 ≠ 数量时忽略
标签体Markdown 图片✅—![说明](URL),可多行

默认布局表(DEFAULT_LAYOUTS):

张数每行张数视觉
21,1两行单图
32,1上二下一
42,2两行两列
53,2上三下二
63,3两行三列
73,2,2三行
83,2,3三行
93,3,3三行三列
103,2,2,3四行

别名:{% gi %} 等价于 {% groupimage %}。

11.4 进阶/组合

  • 图片用 Markdown 语法 → 自动继承主题的 lightGallery 点击放大(hover 放大 + 灯箱)。
  • 支持图片尺寸插件(markdown-it-imsize)的写法,但尺寸与网格列宽会互相挤压,谨慎组合。
  • 想统一裁切比例,用 CSS 控制 .group-image-wrap img 而不是改 tag。

11.5 边界与坑

情况行为
布局和 ≠ 数量(3 1-1)静默回退到该数量的默认布局
数量 > 10 或 < 2走 defaults():每行固定 3 张
实际图片少于数量后几行为空 <div>,网格出现空洞
实际图片多于数量多于布局总数的图片被丢弃(不报错)
写 3 2,1(逗号)split('-') 得到 [NaN] → 非法布局 → 回退默认
标签体里没有图片输出空容器

11.6 小结

  • 数量必须等于实际图片数
  • 布局可省,省了按默认表
  • 别名 gi 更短,但语义不直观,教程里建议写全名

12. URL 卡片 cardurl

用途:展示链接的摘要卡片(标题、描述、图标),用于推荐链接 / 引用资源。

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

12.1 最小示例

参数是方括号 [key=value] 形式,每个参数之间用空格分隔。

效果:

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

对应源码:

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

12.2 带图标

avatar 会在左侧渲染一个圆形图标(点击同样跳转)。

效果:

https://hexo.io
快速、简洁且高效的博客框架

对应源码:

{% cardurl [url=https://hexo.io] [title=Hexo 官网] [desc=快速、简洁且高效的博客框架] [avatar=/medias_webp/cover/hexo.webp] %}

12.3 完整参数

url.js 用 /\[([^=]+)=(.+)\]/ 逐参数匹配,只认这 4 个键。

参数必填默认说明
url✅—目标链接(href 与 src 都会转义)
title❌回退为 url 的值卡片标题
desc❌无 → 渲染成 undefined 字样描述文字
avatar❌无(不输出图标)图标图片 URL

所有值都经过 esc() HTML 转义(含引号),因此不能注入 HTML,也不渲染 Markdown。

12.4 进阶/组合

  • 三个键都不写、只写 [url=...] 时,卡片标题就是 URL 本身。
  • 转义是安全设计:title 里写 <script> 会原样显示为文本。
  • 与 button 的区别:cardurl 是「资源卡片」(标题+描述+图标),button 是「操作按钮」。

12.5 边界与坑

情况行为
省略 desc描述区显示字面量 undefined(String(undefined))——建议永远写 desc,哪怕只有一个词
值里含空格Hexo 按空格切参数 → (.+) 只拿到第一段,值被截断
用 : 而不是 =正则只认 = → 该参数被忽略
url 省略title 与 href 都变 undefined
想换行/多段描述不支持,desc 是单段纯文本

12.6 小结

  • 四个键:url(必填)/ title / desc / avatar
  • 一定写 desc,否则页面出现 undefined
  • 值里不要有空格

高级标签插件

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

用途:用 ::: 类型 语法渲染提示容器(warning / note / info / attention / error / tip / hint),区别于 note tag——由 markdown-it-container 插件实现(根 _config.yml 注册,无需 front-matter)。

学习路径:最小示例 → 七种类型 → 完整参数 → 与 note 的区别 → 边界与坑 → 小结

13.1 最小示例

效果:

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

对应源码:

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

13.2 七种类型

效果:

note —— 补充说明。

info —— 信息提示。

attention —— 注意警示。

error —— 严重错误。

tip —— 小技巧。

hint —— 隐含提示。

对应源码:

::: note
note —— 补充说明。
:::

::: info
info —— 信息提示。
:::

::: attention
attention —— 注意警示。
:::

::: error
error —— 严重错误。
:::

::: tip
tip —— 小技巧。
:::

::: hint
hint —— 隐含提示。
:::

13.3 完整参数

位置参数必填默认说明
开启行::: + 类型✅—类型必须与 _config.yml 注册值完全一致(小写)
中间容器内容❌空走 Markdown 渲染(列表/代码/表格都行)
结束行:::✅—可以比开启行更长(::::),但要成对

全部可选值(根 _config.yml 的 markdown-it-container options):warning / note / info / attention / error / tip / hint,共 7 种。

对应语义色:warning / attention = 黄色琥珀 #ffc107;note = 绿色;info = 蓝色;error = 红色;tip / hint = 蓝色(与 info 同色)。

13.4 与 note tag 的区别

维度{% note %} tag::: 类型 容器
实现主题 tag(scripts/tags/note.js)markdown-it 插件(渲染器层)
颜色数6 + no-icon7 种类型
可折叠✅(写标题即折叠)❌
自定义图标✅(每个颜色有固定图标)❌
产物 classdiv.note primarydiv.warning(用类型名做类)
嵌套✅ 可嵌套 note❌ 不支持嵌套

13.5 边界与坑

情况行为
类型写错(::: Warning)不是已注册容器 → 原样输出为段落文本
开启行/结束行不在行首不识别,退化为普通文本
想嵌套容器markdown-it-container 默认不嵌套,内层会被当普通内容
内容为空输出空容器,不报错
与 note tag 混用都可用,但同一页出现两种提示块风格会显得杂乱

13.6 小结

  • ::: 类型 三行结构,类型必须小写且已注册
  • 没有折叠/图标能力——需要时用 note tag
  • 属于 Markdown 语法,不是 tag,所以不需要 raw(但代码块里示例仍建议包裹)

14. 内部集成插件配置(tag_tabs)

用途:主题内部集成的 tag 插件(参考 hexo-tag-common)的 CDN 与布局配置。

学习路径:先看效果(改它到底有没有用)→ 配置项 → 代码依据 → 结论

14.1 改配置的效果

效果:没有任何可见变化。 这两个键当前都没有被消费——tabs.js 读取 tag_tabs 后把结果存进局部变量 config,之后再也没引用过(上游插件用它拼前端资源地址,本主题未走这条路径)。

对应源码(themes/matery/scripts/tags/tabs.js 第 5–16 行):

module.exports = function(hexo) {
  const defaultConfig = {
    cdn: 'https://fastly.jsdelivr.net/npm/',
    layout: ['post', 'page']
  };

  const userConfig = hexo.theme.config.tag_tabs || {};
  const config = {          // ← 组装完就没人用了
    ...defaultConfig,
    ...userConfig,
    layout: Array.isArray(userConfig.layout) ? userConfig.layout : defaultConfig.layout
  };

  return function(args, content) { /* 无 config 引用 */ };
};

14.2 配置项

对应配置(userConfig/_config.tmp.yml → 构建时注入为 themes/matery/_config.yml):

tag_tabs:
  cdn: https://fastly.jsdelivr.net/npm/   # CDN 地址(代码默认 jsdelivr)
  layout:                                  # 注释写「生效布局」,实际未消费(代码默认 ['post', 'page'])
    - post
    # - page
    # - home
键代码默认当前是否生效说明
cdnhttps://fastly.jsdelivr.net/npm/❌ 不生效组装进 config 后未被使用
layout['post', 'page']❌ 不生效同上;配置文件里勾了 post 也不影响 tabs 渲染

14.3 边界与坑

情况行为
把 cdn 换成自己的镜像无任何变化(没人读)
只勾 post,在 wiki 页用 tabs照常渲染——layout 不参与判断
删掉整个 tag_tabs 段不报错,走代码默认值

14.4 小结

  • 这两个键是历史遗留,保留默认即可
  • 真正的 tabs 样式在 source/css/ 的 .tabs 规则里,改那里才有用
  • 若将来接入 CDN 按需加载,才需要动 cdn

15. 内容加密 encrypt(2026-09-07 新增)

用途:文章内某段 markdown 内容加密,输入密码解密展开显示。每个片段独立密码。

学习路径:最小示例 → 嵌套 tag → 完整参数 → 边界与坑 → 小结

15.1 最小示例

效果(密码:demo):

加密片段示例

对应源码:

{% encrypt demo "加密片段示例" "输入 demo 解锁" %}
这段内容在**构建时**就被 AES-GCM 加密,页面 HTML 里没有明文。
{% endencrypt %}

15.2 嵌套其它 tag

片段内容先走 Markdown、再走 tag 渲染,所以内部可以嵌 note / timeline 等组件(方案 Y:after_post_render 二次渲染)。

效果(密码:demo2):

嵌套标签示例

对应源码:

{% encrypt demo2 "嵌套标签示例" "输入 demo2 解锁" %}
**粗体**和 {% label primary@行内标签 %} 都在加密片段里。

{% note info 内部 note %}
加密片段里还能放便签。
{% endnote %}
{% endencrypt %}

15.3 完整参数

encrypt-tag.js:args[0] 密码、args[1] 标题、args[2] 简介(Hexo 已自动去引号)。

位置参数必填默认说明
1密码✅—解密密码;缺失时不加密,原文明文输出
2提示标题❌空加密块提示语(密码框上方)
3内容简介❌空输入框占位文字
标签体被加密内容❌空Markdown,可含内部 tag

运行时特性:

  • 渲染后 HTML 不含明文(加密发生在最终 HTML 阶段)
  • 解密后 1 天内刷新免密(本地存派生密钥 dk,带 TTL)
  • 同页多个加密块各自独立,互不串键(2026-09-23 修复并发串扰)

15.4 进阶/组合

  • 密码含空格必须加引号;不加引号会被拆成多个参数。
  • 整页加密用 front-matter password:,不要和片段加密混用同一密码体系(前者把整篇 HTML 加密,后者只加密片段)。
  • 片段里的标题不会进 TOC(TOC 基于明文结构生成,加密后前端才展开)。

15.5 边界与坑

情况行为
不写密码log.warn + 原文明文返回 → 等于没加密,务必检查
密码含空格未加引号密码被截断,后续词变成标题/简介
标题/简介顺序颠倒提示语出现在错误位置,但不影响解密
同一页两个片段同密码各自独立存储(key 含片段序号),不会一次解锁全部
依赖 async: true与 insertmd 等 async tag 同页时顺序正确(2026-09-23 修复后)

15.6 自动化测试

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

15.7 小结

  • 三参数:密码(必填)→ 标题 → 简介
  • 密码绝不能省,否则内容明文泄露
  • 片段内部可以正常用其它 tag

16. 脑图 markmap(hexo-markmap 插件)

用途:把一段 Markdown 列表渲染成交互式思维导图(点击展开/折叠)。

学习路径:实际效果 → 源码 → 参数 → 性能与测试 → 边界与坑

16.1 实际效果

效果:渲染为可缩放/折叠的 SVG 脑图({% markmap %} 块标签,内容是一段多级列表)。完整实例见 Hexo Docker 环境与基础配置篇 的「写博客的重要性」一节——那里用 650px 高度展示了 8 个分支的脑图。

本 wiki 页为控制体积未内嵌渲染,但下面的源码可直接复制到任意文章使用。

对应源码:

{% markmap 650px %}

- 1、对知识点总结、回顾、思考
	- A、写作整理资源,补充学习
	- B、加深对技术理解
- 2、帮助别人
	- A、别人通过你的博客学习到一些知识
	- B、帮助后来者节省时间

{% endmarkmap %}

16.2 完整参数

位置参数必填默认说明
标签参数高度❌插件默认高度带单位的高度(如 650px);不同文章可给不同高度
标签体多级列表✅空纯 Markdown 列表,缩进(Tab 或 2 空格)决定层级

插件配置(根 _config.yml,当前值):

hexo_markmap:
  CDN: 'jsdelivr'   # fastly.jsdelivr.net 不可达,故用 cdn.jsdelivr.net
  features: [math, prism, zoom]
  theme: auto       # 跟随明暗模式

16.3 性能优化(2026-09-25 / 09-26)

  • 按需注入:无脑图的页面不再注入 markmap 插件资源(288 页各省 ~106KB)
  • 加密页串行加载:加密页改用串行加载器,修复解密后依赖顺序错乱(markmap 先于其依赖执行)

16.4 自动化测试

tools/tests/unit/unit-markmap-prune.test.js(6 用例,验证无脑图页零注入);rich-content-integration.test.js(L11,含 markmap 渲染断言)

16.5 边界与坑

情况行为
内容不是列表(段落/标题)层级退化为单节点,几乎看不出结构
用空格缩进但数量不一致层级错乱——同一层必须用相同缩进
高度不写单位(650)样式无效,按默认高度渲染
页面内没有 markmap不注入任何插件资源(按需注入生效)
写在加密页里靠串行加载器保证依赖顺序,勿手动改加载方式

16.6 小结

  • 块标签 + Markdown 列表,不是代码块
  • 高度写 650px 这种带单位值
  • 不用就不注入,零成本

附:tag 插件速查表

插件语法关键参数是否需 front-matter
note{% note 颜色 %}内容{% endnote %}6 色 + no-icon + 标题折叠否
timeline{% timeline 标题,颜色 %}...{% endtimeline %}标题 / 颜色否
tabs{% tabs 名称,序号 %}...{% endtabs %}名称(必填) / 激活序号否
label{% label 颜色@文字 %}颜色在前文字在后否
button{% button 链接,文字,图标,title %}链接第一 / 文字第二否
githubCard{% githubCard user:xx repo:yy %}user 必填 / repo / width / height / theme / target / client_id否
mermaid{% mermaid %}...{% endmermaid %}图表类型(graph / sequenceDiagram / classDiagram)是(mermaid: true)
pdf{% pdf URL %}URL(自动识别 googledoc / slideshare / normal)否
insertmd{% insertmd '路径.md' %}相对 source/ 的路径或目录 + 分隔符否
wechat_dialog{% wechat_dialog %}User:/Assistant: 行{% endwechat_dialog %}User/Assistant 行格式否
groupimage{% groupimage 数量 布局 %}![图](url){% endgroupimage %}数量 2–10 / 布局 - 分隔否
cardurl{% cardurl [url=xx] [title=xx] [desc=xx] [avatar=xx] %}url 必填 / desc 建议必填否
admonition::: 类型 内容 :::warning / note / info / attention / error / tip / hint否(根 _config.yml 全局启用)
encrypt{% encrypt 密码 "标题" "简介" %}...{% endencrypt %}密码必填 / 标题 / 简介否
markmap{% markmap 650px %}列表{% endmarkmap %}高度参数 / 内容为 Markdown 列表否

常见坑:

  • 代码块内展示 {% xxx %} 语法必须用 raw 标签包裹,否则构建报 unknown block tag
  • label 颜色在 @ 前、文字在 @ 后;button 链接在第一个参数
  • mermaid 忘记 front-matter mermaid: true 时图表不渲染(specific 模式)
  • wechat_dialog 只认 User: / Assistant: 行首格式(大小写敏感),不是行内参数
  • timeline / tabs 的 <!-- xxx --> 标记后必须换行,否则该节点被静默丢弃
  • cardurl 省略 desc 会显示 undefined,务必写上
  • groupimage 的布局各数之和必须等于图片数量,否则静默回退默认布局
  • encrypt 忘记密码会明文输出内容,不会报错
  • 构建失败时先看构建日志有无 Nunjucks Error,再检查对应 tag 语法
评论
数据加载中 ...