@mieweb/ui 0.11.0-dev.36 → 0.11.0-dev.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/agent/README.md +1 -1
  2. package/agent/agents/mieweb-ui-design.agent.md +12 -7
  3. package/agent/agents/mieweb-ui-migration.agent.md +144 -0
  4. package/agent/skills/mieweb-ui-catalog/SKILL.md +8 -6
  5. package/agent/skills/mieweb-ui-catalog/selection-guidance.md +9 -5
  6. package/dist/{chunk-TUYWCUKH.js → chunk-4LKAZW2W.js} +7 -3
  7. package/dist/chunk-4LKAZW2W.js.map +1 -0
  8. package/dist/{chunk-MNXIN4MS.js → chunk-5WA6IYZD.js} +60 -2
  9. package/dist/chunk-5WA6IYZD.js.map +1 -0
  10. package/dist/{chunk-DT3X3RBM.js → chunk-7U7SONM5.js} +3 -3
  11. package/dist/{chunk-DT3X3RBM.js.map → chunk-7U7SONM5.js.map} +1 -1
  12. package/dist/{chunk-OGFSFUIX.cjs → chunk-AHYDJN4H.cjs} +501 -120
  13. package/dist/chunk-AHYDJN4H.cjs.map +1 -0
  14. package/dist/{chunk-C4GEPUE7.js → chunk-CKECOCYJ.js} +2 -2
  15. package/dist/{chunk-C4GEPUE7.js.map → chunk-CKECOCYJ.js.map} +1 -1
  16. package/dist/{chunk-V5VPECJI.js → chunk-CRR7AE5D.js} +11 -7
  17. package/dist/chunk-CRR7AE5D.js.map +1 -0
  18. package/dist/chunk-D3FNZMOB.cjs +4 -0
  19. package/dist/{chunk-GLXTVCHN.cjs.map → chunk-D3FNZMOB.cjs.map} +1 -1
  20. package/dist/{chunk-ZK2R6UGF.cjs → chunk-DCV34MEF.cjs} +4 -4
  21. package/dist/{chunk-ZK2R6UGF.cjs.map → chunk-DCV34MEF.cjs.map} +1 -1
  22. package/dist/chunk-G4VQEQGW.js +3 -0
  23. package/dist/{chunk-RISZWWEQ.js.map → chunk-G4VQEQGW.js.map} +1 -1
  24. package/dist/{chunk-S3LTUSO5.cjs → chunk-GG3IK5TL.cjs} +7 -7
  25. package/dist/{chunk-S3LTUSO5.cjs.map → chunk-GG3IK5TL.cjs.map} +1 -1
  26. package/dist/{chunk-QHCAWDMG.cjs → chunk-HDZQLTBP.cjs} +7 -3
  27. package/dist/chunk-HDZQLTBP.cjs.map +1 -0
  28. package/dist/chunk-JCIQIR5V.js +546 -0
  29. package/dist/chunk-JCIQIR5V.js.map +1 -0
  30. package/dist/{chunk-AIO2GCTL.js → chunk-JUY2JLFL.js} +501 -120
  31. package/dist/chunk-JUY2JLFL.js.map +1 -0
  32. package/dist/{chunk-2S76TRBJ.cjs → chunk-JUYBJLPI.cjs} +4 -4
  33. package/dist/{chunk-2S76TRBJ.cjs.map → chunk-JUYBJLPI.cjs.map} +1 -1
  34. package/dist/{chunk-UDUYZDTA.js → chunk-JWMMWNTT.js} +3 -3
  35. package/dist/{chunk-UDUYZDTA.js.map → chunk-JWMMWNTT.js.map} +1 -1
  36. package/dist/{chunk-RM4CBS7Y.js → chunk-LQ22CY7X.js} +4 -4
  37. package/dist/{chunk-RM4CBS7Y.js.map → chunk-LQ22CY7X.js.map} +1 -1
  38. package/dist/{chunk-AKMTPCLC.cjs → chunk-LTCHD3K5.cjs} +2 -2
  39. package/dist/{chunk-AKMTPCLC.cjs.map → chunk-LTCHD3K5.cjs.map} +1 -1
  40. package/dist/{chunk-GDKOYVX3.cjs → chunk-NZUQL42A.cjs} +11 -7
  41. package/dist/chunk-NZUQL42A.cjs.map +1 -0
  42. package/dist/chunk-QAEAC7HO.cjs +4 -0
  43. package/dist/chunk-QAEAC7HO.cjs.map +1 -0
  44. package/dist/chunk-SCGXD334.js +3 -0
  45. package/dist/chunk-SCGXD334.js.map +1 -0
  46. package/dist/chunk-TZKBW4GA.cjs +578 -0
  47. package/dist/chunk-TZKBW4GA.cjs.map +1 -0
  48. package/dist/{chunk-47AOS5EC.cjs → chunk-XX6PNPF2.cjs} +60 -2
  49. package/dist/chunk-XX6PNPF2.cjs.map +1 -0
  50. package/dist/components/AudioPlayer/index.cjs +7 -6
  51. package/dist/components/AudioPlayer/index.js +2 -1
  52. package/dist/components/MediaEditor/index.cjs +10 -9
  53. package/dist/components/MediaEditor/index.js +5 -4
  54. package/dist/components/MediaFeed/index.cjs +8 -5
  55. package/dist/components/MediaFeed/index.d.cts +33 -0
  56. package/dist/components/MediaFeed/index.d.ts +33 -0
  57. package/dist/components/MediaFeed/index.js +7 -4
  58. package/dist/components/MediaPlayer/index.cjs +13 -6
  59. package/dist/components/MediaPlayer/index.d.cts +103 -5
  60. package/dist/components/MediaPlayer/index.d.ts +103 -5
  61. package/dist/components/MediaPlayer/index.js +6 -3
  62. package/dist/components/Slider/index.cjs +5 -5
  63. package/dist/components/Slider/index.d.cts +6 -0
  64. package/dist/components/Slider/index.d.ts +6 -0
  65. package/dist/components/Slider/index.js +1 -1
  66. package/dist/components/SliderCalculator/index.cjs +4 -4
  67. package/dist/components/SliderCalculator/index.js +2 -2
  68. package/dist/components/SuperChat/index.cjs +16 -14
  69. package/dist/components/SuperChat/index.cjs.map +1 -1
  70. package/dist/components/SuperChat/index.d.cts +2 -2
  71. package/dist/components/SuperChat/index.d.ts +2 -2
  72. package/dist/components/SuperChat/index.js +8 -6
  73. package/dist/components/SuperChat/index.js.map +1 -1
  74. package/dist/components/SuperChat/plugins/index.d.cts +2 -2
  75. package/dist/components/SuperChat/plugins/index.d.ts +2 -2
  76. package/dist/hooks/index.cjs +2 -2
  77. package/dist/hooks/index.js +1 -1
  78. package/dist/index.cjs +73 -68
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.d.cts +187 -90
  81. package/dist/index.d.ts +187 -90
  82. package/dist/index.js +14 -13
  83. package/dist/index.js.map +1 -1
  84. package/dist/styles.css +1 -1
  85. package/dist/tailwind-preset.cjs +4 -4
  86. package/dist/tailwind-preset.js +1 -1
  87. package/dist/{types-DSWbrVkM.d.cts → types-BfVolads.d.cts} +33 -0
  88. package/dist/{types-DSWbrVkM.d.ts → types-BfVolads.d.ts} +33 -0
  89. package/package.json +1 -1
  90. package/dist/chunk-47AOS5EC.cjs.map +0 -1
  91. package/dist/chunk-AIO2GCTL.js.map +0 -1
  92. package/dist/chunk-ECKIFQSD.cjs +0 -183
  93. package/dist/chunk-ECKIFQSD.cjs.map +0 -1
  94. package/dist/chunk-GDKOYVX3.cjs.map +0 -1
  95. package/dist/chunk-GLXTVCHN.cjs +0 -4
  96. package/dist/chunk-MNXIN4MS.js.map +0 -1
  97. package/dist/chunk-OGFSFUIX.cjs.map +0 -1
  98. package/dist/chunk-QHCAWDMG.cjs.map +0 -1
  99. package/dist/chunk-R5P7NAJQ.js +0 -159
  100. package/dist/chunk-R5P7NAJQ.js.map +0 -1
  101. package/dist/chunk-RISZWWEQ.js +0 -3
  102. package/dist/chunk-TUYWCUKH.js.map +0 -1
  103. package/dist/chunk-V5VPECJI.js.map +0 -1
