HyperFrames changelog-video 构建规范详解:1080 方形母版时间线、品牌令牌与接缝机制
HyperFrames 的 changelog-video skill 负责把一份周报式 changelog Markdown 变成一条 45–60 秒、1080×1080 方形的品牌化视频,而 build-spec.md 正是这条流水线的"构建宪法":它定义了母版时间线的命名契约、品牌字体与颜色令牌、动画背景的 ffmpeg 编码管线、场景坐标规范,以及接缝(seam)机制与 Lint 陷阱的完整解法。读完本文,你将掌握从 scaffold 到 seam-stamp、从字幕轨到渲染门禁的整套构建细节,并理解每条规则背后的渲染器行为依据。
单文档母版:为什么所有场景都写进一个 index.html
build-spec 的第一条约束是结构性的:整条视频是单文档 index.html,所有场景都是绝对定位在轨道 1(track 1)上的 .slide clip;存在一条母版时间线,且必须命名为 tl——因为 seam-stamp 工具生成接缝代码时直接发出 tl.* 调用,名字不对整个接缝系统就失效。这条时间线需要是 paused 的、被 pad 到总时长、并注册到 window.__timelines["main"]。
仓库中的字面 scaffold master-skeleton.html 完整展示了这套契约:
- 根节点声明合成尺寸与总时长:
<div id="root" data-composition-id="main" data-start="0" data-duration="<TOTAL>" data-width="1080" data-height="1080">(骨架 L46); - 时间线创建与 pad:
const tl = gsap.timeline({ paused: true }); tl.to({}, { duration: /*<TOTAL>*/ 47 }, 0);(骨架 L76-L77),骨架同时初始化gsap.defaults({ overwrite: 'auto' }); - 注册与接缝插入点:
window.__timelines = window.__timelines || {}; window.__timelines["main"] = tl;,其后紧跟注释"seam-stamp.mjs inserts the<seams:auto>block after the registration above"(骨架 L114-L117)。
scaffold 还从 CDN 引入 GSAP 3.14.2(见 骨架 L9),并在 <head> 内联全部 @font-face 与布局样式。SKILL 文档明确要求:步骤 0 就是把 examples/master-skeleton.html 拷贝为 project/index.html,之后只允许填充占位符(<RANGE>、<TOTAL>、<CUT_N>、<DUR_N>、场景正文),不允许重写 scaffold 的 chrome、字体、调色板或布局壳(SKILL.md 步骤 0)。
品牌令牌:字体、颜色与玻璃卡片的精确取值
build-spec 的第二部分是品牌令牌(以 HeyGen for Developers 为例),核心是一组 @font-face 声明与调色板注释。字体共 5 款,全部以本地 woff2 内嵌——skill 自带字体文件(assets/fonts/ 下有 ABCSolarDisplay-Bold、TT_Norms_Pro_Normal/Medium/Bold、tt_norms_pro_mono_regular 五个 woff2),构建时拷贝到项目 assets/fonts/:
@font-face {
font-family: "ABC Solar Display";
font-weight: 700;
src: url(assets/fonts/ABCSolarDisplay-Bold.woff2) format("woff2");
}
@font-face {
font-family: "TT Norms Pro";
font-weight: 400;
src: url(assets/fonts/TT_Norms_Pro_Normal.woff2) format("woff2");
}
/* 同族 500 / 700 分别对应 TT_Norms_Pro_Medium / Bold */
@font-face {
font-family: "TT Norms Mono";
font-weight: 400;
src: url(assets/fonts/tt_norms_pro_mono_regular-webfont.woff2) format("woff2");
}
build-spec 内嵌的调色板注释值得逐字继承,因为它把"为什么"也写清楚了:
- ink(正文白)
#f5f6f4,ink2rgba(245,246,244,.72),dimrgba(245,246,244,.66)——注释特别强调 dim 不能低于 .66(而不是 .45),因为对比度门禁(contrast gate)会拦截玻璃面上小于 4.5:1 的小字; - 强调绿
#5ef17c,且是"配给制"(RATIONED):每个场景只允许一个绿色时刻; - 背景底
#0a0c0b、chip 底rgba(10,12,11,.78)、玻璃描边rgba(190,255,205,.32)。
组件级取值规则:玻璃卡(glass card)用 chip-bg 填充、1px 玻璃描边、圆角 22、box-shadow: 0 24px 60px rgba(0,0,0,.5),且明确禁止 backdrop-filter;chip 用等宽字体 18–22px、圆角 12、rgba(255,255,255,.14) 描边 + rgba(255,255,255,.05) 底。字体分工:展示标题用 ABC Solar Display 700,正文用 TT Norms Pro,一切代码/UI 标签用 TT Norms Mono。安全边距为 x/y ∈ [76, 1004]——这与 scaffold 里 kicker chip top: 44px; left: 76px、sec-chip top: 128px; left: 76px 的硬编码坐标(骨架 L21-L32)一一对应。
动画背景:把循环源片编码成"整片时长"的 MP4
build-spec 称之为"house pattern"的动画背景有一段硬性管线:把 skill 自带的源片 bg-pattern.mp4 编码到与整片完全相同的时长。原因写在文档里——渲染编译器会把视频 clip 的槽位缩短到媒体长度(shorten a video's slot to the media length),所以编码时长必须 ≥ 总时长:
ffmpeg -y -stream_loop 15 -i <SKILL_DIR>/assets/bg-pattern.mp4 -t <TOTAL> \
-vf "scale=1080:1080,fps=30,eq=saturation=0.72,drawbox=c=black@0.5:t=fill" \
-an -c:v libx264 -crf 20 -pix_fmt yuv420p assets/bg-pattern-<TOTAL>s.mp4
滤镜链的每个环节都有意图:缩放满 1080 方形、锁 30fps、saturation=0.72 压饱和度,最后 drawbox=c=black@0.5:t=fill 整体压暗——文档警告"保留这层压暗(keep the darkening crush),原始图案太刺眼了"。这条命令与 SKILL.md 步骤 0 的 bootstrap 命令块完全一致,说明它是"非协商"的启动步骤。
挂载约束同样具体:<video id="bg-video" class="clip" muted> 放在 track 0,外加一层静态 rgba(8,10,9,.25) 的 scrim div(scaffold 中即 #bg-scrim,骨架 L19)。<video> 必须有 id(否则渲染结果静默黑屏),且必须保持平面 2D(不能有 3D 祖先节点)。scaffold 中 bg-video 与 BGM 的挂载示例:
<video id="bg-video" class="clip" src="assets/bg-pattern-<TOTAL>s.mp4"
muted data-start="0" data-duration="<TOTAL>" data-track-index="0"></video>
<audio id="bgm" src="bgm.mp3" data-start="0" data-duration="<TOTAL>"
data-track-index="2" data-volume="0.14" data-media-start="0"></audio>
场景解剖学:chrome、标题、主题场、Outro 与字幕轨
build-spec 把一条 changelog 视频的 DOM 拆成五种角色,每种的坐标与时长预算都是定死的:
Chrome(无计时,z-index 6):左上角 kicker chip 显示 HYPERFRAMES WEEKLY · <RANGE>;右上角进度点(progress dots)每个主题一个,用 tl.set 在每个切点改 backgroundColor——active 为 #f5f6f4,done 为 .45 透明度。文档特别禁止用 tl.call 改状态,scaffold 里同样注释了"progress dots via tl.set at each cut (never tl.call)"(骨架 L79-L82)。
Title(≤2s):等宽 kicker 日期、ABC Solar h1 约 104px、绿色横线扫入。Title 是"影片开场",它是唯一自己手写 entry 的场景。
Theme 场景:sec-chip 0N · THEME NAME 位于 top 128,ABC Solar 主标题约 54px 位于 top 186,可视化 mock(来自可视化注册表)填充 y ∈ [288, 944] 区间。scaffold 中这些位置就是 .sec-chip / .sec-head 的 CSS 常量(骨架 L27-L32)。
Outro(≤3.5s):kicker FULL DIGEST、"See what shipped." 约 96px、绿线、等宽 URL chip、tagline;全部元素在结束前约 0.5s 淡出(含 chrome)。
字幕轨(caption rail,强制项):按 script-voice.md 的约定——top: 990px、font-size 32px、高 52px,TT Norms Pro 500,ink .94,柔和暗色 text-shadow;它是叠在影片之上的 overlay,不是预留色带。scaffold 内置了一段字幕 IIFE:读取 LINES 数组([{id, end, w:[[display, start], …]}]),逐词建 span,再用 tl.set 控制整句显隐、tl.to(0.12s,power1.out)逐词淡入(骨架 L89-L112)。渲染前必须把 captions.json 的内容填进 LINES——SKILL.md 把它定性为"空 LINES 是 shipped bug,不是风格选择",而门禁第 5 条会抽样渲染帧逐帧验证字幕可见。
接缝与内部生命:ledger、seam-stamp 与"内部节拍"纪律
build-spec 的"doctrine mechanics"部分是这条流水线最硬核的约束集,它建立在 motion-doctrine skill 之上(motion-doctrine/SKILL.md 定义了向量律与 Seam Gate),具体规则是:
ledger.json:每一条普通接缝都记为cut-the-curve LEFT(x 轴、方向 −1),exit 与 entry 的 selector 都是 slide 的 wrapper;Outro 的 entry 记travel: 8。seam-stamp.mjs --ledger ledger.json --write index.html拥有所有 wrapper 的 entry/exit——作者本人不得手写接缝代码;Title(影片开场)只手写自己的 entry,exit 仍由 stamp 生成。- Slides 的 CSS 基态是
opacity: 0,data-start必须精确等于切点时间——这正是 motion-doctrine 指出的 clip-gating 陷阱:data-start早于 entry tween 会让 clip 以初始透明度提前显形,造成零重叠门禁失败。 - 每个场景的 shell(chip、headline、mock chrome、初始状态)在局部 t=0 就已"摆好"(COMPOSED),由 wrapper 把它飞进来;内部 reveal 必须在切点后 ≥0.4s 才开始、在下一切点前 ≥0.45s 结束(因为 stamped 的 exit 从 cut − 0.34s 开始)。
- 每个内部节拍(beat)落在
vo-words.json的一个 VO 词上;每个场景要声明它的持续运动路线(mock 用 sequenced UI life,checklist 用 staged reveals);每个场景一个绿色时刻。 - GSAP 用法纪律:初始态用
gsap.set(...)在构建时打;动画只用顺序的tl.to(...)(禁用裸对象 keyframes、禁用repeat: -1、禁用对 left/top 的 tween——基础位置写在 CSS,位移 tween x/y);计数器用带onUpdate的对象 tween(缓存 DOM 引用,绝不用tl.eventCallback)。
seam-stamp 到底往 index.html 里写了什么,可以从 seam-stamp.mjs 的 emit 逻辑直接验证:在切点处生成成对的显隐指令(tl.set("<exit>", { autoAlpha: 0 }, cut) / tl.set("<entry>", { autoAlpha: 1 }, cut),脚本 L76-L77);cut-the-curve 变体则发出 exit 侧 power3.in 缩放+模糊+渐隐、entry 侧 expo.out 的 fromTo(脚本 L99-L106);travel 变体按方向符号位移后 power4.out 回位(脚本 L114-L118)。这解释了 build-spec 为什么要求 tl 必须存在、slide 必须 opacity: 0 起:stamp 生成的代码假设这些前置条件,否则渲染会出现重叠或闪帧。
VO 词级时间戳从哪来?SKILL.md 的完整链路是:script-tokens.json(双层脚本,display 给字幕、spoken 给 TTS)→ HeyGen TTS 生成 voiceover.mp3 + vo-words.json → align-captions.mjs 把 spoken 时间戳映射回 display token 产出 captions.json。该脚本用 Levenshtein 模糊匹配消耗词流(一个 display token 可对应多个 spoken 词,如 "C L I" = 3 个词),无法吸收的差异会打印 MISMATCH 并以退出码 1 终止(脚本 L90-L126)——SKILL.md 要求所有 MISMATCH 必须在构建前清零。若 TTS 只返回音频没有词级时间戳,SKILL.md 给出 whisper 强制对齐的兜底命令,保证字幕永不缺席。
Lint / Check 陷阱清单:全部踩过、全部预解
build-spec 最后一节是"全部命中过、且已给出解法"的陷阱清单,这是从多次实际构建中沉淀的经验值:
| 陷阱 | 解法(build-spec 原文规则) |
|---|---|
| mock 容器存在故意叠放,被布局 lint 拦 | 在故意参与叠放的每个文本块上加 data-layout-allow-overlap,绝不加在 slide 根上;被 playhead/线穿过的元素加 data-layout-allow-occlusion |
| 暗字对比度不达标 | dim 文本最低 rgba(245,246,244,.66)(contrast gate 下限) |
<audio> 缺失 id |
每个 <audio> 都带 id;BGM 用 skill 自带的 house track(assets/bgm.mp3,159s 器乐)拷入项目并挂在独立轨道上,完整挂载属性见上文 bgm 示例(track 2、volume 0.14 压在 VO 之下;用户给了自己的曲子才替换) |
| 改了代码预览却不变 | 预览服务器会缓存 bundle——改完必须重启,然后到原始 comp 页(/api/projects/<id>/preview/comp/index.html)用 window.__player.seek(t) 逐点验证 |
其中 data-layout-allow-caption-zone 这类语义化豁免属性也出现在 scaffold 的字幕容器上(骨架 L51),说明"豁免要加在具体元素上而非根节点"是整条 lint 体系的通用原则。
产物布局与交付门禁
结合 SKILL.md 的项目布局,build-spec 约束的所有文件最终落在一个标准目录里:index.html(单文档母版,场景即 slide、接缝已 stamp)、ledger.json(seam-stamp 输入)、script-tokens.json(VO 与字幕的共同事实源)、vo-spoken.txt、voiceover.mp3 + vo-words.json + captions.json、bgm.mp3,以及 assets/fonts/ 与 assets/bg-pattern-<dur>s.mp4。交付前的门禁依次为:hyperframes check --caption-zone(0 error,dim 文本 ≥ .66 透明度、场景内容避开字幕轨)→ seam-gate.mjs verify 0 fail → 重启预览服务器后 __player.seek 抽查 3–4 个节拍 → 仅在用户要求时才渲染,渲染后从 MP4 抽帧验证字幕在场、背景视频不黑、无过小/冻帧。
适用前提:本文所有坐标、时长与命令均以当前仓库 .agents/skills/changelog-video/ 内的 skill 资产为准(1080×1080、约 45–60s、HeyGen Annie 配音);字体、背景源片与 BGM 都来自该 skill 的 assets/ 目录,scaffold 来自 examples/master-skeleton.html——按 SKILL.md 步骤 0 原样拷贝它们,而不是从上一版视频里复用 index.html,是保证成片"是这个 skill 的品牌"而不是"某个相似项目"的第一道防线。
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 StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00