dsh-motion 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +3 -1
- package/README.md +37 -5
- package/docs/ai-native-motion.md +127 -16
- package/docs/dev-loop.md +1 -1
- package/docs/performance-budget.md +73 -0
- package/lib/ops.js +44 -2
- package/package.json +7 -2
- package/skills/animation-router/SKILL.md +83 -16
- package/skills/animation-router/references/confirmation.md +82 -0
- package/skills/design-spec/SKILL.md +18 -4
- package/skills/qa-rubric/SKILL.md +13 -0
- package/styles/3b1b/STYLE.md +68 -0
- package/styles/3b1b/preview.jpg +0 -0
- package/styles/3b1b/style.json +50 -0
- package/styles/3b1b/theme.py +46 -0
- package/styles/3b1b/tokens.json +32 -0
- package/styles/README.md +13 -6
- package/styles/blueprint/style.json +39 -3
- package/styles/blueprint/theme.py +3 -0
- package/styles/dataviz/style.json +42 -2
- package/styles/dataviz/theme.py +3 -0
- package/styles/registry.json +213 -0
- package/styles/swiss-motion/style.json +38 -2
- package/styles/swiss-motion/theme.py +3 -0
- package/styles/whiteboard/STYLE.md +58 -0
- package/styles/whiteboard/preview.jpg +0 -0
- package/styles/whiteboard/style.json +47 -0
- package/styles/whiteboard/theme.py +56 -0
- package/styles/whiteboard/tokens.json +27 -0
package/NOTICE.md
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
| [Manim Community](https://github.com/ManimCommunity/manim) | MIT | 可选渲染后端 |
|
|
9
9
|
| [manimgx](https://github.com/academa-labs/manimgx) | MIT | 默认渲染后端 |
|
|
10
10
|
| [SymPy](https://github.com/sympy/sympy) | BSD-3-Clause | `formula_check` |
|
|
11
|
-
| [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) | MIT(风格文档/代码)· CC BY 4.0(demo 静帧) | `swiss-motion` / `dataviz` / `blueprint`
|
|
11
|
+
| [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) | MIT(风格文档/代码)· CC BY 4.0(demo 静帧) | `swiss-motion` / `dataviz` / `blueprint` / `whiteboard` 的改编来源;只取语法与规范,不打包 demo 代码 |
|
|
12
|
+
| [subinium/3b1b-style-animation-skill](https://github.com/subinium/3b1b-style-animation-skill) | MIT | `3b1b` 的配色与教学法来源;非 3Blue1Brown 官方项目 |
|
|
13
|
+
| [adithya-s-k/manim_skill](https://github.com/adithya-s-k/manim_skill) | MIT | `3b1b` 的方法论参考(Manim 社区常用 skill) |
|
|
12
14
|
| [FFmpeg](https://ffmpeg.org) | LGPL/GPL(取决于构建) | 渲染、后期、抽帧;**不打包**,由用户自行安装 |
|
|
13
15
|
|
|
14
16
|
字体不随包分发;若你的 `DESIGN.md` 指定了第三方字体,请确认其许可(OFL / 商用授权)。
|
package/README.md
CHANGED
|
@@ -34,28 +34,60 @@
|
|
|
34
34
|
|
|
35
35
|
## 风格库
|
|
36
36
|
|
|
37
|
-
默认 **瑞士动态排版(swiss-motion
|
|
37
|
+
默认 **瑞士动态排版(swiss-motion)**;另外四套按题材选。四套改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar),`3b1b` 来自 Manim 社区,均非自创。
|
|
38
38
|
|
|
39
39
|
| 风格 | 适合 | 规范 |
|
|
40
40
|
|---|---|---|
|
|
41
41
|
| `swiss-motion`(默认) | 公式、定义、数据结论 | [STYLE](styles/swiss-motion/STYLE.md) · [tokens](styles/swiss-motion/tokens.json) |
|
|
42
42
|
| `dataviz` | 趋势、比较、分布、科学数据 | [STYLE](styles/dataviz/STYLE.md) · [tokens](styles/dataviz/tokens.json) |
|
|
43
43
|
| `blueprint` | 几何证明、结构、尺寸关系 | [STYLE](styles/blueprint/STYLE.md) · [tokens](styles/blueprint/tokens.json) |
|
|
44
|
+
| `whiteboard` | 科学解释、课程、机制说明 | [STYLE](styles/whiteboard/STYLE.md) · [tokens](styles/whiteboard/tokens.json) |
|
|
45
|
+
| `3b1b` | 线代、微积分、概率、神经网络 | [STYLE](styles/3b1b/STYLE.md) · [tokens](styles/3b1b/tokens.json) |
|
|
44
46
|
|
|
45
47
|
**参考与示例统一用 HyperFrames 的库**:[examples](https://hyperframes.heygen.com/examples)、[catalog](https://hyperframes.heygen.com/catalog)、[frame.md](https://hyperframes.heygen.com/prompting/design-systems)。本仓库不再内置样片/分集;`showcase/` 只放可复现的演示。
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
路由是显式的:`node scripts/route-style.mjs "<题材>"`(点名优先 → registry 打分 → 默认 `swiss-motion`;分数并列/为 0 时要求确认)。规则在 [styles/registry.json](styles/registry.json)。
|
|
50
|
+
|
|
51
|
+
迭代预算(首帧 ≤60s、输出 ≤30k、上下文 ≤100k、读图 ≤10)见 [docs/performance-budget.md](docs/performance-budget.md)。
|
|
52
|
+
|
|
53
|
+
开工前先过需求卡点:`npm run plan -- "竖屏抖音,讲导数,要旁白"`,会推断画幅、交付、风格的候选项,再交给 `ask_user_question`;能推断的不问。
|
|
54
|
+
|
|
55
|
+
项目 `DESIGN.md` 头部写 `style: <slug>` + `style_version: 1.0.0`。风格来源、HF 集成、抽帧能力与 Lemo 调研见 [docs/ai-native-motion.md](docs/ai-native-motion.md)。
|
|
48
56
|
|
|
49
57
|
## 演示
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
五个类别各一支 10 秒内短片,记录出帧/渲染耗时:
|
|
52
60
|
|
|
53
61
|
- 公式:[showcase/swiss-euler](showcase/swiss-euler)
|
|
54
62
|
- 数据:[showcase/dataviz-secant](showcase/dataviz-secant)
|
|
55
63
|
- 几何:[showcase/blueprint-pythagoras](showcase/blueprint-pythagoras)
|
|
64
|
+
- 白板:[showcase/whiteboard-derivative](showcase/whiteboard-derivative)
|
|
65
|
+
- 暗场数学:[showcase/3b1b-unit-circle](showcase/3b1b-unit-circle)
|
|
56
66
|
|
|
57
67
|
汇总见 [showcase/README.md](showcase/README.md)。
|
|
58
68
|
|
|
69
|
+
## 开工前确认
|
|
70
|
+
|
|
71
|
+
顺序是硬性的:
|
|
72
|
+
|
|
73
|
+
1. 需求卡点(画幅 / 交付 / 风格 / 受众 / 旁白)
|
|
74
|
+
2. 脚本 / 文案(逐句旁白、上屏文字、公式)
|
|
75
|
+
3. 分镜(镜头、秒数、画面、运动、字幕)
|
|
76
|
+
4. `DESIGN.md` + 3 张风格帧(只出帧,不渲染)
|
|
77
|
+
5. 交付清单(尺寸 / fps / 时长 / 文件 / 音频 / 字幕)
|
|
78
|
+
6. **用户明确说“可以开始 / 开始做 / 按这个执行”**
|
|
79
|
+
|
|
80
|
+
没听到第 6 步的明确指令,不写完整场景、不出全片。清单模板见
|
|
81
|
+
[skills/animation-router/references/confirmation.md](skills/animation-router/references/confirmation.md)。
|
|
82
|
+
|
|
83
|
+
按体量分档:`light`(≤15 s 无旁白:脚本+分镜合并)、`standard`(15–60 s 或旁白)、`full`(>60 s 或多段,加 animatic)。确认状态落在项目里的 `APPROVALS.json`:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm run approvals -- init <project> --mode light
|
|
87
|
+
npm run approvals -- set <project> start approved --note "用户说可以开始"
|
|
88
|
+
npm run approvals -- check <project>
|
|
89
|
+
```
|
|
90
|
+
|
|
59
91
|
## 安装
|
|
60
92
|
|
|
61
93
|
```bash
|
|
@@ -186,8 +218,8 @@ npx hyperframes render -o out.mp4
|
|
|
186
218
|
日常改代码**不需要重装**:link 安装一次后,改 `lib/ops.js` / `lib/doctor.js` 会在下一次工具调用时按 mtime 热加载;只有改工具名/参数 schema 才需要 `⌘⇧R`。风格、`tokens.json`、场景文件本来就是运行时读取,不用重启。详见 [docs/dev-loop.md](docs/dev-loop.md),也可以完全绕开 Harness:
|
|
187
219
|
|
|
188
220
|
```bash
|
|
189
|
-
node scripts/dev.mjs frames showcase/swiss-euler/scene.py 1,3,end
|
|
190
|
-
node scripts/dev.mjs render showcase/swiss-euler/scene.py --fps 30
|
|
221
|
+
node scripts/dev.mjs frames showcase/swiss-euler/scene.py 1,3,end # → out/swiss-euler-frames.png
|
|
222
|
+
node scripts/dev.mjs render showcase/swiss-euler/scene.py --fps 30 # → out/swiss-euler.mp4
|
|
191
223
|
```
|
|
192
224
|
|
|
193
225
|
## 发版(维护者)
|
package/docs/ai-native-motion.md
CHANGED
|
@@ -50,10 +50,22 @@ HF 的卖点是 "same input, same frames, same output":HTML + 可 seek 的动
|
|
|
50
50
|
|
|
51
51
|
- Lemo-Opuscar 明确写着风格是为 Claude Opus 5.5 调的,别的模型不一定复现得好;代码 MIT、demo 与静帧 CC BY 4.0;
|
|
52
52
|
- 它的 59 个风格覆盖纸感、印刷、像素、3D、纪录片等,适合当"语言菜单",不适合直接当品牌规范;
|
|
53
|
-
- 可借鉴的三件事:`STYLE.md` 作为 style prompt
|
|
53
|
+
- 可借鉴的三件事:`STYLE.md` 作为 style prompt、三张风格帧验收、分镜作为确认材料;
|
|
54
54
|
- 授权上,demo 静帧可商用但需署名 LemoLab;风格文件本身是 MIT。
|
|
55
55
|
|
|
56
|
-
本仓库的选择:删除自创的 `dark-editorial`,改为
|
|
56
|
+
本仓库的选择:删除自创的 `dark-editorial`,改为 4 套 Lemo 风格(`swiss-motion` / `dataviz` / `blueprint` / `whiteboard`)+ 1 套 Manim 社区的 `3b1b`,每套按 `STYLE.md` + `tokens.json` + `theme.py` + `preview.jpg` 落地;外部参考统一指向 HF。
|
|
57
|
+
|
|
58
|
+
### 3b1b 风格:社区里谁在用
|
|
59
|
+
|
|
60
|
+
3Blue1Brown 本人用的是 ManimGL,官方没有发布“风格规范”。社区里把它固化成 agent skill 的主要有三家:
|
|
61
|
+
|
|
62
|
+
| 项目 | 规模 | 提供什么 |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| [adithya-s-k/manim_skill](https://github.com/adithya-s-k/manim_skill) | 约 1.2k stars | ManimCE / ManimGL 两套 best-practice skill,含大量可运行示例;是这类 skill 里用得最多的 |
|
|
65
|
+
| [Yusuke710/manim-skill](https://github.com/Yusuke710/manim-skill) | 约 180 stars | 面向 agent 的 Manim 动画 skill |
|
|
66
|
+
| [subinium/3b1b-style-animation-skill](https://github.com/subinium/3b1b-style-animation-skill) | 约 7 stars | 唯一把 3b1b 配色写成明确 token 的 skill(`#1c1c1c` / 蓝 `#3b82f6` / 黄 `#fbbf24`) |
|
|
67
|
+
|
|
68
|
+
本仓库的 `3b1b` 取 subinium 的配色 + adithya 的方法论,按 manimgx 重写;不是 3Blue1Brown 官方项目。
|
|
57
69
|
|
|
58
70
|
## 3. 和 HyperFrames 怎么结合
|
|
59
71
|
|
|
@@ -113,7 +125,7 @@ manimgx inspect scene.py -t 1.2,4.5,end
|
|
|
113
125
|
DESIGN.md
|
|
114
126
|
→ motion_frames(3–6 帧:开场 / 转折 / 结论)
|
|
115
127
|
→ 改场景 → 再出帧(秒级)
|
|
116
|
-
→
|
|
128
|
+
→ 确认门:脚本 → 分镜 → DESIGN/风格帧 → 用户明确“开始”
|
|
117
129
|
→ formula_check
|
|
118
130
|
→ motion_render(定稿后)
|
|
119
131
|
→ 需要字幕/旁白/混音:进 HF
|
|
@@ -138,15 +150,7 @@ DESIGN.md
|
|
|
138
150
|
- 需要交付时才升级到 `motion_render` / HF;
|
|
139
151
|
- 成片检查(`motion_probe`)只在已经有 mp4 时运行,不做"为了检查而渲染"。
|
|
140
152
|
|
|
141
|
-
##
|
|
142
|
-
|
|
143
|
-
- [HyperFrames design systems](https://hyperframes.heygen.com/prompting/design-systems)
|
|
144
|
-
- [HyperFrames examples](https://hyperframes.heygen.com/examples) · [catalog](https://hyperframes.heygen.com/catalog)
|
|
145
|
-
- [HyperFrames CLI rendering](https://hyperframes.heygen.com/guides/rendering)
|
|
146
|
-
- [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) · [DIRECTOR.md](https://github.com/lemomo-ai/lemo-opuscar/blob/main/DIRECTOR.md) · [TECHNIQUE.md](https://github.com/lemomo-ai/lemo-opuscar/blob/main/TECHNIQUE.md)
|
|
147
|
-
- 本地 `manimgx inspect --help`(manimgx 0.1.1)
|
|
148
|
-
|
|
149
|
-
## 4.5 效率:大家怎么缩短时间
|
|
153
|
+
## 5. 效率:大家怎么缩短时间
|
|
150
154
|
|
|
151
155
|
共识是**把检查点前移、把渲染延后**:
|
|
152
156
|
|
|
@@ -166,7 +170,7 @@ DESIGN.md
|
|
|
166
170
|
- 关键不是"帧比视频快多少",而是**看帧成本与片长无关**,且不经过编码、起浏览器、抽帧三次搬运;
|
|
167
171
|
- 如果已经在 HF 里,用 `hyperframes snapshot` 而不是"先 render 再 ffmpeg 截图"。
|
|
168
172
|
|
|
169
|
-
##
|
|
173
|
+
## 6. 开发调试
|
|
170
174
|
|
|
171
175
|
服务端插件没有 HMR,但也不需要每次重装:
|
|
172
176
|
|
|
@@ -177,7 +181,106 @@ DESIGN.md
|
|
|
177
181
|
|
|
178
182
|
细节见 [dev-loop.md](dev-loop.md)。
|
|
179
183
|
|
|
180
|
-
##
|
|
184
|
+
## 7. 需求卡点:先问,再猜
|
|
185
|
+
|
|
186
|
+
默认流程多了一个“卡点”,避免在没有信息时自己替用户决定:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
node scripts/plan-questions.mjs "<brief>" --json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
输出可直接交给 `ask_user_question`:
|
|
193
|
+
|
|
194
|
+
- **画幅**:brief 已有“竖屏/横屏/16:9”就直接写进 assumptions,不再问;没有才给推断的 2 个;
|
|
195
|
+
- **交付**:brief 已有“旁白/静音”就跳过;没有才问;
|
|
196
|
+
- **风格**:`route-style` 的 top 2–3;点名风格时直接省略;
|
|
197
|
+
- **受众**:只对数学/科学、且 brief 未提“高中/大学/大众/专业”时问;选项按平台排(竖屏→大众科普,横屏→高中/大学);
|
|
198
|
+
- **旁白**:只在交付为旁白、且 brief 未提“中文/英文/双语”时问;选项是语言 + 声音;
|
|
199
|
+
- **时长/语言**:从 brief 提取,不单独问;未回答的写进 `assumptions`。
|
|
200
|
+
|
|
201
|
+
最多 4 题。关键不是“问满四道”,而是**能推断的不问、选项按上下文生成、问完一次就开做**。同时输出 `assumptions.gateMode`:≤15s 且无旁白为 `light`,15–60s 或有旁白为 `standard`,>60s 或多段为 `full`。
|
|
202
|
+
|
|
203
|
+
### 确认门(本仓库的硬规则,分档)
|
|
204
|
+
|
|
205
|
+
需求卡点问完,**不等于开工**。按体量选一档:
|
|
206
|
+
|
|
207
|
+
| 模式 | 适用 | 阶段 |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `light` | ≤15 s、无旁白 | 需求 → **脚本+分镜(合并)** → 视觉 → 开工 |
|
|
210
|
+
| `standard` | 15–60 s,或有旁白 | 需求 → 脚本 → 分镜 → 视觉 → 交付 → 开工 |
|
|
211
|
+
| `full` | >60 s、多段、复杂旁白 | 需求 → 脚本 → 分镜 → **animatic** → 视觉 → 交付 → 开工 |
|
|
212
|
+
|
|
213
|
+
确认状态落在项目里的 `APPROVALS.json`:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
node scripts/approvals.mjs init <project> --mode light
|
|
217
|
+
node scripts/approvals.mjs set <project> script_storyboard approved --note "用户确认"
|
|
218
|
+
node scripts/approvals.mjs check <project>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`check` 未全部 approved 时退出 1;不能跳过前置阶段;`start` 必须是用户明确授权。
|
|
222
|
+
|
|
223
|
+
**是不是太严?** 对 6 秒、无旁白的单公式短片,强制“脚本、分镜、交付清单”三份独立材料确实过重;所以 `light` 把脚本和分镜合并成一张表,保留“明确开工”这一道。对 30 秒以上或带旁白的片子,`standard` 的独立脚本/分镜是值得的;`full` 才加 animatic。
|
|
224
|
+
|
|
225
|
+
模板在 [skills/animation-router/references/confirmation.md](../skills/animation-router/references/confirmation.md)。
|
|
226
|
+
|
|
227
|
+
## 8. 风格路由:目前怎么做,通用标准是什么
|
|
228
|
+
|
|
229
|
+
### 本仓库
|
|
230
|
+
|
|
231
|
+
没有隐藏的自动路由。风格在**写作阶段**确定,分三层:
|
|
232
|
+
|
|
233
|
+
1. `styles/registry.json`:机器可读的风格目录(slug、中文名、uses、keywords、avoid、画幅、复杂度);
|
|
234
|
+
2. `node scripts/route-style.mjs "<题材>"`:显式打分——点名风格 12 分,`keywords` 命中每个 3 分,`uses` 每个 2 分,命中 `avoid` 扣 2 分;
|
|
235
|
+
3. `DESIGN.md` 头部 `style:` + `style_version:`:最终声明;场景代码从 `styles/<slug>/theme.py` 取 token。
|
|
236
|
+
|
|
237
|
+
判定规则:
|
|
238
|
+
|
|
239
|
+
- 点名("用 3b1b"/"瑞士动态排版")→ 直接命中,最高优先;
|
|
240
|
+
- 否则打分;`high` 直接写,`medium`/`low`/并列 → 给 top 2–3 让用户确认;
|
|
241
|
+
- 完全无命中 → 回退 `default`(`swiss-motion`),并说明是默认。
|
|
242
|
+
|
|
243
|
+
这样路由是**可复现的**:同样的题材和 registry 得到同样的推荐,不靠模型"感觉像"。
|
|
244
|
+
|
|
245
|
+
### 通用做法(没有 ISO 标准,但有事实标准)
|
|
246
|
+
|
|
247
|
+
| 做法 | 代表 | 要点 |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| 风格索引 + 元数据 | Lemo `styles/README.md` + `style.json`;HF catalog | 每个风格有稳定 slug、中英文名、分类、`uses`;索引由元数据生成,不手写 |
|
|
250
|
+
| 规范文件做声明 | HF `frame.md`;本仓库 `DESIGN.md` | frontmatter 定 hex/字体,正文定 Do/Don't;agent 先读再画 |
|
|
251
|
+
| 点名优先 + 推荐 2–3 个 | Lemo `AGENTS.md` | 用户没选时给最接近的 2–3 个,不静默换风格 |
|
|
252
|
+
| 打分 / 检索路由 | registry 关键词;HF `catalog --query` 语义检索 | 关键词/描述匹配,输出带理由与置信度 |
|
|
253
|
+
| 人类确认门 | 本仓库 `needsConfirmation` | 低置信度、并列、冲突时不自动拍板 |
|
|
254
|
+
| 版本与许可 | `style_version`、MIT/CC BY 标注 | 风格可升级、可追溯,不把 demo 当规范 |
|
|
255
|
+
|
|
256
|
+
没有"W3C 风格路由标准"这种东西;能算标准的是 **design tokens**(颜色/字体/间距的可交换格式)和 **DESIGN.md / frame.md** 这类声明文件。路由本身是产品约定,关键不是算法多复杂,而是**规则显式、结果可复现、低置信度会问人**。
|
|
257
|
+
|
|
258
|
+
### 对齐事实标准:对齐什么,不对齐什么
|
|
259
|
+
|
|
260
|
+
**对齐(接口层)**
|
|
261
|
+
|
|
262
|
+
- `style.json` 采用 Lemo 的字段:`slug` / `num` / `en` / `cn` / `category_en` / `category_cn` / `uses` / `line` / `demo`,再扩展 `keywords` / `avoid` / `aspect` / `fps` / `complexity`;
|
|
263
|
+
- `styles/README.md` 的表和 `styles/registry.json` 从 `style.json` 生成,不再手写(对应 Lemo 的 `styleboard/build.py`);
|
|
264
|
+
- 选风格语义采用 Lemo `AGENTS.md` 的“点名优先 + 推荐 2–3 个 + 不静默换风格”,并加置信度门;
|
|
265
|
+
- 保留 `style_version` / `source` / `license`,保证可升级、可追溯。
|
|
266
|
+
|
|
267
|
+
**不对齐(实现层)**
|
|
268
|
+
|
|
269
|
+
- 不复制 Lemo 的 Canvas/WebGL demo 代码,也不改 manimgx 渲染链;
|
|
270
|
+
- 不采用 Lemo 的 24 fps、相机旅行、手写笔刷等实现细节,只取语法;
|
|
271
|
+
- 不把 `tokens.json` 强改成 W3C DTCG:目前没有外部 token 消费方,改了只会增加转换成本;将来要接 Figma/HF design tokens 时再加适配层;
|
|
272
|
+
- 不照搬 59 套风格,只接与数学/数据/科学相关的几套。
|
|
273
|
+
|
|
274
|
+
这样既能在元数据、索引、选择语义上和生态互操作,又不会把实现绑死在别人的 demo 上。
|
|
275
|
+
|
|
276
|
+
### 目前的局限
|
|
277
|
+
|
|
278
|
+
- 打分是关键词匹配,不理解语义;"导数"和"切线"可能一个命中 dataviz、一个命中 3b1b(已通过去掉 3b1b 的"导数"关键词缓解);
|
|
279
|
+
- 没做向量检索或多语言同义词;
|
|
280
|
+
- registry 的 `keywords` 需要随新题材维护;
|
|
281
|
+
- 画幅/音频/复杂度字段目前只记录,还没参与硬约束(比如 9:16 未验证的风格应直接拒绝)。
|
|
282
|
+
|
|
283
|
+
## 9. 现成的风格体系(2026-10 可直接采用)
|
|
181
284
|
|
|
182
285
|
不必从零写风格文档。这个生态已经沉淀出几套公开、可复用的体系:
|
|
183
286
|
|
|
@@ -195,7 +298,7 @@ DESIGN.md
|
|
|
195
298
|
1. **风格 = 不变量 + 自由故事。** `STYLE.md` 锁死 look、颜色、字体、运动、声音;故事、镜头、节奏由导演决定(Lemo 的 AGENTS.md 原话:`STYLE.md` is fixed;everything else is yours to direct)。
|
|
196
299
|
2. **风格按名字选,不按形容词选。** 用户说 "swiss-motion" / "瑞士动态排版",agent 查索引、读 `STYLE.md`;没选就推荐 2–3 个最接近的。
|
|
197
300
|
3. **风格文档有固定骨架。** Lemo 是 11 节;mg-styles-15 是四段式,其中 signature features 必须可检验("cubic-bezier(0.34,1.56,0.64,1),错峰 2–4 帧",不是"要有弹性")。
|
|
198
|
-
4.
|
|
301
|
+
4. **先确认,后执行。** 脚本 → 分镜 → DESIGN/风格帧 → 交付清单,逐项确认;用户明确说“开始”才写完整场景、出片。
|
|
199
302
|
5. **独立评审。** 另一个 AI 用 rubric 按 7 个维度打分(风格保真、概念、运动、排版、质感、声画同步、技术),给 5–10 条带时间码的修改,再改两轮。
|
|
200
303
|
6. **确定性。** 帧是 `render(t)` 的纯函数;同一输入同一输出,便于批量与回归。
|
|
201
304
|
|
|
@@ -222,7 +325,7 @@ DESIGN.md
|
|
|
222
325
|
| `pictogram-motion` | Sports events / Visual identity / Event openers | 不选:偏活动/体育,公式场景弱 |
|
|
223
326
|
| `dark-keynote` | Product launches / SaaS / Keynotes | 不选:与 `swiss-motion` 的排版角色重叠 |
|
|
224
327
|
|
|
225
|
-
已落 **
|
|
328
|
+
已落 **5 套**:`swiss-motion` / `dataviz` / `blueprint` / `whiteboard` + `3b1b`。`iso-infographic`、`one-line`、`ascii-crt` 仍是候选,等有对应题材再验;这个列表不是永久名单——风格帧验收不过就换。
|
|
226
329
|
|
|
227
330
|
落地方式:
|
|
228
331
|
|
|
@@ -231,3 +334,11 @@ DESIGN.md
|
|
|
231
334
|
3. 画面仍由 manimgx 画,不复制它们的 Canvas/WebGL demo 代码,只取语法与 prompt;
|
|
232
335
|
4. 交付层继续交给 HF,`frame.md` 只做品牌/版式契约;
|
|
233
336
|
5. 标注来源与许可:Lemo 代码/文档 MIT,demo 静帧 CC BY 4.0(署名 LemoLab);mg-styles-15 MIT;HF Apache-2.0。
|
|
337
|
+
|
|
338
|
+
## 参考
|
|
339
|
+
|
|
340
|
+
- [HyperFrames design systems](https://hyperframes.heygen.com/prompting/design-systems)
|
|
341
|
+
- [HyperFrames examples](https://hyperframes.heygen.com/examples) · [catalog](https://hyperframes.heygen.com/catalog)
|
|
342
|
+
- [HyperFrames CLI rendering](https://hyperframes.heygen.com/guides/rendering)
|
|
343
|
+
- [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) · [DIRECTOR.md](https://github.com/lemomo-ai/lemo-opuscar/blob/main/DIRECTOR.md) · [TECHNIQUE.md](https://github.com/lemomo-ai/lemo-opuscar/blob/main/TECHNIQUE.md)
|
|
344
|
+
- 本地 `manimgx inspect --help`(manimgx 0.1.1)
|
package/docs/dev-loop.md
CHANGED
|
@@ -40,7 +40,7 @@ node scripts/dev.mjs probe out.mp4
|
|
|
40
40
|
node scripts/dev.mjs doctor
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
它直接 import `lib/ops.js
|
|
43
|
+
它直接 import `lib/ops.js`,不经过插件注册,适合调参数、量耗时、跑回归。默认输出到 `out/`(已 git 忽略),不会弄脏场景目录;`showcase/` 里是提交的展示片,测试片不要放那里。
|
|
44
44
|
|
|
45
45
|
## 方案 3:把变化关在数据里
|
|
46
46
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 迭代预算(P0)
|
|
2
|
+
|
|
3
|
+
依据:30.02 s 成片、882.1 s 端到端的一次真实诊断。
|
|
4
|
+
|
|
5
|
+
| 指标 | 那次 | 目标 |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| 首帧 | 261 s | **≤60 s** |
|
|
8
|
+
| 端到端 | 882 s | **≤400 s** |
|
|
9
|
+
| 模型时间 | 740.6 s(84%) | 不单独设目标,随步数/token 降 |
|
|
10
|
+
| 输出 tokens | 126,839 | **≤30k** |
|
|
11
|
+
| 上下文峰值 | 222k / 256k | **≤100k** |
|
|
12
|
+
| 读图 | 32 张 1080p | **≤10 张 sheet/局部** |
|
|
13
|
+
| 全文件重写 | 2 次 | **0 次**(例外见下) |
|
|
14
|
+
| `motion_probe` / `motion_render` | 3 / 4 次 | **1 / 2 次** |
|
|
15
|
+
|
|
16
|
+
## P0 四条
|
|
17
|
+
|
|
18
|
+
### 1. 先构图,后动画
|
|
19
|
+
|
|
20
|
+
- 先写“最小场景”:只放 6–8 个关键构图,不写完整动画;
|
|
21
|
+
- `motion_frames` 出 sheet,确认构图与版式;
|
|
22
|
+
- **60 s 内看不到第一帧就停下来**,检查是不是在盲写;
|
|
23
|
+
- 构图确认后才补时间轴、运动、转场。
|
|
24
|
+
|
|
25
|
+
### 2. 只做局部 edit
|
|
26
|
+
|
|
27
|
+
- 默认用 `edit`,不整文件重写;
|
|
28
|
+
- 只有改动超过文件 50%,或文件 <40 行时,才允许重写;
|
|
29
|
+
- 重写必须在回复里说明原因,不能“顺手重写”。
|
|
30
|
+
|
|
31
|
+
### 3. 图片预算
|
|
32
|
+
|
|
33
|
+
- 每次迭代只看 **1 张 contact sheet**,宽度约 960 px;
|
|
34
|
+
- 只有可疑格才读全分辨率;
|
|
35
|
+
- 同一张图不重复读;改了场景才重新出图;
|
|
36
|
+
- 目标:整个任务 ≤10 次读图。
|
|
37
|
+
|
|
38
|
+
### 4. 上下文预算
|
|
39
|
+
|
|
40
|
+
- 只保留当前场景版本,旧版本用一两句话摘要;
|
|
41
|
+
- 不把旧代码、旧图、已经确认过的长文重复贴回上下文;
|
|
42
|
+
- 目标峰值 ≤100k;接近 100k 时先摘要再继续。
|
|
43
|
+
|
|
44
|
+
## P1(工具)
|
|
45
|
+
|
|
46
|
+
已完成:
|
|
47
|
+
|
|
48
|
+
- [x] `motion_frames` 按场景 fps 取帧:各风格 `theme.py` 在模块层设置 `m.config.frame_rate`;
|
|
49
|
+
- [x] post 强制 CFR,并校验成片时长(`tpad + CFR` 修正后复验);
|
|
50
|
+
- [x] storyboard 顶部 caption 条加提示,避免误判“裁切”。
|
|
51
|
+
|
|
52
|
+
明确不做:
|
|
53
|
+
|
|
54
|
+
- CJK 字符级 `ReplacementTransform` / `Transform` 的字形变形。manimgx 核心按点插值,汉字轮廓点数与顺序不一致,修起来是字形级工程;本仓库按“不支持”处理。
|
|
55
|
+
- 中文标题/标签:`Write`、`FadeIn/Out`、`Indicate`、改颜色;
|
|
56
|
+
- 换词:`FadeOut(old)` + `FadeIn(new)`,或按词/行 stagger;
|
|
57
|
+
- 公式:`TransformMatchingTex`;中文注释放在 `Text`,不混进 MathTex。
|
|
58
|
+
|
|
59
|
+
待做:
|
|
60
|
+
|
|
61
|
+
- [ ] `motion_probe` 只在最终成片跑一次;
|
|
62
|
+
- [ ] `--draft` 低分辨率/低帧率、分段渲染、`manimgx preview` 包装、animatic。
|
|
63
|
+
|
|
64
|
+
## 怎么量
|
|
65
|
+
|
|
66
|
+
每个项目记录:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
first_frame_s, total_s, model_s, tool_s, render_s,
|
|
70
|
+
output_tokens, context_peak, image_reads, full_rewrites, probes, renders
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
复测时用同一台机器、同一支 30 s 片,先对比“首帧”和“输出 tokens”,再看总时长。
|
package/lib/ops.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { execFile } from 'node:child_process'
|
|
11
|
-
import { existsSync, statSync } from 'node:fs'
|
|
11
|
+
import { existsSync, renameSync, statSync } from 'node:fs'
|
|
12
12
|
import { basename, dirname, join, resolve } from 'node:path'
|
|
13
13
|
import { promisify } from 'node:util'
|
|
14
14
|
|
|
@@ -104,10 +104,47 @@ export async function renderScene(config, args) {
|
|
|
104
104
|
summary.video = finalOut
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// post/ffmpeg 可能因 tmix/丢帧把时长压短;用 CFR 补齐并校验,避免"30s 变 27.4s"。
|
|
108
|
+
if (summary.durationSec) {
|
|
109
|
+
const fixed = await ensureDuration(config, summary, fps)
|
|
110
|
+
if (fixed) summary.normalized = true
|
|
111
|
+
}
|
|
112
|
+
|
|
107
113
|
summary.wallSec = Number(((Date.now() - started) / 1000).toFixed(1))
|
|
108
114
|
return summary
|
|
109
115
|
}
|
|
110
116
|
|
|
117
|
+
/** 成片时长与渲染器报告的时长不一致时,用 tpad + CFR 修正;返回是否修过。 */
|
|
118
|
+
async function ensureDuration(config, summary, fps) {
|
|
119
|
+
const expected = Number(summary.durationSec)
|
|
120
|
+
if (!Number.isFinite(expected) || expected <= 0) return false
|
|
121
|
+
const file = summary.output
|
|
122
|
+
const before = await probeOutput(config, file)
|
|
123
|
+
const delta = expected - before.durationSec
|
|
124
|
+
if (!Number.isFinite(delta) || Math.abs(delta) <= 0.08) return false
|
|
125
|
+
|
|
126
|
+
const tmp = `${file}.cfr.mp4`
|
|
127
|
+
const chain = []
|
|
128
|
+
if (delta > 0.05) chain.push(`tpad=stop_mode=clone:stop_duration=${delta.toFixed(3)}`)
|
|
129
|
+
chain.push(`fps=${fps}`)
|
|
130
|
+
const args = [
|
|
131
|
+
'-y', '-loglevel', 'error', '-i', file,
|
|
132
|
+
'-vf', chain.join(','), '-fps_mode', 'cfr',
|
|
133
|
+
'-c:v', 'libx264', '-crf', '18', '-pix_fmt', 'yuv420p', '-an',
|
|
134
|
+
]
|
|
135
|
+
if (delta < -0.05) args.push('-t', expected.toFixed(3))
|
|
136
|
+
args.push(tmp)
|
|
137
|
+
await exec(binaryOf(config, 'ffmpeg'), args, { timeout: 900_000, maxBuffer: 8 * 1024 * 1024 })
|
|
138
|
+
|
|
139
|
+
const after = await probeOutput(config, tmp)
|
|
140
|
+
if (!Number.isFinite(after.durationSec) || Math.abs(after.durationSec - expected) > 0.12) {
|
|
141
|
+
throw new Error(`CFR 修正后时长仍不对:期望 ${expected}s,实际 ${after.durationSec}s(${tmp})`)
|
|
142
|
+
}
|
|
143
|
+
renameSync(tmp, file)
|
|
144
|
+
Object.assign(summary, after)
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
111
148
|
/** 归一化后期参数:枚举/上限之外的值一律夹到合法区间,交给 ffmpeg 之前就把脏输入挡掉。 */
|
|
112
149
|
export function normalizePost(raw) {
|
|
113
150
|
const p = raw && typeof raw === 'object' ? raw : {}
|
|
@@ -135,7 +172,8 @@ async function applyPost(config, input, output, fps, post) {
|
|
|
135
172
|
if (chain.length === 0) return
|
|
136
173
|
await exec(binaryOf(config, 'ffmpeg'), [
|
|
137
174
|
'-y', '-loglevel', 'error', '-i', input,
|
|
138
|
-
'-vf', chain.join(','), '-
|
|
175
|
+
'-vf', chain.join(','), '-fps_mode', 'cfr', '-r', String(fps),
|
|
176
|
+
'-c:v', 'libx264', '-crf', '19', '-pix_fmt', 'yuv420p',
|
|
139
177
|
'-an', output,
|
|
140
178
|
], { timeout: 900_000, maxBuffer: 8 * 1024 * 1024 })
|
|
141
179
|
}
|
|
@@ -252,6 +290,10 @@ export async function inspectFrames(config, args) {
|
|
|
252
290
|
mode: 'frames',
|
|
253
291
|
requestedTimes: times,
|
|
254
292
|
...parsed,
|
|
293
|
+
// 每格顶部约 28px 是 caption 条,不属于画面;不要据此判断"上沿被裁"。
|
|
294
|
+
storyboardNote: parsed.storyboard.length
|
|
295
|
+
? '每格顶部是 caption 条,不是画面;怀疑裁切时看单帧或全图。'
|
|
296
|
+
: null,
|
|
255
297
|
wallSec: Number(((Date.now() - started) / 1000).toFixed(1)),
|
|
256
298
|
}
|
|
257
299
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-motion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Code-generated math & science animations for DeepSeek Harness: frames-first preview, Manim clips, formula checks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,9 +38,14 @@
|
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"check": "node --check lib/index.js && node --check lib/ops.js && node --check lib/env.js && node --check lib/doctor.js",
|
|
41
|
-
"test": "node scripts/test-ops.mjs",
|
|
41
|
+
"test": "node scripts/test-ops.mjs && node scripts/test-route.mjs && node scripts/test-plan.mjs && node scripts/test-approvals.mjs",
|
|
42
42
|
"smoke": "node scripts/smoke.mjs",
|
|
43
43
|
"dev": "node scripts/dev.mjs",
|
|
44
|
+
"route": "node scripts/route-style.mjs",
|
|
45
|
+
"plan": "node scripts/plan-questions.mjs",
|
|
46
|
+
"approvals": "node scripts/approvals.mjs",
|
|
47
|
+
"styles:index": "node scripts/build-style-index.mjs",
|
|
48
|
+
"styles:check": "node scripts/build-style-index.mjs --check",
|
|
44
49
|
"check:meta": "node scripts/check-meta.mjs",
|
|
45
50
|
"prepack": "npm run check && npm test && npm run smoke"
|
|
46
51
|
},
|
|
@@ -5,6 +5,27 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
|
|
|
5
5
|
|
|
6
6
|
# 总入口:先分题材,再选画面层
|
|
7
7
|
|
|
8
|
+
## 0. 需求卡点(只问一次)
|
|
9
|
+
|
|
10
|
+
开工前先推断;能从 brief 看出来的,不要问。需要问时:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node scripts/plan-questions.mjs "<brief>" --json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
把返回的 `questions` 原样交给 `ask_user_question`,`assumptions` 作为默认值。规则:
|
|
17
|
+
|
|
18
|
+
- **画幅**:brief 已说"竖屏/横屏/16:9"就跳过;没说才问,只给推断出的 2 个;
|
|
19
|
+
- **交付**:brief 已说"要旁白"或"静音"就跳过;没说才问;
|
|
20
|
+
- **风格**:从 `styles/registry.json` 路由出的 top 2–3;用户点名则跳过;
|
|
21
|
+
- **受众**:只对数学/科学题材、且 brief 没提"高中/大学/大众"时问;选项按平台排(抖音→大众科普,B站→高中);
|
|
22
|
+
- **旁白**:只在交付为旁白、且 brief 没说"中文/英文/双语"时问;选项是"语言 + 声音";
|
|
23
|
+
- **时长**:从 brief 提取("30 秒"/"1 分钟"),不单独问;
|
|
24
|
+
- 最多 4 题,问完不再追问;没回答的按 `assumptions` 执行。**问完不等于开工**,接着进 §4 确认门。
|
|
25
|
+
|
|
26
|
+
选项是**推出来的**,不是固定清单:画幅看平台线索,交付看有无旁白,风格看 registry 命中,受众看题材与平台。
|
|
27
|
+
用户也可以直接在 `Other` 里写别的。
|
|
28
|
+
|
|
8
29
|
## 1. 判题材
|
|
9
30
|
|
|
10
31
|
| 题材 | 观众记住 | 判据 |
|
|
@@ -12,7 +33,19 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
|
|
|
12
33
|
| 数学讲解 | 一个**关系** | 公式、坐标、推导、表格 |
|
|
13
34
|
| 科普叙事 | 一个**意象** | 画面、比喻、节奏、材质 |
|
|
14
35
|
|
|
15
|
-
## 2.
|
|
36
|
+
## 2. 选风格
|
|
37
|
+
|
|
38
|
+
先读 `styles/registry.json`,再按顺序:
|
|
39
|
+
|
|
40
|
+
1. **用户点名**("用 3b1b / 瑞士动态排版")→ 直接用,不再打分;
|
|
41
|
+
2. 没有点名 → `node scripts/route-style.mjs "<题材>" --json`;
|
|
42
|
+
3. `confidence: high` → 写进 `DESIGN.md` 的 `style:`;
|
|
43
|
+
4. `medium` / `low` / `needsConfirmation: true` → 给用户 top 2–3 和理由,等确认;
|
|
44
|
+
5. 完全不命中 → 默认 `swiss-motion`,并说明这是默认。
|
|
45
|
+
|
|
46
|
+
不要用"感觉像"选风格;理由是 registry 的关键词/uses 命中,不是形容词。
|
|
47
|
+
|
|
48
|
+
## 3. 选画面层
|
|
16
49
|
|
|
17
50
|
| 画面层 | 适合 | 不用它的理由 |
|
|
18
51
|
|---|---|---|
|
|
@@ -22,25 +55,59 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
|
|
|
22
55
|
|
|
23
56
|
三者都只是素材层;**需要交付时**再统一进 HyperFrames 工程,默认的极简模式不经过 HF。
|
|
24
57
|
|
|
25
|
-
##
|
|
58
|
+
## 4. 确认门(阻塞,分档)
|
|
59
|
+
|
|
60
|
+
写完整场景、出全片之前,按项目体量选一档,逐项确认。**不要跳步,不要用“我先做起来”代替确认。**
|
|
61
|
+
|
|
62
|
+
| 模式 | 适用 | 阶段 |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `light`(默认) | ≤15 s、无旁白、一个结论 | 需求 → **脚本+分镜(合并)** → 视觉 → 开工 |
|
|
65
|
+
| `standard` | 15–60 s,或有旁白 | 需求 → 脚本 → 分镜 → 视觉 → 交付 → 开工 |
|
|
66
|
+
| `full` | >60 s、多段、复杂旁白 | 需求 → 脚本 → 分镜 → **animatic** → 视觉 → 交付 → 开工 |
|
|
67
|
+
|
|
68
|
+
- 脚本:逐句旁白、上屏文字、公式(先过 `formula_check`);
|
|
69
|
+
- 分镜:镜头号、起止秒、画面、运动、字幕/旁白、验收点;
|
|
70
|
+
- 视觉:`DESIGN.md` + 3 张风格帧(真实代码,只出帧);
|
|
71
|
+
- 交付清单:画幅 / fps / 时长 / 文件 / 音频 / 字幕;
|
|
72
|
+
- animatic:带时长的分镜 + 临时旁白,只在 `full` 或用户要求时做。
|
|
73
|
+
|
|
74
|
+
用账本记录,别靠上下文记忆:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
node scripts/approvals.mjs init <project> --mode light
|
|
78
|
+
node scripts/approvals.mjs set <project> script_storyboard approved --note "用户确认"
|
|
79
|
+
node scripts/approvals.mjs check <project> # 未全部 approved 时退出 1
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- `check` 不通过,不进入执行;
|
|
83
|
+
- `start` 是独立阶段,只有用户**明确**说“可以开始 / 开始做 / 按这个执行 / 开工”才能置为 approved;
|
|
84
|
+
- “好 / 可以 / 行”如果只是回答某个选项,**不算**整体开工;
|
|
85
|
+
- 不确定就问:“脚本、分镜、风格帧都确认了,现在开始执行吗?”
|
|
86
|
+
|
|
87
|
+
模板与清单见 [references/confirmation.md](references/confirmation.md)。
|
|
88
|
+
|
|
89
|
+
## 5. 执行顺序(确认后才开始)
|
|
26
90
|
|
|
27
91
|
```
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
92
|
+
0. 最小场景:只放 6–8 个关键构图,motion_frames 出 sheet(首帧预算 ≤60s)
|
|
93
|
+
1. 补全场景代码 + formula_check
|
|
94
|
+
2. motion_frames(3–6 帧,局部改动后)
|
|
95
|
+
3. 确认过的分镜帧(6–9 帧,若要)
|
|
96
|
+
4. motion_render(draft → final)
|
|
97
|
+
5. HyperFrames composition(字幕/旁白/混音;需要时才进)
|
|
98
|
+
6. hyperframes check / motion_probe(只对最终成片跑一次)
|
|
35
99
|
```
|
|
36
100
|
|
|
37
|
-
|
|
38
|
-
`motion_frames` 内部是 `manimgx inspect`,只画请求的时刻并做版式检查,几秒出图;
|
|
101
|
+
`motion_frames` 内部是 `manimgx inspect`,只画请求的时刻并做版式检查;
|
|
39
102
|
`motion_probe` 是给已有 mp4 做体检的,不要用它替代看帧。
|
|
40
103
|
|
|
41
|
-
##
|
|
104
|
+
## 6. 五条硬规则
|
|
42
105
|
|
|
43
|
-
1.
|
|
44
|
-
2.
|
|
45
|
-
3.
|
|
46
|
-
4.
|
|
106
|
+
1. **没有明确开工指令,不执行**:没听到“可以开始”就不写完整场景、不出全片;
|
|
107
|
+
2. **没有 DESIGN.md 或参考语法,不开工**——否则每次都是同一个默认 look;
|
|
108
|
+
3. **默认先帧后片**:先用 `motion_frames` 把画面改对,再决定要不要渲染;
|
|
109
|
+
4. **字幕放在 composition 层**,不烧进 Manim 或 canvas;
|
|
110
|
+
5. **公式先过 `formula_check`**,再进画面;
|
|
111
|
+
6. **首帧预算 ≤60s**:先最小场景 + 构图 sheet,60s 看不到第一帧就停;
|
|
112
|
+
7. **只做局部 edit**:改动 <50% 不重写整个文件;
|
|
113
|
+
8. **图片/上下文预算**:每次迭代 1 张 sheet(约 960px),上下文 ≤100k。
|