@mieweb/ui 0.11.0-dev.35 → 0.11.0-dev.37

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 (224) hide show
  1. package/MIGRATION.md +45 -11
  2. package/agent/skills/mieweb-ui-catalog/SKILL.md +5 -3
  3. package/agent/skills/mieweb-ui-catalog/selection-guidance.md +6 -3
  4. package/dist/{EncounterMdyEditor-73PTTIRY.cjs → EncounterMdyEditor-46WMYRFI.cjs} +6 -6
  5. package/dist/{EncounterMdyEditor-73PTTIRY.cjs.map → EncounterMdyEditor-46WMYRFI.cjs.map} +1 -1
  6. package/dist/{EncounterMdyEditor-3SQY4MK2.js → EncounterMdyEditor-L7RBLE7B.js} +3 -3
  7. package/dist/{EncounterMdyEditor-3SQY4MK2.js.map → EncounterMdyEditor-L7RBLE7B.js.map} +1 -1
  8. package/dist/{chunk-4SUEBKTY.js → chunk-2OTPQPJS.js} +52 -44
  9. package/dist/chunk-2OTPQPJS.js.map +1 -0
  10. package/dist/{chunk-MZR634HI.cjs → chunk-3M2BBOXF.cjs} +9 -9
  11. package/dist/{chunk-MZR634HI.cjs.map → chunk-3M2BBOXF.cjs.map} +1 -1
  12. package/dist/{chunk-TUYWCUKH.js → chunk-4LKAZW2W.js} +7 -3
  13. package/dist/chunk-4LKAZW2W.js.map +1 -0
  14. package/dist/{chunk-JEXEFWEM.js → chunk-5WA6IYZD.js} +68 -2
  15. package/dist/chunk-5WA6IYZD.js.map +1 -0
  16. package/dist/{chunk-DXNVQQYN.cjs → chunk-6L7CTEX2.cjs} +51 -44
  17. package/dist/chunk-6L7CTEX2.cjs.map +1 -0
  18. package/dist/{chunk-DT3X3RBM.js → chunk-7U7SONM5.js} +3 -3
  19. package/dist/{chunk-DT3X3RBM.js.map → chunk-7U7SONM5.js.map} +1 -1
  20. package/dist/{chunk-4M2KE4O5.cjs → chunk-AHYDJN4H.cjs} +506 -125
  21. package/dist/chunk-AHYDJN4H.cjs.map +1 -0
  22. package/dist/{chunk-EX7Q5545.js → chunk-AKC677ZF.js} +5 -5
  23. package/dist/{chunk-EX7Q5545.js.map → chunk-AKC677ZF.js.map} +1 -1
  24. package/dist/{chunk-C4GEPUE7.js → chunk-CKECOCYJ.js} +2 -2
  25. package/dist/{chunk-C4GEPUE7.js.map → chunk-CKECOCYJ.js.map} +1 -1
  26. package/dist/{chunk-V5VPECJI.js → chunk-CRR7AE5D.js} +11 -7
  27. package/dist/chunk-CRR7AE5D.js.map +1 -0
  28. package/dist/chunk-D3FNZMOB.cjs +4 -0
  29. package/dist/{chunk-GLXTVCHN.cjs.map → chunk-D3FNZMOB.cjs.map} +1 -1
  30. package/dist/{chunk-ZK2R6UGF.cjs → chunk-DCV34MEF.cjs} +4 -4
  31. package/dist/{chunk-ZK2R6UGF.cjs.map → chunk-DCV34MEF.cjs.map} +1 -1
  32. package/dist/{chunk-XEHD2HA5.cjs → chunk-FRPELUI5.cjs} +3 -3
  33. package/dist/{chunk-XEHD2HA5.cjs.map → chunk-FRPELUI5.cjs.map} +1 -1
  34. package/dist/chunk-G4VQEQGW.js +3 -0
  35. package/dist/{chunk-RISZWWEQ.js.map → chunk-G4VQEQGW.js.map} +1 -1
  36. package/dist/{chunk-3O3Q3DX7.cjs → chunk-GG3IK5TL.cjs} +26 -26
  37. package/dist/{chunk-3O3Q3DX7.cjs.map → chunk-GG3IK5TL.cjs.map} +1 -1
  38. package/dist/{chunk-DGHS5IVD.js → chunk-GJSLKD4Q.js} +3 -3
  39. package/dist/{chunk-DGHS5IVD.js.map → chunk-GJSLKD4Q.js.map} +1 -1
  40. package/dist/{chunk-VMEVLBI3.js → chunk-GKMMDKYY.js} +3 -3
  41. package/dist/{chunk-VMEVLBI3.js.map → chunk-GKMMDKYY.js.map} +1 -1
  42. package/dist/{chunk-46RYJYN2.cjs → chunk-GU6TPVXF.cjs} +6 -6
  43. package/dist/{chunk-46RYJYN2.cjs.map → chunk-GU6TPVXF.cjs.map} +1 -1
  44. package/dist/{chunk-QHCAWDMG.cjs → chunk-HDZQLTBP.cjs} +7 -3
  45. package/dist/chunk-HDZQLTBP.cjs.map +1 -0
  46. package/dist/chunk-IALTKN3Y.js +1017 -0
  47. package/dist/chunk-IALTKN3Y.js.map +1 -0
  48. package/dist/{chunk-33Y3KQWL.js → chunk-IGRHPZOJ.js} +3 -3
  49. package/dist/{chunk-33Y3KQWL.js.map → chunk-IGRHPZOJ.js.map} +1 -1
  50. package/dist/chunk-JCIQIR5V.js +546 -0
  51. package/dist/chunk-JCIQIR5V.js.map +1 -0
  52. package/dist/{chunk-RTADGBBN.js → chunk-JUY2JLFL.js} +502 -121
  53. package/dist/chunk-JUY2JLFL.js.map +1 -0
  54. package/dist/{chunk-2S76TRBJ.cjs → chunk-JUYBJLPI.cjs} +4 -4
  55. package/dist/{chunk-2S76TRBJ.cjs.map → chunk-JUYBJLPI.cjs.map} +1 -1
  56. package/dist/{chunk-UDUYZDTA.js → chunk-JWMMWNTT.js} +3 -3
  57. package/dist/{chunk-UDUYZDTA.js.map → chunk-JWMMWNTT.js.map} +1 -1
  58. package/dist/{chunk-PLBWZW4F.js → chunk-KAQNYX7B.js} +3 -3
  59. package/dist/{chunk-PLBWZW4F.js.map → chunk-KAQNYX7B.js.map} +1 -1
  60. package/dist/{chunk-SPGP252Q.js → chunk-KB2PLV5Q.js} +4 -4
  61. package/dist/{chunk-SPGP252Q.js.map → chunk-KB2PLV5Q.js.map} +1 -1
  62. package/dist/{chunk-NCPDUJKJ.cjs → chunk-KQXD6BFO.cjs} +4 -4
  63. package/dist/{chunk-NCPDUJKJ.cjs.map → chunk-KQXD6BFO.cjs.map} +1 -1
  64. package/dist/{chunk-4VSK3EE5.cjs → chunk-L77XJPKC.cjs} +3 -3
  65. package/dist/{chunk-4VSK3EE5.cjs.map → chunk-L77XJPKC.cjs.map} +1 -1
  66. package/dist/{chunk-PGJLW2NO.cjs → chunk-LLIG4X6M.cjs} +7 -7
  67. package/dist/{chunk-PGJLW2NO.cjs.map → chunk-LLIG4X6M.cjs.map} +1 -1
  68. package/dist/{chunk-YGSPIDE4.js → chunk-LQ22CY7X.js} +5 -5
  69. package/dist/{chunk-YGSPIDE4.js.map → chunk-LQ22CY7X.js.map} +1 -1
  70. package/dist/{chunk-AKMTPCLC.cjs → chunk-LTCHD3K5.cjs} +2 -2
  71. package/dist/{chunk-AKMTPCLC.cjs.map → chunk-LTCHD3K5.cjs.map} +1 -1
  72. package/dist/{chunk-NW6D6AFL.js → chunk-LWLHCUDP.js} +10 -10
  73. package/dist/{chunk-NW6D6AFL.js.map → chunk-LWLHCUDP.js.map} +1 -1
  74. package/dist/{chunk-YR45POH2.js → chunk-MAEANAAJ.js} +3 -3
  75. package/dist/{chunk-YR45POH2.js.map → chunk-MAEANAAJ.js.map} +1 -1
  76. package/dist/{chunk-X5HLVACK.js → chunk-MLCIINXT.js} +4 -4
  77. package/dist/{chunk-X5HLVACK.js.map → chunk-MLCIINXT.js.map} +1 -1
  78. package/dist/{chunk-PHUTJKHL.cjs → chunk-MXIIPFTA.cjs} +3 -3
  79. package/dist/{chunk-PHUTJKHL.cjs.map → chunk-MXIIPFTA.cjs.map} +1 -1
  80. package/dist/{chunk-QO4A6U73.cjs → chunk-NCG7NJAW.cjs} +93 -30
  81. package/dist/chunk-NCG7NJAW.cjs.map +1 -0
  82. package/dist/{chunk-25MPUVTM.js → chunk-NIE3WBJH.js} +94 -31
  83. package/dist/chunk-NIE3WBJH.js.map +1 -0
  84. package/dist/{chunk-4GSFVD5V.js → chunk-NUHHWELE.js} +3 -3
  85. package/dist/{chunk-4GSFVD5V.js.map → chunk-NUHHWELE.js.map} +1 -1
  86. package/dist/{chunk-GDKOYVX3.cjs → chunk-NZUQL42A.cjs} +11 -7
  87. package/dist/chunk-NZUQL42A.cjs.map +1 -0
  88. package/dist/{chunk-YSIFPHLX.js → chunk-OJQMC426.js} +2 -2
  89. package/dist/{chunk-YSIFPHLX.js.map → chunk-OJQMC426.js.map} +1 -1
  90. package/dist/{chunk-SVC7NPQ6.cjs → chunk-PDLVSJMZ.cjs} +6 -6
  91. package/dist/{chunk-SVC7NPQ6.cjs.map → chunk-PDLVSJMZ.cjs.map} +1 -1
  92. package/dist/chunk-QAEAC7HO.cjs +4 -0
  93. package/dist/chunk-QAEAC7HO.cjs.map +1 -0
  94. package/dist/{chunk-KD7YD6US.cjs → chunk-QOQQVNP3.cjs} +2 -2
  95. package/dist/{chunk-KD7YD6US.cjs.map → chunk-QOQQVNP3.cjs.map} +1 -1
  96. package/dist/{chunk-6O6EYKOG.cjs → chunk-R4QWCP2T.cjs} +65 -65
  97. package/dist/{chunk-6O6EYKOG.cjs.map → chunk-R4QWCP2T.cjs.map} +1 -1
  98. package/dist/chunk-RCTIPBLU.cjs +1039 -0
  99. package/dist/chunk-RCTIPBLU.cjs.map +1 -0
  100. package/dist/{chunk-FSWHZY3T.cjs → chunk-RD2I67C4.cjs} +3 -3
  101. package/dist/{chunk-FSWHZY3T.cjs.map → chunk-RD2I67C4.cjs.map} +1 -1
  102. package/dist/chunk-SCGXD334.js +3 -0
  103. package/dist/chunk-SCGXD334.js.map +1 -0
  104. package/dist/{chunk-2OXNJJHJ.cjs → chunk-TERHGQ73.cjs} +3 -3
  105. package/dist/{chunk-2OXNJJHJ.cjs.map → chunk-TERHGQ73.cjs.map} +1 -1
  106. package/dist/chunk-TZKBW4GA.cjs +578 -0
  107. package/dist/chunk-TZKBW4GA.cjs.map +1 -0
  108. package/dist/{chunk-DO7EED3B.cjs → chunk-WZ7WTIVU.cjs} +3 -3
  109. package/dist/{chunk-DO7EED3B.cjs.map → chunk-WZ7WTIVU.cjs.map} +1 -1
  110. package/dist/{chunk-6UYMO3PF.cjs → chunk-XX6PNPF2.cjs} +68 -2
  111. package/dist/chunk-XX6PNPF2.cjs.map +1 -0
  112. package/dist/{chunk-566FIEPE.js → chunk-YFZNSP7F.js} +3 -3
  113. package/dist/{chunk-566FIEPE.js.map → chunk-YFZNSP7F.js.map} +1 -1
  114. package/dist/{chunk-I6Y2KLTZ.js → chunk-ZP4H5HA4.js} +3 -3
  115. package/dist/{chunk-I6Y2KLTZ.js.map → chunk-ZP4H5HA4.js.map} +1 -1
  116. package/dist/components/ActionPlan/index.cjs +1 -1
  117. package/dist/components/ActionPlan/index.js +1 -1
  118. package/dist/components/AlertDialog/index.cjs +7 -7
  119. package/dist/components/AlertDialog/index.js +3 -3
  120. package/dist/components/AudioPlayer/index.cjs +7 -6
  121. package/dist/components/AudioPlayer/index.js +2 -1
  122. package/dist/components/CustomizableDashboard/index.cjs +1 -1
  123. package/dist/components/CustomizableDashboard/index.js +1 -1
  124. package/dist/components/DateInput/index.cjs +3 -3
  125. package/dist/components/DateInput/index.js +2 -2
  126. package/dist/components/EmailEditor/index.cjs +16 -13
  127. package/dist/components/EmailEditor/index.js +7 -4
  128. package/dist/components/KeyboardShortcutsOverlay/index.cjs +5 -5
  129. package/dist/components/KeyboardShortcutsOverlay/index.js +3 -3
  130. package/dist/components/Markdown/index.cjs +10 -10
  131. package/dist/components/Markdown/index.js +2 -2
  132. package/dist/components/MediaEditor/index.cjs +13 -12
  133. package/dist/components/MediaEditor/index.js +8 -7
  134. package/dist/components/MediaFeed/index.cjs +10 -7
  135. package/dist/components/MediaFeed/index.d.cts +33 -0
  136. package/dist/components/MediaFeed/index.d.ts +33 -0
  137. package/dist/components/MediaFeed/index.js +9 -6
  138. package/dist/components/MediaPlayer/index.cjs +13 -6
  139. package/dist/components/MediaPlayer/index.d.cts +103 -5
  140. package/dist/components/MediaPlayer/index.d.ts +103 -5
  141. package/dist/components/MediaPlayer/index.js +6 -3
  142. package/dist/components/Modal/index.cjs +10 -10
  143. package/dist/components/Modal/index.js +2 -2
  144. package/dist/components/RadialExplorer/index.cjs +3 -3
  145. package/dist/components/RadialExplorer/index.js +2 -2
  146. package/dist/components/RecordHeader/index.cjs +4 -4
  147. package/dist/components/RecordHeader/index.js +2 -2
  148. package/dist/components/RecordLayout/index.cjs +1 -1
  149. package/dist/components/RecordLayout/index.js +1 -1
  150. package/dist/components/Sheet/index.cjs +1 -1
  151. package/dist/components/Sheet/index.js +1 -1
  152. package/dist/components/Slider/index.cjs +5 -5
  153. package/dist/components/Slider/index.d.cts +6 -0
  154. package/dist/components/Slider/index.d.ts +6 -0
  155. package/dist/components/Slider/index.js +1 -1
  156. package/dist/components/SliderCalculator/index.cjs +4 -4
  157. package/dist/components/SliderCalculator/index.js +2 -2
  158. package/dist/components/SuperChat/index.cjs +20 -18
  159. package/dist/components/SuperChat/index.cjs.map +1 -1
  160. package/dist/components/SuperChat/index.d.cts +2 -2
  161. package/dist/components/SuperChat/index.d.ts +2 -2
  162. package/dist/components/SuperChat/index.js +12 -10
  163. package/dist/components/SuperChat/index.js.map +1 -1
  164. package/dist/components/SuperChat/plugins/index.cjs +1 -1
  165. package/dist/components/SuperChat/plugins/index.d.cts +2 -2
  166. package/dist/components/SuperChat/plugins/index.d.ts +2 -2
  167. package/dist/components/SuperChat/plugins/index.js +1 -1
  168. package/dist/components/TagEditor/index.cjs +4 -4
  169. package/dist/components/TagEditor/index.js +2 -2
  170. package/dist/components/Textarea/index.cjs +7 -4
  171. package/dist/components/Textarea/index.js +4 -1
  172. package/dist/esheet.cjs +122 -123
  173. package/dist/esheet.cjs.map +1 -1
  174. package/dist/esheet.js +25 -26
  175. package/dist/esheet.js.map +1 -1
  176. package/dist/hooks/index.cjs +17 -17
  177. package/dist/hooks/index.js +4 -4
  178. package/dist/index.cjs +518 -482
  179. package/dist/index.cjs.map +1 -1
  180. package/dist/index.d.cts +204 -98
  181. package/dist/index.d.ts +204 -98
  182. package/dist/index.js +91 -52
  183. package/dist/index.js.map +1 -1
  184. package/dist/kerebron.cjs +32 -62
  185. package/dist/kerebron.cjs.map +1 -1
  186. package/dist/kerebron.css +145 -8
  187. package/dist/kerebron.d.cts +299 -1
  188. package/dist/kerebron.d.ts +299 -1
  189. package/dist/kerebron.js +22 -63
  190. package/dist/kerebron.js.map +1 -1
  191. package/dist/{model-EFXX5JZN.cjs → model-5PB6KNYH.cjs} +20 -20
  192. package/dist/{model-EFXX5JZN.cjs.map → model-5PB6KNYH.cjs.map} +1 -1
  193. package/dist/{model-TAARSYDY.js → model-OGWTZEXR.js} +6 -6
  194. package/dist/{model-TAARSYDY.js.map → model-OGWTZEXR.js.map} +1 -1
  195. package/dist/styles.css +1 -1
  196. package/dist/tailwind-preset.cjs +4 -4
  197. package/dist/tailwind-preset.js +1 -1
  198. package/dist/templates.cjs +31 -31
  199. package/dist/templates.js +2 -2
  200. package/dist/{types-DSWbrVkM.d.ts → types-BfVolads.d.cts} +33 -0
  201. package/dist/{types-DSWbrVkM.d.cts → types-BfVolads.d.ts} +33 -0
  202. package/dist/utils/index.cjs +5 -5
  203. package/dist/utils/index.d.cts +12 -12
  204. package/dist/utils/index.d.ts +12 -12
  205. package/dist/utils/index.js +1 -1
  206. package/package.json +3 -13
  207. package/dist/chunk-25MPUVTM.js.map +0 -1
  208. package/dist/chunk-4M2KE4O5.cjs.map +0 -1
  209. package/dist/chunk-4SUEBKTY.js.map +0 -1
  210. package/dist/chunk-6UYMO3PF.cjs.map +0 -1
  211. package/dist/chunk-DXNVQQYN.cjs.map +0 -1
  212. package/dist/chunk-ECKIFQSD.cjs +0 -183
  213. package/dist/chunk-ECKIFQSD.cjs.map +0 -1
  214. package/dist/chunk-GDKOYVX3.cjs.map +0 -1
  215. package/dist/chunk-GLXTVCHN.cjs +0 -4
  216. package/dist/chunk-JEXEFWEM.js.map +0 -1
  217. package/dist/chunk-QHCAWDMG.cjs.map +0 -1
  218. package/dist/chunk-QO4A6U73.cjs.map +0 -1
  219. package/dist/chunk-R5P7NAJQ.js +0 -159
  220. package/dist/chunk-R5P7NAJQ.js.map +0 -1
  221. package/dist/chunk-RISZWWEQ.js +0 -3
  222. package/dist/chunk-RTADGBBN.js.map +0 -1
  223. package/dist/chunk-TUYWCUKH.js.map +0 -1
  224. package/dist/chunk-V5VPECJI.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { MediaPlayer } from './chunk-R5P7NAJQ.js';
