tempest-react-sdk 0.77.0 → 0.79.0

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 (1623) hide show
  1. package/README.md +6 -6
  2. package/bin/lib/alias/collect.mjs +20 -3
  3. package/bin/lib/alias/index.mjs +12 -1
  4. package/bin/lib/alias/index.test.mjs +24 -0
  5. package/bin/lib/css/analyze.test.mjs +21 -1
  6. package/bin/lib/css/collect.mjs +14 -0
  7. package/bin/lib/css/extract.mjs +63 -5
  8. package/bin/lib/css/extract.test.mjs +110 -1
  9. package/bin/lib/css/fix.e2e.test.mjs +50 -0
  10. package/bin/lib/css/index.mjs +6 -1
  11. package/bin/lib/fs/contain.mjs +50 -0
  12. package/bin/lib/icons/generate.mjs +28 -3
  13. package/bin/lib/icons/generate.test.mjs +57 -1
  14. package/bin/lib/openapi/escape.mjs +20 -6
  15. package/bin/lib/openapi/generate.encode.test.mjs +120 -0
  16. package/bin/lib/openapi/generate.hostile.test.mjs +38 -9
  17. package/bin/lib/openapi/generate.mjs +203 -64
  18. package/bin/lib/openapi/generate.pagination.test.mjs +3 -2
  19. package/bin/lib/openapi/generate.test.mjs +270 -3
  20. package/bin/lib/openapi/schema-to-zod.mjs +5 -3
  21. package/bin/tempest-gen.test.mjs +188 -0
  22. package/bin/tempest.mjs +120 -44
  23. package/dist/access/Can.cjs.map +1 -1
  24. package/dist/access/Can.js.map +1 -1
  25. package/dist/access/access-control-context.cjs.map +1 -1
  26. package/dist/access/access-control-context.js.map +1 -1
  27. package/dist/access/create-role-access-control.cjs.map +1 -1
  28. package/dist/access/create-role-access-control.js.map +1 -1
  29. package/dist/access/permissions-from-token.cjs.map +1 -1
  30. package/dist/access/permissions-from-token.js.map +1 -1
  31. package/dist/access/use-can.cjs.map +1 -1
  32. package/dist/access/use-can.js.map +1 -1
  33. package/dist/app/AppProviders.cjs.map +1 -1
  34. package/dist/app/AppProviders.js.map +1 -1
  35. package/dist/audio/audio-bus.cjs.map +1 -1
  36. package/dist/audio/audio-bus.js.map +1 -1
  37. package/dist/audio/audio-output.cjs.map +1 -1
  38. package/dist/audio/audio-output.js.map +1 -1
  39. package/dist/audio/audio-player.cjs.map +1 -1
  40. package/dist/audio/audio-player.js.map +1 -1
  41. package/dist/audio/audio-recorder.cjs.map +1 -1
  42. package/dist/audio/audio-recorder.js.map +1 -1
  43. package/dist/audio/duration.cjs.map +1 -1
  44. package/dist/audio/duration.js.map +1 -1
  45. package/dist/audio/level-meter.cjs.map +1 -1
  46. package/dist/audio/level-meter.js.map +1 -1
  47. package/dist/audio/media-access.cjs.map +1 -1
  48. package/dist/audio/media-access.js.map +1 -1
  49. package/dist/audio/noise-floor.cjs.map +1 -1
  50. package/dist/audio/noise-floor.js.map +1 -1
  51. package/dist/audio/sfx-pool.cjs.map +1 -1
  52. package/dist/audio/sfx-pool.js.map +1 -1
  53. package/dist/audio/use-audio-bus.cjs.map +1 -1
  54. package/dist/audio/use-audio-bus.js.map +1 -1
  55. package/dist/audio/use-audio-recorder.cjs.map +1 -1
  56. package/dist/audio/use-audio-recorder.js.map +1 -1
  57. package/dist/audio/use-audio.cjs.map +1 -1
  58. package/dist/audio/use-audio.js.map +1 -1
  59. package/dist/audio/use-microphone.cjs +1 -1
  60. package/dist/audio/use-microphone.cjs.map +1 -1
  61. package/dist/audio/use-microphone.js +1 -5
  62. package/dist/audio/use-microphone.js.map +1 -1
  63. package/dist/audio/use-sfx-pool.cjs.map +1 -1
  64. package/dist/audio/use-sfx-pool.js.map +1 -1
  65. package/dist/audio/voice-activity.cjs.map +1 -1
  66. package/dist/audio/voice-activity.js.map +1 -1
  67. package/dist/audio/voice-chain-stages.cjs.map +1 -1
  68. package/dist/audio/voice-chain-stages.js.map +1 -1
  69. package/dist/audio/voice-chain.cjs.map +1 -1
  70. package/dist/audio/voice-chain.js.map +1 -1
  71. package/dist/audio/wav.cjs.map +1 -1
  72. package/dist/audio/wav.js.map +1 -1
  73. package/dist/auth/AuthGuard.cjs.map +1 -1
  74. package/dist/auth/AuthGuard.js.map +1 -1
  75. package/dist/auth/create-auth-store.cjs.map +1 -1
  76. package/dist/auth/create-auth-store.js.map +1 -1
  77. package/dist/auth/create-tempest-auth.cjs +1 -1
  78. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  79. package/dist/auth/create-tempest-auth.js +185 -114
  80. package/dist/auth/create-tempest-auth.js.map +1 -1
  81. package/dist/auth/jwt.cjs.map +1 -1
  82. package/dist/auth/jwt.js.map +1 -1
  83. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  84. package/dist/auth/lazy-with-retry.js.map +1 -1
  85. package/dist/auth/passkey.cjs.map +1 -1
  86. package/dist/auth/passkey.js.map +1 -1
  87. package/dist/auth/refresh-queue.cjs.map +1 -1
  88. package/dist/auth/refresh-queue.js.map +1 -1
  89. package/dist/auth/use-passkey.cjs +1 -1
  90. package/dist/auth/use-passkey.cjs.map +1 -1
  91. package/dist/auth/use-passkey.js +1 -1
  92. package/dist/auth/use-passkey.js.map +1 -1
  93. package/dist/br/BrazilMap.cjs.map +1 -1
  94. package/dist/br/BrazilMap.js.map +1 -1
  95. package/dist/br/BrazilMap.module.cjs.map +1 -1
  96. package/dist/br/BrazilMap.module.js.map +1 -1
  97. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  98. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  99. package/dist/br/BrazilStateMap.cjs.map +1 -1
  100. package/dist/br/BrazilStateMap.js.map +1 -1
  101. package/dist/br/MapLegend.cjs.map +1 -1
  102. package/dist/br/MapLegend.js.map +1 -1
  103. package/dist/br/MapLegend.module.cjs.map +1 -1
  104. package/dist/br/MapLegend.module.js.map +1 -1
  105. package/dist/br/MapMarkers.cjs.map +1 -1
  106. package/dist/br/MapMarkers.js.map +1 -1
  107. package/dist/br/MapTooltip.cjs.map +1 -1
  108. package/dist/br/MapTooltip.js.map +1 -1
  109. package/dist/br/MunicipalitySearch.cjs +1 -1
  110. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  111. package/dist/br/MunicipalitySearch.js +2 -5
  112. package/dist/br/MunicipalitySearch.js.map +1 -1
  113. package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
  114. package/dist/br/MunicipalitySearch.module.js.map +1 -1
  115. package/dist/br/PixQRCode.cjs.map +1 -1
  116. package/dist/br/PixQRCode.js.map +1 -1
  117. package/dist/br/PixQRCode.module.cjs.map +1 -1
  118. package/dist/br/PixQRCode.module.js.map +1 -1
  119. package/dist/br/boleto.cjs.map +1 -1
  120. package/dist/br/boleto.js.map +1 -1
  121. package/dist/br/br-geo.cjs.map +1 -1
  122. package/dist/br/br-geo.js.map +1 -1
  123. package/dist/br/data/br-centroids.cjs.map +1 -1
  124. package/dist/br/data/br-centroids.js.map +1 -1
  125. package/dist/br/data/br-locations.cjs.map +1 -1
  126. package/dist/br/data/br-locations.js.map +1 -1
  127. package/dist/br/data/br-uf-geo.cjs.map +1 -1
  128. package/dist/br/data/br-uf-geo.js.map +1 -1
  129. package/dist/br/data/mun/AC.cjs.map +1 -1
  130. package/dist/br/data/mun/AC.js.map +1 -1
  131. package/dist/br/data/mun/AL.cjs.map +1 -1
  132. package/dist/br/data/mun/AL.js.map +1 -1
  133. package/dist/br/data/mun/AM.cjs.map +1 -1
  134. package/dist/br/data/mun/AM.js.map +1 -1
  135. package/dist/br/data/mun/AP.cjs.map +1 -1
  136. package/dist/br/data/mun/AP.js.map +1 -1
  137. package/dist/br/data/mun/BA.cjs.map +1 -1
  138. package/dist/br/data/mun/BA.js.map +1 -1
  139. package/dist/br/data/mun/CE.cjs.map +1 -1
  140. package/dist/br/data/mun/CE.js.map +1 -1
  141. package/dist/br/data/mun/DF.cjs.map +1 -1
  142. package/dist/br/data/mun/DF.js.map +1 -1
  143. package/dist/br/data/mun/ES.cjs.map +1 -1
  144. package/dist/br/data/mun/ES.js.map +1 -1
  145. package/dist/br/data/mun/GO.cjs.map +1 -1
  146. package/dist/br/data/mun/GO.js.map +1 -1
  147. package/dist/br/data/mun/MA.cjs.map +1 -1
  148. package/dist/br/data/mun/MA.js.map +1 -1
  149. package/dist/br/data/mun/MG.cjs.map +1 -1
  150. package/dist/br/data/mun/MG.js.map +1 -1
  151. package/dist/br/data/mun/MS.cjs.map +1 -1
  152. package/dist/br/data/mun/MS.js.map +1 -1
  153. package/dist/br/data/mun/MT.cjs.map +1 -1
  154. package/dist/br/data/mun/MT.js.map +1 -1
  155. package/dist/br/data/mun/PA.cjs.map +1 -1
  156. package/dist/br/data/mun/PA.js.map +1 -1
  157. package/dist/br/data/mun/PB.cjs.map +1 -1
  158. package/dist/br/data/mun/PB.js.map +1 -1
  159. package/dist/br/data/mun/PE.cjs.map +1 -1
  160. package/dist/br/data/mun/PE.js.map +1 -1
  161. package/dist/br/data/mun/PI.cjs.map +1 -1
  162. package/dist/br/data/mun/PI.js.map +1 -1
  163. package/dist/br/data/mun/PR.cjs.map +1 -1
  164. package/dist/br/data/mun/PR.js.map +1 -1
  165. package/dist/br/data/mun/RJ.cjs.map +1 -1
  166. package/dist/br/data/mun/RJ.js.map +1 -1
  167. package/dist/br/data/mun/RN.cjs.map +1 -1
  168. package/dist/br/data/mun/RN.js.map +1 -1
  169. package/dist/br/data/mun/RO.cjs.map +1 -1
  170. package/dist/br/data/mun/RO.js.map +1 -1
  171. package/dist/br/data/mun/RR.cjs.map +1 -1
  172. package/dist/br/data/mun/RR.js.map +1 -1
  173. package/dist/br/data/mun/RS.cjs.map +1 -1
  174. package/dist/br/data/mun/RS.js.map +1 -1
  175. package/dist/br/data/mun/SC.cjs.map +1 -1
  176. package/dist/br/data/mun/SC.js.map +1 -1
  177. package/dist/br/data/mun/SE.cjs.map +1 -1
  178. package/dist/br/data/mun/SE.js.map +1 -1
  179. package/dist/br/data/mun/SP.cjs.map +1 -1
  180. package/dist/br/data/mun/SP.js.map +1 -1
  181. package/dist/br/data/mun/TO.cjs.map +1 -1
  182. package/dist/br/data/mun/TO.js.map +1 -1
  183. package/dist/br/geocode.cjs.map +1 -1
  184. package/dist/br/geocode.js.map +1 -1
  185. package/dist/br/holidays.cjs.map +1 -1
  186. package/dist/br/holidays.js.map +1 -1
  187. package/dist/br/locations.cjs.map +1 -1
  188. package/dist/br/locations.js.map +1 -1
  189. package/dist/br/nfe.cjs.map +1 -1
  190. package/dist/br/nfe.js.map +1 -1
  191. package/dist/br/pix.cjs +1 -1
  192. package/dist/br/pix.cjs.map +1 -1
  193. package/dist/br/pix.js +108 -71
  194. package/dist/br/pix.js.map +1 -1
  195. package/dist/br/regions.cjs.map +1 -1
  196. package/dist/br/regions.js.map +1 -1
  197. package/dist/br/scales.cjs.map +1 -1
  198. package/dist/br/scales.js.map +1 -1
  199. package/dist/br/state-geo.cjs.map +1 -1
  200. package/dist/br/state-geo.js.map +1 -1
  201. package/dist/br/svg-utils.cjs.map +1 -1
  202. package/dist/br/svg-utils.js.map +1 -1
  203. package/dist/br/use-map-hover.cjs.map +1 -1
  204. package/dist/br/use-map-hover.js.map +1 -1
  205. package/dist/br/use-map-zoom.cjs.map +1 -1
  206. package/dist/br/use-map-zoom.js.map +1 -1
  207. package/dist/br/whatsapp.cjs.map +1 -1
  208. package/dist/br/whatsapp.js.map +1 -1
  209. package/dist/br.cjs.map +1 -1
  210. package/dist/br.d.ts +16 -5
  211. package/dist/br.js.map +1 -1
  212. package/dist/capture/barcode.cjs.map +1 -1
  213. package/dist/capture/barcode.js.map +1 -1
  214. package/dist/capture/media-recorder.cjs.map +1 -1
  215. package/dist/capture/media-recorder.js.map +1 -1
  216. package/dist/capture/use-barcode-scanner.cjs.map +1 -1
  217. package/dist/capture/use-barcode-scanner.js.map +1 -1
  218. package/dist/capture/use-screen-capture.cjs +1 -1
  219. package/dist/capture/use-screen-capture.cjs.map +1 -1
  220. package/dist/capture/use-screen-capture.js +1 -7
  221. package/dist/capture/use-screen-capture.js.map +1 -1
  222. package/dist/capture/use-speech-recognition.cjs +1 -1
  223. package/dist/capture/use-speech-recognition.cjs.map +1 -1
  224. package/dist/capture/use-speech-recognition.js +35 -36
  225. package/dist/capture/use-speech-recognition.js.map +1 -1
  226. package/dist/capture/use-torch.cjs.map +1 -1
  227. package/dist/capture/use-torch.js.map +1 -1
  228. package/dist/capture/use-video-recorder.cjs.map +1 -1
  229. package/dist/capture/use-video-recorder.js.map +1 -1
  230. package/dist/capture/video-recorder.cjs.map +1 -1
  231. package/dist/capture/video-recorder.js.map +1 -1
  232. package/dist/charts/AreaChart.cjs.map +1 -1
  233. package/dist/charts/AreaChart.js.map +1 -1
  234. package/dist/charts/BarChart.cjs.map +1 -1
  235. package/dist/charts/BarChart.js.map +1 -1
  236. package/dist/charts/LineChart.cjs.map +1 -1
  237. package/dist/charts/LineChart.js.map +1 -1
  238. package/dist/charts/PieChart.cjs.map +1 -1
  239. package/dist/charts/PieChart.js.map +1 -1
  240. package/dist/charts/RadarChart.cjs.map +1 -1
  241. package/dist/charts/RadarChart.js.map +1 -1
  242. package/dist/charts/palette.cjs.map +1 -1
  243. package/dist/charts/palette.js.map +1 -1
  244. package/dist/charts/scales.cjs.map +1 -1
  245. package/dist/charts/scales.js.map +1 -1
  246. package/dist/charts/types.cjs.map +1 -1
  247. package/dist/charts/types.js.map +1 -1
  248. package/dist/charts/use-chart-colors.cjs.map +1 -1
  249. package/dist/charts/use-chart-colors.js.map +1 -1
  250. package/dist/components/AIChat/AIChat.cjs +1 -1
  251. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  252. package/dist/components/AIChat/AIChat.js +1 -5
  253. package/dist/components/AIChat/AIChat.js.map +1 -1
  254. package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
  255. package/dist/components/AIChat/AIChat.module.js.map +1 -1
  256. package/dist/components/AIChat/AIChatComposer.cjs +1 -1
  257. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  258. package/dist/components/AIChat/AIChatComposer.js +1 -7
  259. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  260. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
  261. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  262. package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -1
  263. package/dist/components/AIChat/ai-chat-turns.js.map +1 -1
  264. package/dist/components/Accordion/Accordion.cjs.map +1 -1
  265. package/dist/components/Accordion/Accordion.js.map +1 -1
  266. package/dist/components/Accordion/Accordion.module.cjs.map +1 -1
  267. package/dist/components/Accordion/Accordion.module.js.map +1 -1
  268. package/dist/components/Alert/Alert.cjs.map +1 -1
  269. package/dist/components/Alert/Alert.js.map +1 -1
  270. package/dist/components/Alert/Alert.module.cjs.map +1 -1
  271. package/dist/components/Alert/Alert.module.js.map +1 -1
  272. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  273. package/dist/components/AppBar/AppBar.js.map +1 -1
  274. package/dist/components/AppBar/AppBar.module.cjs.map +1 -1
  275. package/dist/components/AppBar/AppBar.module.js.map +1 -1
  276. package/dist/components/AppBar/use-sticky-body-warning.cjs +1 -1
  277. package/dist/components/AppBar/use-sticky-body-warning.cjs.map +1 -1
  278. package/dist/components/AppBar/use-sticky-body-warning.js +1 -1
  279. package/dist/components/AppBar/use-sticky-body-warning.js.map +1 -1
  280. package/dist/components/AppShell/AppShell.cjs.map +1 -1
  281. package/dist/components/AppShell/AppShell.js.map +1 -1
  282. package/dist/components/AppShell/AppShell.module.cjs.map +1 -1
  283. package/dist/components/AppShell/AppShell.module.js.map +1 -1
  284. package/dist/components/AspectRatio/AspectRatio.cjs.map +1 -1
  285. package/dist/components/AspectRatio/AspectRatio.js.map +1 -1
  286. package/dist/components/AspectRatio/AspectRatio.module.cjs.map +1 -1
  287. package/dist/components/AspectRatio/AspectRatio.module.js.map +1 -1
  288. package/dist/components/AudioPlayer/AudioPlayer.cjs +1 -1
  289. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  290. package/dist/components/AudioPlayer/AudioPlayer.js +1 -7
  291. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
  292. package/dist/components/AudioPlayer/AudioPlayer.module.cjs.map +1 -1
  293. package/dist/components/AudioPlayer/AudioPlayer.module.js.map +1 -1
  294. package/dist/components/AudioRecorder/AudioRecorder.cjs +1 -1
  295. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
  296. package/dist/components/AudioRecorder/AudioRecorder.js +16 -21
  297. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
  298. package/dist/components/AudioRecorder/AudioRecorder.module.cjs.map +1 -1
  299. package/dist/components/AudioRecorder/AudioRecorder.module.js.map +1 -1
  300. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  301. package/dist/components/Avatar/Avatar.js.map +1 -1
  302. package/dist/components/Avatar/Avatar.module.cjs.map +1 -1
  303. package/dist/components/Avatar/Avatar.module.js.map +1 -1
  304. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
  305. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
  306. package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -1
  307. package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -1
  308. package/dist/components/Badge/Badge.cjs.map +1 -1
  309. package/dist/components/Badge/Badge.js.map +1 -1
  310. package/dist/components/Badge/Badge.module.cjs.map +1 -1
  311. package/dist/components/Badge/Badge.module.js.map +1 -1
  312. package/dist/components/Banner/Banner.cjs.map +1 -1
  313. package/dist/components/Banner/Banner.js.map +1 -1
  314. package/dist/components/Banner/Banner.module.cjs.map +1 -1
  315. package/dist/components/Banner/Banner.module.js.map +1 -1
  316. package/dist/components/BarList/BarList.cjs.map +1 -1
  317. package/dist/components/BarList/BarList.js.map +1 -1
  318. package/dist/components/BarList/BarList.module.cjs.map +1 -1
  319. package/dist/components/BarList/BarList.module.js.map +1 -1
  320. package/dist/components/BarList/bar-list-model.cjs.map +1 -1
  321. package/dist/components/BarList/bar-list-model.js.map +1 -1
  322. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
  323. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
  324. package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs.map +1 -1
  325. package/dist/components/BarcodeScanner/BarcodeScanner.module.js.map +1 -1
  326. package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  327. package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
  328. package/dist/components/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
  329. package/dist/components/BottomNavigation/BottomNavigation.module.js.map +1 -1
  330. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  331. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  332. package/dist/components/BottomSheet/BottomSheet.module.cjs.map +1 -1
  333. package/dist/components/BottomSheet/BottomSheet.module.js.map +1 -1
  334. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  335. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  336. package/dist/components/Breadcrumbs/Breadcrumbs.module.cjs.map +1 -1
  337. package/dist/components/Breadcrumbs/Breadcrumbs.module.js.map +1 -1
  338. package/dist/components/Button/Button.cjs.map +1 -1
  339. package/dist/components/Button/Button.js.map +1 -1
  340. package/dist/components/Button/Button.module.cjs.map +1 -1
  341. package/dist/components/Button/Button.module.js.map +1 -1
  342. package/dist/components/Button/ButtonContent.cjs.map +1 -1
  343. package/dist/components/Button/ButtonContent.js.map +1 -1
  344. package/dist/components/Button/ButtonSlot.cjs.map +1 -1
  345. package/dist/components/Button/ButtonSlot.js.map +1 -1
  346. package/dist/components/Button/button-class.cjs.map +1 -1
  347. package/dist/components/Button/button-class.js.map +1 -1
  348. package/dist/components/Button/link-rel.cjs.map +1 -1
  349. package/dist/components/Button/link-rel.js.map +1 -1
  350. package/dist/components/Button/slot.cjs.map +1 -1
  351. package/dist/components/Button/slot.js.map +1 -1
  352. package/dist/components/Calendar/Calendar.cjs.map +1 -1
  353. package/dist/components/Calendar/Calendar.js.map +1 -1
  354. package/dist/components/Calendar/Calendar.module.cjs.map +1 -1
  355. package/dist/components/Calendar/Calendar.module.js.map +1 -1
  356. package/dist/components/Card/Card.cjs.map +1 -1
  357. package/dist/components/Card/Card.js.map +1 -1
  358. package/dist/components/Card/Card.module.cjs.map +1 -1
  359. package/dist/components/Card/Card.module.js.map +1 -1
  360. package/dist/components/Carousel/Carousel.cjs.map +1 -1
  361. package/dist/components/Carousel/Carousel.js.map +1 -1
  362. package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
  363. package/dist/components/Carousel/Carousel.module.js.map +1 -1
  364. package/dist/components/Center/Center.cjs.map +1 -1
  365. package/dist/components/Center/Center.js.map +1 -1
  366. package/dist/components/Center/Center.module.cjs.map +1 -1
  367. package/dist/components/Center/Center.module.js.map +1 -1
  368. package/dist/components/Chat/Chat.cjs.map +1 -1
  369. package/dist/components/Chat/Chat.js.map +1 -1
  370. package/dist/components/Chat/Chat.module.cjs.map +1 -1
  371. package/dist/components/Chat/Chat.module.js.map +1 -1
  372. package/dist/components/Chat/ChatActionsMenu.cjs.map +1 -1
  373. package/dist/components/Chat/ChatActionsMenu.js.map +1 -1
  374. package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
  375. package/dist/components/Chat/ChatAttachments.js.map +1 -1
  376. package/dist/components/Chat/ChatBubble.cjs.map +1 -1
  377. package/dist/components/Chat/ChatBubble.js.map +1 -1
  378. package/dist/components/Chat/ChatComposer.cjs.map +1 -1
  379. package/dist/components/Chat/ChatComposer.js.map +1 -1
  380. package/dist/components/Chat/chat-groups.cjs +1 -1
  381. package/dist/components/Chat/chat-groups.cjs.map +1 -1
  382. package/dist/components/Chat/chat-groups.js +2 -6
  383. package/dist/components/Chat/chat-groups.js.map +1 -1
  384. package/dist/components/Chat/chat-receipt.cjs.map +1 -1
  385. package/dist/components/Chat/chat-receipt.js.map +1 -1
  386. package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
  387. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  388. package/dist/components/Checkbox/Checkbox.module.cjs.map +1 -1
  389. package/dist/components/Checkbox/Checkbox.module.js.map +1 -1
  390. package/dist/components/ChipInput/ChipInput.cjs +1 -1
  391. package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
  392. package/dist/components/ChipInput/ChipInput.js +2 -12
  393. package/dist/components/ChipInput/ChipInput.js.map +1 -1
  394. package/dist/components/ChipInput/ChipInput.module.cjs.map +1 -1
  395. package/dist/components/ChipInput/ChipInput.module.js.map +1 -1
  396. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  397. package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
  398. package/dist/components/CodeBlock/CodeBlock.cjs +1 -1
  399. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
  400. package/dist/components/CodeBlock/CodeBlock.js +1 -1
  401. package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
  402. package/dist/components/CodeBlock/CodeBlock.module.cjs.map +1 -1
  403. package/dist/components/CodeBlock/CodeBlock.module.js.map +1 -1
  404. package/dist/components/CodeBlock/tokenize.cjs +1 -1
  405. package/dist/components/CodeBlock/tokenize.cjs.map +1 -1
  406. package/dist/components/CodeBlock/tokenize.js +54 -25
  407. package/dist/components/CodeBlock/tokenize.js.map +1 -1
  408. package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
  409. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  410. package/dist/components/Collapsible/Collapsible.module.cjs.map +1 -1
  411. package/dist/components/Collapsible/Collapsible.module.js.map +1 -1
  412. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  413. package/dist/components/Combobox/Combobox.js.map +1 -1
  414. package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
  415. package/dist/components/Combobox/Combobox.module.js.map +1 -1
  416. package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -1
  417. package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -1
  418. package/dist/components/Command/Command.cjs +1 -1
  419. package/dist/components/Command/Command.cjs.map +1 -1
  420. package/dist/components/Command/Command.js +4 -11
  421. package/dist/components/Command/Command.js.map +1 -1
  422. package/dist/components/Command/Command.module.cjs.map +1 -1
  423. package/dist/components/Command/Command.module.js.map +1 -1
  424. package/dist/components/ConditionalWrapper/ConditionalWrapper.cjs.map +1 -1
  425. package/dist/components/ConditionalWrapper/ConditionalWrapper.js.map +1 -1
  426. package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
  427. package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
  428. package/dist/components/ContextMenu/ContextMenu.cjs +1 -1
  429. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  430. package/dist/components/ContextMenu/ContextMenu.js +1 -1
  431. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  432. package/dist/components/ContextMenu/ContextMenu.module.cjs.map +1 -1
  433. package/dist/components/ContextMenu/ContextMenu.module.js.map +1 -1
  434. package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
  435. package/dist/components/CopyButton/CopyButton.js.map +1 -1
  436. package/dist/components/CopyButton/CopyButton.module.cjs.map +1 -1
  437. package/dist/components/CopyButton/CopyButton.module.js.map +1 -1
  438. package/dist/components/DataList/DataList.cjs.map +1 -1
  439. package/dist/components/DataList/DataList.js.map +1 -1
  440. package/dist/components/DataList/DataList.module.cjs.map +1 -1
  441. package/dist/components/DataList/DataList.module.js.map +1 -1
  442. package/dist/components/DataTable/DataTable.cjs +1 -1
  443. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  444. package/dist/components/DataTable/DataTable.js +6 -14
  445. package/dist/components/DataTable/DataTable.js.map +1 -1
  446. package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
  447. package/dist/components/DataTable/DataTable.module.js.map +1 -1
  448. package/dist/components/DataTable/EditableCell.cjs +1 -1
  449. package/dist/components/DataTable/EditableCell.cjs.map +1 -1
  450. package/dist/components/DataTable/EditableCell.js +1 -9
  451. package/dist/components/DataTable/EditableCell.js.map +1 -1
  452. package/dist/components/DataTable/LoadingRows.cjs.map +1 -1
  453. package/dist/components/DataTable/LoadingRows.js.map +1 -1
  454. package/dist/components/DataTable/edit-labels.cjs.map +1 -1
  455. package/dist/components/DataTable/edit-labels.js.map +1 -1
  456. package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
  457. package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
  458. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  459. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  460. package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
  461. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  462. package/dist/components/DateRangePicker/DateRangePicker.module.cjs.map +1 -1
  463. package/dist/components/DateRangePicker/DateRangePicker.module.js.map +1 -1
  464. package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
  465. package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
  466. package/dist/components/DescriptionList/DescriptionList.module.cjs.map +1 -1
  467. package/dist/components/DescriptionList/DescriptionList.module.js.map +1 -1
  468. package/dist/components/Divider/Divider.cjs.map +1 -1
  469. package/dist/components/Divider/Divider.js.map +1 -1
  470. package/dist/components/Divider/Divider.module.cjs.map +1 -1
  471. package/dist/components/Divider/Divider.module.js.map +1 -1
  472. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  473. package/dist/components/Drawer/Drawer.js.map +1 -1
  474. package/dist/components/Drawer/Drawer.module.cjs.map +1 -1
  475. package/dist/components/Drawer/Drawer.module.js.map +1 -1
  476. package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
  477. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  478. package/dist/components/DropdownMenu/DropdownMenu.js +2 -14
  479. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  480. package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
  481. package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
  482. package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
  483. package/dist/components/Dropzone/Dropzone.js.map +1 -1
  484. package/dist/components/Dropzone/Dropzone.module.cjs.map +1 -1
  485. package/dist/components/Dropzone/Dropzone.module.js.map +1 -1
  486. package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
  487. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  488. package/dist/components/EmptyState/EmptyState.module.cjs.map +1 -1
  489. package/dist/components/EmptyState/EmptyState.module.js.map +1 -1
  490. package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
  491. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  492. package/dist/components/ErrorState/ErrorState.module.cjs.map +1 -1
  493. package/dist/components/ErrorState/ErrorState.module.js.map +1 -1
  494. package/dist/components/ErrorText/ErrorText.cjs.map +1 -1
  495. package/dist/components/ErrorText/ErrorText.js.map +1 -1
  496. package/dist/components/ErrorText/ErrorText.module.cjs.map +1 -1
  497. package/dist/components/ErrorText/ErrorText.module.js.map +1 -1
  498. package/dist/components/FileUpload/FileUpload.cjs +1 -1
  499. package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
  500. package/dist/components/FileUpload/FileUpload.js +7 -17
  501. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  502. package/dist/components/FileUpload/FileUpload.module.cjs.map +1 -1
  503. package/dist/components/FileUpload/FileUpload.module.js.map +1 -1
  504. package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
  505. package/dist/components/FilterBar/FilterBar.js.map +1 -1
  506. package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -1
  507. package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
  508. package/dist/components/FilterBar/filter-apply.cjs.map +1 -1
  509. package/dist/components/FilterBar/filter-apply.js.map +1 -1
  510. package/dist/components/FilterBar/filter-model.cjs.map +1 -1
  511. package/dist/components/FilterBar/filter-model.js.map +1 -1
  512. package/dist/components/FilterBar/filter-query.cjs +1 -1
  513. package/dist/components/FilterBar/filter-query.cjs.map +1 -1
  514. package/dist/components/FilterBar/filter-query.js +2 -5
  515. package/dist/components/FilterBar/filter-query.js.map +1 -1
  516. package/dist/components/FilterPanel/FilterPanel.cjs.map +1 -1
  517. package/dist/components/FilterPanel/FilterPanel.js.map +1 -1
  518. package/dist/components/FilterPanel/FilterPanel.module.cjs.map +1 -1
  519. package/dist/components/FilterPanel/FilterPanel.module.js.map +1 -1
  520. package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
  521. package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
  522. package/dist/components/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
  523. package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
  524. package/dist/components/For/For.cjs.map +1 -1
  525. package/dist/components/For/For.js.map +1 -1
  526. package/dist/components/Form/Form.cjs.map +1 -1
  527. package/dist/components/Form/Form.js.map +1 -1
  528. package/dist/components/Form/Form.module.cjs.map +1 -1
  529. package/dist/components/Form/Form.module.js.map +1 -1
  530. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  531. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  532. package/dist/components/HoverCard/HoverCard.module.cjs.map +1 -1
  533. package/dist/components/HoverCard/HoverCard.module.js.map +1 -1
  534. package/dist/components/Image/Image.cjs.map +1 -1
  535. package/dist/components/Image/Image.js.map +1 -1
  536. package/dist/components/Image/Image.module.cjs.map +1 -1
  537. package/dist/components/Image/Image.module.js.map +1 -1
  538. package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
  539. package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
  540. package/dist/components/ImageCropper/ImageCropper.js +4 -8
  541. package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
  542. package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
  543. package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
  544. package/dist/components/ImageCropper/crop-geometry.cjs.map +1 -1
  545. package/dist/components/ImageCropper/crop-geometry.js.map +1 -1
  546. package/dist/components/Input/Input.cjs.map +1 -1
  547. package/dist/components/Input/Input.js.map +1 -1
  548. package/dist/components/Input/Input.module.cjs.map +1 -1
  549. package/dist/components/Input/Input.module.js.map +1 -1
  550. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  551. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  552. package/dist/components/InstallBanner/InstallBanner.module.cjs.map +1 -1
  553. package/dist/components/InstallBanner/InstallBanner.module.js.map +1 -1
  554. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  555. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  556. package/dist/components/InstallButton/InstallButton.module.cjs.map +1 -1
  557. package/dist/components/InstallButton/InstallButton.module.js.map +1 -1
  558. package/dist/components/InstallButton/install-hints.cjs.map +1 -1
  559. package/dist/components/InstallButton/install-hints.js.map +1 -1
  560. package/dist/components/Kanban/Kanban.cjs.map +1 -1
  561. package/dist/components/Kanban/Kanban.js.map +1 -1
  562. package/dist/components/Kanban/Kanban.module.cjs.map +1 -1
  563. package/dist/components/Kanban/Kanban.module.js.map +1 -1
  564. package/dist/components/Kbd/Kbd.cjs.map +1 -1
  565. package/dist/components/Kbd/Kbd.js.map +1 -1
  566. package/dist/components/Kbd/Kbd.module.cjs.map +1 -1
  567. package/dist/components/Kbd/Kbd.module.js.map +1 -1
  568. package/dist/components/Label/Label.cjs.map +1 -1
  569. package/dist/components/Label/Label.js.map +1 -1
  570. package/dist/components/Label/Label.module.cjs.map +1 -1
  571. package/dist/components/Label/Label.module.js.map +1 -1
  572. package/dist/components/Layout/Layout.cjs.map +1 -1
  573. package/dist/components/Layout/Layout.js.map +1 -1
  574. package/dist/components/Layout/Layout.module.cjs.map +1 -1
  575. package/dist/components/Layout/Layout.module.js.map +1 -1
  576. package/dist/components/Lightbox/Lightbox.cjs +1 -1
  577. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  578. package/dist/components/Lightbox/Lightbox.js +1 -1
  579. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  580. package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -1
  581. package/dist/components/Lightbox/Lightbox.module.js.map +1 -1
  582. package/dist/components/ListTile/ListTile.cjs.map +1 -1
  583. package/dist/components/ListTile/ListTile.js.map +1 -1
  584. package/dist/components/ListTile/ListTile.module.cjs.map +1 -1
  585. package/dist/components/ListTile/ListTile.module.js.map +1 -1
  586. package/dist/components/Markdown/Markdown.cjs.map +1 -1
  587. package/dist/components/Markdown/Markdown.js.map +1 -1
  588. package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
  589. package/dist/components/Markdown/Markdown.module.js.map +1 -1
  590. package/dist/components/Markdown/markdown-parse.cjs +6 -6
  591. package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
  592. package/dist/components/Markdown/markdown-parse.js +94 -96
  593. package/dist/components/Markdown/markdown-parse.js.map +1 -1
  594. package/dist/components/Masonry/Masonry.cjs.map +1 -1
  595. package/dist/components/Masonry/Masonry.js.map +1 -1
  596. package/dist/components/Masonry/Masonry.module.cjs.map +1 -1
  597. package/dist/components/Masonry/Masonry.module.js.map +1 -1
  598. package/dist/components/Masonry/masonry-layout.cjs.map +1 -1
  599. package/dist/components/Masonry/masonry-layout.js.map +1 -1
  600. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  601. package/dist/components/Menubar/Menubar.js.map +1 -1
  602. package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
  603. package/dist/components/Menubar/Menubar.module.js.map +1 -1
  604. package/dist/components/Modal/Modal.cjs.map +1 -1
  605. package/dist/components/Modal/Modal.js.map +1 -1
  606. package/dist/components/Modal/Modal.module.cjs.map +1 -1
  607. package/dist/components/Modal/Modal.module.js.map +1 -1
  608. package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
  609. package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
  610. package/dist/components/ModalsManager/ModalsManager.module.cjs.map +1 -1
  611. package/dist/components/ModalsManager/ModalsManager.module.js.map +1 -1
  612. package/dist/components/Money/Money.cjs.map +1 -1
  613. package/dist/components/Money/Money.js.map +1 -1
  614. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  615. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  616. package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
  617. package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
  618. package/dist/components/NProgress/NProgress.cjs.map +1 -1
  619. package/dist/components/NProgress/NProgress.js.map +1 -1
  620. package/dist/components/NProgress/NProgress.module.cjs.map +1 -1
  621. package/dist/components/NProgress/NProgress.module.js.map +1 -1
  622. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  623. package/dist/components/Navbar/Navbar.js.map +1 -1
  624. package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
  625. package/dist/components/Navbar/Navbar.module.js.map +1 -1
  626. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  627. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  628. package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
  629. package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
  630. package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
  631. package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
  632. package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
  633. package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
  634. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
  635. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  636. package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -1
  637. package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
  638. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
  639. package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -1
  640. package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
  641. package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
  642. package/dist/components/OfflineIndicator/OfflineIndicator.module.cjs.map +1 -1
  643. package/dist/components/OfflineIndicator/OfflineIndicator.module.js.map +1 -1
  644. package/dist/components/Page/Page.cjs.map +1 -1
  645. package/dist/components/Page/Page.js.map +1 -1
  646. package/dist/components/Page/Page.module.cjs.map +1 -1
  647. package/dist/components/Page/Page.module.js.map +1 -1
  648. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  649. package/dist/components/Pagination/Pagination.js.map +1 -1
  650. package/dist/components/Pagination/Pagination.module.cjs.map +1 -1
  651. package/dist/components/Pagination/Pagination.module.js.map +1 -1
  652. package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
  653. package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
  654. package/dist/components/PasswordInput/PasswordInput.module.cjs.map +1 -1
  655. package/dist/components/PasswordInput/PasswordInput.module.js.map +1 -1
  656. package/dist/components/PinInput/PinInput.cjs.map +1 -1
  657. package/dist/components/PinInput/PinInput.js.map +1 -1
  658. package/dist/components/PinInput/PinInput.module.cjs.map +1 -1
  659. package/dist/components/PinInput/PinInput.module.js.map +1 -1
  660. package/dist/components/Popover/Popover.cjs.map +1 -1
  661. package/dist/components/Popover/Popover.js.map +1 -1
  662. package/dist/components/Popover/Popover.module.cjs.map +1 -1
  663. package/dist/components/Popover/Popover.module.js.map +1 -1
  664. package/dist/components/Portal/Portal.cjs.map +1 -1
  665. package/dist/components/Portal/Portal.js.map +1 -1
  666. package/dist/components/Portal/anchor-position.cjs.map +1 -1
  667. package/dist/components/Portal/anchor-position.js.map +1 -1
  668. package/dist/components/Portal/escape-layer.cjs.map +1 -1
  669. package/dist/components/Portal/escape-layer.js.map +1 -1
  670. package/dist/components/Portal/portal-host.cjs.map +1 -1
  671. package/dist/components/Portal/portal-host.js.map +1 -1
  672. package/dist/components/Portal/tab-bridge.cjs +1 -1
  673. package/dist/components/Portal/tab-bridge.cjs.map +1 -1
  674. package/dist/components/Portal/tab-bridge.js +1 -5
  675. package/dist/components/Portal/tab-bridge.js.map +1 -1
  676. package/dist/components/Progress/Progress.cjs.map +1 -1
  677. package/dist/components/Progress/Progress.js.map +1 -1
  678. package/dist/components/Progress/Progress.module.cjs.map +1 -1
  679. package/dist/components/Progress/Progress.module.js.map +1 -1
  680. package/dist/components/QRCode/QRCode.cjs.map +1 -1
  681. package/dist/components/QRCode/QRCode.js.map +1 -1
  682. package/dist/components/QRCode/QRCode.module.cjs.map +1 -1
  683. package/dist/components/QRCode/QRCode.module.js.map +1 -1
  684. package/dist/components/QRCode/qr-encode.cjs +1 -1
  685. package/dist/components/QRCode/qr-encode.cjs.map +1 -1
  686. package/dist/components/QRCode/qr-encode.js +7 -13
  687. package/dist/components/QRCode/qr-encode.js.map +1 -1
  688. package/dist/components/QRCode/qr-tables.cjs.map +1 -1
  689. package/dist/components/QRCode/qr-tables.js.map +1 -1
  690. package/dist/components/Radio/Radio.cjs.map +1 -1
  691. package/dist/components/Radio/Radio.js.map +1 -1
  692. package/dist/components/Radio/Radio.module.cjs.map +1 -1
  693. package/dist/components/Radio/Radio.module.js.map +1 -1
  694. package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
  695. package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
  696. package/dist/components/RangeSlider/RangeSlider.module.cjs.map +1 -1
  697. package/dist/components/RangeSlider/RangeSlider.module.js.map +1 -1
  698. package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
  699. package/dist/components/RatingStars/RatingStars.js.map +1 -1
  700. package/dist/components/RatingStars/RatingStars.module.cjs.map +1 -1
  701. package/dist/components/RatingStars/RatingStars.module.js.map +1 -1
  702. package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
  703. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  704. package/dist/components/RefreshIndicator/RefreshIndicator.js +10 -18
  705. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  706. package/dist/components/RefreshIndicator/RefreshIndicator.module.cjs.map +1 -1
  707. package/dist/components/RefreshIndicator/RefreshIndicator.module.js.map +1 -1
  708. package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
  709. package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
  710. package/dist/components/Resizable/Resizable.cjs +1 -1
  711. package/dist/components/Resizable/Resizable.cjs.map +1 -1
  712. package/dist/components/Resizable/Resizable.js +1 -9
  713. package/dist/components/Resizable/Resizable.js.map +1 -1
  714. package/dist/components/Resizable/Resizable.module.cjs.map +1 -1
  715. package/dist/components/Resizable/Resizable.module.js.map +1 -1
  716. package/dist/components/Responsive/Responsive.cjs.map +1 -1
  717. package/dist/components/Responsive/Responsive.js.map +1 -1
  718. package/dist/components/Reveal/Reveal.cjs.map +1 -1
  719. package/dist/components/Reveal/Reveal.js.map +1 -1
  720. package/dist/components/Reveal/Reveal.module.cjs.map +1 -1
  721. package/dist/components/Reveal/Reveal.module.js.map +1 -1
  722. package/dist/components/Reveal/reveal-threshold.cjs.map +1 -1
  723. package/dist/components/Reveal/reveal-threshold.js.map +1 -1
  724. package/dist/components/SafeArea/SafeArea.cjs.map +1 -1
  725. package/dist/components/SafeArea/SafeArea.js.map +1 -1
  726. package/dist/components/SafeArea/SafeArea.module.cjs.map +1 -1
  727. package/dist/components/SafeArea/SafeArea.module.js.map +1 -1
  728. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  729. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  730. package/dist/components/Scheduler/Scheduler.module.cjs.map +1 -1
  731. package/dist/components/Scheduler/Scheduler.module.js.map +1 -1
  732. package/dist/components/Scheduler/scheduler-layout.cjs.map +1 -1
  733. package/dist/components/Scheduler/scheduler-layout.js.map +1 -1
  734. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  735. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  736. package/dist/components/ScrollArea/ScrollArea.module.cjs.map +1 -1
  737. package/dist/components/ScrollArea/ScrollArea.module.js.map +1 -1
  738. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  739. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  740. package/dist/components/SearchBar/SearchBar.module.cjs.map +1 -1
  741. package/dist/components/SearchBar/SearchBar.module.js.map +1 -1
  742. package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
  743. package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
  744. package/dist/components/SectionHeader/SectionHeader.module.cjs.map +1 -1
  745. package/dist/components/SectionHeader/SectionHeader.module.js.map +1 -1
  746. package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  747. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  748. package/dist/components/SegmentedControl/SegmentedControl.module.cjs.map +1 -1
  749. package/dist/components/SegmentedControl/SegmentedControl.module.js.map +1 -1
  750. package/dist/components/Select/CaretIcon.cjs.map +1 -1
  751. package/dist/components/Select/CaretIcon.js.map +1 -1
  752. package/dist/components/Select/Select.cjs.map +1 -1
  753. package/dist/components/Select/Select.js.map +1 -1
  754. package/dist/components/Select/Select.module.cjs.map +1 -1
  755. package/dist/components/Select/Select.module.js.map +1 -1
  756. package/dist/components/Select/options.cjs.map +1 -1
  757. package/dist/components/Select/options.js.map +1 -1
  758. package/dist/components/Sidebar/Sidebar.cjs +1 -1
  759. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  760. package/dist/components/Sidebar/Sidebar.js +13 -23
  761. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  762. package/dist/components/Sidebar/Sidebar.module.cjs.map +1 -1
  763. package/dist/components/Sidebar/Sidebar.module.js.map +1 -1
  764. package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
  765. package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
  766. package/dist/components/Sidebar/active-nav-key.cjs.map +1 -1
  767. package/dist/components/Sidebar/active-nav-key.js.map +1 -1
  768. package/dist/components/SignaturePad/SignaturePad.cjs +1 -1
  769. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
  770. package/dist/components/SignaturePad/SignaturePad.js +1 -5
  771. package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
  772. package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -1
  773. package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -1
  774. package/dist/components/Skeleton/Skeleton.cjs.map +1 -1
  775. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  776. package/dist/components/Skeleton/Skeleton.module.cjs.map +1 -1
  777. package/dist/components/Skeleton/Skeleton.module.js.map +1 -1
  778. package/dist/components/Slider/Slider.cjs.map +1 -1
  779. package/dist/components/Slider/Slider.js.map +1 -1
  780. package/dist/components/Slider/Slider.module.cjs.map +1 -1
  781. package/dist/components/Slider/Slider.module.js.map +1 -1
  782. package/dist/components/Spacer/Spacer.cjs.map +1 -1
  783. package/dist/components/Spacer/Spacer.js.map +1 -1
  784. package/dist/components/Spacer/Spacer.module.cjs.map +1 -1
  785. package/dist/components/Spacer/Spacer.module.js.map +1 -1
  786. package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
  787. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  788. package/dist/components/Sparkline/Sparkline.module.cjs.map +1 -1
  789. package/dist/components/Sparkline/Sparkline.module.js.map +1 -1
  790. package/dist/components/Sparkline/sparkline-geometry.cjs.map +1 -1
  791. package/dist/components/Sparkline/sparkline-geometry.js.map +1 -1
  792. package/dist/components/Spinner/Spinner.cjs.map +1 -1
  793. package/dist/components/Spinner/Spinner.js.map +1 -1
  794. package/dist/components/Spinner/Spinner.module.cjs.map +1 -1
  795. package/dist/components/Spinner/Spinner.module.js.map +1 -1
  796. package/dist/components/Stat/Stat.cjs.map +1 -1
  797. package/dist/components/Stat/Stat.js.map +1 -1
  798. package/dist/components/Stat/Stat.module.cjs.map +1 -1
  799. package/dist/components/Stat/Stat.module.js.map +1 -1
  800. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  801. package/dist/components/Stepper/Stepper.js.map +1 -1
  802. package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
  803. package/dist/components/Stepper/Stepper.module.js.map +1 -1
  804. package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
  805. package/dist/components/StepperInput/StepperInput.js.map +1 -1
  806. package/dist/components/StepperInput/StepperInput.module.cjs.map +1 -1
  807. package/dist/components/StepperInput/StepperInput.module.js.map +1 -1
  808. package/dist/components/Switch/Switch.cjs.map +1 -1
  809. package/dist/components/Switch/Switch.js.map +1 -1
  810. package/dist/components/Switch/Switch.module.cjs.map +1 -1
  811. package/dist/components/Switch/Switch.module.js.map +1 -1
  812. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  813. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  814. package/dist/components/SyncStatusBadge/SyncStatusBadge.module.cjs.map +1 -1
  815. package/dist/components/SyncStatusBadge/SyncStatusBadge.module.js.map +1 -1
  816. package/dist/components/Table/Table.cjs.map +1 -1
  817. package/dist/components/Table/Table.js.map +1 -1
  818. package/dist/components/Table/Table.module.cjs.map +1 -1
  819. package/dist/components/Table/Table.module.js.map +1 -1
  820. package/dist/components/Tabs/Tabs.cjs.map +1 -1
  821. package/dist/components/Tabs/Tabs.js.map +1 -1
  822. package/dist/components/Tabs/Tabs.module.cjs.map +1 -1
  823. package/dist/components/Tabs/Tabs.module.js.map +1 -1
  824. package/dist/components/Tag/Tag.cjs.map +1 -1
  825. package/dist/components/Tag/Tag.js.map +1 -1
  826. package/dist/components/Tag/Tag.module.cjs.map +1 -1
  827. package/dist/components/Tag/Tag.module.js.map +1 -1
  828. package/dist/components/Textarea/Textarea.cjs.map +1 -1
  829. package/dist/components/Textarea/Textarea.js.map +1 -1
  830. package/dist/components/Textarea/Textarea.module.cjs.map +1 -1
  831. package/dist/components/Textarea/Textarea.module.js.map +1 -1
  832. package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
  833. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  834. package/dist/components/TimePicker/TimePicker.module.cjs.map +1 -1
  835. package/dist/components/TimePicker/TimePicker.module.js.map +1 -1
  836. package/dist/components/Timeline/Timeline.cjs.map +1 -1
  837. package/dist/components/Timeline/Timeline.js.map +1 -1
  838. package/dist/components/Timeline/Timeline.module.cjs.map +1 -1
  839. package/dist/components/Timeline/Timeline.module.js.map +1 -1
  840. package/dist/components/Toast/Toast.module.cjs.map +1 -1
  841. package/dist/components/Toast/Toast.module.js.map +1 -1
  842. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  843. package/dist/components/Toast/ToastProvider.js.map +1 -1
  844. package/dist/components/Toggle/Toggle.cjs.map +1 -1
  845. package/dist/components/Toggle/Toggle.js.map +1 -1
  846. package/dist/components/Toggle/Toggle.module.cjs.map +1 -1
  847. package/dist/components/Toggle/Toggle.module.js.map +1 -1
  848. package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
  849. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  850. package/dist/components/ToggleGroup/ToggleGroup.module.cjs.map +1 -1
  851. package/dist/components/ToggleGroup/ToggleGroup.module.js.map +1 -1
  852. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  853. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  854. package/dist/components/Tooltip/Tooltip.module.cjs.map +1 -1
  855. package/dist/components/Tooltip/Tooltip.module.js.map +1 -1
  856. package/dist/components/Tour/Tour.cjs +1 -1
  857. package/dist/components/Tour/Tour.cjs.map +1 -1
  858. package/dist/components/Tour/Tour.js +1 -5
  859. package/dist/components/Tour/Tour.js.map +1 -1
  860. package/dist/components/Tour/Tour.module.cjs.map +1 -1
  861. package/dist/components/Tour/Tour.module.js.map +1 -1
  862. package/dist/components/Tour/tour-position.cjs.map +1 -1
  863. package/dist/components/Tour/tour-position.js.map +1 -1
  864. package/dist/components/Transfer/Transfer.cjs.map +1 -1
  865. package/dist/components/Transfer/Transfer.js.map +1 -1
  866. package/dist/components/Transfer/Transfer.module.cjs.map +1 -1
  867. package/dist/components/Transfer/Transfer.module.js.map +1 -1
  868. package/dist/components/Transfer/transfer-state.cjs.map +1 -1
  869. package/dist/components/Transfer/transfer-state.js.map +1 -1
  870. package/dist/components/TreeView/TreeView.cjs.map +1 -1
  871. package/dist/components/TreeView/TreeView.js.map +1 -1
  872. package/dist/components/TreeView/TreeView.module.cjs.map +1 -1
  873. package/dist/components/TreeView/TreeView.module.js.map +1 -1
  874. package/dist/components/TruncateText/TruncateText.cjs.map +1 -1
  875. package/dist/components/TruncateText/TruncateText.js.map +1 -1
  876. package/dist/components/TruncateText/TruncateText.module.cjs.map +1 -1
  877. package/dist/components/TruncateText/TruncateText.module.js.map +1 -1
  878. package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
  879. package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
  880. package/dist/components/UpdatePrompt/UpdatePrompt.module.cjs.map +1 -1
  881. package/dist/components/UpdatePrompt/UpdatePrompt.module.js.map +1 -1
  882. package/dist/components/VideoPlayer/VideoPlayer.cjs +1 -1
  883. package/dist/components/VideoPlayer/VideoPlayer.cjs.map +1 -1
  884. package/dist/components/VideoPlayer/VideoPlayer.js +1 -7
  885. package/dist/components/VideoPlayer/VideoPlayer.js.map +1 -1
  886. package/dist/components/VideoPlayer/VideoPlayer.module.cjs.map +1 -1
  887. package/dist/components/VideoPlayer/VideoPlayer.module.js.map +1 -1
  888. package/dist/components/VideoPlayer/playback-rates.cjs.map +1 -1
  889. package/dist/components/VideoPlayer/playback-rates.js.map +1 -1
  890. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  891. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  892. package/dist/components/VirtualList/VirtualList.module.cjs.map +1 -1
  893. package/dist/components/VirtualList/VirtualList.module.js.map +1 -1
  894. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  895. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  896. package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -1
  897. package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
  898. package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
  899. package/dist/components/VisuallyHidden/VisuallyHidden.js.map +1 -1
  900. package/dist/components/VisuallyHidden/VisuallyHidden.module.cjs.map +1 -1
  901. package/dist/components/VisuallyHidden/VisuallyHidden.module.js.map +1 -1
  902. package/dist/components/Wizard/Wizard.cjs +1 -1
  903. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  904. package/dist/components/Wizard/Wizard.js +4 -11
  905. package/dist/components/Wizard/Wizard.js.map +1 -1
  906. package/dist/components/Wizard/Wizard.module.cjs.map +1 -1
  907. package/dist/components/Wizard/Wizard.module.js.map +1 -1
  908. package/dist/data/create-data-provider.cjs.map +1 -1
  909. package/dist/data/create-data-provider.js.map +1 -1
  910. package/dist/data/data-provider-context.cjs.map +1 -1
  911. package/dist/data/data-provider-context.js.map +1 -1
  912. package/dist/data/use-resource.cjs.map +1 -1
  913. package/dist/data/use-resource.js.map +1 -1
  914. package/dist/editor/RichTextEditor.cjs.map +1 -1
  915. package/dist/editor/RichTextEditor.js.map +1 -1
  916. package/dist/editor/RichTextEditor.module.cjs.map +1 -1
  917. package/dist/editor/RichTextEditor.module.js.map +1 -1
  918. package/dist/editor.js +1 -2
  919. package/dist/error-boundary/ErrorBoundary.cjs.map +1 -1
  920. package/dist/error-boundary/ErrorBoundary.js.map +1 -1
  921. package/dist/error-boundary/use-error-handler.cjs.map +1 -1
  922. package/dist/error-boundary/use-error-handler.js.map +1 -1
  923. package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
  924. package/dist/feature-flags/FeatureFlagsProvider.js.map +1 -1
  925. package/dist/feature-flags/growthbook-adapter.cjs.map +1 -1
  926. package/dist/feature-flags/growthbook-adapter.js.map +1 -1
  927. package/dist/feature-flags/in-memory-adapter.cjs.map +1 -1
  928. package/dist/feature-flags/in-memory-adapter.js.map +1 -1
  929. package/dist/feature-flags/launchdarkly-adapter.cjs.map +1 -1
  930. package/dist/feature-flags/launchdarkly-adapter.js.map +1 -1
  931. package/dist/forms/FormField.cjs.map +1 -1
  932. package/dist/forms/FormField.js.map +1 -1
  933. package/dist/forms/br-validators.cjs.map +1 -1
  934. package/dist/forms/br-validators.js.map +1 -1
  935. package/dist/forms/masked-inputs.cjs.map +1 -1
  936. package/dist/forms/masked-inputs.js.map +1 -1
  937. package/dist/forms/use-viacep.cjs.map +1 -1
  938. package/dist/forms/use-viacep.js.map +1 -1
  939. package/dist/forms/use-zod-form.cjs.map +1 -1
  940. package/dist/forms/use-zod-form.js.map +1 -1
  941. package/dist/forms/validate-form.cjs.map +1 -1
  942. package/dist/forms/validate-form.js.map +1 -1
  943. package/dist/forms/zod-resolver.cjs.map +1 -1
  944. package/dist/forms/zod-resolver.js.map +1 -1
  945. package/dist/geo/TrajectoryMap.cjs.map +1 -1
  946. package/dist/geo/TrajectoryMap.js.map +1 -1
  947. package/dist/geo/TrajectoryMap.module.cjs.map +1 -1
  948. package/dist/geo/TrajectoryMap.module.js.map +1 -1
  949. package/dist/geo/bounds.cjs.map +1 -1
  950. package/dist/geo/bounds.js.map +1 -1
  951. package/dist/geo/create-position-tracker.cjs +1 -1
  952. package/dist/geo/create-position-tracker.cjs.map +1 -1
  953. package/dist/geo/create-position-tracker.js +1 -7
  954. package/dist/geo/create-position-tracker.js.map +1 -1
  955. package/dist/geo/distance.cjs.map +1 -1
  956. package/dist/geo/distance.js.map +1 -1
  957. package/dist/geo/estimate.cjs.map +1 -1
  958. package/dist/geo/estimate.js.map +1 -1
  959. package/dist/geo/leaflet-map.cjs.map +1 -1
  960. package/dist/geo/leaflet-map.js.map +1 -1
  961. package/dist/geo/projection.cjs.map +1 -1
  962. package/dist/geo/projection.js.map +1 -1
  963. package/dist/geo/routing.cjs.map +1 -1
  964. package/dist/geo/routing.js.map +1 -1
  965. package/dist/geo/types.cjs.map +1 -1
  966. package/dist/geo/types.js.map +1 -1
  967. package/dist/geo/use-position-tracker.cjs.map +1 -1
  968. package/dist/geo/use-position-tracker.js.map +1 -1
  969. package/dist/hooks/pwa-env.cjs.map +1 -1
  970. package/dist/hooks/pwa-env.js.map +1 -1
  971. package/dist/hooks/tab-sequence.cjs.map +1 -1
  972. package/dist/hooks/tab-sequence.js.map +1 -1
  973. package/dist/hooks/use-announce.cjs +1 -1
  974. package/dist/hooks/use-announce.cjs.map +1 -1
  975. package/dist/hooks/use-announce.js +2 -2
  976. package/dist/hooks/use-announce.js.map +1 -1
  977. package/dist/hooks/use-async.cjs.map +1 -1
  978. package/dist/hooks/use-async.js.map +1 -1
  979. package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
  980. package/dist/hooks/use-before-install-prompt.js.map +1 -1
  981. package/dist/hooks/use-breakpoint.cjs.map +1 -1
  982. package/dist/hooks/use-breakpoint.js.map +1 -1
  983. package/dist/hooks/use-click-outside.cjs.map +1 -1
  984. package/dist/hooks/use-click-outside.js.map +1 -1
  985. package/dist/hooks/use-client-filter.cjs.map +1 -1
  986. package/dist/hooks/use-client-filter.js.map +1 -1
  987. package/dist/hooks/use-clipboard.cjs.map +1 -1
  988. package/dist/hooks/use-clipboard.js.map +1 -1
  989. package/dist/hooks/use-countdown.cjs.map +1 -1
  990. package/dist/hooks/use-countdown.js.map +1 -1
  991. package/dist/hooks/use-counter.cjs.map +1 -1
  992. package/dist/hooks/use-counter.js.map +1 -1
  993. package/dist/hooks/use-debounce.cjs.map +1 -1
  994. package/dist/hooks/use-debounce.js.map +1 -1
  995. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  996. package/dist/hooks/use-deep-memo.js.map +1 -1
  997. package/dist/hooks/use-disclosure.cjs.map +1 -1
  998. package/dist/hooks/use-disclosure.js.map +1 -1
  999. package/dist/hooks/use-document-title.cjs.map +1 -1
  1000. package/dist/hooks/use-document-title.js.map +1 -1
  1001. package/dist/hooks/use-document-visibility.cjs.map +1 -1
  1002. package/dist/hooks/use-document-visibility.js.map +1 -1
  1003. package/dist/hooks/use-draft-filters.cjs.map +1 -1
  1004. package/dist/hooks/use-draft-filters.js.map +1 -1
  1005. package/dist/hooks/use-event-listener.cjs.map +1 -1
  1006. package/dist/hooks/use-event-listener.js.map +1 -1
  1007. package/dist/hooks/use-favicon.cjs.map +1 -1
  1008. package/dist/hooks/use-favicon.js.map +1 -1
  1009. package/dist/hooks/use-focus-trap.cjs.map +1 -1
  1010. package/dist/hooks/use-focus-trap.js.map +1 -1
  1011. package/dist/hooks/use-fullscreen-element.cjs.map +1 -1
  1012. package/dist/hooks/use-fullscreen-element.js.map +1 -1
  1013. package/dist/hooks/use-fullscreen.cjs +1 -1
  1014. package/dist/hooks/use-fullscreen.cjs.map +1 -1
  1015. package/dist/hooks/use-fullscreen.js +7 -23
  1016. package/dist/hooks/use-fullscreen.js.map +1 -1
  1017. package/dist/hooks/use-geolocation.cjs.map +1 -1
  1018. package/dist/hooks/use-geolocation.js.map +1 -1
  1019. package/dist/hooks/use-hover.cjs.map +1 -1
  1020. package/dist/hooks/use-hover.js.map +1 -1
  1021. package/dist/hooks/use-idle.cjs.map +1 -1
  1022. package/dist/hooks/use-idle.js.map +1 -1
  1023. package/dist/hooks/use-install-prompt.cjs +1 -1
  1024. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  1025. package/dist/hooks/use-install-prompt.js +1 -1
  1026. package/dist/hooks/use-install-prompt.js.map +1 -1
  1027. package/dist/hooks/use-intersection-observer.cjs.map +1 -1
  1028. package/dist/hooks/use-intersection-observer.js.map +1 -1
  1029. package/dist/hooks/use-interval.cjs.map +1 -1
  1030. package/dist/hooks/use-interval.js.map +1 -1
  1031. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  1032. package/dist/hooks/use-is-first-render.js.map +1 -1
  1033. package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
  1034. package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
  1035. package/dist/hooks/use-latest-ref.cjs.map +1 -1
  1036. package/dist/hooks/use-latest-ref.js.map +1 -1
  1037. package/dist/hooks/use-list-state.cjs.map +1 -1
  1038. package/dist/hooks/use-list-state.js.map +1 -1
  1039. package/dist/hooks/use-local-storage.cjs +1 -1
  1040. package/dist/hooks/use-local-storage.cjs.map +1 -1
  1041. package/dist/hooks/use-local-storage.js +2 -5
  1042. package/dist/hooks/use-local-storage.js.map +1 -1
  1043. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  1044. package/dist/hooks/use-long-press-handlers.js.map +1 -1
  1045. package/dist/hooks/use-long-press.cjs.map +1 -1
  1046. package/dist/hooks/use-long-press.js.map +1 -1
  1047. package/dist/hooks/use-map.cjs.map +1 -1
  1048. package/dist/hooks/use-map.js.map +1 -1
  1049. package/dist/hooks/use-media-devices.cjs.map +1 -1
  1050. package/dist/hooks/use-media-devices.js.map +1 -1
  1051. package/dist/hooks/use-media-permission.cjs +1 -1
  1052. package/dist/hooks/use-media-permission.cjs.map +1 -1
  1053. package/dist/hooks/use-media-permission.js +2 -5
  1054. package/dist/hooks/use-media-permission.js.map +1 -1
  1055. package/dist/hooks/use-media-query.cjs.map +1 -1
  1056. package/dist/hooks/use-media-query.js.map +1 -1
  1057. package/dist/hooks/use-object-url.cjs.map +1 -1
  1058. package/dist/hooks/use-object-url.js.map +1 -1
  1059. package/dist/hooks/use-online.cjs.map +1 -1
  1060. package/dist/hooks/use-online.js.map +1 -1
  1061. package/dist/hooks/use-pagination.cjs.map +1 -1
  1062. package/dist/hooks/use-pagination.js.map +1 -1
  1063. package/dist/hooks/use-previous.cjs.map +1 -1
  1064. package/dist/hooks/use-previous.js.map +1 -1
  1065. package/dist/hooks/use-push-to-talk.cjs.map +1 -1
  1066. package/dist/hooks/use-push-to-talk.js.map +1 -1
  1067. package/dist/hooks/use-queue.cjs.map +1 -1
  1068. package/dist/hooks/use-queue.js.map +1 -1
  1069. package/dist/hooks/use-resize-observer.cjs.map +1 -1
  1070. package/dist/hooks/use-resize-observer.js.map +1 -1
  1071. package/dist/hooks/use-scroll-lock.cjs.map +1 -1
  1072. package/dist/hooks/use-scroll-lock.js.map +1 -1
  1073. package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
  1074. package/dist/hooks/use-scroll-overflow.js.map +1 -1
  1075. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  1076. package/dist/hooks/use-service-worker-update.js.map +1 -1
  1077. package/dist/hooks/use-set.cjs.map +1 -1
  1078. package/dist/hooks/use-set.js.map +1 -1
  1079. package/dist/hooks/use-sortable.cjs.map +1 -1
  1080. package/dist/hooks/use-sortable.js.map +1 -1
  1081. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  1082. package/dist/hooks/use-stable-callback.js.map +1 -1
  1083. package/dist/hooks/use-storage-estimate.cjs.map +1 -1
  1084. package/dist/hooks/use-storage-estimate.js.map +1 -1
  1085. package/dist/hooks/use-throttle.cjs.map +1 -1
  1086. package/dist/hooks/use-throttle.js.map +1 -1
  1087. package/dist/hooks/use-timeout.cjs.map +1 -1
  1088. package/dist/hooks/use-timeout.js.map +1 -1
  1089. package/dist/hooks/use-toggle.cjs.map +1 -1
  1090. package/dist/hooks/use-toggle.js.map +1 -1
  1091. package/dist/hooks/use-typewriter.cjs.map +1 -1
  1092. package/dist/hooks/use-typewriter.js.map +1 -1
  1093. package/dist/hooks/use-window-size.cjs.map +1 -1
  1094. package/dist/hooks/use-window-size.js.map +1 -1
  1095. package/dist/http/api-client.cjs.map +1 -1
  1096. package/dist/http/api-client.js.map +1 -1
  1097. package/dist/http/build-url.cjs.map +1 -1
  1098. package/dist/http/build-url.js.map +1 -1
  1099. package/dist/http/credential-scope.cjs.map +1 -1
  1100. package/dist/http/credential-scope.js.map +1 -1
  1101. package/dist/http/csrf.cjs.map +1 -1
  1102. package/dist/http/csrf.js.map +1 -1
  1103. package/dist/http/decode-response.cjs.map +1 -1
  1104. package/dist/http/decode-response.js.map +1 -1
  1105. package/dist/http/describe-api-error.cjs.map +1 -1
  1106. package/dist/http/describe-api-error.js.map +1 -1
  1107. package/dist/http/errors.cjs.map +1 -1
  1108. package/dist/http/errors.js.map +1 -1
  1109. package/dist/http/idempotency.cjs.map +1 -1
  1110. package/dist/http/idempotency.js.map +1 -1
  1111. package/dist/http/parse-response.cjs.map +1 -1
  1112. package/dist/http/parse-response.js.map +1 -1
  1113. package/dist/http/resumable-upload.cjs +1 -1
  1114. package/dist/http/resumable-upload.cjs.map +1 -1
  1115. package/dist/http/resumable-upload.js +141 -105
  1116. package/dist/http/resumable-upload.js.map +1 -1
  1117. package/dist/http/retry.cjs +1 -1
  1118. package/dist/http/retry.cjs.map +1 -1
  1119. package/dist/http/retry.js +5 -6
  1120. package/dist/http/retry.js.map +1 -1
  1121. package/dist/http/timeout.cjs.map +1 -1
  1122. package/dist/http/timeout.js.map +1 -1
  1123. package/dist/http/upload-with-progress.cjs +1 -1
  1124. package/dist/http/upload-with-progress.cjs.map +1 -1
  1125. package/dist/http/upload-with-progress.js +2 -7
  1126. package/dist/http/upload-with-progress.js.map +1 -1
  1127. package/dist/http/use-describe-api-error.cjs.map +1 -1
  1128. package/dist/http/use-describe-api-error.js.map +1 -1
  1129. package/dist/http/use-poll.cjs +1 -1
  1130. package/dist/http/use-poll.cjs.map +1 -1
  1131. package/dist/http/use-poll.js +2 -5
  1132. package/dist/http/use-poll.js.map +1 -1
  1133. package/dist/i18n/I18nProvider.cjs.map +1 -1
  1134. package/dist/i18n/I18nProvider.js.map +1 -1
  1135. package/dist/i18n/create-i18n.cjs.map +1 -1
  1136. package/dist/i18n/create-i18n.js.map +1 -1
  1137. package/dist/icons/Icon.cjs.map +1 -1
  1138. package/dist/icons/Icon.js.map +1 -1
  1139. package/dist/icons/IconPicker.cjs.map +1 -1
  1140. package/dist/icons/IconPicker.js.map +1 -1
  1141. package/dist/icons/IconPicker.module.cjs.map +1 -1
  1142. package/dist/icons/IconPicker.module.js.map +1 -1
  1143. package/dist/icons/IconProvider.cjs.map +1 -1
  1144. package/dist/icons/IconProvider.js.map +1 -1
  1145. package/dist/icons/alias.cjs.map +1 -1
  1146. package/dist/icons/alias.js.map +1 -1
  1147. package/dist/icons/generated/aliases.cjs.map +1 -1
  1148. package/dist/icons/generated/aliases.js.map +1 -1
  1149. package/dist/icons/generated/icon-names.cjs.map +1 -1
  1150. package/dist/icons/generated/icon-names.js.map +1 -1
  1151. package/dist/icons/generated/loaders.cjs.map +1 -1
  1152. package/dist/icons/generated/loaders.js.map +1 -1
  1153. package/dist/icons/generated/shard-00.cjs.map +1 -1
  1154. package/dist/icons/generated/shard-00.js.map +1 -1
  1155. package/dist/icons/generated/shard-01.cjs.map +1 -1
  1156. package/dist/icons/generated/shard-01.js.map +1 -1
  1157. package/dist/icons/generated/shard-02.cjs.map +1 -1
  1158. package/dist/icons/generated/shard-02.js.map +1 -1
  1159. package/dist/icons/generated/shard-03.cjs.map +1 -1
  1160. package/dist/icons/generated/shard-03.js.map +1 -1
  1161. package/dist/icons/generated/shard-04.cjs.map +1 -1
  1162. package/dist/icons/generated/shard-04.js.map +1 -1
  1163. package/dist/icons/generated/shard-05.cjs.map +1 -1
  1164. package/dist/icons/generated/shard-05.js.map +1 -1
  1165. package/dist/icons/generated/shard-06.cjs.map +1 -1
  1166. package/dist/icons/generated/shard-06.js.map +1 -1
  1167. package/dist/icons/generated/shard-07.cjs.map +1 -1
  1168. package/dist/icons/generated/shard-07.js.map +1 -1
  1169. package/dist/icons/generated/shard-08.cjs.map +1 -1
  1170. package/dist/icons/generated/shard-08.js.map +1 -1
  1171. package/dist/icons/generated/shard-09.cjs.map +1 -1
  1172. package/dist/icons/generated/shard-09.js.map +1 -1
  1173. package/dist/icons/generated/shard-10.cjs.map +1 -1
  1174. package/dist/icons/generated/shard-10.js.map +1 -1
  1175. package/dist/icons/generated/shard-11.cjs.map +1 -1
  1176. package/dist/icons/generated/shard-11.js.map +1 -1
  1177. package/dist/icons/generated/shard-12.cjs.map +1 -1
  1178. package/dist/icons/generated/shard-12.js.map +1 -1
  1179. package/dist/icons/generated/shard-13.cjs.map +1 -1
  1180. package/dist/icons/generated/shard-13.js.map +1 -1
  1181. package/dist/icons/generated/shard-14.cjs.map +1 -1
  1182. package/dist/icons/generated/shard-14.js.map +1 -1
  1183. package/dist/icons/generated/shard-15.cjs.map +1 -1
  1184. package/dist/icons/generated/shard-15.js.map +1 -1
  1185. package/dist/icons/generated/shard-16.cjs.map +1 -1
  1186. package/dist/icons/generated/shard-16.js.map +1 -1
  1187. package/dist/icons/generated/shard-17.cjs.map +1 -1
  1188. package/dist/icons/generated/shard-17.js.map +1 -1
  1189. package/dist/icons/generated/shard-18.cjs.map +1 -1
  1190. package/dist/icons/generated/shard-18.js.map +1 -1
  1191. package/dist/icons/generated/shard-19.cjs.map +1 -1
  1192. package/dist/icons/generated/shard-19.js.map +1 -1
  1193. package/dist/icons/generated/shard-20.cjs.map +1 -1
  1194. package/dist/icons/generated/shard-20.js.map +1 -1
  1195. package/dist/icons/generated/shard-21.cjs.map +1 -1
  1196. package/dist/icons/generated/shard-21.js.map +1 -1
  1197. package/dist/icons/generated/shard-22.cjs.map +1 -1
  1198. package/dist/icons/generated/shard-22.js.map +1 -1
  1199. package/dist/icons/generated/shard-23.cjs.map +1 -1
  1200. package/dist/icons/generated/shard-23.js.map +1 -1
  1201. package/dist/icons/generated/shard-24.cjs.map +1 -1
  1202. package/dist/icons/generated/shard-24.js.map +1 -1
  1203. package/dist/icons/generated/shard-25.cjs.map +1 -1
  1204. package/dist/icons/generated/shard-25.js.map +1 -1
  1205. package/dist/icons/generated/shard-26.cjs.map +1 -1
  1206. package/dist/icons/generated/shard-26.js.map +1 -1
  1207. package/dist/icons/generated/shard-27.cjs.map +1 -1
  1208. package/dist/icons/generated/shard-27.js.map +1 -1
  1209. package/dist/icons/generated/shard-28.cjs.map +1 -1
  1210. package/dist/icons/generated/shard-28.js.map +1 -1
  1211. package/dist/icons/generated/shard-29.cjs.map +1 -1
  1212. package/dist/icons/generated/shard-29.js.map +1 -1
  1213. package/dist/icons/generated/shard-30.cjs.map +1 -1
  1214. package/dist/icons/generated/shard-30.js.map +1 -1
  1215. package/dist/icons/generated/shard-31.cjs.map +1 -1
  1216. package/dist/icons/generated/shard-31.js.map +1 -1
  1217. package/dist/icons/generated/shard-32.cjs.map +1 -1
  1218. package/dist/icons/generated/shard-32.js.map +1 -1
  1219. package/dist/icons/generated/shard-33.cjs.map +1 -1
  1220. package/dist/icons/generated/shard-33.js.map +1 -1
  1221. package/dist/icons/generated/shard-34.cjs.map +1 -1
  1222. package/dist/icons/generated/shard-34.js.map +1 -1
  1223. package/dist/icons/generated/shard-35.cjs.map +1 -1
  1224. package/dist/icons/generated/shard-35.js.map +1 -1
  1225. package/dist/icons/generated/shard-36.cjs.map +1 -1
  1226. package/dist/icons/generated/shard-36.js.map +1 -1
  1227. package/dist/icons/generated/shard-37.cjs.map +1 -1
  1228. package/dist/icons/generated/shard-37.js.map +1 -1
  1229. package/dist/icons/generated/shard-38.cjs.map +1 -1
  1230. package/dist/icons/generated/shard-38.js.map +1 -1
  1231. package/dist/icons/generated/shard-39.cjs.map +1 -1
  1232. package/dist/icons/generated/shard-39.js.map +1 -1
  1233. package/dist/icons/generated/shard-40.cjs.map +1 -1
  1234. package/dist/icons/generated/shard-40.js.map +1 -1
  1235. package/dist/icons/generated/shard-41.cjs.map +1 -1
  1236. package/dist/icons/generated/shard-41.js.map +1 -1
  1237. package/dist/icons/generated/shard-42.cjs.map +1 -1
  1238. package/dist/icons/generated/shard-42.js.map +1 -1
  1239. package/dist/icons/generated/shard-43.cjs.map +1 -1
  1240. package/dist/icons/generated/shard-43.js.map +1 -1
  1241. package/dist/icons/generated/shard-44.cjs.map +1 -1
  1242. package/dist/icons/generated/shard-44.js.map +1 -1
  1243. package/dist/icons/generated/shard-45.cjs.map +1 -1
  1244. package/dist/icons/generated/shard-45.js.map +1 -1
  1245. package/dist/icons/icon-context.cjs.map +1 -1
  1246. package/dist/icons/icon-context.js.map +1 -1
  1247. package/dist/icons/is-icon-name.cjs.map +1 -1
  1248. package/dist/icons/is-icon-name.js.map +1 -1
  1249. package/dist/icons/material-symbols.cjs.map +1 -1
  1250. package/dist/icons/material-symbols.js.map +1 -1
  1251. package/dist/icons/normalize-icon-name.cjs.map +1 -1
  1252. package/dist/icons/normalize-icon-name.js.map +1 -1
  1253. package/dist/icons/shard-cache.cjs +1 -1
  1254. package/dist/icons/shard-cache.cjs.map +1 -1
  1255. package/dist/icons/shard-cache.js +2 -5
  1256. package/dist/icons/shard-cache.js.map +1 -1
  1257. package/dist/icons/use-icon.cjs.map +1 -1
  1258. package/dist/icons/use-icon.js.map +1 -1
  1259. package/dist/icons/validate-icon-name.cjs.map +1 -1
  1260. package/dist/icons/validate-icon-name.js.map +1 -1
  1261. package/dist/icons-virtual.cjs.map +1 -1
  1262. package/dist/icons-virtual.js.map +1 -1
  1263. package/dist/imaging/canvas.cjs.map +1 -1
  1264. package/dist/imaging/canvas.js.map +1 -1
  1265. package/dist/imaging/compress.cjs.map +1 -1
  1266. package/dist/imaging/compress.js.map +1 -1
  1267. package/dist/imaging/decode.cjs.map +1 -1
  1268. package/dist/imaging/decode.js.map +1 -1
  1269. package/dist/imaging/encode.cjs.map +1 -1
  1270. package/dist/imaging/encode.js.map +1 -1
  1271. package/dist/imaging/exceptions.cjs.map +1 -1
  1272. package/dist/imaging/exceptions.js.map +1 -1
  1273. package/dist/imaging/frame.cjs.map +1 -1
  1274. package/dist/imaging/frame.js.map +1 -1
  1275. package/dist/imaging/thumbnails.cjs.map +1 -1
  1276. package/dist/imaging/thumbnails.js.map +1 -1
  1277. package/dist/imaging/transform.cjs.map +1 -1
  1278. package/dist/imaging/transform.js.map +1 -1
  1279. package/dist/imaging/use-image-processing.cjs.map +1 -1
  1280. package/dist/imaging/use-image-processing.js.map +1 -1
  1281. package/dist/logger/logger.cjs.map +1 -1
  1282. package/dist/logger/logger.js.map +1 -1
  1283. package/dist/neural.cjs.map +1 -1
  1284. package/dist/neural.js.map +1 -1
  1285. package/dist/oauth/GoogleSignIn.cjs +1 -1
  1286. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  1287. package/dist/oauth/GoogleSignIn.js +6 -9
  1288. package/dist/oauth/GoogleSignIn.js.map +1 -1
  1289. package/dist/oauth/callback.cjs.map +1 -1
  1290. package/dist/oauth/callback.js.map +1 -1
  1291. package/dist/oauth/use-oauth-callback.cjs.map +1 -1
  1292. package/dist/oauth/use-oauth-callback.js.map +1 -1
  1293. package/dist/offline/conflict.cjs.map +1 -1
  1294. package/dist/offline/conflict.js.map +1 -1
  1295. package/dist/offline/create-offline-database.cjs.map +1 -1
  1296. package/dist/offline/create-offline-database.js.map +1 -1
  1297. package/dist/offline/create-offline-store.cjs +1 -1
  1298. package/dist/offline/create-offline-store.cjs.map +1 -1
  1299. package/dist/offline/create-offline-store.js +1 -5
  1300. package/dist/offline/create-offline-store.js.map +1 -1
  1301. package/dist/offline/create-offline-sync.cjs +1 -1
  1302. package/dist/offline/create-offline-sync.cjs.map +1 -1
  1303. package/dist/offline/create-offline-sync.js +9 -14
  1304. package/dist/offline/create-offline-sync.js.map +1 -1
  1305. package/dist/offline/use-offline-sync.cjs.map +1 -1
  1306. package/dist/offline/use-offline-sync.js.map +1 -1
  1307. package/dist/perf/cache-size.cjs.map +1 -1
  1308. package/dist/perf/cache-size.js.map +1 -1
  1309. package/dist/perf/device.cjs.map +1 -1
  1310. package/dist/perf/device.js.map +1 -1
  1311. package/dist/perf/format.cjs.map +1 -1
  1312. package/dist/perf/format.js.map +1 -1
  1313. package/dist/perf/profiler.cjs.map +1 -1
  1314. package/dist/perf/profiler.js.map +1 -1
  1315. package/dist/push/use-push-subscription.cjs.map +1 -1
  1316. package/dist/push/use-push-subscription.js.map +1 -1
  1317. package/dist/push/utils.cjs.map +1 -1
  1318. package/dist/push/utils.js.map +1 -1
  1319. package/dist/push/web-push-client.cjs.map +1 -1
  1320. package/dist/push/web-push-client.js.map +1 -1
  1321. package/dist/query/QueryProvider.cjs.map +1 -1
  1322. package/dist/query/QueryProvider.js.map +1 -1
  1323. package/dist/query/constants.cjs.map +1 -1
  1324. package/dist/query/constants.js.map +1 -1
  1325. package/dist/query/create-query-keys.cjs.map +1 -1
  1326. package/dist/query/create-query-keys.js.map +1 -1
  1327. package/dist/query/foreign-client-warning.cjs.map +1 -1
  1328. package/dist/query/foreign-client-warning.js.map +1 -1
  1329. package/dist/query/offline-persistence.cjs +1 -1
  1330. package/dist/query/offline-persistence.cjs.map +1 -1
  1331. package/dist/query/offline-persistence.js +2 -10
  1332. package/dist/query/offline-persistence.js.map +1 -1
  1333. package/dist/query/optimistic.cjs.map +1 -1
  1334. package/dist/query/optimistic.js.map +1 -1
  1335. package/dist/query/pagination.cjs.map +1 -1
  1336. package/dist/query/pagination.js.map +1 -1
  1337. package/dist/query/retry-policy.cjs.map +1 -1
  1338. package/dist/query/retry-policy.js.map +1 -1
  1339. package/dist/query/use-cursor-query.cjs.map +1 -1
  1340. package/dist/query/use-cursor-query.js.map +1 -1
  1341. package/dist/query/use-offline-mutation.cjs.map +1 -1
  1342. package/dist/query/use-offline-mutation.js.map +1 -1
  1343. package/dist/query/use-paginated-query.cjs.map +1 -1
  1344. package/dist/query/use-paginated-query.js.map +1 -1
  1345. package/dist/router/AppRouter.cjs.map +1 -1
  1346. package/dist/router/AppRouter.js.map +1 -1
  1347. package/dist/router/RouteGuard.cjs.map +1 -1
  1348. package/dist/router/RouteGuard.js.map +1 -1
  1349. package/dist/router/index.js +2 -2
  1350. package/dist/share/share-or-download.cjs.map +1 -1
  1351. package/dist/share/share-or-download.js.map +1 -1
  1352. package/dist/share/share.cjs.map +1 -1
  1353. package/dist/share/share.js.map +1 -1
  1354. package/dist/sse/create-event-stream.cjs.map +1 -1
  1355. package/dist/sse/create-event-stream.js.map +1 -1
  1356. package/dist/sse/use-event-stream.cjs.map +1 -1
  1357. package/dist/sse/use-event-stream.js.map +1 -1
  1358. package/dist/store/create-selectors.cjs.map +1 -1
  1359. package/dist/store/create-selectors.js.map +1 -1
  1360. package/dist/store/create-store.cjs.map +1 -1
  1361. package/dist/store/create-store.js.map +1 -1
  1362. package/dist/sw/background-sync.cjs +1 -1
  1363. package/dist/sw/background-sync.cjs.map +1 -1
  1364. package/dist/sw/background-sync.js +32 -31
  1365. package/dist/sw/background-sync.js.map +1 -1
  1366. package/dist/sw/cache-inspect.cjs.map +1 -1
  1367. package/dist/sw/cache-inspect.js.map +1 -1
  1368. package/dist/sw/cache.cjs +1 -1
  1369. package/dist/sw/cache.cjs.map +1 -1
  1370. package/dist/sw/cache.js +2 -6
  1371. package/dist/sw/cache.js.map +1 -1
  1372. package/dist/sw/create-push-handler.cjs.map +1 -1
  1373. package/dist/sw/create-push-handler.js.map +1 -1
  1374. package/dist/sw/credential-headers.cjs.map +1 -1
  1375. package/dist/sw/credential-headers.js.map +1 -1
  1376. package/dist/sw/periodic-sync.cjs.map +1 -1
  1377. package/dist/sw/periodic-sync.js.map +1 -1
  1378. package/dist/sw/register-service-worker.cjs.map +1 -1
  1379. package/dist/sw/register-service-worker.js.map +1 -1
  1380. package/dist/sw/session-data.cjs.map +1 -1
  1381. package/dist/sw/session-data.js.map +1 -1
  1382. package/dist/sw.d.ts +46 -8
  1383. package/dist/tabular/assets.cjs.map +1 -1
  1384. package/dist/tabular/assets.js.map +1 -1
  1385. package/dist/tabular/cache.cjs.map +1 -1
  1386. package/dist/tabular/cache.js.map +1 -1
  1387. package/dist/tabular/compact.cjs.map +1 -1
  1388. package/dist/tabular/compact.js.map +1 -1
  1389. package/dist/tabular/exceptions.cjs.map +1 -1
  1390. package/dist/tabular/exceptions.js.map +1 -1
  1391. package/dist/tabular/manifest.cjs.map +1 -1
  1392. package/dist/tabular/manifest.js.map +1 -1
  1393. package/dist/tabular/predictor.cjs.map +1 -1
  1394. package/dist/tabular/predictor.js.map +1 -1
  1395. package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
  1396. package/dist/tabular/use-tabular-predictor.js.map +1 -1
  1397. package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
  1398. package/dist/telemetry/TelemetryProvider.js.map +1 -1
  1399. package/dist/telemetry/app-error-adapter.cjs.map +1 -1
  1400. package/dist/telemetry/app-error-adapter.js.map +1 -1
  1401. package/dist/telemetry/app-error-queue.cjs.map +1 -1
  1402. package/dist/telemetry/app-error-queue.js.map +1 -1
  1403. package/dist/telemetry/app-error-reporter.cjs +1 -1
  1404. package/dist/telemetry/app-error-reporter.cjs.map +1 -1
  1405. package/dist/telemetry/app-error-reporter.js +1 -1
  1406. package/dist/telemetry/app-error-reporter.js.map +1 -1
  1407. package/dist/telemetry/app-error-schema.cjs.map +1 -1
  1408. package/dist/telemetry/app-error-schema.js.map +1 -1
  1409. package/dist/telemetry/app-error-transport.cjs.map +1 -1
  1410. package/dist/telemetry/app-error-transport.js.map +1 -1
  1411. package/dist/telemetry/console-adapter.cjs.map +1 -1
  1412. package/dist/telemetry/console-adapter.js.map +1 -1
  1413. package/dist/telemetry/describe-app-error.cjs.map +1 -1
  1414. package/dist/telemetry/describe-app-error.js.map +1 -1
  1415. package/dist/telemetry/posthog-adapter.cjs +1 -1
  1416. package/dist/telemetry/posthog-adapter.cjs.map +1 -1
  1417. package/dist/telemetry/posthog-adapter.js +1 -5
  1418. package/dist/telemetry/posthog-adapter.js.map +1 -1
  1419. package/dist/telemetry/sentry-adapter.cjs.map +1 -1
  1420. package/dist/telemetry/sentry-adapter.js.map +1 -1
  1421. package/dist/tempest-react-sdk.cjs +1 -1
  1422. package/dist/tempest-react-sdk.d.ts +448 -15
  1423. package/dist/tempest-react-sdk.js +119 -119
  1424. package/dist/testing/create-mock-handlers.cjs.map +1 -1
  1425. package/dist/testing/create-mock-handlers.js.map +1 -1
  1426. package/dist/testing.js +1 -2
  1427. package/dist/theme/ThemeProvider.cjs.map +1 -1
  1428. package/dist/theme/ThemeProvider.js.map +1 -1
  1429. package/dist/theme/apply-theme.cjs.map +1 -1
  1430. package/dist/theme/apply-theme.js.map +1 -1
  1431. package/dist/theme/color.cjs.map +1 -1
  1432. package/dist/theme/color.js.map +1 -1
  1433. package/dist/theme/create-theme.cjs.map +1 -1
  1434. package/dist/theme/create-theme.js.map +1 -1
  1435. package/dist/theme/data-viz-ramps.cjs.map +1 -1
  1436. package/dist/theme/data-viz-ramps.js.map +1 -1
  1437. package/dist/theme/initial-theme.cjs.map +1 -1
  1438. package/dist/theme/initial-theme.js.map +1 -1
  1439. package/dist/theme/inverse-render.cjs.map +1 -1
  1440. package/dist/theme/inverse-render.js.map +1 -1
  1441. package/dist/theme/inverse-restore.cjs.map +1 -1
  1442. package/dist/theme/inverse-restore.js.map +1 -1
  1443. package/dist/theme/inverse-surface.cjs.map +1 -1
  1444. package/dist/theme/inverse-surface.js.map +1 -1
  1445. package/dist/theme/status-tokens.cjs.map +1 -1
  1446. package/dist/theme/status-tokens.js.map +1 -1
  1447. package/dist/theme/theme-presets.cjs.map +1 -1
  1448. package/dist/theme/theme-presets.js.map +1 -1
  1449. package/dist/utils/absent.cjs.map +1 -1
  1450. package/dist/utils/absent.js.map +1 -1
  1451. package/dist/utils/arrays.cjs.map +1 -1
  1452. package/dist/utils/arrays.js.map +1 -1
  1453. package/dist/utils/base64.cjs.map +1 -1
  1454. package/dist/utils/base64.js.map +1 -1
  1455. package/dist/utils/cn.cjs.map +1 -1
  1456. package/dist/utils/cn.js.map +1 -1
  1457. package/dist/utils/compare-values.cjs.map +1 -1
  1458. package/dist/utils/compare-values.js.map +1 -1
  1459. package/dist/utils/compressed-storage.cjs.map +1 -1
  1460. package/dist/utils/compressed-storage.js.map +1 -1
  1461. package/dist/utils/csv.cjs +3 -3
  1462. package/dist/utils/csv.cjs.map +1 -1
  1463. package/dist/utils/csv.js +11 -10
  1464. package/dist/utils/csv.js.map +1 -1
  1465. package/dist/utils/dev-mode.cjs.map +1 -1
  1466. package/dist/utils/dev-mode.js.map +1 -1
  1467. package/dist/utils/duplicate-instance.cjs.map +1 -1
  1468. package/dist/utils/duplicate-instance.js.map +1 -1
  1469. package/dist/utils/format.cjs.map +1 -1
  1470. package/dist/utils/format.js.map +1 -1
  1471. package/dist/utils/functions.cjs +1 -1
  1472. package/dist/utils/functions.cjs.map +1 -1
  1473. package/dist/utils/functions.js +1 -1
  1474. package/dist/utils/functions.js.map +1 -1
  1475. package/dist/utils/guards.cjs.map +1 -1
  1476. package/dist/utils/guards.js.map +1 -1
  1477. package/dist/utils/ids.cjs.map +1 -1
  1478. package/dist/utils/ids.js.map +1 -1
  1479. package/dist/utils/intl-cache.cjs.map +1 -1
  1480. package/dist/utils/intl-cache.js.map +1 -1
  1481. package/dist/utils/json-frame.cjs.map +1 -1
  1482. package/dist/utils/json-frame.js.map +1 -1
  1483. package/dist/utils/numbers.cjs.map +1 -1
  1484. package/dist/utils/numbers.js.map +1 -1
  1485. package/dist/utils/objects.cjs.map +1 -1
  1486. package/dist/utils/objects.js.map +1 -1
  1487. package/dist/{telemetry/app-error-owner.cjs → utils/owner-fingerprint.cjs} +2 -2
  1488. package/dist/utils/owner-fingerprint.cjs.map +1 -0
  1489. package/dist/{telemetry/app-error-owner.js → utils/owner-fingerprint.js} +3 -3
  1490. package/dist/utils/owner-fingerprint.js.map +1 -0
  1491. package/dist/utils/promises.cjs.map +1 -1
  1492. package/dist/utils/promises.js.map +1 -1
  1493. package/dist/utils/relative-time.cjs.map +1 -1
  1494. package/dist/utils/relative-time.js.map +1 -1
  1495. package/dist/utils/safe-url.cjs.map +1 -1
  1496. package/dist/utils/safe-url.js.map +1 -1
  1497. package/dist/utils/schema-like.cjs.map +1 -1
  1498. package/dist/utils/schema-like.js.map +1 -1
  1499. package/dist/utils/storage.cjs +1 -1
  1500. package/dist/utils/storage.cjs.map +1 -1
  1501. package/dist/utils/storage.js +2 -2
  1502. package/dist/utils/storage.js.map +1 -1
  1503. package/dist/utils/strings.cjs.map +1 -1
  1504. package/dist/utils/strings.js.map +1 -1
  1505. package/dist/utils/xlsx.cjs.map +1 -1
  1506. package/dist/utils/xlsx.js.map +1 -1
  1507. package/dist/vision/core/canvas.cjs.map +1 -1
  1508. package/dist/vision/core/canvas.js.map +1 -1
  1509. package/dist/vision/core/dtypes.cjs.map +1 -1
  1510. package/dist/vision/core/dtypes.js.map +1 -1
  1511. package/dist/vision/core/exceptions.cjs.map +1 -1
  1512. package/dist/vision/core/exceptions.js.map +1 -1
  1513. package/dist/vision/core/graph.cjs.map +1 -1
  1514. package/dist/vision/core/graph.js.map +1 -1
  1515. package/dist/vision/core/metadata.cjs +1 -1
  1516. package/dist/vision/core/metadata.cjs.map +1 -1
  1517. package/dist/vision/core/metadata.js +9 -26
  1518. package/dist/vision/core/metadata.js.map +1 -1
  1519. package/dist/vision/core/providers.cjs.map +1 -1
  1520. package/dist/vision/core/providers.js.map +1 -1
  1521. package/dist/vision/core/session.cjs.map +1 -1
  1522. package/dist/vision/core/session.js.map +1 -1
  1523. package/dist/vision/core/timing.cjs.map +1 -1
  1524. package/dist/vision/core/timing.js.map +1 -1
  1525. package/dist/vision/fusion.cjs.map +1 -1
  1526. package/dist/vision/fusion.js.map +1 -1
  1527. package/dist/vision/index.cjs.map +1 -1
  1528. package/dist/vision/index.js.map +1 -1
  1529. package/dist/vision/io/image.cjs.map +1 -1
  1530. package/dist/vision/io/image.js.map +1 -1
  1531. package/dist/vision/labels.cjs.map +1 -1
  1532. package/dist/vision/labels.js.map +1 -1
  1533. package/dist/vision/luminance.cjs.map +1 -1
  1534. package/dist/vision/luminance.js.map +1 -1
  1535. package/dist/vision/normalization.cjs.map +1 -1
  1536. package/dist/vision/normalization.js.map +1 -1
  1537. package/dist/vision/postprocess/classification.cjs.map +1 -1
  1538. package/dist/vision/postprocess/classification.js.map +1 -1
  1539. package/dist/vision/postprocess/detection.cjs.map +1 -1
  1540. package/dist/vision/postprocess/detection.js.map +1 -1
  1541. package/dist/vision/postprocess/segmentation.cjs.map +1 -1
  1542. package/dist/vision/postprocess/segmentation.js.map +1 -1
  1543. package/dist/vision/preprocess/image.cjs.map +1 -1
  1544. package/dist/vision/preprocess/image.js.map +1 -1
  1545. package/dist/vision/preprocess/pipeline.cjs.map +1 -1
  1546. package/dist/vision/preprocess/pipeline.js.map +1 -1
  1547. package/dist/vision/results.cjs.map +1 -1
  1548. package/dist/vision/results.js.map +1 -1
  1549. package/dist/vision/tasks/base.cjs.map +1 -1
  1550. package/dist/vision/tasks/base.js.map +1 -1
  1551. package/dist/vision/tasks/classifier.cjs.map +1 -1
  1552. package/dist/vision/tasks/classifier.js.map +1 -1
  1553. package/dist/vision/tasks/detectClassify.cjs.map +1 -1
  1554. package/dist/vision/tasks/detectClassify.js.map +1 -1
  1555. package/dist/vision/tasks/detector.cjs.map +1 -1
  1556. package/dist/vision/tasks/detector.js.map +1 -1
  1557. package/dist/vision/tasks/segmenter.cjs.map +1 -1
  1558. package/dist/vision/tasks/segmenter.js.map +1 -1
  1559. package/dist/vision/types.cjs.map +1 -1
  1560. package/dist/vision/types.js.map +1 -1
  1561. package/dist/vision/use-camera-stream.cjs +1 -1
  1562. package/dist/vision/use-camera-stream.cjs.map +1 -1
  1563. package/dist/vision/use-camera-stream.js +3 -9
  1564. package/dist/vision/use-camera-stream.js.map +1 -1
  1565. package/dist/vision/use-live-luminance.cjs +1 -1
  1566. package/dist/vision/use-live-luminance.cjs.map +1 -1
  1567. package/dist/vision/use-live-luminance.js +1 -5
  1568. package/dist/vision/use-live-luminance.js.map +1 -1
  1569. package/dist/vite/base-url.cjs.map +1 -1
  1570. package/dist/vite/base-url.js.map +1 -1
  1571. package/dist/vite/create-vite-config.cjs.map +1 -1
  1572. package/dist/vite/create-vite-config.js.map +1 -1
  1573. package/dist/vite/narrow-tokens.cjs.map +1 -1
  1574. package/dist/vite/narrow-tokens.js.map +1 -1
  1575. package/dist/vite/tempest-csp.cjs +2 -2
  1576. package/dist/vite/tempest-csp.cjs.map +1 -1
  1577. package/dist/vite/tempest-csp.js +69 -66
  1578. package/dist/vite/tempest-csp.js.map +1 -1
  1579. package/dist/vite/tempest-icons.cjs.map +1 -1
  1580. package/dist/vite/tempest-icons.js.map +1 -1
  1581. package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
  1582. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  1583. package/dist/vite/tempest-pwa-dev-sw.js +9 -16
  1584. package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
  1585. package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
  1586. package/dist/vite/tempest-pwa-icons.js.map +1 -1
  1587. package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
  1588. package/dist/vite/tempest-pwa-manifest.js.map +1 -1
  1589. package/dist/vite/tempest-styles.cjs.map +1 -1
  1590. package/dist/vite/tempest-styles.js.map +1 -1
  1591. package/dist/vite/tempest-vitest.cjs.map +1 -1
  1592. package/dist/vite/tempest-vitest.js.map +1 -1
  1593. package/dist/vite.d.ts +5 -3
  1594. package/dist/webrtc/link-stats.cjs.map +1 -1
  1595. package/dist/webrtc/link-stats.js.map +1 -1
  1596. package/dist/webrtc/mesh-quality.cjs.map +1 -1
  1597. package/dist/webrtc/mesh-quality.js.map +1 -1
  1598. package/dist/webrtc/opus-sdp.cjs.map +1 -1
  1599. package/dist/webrtc/opus-sdp.js.map +1 -1
  1600. package/dist/webrtc/peer-link.cjs +1 -1
  1601. package/dist/webrtc/peer-link.cjs.map +1 -1
  1602. package/dist/webrtc/peer-link.js +2 -5
  1603. package/dist/webrtc/peer-link.js.map +1 -1
  1604. package/dist/webrtc/peer-mesh.cjs +1 -1
  1605. package/dist/webrtc/peer-mesh.cjs.map +1 -1
  1606. package/dist/webrtc/peer-mesh.js +8 -24
  1607. package/dist/webrtc/peer-mesh.js.map +1 -1
  1608. package/dist/webrtc/sender-bitrate.cjs.map +1 -1
  1609. package/dist/webrtc/sender-bitrate.js.map +1 -1
  1610. package/dist/webrtc/use-link-stats.cjs.map +1 -1
  1611. package/dist/webrtc/use-link-stats.js.map +1 -1
  1612. package/dist/ws/create-web-socket.cjs +1 -1
  1613. package/dist/ws/create-web-socket.cjs.map +1 -1
  1614. package/dist/ws/create-web-socket.js +4 -22
  1615. package/dist/ws/create-web-socket.js.map +1 -1
  1616. package/dist/ws/resilience.cjs.map +1 -1
  1617. package/dist/ws/resilience.js.map +1 -1
  1618. package/dist/ws/use-web-socket.cjs.map +1 -1
  1619. package/dist/ws/use-web-socket.js.map +1 -1
  1620. package/package.json +9 -6
  1621. package/template/package.json +1 -1
  1622. package/dist/telemetry/app-error-owner.cjs.map +0 -1
  1623. package/dist/telemetry/app-error-owner.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sfx-pool.js","names":[],"sources":["../../src/audio/sfx-pool.ts"],"sourcesContent":["/** Options for {@link createSfxPool}. */\nexport interface SfxPoolOptions {\n /**\n * Master volume, `0`–`1`, multiplied into every per-play volume. Default\n * `1`. Wire it to the app's sound setting so one value governs the lot.\n */\n volume?: number;\n /**\n * Prefix applied to sources that are not absolute URLs — typically Vite's\n * `import.meta.env.BASE_URL`, so a build served from a subpath resolves.\n * Default `\"\"`.\n */\n baseUrl?: string;\n /**\n * Elements kept per source. `1` (the default) restarts the clip on every\n * play, which is what a menu blip wants. Raise it to let a sound overlap\n * itself — a hit landing while the previous one is still ringing.\n */\n voices?: number;\n /**\n * Maximum number of distinct sources held. When exceeded, the least\n * recently played source is released. Default `48`.\n */\n maxSources?: number;\n}\n\n/** Per-play overrides. */\nexport interface PlaySfxOptions {\n /** Volume for this play, `0`–`1`, multiplied by the pool's master. Default `1`. */\n volume?: number;\n}\n\n/** Imperative handle over a pool of short sound effects. */\nexport interface SfxPool {\n /** Play a clip, allocating and caching its element on first use. */\n play: (src: string, options?: PlaySfxOptions) => void;\n /** Fetch clips ahead of the first play, so it is not silent while the file downloads. */\n preload: (src: string | string[]) => void;\n /** Set the master volume, applying it to anything already sounding. */\n setVolume: (volume: number) => void;\n /** Stop one source, or every source when called with no argument. */\n stop: (src?: string) => void;\n /** Release every element. Call on unmount. */\n dispose: () => void;\n}\n\n/** Clamp to the range `HTMLMediaElement.volume` accepts, rejecting `NaN`. */\nfunction clampVolume(value: number): number {\n if (!Number.isFinite(value)) return 1;\n return Math.min(1, Math.max(0, value));\n}\n\n/**\n * A pool of preallocated `<audio>` elements for short sound effects.\n *\n * `new Audio(src)` on every play allocates an element and re-enters the network\n * stack for a file the browser already has, which is the wrong shape for a\n * sound that fires dozens of times a minute — a UI blip, a hit, a pickup. The\n * pool allocates once per source and replays.\n *\n * This is deliberately not {@link createAudioPlayer}: that handle tracks a\n * single \"current\" clip with loop, sink routing and lifecycle callbacks, which\n * is what background music needs. Effects are the opposite case — many\n * sources, all short, fire-and-forget, and the only thing that matters is that\n * firing one is cheap.\n *\n * A blocked `play()` is swallowed. Browsers reject playback until the user has\n * interacted with the page, and a sound effect is by definition not worth\n * interrupting anything over; call {@link SfxPool.preload} after the first\n * interaction if you want the pool warm.\n *\n * @param options - Master volume, base URL, voices per source and pool size.\n * @returns The pool handle.\n *\n * @example\n * const sfx = createSfxPool({ volume: 0.6, baseUrl: import.meta.env.BASE_URL });\n * sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\n/** One source's voices plus the round-robin cursor over them. */\ninterface SourceEntry {\n /** The audio elements sharing this source. */\n elements: HTMLAudioElement[];\n /** Index of the element the next play claims. */\n next: number;\n}\n\n/**\n * Resolve a source against the pool's base URL.\n *\n * An absolute URL, a protocol-relative one and a `data:` URI are already\n * addresses — prefixing them would produce a path that resolves to nothing.\n *\n * @param baseUrl - Pool base, possibly empty.\n * @param src - Source as the caller wrote it.\n * @returns The URL to load.\n */\nfunction resolveSource(baseUrl: string, src: string): string {\n if (!baseUrl || /^(https?:)?\\/\\//.test(src) || src.startsWith(\"data:\")) return src;\n return `${baseUrl.replace(/\\/$/, \"\")}/${src.replace(/^\\//, \"\")}`;\n}\n\n/**\n * Stop every voice of an entry and let the browser drop its buffer.\n *\n * Removing `src` before `load()` is what frees the decoded audio: `load()` on an\n * element that still has a source re-fetches it instead.\n *\n * @param entry - The entry being evicted.\n */\nfunction releaseEntry(entry: { elements: HTMLAudioElement[] }): void {\n for (const element of entry.elements) {\n element.pause();\n element.removeAttribute(\"src\");\n element.load();\n }\n}\n\n/**\n * Drop the least recently played source once the pool is over its cap.\n *\n * The map is insertion-ordered by last play, so the first key is the coldest.\n * The source that was just admitted is never the one evicted, which would\n * otherwise happen with a cap of one.\n *\n * @param sources - The live pool, mutated in place.\n * @param keepUrl - The source that must survive this eviction.\n * @param maxSources - How many sources the pool may hold.\n */\nfunction evictLeastRecent(\n sources: Map<string, SourceEntry>,\n keepUrl: string,\n maxSources: number,\n): void {\n if (sources.size <= maxSources) return;\n const oldest = sources.keys().next();\n if (oldest.done || oldest.value === keepUrl) return;\n const evicted = sources.get(oldest.value);\n if (evicted) releaseEntry(evicted);\n sources.delete(oldest.value);\n}\n\nexport function createSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { baseUrl = \"\", voices = 1, maxSources = 48 } = options;\n\n let master = clampVolume(options.volume ?? 1);\n const voiceCount = Math.max(1, Math.floor(voices));\n\n /**\n * Insertion-ordered by last play, so evicting the first key drops the\n * least recently used source.\n */\n const sources = new Map<string, SourceEntry>();\n\n /**\n * Per-play gain of whatever each element last played at.\n *\n * `setVolume` has to rescale live playback by the same factor it was\n * started with; assigning the master directly would yank a clip that\n * started at half volume up to full.\n */\n const gains = new WeakMap<HTMLAudioElement, number>();\n\n /**\n * The pool entry for a source, creating and admitting it when it is new.\n *\n * Re-inserts an existing entry so the map order stays \"least recently used\n * first\", which is what makes evicting the first key correct.\n *\n * `created` is reported because assigning `src` on an element whose `preload`\n * is `\"auto\"` already starts the fetch: calling `load()` on an entry that was\n * merely looked up would abort that fetch and discard whatever is already\n * buffered, per the media element load algorithm.\n *\n * @param src - Raw source, resolved against `baseUrl`.\n * @returns The entry and whether this call created it, or `null` where\n * `Audio` does not exist.\n */\n function acquire(src: string): { entry: SourceEntry; created: boolean } | null {\n if (typeof Audio === \"undefined\") return null;\n\n const url = resolveSource(baseUrl, src);\n const existing = sources.get(url);\n if (existing) {\n sources.delete(url);\n sources.set(url, existing);\n return { entry: existing, created: false };\n }\n\n const entry: SourceEntry = {\n elements: Array.from({ length: voiceCount }, () => {\n const element = new Audio(url);\n element.preload = \"auto\";\n return element;\n }),\n next: 0,\n };\n sources.set(url, entry);\n\n evictLeastRecent(sources, url, maxSources);\n\n return { entry, created: true };\n }\n\n return {\n play(src, playOptions = {}) {\n const acquired = acquire(src);\n if (!acquired) return;\n\n const entry = acquired.entry;\n const element = entry.elements[entry.next];\n entry.next = (entry.next + 1) % entry.elements.length;\n\n const gain = clampVolume(playOptions.volume ?? 1);\n gains.set(element, gain);\n\n element.pause();\n element.currentTime = 0;\n element.volume = clampVolume(gain * master);\n void element.play().catch(() => {});\n },\n\n preload(src) {\n for (const one of Array.isArray(src) ? src : [src]) {\n const acquired = acquire(one);\n if (!acquired?.created) continue;\n for (const element of acquired.entry.elements) element.load();\n }\n },\n\n setVolume(volume) {\n master = clampVolume(volume);\n for (const entry of sources.values()) {\n for (const element of entry.elements) {\n if (element.paused) continue;\n element.volume = clampVolume((gains.get(element) ?? 1) * master);\n }\n }\n },\n\n stop(src) {\n const targets = src\n ? [sources.get(resolveSource(baseUrl, src))]\n : [...sources.values()];\n for (const entry of targets) {\n if (!entry) continue;\n for (const element of entry.elements) {\n element.pause();\n element.currentTime = 0;\n }\n }\n },\n\n dispose() {\n for (const entry of sources.values()) releaseEntry(entry);\n sources.clear();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sfx-pool.js","names":["clampVolume","value","resolveSource","baseUrl","src","releaseEntry","element","entry","evictLeastRecent","sources","maxSources","oldest","keepUrl","evicted","createSfxPool","options","master","voiceCount","voices","gains","acquire","url","existing","acquired","gain","playOptions","one","volume","targets"],"sources":["../../src/audio/sfx-pool.ts"],"sourcesContent":["/** Options for {@link createSfxPool}. */\nexport interface SfxPoolOptions {\n /**\n * Master volume, `0`–`1`, multiplied into every per-play volume. Default\n * `1`. Wire it to the app's sound setting so one value governs the lot.\n */\n volume?: number;\n /**\n * Prefix applied to sources that are not absolute URLs — typically Vite's\n * `import.meta.env.BASE_URL`, so a build served from a subpath resolves.\n * Default `\"\"`.\n */\n baseUrl?: string;\n /**\n * Elements kept per source. `1` (the default) restarts the clip on every\n * play, which is what a menu blip wants. Raise it to let a sound overlap\n * itself — a hit landing while the previous one is still ringing.\n */\n voices?: number;\n /**\n * Maximum number of distinct sources held. When exceeded, the least\n * recently played source is released. Default `48`.\n */\n maxSources?: number;\n}\n\n/** Per-play overrides. */\nexport interface PlaySfxOptions {\n /** Volume for this play, `0`–`1`, multiplied by the pool's master. Default `1`. */\n volume?: number;\n}\n\n/** Imperative handle over a pool of short sound effects. */\nexport interface SfxPool {\n /** Play a clip, allocating and caching its element on first use. */\n play: (src: string, options?: PlaySfxOptions) => void;\n /** Fetch clips ahead of the first play, so it is not silent while the file downloads. */\n preload: (src: string | string[]) => void;\n /** Set the master volume, applying it to anything already sounding. */\n setVolume: (volume: number) => void;\n /** Stop one source, or every source when called with no argument. */\n stop: (src?: string) => void;\n /** Release every element. Call on unmount. */\n dispose: () => void;\n}\n\n/** Clamp to the range `HTMLMediaElement.volume` accepts, rejecting `NaN`. */\nfunction clampVolume(value: number): number {\n if (!Number.isFinite(value)) return 1;\n return Math.min(1, Math.max(0, value));\n}\n\n/**\n * A pool of preallocated `<audio>` elements for short sound effects.\n *\n * `new Audio(src)` on every play allocates an element and re-enters the network\n * stack for a file the browser already has, which is the wrong shape for a\n * sound that fires dozens of times a minute — a UI blip, a hit, a pickup. The\n * pool allocates once per source and replays.\n *\n * This is deliberately not {@link createAudioPlayer}: that handle tracks a\n * single \"current\" clip with loop, sink routing and lifecycle callbacks, which\n * is what background music needs. Effects are the opposite case — many\n * sources, all short, fire-and-forget, and the only thing that matters is that\n * firing one is cheap.\n *\n * A blocked `play()` is swallowed. Browsers reject playback until the user has\n * interacted with the page, and a sound effect is by definition not worth\n * interrupting anything over; call {@link SfxPool.preload} after the first\n * interaction if you want the pool warm.\n *\n * @param options - Master volume, base URL, voices per source and pool size.\n * @returns The pool handle.\n *\n * @example\n * const sfx = createSfxPool({ volume: 0.6, baseUrl: import.meta.env.BASE_URL });\n * sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\n/** One source's voices plus the round-robin cursor over them. */\ninterface SourceEntry {\n /** The audio elements sharing this source. */\n elements: HTMLAudioElement[];\n /** Index of the element the next play claims. */\n next: number;\n}\n\n/**\n * Resolve a source against the pool's base URL.\n *\n * An absolute URL, a protocol-relative one and a `data:` URI are already\n * addresses — prefixing them would produce a path that resolves to nothing.\n *\n * @param baseUrl - Pool base, possibly empty.\n * @param src - Source as the caller wrote it.\n * @returns The URL to load.\n */\nfunction resolveSource(baseUrl: string, src: string): string {\n if (!baseUrl || /^(https?:)?\\/\\//.test(src) || src.startsWith(\"data:\")) return src;\n return `${baseUrl.replace(/\\/$/, \"\")}/${src.replace(/^\\//, \"\")}`;\n}\n\n/**\n * Stop every voice of an entry and let the browser drop its buffer.\n *\n * Removing `src` before `load()` is what frees the decoded audio: `load()` on an\n * element that still has a source re-fetches it instead.\n *\n * @param entry - The entry being evicted.\n */\nfunction releaseEntry(entry: { elements: HTMLAudioElement[] }): void {\n for (const element of entry.elements) {\n element.pause();\n element.removeAttribute(\"src\");\n element.load();\n }\n}\n\n/**\n * Drop the least recently played source once the pool is over its cap.\n *\n * The map is insertion-ordered by last play, so the first key is the coldest.\n * The source that was just admitted is never the one evicted, which would\n * otherwise happen with a cap of one.\n *\n * @param sources - The live pool, mutated in place.\n * @param keepUrl - The source that must survive this eviction.\n * @param maxSources - How many sources the pool may hold.\n */\nfunction evictLeastRecent(\n sources: Map<string, SourceEntry>,\n keepUrl: string,\n maxSources: number,\n): void {\n if (sources.size <= maxSources) return;\n const oldest = sources.keys().next();\n if (oldest.done || oldest.value === keepUrl) return;\n const evicted = sources.get(oldest.value);\n if (evicted) releaseEntry(evicted);\n sources.delete(oldest.value);\n}\n\nexport function createSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { baseUrl = \"\", voices = 1, maxSources = 48 } = options;\n\n let master = clampVolume(options.volume ?? 1);\n const voiceCount = Math.max(1, Math.floor(voices));\n\n /**\n * Insertion-ordered by last play, so evicting the first key drops the\n * least recently used source.\n */\n const sources = new Map<string, SourceEntry>();\n\n /**\n * Per-play gain of whatever each element last played at.\n *\n * `setVolume` has to rescale live playback by the same factor it was\n * started with; assigning the master directly would yank a clip that\n * started at half volume up to full.\n */\n const gains = new WeakMap<HTMLAudioElement, number>();\n\n /**\n * The pool entry for a source, creating and admitting it when it is new.\n *\n * Re-inserts an existing entry so the map order stays \"least recently used\n * first\", which is what makes evicting the first key correct.\n *\n * `created` is reported because assigning `src` on an element whose `preload`\n * is `\"auto\"` already starts the fetch: calling `load()` on an entry that was\n * merely looked up would abort that fetch and discard whatever is already\n * buffered, per the media element load algorithm.\n *\n * @param src - Raw source, resolved against `baseUrl`.\n * @returns The entry and whether this call created it, or `null` where\n * `Audio` does not exist.\n */\n function acquire(src: string): { entry: SourceEntry; created: boolean } | null {\n if (typeof Audio === \"undefined\") return null;\n\n const url = resolveSource(baseUrl, src);\n const existing = sources.get(url);\n if (existing) {\n sources.delete(url);\n sources.set(url, existing);\n return { entry: existing, created: false };\n }\n\n const entry: SourceEntry = {\n elements: Array.from({ length: voiceCount }, () => {\n const element = new Audio(url);\n element.preload = \"auto\";\n return element;\n }),\n next: 0,\n };\n sources.set(url, entry);\n\n evictLeastRecent(sources, url, maxSources);\n\n return { entry, created: true };\n }\n\n return {\n play(src, playOptions = {}) {\n const acquired = acquire(src);\n if (!acquired) return;\n\n const entry = acquired.entry;\n const element = entry.elements[entry.next];\n entry.next = (entry.next + 1) % entry.elements.length;\n\n const gain = clampVolume(playOptions.volume ?? 1);\n gains.set(element, gain);\n\n element.pause();\n element.currentTime = 0;\n element.volume = clampVolume(gain * master);\n void element.play().catch(() => {});\n },\n\n preload(src) {\n for (const one of Array.isArray(src) ? src : [src]) {\n const acquired = acquire(one);\n if (!acquired?.created) continue;\n for (const element of acquired.entry.elements) element.load();\n }\n },\n\n setVolume(volume) {\n master = clampVolume(volume);\n for (const entry of sources.values()) {\n for (const element of entry.elements) {\n if (element.paused) continue;\n element.volume = clampVolume((gains.get(element) ?? 1) * master);\n }\n }\n },\n\n stop(src) {\n const targets = src\n ? [sources.get(resolveSource(baseUrl, src))]\n : [...sources.values()];\n for (const entry of targets) {\n if (!entry) continue;\n for (const element of entry.elements) {\n element.pause();\n element.currentTime = 0;\n }\n }\n },\n\n dispose() {\n for (const entry of sources.values()) releaseEntry(entry);\n sources.clear();\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio-bus.cjs","names":[],"sources":["../../src/audio/use-audio-bus.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createAudioBus, type AudioBus, type AudioBusOptions } from \"./audio-bus\";\n\n/**\n * Keep one {@link AudioBus} alive for as long as the component is mounted.\n *\n * The bus is built on the first render and closed on unmount, which matters more\n * than it looks: browsers cap the number of live `AudioContext`s (Chrome allows\n * around six), so a bus leaked on unmount eventually breaks every later one on\n * the page.\n *\n * Options are read once. A bus is a device route and a mixing graph, not a\n * render output — rebuilding it because a prop changed would drop every attached\n * source mid-sentence. Change gain and output through the bus itself.\n *\n * @param options - See {@link AudioBusOptions}. Read on the first render only.\n * @returns The bus, stable for the component's lifetime.\n *\n * @example\n * const bus = useAudioBus({ maxGain: 3 });\n *\n * useEffect(() => {\n * const handle = bus.attach(stream, { gain: 1 });\n * return () => handle.stop();\n * }, [bus, stream]);\n */\nexport function useAudioBus(options: AudioBusOptions = {}): AudioBus {\n const [bus] = useState<AudioBus>(() => createAudioBus(options));\n\n useEffect(() => {\n return () => bus.close();\n }, [bus]);\n\n return bus;\n}\n"],"mappings":"0DA0BA,SAAgB,EAAY,EAA2B,CAAC,EAAa,CACjE,GAAM,CAAC,IAAA,EAAO,EAAA,SAAA,KAAyB,EAAA,eAAe,CAAO,CAAC,EAM9D,OAJA,EAAA,EAAA,UAAA,SACiB,EAAI,MAAM,EACxB,CAAC,CAAG,CAAC,EAED,CACX"}
1
+ {"version":3,"file":"use-audio-bus.cjs","names":["useState","createAudioBus","require_audio_bus","react","useAudioBus","bus","options"],"sources":["../../src/audio/use-audio-bus.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createAudioBus, type AudioBus, type AudioBusOptions } from \"./audio-bus\";\n\n/**\n * Keep one {@link AudioBus} alive for as long as the component is mounted.\n *\n * The bus is built on the first render and closed on unmount, which matters more\n * than it looks: browsers cap the number of live `AudioContext`s (Chrome allows\n * around six), so a bus leaked on unmount eventually breaks every later one on\n * the page.\n *\n * Options are read once. A bus is a device route and a mixing graph, not a\n * render output — rebuilding it because a prop changed would drop every attached\n * source mid-sentence. Change gain and output through the bus itself.\n *\n * @param options - See {@link AudioBusOptions}. Read on the first render only.\n * @returns The bus, stable for the component's lifetime.\n *\n * @example\n * const bus = useAudioBus({ maxGain: 3 });\n *\n * useEffect(() => {\n * const handle = bus.attach(stream, { gain: 1 });\n * return () => handle.stop();\n * }, [bus, stream]);\n */\nexport function useAudioBus(options: AudioBusOptions = {}): AudioBus {\n const [bus] = useState<AudioBus>(() => createAudioBus(options));\n\n useEffect(() => {\n return () => bus.close();\n }, [bus]);\n\n return bus;\n}\n"],"mappings":"A,0DA0BA,SAAgBI,EAAY,EAA2B,CAAC,EAAa,CACjE,GAAM,CAACC,IAAA,EAAOL,EAAAA,SAAAA,KAAyBC,EAAAA,eAAeK,CAAO,CAAC,EAM9D,OAJA,EAAAH,EAAA,UAAA,SACiBE,EAAI,MAAM,EACxB,CAACA,CAAG,CAAC,EAEDA,CACX,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio-bus.js","names":[],"sources":["../../src/audio/use-audio-bus.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createAudioBus, type AudioBus, type AudioBusOptions } from \"./audio-bus\";\n\n/**\n * Keep one {@link AudioBus} alive for as long as the component is mounted.\n *\n * The bus is built on the first render and closed on unmount, which matters more\n * than it looks: browsers cap the number of live `AudioContext`s (Chrome allows\n * around six), so a bus leaked on unmount eventually breaks every later one on\n * the page.\n *\n * Options are read once. A bus is a device route and a mixing graph, not a\n * render output — rebuilding it because a prop changed would drop every attached\n * source mid-sentence. Change gain and output through the bus itself.\n *\n * @param options - See {@link AudioBusOptions}. Read on the first render only.\n * @returns The bus, stable for the component's lifetime.\n *\n * @example\n * const bus = useAudioBus({ maxGain: 3 });\n *\n * useEffect(() => {\n * const handle = bus.attach(stream, { gain: 1 });\n * return () => handle.stop();\n * }, [bus, stream]);\n */\nexport function useAudioBus(options: AudioBusOptions = {}): AudioBus {\n const [bus] = useState<AudioBus>(() => createAudioBus(options));\n\n useEffect(() => {\n return () => bus.close();\n }, [bus]);\n\n return bus;\n}\n"],"mappings":";;;AA0BA,SAAgB,EAAY,IAA2B,CAAC,GAAa;CACjE,IAAM,CAAC,KAAO,QAAyB,EAAe,CAAO,CAAC;CAM9D,OAJA,cACiB,EAAI,MAAM,GACxB,CAAC,CAAG,CAAC,GAED;AACX"}
1
+ {"version":3,"file":"use-audio-bus.js","names":["useAudioBus","bus","useState","createAudioBus","options","useEffect"],"sources":["../../src/audio/use-audio-bus.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createAudioBus, type AudioBus, type AudioBusOptions } from \"./audio-bus\";\n\n/**\n * Keep one {@link AudioBus} alive for as long as the component is mounted.\n *\n * The bus is built on the first render and closed on unmount, which matters more\n * than it looks: browsers cap the number of live `AudioContext`s (Chrome allows\n * around six), so a bus leaked on unmount eventually breaks every later one on\n * the page.\n *\n * Options are read once. A bus is a device route and a mixing graph, not a\n * render output — rebuilding it because a prop changed would drop every attached\n * source mid-sentence. Change gain and output through the bus itself.\n *\n * @param options - See {@link AudioBusOptions}. Read on the first render only.\n * @returns The bus, stable for the component's lifetime.\n *\n * @example\n * const bus = useAudioBus({ maxGain: 3 });\n *\n * useEffect(() => {\n * const handle = bus.attach(stream, { gain: 1 });\n * return () => handle.stop();\n * }, [bus, stream]);\n */\nexport function useAudioBus(options: AudioBusOptions = {}): AudioBus {\n const [bus] = useState<AudioBus>(() => createAudioBus(options));\n\n useEffect(() => {\n return () => bus.close();\n }, [bus]);\n\n return bus;\n}\n"],"mappings":";;;AA0BA,SAAgBA,EAAY,IAA2B,CAAC,GAAa;CACjE,IAAM,CAACC,KAAOC,QAAyBC,EAAeC,CAAO,CAAC;CAM9D,OAJAC,cACiBJ,EAAI,MAAM,GACxB,CAACA,CAAG,CAAC,GAEDA;AACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio-recorder.cjs","names":[],"sources":["../../src/audio/use-audio-recorder.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the hook is the recorder's lifecycle seen from React:\n * permission, the recorder handle, the clock, the level meter and the auto-stop\n * timer, all of which have to be torn down together on unmount. A second hook would\n * own half the teardown.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\nimport {\n createAudioRecorder,\n type AudioRecorderHandle,\n type AudioRecorderOptions,\n type AudioRecorderStatus,\n type AudioRecording,\n} from \"./audio-recorder\";\nimport { createLevelMeter, type LevelMeter } from \"./level-meter\";\n\n/** Options for {@link useAudioRecorder}. */\nexport interface UseAudioRecorderOptions extends AudioRecorderOptions {\n /**\n * Stop automatically after this many milliseconds.\n *\n * Worth setting on anything user-facing: a recording left running by accident is\n * a memory buffer that grows until the tab dies.\n */\n maxDurationMs?: number;\n /** Called once the recording is assembled, whether by `stop()` or `maxDurationMs`. */\n onRecorded?: (recording: AudioRecording) => void;\n /** How often the clock and the level are published, in ms. Default 100. */\n tickMs?: number;\n /** Skip the level meter — it costs an `AudioContext`. Default `false`. */\n disableLevelMeter?: boolean;\n}\n\n/** Value returned by {@link useAudioRecorder}. */\nexport interface UseAudioRecorderResult {\n status: AudioRecorderStatus;\n /** Recorded length so far, excluding paused time. */\n durationMs: number;\n /** Loudness 0–1, or `0` when the meter is off or nothing is recording. */\n level: number;\n /** The finished recording, or `null` before the first `stop()`. */\n recording: AudioRecording | null;\n /** Recorder-level error (encoder failure, device pulled mid-recording). */\n error: unknown;\n /** `false` when there is no stream yet, or the browser cannot record. */\n ready: boolean;\n start: () => void;\n pause: () => void;\n resume: () => void;\n /** Stop and resolve with the recording. Also published on `recording`. */\n stop: () => Promise<AudioRecording | null>;\n /** Stop and throw the audio away. */\n cancel: () => void;\n}\n\n/**\n * Record the given stream, with a clock, a level meter and a state machine.\n *\n * Pass the stream from {@link useMicrophone}; the hook stays `ready: false` until\n * there is one, so a page can render the whole recorder UI before permission is\n * granted and simply have it disabled.\n *\n * The clock and the level are published on an interval (`tickMs`, default 100 ms)\n * rather than per animation frame. A hook that set state 60 times a second would\n * re-render everything the recorder is inside; 10 Hz is smooth enough for a timer and\n * a meter, and the underlying `createLevelMeter` is still available for anyone who\n * wants a frame-accurate bar written straight to the DOM.\n *\n * @param stream - The stream to record, or `null` while permission is pending.\n * @param options - See {@link UseAudioRecorderOptions}.\n *\n * @example\n * const mic = useMicrophone();\n * const rec = useAudioRecorder(mic.stream, { maxDurationMs: 120_000 });\n * <button disabled={!rec.ready} onClick={rec.start}>Gravar</button>\n */\nexport function useAudioRecorder(\n stream: MediaStream | null,\n options: UseAudioRecorderOptions = {},\n): UseAudioRecorderResult {\n const {\n maxDurationMs,\n onRecorded,\n onChunk,\n onError,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n tickMs = 100,\n disableLevelMeter = false,\n } = options;\n\n const [status, setStatus] = useState<AudioRecorderStatus>(\"idle\");\n const [durationMs, setDurationMs] = useState(0);\n const [level, setLevel] = useState(0);\n const [recording, setRecording] = useState<AudioRecording | null>(null);\n const [error, setError] = useState<unknown>(null);\n const [ready, setReady] = useState(false);\n\n const recorderRef = useRef<AudioRecorderHandle | null>(null);\n const meterRef = useRef<LevelMeter | null>(null);\n\n const emitRecorded = useStableCallback((result: AudioRecording) => onRecorded?.(result));\n const emitChunk = useStableCallback((chunk: Blob) => onChunk?.(chunk));\n const emitError = useStableCallback((err: unknown) => {\n setError(err);\n onError?.(err);\n });\n\n /**\n * Build the recorder and the meter for a stream, and tear both down when it\n * changes.\n *\n * The recorder is rebuilt per stream because `MediaRecorder` is bound to the\n * stream it was constructed with — swapping microphones mid-session hands us a\n * new `MediaStream`, and reusing the old recorder would keep recording the device\n * the user just switched away from. It is rebuilt on a container or bitrate change\n * for the same reason: both are constructor arguments.\n */\n useEffect(() => {\n if (!stream) {\n setReady(false);\n return;\n }\n let recorder: AudioRecorderHandle;\n try {\n recorder = createAudioRecorder(stream, {\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n onChunk: emitChunk,\n onError: emitError,\n });\n } catch (err) {\n setError(err);\n setReady(false);\n return;\n }\n recorderRef.current = recorder;\n meterRef.current = disableLevelMeter ? null : createLevelMeter(stream);\n setReady(true);\n setStatus(\"idle\");\n\n return () => {\n if (recorder.status() === \"recording\" || recorder.status() === \"paused\") {\n recorder.cancel();\n }\n meterRef.current?.stop();\n meterRef.current = null;\n recorderRef.current = null;\n setReady(false);\n };\n }, [\n stream,\n disableLevelMeter,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n emitChunk,\n emitError,\n ]);\n\n const stop = useCallback(async (): Promise<AudioRecording | null> => {\n const recorder = recorderRef.current;\n if (!recorder) return null;\n const current = recorder.status();\n if (current !== \"recording\" && current !== \"paused\") return null;\n const result = await recorder.stop();\n setStatus(\"stopped\");\n setDurationMs(result.durationMs);\n setLevel(0);\n setRecording(result);\n emitRecorded(result);\n return result;\n }, [emitRecorded]);\n\n /**\n * Publish the clock and the level while something is running, and enforce\n * `maxDurationMs`.\n *\n * The cap is checked on the same tick rather than with a `setTimeout` armed at\n * `start()`, because a paused recording must not keep counting toward it — a\n * timeout would fire while the user is paused mid-sentence.\n */\n useEffect(() => {\n if (status !== \"recording\" && status !== \"paused\") {\n setLevel(0);\n return;\n }\n const id = setInterval(() => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n const elapsed = recorder.durationMs();\n setDurationMs(elapsed);\n setLevel(status === \"recording\" ? (meterRef.current?.level() ?? 0) : 0);\n if (maxDurationMs !== undefined && elapsed >= maxDurationMs) void stop();\n }, tickMs);\n return () => clearInterval(id);\n }, [status, tickMs, maxDurationMs, stop]);\n\n const start = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n setRecording(null);\n setError(null);\n setDurationMs(0);\n recorder.start();\n setStatus(recorder.status());\n }, []);\n\n const pause = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.pause();\n setDurationMs(recorder.durationMs());\n setStatus(recorder.status());\n }, []);\n\n const resume = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.resume();\n setStatus(recorder.status());\n }, []);\n\n const cancel = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.cancel();\n setStatus(\"idle\");\n setDurationMs(0);\n setLevel(0);\n setRecording(null);\n }, []);\n\n return {\n status,\n durationMs,\n level,\n recording,\n error,\n ready,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-audio-recorder.cjs","names":["useState","useRef","useStableCallback","createAudioRecorder","createLevelMeter","useCallback","require_use_stable_callback","require_audio_recorder","require_level_meter","react","useAudioRecorder","options","status","setStatus","durationMs","setDurationMs","level","setLevel","recording","setRecording","error","setError","ready","setReady","recorderRef","meterRef","emitRecorded","onRecorded","result","emitChunk","onChunk","chunk","emitError","err","onError","stream","recorder","disableLevelMeter","mimeType","audioBitsPerSecond","timesliceMs","stop","current","id","elapsed","maxDurationMs","tickMs"],"sources":["../../src/audio/use-audio-recorder.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the hook is the recorder's lifecycle seen from React:\n * permission, the recorder handle, the clock, the level meter and the auto-stop\n * timer, all of which have to be torn down together on unmount. A second hook would\n * own half the teardown.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\nimport {\n createAudioRecorder,\n type AudioRecorderHandle,\n type AudioRecorderOptions,\n type AudioRecorderStatus,\n type AudioRecording,\n} from \"./audio-recorder\";\nimport { createLevelMeter, type LevelMeter } from \"./level-meter\";\n\n/** Options for {@link useAudioRecorder}. */\nexport interface UseAudioRecorderOptions extends AudioRecorderOptions {\n /**\n * Stop automatically after this many milliseconds.\n *\n * Worth setting on anything user-facing: a recording left running by accident is\n * a memory buffer that grows until the tab dies.\n */\n maxDurationMs?: number;\n /** Called once the recording is assembled, whether by `stop()` or `maxDurationMs`. */\n onRecorded?: (recording: AudioRecording) => void;\n /** How often the clock and the level are published, in ms. Default 100. */\n tickMs?: number;\n /** Skip the level meter — it costs an `AudioContext`. Default `false`. */\n disableLevelMeter?: boolean;\n}\n\n/** Value returned by {@link useAudioRecorder}. */\nexport interface UseAudioRecorderResult {\n status: AudioRecorderStatus;\n /** Recorded length so far, excluding paused time. */\n durationMs: number;\n /** Loudness 0–1, or `0` when the meter is off or nothing is recording. */\n level: number;\n /** The finished recording, or `null` before the first `stop()`. */\n recording: AudioRecording | null;\n /** Recorder-level error (encoder failure, device pulled mid-recording). */\n error: unknown;\n /** `false` when there is no stream yet, or the browser cannot record. */\n ready: boolean;\n start: () => void;\n pause: () => void;\n resume: () => void;\n /** Stop and resolve with the recording. Also published on `recording`. */\n stop: () => Promise<AudioRecording | null>;\n /** Stop and throw the audio away. */\n cancel: () => void;\n}\n\n/**\n * Record the given stream, with a clock, a level meter and a state machine.\n *\n * Pass the stream from {@link useMicrophone}; the hook stays `ready: false` until\n * there is one, so a page can render the whole recorder UI before permission is\n * granted and simply have it disabled.\n *\n * The clock and the level are published on an interval (`tickMs`, default 100 ms)\n * rather than per animation frame. A hook that set state 60 times a second would\n * re-render everything the recorder is inside; 10 Hz is smooth enough for a timer and\n * a meter, and the underlying `createLevelMeter` is still available for anyone who\n * wants a frame-accurate bar written straight to the DOM.\n *\n * @param stream - The stream to record, or `null` while permission is pending.\n * @param options - See {@link UseAudioRecorderOptions}.\n *\n * @example\n * const mic = useMicrophone();\n * const rec = useAudioRecorder(mic.stream, { maxDurationMs: 120_000 });\n * <button disabled={!rec.ready} onClick={rec.start}>Gravar</button>\n */\nexport function useAudioRecorder(\n stream: MediaStream | null,\n options: UseAudioRecorderOptions = {},\n): UseAudioRecorderResult {\n const {\n maxDurationMs,\n onRecorded,\n onChunk,\n onError,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n tickMs = 100,\n disableLevelMeter = false,\n } = options;\n\n const [status, setStatus] = useState<AudioRecorderStatus>(\"idle\");\n const [durationMs, setDurationMs] = useState(0);\n const [level, setLevel] = useState(0);\n const [recording, setRecording] = useState<AudioRecording | null>(null);\n const [error, setError] = useState<unknown>(null);\n const [ready, setReady] = useState(false);\n\n const recorderRef = useRef<AudioRecorderHandle | null>(null);\n const meterRef = useRef<LevelMeter | null>(null);\n\n const emitRecorded = useStableCallback((result: AudioRecording) => onRecorded?.(result));\n const emitChunk = useStableCallback((chunk: Blob) => onChunk?.(chunk));\n const emitError = useStableCallback((err: unknown) => {\n setError(err);\n onError?.(err);\n });\n\n /**\n * Build the recorder and the meter for a stream, and tear both down when it\n * changes.\n *\n * The recorder is rebuilt per stream because `MediaRecorder` is bound to the\n * stream it was constructed with — swapping microphones mid-session hands us a\n * new `MediaStream`, and reusing the old recorder would keep recording the device\n * the user just switched away from. It is rebuilt on a container or bitrate change\n * for the same reason: both are constructor arguments.\n */\n useEffect(() => {\n if (!stream) {\n setReady(false);\n return;\n }\n let recorder: AudioRecorderHandle;\n try {\n recorder = createAudioRecorder(stream, {\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n onChunk: emitChunk,\n onError: emitError,\n });\n } catch (err) {\n setError(err);\n setReady(false);\n return;\n }\n recorderRef.current = recorder;\n meterRef.current = disableLevelMeter ? null : createLevelMeter(stream);\n setReady(true);\n setStatus(\"idle\");\n\n return () => {\n if (recorder.status() === \"recording\" || recorder.status() === \"paused\") {\n recorder.cancel();\n }\n meterRef.current?.stop();\n meterRef.current = null;\n recorderRef.current = null;\n setReady(false);\n };\n }, [\n stream,\n disableLevelMeter,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n emitChunk,\n emitError,\n ]);\n\n const stop = useCallback(async (): Promise<AudioRecording | null> => {\n const recorder = recorderRef.current;\n if (!recorder) return null;\n const current = recorder.status();\n if (current !== \"recording\" && current !== \"paused\") return null;\n const result = await recorder.stop();\n setStatus(\"stopped\");\n setDurationMs(result.durationMs);\n setLevel(0);\n setRecording(result);\n emitRecorded(result);\n return result;\n }, [emitRecorded]);\n\n /**\n * Publish the clock and the level while something is running, and enforce\n * `maxDurationMs`.\n *\n * The cap is checked on the same tick rather than with a `setTimeout` armed at\n * `start()`, because a paused recording must not keep counting toward it — a\n * timeout would fire while the user is paused mid-sentence.\n */\n useEffect(() => {\n if (status !== \"recording\" && status !== \"paused\") {\n setLevel(0);\n return;\n }\n const id = setInterval(() => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n const elapsed = recorder.durationMs();\n setDurationMs(elapsed);\n setLevel(status === \"recording\" ? (meterRef.current?.level() ?? 0) : 0);\n if (maxDurationMs !== undefined && elapsed >= maxDurationMs) void stop();\n }, tickMs);\n return () => clearInterval(id);\n }, [status, tickMs, maxDurationMs, stop]);\n\n const start = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n setRecording(null);\n setError(null);\n setDurationMs(0);\n recorder.start();\n setStatus(recorder.status());\n }, []);\n\n const pause = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.pause();\n setDurationMs(recorder.durationMs());\n setStatus(recorder.status());\n }, []);\n\n const resume = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.resume();\n setStatus(recorder.status());\n }, []);\n\n const cancel = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.cancel();\n setStatus(\"idle\");\n setDurationMs(0);\n setLevel(0);\n setRecording(null);\n }, []);\n\n return {\n status,\n durationMs,\n level,\n recording,\n error,\n ready,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"mappings":"A,4IA+EA,SAAgBU,EACZ,EACA,EAAmC,CAAC,EACd,CACtB,GAAM,CACF,gBACA,aACA,UACA,UACA,WACA,qBACA,cACA,SAAS,IACT,oBAAoB,IACpBC,EAEE,CAACC,EAAQC,IAAA,EAAab,EAAAA,SAAAA,CAA8B,MAAM,EAC1D,CAACc,EAAYC,IAAA,EAAiBf,EAAAA,SAAAA,CAAS,CAAC,EACxC,CAACgB,EAAOC,IAAA,EAAYjB,EAAAA,SAAAA,CAAS,CAAC,EAC9B,CAACkB,EAAWC,IAAA,EAAgBnB,EAAAA,SAAAA,CAAgC,IAAI,EAChE,CAACoB,EAAOC,IAAA,EAAYrB,EAAAA,SAAAA,CAAkB,IAAI,EAC1C,CAACsB,EAAOC,IAAA,EAAYvB,EAAAA,SAAAA,CAAS,EAAK,EAElCwB,GAAA,EAAcvB,EAAAA,OAAAA,CAAmC,IAAI,EACrDwB,GAAA,EAAWxB,EAAAA,OAAAA,CAA0B,IAAI,EAEzCyB,EAAexB,EAAAA,kBAAmB,GAA2ByB,IAAaC,CAAM,CAAC,EACjFC,EAAY3B,EAAAA,kBAAmB,GAAgB4B,IAAUC,CAAK,CAAC,EAC/DC,EAAY9B,EAAAA,kBAAmB,GAAiB,CAClDmB,EAASY,CAAG,EACZC,IAAUD,CAAG,CACjB,CAAC,GAYD,EAAAxB,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC0B,EAAQ,CACTZ,EAAS,EAAK,EACd,MACJ,CACA,IAAIa,EACJ,GAAI,CACA,EAAWjC,EAAAA,oBAAoBgC,EAAQ,CACnC,WACA,qBACA,cACA,QAASN,EACT,QAASG,CACb,CAAC,CACL,OAASC,EAAK,CACVZ,EAASY,CAAG,EACZV,EAAS,EAAK,EACd,MACJ,CAMA,MALA,GAAY,QAAUa,EACtB,EAAS,QAAUC,EAAoB,KAAOjC,EAAAA,iBAAiB+B,CAAM,EACrEZ,EAAS,EAAI,EACbV,EAAU,MAAM,MAEH,EACLuB,EAAS,OAAO,IAAM,aAAeA,EAAS,OAAO,IAAM,WAC3DA,EAAS,OAAO,EAEpBX,EAAS,SAAS,KAAK,EACvB,EAAS,QAAU,KACnB,EAAY,QAAU,KACtBF,EAAS,EAAK,CAClB,CACJ,EAAG,CACCY,EACAE,EACAC,EACAC,EACAC,EACAX,EACAG,CACJ,CAAC,EAED,IAAMS,GAAA,EAAOpC,EAAAA,YAAAA,CAAY,SAA4C,CACjE,IAAM+B,EAAWZ,EAAY,QAC7B,GAAI,CAACY,EAAU,OAAO,KACtB,IAAMM,EAAUN,EAAS,OAAO,EAChC,GAAIM,IAAY,aAAeA,IAAY,SAAU,OAAO,KAC5D,IAAMd,EAAS,MAAMQ,EAAS,KAAK,EAMnC,OALAvB,EAAU,SAAS,EACnBE,EAAca,EAAO,UAAU,EAC/BX,EAAS,CAAC,EACVE,EAAaS,CAAM,EACnBF,EAAaE,CAAM,EACZA,CACX,EAAG,CAACF,CAAY,CAAC,EA6DjB,OAnDA,EAAAjB,EAAA,UAAA,KAAgB,CACZ,GAAIG,IAAW,aAAeA,IAAW,SAAU,CAC/CK,EAAS,CAAC,EACV,MACJ,CACA,IAAM0B,EAAK,gBAAkB,CACzB,IAAMP,EAAWZ,EAAY,QAC7B,GAAI,CAACY,EAAU,OACf,IAAMQ,EAAUR,EAAS,WAAW,EACpCrB,EAAc6B,CAAO,EACrB3B,EAASL,IAAW,YAAea,EAAS,SAAS,MAAM,GAAK,EAAK,CAAC,EAClEoB,IAAkB,IAAA,IAAaD,GAAWC,GAAeJ,EAAU,CAC3E,EAAGK,CAAM,EACT,UAAa,cAAcH,CAAE,CACjC,EAAG,CAAC/B,EAAQkC,EAAQD,EAAeJ,CAAI,CAAC,EAqCjC,CACH,SACA,aACA,QACA,YACA,QACA,QACA,OAAA,EA1CUpC,EAAAA,YAAAA,KAAwB,CAClC,IAAM+B,EAAWZ,EAAY,QACxBY,IACLjB,EAAa,IAAI,EACjBE,EAAS,IAAI,EACbN,EAAc,CAAC,EACfqB,EAAS,MAAM,EACfvB,EAAUuB,EAAS,OAAO,CAAC,EAC/B,EAAG,CAAC,CAkCA,EACA,OAAA,EAjCU/B,EAAAA,YAAAA,KAAwB,CAClC,IAAM+B,EAAWZ,EAAY,QACxBY,IACLA,EAAS,MAAM,EACfrB,EAAcqB,EAAS,WAAW,CAAC,EACnCvB,EAAUuB,EAAS,OAAO,CAAC,EAC/B,EAAG,CAAC,CA2BA,EACA,QAAA,EA1BW/B,EAAAA,YAAAA,KAAwB,CACnC,IAAM+B,EAAWZ,EAAY,QACxBY,IACLA,EAAS,OAAO,EAChBvB,EAAUuB,EAAS,OAAO,CAAC,EAC/B,EAAG,CAAC,CAqBA,EACA,OACA,QAAA,EArBW/B,EAAAA,YAAAA,KAAwB,CACnC,IAAM+B,EAAWZ,EAAY,QACxBY,IACLA,EAAS,OAAO,EAChBvB,EAAU,MAAM,EAChBE,EAAc,CAAC,EACfE,EAAS,CAAC,EACVE,EAAa,IAAI,EACrB,EAAG,CAAC,CAaA,CACJ,CACJ,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio-recorder.js","names":[],"sources":["../../src/audio/use-audio-recorder.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the hook is the recorder's lifecycle seen from React:\n * permission, the recorder handle, the clock, the level meter and the auto-stop\n * timer, all of which have to be torn down together on unmount. A second hook would\n * own half the teardown.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\nimport {\n createAudioRecorder,\n type AudioRecorderHandle,\n type AudioRecorderOptions,\n type AudioRecorderStatus,\n type AudioRecording,\n} from \"./audio-recorder\";\nimport { createLevelMeter, type LevelMeter } from \"./level-meter\";\n\n/** Options for {@link useAudioRecorder}. */\nexport interface UseAudioRecorderOptions extends AudioRecorderOptions {\n /**\n * Stop automatically after this many milliseconds.\n *\n * Worth setting on anything user-facing: a recording left running by accident is\n * a memory buffer that grows until the tab dies.\n */\n maxDurationMs?: number;\n /** Called once the recording is assembled, whether by `stop()` or `maxDurationMs`. */\n onRecorded?: (recording: AudioRecording) => void;\n /** How often the clock and the level are published, in ms. Default 100. */\n tickMs?: number;\n /** Skip the level meter — it costs an `AudioContext`. Default `false`. */\n disableLevelMeter?: boolean;\n}\n\n/** Value returned by {@link useAudioRecorder}. */\nexport interface UseAudioRecorderResult {\n status: AudioRecorderStatus;\n /** Recorded length so far, excluding paused time. */\n durationMs: number;\n /** Loudness 0–1, or `0` when the meter is off or nothing is recording. */\n level: number;\n /** The finished recording, or `null` before the first `stop()`. */\n recording: AudioRecording | null;\n /** Recorder-level error (encoder failure, device pulled mid-recording). */\n error: unknown;\n /** `false` when there is no stream yet, or the browser cannot record. */\n ready: boolean;\n start: () => void;\n pause: () => void;\n resume: () => void;\n /** Stop and resolve with the recording. Also published on `recording`. */\n stop: () => Promise<AudioRecording | null>;\n /** Stop and throw the audio away. */\n cancel: () => void;\n}\n\n/**\n * Record the given stream, with a clock, a level meter and a state machine.\n *\n * Pass the stream from {@link useMicrophone}; the hook stays `ready: false` until\n * there is one, so a page can render the whole recorder UI before permission is\n * granted and simply have it disabled.\n *\n * The clock and the level are published on an interval (`tickMs`, default 100 ms)\n * rather than per animation frame. A hook that set state 60 times a second would\n * re-render everything the recorder is inside; 10 Hz is smooth enough for a timer and\n * a meter, and the underlying `createLevelMeter` is still available for anyone who\n * wants a frame-accurate bar written straight to the DOM.\n *\n * @param stream - The stream to record, or `null` while permission is pending.\n * @param options - See {@link UseAudioRecorderOptions}.\n *\n * @example\n * const mic = useMicrophone();\n * const rec = useAudioRecorder(mic.stream, { maxDurationMs: 120_000 });\n * <button disabled={!rec.ready} onClick={rec.start}>Gravar</button>\n */\nexport function useAudioRecorder(\n stream: MediaStream | null,\n options: UseAudioRecorderOptions = {},\n): UseAudioRecorderResult {\n const {\n maxDurationMs,\n onRecorded,\n onChunk,\n onError,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n tickMs = 100,\n disableLevelMeter = false,\n } = options;\n\n const [status, setStatus] = useState<AudioRecorderStatus>(\"idle\");\n const [durationMs, setDurationMs] = useState(0);\n const [level, setLevel] = useState(0);\n const [recording, setRecording] = useState<AudioRecording | null>(null);\n const [error, setError] = useState<unknown>(null);\n const [ready, setReady] = useState(false);\n\n const recorderRef = useRef<AudioRecorderHandle | null>(null);\n const meterRef = useRef<LevelMeter | null>(null);\n\n const emitRecorded = useStableCallback((result: AudioRecording) => onRecorded?.(result));\n const emitChunk = useStableCallback((chunk: Blob) => onChunk?.(chunk));\n const emitError = useStableCallback((err: unknown) => {\n setError(err);\n onError?.(err);\n });\n\n /**\n * Build the recorder and the meter for a stream, and tear both down when it\n * changes.\n *\n * The recorder is rebuilt per stream because `MediaRecorder` is bound to the\n * stream it was constructed with — swapping microphones mid-session hands us a\n * new `MediaStream`, and reusing the old recorder would keep recording the device\n * the user just switched away from. It is rebuilt on a container or bitrate change\n * for the same reason: both are constructor arguments.\n */\n useEffect(() => {\n if (!stream) {\n setReady(false);\n return;\n }\n let recorder: AudioRecorderHandle;\n try {\n recorder = createAudioRecorder(stream, {\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n onChunk: emitChunk,\n onError: emitError,\n });\n } catch (err) {\n setError(err);\n setReady(false);\n return;\n }\n recorderRef.current = recorder;\n meterRef.current = disableLevelMeter ? null : createLevelMeter(stream);\n setReady(true);\n setStatus(\"idle\");\n\n return () => {\n if (recorder.status() === \"recording\" || recorder.status() === \"paused\") {\n recorder.cancel();\n }\n meterRef.current?.stop();\n meterRef.current = null;\n recorderRef.current = null;\n setReady(false);\n };\n }, [\n stream,\n disableLevelMeter,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n emitChunk,\n emitError,\n ]);\n\n const stop = useCallback(async (): Promise<AudioRecording | null> => {\n const recorder = recorderRef.current;\n if (!recorder) return null;\n const current = recorder.status();\n if (current !== \"recording\" && current !== \"paused\") return null;\n const result = await recorder.stop();\n setStatus(\"stopped\");\n setDurationMs(result.durationMs);\n setLevel(0);\n setRecording(result);\n emitRecorded(result);\n return result;\n }, [emitRecorded]);\n\n /**\n * Publish the clock and the level while something is running, and enforce\n * `maxDurationMs`.\n *\n * The cap is checked on the same tick rather than with a `setTimeout` armed at\n * `start()`, because a paused recording must not keep counting toward it — a\n * timeout would fire while the user is paused mid-sentence.\n */\n useEffect(() => {\n if (status !== \"recording\" && status !== \"paused\") {\n setLevel(0);\n return;\n }\n const id = setInterval(() => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n const elapsed = recorder.durationMs();\n setDurationMs(elapsed);\n setLevel(status === \"recording\" ? (meterRef.current?.level() ?? 0) : 0);\n if (maxDurationMs !== undefined && elapsed >= maxDurationMs) void stop();\n }, tickMs);\n return () => clearInterval(id);\n }, [status, tickMs, maxDurationMs, stop]);\n\n const start = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n setRecording(null);\n setError(null);\n setDurationMs(0);\n recorder.start();\n setStatus(recorder.status());\n }, []);\n\n const pause = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.pause();\n setDurationMs(recorder.durationMs());\n setStatus(recorder.status());\n }, []);\n\n const resume = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.resume();\n setStatus(recorder.status());\n }, []);\n\n const cancel = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.cancel();\n setStatus(\"idle\");\n setDurationMs(0);\n setLevel(0);\n setRecording(null);\n }, []);\n\n return {\n status,\n durationMs,\n level,\n recording,\n error,\n ready,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-audio-recorder.js","names":["useAudioRecorder","options","status","setStatus","useState","durationMs","setDurationMs","level","setLevel","recording","setRecording","error","setError","ready","setReady","recorderRef","useRef","meterRef","emitRecorded","useStableCallback","onRecorded","result","emitChunk","onChunk","chunk","emitError","err","onError","stream","recorder","createAudioRecorder","disableLevelMeter","createLevelMeter","mimeType","audioBitsPerSecond","timesliceMs","stop","useCallback","current","useEffect","id","elapsed","maxDurationMs","tickMs"],"sources":["../../src/audio/use-audio-recorder.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the hook is the recorder's lifecycle seen from React:\n * permission, the recorder handle, the clock, the level meter and the auto-stop\n * timer, all of which have to be torn down together on unmount. A second hook would\n * own half the teardown.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\nimport {\n createAudioRecorder,\n type AudioRecorderHandle,\n type AudioRecorderOptions,\n type AudioRecorderStatus,\n type AudioRecording,\n} from \"./audio-recorder\";\nimport { createLevelMeter, type LevelMeter } from \"./level-meter\";\n\n/** Options for {@link useAudioRecorder}. */\nexport interface UseAudioRecorderOptions extends AudioRecorderOptions {\n /**\n * Stop automatically after this many milliseconds.\n *\n * Worth setting on anything user-facing: a recording left running by accident is\n * a memory buffer that grows until the tab dies.\n */\n maxDurationMs?: number;\n /** Called once the recording is assembled, whether by `stop()` or `maxDurationMs`. */\n onRecorded?: (recording: AudioRecording) => void;\n /** How often the clock and the level are published, in ms. Default 100. */\n tickMs?: number;\n /** Skip the level meter — it costs an `AudioContext`. Default `false`. */\n disableLevelMeter?: boolean;\n}\n\n/** Value returned by {@link useAudioRecorder}. */\nexport interface UseAudioRecorderResult {\n status: AudioRecorderStatus;\n /** Recorded length so far, excluding paused time. */\n durationMs: number;\n /** Loudness 0–1, or `0` when the meter is off or nothing is recording. */\n level: number;\n /** The finished recording, or `null` before the first `stop()`. */\n recording: AudioRecording | null;\n /** Recorder-level error (encoder failure, device pulled mid-recording). */\n error: unknown;\n /** `false` when there is no stream yet, or the browser cannot record. */\n ready: boolean;\n start: () => void;\n pause: () => void;\n resume: () => void;\n /** Stop and resolve with the recording. Also published on `recording`. */\n stop: () => Promise<AudioRecording | null>;\n /** Stop and throw the audio away. */\n cancel: () => void;\n}\n\n/**\n * Record the given stream, with a clock, a level meter and a state machine.\n *\n * Pass the stream from {@link useMicrophone}; the hook stays `ready: false` until\n * there is one, so a page can render the whole recorder UI before permission is\n * granted and simply have it disabled.\n *\n * The clock and the level are published on an interval (`tickMs`, default 100 ms)\n * rather than per animation frame. A hook that set state 60 times a second would\n * re-render everything the recorder is inside; 10 Hz is smooth enough for a timer and\n * a meter, and the underlying `createLevelMeter` is still available for anyone who\n * wants a frame-accurate bar written straight to the DOM.\n *\n * @param stream - The stream to record, or `null` while permission is pending.\n * @param options - See {@link UseAudioRecorderOptions}.\n *\n * @example\n * const mic = useMicrophone();\n * const rec = useAudioRecorder(mic.stream, { maxDurationMs: 120_000 });\n * <button disabled={!rec.ready} onClick={rec.start}>Gravar</button>\n */\nexport function useAudioRecorder(\n stream: MediaStream | null,\n options: UseAudioRecorderOptions = {},\n): UseAudioRecorderResult {\n const {\n maxDurationMs,\n onRecorded,\n onChunk,\n onError,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n tickMs = 100,\n disableLevelMeter = false,\n } = options;\n\n const [status, setStatus] = useState<AudioRecorderStatus>(\"idle\");\n const [durationMs, setDurationMs] = useState(0);\n const [level, setLevel] = useState(0);\n const [recording, setRecording] = useState<AudioRecording | null>(null);\n const [error, setError] = useState<unknown>(null);\n const [ready, setReady] = useState(false);\n\n const recorderRef = useRef<AudioRecorderHandle | null>(null);\n const meterRef = useRef<LevelMeter | null>(null);\n\n const emitRecorded = useStableCallback((result: AudioRecording) => onRecorded?.(result));\n const emitChunk = useStableCallback((chunk: Blob) => onChunk?.(chunk));\n const emitError = useStableCallback((err: unknown) => {\n setError(err);\n onError?.(err);\n });\n\n /**\n * Build the recorder and the meter for a stream, and tear both down when it\n * changes.\n *\n * The recorder is rebuilt per stream because `MediaRecorder` is bound to the\n * stream it was constructed with — swapping microphones mid-session hands us a\n * new `MediaStream`, and reusing the old recorder would keep recording the device\n * the user just switched away from. It is rebuilt on a container or bitrate change\n * for the same reason: both are constructor arguments.\n */\n useEffect(() => {\n if (!stream) {\n setReady(false);\n return;\n }\n let recorder: AudioRecorderHandle;\n try {\n recorder = createAudioRecorder(stream, {\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n onChunk: emitChunk,\n onError: emitError,\n });\n } catch (err) {\n setError(err);\n setReady(false);\n return;\n }\n recorderRef.current = recorder;\n meterRef.current = disableLevelMeter ? null : createLevelMeter(stream);\n setReady(true);\n setStatus(\"idle\");\n\n return () => {\n if (recorder.status() === \"recording\" || recorder.status() === \"paused\") {\n recorder.cancel();\n }\n meterRef.current?.stop();\n meterRef.current = null;\n recorderRef.current = null;\n setReady(false);\n };\n }, [\n stream,\n disableLevelMeter,\n mimeType,\n audioBitsPerSecond,\n timesliceMs,\n emitChunk,\n emitError,\n ]);\n\n const stop = useCallback(async (): Promise<AudioRecording | null> => {\n const recorder = recorderRef.current;\n if (!recorder) return null;\n const current = recorder.status();\n if (current !== \"recording\" && current !== \"paused\") return null;\n const result = await recorder.stop();\n setStatus(\"stopped\");\n setDurationMs(result.durationMs);\n setLevel(0);\n setRecording(result);\n emitRecorded(result);\n return result;\n }, [emitRecorded]);\n\n /**\n * Publish the clock and the level while something is running, and enforce\n * `maxDurationMs`.\n *\n * The cap is checked on the same tick rather than with a `setTimeout` armed at\n * `start()`, because a paused recording must not keep counting toward it — a\n * timeout would fire while the user is paused mid-sentence.\n */\n useEffect(() => {\n if (status !== \"recording\" && status !== \"paused\") {\n setLevel(0);\n return;\n }\n const id = setInterval(() => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n const elapsed = recorder.durationMs();\n setDurationMs(elapsed);\n setLevel(status === \"recording\" ? (meterRef.current?.level() ?? 0) : 0);\n if (maxDurationMs !== undefined && elapsed >= maxDurationMs) void stop();\n }, tickMs);\n return () => clearInterval(id);\n }, [status, tickMs, maxDurationMs, stop]);\n\n const start = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n setRecording(null);\n setError(null);\n setDurationMs(0);\n recorder.start();\n setStatus(recorder.status());\n }, []);\n\n const pause = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.pause();\n setDurationMs(recorder.durationMs());\n setStatus(recorder.status());\n }, []);\n\n const resume = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.resume();\n setStatus(recorder.status());\n }, []);\n\n const cancel = useCallback((): void => {\n const recorder = recorderRef.current;\n if (!recorder) return;\n recorder.cancel();\n setStatus(\"idle\");\n setDurationMs(0);\n setLevel(0);\n setRecording(null);\n }, []);\n\n return {\n status,\n durationMs,\n level,\n recording,\n error,\n ready,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio.cjs","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayerHandle, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayerHandle}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayerHandle>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":"6DAyBA,SAAgB,GAA2B,CACvC,GAAM,CAAC,IAAA,EAAU,EAAA,SAAA,KAAkC,EAAA,kBAAkB,CAAC,EAChE,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAkB,EAAK,EAoBvD,OAlBA,EAAA,EAAA,UAAA,SACiB,CACT,EAAO,KAAK,CAChB,EACD,CAAC,CAAM,CAAC,EAcJ,CAAE,MAAA,EAZI,EAAA,YAAA,CACT,MAAO,EAAa,IAA8C,CAE1D,MADiB,EAAO,KAAK,EAAK,CAAO,GACjC,EAAY,EAAI,CAChC,EACA,CAAC,CAAM,CAOF,EAAM,MAAA,EAJF,EAAA,YAAA,KAAwB,CACjC,EAAO,KAAK,CAChB,EAAG,CAAC,CAAM,CAEK,EAAM,UAAS,CAClC"}
1
+ {"version":3,"file":"use-audio.cjs","names":["useState","createAudioPlayer","useCallback","require_audio_player","react","useAudio","player","unlocked","setUnlocked","src","options"],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayerHandle, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayerHandle}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayerHandle>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":"A,6DAyBA,SAAgBK,GAA2B,CACvC,GAAM,CAACC,IAAA,EAAUN,EAAAA,SAAAA,KAAkCC,EAAAA,kBAAkB,CAAC,EAChE,CAACM,EAAUC,IAAA,EAAeR,EAAAA,SAAAA,CAAkB,EAAK,EAoBvD,OAlBA,EAAAI,EAAA,UAAA,SACiB,CACTE,EAAO,KAAK,CAChB,EACD,CAACA,CAAM,CAAC,EAcJ,CAAE,MAAA,EAZIJ,EAAAA,YAAAA,CACT,MAAO,EAAa,IAA8C,CAE1D,MADiBI,EAAO,KAAKG,EAAKC,CAAO,GACjCF,EAAY,EAAI,CAChC,EACA,CAACF,CAAM,CAOF,EAAM,MAAA,EAJFJ,EAAAA,YAAAA,KAAwB,CACjCI,EAAO,KAAK,CAChB,EAAG,CAACA,CAAM,CAEK,EAAM,UAAS,CAClC,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio.js","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayerHandle, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayerHandle}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayerHandle>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":";;;AAyBA,SAAgB,IAA2B;CACvC,IAAM,CAAC,KAAU,QAAkC,EAAkB,CAAC,GAChE,CAAC,GAAU,KAAe,EAAkB,EAAK;CAoBvD,OAlBA,cACiB;EACT,EAAO,KAAK;CAChB,GACD,CAAC,CAAM,CAAC,GAcJ;EAAE,MAZI,EACT,OAAO,GAAa,MAA8C;GAE9D,AAAI,MADiB,EAAO,KAAK,GAAK,CAAO,KACjC,EAAY,EAAI;EAChC,GACA,CAAC,CAAM,CAOF;EAAM,MAJF,QAAwB;GACjC,EAAO,KAAK;EAChB,GAAG,CAAC,CAAM,CAEK;EAAM;CAAS;AAClC"}
1
+ {"version":3,"file":"use-audio.js","names":["useAudio","player","useState","createAudioPlayer","unlocked","setUnlocked","useEffect","useCallback","src","options"],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayerHandle, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayerHandle}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayerHandle>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":";;;AAyBA,SAAgBA,IAA2B;CACvC,IAAM,CAACC,KAAUC,QAAkCC,EAAkB,CAAC,GAChE,CAACC,GAAUC,KAAeH,EAAkB,EAAK;CAoBvD,OAlBAI,cACiB;EACT,EAAO,KAAK;CAChB,GACD,CAACL,CAAM,CAAC,GAcJ;EAAE,MAZIM,EACT,OAAO,GAAa,MAA8C;GAE9D,AAAI,MADiBN,EAAO,KAAKO,GAAKC,CAAO,KACjCJ,EAAY,EAAI;EAChC,GACA,CAACJ,CAAM,CAOF;EAAM,MAJFM,QAAwB;GACjC,EAAO,KAAK;EAChB,GAAG,CAACN,CAAM,CAEK;EAAM;CAAS;AAClC"}
@@ -1,2 +1,2 @@
1
- const e=require("./media-access.cjs");let t=require("react");function n(n={}){let{autoStart:r=!1,deviceId:i,echoCancellation:a=!0,noiseSuppression:o=!0,autoGainControl:s=!0,constraints:c}=n,[l,u]=(0,t.useState)(`idle`),[d,f]=(0,t.useState)(null),[p,m]=(0,t.useState)(null),h=(0,t.useRef)(null),g=(0,t.useRef)(0),_=(0,t.useCallback)(()=>{h.current?.getTracks().forEach(e=>e.stop()),h.current=null},[]),v=(0,t.useCallback)(()=>{g.current+=1,_(),f(null),u(`idle`),m(null)},[_]),y=(0,t.useCallback)(()=>{if(h.current)return;let t=g.current+=1;if(u(`requesting`),m(null),!e.isMediaCaptureSupported()){m(e.missingCaptureApiError(`microphone`)),u(`error`);return}let n=c??{audio:{echoCancellation:a,noiseSuppression:o,autoGainControl:s,...i?{deviceId:{exact:i}}:{}},video:!1};navigator.mediaDevices.getUserMedia(n).then(e=>{if(t!==g.current){e.getTracks().forEach(e=>e.stop());return}h.current=e,f(e),u(`ready`)}).catch(n=>{t===g.current&&(m(e.classifyMediaError(n,`microphone`)),u(`error`))})},[c,i,a,o,s]);return(0,t.useEffect)(()=>{r&&y()},[r]),(0,t.useEffect)(()=>_,[_]),{status:l,stream:d,error:p,start:y,stop:v}}exports.useMicrophone=n;
1
+ const e=require("./media-access.cjs");let t=require("react");function n(n={}){let{autoStart:r=!1,deviceId:i,echoCancellation:a=!0,noiseSuppression:o=!0,autoGainControl:s=!0,constraints:c}=n,[l,u]=(0,t.useState)(`idle`),[d,f]=(0,t.useState)(null),[p,m]=(0,t.useState)(null),h=(0,t.useRef)(null),g=(0,t.useRef)(0),_=(0,t.useCallback)(()=>{h.current?.getTracks().forEach(e=>e.stop()),h.current=null},[]),v=(0,t.useCallback)(()=>{g.current+=1,_(),f(null),u(`idle`),m(null)},[_]),y=(0,t.useCallback)(()=>{if(h.current)return;let t=g.current+=1;if(u(`requesting`),m(null),!e.isMediaCaptureSupported()){m(e.missingCaptureApiError(`microphone`)),u(`error`);return}let n=c??{audio:{echoCancellation:a,noiseSuppression:o,autoGainControl:s,...i?{deviceId:{exact:i}}:{}},video:!1};navigator.mediaDevices.getUserMedia(n).then(e=>{t===g.current?(h.current=e,f(e),u(`ready`)):e.getTracks().forEach(e=>e.stop())}).catch(n=>{t===g.current&&(m(e.classifyMediaError(n,`microphone`)),u(`error`))})},[c,i,a,o,s]);return(0,t.useEffect)(()=>{r&&y()},[r]),(0,t.useEffect)(()=>_,[_]),{status:l,stream:d,error:p,start:y,stop:v}}exports.useMicrophone=n;
2
2
  //# sourceMappingURL=use-microphone.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-microphone.cjs","names":[],"sources":["../../src/audio/use-microphone.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n isMediaCaptureSupported,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"./media-access\";\n\n/** Lifecycle of a microphone stream. */\nexport type MicrophoneStatus = \"idle\" | \"requesting\" | \"ready\" | \"error\";\n\n/** Options for {@link useMicrophone}. */\nexport interface UseMicrophoneOptions {\n /**\n * Open the stream on mount instead of waiting for `start()`. Default `false`.\n *\n * Left off on purpose. Opening on mount means prompting on mount, and a\n * permission prompt the user did not provoke is the most reliable way to earn a\n * permanent \"Block\" — after which `getUserMedia` rejects without ever prompting\n * again. Wire `start()` to the button that needs the microphone.\n */\n autoStart?: boolean;\n /** Specific microphone, from `useMediaDevices().audioInputs`. */\n deviceId?: string;\n /**\n * Browser voice processing. All three default to `true`, which is what speech\n * wants; turn them off for music, where a gate chewing on a decaying note is\n * worse than the room noise it removes.\n */\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n /** Escape hatch: full constraints, replacing everything above. */\n constraints?: MediaStreamConstraints;\n}\n\n/** Value returned by {@link useMicrophone}. */\nexport interface UseMicrophoneResult {\n status: MicrophoneStatus;\n /** The live stream, or `null`. Feed it to {@link useAudioRecorder} or an analyser. */\n stream: MediaStream | null;\n /** Classified error, or `null` outside the `error` status. */\n error: MediaAccessError | null;\n /** Open the stream. Safe to call when already open — it is a no-op. */\n start: () => void;\n /** Release the microphone. The browser's recording indicator only clears here. */\n stop: () => void;\n}\n\n/**\n * Acquire a microphone `MediaStream`, classified errors included, and release it\n * properly.\n *\n * Releasing matters more than it looks. Every track has to be stopped by hand:\n * dropping the last reference to a `MediaStream` does **not** turn off the\n * microphone, so the browser keeps showing its recording indicator and the OS keeps\n * the device busy — which then makes the *next* `getUserMedia` fail with\n * `NotReadableError` in another tab. The hook stops tracks on `stop()`, on unmount,\n * and before re-opening.\n *\n * @param options - See {@link UseMicrophoneOptions}.\n * @returns The stream, its status, a classified error, and `start`/`stop`.\n *\n * @example\n * const mic = useMicrophone();\n * const recorder = useAudioRecorder(mic.stream);\n * <button onClick={mic.start}>Liberar microfone</button>\n */\nexport function useMicrophone(options: UseMicrophoneOptions = {}): UseMicrophoneResult {\n const {\n autoStart = false,\n deviceId,\n echoCancellation = true,\n noiseSuppression = true,\n autoGainControl = true,\n constraints,\n } = options;\n\n const [status, setStatus] = useState<MicrophoneStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const generation = useRef(0);\n\n const release = useCallback((): void => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n setStream(null);\n setStatus(\"idle\");\n setError(null);\n }, [release]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isMediaCaptureSupported()) {\n setError(missingCaptureApiError(\"microphone\"));\n setStatus(\"error\");\n return;\n }\n\n const request: MediaStreamConstraints = constraints ?? {\n audio: {\n echoCancellation,\n noiseSuppression,\n autoGainControl,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {}),\n },\n video: false,\n };\n\n void navigator.mediaDevices\n .getUserMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n setStream(next);\n setStatus(\"ready\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n setError(classifyMediaError(err, \"microphone\"));\n setStatus(\"error\");\n });\n }, [constraints, deviceId, echoCancellation, noiseSuppression, autoGainControl]);\n\n useEffect(() => {\n if (autoStart) start();\n // `start` is stable per constraint set; re-running on every identity change\n // would reopen the device on each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoStart]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, start, stop };\n}\n"],"mappings":"6DAqEA,SAAgB,EAAc,EAAgC,CAAC,EAAwB,CACnF,GAAM,CACF,YAAY,GACZ,WACA,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,eACA,EAEE,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA2B,MAAM,EACvD,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA6B,IAAI,EACvD,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAkC,IAAI,EAC1D,GAAA,EAAY,EAAA,OAAA,CAA2B,IAAI,EAC3C,GAAA,EAAa,EAAA,OAAA,CAAO,CAAC,EAErB,GAAA,EAAU,EAAA,YAAA,KAAwB,CACpC,EAAU,SAAS,UAAU,CAAC,CAAC,QAAS,GAAU,EAAM,KAAK,CAAC,EAC9D,EAAU,QAAU,IACxB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAO,EAAA,YAAA,KAAwB,CACjC,EAAW,SAAW,EACtB,EAAQ,EACR,EAAU,IAAI,EACd,EAAU,MAAM,EAChB,EAAS,IAAI,CACjB,EAAG,CAAC,CAAO,CAAC,EAEN,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,GAAI,EAAU,QAAS,OACvB,IAAM,EAAO,EAAW,SAAW,EAInC,GAHA,EAAU,YAAY,EACtB,EAAS,IAAI,EAET,CAAC,EAAA,wBAAwB,EAAG,CAC5B,EAAS,EAAA,uBAAuB,YAAY,CAAC,EAC7C,EAAU,OAAO,EACjB,MACJ,CAEA,IAAM,EAAkC,GAAe,CACnD,MAAO,CACH,mBACA,mBACA,kBACA,GAAI,EAAW,CAAE,SAAU,CAAE,MAAO,CAAS,CAAE,EAAI,CAAC,CACxD,EACA,MAAO,EACX,EAEA,UAAe,aACV,aAAa,CAAO,CAAC,CACrB,KAAM,GAAS,CACZ,GAAI,IAAQ,EAAW,QAAS,CAC5B,EAAK,UAAU,CAAC,CAAC,QAAS,GAAU,EAAM,KAAK,CAAC,EAChD,MACJ,CACA,EAAU,QAAU,EACpB,EAAU,CAAI,EACd,EAAU,OAAO,CACrB,CAAC,CAAC,CACD,MAAO,GAAiB,CACjB,IAAQ,EAAW,UACvB,EAAS,EAAA,mBAAmB,EAAK,YAAY,CAAC,EAC9C,EAAU,OAAO,EACrB,CAAC,CACT,EAAG,CAAC,EAAa,EAAU,EAAkB,EAAkB,CAAe,CAAC,EAW/E,OATA,EAAA,EAAA,UAAA,KAAgB,CACR,GAAW,EAAM,CAIzB,EAAG,CAAC,CAAS,CAAC,GAEd,EAAA,EAAA,UAAA,KAAgB,EAAS,CAAC,CAAO,CAAC,EAE3B,CAAE,SAAQ,SAAQ,QAAO,QAAO,MAAK,CAChD"}
1
+ {"version":3,"file":"use-microphone.cjs","names":["useState","useRef","useCallback","isMediaCaptureSupported","missingCaptureApiError","classifyMediaError","require_media_access","react","useMicrophone","options","status","setStatus","stream","setStream","error","setError","streamRef","generation","release","track","stop","start","run","request","constraints","deviceId","next","err","echoCancellation","noiseSuppression","autoGainControl","autoStart"],"sources":["../../src/audio/use-microphone.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n isMediaCaptureSupported,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"./media-access\";\n\n/** Lifecycle of a microphone stream. */\nexport type MicrophoneStatus = \"idle\" | \"requesting\" | \"ready\" | \"error\";\n\n/** Options for {@link useMicrophone}. */\nexport interface UseMicrophoneOptions {\n /**\n * Open the stream on mount instead of waiting for `start()`. Default `false`.\n *\n * Left off on purpose. Opening on mount means prompting on mount, and a\n * permission prompt the user did not provoke is the most reliable way to earn a\n * permanent \"Block\" — after which `getUserMedia` rejects without ever prompting\n * again. Wire `start()` to the button that needs the microphone.\n */\n autoStart?: boolean;\n /** Specific microphone, from `useMediaDevices().audioInputs`. */\n deviceId?: string;\n /**\n * Browser voice processing. All three default to `true`, which is what speech\n * wants; turn them off for music, where a gate chewing on a decaying note is\n * worse than the room noise it removes.\n */\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n /** Escape hatch: full constraints, replacing everything above. */\n constraints?: MediaStreamConstraints;\n}\n\n/** Value returned by {@link useMicrophone}. */\nexport interface UseMicrophoneResult {\n status: MicrophoneStatus;\n /** The live stream, or `null`. Feed it to {@link useAudioRecorder} or an analyser. */\n stream: MediaStream | null;\n /** Classified error, or `null` outside the `error` status. */\n error: MediaAccessError | null;\n /** Open the stream. Safe to call when already open — it is a no-op. */\n start: () => void;\n /** Release the microphone. The browser's recording indicator only clears here. */\n stop: () => void;\n}\n\n/**\n * Acquire a microphone `MediaStream`, classified errors included, and release it\n * properly.\n *\n * Releasing matters more than it looks. Every track has to be stopped by hand:\n * dropping the last reference to a `MediaStream` does **not** turn off the\n * microphone, so the browser keeps showing its recording indicator and the OS keeps\n * the device busy — which then makes the *next* `getUserMedia` fail with\n * `NotReadableError` in another tab. The hook stops tracks on `stop()`, on unmount,\n * and before re-opening.\n *\n * @param options - See {@link UseMicrophoneOptions}.\n * @returns The stream, its status, a classified error, and `start`/`stop`.\n *\n * @example\n * const mic = useMicrophone();\n * const recorder = useAudioRecorder(mic.stream);\n * <button onClick={mic.start}>Liberar microfone</button>\n */\nexport function useMicrophone(options: UseMicrophoneOptions = {}): UseMicrophoneResult {\n const {\n autoStart = false,\n deviceId,\n echoCancellation = true,\n noiseSuppression = true,\n autoGainControl = true,\n constraints,\n } = options;\n\n const [status, setStatus] = useState<MicrophoneStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const generation = useRef(0);\n\n const release = useCallback((): void => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n setStream(null);\n setStatus(\"idle\");\n setError(null);\n }, [release]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isMediaCaptureSupported()) {\n setError(missingCaptureApiError(\"microphone\"));\n setStatus(\"error\");\n return;\n }\n\n const request: MediaStreamConstraints = constraints ?? {\n audio: {\n echoCancellation,\n noiseSuppression,\n autoGainControl,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {}),\n },\n video: false,\n };\n\n void navigator.mediaDevices\n .getUserMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n setStream(next);\n setStatus(\"ready\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n setError(classifyMediaError(err, \"microphone\"));\n setStatus(\"error\");\n });\n }, [constraints, deviceId, echoCancellation, noiseSuppression, autoGainControl]);\n\n useEffect(() => {\n if (autoStart) start();\n // `start` is stable per constraint set; re-running on every identity change\n // would reopen the device on each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoStart]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, start, stop };\n}\n"],"mappings":"A,6DAqEA,SAAgBQ,EAAc,EAAgC,CAAC,EAAwB,CACnF,GAAM,CACF,YAAY,GACZ,WACA,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,eACAC,EAEE,CAACC,EAAQC,IAAA,EAAaX,EAAAA,SAAAA,CAA2B,MAAM,EACvD,CAACY,EAAQC,IAAA,EAAab,EAAAA,SAAAA,CAA6B,IAAI,EACvD,CAACc,EAAOC,IAAA,EAAYf,EAAAA,SAAAA,CAAkC,IAAI,EAC1DgB,GAAA,EAAYf,EAAAA,OAAAA,CAA2B,IAAI,EAC3CgB,GAAA,EAAahB,EAAAA,OAAAA,CAAO,CAAC,EAErBiB,GAAA,EAAUhB,EAAAA,YAAAA,KAAwB,CACpCc,EAAU,SAAS,UAAU,CAAC,CAAC,QAAS,GAAUG,EAAM,KAAK,CAAC,EAC9D,EAAU,QAAU,IACxB,EAAG,CAAC,CAAC,EAECC,GAAA,EAAOlB,EAAAA,YAAAA,KAAwB,CACjC,EAAW,SAAW,EACtBgB,EAAQ,EACRL,EAAU,IAAI,EACdF,EAAU,MAAM,EAChBI,EAAS,IAAI,CACjB,EAAG,CAACG,CAAO,CAAC,EAENG,GAAA,EAAQnB,EAAAA,YAAAA,KAAwB,CAClC,GAAIc,EAAU,QAAS,OACvB,IAAMM,EAAO,EAAW,SAAW,EAInC,GAHAX,EAAU,YAAY,EACtBI,EAAS,IAAI,EAET,CAACZ,EAAAA,wBAAwB,EAAG,CAC5BY,EAASX,EAAAA,uBAAuB,YAAY,CAAC,EAC7CO,EAAU,OAAO,EACjB,MACJ,CAEA,IAAMY,EAAkCC,GAAe,CACnD,MAAO,CACH,mBACA,mBACA,kBACA,GAAIC,EAAW,CAAE,SAAU,CAAE,MAAOA,CAAS,CAAE,EAAI,CAAC,CACxD,EACA,MAAO,EACX,EAEA,UAAe,aACV,aAAaF,CAAO,CAAC,CACrB,KAAM,GAAS,CACRD,IAAQL,EAAW,SAIvB,EAAU,QAAUS,EACpBb,EAAUa,CAAI,EACdf,EAAU,OAAO,GALbe,EAAK,UAAU,CAAC,CAAC,QAAS,GAAUP,EAAM,KAAK,CAAC,CAMxD,CAAC,CAAC,CACD,MAAO,GAAiB,CACjBG,IAAQL,EAAW,UACvBF,EAASV,EAAAA,mBAAmBsB,EAAK,YAAY,CAAC,EAC9ChB,EAAU,OAAO,EACrB,CAAC,CACT,EAAG,CAACa,EAAaC,EAAUG,EAAkBC,EAAkBC,CAAe,CAAC,EAW/E,OATA,EAAAvB,EAAA,UAAA,KAAgB,CACRwB,GAAWV,EAAM,CAIzB,EAAG,CAACU,CAAS,CAAC,GAEd,EAAAxB,EAAA,UAAA,KAAgBW,EAAS,CAACA,CAAO,CAAC,EAE3B,CAAE,SAAQ,SAAQ,QAAO,QAAO,MAAK,CAChD,C"}
@@ -23,11 +23,7 @@ function s(s = {}) {
23
23
  video: !1
24
24
  };
25
25
  navigator.mediaDevices.getUserMedia(i).then((e) => {
26
- if (r !== x.current) {
27
- e.getTracks().forEach((e) => e.stop());
28
- return;
29
- }
30
- b.current = e, _(e), h("ready");
26
+ r === x.current ? (b.current = e, _(e), h("ready")) : e.getTracks().forEach((e) => e.stop());
31
27
  }).catch((t) => {
32
28
  r === x.current && (y(e(t, "microphone")), h("error"));
33
29
  });
@@ -1 +1 @@
1
- {"version":3,"file":"use-microphone.js","names":[],"sources":["../../src/audio/use-microphone.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n isMediaCaptureSupported,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"./media-access\";\n\n/** Lifecycle of a microphone stream. */\nexport type MicrophoneStatus = \"idle\" | \"requesting\" | \"ready\" | \"error\";\n\n/** Options for {@link useMicrophone}. */\nexport interface UseMicrophoneOptions {\n /**\n * Open the stream on mount instead of waiting for `start()`. Default `false`.\n *\n * Left off on purpose. Opening on mount means prompting on mount, and a\n * permission prompt the user did not provoke is the most reliable way to earn a\n * permanent \"Block\" — after which `getUserMedia` rejects without ever prompting\n * again. Wire `start()` to the button that needs the microphone.\n */\n autoStart?: boolean;\n /** Specific microphone, from `useMediaDevices().audioInputs`. */\n deviceId?: string;\n /**\n * Browser voice processing. All three default to `true`, which is what speech\n * wants; turn them off for music, where a gate chewing on a decaying note is\n * worse than the room noise it removes.\n */\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n /** Escape hatch: full constraints, replacing everything above. */\n constraints?: MediaStreamConstraints;\n}\n\n/** Value returned by {@link useMicrophone}. */\nexport interface UseMicrophoneResult {\n status: MicrophoneStatus;\n /** The live stream, or `null`. Feed it to {@link useAudioRecorder} or an analyser. */\n stream: MediaStream | null;\n /** Classified error, or `null` outside the `error` status. */\n error: MediaAccessError | null;\n /** Open the stream. Safe to call when already open — it is a no-op. */\n start: () => void;\n /** Release the microphone. The browser's recording indicator only clears here. */\n stop: () => void;\n}\n\n/**\n * Acquire a microphone `MediaStream`, classified errors included, and release it\n * properly.\n *\n * Releasing matters more than it looks. Every track has to be stopped by hand:\n * dropping the last reference to a `MediaStream` does **not** turn off the\n * microphone, so the browser keeps showing its recording indicator and the OS keeps\n * the device busy — which then makes the *next* `getUserMedia` fail with\n * `NotReadableError` in another tab. The hook stops tracks on `stop()`, on unmount,\n * and before re-opening.\n *\n * @param options - See {@link UseMicrophoneOptions}.\n * @returns The stream, its status, a classified error, and `start`/`stop`.\n *\n * @example\n * const mic = useMicrophone();\n * const recorder = useAudioRecorder(mic.stream);\n * <button onClick={mic.start}>Liberar microfone</button>\n */\nexport function useMicrophone(options: UseMicrophoneOptions = {}): UseMicrophoneResult {\n const {\n autoStart = false,\n deviceId,\n echoCancellation = true,\n noiseSuppression = true,\n autoGainControl = true,\n constraints,\n } = options;\n\n const [status, setStatus] = useState<MicrophoneStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const generation = useRef(0);\n\n const release = useCallback((): void => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n setStream(null);\n setStatus(\"idle\");\n setError(null);\n }, [release]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isMediaCaptureSupported()) {\n setError(missingCaptureApiError(\"microphone\"));\n setStatus(\"error\");\n return;\n }\n\n const request: MediaStreamConstraints = constraints ?? {\n audio: {\n echoCancellation,\n noiseSuppression,\n autoGainControl,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {}),\n },\n video: false,\n };\n\n void navigator.mediaDevices\n .getUserMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n setStream(next);\n setStatus(\"ready\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n setError(classifyMediaError(err, \"microphone\"));\n setStatus(\"error\");\n });\n }, [constraints, deviceId, echoCancellation, noiseSuppression, autoGainControl]);\n\n useEffect(() => {\n if (autoStart) start();\n // `start` is stable per constraint set; re-running on every identity change\n // would reopen the device on each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoStart]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, start, stop };\n}\n"],"mappings":";;;AAqEA,SAAgB,EAAc,IAAgC,CAAC,GAAwB;CACnF,IAAM,EACF,eAAY,IACZ,aACA,sBAAmB,IACnB,sBAAmB,IACnB,qBAAkB,IAClB,mBACA,GAEE,CAAC,GAAQ,KAAa,EAA2B,MAAM,GACvD,CAAC,GAAQ,KAAa,EAA6B,IAAI,GACvD,CAAC,GAAO,KAAY,EAAkC,IAAI,GAC1D,IAAY,EAA2B,IAAI,GAC3C,IAAa,EAAO,CAAC,GAErB,IAAU,QAAwB;EAEpC,AADA,EAAU,SAAS,UAAU,CAAC,CAAC,SAAS,MAAU,EAAM,KAAK,CAAC,GAC9D,EAAU,UAAU;CACxB,GAAG,CAAC,CAAC,GAEC,IAAO,QAAwB;EAKjC,AAJA,EAAW,WAAW,GACtB,EAAQ,GACR,EAAU,IAAI,GACd,EAAU,MAAM,GAChB,EAAS,IAAI;CACjB,GAAG,CAAC,CAAO,CAAC,GAEN,IAAQ,QAAwB;EAClC,IAAI,EAAU,SAAS;EACvB,IAAM,IAAO,EAAW,WAAW;EAInC,IAHA,EAAU,YAAY,GACtB,EAAS,IAAI,GAET,CAAC,EAAwB,GAAG;GAE5B,AADA,EAAS,EAAuB,YAAY,CAAC,GAC7C,EAAU,OAAO;GACjB;EACJ;EAEA,IAAM,IAAkC,KAAe;GACnD,OAAO;IACH;IACA;IACA;IACA,GAAI,IAAW,EAAE,UAAU,EAAE,OAAO,EAAS,EAAE,IAAI,CAAC;GACxD;GACA,OAAO;EACX;EAEA,UAAe,aACV,aAAa,CAAO,CAAC,CACrB,MAAM,MAAS;GACZ,IAAI,MAAQ,EAAW,SAAS;IAC5B,EAAK,UAAU,CAAC,CAAC,SAAS,MAAU,EAAM,KAAK,CAAC;IAChD;GACJ;GAGA,AAFA,EAAU,UAAU,GACpB,EAAU,CAAI,GACd,EAAU,OAAO;EACrB,CAAC,CAAC,CACD,OAAO,MAAiB;GACjB,MAAQ,EAAW,YACvB,EAAS,EAAmB,GAAK,YAAY,CAAC,GAC9C,EAAU,OAAO;EACrB,CAAC;CACT,GAAG;EAAC;EAAa;EAAU;EAAkB;EAAkB;CAAe,CAAC;CAW/E,OATA,QAAgB;EACZ,AAAI,KAAW,EAAM;CAIzB,GAAG,CAAC,CAAS,CAAC,GAEd,QAAgB,GAAS,CAAC,CAAO,CAAC,GAE3B;EAAE;EAAQ;EAAQ;EAAO;EAAO;CAAK;AAChD"}
1
+ {"version":3,"file":"use-microphone.js","names":["useMicrophone","options","status","setStatus","useState","stream","setStream","error","setError","streamRef","useRef","generation","release","useCallback","track","stop","start","run","isMediaCaptureSupported","missingCaptureApiError","request","constraints","deviceId","next","classifyMediaError","err","echoCancellation","noiseSuppression","autoGainControl","useEffect","autoStart"],"sources":["../../src/audio/use-microphone.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n isMediaCaptureSupported,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"./media-access\";\n\n/** Lifecycle of a microphone stream. */\nexport type MicrophoneStatus = \"idle\" | \"requesting\" | \"ready\" | \"error\";\n\n/** Options for {@link useMicrophone}. */\nexport interface UseMicrophoneOptions {\n /**\n * Open the stream on mount instead of waiting for `start()`. Default `false`.\n *\n * Left off on purpose. Opening on mount means prompting on mount, and a\n * permission prompt the user did not provoke is the most reliable way to earn a\n * permanent \"Block\" — after which `getUserMedia` rejects without ever prompting\n * again. Wire `start()` to the button that needs the microphone.\n */\n autoStart?: boolean;\n /** Specific microphone, from `useMediaDevices().audioInputs`. */\n deviceId?: string;\n /**\n * Browser voice processing. All three default to `true`, which is what speech\n * wants; turn them off for music, where a gate chewing on a decaying note is\n * worse than the room noise it removes.\n */\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n /** Escape hatch: full constraints, replacing everything above. */\n constraints?: MediaStreamConstraints;\n}\n\n/** Value returned by {@link useMicrophone}. */\nexport interface UseMicrophoneResult {\n status: MicrophoneStatus;\n /** The live stream, or `null`. Feed it to {@link useAudioRecorder} or an analyser. */\n stream: MediaStream | null;\n /** Classified error, or `null` outside the `error` status. */\n error: MediaAccessError | null;\n /** Open the stream. Safe to call when already open — it is a no-op. */\n start: () => void;\n /** Release the microphone. The browser's recording indicator only clears here. */\n stop: () => void;\n}\n\n/**\n * Acquire a microphone `MediaStream`, classified errors included, and release it\n * properly.\n *\n * Releasing matters more than it looks. Every track has to be stopped by hand:\n * dropping the last reference to a `MediaStream` does **not** turn off the\n * microphone, so the browser keeps showing its recording indicator and the OS keeps\n * the device busy — which then makes the *next* `getUserMedia` fail with\n * `NotReadableError` in another tab. The hook stops tracks on `stop()`, on unmount,\n * and before re-opening.\n *\n * @param options - See {@link UseMicrophoneOptions}.\n * @returns The stream, its status, a classified error, and `start`/`stop`.\n *\n * @example\n * const mic = useMicrophone();\n * const recorder = useAudioRecorder(mic.stream);\n * <button onClick={mic.start}>Liberar microfone</button>\n */\nexport function useMicrophone(options: UseMicrophoneOptions = {}): UseMicrophoneResult {\n const {\n autoStart = false,\n deviceId,\n echoCancellation = true,\n noiseSuppression = true,\n autoGainControl = true,\n constraints,\n } = options;\n\n const [status, setStatus] = useState<MicrophoneStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const generation = useRef(0);\n\n const release = useCallback((): void => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n setStream(null);\n setStatus(\"idle\");\n setError(null);\n }, [release]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isMediaCaptureSupported()) {\n setError(missingCaptureApiError(\"microphone\"));\n setStatus(\"error\");\n return;\n }\n\n const request: MediaStreamConstraints = constraints ?? {\n audio: {\n echoCancellation,\n noiseSuppression,\n autoGainControl,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {}),\n },\n video: false,\n };\n\n void navigator.mediaDevices\n .getUserMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n setStream(next);\n setStatus(\"ready\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n setError(classifyMediaError(err, \"microphone\"));\n setStatus(\"error\");\n });\n }, [constraints, deviceId, echoCancellation, noiseSuppression, autoGainControl]);\n\n useEffect(() => {\n if (autoStart) start();\n // `start` is stable per constraint set; re-running on every identity change\n // would reopen the device on each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoStart]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, start, stop };\n}\n"],"mappings":";;;AAqEA,SAAgBA,EAAc,IAAgC,CAAC,GAAwB;CACnF,IAAM,EACF,eAAY,IACZ,aACA,sBAAmB,IACnB,sBAAmB,IACnB,qBAAkB,IAClB,mBACAC,GAEE,CAACC,GAAQC,KAAaC,EAA2B,MAAM,GACvD,CAACC,GAAQC,KAAaF,EAA6B,IAAI,GACvD,CAACG,GAAOC,KAAYJ,EAAkC,IAAI,GAC1DK,IAAYC,EAA2B,IAAI,GAC3CC,IAAaD,EAAO,CAAC,GAErBE,IAAUC,QAAwB;EAEpC,AADAJ,EAAU,SAAS,UAAU,CAAC,CAAC,SAAS,MAAUK,EAAM,KAAK,CAAC,GAC9D,EAAU,UAAU;CACxB,GAAG,CAAC,CAAC,GAECC,IAAOF,QAAwB;EAKjC,AAJA,EAAW,WAAW,GACtBD,EAAQ,GACRN,EAAU,IAAI,GACdH,EAAU,MAAM,GAChBK,EAAS,IAAI;CACjB,GAAG,CAACI,CAAO,CAAC,GAENI,IAAQH,QAAwB;EAClC,IAAIJ,EAAU,SAAS;EACvB,IAAMQ,IAAO,EAAW,WAAW;EAInC,IAHAd,EAAU,YAAY,GACtBK,EAAS,IAAI,GAET,CAACU,EAAwB,GAAG;GAE5B,AADAV,EAASW,EAAuB,YAAY,CAAC,GAC7ChB,EAAU,OAAO;GACjB;EACJ;EAEA,IAAMiB,IAAkCC,KAAe;GACnD,OAAO;IACH;IACA;IACA;IACA,GAAIC,IAAW,EAAE,UAAU,EAAE,OAAOA,EAAS,EAAE,IAAI,CAAC;GACxD;GACA,OAAO;EACX;EAEA,UAAe,aACV,aAAaF,CAAO,CAAC,CACrB,MAAM,MAAS;GACZ,AAAIH,MAAQN,EAAW,WAIvB,EAAU,UAAUY,GACpBjB,EAAUiB,CAAI,GACdpB,EAAU,OAAO,KALboB,EAAK,UAAU,CAAC,CAAC,SAAS,MAAUT,EAAM,KAAK,CAAC;EAMxD,CAAC,CAAC,CACD,OAAO,MAAiB;GACrB,AAAIG,MAAQN,EAAW,YACvBH,EAASgB,EAAmBC,GAAK,YAAY,CAAC,GAC9CtB,EAAU,OAAO;EACrB,CAAC;CACT,GAAG;EAACkB;EAAaC;EAAUI;EAAkBC;EAAkBC;CAAe,CAAC;CAW/E,OATAC,QAAgB;EACZ,AAAIC,KAAWd,EAAM;CAIzB,GAAG,CAACc,CAAS,CAAC,GAEdD,QAAgBjB,GAAS,CAACA,CAAO,CAAC,GAE3B;EAAE;EAAQ;EAAQ;EAAO;EAAO;CAAK;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-sfx-pool.cjs","names":[],"sources":["../../src/audio/use-sfx-pool.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createSfxPool, type SfxPool, type SfxPoolOptions } from \"./sfx-pool\";\n\n/**\n * Component-scoped {@link createSfxPool}, with the lifecycle wired up.\n *\n * The pool is created once — through a lazy `useState` initializer, so no\n * element is allocated on a render that gets discarded — and disposed on\n * unmount, so its elements do not outlive the screen that owns them.\n *\n * `volume` is tracked separately: changing it calls `setVolume` on the existing\n * pool rather than rebuilding it, which would throw away every clip the user\n * has already downloaded — exactly the cost the pool exists to avoid.\n * `baseUrl`, `voices` and `maxSources` are read once, at creation. The options\n * object is forwarded whole rather than rebuilt field by field, so a field added\n * to {@link SfxPoolOptions} does not silently stop reaching the pool.\n *\n * @param options - Passed through to {@link createSfxPool}.\n * @returns A stable pool handle.\n *\n * @example\n * const sfx = useSfxPool({ volume: settings.sfxVolume / 100, baseUrl: import.meta.env.BASE_URL });\n *\n * useEffect(() => { sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]); }, [sfx]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\nexport function useSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { volume = 1 } = options;\n\n const [pool] = useState(() => createSfxPool(options));\n\n useEffect(() => {\n pool.setVolume(volume);\n }, [pool, volume]);\n\n useEffect(() => () => pool.dispose(), [pool]);\n\n return pool;\n}\n"],"mappings":"yDA2BA,SAAgB,EAAW,EAA0B,CAAC,EAAY,CAC9D,GAAM,CAAE,SAAS,GAAM,EAEjB,CAAC,IAAA,EAAQ,EAAA,SAAA,KAAe,EAAA,cAAc,CAAO,CAAC,EAQpD,OANA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAK,UAAU,CAAM,CACzB,EAAG,CAAC,EAAM,CAAM,CAAC,GAEjB,EAAA,EAAA,UAAA,SAAsB,EAAK,QAAQ,EAAG,CAAC,CAAI,CAAC,EAErC,CACX"}
1
+ {"version":3,"file":"use-sfx-pool.cjs","names":["useState","createSfxPool","require_sfx_pool","react","useSfxPool","options","pool","volume"],"sources":["../../src/audio/use-sfx-pool.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createSfxPool, type SfxPool, type SfxPoolOptions } from \"./sfx-pool\";\n\n/**\n * Component-scoped {@link createSfxPool}, with the lifecycle wired up.\n *\n * The pool is created once — through a lazy `useState` initializer, so no\n * element is allocated on a render that gets discarded — and disposed on\n * unmount, so its elements do not outlive the screen that owns them.\n *\n * `volume` is tracked separately: changing it calls `setVolume` on the existing\n * pool rather than rebuilding it, which would throw away every clip the user\n * has already downloaded — exactly the cost the pool exists to avoid.\n * `baseUrl`, `voices` and `maxSources` are read once, at creation. The options\n * object is forwarded whole rather than rebuilt field by field, so a field added\n * to {@link SfxPoolOptions} does not silently stop reaching the pool.\n *\n * @param options - Passed through to {@link createSfxPool}.\n * @returns A stable pool handle.\n *\n * @example\n * const sfx = useSfxPool({ volume: settings.sfxVolume / 100, baseUrl: import.meta.env.BASE_URL });\n *\n * useEffect(() => { sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]); }, [sfx]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\nexport function useSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { volume = 1 } = options;\n\n const [pool] = useState(() => createSfxPool(options));\n\n useEffect(() => {\n pool.setVolume(volume);\n }, [pool, volume]);\n\n useEffect(() => () => pool.dispose(), [pool]);\n\n return pool;\n}\n"],"mappings":"A,yDA2BA,SAAgBI,EAAW,EAA0B,CAAC,EAAY,CAC9D,GAAM,CAAE,SAAS,GAAMC,EAEjB,CAACC,IAAA,EAAQN,EAAAA,SAAAA,KAAeC,EAAAA,cAAcI,CAAO,CAAC,EAQpD,OANA,EAAAF,EAAA,UAAA,KAAgB,CACZG,EAAK,UAAUC,CAAM,CACzB,EAAG,CAACD,EAAMC,CAAM,CAAC,GAEjB,EAAAJ,EAAA,UAAA,SAAsBG,EAAK,QAAQ,EAAG,CAACA,CAAI,CAAC,EAErCA,CACX,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-sfx-pool.js","names":[],"sources":["../../src/audio/use-sfx-pool.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createSfxPool, type SfxPool, type SfxPoolOptions } from \"./sfx-pool\";\n\n/**\n * Component-scoped {@link createSfxPool}, with the lifecycle wired up.\n *\n * The pool is created once — through a lazy `useState` initializer, so no\n * element is allocated on a render that gets discarded — and disposed on\n * unmount, so its elements do not outlive the screen that owns them.\n *\n * `volume` is tracked separately: changing it calls `setVolume` on the existing\n * pool rather than rebuilding it, which would throw away every clip the user\n * has already downloaded — exactly the cost the pool exists to avoid.\n * `baseUrl`, `voices` and `maxSources` are read once, at creation. The options\n * object is forwarded whole rather than rebuilt field by field, so a field added\n * to {@link SfxPoolOptions} does not silently stop reaching the pool.\n *\n * @param options - Passed through to {@link createSfxPool}.\n * @returns A stable pool handle.\n *\n * @example\n * const sfx = useSfxPool({ volume: settings.sfxVolume / 100, baseUrl: import.meta.env.BASE_URL });\n *\n * useEffect(() => { sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]); }, [sfx]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\nexport function useSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { volume = 1 } = options;\n\n const [pool] = useState(() => createSfxPool(options));\n\n useEffect(() => {\n pool.setVolume(volume);\n }, [pool, volume]);\n\n useEffect(() => () => pool.dispose(), [pool]);\n\n return pool;\n}\n"],"mappings":";;;AA2BA,SAAgB,EAAW,IAA0B,CAAC,GAAY;CAC9D,IAAM,EAAE,YAAS,MAAM,GAEjB,CAAC,KAAQ,QAAe,EAAc,CAAO,CAAC;CAQpD,OANA,QAAgB;EACZ,EAAK,UAAU,CAAM;CACzB,GAAG,CAAC,GAAM,CAAM,CAAC,GAEjB,cAAsB,EAAK,QAAQ,GAAG,CAAC,CAAI,CAAC,GAErC;AACX"}
1
+ {"version":3,"file":"use-sfx-pool.js","names":["useSfxPool","options","pool","useState","createSfxPool","useEffect","volume"],"sources":["../../src/audio/use-sfx-pool.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createSfxPool, type SfxPool, type SfxPoolOptions } from \"./sfx-pool\";\n\n/**\n * Component-scoped {@link createSfxPool}, with the lifecycle wired up.\n *\n * The pool is created once — through a lazy `useState` initializer, so no\n * element is allocated on a render that gets discarded — and disposed on\n * unmount, so its elements do not outlive the screen that owns them.\n *\n * `volume` is tracked separately: changing it calls `setVolume` on the existing\n * pool rather than rebuilding it, which would throw away every clip the user\n * has already downloaded — exactly the cost the pool exists to avoid.\n * `baseUrl`, `voices` and `maxSources` are read once, at creation. The options\n * object is forwarded whole rather than rebuilt field by field, so a field added\n * to {@link SfxPoolOptions} does not silently stop reaching the pool.\n *\n * @param options - Passed through to {@link createSfxPool}.\n * @returns A stable pool handle.\n *\n * @example\n * const sfx = useSfxPool({ volume: settings.sfxVolume / 100, baseUrl: import.meta.env.BASE_URL });\n *\n * useEffect(() => { sfx.preload([\"sfx/select.mp3\", \"sfx/back.mp3\"]); }, [sfx]);\n *\n * <button onClick={() => sfx.play(\"sfx/select.mp3\")}>Confirmar</button>\n */\nexport function useSfxPool(options: SfxPoolOptions = {}): SfxPool {\n const { volume = 1 } = options;\n\n const [pool] = useState(() => createSfxPool(options));\n\n useEffect(() => {\n pool.setVolume(volume);\n }, [pool, volume]);\n\n useEffect(() => () => pool.dispose(), [pool]);\n\n return pool;\n}\n"],"mappings":";;;AA2BA,SAAgBA,EAAW,IAA0B,CAAC,GAAY;CAC9D,IAAM,EAAE,YAAS,MAAMC,GAEjB,CAACC,KAAQC,QAAeC,EAAcH,CAAO,CAAC;CAQpD,OANAI,QAAgB;EACZ,EAAK,UAAUC,CAAM;CACzB,GAAG,CAACJ,GAAMI,CAAM,CAAC,GAEjBD,cAAsBH,EAAK,QAAQ,GAAG,CAACA,CAAI,CAAC,GAErCA;AACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"voice-activity.cjs","names":[],"sources":["../../src/audio/voice-activity.ts"],"sourcesContent":["import { createLevelMeter } from \"./level-meter\";\n\n/** RMS at which a window counts as speech. */\nconst DEFAULT_THRESHOLD = 0.045;\n\n/**\n * How long the indicator stays on after the level drops.\n *\n * RMS falls to nothing in the gaps between words, so a bare threshold strobes on\n * every one of them. Ported from `tempest-mirror-screen`'s `voiceActivity.ts`,\n * where it was arrived at by watching a real call.\n */\nconst DEFAULT_RELEASE_MS = 350;\n\n/** How often the level is read. */\nconst DEFAULT_POLL_MS = 100;\n\n/** Options for {@link monitorVoiceActivity}. */\nexport interface VoiceActivityOptions {\n /** RMS at or above which a window counts as speech. Default `0.045`. */\n threshold?: number;\n /** How long the state stays `true` after the level drops. Default `350`. */\n releaseMs?: number;\n /** How often the level is sampled, in ms. Default `100`. */\n pollMs?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * A monitor per remote participant hits the browser's cap on live contexts\n * (Chrome allows around six) in a five-person call. Open one and share it.\n */\n context?: AudioContext;\n}\n\n/** A running voice-activity monitor. */\nexport interface VoiceActivityMonitor {\n /** Whether the track is currently carrying speech. */\n readonly speaking: boolean;\n /** Stop sampling and release the analysis graph. */\n stop: () => void;\n}\n\n/**\n * Report when a track is carrying speech.\n *\n * Runs entirely on the **receiving** side, over a track the connection already\n * delivers, and that is the whole point: a speaking indicator changes several\n * times per second, so signalling it through a server floods the room with\n * messages carrying information every client can derive for free.\n *\n * Two details separate this from a threshold on a level meter:\n *\n * 1. **The release.** RMS drops to nothing in the gaps between words, so a bare\n * comparison strobes on every syllable. The state only falls after\n * `releaseMs` of quiet.\n * 2. **`onChange` fires on the flip, not on the sample.** A callback per poll\n * would re-render a participant list ten times a second to say the same thing.\n *\n * The meter underneath runs with smoothing off: its eased decay plus this release\n * would hold the indicator open long after the speech stopped.\n *\n * @param stream - The stream whose first audio track is measured.\n * @param onChange - Called only when the speaking state actually flips.\n * @param options - See {@link VoiceActivityOptions}.\n * @returns A handle carrying the current state and the teardown.\n *\n * @example\n * const vad = monitorVoiceActivity(remoteStream, (speaking) => setSpeaking(speaking));\n * // later\n * vad.stop();\n */\nexport function monitorVoiceActivity(\n stream: MediaStream,\n onChange: (speaking: boolean) => void,\n {\n threshold = DEFAULT_THRESHOLD,\n releaseMs = DEFAULT_RELEASE_MS,\n pollMs = DEFAULT_POLL_MS,\n context,\n }: VoiceActivityOptions = {},\n): VoiceActivityMonitor {\n const meter = createLevelMeter(stream, { decay: 0, ...(context ? { context } : {}) });\n let speaking = false;\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n const now = performance.now();\n if (meter.level() >= threshold) lastLoudAt = now;\n const next = lastLoudAt > 0 && now - lastLoudAt < releaseMs;\n if (next === speaking) return;\n speaking = next;\n onChange(speaking);\n }, pollMs);\n\n return {\n get speaking(): boolean {\n return speaking;\n },\n stop: (): void => {\n clearInterval(timer);\n meter.stop();\n },\n };\n}\n"],"mappings":"qCAGA,IAAM,EAAoB,KASpB,EAAqB,IAGrB,EAAkB,IAwDxB,SAAgB,EACZ,EACA,EACA,CACI,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,WACsB,CAAC,EACP,CACpB,IAAM,EAAQ,EAAA,iBAAiB,EAAQ,CAAE,MAAO,EAAG,GAAI,EAAU,CAAE,SAAQ,EAAI,CAAC,CAAG,CAAC,EAChF,EAAW,GACX,EAAa,EAEX,EAAQ,gBAAkB,CAC5B,IAAM,EAAM,YAAY,IAAI,EACxB,EAAM,MAAM,GAAK,IAAW,EAAa,GAC7C,IAAM,EAAO,EAAa,GAAK,EAAM,EAAa,EAC9C,IAAS,IACb,EAAW,EACX,EAAS,CAAQ,EACrB,EAAG,CAAM,EAET,MAAO,CACH,IAAI,UAAoB,CACpB,OAAO,CACX,EACA,SAAkB,CACd,cAAc,CAAK,EACnB,EAAM,KAAK,CACf,CACJ,CACJ"}
1
+ {"version":3,"file":"voice-activity.cjs","names":["createLevelMeter","require_level_meter","DEFAULT_THRESHOLD","DEFAULT_RELEASE_MS","DEFAULT_POLL_MS","monitorVoiceActivity","meter","stream","context","speaking","lastLoudAt","timer","now","threshold","next","releaseMs","onChange","pollMs"],"sources":["../../src/audio/voice-activity.ts"],"sourcesContent":["import { createLevelMeter } from \"./level-meter\";\n\n/** RMS at which a window counts as speech. */\nconst DEFAULT_THRESHOLD = 0.045;\n\n/**\n * How long the indicator stays on after the level drops.\n *\n * RMS falls to nothing in the gaps between words, so a bare threshold strobes on\n * every one of them. Ported from `tempest-mirror-screen`'s `voiceActivity.ts`,\n * where it was arrived at by watching a real call.\n */\nconst DEFAULT_RELEASE_MS = 350;\n\n/** How often the level is read. */\nconst DEFAULT_POLL_MS = 100;\n\n/** Options for {@link monitorVoiceActivity}. */\nexport interface VoiceActivityOptions {\n /** RMS at or above which a window counts as speech. Default `0.045`. */\n threshold?: number;\n /** How long the state stays `true` after the level drops. Default `350`. */\n releaseMs?: number;\n /** How often the level is sampled, in ms. Default `100`. */\n pollMs?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * A monitor per remote participant hits the browser's cap on live contexts\n * (Chrome allows around six) in a five-person call. Open one and share it.\n */\n context?: AudioContext;\n}\n\n/** A running voice-activity monitor. */\nexport interface VoiceActivityMonitor {\n /** Whether the track is currently carrying speech. */\n readonly speaking: boolean;\n /** Stop sampling and release the analysis graph. */\n stop: () => void;\n}\n\n/**\n * Report when a track is carrying speech.\n *\n * Runs entirely on the **receiving** side, over a track the connection already\n * delivers, and that is the whole point: a speaking indicator changes several\n * times per second, so signalling it through a server floods the room with\n * messages carrying information every client can derive for free.\n *\n * Two details separate this from a threshold on a level meter:\n *\n * 1. **The release.** RMS drops to nothing in the gaps between words, so a bare\n * comparison strobes on every syllable. The state only falls after\n * `releaseMs` of quiet.\n * 2. **`onChange` fires on the flip, not on the sample.** A callback per poll\n * would re-render a participant list ten times a second to say the same thing.\n *\n * The meter underneath runs with smoothing off: its eased decay plus this release\n * would hold the indicator open long after the speech stopped.\n *\n * @param stream - The stream whose first audio track is measured.\n * @param onChange - Called only when the speaking state actually flips.\n * @param options - See {@link VoiceActivityOptions}.\n * @returns A handle carrying the current state and the teardown.\n *\n * @example\n * const vad = monitorVoiceActivity(remoteStream, (speaking) => setSpeaking(speaking));\n * // later\n * vad.stop();\n */\nexport function monitorVoiceActivity(\n stream: MediaStream,\n onChange: (speaking: boolean) => void,\n {\n threshold = DEFAULT_THRESHOLD,\n releaseMs = DEFAULT_RELEASE_MS,\n pollMs = DEFAULT_POLL_MS,\n context,\n }: VoiceActivityOptions = {},\n): VoiceActivityMonitor {\n const meter = createLevelMeter(stream, { decay: 0, ...(context ? { context } : {}) });\n let speaking = false;\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n const now = performance.now();\n if (meter.level() >= threshold) lastLoudAt = now;\n const next = lastLoudAt > 0 && now - lastLoudAt < releaseMs;\n if (next === speaking) return;\n speaking = next;\n onChange(speaking);\n }, pollMs);\n\n return {\n get speaking(): boolean {\n return speaking;\n },\n stop: (): void => {\n clearInterval(timer);\n meter.stop();\n },\n };\n}\n"],"mappings":"A,qCAGA,IAAME,EAAoB,KASpBC,EAAqB,IAGrBC,EAAkB,IAwDxB,SAAgBC,EACZ,EACA,EACA,CACI,YAAYH,EACZ,YAAYC,EACZ,SAASC,EACT,WACsB,CAAC,EACP,CACpB,IAAME,EAAQN,EAAAA,iBAAiBO,EAAQ,CAAE,MAAO,EAAG,GAAIC,EAAU,CAAE,SAAQ,EAAI,CAAC,CAAG,CAAC,EAChFC,EAAW,GACXC,EAAa,EAEXC,EAAQ,gBAAkB,CAC5B,IAAMC,EAAM,YAAY,IAAI,EACxBN,EAAM,MAAM,GAAKO,IAAW,EAAaD,GAC7C,IAAME,EAAOJ,EAAa,GAAKE,EAAMF,EAAaK,EAC9CD,IAASL,IACb,EAAWK,EACXE,EAASP,CAAQ,EACrB,EAAGQ,CAAM,EAET,MAAO,CACH,IAAI,UAAoB,CACpB,OAAOR,CACX,EACA,SAAkB,CACd,cAAcE,CAAK,EACnBL,EAAM,KAAK,CACf,CACJ,CACJ,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"voice-activity.js","names":[],"sources":["../../src/audio/voice-activity.ts"],"sourcesContent":["import { createLevelMeter } from \"./level-meter\";\n\n/** RMS at which a window counts as speech. */\nconst DEFAULT_THRESHOLD = 0.045;\n\n/**\n * How long the indicator stays on after the level drops.\n *\n * RMS falls to nothing in the gaps between words, so a bare threshold strobes on\n * every one of them. Ported from `tempest-mirror-screen`'s `voiceActivity.ts`,\n * where it was arrived at by watching a real call.\n */\nconst DEFAULT_RELEASE_MS = 350;\n\n/** How often the level is read. */\nconst DEFAULT_POLL_MS = 100;\n\n/** Options for {@link monitorVoiceActivity}. */\nexport interface VoiceActivityOptions {\n /** RMS at or above which a window counts as speech. Default `0.045`. */\n threshold?: number;\n /** How long the state stays `true` after the level drops. Default `350`. */\n releaseMs?: number;\n /** How often the level is sampled, in ms. Default `100`. */\n pollMs?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * A monitor per remote participant hits the browser's cap on live contexts\n * (Chrome allows around six) in a five-person call. Open one and share it.\n */\n context?: AudioContext;\n}\n\n/** A running voice-activity monitor. */\nexport interface VoiceActivityMonitor {\n /** Whether the track is currently carrying speech. */\n readonly speaking: boolean;\n /** Stop sampling and release the analysis graph. */\n stop: () => void;\n}\n\n/**\n * Report when a track is carrying speech.\n *\n * Runs entirely on the **receiving** side, over a track the connection already\n * delivers, and that is the whole point: a speaking indicator changes several\n * times per second, so signalling it through a server floods the room with\n * messages carrying information every client can derive for free.\n *\n * Two details separate this from a threshold on a level meter:\n *\n * 1. **The release.** RMS drops to nothing in the gaps between words, so a bare\n * comparison strobes on every syllable. The state only falls after\n * `releaseMs` of quiet.\n * 2. **`onChange` fires on the flip, not on the sample.** A callback per poll\n * would re-render a participant list ten times a second to say the same thing.\n *\n * The meter underneath runs with smoothing off: its eased decay plus this release\n * would hold the indicator open long after the speech stopped.\n *\n * @param stream - The stream whose first audio track is measured.\n * @param onChange - Called only when the speaking state actually flips.\n * @param options - See {@link VoiceActivityOptions}.\n * @returns A handle carrying the current state and the teardown.\n *\n * @example\n * const vad = monitorVoiceActivity(remoteStream, (speaking) => setSpeaking(speaking));\n * // later\n * vad.stop();\n */\nexport function monitorVoiceActivity(\n stream: MediaStream,\n onChange: (speaking: boolean) => void,\n {\n threshold = DEFAULT_THRESHOLD,\n releaseMs = DEFAULT_RELEASE_MS,\n pollMs = DEFAULT_POLL_MS,\n context,\n }: VoiceActivityOptions = {},\n): VoiceActivityMonitor {\n const meter = createLevelMeter(stream, { decay: 0, ...(context ? { context } : {}) });\n let speaking = false;\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n const now = performance.now();\n if (meter.level() >= threshold) lastLoudAt = now;\n const next = lastLoudAt > 0 && now - lastLoudAt < releaseMs;\n if (next === speaking) return;\n speaking = next;\n onChange(speaking);\n }, pollMs);\n\n return {\n get speaking(): boolean {\n return speaking;\n },\n stop: (): void => {\n clearInterval(timer);\n meter.stop();\n },\n };\n}\n"],"mappings":";;AAGA,IAAM,IAAoB,MASpB,IAAqB,KAGrB,IAAkB;AAwDxB,SAAgB,EACZ,GACA,GACA,EACI,eAAY,GACZ,eAAY,GACZ,YAAS,GACT,eACsB,CAAC,GACP;CACpB,IAAM,IAAQ,EAAiB,GAAQ;EAAE,OAAO;EAAG,GAAI,IAAU,EAAE,WAAQ,IAAI,CAAC;CAAG,CAAC,GAChF,IAAW,IACX,IAAa,GAEX,IAAQ,kBAAkB;EAC5B,IAAM,IAAM,YAAY,IAAI;EAC5B,AAAI,EAAM,MAAM,KAAK,MAAW,IAAa;EAC7C,IAAM,IAAO,IAAa,KAAK,IAAM,IAAa;EAC9C,MAAS,MACb,IAAW,GACX,EAAS,CAAQ;CACrB,GAAG,CAAM;CAET,OAAO;EACH,IAAI,WAAoB;GACpB,OAAO;EACX;EACA,YAAkB;GAEd,AADA,cAAc,CAAK,GACnB,EAAM,KAAK;EACf;CACJ;AACJ"}
1
+ {"version":3,"file":"voice-activity.js","names":["DEFAULT_THRESHOLD","DEFAULT_RELEASE_MS","DEFAULT_POLL_MS","monitorVoiceActivity","meter","createLevelMeter","stream","context","speaking","lastLoudAt","timer","now","threshold","next","releaseMs","onChange","pollMs"],"sources":["../../src/audio/voice-activity.ts"],"sourcesContent":["import { createLevelMeter } from \"./level-meter\";\n\n/** RMS at which a window counts as speech. */\nconst DEFAULT_THRESHOLD = 0.045;\n\n/**\n * How long the indicator stays on after the level drops.\n *\n * RMS falls to nothing in the gaps between words, so a bare threshold strobes on\n * every one of them. Ported from `tempest-mirror-screen`'s `voiceActivity.ts`,\n * where it was arrived at by watching a real call.\n */\nconst DEFAULT_RELEASE_MS = 350;\n\n/** How often the level is read. */\nconst DEFAULT_POLL_MS = 100;\n\n/** Options for {@link monitorVoiceActivity}. */\nexport interface VoiceActivityOptions {\n /** RMS at or above which a window counts as speech. Default `0.045`. */\n threshold?: number;\n /** How long the state stays `true` after the level drops. Default `350`. */\n releaseMs?: number;\n /** How often the level is sampled, in ms. Default `100`. */\n pollMs?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * A monitor per remote participant hits the browser's cap on live contexts\n * (Chrome allows around six) in a five-person call. Open one and share it.\n */\n context?: AudioContext;\n}\n\n/** A running voice-activity monitor. */\nexport interface VoiceActivityMonitor {\n /** Whether the track is currently carrying speech. */\n readonly speaking: boolean;\n /** Stop sampling and release the analysis graph. */\n stop: () => void;\n}\n\n/**\n * Report when a track is carrying speech.\n *\n * Runs entirely on the **receiving** side, over a track the connection already\n * delivers, and that is the whole point: a speaking indicator changes several\n * times per second, so signalling it through a server floods the room with\n * messages carrying information every client can derive for free.\n *\n * Two details separate this from a threshold on a level meter:\n *\n * 1. **The release.** RMS drops to nothing in the gaps between words, so a bare\n * comparison strobes on every syllable. The state only falls after\n * `releaseMs` of quiet.\n * 2. **`onChange` fires on the flip, not on the sample.** A callback per poll\n * would re-render a participant list ten times a second to say the same thing.\n *\n * The meter underneath runs with smoothing off: its eased decay plus this release\n * would hold the indicator open long after the speech stopped.\n *\n * @param stream - The stream whose first audio track is measured.\n * @param onChange - Called only when the speaking state actually flips.\n * @param options - See {@link VoiceActivityOptions}.\n * @returns A handle carrying the current state and the teardown.\n *\n * @example\n * const vad = monitorVoiceActivity(remoteStream, (speaking) => setSpeaking(speaking));\n * // later\n * vad.stop();\n */\nexport function monitorVoiceActivity(\n stream: MediaStream,\n onChange: (speaking: boolean) => void,\n {\n threshold = DEFAULT_THRESHOLD,\n releaseMs = DEFAULT_RELEASE_MS,\n pollMs = DEFAULT_POLL_MS,\n context,\n }: VoiceActivityOptions = {},\n): VoiceActivityMonitor {\n const meter = createLevelMeter(stream, { decay: 0, ...(context ? { context } : {}) });\n let speaking = false;\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n const now = performance.now();\n if (meter.level() >= threshold) lastLoudAt = now;\n const next = lastLoudAt > 0 && now - lastLoudAt < releaseMs;\n if (next === speaking) return;\n speaking = next;\n onChange(speaking);\n }, pollMs);\n\n return {\n get speaking(): boolean {\n return speaking;\n },\n stop: (): void => {\n clearInterval(timer);\n meter.stop();\n },\n };\n}\n"],"mappings":";;AAGA,IAAMA,IAAoB,MASpBC,IAAqB,KAGrBC,IAAkB;AAwDxB,SAAgBC,EACZ,GACA,GACA,EACI,eAAYH,GACZ,eAAYC,GACZ,YAASC,GACT,eACsB,CAAC,GACP;CACpB,IAAME,IAAQC,EAAiBC,GAAQ;EAAE,OAAO;EAAG,GAAIC,IAAU,EAAE,WAAQ,IAAI,CAAC;CAAG,CAAC,GAChFC,IAAW,IACXC,IAAa,GAEXC,IAAQ,kBAAkB;EAC5B,IAAMC,IAAM,YAAY,IAAI;EAC5B,AAAIP,EAAM,MAAM,KAAKQ,MAAW,IAAaD;EAC7C,IAAME,IAAOJ,IAAa,KAAKE,IAAMF,IAAaK;EAClD,AAAID,MAASL,MACb,IAAWK,GACXE,EAASP,CAAQ;CACrB,GAAGQ,CAAM;CAET,OAAO;EACH,IAAI,WAAoB;GACpB,OAAOR;EACX;EACA,YAAkB;GAEd,AADA,cAAcE,CAAK,GACnBN,EAAM,KAAK;EACf;CACJ;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"voice-chain-stages.cjs","names":[],"sources":["../../src/audio/voice-chain-stages.ts"],"sourcesContent":["/**\n * The individual stages a voice chain is built from, and the two side-chains\n * Web Audio cannot express declaratively.\n *\n * Split out of `voice-chain.ts` because every number here is a tuning decision\n * with a reason attached, and the file that assembles them should read as the\n * signal path rather than as a wall of constants.\n */\n\n/** Everything below this is rumble, not speech. */\nconst HIGH_PASS_HZ = 85;\n\n/** Where consonants live — the band that decides whether a word is understood. */\nconst PRESENCE_HZ = 3000;\nconst PRESENCE_GAIN_DB = 3;\nconst PRESENCE_Q = 1;\n\n/** Where microphone hiss lives, above anything speech needs. */\nconst HISS_HZ = 9000;\n\n/** The sibilance band a de-esser rides. */\nconst SIBILANCE_HZ = 7000;\nconst SIBILANCE_Q = 2;\n\n/** Deepest cut the de-esser applies, in dB. */\nconst DEESSER_MAX_CUT_DB = 10;\n\n/** Band energy above which the cut starts, as RMS. */\nconst DEESSER_THRESHOLD = 0.02;\n\n/** How steeply the cut follows the band's energy above the threshold. */\nconst DEESSER_SLOPE_DB_PER_RMS = 200;\n\n/** Seconds the de-esser takes to reach a new cut. */\nconst DEESSER_SMOOTHING = 0.02;\n\n/**\n * A leveller, not a limiter: 3:1 with a soft knee evens out the distance between\n * a whisper and a shout without audibly pumping.\n */\nconst COMPRESSOR = {\n threshold: -24,\n knee: 30,\n ratio: 3,\n attack: 0.003,\n release: 0.25,\n} as const;\n\n/**\n * The ceiling the limiter holds, in dB relative to full scale.\n *\n * Just under zero, because digital clipping is not a degradation the other end\n * can recover from — it arrives as squared-off samples that no amount of\n * processing undoes.\n */\nconst LIMITER = {\n threshold: -1,\n knee: 0,\n ratio: 20,\n attack: 0.001,\n release: 0.1,\n} as const;\n\n/** How often the gate and the de-esser look at the signal, in milliseconds. */\nexport const DETECTOR_POLL_MS = 20;\n\n/** Time constant for opening the gate. Short, so a first syllable survives. */\nconst GATE_ATTACK = 0.005;\n\n/** Time constant for closing it. Long, so a word does not end abruptly. */\nconst GATE_RELEASE = 0.12;\n\n/**\n * How long the gate stays open after the level drops.\n *\n * RMS falls to nothing between syllables, so a gate without hold chops speech\n * into pieces — it closes inside a word and reopens on the next one. This is the\n * difference between a gate that works and one everybody turns off again.\n */\nexport const GATE_HOLD_MS = 220;\n\n/** The window each detector reads. */\nconst DETECTOR_FFT_SIZE = 1024;\n\n/**\n * Root-mean-square of a time-domain window.\n *\n * RMS rather than peak because both detectors here answer \"how much energy is\n * there\", not \"did one sample spike\": a peak reading opens a gate on a keyboard\n * click and drives a de-esser off a single transient.\n *\n * @param buffer - Time-domain samples, -1..1.\n * @returns The window's RMS, 0..1.\n */\nfunction rms(buffer: Float32Array): number {\n let sum = 0;\n for (const sample of buffer) sum += sample * sample;\n return Math.sqrt(sum / buffer.length);\n}\n\n/** Build the analyser both detectors read through. */\nfunction detectorFor(context: AudioContext): AnalyserNode {\n const analyser = context.createAnalyser();\n analyser.fftSize = DETECTOR_FFT_SIZE;\n return analyser;\n}\n\n/** A high-pass at 85 Hz: fans, traffic, desk knocks, plosives. */\nexport function highPassStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"highpass\";\n filter.frequency.value = HIGH_PASS_HZ;\n return filter;\n}\n\n/** A 3:1 leveller with a soft knee. */\nexport function compressorStage(context: AudioContext): DynamicsCompressorNode {\n const comp = context.createDynamicsCompressor();\n comp.threshold.value = COMPRESSOR.threshold;\n comp.knee.value = COMPRESSOR.knee;\n comp.ratio.value = COMPRESSOR.ratio;\n comp.attack.value = COMPRESSOR.attack;\n comp.release.value = COMPRESSOR.release;\n return comp;\n}\n\n/** A +3 dB peak at 3 kHz: speech that reads without getting louder. */\nexport function presenceStage(context: AudioContext): BiquadFilterNode {\n const eq = context.createBiquadFilter();\n eq.type = \"peaking\";\n eq.frequency.value = PRESENCE_HZ;\n eq.Q.value = PRESENCE_Q;\n eq.gain.value = PRESENCE_GAIN_DB;\n return eq;\n}\n\n/** A low-pass at 9 kHz: the hiss of a cheap microphone. */\nexport function hissCutStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"lowpass\";\n filter.frequency.value = HISS_HZ;\n return filter;\n}\n\n/** A hard 20:1 ceiling at -1 dBFS, placed after the output gain. */\nexport function limiterStage(context: AudioContext): DynamicsCompressorNode {\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = LIMITER.threshold;\n limiter.knee.value = LIMITER.knee;\n limiter.ratio.value = LIMITER.ratio;\n limiter.attack.value = LIMITER.attack;\n limiter.release.value = LIMITER.release;\n return limiter;\n}\n\n/** The nodes a gate stage owns. */\nexport interface GateStage {\n /** Reads the signal entering the gate. */\n detector: AnalyserNode;\n /** The gain the detector drives, 0 when closed. */\n gain: GainNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Silence the line between phrases, without cutting inside a word.\n *\n * Measured on a timer rather than in an `AudioWorklet`. A worklet would gate\n * sample-accurately at the cost of a separate module file the consumer has to\n * serve and a second graph to keep in sync; at 20 ms the difference is inaudible\n * for speech, because the hold and the release are an order of magnitude longer\n * than the polling interval and are what actually shape how a gate sounds.\n *\n * The gate starts closed and opens on the first window over the threshold, so a\n * chain built while the room is quiet does not leak the first 20 ms of it.\n *\n * @param context - The graph's context.\n * @param threshold - Read on every tick, so the caller can move a slider while\n * the chain is live rather than rebuild it.\n * @returns The stage's nodes and its teardown.\n */\nexport function gateStage(context: AudioContext, threshold: () => number): GateStage {\n const detector = detectorFor(context);\n const gain = context.createGain();\n gain.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const now = performance.now();\n if (rms(buffer) >= threshold()) lastLoudAt = now;\n const open = lastLoudAt > 0 && now - lastLoudAt < GATE_HOLD_MS;\n gain.gain.setTargetAtTime(\n open ? 1 : 0,\n context.currentTime,\n open ? GATE_ATTACK : GATE_RELEASE,\n );\n }, DETECTOR_POLL_MS);\n\n return { detector, gain, stop: () => clearInterval(timer) };\n}\n\n/** The nodes a de-esser stage owns. */\nexport interface DeEsserStage {\n /** Band-pass feeding the detector — the side-chain's input. */\n band: BiquadFilterNode;\n /** Reads 6-8 kHz only. */\n detector: AnalyserNode;\n /** The peaking filter in the signal path, whose gain rides the band. */\n shaper: BiquadFilterNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Pull the sibilance band down while it is loud, and let go when it is not.\n *\n * Web Audio has no side-chain, so the detector is built by hand: a band-pass\n * feeds an analyser watching only 6-8 kHz, and that analyser's energy drives the\n * gain of a peaking filter sitting in the main path. The distinction is the whole\n * stage — the cut has to answer to the *band*, not to how loud the person is. A\n * static cut at the same frequency would dull every consonant instead of the\n * syllables that actually hiss.\n *\n * @param context - The graph's context.\n * @returns The stage's nodes and its teardown.\n */\nexport function deEsserStage(context: AudioContext): DeEsserStage {\n const band = context.createBiquadFilter();\n band.type = \"bandpass\";\n band.frequency.value = SIBILANCE_HZ;\n band.Q.value = SIBILANCE_Q;\n\n const detector = detectorFor(context);\n band.connect(detector);\n\n const shaper = context.createBiquadFilter();\n shaper.type = \"peaking\";\n shaper.frequency.value = SIBILANCE_HZ;\n shaper.Q.value = SIBILANCE_Q;\n shaper.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const over = Math.max(0, rms(buffer) - DEESSER_THRESHOLD);\n const cut = Math.min(DEESSER_MAX_CUT_DB, over * DEESSER_SLOPE_DB_PER_RMS);\n shaper.gain.setTargetAtTime(-cut, context.currentTime, DEESSER_SMOOTHING);\n }, DETECTOR_POLL_MS);\n\n return { band, detector, shaper, stop: () => clearInterval(timer) };\n}\n\nexport { rms as windowRms };\n"],"mappings":"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"}
1
+ {"version":3,"file":"voice-chain-stages.cjs","names":["SIBILANCE_HZ","COMPRESSOR","LIMITER","GATE_ATTACK","GATE_RELEASE","DETECTOR_FFT_SIZE","rms","sum","sample","buffer","detectorFor","analyser","context","highPassStage","filter","compressorStage","comp","presenceStage","eq","hissCutStage","limiterStage","limiter","gateStage","detector","gain","lastLoudAt","timer","now","threshold","open","deEsserStage","band","shaper","over","cut"],"sources":["../../src/audio/voice-chain-stages.ts"],"sourcesContent":["/**\n * The individual stages a voice chain is built from, and the two side-chains\n * Web Audio cannot express declaratively.\n *\n * Split out of `voice-chain.ts` because every number here is a tuning decision\n * with a reason attached, and the file that assembles them should read as the\n * signal path rather than as a wall of constants.\n */\n\n/** Everything below this is rumble, not speech. */\nconst HIGH_PASS_HZ = 85;\n\n/** Where consonants live — the band that decides whether a word is understood. */\nconst PRESENCE_HZ = 3000;\nconst PRESENCE_GAIN_DB = 3;\nconst PRESENCE_Q = 1;\n\n/** Where microphone hiss lives, above anything speech needs. */\nconst HISS_HZ = 9000;\n\n/** The sibilance band a de-esser rides. */\nconst SIBILANCE_HZ = 7000;\nconst SIBILANCE_Q = 2;\n\n/** Deepest cut the de-esser applies, in dB. */\nconst DEESSER_MAX_CUT_DB = 10;\n\n/** Band energy above which the cut starts, as RMS. */\nconst DEESSER_THRESHOLD = 0.02;\n\n/** How steeply the cut follows the band's energy above the threshold. */\nconst DEESSER_SLOPE_DB_PER_RMS = 200;\n\n/** Seconds the de-esser takes to reach a new cut. */\nconst DEESSER_SMOOTHING = 0.02;\n\n/**\n * A leveller, not a limiter: 3:1 with a soft knee evens out the distance between\n * a whisper and a shout without audibly pumping.\n */\nconst COMPRESSOR = {\n threshold: -24,\n knee: 30,\n ratio: 3,\n attack: 0.003,\n release: 0.25,\n} as const;\n\n/**\n * The ceiling the limiter holds, in dB relative to full scale.\n *\n * Just under zero, because digital clipping is not a degradation the other end\n * can recover from — it arrives as squared-off samples that no amount of\n * processing undoes.\n */\nconst LIMITER = {\n threshold: -1,\n knee: 0,\n ratio: 20,\n attack: 0.001,\n release: 0.1,\n} as const;\n\n/** How often the gate and the de-esser look at the signal, in milliseconds. */\nexport const DETECTOR_POLL_MS = 20;\n\n/** Time constant for opening the gate. Short, so a first syllable survives. */\nconst GATE_ATTACK = 0.005;\n\n/** Time constant for closing it. Long, so a word does not end abruptly. */\nconst GATE_RELEASE = 0.12;\n\n/**\n * How long the gate stays open after the level drops.\n *\n * RMS falls to nothing between syllables, so a gate without hold chops speech\n * into pieces — it closes inside a word and reopens on the next one. This is the\n * difference between a gate that works and one everybody turns off again.\n */\nexport const GATE_HOLD_MS = 220;\n\n/** The window each detector reads. */\nconst DETECTOR_FFT_SIZE = 1024;\n\n/**\n * Root-mean-square of a time-domain window.\n *\n * RMS rather than peak because both detectors here answer \"how much energy is\n * there\", not \"did one sample spike\": a peak reading opens a gate on a keyboard\n * click and drives a de-esser off a single transient.\n *\n * @param buffer - Time-domain samples, -1..1.\n * @returns The window's RMS, 0..1.\n */\nfunction rms(buffer: Float32Array): number {\n let sum = 0;\n for (const sample of buffer) sum += sample * sample;\n return Math.sqrt(sum / buffer.length);\n}\n\n/** Build the analyser both detectors read through. */\nfunction detectorFor(context: AudioContext): AnalyserNode {\n const analyser = context.createAnalyser();\n analyser.fftSize = DETECTOR_FFT_SIZE;\n return analyser;\n}\n\n/** A high-pass at 85 Hz: fans, traffic, desk knocks, plosives. */\nexport function highPassStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"highpass\";\n filter.frequency.value = HIGH_PASS_HZ;\n return filter;\n}\n\n/** A 3:1 leveller with a soft knee. */\nexport function compressorStage(context: AudioContext): DynamicsCompressorNode {\n const comp = context.createDynamicsCompressor();\n comp.threshold.value = COMPRESSOR.threshold;\n comp.knee.value = COMPRESSOR.knee;\n comp.ratio.value = COMPRESSOR.ratio;\n comp.attack.value = COMPRESSOR.attack;\n comp.release.value = COMPRESSOR.release;\n return comp;\n}\n\n/** A +3 dB peak at 3 kHz: speech that reads without getting louder. */\nexport function presenceStage(context: AudioContext): BiquadFilterNode {\n const eq = context.createBiquadFilter();\n eq.type = \"peaking\";\n eq.frequency.value = PRESENCE_HZ;\n eq.Q.value = PRESENCE_Q;\n eq.gain.value = PRESENCE_GAIN_DB;\n return eq;\n}\n\n/** A low-pass at 9 kHz: the hiss of a cheap microphone. */\nexport function hissCutStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"lowpass\";\n filter.frequency.value = HISS_HZ;\n return filter;\n}\n\n/** A hard 20:1 ceiling at -1 dBFS, placed after the output gain. */\nexport function limiterStage(context: AudioContext): DynamicsCompressorNode {\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = LIMITER.threshold;\n limiter.knee.value = LIMITER.knee;\n limiter.ratio.value = LIMITER.ratio;\n limiter.attack.value = LIMITER.attack;\n limiter.release.value = LIMITER.release;\n return limiter;\n}\n\n/** The nodes a gate stage owns. */\nexport interface GateStage {\n /** Reads the signal entering the gate. */\n detector: AnalyserNode;\n /** The gain the detector drives, 0 when closed. */\n gain: GainNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Silence the line between phrases, without cutting inside a word.\n *\n * Measured on a timer rather than in an `AudioWorklet`. A worklet would gate\n * sample-accurately at the cost of a separate module file the consumer has to\n * serve and a second graph to keep in sync; at 20 ms the difference is inaudible\n * for speech, because the hold and the release are an order of magnitude longer\n * than the polling interval and are what actually shape how a gate sounds.\n *\n * The gate starts closed and opens on the first window over the threshold, so a\n * chain built while the room is quiet does not leak the first 20 ms of it.\n *\n * @param context - The graph's context.\n * @param threshold - Read on every tick, so the caller can move a slider while\n * the chain is live rather than rebuild it.\n * @returns The stage's nodes and its teardown.\n */\nexport function gateStage(context: AudioContext, threshold: () => number): GateStage {\n const detector = detectorFor(context);\n const gain = context.createGain();\n gain.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const now = performance.now();\n if (rms(buffer) >= threshold()) lastLoudAt = now;\n const open = lastLoudAt > 0 && now - lastLoudAt < GATE_HOLD_MS;\n gain.gain.setTargetAtTime(\n open ? 1 : 0,\n context.currentTime,\n open ? GATE_ATTACK : GATE_RELEASE,\n );\n }, DETECTOR_POLL_MS);\n\n return { detector, gain, stop: () => clearInterval(timer) };\n}\n\n/** The nodes a de-esser stage owns. */\nexport interface DeEsserStage {\n /** Band-pass feeding the detector — the side-chain's input. */\n band: BiquadFilterNode;\n /** Reads 6-8 kHz only. */\n detector: AnalyserNode;\n /** The peaking filter in the signal path, whose gain rides the band. */\n shaper: BiquadFilterNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Pull the sibilance band down while it is loud, and let go when it is not.\n *\n * Web Audio has no side-chain, so the detector is built by hand: a band-pass\n * feeds an analyser watching only 6-8 kHz, and that analyser's energy drives the\n * gain of a peaking filter sitting in the main path. The distinction is the whole\n * stage — the cut has to answer to the *band*, not to how loud the person is. A\n * static cut at the same frequency would dull every consonant instead of the\n * syllables that actually hiss.\n *\n * @param context - The graph's context.\n * @returns The stage's nodes and its teardown.\n */\nexport function deEsserStage(context: AudioContext): DeEsserStage {\n const band = context.createBiquadFilter();\n band.type = \"bandpass\";\n band.frequency.value = SIBILANCE_HZ;\n band.Q.value = SIBILANCE_Q;\n\n const detector = detectorFor(context);\n band.connect(detector);\n\n const shaper = context.createBiquadFilter();\n shaper.type = \"peaking\";\n shaper.frequency.value = SIBILANCE_HZ;\n shaper.Q.value = SIBILANCE_Q;\n shaper.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const over = Math.max(0, rms(buffer) - DEESSER_THRESHOLD);\n const cut = Math.min(DEESSER_MAX_CUT_DB, over * DEESSER_SLOPE_DB_PER_RMS);\n shaper.gain.setTargetAtTime(-cut, context.currentTime, DEESSER_SMOOTHING);\n }, DETECTOR_POLL_MS);\n\n return { band, detector, shaper, stop: () => clearInterval(timer) };\n}\n\nexport { rms as windowRms };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"voice-chain-stages.js","names":[],"sources":["../../src/audio/voice-chain-stages.ts"],"sourcesContent":["/**\n * The individual stages a voice chain is built from, and the two side-chains\n * Web Audio cannot express declaratively.\n *\n * Split out of `voice-chain.ts` because every number here is a tuning decision\n * with a reason attached, and the file that assembles them should read as the\n * signal path rather than as a wall of constants.\n */\n\n/** Everything below this is rumble, not speech. */\nconst HIGH_PASS_HZ = 85;\n\n/** Where consonants live — the band that decides whether a word is understood. */\nconst PRESENCE_HZ = 3000;\nconst PRESENCE_GAIN_DB = 3;\nconst PRESENCE_Q = 1;\n\n/** Where microphone hiss lives, above anything speech needs. */\nconst HISS_HZ = 9000;\n\n/** The sibilance band a de-esser rides. */\nconst SIBILANCE_HZ = 7000;\nconst SIBILANCE_Q = 2;\n\n/** Deepest cut the de-esser applies, in dB. */\nconst DEESSER_MAX_CUT_DB = 10;\n\n/** Band energy above which the cut starts, as RMS. */\nconst DEESSER_THRESHOLD = 0.02;\n\n/** How steeply the cut follows the band's energy above the threshold. */\nconst DEESSER_SLOPE_DB_PER_RMS = 200;\n\n/** Seconds the de-esser takes to reach a new cut. */\nconst DEESSER_SMOOTHING = 0.02;\n\n/**\n * A leveller, not a limiter: 3:1 with a soft knee evens out the distance between\n * a whisper and a shout without audibly pumping.\n */\nconst COMPRESSOR = {\n threshold: -24,\n knee: 30,\n ratio: 3,\n attack: 0.003,\n release: 0.25,\n} as const;\n\n/**\n * The ceiling the limiter holds, in dB relative to full scale.\n *\n * Just under zero, because digital clipping is not a degradation the other end\n * can recover from — it arrives as squared-off samples that no amount of\n * processing undoes.\n */\nconst LIMITER = {\n threshold: -1,\n knee: 0,\n ratio: 20,\n attack: 0.001,\n release: 0.1,\n} as const;\n\n/** How often the gate and the de-esser look at the signal, in milliseconds. */\nexport const DETECTOR_POLL_MS = 20;\n\n/** Time constant for opening the gate. Short, so a first syllable survives. */\nconst GATE_ATTACK = 0.005;\n\n/** Time constant for closing it. Long, so a word does not end abruptly. */\nconst GATE_RELEASE = 0.12;\n\n/**\n * How long the gate stays open after the level drops.\n *\n * RMS falls to nothing between syllables, so a gate without hold chops speech\n * into pieces — it closes inside a word and reopens on the next one. This is the\n * difference between a gate that works and one everybody turns off again.\n */\nexport const GATE_HOLD_MS = 220;\n\n/** The window each detector reads. */\nconst DETECTOR_FFT_SIZE = 1024;\n\n/**\n * Root-mean-square of a time-domain window.\n *\n * RMS rather than peak because both detectors here answer \"how much energy is\n * there\", not \"did one sample spike\": a peak reading opens a gate on a keyboard\n * click and drives a de-esser off a single transient.\n *\n * @param buffer - Time-domain samples, -1..1.\n * @returns The window's RMS, 0..1.\n */\nfunction rms(buffer: Float32Array): number {\n let sum = 0;\n for (const sample of buffer) sum += sample * sample;\n return Math.sqrt(sum / buffer.length);\n}\n\n/** Build the analyser both detectors read through. */\nfunction detectorFor(context: AudioContext): AnalyserNode {\n const analyser = context.createAnalyser();\n analyser.fftSize = DETECTOR_FFT_SIZE;\n return analyser;\n}\n\n/** A high-pass at 85 Hz: fans, traffic, desk knocks, plosives. */\nexport function highPassStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"highpass\";\n filter.frequency.value = HIGH_PASS_HZ;\n return filter;\n}\n\n/** A 3:1 leveller with a soft knee. */\nexport function compressorStage(context: AudioContext): DynamicsCompressorNode {\n const comp = context.createDynamicsCompressor();\n comp.threshold.value = COMPRESSOR.threshold;\n comp.knee.value = COMPRESSOR.knee;\n comp.ratio.value = COMPRESSOR.ratio;\n comp.attack.value = COMPRESSOR.attack;\n comp.release.value = COMPRESSOR.release;\n return comp;\n}\n\n/** A +3 dB peak at 3 kHz: speech that reads without getting louder. */\nexport function presenceStage(context: AudioContext): BiquadFilterNode {\n const eq = context.createBiquadFilter();\n eq.type = \"peaking\";\n eq.frequency.value = PRESENCE_HZ;\n eq.Q.value = PRESENCE_Q;\n eq.gain.value = PRESENCE_GAIN_DB;\n return eq;\n}\n\n/** A low-pass at 9 kHz: the hiss of a cheap microphone. */\nexport function hissCutStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"lowpass\";\n filter.frequency.value = HISS_HZ;\n return filter;\n}\n\n/** A hard 20:1 ceiling at -1 dBFS, placed after the output gain. */\nexport function limiterStage(context: AudioContext): DynamicsCompressorNode {\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = LIMITER.threshold;\n limiter.knee.value = LIMITER.knee;\n limiter.ratio.value = LIMITER.ratio;\n limiter.attack.value = LIMITER.attack;\n limiter.release.value = LIMITER.release;\n return limiter;\n}\n\n/** The nodes a gate stage owns. */\nexport interface GateStage {\n /** Reads the signal entering the gate. */\n detector: AnalyserNode;\n /** The gain the detector drives, 0 when closed. */\n gain: GainNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Silence the line between phrases, without cutting inside a word.\n *\n * Measured on a timer rather than in an `AudioWorklet`. A worklet would gate\n * sample-accurately at the cost of a separate module file the consumer has to\n * serve and a second graph to keep in sync; at 20 ms the difference is inaudible\n * for speech, because the hold and the release are an order of magnitude longer\n * than the polling interval and are what actually shape how a gate sounds.\n *\n * The gate starts closed and opens on the first window over the threshold, so a\n * chain built while the room is quiet does not leak the first 20 ms of it.\n *\n * @param context - The graph's context.\n * @param threshold - Read on every tick, so the caller can move a slider while\n * the chain is live rather than rebuild it.\n * @returns The stage's nodes and its teardown.\n */\nexport function gateStage(context: AudioContext, threshold: () => number): GateStage {\n const detector = detectorFor(context);\n const gain = context.createGain();\n gain.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const now = performance.now();\n if (rms(buffer) >= threshold()) lastLoudAt = now;\n const open = lastLoudAt > 0 && now - lastLoudAt < GATE_HOLD_MS;\n gain.gain.setTargetAtTime(\n open ? 1 : 0,\n context.currentTime,\n open ? GATE_ATTACK : GATE_RELEASE,\n );\n }, DETECTOR_POLL_MS);\n\n return { detector, gain, stop: () => clearInterval(timer) };\n}\n\n/** The nodes a de-esser stage owns. */\nexport interface DeEsserStage {\n /** Band-pass feeding the detector — the side-chain's input. */\n band: BiquadFilterNode;\n /** Reads 6-8 kHz only. */\n detector: AnalyserNode;\n /** The peaking filter in the signal path, whose gain rides the band. */\n shaper: BiquadFilterNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Pull the sibilance band down while it is loud, and let go when it is not.\n *\n * Web Audio has no side-chain, so the detector is built by hand: a band-pass\n * feeds an analyser watching only 6-8 kHz, and that analyser's energy drives the\n * gain of a peaking filter sitting in the main path. The distinction is the whole\n * stage — the cut has to answer to the *band*, not to how loud the person is. A\n * static cut at the same frequency would dull every consonant instead of the\n * syllables that actually hiss.\n *\n * @param context - The graph's context.\n * @returns The stage's nodes and its teardown.\n */\nexport function deEsserStage(context: AudioContext): DeEsserStage {\n const band = context.createBiquadFilter();\n band.type = \"bandpass\";\n band.frequency.value = SIBILANCE_HZ;\n band.Q.value = SIBILANCE_Q;\n\n const detector = detectorFor(context);\n band.connect(detector);\n\n const shaper = context.createBiquadFilter();\n shaper.type = \"peaking\";\n shaper.frequency.value = SIBILANCE_HZ;\n shaper.Q.value = SIBILANCE_Q;\n shaper.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const over = Math.max(0, rms(buffer) - DEESSER_THRESHOLD);\n const cut = Math.min(DEESSER_MAX_CUT_DB, over * DEESSER_SLOPE_DB_PER_RMS);\n shaper.gain.setTargetAtTime(-cut, context.currentTime, DEESSER_SMOOTHING);\n }, DETECTOR_POLL_MS);\n\n return { band, detector, shaper, stop: () => clearInterval(timer) };\n}\n\nexport { rms as windowRms };\n"],"mappings":"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"}
1
+ {"version":3,"file":"voice-chain-stages.js","names":["SIBILANCE_HZ","COMPRESSOR","LIMITER","GATE_ATTACK","GATE_RELEASE","DETECTOR_FFT_SIZE","rms","sum","sample","buffer","detectorFor","analyser","context","highPassStage","filter","compressorStage","comp","presenceStage","eq","hissCutStage","limiterStage","limiter","gateStage","detector","gain","lastLoudAt","timer","now","threshold","open","deEsserStage","band","shaper","over","cut"],"sources":["../../src/audio/voice-chain-stages.ts"],"sourcesContent":["/**\n * The individual stages a voice chain is built from, and the two side-chains\n * Web Audio cannot express declaratively.\n *\n * Split out of `voice-chain.ts` because every number here is a tuning decision\n * with a reason attached, and the file that assembles them should read as the\n * signal path rather than as a wall of constants.\n */\n\n/** Everything below this is rumble, not speech. */\nconst HIGH_PASS_HZ = 85;\n\n/** Where consonants live — the band that decides whether a word is understood. */\nconst PRESENCE_HZ = 3000;\nconst PRESENCE_GAIN_DB = 3;\nconst PRESENCE_Q = 1;\n\n/** Where microphone hiss lives, above anything speech needs. */\nconst HISS_HZ = 9000;\n\n/** The sibilance band a de-esser rides. */\nconst SIBILANCE_HZ = 7000;\nconst SIBILANCE_Q = 2;\n\n/** Deepest cut the de-esser applies, in dB. */\nconst DEESSER_MAX_CUT_DB = 10;\n\n/** Band energy above which the cut starts, as RMS. */\nconst DEESSER_THRESHOLD = 0.02;\n\n/** How steeply the cut follows the band's energy above the threshold. */\nconst DEESSER_SLOPE_DB_PER_RMS = 200;\n\n/** Seconds the de-esser takes to reach a new cut. */\nconst DEESSER_SMOOTHING = 0.02;\n\n/**\n * A leveller, not a limiter: 3:1 with a soft knee evens out the distance between\n * a whisper and a shout without audibly pumping.\n */\nconst COMPRESSOR = {\n threshold: -24,\n knee: 30,\n ratio: 3,\n attack: 0.003,\n release: 0.25,\n} as const;\n\n/**\n * The ceiling the limiter holds, in dB relative to full scale.\n *\n * Just under zero, because digital clipping is not a degradation the other end\n * can recover from — it arrives as squared-off samples that no amount of\n * processing undoes.\n */\nconst LIMITER = {\n threshold: -1,\n knee: 0,\n ratio: 20,\n attack: 0.001,\n release: 0.1,\n} as const;\n\n/** How often the gate and the de-esser look at the signal, in milliseconds. */\nexport const DETECTOR_POLL_MS = 20;\n\n/** Time constant for opening the gate. Short, so a first syllable survives. */\nconst GATE_ATTACK = 0.005;\n\n/** Time constant for closing it. Long, so a word does not end abruptly. */\nconst GATE_RELEASE = 0.12;\n\n/**\n * How long the gate stays open after the level drops.\n *\n * RMS falls to nothing between syllables, so a gate without hold chops speech\n * into pieces — it closes inside a word and reopens on the next one. This is the\n * difference between a gate that works and one everybody turns off again.\n */\nexport const GATE_HOLD_MS = 220;\n\n/** The window each detector reads. */\nconst DETECTOR_FFT_SIZE = 1024;\n\n/**\n * Root-mean-square of a time-domain window.\n *\n * RMS rather than peak because both detectors here answer \"how much energy is\n * there\", not \"did one sample spike\": a peak reading opens a gate on a keyboard\n * click and drives a de-esser off a single transient.\n *\n * @param buffer - Time-domain samples, -1..1.\n * @returns The window's RMS, 0..1.\n */\nfunction rms(buffer: Float32Array): number {\n let sum = 0;\n for (const sample of buffer) sum += sample * sample;\n return Math.sqrt(sum / buffer.length);\n}\n\n/** Build the analyser both detectors read through. */\nfunction detectorFor(context: AudioContext): AnalyserNode {\n const analyser = context.createAnalyser();\n analyser.fftSize = DETECTOR_FFT_SIZE;\n return analyser;\n}\n\n/** A high-pass at 85 Hz: fans, traffic, desk knocks, plosives. */\nexport function highPassStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"highpass\";\n filter.frequency.value = HIGH_PASS_HZ;\n return filter;\n}\n\n/** A 3:1 leveller with a soft knee. */\nexport function compressorStage(context: AudioContext): DynamicsCompressorNode {\n const comp = context.createDynamicsCompressor();\n comp.threshold.value = COMPRESSOR.threshold;\n comp.knee.value = COMPRESSOR.knee;\n comp.ratio.value = COMPRESSOR.ratio;\n comp.attack.value = COMPRESSOR.attack;\n comp.release.value = COMPRESSOR.release;\n return comp;\n}\n\n/** A +3 dB peak at 3 kHz: speech that reads without getting louder. */\nexport function presenceStage(context: AudioContext): BiquadFilterNode {\n const eq = context.createBiquadFilter();\n eq.type = \"peaking\";\n eq.frequency.value = PRESENCE_HZ;\n eq.Q.value = PRESENCE_Q;\n eq.gain.value = PRESENCE_GAIN_DB;\n return eq;\n}\n\n/** A low-pass at 9 kHz: the hiss of a cheap microphone. */\nexport function hissCutStage(context: AudioContext): BiquadFilterNode {\n const filter = context.createBiquadFilter();\n filter.type = \"lowpass\";\n filter.frequency.value = HISS_HZ;\n return filter;\n}\n\n/** A hard 20:1 ceiling at -1 dBFS, placed after the output gain. */\nexport function limiterStage(context: AudioContext): DynamicsCompressorNode {\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = LIMITER.threshold;\n limiter.knee.value = LIMITER.knee;\n limiter.ratio.value = LIMITER.ratio;\n limiter.attack.value = LIMITER.attack;\n limiter.release.value = LIMITER.release;\n return limiter;\n}\n\n/** The nodes a gate stage owns. */\nexport interface GateStage {\n /** Reads the signal entering the gate. */\n detector: AnalyserNode;\n /** The gain the detector drives, 0 when closed. */\n gain: GainNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Silence the line between phrases, without cutting inside a word.\n *\n * Measured on a timer rather than in an `AudioWorklet`. A worklet would gate\n * sample-accurately at the cost of a separate module file the consumer has to\n * serve and a second graph to keep in sync; at 20 ms the difference is inaudible\n * for speech, because the hold and the release are an order of magnitude longer\n * than the polling interval and are what actually shape how a gate sounds.\n *\n * The gate starts closed and opens on the first window over the threshold, so a\n * chain built while the room is quiet does not leak the first 20 ms of it.\n *\n * @param context - The graph's context.\n * @param threshold - Read on every tick, so the caller can move a slider while\n * the chain is live rather than rebuild it.\n * @returns The stage's nodes and its teardown.\n */\nexport function gateStage(context: AudioContext, threshold: () => number): GateStage {\n const detector = detectorFor(context);\n const gain = context.createGain();\n gain.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n let lastLoudAt = 0;\n\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const now = performance.now();\n if (rms(buffer) >= threshold()) lastLoudAt = now;\n const open = lastLoudAt > 0 && now - lastLoudAt < GATE_HOLD_MS;\n gain.gain.setTargetAtTime(\n open ? 1 : 0,\n context.currentTime,\n open ? GATE_ATTACK : GATE_RELEASE,\n );\n }, DETECTOR_POLL_MS);\n\n return { detector, gain, stop: () => clearInterval(timer) };\n}\n\n/** The nodes a de-esser stage owns. */\nexport interface DeEsserStage {\n /** Band-pass feeding the detector — the side-chain's input. */\n band: BiquadFilterNode;\n /** Reads 6-8 kHz only. */\n detector: AnalyserNode;\n /** The peaking filter in the signal path, whose gain rides the band. */\n shaper: BiquadFilterNode;\n /** Stop the detector's timer. */\n stop: () => void;\n}\n\n/**\n * Pull the sibilance band down while it is loud, and let go when it is not.\n *\n * Web Audio has no side-chain, so the detector is built by hand: a band-pass\n * feeds an analyser watching only 6-8 kHz, and that analyser's energy drives the\n * gain of a peaking filter sitting in the main path. The distinction is the whole\n * stage — the cut has to answer to the *band*, not to how loud the person is. A\n * static cut at the same frequency would dull every consonant instead of the\n * syllables that actually hiss.\n *\n * @param context - The graph's context.\n * @returns The stage's nodes and its teardown.\n */\nexport function deEsserStage(context: AudioContext): DeEsserStage {\n const band = context.createBiquadFilter();\n band.type = \"bandpass\";\n band.frequency.value = SIBILANCE_HZ;\n band.Q.value = SIBILANCE_Q;\n\n const detector = detectorFor(context);\n band.connect(detector);\n\n const shaper = context.createBiquadFilter();\n shaper.type = \"peaking\";\n shaper.frequency.value = SIBILANCE_HZ;\n shaper.Q.value = SIBILANCE_Q;\n shaper.gain.value = 0;\n\n const buffer = new Float32Array(detector.fftSize);\n const timer = setInterval(() => {\n detector.getFloatTimeDomainData(buffer);\n const over = Math.max(0, rms(buffer) - DEESSER_THRESHOLD);\n const cut = Math.min(DEESSER_MAX_CUT_DB, over * DEESSER_SLOPE_DB_PER_RMS);\n shaper.gain.setTargetAtTime(-cut, context.currentTime, DEESSER_SMOOTHING);\n }, DETECTOR_POLL_MS);\n\n return { band, detector, shaper, stop: () => clearInterval(timer) };\n}\n\nexport { rms as windowRms };\n"],"mappings":"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"}