--- name: video-talkcraft description: 终极口播视频 skill:中文口播稿 + 成品配音 → CPU 字级时间戳 → SHOTBOOK 层矩阵分镜 → Remotion 电影感成片(横屏默认/竖屏)。当用户要"做口播视频"、"解说/科普视频"、"把文案变成视频"、"给配音配画面动效"时使用。默认使用成品配音,可选 Fish Audio 从稿子合成配音与时间戳;数字人生成技术不在本 skill 内(人物素材是输入)。含统一视觉语言(Apple 范式)、108 张动效配方卡、镜头三面分层工作单、七层镜头反PPT系统(极缓推拉相机/让位,运动做减法)、六式运动承接转场(每式一卡)、长镜头世界画布、anime.js+three.js 桥、自动静止检测 + 独立 subagent 评估循环。 --- # video-talkcraft — 口播视频 skill 三大来源合体:**管线**(配音→字级时间戳→Remotion)+ **词汇**(108 张动效配方卡,全配可播 demo + 自包含 tsx)+ **镜头**(七层模型反 PPT 系统)+ **视觉语言**(Apple 范式默认版)。 核心范式:解说词驱动画面,每句都要有**活的画面响应**(相机极缓推拉/已有元素的变化), 但**新元素只在语义拍边界进场,禁止机械的"一句一个新元素"**(一句一元素是堆积型凌乱的制度根源; 分镜按语义段落切,排版预算见 cinematography.md §4.5); **一个节拍只有一个主角,说完就让位**;字幕句边界 = 全片时间锚点。 ## 流程 ``` ① 文案 → ② 配音输入+时间戳(本机CPU) → ③ 素材 → ④ SHOTBOOK 层矩阵 → ⑤ 实现(全局系统 → 首镜先做先确认 → 其余镜头) → ⑥ 渲染 → ⑦ 三重验收(机器闸全过 + 1 轮审片修 P0/P1)→ ⑧ 交付(可选续审 ≤3 轮) ``` ## ⓪ 开工体检:统一依赖 + 画幅与视觉语言 **依赖只有一份**:所有口播工程的 `remotion/node_modules` 与 `workbench/node_modules` 都是指向 `/runtime/node_modules` 的软链,版本只在 `runtime/package.json` 里钉(`@remotion/*` 全家同号)。**新片开工第一件事**(用户 2026-09-15 定版:每次新做视频都要检查更新): ```bash bash /runtime/check-runtime.sh --upgrade # 装好共享依赖 + 共享无头浏览器 + 链好 workbench;Remotion 有新版就全家升级并冒烟渲 1 帧,不过自动回滚 ``` 末行 `[runtime] OK` 才往下走;升了级要把 `runtime/package.json` / `package-lock.json` 的改动提交。**制作中途不升级**(母版段缓存不认依赖版本,混版本渲出的段不一致)。 工程目录里**永远不跑 `npm install`**(会写进共享目录);要加包 → 加进 `runtime/package.json`,`npm ci`,再提交。 ### 画幅与视觉语言(开工先定,全流程引用) - **画幅默认横屏 1920×1080**(用户偏好);明确要发抖音/竖屏渠道才用 1080×1920 - 视觉语言:**用户明确点了风格就按用户的来**(整套 token 替换);没点时**先从口播稿判定领域、派生本片风格档** (`references/design-language.md` §0:读全稿答"讲什么 / 对谁讲 / 什么口吻"→ 领域 → 风格档表给底色策略 / accent / 字体气质 / 材质与卡造型 / 图表语言 / 素材气质 / 能量档, 写进 SHOTBOOK §0 `G0 风格档`),骨架仍是 Apple 范式(一个强调色/一个投影/底色交替分幕/两档字重/ 默认幕底:浅 `pastel-mesh-flow` / 深 `mesh-flow-dark`——12 款幕底见 §1.1,代码 `template/motion-systems/backdrop.tsx`), 派生本片 token 落成 `theme.ts`。对任何风格都成立的只有一条:禁止逐场景随手取色。 **卡是中性 UI,进片必须蒙皮**:动效卡的 demo / tsx 是无风格的中性呈现,复制进工程后按风格档改皮(颜色 / 字体 / 圆角 / 材质 / 图表坐标轴与标记 / 占位图形)、 不改运动命门(时序 / 缓动 / 几何比例 / 层级),契约与反例见 design-language §0.4;SHOTBOOK 每镜写**蒙皮行** ## ① 口播稿 - 每句一个信息点,钩子在第一句(数字/冲突);13 句 ≈ 95s - **数字一律汉字**(时间戳按文本逐字锚定,`197747` 无法与"十九万七千"的读音对位);英文品牌词直接写(中英混合对齐已验证) - 先调研核实事实,列"事实红线清单"(不可说错的数字/未验证数据不引用) ## ② 配音输入 → 预剪 → 字级时间戳(默认本机 CPU;可选 Fish Audio) **默认使用成品配音**:真人录音或任何 TTS 皆可,输入 = 一条完整配音(wav/mp3)+ 与之逐字一致的口播稿。 没有录音且用户选择合成时,可使用下面方案 B;它不会替换已有配音或默认的本机 CPU 流程。 **②-0 配音预剪(真人录音必做;TTS 只会压气口,可跳过)**——剪掉口水词(嗯 / 呃 / 那个…)、结巴重说、过长停顿, **在做时间戳之前**跑(时间戳做完再动音频 = timing / beats 全体错位;预剪 → 时间戳 → 一切后续,顺序不可换): ```bash python3 scripts/voice_trim.py audio/raw.wav script.json --dry-run --words-out audio/asr_words.json # 先只出报告:逐条 [filler / repeat / pause] 起止 + 共剪多少;ASR 结果落盘 python3 scripts/voice_trim.py audio/raw.wav script.json --words audio/asr_words.json --out audio/full.wav \ [--video dh/host_raw.mp4 --video-out dh/host.mp4] # 用户认可后落盘(吃回上一步的词表,不再跑一次 ASR);同一条录音的人物视频用同一 EDL 剪(音画逐帧同长) ``` - **稿子是真值,宁漏勿错**:只剪 ASR 里**稿子没有的插入段**——全由口水词表拼成的(filler)、等于紧邻稿文的结巴 / 重说(repeat); ASR 听错稿子里的字永不剪(剪它就剪掉了那个字的真实发音);稿外改了措辞的重说只报告,`--cut-unmatched` 才剪。 词表来源缺省跑 FireRed(与下面时间戳同一后端),也吃剪映等导出的**逐字** SRT(`--srt`)或词级 JSON(`--words`); 句级 SRT 定位不到口水词,脚本会警告。无稿时只剪**整 token 文字恰为**保守词表(嗯 / 呃 / 额 / um…)的口水词——按规范化文字精确比对、不认同音(五≠唔、饿≠呃); 紧邻重复的短词无稿只报告不剪(逐字 token 下分不清叠词与结巴)。剪同录的透明 webm 时会解一帧核 alpha,帧数对但透明丢了也算 FAIL。 - 停顿:≥0.7s 的静音压到 0.35s(留段内最安静的一窗真实房间音,不补数字零),首尾留白 0.25 / 0.5s; 切点先找能量谷、再吸附 30fps 帧网格;`cuts.json` 是 EDL(含源 → 新时间轴映射表,剪人物视频与后续任何对账都吃它)。 - **dry-run 报告必须给用户过目再落盘**:标了「切点未落在静音里,听一下」的条目让用户听那一处;脚本只删不合成,不改语速不改音高。 剪完人物视频后跑 ③ 的 `preflight.py --media-only` 核时长——同一 EDL 只对同一条录音成立。 字级时间戳(对**预剪后**的 `audio/full.wav`): ```bash # 方案 A(已有录音,本机 CPU 对齐): pip install zhconv pypinyin sherpa-onnx soundfile numpy # 默认后端 FireRedASR2-CTC int8 的全部依赖 # 首次:下载模型 767MB(model.int8.onnx + tokens.txt)放 ~/.cache/koubo/<模型名>/,地址见脚本头注释 python3 scripts/timestamps_cpu.py audio/full.wav script.json audio/timestamps.json # 备选(免手动下模型):pip install faster-whisper 后加 --backend whisper(首跑自动下载 460MB) python3 scripts/make_timing.py audio/timestamps.json remotion/src/timing.json # 方案 B(可选生成配音 + 时间戳,默认请求 s2.1-pro-free;可用性以 Fish Audio 为准): pip install requests python-dotenv # 还需 ffmpeg,生成前会检查 # 在 .env 配置 FISH_AUDIO_API_KEY=xxx 及可选 FISH_AUDIO_REFERENCE_ID=xxx(见 .env.example) python3 scripts/tts_fishaudio.py script.json audio/full.wav audio/timestamps.json --timing-out remotion/src/timing.json ``` - tts_fishaudio.py:默认 `--mode sentence` 每句一次请求,插入 `--pause-sec` 指定的真实静音(默认 0.25 秒);`--mode stream` 整稿一次请求、保留自然停顿。两者均接收 UTF-8 SSE,生成结束才写文件,不提供流式播放。先解码 PCM 再拼接,偏移按采样数计算。空音频、缺失/无效对齐或文本不匹配时报错;只忽略标点、大小写与字符宽度,不自动转换数字读法。中文用接口字级锚点,英文词内插值;`match/ok` 仅确认文本映射,仍需试听。合成后若再预剪,须重新运行 CPU 对齐。 - timestamps_cpu.py:ASR 词级时间戳 → 与口播稿字符级对齐(**CJK 是可靠锚点**, 匹配键=繁简归一+无声调拼音,同音字不算错;拉丁词各家 ASR 都常拼错,在锚点间插值)→ 每句 match 质检,<0.90 标出人工听核。默认后端 FireRedASR2-CTC(尾部最稳、零误报;整段喂入 ~200s 崩、内存平方涨,脚本默认按静音切 ≤75s 段再加回偏移,`--chunk-sec` 可调),备选 faster-whisper; 各后端横评数据与模型下载地址见脚本头注释。 - timestamps.json schema:`{sr, total, sentences:[{i,text,start,end,match,ok,words:[{text,start,end}]}]}` ——words 为 CJK 逐字 + 拉丁整段 token(标点跳过);满足此 schema 的任何对齐工具都可替换。 - make_timing.py:转成 timing.json(chars 与文本逐字符 1:1,标点零时长),供 `tSay/msSay` 锚点查询 - 配音自查(耳听):无爆音/截断/误读;句间留 ~0.3s 气口,时间锚点更稳 ### ②-1 语义标注(2026-09-22 起必做,正主 `references/semantic-annotation.md`) 口播稿逐句标「这句在做什么」,落成工程根 `semantics.json`——**这是选卡的需求侧产物**,没有它,选卡只剩输入类型一道过滤、剩下几十张卡靠印象挑(2026-09-21 那支片的「我是万里」就这样拿到了全库能量最低的文字卡,库里 P0 的人名条没人查到)。 ```bash python3 /scripts/semantic_annotate.py --init # 从 timestamps.json 出骨架(带词法提示 hint) # 逐句填 sem(26 词封闭词表,见 taxonomy.md「语义索引」)/ weight(main 主句 = 允许进新元素、sub 陪衬句)/ entities / need python3 /scripts/semantic_annotate.py --stats # 校验:与时间戳逐字一致 · 词表封闭 · 词法硬规(我是X→自我介绍、点赞订阅→号召、数量词→数据)· 每镜至少一个 main ``` 标注是**派生物**:预剪 → 时间戳 → 标注 → 其后一切;改稿或重剪后 `text` 对不上就是标注失效,重新 `--init`。 **②-1 早于分镜,所以 `shot` 字段此时是空的**(SHOTBOOK 还没写):④ 写完后必须回填一次,否则下游的镜头级覆盖核不到任何东西—— ```bash python3 /scripts/semantic_annotate.py --sync-shots # ④ 之后:按 SHOTBOOK 标题的起止秒(或 shots.json)回填 shot,只动 shot,标注不丢 ``` ## ③ 素材 - **素材清单从 ②-1 的语义标注生成,不靠拍脑袋**:`need` 是 `证据` 的句去截真页 / 找出处,`身份` 要头像与账号信息,`量化` 要图表或数据源,`entities` 里的人名 / 品牌 / 地点 / URL 就是检索词。 - **先给每个镜头标素材模式(多选,可组合)**:`B-roll`(实拍视频)/ `图片`(照片 / 海报 / 插图)/ `截图`(网页 / 界面证据画面)/ `纯动效`——如"B-roll 打底 + 截图证据卡"。新闻/信息类话题证据优先:Playwright 实时截图比泛用 B-roll 更有信息量。 四档对应 taxonomy.md 输入类型代号 **V / 图 / 截图 / 文**,SHOTBOOK 每镜写一行 `素材:V(路径)· 图(路径)· 文`(格式见 ④) - **实拍素材是必需项,不是可选项**(2026-09-06 硬规):全片零 B-roll / 图片 = 只有动效 + 口播人物 = 观众看到的是"讲 PPT"。 `preflight.py` 对账 SHOTBOOK:**零 V / 图 镜头直接 FAIL**,V / 图 镜头占比 < 1/3 WARN(要在 SHOTBOOK 给依据,如证据类题材以截图为主)。 「本片不做 B-roll」**不允许**写成设计决定;视频源搜不到就降级图片(Pexels photos 原图 → Pixabay),图片也没有才进「未完成 / 未采集清单」。 图片与视频同源同 key,采集规格(分辨率下限、落盘目录、登记)见 `references/broll-sources.md`「配图采集」 - **真图硬规**:话题存在可截的真实页面(产品官网/GitHub/文档/画廊)时, 成片中的浏览器/页面类镜头**禁止用代码 mock 冒充截图**——卡片 demo 里的灰条假 UI 是占位物, 成片必须按卡片"复用指引"整块换成 `` 真图;mock 只允许表现无真实对应物的示意 UI, 且 SHOTBOOK 逐镜标注"为何无真图"。**引申**:口播讲"这样的成片/效果"时, 示例画面必须是真成片片段(`` 内嵌已有成片/真机内录裁切,muted); 讲"长页面/看板/参数页"时用 Playwright **全页长截图**(放大镜/巡航类动效直接吃真图坐标)。 **真图上的标注坐标一律机器实测,禁目测**:页面元素用 DOM `getBoundingClientRect`、成图用逐像素量测 (目测偏 ±30px 就会把环框到别的元素上);**会滚动/移动的真图,标注(环/框/pill)必须钉在内容坐标系上随内容动**, 钉屏幕固定位就是错位根源 - **单视频镜头要有主题边框**(2026-09-07 用户定版):镜头里唯一主体是一段视频(录屏 / 单条 B-roll / 引用别人的成片或采访)时, **禁止裸贴满幅、禁止裸放白卡、也不装假播放器**(进度条 / 播放键 / 时间码一律不要——不是真播放器就是在撒谎),视频区必须包一层 `template/components/theme-frame.tsx` 的 ``:八式(复古浏览器窗口 / 杂志相框 / 35mm 胶片 / 拍立得 / 工程图纸 / 笔记本 / 邮票齿边 / 双发丝线), **按片子调性选一式、一片只用一式**,写进 SHOTBOOK 蒙皮行;多视频的卡(bed-echo-blur 前景 / split-60-40-story 左格 / gallery-wall-dolly)的视频区也包同一式。 框只管造型与自己的装饰接力,整体入场 / 退场 / 极缓推归镜头层;框里的画面零处理(不滤镜 / 不缩放 / 不淡入淡出)。 例外只有两种:视频当**底床**不当主体(bed-echo-blur / §1.2 实拍底床),以及产品界面卡(chat-gpt / claude-code 类,皮即内容)。 静图不进框(框说"这是录像",画面不动一眼假;图用 media-pop-in / slow-push-in)。规则与八式表:design-language §1.3 - **网页拍摄不贴图**:找资料/找素材时判定可用的网页,成片里**禁止以静态截图贴屏**, 必须像手持镜头一样"拍"它——**滚**(`evidence-scroll-tour◆`:匀速上滚 ≈10% 页高/s,讲到关键条提前减速停 1~2s)/ **巡**(`stage-keyframe-tour◇`:长页躺台上不动,相机挨个停靠兴趣点)/ **放大**(`magnifier-detail` 圆形放大镜 看一眼就撤,`pip-zoom-box◈` 拎出来长期挂着)/ **划**(`highlighter-sweep` 扫整句、`ink-underline◇` 划一个词、 `scribble-annotation` 圈注箭头、`corner-bracket-frame◈` 框区域);一屏装完的页面至少走 `slow-push-in` 底噪。 **一镜一主式**:滚动/巡游段内不弹放大镜、不现场划线,顺序是「滚到 → 停 → 划/放大 → 再滚」; 拍法选型表见 shot-design.md §2④「网页拍摄」。素材按 broll-sources.md「网页拍摄素材采集」规格落盘: Playwright **全页 2× 长截图 + 同一会话 DOM 实测的目标坐标 JSON**(放大镜/划线/停点全吃这份坐标,接上一条"禁目测")。 拍摄一律由 Remotion 在长截图上完成(seek-safe、可对词锚),**不用浏览器录屏**(帧率不稳、懒加载与粘性头穿帮、 对不上字级时间戳)。唯一放行:一屏装得下且只当配角(media-pop-in 多张堆叠里的一张)的小截图可静态入卡, 但仍带 Ken Burns,不得是该拍主体 - 标了 B-roll 的镜头列 2–3 个英文视觉概念词跑 **Pexels + Pixabay API 双源并行**,候选落 `assets/broll/`; 源分层与授权红线(**只用免署名源**)见 `references/broll-sources.md` - **调研记账**:承载关键事实的来源页逐一截图存档,`sources.md` 里链接与本地截图一一对应 (禁止只存链接不留证据);成片引用时优先用存档截图当画面证据 + micro 阶来源行 - **有 B-roll/截图 + 对应口播的人物素材(录播/数字人成品)时,人物一律降级成角标常驻**—— 圆形头像章(`host-shrink-to-chip◆`)或抠人贴角,落左下 / 右下角;不许切走人、不许人物占满画幅。 两路选型与全部硬约束以 `references/host-footage.md` §5 为准,镜头预设见 shot-design.md §2⑦ - Manim 图表:`--transparent --format=mov` 后**必转 VP9 webm**(`-c:v libvpx-vp9 -pix_fmt yuva420p`) - 全部落盘 public/,禁止渲染时拉远程 - **③ 收尾必跑素材体检**(把 host-footage / broll-sources 里的入场硬规变成断言;答案早就写在 reference 里、但没人在正确时刻去查, 是 2026-09-06 复盘里两个最大的坑的共同失效模式——所以不再补文档,改成开工前必跑): ```bash python3 scripts/preflight.py --media-only --host remotion/public/dh/host.webm --fps 30 --voice audio/full.wav # 查:人物素材 r/avg 帧率一致(VFR)· 源片无重复帧签名 · 素材 fps = 成片 fps(不等→成片改成素材 fps,禁 -r 直转)· # 时长与配音逐帧对齐 · 真实宽高比 · 素材盘点(实拍/图片/网页长图数)· sources.md 在册。任一 FAIL 不进 ④ ``` ## ④ SHOTBOOK(必产出,实现前评审) 先用 `references/shot-design.md` 给每个镜头填**三面分层工作单**(背景面/主体面/文字面 + 各面动效 + 七种镜头类型预设),再按 `references/cinematography.md` §4 展开成层矩阵,范例 `references/shotbook-example.md`。 每场景:一句意图 + 主体接力线 + 逐节拍层矩阵(节拍锚定字级时间戳;每行动作必须答得出"配合谁")。 **每镜必写「素材:」行**(机器可读,preflight 逐个 stat 文件):`- 素材:V(public/broll/gpu.mp4)· 图(public/stills/a.jpg, public/stills/b.jpg)· 截图(public/pages/gh/page.png)· 文`—— V / 图 / 截图 必须括号给路径,写"待采"= FAIL;纯动效镜也要写 `素材:文`。 **必填「## 未完成 / 未采集清单」节**:任何"本片不做 X"二选一——归入 §0 的设计决定(+依据),或归入本节(+阻塞原因 + 兜底源是否试过); 允许写"无",不允许缺节。这一节专门拦"未完成被包装成设计原则、进而变成不再被质疑的前提"(2026-09-06 复盘的真正失效模式)。 **G0 写一行「样板镜:sNN」**(⑤-1 首镜先做先确认用):默认 s01;s01 是章节卡 / 空镜等不带主体动效的镜头时,选第一个有卡、有字幕、有人物或素材的镜头。 **节拍必须机器可验**:每条画面重音落成 `remotion/beats.json` (`{t, anchor, sentence, what}`,t 一律由 timing.json/`atChar()` 查得,**禁止手敲近似秒数**—— 手敲的误差静帧 QA 看不出来),SHOTBOOK 节拍表与 beats.json 一致, 机器闸用 `scripts/beat_lint.py` 对 timestamps.json 校验 |Δ|≤0.1s。 **未到拍不显形**:词锚未到的数字/图形必须完全不可见(opacity 0), 禁止压暗/灰显"预告";行内数字要连同其后继字符一起 gate(「就 __ 类」的空洞挂着同样是缺陷)。 **开镜不空台**:镜头开场到第一个动效锚点 >1.5s 的空窗必须有承载画面 (真实 b-roll / 上一镜元素延续 / 真素材墙),不许空画布干等词锚。 **镜尾保护带**:词锚动效落点距镜头出点 <0.7s 的,要么提前、要么挪进下一镜——落点会被转场吞掉; `beat_lint.py --shots shots.json` 机器查 ≥0.5s 硬底线。 **幕级转场事件同样入 beats.json**:shape wipe/换幕的**遮挡峰值**时刻也由词锚生成入表—— 手敲绝对秒的转场事件表游离在机器可验体系外,静帧 QA 与 beat_lint 都看不见。 **排版预算**(全表 cinematography.md §4.5):分镜按语义段落切、每镜一个 primary visual job; **纯文镜必配陪衬图形**(2026-09-07 用户反馈"只有文字动效往上堆太单一"):素材行只有「文」的镜头(章节卡除外),层矩阵必须多一行 「G5 线稿示意图 ← 讲 X 所以画 Y」(`references/schematic.md`,代码 `template/motion-systems/schematic.tsx`),preflight 对缺行的纯文镜 WARN; 枢轴句("但这次不是X"式转折/设问)的动效归它**开启**的下一镜;任一时刻同屏主体组 ≤3(降权留守**计入**)、 每镜至少留一个空象限;hero 造型一屏一个。 **排版规范**(全表 `references/layout.md`):预算管"放多少",规范管"放哪、多大、怎么对齐"—— SHOTBOOK 每镜写**版式行**(栏跨度 + 组包围盒 + 对齐基准 + 字阶),定妆帧开 `debugOverlay` 核九项,任一失败 = P1; 独句 hero 居中但不得覆盖人脸(含 B-roll 里的人脸,纵向改取人脸之外的三分线)。 **版式轮换**(2026-09-21 用户:成片"人物在左下角、素材在中间方块、右边放文字,过于固定";全表 cinematography.md §4.5 第 9 条):写逐镜矩阵**之前**先在 G0 写「版式节奏表」 (`| 镜 | 人物形态·方位 | 素材容器 | 主卡 |`,格式 cinematography.md §4)——同一张呈现卡(素材呈现 / 数据 / 运镜类)不连用 3 镜、全片 ≤1/3(≥6 镜的片);人物形态(半身 / 角标左下 / 角标右下 / 分屏格内 / 抠人贴角 / 短暂离场) 与素材容器(出血全屏 / 装框 / 分屏格 / 底床 / 多图编排 / 3D 墙 / 长页)连续 3 镜至少换其一;同一条 B-roll 不进相邻两镜。单条 B-roll 按 shot-design.md §2⑦ 七式选、相邻镜不同式, 多素材按 §2④′ 关系表;preflight 按节奏表 + 蒙皮行核(连用 / 占比 FAIL)。轮换是换构图不是加运动——每式内部仍只有相机极缓推拉。 **每镜必写「选卡行」**(②-1 的语义 → 卡;候选由机器给): ```bash python3 /scripts/card_match.py --out qa/card-candidates.md # 语义 × 素材行 × 卡索引 → 每镜每个主句语义的可行候选 + 排序理由 + 落选理由 ``` 跑之前先 `semantic_annotate.py --sync-shots` 回填 shot(②-1 时还没有分镜)。候选表除按语义列候选,还会给声明了素材的镜单列一行 **素材承接候选**——声明了 V / 图 / 截图 却没有呈现 / 运镜类卡承接 = 素材裸贴,preflight 判 FAIL。 SHOTBOOK 每镜写 `- 选卡行:<语义> → <卡>、<语义> → <卡>`(选卡行里的卡要真的落进蒙皮行,preflight 对账); **选候选之外的卡**要在该镜写一行 `- 语义偏离:自我介绍 ← 开场已报身份,s11 不再重复`(理由 ≥4 字,preflight 认这行放行)。 层矩阵的节拍行加一列**语义**(值取自 semantics.json,不另造词),这样「这一拍在做什么」在分镜里就是机器可读的。 **选卡必读卡经验**:每张选中的卡,把 `references/cards/.md` 的「已知坑」与「落位自检」**逐条抄进该镜层矩阵的自检列**, 实现后按条核(例:取景框 / 圈注 / 下划线类卡必核标注是否套住目标;`gooey-morph` 只用于图不用于字且无人物时居中;`chapter-title-card` **每章一套主题色 + 一个与本章内容相关的线稿 motif**,SHOTBOOK 写章节主题行——四张同色同纹样的章节卡是"又来了"不是"翻页")—— 卡经验不进 SHOTBOOK 就等于没读。 动效词汇从 **108 张配方卡** 里选,两道过滤都查 `references/taxonomy.md` 的机器生成索引(源头是各卡 frontmatter 的「输入 / 语义 / 素材形态 / 位置 / props」五个字段,`scripts/cards_index.py --write` 生成、`--check` 校验,手改索引无效):**先按这一镜的输入过滤**(人 / V / 图 / 截图 / 文 / 界 / 场——「输入类型索引」),**再按这句口播的语义过滤**(自我介绍 / 数据 / 对比 / 列举 / 引用 / 号召……26 词封闭词表——「语义索引」,一张卡只列在它专为之而设的语义下);索引里带 ◦ 的卡**没有可换内容的 prop**(只暴露资源 / 皮肤类,或什么都不暴露),改文案 / 数据要动 tsx(各卡复用指引的 props 行写了改哪里)。◉◎ 两批新卡(29 张)md 开头另有「输入类型」表 + 「常用场景」四条。然后 `references/taxonomy.md` 分类索引 → `references/cards/.md` 参数与坑 → `template/cards/.tsx` **自包含 Remotion 源码(实现以它为准,复制进工程改 CONFIG 即用)**;`demos//index.html` 是同画面的 HTML 预览(`open gallery/index.html` 一屏浏览、demo 滚入即自动播放;带★实战卡的生产母本另在 template/motion-systems|components)。 **保真铁律**:每张用到的卡在工程里必须真实存在 `src/cards/.tsx` (自 template 复制改 CONFIG)——只读 md 就凭卡名手写"神似"简化版是最大翻车源 (回弹/拍击/密度全丢、取景框括号方向画反、名片变色块),机器闸用 `scripts/card_lint.py` 逐 slug 校验存在性与相似度(≥0.55,改 CONFIG/文案在容忍内)。 **蒙皮不是重写**:复制来的卡是中性 UI,必须按 SHOTBOOK §0 风格档改皮——颜色全换 theme token、字体栈与字重、圆角 / 描边 / 投影 / 材质、 图表卡的坐标轴 / 网格 / 标记 / 数字字体、卡片类的占位图形换真素材或风格化图形——**只改皮层,不改时序 / 缓动 / 几何比例 / 层级**; 每镜层矩阵旁写**蒙皮行**(`卡名 → 改了什么皮`),同一片内同类卡共用一套皮;契约、例外(产品界面卡不蒙皮、语义色不换色相)与反例见 design-language §0.4。 card_lint 的 0.55 就是给蒙皮留的余量:改皮过得了,重写运动才掉下去。 三段式铁律:入场 0.2~0.8s → hold(**静置即可**——画面的活由场景相机极缓推拉负责)→ 出场 0.15~0.5s;入场永远比出场用力;同屏重音同一时刻只能有一个。 **选了动效就要带上它的音效**:每张卡在 `demos/_lib/sfx-map.js` 有 cue 表(`{t, name, vol, rate?, clip?}`,t 为卡内相对秒)—— SHOTBOOK 选卡时把 cue 抄进该镜头的层矩阵(换算成绝对秒;**vol 按成片口径重标 ≤0.35**, demo 库的 0.65 上限是试听口径不是成片口径)。实现时按 ⑤ 的 sfx 步骤落地。 覆盖口径:**主要动效入场全覆盖**,对齐 demo 库密度 (每卡 2~6 记 ≈ 0.4 记/s,100s 的片约 40~50 记)——"少而准"管的是单点不叠双记、 音量克制(≤0.35)、连续揭示类(缓拉/对焦/逐字升起)与金句纯文字卡、logo 落幕留白、 转场只配蓄势不配落点、不要收尾叮当与重砸;不是砍覆盖面。真采样(`pk-` 前缀)优先。 **cue 的 file 名以 `ls public/sfx/` 为准**(`pk:` 键名里的冒号导出成 `pk-`, 个别键自带前缀会出现 `pk-transition-transition-soft` 这类双段名——名字错了渲染直接 404 失败)。 **④→⑤ 闸:SHOTBOOK 写完先过 preflight 全量,再进实现**(③ 的素材体检 + SHOTBOOK 对账,任一 FAIL 挡住 ⑤): ```bash python3 scripts/preflight.py --shotbook SHOTBOOK.md --host remotion/public/dh/host.webm --fps 30 --voice audio/full.wav --shots remotion/shots.json --semantics semantics.json # SHOTBOOK 对账:每镜有「素材:」行 · V/图/截图 的文件都在盘上 · 零 V/图 镜头 FAIL · 占比 <1/3 WARN · 「未完成 / 未采集清单」节在册 · shots.json 与镜头 id 一致 # · 版式轮换:同卡连用 ≥3 镜 / 呈现卡 >1/3 / 节奏表连续 3 镜同形态同容器 FAIL;版式行复制 / 素材相邻复用 / 缺节奏表 WARN ``` ## ⑤ 实现(Remotion) **先装全局系统再写场景**(代码 `template/motion-systems/`,规范正主 cinematography.md §2,运动做减法): 只装 **G1 CameraRig**(每场景一条极缓推进或拉出的 scale 曲线,1.00→1.04~1.06 或反向,不做 x/y/旋转/模糊,`impulses` 留空,shots.ts 表驱动) 与 **G3 让位状态机**(`Live demoteAt` = 下一主体锚点 / Defocus,`idle` 关、落定即静置;**demoteAt 是降权留守不是退场**—— 元素压暗缩小后仍占着原槽、计入同屏预算,新主体不得摆进它的位置;旧名 `retireAt` 仍可用但已 deprecated,2026-09-06 因名字误导出过 P0 文字相撞); G2 视差、G4 分幕色温可选、默认不装;主体 idle / 环境呼吸 vignette / 扫光 / 曝光脉冲 / 相机脉冲一律不做。 **G5 线稿示意图**(`schematic.tsx` + `icons.ts`)只给纯文镜装:DrawPath / DrawIcon / Connector / Node / Plate / Panel / Cross / Tick / Traveller / Label, 全部 abs 秒驱动、机器一笔画、线到哪亮哪、一套皮;图标缺什么跑 `python3 scripts/fetch_icons.py --merge --out <工程里 icons.ts 的路径>`(不带 `--out` 写的是库内 `template/motion-systems/icons.ts`;Iconify lucide = ISC + 部分 Feather MIT,**根目录 `THIRD_PARTY_NOTICES.md` 随 icons.ts 一起拷进工程**并登记进 sources.md)。 **每个镜头边界必须有明确转场处置,禁止裸切**:运动承接六式(lead/tail 重叠 12–16 帧 + ShotFade, 代码 `template/motion-systems/transitions.tsx`)或 caret/shape-wipe 轻量式,选型见 cinematography.md §3; 一个边界只用一式。空间/流程叙事段落可改用**长镜头世界画布**(`longtake.tsx`,cinematography.md §3.5)。 `template/components/` 是即取即用件:Subtitles 整句硬现版(chunks 由 props 注入)/FlowerWord 花字/SmashWord 砸字/HighlightSweep 荧光笔/PencilDraw 铅笔手绘/Mascot 吉祥物/NumberRoll。 **底部字幕:素排、无标点**(正主 design-language.md §5):跟读字幕不加任何动效、整句硬现,不含任何句读(数字/型号间的半角点号除外,停顿靠拆卡); 唯一例外 `keyword-pop-highlight` 关键词弹出且全片 **≤3 次**(motion-systems 版 `keywords` prop 有此上限自检)。 **音效落地**:`node scripts/sfx_dump.mjs remotion/public/sfx` 把库里采样解码成 mp3 → SHOTBOOK 抄来的 cue 表落成一张 `sfx.ts`(绝对秒),场景里 `