html-to-pptx
何时触发
用户说出下列任一意图:
- "把这个 HTML / 网页 deck 转成 PPT / pptx"
- "做了 HTML 幻灯片,要给同事一份 ppt"
- "汇报现场不方便放浏览器,想要 ppt 文件"
- 已有 HTML 文件路径 + 提到 ppt / pptx / 演示 / 幻灯片
调用
<skill_dir> 是这个 skill 安装路径(通常 ~/.claude/skills/html-to-pptx/,Windows 上是 %USERPROFILE%\.claude\skills\html-to-pptx\)。下面命令里的 <skill_dir> 替换成实际路径,或者先 cd <skill_dir> 再直接 python convert.py …。
python <skill_dir>/convert.py <input.html>
- 默认输出到与输入同目录的
<input>.pptx - 字体完全按需:HTML 用到的字体在 convert 时从 Google Fonts 拉取并 subset 嵌入,缓存到
%LOCALAPPDATA%\html-to-pptx\fonts\或~/.cache/html-to-pptx/fonts/,下次同名字体秒复用 - HTML 含 CJK 字符会自动种子 Noto Sans SC + Noto Serif SC(首次约 40 MB / ~30s 下载并 instance 静态 Regular/Bold,之后命中 cache)
- GF 没有的家族会回退到 viewer 系统字体并打印 warning
| 选项 | 含义 |
|---|---|
--out <path> | 自定义输出 .pptx 路径 |
--keep-screenshots | 同时保留每页 HTML 参考截图 + preflight.json |
--no-embed-fonts | 跳过字体嵌入。文件更小但换机会回退到系统字体 |
--no-preflight | 关闭 Stage 1 风险预扫 |
--no-verify | 关闭 Stage 5a 结构化自检 |
--no-visual-audit | 关闭 Stage 5b 视觉 audit 物料产出。日常不要关 |
--install-user-fonts | 把自动解析到的非 CJK 字体装到用户字体目录(让 WPS 能正确渲染)。Win/macOS/Linux 都支持。必须先问用户,见下方"配置确认"章节 |
--only-slides N,N,N | 增量重跑。逗号分隔的页号(1-based)。measure 只跑指定页 + 与上轮 cached measurement 合并;assemble/embed 仍全量;Stage 5a 只渲指定页;Stage 5b 只重建指定页的 compare 图——其它页全部复用上轮缓存。audit 迭代轮专用,详见下方"增量重跑"章节 |
--cleanup | 不做转换。删 input.pptx 旁的 audit / measurement / preflight 工作物,只保留 .pptx 和 audited.html。最终交付前用,见下方"工作流"末步 |
工作副本(原 HTML 不动)
第一次 python convert.py <input>.html 跑完,convert.py 自动 cp 一份 <input>.audited.html 到源 HTML 同目录。机制:
- 所有 audit 修复改
audited.html,不改源 HTML - 后续轮 convert(含
--only-slides)input 用<input>.audited.html,输出仍是<input>.pptx(自动去掉.audited后缀) audited.html已存在 → 复用,不覆盖(保留上轮修复)- agent 误传源 HTML 当 input → convert.py 内部检测后切到 audited.html 兜底
- cleanup 不删 audited.html(视为可交付副产物,跟
.pptx一组交付) - 想从头开始 → 手动删 audited.html 后重跑
配置(.config.local.toml)
skill 根目录下可选的本机偏好文件,gitignored,每用户一份。当前有三类偏好:
[fonts]
auto_install = "ask" # "yes" 自动装、"no" 永不装、"ask" 由 agent 询问
[cleanup]
default = "clean" # "clean" 工作流末步自动 cleanup、"keep" 保留 audit 产物
[audit]
mode = "ask" # "triage" / "page" / "manual" / "ask" 由 agent 询问
- 文件 / key 缺失 → 走默认(同上)
- 模板:
<skill_dir>/.config.local.toml.example fonts.auto_install由 convert.py 强制读取并应用(=yes 自动加--install-user-fonts、=no 自动忽略)cleanup.default由 agent 在工作流末步读取决定行为audit.mode由 agent 在第一次 convert 前确认;Stage 5b 后按该值执行审查:"triage"→ 主 agent 自己看audit_contact_*.png总览分流,再派 sub-agent 看入围页单图,节省 token"page"→ 每个目标页都派 sub-agent 看单页slide_NN_compare.png,覆盖最严"manual"→ 只生成审查物料,交给用户人工核查;用户反馈问题后再修"ask"或缺失 → agent 通过 AskUserQuestion 收集偏好后改写此项(默认)
- 首次使用:agent 通过 AskUserQuestion 收集
fonts.auto_install和audit.mode后自己 Write 这个文件——用户不用手动建
配置确认(第一次 convert 之前完成)
两条偏好要在第一次 convert 之前确认:fonts.auto_install 和 audit.mode。流程相同,差别只在触发条件和问法。
通用流程
每条偏好独立按下面顺序判定:
- Read
<skill_dir>/.config.local.toml,看对应字段:- 合法非
ask值 → 按该值走,会话内不再问 ask或文件 / 字段缺失 → 走"触发判定 + ask"
- 合法非
- CLAUDE.md / 全局指令写明了等同值的,按写明值走,并补写 config(保持单一事实源)
触发后 AskUserQuestion 问一次,两条都要做:
- 写入
<skill_dir>/.config.local.toml对应字段为用户答复(不写ask,否则下次会话还会再问) - 本次会话同时按答复走
写 config 时:文件不存在 → Write 整文件落盘([fonts] [cleanup] [audit] 三段模板);文件已存在 → 用 Edit 做单行 replace(只改这一个字段),不要 Write 重写整文件,否则会吞掉用户其它自定义 key。
同一会话两条都触发 → 合并到同一条 AskUserQuestion 里一次问完。
fonts.auto_install —— 字体安装
PowerPoint COM slide.Export() 和 WPS Office 都不读 pptx 内嵌的裸 TTF——audit 渲染和 WPS 都回退到系统字体。装到用户字体目录后两者都按系统字体走。目录(均为用户级,可手工删):
- Windows:
%LOCALAPPDATA%\Microsoft\Windows\Fonts\+ HKCU 注册 - macOS:
~/Library/Fonts/ - Linux:
~/.local/share/fonts/+fc-cache -f
触发判定:看 HTML <head> 的 <link href="...fonts.googleapis.com/..."> / @import url(https://fonts.googleapis.com/...) / <style> 里 font-family: 出现的字体名:
- 触发:任何 GF / 自托管字体(Bricolage Grotesque、DM Sans、Inter、Space Grotesk、Caveat 等)
- 不触发:只用系统字体白名单(Arial、Times New Roman、Helvetica、Helvetica Neue、Courier、system-ui、-apple-system、BlinkMacSystemFont、SF Pro、Microsoft YaHei、SimSun、PingFang、Hiragino)
按答复:同意 → 加 --install-user-fonts;拒绝 → 不加。
audit.mode —— 视觉审查模式
三种模式 triage / page / manual(含义见上方 "配置" 章节)。每次会话第一次 convert 之前都判定(不依赖 HTML 内容触发)。AskUserQuestion 用单选题,选项即三种模式。
工作流(强制)
convert.py 跑完不等于交付完成。完整流程:
convert.py(首轮自动 cp <input>.audited.html,所有修复改这个副本)
→ Stage 5a 结构化自检(OOXML 扫描,给提示)
→ Stage 5b 视觉 audit 物料 — 需要 PowerPoint COM 或 LibreOffice 渲染器
→ 读 `audit.mode` 决定审查方式:triage / page / manual
→ 【agent audit】
triage 模式:主 agent 读 contact sheet 分流 → sub-agent 看入围页单图 → 合并写 audit_findings.md
page 模式:全量 sub-agent 看单页 → 合并写 audit_findings.md
manual 模式:把 audit 物料交给用户,等待用户人工反馈
→ 按 finding 逐项做最小局部修改,目标是 audited.html(见下"修复纪律")
→ 一批 finding 改完后 `convert.py <input>.audited.html --only-slides <被改页号>` + 只对 fresh 页重审(见下"增量重跑")
→ 所有页 OK 或仅剩 LOW
→ 【若撞到 HTML 反模式 / 新 OOXML 边界】沉淀到 lessons-learned,见下方"沉淀 HTML 问题与 OOXML 边界"
→ 读 `<skill_dir>/.config.local.toml` 的 `cleanup.default`:
"clean"(缺省)→ 直接 `python convert.py <out>.pptx --cleanup`,不问用户
清完目录剩:<input>.html(源,未动)+ <input>.audited.html(修复版)+ <out>.pptx
"keep" → 跳过 cleanup,audit 产物全部保留
→ 把 .pptx 路径交付给用户(audited.html 作为修复版 HTML 一同交付)
矢量优先原则(强制)
能用 PPT 原生 text / shape / line 表达的内容,优先保持可编辑:普通背景色、普通四边框、单边线、简单矩形/椭圆、简单 ::before / ::after 线框都应走 PPT shape。
deco_snapshot 只兜底 OOXML 难表达的装饰(命中条件 + 完整档位定义 + 截图/矢量层协同机制见 references/supported-css.md)。不要为了省事把普通线条、图表轴线、简单边框统一截图——这会牺牲 PPT 可编辑性,也会让后续局部修复变难。
渲染器要求(Stage 5b 前置条件)
Stage 5b 视觉 audit 需要把 .pptx 渲染成 PNG,依赖:
- PowerPoint COM(Windows + Office +
pywin32),或 - LibreOffice(跨平台,配
pip install pdf2image)
任一可用就能跑 audit。convert 输出里看到 [self-check] 跳过:找不到可用的 pptx 渲染器,说明两个都没装——此时 audit 不会产出 compare 图,Stage 5b 直接跳过。
这种情况下 agent 必须 ask 用户(不要静默交付未审计的 pptx):
"你机器上没装 PowerPoint 也没装 LibreOffice,视觉 audit 跑不了,PPT 可能有看不出的视觉 bug。三个选择:
- 装 LibreOffice(推荐,跨平台,2-3 分钟):
winget install LibreOffice.LibreOffice(Windows)/brew install --cask libreoffice(mac)/apt install libreoffice(Linux),然后pip install pdf2image,重跑 convert- 跳过 audit 直接交付(接受 PPT 可能有视觉 bug 的风险)
- 在已经装了 Office 的另一台机器上重跑"
用户选 1 → 等他装完重跑;选 2 → 加 --no-visual-audit 跑一遍,把告知风险后交付;选 3 → 把当前目录 + HTML 发给他。
视觉 audit 模式(config,前提:5b 跑起来了)
执行视觉审查前读取 <skill_dir>/.config.local.toml 的 [audit].mode。正常应在第一次 convert 前通过上方 "配置确认" 完成询问;如果到这里 mode 还是 "ask"(说明 agent 漏问了),立刻按 "配置确认" 章节询问用户再继续。
每个 mode 的具体含义见上方 "配置" 章节;这里只列适用场景:
| mode | 适用 |
|---|---|
triage | 默认,节省 token |
page | 最终交付前、关键客户、用户要求最严 |
manual | 用户想自己看 |
triage 的分工很明确:第 1 轮 contact pass = 主 agent 自己看缩略图分流(廉价,上下文已含源 HTML / Stage 5a / preflight);第 2 轮 detail pass = 把入围页派 sub-agent 看单页 compare 图。不能只凭缩略图判 HIGH/MID finding;要报 finding 必须经第 2 轮 sub-agent 单页确认。
audit 分发与 compare 图读取规则
page 模式所有页都走 Claude Agent(...) sub-agent 并行 dispatch。triage 模式只有第 2 轮入围页走 sub-agent;第 1 轮 contact sheet 由主 agent 自己 Read,不派 sub-agent。
主 agent 主动读 compare 图的场景仅限以下四种:
triage第 1 轮读audit_contact_*.png总览分流- finding 描述含糊 / 与源代码冲突无法定位时,读对应单页
slide_NN_compare.png - sticky 命中前确认读单页
- 用户点名反馈某一页读单页
除此之外不逐页 Read compare 图。
| slide 数 | 策略 |
|---|---|
| ≤ 4 | 1 个 batch |
| 5-20 | 每 batch 4 页(向上取整,例:9 页 = 4+5) |
| > 20 | 每 batch 4-5 页 |
完整 sub-agent 调用模板 + 检查清单 + findings 格式见 <out>_audit/audit_prompt.md。多个 Agent(..., run_in_background: true, subagent_type: "general-purpose") 调用塞在主 agent 同一条 message 里才并行。
sub-agent 只返回 findings 文本(page 模式含每个目标页的 ## page NN 块;triage 细看批次含每个被放大复核页的块),主 agent 统一合并写 audit_findings.md——并发写会互相覆盖。
每条 finding 必须点名稳定元素短名,并描述"HTML 半图实际状态;PPT 半图差异"。只报告 PPT 相对 HTML 新增或放大的视觉问题;HTML 半图本身已有的问题不算转换 finding。
与 sub-agent 并行的主 agent 准备(强制)
发起 page 模式或 triage 第 2 轮 detail sub-agent 时,Agent 调用用 run_in_background: true,并在*