@ttt-productions/media-viewer 0.10.18 → 0.10.19

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 (60) hide show
  1. package/dist/media-duration.d.ts +1 -14
  2. package/dist/media-duration.d.ts.map +1 -1
  3. package/dist/media-duration.js +3 -18
  4. package/dist/media-duration.js.map +1 -1
  5. package/dist/react/audio-player-chrome.d.ts +0 -3
  6. package/dist/react/audio-player-chrome.d.ts.map +1 -1
  7. package/dist/react/audio-player-chrome.js +7 -41
  8. package/dist/react/audio-player-chrome.js.map +1 -1
  9. package/dist/react/audio-player.d.ts.map +1 -1
  10. package/dist/react/audio-player.js +0 -2
  11. package/dist/react/audio-player.js.map +1 -1
  12. package/dist/react/audio-viewer.d.ts.map +1 -1
  13. package/dist/react/audio-viewer.js +2 -12
  14. package/dist/react/audio-viewer.js.map +1 -1
  15. package/dist/react/fallback.d.ts +0 -3
  16. package/dist/react/fallback.d.ts.map +1 -1
  17. package/dist/react/fallback.js +0 -3
  18. package/dist/react/fallback.js.map +1 -1
  19. package/dist/react/image-viewer.d.ts.map +1 -1
  20. package/dist/react/image-viewer.js +0 -7
  21. package/dist/react/image-viewer.js.map +1 -1
  22. package/dist/react/index.d.ts +2 -2
  23. package/dist/react/index.d.ts.map +1 -1
  24. package/dist/react/index.js +1 -1
  25. package/dist/react/index.js.map +1 -1
  26. package/dist/react/media-viewer.d.ts +1 -3
  27. package/dist/react/media-viewer.d.ts.map +1 -1
  28. package/dist/react/media-viewer.js +1 -27
  29. package/dist/react/media-viewer.js.map +1 -1
  30. package/dist/react/types.d.ts +2 -106
  31. package/dist/react/types.d.ts.map +1 -1
  32. package/dist/react/types.js.map +1 -1
  33. package/dist/react/use-load-watchdog.d.ts +1 -15
  34. package/dist/react/use-load-watchdog.d.ts.map +1 -1
  35. package/dist/react/use-load-watchdog.js +1 -15
  36. package/dist/react/use-load-watchdog.js.map +1 -1
  37. package/dist/react/use-media-playback.d.ts +2 -67
  38. package/dist/react/use-media-playback.d.ts.map +1 -1
  39. package/dist/react/use-media-playback.js +9 -155
  40. package/dist/react/use-media-playback.js.map +1 -1
  41. package/dist/react/use-media-recovery.d.ts +0 -32
  42. package/dist/react/use-media-recovery.d.ts.map +1 -1
  43. package/dist/react/use-media-recovery.js +2 -64
  44. package/dist/react/use-media-recovery.js.map +1 -1
  45. package/dist/react/video-viewer.d.ts.map +1 -1
  46. package/dist/react/video-viewer.js +3 -20
  47. package/dist/react/video-viewer.js.map +1 -1
  48. package/dist/recovery.d.ts +0 -66
  49. package/dist/recovery.d.ts.map +1 -1
  50. package/dist/recovery.js +0 -37
  51. package/dist/recovery.js.map +1 -1
  52. package/dist/types.d.ts +0 -13
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js.map +1 -1
  55. package/dist/visualizer.d.ts +0 -15
  56. package/dist/visualizer.d.ts.map +1 -1
  57. package/dist/visualizer.js +1 -24
  58. package/dist/visualizer.js.map +1 -1
  59. package/package.json +3 -3
  60. package/src/styles/media-viewer.css +10 -11
@@ -1,16 +1,3 @@
1
- /**
2
- * Chromium MediaRecorder blobs (webm/opus audio, webm video) report
3
- * `duration: Infinity` (or NaN) at `loadedmetadata` — the webm header carries
4
- * no duration until the engine has scanned the stream. The standard workaround:
5
- * seek far past the end; the engine then computes the real duration and fires
6
- * `durationchange` with a finite value.
7
- *
8
- * Resolves the element's REAL finite duration in seconds, or `null` when it
9
- * cannot be determined within `timeoutMs` — callers decide their own fallback
10
- * (the file-input duration validator fails OPEN on null; the audio player
11
- * chrome keeps its placeholder). When the workaround seek actually ran,
12
- * `currentTime` is restored to 0 before resolving, so a paused just-mounted
13
- * preview is left visually untouched.
14
- */
1
+ /** Chromium MediaRecorder blobs report `duration: Infinity` (or NaN) at `loadedmetadata`; seeking far past the end makes the engine compute the real duration. */
15
2
  export declare function resolveInfiniteDuration(el: HTMLMediaElement, timeoutMs?: number): Promise<number | null>;
16
3
  //# sourceMappingURL=media-duration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media-duration.d.ts","sourceRoot":"","sources":["../src/media-duration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,gBAAgB,EACpB,SAAS,SAAO,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2BxB"}
1
+ {"version":3,"file":"media-duration.d.ts","sourceRoot":"","sources":["../src/media-duration.ts"],"names":[],"mappings":"AAAA,kKAAkK;AAClK,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,gBAAgB,EACpB,SAAS,SAAO,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyBxB"}
@@ -1,17 +1,4 @@
1
- /**
2
- * Chromium MediaRecorder blobs (webm/opus audio, webm video) report
3
- * `duration: Infinity` (or NaN) at `loadedmetadata` — the webm header carries
4
- * no duration until the engine has scanned the stream. The standard workaround:
5
- * seek far past the end; the engine then computes the real duration and fires
6
- * `durationchange` with a finite value.
7
- *
8
- * Resolves the element's REAL finite duration in seconds, or `null` when it
9
- * cannot be determined within `timeoutMs` — callers decide their own fallback
10
- * (the file-input duration validator fails OPEN on null; the audio player
11
- * chrome keeps its placeholder). When the workaround seek actually ran,
12
- * `currentTime` is restored to 0 before resolving, so a paused just-mounted
13
- * preview is left visually untouched.
14
- */
1
+ /** Chromium MediaRecorder blobs report `duration: Infinity` (or NaN) at `loadedmetadata`; seeking far past the end makes the engine compute the real duration. */
15
2
  export function resolveInfiniteDuration(el, timeoutMs = 3000) {
16
3
  if (Number.isFinite(el.duration))
17
4
  return Promise.resolve(el.duration);
@@ -24,11 +11,9 @@ export function resolveInfiniteDuration(el, timeoutMs = 3000) {
24
11
  el.removeEventListener('durationchange', onDurationChange);
25
12
  clearTimeout(timer);
26
13
  try {
27
- el.currentTime = 0; // undo the workaround seek
28
- }
29
- catch {
30
- // detached/errored element — the resolved value still stands
14
+ el.currentTime = 0;
31
15
  }
16
+ catch { }
32
17
  resolve(value);
33
18
  };
34
19
  const onDurationChange = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"media-duration.js","sourceRoot":"","sources":["../src/media-duration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAoB,EACpB,SAAS,GAAG,IAAI;IAEhB,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IACtE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,CAAC,KAAoB,EAAE,EAAE;YACtC,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,EAAE,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;YAC3D,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC;gBACH,EAAE,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,2BAA2B;YACjD,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC;QACF,MAAM,gBAAgB,GAAG,GAAG,EAAE;YAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC;gBAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QACxD,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;QACxD,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QACxD,IAAI,CAAC;YACH,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Chromium MediaRecorder blobs (webm/opus audio, webm video) report\n * `duration: Infinity` (or NaN) at `loadedmetadata` — the webm header carries\n * no duration until the engine has scanned the stream. The standard workaround:\n * seek far past the end; the engine then computes the real duration and fires\n * `durationchange` with a finite value.\n *\n * Resolves the element's REAL finite duration in seconds, or `null` when it\n * cannot be determined within `timeoutMs` — callers decide their own fallback\n * (the file-input duration validator fails OPEN on null; the audio player\n * chrome keeps its placeholder). When the workaround seek actually ran,\n * `currentTime` is restored to 0 before resolving, so a paused just-mounted\n * preview is left visually untouched.\n */\nexport function resolveInfiniteDuration(\n el: HTMLMediaElement,\n timeoutMs = 3000,\n): Promise<number | null> {\n if (Number.isFinite(el.duration)) return Promise.resolve(el.duration);\n return new Promise((resolve) => {\n let done = false;\n const finish = (value: number | null) => {\n if (done) return;\n done = true;\n el.removeEventListener('durationchange', onDurationChange);\n clearTimeout(timer);\n try {\n el.currentTime = 0; // undo the workaround seek\n } catch {\n // detached/errored element — the resolved value still stands\n }\n resolve(value);\n };\n const onDurationChange = () => {\n if (Number.isFinite(el.duration)) finish(el.duration);\n };\n const timer = setTimeout(() => finish(null), timeoutMs);\n el.addEventListener('durationchange', onDurationChange);\n try {\n el.currentTime = Number.MAX_SAFE_INTEGER;\n } catch {\n finish(null);\n }\n });\n}\n"]}
