HyperFrames v0.7.111 解析:内容寻址 Plan v2 分布式渲染、pretext 文本测量钩子与 Catalog 搜索排序
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
本文基于
releases/v0.7.111.md(2026-08-17 发布)展开。该版本的核心变更集中在三处:分布式云渲染默认切换到内容寻址(Content-Addressed)的 Plan v2 传输协议,同时为 v1 保留显式兼容路径;window.__hyperframes运行时新增 pretext 文本测量钩子,让自定义工具获得更精确的排版布局数据;Catalog 搜索排序改为按词出现位置与稀有度加权。下文结合仓库源码逐一拆解这些能力的实现原理与实战用法。
版本概览
v0.7.111 是一次典型的"能力与工程并重"的版本:
- Cloud:分布式计划(distributed plans)默认切换为 v2(PR #3311);
- Core:在
window.__hyperframes上暴露 pretext 文本测量接口(PR #3302); - Catalog:搜索按词出现位置与稀有度进行排名(PR #3312);
- Internal:Studio 中将两个超过 600 行的文件拆分为多个(PR #3313)。
其中前两项是面向开发者与 Agent 的公共能力变更,后两项分别影响目录检索体验与代码可维护性。下面从最核心的分布式渲染传输协议讲起。
一、分布式渲染计划:从 v1 整体目录到 v2 内容寻址传输
1.1 背景:v1 的"整体 planDir"模型
在介绍 v2 之前,需要先理解两个传输协议共享的底层表示——本地执行计划(Local Execution Plan)。
在 packages/producer/src/services/distributed/plan.ts 的模块文档中明确给出了 planDir 的目录布局:
<planDir>/ ├── plan.json ├── compiled/ # compileForRender 输出(自包含) ├── video-frames/ # 每段视频的 JPEG 帧序列(解除符号链接) ├── audio.m4a # 仅当组合包含音频时存在 └── meta/ ├── composition.json ├── encoder.json # LockedRenderConfig └── chunks.jsonbuildLocalExecutionPlan(projectDir, config, executionPlanDir)把既有渲染阶段(编译 → 探测 → 抽帧 → 音频 → 冻结)组合成一个自包含的本地执行目录,供下游分块 worker 消费。它是对本地路径的纯函数:无网络调用,相同输入两次调用产生相同的执行计划哈希(planHash)。v1 的plan()包装器与 v2 的 manifest/CAS 发布器都基于这一表示。
v1 传输模型(plan.ts 中的plan(),现已标记@deprecated)的典型特征是"整体搬运":整个 planDir(可能包含多 GiB 的视频帧序列)被打包传输,且受PLAN_DIR_SIZE_LIMIT_BYTES(2 GiB,见 plan.ts)硬性上限约束。超限会抛出带PLAN_TOO_LARGE非重试错误码的PlanTooLargeError——因为同样的 planDir 每次重试都会超限,工作流适配器会依据code字段决定不自动重试。
1.2 v2 协议描述符:内容寻址布局
v0.7.111 的默认切换意味着分布式云渲染(AWS Lambda、GCP Cloud Run 等)默认产出 v2 计划。协议描述符定义在 packages/producer/src/services/distributed/planProtocol.ts:
| 字段 | v1 | v2 |
|---|---|---|
schemaVersion | 1 | 2 |
artifactLayout | plan-dir-v1 | content-addressed-plan-v2 |
hashSchema | hyperframes-plan-hash-v1 | hyperframes-plan-manifest-hash-v2 |
plan.json中的protocol描述符是兼容性契约:缺失描述符即视为最原始的 v1 布局(acceptsLegacyV1WithoutDescriptor: true保留了对旧 planDir 的重放兼容);一旦存在描述符,就必须完整且被识别,否则抛出PlanProtocolUnsupportedError(PLAN_PROTOCOL_UNSUPPORTED,非确定性失败,重试无法自愈)。readPlanProtocol还会校验 worker 的能力集(DISTRIBUTED_RENDER_CAPABILITIES:planner 同时产出 v1/v2,chunk 与 assembler 同时接受 v1/v2),从而支持不锁步的集群滚动升级。
1.3 v2 传输:小型 manifest + sha256 寻址 blob
v2 的核心设计是将传输与执行分离(见 packages/producer/src/services/distributed/planV2.ts 模块文档):
传输根是一个小型不可变
plan.jsonmanifest,加上按 sha256 寻址的 blob。worker 只选择并物化(materialize)自己角色所需的依赖,然后在校验过的本地布局上调用共享执行函数。
PlanV2Manifest的关键字段包括:
protocol:v2 描述符;planHash:manifest 自身的摘要(hyperframes-plan-manifest-hash-v2\x00前缀 + 规范 JSON 的 SHA-256);sourcePlanV1Hash:保留的本地执行计划哈希(兼容线缆名称,新代码请用getPlanV2ExecutionPlanHash);chunkCount/totalFrames/fps/width/height/format/ffmpegVersion/producerVersion:与 v1 结果对齐的执行元数据;limitations.videoDependencyMode:"exact-rendered-frames"或"full-source-pack";artifacts:每个工件记录path、sha256、sizeBytes、chunks("all"或 chunk 索引数组)与assembler布尔标记。
工件寻址路径由 packages/producer/src/services/distributed/planV2Layout.ts 的planV2BlobPath提供,S3 上对应<prefix>/v2/artifacts/sha256/<前两位>/<完整摘要>的布局(见下文发布器)。
1.4 角色化依赖:chunk 只拿自己需要的帧
v2 相比 v1 最实质的收益是按角色裁剪传输体积。artifactTargets(planV2.ts)维护了一张"fail-safe 策略表":
plan.json、meta/chunks.json、meta/encoder.json→ 所有 chunk + assembler;meta/composition.json、meta/videos.json、compiled/*→ 所有 chunk(assembler 不需要);audio.m4a等音频工件 → 仅 assembler;video-frames/*→ 依据videoDependencies精确到帧;- 未知的未来执行文件 → 默认发给所有角色("过度包含是安全的,静默遗漏新依赖则不是")。
视频帧依赖通过buildVideoChunkDependencies计算:调用引擎的createFrameLookupTable,对每个 chunk 覆盖的每个全局帧求globalTime = (frame * fpsDen) / fpsNum,再查表得到该帧实际激活的帧路径,从而生成"哪个 chunk 需要哪一帧"的映射。若旧版视频元数据缺失,则显式回退到"full-source-pack"(整段源帧打包),保证 fail-closed。
物化侧(materializePlanV2Target)先完整校验所有选中 blob(大小 + SHA-256),再在临时目录原子性地rename到目标目录,并写入.hyperframes-plan-v2.json物化标记;validatePlanV2MaterializedTarget会在执行前再次复核子集完整性。meta/videos.json保证分块渲染与进程内渲染对含视频源的组合做到像素级可比——chunk worker 依据它重建BeforeCaptureHook,避免页面原生<video>元素相对预抽取图像偏离约 1 帧。
1.5 发布器:manifest-last 的 S3 与 GCS 实现
v2 的发布走"manifest-last"流程,确保 manifest 不会在全部引用 blob 持久化之前可见。
AWS 侧实现在 packages/aws-lambda/src/s3PlanV2Publisher.ts(S3PlanV2ArtifactPublisher):
putBlob:校验 sha256 格式与源文件字节数后,流式上传到<prefix>/v2/artifacts/sha256/<digest前2位>/<digest>,并登记已发布摘要;commitManifest:遍历 manifest 中所有工件摘要,任一摘要未持久化即抛出PlanV2IntegrityError;随后在临时目录写 manifest.json 并上传到<prefix>/v2/manifest.json,状态置为committed;abort:未提交时状态置为aborted,远程 CAS blob 不可变,可被重试复用,未提交则不可达,由 bucket 的中间对象生命周期策略过期清理。
GCP 侧有对称实现 packages/gcp-cloud-run/src/gcsPlanV2Publisher.ts。两组发布器各自配套测试(s3PlanV2Publisher.test.ts、gcsPlanV2Publisher.test.ts)覆盖"manifest 先于 blob 可见"的竞态路径。
1.6 迁移与兼容:v0.7.111 下的升级路径
- 新集成请使用
planV2()或planV2WithPublisher()(planV2.ts),后者接受任意实现PlanV2ArtifactPublisher接口的发布器(本地目录、S3、GCS 或其他持久化 CAS); planV2()直接产出一个本地 v2 目录:先构建共享本地执行表示作为"planner 私有暂存",发布时把 2 GiB 传输上限禁用(executionPlanSizeLimitBytes: Number.MAX_SAFE_INTEGER),因为不再产出整体归档;- v1 的
plan()与createPlanV2FromV1/publishPlanV2FromV1兼容别名仍受支持,sourcePlanV1Hash线缆字段保留以便输出/重放关联(planV2.ts); - 显式升级期间(混合舰队),
DISTRIBUTED_RENDER_CAPABILITIES保证 chunk/assembler 同时接受 v1 与 v2;目标路径层面,worker 在消费前通过readPlanProtocol判别协议,v2 必须先物化才能以 v1 布局访问(readPlanProtocolV1会拒绝未物化的 v2)。
二、pretext:window.__hyperframes上的文本测量钩子
2.1 为什么需要它:避开 DOM reflow 与不确定性的双刃剑
把文本写进 DOM 再读回来测宽,会强制触发 reflow——逐帧做既慢,又是不确定性来源(测量结果依赖读取时机)。v0.7.111 把基于 canvas 的测量面挂到运行时上,组合可以在任意帧测量字符串来决定容器尺寸、字号或断行位置,而无需扰动布局。
实现位于 packages/core/src/text/pretext.ts,围绕@chenglou/pretext封装,并在 packages/core/src/runtime/entry.ts 中随window.__hyperframes一起在脚本求值期立即挂载(早于DOMContentLoaded,因为字体适配常在脚本求值阶段就要跑)。
2.2 暴露的 API 面
| 方法 | 用途 |
|---|---|
prepare(font) | 为某个 CSS 字体(如"700 48px Inter")测量字符串,返回 prepared 对象 |
layout(prepared, width) | 计算 prepared 字符串在给定宽度下的行数与总高度 |
prepareWithSegments(font) | 同prepare,但保留分段以便测量各段宽度 |
measureLineStats(prepared, width) | 行数 +maxLineWidth(最宽的一行) |
measureNaturalWidth(prepared) | 字符串在单行不换行时占据的宽度 |
2.3 调用位置的注意点(源码中的关键注释)
prepare/prepareWithSegments需要 canvas,只能在浏览器中调用,Node 中不可用;而 prepared 字符串下游的一切都是纯算术,足够便宜,可以逐帧执行。- 刻意不暴露
clearCache与setLocale:二者会改变进程级全局状态,影响"同一文件每次渲染同一视频"的确定性保证。缓存按(segment, font)键控,segment 来自Intl.Segmenter的词语粒度切分,因此缓存随组合的词汇量增长而非随帧数增长——计数器或打字机逐帧重测会复用同一批条目,只有真正的新词才会分配内存,内存上界即组合的词汇表。 - 增量游标 API(
layoutNextLine、walkLineRanges等)尚无调用方,故暂不暴露。
2.4 与既有排版工具的关系
引擎的fitTextFontSize本身就是基于prepare+layout构建的(见 packages/core/src/text/pretext.ts 注释)。因此自定义工具获得这一测量钩子后,可以复刻引擎级排版决策(如精确字号缩放),并在浏览器端测试契约 packages/core/src/text/pretext.test.ts 与脚本 packages/engine/scripts/test-fitTextFontSize-browser.ts 中验证同样的测量语义。
三、Catalog 搜索:按词出现位置与稀有度排序
3.1 从子串匹配到词汇打分
v0.7.111 的 Catalog 搜索排序变更,实现在 packages/cli/src/registry/localSearch.ts。该模块文档开门见山:此前的子串测试description.includes(query)无法回答"让节奏突然变快"这类语义查询——因为没有任何描述包含该短语,诚实的结果是零匹配。新的打分器在离线、无模型、无网络的前提下用共享词汇作答。
打分管线(tokenize+rankByWords):
- 小写化,按
[a-z]+切词; - 丢弃停用词(
STOP集合:the/a/an/and/or…); - 丢弃长度 ≤ 3 的词;
- 复数折叠到单数(
PLURAL_RULES:stories→story、matches→match,并保护press/status/axis这类非复数的尾部 s); - 共享词计数除以条目词数的平方根——这个除数至关重要,否则最啰嗦的条目会凭表面积赢下所有查询。
rankByWords返回全部条目(零分排最后),由调用方决定截断点。
3.2 两个承重的加权设计
- 强字段权重:命中条目名称/标题的词按
STRONG_FIELD_WEIGHT = 3计分,远高于描述命中。没有它,搜 "typewriter effect on a title" 时字面叫typewriter的条目会排到第七名,落后于只是顺带提到打字效果的条目。作者敲出某个效果的名字是最强信号,不能被平均掉。 - 稀有词加权(IDF 语义):每个词按其稀有度加权——这就是发布说明中"rarer words rank more accurately"所指的机制。常见词(如
count)权重低,罕见复合词(如countdown)保留高逆文档频率权重。
3.3 复合词双向展开:只增信号、不发明匹配
expandCompounds处理"一个想法两种拼法"的问题:countdown是一个 token,count down是两个,两者永远无法互中。规则是:
- 查询 token 长度 ≥ 6 时,若从中间切开的两个部分都是目录词汇表里的词,则把两部分以推断权重
INFERRED_TOKEN_WEIGHT = 0.35加入(复合词本身始终保留); - 相邻查询 token 拼接后若在词汇表中,也以推断权重加入;
- 两部分都不是目录词(如
timer从未出现在任何条目中)则保持原样——这是拓宽措辞,不是编造匹配。
推断权重 0.35 的存在是为了防止"半个词"喧宾夺主:以全强度让type去命中type-match-cut的名称,会压过typewriter命中typewriter名称的真信号。配套的 packages/cli/src/registry/localSearch.test.ts 固定了这些排序行为,另有hasNoSearchableTokens帮助调用方区分"查询无可用 token"与"目录确实没有匹配"(例如非英文脚本查询不会再被误报为缺失)。
四、内部工程:Studio 600 行上限拆分
v0.7.111 的最后一项是纯内部重构(PR #3313):将 Studio 中两个超过 600 行上限的文件拆分为多个。这是仓库既有行数纪律的延续——相关的被拆分文件(如属性面板、图层面板等模块)可在 packages/studio/src/components/editor/ 下看到拆分后的形态。该变更不改变任何对外行为,属于可维护性基建。
小结
v0.7.111 的三项公共能力分别解决一个问题:
- Plan v2 内容寻址传输让分布式云渲染摆脱 2 GiB 整体归档上限,通过 sha256 寻址 + 角色化物化让 chunk worker 只下载自己实际渲染的帧,manifest-last 发布保证引用完整性,同时以协议描述符 + 能力集为混合舰队升级留出显式兼容通道;
- pretext 文本测量钩子把引擎内部的 canvas 测量能力开放给组合脚本与自定义工具,以"prepare 一次、算术逐帧"的模式兼顾性能与渲染确定性;
- Catalog 搜索排序用"名称 3 倍加权 + 稀有词加权 + 复数折叠 + 复合词推断展开"的轻量打分器,离线显著改善目录检索的语义命中率。
对需要升级分布式渲染管线的开发者,建议优先核对 worker 的协议能力集声明(planProtocol.ts)与发布器实现(s3PlanV2Publisher.ts / gcsPlanV2Publisher.ts),并参考 planV2.test.ts 与 planProtocol.test.ts 中的边界用例完成迁移验证。
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考