@ttt-productions/media-viewer 0.10.11 → 0.10.12
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.
- package/dist/react/audio-player-chrome.d.ts.map +1 -1
- package/dist/react/audio-player-chrome.js +22 -1
- package/dist/react/audio-player-chrome.js.map +1 -1
- package/dist/react/fallback.d.ts +5 -0
- package/dist/react/fallback.d.ts.map +1 -1
- package/dist/react/fallback.js +6 -0
- package/dist/react/fallback.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/media-viewer.d.ts.map +1 -1
- package/dist/react/media-viewer.js +3 -3
- package/dist/react/media-viewer.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/styles/media-viewer.css +11 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-player-chrome.d.ts","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"audio-player-chrome.d.ts","sourceRoot":"","sources":["../../src/react/audio-player-chrome.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA+B5D,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,qBA+TxB"}
|
|
@@ -14,6 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
// the element itself changes.
|
|
15
15
|
import * as React from "react";
|
|
16
16
|
import { Pause, Play, Volume2, VolumeX, ChevronDown, ChevronUp, Activity, BarChart3 } from "lucide-react";
|
|
17
|
+
import { Spinner } from "@ttt-productions/ui-core/react";
|
|
17
18
|
import { startWaveformLoop, drawIdleFrame } from "../visualizer.js";
|
|
18
19
|
import { resolveInfiniteDuration } from "../media-duration.js";
|
|
19
20
|
const DEFAULT_PERSIST_KEY = "mv-audio-player";
|
|
@@ -47,6 +48,8 @@ function formatTime(seconds) {
|
|
|
47
48
|
}
|
|
48
49
|
export function AudioPlayerChrome({ audioRef, visualizerMode = "bars", persistKey = DEFAULT_PERSIST_KEY, extraActions, }) {
|
|
49
50
|
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
51
|
+
// Playback is waiting on data (initial buffer, a stall, or a seek landing).
|
|
52
|
+
const [buffering, setBuffering] = React.useState(false);
|
|
50
53
|
const [currentTime, setCurrentTime] = React.useState(0);
|
|
51
54
|
const [duration, setDuration] = React.useState(0);
|
|
52
55
|
const [isMuted, setIsMuted] = React.useState(false);
|
|
@@ -182,6 +185,8 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = "bars", persistKe
|
|
|
182
185
|
setIsMuted(el.muted);
|
|
183
186
|
setVolume(el.volume);
|
|
184
187
|
};
|
|
188
|
+
const onWaiting = () => setBuffering(true);
|
|
189
|
+
const onReady = () => setBuffering(false);
|
|
185
190
|
el.addEventListener("play", onPlay);
|
|
186
191
|
el.addEventListener("pause", onPause);
|
|
187
192
|
el.addEventListener("ended", onEnded);
|
|
@@ -189,6 +194,14 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = "bars", persistKe
|
|
|
189
194
|
el.addEventListener("durationchange", onDurationChange);
|
|
190
195
|
el.addEventListener("loadedmetadata", onDurationChange);
|
|
191
196
|
el.addEventListener("volumechange", onVolumeChange);
|
|
197
|
+
el.addEventListener("waiting", onWaiting);
|
|
198
|
+
el.addEventListener("seeking", onWaiting);
|
|
199
|
+
el.addEventListener("playing", onReady);
|
|
200
|
+
el.addEventListener("seeked", onReady);
|
|
201
|
+
el.addEventListener("canplay", onReady);
|
|
202
|
+
el.addEventListener("pause", onReady);
|
|
203
|
+
el.addEventListener("ended", onReady);
|
|
204
|
+
el.addEventListener("error", onReady);
|
|
192
205
|
return () => {
|
|
193
206
|
el.removeEventListener("play", onPlay);
|
|
194
207
|
el.removeEventListener("pause", onPause);
|
|
@@ -197,6 +210,14 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = "bars", persistKe
|
|
|
197
210
|
el.removeEventListener("durationchange", onDurationChange);
|
|
198
211
|
el.removeEventListener("loadedmetadata", onDurationChange);
|
|
199
212
|
el.removeEventListener("volumechange", onVolumeChange);
|
|
213
|
+
el.removeEventListener("waiting", onWaiting);
|
|
214
|
+
el.removeEventListener("seeking", onWaiting);
|
|
215
|
+
el.removeEventListener("playing", onReady);
|
|
216
|
+
el.removeEventListener("seeked", onReady);
|
|
217
|
+
el.removeEventListener("canplay", onReady);
|
|
218
|
+
el.removeEventListener("pause", onReady);
|
|
219
|
+
el.removeEventListener("ended", onReady);
|
|
220
|
+
el.removeEventListener("error", onReady);
|
|
200
221
|
};
|
|
201
222
|
}, [audioRef, ensureGraph, startLoop, stopLoop]);
|
|
202
223
|
// Full teardown when the chrome unmounts.
|
|
@@ -262,6 +283,6 @@ export function AudioPlayerChrome({ audioRef, visualizerMode = "bars", persistKe
|
|
|
262
283
|
});
|
|
263
284
|
};
|
|
264
285
|
const shownTime = scrubTime ?? currentTime;
|
|
265
|
-
return (_jsxs("div", { className: "mv-player", "data-mv-player": true, "data-minimized": minimized ? "true" : "false", "data-mode": mode, children: [!minimized && (_jsxs("div", { className: "mv-player-viz", children: [_jsx("canvas", { ref: canvasRef, width: 600, height: 96, className: "mv-player-canvas", "aria-hidden": "true" }), _jsx("button", { type: "button", className: "mv-player-btn mv-player-mode-btn", onClick: toggleMode, "aria-label": mode === "line" ? "Switch visualizer to bars" : "Switch visualizer to waveform line", children: mode === "line" ? _jsx(BarChart3, { "aria-hidden": "true" }) : _jsx(Activity, { "aria-hidden": "true" }) })] })), _jsxs("div", { className: "mv-player-controls", children: [_jsx("button", { type: "button", className: "mv-player-btn mv-player-play-btn", onClick: togglePlay, "aria-label": isPlaying ? "Pause" : "Play", children: isPlaying ? _jsx(Pause, { "aria-hidden": "true" }) : _jsx(Play, { "aria-hidden": "true" }) }), _jsx("span", { className: "mv-player-time", "aria-hidden": "true", children: formatTime(shownTime) }), _jsx("input", { type: "range", className: "mv-player-seek", min: 0, max: duration > 0 ? duration : 0, step: 0.1, value: Math.min(shownTime, duration > 0 ? duration : shownTime), onChange: onSeekInput, onMouseUp: commitSeek, onTouchEnd: commitSeek, onKeyUp: commitSeek, disabled: duration <= 0, "aria-label": "Seek" }), _jsx("span", { className: "mv-player-time", "aria-hidden": "true", children: formatTime(duration) }), _jsx("button", { type: "button", className: "mv-player-btn", onClick: toggleMute, "aria-label": isMuted ? "Unmute" : "Mute", children: isMuted ? _jsx(VolumeX, { "aria-hidden": "true" }) : _jsx(Volume2, { "aria-hidden": "true" }) }), _jsx("input", { type: "range", className: "mv-player-volume", min: 0, max: 1, step: 0.05, value: isMuted ? 0 : volume, onChange: onVolumeInput, "aria-label": "Volume" }), _jsx("button", { type: "button", className: "mv-player-btn", onClick: toggleMinimized, "aria-label": minimized ? "Show visualizer" : "Hide visualizer", children: minimized ? _jsx(ChevronUp, { "aria-hidden": "true" }) : _jsx(ChevronDown, { "aria-hidden": "true" }) }), extraActions != null && _jsx("div", { className: "mv-player-extra", children: extraActions })] })] }));
|
|
286
|
+
return (_jsxs("div", { className: "mv-player", "data-mv-player": true, "data-minimized": minimized ? "true" : "false", "data-mode": mode, children: [!minimized && (_jsxs("div", { className: "mv-player-viz", children: [_jsx("canvas", { ref: canvasRef, width: 600, height: 96, className: "mv-player-canvas", "aria-hidden": "true" }), _jsx("button", { type: "button", className: "mv-player-btn mv-player-mode-btn", onClick: toggleMode, "aria-label": mode === "line" ? "Switch visualizer to bars" : "Switch visualizer to waveform line", children: mode === "line" ? _jsx(BarChart3, { "aria-hidden": "true" }) : _jsx(Activity, { "aria-hidden": "true" }) })] })), _jsxs("div", { className: "mv-player-controls", children: [_jsx("button", { type: "button", className: "mv-player-btn mv-player-play-btn", onClick: togglePlay, "aria-label": isPlaying ? "Pause" : "Play", "aria-busy": isPlaying && buffering ? true : undefined, children: isPlaying && buffering ? _jsx(Spinner, { size: "xs" }) : isPlaying ? _jsx(Pause, { "aria-hidden": "true" }) : _jsx(Play, { "aria-hidden": "true" }) }), _jsx("span", { className: "mv-player-time", "aria-hidden": "true", children: formatTime(shownTime) }), _jsx("input", { type: "range", className: "mv-player-seek", min: 0, max: duration > 0 ? duration : 0, step: 0.1, value: Math.min(shownTime, duration > 0 ? duration : shownTime), onChange: onSeekInput, onMouseUp: commitSeek, onTouchEnd: commitSeek, onKeyUp: commitSeek, disabled: duration <= 0, "aria-label": "Seek" }), _jsx("span", { className: "mv-player-time", "aria-hidden": "true", children: formatTime(duration) }), _jsx("button", { type: "button", className: "mv-player-btn", onClick: toggleMute, "aria-label": isMuted ? "Unmute" : "Mute", children: isMuted ? _jsx(VolumeX, { "aria-hidden": "true" }) : _jsx(Volume2, { "aria-hidden": "true" }) }), _jsx("input", { type: "range", className: "mv-player-volume", min: 0, max: 1, step: 0.05, value: isMuted ? 0 : volume, onChange: onVolumeInput, "aria-label": "Volume" }), _jsx("button", { type: "button", className: "mv-player-btn", onClick: toggleMinimized, "aria-label": minimized ? "Show visualizer" : "Hide visualizer", children: minimized ? _jsx(ChevronUp, { "aria-hidden": "true" }) : _jsx(ChevronDown, { "aria-hidden": "true" }) }), extraActions != null && _jsx("div", { className: "mv-player-extra", children: extraActions })] })] }));
|
|
266
287
|
}
|
|
267
288
|
//# sourceMappingURL=audio-player-chrome.js.map
|
|
@@ -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.\n\nimport * as React from \"react\";\nimport { Pause, Play, Volume2, VolumeX, ChevronDown, ChevronUp, Activity, BarChart3 } from \"lucide-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\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 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>(() => readPref(persistKey, \"minimized\") === \"1\");\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 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\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 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 };\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 className=\"mv-player\" data-mv-player data-minimized={minimized ? \"true\" : \"false\"} data-mode={mode}>\n {!minimized && (\n <div className=\"mv-player-viz\">\n <canvas ref={canvasRef} width={600} height={96} className=\"mv-player-canvas\" aria-hidden=\"true\" />\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-mode-btn\"\n onClick={toggleMode}\n aria-label={mode === \"line\" ? \"Switch visualizer to bars\" : \"Switch visualizer to waveform line\"}\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 >\n {isPlaying ? <Pause aria-hidden=\"true\" /> : <Play aria-hidden=\"true\" />}\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// 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.\n\nimport * as React from \"react\";\nimport { Pause, Play, Volume2, VolumeX, ChevronDown, ChevronUp, Activity, BarChart3 } 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\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>(() => readPref(persistKey, \"minimized\") === \"1\");\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 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 className=\"mv-player\" data-mv-player data-minimized={minimized ? \"true\" : \"false\"} data-mode={mode}>\n {!minimized && (\n <div className=\"mv-player-viz\">\n <canvas ref={canvasRef} width={600} height={96} className=\"mv-player-canvas\" aria-hidden=\"true\" />\n <button\n type=\"button\"\n className=\"mv-player-btn mv-player-mode-btn\"\n onClick={toggleMode}\n aria-label={mode === \"line\" ? \"Switch visualizer to bars\" : \"Switch visualizer to waveform line\"}\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 ? <Spinner size=\"xs\" /> : isPlaying ? <Pause aria-hidden=\"true\" /> : <Play aria-hidden=\"true\" />}\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"]}
|
package/dist/react/fallback.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export declare function EmptyFallback(props: {
|
|
|
11
11
|
isCircular?: boolean;
|
|
12
12
|
className?: string;
|
|
13
13
|
}): import("react").JSX.Element;
|
|
14
|
+
/** Loading state: the URL is still being prepared (see `urlPending`). */
|
|
15
|
+
export declare function PendingFallback(props: {
|
|
16
|
+
isCircular?: boolean;
|
|
17
|
+
className?: string;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
14
19
|
/** Error state: failed to load */
|
|
15
20
|
export declare function ErrorFallback(props: {
|
|
16
21
|
isCircular?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":"
|
|
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,+BAehF;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,+BAchF"}
|
package/dist/react/fallback.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Spinner } from "@ttt-productions/ui-core/react";
|
|
2
3
|
export function MediaFallbackLink(props) {
|
|
3
4
|
const { url, filename, className, label } = props;
|
|
4
5
|
return (_jsx("a", { href: url, target: "_blank", rel: "noreferrer", download: filename || undefined, className: className, children: label ?? "Download" }));
|
|
@@ -14,6 +15,11 @@ export function EmptyFallback(props) {
|
|
|
14
15
|
}
|
|
15
16
|
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" })] }) }));
|
|
16
17
|
}
|
|
18
|
+
/** Loading state: the URL is still being prepared (see `urlPending`). */
|
|
19
|
+
export function PendingFallback(props) {
|
|
20
|
+
const { isCircular, className } = props;
|
|
21
|
+
return (_jsx("div", { className: className ?? (isCircular ? "mv-avatar-fallback" : "mv-pending-fallback"), children: _jsx(Spinner, { size: isCircular ? "xs" : "md", label: "Loading media" }) }));
|
|
22
|
+
}
|
|
17
23
|
/** Error state: failed to load */
|
|
18
24
|
export function ErrorFallback(props) {
|
|
19
25
|
const { isCircular, className } = props;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/react/fallback.tsx"],"names":[],"mappings":";
|
|
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,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,oBAAoB,kBAAS,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,mBAAmB,YAC9C,eAAK,KAAK,EAAC,4BAA4B,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,aAC/K,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,eAAK,KAAK,EAAC,4BAA4B,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,aAC/K,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 (\n <div className={className ?? \"mv-avatar-fallback\"}>?</div>\n );\n }\n return (\n <div className={className ?? \"mv-empty-fallback\"}>\n <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\">\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 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\">\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"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { VideoViewer } from "./video-viewer.js";
|
|
|
4
4
|
export { AudioViewer } from "./audio-viewer.js";
|
|
5
5
|
export { AudioPlayer } from "./audio-player.js";
|
|
6
6
|
export type { AudioPlayerProps } from "./audio-player.js";
|
|
7
|
-
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
7
|
+
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback, PendingFallback } from "./fallback.js";
|
|
8
8
|
export { useMediaRecovery } from "./use-media-recovery.js";
|
|
9
9
|
export type { UseMediaRecoveryOptions, UseMediaRecoveryResult } from "./use-media-recovery.js";
|
|
10
10
|
export { useMediaPlayback, PROGRESS_SAMPLE_INTERVAL_MS, LATE_RESUME_ADOPT_WINDOW_SEC, } from "./use-media-playback.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { ImageViewer } from "./image-viewer.js";
|
|
|
4
4
|
export { VideoViewer } from "./video-viewer.js";
|
|
5
5
|
export { AudioViewer } from "./audio-viewer.js";
|
|
6
6
|
export { AudioPlayer } from "./audio-player.js";
|
|
7
|
-
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
7
|
+
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback, PendingFallback } from "./fallback.js";
|
|
8
8
|
export { useMediaRecovery } from "./use-media-recovery.js";
|
|
9
9
|
export { useMediaPlayback, PROGRESS_SAMPLE_INTERVAL_MS, LATE_RESUME_ADOPT_WINDOW_SEC, } from "./use-media-playback.js";
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC","sourcesContent":["\"use client\";\n\nexport { MediaViewer, MediaPreview } from \"./media-viewer.js\";\nexport { ImageViewer } from \"./image-viewer.js\";\nexport { VideoViewer } from \"./video-viewer.js\";\nexport { AudioViewer } from \"./audio-viewer.js\";\nexport { AudioPlayer } from \"./audio-player.js\";\nexport type { AudioPlayerProps } from \"./audio-player.js\";\nexport { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback, PendingFallback } from \"./fallback.js\";\nexport { useMediaRecovery } from \"./use-media-recovery.js\";\nexport type { UseMediaRecoveryOptions, UseMediaRecoveryResult } from \"./use-media-recovery.js\";\nexport {\n useMediaPlayback,\n PROGRESS_SAMPLE_INTERVAL_MS,\n LATE_RESUME_ADOPT_WINDOW_SEC,\n} from \"./use-media-playback.js\";\nexport type { UseMediaPlaybackResult } from \"./use-media-playback.js\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;AA6GpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;AA6GpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,4BA4OnD;AAED,wBAAwB;AACxB,eAAO,MAAM,YAAY,oBAAc,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { getSimplifiedMediaType } from "@ttt-productions/media-schemas";
|
|
4
|
-
import { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
4
|
+
import { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback, PendingFallback } from "./fallback.js";
|
|
5
5
|
import { ImageViewer } from "./image-viewer.js";
|
|
6
6
|
import { VideoViewer } from "./video-viewer.js";
|
|
7
7
|
import { AudioViewer } from "./audio-viewer.js";
|
|
@@ -59,7 +59,7 @@ function RecoveryOverlay({ state, onManualRetry, isCircular }) {
|
|
|
59
59
|
// Main component
|
|
60
60
|
// ---------------------------------------------------------------------------
|
|
61
61
|
export function MediaViewer(props) {
|
|
62
|
-
const { url, alt, type, className, unloadOnExit, isCircular = false, enableZoom, priority, skeleton, lazy, controls, autoPlay, muted, loop, posterUrl, preload, autoPlayOnVisible, onLoad, onError, onLoadChange, onEnded, onProgressSample, startAtSeconds, endOverlay, playbackControlsRef, audioVisualizerMode, audioPersistKey, audioExtraActions, fallbackMode = "link", fallbackLabel, filename, mime, name, recoveryAdapter, assetStatusHint, loadTimeoutMs, } = props;
|
|
62
|
+
const { url, alt, type, className, unloadOnExit, urlPending = false, isCircular = false, enableZoom, priority, skeleton, lazy, controls, autoPlay, muted, loop, posterUrl, preload, autoPlayOnVisible, onLoad, onError, onLoadChange, onEnded, onProgressSample, startAtSeconds, endOverlay, playbackControlsRef, audioVisualizerMode, audioPersistKey, audioExtraActions, fallbackMode = "link", fallbackLabel, filename, mime, name, recoveryAdapter, assetStatusHint, loadTimeoutMs, } = props;
|
|
63
63
|
const [hasError, setHasError] = React.useState(false);
|
|
64
64
|
const [sourceUrl, setSourceUrl] = React.useState(null);
|
|
65
65
|
// Remount key: incrementing this causes child media elements to unmount and
|
|
@@ -126,7 +126,7 @@ export function MediaViewer(props) {
|
|
|
126
126
|
recoveryState.phase !== "loading";
|
|
127
127
|
// No URL -> empty fallback
|
|
128
128
|
if (!sourceUrl) {
|
|
129
|
-
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(EmptyFallback, { isCircular: isCircular }) }));
|
|
129
|
+
return (_jsx("div", { className: className, style: wrapperStyle, children: urlPending ? _jsx(PendingFallback, { isCircular: isCircular }) : _jsx(EmptyFallback, { isCircular: isCircular }) }));
|
|
130
130
|
}
|
|
131
131
|
// Recovery overlay (for all non-idle/non-loaded recovery states)
|
|
132
132
|
if (isInRecovery && hasError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\";\nimport { getSimplifiedMediaType } from \"@ttt-productions/media-schemas\";\nimport type { SimplifiedMediaType } from \"@ttt-productions/media-schemas\";\n\nimport type { MediaPreviewProps, MediaViewerType, FallbackMode } from \"../types.js\";\nimport { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from \"./fallback.js\";\nimport { ImageViewer } from \"./image-viewer.js\";\nimport { VideoViewer } from \"./video-viewer.js\";\nimport { AudioViewer } from \"./audio-viewer.js\";\nimport { useMediaRecovery } from \"./use-media-recovery.js\";\nimport type { RecoveryState } from \"../recovery.js\";\n\nfunction inferType(props: { type?: MediaPreviewProps[\"type\"]; mime?: string; name?: string; url?: string }): MediaViewerType {\n if (props.type) {\n // Handle MIME strings like \"image/png\" -> \"image\"\n const primary = typeof props.type === \"string\" ? props.type.split(\"/\")[0] : props.type;\n if (primary === \"image\" || primary === \"video\" || primary === \"audio\") return primary;\n if (primary === \"other\") return \"other\";\n }\n\n const hint = props.mime ?? props.name ?? props.url;\n if (!hint) return \"other\";\n const simplified: SimplifiedMediaType = getSimplifiedMediaType(hint);\n return simplified;\n}\n\n// ---------------------------------------------------------------------------\n// Recovery-phase UI helpers\n// ---------------------------------------------------------------------------\n\nfunction RecoveryOverlay({ state, onManualRetry, isCircular }: {\n state: RecoveryState;\n onManualRetry: () => void;\n isCircular?: boolean;\n}) {\n const wrapStyle: React.CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n gap: \"0.5rem\",\n ...(isCircular ? { borderRadius: \"50%\" } : {}),\n };\n\n if (state.phase === \"processing\") {\n return (\n <div className=\"mv-recovery-processing\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Processing media…</span>\n </div>\n );\n }\n\n if (state.phase === \"finalizing\") {\n return (\n <div className=\"mv-recovery-finalizing\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Finishing media…</span>\n </div>\n );\n }\n\n if (state.phase === \"transient-retry\" || state.phase === \"propagating\") {\n return (\n <div className=\"mv-recovery-retrying\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Loading…</span>\n </div>\n );\n }\n\n if (state.phase === \"auth-retry\") {\n return (\n <div className=\"mv-recovery-auth\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Refreshing access…</span>\n </div>\n );\n }\n\n if (state.phase === \"hard-unavailable\") {\n return (\n <div className=\"mv-recovery-hard\" style={wrapStyle}>\n <ErrorFallback isCircular={isCircular} />\n {state.reason === \"auth\" ? (\n <span className=\"mv-recovery-label\">Access denied.</span>\n ) : (\n <span className=\"mv-recovery-label\">Media unavailable.</span>\n )}\n </div>\n );\n }\n\n if (state.phase === \"max-wait-fallback\") {\n return (\n <div className=\"mv-recovery-max-wait\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">This media is taking longer than expected.</span>\n <button\n type=\"button\"\n className=\"mv-recovery-retry-btn\"\n onClick={onManualRetry}\n >\n Retry\n </button>\n </div>\n );\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nexport function MediaViewer(props: MediaPreviewProps) {\n const {\n url,\n alt,\n type,\n className,\n unloadOnExit,\n isCircular = false,\n enableZoom,\n priority,\n skeleton,\n lazy,\n controls,\n autoPlay,\n muted,\n loop,\n posterUrl,\n preload,\n autoPlayOnVisible,\n onLoad,\n onError,\n onLoadChange,\n onEnded,\n onProgressSample,\n startAtSeconds,\n endOverlay,\n playbackControlsRef,\n audioVisualizerMode,\n audioPersistKey,\n audioExtraActions,\n fallbackMode = \"link\",\n fallbackLabel,\n filename,\n mime,\n name,\n recoveryAdapter,\n assetStatusHint,\n loadTimeoutMs,\n } = props;\n\n const [hasError, setHasError] = React.useState(false);\n const [sourceUrl, setSourceUrl] = React.useState<string | null>(null);\n\n // Remount key: incrementing this causes child media elements to unmount and\n // re-mount from scratch, re-issuing the HTTP request.\n const [remountKey, setRemountKey] = React.useState(0);\n\n const handleRemount = React.useCallback(() => {\n setHasError(false);\n setRemountKey((k) => k + 1);\n }, []);\n\n // Recovery hook (no-op when recoveryAdapter is omitted)\n const { recoveryState, onMediaError, onMediaLoad, manualRetry } =\n useMediaRecovery({\n url: typeof url === \"string\" ? url : null,\n adapter: recoveryAdapter,\n statusHint: assetStatusHint,\n onRemount: handleRemount,\n });\n\n React.useEffect(() => {\n setHasError(false);\n let objectUrl: string | null = null;\n\n if (typeof url === \"string\") {\n setSourceUrl(url);\n } else if (url instanceof Blob) {\n objectUrl = URL.createObjectURL(url);\n setSourceUrl(objectUrl);\n } else {\n setSourceUrl(null);\n }\n\n return () => {\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [url]);\n\n const handleError = React.useCallback(() => {\n setHasError(true);\n onError?.();\n // If a recovery adapter is provided, delegate to the recovery hook.\n if (recoveryAdapter) {\n onMediaError();\n }\n }, [onError, recoveryAdapter, onMediaError]);\n\n const handleLoad = React.useCallback(() => {\n setHasError(false);\n onLoad?.();\n // Signal successful load to the recovery state machine.\n if (recoveryAdapter) {\n onMediaLoad();\n }\n }, [onLoad, recoveryAdapter, onMediaLoad]);\n\n const circularStyle: React.CSSProperties = isCircular\n ? { borderRadius: \"50%\", overflow: \"hidden\" }\n : {};\n\n const wrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n ...circularStyle,\n };\n\n // When recovery is active and in a terminal/blocking state, show the overlay.\n const isInRecovery =\n recoveryAdapter !== undefined &&\n recoveryState.phase !== \"idle\" &&\n recoveryState.phase !== \"loaded\" &&\n recoveryState.phase !== \"loading\";\n\n // No URL -> empty fallback\n if (!sourceUrl) {\n return (\n <div className={className} style={wrapperStyle}>\n <EmptyFallback isCircular={isCircular} />\n </div>\n );\n }\n\n // Recovery overlay (for all non-idle/non-loaded recovery states)\n if (isInRecovery && hasError) {\n return (\n <div className={className} style={wrapperStyle}>\n <RecoveryOverlay\n state={recoveryState}\n onManualRetry={manualRetry}\n isCircular={isCircular}\n />\n </div>\n );\n }\n\n // Vanilla error (no recovery adapter) -> error fallback\n if (hasError && !recoveryAdapter) {\n return (\n <div className={className} style={wrapperStyle}>\n <ErrorFallback isCircular={isCircular} />\n </div>\n );\n }\n\n const t = inferType({ type, mime, name, url: sourceUrl });\n\n if (t === \"image\") {\n return (\n <div className={className} style={wrapperStyle}>\n <ImageViewer\n key={remountKey}\n url={sourceUrl}\n alt={alt}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n unloadOnExit={unloadOnExit}\n isCircular={isCircular}\n enableZoom={enableZoom}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onError={handleError}\n fallback={<ErrorFallback isCircular={isCircular} />}\n />\n </div>\n );\n }\n\n if (t === \"video\") {\n return (\n <div className={className} style={wrapperStyle}>\n <VideoViewer\n key={remountKey}\n url={sourceUrl}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n unloadOnExit={unloadOnExit}\n controls={controls}\n autoPlay={autoPlay}\n muted={muted}\n loop={loop}\n posterUrl={posterUrl}\n preload={preload}\n autoPlayOnVisible={autoPlayOnVisible}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onError={handleError}\n onEnded={onEnded}\n onProgressSample={onProgressSample}\n startAtSeconds={startAtSeconds}\n endOverlay={endOverlay}\n playbackControlsRef={playbackControlsRef}\n />\n </div>\n );\n }\n\n if (t === \"audio\") {\n return (\n <div className={className} style={wrapperStyle}>\n <AudioViewer\n key={remountKey}\n url={sourceUrl}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n autoPlay={autoPlay}\n loop={loop}\n preload={preload}\n visualizerMode={audioVisualizerMode}\n persistKey={audioPersistKey}\n extraActions={audioExtraActions}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onLoadChange={onLoadChange}\n onError={handleError}\n onEnded={onEnded}\n onProgressSample={onProgressSample}\n startAtSeconds={startAtSeconds}\n endOverlay={endOverlay}\n playbackControlsRef={playbackControlsRef}\n />\n </div>\n );\n }\n\n // Unknown type -> fallback link\n return shouldShowFallback(fallbackMode as FallbackMode) ? (\n <div className={className} style={wrapperStyle}>\n <MediaFallbackLink url={sourceUrl} filename={filename} label={fallbackLabel ?? \"Download\"} />\n </div>\n ) : null;\n}\n\n/** Back-compat alias */\nexport const MediaPreview = MediaViewer;\n"]}
|
|
1
|
+
{"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\";\nimport { getSimplifiedMediaType } from \"@ttt-productions/media-schemas\";\nimport type { SimplifiedMediaType } from \"@ttt-productions/media-schemas\";\n\nimport type { MediaPreviewProps, MediaViewerType, FallbackMode } from \"../types.js\";\nimport { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback, PendingFallback } from \"./fallback.js\";\nimport { ImageViewer } from \"./image-viewer.js\";\nimport { VideoViewer } from \"./video-viewer.js\";\nimport { AudioViewer } from \"./audio-viewer.js\";\nimport { useMediaRecovery } from \"./use-media-recovery.js\";\nimport type { RecoveryState } from \"../recovery.js\";\n\nfunction inferType(props: { type?: MediaPreviewProps[\"type\"]; mime?: string; name?: string; url?: string }): MediaViewerType {\n if (props.type) {\n // Handle MIME strings like \"image/png\" -> \"image\"\n const primary = typeof props.type === \"string\" ? props.type.split(\"/\")[0] : props.type;\n if (primary === \"image\" || primary === \"video\" || primary === \"audio\") return primary;\n if (primary === \"other\") return \"other\";\n }\n\n const hint = props.mime ?? props.name ?? props.url;\n if (!hint) return \"other\";\n const simplified: SimplifiedMediaType = getSimplifiedMediaType(hint);\n return simplified;\n}\n\n// ---------------------------------------------------------------------------\n// Recovery-phase UI helpers\n// ---------------------------------------------------------------------------\n\nfunction RecoveryOverlay({ state, onManualRetry, isCircular }: {\n state: RecoveryState;\n onManualRetry: () => void;\n isCircular?: boolean;\n}) {\n const wrapStyle: React.CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n gap: \"0.5rem\",\n ...(isCircular ? { borderRadius: \"50%\" } : {}),\n };\n\n if (state.phase === \"processing\") {\n return (\n <div className=\"mv-recovery-processing\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Processing media…</span>\n </div>\n );\n }\n\n if (state.phase === \"finalizing\") {\n return (\n <div className=\"mv-recovery-finalizing\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Finishing media…</span>\n </div>\n );\n }\n\n if (state.phase === \"transient-retry\" || state.phase === \"propagating\") {\n return (\n <div className=\"mv-recovery-retrying\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Loading…</span>\n </div>\n );\n }\n\n if (state.phase === \"auth-retry\") {\n return (\n <div className=\"mv-recovery-auth\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">Refreshing access…</span>\n </div>\n );\n }\n\n if (state.phase === \"hard-unavailable\") {\n return (\n <div className=\"mv-recovery-hard\" style={wrapStyle}>\n <ErrorFallback isCircular={isCircular} />\n {state.reason === \"auth\" ? (\n <span className=\"mv-recovery-label\">Access denied.</span>\n ) : (\n <span className=\"mv-recovery-label\">Media unavailable.</span>\n )}\n </div>\n );\n }\n\n if (state.phase === \"max-wait-fallback\") {\n return (\n <div className=\"mv-recovery-max-wait\" style={wrapStyle}>\n <span className=\"mv-recovery-label\">This media is taking longer than expected.</span>\n <button\n type=\"button\"\n className=\"mv-recovery-retry-btn\"\n onClick={onManualRetry}\n >\n Retry\n </button>\n </div>\n );\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nexport function MediaViewer(props: MediaPreviewProps) {\n const {\n url,\n alt,\n type,\n className,\n unloadOnExit,\n urlPending = false,\n isCircular = false,\n enableZoom,\n priority,\n skeleton,\n lazy,\n controls,\n autoPlay,\n muted,\n loop,\n posterUrl,\n preload,\n autoPlayOnVisible,\n onLoad,\n onError,\n onLoadChange,\n onEnded,\n onProgressSample,\n startAtSeconds,\n endOverlay,\n playbackControlsRef,\n audioVisualizerMode,\n audioPersistKey,\n audioExtraActions,\n fallbackMode = \"link\",\n fallbackLabel,\n filename,\n mime,\n name,\n recoveryAdapter,\n assetStatusHint,\n loadTimeoutMs,\n } = props;\n\n const [hasError, setHasError] = React.useState(false);\n const [sourceUrl, setSourceUrl] = React.useState<string | null>(null);\n\n // Remount key: incrementing this causes child media elements to unmount and\n // re-mount from scratch, re-issuing the HTTP request.\n const [remountKey, setRemountKey] = React.useState(0);\n\n const handleRemount = React.useCallback(() => {\n setHasError(false);\n setRemountKey((k) => k + 1);\n }, []);\n\n // Recovery hook (no-op when recoveryAdapter is omitted)\n const { recoveryState, onMediaError, onMediaLoad, manualRetry } =\n useMediaRecovery({\n url: typeof url === \"string\" ? url : null,\n adapter: recoveryAdapter,\n statusHint: assetStatusHint,\n onRemount: handleRemount,\n });\n\n React.useEffect(() => {\n setHasError(false);\n let objectUrl: string | null = null;\n\n if (typeof url === \"string\") {\n setSourceUrl(url);\n } else if (url instanceof Blob) {\n objectUrl = URL.createObjectURL(url);\n setSourceUrl(objectUrl);\n } else {\n setSourceUrl(null);\n }\n\n return () => {\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [url]);\n\n const handleError = React.useCallback(() => {\n setHasError(true);\n onError?.();\n // If a recovery adapter is provided, delegate to the recovery hook.\n if (recoveryAdapter) {\n onMediaError();\n }\n }, [onError, recoveryAdapter, onMediaError]);\n\n const handleLoad = React.useCallback(() => {\n setHasError(false);\n onLoad?.();\n // Signal successful load to the recovery state machine.\n if (recoveryAdapter) {\n onMediaLoad();\n }\n }, [onLoad, recoveryAdapter, onMediaLoad]);\n\n const circularStyle: React.CSSProperties = isCircular\n ? { borderRadius: \"50%\", overflow: \"hidden\" }\n : {};\n\n const wrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n ...circularStyle,\n };\n\n // When recovery is active and in a terminal/blocking state, show the overlay.\n const isInRecovery =\n recoveryAdapter !== undefined &&\n recoveryState.phase !== \"idle\" &&\n recoveryState.phase !== \"loaded\" &&\n recoveryState.phase !== \"loading\";\n\n // No URL -> empty fallback\n if (!sourceUrl) {\n return (\n <div className={className} style={wrapperStyle}>\n {urlPending ? <PendingFallback isCircular={isCircular} /> : <EmptyFallback isCircular={isCircular} />}\n </div>\n );\n }\n\n // Recovery overlay (for all non-idle/non-loaded recovery states)\n if (isInRecovery && hasError) {\n return (\n <div className={className} style={wrapperStyle}>\n <RecoveryOverlay\n state={recoveryState}\n onManualRetry={manualRetry}\n isCircular={isCircular}\n />\n </div>\n );\n }\n\n // Vanilla error (no recovery adapter) -> error fallback\n if (hasError && !recoveryAdapter) {\n return (\n <div className={className} style={wrapperStyle}>\n <ErrorFallback isCircular={isCircular} />\n </div>\n );\n }\n\n const t = inferType({ type, mime, name, url: sourceUrl });\n\n if (t === \"image\") {\n return (\n <div className={className} style={wrapperStyle}>\n <ImageViewer\n key={remountKey}\n url={sourceUrl}\n alt={alt}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n unloadOnExit={unloadOnExit}\n isCircular={isCircular}\n enableZoom={enableZoom}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onError={handleError}\n fallback={<ErrorFallback isCircular={isCircular} />}\n />\n </div>\n );\n }\n\n if (t === \"video\") {\n return (\n <div className={className} style={wrapperStyle}>\n <VideoViewer\n key={remountKey}\n url={sourceUrl}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n unloadOnExit={unloadOnExit}\n controls={controls}\n autoPlay={autoPlay}\n muted={muted}\n loop={loop}\n posterUrl={posterUrl}\n preload={preload}\n autoPlayOnVisible={autoPlayOnVisible}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onError={handleError}\n onEnded={onEnded}\n onProgressSample={onProgressSample}\n startAtSeconds={startAtSeconds}\n endOverlay={endOverlay}\n playbackControlsRef={playbackControlsRef}\n />\n </div>\n );\n }\n\n if (t === \"audio\") {\n return (\n <div className={className} style={wrapperStyle}>\n <AudioViewer\n key={remountKey}\n url={sourceUrl}\n lazy={lazy}\n priority={priority}\n skeleton={skeleton}\n autoPlay={autoPlay}\n loop={loop}\n preload={preload}\n visualizerMode={audioVisualizerMode}\n persistKey={audioPersistKey}\n extraActions={audioExtraActions}\n loadTimeoutMs={loadTimeoutMs}\n onLoad={handleLoad}\n onLoadChange={onLoadChange}\n onError={handleError}\n onEnded={onEnded}\n onProgressSample={onProgressSample}\n startAtSeconds={startAtSeconds}\n endOverlay={endOverlay}\n playbackControlsRef={playbackControlsRef}\n />\n </div>\n );\n }\n\n // Unknown type -> fallback link\n return shouldShowFallback(fallbackMode as FallbackMode) ? (\n <div className={className} style={wrapperStyle}>\n <MediaFallbackLink url={sourceUrl} filename={filename} label={fallbackLabel ?? \"Download\"} />\n </div>\n ) : null;\n}\n\n/** Back-compat alias */\nexport const MediaPreview = MediaViewer;\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -136,6 +136,13 @@ export type AudioViewerProps = BaseMediaProps & MediaPlaybackProps & {
|
|
|
136
136
|
};
|
|
137
137
|
export type MediaPreviewProps = {
|
|
138
138
|
url?: string | File | Blob | null;
|
|
139
|
+
/**
|
|
140
|
+
* The URL is still being prepared (for example a short-lived access grant is
|
|
141
|
+
* being minted). While true and no `url` is available, the viewer renders a
|
|
142
|
+
* loading state instead of the empty fallback, so "not yet" never reads as
|
|
143
|
+
* "nothing here".
|
|
144
|
+
*/
|
|
145
|
+
urlPending?: boolean;
|
|
139
146
|
alt?: string;
|
|
140
147
|
type?: MediaViewerType | string;
|
|
141
148
|
className?: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as React from \"react\";\nimport type { MediaDiagnosticAdapter, AssetStatusHint } from \"./recovery.js\";\nimport type { AudioVisualizerMode } from \"./visualizer.js\";\n\nexport type { MediaDiagnosticAdapter, AssetStatusHint, DiagnosisResult, RecoveryState } from \"./recovery.js\";\nexport type { AudioVisualizerMode } from \"./visualizer.js\";\n\nexport type MediaViewerType = \"image\" | \"video\" | \"audio\" | \"other\";\nexport type FallbackMode = \"none\" | \"link\";\n\n/**\n * Imperative playback controls, exposed via the `playbackControlsRef` prop on\n * `VideoViewer` / `AudioViewer` (and forwarded by `MediaPreview` for\n * video/audio). Obtain via `React.useRef<MediaPlaybackControls>(null)`.\n */\nexport type MediaPlaybackControls = {\n /** Seek to 0 and play from the start. Clears any end-overlay. */\n restart(): void;\n /** Seek to the given position (seconds), clamped to [0, duration]. */\n seekTo(seconds: number): void;\n};\n\n/**\n * Why a progress sample was emitted. `periodic` is a throttled position report\n * during playback and is safe for a consumer to coalesce or drop; the other\n * three mark an exact moment the user's position changed and should be\n * committed as-is.\n */\nexport type MediaProgressSampleReason = \"periodic\" | \"pause\" | \"seeked\" | \"ended\";\n\n/**\n * Additive, fully optional playback API shared by `VideoViewer` and\n * `AudioViewer`. Every field is optional; when all are absent the viewers\n * behave exactly as before.\n */\nexport type MediaPlaybackProps = {\n /**\n * Fires when the media element ends (native `ended` event) with the play\n * cursor at duration. NOTE: when `loop` is set the browser restarts natively\n * and emits no `ended` event, so `onEnded` never fires in that mode.\n */\n onEnded?: () => void;\n\n /**\n * Playback-position reporting. Every sample carries the `reason` it was\n * emitted for ({@link MediaProgressSampleReason}) as an additive third\n * argument, so a consumer can throttle `periodic` reports while committing\n * the exact-moment ones. Contract:\n * - `periodic` — while playing, fires at most once every ~5 seconds of\n * playback (throttled off `timeupdate`). Never fires while paused, and is\n * SUPPRESSED near the start until the resume decision is settled: while no\n * `startAtSeconds` seed has been adopted and the element is still within\n * the late-resume adoption window, an autoplaying element reports nothing,\n * so a position of ~0 can never overwrite a saved one.\n * - `pause` / `seeked` / `ended` — fires once immediately on that event with\n * the exact position, never suppressed. The programmatic resume seek that\n * applies `startAtSeconds` reports as `seeked` with the resumed position.\n * `durationSeconds` is `0` until the element's duration is known.\n *\n * Active-element retirement: samples come only from the ACTIVE media-element\n * generation. When the viewer tears an element down (unload off-screen,\n * unmount, source replacement), it retires the element first — the still-live\n * cursor is committed once as a final `pause` sample (skipped when the last\n * commit already reported that exact position, when playback never started,\n * or while still inside the pre-seed suppression window) — and the reset\n * events the teardown itself produces (browsers snap the cursor to 0 and can\n * fire pause/seeked/timeupdate) are ignored. A genuine user seek or restart\n * to exactly 0 on the active element still reports normally.\n */\n onProgressSample?: (\n currentTimeSeconds: number,\n durationSeconds: number,\n reason: MediaProgressSampleReason,\n ) => void;\n\n /**\n * Initial playback position (seconds), applied once the element has metadata.\n * May arrive late (e.g. from an async prefs read that resolves after the\n * viewer mounted): a value that appears while playback has not yet begun is\n * adopted and applied to the already-loaded element. Once real playback has\n * begun a later value is ignored — it never yanks playing media.\n * Survives the `unloadOnExit` unload/remount cycle: once playback has started\n * the viewer tracks the live position internally and resumes from the LAST\n * KNOWN position on remount — it does not re-apply this prop and does not\n * restart at 0.\n */\n startAtSeconds?: number;\n\n /**\n * Rendered as an overlay covering the media area when the media has ended.\n * Removed when playback restarts (native replay or imperative `restart()`).\n * The overlay only intercepts pointer events while shown and renders above\n * the element. In a native-fullscreen exit state the overlay reappears over\n * the inline element (it is part of normal document flow, not the fullscreen\n * layer).\n */\n endOverlay?: React.ReactNode;\n\n /** Imperative controls handle — see {@link MediaPlaybackControls}. */\n playbackControlsRef?: React.Ref<MediaPlaybackControls>;\n};\n\nexport type BaseMediaProps = {\n url: string;\n className?: string;\n mediaClassName?: string;\n lazy?: boolean;\n priority?: boolean;\n skeleton?: boolean;\n onLoad?: () => void;\n onError?: () => void;\n fallback?: React.ReactNode;\n /**\n * Load-watchdog budget in ms (default LOAD_WATCHDOG_MS). A visible, loading\n * element that produces neither a load/metadata nor an error event within\n * this budget synthesizes an error so the asset resolves to a bounded\n * terminal state instead of an eternal skeleton. `0` disables.\n */\n loadTimeoutMs?: number;\n};\n\nexport type ImageViewerProps = BaseMediaProps & {\n alt?: string;\n enableZoom?: boolean;\n unloadOnExit?: boolean;\n isCircular?: boolean;\n preventGestures?: boolean;\n};\n\nexport type VideoViewerProps = BaseMediaProps & MediaPlaybackProps & {\n controls?: boolean;\n autoPlay?: boolean;\n muted?: boolean;\n loop?: boolean;\n posterUrl?: string;\n enableFullscreen?: boolean;\n unloadOnExit?: boolean;\n preload?: \"auto\" | \"metadata\" | \"none\";\n autoPlayOnVisible?: boolean;\n};\n\nexport type AudioViewerProps = BaseMediaProps & MediaPlaybackProps & {\n autoPlay?: boolean;\n loop?: boolean;\n preload?: \"auto\" | \"metadata\" | \"none\";\n onLoadChange?: (isLoading: boolean) => void;\n\n // AudioViewer always uses the package-owned control surface. Native browser\n // audio controls are intentionally not part of the public contract.\n /** Initial visualizer mode (\"line\" oscilloscope | \"bars\" EQ). A persisted user choice wins. */\n visualizerMode?: AudioVisualizerMode;\n /** localStorage key base for the minimize/mode prefs. `null` disables persistence. */\n persistKey?: string | null;\n /** App-injected action buttons rendered at the end of the player control row. */\n extraActions?: React.ReactNode;\n};\n\nexport type MediaPreviewProps = {\n url?: string | File | Blob | null;\n alt?: string;\n type?: MediaViewerType | string;\n className?: string;\n unloadOnExit?: boolean;\n isCircular?: boolean;\n /** Image type only — forwarded to ImageViewer's click-to-zoom toggle (which\n * defaults ON). Pass `false` to render a plain, non-zooming image surface.\n * Ignored for video/audio/other types. */\n enableZoom?: ImageViewerProps[\"enableZoom\"];\n priority?: boolean;\n skeleton?: boolean;\n lazy?: boolean;\n /** Video controls only. Audio always renders the canonical package-owned controls. */\n controls?: boolean;\n autoPlay?: boolean;\n muted?: boolean;\n loop?: boolean;\n posterUrl?: string;\n preload?: \"auto\" | \"metadata\" | \"none\";\n autoPlayOnVisible?: boolean;\n onLoad?: () => void;\n onError?: () => void;\n onLoadChange?: (isLoading: boolean) => void;\n\n // -------------------------------------------------------------------------\n // Playback API (additive, optional) — forwarded to the video/audio viewer.\n // Ignored for image/other types. See MediaPlaybackProps for the contract.\n // -------------------------------------------------------------------------\n onEnded?: MediaPlaybackProps[\"onEnded\"];\n onProgressSample?: MediaPlaybackProps[\"onProgressSample\"];\n startAtSeconds?: MediaPlaybackProps[\"startAtSeconds\"];\n endOverlay?: MediaPlaybackProps[\"endOverlay\"];\n playbackControlsRef?: MediaPlaybackProps[\"playbackControlsRef\"];\n\n // Audio always uses the package-owned player. These props customize that\n // single control surface and are ignored for every other media type.\n audioVisualizerMode?: AudioVisualizerMode;\n audioPersistKey?: string | null;\n audioExtraActions?: React.ReactNode;\n\n fallbackMode?: FallbackMode;\n fallbackLabel?: string;\n filename?: string;\n mime?: string;\n name?: string;\n\n // -------------------------------------------------------------------------\n // Recovery adapter (additive, optional — backward-compatible)\n // -------------------------------------------------------------------------\n /**\n * Injected diagnostic adapter. When provided, element load errors for\n * assets the app believes are live will trigger the bounded recovery state\n * machine. When omitted, the existing error fallback behavior is unchanged.\n */\n recoveryAdapter?: MediaDiagnosticAdapter;\n\n /**\n * Optional app hint about the server-side lifecycle state of the asset.\n * Drives which recovery phase is shown (e.g. processing → skeleton,\n * finalizing → spinner, failed/rejected → hard error immediately).\n */\n assetStatusHint?: AssetStatusHint;\n\n /** Load-watchdog budget forwarded to the underlying viewer — see BaseMediaProps. */\n loadTimeoutMs?: number;\n};\n\n/** Back-compat alias */\nexport type MediaViewerProps = MediaPreviewProps;\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as React from \"react\";\nimport type { MediaDiagnosticAdapter, AssetStatusHint } from \"./recovery.js\";\nimport type { AudioVisualizerMode } from \"./visualizer.js\";\n\nexport type { MediaDiagnosticAdapter, AssetStatusHint, DiagnosisResult, RecoveryState } from \"./recovery.js\";\nexport type { AudioVisualizerMode } from \"./visualizer.js\";\n\nexport type MediaViewerType = \"image\" | \"video\" | \"audio\" | \"other\";\nexport type FallbackMode = \"none\" | \"link\";\n\n/**\n * Imperative playback controls, exposed via the `playbackControlsRef` prop on\n * `VideoViewer` / `AudioViewer` (and forwarded by `MediaPreview` for\n * video/audio). Obtain via `React.useRef<MediaPlaybackControls>(null)`.\n */\nexport type MediaPlaybackControls = {\n /** Seek to 0 and play from the start. Clears any end-overlay. */\n restart(): void;\n /** Seek to the given position (seconds), clamped to [0, duration]. */\n seekTo(seconds: number): void;\n};\n\n/**\n * Why a progress sample was emitted. `periodic` is a throttled position report\n * during playback and is safe for a consumer to coalesce or drop; the other\n * three mark an exact moment the user's position changed and should be\n * committed as-is.\n */\nexport type MediaProgressSampleReason = \"periodic\" | \"pause\" | \"seeked\" | \"ended\";\n\n/**\n * Additive, fully optional playback API shared by `VideoViewer` and\n * `AudioViewer`. Every field is optional; when all are absent the viewers\n * behave exactly as before.\n */\nexport type MediaPlaybackProps = {\n /**\n * Fires when the media element ends (native `ended` event) with the play\n * cursor at duration. NOTE: when `loop` is set the browser restarts natively\n * and emits no `ended` event, so `onEnded` never fires in that mode.\n */\n onEnded?: () => void;\n\n /**\n * Playback-position reporting. Every sample carries the `reason` it was\n * emitted for ({@link MediaProgressSampleReason}) as an additive third\n * argument, so a consumer can throttle `periodic` reports while committing\n * the exact-moment ones. Contract:\n * - `periodic` — while playing, fires at most once every ~5 seconds of\n * playback (throttled off `timeupdate`). Never fires while paused, and is\n * SUPPRESSED near the start until the resume decision is settled: while no\n * `startAtSeconds` seed has been adopted and the element is still within\n * the late-resume adoption window, an autoplaying element reports nothing,\n * so a position of ~0 can never overwrite a saved one.\n * - `pause` / `seeked` / `ended` — fires once immediately on that event with\n * the exact position, never suppressed. The programmatic resume seek that\n * applies `startAtSeconds` reports as `seeked` with the resumed position.\n * `durationSeconds` is `0` until the element's duration is known.\n *\n * Active-element retirement: samples come only from the ACTIVE media-element\n * generation. When the viewer tears an element down (unload off-screen,\n * unmount, source replacement), it retires the element first — the still-live\n * cursor is committed once as a final `pause` sample (skipped when the last\n * commit already reported that exact position, when playback never started,\n * or while still inside the pre-seed suppression window) — and the reset\n * events the teardown itself produces (browsers snap the cursor to 0 and can\n * fire pause/seeked/timeupdate) are ignored. A genuine user seek or restart\n * to exactly 0 on the active element still reports normally.\n */\n onProgressSample?: (\n currentTimeSeconds: number,\n durationSeconds: number,\n reason: MediaProgressSampleReason,\n ) => void;\n\n /**\n * Initial playback position (seconds), applied once the element has metadata.\n * May arrive late (e.g. from an async prefs read that resolves after the\n * viewer mounted): a value that appears while playback has not yet begun is\n * adopted and applied to the already-loaded element. Once real playback has\n * begun a later value is ignored — it never yanks playing media.\n * Survives the `unloadOnExit` unload/remount cycle: once playback has started\n * the viewer tracks the live position internally and resumes from the LAST\n * KNOWN position on remount — it does not re-apply this prop and does not\n * restart at 0.\n */\n startAtSeconds?: number;\n\n /**\n * Rendered as an overlay covering the media area when the media has ended.\n * Removed when playback restarts (native replay or imperative `restart()`).\n * The overlay only intercepts pointer events while shown and renders above\n * the element. In a native-fullscreen exit state the overlay reappears over\n * the inline element (it is part of normal document flow, not the fullscreen\n * layer).\n */\n endOverlay?: React.ReactNode;\n\n /** Imperative controls handle — see {@link MediaPlaybackControls}. */\n playbackControlsRef?: React.Ref<MediaPlaybackControls>;\n};\n\nexport type BaseMediaProps = {\n url: string;\n className?: string;\n mediaClassName?: string;\n lazy?: boolean;\n priority?: boolean;\n skeleton?: boolean;\n onLoad?: () => void;\n onError?: () => void;\n fallback?: React.ReactNode;\n /**\n * Load-watchdog budget in ms (default LOAD_WATCHDOG_MS). A visible, loading\n * element that produces neither a load/metadata nor an error event within\n * this budget synthesizes an error so the asset resolves to a bounded\n * terminal state instead of an eternal skeleton. `0` disables.\n */\n loadTimeoutMs?: number;\n};\n\nexport type ImageViewerProps = BaseMediaProps & {\n alt?: string;\n enableZoom?: boolean;\n unloadOnExit?: boolean;\n isCircular?: boolean;\n preventGestures?: boolean;\n};\n\nexport type VideoViewerProps = BaseMediaProps & MediaPlaybackProps & {\n controls?: boolean;\n autoPlay?: boolean;\n muted?: boolean;\n loop?: boolean;\n posterUrl?: string;\n enableFullscreen?: boolean;\n unloadOnExit?: boolean;\n preload?: \"auto\" | \"metadata\" | \"none\";\n autoPlayOnVisible?: boolean;\n};\n\nexport type AudioViewerProps = BaseMediaProps & MediaPlaybackProps & {\n autoPlay?: boolean;\n loop?: boolean;\n preload?: \"auto\" | \"metadata\" | \"none\";\n onLoadChange?: (isLoading: boolean) => void;\n\n // AudioViewer always uses the package-owned control surface. Native browser\n // audio controls are intentionally not part of the public contract.\n /** Initial visualizer mode (\"line\" oscilloscope | \"bars\" EQ). A persisted user choice wins. */\n visualizerMode?: AudioVisualizerMode;\n /** localStorage key base for the minimize/mode prefs. `null` disables persistence. */\n persistKey?: string | null;\n /** App-injected action buttons rendered at the end of the player control row. */\n extraActions?: React.ReactNode;\n};\n\nexport type MediaPreviewProps = {\n url?: string | File | Blob | null;\n /**\n * The URL is still being prepared (for example a short-lived access grant is\n * being minted). While true and no `url` is available, the viewer renders a\n * loading state instead of the empty fallback, so \"not yet\" never reads as\n * \"nothing here\".\n */\n urlPending?: boolean;\n alt?: string;\n type?: MediaViewerType | string;\n className?: string;\n unloadOnExit?: boolean;\n isCircular?: boolean;\n /** Image type only — forwarded to ImageViewer's click-to-zoom toggle (which\n * defaults ON). Pass `false` to render a plain, non-zooming image surface.\n * Ignored for video/audio/other types. */\n enableZoom?: ImageViewerProps[\"enableZoom\"];\n priority?: boolean;\n skeleton?: boolean;\n lazy?: boolean;\n /** Video controls only. Audio always renders the canonical package-owned controls. */\n controls?: boolean;\n autoPlay?: boolean;\n muted?: boolean;\n loop?: boolean;\n posterUrl?: string;\n preload?: \"auto\" | \"metadata\" | \"none\";\n autoPlayOnVisible?: boolean;\n onLoad?: () => void;\n onError?: () => void;\n onLoadChange?: (isLoading: boolean) => void;\n\n // -------------------------------------------------------------------------\n // Playback API (additive, optional) — forwarded to the video/audio viewer.\n // Ignored for image/other types. See MediaPlaybackProps for the contract.\n // -------------------------------------------------------------------------\n onEnded?: MediaPlaybackProps[\"onEnded\"];\n onProgressSample?: MediaPlaybackProps[\"onProgressSample\"];\n startAtSeconds?: MediaPlaybackProps[\"startAtSeconds\"];\n endOverlay?: MediaPlaybackProps[\"endOverlay\"];\n playbackControlsRef?: MediaPlaybackProps[\"playbackControlsRef\"];\n\n // Audio always uses the package-owned player. These props customize that\n // single control surface and are ignored for every other media type.\n audioVisualizerMode?: AudioVisualizerMode;\n audioPersistKey?: string | null;\n audioExtraActions?: React.ReactNode;\n\n fallbackMode?: FallbackMode;\n fallbackLabel?: string;\n filename?: string;\n mime?: string;\n name?: string;\n\n // -------------------------------------------------------------------------\n // Recovery adapter (additive, optional — backward-compatible)\n // -------------------------------------------------------------------------\n /**\n * Injected diagnostic adapter. When provided, element load errors for\n * assets the app believes are live will trigger the bounded recovery state\n * machine. When omitted, the existing error fallback behavior is unchanged.\n */\n recoveryAdapter?: MediaDiagnosticAdapter;\n\n /**\n * Optional app hint about the server-side lifecycle state of the asset.\n * Drives which recovery phase is shown (e.g. processing → skeleton,\n * finalizing → spinner, failed/rejected → hard error immediately).\n */\n assetStatusHint?: AssetStatusHint;\n\n /** Load-watchdog budget forwarded to the underlying viewer — see BaseMediaProps. */\n loadTimeoutMs?: number;\n};\n\n/** Back-compat alias */\nexport type MediaViewerProps = MediaPreviewProps;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/media-viewer",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.12",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@ttt-productions/media-schemas": "^0.14.5",
|
|
37
|
-
"@ttt-productions/ui-core": "^0.11.
|
|
37
|
+
"@ttt-productions/ui-core": "^0.11.15"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": ">=19.0.0",
|
|
@@ -40,6 +40,17 @@
|
|
|
40
40
|
background: hsl(var(--muted, 220 14% 96%));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/* Pending state: the URL is still being prepared. Same seat as the empty state,
|
|
44
|
+
but the spinner keeps its own size and color. */
|
|
45
|
+
.mv-pending-fallback {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 100%;
|
|
51
|
+
background: hsl(var(--muted, 220 14% 96%));
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
.mv-empty-fallback svg {
|
|
44
55
|
width: 50%;
|
|
45
56
|
height: 50%;
|