17 KiB
CORE-17 键盘、手势、焦点与全局事件
状态:完成。以下阶段记录保留当时的进度;当前交付结论以末尾最终验收为准。
先核实公开键盘注册、焦点及跨 document 行为,保留回调 this、参数、顺序、可变 keys 数组和链式返回。后续处理原生监听器注册/释放、全局 document/window 重绑、点击/悬停/移动/触摸,以及 resize/view/RAF 调度归属。
源码观察:Hotkey 的输入判断固定读取全局 document.activeElement,重复 init 会重复订阅;Events 的重绑先删除旧监听,失败期间可能保留部分新监听;触摸只处理结束、缺少取消。尚未复现的点不登记为已确认缺陷。BASE-PERF-01 的 pending resize 已在 CORE-04 修正,本任务继续回归,不重复宣称新修复。
第一批:Hotkey
hotkey.ts 保留 art/keys 自有字段、init/add/remove 原型方法、keys 的普通对象原型与数组身份、链式返回、回调 this=art 和原同步事件顺序。input/hotkey-defaults.ts 管理稳定的默认回调;keyboard-focus.ts 判断事件 document 与编辑区域;hotkey-types.ts 描述最小泛型宿主。保持回调数组的旧实时遍历语义,注册/删除中途修改数组仍直接影响本次派发;不借本次迁移静默改成快照模型。
发布/候选初始基线 24 项中,15 通过、候选目标 9 失败,复现 iframe 与 Shadow DOM 输入被快捷键拦截,以及重复 init 触发两次。迁移后 24 项通过。随后补充已发布包的 prototype-named key 复现:constructor/toString/proto 的注册会因继承属性报错;候选保持普通 keys 原型,用自身属性注册消除碰撞。分别登记 BASE-DOM-16/17 与 BASE-LIFE-23,当前均保持 open,等待完整 CORE-17 安装包出口。
键盘判断优先读取实际事件 document,回退到 player 当前 ownerDocument,并检查深层 shadow focus/composed target;input/textarea/select、继承或 plaintext 编辑区、IME 组合输入不触发快捷键,仍保留通用 keydown 通知。修饰键规则保持。实例 scope 清理订阅,销毁中回调不再继续后续回调或 hotkey/keydown 通知;同步回调错误仍保留身份。重复 init 复用默认回调,并可恢复显式移除的默认键;移动端默认不初始化,原手动 init 路径保留。
新增 9 项 Node 测试(与已有播放测试合跑 23 项),覆盖公开形态、默认与自定义键、重复初始化/恢复、回调上下文与数组修改、销毁、错误、修饰键/IME 和空 activeElement。类型用例保留泛型扩展与 this,拒绝错误事件/接收者类型。浏览器扩展首轮 45 项通过;最后补入移动 UA 的旧手动 init 行为,并与显示模块组合回归。未新增依赖,test:unit 加入键盘用例。
Events 注册/重绑、点击/手势/悬停以及 view/resize/RAF 仍未完成;本阶段不标记整个 CORE-17 完成,也不作完成任务提交。
本批最终 CI 278 项(249 单元、4 工程、25 基线)、168 个生产 TS 文件和五组消费者严格检查通过;键盘与显示模式组合 351 项三浏览器通过,含键盘 51 项。核心三个格式和 i18n 构建通过,docs/compiled 一致;独立重建候选并逐项核对浏览器 manifest,见 键盘阶段证据。这不是全量源码浏览器或最终安装 UMD/legacy 验收。回退本批时恢复 hotkey.js,删除 input/hotkey 与 keyboard-focus 模块和对应测试,再通过仓库脚本重建;保留基线缺陷记录。
第二批:原生监听器与全局重绑
events/listener-registry.ts 管理原生监听器注册、capture 快照、数组失败回滚、abort 和清理;events/index.js 仍保留旧门面、bound proxy/hover、destroyEvents Set 与初始化顺序。返回的 disposer 直接执行后移除记录,重复执行无副作用。移除失败警告并保留记录供后续 remove/destroy 重试,其他监听器继续清理;自定义 target 永久拒绝移除仍属于明确能力边界。
保留原回调交给原生 EventTarget,因此 this、event 身份、外部 removeEventListener、原生 once/重复注册语义保持。once 触发后,记录在显式 disposer 或实例销毁时清除;不为了提前清记录包装用户回调。相同回调的重复注册不做引用计数。abort 清理记录,预先 aborted 的信号不注册。测试还发现本次迁移曾误拒绝 null options:旧包三浏览器通过、初始候选三浏览器失败,已恢复原生默认行为并保留回归用例。
原生注册初始基线 24 项中旧包 12 通过、候选目标 12 失败;迁移后配合生命周期 54 项通过。补入 null、once/abort 后,与键盘/生命周期合跑 117 项通过。新增 9 项 Node 测试覆盖原生身份、可变选项、对象监听器、取消、注册后抛错、销毁中注册、清理失败重试、手动 destroy 后复用和重复注册。确认 BASE-LIFE-24,保持 open 等待整个 CORE-17 安装包出口。
events/globalInit.ts 与 global-types.ts 拆出全局监听组及最小宿主。先注册完整新组,成功后再切换活动组并释放旧组;失败回滚新组,保留旧组可用。暂存组不转发事件,代次检查确保嵌套重绑由更新调用生效,销毁中断的注册不会晚到激活。保留原事件名称/身份、void 返回、document/window 独立回退规则(单传 document 不隐式更换 window)及成功重绑后不再接收旧目标事件。
实际 iframe 的初始基线 12 项中 9 通过、候选失败回滚目标 3 失败,证实旧实现失败后失去旧 document 并保留部分新监听。迁移后与键盘合跑 63 项通过;再补浏览器嵌套重绑/销毁检查。5 项 Node 测试覆盖 ownerDocument 变化、window 注册失败、暂存期间事件、嵌套重绑与销毁。确认 BASE-LIFE-25,保持 open;两组共 14 项 Node 测试通过。未新增依赖,已把两组测试接入 test:unit。
以上键盘阶段指纹只对应第一批历史源码,不能代表第二批。第二批最终 CI 292 项(263 单元、4 工程、25 基线)、171 个生产 TS 和五组消费者严格检查通过;键盘/原生监听器/全局重绑/生命周期组合 135 项三浏览器通过,无重试、跳过或页面错误。核心三个格式和 i18n 已重建,docs/compiled 字节一致;独立重建并逐项核对候选 manifest,见 监听器及重绑阶段证据。这是源码阶段的局部组合回归,不是最终安装包全量验收。
点击、悬停、移动、手势和 resize/view/RAF 仍待实施。回退本批时恢复 events/index.js/globalInit.js,删除新的 listener-registry/globalInit/global-types TS 与测试,并用仓库脚本重建。整个 CORE-17 仍为 doing,不作完成任务提交。
第三批:点击、焦点、悬停与移动
clickInit.ts、hoverInit.ts、moveInit.ts 迁移为 TS,pointer-types.ts 定义最小宿主/监听接口,pointer-focus.ts 独立管理文档焦点订阅。保留原始 Event、类名变化先于 hover 通知、isInput 仅识别 INPUT 的旧语义、单/双击闭区间阈值、第三次点击重置、动态静态配置与嵌套同步派发。Emitter 的 this 仍由显式 ctx 参数决定,不能套用 hotkey 的 this=art 约定。销毁移除自有焦点订阅,并阻止失效的点击/悬停/移动回调继续修改状态。
确认 BASE-LIFE-26:桌面 click 或移动 UA dblclick 回调销毁实例后,旧实现仍调用原生媒体 play。候选在用户事件回调返回后检查实例生命周期,同时在处理入口拦截已关闭实例;单元测试也验证销毁时不再进入 fullscreen 默认动作。该修正不改变正常单击播放、双击行为、用户异常身份及内部播放拒绝处理。
最初浏览器夹具错误地替换不可重定义的 toggle 属性,导致 18 项失败;修正为观察底层 video.play 后,暴露出默认 Emitter ctx 的错误测试假设。保留了两次失败报告,不把这些计为产品缺陷;第二次报告中另有 6 项候选销毁目标失败,均复现上述真实问题。最终测试显式传 ctx,保留旧不可配置属性不变。修复后 24 项三浏览器契约通过,再新增实际鼠标点击、播放/暂停、焦点和 hover 的 6 项浏览器验证通过。新增 8 项单元测试通过,包括阈值、嵌套派发、销毁、异常与自有订阅清理,已接入 test:unit,未新增依赖。
最终 CI 300 项(271 单元、4 工程、25 基线)、176 个生产 TS 与五组消费者严格检查通过。首次组合浏览器为 164 通过、1 项 WebKit 键盘定位失败;该测试设置初始 currentTime 后未等待定位完成,现补充 paused/seeking 前置条件,并断言方向键确实进入 hotkey。保留失败记录,修正测试后组合 165 项三浏览器通过,无重试/跳过/页面错误,含指针交互 30 项。核心三个格式与 i18n 重建通过,独立重建候选并核对每项 manifest、源码及 docs/compiled 指纹,见 指针阶段证据。
前两批证据仅代表各自历史源码。本批是局部源码组合回归,手势、resize/view/RAF 与 Events 门面还待迁移,整个 CORE-17 安装包出口未完成。回退本批时恢复三个 Init.js,删除 pointer-focus/pointer-types 和对应测试,通过仓库脚本重建。保持 doing,不作完成任务提交。
第四批:触摸手势
gestureInit.ts 负责移动端安装与选项分支;gesture-controller.ts 管理触点、源身份、方向快照和结束/取消/锁定订阅;gesture-direction.ts 保留原两像素阈值与对角边界;gesture-types.ts 描述最小宿主和当前手势。gesture=false 仍支持进度条触摸,isLive 不安装;普通视频拖动保留 TOUCH_MOVE_RATIO,进度条拖动保留全尺寸倍率,旋转时保留纵向计算。
初始 36 项三浏览器基线:21 通过、15 个候选修正目标失败。复现 touchcancel、锁定、多指、触点 identifier 改变后继续沿用旧拖动,以及 seek 回调销毁后继续发出 setBar。分别登记 BASE-LIFE-27/28。候选取消失效手势,并在用户回调后检查手势/实例/源身份;进度条触摸也不会在 setBar 回调销毁或启动新手势后继续旧 seek。失败起始回调保留异常身份并取消未完成手势。
control/progress/position.ts 改为最小宿主,并增加可选的 active 回调,供触摸调用在 setBar 与 seek 之间检查生命周期;原双参数桌面调用默认行为保持,纳入既有组件回归。新增 document:touchcancel 转发及同名公开 Event 声明,旧事件/类型保持。原生目标也监听取消,因此全局 document 被重绑后仍可释放自身手势。源切换通过既有 source 身份取消,避免依赖延迟到达的媒体事件。
12 项单元测试通过,覆盖轴/阈值、绝对/相对顺序、锁定/取消/多指/换指、源/旋转改变、无效尺寸、销毁、重入、错误和订阅清理。类型用例校验 TouchEvent 输入和新增取消事件。首轮手势+重绑浏览器为 52 通过、2 项 WebKit 用例停在触摸执行前的初始定位,最终媒体仍接近 0,未进入手势断言;保留失败记录。准备步骤显式播放/暂停以初始化解码器后,54 项通过;另外独立覆盖首次播放前的 seek 请求契约。扩展进度条绝对定位/销毁和原事件顺序后,手势 54 项三浏览器通过。
本批最终 CI 312 项(283 单元、4 工程、25 基线)、180 个生产 TS 与五组消费者严格检查通过;输入/生命周期/组件组合 303 项三浏览器通过,含 54 项手势与 84 项组件回归,无重试/跳过/页面错误。核心三个格式和 i18n 已重建,独立重建候选核对全部浏览器 manifest,源码/产物/文档副本指纹一致;公开声明差异逐字核对,仅增加 document:touchcancel。见 手势阶段证据。
浏览器触摸载荷和 Android UA 是受控输入,使用实际 DOM、媒体及定位;不将其宣称为实体手机触摸、滚动仲裁或系统取消的验收。未新增依赖,单元用例加入 test:unit。剩余调度/Events 门面和整个 CORE-17 安装包出口仍待完成;任务保持 doing。回退本批时恢复 gestureInit.js 与 position.ts,删除新的 gesture 模块和测试,撤销新增取消转发/声明后按仓库脚本重建。
第五批:调度与 Events 门面
resizeInit.ts 保留合并触发、动态 RESIZE_TIME、标准状态 autoSize、宽高比恢复与 notice 清理,并在重入销毁后停止后续写入。方向事件按 owner window 的 addEventListener 能力注册,不再依赖 onchange 已被赋值。viewInit.ts 拥有首发节流的复位定时器和订阅,保留构造时 SCROLL_TIME、动态 SCROLL_GAP、无尾随通知及原同步嵌套时序;viewport.ts 按 container 当前 ownerDocument 计算,但不更改原边界/容差公式。公共 throttle/debounce/isInViewport 不改。
updateInit.ts 保留 USE_RAF 开关、首次 playing 时立即通知、暂停不发 raf 和单个待执行帧;实例销毁或 raf 回调内销毁不再安排新帧,显式 destroy 通知仍可取消活跃实例的待执行帧。subscriptions.ts 以明确事件映射管理内部订阅并在实例关闭时移除;scheduling-types.ts 定义最小宿主。
events/index.ts 完成门面迁移,types.ts 组合初始化器宿主;保留 destroyEvents/proxy/hover/bindGlobalEvents 自有字段、原型方法、bound proxy/hover、注册返回和初始化顺序。bindGlobalEvents 使用 declare,不在构造开始时生成额外字段。strict 检查暴露的泛型推断问题通过显式事件映射修正,没有用 any 绕过;类型用例验证注册重载、回调 payload 和订阅边界。
初始 24 项浏览器基线为 15 通过、9 个候选目标失败。确认方向 onchange=null 时无 resize、销毁残留 scroll 节流定时器、跨 iframe 使用错误 viewport;登记 BASE-LIFE-29/30、BASE-DOM-18。首轮迁移后与生命周期组合 51 通过、3 个 viewport 用例仍失败,检查发现测试 top=150 恰好处于旧 50 像素容差边界;改为 top=250,保留闭区间规则并加入单元边界检查,不把容差规则改掉来迎合测试。补入实际 RAF/播放/销毁后,60 项三浏览器组合通过。9 项新增单元测试通过并接入 test:unit。
源码阶段 CI 321 项(292 单元、4 工程、25 基线)、188 个生产 TS 文件与五组消费者严格检查通过。实际 core/chapter tarball 安装后 27 项运行时检查、五组类型零诊断。核心三个格式及 i18n 已重建;最终安装 UMD/legacy 完整浏览器矩阵仍在验收,未将本任务标记完成。此前阶段报告仅记录当时源码,最终结论将以完整安装包指纹为准。
最终验收
- Hotkey、Events 门面及全部输入初始化器已迁移 TS,拆出键盘焦点/默认键、原生监听器、全局监听组、指针焦点、手势控制/方向、可见性和调度宿主。核心 183 个与 chapter 5 个生产 TS 文件严格检查通过;主播放器入口的剩余整合继续由后续任务处理。
- yarn ci:check:321 项通过(292 单元、4 工程、25 基线)。实际 core/chapter tarball 仓库外安装后 27 项运行时、五组类型零诊断。
- 最终安装 UMD 和 legacy 各完成 1119 项全量及 24 项补充三浏览器检查,分别对同一安装产物累计验证 1143 项;其中输入专项合计 243 项,既有显示模式 300 项;无重试、跳过或未处理页面错误。保留了早期失败记录与修正原因,未把早期阶段指纹混作最终证据。
- 最终证据核对全部当前核心源码与打包快照、核心和 chapter 实际浏览器产物、全部包内文件、三个分发格式与 docs/compiled、22 个语言文件和独立分发许可。
- 保留公开字段/原型、方法绑定、回调数组和 native listener 身份、Emitter 显式 ctx、普通点击/触摸/调度时序。公开声明仅增加 document:touchcancel,其余内容与父提交逐字核对一致;五组旧/新 TS 消费者仍通过。
- 实际鼠标/键盘播放、定位、iframe 焦点/可见性和 RAF 已验证。触摸载荷、Android UA、IME、方向事件等包含受控路径;实体 iOS/Android 手势、滚动仲裁、系统取消、旋转和安全区仍交 REL-03/REVIEW-02。
- 保留原生 once/重复注册语义;once 记录在显式 disposer 或 owner 清理时释放。永久拒绝移除的自定义 target 只能警告并保留可重试记录,不承诺强制恢复外部副作用。
- 无新增依赖、锁文件或版本修改。六个新增 Node 测试文件已接入 test:unit,并补充类型与浏览器用例;既有公共类型差异仍由 CORE-21 协调。回退时恢复输入入口/初始化器与进度位置辅助函数,删除新增输入模块和用例,撤销新增取消事件,再通过仓库脚本重建,保留基线记录。
关闭本任务十一项输入/生命周期差异;BASE-PERF-01 的既有 resize 修复仅作回归验证,不重复计为新修复。控件 Tab 可达性 BASE-DOM-01 继续由 CORE-23 完成,未在本任务关闭。当前 214 项中 53 完成、161 待办,下一项 CORE-18 内置插件和提示模块。本任务独立本地提交,不推送或发布;全生态迁移和发布复盘继续按完整计划推进。