内容控件演示
目录
这篇文章把主题支持的内容控件逐个演示一遍,方便速查。
图片
普通 Markdown 写法就够了,主题会自动加上懒加载:

想让图片居中并带图注,用 figure 短路代码:

还可以加小标题、限制宽度、让点击图片跳转:

正文里的图片都可以点击放大,按 Esc 或点空白处关闭。
图册
多张图片用 gallery 短路代码,每行写「图片地址 | 说明」:
想固定列数就加 cols="2":
附件下载
把文件放到 static/files/ 下,用 attachment 短路代码做成下载卡片,
文件名和大小会自动读出来:
PDF 内嵌预览
用 pdf 短路代码把 PDF 直接嵌在文章里。桌面浏览器都能正常预览;
手机浏览器不支持内嵌时会显示"点这里打开"的提示,图注里也始终附有打开链接。
代码
普通代码块还是老写法,鼠标移上去右上角会出现「复制」按钮:
const greeting = (name) => `你好,${name}`;
console.log(greeting('世界'));
想带上文件名和语言标签,用 code 短路代码(可以加 opts="linenos=table" 显示行号):
| |
引用本地代码片段
同一段代码要在好几篇文章里出现时,不用复制粘贴,把它放进 assets/snippets/,
然后用 snippet 短路代码引用:
#!/usr/bin/env bash
#
# 新建一篇文章,然后直接用编辑器打开。
#
# 用法:
# ./new-post.sh 我的新文章
#
# 想换编辑器就改下面的 EDITOR,或者先 export EDITOR=vim 再运行。
set -euo pipefail
name="${1:?用法: ./new-post.sh 文章文件名}"
EDITOR="${EDITOR:-code}"
SITE_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SITE_DIR"
hugo new content "posts/${name}.md"
"$EDITOR" "content/posts/${name}.md"
文件名和语言都会自动读出来(语言按扩展名判断),也可以手动指定:
{{< snippet path="nginx.conf" file="网站配置" lang="nginx" opts="linenos=table" >}}
好处是只有一份文件需要维护:改了片段,所有引用它的文章一起更新; 而且引用的是本地文件,构建不需要联网,内容也不会在你不知情的时候变。
音频
把音频文件放进 static/media/,用 audio 短路代码引用:
视频
放在自己服务器上的视频用 video 短路代码,可以配封面图:
终端录制(asciinema)
用 asciinema 短路代码播放终端操作录制。下面这段是本地文件(static/casts/demo.cast):
倍速与循环播放
让读者自己调速
加上 speeds 参数,播放器下面会出现一个速度选择框:
常用参数:
| 参数 | 作用 |
|---|---|
speed="2" | 播放速度,默认 1 |
loop="true" | 循环播放 |
autoplay="true" | 自动播放(常和 loop 一起用,做成"一直跑着"的效果) |
speeds="0.5,1,2" | 播放器下面加一个速度选择框,让读者自己调速 |
idle-time-limit="2" | 空闲时间上限(秒),超过就快进,用来跳过录制里无聊的等待 |
start-at="3" | 从第 3 秒开始播 |
theme="solarized-light" | 配色,可选 asciinema / tango / solarized-dark / solarized-light / monokai / nord |
也可以直接引用 asciinema.org 上的录制,填网址里的编号即可:
{{< asciinema id="335480" autoplay="true" speed="1.5" idle-time-limit="2" theme="solarized-light" >}}
提示框
用来强调说明、注意事项或警告,只有左边一条竖线和标题颜色不同:
折叠面板
把不常看的内容收起来,页面会清爽不少:
点开看看里面有什么
这里是折叠起来的内容,同样支持 Markdown,也可以放图片、代码块等。
- 第一步
- 第二步
默认就展开的面板
open="true" 的面板一进来就是展开的。图表(Mermaid)
用文字描述就能画图,语法参考 Mermaid 文档。 下面三种最常用,图表在浏览器里实时绘制,只有用到它的页面才会加载 Mermaid。
流程图
graph LR
A[写 Markdown] --> B[hugo 构建]
B --> C{自己看一眼}
C -->|没问题| D[public/ 上线]
C -->|有问题| A时序图
sequenceDiagram participant U as 读者 participant B as 浏览器 participant S as 静态服务器 U->>B: 打开文章 B->>S: 请求 HTML S-->>B: 返回页面 B->>S: 再请求 CSS 和 JS S-->>B: 返回资源 B-->>U: 渲染完成
sequenceDiagram
autonumber
actor User as 用户
participant CLI as 调试器 CLI
participant Files as 本地配置/音频文件
participant OTA as OTA 服务
participant WS as 小智 WebSocket 服务
participant ASR as 服务端 STT
participant LLM as 服务端 LLM
participant TTS as 服务端 TTS
participant Player as 本机音频播放器
User->>CLI: 启动 python debug_websocket.py
CLI->>Files: 读取 config.json
Files-->>CLI: device_id、client_id、OTA、交互和音频参数
alt ota.enabled = true
CLI->>OTA: HTTPS POST OTA 查询<br/>Activation-Version: 1<br/>Device-Id / Client-Id / User-Agent / Accept-Language
Note over CLI,OTA: JSON 系统信息包含模拟固件版本、芯片、板型、MAC、UUID、Flash、分区表等
alt OTA 只返回 activation,没有 websocket 配置
OTA-->>CLI: activation.message、activation.code
CLI-->>User: 显示验证码并等待,不退出
User->>OTA: 在小智平台添加设备并输入验证码
User->>CLI: 回到终端按 Enter
CLI->>OTA: 再次 POST OTA 查询
OTA-->>CLI: websocket.url、token、version 等配置
else OTA 已返回 websocket 配置
OTA-->>CLI: websocket.url、token、version 等配置
end
else ota.enabled = false
CLI->>Files: 读取 websocket 直连配置
Files-->>CLI: URL、token、协议版本
end
CLI->>Files: 读取首轮 input_audio 的 Ogg Opus 文件
Files-->>CLI: 解析出 OpusHead、OpusTags 和音频 Opus packets
CLI->>WS: 建立 WebSocket 连接
Note over CLI,WS: 握手 HTTP Headers:Protocol-Version、Device-Id、Client-Id、可选 Authorization
CLI->>WS: JSON hello<br/>version、transport=websocket、features.mcp=true<br/>audio_params: Opus / 16000 Hz / mono / 60 ms
WS-->>CLI: JSON hello<br/>transport=websocket、session_id、服务端 audio_params
Note over CLI,WS: 例如客户端请求 16000 Hz;服务端可能返回 24000 Hz,后续按服务端采样率封装回复音频
loop 每一轮语音对话
CLI->>WS: JSON listen/start<br/>session_id、mode=manual
loop 上传录音中的每个 Opus packet
CLI->>WS: WebSocket Binary:一个 Opus 音频包
Note over CLI,WS: 调试器按音频包时长节奏发送;协议 v1 为裸 Opus,v2/v3 带二进制帧头
end
CLI->>WS: JSON listen/stop<br/>session_id
opt 服务端发送 MCP 消息
WS-->>CLI: JSON type=mcp<br/>例如 initialize / 工具相关 payload
end
WS->>ASR: 将收到的语音交给语音识别
ASR-->>WS: 识别出的用户文本
WS-->>CLI: JSON type=stt、text
Note over CLI,User: 这是识别结果,不是调试器上传的文本
WS->>LLM: 将识别文本交给模型生成回复
LLM-->>WS: 回复文本;可伴随情绪等信息
opt 服务端发送模型状态或情绪
WS-->>CLI: JSON type=llm、emotion 等
end
WS->>TTS: 将回复文本合成为语音
TTS-->>WS: 合成音频及句子边界
WS-->>CLI: JSON type=tts、state=start
loop 每个 TTS 句子,可能有多个句子
WS-->>CLI: JSON type=tts、state=sentence_start、text
Note over CLI,WS: sentence_start 中的 text 是当前播报句子的文本
loop 当前句子的每个音频包
WS-->>CLI: WebSocket Binary:一个下行 Opus 音频包
end
WS-->>CLI: JSON type=tts、state=sentence_end
end
opt 服务端发送结束标记
WS-->>CLI: JSON type=tts、state=stop
end
alt 收到 tts/stop
Note over CLI: 本轮 TTS 音频接收完成
else 服务端未发送 tts/stop,但已收到 sentence_end
Note over CLI: 等待最后一条消息后的静默时间,默认 3 秒;有新消息则重新计时
else 回复等待超时
Note over CLI: response_timeout_seconds 默认 60 秒;若已开始收到 TTS,则保存已收到的音频
end
CLI->>Files: 将下行 Opus packets 封装成 Ogg Opus
Files-->>CLI: response.ogg 或 response_02.ogg 等
opt 自动播放已启用
CLI->>Player: 使用系统默认播放器打开回复文件
Player-->>User: 播放 TTS 回复
end
alt interactive = false 或启动参数 --once
CLI->>WS: 关闭 WebSocket 连接
else 交互模式
CLI-->>User: 提示输入下一段 Ogg Opus 录音路径
alt 输入录音路径
User->>CLI: 文件路径
CLI->>Files: 读取并解析下一段录音
else 直接按 Enter
User->>CLI: 回车
CLI->>Files: 重放默认样例录音
else 输入 q
User->>CLI: q
CLI->>WS: 关闭 WebSocket 连接
end
end
end甘特图
gantt
title 博客改版排期
dateFormat YYYY-MM-DD
section 内容
整理旧文章 :done, a1, 2026-09-01, 5d
写新文章 :active, a2, after a1, 10d
section 样式
定配色 :done, b1, 2026-09-03, 3d
调正文排版 :b2, after b1, 4d思维导图
底层同样是 Mermaid,缩进决定层级:
mindmap
root((博客))
内容
文章
关于页
样式
颜色变量
排版
功能
搜索
图册数学公式
这个主题用 Hugo 内置的 KaTeX,公式在构建时就渲染成 HTML, 页面不加载任何公式脚本,也不会出现「公式先闪一下」。
输入语法就是标准 LaTeX——和 MathJax 是同一套语法。
行内公式
用一对美元符号包起来:、、、 、、、、。
块级公式
单独占一段,用两个美元符号:
常用结构
分数、根号、上下标:
向量、希腊字母、关系符:
极限与导数:
矩阵:
分段函数:
多行对齐(& 是对齐点,\\ 换行):
化学式(KaTeX 自带 mhchem 扩展):
算法复杂度:
公式其他要点
- 想显示美元符号本身:前面加反斜杠,写成
\$100 - 公式里放中文:要用
\text{}包起来,例如 - 颜色:可以跟着站点的强调色走,例如
- 行内公式定界符是
$...$,不要用\(...\)(符号会被 Markdown 的转义规则吃掉) - 公式写错时不会让构建失败,KaTeX 会把它标红显示在原来的位置,正好一眼能看到
外部视频(YouTube / Bilibili)
视频放在视频网站上时,用 youtube / bilibili 短路代码嵌入,会自动按 16:9 铺满正文:
{{< youtube id="aqz-KE-bpKQ" start="30" caption="视频下方的说明" >}}
{{< bilibili bvid="BV12341117rG" page="2" >}}
竖屏视频加 ratio="9 / 16" 就能改成竖向。
链接与脚注
站外链接会自动在新标签页打开,并在后面加一个小小的 ↗ 标记,例如 Hugo 官方文档;站内链接保持原样,例如 归档页。
脚注用 Markdown 原生写法就行1,会自动收到文章底部。
分栏卡片
用 columns + column 把内容并排放在卡片里,放不下会自动折行:
前端
- 只用 CSS 变量
- 不用前端框架
- 骨架屏?不必了
后端
- 没有后端
- 构建即发布
部署
hugo --minify- 丢到任意静态托管
看板
kanban + kanban-column 用来展示任务状态,每一栏里的列表项会变成卡片。
屏幕放不下时整块可以左右滑动:
待办
- 写新文章
- 整理旧笔记
进行中
- 给主题补文档
已完成
- 站内搜索
- 图册
密码访问
有两种用法。整篇文章加密:在 front matter 里写一行 password = '秘密'
(演示文章见「这篇需要密码」)。只加密一段内容:用 protected 短路代码:
Emoji
hugo.toml 里已经打开了 enableEmoji,所以正文里可以直接写短代码:
写 :smile: :rocket: :sparkles: 会显示成 😄 🚀 ✨。
也可以直接用输入法打出来的表情符号,效果一样 👍
这就是脚注的内容。另外,鼠标移到任意标题上会出现一个
#号,点它就能复制这一节的链接地址。 ↩︎
