pdf-triptych · git:20260921.7c55960 · 2026-09-21 · sha256 696686f9133c76d9
pdf-triptych git:20260921.7c55960A
Immutable. This exact content is served forever at /api/v1/blob/696686f9133c76d9.
--- name: pdf-triptych description: | 把一份标准、规范、白皮书或长技术 PDF 拆解成一页三联图 HTML:第一张讲骨架(这份文档的结构是什么),第二张讲细节(概念之间的准确关系),第三张举例子(换成具体场景后每样东西落在哪)。三张图共用同一根横轴和同一套颜色,既有递进也有共同标尺。用于把读不动的长文档变成一页能讲、能评审、能贴进汇报的图。当用户说 /pdf-triptych、把这份 PDF 画成图、标准拆解、三联图、一张图讲清这份标准、文档骨架图、把 PDF 做成可视化 时使用。不用于普通 PDF 转文字、摘要,也不用于纯数据图表。 --- # pdf-triptych 把一份长文档拆成三张有递进、有共同标尺的图。 ## 这套流程解决什么 长标准文档读不动,不是因为字多,是因为**结构藏在字里**:哪些是主线、哪些是横切、哪些只是用法,原文往往不说。直接"把 PDF 转成图"会得到一堆罗列的方框;正确顺序是先把结构挖出来,再决定用什么形状承载,最后才写代码。 三张图的分工与联系: | 图 | 讲什么 | 与上一张的关系 | |---|---|---| | 一 · 骨架 | 这份文档的层级结构:从抽象到具体怎么展开,哪些定义横跨若干层,有没有第三维度 | — | | 二 · 细节 | 概念之间的准确关系,通常是把原文里的几张 figure 揉成一张 | 与图一**同轴叠放**:图二的每个框落在图一的哪一列,就属于哪一层 | | 三 · 例子 | 换成一个具体场景,每样东西落在哪一列 | 与图一**同列**:沿竖虚线往上看就知道它对应结构的哪一层 | 共同标尺有两样,缺一不可:**贯穿全图的竖向虚线(列)** 和 **全文统一的三类颜色**。 **张数由文档结构决定,不是写死三张。** 判断标准: - 原文的几张 figure **之间有主从关系**(一张是骨架,另几张是它某个节点的放大)→ 骨架与细节**分成两张**,细节那张标明谁是谁的放大。 - 原文的 figure **互不相交、谁也不属于谁** → 骨架与细节**合成一张**,把几张切面并排,并明说"它们不是一条线上的几段"。这时总共两张图。 - 例子层始终独立成图。 第二次执行(ISO/IEC 22989)走的是第二条:那份标准的三张图互不兼容,原文从未说过谁属于谁,合成一张并列展示是对的。 ## 视觉语法的底线 这套图的视觉语法是固定的,不要每次重新发明。下面几条直接用: | 项 | 内容 | |---|---| | 色板哲学 | 纸灰 `#F0EFEB` + 炭黑两极;**一律实心**:不透明材质、不发光、不渐变滤镜、无阴影。质感全靠明度对比和形状 | | 卡片四件套 | 结论式标题 `h2` + 副标题(图例、单位、范围,用 ` · ` 分隔)+ 图 + 来源行(全大写、加字距) | | 标题规则 | **写结论不写图型名**。"从一个概念展开到每一条可落地的指标" 好过 "扇出树" | | 形状 | 卡片圆角 24px,无边框无阴影,靠留白分卡 | | 字体 | Inter + 苹方;标题 700 / 数值 800 / 轴标签 600 | | 动画 | 只用一次淡入,快进快停不弹跳;必须带 `prefers-reduced-motion` 降级 | | 交互 | 这根线背后有没有真实记录?没有(纯示意的发丝线)→ **禁止加交互**,给没有内容的元素加 hover 是欺骗 | | 演示数据 | 确定性伪随机,禁用 `Math.random()`——刷新必须长一样,否则截图和回归对比全失效 | | 说不的底线 | 发光 / 玻璃拟态 / 3D → 拒绝;断轴 → 拒绝 | 有两条是**概念结构图特有的**,和常见的数据图表做法正好相反: **一、不要单一强调色。** 数据图表通常有一个要讲的结论(哪个最大、哪个异常),拿一个强调色指向它是对的。但概念结构图要讲的是**整体结构**,没有哪个分支比别的更重要,单一强调色会制造虚假的重点。 > **失败模式**:首次执行时,我把唯一的橙色给了一个分支(它确实是原文里最特别的一支)。用户的反馈是:"整个页面唯一的颜色给了这一支,一般人会觉得这是所有体系里最重要的一点,但我们想表达的是整体逻辑。" > > 改法:**三类分类色**——颜色表达"这个东西属于哪一类",不表达"它有多重要"。具体见 `references/visual-grammar.md`。 **二、最小字号按中文定。** 常见的图表字号下限 6.5 / 5.5px 是给英文和数字定的。**中文笔画密,9px 才读得清**,8.4px 是绝对下限(检查脚本按这个报警)。装不下就换行或缩短文案,不要缩字号。 色值、字号、部件画法全部写在 `references/visual-grammar.md`,那个文件是自包含的,不依赖任何外部 skill。 ## 阶段 0 · 提取 ```bash pdftotext -layout input.pdf out.txt # 正文 pdfinfo input.pdf | grep Pages # 页数 ``` **图必须用 Read 工具直接看,不能只读 pdftotext 的输出。** 原文的 figure 在文本里只剩一行图题,框名、箭头方向、箭头上的文字、图例分组全都丢了。这个 skill 的图二基本就是在重画原文的 figure,看不到图就只能瞎编。 ``` Read(file_path="input.pdf", pages="11-12") # 直接看图所在的页 ``` ## 阶段 1 · 结构探针(必须派 subagent,不可跳过) 派一个 opus subagent 通读全文。**不要自己只读目录和几节就动手**——这是本流程最容易翻车的地方:只读局部会把并列关系误认成父子关系,把"可以"读成"必须"。 提问模板见 `references/probe-template.md`,六个问题一个都不能少。重点是: - 层与层之间用**原文的英文短句**连接,不是你的概括 - 每张 figure 的**每个框名、每条箭头的方向与文字、图例的分组** - **易误读点**:全文有没有 shall、某条规则是不是带限定词、某个说法是不是只针对某一类对象 subagent 返回后,把它纠正你的地方单独记下来。这些是后面写图注的素材,也是最能体现"真读了"的证据。 **失败模式**:首次执行(ISO/IEC 25002)时,我只读了其中五六节就开画,把上位的两个分类当成了下位四个模型的父节点。subagent 通读后指出:原文是并列的分类句,全文没有任何 is-a 措辞。若不纠正,整张骨架图的第 2 层就是错的——**层级关系是这类图最核心的断言,也最容易在只读局部时读反。** ## 阶段 2 · 视觉逻辑推导(不碰代码) 把结构翻译成**表达需求**,每个需求配一条**独立的视觉通道**。通道撞车,图就乱。 典型的四个需求与可用通道: | 表达需求 | 可用通道 | 不要用 | |---|---|---| | 抽象 → 具体,数量逐层变多 | 横向分叉扇出、同心环周长、分层板 | 金字塔(它表达漏斗与转化,不是展开) | | 同层内部再分类 | 分支归属、扇区归属、上下分区 | 颜色(颜色要留给跨图的类别) | | 某些定义横跨若干层,长短不一 | 与层共用同一根轴的**长短条**,条的起止位置即跨度 | 一视同仁的并列方框 | | 第三维度(谁用、在哪些过程里用、适用范围) | 罩在整体之外的横括号或外圈 | 混进层级里 | 产出一段文字 + 一张通道分配表,先给用户看。**这一步不写任何代码。** ## 阶段 3 · 方案并置 做 3–4 个方案放在同一页,每个配一句话标题、一段副标题、一张图、一行"长处 / 短处"。不要替用户选。 常用的四种形: - **扇出树 + 跨度条**:层级是横轴,越往右分得越细;贯穿性定义是下方同轴的长短条。信息密度最高,最适合当主图。 - **同心环 + 辐条**:圆心最抽象,外圈周长天然表达"越具体越多"。造型强,但径向文字难排。 - **原文 figure 合一**:把文档里的几张图揉成一张,标明谁是谁的放大。忠实度最高,但层次感弱。 - **分层剖面(可拖动)**:每层一块板,贯穿定义是穿板的立柱。最直观,但信息密度最低。 ## 阶段 4 · 融合与对齐 用户通常会选两个:一个讲结构清楚,一个讲关系准确。 **融合不是提炼,是同轴叠放。** 两者完整保留,上下放,重排列宽让它们共用同一根横轴。 **失败模式**:本流程首次执行时,我说"扇出树的第 3 列就是原文的图 3",于是只保留了"哪个模型对应哪个实体",把图 3 的嵌套结构、四要素框、模型钉在哪个框上全丢了。用户一眼看出"丢了很多细节"。**任何以"其实已经包含了"开头的融合理由,都要先回去数一遍对方有几个框。** 对齐的做法: 1. 定一组分界线 `X[]`。**它是泳道的分界线,不是"列的位置"**——虚线本身没有实体,两条线之间的区域才是实体 2. 把最宽的那块内容(通常是原文 figure 的主体)测出需要多宽,反过来调整 `X[]` 的间距 3. 竖向虚线从图一顶部画到**最后一块按列读的内容**为止——不要无脑画到画布底部 4. 三张图用同一组 `X[]` **一切内容在泳道内居中,禁止写 `X[i] + 偏移`。** 用 `cx(i, w)` 拿左上角,`lane(i).cx` 拿中心。泳道头、连接线端点、成组的小条,全都一样——连接线端点贴的是**矩形的边缘**,不是虚线。详见 `references/visual-grammar.md` 的「泳道」一节,检查脚本会逐个量左右间距。 ### 横向区块一律走 place(),列归属必须显式声明 **共同标尺是这套图的核心契约:任何元素的横向跨度都在向读者宣称"我属于这些列"。** 跨度与真实归属不符就是撒谎,和柱状图断轴是同一类错误。 `references/skeleton.html` 提供了 `place(x, y, w, h, cols)`,`cols` 不给会直接抛错: | `cols` | 含义 | 自动做什么 | |---|---|---| | `[2,5]` | 跨第 2–5 列,按列读 | 什么都不做 | | `5` | 整块只属于第 5 列,但要更宽才放得下 | 画归属线回第 5 列列头 + 一句说明 | | `null` | 不按列读:并列切面、全局词表、容器框 | 不透明底色盖住虚线 | 检查脚本会从竖虚线本身推断列位置(只认长度超过画布 30% 的长虚线,短的是刻度或连接线),扫出所有横跨 ≥1.2 列却没有 `data-cols` 的矩形,列为"待确认"。它不影响退出码,但每一条都要逐个看过。 > **为什么这条要做成机器检查,而不是写在文档里就算了**:第二次执行时,同一份产物里一条四步链横跨七列却只属于其中一列(错),另一处三张并列切面却用底色盖住了虚线并明说"不是一条线上的三段"(对)。同一个问题一处解决一处没有——**靠文档里的规则拦不住**。回头看第一次执行的产物也有两处未声明(跨度条的起止是手工算的、一个容器框横跨全宽却不按列读),只是当时没人发现。 ## 阶段 5 · 视觉打磨 ### 5.1 先做一次纯视觉 review 改文案之前,先当作没读过内容,只看形: ```bash python3 ~/.claude/skills/pdf-triptych/scripts/render_check.py out.html --scale 2 --slice --height 4800 ``` 脚本会做五件事:**自动测量页面真实高度**(不用手猜 `--height`)、`node --check` 语法、抓 Chrome console 的运行时错误、在页面里自检(文字溢出画布 / 同行重叠 / 字号低于 8.4px)、按真实高度截图并分块。**分块截图必须用 Read 工具逐张看**——脚本只能抓到机械问题,"这一块读起来别扭"只有眼睛能发现。本流程首次执行时这一步抓到四个问题:列虚线太淡看不见、标签被分叉线压住、扇出起点因线叠线形成黑楔子、左下角一大块空地没用。 看的时候重点查三件脚本查不出来的事: 1. **有没有"不按列读"的区块被虚线穿过**——见 `visual-grammar.md` 同名小节,这是第二次执行时唯一的真实错误。 2. **一张图的视觉分区有没有超过 5 块**。超了不一定要拆,但要检查是不是靠留白和小标题分开了;挤在一起就该拆或该合并同类项。 3. **某张图是不是八成以上一个颜色**。例子层常见(全是"过程"类),可以接受,但要在副标题里说一句。 > **不要自己猜窗口高度。** 第二次 review 时我手动传了 `--height 5200`,截出来 70% 是空白,差点把它误判成产物的 viewBox 设错了——实测 viewBox 只空余 15px,完全正确。现在脚本默认自动测量,不要再手传,除非你明确要看某个高度下的表现。 ### 5.2 颜色 - **不超过三类**,一类内容一个颜色,全文统一。类别按"这个东西属于什么"分,不按"它重不重要"分。 - **黑灰只给结构**:层的分界线、箭头、跨度条、外部维度。 - **不要单一强调色**。给某一支单独上色,读者会理解成"这是全文最重要的一点",而你想表达的是整体逻辑。 - **细线上的颜色要够亮**。低明度低饱和的色画成 1px 线,和黑色分不出来。色块能认出是绿,不代表线也能。 ### 5.3 去掉决策过程 页面上不留只有作者知道的编号: | 不要 | 改成 | |---|---| | 方案 A、风格 B | 直接写这张图讲什么 | | 图 2 = 把这一段展开 | 这一段的完整展开 · §7.1 | | 上层是全貌(方案 A) | 上层是全貌:概念怎样一层层展开 | **条款号保留**(§7.2 这种),它能回查原文,是可信度的一部分。 ### 5.4 每次改动后验证 ```bash python3 ~/.claude/skills/pdf-triptych/scripts/render_check.py out.html --height 4800 ``` **语法检查过不代表能跑。** 本流程首次执行时出过一次变量先用后定义,`node --check` 通过,但整张图画不出来——因为 `const` 的暂时性死区是运行时才触发的。脚本因此同时抓 console 错误。 脚本自身经过对抗测试:人造一个带重叠、溢出、6px 小字的页面,三类问题都能抓到;真实产物全绿。 ## 什么该固化成规则,什么不该 每次执行完都会有新发现,但不是所有发现都该写进 skill。判断标准只有一条:**它是这套图型的固有契约,还是这份文档的特殊情况?** | 该固化 | 不该固化 | |---|---| | 列归属必须声明——共同标尺是图型契约,任何文档都适用 | "生命周期适合当横轴"——那是某一份标准的结论,别的文档未必有生命周期 | | 图的张数由 figure 之间有无主从关系决定——这是通用判据 | "要画三张切面并排"——那是某份文档的 figure 恰好互不相交 | | 列线要用长度过滤识别——任何图都可能有装饰性短虚线 | "117 条术语画成 barcode"——那是一次形式创意,换份文档可能完全不适用 | | 派 subagent 通读、figure 必须看图——这是方法 | "全文 0 个 shall"——那是某一份标准的事实 | 判断不了的时候问自己:**换一份完全不同的 PDF,这条还成立吗?** 不成立就别写进来,写进 `references/` 当案例可以,但不要写成规则。 规则宁可少而硬,不要多而软。多一条软规则,执行者就多一分"看过了但没照做"的空间。 ## 阶段 6 · 例子层 用同一组列换成一个具体场景。例子要**自下而上读**:最底下是"这个场景里有什么",往上是"怎么评价它",最上面是"拿来做什么"。 每一行是一条可追溯的链:挂在哪个实体上 → 从哪来 → 用哪个标准概念 → 量什么 → 实际多少。 ## 交付物 ``` <name>.html 单文件,无构建,双击可开 <name>.html.bak 首次改动前的备份 ``` HTML 骨架见 `references/skeleton.html`,视觉语法细则见 `references/visual-grammar.md`。 ## 检查清单 交付前逐条过: - [ ] 派过 subagent 通读全文,不是只读了目录和几节 - [ ] 原文的每张 figure 都用 Read 直接看过 - [ ] subagent 指出的易误读点,图上有体现(比如"全文规则均为 should") - [ ] 三张图共用同一组列,竖虚线贯穿 - [ ] 颜色不超过三类,全文统一,黑灰只给结构;**没有单一强调色** - [ ] 卡片四件套齐全(结论式标题 / 副标题说清图例 / 图 / 来源行) - [ ] 没有"方案 A""图 2"这类只有作者知道的编号 - [ ] 条款号保留,能回查 - [ ] 做过一次纯视觉 review(2 倍截图分块看),且截图高度是脚本自动测的 - [ ] 所有元素用 `cx(i,w)` / `lane(i).cx` 定位,全文没有 `X[i] + 偏移`;检查脚本的「泳道居中」零报 - [ ] 横向区块都走了 `place()`,`cols` 逐个想清楚;检查脚本报的"待确认"逐条看过 - [ ] 图例只画一次,第二张起写"颜色含义与上图相同" - [ ] `node --check` 过,且 Chrome console 无 uncaught - [ ] 最小字号 ≥ 9px(中文),无文字重叠、无超出画布 - [ ] 数据来源行写明了哪些内容不来自这份 PDF