Boxloom:编写演示文稿
Boxloom 将含义与外观分离。你只需编写内容本身是什么(要点列表、选项对比、带日期的计划)。模板决定了所有的颜色、字体大小、边距和位置。除了一个逃生通道(shapes,最后手段)外,你永远不需要编写颜色、大小或坐标。
流程总是:
boxloom schema— 读取当前模板接受的内容(先做这一步)。- 编写
deck.bx.md。 boxloom check -format json deck.bx.md— 修复所有错误,然后修复你能修复的所有警告。- 重复第3步,直到
"ok": true且没有layout-overflow警告。 boxloom build -o deck.pptx deck.bx.md(标志位于文件之前)。
命令从仓库的 boxloom/ 目录运行:
go run ./cmd/boxloom <schema|check|build|parse> …(Go 1.24+,无其他依赖)。
1. 先读取模式
boxloom schema [-template light|dark] 输出一个 JSON 文档。它是真理之源;如果本文件和模式冲突,以模式为准。
markdown.rules— 语法规则(简短列表;需全部遵守)。markdown.frontMatter—---头部允许的键(title是必需的)。templates[].blocks— 此模板识别的围栏块(:::name),包含inside(允许出现的位置)、contains(内部包含的内容)和minChildren。templates[].subsystems— 图形类型(```name代码块),包含markdown(其额外规则)和schema(一个指向irSchema的 JSON 指针,列出了所有允许的键)。- 未列出的任何内容都是错误。不要发明块名、属性或键。
- 模板通过名称选择(front matter 中的
template: light,或-template dark)。自定义模板是一个按路径传递的值表文件:-template ./my.json(适用于schema、check、build、fonts;示例examples/templates/forest.json)。当演示文稿将使用自定义模板构建时,请使用相同的-template运行schema和check— 角色名称和文本样式来自该文件。 variant(# Title {variant=…})存在于语法中,但当前模板未声明任何变体(templates[].variants为空),因此不要使用它。
输出很大。只看你需要的内容:
go run ./cmd/boxloom schema -template light > /tmp/schema.json
python3 -c "import json;d=json.load(open('/tmp/schema.json'));print(json.dumps({'rules':d['markdown']['rules'],'blocks':d['templates'][0]['blocks'],'subsystems':[(x['name'],x['markdown']) for x in d['templates'][0]['subsystems']]},ensure_ascii=False,indent=1))"
python3 -c "import json;d=json.load(open('/tmp/schema.json'));print(json.dumps(d['irSchema']['\$defs']['gantt'],ensure_ascii=False,indent=1))" # 所有甘特图键
2. 一屏看完的语法
---
title: Moving the reporting service to the new cluster
subtitle: Plan and options (fictional example)
author: Platform team
date: 2026-11-11
confidential: Internal
disclaimer: All names and figures are fictional
template: light
---
# Vendor support of the old cluster
:::lead
The old cluster loses vendor support in March 2028, so the 14 services on it must move before then.
:::
- The vendor ends security fixes on **2028-03-31**
- 14 services still run there; 9 can move without code changes
- Moving takes about 6 weeks per service group
:::source
Vendor support notice of 2026-09; service inventory as of October 2026
:::
:::notes
Speaker notes go here. Plain paragraphs and bullets only.
:::
-
# Title开始一张幻灯片。保持标题为一行内能显示的简短标题(“旧集群的供应商支持”);换行的标题会被警告(title-wrap)。将幻灯片要表达的内容——结论——用一句:::lead写在标题正下方(1–2 行)。模板将 lead 放在标题下方,正文之外。 -
---不是幻灯片分隔符(它是错误的)。##及更深层次的标题是错误的(分组标题应写作:::column Title,而不是子标题)。 -
正文:段落、
-/1.项目符号(最多两级)、**强调**(仅一种强调)、`code`、[text](url)、行尾的\(表示有意义换行)。没有斜体、删除线、脚注、HTML。 -
围栏块:以
:::name开始,以相同数量的冒号结束。嵌套时,外层围栏比内层多一个冒号::::::compare ::::option Keep the old cluster :::pros - No migration work this year ::: :::cons - No security fixes after March 2028 ::: :::: ::::option Move to the new cluster :::pros - Supported until 2033 ::: :::cons - Six weeks of work per service group ::: :::: ::::: -
:::lead(幻灯片的消息)、:::notes(演讲者备注)和:::source(幻灯片数据的来源)必须位于幻灯片的最顶层,每张幻灯片各一次。仅内容幻灯片使用:::lead。 -
front matter 中的
date、confidential和disclaimer为整个演示文稿编写一次;模板将其放在封面(confidential也放在页脚)。不要在每张幻灯片的:::note中重复“虚构”/“保密”。 -
图形是围栏代码块,其语言是子系统名称,主体为受限制的 YAML:无锚点/别名(
&、*)、无标签、无多行值、无|/>块;布尔值只能使用true/false;日期为字符串YYYY-MM或YYYY-MM-DD;范围2027-01 .. 2027-06。对任何以*、&、!开头或包含": "的值加引号,例如text: ["**Slowest step**", 5 days on average]— 一个未加引号的**…**在流程项开头会被解读为 YAML 别名并被拒绝。
3. 为每段内容选择合适的模式
在编写之前决定内容的类型。按顺序应用这些规则,并使用第一个匹配的规则。(完整规则集及好/坏示例:docs/patterns.md §4。)
当前可用(请检查 boxloom schema — 可能已添加更多):
| 内容 | 使用 | 说明 |
|---|---|---|
| 章节分隔 | # Part 2: How we migrate {type=section} | 标题文本是章节标题;其后只能跟 :::notes。不是目录 — 当演示文稿有两个或更多章节时,模板会在每张章节幻灯片上自动绘制当前高亮的章节列表 |
| 幻灯片只说一句话;证据在别的幻灯片上 | # Migration must move two years earlier {type=statement} | 标题文本就是这句话(这是唯一一种标题是完整句子的幻灯片类型);其后只能跟 :::notes 和 :::source。如果你要添加理由,请使用带简短标题和 :::lead 作为结论的普通幻灯片 |
| 1–4 个要强有力展示的数字,且无系列可比较 | ::::stat → :::value 3 → 12 {unit=models} + 单行标签 | 5 个及以上数字或时间序列应使用图表/表格,而非 stat。值按原样编写,必须包含数字。值和单位永不在中间断开,因此保持简短 — 4 个卡片较窄(stat-value) |
| 数字系列(3+ 点):排名、随时间变化、整体中的份额、分布、两个量的关系 | ```chart | compare: item / timeSeries / component / frequency / correlation — 系统会自动为你选择图表类型(永远不要写柱状图/折线图/饼图)。categories: [...]、series: [{name, values: [...]}](每个分类一个值,数字不加引号,最多 3 个系列)、可选的 unit、emphasis: [category](一个系列)或一个系列的 emphasis: true、当系列是整体的部分时使用 stacked: true。correlation 使用 xAxis: {title, unit?}、yAxis 和 points: [[x, y], …] 代替 categories。component(份额)有 1 个系列且最多 6 部分 — 将小部分合并为“其他” |
| 别人的话 | :::quote Source text + 内部文字 | 来源(标题文本)是必需的。永远不要引用你自己的结论 |
| 需要在普通幻灯片内强调的一句话 | :::takeaway + 一句话 | 每张幻灯片最多一个,通常放在最后 |
| 纯文本或数字的行×列 | GFM 表格:| A | B | + | --- | --- | + 行 | 第一行是表头;每行每个列有一个单元格(table-shape)。没有对齐标记(:--、--: 是错误的);数字列会自动右对齐。单元格仅限内联。表格后紧跟一行 : caption 作为其标题。如果最后一行是总计,则在该行末尾加上 {total=last}(: Estimates {total=last},或仅 : {total=last});该行将渲染为总计行。系统不会计算总计 — 请写入数值。将单位放在标题或表头中一次,不要在每个单元格中重复。单元格中的评级 → matrix(见下文) |
| 每个单元格都是评级的行×列(已支持 / 有条件 / 不支持 / 风险) | ```matrix | columns: [A, B]、rows: [{label, cells: [{status, text?}]}]、可选的 legend: {support: Good}。每列一个单元格(matrix-cells)。推荐选项:emphasis: <column heading text>(与 columns 中的写法完全一致,matrix-emphasis) — 永远不要在正文中说“蓝色列”。单元格是普通文本或数字的是表格,不是矩阵 |
| 3–7 个步骤(无日期,有方向,前一步的输出是下一步的输入) | ```process | steps: [{title, detail?, status?, emphasis?}]、可选的 caption。系统会自动添加数字(1. 2. …)和箭头;长流程会换行到第二行。如果使用 status,请在 legend: {conditional: Needs approval} 中说明其含义(figure-legend) |
| 两个或更多选项,每个有优缺点 | :::compare → :::option → :::pros / :::cons | 如果 3 个及以上共享标准适用于每个选项,则矩阵更合适(见上文) |
| 有子项(及孙项)的父项:团队设置、所有权结构 | ```orgchart | root: {title, detail?}、units: [{title, detail?, members: [{title, detail?}]}]。最多 3 层。盒子上的 status 需要 legend: {conditional: Two more people needed} — 图表下方的图例解释它;不要在 :::note 中解释轮廓颜色。不适用于并列的不相关团队 |
| 阶段数量减少(访问 → 注册 → 付费) | ```funnel | stages: [{label, value, note?}]、可选的 unit。value 是数字或文本;条形宽度是阶梯式的,不成比例。阶段间转化率(↓ 45%)会自动从数值计算出来 — 不要在 note 中写;使用 note 说明为什么阶段下降。status 需要 legend。增长的数值会被警告(funnel-increase) — 对于简单步骤使用 process |
| 带日期的计划、周期、截止日期、随时间变化的状态 | ```gantt | 状态为 support、conditional、none、danger(角色,而非颜色)。硬件/支持风险周期是组 risk: A .. B。注释通过 target 指向一个组/行 id;子系统会放置它们 |
| 2–3 个并列分组并排阅读(“今年 / 明年 / 不做”) | ::::columns → :::column Group title(2 或 3 个) | 每个分组包含段落、项目符号、一个表格、一张图片、一个 ```chart 或 :::note。无嵌套的 columns、compare、gallery 或其他图形。图表及其解读并排放置:一个分组放图表,另一个分组的项目符号放要点。宽度不是数字:标记一个主要分组 {role=main}(其他分组支持它),或让所有分组相等。4 个及以上单行分组 → 使用项目符号。不适用于分成两半的一个论点 |
| 能说明问题的图片(截图、照片、绘图) | 一个仅包含  的段落 | 替代文本是必需的(image-alt)。路径相对于演示文稿文件;无 URL、绝对路径或 ..(image-path)。仅 PNG 或 JPEG(image-format)。保持比例,绝不裁剪。图片 + 文字并排 → ::::columns,图片放在一个分组中 |
| 2–6 张图片比较或按顺序 | 一个段落包含 2–6 个  | 成为画廊(2–3 张一排,4–6 张网格)。7 张及以上是错误的:拆分幻灯片 |
| 并列的要点 | 项目符号 | 最多 2 级 |
| 连贯的散文 | 段落 | 2–3 句 |
| 幻灯片要说明的内容:其结论(一句或两句) | :::lead 在内容幻灯片的最顶层,一个段落 | 每张内容幻灯片都有一个。 模板将其放在标题正下方,正文之外;它会占用正文空间(1 行 ≈ 32 pt,2 行 ≈ 57 pt)。当需要两行时,引擎会在句末(。)或(如不行)在逗号(、)后断行,不另起一行 — 因此两个短句最易读。不是要点的摘要,而是它们要证明的主张 |
| 幻灯片中数字、表格、图形或引用的来源(来源、样本量、日期) | :::source 在幻灯片最顶层,一个段落 | 每张显示数字、表格、图表或引用的幻灯片都有一个。 仅限内容幻灯片和陈述幻灯片(非章节)。模板将其放在正文下方,页脚上方;它会占用正文空间(layout-overflow 会计入它)。每张幻灯片一个:多个来源放在一个段落中 |
| 演示文稿范围的日期、保密性、免责声明(“所有数字均为虚构”) | front matter 中的 date、confidential、disclaimer | 编写一次;显示在封面(confidential 也放在页脚) |
| 警告/类似脚注的说明 | :::note | 一个短行。不用于来源(:::source)、不用于演示文稿范围的免责声明(front matter),也绝不用于结论或关键数字 |
| 位置/链接承载含义且上述项均不合适的图示 | ```shapes | 最后手段,参见 §3.1 |
在上述所有四种图形中,你仅编写文本、status(support、conditional、none、danger)和 emphasis: true;位置、大小和颜色来自模板。在 chart 中,你仅编写比较的对象和数字;图表是真正的 PowerPoint 图表,其值仍然可编辑。强调标题要说明的那一点:
compare: timeSeries
unit: " sign-ups"
categories: [Apr, May, Jun, Jul, Aug, Sep]
emphasis: [Sep]
series:
- name: Sign-ups
values: [120, 150, 180, 220, 290, 380]
4. 密度限制
布局引擎为可用的空间建模,密度限制是硬性的:它们防止幻灯片在构建时溢出到右侧或底部(感觉像一个盒子,其内容必须适合内部,否则你会看到溢出)。
在处理了一张幻灯片后,引擎会报告 layout-overflow 警告。在你的 check 输出中查找它:
"warnings": [
{
"rule": "layout-overflow",
"slide_id": 1,
"message": "Slide 1 overflows its content area by 42 pt",
"instruction": "Reduce body content by approximately 2 lines"
}
]
要修复:
- 依靠
instruction— 它给出了删除多少文本的估算(行数)。首先删除这个量。 - 简化
:::lead(两行会占用大量空间)。 - 将项目符号压缩为更少的项,或将长项拆分为两张幻灯片。
- 将大表格/图形移动到自己单独的幻灯片。
5. 检查 → 修复循环
目前不要执行构建。重复以下步骤:
go run ./cmd/boxloom check -format json deck.bx.md | python3 -m json.tool
编辑 deck.bx.md,然后重新运行。当 check 输出 "ok": true 且无 layout-overflow 警告时,转到 build:
go run ./cmd/boxloom build -o deck.pptx deck.bx.md
附录 A:常见陷阱
- 缺少
:::lead— 如果内容幻灯片没有:::lead,表格将认为它是一个章节幻灯片并产生格式混乱的 cover+body。 - 在
:::lead中使用项目符号 — 使用包含-的行(例如,行以-开头)会使它看起来像两个独立行,一字排开。读取器将前两个字符解释为项目符号。最好的写法是:::lead是一段不被拆分的文本。 - 错误而使用
type=statement的幻灯片 — 它只能有标题和:::notes。如果你添加了:::lead,则会错误。 :::source内有空行 — 来源是单行段落。空行会被视为新段落并可能干扰布局。:::note包含冗长信息 — 演讲者备注很短(一句话);如果幻灯片本身有正文,则不要使用它们作为正文。- 自定义模板缺少元数据 — 如果
-template ./my.json缺少markdown或irSchema键,check会出错。始终从现有模板开始,并仅修改视觉属性。 - 忘记在
check中使用-template— 如果使用自定义模板构建,请使用相同的-template运行check;否则角色检查将失败,因为默认模板可能不包含自定义角色。
此版本不包括 docs/patterns.md 的 §3(形状)、§5(示例工作流)和附录(自定义模板格式)。专注于掌握这些核心部分。
