@stream-io/video-react-sdk 1.43.2 → 2.0.0-beta.1

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 (328) hide show
  1. package/CHANGELOG.md +18 -19
  2. package/dist/css/embedded.css +2592 -1498
  3. package/dist/css/embedded.css.map +1 -1
  4. package/dist/css/styles.css +2388 -1222
  5. package/dist/css/styles.css.map +1 -1
  6. package/dist/embedded.cjs.js +535 -476
  7. package/dist/embedded.cjs.js.map +1 -1
  8. package/dist/esm/index.js +10 -7
  9. package/dist/esm/index.js.map +1 -1
  10. package/dist/esm/src/components/Avatar/Avatar.js +13 -5
  11. package/dist/esm/src/components/Avatar/Avatar.js.map +1 -1
  12. package/dist/esm/src/components/Badge/Badge.js +21 -0
  13. package/dist/esm/src/components/Badge/Badge.js.map +1 -0
  14. package/dist/esm/src/components/Button/Button.js +10 -0
  15. package/dist/esm/src/components/Button/Button.js.map +1 -0
  16. package/dist/esm/src/components/Button/CompositeButton.js +10 -17
  17. package/dist/esm/src/components/Button/CompositeButton.js.map +1 -1
  18. package/dist/esm/src/components/Button/IconButton.js +3 -9
  19. package/dist/esm/src/components/Button/IconButton.js.map +1 -1
  20. package/dist/esm/src/components/CallControls/AcceptCallButton.js +3 -2
  21. package/dist/esm/src/components/CallControls/AcceptCallButton.js.map +1 -1
  22. package/dist/esm/src/components/CallControls/CallControls.js +3 -0
  23. package/dist/esm/src/components/CallControls/CallControls.js.map +1 -1
  24. package/dist/esm/src/components/CallControls/CancelCallButton.js +11 -6
  25. package/dist/esm/src/components/CallControls/CancelCallButton.js.map +1 -1
  26. package/dist/esm/src/components/CallControls/ReactionsButton.js +7 -3
  27. package/dist/esm/src/components/CallControls/ReactionsButton.js.map +1 -1
  28. package/dist/esm/src/components/CallControls/RecordCallButton.js +17 -11
  29. package/dist/esm/src/components/CallControls/RecordCallButton.js.map +1 -1
  30. package/dist/esm/src/components/CallControls/ScreenShareButton.js +11 -4
  31. package/dist/esm/src/components/CallControls/ScreenShareButton.js.map +1 -1
  32. package/dist/esm/src/components/CallControls/ToggleAudioButton.js +28 -17
  33. package/dist/esm/src/components/CallControls/ToggleAudioButton.js.map +1 -1
  34. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js +9 -4
  35. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js.map +1 -1
  36. package/dist/esm/src/components/CallControls/ToggleVideoButton.js +32 -23
  37. package/dist/esm/src/components/CallControls/ToggleVideoButton.js.map +1 -1
  38. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js +3 -3
  39. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js.map +1 -1
  40. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js +8 -3
  41. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js.map +1 -1
  42. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js +20 -7
  43. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -1
  44. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js +8 -5
  45. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js.map +1 -1
  46. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js +5 -2
  47. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js.map +1 -1
  48. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js +7 -3
  49. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js.map +1 -1
  50. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js +1 -1
  51. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js.map +1 -1
  52. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js +1 -1
  53. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js.map +1 -1
  54. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js +2 -2
  55. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js.map +1 -1
  56. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js +8 -5
  57. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js.map +1 -1
  58. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js +10 -4
  59. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js.map +1 -1
  60. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js +1 -1
  61. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js.map +1 -1
  62. package/dist/esm/src/components/Menu/GenericMenu.js +2 -1
  63. package/dist/esm/src/components/Menu/GenericMenu.js.map +1 -1
  64. package/dist/esm/src/components/Menu/MenuToggle.js +3 -0
  65. package/dist/esm/src/components/Menu/MenuToggle.js.map +1 -1
  66. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js +6 -3
  67. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js.map +1 -1
  68. package/dist/esm/src/components/Notification/Notification.js +12 -3
  69. package/dist/esm/src/components/Notification/Notification.js.map +1 -1
  70. package/dist/esm/src/components/Notification/PermissionNotification.js +2 -2
  71. package/dist/esm/src/components/Notification/PermissionNotification.js.map +1 -1
  72. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js +7 -3
  73. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js.map +1 -1
  74. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js +7 -2
  75. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js.map +1 -1
  76. package/dist/esm/src/components/Permissions/PermissionRequests.js +13 -12
  77. package/dist/esm/src/components/Permissions/PermissionRequests.js.map +1 -1
  78. package/dist/esm/src/components/RingingCall/RingingCall.js +38 -14
  79. package/dist/esm/src/components/RingingCall/RingingCall.js.map +1 -1
  80. package/dist/esm/src/components/RingingCall/RingingCallControls.js +7 -3
  81. package/dist/esm/src/components/RingingCall/RingingCallControls.js.map +1 -1
  82. package/dist/esm/src/components/Search/SearchInput.js +1 -1
  83. package/dist/esm/src/components/Search/SearchInput.js.map +1 -1
  84. package/dist/esm/src/components/StreamTheme/StreamTheme.js +2 -2
  85. package/dist/esm/src/components/StreamTheme/StreamTheme.js.map +1 -1
  86. package/dist/esm/src/components/Tooltip/Tooltip.js +8 -8
  87. package/dist/esm/src/components/Tooltip/Tooltip.js.map +1 -1
  88. package/dist/esm/src/components/VideoPreview/VideoPreview.js +36 -5
  89. package/dist/esm/src/components/VideoPreview/VideoPreview.js.map +1 -1
  90. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js +38 -12
  91. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js.map +1 -1
  92. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js +9 -5
  93. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -1
  94. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js +9 -6
  95. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js.map +1 -1
  96. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js +9 -6
  97. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js.map +1 -1
  98. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js +11 -6
  99. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js.map +1 -1
  100. package/dist/esm/src/core/components/ParticipantView/ConnectionQualityIndicator.js +25 -0
  101. package/dist/esm/src/core/components/ParticipantView/ConnectionQualityIndicator.js.map +1 -0
  102. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js +17 -13
  103. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js.map +1 -1
  104. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js +13 -8
  105. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js.map +1 -1
  106. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js +24 -3
  107. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js.map +1 -1
  108. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js +3 -13
  109. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js.map +1 -1
  110. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js +4 -2
  111. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js.map +1 -1
  112. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js +4 -2
  113. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js.map +1 -1
  114. package/dist/esm/src/embedded/EmbeddedClientProvider.js +7 -4
  115. package/dist/esm/src/embedded/EmbeddedClientProvider.js.map +1 -1
  116. package/dist/esm/src/embedded/call/CallControls.js +10 -6
  117. package/dist/esm/src/embedded/call/CallControls.js.map +1 -1
  118. package/dist/esm/src/embedded/call/CallHeader.js +10 -7
  119. package/dist/esm/src/embedded/call/CallHeader.js.map +1 -1
  120. package/dist/esm/src/embedded/call/CallLayout.js +8 -5
  121. package/dist/esm/src/embedded/call/CallLayout.js.map +1 -1
  122. package/dist/esm/src/embedded/call/CallStateRouter.js +7 -4
  123. package/dist/esm/src/embedded/call/CallStateRouter.js.map +1 -1
  124. package/dist/esm/src/embedded/hooks/useLayout.js +7 -4
  125. package/dist/esm/src/embedded/hooks/useLayout.js.map +1 -1
  126. package/dist/esm/src/embedded/livestream/host/HostLayout.js +12 -7
  127. package/dist/esm/src/embedded/livestream/host/HostLayout.js.map +1 -1
  128. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js +12 -7
  129. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js.map +1 -1
  130. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js +9 -6
  131. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js.map +1 -1
  132. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js +12 -9
  133. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js.map +1 -1
  134. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js +7 -4
  135. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js.map +1 -1
  136. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js +13 -9
  137. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js.map +1 -1
  138. package/dist/esm/src/embedded/shared/CallDuration/CallDuration.js +40 -0
  139. package/dist/esm/src/embedded/shared/CallDuration/CallDuration.js.map +1 -0
  140. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js +8 -6
  141. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js.map +1 -1
  142. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js +26 -2
  143. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js.map +1 -1
  144. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js +12 -6
  145. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js.map +1 -1
  146. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js +8 -6
  147. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js.map +1 -1
  148. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js +15 -14
  149. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js.map +1 -1
  150. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js +4 -0
  151. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js.map +1 -1
  152. package/dist/esm/src/embedded/shared/JoinError/JoinError.js +8 -6
  153. package/dist/esm/src/embedded/shared/JoinError/JoinError.js.map +1 -1
  154. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js +11 -7
  155. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js.map +1 -1
  156. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js +6 -4
  157. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js.map +1 -1
  158. package/dist/esm/src/embedded/shared/Lobby/Lobby.js +10 -9
  159. package/dist/esm/src/embedded/shared/Lobby/Lobby.js.map +1 -1
  160. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js +6 -4
  161. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js.map +1 -1
  162. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js +8 -6
  163. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js.map +1 -1
  164. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js +11 -9
  165. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js.map +1 -1
  166. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js +6 -4
  167. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js.map +1 -1
  168. package/dist/esm/src/hooks/useDeviceList.js +4 -2
  169. package/dist/esm/src/hooks/useDeviceList.js.map +1 -1
  170. package/dist/esm/src/i18n/Streami18n.js +58 -0
  171. package/dist/esm/src/i18n/Streami18n.js.map +1 -0
  172. package/dist/esm/src/i18n/TranslationContext.js +42 -0
  173. package/dist/esm/src/i18n/TranslationContext.js.map +1 -0
  174. package/dist/esm/src/i18n/runtimeDefaults.js +20 -0
  175. package/dist/esm/src/i18n/runtimeDefaults.js.map +1 -0
  176. package/dist/esm/src/i18n/useStreami18n.js +49 -0
  177. package/dist/esm/src/i18n/useStreami18n.js.map +1 -0
  178. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js +4 -0
  179. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js.map +1 -1
  180. package/dist/index.cjs.js +559 -623
  181. package/dist/index.cjs.js.map +1 -1
  182. package/dist/index.d.ts +1 -1
  183. package/dist/src/components/Avatar/Avatar.d.ts +9 -4
  184. package/dist/src/components/Badge/Badge.d.ts +23 -0
  185. package/dist/src/components/Badge/index.d.ts +1 -0
  186. package/dist/src/components/Button/Button.d.ts +16 -0
  187. package/dist/src/components/Button/CompositeButton.d.ts +6 -3
  188. package/dist/src/components/Button/IconButton.d.ts +7 -7
  189. package/dist/src/components/Button/index.d.ts +2 -2
  190. package/dist/src/components/CallControls/CancelCallButton.d.ts +3 -1
  191. package/dist/src/components/CallControls/ToggleAudioButton.d.ts +3 -3
  192. package/dist/src/components/CallControls/ToggleAudioOutputButton.d.ts +2 -2
  193. package/dist/src/components/CallControls/ToggleVideoButton.d.ts +3 -3
  194. package/dist/src/components/CallControls/index.d.ts +0 -1
  195. package/dist/src/components/Menu/GenericMenu.d.ts +1 -0
  196. package/dist/src/components/Notification/Notification.d.ts +2 -1
  197. package/dist/src/components/StreamTheme/StreamTheme.d.ts +2 -1
  198. package/dist/src/components/VideoPreview/VideoPreview.d.ts +6 -1
  199. package/dist/src/components/index.d.ts +1 -1
  200. package/dist/src/core/components/ParticipantView/ConnectionQualityIndicator.d.ts +10 -0
  201. package/dist/src/core/components/ParticipantView/index.d.ts +1 -0
  202. package/dist/src/core/components/StreamVideo/StreamVideo.d.ts +18 -2
  203. package/dist/src/embedded/shared/CallDuration/CallDuration.d.ts +9 -0
  204. package/dist/src/embedded/shared/index.d.ts +1 -0
  205. package/dist/src/i18n/Streami18n.d.ts +56 -0
  206. package/dist/src/i18n/TranslationContext.d.ts +30 -0
  207. package/dist/src/i18n/index.d.ts +16 -0
  208. package/dist/src/i18n/keys.d.ts +164 -0
  209. package/dist/src/i18n/runtimeDefaults.d.ts +17 -0
  210. package/dist/src/i18n/types.d.ts +45 -0
  211. package/dist/src/i18n/useStreami18n.d.ts +19 -0
  212. package/index.ts +1 -1
  213. package/package.json +18 -13
  214. package/src/components/Avatar/Avatar.tsx +53 -19
  215. package/src/components/Badge/Badge.tsx +48 -0
  216. package/src/components/Badge/index.ts +1 -0
  217. package/src/components/Button/Button.tsx +49 -0
  218. package/src/components/Button/CompositeButton.tsx +82 -73
  219. package/src/components/Button/IconButton.tsx +19 -29
  220. package/src/components/Button/index.ts +2 -2
  221. package/src/components/CallControls/AcceptCallButton.tsx +3 -1
  222. package/src/components/CallControls/CancelCallButton.tsx +32 -24
  223. package/src/components/CallControls/ReactionsButton.tsx +7 -3
  224. package/src/components/CallControls/RecordCallButton.tsx +50 -23
  225. package/src/components/CallControls/ScreenShareButton.tsx +27 -6
  226. package/src/components/CallControls/ToggleAudioButton.tsx +62 -61
  227. package/src/components/CallControls/ToggleAudioOutputButton.tsx +7 -4
  228. package/src/components/CallControls/ToggleVideoButton.tsx +66 -66
  229. package/src/components/CallControls/index.ts +0 -1
  230. package/src/components/CallParticipantsList/BlockedUserListing.tsx +9 -2
  231. package/src/components/CallParticipantsList/CallParticipantListHeader.tsx +10 -3
  232. package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +33 -8
  233. package/src/components/CallParticipantsList/CallParticipantsList.tsx +21 -5
  234. package/src/components/CallParticipantsList/EmptyParticipantSearchList.tsx +2 -2
  235. package/src/components/CallRecordingList/CallRecordingListHeader.tsx +11 -3
  236. package/src/components/DeviceSettings/DeviceAudioPreviewItem.tsx +3 -1
  237. package/src/components/DeviceSettings/DeviceSelector.tsx +1 -1
  238. package/src/components/DeviceSettings/DeviceSelectorAudio.tsx +2 -7
  239. package/src/components/DeviceSettings/DeviceSettings.tsx +14 -5
  240. package/src/components/DeviceSettings/SpeakerTest.tsx +7 -2
  241. package/src/components/DropdownSelect/DropdownSelect.tsx +3 -1
  242. package/src/components/Menu/GenericMenu.tsx +4 -0
  243. package/src/components/Notification/MicCaptureErrorNotification.tsx +7 -2
  244. package/src/components/Notification/Notification.tsx +20 -4
  245. package/src/components/Notification/PermissionNotification.tsx +2 -0
  246. package/src/components/Notification/RecordingInProgressNotification.tsx +5 -6
  247. package/src/components/Notification/SpeakingWhileMutedNotification.tsx +8 -2
  248. package/src/components/Permissions/PermissionRequests.tsx +50 -43
  249. package/src/components/RingingCall/RingingCall.tsx +40 -14
  250. package/src/components/RingingCall/RingingCallControls.tsx +2 -1
  251. package/src/components/Search/SearchInput.tsx +2 -3
  252. package/src/components/StreamTheme/StreamTheme.tsx +3 -1
  253. package/src/components/Tooltip/Tooltip.tsx +3 -10
  254. package/src/components/VideoPreview/VideoPreview.tsx +50 -5
  255. package/src/components/index.ts +1 -1
  256. package/src/core/components/CallLayout/LivestreamLayout.tsx +48 -18
  257. package/src/core/components/CallLayout/PaginatedGridLayout.tsx +14 -0
  258. package/src/core/components/CallLayout/PipLayout/Grid.tsx +10 -2
  259. package/src/core/components/CallLayout/PipLayout/Pip.tsx +6 -2
  260. package/src/core/components/CallLayout/SpeakerLayout.tsx +27 -0
  261. package/src/core/components/CallLayout/partcipantFilter.test.ts +38 -31
  262. package/src/core/components/ParticipantView/ConnectionQualityIndicator.tsx +54 -0
  263. package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +60 -27
  264. package/src/core/components/ParticipantView/ParticipantActionsContextMenu.tsx +63 -22
  265. package/src/core/components/ParticipantView/index.ts +1 -0
  266. package/src/core/components/StreamVideo/StreamVideo.tsx +39 -4
  267. package/src/core/components/Video/BaseVideoPlaceholder.tsx +13 -30
  268. package/src/core/components/Video/DefaultPictureInPicturePlaceholder.tsx +5 -2
  269. package/src/core/components/Video/DefaultVideoPlaceholder.tsx +2 -2
  270. package/src/embedded/call/CallControls.tsx +9 -18
  271. package/src/embedded/call/CallHeader.tsx +3 -12
  272. package/src/embedded/call/CallLayout.tsx +1 -1
  273. package/src/embedded/livestream/host/HostLayout.tsx +26 -20
  274. package/src/embedded/livestream/host/HostStateRouter.tsx +12 -8
  275. package/src/embedded/livestream/viewer/ViewerLayout.tsx +8 -11
  276. package/src/embedded/livestream/viewer/ViewerLobby.tsx +31 -15
  277. package/src/embedded/shared/BlurToggleButton/BlurToggleButton.tsx +18 -12
  278. package/src/embedded/shared/CallDuration/CallDuration.tsx +32 -0
  279. package/src/embedded/shared/CallFeedback/CallEndedScreen.tsx +27 -13
  280. package/src/embedded/shared/CallFeedback/RatingScreen.tsx +20 -12
  281. package/src/embedded/shared/CallFeedback/StarRating.tsx +10 -3
  282. package/src/embedded/shared/CallFeedback/ThankYouScreen.tsx +6 -3
  283. package/src/embedded/shared/ConnectionNotification/ConnectionNotification.tsx +21 -14
  284. package/src/embedded/shared/JoinError/JoinError.tsx +7 -6
  285. package/src/embedded/shared/Lobby/DeviceControls.tsx +25 -3
  286. package/src/embedded/shared/Lobby/Lobby.tsx +19 -22
  287. package/src/embedded/shared/Lobby/ToggleMicButton.tsx +10 -3
  288. package/src/embedded/shared/Lobby/VideoPreviewFallbacks.tsx +4 -2
  289. package/src/embedded/shared/index.ts +1 -0
  290. package/src/hooks/useDeviceList.tsx +2 -2
  291. package/src/i18n/Streami18n.ts +67 -0
  292. package/src/i18n/TranslationContext.tsx +55 -0
  293. package/src/i18n/index.ts +22 -0
  294. package/src/i18n/keys.ts +169 -0
  295. package/src/i18n/runtimeDefaults.ts +17 -0
  296. package/src/i18n/types.ts +78 -0
  297. package/src/i18n/useStreami18n.ts +66 -0
  298. package/src/utilities/filter.test.ts +132 -124
  299. package/src/utilities/normalizeString.test.ts +50 -48
  300. package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js +0 -59
  301. package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js.map +0 -1
  302. package/dist/esm/src/components/Button/TextButton.js +0 -8
  303. package/dist/esm/src/components/Button/TextButton.js.map +0 -1
  304. package/dist/esm/src/components/CallControls/CallStatsButton.js +0 -18
  305. package/dist/esm/src/components/CallControls/CallStatsButton.js.map +0 -1
  306. package/dist/esm/src/components/CallStats/CallStats.js +0 -180
  307. package/dist/esm/src/components/CallStats/CallStats.js.map +0 -1
  308. package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js +0 -57
  309. package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js.map +0 -1
  310. package/dist/esm/src/translations/en.json.js +0 -177
  311. package/dist/esm/src/translations/en.json.js.map +0 -1
  312. package/dist/esm/src/translations/index.js +0 -6
  313. package/dist/esm/src/translations/index.js.map +0 -1
  314. package/dist/latency-chart-CpL1M_s0.cjs.js +0 -59
  315. package/dist/latency-chart-CpL1M_s0.cjs.js.map +0 -1
  316. package/dist/src/components/Button/TextButton.d.ts +0 -2
  317. package/dist/src/components/CallControls/CallStatsButton.d.ts +0 -4
  318. package/dist/src/components/CallStats/CallStats.d.ts +0 -22
  319. package/dist/src/components/CallStats/CallStatsLatencyChart.d.ts +0 -7
  320. package/dist/src/components/CallStats/index.d.ts +0 -1
  321. package/dist/src/translations/index.d.ts +0 -146
  322. package/src/components/Button/TextButton.tsx +0 -12
  323. package/src/components/CallControls/CallStatsButton.tsx +0 -37
  324. package/src/components/CallStats/CallStats.tsx +0 -439
  325. package/src/components/CallStats/CallStatsLatencyChart.tsx +0 -76
  326. package/src/components/CallStats/index.ts +0 -1
  327. package/src/translations/en.json +0 -154
  328. package/src/translations/index.ts +0 -3
@@ -4,7 +4,9 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
  var videoReactBindings = require('@stream-io/video-react-bindings');
6
6
  var videoClient = require('@stream-io/video-client');
7
- var react$1 = require('@floating-ui/react');
7
+ var i18n = require('@stream-io/i18n');
8
+ var react$1 = require('@stream-io/i18n/react');
9
+ var react$2 = require('@floating-ui/react');
8
10
  var clsx = require('clsx');
9
11
 
