@livepeer-frameworks/player-wc 0.1.3 → 0.1.4

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.
@@ -14,6 +14,8 @@ exports.FwPlayerControls = class FwPlayerControls extends lit.LitElement {
14
14
  super(...arguments);
15
15
  this.playbackMode = "auto";
16
16
  this.isContentLive = false;
17
+ this.devMode = false;
18
+ this.isDevPanelOpen = false;
17
19
  this.showStatsButton = false;
18
20
  this.isStatsOpen = false;
19
21
  this._settingsOpen = false;
@@ -203,6 +205,12 @@ exports.FwPlayerControls = class FwPlayerControls extends lit.LitElement {
203
205
  const state = this.pc.s;
204
206
  const disabled = !state.videoElement;
205
207
  const context = this._getSeekingContext();
208
+ const shouldShowControls = state.shouldShowControls ||
209
+ state.isPaused ||
210
+ !state.hasPlaybackStarted ||
211
+ state.shouldShowIdleScreen ||
212
+ !!state.error ||
213
+ this._settingsOpen;
206
214
  const timeDisplay = playerCore.formatTimeDisplay({
207
215
  isLive: context.isLive,
208
216
  currentTime: state.currentTime,
@@ -217,8 +225,8 @@ exports.FwPlayerControls = class FwPlayerControls extends lit.LitElement {
217
225
  class=${classMap_js.classMap({
218
226
  "fw-player-surface": true,
219
227
  "fw-controls-wrapper": true,
220
- "fw-controls-wrapper--visible": state.shouldShowControls,
221
- "fw-controls-wrapper--hidden": !state.shouldShowControls,
228
+ "fw-controls-wrapper--visible": shouldShowControls,
229
+ "fw-controls-wrapper--hidden": !shouldShowControls,
222
230
  })}
223
231
  >
224
232
  <div class="fw-control-bar" @click=${(event) => event.stopPropagation()}>
@@ -335,6 +343,29 @@ exports.FwPlayerControls = class FwPlayerControls extends lit.LitElement {
335
343
  </div>
336
344
  `
337
345
  : lit.nothing}
346
+ ${this.devMode
347
+ ? lit.html `
348
+ <div class="fw-control-group">
349
+ <button
350
+ type="button"
351
+ class=${classMap_js.classMap({
352
+ "fw-btn-flush": true,
353
+ "fw-btn-flush--active": this.isDevPanelOpen,
354
+ })}
355
+ aria-label=${this.isDevPanelOpen
356
+ ? "Hide advanced settings sidebar"
357
+ : "Show advanced settings sidebar"}
358
+ title=${this.isDevPanelOpen ? "Hide advanced sidebar" : "Advanced sidebar"}
359
+ @click=${() => this.dispatchEvent(new CustomEvent("fw-dev-panel-toggle", {
360
+ bubbles: true,
361
+ composed: true,
362
+ }))}
363
+ >
364
+ <span class="fw-dev-toggle-label">ADV</span>
365
+ </button>
366
+ </div>
367
+ `
368
+ : lit.nothing}
338
369
 
339
370
  <div class="fw-control-group fw-settings-anchor">
340
371
  <button
@@ -400,6 +431,12 @@ exports.FwPlayerControls.styles = [
400
431
  .fw-settings-anchor {
401
432
  position: relative;
402
433
  }
434
+
435
+ .fw-dev-toggle-label {
436
+ font-size: 10px;
437
+ font-weight: 700;
438
+ letter-spacing: 0.08em;
439
+ }
403
440
  `,
404
441
  ];
405
442
  tslib_es6.__decorate([
@@ -411,6 +448,12 @@ tslib_es6.__decorate([
411
448
  tslib_es6.__decorate([
412
449
  decorators_js.property({ type: Boolean, attribute: "is-content-live" })
413
450
  ], exports.FwPlayerControls.prototype, "isContentLive", void 0);
451
+ tslib_es6.__decorate([
452
+ decorators_js.property({ type: Boolean, attribute: "dev-mode" })
453
+ ], exports.FwPlayerControls.prototype, "devMode", void 0);
454
+ tslib_es6.__decorate([
455
+ decorators_js.property({ type: Boolean, attribute: "is-dev-panel-open" })
456
+ ], exports.FwPlayerControls.prototype, "isDevPanelOpen", void 0);
414
457
  tslib_es6.__decorate([
415
458
  decorators_js.property({ type: Boolean, attribute: "show-stats-button" })
416
459
  ], exports.FwPlayerControls.prototype, "showStatsButton", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"fw-player-controls.js","sources":["../../../../src/components/fw-player-controls.ts"],"sourcesContent":["/**\n * <fw-player-controls> — Player controls with seek, volume, live state, and settings.\n * Parity port of React/Svelte control behavior.\n */\nimport { LitElement, html, css, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport { sharedStyles } from \"../styles/shared-styles.js\";\nimport { utilityStyles } from \"../styles/utility-styles.js\";\nimport {\n playIcon,\n pauseIcon,\n fullscreenIcon,\n fullscreenExitIcon,\n settingsIcon,\n seekToLiveIcon,\n skipBackIcon,\n skipForwardIcon,\n statsIcon,\n} from \"../icons/index.js\";\nimport {\n calculateIsNearLive,\n calculateLiveThresholds,\n calculateSeekableRange,\n canSeekStream,\n formatTimeDisplay,\n isLiveContent,\n isMediaStreamSource,\n type MistStreamInfo,\n type PlaybackMode,\n} from \"@livepeer-frameworks/player-core\";\nimport type { PlayerControllerHost } from \"../controllers/player-controller-host.js\";\n\ninterface SeekingContext {\n mistStreamInfo?: MistStreamInfo;\n isLive: boolean;\n sourceType?: string;\n seekableStart: number;\n liveEdge: number;\n hasDvrWindow: boolean;\n canSeek: boolean;\n commitOnRelease: boolean;\n liveThresholds: ReturnType<typeof calculateLiveThresholds>;\n}\n\n@customElement(\"fw-player-controls\")\nexport class FwPlayerControls extends LitElement {\n @property({ attribute: false }) pc!: PlayerControllerHost;\n @property({ type: String }) playbackMode: PlaybackMode = \"auto\";\n @property({ type: Boolean, attribute: \"is-content-live\" }) isContentLive = false;\n @property({ type: Boolean, attribute: \"show-stats-button\" }) showStatsButton = false;\n @property({ type: Boolean, attribute: \"is-stats-open\" }) isStatsOpen = false;\n\n @state() private _settingsOpen = false;\n @state() private _isNearLiveState = true;\n @state() private _buffered: TimeRanges | null = null;\n\n private _boundVideo: HTMLVideoElement | null = null;\n private _onBufferedUpdate: (() => void) | null = null;\n\n static styles = [\n sharedStyles,\n utilityStyles,\n css`\n :host {\n display: contents;\n }\n\n .fw-settings-anchor {\n position: relative;\n }\n `,\n ];\n\n connectedCallback(): void {\n super.connectedCallback();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._unbindVideoEvents();\n this._detachWindowClickListener();\n }\n\n protected updated(changed: PropertyValues<this>): void {\n this._bindVideoEvents();\n this._reconcileNearLiveState();\n\n if (changed.has(\"_settingsOpen\" as keyof FwPlayerControls)) {\n if (this._settingsOpen) {\n this._attachWindowClickListener();\n } else {\n this._detachWindowClickListener();\n }\n }\n }\n\n private _bindVideoEvents(): void {\n const video = this.pc?.s.videoElement ?? null;\n if (video === this._boundVideo) {\n return;\n }\n\n this._unbindVideoEvents();\n this._boundVideo = video;\n\n if (!video) {\n this._buffered = null;\n return;\n }\n\n const updateBuffered = () => {\n this._buffered = this.pc.getBufferedRanges() ?? video.buffered;\n };\n\n updateBuffered();\n video.addEventListener(\"progress\", updateBuffered);\n video.addEventListener(\"loadeddata\", updateBuffered);\n this._onBufferedUpdate = updateBuffered;\n }\n\n private _unbindVideoEvents(): void {\n if (!this._boundVideo) {\n return;\n }\n\n const updateBuffered = this._onBufferedUpdate;\n if (updateBuffered) {\n this._boundVideo.removeEventListener(\"progress\", updateBuffered);\n this._boundVideo.removeEventListener(\"loadeddata\", updateBuffered);\n }\n\n this._boundVideo = null;\n this._onBufferedUpdate = null;\n }\n\n private _attachWindowClickListener(): void {\n window.setTimeout(() => {\n if (!this._settingsOpen) {\n return;\n }\n window.addEventListener(\"click\", this._onWindowClick);\n }, 0);\n }\n\n private _detachWindowClickListener(): void {\n window.removeEventListener(\"click\", this._onWindowClick);\n }\n\n private _onWindowClick = (event: MouseEvent): void => {\n const path = event.composedPath();\n const insideControls = path.some((entry) => {\n if (!(entry instanceof HTMLElement)) {\n return false;\n }\n return (\n entry.classList.contains(\"fw-settings-anchor\") ||\n entry.classList.contains(\"fw-settings-menu\")\n );\n });\n\n if (!insideControls) {\n this._settingsOpen = false;\n }\n };\n\n private _deriveBufferWindowMs(\n tracks?: Record<string, { firstms?: number; lastms?: number }>\n ): number | undefined {\n if (!tracks) {\n return undefined;\n }\n\n const trackValues = Object.values(tracks);\n if (trackValues.length === 0) {\n return undefined;\n }\n\n const firstmsValues = trackValues\n .map((track) => track.firstms)\n .filter((value): value is number => typeof value === \"number\");\n const lastmsValues = trackValues\n .map((track) => track.lastms)\n .filter((value): value is number => typeof value === \"number\");\n\n if (firstmsValues.length === 0 || lastmsValues.length === 0) {\n return undefined;\n }\n\n const firstms = Math.max(...firstmsValues);\n const lastms = Math.min(...lastmsValues);\n const window = lastms - firstms;\n\n if (!Number.isFinite(window) || window <= 0) {\n return undefined;\n }\n\n return window;\n }\n\n private _getSeekingContext(): SeekingContext {\n const state = this.pc.s;\n const controller = this.pc.getController();\n const sourceType = state.currentSourceInfo?.type;\n const mistStreamInfo = state.streamState?.streamInfo as MistStreamInfo | undefined;\n\n const isLive = isLiveContent(this.isContentLive, mistStreamInfo, state.duration);\n const bufferWindowMs =\n mistStreamInfo?.meta?.buffer_window ??\n this._deriveBufferWindowMs(\n mistStreamInfo?.meta?.tracks as\n | Record<string, { firstms?: number; lastms?: number }>\n | undefined\n );\n\n const isWebRTC = isMediaStreamSource(state.videoElement);\n\n const allowMediaStreamDvr =\n isMediaStreamSource(state.videoElement) &&\n bufferWindowMs !== undefined &&\n bufferWindowMs > 0 &&\n sourceType !== \"whep\" &&\n sourceType !== \"webrtc\";\n\n const calculatedRange = calculateSeekableRange({\n isLive,\n video: state.videoElement,\n mistStreamInfo,\n currentTime: state.currentTime,\n duration: state.duration,\n allowMediaStreamDvr,\n });\n\n const controllerSeekableStart = this.pc.getSeekableStart();\n const controllerLiveEdge = this.pc.getLiveEdge();\n\n const useControllerRange =\n Number.isFinite(controllerSeekableStart) &&\n Number.isFinite(controllerLiveEdge) &&\n controllerLiveEdge >= controllerSeekableStart &&\n (controllerLiveEdge > 0 || controllerSeekableStart > 0);\n\n const seekableStart = useControllerRange\n ? controllerSeekableStart\n : calculatedRange.seekableStart;\n const liveEdge = useControllerRange ? controllerLiveEdge : calculatedRange.liveEdge;\n\n const hasDvrWindow =\n isLive &&\n Number.isFinite(liveEdge) &&\n Number.isFinite(seekableStart) &&\n liveEdge > seekableStart;\n\n const baseCanSeek =\n controller?.canSeekStream?.() ??\n canSeekStream({\n video: state.videoElement,\n isLive,\n duration: state.duration,\n bufferWindowMs,\n });\n\n const liveThresholds = calculateLiveThresholds(sourceType, isWebRTC, bufferWindowMs);\n\n return {\n mistStreamInfo,\n isLive,\n sourceType,\n seekableStart,\n liveEdge,\n hasDvrWindow,\n canSeek: baseCanSeek && (!isLive || hasDvrWindow),\n commitOnRelease: isLive,\n liveThresholds,\n };\n }\n\n private _reconcileNearLiveState(): void {\n const context = this._getSeekingContext();\n\n if (!context.isLive) {\n if (!this._isNearLiveState) {\n this._isNearLiveState = true;\n }\n return;\n }\n\n const next = calculateIsNearLive(\n this.pc.s.currentTime,\n context.liveEdge,\n context.liveThresholds,\n this._isNearLiveState\n );\n\n if (next !== this._isNearLiveState) {\n this._isNearLiveState = next;\n }\n }\n\n private _handleModeChange(\n event: CustomEvent<{ mode: \"auto\" | \"low-latency\" | \"quality\" }>\n ): void {\n const { mode } = event.detail;\n this.dispatchEvent(\n new CustomEvent(\"fw-mode-change\", {\n detail: { mode },\n bubbles: true,\n composed: true,\n })\n );\n }\n\n protected render() {\n const state = this.pc.s;\n const disabled = !state.videoElement;\n const context = this._getSeekingContext();\n\n const timeDisplay = formatTimeDisplay({\n isLive: context.isLive,\n currentTime: state.currentTime,\n duration: state.duration,\n liveEdge: context.liveEdge,\n seekableStart: context.seekableStart,\n unixoffset: context.mistStreamInfo?.unixoffset,\n });\n\n const liveButtonDisabled = !context.hasDvrWindow || this._isNearLiveState;\n\n return html`\n <div\n class=${classMap({\n \"fw-player-surface\": true,\n \"fw-controls-wrapper\": true,\n \"fw-controls-wrapper--visible\": state.shouldShowControls,\n \"fw-controls-wrapper--hidden\": !state.shouldShowControls,\n })}\n >\n <div class=\"fw-control-bar\" @click=${(event: Event) => event.stopPropagation()}>\n ${context.canSeek\n ? html`\n <div class=\"fw-seek-wrapper\">\n <fw-seek-bar\n .currentTime=${state.currentTime}\n .duration=${state.duration}\n .buffered=${this._buffered}\n .disabled=${disabled}\n .isLive=${context.isLive}\n .seekableStart=${context.seekableStart}\n .liveEdge=${context.liveEdge}\n .commitOnRelease=${context.commitOnRelease}\n @fw-seek=${(event: CustomEvent<{ time: number }>) =>\n this.pc.seek(event.detail.time)}\n ></fw-seek-bar>\n </div>\n `\n : nothing}\n\n <div class=\"fw-controls-row\">\n <div class=\"fw-controls-left\">\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=\"fw-btn-flush\"\n ?disabled=${disabled}\n aria-label=${state.isPlaying ? \"Pause\" : \"Play\"}\n @click=${() => this.pc.togglePlay()}\n >\n ${state.isPlaying ? pauseIcon(18) : playIcon(18)}\n </button>\n\n ${context.canSeek\n ? html`\n <button\n type=\"button\"\n class=\"fw-btn-flush hidden sm:flex\"\n ?disabled=${disabled}\n aria-label=\"Skip back 10 seconds\"\n @click=${() => this.pc.seekBy(-10)}\n >\n ${skipBackIcon(16)}\n </button>\n <button\n type=\"button\"\n class=\"fw-btn-flush hidden sm:flex\"\n ?disabled=${disabled}\n aria-label=\"Skip forward 10 seconds\"\n @click=${() => this.pc.seekBy(10)}\n >\n ${skipForwardIcon(16)}\n </button>\n `\n : nothing}\n </div>\n\n <div class=\"fw-control-group\">\n <fw-volume-control .pc=${this.pc}></fw-volume-control>\n </div>\n\n <div class=\"fw-control-group\">\n <span class=\"fw-time-display\">${timeDisplay}</span>\n </div>\n\n ${context.isLive\n ? html`\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n @click=${() => this.pc.jumpToLive()}\n ?disabled=${liveButtonDisabled}\n class=${classMap({\n \"fw-live-badge\": true,\n \"fw-live-badge--active\": liveButtonDisabled,\n \"fw-live-badge--behind\": !liveButtonDisabled,\n })}\n title=${!context.hasDvrWindow\n ? \"Live only\"\n : this._isNearLiveState\n ? \"At live edge\"\n : \"Jump to live\"}\n >\n LIVE\n ${!this._isNearLiveState && context.hasDvrWindow\n ? seekToLiveIcon(10)\n : nothing}\n </button>\n </div>\n `\n : nothing}\n </div>\n\n <div class=\"fw-controls-right\">\n ${this.showStatsButton\n ? html`\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-btn-flush\": true,\n \"fw-btn-flush--active\": this.isStatsOpen,\n })}\n aria-label=\"Toggle stats\"\n title=\"Stats\"\n @click=${() =>\n this.dispatchEvent(\n new CustomEvent(\"fw-stats-toggle\", {\n bubbles: true,\n composed: true,\n })\n )}\n >\n ${statsIcon(16)}\n </button>\n </div>\n `\n : nothing}\n\n <div class=\"fw-control-group fw-settings-anchor\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-btn-flush\": true,\n group: true,\n \"fw-btn-flush--active\": this._settingsOpen,\n })}\n aria-label=\"Settings\"\n title=\"Settings\"\n ?disabled=${disabled}\n @click=${() => {\n if (disabled) {\n return;\n }\n this._settingsOpen = !this._settingsOpen;\n }}\n >\n <span class=\"transition-transform group-hover:rotate-90\"\n >${settingsIcon(16)}</span\n >\n </button>\n\n <fw-settings-menu\n .pc=${this.pc}\n .open=${this._settingsOpen}\n .playbackMode=${this.playbackMode}\n .isContentLive=${this.isContentLive}\n @fw-close=${() => {\n this._settingsOpen = false;\n }}\n @fw-mode-change=${this._handleModeChange}\n ></fw-settings-menu>\n </div>\n\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=\"fw-btn-flush\"\n ?disabled=${disabled}\n aria-label=${state.isFullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n @click=${() => this.pc.toggleFullscreen()}\n >\n ${state.isFullscreen ? fullscreenExitIcon(16) : fullscreenIcon(16)}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fw-player-controls\": FwPlayerControls;\n }\n}\n"],"names":["FwPlayerControls","LitElement","isLiveContent","isMediaStreamSource","calculateSeekableRange","canSeekStream","calculateLiveThresholds","calculateIsNearLive","formatTimeDisplay","html","classMap","nothing","pauseIcon","playIcon","skipBackIcon","skipForwardIcon","seekToLiveIcon","statsIcon","settingsIcon","fullscreenExitIcon","fullscreenIcon","sharedStyles","utilityStyles","css","__decorate","property","state","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"fw-player-controls.js","sources":["../../../../src/components/fw-player-controls.ts"],"sourcesContent":["/**\n * <fw-player-controls> — Player controls with seek, volume, live state, and settings.\n * Parity port of React/Svelte control behavior.\n */\nimport { LitElement, html, css, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport { sharedStyles } from \"../styles/shared-styles.js\";\nimport { utilityStyles } from \"../styles/utility-styles.js\";\nimport {\n playIcon,\n pauseIcon,\n fullscreenIcon,\n fullscreenExitIcon,\n settingsIcon,\n seekToLiveIcon,\n skipBackIcon,\n skipForwardIcon,\n statsIcon,\n} from \"../icons/index.js\";\nimport {\n calculateIsNearLive,\n calculateLiveThresholds,\n calculateSeekableRange,\n canSeekStream,\n formatTimeDisplay,\n isLiveContent,\n isMediaStreamSource,\n type MistStreamInfo,\n type PlaybackMode,\n} from \"@livepeer-frameworks/player-core\";\nimport type { PlayerControllerHost } from \"../controllers/player-controller-host.js\";\n\ninterface SeekingContext {\n mistStreamInfo?: MistStreamInfo;\n isLive: boolean;\n sourceType?: string;\n seekableStart: number;\n liveEdge: number;\n hasDvrWindow: boolean;\n canSeek: boolean;\n commitOnRelease: boolean;\n liveThresholds: ReturnType<typeof calculateLiveThresholds>;\n}\n\n@customElement(\"fw-player-controls\")\nexport class FwPlayerControls extends LitElement {\n @property({ attribute: false }) pc!: PlayerControllerHost;\n @property({ type: String }) playbackMode: PlaybackMode = \"auto\";\n @property({ type: Boolean, attribute: \"is-content-live\" }) isContentLive = false;\n @property({ type: Boolean, attribute: \"dev-mode\" }) devMode = false;\n @property({ type: Boolean, attribute: \"is-dev-panel-open\" }) isDevPanelOpen = false;\n @property({ type: Boolean, attribute: \"show-stats-button\" }) showStatsButton = false;\n @property({ type: Boolean, attribute: \"is-stats-open\" }) isStatsOpen = false;\n\n @state() private _settingsOpen = false;\n @state() private _isNearLiveState = true;\n @state() private _buffered: TimeRanges | null = null;\n\n private _boundVideo: HTMLVideoElement | null = null;\n private _onBufferedUpdate: (() => void) | null = null;\n\n static styles = [\n sharedStyles,\n utilityStyles,\n css`\n :host {\n display: contents;\n }\n\n .fw-settings-anchor {\n position: relative;\n }\n\n .fw-dev-toggle-label {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.08em;\n }\n `,\n ];\n\n connectedCallback(): void {\n super.connectedCallback();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._unbindVideoEvents();\n this._detachWindowClickListener();\n }\n\n protected updated(changed: PropertyValues<this>): void {\n this._bindVideoEvents();\n this._reconcileNearLiveState();\n\n if (changed.has(\"_settingsOpen\" as keyof FwPlayerControls)) {\n if (this._settingsOpen) {\n this._attachWindowClickListener();\n } else {\n this._detachWindowClickListener();\n }\n }\n }\n\n private _bindVideoEvents(): void {\n const video = this.pc?.s.videoElement ?? null;\n if (video === this._boundVideo) {\n return;\n }\n\n this._unbindVideoEvents();\n this._boundVideo = video;\n\n if (!video) {\n this._buffered = null;\n return;\n }\n\n const updateBuffered = () => {\n this._buffered = this.pc.getBufferedRanges() ?? video.buffered;\n };\n\n updateBuffered();\n video.addEventListener(\"progress\", updateBuffered);\n video.addEventListener(\"loadeddata\", updateBuffered);\n this._onBufferedUpdate = updateBuffered;\n }\n\n private _unbindVideoEvents(): void {\n if (!this._boundVideo) {\n return;\n }\n\n const updateBuffered = this._onBufferedUpdate;\n if (updateBuffered) {\n this._boundVideo.removeEventListener(\"progress\", updateBuffered);\n this._boundVideo.removeEventListener(\"loadeddata\", updateBuffered);\n }\n\n this._boundVideo = null;\n this._onBufferedUpdate = null;\n }\n\n private _attachWindowClickListener(): void {\n window.setTimeout(() => {\n if (!this._settingsOpen) {\n return;\n }\n window.addEventListener(\"click\", this._onWindowClick);\n }, 0);\n }\n\n private _detachWindowClickListener(): void {\n window.removeEventListener(\"click\", this._onWindowClick);\n }\n\n private _onWindowClick = (event: MouseEvent): void => {\n const path = event.composedPath();\n const insideControls = path.some((entry) => {\n if (!(entry instanceof HTMLElement)) {\n return false;\n }\n return (\n entry.classList.contains(\"fw-settings-anchor\") ||\n entry.classList.contains(\"fw-settings-menu\")\n );\n });\n\n if (!insideControls) {\n this._settingsOpen = false;\n }\n };\n\n private _deriveBufferWindowMs(\n tracks?: Record<string, { firstms?: number; lastms?: number }>\n ): number | undefined {\n if (!tracks) {\n return undefined;\n }\n\n const trackValues = Object.values(tracks);\n if (trackValues.length === 0) {\n return undefined;\n }\n\n const firstmsValues = trackValues\n .map((track) => track.firstms)\n .filter((value): value is number => typeof value === \"number\");\n const lastmsValues = trackValues\n .map((track) => track.lastms)\n .filter((value): value is number => typeof value === \"number\");\n\n if (firstmsValues.length === 0 || lastmsValues.length === 0) {\n return undefined;\n }\n\n const firstms = Math.max(...firstmsValues);\n const lastms = Math.min(...lastmsValues);\n const window = lastms - firstms;\n\n if (!Number.isFinite(window) || window <= 0) {\n return undefined;\n }\n\n return window;\n }\n\n private _getSeekingContext(): SeekingContext {\n const state = this.pc.s;\n const controller = this.pc.getController();\n const sourceType = state.currentSourceInfo?.type;\n const mistStreamInfo = state.streamState?.streamInfo as MistStreamInfo | undefined;\n\n const isLive = isLiveContent(this.isContentLive, mistStreamInfo, state.duration);\n const bufferWindowMs =\n mistStreamInfo?.meta?.buffer_window ??\n this._deriveBufferWindowMs(\n mistStreamInfo?.meta?.tracks as\n | Record<string, { firstms?: number; lastms?: number }>\n | undefined\n );\n\n const isWebRTC = isMediaStreamSource(state.videoElement);\n\n const allowMediaStreamDvr =\n isMediaStreamSource(state.videoElement) &&\n bufferWindowMs !== undefined &&\n bufferWindowMs > 0 &&\n sourceType !== \"whep\" &&\n sourceType !== \"webrtc\";\n\n const calculatedRange = calculateSeekableRange({\n isLive,\n video: state.videoElement,\n mistStreamInfo,\n currentTime: state.currentTime,\n duration: state.duration,\n allowMediaStreamDvr,\n });\n\n const controllerSeekableStart = this.pc.getSeekableStart();\n const controllerLiveEdge = this.pc.getLiveEdge();\n\n const useControllerRange =\n Number.isFinite(controllerSeekableStart) &&\n Number.isFinite(controllerLiveEdge) &&\n controllerLiveEdge >= controllerSeekableStart &&\n (controllerLiveEdge > 0 || controllerSeekableStart > 0);\n\n const seekableStart = useControllerRange\n ? controllerSeekableStart\n : calculatedRange.seekableStart;\n const liveEdge = useControllerRange ? controllerLiveEdge : calculatedRange.liveEdge;\n\n const hasDvrWindow =\n isLive &&\n Number.isFinite(liveEdge) &&\n Number.isFinite(seekableStart) &&\n liveEdge > seekableStart;\n\n const baseCanSeek =\n controller?.canSeekStream?.() ??\n canSeekStream({\n video: state.videoElement,\n isLive,\n duration: state.duration,\n bufferWindowMs,\n });\n\n const liveThresholds = calculateLiveThresholds(sourceType, isWebRTC, bufferWindowMs);\n\n return {\n mistStreamInfo,\n isLive,\n sourceType,\n seekableStart,\n liveEdge,\n hasDvrWindow,\n canSeek: baseCanSeek && (!isLive || hasDvrWindow),\n commitOnRelease: isLive,\n liveThresholds,\n };\n }\n\n private _reconcileNearLiveState(): void {\n const context = this._getSeekingContext();\n\n if (!context.isLive) {\n if (!this._isNearLiveState) {\n this._isNearLiveState = true;\n }\n return;\n }\n\n const next = calculateIsNearLive(\n this.pc.s.currentTime,\n context.liveEdge,\n context.liveThresholds,\n this._isNearLiveState\n );\n\n if (next !== this._isNearLiveState) {\n this._isNearLiveState = next;\n }\n }\n\n private _handleModeChange(\n event: CustomEvent<{ mode: \"auto\" | \"low-latency\" | \"quality\" }>\n ): void {\n const { mode } = event.detail;\n this.dispatchEvent(\n new CustomEvent(\"fw-mode-change\", {\n detail: { mode },\n bubbles: true,\n composed: true,\n })\n );\n }\n\n protected render() {\n const state = this.pc.s;\n const disabled = !state.videoElement;\n const context = this._getSeekingContext();\n const shouldShowControls =\n state.shouldShowControls ||\n state.isPaused ||\n !state.hasPlaybackStarted ||\n state.shouldShowIdleScreen ||\n !!state.error ||\n this._settingsOpen;\n\n const timeDisplay = formatTimeDisplay({\n isLive: context.isLive,\n currentTime: state.currentTime,\n duration: state.duration,\n liveEdge: context.liveEdge,\n seekableStart: context.seekableStart,\n unixoffset: context.mistStreamInfo?.unixoffset,\n });\n\n const liveButtonDisabled = !context.hasDvrWindow || this._isNearLiveState;\n\n return html`\n <div\n class=${classMap({\n \"fw-player-surface\": true,\n \"fw-controls-wrapper\": true,\n \"fw-controls-wrapper--visible\": shouldShowControls,\n \"fw-controls-wrapper--hidden\": !shouldShowControls,\n })}\n >\n <div class=\"fw-control-bar\" @click=${(event: Event) => event.stopPropagation()}>\n ${context.canSeek\n ? html`\n <div class=\"fw-seek-wrapper\">\n <fw-seek-bar\n .currentTime=${state.currentTime}\n .duration=${state.duration}\n .buffered=${this._buffered}\n .disabled=${disabled}\n .isLive=${context.isLive}\n .seekableStart=${context.seekableStart}\n .liveEdge=${context.liveEdge}\n .commitOnRelease=${context.commitOnRelease}\n @fw-seek=${(event: CustomEvent<{ time: number }>) =>\n this.pc.seek(event.detail.time)}\n ></fw-seek-bar>\n </div>\n `\n : nothing}\n\n <div class=\"fw-controls-row\">\n <div class=\"fw-controls-left\">\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=\"fw-btn-flush\"\n ?disabled=${disabled}\n aria-label=${state.isPlaying ? \"Pause\" : \"Play\"}\n @click=${() => this.pc.togglePlay()}\n >\n ${state.isPlaying ? pauseIcon(18) : playIcon(18)}\n </button>\n\n ${context.canSeek\n ? html`\n <button\n type=\"button\"\n class=\"fw-btn-flush hidden sm:flex\"\n ?disabled=${disabled}\n aria-label=\"Skip back 10 seconds\"\n @click=${() => this.pc.seekBy(-10)}\n >\n ${skipBackIcon(16)}\n </button>\n <button\n type=\"button\"\n class=\"fw-btn-flush hidden sm:flex\"\n ?disabled=${disabled}\n aria-label=\"Skip forward 10 seconds\"\n @click=${() => this.pc.seekBy(10)}\n >\n ${skipForwardIcon(16)}\n </button>\n `\n : nothing}\n </div>\n\n <div class=\"fw-control-group\">\n <fw-volume-control .pc=${this.pc}></fw-volume-control>\n </div>\n\n <div class=\"fw-control-group\">\n <span class=\"fw-time-display\">${timeDisplay}</span>\n </div>\n\n ${context.isLive\n ? html`\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n @click=${() => this.pc.jumpToLive()}\n ?disabled=${liveButtonDisabled}\n class=${classMap({\n \"fw-live-badge\": true,\n \"fw-live-badge--active\": liveButtonDisabled,\n \"fw-live-badge--behind\": !liveButtonDisabled,\n })}\n title=${!context.hasDvrWindow\n ? \"Live only\"\n : this._isNearLiveState\n ? \"At live edge\"\n : \"Jump to live\"}\n >\n LIVE\n ${!this._isNearLiveState && context.hasDvrWindow\n ? seekToLiveIcon(10)\n : nothing}\n </button>\n </div>\n `\n : nothing}\n </div>\n\n <div class=\"fw-controls-right\">\n ${this.showStatsButton\n ? html`\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-btn-flush\": true,\n \"fw-btn-flush--active\": this.isStatsOpen,\n })}\n aria-label=\"Toggle stats\"\n title=\"Stats\"\n @click=${() =>\n this.dispatchEvent(\n new CustomEvent(\"fw-stats-toggle\", {\n bubbles: true,\n composed: true,\n })\n )}\n >\n ${statsIcon(16)}\n </button>\n </div>\n `\n : nothing}\n ${this.devMode\n ? html`\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-btn-flush\": true,\n \"fw-btn-flush--active\": this.isDevPanelOpen,\n })}\n aria-label=${this.isDevPanelOpen\n ? \"Hide advanced settings sidebar\"\n : \"Show advanced settings sidebar\"}\n title=${this.isDevPanelOpen ? \"Hide advanced sidebar\" : \"Advanced sidebar\"}\n @click=${() =>\n this.dispatchEvent(\n new CustomEvent(\"fw-dev-panel-toggle\", {\n bubbles: true,\n composed: true,\n })\n )}\n >\n <span class=\"fw-dev-toggle-label\">ADV</span>\n </button>\n </div>\n `\n : nothing}\n\n <div class=\"fw-control-group fw-settings-anchor\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-btn-flush\": true,\n group: true,\n \"fw-btn-flush--active\": this._settingsOpen,\n })}\n aria-label=\"Settings\"\n title=\"Settings\"\n ?disabled=${disabled}\n @click=${() => {\n if (disabled) {\n return;\n }\n this._settingsOpen = !this._settingsOpen;\n }}\n >\n <span class=\"transition-transform group-hover:rotate-90\"\n >${settingsIcon(16)}</span\n >\n </button>\n\n <fw-settings-menu\n .pc=${this.pc}\n .open=${this._settingsOpen}\n .playbackMode=${this.playbackMode}\n .isContentLive=${this.isContentLive}\n @fw-close=${() => {\n this._settingsOpen = false;\n }}\n @fw-mode-change=${this._handleModeChange}\n ></fw-settings-menu>\n </div>\n\n <div class=\"fw-control-group\">\n <button\n type=\"button\"\n class=\"fw-btn-flush\"\n ?disabled=${disabled}\n aria-label=${state.isFullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n @click=${() => this.pc.toggleFullscreen()}\n >\n ${state.isFullscreen ? fullscreenExitIcon(16) : fullscreenIcon(16)}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fw-player-controls\": FwPlayerControls;\n }\n}\n"],"names":["FwPlayerControls","LitElement","isLiveContent","isMediaStreamSource","calculateSeekableRange","canSeekStream","calculateLiveThresholds","calculateIsNearLive","formatTimeDisplay","html","classMap","nothing","pauseIcon","playIcon","skipBackIcon","skipForwardIcon","seekToLiveIcon","statsIcon","settingsIcon","fullscreenExitIcon","fullscreenIcon","sharedStyles","utilityStyles","css","__decorate","property","state","customElement"],"mappings":";;;;;;;;;;;AA8CaA,wBAAgB,GAAtB,MAAM,gBAAiB,SAAQC,cAAU,CAAA;AAAzC,IAAA,WAAA,GAAA;;QAEuB,IAAA,CAAA,YAAY,GAAiB,MAAM;QACJ,IAAA,CAAA,aAAa,GAAG,KAAK;QAC5B,IAAA,CAAA,OAAO,GAAG,KAAK;QACN,IAAA,CAAA,cAAc,GAAG,KAAK;QACtB,IAAA,CAAA,eAAe,GAAG,KAAK;QAC3B,IAAA,CAAA,WAAW,GAAG,KAAK;QAE3D,IAAA,CAAA,aAAa,GAAG,KAAK;QACrB,IAAA,CAAA,gBAAgB,GAAG,IAAI;QACvB,IAAA,CAAA,SAAS,GAAsB,IAAI;QAE5C,IAAA,CAAA,WAAW,GAA4B,IAAI;QAC3C,IAAA,CAAA,iBAAiB,GAAwB,IAAI;AAiG7C,QAAA,IAAA,CAAA,cAAc,GAAG,CAAC,KAAiB,KAAU;AACnD,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE;YACjC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAI;AACzC,gBAAA,IAAI,EAAE,KAAK,YAAY,WAAW,CAAC,EAAE;AACnC,oBAAA,OAAO,KAAK;gBACd;gBACA,QACE,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC;oBAC9C,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC;AAEhD,YAAA,CAAC,CAAC;YAEF,IAAI,CAAC,cAAc,EAAE;AACnB,gBAAA,IAAI,CAAC,aAAa,GAAG,KAAK;YAC5B;AACF,QAAA,CAAC;IAyXH;IAndE,iBAAiB,GAAA;QACf,KAAK,CAAC,iBAAiB,EAAE;IAC3B;IAEA,oBAAoB,GAAA;QAClB,KAAK,CAAC,oBAAoB,EAAE;QAC5B,IAAI,CAAC,kBAAkB,EAAE;QACzB,IAAI,CAAC,0BAA0B,EAAE;IACnC;AAEU,IAAA,OAAO,CAAC,OAA6B,EAAA;QAC7C,IAAI,CAAC,gBAAgB,EAAE;QACvB,IAAI,CAAC,uBAAuB,EAAE;AAE9B,QAAA,IAAI,OAAO,CAAC,GAAG,CAAC,eAAyC,CAAC,EAAE;AAC1D,YAAA,IAAI,IAAI,CAAC,aAAa,EAAE;gBACtB,IAAI,CAAC,0BAA0B,EAAE;YACnC;iBAAO;gBACL,IAAI,CAAC,0BAA0B,EAAE;YACnC;QACF;IACF;IAEQ,gBAAgB,GAAA;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,IAAI,IAAI;AAC7C,QAAA,IAAI,KAAK,KAAK,IAAI,CAAC,WAAW,EAAE;YAC9B;QACF;QAEA,IAAI,CAAC,kBAAkB,EAAE;AACzB,QAAA,IAAI,CAAC,WAAW,GAAG,KAAK;QAExB,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI;YACrB;QACF;QAEA,MAAM,cAAc,GAAG,MAAK;AAC1B,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,iBAAiB,EAAE,IAAI,KAAK,CAAC,QAAQ;AAChE,QAAA,CAAC;AAED,QAAA,cAAc,EAAE;AAChB,QAAA,KAAK,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,CAAC;AAClD,QAAA,KAAK,CAAC,gBAAgB,CAAC,YAAY,EAAE,cAAc,CAAC;AACpD,QAAA,IAAI,CAAC,iBAAiB,GAAG,cAAc;IACzC;IAEQ,kBAAkB,GAAA;AACxB,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACrB;QACF;AAEA,QAAA,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB;QAC7C,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,UAAU,EAAE,cAAc,CAAC;YAChE,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,YAAY,EAAE,cAAc,CAAC;QACpE;AAEA,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;AACvB,QAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;IAC/B;IAEQ,0BAA0B,GAAA;AAChC,QAAA,MAAM,CAAC,UAAU,CAAC,MAAK;AACrB,YAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;gBACvB;YACF;YACA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC;QACvD,CAAC,EAAE,CAAC,CAAC;IACP;IAEQ,0BAA0B,GAAA;QAChC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC;IAC1D;AAmBQ,IAAA,qBAAqB,CAC3B,MAA8D,EAAA;QAE9D,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,OAAO,SAAS;QAClB;QAEA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;AACzC,QAAA,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AAC5B,YAAA,OAAO,SAAS;QAClB;QAEA,MAAM,aAAa,GAAG;aACnB,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO;aAC5B,MAAM,CAAC,CAAC,KAAK,KAAsB,OAAO,KAAK,KAAK,QAAQ,CAAC;QAChE,MAAM,YAAY,GAAG;aAClB,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM;aAC3B,MAAM,CAAC,CAAC,KAAK,KAAsB,OAAO,KAAK,KAAK,QAAQ,CAAC;AAEhE,QAAA,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE;AAC3D,YAAA,OAAO,SAAS;QAClB;QAEA,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;AACxC,QAAA,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO;AAE/B,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE;AAC3C,YAAA,OAAO,SAAS;QAClB;AAEA,QAAA,OAAO,MAAM;IACf;IAEQ,kBAAkB,GAAA;AACxB,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE;AAC1C,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,iBAAiB,EAAE,IAAI;AAChD,QAAA,MAAM,cAAc,GAAG,KAAK,CAAC,WAAW,EAAE,UAAwC;AAElF,QAAA,MAAM,MAAM,GAAGC,wBAAa,CAAC,IAAI,CAAC,aAAa,EAAE,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC;AAChF,QAAA,MAAM,cAAc,GAClB,cAAc,EAAE,IAAI,EAAE,aAAa;YACnC,IAAI,CAAC,qBAAqB,CACxB,cAAc,EAAE,IAAI,EAAE,MAET,CACd;QAEH,MAAM,QAAQ,GAAGC,8BAAmB,CAAC,KAAK,CAAC,YAAY,CAAC;AAExD,QAAA,MAAM,mBAAmB,GACvBA,8BAAmB,CAAC,KAAK,CAAC,YAAY,CAAC;AACvC,YAAA,cAAc,KAAK,SAAS;AAC5B,YAAA,cAAc,GAAG,CAAC;AAClB,YAAA,UAAU,KAAK,MAAM;YACrB,UAAU,KAAK,QAAQ;QAEzB,MAAM,eAAe,GAAGC,iCAAsB,CAAC;YAC7C,MAAM;YACN,KAAK,EAAE,KAAK,CAAC,YAAY;YACzB,cAAc;YACd,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,mBAAmB;AACpB,SAAA,CAAC;QAEF,MAAM,uBAAuB,GAAG,IAAI,CAAC,EAAE,CAAC,gBAAgB,EAAE;QAC1D,MAAM,kBAAkB,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;AAEhD,QAAA,MAAM,kBAAkB,GACtB,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC;AACxC,YAAA,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC;AACnC,YAAA,kBAAkB,IAAI,uBAAuB;aAC5C,kBAAkB,GAAG,CAAC,IAAI,uBAAuB,GAAG,CAAC,CAAC;QAEzD,MAAM,aAAa,GAAG;AACpB,cAAE;AACF,cAAE,eAAe,CAAC,aAAa;AACjC,QAAA,MAAM,QAAQ,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,eAAe,CAAC,QAAQ;QAEnF,MAAM,YAAY,GAChB,MAAM;AACN,YAAA,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACzB,YAAA,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC9B,QAAQ,GAAG,aAAa;AAE1B,QAAA,MAAM,WAAW,GACf,UAAU,EAAE,aAAa,IAAI;AAC7B,YAAAC,wBAAa,CAAC;gBACZ,KAAK,EAAE,KAAK,CAAC,YAAY;gBACzB,MAAM;gBACN,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,cAAc;AACf,aAAA,CAAC;QAEJ,MAAM,cAAc,GAAGC,kCAAuB,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,CAAC;QAEpF,OAAO;YACL,cAAc;YACd,MAAM;YACN,UAAU;YACV,aAAa;YACb,QAAQ;YACR,YAAY;YACZ,OAAO,EAAE,WAAW,KAAK,CAAC,MAAM,IAAI,YAAY,CAAC;AACjD,YAAA,eAAe,EAAE,MAAM;YACvB,cAAc;SACf;IACH;IAEQ,uBAAuB,GAAA;AAC7B,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE;AAEzC,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;AACnB,YAAA,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE;AAC1B,gBAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;YAC9B;YACA;QACF;QAEA,MAAM,IAAI,GAAGC,8BAAmB,CAC9B,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EACrB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,cAAc,EACtB,IAAI,CAAC,gBAAgB,CACtB;AAED,QAAA,IAAI,IAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE;AAClC,YAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;QAC9B;IACF;AAEQ,IAAA,iBAAiB,CACvB,KAAgE,EAAA;AAEhE,QAAA,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,MAAM;AAC7B,QAAA,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,gBAAgB,EAAE;YAChC,MAAM,EAAE,EAAE,IAAI,EAAE;AAChB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC,CACH;IACH;IAEU,MAAM,GAAA;AACd,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;AACvB,QAAA,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,YAAY;AACpC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE;AACzC,QAAA,MAAM,kBAAkB,GACtB,KAAK,CAAC,kBAAkB;AACxB,YAAA,KAAK,CAAC,QAAQ;YACd,CAAC,KAAK,CAAC,kBAAkB;AACzB,YAAA,KAAK,CAAC,oBAAoB;YAC1B,CAAC,CAAC,KAAK,CAAC,KAAK;YACb,IAAI,CAAC,aAAa;QAEpB,MAAM,WAAW,GAAGC,4BAAiB,CAAC;YACpC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,aAAa,EAAE,OAAO,CAAC,aAAa;AACpC,YAAA,UAAU,EAAE,OAAO,CAAC,cAAc,EAAE,UAAU;AAC/C,SAAA,CAAC;QAEF,MAAM,kBAAkB,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,gBAAgB;AAEzE,QAAA,OAAOC,QAAI,CAAA;;AAEC,cAAA,EAAAC,oBAAQ,CAAC;AACf,YAAA,mBAAmB,EAAE,IAAI;AACzB,YAAA,qBAAqB,EAAE,IAAI;AAC3B,YAAA,8BAA8B,EAAE,kBAAkB;YAClD,6BAA6B,EAAE,CAAC,kBAAkB;SACnD,CAAC;;AAEmC,2CAAA,EAAA,CAAC,KAAY,KAAK,KAAK,CAAC,eAAe,EAAE,CAAA;AAC1E,UAAA,EAAA,OAAO,CAAC;cACND,QAAI,CAAA;;;AAGiB,iCAAA,EAAA,KAAK,CAAC,WAAW;AACpB,8BAAA,EAAA,KAAK,CAAC,QAAQ;AACd,8BAAA,EAAA,IAAI,CAAC,SAAS;gCACd,QAAQ;AACV,4BAAA,EAAA,OAAO,CAAC,MAAM;AACP,mCAAA,EAAA,OAAO,CAAC,aAAa;AAC1B,8BAAA,EAAA,OAAO,CAAC,QAAQ;AACT,qCAAA,EAAA,OAAO,CAAC,eAAe;AAC/B,6BAAA,EAAA,CAAC,KAAoC,KAC9C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;;;AAGtC,cAAA;AACH,cAAEE,WAAO;;;;;;;;8BAQS,QAAQ;+BACP,KAAK,CAAC,SAAS,GAAG,OAAO,GAAG,MAAM;AACtC,yBAAA,EAAA,MAAM,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE;;AAEjC,kBAAA,EAAA,KAAK,CAAC,SAAS,GAAGC,eAAS,CAAC,EAAE,CAAC,GAAGC,cAAQ,CAAC,EAAE,CAAC;;;AAGhD,gBAAA,EAAA,OAAO,CAAC;cACNJ,QAAI,CAAA;;;;oCAIY,QAAQ;;iCAEX,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;;0BAEhCK,kBAAY,CAAC,EAAE,CAAC;;;;;oCAKN,QAAQ;;iCAEX,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;;0BAE/BC,qBAAe,CAAC,EAAE,CAAC;;AAExB,oBAAA;AACH,cAAEJ,WAAO;;;;AAIc,uCAAA,EAAA,IAAI,CAAC,EAAE,CAAA;;;;gDAIA,WAAW,CAAA;;;AAG3C,cAAA,EAAA,OAAO,CAAC;cACNF,QAAI,CAAA;;;;AAIW,+BAAA,EAAA,MAAM,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE;oCACvB,kBAAkB;AACtB,8BAAA,EAAAC,oBAAQ,CAAC;AACf,gBAAA,eAAe,EAAE,IAAI;AACrB,gBAAA,uBAAuB,EAAE,kBAAkB;gBAC3C,uBAAuB,EAAE,CAAC,kBAAkB;aAC7C,CAAC;gCACM,CAAC,OAAO,CAAC;AACf,kBAAE;kBACA,IAAI,CAAC;AACL,sBAAE;AACF,sBAAE,cAAc;;;AAGlB,wBAAA,EAAA,CAAC,IAAI,CAAC,gBAAgB,IAAI,OAAO,CAAC;AAClC,kBAAEM,oBAAc,CAAC,EAAE;AACnB,kBAAEL,WAAO;;;AAGhB,kBAAA;AACH,cAAEA,WAAO;;;;AAIT,cAAA,EAAA,IAAI,CAAC;cACHF,QAAI,CAAA;;;;AAIU,8BAAA,EAAAC,oBAAQ,CAAC;AACf,gBAAA,cAAc,EAAE,IAAI;gBACpB,sBAAsB,EAAE,IAAI,CAAC,WAAW;aACzC,CAAC;;;iCAGO,MACP,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;AACjC,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,QAAQ,EAAE,IAAI;AACf,aAAA,CAAC,CACH;;0BAEDO,eAAS,CAAC,EAAE,CAAC;;;AAGpB,kBAAA;AACH,cAAEN,WAAO;AACT,cAAA,EAAA,IAAI,CAAC;cACHF,QAAI,CAAA;;;;AAIU,8BAAA,EAAAC,oBAAQ,CAAC;AACf,gBAAA,cAAc,EAAE,IAAI;gBACpB,sBAAsB,EAAE,IAAI,CAAC,cAAc;aAC5C,CAAC;AACW,mCAAA,EAAA,IAAI,CAAC;AAChB,kBAAE;AACF,kBAAE,gCAAgC;gCAC5B,IAAI,CAAC,cAAc,GAAG,uBAAuB,GAAG,kBAAkB;iCACjE,MACP,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,qBAAqB,EAAE;AACrC,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,QAAQ,EAAE,IAAI;AACf,aAAA,CAAC,CACH;;;;;AAKR,kBAAA;AACH,cAAEC,WAAO;;;;;AAKC,wBAAA,EAAAD,oBAAQ,CAAC;AACf,YAAA,cAAc,EAAE,IAAI;AACpB,YAAA,KAAK,EAAE,IAAI;YACX,sBAAsB,EAAE,IAAI,CAAC,aAAa;SAC3C,CAAC;;;8BAGU,QAAQ;AACX,yBAAA,EAAA,MAAK;YACZ,IAAI,QAAQ,EAAE;gBACZ;YACF;AACA,YAAA,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,CAAC,aAAa;QAC1C,CAAC;;;uBAGIQ,kBAAY,CAAC,EAAE,CAAC,CAAA;;;;;AAKf,sBAAA,EAAA,IAAI,CAAC,EAAE;AACL,wBAAA,EAAA,IAAI,CAAC,aAAa;AACV,gCAAA,EAAA,IAAI,CAAC,YAAY;AAChB,iCAAA,EAAA,IAAI,CAAC,aAAa;AACvB,4BAAA,EAAA,MAAK;AACf,YAAA,IAAI,CAAC,aAAa,GAAG,KAAK;QAC5B,CAAC;AACiB,kCAAA,EAAA,IAAI,CAAC,iBAAiB;;;;;;;;8BAQ5B,QAAQ;+BACP,KAAK,CAAC,YAAY,GAAG,iBAAiB,GAAG,YAAY;AACzD,yBAAA,EAAA,MAAM,IAAI,CAAC,EAAE,CAAC,gBAAgB,EAAE;;AAEvC,kBAAA,EAAA,KAAK,CAAC,YAAY,GAAGC,wBAAkB,CAAC,EAAE,CAAC,GAAGC,oBAAc,CAAC,EAAE,CAAC;;;;;;;KAO/E;IACH;;AAteOpB,wBAAA,CAAA,MAAM,GAAG;IACdqB,yBAAY;IACZC,2BAAa;AACb,IAAAC,OAAG,CAAA;;;;;;;;;;;;;;AAcF,IAAA,CAAA;AACF,CAlBY;AAfmBC,oBAAA,CAAA;AAA/B,IAAAC,sBAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE;AAA4B,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,IAAA,EAAA,MAAA,CAAA;AAC9BwB,oBAAA,CAAA;AAA3B,IAAAC,sBAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;AAAsC,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,cAAA,EAAA,MAAA,CAAA;AACLwB,oBAAA,CAAA;IAA1DC,sBAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE;AAAwB,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,eAAA,EAAA,MAAA,CAAA;AAC7BwB,oBAAA,CAAA;IAAnDC,sBAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE;AAAkB,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,SAAA,EAAA,MAAA,CAAA;AACPwB,oBAAA,CAAA;IAA5DC,sBAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE;AAAyB,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,gBAAA,EAAA,MAAA,CAAA;AACvBwB,oBAAA,CAAA;IAA5DC,sBAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE;AAA0B,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,iBAAA,EAAA,MAAA,CAAA;AAC5BwB,oBAAA,CAAA;IAAxDC,sBAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE;AAAsB,CAAA,EAAAzB,wBAAA,CAAA,SAAA,EAAA,aAAA,EAAA,MAAA,CAAA;AAE5DwB,oBAAA,CAAA;AAAhB,IAAAE,mBAAK;AAAiC,CAAA,EAAA1B,wBAAA,CAAA,SAAA,EAAA,eAAA,EAAA,MAAA,CAAA;AACtBwB,oBAAA,CAAA;AAAhB,IAAAE,mBAAK;AAAmC,CAAA,EAAA1B,wBAAA,CAAA,SAAA,EAAA,kBAAA,EAAA,MAAA,CAAA;AACxBwB,oBAAA,CAAA;AAAhB,IAAAE,mBAAK;AAA+C,CAAA,EAAA1B,wBAAA,CAAA,SAAA,EAAA,WAAA,EAAA,MAAA,CAAA;AAX1CA,wBAAgB,GAAAwB,oBAAA,CAAA;IAD5BG,2BAAa,CAAC,oBAAoB;AACtB,CAAA,EAAA3B,wBAAgB,CAuf5B;;"}
@@ -16,7 +16,11 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
16
16
  this.contentId = "";
17
17
  this.autoplay = true;
18
18
  this.muted = true;
19
+ // React/Svelte use `stockControls` for native controls. Keep `controls` as a
20
+ // compatibility no-op so WC parity does not hide custom controls/seekbar.
19
21
  this.controls = false;
22
+ this.stockControls = false;
23
+ this.nativeControls = false;
20
24
  this.debug = false;
21
25
  this.devMode = false;
22
26
  this.playbackMode = "auto";
@@ -77,6 +81,27 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
77
81
  return (this.shadowRoot ?? this.renderRoot ?? null);
78
82
  };
79
83
  this._getContextMenuElement = () => this._getQueryRoot()?.querySelector('[data-context-menu="true"]') ?? null;
84
+ this._getContextMenuBounds = () => {
85
+ const root = this._getQueryRoot()?.querySelector('[part="root"]');
86
+ const rect = root?.getBoundingClientRect() ?? this.getBoundingClientRect();
87
+ const width = rect.width > 0 ? rect.width : window.innerWidth;
88
+ const height = rect.height > 0 ? rect.height : window.innerHeight;
89
+ return {
90
+ left: rect.left,
91
+ top: rect.top,
92
+ right: rect.left + width,
93
+ bottom: rect.top + height,
94
+ width,
95
+ height,
96
+ };
97
+ };
98
+ this._toLocalContextMenuPoint = (clientX, clientY) => {
99
+ const bounds = this._getContextMenuBounds();
100
+ return {
101
+ x: clientX - bounds.left,
102
+ y: clientY - bounds.top,
103
+ };
104
+ };
80
105
  this._getContextMenuItems = (level = this._contextMenuActiveLevel) => Array.from(this._getQueryRoot()?.querySelectorAll(`[data-context-menu-item="true"][data-context-menu-level="${level}"]:not([data-disabled="true"])`) ?? []);
81
106
  this._focusFirstContextMenuItem = (level = this._contextMenuActiveLevel) => {
82
107
  const [firstItem] = this._getContextMenuItems(level);
@@ -105,8 +130,9 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
105
130
  };
106
131
  this._clampContextMenuPosition = (x, y, width, height) => {
107
132
  const viewportPadding = 8;
108
- const maxX = Math.max(viewportPadding, window.innerWidth - width - viewportPadding);
109
- const maxY = Math.max(viewportPadding, window.innerHeight - height - viewportPadding);
133
+ const bounds = this._getContextMenuBounds();
134
+ const maxX = Math.max(viewportPadding, bounds.width - width - viewportPadding);
135
+ const maxY = Math.max(viewportPadding, bounds.height - height - viewportPadding);
110
136
  return {
111
137
  x: Math.max(viewportPadding, Math.min(x, maxX)),
112
138
  y: Math.max(viewportPadding, Math.min(y, maxY)),
@@ -132,14 +158,16 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
132
158
  const menu = this._getContextMenuElement();
133
159
  if (!menu)
134
160
  return;
161
+ const bounds = this._getContextMenuBounds();
135
162
  const rect = menu.getBoundingClientRect();
136
163
  const estimatedSubmenuWidth = 190;
137
164
  this._contextMenuSubmenuSide =
138
- rect.right + estimatedSubmenuWidth > window.innerWidth - 8 ? "left" : "right";
165
+ rect.right + estimatedSubmenuWidth > bounds.right - 8 ? "left" : "right";
139
166
  };
140
- this._openContextMenu = (x, y) => {
141
- const next = this._clampContextMenuPosition(x, y, 220, 200);
142
- this._contextMenuSide = this._resolveContextMenuSide(x, y, next.x, next.y);
167
+ this._openContextMenu = (clientX, clientY) => {
168
+ const local = this._toLocalContextMenuPoint(clientX, clientY);
169
+ const next = this._clampContextMenuPosition(local.x, local.y, 220, 200);
170
+ this._contextMenuSide = this._resolveContextMenuSide(local.x, local.y, next.x, next.y);
143
171
  this._contextMenuX = next.x;
144
172
  this._contextMenuY = next.y;
145
173
  this._contextMenuMounted = true;
@@ -279,7 +307,8 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
279
307
  changed.has("authToken") ||
280
308
  changed.has("autoplay") ||
281
309
  changed.has("muted") ||
282
- changed.has("controls") ||
310
+ changed.has("stockControls") ||
311
+ changed.has("nativeControls") ||
283
312
  changed.has("debug") ||
284
313
  changed.has("thumbnailUrl") ||
285
314
  changed.has("endpoints")) {
@@ -292,7 +321,7 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
292
321
  authToken: this.authToken,
293
322
  autoplay: this.autoplay,
294
323
  muted: this.muted,
295
- controls: this.controls,
324
+ controls: this.stockControls || this.nativeControls,
296
325
  poster: this.thumbnailUrl,
297
326
  debug: this.debug,
298
327
  });
@@ -356,7 +385,9 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
356
385
  return "Waiting for endpoint...";
357
386
  }
358
387
  get _useStockControls() {
359
- return this.controls || this.pc.s.currentPlayerInfo?.shortname === "mist-legacy";
388
+ return (this.stockControls ||
389
+ this.nativeControls ||
390
+ this.pc.s.currentPlayerInfo?.shortname === "mist-legacy");
360
391
  }
361
392
  // ---- Public API methods ----
362
393
  async play() {
@@ -636,9 +667,14 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
636
667
  .pc=${this.pc}
637
668
  .playbackMode=${this.playbackMode}
638
669
  .isContentLive=${s.isEffectivelyLive}
670
+ .devMode=${this.devMode}
671
+ .isDevPanelOpen=${this._isDevPanelOpen}
639
672
  .isStatsOpen=${this._isStatsOpen}
640
673
  @fw-stats-toggle=${() => {
641
674
  this._isStatsOpen = !this._isStatsOpen;
675
+ }}
676
+ @fw-dev-panel-toggle=${() => {
677
+ this._isDevPanelOpen = !this._isDevPanelOpen;
642
678
  }}
643
679
  @fw-mode-change=${(event) => {
644
680
  this.playbackMode = event.detail.mode;
@@ -677,7 +713,8 @@ exports.FwPlayer = class FwPlayer extends lit.LitElement {
677
713
  role="menu"
678
714
  aria-label="Player options"
679
715
  tabindex="-1"
680
- style="position: fixed; left: ${this._contextMenuX}px; top: ${this._contextMenuY}px;"
716
+ style="position: absolute; left: ${this._contextMenuX}px; top: ${this
717
+ ._contextMenuY}px;"
681
718
  @contextmenu=${(e) => e.preventDefault()}
682
719
  @keydown=${this._handleContextMenuKeyDown}
683
720
  >
@@ -910,6 +947,12 @@ tslib_es6.__decorate([
910
947
  tslib_es6.__decorate([
911
948
  decorators_js.property({ type: Boolean })
912
949
  ], exports.FwPlayer.prototype, "controls", void 0);
950
+ tslib_es6.__decorate([
951
+ decorators_js.property({ type: Boolean, attribute: "stock-controls" })
952
+ ], exports.FwPlayer.prototype, "stockControls", void 0);
953
+ tslib_es6.__decorate([
954
+ decorators_js.property({ type: Boolean, attribute: "native-controls" })
955
+ ], exports.FwPlayer.prototype, "nativeControls", void 0);
913
956
  tslib_es6.__decorate([
914
957
  decorators_js.property({ type: Boolean })
915
958
  ], exports.FwPlayer.prototype, "debug", void 0);