Hyperframes Embedded Captions 定制设计指南:Presets 覆盖、Canonical 克隆与 Typography 逐场景决策
本篇指南聚焦 hyperframes 仓库中 embedded-captions skill 的核心设计方法论:5 个预设样式(intro / phrase / emph / dream / crown)与 3 个模板(wall-embed / corner-column-crown / portrait-header)只是脚手架而非规则,真正高质量的嵌入字幕渲染几乎都来自对预设的覆盖(override)与对已验证渲染的克隆(clone)。读完本文,你将掌握「先定形状 → 判断能否克隆 → 否则从预设出发逐组覆盖」的三步工作流,理解 Cinematic 模式下哪些属性被锁定不可覆盖、哪些保留给 Agent 自由编排,并学会通过 custom_css、flex 累积排版、字号缩放表和渲染历史快照实现场景级定制。
定位:Presets 是脚手架,不是规则
在 embedded-captions 的 Cinematic 纯嵌入模式中,每条字幕都直接合成在人物背后(matte 遮挡),没有 rail 轨道承载正文。此时排版是否贴合场景,直接决定渲染成败。而 bespoke-vs-presets.md 给出的核心结论是:
- 只依赖 presets → 渲染看起来千篇一律(generic);
- 只复制既有渲染 → skill 无法适应新场景;
- 正确的做法是三步决策:
- 先决定形状(template 选择、plane 位置、blend mode);
- 检查是否有足够接近的 canonical 示例 → 克隆并替换 words + timings;
- 否则从 presets 出发 → 通过
custom_css逐组覆盖。
原因很直接:typography 是逐场景(per-scene)的决策,不是通用规则。最好的出片几乎都是针对特定群体覆盖了预设的结果。
Canonical 示例渲染:验证过的设计资产
两个经过验证的完整渲染 HTML 存放在 skills/embedded-captions/references/example-renders/:
| 文件 | 场景 | 它为什么有效 |
|---|---|---|
| memory-wall.html | 内省独白、右侧泡沫吸音墙、中调 | 右对齐级联,逐组定制尺寸(cap-1 78 italic / cap-2 66 italic + 右侧悬挂缩进 / cap-3 72 正体 / cap-4 90 大写)。对偏暗泡沫墙面使用 mix-blend-mode: screen |
| champion.html | 播客访谈、杂乱书架背景、1920×1080 | 左上列 + 舞台中央 crown。调校后的预设类尺寸(cap-intro 52 / cap-phrase 60 / cap-emph 70 / cap-crown 140)。screen 混合让书架透出文字 |
当新场景与其中之一高度相似(相近构图、相近主体位置、相近背景类型)时:直接克隆 HTML,只替换 GROUPS 数组和单词时间戳。不要从预设重新推导设计——那会丢掉经过多轮迭代验证的特定选择。
两个示例的源码结构本身就是一份教材。以 memory-wall.html 的 GROUPS 为例,每个组包含 id、逐词时间戳(words[].start/end)、组的 in/out 窗口和 tone:
const GROUPS = [
{
id: "cg-0",
words: [
{ text: "Some", start: 0.24, end: 0.44 },
{ text: "memories", start: 0.48, end: 0.82 },
{ text: "feel", start: 0.9, end: 1.14 },
{ text: "soft", start: 1.2, end: 1.64 },
],
in: 0.2,
out: 4.85,
tone: "soft",
},
// ...cg-1 / cg-2 / cg-3
];
对应 HTML 中的字幕容器则形如 <div id="cg-0" class="cap cap-1">…</div>,每个词包裹在 <span class="w" data-i="N"> 中,供 GSAP 时间线逐词驱动(详见 memory-wall.html)。
何时 presets 是错的:四种典型越界场景
你会本能地写 "style": "emph",但场景真正需要的可能是下面四种情况之一。
场景一:「这条字幕位于第 N 个位置,值得专属处理」
memory-wall.html 使用 cap-1 / cap-2 / cap-3 / cap-4——按位置索引(position-indexed),而非按角色索引(role-indexed)。每一个都是针对叙事弧线上特定短语的定制设计:
- cap-1(柔和开场,4 词):78px italic 600 —— 像耳语;
- cap-2(梦幻修饰语,3 词):66px italic 500 +
padding-right: 44px—— 悬挂缩进制造右侧参差、诗意般的边缘; - cap-3(转折,2 词):72px 正体 700 —— 句法枢纽,无斜体;
- cap-4(高潮,4 词):90px uppercase 900 —— 三行右对齐级联。
phrase/emph/intro 无法表达「这条字幕带悬挂缩进」或「这条字幕是句法转折点」。当这一点重要时,发明自己的类名,并通过 plan.json 的 custom_css 注入:
{
"template": "wall-embed",
"custom_css": "
.cap-1 { font-size: 78px; font-weight: 600; font-style: italic;
letter-spacing: -0.01em; }
.cap-2 { font-size: 66px; font-weight: 500; font-style: italic;
padding-right: 44px; }
.cap-3 { font-size: 72px; font-weight: 700; letter-spacing: -0.015em; }
.cap-4 { font-size: 90px; font-weight: 900; letter-spacing: -0.03em;
text-transform: uppercase; line-height: 1.0; }
",
"groups": [
{ "id": "cg-0", "style": "1", "words": [...] },
{ "id": "cg-1", "style": "2", "words": [...] }
]
}
关键机制:"style": "1" 字段会变成元素上的 class="cap-1"——任意字符串都行,没有任何校验。这可以从编译器的源码直接印证:make-composition.cjs 中 renderCap() 读取 g.slot ?? g.style 并拼出 class="cap cap-${slot}";随后 buildPerGroupCss() 会把每组 css 写入 #${g.id} { ... } 规则。
场景二:「模板的混合模式不适合这个背景」→ 换模板,不要覆盖
Cinematic 模式不会覆盖颜色或混合模式。 模板的 mix-blend-mode + fill 是锁定的 DNA——make-composition.cjs 源码中明确注释:plan.cap_color / blend_mode / text_shadow / text_filter 被有意忽略。选择模板即承诺其外观;Agent 唯一可写的只有布局(planes/positions)和逐组排版。
因此,应该用字幕区域亮度去选择本就匹配的模板,而不是重着色:
| 区域亮度 | 适合什么 | 原因 |
|---|---|---|
| < 60(暗 / 低调) | cream + screen 模板(cinematic-cream、memory-wall、champion、portrait-header) |
浅色文字发光,拾取场景 |
| 60–180(中调) | cream + screen 模板仍可读(边缘情况可经 Standard 加 scrim) |
文字拾取纹理 |
| > 180(明亮:窗户、浅色墙面) | cream/screen Cinematic 模板一个都不行——会洗白 |
→ 改用 Standard 模式(不透明 rail,按所选模板设置) |
如果场景明亮、cream/screen 观感被洗白,正确的信号是切换到 Standard 模式(在 HTML 中设置不透明颜色),而不是把一个 Cinematic 模板重着色成它本不是的东西。
场景三:「悬挂缩进 / 外缩 / 字宽微调」
这些是按组(per-group)的辅助能力,偶尔需要。通过 custom_css 表达:
#cg-2 {
padding-right: 44px;
} /* 右对齐:收缩右边缘,形成左侧外缩 */
#cg-2 {
padding-left: 44px;
} /* 左对齐:右移起始位置 */
.cap-emph .w:first-child {
font-size: 110%;
} /* 仅放大首词 */
#cg-N 选择器永远有效,因为 make-composition.cjs 的 renderCap() 为每个组都写出 <div id="cg-N" ...>。
场景四:「字幕应该累积(flex 堆叠)而不是替换」
三个模板默认在其 plane 内对 .cap 使用 position: absolute——字幕堆在同一位置,只有激活的一条可见(单字幕替换)。这对 portrait-header 和 corner-column-crown 是正确的:每条字幕替换上一条。
而 memory-wall 使用 flex 列累积——字幕像诗一样堆积。模板默认不是这样,需要通过 custom_css 覆盖:
.wall-plane {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end; /* 或 flex-start 用于左对齐 */
text-align: right;
gap: 14px;
}
.wall-plane .cap {
position: static; /* 解除模板的 absolute */
top: auto;
right: auto;
max-width: 100%;
}
配合错开的 in / out 时间:cap-0 在 t=0.2 淡入,cap-1 在 t=2.55(flex 顺序上位于 cap-0 下方),cap-2 在 t=4.90 进入(此时两者在 t=4.85 一起淡出)——这正是 memory-wall 诗歌分页的工作原理,实现在 memory-wall.html 的 GROUPS 时间中。
场景五:「字号与场景不匹配」
模板预设尺寸是针对特定列宽 + 帧尺寸调校的。不要对抗它们——直接覆盖:
"custom_css": ".cap-intro { font-size: 52px; } .cap-phrase { font-size: 60px; }"
然后参考 typography-presets.md 中「字号随列宽缩放」一节,依据 plane 的实际尺寸决定目标值。
预设体系速览:五种样式与两种 tone
typography-presets.md 定义了与 cap-* CSS 类对应的五种命名样式,在 plan.json 中按行的语义角色为每个字幕组选择:
| style | CSS | 何时使用 |
|---|---|---|
intro |
66px italic 500 | 首行、填充语("You know…"、"So…")、沉思式开场。低视觉重量 |
phrase |
78px upright 600 | 主要陈述从句。多数行的默认 |
emph |
92px upright 800 | 情绪峰值或关键成就行("I've achieved incredible things") |
dream |
82px italic 700 | 向往 / "was dreaming of…" 风格行。斜体传达记忆/想象 |
crown |
140px upright 900 uppercase | 仅限高潮行。用于舞台中央 crown-plane。每个合成最多一条——理想情况下是最后一条字幕 |
每个组还有独立于样式的 tone 字段:soft(柔和淡入 + 8px 上移入场,power2.out 缓动,漂浮怀旧感,用于记忆、intro、dream)与 present(干脆的 6px 上移 + 1.04 缩放弹入,power3.out,transformOrigin 居中,果断在场感,用于 emph 和 crown)。
自动挑选启发式:1)含最高级词汇(incredible, best, only, never, always)或品牌/专有名词 → emph + present;2)以话语标记开头(you know, so, well, look)或很短(≤3 词)→ intro + soft;3)提及梦想、希望、记忆、过去时动词如 "was dreaming" → dream + soft;4)收束句且像标题/头条 → crown + present(每片仅一条);5)其余 → phrase +(跟随相邻组 tone,默认 soft)。
字号随列宽缩放:.cap-* 默认值针对 ~560px 列(champion 原始构图)调校。plane 更宽时字体显得欠重:
| Plane 宽度 | intro | phrase | emph | dream | crown(居中全幅) | crown(仅干净区) |
|---|---|---|---|---|---|---|
| 460–580 px(紧凑) | 66 | 78 | 92 | 82 | 140 | n/a |
| 600–760 px(中等) | 78 | 108 | 128 | 100 | 220 | 118 |
| 780+ px(宽) | 90 | 128 | 150 | 116 | 260 | 140 |
注意:crown 尺寸假设横屏 1920×1080;竖屏 1080×1920 时所有 crown 尺寸除以 1.5。「全幅 crown」指居中、宽度设计为帧宽 0.8+ 并横跨主体身体,仅当 layout-heuristics.md 的 Crown 放置条件通过时使用;「仅干净区 crown」放在一个干净区内、字号更小,是居中 crown 吃掉太多画面时的回退。另外,若 plane 的 rotateY 超过约 8°,有效可视宽度会收缩,字号需上调约 10% 补偿。
反模式清单:不要每片超过一个 crown(它是唯一的回报时刻);emph 不要超过约 30% 的组(处处强调等于没有强调);不要因为词短就为正文选 intro("I won" 两词应选 emph 或 crown);不要无理由地交替 soft/present——tone 应跟随故事弧线:soft 开场 → present 铺垫 → emph 峰值 →(可选 crown 高潮)。
Clone-and-tweak 工作流:跳过 plan.json 直接复制
当新视频与既有 canonical 示例明显相似时:
# 1. 搭建项目脚手架
hyperframes init <project> --non-interactive --video <video.mp4> --skill=embedded-captions
# 2. 抠像 + 转写
node scripts/matte.cjs <project>
node scripts/transcribe.cjs <project>
# 3. 复制 canonical HTML,而不是编写 plan.json
cp references/example-renders/memory-wall.html <project>/index.html
# 4. 用新转写稿的分组替换 GROUPS 数组(手工编辑 index.html)
# 5. 直接渲染(跳过了 make-composition.cjs,因为我们不用 plan.json)
bash scripts/render-and-composite.sh <project>
这条路径完全跳过基于预设的 plan.json。适合克隆:主体构图、镜头布局、背景类型与示例相似;只需替换文字和时间;示例的定制排版正是你想要的。不要克隆:主体位置差异显著(如居中 vs 偏侧)、场景亮度 / 混合模式需求不同、想实验新排版——这些情况应回到 plan.json + custom_css 迭代。
关于渲染脚本的细节,render-and-composite.sh 会根据项目内是否存在 index.html / plan.json / cinematic.json 自动决定编译器:无 index.html 时自动调用 make-composition.cjs(或 make-cinematic.cjs);当源文件比 index.html 新时自动重编译,避免渲染过期产物。
渲染历史快照:迭代可回退
render-and-composite.sh 会在每次渲染前把 index.html + plan.json 快照到 <project>/history/(带时间戳,见 render-and-composite.sh)。当用户说「上一个更好」时,与最新快照做 diff:
ls <project>/history/
diff <project>/history/index-20260422-203947.html <project>/index.html
这让你能恢复迭代中被改掉的某个设计,而无需重读 Agent 的对话记录。
附:从源码看「可写」与「锁定」的边界
结合 make-composition.cjs 可以精确界定 Cinematic 模式的职责边界:
- Agent 可写:
plan.plane/plan.header/plan.crown(位置、尺寸、旋转角度,见 make-composition.cjs 的PLANE_*/CROWN_*占位符替换)、plan.font_scale(全局字号缩放)、每组的style/slot/css/scale/tone/hero; - 编译器接管:画布时长默认跟随源视频时长而非最后一条字幕(源码中标注的 Bug-1 修复策略,make-composition.cjs)、合成背景、字体注入、以及 occlclusion/timing/overflow 等一系列渲染前 gate。
因此,本文讨论的「覆盖」永远发生在布局与逐组排版层面;颜色与混合模式层面请始终通过选择模板来解决。这就是 bespoke-vs-presets 方法论与源码实现完全吻合的地方:预设负责锁定视觉身份,而 Agent 的创造力集中在每个场景独有的形状、位置与字体叙事上。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00