10
12
  const Audio = react.memo(function AudioRender({ participant, trackType = 'audioTrack', ...rest }) {
@@ -39,18 +41,169 @@ const ParticipantsAudio = (props) => {
39
41
  };
40
42
  ParticipantsAudio.displayName = 'ParticipantsAudio';
41
43
 
44
+ /**
45
+ * The SDK's bundled translation data: the keys that cannot carry an inline `defaultValue` at their
46
+ * call site. `Streami18n` layers it under *every* language, so a partial dictionary never knocks
47
+ * these out.
48
+ *
49
+ * Empty, and that is the intended steady state. The four lookups this SDK resolved from a runtime
50
+ * value — the only candidates for an entry here — are written as `switch` statements over literal
51
+ * `t()` calls instead, so every branch carries its own English copy inline and the codegen reads it
52
+ * straight off the call site. Nothing is left without a default. `timestamp.*` / `duration.*`
53
+ * formatter expressions would also live here; this SDK has none yet.
54
+ *
55
+ * Adding an entry later needs no wiring. `Streami18n` already spreads this into the core options and
56
+ * `scripts/generate-i18n-keys.mts` already reads this path, so a new key reaches `keys.ts` on the
57
+ * next `build-translations` run — where a key nothing references, or one that shadows an inline
58
+ * default, fails a codegen guard.
59
+ */
60
+ const runtimeDefaults = {};
61
+
62
+ /**
63
+ * Wrapper around [i18next](https://www.i18next.com/) for this SDK's translations. Pass an instance
64
+ * to `<StreamVideo i18nInstance={…}>` to control the language and the copy.
65
+ *
66
+ * The implementation lives in `@stream-io/i18n`, shared with the React Native SDK and with Chat.
67
+ * What this subclass adds is what is this package's own: its bundled translation data, and the two
68
+ * type parameters — core cannot supply either, because the key catalog is generated from *this*
69
+ * package's `t()` call sites.
70
+ *
71
+ * ## Overriding some of the English copy
72
+ *
73
+ * ```ts
74
+ * const i18n = new Streami18n({
75
+ * translationsForLanguage: {
76
+ * 'common.participants.label': 'People',
77
+ * },
78
+ * });
79
+ * ```
80
+ *
81
+ * ## Adding a language
82
+ *
83
+ * ```ts
84
+ * import 'dayjs/locale/nl';
85
+ *
86
+ * const i18n = new Streami18n({ language: 'nl' });
87
+ * i18n.registerTranslation('nl', {
88
+ * 'callControls.cancelCallButton.leaveCall.title': 'Verlaten',
89
+ * });
90
+ * ```
91
+ *
92
+ * No dayjs locale file defines `calendar` — that field belongs to the calendar plugin — so a
93
+ * language whose relative dates must read natively needs the locale import *and* a `calendar`
94
+ * config passed as the third argument.
95
+ *
96
+ * Type your dictionary as `TranslationDictionary` to turn a typo or a key removed in a later major
97
+ * into a compile error. A **partial dictionary is safe**: any key you leave out renders the English
98
+ * copy that ships inline at each call site, never a raw dotted path — so there is no need to
99
+ * translate everything before shipping a language.
100
+ *
101
+ * Reactivity goes through `i18n.state`, a `StateStore`. `setLanguage()` returns nothing — the new
102
+ * `t` is published to that store, which the provider subscribes to.
103
+ */
104
+ class Streami18n extends i18n.Streami18n {
105
+ constructor(options = {}) {
106
+ super({
107
+ ...options,
108
+ // Merged, not replaced. Spreading `options` over a literal would let an integrator supplying
109
+ // one entry silently drop the SDK's own bundled data, which then renders as raw keys.
110
+ runtimeDefaults: { ...runtimeDefaults, ...options.runtimeDefaults },
111
+ });
112
+ }
113
+ }
114
+
115
+ /**
116
+ * The `t` in force before i18next has initialized, and the context's default.
117
+ *
118
+ * Core's factory, instantiated against this SDK's catalog so it is typed exactly like the real `t`.
119
+ * It honours the inline `defaultValue` each prose call site passes, which is what stops raw dotted
120
+ * keys flashing on the first frame.
121
+ */
122
+ const defaultTranslatorFunction = i18n.createDefaultTranslatorFunction();
123
+ /**
124
+ * Built from the shared factory in `@stream-io/i18n/react`.
125
+ *
126
+ * A **default value** is supplied rather than making the hook throw, and that is deliberate: it
127
+ * matches what `useI18n` did before this module existed, and it is what lets the `embedded/`
128
+ * components — which are used standalone, outside any provider — render correct English instead of
129
+ * crashing. Chat's React Native SDK throws instead; hence core exposes this as a factory.
130
+ */
131
+ const { TranslationContext, TranslationProvider, useTranslationContext } = react$1.createTranslationContext({
132
+ defaultValue: {
133
+ t: defaultTranslatorFunction,
134
+ tDateTimeParser: i18n.defaultDateTimeParser,
135
+ },
136
+ displayName: 'StreamVideoTranslationContext',
137
+ });
138
+ /**
139
+ * Reads `t` and `tDateTimeParser` from the closest provider.
140
+ *
141
+ * Works outside a provider too — the default translator renders each call site's inline English.
142
+ *
143
+ * The name is `useI18n` rather than core's `useTranslationContext` because ~500 call sites in this
144
+ * SDK already import it; renaming them is churn with no payoff.
145
+ *
146
+ * @example
147
+ * const { t } = useI18n();
148
+ * const label = t('callControls.cancelCallButton.leaveCall.title', 'Leave call');
149
+ */
150
+ const useI18n = useTranslationContext;
151
+
152
+ /**
153
+ * Resolves the translation context value: creates or adopts a {@link Streami18n} instance,
154
+ * registers the supplied dictionaries, initializes it and keeps `t` fresh across language changes.
155
+ *
156
+ * `init()` and the store subscription belong to `useStreami18nState` in `@stream-io/i18n/react`,
157
+ * shared with the React Native SDK.
158
+ */
159
+ const useStreami18n = ({ i18nInstance, language, translations, }) => {
160
+ // Lazy `useState`, not `useMemo`: `new Streami18n()` is a real allocation with identity that must
161
+ // survive a re-render, and this repo lints `react-hooks/purity` as an error — `useMemo` is a
162
+ // cache React is free to drop, so constructing there is a purity violation. A later change to
163
+ // `i18nInstance` is ignored on purpose; swapping the instance mid-tree is not a supported move.
164
+ const [i18n] = react.useState(() => {
165
+ const instance = i18nInstance ?? new Streami18n({ language });
166
+ // Registered here rather than in an effect. `useStreami18nState` calls `init()` from its own
167
+ // effect, which runs *before* any effect declared below it in this hook — so a dictionary
168
+ // registered in an effect would arrive after i18next was already initialized, and the first
169
+ // `init()` would warn that the language has no dictionary.
170
+ if (translations) {
171
+ for (const [code, dictionary] of Object.entries(translations)) {
172
+ instance.registerTranslation(code, dictionary);
173
+ }
174
+ }
175
+ return instance;
176
+ });
177
+ // No `onInitError` handler: core already logs a warning and leaves the instance usable, with `t`
178
+ // still rendering each call site's inline English. Pass one only to surface init failures
179
+ // somewhere other than the console.
180
+ const translators = react$1.useStreami18nState(i18n);
181
+ react.useEffect(() => {
182
+ // Guarded on the current value: `setLanguage` re-runs the formatter factories and a full
183
+ // i18next `changeLanguage`, and the first render already constructed the instance with
184
+ // `language`. Undefined means "leave whatever the instance was configured with".
185
+ if (!language || i18n.currentLanguage === language)
186
+ return;
187
+ i18n.setLanguage(language);
188
+ }, [i18n, language]);
189
+ // Returned as-is: `useStreami18nState` already caches this object and only produces a new one
190
+ // when `t` or `tDateTimeParser` actually changes, so wrapping it in a `useMemo` would add a
191
+ // second cache over a stable value.
192
+ return translators;
193
+ };
194
+
42
195
  const ParticipantViewContext = react.createContext(undefined);
43
196
  const useParticipantViewContext = () => react.useContext(ParticipantViewContext);
44
197
 
45
198
  const useFloatingUIPreset = ({ middleware = [], placement, strategy, offset: offsetInPx = 10, }) => {
46
- const { refs, x, y, update, elements: { domReference, floating }, context, } = react$1.useFloating({
199
+ const { refs, x, y, update, elements: { domReference, floating }, context, } = react$2.useFloating({
47
200
  placement,
48
201
  strategy,
49
202
  middleware: [
50
- react$1.offset(offsetInPx),
51
- react$1.shift(),
52
- react$1.flip(),
53
- react$1.size({
203
+ react$2.offset(offsetInPx),
204
+ react$2.shift(),
205
+ react$2.flip(),
206
+ react$2.size({
54
207
  padding: 10,
55
208
  apply: ({ availableHeight, elements }) => {
56
209
  Object.assign(elements.floating.style, {
@@ -65,7 +218,7 @@ const useFloatingUIPreset = ({ middleware = [], placement, strategy, offset: off
65
218
  react.useEffect(() => {
66
219
  if (!domReference || !floating)
67
220
  return;
68
- const cleanup = react$1.autoUpdate(domReference, floating, update);
221
+ const cleanup = react$2.autoUpdate(domReference, floating, update);
69
222
  return () => cleanup();
70
223
  }, [domReference, floating, update]);
71
224
  return { refs, x, y, domReference, floating, strategy, context };
@@ -177,7 +330,7 @@ const useRequestPermission = (permission) => {
177
330
  * 2. Maps the device list to a format more suitable for rendering.
178
331
  */
179
332
  function useDeviceList(devices, selectedDeviceId) {
180
- const { t } = videoReactBindings.useI18n();
333
+ const { t } = useI18n();
181
334
  return react.useMemo(() => {
182
335
  let selectedDeviceInfo = null;
183
336
  let selectedIndex = null;
@@ -193,7 +346,7 @@ function useDeviceList(devices, selectedDeviceId) {
193
346
  if (selectedDeviceInfo === null || selectedIndex === null) {
194
347
  const defaultDevice = {
195
348
  deviceId: 'default',
196
- label: t('Default'),
349
+ label: t('common.default.label', 'Default'),
197
350
  isSelected: true,
198
351
  };
199
352
  selectedDeviceInfo = defaultDevice;
@@ -333,7 +486,7 @@ const useMenuContext = () => {
333
486
  };
334
487
  const MenuPortal = ({ children, refs, }) => {
335
488
  const [portalRoot, setPortalRoot] = react.useState(null);
336
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: setPortalRoot, className: "str-video__portal" }), jsxRuntime.jsx(react$1.FloatingOverlay, { children: jsxRuntime.jsx(react$1.FloatingPortal, { root: portalRoot, children: jsxRuntime.jsx("div", { className: "str-video__portal-content", ref: refs.setFloating, children: children }) }) })] }));
489
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: setPortalRoot, className: "str-video__portal" }), jsxRuntime.jsx(react$2.FloatingOverlay, { children: jsxRuntime.jsx(react$2.FloatingPortal, { root: portalRoot, children: jsxRuntime.jsx("div", { className: "str-video__portal-content", ref: refs.setFloating, children: children }) }) })] }));
337
490
  };
338
491
  const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolute', offset, visualType = MenuVisualType.MENU, children, onToggle, }) => {
339
492
  const [menuShown, setMenuShown] = react.useState(false);
@@ -395,6 +548,7 @@ const GenericMenu = ({ children, onItemClick, }) => {
395
548
  const GenericMenuButtonItem = ({ children, ...rest }) => {
396
549
  return (jsxRuntime.jsx("li", { className: "str-video__generic-menu--item", children: jsxRuntime.jsx("button", { ...rest, children: children }) }));
397
550
  };
551
+ const GenericMenuSeparator = () => (jsxRuntime.jsx("li", { className: "str-video__generic-menu--separator", role: "separator" }));
398
552
 
399
553
  const Icon = ({ className, icon }) => (jsxRuntime.jsx("span", { className: clsx('str-video__icon', icon && `str-video__icon--${icon}`, className) }));
400
554
 
@@ -423,7 +577,7 @@ const ParticipantActionsContextMenu = () => {
423
577
  const [fullscreenModeOn, setFullscreenModeOn] = react.useState(!!document.fullscreenElement);
424
578
  const call = videoReactBindings.useCall();
425
579
  const isPiP = usePictureInPictureState(videoElement ?? undefined);
426
- const { t } = videoReactBindings.useI18n();
580
+ const { t } = useI18n();
427
581
  const { pin, sessionId, userId } = participant;
428
582
  const hasAudioTrack = videoClient.hasAudio(participant);
429
583
  const hasVideoTrack = videoClient.hasVideo(participant);
@@ -495,12 +649,14 @@ const ParticipantActionsContextMenu = () => {
495
649
  return document.exitPictureInPicture().catch(console.error);
496
650
  };
497
651
  const { close } = useMenuContext() || {};
498
- return (jsxRuntime.jsxs(GenericMenu, { onItemClick: close, children: [jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: toggleParticipantPin, disabled: pin && !pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), pin ? t('Unpin') : t('Pin')] }), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.PIN_FOR_EVERYONE], children: [jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: pinForEveryone, disabled: pin && !pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), t('Pin for everyone')] }), jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: unpinForEveryone, disabled: !pin || pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), t('Unpin for everyone')] })] }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.BLOCK_USERS], children: jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: blockUser, children: [jsxRuntime.jsx(Icon, { icon: "not-allowed" }), t('Block')] }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.KICK_USER], children: jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: kickUser, children: [jsxRuntime.jsx(Icon, { icon: "kick-user" }), t('Kick')] }) }), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.MUTE_USERS], children: [hasVideoTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteVideo, children: [jsxRuntime.jsx(Icon, { icon: "camera-off-outline" }), t('Turn off video')] })), hasScreenShareTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteScreenShare, children: [jsxRuntime.jsx(Icon, { icon: "screen-share-off" }), t('Turn off screen share')] })), hasAudioTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteAudio, children: [jsxRuntime.jsx(Icon, { icon: "no-audio" }), t('Mute audio')] })), hasScreenShareAudioTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteScreenShareAudio, children: [jsxRuntime.jsx(Icon, { icon: "no-audio" }), t('Mute screen share audio')] }))] }), participantViewElement &&
499
- typeof participantViewElement.requestFullscreen !== 'undefined' && (jsxRuntime.jsx(GenericMenuButtonItem, { onClick: toggleFullscreenMode, children: t('{{ direction }} fullscreen', {
500
- direction: fullscreenModeOn ? t('Leave') : t('Enter'),
501
- }) })), videoElement && document.pictureInPictureEnabled && (jsxRuntime.jsx(GenericMenuButtonItem, { onClick: togglePictureInPicture, children: t('{{ direction }} picture-in-picture', {
502
- direction: isPiP ? t('Leave') : t('Enter'),
503
- }) })), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.UPDATE_CALL_PERMISSIONS], children: [jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SEND_AUDIO), children: t('Allow audio') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SEND_VIDEO), children: t('Allow video') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SCREENSHARE), children: t('Allow screen sharing') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SEND_AUDIO), children: t('Disable audio') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SEND_VIDEO), children: t('Disable video') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SCREENSHARE), children: t('Disable screen sharing') })] })] }));
652
+ return (jsxRuntime.jsxs(GenericMenu, { onItemClick: close, children: [jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: toggleParticipantPin, disabled: pin && !pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), pin
653
+ ? t('participantView.unpin.label', 'Unpin')
654
+ : t('participantView.actionsMenu.pin.label', 'Pin')] }), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.PIN_FOR_EVERYONE], children: [jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: pinForEveryone, disabled: pin && !pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), t('participantView.actionsMenu.pinForEveryone.label', 'Pin for everyone')] }), jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: unpinForEveryone, disabled: !pin || pin.isLocalPin, children: [jsxRuntime.jsx(Icon, { icon: "pin" }), t('participantView.actionsMenu.unpinForEveryone.label', 'Unpin for everyone')] })] }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.BLOCK_USERS], children: jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: blockUser, children: [jsxRuntime.jsx(Icon, { icon: "not-allowed" }), t('participantView.actionsMenu.block.label', 'Block')] }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.KICK_USER], children: jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: kickUser, children: [jsxRuntime.jsx(Icon, { icon: "kick-user" }), t('participantView.actionsMenu.kick.label', 'Kick')] }) }), jsxRuntime.jsx(GenericMenuSeparator, {}), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.MUTE_USERS], children: [hasVideoTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteVideo, children: [jsxRuntime.jsx(Icon, { icon: "camera-off-outline" }), t('participantView.actionsMenu.turnOffVideo.label', 'Turn off video')] })), hasScreenShareTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteScreenShare, children: [jsxRuntime.jsx(Icon, { icon: "screen-share-off" }), t('participantView.actionsMenu.turnOffScreenShare.label', 'Turn off screen share')] })), hasAudioTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteAudio, children: [jsxRuntime.jsx(Icon, { icon: "no-audio" }), t('participantView.actionsMenu.muteAudio.label', 'Mute audio')] })), hasScreenShareAudioTrack && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: muteScreenShareAudio, children: [jsxRuntime.jsx(Icon, { icon: "no-audio" }), t('participantView.actionsMenu.muteScreenShareAudio.label', 'Mute screen share audio')] }))] }), participantViewElement &&
655
+ typeof participantViewElement.requestFullscreen !== 'undefined' && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: toggleFullscreenMode, children: [jsxRuntime.jsx(Icon, { icon: "fullscreen" }), fullscreenModeOn
656
+ ? t('participantView.actionsMenu.leaveFullscreen.label', 'Leave fullscreen')
657
+ : t('participantView.actionsMenu.enterFullscreen.label', 'Enter fullscreen')] })), videoElement && document.pictureInPictureEnabled && (jsxRuntime.jsxs(GenericMenuButtonItem, { onClick: togglePictureInPicture, children: [jsxRuntime.jsx(Icon, { icon: "pip" }), isPiP
658
+ ? t('participantView.actionsMenu.leavePip.label', 'Leave picture-in-picture')
659
+ : t('participantView.actionsMenu.enterPip.label', 'Enter picture-in-picture')] })), jsxRuntime.jsx(GenericMenuSeparator, {}), jsxRuntime.jsxs(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.UPDATE_CALL_PERMISSIONS], children: [jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SEND_AUDIO), children: t('participantView.actionsMenu.allowAudio.label', 'Allow audio') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SEND_VIDEO), children: t('participantView.actionsMenu.allowVideo.label', 'Allow video') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: grantPermission(videoClient.OwnCapability.SCREENSHARE), children: t('participantView.actionsMenu.allowScreenSharing.label', 'Allow screen sharing') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SEND_AUDIO), children: t('participantView.actionsMenu.disableAudio.label', 'Disable audio') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SEND_VIDEO), children: t('participantView.actionsMenu.disableVideo.label', 'Disable video') }), jsxRuntime.jsx(GenericMenuButtonItem, { onClick: revokePermission(videoClient.OwnCapability.SCREENSHARE), children: t('participantView.actionsMenu.disableScreenSharing.label', 'Disable screen sharing') })] })] }));
504
660
  };
505
661
 
506
662
  const isComponentType = (elementOrComponent) => {
@@ -565,34 +721,39 @@ const BaseVideo = react.forwardRef(function BaseVideoRender({ stream, ...rest },
565
721
  } }));
566
722
  });
567
723
 
568
- const BaseVideoPlaceholder = react.forwardRef(function DefaultVideoPlaceholder({ participant, style, children }, ref) {
724
+ const variantClasses = ({ size = 'md', showOutline }) => [
725
+ `str-video__avatar--size-${size}`,
726
+ showOutline && 'str-video__avatar--outlined',
727
+ ];
728
+ const Avatar = ({ imageSrc, name, style, className, size = 'md', showOutline, ...rest }) => {
569
729
  const [error, setError] = react.useState(false);
730
+ if (!imageSrc || error) {
731
+ return name ? (jsxRuntime.jsx(AvatarFallback, { className: className, style: style, names: name.split(/\s+/).filter(Boolean), size: size, showOutline: showOutline })) : null;
732
+ }
733
+ return (jsxRuntime.jsx("img", { onError: () => setError(true), alt: "avatar", className: clsx('str-video__avatar', variantClasses({ size, showOutline }), className), src: imageSrc, style: style, ...rest }));
734
+ };
735
+ const AvatarFallback = ({ className, names, style, size = 'md', showOutline, }) => {
736
+ const isCompact = size === 'xs' || size === 'sm';
737
+ return (jsxRuntime.jsx("div", { className: clsx('str-video__avatar--initials-fallback', variantClasses({ size, showOutline }), className), style: style, children: jsxRuntime.jsxs("div", { children: [names[0]?.[0], !isCompact && names[1]?.[0]] }) }));
738
+ };
739
+
740
+ const BaseVideoPlaceholder = react.forwardRef(function DefaultVideoPlaceholder({ participant, style, children }, ref) {
570
741
  const name = participant.name || participant.userId;
571
- return (jsxRuntime.jsxs("div", { className: "str-video__video-placeholder", style: style, ref: ref, children: [(!participant.image || error) &&
572
- (name ? (jsxRuntime.jsx(InitialsFallback, { name: name })) : (jsxRuntime.jsx("div", { className: "str-video__video-placeholder__no-video-label", children: children }))), participant.image && !error && (jsxRuntime.jsx("img", { onError: () => setError(true), alt: name, className: "str-video__video-placeholder__avatar", src: participant.image }))] }));
742
+ return (jsxRuntime.jsxs("div", { className: "str-video__video-placeholder", style: style, ref: ref, children: [(participant.image || name) && (jsxRuntime.jsx(Avatar, { size: "2xl", showOutline: true, imageSrc: participant.image, name: name })), !participant.image && !name && (jsxRuntime.jsx("div", { className: "str-video__video-placeholder__no-video-label", children: children }))] }));
573
743
  });
574
- const InitialsFallback = (props) => {
575
- const { name } = props;
576
- const initials = name
577
- .split(' ')
578
- .slice(0, 2)
579
- .map((n) => n[0])
580
- .join('');
581
- return (jsxRuntime.jsx("div", { className: "str-video__video-placeholder__initials-fallback", children: initials }));
582
- };
583
744
 
584
745
  const DefaultVideoPlaceholder = react.forwardRef(function DefaultVideoPlaceholderRender(props, ref) {
585
- const { t } = videoReactBindings.useI18n();
586
- return (jsxRuntime.jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('Video is disabled') }));
746
+ const { t } = useI18n();
747
+ return (jsxRuntime.jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('common.videoDisabled.text', 'Video is disabled') }));
587
748
  });
588
749
 
589
750
  const DefaultPictureInPicturePlaceholder = react.forwardRef(function DefaultPictureInPicturePlaceholderRender(props, ref) {
590
- const { t } = videoReactBindings.useI18n();
591
- return (jsxRuntime.jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('Video is playing in a popup') }));
751
+ const { t } = useI18n();
752
+ return (jsxRuntime.jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('videoPlaceholder.videoPlayingInPopup.text', 'Video is playing in a popup') }));
592
753
  });
593
754
 
594
755
  const ABSOLUTE_POSITION_STYLE = { position: 'absolute' };
595
- const Video$1 = ({ enabled = true, mirror, trackType, participant, className, VideoPlaceholder = DefaultVideoPlaceholder, PictureInPicturePlaceholder = DefaultPictureInPicturePlaceholder, refs, ...rest }) => {
756
+ const Video = ({ enabled = true, mirror, trackType, participant, className, VideoPlaceholder = DefaultVideoPlaceholder, PictureInPicturePlaceholder = DefaultPictureInPicturePlaceholder, refs, ...rest }) => {
596
757
  const { sessionId, videoStream, screenShareStream, viewportVisibilityState, isLocalParticipant, userId, } = participant;
597
758
  const call = videoReactBindings.useCall();
598
759
  const [videoElement, setVideoElement] = react.useState(null);
@@ -666,7 +827,7 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
666
827
  refs?.setVideoElement?.(element);
667
828
  } })), isPiP && PictureInPicturePlaceholder && (jsxRuntime.jsx(PictureInPicturePlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsxRuntime.jsx(VideoPlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
668
829
  };
669
- Video$1.displayName = 'Video';
830
+ Video.displayName = 'Video';
670
831
 
671
832
  const useTrackElementVisibility = ({ trackedElement, sessionId, trackType, }) => {
672
833
  const call = videoReactBindings.useCall();
@@ -677,14 +838,6 @@ const useTrackElementVisibility = ({ trackedElement, sessionId, trackType, }) =>
677
838
  }, [trackedElement, call, sessionId, trackType]);
678
839
  };
679
840
 
680
- const Avatar = ({ imageSrc, name, style, className, ...rest }) => {
681
- const [error, setError] = react.useState(false);
682
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(!imageSrc || error) && name && (jsxRuntime.jsx(AvatarFallback, { className: className, style: style, names: [name] })), imageSrc && !error && (jsxRuntime.jsx("img", { onError: () => setError(true), alt: "avatar", className: clsx('str-video__avatar', className), src: imageSrc, style: style, ...rest }))] }));
683
- };
684
- const AvatarFallback = ({ className, names, style, }) => {
685
- return (jsxRuntime.jsx("div", { className: clsx('str-video__avatar--initials-fallback', className), style: style, children: jsxRuntime.jsxs("div", { children: [names[0][0], names[1]?.[0]] }) }));
686
- };
687
-
688
841
  const BackgroundFiltersProviderImpl = react.lazy(() => Promise.resolve().then(function () { return require('./embedded-BackgroundFilters-DMStbSEg.cjs.js'); }).then((m) => ({
689
842
  default: m.BackgroundFiltersProvider,
690
843
  })));
@@ -713,43 +866,44 @@ const BackgroundFiltersProvider = (props) => {
713
866
  return (jsxRuntime.jsx(react.Suspense, { fallback: SuspenseFallback, children: jsxRuntime.jsx(BackgroundFiltersProviderImpl, { ...filterProps, ContextProvider: BackgroundFiltersContext }) }));
714
867
  };
715
868
 
