tempest-react-sdk 0.78.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 (1585) hide show
  1. package/README.md +6 -6
  2. package/bin/lib/openapi/generate.mjs +168 -64
  3. package/bin/lib/openapi/generate.pagination.test.mjs +3 -2
  4. package/bin/lib/openapi/generate.test.mjs +263 -1
  5. package/dist/access/Can.cjs.map +1 -1
  6. package/dist/access/Can.js.map +1 -1
  7. package/dist/access/access-control-context.cjs.map +1 -1
  8. package/dist/access/access-control-context.js.map +1 -1
  9. package/dist/access/create-role-access-control.cjs.map +1 -1
  10. package/dist/access/create-role-access-control.js.map +1 -1
  11. package/dist/access/permissions-from-token.cjs.map +1 -1
  12. package/dist/access/permissions-from-token.js.map +1 -1
  13. package/dist/access/use-can.cjs.map +1 -1
  14. package/dist/access/use-can.js.map +1 -1
  15. package/dist/app/AppProviders.cjs.map +1 -1
  16. package/dist/app/AppProviders.js.map +1 -1
  17. package/dist/audio/audio-bus.cjs.map +1 -1
  18. package/dist/audio/audio-bus.js.map +1 -1
  19. package/dist/audio/audio-output.cjs.map +1 -1
  20. package/dist/audio/audio-output.js.map +1 -1
  21. package/dist/audio/audio-player.cjs.map +1 -1
  22. package/dist/audio/audio-player.js.map +1 -1
  23. package/dist/audio/audio-recorder.cjs.map +1 -1
  24. package/dist/audio/audio-recorder.js.map +1 -1
  25. package/dist/audio/duration.cjs.map +1 -1
  26. package/dist/audio/duration.js.map +1 -1
  27. package/dist/audio/level-meter.cjs.map +1 -1
  28. package/dist/audio/level-meter.js.map +1 -1
  29. package/dist/audio/media-access.cjs.map +1 -1
  30. package/dist/audio/media-access.js.map +1 -1
  31. package/dist/audio/noise-floor.cjs.map +1 -1
  32. package/dist/audio/noise-floor.js.map +1 -1
  33. package/dist/audio/sfx-pool.cjs.map +1 -1
  34. package/dist/audio/sfx-pool.js.map +1 -1
  35. package/dist/audio/use-audio-bus.cjs.map +1 -1
  36. package/dist/audio/use-audio-bus.js.map +1 -1
  37. package/dist/audio/use-audio-recorder.cjs.map +1 -1
  38. package/dist/audio/use-audio-recorder.js.map +1 -1
  39. package/dist/audio/use-audio.cjs.map +1 -1
  40. package/dist/audio/use-audio.js.map +1 -1
  41. package/dist/audio/use-microphone.cjs +1 -1
  42. package/dist/audio/use-microphone.cjs.map +1 -1
  43. package/dist/audio/use-microphone.js +1 -5
  44. package/dist/audio/use-microphone.js.map +1 -1
  45. package/dist/audio/use-sfx-pool.cjs.map +1 -1
  46. package/dist/audio/use-sfx-pool.js.map +1 -1
  47. package/dist/audio/voice-activity.cjs.map +1 -1
  48. package/dist/audio/voice-activity.js.map +1 -1
  49. package/dist/audio/voice-chain-stages.cjs.map +1 -1
  50. package/dist/audio/voice-chain-stages.js.map +1 -1
  51. package/dist/audio/voice-chain.cjs.map +1 -1
  52. package/dist/audio/voice-chain.js.map +1 -1
  53. package/dist/audio/wav.cjs.map +1 -1
  54. package/dist/audio/wav.js.map +1 -1
  55. package/dist/auth/AuthGuard.cjs.map +1 -1
  56. package/dist/auth/AuthGuard.js.map +1 -1
  57. package/dist/auth/create-auth-store.cjs.map +1 -1
  58. package/dist/auth/create-auth-store.js.map +1 -1
  59. package/dist/auth/create-tempest-auth.cjs +1 -1
  60. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  61. package/dist/auth/create-tempest-auth.js +1 -3
  62. package/dist/auth/create-tempest-auth.js.map +1 -1
  63. package/dist/auth/jwt.cjs.map +1 -1
  64. package/dist/auth/jwt.js.map +1 -1
  65. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  66. package/dist/auth/lazy-with-retry.js.map +1 -1
  67. package/dist/auth/passkey.cjs.map +1 -1
  68. package/dist/auth/passkey.js.map +1 -1
  69. package/dist/auth/refresh-queue.cjs.map +1 -1
  70. package/dist/auth/refresh-queue.js.map +1 -1
  71. package/dist/auth/use-passkey.cjs +1 -1
  72. package/dist/auth/use-passkey.cjs.map +1 -1
  73. package/dist/auth/use-passkey.js +1 -1
  74. package/dist/auth/use-passkey.js.map +1 -1
  75. package/dist/br/BrazilMap.cjs.map +1 -1
  76. package/dist/br/BrazilMap.js.map +1 -1
  77. package/dist/br/BrazilMap.module.cjs.map +1 -1
  78. package/dist/br/BrazilMap.module.js.map +1 -1
  79. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  80. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  81. package/dist/br/BrazilStateMap.cjs.map +1 -1
  82. package/dist/br/BrazilStateMap.js.map +1 -1
  83. package/dist/br/MapLegend.cjs.map +1 -1
  84. package/dist/br/MapLegend.js.map +1 -1
  85. package/dist/br/MapLegend.module.cjs.map +1 -1
  86. package/dist/br/MapLegend.module.js.map +1 -1
  87. package/dist/br/MapMarkers.cjs.map +1 -1
  88. package/dist/br/MapMarkers.js.map +1 -1
  89. package/dist/br/MapTooltip.cjs.map +1 -1
  90. package/dist/br/MapTooltip.js.map +1 -1
  91. package/dist/br/MunicipalitySearch.cjs +1 -1
  92. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  93. package/dist/br/MunicipalitySearch.js +2 -5
  94. package/dist/br/MunicipalitySearch.js.map +1 -1
  95. package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
  96. package/dist/br/MunicipalitySearch.module.js.map +1 -1
  97. package/dist/br/PixQRCode.cjs.map +1 -1
  98. package/dist/br/PixQRCode.js.map +1 -1
  99. package/dist/br/PixQRCode.module.cjs.map +1 -1
  100. package/dist/br/PixQRCode.module.js.map +1 -1
  101. package/dist/br/boleto.cjs.map +1 -1
  102. package/dist/br/boleto.js.map +1 -1
  103. package/dist/br/br-geo.cjs.map +1 -1
  104. package/dist/br/br-geo.js.map +1 -1
  105. package/dist/br/data/br-centroids.cjs.map +1 -1
  106. package/dist/br/data/br-centroids.js.map +1 -1
  107. package/dist/br/data/br-locations.cjs.map +1 -1
  108. package/dist/br/data/br-locations.js.map +1 -1
  109. package/dist/br/data/br-uf-geo.cjs.map +1 -1
  110. package/dist/br/data/br-uf-geo.js.map +1 -1
  111. package/dist/br/data/mun/AC.cjs.map +1 -1
  112. package/dist/br/data/mun/AC.js.map +1 -1
  113. package/dist/br/data/mun/AL.cjs.map +1 -1
  114. package/dist/br/data/mun/AL.js.map +1 -1
  115. package/dist/br/data/mun/AM.cjs.map +1 -1
  116. package/dist/br/data/mun/AM.js.map +1 -1
  117. package/dist/br/data/mun/AP.cjs.map +1 -1
  118. package/dist/br/data/mun/AP.js.map +1 -1
  119. package/dist/br/data/mun/BA.cjs.map +1 -1
  120. package/dist/br/data/mun/BA.js.map +1 -1
  121. package/dist/br/data/mun/CE.cjs.map +1 -1
  122. package/dist/br/data/mun/CE.js.map +1 -1
  123. package/dist/br/data/mun/DF.cjs.map +1 -1
  124. package/dist/br/data/mun/DF.js.map +1 -1
  125. package/dist/br/data/mun/ES.cjs.map +1 -1
  126. package/dist/br/data/mun/ES.js.map +1 -1
  127. package/dist/br/data/mun/GO.cjs.map +1 -1
  128. package/dist/br/data/mun/GO.js.map +1 -1
  129. package/dist/br/data/mun/MA.cjs.map +1 -1
  130. package/dist/br/data/mun/MA.js.map +1 -1
  131. package/dist/br/data/mun/MG.cjs.map +1 -1
  132. package/dist/br/data/mun/MG.js.map +1 -1
  133. package/dist/br/data/mun/MS.cjs.map +1 -1
  134. package/dist/br/data/mun/MS.js.map +1 -1
  135. package/dist/br/data/mun/MT.cjs.map +1 -1
  136. package/dist/br/data/mun/MT.js.map +1 -1
  137. package/dist/br/data/mun/PA.cjs.map +1 -1
  138. package/dist/br/data/mun/PA.js.map +1 -1
  139. package/dist/br/data/mun/PB.cjs.map +1 -1
  140. package/dist/br/data/mun/PB.js.map +1 -1
  141. package/dist/br/data/mun/PE.cjs.map +1 -1
  142. package/dist/br/data/mun/PE.js.map +1 -1
  143. package/dist/br/data/mun/PI.cjs.map +1 -1
  144. package/dist/br/data/mun/PI.js.map +1 -1
  145. package/dist/br/data/mun/PR.cjs.map +1 -1
  146. package/dist/br/data/mun/PR.js.map +1 -1
  147. package/dist/br/data/mun/RJ.cjs.map +1 -1
  148. package/dist/br/data/mun/RJ.js.map +1 -1
  149. package/dist/br/data/mun/RN.cjs.map +1 -1
  150. package/dist/br/data/mun/RN.js.map +1 -1
  151. package/dist/br/data/mun/RO.cjs.map +1 -1
  152. package/dist/br/data/mun/RO.js.map +1 -1
  153. package/dist/br/data/mun/RR.cjs.map +1 -1
  154. package/dist/br/data/mun/RR.js.map +1 -1
  155. package/dist/br/data/mun/RS.cjs.map +1 -1
  156. package/dist/br/data/mun/RS.js.map +1 -1
  157. package/dist/br/data/mun/SC.cjs.map +1 -1
  158. package/dist/br/data/mun/SC.js.map +1 -1
  159. package/dist/br/data/mun/SE.cjs.map +1 -1
  160. package/dist/br/data/mun/SE.js.map +1 -1
  161. package/dist/br/data/mun/SP.cjs.map +1 -1
  162. package/dist/br/data/mun/SP.js.map +1 -1
  163. package/dist/br/data/mun/TO.cjs.map +1 -1
  164. package/dist/br/data/mun/TO.js.map +1 -1
  165. package/dist/br/geocode.cjs.map +1 -1
  166. package/dist/br/geocode.js.map +1 -1
  167. package/dist/br/holidays.cjs.map +1 -1
  168. package/dist/br/holidays.js.map +1 -1
  169. package/dist/br/locations.cjs.map +1 -1
  170. package/dist/br/locations.js.map +1 -1
  171. package/dist/br/nfe.cjs.map +1 -1
  172. package/dist/br/nfe.js.map +1 -1
  173. package/dist/br/pix.cjs +1 -1
  174. package/dist/br/pix.cjs.map +1 -1
  175. package/dist/br/pix.js +108 -74
  176. package/dist/br/pix.js.map +1 -1
  177. package/dist/br/regions.cjs.map +1 -1
  178. package/dist/br/regions.js.map +1 -1
  179. package/dist/br/scales.cjs.map +1 -1
  180. package/dist/br/scales.js.map +1 -1
  181. package/dist/br/state-geo.cjs.map +1 -1
  182. package/dist/br/state-geo.js.map +1 -1
  183. package/dist/br/svg-utils.cjs.map +1 -1
  184. package/dist/br/svg-utils.js.map +1 -1
  185. package/dist/br/use-map-hover.cjs.map +1 -1
  186. package/dist/br/use-map-hover.js.map +1 -1
  187. package/dist/br/use-map-zoom.cjs.map +1 -1
  188. package/dist/br/use-map-zoom.js.map +1 -1
  189. package/dist/br/whatsapp.cjs.map +1 -1
  190. package/dist/br/whatsapp.js.map +1 -1
  191. package/dist/br.cjs.map +1 -1
  192. package/dist/br.d.ts +6 -2
  193. package/dist/br.js.map +1 -1
  194. package/dist/capture/barcode.cjs.map +1 -1
  195. package/dist/capture/barcode.js.map +1 -1
  196. package/dist/capture/media-recorder.cjs.map +1 -1
  197. package/dist/capture/media-recorder.js.map +1 -1
  198. package/dist/capture/use-barcode-scanner.cjs.map +1 -1
  199. package/dist/capture/use-barcode-scanner.js.map +1 -1
  200. package/dist/capture/use-screen-capture.cjs +1 -1
  201. package/dist/capture/use-screen-capture.cjs.map +1 -1
  202. package/dist/capture/use-screen-capture.js +1 -7
  203. package/dist/capture/use-screen-capture.js.map +1 -1
  204. package/dist/capture/use-speech-recognition.cjs +1 -1
  205. package/dist/capture/use-speech-recognition.cjs.map +1 -1
  206. package/dist/capture/use-speech-recognition.js +35 -36
  207. package/dist/capture/use-speech-recognition.js.map +1 -1
  208. package/dist/capture/use-torch.cjs.map +1 -1
  209. package/dist/capture/use-torch.js.map +1 -1
  210. package/dist/capture/use-video-recorder.cjs.map +1 -1
  211. package/dist/capture/use-video-recorder.js.map +1 -1
  212. package/dist/capture/video-recorder.cjs.map +1 -1
  213. package/dist/capture/video-recorder.js.map +1 -1
  214. package/dist/charts/AreaChart.cjs.map +1 -1
  215. package/dist/charts/AreaChart.js.map +1 -1
  216. package/dist/charts/BarChart.cjs.map +1 -1
  217. package/dist/charts/BarChart.js.map +1 -1
  218. package/dist/charts/LineChart.cjs.map +1 -1
  219. package/dist/charts/LineChart.js.map +1 -1
  220. package/dist/charts/PieChart.cjs.map +1 -1
  221. package/dist/charts/PieChart.js.map +1 -1
  222. package/dist/charts/RadarChart.cjs.map +1 -1
  223. package/dist/charts/RadarChart.js.map +1 -1
  224. package/dist/charts/palette.cjs.map +1 -1
  225. package/dist/charts/palette.js.map +1 -1
  226. package/dist/charts/scales.cjs.map +1 -1
  227. package/dist/charts/scales.js.map +1 -1
  228. package/dist/charts/types.cjs.map +1 -1
  229. package/dist/charts/types.js.map +1 -1
  230. package/dist/charts/use-chart-colors.cjs.map +1 -1
  231. package/dist/charts/use-chart-colors.js.map +1 -1
  232. package/dist/components/AIChat/AIChat.cjs +1 -1
  233. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  234. package/dist/components/AIChat/AIChat.js +1 -5
  235. package/dist/components/AIChat/AIChat.js.map +1 -1
  236. package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
  237. package/dist/components/AIChat/AIChat.module.js.map +1 -1
  238. package/dist/components/AIChat/AIChatComposer.cjs +1 -1
  239. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  240. package/dist/components/AIChat/AIChatComposer.js +1 -7
  241. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  242. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
  243. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  244. package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -1
  245. package/dist/components/AIChat/ai-chat-turns.js.map +1 -1
  246. package/dist/components/Accordion/Accordion.cjs.map +1 -1
  247. package/dist/components/Accordion/Accordion.js.map +1 -1
  248. package/dist/components/Accordion/Accordion.module.cjs.map +1 -1
  249. package/dist/components/Accordion/Accordion.module.js.map +1 -1
  250. package/dist/components/Alert/Alert.cjs.map +1 -1
  251. package/dist/components/Alert/Alert.js.map +1 -1
  252. package/dist/components/Alert/Alert.module.cjs.map +1 -1
  253. package/dist/components/Alert/Alert.module.js.map +1 -1
  254. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  255. package/dist/components/AppBar/AppBar.js.map +1 -1
  256. package/dist/components/AppBar/AppBar.module.cjs.map +1 -1
  257. package/dist/components/AppBar/AppBar.module.js.map +1 -1
  258. package/dist/components/AppBar/use-sticky-body-warning.cjs +1 -1
  259. package/dist/components/AppBar/use-sticky-body-warning.cjs.map +1 -1
  260. package/dist/components/AppBar/use-sticky-body-warning.js +1 -1
  261. package/dist/components/AppBar/use-sticky-body-warning.js.map +1 -1
  262. package/dist/components/AppShell/AppShell.cjs.map +1 -1
  263. package/dist/components/AppShell/AppShell.js.map +1 -1
  264. package/dist/components/AppShell/AppShell.module.cjs.map +1 -1
  265. package/dist/components/AppShell/AppShell.module.js.map +1 -1
  266. package/dist/components/AspectRatio/AspectRatio.cjs.map +1 -1
  267. package/dist/components/AspectRatio/AspectRatio.js.map +1 -1
  268. package/dist/components/AspectRatio/AspectRatio.module.cjs.map +1 -1
  269. package/dist/components/AspectRatio/AspectRatio.module.js.map +1 -1
  270. package/dist/components/AudioPlayer/AudioPlayer.cjs +1 -1
  271. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  272. package/dist/components/AudioPlayer/AudioPlayer.js +1 -7
  273. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
  274. package/dist/components/AudioPlayer/AudioPlayer.module.cjs.map +1 -1
  275. package/dist/components/AudioPlayer/AudioPlayer.module.js.map +1 -1
  276. package/dist/components/AudioRecorder/AudioRecorder.cjs +1 -1
  277. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
  278. package/dist/components/AudioRecorder/AudioRecorder.js +16 -21
  279. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
  280. package/dist/components/AudioRecorder/AudioRecorder.module.cjs.map +1 -1
  281. package/dist/components/AudioRecorder/AudioRecorder.module.js.map +1 -1
  282. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  283. package/dist/components/Avatar/Avatar.js.map +1 -1
  284. package/dist/components/Avatar/Avatar.module.cjs.map +1 -1
  285. package/dist/components/Avatar/Avatar.module.js.map +1 -1
  286. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
  287. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
  288. package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -1
  289. package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -1
  290. package/dist/components/Badge/Badge.cjs.map +1 -1
  291. package/dist/components/Badge/Badge.js.map +1 -1
  292. package/dist/components/Badge/Badge.module.cjs.map +1 -1
  293. package/dist/components/Badge/Badge.module.js.map +1 -1
  294. package/dist/components/Banner/Banner.cjs.map +1 -1
  295. package/dist/components/Banner/Banner.js.map +1 -1
  296. package/dist/components/Banner/Banner.module.cjs.map +1 -1
  297. package/dist/components/Banner/Banner.module.js.map +1 -1
  298. package/dist/components/BarList/BarList.cjs.map +1 -1
  299. package/dist/components/BarList/BarList.js.map +1 -1
  300. package/dist/components/BarList/BarList.module.cjs.map +1 -1
  301. package/dist/components/BarList/BarList.module.js.map +1 -1
  302. package/dist/components/BarList/bar-list-model.cjs.map +1 -1
  303. package/dist/components/BarList/bar-list-model.js.map +1 -1
  304. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
  305. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
  306. package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs.map +1 -1
  307. package/dist/components/BarcodeScanner/BarcodeScanner.module.js.map +1 -1
  308. package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  309. package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
  310. package/dist/components/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
  311. package/dist/components/BottomNavigation/BottomNavigation.module.js.map +1 -1
  312. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  313. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  314. package/dist/components/BottomSheet/BottomSheet.module.cjs.map +1 -1
  315. package/dist/components/BottomSheet/BottomSheet.module.js.map +1 -1
  316. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  317. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  318. package/dist/components/Breadcrumbs/Breadcrumbs.module.cjs.map +1 -1
  319. package/dist/components/Breadcrumbs/Breadcrumbs.module.js.map +1 -1
  320. package/dist/components/Button/Button.cjs.map +1 -1
  321. package/dist/components/Button/Button.js.map +1 -1
  322. package/dist/components/Button/Button.module.cjs.map +1 -1
  323. package/dist/components/Button/Button.module.js.map +1 -1
  324. package/dist/components/Button/ButtonContent.cjs.map +1 -1
  325. package/dist/components/Button/ButtonContent.js.map +1 -1
  326. package/dist/components/Button/ButtonSlot.cjs.map +1 -1
  327. package/dist/components/Button/ButtonSlot.js.map +1 -1
  328. package/dist/components/Button/button-class.cjs.map +1 -1
  329. package/dist/components/Button/button-class.js.map +1 -1
  330. package/dist/components/Button/link-rel.cjs.map +1 -1
  331. package/dist/components/Button/link-rel.js.map +1 -1
  332. package/dist/components/Button/slot.cjs.map +1 -1
  333. package/dist/components/Button/slot.js.map +1 -1
  334. package/dist/components/Calendar/Calendar.cjs.map +1 -1
  335. package/dist/components/Calendar/Calendar.js.map +1 -1
  336. package/dist/components/Calendar/Calendar.module.cjs.map +1 -1
  337. package/dist/components/Calendar/Calendar.module.js.map +1 -1
  338. package/dist/components/Card/Card.cjs.map +1 -1
  339. package/dist/components/Card/Card.js.map +1 -1
  340. package/dist/components/Card/Card.module.cjs.map +1 -1
  341. package/dist/components/Card/Card.module.js.map +1 -1
  342. package/dist/components/Carousel/Carousel.cjs.map +1 -1
  343. package/dist/components/Carousel/Carousel.js.map +1 -1
  344. package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
  345. package/dist/components/Carousel/Carousel.module.js.map +1 -1
  346. package/dist/components/Center/Center.cjs.map +1 -1
  347. package/dist/components/Center/Center.js.map +1 -1
  348. package/dist/components/Center/Center.module.cjs.map +1 -1
  349. package/dist/components/Center/Center.module.js.map +1 -1
  350. package/dist/components/Chat/Chat.cjs.map +1 -1
  351. package/dist/components/Chat/Chat.js.map +1 -1
  352. package/dist/components/Chat/Chat.module.cjs.map +1 -1
  353. package/dist/components/Chat/Chat.module.js.map +1 -1
  354. package/dist/components/Chat/ChatActionsMenu.cjs.map +1 -1
  355. package/dist/components/Chat/ChatActionsMenu.js.map +1 -1
  356. package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
  357. package/dist/components/Chat/ChatAttachments.js.map +1 -1
  358. package/dist/components/Chat/ChatBubble.cjs.map +1 -1
  359. package/dist/components/Chat/ChatBubble.js.map +1 -1
  360. package/dist/components/Chat/ChatComposer.cjs.map +1 -1
  361. package/dist/components/Chat/ChatComposer.js.map +1 -1
  362. package/dist/components/Chat/chat-groups.cjs +1 -1
  363. package/dist/components/Chat/chat-groups.cjs.map +1 -1
  364. package/dist/components/Chat/chat-groups.js +2 -6
  365. package/dist/components/Chat/chat-groups.js.map +1 -1
  366. package/dist/components/Chat/chat-receipt.cjs.map +1 -1
  367. package/dist/components/Chat/chat-receipt.js.map +1 -1
  368. package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
  369. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  370. package/dist/components/Checkbox/Checkbox.module.cjs.map +1 -1
  371. package/dist/components/Checkbox/Checkbox.module.js.map +1 -1
  372. package/dist/components/ChipInput/ChipInput.cjs +1 -1
  373. package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
  374. package/dist/components/ChipInput/ChipInput.js +2 -12
  375. package/dist/components/ChipInput/ChipInput.js.map +1 -1
  376. package/dist/components/ChipInput/ChipInput.module.cjs.map +1 -1
  377. package/dist/components/ChipInput/ChipInput.module.js.map +1 -1
  378. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  379. package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
  380. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
  381. package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
  382. package/dist/components/CodeBlock/CodeBlock.module.cjs.map +1 -1
  383. package/dist/components/CodeBlock/CodeBlock.module.js.map +1 -1
  384. package/dist/components/CodeBlock/tokenize.cjs.map +1 -1
  385. package/dist/components/CodeBlock/tokenize.js.map +1 -1
  386. package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
  387. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  388. package/dist/components/Collapsible/Collapsible.module.cjs.map +1 -1
  389. package/dist/components/Collapsible/Collapsible.module.js.map +1 -1
  390. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  391. package/dist/components/Combobox/Combobox.js.map +1 -1
  392. package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
  393. package/dist/components/Combobox/Combobox.module.js.map +1 -1
  394. package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -1
  395. package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -1
  396. package/dist/components/Command/Command.cjs +1 -1
  397. package/dist/components/Command/Command.cjs.map +1 -1
  398. package/dist/components/Command/Command.js +4 -11
  399. package/dist/components/Command/Command.js.map +1 -1
  400. package/dist/components/Command/Command.module.cjs.map +1 -1
  401. package/dist/components/Command/Command.module.js.map +1 -1
  402. package/dist/components/ConditionalWrapper/ConditionalWrapper.cjs.map +1 -1
  403. package/dist/components/ConditionalWrapper/ConditionalWrapper.js.map +1 -1
  404. package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
  405. package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
  406. package/dist/components/ContextMenu/ContextMenu.cjs +1 -1
  407. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  408. package/dist/components/ContextMenu/ContextMenu.js +1 -1
  409. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  410. package/dist/components/ContextMenu/ContextMenu.module.cjs.map +1 -1
  411. package/dist/components/ContextMenu/ContextMenu.module.js.map +1 -1
  412. package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
  413. package/dist/components/CopyButton/CopyButton.js.map +1 -1
  414. package/dist/components/CopyButton/CopyButton.module.cjs.map +1 -1
  415. package/dist/components/CopyButton/CopyButton.module.js.map +1 -1
  416. package/dist/components/DataList/DataList.cjs.map +1 -1
  417. package/dist/components/DataList/DataList.js.map +1 -1
  418. package/dist/components/DataList/DataList.module.cjs.map +1 -1
  419. package/dist/components/DataList/DataList.module.js.map +1 -1
  420. package/dist/components/DataTable/DataTable.cjs +1 -1
  421. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  422. package/dist/components/DataTable/DataTable.js +6 -14
  423. package/dist/components/DataTable/DataTable.js.map +1 -1
  424. package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
  425. package/dist/components/DataTable/DataTable.module.js.map +1 -1
  426. package/dist/components/DataTable/EditableCell.cjs +1 -1
  427. package/dist/components/DataTable/EditableCell.cjs.map +1 -1
  428. package/dist/components/DataTable/EditableCell.js +1 -9
  429. package/dist/components/DataTable/EditableCell.js.map +1 -1
  430. package/dist/components/DataTable/LoadingRows.cjs.map +1 -1
  431. package/dist/components/DataTable/LoadingRows.js.map +1 -1
  432. package/dist/components/DataTable/edit-labels.cjs.map +1 -1
  433. package/dist/components/DataTable/edit-labels.js.map +1 -1
  434. package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
  435. package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
  436. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  437. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  438. package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
  439. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  440. package/dist/components/DateRangePicker/DateRangePicker.module.cjs.map +1 -1
  441. package/dist/components/DateRangePicker/DateRangePicker.module.js.map +1 -1
  442. package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
  443. package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
  444. package/dist/components/DescriptionList/DescriptionList.module.cjs.map +1 -1
  445. package/dist/components/DescriptionList/DescriptionList.module.js.map +1 -1
  446. package/dist/components/Divider/Divider.cjs.map +1 -1
  447. package/dist/components/Divider/Divider.js.map +1 -1
  448. package/dist/components/Divider/Divider.module.cjs.map +1 -1
  449. package/dist/components/Divider/Divider.module.js.map +1 -1
  450. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  451. package/dist/components/Drawer/Drawer.js.map +1 -1
  452. package/dist/components/Drawer/Drawer.module.cjs.map +1 -1
  453. package/dist/components/Drawer/Drawer.module.js.map +1 -1
  454. package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
  455. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  456. package/dist/components/DropdownMenu/DropdownMenu.js +2 -14
  457. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  458. package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
  459. package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
  460. package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
  461. package/dist/components/Dropzone/Dropzone.js.map +1 -1
  462. package/dist/components/Dropzone/Dropzone.module.cjs.map +1 -1
  463. package/dist/components/Dropzone/Dropzone.module.js.map +1 -1
  464. package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
  465. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  466. package/dist/components/EmptyState/EmptyState.module.cjs.map +1 -1
  467. package/dist/components/EmptyState/EmptyState.module.js.map +1 -1
  468. package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
  469. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  470. package/dist/components/ErrorState/ErrorState.module.cjs.map +1 -1
  471. package/dist/components/ErrorState/ErrorState.module.js.map +1 -1
  472. package/dist/components/ErrorText/ErrorText.cjs.map +1 -1
  473. package/dist/components/ErrorText/ErrorText.js.map +1 -1
  474. package/dist/components/ErrorText/ErrorText.module.cjs.map +1 -1
  475. package/dist/components/ErrorText/ErrorText.module.js.map +1 -1
  476. package/dist/components/FileUpload/FileUpload.cjs +1 -1
  477. package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
  478. package/dist/components/FileUpload/FileUpload.js +7 -17
  479. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  480. package/dist/components/FileUpload/FileUpload.module.cjs.map +1 -1
  481. package/dist/components/FileUpload/FileUpload.module.js.map +1 -1
  482. package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
  483. package/dist/components/FilterBar/FilterBar.js.map +1 -1
  484. package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -1
  485. package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
  486. package/dist/components/FilterBar/filter-apply.cjs.map +1 -1
  487. package/dist/components/FilterBar/filter-apply.js.map +1 -1
  488. package/dist/components/FilterBar/filter-model.cjs.map +1 -1
  489. package/dist/components/FilterBar/filter-model.js.map +1 -1
  490. package/dist/components/FilterBar/filter-query.cjs +1 -1
  491. package/dist/components/FilterBar/filter-query.cjs.map +1 -1
  492. package/dist/components/FilterBar/filter-query.js +2 -5
  493. package/dist/components/FilterBar/filter-query.js.map +1 -1
  494. package/dist/components/FilterPanel/FilterPanel.cjs.map +1 -1
  495. package/dist/components/FilterPanel/FilterPanel.js.map +1 -1
  496. package/dist/components/FilterPanel/FilterPanel.module.cjs.map +1 -1
  497. package/dist/components/FilterPanel/FilterPanel.module.js.map +1 -1
  498. package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
  499. package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
  500. package/dist/components/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
  501. package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
  502. package/dist/components/For/For.cjs.map +1 -1
  503. package/dist/components/For/For.js.map +1 -1
  504. package/dist/components/Form/Form.cjs.map +1 -1
  505. package/dist/components/Form/Form.js.map +1 -1
  506. package/dist/components/Form/Form.module.cjs.map +1 -1
  507. package/dist/components/Form/Form.module.js.map +1 -1
  508. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  509. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  510. package/dist/components/HoverCard/HoverCard.module.cjs.map +1 -1
  511. package/dist/components/HoverCard/HoverCard.module.js.map +1 -1
  512. package/dist/components/Image/Image.cjs.map +1 -1
  513. package/dist/components/Image/Image.js.map +1 -1
  514. package/dist/components/Image/Image.module.cjs.map +1 -1
  515. package/dist/components/Image/Image.module.js.map +1 -1
  516. package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
  517. package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
  518. package/dist/components/ImageCropper/ImageCropper.js +4 -8
  519. package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
  520. package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
  521. package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
  522. package/dist/components/ImageCropper/crop-geometry.cjs.map +1 -1
  523. package/dist/components/ImageCropper/crop-geometry.js.map +1 -1
  524. package/dist/components/Input/Input.cjs.map +1 -1
  525. package/dist/components/Input/Input.js.map +1 -1
  526. package/dist/components/Input/Input.module.cjs.map +1 -1
  527. package/dist/components/Input/Input.module.js.map +1 -1
  528. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  529. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  530. package/dist/components/InstallBanner/InstallBanner.module.cjs.map +1 -1
  531. package/dist/components/InstallBanner/InstallBanner.module.js.map +1 -1
  532. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  533. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  534. package/dist/components/InstallButton/InstallButton.module.cjs.map +1 -1
  535. package/dist/components/InstallButton/InstallButton.module.js.map +1 -1
  536. package/dist/components/InstallButton/install-hints.cjs.map +1 -1
  537. package/dist/components/InstallButton/install-hints.js.map +1 -1
  538. package/dist/components/Kanban/Kanban.cjs.map +1 -1
  539. package/dist/components/Kanban/Kanban.js.map +1 -1
  540. package/dist/components/Kanban/Kanban.module.cjs.map +1 -1
  541. package/dist/components/Kanban/Kanban.module.js.map +1 -1
  542. package/dist/components/Kbd/Kbd.cjs.map +1 -1
  543. package/dist/components/Kbd/Kbd.js.map +1 -1
  544. package/dist/components/Kbd/Kbd.module.cjs.map +1 -1
  545. package/dist/components/Kbd/Kbd.module.js.map +1 -1
  546. package/dist/components/Label/Label.cjs.map +1 -1
  547. package/dist/components/Label/Label.js.map +1 -1
  548. package/dist/components/Label/Label.module.cjs.map +1 -1
  549. package/dist/components/Label/Label.module.js.map +1 -1
  550. package/dist/components/Layout/Layout.cjs.map +1 -1
  551. package/dist/components/Layout/Layout.js.map +1 -1
  552. package/dist/components/Layout/Layout.module.cjs.map +1 -1
  553. package/dist/components/Layout/Layout.module.js.map +1 -1
  554. package/dist/components/Lightbox/Lightbox.cjs +1 -1
  555. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  556. package/dist/components/Lightbox/Lightbox.js +1 -1
  557. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  558. package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -1
  559. package/dist/components/Lightbox/Lightbox.module.js.map +1 -1
  560. package/dist/components/ListTile/ListTile.cjs.map +1 -1
  561. package/dist/components/ListTile/ListTile.js.map +1 -1
  562. package/dist/components/ListTile/ListTile.module.cjs.map +1 -1
  563. package/dist/components/ListTile/ListTile.module.js.map +1 -1
  564. package/dist/components/Markdown/Markdown.cjs.map +1 -1
  565. package/dist/components/Markdown/Markdown.js.map +1 -1
  566. package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
  567. package/dist/components/Markdown/Markdown.module.js.map +1 -1
  568. package/dist/components/Markdown/markdown-parse.cjs +2 -2
  569. package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
  570. package/dist/components/Markdown/markdown-parse.js +8 -27
  571. package/dist/components/Markdown/markdown-parse.js.map +1 -1
  572. package/dist/components/Masonry/Masonry.cjs.map +1 -1
  573. package/dist/components/Masonry/Masonry.js.map +1 -1
  574. package/dist/components/Masonry/Masonry.module.cjs.map +1 -1
  575. package/dist/components/Masonry/Masonry.module.js.map +1 -1
  576. package/dist/components/Masonry/masonry-layout.cjs.map +1 -1
  577. package/dist/components/Masonry/masonry-layout.js.map +1 -1
  578. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  579. package/dist/components/Menubar/Menubar.js.map +1 -1
  580. package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
  581. package/dist/components/Menubar/Menubar.module.js.map +1 -1
  582. package/dist/components/Modal/Modal.cjs.map +1 -1
  583. package/dist/components/Modal/Modal.js.map +1 -1
  584. package/dist/components/Modal/Modal.module.cjs.map +1 -1
  585. package/dist/components/Modal/Modal.module.js.map +1 -1
  586. package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
  587. package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
  588. package/dist/components/ModalsManager/ModalsManager.module.cjs.map +1 -1
  589. package/dist/components/ModalsManager/ModalsManager.module.js.map +1 -1
  590. package/dist/components/Money/Money.cjs.map +1 -1
  591. package/dist/components/Money/Money.js.map +1 -1
  592. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  593. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  594. package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
  595. package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
  596. package/dist/components/NProgress/NProgress.cjs.map +1 -1
  597. package/dist/components/NProgress/NProgress.js.map +1 -1
  598. package/dist/components/NProgress/NProgress.module.cjs.map +1 -1
  599. package/dist/components/NProgress/NProgress.module.js.map +1 -1
  600. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  601. package/dist/components/Navbar/Navbar.js.map +1 -1
  602. package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
  603. package/dist/components/Navbar/Navbar.module.js.map +1 -1
  604. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  605. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  606. package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
  607. package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
  608. package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
  609. package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
  610. package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
  611. package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
  612. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
  613. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  614. package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -1
  615. package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
  616. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
  617. package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -1
  618. package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
  619. package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
  620. package/dist/components/OfflineIndicator/OfflineIndicator.module.cjs.map +1 -1
  621. package/dist/components/OfflineIndicator/OfflineIndicator.module.js.map +1 -1
  622. package/dist/components/Page/Page.cjs.map +1 -1
  623. package/dist/components/Page/Page.js.map +1 -1
  624. package/dist/components/Page/Page.module.cjs.map +1 -1
  625. package/dist/components/Page/Page.module.js.map +1 -1
  626. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  627. package/dist/components/Pagination/Pagination.js.map +1 -1
  628. package/dist/components/Pagination/Pagination.module.cjs.map +1 -1
  629. package/dist/components/Pagination/Pagination.module.js.map +1 -1
  630. package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
  631. package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
  632. package/dist/components/PasswordInput/PasswordInput.module.cjs.map +1 -1
  633. package/dist/components/PasswordInput/PasswordInput.module.js.map +1 -1
  634. package/dist/components/PinInput/PinInput.cjs.map +1 -1
  635. package/dist/components/PinInput/PinInput.js.map +1 -1
  636. package/dist/components/PinInput/PinInput.module.cjs.map +1 -1
  637. package/dist/components/PinInput/PinInput.module.js.map +1 -1
  638. package/dist/components/Popover/Popover.cjs.map +1 -1
  639. package/dist/components/Popover/Popover.js.map +1 -1
  640. package/dist/components/Popover/Popover.module.cjs.map +1 -1
  641. package/dist/components/Popover/Popover.module.js.map +1 -1
  642. package/dist/components/Portal/Portal.cjs.map +1 -1
  643. package/dist/components/Portal/Portal.js.map +1 -1
  644. package/dist/components/Portal/anchor-position.cjs.map +1 -1
  645. package/dist/components/Portal/anchor-position.js.map +1 -1
  646. package/dist/components/Portal/escape-layer.cjs.map +1 -1
  647. package/dist/components/Portal/escape-layer.js.map +1 -1
  648. package/dist/components/Portal/portal-host.cjs.map +1 -1
  649. package/dist/components/Portal/portal-host.js.map +1 -1
  650. package/dist/components/Portal/tab-bridge.cjs +1 -1
  651. package/dist/components/Portal/tab-bridge.cjs.map +1 -1
  652. package/dist/components/Portal/tab-bridge.js +1 -5
  653. package/dist/components/Portal/tab-bridge.js.map +1 -1
  654. package/dist/components/Progress/Progress.cjs.map +1 -1
  655. package/dist/components/Progress/Progress.js.map +1 -1
  656. package/dist/components/Progress/Progress.module.cjs.map +1 -1
  657. package/dist/components/Progress/Progress.module.js.map +1 -1
  658. package/dist/components/QRCode/QRCode.cjs.map +1 -1
  659. package/dist/components/QRCode/QRCode.js.map +1 -1
  660. package/dist/components/QRCode/QRCode.module.cjs.map +1 -1
  661. package/dist/components/QRCode/QRCode.module.js.map +1 -1
  662. package/dist/components/QRCode/qr-encode.cjs +1 -1
  663. package/dist/components/QRCode/qr-encode.cjs.map +1 -1
  664. package/dist/components/QRCode/qr-encode.js +7 -13
  665. package/dist/components/QRCode/qr-encode.js.map +1 -1
  666. package/dist/components/QRCode/qr-tables.cjs.map +1 -1
  667. package/dist/components/QRCode/qr-tables.js.map +1 -1
  668. package/dist/components/Radio/Radio.cjs.map +1 -1
  669. package/dist/components/Radio/Radio.js.map +1 -1
  670. package/dist/components/Radio/Radio.module.cjs.map +1 -1
  671. package/dist/components/Radio/Radio.module.js.map +1 -1
  672. package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
  673. package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
  674. package/dist/components/RangeSlider/RangeSlider.module.cjs.map +1 -1
  675. package/dist/components/RangeSlider/RangeSlider.module.js.map +1 -1
  676. package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
  677. package/dist/components/RatingStars/RatingStars.js.map +1 -1
  678. package/dist/components/RatingStars/RatingStars.module.cjs.map +1 -1
  679. package/dist/components/RatingStars/RatingStars.module.js.map +1 -1
  680. package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
  681. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  682. package/dist/components/RefreshIndicator/RefreshIndicator.js +10 -18
  683. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  684. package/dist/components/RefreshIndicator/RefreshIndicator.module.cjs.map +1 -1
  685. package/dist/components/RefreshIndicator/RefreshIndicator.module.js.map +1 -1
  686. package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
  687. package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
  688. package/dist/components/Resizable/Resizable.cjs +1 -1
  689. package/dist/components/Resizable/Resizable.cjs.map +1 -1
  690. package/dist/components/Resizable/Resizable.js +1 -9
  691. package/dist/components/Resizable/Resizable.js.map +1 -1
  692. package/dist/components/Resizable/Resizable.module.cjs.map +1 -1
  693. package/dist/components/Resizable/Resizable.module.js.map +1 -1
  694. package/dist/components/Responsive/Responsive.cjs.map +1 -1
  695. package/dist/components/Responsive/Responsive.js.map +1 -1
  696. package/dist/components/Reveal/Reveal.cjs.map +1 -1
  697. package/dist/components/Reveal/Reveal.js.map +1 -1
  698. package/dist/components/Reveal/Reveal.module.cjs.map +1 -1
  699. package/dist/components/Reveal/Reveal.module.js.map +1 -1
  700. package/dist/components/Reveal/reveal-threshold.cjs.map +1 -1
  701. package/dist/components/Reveal/reveal-threshold.js.map +1 -1
  702. package/dist/components/SafeArea/SafeArea.cjs.map +1 -1
  703. package/dist/components/SafeArea/SafeArea.js.map +1 -1
  704. package/dist/components/SafeArea/SafeArea.module.cjs.map +1 -1
  705. package/dist/components/SafeArea/SafeArea.module.js.map +1 -1
  706. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  707. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  708. package/dist/components/Scheduler/Scheduler.module.cjs.map +1 -1
  709. package/dist/components/Scheduler/Scheduler.module.js.map +1 -1
  710. package/dist/components/Scheduler/scheduler-layout.cjs.map +1 -1
  711. package/dist/components/Scheduler/scheduler-layout.js.map +1 -1
  712. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  713. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  714. package/dist/components/ScrollArea/ScrollArea.module.cjs.map +1 -1
  715. package/dist/components/ScrollArea/ScrollArea.module.js.map +1 -1
  716. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  717. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  718. package/dist/components/SearchBar/SearchBar.module.cjs.map +1 -1
  719. package/dist/components/SearchBar/SearchBar.module.js.map +1 -1
  720. package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
  721. package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
  722. package/dist/components/SectionHeader/SectionHeader.module.cjs.map +1 -1
  723. package/dist/components/SectionHeader/SectionHeader.module.js.map +1 -1
  724. package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  725. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  726. package/dist/components/SegmentedControl/SegmentedControl.module.cjs.map +1 -1
  727. package/dist/components/SegmentedControl/SegmentedControl.module.js.map +1 -1
  728. package/dist/components/Select/CaretIcon.cjs.map +1 -1
  729. package/dist/components/Select/CaretIcon.js.map +1 -1
  730. package/dist/components/Select/Select.cjs.map +1 -1
  731. package/dist/components/Select/Select.js.map +1 -1
  732. package/dist/components/Select/Select.module.cjs.map +1 -1
  733. package/dist/components/Select/Select.module.js.map +1 -1
  734. package/dist/components/Select/options.cjs.map +1 -1
  735. package/dist/components/Select/options.js.map +1 -1
  736. package/dist/components/Sidebar/Sidebar.cjs +1 -1
  737. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  738. package/dist/components/Sidebar/Sidebar.js +13 -23
  739. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  740. package/dist/components/Sidebar/Sidebar.module.cjs.map +1 -1
  741. package/dist/components/Sidebar/Sidebar.module.js.map +1 -1
  742. package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
  743. package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
  744. package/dist/components/Sidebar/active-nav-key.cjs.map +1 -1
  745. package/dist/components/Sidebar/active-nav-key.js.map +1 -1
  746. package/dist/components/SignaturePad/SignaturePad.cjs +1 -1
  747. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
  748. package/dist/components/SignaturePad/SignaturePad.js +1 -5
  749. package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
  750. package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -1
  751. package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -1
  752. package/dist/components/Skeleton/Skeleton.cjs.map +1 -1
  753. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  754. package/dist/components/Skeleton/Skeleton.module.cjs.map +1 -1
  755. package/dist/components/Skeleton/Skeleton.module.js.map +1 -1
  756. package/dist/components/Slider/Slider.cjs.map +1 -1
  757. package/dist/components/Slider/Slider.js.map +1 -1
  758. package/dist/components/Slider/Slider.module.cjs.map +1 -1
  759. package/dist/components/Slider/Slider.module.js.map +1 -1
  760. package/dist/components/Spacer/Spacer.cjs.map +1 -1
  761. package/dist/components/Spacer/Spacer.js.map +1 -1
  762. package/dist/components/Spacer/Spacer.module.cjs.map +1 -1
  763. package/dist/components/Spacer/Spacer.module.js.map +1 -1
  764. package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
  765. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  766. package/dist/components/Sparkline/Sparkline.module.cjs.map +1 -1
  767. package/dist/components/Sparkline/Sparkline.module.js.map +1 -1
  768. package/dist/components/Sparkline/sparkline-geometry.cjs.map +1 -1
  769. package/dist/components/Sparkline/sparkline-geometry.js.map +1 -1
  770. package/dist/components/Spinner/Spinner.cjs.map +1 -1
  771. package/dist/components/Spinner/Spinner.js.map +1 -1
  772. package/dist/components/Spinner/Spinner.module.cjs.map +1 -1
  773. package/dist/components/Spinner/Spinner.module.js.map +1 -1
  774. package/dist/components/Stat/Stat.cjs.map +1 -1
  775. package/dist/components/Stat/Stat.js.map +1 -1
  776. package/dist/components/Stat/Stat.module.cjs.map +1 -1
  777. package/dist/components/Stat/Stat.module.js.map +1 -1
  778. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  779. package/dist/components/Stepper/Stepper.js.map +1 -1
  780. package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
  781. package/dist/components/Stepper/Stepper.module.js.map +1 -1
  782. package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
  783. package/dist/components/StepperInput/StepperInput.js.map +1 -1
  784. package/dist/components/StepperInput/StepperInput.module.cjs.map +1 -1
  785. package/dist/components/StepperInput/StepperInput.module.js.map +1 -1
  786. package/dist/components/Switch/Switch.cjs.map +1 -1
  787. package/dist/components/Switch/Switch.js.map +1 -1
  788. package/dist/components/Switch/Switch.module.cjs.map +1 -1
  789. package/dist/components/Switch/Switch.module.js.map +1 -1
  790. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  791. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  792. package/dist/components/SyncStatusBadge/SyncStatusBadge.module.cjs.map +1 -1
  793. package/dist/components/SyncStatusBadge/SyncStatusBadge.module.js.map +1 -1
  794. package/dist/components/Table/Table.cjs.map +1 -1
  795. package/dist/components/Table/Table.js.map +1 -1
  796. package/dist/components/Table/Table.module.cjs.map +1 -1
  797. package/dist/components/Table/Table.module.js.map +1 -1
  798. package/dist/components/Tabs/Tabs.cjs.map +1 -1
  799. package/dist/components/Tabs/Tabs.js.map +1 -1
  800. package/dist/components/Tabs/Tabs.module.cjs.map +1 -1
  801. package/dist/components/Tabs/Tabs.module.js.map +1 -1
  802. package/dist/components/Tag/Tag.cjs.map +1 -1
  803. package/dist/components/Tag/Tag.js.map +1 -1
  804. package/dist/components/Tag/Tag.module.cjs.map +1 -1
  805. package/dist/components/Tag/Tag.module.js.map +1 -1
  806. package/dist/components/Textarea/Textarea.cjs.map +1 -1
  807. package/dist/components/Textarea/Textarea.js.map +1 -1
  808. package/dist/components/Textarea/Textarea.module.cjs.map +1 -1
  809. package/dist/components/Textarea/Textarea.module.js.map +1 -1
  810. package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
  811. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  812. package/dist/components/TimePicker/TimePicker.module.cjs.map +1 -1
  813. package/dist/components/TimePicker/TimePicker.module.js.map +1 -1
  814. package/dist/components/Timeline/Timeline.cjs.map +1 -1
  815. package/dist/components/Timeline/Timeline.js.map +1 -1
  816. package/dist/components/Timeline/Timeline.module.cjs.map +1 -1
  817. package/dist/components/Timeline/Timeline.module.js.map +1 -1
  818. package/dist/components/Toast/Toast.module.cjs.map +1 -1
  819. package/dist/components/Toast/Toast.module.js.map +1 -1
  820. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  821. package/dist/components/Toast/ToastProvider.js.map +1 -1
  822. package/dist/components/Toggle/Toggle.cjs.map +1 -1
  823. package/dist/components/Toggle/Toggle.js.map +1 -1
  824. package/dist/components/Toggle/Toggle.module.cjs.map +1 -1
  825. package/dist/components/Toggle/Toggle.module.js.map +1 -1
  826. package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
  827. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  828. package/dist/components/ToggleGroup/ToggleGroup.module.cjs.map +1 -1
  829. package/dist/components/ToggleGroup/ToggleGroup.module.js.map +1 -1
  830. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  831. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  832. package/dist/components/Tooltip/Tooltip.module.cjs.map +1 -1
  833. package/dist/components/Tooltip/Tooltip.module.js.map +1 -1
  834. package/dist/components/Tour/Tour.cjs +1 -1
  835. package/dist/components/Tour/Tour.cjs.map +1 -1
  836. package/dist/components/Tour/Tour.js +1 -5
  837. package/dist/components/Tour/Tour.js.map +1 -1
  838. package/dist/components/Tour/Tour.module.cjs.map +1 -1
  839. package/dist/components/Tour/Tour.module.js.map +1 -1
  840. package/dist/components/Tour/tour-position.cjs.map +1 -1
  841. package/dist/components/Tour/tour-position.js.map +1 -1
  842. package/dist/components/Transfer/Transfer.cjs.map +1 -1
  843. package/dist/components/Transfer/Transfer.js.map +1 -1
  844. package/dist/components/Transfer/Transfer.module.cjs.map +1 -1
  845. package/dist/components/Transfer/Transfer.module.js.map +1 -1
  846. package/dist/components/Transfer/transfer-state.cjs.map +1 -1
  847. package/dist/components/Transfer/transfer-state.js.map +1 -1
  848. package/dist/components/TreeView/TreeView.cjs.map +1 -1
  849. package/dist/components/TreeView/TreeView.js.map +1 -1
  850. package/dist/components/TreeView/TreeView.module.cjs.map +1 -1
  851. package/dist/components/TreeView/TreeView.module.js.map +1 -1
  852. package/dist/components/TruncateText/TruncateText.cjs.map +1 -1
  853. package/dist/components/TruncateText/TruncateText.js.map +1 -1
  854. package/dist/components/TruncateText/TruncateText.module.cjs.map +1 -1
  855. package/dist/components/TruncateText/TruncateText.module.js.map +1 -1
  856. package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
  857. package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
  858. package/dist/components/UpdatePrompt/UpdatePrompt.module.cjs.map +1 -1
  859. package/dist/components/UpdatePrompt/UpdatePrompt.module.js.map +1 -1
  860. package/dist/components/VideoPlayer/VideoPlayer.cjs +1 -1
  861. package/dist/components/VideoPlayer/VideoPlayer.cjs.map +1 -1
  862. package/dist/components/VideoPlayer/VideoPlayer.js +1 -7
  863. package/dist/components/VideoPlayer/VideoPlayer.js.map +1 -1
  864. package/dist/components/VideoPlayer/VideoPlayer.module.cjs.map +1 -1
  865. package/dist/components/VideoPlayer/VideoPlayer.module.js.map +1 -1
  866. package/dist/components/VideoPlayer/playback-rates.cjs.map +1 -1
  867. package/dist/components/VideoPlayer/playback-rates.js.map +1 -1
  868. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  869. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  870. package/dist/components/VirtualList/VirtualList.module.cjs.map +1 -1
  871. package/dist/components/VirtualList/VirtualList.module.js.map +1 -1
  872. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  873. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  874. package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -1
  875. package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
  876. package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
  877. package/dist/components/VisuallyHidden/VisuallyHidden.js.map +1 -1
  878. package/dist/components/VisuallyHidden/VisuallyHidden.module.cjs.map +1 -1
  879. package/dist/components/VisuallyHidden/VisuallyHidden.module.js.map +1 -1
  880. package/dist/components/Wizard/Wizard.cjs +1 -1
  881. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  882. package/dist/components/Wizard/Wizard.js +4 -11
  883. package/dist/components/Wizard/Wizard.js.map +1 -1
  884. package/dist/components/Wizard/Wizard.module.cjs.map +1 -1
  885. package/dist/components/Wizard/Wizard.module.js.map +1 -1
  886. package/dist/data/create-data-provider.cjs.map +1 -1
  887. package/dist/data/create-data-provider.js.map +1 -1
  888. package/dist/data/data-provider-context.cjs.map +1 -1
  889. package/dist/data/data-provider-context.js.map +1 -1
  890. package/dist/data/use-resource.cjs.map +1 -1
  891. package/dist/data/use-resource.js.map +1 -1
  892. package/dist/editor/RichTextEditor.cjs.map +1 -1
  893. package/dist/editor/RichTextEditor.js.map +1 -1
  894. package/dist/editor/RichTextEditor.module.cjs.map +1 -1
  895. package/dist/editor/RichTextEditor.module.js.map +1 -1
  896. package/dist/editor.js +1 -2
  897. package/dist/error-boundary/ErrorBoundary.cjs.map +1 -1
  898. package/dist/error-boundary/ErrorBoundary.js.map +1 -1
  899. package/dist/error-boundary/use-error-handler.cjs.map +1 -1
  900. package/dist/error-boundary/use-error-handler.js.map +1 -1
  901. package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
  902. package/dist/feature-flags/FeatureFlagsProvider.js.map +1 -1
  903. package/dist/feature-flags/growthbook-adapter.cjs.map +1 -1
  904. package/dist/feature-flags/growthbook-adapter.js.map +1 -1
  905. package/dist/feature-flags/in-memory-adapter.cjs.map +1 -1
  906. package/dist/feature-flags/in-memory-adapter.js.map +1 -1
  907. package/dist/feature-flags/launchdarkly-adapter.cjs.map +1 -1
  908. package/dist/feature-flags/launchdarkly-adapter.js.map +1 -1
  909. package/dist/forms/FormField.cjs.map +1 -1
  910. package/dist/forms/FormField.js.map +1 -1
  911. package/dist/forms/br-validators.cjs.map +1 -1
  912. package/dist/forms/br-validators.js.map +1 -1
  913. package/dist/forms/masked-inputs.cjs.map +1 -1
  914. package/dist/forms/masked-inputs.js.map +1 -1
  915. package/dist/forms/use-viacep.cjs.map +1 -1
  916. package/dist/forms/use-viacep.js.map +1 -1
  917. package/dist/forms/use-zod-form.cjs.map +1 -1
  918. package/dist/forms/use-zod-form.js.map +1 -1
  919. package/dist/forms/validate-form.cjs.map +1 -1
  920. package/dist/forms/validate-form.js.map +1 -1
  921. package/dist/forms/zod-resolver.cjs.map +1 -1
  922. package/dist/forms/zod-resolver.js.map +1 -1
  923. package/dist/geo/TrajectoryMap.cjs.map +1 -1
  924. package/dist/geo/TrajectoryMap.js.map +1 -1
  925. package/dist/geo/TrajectoryMap.module.cjs.map +1 -1
  926. package/dist/geo/TrajectoryMap.module.js.map +1 -1
  927. package/dist/geo/bounds.cjs.map +1 -1
  928. package/dist/geo/bounds.js.map +1 -1
  929. package/dist/geo/create-position-tracker.cjs +1 -1
  930. package/dist/geo/create-position-tracker.cjs.map +1 -1
  931. package/dist/geo/create-position-tracker.js +1 -7
  932. package/dist/geo/create-position-tracker.js.map +1 -1
  933. package/dist/geo/distance.cjs.map +1 -1
  934. package/dist/geo/distance.js.map +1 -1
  935. package/dist/geo/estimate.cjs.map +1 -1
  936. package/dist/geo/estimate.js.map +1 -1
  937. package/dist/geo/leaflet-map.cjs.map +1 -1
  938. package/dist/geo/leaflet-map.js.map +1 -1
  939. package/dist/geo/projection.cjs.map +1 -1
  940. package/dist/geo/projection.js.map +1 -1
  941. package/dist/geo/routing.cjs.map +1 -1
  942. package/dist/geo/routing.js.map +1 -1
  943. package/dist/geo/types.cjs.map +1 -1
  944. package/dist/geo/types.js.map +1 -1
  945. package/dist/geo/use-position-tracker.cjs.map +1 -1
  946. package/dist/geo/use-position-tracker.js.map +1 -1
  947. package/dist/hooks/pwa-env.cjs.map +1 -1
  948. package/dist/hooks/pwa-env.js.map +1 -1
  949. package/dist/hooks/tab-sequence.cjs.map +1 -1
  950. package/dist/hooks/tab-sequence.js.map +1 -1
  951. package/dist/hooks/use-announce.cjs +1 -1
  952. package/dist/hooks/use-announce.cjs.map +1 -1
  953. package/dist/hooks/use-announce.js +2 -2
  954. package/dist/hooks/use-announce.js.map +1 -1
  955. package/dist/hooks/use-async.cjs.map +1 -1
  956. package/dist/hooks/use-async.js.map +1 -1
  957. package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
  958. package/dist/hooks/use-before-install-prompt.js.map +1 -1
  959. package/dist/hooks/use-breakpoint.cjs.map +1 -1
  960. package/dist/hooks/use-breakpoint.js.map +1 -1
  961. package/dist/hooks/use-click-outside.cjs.map +1 -1
  962. package/dist/hooks/use-click-outside.js.map +1 -1
  963. package/dist/hooks/use-client-filter.cjs.map +1 -1
  964. package/dist/hooks/use-client-filter.js.map +1 -1
  965. package/dist/hooks/use-clipboard.cjs.map +1 -1
  966. package/dist/hooks/use-clipboard.js.map +1 -1
  967. package/dist/hooks/use-countdown.cjs.map +1 -1
  968. package/dist/hooks/use-countdown.js.map +1 -1
  969. package/dist/hooks/use-counter.cjs.map +1 -1
  970. package/dist/hooks/use-counter.js.map +1 -1
  971. package/dist/hooks/use-debounce.cjs.map +1 -1
  972. package/dist/hooks/use-debounce.js.map +1 -1
  973. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  974. package/dist/hooks/use-deep-memo.js.map +1 -1
  975. package/dist/hooks/use-disclosure.cjs.map +1 -1
  976. package/dist/hooks/use-disclosure.js.map +1 -1
  977. package/dist/hooks/use-document-title.cjs.map +1 -1
  978. package/dist/hooks/use-document-title.js.map +1 -1
  979. package/dist/hooks/use-document-visibility.cjs.map +1 -1
  980. package/dist/hooks/use-document-visibility.js.map +1 -1
  981. package/dist/hooks/use-draft-filters.cjs.map +1 -1
  982. package/dist/hooks/use-draft-filters.js.map +1 -1
  983. package/dist/hooks/use-event-listener.cjs.map +1 -1
  984. package/dist/hooks/use-event-listener.js.map +1 -1
  985. package/dist/hooks/use-favicon.cjs.map +1 -1
  986. package/dist/hooks/use-favicon.js.map +1 -1
  987. package/dist/hooks/use-focus-trap.cjs.map +1 -1
  988. package/dist/hooks/use-focus-trap.js.map +1 -1
  989. package/dist/hooks/use-fullscreen-element.cjs.map +1 -1
  990. package/dist/hooks/use-fullscreen-element.js.map +1 -1
  991. package/dist/hooks/use-fullscreen.cjs +1 -1
  992. package/dist/hooks/use-fullscreen.cjs.map +1 -1
  993. package/dist/hooks/use-fullscreen.js +7 -23
  994. package/dist/hooks/use-fullscreen.js.map +1 -1
  995. package/dist/hooks/use-geolocation.cjs.map +1 -1
  996. package/dist/hooks/use-geolocation.js.map +1 -1
  997. package/dist/hooks/use-hover.cjs.map +1 -1
  998. package/dist/hooks/use-hover.js.map +1 -1
  999. package/dist/hooks/use-idle.cjs.map +1 -1
  1000. package/dist/hooks/use-idle.js.map +1 -1
  1001. package/dist/hooks/use-install-prompt.cjs +1 -1
  1002. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  1003. package/dist/hooks/use-install-prompt.js +1 -1
  1004. package/dist/hooks/use-install-prompt.js.map +1 -1
  1005. package/dist/hooks/use-intersection-observer.cjs.map +1 -1
  1006. package/dist/hooks/use-intersection-observer.js.map +1 -1
  1007. package/dist/hooks/use-interval.cjs.map +1 -1
  1008. package/dist/hooks/use-interval.js.map +1 -1
  1009. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  1010. package/dist/hooks/use-is-first-render.js.map +1 -1
  1011. package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
  1012. package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
  1013. package/dist/hooks/use-latest-ref.cjs.map +1 -1
  1014. package/dist/hooks/use-latest-ref.js.map +1 -1
  1015. package/dist/hooks/use-list-state.cjs.map +1 -1
  1016. package/dist/hooks/use-list-state.js.map +1 -1
  1017. package/dist/hooks/use-local-storage.cjs +1 -1
  1018. package/dist/hooks/use-local-storage.cjs.map +1 -1
  1019. package/dist/hooks/use-local-storage.js +2 -5
  1020. package/dist/hooks/use-local-storage.js.map +1 -1
  1021. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  1022. package/dist/hooks/use-long-press-handlers.js.map +1 -1
  1023. package/dist/hooks/use-long-press.cjs.map +1 -1
  1024. package/dist/hooks/use-long-press.js.map +1 -1
  1025. package/dist/hooks/use-map.cjs.map +1 -1
  1026. package/dist/hooks/use-map.js.map +1 -1
  1027. package/dist/hooks/use-media-devices.cjs.map +1 -1
  1028. package/dist/hooks/use-media-devices.js.map +1 -1
  1029. package/dist/hooks/use-media-permission.cjs +1 -1
  1030. package/dist/hooks/use-media-permission.cjs.map +1 -1
  1031. package/dist/hooks/use-media-permission.js +2 -5
  1032. package/dist/hooks/use-media-permission.js.map +1 -1
  1033. package/dist/hooks/use-media-query.cjs.map +1 -1
  1034. package/dist/hooks/use-media-query.js.map +1 -1
  1035. package/dist/hooks/use-object-url.cjs.map +1 -1
  1036. package/dist/hooks/use-object-url.js.map +1 -1
  1037. package/dist/hooks/use-online.cjs.map +1 -1
  1038. package/dist/hooks/use-online.js.map +1 -1
  1039. package/dist/hooks/use-pagination.cjs.map +1 -1
  1040. package/dist/hooks/use-pagination.js.map +1 -1
  1041. package/dist/hooks/use-previous.cjs.map +1 -1
  1042. package/dist/hooks/use-previous.js.map +1 -1
  1043. package/dist/hooks/use-push-to-talk.cjs.map +1 -1
  1044. package/dist/hooks/use-push-to-talk.js.map +1 -1
  1045. package/dist/hooks/use-queue.cjs.map +1 -1
  1046. package/dist/hooks/use-queue.js.map +1 -1
  1047. package/dist/hooks/use-resize-observer.cjs.map +1 -1
  1048. package/dist/hooks/use-resize-observer.js.map +1 -1
  1049. package/dist/hooks/use-scroll-lock.cjs.map +1 -1
  1050. package/dist/hooks/use-scroll-lock.js.map +1 -1
  1051. package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
  1052. package/dist/hooks/use-scroll-overflow.js.map +1 -1
  1053. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  1054. package/dist/hooks/use-service-worker-update.js.map +1 -1
  1055. package/dist/hooks/use-set.cjs.map +1 -1
  1056. package/dist/hooks/use-set.js.map +1 -1
  1057. package/dist/hooks/use-sortable.cjs.map +1 -1
  1058. package/dist/hooks/use-sortable.js.map +1 -1
  1059. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  1060. package/dist/hooks/use-stable-callback.js.map +1 -1
  1061. package/dist/hooks/use-storage-estimate.cjs.map +1 -1
  1062. package/dist/hooks/use-storage-estimate.js.map +1 -1
  1063. package/dist/hooks/use-throttle.cjs.map +1 -1
  1064. package/dist/hooks/use-throttle.js.map +1 -1
  1065. package/dist/hooks/use-timeout.cjs.map +1 -1
  1066. package/dist/hooks/use-timeout.js.map +1 -1
  1067. package/dist/hooks/use-toggle.cjs.map +1 -1
  1068. package/dist/hooks/use-toggle.js.map +1 -1
  1069. package/dist/hooks/use-typewriter.cjs.map +1 -1
  1070. package/dist/hooks/use-typewriter.js.map +1 -1
  1071. package/dist/hooks/use-window-size.cjs.map +1 -1
  1072. package/dist/hooks/use-window-size.js.map +1 -1
  1073. package/dist/http/api-client.cjs.map +1 -1
  1074. package/dist/http/api-client.js.map +1 -1
  1075. package/dist/http/build-url.cjs.map +1 -1
  1076. package/dist/http/build-url.js.map +1 -1
  1077. package/dist/http/credential-scope.cjs.map +1 -1
  1078. package/dist/http/credential-scope.js.map +1 -1
  1079. package/dist/http/csrf.cjs.map +1 -1
  1080. package/dist/http/csrf.js.map +1 -1
  1081. package/dist/http/decode-response.cjs.map +1 -1
  1082. package/dist/http/decode-response.js.map +1 -1
  1083. package/dist/http/describe-api-error.cjs.map +1 -1
  1084. package/dist/http/describe-api-error.js.map +1 -1
  1085. package/dist/http/errors.cjs.map +1 -1
  1086. package/dist/http/errors.js.map +1 -1
  1087. package/dist/http/idempotency.cjs.map +1 -1
  1088. package/dist/http/idempotency.js.map +1 -1
  1089. package/dist/http/parse-response.cjs.map +1 -1
  1090. package/dist/http/parse-response.js.map +1 -1
  1091. package/dist/http/resumable-upload.cjs.map +1 -1
  1092. package/dist/http/resumable-upload.js.map +1 -1
  1093. package/dist/http/retry.cjs.map +1 -1
  1094. package/dist/http/retry.js.map +1 -1
  1095. package/dist/http/timeout.cjs.map +1 -1
  1096. package/dist/http/timeout.js.map +1 -1
  1097. package/dist/http/upload-with-progress.cjs +1 -1
  1098. package/dist/http/upload-with-progress.cjs.map +1 -1
  1099. package/dist/http/upload-with-progress.js +2 -7
  1100. package/dist/http/upload-with-progress.js.map +1 -1
  1101. package/dist/http/use-describe-api-error.cjs.map +1 -1
  1102. package/dist/http/use-describe-api-error.js.map +1 -1
  1103. package/dist/http/use-poll.cjs +1 -1
  1104. package/dist/http/use-poll.cjs.map +1 -1
  1105. package/dist/http/use-poll.js +2 -5
  1106. package/dist/http/use-poll.js.map +1 -1
  1107. package/dist/i18n/I18nProvider.cjs.map +1 -1
  1108. package/dist/i18n/I18nProvider.js.map +1 -1
  1109. package/dist/i18n/create-i18n.cjs.map +1 -1
  1110. package/dist/i18n/create-i18n.js.map +1 -1
  1111. package/dist/icons/Icon.cjs.map +1 -1
  1112. package/dist/icons/Icon.js.map +1 -1
  1113. package/dist/icons/IconPicker.cjs.map +1 -1
  1114. package/dist/icons/IconPicker.js.map +1 -1
  1115. package/dist/icons/IconPicker.module.cjs.map +1 -1
  1116. package/dist/icons/IconPicker.module.js.map +1 -1
  1117. package/dist/icons/IconProvider.cjs.map +1 -1
  1118. package/dist/icons/IconProvider.js.map +1 -1
  1119. package/dist/icons/alias.cjs.map +1 -1
  1120. package/dist/icons/alias.js.map +1 -1
  1121. package/dist/icons/generated/aliases.cjs.map +1 -1
  1122. package/dist/icons/generated/aliases.js.map +1 -1
  1123. package/dist/icons/generated/icon-names.cjs.map +1 -1
  1124. package/dist/icons/generated/icon-names.js.map +1 -1
  1125. package/dist/icons/generated/loaders.cjs.map +1 -1
  1126. package/dist/icons/generated/loaders.js.map +1 -1
  1127. package/dist/icons/generated/shard-00.cjs.map +1 -1
  1128. package/dist/icons/generated/shard-00.js.map +1 -1
  1129. package/dist/icons/generated/shard-01.cjs.map +1 -1
  1130. package/dist/icons/generated/shard-01.js.map +1 -1
  1131. package/dist/icons/generated/shard-02.cjs.map +1 -1
  1132. package/dist/icons/generated/shard-02.js.map +1 -1
  1133. package/dist/icons/generated/shard-03.cjs.map +1 -1
  1134. package/dist/icons/generated/shard-03.js.map +1 -1
  1135. package/dist/icons/generated/shard-04.cjs.map +1 -1
  1136. package/dist/icons/generated/shard-04.js.map +1 -1
  1137. package/dist/icons/generated/shard-05.cjs.map +1 -1
  1138. package/dist/icons/generated/shard-05.js.map +1 -1
  1139. package/dist/icons/generated/shard-06.cjs.map +1 -1
  1140. package/dist/icons/generated/shard-06.js.map +1 -1
  1141. package/dist/icons/generated/shard-07.cjs.map +1 -1
  1142. package/dist/icons/generated/shard-07.js.map +1 -1
  1143. package/dist/icons/generated/shard-08.cjs.map +1 -1
  1144. package/dist/icons/generated/shard-08.js.map +1 -1
  1145. package/dist/icons/generated/shard-09.cjs.map +1 -1
  1146. package/dist/icons/generated/shard-09.js.map +1 -1
  1147. package/dist/icons/generated/shard-10.cjs.map +1 -1
  1148. package/dist/icons/generated/shard-10.js.map +1 -1
  1149. package/dist/icons/generated/shard-11.cjs.map +1 -1
  1150. package/dist/icons/generated/shard-11.js.map +1 -1
  1151. package/dist/icons/generated/shard-12.cjs.map +1 -1
  1152. package/dist/icons/generated/shard-12.js.map +1 -1
  1153. package/dist/icons/generated/shard-13.cjs.map +1 -1
  1154. package/dist/icons/generated/shard-13.js.map +1 -1
  1155. package/dist/icons/generated/shard-14.cjs.map +1 -1
  1156. package/dist/icons/generated/shard-14.js.map +1 -1
  1157. package/dist/icons/generated/shard-15.cjs.map +1 -1
  1158. package/dist/icons/generated/shard-15.js.map +1 -1
  1159. package/dist/icons/generated/shard-16.cjs.map +1 -1
  1160. package/dist/icons/generated/shard-16.js.map +1 -1
  1161. package/dist/icons/generated/shard-17.cjs.map +1 -1
  1162. package/dist/icons/generated/shard-17.js.map +1 -1
  1163. package/dist/icons/generated/shard-18.cjs.map +1 -1
  1164. package/dist/icons/generated/shard-18.js.map +1 -1
  1165. package/dist/icons/generated/shard-19.cjs.map +1 -1
  1166. package/dist/icons/generated/shard-19.js.map +1 -1
  1167. package/dist/icons/generated/shard-20.cjs.map +1 -1
  1168. package/dist/icons/generated/shard-20.js.map +1 -1
  1169. package/dist/icons/generated/shard-21.cjs.map +1 -1
  1170. package/dist/icons/generated/shard-21.js.map +1 -1
  1171. package/dist/icons/generated/shard-22.cjs.map +1 -1
  1172. package/dist/icons/generated/shard-22.js.map +1 -1
  1173. package/dist/icons/generated/shard-23.cjs.map +1 -1
  1174. package/dist/icons/generated/shard-23.js.map +1 -1
  1175. package/dist/icons/generated/shard-24.cjs.map +1 -1
  1176. package/dist/icons/generated/shard-24.js.map +1 -1
  1177. package/dist/icons/generated/shard-25.cjs.map +1 -1
  1178. package/dist/icons/generated/shard-25.js.map +1 -1
  1179. package/dist/icons/generated/shard-26.cjs.map +1 -1
  1180. package/dist/icons/generated/shard-26.js.map +1 -1
  1181. package/dist/icons/generated/shard-27.cjs.map +1 -1
  1182. package/dist/icons/generated/shard-27.js.map +1 -1
  1183. package/dist/icons/generated/shard-28.cjs.map +1 -1
  1184. package/dist/icons/generated/shard-28.js.map +1 -1
  1185. package/dist/icons/generated/shard-29.cjs.map +1 -1
  1186. package/dist/icons/generated/shard-29.js.map +1 -1
  1187. package/dist/icons/generated/shard-30.cjs.map +1 -1
  1188. package/dist/icons/generated/shard-30.js.map +1 -1
  1189. package/dist/icons/generated/shard-31.cjs.map +1 -1
  1190. package/dist/icons/generated/shard-31.js.map +1 -1
  1191. package/dist/icons/generated/shard-32.cjs.map +1 -1
  1192. package/dist/icons/generated/shard-32.js.map +1 -1
  1193. package/dist/icons/generated/shard-33.cjs.map +1 -1
  1194. package/dist/icons/generated/shard-33.js.map +1 -1
  1195. package/dist/icons/generated/shard-34.cjs.map +1 -1
  1196. package/dist/icons/generated/shard-34.js.map +1 -1
  1197. package/dist/icons/generated/shard-35.cjs.map +1 -1
  1198. package/dist/icons/generated/shard-35.js.map +1 -1
  1199. package/dist/icons/generated/shard-36.cjs.map +1 -1
  1200. package/dist/icons/generated/shard-36.js.map +1 -1
  1201. package/dist/icons/generated/shard-37.cjs.map +1 -1
  1202. package/dist/icons/generated/shard-37.js.map +1 -1
  1203. package/dist/icons/generated/shard-38.cjs.map +1 -1
  1204. package/dist/icons/generated/shard-38.js.map +1 -1
  1205. package/dist/icons/generated/shard-39.cjs.map +1 -1
  1206. package/dist/icons/generated/shard-39.js.map +1 -1
  1207. package/dist/icons/generated/shard-40.cjs.map +1 -1
  1208. package/dist/icons/generated/shard-40.js.map +1 -1
  1209. package/dist/icons/generated/shard-41.cjs.map +1 -1
  1210. package/dist/icons/generated/shard-41.js.map +1 -1
  1211. package/dist/icons/generated/shard-42.cjs.map +1 -1
  1212. package/dist/icons/generated/shard-42.js.map +1 -1
  1213. package/dist/icons/generated/shard-43.cjs.map +1 -1
  1214. package/dist/icons/generated/shard-43.js.map +1 -1
  1215. package/dist/icons/generated/shard-44.cjs.map +1 -1
  1216. package/dist/icons/generated/shard-44.js.map +1 -1
  1217. package/dist/icons/generated/shard-45.cjs.map +1 -1
  1218. package/dist/icons/generated/shard-45.js.map +1 -1
  1219. package/dist/icons/icon-context.cjs.map +1 -1
  1220. package/dist/icons/icon-context.js.map +1 -1
  1221. package/dist/icons/is-icon-name.cjs.map +1 -1
  1222. package/dist/icons/is-icon-name.js.map +1 -1
  1223. package/dist/icons/material-symbols.cjs.map +1 -1
  1224. package/dist/icons/material-symbols.js.map +1 -1
  1225. package/dist/icons/normalize-icon-name.cjs.map +1 -1
  1226. package/dist/icons/normalize-icon-name.js.map +1 -1
  1227. package/dist/icons/shard-cache.cjs +1 -1
  1228. package/dist/icons/shard-cache.cjs.map +1 -1
  1229. package/dist/icons/shard-cache.js +2 -5
  1230. package/dist/icons/shard-cache.js.map +1 -1
  1231. package/dist/icons/use-icon.cjs.map +1 -1
  1232. package/dist/icons/use-icon.js.map +1 -1
  1233. package/dist/icons/validate-icon-name.cjs.map +1 -1
  1234. package/dist/icons/validate-icon-name.js.map +1 -1
  1235. package/dist/icons-virtual.cjs.map +1 -1
  1236. package/dist/icons-virtual.js.map +1 -1
  1237. package/dist/imaging/canvas.cjs.map +1 -1
  1238. package/dist/imaging/canvas.js.map +1 -1
  1239. package/dist/imaging/compress.cjs.map +1 -1
  1240. package/dist/imaging/compress.js.map +1 -1
  1241. package/dist/imaging/decode.cjs.map +1 -1
  1242. package/dist/imaging/decode.js.map +1 -1
  1243. package/dist/imaging/encode.cjs.map +1 -1
  1244. package/dist/imaging/encode.js.map +1 -1
  1245. package/dist/imaging/exceptions.cjs.map +1 -1
  1246. package/dist/imaging/exceptions.js.map +1 -1
  1247. package/dist/imaging/frame.cjs.map +1 -1
  1248. package/dist/imaging/frame.js.map +1 -1
  1249. package/dist/imaging/thumbnails.cjs.map +1 -1
  1250. package/dist/imaging/thumbnails.js.map +1 -1
  1251. package/dist/imaging/transform.cjs.map +1 -1
  1252. package/dist/imaging/transform.js.map +1 -1
  1253. package/dist/imaging/use-image-processing.cjs.map +1 -1
  1254. package/dist/imaging/use-image-processing.js.map +1 -1
  1255. package/dist/logger/logger.cjs.map +1 -1
  1256. package/dist/logger/logger.js.map +1 -1
  1257. package/dist/neural.cjs.map +1 -1
  1258. package/dist/neural.js.map +1 -1
  1259. package/dist/oauth/GoogleSignIn.cjs +1 -1
  1260. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  1261. package/dist/oauth/GoogleSignIn.js +6 -9
  1262. package/dist/oauth/GoogleSignIn.js.map +1 -1
  1263. package/dist/oauth/callback.cjs.map +1 -1
  1264. package/dist/oauth/callback.js.map +1 -1
  1265. package/dist/oauth/use-oauth-callback.cjs.map +1 -1
  1266. package/dist/oauth/use-oauth-callback.js.map +1 -1
  1267. package/dist/offline/conflict.cjs.map +1 -1
  1268. package/dist/offline/conflict.js.map +1 -1
  1269. package/dist/offline/create-offline-database.cjs.map +1 -1
  1270. package/dist/offline/create-offline-database.js.map +1 -1
  1271. package/dist/offline/create-offline-store.cjs +1 -1
  1272. package/dist/offline/create-offline-store.cjs.map +1 -1
  1273. package/dist/offline/create-offline-store.js +1 -5
  1274. package/dist/offline/create-offline-store.js.map +1 -1
  1275. package/dist/offline/create-offline-sync.cjs +1 -1
  1276. package/dist/offline/create-offline-sync.cjs.map +1 -1
  1277. package/dist/offline/create-offline-sync.js +9 -14
  1278. package/dist/offline/create-offline-sync.js.map +1 -1
  1279. package/dist/offline/use-offline-sync.cjs.map +1 -1
  1280. package/dist/offline/use-offline-sync.js.map +1 -1
  1281. package/dist/perf/cache-size.cjs.map +1 -1
  1282. package/dist/perf/cache-size.js.map +1 -1
  1283. package/dist/perf/device.cjs.map +1 -1
  1284. package/dist/perf/device.js.map +1 -1
  1285. package/dist/perf/format.cjs.map +1 -1
  1286. package/dist/perf/format.js.map +1 -1
  1287. package/dist/perf/profiler.cjs.map +1 -1
  1288. package/dist/perf/profiler.js.map +1 -1
  1289. package/dist/push/use-push-subscription.cjs.map +1 -1
  1290. package/dist/push/use-push-subscription.js.map +1 -1
  1291. package/dist/push/utils.cjs.map +1 -1
  1292. package/dist/push/utils.js.map +1 -1
  1293. package/dist/push/web-push-client.cjs.map +1 -1
  1294. package/dist/push/web-push-client.js.map +1 -1
  1295. package/dist/query/QueryProvider.cjs.map +1 -1
  1296. package/dist/query/QueryProvider.js.map +1 -1
  1297. package/dist/query/constants.cjs.map +1 -1
  1298. package/dist/query/constants.js.map +1 -1
  1299. package/dist/query/create-query-keys.cjs.map +1 -1
  1300. package/dist/query/create-query-keys.js.map +1 -1
  1301. package/dist/query/foreign-client-warning.cjs.map +1 -1
  1302. package/dist/query/foreign-client-warning.js.map +1 -1
  1303. package/dist/query/offline-persistence.cjs +1 -1
  1304. package/dist/query/offline-persistence.cjs.map +1 -1
  1305. package/dist/query/offline-persistence.js +2 -10
  1306. package/dist/query/offline-persistence.js.map +1 -1
  1307. package/dist/query/optimistic.cjs.map +1 -1
  1308. package/dist/query/optimistic.js.map +1 -1
  1309. package/dist/query/pagination.cjs.map +1 -1
  1310. package/dist/query/pagination.js.map +1 -1
  1311. package/dist/query/retry-policy.cjs.map +1 -1
  1312. package/dist/query/retry-policy.js.map +1 -1
  1313. package/dist/query/use-cursor-query.cjs.map +1 -1
  1314. package/dist/query/use-cursor-query.js.map +1 -1
  1315. package/dist/query/use-offline-mutation.cjs.map +1 -1
  1316. package/dist/query/use-offline-mutation.js.map +1 -1
  1317. package/dist/query/use-paginated-query.cjs.map +1 -1
  1318. package/dist/query/use-paginated-query.js.map +1 -1
  1319. package/dist/router/AppRouter.cjs.map +1 -1
  1320. package/dist/router/AppRouter.js.map +1 -1
  1321. package/dist/router/RouteGuard.cjs.map +1 -1
  1322. package/dist/router/RouteGuard.js.map +1 -1
  1323. package/dist/router/index.js +2 -2
  1324. package/dist/share/share-or-download.cjs.map +1 -1
  1325. package/dist/share/share-or-download.js.map +1 -1
  1326. package/dist/share/share.cjs.map +1 -1
  1327. package/dist/share/share.js.map +1 -1
  1328. package/dist/sse/create-event-stream.cjs.map +1 -1
  1329. package/dist/sse/create-event-stream.js.map +1 -1
  1330. package/dist/sse/use-event-stream.cjs.map +1 -1
  1331. package/dist/sse/use-event-stream.js.map +1 -1
  1332. package/dist/store/create-selectors.cjs.map +1 -1
  1333. package/dist/store/create-selectors.js.map +1 -1
  1334. package/dist/store/create-store.cjs.map +1 -1
  1335. package/dist/store/create-store.js.map +1 -1
  1336. package/dist/sw/background-sync.cjs.map +1 -1
  1337. package/dist/sw/background-sync.js.map +1 -1
  1338. package/dist/sw/cache-inspect.cjs.map +1 -1
  1339. package/dist/sw/cache-inspect.js.map +1 -1
  1340. package/dist/sw/cache.cjs +1 -1
  1341. package/dist/sw/cache.cjs.map +1 -1
  1342. package/dist/sw/cache.js +2 -6
  1343. package/dist/sw/cache.js.map +1 -1
  1344. package/dist/sw/create-push-handler.cjs.map +1 -1
  1345. package/dist/sw/create-push-handler.js.map +1 -1
  1346. package/dist/sw/credential-headers.cjs.map +1 -1
  1347. package/dist/sw/credential-headers.js.map +1 -1
  1348. package/dist/sw/periodic-sync.cjs.map +1 -1
  1349. package/dist/sw/periodic-sync.js.map +1 -1
  1350. package/dist/sw/register-service-worker.cjs.map +1 -1
  1351. package/dist/sw/register-service-worker.js.map +1 -1
  1352. package/dist/sw/session-data.cjs.map +1 -1
  1353. package/dist/sw/session-data.js.map +1 -1
  1354. package/dist/tabular/assets.cjs.map +1 -1
  1355. package/dist/tabular/assets.js.map +1 -1
  1356. package/dist/tabular/cache.cjs.map +1 -1
  1357. package/dist/tabular/cache.js.map +1 -1
  1358. package/dist/tabular/compact.cjs.map +1 -1
  1359. package/dist/tabular/compact.js.map +1 -1
  1360. package/dist/tabular/exceptions.cjs.map +1 -1
  1361. package/dist/tabular/exceptions.js.map +1 -1
  1362. package/dist/tabular/manifest.cjs.map +1 -1
  1363. package/dist/tabular/manifest.js.map +1 -1
  1364. package/dist/tabular/predictor.cjs.map +1 -1
  1365. package/dist/tabular/predictor.js.map +1 -1
  1366. package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
  1367. package/dist/tabular/use-tabular-predictor.js.map +1 -1
  1368. package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
  1369. package/dist/telemetry/TelemetryProvider.js.map +1 -1
  1370. package/dist/telemetry/app-error-adapter.cjs.map +1 -1
  1371. package/dist/telemetry/app-error-adapter.js.map +1 -1
  1372. package/dist/telemetry/app-error-queue.cjs.map +1 -1
  1373. package/dist/telemetry/app-error-queue.js.map +1 -1
  1374. package/dist/telemetry/app-error-reporter.cjs.map +1 -1
  1375. package/dist/telemetry/app-error-reporter.js.map +1 -1
  1376. package/dist/telemetry/app-error-schema.cjs.map +1 -1
  1377. package/dist/telemetry/app-error-schema.js.map +1 -1
  1378. package/dist/telemetry/app-error-transport.cjs.map +1 -1
  1379. package/dist/telemetry/app-error-transport.js.map +1 -1
  1380. package/dist/telemetry/console-adapter.cjs.map +1 -1
  1381. package/dist/telemetry/console-adapter.js.map +1 -1
  1382. package/dist/telemetry/describe-app-error.cjs.map +1 -1
  1383. package/dist/telemetry/describe-app-error.js.map +1 -1
  1384. package/dist/telemetry/posthog-adapter.cjs +1 -1
  1385. package/dist/telemetry/posthog-adapter.cjs.map +1 -1
  1386. package/dist/telemetry/posthog-adapter.js +1 -5
  1387. package/dist/telemetry/posthog-adapter.js.map +1 -1
  1388. package/dist/telemetry/sentry-adapter.cjs.map +1 -1
  1389. package/dist/telemetry/sentry-adapter.js.map +1 -1
  1390. package/dist/tempest-react-sdk.cjs +1 -1
  1391. package/dist/tempest-react-sdk.d.ts +234 -3
  1392. package/dist/tempest-react-sdk.js +85 -85
  1393. package/dist/testing/create-mock-handlers.cjs.map +1 -1
  1394. package/dist/testing/create-mock-handlers.js.map +1 -1
  1395. package/dist/testing.js +1 -2
  1396. package/dist/theme/ThemeProvider.cjs.map +1 -1
  1397. package/dist/theme/ThemeProvider.js.map +1 -1
  1398. package/dist/theme/apply-theme.cjs.map +1 -1
  1399. package/dist/theme/apply-theme.js.map +1 -1
  1400. package/dist/theme/color.cjs.map +1 -1
  1401. package/dist/theme/color.js.map +1 -1
  1402. package/dist/theme/create-theme.cjs.map +1 -1
  1403. package/dist/theme/create-theme.js.map +1 -1
  1404. package/dist/theme/data-viz-ramps.cjs.map +1 -1
  1405. package/dist/theme/data-viz-ramps.js.map +1 -1
  1406. package/dist/theme/initial-theme.cjs.map +1 -1
  1407. package/dist/theme/initial-theme.js.map +1 -1
  1408. package/dist/theme/inverse-render.cjs.map +1 -1
  1409. package/dist/theme/inverse-render.js.map +1 -1
  1410. package/dist/theme/inverse-restore.cjs.map +1 -1
  1411. package/dist/theme/inverse-restore.js.map +1 -1
  1412. package/dist/theme/inverse-surface.cjs.map +1 -1
  1413. package/dist/theme/inverse-surface.js.map +1 -1
  1414. package/dist/theme/status-tokens.cjs.map +1 -1
  1415. package/dist/theme/status-tokens.js.map +1 -1
  1416. package/dist/theme/theme-presets.cjs.map +1 -1
  1417. package/dist/theme/theme-presets.js.map +1 -1
  1418. package/dist/utils/absent.cjs.map +1 -1
  1419. package/dist/utils/absent.js.map +1 -1
  1420. package/dist/utils/arrays.cjs.map +1 -1
  1421. package/dist/utils/arrays.js.map +1 -1
  1422. package/dist/utils/base64.cjs.map +1 -1
  1423. package/dist/utils/base64.js.map +1 -1
  1424. package/dist/utils/cn.cjs.map +1 -1
  1425. package/dist/utils/cn.js.map +1 -1
  1426. package/dist/utils/compare-values.cjs.map +1 -1
  1427. package/dist/utils/compare-values.js.map +1 -1
  1428. package/dist/utils/compressed-storage.cjs.map +1 -1
  1429. package/dist/utils/compressed-storage.js.map +1 -1
  1430. package/dist/utils/csv.cjs.map +1 -1
  1431. package/dist/utils/csv.js.map +1 -1
  1432. package/dist/utils/dev-mode.cjs.map +1 -1
  1433. package/dist/utils/dev-mode.js.map +1 -1
  1434. package/dist/utils/duplicate-instance.cjs.map +1 -1
  1435. package/dist/utils/duplicate-instance.js.map +1 -1
  1436. package/dist/utils/format.cjs.map +1 -1
  1437. package/dist/utils/format.js.map +1 -1
  1438. package/dist/utils/functions.cjs +1 -1
  1439. package/dist/utils/functions.cjs.map +1 -1
  1440. package/dist/utils/functions.js +1 -1
  1441. package/dist/utils/functions.js.map +1 -1
  1442. package/dist/utils/guards.cjs.map +1 -1
  1443. package/dist/utils/guards.js.map +1 -1
  1444. package/dist/utils/ids.cjs.map +1 -1
  1445. package/dist/utils/ids.js.map +1 -1
  1446. package/dist/utils/intl-cache.cjs.map +1 -1
  1447. package/dist/utils/intl-cache.js.map +1 -1
  1448. package/dist/utils/json-frame.cjs.map +1 -1
  1449. package/dist/utils/json-frame.js.map +1 -1
  1450. package/dist/utils/numbers.cjs.map +1 -1
  1451. package/dist/utils/numbers.js.map +1 -1
  1452. package/dist/utils/objects.cjs.map +1 -1
  1453. package/dist/utils/objects.js.map +1 -1
  1454. package/dist/utils/owner-fingerprint.cjs.map +1 -1
  1455. package/dist/utils/owner-fingerprint.js.map +1 -1
  1456. package/dist/utils/promises.cjs.map +1 -1
  1457. package/dist/utils/promises.js.map +1 -1
  1458. package/dist/utils/relative-time.cjs.map +1 -1
  1459. package/dist/utils/relative-time.js.map +1 -1
  1460. package/dist/utils/safe-url.cjs.map +1 -1
  1461. package/dist/utils/safe-url.js.map +1 -1
  1462. package/dist/utils/schema-like.cjs.map +1 -1
  1463. package/dist/utils/schema-like.js.map +1 -1
  1464. package/dist/utils/storage.cjs +1 -1
  1465. package/dist/utils/storage.cjs.map +1 -1
  1466. package/dist/utils/storage.js +2 -2
  1467. package/dist/utils/storage.js.map +1 -1
  1468. package/dist/utils/strings.cjs.map +1 -1
  1469. package/dist/utils/strings.js.map +1 -1
  1470. package/dist/utils/xlsx.cjs.map +1 -1
  1471. package/dist/utils/xlsx.js.map +1 -1
  1472. package/dist/vision/core/canvas.cjs.map +1 -1
  1473. package/dist/vision/core/canvas.js.map +1 -1
  1474. package/dist/vision/core/dtypes.cjs.map +1 -1
  1475. package/dist/vision/core/dtypes.js.map +1 -1
  1476. package/dist/vision/core/exceptions.cjs.map +1 -1
  1477. package/dist/vision/core/exceptions.js.map +1 -1
  1478. package/dist/vision/core/graph.cjs.map +1 -1
  1479. package/dist/vision/core/graph.js.map +1 -1
  1480. package/dist/vision/core/metadata.cjs +1 -1
  1481. package/dist/vision/core/metadata.cjs.map +1 -1
  1482. package/dist/vision/core/metadata.js +9 -26
  1483. package/dist/vision/core/metadata.js.map +1 -1
  1484. package/dist/vision/core/providers.cjs.map +1 -1
  1485. package/dist/vision/core/providers.js.map +1 -1
  1486. package/dist/vision/core/session.cjs.map +1 -1
  1487. package/dist/vision/core/session.js.map +1 -1
  1488. package/dist/vision/core/timing.cjs.map +1 -1
  1489. package/dist/vision/core/timing.js.map +1 -1
  1490. package/dist/vision/fusion.cjs.map +1 -1
  1491. package/dist/vision/fusion.js.map +1 -1
  1492. package/dist/vision/index.cjs.map +1 -1
  1493. package/dist/vision/index.js.map +1 -1
  1494. package/dist/vision/io/image.cjs.map +1 -1
  1495. package/dist/vision/io/image.js.map +1 -1
  1496. package/dist/vision/labels.cjs.map +1 -1
  1497. package/dist/vision/labels.js.map +1 -1
  1498. package/dist/vision/luminance.cjs.map +1 -1
  1499. package/dist/vision/luminance.js.map +1 -1
  1500. package/dist/vision/normalization.cjs.map +1 -1
  1501. package/dist/vision/normalization.js.map +1 -1
  1502. package/dist/vision/postprocess/classification.cjs.map +1 -1
  1503. package/dist/vision/postprocess/classification.js.map +1 -1
  1504. package/dist/vision/postprocess/detection.cjs.map +1 -1
  1505. package/dist/vision/postprocess/detection.js.map +1 -1
  1506. package/dist/vision/postprocess/segmentation.cjs.map +1 -1
  1507. package/dist/vision/postprocess/segmentation.js.map +1 -1
  1508. package/dist/vision/preprocess/image.cjs.map +1 -1
  1509. package/dist/vision/preprocess/image.js.map +1 -1
  1510. package/dist/vision/preprocess/pipeline.cjs.map +1 -1
  1511. package/dist/vision/preprocess/pipeline.js.map +1 -1
  1512. package/dist/vision/results.cjs.map +1 -1
  1513. package/dist/vision/results.js.map +1 -1
  1514. package/dist/vision/tasks/base.cjs.map +1 -1
  1515. package/dist/vision/tasks/base.js.map +1 -1
  1516. package/dist/vision/tasks/classifier.cjs.map +1 -1
  1517. package/dist/vision/tasks/classifier.js.map +1 -1
  1518. package/dist/vision/tasks/detectClassify.cjs.map +1 -1
  1519. package/dist/vision/tasks/detectClassify.js.map +1 -1
  1520. package/dist/vision/tasks/detector.cjs.map +1 -1
  1521. package/dist/vision/tasks/detector.js.map +1 -1
  1522. package/dist/vision/tasks/segmenter.cjs.map +1 -1
  1523. package/dist/vision/tasks/segmenter.js.map +1 -1
  1524. package/dist/vision/types.cjs.map +1 -1
  1525. package/dist/vision/types.js.map +1 -1
  1526. package/dist/vision/use-camera-stream.cjs +1 -1
  1527. package/dist/vision/use-camera-stream.cjs.map +1 -1
  1528. package/dist/vision/use-camera-stream.js +3 -9
  1529. package/dist/vision/use-camera-stream.js.map +1 -1
  1530. package/dist/vision/use-live-luminance.cjs +1 -1
  1531. package/dist/vision/use-live-luminance.cjs.map +1 -1
  1532. package/dist/vision/use-live-luminance.js +1 -5
  1533. package/dist/vision/use-live-luminance.js.map +1 -1
  1534. package/dist/vite/base-url.cjs.map +1 -1
  1535. package/dist/vite/base-url.js.map +1 -1
  1536. package/dist/vite/create-vite-config.cjs.map +1 -1
  1537. package/dist/vite/create-vite-config.js.map +1 -1
  1538. package/dist/vite/narrow-tokens.cjs.map +1 -1
  1539. package/dist/vite/narrow-tokens.js.map +1 -1
  1540. package/dist/vite/tempest-csp.cjs +1 -1
  1541. package/dist/vite/tempest-csp.cjs.map +1 -1
  1542. package/dist/vite/tempest-csp.js +2 -10
  1543. package/dist/vite/tempest-csp.js.map +1 -1
  1544. package/dist/vite/tempest-icons.cjs.map +1 -1
  1545. package/dist/vite/tempest-icons.js.map +1 -1
  1546. package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
  1547. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  1548. package/dist/vite/tempest-pwa-dev-sw.js +9 -16
  1549. package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
  1550. package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
  1551. package/dist/vite/tempest-pwa-icons.js.map +1 -1
  1552. package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
  1553. package/dist/vite/tempest-pwa-manifest.js.map +1 -1
  1554. package/dist/vite/tempest-styles.cjs.map +1 -1
  1555. package/dist/vite/tempest-styles.js.map +1 -1
  1556. package/dist/vite/tempest-vitest.cjs.map +1 -1
  1557. package/dist/vite/tempest-vitest.js.map +1 -1
  1558. package/dist/webrtc/link-stats.cjs.map +1 -1
  1559. package/dist/webrtc/link-stats.js.map +1 -1
  1560. package/dist/webrtc/mesh-quality.cjs.map +1 -1
  1561. package/dist/webrtc/mesh-quality.js.map +1 -1
  1562. package/dist/webrtc/opus-sdp.cjs.map +1 -1
  1563. package/dist/webrtc/opus-sdp.js.map +1 -1
  1564. package/dist/webrtc/peer-link.cjs +1 -1
  1565. package/dist/webrtc/peer-link.cjs.map +1 -1
  1566. package/dist/webrtc/peer-link.js +2 -5
  1567. package/dist/webrtc/peer-link.js.map +1 -1
  1568. package/dist/webrtc/peer-mesh.cjs +1 -1
  1569. package/dist/webrtc/peer-mesh.cjs.map +1 -1
  1570. package/dist/webrtc/peer-mesh.js +8 -24
  1571. package/dist/webrtc/peer-mesh.js.map +1 -1
  1572. package/dist/webrtc/sender-bitrate.cjs.map +1 -1
  1573. package/dist/webrtc/sender-bitrate.js.map +1 -1
  1574. package/dist/webrtc/use-link-stats.cjs.map +1 -1
  1575. package/dist/webrtc/use-link-stats.js.map +1 -1
  1576. package/dist/ws/create-web-socket.cjs +1 -1
  1577. package/dist/ws/create-web-socket.cjs.map +1 -1
  1578. package/dist/ws/create-web-socket.js +4 -22
  1579. package/dist/ws/create-web-socket.js.map +1 -1
  1580. package/dist/ws/resilience.cjs.map +1 -1
  1581. package/dist/ws/resilience.js.map +1 -1
  1582. package/dist/ws/use-web-socket.cjs.map +1 -1
  1583. package/dist/ws/use-web-socket.js.map +1 -1
  1584. package/package.json +9 -6
  1585. package/template/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"encode.cjs","names":[],"sources":["../../src/imaging/encode.ts"],"sourcesContent":["/**\n * Getting bytes back out, and knowing which bytes you got.\n *\n * The trap here is silent: `canvas.toBlob(cb, \"image/avif\")` does not throw\n * on a browser without an AVIF encoder. It hands back a **PNG** with the\n * requested type ignored, and an app that trusted the request uploads\n * several megabytes where it planned for a few hundred kilobytes. Every\n * function here reports the type actually produced, and\n * :func:`supportsImageType` answers the question up front.\n */\n\nimport { getContext, type Surface } from \"./canvas\";\nimport { ImageEncodeError } from \"./exceptions\";\nimport type { EncodeOptions, ImageType, ProcessedImage } from \"./types\";\n\n/** Quality used when the caller does not choose. */\nexport const DEFAULT_QUALITY = 0.85;\n\n/** Format used when the caller does not choose. */\nexport const DEFAULT_TYPE: ImageType = \"image/jpeg\";\n\nconst supportCache = new Map<string, boolean>();\n\n/**\n * Encode a surface into image bytes.\n *\n * @param surface The canvas to encode.\n * @param options Format and quality.\n * @returns The blob plus the dimensions and the type actually produced.\n * @throws {@link ImageEncodeError} when the canvas produces nothing.\n */\nexport async function encodeImage(\n surface: Surface,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const type = options.type ?? DEFAULT_TYPE;\n const quality = options.quality ?? DEFAULT_QUALITY;\n\n const blob = await surfaceToBlob(surface, type, quality);\n if (blob === null) {\n throw new ImageEncodeError(\n `The canvas produced no bytes for ${type}. The image may be too large ` +\n \"for this browser's canvas limits.\",\n );\n }\n\n return {\n blob,\n width: surface.width,\n height: surface.height,\n type: blob.type || type,\n bytes: blob.size,\n };\n}\n\n/**\n * Encode a surface, whichever kind it is.\n *\n * `OffscreenCanvas` and `HTMLCanvasElement` disagree on how to hand back\n * bytes — one returns a promise, the other takes a callback.\n *\n * @param surface The canvas.\n * @param type MIME type to request.\n * @param quality Quality for lossy formats.\n * @returns The blob, or `null` when the canvas produced nothing.\n */\nasync function surfaceToBlob(\n surface: Surface,\n type: string,\n quality: number,\n): Promise<Blob | null> {\n if (\"convertToBlob\" in surface) {\n return await surface.convertToBlob({ type, quality });\n }\n return await new Promise<Blob | null>((resolve) => {\n surface.toBlob((blob) => resolve(blob), type, quality);\n });\n}\n\n/**\n * Whether this browser can actually encode a format.\n *\n * Asks for a 1x1 image in that type and checks what came back, because\n * that is the only answer that counts: a browser that \"supports\" WebP for\n * display may still not encode it.\n *\n * @example\n * ```ts\n * const type = (await supportsImageType(\"image/webp\")) ? \"image/webp\" : \"image/jpeg\";\n * const resized = await resizeImage(file, { width: 1200, type });\n * ```\n *\n * @param type The format to test.\n * @returns Whether encoding produces that type. Cached per type.\n */\nexport async function supportsImageType(type: ImageType): Promise<boolean> {\n const cached = supportCache.get(type);\n if (cached !== undefined) return cached;\n\n let supported: boolean;\n try {\n const { createSurface } = await import(\"./canvas\");\n const surface = createSurface(1, 1);\n getContext(surface);\n const blob = await surfaceToBlob(surface, type, 0.5);\n supported = blob !== null && blob.type === type;\n } catch {\n supported = false;\n }\n\n supportCache.set(type, supported);\n return supported;\n}\n\n/**\n * Pick the smallest format this browser can actually produce.\n *\n * @example\n * ```ts\n * const type = await bestSupportedType([\"image/avif\", \"image/webp\", \"image/jpeg\"]);\n * ```\n *\n * @param preferences Formats in preference order.\n * @returns The first supported one, falling back to `image/jpeg`, which\n * every canvas implementation encodes.\n */\nexport async function bestSupportedType(\n preferences: readonly ImageType[] = [\"image/webp\", \"image/jpeg\"],\n): Promise<ImageType> {\n for (const type of preferences) {\n if (await supportsImageType(type)) return type;\n }\n return \"image/jpeg\";\n}\n\n/**\n * Clear the format-support cache.\n *\n * Only useful in tests: browser support does not change at runtime.\n */\nexport function resetImageTypeSupportCache(): void {\n supportCache.clear();\n}\n"],"mappings":"8DAgBA,IAAa,EAAkB,IAGlB,EAA0B,aAEjC,EAAe,IAAI,IAUzB,eAAsB,EAClB,EACA,EAAyB,CAAC,EACH,CACvB,IAAM,EAAO,EAAQ,MAAA,aAGf,EAAO,MAAM,EAAc,EAAS,EAF1B,EAAQ,SAAA,GAE+B,EACvD,GAAI,IAAS,KACT,MAAM,IAAI,EAAA,iBACN,oCAAoC,EAAK,+DAE7C,EAGJ,MAAO,CACH,OACA,MAAO,EAAQ,MACf,OAAQ,EAAQ,OAChB,KAAM,EAAK,MAAQ,EACnB,MAAO,EAAK,IAChB,CACJ,CAaA,eAAe,EACX,EACA,EACA,EACoB,CAIpB,MAHI,kBAAmB,EACZ,MAAM,EAAQ,cAAc,CAAE,OAAM,SAAQ,CAAC,EAEjD,MAAM,IAAI,QAAsB,GAAY,CAC/C,EAAQ,OAAQ,GAAS,EAAQ,CAAI,EAAG,EAAM,CAAO,CACzD,CAAC,CACL,CAkBA,eAAsB,EAAkB,EAAmC,CACvE,IAAM,EAAS,EAAa,IAAI,CAAI,EACpC,GAAI,IAAW,IAAA,GAAW,OAAO,EAEjC,IAAI,EACJ,GAAI,CACA,GAAM,CAAE,iBAAkB,MAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAM,cAAA,CAAA,EAC1B,EAAU,EAAc,EAAG,CAAC,EAClC,EAAA,WAAW,CAAO,EAClB,IAAM,EAAO,MAAM,EAAc,EAAS,EAAM,EAAG,EACnD,EAAY,IAAS,MAAQ,EAAK,OAAS,CAC/C,MAAQ,CACJ,EAAY,EAChB,CAGA,OADA,EAAa,IAAI,EAAM,CAAS,EACzB,CACX,CAcA,eAAsB,EAClB,EAAoC,CAAC,aAAc,YAAY,EAC7C,CAClB,IAAK,IAAM,KAAQ,EACf,GAAI,MAAM,EAAkB,CAAI,EAAG,OAAO,EAE9C,MAAO,YACX"}
