From 2e13df47731ff0fd9cccd5d1bc7c15056952d6c0 Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 17 Jul 2026 07:03:10 +0200 Subject: [PATCH] docs(playback): design shared controls setting --- ...b-player-shared-controls-setting-design.md | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-17-web-player-shared-controls-setting-design.md diff --git a/docs/superpowers/specs/2026-07-17-web-player-shared-controls-setting-design.md b/docs/superpowers/specs/2026-07-17-web-player-shared-controls-setting-design.md new file mode 100644 index 000000000..1e055419e --- /dev/null +++ b/docs/superpowers/specs/2026-07-17-web-player-shared-controls-setting-design.md @@ -0,0 +1,210 @@ +# Web-Player Shared Controls Setting Design + +## Context + +The shared `app-player-controls` presentation is already integrated with four +playback paths: + +- Embedded MPV frame-copy uses it unconditionally because its video is rendered + into a DOM canvas. +- HTML5, Video.js, and ArtPlayer can use it behind the default-off + `WEB_PLAYER_SHARED_CONTROLS` rollout token. + +The web-player token currently resolves from the compile-time +`WEB_PLAYER_SHARED_CONTROLS_ENABLED = false` constant. Enabling the integrations +therefore requires a code change, rebuild, and application restart. The +integrations are ready for opt-in testing, so the rollout choice should be a +persisted user setting instead. + +The Settings route does not keep an active player mounted. The setting can +therefore be applied when the next playback session is created; changing an +already-running engine or preserving an active playback position is not +required. + +## Goals + +- Add an experimental Playback checkbox for opting HTML5, Video.js, and + ArtPlayer into the shared controls. +- Persist the choice in the canonical `SettingsStore`. +- Apply the saved choice to the next web-player session without restarting or + reloading the application. +- Keep the controls mode immutable for the lifetime of a player session. +- Preserve default-off behavior and compatibility with existing stored + settings. +- Keep Embedded MPV and external-player behavior unchanged. + +## Non-goals + +- Make shared web controls the default. +- Change an active player between legacy and shared controls. +- Add separate preferences for HTML5, Video.js, and ArtPlayer. +- Make Embedded MPV native-view use a DOM overlay. +- Allow frame-copy Embedded MPV to fall back to the legacy controls dock. +- Control the native UI of external MPV or VLC processes. + +## Embedded MPV Semantics + +The new checkbox is intentionally web-player-specific. + +Embedded MPV has two rendering paths: + +- **Frame-copy:** video is uploaded to a renderer canvas. It always uses + `app-player-controls`; a second controls toggle would only duplicate an + already-required part of this engine. +- **Native-view:** video is hosted in a platform-native surface. DOM controls + cannot reliably stack above that surface, so the compositor-safe legacy dock + remains required. + +The existing `embeddedMpvFrameCopy` setting continues to select the rendering +engine and continues to require an application restart. It is independent from +the new web-player controls preference. + +External MPV and VLC processes continue to own their own controls UI. + +## Considered Approaches + +### 1. Conditional global web-player preference + +Selected. + +One setting controls HTML5, Video.js, and ArtPlayer together. Its checkbox is +shown only while one of those players is selected. This matches the existing +single rollout token, keeps the Settings UI relevant to the selected player, +and avoids suggesting that the option changes Embedded MPV or external +players. + +### 2. Always-visible "shared controls where available" preference + +Rejected because it would be a no-op for frame-copy Embedded MPV, unsupported +for native-view, and outside the app for external players. The resulting +conditional semantics would be harder to explain than a web-player-specific +label. + +### 3. Separate preference for every playback engine + +Rejected because the three web integrations deliberately share one rollout +contract. Per-engine preferences would add settings and test combinations +without a current product requirement. + +## Settings Contract + +Add an optional boolean to the shared settings interface: + +```ts +webPlayerSharedControls?: boolean; +``` + +The canonical default is `false`. Loading an older settings object without the +field must normalize to `false`, and saving settings must write the complete +resolved value back to storage. + +The settings form adds a `webPlayerSharedControls` control with a `false` +default. Form hydration, serialization, reset behavior, backup/export, and the +existing Save workflow continue to use the normal complete `Settings` object. +This is renderer-only state; Electron main-process behavior and preload IPC do +not need a new command. + +## Runtime Data Flow + +The existing `WEB_PLAYER_SHARED_CONTROLS` injection token remains the +immutable, test-overridable session seam used by the three engine components. +The compile-time constant remains only as the default-off fallback for direct +component use and focused tests. + +Normal runtime resolution moves to `WebPlayerViewComponent`: + +1. `SettingsStore` loads `webPlayerSharedControls`, defaulting to `false`. +2. The Settings page updates and persists it through the existing Save action. +3. Leaving Settings destroys that route; no player is active during the + change. +4. The next `WebPlayerViewComponent` resolves a component-scoped boolean + snapshot from `SettingsStore`. +5. HTML5, Video.js, or ArtPlayer receives that snapshot through + `WEB_PLAYER_SHARED_CONTROLS` and constructs exactly one controls system. +6. The snapshot does not change until that player host is destroyed and a new + playback session is created. + +This preserves the construction-time requirements of Video.js and ArtPlayer: +vendor controls, hotkeys, gestures, plugins, source bridges, and the shared +adapter are configured atomically. A reactive boolean must not flip only the +template while leaving the engine configured for the other mode. + +Embedded MPV does not consume the setting. Its component continues to choose +shared controls for `engine === 'frame-copy'` and the legacy dock for the +native-view engine. + +## Settings UI + +Add one standard `setting-item` to the Playback section, using the existing +Material checkbox and layout styles. + +- Label meaning: **Use unified controls for web players (experimental)**. +- Description meaning: use IPTVnator's common controls in HTML5, Video.js, and + ArtPlayer. +- Visibility: selected player is `videojs`, `html5`, or `artplayer`. +- Hidden for Embedded MPV, external MPV, and VLC. +- Add a stable test selector to the row and checkbox. +- Add the label and description keys to every locale file so the i18n key sets + remain aligned. + +No new colors, spacing rules, or one-off UI components are required. + +## Failure and Compatibility Behavior + +- Missing or malformed non-boolean stored values resolve to `false`. +- A storage write failure is reported through the existing + `SettingsStore.updateSettings()` rejection and must not require + player-specific recovery. +- Legacy mode remains the safe fallback whenever the setting cannot be + resolved. +- Direct engine tests can continue overriding + `WEB_PLAYER_SHARED_CONTROLS` with `true` or `false`. +- The setting does not relax Electron sandboxing and does not alter the + frame-copy engine opt-in. + +## Testing Strategy + +### Shared interfaces and settings store + +- Default is `false`. +- Older stored settings without the field load as `false`. +- A stored `true` value is restored. +- `updateSettings()` persists the complete resolved value. + +### Settings form and UI + +- Form creation and serialization include the new field. +- The checkbox hydrates from stored settings and is included in the Save + payload. +- The row is visible for HTML5, Video.js, and ArtPlayer. +- The row is hidden for Embedded MPV, external MPV, and VLC. +- Translation key parity remains valid across all locales. + +### Playback host + +- A newly created HTML5, Video.js, or ArtPlayer session receives the saved + boolean snapshot. +- `false` preserves the existing vendor/native UI. +- `true` mounts the shared controls path. +- Embedded MPV selection ignores the web-player preference. +- One session never renders or initializes both controls systems. + +### Validation + +- Run focused Settings form/component and `SettingsStore` tests. +- Run the complete `ui-playback` test target because all three web engines + share the rollout seam. +- Run affected lint targets and the workspace typecheck. +- Run the settings E2E flow for checkbox visibility, persistence, and use after + returning to playback. +- Run the closest Electron playback smoke/E2E coverage because the same + Settings surface is used in the desktop app. + +## Documentation Impact + +Update `docs/architecture/player-controls-contract.md` and the shared-controls +sections of `AGENTS.md` and `CLAUDE.md` to replace the compile-time-only +default-off rollout description with the persisted experimental preference. +The Embedded MPV architecture documentation only needs clarification if its +existing distinction between frame-copy shared controls and the native-view +legacy dock would otherwise become ambiguous.