Lavender's Blog

浩瀚众星,皆降为尘

在字里行间,遇见灵感

在原有 Shoka 特殊语法的基础上,整理适合写教程、项目介绍和图文记录的 Markdown 扩展,并补充本站已有的交互式 3D 留声机标签,以及本次新增的行内批注、参数卡片和阅读增强。这篇文章既是使用手册,也是一张可以直接操作的展示页:每一节先给出源码,再展示实际效果。

原有语法仍然可以照常使用,包括提示块、折叠块、选项卡、公式、Mermaid、行号与高亮代码块等。原有手册见 Step.4 主题特殊功能(转载)。本文的新增语法和查看器不会替换原有写法或音乐语法。

#一、扩展速览

扩展最简写法适合的内容
文件树:::file-tree文件夹层级、项目结构、文件增删说明
代码树:::code-tree一个示例包含多个代码文件时,切换查看
步骤流:::steps安装、配置、排错等需要按顺序操作的教程
图片画廊网格:::grid截图、作品集、照片;点击查看完整原图
GitHub 仓库卡片::github{repo="作者/仓库"}分享开源项目
交互式 3D 留声机gramophone 成对标签歌单唱片架、音乐播放、歌词与模型互动
行内批注[+术语] 与 [+术语]: 说明不打断阅读的术语解释和补充说明
参数卡片:::field 参数名配置项、接口字段、类型、必填与默认值
增强代码块三个反引号后填写 js title="app.js"文件标题、增删标记、局部折叠、复制和下载
代码组::: code-group labels=[npm,pnpm]同一操作的不同命令,切换选项卡阅读

#使用前需要设置什么?

除留声机外,表中的语法直接写在文章正文中即可,不需要为每篇文章单独打开开关,也不需要安装 Astro 或 Svelte。留声机还需要在该文章顶部的 Front Matter 中加入 gramophone: true ,具体见第七节。批注、参数卡片、流程图查看器与阅读设置见第九节。

  • 多行容器以 ::: 结束;GitHub 卡片只有一行,不写结束标记。
  • 参数写在开头的花括号中,含空格的值使用英文引号。
  • 示例中的四个反引号仅用于在教程里展示三个反引号的写法;真正使用时,复制示例框里面的内容即可。
  • 修改语法插件后,如果本地预览一直开着,需要重启一次 hexo s ;正常写文章不需要反复重启。

#二、文件树:清楚展示目录与文件改动

#2.1 用缩进列表写文件树

- 表示一项,前面多缩进两个空格,表示它属于上一层目录。

:::file-tree{title="博客示例目录"}
- source
  - _posts
    - **hello.md** # 重点关注的文章
  - images/ # 点击展开这个目录
    - cover.webp
- themes
  - shoka
    - ++ markdown-extensions.styl # 新增样式
    - -- legacy.css # 准备移除的旧文件
- package.json # 依赖清单
:::

实际效果: 可以点击 images 左边的箭头展开目录。

博客示例目录FILE TREE
  • source
    • _posts
      • hello.md重点关注的文章
    • images点击展开这个目录
      • cover.webp
  • themes
    • shoka
      • markdown-extensions.styl新增新增样式
      • legacy.css删除准备移除的旧文件
  • package.json依赖清单

#2.2 名称后面这些符号是什么意思?

写法显示效果
- src 下方继续缩进识别为目录,默认展开
- src/ 下方继续缩进识别为目录,默认折叠
- empty/ ,没有子项显示为空目录
- ++ new.js新增标记
- -- old.js删除标记和删除线
- **important.js**强调文件名
- main.js # 入口文件文件名旁边显示说明

注意,说明的写法是 “空格 + # + 空格”。 C# 这样的文件名不会被误当成说明。这里的新增、删除标记只是文档展示,不会真的修改或删除电脑里的文件。

#2.3 直接粘贴终端的树形结构

已经有 tree 一类命令生成的目录文本,也可以直接使用 file-tree 代码块。

```file-tree title="构建后的文件" icon="simple"
public
├── css/
│   ├── app.css
│   └── markdown-extensions.css
├── js
│   ├── app.js
│   └── markdown-extensions.js
└── index.html
```
构建后的文件FILE TREE
  • public
    • css
      • app.css
      • markdown-extensions.css
    • js
      • app.js
      • markdown-extensions.js
    • index.html

支持 ├── 、 └── 、 │ 和 Windows 风格的 +--- 、 \--- 、 | 。 icon="simple" 使用统一颜色的图标;省略该参数或写 icon="colored" ,会按文件类型使用不同颜色。

#三、代码树:像小型编辑器一样切换文件

#3.1 手动写入多个文件

把多个代码块放进 :::code-tree ,每个代码块通过 title="路径/文件名" 声明它代表的文件。

:::code-tree{title="一个主题按钮" height="360px" entry="src/main.js"}
```html title="index.html"
<button class="hello">点我试试</button>
```
```js title="src/main.js"
const button = document.querySelector('.hello');
button.addEventListener('click', () => {
  button.textContent = '你好,Shoka!';
});
```
```css title="src/button.css"
.hello {
  padding: 10px 18px;
  border: 0;
  border-radius: 12px;
  color: white;
  background: linear-gradient(135deg, #ab66ff, #819ee8);
}
```
:::