716
- const IconButton = react.forwardRef(function IconButtonRender(props, ref) {
717
- const { icon, enabled, variant, onClick, className, ...rest } = props;
718
- return (jsxRuntime.jsx("button", { className: clsx('str-video__call-controls__button', className, {
719
- [`str-video__call-controls__button--variant-${variant}`]: variant,
720
- 'str-video__call-controls__button--enabled': enabled,
721
- }), onClick: (e) => {
722
- e.preventDefault();
723
- onClick?.(e);
724
- }, ref: ref, ...rest, children: jsxRuntime.jsx(Icon, { icon: icon }) }));
869
+ /**
870
+ * A compact badge for counts, notifications and error markers.
871
+ *
872
+ * Without `icon` it is a pill that grows with its content; with `icon` it is a
873
+ * circle of the size given by `size`.
874
+ *
875
+ * The badge carries no positioning of its own — whatever it annotates places
876
+ * it. For the call controls, rendering a badge among `CompositeButton`'s
877
+ * children anchors it to the top-right corner of the button.
878
+ */
879
+ const Badge = react.forwardRef(function BadgeRender({ variant = 'default', size = 'sm', icon, className, children, ...rest }, ref) {
880
+ return (jsxRuntime.jsx("span", { ref: ref, className: clsx('str-video__badge', `str-video__badge--${variant}`, `str-video__badge--size-${size}`, icon && 'str-video__badge--icon', className), ...rest, children: icon ? jsxRuntime.jsx(Icon, { icon: icon }) : children }));
725
881
  });
726
882
 
727
- const CompositeButton = react.forwardRef(function CompositeButtonRender({ disabled, caption, children, className, active, Menu, menuPlacement, menuOffset, title, ToggleMenuButton = DefaultToggleMenuButton, variant, onClick, onMenuToggle, ...restButtonProps }, ref) {
728
- return (jsxRuntime.jsxs("div", { className: clsx('str-video__composite-button', className, {
729
- 'str-video__composite-button--caption': caption,
730
- 'str-video__composite-button--menu': Menu,
731
- }), title: title, ref: ref, children: [jsxRuntime.jsxs("div", { className: clsx('str-video__composite-button__button-group', {
732
- 'str-video__composite-button__button-group--active': active,
733
- 'str-video__composite-button__button-group--active-primary': active && variant === 'primary',
734
- 'str-video__composite-button__button-group--active-secondary': active && variant === 'secondary',
735
- 'str-video__composite-button__button-group--disabled': disabled,
736
- }), children: [jsxRuntime.jsx("button", { type: "button", className: "str-video__composite-button__button", onClick: (e) => {
737
- e.preventDefault();
738
- onClick?.(e);
739
- }, disabled: disabled, ...restButtonProps, children: children }), Menu && (jsxRuntime.jsx(MenuToggle, { offset: menuOffset, placement: menuPlacement, ToggleButton: ToggleMenuButton, onToggle: onMenuToggle, children: isComponentType(Menu) ? jsxRuntime.jsx(Menu, {}) : Menu }))] }), caption && (jsxRuntime.jsx("div", { className: "str-video__composite-button__caption", children: caption }))] }));
883
+ const Button = react.forwardRef(function ButtonRender({ variant = 'primary', appearance = 'solid', size = 'md', active, className, type = 'button', children, ...rest }, ref) {
884
+ return (jsxRuntime.jsx("button", { ref: ref, type: type, className: clsx('str-video__button', `str-video__button--${variant}`, `str-video__button--${appearance}`, `str-video__button--size-${size}`, className), ...rest, "aria-pressed": active ?? rest['aria-pressed'], children: children }));
740
885
  });
741
- const DefaultToggleMenuButton = react.forwardRef(function DefaultToggleMenuButtonRender({ menuShown }, ref) {
742
- return (jsxRuntime.jsx(IconButton, { className: clsx('str-video__menu-toggle-button', {
743
- 'str-video__menu-toggle-button--active': menuShown,
744
- }), icon: menuShown ? 'caret-down' : 'caret-up', ref: ref }));
886
+
887
+ const IconButton = react.forwardRef(function IconButtonRender({ icon, className, ...props }, ref) {
888
+ return (jsxRuntime.jsx(Button, { ref: ref, className: clsx('str-video__button--icon-only', className), ...props, children: jsxRuntime.jsx(Icon, { icon: icon }) }));
745
889
  });
746
890
 
747
- const TextButton = ({ children, ...rest }) => {
748
- return (jsxRuntime.jsx("button", { ...rest, className: "str-video__text-button", children: children }));
749
- };
891
+ const CompositeButton = react.forwardRef(function CompositeButtonRender({ appearance, caption, children, className, disabled, Menu, menuOffset, menuPlacement, onMenuToggle, active, size = 'md', title, ToggleMenuButton = CompositeCaret, variant = 'secondary', ...buttonProps }, ref) {
892
+ return (jsxRuntime.jsxs("div", { ref: ref, title: title, className: clsx('str-video__composite-button', `str-video__composite-button--${variant}`, `str-video__composite-button--size-${size}`, `str-video__composite-button--${appearance ?? 'solid'}`, Menu && 'str-video__composite-button--menu', className), children: [jsxRuntime.jsxs("div", { className: "str-video__composite-button__group", children: [jsxRuntime.jsx(Button, { variant: variant, appearance: appearance, size: size, disabled: disabled, active: active, className: "str-video__composite-button__action str-video__button--icon-only", ...buttonProps, children: children }), Menu &&
893
+ (disabled ? (jsxRuntime.jsx(CompositeCaret, { menuShown: false, disabled: true })) : (jsxRuntime.jsx(MenuToggle, { offset: menuOffset, placement: menuPlacement, ToggleButton: ToggleMenuButton, onToggle: onMenuToggle, children: isComponentType(Menu) ? jsxRuntime.jsx(Menu, {}) : Menu })))] }), caption && (jsxRuntime.jsx("span", { className: "str-video__composite-button__caption", children: caption }))] }));
894
+ });
895
+ const CompositeCaret = react.forwardRef(function CompositeCaretRender({ menuShown, disabled }, ref) {
896
+ const { t } = useI18n();
897
+ return (jsxRuntime.jsx(IconButton, { className: "str-video__composite-button__caret", size: "xs", appearance: "ghost", disabled: disabled, "aria-label": t('common.toggleMenu.ariaLabel', 'Toggle menu'), "aria-haspopup": "menu", "aria-expanded": menuShown, icon: menuShown ? 'caret-down' : 'caret-up', ref: ref }));
898
+ });
750
899
 
900
+ const ICON_BY_STATE = {
901
+ success: 'checkmark',
902
+ error: 'exclamation-circle-fill',
903
+ loading: 'loading',
904
+ };
751
905
  const Notification = (props) => {
752
- const { isVisible, message, children, visibilityTimeout, resetIsVisible, placement = 'top', className, iconClassName = 'str-video__notification__icon', close, } = props;
906
+ const { isVisible, message, children, visibilityTimeout, resetIsVisible, placement = 'top', className, state, close, } = props;
753
907
  const { refs, x, y, strategy } = useFloatingUIPreset({
754
908
  placement,
755
909
  strategy: 'absolute',
@@ -762,12 +916,13 @@ const Notification = (props) => {
762
916
  }, visibilityTimeout);
763
917
  return () => clearTimeout(timeout);
764
918
  }, [isVisible, resetIsVisible, visibilityTimeout]);
765
- return (jsxRuntime.jsxs("div", { className: "str-video__notification-wrapper", ref: refs.setReference, children: [isVisible && (jsxRuntime.jsxs("div", { className: clsx('str-video__notification', className), ref: refs.setFloating, style: {
919
+ const icon = state && `str-video__icon str-video__icon--${ICON_BY_STATE[state]}`;
920
+ return (jsxRuntime.jsxs("div", { className: "str-video__notification-wrapper", ref: refs.setReference, children: [isVisible && (jsxRuntime.jsxs("div", { className: clsx('str-video__notification', state && `str-video__notification--${state}`, close && 'str-video__notification--dismissible', className), ref: refs.setFloating, style: {
766
921
  position: strategy,
767
922
  top: y ?? 0,
768
923
  left: x ?? 0,
769
924
  overflowY: 'auto',
770
- }, children: [iconClassName && jsxRuntime.jsx("i", { className: iconClassName }), jsxRuntime.jsx("span", { className: "str-video__notification__message", children: message }), close ? (jsxRuntime.jsx("i", { className: "str-video__icon str-video__icon--close str-video__notification__close", onClick: close })) : null] })), children] }));
925
+ }, children: [icon && jsxRuntime.jsx("i", { className: icon }), jsxRuntime.jsx("span", { className: "str-video__notification__message", children: message }), close ? (jsxRuntime.jsx("i", { className: "str-video__icon str-video__icon--close str-video__notification__close", onClick: close })) : null] })), children] }));
771
926
  };
772
927
 
773
928
  const PermissionNotification = (props) => {
@@ -790,22 +945,23 @@ const PermissionNotification = (props) => {
790
945
  }, [hasPermission]);
791
946
  const resetIsVisible = react.useCallback(() => setShowNotification(undefined), []);
792
947
  if (isAwaitingApproval) {
793
- return (jsxRuntime.jsx(Notification, { isVisible: isAwaitingApproval && !hasPermission, message: messageAwaitingApproval, children: children }));
948
+ return (jsxRuntime.jsx(Notification, { state: "loading", isVisible: isAwaitingApproval && !hasPermission, message: messageAwaitingApproval, children: children }));
794
949
  }
795
- return (jsxRuntime.jsx(Notification, { isVisible: !!showNotification, visibilityTimeout: visibilityTimeout, resetIsVisible: resetIsVisible, message: showNotification === 'granted' ? messageApproved : messageRevoked, children: children }));
950
+ return (jsxRuntime.jsx(Notification, { state: showNotification === 'granted' ? 'success' : 'error', isVisible: !!showNotification, visibilityTimeout: visibilityTimeout, resetIsVisible: resetIsVisible, message: showNotification === 'granted' ? messageApproved : messageRevoked, children: children }));
796
951
  };
797
952
 
798
953
  const SpeakingWhileMutedNotification = ({ children, text, placement, }) => {
799
954
  const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
800
955
  const { isSpeakingWhileMuted } = useMicrophoneState();
801
- const { t } = videoReactBindings.useI18n();
802
- const message = text ?? t('You are muted. Unmute to speak.');
956
+ const { t } = useI18n();
957
+ const message = text ??
958
+ t('notification.speakingWhileMuted.text', 'You are muted. Unmute to speak.');
803
959
  return (jsxRuntime.jsx(Notification, { message: message, isVisible: isSpeakingWhileMuted, placement: placement || 'top-start', children: children }));
804
960
  };
805
961
 
806
962
  const MicCaptureErrorNotification = ({ children, text, placement, }) => {
807
963
  const call = videoReactBindings.useCall();
808
- const { t } = videoReactBindings.useI18n();
964
+ const { t } = useI18n();
809
965
  const [isVisible, setIsVisible] = react.useState(false);
810
966
  react.useEffect(() => {
811
967
  if (!call)
@@ -815,8 +971,8 @@ const MicCaptureErrorNotification = ({ children, text, placement, }) => {
815
971
  });
816
972
  }, [call]);
817
973
  const message = text ??
818
- t('Your microphone is not capturing audio. Please check your setup.');
819
- return (jsxRuntime.jsx(Notification, { message: message, isVisible: isVisible, placement: placement, close: () => setIsVisible(false), children: children }));
974
+ t('notification.micCaptureError.text', 'Your microphone is not capturing audio. Please check your setup.');
975
+ return (jsxRuntime.jsx(Notification, { message: message, state: "error", isVisible: isVisible, placement: placement, close: () => setIsVisible(false), children: children }));
820
976
  };
821
977
 
822
978
  const LoadingIndicator = ({ className, type = 'spinner', text, tooltip, }) => {
@@ -824,22 +980,20 @@ const LoadingIndicator = ({ className, type = 'spinner', text, tooltip, }) => {
824
980
  };
825
981
 
826
982
  const Tooltip = ({ children, referenceElement, tooltipClassName, tooltipPlacement = 'top', visible = false, }) => {
827
- const arrowRef = react.useRef(null);
828
- const { refs, x, y, strategy, context } = useFloatingUIPreset({
983
+ const { refs, x, y, strategy } = useFloatingUIPreset({
829
984
  placement: tooltipPlacement,
830
985
  strategy: 'absolute',
831
- middleware: [react$1.arrow({ element: arrowRef })],
832
986
  });
833
987
  react.useEffect(() => {
834
988
  refs.setReference(referenceElement);
835
989
  }, [referenceElement, refs]);
836
990
  if (!visible)
837
991
  return null;
838
- return (jsxRuntime.jsxs("div", { className: clsx('str-video__tooltip', tooltipClassName), ref: refs.setFloating, style: {
992
+ return (jsxRuntime.jsx("div", { className: clsx('str-video__tooltip', tooltipClassName), ref: refs.setFloating, style: {
839
993
  position: strategy,
840
994
  top: y ?? 0,
841
995
  left: x ?? 0,
842
- }, children: [jsxRuntime.jsx(react$1.FloatingArrow, { ref: arrowRef, context: context, fill: "var(--str-video__tooltip--background-color)" }), children] }));
996
+ }, children: children }));
843
997
  };
844
998
 
845
999
  const useEnterLeaveHandlers = ({ onMouseEnter, onMouseLeave, } = {}) => {
@@ -891,18 +1045,18 @@ function isNotAllowedError(error) {
891
1045
  }
892
1046
 
893
1047
  const RecordEndConfirmation = (props) => {
894
- const { t } = videoReactBindings.useI18n();
1048
+ const { t } = useI18n();
895
1049
  const { toggleCallRecording, isAwaitingResponse } = videoReactBindings.useToggleCallRecording();
896
1050
  const { close } = useMenuContext();
897
1051
  const handleClick = createCallControlHandler(props, toggleCallRecording);
898
- return (jsxRuntime.jsxs("div", { className: "str-video__end-recording__confirmation", children: [jsxRuntime.jsxs("div", { className: "str-video__end-recording__header", children: [jsxRuntime.jsx(Icon, { icon: "recording-on" }), jsxRuntime.jsx("h2", { className: "str-video__end-recording__heading", children: t('End recording') })] }), jsxRuntime.jsx("p", { className: "str-video__end-recording__description", children: t('Are you sure you want end the recording?') }), jsxRuntime.jsxs("div", { className: "str-video__end-recording__actions", children: [jsxRuntime.jsx(CompositeButton, { variant: "secondary", onClick: close, children: t('Cancel') }), jsxRuntime.jsx(CompositeButton, { variant: "primary", onClick: isAwaitingResponse ? undefined : handleClick, children: isAwaitingResponse ? jsxRuntime.jsx(LoadingIndicator, {}) : t('End recording') })] })] }));
1052
+ return (jsxRuntime.jsxs("div", { className: "str-video__end-recording__confirmation", children: [jsxRuntime.jsxs("div", { className: "str-video__end-recording__header", children: [jsxRuntime.jsx(Icon, { icon: "recording-on" }), jsxRuntime.jsxs("div", { className: "str-video__end-recording__text", children: [jsxRuntime.jsx("h2", { className: "str-video__end-recording__heading", children: t('callControls.recordCallButton.endRecording.title', 'End recording') }), jsxRuntime.jsx("p", { className: "str-video__end-recording__description", children: t('callControls.recordCallButton.confirmEndRecording.description', 'Are you sure you want end the recording?') })] })] }), jsxRuntime.jsxs("div", { className: "str-video__end-recording__actions", children: [jsxRuntime.jsx(Button, { variant: "destructive", onClick: isAwaitingResponse ? undefined : handleClick, children: isAwaitingResponse ? (jsxRuntime.jsx(LoadingIndicator, {})) : (t('callControls.recordCallButton.endRecording.title', 'End recording')) }), jsxRuntime.jsx(Button, { variant: "secondary", appearance: "outline", onClick: close, children: t('common.cancel.label', 'Cancel') })] })] }));
899
1053
  };
900
1054
  const ToggleEndRecordingMenuButton = react.forwardRef(function ToggleEndRecordingMenuButtonRender(props, ref) {
901
- return (jsxRuntime.jsx(CompositeButton, { ref: ref, active: true, variant: "secondary", "data-testid": "recording-stop-button", children: jsxRuntime.jsx(Icon, { icon: "recording-off" }) }));
1055
+ return (jsxRuntime.jsx(CompositeButton, { ref: ref, active: true, variant: "destructive", "data-testid": "recording-stop-button", children: jsxRuntime.jsx(Icon, { icon: "recording-off" }) }));
902
1056
  });
903
1057
  const RecordCallConfirmationButton = (props) => {
904
1058
  const { caption } = props;
905
- const { t } = videoReactBindings.useI18n();
1059
+ const { t } = useI18n();
906
1060
  const { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress } = videoReactBindings.useToggleCallRecording();
907
1061
  const handleClick = createCallControlHandler(props, toggleCallRecording);
908
1062
  if (isCallRecordingInProgress) {
@@ -912,12 +1066,13 @@ const RecordCallConfirmationButton = (props) => {
912
1066
  ], children: jsxRuntime.jsx(MenuToggle, { ToggleButton: ToggleEndRecordingMenuButton, visualType: MenuVisualType.PORTAL, children: jsxRuntime.jsx(RecordEndConfirmation, { onError: props.onError }) }) }));
913
1067
  }
914
1068
  const title = isAwaitingResponse
915
- ? t('Waiting for recording to start...')
916
- : (caption ?? t('Record call'));
1069
+ ? t('callControls.recordCallButton.waitingRecordingStart.title', 'Waiting for recording to start...')
1070
+ : (caption ??
1071
+ t('callControls.recordCallButton.recordCall.title', 'Record call'));
917
1072
  return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [
918
1073
  videoClient.OwnCapability.START_RECORD_CALL,
919
1074
  videoClient.OwnCapability.STOP_RECORD_CALL,
920
- ], children: jsxRuntime.jsx(WithTooltip, { title: title, children: jsxRuntime.jsx(CompositeButton, { active: isCallRecordingInProgress, caption: caption, variant: "secondary", "data-testid": "recording-start-button", onClick: isAwaitingResponse ? undefined : handleClick, children: isAwaitingResponse ? (jsxRuntime.jsx(LoadingIndicator, {})) : (jsxRuntime.jsx(Icon, { icon: "recording-off" })) }) }) }));
1075
+ ], children: jsxRuntime.jsx(WithTooltip, { title: title, children: jsxRuntime.jsx(CompositeButton, { active: isCallRecordingInProgress, caption: caption, variant: isCallRecordingInProgress ? 'destructive' : 'secondary', "data-testid": "recording-start-button", onClick: isAwaitingResponse ? undefined : handleClick, children: isAwaitingResponse ? (jsxRuntime.jsx(LoadingIndicator, {})) : (jsxRuntime.jsx(Icon, { icon: "recording-off" })) }) }) }));
921
1076
  };
922
1077
 
923
1078
  const defaultEmojiReactionMap = {
@@ -977,8 +1132,8 @@ const ReactionsButton = ({ reactions = defaultReactions, }) => {
977
1132
  return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx(MenuToggle, { placement: "top", ToggleButton: ToggleReactionsMenuButton, visualType: MenuVisualType.MENU, children: jsxRuntime.jsx(DefaultReactionsMenu, { reactions: reactions }) }) }));
978
1133
  };
979
1134
  const ToggleReactionsMenuButton = react.forwardRef(function ToggleReactionsMenuButtonRender({ menuShown }, ref) {
980
- const { t } = videoReactBindings.useI18n();
981
- return (jsxRuntime.jsx(WithTooltip, { title: t('Reactions'), tooltipDisabled: menuShown, children: jsxRuntime.jsx(CompositeButton, { ref: ref, active: menuShown, variant: "primary", children: jsxRuntime.jsx(Icon, { icon: "reactions" }) }) }));
1135
+ const { t } = useI18n();
1136
+ return (jsxRuntime.jsx(WithTooltip, { title: t('callControls.reactionsButton.reactions.title', 'Reactions'), tooltipDisabled: menuShown, children: jsxRuntime.jsx(CompositeButton, { ref: ref, active: menuShown, children: jsxRuntime.jsx(Icon, { icon: "reactions" }) }) }));
982
1137
  });
983
1138
  const DefaultReactionsMenu = ({ reactions, layout = 'horizontal', }) => {
984
1139
  const call = videoReactBindings.useCall();
@@ -993,7 +1148,7 @@ const DefaultReactionsMenu = ({ reactions, layout = 'horizontal', }) => {
993
1148
  };
994
1149
 
995
1150
  const ScreenShareButton = (props) => {
996
- const { t } = videoReactBindings.useI18n();
1151
+ const { t } = useI18n();
997
1152
  const { caption, optimisticUpdates } = props;
998
1153
  const { useHasOngoingScreenShare, useScreenShareState, useCallSettings } = videoReactBindings.useCallStateHooks();
999
1154
  const isSomeoneScreenSharing = useHasOngoingScreenShare();
@@ -1015,7 +1170,10 @@ const ScreenShareButton = (props) => {
1015
1170
  await screenShare.toggle();
1016
1171
  }
1017
1172
  });
1018
- return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SCREENSHARE, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now share your screen.'), messageAwaitingApproval: t('Awaiting for an approval to share screen.'), messageRevoked: t('You can no longer share your screen.'), children: jsxRuntime.jsx(WithTooltip, { title: caption ?? t('Share screen'), children: jsxRuntime.jsx(CompositeButton, { active: isSomeoneScreenSharing || amIScreenSharing, caption: caption, variant: "primary", "data-testid": isSomeoneScreenSharing
1173
+ return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SCREENSHARE, isAwaitingApproval: isAwaitingPermission, messageApproved: t('callControls.screenShareButton.permissionGranted.text', 'You can now share your screen.'), messageAwaitingApproval: t('callControls.screenShareButton.awaitingApproval.text', 'Awaiting for an approval to share screen.'), messageRevoked: t('callControls.screenShareButton.permissionRevoked.text', 'You can no longer share your screen.'), children: jsxRuntime.jsx(WithTooltip, { title: caption ??
1174
+ t('callControls.screenShareButton.shareScreen.title', 'Share screen'), children: jsxRuntime.jsx(CompositeButton, { active: isSomeoneScreenSharing || amIScreenSharing, caption: caption, variant: isSomeoneScreenSharing || amIScreenSharing
1175
+ ? 'primary'
1176
+ : 'secondary', "data-testid": isSomeoneScreenSharing
1019
1177
  ? 'screen-share-stop-button'
1020
1178
  : 'screen-share-start-button', disabled: disableScreenShareButton, onClick: handleClick, children: jsxRuntime.jsx(Icon, { icon: isSomeoneScreenSharing ? 'screen-share-on' : 'screen-share-off' }) }) }) }) }));
1021
1179
  };
