Skip to content

feat(html): add createMediaElement - #2603

Draft
mihar-22 wants to merge 22 commits into
mainfrom
pr6a/create-media-element
Draft

feat(html): add createMediaElement#2603
mihar-22 wants to merge 22 commits into
mainfrom
pr6a/create-media-element

Conversation

@mihar-22

@mihar-22 mihar-22 commented Sep 2, 2026

Copy link
Copy Markdown
Member

What this does

Adds createMediaElement, the html façade factory, and builds the built-in media elements with it.

import { createMediaElement } from '@videojs/html';
import { HlsJsAdapter } from '@videojs/hlsjs-video';

export class HlsJsVideoElement extends createMediaElement(HlsJsAdapter) {
  static readonly tagName = 'hlsjs-video';
}

Each html façade lives in media/<name>/element.ts as <Name>Element with its own tagName, like the UI elements; the define/media entry only registers it.

The adapter contract

An adapter declares two statics and the element derives everything else from them:

Static Says Built-ins
host the element it drives 'video' for HTMLVideoAdapter and everything on it, 'audio' for HTMLAudioAdapter and the SPF audio adapters, 'iframe' for Cloudflare, TikTok, Twitch, Vimeo, YouTube, Spotify. The hls.js MuxAudioAdapter overrides its video base to 'audio'.
defaultProps its configurable properties and their defaults already declared everywhere; DASH and SPF audio now list streamType, which they could always set

From defaultProps, each primitive default becomes a content attribute of that type, named by its WHATWG spelling or kebab-case (src, preload, stream-type), reset to the default when removed. Objects such as source stay property-only. It's the rule createMediaComponent already applies to React props, so both façades expose one surface from one declaration.

A boolean that defaults to true stays property-only, since an absent attribute cannot mean true. The muted attribute belongs to defaultMuted, as in HTML, and seeds the muted state when it changes.

A <video> or <audio> host also accepts what its native element understands (videoContentAttributes / audioContentAttributes, declared beside the base adapters). Ones the adapter can set go through the adapter; the rest are copied onto the inner element. An <iframe> host accepts only what its adapter declares.

CustomMediaElement is now a composition

CustomMediaElement(Adapter, { template? }) composes pieces that stand on their own in @videojs/media/dom: renderHost, AdapterAttachment, forwardAdapter / reflectAttributes / bridgeEvent, and MediaChildren. createMediaElement wraps it in MediaAttachMixin. <background-video> and <hls-background-video> use renderHost and AdapterAttachment instead of hand-rolling their shadow root and teardown. The six iframe embeds share one embedTemplate and read their initial attributes through propsFromAttributes, the same defaultProps coercion the element applies later, instead of six copies of the markup and the mapper. static properties is gone; the element's static getTemplateHTML is renamed template.

Also fixed on the way: definition state was one flag and one map per factory call, so two subclasses with different attribute sets shared the first one's; the light-DOM target lookup could adopt a <video> nested in slotted content.

Shape

createMediaElement(Adapter, options?)

Option Default Use
template bare host element Embeds render their provider <iframe> from the element's initial attributes

Converted elements

Every built-in media element except <wistia-video> (the adapter is itself an element) now uses the factory, including the six iframe embeds through template.

Docs builder

Resolves each element from what its define entry passes to safeDefine, follows the adapter's static host through the extends chain to the base adapters, reads standard attributes from the base adapters' native lists, and derives custom attributes from defaultProps. Fixtures cover both factories, an iframe-backed audio element, and a flavour reached through a nested barrel.

Breaking

  • The html media classes are HlsJsVideoElement, SpotifyAudioElement, and so on, in media/<name>/element.ts, and declare their own tagName. @videojs/html/media/<name> keeps exporting the element class.
  • CustomMediaElement takes an adapter, not a tag; adapters must declare static host and static defaultProps; static properties is removed.
  • Audio elements no longer reflect poster, playsinline, or picture-in-picture; embeds reflect only the attributes their adapter declares.

Review

Reviewed by gpt-5.6-sol against the branch; its findings on true-by-default booleans, defaultMuted ownership, field-backed adapter props, target replay on slot change, event bridging, MediaChildren attribute removal, and the exported surface are all addressed in the last commits, with tests for each.

Validation

Build, typecheck, lint, workspace check, @videojs/media, @videojs/html, adapter, SPF audio, cdn, and builder tests, astro check, regenerated media references with all 14 targets intact.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
v10-sandbox Ready Ready Preview Sep 8, 2026 12:56am UTC

Request Review

@pkg-pr-new

pkg-pr-new Bot commented Sep 2, 2026