1
+ {"version":3,"file":"encode.cjs","names":["ImageEncodeError","require_exceptions","require_canvas","DEFAULT_QUALITY","DEFAULT_TYPE","supportCache","encodeImage","type","options","blob","surfaceToBlob","surface","resolve","quality","supportsImageType","cached","supported","createSurface","bestSupportedType","preferences"],"sources":["../../src/imaging/encode.ts"],"sourcesContent":["/**\n * Getting bytes back out, and knowing which bytes you got.\n *\n * The trap here is silent: `canvas.toBlob(cb, \"image/avif\")` does not throw\n * on a browser without an AVIF encoder. It hands back a **PNG** with the\n * requested type ignored, and an app that trusted the request uploads\n * several megabytes where it planned for a few hundred kilobytes. Every\n * function here reports the type actually produced, and\n * :func:`supportsImageType` answers the question up front.\n */\n\nimport { getContext, type Surface } from \"./canvas\";\nimport { ImageEncodeError } from \"./exceptions\";\nimport type { EncodeOptions, ImageType, ProcessedImage } from \"./types\";\n\n/** Quality used when the caller does not choose. */\nexport const DEFAULT_QUALITY = 0.85;\n\n/** Format used when the caller does not choose. */\nexport const DEFAULT_TYPE: ImageType = \"image/jpeg\";\n\nconst supportCache = new Map<string, boolean>();\n\n/**\n * Encode a surface into image bytes.\n *\n * @param surface The canvas to encode.\n * @param options Format and quality.\n * @returns The blob plus the dimensions and the type actually produced.\n * @throws {@link ImageEncodeError} when the canvas produces nothing.\n */\nexport async function encodeImage(\n surface: Surface,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const type = options.type ?? DEFAULT_TYPE;\n const quality = options.quality ?? DEFAULT_QUALITY;\n\n const blob = await surfaceToBlob(surface, type, quality);\n if (blob === null) {\n throw new ImageEncodeError(\n `The canvas produced no bytes for ${type}. The image may be too large ` +\n \"for this browser's canvas limits.\",\n );\n }\n\n return {\n blob,\n width: surface.width,\n height: surface.height,\n type: blob.type || type,\n bytes: blob.size,\n };\n}\n\n/**\n * Encode a surface, whichever kind it is.\n *\n * `OffscreenCanvas` and `HTMLCanvasElement` disagree on how to hand back\n * bytes — one returns a promise, the other takes a callback.\n *\n * @param surface The canvas.\n * @param type MIME type to request.\n * @param quality Quality for lossy formats.\n * @returns The blob, or `null` when the canvas produced nothing.\n */\nasync function surfaceToBlob(\n surface: Surface,\n type: string,\n quality: number,\n): Promise<Blob | null> {\n if (\"convertToBlob\" in surface) {\n return await surface.convertToBlob({ type, quality });\n }\n return await new Promise<Blob | null>((resolve) => {\n surface.toBlob((blob) => resolve(blob), type, quality);\n });\n}\n\n/**\n * Whether this browser can actually encode a format.\n *\n * Asks for a 1x1 image in that type and checks what came back, because\n * that is the only answer that counts: a browser that \"supports\" WebP for\n * display may still not encode it.\n *\n * @example\n * ```ts\n * const type = (await supportsImageType(\"image/webp\")) ? \"image/webp\" : \"image/jpeg\";\n * const resized = await resizeImage(file, { width: 1200, type });\n * ```\n *\n * @param type The format to test.\n * @returns Whether encoding produces that type. Cached per type.\n */\nexport async function supportsImageType(type: ImageType): Promise<boolean> {\n const cached = supportCache.get(type);\n if (cached !== undefined) return cached;\n\n let supported: boolean;\n try {\n const { createSurface } = await import(\"./canvas\");\n const surface = createSurface(1, 1);\n getContext(surface);\n const blob = await surfaceToBlob(surface, type, 0.5);\n supported = blob !== null && blob.type === type;\n } catch {\n supported = false;\n }\n\n supportCache.set(type, supported);\n return supported;\n}\n\n/**\n * Pick the smallest format this browser can actually produce.\n *\n * @example\n * ```ts\n * const type = await bestSupportedType([\"image/avif\", \"image/webp\", \"image/jpeg\"]);\n * ```\n *\n * @param preferences Formats in preference order.\n * @returns The first supported one, falling back to `image/jpeg`, which\n * every canvas implementation encodes.\n */\nexport async function bestSupportedType(\n preferences: readonly ImageType[] = [\"image/webp\", \"image/jpeg\"],\n): Promise<ImageType> {\n for (const type of preferences) {\n if (await supportsImageType(type)) return type;\n }\n return \"image/jpeg\";\n}\n\n/**\n * Clear the format-support cache.\n *\n * Only useful in tests: browser support does not change at runtime.\n */\nexport function resetImageTypeSupportCache(): void {\n supportCache.clear();\n}\n"],"mappings":"A,8DAgBA,IAAaG,EAAkB,IAGlBC,EAA0B,aAEjCC,EAAe,IAAI,IAUzB,eAAsBC,EAClB,EACA,EAAyB,CAAC,EACH,CACvB,IAAMC,EAAOC,EAAQ,MAAA,aAGfC,EAAO,MAAMC,EAAcC,EAASJ,EAF1BC,EAAQ,SAAA,GAE+B,EACvD,GAAIC,IAAS,KACT,MAAM,IAAIT,EAAAA,iBACN,oCAAoCO,EAAK,+DAE7C,EAGJ,MAAO,CACH,OACA,MAAOI,EAAQ,MACf,OAAQA,EAAQ,OAChB,KAAMF,EAAK,MAAQF,EACnB,MAAOE,EAAK,IAChB,CACJ,CAaA,eAAeC,EACX,EACA,EACA,EACoB,CAIpB,MAHI,kBAAmBC,EACZ,MAAMA,EAAQ,cAAc,CAAE,OAAM,SAAQ,CAAC,EAEjD,MAAM,IAAI,QAAsB,GAAY,CAC/CA,EAAQ,OAAQ,GAASC,EAAQH,CAAI,EAAGF,EAAMM,CAAO,CACzD,CAAC,CACL,CAkBA,eAAsBC,EAAkB,EAAmC,CACvE,IAAMC,EAASV,EAAa,IAAIE,CAAI,EACpC,GAAIQ,IAAW,IAAA,GAAW,OAAOA,EAEjC,IAAIC,EACJ,GAAI,CACA,GAAM,CAAE,iBAAkB,MAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAM,cAAA,CAAA,EAC1BL,EAAUM,EAAc,EAAG,CAAC,EAClCf,EAAA,WAAWS,CAAO,EAClB,IAAMF,EAAO,MAAMC,EAAcC,EAASJ,EAAM,EAAG,EACnD,EAAYE,IAAS,MAAQA,EAAK,OAASF,CAC/C,MAAQ,CACJ,EAAY,EAChB,CAGA,OADAF,EAAa,IAAIE,EAAMS,CAAS,EACzBA,CACX,CAcA,eAAsBE,EAClB,EAAoC,CAAC,aAAc,YAAY,EAC7C,CAClB,IAAK,IAAMX,KAAQY,EACf,GAAI,MAAML,EAAkBP,CAAI,EAAG,OAAOA,EAE9C,MAAO,YACX,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"encode.js","names":[],"sources":["../../src/imaging/encode.ts"],"sourcesContent":["/**\n * Getting bytes back out, and knowing which bytes you got.\n *\n * The trap here is silent: `canvas.toBlob(cb, \"image/avif\")` does not throw\n * on a browser without an AVIF encoder. It hands back a **PNG** with the\n * requested type ignored, and an app that trusted the request uploads\n * several megabytes where it planned for a few hundred kilobytes. Every\n * function here reports the type actually produced, and\n * :func:`supportsImageType` answers the question up front.\n */\n\nimport { getContext, type Surface } from \"./canvas\";\nimport { ImageEncodeError } from \"./exceptions\";\nimport type { EncodeOptions, ImageType, ProcessedImage } from \"./types\";\n\n/** Quality used when the caller does not choose. */\nexport const DEFAULT_QUALITY = 0.85;\n\n/** Format used when the caller does not choose. */\nexport const DEFAULT_TYPE: ImageType = \"image/jpeg\";\n\nconst supportCache = new Map<string, boolean>();\n\n/**\n * Encode a surface into image bytes.\n *\n * @param surface The canvas to encode.\n * @param options Format and quality.\n * @returns The blob plus the dimensions and the type actually produced.\n * @throws {@link ImageEncodeError} when the canvas produces nothing.\n */\nexport async function encodeImage(\n surface: Surface,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const type = options.type ?? DEFAULT_TYPE;\n const quality = options.quality ?? DEFAULT_QUALITY;\n\n const blob = await surfaceToBlob(surface, type, quality);\n if (blob === null) {\n throw new ImageEncodeError(\n `The canvas produced no bytes for ${type}. The image may be too large ` +\n \"for this browser's canvas limits.\",\n );\n }\n\n return {\n blob,\n width: surface.width,\n height: surface.height,\n type: blob.type || type,\n bytes: blob.size,\n };\n}\n\n/**\n * Encode a surface, whichever kind it is.\n *\n * `OffscreenCanvas` and `HTMLCanvasElement` disagree on how to hand back\n * bytes — one returns a promise, the other takes a callback.\n *\n * @param surface The canvas.\n * @param type MIME type to request.\n * @param quality Quality for lossy formats.\n * @returns The blob, or `null` when the canvas produced nothing.\n */\nasync function surfaceToBlob(\n surface: Surface,\n type: string,\n quality: number,\n): Promise<Blob | null> {\n if (\"convertToBlob\" in surface) {\n return await surface.convertToBlob({ type, quality });\n }\n return await new Promise<Blob | null>((resolve) => {\n surface.toBlob((blob) => resolve(blob), type, quality);\n });\n}\n\n/**\n * Whether this browser can actually encode a format.\n *\n * Asks for a 1x1 image in that type and checks what came back, because\n * that is the only answer that counts: a browser that \"supports\" WebP for\n * display may still not encode it.\n *\n * @example\n * ```ts\n * const type = (await supportsImageType(\"image/webp\")) ? \"image/webp\" : \"image/jpeg\";\n * const resized = await resizeImage(file, { width: 1200, type });\n * ```\n *\n * @param type The format to test.\n * @returns Whether encoding produces that type. Cached per type.\n */\nexport async function supportsImageType(type: ImageType): Promise<boolean> {\n const cached = supportCache.get(type);\n if (cached !== undefined) return cached;\n\n let supported: boolean;\n try {\n const { createSurface } = await import(\"./canvas\");\n const surface = createSurface(1, 1);\n getContext(surface);\n const blob = await surfaceToBlob(surface, type, 0.5);\n supported = blob !== null && blob.type === type;\n } catch {\n supported = false;\n }\n\n supportCache.set(type, supported);\n return supported;\n}\n\n/**\n * Pick the smallest format this browser can actually produce.\n *\n * @example\n * ```ts\n * const type = await bestSupportedType([\"image/avif\", \"image/webp\", \"image/jpeg\"]);\n * ```\n *\n * @param preferences Formats in preference order.\n * @returns The first supported one, falling back to `image/jpeg`, which\n * every canvas implementation encodes.\n */\nexport async function bestSupportedType(\n preferences: readonly ImageType[] = [\"image/webp\", \"image/jpeg\"],\n): Promise<ImageType> {\n for (const type of preferences) {\n if (await supportsImageType(type)) return type;\n }\n return \"image/jpeg\";\n}\n\n/**\n * Clear the format-support cache.\n *\n * Only useful in tests: browser support does not change at runtime.\n */\nexport function resetImageTypeSupportCache(): void {\n supportCache.clear();\n}\n"],"mappings":";;;AAgBA,IAAa,IAAkB,KAGlB,IAA0B,cAEjC,oBAAe,IAAI,IAAqB;AAU9C,eAAsB,EAClB,GACA,IAAyB,CAAC,GACH;CACvB,IAAM,IAAO,EAAQ,QAAA,cAGf,IAAO,MAAM,EAAc,GAAS,GAF1B,EAAQ,WAAA,GAE+B;CACvD,IAAI,MAAS,MACT,MAAM,IAAI,EACN,oCAAoC,EAAK,+DAE7C;CAGJ,OAAO;EACH;EACA,OAAO,EAAQ;EACf,QAAQ,EAAQ;EAChB,MAAM,EAAK,QAAQ;EACnB,OAAO,EAAK;CAChB;AACJ;AAaA,eAAe,EACX,GACA,GACA,GACoB;CAIpB,OAHI,mBAAmB,IACZ,MAAM,EAAQ,cAAc;EAAE;EAAM;CAAQ,CAAC,IAEjD,MAAM,IAAI,SAAsB,MAAY;EAC/C,EAAQ,QAAQ,MAAS,EAAQ,CAAI,GAAG,GAAM,CAAO;CACzD,CAAC;AACL;AAkBA,eAAsB,EAAkB,GAAmC;CACvE,IAAM,IAAS,EAAa,IAAI,CAAI;CACpC,IAAI,MAAW,KAAA,GAAW,OAAO;CAEjC,IAAI;CACJ,IAAI;EACA,IAAM,EAAE,qBAAkB,MAAM,OAAO,gBACjC,IAAU,EAAc,GAAG,CAAC;EAClC,EAAW,CAAO;EAClB,IAAM,IAAO,MAAM,EAAc,GAAS,GAAM,EAAG;EACnD,IAAY,MAAS,QAAQ,EAAK,SAAS;CAC/C,QAAQ;EACJ,IAAY;CAChB;CAGA,OADA,EAAa,IAAI,GAAM,CAAS,GACzB;AACX;AAcA,eAAsB,EAClB,IAAoC,CAAC,cAAc,YAAY,GAC7C;CAClB,KAAK,IAAM,KAAQ,GACf,IAAI,MAAM,EAAkB,CAAI,GAAG,OAAO;CAE9C,OAAO;AACX"}
1
+ {"version":3,"file":"encode.js","names":["DEFAULT_QUALITY","DEFAULT_TYPE","supportCache","encodeImage","type","options","blob","surfaceToBlob","surface","ImageEncodeError","resolve","quality","supportsImageType","cached","supported","createSurface","bestSupportedType","preferences"],"sources":["../../src/imaging/encode.ts"],"sourcesContent":["/**\n * Getting bytes back out, and knowing which bytes you got.\n *\n * The trap here is silent: `canvas.toBlob(cb, \"image/avif\")` does not throw\n * on a browser without an AVIF encoder. It hands back a **PNG** with the\n * requested type ignored, and an app that trusted the request uploads\n * several megabytes where it planned for a few hundred kilobytes. Every\n * function here reports the type actually produced, and\n * :func:`supportsImageType` answers the question up front.\n */\n\nimport { getContext, type Surface } from \"./canvas\";\nimport { ImageEncodeError } from \"./exceptions\";\nimport type { EncodeOptions, ImageType, ProcessedImage } from \"./types\";\n\n/** Quality used when the caller does not choose. */\nexport const DEFAULT_QUALITY = 0.85;\n\n/** Format used when the caller does not choose. */\nexport const DEFAULT_TYPE: ImageType = \"image/jpeg\";\n\nconst supportCache = new Map<string, boolean>();\n\n/**\n * Encode a surface into image bytes.\n *\n * @param surface The canvas to encode.\n * @param options Format and quality.\n * @returns The blob plus the dimensions and the type actually produced.\n * @throws {@link ImageEncodeError} when the canvas produces nothing.\n */\nexport async function encodeImage(\n surface: Surface,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const type = options.type ?? DEFAULT_TYPE;\n const quality = options.quality ?? DEFAULT_QUALITY;\n\n const blob = await surfaceToBlob(surface, type, quality);\n if (blob === null) {\n throw new ImageEncodeError(\n `The canvas produced no bytes for ${type}. The image may be too large ` +\n \"for this browser's canvas limits.\",\n );\n }\n\n return {\n blob,\n width: surface.width,\n height: surface.height,\n type: blob.type || type,\n bytes: blob.size,\n };\n}\n\n/**\n * Encode a surface, whichever kind it is.\n *\n * `OffscreenCanvas` and `HTMLCanvasElement` disagree on how to hand back\n * bytes — one returns a promise, the other takes a callback.\n *\n * @param surface The canvas.\n * @param type MIME type to request.\n * @param quality Quality for lossy formats.\n * @returns The blob, or `null` when the canvas produced nothing.\n */\nasync function surfaceToBlob(\n surface: Surface,\n type: string,\n quality: number,\n): Promise<Blob | null> {\n if (\"convertToBlob\" in surface) {\n return await surface.convertToBlob({ type, quality });\n }\n return await new Promise<Blob | null>((resolve) => {\n surface.toBlob((blob) => resolve(blob), type, quality);\n });\n}\n\n/**\n * Whether this browser can actually encode a format.\n *\n * Asks for a 1x1 image in that type and checks what came back, because\n * that is the only answer that counts: a browser that \"supports\" WebP for\n * display may still not encode it.\n *\n * @example\n * ```ts\n * const type = (await supportsImageType(\"image/webp\")) ? \"image/webp\" : \"image/jpeg\";\n * const resized = await resizeImage(file, { width: 1200, type });\n * ```\n *\n * @param type The format to test.\n * @returns Whether encoding produces that type. Cached per type.\n */\nexport async function supportsImageType(type: ImageType): Promise<boolean> {\n const cached = supportCache.get(type);\n if (cached !== undefined) return cached;\n\n let supported: boolean;\n try {\n const { createSurface } = await import(\"./canvas\");\n const surface = createSurface(1, 1);\n getContext(surface);\n const blob = await surfaceToBlob(surface, type, 0.5);\n supported = blob !== null && blob.type === type;\n } catch {\n supported = false;\n }\n\n supportCache.set(type, supported);\n return supported;\n}\n\n/**\n * Pick the smallest format this browser can actually produce.\n *\n * @example\n * ```ts\n * const type = await bestSupportedType([\"image/avif\", \"image/webp\", \"image/jpeg\"]);\n * ```\n *\n * @param preferences Formats in preference order.\n * @returns The first supported one, falling back to `image/jpeg`, which\n * every canvas implementation encodes.\n */\nexport async function bestSupportedType(\n preferences: readonly ImageType[] = [\"image/webp\", \"image/jpeg\"],\n): Promise<ImageType> {\n for (const type of preferences) {\n if (await supportsImageType(type)) return type;\n }\n return \"image/jpeg\";\n}\n\n/**\n * Clear the format-support cache.\n *\n * Only useful in tests: browser support does not change at runtime.\n */\nexport function resetImageTypeSupportCache(): void {\n supportCache.clear();\n}\n"],"mappings":";;;AAgBA,IAAaA,IAAkB,KAGlBC,IAA0B,cAEjCC,oBAAe,IAAI,IAAqB;AAU9C,eAAsBC,EAClB,GACA,IAAyB,CAAC,GACH;CACvB,IAAMC,IAAOC,EAAQ,QAAA,cAGfC,IAAO,MAAMC,EAAcC,GAASJ,GAF1BC,EAAQ,WAAA,GAE+B;CACvD,IAAIC,MAAS,MACT,MAAM,IAAIG,EACN,oCAAoCL,EAAK,+DAE7C;CAGJ,OAAO;EACH;EACA,OAAOI,EAAQ;EACf,QAAQA,EAAQ;EAChB,MAAMF,EAAK,QAAQF;EACnB,OAAOE,EAAK;CAChB;AACJ;AAaA,eAAeC,EACX,GACA,GACA,GACoB;CAIpB,OAHI,mBAAmBC,IACZ,MAAMA,EAAQ,cAAc;EAAE;EAAM;CAAQ,CAAC,IAEjD,MAAM,IAAI,SAAsB,MAAY;EAC/C,EAAQ,QAAQ,MAASE,EAAQJ,CAAI,GAAGF,GAAMO,CAAO;CACzD,CAAC;AACL;AAkBA,eAAsBC,EAAkB,GAAmC;CACvE,IAAMC,IAASX,EAAa,IAAIE,CAAI;CACpC,IAAIS,MAAW,KAAA,GAAW,OAAOA;CAEjC,IAAIC;CACJ,IAAI;EACA,IAAM,EAAE,qBAAkB,MAAM,OAAO,gBACjCN,IAAUO,EAAc,GAAG,CAAC;EAClC,EAAWP,CAAO;EAClB,IAAMF,IAAO,MAAMC,EAAcC,GAASJ,GAAM,EAAG;EACnD,IAAYE,MAAS,QAAQA,EAAK,SAASF;CAC/C,QAAQ;EACJ,IAAY;CAChB;CAGA,OADAF,EAAa,IAAIE,GAAMU,CAAS,GACzBA;AACX;AAcA,eAAsBE,EAClB,IAAoC,CAAC,cAAc,YAAY,GAC7C;CAClB,KAAK,IAAMZ,KAAQa,GACf,IAAI,MAAML,EAAkBR,CAAI,GAAG,OAAOA;CAE9C,OAAO;AACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"exceptions.cjs","names":[],"sources":["../../src/imaging/exceptions.ts"],"sourcesContent":["/**\n * Errors thrown by the imaging module.\n *\n * `name` is a literal string on every subclass: minifiers rename classes,\n * and a derived `new.target.name` ships as `error.name === \"t\"`.\n */\n\n/** Base class for every error this module throws. */\nexport class ImagingError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingError\";\n }\n}\n\n/** The source could not be decoded into pixels. */\nexport class ImageDecodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageDecodeError\";\n }\n}\n\n/** The canvas could not produce encoded bytes. */\nexport class ImageEncodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageEncodeError\";\n }\n}\n\n/**\n * The browser cannot encode the requested format.\n *\n * Worth its own class because the failure is otherwise silent: asking a\n * canvas for an unsupported type does not throw — it hands back a PNG,\n * and an app that trusted the request ships 4 MB where it expected 300 KB.\n */\nexport class UnsupportedImageTypeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"UnsupportedImageTypeError\";\n }\n}\n\n/**\n * The frame asked for never arrived.\n *\n * Its own class because the alternative is the failure this module refuses to\n * produce: a frame from the wrong instant. `seeked` firing does not mean the\n * frame for the new position is composited and readable, so a capture that\n * gave up waiting has to say so — a thumbnail of the neighbouring frame looks\n * exactly like a correct one, and nothing downstream can tell.\n */\nexport class FrameSeekError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FrameSeekError\";\n }\n}\n\n/** The environment has no canvas to draw on. */\nexport class ImagingUnavailableError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingUnavailableError\";\n }\n}\n"],"mappings":"AAQA,IAAa,EAAb,cAAkC,KAAM,CACpC,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,cAChB,CACJ,EAGa,EAAb,cAAsC,CAAa,CAC/C,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,kBAChB,CACJ,EAGa,EAAb,cAAsC,CAAa,CAC/C,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,kBAChB,CACJ,EASa,EAAb,cAA+C,CAAa,CACxD,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,2BAChB,CACJ,EAWa,EAAb,cAAoC,CAAa,CAC7C,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,gBAChB,CACJ,EAGa,EAAb,cAA6C,CAAa,CACtD,YAAY,EAAiB,EAAwB,CACjD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,yBAChB,CACJ"}
1
+ {"version":3,"file":"exceptions.cjs","names":["ImagingError","message","options","ImageDecodeError","ImageEncodeError","UnsupportedImageTypeError","FrameSeekError","ImagingUnavailableError"],"sources":["../../src/imaging/exceptions.ts"],"sourcesContent":["/**\n * Errors thrown by the imaging module.\n *\n * `name` is a literal string on every subclass: minifiers rename classes,\n * and a derived `new.target.name` ships as `error.name === \"t\"`.\n */\n\n/** Base class for every error this module throws. */\nexport class ImagingError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingError\";\n }\n}\n\n/** The source could not be decoded into pixels. */\nexport class ImageDecodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageDecodeError\";\n }\n}\n\n/** The canvas could not produce encoded bytes. */\nexport class ImageEncodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageEncodeError\";\n }\n}\n\n/**\n * The browser cannot encode the requested format.\n *\n * Worth its own class because the failure is otherwise silent: asking a\n * canvas for an unsupported type does not throw — it hands back a PNG,\n * and an app that trusted the request ships 4 MB where it expected 300 KB.\n */\nexport class UnsupportedImageTypeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"UnsupportedImageTypeError\";\n }\n}\n\n/**\n * The frame asked for never arrived.\n *\n * Its own class because the alternative is the failure this module refuses to\n * produce: a frame from the wrong instant. `seeked` firing does not mean the\n * frame for the new position is composited and readable, so a capture that\n * gave up waiting has to say so — a thumbnail of the neighbouring frame looks\n * exactly like a correct one, and nothing downstream can tell.\n */\nexport class FrameSeekError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FrameSeekError\";\n }\n}\n\n/** The environment has no canvas to draw on. */\nexport class ImagingUnavailableError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingUnavailableError\";\n }\n}\n"],"mappings":"AAQA,IAAaA,EAAb,cAAkC,KAAM,CACpC,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,cAChB,CACJ,EAGaC,EAAb,cAAsCH,CAAa,CAC/C,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,kBAChB,CACJ,EAGaE,EAAb,cAAsCJ,CAAa,CAC/C,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,kBAChB,CACJ,EASaG,EAAb,cAA+CL,CAAa,CACxD,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,2BAChB,CACJ,EAWaI,EAAb,cAAoCN,CAAa,CAC7C,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,gBAChB,CACJ,EAGaK,EAAb,cAA6CP,CAAa,CACtD,YAAY,EAAiB,EAAwB,CACjD,MAAMC,EAASC,CAAO,EACtB,KAAK,KAAO,yBAChB,CACJ,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"exceptions.js","names":[],"sources":["../../src/imaging/exceptions.ts"],"sourcesContent":["/**\n * Errors thrown by the imaging module.\n *\n * `name` is a literal string on every subclass: minifiers rename classes,\n * and a derived `new.target.name` ships as `error.name === \"t\"`.\n */\n\n/** Base class for every error this module throws. */\nexport class ImagingError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingError\";\n }\n}\n\n/** The source could not be decoded into pixels. */\nexport class ImageDecodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageDecodeError\";\n }\n}\n\n/** The canvas could not produce encoded bytes. */\nexport class ImageEncodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageEncodeError\";\n }\n}\n\n/**\n * The browser cannot encode the requested format.\n *\n * Worth its own class because the failure is otherwise silent: asking a\n * canvas for an unsupported type does not throw — it hands back a PNG,\n * and an app that trusted the request ships 4 MB where it expected 300 KB.\n */\nexport class UnsupportedImageTypeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"UnsupportedImageTypeError\";\n }\n}\n\n/**\n * The frame asked for never arrived.\n *\n * Its own class because the alternative is the failure this module refuses to\n * produce: a frame from the wrong instant. `seeked` firing does not mean the\n * frame for the new position is composited and readable, so a capture that\n * gave up waiting has to say so — a thumbnail of the neighbouring frame looks\n * exactly like a correct one, and nothing downstream can tell.\n */\nexport class FrameSeekError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FrameSeekError\";\n }\n}\n\n/** The environment has no canvas to draw on. */\nexport class ImagingUnavailableError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingUnavailableError\";\n }\n}\n"],"mappings":";AAQA,IAAa,IAAb,cAAkC,MAAM;CACpC,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGa,IAAb,cAAsC,EAAa;CAC/C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGa,IAAb,cAAsC,EAAa;CAC/C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GASa,IAAb,cAA+C,EAAa;CACxD,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAWa,IAAb,cAAoC,EAAa;CAC7C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGa,IAAb,cAA6C,EAAa;CACtD,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ"}
1
+ {"version":3,"file":"exceptions.js","names":["ImagingError","message","options","ImageDecodeError","ImageEncodeError","UnsupportedImageTypeError","FrameSeekError","ImagingUnavailableError"],"sources":["../../src/imaging/exceptions.ts"],"sourcesContent":["/**\n * Errors thrown by the imaging module.\n *\n * `name` is a literal string on every subclass: minifiers rename classes,\n * and a derived `new.target.name` ships as `error.name === \"t\"`.\n */\n\n/** Base class for every error this module throws. */\nexport class ImagingError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingError\";\n }\n}\n\n/** The source could not be decoded into pixels. */\nexport class ImageDecodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageDecodeError\";\n }\n}\n\n/** The canvas could not produce encoded bytes. */\nexport class ImageEncodeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImageEncodeError\";\n }\n}\n\n/**\n * The browser cannot encode the requested format.\n *\n * Worth its own class because the failure is otherwise silent: asking a\n * canvas for an unsupported type does not throw — it hands back a PNG,\n * and an app that trusted the request ships 4 MB where it expected 300 KB.\n */\nexport class UnsupportedImageTypeError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"UnsupportedImageTypeError\";\n }\n}\n\n/**\n * The frame asked for never arrived.\n *\n * Its own class because the alternative is the failure this module refuses to\n * produce: a frame from the wrong instant. `seeked` firing does not mean the\n * frame for the new position is composited and readable, so a capture that\n * gave up waiting has to say so — a thumbnail of the neighbouring frame looks\n * exactly like a correct one, and nothing downstream can tell.\n */\nexport class FrameSeekError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FrameSeekError\";\n }\n}\n\n/** The environment has no canvas to draw on. */\nexport class ImagingUnavailableError extends ImagingError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ImagingUnavailableError\";\n }\n}\n"],"mappings":";AAQA,IAAaA,IAAb,cAAkC,MAAM;CACpC,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGaC,IAAb,cAAsCH,EAAa;CAC/C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGaE,IAAb,cAAsCJ,EAAa;CAC/C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GASaG,IAAb,cAA+CL,EAAa;CACxD,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAWaI,IAAb,cAAoCN,EAAa;CAC7C,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ,GAGaK,IAAb,cAA6CP,EAAa;CACtD,YAAY,GAAiB,GAAwB;EAEjD,AADA,MAAMC,GAASC,CAAO,GACtB,KAAK,OAAO;CAChB;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"frame.cjs","names":[],"sources":["../../src/imaging/frame.ts"],"sourcesContent":["/**\n * Reading one frame out of a `<video>`, at the instant you asked for.\n *\n * The current frame needs nothing from this file: `createImageBitmap` takes a\n * video element, so `resizeImage(video)` already works. A **chosen** instant is\n * the part every app writes by hand and writes wrong:\n *\n * ```ts\n * video.currentTime = 12.5;\n * await new Promise((r) => video.addEventListener(\"seeked\", r, { once: true }));\n * ctx.drawImage(video, 0, 0); // may draw the PREVIOUS frame\n * ```\n *\n * `seeked` says the seek finished, not that the frame for the new position is\n * composited and readable by `drawImage`. The symptom is the worst kind: it\n * works on the machine it was written on and produces the neighbouring frame\n * elsewhere, with no error and no log.\n *\n * `requestVideoFrameCallback` is the only signal that reports a frame having\n * been **presented** — and measuring it, 2026-09-04 in Chromium, settled how it\n * can be used here: it fires while the video **plays** and does **not** fire\n * for a seek on a paused element. So blocking a seek on it would stall every\n * capture for the whole timeout and then proceed anyway. Instead:\n *\n * - Capturing from a **playing** video waits for the next presented frame, so\n * the pixels are demonstrably fresh. That is the screen-recording print.\n * - Capturing at an **instant** waits for `seeked` and then two animation\n * frames, which is what a browser offers for a paused element.\n * - Capturing the current frame of a **paused** video waits for nothing: the\n * frame on screen already is the frame.\n *\n * `confirmed` in the result says which of those happened, so a caller can tell\n * a demonstrably fresh frame from a best-effort one instead of guessing. A\n * seek-based capture reporting `confirmed: false` is the normal case, not a\n * warning sign.\n *\n * Three more traps come along for the ride:\n *\n * - **A recording may arrive with no duration.** `MediaRecorder` does not\n * guarantee a duration in the WebM header, so the blob `useVideoRecorder`\n * just handed you may report `Infinity` — and the video an app most wants to\n * grab a frame from is exactly that one. Seeking past the end forces the\n * browser to demux to the last frame, after which it knows the length; that\n * probe runs here rather than in every caller. Chromium was measured writing\n * the duration for a one-shot recording, so this is for the paths that omit\n * it — chunked `timeslice` recording, and other engines.\n * - **A cross-origin video taints the canvas**, and the failure surfaces far\n * from its cause, so it is re-thrown saying what to set.\n * - **Moving `currentTime` moves the player** somebody is watching; `restore`\n * puts it back, and touches only what actually moved.\n */\n\nimport { FrameSeekError, ImageDecodeError } from \"./exceptions\";\nimport { resizeImage } from \"./transform\";\nimport type { CaptureFrameOptions, CapturedFrame } from \"./types\";\n\n/** How long a seek and the frame after it may take, in milliseconds. */\nexport const DEFAULT_FRAME_TIMEOUT_MS = 3000;\n\n/** `HTMLMediaElement.HAVE_CURRENT_DATA` — there are pixels for the current position. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** Below this, in seconds, a seek to `currentTime` is a seek to where we already are. */\nconst SAME_FRAME_EPSILON = 0.001;\n\n/**\n * How long to wait for a presented frame, in milliseconds.\n *\n * Its own budget, not the caller's `timeoutMs`: this wait is an optimisation on\n * freshness, and a video playing at 24 fps presents one every 42 ms. Spending\n * seconds here would stall the capture for a guarantee the platform may simply\n * not be able to give.\n */\nconst FRAME_SETTLE_MS = 200;\n\n/**\n * A video element that reports presented frames.\n *\n * Declared structurally rather than by augmenting `HTMLVideoElement`: this is a\n * published package, and widening a lib interface would widen it for every\n * consumer, in every file, whether they load a polyfill or not.\n *\n * The two members are one capability. The spec defines them together, so a\n * runtime with `requestVideoFrameCallback` and no `cancelVideoFrameCallback`\n * does not exist — guarding the second separately would add a branch no test\n * can honestly reach.\n */\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback: (callback: (now: number) => void) => number;\n cancelVideoFrameCallback: (handle: number) => void;\n};\n\n/**\n * Whether this element reports presented frames.\n *\n * @param video The element to test.\n * @returns `true` when the frame callback pair is available.\n */\nfunction reportsFrames(video: HTMLVideoElement): video is FrameCallbackVideo {\n return typeof (video as Partial<FrameCallbackVideo>).requestVideoFrameCallback === \"function\";\n}\n\n/** Seek target that forces a demux to the end, to learn an unknown duration. */\nconst PAST_THE_END_SECONDS = 1e101;\n\n/** The `AbortError` a caller's `signal` produces. */\nfunction abortError(): DOMException {\n return new DOMException(\"The frame capture was aborted.\", \"AbortError\");\n}\n\n/**\n * Wait for one media event, and clean up on every exit.\n *\n * @param video The element to listen on.\n * @param type The event to wait for.\n * @param timeoutMs How long to wait.\n * @param signal Optional abort signal.\n * @param whenLate Message for the {@link FrameSeekError} on timeout.\n * @returns Resolves when the event fires.\n * @throws {@link FrameSeekError} when the timeout is reached.\n */\nfunction onceEvent(\n video: HTMLVideoElement,\n type: string,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n whenLate: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const finish = (settle: () => void): void => {\n clearTimeout(timer);\n video.removeEventListener(type, onEvent);\n signal?.removeEventListener(\"abort\", onAbort);\n settle();\n };\n const onEvent = (): void => finish(resolve);\n const onAbort = (): void => finish(() => reject(abortError()));\n\n const timer = setTimeout(\n () => finish(() => reject(new FrameSeekError(whenLate))),\n timeoutMs,\n );\n video.addEventListener(type, onEvent);\n signal?.addEventListener(\"abort\", onAbort);\n });\n}\n\n/**\n * Wait for the next frame the compositor presents.\n *\n * Only ever called for a **playing** element, because that is the only state in\n * which the callback fires — measured, not assumed. The timeout is the escape\n * for a hidden tab, which presents nothing at all.\n *\n * @param video A playing element that reports presented frames.\n * @returns `true` when a frame was presented, `false` when the wait expired.\n */\nfunction nextPresentedFrame(video: FrameCallbackVideo): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n video.cancelVideoFrameCallback(handle);\n resolve(false);\n }, FRAME_SETTLE_MS);\n const handle = video.requestVideoFrameCallback(() => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n}\n\n/**\n * Let the compositor catch up after a seek on a paused element.\n *\n * Two animation frames is what a browser offers here: the frame callback does\n * not fire for a paused seek, so there is nothing to confirm against. A runtime\n * with no animation frames at all gets a macrotask — enough for a test\n * environment, not a promise about pixels.\n *\n * @returns Resolves once the wait is over. Never a confirmation.\n */\nfunction settleAfterSeek(): Promise<void> {\n if (typeof requestAnimationFrame === \"function\") {\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()));\n });\n }\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n\n/**\n * Wait for the freshest frame this element's state allows, and say which.\n *\n * @param video The element about to be read.\n * @param seeked Whether a seek just moved it.\n * @returns `true` when a presented frame was observed.\n */\nasync function awaitReadableFrame(video: HTMLVideoElement, seeked: boolean): Promise<boolean> {\n if (!video.paused && reportsFrames(video)) return await nextPresentedFrame(video);\n if (seeked) await settleAfterSeek();\n return false;\n}\n\n/**\n * Whether the element is fed by a live stream rather than by a file.\n *\n * A truthiness check rather than `!== null`: the property is absent in\n * environments that model only part of `HTMLMediaElement`, where comparing\n * against `null` would read every element as live.\n *\n * @param video The element to test.\n * @returns `true` when a `MediaStream` is playing into it.\n */\nfunction isLiveStream(video: HTMLVideoElement): boolean {\n return Boolean(video.srcObject);\n}\n\n/**\n * The video's length in seconds, probing for it when the header lacks one.\n *\n * `MediaRecorder` does not guarantee a duration in the WebM it produces, so a\n * recording can report `Infinity` until something forces the browser to demux\n * to the end. That is what the seek past the end does — the same hack the\n * `AudioPlayer` uses, for the same reason: the value is not in the file.\n *\n * @param video The element to measure.\n * @param timeoutMs How long the probe may take.\n * @param signal Optional abort signal.\n * @returns The duration in seconds.\n * @throws {@link FrameSeekError} when the element is a live stream, or the\n * probe produced no duration.\n */\nasync function resolveDuration(\n video: HTMLVideoElement,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<number> {\n if (Number.isFinite(video.duration)) return video.duration;\n\n if (isLiveStream(video)) {\n throw new FrameSeekError(\n \"This element is playing a live MediaStream, which has no timeline to seek: \" +\n \"there is no instant but now. Leave `atMs` out to capture the frame on screen.\",\n );\n }\n\n const probed = onceEvent(\n video,\n \"timeupdate\",\n timeoutMs,\n signal,\n `The video reported no duration and the probe for it did not answer within ` +\n `${timeoutMs}ms, so there is no timeline to seek on.`,\n );\n video.currentTime = PAST_THE_END_SECONDS;\n await probed;\n\n if (!Number.isFinite(video.duration)) {\n throw new FrameSeekError(\n \"This video still reports no duration after seeking past its end, so it has \" +\n \"no seekable timeline — a live HLS or DASH source behaves this way. Leave \" +\n \"`atMs` out to capture the frame on screen.\",\n );\n }\n return video.duration;\n}\n\n/**\n * Move the video to an instant, and wait for that instant's frame.\n *\n * @param video The element to move.\n * @param atMs The instant, in milliseconds. Clamped to the duration.\n * @param timeoutMs How long the seek and the frame may take.\n * @param signal Optional abort signal.\n * @returns Whether a presented frame confirmed the new position.\n * @throws {@link FrameSeekError} when the video has no seekable timeline, or\n * the seek did not land in time.\n */\nasync function seekTo(\n video: HTMLVideoElement,\n atMs: number,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n const duration = await resolveDuration(video, timeoutMs, signal);\n\n const target = Math.min(Math.max(atMs / 1000, 0), duration);\n if (Math.abs(video.currentTime - target) < SAME_FRAME_EPSILON) {\n return await awaitReadableFrame(video, false);\n }\n\n const seeked = onceEvent(\n video,\n \"seeked\",\n timeoutMs,\n signal,\n `The video did not finish seeking to ${target.toFixed(3)}s within ${timeoutMs}ms. ` +\n \"Nothing was captured: a frame from the wrong instant is indistinguishable \" +\n \"from a correct one downstream.\",\n );\n video.currentTime = target;\n await seeked;\n return await awaitReadableFrame(video, true);\n}\n\n/**\n * Re-throw a tainted-canvas failure saying what to fix.\n *\n * A cross-origin video without `crossOrigin` taints everything it is drawn\n * into, and the `SecurityError` then arrives from the encoder — one call away\n * from the element that caused it, wrapped in whatever the decode said.\n *\n * @param error What the imaging pipeline threw.\n * @returns The error to throw instead.\n */\nfunction explained(error: unknown): unknown {\n const names = [error, (error as { cause?: unknown } | null)?.cause].map((candidate) =>\n typeof candidate === \"object\" && candidate !== null && \"name\" in candidate\n ? String((candidate as { name: unknown }).name)\n : \"\",\n );\n if (!names.includes(\"SecurityError\")) return error;\n return new ImageDecodeError(\n \"The video is cross-origin, so reading its pixels is not allowed. Set \" +\n 'crossOrigin=\"anonymous\" on the element before the source loads, and serve the ' +\n \"video with a permissive Access-Control-Allow-Origin.\",\n { cause: error },\n );\n}\n\n/**\n * Put playback back where the capture found it, touching only what moved.\n *\n * Each half is guarded because writing `currentTime` **is** a seek, even when\n * the value is unchanged: a capture that was refused before it moved anything\n * — a live stream, an abort — would otherwise perturb the player it never\n * touched. And a capture that paused without needing to move still has to let\n * go of the pause.\n *\n * @param video The element to restore.\n * @param time Where `currentTime` was.\n * @param wasPlaying Whether it was playing before the capture paused it.\n */\nfunction restorePlayback(video: HTMLVideoElement, time: number, wasPlaying: boolean): void {\n if (video.currentTime !== time) video.currentTime = time;\n if (wasPlaying && video.paused) void Promise.resolve(video.play()).catch(() => undefined);\n}\n\n/**\n * Capture a frame from a video as encoded image bytes.\n *\n * Without `atMs` it reads the frame on screen, which is what a screen or camera\n * recording wants — a print of what is being recorded. With `atMs` it seeks,\n * waits for that instant's frame to be presented, captures, and puts the player\n * back.\n *\n * @example Print of a screen recording in progress\n * ```ts\n * const shot = await captureFrame(videoRef.current!, {\n * type: \"image/webp\",\n * quality: 0.9,\n * });\n * await shareOrDownloadBlob(shot.blob, \"print.webp\");\n * ```\n *\n * @example A poster from ten seconds in, scaled down\n * ```ts\n * const poster = await captureFrame(video, { atMs: 10_000, width: 640 });\n * setPosterUrl(URL.createObjectURL(poster.blob));\n * console.log(`landed on ${poster.atMs}ms`);\n * ```\n *\n * @param video The element to read. It needs data — the capture waits for\n * `loadeddata` when the element has none yet.\n * @param options Instant, output box, format, and how long to wait.\n * @returns The encoded frame, the instant it actually came from, and whether a\n * presented frame confirmed that instant.\n * @throws {@link FrameSeekError} when the video has no data or no timeline in\n * time, or the seek did not land.\n * @throws {@link ImageDecodeError} when the pixels cannot be read — a\n * cross-origin video without `crossOrigin` is the common one.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function captureFrame(\n video: HTMLVideoElement,\n options: CaptureFrameOptions = {},\n): Promise<CapturedFrame> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;\n if (options.signal?.aborted === true) throw abortError();\n\n if (video.readyState < HAVE_CURRENT_DATA) {\n await onceEvent(\n video,\n \"loadeddata\",\n timeoutMs,\n options.signal,\n `The video had no frame to read within ${timeoutMs}ms (readyState ` +\n `${video.readyState}). Give it a source, or wait for it yourself.`,\n );\n }\n\n const { atMs: requestedMs } = options;\n const previousTime = video.currentTime;\n const wasPlaying = !video.paused;\n\n try {\n let confirmed: boolean;\n if (requestedMs === undefined) {\n confirmed = await awaitReadableFrame(video, false);\n } else {\n if (wasPlaying) video.pause();\n confirmed = await seekTo(video, requestedMs, timeoutMs, options.signal);\n }\n\n const atMs = video.currentTime * 1000;\n const encoded = await resizeImage(video, options);\n return { ...encoded, atMs, confirmed };\n } catch (error) {\n throw explained(error);\n } finally {\n if (options.restore !== false) restorePlayback(video, previousTime, wasPlaying);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"frame.cjs","names":["FrameSeekError","ImageDecodeError","resizeImage","require_exceptions","require_transform","DEFAULT_FRAME_TIMEOUT_MS","HAVE_CURRENT_DATA","SAME_FRAME_EPSILON","FRAME_SETTLE_MS","reportsFrames","video","PAST_THE_END_SECONDS","abortError","onceEvent","finish","timer","type","onEvent","signal","onAbort","settle","resolve","reject","whenLate","timeoutMs","nextPresentedFrame","handle","settleAfterSeek","awaitReadableFrame","seeked","isLiveStream","resolveDuration","probed","seekTo","duration","target","atMs","explained","error","candidate","restorePlayback","time","wasPlaying","captureFrame","options","requestedMs","previousTime","confirmed"],"sources":["../../src/imaging/frame.ts"],"sourcesContent":["/**\n * Reading one frame out of a `<video>`, at the instant you asked for.\n *\n * The current frame needs nothing from this file: `createImageBitmap` takes a\n * video element, so `resizeImage(video)` already works. A **chosen** instant is\n * the part every app writes by hand and writes wrong:\n *\n * ```ts\n * video.currentTime = 12.5;\n * await new Promise((r) => video.addEventListener(\"seeked\", r, { once: true }));\n * ctx.drawImage(video, 0, 0); // may draw the PREVIOUS frame\n * ```\n *\n * `seeked` says the seek finished, not that the frame for the new position is\n * composited and readable by `drawImage`. The symptom is the worst kind: it\n * works on the machine it was written on and produces the neighbouring frame\n * elsewhere, with no error and no log.\n *\n * `requestVideoFrameCallback` is the only signal that reports a frame having\n * been **presented** — and measuring it, 2026-09-04 in Chromium, settled how it\n * can be used here: it fires while the video **plays** and does **not** fire\n * for a seek on a paused element. So blocking a seek on it would stall every\n * capture for the whole timeout and then proceed anyway. Instead:\n *\n * - Capturing from a **playing** video waits for the next presented frame, so\n * the pixels are demonstrably fresh. That is the screen-recording print.\n * - Capturing at an **instant** waits for `seeked` and then two animation\n * frames, which is what a browser offers for a paused element.\n * - Capturing the current frame of a **paused** video waits for nothing: the\n * frame on screen already is the frame.\n *\n * `confirmed` in the result says which of those happened, so a caller can tell\n * a demonstrably fresh frame from a best-effort one instead of guessing. A\n * seek-based capture reporting `confirmed: false` is the normal case, not a\n * warning sign.\n *\n * Three more traps come along for the ride:\n *\n * - **A recording may arrive with no duration.** `MediaRecorder` does not\n * guarantee a duration in the WebM header, so the blob `useVideoRecorder`\n * just handed you may report `Infinity` — and the video an app most wants to\n * grab a frame from is exactly that one. Seeking past the end forces the\n * browser to demux to the last frame, after which it knows the length; that\n * probe runs here rather than in every caller. Chromium was measured writing\n * the duration for a one-shot recording, so this is for the paths that omit\n * it — chunked `timeslice` recording, and other engines.\n * - **A cross-origin video taints the canvas**, and the failure surfaces far\n * from its cause, so it is re-thrown saying what to set.\n * - **Moving `currentTime` moves the player** somebody is watching; `restore`\n * puts it back, and touches only what actually moved.\n */\n\nimport { FrameSeekError, ImageDecodeError } from \"./exceptions\";\nimport { resizeImage } from \"./transform\";\nimport type { CaptureFrameOptions, CapturedFrame } from \"./types\";\n\n/** How long a seek and the frame after it may take, in milliseconds. */\nexport const DEFAULT_FRAME_TIMEOUT_MS = 3000;\n\n/** `HTMLMediaElement.HAVE_CURRENT_DATA` — there are pixels for the current position. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** Below this, in seconds, a seek to `currentTime` is a seek to where we already are. */\nconst SAME_FRAME_EPSILON = 0.001;\n\n/**\n * How long to wait for a presented frame, in milliseconds.\n *\n * Its own budget, not the caller's `timeoutMs`: this wait is an optimisation on\n * freshness, and a video playing at 24 fps presents one every 42 ms. Spending\n * seconds here would stall the capture for a guarantee the platform may simply\n * not be able to give.\n */\nconst FRAME_SETTLE_MS = 200;\n\n/**\n * A video element that reports presented frames.\n *\n * Declared structurally rather than by augmenting `HTMLVideoElement`: this is a\n * published package, and widening a lib interface would widen it for every\n * consumer, in every file, whether they load a polyfill or not.\n *\n * The two members are one capability. The spec defines them together, so a\n * runtime with `requestVideoFrameCallback` and no `cancelVideoFrameCallback`\n * does not exist — guarding the second separately would add a branch no test\n * can honestly reach.\n */\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback: (callback: (now: number) => void) => number;\n cancelVideoFrameCallback: (handle: number) => void;\n};\n\n/**\n * Whether this element reports presented frames.\n *\n * @param video The element to test.\n * @returns `true` when the frame callback pair is available.\n */\nfunction reportsFrames(video: HTMLVideoElement): video is FrameCallbackVideo {\n return typeof (video as Partial<FrameCallbackVideo>).requestVideoFrameCallback === \"function\";\n}\n\n/** Seek target that forces a demux to the end, to learn an unknown duration. */\nconst PAST_THE_END_SECONDS = 1e101;\n\n/** The `AbortError` a caller's `signal` produces. */\nfunction abortError(): DOMException {\n return new DOMException(\"The frame capture was aborted.\", \"AbortError\");\n}\n\n/**\n * Wait for one media event, and clean up on every exit.\n *\n * @param video The element to listen on.\n * @param type The event to wait for.\n * @param timeoutMs How long to wait.\n * @param signal Optional abort signal.\n * @param whenLate Message for the {@link FrameSeekError} on timeout.\n * @returns Resolves when the event fires.\n * @throws {@link FrameSeekError} when the timeout is reached.\n */\nfunction onceEvent(\n video: HTMLVideoElement,\n type: string,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n whenLate: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const finish = (settle: () => void): void => {\n clearTimeout(timer);\n video.removeEventListener(type, onEvent);\n signal?.removeEventListener(\"abort\", onAbort);\n settle();\n };\n const onEvent = (): void => finish(resolve);\n const onAbort = (): void => finish(() => reject(abortError()));\n\n const timer = setTimeout(\n () => finish(() => reject(new FrameSeekError(whenLate))),\n timeoutMs,\n );\n video.addEventListener(type, onEvent);\n signal?.addEventListener(\"abort\", onAbort);\n });\n}\n\n/**\n * Wait for the next frame the compositor presents.\n *\n * Only ever called for a **playing** element, because that is the only state in\n * which the callback fires — measured, not assumed. The timeout is the escape\n * for a hidden tab, which presents nothing at all.\n *\n * @param video A playing element that reports presented frames.\n * @returns `true` when a frame was presented, `false` when the wait expired.\n */\nfunction nextPresentedFrame(video: FrameCallbackVideo): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n video.cancelVideoFrameCallback(handle);\n resolve(false);\n }, FRAME_SETTLE_MS);\n const handle = video.requestVideoFrameCallback(() => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n}\n\n/**\n * Let the compositor catch up after a seek on a paused element.\n *\n * Two animation frames is what a browser offers here: the frame callback does\n * not fire for a paused seek, so there is nothing to confirm against. A runtime\n * with no animation frames at all gets a macrotask — enough for a test\n * environment, not a promise about pixels.\n *\n * @returns Resolves once the wait is over. Never a confirmation.\n */\nfunction settleAfterSeek(): Promise<void> {\n if (typeof requestAnimationFrame === \"function\") {\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()));\n });\n }\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n\n/**\n * Wait for the freshest frame this element's state allows, and say which.\n *\n * @param video The element about to be read.\n * @param seeked Whether a seek just moved it.\n * @returns `true` when a presented frame was observed.\n */\nasync function awaitReadableFrame(video: HTMLVideoElement, seeked: boolean): Promise<boolean> {\n if (!video.paused && reportsFrames(video)) return await nextPresentedFrame(video);\n if (seeked) await settleAfterSeek();\n return false;\n}\n\n/**\n * Whether the element is fed by a live stream rather than by a file.\n *\n * A truthiness check rather than `!== null`: the property is absent in\n * environments that model only part of `HTMLMediaElement`, where comparing\n * against `null` would read every element as live.\n *\n * @param video The element to test.\n * @returns `true` when a `MediaStream` is playing into it.\n */\nfunction isLiveStream(video: HTMLVideoElement): boolean {\n return Boolean(video.srcObject);\n}\n\n/**\n * The video's length in seconds, probing for it when the header lacks one.\n *\n * `MediaRecorder` does not guarantee a duration in the WebM it produces, so a\n * recording can report `Infinity` until something forces the browser to demux\n * to the end. That is what the seek past the end does — the same hack the\n * `AudioPlayer` uses, for the same reason: the value is not in the file.\n *\n * @param video The element to measure.\n * @param timeoutMs How long the probe may take.\n * @param signal Optional abort signal.\n * @returns The duration in seconds.\n * @throws {@link FrameSeekError} when the element is a live stream, or the\n * probe produced no duration.\n */\nasync function resolveDuration(\n video: HTMLVideoElement,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<number> {\n if (Number.isFinite(video.duration)) return video.duration;\n\n if (isLiveStream(video)) {\n throw new FrameSeekError(\n \"This element is playing a live MediaStream, which has no timeline to seek: \" +\n \"there is no instant but now. Leave `atMs` out to capture the frame on screen.\",\n );\n }\n\n const probed = onceEvent(\n video,\n \"timeupdate\",\n timeoutMs,\n signal,\n `The video reported no duration and the probe for it did not answer within ` +\n `${timeoutMs}ms, so there is no timeline to seek on.`,\n );\n video.currentTime = PAST_THE_END_SECONDS;\n await probed;\n\n if (!Number.isFinite(video.duration)) {\n throw new FrameSeekError(\n \"This video still reports no duration after seeking past its end, so it has \" +\n \"no seekable timeline — a live HLS or DASH source behaves this way. Leave \" +\n \"`atMs` out to capture the frame on screen.\",\n );\n }\n return video.duration;\n}\n\n/**\n * Move the video to an instant, and wait for that instant's frame.\n *\n * @param video The element to move.\n * @param atMs The instant, in milliseconds. Clamped to the duration.\n * @param timeoutMs How long the seek and the frame may take.\n * @param signal Optional abort signal.\n * @returns Whether a presented frame confirmed the new position.\n * @throws {@link FrameSeekError} when the video has no seekable timeline, or\n * the seek did not land in time.\n */\nasync function seekTo(\n video: HTMLVideoElement,\n atMs: number,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n const duration = await resolveDuration(video, timeoutMs, signal);\n\n const target = Math.min(Math.max(atMs / 1000, 0), duration);\n if (Math.abs(video.currentTime - target) < SAME_FRAME_EPSILON) {\n return await awaitReadableFrame(video, false);\n }\n\n const seeked = onceEvent(\n video,\n \"seeked\",\n timeoutMs,\n signal,\n `The video did not finish seeking to ${target.toFixed(3)}s within ${timeoutMs}ms. ` +\n \"Nothing was captured: a frame from the wrong instant is indistinguishable \" +\n \"from a correct one downstream.\",\n );\n video.currentTime = target;\n await seeked;\n return await awaitReadableFrame(video, true);\n}\n\n/**\n * Re-throw a tainted-canvas failure saying what to fix.\n *\n * A cross-origin video without `crossOrigin` taints everything it is drawn\n * into, and the `SecurityError` then arrives from the encoder — one call away\n * from the element that caused it, wrapped in whatever the decode said.\n *\n * @param error What the imaging pipeline threw.\n * @returns The error to throw instead.\n */\nfunction explained(error: unknown): unknown {\n const names = [error, (error as { cause?: unknown } | null)?.cause].map((candidate) =>\n typeof candidate === \"object\" && candidate !== null && \"name\" in candidate\n ? String((candidate as { name: unknown }).name)\n : \"\",\n );\n if (!names.includes(\"SecurityError\")) return error;\n return new ImageDecodeError(\n \"The video is cross-origin, so reading its pixels is not allowed. Set \" +\n 'crossOrigin=\"anonymous\" on the element before the source loads, and serve the ' +\n \"video with a permissive Access-Control-Allow-Origin.\",\n { cause: error },\n );\n}\n\n/**\n * Put playback back where the capture found it, touching only what moved.\n *\n * Each half is guarded because writing `currentTime` **is** a seek, even when\n * the value is unchanged: a capture that was refused before it moved anything\n * — a live stream, an abort — would otherwise perturb the player it never\n * touched. And a capture that paused without needing to move still has to let\n * go of the pause.\n *\n * @param video The element to restore.\n * @param time Where `currentTime` was.\n * @param wasPlaying Whether it was playing before the capture paused it.\n */\nfunction restorePlayback(video: HTMLVideoElement, time: number, wasPlaying: boolean): void {\n if (video.currentTime !== time) video.currentTime = time;\n if (wasPlaying && video.paused) void Promise.resolve(video.play()).catch(() => undefined);\n}\n\n/**\n * Capture a frame from a video as encoded image bytes.\n *\n * Without `atMs` it reads the frame on screen, which is what a screen or camera\n * recording wants — a print of what is being recorded. With `atMs` it seeks,\n * waits for that instant's frame to be presented, captures, and puts the player\n * back.\n *\n * @example Print of a screen recording in progress\n * ```ts\n * const shot = await captureFrame(videoRef.current!, {\n * type: \"image/webp\",\n * quality: 0.9,\n * });\n * await shareOrDownloadBlob(shot.blob, \"print.webp\");\n * ```\n *\n * @example A poster from ten seconds in, scaled down\n * ```ts\n * const poster = await captureFrame(video, { atMs: 10_000, width: 640 });\n * setPosterUrl(URL.createObjectURL(poster.blob));\n * console.log(`landed on ${poster.atMs}ms`);\n * ```\n *\n * @param video The element to read. It needs data — the capture waits for\n * `loadeddata` when the element has none yet.\n * @param options Instant, output box, format, and how long to wait.\n * @returns The encoded frame, the instant it actually came from, and whether a\n * presented frame confirmed that instant.\n * @throws {@link FrameSeekError} when the video has no data or no timeline in\n * time, or the seek did not land.\n * @throws {@link ImageDecodeError} when the pixels cannot be read — a\n * cross-origin video without `crossOrigin` is the common one.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function captureFrame(\n video: HTMLVideoElement,\n options: CaptureFrameOptions = {},\n): Promise<CapturedFrame> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;\n if (options.signal?.aborted === true) throw abortError();\n\n if (video.readyState < HAVE_CURRENT_DATA) {\n await onceEvent(\n video,\n \"loadeddata\",\n timeoutMs,\n options.signal,\n `The video had no frame to read within ${timeoutMs}ms (readyState ` +\n `${video.readyState}). Give it a source, or wait for it yourself.`,\n );\n }\n\n const { atMs: requestedMs } = options;\n const previousTime = video.currentTime;\n const wasPlaying = !video.paused;\n\n try {\n let confirmed: boolean;\n if (requestedMs === undefined) {\n confirmed = await awaitReadableFrame(video, false);\n } else {\n if (wasPlaying) video.pause();\n confirmed = await seekTo(video, requestedMs, timeoutMs, options.signal);\n }\n\n const atMs = video.currentTime * 1000;\n const encoded = await resizeImage(video, options);\n return { ...encoded, atMs, confirmed };\n } catch (error) {\n throw explained(error);\n } finally {\n if (options.restore !== false) restorePlayback(video, previousTime, wasPlaying);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"frame.js","names":[],"sources":["../../src/imaging/frame.ts"],"sourcesContent":["/**\n * Reading one frame out of a `<video>`, at the instant you asked for.\n *\n * The current frame needs nothing from this file: `createImageBitmap` takes a\n * video element, so `resizeImage(video)` already works. A **chosen** instant is\n * the part every app writes by hand and writes wrong:\n *\n * ```ts\n * video.currentTime = 12.5;\n * await new Promise((r) => video.addEventListener(\"seeked\", r, { once: true }));\n * ctx.drawImage(video, 0, 0); // may draw the PREVIOUS frame\n * ```\n *\n * `seeked` says the seek finished, not that the frame for the new position is\n * composited and readable by `drawImage`. The symptom is the worst kind: it\n * works on the machine it was written on and produces the neighbouring frame\n * elsewhere, with no error and no log.\n *\n * `requestVideoFrameCallback` is the only signal that reports a frame having\n * been **presented** — and measuring it, 2026-09-04 in Chromium, settled how it\n * can be used here: it fires while the video **plays** and does **not** fire\n * for a seek on a paused element. So blocking a seek on it would stall every\n * capture for the whole timeout and then proceed anyway. Instead:\n *\n * - Capturing from a **playing** video waits for the next presented frame, so\n * the pixels are demonstrably fresh. That is the screen-recording print.\n * - Capturing at an **instant** waits for `seeked` and then two animation\n * frames, which is what a browser offers for a paused element.\n * - Capturing the current frame of a **paused** video waits for nothing: the\n * frame on screen already is the frame.\n *\n * `confirmed` in the result says which of those happened, so a caller can tell\n * a demonstrably fresh frame from a best-effort one instead of guessing. A\n * seek-based capture reporting `confirmed: false` is the normal case, not a\n * warning sign.\n *\n * Three more traps come along for the ride:\n *\n * - **A recording may arrive with no duration.** `MediaRecorder` does not\n * guarantee a duration in the WebM header, so the blob `useVideoRecorder`\n * just handed you may report `Infinity` — and the video an app most wants to\n * grab a frame from is exactly that one. Seeking past the end forces the\n * browser to demux to the last frame, after which it knows the length; that\n * probe runs here rather than in every caller. Chromium was measured writing\n * the duration for a one-shot recording, so this is for the paths that omit\n * it — chunked `timeslice` recording, and other engines.\n * - **A cross-origin video taints the canvas**, and the failure surfaces far\n * from its cause, so it is re-thrown saying what to set.\n * - **Moving `currentTime` moves the player** somebody is watching; `restore`\n * puts it back, and touches only what actually moved.\n */\n\nimport { FrameSeekError, ImageDecodeError } from \"./exceptions\";\nimport { resizeImage } from \"./transform\";\nimport type { CaptureFrameOptions, CapturedFrame } from \"./types\";\n\n/** How long a seek and the frame after it may take, in milliseconds. */\nexport const DEFAULT_FRAME_TIMEOUT_MS = 3000;\n\n/** `HTMLMediaElement.HAVE_CURRENT_DATA` — there are pixels for the current position. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** Below this, in seconds, a seek to `currentTime` is a seek to where we already are. */\nconst SAME_FRAME_EPSILON = 0.001;\n\n/**\n * How long to wait for a presented frame, in milliseconds.\n *\n * Its own budget, not the caller's `timeoutMs`: this wait is an optimisation on\n * freshness, and a video playing at 24 fps presents one every 42 ms. Spending\n * seconds here would stall the capture for a guarantee the platform may simply\n * not be able to give.\n */\nconst FRAME_SETTLE_MS = 200;\n\n/**\n * A video element that reports presented frames.\n *\n * Declared structurally rather than by augmenting `HTMLVideoElement`: this is a\n * published package, and widening a lib interface would widen it for every\n * consumer, in every file, whether they load a polyfill or not.\n *\n * The two members are one capability. The spec defines them together, so a\n * runtime with `requestVideoFrameCallback` and no `cancelVideoFrameCallback`\n * does not exist — guarding the second separately would add a branch no test\n * can honestly reach.\n */\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback: (callback: (now: number) => void) => number;\n cancelVideoFrameCallback: (handle: number) => void;\n};\n\n/**\n * Whether this element reports presented frames.\n *\n * @param video The element to test.\n * @returns `true` when the frame callback pair is available.\n */\nfunction reportsFrames(video: HTMLVideoElement): video is FrameCallbackVideo {\n return typeof (video as Partial<FrameCallbackVideo>).requestVideoFrameCallback === \"function\";\n}\n\n/** Seek target that forces a demux to the end, to learn an unknown duration. */\nconst PAST_THE_END_SECONDS = 1e101;\n\n/** The `AbortError` a caller's `signal` produces. */\nfunction abortError(): DOMException {\n return new DOMException(\"The frame capture was aborted.\", \"AbortError\");\n}\n\n/**\n * Wait for one media event, and clean up on every exit.\n *\n * @param video The element to listen on.\n * @param type The event to wait for.\n * @param timeoutMs How long to wait.\n * @param signal Optional abort signal.\n * @param whenLate Message for the {@link FrameSeekError} on timeout.\n * @returns Resolves when the event fires.\n * @throws {@link FrameSeekError} when the timeout is reached.\n */\nfunction onceEvent(\n video: HTMLVideoElement,\n type: string,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n whenLate: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const finish = (settle: () => void): void => {\n clearTimeout(timer);\n video.removeEventListener(type, onEvent);\n signal?.removeEventListener(\"abort\", onAbort);\n settle();\n };\n const onEvent = (): void => finish(resolve);\n const onAbort = (): void => finish(() => reject(abortError()));\n\n const timer = setTimeout(\n () => finish(() => reject(new FrameSeekError(whenLate))),\n timeoutMs,\n );\n video.addEventListener(type, onEvent);\n signal?.addEventListener(\"abort\", onAbort);\n });\n}\n\n/**\n * Wait for the next frame the compositor presents.\n *\n * Only ever called for a **playing** element, because that is the only state in\n * which the callback fires — measured, not assumed. The timeout is the escape\n * for a hidden tab, which presents nothing at all.\n *\n * @param video A playing element that reports presented frames.\n * @returns `true` when a frame was presented, `false` when the wait expired.\n */\nfunction nextPresentedFrame(video: FrameCallbackVideo): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n video.cancelVideoFrameCallback(handle);\n resolve(false);\n }, FRAME_SETTLE_MS);\n const handle = video.requestVideoFrameCallback(() => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n}\n\n/**\n * Let the compositor catch up after a seek on a paused element.\n *\n * Two animation frames is what a browser offers here: the frame callback does\n * not fire for a paused seek, so there is nothing to confirm against. A runtime\n * with no animation frames at all gets a macrotask — enough for a test\n * environment, not a promise about pixels.\n *\n * @returns Resolves once the wait is over. Never a confirmation.\n */\nfunction settleAfterSeek(): Promise<void> {\n if (typeof requestAnimationFrame === \"function\") {\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()));\n });\n }\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n\n/**\n * Wait for the freshest frame this element's state allows, and say which.\n *\n * @param video The element about to be read.\n * @param seeked Whether a seek just moved it.\n * @returns `true` when a presented frame was observed.\n */\nasync function awaitReadableFrame(video: HTMLVideoElement, seeked: boolean): Promise<boolean> {\n if (!video.paused && reportsFrames(video)) return await nextPresentedFrame(video);\n if (seeked) await settleAfterSeek();\n return false;\n}\n\n/**\n * Whether the element is fed by a live stream rather than by a file.\n *\n * A truthiness check rather than `!== null`: the property is absent in\n * environments that model only part of `HTMLMediaElement`, where comparing\n * against `null` would read every element as live.\n *\n * @param video The element to test.\n * @returns `true` when a `MediaStream` is playing into it.\n */\nfunction isLiveStream(video: HTMLVideoElement): boolean {\n return Boolean(video.srcObject);\n}\n\n/**\n * The video's length in seconds, probing for it when the header lacks one.\n *\n * `MediaRecorder` does not guarantee a duration in the WebM it produces, so a\n * recording can report `Infinity` until something forces the browser to demux\n * to the end. That is what the seek past the end does — the same hack the\n * `AudioPlayer` uses, for the same reason: the value is not in the file.\n *\n * @param video The element to measure.\n * @param timeoutMs How long the probe may take.\n * @param signal Optional abort signal.\n * @returns The duration in seconds.\n * @throws {@link FrameSeekError} when the element is a live stream, or the\n * probe produced no duration.\n */\nasync function resolveDuration(\n video: HTMLVideoElement,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<number> {\n if (Number.isFinite(video.duration)) return video.duration;\n\n if (isLiveStream(video)) {\n throw new FrameSeekError(\n \"This element is playing a live MediaStream, which has no timeline to seek: \" +\n \"there is no instant but now. Leave `atMs` out to capture the frame on screen.\",\n );\n }\n\n const probed = onceEvent(\n video,\n \"timeupdate\",\n timeoutMs,\n signal,\n `The video reported no duration and the probe for it did not answer within ` +\n `${timeoutMs}ms, so there is no timeline to seek on.`,\n );\n video.currentTime = PAST_THE_END_SECONDS;\n await probed;\n\n if (!Number.isFinite(video.duration)) {\n throw new FrameSeekError(\n \"This video still reports no duration after seeking past its end, so it has \" +\n \"no seekable timeline — a live HLS or DASH source behaves this way. Leave \" +\n \"`atMs` out to capture the frame on screen.\",\n );\n }\n return video.duration;\n}\n\n/**\n * Move the video to an instant, and wait for that instant's frame.\n *\n * @param video The element to move.\n * @param atMs The instant, in milliseconds. Clamped to the duration.\n * @param timeoutMs How long the seek and the frame may take.\n * @param signal Optional abort signal.\n * @returns Whether a presented frame confirmed the new position.\n * @throws {@link FrameSeekError} when the video has no seekable timeline, or\n * the seek did not land in time.\n */\nasync function seekTo(\n video: HTMLVideoElement,\n atMs: number,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n const duration = await resolveDuration(video, timeoutMs, signal);\n\n const target = Math.min(Math.max(atMs / 1000, 0), duration);\n if (Math.abs(video.currentTime - target) < SAME_FRAME_EPSILON) {\n return await awaitReadableFrame(video, false);\n }\n\n const seeked = onceEvent(\n video,\n \"seeked\",\n timeoutMs,\n signal,\n `The video did not finish seeking to ${target.toFixed(3)}s within ${timeoutMs}ms. ` +\n \"Nothing was captured: a frame from the wrong instant is indistinguishable \" +\n \"from a correct one downstream.\",\n );\n video.currentTime = target;\n await seeked;\n return await awaitReadableFrame(video, true);\n}\n\n/**\n * Re-throw a tainted-canvas failure saying what to fix.\n *\n * A cross-origin video without `crossOrigin` taints everything it is drawn\n * into, and the `SecurityError` then arrives from the encoder — one call away\n * from the element that caused it, wrapped in whatever the decode said.\n *\n * @param error What the imaging pipeline threw.\n * @returns The error to throw instead.\n */\nfunction explained(error: unknown): unknown {\n const names = [error, (error as { cause?: unknown } | null)?.cause].map((candidate) =>\n typeof candidate === \"object\" && candidate !== null && \"name\" in candidate\n ? String((candidate as { name: unknown }).name)\n : \"\",\n );\n if (!names.includes(\"SecurityError\")) return error;\n return new ImageDecodeError(\n \"The video is cross-origin, so reading its pixels is not allowed. Set \" +\n 'crossOrigin=\"anonymous\" on the element before the source loads, and serve the ' +\n \"video with a permissive Access-Control-Allow-Origin.\",\n { cause: error },\n );\n}\n\n/**\n * Put playback back where the capture found it, touching only what moved.\n *\n * Each half is guarded because writing `currentTime` **is** a seek, even when\n * the value is unchanged: a capture that was refused before it moved anything\n * — a live stream, an abort — would otherwise perturb the player it never\n * touched. And a capture that paused without needing to move still has to let\n * go of the pause.\n *\n * @param video The element to restore.\n * @param time Where `currentTime` was.\n * @param wasPlaying Whether it was playing before the capture paused it.\n */\nfunction restorePlayback(video: HTMLVideoElement, time: number, wasPlaying: boolean): void {\n if (video.currentTime !== time) video.currentTime = time;\n if (wasPlaying && video.paused) void Promise.resolve(video.play()).catch(() => undefined);\n}\n\n/**\n * Capture a frame from a video as encoded image bytes.\n *\n * Without `atMs` it reads the frame on screen, which is what a screen or camera\n * recording wants — a print of what is being recorded. With `atMs` it seeks,\n * waits for that instant's frame to be presented, captures, and puts the player\n * back.\n *\n * @example Print of a screen recording in progress\n * ```ts\n * const shot = await captureFrame(videoRef.current!, {\n * type: \"image/webp\",\n * quality: 0.9,\n * });\n * await shareOrDownloadBlob(shot.blob, \"print.webp\");\n * ```\n *\n * @example A poster from ten seconds in, scaled down\n * ```ts\n * const poster = await captureFrame(video, { atMs: 10_000, width: 640 });\n * setPosterUrl(URL.createObjectURL(poster.blob));\n * console.log(`landed on ${poster.atMs}ms`);\n * ```\n *\n * @param video The element to read. It needs data — the capture waits for\n * `loadeddata` when the element has none yet.\n * @param options Instant, output box, format, and how long to wait.\n * @returns The encoded frame, the instant it actually came from, and whether a\n * presented frame confirmed that instant.\n * @throws {@link FrameSeekError} when the video has no data or no timeline in\n * time, or the seek did not land.\n * @throws {@link ImageDecodeError} when the pixels cannot be read — a\n * cross-origin video without `crossOrigin` is the common one.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function captureFrame(\n video: HTMLVideoElement,\n options: CaptureFrameOptions = {},\n): Promise<CapturedFrame> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;\n if (options.signal?.aborted === true) throw abortError();\n\n if (video.readyState < HAVE_CURRENT_DATA) {\n await onceEvent(\n video,\n \"loadeddata\",\n timeoutMs,\n options.signal,\n `The video had no frame to read within ${timeoutMs}ms (readyState ` +\n `${video.readyState}). Give it a source, or wait for it yourself.`,\n );\n }\n\n const { atMs: requestedMs } = options;\n const previousTime = video.currentTime;\n const wasPlaying = !video.paused;\n\n try {\n let confirmed: boolean;\n if (requestedMs === undefined) {\n confirmed = await awaitReadableFrame(video, false);\n } else {\n if (wasPlaying) video.pause();\n confirmed = await seekTo(video, requestedMs, timeoutMs, options.signal);\n }\n\n const atMs = video.currentTime * 1000;\n const encoded = await resizeImage(video, options);\n return { ...encoded, atMs, confirmed };\n } catch (error) {\n throw explained(error);\n } finally {\n if (options.restore !== false) restorePlayback(video, previousTime, wasPlaying);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"frame.js","names":["DEFAULT_FRAME_TIMEOUT_MS","HAVE_CURRENT_DATA","SAME_FRAME_EPSILON","FRAME_SETTLE_MS","reportsFrames","video","PAST_THE_END_SECONDS","abortError","onceEvent","finish","timer","type","onEvent","signal","onAbort","settle","resolve","reject","FrameSeekError","whenLate","timeoutMs","nextPresentedFrame","handle","settleAfterSeek","awaitReadableFrame","seeked","isLiveStream","resolveDuration","probed","seekTo","duration","target","atMs","explained","error","candidate","ImageDecodeError","restorePlayback","time","wasPlaying","captureFrame","options","requestedMs","previousTime","confirmed","resizeImage"],"sources":["../../src/imaging/frame.ts"],"sourcesContent":["/**\n * Reading one frame out of a `<video>`, at the instant you asked for.\n *\n * The current frame needs nothing from this file: `createImageBitmap` takes a\n * video element, so `resizeImage(video)` already works. A **chosen** instant is\n * the part every app writes by hand and writes wrong:\n *\n * ```ts\n * video.currentTime = 12.5;\n * await new Promise((r) => video.addEventListener(\"seeked\", r, { once: true }));\n * ctx.drawImage(video, 0, 0); // may draw the PREVIOUS frame\n * ```\n *\n * `seeked` says the seek finished, not that the frame for the new position is\n * composited and readable by `drawImage`. The symptom is the worst kind: it\n * works on the machine it was written on and produces the neighbouring frame\n * elsewhere, with no error and no log.\n *\n * `requestVideoFrameCallback` is the only signal that reports a frame having\n * been **presented** — and measuring it, 2026-09-04 in Chromium, settled how it\n * can be used here: it fires while the video **plays** and does **not** fire\n * for a seek on a paused element. So blocking a seek on it would stall every\n * capture for the whole timeout and then proceed anyway. Instead:\n *\n * - Capturing from a **playing** video waits for the next presented frame, so\n * the pixels are demonstrably fresh. That is the screen-recording print.\n * - Capturing at an **instant** waits for `seeked` and then two animation\n * frames, which is what a browser offers for a paused element.\n * - Capturing the current frame of a **paused** video waits for nothing: the\n * frame on screen already is the frame.\n *\n * `confirmed` in the result says which of those happened, so a caller can tell\n * a demonstrably fresh frame from a best-effort one instead of guessing. A\n * seek-based capture reporting `confirmed: false` is the normal case, not a\n * warning sign.\n *\n * Three more traps come along for the ride:\n *\n * - **A recording may arrive with no duration.** `MediaRecorder` does not\n * guarantee a duration in the WebM header, so the blob `useVideoRecorder`\n * just handed you may report `Infinity` — and the video an app most wants to\n * grab a frame from is exactly that one. Seeking past the end forces the\n * browser to demux to the last frame, after which it knows the length; that\n * probe runs here rather than in every caller. Chromium was measured writing\n * the duration for a one-shot recording, so this is for the paths that omit\n * it — chunked `timeslice` recording, and other engines.\n * - **A cross-origin video taints the canvas**, and the failure surfaces far\n * from its cause, so it is re-thrown saying what to set.\n * - **Moving `currentTime` moves the player** somebody is watching; `restore`\n * puts it back, and touches only what actually moved.\n */\n\nimport { FrameSeekError, ImageDecodeError } from \"./exceptions\";\nimport { resizeImage } from \"./transform\";\nimport type { CaptureFrameOptions, CapturedFrame } from \"./types\";\n\n/** How long a seek and the frame after it may take, in milliseconds. */\nexport const DEFAULT_FRAME_TIMEOUT_MS = 3000;\n\n/** `HTMLMediaElement.HAVE_CURRENT_DATA` — there are pixels for the current position. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** Below this, in seconds, a seek to `currentTime` is a seek to where we already are. */\nconst SAME_FRAME_EPSILON = 0.001;\n\n/**\n * How long to wait for a presented frame, in milliseconds.\n *\n * Its own budget, not the caller's `timeoutMs`: this wait is an optimisation on\n * freshness, and a video playing at 24 fps presents one every 42 ms. Spending\n * seconds here would stall the capture for a guarantee the platform may simply\n * not be able to give.\n */\nconst FRAME_SETTLE_MS = 200;\n\n/**\n * A video element that reports presented frames.\n *\n * Declared structurally rather than by augmenting `HTMLVideoElement`: this is a\n * published package, and widening a lib interface would widen it for every\n * consumer, in every file, whether they load a polyfill or not.\n *\n * The two members are one capability. The spec defines them together, so a\n * runtime with `requestVideoFrameCallback` and no `cancelVideoFrameCallback`\n * does not exist — guarding the second separately would add a branch no test\n * can honestly reach.\n */\ntype FrameCallbackVideo = HTMLVideoElement & {\n requestVideoFrameCallback: (callback: (now: number) => void) => number;\n cancelVideoFrameCallback: (handle: number) => void;\n};\n\n/**\n * Whether this element reports presented frames.\n *\n * @param video The element to test.\n * @returns `true` when the frame callback pair is available.\n */\nfunction reportsFrames(video: HTMLVideoElement): video is FrameCallbackVideo {\n return typeof (video as Partial<FrameCallbackVideo>).requestVideoFrameCallback === \"function\";\n}\n\n/** Seek target that forces a demux to the end, to learn an unknown duration. */\nconst PAST_THE_END_SECONDS = 1e101;\n\n/** The `AbortError` a caller's `signal` produces. */\nfunction abortError(): DOMException {\n return new DOMException(\"The frame capture was aborted.\", \"AbortError\");\n}\n\n/**\n * Wait for one media event, and clean up on every exit.\n *\n * @param video The element to listen on.\n * @param type The event to wait for.\n * @param timeoutMs How long to wait.\n * @param signal Optional abort signal.\n * @param whenLate Message for the {@link FrameSeekError} on timeout.\n * @returns Resolves when the event fires.\n * @throws {@link FrameSeekError} when the timeout is reached.\n */\nfunction onceEvent(\n video: HTMLVideoElement,\n type: string,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n whenLate: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const finish = (settle: () => void): void => {\n clearTimeout(timer);\n video.removeEventListener(type, onEvent);\n signal?.removeEventListener(\"abort\", onAbort);\n settle();\n };\n const onEvent = (): void => finish(resolve);\n const onAbort = (): void => finish(() => reject(abortError()));\n\n const timer = setTimeout(\n () => finish(() => reject(new FrameSeekError(whenLate))),\n timeoutMs,\n );\n video.addEventListener(type, onEvent);\n signal?.addEventListener(\"abort\", onAbort);\n });\n}\n\n/**\n * Wait for the next frame the compositor presents.\n *\n * Only ever called for a **playing** element, because that is the only state in\n * which the callback fires — measured, not assumed. The timeout is the escape\n * for a hidden tab, which presents nothing at all.\n *\n * @param video A playing element that reports presented frames.\n * @returns `true` when a frame was presented, `false` when the wait expired.\n */\nfunction nextPresentedFrame(video: FrameCallbackVideo): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n video.cancelVideoFrameCallback(handle);\n resolve(false);\n }, FRAME_SETTLE_MS);\n const handle = video.requestVideoFrameCallback(() => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n}\n\n/**\n * Let the compositor catch up after a seek on a paused element.\n *\n * Two animation frames is what a browser offers here: the frame callback does\n * not fire for a paused seek, so there is nothing to confirm against. A runtime\n * with no animation frames at all gets a macrotask — enough for a test\n * environment, not a promise about pixels.\n *\n * @returns Resolves once the wait is over. Never a confirmation.\n */\nfunction settleAfterSeek(): Promise<void> {\n if (typeof requestAnimationFrame === \"function\") {\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()));\n });\n }\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n\n/**\n * Wait for the freshest frame this element's state allows, and say which.\n *\n * @param video The element about to be read.\n * @param seeked Whether a seek just moved it.\n * @returns `true` when a presented frame was observed.\n */\nasync function awaitReadableFrame(video: HTMLVideoElement, seeked: boolean): Promise<boolean> {\n if (!video.paused && reportsFrames(video)) return await nextPresentedFrame(video);\n if (seeked) await settleAfterSeek();\n return false;\n}\n\n/**\n * Whether the element is fed by a live stream rather than by a file.\n *\n * A truthiness check rather than `!== null`: the property is absent in\n * environments that model only part of `HTMLMediaElement`, where comparing\n * against `null` would read every element as live.\n *\n * @param video The element to test.\n * @returns `true` when a `MediaStream` is playing into it.\n */\nfunction isLiveStream(video: HTMLVideoElement): boolean {\n return Boolean(video.srcObject);\n}\n\n/**\n * The video's length in seconds, probing for it when the header lacks one.\n *\n * `MediaRecorder` does not guarantee a duration in the WebM it produces, so a\n * recording can report `Infinity` until something forces the browser to demux\n * to the end. That is what the seek past the end does — the same hack the\n * `AudioPlayer` uses, for the same reason: the value is not in the file.\n *\n * @param video The element to measure.\n * @param timeoutMs How long the probe may take.\n * @param signal Optional abort signal.\n * @returns The duration in seconds.\n * @throws {@link FrameSeekError} when the element is a live stream, or the\n * probe produced no duration.\n */\nasync function resolveDuration(\n video: HTMLVideoElement,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<number> {\n if (Number.isFinite(video.duration)) return video.duration;\n\n if (isLiveStream(video)) {\n throw new FrameSeekError(\n \"This element is playing a live MediaStream, which has no timeline to seek: \" +\n \"there is no instant but now. Leave `atMs` out to capture the frame on screen.\",\n );\n }\n\n const probed = onceEvent(\n video,\n \"timeupdate\",\n timeoutMs,\n signal,\n `The video reported no duration and the probe for it did not answer within ` +\n `${timeoutMs}ms, so there is no timeline to seek on.`,\n );\n video.currentTime = PAST_THE_END_SECONDS;\n await probed;\n\n if (!Number.isFinite(video.duration)) {\n throw new FrameSeekError(\n \"This video still reports no duration after seeking past its end, so it has \" +\n \"no seekable timeline — a live HLS or DASH source behaves this way. Leave \" +\n \"`atMs` out to capture the frame on screen.\",\n );\n }\n return video.duration;\n}\n\n/**\n * Move the video to an instant, and wait for that instant's frame.\n *\n * @param video The element to move.\n * @param atMs The instant, in milliseconds. Clamped to the duration.\n * @param timeoutMs How long the seek and the frame may take.\n * @param signal Optional abort signal.\n * @returns Whether a presented frame confirmed the new position.\n * @throws {@link FrameSeekError} when the video has no seekable timeline, or\n * the seek did not land in time.\n */\nasync function seekTo(\n video: HTMLVideoElement,\n atMs: number,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<boolean> {\n const duration = await resolveDuration(video, timeoutMs, signal);\n\n const target = Math.min(Math.max(atMs / 1000, 0), duration);\n if (Math.abs(video.currentTime - target) < SAME_FRAME_EPSILON) {\n return await awaitReadableFrame(video, false);\n }\n\n const seeked = onceEvent(\n video,\n \"seeked\",\n timeoutMs,\n signal,\n `The video did not finish seeking to ${target.toFixed(3)}s within ${timeoutMs}ms. ` +\n \"Nothing was captured: a frame from the wrong instant is indistinguishable \" +\n \"from a correct one downstream.\",\n );\n video.currentTime = target;\n await seeked;\n return await awaitReadableFrame(video, true);\n}\n\n/**\n * Re-throw a tainted-canvas failure saying what to fix.\n *\n * A cross-origin video without `crossOrigin` taints everything it is drawn\n * into, and the `SecurityError` then arrives from the encoder — one call away\n * from the element that caused it, wrapped in whatever the decode said.\n *\n * @param error What the imaging pipeline threw.\n * @returns The error to throw instead.\n */\nfunction explained(error: unknown): unknown {\n const names = [error, (error as { cause?: unknown } | null)?.cause].map((candidate) =>\n typeof candidate === \"object\" && candidate !== null && \"name\" in candidate\n ? String((candidate as { name: unknown }).name)\n : \"\",\n );\n if (!names.includes(\"SecurityError\")) return error;\n return new ImageDecodeError(\n \"The video is cross-origin, so reading its pixels is not allowed. Set \" +\n 'crossOrigin=\"anonymous\" on the element before the source loads, and serve the ' +\n \"video with a permissive Access-Control-Allow-Origin.\",\n { cause: error },\n );\n}\n\n/**\n * Put playback back where the capture found it, touching only what moved.\n *\n * Each half is guarded because writing `currentTime` **is** a seek, even when\n * the value is unchanged: a capture that was refused before it moved anything\n * — a live stream, an abort — would otherwise perturb the player it never\n * touched. And a capture that paused without needing to move still has to let\n * go of the pause.\n *\n * @param video The element to restore.\n * @param time Where `currentTime` was.\n * @param wasPlaying Whether it was playing before the capture paused it.\n */\nfunction restorePlayback(video: HTMLVideoElement, time: number, wasPlaying: boolean): void {\n if (video.currentTime !== time) video.currentTime = time;\n if (wasPlaying && video.paused) void Promise.resolve(video.play()).catch(() => undefined);\n}\n\n/**\n * Capture a frame from a video as encoded image bytes.\n *\n * Without `atMs` it reads the frame on screen, which is what a screen or camera\n * recording wants — a print of what is being recorded. With `atMs` it seeks,\n * waits for that instant's frame to be presented, captures, and puts the player\n * back.\n *\n * @example Print of a screen recording in progress\n * ```ts\n * const shot = await captureFrame(videoRef.current!, {\n * type: \"image/webp\",\n * quality: 0.9,\n * });\n * await shareOrDownloadBlob(shot.blob, \"print.webp\");\n * ```\n *\n * @example A poster from ten seconds in, scaled down\n * ```ts\n * const poster = await captureFrame(video, { atMs: 10_000, width: 640 });\n * setPosterUrl(URL.createObjectURL(poster.blob));\n * console.log(`landed on ${poster.atMs}ms`);\n * ```\n *\n * @param video The element to read. It needs data — the capture waits for\n * `loadeddata` when the element has none yet.\n * @param options Instant, output box, format, and how long to wait.\n * @returns The encoded frame, the instant it actually came from, and whether a\n * presented frame confirmed that instant.\n * @throws {@link FrameSeekError} when the video has no data or no timeline in\n * time, or the seek did not land.\n * @throws {@link ImageDecodeError} when the pixels cannot be read — a\n * cross-origin video without `crossOrigin` is the common one.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function captureFrame(\n video: HTMLVideoElement,\n options: CaptureFrameOptions = {},\n): Promise<CapturedFrame> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;\n if (options.signal?.aborted === true) throw abortError();\n\n if (video.readyState < HAVE_CURRENT_DATA) {\n await onceEvent(\n video,\n \"loadeddata\",\n timeoutMs,\n options.signal,\n `The video had no frame to read within ${timeoutMs}ms (readyState ` +\n `${video.readyState}). Give it a source, or wait for it yourself.`,\n );\n }\n\n const { atMs: requestedMs } = options;\n const previousTime = video.currentTime;\n const wasPlaying = !video.paused;\n\n try {\n let confirmed: boolean;\n if (requestedMs === undefined) {\n confirmed = await awaitReadableFrame(video, false);\n } else {\n if (wasPlaying) video.pause();\n confirmed = await seekTo(video, requestedMs, timeoutMs, options.signal);\n }\n\n const atMs = video.currentTime * 1000;\n const encoded = await resizeImage(video, options);\n return { ...encoded, atMs, confirmed };\n } catch (error) {\n throw explained(error);\n } finally {\n if (options.restore !== false) restorePlayback(video, previousTime, wasPlaying);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"thumbnails.cjs","names":[],"sources":["../../src/imaging/thumbnails.ts"],"sourcesContent":["/**\n * Producing several sizes from one decode.\n *\n * A gallery needs a grid thumbnail, a list avatar and a detail view. Doing\n * that with three `resizeImage` calls decodes the same photo three times,\n * which on a 12-megapixel picture is most of the cost — the decode dominates,\n * not the scaling.\n */\n\nimport { createSurface, drawScaled } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport { encodeImage } from \"./encode\";\nimport type { EncodeOptions, ImageSource, ProcessedImage } from \"./types\";\n\n/** One requested size. */\nexport interface ThumbnailSpec {\n /** Name to find it by in the result. */\n readonly name: string;\n /** Longest edge in pixels. */\n readonly size: number;\n /** Format and quality, overriding the shared options. */\n readonly encode?: EncodeOptions;\n}\n\n/** A produced thumbnail. */\nexport interface Thumbnail extends ProcessedImage {\n readonly name: string;\n}\n\n/**\n * Produce several sizes from a single decode.\n *\n * @example\n * ```ts\n * const [thumb, card] = await createThumbnails(file, [\n * { name: \"thumb\", size: 96 },\n * { name: \"card\", size: 480 },\n * ]);\n * ```\n *\n * Sizes are the **longest edge**, and the aspect ratio is kept, so a\n * portrait and a landscape photo both fit the same grid cell without a\n * separate calculation per orientation.\n *\n * @param source Anything decodable.\n * @param specs The sizes to produce.\n * @param options Shared format and quality.\n * @returns One result per spec, in the order requested.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function createThumbnails(\n source: ImageSource,\n specs: readonly ThumbnailSpec[],\n options: EncodeOptions = {},\n): Promise<Thumbnail[]> {\n const { bitmap } = await decodeImage(source);\n try {\n const results: Thumbnail[] = [];\n for (const spec of specs) {\n const scale = Math.min(1, spec.size / Math.max(bitmap.width, bitmap.height));\n const width = Math.max(1, Math.round(bitmap.width * scale));\n const height = Math.max(1, Math.round(bitmap.height * scale));\n\n const surface = createSurface(width, height);\n drawScaled(bitmap, surface, { x: 0, y: 0, width, height });\n const encoded = await encodeImage(surface, { ...options, ...spec.encode });\n results.push({ ...encoded, name: spec.name });\n }\n return results;\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":"oFAkDA,eAAsB,EAClB,EACA,EACA,EAAyB,CAAC,EACN,CACpB,GAAM,CAAE,UAAW,MAAM,EAAA,YAAY,CAAM,EAC3C,GAAI,CACA,IAAM,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAQ,EAAO,CACtB,IAAM,EAAQ,KAAK,IAAI,EAAG,EAAK,KAAO,KAAK,IAAI,EAAO,MAAO,EAAO,MAAM,CAAC,EACrE,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,MAAQ,CAAK,CAAC,EACpD,EAAS,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,OAAS,CAAK,CAAC,EAEtD,EAAU,EAAA,cAAc,EAAO,CAAM,EAC3C,EAAA,WAAW,EAAQ,EAAS,CAAE,EAAG,EAAG,EAAG,EAAG,QAAO,QAAO,CAAC,EACzD,IAAM,EAAU,MAAM,EAAA,YAAY,EAAS,CAAE,GAAG,EAAS,GAAG,EAAK,MAAO,CAAC,EACzE,EAAQ,KAAK,CAAE,GAAG,EAAS,KAAM,EAAK,IAAK,CAAC,CAChD,CACA,OAAO,CACX,QAAU,CACN,EAAO,QAAQ,CACnB,CACJ"}
1
+ {"version":3,"file":"thumbnails.cjs","names":["decodeImage","createSurface","encodeImage","require_canvas","require_decode","require_encode","createThumbnails","source","results","spec","specs","scale","bitmap","width","height","surface","encoded","options"],"sources":["../../src/imaging/thumbnails.ts"],"sourcesContent":["/**\n * Producing several sizes from one decode.\n *\n * A gallery needs a grid thumbnail, a list avatar and a detail view. Doing\n * that with three `resizeImage` calls decodes the same photo three times,\n * which on a 12-megapixel picture is most of the cost — the decode dominates,\n * not the scaling.\n */\n\nimport { createSurface, drawScaled } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport { encodeImage } from \"./encode\";\nimport type { EncodeOptions, ImageSource, ProcessedImage } from \"./types\";\n\n/** One requested size. */\nexport interface ThumbnailSpec {\n /** Name to find it by in the result. */\n readonly name: string;\n /** Longest edge in pixels. */\n readonly size: number;\n /** Format and quality, overriding the shared options. */\n readonly encode?: EncodeOptions;\n}\n\n/** A produced thumbnail. */\nexport interface Thumbnail extends ProcessedImage {\n readonly name: string;\n}\n\n/**\n * Produce several sizes from a single decode.\n *\n * @example\n * ```ts\n * const [thumb, card] = await createThumbnails(file, [\n * { name: \"thumb\", size: 96 },\n * { name: \"card\", size: 480 },\n * ]);\n * ```\n *\n * Sizes are the **longest edge**, and the aspect ratio is kept, so a\n * portrait and a landscape photo both fit the same grid cell without a\n * separate calculation per orientation.\n *\n * @param source Anything decodable.\n * @param specs The sizes to produce.\n * @param options Shared format and quality.\n * @returns One result per spec, in the order requested.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function createThumbnails(\n source: ImageSource,\n specs: readonly ThumbnailSpec[],\n options: EncodeOptions = {},\n): Promise<Thumbnail[]> {\n const { bitmap } = await decodeImage(source);\n try {\n const results: Thumbnail[] = [];\n for (const spec of specs) {\n const scale = Math.min(1, spec.size / Math.max(bitmap.width, bitmap.height));\n const width = Math.max(1, Math.round(bitmap.width * scale));\n const height = Math.max(1, Math.round(bitmap.height * scale));\n\n const surface = createSurface(width, height);\n drawScaled(bitmap, surface, { x: 0, y: 0, width, height });\n const encoded = await encodeImage(surface, { ...options, ...spec.encode });\n results.push({ ...encoded, name: spec.name });\n }\n return results;\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":"A,oFAkDA,eAAsBM,EAClB,EACA,EACA,EAAyB,CAAC,EACN,CACpB,GAAM,CAAE,UAAW,MAAMN,EAAAA,YAAYO,CAAM,EAC3C,GAAI,CACA,IAAMC,EAAuB,CAAC,EAC9B,IAAK,IAAMC,KAAQC,EAAO,CACtB,IAAMC,EAAQ,KAAK,IAAI,EAAGF,EAAK,KAAO,KAAK,IAAIG,EAAO,MAAOA,EAAO,MAAM,CAAC,EACrEC,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAMD,EAAO,MAAQD,CAAK,CAAC,EACpDG,EAAS,KAAK,IAAI,EAAG,KAAK,MAAMF,EAAO,OAASD,CAAK,CAAC,EAEtDI,EAAUd,EAAAA,cAAcY,EAAOC,CAAM,EAC3CX,EAAA,WAAWS,EAAQG,EAAS,CAAE,EAAG,EAAG,EAAG,EAAG,QAAO,QAAO,CAAC,EACzD,IAAMC,EAAU,MAAMd,EAAAA,YAAYa,EAAS,CAAE,GAAGE,EAAS,GAAGR,EAAK,MAAO,CAAC,EACzED,EAAQ,KAAK,CAAE,GAAGQ,EAAS,KAAMP,EAAK,IAAK,CAAC,CAChD,CACA,OAAOD,CACX,QAAU,CACNI,EAAO,QAAQ,CACnB,CACJ,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"thumbnails.js","names":[],"sources":["../../src/imaging/thumbnails.ts"],"sourcesContent":["/**\n * Producing several sizes from one decode.\n *\n * A gallery needs a grid thumbnail, a list avatar and a detail view. Doing\n * that with three `resizeImage` calls decodes the same photo three times,\n * which on a 12-megapixel picture is most of the cost — the decode dominates,\n * not the scaling.\n */\n\nimport { createSurface, drawScaled } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport { encodeImage } from \"./encode\";\nimport type { EncodeOptions, ImageSource, ProcessedImage } from \"./types\";\n\n/** One requested size. */\nexport interface ThumbnailSpec {\n /** Name to find it by in the result. */\n readonly name: string;\n /** Longest edge in pixels. */\n readonly size: number;\n /** Format and quality, overriding the shared options. */\n readonly encode?: EncodeOptions;\n}\n\n/** A produced thumbnail. */\nexport interface Thumbnail extends ProcessedImage {\n readonly name: string;\n}\n\n/**\n * Produce several sizes from a single decode.\n *\n * @example\n * ```ts\n * const [thumb, card] = await createThumbnails(file, [\n * { name: \"thumb\", size: 96 },\n * { name: \"card\", size: 480 },\n * ]);\n * ```\n *\n * Sizes are the **longest edge**, and the aspect ratio is kept, so a\n * portrait and a landscape photo both fit the same grid cell without a\n * separate calculation per orientation.\n *\n * @param source Anything decodable.\n * @param specs The sizes to produce.\n * @param options Shared format and quality.\n * @returns One result per spec, in the order requested.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function createThumbnails(\n source: ImageSource,\n specs: readonly ThumbnailSpec[],\n options: EncodeOptions = {},\n): Promise<Thumbnail[]> {\n const { bitmap } = await decodeImage(source);\n try {\n const results: Thumbnail[] = [];\n for (const spec of specs) {\n const scale = Math.min(1, spec.size / Math.max(bitmap.width, bitmap.height));\n const width = Math.max(1, Math.round(bitmap.width * scale));\n const height = Math.max(1, Math.round(bitmap.height * scale));\n\n const surface = createSurface(width, height);\n drawScaled(bitmap, surface, { x: 0, y: 0, width, height });\n const encoded = await encodeImage(surface, { ...options, ...spec.encode });\n results.push({ ...encoded, name: spec.name });\n }\n return results;\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":";;;;AAkDA,eAAsB,EAClB,GACA,GACA,IAAyB,CAAC,GACN;CACpB,IAAM,EAAE,cAAW,MAAM,EAAY,CAAM;CAC3C,IAAI;EACA,IAAM,IAAuB,CAAC;EAC9B,KAAK,IAAM,KAAQ,GAAO;GACtB,IAAM,IAAQ,KAAK,IAAI,GAAG,EAAK,OAAO,KAAK,IAAI,EAAO,OAAO,EAAO,MAAM,CAAC,GACrE,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,QAAQ,CAAK,CAAC,GACpD,IAAS,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,SAAS,CAAK,CAAC,GAEtD,IAAU,EAAc,GAAO,CAAM;GAC3C,EAAW,GAAQ,GAAS;IAAE,GAAG;IAAG,GAAG;IAAG;IAAO;GAAO,CAAC;GACzD,IAAM,IAAU,MAAM,EAAY,GAAS;IAAE,GAAG;IAAS,GAAG,EAAK;GAAO,CAAC;GACzE,EAAQ,KAAK;IAAE,GAAG;IAAS,MAAM,EAAK;GAAK,CAAC;EAChD;EACA,OAAO;CACX,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ"}
1
+ {"version":3,"file":"thumbnails.js","names":["createThumbnails","decodeImage","source","results","spec","specs","scale","bitmap","width","height","surface","createSurface","encoded","encodeImage","options"],"sources":["../../src/imaging/thumbnails.ts"],"sourcesContent":["/**\n * Producing several sizes from one decode.\n *\n * A gallery needs a grid thumbnail, a list avatar and a detail view. Doing\n * that with three `resizeImage` calls decodes the same photo three times,\n * which on a 12-megapixel picture is most of the cost — the decode dominates,\n * not the scaling.\n */\n\nimport { createSurface, drawScaled } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport { encodeImage } from \"./encode\";\nimport type { EncodeOptions, ImageSource, ProcessedImage } from \"./types\";\n\n/** One requested size. */\nexport interface ThumbnailSpec {\n /** Name to find it by in the result. */\n readonly name: string;\n /** Longest edge in pixels. */\n readonly size: number;\n /** Format and quality, overriding the shared options. */\n readonly encode?: EncodeOptions;\n}\n\n/** A produced thumbnail. */\nexport interface Thumbnail extends ProcessedImage {\n readonly name: string;\n}\n\n/**\n * Produce several sizes from a single decode.\n *\n * @example\n * ```ts\n * const [thumb, card] = await createThumbnails(file, [\n * { name: \"thumb\", size: 96 },\n * { name: \"card\", size: 480 },\n * ]);\n * ```\n *\n * Sizes are the **longest edge**, and the aspect ratio is kept, so a\n * portrait and a landscape photo both fit the same grid cell without a\n * separate calculation per orientation.\n *\n * @param source Anything decodable.\n * @param specs The sizes to produce.\n * @param options Shared format and quality.\n * @returns One result per spec, in the order requested.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function createThumbnails(\n source: ImageSource,\n specs: readonly ThumbnailSpec[],\n options: EncodeOptions = {},\n): Promise<Thumbnail[]> {\n const { bitmap } = await decodeImage(source);\n try {\n const results: Thumbnail[] = [];\n for (const spec of specs) {\n const scale = Math.min(1, spec.size / Math.max(bitmap.width, bitmap.height));\n const width = Math.max(1, Math.round(bitmap.width * scale));\n const height = Math.max(1, Math.round(bitmap.height * scale));\n\n const surface = createSurface(width, height);\n drawScaled(bitmap, surface, { x: 0, y: 0, width, height });\n const encoded = await encodeImage(surface, { ...options, ...spec.encode });\n results.push({ ...encoded, name: spec.name });\n }\n return results;\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":";;;;AAkDA,eAAsBA,EAClB,GACA,GACA,IAAyB,CAAC,GACN;CACpB,IAAM,EAAE,cAAW,MAAMC,EAAYC,CAAM;CAC3C,IAAI;EACA,IAAMC,IAAuB,CAAC;EAC9B,KAAK,IAAMC,KAAQC,GAAO;GACtB,IAAMC,IAAQ,KAAK,IAAI,GAAGF,EAAK,OAAO,KAAK,IAAIG,EAAO,OAAOA,EAAO,MAAM,CAAC,GACrEC,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAMD,EAAO,QAAQD,CAAK,CAAC,GACpDG,IAAS,KAAK,IAAI,GAAG,KAAK,MAAMF,EAAO,SAASD,CAAK,CAAC,GAEtDI,IAAUC,EAAcH,GAAOC,CAAM;GAC3C,EAAWF,GAAQG,GAAS;IAAE,GAAG;IAAG,GAAG;IAAG;IAAO;GAAO,CAAC;GACzD,IAAME,IAAU,MAAMC,EAAYH,GAAS;IAAE,GAAGI;IAAS,GAAGV,EAAK;GAAO,CAAC;GACzE,EAAQ,KAAK;IAAE,GAAGQ;IAAS,MAAMR,EAAK;GAAK,CAAC;EAChD;EACA,OAAOD;CACX,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform.cjs","names":[],"sources":["../../src/imaging/transform.ts"],"sourcesContent":["/**\n * Resize, crop, rotate — the operations a PWA runs before an upload.\n *\n * Every one of them ends in a re-encode, which has a consequence worth\n * knowing: **the output carries no EXIF.** Location, camera serial and\n * timestamp do not survive a canvas round trip. For an app handling user\n * photos that is usually the point, and it is the reason `resizeImage` is\n * also the privacy step, not only the bandwidth step.\n */\n\nimport { createSurface, drawScaled, getContext } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport type {\n CropRect,\n EncodeOptions,\n ImageSource,\n ProcessedImage,\n ResizeFit,\n ResizeOptions,\n} from \"./types\";\nimport { encodeImage } from \"./encode\";\n\n/** Background used when a format cannot carry transparency. */\nexport const DEFAULT_BACKGROUND = \"#ffffff\";\n\n/** Formats that have no alpha channel. */\nconst OPAQUE_TYPES = new Set([\"image/jpeg\", \"image/jpg\"]);\n\n/**\n * Compute the drawing geometry for a fit mode.\n *\n * @param source Source dimensions.\n * @param box Requested box; a missing side is derived from the aspect ratio.\n * @param fit How to fit the box.\n * @param withoutEnlargement Never scale up.\n * @returns Surface size and where the image lands inside it.\n */\nfunction layout(\n source: { width: number; height: number },\n box: { width?: number; height?: number },\n fit: ResizeFit,\n withoutEnlargement: boolean,\n): {\n surface: { width: number; height: number };\n draw: { x: number; y: number; width: number; height: number };\n} {\n const ratio = source.width / source.height;\n let targetWidth = box.width ?? (box.height !== undefined ? box.height * ratio : source.width);\n let targetHeight = box.height ?? (box.width !== undefined ? box.width / ratio : source.height);\n\n if (withoutEnlargement) {\n const scale = Math.min(1, source.width / targetWidth, source.height / targetHeight);\n targetWidth *= scale;\n targetHeight *= scale;\n }\n\n targetWidth = Math.max(1, Math.round(targetWidth));\n targetHeight = Math.max(1, Math.round(targetHeight));\n\n if (fit === \"fill\") {\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: { x: 0, y: 0, width: targetWidth, height: targetHeight },\n };\n }\n\n const scale =\n fit === \"cover\"\n ? Math.max(targetWidth / source.width, targetHeight / source.height)\n : Math.min(targetWidth / source.width, targetHeight / source.height);\n\n const drawWidth = Math.max(1, Math.round(source.width * scale));\n const drawHeight = Math.max(1, Math.round(source.height * scale));\n\n if (fit === \"contain\") {\n return {\n surface: { width: drawWidth, height: drawHeight },\n draw: { x: 0, y: 0, width: drawWidth, height: drawHeight },\n };\n }\n\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: {\n x: Math.round((targetWidth - drawWidth) / 2),\n y: Math.round((targetHeight - drawHeight) / 2),\n width: drawWidth,\n height: drawHeight,\n },\n };\n}\n\n/**\n * Resolve the background to paint before drawing.\n *\n * @param options The caller's options.\n * @returns A colour, or `undefined` to keep transparency.\n */\nfunction backgroundFor(options: ResizeOptions): string | undefined {\n if (options.background !== undefined) return options.background;\n const type = options.type ?? \"image/jpeg\";\n if (OPAQUE_TYPES.has(type)) return DEFAULT_BACKGROUND;\n return options.fit === \"pad\" ? DEFAULT_BACKGROUND : undefined;\n}\n\n/**\n * Resize an image, re-encoding it.\n *\n * @example\n * ```ts\n * const resized = await resizeImage(file, { width: 1600, type: \"image/webp\" });\n * console.log(resized.width, resized.bytes, resized.type);\n * ```\n *\n * @param source Anything decodable.\n * @param options Target box, fit, format and quality.\n * @returns The encoded result.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function resizeImage(\n source: ImageSource,\n options: ResizeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const geometry = layout(\n bitmap,\n { width: options.width, height: options.height },\n options.fit ?? \"contain\",\n options.withoutEnlargement !== false,\n );\n const surface = createSurface(geometry.surface.width, geometry.surface.height);\n drawScaled(bitmap, surface, geometry.draw, backgroundFor(options));\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Crop a rectangle out of an image, in source pixels.\n *\n * The rectangle is clamped to the image, so a crop dragged past the edge\n * produces a smaller result instead of transparent padding.\n *\n * @example\n * ```ts\n * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });\n * ```\n *\n * @param source Anything decodable.\n * @param rect The region to keep.\n * @param options Format and quality.\n * @returns The encoded crop.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function cropImage(\n source: ImageSource,\n rect: CropRect,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const x = Math.max(0, Math.min(Math.round(rect.x), bitmap.width - 1));\n const y = Math.max(0, Math.min(Math.round(rect.y), bitmap.height - 1));\n const width = Math.max(1, Math.min(Math.round(rect.width), bitmap.width - x));\n const height = Math.max(1, Math.min(Math.round(rect.height), bitmap.height - y));\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.drawImage(bitmap, x, y, width, height, 0, 0, width, height);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Rotate an image by a multiple of 90 degrees.\n *\n * Restricted to right angles on purpose: an arbitrary angle needs a\n * decision about the corners (crop, pad, or grow the canvas) that belongs\n * to the caller's design, not to a utility default.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(file, 90);\n * ```\n *\n * @param source Anything decodable.\n * @param degrees `90`, `180`, `270` — or any multiple, normalised.\n * @param options Format and quality.\n * @returns The rotated image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link RangeError} when the angle is not a multiple of 90.\n */\nexport async function rotateImage(\n source: ImageSource,\n degrees: number,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n if (degrees % 90 !== 0) {\n throw new RangeError(\n `rotateImage takes multiples of 90 degrees; got ${degrees}. For an ` +\n \"arbitrary angle, draw it yourself: the corner handling is a design \" +\n \"decision, not a default.\",\n );\n }\n const turns = (((degrees / 90) % 4) + 4) % 4;\n const { bitmap } = await decodeImage(source);\n try {\n const swapped = turns % 2 === 1;\n const width = swapped ? bitmap.height : bitmap.width;\n const height = swapped ? bitmap.width : bitmap.height;\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(width / 2, height / 2);\n context.rotate((turns * Math.PI) / 2);\n context.drawImage(bitmap, -bitmap.width / 2, -bitmap.height / 2);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Mirror an image horizontally, vertically, or both.\n *\n * @example\n * ```ts\n * const selfie = await flipImage(capture, { horizontal: true });\n * ```\n *\n * @param source Anything decodable.\n * @param axes Which axes to mirror.\n * @param options Format and quality.\n * @returns The flipped image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function flipImage(\n source: ImageSource,\n axes: { horizontal?: boolean; vertical?: boolean },\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const surface = createSurface(bitmap.width, bitmap.height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(\n axes.horizontal === true ? bitmap.width : 0,\n axes.vertical === true ? bitmap.height : 0,\n );\n context.scale(axes.horizontal === true ? -1 : 1, axes.vertical === true ? -1 : 1);\n context.drawImage(bitmap, 0, 0);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transform.cjs","names":["decodeImage","createSurface","encodeImage","getContext","require_canvas","require_decode","require_encode","DEFAULT_BACKGROUND","OPAQUE_TYPES","layout","ratio","source","targetWidth","box","targetHeight","withoutEnlargement","scale","fit","drawWidth","drawHeight","backgroundFor","options","type","resizeImage","geometry","bitmap","surface","cropImage","x","rect","y","width","height","rotateImage","degrees","turns","swapped","context","flipImage","axes"],"sources":["../../src/imaging/transform.ts"],"sourcesContent":["/**\n * Resize, crop, rotate — the operations a PWA runs before an upload.\n *\n * Every one of them ends in a re-encode, which has a consequence worth\n * knowing: **the output carries no EXIF.** Location, camera serial and\n * timestamp do not survive a canvas round trip. For an app handling user\n * photos that is usually the point, and it is the reason `resizeImage` is\n * also the privacy step, not only the bandwidth step.\n */\n\nimport { createSurface, drawScaled, getContext } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport type {\n CropRect,\n EncodeOptions,\n ImageSource,\n ProcessedImage,\n ResizeFit,\n ResizeOptions,\n} from \"./types\";\nimport { encodeImage } from \"./encode\";\n\n/** Background used when a format cannot carry transparency. */\nexport const DEFAULT_BACKGROUND = \"#ffffff\";\n\n/** Formats that have no alpha channel. */\nconst OPAQUE_TYPES = new Set([\"image/jpeg\", \"image/jpg\"]);\n\n/**\n * Compute the drawing geometry for a fit mode.\n *\n * @param source Source dimensions.\n * @param box Requested box; a missing side is derived from the aspect ratio.\n * @param fit How to fit the box.\n * @param withoutEnlargement Never scale up.\n * @returns Surface size and where the image lands inside it.\n */\nfunction layout(\n source: { width: number; height: number },\n box: { width?: number; height?: number },\n fit: ResizeFit,\n withoutEnlargement: boolean,\n): {\n surface: { width: number; height: number };\n draw: { x: number; y: number; width: number; height: number };\n} {\n const ratio = source.width / source.height;\n let targetWidth = box.width ?? (box.height !== undefined ? box.height * ratio : source.width);\n let targetHeight = box.height ?? (box.width !== undefined ? box.width / ratio : source.height);\n\n if (withoutEnlargement) {\n const scale = Math.min(1, source.width / targetWidth, source.height / targetHeight);\n targetWidth *= scale;\n targetHeight *= scale;\n }\n\n targetWidth = Math.max(1, Math.round(targetWidth));\n targetHeight = Math.max(1, Math.round(targetHeight));\n\n if (fit === \"fill\") {\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: { x: 0, y: 0, width: targetWidth, height: targetHeight },\n };\n }\n\n const scale =\n fit === \"cover\"\n ? Math.max(targetWidth / source.width, targetHeight / source.height)\n : Math.min(targetWidth / source.width, targetHeight / source.height);\n\n const drawWidth = Math.max(1, Math.round(source.width * scale));\n const drawHeight = Math.max(1, Math.round(source.height * scale));\n\n if (fit === \"contain\") {\n return {\n surface: { width: drawWidth, height: drawHeight },\n draw: { x: 0, y: 0, width: drawWidth, height: drawHeight },\n };\n }\n\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: {\n x: Math.round((targetWidth - drawWidth) / 2),\n y: Math.round((targetHeight - drawHeight) / 2),\n width: drawWidth,\n height: drawHeight,\n },\n };\n}\n\n/**\n * Resolve the background to paint before drawing.\n *\n * @param options The caller's options.\n * @returns A colour, or `undefined` to keep transparency.\n */\nfunction backgroundFor(options: ResizeOptions): string | undefined {\n if (options.background !== undefined) return options.background;\n const type = options.type ?? \"image/jpeg\";\n if (OPAQUE_TYPES.has(type)) return DEFAULT_BACKGROUND;\n return options.fit === \"pad\" ? DEFAULT_BACKGROUND : undefined;\n}\n\n/**\n * Resize an image, re-encoding it.\n *\n * @example\n * ```ts\n * const resized = await resizeImage(file, { width: 1600, type: \"image/webp\" });\n * console.log(resized.width, resized.bytes, resized.type);\n * ```\n *\n * @param source Anything decodable.\n * @param options Target box, fit, format and quality.\n * @returns The encoded result.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function resizeImage(\n source: ImageSource,\n options: ResizeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const geometry = layout(\n bitmap,\n { width: options.width, height: options.height },\n options.fit ?? \"contain\",\n options.withoutEnlargement !== false,\n );\n const surface = createSurface(geometry.surface.width, geometry.surface.height);\n drawScaled(bitmap, surface, geometry.draw, backgroundFor(options));\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Crop a rectangle out of an image, in source pixels.\n *\n * The rectangle is clamped to the image, so a crop dragged past the edge\n * produces a smaller result instead of transparent padding.\n *\n * @example\n * ```ts\n * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });\n * ```\n *\n * @param source Anything decodable.\n * @param rect The region to keep.\n * @param options Format and quality.\n * @returns The encoded crop.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function cropImage(\n source: ImageSource,\n rect: CropRect,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const x = Math.max(0, Math.min(Math.round(rect.x), bitmap.width - 1));\n const y = Math.max(0, Math.min(Math.round(rect.y), bitmap.height - 1));\n const width = Math.max(1, Math.min(Math.round(rect.width), bitmap.width - x));\n const height = Math.max(1, Math.min(Math.round(rect.height), bitmap.height - y));\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.drawImage(bitmap, x, y, width, height, 0, 0, width, height);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Rotate an image by a multiple of 90 degrees.\n *\n * Restricted to right angles on purpose: an arbitrary angle needs a\n * decision about the corners (crop, pad, or grow the canvas) that belongs\n * to the caller's design, not to a utility default.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(file, 90);\n * ```\n *\n * @param source Anything decodable.\n * @param degrees `90`, `180`, `270` — or any multiple, normalised.\n * @param options Format and quality.\n * @returns The rotated image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link RangeError} when the angle is not a multiple of 90.\n */\nexport async function rotateImage(\n source: ImageSource,\n degrees: number,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n if (degrees % 90 !== 0) {\n throw new RangeError(\n `rotateImage takes multiples of 90 degrees; got ${degrees}. For an ` +\n \"arbitrary angle, draw it yourself: the corner handling is a design \" +\n \"decision, not a default.\",\n );\n }\n const turns = (((degrees / 90) % 4) + 4) % 4;\n const { bitmap } = await decodeImage(source);\n try {\n const swapped = turns % 2 === 1;\n const width = swapped ? bitmap.height : bitmap.width;\n const height = swapped ? bitmap.width : bitmap.height;\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(width / 2, height / 2);\n context.rotate((turns * Math.PI) / 2);\n context.drawImage(bitmap, -bitmap.width / 2, -bitmap.height / 2);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Mirror an image horizontally, vertically, or both.\n *\n * @example\n * ```ts\n * const selfie = await flipImage(capture, { horizontal: true });\n * ```\n *\n * @param source Anything decodable.\n * @param axes Which axes to mirror.\n * @param options Format and quality.\n * @returns The flipped image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function flipImage(\n source: ImageSource,\n axes: { horizontal?: boolean; vertical?: boolean },\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const surface = createSurface(bitmap.width, bitmap.height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(\n axes.horizontal === true ? bitmap.width : 0,\n axes.vertical === true ? bitmap.height : 0,\n );\n context.scale(axes.horizontal === true ? -1 : 1, axes.vertical === true ? -1 : 1);\n context.drawImage(bitmap, 0, 0);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","names":[],"sources":["../../src/imaging/transform.ts"],"sourcesContent":["/**\n * Resize, crop, rotate — the operations a PWA runs before an upload.\n *\n * Every one of them ends in a re-encode, which has a consequence worth\n * knowing: **the output carries no EXIF.** Location, camera serial and\n * timestamp do not survive a canvas round trip. For an app handling user\n * photos that is usually the point, and it is the reason `resizeImage` is\n * also the privacy step, not only the bandwidth step.\n */\n\nimport { createSurface, drawScaled, getContext } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport type {\n CropRect,\n EncodeOptions,\n ImageSource,\n ProcessedImage,\n ResizeFit,\n ResizeOptions,\n} from \"./types\";\nimport { encodeImage } from \"./encode\";\n\n/** Background used when a format cannot carry transparency. */\nexport const DEFAULT_BACKGROUND = \"#ffffff\";\n\n/** Formats that have no alpha channel. */\nconst OPAQUE_TYPES = new Set([\"image/jpeg\", \"image/jpg\"]);\n\n/**\n * Compute the drawing geometry for a fit mode.\n *\n * @param source Source dimensions.\n * @param box Requested box; a missing side is derived from the aspect ratio.\n * @param fit How to fit the box.\n * @param withoutEnlargement Never scale up.\n * @returns Surface size and where the image lands inside it.\n */\nfunction layout(\n source: { width: number; height: number },\n box: { width?: number; height?: number },\n fit: ResizeFit,\n withoutEnlargement: boolean,\n): {\n surface: { width: number; height: number };\n draw: { x: number; y: number; width: number; height: number };\n} {\n const ratio = source.width / source.height;\n let targetWidth = box.width ?? (box.height !== undefined ? box.height * ratio : source.width);\n let targetHeight = box.height ?? (box.width !== undefined ? box.width / ratio : source.height);\n\n if (withoutEnlargement) {\n const scale = Math.min(1, source.width / targetWidth, source.height / targetHeight);\n targetWidth *= scale;\n targetHeight *= scale;\n }\n\n targetWidth = Math.max(1, Math.round(targetWidth));\n targetHeight = Math.max(1, Math.round(targetHeight));\n\n if (fit === \"fill\") {\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: { x: 0, y: 0, width: targetWidth, height: targetHeight },\n };\n }\n\n const scale =\n fit === \"cover\"\n ? Math.max(targetWidth / source.width, targetHeight / source.height)\n : Math.min(targetWidth / source.width, targetHeight / source.height);\n\n const drawWidth = Math.max(1, Math.round(source.width * scale));\n const drawHeight = Math.max(1, Math.round(source.height * scale));\n\n if (fit === \"contain\") {\n return {\n surface: { width: drawWidth, height: drawHeight },\n draw: { x: 0, y: 0, width: drawWidth, height: drawHeight },\n };\n }\n\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: {\n x: Math.round((targetWidth - drawWidth) / 2),\n y: Math.round((targetHeight - drawHeight) / 2),\n width: drawWidth,\n height: drawHeight,\n },\n };\n}\n\n/**\n * Resolve the background to paint before drawing.\n *\n * @param options The caller's options.\n * @returns A colour, or `undefined` to keep transparency.\n */\nfunction backgroundFor(options: ResizeOptions): string | undefined {\n if (options.background !== undefined) return options.background;\n const type = options.type ?? \"image/jpeg\";\n if (OPAQUE_TYPES.has(type)) return DEFAULT_BACKGROUND;\n return options.fit === \"pad\" ? DEFAULT_BACKGROUND : undefined;\n}\n\n/**\n * Resize an image, re-encoding it.\n *\n * @example\n * ```ts\n * const resized = await resizeImage(file, { width: 1600, type: \"image/webp\" });\n * console.log(resized.width, resized.bytes, resized.type);\n * ```\n *\n * @param source Anything decodable.\n * @param options Target box, fit, format and quality.\n * @returns The encoded result.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function resizeImage(\n source: ImageSource,\n options: ResizeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const geometry = layout(\n bitmap,\n { width: options.width, height: options.height },\n options.fit ?? \"contain\",\n options.withoutEnlargement !== false,\n );\n const surface = createSurface(geometry.surface.width, geometry.surface.height);\n drawScaled(bitmap, surface, geometry.draw, backgroundFor(options));\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Crop a rectangle out of an image, in source pixels.\n *\n * The rectangle is clamped to the image, so a crop dragged past the edge\n * produces a smaller result instead of transparent padding.\n *\n * @example\n * ```ts\n * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });\n * ```\n *\n * @param source Anything decodable.\n * @param rect The region to keep.\n * @param options Format and quality.\n * @returns The encoded crop.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function cropImage(\n source: ImageSource,\n rect: CropRect,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const x = Math.max(0, Math.min(Math.round(rect.x), bitmap.width - 1));\n const y = Math.max(0, Math.min(Math.round(rect.y), bitmap.height - 1));\n const width = Math.max(1, Math.min(Math.round(rect.width), bitmap.width - x));\n const height = Math.max(1, Math.min(Math.round(rect.height), bitmap.height - y));\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.drawImage(bitmap, x, y, width, height, 0, 0, width, height);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Rotate an image by a multiple of 90 degrees.\n *\n * Restricted to right angles on purpose: an arbitrary angle needs a\n * decision about the corners (crop, pad, or grow the canvas) that belongs\n * to the caller's design, not to a utility default.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(file, 90);\n * ```\n *\n * @param source Anything decodable.\n * @param degrees `90`, `180`, `270` — or any multiple, normalised.\n * @param options Format and quality.\n * @returns The rotated image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link RangeError} when the angle is not a multiple of 90.\n */\nexport async function rotateImage(\n source: ImageSource,\n degrees: number,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n if (degrees % 90 !== 0) {\n throw new RangeError(\n `rotateImage takes multiples of 90 degrees; got ${degrees}. For an ` +\n \"arbitrary angle, draw it yourself: the corner handling is a design \" +\n \"decision, not a default.\",\n );\n }\n const turns = (((degrees / 90) % 4) + 4) % 4;\n const { bitmap } = await decodeImage(source);\n try {\n const swapped = turns % 2 === 1;\n const width = swapped ? bitmap.height : bitmap.width;\n const height = swapped ? bitmap.width : bitmap.height;\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(width / 2, height / 2);\n context.rotate((turns * Math.PI) / 2);\n context.drawImage(bitmap, -bitmap.width / 2, -bitmap.height / 2);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Mirror an image horizontally, vertically, or both.\n *\n * @example\n * ```ts\n * const selfie = await flipImage(capture, { horizontal: true });\n * ```\n *\n * @param source Anything decodable.\n * @param axes Which axes to mirror.\n * @param options Format and quality.\n * @returns The flipped image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function flipImage(\n source: ImageSource,\n axes: { horizontal?: boolean; vertical?: boolean },\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const surface = createSurface(bitmap.width, bitmap.height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(\n axes.horizontal === true ? bitmap.width : 0,\n axes.vertical === true ? bitmap.height : 0,\n );\n context.scale(axes.horizontal === true ? -1 : 1, axes.vertical === true ? -1 : 1);\n context.drawImage(bitmap, 0, 0);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":";;;;AAuBA,IAAa,IAAqB,WAG5B,oBAAe,IAAI,IAAI,CAAC,cAAc,WAAW,CAAC;AAWxD,SAAS,EACL,GACA,GACA,GACA,GAIF;CACE,IAAM,IAAQ,EAAO,QAAQ,EAAO,QAChC,IAAc,EAAI,UAAU,EAAI,WAAW,KAAA,IAAiC,EAAO,QAA5B,EAAI,SAAS,IACpE,IAAe,EAAI,WAAW,EAAI,UAAU,KAAA,IAAgC,EAAO,SAA3B,EAAI,QAAQ;CAExE,IAAI,GAAoB;EACpB,IAAM,IAAQ,KAAK,IAAI,GAAG,EAAO,QAAQ,GAAa,EAAO,SAAS,CAAY;EAElF,AADA,KAAe,GACf,KAAgB;CACpB;CAKA,IAHA,IAAc,KAAK,IAAI,GAAG,KAAK,MAAM,CAAW,CAAC,GACjD,IAAe,KAAK,IAAI,GAAG,KAAK,MAAM,CAAY,CAAC,GAE/C,MAAQ,QACR,OAAO;EACH,SAAS;GAAE,OAAO;GAAa,QAAQ;EAAa;EACpD,MAAM;GAAE,GAAG;GAAG,GAAG;GAAG,OAAO;GAAa,QAAQ;EAAa;CACjE;CAGJ,IAAM,IACF,MAAQ,UACF,KAAK,IAAI,IAAc,EAAO,OAAO,IAAe,EAAO,MAAM,IACjE,KAAK,IAAI,IAAc,EAAO,OAAO,IAAe,EAAO,MAAM,GAErE,IAAY,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,QAAQ,CAAK,CAAC,GACxD,IAAa,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,SAAS,CAAK,CAAC;CAShE,OAPI,MAAQ,YACD;EACH,SAAS;GAAE,OAAO;GAAW,QAAQ;EAAW;EAChD,MAAM;GAAE,GAAG;GAAG,GAAG;GAAG,OAAO;GAAW,QAAQ;EAAW;CAC7D,IAGG;EACH,SAAS;GAAE,OAAO;GAAa,QAAQ;EAAa;EACpD,MAAM;GACF,GAAG,KAAK,OAAO,IAAc,KAAa,CAAC;GAC3C,GAAG,KAAK,OAAO,IAAe,KAAc,CAAC;GAC7C,OAAO;GACP,QAAQ;EACZ;CACJ;AACJ;AAQA,SAAS,EAAc,GAA4C;CAC/D,IAAI,EAAQ,eAAe,KAAA,GAAW,OAAO,EAAQ;CACrD,IAAM,IAAO,EAAQ,QAAQ;CAE7B,OADI,EAAa,IAAI,CAAI,KAClB,EAAQ,QAAQ,QADY,IACiB,KAAA;AACxD;AAiBA,eAAsB,EAClB,GACA,IAAyB,CAAC,GACH;CACvB,IAAM,EAAE,cAAW,MAAM,EAAY,CAAM;CAC3C,IAAI;EACA,IAAM,IAAW,EACb,GACA;GAAE,OAAO,EAAQ;GAAO,QAAQ,EAAQ;EAAO,GAC/C,EAAQ,OAAO,WACf,EAAQ,uBAAuB,EACnC,GACM,IAAU,EAAc,EAAS,QAAQ,OAAO,EAAS,QAAQ,MAAM;EAE7E,OADA,EAAW,GAAQ,GAAS,EAAS,MAAM,EAAc,CAAO,CAAC,GAC1D,MAAM,EAAY,GAAS,CAAO;CAC7C,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ;AAmBA,eAAsB,EAClB,GACA,GACA,IAAyB,CAAC,GACH;CACvB,IAAM,EAAE,cAAW,MAAM,EAAY,CAAM;CAC3C,IAAI;EACA,IAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,EAAK,CAAC,GAAG,EAAO,QAAQ,CAAC,CAAC,GAC9D,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,EAAK,CAAC,GAAG,EAAO,SAAS,CAAC,CAAC,GAC/D,IAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,EAAK,KAAK,GAAG,EAAO,QAAQ,CAAC,CAAC,GACtE,IAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,EAAK,MAAM,GAAG,EAAO,SAAS,CAAC,CAAC,GAEzE,IAAU,EAAc,GAAO,CAAM;EAM3C,OADA,EAHI,GACA,EAAa,IAAI,EAAQ,QAAQ,YAAY,IAAI,IAAqB,KAAA,CAE1E,CAAA,CAAQ,UAAU,GAAQ,GAAG,GAAG,GAAO,GAAQ,GAAG,GAAG,GAAO,CAAM,GAC3D,MAAM,EAAY,GAAS,CAAO;CAC7C,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ;AAqBA,eAAsB,EAClB,GACA,GACA,IAAyB,CAAC,GACH;CACvB,IAAI,IAAU,MAAO,GACjB,MAAU,WACN,kDAAkD,EAAQ,qGAG9D;CAEJ,IAAM,KAAW,IAAU,KAAM,IAAK,KAAK,GACrC,EAAE,cAAW,MAAM,EAAY,CAAM;CAC3C,IAAI;EACA,IAAM,IAAU,IAAQ,KAAM,GACxB,IAAQ,IAAU,EAAO,SAAS,EAAO,OACzC,IAAS,IAAU,EAAO,QAAQ,EAAO,QAEzC,IAAU,EAAc,GAAO,CAAM,GACrC,IAAU,EACZ,GACA,EAAa,IAAI,EAAQ,QAAQ,YAAY,IAAI,IAAqB,KAAA,CAC1E;EAIA,OAHA,EAAQ,UAAU,IAAQ,GAAG,IAAS,CAAC,GACvC,EAAQ,OAAQ,IAAQ,KAAK,KAAM,CAAC,GACpC,EAAQ,UAAU,GAAQ,CAAC,EAAO,QAAQ,GAAG,CAAC,EAAO,SAAS,CAAC,GACxD,MAAM,EAAY,GAAS,CAAO;CAC7C,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ;AAgBA,eAAsB,EAClB,GACA,GACA,IAAyB,CAAC,GACH;CACvB,IAAM,EAAE,cAAW,MAAM,EAAY,CAAM;CAC3C,IAAI;EACA,IAAM,IAAU,EAAc,EAAO,OAAO,EAAO,MAAM,GACnD,IAAU,EACZ,GACA,EAAa,IAAI,EAAQ,QAAQ,YAAY,IAAI,IAAqB,KAAA,CAC1E;EAOA,OANA,EAAQ,UACJ,EAAK,eAAe,KAAO,EAAO,QAAQ,GAC1C,EAAK,aAAa,KAAO,EAAO,SAAS,CAC7C,GACA,EAAQ,MAAM,EAAK,eAAe,KAAO,KAAK,GAAG,EAAK,aAAa,KAAO,KAAK,CAAC,GAChF,EAAQ,UAAU,GAAQ,GAAG,CAAC,GACvB,MAAM,EAAY,GAAS,CAAO;CAC7C,UAAU;EACN,EAAO,QAAQ;CACnB;AACJ"}
1
+ {"version":3,"file":"transform.js","names":["DEFAULT_BACKGROUND","OPAQUE_TYPES","layout","ratio","source","targetWidth","box","targetHeight","withoutEnlargement","scale","fit","drawWidth","drawHeight","backgroundFor","options","type","resizeImage","decodeImage","geometry","bitmap","surface","createSurface","drawScaled","encodeImage","cropImage","x","rect","y","width","height","getContext","rotateImage","degrees","turns","swapped","context","flipImage","axes"],"sources":["../../src/imaging/transform.ts"],"sourcesContent":["/**\n * Resize, crop, rotate — the operations a PWA runs before an upload.\n *\n * Every one of them ends in a re-encode, which has a consequence worth\n * knowing: **the output carries no EXIF.** Location, camera serial and\n * timestamp do not survive a canvas round trip. For an app handling user\n * photos that is usually the point, and it is the reason `resizeImage` is\n * also the privacy step, not only the bandwidth step.\n */\n\nimport { createSurface, drawScaled, getContext } from \"./canvas\";\nimport { decodeImage } from \"./decode\";\nimport type {\n CropRect,\n EncodeOptions,\n ImageSource,\n ProcessedImage,\n ResizeFit,\n ResizeOptions,\n} from \"./types\";\nimport { encodeImage } from \"./encode\";\n\n/** Background used when a format cannot carry transparency. */\nexport const DEFAULT_BACKGROUND = \"#ffffff\";\n\n/** Formats that have no alpha channel. */\nconst OPAQUE_TYPES = new Set([\"image/jpeg\", \"image/jpg\"]);\n\n/**\n * Compute the drawing geometry for a fit mode.\n *\n * @param source Source dimensions.\n * @param box Requested box; a missing side is derived from the aspect ratio.\n * @param fit How to fit the box.\n * @param withoutEnlargement Never scale up.\n * @returns Surface size and where the image lands inside it.\n */\nfunction layout(\n source: { width: number; height: number },\n box: { width?: number; height?: number },\n fit: ResizeFit,\n withoutEnlargement: boolean,\n): {\n surface: { width: number; height: number };\n draw: { x: number; y: number; width: number; height: number };\n} {\n const ratio = source.width / source.height;\n let targetWidth = box.width ?? (box.height !== undefined ? box.height * ratio : source.width);\n let targetHeight = box.height ?? (box.width !== undefined ? box.width / ratio : source.height);\n\n if (withoutEnlargement) {\n const scale = Math.min(1, source.width / targetWidth, source.height / targetHeight);\n targetWidth *= scale;\n targetHeight *= scale;\n }\n\n targetWidth = Math.max(1, Math.round(targetWidth));\n targetHeight = Math.max(1, Math.round(targetHeight));\n\n if (fit === \"fill\") {\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: { x: 0, y: 0, width: targetWidth, height: targetHeight },\n };\n }\n\n const scale =\n fit === \"cover\"\n ? Math.max(targetWidth / source.width, targetHeight / source.height)\n : Math.min(targetWidth / source.width, targetHeight / source.height);\n\n const drawWidth = Math.max(1, Math.round(source.width * scale));\n const drawHeight = Math.max(1, Math.round(source.height * scale));\n\n if (fit === \"contain\") {\n return {\n surface: { width: drawWidth, height: drawHeight },\n draw: { x: 0, y: 0, width: drawWidth, height: drawHeight },\n };\n }\n\n return {\n surface: { width: targetWidth, height: targetHeight },\n draw: {\n x: Math.round((targetWidth - drawWidth) / 2),\n y: Math.round((targetHeight - drawHeight) / 2),\n width: drawWidth,\n height: drawHeight,\n },\n };\n}\n\n/**\n * Resolve the background to paint before drawing.\n *\n * @param options The caller's options.\n * @returns A colour, or `undefined` to keep transparency.\n */\nfunction backgroundFor(options: ResizeOptions): string | undefined {\n if (options.background !== undefined) return options.background;\n const type = options.type ?? \"image/jpeg\";\n if (OPAQUE_TYPES.has(type)) return DEFAULT_BACKGROUND;\n return options.fit === \"pad\" ? DEFAULT_BACKGROUND : undefined;\n}\n\n/**\n * Resize an image, re-encoding it.\n *\n * @example\n * ```ts\n * const resized = await resizeImage(file, { width: 1600, type: \"image/webp\" });\n * console.log(resized.width, resized.bytes, resized.type);\n * ```\n *\n * @param source Anything decodable.\n * @param options Target box, fit, format and quality.\n * @returns The encoded result.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link ImageEncodeError} when the canvas produces no bytes.\n */\nexport async function resizeImage(\n source: ImageSource,\n options: ResizeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const geometry = layout(\n bitmap,\n { width: options.width, height: options.height },\n options.fit ?? \"contain\",\n options.withoutEnlargement !== false,\n );\n const surface = createSurface(geometry.surface.width, geometry.surface.height);\n drawScaled(bitmap, surface, geometry.draw, backgroundFor(options));\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Crop a rectangle out of an image, in source pixels.\n *\n * The rectangle is clamped to the image, so a crop dragged past the edge\n * produces a smaller result instead of transparent padding.\n *\n * @example\n * ```ts\n * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });\n * ```\n *\n * @param source Anything decodable.\n * @param rect The region to keep.\n * @param options Format and quality.\n * @returns The encoded crop.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function cropImage(\n source: ImageSource,\n rect: CropRect,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const x = Math.max(0, Math.min(Math.round(rect.x), bitmap.width - 1));\n const y = Math.max(0, Math.min(Math.round(rect.y), bitmap.height - 1));\n const width = Math.max(1, Math.min(Math.round(rect.width), bitmap.width - x));\n const height = Math.max(1, Math.min(Math.round(rect.height), bitmap.height - y));\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.drawImage(bitmap, x, y, width, height, 0, 0, width, height);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Rotate an image by a multiple of 90 degrees.\n *\n * Restricted to right angles on purpose: an arbitrary angle needs a\n * decision about the corners (crop, pad, or grow the canvas) that belongs\n * to the caller's design, not to a utility default.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(file, 90);\n * ```\n *\n * @param source Anything decodable.\n * @param degrees `90`, `180`, `270` — or any multiple, normalised.\n * @param options Format and quality.\n * @returns The rotated image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n * @throws {@link RangeError} when the angle is not a multiple of 90.\n */\nexport async function rotateImage(\n source: ImageSource,\n degrees: number,\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n if (degrees % 90 !== 0) {\n throw new RangeError(\n `rotateImage takes multiples of 90 degrees; got ${degrees}. For an ` +\n \"arbitrary angle, draw it yourself: the corner handling is a design \" +\n \"decision, not a default.\",\n );\n }\n const turns = (((degrees / 90) % 4) + 4) % 4;\n const { bitmap } = await decodeImage(source);\n try {\n const swapped = turns % 2 === 1;\n const width = swapped ? bitmap.height : bitmap.width;\n const height = swapped ? bitmap.width : bitmap.height;\n\n const surface = createSurface(width, height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(width / 2, height / 2);\n context.rotate((turns * Math.PI) / 2);\n context.drawImage(bitmap, -bitmap.width / 2, -bitmap.height / 2);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n\n/**\n * Mirror an image horizontally, vertically, or both.\n *\n * @example\n * ```ts\n * const selfie = await flipImage(capture, { horizontal: true });\n * ```\n *\n * @param source Anything decodable.\n * @param axes Which axes to mirror.\n * @param options Format and quality.\n * @returns The flipped image.\n * @throws {@link ImageDecodeError} when the source cannot be decoded.\n */\nexport async function flipImage(\n source: ImageSource,\n axes: { horizontal?: boolean; vertical?: boolean },\n options: EncodeOptions = {},\n): Promise<ProcessedImage> {\n const { bitmap } = await decodeImage(source);\n try {\n const surface = createSurface(bitmap.width, bitmap.height);\n const context = getContext(\n surface,\n OPAQUE_TYPES.has(options.type ?? \"image/jpeg\") ? DEFAULT_BACKGROUND : undefined,\n );\n context.translate(\n axes.horizontal === true ? bitmap.width : 0,\n axes.vertical === true ? bitmap.height : 0,\n );\n context.scale(axes.horizontal === true ? -1 : 1, axes.vertical === true ? -1 : 1);\n context.drawImage(bitmap, 0, 0);\n return await encodeImage(surface, options);\n } finally {\n bitmap.close?.();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-image-processing.cjs","names":[],"sources":["../../src/imaging/use-image-processing.ts"],"sourcesContent":["/**\n * React bindings for image work.\n *\n * Two things components get wrong every time: they leak object URLs (an\n * `URL.createObjectURL` without its `revokeObjectURL` holds the whole blob\n * in memory for the page's lifetime), and they set state after unmounting\n * when a slow resize finishes late. Both live here instead of in each\n * component.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { compressToTarget, type CompressedImage } from \"./compress\";\nimport type { CompressOptions, ImageSource, ProcessedImage, ResizeOptions } from \"./types\";\nimport { resizeImage } from \"./transform\";\n\n/** What {@link useImagePreview} returns. */\nexport interface UseImagePreviewResult {\n /** Object URL for the current source, or `null`. */\n readonly url: string | null;\n}\n\n/**\n * Hold an object URL for a blob, and revoke it when it is replaced.\n *\n * @example\n * ```tsx\n * function Preview({ file }: { file: File | null }) {\n * const { url } = useImagePreview(file);\n * return url === null ? null : <img src={url} alt=\"\" />;\n * }\n * ```\n *\n * @param source The blob to preview, or `null`.\n * @returns The object URL, valid until the source changes or the component\n * unmounts.\n */\nexport function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (source === null || source === undefined) {\n setUrl(null);\n return;\n }\n const created = URL.createObjectURL(source);\n setUrl(created);\n return () => {\n URL.revokeObjectURL(created);\n };\n }, [source]);\n\n return { url };\n}\n\n/** Lifecycle of a processing call. */\nexport type ImageProcessingStatus = \"idle\" | \"working\" | \"done\" | \"error\";\n\n/** What {@link useImageProcessing} returns. */\nexport interface UseImageProcessingResult {\n /** Resize (and re-encode) an image. */\n readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;\n /** Compress an image into a byte budget. */\n readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;\n /** The most recent result. */\n readonly result: ProcessedImage | null;\n /** Where the last call is. */\n readonly status: ImageProcessingStatus;\n /** Why the last call failed. */\n readonly error: Error | null;\n /** Whether a call is in flight. */\n readonly isWorking: boolean;\n}\n\n/**\n * Run image operations with status tracking, safe against unmount.\n *\n * @example\n * ```tsx\n * function Upload() {\n * const { compress, isWorking, result } = useImageProcessing();\n *\n * async function onPick(file: File) {\n * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });\n * await fetch(\"/api/photos\", { method: \"POST\", body: ready.blob });\n * }\n *\n * return <input type=\"file\" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;\n * }\n * ```\n *\n * The returned promises still reject on failure, so a caller can `try`\n * around them; `status` and `error` exist for rendering, not for swallowing\n * the failure.\n *\n * @returns The operations plus their state.\n */\nexport function useImageProcessing(): UseImageProcessingResult {\n const [result, setResult] = useState<ProcessedImage | null>(null);\n const [status, setStatus] = useState<ImageProcessingStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const run = useCallback(\n async <T extends ProcessedImage>(work: () => Promise<T>): Promise<T> => {\n if (mounted.current) {\n setStatus(\"working\");\n setError(null);\n }\n try {\n const produced = await work();\n if (mounted.current) {\n setResult(produced);\n setStatus(\"done\");\n }\n return produced;\n } catch (caught) {\n const failure = caught instanceof Error ? caught : new Error(String(caught));\n if (mounted.current) {\n setError(failure);\n setStatus(\"error\");\n }\n throw failure;\n }\n },\n [],\n );\n\n const resize = useCallback(\n (source: ImageSource, options: ResizeOptions = {}) =>\n run(() => resizeImage(source, options)),\n [run],\n );\n\n const compress = useCallback(\n (source: ImageSource, options: CompressOptions) =>\n run(() => compressToTarget(source, options)),\n [run],\n );\n\n return {\n resize,\n compress,\n result,\n status,\n error,\n isWorking: status === \"working\",\n };\n}\n"],"mappings":"sFAqCA,SAAgB,EAAgB,EAAwD,CACpF,GAAM,CAAC,EAAK,IAAA,EAAU,EAAA,SAAA,CAAwB,IAAI,EAclD,OAZA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,GAAW,KAA8B,CACzC,EAAO,IAAI,EACX,MACJ,CACA,IAAM,EAAU,IAAI,gBAAgB,CAAM,EAE1C,OADA,EAAO,CAAO,MACD,CACT,IAAI,gBAAgB,CAAO,CAC/B,CACJ,EAAG,CAAC,CAAM,CAAC,EAEJ,CAAE,KAAI,CACjB,CA4CA,SAAgB,GAA+C,CAC3D,GAAM,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAAgC,IAAI,EAC1D,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAAgC,MAAM,EAC5D,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAuB,IAAI,EAC/C,GAAA,EAAU,EAAA,OAAA,CAAO,EAAI,GAE3B,EAAA,EAAA,UAAA,MACI,EAAQ,QAAU,OACL,CACT,EAAQ,QAAU,EACtB,GACD,CAAC,CAAC,EAEL,IAAM,GAAA,EAAM,EAAA,YAAA,CACR,KAAiC,IAAuC,CAChE,EAAQ,UACR,EAAU,SAAS,EACnB,EAAS,IAAI,GAEjB,GAAI,CACA,IAAM,EAAW,MAAM,EAAK,EAK5B,OAJI,EAAQ,UACR,EAAU,CAAQ,EAClB,EAAU,MAAM,GAEb,CACX,OAAS,EAAQ,CACb,IAAM,EAAU,aAAkB,MAAQ,EAAa,MAAM,OAAO,CAAM,CAAC,EAK3E,MAJI,EAAQ,UACR,EAAS,CAAO,EAChB,EAAU,OAAO,GAEf,CACV,CACJ,EACA,CAAC,CACL,EAcA,MAAO,CACH,QAAA,EAbW,EAAA,YAAA,EACV,EAAqB,EAAyB,CAAC,IAC5C,MAAU,EAAA,YAAY,EAAQ,CAAO,CAAC,EAC1C,CAAC,CAAG,CAUJ,EACA,UAAA,EARa,EAAA,YAAA,EACZ,EAAqB,IAClB,MAAU,EAAA,iBAAiB,EAAQ,CAAO,CAAC,EAC/C,CAAC,CAAG,CAKJ,EACA,SACA,SACA,QACA,UAAW,IAAW,SAC1B,CACJ"}
1
+ {"version":3,"file":"use-image-processing.cjs","names":["useState","useRef","useCallback","resizeImage","compressToTarget","require_transform","require_compress","react","useImagePreview","url","setUrl","source","created","useImageProcessing","result","setResult","status","setStatus","error","setError","mounted","run","produced","work","caught","failure","options"],"sources":["../../src/imaging/use-image-processing.ts"],"sourcesContent":["/**\n * React bindings for image work.\n *\n * Two things components get wrong every time: they leak object URLs (an\n * `URL.createObjectURL` without its `revokeObjectURL` holds the whole blob\n * in memory for the page's lifetime), and they set state after unmounting\n * when a slow resize finishes late. Both live here instead of in each\n * component.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { compressToTarget, type CompressedImage } from \"./compress\";\nimport type { CompressOptions, ImageSource, ProcessedImage, ResizeOptions } from \"./types\";\nimport { resizeImage } from \"./transform\";\n\n/** What {@link useImagePreview} returns. */\nexport interface UseImagePreviewResult {\n /** Object URL for the current source, or `null`. */\n readonly url: string | null;\n}\n\n/**\n * Hold an object URL for a blob, and revoke it when it is replaced.\n *\n * @example\n * ```tsx\n * function Preview({ file }: { file: File | null }) {\n * const { url } = useImagePreview(file);\n * return url === null ? null : <img src={url} alt=\"\" />;\n * }\n * ```\n *\n * @param source The blob to preview, or `null`.\n * @returns The object URL, valid until the source changes or the component\n * unmounts.\n */\nexport function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (source === null || source === undefined) {\n setUrl(null);\n return;\n }\n const created = URL.createObjectURL(source);\n setUrl(created);\n return () => {\n URL.revokeObjectURL(created);\n };\n }, [source]);\n\n return { url };\n}\n\n/** Lifecycle of a processing call. */\nexport type ImageProcessingStatus = \"idle\" | \"working\" | \"done\" | \"error\";\n\n/** What {@link useImageProcessing} returns. */\nexport interface UseImageProcessingResult {\n /** Resize (and re-encode) an image. */\n readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;\n /** Compress an image into a byte budget. */\n readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;\n /** The most recent result. */\n readonly result: ProcessedImage | null;\n /** Where the last call is. */\n readonly status: ImageProcessingStatus;\n /** Why the last call failed. */\n readonly error: Error | null;\n /** Whether a call is in flight. */\n readonly isWorking: boolean;\n}\n\n/**\n * Run image operations with status tracking, safe against unmount.\n *\n * @example\n * ```tsx\n * function Upload() {\n * const { compress, isWorking, result } = useImageProcessing();\n *\n * async function onPick(file: File) {\n * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });\n * await fetch(\"/api/photos\", { method: \"POST\", body: ready.blob });\n * }\n *\n * return <input type=\"file\" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;\n * }\n * ```\n *\n * The returned promises still reject on failure, so a caller can `try`\n * around them; `status` and `error` exist for rendering, not for swallowing\n * the failure.\n *\n * @returns The operations plus their state.\n */\nexport function useImageProcessing(): UseImageProcessingResult {\n const [result, setResult] = useState<ProcessedImage | null>(null);\n const [status, setStatus] = useState<ImageProcessingStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const run = useCallback(\n async <T extends ProcessedImage>(work: () => Promise<T>): Promise<T> => {\n if (mounted.current) {\n setStatus(\"working\");\n setError(null);\n }\n try {\n const produced = await work();\n if (mounted.current) {\n setResult(produced);\n setStatus(\"done\");\n }\n return produced;\n } catch (caught) {\n const failure = caught instanceof Error ? caught : new Error(String(caught));\n if (mounted.current) {\n setError(failure);\n setStatus(\"error\");\n }\n throw failure;\n }\n },\n [],\n );\n\n const resize = useCallback(\n (source: ImageSource, options: ResizeOptions = {}) =>\n run(() => resizeImage(source, options)),\n [run],\n );\n\n const compress = useCallback(\n (source: ImageSource, options: CompressOptions) =>\n run(() => compressToTarget(source, options)),\n [run],\n );\n\n return {\n resize,\n compress,\n result,\n status,\n error,\n isWorking: status === \"working\",\n };\n}\n"],"mappings":"A,sFAqCA,SAAgBQ,EAAgB,EAAwD,CACpF,GAAM,CAACC,EAAKC,IAAA,EAAUV,EAAAA,SAAAA,CAAwB,IAAI,EAclD,OAZA,EAAAO,EAAA,UAAA,KAAgB,CACZ,GAAII,GAAW,KAA8B,CACzCD,EAAO,IAAI,EACX,MACJ,CACA,IAAME,EAAU,IAAI,gBAAgBD,CAAM,EAE1C,OADAD,EAAOE,CAAO,MACD,CACT,IAAI,gBAAgBA,CAAO,CAC/B,CACJ,EAAG,CAACD,CAAM,CAAC,EAEJ,CAAE,KAAI,CACjB,CA4CA,SAAgBE,GAA+C,CAC3D,GAAM,CAACC,EAAQC,IAAA,EAAaf,EAAAA,SAAAA,CAAgC,IAAI,EAC1D,CAACgB,EAAQC,IAAA,EAAajB,EAAAA,SAAAA,CAAgC,MAAM,EAC5D,CAACkB,EAAOC,IAAA,EAAYnB,EAAAA,SAAAA,CAAuB,IAAI,EAC/CoB,GAAA,EAAUnB,EAAAA,OAAAA,CAAO,EAAI,GAE3B,EAAAM,EAAA,UAAA,MACI,EAAQ,QAAU,OACL,CACT,EAAQ,QAAU,EACtB,GACD,CAAC,CAAC,EAEL,IAAMc,GAAA,EAAMnB,EAAAA,YAAAA,CACR,KAAiC,IAAuC,CAChEkB,EAAQ,UACRH,EAAU,SAAS,EACnBE,EAAS,IAAI,GAEjB,GAAI,CACA,IAAMG,EAAW,MAAMC,EAAK,EAK5B,OAJIH,EAAQ,UACRL,EAAUO,CAAQ,EAClBL,EAAU,MAAM,GAEbK,CACX,OAASE,EAAQ,CACb,IAAMC,EAAUD,aAAkB,MAAQA,EAAa,MAAM,OAAOA,CAAM,CAAC,EAK3E,MAJIJ,EAAQ,UACRD,EAASM,CAAO,EAChBR,EAAU,OAAO,GAEfQ,CACV,CACJ,EACA,CAAC,CACL,EAcA,MAAO,CACH,QAAA,EAbWvB,EAAAA,YAAAA,EACV,EAAqB,EAAyB,CAAC,IAC5CmB,MAAUlB,EAAAA,YAAYQ,EAAQe,CAAO,CAAC,EAC1C,CAACL,CAAG,CAUJ,EACA,UAAA,EARanB,EAAAA,YAAAA,EACZ,EAAqB,IAClBmB,MAAUjB,EAAAA,iBAAiBO,EAAQe,CAAO,CAAC,EAC/C,CAACL,CAAG,CAKJ,EACA,SACA,SACA,QACA,UAAWL,IAAW,SAC1B,CACJ,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-image-processing.js","names":[],"sources":["../../src/imaging/use-image-processing.ts"],"sourcesContent":["/**\n * React bindings for image work.\n *\n * Two things components get wrong every time: they leak object URLs (an\n * `URL.createObjectURL` without its `revokeObjectURL` holds the whole blob\n * in memory for the page's lifetime), and they set state after unmounting\n * when a slow resize finishes late. Both live here instead of in each\n * component.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { compressToTarget, type CompressedImage } from \"./compress\";\nimport type { CompressOptions, ImageSource, ProcessedImage, ResizeOptions } from \"./types\";\nimport { resizeImage } from \"./transform\";\n\n/** What {@link useImagePreview} returns. */\nexport interface UseImagePreviewResult {\n /** Object URL for the current source, or `null`. */\n readonly url: string | null;\n}\n\n/**\n * Hold an object URL for a blob, and revoke it when it is replaced.\n *\n * @example\n * ```tsx\n * function Preview({ file }: { file: File | null }) {\n * const { url } = useImagePreview(file);\n * return url === null ? null : <img src={url} alt=\"\" />;\n * }\n * ```\n *\n * @param source The blob to preview, or `null`.\n * @returns The object URL, valid until the source changes or the component\n * unmounts.\n */\nexport function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (source === null || source === undefined) {\n setUrl(null);\n return;\n }\n const created = URL.createObjectURL(source);\n setUrl(created);\n return () => {\n URL.revokeObjectURL(created);\n };\n }, [source]);\n\n return { url };\n}\n\n/** Lifecycle of a processing call. */\nexport type ImageProcessingStatus = \"idle\" | \"working\" | \"done\" | \"error\";\n\n/** What {@link useImageProcessing} returns. */\nexport interface UseImageProcessingResult {\n /** Resize (and re-encode) an image. */\n readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;\n /** Compress an image into a byte budget. */\n readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;\n /** The most recent result. */\n readonly result: ProcessedImage | null;\n /** Where the last call is. */\n readonly status: ImageProcessingStatus;\n /** Why the last call failed. */\n readonly error: Error | null;\n /** Whether a call is in flight. */\n readonly isWorking: boolean;\n}\n\n/**\n * Run image operations with status tracking, safe against unmount.\n *\n * @example\n * ```tsx\n * function Upload() {\n * const { compress, isWorking, result } = useImageProcessing();\n *\n * async function onPick(file: File) {\n * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });\n * await fetch(\"/api/photos\", { method: \"POST\", body: ready.blob });\n * }\n *\n * return <input type=\"file\" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;\n * }\n * ```\n *\n * The returned promises still reject on failure, so a caller can `try`\n * around them; `status` and `error` exist for rendering, not for swallowing\n * the failure.\n *\n * @returns The operations plus their state.\n */\nexport function useImageProcessing(): UseImageProcessingResult {\n const [result, setResult] = useState<ProcessedImage | null>(null);\n const [status, setStatus] = useState<ImageProcessingStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const run = useCallback(\n async <T extends ProcessedImage>(work: () => Promise<T>): Promise<T> => {\n if (mounted.current) {\n setStatus(\"working\");\n setError(null);\n }\n try {\n const produced = await work();\n if (mounted.current) {\n setResult(produced);\n setStatus(\"done\");\n }\n return produced;\n } catch (caught) {\n const failure = caught instanceof Error ? caught : new Error(String(caught));\n if (mounted.current) {\n setError(failure);\n setStatus(\"error\");\n }\n throw failure;\n }\n },\n [],\n );\n\n const resize = useCallback(\n (source: ImageSource, options: ResizeOptions = {}) =>\n run(() => resizeImage(source, options)),\n [run],\n );\n\n const compress = useCallback(\n (source: ImageSource, options: CompressOptions) =>\n run(() => compressToTarget(source, options)),\n [run],\n );\n\n return {\n resize,\n compress,\n result,\n status,\n error,\n isWorking: status === \"working\",\n };\n}\n"],"mappings":";;;;AAqCA,SAAgB,EAAgB,GAAwD;CACpF,IAAM,CAAC,GAAK,KAAU,EAAwB,IAAI;CAclD,OAZA,QAAgB;EACZ,IAAI,KAAW,MAA8B;GACzC,EAAO,IAAI;GACX;EACJ;EACA,IAAM,IAAU,IAAI,gBAAgB,CAAM;EAE1C,OADA,EAAO,CAAO,SACD;GACT,IAAI,gBAAgB,CAAO;EAC/B;CACJ,GAAG,CAAC,CAAM,CAAC,GAEJ,EAAE,OAAI;AACjB;AA4CA,SAAgB,IAA+C;CAC3D,IAAM,CAAC,GAAQ,KAAa,EAAgC,IAAI,GAC1D,CAAC,GAAQ,KAAa,EAAgC,MAAM,GAC5D,CAAC,GAAO,KAAY,EAAuB,IAAI,GAC/C,IAAU,EAAO,EAAI;CAE3B,SACI,EAAQ,UAAU,UACL;EACT,EAAQ,UAAU;CACtB,IACD,CAAC,CAAC;CAEL,IAAM,IAAM,EACR,OAAiC,MAAuC;EACpE,AAAI,EAAQ,YACR,EAAU,SAAS,GACnB,EAAS,IAAI;EAEjB,IAAI;GACA,IAAM,IAAW,MAAM,EAAK;GAK5B,OAJI,EAAQ,YACR,EAAU,CAAQ,GAClB,EAAU,MAAM,IAEb;EACX,SAAS,GAAQ;GACb,IAAM,IAAU,aAAkB,QAAQ,IAAa,MAAM,OAAO,CAAM,CAAC;GAK3E,MAJI,EAAQ,YACR,EAAS,CAAO,GAChB,EAAU,OAAO,IAEf;EACV;CACJ,GACA,CAAC,CACL;CAcA,OAAO;EACH,QAbW,GACV,GAAqB,IAAyB,CAAC,MAC5C,QAAU,EAAY,GAAQ,CAAO,CAAC,GAC1C,CAAC,CAAG,CAUJ;EACA,UARa,GACZ,GAAqB,MAClB,QAAU,EAAiB,GAAQ,CAAO,CAAC,GAC/C,CAAC,CAAG,CAKJ;EACA;EACA;EACA;EACA,WAAW,MAAW;CAC1B;AACJ"}
1
+ {"version":3,"file":"use-image-processing.js","names":["useImagePreview","url","setUrl","useState","useEffect","source","created","useImageProcessing","result","setResult","status","setStatus","error","setError","mounted","useRef","run","useCallback","produced","work","caught","failure","resizeImage","options","compressToTarget"],"sources":["../../src/imaging/use-image-processing.ts"],"sourcesContent":["/**\n * React bindings for image work.\n *\n * Two things components get wrong every time: they leak object URLs (an\n * `URL.createObjectURL` without its `revokeObjectURL` holds the whole blob\n * in memory for the page's lifetime), and they set state after unmounting\n * when a slow resize finishes late. Both live here instead of in each\n * component.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { compressToTarget, type CompressedImage } from \"./compress\";\nimport type { CompressOptions, ImageSource, ProcessedImage, ResizeOptions } from \"./types\";\nimport { resizeImage } from \"./transform\";\n\n/** What {@link useImagePreview} returns. */\nexport interface UseImagePreviewResult {\n /** Object URL for the current source, or `null`. */\n readonly url: string | null;\n}\n\n/**\n * Hold an object URL for a blob, and revoke it when it is replaced.\n *\n * @example\n * ```tsx\n * function Preview({ file }: { file: File | null }) {\n * const { url } = useImagePreview(file);\n * return url === null ? null : <img src={url} alt=\"\" />;\n * }\n * ```\n *\n * @param source The blob to preview, or `null`.\n * @returns The object URL, valid until the source changes or the component\n * unmounts.\n */\nexport function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (source === null || source === undefined) {\n setUrl(null);\n return;\n }\n const created = URL.createObjectURL(source);\n setUrl(created);\n return () => {\n URL.revokeObjectURL(created);\n };\n }, [source]);\n\n return { url };\n}\n\n/** Lifecycle of a processing call. */\nexport type ImageProcessingStatus = \"idle\" | \"working\" | \"done\" | \"error\";\n\n/** What {@link useImageProcessing} returns. */\nexport interface UseImageProcessingResult {\n /** Resize (and re-encode) an image. */\n readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;\n /** Compress an image into a byte budget. */\n readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;\n /** The most recent result. */\n readonly result: ProcessedImage | null;\n /** Where the last call is. */\n readonly status: ImageProcessingStatus;\n /** Why the last call failed. */\n readonly error: Error | null;\n /** Whether a call is in flight. */\n readonly isWorking: boolean;\n}\n\n/**\n * Run image operations with status tracking, safe against unmount.\n *\n * @example\n * ```tsx\n * function Upload() {\n * const { compress, isWorking, result } = useImageProcessing();\n *\n * async function onPick(file: File) {\n * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });\n * await fetch(\"/api/photos\", { method: \"POST\", body: ready.blob });\n * }\n *\n * return <input type=\"file\" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;\n * }\n * ```\n *\n * The returned promises still reject on failure, so a caller can `try`\n * around them; `status` and `error` exist for rendering, not for swallowing\n * the failure.\n *\n * @returns The operations plus their state.\n */\nexport function useImageProcessing(): UseImageProcessingResult {\n const [result, setResult] = useState<ProcessedImage | null>(null);\n const [status, setStatus] = useState<ImageProcessingStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const run = useCallback(\n async <T extends ProcessedImage>(work: () => Promise<T>): Promise<T> => {\n if (mounted.current) {\n setStatus(\"working\");\n setError(null);\n }\n try {\n const produced = await work();\n if (mounted.current) {\n setResult(produced);\n setStatus(\"done\");\n }\n return produced;\n } catch (caught) {\n const failure = caught instanceof Error ? caught : new Error(String(caught));\n if (mounted.current) {\n setError(failure);\n setStatus(\"error\");\n }\n throw failure;\n }\n },\n [],\n );\n\n const resize = useCallback(\n (source: ImageSource, options: ResizeOptions = {}) =>\n run(() => resizeImage(source, options)),\n [run],\n );\n\n const compress = useCallback(\n (source: ImageSource, options: CompressOptions) =>\n run(() => compressToTarget(source, options)),\n [run],\n );\n\n return {\n resize,\n compress,\n result,\n status,\n error,\n isWorking: status === \"working\",\n };\n}\n"],"mappings":";;;;AAqCA,SAAgBA,EAAgB,GAAwD;CACpF,IAAM,CAACC,GAAKC,KAAUC,EAAwB,IAAI;CAclD,OAZAC,QAAgB;EACZ,IAAIC,KAAW,MAA8B;GACzC,EAAO,IAAI;GACX;EACJ;EACA,IAAMC,IAAU,IAAI,gBAAgBD,CAAM;EAE1C,OADAH,EAAOI,CAAO,SACD;GACT,IAAI,gBAAgBA,CAAO;EAC/B;CACJ,GAAG,CAACD,CAAM,CAAC,GAEJ,EAAE,OAAI;AACjB;AA4CA,SAAgBE,IAA+C;CAC3D,IAAM,CAACC,GAAQC,KAAaN,EAAgC,IAAI,GAC1D,CAACO,GAAQC,KAAaR,EAAgC,MAAM,GAC5D,CAACS,GAAOC,KAAYV,EAAuB,IAAI,GAC/CW,IAAUC,EAAO,EAAI;CAE3B,SACI,EAAQ,UAAU,UACL;EACT,EAAQ,UAAU;CACtB,IACD,CAAC,CAAC;CAEL,IAAMC,IAAMC,EACR,OAAiC,MAAuC;EACpE,AAAIH,EAAQ,YACRH,EAAU,SAAS,GACnBE,EAAS,IAAI;EAEjB,IAAI;GACA,IAAMK,IAAW,MAAMC,EAAK;GAK5B,OAJIL,EAAQ,YACRL,EAAUS,CAAQ,GAClBP,EAAU,MAAM,IAEbO;EACX,SAASE,GAAQ;GACb,IAAMC,IAAUD,aAAkB,QAAQA,IAAa,MAAM,OAAOA,CAAM,CAAC;GAK3E,MAJIN,EAAQ,YACRD,EAASQ,CAAO,GAChBV,EAAU,OAAO,IAEfU;EACV;CACJ,GACA,CAAC,CACL;CAcA,OAAO;EACH,QAbWJ,GACV,GAAqB,IAAyB,CAAC,MAC5CD,QAAUM,EAAYjB,GAAQkB,CAAO,CAAC,GAC1C,CAACP,CAAG,CAUJ;EACA,UARaC,GACZ,GAAqB,MAClBD,QAAUQ,EAAiBnB,GAAQkB,CAAO,CAAC,GAC/C,CAACP,CAAG,CAKJ;EACA;EACA;EACA;EACA,WAAWN,MAAW;CAC1B;AACJ"}