Slides 终端演示工具实战指南:用 Markdown 在终端里完成幻灯片编写、代码执行与远程演示

原创2026-09-22 13:11:401,749 阅读
文章标签:CLI开发工具

Slides 终端演示工具实战指南:用 Markdown 在终端里完成幻灯片编写、代码执行与远程演示

Slides 是一个完全运行在终端里的演示文稿工具,它把「演示文稿」简化为「一个 Markdown 文件」:你用纯文本组织内容、用 --- 分隔页面,剩下的排版、高亮、翻页、代码执行全部交给工具处理。本文以仓库内置教程 internal/model/tutorial.md 为主线,结合 README 与 Go 源码(入口 main.go、核心模型 internal/model/model.go),完整讲解幻灯片编写、键盘导航、代码执行、预处理、元数据配置与 SSH 远程演示,读完即可上手制作并演示你自己的终端幻灯片。

Slides 终端演示工具运行界面:包含标题、Go 代码块与底部作者/页码状态栏

一切皆 Markdown:演示文稿就是一个 Markdown 文件

Slides 的核心设计理念,用内置教程 tutorial.md 的原话来说就是:Everything is markdown——整场演示本身就是一份 Markdown 文件。这份文件同时也是仓库的一部分:当你直接运行 slides 而不带任何参数时,程序会通过 //go:embed tutorial.md 把教程嵌入二进制并播放(见 internal/model/model.go 与 readStdin 的兜底逻辑)。也就是说,你第一次打开这个工具时看到的,就是一份最好的入门示例。

一张幻灯片就是一个独立的 Markdown 片段,幻灯片之间用独占一行的三个连字符 --- 分隔:

# Slide 1
Some stuff

---

# Slide 2
Some other stuff

在 internal/model/model.go 中可以看到解析逻辑:文件内容先去掉开头的分隔符,再按常量 delimiter = "\n---\n" 切分,得到字符串切片 m.Slides,其中每个元素渲染为一页;如果第一页是 YAML front matter(以 --- 开头),它会被解析为配置而非幻灯片。由于 Markdown 渲染由 charmbracelet/glamour 完成,你在 GitHub、笔记软件里熟悉的一切 Markdown 语法都能直接用:

  • 六级标题 # h1 到 ###### h6
  • 无序列表 * like bulleted list 与有序列表 1. Numbered lists too
  • 表格:
Tables Too
Even Tables
  • 代码块与语法高亮
  • 以及通过 dot 图 + 预渲染 ASCII 图表达的图表(digraph { rankdir = LR; a -> b; b -> c; } 配合手工绘制的 ┌───┐ ──▶ 方框箭头图)

安装与快速启动