Copy link
Copy Markdown
@videojs/cdn

pnpm add https://pkg.pr.new/videojs/v10/@videojs/cdn@2603

@videojs/core

pnpm add https://pkg.pr.new/videojs/v10/@videojs/core@2603

@videojs/element

pnpm add https://pkg.pr.new/videojs/v10/@videojs/element@2603

@videojs/html

pnpm add https://pkg.pr.new/videojs/v10/@videojs/html@2603

@videojs/media

pnpm add https://pkg.pr.new/videojs/v10/@videojs/media@2603

@videojs/react

pnpm add https://pkg.pr.new/videojs/v10/@videojs/react@2603

@videojs/spf

pnpm add https://pkg.pr.new/videojs/v10/@videojs/spf@2603

@videojs/store

pnpm add https://pkg.pr.new/videojs/v10/@videojs/store@2603

@videojs/utils

pnpm add https://pkg.pr.new/videojs/v10/@videojs/utils@2603

@videojs/cloudflare-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/cloudflare-video@2603

@videojs/dash-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/dash-video@2603

@videojs/hlsjs-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/hlsjs-video@2603

@videojs/mux-audio

pnpm add https://pkg.pr.new/videojs/v10/@videojs/mux-audio@2603

@videojs/mux-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/mux-video@2603

@videojs/native-hls-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/native-hls-video@2603

@videojs/shaka-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/shaka-video@2603

@videojs/spotify-audio

pnpm add https://pkg.pr.new/videojs/v10/@videojs/spotify-audio@2603

@videojs/tiktok-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/tiktok-video@2603

@videojs/twitch-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/twitch-video@2603

@videojs/vimeo-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/vimeo-video@2603

@videojs/wistia-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/wistia-video@2603

@videojs/youtube-video

pnpm add https://pkg.pr.new/videojs/v10/@videojs/youtube-video@2603

@videojs/google-cast

pnpm add https://pkg.pr.new/videojs/v10/@videojs/google-cast@2603

@videojs/mux-data

pnpm add https://pkg.pr.new/videojs/v10/@videojs/mux-data@2603

