@mango-iiif/av 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +6 -0
  2. package/dist/annotations.d.ts +2 -2
  3. package/dist/annotations.js +3 -3
  4. package/dist/chapters.d.ts +2 -2
  5. package/dist/chapters.js +3 -3
  6. package/dist/{chunk-3LLEY7MA.js → chunk-3PPOT5YI.js} +2 -2
  7. package/dist/{chunk-YH6DS7J6.js → chunk-ARYOSUFI.js} +5 -4
  8. package/dist/chunk-ARYOSUFI.js.map +1 -0
  9. package/dist/{chunk-PZ6GV6ZJ.js → chunk-DF3YH3BM.js} +6 -6
  10. package/dist/{chunk-R4OUWZ3R.js → chunk-HOISQL65.js} +31 -11
  11. package/dist/chunk-HOISQL65.js.map +1 -0
  12. package/dist/{chunk-IFTSCRCU.js → chunk-IMQ4IJY6.js} +3 -3
  13. package/dist/{chunk-JXUHKKIX.js → chunk-QMUWPXP5.js} +2 -2
  14. package/dist/{chunk-GX7UU6V4.js → chunk-RJDPI4KL.js} +61 -21
  15. package/dist/chunk-RJDPI4KL.js.map +1 -0
  16. package/dist/{chunk-ML77YRJL.js → chunk-ZMC36FZ2.js} +92 -18
  17. package/dist/chunk-ZMC36FZ2.js.map +1 -0
  18. package/dist/chunk-ZRPA2VFR.js +218 -0
  19. package/dist/chunk-ZRPA2VFR.js.map +1 -0
  20. package/dist/{controller-DLy38ais.d.ts → controller-BLA8XNsG.d.ts} +10 -2
  21. package/dist/{controller-element-BOKM9v2S.d.ts → controller-element-BY0157ve.d.ts} +1 -1
  22. package/dist/core.d.ts +2 -2
  23. package/dist/core.js +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +9 -9
  26. package/dist/media.d.ts +1 -1
  27. package/dist/media.js +4 -4
  28. package/dist/player.d.ts +3 -2
  29. package/dist/player.js +5 -4
  30. package/dist/player.js.map +1 -1
  31. package/dist/transcript-element.d.ts +2 -2
  32. package/dist/transcript-element.js +4 -4
  33. package/docs/API.md +10 -0
  34. package/package.json +1 -1
  35. package/dist/chunk-GX7UU6V4.js.map +0 -1
  36. package/dist/chunk-ML77YRJL.js.map +0 -1
  37. package/dist/chunk-NTHMZZYJ.js +0 -38
  38. package/dist/chunk-NTHMZZYJ.js.map +0 -1
  39. package/dist/chunk-R4OUWZ3R.js.map +0 -1
  40. package/dist/chunk-YH6DS7J6.js.map +0 -1
  41. /package/dist/{chunk-3LLEY7MA.js.map → chunk-3PPOT5YI.js.map} +0 -0
  42. /package/dist/{chunk-PZ6GV6ZJ.js.map → chunk-DF3YH3BM.js.map} +0 -0
  43. /package/dist/{chunk-IFTSCRCU.js.map → chunk-IMQ4IJY6.js.map} +0 -0
  44. /package/dist/{chunk-JXUHKKIX.js.map → chunk-QMUWPXP5.js.map} +0 -0
@@ -1,19 +1,24 @@
1
1
  import {
2
- ensureMediaChrome
3
- } from "./chunk-NTHMZZYJ.js";
2
+ AudioArtVisualizer,
3
+ audioArtContentHtml,
4
+ audioArtTranscriptHtml,
5
+ ensureMediaChrome,
6
+ resolveAudioVisualizerMode,
7
+ syncAudioArtTranscript
8
+ } from "./chunk-ZRPA2VFR.js";
4
9
  import {
5
10
  playFromInteraction
6
11
  } from "./chunk-CDLYT3KB.js";
7
12
  import {
8
13
  loadTranscript
9
- } from "./chunk-3LLEY7MA.js";
14
+ } from "./chunk-3PPOT5YI.js";
10
15
  import {
11
16
  AVPlayerController,
12
17
  escapeHtml,
13
18
  formatString,
14
19
  formatTime,
15
20
  resolveStrings
16
- } from "./chunk-R4OUWZ3R.js";
21
+ } from "./chunk-HOISQL65.js";
17
22
 
18
23
  // src/components/mango-av-media.ts