1
+ import { defaultMediaControlsLabels, overlayButtonClass, isInPictureInPicture, exitPictureInPicture, PIP_EVENTS, MediaPlayer, playBadgeIconClass, playBadgeClass } from './chunk-JCIQIR5V.js';
2
2
  import { Avatar } from './chunk-TI3TQQ7F.js';
3
- import { Modal, ModalHeader, ModalTitle, ModalClose } from './chunk-VMEVLBI3.js';
3
+ import { Modal, ModalHeader, ModalTitle, ModalClose } from './chunk-GKMMDKYY.js';
4
4
  import { usePrefersReducedMotion } from './chunk-XDU65GLW.js';
5
5
  import { Button } from './chunk-WX2XB6FP.js';
6
6
  import { cn } from './chunk-TQONQSJO.js';
@@ -39,7 +39,8 @@ function NativeFeedMedia({
39
39
  labels,
40
40
  onActivate,
41
41
  playbackPosition,
42
- playbackRequestId
42
+ playbackRequestId,
43
+ overlay
43
44
  }) {
44
45
  const mediaElementRef = React2.useRef(null);
45
46
  const [state, setState] = React2.useState("idle");
@@ -55,24 +56,29 @@ function NativeFeedMedia({
55
56
  const attemptId = React2.useRef(0);
56
57
  const activeRef = React2.useRef(active);
57
58
  const canSavePosition = React2.useRef(false);
59
+ const autoAttempt = React2.useRef(null);
58
60
  activeRef.current = active;
59
- const tryPlay = React2.useCallback(async () => {
61
+ const tryPlay = React2.useCallback(async (automatic = false) => {
60
62
  const element = mediaElementRef.current;
61
63
  if (!element || !activeRef.current) return;
62
64
  const attempt = ++attemptId.current;
63
65
  setBlocked(false);
66
+ autoAttempt.current = automatic ? attempt : null;
64
67
  try {
65
68
  await element.play();
66
- if (!activeRef.current || element !== mediaElementRef.current)
69
+ if ((!activeRef.current || element !== mediaElementRef.current) && !isInPictureInPicture(element))
67
70
  element.pause();
68
71
  } catch (error) {
69
72
  if (!activeRef.current || attempt !== attemptId.current) return;
70
73
  if (error instanceof Error && error.name === "AbortError") return;
71
74
  setBlocked(true);
75
+ } finally {
76
+ if (autoAttempt.current === attempt) autoAttempt.current = null;
72
77
  }
73
78
  }, []);
74
79
  const pauseForOwnership = React2.useCallback(
75
80
  (element) => {
81
+ if (isInPictureInPicture(element)) return;
76
82
  if (element && element === mediaElementRef.current && !element.paused)
77
83
  ownershipPause.current = true;
78
84
  element?.pause();
@@ -95,9 +101,13 @@ function NativeFeedMedia({
95
101
  else element.addEventListener("loadedmetadata", restorePosition);
96
102
  }
97
103
  if (active && (latestPlaybackRequest.current === void 0 || latestPlaybackRequest.current === handledPlaybackRequest.current) && (autoPlay || wantsPlayback.current)) {
98
- void tryPlay();
104
+ void tryPlay(!wantsPlayback.current);
99
105
  } else if (!active) {
100
106
  attemptId.current++;
107
+ const fullscreen = document.fullscreenElement;
108
+ if (element && fullscreen && (fullscreen === element || fullscreen === element.closest('[data-slot="media-player-box"]')))
109
+ void document.exitFullscreen?.()?.catch(() => {
110
+ });
101
111
  pauseForOwnership(element);
102
112
  setBlocked(false);
103
113
  }
@@ -116,6 +126,17 @@ function NativeFeedMedia({
116
126
  retryVersion,
117
127
  tryPlay
118
128
  ]);
129
+ React2.useEffect(() => {
130
+ const element = mediaElementRef.current;
131
+ if (!element) return;
132
+ const landed = () => {
133
+ if (!activeRef.current && !isInPictureInPicture(element)) element.pause();
134
+ };
135
+ for (const name of PIP_EVENTS) element.addEventListener(name, landed);
136
+ return () => {
137
+ for (const name of PIP_EVENTS) element.removeEventListener(name, landed);
138
+ };
139
+ }, [retryVersion]);
119
140
  React2.useEffect(() => {
120
141
  if (!active || playbackRequestId === void 0 || playbackRequestId === handledPlaybackRequest.current)
121
142
  return;
@@ -136,64 +157,126 @@ function NativeFeedMedia({
136
157
  if (active) void tryPlay();
137
158
  }
138
159
  };
139
- return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 w-full flex-col items-center justify-center gap-3", children: [
140
- /* @__PURE__ */ jsx(
141
- MediaPlayer,
142
- {
143
- src: media.src,
144
- kind: media.kind === "audio" ? "audio" : "video",
145
- mediaElementRef,
146
- muted,
147
- loop,
148
- poster: media.poster,
149
- preload: active ? "metadata" : "none",
150
- controls: active,
151
- "aria-label": title,
152
- onStateChange: (next) => {
153
- setState(next);
154
- if (next === "playing") ownershipPause.current = false;
155
- if (next === "paused") {
156
- if (activeRef.current && !ownershipPause.current) {
157
- wantsPlayback.current = false;
158
- playbackPosition.playRequested = false;
160
+ return /* @__PURE__ */ jsxs(
161
+ "div",
162
+ {
163
+ className: cn(
164
+ "flex h-full min-h-0 w-full flex-col items-center justify-center",
165
+ overlay ? "relative" : "gap-3"
166
+ ),
167
+ children: [
168
+ /* @__PURE__ */ jsx(
169
+ MediaPlayer,
170
+ {
171
+ src: media.src,
172
+ kind: media.kind === "audio" ? "audio" : "video",
173
+ mediaElementRef,
174
+ muted,
175
+ loop,
176
+ poster: media.poster,
177
+ preload: active ? "metadata" : "none",
178
+ controls: active,
179
+ controlsStyle: overlay ? "overlay" : "native",
180
+ "aria-label": title,
181
+ onStateChange: (next) => {
182
+ setState(next);
183
+ if (next === "playing") {
184
+ ownershipPause.current = false;
185
+ setBlocked(false);
186
+ if ((activeRef.current || isInPictureInPicture(mediaElementRef.current)) && autoAttempt.current !== attemptId.current) {
187
+ wantsPlayback.current = true;
188
+ playbackPosition.playRequested = true;
189
+ }
190
+ }
191
+ if (next === "paused") {
192
+ if ((activeRef.current || isInPictureInPicture(mediaElementRef.current)) && !ownershipPause.current) {
193
+ wantsPlayback.current = false;
194
+ playbackPosition.playRequested = false;
195
+ }
196
+ ownershipPause.current = false;
197
+ }
198
+ },
199
+ onTimeUpdate: (timeMs) => {
200
+ if (canSavePosition.current) playbackPosition.timeMs = timeMs;
201
+ },
202
+ onRetry: () => {
203
+ ownershipPause.current = false;
204
+ canSavePosition.current = false;
205
+ setState("idle");
206
+ setBlocked(false);
207
+ setRetryVersion((version) => version + 1);
208
+ },
209
+ labels: {
210
+ error: labels.mediaError,
211
+ retry: labels.retry,
212
+ play: labels.play,
213
+ pause: labels.pause,
214
+ mute: labels.mute,
215
+ unmute: labels.unmute,
216
+ seek: labels.seek,
217
+ fullscreen: labels.playerFullscreen,
218
+ exitFullscreen: labels.exitPlayerFullscreen,
219
+ pictureInPicture: labels.pictureInPicture,
220
+ exitPictureInPicture: labels.exitPictureInPicture,
221
+ time: labels.time,
222
+ rate: labels.playerRate,
223
+ speed: labels.playerSpeed,
224
+ showCaptions: labels.showCaptions,
225
+ hideCaptions: labels.hideCaptions
226
+ },
227
+ className: "min-h-0 flex-1 [&_video]:h-full [&_video]:w-full"
228
+ }
229
+ ),
230
+ overlay ? (
231
+ // An item that isn't the active one has no controls yet: its ▶
232
+ // selects it and starts it.
233
+ !active && state !== "error" && state !== "playing" && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(
234
+ Button,
235
+ {
236
+ type: "button",
237
+ variant: "ghost",
238
+ size: "icon",
239
+ "aria-label": labels.play,
240
+ title: labels.play,
241
+ className: cn(
242
+ overlayButtonClass,
243
+ playBadgeClass,
244
+ "pointer-events-auto pointer-fine:h-14 pointer-fine:w-14"
245
+ ),
246
+ onClick: togglePlayback,
247
+ children: /* @__PURE__ */ jsx(Play, { className: playBadgeIconClass, "aria-hidden": "true" })
159
248
  }
160
- ownershipPause.current = false;
249
+ ) })
250
+ ) : state !== "error" && /* @__PURE__ */ jsx(
251
+ Button,
252
+ {
253
+ type: "button",
254
+ variant: "secondary",
255
+ size: "sm",
256
+ leftIcon: state === "playing" ? /* @__PURE__ */ jsx(Pause, { className: "h-4 w-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Play, { className: "h-4 w-4", "aria-hidden": "true" }),
257
+ onClick: togglePlayback,
258
+ children: state === "playing" ? labels.pause : labels.play
161
259
  }
162
- },
163
- onTimeUpdate: (timeMs) => {
164
- if (canSavePosition.current) playbackPosition.timeMs = timeMs;
165
- },
166
- onRetry: () => {
167
- ownershipPause.current = false;
168
- canSavePosition.current = false;
169
- setState("idle");
170
- setBlocked(false);
171
- setRetryVersion((version) => version + 1);
172
- },
173
- labels: { error: labels.mediaError, retry: labels.retry },
174
- className: "min-h-0 flex-1 [&_video]:h-full [&_video]:w-full"
175
- }
176
- ),
177
- state !== "error" && /* @__PURE__ */ jsx(
178
- Button,
179
- {
180
- variant: "secondary",
181
- size: "sm",
182
- leftIcon: state === "playing" ? /* @__PURE__ */ jsx(Pause, { className: "h-4 w-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Play, { className: "h-4 w-4", "aria-hidden": "true" }),
183
- onClick: togglePlayback,
184
- children: state === "playing" ? labels.pause : labels.play
185
- }
186
- ),
187
- blocked && /* @__PURE__ */ jsx(
188
- "p",
189
- {
190
- role: "status",
191
- className: "text-muted-foreground m-0 text-center text-sm",
192
- children: labels.blockedAutoplay
193
- }
194
- ),
195
- state === "error" && /* @__PURE__ */ jsx(SourceLink, { src: media.src, labels })
196
- ] });
260
+ ),
261
+ blocked && state !== "playing" && /* @__PURE__ */ jsx(
262
+ "p",
263
+ {
264
+ role: "status",
265
+ className: cn(
266
+ "m-0 text-center text-sm",
267
+ overlay ? (
268
+ // Along the top, clear of the details and actions at the foot
269
+ // and of a host's Back button in the top corner.
270
+ "pointer-events-none absolute inset-x-0 top-3 flex justify-center px-14"
271
+ ) : "text-muted-foreground"
272
+ ),
273
+ children: overlay ? /* @__PURE__ */ jsx("span", { className: "rounded bg-black/60 px-2 py-1 text-white", children: labels.blockedAutoplay }) : labels.blockedAutoplay
274
+ }
275
+ ),
276
+ state === "error" && /* @__PURE__ */ jsx(SourceLink, { src: media.src, labels })
277
+ ]
278
+ }
279
+ );
197
280
  }
198
281
  function ImageFeedMedia({ media, title, labels }) {
199
282
  const [failed, setFailed] = React2.useState(false);
@@ -298,6 +381,7 @@ function YouTubeFeedMedia({
298
381
  id && !failed ? /* @__PURE__ */ jsx(
299
382
  Button,
300
383
  {
384
+ type: "button",
301
385
  variant: "secondary",
302
386
  leftIcon: /* @__PURE__ */ jsx(Play, { className: "h-4 w-4", "aria-hidden": "true" }),
303
387
  onClick: () => {
@@ -335,6 +419,21 @@ var defaultLabels = {
335
419
  next: "Next item",
336
420
  play: "Play",
337
421
  pause: "Pause",
422
+ // The player's own controls, in the overlay layout.
423
+ mute: defaultMediaControlsLabels.mute,
424
+ unmute: defaultMediaControlsLabels.unmute,
425
+ seek: defaultMediaControlsLabels.seek,
426
+ playerFullscreen: defaultMediaControlsLabels.fullscreen,
427
+ exitPlayerFullscreen: defaultMediaControlsLabels.exitFullscreen,
428
+ pictureInPicture: defaultMediaControlsLabels.pictureInPicture,
429
+ exitPictureInPicture: defaultMediaControlsLabels.exitPictureInPicture,
430
+ time: defaultMediaControlsLabels.time,
431
+ playerRate: defaultMediaControlsLabels.rate,
432
+ playerSpeed: defaultMediaControlsLabels.speed,
433
+ showCaptions: defaultMediaControlsLabels.showCaptions,
434
+ hideCaptions: defaultMediaControlsLabels.hideCaptions,
435
+ showMore: "Show more",
436
+ showLess: "Show less",
338
437
  blockedAutoplay: "Playback did not start. Press Play to try again.",
339
438
  mediaError: "This media is unavailable.",
340
439
  openSource: "Open source",
@@ -342,6 +441,181 @@ var defaultLabels = {
342
441
  item: (position, total) => `Media item ${position} of ${total}`,
343
442
  position: (position, total) => `${position} / ${total}`
344
443
  };
444
+ var OVERLAY_BAR_SCRIM = "[&_[data-slot=media-controls-bar]]:bg-none [&_[data-slot=media-controls-bar]]:bg-black/55";
445
+ function useControlsBarHeight(ref) {
446
+ const [height, setHeight] = React2.useState(0);
447
+ React2.useLayoutEffect(() => {
448
+ const item = ref.current?.closest('[data-slot="item"]');
449
+ if (!item) return;
450
+ let bar = null;
451
+ const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(
452
+ () => setHeight(bar?.getBoundingClientRect().height ?? 0)
453
+ );
454
+ const attach = () => {
455
+ const next = item.querySelector('[data-slot="media-controls-bar"]');
456
+ if (next === bar) return;
457
+ if (bar) resize?.unobserve(bar);
458
+ bar = next;
459
+ if (bar) resize?.observe(bar);
460
+ setHeight(bar?.getBoundingClientRect().height ?? 0);
461
+ };
462
+ attach();
463
+ const mutations = typeof MutationObserver === "undefined" ? null : new MutationObserver(attach);
464
+ mutations?.observe(item, { childList: true, subtree: true });
465
+ return () => {
466
+ mutations?.disconnect();
467
+ resize?.disconnect();
468
+ };
469
+ }, [ref]);
470
+ return height;
471
+ }
472
+ function OverlayDetails({
473
+ author,
474
+ title,
475
+ caption,
476
+ actions,
477
+ fullscreenButton,
478
+ labels,
479
+ classNames
480
+ }) {
481
+ const rootRef = React2.useRef(null);
482
+ const barHeight = useControlsBarHeight(rootRef);
483
+ const showCaption = caption != null && caption !== "" && !(typeof caption === "string" && caption.trim() === (title ?? "").trim());
484
+ const textId = React2.useId();
485
+ const titleRef = React2.useRef(null);
486
+ const captionRef = React2.useRef(null);
487
+ const [expanded, setExpanded] = React2.useState(false);
488
+ const [clipped, setClipped] = React2.useState(false);
489
+ React2.useLayoutEffect(() => {
490
+ if (expanded) return;
491
+ const titleEl = titleRef.current;
492
+ const captionEl = captionRef.current;
493
+ const measure = () => setClipped(
494
+ !!titleEl && titleEl.scrollWidth > titleEl.clientWidth + 1 || !!captionEl && captionEl.scrollHeight > captionEl.clientHeight + 1
495
+ );
496
+ measure();
497
+ if (typeof ResizeObserver === "undefined") return;
498
+ const observer = new ResizeObserver(measure);
499
+ for (const el of [titleEl, captionEl]) if (el) observer.observe(el);
500
+ return () => observer.disconnect();
501
+ }, [title, caption, expanded]);
502
+ return /* @__PURE__ */ jsxs(
503
+ "div",
504
+ {
505
+ ref: rootRef,
506
+ "data-slot": "details",
507
+ "data-expanded": expanded || void 0,
508
+ style: { bottom: barHeight, maxHeight: `calc(100% - ${barHeight}px)` },
509
+ className: cn(
510
+ "pointer-events-none absolute inset-x-0 z-10 flex items-end justify-between gap-3 bg-gradient-to-t from-black/55 via-black/30 to-transparent ps-3 pe-2 pt-16 pb-3 text-white [text-shadow:0_1px_2px_rgb(0_0_0/0.6)]",
511
+ // A darker scrim behind the full text, which can run over a bright frame.
512
+ expanded && "via-black/55",
513
+ classNames.details
514
+ ),
515
+ children: [
516
+ /* @__PURE__ */ jsx(
517
+ "div",
518
+ {
519
+ id: textId,
520
+ className: cn(
521
+ "flex max-w-xl min-w-0 flex-1 flex-col leading-5",
522
+ expanded && "pointer-events-auto min-h-0 self-stretch overflow-y-auto overscroll-contain"
523
+ ),
524
+ children: /* @__PURE__ */ jsxs("div", { className: "mt-auto", children: [
525
+ author && /* @__PURE__ */ jsxs(
526
+ "div",
527
+ {
528
+ "data-slot": "author",
529
+ className: cn(
530
+ "mb-1 flex min-w-0 items-center gap-2",
531
+ classNames.author
532
+ ),
533
+ children: [
534
+ /* @__PURE__ */ jsx(
535
+ Avatar,
536
+ {
537
+ name: author.name,
538
+ src: author.avatar,
539
+ alt: "",
540
+ size: "sm",
541
+ className: "shrink-0 ring-1 ring-white/70"
542
+ }
543
+ ),
544
+ /* @__PURE__ */ jsx(
545
+ "p",
546
+ {
547
+ dir: "auto",
548
+ className: "m-0 w-fit max-w-full truncate text-sm font-semibold",
549
+ children: author.name
550
+ }
551
+ )
552
+ ]
553
+ }
554
+ ),
555
+ title && /* @__PURE__ */ jsx(
556
+ "h3",
557
+ {
558
+ ref: titleRef,
559
+ "data-slot": "title",
560
+ dir: "auto",
561
+ className: cn(
562
+ "m-0 w-fit max-w-full text-sm font-semibold",
563
+ expanded ? "break-words" : "truncate",
564
+ classNames.title
565
+ ),
566
+ children: title
567
+ }
568
+ ),
569
+ showCaption && // A div, not a p: a host caption can hold block content, and its
570
+ // links stay tappable through the details layer.
571
+ /* @__PURE__ */ jsx(
572
+ "div",
573
+ {
574
+ ref: captionRef,
575
+ "data-slot": "caption",
576
+ dir: "auto",
577
+ className: cn(
578
+ "pointer-events-auto m-0 w-fit max-w-full text-sm break-words text-white/90",
579
+ !expanded && "line-clamp-2",
580
+ classNames.caption
581
+ ),
582
+ children: caption
583
+ }
584
+ ),
585
+ (clipped || expanded) && /* @__PURE__ */ jsx(
586
+ "button",
587
+ {
588
+ type: "button",
589
+ "data-slot": "details-toggle",
590
+ "aria-expanded": expanded,
591
+ "aria-controls": textId,
592
+ onClick: () => setExpanded((open) => !open),
593
+ className: "pointer-events-auto mt-0.5 rounded-sm text-sm font-semibold text-white/80 underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-white/70",
594
+ children: expanded ? labels.showLess : labels.showMore
595
+ }
596
+ )
597
+ ] })
598
+ }
599
+ ),
600
+ (actions || fullscreenButton) && // The action column, at the bottom end beside the text.
601
+ /* @__PURE__ */ jsxs("div", { className: "pointer-events-auto flex shrink-0 flex-col items-center gap-3", children: [
602
+ actions && /* @__PURE__ */ jsx(
603
+ "div",
604
+ {
605
+ "data-slot": "actions",
606
+ className: cn(
607
+ "flex flex-col items-center gap-3",
608
+ classNames.actions
609
+ ),
610
+ children: actions
611
+ }
612
+ ),
613
+ fullscreenButton
614
+ ] })
615
+ ]
616
+ }
617
+ );
618
+ }
345
619
  function MediaFeedInner({
346
620
  items,
347
621
  getId,
@@ -351,6 +625,8 @@ function MediaFeedInner({
351
625
  getAuthor,
352
626
  renderActions,
353
627
  renderMedia,
628
+ layout = "stacked",
629
+ allowFullscreen: allowFullscreenProp,
354
630
  activeItemId,
355
631
  defaultActiveItemId,
356
632
  onActiveItemChange,
@@ -367,6 +643,8 @@ function MediaFeedInner({
367
643
  ...rest
368
644
  }, forwardedRef) {
369
645
  const labels = { ...defaultLabels, ...labelOverrides };
646
+ const overlay = layout === "overlay";
647
+ const allowFullscreen = allowFullscreenProp ?? !overlay;
370
648
  const prefersReducedMotion = usePrefersReducedMotion();
371
649
  const allowAutoPlay = autoPlay && !prefersReducedMotion;
372
650
  const ids = items.map(getId);
@@ -486,8 +764,8 @@ function MediaFeedInner({
486
764
  }
487
765
  }, [activeId, idsKey, hasObserver, loading, error]);
488
766
  React2.useEffect(() => {
489
- if (!items.length) setFullscreen(false);
490
- }, [items.length]);
767
+ if (!items.length || !allowFullscreen) setFullscreen(false);
768
+ }, [items.length, allowFullscreen]);
491
769
  React2.useEffect(() => {
492
770
  if (loading || error || activeIndex < 0 || !playbackRequest || playbackRequest.itemId !== activeId)
493
771
  return;
@@ -577,12 +855,76 @@ function MediaFeedInner({
577
855
  playbackPosition = { src: media.src, timeMs: 0 };
578
856
  playbackPositions.current.set(id, playbackPosition);
579
857
  }
580
- const title = getTitle?.(item) || labels.item(index + 1, items.length);
858
+ const ownTitle = getTitle?.(item);
859
+ const title = ownTitle || labels.item(index + 1, items.length);
581
860
  const author = getAuthor?.(item);
582
861
  const caption = getCaption?.(item);
583
862
  const active = !loading && !error && pageVisible && (immersive ? fullscreen && id === activeId : !fullscreen && viewportVisible && id === activeId && id === visibleId);
584
863
  const playbackRequestId = active && playbackRequest?.itemId === id && consumedPlaybackRequests.current.get(id) !== playbackRequest.requestId ? playbackRequest.requestId : void 0;
585
- return /* @__PURE__ */ jsxs(
864
+ const fullscreenButton = allowFullscreen && !immersive && /* @__PURE__ */ jsx(
865
+ Button,
866
+ {
867
+ type: "button",
868
+ variant: "ghost",
869
+ size: "sm",
870
+ "aria-label": labels.fullscreen,
871
+ title: labels.fullscreen,
872
+ className: overlay ? cn(overlayButtonClass, "shrink-0 bg-transparent") : void 0,
873
+ onClick: (event) => {
874
+ fullscreenTriggerRef.current = event.currentTarget;
875
+ const open = () => {
876
+ select(id);
877
+ setFullscreen(true);
878
+ };
879
+ const video = event.currentTarget.closest('[data-slot="item"]')?.querySelector("video");
880
+ if (isInPictureInPicture(video))
881
+ void exitPictureInPicture(video).then(() => {
882
+ if (isInPictureInPicture(video)) video?.pause();
883
+ open();
884
+ });
885
+ else open();
886
+ },
887
+ children: /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4", "aria-hidden": "true" })
888
+ }
889
+ );
890
+ const mediaSlot = /* @__PURE__ */ jsx(
891
+ "div",
892
+ {
893
+ "data-slot": "media",
894
+ className: cn(
895
+ overlay ? (
896
+ // Black letterboxing around the clip, as on VideoCard.
897
+ "absolute inset-0 flex items-center justify-center bg-black"
898
+ ) : "bg-muted flex min-h-0 flex-1 items-center justify-center p-3",
899
+ classNames.media
900
+ ),
901
+ children: renderMedia ? renderMedia(media, {
902
+ item,
903
+ active,
904
+ autoPlay: allowAutoPlay,
905
+ muted,
906
+ loop,
907
+ playbackRequestId
908
+ }) : /* @__PURE__ */ jsx(
909
+ FeedMedia,
910
+ {
911
+ media,
912
+ title,
913
+ active,
914
+ autoPlay: allowAutoPlay,
915
+ muted,
916
+ loop,
917
+ labels,
918
+ onActivate: () => select(id),
919
+ playbackPosition,
920
+ playbackRequestId,
921
+ overlay
922
+ },
923
+ `${id}:${media.kind}:${media.src}:${immersive}`
924
+ )
925
+ }
926
+ );
927
+ return /* @__PURE__ */ jsx(
586
928
  "article",
587
929
  {
588
930
  ref: immersive ? void 0 : (node) => {
@@ -597,44 +939,29 @@ function MediaFeedInner({
597
939
  "aria-setsize": items.length,
598
940
  tabIndex: id === activeId ? 0 : -1,
599
941
  className: cn(
600
- "border-border bg-card text-card-foreground focus-visible:ring-ring flex min-h-0 w-full snap-start flex-col overflow-hidden border focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
601
- immersive ? "h-full flex-1 border-0" : "h-full shrink-0 rounded-xl",
942
+ overlay ? cn(
943
+ "relative h-full min-h-0 w-full snap-start snap-always overflow-hidden bg-black focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-white/70",
944
+ OVERLAY_BAR_SCRIM
945
+ ) : "border-border bg-card text-card-foreground focus-visible:ring-ring flex min-h-0 w-full snap-start flex-col overflow-hidden border focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
946
+ overlay ? immersive ? "flex-1" : "shrink-0" : immersive ? "h-full flex-1 border-0" : "h-full shrink-0 rounded-xl",
602
947
  classNames.item
603
948
  ),
604
- children: [
949
+ children: overlay ? /* @__PURE__ */ jsxs(Fragment, { children: [
605
950
  /* @__PURE__ */ jsx(
606
- "div",
951
+ OverlayDetails,
607
952
  {
608
- "data-slot": "media",
609
- className: cn(
610
- "bg-muted flex min-h-0 flex-1 items-center justify-center p-3",
611
- classNames.media
612
- ),
613
- children: renderMedia ? renderMedia(media, {
614
- item,
615
- active,
616
- autoPlay: allowAutoPlay,
617
- muted,
618
- loop,
619
- playbackRequestId
620
- }) : /* @__PURE__ */ jsx(
621
- FeedMedia,
622
- {
623
- media,
624
- title,
625
- active,
626
- autoPlay: allowAutoPlay,
627
- muted,
628
- loop,
629
- labels,
630
- onActivate: () => select(id),
631
- playbackPosition,
632
- playbackRequestId
633
- },
634
- `${id}:${media.kind}:${media.src}:${immersive}`
635
- )
953
+ author,
954
+ title: ownTitle,
955
+ caption,
956
+ actions: renderActions?.(item),
957
+ fullscreenButton,
958
+ labels,
959
+ classNames
636
960
  }
637
961
  ),
962
+ mediaSlot
963
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
964
+ mediaSlot,
638
965
  /* @__PURE__ */ jsxs(
639
966
  "div",
640
967
  {
@@ -672,26 +999,15 @@ function MediaFeedInner({
672
999
  "h3",
673
1000
  {
674
1001
  "data-slot": "title",
675
- className: cn("m-0 text-base font-semibold", classNames.title),
1002
+ className: cn(
1003
+ "m-0 text-base font-semibold",
1004
+ classNames.title
1005
+ ),
676
1006
  children: title
677
1007
  }
678
1008
  )
679
1009
  ] }),
680
- !immersive && /* @__PURE__ */ jsx(
681
- Button,
682
- {
683
- variant: "ghost",
684
- size: "sm",
685
- "aria-label": labels.fullscreen,
686
- title: labels.fullscreen,
687
- onClick: (event) => {
688
- fullscreenTriggerRef.current = event.currentTarget;
689
- select(id);
690
- setFullscreen(true);
691
- },
692
- children: /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4", "aria-hidden": "true" })
693
- }
694
- )
1010
+ fullscreenButton
695
1011
  ] }),
696
1012
  caption != null && /* @__PURE__ */ jsx(
697
1013
  "div",
@@ -718,12 +1034,67 @@ function MediaFeedInner({
718
1034
  ]
719
1035
  }
720
1036
  )
721
- ]
1037
+ ] })
722
1038
  },
723
1039
  id
724
1040
  );
725
1041
  };
726
- const controls = /* @__PURE__ */ jsxs(
1042
+ const controls = overlay ? (
1043
+ // Over the middle of the feed's end edge, stacked the way the feed
1044
+ // scrolls, above the actions and the player's bar at the bottom. On a
1045
+ // short screen (a phone held sideways, as SuperChat reckons it) the middle
1046
+ // runs into the actions, so they move to the top end, clear of a host's
1047
+ // Back at the top start.
1048
+ /* @__PURE__ */ jsxs(
1049
+ "div",
1050
+ {
1051
+ "data-slot": "controls",
1052
+ className: cn(
1053
+ "pointer-events-none absolute end-2 top-1/2 z-10 flex -translate-y-1/2 flex-col items-center gap-1.5 [@media(max-height:30rem)]:top-2 [@media(max-height:30rem)]:translate-y-0",
1054
+ classNames.controls
1055
+ ),
1056
+ children: [
1057
+ /* @__PURE__ */ jsx(
1058
+ Button,
1059
+ {
1060
+ type: "button",
1061
+ variant: "ghost",
1062
+ size: "icon",
1063
+ "aria-label": labels.previous,
1064
+ title: labels.previous,
1065
+ className: cn(overlayButtonClass, "pointer-events-auto"),
1066
+ disabled: activeIndex <= 0,
1067
+ onClick: () => navigate(activeIndex - 1),
1068
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-5 w-5", "aria-hidden": "true" })
1069
+ }
1070
+ ),
1071
+ /* @__PURE__ */ jsx(
1072
+ "span",
1073
+ {
1074
+ role: "status",
1075
+ "aria-live": "polite",
1076
+ className: "rounded-full bg-black/45 px-1.5 py-0.5 text-xs whitespace-nowrap text-white tabular-nums backdrop-blur-sm",
1077
+ children: /* @__PURE__ */ jsx("bdi", { children: labels.position(activeIndex + 1, items.length) })
1078
+ }
1079
+ ),
1080
+ /* @__PURE__ */ jsx(
1081
+ Button,
1082
+ {
1083
+ type: "button",
1084
+ variant: "ghost",
1085
+ size: "icon",
1086
+ "aria-label": labels.next,
1087
+ title: labels.next,
1088
+ className: cn(overlayButtonClass, "pointer-events-auto"),
1089
+ disabled: activeIndex >= items.length - 1,
1090
+ onClick: () => navigate(activeIndex + 1),
1091
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-5 w-5", "aria-hidden": "true" })
1092
+ }
1093
+ )
1094
+ ]
1095
+ }
1096
+ )
1097
+ ) : /* @__PURE__ */ jsxs(
727
1098
  "div",
728
1099
  {
729
1100
  "data-slot": "controls",
@@ -735,6 +1106,7 @@ function MediaFeedInner({
735
1106
  /* @__PURE__ */ jsx(
736
1107
  Button,
737
1108
  {
1109
+ type: "button",
738
1110
  variant: "ghost",
739
1111
  size: "sm",
740
1112
  className: "h-auto min-h-8 flex-1 py-2 [&_[data-slot=button-label]]:whitespace-normal",
@@ -750,12 +1122,13 @@ function MediaFeedInner({
750
1122
  role: "status",
751
1123
  "aria-live": "polite",
752
1124
  className: "text-muted-foreground shrink-0 text-sm whitespace-nowrap",
753
- children: labels.position(activeIndex + 1, items.length)
1125
+ children: /* @__PURE__ */ jsx("bdi", { children: labels.position(activeIndex + 1, items.length) })
754
1126
  }
755
1127
  ),
756
1128
  /* @__PURE__ */ jsx(
757
1129
  Button,
758
1130
  {
1131
+ type: "button",
759
1132
  variant: "ghost",
760
1133
  size: "sm",
761
1134
  className: "h-auto min-h-8 flex-1 py-2 [&_[data-slot=button-label]]:whitespace-normal",
@@ -774,8 +1147,11 @@ function MediaFeedInner({
774
1147
  ref: setRoot,
775
1148
  "data-slot": "root",
776
1149
  "data-component": "media-feed",
1150
+ "data-layout": layout,
1151
+ "data-theme": overlay ? "dark" : void 0,
777
1152
  className: cn(
778
1153
  "bg-background text-foreground flex h-[min(48rem,80dvh)] min-h-0 w-full flex-col",
1154
+ overlay && "relative",
779
1155
  classNames.root,
780
1156
  className
781
1157
  ),
@@ -796,6 +1172,7 @@ function MediaFeedInner({
796
1172
  error && onRetry && /* @__PURE__ */ jsx(
797
1173
  Button,
798
1174
  {
1175
+ type: "button",
799
1176
  variant: "outline",
800
1177
  isLoading: retrying,
801
1178
  loadingText: labels.loading,
@@ -820,7 +1197,8 @@ function MediaFeedInner({
820
1197
  tabIndex: 0,
821
1198
  onKeyDown: handleKeyDown,
822
1199
  className: cn(
823
- "focus-visible:ring-ring relative flex min-h-0 flex-1 snap-y snap-mandatory flex-col gap-4 overflow-y-auto overscroll-contain focus-visible:ring-2 focus-visible:outline-none",
1200
+ "focus-visible:ring-ring relative flex min-h-0 flex-1 snap-y snap-mandatory flex-col overflow-y-auto overscroll-contain focus-visible:ring-2 focus-visible:outline-none",
1201
+ !overlay && "gap-4",
824
1202
  classNames.viewport
825
1203
  ),
826
1204
  children: items.map((item, index) => renderCard(item, index, false))
@@ -831,7 +1209,7 @@ function MediaFeedInner({
831
1209
  /* @__PURE__ */ jsxs(
832
1210
  Modal,
833
1211
  {
834
- open: fullscreen && !loading && !error && !!items.length,
1212
+ open: allowFullscreen && fullscreen && !loading && !error && !!items.length,
835
1213
  onOpenChange: setFullscreen,
836
1214
  size: "full",
837
1215
  className: "h-dvh min-h-0 sm:h-[calc(100dvh-2rem)]",
@@ -845,12 +1223,15 @@ function MediaFeedInner({
845
1223
  {
846
1224
  "data-slot": "fullscreen",
847
1225
  "data-component": "media-feed",
1226
+ "data-layout": layout,
1227
+ "data-theme": overlay ? "dark" : void 0,
848
1228
  role: "feed",
849
1229
  "aria-label": labels.feed,
850
1230
  tabIndex: 0,
851
1231
  onKeyDown: handleKeyDown,
852
1232
  className: cn(
853
1233
  "flex min-h-0 flex-1 flex-col px-2 pb-2 sm:px-4",
1234
+ overlay && "relative",
854
1235
  classNames.fullscreen
855
1236
  ),
856
1237
  children: [
@@ -870,5 +1251,5 @@ var MediaFeed = React2.forwardRef(MediaFeedInner);
870
1251
  MediaFeed.displayName = "MediaFeed";
871
1252
 
872
1253
  export { MediaFeed };
873
- //# sourceMappingURL=chunk-RTADGBBN.js.map
874
- //# sourceMappingURL=chunk-RTADGBBN.js.map
1254
+ //# sourceMappingURL=chunk-JUY2JLFL.js.map
1255
+ //# sourceMappingURL=chunk-JUY2JLFL.js.map