commit: ed0bb45

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base initial PR initial Initial diff Lazy diff
/media/mux-video 3.02 kB 3.81 kB +801 B (+25.9%)
/media/mux-audio 2.69 kB 3.48 kB +814 B (+29.6%)
/media/cloudflare-video 2.90 kB 3.75 kB +872 B (+29.4%)
/media/dash-video 2.63 kB 3.41 kB +802 B (+29.8%)
/media/hls-audio 22.05 kB 22.82 kB +793 B (+3.5%)
/media/hls-video 28.00 kB 28.77 kB +796 B (+2.8%)
/media/hlsjs-video 2.63 kB 3.42 kB +806 B (+30.0%)
/media/native-hls-video 7.57 kB 8.34 kB +794 B (+10.2%)
/media/shaka-video 2.63 kB 3.41 kB +803 B (+29.9%)
/media/spotify-audio 3.02 kB 3.76 kB +761 B (+24.6%)
/media/tiktok-video 3.13 kB 3.81 kB +701 B (+21.9%)
/media/twitch-video 2.93 kB 3.80 kB +890 B (+29.7%)
/media/vimeo-video 2.90 kB 3.75 kB +868 B (+29.2%)
/media/youtube-video 2.90 kB 3.75 kB +869 B (+29.3%)
/video (default + hls) 64.52 kB 65.15 kB +648 B (+1.0%) 0 B
/video (minimal + hls) 64.77 kB 65.51 kB +757 B (+1.1%) 0 B
Small changes (32, ≤ 300 B)
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin.css 7.65 kB 7.68 kB +31 B (+0.4%)
/video/skin.css 7.46 kB 7.50 kB +38 B (+0.5%)
/video/minimal-skin 61.46 kB 61.55 kB +98 B (+0.2%) 0 B
/video/minimal-skin2 8.09 kB 8.15 kB +60 B (+0.7%)
/video/skin 61.34 kB 61.31 kB -27 B (-0.0%) 0 B
/video/skin2 7.95 kB 8.00 kB +54 B (+0.7%)
/audio/minimal-skin.css 5.53 kB 5.55 kB +29 B (+0.5%)
/audio/skin.css 5.34 kB 5.37 kB +27 B (+0.5%)
/audio/minimal-skin 45.77 kB 45.81 kB +40 B (+0.1%) 0 B
/audio/minimal-skin2 5.83 kB 5.88 kB +42 B (+0.7%)
/audio/skin 45.50 kB 45.56 kB +70 B (+0.2%) 0 B
/audio/skin2 5.65 kB 5.69 kB +45 B (+0.8%)
/live-video/minimal-skin.css 6.21 kB 6.23 kB +20 B (+0.3%)
/live-video/skin.css 6.18 kB 6.20 kB +24 B (+0.4%)
/live-video/minimal-skin 49.42 kB 49.39 kB -24 B (-0.0%) 0 B
/live-video/minimal-skin2 6.55 kB 6.58 kB +32 B (+0.5%)
/live-video/skin 49.43 kB 49.40 kB -31 B (-0.1%) 0 B
/live-video/skin2 6.58 kB 6.61 kB +28 B (+0.4%)
/live-audio/minimal-skin.css 4.39 kB 4.42 kB +31 B (+0.7%)
/live-audio/skin.css 4.37 kB 4.39 kB +26 B (+0.6%)
/live-audio/minimal-skin 35.43 kB 35.47 kB +40 B (+0.1%) 0 B
/live-audio/minimal-skin2 4.64 kB 4.68 kB +42 B (+0.9%)
/live-audio/skin 35.36 kB 35.46 kB +107 B (+0.3%) 0 B
/live-audio/skin2 4.63 kB 4.66 kB +37 B (+0.8%)
/media/background-video 1.16 kB 1.18 kB +18 B (+1.5%)
/media/hls-background-video 16.86 kB 16.94 kB +80 B (+0.5%)
/media/mux-background-video 16.86 kB 16.93 kB +69 B (+0.4%)
/media/wistia-video 607 B 602 B -5 B (-0.8%)
/video (default) 62.79 kB 62.82 kB +31 B (+0.0%) 0 B
/video (minimal) 63.04 kB 62.97 kB -67 B (-0.1%) 0 B
/audio (default) 46.99 kB 47.03 kB +43 B (+0.1%) 0 B
/audio (minimal) 47.21 kB 47.23 kB +27 B (+0.1%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 62.82 kB 56.87 kB
/video (default + hls) 65.15 kB 56.87 kB
/video (minimal) 62.97 kB 56.87 kB
/video (minimal + hls) 65.51 kB 56.87 kB
/audio (default) 47.03 kB 56.87 kB
/audio (minimal) 47.23 kB 56.87 kB
/background 11.57 kB 56.87 kB
Media (18)
Entry Initial
/media/mux-video 3.81 kB
/media/mux-audio 3.48 kB
/media/background-video 1.18 kB
/media/cloudflare-video 3.75 kB
/media/dash-video 3.41 kB
/media/hls-audio 22.82 kB
/media/hls-background-video 16.94 kB
/media/hls-video 28.77 kB
/media/hlsjs-video 3.42 kB
/media/mux-background-video 16.93 kB
/media/native-hls-video 8.34 kB
/media/shaka-video 3.41 kB
/media/spotify-audio 3.76 kB
/media/tiktok-video 3.81 kB
/media/twitch-video 3.80 kB
/media/vimeo-video 3.75 kB
/media/wistia-video 602 B
/media/youtube-video 3.75 kB
Players (5)
Entry Initial Lazy
/video/player 9.69 kB 56.87 kB
/audio/player 6.14 kB 56.87 kB
/background/player 4.46 kB 56.87 kB
/live-video/player 9.06 kB 56.87 kB
/live-audio/player 6.15 kB 56.87 kB
Skins (29)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.68 kB
/video/skin.css css 7.50 kB
/video/minimal-skin js 61.55 kB 56.87 kB
/video/minimal-skin2 js 8.15 kB
/video/skin js 61.31 kB 56.87 kB
/video/skin2 js 8.00 kB
/audio/minimal-skin.css css 5.55 kB
/audio/skin.css css 5.37 kB
/audio/minimal-skin js 45.81 kB 56.87 kB
/audio/minimal-skin2 js 5.88 kB
/audio/skin js 45.56 kB 56.87 kB
/audio/skin2 js 5.69 kB
/background/skin.css css 139 B
/background/skin js 9.81 kB 56.87 kB
/background/skin2 js 187 B
/live-video/minimal-skin.css css 6.23 kB
/live-video/skin.css css 6.20 kB
/live-video/minimal-skin js 49.39 kB 56.87 kB
/live-video/minimal-skin2 js 6.58 kB
/live-video/skin js 49.40 kB 56.87 kB
/live-video/skin2 js 6.61 kB
/live-audio/minimal-skin.css css 4.42 kB
/live-audio/skin.css css 4.39 kB
/live-audio/minimal-skin js 35.47 kB 56.87 kB
/live-audio/minimal-skin2 js 4.68 kB
/live-audio/skin js 35.46 kB 56.87 kB
/live-audio/skin2 js 4.66 kB
/global.css css 190 B
/shared.css css 118 B
UI Components (62)
Entry Initial Lazy
/ui/airplay-button 11.26 kB 56.87 kB
/ui/alert-dialog 8.79 kB 56.87 kB
/ui/audio-track-radio-group 11.38 kB 56.87 kB
/ui/buffering-indicator 8.60 kB 56.87 kB
/ui/captions-button 11.51 kB 56.87 kB
/ui/captions-radio-group 11.45 kB 56.87 kB
/ui/cast-button 11.25 kB 56.87 kB
/ui/container 9.47 kB 56.87 kB
/ui/controls 8.77 kB 56.87 kB
/ui/controls-backdrop 1.69 kB 56.87 kB
/ui/controls-content 1.67 kB 56.87 kB
/ui/dialog 8.73 kB 56.87 kB
/ui/dialog-backdrop 1.70 kB 56.87 kB
/ui/dialog-close 2.06 kB 56.87 kB
/ui/dialog-description 1.69 kB 56.87 kB
/ui/dialog-popup 1.90 kB 56.87 kB
/ui/dialog-title 1.70 kB 56.87 kB
/ui/error-dialog 12.38 kB 56.87 kB
/ui/fullscreen-button 11.26 kB 56.87 kB
/ui/gesture 9.57 kB 56.87 kB
/ui/hotkey 9.97 kB 56.87 kB
/ui/live-button 9.74 kB 56.87 kB
/ui/menu 18.45 kB 56.87 kB
/ui/menu-checkbox-item 1.97 kB 56.87 kB
/ui/menu-content 7.52 kB 56.87 kB
/ui/menu-group 2.09 kB 56.87 kB
/ui/menu-group-label 1.64 kB
/ui/menu-item 2.29 kB 56.87 kB
/ui/menu-item-indicator 1.37 kB 56.87 kB
/ui/menu-radio-group 3.01 kB 56.87 kB
/ui/menu-radio-item 2.13 kB 56.87 kB
/ui/menu-separator 1.30 kB 56.87 kB
/ui/mute-button 11.28 kB 56.87 kB
/ui/pip-button 11.22 kB 56.87 kB
/ui/play-button 11.22 kB 56.87 kB
/ui/playback-rate-button 11.32 kB 56.87 kB
/ui/playback-rate-radio-group 11.38 kB 56.87 kB
/ui/popover 8.57 kB 56.87 kB
/ui/poster 8.67 kB 56.87 kB
/ui/quality-radio-group 11.97 kB 56.87 kB
/ui/seek-button 11.27 kB 56.87 kB
/ui/seek-indicator 12.69 kB 56.87 kB
/ui/seek-indicator-value 1.10 kB
/ui/slider 11.65 kB 56.87 kB
/ui/slider-preview 2.01 kB 56.87 kB
/ui/status-announcer 10.60 kB 56.87 kB
/ui/status-indicator 13.01 kB 56.87 kB
/ui/status-indicator-value 1.10 kB
/ui/thumbnail 9.84 kB 56.87 kB
/ui/time 10.80 kB 56.87 kB
/ui/time-slider 12.99 kB 56.87 kB
/ui/time-slider-chapter-title 8.79 kB 56.87 kB
/ui/time-slider-chapters 9.63 kB 56.87 kB
/ui/title 8.17 kB 56.87 kB
/ui/tooltip 9.95 kB 56.87 kB
/ui/tooltip-label 1.21 kB
/ui/tooltip-shortcut 1.18 kB
/ui/volume-indicator 13.02 kB 56.87 kB
/ui/volume-indicator-fill 1.11 kB
/ui/volume-indicator-value 1.10 kB
/ui/volume-popover 14.74 kB 56.87 kB
/ui/volume-slider 12.31 kB 56.87 kB
⚛️ @videojs/react — 7 small size changes
Path Base initial PR initial Initial diff Lazy diff
/extensions/google-cast 1.59 kB 1.61 kB +14 B (+0.9%)
/extensions/mux-data 1.64 kB 1.65 kB +14 B (+0.8%)
/media/hls-audio 20.36 kB 20.36 kB -3 B (-0.0%)
/media/hls-video 26.35 kB 26.41 kB +65 B (+0.2%)
/media/native-hls-video 5.77 kB 5.90 kB +134 B (+2.3%)
/video/minimal-skin.css 7.52 kB 7.53 kB +8 B (+0.1%)
/video/skin.css 7.34 kB 7.35 kB +11 B (+0.1%)
Presets (7)
Entry Initial Lazy
/video (default) 50.61 kB 56.87 kB
/video (default + hls) 50.83 kB 56.87 kB
/video (minimal) 50.66 kB 56.87 kB
/video (minimal + hls) 51.06 kB 56.87 kB
/audio (default) 39.50 kB 56.87 kB
/audio (minimal) 39.56 kB 56.87 kB
/background 581 B 56.87 kB
Media (22)
Entry Initial
/media/mux-video/hls-js 958 B
/media/mux-video/spf 961 B
/media/mux-audio/hls-js 699 B
/media/mux-audio/spf 703 B
/media/background-video 402 B
/media/cloudflare-video 911 B
/media/dash-video 693 B
/media/hls-audio 20.36 kB
/media/hls-background-video 16.40 kB
/media/hls-video 26.41 kB
/media/hlsjs-video 695 B
/media/mux-audio 693 B
/media/mux-background-video 16.47 kB
/media/mux-video 950 B
/media/native-hls-video 5.90 kB
/media/shaka-video 704 B
/media/spotify-audio 901 B
/media/tiktok-video 906 B
/media/twitch-video 929 B
/media/vimeo-video 888 B
/media/wistia-video 619 B
/media/youtube-video 897 B
Extensions (2)
Entry Initial
/extensions/google-cast 1.61 kB
/extensions/mux-data 1.65 kB
Players (5)
Entry Initial Lazy
/video/player 7.56 kB 56.87 kB
/audio/player 4.00 kB 56.87 kB
/background/player 2.27 kB 56.87 kB
/live-video/player 6.95 kB 56.87 kB
/live-audio/player 3.99 kB 56.87 kB
Skins (18)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.53 kB
/video/skin.css css 7.35 kB
/video/minimal-skin js 50.63 kB 56.87 kB
/video/skin js 50.50 kB 56.87 kB
/audio/minimal-skin.css css 5.37 kB
/audio/skin.css css 5.18 kB
/audio/minimal-skin js 39.46 kB 56.87 kB
/audio/skin js 39.40 kB 56.87 kB
/background/skin.css css 90 B
/background/skin js 277 B
/live-video/minimal-skin.css css 6.07 kB
/live-video/skin.css css 6.05 kB
/live-video/minimal-skin js 42.57 kB 56.87 kB
/live-video/skin js 42.52 kB 56.87 kB
/live-audio/minimal-skin.css css 4.24 kB
/live-audio/skin.css css 4.21 kB
/live-audio/minimal-skin js 30.35 kB 56.87 kB
/live-audio/skin js 30.36 kB 56.87 kB
UI Components (39)
Entry Initial Lazy
/ui/airplay-button 10.45 kB 56.87 kB
/ui/alert-dialog 4.73 kB 56.87 kB
/ui/audio-track 7.92 kB 56.87 kB
/ui/audio-track-radio-group 9.48 kB 56.87 kB
/ui/buffering-indicator 7.79 kB 56.87 kB
/ui/captions-button 10.43 kB 56.87 kB
/ui/captions-radio-group 9.59 kB 56.87 kB
/ui/cast-button 10.43 kB 56.87 kB
/ui/controls 7.71 kB 56.87 kB
/ui/dialog 4.75 kB 56.87 kB
/ui/error-dialog 11.80 kB 56.87 kB
/ui/fullscreen-button 10.39 kB 56.87 kB
/ui/gesture 7.98 kB 56.87 kB
/ui/hotkey 8.38 kB 56.87 kB
/ui/live-button 8.80 kB 56.87 kB
/ui/menu 19.45 kB 56.87 kB
/ui/mute-button 10.44 kB 56.87 kB
/ui/pip-button 10.39 kB 56.87 kB
/ui/play-button 10.37 kB 56.87 kB
/ui/playback-rate 7.99 kB 56.87 kB
/ui/playback-rate-button 10.37 kB 56.87 kB
/ui/playback-rate-radio-group 9.53 kB 56.87 kB
/ui/popover 8.16 kB 56.87 kB
/ui/poster 7.57 kB 56.87 kB
/ui/quality 8.42 kB 56.87 kB
/ui/quality-radio-group 9.96 kB 56.87 kB
/ui/seek-button 10.41 kB 56.87 kB
/ui/seek-indicator 11.98 kB 56.87 kB
/ui/slider 12.74 kB 56.87 kB
/ui/status-announcer 10.10 kB 56.87 kB
/ui/status-indicator 12.29 kB 56.87 kB
/ui/thumbnail 8.73 kB 56.87 kB
/ui/time 9.77 kB 56.87 kB
/ui/time-slider 14.17 kB 56.87 kB
/ui/title 7.28 kB 56.87 kB
/ui/tooltip 8.93 kB 56.87 kB
/ui/volume-indicator 12.34 kB 56.87 kB
/ui/volume-popover 14.20 kB 56.87 kB
/ui/volume-slider 12.13 kB 56.87 kB
🧩 @videojs/core — no changes
Entries (76)
Entry Initial Lazy
. 13.15 kB
/dom 22.53 kB 56.87 kB
/vjsc 1.82 kB
/i18n 3.23 kB 56.87 kB
/i18n/locales/all 34.97 kB
/i18n/locales/ar 1.22 kB
/i18n/locales/az 1.09 kB
/i18n/locales/bg 1.29 kB
/i18n/locales/bn 1.23 kB
/i18n/locales/bs 1.00 kB
/i18n/locales/ca 1.07 kB
/i18n/locales/cs 1.07 kB
/i18n/locales/cy 1.02 kB
/i18n/locales/da 1006 B
/i18n/locales/de 1.09 kB
/i18n/locales/el 1.49 kB
/i18n/locales/en 785 B
/i18n/locales/es 1018 B
/i18n/locales/et 1.05 kB
/i18n/locales/eu 1.03 kB
/i18n/locales/fa 1.22 kB
/i18n/locales/fi 1.04 kB
/i18n/locales/fr 1.07 kB
/i18n/locales/gd 1.10 kB
/i18n/locales/gl 1022 B
/i18n/locales/he 1.14 kB
/i18n/locales/hi 1.30 kB
/i18n/locales/hr 1.02 kB
/i18n/locales/hu 1.10 kB
/i18n/locales/id 919 B
/i18n/locales/it 1.01 kB
/i18n/locales/ja 1.18 kB
/i18n/locales/ko 1.14 kB
/i18n/locales/lt 1.02 kB
/i18n/locales/lv 1.09 kB
/i18n/locales/mr 1.30 kB
/i18n/locales/nb 987 B
/i18n/locales/ne 1.29 kB
/i18n/locales/nl 1.00 kB
/i18n/locales/nn 987 B
/i18n/locales/oc 1.08 kB
/i18n/locales/pl 1.14 kB
/i18n/locales/pt 1.03 kB
/i18n/locales/pt-BR 1.03 kB
/i18n/locales/pt-PT 1.01 kB
/i18n/locales/ro 1.07 kB
/i18n/locales/ru 1.40 kB
/i18n/locales/sk 1.10 kB
/i18n/locales/sl 1.04 kB
/i18n/locales/sr 1.07 kB
/i18n/locales/sv 1.01 kB
/i18n/locales/te 1.32 kB
/i18n/locales/th 1.29 kB
/i18n/locales/tr 1.08 kB
/i18n/locales/uk 1.41 kB
/i18n/locales/vi 1.11 kB
/i18n/locales/zh 1.02 kB
/i18n/locales/zh-CN 1.02 kB
/i18n/locales/zh-TW 1.02 kB
/i18n/text/airplay 101 B
/i18n/text/buttons 137 B
/i18n/text/captions 93 B
/i18n/text/cast 114 B
/i18n/text/common 90 B
/i18n/text/container 75 B
/i18n/text/errors 285 B
/i18n/text/fullscreen 98 B
/i18n/text/live 126 B
/i18n/text/menu 249 B
/i18n/text/pip 101 B
/i18n/text/playback 80 B
/i18n/text/seek 105 B
/i18n/text/slider 65 B
/i18n/text/status 222 B
/i18n/text/time 280 B
/i18n/text/volume 133 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Initial
. 1.05 kB
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Initial
. 1.72 kB
/html 703 B
/react 365 B
🔧 @videojs/utils — no changes
Entries (13)
Entry Initial
/array 271 B
/dom 5.02 kB
/events 320 B
/function 386 B
/i18n 249 B
/jwt 176 B
/object 535 B
/predicate 297 B
/percent 281 B
/string 239 B
/style 212 B
/time 976 B
/number 199 B
📦 @videojs/cdn — no changes
📦 @videojs/cloudflare-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 3.70 kB 3.71 kB +6 B (+0.2%)
📦 @videojs/dash-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 3.29 kB 3.43 kB +142 B (+4.2%)
📦 @videojs/google-cast — no changes
📦 @videojs/hlsjs-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 134.63 kB 134.81 kB +180 B (+0.1%)

📦 @videojs/media

Path Base initial PR initial Initial diff Lazy diff
/dom 4.12 kB 5.02 kB +924 B (+21.9%)
Entries (3)
Entry Initial
. 1.14 kB
/dom 5.02 kB
/media-tracks 2.05 kB
📦 @videojs/mux — no changes
📦 @videojs/mux-audio — 2 small size changes
Path Base initial PR initial Initial diff Lazy diff
. 135.58 kB 135.78 kB +197 B (+0.1%)
/spf 20.84 kB 20.82 kB -15 B (-0.1%)
Entries (2)
Entry Initial
. 135.78 kB
/spf 20.82 kB
📦 @videojs/mux-data — no changes
📦 @videojs/mux-video — 2 small size changes
Path Base initial PR initial Initial diff Lazy diff
. 135.56 kB 135.73 kB +174 B (+0.1%)
/spf 26.75 kB 26.86 kB +105 B (+0.4%)
Entries (2)
Entry Initial
. 135.73 kB
/spf 26.86 kB
📦 @videojs/native-hls-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 5.25 kB 5.39 kB +143 B (+2.7%)
📦 @videojs/shaka-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 196.83 kB 196.93 kB +99 B (+0.0%)
📦 @videojs/spf — 2 small size changes
Path Base initial PR initial Initial diff Lazy diff
/hls-audio 19.84 kB 19.90 kB +61 B (+0.3%)
/hls-video 25.89 kB 25.99 kB +100 B (+0.4%)
Entries (7)
Entry Initial
. 4.55 kB
/dom 6.60 kB
/hls 22.06 kB
/media-tracks 504 B
/hls-background-video 15.84 kB
/hls-audio 19.90 kB
/hls-video 25.99 kB
📦 @videojs/spotify-audio — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 3.58 kB 3.59 kB +10 B (+0.3%)
📦 @videojs/tiktok-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 3.56 kB 3.57 kB +8 B (+0.2%)
📦 @videojs/twitch-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 3.63 kB 3.64 kB +11 B (+0.3%)
📦 @videojs/vimeo-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 10.59 kB 10.62 kB +29 B (+0.3%)
📦 @videojs/wistia-video — 2 small size changes
Path Base initial PR initial Initial diff Lazy diff
. 1.91 kB 1.92 kB +3 B (+0.2%)
/helpers 1.77 kB 1.77 kB +2 B (+0.1%)
Entries (2)
Entry Initial
. 1.92 kB
/helpers 1.77 kB
📦 @videojs/youtube-video — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 4.24 kB 4.24 kB +2 B (+0.0%)

ℹ️ How to interpret

Each entry is independently bundled, minified, and brotli-compressed. Initial size includes its static import graph; lazy dynamic chunks are reported separately.

Entries are not additive because their dependency graphs overlap. Preset rows represent realistic combined bundles.

Changes of 300 B or less across initial, lazy, and total size are collapsed, not discarded.

Run pnpm size locally to check current initial sizes.

@netlify

netlify Bot commented Sep 3, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit ed0bb45
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a9f5cfa17ec3f0008f2cf2e
😎 Deploy Preview https://deploy-preview-2603--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

mihar-22 and others added 2 commits September 6, 2026 20:56
`CustomMediaElement(Adapter)` reads the element it renders and attaches to
from the adapter's static `host` ('video' | 'audio' | 'iframe') instead of a
tag argument. `HTMLVideoAdapter` and `HTMLAudioAdapter` declare theirs, the
iframe embed adapters declare 'iframe', and the hls.js-backed
`MuxAudioAdapter` overrides the video base it extends.

`createMediaElement` drops its `tag` option, leaving `template` as the only
knob, and the element's static `getTemplateHTML` is renamed `template`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The API-docs builder follows the host class chain, through mixin calls and
hoisted bases, to its static `host` or the `HTMLVideoAdapter` /
`HTMLAudioAdapter` root, now that neither factory names the target.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b034e90. Configure here.

Comment thread site/scripts/api-docs-builder/src/media-element-handler.ts Outdated
@mihar-22
mihar-22 marked this pull request as draft September 7, 2026 04:23
mihar-22 and others added 3 commits September 6, 2026 21:26
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define file exports `SpotifyAudioElement`, so the `endsWith('Audio')`
check has to run on the suffix-stripped name. Adds an `EmbedAudio` fixture
so the iframe-audio path stays covered.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` is `'video' | 'audio'` so the hls.js
`MuxAudioAdapter` override still fits, and every other adapter keeps the
literal, so façades can read the host off the type as well as the value.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
It shadowed the `PlaybackAdapter` interface; the builder fixture stub
mirrors the name and types the static `host` it reads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mihar-22 and others added 5 commits September 6, 2026 22:46
…clared

`streamType` is settable on every media adapter, but the DASH and SPF audio
adapters left it out of `defaultProps`, so neither `<dash-video stream-type>`
nor the React `streamType` prop reached them by contract.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An adapter declares two statics, `host` and `defaultProps`, and the element
derives everything else from them. Each primitive default becomes a content
attribute of that type, named by its WHATWG spelling or kebab-case, and reset
to the default when removed; objects stay property-only. The base adapters
declare the attributes their native element accepts (`videoContentAttributes`,
`audioContentAttributes`), which pass through to the inner element; an iframe
passes nothing. `static properties` is gone.

`CustomMediaElement(Adapter, { template? })` is now a thin composition of
pieces that stand on their own: `renderHost`, `AdapterAttachment`,
`forwardAdapter` / `reflectAttributes` / `bridgeEvent`, and `MediaChildren`.
Definition state is per class and the light-DOM target lookup only adopts
direct children.

BREAKING CHANGE: `CustomMediaElement` no longer takes a tag or exposes
`static properties`; adapters must declare `static host` and
`static defaultProps`. Audio elements drop the video-only attributes and
embeds reflect only the attributes their adapter declares.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The builder reads the native attribute lists beside the base adapters and
derives custom attributes from each adapter's `defaultProps`, the same rule
the element applies, instead of parsing `static properties`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eces

`<background-video>` renders through `renderHost` and `<hls-background-video>`
keeps its engine attached and destroyed through `AdapterAttachment`, instead
of each hand-rolling the shadow root and the deferred teardown.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The fixture stub asked for an adapter with `host` and `defaultProps`; the
fixture base adapters only had `host` where the builder needed it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mihar-22 and others added 8 commits September 6, 2026 23:05
The `create-media-component` reference lands in #2604, so linking it here
broke the site build for this PR alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The html façades follow the UI elements: `media/<name>/element.ts` exports
`<Name>Element` carrying its static `tagName`, and the define entry only
registers it. `<mux-background-video>` is a subclass of
`HlsBackgroundVideoElement` with its own tag, since one class can hold one
tag name.

BREAKING CHANGE: `HlsJsVideo`, `SpotifyAudio`, and the other html media
classes are now `HlsJsVideoElement`, `SpotifyAudioElement`, and so on, and
declare their own `tagName`. `@videojs/html/media/<name>` keeps exporting the
element class.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define entries no longer declare a class, so the builder resolves what
they pass to `safeDefine()` to the element class in the media directory and
reads its `tagName` and composition there. Fixtures follow the element.ts
layout, including a flavour reached through a nested barrel.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`renderHost(element, attrs)` takes the template and shadow root options from
the element's own constructor, so callers stop passing what the class already
declares.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Six embeds each carried the same `<style>` and `<iframe>` markup and the same
attribute-to-props mapper. `embedTemplate` renders the frame from what
differs (URL, feature policy, extra attributes, host sizing, the rule for a
host without controls), and `propsFromAttributes` in `@videojs/media/dom`
reads the initial attributes through the adapter's `defaultProps`, the same
coercion the element applies later.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` was typed `'video' | 'audio'` only so the hls.js
`MuxAudioAdapter`, which extends the video adapter, could override it. That
made every video adapter's `host` a union in the types. The base is `'video'`
again and the Mux audio adapter casts its inverted base to drop the literal,
the same way `MuxBackgroundVideoElement` does for `tagName`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Attributes:
- A boolean that defaults to `true` stays property-only. An absent HTML
  boolean attribute cannot mean `true`, so `<x playsinline>` could never turn
  it off and `el.playsInline = false` did nothing.
- A property from `defaultProps` owns its attribute by definition, so an
  adapter written with fields rather than accessors works; one exposed
  read-only only reflects, with a development warning.
- `defaultMuted` owns the `muted` attribute, as in HTML, and seeds the `muted`
  state with it. It used to write `muted` instead, so an embed that stores the
  two apart built an unmuted URL after `el.defaultMuted = true`.
- A number that does not parse falls back to the default.
- A target that arrives later gets every attribute already on the element
  replayed, and only a direct child of the host tag is adopted as `media`.

Surface:
- Forwarding copies `enumerable`, treats underscore-prefixed members as
  private, keeps `title` for the element, and reports other collisions in
  development.
- The event bridge forwards composed events an adapter raises itself, skipping
  only the adapter's copies of events its target dispatched, keeps a stable
  handler, and is torn down with the adapter.
- `MediaChildren` removes an attribute from the clone when it leaves the
  child, and can disconnect.
- `@videojs/media/dom` exports only the deliberate extension points.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant