@video-lab/player-ui 1.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,15 @@
1
1
  # @video-lab/player-ui
2
2
 
3
- Video Lab Player 的覆盖层(L2)· **框架无关**。四个 custom element,零框架依赖 ——
4
- inline(`video-react`)和 iframe 内部(`apps/embed-app`)用的是同一份。
3
+ Video Lab Player 的框架无关覆盖层。它提供四个 custom element,零框架依赖,可供 inline 和 iframe 集成共用。
5
4
 
6
5
  依赖:只有 `@video-lab/protocol`。**没有 peer 依赖**,不依赖 React,也不依赖 Vue。
7
6
 
8
7
  ## 实现范围
9
8
 
10
- **SDK 侧 4 个覆盖层全部实现**:`<sentinel-poster>`(封面)、`<sentinel-loading>`(缓冲)、
9
+ 提供四个覆盖层:`<sentinel-poster>`(封面)、`<sentinel-loading>`(缓冲)、
11
10
  `<sentinel-error>`(错误 + 重试)、`<sentinel-pause>`(暂停时的中性广告 / 推荐插槽,可选关闭按钮)。
12
11
 
13
- **结束覆盖层 + 倒计时(End / Countdown)按 ADR-025 归团队层**(examples/team-video-vue 用 `ended` 事件
14
- 自己渲染、自跑 `setInterval` 倒计时),**刻意不在本包** —— 业务调度的覆盖层归团队层,SDK 只做
15
- 事件驱动、无需业务内容的自动覆盖层(Poster/Loading/Error/Pause)。
12
+ 结束覆盖层和倒计时属于业务应用职责,刻意不在本包中实现。本包仅处理由播放器事件驱动、无需业务内容的 Poster、Loading、Error Pause 覆盖层。
16
13
 
17
14
  ## 用法
18
15
 