@@ -1071,7 +1229,7 @@ const DeviceLevelIndicator = ({ deviceId }) => {
1071
1229
  const DeviceAudioPreviewItem = ({ device, onSelect, }) => {
1072
1230
  if (device.deviceId === 'default')
1073
1231
  return null;
1074
- return (jsxRuntime.jsxs("label", { className: `str-video__device-settings__option${device.isSelected ? ' str-video__device-settings__option--selected' : ''}`, htmlFor: `audioinput--${device.deviceId}`, children: [jsxRuntime.jsx("input", { type: "radio", name: "audioinput", value: device.deviceId, id: `audioinput--${device.deviceId}`, checked: device.isSelected, onChange: (e) => onSelect(e.target.value) }), device.label, jsxRuntime.jsx(DeviceLevelIndicator, { deviceId: device.deviceId })] }));
1232
+ return (jsxRuntime.jsxs("label", { className: `str-video__device-settings__option${device.isSelected ? ' str-video__device-settings__option--selected' : ''}`, htmlFor: `audioinput--${device.deviceId}`, children: [jsxRuntime.jsx("input", { type: "radio", name: "audioinput", value: device.deviceId, id: `audioinput--${device.deviceId}`, checked: device.isSelected, onChange: (e) => onSelect(e.target.value) }), jsxRuntime.jsx("span", { className: "str-video__device-settings__option__label", children: device.label }), jsxRuntime.jsx(DeviceLevelIndicator, { deviceId: device.deviceId })] }));
1075
1233
  };
1076
1234
 
1077
1235
  const SelectContext = react.createContext({});
@@ -1081,12 +1239,12 @@ const Select = (props) => {
1081
1239
  const [activeIndex, setActiveIndex] = react.useState(null);
1082
1240
  const [selectedIndex, setSelectedIndex] = react.useState(defaultSelectedIndex);
1083
1241
  const [selectedLabel, setSelectedLabel] = react.useState(defaultSelectedLabel);
1084
- const { refs, context } = react$1.useFloating({
1242
+ const { refs, context } = react$2.useFloating({
1085
1243
  placement: 'bottom-start',
1086
1244
  open: isOpen,
1087
1245
  onOpenChange: setIsOpen,
1088
- whileElementsMounted: react$1.autoUpdate,
1089
- middleware: [react$1.flip()],
1246
+ whileElementsMounted: react$2.autoUpdate,
1247
+ middleware: [react$2.flip()],
1090
1248
  });
1091
1249
  const elementsRef = react.useRef([]);
1092
1250
  const labelsRef = react.useRef([]);
@@ -1106,34 +1264,34 @@ const Select = (props) => {
1106
1264
  handleSelect(index);
1107
1265
  }
1108
1266
  };
1109
- const listNav = react$1.useListNavigation(context, {
1267
+ const listNav = react$2.useListNavigation(context, {
1110
1268
  listRef: elementsRef,
1111
1269
  activeIndex,
1112
1270
  selectedIndex,
1113
1271
  onNavigate: setActiveIndex,
1114
1272
  });
1115
- const typeahead = react$1.useTypeahead(context, {
1273
+ const typeahead = react$2.useTypeahead(context, {
1116
1274
  listRef: labelsRef,
1117
1275
  activeIndex,
1118
1276
  selectedIndex,
1119
1277
  onMatch: handleTypeaheadMatch,
1120
1278
  });
1121
- const click = react$1.useClick(context);
1122
- const dismiss = react$1.useDismiss(context);
1123
- const role = react$1.useRole(context, { role: 'listbox' });
1124
- const { getReferenceProps, getFloatingProps, getItemProps } = react$1.useInteractions([listNav, typeahead, click, dismiss, role]);
1279
+ const click = react$2.useClick(context);
1280
+ const dismiss = react$2.useDismiss(context);
1281
+ const role = react$2.useRole(context, { role: 'listbox' });
1282
+ const { getReferenceProps, getFloatingProps, getItemProps } = react$2.useInteractions([listNav, typeahead, click, dismiss, role]);
1125
1283
  const selectContext = react.useMemo(() => ({
1126
1284
  activeIndex,
1127
1285
  selectedIndex,
1128
1286
  getItemProps,
1129
1287
  handleSelect,
1130
1288
  }), [activeIndex, selectedIndex, getItemProps, handleSelect]);
1131
- return (jsxRuntime.jsxs("div", { className: "str-video__dropdown", children: [jsxRuntime.jsxs("div", { className: "str-video__dropdown-selected", ref: refs.setReference, tabIndex: 0, ...getReferenceProps(), children: [jsxRuntime.jsxs("label", { className: "str-video__dropdown-selected__label", children: [icon && (jsxRuntime.jsx(Icon, { className: "str-video__dropdown-selected__icon", icon: icon })), selectedLabel] }), jsxRuntime.jsx(Icon, { className: "str-video__dropdown-selected__chevron", icon: isOpen ? 'chevron-up' : 'chevron-down' })] }), jsxRuntime.jsx(SelectContext.Provider, { value: selectContext, children: isOpen && (jsxRuntime.jsx(react$1.FloatingFocusManager, { context: context, modal: false, children: jsxRuntime.jsx("div", { className: "str-video__dropdown-list", ref: refs.setFloating, ...getFloatingProps(), children: jsxRuntime.jsx(react$1.FloatingList, { elementsRef: elementsRef, labelsRef: labelsRef, children: children }) }) })) })] }));
1289
+ return (jsxRuntime.jsxs("div", { className: "str-video__dropdown", children: [jsxRuntime.jsxs("div", { className: "str-video__dropdown-selected", ref: refs.setReference, tabIndex: 0, ...getReferenceProps(), children: [jsxRuntime.jsxs("label", { className: "str-video__dropdown-selected__label", children: [icon && (jsxRuntime.jsx(Icon, { className: "str-video__dropdown-selected__icon", icon: icon })), jsxRuntime.jsx("span", { className: "str-video__dropdown-selected__text", children: selectedLabel })] }), jsxRuntime.jsx(Icon, { className: "str-video__dropdown-selected__chevron", icon: isOpen ? 'chevron-up' : 'chevron-down' })] }), jsxRuntime.jsx(SelectContext.Provider, { value: selectContext, children: isOpen && (jsxRuntime.jsx(react$2.FloatingFocusManager, { context: context, modal: false, children: jsxRuntime.jsx("div", { className: "str-video__dropdown-list", ref: refs.setFloating, ...getFloatingProps(), children: jsxRuntime.jsx(react$2.FloatingList, { elementsRef: elementsRef, labelsRef: labelsRef, children: children }) }) })) })] }));
1132
1290
  };
1133
1291
  const DropDownSelectOption = (props) => {
1134
1292
  const { selected, label, icon } = props;
1135
1293
  const { getItemProps, handleSelect } = react.useContext(SelectContext);
1136
- const { ref, index } = react$1.useListItem();
1294
+ const { ref, index } = react$2.useListItem();
1137
1295
  return (jsxRuntime.jsxs("div", { className: clsx('str-video__dropdown-option', {
1138
1296
  'str-video__dropdown-option--selected': selected,
1139
1297
  }), ref: ref, ...getItemProps({
@@ -1149,7 +1307,7 @@ const DeviceSelectorOption = ({ disabled, id, label, onChange, name, selected, d
1149
1307
  return (jsxRuntime.jsxs("label", { className: clsx('str-video__device-settings__option', {
1150
1308
  'str-video__device-settings__option--selected': selected,
1151
1309
  'str-video__device-settings__option--disabled': disabled,
1152
- }), htmlFor: id, children: [jsxRuntime.jsx("input", { type: "radio", name: name, onChange: onChange, value: value, id: id, checked: selected, defaultChecked: defaultChecked, disabled: disabled }), label] }));
1310
+ }), htmlFor: id, children: [jsxRuntime.jsx("input", { type: "radio", name: name, onChange: onChange, value: value, id: id, checked: selected, defaultChecked: defaultChecked, disabled: disabled }), jsxRuntime.jsx("span", { className: "str-video__device-settings__option__label", children: label })] }));
1153
1311
  };
1154
1312
  const DeviceSelectorList = (props) => {
1155
1313
  const { devices = [], selectedDeviceId, title, type, onChange, children, } = props;
@@ -1209,8 +1367,8 @@ const SpeakerTest = (props) => {
1209
1367
  const { selectedDevice } = useSpeakerState();
1210
1368
  const audioElementRef = react.useRef(null);
1211
1369
  const [isPlaying, setIsPlaying] = react.useState(false);
1212
- const { t } = videoReactBindings.useI18n();
1213
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.43.2"}/assets/piano.mp3`, } = props;
1370
+ const { t } = useI18n();
1371
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"2.0.0-beta.1"}/assets/piano.mp3`, } = props;
1214
1372
  // Update audio output device when selection changes
1215
1373
  react.useEffect(() => {
1216
1374
  const audio = audioElementRef.current;
@@ -1245,7 +1403,9 @@ const SpeakerTest = (props) => {
1245
1403
  }
1246
1404
  }, [isPlaying, audioUrl]);
1247
1405
  const handleAudioEnded = react.useCallback(() => setIsPlaying(false), []);
1248
- return (jsxRuntime.jsxs("div", { className: "str-video__speaker-test", children: [jsxRuntime.jsx("audio", { ref: audioElementRef, onEnded: handleAudioEnded, onPause: handleAudioEnded }), jsxRuntime.jsx(CompositeButton, { className: "str-video__speaker-test__button", onClick: handleStartTest, type: "button", children: jsxRuntime.jsxs("div", { className: "str-video__speaker-test__button-content", children: [jsxRuntime.jsx(Icon, { icon: "speaker" }), isPlaying ? t('Stop test') : t('Test speaker')] }) })] }));
1406
+ return (jsxRuntime.jsxs("div", { className: "str-video__speaker-test", children: [jsxRuntime.jsx("audio", { ref: audioElementRef, onEnded: handleAudioEnded, onPause: handleAudioEnded }), jsxRuntime.jsx(CompositeButton, { className: "str-video__speaker-test__button", appearance: "outline", size: "sm", onClick: handleStartTest, type: "button", children: jsxRuntime.jsxs("div", { className: "str-video__speaker-test__button-content", children: [jsxRuntime.jsx(Icon, { icon: "speaker" }), isPlaying
1407
+ ? t('deviceSettings.speakerTest.stopTest.label', 'Stop test')
1408
+ : t('deviceSettings.speakerTest.testSpeaker.label', 'Test speaker')] }) })] }));
1249
1409
  };
1250
1410
 
1251
1411
  const DeviceSelectorAudioInput = ({ title, visualType, volumeIndicatorVisible = true, }) => {
@@ -1253,7 +1413,7 @@ const DeviceSelectorAudioInput = ({ title, visualType, volumeIndicatorVisible =
1253
1413
  const { microphone, selectedDevice, devices } = useMicrophoneState();
1254
1414
  return (jsxRuntime.jsx(DeviceSelector, { devices: devices || [], selectedDeviceId: selectedDevice, type: "audioinput", onChange: async (deviceId) => {
1255
1415
  await microphone.select(deviceId);
1256
- }, title: title, visualType: visualType, icon: "mic", PreviewItem: DeviceAudioPreviewItem, children: volumeIndicatorVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" }), jsxRuntime.jsx(AudioVolumeIndicator, {})] })) }));
1416
+ }, title: title, visualType: visualType, icon: "mic", PreviewItem: DeviceAudioPreviewItem, children: volumeIndicatorVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(AudioVolumeIndicator, {}), jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" })] })) }));
1257
1417
  };
1258
1418
  const DeviceSelectorAudioOutput = ({ title, visualType, speakerTestVisible = true, speakerTestAudioUrl, }) => {
1259
1419
  const { useSpeakerState } = videoReactBindings.useCallStateHooks();
@@ -1262,7 +1422,7 @@ const DeviceSelectorAudioOutput = ({ title, visualType, speakerTestVisible = tru
1262
1422
  return null;
1263
1423
  return (jsxRuntime.jsx(DeviceSelector, { devices: devices, type: "audiooutput", selectedDeviceId: selectedDevice, onChange: (deviceId) => {
1264
1424
  speaker.select(deviceId);
1265
- }, title: title, visualType: visualType, icon: "speaker", children: speakerTestVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" }), jsxRuntime.jsx(SpeakerTest, { audioUrl: speakerTestAudioUrl })] })) }));
1425
+ }, title: title, visualType: visualType, icon: "speaker", children: speakerTestVisible && jsxRuntime.jsx(SpeakerTest, { audioUrl: speakerTestAudioUrl }) }));
1266
1426
  };
1267
1427
 
1268
1428
  const DeviceVideoPreview = ({ deviceId }) => {
@@ -1310,34 +1470,37 @@ const DeviceSelectorVideo = ({ title, visualType, }) => {
1310
1470
  };
1311
1471
 
1312
1472
  react.forwardRef(function ToggleDeviceSettingsMenuButtonRender({ menuShown }, ref) {
1313
- const { t } = videoReactBindings.useI18n();
1314
- return (jsxRuntime.jsx(IconButton, { className: clsx('str-video__device-settings__button', {
1473
+ const { t } = useI18n();
1474
+ return (jsxRuntime.jsx(IconButton, { active: menuShown, size: "sm", variant: "secondary", className: clsx('str-video__device-settings__button', {
1315
1475
  'str-video__device-settings__button--active': menuShown,
1316
- }), title: t('Toggle device menu'), icon: "device-settings", ref: ref }));
1476
+ }), title: t('deviceSettings.toggleDeviceMenu.title', 'Toggle device menu'), icon: "device-settings", ref: ref }));
1317
1477
  });
1318
1478
 
1319
1479
  const ToggleAudioPreviewButton = (props) => {
1320
1480
  const { caption, Menu = DeviceSelectorAudioInput, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1321
- const { t } = videoReactBindings.useI18n();
1481
+ const { t } = useI18n();
1322
1482
  const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1323
1483
  const { microphone, hasBrowserPermission, isPromptingPermission, optionsAwareIsMute, isTogglePending, } = useMicrophoneState({ optimisticUpdates });
1324
1484
  const localParticipant = useLocalParticipant();
1325
1485
  const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.AUDIO);
1326
1486
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1327
1487
  const handleClick = createCallControlHandler(props, () => microphone.toggle());
1328
- return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1329
- ? t('Check your browser audio permissions')
1330
- : isSystemMuted
1331
- ? t('Microphone is paused by your system')
1332
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1488
+ return (jsxRuntime.jsx(WithTooltip, { title: isPromptingPermission
1489
+ ? t('common.waitingForPermission.title', 'Waiting for permission')
1490
+ : !hasBrowserPermission
1491
+ ? t('callControls.toggleAudioButton.checkBrowserAudioPermissions.title', 'Check your browser audio permissions')
1492
+ : isSystemMuted
1493
+ ? t('callControls.toggleAudioButton.microphonePausedBySystem.title', 'Microphone is paused by your system')
1494
+ : (caption ??
1495
+ t('callControls.toggleAudioButton.mic.title', 'Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: optionsAwareIsMute ? 'destructive' : 'secondary', disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1333
1496
  ? 'preview-audio-unmute-button'
1334
1497
  : 'preview-audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1335
1498
  setTooltipDisabled(shown);
1336
1499
  onMenuToggle?.(shown);
1337
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }));
1500
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), isPromptingPermission ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "question-mark-fill" })) : !hasBrowserPermission || isSystemMuted ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "exclamation-mark-fill" })) : null] }) }));
1338
1501
  };
1339
1502
  const ToggleAudioPublishingButton = (props) => {
1340
- const { t } = videoReactBindings.useI18n();
1503
+ const { t } = useI18n();
1341
1504
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1342
1505
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.RequestPermissionRequestPermissionsEnum.SEND_AUDIO);
1343
1506
  const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
@@ -1353,43 +1516,49 @@ const ToggleAudioPublishingButton = (props) => {
1353
1516
  await microphone.toggle();
1354
1517
  }
1355
1518
  });
1356
- return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_AUDIO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now speak.'), messageAwaitingApproval: t('Awaiting for an approval to speak.'), messageRevoked: t('You can no longer speak.'), children: jsxRuntime.jsx(WithTooltip, { title: !hasPermission
1357
- ? t('You have no permission to share your audio')
1358
- : !hasBrowserPermission
1359
- ? t('Check your browser mic permissions')
1360
- : isSystemMuted
1361
- ? t('Microphone is paused by your system')
1362
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1519
+ return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_AUDIO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('callControls.toggleAudioButton.permissionGranted.text', 'You can now speak.'), messageAwaitingApproval: t('callControls.toggleAudioButton.awaitingApproval.text', 'Awaiting for an approval to speak.'), messageRevoked: t('callControls.toggleAudioButton.permissionRevoked.text', 'You can no longer speak.'), children: jsxRuntime.jsx(WithTooltip, { title: isPromptingPermission
1520
+ ? t('common.waitingForPermission.title', 'Waiting for permission')
1521
+ : !hasPermission
1522
+ ? t('callControls.toggleAudioButton.noPermissionToShareAudio.title', 'You have no permission to share your audio')
1523
+ : !hasBrowserPermission
1524
+ ? t('callControls.toggleAudioButton.checkBrowserMicPermissions.title', 'Check your browser mic permissions')
1525
+ : isSystemMuted
1526
+ ? t('callControls.toggleAudioButton.microphonePausedBySystem.title', 'Microphone is paused by your system')
1527
+ : (caption ??
1528
+ t('callControls.toggleAudioButton.mic.title', 'Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: optionsAwareIsMute ? 'destructive' : 'secondary', disabled: !hasBrowserPermission ||
1363
1529
  !hasPermission ||
1364
1530
  // disable button while the toggle action is pending when not using optimistic updates
1365
1531
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'audio-unmute-button' : 'audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1366
1532
  setTooltipDisabled(shown);
1367
1533
  onMenuToggle?.(shown);
1368
- }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && hasPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }) }) }));
1534
+ }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), isPromptingPermission ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "question-mark-fill" })) : !hasBrowserPermission || !hasPermission || isSystemMuted ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "exclamation-mark-fill" })) : null] }) }) }) }));
1369
1535
  };
1370
1536
 
1371
1537
  const ToggleVideoPreviewButton = (props) => {
1372
1538
  const { caption, Menu = DeviceSelectorVideo, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1373
- const { t } = videoReactBindings.useI18n();
1539
+ const { t } = useI18n();
1374
1540
  const { useCameraState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1375
1541
  const { camera, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useCameraState({ optimisticUpdates });
1376
1542
  const localParticipant = useLocalParticipant();
1377
1543
  const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.VIDEO);
1378
1544
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1379
1545
  const handleClick = createCallControlHandler(props, () => camera.toggle());
1380
- return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1381
- ? t('Check your browser video permissions')
1382
- : isSystemMuted
1383
- ? t('Camera is paused by your system')
1384
- : (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
1546
+ return (jsxRuntime.jsx(WithTooltip, { title: isPromptingPermission
1547
+ ? t('common.waitingForPermission.title', 'Waiting for permission')
1548
+ : !hasBrowserPermission
1549
+ ? t('callControls.toggleVideoButton.checkBrowserVideoPermissions.title', 'Check your browser video permissions')
1550
+ : isSystemMuted
1551
+ ? t('callControls.toggleVideoButton.cameraPausedBySystem.title', 'Camera is paused by your system')
1552
+ : (caption ??
1553
+ t('callControls.toggleVideoButton.video.title', 'Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: optionsAwareIsMute ? 'destructive' : 'secondary', "data-testid": optionsAwareIsMute
1385
1554
  ? 'preview-video-unmute-button'
1386
1555
  : 'preview-video-mute-button', onClick: handleClick, disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1387
1556
  setTooltipDisabled(shown);
1388
1557
  onMenuToggle?.(shown);
1389
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }));
1558
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), isPromptingPermission ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "question-mark-fill" })) : !hasBrowserPermission || isSystemMuted ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "exclamation-mark-fill" })) : null] }) }));
1390
1559
  };
1391
1560
  const ToggleVideoPublishingButton = (props) => {
1392
- const { t } = videoReactBindings.useI18n();
1561
+ const { t } = useI18n();
1393
1562
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorVideo, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1394
1563
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.RequestPermissionRequestPermissionsEnum.SEND_VIDEO);
1395
1564
  const { useCameraState, useCallSettings, useLocalParticipant } = videoReactBindings.useCallStateHooks();
@@ -1407,36 +1576,37 @@ const ToggleVideoPublishingButton = (props) => {
1407
1576
  await camera.toggle();
1408
1577
  }
1409
1578
  });
1410
- return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_VIDEO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now share your video.'), messageAwaitingApproval: t('Awaiting for an approval to share your video.'), messageRevoked: t('You can no longer share your video.'), children: jsxRuntime.jsx(WithTooltip, { title: !hasPermission
1411
- ? t('You have no permission to share your video')
1412
- : !hasBrowserPermission
1413
- ? t('Check your browser video permissions')
1414
- : !isPublishingVideoAllowed
1415
- ? t('Video publishing is disabled by the system')
1416
- : isSystemMuted
1417
- ? t('Camera is paused by your system')
1418
- : caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1579
+ return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_VIDEO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('callControls.toggleVideoButton.permissionGranted.text', 'You can now share your video.'), messageAwaitingApproval: t('callControls.toggleVideoButton.awaitingApproval.text', 'Awaiting for an approval to share your video.'), messageRevoked: t('callControls.toggleVideoButton.permissionRevoked.text', 'You can no longer share your video.'), children: jsxRuntime.jsx(WithTooltip, { title: isPromptingPermission
1580
+ ? t('common.waitingForPermission.title', 'Waiting for permission')
1581
+ : !hasPermission
1582
+ ? t('callControls.toggleVideoButton.noPermissionToShareVideo.title', 'You have no permission to share your video')
1583
+ : !hasBrowserPermission
1584
+ ? t('callControls.toggleVideoButton.checkBrowserVideoPermissions.title', 'Check your browser video permissions')
1585
+ : !isPublishingVideoAllowed
1586
+ ? t('callControls.toggleVideoButton.videoPublishingDisabled.title', 'Video publishing is disabled by the system')
1587
+ : isSystemMuted
1588
+ ? t('callControls.toggleVideoButton.cameraPausedBySystem.title', 'Camera is paused by your system')
1589
+ : caption ||
1590
+ t('callControls.toggleVideoButton.video.title', 'Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: optionsAwareIsMute ? 'destructive' : 'secondary', disabled: !hasBrowserPermission ||
1419
1591
  !hasPermission ||
1420
1592
  !isPublishingVideoAllowed ||
1421
1593
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'video-unmute-button' : 'video-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1422
1594
  setTooltipDisabled(shown);
1423
1595
  onMenuToggle?.(shown);
1424
- }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
1596
+ }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'camera-off' : 'camera' }), isPromptingPermission ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "question-mark-fill" })) : !hasBrowserPermission ||
1425
1597
  !hasPermission ||
1426
- !isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted &&
1427
- hasBrowserPermission &&
1428
- hasPermission &&
1429
- isPublishingVideoAllowed && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }) }) }));
1598
+ !isPublishingVideoAllowed ||
1599
+ isSystemMuted ? (jsxRuntime.jsx(Badge, { variant: "error", icon: "exclamation-mark-fill" })) : null] }) }) }) }));
1430
1600
  };
1431
1601
 
1432
1602
  const EndCallMenu = (props) => {
1433
1603
  const { onLeave, onEnd } = props;
1434
- const { t } = videoReactBindings.useI18n();
1435
- return (jsxRuntime.jsxs("div", { className: "str-video__end-call__confirmation", children: [jsxRuntime.jsxs("button", { className: "str-video__button str-video__end-call__leave", type: "button", "data-testid": "leave-call-button", onClick: onLeave, children: [jsxRuntime.jsx(Icon, { className: "str-video__button__icon str-video__end-call__leave-icon", icon: "logout" }), t('Leave call')] }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.END_CALL], children: jsxRuntime.jsxs("button", { className: "str-video__button str-video__end-call__end", type: "button", "data-testid": "end-call-for-all-button", onClick: onEnd, children: [jsxRuntime.jsx(Icon, { className: "str-video__button__icon str-video__end-call__end-icon", icon: "call-end" }), t('End call for all')] }) })] }));
1604
+ const { t } = useI18n();
1605
+ return (jsxRuntime.jsxs("div", { className: "str-video__end-call__confirmation", children: [jsxRuntime.jsxs(Button, { variant: "secondary", appearance: "outline", className: "str-video__end-call__leave", "data-testid": "leave-call-button", onClick: onLeave, children: [jsxRuntime.jsx(Icon, { icon: "logout" }), t('callControls.cancelCallButton.leaveCall.title', 'Leave call')] }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.END_CALL], children: jsxRuntime.jsxs(Button, { variant: "destructive", className: "str-video__end-call__end", "data-testid": "end-call-for-all-button", onClick: onEnd, children: [jsxRuntime.jsx(Icon, { icon: "call-end" }), t('callControls.cancelCallButton.endCallForAll.label', 'End call for all')] }) })] }));
1436
1606
  };
1437
1607
  const CancelCallToggleMenuButton = react.forwardRef(function CancelCallToggleMenuButtonRender({ menuShown }, ref) {
1438
- const { t } = videoReactBindings.useI18n();
1439
- return (jsxRuntime.jsx(WithTooltip, { title: t('Leave call'), tooltipDisabled: menuShown, children: jsxRuntime.jsx(IconButton, { icon: menuShown ? 'close' : 'call-end', variant: menuShown ? 'active' : 'danger', "data-testid": "leave-call-button", ref: ref }) }));
1608
+ const { t } = useI18n();
1609
+ return (jsxRuntime.jsx(WithTooltip, { title: t('callControls.cancelCallButton.leaveCall.title', 'Leave call'), tooltipDisabled: menuShown, children: jsxRuntime.jsx(IconButton, { icon: menuShown ? 'close' : 'call-end', variant: "destructive", "data-testid": "leave-call-button", ref: ref }) }));
1440
1610
  });
1441
1611
  const CancelCallConfirmButton = ({ onClick, onLeave, }) => {
1442
1612
  const call = videoReactBindings.useCall();
@@ -1473,20 +1643,6 @@ const CancelCallConfirmButton = ({ onClick, onLeave, }) => {
1473
1643
  return (jsxRuntime.jsx(MenuToggle, { placement: "top-start", ToggleButton: CancelCallToggleMenuButton, children: jsxRuntime.jsx(EndCallMenu, { onEnd: handleEndCall, onLeave: handleLeave }) }));
1474
1644
  };
1475
1645
 
1476
- react.lazy(() => Promise.resolve().then(function () { return require('./embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js'); }));
1477
- var Status;
1478
- (function (Status) {
1479
- Status["GOOD"] = "Good";
1480
- Status["OK"] = "Ok";
1481
- Status["BAD"] = "Bad";
1482
- })(Status || (Status = {}));
1483
-
1484
- react.forwardRef(function ToggleMenuButtonRender(props, ref) {
1485
- const { t } = videoReactBindings.useI18n();
1486
- const { caption, menuShown } = props;
1487
- return (jsxRuntime.jsx(CompositeButton, { ref: ref, active: menuShown, caption: caption, title: caption || t('Statistics'), "data-testid": "stats-button", children: jsxRuntime.jsx(Icon, { icon: "stats" }) }));
1488
- });
1489
-
1490
1646
  const BlockedUserListing = ({ data }) => {
1491
1647
  if (!data.length)
1492
1648
  return null;
@@ -1498,34 +1654,43 @@ const BlockedUserListingItem = ({ userId }) => {
1498
1654
  if (userId)
1499
1655
  call?.unblockUser(userId);
1500
1656
  };
1501
- return (jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item", children: [jsxRuntime.jsx("div", { className: "str-video__participant-listing-item__display-name", children: userId }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.BLOCK_USERS], children: jsxRuntime.jsx(TextButton, { onClick: unblockUserClickHandler, children: "Unblock" }) })] }));
1657
+ return (jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item", children: [jsxRuntime.jsx("div", { className: "str-video__participant-listing-item__display-name", children: userId }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.BLOCK_USERS], children: jsxRuntime.jsx(Button, { appearance: "ghost", size: "xs", variant: "secondary", onClick: unblockUserClickHandler, children: "Unblock" }) })] }));
1502
1658
  };
1503
1659
 
1504
1660
  const CallParticipantListHeader = ({ onClose, }) => {
1505
1661
  const { useParticipants, useAnonymousParticipantCount } = videoReactBindings.useCallStateHooks();
1506
1662
  const participants = useParticipants();
1507
1663
  const anonymousParticipantCount = useAnonymousParticipantCount();
1508
- const { t } = videoReactBindings.useI18n();
1509
- return (jsxRuntime.jsxs("div", { className: "str-video__participant-list-header", children: [jsxRuntime.jsxs("div", { className: "str-video__participant-list-header__title", children: [t('Participants'), ' ', jsxRuntime.jsxs("span", { className: "str-video__participant-list-header__title-count", children: ["[", participants.length, "]"] }), anonymousParticipantCount > 0 && (jsxRuntime.jsx("span", { className: "str-video__participant-list-header__title-anonymous", children: t('Anonymous', { count: anonymousParticipantCount }) }))] }), jsxRuntime.jsx(IconButton, { onClick: onClose, className: "str-video__participant-list-header__close-button", icon: "close" })] }));
1664
+ const { t } = useI18n();
1665
+ return (jsxRuntime.jsxs("div", { className: "str-video__participant-list-header", children: [jsxRuntime.jsxs("div", { className: "str-video__participant-list-header__title", children: [t('common.participants.label', 'Participants'), ' ', jsxRuntime.jsxs("span", { className: "str-video__participant-list-header__title-count", children: ["[", participants.length, "]"] }), anonymousParticipantCount > 0 && (jsxRuntime.jsx("span", { className: "str-video__participant-list-header__title-anonymous", children: t('participantList.anonymousCount.text', ', and ({{ anonymousCount }}) anonymous', { anonymousCount: anonymousParticipantCount }) }))] }), jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", onClick: onClose, className: "str-video__participant-list-header__close-button", icon: "close" })] }));
1510
1666
  };
1511
1667
 
1512
1668
  const CallParticipantListingItem = react.memo(function CallParticipantListingItemRender({ participant, DisplayName = DefaultDisplayName, }) {
1513
1669
  const isAudioOn = videoClient.hasAudio(participant);
1514
1670
  const isVideoOn = videoClient.hasVideo(participant);
1515
1671
  const isPinnedOn = videoClient.isPinned(participant);
1516
- const { t } = videoReactBindings.useI18n();
1517
- return (jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item", children: [jsxRuntime.jsx(Avatar, { name: participant.name, imageSrc: participant.image }), jsxRuntime.jsx(DisplayName, { participant: participant }), jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item__media-indicator-group", children: [jsxRuntime.jsx(MediaIndicator, { title: isAudioOn ? t('Microphone on') : t('Microphone off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isAudioOn ? 'mic' : 'mic-off'}`) }), jsxRuntime.jsx(MediaIndicator, { title: isVideoOn ? t('Camera on') : t('Camera off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isVideoOn ? 'camera' : 'camera-off'}`) }), isPinnedOn && (jsxRuntime.jsx(MediaIndicator, { title: t('Pinned'), className: clsx('str-video__participant-listing-item__icon', 'str-video__participant-listing-item__icon-pinned') })), jsxRuntime.jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$2, children: jsxRuntime.jsx(ParticipantViewContext.Provider, { value: { participant, trackType: 'none' }, children: jsxRuntime.jsx(ParticipantActionsContextMenu, {}) }) })] })] }));
1672
+ const { t } = useI18n();
1673
+ return (jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item", children: [jsxRuntime.jsx(Avatar, { name: participant.name, imageSrc: participant.image }), jsxRuntime.jsx(DisplayName, { participant: participant }), jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item__media-indicator-group", children: [jsxRuntime.jsx(MediaIndicator, { title: isAudioOn
1674
+ ? t('participantList.microphoneOn.title', 'Microphone on')
1675
+ : t('participantList.microphoneOff.title', 'Microphone off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isAudioOn ? 'mic' : 'mic-off'}`) }), jsxRuntime.jsx(MediaIndicator, { title: isVideoOn
1676
+ ? t('participantList.cameraOn.title', 'Camera on')
1677
+ : t('participantList.cameraOff.title', 'Camera off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isVideoOn ? 'camera' : 'camera-off'}`) }), isPinnedOn && (jsxRuntime.jsx(MediaIndicator, { title: t('participantList.pinned.title', 'Pinned'), className: clsx('str-video__participant-listing-item__icon', 'str-video__participant-listing-item__icon-pinned') })), jsxRuntime.jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$2, children: jsxRuntime.jsx(ParticipantViewContext.Provider, { value: { participant, trackType: 'none' }, children: jsxRuntime.jsx(ParticipantActionsContextMenu, {}) }) })] })] }));
1518
1678
  });
1519
1679
  CallParticipantListingItem.displayName = 'CallParticipantListingItem';
1520
1680
  const MediaIndicator = (props) => (jsxRuntime.jsx(WithTooltip, { ...props }));
1521
1681
  const DefaultDisplayName = ({ participant }) => {
1522
1682
  const connectedUser = videoReactBindings.useConnectedUser();
1523
- const { t } = videoReactBindings.useI18n();
1524
- const meFlag = participant.userId === connectedUser?.id ? t('Me') : '';
1525
- const nameOrId = participant.name || participant.userId || t('Unknown');
1683
+ const { t } = useI18n();
1684
+ const meFlag = participant.userId === connectedUser?.id
1685
+ ? t('participantList.me.text', 'Me')
1686
+ : '';
1687
+ const nameOrId = participant.name ||
1688
+ participant.userId ||
1689
+ t('participantList.unknown.text', 'Unknown');
1526
1690
  let displayName;
1527
1691
  if (!participant.name) {
1528
- displayName = meFlag || nameOrId || t('Unknown');
1692
+ displayName =
1693
+ meFlag || nameOrId || t('participantList.unknown.text', 'Unknown');
1529
1694
  }
1530
1695
  else if (meFlag) {
1531
1696
  displayName = `${nameOrId} (${meFlag})`;
@@ -1536,14 +1701,14 @@ const DefaultDisplayName = ({ participant }) => {
1536
1701
  return (jsxRuntime.jsx(WithTooltip, { className: "str-video__participant-listing-item__display-name", title: displayName, children: displayName }));
1537
1702
  };
1538
1703
  const ToggleButton$2 = react.forwardRef(function ToggleButtonRender(props, ref) {
1539
- return jsxRuntime.jsx(IconButton, { enabled: props.menuShown, icon: "ellipsis", ref: ref });
1704
+ return (jsxRuntime.jsx(IconButton, { className: "str-video__participant-listing-item__menu-button", active: props.menuShown, size: "sm", variant: "secondary", ref: ref, icon: "ellipsis" }));
1540
1705
  });
1541
1706
 
1542
1707
  const CallParticipantListing = ({ data, }) => (jsxRuntime.jsx("div", { className: "str-video__participant-listing", children: data.map((participant) => (jsxRuntime.jsx(CallParticipantListingItem, { participant: participant }, participant.sessionId))) }));
1543
1708
 
1544
1709
  const EmptyParticipantSearchList = () => {
1545
- const { t } = videoReactBindings.useI18n();
1546
- return (jsxRuntime.jsx("div", { className: "str-video__participant-list--empty", children: t('No participants found') }));
1710
+ const { t } = useI18n();
1711
+ return (jsxRuntime.jsx("div", { className: "str-video__participant-list--empty", children: t('participantList.noParticipantsFound.text', 'No participants found') }));
1547
1712
  };
1548
1713
 
1549
1714
  const SearchInput = ({ exitSearch, isActive, ...rest }) => {
@@ -1563,7 +1728,7 @@ const SearchInput = ({ exitSearch, isActive, ...rest }) => {
1563
1728
  }, [inputElement]);
1564
1729
  return (jsxRuntime.jsxs("div", { className: clsx('str-video__search-input__container', {
1565
1730
  'str-video__search-input__container--active': isActive,
1566
- }), children: [jsxRuntime.jsx("input", { placeholder: "Search", ...rest, ref: setInputElement }), isActive ? (jsxRuntime.jsx("button", { className: "str-video__search-input__clear-btn", onClick: exitSearch, children: jsxRuntime.jsx("span", { className: "str-video__search-input__icon--active" }) })) : (jsxRuntime.jsx("span", { className: "str-video__search-input__icon" }))] }));
1731
+ }), children: [jsxRuntime.jsx("span", { className: "str-video__search-input__icon" }), jsxRuntime.jsx("input", { placeholder: "Search", ...rest, ref: setInputElement }), isActive && (jsxRuntime.jsx("button", { className: "str-video__search-input__clear-btn", onClick: exitSearch, children: jsxRuntime.jsx("span", { className: "str-video__search-input__icon--active" }) }))] }));
1567
1732
  };
1568
1733
 
1569
1734
  function SearchResults({ EmptySearchResultComponent, LoadingIndicator: LoadingIndicator$1 = LoadingIndicator, searchQueryInProgress, searchResults, SearchResultList, }) {
@@ -1622,11 +1787,11 @@ const CallParticipantsList = ({ onClose, activeUsersSearchFn, blockedUsersSearch
1622
1787
  };
1623
1788
  const CallParticipantListContentHeader = ({ userListType, setUserListType, }) => {
1624
1789
  const call = videoReactBindings.useCall();
1625
- const { t } = videoReactBindings.useI18n();
1790
+ const { t } = useI18n();
1626
1791
  const muteAll = react.useCallback(() => {
1627
1792
  call?.muteAllUsers('audio');
1628
1793
  }, [call]);
1629
- return (jsxRuntime.jsxs("div", { className: "str-video__participant-list__content-header", children: [jsxRuntime.jsx("div", { className: "str-video__participant-list__content-header-title", children: userListType === 'active' && (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.MUTE_USERS], hasPermissionsOnly: true, children: jsxRuntime.jsx(TextButton, { onClick: muteAll, children: t('Mute all') }) })) }), jsxRuntime.jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$1, children: jsxRuntime.jsx(GenericMenu, { children: Object.keys(UserListTypes).map((lt) => (jsxRuntime.jsx(GenericMenuButtonItem, { "aria-selected": lt === userListType, onClick: () => setUserListType(lt), children: UserListTypes[lt] }, lt))) }) })] }));
1794
+ return (jsxRuntime.jsxs("div", { className: "str-video__participant-list__content-header", children: [jsxRuntime.jsx("div", { className: "str-video__participant-list__content-header-title", children: userListType === 'active' && (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.MUTE_USERS], hasPermissionsOnly: true, children: jsxRuntime.jsx(Button, { appearance: "ghost", size: "xs", variant: "secondary", onClick: muteAll, children: t('participantList.muteAll.label', 'Mute all') }) })) }), jsxRuntime.jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$1, children: jsxRuntime.jsx(GenericMenu, { children: Object.keys(UserListTypes).map((lt) => (jsxRuntime.jsx(GenericMenuButtonItem, { "aria-current": lt === userListType, onClick: () => setUserListType(lt), children: UserListTypes[lt] }, lt))) }) })] }));
1630
1795
  };
1631
1796
  const ActiveUsersSearchResults = ({ searchQuery, activeUsersSearchFn: activeUsersSearchFnFromProps, debounceSearchInterval, }) => {
1632
1797
  const { useParticipants } = videoReactBindings.useCallStateHooks();
@@ -1667,7 +1832,7 @@ const BlockedUsersSearchResults = ({ blockedUsersSearchFn: blockedUsersSearchFnF
1667
1832
  return (jsxRuntime.jsx(SearchResults, { EmptySearchResultComponent: EmptyParticipantSearchList, LoadingIndicator: LoadingIndicator, searchQueryInProgress: searchQueryInProgress, searchResults: searchQuery ? searchResults : blockedUsers, SearchResultList: BlockedUserListing }));
1668
1833
  };
1669
1834
  const ToggleButton$1 = react.forwardRef(function ToggleButtonRender(props, ref) {
1670
- return jsxRuntime.jsx(IconButton, { enabled: props.menuShown, icon: "filter", ref: ref });
1835
+ return (jsxRuntime.jsx(IconButton, { className: "str-video__participant-list__filter-button", active: props.menuShown, size: "sm", variant: "secondary", ref: ref, icon: "filter" }));
1671
1836
  });
1672
1837
 
1673
1838
  const NoiseCancellationContext = react.createContext(null);
@@ -1748,19 +1913,6 @@ const NoiseCancellationProvider = (props) => {
1748
1913
  return (jsxRuntime.jsx(NoiseCancellationContext.Provider, { value: contextValue, children: children }));
1749
1914
  };
1750
1915
 
1751
- ({
1752
- [videoClient.CallingState.JOINING]: 'Joining',
1753
- [videoClient.CallingState.RINGING]: 'Ringing',
1754
- [videoClient.CallingState.MIGRATING]: 'Migrating',
1755
- [videoClient.CallingState.RECONNECTING]: 'Re-connecting',
1756
- [videoClient.CallingState.RECONNECTING_FAILED]: 'Failed',
1757
- [videoClient.CallingState.OFFLINE]: 'No internet connection',
1758
- [videoClient.CallingState.IDLE]: '',
1759
- [videoClient.CallingState.UNKNOWN]: '',
1760
- [videoClient.CallingState.JOINED]: 'Joined',
1761
- [videoClient.CallingState.LEFT]: 'Left call',
1762
- });
1763
-
1764
1916
  const byNameOrId = (a, b) => {
1765
1917
  if (a.name && b.name && a.name < b.name)
1766
1918
  return -1;
@@ -1810,7 +1962,7 @@ const PermissionRequests = () => {
1810
1962
  // don't render anything if there are no permission requests
1811
1963
  if (permissionRequests.length === 0)
1812
1964
  return null;
1813
- return (jsxRuntime.jsxs("div", { className: "str-video__permission-requests", ref: refs.setReference, children: [jsxRuntime.jsxs("div", { className: "str-video__permission-requests__notification", children: [jsxRuntime.jsxs("span", { className: "str-video__permission-requests__notification__message", children: [permissionRequests.length, " pending permission requests"] }), jsxRuntime.jsx(Button, { type: "button", onClick: () => {
1965
+ return (jsxRuntime.jsxs("div", { className: "str-video__permission-requests", ref: refs.setReference, children: [jsxRuntime.jsxs("div", { className: "str-video__permission-requests__notification", children: [jsxRuntime.jsxs("span", { className: "str-video__permission-requests__notification__message", children: [permissionRequests.length, " pending permission requests"] }), jsxRuntime.jsx(Button, { variant: "secondary", appearance: "outline", size: "sm", className: "str-video__permission-request__button", onClick: () => {
1814
1966
  setExpanded((e) => !e);
1815
1967
  }, children: expanded ? 'Hide requests' : 'Show requests' })] }), expanded && (jsxRuntime.jsx(PermissionRequestList, { ref: refs.setFloating, style: {
1816
1968
  position: strategy,
@@ -1821,51 +1973,71 @@ const PermissionRequests = () => {
1821
1973
  };
1822
1974
  const PermissionRequestList = react.forwardRef(function PermissionRequestListRender(props, ref) {
1823
1975
  const { permissionRequests, handleUpdatePermission, ...rest } = props;
1824
- const { t } = videoReactBindings.useI18n();
1976
+ const { t } = useI18n();
1825
1977
  return (jsxRuntime.jsx("div", { className: "str-video__permission-requests-list", ref: ref, ...rest, children: permissionRequests.map((request, reqIndex) => {
1826
1978
  const { user, permissions } = request;
1827
- return (jsxRuntime.jsx(react.Fragment, { children: permissions.map((permission) => (jsxRuntime.jsxs("div", { className: "str-video__permission-request", children: [jsxRuntime.jsx("div", { className: "str-video__permission-request__message", children: messageForPermission(user.name || user.id, permission, t) }), jsxRuntime.jsx(Button, { className: "str-video__permission-request__button--allow", type: "button", onClick: handleUpdatePermission(request, 'grant'), children: t('Allow') }), jsxRuntime.jsx(Button, { className: "str-video__permission-request__button--reject", type: "button", onClick: handleUpdatePermission(request, 'revoke'), children: t('Revoke') }), jsxRuntime.jsx(Button, { className: "str-video__permission-request__button--reject", type: "button", onClick: handleUpdatePermission(request, 'dismiss'), children: t('Dismiss') })] }, permission))) }, `${user.id}/${reqIndex}`));
1979
+ return (jsxRuntime.jsx(react.Fragment, { children: permissions.map((permission) => (jsxRuntime.jsxs("div", { className: "str-video__permission-request", children: [jsxRuntime.jsx("div", { className: "str-video__permission-request__message", children: messageForPermission(user.name || user.id, permission, t) }), jsxRuntime.jsx(Button, { variant: "primary", appearance: "solid", size: "sm", className: "str-video__permission-request__button str-video__permission-request__button--allow", onClick: handleUpdatePermission(request, 'grant'), children: t('common.allow.label', 'Allow') }), jsxRuntime.jsx(Button, { variant: "secondary", appearance: "outline", size: "sm", className: "str-video__permission-request__button str-video__permission-request__button--reject", onClick: handleUpdatePermission(request, 'revoke'), children: t('common.revoke.label', 'Revoke') }), jsxRuntime.jsx(Button, { variant: "secondary", appearance: "outline", size: "sm", className: "str-video__permission-request__button str-video__permission-request__button--reject", onClick: handleUpdatePermission(request, 'dismiss'), children: t('common.dismiss.label', 'Dismiss') })] }, permission))) }, `${user.id}/${reqIndex}`));
1828
1980
  }) }));
1829
1981
  });
1830
- const Button = (props) => {
1831
- const { className, ...rest } = props;
1832
- return (jsxRuntime.jsx("button", { className: clsx('str-video__permission-request__button', className), ...rest }));
1833
- };
1834
1982
  const messageForPermission = (userName, permission, t) => {
1835
1983
  switch (permission) {
1836
1984
  case videoClient.OwnCapability.SEND_AUDIO:
1837
- return t('{{ userName }} is requesting to speak', { userName });
1985
+ return t('permissions.requestingToSpeak.text', '{{ userName }} is requesting to speak', { userName });
1838
1986
  case videoClient.OwnCapability.SEND_VIDEO:
1839
- return t('{{ userName }} is requesting to share their camera', {
1987
+ return t('permissions.requestingCamera.text', '{{ userName }} is requesting to share their camera', {
1840
1988
  userName,
1841
1989
  });
1842
1990
  case videoClient.OwnCapability.SCREENSHARE:
1843
- return t('{{ userName }} is requesting to present their screen', {
1991
+ return t('permissions.requestingScreenShare.text', '{{ userName }} is requesting to present their screen', {
1844
1992
  userName,
1845
1993
  });
1846
1994
  default:
1847
- return t('{{ userName }} is requesting permission: {{ permission }}', {
1995
+ return t('permissions.requestingPermission.text', '{{ userName }} is requesting permission: {{ permission }}', {
1848
1996
  userName,
1849
1997
  permission,
1850
1998
  });
1851
1999
  }
1852
2000
  };
1853
2001
 
1854
- const StreamTheme = ({ as: Component = 'div', className, children, ...props }) => {
1855
- return (jsxRuntime.jsx(Component, { ...props, className: clsx('str-video', className), children: children }));
2002
+ const StreamTheme = ({ as: Component = 'div', className, children, theme = 'str-video__theme-dark', ...props }) => {
2003
+ return (jsxRuntime.jsx(Component, { ...props, className: clsx('str-video', theme, className), children: children }));
1856
2004
  };
1857
2005
 
1858
2006
  const DefaultDisabledVideoPreview = () => {
1859
- const { t } = videoReactBindings.useI18n();
1860
- return (jsxRuntime.jsx("div", { className: "str_video__video-preview__disabled-video-preview", children: t('Video is disabled') }));
2007
+ const { t } = useI18n();
2008
+ return (jsxRuntime.jsx("div", { className: "str_video__video-preview__disabled-video-preview", children: t('common.videoDisabled.text', 'Video is disabled') }));
1861
2009
  };
1862
2010
  const DefaultNoCameraPreview = () => {
1863
- const { t } = videoReactBindings.useI18n();
1864
- return (jsxRuntime.jsx("div", { className: "str_video__video-preview__no-camera-preview", children: t('No camera found') }));
2011
+ const { t } = useI18n();
2012
+ return (jsxRuntime.jsx("div", { className: "str_video__video-preview__no-camera-preview", children: t('common.noCameraFound.text', 'No camera found') }));
1865
2013
  };
1866
- const VideoPreview = ({ className, mirror = true, DisabledVideoPreview = DefaultDisabledVideoPreview, NoCameraPreview = DefaultNoCameraPreview, StartingCameraPreview = LoadingIndicator, }) => {
2014
+ /**
2015
+ * Reports whether the local user is currently speaking, before they join the
2016
+ * call. `MicrophoneManager` only runs its detector while the microphone is
2017
+ * muted (for `speakingWhileMuted`), so this mirrors `AudioVolumeIndicator` and
2018
+ * runs one on the live microphone stream instead.
2019
+ */
2020
+ const useLocalSpeaking = (enabled) => {
2021
+ const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
2022
+ const { isEnabled, mediaStream } = useMicrophoneState();
2023
+ const [isSpeaking, setIsSpeaking] = react.useState(false);
2024
+ react.useEffect(() => {
2025
+ if (!enabled || !isEnabled || !mediaStream) {
2026
+ setIsSpeaking(false);
2027
+ return;
2028
+ }
2029
+ const dispose = videoClient.createSoundDetector(mediaStream, ({ isSoundDetected }) => setIsSpeaking(isSoundDetected), { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
2030
+ return () => {
2031
+ setIsSpeaking(false);
2032
+ dispose().catch(console.error);
2033
+ };
2034
+ }, [enabled, isEnabled, mediaStream]);
2035
+ return isSpeaking;
2036
+ };
2037
+ const VideoPreview = ({ className, mirror = true, DisabledVideoPreview = DefaultDisabledVideoPreview, NoCameraPreview = DefaultNoCameraPreview, StartingCameraPreview = LoadingIndicator, speakingIndicatorVisible = false, }) => {
1867
2038
  const { useCameraState } = videoReactBindings.useCallStateHooks();
1868
2039
  const { devices, status, isMute, mediaStream } = useCameraState();
2040
+ const isSpeaking = useLocalSpeaking(speakingIndicatorVisible);
1869
2041
  let contents;
1870
2042
  if (isMute && devices?.length === 0) {
1871
2043
  contents = jsxRuntime.jsx(NoCameraPreview, {});
@@ -1880,21 +2052,43 @@ const VideoPreview = ({ className, mirror = true, DisabledVideoPreview = Default
1880
2052
  else {
1881
2053
  contents = jsxRuntime.jsx(DisabledVideoPreview, {});
1882
2054
  }
1883
- return (jsxRuntime.jsx("div", { className: clsx('str-video__video-preview-container', className), children: contents }));
2055
+ return (jsxRuntime.jsx("div", { className: clsx('str-video__video-preview-container', className, {
2056
+ 'str-video__video-preview-container--speaking': isSpeaking,
2057
+ }), children: contents }));
2058
+ };
2059
+
2060
+ /** How many of the three bars are lit, per quality level. */
2061
+ const ACTIVE_BARS = {
2062
+ [videoClient.SfuModels.ConnectionQuality.POOR]: 1,
2063
+ [videoClient.SfuModels.ConnectionQuality.GOOD]: 2,
2064
+ [videoClient.SfuModels.ConnectionQuality.EXCELLENT]: 3,
2065
+ };
2066
+ const BAR_COUNT = 3;
2067
+ /**
2068
+ * Three ascending bars showing a participant's connection quality.
2069
+ */
2070
+ const ConnectionQualityIndicator = ({ quality, title, className, }) => {
2071
+ const activeBars = ACTIVE_BARS[quality];
2072
+ if (!activeBars)
2073
+ return null;
2074
+ const level = videoClient.SfuModels.ConnectionQuality[quality].toLowerCase();
2075
+ return (jsxRuntime.jsx("span", { className: clsx('str-video__connection-quality-indicator', `str-video__connection-quality-indicator--${level}`, className), title: title ?? level, children: Array.from({ length: BAR_COUNT }, (_, index) => (jsxRuntime.jsx("span", { className: clsx('str-video__connection-quality-indicator__bar', index < activeBars &&
2076
+ 'str-video__connection-quality-indicator__bar--active') }, index))) }));
1884
2077
  };
1885
2078
 
1886
2079
  const ToggleButton = react.forwardRef(function ToggleButtonRender(props, ref) {
1887
- return jsxRuntime.jsx(IconButton, { enabled: props.menuShown, icon: "ellipsis", ref: ref });
2080
+ const { t } = useI18n();
2081
+ return (jsxRuntime.jsx(IconButton, { className: "str-video__participant-view__menu-button", active: props.menuShown, size: "sm", variant: "secondary", ref: ref, title: t('participantView.moreOptions.title', 'More options'), icon: "ellipsis" }));
1888
2082
  });
1889
2083
  const DefaultScreenShareOverlay = () => {
1890
2084
  const call = videoReactBindings.useCall();
1891
- const { t } = videoReactBindings.useI18n();
2085
+ const { t } = useI18n();
1892
2086
  const stopScreenShare = () => {
1893
2087
  call?.screenShare.disable().catch((err) => {
1894
2088
  console.error('Failed to stop screen sharing:', err);
1895
2089
  });
1896
2090
  };
1897
- return (jsxRuntime.jsxs("div", { className: "str-video__screen-share-overlay", children: [jsxRuntime.jsx(Icon, { icon: "screen-share-off" }), jsxRuntime.jsx("span", { className: "str-video__screen-share-overlay__title", children: t('You are presenting your screen') }), jsxRuntime.jsxs("button", { onClick: stopScreenShare, type: "button", className: "str-video__screen-share-overlay__button", children: [jsxRuntime.jsx(Icon, { icon: "close" }), " ", t('Stop Screen Sharing')] })] }));
2091
+ return (jsxRuntime.jsxs("div", { className: "str-video__screen-share-overlay", children: [jsxRuntime.jsx(Icon, { icon: "screen-share-off" }), jsxRuntime.jsx("span", { className: "str-video__screen-share-overlay__title", children: t('common.presentingYourScreen.text', 'You are presenting your screen') }), jsxRuntime.jsxs(Button, { variant: "destructive", onClick: stopScreenShare, className: "str-video__screen-share-overlay__button", children: [jsxRuntime.jsx(Icon, { icon: "close" }), ' ', t('participantView.stopScreenSharing.label', 'Stop Screen Sharing')] })] }));
1898
2092
  };
1899
2093
  /**
1900
2094
  * Rendered over a remote participant's tile when the browser's autoplay policy
@@ -1903,8 +2097,8 @@ const DefaultScreenShareOverlay = () => {
1903
2097
  */
1904
2098
  const DefaultAudioBlockedNotification = () => {
1905
2099
  const call = videoReactBindings.useCall();
1906
- const { t } = videoReactBindings.useI18n();
1907
- return (jsxRuntime.jsx("button", { type: "button", className: "str-video__audio-blocked-notification", onClick: call?.resumeAudio, "aria-live": "polite", children: jsxRuntime.jsxs("span", { className: "str-video__audio-blocked-notification__message", children: [jsxRuntime.jsx(Icon, { icon: "speaker" }), jsxRuntime.jsxs("span", { className: "str-video__audio-blocked-notification__text", children: [jsxRuntime.jsx("span", { className: "str-video__audio-blocked-notification__title", children: t('Audio is blocked by your system') }), jsxRuntime.jsx("span", { className: "str-video__audio-blocked-notification__subtitle", children: t('Click or tap to play audio') })] })] }) }));
2100
+ const { t } = useI18n();
2101
+ return (jsxRuntime.jsx("button", { type: "button", className: "str-video__audio-blocked-notification", onClick: call?.resumeAudio, "aria-live": "polite", children: jsxRuntime.jsxs("span", { className: "str-video__audio-blocked-notification__message", children: [jsxRuntime.jsx(Icon, { icon: "speaker" }), jsxRuntime.jsxs("span", { className: "str-video__audio-blocked-notification__text", children: [jsxRuntime.jsx("span", { className: "str-video__audio-blocked-notification__title", children: t('participantView.audioBlocked.title', 'Audio is blocked by your system') }), jsxRuntime.jsx("span", { className: "str-video__audio-blocked-notification__subtitle", children: t('participantView.audioBlocked.description', 'Click or tap to play audio') })] })] }) }));
1908
2102
  };
1909
2103
  const DefaultParticipantViewUI = ({ indicatorsVisible = true, menuPlacement = 'bottom-start', showMenuButton = true, ParticipantActionsContextMenu: ParticipantActionsContextMenu$1 = ParticipantActionsContextMenu, AudioBlockedNotification = DefaultAudioBlockedNotification, }) => {
1910
2104
  const { participant, trackType } = useParticipantViewContext();
@@ -1925,17 +2119,17 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
1925
2119
  const { participant, trackType } = useParticipantViewContext();
1926
2120
  const { isLocalParticipant, connectionQuality, pin, sessionId, name, userId, } = participant;
1927
2121
  const call = videoReactBindings.useCall();
1928
- const { t } = videoReactBindings.useI18n();
1929
- const connectionQualityAsString = !!connectionQuality &&
1930
- videoClient.SfuModels.ConnectionQuality[connectionQuality].toLowerCase();
2122
+ const { t } = useI18n();
1931
2123
  const hasAudioTrack = videoClient.hasAudio(participant);
1932
2124
  const hasVideoTrack = videoClient.hasVideo(participant);
1933
2125
  const canUnpin = !!pin && pin.isLocalPin;
1934
2126
  const isTrackPaused = trackType !== 'none' ? videoClient.hasPausedTrack(participant, trackType) : false;
1935
2127
  const isAudioConnecting = videoReactBindings.useIsAudioConnecting(participant);
1936
2128
  const isVideoConnecting = videoReactBindings.useIsVideoConnecting(participant);
1937
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "str-video__participant-details", children: jsxRuntime.jsxs("div", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isAudioConnecting && (jsxRuntime.jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && isVideoConnecting && (jsxRuntime.jsx(LoadingIndicator, { className: "str-video__participant-details__name--video-connecting", tooltip: t('Video is connecting...') })), indicatorsVisible && !hasVideoTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsxRuntime.jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsxRuntime.jsx("span", { title: canUnpin ? t('Unpin') : t('Pinned'), onClick: canUnpin ? () => call?.unpin(sessionId) : undefined, className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsxRuntime.jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsxRuntime.jsx(Notification, { isVisible: isLocalParticipant &&
1938
- connectionQuality === videoClient.SfuModels.ConnectionQuality.POOR, message: t('Poor connection quality'), children: connectionQualityAsString && (jsxRuntime.jsx("span", { className: clsx('str-video__participant-details__connection-quality', `str-video__participant-details__connection-quality--${connectionQualityAsString}`), title: connectionQualityAsString })) }))] }));
2129
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "str-video__participant-details", children: jsxRuntime.jsxs("div", { className: "str-video__participant-details__name", children: [jsxRuntime.jsx("span", { className: "str-video__participant-details__name-text", children: name || userId }), indicatorsVisible && isAudioConnecting && (jsxRuntime.jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('participantView.audioConnecting.title', 'Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && isVideoConnecting && (jsxRuntime.jsx(LoadingIndicator, { className: "str-video__participant-details__name--video-connecting", tooltip: t('participantView.videoConnecting.title', 'Video is connecting...') })), indicatorsVisible && !hasVideoTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsxRuntime.jsx("span", { title: t('participantView.videoPausedInsufficientBandwidth.title', 'Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsxRuntime.jsx("span", { title: canUnpin
2130
+ ? t('participantView.unpin.label', 'Unpin')
2131
+ : t('participantList.pinned.title', 'Pinned'), onClick: canUnpin ? () => call?.unpin(sessionId) : undefined, className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsxRuntime.jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsxRuntime.jsx(Notification, { state: "error", isVisible: isLocalParticipant &&
2132
+ connectionQuality === videoClient.SfuModels.ConnectionQuality.POOR, message: t('participantView.connectionQuality.poor.text', 'Poor connection quality. Please check your internet connection.'), children: connectionQuality && (jsxRuntime.jsx(ConnectionQualityIndicator, { quality: connectionQuality })) }))] }));
1939
2133
  };
1940
2134
  const SpeechIndicator = () => {
1941
2135
  const { participant } = useParticipantViewContext();
@@ -1985,7 +2179,7 @@ const ParticipantView = react.memo(react.forwardRef(function ParticipantViewRend
1985
2179
  applyElementToRef(ref, element);
1986
2180
  setTrackedElement(element);
1987
2181
  }, className: clsx('str-video__participant-view', isDominantSpeaker &&
1988
- 'str-video__participant-view--dominant-speaker', isSpeaking && 'str-video__participant-view--speaking', !hasVideoTrack && 'str-video__participant-view--no-video', !hasAudioTrack && 'str-video__participant-view--no-audio', className), children: jsxRuntime.jsxs(ParticipantViewContext.Provider, { value: participantViewContextValue, children: [!isLocalParticipant && !muteAudio && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "audioTrack" })), hasScreenShareAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "screenShareAudioTrack" }))] })), jsxRuntime.jsx(Video$1, { VideoPlaceholder: VideoPlaceholder, PictureInPicturePlaceholder: PictureInPicturePlaceholder, participant: participant, trackType: trackType, refs: videoRefs, enabled: isLocalParticipant ||
2182
+ 'str-video__participant-view--dominant-speaker', isSpeaking && 'str-video__participant-view--speaking', !hasVideoTrack && 'str-video__participant-view--no-video', !hasAudioTrack && 'str-video__participant-view--no-audio', className), children: jsxRuntime.jsxs(ParticipantViewContext.Provider, { value: participantViewContextValue, children: [!isLocalParticipant && !muteAudio && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "audioTrack" })), hasScreenShareAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "screenShareAudioTrack" }))] })), jsxRuntime.jsx(Video, { VideoPlaceholder: VideoPlaceholder, PictureInPicturePlaceholder: PictureInPicturePlaceholder, participant: participant, trackType: trackType, refs: videoRefs, enabled: isLocalParticipant ||
1989
2183
  trackType !== 'videoTrack' ||
1990
2184
  isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsxRuntime.jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
1991
2185
  }));
@@ -1995,185 +2189,27 @@ ParticipantView.displayName = 'ParticipantView';
1995
2189
  const StreamCall = videoReactBindings.StreamCallProvider;
1996
2190
  StreamCall.displayName = 'StreamCall';
1997
2191
 
1998
- var Joining = "Joining";
1999
- var Mic = "Mic";
2000
- var Ringing = "Ringing";
2001
- var Speakers = "Speakers";
2002
- var Video = "Video";
2003
- var Live = "Live";
2004
- var Reactions = "Reactions";
2005
- var Statistics = "Statistics";
2006
- var Invite = "Invite";
2007
- var Join = "Join";
2008
- var You = "You";
2009
- var Me = "Me";
2010
- var Unknown = "Unknown";
2011
- var Default = "Default";
2012
- var Refresh = "Refresh";
2013
- var Allow = "Allow";
2014
- var Revoke = "Revoke";
2015
- var Dismiss = "Dismiss";
2016
- var Pinned = "Pinned";
2017
- var Unpin = "Unpin";
2018
- var Pin = "Pin";
2019
- var Block = "Block";
2020
- var Kick = "Kick";
2021
- var Enter = "Enter";
2022
- var Leave = "Leave";
2023
- var Participants = "Participants";
2024
- var Anonymous = ", and ({{ count }}) anonymous";
2025
- var Speaker = "Speaker";
2026
- var Microphone = "Microphone";
2027
- var Backstage = "Backstage";
2028
- var en = {
2029
- Joining: Joining,
2030
- Mic: Mic,
2031
- "No internet connection": "No internet connection",
2032
- "Re-connecting": "Re-connecting",
2033
- Ringing: Ringing,
2034
- "Screen Share": "Screen Share",
2035
- "Select a Camera": "Select a Camera",
2036
- "Select a Mic": "Select a Mic",
2037
- "Select Speakers": "Select Speakers",
2038
- Speakers: Speakers,
2039
- Video: Video,
2040
- "You are muted. Unmute to speak.": "You are muted. Unmute to speak.",
2041
- "Background filters performance is degraded. Consider disabling filters for better performance.": "Background filters performance is degraded. Consider disabling filters for better performance.",
2042
- Live: Live,
2043
- "Livestream starts soon": "Livestream starts soon",
2044
- "Livestream starts at {{ time }}": "Livestream starts at {{ time }}",
2045
- "{{ count }} participants joined early_one": "{{ count }} participant joined early",
2046
- "{{ count }} participants joined early_other": "{{ count }} participants joined early",
2047
- "You can now speak.": "You can now speak.",
2048
- "Awaiting for an approval to speak.": "Awaiting for an approval to speak.",
2049
- "You can no longer speak.": "You can no longer speak.",
2050
- "You can now share your video.": "You can now share your video.",
2051
- "Awaiting for an approval to share your video.": "Awaiting for an approval to share your video.",
2052
- "You can no longer share your video.": "You can no longer share your video.",
2053
- "Waiting for recording to stop...": "Waiting for recording to stop...",
2054
- "Waiting for recording to start...": "Waiting for recording to start...",
2055
- "Record call": "Record call",
2056
- Reactions: Reactions,
2057
- Statistics: Statistics,
2058
- "You can now share your screen.": "You can now share your screen.",
2059
- "Awaiting for an approval to share screen.": "Awaiting for an approval to share screen.",
2060
- "You can no longer share your screen.": "You can no longer share your screen.",
2061
- "Share screen": "Share screen",
2062
- "Incoming Call...": "Incoming Call...",
2063
- "Calling...": "Calling...",
2064
- "Mute All": "Mute All",
2065
- Invite: Invite,
2066
- Join: Join,
2067
- You: You,
2068
- Me: Me,
2069
- Unknown: Unknown,
2070
- "Toggle device menu": "Toggle device menu",
2071
- Default: Default,
2072
- "Call Recordings": "Call Recordings",
2073
- Refresh: Refresh,
2074
- "Check your browser video permissions": "Check your browser video permissions",
2075
- "Video publishing is disabled by the system": "Video publishing is disabled by the system",
2076
- "You have no permission to share your video": "You have no permission to share your video",
2077
- "You have no permission to share your audio": "You have no permission to share your audio",
2078
- "You are presenting your screen": "You are presenting your screen",
2079
- "Stop Screen Sharing": "Stop Screen Sharing",
2080
- "Audio is blocked by your system": "Audio is blocked by your system",
2081
- "Click or tap to play audio": "Click or tap to play audio",
2082
- Allow: Allow,
2083
- Revoke: Revoke,
2084
- Dismiss: Dismiss,
2085
- "Microphone on": "Microphone on",
2086
- "Microphone off": "Microphone off",
2087
- "Microphone is paused by your system": "Microphone is paused by your system",
2088
- "Camera on": "Camera on",
2089
- "Camera off": "Camera off",
2090
- "Camera is paused by your system": "Camera is paused by your system",
2091
- "No camera found": "No camera found",
2092
- "Video is disabled": "Video is disabled",
2093
- Pinned: Pinned,
2094
- Unpin: Unpin,
2095
- Pin: Pin,
2096
- "Pin for everyone": "Pin for everyone",
2097
- "Unpin for everyone": "Unpin for everyone",
2098
- Block: Block,
2099
- Kick: Kick,
2100
- "Turn off video": "Turn off video",
2101
- "Turn off screen share": "Turn off screen share",
2102
- "Mute audio": "Mute audio",
2103
- "Mute screen share audio": "Mute screen share audio",
2104
- "Allow audio": "Allow audio",
2105
- "Allow video": "Allow video",
2106
- "Allow screen sharing": "Allow screen sharing",
2107
- "Disable audio": "Disable audio",
2108
- "Disable video": "Disable video",
2109
- "Disable screen sharing": "Disable screen sharing",
2110
- Enter: Enter,
2111
- Leave: Leave,
2112
- "Leave call": "Leave call",
2113
- "End call for all": "End call for all",
2114
- "{{ direction }} fullscreen": "{{ direction }} fullscreen",
2115
- "{{ direction }} picture-in-picture": "{{ direction }} picture-in-picture",
2116
- "Dominant Speaker": "Dominant Speaker",
2117
- "Poor connection quality": "Poor connection quality. Please check your internet connection.",
2118
- "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
2119
- "Audio is connecting...": "Audio is connecting...",
2120
- "Video is connecting...": "Video is connecting...",
2121
- Participants: Participants,
2122
- Anonymous: Anonymous,
2123
- "No participants found": "No participants found",
2124
- "Participants ({{ numberOfParticipants }})": "Participants ({{ numberOfParticipants }})",
2125
- "{{ userName }} is sharing their screen": "{{ userName }} is sharing their screen",
2126
- "{{ userName }} is requesting to speak": "{{ userName }} is requesting to speak",
2127
- "{{ userName }} is requesting to share their camera": "{{ userName }} is requesting to share their camera",
2128
- "{{ userName }} is requesting to present their screen": "{{ userName }} is requesting to present their screen",
2129
- "{{ userName }} is requesting permission: {{ permission }}": "{{ userName }} is requesting permission: {{ permission }}",
2130
- "Applying...": "Applying...",
2131
- "Disable blur": "Disable blur",
2132
- "Blur background": "Blur background",
2133
- "Migrating...": "Migrating...",
2134
- "Reconnecting...": "Reconnecting...",
2135
- "You are offline. Check your internet connection.": "You are offline. Check your internet connection.",
2136
- "Failed to restore connection. Please try again.": "Failed to restore connection. Please try again.",
2137
- "Failed to join. Please try again.": "Failed to join. Please try again.",
2138
- "Set up your call before joining": "Set up your call before joining",
2139
- "Please grant your browser permission to access your camera and microphone.": "Please grant your browser permission to access your camera and microphone.",
2140
- "Start call": "Start call",
2141
- Speaker: Speaker,
2142
- Microphone: Microphone,
2143
- Backstage: Backstage,
2144
- "Go Live": "Go Live",
2145
- "Stop Live": "End Live",
2146
- "Enter Backstage": "Enter Backstage",
2147
- "Prepare your livestream": "Prepare your livestream",
2148
- "Ready to go live": "Ready to go live",
2149
- "Stream is ready!": "Stream is ready!",
2150
- "Waiting for the livestream to start": "Waiting for the livestream to start",
2151
- "{{ count }} waiting": "{{ count }} waiting",
2152
- "Join Stream": "Join Stream",
2153
- "Join automatically when stream starts": "Join automatically when stream starts",
2154
- "Display name": "Display name",
2155
- "Permission needed": "Permission needed",
2156
- "Call ended": "Call ended",
2157
- "Rejoin call": "Rejoin call",
2158
- "Left by mistake?": "Left by mistake?",
2159
- "Help us improve": "Help us improve",
2160
- "Leave feedback": "Leave feedback",
2161
- "Failed to rejoin. Please try again.": "Failed to rejoin. Please try again.",
2162
- "Share your feedback": "Share your feedback",
2163
- "Tell us about your experience...": "Tell us about your experience...",
2164
- "Submit feedback": "Submit feedback",
2165
- "Feedback message": "Feedback message",
2166
- "How was your call quality?": "How was your call quality?",
2167
- "Rate {{ count }} star_one": "Rate {{ count }} star",
2168
- "Rate {{ count }} star_other": "Rate {{ count }} stars",
2169
- "Thank you!": "Thank you!",
2170
- "Your feedback helps improve call quality.": "Your feedback helps improve call quality."
2171
- };
2172
-
2173
- const translations = { en };
2174
-
2175
- const StreamVideo = (props) => {
2176
- return (jsxRuntime.jsx(videoReactBindings.StreamVideoProvider, { translationsOverrides: translations, ...props }));
2192
+ /**
2193
+ * StreamVideo is the root provider of the SDK: it provides the client to the component tree and
2194
+ * mounts the SDK's translation context.
2195
+ *
2196
+ * The i18n runtime lives here rather than in `@stream-io/video-react-bindings` because the key
2197
+ * catalog is generated from *this* package's `t()` call sites.
2198
+ *
2199
+ * This also fixes a real bug in the previous implementation, which rendered
2200
+ * `<StreamVideoProvider translationsOverrides={translations} {...props} />`. Because `{...props}`
2201
+ * came last, an integrator passing `translationsOverrides` *replaced* the SDK's bundled strings
2202
+ * instead of merging with them, leaving every untranslated key rendering as a raw dotted path.
2203
+ * `useStreami18n` layers integrator dictionaries over the SDK's own defaults, so a partial
2204
+ * dictionary is now safe.
2205
+ */
2206
+ const StreamVideo = ({ children, client, i18nInstance, language, translations, }) => {
2207
+ const translationContext = useStreami18n({
2208
+ i18nInstance,
2209
+ language,
2210
+ translations,
2211
+ });
2212
+ return (jsxRuntime.jsx(videoReactBindings.StreamVideoProvider, { client: client, children: jsxRuntime.jsx(TranslationProvider, { value: translationContext, children: children }) }));
2177
2213
  };
2178
2214
  StreamVideo.displayName = 'StreamVideo';
2179
2215
 
@@ -2364,14 +2400,14 @@ const ParticipantOverlay = (props) => {
2364
2400
  const toggleFullScreen = useToggleFullScreen();
2365
2401
  const { speaker, volume } = useSpeakerState();
2366
2402
  const isSpeakerMuted = volume === 0;
2367
- const { t } = videoReactBindings.useI18n();
2368
- return (jsxRuntime.jsx("div", { className: "str-video__livestream-layout__overlay", children: overlayBarVisible && (jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar", children: [jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar-left", children: [showLiveBadge && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__live-badge", children: t('Live') })), showParticipantCount && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__viewers-count", children: humanizeParticipantCount
2369
- ? videoClient.humanize(participantCount)
2370
- : participantCount })), showSpeakerName && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__speaker-name", title: participant.name || participant.userId || '', children: participant.name || participant.userId || '' }))] }), jsxRuntime.jsx("div", { className: "str-video__livestream-layout__overlay__bar-center", children: showDuration && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__duration", children: formatDuration(duration) })) }), jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar-right", children: [showMuteButton && (jsxRuntime.jsx("span", { className: clsx('str-video__livestream-layout__mute-button', isSpeakerMuted &&
2403
+ const { t } = useI18n();
2404
+ return (jsxRuntime.jsx("div", { className: "str-video__livestream-layout__overlay", children: overlayBarVisible && (jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar", children: [jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar-left", children: [showLiveBadge && (jsxRuntime.jsxs("span", { className: "str-video__livestream-layout__live-badge", children: [jsxRuntime.jsx("span", { className: "str-video__livestream-layout__live-badge__dot", "aria-hidden": "true" }), t('common.live.label', 'Live')] })), showParticipantCount && (jsxRuntime.jsxs("span", { className: "str-video__livestream-layout__viewers-count", children: [jsxRuntime.jsx(Icon, { icon: "livestream-viewers" }), humanizeParticipantCount
2405
+ ? videoClient.humanize(participantCount)
2406
+ : participantCount] })), showSpeakerName && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__speaker-name", title: participant.name || participant.userId || '', children: jsxRuntime.jsx("span", { children: participant.name || participant.userId || '' }) }))] }), jsxRuntime.jsx("div", { className: "str-video__livestream-layout__overlay__bar-center", children: showDuration && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__duration", children: formatDuration(duration) })) }), jsxRuntime.jsxs("div", { className: "str-video__livestream-layout__overlay__bar-right", children: [showMuteButton && (jsxRuntime.jsx(IconButton, { icon: isSpeakerMuted ? 'speaker-off' : 'speaker', variant: "secondary", appearance: "solid", size: "sm", className: clsx('str-video__livestream-layout__mute-button', isSpeakerMuted &&
2371
2407
  'str-video__livestream-layout__mute-button--muted'), onClick: () => speaker.setVolume(isSpeakerMuted ? 1 : 0) })), enableFullScreen &&
2372
2408
  participantViewElement &&
2373
2409
  typeof participantViewElement.requestFullscreen !==
2374
- 'undefined' && (jsxRuntime.jsx("span", { className: "str-video__livestream-layout__go-fullscreen", onClick: toggleFullScreen }))] })] })) }));
2410
+ 'undefined' && (jsxRuntime.jsx(IconButton, { icon: "fullscreen", variant: "secondary", appearance: "solid", size: "sm", className: "str-video__livestream-layout__go-fullscreen", onClick: toggleFullScreen }))] })] })) }));
2375
2411
  };
2376
2412
  const useUpdateCallDuration = () => {
2377
2413
  const { useIsCallLive, useCallSession } = videoReactBindings.useCallStateHooks();
@@ -2436,6 +2472,7 @@ const PaginatedGridLayoutGroup = ({ group, mirror, VideoPlaceholder, PictureInPi
2436
2472
  }), children: group.map((participant) => (jsxRuntime.jsx(ParticipantView, { participant: participant, muteAudio: true, mirror: mirror, VideoPlaceholder: VideoPlaceholder, PictureInPicturePlaceholder: PictureInPicturePlaceholder, ParticipantViewUI: ParticipantViewUI }, participant.sessionId))) }));
2437
2473
  };
2438
2474
  const PaginatedGridLayout = (props) => {
2475
+ const { t } = useI18n();
2439
2476
  const { groupSize = (props.groupSize || 0) > 0
2440
2477
  ? props.groupSize || GROUP_SIZE
2441
2478
  : GROUP_SIZE, excludeLocalParticipant = false, filterParticipants, mirrorLocalParticipantVideo = true, pageArrowsVisible = true, VideoPlaceholder, ParticipantViewUI = DefaultParticipantViewUI, PictureInPicturePlaceholder, muted, } = props;
@@ -2467,7 +2504,7 @@ const PaginatedGridLayout = (props) => {
2467
2504
  const mirror = mirrorLocalParticipantVideo ? undefined : false;
2468
2505
  if (!call)
2469
2506
  return null;
2470
- return (jsxRuntime.jsxs("div", { className: "str-video__paginated-grid-layout__wrapper", ref: setPaginatedGridLayoutWrapperElement, children: [!muted && jsxRuntime.jsx(ParticipantsAudio, { participants: remoteParticipants }), jsxRuntime.jsxs("div", { className: "str-video__paginated-grid-layout", children: [pageArrowsVisible && pageCount > 1 && (jsxRuntime.jsx(IconButton, { icon: "caret-left", disabled: page === 0, onClick: () => setPage((currentPage) => Math.max(0, currentPage - 1)) })), selectedGroup && (jsxRuntime.jsx(PaginatedGridLayoutGroup, { group: selectedGroup, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI, PictureInPicturePlaceholder: PictureInPicturePlaceholder })), pageArrowsVisible && pageCount > 1 && (jsxRuntime.jsx(IconButton, { disabled: page === pageCount - 1, icon: "caret-right", onClick: () => setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1)) }))] })] }));
2507
+ return (jsxRuntime.jsxs("div", { className: "str-video__paginated-grid-layout__wrapper", ref: setPaginatedGridLayoutWrapperElement, children: [!muted && jsxRuntime.jsx(ParticipantsAudio, { participants: remoteParticipants }), jsxRuntime.jsxs("div", { className: "str-video__paginated-grid-layout", children: [pageArrowsVisible && pageCount > 1 && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", icon: "caret-left", "aria-label": t('callLayout.paginatedGridLayout.previousPage.ariaLabel', 'Previous page'), disabled: page === 0, onClick: () => setPage((currentPage) => Math.max(0, currentPage - 1)) })), selectedGroup && (jsxRuntime.jsx(PaginatedGridLayoutGroup, { group: selectedGroup, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI, PictureInPicturePlaceholder: PictureInPicturePlaceholder })), pageArrowsVisible && pageCount > 1 && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", disabled: page === pageCount - 1, icon: "caret-right", "aria-label": t('callLayout.paginatedGridLayout.nextPage.ariaLabel', 'Next page'), onClick: () => setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1)) }))] })] }));
2471
2508
  };
