
Web Video Presentation
- 49 installs
- 10.1k repo stars
- Updated July 12, 2026
- conardli/web-design-skill
This is a copy of web-video-presentation by conardli - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
web-video-presentation is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- web-video-presentation
- AI & Agent Building
- AI-coding skill
Web Video Presentation by the numbers
- 49 all-time installs (skills.sh)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/conardli/web-design-skill --skill web-video-presentationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 49 |
|---|---|
| repo stars | ★ 10.1k |
| Last updated | July 12, 2026 |
| Repository | conardli/web-design-skill ↗ |
What it does
Helps with ai & agent building tasks.
Files
Web Video Presentation
把一篇文章或口播稿,一步步做成可录屏的"伪装成视频的网页",可选合成 口播音频。产出物 = Vite + React + TS 项目 + 按章节切分的音频。
适用场景
- "我有口播稿 / 一篇文章,帮我做成视频" —— 口播驱动的内容
- 想做 "动态 PPT"
- 16:9 横屏录屏,大字、留白、每屏都要有动效
- 教学 / 产品演示 / keynote 想要电影感
- B 站 / YouTube /抖音视频内容
本 Skill 以方法论 + 协作流程为核心。脚手架模板提供 token 和原语, 但每个美学决策(配色、字型、动效气质)都应该针对你的主题重新设计 —— 不要照搬。
---
工作流总览
Phase 1 内容编写
1.1 识别用户输入
1.2 一次产出 script.md + outline.md
(口播稿 + 开发计划)
▼
[Checkpoint Plan] ← 必须停。一次对齐 5 件事:
稿子 / outline / 主题 / 素材 / 开发模式
▼
Phase 2 网页开发
2.1 脚手架(按选定主题)
2.2 第 1 章 = 主线程 + 完整版本(强制 anchor)
▼
[硬节点] 用户验收第 1 章 ← 不可跳过
▼
2.3 第 2~N 章(按选定模式:A 逐章 / B 顺序 / C 并行)
▼
[Checkpoint Audio] ← 必须停。是否合成音频
▼
Phase 3 音频合成(可选)
▼
Phase 4 录屏 + 后期工作目录约定(agent 在用户当前目录下创建 / 编辑):
my-video/
├── article.md # 用户给原文时必有 —— 不删!开发阶段画面信息源
├── script.md # 必有:保持原文语言的平台化口播稿(决定节拍)
├── outline.md # 必有:开发计划(章节切分 + 每步内容 + 信息池)
└── presentation/ # 脚手架产出的 Vite + React + TS 项目
├── src/chapters/<NN>-<id>/
│ ├── <Chapter>.tsx # 视觉实现
│ ├── <Chapter>.css
│ └── narrations.ts # ★ step 数 + 口播文本的唯一真相源
├── scripts/
│ ├── extract-narrations.ts # 扫所有 narrations.ts → audio-segments.json
│ ├── synthesize-audio.sh # provider-agnostic runner(循环 segments)
│ └── tts-providers/ # 每 provider 一个 .sh(内置 2 个)
│ ├── README.md # 三函数契约 + 5 段现成代码片段(11labs / edge-tts / say / azure / gcloud)
│ ├── minimax.sh # 默认 provider,用 mmx-cli
│ └── openai.sh # 内置 OpenAI TTS(curl + OPENAI_API_KEY)
├── audio-segments.json # extract 产出(合成前 review)
└── public/audio/<id>/<N>.mp3 # 可选:合成的音频关键:narrations.ts 是 step 数和音频合成的唯一真相源。章节.tsx里的if (step === N)出现的最大 N + 1 必须等于
narrations.length。这保证 5 处地方(script / outline / 章节代码 /chapters.ts / 音频文件)永远不会漂。
---
硬性自检协议(贯穿整个 Skill)
下面三个产出,每一个完成后必须走自检 → 修复 → 再汇报 / 推进:
| 产出 | 自检清单出处 |
|---|---|
script.md | `SCRIPT-STYLE.md` 三层自检(形式 / 风骨 / 念出来) |
outline.md | `OUTLINE-FORMAT.md` 自检 |
| 单章实现完成 | `CHAPTER-CRAFT.md` 完工自检 |
执行方式(按能力降级,优先用更隔离的方式):
1. Agent Teams(最优):开一个独立的 reviewer agent,给它"产出文件 路径 + 对应清单 + 关键上下文",让它逐项核查并严格汇报结论 (哪几条 pass / 哪几条 fail + 证据 + 改写建议)。 2. subAgent(次优):没有 Teams 能力但能开 subagent 就用 subagent 走同样流程。 3. 自检(兜底):当前 agent 都没有上述能力,就自己严格逐项 核查 —— 不允许目测一遍就放行。
铁律:拿到结论后先按 fail 项把产出改完,再向用户汇报"做完了 + 自检结论 + 改了什么"。直接拿原始结论汇报但不修复 = 违规。
---
各阶段文件读取指南
不同阶段读不同的文件。长会话里 agent 容易遗忘原则,特别是 Phase 2.4 的"实现单章"会重复 N 次 —— 每次都要回看核心约束。
| 阶段 | 必读(每次都看) | 一次性看完 / 按需查 |
|---|---|---|
| Phase 1.1-1.2 内容编写 | references/SCRIPT-STYLE.md + references/OUTLINE-FORMAT.md + article.md(用户原文,如有) | —— |
| Checkpoint Plan 选主题 | —— | themes/*/theme.json(动态读全部,列清单 + bestFor 推荐 + descriptionZh);references/THEMES.md(用户想了解主题系统时) |
| Phase 2.1 脚手架 | —— | SKILL.md 本节看一次 |
| Phase 2.4 实现单章(×N 次,被 2.2 / 2.3 调用) | `references/CHAPTER-CRAFT.md` 单一入口 —— Part 0 十条原则 / Part 1 开工 5 问 / Part 2 关系→动作决策树 / Part 3 视觉工具箱 / Part 4 时长参考 / Part 5 反 AI 味反模式 / Part 6 代码硬规则(含 narrations.ts 强制约束)/ Part 7 完工自检 / Part 8 反馈速查 + 当前主题的 themes/<id>/theme.json + 当前章节的 outline.md 段落 + `article.md` 本章对应段落 + 素材清单 | references/EXAMPLES/(结构示意,不是抄袭模板);references/THEMES.md 完整 token 契约 |
| Phase 3 音频合成 | references/AUDIO.md(含 narrations.ts → segments.json → 任意 provider 流程,内置 minimax + openai) | templates/scripts/tts-providers/README.md(换 provider / 自带 TTS 时) |
| Phase 4 录屏 + 后期 | references/RECORDING.md(含 ?auto=1 自动录屏) | —— |
| 选 / 造 / 切主题 | —— | references/THEMES.md |
写章节时只读一份 `CHAPTER-CRAFT.md`。十条原则 / 开工 self-prompting /
决策树 / 反 AI 味反模式 / 完工自检全部并入这一份单一入口。EXAMPLES/不是必读 —— 先按内容自由设计,卡壳才翻(按 anchor 翻"形",不要照搬)。
---
Phase 1 —— 内容编写(一次产出)
1.1 识别用户输入
| 用户给的东西 | 该做的 |
|---|---|
| 原始文章(书面语 / 公众号 / 论文 / 博客) | 一次产出 script.md + outline.md(1.2),过 Checkpoint Plan |
| 直接的口播稿 / 视频脚本 | 落盘成 script.md,一次产出 outline.md(1.2 简化版),过 Checkpoint Plan |
| 啥都没有,只说"帮我做个 X 主题的视频" | 反问:先给一段素材或大纲。Skill 不替用户构思内容 |
1.2 一次产出 script.md + outline.md
两份产出物在一次思考中完成:
1. 生成 `script.md`:按 `references/SCRIPT-STYLE.md` 的规则把 article 转成保持原文语言的平台化口播稿。保留 `article.md` 不删——它是 outline 写信息池和章节实现画面时的细节源(双源原则)。 2. 生成 `outline.md`:按 `references/OUTLINE-FORMAT.md` 规则切章节 + 切 step + 每章首段抽信息池。
outline 的边界(关键):
| outline 必须写 | outline 不要写 |
|---|---|
| 章节切分 / 每章 step 数 / 估时 | 具体动画类型(blur clear / wipe / 弹簧) |
| 每步屏幕内容(hero / 数据 / 标语 / 列表项) | CSS 实现手段(filter / SVG / clip-path) |
| 章节级信息池:从 article 抽的数字 / 引用 / 案例 / 标签 | 时长数值(不写 ~2.5s / 80~120ms) |
| 步级关系名前缀("反差对照" / "递进列表" / "金句" 等可选 hint) | 持续微动 / 错峰量等微观节奏 |
outline 不写动画的理由:写死动画 = chapter agent 退化为翻译机;
留白让 chapter agent 在每步开工时按 `CHAPTER-CRAFT.md`
的"内容驱动决策树"自由设计,才有真正的视频感。详见
`CHAPTER-CRAFT.md` Part 0 原则 7。
落盘后必须先走自检再进 Checkpoint Plan:按上文「硬性自检协议」分别 对 script.md / outline.md 执行(优先 Agent Teams → subAgent → 自检), 按结论修复完成后再进入 Checkpoint Plan。
---
Checkpoint Plan —— 5 件事一次对齐(硬节点)
script.md + outline.md 写完后必须停下来。用户在这一个节点同时确认 5 件事。
agent 此时要做的预备工作
1. 读所有 themes/*/theme.json 拿 nameZh / descriptionZh / bestFor / mood —— 不要硬编码清单 2. 根据 script.md 的内容类型 / 关键词 / 语气,主动从主题里挑 2~3 套最匹配的推荐(匹配 bestFor 字段) 3. 扫一遍 outline.md 末尾"素材清单"部分
总结模板(骨架,agent 按情况填充)
内容计划写完,产出文件:
📄 article.md {若用户给原文则保留}
📄 script.md {X} 字 / ~{T} 分钟
📄 outline.md {N} 章 / {M} 步 + 每章信息池 + 末尾素材清单
章节速览:
1. <id> <章节标题> <S> 步 ~<T>s
2. ...
接下来一次对齐 5 件事:
1. 稿子 (script.md) 要不要改?
可以直接编辑文件,或口头告诉我修改方向。
2. 开发计划 (outline.md) 要不要改?重点看:
- 章节切分 / step 数 / 估时是否合理(合理判断:每章 30~60s)
- 每步屏幕内容是否清晰
- 每章首段「信息池」是否有足够的 article 细节供画面挂
- 末尾素材清单是否完整
3. 选哪个主题?我的推荐:
★ <推荐 1:nameZh (id)> — 因为 <bestFor 命中>;<descriptionZh 摘要>
★ <推荐 2 / 推荐 3>
其它可选:<剩余主题,nameZh + 一句话>
也可以让我帮你做新主题(详见 references/THEMES.md)。
4. 真素材怎么准备?粗看本视频要的图:<列粗略清单>
a) 我从 <现有素材路径> 帮你挑 b) 你自己提供 c) 全部 placeholder
5. 开发模式选哪个?
**第 1 章无论哪种模式都必须主线程做完 + 用户验收**(强制 anchor)。
差异在第 2 章及之后:
A) 默认 · 逐章确认(推荐)
每章做完都暂停验收 → 风险可控 / 节奏最稳
B) 第 1 章后顺序开发(不并行)
第 2~N 章主线程顺序做完后统一验收 → 速度中 / 适合 agent 不支持并行
C) 第 1 章后并行开发(subagent)
第 2~N 章用 subagent 并行 → 最快 / 用户控并行数(一次几章)
⚠️ 风格各章会有差异(这是预期,主题禁区兜底)收到反馈后:
- 稿子 / outline 要改:直接编辑文件,编辑完 ping 一次(或口头描述 agent 改)
- 主题必须明确才进入 Phase 2。用户说"主题你帮我选" → 取你推荐的第 1 个,
告诉用户你选了什么、为什么,给反悔机会
- 模式选定 → 进 Phase 2
---
Phase 2 —— 网页开发
2.1 脚手架
bash <path-to-web-video-presentation>/scripts/scaffold.sh \
./presentation \
--theme=<用户选的主题 id>
bash <path-to-web-video-presentation>/scripts/scaffold.sh --list-themes自定义主题 → 先按 `references/THEMES.md`
"创作新主题"流程做一个themes/<my-theme>/,再--theme=<my-theme>。
脚手架带一个 01-example demo。在写第一章真实内容前删掉:
rm -rf presentation/src/chapters/01-example并把 presentation/src/registry/chapters.ts 里 EXAMPLE_CHAPTER 的 import 和数组项移除。
2.2 第 1 章 —— 主线程 + 强制验收
核心:第 1 章 = 完整版本一次到位(节奏 + 视觉 + 真素材齐全)。 没有"骨架版"概念 —— 第一章就要做出用户能直接验收的样板。
为什么第 1 章必须主线程:
- 它是 `CHAPTER-CRAFT.md` 这套指引在**当前
主题 + 当前题材**下的第一次落地
- 如果指引有盲区 / 主题颜色 / 字体 token 不够用,第 1 章一定会暴露 ——
这时候有人类反馈就能修指引 / 调主题,早改成本最低
- 后续章节(无论顺序 / 并行)都要参考第 1 章的代码模式,所以第 1 章 =
当次项目的"风格锚点(不强求章节间一致,但单章自身得有完整说服力)"
做完第 1 章后必须停下来等用户验收:
第 1 章 <id> 做完了,dev server 在 localhost:5173 运行。
验收重点:
□ 视觉气质对不对?符合 <theme nameZh> 的预期吗?
□ 节奏对不对?某些步太快 / 太慢 / 信息太薄?
□ 内容驱动动画是否到位?还是有几步是无脑入场动画?
□ 双源原则:屏幕画面有没有"口播没念但 article 能挂"的细节?
□ 反 AI 味检查:紫粉渐变 / 圆角彩色边框 / 假插画 / emoji 是否有?
问题告诉我,我针对性改。OK 了告诉我"继续",我按选定模式做第 2 章及之后。2.3 第 2~N 章 —— 按选定模式
所有模式下的共同规则:每章独立按 `CHAPTER-CRAFT.md` 开发。风格不强求章节间完全一致 —— 主题颜色 / 字体 token 兜底视觉 统一,动画 / 节奏 / 视觉演示由章节自由发挥是设计预期。
模式 A · 默认 · 逐章确认
第 2 章做完 → 暂停验收 → OK → 第 3 章 → 暂停 → ... → 第 N 章。每章 独立验收,问题随时改,风险最低,节奏最稳。用户不明确选模式时 默认走这个。
模式 B · 第 1 章后顺序开发
第 2 章 → 第 3 章 → ... → 第 N 章 主线程顺序做完,最后统一验收。 速度中等,适合 agent 不支持并行任务的环境。
模式 C · 第 1 章后并行开发(subagent)
用 subagent 把第 2~N 章并行做完,最大并行数由用户控制("一次 4 章" / "一次 2 章")。最快,但风格各章会有差异 —— 这是预期,因为:
1. 每个 subagent 看不到别的 subagent 产出,无法机械对齐 2. 章节代码物理分离(每章一个文件夹 / 自己的 CSS 前缀),不会互相 破坏 3. 主题 token 兜底视觉统一(颜色 / 字体 / hero 数字 / 卡片 / 分割线 性格 / 装饰),气质不会跑偏 4. 风格不一致 = 人手写视频的呼吸感(多 voice / 多视角)
并行 subagent 的 prompt 必须包含:
- 当前章节 outline 段落(含信息池)
references/CHAPTER-CRAFT.md的路径(单一必读 —— 视觉演示要求 +
逐步揭示 + 双源原则 + 反 AI 味 + 代码红线 + 完工自检全部在这一份里)
- 当前主题
theme.json的descriptionZh/mood/bestFor(参考气质
即可,动画 / 时长 / 字号 / emoji 由 chapter agent 自由决定)
- 第 1 章代码作为"代码风格"参考(不是"视觉抄袭对象")
- 硬规则:每章独立 CSS 前缀(
.cd-/.mg-/.pm-/ ...);
不修改 chapters.ts;完工跑 npx tsc --noEmit
重要:无论选哪种模式,用户随时可以中途切换模式。第 2 章 OK 后用户说"剩下的并行" / "剩下的逐章" 都行。
2.4 实现单章(每章必走)
详细指引见 `references/CHAPTER-CRAFT.md` —— 单一必读入口,覆盖:视觉演示要求 / 逐步揭示 / 内容取舍 / 双源原则 / 视频演示基本审美 / 反 AI 味 / 代码红线 / 完工自检。
核心要点(CHAPTER-CRAFT.md 详述):
- 每章必须有 CSS / SVG / Canvas / JS 视觉演示,禁纯文字章节
- 逐步揭示:清单 / 列表必须 1 项 = 1 step,禁一次全展示
- 双源原则:节奏跟口播稿(顺序不能乱),细节回原文章抽(信息池 +
本章 article 段落)
- 完工自检逐项过,不达标回去改 —— 按上文「硬性自检协议」执行
(优先 Agent Teams → subAgent → 自检),改完再向用户汇报本章交付
2.5 大改后 bump STORAGE_KEY
改动 chapters.ts(增加 / 删除 / 重排章节,或某章 narrations.ts 长度变化)后,bump presentation/src/hooks/useStepper.ts 的 STORAGE_KEY(如 v4 → v5),避免持久化游标落到不存在的 step 上。
---
Checkpoint Audio —— 是否合成音频(硬节点)
Phase 2 结束后必须停下来,问用户:
网页做完,{N} 章 {M} 步,dev server 在 localhost:5173 跑着。
要不要合成音频做"自动播放录屏"?
✓ 合成 → 扫所有章节的 narrations.ts 出 audio-segments.json,
调 TTS provider 合成每步一个 mp3 到 public/audio/。
合成完后用 ?auto=1 模式可以一镜到底录屏(音视频天然同步)。
内置两个 provider:
• minimax (mmx-cli) —— 默认,中文音色稳
• openai (OPENAI_API_KEY) —— curl-based,多数已有 key
其它后端 (ElevenLabs / edge-tts 免费 / macOS say 离线 /
Azure / Google) 见 scripts/tts-providers/README.md 的现成片段。
✗ 不合成 → 跳过 Phase 3,直接 Phase 4 用手动录屏 + 后期配音。要合成 → Phase 3。不合成 → 直接 Phase 4。
---
Phase 3 —— 音频合成(可选)
详细流程见 `references/AUDIO.md`。简版:
cd presentation
npm run extract-narrations # 扫所有 narrations.ts → audio-segments.json
# 让用户扫一眼 audio-segments.json 确认文本对
npm run synthesize-audio # 默认 minimax provider,增量
# 或用内置 openai (要 OPENAI_API_KEY):
PRESENTATION_TTS=openai npm run synthesize-audio
# 或自定义:写一个 scripts/tts-providers/<name>.sh,见该目录的 README.md合成完告诉用户:输出位置 / 总段数 / 哪些段时长异常(太长 = 该 step 拆 分;太短 = 文案太薄)—— 给最后一次校准节奏的机会。然后进入 Phase 4。
---
Phase 4 —— 录屏 + 后期
详见 `references/RECORDING.md`。两种路径:
| 场景 | 推荐路径 |
|---|---|
| Phase 3 已合成音频 | Auto 模式一镜到底:浏览器开 localhost:5173/?auto=1 → 按 SPACE → 整片自动播完 → 停录 → 裁头尾即成片,无需后期对音轨 |
| Phase 3 跳过 | 默认 Manual 模式手动点击推进 → 后期任意剪辑工具配音 |
agent 在 Phase 3 / Checkpoint Audio 后主动告诉用户适合的录屏路径。
---
十条原则(一句话清单)
完整展开见 `references/CHAPTER-CRAFT.md` Part 0 —— 写章节时回那里查,下面只是索引。
| # | 原则 | 一句话 |
|---|---|---|
| 1 | 16:9 固定舞台 | 内容 1920×1080 + transform scale,没有响应式 |
| 2 | 全局 step 计数器 | 章节是 step 的纯函数,无定时器 |
| 3 | 每步独占整屏 | if (step === N) return <FullScene /> |
| 4 | 口播节拍 = step | 一节拍 = 一 step = 一聚焦想法 |
| 5 | 隐藏的边角控件 | 进度条 / 翻页器默认 opacity 0 |
| 6 | 舞台无 chrome | 没有 header / footer / 页码 / 品牌条 |
| 7 | 内容驱动动画 | 先找内在动作,找不到才入场动画兜底;持续微动慎用 |
| 8 | 多点逐个揭示 | 1 项 = 1 step,禁同步 stagger 上 N 项 |
| 9 | 整片同一主题 | 章节间不翻表面色;颜色 / 字体走 token,其它尺度章节自由 |
| 10 | 双源原则 | script 定节拍,article 定画面密度(落到信息池) |
---
常见用户反馈速查
简化表见 `references/CHAPTER-CRAFT.md` Part 8「常见反馈速查」。关键:先定位是哪一层(节奏 / 视觉 / 内容 / 代码),再改最小切片,不要重做整章。
---
相关资源
按"何时读"标注,避免一次性全读:
| 文件 | 何时读 | 内容 |
|---|---|---|
| `references/SCRIPT-STYLE.md` | Phase 1.2 必读 | 文章 → 口播稿规则、平台变体 |
| `references/OUTLINE-FORMAT.md` | Phase 1.2 必读 | outline.md 字段 spec、命名约定、章节切分、信息池 |
| `references/CHAPTER-CRAFT.md` | Phase 2.4 每章单一必读入口 | Part 0 十条原则 / Part 1 开工 5 问 / Part 2 关系→动作决策树 / Part 3 视觉工具箱 / Part 4 时长 / Part 5 反 AI 味反模式 / Part 6 代码硬规则 / Part 7 完工自检 / Part 8 反馈速查 |
| `references/EXAMPLES/` | 可选 —— 看结构 | 章节结构示意(hook / list-reveal / case-tech-review);不是抄袭模板 |
| `references/THEMES.md` | 选 / 造 / 切主题时 | 完整 token 契约 + 内置主题清单 + 创作流程 |
| `references/AUDIO.md` | Phase 3 才读 | provider-agnostic 音频合成流程、内置 minimax 用法、换 provider 路径、故障排查 |
| `templates/scripts/tts-providers/README.md` | 换 / 加 TTS provider 时 | 三函数契约 + 内置 2 个 (minimax / openai) + 5 种现成代码片段(ElevenLabs / edge-tts / macOS say / Azure / Google) |
| `references/RECORDING.md` | Phase 4 才读 | 录屏工具 + 后期合成 |
| `themes/` | Checkpoint Plan / Phase 1.2 时翻 | 内置主题(每个含 theme.json + tokens.css) |
| `scripts/scaffold.sh` | Phase 2.1 跑一次 | 一键项目脚手架 |
{
"name": "web-video-presentation",
"version": "1.2.2",
"category": "Web Video / Presentation",
"description": "Turn scripts, articles, lessons, product demos, and talks into click-driven 16:9 web presentations that can be screen-recorded as cinematic videos. Ships a Vite + React + TypeScript scaffold, a (chapter, step) cursor model, hard collaboration checkpoints, and a theme-token architecture.",
"homepage": "https://github.com/ConardLi/garden-skills/tree/main/skills/web-video-presentation",
"compat": [
"claude-code",
"claude-ai",
"cursor",
"codex-cli",
"gemini-cli",
"opencode"
]
}
Web Video Presentation Skill
A method-driven agent skill for turning scripts and articles into click-driven 16:9 web presentations that can be screen-recorded as cinematic videos.
中文文档 · Back to collection root

