@mango-iiif/av 0.4.0 → 1.0.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 (69) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +24 -17
  3. package/dist/annotations.d.ts +2 -2
  4. package/dist/annotations.js +4 -4
  5. package/dist/captions.d.ts +23 -0
  6. package/dist/captions.js +16 -0
  7. package/dist/captions.js.map +1 -0
  8. package/dist/chapters.d.ts +2 -2
  9. package/dist/chapters.js +3 -3
  10. package/dist/{chunk-4NOWGMI5.js → chunk-53ZN4Y4L.js} +28 -66
  11. package/dist/chunk-53ZN4Y4L.js.map +1 -0
  12. package/dist/chunk-AWLDINQR.js +73 -0
  13. package/dist/chunk-AWLDINQR.js.map +1 -0
  14. package/dist/chunk-BB7RE7EO.js +407 -0
  15. package/dist/chunk-BB7RE7EO.js.map +1 -0
  16. package/dist/chunk-BMMQFCD3.js +86 -0
  17. package/dist/chunk-BMMQFCD3.js.map +1 -0
  18. package/dist/chunk-IZYEGSGT.js +148 -0
  19. package/dist/chunk-IZYEGSGT.js.map +1 -0
  20. package/dist/chunk-JXZR5DL2.js +160 -0
  21. package/dist/chunk-JXZR5DL2.js.map +1 -0
  22. package/dist/{chunk-CIAQZ2EB.js → chunk-LMZIFVHN.js} +8 -4
  23. package/dist/chunk-LMZIFVHN.js.map +1 -0
  24. package/dist/chunk-TQNEVWUH.js +39 -0
  25. package/dist/chunk-TQNEVWUH.js.map +1 -0
  26. package/dist/chunk-XXM4G3K5.js +296 -0
  27. package/dist/chunk-XXM4G3K5.js.map +1 -0
  28. package/dist/chunk-YVI7J43Q.js +29 -0
  29. package/dist/chunk-YVI7J43Q.js.map +1 -0
  30. package/dist/chunk-Z2OF3RKT.js +152 -0
  31. package/dist/chunk-Z2OF3RKT.js.map +1 -0
  32. package/dist/{controller-SkwGkVE8.d.ts → controller-Ag3nFtwP.d.ts} +16 -24
  33. package/dist/{controller-element-CkXIA6w2.d.ts → controller-element-Cn5cxKnD.d.ts} +4 -2
  34. package/dist/core.d.ts +2 -2
  35. package/dist/core.js +2 -2
  36. package/dist/index.d.ts +6 -3
  37. package/dist/index.js +21 -14
  38. package/dist/index.js.map +1 -1
  39. package/dist/media.d.ts +3 -3
  40. package/dist/media.js +11 -5
  41. package/dist/media.js.map +1 -1
  42. package/dist/player.d.ts +8 -2
  43. package/dist/player.js +3 -5
  44. package/dist/player.js.map +1 -1
  45. package/dist/ramp-2XKALEGA.js +98 -0
  46. package/dist/ramp-2XKALEGA.js.map +1 -0
  47. package/dist/transcript-element.d.ts +2 -2
  48. package/dist/transcript-element.js +6 -5
  49. package/dist/transcript-element.js.map +1 -1
  50. package/docs/API.md +70 -45
  51. package/package.json +18 -11
  52. package/dist/chunk-4NOWGMI5.js.map +0 -1
  53. package/dist/chunk-BAE4FKBJ.js +0 -645
  54. package/dist/chunk-BAE4FKBJ.js.map +0 -1
  55. package/dist/chunk-C4H46HDS.js +0 -95
  56. package/dist/chunk-C4H46HDS.js.map +0 -1
  57. package/dist/chunk-CDLYT3KB.js +0 -17
  58. package/dist/chunk-CDLYT3KB.js.map +0 -1
  59. package/dist/chunk-CIAQZ2EB.js.map +0 -1
  60. package/dist/chunk-EHIKFRFV.js +0 -75
  61. package/dist/chunk-EHIKFRFV.js.map +0 -1
  62. package/dist/chunk-FVYEV2LO.js +0 -163
  63. package/dist/chunk-FVYEV2LO.js.map +0 -1
  64. package/dist/chunk-GQD675FA.js +0 -289
  65. package/dist/chunk-GQD675FA.js.map +0 -1
  66. package/dist/chunk-M5ZZRBGD.js +0 -357
  67. package/dist/chunk-M5ZZRBGD.js.map +0 -1
  68. package/dist/chunk-YX32ZQPR.js +0 -83
  69. package/dist/chunk-YX32ZQPR.js.map +0 -1