2472
2509
  PaginatedGridLayout.displayName = 'PaginatedGridLayout';
2473
2510
 
@@ -2572,6 +2609,7 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
2572
2609
  };
2573
2610
  SpeakerLayout.displayName = 'SpeakerLayout';
2574
2611
  const HorizontalScrollButtons = ({ scrollWrapper, }) => {
2612
+ const { t } = useI18n();
2575
2613
  const scrollPosition = useHorizontalScrollPosition(scrollWrapper);
2576
2614
  const scrollStartClickHandler = () => {
2577
2615
  scrollWrapper?.scrollBy({ left: -150, behavior: 'smooth' });
@@ -2579,9 +2617,10 @@ const HorizontalScrollButtons = ({ scrollWrapper, }) => {
2579
2617
  const scrollEndClickHandler = () => {
2580
2618
  scrollWrapper?.scrollBy({ left: 150, behavior: 'smooth' });
2581
2619
  };
2582
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [scrollPosition && scrollPosition !== 'start' && (jsxRuntime.jsx(IconButton, { onClick: scrollStartClickHandler, icon: "caret-left", className: "str-video__speaker-layout__participants-bar--button-left" })), scrollPosition && scrollPosition !== 'end' && (jsxRuntime.jsx(IconButton, { onClick: scrollEndClickHandler, icon: "caret-right", className: "str-video__speaker-layout__participants-bar--button-right" }))] }));
2620
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [scrollPosition && scrollPosition !== 'start' && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollStartClickHandler, icon: "caret-left", "aria-label": t('callLayout.speakerLayout.scrollParticipantsLeft.ariaLabel', 'Scroll participants left'), className: "str-video__speaker-layout__participants-bar--button-left" })), scrollPosition && scrollPosition !== 'end' && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollEndClickHandler, icon: "caret-right", "aria-label": t('callLayout.speakerLayout.scrollParticipantsRight.ariaLabel', 'Scroll participants right'), className: "str-video__speaker-layout__participants-bar--button-right" }))] }));
2583
2621
  };