---
What Is This?
web-video-presentation helps an agent build a Vite + React + TypeScript presentation that behaves like a video production surface rather than a slide deck. Each click advances one narration beat, each step owns the whole 1920×1080 stage, and the progress UI stays hidden unless hovered so the output is clean for screen recording.
It is designed for:
- Turning a written article into a Bilibili / YouTube / video-channel narration script
- Turning an existing voiceover script into a cinematic web presentation
- Building product demos, tutorials, keynote-style explainers, and visual talks
- Creating “dynamic PPT, but not PPT” experiences with strong motion and pacing
- Optionally synthesizing narration audio after the visual outline is approved
The skill is primarily a methodology and collaboration workflow. The scaffold supplies reusable tokens, stage primitives, themes, and examples, but each project should still choose a visual language that fits the topic.
---
Core Ideas
- Fixed 16:9 stage — content is authored in a stable 1920×1080 coordinate system and scaled to the viewport.
- One global step cursor — click or keyboard advances
(chapter, step), with the cursor persisted locally. - One step, one idea — every beat gets a focused full-screen scene instead of accumulating slide bullets.
- Script beats drive structure — narration rhythm maps directly to visual steps.
- Hidden chrome — progress controls are hover-only, keeping recordings clean.
- Motion first — each scene needs a moving visual anchor; static paragraphs are treated as a smell.
- Theme tokens — visual decisions flow through semantic tokens so themes can change the whole feel.
- Pluggable TTS — provider-agnostic audio runner ships two built-in providers (MiniMax
mmx-cliand OpenAI TTS via curl); swap to ElevenLabs / edge-tts / Azure / Google Cloud / macOSsay/ any self-hosted TTS by dropping a single shell file intotts-providers/. - Hard checkpoints — the agent pauses after script/theme alignment, after outline approval, and before optional audio synthesis.
---
Workflow
Phase 1.1 Identify input
Phase 1.2 Article -> narration script
|
Checkpoint A1 Script, theme, and rough asset plan
|
Phase 1.3 Script + article -> outline.md
|
Checkpoint A2 Outline approval + development mode
|
Phase 2 Build the Vite / React / TS presentation
|
Checkpoint B Ask whether to synthesize audio
|
Phase 3 Optional audio synthesis
Phase 4 Recording and post-productionThe checkpoints are part of the skill contract: the agent should not silently rush from raw article to finished code. Theme choice influences motion design, and outline approval keeps chapter pacing from drifting.
---
What It Ships
skills/web-video-presentation/
├── SKILL.md
├── README.md / README.zh-CN.md
├── references/
│ ├── PRINCIPLES.md
│ ├── CHAPTER-CRAFT.md
│ ├── OUTLINE-FORMAT.md
│ ├── SCRIPT-STYLE.md
│ ├── THEMES.md
│ ├── AUDIO.md
│ └── RECORDING.md
├── scripts/
│ └── scaffold.sh
├── templates/
│ ├── index.html
│ ├── vite.config.ts
│ ├── scripts/
│ │ ├── extract-narrations.ts
│ │ ├── synthesize-audio.sh # provider-agnostic runner
│ │ └── tts-providers/ # 1 file = 1 TTS backend
│ │ ├── README.md # contract + ready-to-paste ElevenLabs / edge-tts / Azure / Google / say snippets
│ │ ├── minimax.sh # default — uses mmx-cli
│ │ └── openai.sh # built-in — uses OPENAI_API_KEY via curl
│ └── src/
└── themes/ # 23 themes, each with its own signature
├── midnight-press/
├── warm-keynote/
├── newsroom/
├── bauhaus-bold/
└── ... # full list in references/THEMES.md---
Quick Start
Copy the skill into the directory your agent scans, then ask it to turn a script or article into a web-video presentation.
To scaffold manually from inside a project:
bash skills/web-video-presentation/scripts/scaffold.sh ./presentation --theme=paper-pressList available themes:
bash skills/web-video-presentation/scripts/scaffold.sh --list-themesThe generated presentation/ project is a normal Vite + React + TypeScript app. Run it like any other Vite project, then record the 16:9 stage with your screen recorder.
---
Theme Gallery
The skill ships 23 themes, each with its own design DNA — not a simple color swap. Browse the gallery below by canvas tone, pick one that fits the topic, or use any tile as a starting point for a derived theme. Click any preview to open the full-size 1920×1080 frame.
Frames are real 16:9 stages rendered by the live demo gallery at `demo/web-video-presentation-demo`.
Dark · 8 themes
Cinematic dark canvases — for focus, drama, and high-contrast storytelling.
<table> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/midnight-press.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/midnight-press.webp" alt="midnight-press preview" /></a> <br /><strong><code>midnight-press</code></strong> <br /><sub>Cinematic editorial dark · warm espresso + hot orange</sub> <br /><sub><b>Best for</b> · developer tutorials · AI & tool reviews · technical deep dives</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dark-botanical.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dark-botanical.webp" alt="dark-botanical preview" /></a> <br /><strong><code>dark-botanical</code></strong> <br /><sub>Premium editorial dark · terracotta / blush / gold glow</sub> <br /><sub><b>Best for</b> · brand films · fashion & beauty · premium product launches</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/chalk-garden.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/chalk-garden.webp" alt="chalk-garden preview" /></a> <br /><strong><code>chalk-garden</code></strong> <br /><sub>Slate chalkboard · handwritten Patrick Hand + chalk-yellow</sub> <br /><sub><b>Best for</b> · explainers · classroom teaching · beginner-friendly walk-throughs</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/blueprint.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/blueprint.webp" alt="blueprint preview" /></a> <br /><strong><code>blueprint</code></strong> <br /><sub>Drafting board · deep navy + cyan + 60 px grid</sub> <br /><sub><b>Best for</b> · tech architecture · system breakdowns · API / SDK intros</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/terminal-green.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/terminal-green.webp" alt="terminal-green preview" /></a> <br /><strong><code>terminal-green</code></strong> <br /><sub>80s phosphor CRT · mono-only + scanlines</sub> <br /><sub><b>Best for</b> · CLI tutorials · hacker / security topics · retro-tech homages</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/neon-cyber.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/neon-cyber.webp" alt="neon-cyber preview" /></a> <br /><strong><code>neon-cyber</code></strong> <br /><sub>Cyberpunk future · cyan + magenta double-neon</sub> <br /><sub><b>Best for</b> · AI / LLM reviews · web3 & security · futuristic / cyberpunk topics</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bold-signal.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bold-signal.webp" alt="bold-signal preview" /></a> <br /><strong><code>bold-signal</code></strong> <br /><sub>Hero pitch deck · dark gradient + orange focal card</sub> <br /><sub><b>Best for</b> · pitch decks · product launches · brand keynote opens</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/creative-voltage.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/creative-voltage.webp" alt="creative-voltage preview" /></a> <br /><strong><code>creative-voltage</code></strong> <br /><sub>Saturated electric blue + neon yellow halftone</sub> <br /><sub><b>Best for</b> · design week · studio showcases · type / visual-culture talks</sub> </td> </tr> </table>
Light · 15 themes
Bright editorial canvases — for clarity, restraint, and the warmth of printed paper.
<table> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/paper-press.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/paper-press.webp" alt="paper-press preview" /></a> <br /><strong><code>paper-press</code></strong> <br /><sub>Editorial paper · warm cream + hot orange</sub> <br /><sub><b>Best for</b> · magazine pieces · lifestyle · everyday tool reviews</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/newsroom.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/newsroom.webp" alt="newsroom preview" /></a> <br /><strong><code>newsroom</code></strong> <br /><sub>NYT broadsheet · newsprint cream + banner red</sub> <br /><sub><b>Best for</b> · documentary reporting · deep reviews · current-affairs commentary</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/monochrome-print.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/monochrome-print.webp" alt="monochrome-print preview" /></a> <br /><strong><code>monochrome-print</code></strong> <br /><sub>Refined Monocle / Wallpaper print restraint</sub> <br /><sub><b>Best for</b> · long-read adaptations · academic / opinion · arts criticism</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/vintage-editorial.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/vintage-editorial.webp" alt="vintage-editorial preview" /></a> <br /><strong><code>vintage-editorial</code></strong> <br /><sub>Witty Fraunces + geometric overlay (circle / line / dot)</sub> <br /><sub><b>Best for</b> · personal essays · culture columns · type / design talks</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/sunset-zine.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/sunset-zine.webp" alt="sunset-zine preview" /></a> <br /><strong><code>sunset-zine</code></strong> <br /><sub>Risograph zine · peach + magenta + dashed cut lines</sub> <br /><sub><b>Best for</b> · lifestyle vlogs · creative shares · short-video / zine-style</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/pastel-dream.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/pastel-dream.webp" alt="pastel-dream preview" /></a> <br /><strong><code>pastel-dream</code></strong> <br /><sub>Soft pastel + sage + right-edge pill ribbon</sub> <br /><sub><b>Best for</b> · product onboarding · friendly tutorials · wellness & parenting</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/warm-keynote.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/warm-keynote.webp" alt="warm-keynote preview" /></a> <br /><strong><code>warm-keynote</code></strong> <br /><sub>Modern SaaS keynote · glass slab + teal + warm grid</sub> <br /><sub><b>Best for</b> · SaaS keynotes · B2B launches · team-facing roll-ups</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/electric-studio.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/electric-studio.webp" alt="electric-studio preview" /></a> <br /><strong><code>electric-studio</code></strong> <br /><sub>Corporate clarity · crisp white + electric-blue base bar</sub> <br /><sub><b>Best for</b> · B2B product talks · investor decks · quarterly updates</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bauhaus-bold.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bauhaus-bold.webp" alt="bauhaus-bold preview" /></a> <br /><strong><code>bauhaus-bold</code></strong> <br /><sub>Manifesto modernist · 0 radius + 4 px thick frame</sub> <br /><sub><b>Best for</b> · product launches · manifestos · brand statements</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/swiss-ikb.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/swiss-ikb.webp" alt="swiss-ikb preview" /></a> <br /><strong><code>swiss-ikb</code></strong> <br /><sub>Extra-light 200 Helvetica + IKB + 1 px hairline grid</sub> <br /><sub><b>Best for</b> · AI / tech launches · year-in-review data · info-graphics</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dune.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dune.webp" alt="dune preview" /></a> <br /><strong><code>dune</code></strong> <br /><sub>Charcoal + sand · near-zero accent (architecture brochure)</sub> <br /><sub><b>Best for</b> · architecture & interior · art exhibitions · premium brand books</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/indigo-porcelain.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/indigo-porcelain.webp" alt="indigo-porcelain preview" /></a> <br /><strong><code>indigo-porcelain</code></strong> <br /><sub>Indigo <em>is</em> the ink (not an accent) + porcelain white</sub> <br /><sub><b>Best for</b> · academic research · AI / data deep dives · serious tech briefings</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/forest-ink.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/forest-ink.webp" alt="forest-ink preview" /></a> <br /><strong><code>forest-ink</code></strong> <br /><sub>Forest green <em>is</em> the ink + ivory (vintage National Geographic)</sub> <br /><sub><b>Best for</b> · nature & sustainability · documentary non-fiction · slow living</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/kraft-paper.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/kraft-paper.webp" alt="kraft-paper preview" /></a> <br /><strong><code>kraft-paper</code></strong> <br /><sub>Deep brown <em>is</em> the ink + kraft beige + copper accent</sub> <br /><sub><b>Best for</b> · book reviews · history & nostalgia · craft & food storytelling</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/split-canvas.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/split-canvas.webp" alt="split-canvas preview" /></a> <br /><strong><code>split-canvas</code></strong> <br /><sub>Dual-tone · peach left + lavender right</sub> <br /><sub><b>Best for</b> · A/B comparisons · dialogue stories · concept-contrast explainers</sub> </td> <td align="center" width="50%" valign="middle"> <br /> <strong>+ derive your own</strong> <br /><sub>See <a href="./references/THEMES.md">THEMES.md</a> for the token contract,<br />theme signatures, and Swiss yellow / green / orange variants.</sub> <br /><br /> </td> </tr> </table>
---
Reference Map
- PRINCIPLES.md — core rules for video-like web presentations
- CHAPTER-CRAFT.md — chapter implementation rules and visual checklist
- OUTLINE-FORMAT.md — required outline structure
- SCRIPT-STYLE.md — article-to-narration rewrite guidance
- PATTERNS.md — optional visual primitive recipes
- AUDIO.md — optional narration synthesis workflow (provider-agnostic)
- tts-providers/README.md — TTS provider contract + 2 built-ins (minimax / openai) + ready-to-paste snippets for ElevenLabs / edge-tts / Azure / Google Cloud / macOS say
- RECORDING.md — screen recording and post-production notes
Web Video Presentation Skill
把文章或口播稿做成点击驱动的 16:9 网页演示,并通过录屏产出有电影感视频的 Agent Skill。
English · 返回集合首页