1
+ {"version":3,"file":"media-duration.js","sourceRoot":"","sources":["../src/media-duration.ts"],"names":[],"mappings":"AAAA,kKAAkK;AAClK,MAAM,UAAU,uBAAuB,CACrC,EAAoB,EACpB,SAAS,GAAG,IAAI;IAEhB,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IACtE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,CAAC,KAAoB,EAAE,EAAE;YACtC,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,EAAE,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;YAC3D,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC;gBACH,EAAE,CAAC,WAAW,GAAG,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC;QACF,MAAM,gBAAgB,GAAG,GAAG,EAAE;YAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC;gBAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QACxD,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;QACxD,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QACxD,IAAI,CAAC;YACH,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/** Chromium MediaRecorder blobs report `duration: Infinity` (or NaN) at `loadedmetadata`; seeking far past the end makes the engine compute the real duration. */\nexport function resolveInfiniteDuration(\n el: HTMLMediaElement,\n timeoutMs = 3000,\n): Promise<number | null> {\n if (Number.isFinite(el.duration)) return Promise.resolve(el.duration);\n return new Promise((resolve) => {\n let done = false;\n const finish = (value: number | null) => {\n if (done) return;\n done = true;\n el.removeEventListener('durationchange', onDurationChange);\n clearTimeout(timer);\n try {\n el.currentTime = 0;\n } catch {}\n resolve(value);\n };\n const onDurationChange = () => {\n if (Number.isFinite(el.duration)) finish(el.duration);\n };\n const timer = setTimeout(() => finish(null), timeoutMs);\n el.addEventListener('durationchange', onDurationChange);\n try {\n el.currentTime = Number.MAX_SAFE_INTEGER;\n } catch {\n finish(null);\n }\n });\n}\n"]}
@@ -2,11 +2,8 @@ import * as React from 'react';
2
2
  import type { AudioVisualizerMode } from '../visualizer.js';
3
3
  export type AudioPlayerChromeProps = {
4
4
  audioRef: React.RefObject<HTMLAudioElement | null>;
5
- /** Initial visualizer mode; a persisted user choice wins over this. */
6
5
  visualizerMode?: AudioVisualizerMode;
7
- /** localStorage key base for minimize/mode prefs; `null` disables persistence. */
8
6
  persistKey?: string | null;
9
- /** App-injected action buttons, rendered at the end of the control row. */
10
7
  extraActions?: React.ReactNode;
11
8
  };
12
9
  export declare function AudioPlayerChrome({ audioRef, visualizerMode, persistKey, extraActions, }: AudioPlayerChromeProps): React.JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"audio-player-chrome.d.ts","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA+C5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnD,uEAAuE;IACvE,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAChC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,cAAuB,EACvB,UAAgC,EAChC,YAAY,GACb,EAAE,sBAAsB,qBAqVxB"}
1
+ {"version":3,"file":"audio-player-chrome.d.ts","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAyC5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnD,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAChC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,cAAuB,EACvB,UAAgC,EAChC,YAAY,GACb,EAAE,sBAAsB,qBAqUxB"}
@@ -1,20 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- // Custom audio player chrome — the one owned control surface used by every
4
- // AudioViewer. Owns: play/pause, seek, time readout, mute/volume, and
5
- // the visualizer panel (shared engine in ../visualizer.ts) with minimize and
6
- // line/bars mode toggles. The media ELEMENT (loading, errors, playback API)
7
- // stays owned by AudioViewer — this component only reads/drives it.
8
- //
9
- // Web Audio wiring is created lazily on the first `play` event (autoplay
10
- // policies suspend fresh AudioContexts; a user-gesture play resumes them) and
11
- // is best-effort throughout: if anything throws, the player still plays and
12
- // only the visualizer goes dark. `createMediaElementSource` is once-per-
13
- // element, so the graph is keyed to the element instance and rebuilt only if
14
- // the element itself changes. The graph is built only for audio Web Audio can
15
- // hear (`graphCanHear`): a cross-origin file fetched without CORS reaches the
16
- // graph as silence, and once routed the element's own output is that silence,
17
- // so such a file plays straight to the speakers with the visualizer idle.
3
+ // Autoplay policies suspend a fresh AudioContext until a user-gesture play resumes it.
4
+ // `createMediaElementSource` is once per element, so the graph is keyed to the element instance.
5
+ // A cross-origin file fetched without CORS reaches the graph, and once routed the speakers, as silence.
18
6
  import * as React from 'react';
19
7
  import { Pause, Play, Volume2, VolumeX, ChevronDown, ChevronUp, Activity, BarChart3, } from 'lucide-react';
20
8
  import { Spinner } from '@ttt-productions/ui-core/react';
@@ -37,14 +25,8 @@ function writePref(persistKey, suffix, value) {
37
25
  try {
38
26
  window.localStorage.setItem(`${persistKey}:${suffix}`, value);
39
27
  }
40
- catch {
41
- /* storage unavailable (private mode, SSR) — session-only prefs */
42
- }
28
+ catch { }
43
29
  }
44
- /**
45
- * Whether Web Audio can hear the element's current file: one read with CORS (`crossOrigin` set),
46
- * a local `blob:` / `data:` URL, or a same-origin file.
47
- */
48
30
  function graphCanHear(el) {
49
31
  if (el.crossOrigin)
50
32
  return true;
@@ -69,7 +51,6 @@ function formatTime(seconds) {
69
51
  }
70
52
  export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKey = DEFAULT_PERSIST_KEY, extraActions, }) {
71
53
  const [isPlaying, setIsPlaying] = React.useState(false);
72
- // Playback is waiting on data (initial buffer, a stall, or a seek landing).
73
54
  const [buffering, setBuffering] = React.useState(false);
74
55
  const [currentTime, setCurrentTime] = React.useState(0);
75
56
  const [duration, setDuration] = React.useState(0);
@@ -80,12 +61,10 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
80
61
  const stored = readPref(persistKey, 'mode');
81
62
  return stored === 'line' || stored === 'bars' ? stored : visualizerMode;
82
63
  });
83
- // While the user drags the seek slider, timeupdate must not fight the thumb.
84
64
  const [scrubTime, setScrubTime] = React.useState(null);
85
65
  const canvasRef = React.useRef(null);
86
66
  const modeRef = React.useRef(mode);
87
67
  modeRef.current = mode;
88
- // Web Audio graph (lazy, keyed to the element instance).
89
68
  const audioCtxRef = React.useRef(null);
90
69
  const sourceElRef = React.useRef(null);
91
70
  const analyserRef = React.useRef(null);
@@ -123,8 +102,7 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
123
102
  const source = ctx.createMediaElementSource(el);
124
103
  const analyser = ctx.createAnalyser();
125
104
  analyser.fftSize = 256;
126
- // Element audio now routes through the graph — destination connection
127
- // is REQUIRED or playback goes silent.
105
+ // Once routed through the graph, the element is silent unless the graph reaches the destination.
128
106
  source.connect(analyser);
129
107
  analyser.connect(ctx.destination);
130
108
  audioCtxRef.current = ctx;
@@ -135,7 +113,6 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
135
113
  }
136
114
  }
137
115
  catch {
138
- // Best-effort: playback works without the visualizer.
139
116
  analyserRef.current = null;
140
117
  }
141
118
  }, [teardownGraph]);
@@ -151,18 +128,11 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
151
128
  mode: () => modeRef.current,
152
129
  });
153
130
  }, []);
154
- // Element event subscriptions. The element mounts in the same commit as this
155
- // chrome (AudioViewer renders both once loading starts), so the ref is set
156
- // by the time this effect runs.
131
+ // The element mounts in the same commit as this chrome, so the ref is set by the time this effect runs.
157
132
  React.useEffect(() => {
158
133
  const el = audioRef.current;
159
134
  if (!el)
160
135
  return;
161
- // MediaRecorder blobs report duration Infinity until seeked (Chromium webm
162
- // quirk) — without this the readout sticks at "0:01 / 0:00". Learn the real
163
- // duration via the shared seek workaround; paused-only so an actively
164
- // playing element is never seek-jumped, and one attempt per element so a
165
- // workaround-triggered durationchange can never re-enter it.
166
136
  let durationReconcileAttempted = false;
167
137
  const reconcileInfiniteDuration = () => {
168
138
  if (durationReconcileAttempted || Number.isFinite(el.duration) || !el.paused)
@@ -243,9 +213,7 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
243
213
  el.removeEventListener('error', onReady);
244
214
  };
245
215
  }, [audioRef, ensureGraph, startLoop, stopLoop]);
246
- // Full teardown when the chrome unmounts.
247
216
  React.useEffect(() => teardownGraph, [teardownGraph]);