@@ -1,289 +0,0 @@
1
- import {
2
- escapeHtml
3
- } from "./chunk-4NOWGMI5.js";
4
-
5
- // src/components/media-chrome.ts
6
- var registration;
7
- var languageRegistrations = /* @__PURE__ */ new Map();
8
- var loadLanguage = (language) => {
9
- const normalized = language.toLowerCase();
10
- const code = normalized === "zh-cn" || normalized === "zh-tw" ? normalized : normalized.split("-")[0] ?? normalized;
11
- if (languageRegistrations.has(code)) return languageRegistrations.get(code);
12
- const pending = (() => {
13
- switch (code) {
14
- case "de":
15
- return import("media-chrome/lang/de");
16
- case "es":
17
- return import("media-chrome/lang/es");
18
- case "fr":
19
- return import("media-chrome/lang/fr");
20
- case "pt":
21
- return import("media-chrome/lang/pt");
22
- case "zh-cn":
23
- return import("media-chrome/lang/zh-CN");
24
- case "zh-tw":
25
- return import("media-chrome/lang/zh-TW");
26
- default:
27
- return void 0;
28
- }
29
- })();
30
- if (pending) languageRegistrations.set(code, pending);
31
- return pending;
32
- };
33
- var ensureMediaChrome = async (language) => {
34
- if (language) await loadLanguage(language);
35
- registration ??= import("media-chrome");
36
- await registration;
37
- };
38
-
39
- // src/components/audio-art.ts
40
- var audioArtTranscriptHtml = (label, title, cues, enabled) => {
41
- if (!enabled || cues.length === 0) return title ? `<span class="audio-art-title">${escapeHtml(title)}</span>` : "";
42
- return `<div class="audio-art-transcript" aria-live="polite" aria-label="${escapeHtml(label)}">
43
- ${cues.map((cue) => `<div class="audio-art-cue" data-audio-cue data-start="${cue.start ?? ""}" data-end="${cue.end ?? cue.start ?? ""}">${cue.speaker ? `<span class="audio-art-speaker">${escapeHtml(cue.speaker)}</span>` : ""}<span>${escapeHtml(cue.text)}</span></div>`).join("")}
44
- </div>`;
45
- };
46
- var resolveAudioVisualizerMode = (value) => value === true ? "pulse" : value || void 0;
47
- var VISUALIZER_COLOR_FALLBACK = "#5b3fd6";
48
- var visualizerPropertiesRegistered = false;
49
- var registerVisualizerProperties = () => {
50
- if (visualizerPropertiesRegistered) return;
51
- visualizerPropertiesRegistered = true;
52
- if (typeof CSS === "undefined" || typeof CSS.registerProperty !== "function") return;
53
- try {
54
- CSS.registerProperty({
55
- name: "--mango-av-visualizer-intensity",
56
- syntax: "<number>",
57
- inherits: true,
58
- initialValue: "1"
59
- });
60
- } catch {
61
- }
62
- };
63
- var resolveVisualizerIntensity = (value) => {
64
- const intensity = Number.parseFloat(value);
65
- return Number.isFinite(intensity) && intensity >= 0 ? intensity : 1;
66
- };
67
- var waveformAmplitude = (height, level, intensity, reducedMotion) => reducedMotion ? 0 : Math.min(height * 0.16, (5 + level * height * 0.12) * intensity);
68
- var spectrumBarHeight = (height, energy, intensity) => Math.max(1, Math.min(height * 0.32, energy * height * 0.32 * intensity));
69
- var readVisualizerStyle = (art) => {
70
- const styles = getComputedStyle(art);
71
- return {
72
- color: styles.getPropertyValue("--mango-av-visualizer-color").trim() || styles.getPropertyValue("--mango-av-accent").trim() || VISUALIZER_COLOR_FALLBACK,
73
- intensity: resolveVisualizerIntensity(styles.getPropertyValue("--mango-av-visualizer-intensity"))
74
- };
75
- };
76
- var audioArtContentHtml = (label, title, cues, transcriptEnabled, visualizerMode) => `<div class="audio-art-text">${audioArtTranscriptHtml(label, title, cues, transcriptEnabled)}</div>${visualizerMode === "waveform" || visualizerMode === "spectrum" ? `<canvas class="audio-art-visualizer-canvas" aria-hidden="true"></canvas>` : ""}`;
77
- var syncAudioArtTranscript = (root, time, instant = false) => {
78
- const cues = [...root.querySelectorAll("[data-audio-cue]")];
79
- if (cues.length === 0) return;
80
- const transcript = root.querySelector(".audio-art-transcript");
81
- if (instant && transcript) {
82
- transcript.classList.add("seeking");
83
- requestAnimationFrame(() => transcript.classList.remove("seeking"));
84
- }
85
- let activeIndex = cues.findIndex((cue) => {
86
- const start = Number(cue.dataset.start);
87
- const end = Number(cue.dataset.end);
88
- return Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);
89
- });
90
- if (activeIndex < 0) {
91
- for (let index = cues.length - 1; index >= 0; index -= 1) {
92
- const cue = cues[index];
93
- if (cue && Number.isFinite(Number(cue.dataset.start)) && time >= Number(cue.dataset.start)) {
94
- activeIndex = index;
95
- break;
96
- }
97
- }
98
- }
99
- if (activeIndex < 0) activeIndex = 0;
100
- cues.forEach((cue, index) => {
101
- const offset = index - activeIndex;
102
- cue.classList.toggle("active", offset === 0);
103
- cue.classList.toggle("adjacent", Math.abs(offset) === 1);
104
- cue.toggleAttribute("aria-hidden", offset !== 0);
105
- cue.style.opacity = offset === 0 ? "1" : Math.abs(offset) === 1 ? ".2" : "0";
106
- cue.style.transform = `translateY(calc(-50% + ${offset * 4.25}rem))`;
107
- cue.style.visibility = Math.abs(offset) <= 1 ? "visible" : "hidden";
108
- });
109
- };
110
- var AudioArtVisualizer = class {
111
- #token = 0;
112
- #context;
113
- #source;
114
- #analyzer;
115
- #frame = 0;
116
- #level = 0;
117
- #media;
118
- #resume;
119
- connect(media, art, mode) {
120
- this.disconnect();
121
- const token = ++this.#token;
122
- if (typeof AudioContext === "undefined") return;
123
- try {
124
- if (token !== this.#token || !media.isConnected || !art.isConnected) return;
125
- const context = new AudioContext();
126
- this.#context = context;
127
- const source = context.createMediaElementSource(media);
128
- this.#source = source;
129
- const analyzer = context.createAnalyser();
130
- this.#analyzer = analyzer;
131
- analyzer.fftSize = 512;
132
- analyzer.smoothingTimeConstant = 0.95;
133
- source.connect(context.destination);
134
- source.connect(analyzer);
135
- this.#media = media;
136
- const samples = new Uint8Array(analyzer.fftSize);
137
- const frequencies = new Uint8Array(analyzer.frequencyBinCount);
138
- const canvas = art.querySelector(".audio-art-visualizer-canvas");
139
- const canvasContext = canvas?.getContext("2d");
140
- const reducedMotion = typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches;
141
- const resizeCanvas = () => {
142
- if (!canvas || !canvasContext) return;
143
- const scale = Math.min(2, devicePixelRatio || 1);
144
- const width = Math.max(1, Math.round(art.clientWidth * scale));
145
- const height = Math.max(1, Math.round(art.clientHeight * scale));
146
- if (canvas.width !== width || canvas.height !== height) {
147
- canvas.width = width;
148
- canvas.height = height;
149
- }
150
- canvasContext.setTransform(scale, 0, 0, scale, 0, 0);
151
- };
152
- const drawWaveform = (strength) => {
153
- if (!canvas || !canvasContext) return;
154
- resizeCanvas();
155
- const width = art.clientWidth;
156
- const height = art.clientHeight;
157
- canvasContext.clearRect(0, 0, width, height);
158
- if (strength === 0) return;
159
- const { color, intensity } = readVisualizerStyle(art);
160
- if (intensity === 0) return;
161
- canvasContext.globalAlpha = Math.min(1, Math.min(0.7, 0.18 + strength / 100) * intensity);
162
- canvasContext.strokeStyle = color;
163
- canvasContext.lineWidth = 1.5;
164
- canvasContext.beginPath();
165
- const amplitude = waveformAmplitude(height, this.#level, intensity, reducedMotion);
166
- for (let index = 0; index < samples.length; index += 1) {
167
- const x = index / (samples.length - 1) * width;
168
- const sample = (samples[index] - 128) / 128;
169
- const y = height * 0.72 + sample * amplitude;
170
- if (index === 0) canvasContext.moveTo(x, y);
171
- else canvasContext.lineTo(x, y);
172
- }
173
- canvasContext.stroke();
174
- canvasContext.globalAlpha = 1;
175
- };
176
- const drawSpectrum = (strength) => {
177
- if (!canvas || !canvasContext) return;
178
- resizeCanvas();
179
- const width = art.clientWidth;
180
- const height = art.clientHeight;
181
- canvasContext.clearRect(0, 0, width, height);
182
- if (strength === 0) return;
183
- const { color, intensity } = readVisualizerStyle(art);
184
- if (intensity === 0) return;
185
- analyzer.getByteFrequencyData(frequencies);
186
- const barCount = 24;
187
- const gap = 3;
188
- const barWidth = Math.max(1, (width - gap * (barCount - 1)) / barCount);
189
- canvasContext.fillStyle = color;
190
- canvasContext.globalAlpha = Math.min(1, (reducedMotion ? 0.2 : Math.min(0.55, 0.15 + strength / 120)) * intensity);
191
- for (let index = 0; index < barCount; index += 1) {
192
- const frequencyIndex = Math.floor((index / barCount) ** 1.7 * frequencies.length * 0.7);
193
- const energy = reducedMotion ? 0.08 : (frequencies[frequencyIndex] ?? 0) / 255;
194
- const barHeight = spectrumBarHeight(height, energy, intensity);
195
- canvasContext.fillRect(index * (barWidth + gap), height - barHeight, barWidth, barHeight);
196
- }
197
- canvasContext.globalAlpha = 1;
198
- };
199
- const draw = () => {
200
- if (token !== this.#token) return;
201
- analyzer.getByteTimeDomainData(samples);
202
- let sum = 0;
203
- for (const sample of samples) {
204
- const normalized = (sample - 128) / 128;
205
- sum += normalized * normalized;
206
- }
207
- const rms = Math.sqrt(sum / samples.length);
208
- const target = media.paused || rms < 0.012 ? 0 : Math.min(1, (rms - 0.012) * 5);
209
- this.#level += (target - this.#level) * (target > this.#level ? 0.1 : 0.04);
210
- if (target === 0 && this.#level < 0.01) this.#level = 0;
211
- const strength = reducedMotion ? this.#level * 18 : this.#level === 0 ? 0 : Math.min(52, 12 + this.#level * 40);
212
- art.style.setProperty("--mango-av-wave-rise", `${(8 - (reducedMotion ? 0 : this.#level * 6)).toFixed(2)}%`);
213
- art.style.setProperty("--mango-av-wave-strength", `${strength.toFixed(1)}%`);
214
- art.style.setProperty("--mango-av-wave-mid-strength", `${(strength * 0.45).toFixed(1)}%`);
215
- art.style.setProperty("--mango-av-wave-edge-strength", `${(strength * 0.18).toFixed(1)}%`);
216
- if (mode === "waveform") drawWaveform(strength);
217
- else if (mode === "spectrum") drawSpectrum(strength);
218
- this.#frame = requestAnimationFrame(draw);
219
- };
220
- draw();
221
- const resume = () => void context.resume();
222
- this.#resume = resume;
223
- media.addEventListener("play", resume, { once: true });
224
- if (!media.paused) resume();
225
- } catch {
226
- this.disconnect();
227
- }
228
- }
229
- disconnect() {
230
- this.#token += 1;
231
- if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(this.#frame);
232
- this.#frame = 0;
233
- this.#level = 0;
234
- if (this.#media && this.#resume) this.#media.removeEventListener("play", this.#resume);
235
- try {
236
- this.#source?.disconnect();
237
- } catch {
238
- }
239
- try {
240
- this.#analyzer?.disconnect();
241
- } catch {
242
- }
243
- if (this.#context && this.#context.state !== "closed") void this.#context.close();
244
- this.#analyzer = void 0;
245
- this.#source = void 0;
246
- this.#context = void 0;
247
- this.#media = void 0;
248
- this.#resume = void 0;
249
- }
250
- };
251
-
252
- // src/components/media-policy.ts
253
- var isCrossOriginHttp = (sourceUrl) => {
254
- try {
255
- const url = new URL(sourceUrl, document.baseURI);
256
- return (url.protocol === "http:" || url.protocol === "https:") && url.origin !== location.origin;
257
- } catch {
258
- return false;
259
- }
260
- };
261
- var resolveAudioMediaPolicy = (sourceUrl, configuredCrossOrigin, crossOriginExplicit, visualizerMode, fallback) => {
262
- const crossOrigin = isCrossOriginHttp(sourceUrl);
263
- const implicitCors = Boolean(visualizerMode) && crossOrigin && !crossOriginExplicit && !fallback;
264
- const selectedCrossOrigin = implicitCors ? "anonymous" : configuredCrossOrigin || void 0;
265
- const visualize = Boolean(visualizerMode) && !fallback && !(crossOrigin && !selectedCrossOrigin);
266
- return {
267
- ...selectedCrossOrigin ? { crossOrigin: selectedCrossOrigin } : {},
268
- implicitCors,
269
- visualize
270
- };
271
- };
272
- var applyMediaSource = (media, sourceUrl, crossOrigin) => {
273
- if (crossOrigin) media.crossOrigin = crossOrigin;
274
- else media.removeAttribute("crossorigin");
275
- media.src = sourceUrl;
276
- };
277
-
278
- export {
279
- ensureMediaChrome,
280
- audioArtTranscriptHtml,
281
- resolveAudioVisualizerMode,
282
- registerVisualizerProperties,
283
- audioArtContentHtml,
284
- syncAudioArtTranscript,
285
- AudioArtVisualizer,
286
- resolveAudioMediaPolicy,
287
- applyMediaSource
288
- };
289
- //# sourceMappingURL=chunk-GQD675FA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/media-chrome.ts","../src/components/audio-art.ts","../src/components/media-policy.ts"],"sourcesContent":["let registration: Promise<unknown> | undefined;\nconst languageRegistrations = new Map<string, Promise<unknown>>();\n\nconst loadLanguage = (language: string): Promise<unknown> | undefined => {\n const normalized = language.toLowerCase();\n const code = normalized === 'zh-cn' || normalized === 'zh-tw'\n ? normalized\n : normalized.split('-')[0] ?? normalized;\n if (languageRegistrations.has(code)) return languageRegistrations.get(code);\n const pending = (() => {\n switch (code) {\n case 'de': return import('media-chrome/lang/de');\n case 'es': return import('media-chrome/lang/es');\n case 'fr': return import('media-chrome/lang/fr');\n case 'pt': return import('media-chrome/lang/pt');\n case 'zh-cn': return import('media-chrome/lang/zh-CN');\n case 'zh-tw': return import('media-chrome/lang/zh-TW');\n default: return undefined;\n }\n })();\n if (pending) languageRegistrations.set(code, pending);\n return pending;\n};\n\nexport const ensureMediaChrome = async (language?: string): Promise<void> => {\n if (language) await loadLanguage(language);\n registration ??= import('media-chrome');\n await registration;\n};\n","import type { AVAudioVisualizerMode, TranscriptCue } from '../types';\nimport { escapeHtml } from '../utils';\n\nexport const audioArtTranscriptHtml = (label: string, title: string, cues: TranscriptCue[], enabled: boolean): string => {\n if (!enabled || cues.length === 0) return title ? `<span class=\"audio-art-title\">${escapeHtml(title)}</span>` : '';\n return `<div class=\"audio-art-transcript\" aria-live=\"polite\" aria-label=\"${escapeHtml(label)}\">\n ${cues.map((cue) => `<div class=\"audio-art-cue\" data-audio-cue data-start=\"${cue.start ?? ''}\" data-end=\"${cue.end ?? cue.start ?? ''}\">${cue.speaker ? `<span class=\"audio-art-speaker\">${escapeHtml(cue.speaker)}</span>` : ''}<span>${escapeHtml(cue.text)}</span></div>`).join('')}\n </div>`;\n};\n\nexport const resolveAudioVisualizerMode = (\n value: boolean | AVAudioVisualizerMode | undefined,\n): AVAudioVisualizerMode | undefined => value === true ? 'pulse' : value || undefined;\n\nconst VISUALIZER_COLOR_FALLBACK = '#5b3fd6';\n\n/**\n * Registering the intensity keeps a malformed host value from invalidating the\n * artwork background, which would otherwise drop the base gradient along with\n * the pulse and leave the panel transparent. Registration must happen through\n * the API rather than an `@property` rule in the component's styles, because\n * property registrations are document-scoped and a rule inside a shadow tree is\n * ignored. Browsers without the API keep the `var()` defaults written alongside\n * each use, which cover every case but a malformed value.\n *\n * The colour is deliberately left unregistered: an initial value would be\n * substituted ahead of the `var()` fallback and stop the visualizer following\n * `--mango-av-accent` when the host has set no colour of its own.\n */\nlet visualizerPropertiesRegistered = false;\n\nexport const registerVisualizerProperties = (): void => {\n if (visualizerPropertiesRegistered) return;\n visualizerPropertiesRegistered = true;\n if (typeof CSS === 'undefined' || typeof CSS.registerProperty !== 'function') return;\n try {\n CSS.registerProperty({\n name: '--mango-av-visualizer-intensity',\n syntax: '<number>',\n inherits: true,\n initialValue: '1',\n });\n } catch { /* Already registered by another instance or bundle on this page. */ }\n};\n\n/**\n * `--mango-av-visualizer-intensity` scales how strongly the visualizer renders\n * without touching the analysed audio level, so `1` reproduces the package\n * default exactly and `0` hides the visualizer while playback continues.\n */\nexport const resolveVisualizerIntensity = (value: string): number => {\n const intensity = Number.parseFloat(value);\n return Number.isFinite(intensity) && intensity >= 0 ? intensity : 1;\n};\n\n/**\n * Intensity scales the trace itself, not only its opacity, so the control reads\n * as strength rather than as a fade. The existing ceiling is applied last and so\n * still holds at any intensity, keeping the wave inside the artwork.\n */\nexport const waveformAmplitude = (\n height: number,\n level: number,\n intensity: number,\n reducedMotion: boolean,\n): number => reducedMotion ? 0 : Math.min(height * .16, (5 + level * height * .12) * intensity);\n\n/** Bars are capped at the height an unscaled full-energy bar would reach. */\nexport const spectrumBarHeight = (height: number, energy: number, intensity: number): number =>\n Math.max(1, Math.min(height * .32, energy * height * .32 * intensity));\n\n/**\n * The visualizer colour and intensity are read per frame rather than cached, so\n * a theme change or a host stylesheet swap takes effect without reconnecting\n * the Web Audio graph.\n */\nconst readVisualizerStyle = (art: HTMLElement): { color: string; intensity: number } => {\n const styles = getComputedStyle(art);\n return {\n color: styles.getPropertyValue('--mango-av-visualizer-color').trim()\n || styles.getPropertyValue('--mango-av-accent').trim()\n || VISUALIZER_COLOR_FALLBACK,\n intensity: resolveVisualizerIntensity(styles.getPropertyValue('--mango-av-visualizer-intensity')),\n };\n};\n\nexport const audioArtContentHtml = (\n label: string,\n title: string,\n cues: TranscriptCue[],\n transcriptEnabled: boolean,\n visualizerMode?: AVAudioVisualizerMode,\n): string => `<div class=\"audio-art-text\">${audioArtTranscriptHtml(label, title, cues, transcriptEnabled)}</div>${visualizerMode === 'waveform' || visualizerMode === 'spectrum' ? `<canvas class=\"audio-art-visualizer-canvas\" aria-hidden=\"true\"></canvas>` : ''}`;\n\nexport const syncAudioArtTranscript = (root: ParentNode, time: number, instant = false): void => {\n const cues = [...root.querySelectorAll<HTMLElement>('[data-audio-cue]')];\n if (cues.length === 0) return;\n const transcript = root.querySelector<HTMLElement>('.audio-art-transcript');\n if (instant && transcript) {\n transcript.classList.add('seeking');\n requestAnimationFrame(() => transcript.classList.remove('seeking'));\n }\n\n let activeIndex = cues.findIndex((cue) => {\n const start = Number(cue.dataset.start);\n const end = Number(cue.dataset.end);\n return Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);\n });\n if (activeIndex < 0) {\n for (let index = cues.length - 1; index >= 0; index -= 1) {\n const cue = cues[index];\n if (cue && Number.isFinite(Number(cue.dataset.start)) && time >= Number(cue.dataset.start)) {\n activeIndex = index;\n break;\n }\n }\n }\n if (activeIndex < 0) activeIndex = 0;\n\n cues.forEach((cue, index) => {\n const offset = index - activeIndex;\n cue.classList.toggle('active', offset === 0);\n cue.classList.toggle('adjacent', Math.abs(offset) === 1);\n cue.toggleAttribute('aria-hidden', offset !== 0);\n cue.style.opacity = offset === 0 ? '1' : Math.abs(offset) === 1 ? '.2' : '0';\n cue.style.transform = `translateY(calc(-50% + ${offset * 4.25}rem))`;\n cue.style.visibility = Math.abs(offset) <= 1 ? 'visible' : 'hidden';\n });\n};\n\nexport class AudioArtVisualizer {\n #token = 0;\n #context: AudioContext | undefined;\n #source: MediaElementAudioSourceNode | undefined;\n #analyzer: AnalyserNode | undefined;\n #frame = 0;\n #level = 0;\n #media: HTMLMediaElement | undefined;\n #resume: (() => void) | undefined;\n\n connect(media: HTMLMediaElement, art: HTMLElement, mode: AVAudioVisualizerMode): void {\n this.disconnect();\n const token = ++this.#token;\n if (typeof AudioContext === 'undefined') return;\n\n try {\n if (token !== this.#token || !media.isConnected || !art.isConnected) return;\n const context = new AudioContext();\n this.#context = context;\n const source = context.createMediaElementSource(media);\n this.#source = source;\n const analyzer = context.createAnalyser();\n this.#analyzer = analyzer;\n analyzer.fftSize = 512;\n analyzer.smoothingTimeConstant = .95;\n source.connect(context.destination);\n source.connect(analyzer);\n this.#media = media;\n const samples = new Uint8Array(analyzer.fftSize);\n const frequencies = new Uint8Array(analyzer.frequencyBinCount);\n const canvas = art.querySelector<HTMLCanvasElement>('.audio-art-visualizer-canvas');\n const canvasContext = canvas?.getContext('2d');\n const reducedMotion = typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n const resizeCanvas = () => {\n if (!canvas || !canvasContext) return;\n const scale = Math.min(2, devicePixelRatio || 1);\n const width = Math.max(1, Math.round(art.clientWidth * scale));\n const height = Math.max(1, Math.round(art.clientHeight * scale));\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n }\n canvasContext.setTransform(scale, 0, 0, scale, 0, 0);\n };\n const drawWaveform = (strength: number) => {\n if (!canvas || !canvasContext) return;\n resizeCanvas();\n const width = art.clientWidth;\n const height = art.clientHeight;\n canvasContext.clearRect(0, 0, width, height);\n if (strength === 0) return;\n const { color, intensity } = readVisualizerStyle(art);\n if (intensity === 0) return;\n canvasContext.globalAlpha = Math.min(1, Math.min(.7, .18 + strength / 100) * intensity);\n canvasContext.strokeStyle = color;\n canvasContext.lineWidth = 1.5;\n canvasContext.beginPath();\n const amplitude = waveformAmplitude(height, this.#level, intensity, reducedMotion);\n for (let index = 0; index < samples.length; index += 1) {\n const x = (index / (samples.length - 1)) * width;\n const sample = (samples[index]! - 128) / 128;\n const y = height * .72 + sample * amplitude;\n if (index === 0) canvasContext.moveTo(x, y);\n else canvasContext.lineTo(x, y);\n }\n canvasContext.stroke();\n canvasContext.globalAlpha = 1;\n };\n const drawSpectrum = (strength: number) => {\n if (!canvas || !canvasContext) return;\n resizeCanvas();\n const width = art.clientWidth;\n const height = art.clientHeight;\n canvasContext.clearRect(0, 0, width, height);\n if (strength === 0) return;\n const { color, intensity } = readVisualizerStyle(art);\n if (intensity === 0) return;\n analyzer.getByteFrequencyData(frequencies);\n const barCount = 24;\n const gap = 3;\n const barWidth = Math.max(1, (width - gap * (barCount - 1)) / barCount);\n canvasContext.fillStyle = color;\n canvasContext.globalAlpha = Math.min(1, (reducedMotion ? .2 : Math.min(.55, .15 + strength / 120)) * intensity);\n for (let index = 0; index < barCount; index += 1) {\n const frequencyIndex = Math.floor((index / barCount) ** 1.7 * frequencies.length * .7);\n const energy = reducedMotion ? .08 : (frequencies[frequencyIndex] ?? 0) / 255;\n const barHeight = spectrumBarHeight(height, energy, intensity);\n canvasContext.fillRect(index * (barWidth + gap), height - barHeight, barWidth, barHeight);\n }\n canvasContext.globalAlpha = 1;\n };\n const draw = () => {\n if (token !== this.#token) return;\n analyzer.getByteTimeDomainData(samples);\n let sum = 0;\n for (const sample of samples) {\n const normalized = (sample - 128) / 128;\n sum += normalized * normalized;\n }\n const rms = Math.sqrt(sum / samples.length);\n const target = media.paused || rms < .012 ? 0 : Math.min(1, (rms - .012) * 5);\n this.#level += (target - this.#level) * (target > this.#level ? .1 : .04);\n if (target === 0 && this.#level < .01) this.#level = 0;\n const strength = reducedMotion\n ? this.#level * 18\n : this.#level === 0 ? 0 : Math.min(52, 12 + this.#level * 40);\n art.style.setProperty('--mango-av-wave-rise', `${(8 - (reducedMotion ? 0 : this.#level * 6)).toFixed(2)}%`);\n art.style.setProperty('--mango-av-wave-strength', `${strength.toFixed(1)}%`);\n art.style.setProperty('--mango-av-wave-mid-strength', `${(strength * .45).toFixed(1)}%`);\n art.style.setProperty('--mango-av-wave-edge-strength', `${(strength * .18).toFixed(1)}%`);\n if (mode === 'waveform') drawWaveform(strength);\n else if (mode === 'spectrum') drawSpectrum(strength);\n this.#frame = requestAnimationFrame(draw);\n };\n draw();\n const resume = () => void context.resume();\n this.#resume = resume;\n media.addEventListener('play', resume, { once: true });\n if (!media.paused) resume();\n } catch {\n this.disconnect();\n }\n }\n\n disconnect(): void {\n this.#token += 1;\n if (typeof cancelAnimationFrame !== 'undefined') cancelAnimationFrame(this.#frame);\n this.#frame = 0;\n this.#level = 0;\n if (this.#media && this.#resume) this.#media.removeEventListener('play', this.#resume);\n try { this.#source?.disconnect(); } catch { /* Already disconnected. */ }\n try { this.#analyzer?.disconnect(); } catch { /* Already disconnected. */ }\n if (this.#context && this.#context.state !== 'closed') void this.#context.close();\n this.#analyzer = undefined;\n this.#source = undefined;\n this.#context = undefined;\n this.#media = undefined;\n this.#resume = undefined;\n }\n}\n","import type { AVAudioVisualizerMode } from '../types';\n\nexport interface AudioMediaPolicy {\n crossOrigin?: 'anonymous' | 'use-credentials';\n implicitCors: boolean;\n visualize: boolean;\n}\n\nconst isCrossOriginHttp = (sourceUrl: string): boolean => {\n try {\n const url = new URL(sourceUrl, document.baseURI);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.origin !== location.origin;\n } catch {\n return false;\n }\n};\n\nexport const resolveAudioMediaPolicy = (\n sourceUrl: string,\n configuredCrossOrigin: 'anonymous' | 'use-credentials' | '',\n crossOriginExplicit: boolean,\n visualizerMode: AVAudioVisualizerMode | undefined,\n fallback: boolean,\n): AudioMediaPolicy => {\n const crossOrigin = isCrossOriginHttp(sourceUrl);\n const implicitCors = Boolean(visualizerMode) && crossOrigin && !crossOriginExplicit && !fallback;\n const selectedCrossOrigin = implicitCors ? 'anonymous' : configuredCrossOrigin || undefined;\n // Explicit no-CORS loading is respected, but a cross-origin element loaded that\n // way cannot safely be connected to Web Audio without risking silent output.\n const visualize = Boolean(visualizerMode) && !fallback && !(crossOrigin && !selectedCrossOrigin);\n return {\n ...(selectedCrossOrigin ? { crossOrigin: selectedCrossOrigin } : {}),\n implicitCors,\n visualize,\n };\n};\n\nexport const applyMediaSource = (\n media: HTMLMediaElement,\n sourceUrl: string,\n crossOrigin?: 'anonymous' | 'use-credentials',\n): void => {\n if (crossOrigin) media.crossOrigin = crossOrigin;\n else media.removeAttribute('crossorigin');\n media.src = sourceUrl;\n};\n"],"mappings":"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","names":[]}