2584
2622
  const VerticalScrollButtons = ({ scrollWrapper, }) => {
2623
+ const { t } = useI18n();
2585
2624
  const scrollPosition = useVerticalScrollPosition(scrollWrapper);
2586
2625
  const scrollTopClickHandler = () => {
2587
2626
  scrollWrapper?.scrollBy({ top: -150, behavior: 'smooth' });
@@ -2589,7 +2628,7 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
2589
2628
  const scrollBottomClickHandler = () => {
2590
2629
  scrollWrapper?.scrollBy({ top: 150, behavior: 'smooth' });
2591
2630
  };
2592
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (jsxRuntime.jsx(IconButton, { onClick: scrollTopClickHandler, icon: "caret-up", className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (jsxRuntime.jsx(IconButton, { onClick: scrollBottomClickHandler, icon: "caret-down", className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
2631
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollTopClickHandler, icon: "caret-up", "aria-label": t('callLayout.speakerLayout.scrollParticipantsUp.ariaLabel', 'Scroll participants up'), className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (jsxRuntime.jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollBottomClickHandler, icon: "caret-down", "aria-label": t('callLayout.speakerLayout.scrollParticipantsDown.ariaLabel', 'Scroll participants down'), className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
2593
2632
  };
2594
2633
 
2595
2634
  const defaultConfiguration = {
@@ -2731,7 +2770,7 @@ const useInitializeCall = ({ client, callType, callId, handleError, }) => {
2731
2770
  };
2732
2771
 
2733
2772
  const BlurToggleButton = () => {
2734
- const { t } = videoReactBindings.useI18n();
2773
+ const { t } = useI18n();
2735
2774
  const { useCameraState } = videoReactBindings.useCallStateHooks();
2736
2775
  const { isMute } = useCameraState();
2737
2776
  const { isSupported, isReady, isLoading, backgroundFilter, applyBackgroundBlurFilter, disableBackgroundFilter, } = useBackgroundFilters();
@@ -2754,44 +2793,59 @@ const BlurToggleButton = () => {
2754
2793
  ]);
2755
2794
  const getLabel = () => {
2756
2795
  if (isLoading)
2757
- return t('Applying...');
2758
- return isBlurred ? t('Disable blur') : t('Blur background');
2796
+ return t('callControls.blurToggleButton.applying.label', 'Applying...');
2797
+ return isBlurred
2798
+ ? t('callControls.blurToggleButton.disableBlur.label', 'Disable blur')
2799
+ : t('callControls.blurToggleButton.blurBackground.label', 'Blur background');
2759
2800
  };
2760
2801
  if (!isSupported)
2761
2802
  return null;
2762
- return (jsxRuntime.jsxs("button", { type: "button", className: clsx('str-video__embedded-blur-toggle', isBlurred && 'str-video__embedded-blur-toggle--active'), disabled: isDisabled, "aria-pressed": isBlurred, onClick: handleClick, children: [jsxRuntime.jsx(Icon, { icon: "blur-icon" }), jsxRuntime.jsx("span", { children: getLabel() })] }));
2803
+ return (jsxRuntime.jsxs(Button, { variant: "secondary", appearance: "outline", size: "sm", className: "str-video__embedded-blur-toggle", disabled: isDisabled, active: isBlurred, onClick: handleClick, children: [jsxRuntime.jsx(Icon, { icon: "blur-icon" }), jsxRuntime.jsx("span", { children: getLabel() })] }));
2763
2804
  };
2764
2805
  const CameraMenuWithBlur = () => {
2765
2806
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorVideo, { visualType: "list" }), jsxRuntime.jsx("div", { className: "str-video__embedded-blur-toggle-container", children: jsxRuntime.jsx(BlurToggleButton, {}) })] }));
2766
2807
  };
2767
2808
 
2809
+ const CallDuration = ({ elapsed }) => {
2810
+ if (!elapsed)
2811
+ return null;
2812
+ const secondsAt = elapsed.lastIndexOf(':') + 1;
2813
+ const leading = elapsed.slice(0, secondsAt);
2814
+ const seconds = elapsed.slice(secondsAt);
2815
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-duration", children: [jsxRuntime.jsx(Icon, { icon: "verified", className: "str-video__embedded-call-duration__icon" }), jsxRuntime.jsxs("span", { className: "str-video__embedded-call-duration__time", children: [jsxRuntime.jsx("span", { className: "str-video__embedded-call-duration__time-leading", children: leading }), seconds] })] }));
2816
+ };
2817
+
2768
2818
  const CallEndedScreen = ({ onJoin, onFeedback, }) => {
2769
- const { t } = videoReactBindings.useI18n();
2770
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('Call ended') }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-actions", children: [onJoin && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-column", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__ended-label", children: t('Left by mistake?') }), jsxRuntime.jsxs("button", { type: "button", className: "str-video__embedded-call-feedback__ended-button", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { icon: "login" }), t('Rejoin call')] })] }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__ended-divider" })] })), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-column", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__ended-label", children: t('Help us improve') }), jsxRuntime.jsxs("button", { type: "button", className: "str-video__embedded-call-feedback__ended-button", onClick: onFeedback, children: [jsxRuntime.jsx(Icon, { icon: "feedback" }), t('Leave feedback')] })] })] })] }));
2819
+ const { t } = useI18n();
2820
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('callFeedback.callEndedScreen.callEnded.title', 'Call ended') }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-actions", children: [onJoin && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-column", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__ended-label", children: t('callFeedback.callEndedScreen.leftByMistake.text', 'Left by mistake?') }), jsxRuntime.jsxs(Button, { variant: "secondary", appearance: "outline", className: "str-video__embedded-call-feedback__ended-button", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { icon: "login" }), t('callFeedback.callEndedScreen.rejoinCall.label', 'Rejoin call')] })] }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__ended-divider" })] })), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__ended-column", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__ended-label", children: t('callFeedback.callEndedScreen.helpUsImprove.text', 'Help us improve') }), jsxRuntime.jsxs(Button, { variant: "secondary", appearance: "outline", className: "str-video__embedded-call-feedback__ended-button", onClick: onFeedback, children: [jsxRuntime.jsx(Icon, { icon: "feedback" }), t('callFeedback.callEndedScreen.leaveFeedback.label', 'Leave feedback')] })] })] })] }));
2771
2821
  };