实际效果: 点击左侧文件切换代码,右上角可以复制当前文件或放大阅读。手机上文件导航会放到代码上方。

一个主题按钮3 个文件
index.html
index.html
<button class="hello">点我试试</button>
src/main.js
src/main.js
const button = document.querySelector('.hello');
button.addEventListener('click', () => {
  button.textContent = '你好,Shoka!';
});
src/button.css
src/button.css
.hello {
  padding: 10px 18px;
  border: 0;
  border-radius: 12px;
  color: white;
  background: linear-gradient(135deg, #ab66ff, #819ee8);
}

代码树只是展示代码,不会执行示例里的 JavaScript,也不会把 HTML 变成真正的网页按钮。普通代码块的原有语法没有变化, title="路径" 的文件导航含义只在代码树内部使用。

#3.2 代码树参数与默认文件

参数用途默认值
title代码树顶部名称示例工程
height桌面阅读区域高度,支持 px 、 rem 、 em 、 vh420px
entry第一次打开时选择哪个文件,推荐填写完整相对路径第一个文件
iconcolored 或 simplecolored

也可以在某个代码块的首行末尾加上 :active :

:::code-tree{title="选择默认文件"}
```json title="package.json"
{ "name": "demo" }
```
```js title="main.js" :active
console.log('默认先显示这个文件');
```
:::
选择默认文件2 个文件
package.json
package.json
{ "name": "demo" }
main.js
main.js
console.log('默认先显示这个文件');

同时写了 entry 和 :active 时,以 entry 为准。文件名应保持唯一;没有匹配到时回到第一个文件。

#3.3 从专门准备的本地示例目录导入

不想在文章里重复粘贴多份代码,可以把准备公开的示例放进项目根目录的 code-examples/ ,然后用一行指令导入。

示例文件放在哪里FILE TREE
  • my-blog
    • code-examples
      • markdown-demo
        • main.js
        • message.js
        • styles
          • button.css
    • source
      • _posts

写法:

@[code-tree title="公开的本地示例" entry="main.js" height="320px"](/code-examples/markdown-demo)

实际效果: 下方三个文件在构建时读取并写进静态 HTML,不需要访客额外下载这些源文件。

公开的本地示例3 个文件
main.js
main.js
// 这是可以公开展示的示例,不包含网站真实配置。
import { greeting } from './message.js';
const button = document.querySelector('.hello');
button?.addEventListener('click', () => {
  button.textContent = greeting('Shoka');
});
message.js
message.js
export function greeting(name) {
  return `你好,${name}!`;
}
styles/button.css
styles/button.css
.hello {
  padding: 10px 18px;
  border: 0;
  border-radius: 12px;
  color: white;
  background: linear-gradient(135deg, #ab66ff, #819ee8);
}

等价的单行指令写法:

::code-tree{dir="/code-examples/markdown-demo" title="公开的本地示例" entry="main.js"}

这里的 /code-examples/ 是项目目录约定,不是网站公开 URL,也不是 Windows 磁盘根目录。为避免泄露隐私,不允许直接导入 themes/shoka 、 _config.yml 或项目外目录。请先把需要公开的、已经脱敏的示例放进 code-examples/ 。

会跳过隐藏文件、符号链接、常见敏感文件名和不支持的文件类型。上限为 40 个文件、单文件 64 KB、合计 512 KB。即使文件名普通,也要自己确认内容里没有密码、密钥等信息。

更新被导入的示例后,如果文章仍显示旧版本,可以执行 hexo clean ,再执行 hexo g 重新生成。 code-examples/ 本身不会被当成网页目录发布,只有文章指令选中的内容会进入页面。

#四、Markdown 步骤流

#4.1 把有序列表变成操作流程

:::steps 中放一个有序列表,每个最外层列表项就是一步。属于同一步的段落、代码或子列表,需要和该步骤的正文对齐缩进。

:::steps[发布一篇博客]
1. **创建文章**
   在项目目录执行:
   ```bash
   hexo new "我的新文章"
   ```
2. **编写并预览**
   打开新文章,修改标题并填写正文。
   - 检查图片与链接。
   - 检查手机上的阅读效果。
3. **生成静态文件**
   ```bash
   hexo g
   ```
:::
发布一篇博客STEP BY STEP
  1. 创建文章

    在项目目录执行:

    hexo new "我的新文章"
  2. 编写并预览

    打开新文章,修改标题并填写正文。

    • 检查图片与链接。
    • 检查手机上的阅读效果。
  3. 生成静态文件

    hexo g

#4.2 标题和起始编号

标题可以写成 :::steps[标题] ,也可以写成 :::steps{title="标题"} 。 start 用于接着上一段流程继续编号。

:::steps{title="继续检查" start="4"}
1. **确认生成结果**:查看页面是否正常。
2. **发布更新**:按本站原有方式发布静态文件。
:::
继续检查STEP BY STEP
  1. 确认生成结果:查看页面是否正常。
  2. 发布更新:按本站原有方式发布静态文件。

步骤流中只放一个有序列表。普通介绍段落放在容器外;结构不符合要求时会保留为普通 Markdown,不会把正文丢掉。

#五、图片画廊网格与 Fancybox

#5.1 最简单的画廊

把普通 Markdown 图片放到 :::grid 里,每张图片单独成段,中间留一个空行。

:::grid
![晨色山丘](/images/markdown-demo/aurora.svg)
![轨道与星球](/images/markdown-demo/orbit.svg)
![一页创作笔记](/images/markdown-demo/notes.svg)
:::
晨色山丘
晨色山丘
轨道与星球
轨道与星球
一页创作笔记
一页创作笔记

默认桌面三列,卡片比例 16/10 。点击任意一张,可在 Fancybox 中查看完整原图,并在这一个画廊内部切换。灯箱会按原图比例淡入,不会先把裁剪后的卡片拉伸再恢复比例; aspect 和 fit 只影响页面中的缩略图。

#5.2 参数说明

参数可用值默认值
columns整数 1 到 63
aspect正数比例,例如 16/9 、 3/4 、 1/116/10
fitcover 或 containcover
  • cover :居中裁剪并填满卡片,网格更整齐;原图不受影响。
  • contain :完整显示图片,比例不同时会保留背景留白,适合不能裁剪的截图。
  • 图片的 alt 是无障碍说明,也是默认图注;有可选标题时,标题优先作为图注。
  • 小于 768px 时最多两列,小于 480px 时一列, columns="1" 始终保持一列。
  • 不足一行的图片靠左排列,不会被强行拉宽。

#5.3 两列方形卡片

:::grid{columns="2" aspect="1/1" fit="cover"}
![山丘插画](/images/markdown-demo/aurora.svg "封面裁成正方形,点开仍是完整横图")
![星球插画](/images/markdown-demo/orbit.svg "这张竖图也会裁成相同的卡片比例")
:::
山丘插画
封面裁成正方形,点开仍是完整横图
星球插画
这张竖图也会裁成相同的卡片比例

#5.4 保留完整原图,不做裁剪

:::grid{columns="2" aspect="16/9" fit="contain"}
![完整的竖版星球插画](/images/markdown-demo/orbit.svg "contain:左右留白是正常效果")
![完整的笔记插画](/images/markdown-demo/notes.svg "contain:保留全部边缘内容")
:::
完整的竖版星球插画
contain:左右留白是正常效果
完整的笔记插画
contain:保留全部边缘内容

本页多处重复使用同一张示例图,是为了比较不同的裁剪参数。每个画廊独立分组,不会混进上面或下面的画廊。图片仍使用现有的延迟加载与 Fancybox;不会引入另一套灯箱组件。原有普通图片和 .gallery 语法照常使用。

#六、GitHub 仓库卡片

#6.1 最简写法

repo 填 “所有者/仓库名”,不要粘贴完整网址。

::github{repo="LyraVoid/Shirone"}
LyraVoidShirone

查看项目源码、文档与最新动态

GitHub · 开源仓库

卡片接近可视范围时,才读取 GitHub 的公开仓库信息,补充简介、Stars、Forks、语言和许可证。不需要填写 Token,也不要把私人 Token 写进文章。

#6.2 添加静态说明,或者完全不请求接口

网络不稳定时,可以提供一段静态说明。卡片在请求失败时仍然可阅读、可跳转,不会无限重试或一直显示加载动画。

::github{repo="mrdoob/three.js" description="用于在浏览器中创建三维场景的 JavaScript 库,也是本文留声机使用的图形基础。" fetch="false"}
mrdoobthree.js

用于在浏览器中创建三维场景的 JavaScript 库,也是本文留声机使用的图形基础。

GitHub · 开源仓库

fetch="false" 表示只显示静态卡片,不访问 GitHub API。启用动态信息时,同一仓库在本次浏览中复用缓存约 30 分钟;离开页面会取消未完成的请求。Stars 等数值是公开接口数据,不保证即时更新。

#七、交互式 3D 留声机:在 Markdown 中放一台黑胶播放器

留声机是本站已有的 Hexo 自定义标签,不是 Shirone 提供的组件,也不需要把大段 HTML 粘进文章。这里只演示如何使用;模型与交互的改造过程见 Shoka 主题:使用 Three.js 实现交互式 3D 留声机。

#7.1 最小可用示例

分两步:先在文章顶部两条 --- 之间加入 gramophone: true ,再把成对标签和 YAML 歌单列表放进正文。下面是一个可以复制的新文章示例:

---
title: 我的 3D 留声机
gramophone: true
---
听听音乐吧!
{% gramophone %}
- title: 示例唱片 A
  list:
    - https://music.163.com/playlist?id=3778678
- title: 示例唱片 B
  list:
    - https://music.163.com/playlist?id=19723756
{% endgramophone %}

如果文章已经有标题、日期和标签,只把 gramophone: true 加进原来的 Front Matter,不要再新增第二份 Front Matter。

#7.2 实际效果:挑一张唱片

下方使用两张示例唱片,地址指向网易云的公开榜单,不使用博主的私人歌单。不会自动播放,请自行点击唱片或播放按钮;曲目是否能播放仍取决于音源接口、网络及歌曲版权限制。

正在为留声机上弦唱片与曲库准备中

可以依次试试这些操作:

  1. 点击 “挑选唱片” 或模型后方的唱片架,展开歌单唱片;选择一张,观察换唱片的动画。
  2. 点击 “播放室” 或模型正面的屏幕,打开音乐面板;在 “当前歌单曲目” 中选歌,也可以按歌名或歌手搜索。
  3. 使用播放、暂停、停止、上一首、下一首、进度和音量控制;面板内可以选择单曲循环、列表播放或随机播放。
  4. 播放有歌词的歌曲时查看歌词,也可以展开歌词阅读;没有歌词的歌曲不会凭空生成歌词。
  5. 手机浏览时,先点 “操作模型” 再拖动模型;不操作时关闭它,便于继续上下阅读文章。点击 “复位” 可以恢复视角。

#7.3 如何换成自己的歌单?

配置项写在哪里作用
gramophone: true文章顶部 Front Matter允许这篇文章加载 Three.js 和留声机资源
title标签内部,每个歌单组的第一行唱片架和播放室中显示的歌单名称
list和同组的 title 对齐填写这个歌单组的音乐链接列表
歌单地址list 下方的缩进列表替换为播放器支持的公开歌单链接

增加第三张唱片时,只需在结束标签前再加入一组 title 和 list 。每一组对应唱片架上的一张唱片,而不是每首歌对应一张唱片。

- title: 我的第三张唱片
  list:
    - https://music.163.com/playlist?id=替换为你的公开歌单ID

YAML 缩进请使用空格,不要使用 Tab。 list 和同组的 title 对齐,音乐链接再多缩进两格。示例中的 “替换为你的公开歌单 ID” 需要改成真实 ID,不能原样用于播放。

当前留声机使用固定的画布与播放器标识,因此同一页面只放一组完整的留声机标签;多个歌单放在这一组标签内部,不要复制多台留声机到同一页。不要在公开文章中填写私密歌单链接、登录凭据或接口密钥。

#7.4 为什么只有占位文字,没有模型或音乐?

  • 完全没有模型:检查文章顶部是否开启 gramophone: true ,以及开始与结束标签是否成对。
  • 有模型但没有曲目:检查 YAML 缩进、歌单是否公开,以及音乐接口是否可用。
  • 有曲目但不能播放:尝试其他歌曲,检查音源连接和版权限制;这不一定是 Markdown 语法错误。
  • 修改标签插件后仍是旧效果:重启本地预览,再重新生成并刷新页面。

#八、常见问题与排查

#原来的提示框、折叠框会被改变吗?

不会。本次只增加特定名字的新容器与指令, :::primary 、 +++info 、 ; 选项卡等原有语法继续由原来的渲染逻辑处理。下面仍是原有提示块:

这是原来的 :::success ,它和本文新增的步骤流、文件树可以同时使用。

#嵌套容器如何避免提前结束?

需要在一个容器内部写另一个容器时,外层多写一个冒号,例如外层使用 ::::primary ,内层使用 :::steps 。代码树内部则正常使用三个反引号,不是冒号。

#代码树按钮没出现,或者样式没有变化?

  1. 确认文章中的结束标记完整。
  2. 重启本地预览并强制刷新浏览器。
  3. 部署时同时发布新生成的 css/markdown-extensions.css 和 js/markdown-extensions.js ,不要只更新文章 HTML。
  4. 如果使用自己的静态资源 CDN,也要同步这些新文件。

即使 JavaScript 不可用,代码树仍会显示全部文件内容,不会只剩空白;文件树折叠和步骤流不依赖额外 JavaScript。

#普通页面会多加载这些资源吗?

只有实际使用新语法的文章才会请求扩展样式。代码切换和动态仓库卡片才需要额外的小脚本;文件树和步骤流在构建时已经生成。普通页面不会因此加载 Astro、Svelte 或新的前端框架。

本页为了展示真实留声机,额外开启了 gramophone: true ,会加载对应的 Three.js、留声机脚本、样式和歌单。其他没有开启留声机的文章不会因此加载这些资源;只想写文件树或步骤流时,不需要复制这个开关。

#九、阅读增强:批注、参数卡片与流程图查看

这组扩展参考 Shirone 的阅读设计,沿用本站的 Shoka 主题色。原来的脚注、提示块、代码块和标签页语法不会改变。

#9.1 行内批注

在需要解释的地方写 [+批注名] ,然后在文章中用同名定义补充说明。点击批注会在文字附近展开解释;脚本不可用时,会跳到文末的 “文中批注”,正文仍然可读。

这篇教程采用渐进增强[+渐进增强]。
[+渐进增强]: 先保证正文与链接可用,再添加弹窗、搜索等交互;脚本加载失败也不应丢失内容。

实际效果:这篇教程采用渐进增强渐进增强。同一个批注可以重复引用渐进增强。

#9.2 参数说明卡片

field-group 是可选的外层分组;每张卡片使用 field 参数名称 。 @type 写类型、 @required 标记必填、 @optional 标记可选、 @default 写默认值。元信息后空一行,再写说明正文。

::::field-group
:::field title
@type string
@required
文章的标题,填写一段简洁清楚的文字。
:::
:::field pageSize
@type number
@optional
@default 12
每次展开的记录数量。这里只是**示例参数**,不是博客全局配置。
:::
::::
titlestring必填

文章的标题,填写一段简洁清楚的文字。

pageSizenumber可选
默认值12

每次展开的记录数量。这里只是示例参数,不是博客全局配置。

也支持把参数写在开头,例如 :::field title {type="string" required} 。避免在教程里粘贴自己网站的完整私有配置,只展示相关参数。

#9.3 流程图放大与全屏

仍然使用原来的 Mermaid 代码块,不需要学习另一套流程图语法,也不必为新文章手工添加 mermaid: true 。

```mermaid
flowchart LR
  A[书写 Markdown] --> B[生成静态页面]
  B --> C[按实际内容加载增强]
  C --> D[开始阅读]
```
flowchart LR
  A[书写 Markdown] --> B[生成静态页面]
  B --> C[按实际内容加载增强]
  C --> D[开始阅读]
  • 图表右上角可放大、缩小、适应窗口和全屏查看。
  • 放大后可以拖动。普通滚轮继续滚动文章,不会被图表抢走; Ctrl / ⌘ 加滚轮用于缩放。
  • 焦点位于图表时,可用 + 、 - 缩放,方向键平移, 0 复位。
  • 关闭全屏窗口会回到打开前的位置,不会跳到页面顶部。

#9.4 文章阅读工具在哪里?

文章正文上方提供 “目录”“本文阅读”“分享海报”。手机上还有底部阅读工具栏,支持目录、评论与阅读进度;关闭评论的文章不会显示直达评论按钮。

“本文阅读” 与全站的 “显示与阅读设置” 已分开。全站设置仍负责减弱动效、背景纹理、简洁背景与首页布局;本文阅读只处理文章正文,初始不改变原来的排版。

可以直接在本页尝试:

  1. 点击 本文阅读,选择舒适阅读,或展开正文排版,调整字号、行高、段落间距和宽度。“仅本文” 只改变这篇文章;“作为文章默认” 用于没有单独设置的文章,不影响首页、分类等页面。
  2. 选择 专注阅读,暂时收起侧栏、推荐与封面装饰;点击页面上的 “退出专注阅读” 恢复。退出时保持当前读到的位置。
  3. 选择 教程跟练,第四节的真实步骤会出现 “标记完成”;面板中的复选框与正文同步,刷新后仍记得已经完成的步骤。
  4. 在 “教程辅助” 中选择一个代码文件,点击 固定到伴读窗。代码留在右侧,你可以继续滚动正文;支持复制、切换和跳转原代码,不会执行代码。
  5. 展开 本文速览,按章节、图片、代码、图表或参数定位,之后点击 “返回跳转前”。手机定位后会自动收起抽屉。
  6. “阅读位置” 会保存进度,并提供书签。下次打开文章时,由你点击 “继续阅读”,不会自动跳走。开启段落聚焦后可以点击正文,也可以使用 Alt + ↑ / ↓ 切换段落。
  7. 在正文中选中一句话,再到 私人摘录 中写下想法并保存。支持定位、修改想法和确认删除,不会变成公开评论。

这些记录只保存在当前浏览器,不发送到 Waline,也不会跨设备同步。清理网站数据或关闭无痕窗口可能丢失记录,请勿把它作为重要笔记的唯一备份。没有代码或步骤的文章不会显示相应空工具;完整阅读面板只在打开时加载。

分享海报仍在点击后才生成;图片读取失败会使用后备图,不影响文章阅读或复制文章链接。

旧文章默认在最后更新超过 180 天时显示时效提示。主题只需要维护与本功能相关的配置:

reading:
  enable: true
  notice: true
  notice_days: 180
  related: true
  related_count: 4

这段位于 themes/shoka/_config.yml 。如果单篇不需要提示,在该篇文章顶部添加:

reading:
  notice: false

如果需要关闭该篇的阅读工具、提示和推荐,可用 reading: false 。文章下方及侧栏的推荐根据共同标签和分类生成,不查询数据库。

#9.5 新内容页怎么维护?

  • 个人随笔:使用独立的 Waline / Neon 动态表。在页面点击「发布 / 管理动态」,用 Waline 管理员账号登录即可发布、编辑、保存草稿和回收动态。首次需部署后端并初始化动态表,之后无需 hexo d 。动态支持普通 Markdown,不执行需要 Hexo 构建的主题扩展。静态 YAML 示例保留作备用,但在数据库模式下不展示,也不会自动导入数据库。
  • 个人图集:自动读取文章封面图池,每 24 张分组;保持原图地址,不修改 CDN 或代理前缀。追加相册写入 source/_data/albums.yml ,首次添加时用列表替换末尾的 [] ;可以单独填写 thumbnail ,不填则使用原图,不会自动生成一张不存在的缩略图。
  • 工具箱:编辑 source/_data/resources.yml ,填写资源名、链接、分类和简介。卡片不请求远程图标或统计接口。

工具箱中,每个 - title: 段落就是一张卡片。例如在文件末尾追加以下内容,重新生成和部署后就会出现;相同 group 自动放进同一筛选分类。修改 url 就能更换链接,添加 draft: true 可临时隐藏。

- title: "GitHub Docs"
  url: https://docs.github.com/zh
  group: "开发文档"
  description: "Git 与代码协作功能的官方说明。"

显示与阅读设置的悬浮按钮位于工具栏最上方;滚动顶部时隐藏,向下浏览后出现。选项使用统一尺寸的主题色卡片,日夜模式下均可使用。

数据文件都附有可复制的字段说明。修改后重新生成站点;部署时同时同步 public 下的 HTML、CSS 和 JS。详细维护说明见项目里的 docs/reading-experience.md 。

#9.6 增强代码块:标记、局部折叠与代码组

旧的 mark:2 、 first_line:10 、 command:("$":1) 和带来源链接的标题仍然有效。以下都是可选参数,不需要安装额外框架,也不会执行代码。文章和写作室使用同一个渲染器。

#9.6.1 标题栏、窗口圆点与参考链接

普通代码块的标题栏左侧依次显示 红黄绿圆点、代码类型、文件名,右侧是阅读工具按钮。文件名和来源链接过长时会省略显示,悬停可查看完整文字;手机上空间不足时按钮自动换行,不会盖住左侧内容。没有填写标题和类型的 raw 代码块仍然保留窗口圆点。

下面是带参考链接的实际效果,沿用原 Shoka 的 [language] [title] [url] [link text] 写法:

src/greeting.js参考文档
console.log("你好,Shoka!");
```js src/greeting.js https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/console 参考文档
console.log("你好,Shoka!");
```

不需要参考链接时,也可以使用 title="文件名" 。下面三种框样式均可直接使用:

app.js
const greeting = "编辑器风格";
console.log(greeting);
install.sh
npm install three
note.txt
简洁框不显示窗口圆点。
```js title="app.js" frame="editor"
const greeting = "编辑器风格";
console.log(greeting);
```
```bash title="install.sh" frame="terminal"
npm install three
```
```text title="note.txt" frame="none"
简洁框不显示窗口圆点。
```

frame 不填时自动判断:Shell、PowerShell、ANSI 日志或含命令提示符的代码使用终端框,其余使用编辑器框。 frame="none" 只去掉窗口装饰,不会关闭复制、换行等工具。

没有标题与类型、只保留窗口圆点:

这段文字没有语言高亮,也没有文件名。
仍可换行、复制、下载和全屏阅读。
```raw
这段文字没有语言高亮,也没有文件名。
仍可换行、复制、下载和全屏阅读。
```

#9.6.2 行与文字的增删标记

config.js
const enabled = false;
const enabled = true;
开启功能
console.log(enabled);

上面的示例把第一行标为删除、第二行标为新增,并给文字 enabled 添加标记。颜色代表增删语义,不会随自定义主题色变成同一种颜色。写法如下:

```js title="config.js" frame="editor" del={1} ins={2} mark="enabled" label="2:开启功能"
const enabled = false;
const enabled = true;
console.log(enabled);
```
参数写法示例作用
titletitle="config.js"文件名,也是下载文件的默认名称
frameframe="editor"editor 编辑器、 terminal 终端、 none 简洁框;不填自动判断
行标记mark={2,4-6} 、 ins={2} 、 del={3}中性高亮、新增、删除;使用显示出来的行号
文字标记mark="enabled" 、 ins="新的值"按原文匹配文字,不是正则表达式;可重复填写
行标签label="2:开启功能"在这一行下方显示简短说明;可重复填写
局部折叠collapse={2-4,8-10}默认收起这些行,点击可展开;不影响复制和下载
自动换行wrap=true窄屏自动换行;默认保留续行缩进
续行缩进preserveIndent=false关闭续行缩进对齐
行号lineNumbers=false默认隐藏行号,仍可在工具栏打开
起始行号startLine=10等同于旧的 first_line:10

行范围可用逗号分开,例如 {2,4-6} 。行标记使用显示行号,设置 startLine=10 后,要标记第二行应填写 ins={11} ,不是 ins={2} 。

settings.js
const theme = "default";
const theme = "lavender";
使用新的主题色
const enabled = true;
console.log(theme, enabled);
记录当前设置
```js title="settings.js" startLine=10 del={10} ins={11} mark={12-13} label="11:使用新的主题色" label="13:记录当前设置"
const theme = "default";
const theme = "lavender";
const enabled = true;
console.log(theme, enabled);
```

** 只标记一部分文字:** 带引号的 mark 、 ins 、 del 按原文匹配,不会运行正则表达式;同类参数可以重复填写。

message.js
const oldGreeting = "旧的问候";
const greeting = "新的问候";
console.log(greeting);
```js title="message.js" del="旧的问候" ins="新的问候" mark="console" mark="greeting"
const oldGreeting = "旧的问候";
const greeting = "新的问候";
console.log(greeting);
```

这些标记只是展示说明,不会修改代码逻辑。标记同一段文字时尽量不要让多个参数重叠,以免难以判断要强调哪一种含义。

#9.6.3 局部折叠、长代码与长行换行

局部折叠与长行的实际效果:

greeting.js
function greet() {
  const name = "Shoka";
  const message = "Hello, " + name;
  console.log(message);
}
greet();
  console.log("这是一段很长的代码文字,用来展示手机上的自动换行效果;续行会保留缩进,长行不会挤出文章正文。也可以在工具栏中随时关闭换行。");
```js title="greeting.js" collapse={2-4} wrap=true mark={5}
function greet() {
  const name = "Shoka";
  const message = "Hello, " + name;
  console.log(message);
}
greet();
  console.log("很长的文字会自动换行,并保留续行缩进。");
```

局部折叠用 collapse={起始行-结束行} ,可同时填写多个范围,例如 collapse={2-4,8-10} 。折叠行没有被删除,复制与下载仍会包含它们。

超过 15 行的代码会另外出现 “展开完整代码” 按钮。下面故意放入一段较长的代码,可以同时试试整体展开与局部展开:

long-example.js
const chapters = [];
// 第一组
chapters.push("起点");
chapters.push("林间小路");
chapters.push("午后的雨");
chapters.push("窗边的光");
// 第二组
chapters.push("旧书店");
chapters.push("纸上的风");
chapters.push("散步");
chapters.push("街角");
// 第三组
chapters.push("傍晚");
chapters.push("一封信");
chapters.push("归途");
// 即使上面的行被折叠,复制时也不会丢失。
const summary = chapters.join(" → ");
console.log(summary);
console.log("阅读结束,继续探索。");

这段示例的完整写法是:

```js title="long-example.js" collapse={4-6,13-15} mark={18} wrap=true
const chapters = [];
// 第一组
chapters.push("起点");
chapters.push("林间小路");
chapters.push("午后的雨");
chapters.push("窗边的光");
// 第二组
chapters.push("旧书店");
chapters.push("纸上的风");
chapters.push("散步");
chapters.push("街角");
// 第三组
chapters.push("傍晚");
chapters.push("一封信");
chapters.push("归途");
// 即使上面的行被折叠,复制时也不会丢失。
const summary = chapters.join(" → ");
console.log(summary);
console.log("阅读结束,继续探索。");
```

若想从一开始隐藏行号,加入 lineNumbers=false ;不需要续行缩进时加入 preserveIndent=false :

wrapping.js
  const explanation = "这一行足够长,可以在较窄的文章正文中换行;换行后的文字从代码列左侧开始,而不是继续对齐原来的两个空格。";
  console.log(explanation);
```js title="wrapping.js" wrap=true preserveIndent=false lineNumbers=false
  const explanation = "这一行足够长,可以在较窄的文章正文中换行;换行后的文字从代码列左侧开始,而不是继续对齐原来的两个空格。";
  console.log(explanation);
```

#9.6.4 代码组选项卡

多种安装命令放进一个代码组:

install.sh
npm install three
install.sh
pnpm add three
install.sh
yarn add three
::: code-group labels=[npm,pnpm,yarn]
```bash title="install.sh" frame="terminal"
npm install three
```
```bash title="install.sh" frame="terminal"
pnpm add three
```
```bash title="install.sh" frame="terminal"
yarn add three
```
:::

代码组可以点击切换,也支持方向键、 Home 、 End ;手机上选项卡过多时可横向滚动。复制和下载只处理当前代码文件,不会把其他选项卡混进来。

代码组适合放 npm / pnpm / yarn 等平行示例;第三节的代码树适合显示目录层级和多个关联文件。两者可以分别使用,不会互相替换,也不要把代码组容器嵌进代码树。

#9.6.5 代码差异与真实补丁

展示代码差异时可以用 diff lang="js" ,每行开头的 + / - 会变成边栏标记。复制时不包含这些装饰符:

theme.js
const theme = "default";
const theme = "lavender";
 console.log(theme);
```diff lang="js" title="theme.js"
-const theme = "default";
+const theme = "lavender";
 console.log(theme);
```

如果内容是带 --- 、 +++ 、 @@ 的完整补丁,则保留原来的补丁文字,包括增删符号。下面的示例可点击复制确认:

theme.patch
--- a/theme.js
+++ b/theme.js
@@ -1,2 +1,2 @@
-const theme = "default";
+const theme = "lavender";
 console.log(theme);
```diff title="theme.patch"
--- a/theme.js
+++ b/theme.js
@@ -1,2 +1,2 @@
-const theme = "default";
+const theme = "lavender";
 console.log(theme);
```

#9.6.6 保留旧语法:起始行号、行高亮与命令提示符

已有文章不必逐篇改成新参数。下面仍使用原有的 first_line 、 mark 和 command ,标题栏右侧也能正常显示参考链接:

commands.shnpm文档
npm install three
npm install --save-dev typescript
```bash commands.sh https://docs.npmjs.com/cli/commands/npm-install npm文档 first_line:10 mark:11 command:("$":10||"#":11)
npm install three
npm install --save-dev typescript
```

command 是代码列左侧的提示符装饰,不会被复制或下载到代码文件中。多个提示符用 || 分开; first_line:10 与 startLine=10 二选一即可。

#9.6.7 ANSI 彩色终端日志

命令日志使用 ansi 语言可解析真实 ANSI 控制码,支持标准色、256 色和 RGB 真彩色。下面的 JavaScript 可以生成三种颜色的日志:

print-log.js
console.log("\u001b[32m构建成功\u001b[0m");
console.log("\u001b[38;5;214m请检查配置\u001b[0m");
console.log("\u001b[38;2;128;96;192m下一步:本地预览\u001b[0m");

将包含真实控制字符的日志写进下面这样的围栏中:

```ansi title="build.log" frame="terminal"
在这里粘贴带有真实 ANSI 控制字符的日志。
```

包含真实 ANSI 控制字符的显示效果:

build.log
构建成功
请检查配置
下一步:本地预览

注意:直接在 Markdown 中输入字面文字 \033[32m 或 \u001b[32m 并不会产生彩色效果;它们只有在程序字符串里被解释后才成为控制字符。复制和下载彩色日志时输出纯文本,不会混入颜色控制码;光标移动、清屏和终端链接控制码也不会被当成网页操作执行。

#9.6.8 阅读工具与使用边界

工具栏提供 换行、行号、复制、下载、标记跳转、全屏。复制后会显示 “已复制完整代码”,下载直接保存当前展示的代码文字;折叠区域也包括在内。点击行号可以定位并更新链接锚点;“标记跳转” 按区域逐个定位,隐藏的区域会自动展开。禁用复制的文章同时不提供下载按钮。

操作可以在本节怎样试用
自动换行在 wrapping.js 示例中切换,长行不会撑宽整个页面
显示 / 隐藏行号初始隐藏行号的示例也可以随时重新显示
复制完整代码在局部折叠和长代码示例中复制,确认内容完整,不含行标签和命令提示符
下载当前文件文件名使用 title 或旧式标题中的文件名;代码组只下载当前选项卡
跳到下一处标记仅有标记时显示此按钮,依次定位各个标记区域,并展开被折叠的行
定位某一行点击行号,地址中出现代码行锚点,可在同一篇文章中返回该行
全屏阅读点击右侧全屏图标,按 Esc 或再次点击退出,返回原页面位置
展开完整代码代码超过 15 行时显示在下方,可与局部折叠一起使用

普通代码块默认是五个工具按钮;出现行或文字标记时,再增加 “跳到下一处标记”。红黄绿圆点仅作窗口装饰,不是关闭或缩小按钮。

原有 info 围栏仍是说明文字样式,不会变成带工具栏的代码窗口;Mermaid 仍使用自己的流程图查看器;代码树保留自己的文件切换、复制与全屏入口。文章的 nocopy: true 只隐藏复制和下载入口,不是防止源码被读取的安全措施。

本节新增参数也可在文章写作室中使用,预览沿用文章样式与代码阅读交互;代码是展示内容,不会在浏览器中自动执行。

#十、参考项目与致谢

下面标明各个公开项目的名称、GitHub 地址和在本文中的作用。仓库卡片本身就是第六节语法的实际示例;这里开启动态信息,卡片接近可视区域时会补充 Stars、Forks、主要语言和许可证,同一仓库会复用缓存。网络不可用或接口限流时仍保留项目说明和链接;语言、许可证只展示接口实际提供的信息。

#Shirone:扩展语法与阅读设计参考

项目:LyraVoid / Shirone。文件树、代码树、步骤流、图片网格与 GitHub 卡片的写法和行为参考这个项目;行内批注、参数卡片及阅读增强也借鉴了它的教程表达与阅读设计,再适配当前博客的 Markdown-it、Shoka 主题色、PJAX 与 Fancybox。不是直接加载它的 Astro / Svelte 组件。

LyraVoidShirone

本文 Markdown 扩展、教程表达与阅读体验的参考;实际实现已适配 Hexo / Shoka。

GitHub · 开源仓库

对应文档源码如下,链接固定到参考时的版本:

  • 文件树与代码树文档源码
  • 步骤流文档源码
  • 图片网格文档源码
  • 仓库卡片用法

对应 MIT 许可说明随项目保存在 docs/licenses/Shirone-MIT.txt 。

#Shoka:原主题与已有功能的基础

增强代码阅读参考 Firefly 代码示例 与 Expressive Code 文档。这里使用原生 JavaScript 与现有 Prism 实现,并非加载它们的 Astro 组件或运行时。

CuteLeafFirefly

增强代码框、文字标记与代码阅读交互的设计参考。

GitHub · 开源仓库

项目:amehime / hexo-theme-shoka。原有主题风格、提示块、音乐标签及播放器是本次扩展适配的基础;本站的留声机是在现有音乐功能上增加三维交互,并非上游主题原生提供的模型。

amehimehexo-theme-shoka

原 Shoka 主题,提供本文沿用的主题风格、原有 Markdown 扩展及音乐播放器基础。

GitHub · 开源仓库

#Three.js:留声机的三维渲染基础

项目:mrdoob / three.js。留声机的模型、材质、灯光、相机与交互场景使用 Three.js;页面中的具体模型和音乐联动由本站组件实现。

mrdoobthree.js

JavaScript 三维图形库,用于留声机的模型、光照、相机和场景渲染。

GitHub · 开源仓库

#AudioPlayer:留声机音频可视化的视觉参考

项目:Hocoa / AudioPlayer。原留声机设计过程中参考过这个项目的音乐可视化效果;这里只注明视觉参考来源,不表示本页直接嵌入了它的播放器。

HocoaAudioPlayer

留声机原设计的音乐可视化视觉参考,本页不直接加载该项目的播放器。

GitHub · 开源仓库
文中批注
  1. 渐进增强
    先保证正文与链接可用,再添加弹窗、搜索等交互;脚本加载失败也不应丢失内容。它不是要求所有功能都依赖 JavaScript。
更新于 阅读次数 … 次

请我喝[茶]~( ̄▽ ̄)~*

Lavender 微信支付

微信支付

Lavender 支付宝

支付宝