WP13:单窗口放映状态机与时钟
状态:完成(2026-10-17)。阶段:M1。主责:Runtime。 上层工作项:R02(纯调度);详见模块计划。本包是该工作项的实施拆分,不是另一份配置契约。
前置与输入
前置完成:WP01。 字段与行为以统一契约为准;所有依赖编号见执行清单。
修改范围与交付
- 修改范围:独立控制器的状态、一次性计时、剩余毫秒、generation、开始/暂停/继续/停止、末页结束和循环。
- 交付物:不依赖页面 DOM 的可确定性测试的调度核心。
- 提交边界:不在该包绑定 Vue Router、媒体或双窗口,也不开放自动开始入口。
完成标准
- [x] 实现及评审覆盖上述修改范围,未超出提交边界。
- [x] 行为验收:只有一个推进句柄;旧回调作废;暂停保留剩余时间;手动页等待;单页循环有完整周期。
- [x] 验证范围:虚拟时钟覆盖全部状态迁移、重复命令、边界和资源清理。
- [x] 关联验收:A09、A11、A12、A13;见联调与验收计划。本包只覆盖纯调度层,真实联调在 WP21。
- [x] 记录代码版本、实际测试命令/结果、未运行项及原因,产物保存到任务专属临时目录。
- [x] 回填本卡状态与证据;阶段能力是否开放由对应阶段出口包决定,单包完成不等于整阶段交付。
建议提交标题:WP13:单窗口放映状态机与时钟。通常按一个可评审 PR 实施;若实际改动仍过大,先按输入/输出再拆子包,保留父编号和依赖。
测试环境与命令选择遵循 dev-start-test,不因执行本卡重复启动服务或重置开发数据。
实施记录
- 交付:playback-controller.ts 与虚拟时钟测试 playback-controller.test.ts。控制器不导入 Vue、Vue Router 或 DOM,仅依赖注入的
PlaybackClock、页面目录与导航回调。 - 状态机:
stopped、preparing、playing、paused、ended、blocked、error;重复开始/暂停/继续/停止/就绪均幂等,错误与受阻可重试并重新等待就绪。 - 时钟与句柄:
performance.now()单调计时,任意时刻最多一个setTimeout句柄;暂停保存剩余毫秒,继续按剩余时间补足,不做固定 interval 累计。 - generation:开始、停止、页面切换、失败与单页循环都会递增;就绪/错误/受阻信号必须同时匹配路径与 generation,旧计时与旧导航 Promise 的迟到回调被丢弃。
- 页面语义:同路径查询参数变化不重置计时;隐藏页在构造目录时过滤;手动模式页保持
playing但不创建计时;播放中手动跳页回到preparing,暂停中跳页保持暂停并为新页重置完整周期;停止/结束后当前页就绪状态保留,重新开始只重置停留时钟。 - 边界:末页且不循环进入
ended;多页循环调用导航回到第一个可见页;单页循环不产生路由切换,但每轮重新建立完整停留周期;路径不在可见目录时不自动推进。 - 验证:
pnpm --dir runtime run check通过;专项vitest run src/runtime-shell/playback/playback-controller.test.ts20 passed;eslint 通过;Runtime 全量 130 个文件中仅既有runtime-diagnostics-workspace-pool.test.ts出现一次 5s 并发超时,单独重跑 4 passed(1000 passed / 3 skipped)。证据保存在.tmp/wp13/。 - 未运行项:Router/DOM 就绪桥接与真实过渡信号(WP15)、入口控制条与可见性(WP16)、真实浏览器 A09/A11/A12/A13 联调(WP21)、媒体与双窗口(M2)。本包未开放自动开始入口,也未改变当前能力声明。