HyperFrames v0.6.115 全量解读:Slideshow 演示技术栈落地与 SDK 交互能力扩展
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
HyperFrames 是一个「Write HTML. Render video. Built for agents.」的开源项目:编写 HTML 即得视频。v0.6.115 版本于 2026-06-20 发布,核心成就是一次性交付了完整的 Slideshow(幻灯片演示)技术栈——从 schema、parser、controller 到<hyperframes-slideshow>Web Component、Presenter 模式、Studio 分支编辑面板和hyperframes presentCLI 命令;同时 SDK 层面补齐了元素增删、弹性时序(elastic timing)、关键帧写入、变量/Brand 模型与图像 Alpha 命中测试等能力。读完本文,你将掌握 slideshow manifest 的完整结构、hyperframes present的实战用法,以及各层实现对应的仓库源码位置。
版本概览:一次“演示能力”的完整闭环
v0.6.115 的发布说明非常清晰地概括了本次变更主线:Full slideshow stack——schema、parser、controller、<hyperframes-slideshow>web component、presenter mode、branching editor panel,以及hyperframes presentCLI 命令。换句话说,从「用 HTML 描述一页页幻灯片」到「在浏览器里像演讲者一样翻页、进分支、开观众屏」,这一整条链路在本版本中首次形成闭环。
SDK 侧同步扩展了面向 Agent 的编辑能力:元素增删操作(add/remove element ops)、弹性时序(elastic timing)、关键帧写入(keyframe writers)、变量与品牌模型(variables/brand model),以及图像 Alpha 命中测试(image-alpha hit-testing)。此外还修复了跨 realm iframe 媒体处理、逐元素音频静音、querySelector 注入与 DOM 编辑一致性等问题。
Slideshow Schema:JSON Island 与五类核心类型
Slideshow 的“源文件”仍然是一个普通 HTML 组成(composition),slideshow 信息以 JSON 形式内嵌在<script type="application/hyperframes-slideshow+json">标签中,这个内嵌块在代码中被称为island。
该媒体类型的常量定义在 packages/parsers/src/slideshow/parseSlideshow.ts:
export const SLIDESHOW_ISLAND_TYPE = "application/hyperframes-slideshow+json";类型定义集中在 packages/parsers/src/slideshow/slideshow.types.ts,包含五类核心模型:
- SlideshowManifest:最外层对象,含
version(当前 schema 版本号SLIDESHOW_MANIFEST_VERSION = 1,用于未来迁移)、slides(主线幻灯片数组)与可选的slideSequences(分支序列数组)。 - SlideRef:单张幻灯片的作者视角描述,字段包括
sceneId(必填,指向 composition 中的场景)、可选的startTime/endTime(覆盖场景时间范围)、notes(演讲者备注)、fragments(分段揭示的时间点数组)、hotspots(热点列表)以及autoplay(进入该页时自动播放页内第一个视频,默认 false,slideshow 本身永不自动前进)。 - SlideHotspot:热点 = 页面上的可点击区域,
id、label、target(引用某个 SlideSequence.id)、可选region(占整页百分比坐标{x, y, w, h})。 - SlideSequence:分支序列,
id+label+slides数组,供 hotspot 跳转进入。 - ResolvedSlide / ResolvedSlideshow:解析后的形态,
fragments与hotspots被默认填充、时间范围被解析为确定的start/end。
一个典型的 island 示例(来自 packages/parsers/src/slideshow/parseSlideshow.test.ts 的测试夹具):
<script type="application/hyperframes-slideshow+json"> { "slides": [ { "sceneId": "a", "fragments": [2.0, 1.0], "hotspots": [{ "id": "h1", "label": "Why?", "target": "deep" }] }, { "sceneId": "b" } ], "slideSequences": [ { "id": "deep", "label": "Deep dive", "slides": [ { "sceneId": "c" } ] } ] } </script>Parser 与 Resolver:从 JSON 到可导航的时序
parseSlideshowManifest(html)负责从 composition HTML 中提取 island 并校验结构(packages/parsers/src/slideshow/parseSlideshow.ts)。它的行为在测试中非常明确:
- HTML 中没有 island 时返回
null; - island JSON 非法或结构不符(如顶层是数组、
sceneId不是字符串)时抛错; slideSequences存在但不是数组时抛错。
resolveSlideshow(manifest, scenes)则把「场景时间轴」与「幻灯片引用」绑定起来(parseSlideshow.ts),规则如下:
- 时间解析:
startTime/endTime都显式给出则直接用;都没有则以场景的start与start + duration填充;只给其一则用场景补齐另一边,若场景缺失会报出明确的startTime/endTime cannot be resolved错误。 - fragments 规范化:去重、升序排序,并校验每个 fragment 是否落在该页时间范围内。
- hotspot 目标校验:hotspot 的
target必须指向一个已存在且非空的分支序列,否则报错。 - 主线重叠校验:主线幻灯片按开始时间排序后,相邻页不允许时间重叠。
这些校验同时被 lint 规则复用:@hyperframes/lint的 slideshow 规则(packages/lint/src/rules/slideshow.ts)会扫描 composition 中带data-composition-id的场景元素、解析其data-start/data-duration时序,然后对 island 执行同样的resolveSlideshow,把问题报告为slideshow_invalid或slideshow_unresolved_ref两类 error,并给出修复建议(例如“为每个 sceneId 提供显式 startTime/endTime”)。
Controller 状态机:栈式导航、分支与片段推进
解析完成后的数据交由SlideshowController驱动(packages/player/src/slideshow/SlideshowController.ts)。它是一个纯状态机,通过PlayerPort(seek/play/pause/stopMedia/playSceneMedia/currentTime/onTimeUpdate)与播放器解耦:
- 栈式结构:内部用
StackFrame[]维护导航栈,栈底永远是MAIN主线;进入分支即压栈,back()即弹栈回到父级。breadcrumb(面包屑)由此生成。 - 进入页面的落点:
enterSlide(index)会跳到该页的“首个驻留点”——有 fragments 时停在第一个 fragment 时间点,没有 fragments 时停在页面中点的稳定帧(而不是slide.end,因为那是下一页场景的起点边界,否则第 1 页会渲染出第 2 页的内容)。 - 边界能力:
canPrev/canNext不仅看当前序列内是否还有上一页/下一页,还看是否处于分支中(分支末尾可继续向父级前进)。 - 可观测:
onChange(cb)订阅任何位置变化,驱动组件重渲染与观众端同步。
导航完全是 seek 驱动的(注释明确说明“navigation is seek-driven”),因此dispose()无需清理订阅。
<hyperframes-slideshow>Web Component:双模式与交互细节
packages/player/src/slideshow/hyperframes-slideshow.ts 实现了HyperframesSlideshow自定义元素,是演示体验的前端载体。它围绕「presenter(演讲者)/ audience(观众)」双模式工作,模式由mode属性或 URL 查询参数?mode=audience决定(resolveMode())。
键盘与手势
- 方向键:
ArrowRight/ArrowLeft前进后退,即使焦点不在 deck 上也能响应(但页面上同时存在多个 deck 时,只有获得焦点的那个响应,避免按键驱动所有实例)。 - 空格 / Backspace:前进 / 后退,但要求 deck 真正持有焦点(避免干扰页面滚动和历史记录默认行为)。
- P:直接触发
present(),即使 controller 尚未绑定也立即生效——这是刻意设计,让慢加载页面上快捷键“必须立刻可用”。 - F:切换全屏。
- 触屏:水平滑动翻页,要求
|deltaX| > 40px且水平位移占主导,避免对角线滚动误触。 - 文本输入豁免:焦点在 INPUT / TEXTAREA / SELECT 或 contenteditable 上时按键一律不劫持。
- iframe 键盘转发:当演讲者点击幻灯片导致焦点进入 composition iframe 后,顶部窗口的 keydown 不再触发;组件会在同源 iframe 的
contentWindow上挂同样的监听并随每次load重挂,跨源 iframe 则降级为警告并保留窗口级快捷键。
present():打开同步观众端
present()(hyperframes-slideshow.ts)使用 URL API 拼接?mode=audience打开新标签页(避免#fragment拼接陷阱),通过隐藏<a rel="noopener noreferrer">的点击而非window.open(features)打开——后者的弹窗窗口在完全被遮挡时可能冻结,影响屏幕共享。随后切换到 presenter 布局,并通过BroadcastChannel(SlideshowChannel)以 250ms/750ms/1500ms/3000ms/5000ms 的退避节奏向观众端广播当前位置。
观众端收到消息后调用controller.syncTo(sequenceId, slideIndex, fragmentIndex)对齐到同一页。媒体(video/audio)也通过该通道同步:play/pause/seeking/ratechange/volumechange/ended/timeupdate等动作都会广播,观众端默认静音播放以规避自动播放限制,被浏览器拦截时会在页面底部弹出「Play audience media muted」解锁按钮。
自动交互与媒体接线
组件会自动为内部所有<hyperframes-player>加上interactive属性(否则播放器默认pointer-events: none会挡住点击),并通过 MutationObserver 覆盖动态挂载的播放器;用OwnedMediaRegistry周期扫描 iframe 内的媒体元素接线,避免重复监听并支持节点移除后的自动解绑。sound属性决定是否渲染静音按钮,observedAttributes监听sound/mode以便运行时切换即时生效。
hyperframes presentCLI:本地起一个带观众同步的演示服务器
hyperframes present命令实现在 packages/cli/src/commands/present.ts,用一个 Hono 服务器提供三部分内容:/player.js、/slideshow.js(从@hyperframes/player构建产物读取)以及/composition/*(原样托管 deck 目录文件,带isSafePath防目录穿越)。命令内置示例(--help可查):
# 展示当前 deck hyperframes present # 指定项目目录 hyperframes present ./my-deck # 自定义端口 hyperframes present --port 8080 # 不自动打开浏览器 hyperframes present --no-open # 指定浏览器可执行文件 hyperframes present --browser-path /usr/bin/chromium完整参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dir | positional | 当前项目 | 项目目录 |
--port | string | 3004 | 端口,被占用时自动改用空闲端口并提示 |
--open | boolean | true | 是否自动打开浏览器 |
--browser-path | string | — | 浏览器可执行文件路径 |
--user-data-dir | string | — | Chromium 用户数据目录(依赖--browser-path) |
--remote-debugging-port | string | — | Chromium 远程调试端口(依赖前两者) |
命令启动前会做两项硬校验:@hyperframes/player构建产物必须存在(否则提示先bun run --cwd packages/player build);deck 的index.html中必须包含 slideshow island,且其 JSON 必须可解析——因为 island 非法时组件连 Present 按钮都不会渲染,这里选择“启动即失败”而不是“静默无 UI”。
生成的 presenter 页面结构(buildPresentPage)即标准用法示例:
<hyperframes-slideshow tabindex="0" sound> <hyperframes-player interactive src="/composition/index.html"></hyperframes-player> <script type="application/hyperframes-slideshow+json"> { "slides": [ ... ] } </script> </hyperframes-slideshow>页内还内置了一套sfx/音效播放逻辑:advance.mp3、fragment.mp3、branch-enter.mp3、back.mp3在父文档中播放(iframe 内无手势无法自动播放),音量预设为 0.4~0.45,首次交互时预热解锁,并通过hf-sound事件与静音按钮联动。命令输出的提示还包括屏幕共享建议:Meet 应分享Audience 标签页而非整个窗口,Zoom 桌面版则把 audience 标签拖成独立窗口后共享该窗口。
Studio 分支编辑面板:可视化编排主线与分支
配套的 Studio 能力由多份代码支撑:分支编辑面板(SlideshowPanel)、manifest 持久化(setSlideshowManifest)与标签页状态管理(useSlideshowTabState),分布在 packages/studio/src/components/panels/SlideshowPanel.test.ts、packages/studio/src/utils/setSlideshowManifest.ts 与 packages/studio/src/hooks/useSlideshowTabState.test.ts。setSlideshowManifest在 HTML 中定位并替换application/hyperframes-slideshow+jsonisland,坏 JSON 会给出清晰错误。
Studio 侧的使用流程(对应 docs/studio/slideshows.mdx):
- 搭主线:在 Slides 面板勾选进入主演示的场景并排序;在 Slide Inspector 中补充演讲者备注(备注在短暂停顿后保存,其他编辑即时保存并进入项目历史)。
- 加片段揭示:把播放头移到需要停顿的时刻,点击Mark生成 fragment 驻留点;每个要点并不都需要驻留点。
- 加分支:在 Branches 中命名分支并指派幻灯片;在画布上选中热点按钮/对象,打开 Hotspot Tool 选择目标分支、填写标签并Make hotspot。删除分支会连带删除所有指向它的热点(需确认),源场景保留。
- 本地演示验证:
npx hyperframes present <project-directory>,逐项测试前后翻页、每个 fragment、每个 hotspot、每个分支及返回路径;点击Present或按P打开同步观众视图。
对 Agent 侧,技能库中定义了独立的 slideshow route(skills/hyperframes/references/routes/slideshow.md):输入是讲稿/大纲/现有页面,输出是“可运行的 composition + 供SlideshowController使用的 JSON island”,交付物是可导航的 deck 而不是 MP4;触发词如 “make a pitch deck”“interactive presentation”。
SDK 编辑能力扩展:增删、弹性时序、关键帧写入、品牌模型与 Alpha 命中测试
v0.6.115 的 SDK 扩展集中在 packages/sdk/src/session.ts 与 packages/sdk/src/adapters/iframe.ts,全部可逆、可进入 undo/redo 历史:
- 元素增删:
addElement(parent, index, html)返回新铸造的hf-id,其逆操作即removeElement(id);removeElement会生成对应的撤销 op。 - 关键帧写入(keyframe writers):
addWithKeyframes(targetSelector, position, duration, keyframes, ease?)与replaceWithKeyframes(...)直接以“位置 + 时长 + 关键帧数组”创建/替换 GSAP 动画,返回animationId;底层由@hyperframes/core的 acorn writer 生成 GSAP 脚本补丁(当前默认仍走 recast,acorn 写入器由 cutover 标志门控,见版本 Internal 部分)。 - 弹性时序(elastic timing):
setHold(id, hold)与setTiming(...)配套使用。实现位于 packages/core/src/compiler/timingResolver.ts,其原则是:绝不缩放动画内容,弹性 hold 只是扩展驻留窗口;align-on-adjust——只有显式锚定到某个词的元素才被“词锁定”。对被锚定元素,enterAt = 词开始时间 + enterOffset,holdDuration = max(0, 槽位结束 - (enterAt + 入场时长 + 退场时长)),从而让元素精确贴合配音词的节奏。 - 变量与 Brand 模型(WS-B):变量子系统支持对象值字体/图片,并提供 B1 JSON 模型,变量读写与校验代码位于 packages/core/src/runtime/getVariables.ts 与 packages/core/src/runtime/validateVariables.ts。
- 图像 Alpha 命中测试(WS-G):同源 iframe 的命中测试从“元素矩形命中”升级为“像素级 Alpha 命中”(packages/sdk/src/adapters/iframe.ts):把图像绘制到复用 canvas 后按点取 Alpha 值,透明区域视为可穿透。实现细节包括:canvas 跨源被污染时回退为不透明并输出警告、超大图使用“按需绘制 + 缓存复用”而非每次新建 OffscreenCanvas、路径级尺寸防护。
修复项与内部改动一览
版本还包含一批质量修复,其中与 slideshow 及播放体验直接相关的有:
- 逐元素音频静音(core):预览时按元素静音,避免某个慢解码音轨导致整段被静音([packages/core] 相关逻辑)。
- querySelector 注入修复(core, studio):在属性选择器中转义用户值,杜绝把用户内容拼进 selector 造成注入。
- 跨 realm iframe 媒体处理(player/slideshow):composition iframe 属于另一 realm 时,用鸭子类型判断而非
instanceof处理事件目标与媒体元素。 - DOM 编辑一致性(cutover parity):恢复 Studio 与 SDK 编辑链路的一致性(sdk, studio)。
- Engine 在包含式 clip 结尾保持最后一帧(engine):修复视频 clip 在结束边界处丢帧问题。
- Slideshow 分 PR 评审回填:多轮 review findings(#1580–1584、#1594)与 present 媒体控制修复。
- 发布流水线:tag 单调性守卫收窄到 HEAD 可达的 tag。
内部与发布相关的改动还包括:示例仓库新增三个 slideshow demo(airbnb deck、startup pitch、fixture),以及@hyperframes/sdk正式发布到 npm。
上手路径:从仓库代码到你的第一场演示
若想基于本仓库源码亲自跑通 v0.6.115 的演示链路,可参考以下步骤(仓库只读,均为本地构建/运行):
- 构建播放器产物:
bun run --cwd packages/player build。 - 准备一个带 slideshow island 的 deck 目录,结构参照
hyperframes present生成的 wrapper 页面:<hyperframes-slideshow sound>内嵌<hyperframes-player interactive>与application/hyperframes-slideshow+jsonisland。 - 运行
bun run --cwd packages/cli present ./my-deck(或构建后使用hyperframes present),按 P 打开观众端验证同步。 - 用 lint 规则做静态校验(
@hyperframes/lint的 slideshow 规则会直接报告 island 结构错误、未解析 sceneId 与时间重叠);解析与解析测试可参考 packages/parsers/src/slideshow/parseSlideshow.test.ts 与 packages/lint/src/rules/slideshow.test.ts。
至此,v0.6.115 从「描述」到「解析」再到「演示」的完整链路已经打通:schema 定义数据的形状,parser/resolver 负责把引用解析为确定时序,controller 以栈式状态机驱动导航与分支,web component 提供键盘/触屏/双模式交互,CLI 把它变成一场可分享的实时演示,而 Studio 面板与 SDK 让人类与 Agent 都能高效编排这套内容。
【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考