19
24
  var HTMLElementBase = globalThis.HTMLElement ?? class {
@@ -65,15 +70,24 @@ var styles = `
65
70
  .header h2 { font-size: 1.15rem; margin: 0; }
66
71
  .header p { color: var(--mango-av-muted); margin: .35rem 0 0; }
67
72
  .layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(18rem, var(--mango-av-panel-width)); }
68
- .player-column { background: #08080a; min-width: 0; position: relative; }
73
+ .player-column { align-self: start; background: #08080a; min-width: 0; position: relative; }
69
74
  media-controller { display: block; width: 100%; }
70
75
  media-controller:not([audio]) { aspect-ratio: var(--mango-av-player-aspect-ratio); }
71
76
  video, hls-video { display: block; height: 100%; object-fit: contain; width: 100%; }
72
77
  audio { width: 100%; }
73
78
  media-control-bar { width: 100%; }
74
- .audio-art { align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(135deg, var(--mango-av-accent), #111); color: white; display: flex; justify-content: center; padding: 2rem; text-align: center; }
79
+ .audio-art { --mango-av-wave-edge-strength: 0%; --mango-av-wave-mid-strength: 0%; --mango-av-wave-rise: 8%; --mango-av-wave-strength: 0%; align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(#09080d, #111019); color: white; display: flex; justify-content: center; overflow: hidden; padding: 2rem; position: relative; text-align: center; }
80
+ .audio-art.visualizer--pulse { background: radial-gradient(ellipse 140% 72% at 50% calc(100% + var(--mango-av-wave-rise)), color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-strength), transparent) 0%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-mid-strength), transparent) 40%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-edge-strength), transparent) 62%, transparent 78%), linear-gradient(#09080d, #111019); }
81
+ .audio-art-text { display: contents; }
82
+ .audio-art-visualizer-canvas { height: 100%; inset: 0; pointer-events: none; position: absolute; width: 100%; }
75
83
  .audio-art img { height: 100%; inset: 0; object-fit: contain; position: absolute; width: 100%; }
76
- .audio-art span { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; }
84
+ .audio-art-title { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; position: relative; z-index: 1; }
85
+ .audio-art-transcript { height: 100%; inset: 0; overflow: hidden; position: absolute; z-index: 1; }
86
+ .audio-art-cue { align-items: center; display: flex; flex-direction: column; font-size: clamp(.7rem, 1.3vw, .875rem); font-weight: 550; gap: .15rem; left: 12%; line-height: 1.35; position: absolute; right: 12%; top: 50%; transition: opacity 360ms ease, transform 480ms cubic-bezier(.22, 1, .36, 1); }
87
+ .audio-art-transcript.seeking .audio-art-cue { transition: none; }
88
+ .audio-art-cue.active { font-size: clamp(.78rem, 1.5vw, 1rem); font-weight: 650; }
89
+ .audio-art-cue > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
90
+ .audio-art-speaker { font-size: .7em; letter-spacing: .04em; text-transform: uppercase; }
77
91
  .player-toolbar { align-items: center; background: #151518; color: #fff; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; padding: .55rem .7rem; }
78
92
  .player-toolbar button, .player-toolbar select { background: #29292e; border: 1px solid #44444a; border-radius: .35rem; color: white; padding: .4rem .55rem; }
79
93
  .player-toolbar .group { align-items: center; display: flex; gap: .45rem; }
@@ -89,7 +103,9 @@ var styles = `
89
103
  .empty { color: var(--mango-av-muted); font-style: italic; }
90
104
  .chapter-list, .annotation-list, .cue-list, .file-list { list-style: none; margin: 0; padding: 0; }
91
105
  .chapter-list .chapter-list { border-left: 1px solid var(--mango-av-border); margin-left: .8rem; padding-left: .55rem; }
106
+ .chapter { margin-block: .3rem; }
92
107
  .chapter button, .annotation button, .cue button { background: transparent; border: 0; border-radius: .35rem; color: inherit; display: grid; gap: .15rem; padding: .5rem; text-align: left; width: 100%; }
108
+ .chapter button { border-radius: 0; }
93
109
  .chapter button:hover, .annotation button:hover, .cue button:hover { background: var(--mango-av-surface); }
94
110
  .chapter button.active, .annotation button.active, .cue button.active { background: color-mix(in srgb, var(--mango-av-accent) 14%, transparent); box-shadow: inset 3px 0 var(--mango-av-accent); }
95
111
  .time { color: var(--mango-av-muted); font-size: .78rem; }
@@ -155,6 +171,7 @@ var MangoAVMediaElement = class extends HTMLElementBase {
155
171
  #loadingToken = 0;
156
172
  #resolvedSourceUrl;
157
173
  #sourceToken = 0;
174
+ #visualizer = new AudioArtVisualizer();
158
175
  constructor() {
159
176
  super();
160
177
  this.#root = this.attachShadow({ mode: "open" });
@@ -186,6 +203,8 @@ var MangoAVMediaElement = class extends HTMLElementBase {
186
203
  if (this.#activePanel === "annotations") this.#renderPanelOnly();
187
204
  } else if (type === "av-timeupdate") {
188
205
  this.#syncTimedContent();
206
+ } else if (type === "av-seek") {
207
+ this.#syncTimedContent(event.detail.to, true);
189
208
  }
190
209
  this.dispatchEvent(new CustomEvent(type, {
191
210
  detail: event.detail,
@@ -222,6 +241,7 @@ var MangoAVMediaElement = class extends HTMLElementBase {
222
241
  if (source !== void 0) void this.load(source);
223
242
  }
224
243
  disconnectedCallback() {
244
+ this.#visualizer.disconnect();
225
245
  this.player.destroy();
226
246
  }
227
247
  attributeChangedCallback(name, oldValue, newValue) {
@@ -263,6 +283,7 @@ var MangoAVMediaElement = class extends HTMLElementBase {
263
283
  }
264
284
  }
265
285
  #render() {
286
+ this.#visualizer.disconnect();
266
287
  const state = this.player.state;
267
288
  const manifest = state.manifest;
268
289
  const canvas = this.player.canvas;
@@ -278,7 +299,13 @@ var MangoAVMediaElement = class extends HTMLElementBase {
278
299
  </div>`;
279
300
  this.#bindInteractions();
280
301
  const media = this.#root.querySelector("video, audio, hls-video");
281
- if (media) this.player.attachMedia(media);
302
+ if (media) {
303
+ this.player.attachMedia(media);
304
+ const art = this.#root.querySelector(".audio-art.visualizer");
305
+ const visualizerMode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);
306
+ if (art && media instanceof HTMLAudioElement && visualizerMode) this.#visualizer.connect(media, art, visualizerMode);
307
+ }
308
+ this.#syncTimedContent();
282
309
  }
283
310
  #renderReady(manifest, canvas, kind) {
284
311
  const source = this.player.source;
@@ -293,13 +320,17 @@ var MangoAVMediaElement = class extends HTMLElementBase {
293
320
  panels.supplementalFiles ? ["files", ui.files] : void 0
294
321
  ].filter((value) => Boolean(value));
295
322
  const showPanel = this.#activePanel !== "none" && panelTabs.length > 0;
323
+ const showSourceSelector = controls.sourceSelector && canvas.sources.length > 1;
324
+ const showDownload = controls.download && canvas.rendering.length > 0;
325
+ const showPlayerToolbar = controls.navigation || controls.autoAdvance || showSourceSelector || showDownload;
326
+ const visualizerMode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);
296
327
  return `
297
328
  ${this.#resumeTime !== void 0 ? `<div class="resume">${escapeHtml(formatString(ui.resumePrompt, { time: formatTime(this.#resumeTime) }))} <button type="button" data-action="resume">${escapeHtml(ui.resume)}</button><button type="button" data-action="dismiss-resume">${escapeHtml(ui.startOver)}</button></div>` : ""}
298
329
  <div class="layout" ${showPanel ? "" : 'style="grid-template-columns: 1fr"'}>
299
330
  <section class="player-column" aria-label="${escapeHtml(ui.mediaPlayer)}">
300
331
  <slot name="before-player"></slot>
301
332
  <media-controller ${kind === "audio" ? "audio" : ""} ${this.#mediaChromeLanguage()}>
302
- ${kind === "audio" ? `<div class="audio-art"><span>${escapeHtml(canvas.label)}</span></div>` : ""}
333
+ ${kind === "audio" ? `<div class="audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ""}">${audioArtContentHtml(canvas.label, this.#transcriptCues, Boolean(this.player.config.audioArt.transcript), visualizerMode)}</div>` : ""}
303
334
  ${source ? this.#mediaHtml(canvas, source.kind) : `<div class="audio-art"><span>${escapeHtml(ui.noPlayableMedia)}</span></div>`}
304
335
  <slot name="player-overlay"></slot>
305
336
  <slot name="controls">
@@ -319,17 +350,17 @@ var MangoAVMediaElement = class extends HTMLElementBase {
319
350
  </slot>
320
351
  </media-controller>
321
352
  ${this.#searchMarkersHtml()}
322
- <div class="player-toolbar">
323
- <div class="group">
353
+ ${showPlayerToolbar ? `<div class="player-toolbar">
354
+ ${controls.navigation ? `<div class="group">
324
355
  <button type="button" data-action="previous" ${canvas.index === 0 ? "disabled" : ""} aria-label="${escapeHtml(ui.previousSection)}">\u2190 ${escapeHtml(ui.previous)}</button>
325
356
  <button type="button" data-action="next" ${canvas.index === manifest.canvases.length - 1 && !this.player.state.repeat ? "disabled" : ""} aria-label="${escapeHtml(ui.nextSection)}">${escapeHtml(ui.next)} \u2192</button>
326
- </div>
327
- <div class="group">
328
- ${controls.sourceSelector && canvas.sources.length > 1 ? `<label><span class="sr-only">${escapeHtml(ui.mediaSource)}</span><select data-action="source">${canvas.sources.map((item, index) => `<option value="${index}" ${index === this.player.state.sourceIndex ? "selected" : ""}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join("")}</select></label>` : ""}
329
- ${controls.download && canvas.rendering.length > 0 ? `<label><span class="sr-only">${escapeHtml(ui.downloadMediaFile)}</span><select data-action="download"><option value="">${escapeHtml(ui.download)}\u2026</option>${canvas.rendering.map((file) => `<option value="${escapeHtml(file.id)}" data-filename="${escapeHtml(file.filename ?? "")}">${escapeHtml(file.label)}</option>`).join("")}</select></label>` : ""}
330
- <label><input type="checkbox" data-action="auto-advance" ${this.player.state.autoAdvance ? "checked" : ""}> ${escapeHtml(ui.autoAdvance)}</label>
331
- </div>
332
- </div>
357
+ </div>` : ""}
358
+ ${showSourceSelector || showDownload || controls.autoAdvance ? `<div class="group">
359
+ ${showSourceSelector ? `<label><span class="sr-only">${escapeHtml(ui.mediaSource)}</span><select data-action="source">${canvas.sources.map((item, index) => `<option value="${index}" ${index === this.player.state.sourceIndex ? "selected" : ""}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join("")}</select></label>` : ""}
360
+ ${showDownload ? `<label><span class="sr-only">${escapeHtml(ui.downloadMediaFile)}</span><select data-action="download"><option value="">${escapeHtml(ui.download)}\u2026</option>${canvas.rendering.map((file) => `<option value="${escapeHtml(file.id)}" data-filename="${escapeHtml(file.filename ?? "")}">${escapeHtml(file.label)}</option>`).join("")}</select></label>` : ""}
361
+ ${controls.autoAdvance ? `<label><input type="checkbox" data-action="auto-advance" ${this.player.state.autoAdvance ? "checked" : ""}> ${escapeHtml(ui.autoAdvance)}</label>` : ""}
362
+ </div>` : ""}
363
+ </div>` : ""}
333
364
  <slot name="after-player"></slot>
334
365
  </section>
335
366
  ${showPanel ? `<aside class="panel">
@@ -495,6 +526,7 @@ var MangoAVMediaElement = class extends HTMLElementBase {
495
526
  const cues = await loadTranscript(resource, transcriptOptions);
496
527
  if (token !== this.#loadingToken) return;
497
528
  this.#transcriptCues = cues;
529
+ this.#renderAudioArt();
498
530
  if (this.#activePanel === "transcript") this.#renderPanelOnly();
499
531
  } catch (cause) {
500
532
  if (token !== this.#loadingToken) return;
@@ -518,8 +550,8 @@ var MangoAVMediaElement = class extends HTMLElementBase {
518
550
  this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
519
551
  }
520
552
  }
521
- #syncTimedContent() {
522
- const time = this.player.state.currentTime;
553
+ #syncTimedContent(time = this.player.state.currentTime, instant = false) {
554
+ syncAudioArtTranscript(this.#root, time, instant);
523
555
  const rows = this.#root.querySelectorAll("[data-start][data-end]");
524
556
  let active;
525
557
  rows.forEach((row) => {
@@ -533,6 +565,14 @@ var MangoAVMediaElement = class extends HTMLElementBase {
533
565
  active.scrollIntoView({ block: "nearest", behavior: "smooth" });
534
566
  }
535
567
  }
568
+ #renderAudioArt() {
569
+ const art = this.#root.querySelector(".audio-art");
570
+ const canvas = this.player.canvas;
571
+ if (!art || !canvas || this.player.source?.kind !== "audio") return;
572
+ const text = art.querySelector(".audio-art-text");
573
+ if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, this.#transcriptCues, Boolean(this.player.config.audioArt.transcript));
574
+ syncAudioArtTranscript(art, this.player.state.currentTime);
575
+ }
536
576
  };
537
577
  var defineMangoAVMedia = (tagName = "mango-av-media") => {
538
578
  if (typeof customElements === "undefined") return MangoAVMediaElement;
@@ -544,4 +584,4 @@ export {
544
584
  MangoAVMediaElement,
545
585
  defineMangoAVMedia
546
586
  };
547
- //# sourceMappingURL=chunk-GX7UU6V4.js.map
587
+ //# sourceMappingURL=chunk-RJDPI4KL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/mango-av-media.ts"],"sourcesContent":["import { AVPlayerController } from '../controller';\nimport { loadTranscript } from '../transcript';\nimport { ensureMediaChrome } from './media-chrome';\nimport { playFromInteraction } from './playback';\nimport { AudioArtVisualizer, audioArtContentHtml, audioArtTranscriptHtml, resolveAudioVisualizerMode, syncAudioArtTranscript } from './audio-art';\nimport type {\n AVAnnotation,\n AVCanvas,\n AVManifest,\n AVPlayerConfig,\n AVPlayerEventMap,\n AVStrings,\n Chapter,\n JsonObject,\n MetadataEntry,\n TranscriptCue,\n} from '../types';\nimport { escapeHtml, formatString, formatTime, resolveStrings } from '../utils';\n\nconst HTMLElementBase = (globalThis.HTMLElement ?? class {}) as typeof HTMLElement;\n\nlet hlsElementRegistration: Promise<unknown> | undefined;\nconst ensureHlsElement = (): Promise<unknown> => {\n hlsElementRegistration ??= import('hls-video-element');\n return hlsElementRegistration;\n};\n\nconst eventTypes: Array<keyof AVPlayerEventMap> = [\n 'av-loadstart',\n 'av-ready',\n 'av-error',\n 'av-canvaschange',\n 'av-sourcechange',\n 'av-play',\n 'av-pause',\n 'av-timeupdate',\n 'av-seek',\n 'av-ended',\n 'av-annotationschange',\n 'av-configchange',\n 'av-statechange',\n];\n\nconst styles = `\n :host {\n --mango-av-accent: #5b3fd6;\n --mango-av-accent-contrast: #fff;\n --mango-av-background: #fff;\n --mango-av-surface: #f5f5f8;\n --mango-av-text: #18181b;\n --mango-av-muted: #62626a;\n --mango-av-border: #d7d7de;\n --mango-av-radius: .65rem;\n --mango-av-font: ui-sans-serif, system-ui, sans-serif;\n --mango-av-panel-width: 24rem;\n --mango-av-player-aspect-ratio: 16 / 9;\n --media-primary-color: var(--mango-av-accent-contrast);\n --media-secondary-color: var(--mango-av-accent);\n color: var(--mango-av-text);\n display: block;\n font-family: var(--mango-av-font);\n }\n *, *::before, *::after { box-sizing: border-box; }\n button, input, select { font: inherit; }\n button, select { cursor: pointer; }\n .shell { background: var(--mango-av-background); border: 1px solid var(--mango-av-border); border-radius: var(--mango-av-radius); overflow: clip; }\n .header { padding: 1rem 1.1rem; }\n .header h2 { font-size: 1.15rem; margin: 0; }\n .header p { color: var(--mango-av-muted); margin: .35rem 0 0; }\n .layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(18rem, var(--mango-av-panel-width)); }\n .player-column { align-self: start; background: #08080a; min-width: 0; position: relative; }\n media-controller { display: block; width: 100%; }\n media-controller:not([audio]) { aspect-ratio: var(--mango-av-player-aspect-ratio); }\n video, hls-video { display: block; height: 100%; object-fit: contain; width: 100%; }\n audio { width: 100%; }\n media-control-bar { width: 100%; }\n .audio-art { --mango-av-wave-edge-strength: 0%; --mango-av-wave-mid-strength: 0%; --mango-av-wave-rise: 8%; --mango-av-wave-strength: 0%; align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(#09080d, #111019); color: white; display: flex; justify-content: center; overflow: hidden; padding: 2rem; position: relative; text-align: center; }\n .audio-art.visualizer--pulse { background: radial-gradient(ellipse 140% 72% at 50% calc(100% + var(--mango-av-wave-rise)), color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-strength), transparent) 0%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-mid-strength), transparent) 40%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-edge-strength), transparent) 62%, transparent 78%), linear-gradient(#09080d, #111019); }\n .audio-art-text { display: contents; }\n .audio-art-visualizer-canvas { height: 100%; inset: 0; pointer-events: none; position: absolute; width: 100%; }\n .audio-art img { height: 100%; inset: 0; object-fit: contain; position: absolute; width: 100%; }\n .audio-art-title { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; position: relative; z-index: 1; }\n .audio-art-transcript { height: 100%; inset: 0; overflow: hidden; position: absolute; z-index: 1; }\n .audio-art-cue { align-items: center; display: flex; flex-direction: column; font-size: clamp(.7rem, 1.3vw, .875rem); font-weight: 550; gap: .15rem; left: 12%; line-height: 1.35; position: absolute; right: 12%; top: 50%; transition: opacity 360ms ease, transform 480ms cubic-bezier(.22, 1, .36, 1); }\n .audio-art-transcript.seeking .audio-art-cue { transition: none; }\n .audio-art-cue.active { font-size: clamp(.78rem, 1.5vw, 1rem); font-weight: 650; }\n .audio-art-cue > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }\n .audio-art-speaker { font-size: .7em; letter-spacing: .04em; text-transform: uppercase; }\n .player-toolbar { align-items: center; background: #151518; color: #fff; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; padding: .55rem .7rem; }\n .player-toolbar button, .player-toolbar select { background: #29292e; border: 1px solid #44444a; border-radius: .35rem; color: white; padding: .4rem .55rem; }\n .player-toolbar .group { align-items: center; display: flex; gap: .45rem; }\n .search-markers { background: #242429; height: .45rem; position: relative; }\n .search-marker { background: #ffe24a; border: 0; border-radius: 50%; height: .7rem; padding: 0; position: absolute; top: 50%; transform: translate(-50%, -50%); width: .7rem; }\n .resume { align-items: center; background: #fff8d7; color: #312900; display: flex; gap: .5rem; justify-content: center; padding: .6rem; }\n .resume button { background: var(--mango-av-accent); border: 0; border-radius: .35rem; color: var(--mango-av-accent-contrast); padding: .35rem .65rem; }\n .panel { border-left: 1px solid var(--mango-av-border); display: flex; flex-direction: column; min-height: 22rem; min-width: 0; }\n .tabs { background: var(--mango-av-surface); display: flex; gap: .1rem; overflow-x: auto; padding: .35rem; }\n .tab { background: transparent; border: 0; border-bottom: 3px solid transparent; color: var(--mango-av-muted); padding: .55rem .6rem; white-space: nowrap; }\n .tab[aria-selected=true] { border-color: var(--mango-av-accent); color: var(--mango-av-text); font-weight: 650; }\n .panel-content { flex: 1; max-height: 34rem; overflow: auto; padding: .85rem; }\n .empty { color: var(--mango-av-muted); font-style: italic; }\n .chapter-list, .annotation-list, .cue-list, .file-list { list-style: none; margin: 0; padding: 0; }\n .chapter-list .chapter-list { border-left: 1px solid var(--mango-av-border); margin-left: .8rem; padding-left: .55rem; }\n .chapter { margin-block: .3rem; }\n .chapter button, .annotation button, .cue button { background: transparent; border: 0; border-radius: .35rem; color: inherit; display: grid; gap: .15rem; padding: .5rem; text-align: left; width: 100%; }\n .chapter button { border-radius: 0; }\n .chapter button:hover, .annotation button:hover, .cue button:hover { background: var(--mango-av-surface); }\n .chapter button.active, .annotation button.active, .cue button.active { background: color-mix(in srgb, var(--mango-av-accent) 14%, transparent); box-shadow: inset 3px 0 var(--mango-av-accent); }\n .time { color: var(--mango-av-muted); font-size: .78rem; }\n .panel-tools { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .75rem; }\n .panel-tools input, .panel-tools select { border: 1px solid var(--mango-av-border); border-radius: .35rem; min-width: 0; padding: .45rem; }\n .panel-tools input { flex: 1; }\n .panel-tools a { align-content: center; color: var(--mango-av-accent); }\n .cue mark { background: #ffe770; }\n .speaker { font-weight: 700; }\n .tags { display: flex; flex-wrap: wrap; gap: .25rem; }\n .tag { background: var(--mango-av-surface); border-radius: 999px; font-size: .75rem; padding: .15rem .45rem; }\n dl { margin: 0; }\n dt { font-weight: 700; margin-top: .75rem; }\n dd { color: var(--mango-av-muted); margin: .2rem 0 0; }\n .file-list li { border-bottom: 1px solid var(--mango-av-border); padding: .55rem 0; }\n a { color: var(--mango-av-accent); }\n .error { background: #fff0f0; color: #8a1717; padding: 1rem; }\n .sr-only { clip: rect(0,0,0,0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }\n @media (max-width: 760px) {\n .layout { grid-template-columns: 1fr; }\n .panel { border-left: 0; border-top: 1px solid var(--mango-av-border); }\n }\n`;\n\nconst metadataHtml = (entries: MetadataEntry[], ui: AVStrings): string => {\n if (entries.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noMetadata)}</p>`;\n return `<dl>${entries.map((entry) => `\n <dt>${escapeHtml(entry.label)}</dt>\n <dd>${entry.values.map((value) => escapeHtml(value.value)).join('<br>')}</dd>\n `).join('')}</dl>`;\n};\n\nconst chapterHtml = (chapters: Chapter[], canvasId: string, ui: AVStrings, level = 1): string => {\n if (chapters.length === 0 && level === 1) return `<p class=\"empty\">${escapeHtml(ui.noChapters)}</p>`;\n return `<ul class=\"chapter-list\">${chapters.map((chapter) => {\n const active = chapter.canvasId === canvasId;\n const target = chapter.canvasId ? `data-canvas=\"${escapeHtml(chapter.canvasId)}\"` : '';\n const time = chapter.time ? `data-time=\"${chapter.time.start}\"` : '';\n return `<li class=\"chapter\">\n <button type=\"button\" class=\"${active ? 'active' : ''}\" ${target} ${time} ${chapter.canvasId ? '' : 'disabled'}>\n <span>${escapeHtml(chapter.label)}</span>\n ${chapter.time ? `<span class=\"time\">${formatTime(chapter.time.start)}</span>` : ''}\n </button>\n ${chapterHtml(chapter.children, canvasId, ui, level + 1)}\n </li>`;\n }).join('')}</ul>`;\n};\n\nconst highlight = (text: string, query: string): string => {\n const escaped = escapeHtml(text);\n if (!query) return escaped;\n const safeQuery = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(new RegExp(`(${safeQuery})`, 'gi'), '<mark>$1</mark>');\n};\n\nexport class MangoAVMediaElement extends HTMLElementBase {\n static observedAttributes = ['manifest-url', 'config'];\n\n readonly player: AVPlayerController;\n #root: ShadowRoot;\n #inputManifest: JsonObject | string | undefined;\n #config: AVPlayerConfig = {};\n #activePanel: NonNullable<AVPlayerConfig['activePanel']> = 'chapters';\n #transcriptCues: TranscriptCue[] = [];\n #transcriptIndex = 0;\n #transcriptQuery = '';\n #resumeTime: number | undefined;\n #loadingToken = 0;\n #resolvedSourceUrl: string | undefined;\n #sourceToken = 0;\n #visualizer = new AudioArtVisualizer();\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n this.player = new AVPlayerController();\n for (const type of eventTypes) {\n this.player.addEventListener(type, ((event: CustomEvent) => {\n if (type === 'av-ready') {\n if (this.player.source?.isHls) void ensureHlsElement();\n this.#resolvedSourceUrl = undefined;\n this.#resumeTime = this.player.getSavedPosition();\n this.#activePanel = this.player.config.activePanel;\n this.#render();\n void this.#resolveSource();\n void this.#loadActiveTranscript();\n } else if (type === 'av-canvaschange' || type === 'av-sourcechange') {\n if (this.player.source?.isHls) void ensureHlsElement();\n this.#resolvedSourceUrl = undefined;\n this.#transcriptCues = [];\n this.#transcriptIndex = 0;\n this.#render();\n void this.#resolveSource();\n void this.#loadActiveTranscript();\n } else if (type === 'av-configchange') {\n this.#activePanel = this.player.config.activePanel;\n void ensureMediaChrome(this.player.config.preferredLanguages[0]).then(() => this.#render());\n } else if (type === 'av-error') {\n this.#render();\n } else if (type === 'av-annotationschange') {\n if (this.#activePanel === 'annotations') this.#renderPanelOnly();\n } else if (type === 'av-timeupdate') {\n this.#syncTimedContent();\n } else if (type === 'av-seek') {\n this.#syncTimedContent((event.detail as AVPlayerEventMap['av-seek']).to, true);\n }\n this.dispatchEvent(new CustomEvent(type, {\n detail: event.detail,\n bubbles: true,\n composed: true,\n }));\n }) as EventListener);\n }\n }\n\n get manifest(): JsonObject | string | undefined {\n return this.#inputManifest;\n }\n\n get controller(): AVPlayerController {\n return this.player;\n }\n\n set manifest(value: JsonObject | string | undefined) {\n this.#inputManifest = value;\n if (this.isConnected && value !== undefined) void this.load(value);\n }\n\n get config(): AVPlayerConfig {\n return this.#config;\n }\n\n set config(value: AVPlayerConfig) {\n this.#config = value;\n this.player.configure(value);\n this.#activePanel = value.activePanel ?? this.player.config.activePanel;\n if (this.isConnected) this.#render();\n }\n\n connectedCallback(): void {\n this.#readConfigAttribute();\n void ensureMediaChrome(this.player.config.preferredLanguages[0]);\n this.#render();\n const source = this.#inputManifest ?? this.getAttribute('manifest-url') ?? undefined;\n if (source !== undefined) void this.load(source);\n }\n\n disconnectedCallback(): void {\n this.#visualizer.disconnect();\n this.player.destroy();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected || oldValue === newValue) return;\n if (name === 'manifest-url' && newValue) {\n this.#inputManifest = newValue;\n void this.load(newValue);\n }\n if (name === 'config') {\n this.#readConfigAttribute();\n this.#render();\n }\n }\n\n async load(input: JsonObject | string): Promise<AVManifest> {\n this.#inputManifest = input;\n return await this.player.load(input);\n }\n\n play(): Promise<void> {\n return this.player.play();\n }\n\n pause(): void {\n this.player.pause();\n }\n\n seekTo(time: number): void {\n this.player.seekTo(time);\n }\n\n selectCanvas(indexOrId: number | string, options?: { time?: number; autoplay?: boolean }): AVCanvas | undefined {\n return this.player.selectCanvas(indexOrId, options);\n }\n\n #readConfigAttribute(): void {\n const attribute = this.getAttribute('config');\n if (!attribute) return;\n try {\n this.#config = JSON.parse(attribute) as AVPlayerConfig;\n this.player.configure(this.#config);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #render(): void {\n this.#visualizer.disconnect();\n const state = this.player.state;\n const manifest = state.manifest;\n const canvas = this.player.canvas;\n const source = this.player.source;\n const ui = resolveStrings(this.player.config.strings);\n this.#root.innerHTML = `<style>${styles}</style><div class=\"shell\">\n <slot name=\"header\">\n ${manifest ? `<header class=\"header\"><h2>${escapeHtml(manifest.label)}</h2>${manifest.summary ? `<p>${escapeHtml(manifest.summary)}</p>` : ''}</header>` : ''}\n </slot>\n ${state.status === 'error' ? `<div class=\"error\" role=\"alert\">${escapeHtml(state.error?.message ?? ui.unableToLoadPlayer)}</div>` : ''}\n ${state.status === 'loading' ? `<p class=\"header\" role=\"status\">${escapeHtml(ui.loadingManifest)}</p>` : ''}\n ${manifest && canvas ? this.#renderReady(manifest, canvas, source?.kind ?? 'video') : ''}\n </div>`;\n this.#bindInteractions();\n const media = this.#root.querySelector('video, audio, hls-video') as HTMLMediaElement | null;\n if (media) {\n this.player.attachMedia(media);\n const art = this.#root.querySelector<HTMLElement>('.audio-art.visualizer');\n const visualizerMode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);\n if (art && media instanceof HTMLAudioElement && visualizerMode) this.#visualizer.connect(media, art, visualizerMode);\n }\n this.#syncTimedContent();\n }\n\n #renderReady(manifest: AVManifest, canvas: AVCanvas, kind: 'audio' | 'video'): string {\n const source = this.player.source;\n const panels = this.player.config.panels;\n const controls = this.player.config.controls;\n const ui = resolveStrings(this.player.config.strings);\n const panelTabs = [\n panels.chapters ? ['chapters', ui.chapters] : undefined,\n panels.transcript ? ['transcript', ui.transcript] : undefined,\n panels.annotations ? ['annotations', ui.annotations] : undefined,\n panels.metadata ? ['metadata', ui.metadata] : undefined,\n panels.supplementalFiles ? ['files', ui.files] : undefined,\n ].filter((value): value is [string, string] => Boolean(value));\n const showPanel = this.#activePanel !== 'none' && panelTabs.length > 0;\n const showSourceSelector = controls.sourceSelector && canvas.sources.length > 1;\n const showDownload = controls.download && canvas.rendering.length > 0;\n const showPlayerToolbar = controls.navigation || controls.autoAdvance || showSourceSelector || showDownload;\n const visualizerMode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);\n return `\n ${this.#resumeTime !== undefined ? `<div class=\"resume\">${escapeHtml(formatString(ui.resumePrompt, { time: formatTime(this.#resumeTime) }))} <button type=\"button\" data-action=\"resume\">${escapeHtml(ui.resume)}</button><button type=\"button\" data-action=\"dismiss-resume\">${escapeHtml(ui.startOver)}</button></div>` : ''}\n <div class=\"layout\" ${showPanel ? '' : 'style=\"grid-template-columns: 1fr\"'}>\n <section class=\"player-column\" aria-label=\"${escapeHtml(ui.mediaPlayer)}\">\n <slot name=\"before-player\"></slot>\n <media-controller ${kind === 'audio' ? 'audio' : ''} ${this.#mediaChromeLanguage()}>\n ${kind === 'audio' ? `<div class=\"audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ''}\">${audioArtContentHtml(canvas.label, this.#transcriptCues, Boolean(this.player.config.audioArt.transcript), visualizerMode)}</div>` : ''}\n ${source ? this.#mediaHtml(canvas, source.kind) : `<div class=\"audio-art\"><span>${escapeHtml(ui.noPlayableMedia)}</span></div>`}\n <slot name=\"player-overlay\"></slot>\n <slot name=\"controls\">\n <media-control-bar>\n <media-play-button></media-play-button>\n ${controls.seek ? `<media-seek-backward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-backward-button><media-seek-forward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-forward-button>` : ''}\n <media-time-display showduration></media-time-display>\n ${controls.seek ? '<media-time-range></media-time-range>' : ''}\n ${controls.volume ? '<media-mute-button></media-mute-button><media-volume-range></media-volume-range>' : ''}\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu-button></media-captions-menu-button>' : ''}\n ${controls.playbackRate ? '<media-playback-rate-menu-button></media-playback-rate-menu-button>' : ''}\n ${controls.pictureInPicture && kind === 'video' ? '<media-pip-button></media-pip-button>' : ''}\n ${controls.fullscreen && kind === 'video' ? '<media-fullscreen-button></media-fullscreen-button>' : ''}\n </media-control-bar>\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu hidden></media-captions-menu>' : ''}\n ${controls.playbackRate ? `<media-playback-rate-menu rates=\"${this.player.config.playbackRates.join(' ')}\" hidden></media-playback-rate-menu>` : ''}\n </slot>\n </media-controller>\n ${this.#searchMarkersHtml()}\n ${showPlayerToolbar ? `<div class=\"player-toolbar\">\n ${controls.navigation ? `<div class=\"group\">\n <button type=\"button\" data-action=\"previous\" ${canvas.index === 0 ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.previousSection)}\">← ${escapeHtml(ui.previous)}</button>\n <button type=\"button\" data-action=\"next\" ${canvas.index === manifest.canvases.length - 1 && !this.player.state.repeat ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.nextSection)}\">${escapeHtml(ui.next)} →</button>\n </div>` : ''}\n ${showSourceSelector || showDownload || controls.autoAdvance ? `<div class=\"group\">\n ${showSourceSelector ? `<label><span class=\"sr-only\">${escapeHtml(ui.mediaSource)}</span><select data-action=\"source\">${canvas.sources.map((item, index) => `<option value=\"${index}\" ${index === this.player.state.sourceIndex ? 'selected' : ''}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join('')}</select></label>` : ''}\n ${showDownload ? `<label><span class=\"sr-only\">${escapeHtml(ui.downloadMediaFile)}</span><select data-action=\"download\"><option value=\"\">${escapeHtml(ui.download)}…</option>${canvas.rendering.map((file) => `<option value=\"${escapeHtml(file.id)}\" data-filename=\"${escapeHtml(file.filename ?? '')}\">${escapeHtml(file.label)}</option>`).join('')}</select></label>` : ''}\n ${controls.autoAdvance ? `<label><input type=\"checkbox\" data-action=\"auto-advance\" ${this.player.state.autoAdvance ? 'checked' : ''}> ${escapeHtml(ui.autoAdvance)}</label>` : ''}\n </div>` : ''}\n </div>` : ''}\n <slot name=\"after-player\"></slot>\n </section>\n ${showPanel ? `<aside class=\"panel\">\n <nav class=\"tabs\" role=\"tablist\" aria-label=\"${escapeHtml(ui.mediaInformation)}\">${panelTabs.map(([id, tabLabel]) => `<button class=\"tab\" type=\"button\" role=\"tab\" data-panel=\"${id}\" aria-selected=\"${this.#activePanel === id}\">${escapeHtml(tabLabel)}</button>`).join('')}</nav>\n <div class=\"panel-content\" role=\"tabpanel\">${this.#panelHtml(manifest, canvas)}</div>\n </aside>` : ''}\n </div>`;\n }\n\n #mediaHtml(canvas: AVCanvas, kind: 'audio' | 'video'): string {\n const source = this.player.source;\n if (!source) return '';\n const tag = source.isHls ? 'hls-video' : kind;\n const sourceUrl = this.#resolvedSourceUrl ?? (this.player.config.resolveRequest ? '' : source.src);\n const attributes = [\n 'slot=\"media\"',\n sourceUrl ? `src=\"${escapeHtml(sourceUrl)}\"` : '',\n `preload=\"${this.player.config.preload}\"`,\n this.player.config.crossorigin ? `crossorigin=\"${this.player.config.crossorigin}\"` : '',\n kind === 'video' && canvas.poster ? `poster=\"${escapeHtml(canvas.poster)}\"` : '',\n `aria-label=\"${escapeHtml(canvas.label)}\"`,\n `data-canvasindex=\"${canvas.index}\"`,\n ].filter(Boolean).join(' ');\n const tracks = canvas.tracks.map((track) => `<track src=\"${escapeHtml(track.src)}\" kind=\"${track.kind}\" label=\"${escapeHtml(track.label)}\" ${track.language ? `srclang=\"${escapeHtml(track.language)}\"` : ''} ${track.default ? 'default' : ''}>`).join('');\n return `<${tag} ${attributes}>${tracks}</${tag}>`;\n }\n\n #mediaChromeLanguage(): string {\n const language = this.player.config.preferredLanguages.find((value) => value !== 'none');\n return language ? `lang=\"${escapeHtml(language)}\"` : '';\n }\n\n #panelHtml(manifest: AVManifest, canvas: AVCanvas): string {\n const ui = resolveStrings(this.player.config.strings);\n switch (this.#activePanel) {\n case 'chapters':\n return chapterHtml(manifest.chapters, canvas.id, ui);\n case 'transcript':\n return this.#transcriptHtml(canvas);\n case 'annotations':\n return this.#annotationsHtml(canvas.annotations);\n case 'metadata':\n return `<h3>${escapeHtml(canvas.label)}</h3>${metadataHtml([...manifest.metadata, ...canvas.metadata], ui)}${manifest.requiredStatement ? metadataHtml([manifest.requiredStatement], ui) : ''}${manifest.rights ? `<p><strong>${escapeHtml(ui.rights)}:</strong> <a href=\"${escapeHtml(manifest.rights)}\" target=\"_blank\" rel=\"noreferrer\">${escapeHtml(manifest.rights)}</a></p>` : ''}`;\n case 'files': {\n const files = [...manifest.rendering, ...canvas.rendering];\n return files.length > 0 ? `<ul class=\"file-list\">${files.map((file) => `<li><a href=\"${escapeHtml(file.id)}\" download=\"${escapeHtml(file.filename ?? '')}\">${escapeHtml(file.label)}</a>${file.format ? `<div class=\"time\">${escapeHtml(file.format)}</div>` : ''}</li>`).join('')}</ul>` : `<p class=\"empty\">${escapeHtml(ui.noSupplementalFiles)}</p>`;\n }\n default:\n return '';\n }\n }\n\n #transcriptHtml(canvas: AVCanvas): string {\n const ui = resolveStrings(this.player.config.strings);\n if (canvas.transcripts.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noTranscript)}</p>`;\n const resource = canvas.transcripts[this.#transcriptIndex];\n const query = this.#transcriptQuery.trim();\n const visible = query\n ? this.#transcriptCues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase()))\n : this.#transcriptCues;\n return `<div class=\"panel-tools\">\n ${canvas.transcripts.length > 1 ? `<select data-action=\"transcript\" aria-label=\"${escapeHtml(ui.transcript)}\">${canvas.transcripts.map((item, index) => `<option value=\"${index}\" ${index === this.#transcriptIndex ? 'selected' : ''}>${escapeHtml(item.label)}</option>`).join('')}</select>` : ''}\n ${this.player.config.transcript.search ? `<input type=\"search\" data-action=\"transcript-search\" value=\"${escapeHtml(this.#transcriptQuery)}\" placeholder=\"${escapeHtml(ui.searchTranscript)}\" aria-label=\"${escapeHtml(ui.searchTranscript)}\">` : ''}\n ${this.player.config.transcript.download && resource?.src ? `<a href=\"${escapeHtml(resource.src)}\" download>${escapeHtml(ui.download)}</a>` : ''}\n </div>\n ${this.#transcriptCues.length === 0 ? `<p class=\"empty\">${escapeHtml(ui.loadingTranscript)}</p>` : `<ol class=\"cue-list\">${visible.map((cue) => `<li class=\"cue\" data-cue-id=\"${escapeHtml(cue.id)}\" ${cue.start !== undefined ? `data-start=\"${cue.start}\" data-end=\"${cue.end ?? cue.start}\"` : ''}><button type=\"button\" ${cue.start === undefined ? 'disabled' : ''}>${cue.start !== undefined ? `<span class=\"time\">${formatTime(cue.start)}</span>` : ''}${cue.speaker ? `<span class=\"speaker\">${escapeHtml(cue.speaker)}</span>` : ''}<span>${highlight(cue.text, query)}</span></button></li>`).join('')}</ol>`}`;\n }\n\n #annotationsHtml(annotations: AVAnnotation[]): string {\n const ui = resolveStrings(this.player.config.strings);\n if (annotations.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noAnnotations)}</p>`;\n return `<ol class=\"annotation-list\">${annotations.map((annotation) => `<li class=\"annotation\" data-start=\"${annotation.time?.start ?? ''}\" data-end=\"${annotation.time?.end ?? annotation.time?.start ?? ''}\"><button type=\"button\" ${annotation.time ? '' : 'disabled'}>${annotation.time ? `<span class=\"time\">${formatTime(annotation.time.start)}</span>` : ''}<span>${escapeHtml(annotation.text)}</span>${annotation.tags.length > 0 ? `<span class=\"tags\">${annotation.tags.map((tag) => `<span class=\"tag\">${escapeHtml(tag)}</span>`).join('')}</span>` : ''}</button></li>`).join('')}</ol>`;\n }\n\n #searchMarkersHtml(): string {\n const ui = resolveStrings(this.player.config.strings);\n const query = this.#transcriptQuery.trim().toLowerCase();\n const duration = this.player.state.duration || this.player.canvas?.duration || 0;\n if (!query || !this.player.config.transcript.showSearchMarkers || duration <= 0) return '';\n const matches = this.#transcriptCues.filter((cue) => cue.start !== undefined && cue.text.toLowerCase().includes(query));\n if (matches.length === 0) return '';\n return `<div class=\"search-markers\" aria-label=\"${escapeHtml(ui.transcriptSearchMatches)}\">${matches.map((cue) => `<button type=\"button\" class=\"search-marker\" data-search-time=\"${cue.start}\" style=\"left:${Math.min(100, Math.max(0, ((cue.start ?? 0) / duration) * 100))}%\" aria-label=\"${escapeHtml(formatString(ui.searchMatchAt, { time: formatTime(cue.start ?? 0) }))}\"></button>`).join('')}</div>`;\n }\n\n #bindInteractions(): void {\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((button) => button.addEventListener('click', () => {\n this.#activePanel = button.dataset.panel as NonNullable<AVPlayerConfig['activePanel']>;\n this.#renderPanelOnly();\n if (this.#activePanel === 'transcript') void this.#loadActiveTranscript();\n }));\n this.#root.querySelector<HTMLElement>('[data-action=previous]')?.addEventListener('click', () => this.player.previous({ autoplay: this.player.state.status === 'playing' }));\n this.#root.querySelector<HTMLElement>('[data-action=next]')?.addEventListener('click', () => this.player.next({ autoplay: this.player.state.status === 'playing' }));\n this.#root.querySelector<HTMLSelectElement>('[data-action=source]')?.addEventListener('change', (event) => this.player.selectSource(Number((event.target as HTMLSelectElement).value)));\n this.#root.querySelector<HTMLSelectElement>('[data-action=download]')?.addEventListener('change', (event) => {\n const select = event.target as HTMLSelectElement;\n if (!select.value) return;\n const anchor = document.createElement('a');\n anchor.href = select.value;\n anchor.download = select.selectedOptions[0]?.dataset.filename ?? '';\n anchor.click();\n select.value = '';\n });\n this.#root.querySelector<HTMLInputElement>('[data-action=auto-advance]')?.addEventListener('change', (event) => this.player.setAutoAdvance((event.target as HTMLInputElement).checked));\n this.#root.querySelector<HTMLElement>('[data-action=resume]')?.addEventListener('click', () => {\n if (this.#resumeTime !== undefined) this.player.seekTo(this.#resumeTime);\n this.#resumeTime = undefined;\n this.#render();\n });\n this.#root.querySelector<HTMLElement>('[data-action=dismiss-resume]')?.addEventListener('click', () => {\n this.#resumeTime = undefined;\n this.#render();\n });\n this.#bindPanelInteractions();\n }\n\n #bindPanelInteractions(): void {\n this.#root.querySelectorAll<HTMLElement>('[data-canvas]').forEach((button) => button.addEventListener('click', () => {\n const canvasId = button.dataset.canvas;\n if (canvasId) this.player.selectCanvas(canvasId, { time: Number(button.dataset.time ?? 0), autoplay: true });\n }));\n this.#root.querySelector<HTMLSelectElement>('[data-action=transcript]')?.addEventListener('change', (event) => {\n this.#transcriptIndex = Number((event.target as HTMLSelectElement).value);\n this.#transcriptCues = [];\n this.#renderPanelOnly();\n void this.#loadActiveTranscript();\n });\n this.#root.querySelector<HTMLInputElement>('[data-action=transcript-search]')?.addEventListener('input', (event) => {\n this.#transcriptQuery = (event.target as HTMLInputElement).value;\n const selection = (event.target as HTMLInputElement).selectionStart;\n this.#renderPanelOnly();\n const input = this.#root.querySelector<HTMLInputElement>('[data-action=transcript-search]');\n input?.focus();\n if (selection !== null) input?.setSelectionRange(selection, selection);\n });\n this.#root.querySelectorAll<HTMLElement>('.cue[data-start] button, .annotation[data-start] button').forEach((button) => button.addEventListener('click', () => {\n const row = button.parentElement;\n const start = Number(row?.dataset.start);\n if (Number.isFinite(start)) {\n this.player.seekTo(start);\n playFromInteraction(this.player, this);\n }\n }));\n this.#root.querySelectorAll<HTMLElement>('[data-search-time]').forEach((button) => button.addEventListener('click', () => {\n const time = Number(button.dataset.searchTime);\n if (Number.isFinite(time)) this.player.seekTo(time);\n }));\n }\n\n #renderPanelOnly(): void {\n const manifest = this.player.manifest;\n const canvas = this.player.canvas;\n if (!manifest || !canvas) return;\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((tab) => {\n tab.setAttribute('aria-selected', String(tab.dataset.panel === this.#activePanel));\n });\n const content = this.#root.querySelector<HTMLElement>('.panel-content');\n if (content) content.innerHTML = this.#panelHtml(manifest, canvas);\n this.#root.querySelector('.search-markers')?.remove();\n const toolbar = this.#root.querySelector('.player-toolbar');\n const markers = this.#searchMarkersHtml();\n if (toolbar && markers) toolbar.insertAdjacentHTML('beforebegin', markers);\n this.#bindPanelInteractions();\n }\n\n async #loadActiveTranscript(): Promise<void> {\n const resource = this.player.canvas?.transcripts[this.#transcriptIndex];\n if (!resource) return;\n const token = ++this.#loadingToken;\n try {\n const transcriptOptions: Parameters<typeof loadTranscript>[1] = {};\n if (this.player.config.fetch) transcriptOptions.fetch = this.player.config.fetch;\n if (this.player.config.requestInit) transcriptOptions.requestInit = this.player.config.requestInit;\n if (this.player.config.transcript.showNotes !== undefined) transcriptOptions.showNotes = this.player.config.transcript.showNotes;\n if (this.player.config.transcript.showMetadata !== undefined) transcriptOptions.showMetadata = this.player.config.transcript.showMetadata;\n const cues = await loadTranscript(resource, transcriptOptions);\n if (token !== this.#loadingToken) return;\n this.#transcriptCues = cues;\n this.#renderAudioArt();\n if (this.#activePanel === 'transcript') this.#renderPanelOnly();\n } catch (cause) {\n if (token !== this.#loadingToken) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n this.#transcriptCues = [];\n if (this.#activePanel === 'transcript') this.#renderPanelOnly();\n }\n }\n\n async #resolveSource(): Promise<void> {\n if (!this.player.config.resolveRequest) return;\n const token = ++this.#sourceToken;\n try {\n const sourceUrl = await this.player.resolvedSourceUrl();\n if (token !== this.#sourceToken || !sourceUrl) return;\n this.#resolvedSourceUrl = sourceUrl;\n const media = this.#root.querySelector('video, audio, hls-video') as HTMLMediaElement | null;\n if (media) media.setAttribute('src', sourceUrl);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #syncTimedContent(time = this.player.state.currentTime, instant = false): void {\n syncAudioArtTranscript(this.#root, time, instant);\n const rows = this.#root.querySelectorAll<HTMLElement>('[data-start][data-end]');\n let active: HTMLElement | undefined;\n rows.forEach((row) => {\n const start = Number(row.dataset.start);\n const end = Number(row.dataset.end);\n const isActive = Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);\n row.querySelector('button')?.classList.toggle('active', isActive);\n if (isActive) active = row;\n });\n if (active && this.player.config.transcript.autoScroll) {\n active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n }\n\n #renderAudioArt(): void {\n const art = this.#root.querySelector<HTMLElement>('.audio-art');\n const canvas = this.player.canvas;\n if (!art || !canvas || this.player.source?.kind !== 'audio') return;\n const text = art.querySelector<HTMLElement>('.audio-art-text');\n if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, this.#transcriptCues, Boolean(this.player.config.audioArt.transcript));\n syncAudioArtTranscript(art, this.player.state.currentTime);\n }\n}\n\nexport const defineMangoAVMedia = (tagName = 'mango-av-media'): typeof MangoAVMediaElement => {\n if (typeof customElements === 'undefined') return MangoAVMediaElement;\n if (!customElements.get(tagName)) customElements.define(tagName, MangoAVMediaElement);\n return MangoAVMediaElement;\n};\n"],"mappings":"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","names":[]}
@@ -1,15 +1,23 @@
1
1
  import {
2
- ensureMediaChrome
3
- } from "./chunk-NTHMZZYJ.js";
2
+ AudioArtVisualizer,
3
+ audioArtContentHtml,
4
+ audioArtTranscriptHtml,
5
+ ensureMediaChrome,
6
+ resolveAudioVisualizerMode,
7
+ syncAudioArtTranscript
8
+ } from "./chunk-ZRPA2VFR.js";
4
9
  import {
5
10
  AVControllerElement,
6
11
  componentTheme
7
- } from "./chunk-JXUHKKIX.js";
12
+ } from "./chunk-QMUWPXP5.js";
13
+ import {
14
+ loadTranscript
15
+ } from "./chunk-3PPOT5YI.js";
8
16
  import {
9
17
  escapeHtml,
10
18
  formatString,
11
19
  resolveStrings
12
- } from "./chunk-R4OUWZ3R.js";
20
+ } from "./chunk-HOISQL65.js";
13
21
 
14
22
  // src/components/mango-av-player.ts
15
23
  var hlsElementRegistration;
@@ -25,8 +33,17 @@ var styles = `${componentTheme}
25
33
  video, hls-video { display: block; height: 100%; object-fit: contain; width: 100%; }
26
34
  audio { width: 100%; }
27
35
  media-control-bar { width: 100%; }
28
- .audio-art { align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(135deg, var(--mango-av-accent), #111); display: flex; justify-content: center; padding: 2rem; text-align: center; }
29
- .audio-art span { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; }
36
+ .audio-art { --mango-av-wave-edge-strength: 0%; --mango-av-wave-mid-strength: 0%; --mango-av-wave-rise: 8%; --mango-av-wave-strength: 0%; align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(#09080d, #111019); display: flex; justify-content: center; overflow: hidden; padding: 2rem; position: relative; text-align: center; }
37
+ .audio-art.visualizer--pulse { background: radial-gradient(ellipse 140% 72% at 50% calc(100% + var(--mango-av-wave-rise)), color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-strength), transparent) 0%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-mid-strength), transparent) 40%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-edge-strength), transparent) 62%, transparent 78%), linear-gradient(#09080d, #111019); }
38
+ .audio-art-text { display: contents; }
39
+ .audio-art-visualizer-canvas { height: 100%; inset: 0; pointer-events: none; position: absolute; width: 100%; }
40
+ .audio-art-title { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; position: relative; z-index: 1; }
41
+ .audio-art-transcript { height: 100%; inset: 0; overflow: hidden; position: absolute; z-index: 1; }
42
+ .audio-art-cue { align-items: center; display: flex; flex-direction: column; font-size: clamp(.7rem, 1.3vw, .875rem); font-weight: 550; gap: .15rem; left: 12%; line-height: 1.35; position: absolute; right: 12%; top: 50%; transition: opacity 360ms ease, transform 480ms cubic-bezier(.22, 1, .36, 1); }
43
+ .audio-art-transcript.seeking .audio-art-cue { transition: none; }
44
+ .audio-art-cue.active { font-size: clamp(.78rem, 1.5vw, 1rem); font-weight: 650; }
45
+ .audio-art-cue > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
46
+ .audio-art-speaker { font-size: .7em; letter-spacing: .04em; text-transform: uppercase; }
30
47
  .toolbar { align-items: center; background: #151518; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; padding: .55rem .7rem; }
31
48
  .toolbar button, .toolbar select { background: #29292e; border: 1px solid #44444a; border-radius: .35rem; color: white; padding: .4rem .55rem; }
32
49
  .group { align-items: center; display: flex; gap: .45rem; }
@@ -40,6 +57,9 @@ var MangoAVPlayerElement = class extends AVControllerElement {
40
57
  #config = {};
41
58
  #resolvedSourceUrl;
42
59
  #sourceToken = 0;
60
+ #transcriptCues = [];
61
+ #transcriptToken = 0;
62
+ #visualizer = new AudioArtVisualizer();
43
63
  constructor() {
44
64
  super();
45
65
  this.#root = this.attachShadow({ mode: "open" });
@@ -66,6 +86,10 @@ var MangoAVPlayerElement = class extends AVControllerElement {
66
86
  const source = this.#manifestInput ?? this.getAttribute("manifest-url") ?? void 0;
67
87
  if (source !== void 0 && !this.controller.manifest) void this.load(source);
68
88
  }
89
+ disconnectedCallback() {
90
+ this.#visualizer.disconnect();
91
+ super.disconnectedCallback();
92
+ }
69
93
  attributeChangedCallback(name, oldValue, newValue) {
70
94
  if (!this.isConnected || oldValue === newValue) return;
71
95
  if (name === "manifest-url" && newValue) {
@@ -93,20 +117,34 @@ var MangoAVPlayerElement = class extends AVControllerElement {
93
117
  return this.controller.selectCanvas(indexOrId, options);
94
118
  }
95
119
  controllerChanged() {
120
+ this.#transcriptCues = [];
96
121
  this.#render();
122
+ void this.#loadTranscript();
97
123
  }
98
124
  handleControllerEvent(type, event) {
99
125
  if (type === "av-configchange") {
100
126
  this.#resolvedSourceUrl = void 0;
101
127
  void ensureMediaChrome(this.controller.config.preferredLanguages[0]).then(() => {
102
128
  this.#render();
129
+ void this.#loadTranscript();
103
130
  void this.#resolveSource();
104
131
  });
105
- } else if (type === "av-ready" || type === "av-canvaschange" || type === "av-sourcechange" || type === "av-error") {
132
+ } else if (type === "av-ready" || type === "av-canvaschange") {
106
133
  this.#resolvedSourceUrl = void 0;
134
+ this.#transcriptCues = [];
107
135
  if (this.controller.source?.isHls) void ensureHlsElement();
108
136
  this.#render();
137
+ void this.#loadTranscript();
109
138
  void this.#resolveSource();
139
+ } else if (type === "av-sourcechange" || type === "av-error") {
140
+ this.#resolvedSourceUrl = void 0;
141
+ if (this.controller.source?.isHls) void ensureHlsElement();
142
+ this.#render();
143
+ void this.#resolveSource();
144
+ } else if (type === "av-seek") {
145
+ syncAudioArtTranscript(this.#root, event.detail.to, true);
146
+ } else if (type === "av-timeupdate" || type === "av-statechange") {
147
+ syncAudioArtTranscript(this.#root, this.controller.state.currentTime);
110
148
  }
111
149
  this.dispatchEvent(new CustomEvent(type, { detail: event.detail, bubbles: true, composed: true }));
112
150
  }
@@ -122,6 +160,7 @@ var MangoAVPlayerElement = class extends AVControllerElement {
122
160
  }
123
161
  }
124
162
  #render() {
163
+ this.#visualizer.disconnect();
125
164
  const state = this.controller.state;
126
165
  const canvas = this.controller.canvas;
127
166
  const source = this.controller.source;
@@ -133,7 +172,13 @@ var MangoAVPlayerElement = class extends AVControllerElement {
133
172
  ${canvas && source ? this.#readyHtml(canvas, source) : state.status !== "loading" && state.status !== "error" ? `<div class="audio-art"><span>${escapeHtml(ui.noMediaLoaded)}</span></div>` : ""}
134
173
  </div>`;
135
174
  const media = this.#root.querySelector("video, audio, hls-video");
136
- if (media) this.controller.attachMedia(media);
175
+ if (media) {
176
+ this.controller.attachMedia(media);
177
+ const art = this.#root.querySelector(".audio-art.visualizer");
178
+ const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);
179
+ if (art && media instanceof HTMLAudioElement && visualizerMode) this.#visualizer.connect(media, art, visualizerMode);
180
+ }
181
+ syncAudioArtTranscript(this.#root, this.controller.state.currentTime);
137
182
  this.#bind();
138
183
  }
139
184
  #readyHtml(canvas, source) {
@@ -142,8 +187,11 @@ var MangoAVPlayerElement = class extends AVControllerElement {
142
187
  const kind = source.kind;
143
188
  const ui = resolveStrings(this.controller.config.strings);
144
189
  const language = this.controller.config.preferredLanguages.find((value) => value !== "none");
190
+ const showSourceSelector = controls.sourceSelector && canvas.sources.length > 1;
191
+ const showToolbar = controls.navigation || controls.autoAdvance || showSourceSelector;
192
+ const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);
145
193
  return `<media-controller ${kind === "audio" ? "audio" : ""} ${language ? `lang="${escapeHtml(language)}"` : ""}>
146
- ${kind === "audio" ? `<div class="audio-art"><span>${escapeHtml(canvas.label)}</span></div>` : ""}
194
+ ${kind === "audio" ? `<div class="audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ""}">${audioArtContentHtml(canvas.label, this.#transcriptCues, Boolean(this.controller.config.audioArt.transcript), visualizerMode)}</div>` : ""}
147
195
  ${this.#mediaHtml(canvas, source)}
148
196
  <slot name="overlay"></slot>
149
197
  <slot name="controls">
@@ -162,16 +210,16 @@ var MangoAVPlayerElement = class extends AVControllerElement {
162
210
  ${controls.playbackRate ? `<media-playback-rate-menu rates="${this.controller.config.playbackRates.join(" ")}" hidden></media-playback-rate-menu>` : ""}
163
211
  </slot>
164
212
  </media-controller>
165
- <div class="toolbar">
166
- <div class="group">
213
+ ${showToolbar ? `<div class="toolbar">
214
+ ${controls.navigation ? `<div class="group">
167
215
  <button type="button" data-action="previous" ${canvas.index === 0 ? "disabled" : ""} aria-label="${escapeHtml(ui.previousSection)}">\u2190 ${escapeHtml(ui.previous)}</button>
168
216
  <button type="button" data-action="next" ${canvas.index === (manifest?.canvases.length ?? 1) - 1 && !this.controller.state.repeat ? "disabled" : ""} aria-label="${escapeHtml(ui.nextSection)}">${escapeHtml(ui.next)} \u2192</button>
169
- </div>
170
- <div class="group">
171
- ${controls.sourceSelector && canvas.sources.length > 1 ? `<label><span class="sr-only">${escapeHtml(ui.mediaSource)}</span><select data-action="source">${canvas.sources.map((item, index) => `<option value="${index}" ${index === this.controller.state.sourceIndex ? "selected" : ""}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join("")}</select></label>` : ""}
172
- <label><input type="checkbox" data-action="auto-advance" ${this.controller.state.autoAdvance ? "checked" : ""}> ${escapeHtml(ui.autoAdvance)}</label>
173
- </div>
174
- </div>`;
217
+ </div>` : ""}
218
+ ${showSourceSelector || controls.autoAdvance ? `<div class="group">
219
+ ${showSourceSelector ? `<label><span class="sr-only">${escapeHtml(ui.mediaSource)}</span><select data-action="source">${canvas.sources.map((item, index) => `<option value="${index}" ${index === this.controller.state.sourceIndex ? "selected" : ""}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join("")}</select></label>` : ""}
220
+ ${controls.autoAdvance ? `<label><input type="checkbox" data-action="auto-advance" ${this.controller.state.autoAdvance ? "checked" : ""}> ${escapeHtml(ui.autoAdvance)}</label>` : ""}
221
+ </div>` : ""}
222
+ </div>` : ""}`;
175
223
  }
176
224
  #mediaHtml(canvas, source) {
177
225
  const tag = source.isHls ? "hls-video" : source.kind;
@@ -207,6 +255,32 @@ var MangoAVPlayerElement = class extends AVControllerElement {
207
255
  this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
208
256
  }
209
257
  }
258
+ async #loadTranscript() {
259
+ const resource = this.controller.canvas?.transcripts[0];
260
+ if (!resource || !this.controller.config.audioArt.transcript) return;
261
+ const token = ++this.#transcriptToken;
262
+ const options = {};
263
+ if (this.controller.config.fetch) options.fetch = this.controller.config.fetch;
264
+ if (this.controller.config.requestInit) options.requestInit = this.controller.config.requestInit;
265
+ if (this.controller.config.transcript.showNotes !== void 0) options.showNotes = this.controller.config.transcript.showNotes;
266
+ if (this.controller.config.transcript.showMetadata !== void 0) options.showMetadata = this.controller.config.transcript.showMetadata;
267
+ try {
268
+ const cues = await loadTranscript(resource, options);
269
+ if (token !== this.#transcriptToken) return;
270
+ this.#transcriptCues = cues;
271
+ const art = this.#root.querySelector(".audio-art");
272
+ const canvas = this.controller.canvas;
273
+ if (art && canvas && this.controller.source?.kind === "audio") {
274
+ const text = art.querySelector(".audio-art-text");
275
+ if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, cues, true);
276
+ syncAudioArtTranscript(art, this.controller.state.currentTime);
277
+ }
278
+ } catch (cause) {
279
+ if (token !== this.#transcriptToken) return;
280
+ const error = cause instanceof Error ? cause : new Error(String(cause));
281
+ this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
282
+ }
283
+ }
210
284
  };
211
285
  var defineMangoAVPlayer = (tagName = "mango-av-player") => {
212
286
  if (typeof customElements === "undefined") return MangoAVPlayerElement;
@@ -218,4 +292,4 @@ export {
218
292
  MangoAVPlayerElement,
219
293
  defineMangoAVPlayer
220
294
  };
221
- //# sourceMappingURL=chunk-ML77YRJL.js.map
295
+ //# sourceMappingURL=chunk-ZMC36FZ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/mango-av-player.ts"],"sourcesContent":["import { AVControllerElement } from './controller-element';\nimport { componentTheme } from './component-styles';\nimport { ensureMediaChrome } from './media-chrome';\nimport { AudioArtVisualizer, audioArtContentHtml, audioArtTranscriptHtml, resolveAudioVisualizerMode, syncAudioArtTranscript } from './audio-art';\nimport { loadTranscript } from '../transcript';\nimport type { AVCanvas, AVManifest, AVPlayerConfig, AVPlayerEventMap, JsonObject, MediaSource, TranscriptCue } from '../types';\nimport { escapeHtml, formatString, resolveStrings } from '../utils';\n\nlet hlsElementRegistration: Promise<unknown> | undefined;\nconst ensureHlsElement = (): Promise<unknown> => {\n hlsElementRegistration ??= import('hls-video-element');\n return hlsElementRegistration;\n};\n\nconst styles = `${componentTheme}\n :host { --mango-av-player-aspect-ratio: 16 / 9; }\n .player { background: #08080a; border-radius: var(--mango-av-radius); color: white; overflow: clip; position: relative; }\n media-controller { display: block; width: 100%; }\n media-controller:not([audio]) { aspect-ratio: var(--mango-av-player-aspect-ratio); }\n video, hls-video { display: block; height: 100%; object-fit: contain; width: 100%; }\n audio { width: 100%; }\n media-control-bar { width: 100%; }\n .audio-art { --mango-av-wave-edge-strength: 0%; --mango-av-wave-mid-strength: 0%; --mango-av-wave-rise: 8%; --mango-av-wave-strength: 0%; align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(#09080d, #111019); display: flex; justify-content: center; overflow: hidden; padding: 2rem; position: relative; text-align: center; }\n .audio-art.visualizer--pulse { background: radial-gradient(ellipse 140% 72% at 50% calc(100% + var(--mango-av-wave-rise)), color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-strength), transparent) 0%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-mid-strength), transparent) 40%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-edge-strength), transparent) 62%, transparent 78%), linear-gradient(#09080d, #111019); }\n .audio-art-text { display: contents; }\n .audio-art-visualizer-canvas { height: 100%; inset: 0; pointer-events: none; position: absolute; width: 100%; }\n .audio-art-title { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; position: relative; z-index: 1; }\n .audio-art-transcript { height: 100%; inset: 0; overflow: hidden; position: absolute; z-index: 1; }\n .audio-art-cue { align-items: center; display: flex; flex-direction: column; font-size: clamp(.7rem, 1.3vw, .875rem); font-weight: 550; gap: .15rem; left: 12%; line-height: 1.35; position: absolute; right: 12%; top: 50%; transition: opacity 360ms ease, transform 480ms cubic-bezier(.22, 1, .36, 1); }\n .audio-art-transcript.seeking .audio-art-cue { transition: none; }\n .audio-art-cue.active { font-size: clamp(.78rem, 1.5vw, 1rem); font-weight: 650; }\n .audio-art-cue > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }\n .audio-art-speaker { font-size: .7em; letter-spacing: .04em; text-transform: uppercase; }\n .toolbar { align-items: center; background: #151518; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; padding: .55rem .7rem; }\n .toolbar button, .toolbar select { background: #29292e; border: 1px solid #44444a; border-radius: .35rem; color: white; padding: .4rem .55rem; }\n .group { align-items: center; display: flex; gap: .45rem; }\n .error { background: #fff0f0; color: #8a1717; padding: 1rem; }\n .sr-only { clip: rect(0,0,0,0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }\n`;\n\nexport class MangoAVPlayerElement extends AVControllerElement {\n static observedAttributes = ['manifest-url', 'config'];\n\n #root: ShadowRoot;\n #manifestInput: JsonObject | string | undefined;\n #config: AVPlayerConfig = {};\n #resolvedSourceUrl: string | undefined;\n #sourceToken = 0;\n #transcriptCues: TranscriptCue[] = [];\n #transcriptToken = 0;\n #visualizer = new AudioArtVisualizer();\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n }\n\n get manifest(): JsonObject | string | undefined {\n return this.#manifestInput;\n }\n\n set manifest(value: JsonObject | string | undefined) {\n this.#manifestInput = value;\n if (this.isConnected && value !== undefined) void this.load(value);\n }\n\n get config(): AVPlayerConfig {\n return this.#config;\n }\n\n set config(value: AVPlayerConfig) {\n this.#config = value;\n this.controller.configure(value);\n this.#render();\n }\n\n override connectedCallback(): void {\n this.#readConfigAttribute();\n void ensureMediaChrome(this.controller.config.preferredLanguages[0]);\n super.connectedCallback();\n const source = this.#manifestInput ?? this.getAttribute('manifest-url') ?? undefined;\n if (source !== undefined && !this.controller.manifest) void this.load(source);\n }\n\n override disconnectedCallback(): void {\n this.#visualizer.disconnect();\n super.disconnectedCallback();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected || oldValue === newValue) return;\n if (name === 'manifest-url' && newValue) {\n this.#manifestInput = newValue;\n void this.load(newValue);\n } else if (name === 'config') {\n this.#readConfigAttribute();\n this.#render();\n }\n }\n\n async load(input: JsonObject | string): Promise<AVManifest> {\n this.#manifestInput = input;\n return await this.controller.load(input);\n }\n\n play(): Promise<void> { return this.controller.play(); }\n pause(): void { this.controller.pause(); }\n seekTo(time: number): void { this.controller.seekTo(time); }\n selectCanvas(indexOrId: number | string, options?: { time?: number; autoplay?: boolean }): AVCanvas | undefined {\n return this.controller.selectCanvas(indexOrId, options);\n }\n\n protected controllerChanged(): void {\n this.#transcriptCues = [];\n this.#render();\n void this.#loadTranscript();\n }\n\n protected handleControllerEvent<K extends keyof AVPlayerEventMap>(type: K, event: CustomEvent<AVPlayerEventMap[K]>): void {\n if (type === 'av-configchange') {\n this.#resolvedSourceUrl = undefined;\n void ensureMediaChrome(this.controller.config.preferredLanguages[0]).then(() => {\n this.#render();\n void this.#loadTranscript();\n void this.#resolveSource();\n });\n } else if (type === 'av-ready' || type === 'av-canvaschange') {\n this.#resolvedSourceUrl = undefined;\n this.#transcriptCues = [];\n if (this.controller.source?.isHls) void ensureHlsElement();\n this.#render();\n void this.#loadTranscript();\n void this.#resolveSource();\n } else if (type === 'av-sourcechange' || type === 'av-error') {\n this.#resolvedSourceUrl = undefined;\n if (this.controller.source?.isHls) void ensureHlsElement();\n this.#render();\n void this.#resolveSource();\n } else if (type === 'av-seek') {\n syncAudioArtTranscript(this.#root, (event.detail as AVPlayerEventMap['av-seek']).to, true);\n } else if (type === 'av-timeupdate' || type === 'av-statechange') {\n syncAudioArtTranscript(this.#root, this.controller.state.currentTime);\n }\n this.dispatchEvent(new CustomEvent(type, { detail: event.detail, bubbles: true, composed: true }));\n }\n\n #readConfigAttribute(): void {\n const value = this.getAttribute('config');\n if (!value) return;\n try {\n this.#config = JSON.parse(value) as AVPlayerConfig;\n this.controller.configure(this.#config);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #render(): void {\n this.#visualizer.disconnect();\n const state = this.controller.state;\n const canvas = this.controller.canvas;\n const source = this.controller.source;\n const ui = resolveStrings(this.controller.config.strings);\n this.#root.innerHTML = `<style>${styles}</style>\n <div class=\"player\">\n ${state.status === 'loading' ? `<p role=\"status\">${escapeHtml(ui.loadingManifest)}</p>` : ''}\n ${state.status === 'error' ? `<div class=\"error\" role=\"alert\">${escapeHtml(state.error?.message ?? ui.unableToLoadMedia)}</div>` : ''}\n ${canvas && source ? this.#readyHtml(canvas, source) : state.status !== 'loading' && state.status !== 'error' ? `<div class=\"audio-art\"><span>${escapeHtml(ui.noMediaLoaded)}</span></div>` : ''}\n </div>`;\n const media = this.#root.querySelector('video, audio, hls-video') as HTMLMediaElement | null;\n if (media) {\n this.controller.attachMedia(media);\n const art = this.#root.querySelector<HTMLElement>('.audio-art.visualizer');\n const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);\n if (art && media instanceof HTMLAudioElement && visualizerMode) this.#visualizer.connect(media, art, visualizerMode);\n }\n syncAudioArtTranscript(this.#root, this.controller.state.currentTime);\n this.#bind();\n }\n\n #readyHtml(canvas: AVCanvas, source: MediaSource): string {\n const controls = this.controller.config.controls;\n const manifest = this.controller.manifest;\n const kind = source.kind;\n const ui = resolveStrings(this.controller.config.strings);\n const language = this.controller.config.preferredLanguages.find((value) => value !== 'none');\n const showSourceSelector = controls.sourceSelector && canvas.sources.length > 1;\n const showToolbar = controls.navigation || controls.autoAdvance || showSourceSelector;\n const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);\n return `<media-controller ${kind === 'audio' ? 'audio' : ''} ${language ? `lang=\"${escapeHtml(language)}\"` : ''}>\n ${kind === 'audio' ? `<div class=\"audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ''}\">${audioArtContentHtml(canvas.label, this.#transcriptCues, Boolean(this.controller.config.audioArt.transcript), visualizerMode)}</div>` : ''}\n ${this.#mediaHtml(canvas, source)}\n <slot name=\"overlay\"></slot>\n <slot name=\"controls\">\n <media-control-bar>\n <media-play-button></media-play-button>\n ${controls.seek ? `<media-seek-backward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-backward-button><media-seek-forward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-forward-button>` : ''}\n <media-time-display showduration></media-time-display>\n ${controls.seek ? '<media-time-range></media-time-range>' : ''}\n ${controls.volume ? '<media-mute-button></media-mute-button><media-volume-range></media-volume-range>' : ''}\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu-button></media-captions-menu-button>' : ''}\n ${controls.playbackRate ? '<media-playback-rate-menu-button></media-playback-rate-menu-button>' : ''}\n ${controls.pictureInPicture && kind === 'video' ? '<media-pip-button></media-pip-button>' : ''}\n ${controls.fullscreen && kind === 'video' ? '<media-fullscreen-button></media-fullscreen-button>' : ''}\n </media-control-bar>\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu hidden></media-captions-menu>' : ''}\n ${controls.playbackRate ? `<media-playback-rate-menu rates=\"${this.controller.config.playbackRates.join(' ')}\" hidden></media-playback-rate-menu>` : ''}\n </slot>\n </media-controller>\n ${showToolbar ? `<div class=\"toolbar\">\n ${controls.navigation ? `<div class=\"group\">\n <button type=\"button\" data-action=\"previous\" ${canvas.index === 0 ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.previousSection)}\">← ${escapeHtml(ui.previous)}</button>\n <button type=\"button\" data-action=\"next\" ${canvas.index === (manifest?.canvases.length ?? 1) - 1 && !this.controller.state.repeat ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.nextSection)}\">${escapeHtml(ui.next)} →</button>\n </div>` : ''}\n ${showSourceSelector || controls.autoAdvance ? `<div class=\"group\">\n ${showSourceSelector ? `<label><span class=\"sr-only\">${escapeHtml(ui.mediaSource)}</span><select data-action=\"source\">${canvas.sources.map((item, index) => `<option value=\"${index}\" ${index === this.controller.state.sourceIndex ? 'selected' : ''}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join('')}</select></label>` : ''}\n ${controls.autoAdvance ? `<label><input type=\"checkbox\" data-action=\"auto-advance\" ${this.controller.state.autoAdvance ? 'checked' : ''}> ${escapeHtml(ui.autoAdvance)}</label>` : ''}\n </div>` : ''}\n </div>` : ''}`;\n }\n\n #mediaHtml(canvas: AVCanvas, source: MediaSource): string {\n const tag = source.isHls ? 'hls-video' : source.kind;\n const sourceUrl = this.#resolvedSourceUrl ?? (this.controller.config.resolveRequest ? '' : source.src);\n const attributes = [\n 'slot=\"media\"',\n sourceUrl ? `src=\"${escapeHtml(sourceUrl)}\"` : '',\n `preload=\"${this.controller.config.preload}\"`,\n this.controller.config.crossorigin ? `crossorigin=\"${this.controller.config.crossorigin}\"` : '',\n source.kind === 'video' && canvas.poster ? `poster=\"${escapeHtml(canvas.poster)}\"` : '',\n `aria-label=\"${escapeHtml(canvas.label)}\"`,\n `data-canvasindex=\"${canvas.index}\"`,\n ].filter(Boolean).join(' ');\n const tracks = canvas.tracks.map((track) => `<track src=\"${escapeHtml(track.src)}\" kind=\"${track.kind}\" label=\"${escapeHtml(track.label)}\" ${track.language ? `srclang=\"${escapeHtml(track.language)}\"` : ''} ${track.default ? 'default' : ''}>`).join('');\n return `<${tag} ${attributes}>${tracks}</${tag}>`;\n }\n\n #bind(): void {\n this.#root.querySelector<HTMLElement>('[data-action=previous]')?.addEventListener('click', () => this.controller.previous({ autoplay: this.controller.state.status === 'playing' }));\n this.#root.querySelector<HTMLElement>('[data-action=next]')?.addEventListener('click', () => this.controller.next({ autoplay: this.controller.state.status === 'playing' }));\n this.#root.querySelector<HTMLSelectElement>('[data-action=source]')?.addEventListener('change', (event) => this.controller.selectSource(Number((event.target as HTMLSelectElement).value)));\n this.#root.querySelector<HTMLInputElement>('[data-action=auto-advance]')?.addEventListener('change', (event) => this.controller.setAutoAdvance((event.target as HTMLInputElement).checked));\n }\n\n async #resolveSource(): Promise<void> {\n if (!this.controller.config.resolveRequest) return;\n const token = ++this.#sourceToken;\n try {\n const url = await this.controller.resolvedSourceUrl();\n if (token !== this.#sourceToken || !url) return;\n this.#resolvedSourceUrl = url;\n this.#root.querySelector('video, audio, hls-video')?.setAttribute('src', url);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n async #loadTranscript(): Promise<void> {\n const resource = this.controller.canvas?.transcripts[0];\n if (!resource || !this.controller.config.audioArt.transcript) return;\n const token = ++this.#transcriptToken;\n const options: Parameters<typeof loadTranscript>[1] = {};\n if (this.controller.config.fetch) options.fetch = this.controller.config.fetch;\n if (this.controller.config.requestInit) options.requestInit = this.controller.config.requestInit;\n if (this.controller.config.transcript.showNotes !== undefined) options.showNotes = this.controller.config.transcript.showNotes;\n if (this.controller.config.transcript.showMetadata !== undefined) options.showMetadata = this.controller.config.transcript.showMetadata;\n try {\n const cues = await loadTranscript(resource, options);\n if (token !== this.#transcriptToken) return;\n this.#transcriptCues = cues;\n const art = this.#root.querySelector<HTMLElement>('.audio-art');\n const canvas = this.controller.canvas;\n if (art && canvas && this.controller.source?.kind === 'audio') {\n const text = art.querySelector<HTMLElement>('.audio-art-text');\n if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, cues, true);\n syncAudioArtTranscript(art, this.controller.state.currentTime);\n }\n } catch (cause) {\n if (token !== this.#transcriptToken) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n}\n\nexport const defineMangoAVPlayer = (tagName = 'mango-av-player'): typeof MangoAVPlayerElement => {\n if (typeof customElements === 'undefined') return MangoAVPlayerElement;\n if (!customElements.get(tagName)) customElements.define(tagName, MangoAVPlayerElement);\n return MangoAVPlayerElement;\n};\n"],"mappings":"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","names":[]}