@@ -54,7 +51,7 @@ export function onPlayerEvent(event: PlayerEvent): void {
54
51
 
55
52
  // ③ 重试是 CustomEvent(冒泡),不是回调 prop
56
53
  error.addEventListener('sentinel-retry', () => {
57
- /* 重建播放器 */
54
+ /* 调用当前句柄的 retry(),由 playablechange 更新显示 */
58
55
  })
59
56
  ```
60
57
 
@@ -92,7 +89,7 @@ error.addEventListener('sentinel-retry', () => {
92
89
 
93
90
  ## 两条红线
94
91
 
95
- **不做团队品牌 UI**(ADR-021)。所有颜色走 `--sentinel-overlay-*` CSS 变量,元素里没有一个写死的色值——有测试守着这条。**CSS 自定义属性能穿透 shadow 边界**,所以主题照常从外面注入;要改内部结构的样式走 `::part()`:
92
+ **不内置品牌 UI。** 所有颜色都通过 `--sentinel-overlay-*` CSS 变量提供。CSS 自定义属性可穿透 shadow 边界;需要调整内部结构样式时使用 `::part()`:
96
93
 
97
94
  ```css
98
95
  .player-container {
@@ -104,18 +101,16 @@ sentinel-error::part(retry-button) {
104
101
  }
105
102
  ```
106
103
 
107
- **不内置任何翻译**(ARCHITECTURE § 8.7.1)。元素只收**已经解析好的字符串**——
104
+ **不内置任何翻译。** 元素只接收**已经解析好的字符串**——
108
105
  `message` / `retry-label` / `text` 传什么显示什么,本包不做 key → 文案的查表。
109
- 文案解析归组合层(`video-react` / `embed-app`),每个市场的垂直行业术语各家不同,
110
- SDK 内置一份"标准翻译"反而不准。
106
+ 文案解析应由消费应用完成;不同产品和地区的术语不同,SDK 不应内置一份“标准翻译”。
111
107
 
112
108
  > ⚠️ 只有 `retry-label` 有硬编码兜底(`'Retry'`)。不传就是英文,**不是**漏翻译的报错。
113
109
 
114
110
  ## 设计上值得知道的几件事
115
111
 
116
112
  **注册刻意做成显式函数**,不在模块求值期自动跑。自动注册要在模块顶层碰 `customElements`,
117
- 而那是浏览器全局 —— SSR / `next build` Node 里求值这个模块会当场炸。本仓库为这类事付过一次代价
118
- (flv.js 是 UMD、模块求值期引用 `self`,导致 inline 模式 `next build` 直接失败,而单测 / e2e / 体积检查三层全碰不到)。
113
+ 而那是浏览器全局 —— SSR 构建在 Node 中求值模块时会失败。`defineSentinelOverlays()` 幂等,重复调用不抛错;没有 `customElements` 的环境会返回 `false`。
119
114
  `defineSentinelOverlays()` 幂等,重复调不抛;没有 `customElements` 的环境直接返回 `false` 空转。
120
115
 
121
116
  **`visible: false` 时整棵子树不渲染**,而不是 `display: none`。覆盖层里有 spinner 动画,留在树里会白白跑着。
@@ -136,3 +131,12 @@ pnpm --filter @video-lab/player-ui test
136
131
  ```
137
132
 
138
133
  Vitest + jsdom,直接测 custom element 的注册 / attribute → DOM / 事件派发。
134
+
135
+
136
+ ### 统一恢复(契约 v2)
137
+
138
+ 旧 `reconnect({ resetCounter })` 和 `reconnectstart/success/failed` 已移除。命令入口是 `retry(): Promise<void>`;Promise 完成只表示接受或合并请求。恢复中的重复请求共用预算,强播放证据通过 `recovery` 的 `recovered` 回报。
139
+
140
+ 宿主 UI 读取 `playablechange` 的 `playable`、`recoverable`、`action`。`error` 只提供诊断;预算耗尽才给出 `action: 'retry'`,宿主无需按错误原因拼接重试状态。消费组件/iframe 配置 `showErrorOverlay: false` 与 `showLoadingOverlay: false` 控制默认覆盖层;player-ui 本身只负责渲染,不下发媒体命令。静态 iframe URL 没有宿主命令通道,内部按钮仍走同一调度。
141
+
142
+ 更多迁移语义见 [ADR-099](../../docs/adr/ADR-099-unified-recovery-contract.md)。
package/dist/index.cjs CHANGED
Binary file
package/dist/index.d.cts CHANGED
@@ -48,15 +48,15 @@ declare abstract class SentinelOverlayElement extends HTMLElement {
48
48
  //#endregion
49
49
  //#region src/elements/error.d.ts
50
50
  /**
51
- * `<sentinel-error>` · 错误(error 事件时显示)
51
+ * `<sentinel-error>` · 错误(显隐由组合层消费 playablechange 决定)
52
52
  *
53
53
  * 对应原 `<ErrorOverlay>`,但**文案解析上移了**(spec § 技术方案 ②):
54
54
  * 原版在组件内部做 `messages['error.<CODE>'] → error.message` 的回退,
55
55
  * 现在由组合层(`video-react` / embed-app)解析好,元素只收一个 `message` 字符串。
56
56
  * `code` 仍然保留 —— 它是给消费方做 `::part()` / 属性选择器和埋点用的。
57
57
  *
58
- * **`retryable` 决定给不给重试按钮**:对着一个必然失败的错误
59
- * (比如 `E_MEDIA_NOT_SUPPORTED`)给用户一个重试按钮是在骗人,点几次都不会好。
58
+ * `retryable` 是展示属性,由组合层根据 playablechange.action 为 retry/play 设置;
59
+ * 元素不读取 raw error 的 retryable,也不自行判断恢复预算。
60
60
  *
61
61
  * 点重试派发 `sentinel-retry`(CustomEvent,冒泡),替代原来的 `onRetry` 回调 prop。
62
62
  * 想整块换掉默认 UI,用 `<slot name="retry">`。
@@ -246,6 +246,9 @@ interface OverlayState {
246
246
  posterVisible: boolean;
247
247
  loadingVisible: boolean;
248
248
  error: PlayerError | null;
249
+ diagnostic?: PlayerError;
250
+ action?: 'retry' | 'play' | 'replace-source' | 'recreate-frame' | 'none';
251
+ sessionId?: string;
249
252
  /** 暂停图是否显示(契约字段 pauseImage,ADR-043)。`pause` 置起、`play` 撤下 */
250
253
  pauseImageVisible: boolean;
251
254
  /**
package/dist/index.d.mts CHANGED
@@ -48,15 +48,15 @@ declare abstract class SentinelOverlayElement extends HTMLElement {
48
48
  //#endregion
49
49
  //#region src/elements/error.d.ts
50
50
  /**
51
- * `<sentinel-error>` · 错误(error 事件时显示)
51
+ * `<sentinel-error>` · 错误(显隐由组合层消费 playablechange 决定)
52
52
  *
53
53
  * 对应原 `<ErrorOverlay>`,但**文案解析上移了**(spec § 技术方案 ②):
54
54
  * 原版在组件内部做 `messages['error.<CODE>'] → error.message` 的回退,
55
55
  * 现在由组合层(`video-react` / embed-app)解析好,元素只收一个 `message` 字符串。
56
56
  * `code` 仍然保留 —— 它是给消费方做 `::part()` / 属性选择器和埋点用的。
57
57
  *
58
- * **`retryable` 决定给不给重试按钮**:对着一个必然失败的错误
59
- * (比如 `E_MEDIA_NOT_SUPPORTED`)给用户一个重试按钮是在骗人,点几次都不会好。
58
+ * `retryable` 是展示属性,由组合层根据 playablechange.action 为 retry/play 设置;
59
+ * 元素不读取 raw error 的 retryable,也不自行判断恢复预算。
60
60
  *
61
61
  * 点重试派发 `sentinel-retry`(CustomEvent,冒泡),替代原来的 `onRetry` 回调 prop。
62
62
  * 想整块换掉默认 UI,用 `<slot name="retry">`。
@@ -246,6 +246,9 @@ interface OverlayState {
246
246
  posterVisible: boolean;
247
247
  loadingVisible: boolean;
248
248
  error: PlayerError | null;
249
+ diagnostic?: PlayerError;
250
+ action?: 'retry' | 'play' | 'replace-source' | 'recreate-frame' | 'none';
251
+ sessionId?: string;
249
252
  /** 暂停图是否显示(契约字段 pauseImage,ADR-043)。`pause` 置起、`play` 撤下 */
250
253
  pauseImageVisible: boolean;
251
254
  /**
package/dist/index.mjs CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@video-lab/player-ui",
3
- "version": "1.0.1",
3
+ "version": "3.0.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -28,7 +28,7 @@
28
28
  "dist/**/*.css"
29
29
  ],
30
30
  "dependencies": {
31
- "@video-lab/protocol": "1.0.1"
31
+ "@video-lab/protocol": "3.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "jsdom": "^24.0.0",