在开始写幻灯片之前,先把它装到你的机器上。根据 README.md,支持以下安装途径:

  • macOS(Homebrew):brew install slides
  • Arch Linux(AUR):yay -S slides
  • Nixpkgs(unstable):nix-env -iA nixpkgs.slides
  • 任意运行 snapd 的 Linux 发行版:sudo snap install slides
  • Go 工具链:go install github.com/maaslalani/slides@latest
  • 从源码构建:先 git clone 本仓库(git clone https://gitcode.com/gh_mirrors/sl/slides.git),进入目录后执行 go install

安装完成后,三种启动方式:

# 1. 播放一个 Markdown 文件(推荐)
slides presentation.md

# 2. 从 stdin 接收内容,适合管道组合
curl http://example.com/slides.md | slides

# 3. 不带参数:直接播放内嵌的官方教程(即 tutorial.md)
slides

值得说明的是「实时重载」:只要传入的是文件名,Slides 会每秒检查一次该文件的修改时间(见 internal/model/model.go 中的 fileWatchMsg),文件一变就自动重新加载并刷新当前页,非常适合边写边演示——改完保存,画面立即更新,无需重启。

在幻灯片中展示并执行代码

教程的第二页展示了代码块的能力——展示代码只是基本功,执行代码才是亮点:

package main

import "fmt"

func main() {
  // You can show code in slides
  // Press ctrl+e to execute this code directly in slides
  fmt.Println("Tada!")
}

按下 ctrl+e,Slides 会解析当前页的所有代码块,逐个执行,并把输出以「虚拟文本(VirtualText)」的形式追加在幻灯片末尾显示(实现见 internal/model/model.go:code.Parse 解析 → code.Execute 执行 → 结果拼入 m.VirtualText)。切页时虚拟文本自动清空(SetPage 中 m.VirtualText = "")。

从源码 internal/code/languages.go 可以看到,目前支持 20 种语言:bash、zsh、fish、elixir、go、javascript、lua、ocaml、perl、python、ruby、rust、java、julia、cpp、swift、dart、v、scala、haskell。

执行过程(internal/code/code.go)的底层逻辑是:把代码块写入 os.TempDir 下的临时文件(按语言扩展名命名,如 slides-*.go),再用该语言对应的命令运行它,最后把 stdout 收集为结果。语言表里还支持三个占位符,便于构造复杂命令:

  • <file>:临时文件的完整路径
  • <name>:不带扩展名和路径的文件名
  • <path>:临时文件所在目录

以 Rust 为例,它需要两步「先编译后运行」:{"rustc", "<file>", "-o", "<path>/<name>.run"} 再 {"<path>/<name>.run"};C++ 同理使用 g++ -std=c++20 编译。这意味着只要你的系统装了对应语言运行时,代码块就能真实跑起来,非常适合技术分享时做「现场演示」。

另外,代码块还支持注释隐藏与一键复制:幻灯片上的注释默认不渲染(code.HideComments),按 y 可以把当前页代码块的代码写入系统剪贴板(见 internal/model/model.go)。

键盘导航:Vim 风格的数字缓冲与跳转

README.md 完整列出了翻页键位,全部继承自 Vim 的习惯,先按数字再按动作键即可「跳 N 页」:

动作 按键
回到第一页 g g
下一页 space、right、down、enter、n、j、l、Page Down;数字 + 以上任意键 = 前进 n 页
上一页 left、up、p、h、k、N、Page Up;数字 + 以上任意键 = 后退 n 页
跳到指定页 数字 + G
跳到最后一页 G
退出 ctrl+c 或 q

这套「数字缓冲」机制在 internal/navigation/navigation.go 中实现:按下数字键时先累积进 State.Buffer(如先按 3 再按 5 得到 "35"),随后按下翻页/跳转键时,repeatableAction 会把缓冲解析为重复次数逐页推进,或在 navigateSlide 中解析为具体目标页(destinationSlide-- 因为页码从 1 开始计数,且越界时收敛到首尾页)。例如 5 + n 表示前进 5 页,12 + G 表示跳到第 12 页。实现中还特意注释了「repeat 为 0 时退化为普通单步」,与 Vim 的 least astonishment 原则保持一致。

幻灯片搜索:正则定位与结果跳转

演示内容多时,可以用搜索快速定位。按 / 进入搜索模式(底部状态栏变为搜索输入框,见 internal/navigation/search.go 的 NewSearch:占位符 search、提示符 /),输入关键词后回车即跳转到第一个匹配的幻灯片。

两个细节值得注意:

  1. 搜索词是正则表达式:底层用 regexp.Compile(expr) 编译用户输入,因此可以写 foo|bar、^\d+$ 这类模式;
  2. /i 后缀开启忽略大小写:expr 以 /i 结尾时会被改写为 (?i)expr(见 Search.Execute),例如 /banner/i 会同时匹配 Banner 与 banner。

搜索顺序是从「当前页的下一页」扫到末尾,再折回开头,确保循环覆盖;按 ctrl+n 可以在一次搜索后跳到下一个结果(m.Search.Execute(&m) 会被再次调用)。按 esc 或 ctrl+c 取消搜索。

预处理(Pre-processing):把命令输出直接渲染进幻灯片

这是 Slides 最具「终端原生气质」的特性:你可以在幻灯片里写一个三波浪线 ~~~ 围起来的块,块的第一行是命令,后面是喂给该命令的 stdin,Slides 在展示前会执行命令,并用命令的 stdout 替换整个块。README 与教程中的经典例子是用 graph-easy 把图描述转成 boxart 字符画:

~~~graph-easy --as=boxart
[ A ] - to -> [ B ]
~~~

渲染前被替换为:

┌───┐  to   ┌───┐
│ A │ ────> │ B │
└───┘       └───┘

如果想让输出保持原有换行与格式,把预处理块用三个反引号包裹即可(README 有专门说明)。

安全机制是重点:出于安全考虑,Slides 只对「具有执行权限」的文件执行预处理,避免无意间运行恶意命令。你需要先:

chmod +x file.md

源码侧有两处印证:internal/file/file.go 的 IsExecutable 判断文件权限位 0111 是否齐全(即 owner/group/other 三者都有执行位);internal/model/model.go 的 readFile 在文件可执行时调用 process.Pre(content),若文件以 shebang #! 开头还会先剥掉首行再处理。预处理流程(internal/process/process.go)是:正则 ~~~(.+)\n(?:.|\n)*?\n~~~\s? 解析出所有块 → Execute 用 exec.Command 把块内文本经 stdin 管道喂给命令 → Pre 用输出替换原始块。因此它本质上是「幻灯片文件 = 模板 + 命令渲染」,可以把任何能输出文本的命令(图表、表格生成器、构建产物摘要等)变成幻灯片内容。

用 YAML front matter 配置演示文稿

你可以在 slides.md 顶部用 YAML front matter 定制演示文稿的外观与信息(README.md 的 Configuration 章节)。整段配置都是可选的——缺省字段会自动落到内置默认值:

---
theme: ./path/to/theme.json
author: Gopher
date: MMMM dd, YYYY
paging: Slide %d / %d
---

各字段的语义(对应 internal/meta/meta.go 的实现):

  • theme:指向一个 glamour 主题的 json 文件路径,也可以填一个远程 http(s) URL——Slides 会在演示前自动拉取(styles/styles.go 的 SelectTheme:http 开头走 HTTP GET,否则 os.Open 本地文件,失败则回退默认主题)。另内置四个快捷名:ascii、light、dark、notty。
  • author:显示在左下角的作者名。默认取系统当前用户的完整姓名(user.Current() 的 Name),设为空字符串 "" 可隐藏。
  • date:按模板格式化今天的日期,默认输出 YYYY-MM-DD 格式;如果填的字符串无法被解析成日期模板,就原样显示该字符串(internal/meta/meta.go 中 parseDate 的结果与输入相同时直接展示原文)。
  • paging:页码格式串,可包含 0、1 或 2 个 %d——第一个替换为当前页号,第二个替换为总页数。默认 Slide %d / %d。注意:如果以 % 开头,需要用引号包起来,否则会被当作 YAML 注释语法。

paging 的实际渲染在 internal/model/model.go:用 strings.Count(m.Paging, "%d") 判断指令个数,2 个则传 (Page+1, len(Slides)),1 个则只传页码,0 个则原样输出——这也解释了为什么它可以被用来做完全自定义的进度提示。

日期格式对照表

date 模板使用如下占位符(以 January 02, 2006 为例,转换规则定义在 internal/meta/meta.go):

值 翻译为
YYYY 2006
YY 06
MMMM January
MMM Jan
MM 01
mm 1
DD 02
dd 2

注意区分:MM(两位数字月份)与 mm(不带前导零的月份)、DD(两位数字日期)与 dd(不带前导零的日期)。另外,默认主题还受环境变量 GLAMOUR_STYLE 影响(defaultTheme 优先读取它),方便你在不修改文件的前提下切换全局渲染风格。

通过 SSH 在远程终端演示

Slides 内置了一个 SSH 服务器(internal/cmd/serve.go),让你把演示「托管」在一台机器上,观众通过任意装有 ssh 的机器直接观看,无需安装 Slides、也无需拿到演示文件。

在托管机器上启动:

slides serve [file]

然后从任意机器(包括本机)SSH 连接到它输出的端口:

ssh 127.0.0.1 -p 53531

连接后即可看到演示画面。这对「用一台不装 Slides 的电脑做演示」或「让观众在自己电脑上同步看稿」都非常实用。serve 命令(别名 server)的默认参数为:端口 53531、host localhost、私钥路径 slides,均可通过命令行 flag(--keyPath、--host、--port)覆盖,也支持环境变量 SLIDES_SERVER_KEY_PATH、SLIDES_SERVER_HOST、SLIDES_SERVER_PORT(见 internal/cmd/serve.go 与 flag 定义)。服务优雅停机:收到 SIGINT/SIGTERM 后等待最多 30 秒完成收尾。

结语与仓库索引

总结一下,用 Slides 做演示的完整工作流是:写一个普通 Markdown 文件(可执行文件还能启用预处理)→ slides presentation.md 启动(自动热重载)→ 用 n/space/gg/G 等键翻页 → / 正则搜索 → ctrl+e 现场执行代码 → 需要分享时 slides serve 交给 SSH。整个渲染管线是「front matter 配置 + glamour 渲染 + lipgloss 状态栏」,实现清晰、可读性强,适合想深入研究的读者继续阅读:

登录后查看全文
slides