---
这是什么?
web-video-presentation 帮 Agent 构建一种 Vite + React + TypeScript 演示:它看起来不是传统幻灯片,而更像为录屏设计的视频舞台。每次点击推进一个口播节拍,每一步独占 1920×1080 舞台,进度 UI 平时隐藏,只有悬浮时出现,方便录出干净画面。
它适合:
- 把文章改写成 B 站 / YouTube / 视频号风格口播稿
- 把已有口播稿做成有节奏的网页演示
- 做产品演示、教程、keynote 式讲解、视觉 talk
- 做“动态 PPT,但不要像 PPT”的演示体验
- 在视觉 outline 对齐后,可选合成口播音频
这个 Skill 的核心是方法论 + 协作流程。脚手架提供 token、舞台原语、主题和示例,但每个项目仍然应该根据主题重新选择视觉语言。
---
核心理念
- 固定 16:9 舞台:内容写在稳定的 1920×1080 坐标系里,再按视口缩放。
- 一个全局 step 游标:点击或键盘推进
(chapter, step),游标本地持久化。 - 一步一个想法:每个节拍独占整屏,不堆叠项目符号。
- 口播节拍驱动结构:讲述节奏直接映射为视觉 step。
- 隐藏 chrome:进度控制悬浮才出现,录屏画面保持干净。
- 动效优先:每一步都需要一个移动的视觉锚点,静态正文是坏味道。
- 主题 token:视觉属性通过语义 token 驱动,换主题不只是换颜色。
- 可插拔 TTS:provider-agnostic 音频 runner,内置 2 个 provider(MiniMax
mmx-cli+ OpenAI TTS via curl);往tts-providers/丢一个.sh就能换成 ElevenLabs / edge-tts / Azure / Google Cloud / macOSsay/ 任何自部署 TTS。 - 硬 checkpoint:稿子/主题、outline、音频合成前都必须停下来与用户确认。
---
工作流
Phase 1.1 识别用户输入
Phase 1.2 文章 -> 口播稿
|
Checkpoint A1 稿子、主题、粗略素材计划
|
Phase 1.3 口播稿 + 原文 -> outline.md
|
Checkpoint A2 outline 确认 + 开发模式选择
|
Phase 2 构建 Vite / React / TS 演示
|
Checkpoint B 询问是否合成音频
|
Phase 3 可选音频合成
Phase 4 录屏与后期这些 checkpoint 是 Skill 契约的一部分:Agent 不应该从原文一路闷头做到成品。主题选择会影响动效气质,outline 确认能避免章节节奏跑偏。
---
内含内容
skills/web-video-presentation/
├── SKILL.md
├── README.md / README.zh-CN.md
├── references/
│ ├── PRINCIPLES.md
│ ├── CHAPTER-CRAFT.md
│ ├── OUTLINE-FORMAT.md
│ ├── SCRIPT-STYLE.md
│ ├── THEMES.md
│ ├── AUDIO.md
│ └── RECORDING.md
├── scripts/
│ └── scaffold.sh
├── templates/
│ ├── index.html
│ ├── vite.config.ts
│ ├── scripts/
│ │ ├── extract-narrations.ts
│ │ ├── synthesize-audio.sh # provider-agnostic runner
│ │ └── tts-providers/ # 一个文件 = 一个 TTS 后端
│ │ ├── README.md # 三函数契约 + ElevenLabs / edge-tts / Azure / Google / say 的现成片段
│ │ ├── minimax.sh # 默认 provider(mmx-cli)
│ │ └── openai.sh # 内置:OpenAI TTS(curl + OPENAI_API_KEY)
│ └── src/
└── themes/ # 23 套主题,每套独立设计签名
├── midnight-press/
├── warm-keynote/
├── newsroom/
├── bauhaus-bold/
└── ... # 完整列表见 references/THEMES.md---
快速上手
把这个 Skill 复制到你的 Agent 会扫描的目录,然后让 Agent 把一篇文章或口播稿做成网页视频演示。
如果要手动脚手架:
bash skills/web-video-presentation/scripts/scaffold.sh ./presentation --theme=paper-press查看可用主题:
bash skills/web-video-presentation/scripts/scaffold.sh --list-themes生成的 presentation/ 是普通 Vite + React + TypeScript 项目。启动后用录屏工具录制 16:9 舞台即可。
---
主题画廊
Skill 内置 23 套主题,每套都有独立的设计 DNA —— 不是简单换色版。下面按底色分两组浏览,挑一套接近目标气质的,或者把任意一格当作派生新主题的起点。点击任意预览图可放大查看 1920×1080 原帧。
所有截图都是真实的 16:9 舞台,来自 `demo/web-video-presentation-demo` 现场画廊。
深色 · 8 套
电影感深色画布 —— 适合需要聚焦、戏剧张力、强对比的叙事。
<table> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/midnight-press.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/midnight-press.webp" alt="midnight-press 预览" /></a> <br /><strong><code>midnight-press</code> · 暗色印刷</strong> <br /><sub>电影感编辑暗底 · 暖暗底 + 火热橙</sub> <br /><sub><b>适合</b> · 开发者教程 · AI / 工具评测 · 技术 deep dive</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dark-botanical.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dark-botanical.webp" alt="dark-botanical 预览" /></a> <br /><strong><code>dark-botanical</code> · 暗夜植物</strong> <br /><sub>高级时尚刊物 · 暖陶 / 玫粉 / 鎏金叠层</sub> <br /><sub><b>适合</b> · 品牌故事 · 时尚 / 美妆 · 高端产品发布</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/chalk-garden.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/chalk-garden.webp" alt="chalk-garden 预览" /></a> <br /><strong><code>chalk-garden</code> · 粉笔花园</strong> <br /><sub>深石板黑板 · 手写 Patrick Hand + 粉笔黄</sub> <br /><sub><b>适合</b> · 科普讲解 · 教学课堂 · 面向初学者的亲切口吻</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/blueprint.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/blueprint.webp" alt="blueprint 预览" /></a> <br /><strong><code>blueprint</code> · 工程蓝图</strong> <br /><sub>制图工作台 · 深海军 + 制图青 + 60 px 网格</sub> <br /><sub><b>适合</b> · 技术架构 · 系统拆解 · API / SDK 介绍</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/terminal-green.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/terminal-green.webp" alt="terminal-green 预览" /></a> <br /><strong><code>terminal-green</code> · 终端绿</strong> <br /><sub>80 年代磷光 CRT · 纯等宽 + 扫描线</sub> <br /><sub><b>适合</b> · CLI 工具教程 · 黑客 / 安全话题 · 复古技术致敬</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/neon-cyber.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/neon-cyber.webp" alt="neon-cyber 预览" /></a> <br /><strong><code>neon-cyber</code> · 霓虹赛博</strong> <br /><sub>赛博朋克未来 · 电光青 + 玫红双霓虹</sub> <br /><sub><b>适合</b> · AI / 大模型评测 · web3 / 安全 · 未来主义与赛博朋克</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bold-signal.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bold-signal.webp" alt="bold-signal 预览" /></a> <br /><strong><code>bold-signal</code> · 焦点信号</strong> <br /><sub>Pitch Deck 主舞台 · 暗渐变 + 大橙焦点卡</sub> <br /><sub><b>适合</b> · pitch deck / 路演 · 产品发布 · 大字宣言 / brand keynote</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/creative-voltage.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/creative-voltage.webp" alt="creative-voltage 预览" /></a> <br /><strong><code>creative-voltage</code> · 电压创意</strong> <br /><sub>饱和电光蓝 + 霓虹黄 + halftone 网点</sub> <br /><sub><b>适合</b> · 设计周 / 创意分享 · 工作室作品集 · 字体 / 视觉文化</sub> </td> </tr> </table>
浅色 · 15 套
明亮编辑画布 —— 适合清晰、克制、带纸感温度的内容。
<table> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/paper-press.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/paper-press.webp" alt="paper-press 预览" /></a> <br /><strong><code>paper-press</code> · 亮色印刷</strong> <br /><sub>编辑纸张 · 暖奶油 + 火热橙</sub> <br /><sub><b>适合</b> · 杂志型内容 · 生活方式 · 日常工具评测</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/newsroom.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/newsroom.webp" alt="newsroom 预览" /></a> <br /><strong><code>newsroom</code> · 报社</strong> <br /><sub>NYT 大报 · 新闻纸奶油 + 旗红</sub> <br /><sub><b>适合</b> · 纪录片 / 报道 · 深度评测 · 时事 / 热点解读</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/monochrome-print.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/monochrome-print.webp" alt="monochrome-print 预览" /></a> <br /><strong><code>monochrome-print</code> · 黑白印刷</strong> <br /><sub>精炼克制 · Monocle / Wallpaper 气质</sub> <br /><sub><b>适合</b> · 深度阅读改编 · 学术 / 思想型内容 · 文化艺术评论</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/vintage-editorial.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/vintage-editorial.webp" alt="vintage-editorial 预览" /></a> <br /><strong><code>vintage-editorial</code> · 复古编辑</strong> <br /><sub>俏皮 Fraunces + 几何叠层(圆 / 线 / 点)</sub> <br /><sub><b>适合</b> · 个人见解 / 评论 · 文化随笔 · 设计 / 字体话题</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/sunset-zine.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/sunset-zine.webp" alt="sunset-zine 预览" /></a> <br /><strong><code>sunset-zine</code> · 日落 Zine</strong> <br /><sub>Risograph 拼贴 · 暖桃 + 玫红 + 虚线剪贴</sub> <br /><sub><b>适合</b> · 生活向 vlog · 创意分享 · 小红书 / 抖音风</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/pastel-dream.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/pastel-dream.webp" alt="pastel-dream 预览" /></a> <br /><strong><code>pastel-dream</code> · 柔光梦</strong> <br /><sub>柔粉 + 鼠尾草绿 + 右侧 pill 色条</sub> <br /><sub><b>适合</b> · 产品 onboarding · 友好教学 · 心理 / 健康 / 母婴</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/warm-keynote.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/warm-keynote.webp" alt="warm-keynote 预览" /></a> <br /><strong><code>warm-keynote</code> · 暖色 Keynote</strong> <br /><sub>现代 SaaS Keynote · glass slab + 青绿 + 暖色网格</sub> <br /><sub><b>适合</b> · SaaS keynote · B 端产品发布 · 团队对外汇报</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/electric-studio.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/electric-studio.webp" alt="electric-studio 预览" /></a> <br /><strong><code>electric-studio</code> · 电光企业</strong> <br /><sub>企业级清晰 · 净白 + 贴底电光蓝色条</sub> <br /><sub><b>适合</b> · B2B 产品演讲 · 投资人路演 · 企业财报 / 季度更新</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bauhaus-bold.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/bauhaus-bold.webp" alt="bauhaus-bold 预览" /></a> <br /><strong><code>bauhaus-bold</code> · 包豪斯</strong> <br /><sub>宣言式现代主义 · 0 圆角 + 4 px 厚边</sub> <br /><sub><b>适合</b> · 产品发布 · 观点宣言 · 品牌主张</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/swiss-ikb.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/swiss-ikb.webp" alt="swiss-ikb 预览" /></a> <br /><strong><code>swiss-ikb</code> · 瑞士克莱因蓝</strong> <br /><sub>极细 200 Helvetica + IKB + 1 px 发丝网格</sub> <br /><sub><b>适合</b> · AI / 科技产品发布 · 年度数据汇报 · 信息图</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dune.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/dune.webp" alt="dune 预览" /></a> <br /><strong><code>dune</code> · 沙丘</strong> <br /><sub>炭褐 + 沙底 · 近乎零 accent,建筑画廊感</sub> <br /><sub><b>适合</b> · 建筑 / 室内 / 空间 · 艺术展览 · 高端品牌画册</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/indigo-porcelain.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/indigo-porcelain.webp" alt="indigo-porcelain 预览" /></a> <br /><strong><code>indigo-porcelain</code> · 靛蓝瓷</strong> <br /><sub>靛蓝<em>本身即墨</em>(不是 accent)+ 瓷白</sub> <br /><sub><b>适合</b> · 学术 / 论文解读 · AI / 数据深度 · 严肃技术汇报</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/forest-ink.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/forest-ink.webp" alt="forest-ink 预览" /></a> <br /><strong><code>forest-ink</code> · 森林墨</strong> <br /><sub>森林绿<em>本身即墨</em> + 象牙 · 旧版国家地理</sub> <br /><sub><b>适合</b> · 自然 / 可持续 · 纪录 / 非虚构 · 慢生活</sub> </td> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/kraft-paper.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/kraft-paper.webp" alt="kraft-paper 预览" /></a> <br /><strong><code>kraft-paper</code> · 牛皮纸</strong> <br /><sub>深棕<em>本身即墨</em> + 牛皮米 + 紫铜 accent</sub> <br /><sub><b>适合</b> · 书评 / 文学随笔 · 历史 / 怀旧 · 手工艺 / 食物</sub> </td> </tr> <tr> <td align="center" width="50%"> <a href="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/split-canvas.webp"><img src="https://cdn.jsdelivr.net/gh/ConardLi/assets@main/imgs/web-video/split-canvas.webp" alt="split-canvas 预览" /></a> <br /><strong><code>split-canvas</code> · 双拼画布</strong> <br /><sub>50/50 双底色 · 蜜桃左 + 薰衣草右</sub> <br /><sub><b>适合</b> · 双主题对比 / 辩论 · 故事讲述 · 概念对照科普</sub> </td> <td align="center" width="50%" valign="middle"> <br /> <strong>+ 派生你自己的</strong> <br /><sub>完整 token 契约、每套设计签名、<br />以及怎么派生新主题(Swiss 黄 / 绿 / 橙变体等),<br />见 <a href="./references/THEMES.md">THEMES.md</a>。</sub> <br /><br /> </td> </tr> </table>
---
Reference Map
- PRINCIPLES.md:视频感网页演示的核心原则
- CHAPTER-CRAFT.md:章节实现规则与视觉 checklist
- OUTLINE-FORMAT.md:outline 必须遵循的结构
- SCRIPT-STYLE.md:文章转口播稿规则
- PATTERNS.md:可选视觉 primitive 配方
- AUDIO.md:可选口播音频合成流程(provider-agnostic)
- tts-providers/README.md:TTS provider 三函数契约 + 内置 2 个 (minimax / openai) + ElevenLabs / edge-tts / Azure / Google / macOS say 的现成代码片段
- RECORDING.md:录屏与后期注意事项
音频合成
把每个章节 narrations.ts 里的口播文字按 step 颗粒度合成 mp3, 落到 presentation/public/audio/<chapter-id>/<step-N>.mp3。运行时 Auto 模式会自动按 step 播放并自动推进——录屏可以一镜到底。
真相源:每个章节的 src/chapters/<NN>-<id>/narrations.ts 是 step数 + 口播文本的唯一来源。outline.md 不再参与音频合成,章节代码也不再手写 totalSteps。这一改根除了"网页 step 和音频文件数对不上"这个老问题。
合成器是 provider-agnostic 的:runner 本身不绑定任何 TTS 后端,每个 后端是 scripts/tts-providers/<name>.sh 一个文件。内置 2 个 provider:
| Provider | 默认 | 何时用 |
|---|---|---|
minimax | ✓ | 中文口播首选(用 mmx-cli,要 MiniMax API key) |
openai | —— | 多数 agent 已有 OPENAI_API_KEY;curl-based、响应快 |
换 / 加 provider 见 `scripts/tts-providers/README.md` (脚手架跑完后路径是 presentation/scripts/tts-providers/README.md)。 README 里还附了 5 套可粘贴的现成片段(ElevenLabs / edge-tts / macOS say / Azure / Google Cloud)和写自定义 provider 的三函数契约。
---
文件命名约定
presentation/public/audio/
├── coldopen/
│ ├── 1.mp3
│ ├── 2.mp3
│ └── ...
├── hook/
│ └── ...
└── ...- 章节子目录名 =
chapters.ts里的id - 文件名 =
<step-N>.mp3(1-indexed,对齐 narrations 数组的 index + 1) - 格式默认 mp3。如果你写的 provider 只能出 wav,在函数里加一步
ffmpeg
转 mp3(参见 tts-providers/README.md 的 say.sh 示例)
---
标准流程
1. 抽取 segments
cd presentation
npm run extract-narrations这会扫所有章节的 narrations.ts,按 chapters.ts 注册顺序生成 audio-segments.json:
[
{ "chapter": "coldopen", "step": 1, "text": "...", "audio": "coldopen/1.mp3" },
{ "chapter": "coldopen", "step": 2, "text": "...", "audio": "coldopen/2.mp3" },
...
]让用户先扫一眼这个 json,确认文本和切分都对,再开始烧 token 合成。
空字符串的 narration 会被自动跳过(不烧 TTS token)——运行时 Auto 模式
按字数估时撑过这种"无声过场"step。
2. 选 provider
ls scripts/tts-providers/ # 看本项目带了哪些2.A 用内置 minimax 合成
npm run synthesize-audio # 增量:跳过已存在的 mp3
npm run synthesize-audio -- --force # 全部重合成
npm run synthesize-audio -- --voice=<voice-id> # 指定音色启动时 runner 会先调 provider 的 tts_check:
- mmx 未安装 → 报
mmx CLI not found in PATH,并打印安装说明 - mmx 未登录 → 报
mmx is not authenticated,并提示登录命令
修完再跑。每条段打印进度:
[ 3/24] coldopen/3.mp3 ✓ 4s
[ 4/24] coldopen/4.mp3 skip (exists)合成串行(避免 rate limit),自动跳过已存在文件(断点续合,不烧 重复 token)。
2.B 用内置 openai 合成
export OPENAI_API_KEY=sk-... # 在 platform.openai.com 拿
PRESENTATION_TTS=openai npm run synthesize-audio
# 换音色 + HD 模型
OPENAI_TTS_MODEL=tts-1-hd PRESENTATION_TTS=openai \
npm run synthesize-audio -- --voice=nova可选 env:
| 变量 | 默认 | 作用 |
|---|---|---|
OPENAI_API_KEY | —— 必须 | API key |
OPENAI_BASE_URL | https://api.openai.com/v1 | 切代理 / Azure-OpenAI |
OPENAI_TTS_MODEL | tts-1 | tts-1 快 / tts-1-hd 高质量约 2× 价 |
--voice= / PRESENTATION_TTS_VOICE | alloy | 可选 alloy / echo / fable / onyx / nova / shimmer |
tts_check 会检查 curl / jq / OPENAI_API_KEY 三件套,缺哪个报哪个。
2.C 换 provider / 加自定义 provider
内置之外的常见后端在 scripts/tts-providers/README.md 里有 5 段 可粘贴代码片段(ElevenLabs / edge-tts / macOS say / Azure / Google Cloud)。
挑一个 → 复制 README 里的代码块 → 保存为 scripts/tts-providers/<name>.sh → 设好环境变量 → 切换 provider 跑:
PRESENTATION_TTS=elevenlabs npm run synthesize-audio
# 或
npm run synthesize-audio -- --provider=edge-tts如果用户的 TTS 完全自研,按三函数契约写一个 <name>.sh 即可:
| 函数 | 必需 | 作用 |
|---|---|---|
tts_synthesize <text> <out_path> [<voice>] | ✓ | 把一段文字写成 mp3 到指定路径 |
tts_check | 可选 | 启动时校验环境(CLI / key / auth),未就绪 return 非零 |
tts_install_help | 可选 | tts_check 失败时打印怎么修 |
抄 openai.sh(HTTP-based)或 minimax.sh(CLI-based)起手最快。 详细规范在 scripts/tts-providers/README.md。
2.D 退化路径
如果两个内置 provider 都没就绪(没装 mmx 也没有 OpenAI key)告诉用户:
我可以:
1. 用内置 openai provider(如果你已有 OpenAI key)
export OPENAI_API_KEY=sk-...
PRESENTATION_TTS=openai npm run synthesize-audio
2. 帮你装 MiniMax CLI(默认 provider,中文音色更稳)
npm install -g mmx-cli && mmx auth login --api-key sk-xxxxx
API key 在 https://platform.minimaxi.com 获取
3. 换其它 provider
scripts/tts-providers/README.md 里有 5 种现成代码片段:
• ElevenLabs (要 ELEVENLABS_API_KEY,英文音色最佳)
• edge-tts (免费 / 无 key / pip install edge-tts)
• macOS say (零依赖离线,质量一般,适合预览)
• Azure (要 AZURE_SPEECH_KEY)
• Google (要 gcloud auth)
复制一段保存成 tts-providers/<name>.sh,
再 PRESENTATION_TTS=<name> npm run synthesize-audio
4. 暂时跳过
稿子和 narrations 都在,你自己用任意 TTS 录制即可——文件
按 audio-segments.json 的 audio 字段命名就行。不要假装合成成功。
---
校验时长
合成完后跑:
for f in public/audio/*/*.mp3; do
d=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$f")
echo "$f ${d}s"
done把每条的实际秒数汇总告诉用户。重点关注 ≥ 15s 的条目——口播太长意味 着该 step 的 narration 写得过密,或者 step 没拆够。让用户决定改稿子 重合还是回章节代码拆 step。
---
运行时如何使用合成的音频
合成完成后,不需要任何额外配置——脚手架的 App.tsx 已经接好:
| 模式 | 触发方式 | 行为 |
|---|---|---|
| Manual(默认) | 直接打开页面 | 不播音频,点击 / 方向键推进 |
| Audio(半自动) | URL ?audio=1 或按 M 键 | 进入 step 自动播音频,但你手动推进(点鼠标) |
| Auto(全自动) | URL ?auto=1 或按两次 M 键 | 进入 step 播音频 → 播完自动 next() → 进下个 step → ... |
Auto 模式首次需要按一次 Space 启动(绕过浏览器自动播放限制),之后 全自动跑。录屏时打开屏幕录制 → 按 Space → 整片自动跑完 → stop。
Auto 模式的推进规则就一句话:每段音频播完 + 200ms 缓冲 → 自动 next。
没有"等动画跑完"的兜底——如果你写的视觉动画比口播长,会被当场切。
解决办法:写更长口播 / 拆 step / 调动画速度(详见
`CHAPTER-CRAFT.md` 「代码层最小约束」)。
>
音频文件缺失(还没合成 / 404)或 narration 是空串 → 退化到字数估时
(max(1500ms, 字数 × 250ms)),保证预览也能整片跑通。---
故障排查
通用:
| 现象 | 原因 / 修法 |
|---|---|
chapter id "X" registered but no matching folder found | 章节文件夹应命名为 NN-<id>;id 必须等于 chapters.ts 里注册的 |
narrations.ts in X must export an array named "narrations" | 该章节的 narrations.ts 没 export 名为 narrations 的数组 |
TTS provider 'X' not found | scripts/tts-providers/X.sh 不存在;列出来看哪些可用,或抄 README 加一个 |
provider 'X' does not define tts_synthesize | 你的 <X>.sh 没定义必需的函数。看 README 的契约部分 |
| 中间断了几条没合成 | npm run synthesize-audio 重跑 —— 已存在文件会跳过 |
| 浏览器没播音频 | Auto / Audio 模式下首次需要用户手势——确认你按了 SPACE 启动 Auto,或者点过页面 |
| 音频 404 但 Auto 模式还能跑 | 找不到 mp3 时 useAudioPlayer 退化到字数估时(4 字/秒),保证预览不中断 |
minimax 专属:
| 现象 | 原因 / 修法 |
|---|---|
mmx: command not found | npm install -g mmx-cli;npm 全局 bin 不在 PATH 时 npm config get prefix 看一下 |
mmx is not authenticated | mmx auth login --api-key sk-xxxxx 重新登录 |
| 中文音色不自然 | mmx 默认音色未必最佳;查 mmx speech --help 看 --voice 可选项,传 --voice=<id> |
| 整段合成被截断 | 单段过长(mmx 默认上限约 5000 字符)。在 narrations.ts 里把这条拆成两条(也意味着该 step 应该拆成两个 step) |
openai 专属:
| 现象 | 原因 / 修法 |
|---|---|
OPENAI_API_KEY is not set | export OPENAI_API_KEY=sk-...,或者把它加到 shell rc / .env |
| 全部段 FAILED + key 是对的 | 多半 model / voice 名字错。--voice=alloy 试默认值;OPENAI_TTS_MODEL=tts-1 试默认模型;用 bash -x scripts/synthesize-audio.sh 看请求体 |
| 走代理 / 走 Azure-OpenAI | export OPENAI_BASE_URL=https://your-proxy/v1 |
| HD 太慢 | 改成 OPENAI_TTS_MODEL=tts-1(默认);HD 大约慢 2 倍 |
| 中文音色不像真人 | OpenAI 6 种音色都是英语偏向;中文角色用 minimax 更合适 |
换其它(自定义)provider 之后:
| 现象 | 原因 / 修法 |
|---|---|
<X>_API_KEY not set | 你的 provider 需要 API key,但 env 里没设。export <X>_API_KEY=... 或写到 .env 再 set -a; source .env; set +a |
| 合成的 mp3 浏览器播不了 | 检查 provider 是否真的出了 mp3(不是 wav / opus / aac)。file public/audio/*/*.mp3 看 magic header |
| 一切看起来都对,但全部 FAILED | bash -x scripts/synthesize-audio.sh 看每段实际调了什么 |
---
相关链接
- Provider 契约 + 现成片段:`scripts/tts-providers/README.md`
- mmx-cli 仓库:<https://github.com/MiniMax-AI/cli>
- mmx 官方文档:<https://platform.minimaxi.com/docs/token-plan/minimax-cli>
- mmx 参数 / 音色查询:
mmx speech --help
章节开发指引(每章开发必读)
---
这是视频,不是 PPT
正在做的是视频网页 —— 讲者点击 + 口播 + 录屏发出去给观众看。 判断每一步做对没有,标准非常朴素:
- 不像 PPT —— 观众感觉是在看视频,不是在看翻页幻灯(页面中不得包含页眉页脚,突出主视觉元素)
- 看起来舒服 —— 配色、字体、节奏都让人放松,不得出现大量的纯文字、不得出现字体太小的文字
- 有视觉冲击 —— 画面在演事情,不只是文字堆砌,不得一次性全部罗列所有元素,关键元素随进度逐步推进展现
---
必须用 CSS / SVG / Canvas / JS 大胆绘制视觉演示
这是底线。
>
每一章都至少要有 1~2 处"动起来的图 / 演示元素"。
整章只有纯文字 = 验收不过 = 回去重做。
视频感最强的来源 —— 用户看见了被讲解的东西在屏幕上演给他看:
- 数字在递增 / 横条在生长 / 排名在交换
- 流程节点依次点亮 / 连线自绘
- 对比被一刀切开 / 聚光灯扫过 / 形状在变形
- 粒子聚拢成形 / 噪声背景流动 / 字符雨下落
- 模拟终端交互
- 模拟 AI 对话窗口
- 模拟文件目录树
怎么组合发挥都行 —— 但每章必须用,不允许整章纯文字。
---
逐步揭示,禁止一次全展示
整页内容由全局 `step` 计数器驱动 —— 点击空白处或按 → 键推进 一步。设计每一步时心里要默念:这一步演什么,下一步演什么。
最重要的一条:
当口播在说"第一是 X、第二是 Y、第三是 Z"这种清单 / 列表时,
严禁一个 step 把 X / Y / Z 全部 stagger 上来。
正确做法:
- 一项 = 一个 step
- X 只在它自己的 step 里独自亮起
- 讲到 Y 时,X 灰化保留作上下文 + Y 亮起
- 讲到 Z 时,X / Y 都灰化 + Z 亮起
判断标准:讲者会一个一个念出来吗?会 → 必须逐个揭示。
---
内容取舍:抓重点,不要原文搬运
视频是音 + 画:
- 口播负责把信息线性讲清楚
- 画面负责把节拍重点放大、节奏感拉出来
每个 step 屏幕上只挂这个节拍最值得放大的 1~3 个东西 —— 一个 hero 标语 / 一个数字 / 一组对比 + 必要的视觉演示。
不要试图把原文每个字都搬上去。那是论文阅读,不是视频。
---
双源:节奏跟口播稿,细节回原文章
节奏 / 顺序 / 节拍切分 跟 `script.md` 口播稿 —— 关键顺序不能乱。
画面细节 / 数据 / 引用 / 案例 回 `article.md` 原文章抽。
outline.md 已经在每章首段抽了「信息池」做参考。但实现章节时 也必须回去翻 `article.md` 本章对应段落 —— 那里有比口播稿多得多的 细节(具体数字、引用原话、案例维度、出处时间)。把这些挂到画面上, 让画面信息密度 > 口播信息密度。
如果你只用了口播稿的内容做章节 —— 屏幕等于把口播打字打了一遍
—— 那就是 PPT,不是视频。
>
章节实现一定要回原始文章抽细节,不要嫌麻烦。
---
字体 / 配色 / 动画 / 留白 —— 视频演示基本审美
视频观众离屏幕远、注意力浮动,所以:
- 字号要大 —— hero 文字至少 80px 起,远观也能看清
- 留白要多 —— 舞台四边都要让出大留白,画面不要塞满
- 配色要舒服 —— 颜色和字体家族必须用主题 token(保证换主题不破);
字号 / 间距 / 时长这些章节按内容自由发挥(详见下方「代码层最小约束」)
- 动画要舒服 + 炫酷 —— 出现得干净利落,停下来不抢戏;炫酷靠
设计巧思(内容驱动的演示动画),不靠速度暴力或密集闪烁
---
避免 AI 味
AI 生成的网页有几种共有的"视觉指纹",全部不要:
- 紫粉 / 蓝紫对角渐变背景
- 圆角卡片 + 彩色左边框装饰
- 渐变按钮 + 大圆角药丸
- emoji 当图标用
- 假数据 / 假 logo / 假"X 万用户"
- 整章 N 步用同一种入场动画(全场 fade / 全场 blur)
- 每步都挂 ken burns / 光晕呼吸 / 持续闪烁
- 每屏右下角都挂 mono 角标 / 序号
缺的东西承认缺 —— 用 placeholder 占位卡(一张写着"image · 16:9 描述"的卡片,按真实比例留位)。不要用 emoji 凑、不要找无关图凑、 不要编数字。没有就承认没有,比 fake 强一百倍。
---
框架已经搭好的部分(理解就好,不需重写)
- 16:9 固定舞台:内容设计在 1920×1080 上,外层 transform scale
缩到任何视口,外围 letterbox 留黑 —— 没有响应式断点
- 舞台居中 + 大留白:上下左右四边都让出至少 80px 的安全区
- 隐形进度条:屏幕底部默认完全透明,鼠标悬到底部边缘才出现,
支持点击跳转章节(录屏时摄像头看不到任何 chrome 控件)
- 全局 step 驱动:点击舞台空白处 / 键盘 ←/→ 推进;章节是
step
的纯函数,没有定时器、没有命令式状态
---
代码层最小约束
不能踩的红线,其它怎么写都行:
必须用 token(换主题不破的底线)
- 颜色:
--shell/--surface/--surface-2/--surface-3/
--text / --text-2 / --text-mute / --text-faint / --rule / --accent / --accent-soft / --accent-glow —— 禁硬编码 hex / rgb / 颜色名
- 字体家族:
--font-display-cn/--font-display-en/--font-body
/ --font-mono —— 禁硬编码字体名
- 主题性格签名通过 primitive class 自动接入,**不要在章节 CSS 里
重定义它们**:
.hero-num(hero 数字风格 —— 主题决定衬线 / 等宽 / 粗黑).rule(分割线 —— 主题决定 1px 实线 / 4px 实线 / 2px 虚线).card(卡片 —— 主题决定圆角 + 阴影性格).stage-frame(舞台底色 / 圆角 / 阴影 / 装饰图案 / vignette
全自动,章节什么都不用做)
可硬编码 / 可 token,按内容自由(解锁章节自由设计)
- 字号:想要 80px 就写 80px,想用
var(--t-h1)也行 - 间距 / padding / margin:按画面节奏写具体值
- 动画时长 / 缓动 / keyframe:按动画意图写具体值
(节奏气质参考 theme.json 的 mood —— 慢主题别写 200ms 的快动画)
- 边框宽度 / 非性格圆角 / 字距:随手写
- gap / grid 布局尺寸:按画面构图写
其它工程红线
- 不用
setTimeout/setInterval驱动动画 —— 用 CSS keyframes - 章节内的可交互元素(按钮 / 自定义控件)加
data-no-advance,
否则点了会被舞台误推进 step
- 章节代码物理隔离:每章独立文件夹、独立 CSS 类前缀,不跨章 import
- 每章必须有 `narrations.ts`(与
<Chapter>.tsx同目录): - 数组长度 = 章节代码里
if (step === N)出现的最大 N + 1 - 每个元素 = 一个 string,该 step 要播的口播文本(来自
script.md
对应段,语义一致——可微调标点 / 断句以适配 TTS,但不能漏关键短语)
- 完全无音频的过场 step 用空串
"",Auto 模式会按字数估时撑过 - 这是音频合成 + Auto 模式自动推进的唯一真相源,写错或漏写
会让录屏对不上嘴
- 动画时长必须 ≤ 该 step 的口播时长——Auto 模式严格按音频结束推进,
没有"等动画跑完"的兜底。动画太长 → 三选一:写更长口播 / 拆 step / 调动画速度。详细机制见 `AUDIO.md`
---
完工自检(写完每章强制执行,不可跳过)
⚠️ 硬性流程:章节实现完成后必须走完下面的自检 → 修复 → 汇报
三步。禁止"实现完成 → 直接汇报给用户"。
>
执行方式(按能力降级):
>
1. 优先 Agent Teams:开一个独立的 reviewer agent,传入本章代码路径
+ 本文件 Part「完工自检」清单,让它逐项核查 + 出结论(哪几条
pass / 哪几条 fail + 证据)。
2. 其次 subAgent:当前 agent 没有 Teams 能力但能开 subagent,用 subagent
走同样的流程。
3. 都没有:当前 agent 自己严格逐项核查,不允许目测一遍就放行。
>
拿到自检结论后:先按 fail 项改完代码,然后再向用户汇报"做完
了 + 自检结论 + 改了什么"。直接拿原始结论汇报但不修复 = 违规。
写完一章 + 在浏览器点完一遍后逐项过:
- [ ] 每章至少 1~2 处 CSS / SVG / Canvas / JS 视觉演示 —— 没有 = 回去补
- [ ] 不同 step 的主导动作不一样 —— 全章一种动画 = 回去重做
- [ ] 字号大、留白舒服、配色舒服
- [ ] 清单 / 列表逐个揭示,1 项 = 1 step
- [ ] 画面信息比口播稿多(回了原文章抽细节挂上来)
- [ ] 没有紫粉渐变 / 圆角彩色边框 / emoji / 假数据 / 假 logo
- [ ] 缺的素材用 placeholder,不是 fake
- [ ] 颜色和字体家族全部走 token(无硬编码 hex / 字体名);hero 数字
/ 卡片 / 分割线 / 舞台用 primitive class 接入主题性格 —— 这两条不 达标 = 换主题就破
- [ ] 章节交付时主动告诉用户:"本章还缺这些素材"
- [ ] 禁止出现小号字体,大量纯文字(出现后必须回去改)
- [ ] 禁止出现任何形式的页眉页脚,仅展示关键内容(出现后必须回去改)
- [ ] `npx tsc --noEmit` 通过 —— 不通过禁止汇报"做完了"
- [ ] 章节代码物理隔离:独立 CSS 类前缀(
.cd-/.mg-/ ...),
未跨章 import,未修改 chapters.ts 之外的共享文件
- [ ] `narrations.ts` 存在且
narrations.length=== 章节代码里
if (step === N) 用到的最大 N + 1(不一致 = Auto 模式录屏会错位)
- [ ] 每条 narration 文本与 `script.md` 对应段落语义一致(关键短语 /
数字 / 引用全部保留,可为 TTS 微调标点断句)—— 录屏画外音应当能被 观众听成同一段稿子
- [ ] 每个 step 的视觉动画时长 ≤ 口播时长(口播
字数 ÷ 4≈ 秒数)——
超出会被 Auto 模式当场切断,动画演到一半就跳下一步
任一未过 → 回去改。不要"先放着以后修"。
Outline 节选 · 科技测评类 case
节选:前 2 章(10 step),用来展示 outline 在科技测评题材里的
形状。完整版 7 章 36 步在调用此 Skill 的具体项目里,不进 spec。
主题:midnight-press(电影感慢镜、blur clear、暖橙 accent、scanline;克制有重量。禁砸下 shake / 弹簧 / emoji)
>
总时长:约 6 分 30 秒
---
1. coldopen — 登顶悬念(5 steps · ~30s)
- step 1 (~5s) — 暗场远景粒子云 + 钩子字幕"我刷到一张图,愣了三秒"
· 动画:屏幕从纯黑慢速 fade 到暗暖底(1.5s ease-out)→ 远景光尘粒子云慢漂浮入(1.0s 错峰)→ 字幕 mono 打字机逐字打出(每字 100ms);持续微动:粒子云永不停 brownian 漂移 + 暖橙暗角光晕 6s 周期慢呼吸 + ken burns 缓推 · 手段:CSS background 慢 fade + Canvas 粒子云慢漂 + JS typewriter + filter: drop-shadow 暖橙呼吸 + transform: scale 永动 ken burns
- step 2 (~7s) — 排行榜慢镜景深聚焦 + 主分数 hero 数字 blur clear 浮出
· 动画:截图从 blur(12px) + scale(1.05) 慢速景深聚焦(1.5s ease-out)→ 主分数从 blur(20px) 慢慢锐化(1.2s 错峰 400ms)→ 王冠 SVG 沿数字外圈慢速 mask reveal(1.5s);持续微动:主分数暖橙光晕 5s 呼吸 + ken burns 缓推 · 手段:filter: blur 反向 + transform scale 慢推 + clip-path 沿 path mask reveal + filter: drop-shadow 呼吸 · article 补:主分数(来自 article §1,具体数字)+ 测评窗口("X 月 N 日 ~ Y 日")+ 投票数(mono cue 角标)—— 口播只说"换榜首",画面把"领先多少 / 多少票投出来的"全挂上
- step 3 (~6s) — 第 2 名对比横条 + "+差距分"慢浮锐化
· 动画:第一条从左向右慢速 mask reveal 拉到 100%(1.2s ease-out)→ 第二条同向 mask reveal 但只到 ~70%(错峰 600ms)→ 中间空缺区差距分从 blur(15px) 慢慢锐化进场(错峰);持续微动:差距区暖橙光晕慢呼吸 + accent 横线 8s 缓延展永动 + scanline 极淡 overlay 慢移 · 手段:clip-path inset 慢 reveal + filter: blur 反向 + linear-gradient 暖光晕 + linear-gradient scanline 永动 · article 补:第 2 名具体名字(article §1)+ 差距分(具体数字 vs 模糊"低很多")+ 趋势注释("过去 N 周首次反超")
- step 4 (~6s) — 官方原话 pull-quote 慢镜入场(电影感引文)
· 动画:左右两枚巨大引号 SVG 从 opacity 0 + blur(15px) 慢速锐化进场(1.0s 错峰 200ms,无砸下)→ 引文文字 mono 打字机逐字打出(每字 80ms)→ 落款慢速 blur clear 浮出(0.8s);持续微动:引号暖橙慢光晕呼吸 + 镜头 ken burns 缓推 · 手段:filter: blur 反向锐化 + JS typewriter + transform translateY 慢推 + filter: drop-shadow 呼吸 · article 补:原话直引(来自 article §1,1~2 句)+ 落款来源("— 出处.AI")—— 引文是 article 里口播完全省略的"权威背书"
- step 5 (~5s) — 主持人介绍 + 4 件事预告速览
· 动画:第一行自我介绍 blur clear 慢入场(1.2s ease-out)→ 4 张占位卡分别从 blur(15px) 慢速景深聚焦 stagger 出现(每张 250ms 错峰,每张 1.0s 慢镜),卡内 mono 数字 01/02/03/04 + 关键词;持续微动:每张卡暖橙边线慢光晕呼吸(错峰 400ms)+ 远景粒子永漂 · 手段:filter: blur 反向 + opacity 慢 fade + transform scale 慢推 + filter: drop-shadow 多 instance 错峰呼吸 · article 补:4 件事的关键词(来自 article 章节标题,简化)
口播节选:
我刷到一张图,愣了三秒……今天讲清楚四件事。
---
2. why-strong — 强在哪(5 steps · ~80s)
- step 1 (~6s) — hero"强在哪 · 四个方向" + 4 个 ghost 占位卡
· 动画:hero 字符整体从 blur(20px) + opacity 0 慢速景深聚焦(1.5s ease-out)→ 下方暖橙长横线从中心向两侧慢延展(0.8s)→ 4 张 ghost 卡片同步从 blur 慢镜出现(保持 opacity 0.3 占位状态);持续微动:暖橙下划线 8s 周期慢光晕脉冲 + ghost 卡片暖暗边线慢闪 · 手段:filter: blur 反向 + transform scaleX 慢延展 + opacity 阶梯填充 + filter: drop-shadow 永动呼吸 · article 补:4 个方向各自的关键词(mono cue 标签,"01 X / 02 Y / 03 Z / 04 W")
- step 2 (~16s) — 第 1/4 项填实 + 大图慢镜 takeover
· 动画:卡片 1 从 ghost 状态慢速 mask 填实(0.8s 暖暗底色 + 边线慢光晕亮起)→ 中央 hero 大图从 blur(15px) 慢速景深聚焦(1.5s ease-out)→ mono cue 标签从暗角慢速 blur clear 入场(0.8s)→ 副标打字机逐字打出(每字 80ms);持续微动:暖橙 accent 高亮条永动呼吸 + 大图 ken burns 缓推(0.5% scale 12s 周期)+ scanline 慢移 · 手段:filter: blur 反向 + clip-path 慢 reveal + JS typewriter + transform scale 永动 ken burns + linear-gradient scanline 永动 · article 补:本项的具体表现(article §2 抽 1~2 个数据点 / 案例标签)—— 口播只说"它强在 X",画面挂"具体强到 N% / 跑赢 M / 测评分数 K"
- step 3 (~16s) — 第 2/4 项填实 + 列表/演示
· 动画:卡片 2 慢速 mask 填实(0.8s)→ mono cue 标签 blur clear 慢入场(0.8s)→ 4 行具体细则 typewriter 逐行打出(每行 0.8s 错峰 350ms)→ 每行末尾 mono 光标闪烁后追加暖橙对勾 SVG path stroke 慢绘制;持续微动:mono 光标永闪烁(800ms blink)+ scanline 慢移 · 手段:JS typewriter + opacity blink 光标 + SVG path stroke-dashoffset 慢绘 + linear-gradient scanline overlay 永动 · article 补:4 行细则的具体内容(来自 article §2 第 N 段子列表)—— 口播只说"指令遵循好",画面把 article 列出来的 4 个具体维度"主体放哪 / 背景怎么搭 / ..."逐行打出来
- step 4 (~16s) — 第 3/4 项填实 + before/after 慢镜对照 + 永动 cross-fade
- step 5 (~16s) — 第 4/4 项填实 + 多参数预览 + redacted 注释
口播节选:
实测下来强在四个方向 ……
---
观察:每个 step 的画面都做到"口播说一件事,画面挂多件事"。比如
step 2 口播只是"第一项很强",画面同时呈现:本项关键词 / 大图实例 /
具体数据点 / 副标补充 —— 这是双源原则的具象落地。
Case: 科技测评类(tech review)
一篇 AI / 工具 / 产品实测对比类文章 → 7 章 36 步、6 分 30 秒视频 的真实案例。
## ⚠️ 这是结构示意 / 历史案例,不是抄袭模板
>
这个目录的角色是让 agent 看:"**测评类视频章节怎么切、信息池怎么
抽、章长怎么定"。它包含的具体动画描述(如"慢速 blur clear
1.5s ease-out / 打字机每字 80~100ms")属于历史版本** —— 新版
outline 已经不写动画 / 不写时长(见 `../../OUTLINE-FORMAT.md`)。
新写 outline 时只写"屏幕内容 + 关系名前缀 + 章节级信息池",动画
选型留给章节实现阶段按 `../../CHAPTER-CRAFT.md`
Part 0 五问决定。
>
看这个 case 学的应该是:
1. 测评类怎么切 7 章(钩子 → 优点 → 场景 → 进阶 → 收束)
2. 章长怎么定(每章 4~6 step 防疲劳)
3. 双源原则怎么落地(hero 来自 script / 数据角标来自 article)
>
不应该学:动画选型、CSS 实现、时长数值(这些已下放到 chapter
阶段)。
适用场景
- AI 模型 / 产品 / 工具的实测体验文
- 多家产品对比(A vs B vs C)
- 跑分 / benchmark / 用户投票数据驱动的内容
- "强在哪 / 怎么用 / 怎么用得好"型结构
关键决策
| 维度 | 这个案例的选择 | 通用启发 |
|---|---|---|
| 主题 | midnight-press(电影感慢镜、blur clear、暖橙 accent、scanline) | 科技测评类适合"克制、有重量"的暗色调;避开俏皮 / 糖果色 |
| 章节切分 | 7 章:开场悬念 / 强在哪 / 哪能用 / 怎么用好 / Skill 介绍 / Skill 模式 / 收尾 | 测评类的标准结构:钩子 → 优点 → 场景 → 进阶 → 收束 |
| 章长 | 每章 4~6 step | 测评类信息密度高,每章不超过 6 step 防止观众疲劳 |
| 双源应用 | hero 标语来自 script、画面密度(具体分数 / 投票数 / 时间戳)来自 article | 测评类 article 数据极多 —— 用 mono cue / 角标 / 数据浮层挂出来 |
| 动画风格 | 慢速 blur clear / 打字机 / ken burns 缓推 | midnight-press 暗色印刷气质,章节实现时按主题氛围自由发挥 |
文件
- `outline-snippet.md` —— 前 2 章完整节选(5 + 5 step),
展示双源原则在 outline 里怎么落地
完整 7 章 outline 在调用此 Skill 的具体项目里(gpt-image2-video/outline.md),不放进 Skill 仓库 —— 避免 Skill spec 被某一个项目内容污染。
不在这个 case 出现的情形
测评类通常不需要:
- 慢节奏长镜头(电影感片头 / 旅行 vlog 才需要)
- 手写温暖感(教育 / 亲子 / 食谱才需要)
- 大量插画(设计稿 / 工艺品类才需要)
→ 选别的 case anchor 或自由发挥。
/* ─────────────────────────────────────────────────────────────────
* hook-chapter · 完整章节示例样式
* 默认绑 newsroom 主题。所有视觉属性走语义 token,零硬编码。
* ───────────────────────────────────────────────────────────────── */
.hk-scene {
color: var(--text);
display: flex;
flex-direction: column;
justify-content: center;
}
/* ── kicker ── */
.hk-kicker {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-6);
font-family: var(--font-mono);
font-size: var(--t-cue);
color: var(--accent);
letter-spacing: 0.12em;
text-transform: uppercase;
}
.hk-kicker-line {
width: 64px;
height: 2px;
background: var(--accent);
}
/* ── step 1 三 ghost ── */
.hk-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-5);
align-items: center;
}
.hk-ghost {
aspect-ratio: 16 / 9;
border: var(--rule-w) dashed var(--rule);
border-radius: var(--r-card);
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
padding: var(--space-4);
background: var(--surface);
}
.hk-ghost-num {
font-family: var(--font-mono);
font-size: var(--t-h2);
color: var(--text-mute);
letter-spacing: 0.08em;
}
.hk-ghost-label {
font-family: var(--font-mono);
font-size: var(--t-cue);
color: var(--text-faint);
text-transform: uppercase;
letter-spacing: 0.2em;
align-self: end;
}
/* ── step 2-4 单图独占 ── */
.hk-solo-frame {
width: 78%;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.hk-solo-img-wrap {
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--r-card);
overflow: hidden;
box-shadow: var(--shadow-card);
background: var(--surface-2);
}
.hk-solo-img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.hk-stamp {
position: absolute;
top: var(--space-4);
right: var(--space-4);
padding: var(--space-2) var(--space-3);
border: 3px solid var(--accent);
color: var(--accent);
font-family: var(--font-display-en);
font-size: var(--t-h3);
font-weight: 900;
letter-spacing: 0.1em;
transform: rotate(-8deg);
background: color-mix(in oklch, var(--surface) 60%, transparent);
animation: hk-stamp-drop var(--dur-base) var(--ease-quart) backwards;
animation-delay: 600ms;
}
@keyframes hk-stamp-drop {
0% { transform: rotate(-8deg) scale(2.4); opacity: 0; }
60% { transform: rotate(-8deg) scale(0.92); opacity: 1; }
100% { transform: rotate(-8deg) scale(1); }
}
.hk-solo-meta {
display: flex;
justify-content: space-between;
align-items: baseline;
font-family: var(--font-mono);
font-size: var(--t-cue);
color: var(--text-2);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.hk-solo-label { color: var(--accent); }
.hk-solo-caption {
font-family: var(--font-display-cn);
font-size: var(--t-body);
text-transform: none;
letter-spacing: 0;
color: var(--text);
}
/* ── step 5 takeover ── */
.hk-takeover {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-6);
justify-content: center;
}
.hk-mini-row {
display: flex;
gap: var(--space-3);
}
.hk-mini {
width: 140px;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: calc(var(--r-card) * 0.5);
box-shadow: var(--shadow-card);
animation: hk-mini-shrink var(--dur-base) var(--ease-quart) backwards;
}
@keyframes hk-mini-shrink {
from { transform: scale(2.4); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
.hk-accent-bar {
width: 60%;
height: 4px;
background: var(--accent);
animation: hk-bar-grow 700ms var(--ease-quart) 250ms backwards;
}
@keyframes hk-bar-grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.hk-hero {
margin: 0;
font-family: var(--font-display-en);
font-size: var(--t-display-1);
letter-spacing: -0.025em;
color: var(--text);
line-height: 0.95;
}
/* ── step 6 close + brush ── */
.hk-close {
display: grid;
place-items: center;
}
.hk-quote-wrap {
position: relative;
display: inline-block;
}
.hk-quote {
margin: 0;
font-family: var(--font-display-cn);
font-size: var(--t-display-2);
color: var(--text);
}
.hk-brush {
position: absolute;
left: -4%;
right: -4%;
top: 50%;
height: 0.18em;
background: var(--accent);
transform-origin: left center;
transform: scaleX(0) translateY(-50%);
animation: hk-brush-strike 700ms var(--ease-expo) 500ms forwards;
}
@keyframes hk-brush-strike {
to { transform: scaleX(1) translateY(-50%); }
}
// ⚠️ 这是 anchor 参考代码,不会被任何项目编译。
// 抄到真实项目时(presentation/src/chapters/NN-hook/),
// 把下面两个 import 改成:
// import { MaskReveal } from "../../components/MaskReveal";
// import type { ChapterStepProps } from "../../registry/types";
import { MaskReveal } from "../../../templates/src/components/MaskReveal";
import type { ChapterStepProps } from "../../../templates/src/registry/types";
import "./chapter.css";
/**
* hook-chapter · 完整章节示例
* ─────────────────────────────────────────
* 默认绑 newsroom 主题(serif + 报头红 + 印刷盖章 motion)。
*
* 关键手段:
* - 真素材:<img src="/hook/{name}.png" /> 而不是 placeholder
* - 字号狠对比:hero 用 --t-display-1(≥ 144px)+ 微微负字距
* - 主导动作:mask reveal + 印章砸下(贴 newsroom 印刷气质)
* - takeover:三张图缩入 + 巨字爆出 + accent 红条贯穿
* - 收束:brush 划掉旧概念
*
* 切其它主题时按那个主题的气质自由换"印章砸下 / brush"等效动作,
* 结构和字号节奏保持。
*/
export default function HookChapter({ step }: ChapterStepProps) {
// step 1 — 三张 ghost(精修:加 kicker 引子 + accent 红条)
if (step === 0) {
return (
<div className="hk-scene scene-pad">
<div className="hk-kicker">
<span className="hk-kicker-line" />
<span className="hk-kicker-text">这几天</span>
</div>
<div className="hk-grid" key={step}>
{["01", "02", "03"].map((i, idx) => (
<MaskReveal show key={i} delay={idx * 200} duration={900}>
<div className="hk-ghost">
<span className="hk-ghost-num">{i}</span>
<span className="hk-ghost-label">image</span>
</div>
</MaskReveal>
))}
</div>
</div>
);
}
// step 2-4 — 每张图独占(真素材 + 角章 + 旁白)
// ⚠️ 这是结构示例。具体反例 caption / src 应该来自 outline.md 本章
// article 补字段(双源原则)—— 别照抄下面这些占位字符串。
const reveals: Array<{ src: string; label: string; caption: string }> = [
{
src: "/hook/<asset-1>.png",
label: "01 / 03",
caption: "<反例 1 caption,来自 article §X>",
},
{
src: "/hook/<asset-2>.png",
label: "02 / 03",
caption: "<反例 2 caption>",
},
{
src: "/hook/<asset-3>.png",
label: "03 / 03",
caption: "<反例 3 caption>",
},
];
if (step >= 1 && step <= 3) {
const r = reveals[step - 1];
return (
<div className="hk-scene scene-pad" key={step}>
<div className="hk-solo-frame">
<MaskReveal show duration={1100}>
<div className="hk-solo-img-wrap">
<img className="hk-solo-img" src={r.src} alt={r.caption} />
<div className="hk-stamp">FAKE?</div>
</div>
</MaskReveal>
<MaskReveal show delay={400} duration={900}>
<div className="hk-solo-meta">
<span className="hk-solo-label">{r.label}</span>
<span className="hk-solo-caption">{r.caption}</span>
</div>
</MaskReveal>
</div>
</div>
);
}
// step 5 — takeover:三张缩入 + 巨字爆出 + accent 红条
if (step === 4) {
return (
<div className="hk-scene scene-pad hk-takeover" key={step}>
<div className="hk-mini-row">
{reveals.map((r, idx) => (
<img
key={r.src}
className="hk-mini"
src={r.src}
alt={r.caption}
style={{ animationDelay: `${idx * 80}ms` }}
/>
))}
</div>
<span className="hk-accent-bar" />
<h1 className="hk-hero">
<MaskReveal show duration={1100}>
{/* hero 文案来自 outline 本章 step 5;这里只是占位 */}
<主题大字 takeover>
</MaskReveal>
</h1>
</div>
);
}
// step 6 — 钩子收束:brush 划掉
return (
<div className="hk-scene scene-pad hk-close" key={step}>
<div className="hk-quote-wrap">
<h2 className="hk-quote"><下一句钩子></h2>
<span className="hk-brush" aria-hidden />
</div>
</div>
);
}
Anchor: hook-chapter(钩子型开场)
⚠️ 这是结构示意,不是抄袭模板。先走 `../../CHAPTER-CRAFT.md`
Part 0 五问。本 anchor 给的是"钩子型开场的结构骨架"——你要保留它的
step 切分逻辑、字号关系、布局原则,**按本项目的主题 + 内容换动作
选型**。倒过来照抄 = `../../CHAPTER-CRAFT.md`
Part 5 第 8 条「整章只用一种入场动画」同质化反模式。
定位
视频开头最常用的章节类型:抛 N 张可疑图 / 反例 / 截图 → 引出主题 → 切大字 hero takeover。
适用场景
- 悬念型开头:先甩 3~4 张让人怀疑 / 困惑的图,再揭示原因
- "今天聊聊 X 的几个翻车现场":先看翻车,再切主题
- 产品发布的"问题感"开场:先看痛点截图,再揭示新功能
假设的 outline.md 章节段(抽象)
## 2. hook — <章节标题>(6 steps)
- **step 1** (~4s) — N 张可疑图片占位(虚线 ghost 卡片)
- **step 2** (~5s) — 第 1 张露出:<反例 1 描述>(独占视觉)
- **step 3** (~5s) — 第 2 张露出:<反例 2 描述>(独占视觉)
- **step 4** (~5s) — 第 3 张露出:<反例 3 描述>(独占视觉)
- **step 5** (~4s) — 三张图同时缩入侧栏,中间出 <主题大字> takeover
- **step 6** (~3s) — 切到下一句钩子(被 brush 划掉)关键节奏决策
| step | 节奏意图 | 视觉 |
|---|---|---|
| 1 | 抛悬念 —— N 张未知 | 虚线 ghost 卡片,1/3 屏一张 |
| 2-N | 每张图独占视觉 —— 重点不是"凑数",是让观众盯着每张图想"这是真的吗" | 大图占据 ~70% 屏幕,旁边小字标注图源 |
| N+1 | takeover —— 揭示主题 | 三张缩成左侧迷你卡,中间巨字 |
| 末 | 钩子收束 | brush 划掉旧概念,引下一章 |
为什么 2-N 不能 stagger 同时上
口播会逐个念出来 —— 必须 1 项 = 1 step(CHAPTER-CRAFT.md Part 0 原则 8)。 同时 stagger 上 = 观众扫一眼看完,讲者还在念第一张 = PPT 直觉。
文件结构
hook-chapter/
├── README.md ← 本文件
├── chapter.tsx ← 完整章节示例 —— 默认绑 newsroom 主题
└── chapter.css关键手段(地板线)
| 维度 | 这个 anchor 怎么实现 |
|---|---|
| 素材 | <img src="/hook/<asset>.png" /> 真截图 |
| 字号 | hero = 144px serif (var(--t-display-1)) |
| 主导动作 | brush-stroke + 印章砸下(newsroom 气质) |
| 伴随动作 | accent 红条 scaleX + 副标 stagger 200ms |
| 持续微动 | accent 红条光晕 infinite 呼吸;图片 ken burns 缓推 |
| 卡片样式 | drop-shadow + 微旋转 1deg |
| takeover | 三张图缩入 + hero 巨字爆出 + accent 红条贯穿 |
新写章节时:抄结构和字号关系,按本章内容 + 本主题气质自由
设计动画形式。持续微动按需挂,不强求 —— 详见
`../../CHAPTER-CRAFT.md`「避免 AI 味」一节
关于「每步都挂 ken burns / 持续闪烁」的反模式。
切到其它主题时
bauhaus-bold→ brush 划掉换 hard-cut 大色块;hero 字体换 Archivo Blackterminal-green→ 三张图换"FILE_001/002/003"占位框;hero 用打字机chalk-garden→ 粉笔感虚线 + 慢速 wiggle 入场midnight-press→ blur clear 慢镜入场 + ken burns + scanline;
takeover 改"主标 blur 锐化 + 暖橙光晕呼吸"
结构(N+2 步、独占节奏、takeover、收束)保持不变。
想看具象题材应用
- 科技测评 / 实测对比类视频用这个 anchor 开场长什么样 →
`../case-tech-review/`
/* ─────────────────────────────────────────────────────────────────
* list-reveal · 完整章节示例样式
* 默认绑 newsroom 主题。零硬编码视觉属性。
* ───────────────────────────────────────────────────────────────── */
.lr-scene {
color: var(--text);
display: flex;
flex-direction: column;
gap: var(--space-6);
}
/* ── masthead ── */
.lr-masthead {
display: flex;
align-items: center;
gap: var(--space-3);
}
.lr-rule {
flex: 1;
height: 1px;
background: var(--rule);
}
.lr-kicker {
font-family: var(--font-mono);
font-size: var(--t-cue);
letter-spacing: 0.18em;
color: var(--accent);
text-transform: uppercase;
white-space: nowrap;
}
/* ── intro step ── */
.lr-intro {
justify-content: center;
align-items: stretch;
gap: var(--space-5);
}
.lr-intro-h {
margin: 0;
font-family: var(--font-display-cn);
font-size: var(--t-display-2);
text-align: center;
letter-spacing: -0.01em;
}
.lr-em { color: var(--accent); }
.lr-intro-sub {
font-family: var(--font-body);
font-size: var(--t-h3);
color: var(--text-2);
text-align: center;
margin-bottom: var(--space-5);
}
/* ── grid (3 slots, fixed layout) ── */
.lr-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-5);
}
/* ── slot base ── */
.lr-slot {
border-radius: var(--r-card);
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-3);
min-height: 360px;
transition: opacity var(--dur-base) var(--ease-quart),
filter var(--dur-base) var(--ease-quart),
border-color var(--dur-base) var(--ease-quart);
}
/* ghost: 虚线边 + 大灰序号 */
.lr-slot-ghost {
border: var(--rule-w) dashed var(--rule);
background: transparent;
opacity: 0.55;
}
.lr-slot-ghost .lr-slot-num {
color: var(--text-faint);
}
/* active: 红框 + 实心面板 + 巨号砸下 */
.lr-slot-active {
border: var(--rule-w) solid var(--accent);
background: var(--surface);
box-shadow: var(--shadow-card);
}
.lr-slot-active .lr-slot-num {
color: var(--accent);
animation: lr-num-drop var(--dur-base) var(--ease-quart) backwards;
}
@keyframes lr-num-drop {
0% { transform: translateY(-40%) scale(1.6); opacity: 0; }
60% { transform: translateY(0) scale(0.94); opacity: 1; }
100% { transform: translateY(0) scale(1); }
}
/* past: 灰化 */
.lr-slot-past {
border: var(--rule-w) solid var(--rule);
background: transparent;
opacity: 0.7;
filter: grayscale(0.4);
}
.lr-slot-past .lr-slot-num {
color: var(--text-mute);
}
/* ── slot internals ── */
.lr-slot-num {
font-family: var(--hero-num-font, var(--font-display-en));
font-style: var(--hero-num-style, normal);
font-weight: var(--hero-num-weight, 700);
font-size: calc(var(--t-display-2) * 0.85);
letter-spacing: var(--hero-num-track, -0.02em);
line-height: 1;
}
.lr-slot-content {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.lr-slot-title {
font-family: var(--font-display-cn);
font-size: var(--t-h2);
letter-spacing: -0.005em;
}
.lr-slot-body {
font-family: var(--font-body);
font-size: var(--t-body);
color: var(--text-2);
line-height: 1.6;
max-width: 22ch;
}
// ⚠️ 这是 anchor 参考代码,不会被任何项目编译。
// 抄到真实项目时(presentation/src/chapters/NN-list/),
// 把下面两个 import 改成:
// import { MaskReveal } from "../../components/MaskReveal";
// import type { ChapterStepProps } from "../../registry/types";
import { MaskReveal } from "../../../templates/src/components/MaskReveal";
import type { ChapterStepProps } from "../../../templates/src/registry/types";
import "./chapter.css";
/**
* list-reveal · 完整章节示例
* ─────────────────────────────────────────
* 默认绑 newsroom 主题。
*
* 关键手段:
* - 槽位用 hero-num(serif 巨号)替代普通文字编号
* - 引子用 masthead 双线规则 + serif 大字
* - 槽位状态切换有专属动画:
* ghost → active:mask reveal 标题 + 数字砸下(accent 红)
* active → past :accent 灰化(filter)
* - 关键:所有槽位的 React 节点位置不重排,只切换 className
*/
const ITEMS = [
{ num: "01", title: "文字渲染", body: "图里的文字也能正确写出来" },
{ num: "02", title: "指令遵循", body: "可以给到非常具体的要求" },
{ num: "03", title: "照片真实感", body: "光影 / 材质 / 人物接近真实" },
];
export default function ListRevealChapter({ step }: ChapterStepProps) {
// step 1 — 引子
if (step === 0) {
return (
<div className="lr-scene scene-pad lr-intro">
<header className="lr-masthead">
<span className="lr-rule" />
<span className="lr-kicker">第一部分</span>
<span className="lr-rule" />
</header>
<MaskReveal show duration={1100}>
<h1 className="lr-intro-h">
强在<span className="lr-em">哪</span>
</h1>
</MaskReveal>
<MaskReveal show delay={400} duration={900}>
<div className="lr-intro-sub">三件事 —— 一个个看</div>
</MaskReveal>
<div className="lr-grid">
{ITEMS.map((it) => (
<Slot key={it.num} state="ghost" item={it} />
))}
</div>
</div>
);
}
const activeIdx = step - 1;
return (
<div className="lr-scene scene-pad">
<header className="lr-masthead">
<span className="lr-rule" />
<span className="lr-kicker">第一部分 · 强在哪</span>
<span className="lr-rule" />
</header>
<div className="lr-grid">
{ITEMS.map((it, i) => {
const state =
i < activeIdx ? "past" : i === activeIdx ? "active" : "ghost";
return <Slot key={it.num} state={state} item={it} />;
})}
</div>
</div>
);
}
function Slot({
state,
item,
}: {
state: "ghost" | "active" | "past";
item: { num: string; title: string; body: string };
}) {
return (
<div className={`lr-slot lr-slot-${state}`}>
<div className="lr-slot-num">{item.num}</div>
<div className="lr-slot-content">
{state !== "ghost" && (
<>
<MaskReveal show duration={900} key={`${item.num}-title`}>
<div className="lr-slot-title">{item.title}</div>
</MaskReveal>
{state === "active" && (
<MaskReveal show delay={350} duration={900}>
<div className="lr-slot-body">{item.body}</div>
</MaskReveal>
)}
</>
)}
</div>
</div>
);
}
Anchor: list-reveal(列举型逐个揭示)
⚠️ 这是结构示意,不是抄袭模板。先走 `../../CHAPTER-CRAFT.md`
Part 0 五问。本 anchor 给的是"列举型章节的结构骨架"(单网格 N 槽位 +
每 step 只填一个槽位 + 位置不重排)——保留这个结构,**按本项目的
主题 + 内容换动作选型**。倒过来照抄 = `../../CHAPTER-CRAFT.md`
Part 5 第 8 条「整章只用一种入场动画」同质化反模式。
定位
口播说"三件事 / 四个原因 / N 个特性"时,每项 1 step 逐个揭示。 视频中段最常用的章节类型,最容易翻车成 PPT —— 这是为什么需要 anchor。
适用场景
- "<主体> 强在哪 → 三件事"
- "选购 <X> → 四个角度"
- "为什么我喜欢 <X> → 五个理由"
- 任何"主题 + N 个并列子项"的结构
假设的 outline.md 章节段(抽象)
## 4. <chapter-id> — <主题 N 件事>(N+1 steps)
- **step 1** (~3s) — masthead 引子"<N 件事>"
- **step 2** (~6s) — 第 1 件:<标题> + <article 抽来的细节>
- **step 3** (~6s) — 第 2 件:<标题>
- ...
- **step N+1** (~6s) — 第 N 件:<标题>关键节奏决策
| step | 视觉布局 |
|---|---|
| 1 | 中心引子大字 + 序号 01/02/.../N 占位(不显示内容,纯占位) |
| 2 | "01" 槽位填充:标题 + 简短说明 + accent 编号;其余仍是 ghost |
| 3 | "02" 填充;01 已激活变次级;其余仍 ghost |
| ... | 当前槽位填充;之前的激活降级;之后的 ghost |
CHAPTER-CRAFT.md Part 0 原则 8 的核心实现
"布局不重排,只是单元格内容变化"
整个章节只有一个网格布局,N 个槽位的 React 节点位置完全不变。 变的只是每个槽位的内容状态(ghost / active / past)。这样:
- 单元格不会重排 → 视觉稳
- 每点一次只有"一个槽位变化" → 观众视线明确锁定新揭示的项
反模式:每点一次重新渲染整个布局 → 已揭示的项也跟着抖动 / 重新 入场 → 观众不知道该看哪。
文件结构
list-reveal/
├── README.md
├── chapter.tsx ← 完整章节示例 —— 默认绑 newsroom 主题
└── chapter.css关键手段(地板线)
| 维度 | 这个 anchor 怎么实现 |
|---|---|
| 字号 | 标题 64px / 巨号 144px serif |
| 槽位状态 | dashed → 巨号红色高亮 → 灰化数字(位置不重排) |
| 序号 | hero-num 字体(衬线大数字) |
| 主导动作 | mask reveal(标题)+ 数字砸下(accent 红) |
| 伴随动作 | 副标 stagger 200ms + accent 横线 scaleX |
| 持续微动 | active 槽位的数字 accent 光晕 infinite 呼吸 |
| 引子 | masthead 双线规则 + serif 大字 |
新写章节时:抄结构(单网格 N 槽位、每 step 只填一个槽位、位置
不重排),按本章内容 + 本主题气质自由设计主导动作的形式。
切到其它主题时
bauhaus-bold→ 序号换 Archivo Black + 大色块;用 hard-cut 砸下terminal-green→ 序号[01][02][03]风格;打字机入场chalk-garden→ 粉笔下划线手绘 + wiggle 入场midnight-press→ 数字 blur clear 慢锐化 + 暖橙光晕慢呼吸
结构不变:N+1 step、单网格 N 槽位、每 step 只填一个槽位。
想看具象题材应用
- 科技测评 / 实测对比类视频用这个 anchor 长什么样 →
`../case-tech-review/outline-snippet.md` 里 ## 2. why-strong 章节
EXAMPLES —— 完整章节 / 题材 anchor
## ⚠️ 这是结构示意,不是抄袭模板
>
这些 example 不是给你照抄的。它们的角色是"看一个完整章节大概
什么形状、动画怎么分层、CSS 用了哪些 token、outline 长什么样"。
>
正确使用流程:
>
1. 走完 `../CHAPTER-CRAFT.md` Part 0 五问
2. 实在卡壳"我这一章的整体结构应该是什么"才翻 EXAMPLES
3. 保留它的"形"(step 切分逻辑、字号关系、布局原则),**按本
项目的主题 + 内容换动作选型**
>
倒过来——先翻 EXAMPLES 选一个照搬到底 = `../CHAPTER-CRAFT.md`
Part 5 第 8 条「整章只用一种入场动画」同质化反模式(每个用户的视频
看起来像同一个模板的 N 个变奏)。
两类参考资源,让 agent 在写章节时有具体形状可参考,不用从零设计。
不是必须按这个写。卡壳时翻一翻;用力发挥时大胆偏离。
目录
A. 章节结构 anchor(与题材无关)
| 例子 | 适用场景 | 文件 |
|---|---|---|
| `hook-chapter/` | 钩子型开场 —— 多张图片逐张揭示后 hero takeover | chapter.tsx + chapter.css |
| `list-reveal/` | 列举型 —— 口播说"三件事 / N 个特性",每项 1 step | chapter.tsx + chapter.css |
每个 example 都是完整章节:内容驱动主导动作 + 必要的伴随动作 (不强求挂持续微动,按 `../CHAPTER-CRAFT.md` Part 0 原则 7 节制使用)、真素材(不是占位卡)、字号狠对比、绑了 newsroom 主题作为示范。
B. 题材 case anchor(与题材相关)
| 例子 | 题材 | 文件 |
|---|---|---|
| `case-tech-review/` | 科技测评 / 实测对比 / 跑分类视频 | README + outline 节选 |
题材 case 展示真实 outline 的样子(含 article 补字段如何填、
章节切分如何决策)。拿到与某个 case 题材相似的需求时,先翻它再
写自己的 outline。
怎么用
写章节卡壳时
1. 看哪个 anchor 跟你这一章结构最像(钩子型 vs 列举型 vs 其它) 2. 翻 README.md 看这个例子的设计思路 + 节奏 3. 翻 chapter.tsx 看实现:JSX 结构、step 切分、用了哪些组件 / 类名 4. 翻 chapter.css 看动画用了哪些 keyframes、token、infinite 持续 微动写在哪 5. 写自己这一章时保留 anchor 的"形",按本章内容 + 本主题气质换动画选型
切换主题时
每个 example 的 README 末尾有"切到其它主题怎么换"的提示 —— 通常只需要 换主导动作的形式(newsroom 印章砸下 → terminal 打字机 → chalk 粉笔自绘),结构、step 切分、字号关系不动。
---
⚠️ 这两个 anchor 是"地板",不是"天花板"
这两个例子已经引入印章砸下、stagger、accent 红条 —— 但仍然是相对克 制的版本。鼓励你做得更狂、更"视频感":
进阶玩法(任选搭配)
| 维度 | 这俩 anchor 给的(地板) | 可以升级到(无上限) |
|---|---|---|
| 背景层 | 纯色 surface | + SVG turbulence filter 纸纹永不停斜向漂移 |
| 主导动作 | mask reveal + 印章砸下 | + Canvas 粒子从屏幕外汇聚成 hero 字 |
| 伴随动作 | accent 红条 scaleX | + SVG path stroke-dashoffset 自绘下划线 / 装饰花纹 |
| 持续微动 | accent 光晕呼吸 | + 多层粒子漂移 / scanline / ken burns 缓推 |
| 数字 hero | 直接显示 | + JS 数字滚动(requestAnimationFrame + easeOutQuart) |
| 流程 / 架构 | 仅文字列 | + SVG path 自绘流程图(每条线 stroke-dashoffset 错峰) |
| 对比图 | 两段文字 | + SVG 双柱图自绘 + 差值数字滚动 |
| 转场 | 章节边界硬切 | + clip-path inset 横向擦除转场 |
→ 详细工具箱见 `../CHAPTER-CRAFT.md` Part 2 "视觉手段全栈工具箱"(CSS / SVG / Canvas / JS 四层)。
实测原则
写章节时,先实现 anchor 同等的地板版本(按 `../CHAPTER-CRAFT.md` Part 0 五问选好主导动作),跑起来确认气质对,再决定要不要加伴随 动作 / 持续微动。
判断标准:
- 如果不同 step 的主导动作够多样(PPT 警报通过 `../CHAPTER-CRAFT.md`
Part 0 原则 7 自检)= 不需要再加持续微动
- 如果整章主导动作太单一 = 不要靠"加持续微动"补救,**回 `../CHAPTER-CRAFT.md`
Part 1 五问换主导动作**才是正解(参 Part 5 第 8 条「整章只用一种入场动画」)
不在 EXAMPLES 里出现的章节类型
- 数字型 hero("+47%" → "几乎快了一倍")
- 对比型(前后对照 / 双柱图)
- 链接卡片收尾
这些场景的视觉原语已经在 `../CHAPTER-CRAFT.md` Part 3 视觉工具箱(CSS / SVG / Canvas / JS 全栈)里覆盖了;按 anchor 的"形"组合即可。
outline.md 格式 spec
视频章节规划的产出文件。用户可以直接编辑,所以格式必须人类友好 (用 markdown 不用 JSON / YAML)。
!重要:阅读此文件后必须继续阅读 `CHAPTER-CRAFT.md` 的全部内容,了解对网页效果的真实需求,然后再开始编写 outline
## ⚠️ outline 是开发计划,不是视觉规划
>
outline 只规划节奏 + 内容 + 信息密度:
>
- 章节切分 / 每章 step 数 / 每步估时
- 每步屏幕内容(hero / 标语 / 数据 / 列表项)
- 章节级信息池(从 article 抽的数字 / 引用 / 案例 / 标签)
>
outline 里的 step 数是初始预估。最终 step 数以章节实现时的
narrations.ts 为准——后者既是 step 数源,也是音频合成源(详见 `CHAPTER-CRAFT.md` 「代码层最小约束」+
`AUDIO.md`)。如果实现时章节 step 数和 outline 不一致,
回过来同步 outline 即可,不需要纠结"对得严丝合缝"。
写 outline 前必读(双源原则,CHAPTER-CRAFT.md Part 0 原则 10):
>
- `script.md` —— 决定节拍:按 --- 切节拍,每节拍 1~2 step、估时- `article.md`(如有)—— 决定画面信息密度:每章首段抽信息池
---
抽象示例(看格式)
````markdown
Video Outline
主题:<theme-id>(Checkpoint Plan 已选定)—— <一句话风格描述>总时长:约 <T> 分 <S> 秒(口播 ~<X> 字 ÷ 4 字/秒)
章节数:<N> 章 / <M> 步
---
1. <chapter-id> — <章节标题>(<S> steps · ~<T>s)
信息池(chapter agent 按需挂角标 / 副标 / pull-quote / mono cue):
- <类型:数字 / 引用 / 出处 / 案例 / 词义 / 时间 / 对比 / ...>:<内容> —— <来源 article §X / Lxx>
- ...
开发计划:
- step 1 (~Ts) — <屏幕内容>
- ...
口播节选:
<1~3 句节选,对应到 script.md 完整文本>
---
2. <chapter-id> — ...
````
关于时长:outline 里只写 step 的 (~Ts) 口播估时(音画对齐用),绝对不写动画时长 / 错峰量 / keyframe 数值。这些都在章节开发
阶段决定(`CHAPTER-CRAFT.md` Part 3 时长参考)。
想看具象示例:
- 钩子型开场结构 → `EXAMPLES/hook-chapter/`
- 列举型章节结构 → `EXAMPLES/list-reveal/`
- 科技测评类(实测 / 对比 / 跑分) → `EXAMPLES/case-tech-review/`
---
字段约定
顶部 metadata block
用引用块(>)形式,方便扫一眼整体规模:
| 字段 | 必填 | 说明 |
|---|---|---|
| 主题 | ✓ | Checkpoint Plan 必须已选定。chapter agent 实现时按主题颜色 / 字体 token 走,动画 / 节奏 / 视觉演示由章节自由发挥 |
| 总时长 | ✓ | 估算口播时长(中文 ~ 250 字 / 分钟) |
| 章节数 | ✓ | N 章 / M 步 |
章节标题:## N. <id> — <title>(<S> steps · ~<T>s)
| 部分 | 规则 |
|---|---|
N | 1-indexed 顺序,对齐 chapters.ts 的注册顺序 |
<id> | 小写 + 连字符。会成为 React key / 文件夹名 (src/chapters/0N-<id>/) / 音频子目录 (public/audio/<id>/) |
<title> | 给人看的中文标题。不会进 React 代码 |
<S> steps | 该章 step 总数 |
~<T>s | 该章口播总估时(中文 ~ 4 字/秒) |
合法 id:coldopen、hook、why-good、why-good-text-render。 不合法:why_good(用连字符)、Hook(小写)、第一章(拉丁字符)。
章节首段「信息池」(双源原则核心落地)
每章独立列出从 article.md 抽的细节集合,让 chapter agent 实现每步 画面时按需取用——可能挂成右下角 mono 角标 / 副标小字 / pull-quote 引用 / 数据浮层。
信息池条目格式
- <类型>:<具体内容> —— <来源 article §X / Lxx 或简注>没 article(用户直接给 script):信息池退化为"主动设计画面信息
密度"——靠数字 / 对比 / 元数据等让画面比口播信息密。可以列"画面
装饰元素池"而非"article 抽取池"。
Step 列表:每步 1 行
- step N (~Ts) — <屏幕内容>| 规则 | 原因 |
|---|---|
step N 1-indexed | agent 实现时 if (step === N - 1) ...(注意零基偏移) |
| `(~Ts)` 必填 | 按 script.md 本步对应口播段字数 ÷ 4 估算(中文 ~ 4 字/秒)。范围 3~10s |
| 屏幕内容 | 一句话讲清楚这一步舞台上有什么:hero / 标语 / 数据 / 装饰元素。≤ 1 行,再多就该拆 step |
| 不写动画 | 写死 = 翻译机化(详见本文件顶部框) |
| 不写时长数值 / 错峰量 | 这些在章节开发阶段决定 |
| 不写实现手段 | filter / SVG / Canvas 选型留给 chapter agent |
口播节选(每章末尾,可选但推荐)
精炼 1~3 句,不是完整稿子,仅供章节规划阶段对照"这章在讲什么"。 完整文本回 script.md。outline.md 章节 = script.md 中两个明显 主题切换之间的段落。
音频合成(`AUDIO.md`)会回到 `script.md` 切分完整
文本,不用 outline 节选。
---
命名规则速查
| 对象 | 规则 | 示例 |
|---|---|---|
| 章节 id | 小写 + 连字符 | coldopen, why-good |
| 章节文件夹 | 0N-<id> | src/chapters/01-coldopen/ |
| 章节组件 | PascalCase | Coldopen.tsx, WhyGood.tsx |
| 章节 CSS 类前缀 | 章节缩写(避免跨章冲突) | .cd- / .wg- / .mg- |
| 音频子目录 | <id>/ | public/audio/coldopen/ |
| 音频文件 | <step-N>.mp3 (1-indexed) | public/audio/coldopen/1.mp3 |
---
章节切分的经验法则
- 每章 3~8 步。少于 3 步太薄;多于 8 步观众会忘记这章在讲啥
- 总时长 ÷ 30 秒 ≈ 章节数(一章约 30~60 秒讲完)
- 每章 = 一个聚焦主题。"为什么强 + 怎么用" 是两章,不是一章
- 章节边界 = 口播稿里讲者会换语气 / 换主题的位置。读
script.md
时哪里你下意识想"咳一声接下一段",那里就是章节边界
- 慢节奏 / 长镜头风主题(midnight-press / 电影感片头)每章可少到
2~3 step;信息密集型(科技测评 / 对比表)每章可放宽到 8~10 step
---
素材清单(outline.md 末尾)
## 素材清单
### 1. coldopen
- ✓ <资源 1 描述> (<已就位路径>)
- ⚠️ <资源 2 描述>(待提供)
- ⚠️ <资源 3 描述>(待提供)
---
## 自检(写完 outline **强制**执行,不可跳过)
> ⚠️ **硬性流程**:outline 写完后**必须**走自检 → 修改 → 提交 三步。
> **禁止**写完直接进入 Checkpoint Plan 让用户对齐。
>
> **执行方式**(按能力降级):
>
> 1. **优先 Agent Teams**:开一个独立 reviewer agent,传入 `outline.md`
> + 本节自检清单 + `script.md` / `article.md` 路径,让它**逐项核查 +
> 出结论**(哪几条 fail + 证据)。
> 2. **其次 subAgent**:当前 agent 没 Teams 但能开 subagent,用 subagent
> 走同样流程。
> 3. **都没有**:自己**严格逐项**核查。
>
> 拿到结论后**先按 fail 项改 outline,再进入 Checkpoint Plan**。
- [ ] 每个 step 都是**单一句屏幕内容描述**,没有"动画"行 / "手段"行
- [ ] 没有任何 step 写了具体毫秒 / 秒数(除 `(~Ts)` 口播估时)
- [ ] 每章首段都有「信息池」block,至少 3 条 article 抽取项,**每条
必带来源标注**(`—— 来源 article §X / Lxx`)—— 没标注 chapter agent
回不到原文
- [ ] **所有 step `(~Ts)` 累加 ≈ 顶部声明的总时长**(误差 < 10%)—— 不
一致说明节奏规划失真
- [ ] 章节切分符合"每章 3~8 步 / 30~60s 一聚焦主题"经验
- [ ] 末尾「素材清单」分章节列出,✓ / ⚠️ 标注清楚
- [ ] 脚本不得包含标题、序号等非口播内容,仅包含人类正常可读的内容
写完看一眼:**outline 是不是干净到 chapter agent 看了能立刻开工 + 还有
设计空间**?是 = 合格。如果你看了都觉得"太空,agent 不知道动画选什么"
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Presentation</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
# ────────────────────────────────────────────────────────────────────
# MiniMax provider — uses the official mmx-cli.
#
# Docs: https://platform.minimaxi.com/docs/token-plan/minimax-cli
# Repo: https://github.com/MiniMax-AI/cli
#
# Strengths: Chinese narration quality is consistently good; lots of
# voice options; one-line CLI call.
# ────────────────────────────────────────────────────────────────────
tts_check() {
if ! command -v mmx >/dev/null; then
echo "✗ mmx CLI not found in PATH." >&2
return 1
fi
if ! mmx auth status >/dev/null 2>&1; then
echo "✗ mmx is not authenticated." >&2
return 1
fi
}
tts_install_help() {
cat <<'EOF' >&2
To use the MiniMax provider:
Install: npm install -g mmx-cli
Login: mmx auth login --api-key sk-xxxxx
(get a key at https://platform.minimaxi.com)
Or pick another provider: PRESENTATION_TTS=<name> npm run synthesize-audio
See tts-providers/README.md for the list and how to add your own.
EOF
}
tts_synthesize() {
local text="$1"
local out="$2"
local voice="${3:-}"
# Branch instead of using an empty array — runner uses `set -u`, and
# macOS-default bash 3.2 fires "unbound variable" on "${arr[@]}" when
# arr is empty. The two-branch form is portable to old bash.
if [[ -n "$voice" ]]; then
mmx speech synthesize --voice "$voice" --text "$text" --out "$out" \
>/dev/null 2>&1
else
mmx speech synthesize --text "$text" --out "$out" \
>/dev/null 2>&1
fi
}
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: 5174,
fs: { allow: [".."] },
},
});