2772
2822
 
2773
2823
  const StarRating = ({ value, onChange }) => {
2774
- const { t } = videoReactBindings.useI18n();
2824
+ const { t } = useI18n();
2775
2825
  const [hovered, setHovered] = react.useState(0);
2776
2826
  const displayValue = hovered || value;
2777
2827
  const getStarClasses = (star) => clsx('str-video__embedded-call-feedback__star', star <= displayValue && 'str-video__embedded-call-feedback__star--active');
2778
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__rating-section", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__rating-label", children: t('How was your call quality?') }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__stars", onMouseLeave: () => setHovered(0), children: [1, 2, 3, 4, 5].map((star) => (jsxRuntime.jsx("button", { type: "button", className: getStarClasses(star), onClick: () => onChange(star), onMouseEnter: () => setHovered(star), "aria-label": t('Rate {{ count }} star', { count: star }), children: jsxRuntime.jsx(Icon, { icon: "star-filled" }) }, star))) })] }));
2828
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__rating-section", children: [jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__rating-label", children: t('callFeedback.starRating.howWasCallQuality.text', 'How was your call quality?') }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__stars", onMouseLeave: () => setHovered(0), children: [1, 2, 3, 4, 5].map((star) => (jsxRuntime.jsx("button", { type: "button", className: getStarClasses(star), onClick: () => onChange(star), onMouseEnter: () => setHovered(star), "aria-label": t('callFeedback.starRating.rateStars.ariaLabel', {
2829
+ count: star,
2830
+ defaultValue_one: 'Rate {{ count }} star',
2831
+ defaultValue_other: 'Rate {{ count }} stars',
2832
+ }), children: jsxRuntime.jsx(Icon, { icon: "star-filled" }) }, star))) })] }));
2779
2833
  };
2780
2834
 
2781
2835
  const RatingScreen = ({ onSubmit }) => {
2782
- const { t } = videoReactBindings.useI18n();
2836
+ const { t } = useI18n();
2783
2837
  const [rating, setRating] = react.useState(0);
2784
2838
  const [message, setMessage] = react.useState('');
2785
2839
  const handleSubmit = () => {
2786
2840
  if (rating > 0)
2787
2841
  onSubmit(rating, message);
2788
2842
  };
2789
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('Share your feedback') }), jsxRuntime.jsx(StarRating, { value: rating, onChange: setRating }), jsxRuntime.jsx("textarea", { "aria-label": t('Feedback message'), className: "str-video__embedded-call-feedback__textarea", placeholder: t('Tell us about your experience...'), value: message, onChange: (e) => setMessage(e.target.value), rows: 3 }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__actions", children: jsxRuntime.jsx("button", { type: "button", className: "str-video__button", onClick: handleSubmit, disabled: rating === 0, children: t('Submit feedback') }) })] }));
2843
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('callFeedback.ratingScreen.shareYourFeedback.title', 'Share your feedback') }), jsxRuntime.jsx(StarRating, { value: rating, onChange: setRating }), jsxRuntime.jsx("textarea", { "aria-label": t('callFeedback.ratingScreen.feedbackMessage.ariaLabel', 'Feedback message'), className: "str-video__embedded-call-feedback__textarea", placeholder: t('callFeedback.ratingScreen.tellUsAboutExperience.placeholder', 'Tell us about your experience...'), value: message, onChange: (e) => setMessage(e.target.value), rows: 3 }), jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__actions", children: jsxRuntime.jsx(Button, { size: "sm", onClick: handleSubmit, disabled: rating === 0, children: t('callFeedback.ratingScreen.submitFeedback.label', 'Submit feedback') }) })] }));
2790
2844
  };
2791
2845
 
2792
2846
  const ThankYouScreen = () => {
2793
- const { t } = videoReactBindings.useI18n();
2794
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__checkmark", children: jsxRuntime.jsx(Icon, { icon: "checkmark" }) }), jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('Thank you!') }), jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__subtitle", children: t('Your feedback helps improve call quality.') })] }));
2847
+ const { t } = useI18n();
2848
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-feedback__container", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-call-feedback__checkmark", children: jsxRuntime.jsx(Icon, { icon: "checkmark" }) }), jsxRuntime.jsx("h2", { className: "str-video__embedded-call-feedback__title", children: t('callFeedback.thankYouScreen.thankYou.title', 'Thank you!') }), jsxRuntime.jsx("p", { className: "str-video__embedded-call-feedback__subtitle", children: t('callFeedback.thankYouScreen.feedbackHelpsImprove.text', 'Your feedback helps improve call quality.') })] }));
2795
2849
  };
2796
2850
 
2797
2851
  const CallFeedback = ({ onJoin }) => {
@@ -2820,7 +2874,7 @@ const CallFeedback = ({ onJoin }) => {
2820
2874
  };
2821
2875
 
2822
2876
  const ConnectionNotification = () => {
2823
- const { t } = videoReactBindings.useI18n();
2877
+ const { t } = useI18n();
2824
2878
  const { useCallCallingState } = videoReactBindings.useCallStateHooks();
2825
2879
  const callingState = useCallCallingState();
2826
2880
  const isOffline = callingState === videoClient.CallingState.OFFLINE;
@@ -2831,16 +2885,16 @@ const ConnectionNotification = () => {
2831
2885
  const showError = isOffline || hasFailedToRecover;
2832
2886
  const showLoading = isJoining || isReconnecting || isMigrating;
2833
2887
  if (showError) {
2834
- return (jsxRuntime.jsx("div", { className: "str-video__embedded-connection-notification", children: jsxRuntime.jsx(Notification, { isVisible: true, placement: "bottom", message: isOffline
2835
- ? t('You are offline. Check your internet connection.')
2836
- : t('Failed to restore connection. Please try again.') }) }));
2888
+ return (jsxRuntime.jsx("div", { className: "str-video__embedded-connection-notification", children: jsxRuntime.jsx(Notification, { isVisible: true, state: "error", placement: "bottom", message: isOffline
2889
+ ? t('connectionNotification.offline.text', 'You are offline. Check your internet connection.')
2890
+ : t('connectionNotification.failedToRestore.text', 'Failed to restore connection. Please try again.') }) }));
2837
2891
  }
2838
2892
  if (showLoading) {
2839
- return (jsxRuntime.jsx("div", { className: "str-video__embedded-connection-notification", children: jsxRuntime.jsx(Notification, { isVisible: true, placement: "bottom", iconClassName: null, message: jsxRuntime.jsx(LoadingIndicator, { text: isMigrating
2840
- ? t('Migrating...')
2841
- : isJoining
2842
- ? t('Joining')
2843
- : t('Reconnecting...') }) }) }));
2893
+ return (jsxRuntime.jsx("div", { className: "str-video__embedded-connection-notification", children: jsxRuntime.jsx(Notification, { isVisible: true, state: "loading", placement: "bottom", message: isMigrating
2894
+ ? t('connectionNotification.migrating.text', 'Migrating...')
2895
+ : isJoining
2896
+ ? t('connectionNotification.joining.text', 'Joining')
2897
+ : t('connectionNotification.reconnecting.text', 'Reconnecting...') }) }));
2844
2898
  }
2845
2899
  return null;
2846
2900
  };
@@ -2864,8 +2918,8 @@ const EmbeddedParticipantViewUI = () => {
2864
2918
  };
2865
2919
 
2866
2920
  const JoinError = ({ onJoin }) => {
2867
- const { t } = videoReactBindings.useI18n();
2868
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-join-error", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-join-error__title", children: t('Failed to join the call') }), jsxRuntime.jsx("p", { className: "str-video__embedded-join-error__message", children: t("We couldn't connect to the server. Please check your connection and try again.") }), jsxRuntime.jsxs("button", { type: "button", className: "str-video__button", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { icon: "login" }), t('Try again')] })] }));
2921
+ const { t } = useI18n();
2922
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-join-error", children: [jsxRuntime.jsx("h2", { className: "str-video__embedded-join-error__title", children: t('joinError.failedToJoin.title', 'Failed to join the call') }), jsxRuntime.jsx("p", { className: "str-video__embedded-join-error__message", children: t('joinError.couldNotConnect.text', "We couldn't connect to the server. Please check your connection and try again.") }), jsxRuntime.jsxs(Button, { size: "sm", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { icon: "login" }), t('common.tryAgain.label', 'Try again')] })] }));
2869
2923
  };
2870
2924
 
2871
2925
  const ToggleMenuButton$1 = react.forwardRef(function ToggleMenuButtonRender(props, ref) {
@@ -2875,8 +2929,8 @@ const ToggleMenuButton$1 = react.forwardRef(function ToggleMenuButtonRender(prop
2875
2929
  ?.label || 'Default' }), jsxRuntime.jsx(Icon, { icon: props.menuShown ? 'chevron-down' : 'chevron-up' })] }));
2876
2930
  });
2877
2931
  const ToggleMicButton = () => {
2878
- const { t } = videoReactBindings.useI18n();
2879
- return (jsxRuntime.jsxs(MenuToggle, { placement: "top-start", ToggleButton: ToggleMenuButton$1, visualType: MenuVisualType.MENU, children: [jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('Microphone') }), jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('Speaker') })] }));
2932
+ const { t } = useI18n();
2933
+ return (jsxRuntime.jsxs(MenuToggle, { placement: "top-start", ToggleButton: ToggleMenuButton$1, visualType: MenuVisualType.MENU, children: [jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('common.microphone.label', 'Microphone') }), jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('common.speaker.label', 'Speaker') })] }));
2880
2934
  };
2881
2935
 