@@ -74,6 +74,7 @@ function useMediaTransport(options = {}) {
74
74
  };
75
75
  const onDurationChange = () => setDurationMs(toMs(element.duration));
76
76
  const onTimeUpdateEvt = () => emitTime();
77
+ const onRateChange = () => setPlaybackRateState(element.playbackRate);
77
78
  const onPlay = () => setState("playing");
78
79
  const onPlaying = () => setState("playing");
79
80
  const onPause = () => setState("paused");
@@ -96,6 +97,7 @@ function useMediaTransport(options = {}) {
96
97
  element.addEventListener("loadedmetadata", onLoadedMetadata);
97
98
  element.addEventListener("durationchange", onDurationChange);
98
99
  element.addEventListener("timeupdate", onTimeUpdateEvt);
100
+ element.addEventListener("ratechange", onRateChange);
99
101
  element.addEventListener("play", onPlay);
100
102
  element.addEventListener("playing", onPlaying);
101
103
  element.addEventListener("pause", onPause);
@@ -110,6 +112,7 @@ function useMediaTransport(options = {}) {
110
112
  element.removeEventListener("loadedmetadata", onLoadedMetadata);
111
113
  element.removeEventListener("durationchange", onDurationChange);
112
114
  element.removeEventListener("timeupdate", onTimeUpdateEvt);
115
+ element.removeEventListener("ratechange", onRateChange);
113
116
  element.removeEventListener("play", onPlay);
114
117
  element.removeEventListener("playing", onPlaying);
115
118
  element.removeEventListener("pause", onPause);
@@ -129,7 +132,8 @@ function useMediaTransport(options = {}) {
129
132
  const togglePlay = react.useCallback(() => {
130
133
  const el = elementRef.current;
131
134
  if (!el) return;
132
- if (el.paused) void el.play();
135
+ if (el.paused) void el.play()?.catch(() => {
136
+ });
133
137
  else el.pause();
134
138
  }, []);
135
139
  const seekToMs = react.useCallback((timeMs) => {
@@ -173,5 +177,5 @@ function useMediaTransport(options = {}) {
173
177
  }
174
178
 
175
179
  exports.useMediaTransport = useMediaTransport;
176
- //# sourceMappingURL=chunk-QHCAWDMG.cjs.map
177
- //# sourceMappingURL=chunk-QHCAWDMG.cjs.map
180
+ //# sourceMappingURL=chunk-HDZQLTBP.cjs.map
181
+ //# sourceMappingURL=chunk-HDZQLTBP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useMediaTransport.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"chunk-HDZQLTBP.cjs","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type MediaTransportState =\n | 'idle'\n | 'loading'\n | 'playing'\n | 'paused'\n | 'error';\n\nexport interface UseMediaTransportOptions {\n /** Fired on every native `timeupdate` and on metadata load, in milliseconds. */\n onTimeUpdate?: (currentTimeMs: number, durationMs: number) => void;\n /** Fired whenever the derived transport state changes. */\n onStateChange?: (state: MediaTransportState) => void;\n /** Fired when playback reaches the end. */\n onEnded?: () => void;\n /** Fired on a media error, with a descriptive Error. */\n onError?: (error: Error) => void;\n /** Initial playback rate (default `1`). Kept in sync with the element. */\n initialRate?: number;\n /** Transport state to enter when playback ends (default `'paused'`). */\n endedState?: Extract<MediaTransportState, 'idle' | 'paused'>;\n /** Reset the position to `0` when playback ends (default `false`). */\n resetTimeOnEnd?: boolean;\n /** Human-readable label used in the error message (default `'media'`). */\n errorLabel?: string;\n}\n\nexport interface UseMediaTransportReturn {\n /**\n * Callback ref for the `<audio>`/`<video>` element to control. Pass this to\n * the element's `ref` (or call it imperatively). Transport listeners attach\n * when an element mounts and detach when it unmounts.\n */\n setMediaElement: (element: HTMLMediaElement | null) => void;\n /** The currently attached media element, or `null`. */\n mediaElement: HTMLMediaElement | null;\n\n /** Derived transport state. */\n state: MediaTransportState;\n /** Current playback position in milliseconds. */\n currentTimeMs: number;\n /** Media duration in milliseconds (`0` until known / non-finite). */\n durationMs: number;\n /** Current playback rate. */\n playbackRate: number;\n /** `true` while `state === 'playing'`. */\n isPlaying: boolean;\n /** `true` while `state === 'loading'`. */\n isLoading: boolean;\n\n /** Start playback. */\n play: () => void;\n /** Pause playback. */\n pause: () => void;\n /** Toggle between play and pause. */\n togglePlay: () => void;\n /** Seek to a position in milliseconds. */\n seekToMs: (timeMs: number) => void;\n /** Set the playback rate multiplier. */\n setPlaybackRate: (rate: number) => void;\n /** Read the current position in milliseconds. */\n getCurrentTimeMs: () => number;\n /** Read the duration in milliseconds (`0` when unknown). */\n getDurationMs: () => number;\n /** Whether playback is currently paused (reads the element directly). */\n isPaused: () => boolean;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nconst toMs = (seconds: number): number =>\n Number.isFinite(seconds) ? seconds * 1000 : 0;\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Headless playback transport for a native `HTMLMediaElement`.\n *\n * Owns the derived transport state (idle/loading/playing/paused/error),\n * position, duration and playback rate, and exposes imperative controls — all\n * in **milliseconds**. It never creates or renders an element: pass the\n * returned {@link UseMediaTransportReturn.setMediaElement} callback to an\n * `<audio>`/`<video>` `ref` and the hook wires the native listeners.\n *\n * Shared by `MediaPlayer` (ms API) and TranscriptPlayer's `usePlayback` (seconds,\n * converted at the boundary). The WaveSurfer waveform variant is\n * out of scope; this hook targets native media transport only.\n *\n * @example\n * ```tsx\n * function Player({ src }: { src: string }) {\n * const t = useMediaTransport({ onEnded: () => console.log('done') });\n * return (\n * <div>\n * <video ref={t.setMediaElement} src={src} controls />\n * <button onClick={t.togglePlay}>{t.isPlaying ? 'Pause' : 'Play'}</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaTransport(\n options: UseMediaTransportOptions = {}\n): UseMediaTransportReturn {\n const {\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n initialRate = 1,\n endedState = 'paused',\n resetTimeOnEnd = false,\n errorLabel = 'media',\n } = options;\n\n const [element, setElementState] = useState<HTMLMediaElement | null>(null);\n const [state, setStateRaw] = useState<MediaTransportState>('idle');\n const stateRef = useRef<MediaTransportState>('idle');\n const [currentTimeMs, setCurrentTimeMs] = useState(0);\n const [durationMs, setDurationMs] = useState(0);\n const [playbackRate, setPlaybackRateState] = useState(initialRate);\n\n const elementRef = useRef<HTMLMediaElement | null>(null);\n\n // Keep the latest callbacks/options in a ref so the listener effect does not\n // re-subscribe on every render when callers pass inline functions.\n const handlers = useRef({\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n endedState,\n resetTimeOnEnd,\n errorLabel,\n });\n handlers.current = {\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n endedState,\n resetTimeOnEnd,\n errorLabel,\n };\n\n const setState = useCallback((next: MediaTransportState) => {\n if (stateRef.current === next) return;\n stateRef.current = next;\n setStateRaw(next);\n // Consumers often update parent state here. Keep that notification outside\n // React's state updater, which may run during render or twice in StrictMode.\n handlers.current.onStateChange?.(next);\n }, []);\n\n const setMediaElement = useCallback((el: HTMLMediaElement | null) => {\n elementRef.current = el;\n setElementState(el);\n }, []);\n\n // Attach native transport listeners whenever an element mounts.\n useEffect(() => {\n if (!element) {\n // A detached element (e.g. the source was removed) leaves nothing playing or loaded;\n // an error stays visible to hosts that swap the element for an error panel.\n if (stateRef.current !== 'error') setState('idle');\n setDurationMs(0);\n setCurrentTimeMs(0);\n return;\n }\n\n const emitTime = () => {\n const ms = toMs(element.currentTime);\n const dur = toMs(element.duration);\n setCurrentTimeMs(ms);\n handlers.current.onTimeUpdate?.(ms, dur);\n };\n\n const onLoadStart = () => setState('loading');\n // canplay can also follow a seek mid-playback; only leave the loading state here.\n const onCanPlay = () => {\n if (stateRef.current === 'loading')\n setState(element.paused ? 'idle' : 'playing');\n };\n const onLoadedMetadata = () => {\n setDurationMs(toMs(element.duration));\n emitTime();\n };\n // MediaRecorder webm often reports Infinity until the end is known.\n const onDurationChange = () => setDurationMs(toMs(element.duration));\n const onTimeUpdateEvt = () => emitTime();\n // The speed can change outside these controls (iOS's own full screen player).\n const onRateChange = () => setPlaybackRateState(element.playbackRate);\n const onPlay = () => setState('playing');\n const onPlaying = () => setState('playing');\n const onPause = () => setState('paused');\n const onEndedEvt = () => {\n setState(handlers.current.endedState);\n if (handlers.current.resetTimeOnEnd) {\n element.currentTime = 0;\n setCurrentTimeMs(0);\n }\n handlers.current.onEnded?.();\n };\n const onErrorEvt = () => {\n setState('error');\n handlers.current.onError?.(\n new Error(`Failed to load ${handlers.current.errorLabel}`)\n );\n };\n\n element.addEventListener('loadstart', onLoadStart);\n element.addEventListener('canplay', onCanPlay);\n element.addEventListener('loadedmetadata', onLoadedMetadata);\n element.addEventListener('durationchange', onDurationChange);\n element.addEventListener('timeupdate', onTimeUpdateEvt);\n element.addEventListener('ratechange', onRateChange);\n element.addEventListener('play', onPlay);\n element.addEventListener('playing', onPlaying);\n element.addEventListener('pause', onPause);\n element.addEventListener('ended', onEndedEvt);\n element.addEventListener('error', onErrorEvt);\n\n // Sync on attach; a new element must not inherit the previous one's duration or play state.\n setDurationMs(toMs(element.duration));\n if (element.paused && stateRef.current === 'playing') setState('paused');\n // e.g. a 404 that failed before hydration attached these listeners.\n if (element.error) onErrorEvt();\n\n return () => {\n element.removeEventListener('loadstart', onLoadStart);\n element.removeEventListener('canplay', onCanPlay);\n element.removeEventListener('loadedmetadata', onLoadedMetadata);\n element.removeEventListener('durationchange', onDurationChange);\n element.removeEventListener('timeupdate', onTimeUpdateEvt);\n element.removeEventListener('ratechange', onRateChange);\n element.removeEventListener('play', onPlay);\n element.removeEventListener('playing', onPlaying);\n element.removeEventListener('pause', onPause);\n element.removeEventListener('ended', onEndedEvt);\n element.removeEventListener('error', onErrorEvt);\n };\n }, [element, setState]);\n\n // Keep the element's playback rate in sync with hook state.\n useEffect(() => {\n if (element) element.playbackRate = playbackRate;\n }, [element, playbackRate]);\n\n const play = useCallback(() => {\n void elementRef.current?.play();\n }, []);\n\n const pause = useCallback(() => {\n elementRef.current?.pause();\n }, []);\n\n const togglePlay = useCallback(() => {\n const el = elementRef.current;\n if (!el) return;\n // A refused start (autoplay policy) leaves it paused; nothing to report.\n if (el.paused) void el.play()?.catch(() => {});\n else el.pause();\n }, []);\n\n const seekToMs = useCallback((timeMs: number) => {\n const el = elementRef.current;\n if (el) {\n el.currentTime = timeMs / 1000;\n setCurrentTimeMs(timeMs);\n }\n }, []);\n\n const setPlaybackRate = useCallback((rate: number) => {\n setPlaybackRateState(rate);\n if (elementRef.current) elementRef.current.playbackRate = rate;\n }, []);\n\n const getCurrentTimeMs = useCallback(\n () => toMs(elementRef.current?.currentTime ?? 0),\n []\n );\n\n const getDurationMs = useCallback(\n () => toMs(elementRef.current?.duration ?? 0),\n []\n );\n\n const isPaused = useCallback(() => elementRef.current?.paused ?? true, []);\n\n return {\n setMediaElement,\n mediaElement: element,\n state,\n currentTimeMs,\n durationMs,\n playbackRate,\n isPlaying: state === 'playing',\n isLoading: state === 'loading',\n play,\n pause,\n togglePlay,\n seekToMs,\n setPlaybackRate,\n getCurrentTimeMs,\n getDurationMs,\n isPaused,\n };\n}\n"]}
@@ -0,0 +1,546 @@
1
+ import { Slider } from './chunk-CRR7AE5D.js';
2
+ import { formatTime } from './chunk-CKECOCYJ.js';
3
+ import { useMediaTransport } from './chunk-4LKAZW2W.js';
4
+ import { Button } from './chunk-WX2XB6FP.js';
5
+ import { cn } from './chunk-TQONQSJO.js';
6
+ import * as React from 'react';
7
+ import { Play, Pause, Captions, CaptionsOff, VolumeX, Volume2, PictureInPicture2, Minimize, Maximize } from 'lucide-react';
8
+ import { jsxs, jsx } from 'react/jsx-runtime';
9
+ import { cva } from 'class-variance-authority';
10
+
11
+ // src/components/MediaPlayer/overlayButton.ts
12
+ var overlayButtonClass = [
13
+ "rounded-full bg-black/45 text-white backdrop-blur-sm",
14
+ "hover:bg-black/65 hover:text-white active:bg-black/75",
15
+ "dark:text-white dark:hover:bg-black/65 dark:active:bg-black/75",
16
+ "focus-visible:ring-white focus-visible:ring-offset-0",
17
+ "pointer-fine:h-9 pointer-fine:w-9"
18
+ ].join(" ");
19
+ var playBadgeClass = "flex h-14 w-14 items-center justify-center rounded-full bg-black/60 text-white";
20
+ var playBadgeIconClass = "h-6 w-6 translate-x-0.5 fill-current";
21
+
22
+ // src/components/MediaPlayer/pictureInPicture.ts
23
+ var PIP_EVENTS = [
24
+ "enterpictureinpicture",
25
+ "leavepictureinpicture",
26
+ "webkitpresentationmodechanged"
27
+ ];
28
+ var PIP_EXIT_TIMEOUT_MS = 1e3;
29
+ function isInPictureInPicture(video) {
30
+ if (!video || typeof document === "undefined") return false;
31
+ return document.pictureInPictureElement === video || video.webkitPresentationMode === "picture-in-picture";
32
+ }
33
+ function exitPictureInPicture(video) {
34
+ if (!video || !isInPictureInPicture(video)) return Promise.resolve();
35
+ if (document.pictureInPictureElement === video)
36
+ return document.exitPictureInPicture().catch(() => {
37
+ });
38
+ return new Promise((resolve) => {
39
+ const done = () => {
40
+ clearTimeout(timer);
41
+ video.removeEventListener("webkitpresentationmodechanged", done);
42
+ resolve();
43
+ };
44
+ const timer = setTimeout(done, PIP_EXIT_TIMEOUT_MS);
45
+ video.addEventListener("webkitpresentationmodechanged", done, {
46
+ once: true
47
+ });
48
+ video.webkitSetPresentationMode?.("inline");
49
+ });
50
+ }
51
+ function canPictureInPicture(video) {
52
+ if (!video || typeof document === "undefined") return false;
53
+ if (video.disablePictureInPicture) return false;
54
+ return document.pictureInPictureEnabled || !!video.webkitSupportsPresentationMode?.("picture-in-picture");
55
+ }
56
+ var defaultMediaControlsLabels = {
57
+ play: "Play",
58
+ pause: "Pause",
59
+ mute: "Mute",
60
+ unmute: "Unmute",
61
+ seek: "Seek",
62
+ fullscreen: "Full screen",
63
+ exitFullscreen: "Exit full screen",
64
+ pictureInPicture: "Picture in picture",
65
+ exitPictureInPicture: "Exit picture in picture",
66
+ time: (current, duration) => `${current} / ${duration}`,
67
+ rate: (rate) => `${rate}\xD7`,
68
+ speed: (rate) => `Playback speed ${rate}`,
69
+ showCaptions: "Show captions",
70
+ hideCaptions: "Hide captions"
71
+ };
72
+ var defaultPlaybackRates = [1, 1.5, 2];
73
+ var SMALL_PLAYER_HIDDEN = "@max-xs:hidden";
74
+ var SEEK_STEP_MS = 100;
75
+ var KEYBOARD_SEEK_FRACTION = 0.02;
76
+ var CUE_LINE_HEIGHT_FRACTION = 0.05;
77
+ var DEFAULT_CUE_LINE = -4;
78
+ var formatMs = (ms) => formatTime(ms / 1e3);
79
+ function cueLineAboveBar(video, bar) {
80
+ const lineHeight = video.getBoundingClientRect().height * CUE_LINE_HEIGHT_FRACTION;
81
+ if (!bar || lineHeight <= 0) return DEFAULT_CUE_LINE;
82
+ const controls = bar.getBoundingClientRect().height - (parseFloat(getComputedStyle(bar).paddingTop) || 0);
83
+ return -(Math.ceil(Math.max(controls, 0) / lineHeight) + 1);
84
+ }
85
+ function useInPictureInPicture(video) {
86
+ const [inPip, setInPip] = React.useState(false);
87
+ React.useEffect(() => {
88
+ if (!video) return;
89
+ const update = () => setInPip(isInPictureInPicture(video));
90
+ update();
91
+ for (const event of PIP_EVENTS) video.addEventListener(event, update);
92
+ return () => {
93
+ for (const event of PIP_EVENTS) video.removeEventListener(event, update);
94
+ };
95
+ }, [video]);
96
+ return inPip;
97
+ }
98
+ function useCaptionTracks(video, barRef) {
99
+ const [tracks, setTracks] = React.useState([]);
100
+ const [showing, setShowing] = React.useState(false);
101
+ React.useEffect(() => {
102
+ const list = video?.textTracks;
103
+ if (!list) {
104
+ setTracks([]);
105
+ setShowing(false);
106
+ return;
107
+ }
108
+ const lifted = /* @__PURE__ */ new Map();
109
+ const place = (track, cue, line) => {
110
+ cue.line = line;
111
+ if (track && Array.from(track.activeCues ?? []).includes(cue)) {
112
+ track.removeCue(cue);
113
+ track.addCue(cue);
114
+ }
115
+ };
116
+ const liftCues = (track) => {
117
+ const line = video ? cueLineAboveBar(video, barRef.current) : DEFAULT_CUE_LINE;
118
+ for (const each of Array.from(track.cues ?? [])) {
119
+ if (!("line" in each)) continue;
120
+ const cue = each;
121
+ if (cue.line !== "auto" && lifted.get(cue) !== cue.line) continue;
122
+ if (cue.line === line) continue;
123
+ lifted.set(cue, line);
124
+ place(track, cue, line);
125
+ }
126
+ };
127
+ const lift = (event) => liftCues(event.target);
128
+ let watched = [];
129
+ const update = () => {
130
+ const captions = Array.from(list).filter(
131
+ (track) => track.kind === "captions" || track.kind === "subtitles"
132
+ );
133
+ for (const track of watched)
134
+ track.removeEventListener?.("cuechange", lift);
135
+ for (const track of captions) track.addEventListener?.("cuechange", lift);
136
+ watched = captions;
137
+ setTracks(captions);
138
+ setShowing(captions.some((track) => track.mode === "showing"));
139
+ };
140
+ update();
141
+ const events = ["addtrack", "removetrack", "change"];
142
+ for (const name of events) list.addEventListener?.(name, update);
143
+ const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => watched.forEach(liftCues));
144
+ for (const element of [video, barRef.current])
145
+ if (element) resize?.observe(element);
146
+ return () => {
147
+ resize?.disconnect();
148
+ for (const name of events) list.removeEventListener?.(name, update);
149
+ for (const track of watched)
150
+ track.removeEventListener?.("cuechange", lift);
151
+ for (const [cue, line] of lifted)
152
+ if (cue.line === line) place(cue.track, cue, "auto");
153
+ };
154
+ }, [video, barRef]);
155
+ return { tracks, showing };
156
+ }
157
+ function useMuted(element) {
158
+ const [muted, setMuted] = React.useState(element?.muted ?? false);
159
+ React.useEffect(() => {
160
+ if (!element) return;
161
+ const update = () => setMuted(element.muted);
162
+ update();
163
+ element.addEventListener("volumechange", update);
164
+ return () => element.removeEventListener("volumechange", update);
165
+ }, [element]);
166
+ return muted;
167
+ }
168
+ function useIsFullscreen(target) {
169
+ const [fullscreen, setFullscreen] = React.useState(false);
170
+ React.useEffect(() => {
171
+ if (typeof document === "undefined") return;
172
+ const update = () => setFullscreen(
173
+ !!target?.current && document.fullscreenElement === target.current
174
+ );
175
+ update();
176
+ document.addEventListener("fullscreenchange", update);
177
+ return () => document.removeEventListener("fullscreenchange", update);
178
+ }, [target]);
179
+ return fullscreen;
180
+ }
181
+ var MediaControls = React.forwardRef(function MediaControls2({
182
+ transport,
183
+ fullscreenTarget,
184
+ labels: labelOverrides,
185
+ playbackRates = defaultPlaybackRates,
186
+ className,
187
+ ...rest
188
+ }, ref) {
189
+ const labels = { ...defaultMediaControlsLabels, ...labelOverrides };
190
+ const video = transport.mediaElement;
191
+ const muted = useMuted(video);
192
+ const isFullscreen = useIsFullscreen(fullscreenTarget);
193
+ const inPip = useInPictureInPicture(video);
194
+ const barRef = React.useRef(null);
195
+ const captions = useCaptionTracks(video, barRef);
196
+ const [support, setSupport] = React.useState({ box: false, pip: false });
197
+ React.useEffect(() => {
198
+ const read = () => setSupport({
199
+ box: document.fullscreenEnabled && !!fullscreenTarget,
200
+ pip: canPictureInPicture(video)
201
+ });
202
+ read();
203
+ video?.addEventListener("loadedmetadata", read);
204
+ return () => video?.removeEventListener("loadedmetadata", read);
205
+ }, [video, fullscreenTarget]);
206
+ const playing = transport.isPlaying;
207
+ const durationMs = transport.durationMs;
208
+ const togglePlay = transport.togglePlay;
209
+ const toggleMuted = () => {
210
+ if (video) video.muted = !video.muted;
211
+ };
212
+ const canFullscreen = support.box || !!video?.webkitEnterFullscreen && !!video.webkitSupportsFullscreen;
213
+ const toggleFullscreen = () => {
214
+ if (isFullscreen) {
215
+ void document.exitFullscreen?.()?.catch(() => {
216
+ });
217
+ } else if (support.box && fullscreenTarget?.current) {
218
+ void fullscreenTarget.current.requestFullscreen?.()?.catch(() => {
219
+ });
220
+ } else {
221
+ video?.webkitEnterFullscreen?.();
222
+ }
223
+ };
224
+ const rate = transport.playbackRate;
225
+ const cycleSpeed = () => {
226
+ const index = playbackRates.indexOf(rate);
227
+ transport.setPlaybackRate(
228
+ playbackRates[(index + 1) % playbackRates.length] ?? 1
229
+ );
230
+ };
231
+ const toggleCaptions = () => {
232
+ const [first] = captions.tracks;
233
+ for (const track of captions.tracks)
234
+ track.mode = !captions.showing && track === first ? "showing" : "disabled";
235
+ };
236
+ const togglePictureInPicture = () => {
237
+ if (!video) return;
238
+ if (isInPictureInPicture(video)) {
239
+ void exitPictureInPicture(video);
240
+ } else if (document.pictureInPictureEnabled) {
241
+ void video.requestPictureInPicture().catch(() => {
242
+ });
243
+ } else {
244
+ video.webkitSetPresentationMode?.("picture-in-picture");
245
+ }
246
+ };
247
+ const barButton = (label, onClick, icon, className2) => /* @__PURE__ */ jsx(
248
+ Button,
249
+ {
250
+ type: "button",
251
+ variant: "ghost",
252
+ size: "icon",
253
+ "aria-label": label,
254
+ title: label,
255
+ onClick,
256
+ className: cn(overlayButtonClass, "shrink-0 bg-transparent", className2),
257
+ children: icon
258
+ }
259
+ );
260
+ return /* @__PURE__ */ jsxs(
261
+ "div",
262
+ {
263
+ ref,
264
+ "data-slot": "media-controls",
265
+ className: cn(
266
+ // A container query: a narrow player (a portrait clip) gives the seek
267
+ // bar a row of its own, so it's always long enough to drag.
268
+ "@container pointer-events-none absolute inset-0 flex flex-col justify-end",
269
+ className
270
+ ),
271
+ ...rest,
272
+ children: [
273
+ /* @__PURE__ */ jsx(
274
+ "div",
275
+ {
276
+ "data-slot": "media-controls-surface",
277
+ "aria-hidden": "true",
278
+ className: "pointer-events-auto absolute inset-0 flex cursor-pointer items-center justify-center",
279
+ onClick: togglePlay,
280
+ children: !playing && /* @__PURE__ */ jsx("span", { className: playBadgeClass, children: /* @__PURE__ */ jsx(Play, { className: playBadgeIconClass }) })
281
+ }
282
+ ),
283
+ /* @__PURE__ */ jsxs(
284
+ "div",
285
+ {
286
+ ref: barRef,
287
+ "data-slot": "media-controls-bar",
288
+ dir: "ltr",
289
+ className: "pointer-events-auto relative flex flex-wrap items-center gap-x-1 bg-gradient-to-t from-black/75 via-black/40 to-transparent px-1 pt-6 pb-1 text-white sm:px-2 @max-xs:pt-2",
290
+ children: [
291
+ barButton(
292
+ playing ? labels.pause : labels.play,
293
+ togglePlay,
294
+ playing ? /* @__PURE__ */ jsx(Pause, { className: "h-5 w-5 fill-current", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Play, { className: "h-5 w-5 fill-current", "aria-hidden": "true" })
295
+ ),
296
+ /* @__PURE__ */ jsx(
297
+ "span",
298
+ {
299
+ "data-slot": "media-controls-time",
300
+ className: "shrink-0 px-1 text-xs whitespace-nowrap tabular-nums @max-md:me-auto",
301
+ children: /* @__PURE__ */ jsx("bdi", { children: labels.time(
302
+ formatMs(transport.currentTimeMs),
303
+ formatMs(durationMs)
304
+ ) })
305
+ }
306
+ ),
307
+ /* @__PURE__ */ jsx(
308
+ Slider,
309
+ {
310
+ className: "mx-1 min-w-0 flex-1 @max-md:order-first @max-md:mx-2 @max-md:basis-full pointer-coarse:[&_[data-slot=slider-track-wrapper]]:py-4",
311
+ trackClassName: "bg-white/30 dark:bg-white/30",
312
+ size: "sm",
313
+ min: 0,
314
+ max: durationMs > 0 ? durationMs : 1,
315
+ step: SEEK_STEP_MS,
316
+ keyboardStep: Math.max(
317
+ SEEK_STEP_MS,
318
+ Math.round(durationMs * KEYBOARD_SEEK_FRACTION)
319
+ ),
320
+ value: transport.currentTimeMs,
321
+ onValueChange: transport.seekToMs,
322
+ formatValue: formatMs,
323
+ disabled: durationMs <= 0,
324
+ "aria-label": labels.seek
325
+ }
326
+ ),
327
+ playbackRates.length > 1 && barButton(
328
+ labels.speed(labels.rate(rate)),
329
+ cycleSpeed,
330
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold tabular-nums", children: labels.rate(rate) }),
331
+ SMALL_PLAYER_HIDDEN
332
+ ),
333
+ captions.tracks.length > 0 && barButton(
334
+ captions.showing ? labels.hideCaptions : labels.showCaptions,
335
+ toggleCaptions,
336
+ // The icon shows the state, as mute's does.
337
+ captions.showing ? /* @__PURE__ */ jsx(Captions, { className: "h-5 w-5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(CaptionsOff, { className: "h-5 w-5", "aria-hidden": "true" })
338
+ ),
339
+ barButton(
340
+ muted ? labels.unmute : labels.mute,
341
+ toggleMuted,
342
+ muted ? /* @__PURE__ */ jsx(VolumeX, { className: "h-5 w-5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Volume2, { className: "h-5 w-5", "aria-hidden": "true" })
343
+ ),
344
+ support.pip && barButton(
345
+ inPip ? labels.exitPictureInPicture : labels.pictureInPicture,
346
+ togglePictureInPicture,
347
+ /* @__PURE__ */ jsx(PictureInPicture2, { className: "h-5 w-5", "aria-hidden": "true" }),
348
+ SMALL_PLAYER_HIDDEN
349
+ ),
350
+ canFullscreen && barButton(
351
+ isFullscreen ? labels.exitFullscreen : labels.fullscreen,
352
+ toggleFullscreen,
353
+ isFullscreen ? /* @__PURE__ */ jsx(Minimize, { className: "h-5 w-5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Maximize, { className: "h-5 w-5", "aria-hidden": "true" })
354
+ )
355
+ ]
356
+ }
357
+ )
358
+ ]
359
+ }
360
+ );
361
+ });
362
+ var mediaPlayerVariants = cva(
363
+ "flex h-full w-full items-center justify-center",
364
+ {
365
+ variants: {
366
+ variant: {
367
+ plain: "",
368
+ card: "rounded-xl border border-border bg-card text-card-foreground p-2"
369
+ }
370
+ },
371
+ defaultVariants: {
372
+ variant: "plain"
373
+ }
374
+ }
375
+ );
376
+ var VIDEO_EXTENSIONS = /\.(mp4|mov|avi|webm|mkv|m4v)$/i;
377
+ function inferMediaKind(src) {
378
+ const path = src.split(/[?#]/, 1)[0];
379
+ return VIDEO_EXTENSIONS.test(path) ? "video" : "audio";
380
+ }
381
+ var MediaPlayer = React.forwardRef(
382
+ ({
383
+ src,
384
+ kind,
385
+ controls = true,
386
+ controlsStyle = "native",
387
+ muted,
388
+ loop,
389
+ poster,
390
+ preload,
391
+ children,
392
+ playbackRates,
393
+ labels,
394
+ variant,
395
+ onStateChange,
396
+ onEnded,
397
+ onError,
398
+ onRetry,
399
+ onTimeUpdate,
400
+ className,
401
+ "aria-label": ariaLabel,
402
+ mediaElementRef
403
+ }, ref) => {
404
+ const [error, setError] = React.useState(null);
405
+ React.useEffect(() => {
406
+ setError(null);
407
+ }, [src]);
408
+ const resolvedKind = kind ?? inferMediaKind(src);
409
+ const transport = useMediaTransport({
410
+ onTimeUpdate,
411
+ onStateChange,
412
+ onEnded,
413
+ onError: (err) => {
414
+ setError(
415
+ labels?.error ?? "Unable to load media. The server may be unavailable."
416
+ );
417
+ onError?.(err);
418
+ },
419
+ endedState: "paused",
420
+ errorLabel: `media: ${src}`
421
+ });
422
+ const { setMediaElement, mediaElement } = transport;
423
+ const setElement = React.useCallback(
424
+ (el) => {
425
+ setMediaElement(el);
426
+ if (mediaElementRef) {
427
+ mediaElementRef.current = el;
428
+ }
429
+ },
430
+ [setMediaElement, mediaElementRef]
431
+ );
432
+ React.useImperativeHandle(
433
+ ref,
434
+ () => ({
435
+ seekToMs: transport.seekToMs,
436
+ play: transport.play,
437
+ pause: transport.pause,
438
+ getCurrentTimeMs: transport.getCurrentTimeMs,
439
+ getDurationMs: transport.getDurationMs,
440
+ isPaused: transport.isPaused,
441
+ setPlaybackRate: transport.setPlaybackRate,
442
+ get mediaElement() {
443
+ return mediaElement;
444
+ }
445
+ }),
446
+ [transport, mediaElement]
447
+ );
448
+ const handleRetry = React.useCallback(() => {
449
+ setError(null);
450
+ mediaElement?.load();
451
+ onRetry?.();
452
+ }, [mediaElement, onRetry]);
453
+ const overlay = controlsStyle === "overlay" && resolvedKind === "video";
454
+ const boxRef = React.useRef(null);
455
+ const sharedMediaProps = {
456
+ src,
457
+ controls: controls && !overlay,
458
+ muted,
459
+ loop,
460
+ preload,
461
+ "aria-label": ariaLabel
462
+ };
463
+ const video = (
464
+ // eslint-disable-next-line jsx-a11y/media-has-caption
465
+ /* @__PURE__ */ jsx(
466
+ "video",
467
+ {
468
+ ref: setElement,
469
+ playsInline: true,
470
+ poster,
471
+ className: overlay ? "h-full w-full object-contain" : "h-auto max-h-full w-auto max-w-full object-contain",
472
+ ...sharedMediaProps,
473
+ children
474
+ }
475
+ )
476
+ );
477
+ if (error) {
478
+ return /* @__PURE__ */ jsx("div", { className: mediaPlayerVariants({ variant, className }), children: /* @__PURE__ */ jsxs(
479
+ "div",
480
+ {
481
+ role: "alert",
482
+ className: "border-destructive/30 bg-destructive/10 flex min-h-36 flex-col items-center justify-center gap-3 rounded-lg border p-8 text-center",
483
+ children: [
484
+ /* @__PURE__ */ jsx("p", { className: "text-destructive-700 dark:text-destructive-300 m-0 text-sm", children: error }),
485
+ /* @__PURE__ */ jsx(
486
+ Button,
487
+ {
488
+ variant: "outline",
489
+ size: "sm",
490
+ className: "bg-background",
491
+ onClick: handleRetry,
492
+ children: labels?.retry ?? "Retry"
493
+ }
494
+ )
495
+ ]
496
+ }
497
+ ) });
498
+ }
499
+ return /* @__PURE__ */ jsx("div", { className: mediaPlayerVariants({ variant, className }), children: resolvedKind === "video" ? (
500
+ // The controls cover the player's box, so the video fills it at its
501
+ // own shape and the bar spans the box. The box is a dark surface in
502
+ // every theme (black letterboxing, as on VideoCard), so library
503
+ // controls inside it take their dark-theme colours. With the
504
+ // browser's controls the box is `contents`, leaving the video laid
505
+ // out as before; it stays in the tree either way, so changing
506
+ // `controlsStyle` keeps the same video, its time and playback.
507
+ /* @__PURE__ */ jsxs(
508
+ "div",
509
+ {
510
+ ref: boxRef,
511
+ "data-slot": overlay ? "media-player-box" : void 0,
512
+ "data-theme": overlay ? "dark" : void 0,
513
+ className: overlay ? "relative h-full w-full bg-black" : "contents",
514
+ children: [
515
+ video,
516
+ overlay && controls && /* @__PURE__ */ jsx(
517
+ MediaControls,
518
+ {
519
+ transport,
520
+ fullscreenTarget: boxRef,
521
+ labels,
522
+ playbackRates
523
+ }
524
+ )
525
+ ]
526
+ }
527
+ )
528
+ ) : (
529
+ // eslint-disable-next-line jsx-a11y/media-has-caption
530
+ /* @__PURE__ */ jsx(
531
+ "audio",
532
+ {
533
+ ref: setElement,
534
+ className: "my-4 w-[90%] max-w-lg",
535
+ ...sharedMediaProps,
536
+ children
537
+ }
538
+ )
539
+ ) });
540
+ }
541
+ );
542
+ MediaPlayer.displayName = "MediaPlayer";
543
+
544
+ export { MediaControls, MediaPlayer, PIP_EVENTS, defaultMediaControlsLabels, exitPictureInPicture, inferMediaKind, isInPictureInPicture, mediaPlayerVariants, overlayButtonClass, playBadgeClass, playBadgeIconClass };
545
+ //# sourceMappingURL=chunk-JCIQIR5V.js.map
546
+ //# sourceMappingURL=chunk-JCIQIR5V.js.map