冒险之书
你正在将一份文档转化为一段旅程。读者将按阶段,通过一个根据用户所选主题绘制的插画故事进行探索,故事中每个醒目的单词都可以点击,以查看其所代表的真实事物:来自源文档的实际命令、规则、决策或事实。最终输出是一个无需依赖、可离线运行的 .html 文件。
引擎(导航、弹出式抽屉、图鉴、进度地图、视差滚动、键盘支持、移动布局、减少动画模式)已经内置于 assets/template.html 中。scripts/build_book.py 会将你的内容组装到其中。你的工作是富有创意且忠实于原意的部分:故事、寓言、散文、弹出窗口以及场景美术。
三份参考资料承载着这门手艺:references/story.md(结构——每次都要阅读)、references/themes.md(每个主题的语调和寓言风格)以及 references/scenes.md(绘图)。
需要避免的两个失败场景是文字过多和其下缺乏故事:即一本书只是给文档的章节披上了主题外衣,一个阶段接一个阶段,却用氛围掩盖了真实的事实。文字要更少,旅程要清晰。
工作流程
- 通读整个源文档。 注意它的标题、事件发生的顺序、读者阅读后应能完成的任务,以及任何容易出错的事实(确切的命令、名称、数字、规则)。这些事实正是弹出窗口要保护的内容。
- 确定主题和寓言。 采用用户给定的主题;如果没有,则选择一个适合材料内容的主题,并说明你选择了哪个。阅读
references/themes.md中对应部分,了解其语调、主题、英雄原型和阶段命名模式。然后,为你自己编写一个简短的映射表:源文档中的每个实际概念及其对应的主题概念(VPN = 面纱,数据库 = 伟大的驯服野兽,CI 流水线 = 试炼场)。确保全书的所有映射保持稳定。读者只需一次性学会这个寓言;如果在第四章中“面纱”变成了“迷雾”,那么这本书就失去了教学意义。 - 塑造故事。 在规划任何内容之前,阅读
references/story.md并为这本书填充其故事主干:英雄必须达成的目标、推动他们前进的“因此”链条、他们在接近尾声时犯下的错误,以及这本书结尾的画面。文档是章节的列表;一本书是因果的链条,将前者转化为后者是整个工作的意义所在。不要因为源文档看起来像是一个简单的列表就跳过这一步——那些正是会变成幻灯片放映的内容。 - 规划阶段。 目标定为 5 到 9 个,每个阶段一个节拍,映射到故事主线上(一个到达阶段,三到六个不断升级的中间阶段,一个出错的阶段,一个收获的阶段)。每个阶段通常对应一个源文档章节:合并过于单薄无法承载节拍的章节,并拆分那些需要超过约 120 字叙述的章节。按读者需要知识的顺序(通常是文档本身的顺序)来安排阶段。为每个阶段赋予一个符合主题语调的标题,并添加
source:标题,以便读者可以从阶段内部展开查看原始章节。 - 编写内容文件 到工作文件夹中(
book.json、stages/NN-name.md、scenes/*.svg)。格式如下。 - 删减,然后运行检查清单。 根据“编写故事”部分重新阅读每个阶段,并删减大约三分之一的文字;初稿的长度通常都是所需的两倍,你每删除一句话,都会让标记的文字更容易被看到。然后,执行
references/story.md末尾的检查清单,并修复未通过的项目。这两步都要在构建之前进行;都不是可选的润色工作。 - 构建并阅读警告信息。 修复每一个“未找到弹出窗口 id”、“未找到标题”以及覆盖率警告。重新构建,直到报告干净,或者剩余的每个警告都是有意为之。
- 验证并交付。 打开文件(在 macOS 上使用
open <file>.html),或者如果可用的话,至少使用 Headless Chrome 截取屏幕截图。告诉用户文件在哪里,如何浏览它(方向键、点击地图、点击发光的文字),以及你使用了哪个主题。
内容格式
book.json(路径相对于 JSON 文件):
{
"title": "探索传送门",
"subtitle": "一位骑士穿越 AGENTS.md 的旅程",
"theme": "knight",
"source": "../AGENTS.md",
"cover": { "eyebrow": "一次互动冒险", "scene": "scenes/cover.svg", "blurb": "cover.md" },
"stages": [ "stages/01-gates.md", "stages/02-forge.md" ],
"ending": { "scene": "scenes/ending.svg", "blurb": "ending.md",
"recap": [ "在[[面纱|vpn]]之后才能进行其他操作", "每个包都带有 `@internal/` 标志" ] }
}
每个阶段是一个 Markdown 文件:
---
id: gates
title: 城堡的大门
chapter: 第一章
source: 本地开发环境设置
scene: scenes/01-gates.svg
---
雾气弥漫着通往城堡的道路。一名守卫挡住了去路:除非你通过了[[面纱|vpn]],否则不得入内……
## popout: vpn
title: VPN
在开始任何其他操作之前,你必须先连接到公司 VPN,因为内部包注册表和服务只能从内部网络访问。
构建脚本会强制执行或警告的规则:
- 每个散文字段都是 Markdown。每个场景都是内联 SVG,以
.svg文件的路径形式给出。 [[显示文本|id]]标记一个弹出链接;[[docker]]使用文本作为 id。每个 id 都需要在同一阶段中有一个## popout: id块(或者在book.json中有一个书本级别的popouts数组)。- 前部信息中的
source:指向源文档中的一个标题。脚本会逐字提取该章节,并将其显示在“原始卷轴”按钮后面。弹出窗口块也可以携带source: <heading>,以在其解释下附加相关章节——但它必须放在弹出窗口的标题行中,直接位于## popout: id和title:旁边,而不是在解释之后。如果放错位置,它会被静默地当作散文读取,并且该章节将不会显示。 - 主题可以是一个预设名称(
knight、space、pirate、noir、expedition、deepsea),也可以是一个对象{ "extends": "knight", "colors": {...}, "fonts": {...}, "vocabulary": {...} }。预设存在于assets/themes.json中;可以复制一个作为自定义主题的起点。
构建:
python3 <skill-dir>/scripts/build_book.py --content work/book.json --out <name>.html
编写故事
第二人称,现在时,每个阶段一个场景和一个节拍。 “你”就是英雄。一个阶段不是对一个地方的描述;而是发生在那里的一件事。你到达,你遇到一个人或一个问题,你做了一件事,然后你带着改变或更好的装备离开。如果一个阶段归结为“你在一个地方,它看起来很有气氛”,那么它还没有节拍:在源章节中找到一个——读者必须做的事、他们绝不能犯的错误、所做的决定——并围绕它构建场景。
坚持你规划好的形状。 references/story.md 是结构的权威参考:目标在一开始就阐明,阶段之间由“因此”和“但是”连接,而不是“然后紧接着”,中间部分不断升级,接近尾声时有一个真实的错误,一个重复出现的主题或贯穿始终的物件。