docs(design): add design doc for live presets#1395
Conversation
Made-with: Cursor
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📦 Bundle Size Report🎨 @videojs/html — no changesPresets (7)
Media (8)
Players (3)
Skins (17)
UI Components (25)
Sizes are marginal over the root entry point. ⚛️ @videojs/react — no changesPresets (7)
Media (7)
Skins (14)
UI Components (20)
Sizes are marginal over the root entry point. 🧩 @videojs/core — no changesEntries (9)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (10)
📦 @videojs/spf — no changesEntries (3)
ℹ️ How to interpretAll sizes are standalone totals (minified + brotli).
Run |
Narrow the live preset design to live-only skins that render live UI unconditionally — no streamType branching, no in-skin VOD fallback, no unknown-state flash on first paint. Drop streamTypeFeature from liveVideoFeatures / liveAudioFeatures for now; add it back additively when a live-only affordance actually needs it. Made-with: Cursor
| playbackFeature, | ||
| playbackRateFeature, | ||
| volumeFeature, | ||
| timeFeature, |
There was a problem hiding this comment.
Question: Do we not need live-specific time handling capability?
There was a problem hiding this comment.
No, I tried to keep it simpler downstream for now and updated the duration to seekable-end for live.
We can do the same for current time if needed. In media-chrome we had this separately and handled this in the UI itself but I'm don't see the need for it yet. Let me know if you can think of edge cases.
|
|
||
| Live-only presets win on four axes: | ||
|
|
||
| 1. **VOD players don't pay for live.** The dominant case is on-demand. Keeping `streamTypeFeature` and the live UI tree out of `videoFeatures` / `audioFeatures` means the VOD store has no `streamType` slice, the VOD skin has no live-branch code, and bundles for VOD-only apps omit both. A shared-skin approach forces every player to carry the stream-type event plumbing and the live-UI tree, even when the source is known to be VOD. |
There was a problem hiding this comment.
This is where we could specifically mention file size. It's not a strong argument without answering another level of "why does this matter?". "carry the stream-type event plumbing" doesn't go far enough.
| - **Branch the existing `video` / `audio` skins on `streamType`.** One preset per medium, one skin that covers both modes by reading `streamType`. Pattern already used for `volumeAvailability === 'unsupported'` (see `VolumePopover` in `packages/react/src/presets/video/skin.tsx:60`). | ||
| - **Single preset, runtime-selected skin.** Keep one `video` / `audio` preset but have it mount a different skin tree based on `streamType`. Hides the split from the author but retains the unknown-state and dead-code problems. | ||
|
|
||
| ## Rationale |
There was a problem hiding this comment.
This section is combining the rational for both decisions and I'd love to see that split out.
There was a problem hiding this comment.
Rationale: Split live from VOD
The dominant case is VOD — the vast majority of video / audio integrations never play live. Every byte the VOD preset carries for a capability it doesn't use is a byte the dominant case pays for something it doesn't get.
A shared video preset covering both modes forces every VOD player to ship:
- The
streamTypestate slice and the events, subscribers, and predicate helpers that maintain it. - The live UI tree — live indicator, jump-to-live-edge button, DVR slider — rendered conditionally but present in the bundle.
- The branching logic itself (detection, unknown-state handling, transitions).
A separate live-video preset keeps that cost with the apps that actually use live. The VOD store has no streamType slice; VideoSkin has no live branches; nothing in the default import path references live code. The bundle-size report already separates /video/skin from a future /live-video/skin entry — the split is visible and measurable.
Secondary wins:
- Explicit opt-in matches author intent. Picking
live-videois a one-word signal that live is the supported path — clearer than "usevideoand set some prop" and more discoverable than "videohappens to work if the source is live." - Smaller type surface in the common store.
VideoPlayerStore/AudioPlayerStorekeep their current state shape. AddingstreamTypeto the base ripples into every consumer of those types. - Targetable visual regression tests. Each skin snapshots independently; the VOD snapshot stays stable without a
streamTypeharness.
Rationale: Live preset renders live UI only
Given we're shipping a dedicated live preset, it could still support VOD internally — branch the live skin on streamType and fall back to VOD controls when the source isn't live. We're not doing that either.
-
Smaller live bundle. Live-only (especially non-DVR) unlocks a meaningfully smaller UI: no time slider, no thumbnail previews, no seek buttons, no remaining-time display. A dual-mode live skin has to ship all of those for the VOD branch, plus the branching. Live-only gets to be genuinely smaller, not just differently shaped.
-
No unknown-state flash. A dual-mode live skin starts every load in
streamType === 'unknown'and resolves tolive(oron-demand) after manifest detection. Either we render a neutral placeholder during that window — a third UI state to design and test — or we pick a default and flip visibly once detection completes. A live-only skin has one shape from first paint. -
Fork-template clarity. Skins are reference implementations authors copy and mutate. A live-only skin gives live-app authors a starting point with only live concepts in the tree — no conditional helpers, no
unknownplaceholder, no dead VOD branches to delete. VOD authors forkingVideoSkinget the same treatment from the other side. -
Headroom for divergence. Live UI tends to accumulate bespoke affordances — latency indicator, "behind live" badge, DVR-aware scrubber interactions, live chat/reactions slot. Each lands naturally inside
LiveVideoSkinwithout leaking concepts into the dual-mode branching. -
Easy to add, hard to remove. If we later find authors commonly need one preset that handles live → VOD replay or cross-mode source swaps, we can add
streamTypeFeatureand branch the skin — additive. Shipping a dual-mode skin and later deciding the unknown-state handling and dead code aren't worth it is a breaking change for anyone on the VOD path.
Split the rationale into two sections — one for the preset split, one for the live-only skin — and lead each with its actual driver. - Split rationale: filesize. VOD is the dominant case; every byte the shared preset carries for live is paid by VOD-only apps that don't use it. Enumerates what a shared preset forces onto the VOD bundle (streamType slice, live UI tree, branching logic). - Live-only rationale: smaller live bundle — no time slider, thumbnail previews, seek buttons, or remaining-time display. A dual-mode skin can't ship smaller, only differently shaped. - Drop playbackRateFeature from liveVideoFeatures / liveAudioFeatures; speed controls aren't meaningful for live. - Add a note that no live-specific timeFeature is needed — duration already resolves to seekable.end(last) for live. - Flag textTrackFeature as the clearest follow-up feature split (captions vs chapters vs thumbnails) in Open Question #2. - Tighten Context and cut AI-verbosity throughout. Addresses review feedback from @heff on #1395. Made-with: Cursor
Summary
Records the decision to ship live playback as dedicated, live-only
live-video/live-audiopresets rather than branching the existingvideo/audioskins onstreamType. Live skins render live UI unconditionally — no in-skin VOD fallback, nounknown-state placeholder. VOD-only apps keep their current store shape and bundle; live-only apps get a live-dedicated fork template with no dead branches.Changes
internal/design/ui/live-presets.mdcovering the decision, alternatives, rationale, trade-offs, consequences, open questions, and prior art.Key points captured in the doc
streamTypeand does not attempt to recover to a VOD layout.liveVideoFeatures/liveAudioFeaturesinitially aliasvideoFeatures/audioFeatures—streamTypeFeatureis intentionally not included yet. It's an additive, non-breaking change to append it once a live-only affordance actually needs stream-type state.VideoSkin/AudioSkinor to the derived store types.Testing
Docs-only; no code changes.
Made with Cursor
Note
Low Risk
Docs-only change that adds a design decision record; no runtime, API, or behavior changes.
Overview
Adds a new design doc
internal/design/ui/live-presets.mdthat records the decision to ship separate, live-onlylive-video/live-audiopresets (rather than branching existing VOD skins onstreamType), outlines the intended feature/skin shape at a high level, and documents trade-offs, alternatives, and open questions for future implementation.Reviewed by Cursor Bugbot for commit 463a462. Bugbot is set up for automated code reviews on this repo. Configure here.