2882
2936
  const ToggleMenuButton = react.forwardRef(function ToggleMenuButtonRender(props, ref) {
@@ -2892,48 +2946,49 @@ const ToggleCameraButton = () => {
2892
2946
  const DisabledDeviceButton = ({ icon, label, }) => (jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__device-button str-video__embedded-lobby__device-button--disabled", children: [jsxRuntime.jsx(Icon, { className: "str-video__embedded-lobby__device-button-icon", icon: icon }), jsxRuntime.jsx("span", { className: "str-video__embedded-lobby__device-button-label", children: label })] }));
2893
2947
 
2894
2948
  const DisabledVideoPreview = () => {
2895
- const { t } = videoReactBindings.useI18n();
2949
+ const { t } = useI18n();
2896
2950
  const user = videoReactBindings.useConnectedUser();
2897
2951
  const { useCameraState, useMicrophoneState } = videoReactBindings.useCallStateHooks();
2898
2952
  const { hasBrowserPermission: hasCameraPermission } = useCameraState();
2899
2953
  const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
2900
2954
  const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
2901
- return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby__no-permission", children: hasBrowserMediaPermission ? (jsxRuntime.jsx(Avatar, { imageSrc: user?.image, name: user?.name || user?.id })) : (t('Please grant your browser permission to access your camera and microphone.')) }));
2955
+ return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby__no-permission", children: hasBrowserMediaPermission ? (jsxRuntime.jsx(Avatar, { imageSrc: user?.image, name: user?.name || user?.id })) : (t('lobby.videoPreviewFallbacks.grantBrowserPermission.text', 'Please grant your browser permission to access your camera and microphone.')) }));
2902
2956
  };
2903
2957
  const NoCameraPreview = () => {
2904
- const { t } = videoReactBindings.useI18n();
2958
+ const { t } = useI18n();
2905
2959
  const { useCameraState, useMicrophoneState } = videoReactBindings.useCallStateHooks();
2906
2960
  const { hasBrowserPermission: hasCameraPermission } = useCameraState();
2907
2961
  const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
2908
2962
  const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
2909
2963
  if (!hasBrowserMediaPermission) {
2910
- return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby__no-permission", children: t('Please grant your browser permission to access your camera and microphone.') }));
2964
+ return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby__no-permission", children: t('lobby.videoPreviewFallbacks.grantBrowserPermission.text', 'Please grant your browser permission to access your camera and microphone.') }));
2911
2965
  }
2912
- return (jsxRuntime.jsx("div", { className: "str-video__video-preview__no-camera-preview", children: t('No camera found') }));
2966
+ return (jsxRuntime.jsx("div", { className: "str-video__video-preview__no-camera-preview", children: t('common.noCameraFound.text', 'No camera found') }));
2913
2967
  };
2914
2968
 
2915
2969
  const DeviceControls = ({ isVideoEnabled }) => {
2916
- const { t } = videoReactBindings.useI18n();
2970
+ const { t } = useI18n();
2971
+ const user = videoReactBindings.useConnectedUser();
2917
2972
  const { useCameraState, useMicrophoneState } = videoReactBindings.useCallStateHooks();
2918
2973
  const { hasBrowserPermission: hasCameraPermission } = useCameraState();
2919
2974
  const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
2920
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__video-preview", children: [jsxRuntime.jsx(VideoPreview, { DisabledVideoPreview: DisabledVideoPreview, NoCameraPreview: NoCameraPreview }), jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__media-toggle", children: [jsxRuntime.jsx(ToggleAudioPreviewButton, { Menu: null }), isVideoEnabled && jsxRuntime.jsx(ToggleVideoPreviewButton, { Menu: null })] })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__media", children: [hasMicPermission ? (jsxRuntime.jsx(ToggleMicButton, {})) : (jsxRuntime.jsx(DisabledDeviceButton, { icon: "mic", label: t('Permission needed') })), isVideoEnabled &&
2921
- (hasCameraPermission ? (jsxRuntime.jsx(ToggleCameraButton, {})) : (jsxRuntime.jsx(DisabledDeviceButton, { icon: "camera", label: t('Permission needed') })))] })] }));
2975
+ const displayName = user?.name || user?.id;
2976
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__video-preview", children: [jsxRuntime.jsx(VideoPreview, { DisabledVideoPreview: DisabledVideoPreview, NoCameraPreview: NoCameraPreview }), displayName && (jsxRuntime.jsx("span", { className: "str-video__embedded-lobby__participant-label", children: jsxRuntime.jsx("span", { className: "str-video__embedded-lobby__participant-label-text", children: displayName }) })), jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__media-toggle", children: [jsxRuntime.jsx(ToggleAudioPreviewButton, { Menu: null }), isVideoEnabled && jsxRuntime.jsx(ToggleVideoPreviewButton, { Menu: null })] })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__media", children: [hasMicPermission ? (jsxRuntime.jsx(ToggleMicButton, {})) : (jsxRuntime.jsx(DisabledDeviceButton, { icon: "mic", label: t('lobby.deviceControls.permissionNeeded.label', 'Permission needed') })), isVideoEnabled &&
2977
+ (hasCameraPermission ? (jsxRuntime.jsx(ToggleCameraButton, {})) : (jsxRuntime.jsx(DisabledDeviceButton, { icon: "camera", label: t('lobby.deviceControls.permissionNeeded.label', 'Permission needed') })))] })] }));
2922
2978
  };
2923
2979
 
2924
2980
  /**
2925
2981
  * Lobby component - Device setup screen before joining a call.
2926
2982
  */
2927
2983
  const Lobby = ({ onJoin, title, joinLabel }) => {
2928
- const { t } = videoReactBindings.useI18n();
2929
- const user = videoReactBindings.useConnectedUser();
2984
+ const { t } = useI18n();
2930
2985
  const { useCameraState, useCallSettings } = videoReactBindings.useCallStateHooks();
2931
2986
  const { isMute } = useCameraState();
2932
2987
  const settings = useCallSettings();
2933
2988
  const isVideoEnabled = settings?.video.enabled ?? true;
2934
- const resolvedJoinLabel = joinLabel ?? t('Join');
2935
- const resolvedTitle = title ?? t('Set up your call before joining');
2936
- return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby", children: jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__content", children: [jsxRuntime.jsx("h1", { className: "str-video__embedded-lobby__heading", children: resolvedTitle }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-lobby__camera', isMute && 'str-video__embedded-lobby__camera--off'), children: jsxRuntime.jsx(DeviceControls, { isVideoEnabled: isVideoEnabled }) }), jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__display-name", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-lobby__display-name-label", children: t('Display name') }), jsxRuntime.jsx("span", { className: "str-video__embedded-lobby__display-name-value", children: user?.name }), jsxRuntime.jsxs("button", { className: "str-video__button", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { className: "str-video__button__icon", icon: "login" }), resolvedJoinLabel] })] })] }) }));
2989
+ const resolvedJoinLabel = joinLabel ?? t('lobby.join.label', 'Join');
2990
+ const resolvedTitle = title ?? t('lobby.setUpYourCall.title', 'Set up your call before joining');
2991
+ return (jsxRuntime.jsx("div", { className: "str-video__embedded-lobby", children: jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__content", children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-lobby__title", children: [jsxRuntime.jsx(Icon, { icon: "language", className: "str-video__embedded-lobby__title-icon" }), jsxRuntime.jsx("h1", { className: "str-video__embedded-lobby__heading", children: resolvedTitle })] }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-lobby__camera', isMute && 'str-video__embedded-lobby__camera--off'), children: jsxRuntime.jsx(DeviceControls, { isVideoEnabled: isVideoEnabled }) }), jsxRuntime.jsxs(Button, { className: "str-video__embedded-lobby__join", onClick: onJoin, children: [jsxRuntime.jsx(Icon, { icon: "login" }), resolvedJoinLabel] })] }) }));
2937
2992
  };
2938
2993
 
2939
2994
  const ViewersCount = ({ count }) => (jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration__viewers", children: [jsxRuntime.jsx(Icon, { icon: "eye", className: "str-video__embedded-livestream-duration__eye-icon" }), jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__count", children: videoClient.humanize(count) })] }));
@@ -3223,12 +3278,12 @@ const EmbeddedClientProvider = ({ apiKey, user, callId, callType, token, tokenPr
3223
3278
  * Renders the active call control bar
3224
3279
  */
3225
3280
  const CallControls = ({ showParticipants, onToggleParticipants, }) => {
3226
- const { t } = videoReactBindings.useI18n();
3281
+ const { t } = useI18n();
3227
3282
  const { useCallSession } = videoReactBindings.useCallStateHooks();
3228
3283
  const session = useCallSession();
3229
3284
  const startedAt = session?.started_at;
3230
3285
  const { elapsed } = useCallDuration(startedAt);
3231
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), startedAt && (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-duration str-video__embedded-desktop", children: [jsxRuntime.jsx(Icon, { icon: "verified", className: "str-video__embedded-call-duration__icon" }), jsxRuntime.jsx("span", { className: "str-video__embedded-call-duration__time", children: elapsed })] }))] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('Microphone') }), jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('Speaker') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ScreenShareButton, {}) }) }), jsxRuntime.jsx(RecordCallConfirmationButton, {}), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('Participants'), "aria-pressed": showParticipants, onClick: onToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] }));
3286
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), startedAt && (jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CallDuration, { elapsed: elapsed }) }))] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('common.microphone.label', 'Microphone') }), jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('common.speaker.label', 'Speaker') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ScreenShareButton, {}) }) }), jsxRuntime.jsx(RecordCallConfirmationButton, {}), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('common.participants.label', 'Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('common.participants.label', 'Participants'), "aria-pressed": showParticipants, onClick: onToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] }));
3232
3287
  };
3233
3288
 
3234
3289
  /**
@@ -3239,7 +3294,7 @@ const CallHeader = () => {
3239
3294
  const session = useCallSession();
3240
3295
  const startedAt = session?.started_at;
3241
3296
  const { elapsed } = useCallDuration(startedAt);
3242
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [startedAt && (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-duration", children: [jsxRuntime.jsx(Icon, { icon: "verified", className: "str-video__embedded-call-duration__icon" }), jsxRuntime.jsx("span", { className: "str-video__embedded-call-duration__time", children: elapsed })] })), jsxRuntime.jsx(CancelCallConfirmButton, {})] }));
3297
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [startedAt && jsxRuntime.jsx(CallDuration, { elapsed: elapsed }), jsxRuntime.jsx(CancelCallConfirmButton, {})] }));
3243
3298
  };
3244
3299
 
3245
3300
  /**
@@ -3252,7 +3307,7 @@ const CallLayout = () => {
3252
3307
  const handleToggleParticipants = react.useCallback(() => {
3253
3308
  setShowParticipants((prev) => !prev);
3254
3309
  }, []);
3255
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsx(PermissionRequests, {}), jsxRuntime.jsx("div", { className: "str-video__embedded-notifications", children: jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(SpeakingWhileMutedNotification, {}) }) }), jsxRuntime.jsx(CallHeader, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleToggleParticipants }) })) })] }), jsxRuntime.jsx(CallControls, { showParticipants: showParticipants, onToggleParticipants: handleToggleParticipants })] }));
3310
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsx(PermissionRequests, {}), jsxRuntime.jsx("div", { className: "str-video__embedded-notifications", children: jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(SpeakingWhileMutedNotification, { placement: "top" }) }) }), jsxRuntime.jsx(CallHeader, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleToggleParticipants }) })) })] }), jsxRuntime.jsx(CallControls, { showParticipants: showParticipants, onToggleParticipants: handleToggleParticipants })] }));
3256
3311
  };
3257
3312
 
3258
3313
  /**
@@ -3302,7 +3357,7 @@ const CallStateRouter = () => {
3302
3357
  const EmbeddedCall = ({ children, ...props }) => (jsxRuntime.jsxs(EmbeddedClientProvider, { ...props, children: [jsxRuntime.jsx(CallStateRouter, {}), children] }));
3303
3358
 
3304
3359
  const HostLayout = ({ isLive, isBackstageEnabled, onGoLive, onStopLive, }) => {
3305
- const { t } = videoReactBindings.useI18n();
3360
+ const { t } = useI18n();
3306
3361
  const { useParticipantCount, useCallSession } = videoReactBindings.useCallStateHooks();
3307
3362
  const participantCount = useParticipantCount();
3308
3363
  const session = useCallSession();
@@ -3317,13 +3372,15 @@ const HostLayout = ({ isLive, isBackstageEnabled, onGoLive, onStopLive, }) => {
3317
3372
  }, []);
3318
3373
  const livestreamStatus = (jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration", "aria-live": "polite", "aria-atomic": "true", children: [jsxRuntime.jsx("span", { className: isLive
3319
3374
  ? 'str-video__embedded-livestream-duration__live-badge'
3320
- : 'str-video__embedded-livestream-duration__backstage-badge', children: isLive ? t('Live') : t('Backstage') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), isLive && elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }));
3321
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call str-video__embedded-livestream", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsx(PermissionRequests, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [livestreamStatus, jsxRuntime.jsx(CancelCallConfirmButton, {})] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleCloseParticipants }) })) })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: livestreamStatus })] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('Speaker') }), jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('Microphone') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ScreenShareButton, {}) }) }), jsxRuntime.jsx(RecordCallConfirmationButton, {}), isBackstageEnabled && (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.UPDATE_CALL], children: isLive ? (jsxRuntime.jsx(WithTooltip, { title: t('End Stream'), children: jsxRuntime.jsxs("button", { type: "button", className: "str-video__embedded-end-stream-button", onClick: onStopLive, children: [jsxRuntime.jsx(Icon, { icon: "call-end" }), jsxRuntime.jsx("span", { children: t('Stop Live') })] }) })) : (jsxRuntime.jsx(WithTooltip, { title: t('Start Stream'), children: jsxRuntime.jsxs("button", { type: "button", className: "str-video__embedded-go-live-button", onClick: onGoLive, children: [jsxRuntime.jsx(Icon, { icon: "streaming" }), jsxRuntime.jsx("span", { children: t('Go Live') })] }) })) })), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('Participants'), "aria-pressed": showParticipants, onClick: handleToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] })] }));
3375
+ : 'str-video__embedded-livestream-duration__backstage-badge', children: isLive
3376
+ ? t('common.live.label', 'Live')
3377
+ : t('livestreamHost.backstage.text', 'Backstage') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), isLive && elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }));
3378
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call str-video__embedded-livestream", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsx(PermissionRequests, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [livestreamStatus, jsxRuntime.jsx(CancelCallConfirmButton, {})] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleCloseParticipants }) })) })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: livestreamStatus })] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('common.speaker.label', 'Speaker') }), jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('common.microphone.label', 'Microphone') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ScreenShareButton, {}) }) }), jsxRuntime.jsx(RecordCallConfirmationButton, {}), isBackstageEnabled && (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.UPDATE_CALL], children: isLive ? (jsxRuntime.jsx(WithTooltip, { title: t('livestreamHost.endStream.title', 'End Stream'), children: jsxRuntime.jsxs(Button, { variant: "secondary", size: "sm", className: "str-video__embedded-end-stream-button", onClick: onStopLive, children: [jsxRuntime.jsx(Icon, { icon: "call-end" }), jsxRuntime.jsx("span", { children: t('livestreamHost.endLive.label', 'End Live') })] }) })) : (jsxRuntime.jsx(WithTooltip, { title: t('livestreamHost.startStream.title', 'Start Stream'), children: jsxRuntime.jsxs(Button, { variant: "primary", size: "sm", className: "str-video__embedded-go-live-button", onClick: onGoLive, children: [jsxRuntime.jsx(Icon, { icon: "streaming" }), jsxRuntime.jsx("span", { children: t('livestreamHost.goLive.label', 'Go Live') })] }) })) })), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('common.participants.label', 'Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('common.participants.label', 'Participants'), "aria-pressed": showParticipants, onClick: handleToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] })] }));
3322
3379
  };
3323
3380
 
3324
3381
  const HostStateRouter = () => {
3325
3382
  const call = videoReactBindings.useCall();
3326
- const { t } = videoReactBindings.useI18n();
3383
+ const { t } = useI18n();
3327
3384
  const { onError } = useEmbeddedConfiguration();
3328
3385
  const { useCallCallingState, useIsCallLive, useCallSettings, useLocalParticipant, } = videoReactBindings.useCallStateHooks();
3329
3386
  const callingState = useCallCallingState();
@@ -3372,8 +3429,10 @@ const HostStateRouter = () => {
3372
3429
  if (callingState === videoClient.CallingState.IDLE ||
3373
3430
  callingState === videoClient.CallingState.UNKNOWN) {
3374
3431
  return (jsxRuntime.jsx(Lobby, { onJoin: handleJoin, title: isBackstageEnabled
3375
- ? t('Prepare your livestream')
3376
- : t('Ready to go live'), joinLabel: isBackstageEnabled ? t('Enter Backstage') : t('Go Live') }));
3432
+ ? t('livestreamHost.prepareLivestream.title', 'Prepare your livestream')
3433
+ : t('livestreamHost.readyToGoLive.title', 'Ready to go live'), joinLabel: isBackstageEnabled
3434
+ ? t('livestreamHost.enterBackstage.label', 'Enter Backstage')
3435
+ : t('livestreamHost.goLive.label', 'Go Live') }));
3377
3436
  }
3378
3437
  if (callingState === videoClient.CallingState.JOINING && !localParticipant) {
3379
3438
  return jsxRuntime.jsx(LoadingIndicator, { className: "str-video__embedded-loading" });
@@ -3392,7 +3451,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
3392
3451
  return now >= earliestJoin && now < +startsAt;
3393
3452
  };
3394
3453
  const ViewerLobby = ({ onJoin }) => {
3395
- const { t } = videoReactBindings.useI18n();
3454
+ const { t } = useI18n();
3396
3455
  const { useCallStartsAt, useCallEndedAt, useHasPermissions, useParticipantCount, useIsCallLive, useCallSettings, } = videoReactBindings.useCallStateHooks();
3397
3456
  const startsAt = useCallStartsAt();
3398
3457
  const endedAt = useCallEndedAt();
@@ -3433,9 +3492,9 @@ const ViewerLobby = ({ onJoin }) => {
3433
3492
  if (!startsAt)
3434
3493
  return null;
3435
3494
  if (startsAtPassed) {
3436
- return t('Livestream starts soon');
3495
+ return t('livestream.backstage.startsSoon.text', 'Livestream starts soon');
3437
3496
  }
3438
- return t('Livestream starts at {{ time }}', {
3497
+ return t('livestream.backstage.startsAt.text', 'Livestream starts at {{ time }}', {
3439
3498
  time: startsAt.toLocaleTimeString([], {
3440
3499
  hour: '2-digit',
3441
3500
  minute: '2-digit',
@@ -3443,13 +3502,13 @@ const ViewerLobby = ({ onJoin }) => {
3443
3502
  });
3444
3503
  };
3445
3504
  return (jsxRuntime.jsx("div", { className: "str-video__embedded-viewer-lobby", children: jsxRuntime.jsxs("div", { className: "str-video__embedded-viewer-lobby__content", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-viewer-lobby__icon", children: jsxRuntime.jsx(Icon, { icon: "streaming" }) }), jsxRuntime.jsx("h2", { className: "str-video__embedded-viewer-lobby__title", children: canJoin
3446
- ? t('Stream is ready!')
3447
- : t('Waiting for the livestream to start') }), !canJoin && getStartsAtMessage() && (jsxRuntime.jsx("p", { className: "str-video__embedded-viewer-lobby__starts-at", children: getStartsAtMessage() })), participantCount > 0 && jsxRuntime.jsx(ViewersCount, { count: participantCount }), jsxRuntime.jsx("div", { className: "str-video__embedded-viewer-lobby__actions", children: canJoin ? (jsxRuntime.jsx("button", { className: "str-video__button", onClick: onJoin, children: t('Join Stream') })) : (jsxRuntime.jsxs("label", { className: "str-video__embedded-viewer-lobby__auto-join", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoJoin, onChange: (e) => setAutoJoin(e.target.checked) }), jsxRuntime.jsx("span", { children: t('Join automatically when stream starts') })] })) })] }) }));
3505
+ ? t('livestreamViewer.lobby.streamReady.title', 'Stream is ready!')
3506
+ : t('livestreamViewer.lobby.waitingForStart.title', 'Waiting for the livestream to start') }), !canJoin && getStartsAtMessage() && (jsxRuntime.jsx("p", { className: "str-video__embedded-viewer-lobby__starts-at", children: getStartsAtMessage() })), participantCount > 0 && jsxRuntime.jsx(ViewersCount, { count: participantCount }), jsxRuntime.jsx("div", { className: "str-video__embedded-viewer-lobby__actions", children: canJoin ? (jsxRuntime.jsx(Button, { size: "sm", onClick: onJoin, children: t('livestreamViewer.lobby.joinStream.label', 'Join Stream') })) : (jsxRuntime.jsxs("label", { className: "str-video__embedded-viewer-lobby__auto-join", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoJoin, onChange: (e) => setAutoJoin(e.target.checked) }), jsxRuntime.jsx("span", { children: t('livestreamViewer.lobby.joinAutomatically.text', 'Join automatically when stream starts') })] })) })] }) }));
3448
3507
  };
3449
3508
 
3450
3509
  const ViewerLayout = () => {
3451
3510
  useWakeLock();
3452
- const { t } = videoReactBindings.useI18n();
3511
+ const { t } = useI18n();
3453
3512
  const { useParticipantCount, useCallSession } = videoReactBindings.useCallStateHooks();
3454
3513
  const participantCount = useParticipantCount();
3455
3514
  const session = useCallSession();
@@ -3462,7 +3521,7 @@ const ViewerLayout = () => {
3462
3521
  const handleToggleParticipants = react.useCallback(() => {
3463
3522
  setShowParticipants((prev) => !prev);
3464
3523
  }, []);
3465
- return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call str-video__embedded-livestream", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration", children: [jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__live-badge", children: t('Live') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }), jsxRuntime.jsx(CancelCallConfirmButton, {})] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleCloseParticipants }) })) })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration", children: [jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__live-badge", children: t('Live') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }) })] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('Speaker') }), jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('Microphone') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('Participants'), "aria-pressed": showParticipants, onClick: handleToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] })] }));
3524
+ return (jsxRuntime.jsxs("div", { className: "str-video__embedded-call str-video__embedded-livestream", children: [jsxRuntime.jsx(ConnectionNotification, {}), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-header", children: [jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration", children: [jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__live-badge", children: t('common.live.label', 'Live') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }), jsxRuntime.jsx(CancelCallConfirmButton, {})] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-layout", children: [jsxRuntime.jsx("div", { className: "str-video__embedded-layout__stage", children: jsxRuntime.jsx(LayoutComponent, { ...layoutProps }) }), jsxRuntime.jsx("div", { className: clsx('str-video__embedded-sidebar', showParticipants && 'str-video__embedded-sidebar--open'), children: showParticipants && (jsxRuntime.jsx("div", { className: "str-video__embedded-participants", children: jsxRuntime.jsx(CallParticipantsList, { onClose: handleCloseParticipants }) })) })] }), jsxRuntime.jsxs("div", { className: "str-video__embedded-call-controls str-video__call-controls", children: [jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--options", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-mobile", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsxs("div", { className: "str-video__embedded-livestream-duration", children: [jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__live-badge", children: t('common.live.label', 'Live') }), jsxRuntime.jsx(ViewersCount, { count: participantCount }), elapsed && (jsxRuntime.jsx("span", { className: "str-video__embedded-livestream-duration__elapsed", children: elapsed }))] }) })] }), jsxRuntime.jsxs("div", { className: "str-video__call-controls--group str-video__call-controls--media", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], hasPermissionsOnly: true, children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, { Menu: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DeviceSelectorAudioOutput, { visualType: "list", title: t('common.speaker.label', 'Speaker') }), jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list", title: t('common.microphone.label', 'Microphone') })] }), menuPlacement: "top" }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], hasPermissionsOnly: true, children: jsxRuntime.jsx(ToggleVideoPublishingButton, { Menu: jsxRuntime.jsx(CameraMenuWithBlur, {}), menuPlacement: "top" }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(ReactionsButton, {}) }) }), jsxRuntime.jsx("div", { className: "str-video__embedded-desktop", children: jsxRuntime.jsx(CancelCallConfirmButton, {}) })] }), jsxRuntime.jsx("div", { className: "str-video__call-controls--group str-video__call-controls--sidebar", children: jsxRuntime.jsx(WithTooltip, { title: t('common.participants.label', 'Participants'), children: jsxRuntime.jsx(CompositeButton, { active: showParticipants, "aria-label": t('common.participants.label', 'Participants'), "aria-pressed": showParticipants, onClick: handleToggleParticipants, children: jsxRuntime.jsx(Icon, { icon: "participants" }) }) }) })] })] }));
3466
3525
  };
3467
3526
 
3468
3527
  const ViewerStateRouter = () => {