248
- // Idle frame whenever the visualizer is visible but not animating.
249
217
  React.useEffect(() => {
250
218
  if (minimized || isPlaying)
251
219
  return;
@@ -286,9 +254,7 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = 'bars', persistKe
286
254
  try {
287
255
  el.currentTime = scrubTime;
288
256
  }
289
- catch {
290
- /* not seekable yet */
291
- }
257
+ catch { }
292
258
  }
293
259
  setScrubTime(null);
294
260
  };
@@ -1 +1 @@
1
- {"version":3,"file":"audio-player-chrome.js","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\n// Custom audio player chrome — the one owned control surface used by every\n// AudioViewer. Owns: play/pause, seek, time readout, mute/volume, and\n// the visualizer panel (shared engine in ../visualizer.ts) with minimize and\n// line/bars mode toggles. The media ELEMENT (loading, errors, playback API)\n// stays owned by AudioViewer — this component only reads/drives it.\n//\n// Web Audio wiring is created lazily on the first `play` event (autoplay\n// policies suspend fresh AudioContexts; a user-gesture play resumes them) and\n// is best-effort throughout: if anything throws, the player still plays and\n// only the visualizer goes dark. `createMediaElementSource` is once-per-\n// element, so the graph is keyed to the element instance and rebuilt only if\n// the element itself changes. The graph is built only for audio Web Audio can\n// hear (`graphCanHear`): a cross-origin file fetched without CORS reaches the\n// graph as silence, and once routed the element's own output is that silence,\n// so such a file plays straight to the speakers with the visualizer idle.\n\nimport * as React from 'react';\nimport {\n Pause,\n Play,\n Volume2,\n VolumeX,\n ChevronDown,\n ChevronUp,\n Activity,\n BarChart3,\n} from 'lucide-react';\nimport { Spinner } from '@ttt-productions/ui-core/react';\nimport { startWaveformLoop, drawIdleFrame } from '../visualizer.js';\nimport type { AudioVisualizerMode } from '../visualizer.js';\nimport { resolveInfiniteDuration } from '../media-duration.js';\n\nconst DEFAULT_PERSIST_KEY = 'mv-audio-player';\n\nfunction readPref(persistKey: string | null, suffix: string): string | null {\n if (!persistKey) return null;\n try {\n return window.localStorage.getItem(`${persistKey}:${suffix}`);\n } catch {\n return null;\n }\n}\n\nfunction writePref(persistKey: string | null, suffix: string, value: string): void {\n if (!persistKey) return;\n try {\n window.localStorage.setItem(`${persistKey}:${suffix}`, value);\n } catch {\n /* storage unavailable (private mode, SSR) — session-only prefs */\n }\n}\n\n/**\n * Whether Web Audio can hear the element's current file: one read with CORS (`crossOrigin` set),\n * a local `blob:` / `data:` URL, or a same-origin file.\n */\nfunction graphCanHear(el: HTMLAudioElement): boolean {\n if (el.crossOrigin) return true;\n const src = el.currentSrc || el.src;\n if (!src) return false;\n try {\n const u = new URL(src, window.location.href);\n return u.protocol === 'blob:' || u.protocol === 'data:' || u.origin === window.location.origin;\n } catch {\n return false;\n }\n}\n\nfunction formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\n const whole = Math.floor(seconds);\n const m = Math.floor(whole / 60);\n const s = whole % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n}\n\nexport type AudioPlayerChromeProps = {\n audioRef: React.RefObject<HTMLAudioElement | null>;\n /** Initial visualizer mode; a persisted user choice wins over this. */\n visualizerMode?: AudioVisualizerMode;\n /** localStorage key base for minimize/mode prefs; `null` disables persistence. */\n persistKey?: string | null;\n /** App-injected action buttons, rendered at the end of the control row. */\n extraActions?: React.ReactNode;\n};\n\nexport function AudioPlayerChrome({\n audioRef,\n visualizerMode = 'bars',\n persistKey = DEFAULT_PERSIST_KEY,\n extraActions,\n}: AudioPlayerChromeProps) {\n const [isPlaying, setIsPlaying] = React.useState(false);\n // Playback is waiting on data (initial buffer, a stall, or a seek landing).\n const [buffering, setBuffering] = React.useState(false);\n const [currentTime, setCurrentTime] = React.useState(0);\n const [duration, setDuration] = React.useState(0);\n const [isMuted, setIsMuted] = React.useState(false);\n const [volume, setVolume] = React.useState(1);\n const [minimized, setMinimized] = React.useState<boolean>(\n () => readPref(persistKey, 'minimized') === '1',\n );\n const [mode, setMode] = React.useState<AudioVisualizerMode>(() => {\n const stored = readPref(persistKey, 'mode');\n return stored === 'line' || stored === 'bars' ? stored : visualizerMode;\n });\n\n // While the user drags the seek slider, timeupdate must not fight the thumb.\n const [scrubTime, setScrubTime] = React.useState<number | null>(null);\n\n const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n const modeRef = React.useRef(mode);\n modeRef.current = mode;\n\n // Web Audio graph (lazy, keyed to the element instance).\n const audioCtxRef = React.useRef<AudioContext | null>(null);\n const sourceElRef = React.useRef<HTMLAudioElement | null>(null);\n const analyserRef = React.useRef<AnalyserNode | null>(null);\n const stopLoopRef = React.useRef<(() => void) | null>(null);\n\n const stopLoop = React.useCallback(() => {\n stopLoopRef.current?.();\n stopLoopRef.current = null;\n }, []);\n\n const teardownGraph = React.useCallback(() => {\n stopLoop();\n analyserRef.current = null;\n sourceElRef.current = null;\n const ctx = audioCtxRef.current;\n audioCtxRef.current = null;\n if (ctx && ctx.state !== 'closed') {\n ctx.close().catch(() => {});\n }\n }, [stopLoop]);\n\n const ensureGraph = React.useCallback(\n (el: HTMLAudioElement) => {\n try {\n if (sourceElRef.current === el && audioCtxRef.current) {\n if (audioCtxRef.current.state === 'suspended') {\n audioCtxRef.current.resume().catch(() => {});\n }\n return;\n }\n if (!graphCanHear(el)) return;\n teardownGraph();\n const AudioCtx: typeof AudioContext =\n (window as unknown as { AudioContext?: typeof AudioContext }).AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext!;\n if (!AudioCtx) return;\n const ctx = new AudioCtx();\n const source = ctx.createMediaElementSource(el);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n // Element audio now routes through the graph — destination connection\n // is REQUIRED or playback goes silent.\n source.connect(analyser);\n analyser.connect(ctx.destination);\n audioCtxRef.current = ctx;\n sourceElRef.current = el;\n analyserRef.current = analyser;\n if (ctx.state === 'suspended') {\n ctx.resume().catch(() => {});\n }\n } catch {\n // Best-effort: playback works without the visualizer.\n analyserRef.current = null;\n }\n },\n [teardownGraph],\n );\n\n const startLoop = React.useCallback(() => {\n if (stopLoopRef.current) return;\n const analyser = analyserRef.current;\n if (!analyser) return;\n stopLoopRef.current = startWaveformLoop({\n analyser,\n getCanvas: () => canvasRef.current,\n mode: () => modeRef.current,\n });\n }, []);\n\n // Element event subscriptions. The element mounts in the same commit as this\n // chrome (AudioViewer renders both once loading starts), so the ref is set\n // by the time this effect runs.\n React.useEffect(() => {\n const el = audioRef.current;\n if (!el) return;\n\n // MediaRecorder blobs report duration Infinity until seeked (Chromium webm\n // quirk) — without this the readout sticks at \"0:01 / 0:00\". Learn the real\n // duration via the shared seek workaround; paused-only so an actively\n // playing element is never seek-jumped, and one attempt per element so a\n // workaround-triggered durationchange can never re-enter it.\n let durationReconcileAttempted = false;\n const reconcileInfiniteDuration = () => {\n if (durationReconcileAttempted || Number.isFinite(el.duration) || !el.paused) return;\n durationReconcileAttempted = true;\n void resolveInfiniteDuration(el).then((real) => {\n if (real !== null) setDuration(real);\n });\n };\n\n const syncFromElement = () => {\n setIsPlaying(!el.paused && !el.ended);\n setIsMuted(el.muted);\n setVolume(el.volume);\n if (Number.isFinite(el.duration)) setDuration(el.duration);\n setCurrentTime(el.currentTime);\n };\n syncFromElement();\n if (el.readyState >= 1) reconcileInfiniteDuration();\n\n const onPlay = () => {\n setIsPlaying(true);\n ensureGraph(el);\n startLoop();\n };\n const onPause = () => {\n setIsPlaying(false);\n stopLoop();\n };\n const onEnded = () => {\n setIsPlaying(false);\n stopLoop();\n };\n const onTimeUpdate = () => setCurrentTime(el.currentTime);\n const onDurationChange = () => {\n if (Number.isFinite(el.duration)) setDuration(el.duration);\n else reconcileInfiniteDuration();\n };\n const onVolumeChange = () => {\n setIsMuted(el.muted);\n setVolume(el.volume);\n };\n const onWaiting = () => setBuffering(true);\n const onReady = () => setBuffering(false);\n\n el.addEventListener('play', onPlay);\n el.addEventListener('pause', onPause);\n el.addEventListener('ended', onEnded);\n el.addEventListener('timeupdate', onTimeUpdate);\n el.addEventListener('durationchange', onDurationChange);\n el.addEventListener('loadedmetadata', onDurationChange);\n el.addEventListener('volumechange', onVolumeChange);\n el.addEventListener('waiting', onWaiting);\n el.addEventListener('seeking', onWaiting);\n el.addEventListener('playing', onReady);\n el.addEventListener('seeked', onReady);\n el.addEventListener('canplay', onReady);\n el.addEventListener('pause', onReady);\n el.addEventListener('ended', onReady);\n el.addEventListener('error', onReady);\n return () => {\n el.removeEventListener('play', onPlay);\n el.removeEventListener('pause', onPause);\n el.removeEventListener('ended', onEnded);\n el.removeEventListener('timeupdate', onTimeUpdate);\n el.removeEventListener('durationchange', onDurationChange);\n el.removeEventListener('loadedmetadata', onDurationChange);\n el.removeEventListener('volumechange', onVolumeChange);\n el.removeEventListener('waiting', onWaiting);\n el.removeEventListener('seeking', onWaiting);\n el.removeEventListener('playing', onReady);\n el.removeEventListener('seeked', onReady);\n el.removeEventListener('canplay', onReady);\n el.removeEventListener('pause', onReady);\n el.removeEventListener('ended', onReady);\n el.removeEventListener('error', onReady);\n };\n }, [audioRef, ensureGraph, startLoop, stopLoop]);\n\n // Full teardown when the chrome unmounts.\n React.useEffect(() => teardownGraph, [teardownGraph]);\n\n // Idle frame whenever the visualizer is visible but not animating.\n React.useEffect(() => {\n if (minimized || isPlaying) return;\n const canvas = canvasRef.current;\n if (canvas) drawIdleFrame(canvas, mode);\n }, [minimized, isPlaying, mode]);\n\n const togglePlay = () => {\n const el = audioRef.current;\n if (!el) return;\n if (el.paused) void el.play().catch(() => {});\n else el.pause();\n };\n\n const toggleMute = () => {\n const el = audioRef.current;\n if (!el) return;\n el.muted = !el.muted;\n };\n\n const onVolumeInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const el = audioRef.current;\n if (!el) return;\n const v = Number(e.target.value);\n el.volume = v;\n if (v > 0 && el.muted) el.muted = false;\n };\n\n const onSeekInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n setScrubTime(Number(e.target.value));\n };\n\n const commitSeek = () => {\n const el = audioRef.current;\n if (el && scrubTime !== null) {\n try {\n el.currentTime = scrubTime;\n } catch {\n /* not seekable yet */\n }\n }\n setScrubTime(null);\n };\n\n const toggleMinimized = () => {\n setMinimized((m) => {\n writePref(persistKey, 'minimized', m ? '0' : '1');\n return !m;\n });\n };\n\n const toggleMode = () => {\n setMode((prev) => {\n const next: AudioVisualizerMode = prev === 'line' ? 'bars' : 'line';\n writePref(persistKey, 'mode', next);\n return next;\n });\n };\n\n const shownTime = scrubTime ?? currentTime;\n\n return (\n <div\n className=\"mv-player\"\n data-mv-player\n data-minimized={minimized ? 'true' : 'false'}\n data-mode={mode}\n >\n {!minimized && (\n <div className=\"mv-player-viz\">\n <canvas\n ref={canvasRef}\n width={600}\n height={96}\n className=\"mv-player-canvas\"\n aria-hidden=\"true\"\n />\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-mode-btn\"\n onClick={toggleMode}\n aria-label={\n mode === 'line' ? 'Switch visualizer to bars' : 'Switch visualizer to waveform line'\n }\n >\n {mode === 'line' ? <BarChart3 aria-hidden=\"true\" /> : <Activity aria-hidden=\"true\" />}\n </button>\n </div>\n )}\n <div className=\"mv-player-controls\">\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-play-btn\"\n onClick={togglePlay}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n aria-busy={isPlaying && buffering ? true : undefined}\n >\n {isPlaying && buffering ? (\n <Spinner size=\"xs\" />\n ) : isPlaying ? (\n <Pause aria-hidden=\"true\" />\n ) : (\n <Play aria-hidden=\"true\" />\n )}\n </button>\n <span className=\"mv-player-time\" aria-hidden=\"true\">\n {formatTime(shownTime)}\n </span>\n <input\n type=\"range\"\n className=\"mv-player-seek\"\n min={0}\n max={duration > 0 ? duration : 0}\n step={0.1}\n value={Math.min(shownTime, duration > 0 ? duration : shownTime)}\n onChange={onSeekInput}\n onMouseUp={commitSeek}\n onTouchEnd={commitSeek}\n onKeyUp={commitSeek}\n disabled={duration <= 0}\n aria-label=\"Seek\"\n />\n <span className=\"mv-player-time\" aria-hidden=\"true\">\n {formatTime(duration)}\n </span>\n <button\n type=\"button\"\n className=\"mv-player-btn\"\n onClick={toggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n >\n {isMuted ? <VolumeX aria-hidden=\"true\" /> : <Volume2 aria-hidden=\"true\" />}\n </button>\n <input\n type=\"range\"\n className=\"mv-player-volume\"\n min={0}\n max={1}\n step={0.05}\n value={isMuted ? 0 : volume}\n onChange={onVolumeInput}\n aria-label=\"Volume\"\n />\n <button\n type=\"button\"\n className=\"mv-player-btn\"\n onClick={toggleMinimized}\n aria-label={minimized ? 'Show visualizer' : 'Hide visualizer'}\n >\n {minimized ? <ChevronUp aria-hidden=\"true\" /> : <ChevronDown aria-hidden=\"true\" />}\n </button>\n {extraActions != null && <div className=\"mv-player-extra\">{extraActions}</div>}\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"audio-player-chrome.js","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\n// Autoplay policies suspend a fresh AudioContext until a user-gesture play resumes it.\n// `createMediaElementSource` is once per element, so the graph is keyed to the element instance.\n// A cross-origin file fetched without CORS reaches the graph, and once routed the speakers, as silence.\n\nimport * as React from 'react';\nimport {\n Pause,\n Play,\n Volume2,\n VolumeX,\n ChevronDown,\n ChevronUp,\n Activity,\n BarChart3,\n} from 'lucide-react';\nimport { Spinner } from '@ttt-productions/ui-core/react';\nimport { startWaveformLoop, drawIdleFrame } from '../visualizer.js';\nimport type { AudioVisualizerMode } from '../visualizer.js';\nimport { resolveInfiniteDuration } from '../media-duration.js';\n\nconst DEFAULT_PERSIST_KEY = 'mv-audio-player';\n\nfunction readPref(persistKey: string | null, suffix: string): string | null {\n if (!persistKey) return null;\n try {\n return window.localStorage.getItem(`${persistKey}:${suffix}`);\n } catch {\n return null;\n }\n}\n\nfunction writePref(persistKey: string | null, suffix: string, value: string): void {\n if (!persistKey) return;\n try {\n window.localStorage.setItem(`${persistKey}:${suffix}`, value);\n } catch {}\n}\n\nfunction graphCanHear(el: HTMLAudioElement): boolean {\n if (el.crossOrigin) return true;\n const src = el.currentSrc || el.src;\n if (!src) return false;\n try {\n const u = new URL(src, window.location.href);\n return u.protocol === 'blob:' || u.protocol === 'data:' || u.origin === window.location.origin;\n } catch {\n return false;\n }\n}\n\nfunction formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\n const whole = Math.floor(seconds);\n const m = Math.floor(whole / 60);\n const s = whole % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n}\n\nexport type AudioPlayerChromeProps = {\n audioRef: React.RefObject<HTMLAudioElement | null>;\n visualizerMode?: AudioVisualizerMode;\n persistKey?: string | null;\n extraActions?: React.ReactNode;\n};\n\nexport function AudioPlayerChrome({\n audioRef,\n visualizerMode = 'bars',\n persistKey = DEFAULT_PERSIST_KEY,\n extraActions,\n}: AudioPlayerChromeProps) {\n const [isPlaying, setIsPlaying] = React.useState(false);\n const [buffering, setBuffering] = React.useState(false);\n const [currentTime, setCurrentTime] = React.useState(0);\n const [duration, setDuration] = React.useState(0);\n const [isMuted, setIsMuted] = React.useState(false);\n const [volume, setVolume] = React.useState(1);\n const [minimized, setMinimized] = React.useState<boolean>(\n () => readPref(persistKey, 'minimized') === '1',\n );\n const [mode, setMode] = React.useState<AudioVisualizerMode>(() => {\n const stored = readPref(persistKey, 'mode');\n return stored === 'line' || stored === 'bars' ? stored : visualizerMode;\n });\n\n const [scrubTime, setScrubTime] = React.useState<number | null>(null);\n\n const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n const modeRef = React.useRef(mode);\n modeRef.current = mode;\n\n const audioCtxRef = React.useRef<AudioContext | null>(null);\n const sourceElRef = React.useRef<HTMLAudioElement | null>(null);\n const analyserRef = React.useRef<AnalyserNode | null>(null);\n const stopLoopRef = React.useRef<(() => void) | null>(null);\n\n const stopLoop = React.useCallback(() => {\n stopLoopRef.current?.();\n stopLoopRef.current = null;\n }, []);\n\n const teardownGraph = React.useCallback(() => {\n stopLoop();\n analyserRef.current = null;\n sourceElRef.current = null;\n const ctx = audioCtxRef.current;\n audioCtxRef.current = null;\n if (ctx && ctx.state !== 'closed') {\n ctx.close().catch(() => {});\n }\n }, [stopLoop]);\n\n const ensureGraph = React.useCallback(\n (el: HTMLAudioElement) => {\n try {\n if (sourceElRef.current === el && audioCtxRef.current) {\n if (audioCtxRef.current.state === 'suspended') {\n audioCtxRef.current.resume().catch(() => {});\n }\n return;\n }\n if (!graphCanHear(el)) return;\n teardownGraph();\n const AudioCtx: typeof AudioContext =\n (window as unknown as { AudioContext?: typeof AudioContext }).AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext!;\n if (!AudioCtx) return;\n const ctx = new AudioCtx();\n const source = ctx.createMediaElementSource(el);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n // Once routed through the graph, the element is silent unless the graph reaches the destination.\n source.connect(analyser);\n analyser.connect(ctx.destination);\n audioCtxRef.current = ctx;\n sourceElRef.current = el;\n analyserRef.current = analyser;\n if (ctx.state === 'suspended') {\n ctx.resume().catch(() => {});\n }\n } catch {\n analyserRef.current = null;\n }\n },\n [teardownGraph],\n );\n\n const startLoop = React.useCallback(() => {\n if (stopLoopRef.current) return;\n const analyser = analyserRef.current;\n if (!analyser) return;\n stopLoopRef.current = startWaveformLoop({\n analyser,\n getCanvas: () => canvasRef.current,\n mode: () => modeRef.current,\n });\n }, []);\n\n // The element mounts in the same commit as this chrome, so the ref is set by the time this effect runs.\n React.useEffect(() => {\n const el = audioRef.current;\n if (!el) return;\n\n let durationReconcileAttempted = false;\n const reconcileInfiniteDuration = () => {\n if (durationReconcileAttempted || Number.isFinite(el.duration) || !el.paused) return;\n durationReconcileAttempted = true;\n void resolveInfiniteDuration(el).then((real) => {\n if (real !== null) setDuration(real);\n });\n };\n\n const syncFromElement = () => {\n setIsPlaying(!el.paused && !el.ended);\n setIsMuted(el.muted);\n setVolume(el.volume);\n if (Number.isFinite(el.duration)) setDuration(el.duration);\n setCurrentTime(el.currentTime);\n };\n syncFromElement();\n if (el.readyState >= 1) reconcileInfiniteDuration();\n\n const onPlay = () => {\n setIsPlaying(true);\n ensureGraph(el);\n startLoop();\n };\n const onPause = () => {\n setIsPlaying(false);\n stopLoop();\n };\n const onEnded = () => {\n setIsPlaying(false);\n stopLoop();\n };\n const onTimeUpdate = () => setCurrentTime(el.currentTime);\n const onDurationChange = () => {\n if (Number.isFinite(el.duration)) setDuration(el.duration);\n else reconcileInfiniteDuration();\n };\n const onVolumeChange = () => {\n setIsMuted(el.muted);\n setVolume(el.volume);\n };\n const onWaiting = () => setBuffering(true);\n const onReady = () => setBuffering(false);\n\n el.addEventListener('play', onPlay);\n el.addEventListener('pause', onPause);\n el.addEventListener('ended', onEnded);\n el.addEventListener('timeupdate', onTimeUpdate);\n el.addEventListener('durationchange', onDurationChange);\n el.addEventListener('loadedmetadata', onDurationChange);\n el.addEventListener('volumechange', onVolumeChange);\n el.addEventListener('waiting', onWaiting);\n el.addEventListener('seeking', onWaiting);\n el.addEventListener('playing', onReady);\n el.addEventListener('seeked', onReady);\n el.addEventListener('canplay', onReady);\n el.addEventListener('pause', onReady);\n el.addEventListener('ended', onReady);\n el.addEventListener('error', onReady);\n return () => {\n el.removeEventListener('play', onPlay);\n el.removeEventListener('pause', onPause);\n el.removeEventListener('ended', onEnded);\n el.removeEventListener('timeupdate', onTimeUpdate);\n el.removeEventListener('durationchange', onDurationChange);\n el.removeEventListener('loadedmetadata', onDurationChange);\n el.removeEventListener('volumechange', onVolumeChange);\n el.removeEventListener('waiting', onWaiting);\n el.removeEventListener('seeking', onWaiting);\n el.removeEventListener('playing', onReady);\n el.removeEventListener('seeked', onReady);\n el.removeEventListener('canplay', onReady);\n el.removeEventListener('pause', onReady);\n el.removeEventListener('ended', onReady);\n el.removeEventListener('error', onReady);\n };\n }, [audioRef, ensureGraph, startLoop, stopLoop]);\n\n React.useEffect(() => teardownGraph, [teardownGraph]);\n\n React.useEffect(() => {\n if (minimized || isPlaying) return;\n const canvas = canvasRef.current;\n if (canvas) drawIdleFrame(canvas, mode);\n }, [minimized, isPlaying, mode]);\n\n const togglePlay = () => {\n const el = audioRef.current;\n if (!el) return;\n if (el.paused) void el.play().catch(() => {});\n else el.pause();\n };\n\n const toggleMute = () => {\n const el = audioRef.current;\n if (!el) return;\n el.muted = !el.muted;\n };\n\n const onVolumeInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n const el = audioRef.current;\n if (!el) return;\n const v = Number(e.target.value);\n el.volume = v;\n if (v > 0 && el.muted) el.muted = false;\n };\n\n const onSeekInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n setScrubTime(Number(e.target.value));\n };\n\n const commitSeek = () => {\n const el = audioRef.current;\n if (el && scrubTime !== null) {\n try {\n el.currentTime = scrubTime;\n } catch {}\n }\n setScrubTime(null);\n };\n\n const toggleMinimized = () => {\n setMinimized((m) => {\n writePref(persistKey, 'minimized', m ? '0' : '1');\n return !m;\n });\n };\n\n const toggleMode = () => {\n setMode((prev) => {\n const next: AudioVisualizerMode = prev === 'line' ? 'bars' : 'line';\n writePref(persistKey, 'mode', next);\n return next;\n });\n };\n\n const shownTime = scrubTime ?? currentTime;\n\n return (\n <div\n className=\"mv-player\"\n data-mv-player\n data-minimized={minimized ? 'true' : 'false'}\n data-mode={mode}\n >\n {!minimized && (\n <div className=\"mv-player-viz\">\n <canvas\n ref={canvasRef}\n width={600}\n height={96}\n className=\"mv-player-canvas\"\n aria-hidden=\"true\"\n />\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-mode-btn\"\n onClick={toggleMode}\n aria-label={\n mode === 'line' ? 'Switch visualizer to bars' : 'Switch visualizer to waveform line'\n }\n >\n {mode === 'line' ? <BarChart3 aria-hidden=\"true\" /> : <Activity aria-hidden=\"true\" />}\n </button>\n </div>\n )}\n <div className=\"mv-player-controls\">\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-play-btn\"\n onClick={togglePlay}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n aria-busy={isPlaying && buffering ? true : undefined}\n >\n {isPlaying && buffering ? (\n <Spinner size=\"xs\" />\n ) : isPlaying ? (\n <Pause aria-hidden=\"true\" />\n ) : (\n <Play aria-hidden=\"true\" />\n )}\n </button>\n <span className=\"mv-player-time\" aria-hidden=\"true\">\n {formatTime(shownTime)}\n </span>\n <input\n type=\"range\"\n className=\"mv-player-seek\"\n min={0}\n max={duration > 0 ? duration : 0}\n step={0.1}\n value={Math.min(shownTime, duration > 0 ? duration : shownTime)}\n onChange={onSeekInput}\n onMouseUp={commitSeek}\n onTouchEnd={commitSeek}\n onKeyUp={commitSeek}\n disabled={duration <= 0}\n aria-label=\"Seek\"\n />\n <span className=\"mv-player-time\" aria-hidden=\"true\">\n {formatTime(duration)}\n </span>\n <button\n type=\"button\"\n className=\"mv-player-btn\"\n onClick={toggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n >\n {isMuted ? <VolumeX aria-hidden=\"true\" /> : <Volume2 aria-hidden=\"true\" />}\n </button>\n <input\n type=\"range\"\n className=\"mv-player-volume\"\n min={0}\n max={1}\n step={0.05}\n value={isMuted ? 0 : volume}\n onChange={onVolumeInput}\n aria-label=\"Volume\"\n />\n <button\n type=\"button\"\n className=\"mv-player-btn\"\n onClick={toggleMinimized}\n aria-label={minimized ? 'Show visualizer' : 'Hide visualizer'}\n >\n {minimized ? <ChevronUp aria-hidden=\"true\" /> : <ChevronDown aria-hidden=\"true\" />}\n </button>\n {extraActions != null && <div className=\"mv-player-extra\">{extraActions}</div>}\n </div>\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"audio-player.d.ts","sourceRoot":"","sources":["../../src/react/audio-player.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAElD"}
1
+ {"version":3,"file":"audio-player.d.ts","sourceRoot":"","sources":["../../src/react/audio-player.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAElD"}
@@ -1,7 +1,5 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- // Semantic named entry point for the canonical AudioViewer. Both exports use
4
- // the same package-owned control surface; native browser chrome is unavailable.
5
3
  import { AudioViewer } from './audio-viewer.js';
6
4
  export function AudioPlayer(props) {
7
5
  return _jsx(AudioViewer, { ...props });
@@ -1 +1 @@
1
- {"version":3,"file":"audio-player.js","sourceRoot":"","sources":["../../src/react/audio-player.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,6EAA6E;AAC7E,gFAAgF;AAEhF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,OAAO,KAAC,WAAW,OAAK,KAAK,GAAI,CAAC;AACpC,CAAC","sourcesContent":["'use client';\n\n// Semantic named entry point for the canonical AudioViewer. Both exports use\n// the same package-owned control surface; native browser chrome is unavailable.\n\nimport { AudioViewer } from './audio-viewer.js';\nimport type { AudioViewerProps } from './types.js';\n\nexport type AudioPlayerProps = AudioViewerProps;\n\nexport function AudioPlayer(props: AudioPlayerProps) {\n return <AudioViewer {...props} />;\n}\n"]}
1
+ {"version":3,"file":"audio-player.js","sourceRoot":"","sources":["../../src/react/audio-player.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,OAAO,KAAC,WAAW,OAAK,KAAK,GAAI,CAAC;AACpC,CAAC","sourcesContent":["'use client';\n\nimport { AudioViewer } from './audio-viewer.js';\nimport type { AudioViewerProps } from './types.js';\n\nexport type AudioPlayerProps = AudioViewerProps;\n\nexport function AudioPlayer(props: AudioPlayerProps) {\n return <AudioViewer {...props} />;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"audio-viewer.d.ts","sourceRoot":"","sources":["../../src/react/audio-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,qBA6LlD"}
1
+ {"version":3,"file":"audio-viewer.d.ts","sourceRoot":"","sources":["../../src/react/audio-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,qBAmLlD"}
@@ -12,16 +12,11 @@ export function AudioViewer(props) {
12
12
  const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);
13
13
  const audioRef = React.useRef(null);
14
14
  const loadReportedRef = React.useRef(false);
15
- // Additive playback API — derives from element events (no observer, MEDIA-102).
16
15
  const { hasEnded, handlers: playbackHandlers, attachElement, } = useMediaPlayback(audioRef, { onEnded, onProgressSample, startAtSeconds, endOverlay }, playbackControlsRef, url);
17
- // The element attaches through the playback lifecycle so the hook knows the
18
- // active generation; detach (unmount) retires it.
19
16
  const setAudioRef = React.useCallback((el) => {
20
17
  audioRef.current = el;
21
18
  attachElement(el);
22
19
  }, [attachElement]);
23
- // The viewer's one observer (MEDIA-102): lazy-load gating, and — while an owner listens —
24
- // the visibility it reports, which is why it keeps running after the load starts then.
25
20
  const { ref: inViewRef, inView } = useInView({
26
21
  triggerOnce: false,
27
22
  threshold: 0.01,
@@ -34,7 +29,6 @@ export function AudioViewer(props) {
34
29
  setShouldLoad(true);
35
30
  }
36
31
  }, [inView, shouldLoad]);
37
- // Accordion compatibility: delayed auto-load on mount
38
32
  React.useEffect(() => {
39
33
  if (priority || shouldLoad)
40
34
  return;
@@ -52,9 +46,7 @@ export function AudioViewer(props) {
52
46
  setIsLoaded(false);
53
47
  loadReportedRef.current = false;
54
48
  }, [url]);
55
- // A preload="metadata" element may not fire loadeddata or canplay until play, so the
56
- // first of loadedmetadata / loadeddata / canplay makes it usable: the skeleton goes, the
57
- // player shows, and onLoad fires once for this load.
49
+ // A preload="metadata" element may not fire loadeddata or canplay until play.
58
50
  const reveal = React.useCallback(() => {
59
51
  setIsLoaded(true);
60
52
  if (loadReportedRef.current)
@@ -64,7 +56,7 @@ export function AudioViewer(props) {
64
56
  }, [onLoad]);
65
57
  const handleError = React.useCallback(() => {
66
58
  setHasError(true);
67
- setIsLoaded(true); // Stop skeleton on error
59
+ setIsLoaded(true);
68
60
  onError?.();
69
61
  }, [onError]);
70
62
  const handleLoadedMetadata = React.useCallback((e) => {
@@ -72,8 +64,6 @@ export function AudioViewer(props) {
72
64
  playbackHandlers.onLoadedMetadata(e);
73
65
  }, [reveal, playbackHandlers]);
74
66
  useLoadWatchdog(shouldLoad && !isLoaded && !hasError, loadTimeoutMs, handleError);
75
- // The error state stays inside the observed wrapper, so the viewer keeps reporting
76
- // its visibility while its owner recovers it.
77
67
  if (hasError) {
78
68
  return (_jsx("div", { ref: inViewRef, className: className, style: fallback ? { position: 'relative', width: '100%', height: '100%' } : undefined, children: fallback ?? (_jsx("div", { className: "mv-audio-error", children: _jsx("p", { children: "Failed to load audio" }) })) }));
79
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"audio-viewer.js","sourceRoot":"","sources":["../../src/react/audio-viewer.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\nimport { Skeleton } from '@ttt-productions/ui-core/react';\nimport type { AudioViewerProps } from './types.js';\nimport { useMediaPlayback } from './use-media-playback.js';\nimport { AudioPlayerChrome } from './audio-player-chrome.js';\nimport { useLoadWatchdog } from './use-load-watchdog.js';\n\nexport function AudioViewer(props: AudioViewerProps) {\n const {\n url,\n className,\n mediaClassName,\n autoPlay,\n loop = false,\n lazy = true,\n skeleton: showSkeleton = true,\n priority = false,\n preload = 'metadata',\n onLoad,\n onLoadChange,\n onError,\n onVisibilityChange,\n fallback,\n loadTimeoutMs,\n onEnded,\n onProgressSample,\n startAtSeconds,\n endOverlay,\n playbackControlsRef,\n visualizerMode,\n persistKey,\n extraActions,\n crossOrigin,\n } = props;\n\n const [isLoaded, setIsLoaded] = React.useState(false);\n const [hasError, setHasError] = React.useState(false);\n const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);\n const audioRef = React.useRef<HTMLAudioElement>(null);\n const loadReportedRef = React.useRef(false);\n\n // Additive playback API — derives from element events (no observer, MEDIA-102).\n const {\n hasEnded,\n handlers: playbackHandlers,\n attachElement,\n } = useMediaPlayback(\n audioRef,\n { onEnded, onProgressSample, startAtSeconds, endOverlay },\n playbackControlsRef,\n url,\n );\n\n // The element attaches through the playback lifecycle so the hook knows the\n // active generation; detach (unmount) retires it.\n const setAudioRef = React.useCallback(\n (el: HTMLAudioElement | null) => {\n audioRef.current = el;\n attachElement(el);\n },\n [attachElement],\n );\n\n // The viewer's one observer (MEDIA-102): lazy-load gating, and — while an owner listens —\n // the visibility it reports, which is why it keeps running after the load starts then.\n const { ref: inViewRef, inView } = useInView({\n triggerOnce: false,\n threshold: 0.01,\n skip: priority || (shouldLoad && !onVisibilityChange),\n rootMargin: '100px',\n onChange: (visible) => onVisibilityChange?.(visible),\n });\n\n React.useEffect(() => {\n if (inView && !shouldLoad) {\n setShouldLoad(true);\n }\n }, [inView, shouldLoad]);\n\n // Accordion compatibility: delayed auto-load on mount\n React.useEffect(() => {\n if (priority || shouldLoad) return;\n const timer = setTimeout(() => {\n if (!shouldLoad) setShouldLoad(true);\n }, 100);\n return () => clearTimeout(timer);\n }, [url, priority, shouldLoad]);\n\n React.useEffect(() => {\n onLoadChange?.(!isLoaded);\n }, [isLoaded, onLoadChange]);\n\n React.useEffect(() => {\n setHasError(false);\n setIsLoaded(false);\n loadReportedRef.current = false;\n }, [url]);\n\n // A preload=\"metadata\" element may not fire loadeddata or canplay until play, so the\n // first of loadedmetadata / loadeddata / canplay makes it usable: the skeleton goes, the\n // player shows, and onLoad fires once for this load.\n const reveal = React.useCallback(() => {\n setIsLoaded(true);\n if (loadReportedRef.current) return;\n loadReportedRef.current = true;\n onLoad?.();\n }, [onLoad]);\n\n const handleError = React.useCallback(() => {\n setHasError(true);\n setIsLoaded(true); // Stop skeleton on error\n onError?.();\n }, [onError]);\n\n const handleLoadedMetadata = React.useCallback(\n (e: React.SyntheticEvent<HTMLAudioElement>) => {\n reveal();\n playbackHandlers.onLoadedMetadata(e);\n },\n [reveal, playbackHandlers],\n );\n\n useLoadWatchdog(shouldLoad && !isLoaded && !hasError, loadTimeoutMs, handleError);\n\n // The error state stays inside the observed wrapper, so the viewer keeps reporting\n // its visibility while its owner recovers it.\n if (hasError) {\n return (\n <div\n ref={inViewRef}\n className={className}\n style={fallback ? { position: 'relative', width: '100%', height: '100%' } : undefined}\n >\n {fallback ?? (\n <div className=\"mv-audio-error\">\n <p>Failed to load audio</p>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={inViewRef}\n className={className}\n style={{ position: 'relative', width: '100%', outline: 'none' }}\n >\n {!shouldLoad ? (\n showSkeleton ? (\n <Skeleton style={{ height: '3.5rem', width: '100%' }} />\n ) : null\n ) : (\n <>\n {showSkeleton && !isLoaded && (\n <Skeleton style={{ position: 'absolute', inset: 0, height: '3.5rem', width: '100%' }} />\n )}\n <audio\n ref={setAudioRef}\n src={url}\n crossOrigin={crossOrigin}\n className={mediaClassName}\n controls={false}\n autoPlay={autoPlay}\n loop={loop}\n preload={preload}\n onLoadedData={reveal}\n onCanPlay={reveal}\n onError={handleError}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={playbackHandlers.onTimeUpdate}\n onPlay={playbackHandlers.onPlay}\n onPause={playbackHandlers.onPause}\n onSeeked={playbackHandlers.onSeeked}\n onEnded={playbackHandlers.onEnded}\n style={{\n width: '100%',\n opacity: isLoaded ? 1 : 0,\n transition: 'opacity var(--motion-slow) var(--motion-ease)',\n }}\n />\n <AudioPlayerChrome\n audioRef={audioRef}\n visualizerMode={visualizerMode}\n persistKey={persistKey}\n extraActions={extraActions}\n />\n {endOverlay != null && hasEnded && (\n <div className=\"mv-end-overlay\" style={{ position: 'absolute', inset: 0, zIndex: 2 }}>\n {endOverlay}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"audio-viewer.js","sourceRoot":"","sources":["../../src/react/audio-viewer.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { useInView } from 'react-intersection-observer';\nimport { Skeleton } from '@ttt-productions/ui-core/react';\nimport type { AudioViewerProps } from './types.js';\nimport { useMediaPlayback } from './use-media-playback.js';\nimport { AudioPlayerChrome } from './audio-player-chrome.js';\nimport { useLoadWatchdog } from './use-load-watchdog.js';\n\nexport function AudioViewer(props: AudioViewerProps) {\n const {\n url,\n className,\n mediaClassName,\n autoPlay,\n loop = false,\n lazy = true,\n skeleton: showSkeleton = true,\n priority = false,\n preload = 'metadata',\n onLoad,\n onLoadChange,\n onError,\n onVisibilityChange,\n fallback,\n loadTimeoutMs,\n onEnded,\n onProgressSample,\n startAtSeconds,\n endOverlay,\n playbackControlsRef,\n visualizerMode,\n persistKey,\n extraActions,\n crossOrigin,\n } = props;\n\n const [isLoaded, setIsLoaded] = React.useState(false);\n const [hasError, setHasError] = React.useState(false);\n const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);\n const audioRef = React.useRef<HTMLAudioElement>(null);\n const loadReportedRef = React.useRef(false);\n\n const {\n hasEnded,\n handlers: playbackHandlers,\n attachElement,\n } = useMediaPlayback(\n audioRef,\n { onEnded, onProgressSample, startAtSeconds, endOverlay },\n playbackControlsRef,\n url,\n );\n\n const setAudioRef = React.useCallback(\n (el: HTMLAudioElement | null) => {\n audioRef.current = el;\n attachElement(el);\n },\n [attachElement],\n );\n\n const { ref: inViewRef, inView } = useInView({\n triggerOnce: false,\n threshold: 0.01,\n skip: priority || (shouldLoad && !onVisibilityChange),\n rootMargin: '100px',\n onChange: (visible) => onVisibilityChange?.(visible),\n });\n\n React.useEffect(() => {\n if (inView && !shouldLoad) {\n setShouldLoad(true);\n }\n }, [inView, shouldLoad]);\n\n React.useEffect(() => {\n if (priority || shouldLoad) return;\n const timer = setTimeout(() => {\n if (!shouldLoad) setShouldLoad(true);\n }, 100);\n return () => clearTimeout(timer);\n }, [url, priority, shouldLoad]);\n\n React.useEffect(() => {\n onLoadChange?.(!isLoaded);\n }, [isLoaded, onLoadChange]);\n\n React.useEffect(() => {\n setHasError(false);\n setIsLoaded(false);\n loadReportedRef.current = false;\n }, [url]);\n\n // A preload=\"metadata\" element may not fire loadeddata or canplay until play.\n const reveal = React.useCallback(() => {\n setIsLoaded(true);\n if (loadReportedRef.current) return;\n loadReportedRef.current = true;\n onLoad?.();\n }, [onLoad]);\n\n const handleError = React.useCallback(() => {\n setHasError(true);\n setIsLoaded(true);\n onError?.();\n }, [onError]);\n\n const handleLoadedMetadata = React.useCallback(\n (e: React.SyntheticEvent<HTMLAudioElement>) => {\n reveal();\n playbackHandlers.onLoadedMetadata(e);\n },\n [reveal, playbackHandlers],\n );\n\n useLoadWatchdog(shouldLoad && !isLoaded && !hasError, loadTimeoutMs, handleError);\n\n if (hasError) {\n return (\n <div\n ref={inViewRef}\n className={className}\n style={fallback ? { position: 'relative', width: '100%', height: '100%' } : undefined}\n >\n {fallback ?? (\n <div className=\"mv-audio-error\">\n <p>Failed to load audio</p>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={inViewRef}\n className={className}\n style={{ position: 'relative', width: '100%', outline: 'none' }}\n >\n {!shouldLoad ? (\n showSkeleton ? (\n <Skeleton style={{ height: '3.5rem', width: '100%' }} />\n ) : null\n ) : (\n <>\n {showSkeleton && !isLoaded && (\n <Skeleton style={{ position: 'absolute', inset: 0, height: '3.5rem', width: '100%' }} />\n )}\n <audio\n ref={setAudioRef}\n src={url}\n crossOrigin={crossOrigin}\n className={mediaClassName}\n controls={false}\n autoPlay={autoPlay}\n loop={loop}\n preload={preload}\n onLoadedData={reveal}\n onCanPlay={reveal}\n onError={handleError}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={playbackHandlers.onTimeUpdate}\n onPlay={playbackHandlers.onPlay}\n onPause={playbackHandlers.onPause}\n onSeeked={playbackHandlers.onSeeked}\n onEnded={playbackHandlers.onEnded}\n style={{\n width: '100%',\n opacity: isLoaded ? 1 : 0,\n transition: 'opacity var(--motion-slow) var(--motion-ease)',\n }}\n />\n <AudioPlayerChrome\n audioRef={audioRef}\n visualizerMode={visualizerMode}\n persistKey={persistKey}\n extraActions={extraActions}\n />\n {endOverlay != null && hasEnded && (\n <div className=\"mv-end-overlay\" style={{ position: 'absolute', inset: 0, zIndex: 2 }}>\n {endOverlay}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"]}
@@ -6,17 +6,14 @@ export declare function MediaFallbackLink(props: {
6
6
  label?: string;
7
7
  }): import("react").JSX.Element;
8
8
  export declare function shouldShowFallback(mode: FallbackMode | undefined): boolean;
9
- /** Empty state: no URL provided */
10
9
  export declare function EmptyFallback(props: {
11
10
  isCircular?: boolean;
12
11
  className?: string;
13
12
  }): import("react").JSX.Element;
14
- /** Loading state: the URL is still being prepared (see `urlPending`). */
15
13
  export declare function PendingFallback(props: {
16
14
  isCircular?: boolean;
17
15
  className?: string;
18
16
  }): import("react").JSX.Element;
19
- /** Error state: failed to load */
20
17
  export declare function ErrorFallback(props: {
21
18
  isCircular?: boolean;
22
19
  className?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,+BAcA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAuBhF;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAOlF;AAED,kCAAkC;AAClC,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAwBhF"}
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,+BAcA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAuBhF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAOlF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAwBhF"}
@@ -7,7 +7,6 @@ export function MediaFallbackLink(props) {
7
7
  export function shouldShowFallback(mode) {
8
8
  return mode !== 'none';
9
9
  }
10
- /** Empty state: no URL provided */
11
10
  export function EmptyFallback(props) {
12
11
  const { isCircular, className } = props;
13
12
  if (isCircular) {
@@ -15,12 +14,10 @@ export function EmptyFallback(props) {
15
14
  }
16
15
  return (_jsx("div", { className: className ?? 'mv-empty-fallback', children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }), _jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })] }) }));
17
16
  }
18
- /** Loading state: the URL is still being prepared (see `urlPending`). */
19
17
  export function PendingFallback(props) {
20
18
  const { isCircular, className } = props;
21
19
  return (_jsx("div", { className: className ?? (isCircular ? 'mv-avatar-fallback' : 'mv-pending-fallback'), children: _jsx(Spinner, { size: isCircular ? 'xs' : 'md', label: "Loading media" }) }));
22
20
  }
23
- /** Error state: failed to load */
24
21
  export function ErrorFallback(props) {
25
22
  const { isCircular, className } = props;
26
23
  return (_jsx("div", { className: className ?? 'mv-error-fallback', style: isCircular ? { borderRadius: '50%' } : undefined, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }), _jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }), _jsx("line", { x1: "9", x2: "15", y1: "15", y2: "9" })] }) }));
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAGzD,MAAM,UAAU,iBAAiB,CAAC,KAKjC;IACC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAElD,OAAO,CACL,YACE,IAAI,EAAE,GAAG,EACT,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,QAAQ,EAAE,QAAQ,IAAI,SAAS,EAC/B,SAAS,EAAE,SAAS,YAEnB,KAAK,IAAI,UAAU,GAClB,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,OAAO,IAAI,KAAK,MAAM,CAAC;AACzB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,cAAK,SAAS,EAAE,SAAS,IAAI,oBAAoB,kBAAS,CAAC;IACpE,CAAC;IACD,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,mBAAmB,YAC9C,eACE,KAAK,EAAC,4BAA4B,EAClC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,IAChC,GACF,CACP,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,eAAe,CAAC,KAAmD;IACjF,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,CAAC,YACtF,KAAC,OAAO,IAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAC,eAAe,GAAG,GAC7D,CACP,CAAC;AACJ,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,OAAO,CACL,cACE,SAAS,EAAE,SAAS,IAAI,mBAAmB,EAC3C,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,YAEvD,eACE,KAAK,EAAC,4BAA4B,EAClC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,EACpC,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,GAAG,IAClC,GACF,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { Spinner } from '@ttt-productions/ui-core/react';\nimport type { FallbackMode } from '../types.js';\n\nexport function MediaFallbackLink(props: {\n url: string;\n filename?: string;\n className?: string;\n label?: string;\n}) {\n const { url, filename, className, label } = props;\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n download={filename || undefined}\n className={className}\n >\n {label ?? 'Download'}\n </a>\n );\n}\n\nexport function shouldShowFallback(mode: FallbackMode | undefined): boolean {\n return mode !== 'none';\n}\n\n/** Empty state: no URL provided */\nexport function EmptyFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n if (isCircular) {\n return <div className={className ?? 'mv-avatar-fallback'}>?</div>;\n }\n return (\n <div className={className ?? 'mv-empty-fallback'}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </svg>\n </div>\n );\n}\n\n/** Loading state: the URL is still being prepared (see `urlPending`). */\nexport function PendingFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n return (\n <div className={className ?? (isCircular ? 'mv-avatar-fallback' : 'mv-pending-fallback')}>\n <Spinner size={isCircular ? 'xs' : 'md'} label=\"Loading media\" />\n </div>\n );\n}\n\n/** Error state: failed to load */\nexport function ErrorFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n return (\n <div\n className={className ?? 'mv-error-fallback'}\n style={isCircular ? { borderRadius: '50%' } : undefined}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <line x1=\"9\" x2=\"15\" y1=\"15\" y2=\"9\" />\n </svg>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAGzD,MAAM,UAAU,iBAAiB,CAAC,KAKjC;IACC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAElD,OAAO,CACL,YACE,IAAI,EAAE,GAAG,EACT,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,QAAQ,EAAE,QAAQ,IAAI,SAAS,EAC/B,SAAS,EAAE,SAAS,YAEnB,KAAK,IAAI,UAAU,GAClB,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,OAAO,IAAI,KAAK,MAAM,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,cAAK,SAAS,EAAE,SAAS,IAAI,oBAAoB,kBAAS,CAAC;IACpE,CAAC;IACD,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,mBAAmB,YAC9C,eACE,KAAK,EAAC,4BAA4B,EAClC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,IAChC,GACF,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAmD;IACjF,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,CAAC,YACtF,KAAC,OAAO,IAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAC,eAAe,GAAG,GAC7D,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,OAAO,CACL,cACE,SAAS,EAAE,SAAS,IAAI,mBAAmB,EAC3C,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,YAEvD,eACE,KAAK,EAAC,4BAA4B,EAClC,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,EACpC,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,GAAG,IAClC,GACF,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { Spinner } from '@ttt-productions/ui-core/react';\nimport type { FallbackMode } from '../types.js';\n\nexport function MediaFallbackLink(props: {\n url: string;\n filename?: string;\n className?: string;\n label?: string;\n}) {\n const { url, filename, className, label } = props;\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n download={filename || undefined}\n className={className}\n >\n {label ?? 'Download'}\n </a>\n );\n}\n\nexport function shouldShowFallback(mode: FallbackMode | undefined): boolean {\n return mode !== 'none';\n}\n\nexport function EmptyFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n if (isCircular) {\n return <div className={className ?? 'mv-avatar-fallback'}>?</div>;\n }\n return (\n <div className={className ?? 'mv-empty-fallback'}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </svg>\n </div>\n );\n}\n\nexport function PendingFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n return (\n <div className={className ?? (isCircular ? 'mv-avatar-fallback' : 'mv-pending-fallback')}>\n <Spinner size={isCircular ? 'xs' : 'md'} label=\"Loading media\" />\n </div>\n );\n}\n\nexport function ErrorFallback(props: { isCircular?: boolean; className?: string }) {\n const { isCircular, className } = props;\n return (\n <div\n className={className ?? 'mv-error-fallback'}\n style={isCircular ? { borderRadius: '50%' } : undefined}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <line x1=\"9\" x2=\"15\" y1=\"15\" y2=\"9\" />\n </svg>\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../../src/react/image-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,4BAwKlD"}
1
+ {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../../src/react/image-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,4BAiKlD"}
@@ -10,8 +10,6 @@ export function ImageViewer(props) {
10
10
  const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);
11
11
  const [zoomed, setZoomed] = React.useState(false);
12
12
  const imgRef = React.useRef(null);
13
- // The viewer's one observer (MEDIA-102): lazy-load gating, and — while an owner listens —
14
- // the visibility it reports, which is why it keeps observing after the first sighting then.
15
13
  const { ref: inViewRef, inView } = useInView({
16
14
  triggerOnce: !unloadOnExit && !onVisibilityChange,
17
15
  threshold: 0.01,
@@ -23,7 +21,6 @@ export function ImageViewer(props) {
23
21
  if (inView && !shouldLoad) {
24
22
  setShouldLoad(true);
25
23
  }
26
- // Unload when leaving viewport (if enabled)
27
24
  if (!inView && unloadOnExit && shouldLoad && !priority) {
28
25
  setShouldLoad(false);
29
26
  setIsLoaded(false);
@@ -41,8 +38,6 @@ export function ImageViewer(props) {
41
38
  setHasError(true);
42
39
  onError?.();
43
40
  }, [onError]);
44
- // Bounded load: a visible image that fires neither load nor error within the
45
- // budget is treated as errored so it resolves instead of skeleton-ing forever.
46
41
  useLoadWatchdog(shouldLoad && !isLoaded && !hasError, loadTimeoutMs, handleError);
47
42
  // iOS: prevent multi-touch zoom gestures on the image
48
43
  React.useEffect(() => {
@@ -74,8 +69,6 @@ export function ImageViewer(props) {
74
69
  const wrapperStyle = isCircular
75
70
  ? { borderRadius: '50%', overflow: 'hidden' }
76
71
  : {};
77
- // The error state stays inside the observed wrapper, so the viewer keeps reporting
78
- // its visibility while its owner recovers it.
79
72
  if (hasError) {
80
73
  return fallback ? (_jsx("div", { ref: inViewRef, className: className, style: { position: 'relative', width: '100%', height: '100%', ...wrapperStyle }, children: fallback })) : null;
81
74
  }