tempest-react-sdk 0.38.3 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (519) hide show
  1. package/README.md +1 -1
  2. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  3. package/dist/access/access-control-context.cjs.map +1 -1
  4. package/dist/access/use-can.cjs.map +1 -1
  5. package/dist/app/AppProviders.cjs.map +1 -1
  6. package/dist/app/AppProviders.js.map +1 -1
  7. package/dist/audio/use-audio-recorder.cjs.map +1 -1
  8. package/dist/audio/use-audio.cjs.map +1 -1
  9. package/dist/audio/use-microphone.cjs.map +1 -1
  10. package/dist/auth/create-auth-store.cjs.map +1 -1
  11. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  12. package/dist/auth/use-passkey.cjs.map +1 -1
  13. package/dist/br/BrazilMap.cjs +1 -1
  14. package/dist/br/BrazilMap.cjs.map +1 -1
  15. package/dist/br/BrazilMap.js +2 -2
  16. package/dist/br/BrazilMap.js.map +1 -1
  17. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  18. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  19. package/dist/br/BrazilStateMap.cjs +1 -1
  20. package/dist/br/BrazilStateMap.cjs.map +1 -1
  21. package/dist/br/BrazilStateMap.js +2 -2
  22. package/dist/br/BrazilStateMap.js.map +1 -1
  23. package/dist/br/MapLegend.cjs.map +1 -1
  24. package/dist/br/MapLegend.js.map +1 -1
  25. package/dist/br/MapMarkers.cjs.map +1 -1
  26. package/dist/br/MapMarkers.js.map +1 -1
  27. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  28. package/dist/br/MunicipalitySearch.js.map +1 -1
  29. package/dist/br/PixQRCode.cjs.map +1 -1
  30. package/dist/br/PixQRCode.js.map +1 -1
  31. package/dist/br/use-map-hover.cjs.map +1 -1
  32. package/dist/br/use-map-zoom.cjs.map +1 -1
  33. package/dist/capture/media-recorder.cjs +1 -1
  34. package/dist/capture/media-recorder.js +2 -2
  35. package/dist/capture/use-barcode-scanner.cjs +1 -1
  36. package/dist/capture/use-barcode-scanner.cjs.map +1 -1
  37. package/dist/capture/use-barcode-scanner.js +2 -2
  38. package/dist/capture/use-barcode-scanner.js.map +1 -1
  39. package/dist/capture/use-screen-capture.cjs.map +1 -1
  40. package/dist/capture/use-speech-recognition.cjs.map +1 -1
  41. package/dist/capture/use-torch.cjs.map +1 -1
  42. package/dist/capture/use-video-recorder.cjs.map +1 -1
  43. package/dist/charts/AreaChart.cjs.map +1 -1
  44. package/dist/charts/AreaChart.js.map +1 -1
  45. package/dist/charts/BarChart.cjs.map +1 -1
  46. package/dist/charts/BarChart.js.map +1 -1
  47. package/dist/charts/LineChart.cjs.map +1 -1
  48. package/dist/charts/LineChart.js.map +1 -1
  49. package/dist/charts/PieChart.cjs.map +1 -1
  50. package/dist/charts/PieChart.js.map +1 -1
  51. package/dist/charts/RadarChart.cjs.map +1 -1
  52. package/dist/charts/RadarChart.js.map +1 -1
  53. package/dist/charts/use-chart-colors.cjs.map +1 -1
  54. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  55. package/dist/components/AIChat/AIChat.js.map +1 -1
  56. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  57. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  58. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
  59. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  60. package/dist/components/Accordion/Accordion.cjs.map +1 -1
  61. package/dist/components/Accordion/Accordion.js.map +1 -1
  62. package/dist/components/Alert/Alert.cjs.map +1 -1
  63. package/dist/components/Alert/Alert.js.map +1 -1
  64. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  65. package/dist/components/AppBar/AppBar.js.map +1 -1
  66. package/dist/components/AppShell/AppShell.cjs.map +1 -1
  67. package/dist/components/AppShell/AppShell.js.map +1 -1
  68. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  69. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
  70. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
  71. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
  72. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  73. package/dist/components/Avatar/Avatar.js.map +1 -1
  74. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
  75. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
  76. package/dist/components/Badge/Badge.cjs.map +1 -1
  77. package/dist/components/Badge/Badge.js.map +1 -1
  78. package/dist/components/Banner/Banner.cjs.map +1 -1
  79. package/dist/components/Banner/Banner.js.map +1 -1
  80. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
  81. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
  82. package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  83. package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
  84. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  85. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  86. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  87. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  88. package/dist/components/Button/Button.cjs.map +1 -1
  89. package/dist/components/Button/Button.js.map +1 -1
  90. package/dist/components/Calendar/Calendar.cjs +1 -1
  91. package/dist/components/Calendar/Calendar.cjs.map +1 -1
  92. package/dist/components/Calendar/Calendar.js +38 -41
  93. package/dist/components/Calendar/Calendar.js.map +1 -1
  94. package/dist/components/Card/Card.cjs.map +1 -1
  95. package/dist/components/Card/Card.js.map +1 -1
  96. package/dist/components/Carousel/Carousel.cjs.map +1 -1
  97. package/dist/components/Carousel/Carousel.js.map +1 -1
  98. package/dist/components/Chat/Chat.cjs.map +1 -1
  99. package/dist/components/Chat/Chat.js.map +1 -1
  100. package/dist/components/Chat/ChatComposer.cjs.map +1 -1
  101. package/dist/components/Chat/ChatComposer.js.map +1 -1
  102. package/dist/components/Chat/chat-groups.cjs +1 -1
  103. package/dist/components/Chat/chat-groups.cjs.map +1 -1
  104. package/dist/components/Chat/chat-groups.js +2 -2
  105. package/dist/components/Chat/chat-groups.js.map +1 -1
  106. package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
  107. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  108. package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
  109. package/dist/components/ChipInput/ChipInput.js.map +1 -1
  110. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  111. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
  112. package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
  113. package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
  114. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  115. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  116. package/dist/components/Combobox/Combobox.js.map +1 -1
  117. package/dist/components/Command/Command.cjs.map +1 -1
  118. package/dist/components/Command/Command.js.map +1 -1
  119. package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
  120. package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
  121. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  122. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  123. package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
  124. package/dist/components/CopyButton/CopyButton.js.map +1 -1
  125. package/dist/components/DataList/DataList.cjs.map +1 -1
  126. package/dist/components/DataList/DataList.js.map +1 -1
  127. package/dist/components/DataTable/DataTable.cjs +1 -1
  128. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  129. package/dist/components/DataTable/DataTable.js +2 -2
  130. package/dist/components/DataTable/DataTable.js.map +1 -1
  131. package/dist/components/DataTable/EditableCell.cjs.map +1 -1
  132. package/dist/components/DataTable/EditableCell.js.map +1 -1
  133. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  134. package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
  135. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  136. package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
  137. package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
  138. package/dist/components/Divider/Divider.cjs.map +1 -1
  139. package/dist/components/Divider/Divider.js.map +1 -1
  140. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  141. package/dist/components/Drawer/Drawer.js.map +1 -1
  142. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  143. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  144. package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
  145. package/dist/components/Dropzone/Dropzone.js.map +1 -1
  146. package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
  147. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  148. package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
  149. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  150. package/dist/components/FileUpload/FileUpload.cjs +1 -1
  151. package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
  152. package/dist/components/FileUpload/FileUpload.js +1 -1
  153. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  154. package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
  155. package/dist/components/FilterBar/FilterBar.js.map +1 -1
  156. package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
  157. package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
  158. package/dist/components/Form/Form.cjs.map +1 -1
  159. package/dist/components/Form/Form.js.map +1 -1
  160. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  161. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  162. package/dist/components/Image/Image.cjs.map +1 -1
  163. package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
  164. package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
  165. package/dist/components/ImageCropper/ImageCropper.js +1 -1
  166. package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
  167. package/dist/components/Input/Input.cjs.map +1 -1
  168. package/dist/components/Input/Input.js.map +1 -1
  169. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  170. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  171. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  172. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  173. package/dist/components/Kanban/Kanban.cjs.map +1 -1
  174. package/dist/components/Kanban/Kanban.js.map +1 -1
  175. package/dist/components/Label/Label.cjs.map +1 -1
  176. package/dist/components/Label/Label.js.map +1 -1
  177. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  178. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  179. package/dist/components/ListTile/ListTile.cjs.map +1 -1
  180. package/dist/components/ListTile/ListTile.js.map +1 -1
  181. package/dist/components/Markdown/Markdown.cjs +1 -1
  182. package/dist/components/Markdown/Markdown.cjs.map +1 -1
  183. package/dist/components/Markdown/Markdown.js +2 -2
  184. package/dist/components/Markdown/Markdown.js.map +1 -1
  185. package/dist/components/Masonry/Masonry.cjs.map +1 -1
  186. package/dist/components/Masonry/Masonry.js.map +1 -1
  187. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  188. package/dist/components/Menubar/Menubar.js.map +1 -1
  189. package/dist/components/Modal/Modal.cjs.map +1 -1
  190. package/dist/components/Modal/Modal.js.map +1 -1
  191. package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
  192. package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
  193. package/dist/components/Money/Money.cjs.map +1 -1
  194. package/dist/components/Money/Money.js.map +1 -1
  195. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  196. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  197. package/dist/components/NProgress/NProgress.cjs.map +1 -1
  198. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  199. package/dist/components/Navbar/Navbar.js.map +1 -1
  200. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  201. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  202. package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
  203. package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
  204. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
  205. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  206. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
  207. package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
  208. package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
  209. package/dist/components/Page/Page.cjs.map +1 -1
  210. package/dist/components/Page/Page.js.map +1 -1
  211. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  212. package/dist/components/Pagination/Pagination.js.map +1 -1
  213. package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
  214. package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
  215. package/dist/components/PinInput/PinInput.cjs.map +1 -1
  216. package/dist/components/PinInput/PinInput.js.map +1 -1
  217. package/dist/components/Popover/Popover.cjs.map +1 -1
  218. package/dist/components/Popover/Popover.js.map +1 -1
  219. package/dist/components/Portal/Portal.cjs.map +1 -1
  220. package/dist/components/Progress/Progress.cjs.map +1 -1
  221. package/dist/components/Progress/Progress.js.map +1 -1
  222. package/dist/components/QRCode/QRCode.cjs.map +1 -1
  223. package/dist/components/QRCode/QRCode.js.map +1 -1
  224. package/dist/components/Radio/Radio.cjs.map +1 -1
  225. package/dist/components/Radio/Radio.js.map +1 -1
  226. package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
  227. package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
  228. package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
  229. package/dist/components/RatingStars/RatingStars.js.map +1 -1
  230. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  231. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  232. package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
  233. package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
  234. package/dist/components/Resizable/Resizable.cjs.map +1 -1
  235. package/dist/components/Resizable/Resizable.js.map +1 -1
  236. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  237. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  238. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  239. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  240. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  241. package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  242. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  243. package/dist/components/Select/Select.cjs.map +1 -1
  244. package/dist/components/Select/Select.js.map +1 -1
  245. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  246. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  247. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
  248. package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
  249. package/dist/components/Slider/Slider.cjs.map +1 -1
  250. package/dist/components/Slider/Slider.js.map +1 -1
  251. package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
  252. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  253. package/dist/components/Spinner/Spinner.cjs.map +1 -1
  254. package/dist/components/Spinner/Spinner.js.map +1 -1
  255. package/dist/components/Stat/Stat.cjs.map +1 -1
  256. package/dist/components/Stat/Stat.js.map +1 -1
  257. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  258. package/dist/components/Stepper/Stepper.js.map +1 -1
  259. package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
  260. package/dist/components/StepperInput/StepperInput.js.map +1 -1
  261. package/dist/components/Switch/Switch.cjs.map +1 -1
  262. package/dist/components/Switch/Switch.js.map +1 -1
  263. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  264. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  265. package/dist/components/Table/Table.cjs.map +1 -1
  266. package/dist/components/Table/Table.js.map +1 -1
  267. package/dist/components/Tabs/Tabs.cjs.map +1 -1
  268. package/dist/components/Tabs/Tabs.js.map +1 -1
  269. package/dist/components/Tag/Tag.cjs.map +1 -1
  270. package/dist/components/Tag/Tag.js.map +1 -1
  271. package/dist/components/Textarea/Textarea.cjs.map +1 -1
  272. package/dist/components/Textarea/Textarea.js.map +1 -1
  273. package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
  274. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  275. package/dist/components/Timeline/Timeline.cjs.map +1 -1
  276. package/dist/components/Timeline/Timeline.js.map +1 -1
  277. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  278. package/dist/components/Toast/ToastProvider.js.map +1 -1
  279. package/dist/components/Toggle/Toggle.cjs.map +1 -1
  280. package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
  281. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  282. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  283. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  284. package/dist/components/Tour/Tour.cjs.map +1 -1
  285. package/dist/components/Tour/Tour.js.map +1 -1
  286. package/dist/components/Transfer/Transfer.cjs.map +1 -1
  287. package/dist/components/Transfer/Transfer.js.map +1 -1
  288. package/dist/components/TreeView/TreeView.cjs +1 -1
  289. package/dist/components/TreeView/TreeView.cjs.map +1 -1
  290. package/dist/components/TreeView/TreeView.js +1 -4
  291. package/dist/components/TreeView/TreeView.js.map +1 -1
  292. package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
  293. package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
  294. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  295. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  296. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  297. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  298. package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
  299. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  300. package/dist/components/Wizard/Wizard.js.map +1 -1
  301. package/dist/data/data-provider-context.cjs.map +1 -1
  302. package/dist/data/use-resource.cjs.map +1 -1
  303. package/dist/editor/RichTextEditor.cjs.map +1 -1
  304. package/dist/editor/RichTextEditor.js.map +1 -1
  305. package/dist/error-boundary/use-error-handler.cjs.map +1 -1
  306. package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
  307. package/dist/forms/FormField.cjs.map +1 -1
  308. package/dist/forms/masked-inputs.cjs.map +1 -1
  309. package/dist/forms/use-viacep.cjs.map +1 -1
  310. package/dist/forms/use-zod-form.cjs.map +1 -1
  311. package/dist/geo/TrajectoryMap.cjs.map +1 -1
  312. package/dist/geo/TrajectoryMap.js.map +1 -1
  313. package/dist/geo/leaflet-map.cjs.map +1 -1
  314. package/dist/geo/leaflet-map.js.map +1 -1
  315. package/dist/geo/use-position-tracker.cjs.map +1 -1
  316. package/dist/hooks/use-announce.cjs.map +1 -1
  317. package/dist/hooks/use-async.cjs.map +1 -1
  318. package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
  319. package/dist/hooks/use-breakpoint.cjs.map +1 -1
  320. package/dist/hooks/use-click-outside.cjs.map +1 -1
  321. package/dist/hooks/use-client-filter.cjs.map +1 -1
  322. package/dist/hooks/use-clipboard.cjs.map +1 -1
  323. package/dist/hooks/use-counter.cjs.map +1 -1
  324. package/dist/hooks/use-debounce.cjs.map +1 -1
  325. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  326. package/dist/hooks/use-disclosure.cjs.map +1 -1
  327. package/dist/hooks/use-document-visibility.cjs.map +1 -1
  328. package/dist/hooks/use-event-listener.cjs.map +1 -1
  329. package/dist/hooks/use-geolocation.cjs.map +1 -1
  330. package/dist/hooks/use-hover.cjs.map +1 -1
  331. package/dist/hooks/use-idle.cjs.map +1 -1
  332. package/dist/hooks/use-install-prompt.cjs +1 -1
  333. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  334. package/dist/hooks/use-install-prompt.js +1 -1
  335. package/dist/hooks/use-install-prompt.js.map +1 -1
  336. package/dist/hooks/use-intersection-observer.cjs.map +1 -1
  337. package/dist/hooks/use-interval.cjs.map +1 -1
  338. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  339. package/dist/hooks/use-keyboard-shortcut.cjs +1 -1
  340. package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
  341. package/dist/hooks/use-keyboard-shortcut.js +1 -1
  342. package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
  343. package/dist/hooks/use-list-state.cjs.map +1 -1
  344. package/dist/hooks/use-local-storage.cjs.map +1 -1
  345. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  346. package/dist/hooks/use-long-press.cjs.map +1 -1
  347. package/dist/hooks/use-map.cjs.map +1 -1
  348. package/dist/hooks/use-media-devices.cjs.map +1 -1
  349. package/dist/hooks/use-media-permission.cjs.map +1 -1
  350. package/dist/hooks/use-media-query.cjs.map +1 -1
  351. package/dist/hooks/use-object-url.cjs.map +1 -1
  352. package/dist/hooks/use-online.cjs.map +1 -1
  353. package/dist/hooks/use-pagination.cjs.map +1 -1
  354. package/dist/hooks/use-previous.cjs.map +1 -1
  355. package/dist/hooks/use-queue.cjs.map +1 -1
  356. package/dist/hooks/use-resize-observer.cjs.map +1 -1
  357. package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
  358. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  359. package/dist/hooks/use-set.cjs.map +1 -1
  360. package/dist/hooks/use-sortable.cjs +1 -1
  361. package/dist/hooks/use-sortable.cjs.map +1 -1
  362. package/dist/hooks/use-sortable.js +0 -2
  363. package/dist/hooks/use-sortable.js.map +1 -1
  364. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  365. package/dist/hooks/use-storage-estimate.cjs.map +1 -1
  366. package/dist/hooks/use-throttle.cjs.map +1 -1
  367. package/dist/hooks/use-timeout.cjs.map +1 -1
  368. package/dist/hooks/use-toggle.cjs.map +1 -1
  369. package/dist/hooks/use-window-size.cjs.map +1 -1
  370. package/dist/http/api-client.cjs +1 -1
  371. package/dist/http/api-client.cjs.map +1 -1
  372. package/dist/http/api-client.js +7 -5
  373. package/dist/http/api-client.js.map +1 -1
  374. package/dist/http/resumable-upload.cjs +1 -1
  375. package/dist/http/resumable-upload.cjs.map +1 -1
  376. package/dist/http/resumable-upload.js +1 -1
  377. package/dist/http/resumable-upload.js.map +1 -1
  378. package/dist/http/use-poll.cjs.map +1 -1
  379. package/dist/i18n/I18nProvider.cjs.map +1 -1
  380. package/dist/icons/Icon.cjs.map +1 -1
  381. package/dist/icons/IconProvider.cjs.map +1 -1
  382. package/dist/icons/icon-context.cjs.map +1 -1
  383. package/dist/icons/use-icon.cjs.map +1 -1
  384. package/dist/imaging/use-image-processing.cjs.map +1 -1
  385. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  386. package/dist/oauth/GoogleSignIn.js.map +1 -1
  387. package/dist/oauth/use-oauth-callback.cjs.map +1 -1
  388. package/dist/offline/use-offline-sync.cjs.map +1 -1
  389. package/dist/push/use-push-subscription.cjs.map +1 -1
  390. package/dist/query/QueryProvider.cjs.map +1 -1
  391. package/dist/query/constants.cjs +1 -1
  392. package/dist/query/constants.cjs.map +1 -1
  393. package/dist/query/constants.js +10 -10
  394. package/dist/query/constants.js.map +1 -1
  395. package/dist/query/create-query-keys.cjs +1 -1
  396. package/dist/query/create-query-keys.cjs.map +1 -1
  397. package/dist/query/create-query-keys.js +1 -1
  398. package/dist/query/create-query-keys.js.map +1 -1
  399. package/dist/query/offline-persistence.cjs.map +1 -1
  400. package/dist/query/use-cursor-query.cjs.map +1 -1
  401. package/dist/query/use-offline-mutation.cjs.map +1 -1
  402. package/dist/query/use-paginated-query.cjs.map +1 -1
  403. package/dist/router/AppRouter.cjs +1 -1
  404. package/dist/router/AppRouter.cjs.map +1 -1
  405. package/dist/router/AppRouter.js +7 -2
  406. package/dist/router/AppRouter.js.map +1 -1
  407. package/dist/sse/use-event-stream.cjs.map +1 -1
  408. package/dist/store/create-store.cjs.map +1 -1
  409. package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
  410. package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
  411. package/dist/theme/ThemeProvider.cjs.map +1 -1
  412. package/dist/theme/color.cjs +1 -1
  413. package/dist/theme/color.cjs.map +1 -1
  414. package/dist/theme/color.js +1 -1
  415. package/dist/theme/color.js.map +1 -1
  416. package/dist/utils/cn.cjs +1 -1
  417. package/dist/utils/cn.cjs.map +1 -1
  418. package/dist/utils/cn.js +1 -1
  419. package/dist/utils/cn.js.map +1 -1
  420. package/dist/utils/objects.cjs +1 -1
  421. package/dist/utils/objects.cjs.map +1 -1
  422. package/dist/utils/objects.js +1 -1
  423. package/dist/utils/objects.js.map +1 -1
  424. package/dist/utils/xlsx.cjs +1 -1
  425. package/dist/utils/xlsx.cjs.map +1 -1
  426. package/dist/utils/xlsx.js +3 -3
  427. package/dist/utils/xlsx.js.map +1 -1
  428. package/dist/vision/core/canvas.cjs +1 -1
  429. package/dist/vision/core/canvas.cjs.map +1 -1
  430. package/dist/vision/core/canvas.js +4 -4
  431. package/dist/vision/core/canvas.js.map +1 -1
  432. package/dist/vision/core/exceptions.cjs +1 -1
  433. package/dist/vision/core/exceptions.cjs.map +1 -1
  434. package/dist/vision/core/exceptions.js +2 -2
  435. package/dist/vision/core/exceptions.js.map +1 -1
  436. package/dist/vision/core/metadata.cjs +2 -2
  437. package/dist/vision/core/metadata.cjs.map +1 -1
  438. package/dist/vision/core/metadata.js +11 -8
  439. package/dist/vision/core/metadata.js.map +1 -1
  440. package/dist/vision/core/session.cjs +1 -1
  441. package/dist/vision/core/session.cjs.map +1 -1
  442. package/dist/vision/core/session.js +8 -5
  443. package/dist/vision/core/session.js.map +1 -1
  444. package/dist/vision/fusion.cjs +2 -0
  445. package/dist/vision/fusion.cjs.map +1 -0
  446. package/dist/vision/fusion.js +42 -0
  447. package/dist/vision/fusion.js.map +1 -0
  448. package/dist/vision/index.cjs +1 -1
  449. package/dist/vision/index.cjs.map +1 -1
  450. package/dist/vision/index.js +23 -20
  451. package/dist/vision/index.js.map +1 -1
  452. package/dist/vision/io/image.cjs +1 -1
  453. package/dist/vision/io/image.cjs.map +1 -1
  454. package/dist/vision/io/image.js +4 -2
  455. package/dist/vision/io/image.js.map +1 -1
  456. package/dist/vision/labels.cjs +1 -1
  457. package/dist/vision/labels.cjs.map +1 -1
  458. package/dist/vision/labels.js +7 -4
  459. package/dist/vision/labels.js.map +1 -1
  460. package/dist/vision/postprocess/detection.cjs +1 -1
  461. package/dist/vision/postprocess/detection.cjs.map +1 -1
  462. package/dist/vision/postprocess/detection.js +3 -10
  463. package/dist/vision/postprocess/detection.js.map +1 -1
  464. package/dist/vision/postprocess/segmentation.cjs +1 -1
  465. package/dist/vision/postprocess/segmentation.cjs.map +1 -1
  466. package/dist/vision/postprocess/segmentation.js +11 -15
  467. package/dist/vision/postprocess/segmentation.js.map +1 -1
  468. package/dist/vision/preprocess/image.cjs +1 -1
  469. package/dist/vision/preprocess/image.cjs.map +1 -1
  470. package/dist/vision/preprocess/image.js +4 -2
  471. package/dist/vision/preprocess/image.js.map +1 -1
  472. package/dist/vision/preprocess/pipeline.cjs +2 -0
  473. package/dist/vision/preprocess/pipeline.cjs.map +1 -0
  474. package/dist/vision/preprocess/pipeline.js +61 -0
  475. package/dist/vision/preprocess/pipeline.js.map +1 -0
  476. package/dist/vision/results.cjs +1 -1
  477. package/dist/vision/results.cjs.map +1 -1
  478. package/dist/vision/results.js +23 -2
  479. package/dist/vision/results.js.map +1 -1
  480. package/dist/vision/tasks/base.cjs +1 -1
  481. package/dist/vision/tasks/base.cjs.map +1 -1
  482. package/dist/vision/tasks/base.js +11 -2
  483. package/dist/vision/tasks/base.js.map +1 -1
  484. package/dist/vision/tasks/classifier.cjs +1 -1
  485. package/dist/vision/tasks/classifier.cjs.map +1 -1
  486. package/dist/vision/tasks/classifier.js +2 -2
  487. package/dist/vision/tasks/classifier.js.map +1 -1
  488. package/dist/vision/tasks/detectClassify.cjs +2 -0
  489. package/dist/vision/tasks/detectClassify.cjs.map +1 -0
  490. package/dist/vision/tasks/detectClassify.js +222 -0
  491. package/dist/vision/tasks/detectClassify.js.map +1 -0
  492. package/dist/vision/tasks/detector.cjs +1 -1
  493. package/dist/vision/tasks/detector.cjs.map +1 -1
  494. package/dist/vision/tasks/detector.js +54 -29
  495. package/dist/vision/tasks/detector.js.map +1 -1
  496. package/dist/vision/tasks/segmenter.cjs +1 -1
  497. package/dist/vision/tasks/segmenter.cjs.map +1 -1
  498. package/dist/vision/tasks/segmenter.js +53 -28
  499. package/dist/vision/tasks/segmenter.js.map +1 -1
  500. package/dist/vision/types.cjs.map +1 -1
  501. package/dist/vision/types.js.map +1 -1
  502. package/dist/vision/use-camera-stream.cjs.map +1 -1
  503. package/dist/vision/use-live-luminance.cjs.map +1 -1
  504. package/dist/vision.cjs +1 -1
  505. package/dist/vision.d.ts +1517 -962
  506. package/dist/vision.js +25 -22
  507. package/dist/vite/create-vite-config.cjs +1 -1
  508. package/dist/vite/create-vite-config.cjs.map +1 -1
  509. package/dist/vite/create-vite-config.js +3 -3
  510. package/dist/vite/create-vite-config.js.map +1 -1
  511. package/dist/vite/tempest-icons.cjs.map +1 -1
  512. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  513. package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
  514. package/dist/ws/use-web-socket.cjs +1 -1
  515. package/dist/ws/use-web-socket.cjs.map +1 -1
  516. package/dist/ws/use-web-socket.js +5 -3
  517. package/dist/ws/use-web-socket.js.map +1 -1
  518. package/package.json +2 -2
  519. package/template/package.json +1 -1
@@ -1,2 +1,2 @@
1
- const e=require("./pwa-env.cjs");let t=require("react");var n=10080*60*1e3,r=3e3,i=`tempest:install-declined-at`,a=null,o=new Set;typeof window<`u`&&(window.addEventListener(`beforeinstallprompt`,e=>{e.preventDefault(),a=e,o.forEach(e=>e(a))}),window.addEventListener(`appinstalled`,()=>{a=null,o.forEach(e=>e(null))}));function s(e,t){if(typeof window>`u`)return!1;let n=window.localStorage.getItem(e);if(!n)return!1;let r=Number(n);return Number.isFinite(r)?Date.now()-r<t:!1}function c(c={}){let{declineStorageKey:l=i,declineCooldownMs:u=n,manualFallbackDelayMs:d=r}=c,[f,p]=(0,t.useState)(a),[m,h]=(0,t.useState)(e.isStandalone()),[g,_]=(0,t.useState)(!1);(0,t.useEffect)(()=>(o.add(p),()=>{o.delete(p)}),[]),(0,t.useEffect)(()=>{if(typeof window>`u`)return;let t=window.matchMedia?.(`(display-mode: standalone)`),n=()=>h(e.isStandalone());return t?.addEventListener?.(`change`,n),window.addEventListener(`appinstalled`,n),()=>{t?.removeEventListener?.(`change`,n),window.removeEventListener(`appinstalled`,n)}},[]),(0,t.useEffect)(()=>{if(f){_(!1);return}let e=window.setTimeout(()=>{a||_(!0)},d);return()=>window.clearTimeout(e)},[f,d]);let v=e.isIOS(),y=e.isAndroidWithoutPromptApi(),b=s(l,u),x=`none`;!m&&!b&&(f?x=`native`:v?x=`ios`:(y||g)&&(x=`manual`));let S=()=>{typeof window>`u`||window.localStorage.setItem(l,String(Date.now()))};return{deferredPrompt:f,canInstall:x!==`none`,isIOS:v,isStandalone:m,isManualAndroid:y,promptTimedOut:g,method:x,openInChromeIntent:e.buildOpenInChromeIntent(),install:async()=>{if(!f)return!1;await f.prompt();let e=await f.userChoice;return e.outcome===`dismissed`&&S(),a=null,o.forEach(e=>e(null)),e.outcome===`accepted`},recordDecline:S}}exports.useInstallPrompt=c;
1
+ const e=require("./pwa-env.cjs");let t=require("react");var n=6048e5,r=3e3,i=`tempest:install-declined-at`,a=null,o=new Set;typeof window<`u`&&(window.addEventListener(`beforeinstallprompt`,e=>{e.preventDefault(),a=e,o.forEach(e=>e(a))}),window.addEventListener(`appinstalled`,()=>{a=null,o.forEach(e=>e(null))}));function s(e,t){if(typeof window>`u`)return!1;let n=window.localStorage.getItem(e);if(!n)return!1;let r=Number(n);return Number.isFinite(r)?Date.now()-r<t:!1}function c(c={}){let{declineStorageKey:l=i,declineCooldownMs:u=n,manualFallbackDelayMs:d=r}=c,[f,p]=(0,t.useState)(a),[m,h]=(0,t.useState)(e.isStandalone()),[g,_]=(0,t.useState)(!1);(0,t.useEffect)(()=>(o.add(p),()=>{o.delete(p)}),[]),(0,t.useEffect)(()=>{if(typeof window>`u`)return;let t=window.matchMedia?.(`(display-mode: standalone)`),n=()=>h(e.isStandalone());return t?.addEventListener?.(`change`,n),window.addEventListener(`appinstalled`,n),()=>{t?.removeEventListener?.(`change`,n),window.removeEventListener(`appinstalled`,n)}},[]),(0,t.useEffect)(()=>{if(f){_(!1);return}let e=window.setTimeout(()=>{a||_(!0)},d);return()=>window.clearTimeout(e)},[f,d]);let v=e.isIOS(),y=e.isAndroidWithoutPromptApi(),b=s(l,u),x=`none`;!m&&!b&&(f?x=`native`:v?x=`ios`:(y||g)&&(x=`manual`));let S=()=>{typeof window>`u`||window.localStorage.setItem(l,String(Date.now()))};return{deferredPrompt:f,canInstall:x!==`none`,isIOS:v,isStandalone:m,isManualAndroid:y,promptTimedOut:g,method:x,openInChromeIntent:e.buildOpenInChromeIntent(),install:async()=>{if(!f)return!1;await f.prompt();let e=await f.userChoice;return e.outcome===`dismissed`&&S(),a=null,o.forEach(e=>e(null)),e.outcome===`accepted`},recordDecline:S}}exports.useInstallPrompt=c;
2
2
  //# sourceMappingURL=use-install-prompt.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-install-prompt.cjs","names":[],"sources":["../../src/hooks/use-install-prompt.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n buildOpenInChromeIntent,\n isAndroidWithoutPromptApi,\n isIOS,\n isStandalone,\n type BeforeInstallPromptEvent,\n} from \"./pwa-env\";\n\nconst DEFAULT_DECLINE_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MANUAL_FALLBACK_DELAY_MS = 3000;\nconst DEFAULT_DECLINE_STORAGE_KEY = \"tempest:install-declined-at\";\n\nlet cachedEvent: BeforeInstallPromptEvent | null = null;\nconst eventSubscribers = new Set<(event: BeforeInstallPromptEvent | null) => void>();\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeinstallprompt\", (event: Event) => {\n event.preventDefault();\n cachedEvent = event as BeforeInstallPromptEvent;\n eventSubscribers.forEach((fn) => fn(cachedEvent));\n });\n window.addEventListener(\"appinstalled\", () => {\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n });\n}\n\n/**\n * Strategy the UI should use to install the app:\n *\n * - `\"native\"`: trigger `BeforeInstallPromptEvent.prompt()`.\n * - `\"ios\"`: show iOS Safari Share → Add to Home Screen instructions.\n * - `\"manual\"`: show generic browser-menu instructions (Chromium forks that\n * strip the prompt API, plus the timeout fallback when no event arrives).\n * - `\"none\"`: nothing to offer (already installed or unsupported runtime).\n */\nexport type InstallMethod = \"native\" | \"ios\" | \"manual\" | \"none\";\n\n/** Options for {@link useInstallPrompt}. */\nexport interface UseInstallPromptOptions {\n /**\n * `localStorage` key used to persist the decline timestamp. Defaults to\n * `\"tempest:install-declined-at\"`.\n */\n declineStorageKey?: string;\n /**\n * How long, in ms, the install CTA stays hidden after the user declines.\n * Defaults to 7 days.\n */\n declineCooldownMs?: number;\n /**\n * How long, in ms, to wait for a `beforeinstallprompt` event before\n * resolving to the `\"manual\"` method. Defaults to `3000`.\n */\n manualFallbackDelayMs?: number;\n}\n\n/** State returned by {@link useInstallPrompt}. */\nexport interface UseInstallPromptResult {\n /** The cached `beforeinstallprompt` event, or `null` when unavailable. */\n deferredPrompt: BeforeInstallPromptEvent | null;\n /** True when the app can be installed through any supported method. */\n canInstall: boolean;\n /** True when running on iOS/iPadOS Safari. */\n isIOS: boolean;\n /** True when already running as an installed PWA. */\n isStandalone: boolean;\n /** True when the browser is an Android Chromium fork with no prompt API. */\n isManualAndroid: boolean;\n /** True when no `beforeinstallprompt` arrived within the timeout window. */\n promptTimedOut: boolean;\n /** The resolved install strategy the UI should follow. */\n method: InstallMethod;\n /** An `intent://` URL to re-open the page in Chrome on Android, else `null`. */\n openInChromeIntent: string | null;\n /**\n * Triggers the native prompt.\n *\n * @returns `true` when the user accepted the install, `false` otherwise.\n */\n install: () => Promise<boolean>;\n /** Records that the user declined the CTA, starting the cooldown window. */\n recordDecline: () => void;\n}\n\nfunction readDecline(key: string, cooldownMs: number): boolean {\n if (typeof window === \"undefined\") return false;\n const raw = window.localStorage.getItem(key);\n if (!raw) return false;\n const ts = Number(raw);\n if (!Number.isFinite(ts)) return false;\n return Date.now() - ts < cooldownMs;\n}\n\n/**\n * React hook that resolves how (and whether) to offer PWA installation.\n *\n * It caches the `beforeinstallprompt` event, detects iOS/iPadOS, detects\n * Android Chromium forks that lack the prompt API, detects standalone display\n * mode, and applies a decline cooldown persisted in `localStorage`. The\n * resulting `method` tells the UI which install affordance to render.\n *\n * The decline persistence is pluggable through\n * {@link UseInstallPromptOptions.declineStorageKey} and\n * {@link UseInstallPromptOptions.declineCooldownMs} — no app-specific storage\n * layer is required, and it is SSR-guarded.\n *\n * @param options - Optional storage key, cooldown, and fallback-delay tuning.\n * @returns The install state plus `install()` and `recordDecline()` actions.\n *\n * @example\n * const { method, install } = useInstallPrompt();\n * if (method === \"native\") return <button onClick={install}>Install</button>;\n */\nexport function useInstallPrompt(options: UseInstallPromptOptions = {}): UseInstallPromptResult {\n const {\n declineStorageKey = DEFAULT_DECLINE_STORAGE_KEY,\n declineCooldownMs = DEFAULT_DECLINE_COOLDOWN_MS,\n manualFallbackDelayMs = DEFAULT_MANUAL_FALLBACK_DELAY_MS,\n } = options;\n\n const [event, setEvent] = useState<BeforeInstallPromptEvent | null>(cachedEvent);\n const [standalone, setStandalone] = useState<boolean>(isStandalone());\n const [promptTimedOut, setPromptTimedOut] = useState<boolean>(false);\n\n useEffect(() => {\n eventSubscribers.add(setEvent);\n return () => {\n eventSubscribers.delete(setEvent);\n };\n }, []);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const mq = window.matchMedia?.(\"(display-mode: standalone)\");\n const handler = (): void => setStandalone(isStandalone());\n mq?.addEventListener?.(\"change\", handler);\n window.addEventListener(\"appinstalled\", handler);\n return () => {\n mq?.removeEventListener?.(\"change\", handler);\n window.removeEventListener(\"appinstalled\", handler);\n };\n }, []);\n\n useEffect(() => {\n if (event) {\n setPromptTimedOut(false);\n return;\n }\n const timer = window.setTimeout(() => {\n if (!cachedEvent) setPromptTimedOut(true);\n }, manualFallbackDelayMs);\n return () => window.clearTimeout(timer);\n }, [event, manualFallbackDelayMs]);\n\n const ios = isIOS();\n const manualAndroid = isAndroidWithoutPromptApi();\n\n const declineActive = readDecline(declineStorageKey, declineCooldownMs);\n\n let method: InstallMethod = \"none\";\n if (!standalone && !declineActive) {\n if (event) method = \"native\";\n else if (ios) method = \"ios\";\n else if (manualAndroid || promptTimedOut) method = \"manual\";\n }\n\n const recordDecline = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.setItem(declineStorageKey, String(Date.now()));\n };\n\n const install = async (): Promise<boolean> => {\n if (!event) return false;\n await event.prompt();\n const choice = await event.userChoice;\n if (choice.outcome === \"dismissed\") recordDecline();\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n return choice.outcome === \"accepted\";\n };\n\n return {\n deferredPrompt: event,\n canInstall: method !== \"none\",\n isIOS: ios,\n isStandalone: standalone,\n isManualAndroid: manualAndroid,\n promptTimedOut,\n method,\n openInChromeIntent: buildOpenInChromeIntent(),\n install,\n recordDecline,\n };\n}\n"],"mappings":"wDASA,IAAM,EAA8B,MAAc,GAAK,IACjD,EAAmC,IACnC,EAA8B,8BAEhC,EAA+C,KAC7C,EAAmB,IAAI,IAEzB,OAAO,OAAW,MAClB,OAAO,iBAAiB,sBAAwB,GAAiB,CAC7D,EAAM,eAAe,EACrB,EAAc,EACd,EAAiB,QAAS,GAAO,EAAG,CAAW,CAAC,CACpD,CAAC,EACD,OAAO,iBAAiB,mBAAsB,CAC1C,EAAc,KACd,EAAiB,QAAS,GAAO,EAAG,IAAI,CAAC,CAC7C,CAAC,GA6DL,SAAS,EAAY,EAAa,EAA6B,CAC3D,GAAI,OAAO,OAAW,IAAa,MAAO,GAC1C,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAG,EAC3C,GAAI,CAAC,EAAK,MAAO,GACjB,IAAM,EAAK,OAAO,CAAG,EAErB,OADK,OAAO,SAAS,CAAE,EAChB,KAAK,IAAI,EAAI,EAAK,EADQ,EAErC,CAsBA,SAAgB,EAAiB,EAAmC,CAAC,EAA2B,CAC5F,GAAM,CACF,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACxB,EAEE,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAsD,CAAW,EACzE,CAAC,EAAY,IAAA,EAAA,EAAA,SAAA,CAAmC,EAAA,aAAa,CAAC,EAC9D,CAAC,EAAgB,IAAA,EAAA,EAAA,SAAA,CAAuC,EAAK,GAEnE,EAAA,EAAA,UAAA,MACI,EAAiB,IAAI,CAAQ,MAChB,CACT,EAAiB,OAAO,CAAQ,CACpC,GACD,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAK,OAAO,aAAa,4BAA4B,EACrD,MAAsB,EAAc,EAAA,aAAa,CAAC,EAGxD,OAFA,GAAI,mBAAmB,SAAU,CAAO,EACxC,OAAO,iBAAiB,eAAgB,CAAO,MAClC,CACT,GAAI,sBAAsB,SAAU,CAAO,EAC3C,OAAO,oBAAoB,eAAgB,CAAO,CACtD,CACJ,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,EAAO,CACP,EAAkB,EAAK,EACvB,MACJ,CACA,IAAM,EAAQ,OAAO,eAAiB,CAC7B,GAAa,EAAkB,EAAI,CAC5C,EAAG,CAAqB,EACxB,UAAa,OAAO,aAAa,CAAK,CAC1C,EAAG,CAAC,EAAO,CAAqB,CAAC,EAEjC,IAAM,EAAM,EAAA,MAAM,EACZ,EAAgB,EAAA,0BAA0B,EAE1C,EAAgB,EAAY,EAAmB,CAAiB,EAElE,EAAwB,OACxB,CAAC,GAAc,CAAC,IACZ,EAAO,EAAS,SACX,EAAK,EAAS,OACd,GAAiB,KAAgB,EAAS,WAGvD,IAAM,MAA4B,CAC1B,OAAO,OAAW,KACtB,OAAO,aAAa,QAAQ,EAAmB,OAAO,KAAK,IAAI,CAAC,CAAC,CACrE,EAYA,MAAO,CACH,eAAgB,EAChB,WAAY,IAAW,OACvB,MAAO,EACP,aAAc,EACd,gBAAiB,EACjB,iBACA,SACA,mBAAoB,EAAA,wBAAwB,EAC5C,iBAnB0C,CAC1C,GAAI,CAAC,EAAO,MAAO,GACnB,MAAM,EAAM,OAAO,EACnB,IAAM,EAAS,MAAM,EAAM,WAI3B,OAHI,EAAO,UAAY,aAAa,EAAc,EAClD,EAAc,KACd,EAAiB,QAAS,GAAO,EAAG,IAAI,CAAC,EAClC,EAAO,UAAY,UAC9B,EAYI,eACJ,CACJ"}
1
+ {"version":3,"file":"use-install-prompt.cjs","names":[],"sources":["../../src/hooks/use-install-prompt.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n buildOpenInChromeIntent,\n isAndroidWithoutPromptApi,\n isIOS,\n isStandalone,\n type BeforeInstallPromptEvent,\n} from \"./pwa-env\";\n\nconst DEFAULT_DECLINE_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MANUAL_FALLBACK_DELAY_MS = 3000;\nconst DEFAULT_DECLINE_STORAGE_KEY = \"tempest:install-declined-at\";\n\nlet cachedEvent: BeforeInstallPromptEvent | null = null;\nconst eventSubscribers = new Set<(event: BeforeInstallPromptEvent | null) => void>();\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeinstallprompt\", (event: Event) => {\n event.preventDefault();\n cachedEvent = event as BeforeInstallPromptEvent;\n eventSubscribers.forEach((fn) => fn(cachedEvent));\n });\n window.addEventListener(\"appinstalled\", () => {\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n });\n}\n\n/**\n * Strategy the UI should use to install the app:\n *\n * - `\"native\"`: trigger `BeforeInstallPromptEvent.prompt()`.\n * - `\"ios\"`: show iOS Safari Share → Add to Home Screen instructions.\n * - `\"manual\"`: show generic browser-menu instructions (Chromium forks that\n * strip the prompt API, plus the timeout fallback when no event arrives).\n * - `\"none\"`: nothing to offer (already installed or unsupported runtime).\n */\nexport type InstallMethod = \"native\" | \"ios\" | \"manual\" | \"none\";\n\n/** Options for {@link useInstallPrompt}. */\nexport interface UseInstallPromptOptions {\n /**\n * `localStorage` key used to persist the decline timestamp. Defaults to\n * `\"tempest:install-declined-at\"`.\n */\n declineStorageKey?: string;\n /**\n * How long, in ms, the install CTA stays hidden after the user declines.\n * Defaults to 7 days.\n */\n declineCooldownMs?: number;\n /**\n * How long, in ms, to wait for a `beforeinstallprompt` event before\n * resolving to the `\"manual\"` method. Defaults to `3000`.\n */\n manualFallbackDelayMs?: number;\n}\n\n/** State returned by {@link useInstallPrompt}. */\nexport interface UseInstallPromptResult {\n /** The cached `beforeinstallprompt` event, or `null` when unavailable. */\n deferredPrompt: BeforeInstallPromptEvent | null;\n /** True when the app can be installed through any supported method. */\n canInstall: boolean;\n /** True when running on iOS/iPadOS Safari. */\n isIOS: boolean;\n /** True when already running as an installed PWA. */\n isStandalone: boolean;\n /** True when the browser is an Android Chromium fork with no prompt API. */\n isManualAndroid: boolean;\n /** True when no `beforeinstallprompt` arrived within the timeout window. */\n promptTimedOut: boolean;\n /** The resolved install strategy the UI should follow. */\n method: InstallMethod;\n /** An `intent://` URL to re-open the page in Chrome on Android, else `null`. */\n openInChromeIntent: string | null;\n /**\n * Triggers the native prompt.\n *\n * @returns `true` when the user accepted the install, `false` otherwise.\n */\n install: () => Promise<boolean>;\n /** Records that the user declined the CTA, starting the cooldown window. */\n recordDecline: () => void;\n}\n\nfunction readDecline(key: string, cooldownMs: number): boolean {\n if (typeof window === \"undefined\") return false;\n const raw = window.localStorage.getItem(key);\n if (!raw) return false;\n const ts = Number(raw);\n if (!Number.isFinite(ts)) return false;\n return Date.now() - ts < cooldownMs;\n}\n\n/**\n * React hook that resolves how (and whether) to offer PWA installation.\n *\n * It caches the `beforeinstallprompt` event, detects iOS/iPadOS, detects\n * Android Chromium forks that lack the prompt API, detects standalone display\n * mode, and applies a decline cooldown persisted in `localStorage`. The\n * resulting `method` tells the UI which install affordance to render.\n *\n * The decline persistence is pluggable through\n * {@link UseInstallPromptOptions.declineStorageKey} and\n * {@link UseInstallPromptOptions.declineCooldownMs} — no app-specific storage\n * layer is required, and it is SSR-guarded.\n *\n * @param options - Optional storage key, cooldown, and fallback-delay tuning.\n * @returns The install state plus `install()` and `recordDecline()` actions.\n *\n * @example\n * const { method, install } = useInstallPrompt();\n * if (method === \"native\") return <button onClick={install}>Install</button>;\n */\nexport function useInstallPrompt(options: UseInstallPromptOptions = {}): UseInstallPromptResult {\n const {\n declineStorageKey = DEFAULT_DECLINE_STORAGE_KEY,\n declineCooldownMs = DEFAULT_DECLINE_COOLDOWN_MS,\n manualFallbackDelayMs = DEFAULT_MANUAL_FALLBACK_DELAY_MS,\n } = options;\n\n const [event, setEvent] = useState<BeforeInstallPromptEvent | null>(cachedEvent);\n const [standalone, setStandalone] = useState<boolean>(isStandalone());\n const [promptTimedOut, setPromptTimedOut] = useState<boolean>(false);\n\n useEffect(() => {\n eventSubscribers.add(setEvent);\n return () => {\n eventSubscribers.delete(setEvent);\n };\n }, []);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const mq = window.matchMedia?.(\"(display-mode: standalone)\");\n const handler = (): void => setStandalone(isStandalone());\n mq?.addEventListener?.(\"change\", handler);\n window.addEventListener(\"appinstalled\", handler);\n return () => {\n mq?.removeEventListener?.(\"change\", handler);\n window.removeEventListener(\"appinstalled\", handler);\n };\n }, []);\n\n useEffect(() => {\n if (event) {\n setPromptTimedOut(false);\n return;\n }\n const timer = window.setTimeout(() => {\n if (!cachedEvent) setPromptTimedOut(true);\n }, manualFallbackDelayMs);\n return () => window.clearTimeout(timer);\n }, [event, manualFallbackDelayMs]);\n\n const ios = isIOS();\n const manualAndroid = isAndroidWithoutPromptApi();\n\n const declineActive = readDecline(declineStorageKey, declineCooldownMs);\n\n let method: InstallMethod = \"none\";\n if (!standalone && !declineActive) {\n if (event) method = \"native\";\n else if (ios) method = \"ios\";\n else if (manualAndroid || promptTimedOut) method = \"manual\";\n }\n\n const recordDecline = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.setItem(declineStorageKey, String(Date.now()));\n };\n\n const install = async (): Promise<boolean> => {\n if (!event) return false;\n await event.prompt();\n const choice = await event.userChoice;\n if (choice.outcome === \"dismissed\") recordDecline();\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n return choice.outcome === \"accepted\";\n };\n\n return {\n deferredPrompt: event,\n canInstall: method !== \"none\",\n isIOS: ios,\n isStandalone: standalone,\n isManualAndroid: manualAndroid,\n promptTimedOut,\n method,\n openInChromeIntent: buildOpenInChromeIntent(),\n install,\n recordDecline,\n };\n}\n"],"mappings":"wDASA,IAAM,EAA8B,OAC9B,EAAmC,IACnC,EAA8B,8BAEhC,EAA+C,KAC7C,EAAmB,IAAI,IAEzB,OAAO,OAAW,MAClB,OAAO,iBAAiB,sBAAwB,GAAiB,CAC7D,EAAM,eAAe,EACrB,EAAc,EACd,EAAiB,QAAS,GAAO,EAAG,CAAW,CAAC,CACpD,CAAC,EACD,OAAO,iBAAiB,mBAAsB,CAC1C,EAAc,KACd,EAAiB,QAAS,GAAO,EAAG,IAAI,CAAC,CAC7C,CAAC,GA6DL,SAAS,EAAY,EAAa,EAA6B,CAC3D,GAAI,OAAO,OAAW,IAAa,MAAO,GAC1C,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAG,EAC3C,GAAI,CAAC,EAAK,MAAO,GACjB,IAAM,EAAK,OAAO,CAAG,EAErB,OADK,OAAO,SAAS,CAAE,EAChB,KAAK,IAAI,EAAI,EAAK,EADQ,EAErC,CAsBA,SAAgB,EAAiB,EAAmC,CAAC,EAA2B,CAC5F,GAAM,CACF,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACxB,EAEE,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAA0C,CAAW,EACzE,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAAkB,EAAA,aAAa,CAAC,EAC9D,CAAC,EAAgB,IAAA,EAAqB,EAAA,SAAA,CAAkB,EAAK,GAEnE,EAAA,EAAA,UAAA,MACI,EAAiB,IAAI,CAAQ,MAChB,CACT,EAAiB,OAAO,CAAQ,CACpC,GACD,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAK,OAAO,aAAa,4BAA4B,EACrD,MAAsB,EAAc,EAAA,aAAa,CAAC,EAGxD,OAFA,GAAI,mBAAmB,SAAU,CAAO,EACxC,OAAO,iBAAiB,eAAgB,CAAO,MAClC,CACT,GAAI,sBAAsB,SAAU,CAAO,EAC3C,OAAO,oBAAoB,eAAgB,CAAO,CACtD,CACJ,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,EAAO,CACP,EAAkB,EAAK,EACvB,MACJ,CACA,IAAM,EAAQ,OAAO,eAAiB,CAC7B,GAAa,EAAkB,EAAI,CAC5C,EAAG,CAAqB,EACxB,UAAa,OAAO,aAAa,CAAK,CAC1C,EAAG,CAAC,EAAO,CAAqB,CAAC,EAEjC,IAAM,EAAM,EAAA,MAAM,EACZ,EAAgB,EAAA,0BAA0B,EAE1C,EAAgB,EAAY,EAAmB,CAAiB,EAElE,EAAwB,OACxB,CAAC,GAAc,CAAC,IACZ,EAAO,EAAS,SACX,EAAK,EAAS,OACd,GAAiB,KAAgB,EAAS,WAGvD,IAAM,MAA4B,CAC1B,OAAO,OAAW,KACtB,OAAO,aAAa,QAAQ,EAAmB,OAAO,KAAK,IAAI,CAAC,CAAC,CACrE,EAYA,MAAO,CACH,eAAgB,EAChB,WAAY,IAAW,OACvB,MAAO,EACP,aAAc,EACd,gBAAiB,EACjB,iBACA,SACA,mBAAoB,EAAA,wBAAwB,EAC5C,iBAnB0C,CAC1C,GAAI,CAAC,EAAO,MAAO,GACnB,MAAM,EAAM,OAAO,EACnB,IAAM,EAAS,MAAM,EAAM,WAI3B,OAHI,EAAO,UAAY,aAAa,EAAc,EAClD,EAAc,KACd,EAAiB,QAAS,GAAO,EAAG,IAAI,CAAC,EAClC,EAAO,UAAY,UAC9B,EAYI,eACJ,CACJ"}
@@ -1,7 +1,7 @@
1
1
  import { buildOpenInChromeIntent as e, isAndroidWithoutPromptApi as t, isIOS as n, isStandalone as r } from "./pwa-env.js";
2
2
  import { useEffect as i, useState as a } from "react";
3
3
  //#region src/hooks/use-install-prompt.ts
4
- var o = 10080 * 60 * 1e3, s = 3e3, c = "tempest:install-declined-at", l = null, u = /* @__PURE__ */ new Set();
4
+ var o = 6048e5, s = 3e3, c = "tempest:install-declined-at", l = null, u = /* @__PURE__ */ new Set();
5
5
  typeof window < "u" && (window.addEventListener("beforeinstallprompt", (e) => {
6
6
  e.preventDefault(), l = e, u.forEach((e) => e(l));
7
7
  }), window.addEventListener("appinstalled", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"use-install-prompt.js","names":[],"sources":["../../src/hooks/use-install-prompt.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n buildOpenInChromeIntent,\n isAndroidWithoutPromptApi,\n isIOS,\n isStandalone,\n type BeforeInstallPromptEvent,\n} from \"./pwa-env\";\n\nconst DEFAULT_DECLINE_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MANUAL_FALLBACK_DELAY_MS = 3000;\nconst DEFAULT_DECLINE_STORAGE_KEY = \"tempest:install-declined-at\";\n\nlet cachedEvent: BeforeInstallPromptEvent | null = null;\nconst eventSubscribers = new Set<(event: BeforeInstallPromptEvent | null) => void>();\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeinstallprompt\", (event: Event) => {\n event.preventDefault();\n cachedEvent = event as BeforeInstallPromptEvent;\n eventSubscribers.forEach((fn) => fn(cachedEvent));\n });\n window.addEventListener(\"appinstalled\", () => {\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n });\n}\n\n/**\n * Strategy the UI should use to install the app:\n *\n * - `\"native\"`: trigger `BeforeInstallPromptEvent.prompt()`.\n * - `\"ios\"`: show iOS Safari Share → Add to Home Screen instructions.\n * - `\"manual\"`: show generic browser-menu instructions (Chromium forks that\n * strip the prompt API, plus the timeout fallback when no event arrives).\n * - `\"none\"`: nothing to offer (already installed or unsupported runtime).\n */\nexport type InstallMethod = \"native\" | \"ios\" | \"manual\" | \"none\";\n\n/** Options for {@link useInstallPrompt}. */\nexport interface UseInstallPromptOptions {\n /**\n * `localStorage` key used to persist the decline timestamp. Defaults to\n * `\"tempest:install-declined-at\"`.\n */\n declineStorageKey?: string;\n /**\n * How long, in ms, the install CTA stays hidden after the user declines.\n * Defaults to 7 days.\n */\n declineCooldownMs?: number;\n /**\n * How long, in ms, to wait for a `beforeinstallprompt` event before\n * resolving to the `\"manual\"` method. Defaults to `3000`.\n */\n manualFallbackDelayMs?: number;\n}\n\n/** State returned by {@link useInstallPrompt}. */\nexport interface UseInstallPromptResult {\n /** The cached `beforeinstallprompt` event, or `null` when unavailable. */\n deferredPrompt: BeforeInstallPromptEvent | null;\n /** True when the app can be installed through any supported method. */\n canInstall: boolean;\n /** True when running on iOS/iPadOS Safari. */\n isIOS: boolean;\n /** True when already running as an installed PWA. */\n isStandalone: boolean;\n /** True when the browser is an Android Chromium fork with no prompt API. */\n isManualAndroid: boolean;\n /** True when no `beforeinstallprompt` arrived within the timeout window. */\n promptTimedOut: boolean;\n /** The resolved install strategy the UI should follow. */\n method: InstallMethod;\n /** An `intent://` URL to re-open the page in Chrome on Android, else `null`. */\n openInChromeIntent: string | null;\n /**\n * Triggers the native prompt.\n *\n * @returns `true` when the user accepted the install, `false` otherwise.\n */\n install: () => Promise<boolean>;\n /** Records that the user declined the CTA, starting the cooldown window. */\n recordDecline: () => void;\n}\n\nfunction readDecline(key: string, cooldownMs: number): boolean {\n if (typeof window === \"undefined\") return false;\n const raw = window.localStorage.getItem(key);\n if (!raw) return false;\n const ts = Number(raw);\n if (!Number.isFinite(ts)) return false;\n return Date.now() - ts < cooldownMs;\n}\n\n/**\n * React hook that resolves how (and whether) to offer PWA installation.\n *\n * It caches the `beforeinstallprompt` event, detects iOS/iPadOS, detects\n * Android Chromium forks that lack the prompt API, detects standalone display\n * mode, and applies a decline cooldown persisted in `localStorage`. The\n * resulting `method` tells the UI which install affordance to render.\n *\n * The decline persistence is pluggable through\n * {@link UseInstallPromptOptions.declineStorageKey} and\n * {@link UseInstallPromptOptions.declineCooldownMs} — no app-specific storage\n * layer is required, and it is SSR-guarded.\n *\n * @param options - Optional storage key, cooldown, and fallback-delay tuning.\n * @returns The install state plus `install()` and `recordDecline()` actions.\n *\n * @example\n * const { method, install } = useInstallPrompt();\n * if (method === \"native\") return <button onClick={install}>Install</button>;\n */\nexport function useInstallPrompt(options: UseInstallPromptOptions = {}): UseInstallPromptResult {\n const {\n declineStorageKey = DEFAULT_DECLINE_STORAGE_KEY,\n declineCooldownMs = DEFAULT_DECLINE_COOLDOWN_MS,\n manualFallbackDelayMs = DEFAULT_MANUAL_FALLBACK_DELAY_MS,\n } = options;\n\n const [event, setEvent] = useState<BeforeInstallPromptEvent | null>(cachedEvent);\n const [standalone, setStandalone] = useState<boolean>(isStandalone());\n const [promptTimedOut, setPromptTimedOut] = useState<boolean>(false);\n\n useEffect(() => {\n eventSubscribers.add(setEvent);\n return () => {\n eventSubscribers.delete(setEvent);\n };\n }, []);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const mq = window.matchMedia?.(\"(display-mode: standalone)\");\n const handler = (): void => setStandalone(isStandalone());\n mq?.addEventListener?.(\"change\", handler);\n window.addEventListener(\"appinstalled\", handler);\n return () => {\n mq?.removeEventListener?.(\"change\", handler);\n window.removeEventListener(\"appinstalled\", handler);\n };\n }, []);\n\n useEffect(() => {\n if (event) {\n setPromptTimedOut(false);\n return;\n }\n const timer = window.setTimeout(() => {\n if (!cachedEvent) setPromptTimedOut(true);\n }, manualFallbackDelayMs);\n return () => window.clearTimeout(timer);\n }, [event, manualFallbackDelayMs]);\n\n const ios = isIOS();\n const manualAndroid = isAndroidWithoutPromptApi();\n\n const declineActive = readDecline(declineStorageKey, declineCooldownMs);\n\n let method: InstallMethod = \"none\";\n if (!standalone && !declineActive) {\n if (event) method = \"native\";\n else if (ios) method = \"ios\";\n else if (manualAndroid || promptTimedOut) method = \"manual\";\n }\n\n const recordDecline = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.setItem(declineStorageKey, String(Date.now()));\n };\n\n const install = async (): Promise<boolean> => {\n if (!event) return false;\n await event.prompt();\n const choice = await event.userChoice;\n if (choice.outcome === \"dismissed\") recordDecline();\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n return choice.outcome === \"accepted\";\n };\n\n return {\n deferredPrompt: event,\n canInstall: method !== \"none\",\n isIOS: ios,\n isStandalone: standalone,\n isManualAndroid: manualAndroid,\n promptTimedOut,\n method,\n openInChromeIntent: buildOpenInChromeIntent(),\n install,\n recordDecline,\n };\n}\n"],"mappings":";;;AASA,IAAM,IAA8B,QAAc,KAAK,KACjD,IAAmC,KACnC,IAA8B,+BAEhC,IAA+C,MAC7C,oBAAmB,IAAI,IAAsD;AAE/E,OAAO,SAAW,QAClB,OAAO,iBAAiB,wBAAwB,MAAiB;CAG7D,AAFA,EAAM,eAAe,GACrB,IAAc,GACd,EAAiB,SAAS,MAAO,EAAG,CAAW,CAAC;AACpD,CAAC,GACD,OAAO,iBAAiB,sBAAsB;CAE1C,AADA,IAAc,MACd,EAAiB,SAAS,MAAO,EAAG,IAAI,CAAC;AAC7C,CAAC;AA6DL,SAAS,EAAY,GAAa,GAA6B;CAC3D,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAM,IAAM,OAAO,aAAa,QAAQ,CAAG;CAC3C,IAAI,CAAC,GAAK,OAAO;CACjB,IAAM,IAAK,OAAO,CAAG;CAErB,OADK,OAAO,SAAS,CAAE,IAChB,KAAK,IAAI,IAAI,IAAK,IADQ;AAErC;AAsBA,SAAgB,EAAiB,IAAmC,CAAC,GAA2B;CAC5F,IAAM,EACF,uBAAoB,GACpB,uBAAoB,GACpB,2BAAwB,MACxB,GAEE,CAAC,GAAO,KAAY,EAA0C,CAAW,GACzE,CAAC,GAAY,KAAiB,EAAkB,EAAa,CAAC,GAC9D,CAAC,GAAgB,KAAqB,EAAkB,EAAK;CAqBnE,AAnBA,SACI,EAAiB,IAAI,CAAQ,SAChB;EACT,EAAiB,OAAO,CAAQ;CACpC,IACD,CAAC,CAAC,GAEL,QAAgB;EACZ,IAAI,OAAO,SAAW,KAAa;EACnC,IAAM,IAAK,OAAO,aAAa,4BAA4B,GACrD,UAAsB,EAAc,EAAa,CAAC;EAGxD,OAFA,GAAI,mBAAmB,UAAU,CAAO,GACxC,OAAO,iBAAiB,gBAAgB,CAAO,SAClC;GAET,AADA,GAAI,sBAAsB,UAAU,CAAO,GAC3C,OAAO,oBAAoB,gBAAgB,CAAO;EACtD;CACJ,GAAG,CAAC,CAAC,GAEL,QAAgB;EACZ,IAAI,GAAO;GACP,EAAkB,EAAK;GACvB;EACJ;EACA,IAAM,IAAQ,OAAO,iBAAiB;GAClC,AAAK,KAAa,EAAkB,EAAI;EAC5C,GAAG,CAAqB;EACxB,aAAa,OAAO,aAAa,CAAK;CAC1C,GAAG,CAAC,GAAO,CAAqB,CAAC;CAEjC,IAAM,IAAM,EAAM,GACZ,IAAgB,EAA0B,GAE1C,IAAgB,EAAY,GAAmB,CAAiB,GAElE,IAAwB;CAC5B,AAAI,CAAC,KAAc,CAAC,MACZ,IAAO,IAAS,WACX,IAAK,IAAS,SACd,KAAiB,OAAgB,IAAS;CAGvD,IAAM,UAA4B;EAC1B,OAAO,SAAW,OACtB,OAAO,aAAa,QAAQ,GAAmB,OAAO,KAAK,IAAI,CAAC,CAAC;CACrE;CAYA,OAAO;EACH,gBAAgB;EAChB,YAAY,MAAW;EACvB,OAAO;EACP,cAAc;EACd,iBAAiB;EACjB;EACA;EACA,oBAAoB,EAAwB;EAC5C,qBAnB0C;GAC1C,IAAI,CAAC,GAAO,OAAO;GACnB,MAAM,EAAM,OAAO;GACnB,IAAM,IAAS,MAAM,EAAM;GAI3B,OAHI,EAAO,YAAY,eAAa,EAAc,GAClD,IAAc,MACd,EAAiB,SAAS,MAAO,EAAG,IAAI,CAAC,GAClC,EAAO,YAAY;EAC9B;EAYI;CACJ;AACJ"}
1
+ {"version":3,"file":"use-install-prompt.js","names":[],"sources":["../../src/hooks/use-install-prompt.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n buildOpenInChromeIntent,\n isAndroidWithoutPromptApi,\n isIOS,\n isStandalone,\n type BeforeInstallPromptEvent,\n} from \"./pwa-env\";\n\nconst DEFAULT_DECLINE_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MANUAL_FALLBACK_DELAY_MS = 3000;\nconst DEFAULT_DECLINE_STORAGE_KEY = \"tempest:install-declined-at\";\n\nlet cachedEvent: BeforeInstallPromptEvent | null = null;\nconst eventSubscribers = new Set<(event: BeforeInstallPromptEvent | null) => void>();\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeinstallprompt\", (event: Event) => {\n event.preventDefault();\n cachedEvent = event as BeforeInstallPromptEvent;\n eventSubscribers.forEach((fn) => fn(cachedEvent));\n });\n window.addEventListener(\"appinstalled\", () => {\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n });\n}\n\n/**\n * Strategy the UI should use to install the app:\n *\n * - `\"native\"`: trigger `BeforeInstallPromptEvent.prompt()`.\n * - `\"ios\"`: show iOS Safari Share → Add to Home Screen instructions.\n * - `\"manual\"`: show generic browser-menu instructions (Chromium forks that\n * strip the prompt API, plus the timeout fallback when no event arrives).\n * - `\"none\"`: nothing to offer (already installed or unsupported runtime).\n */\nexport type InstallMethod = \"native\" | \"ios\" | \"manual\" | \"none\";\n\n/** Options for {@link useInstallPrompt}. */\nexport interface UseInstallPromptOptions {\n /**\n * `localStorage` key used to persist the decline timestamp. Defaults to\n * `\"tempest:install-declined-at\"`.\n */\n declineStorageKey?: string;\n /**\n * How long, in ms, the install CTA stays hidden after the user declines.\n * Defaults to 7 days.\n */\n declineCooldownMs?: number;\n /**\n * How long, in ms, to wait for a `beforeinstallprompt` event before\n * resolving to the `\"manual\"` method. Defaults to `3000`.\n */\n manualFallbackDelayMs?: number;\n}\n\n/** State returned by {@link useInstallPrompt}. */\nexport interface UseInstallPromptResult {\n /** The cached `beforeinstallprompt` event, or `null` when unavailable. */\n deferredPrompt: BeforeInstallPromptEvent | null;\n /** True when the app can be installed through any supported method. */\n canInstall: boolean;\n /** True when running on iOS/iPadOS Safari. */\n isIOS: boolean;\n /** True when already running as an installed PWA. */\n isStandalone: boolean;\n /** True when the browser is an Android Chromium fork with no prompt API. */\n isManualAndroid: boolean;\n /** True when no `beforeinstallprompt` arrived within the timeout window. */\n promptTimedOut: boolean;\n /** The resolved install strategy the UI should follow. */\n method: InstallMethod;\n /** An `intent://` URL to re-open the page in Chrome on Android, else `null`. */\n openInChromeIntent: string | null;\n /**\n * Triggers the native prompt.\n *\n * @returns `true` when the user accepted the install, `false` otherwise.\n */\n install: () => Promise<boolean>;\n /** Records that the user declined the CTA, starting the cooldown window. */\n recordDecline: () => void;\n}\n\nfunction readDecline(key: string, cooldownMs: number): boolean {\n if (typeof window === \"undefined\") return false;\n const raw = window.localStorage.getItem(key);\n if (!raw) return false;\n const ts = Number(raw);\n if (!Number.isFinite(ts)) return false;\n return Date.now() - ts < cooldownMs;\n}\n\n/**\n * React hook that resolves how (and whether) to offer PWA installation.\n *\n * It caches the `beforeinstallprompt` event, detects iOS/iPadOS, detects\n * Android Chromium forks that lack the prompt API, detects standalone display\n * mode, and applies a decline cooldown persisted in `localStorage`. The\n * resulting `method` tells the UI which install affordance to render.\n *\n * The decline persistence is pluggable through\n * {@link UseInstallPromptOptions.declineStorageKey} and\n * {@link UseInstallPromptOptions.declineCooldownMs} — no app-specific storage\n * layer is required, and it is SSR-guarded.\n *\n * @param options - Optional storage key, cooldown, and fallback-delay tuning.\n * @returns The install state plus `install()` and `recordDecline()` actions.\n *\n * @example\n * const { method, install } = useInstallPrompt();\n * if (method === \"native\") return <button onClick={install}>Install</button>;\n */\nexport function useInstallPrompt(options: UseInstallPromptOptions = {}): UseInstallPromptResult {\n const {\n declineStorageKey = DEFAULT_DECLINE_STORAGE_KEY,\n declineCooldownMs = DEFAULT_DECLINE_COOLDOWN_MS,\n manualFallbackDelayMs = DEFAULT_MANUAL_FALLBACK_DELAY_MS,\n } = options;\n\n const [event, setEvent] = useState<BeforeInstallPromptEvent | null>(cachedEvent);\n const [standalone, setStandalone] = useState<boolean>(isStandalone());\n const [promptTimedOut, setPromptTimedOut] = useState<boolean>(false);\n\n useEffect(() => {\n eventSubscribers.add(setEvent);\n return () => {\n eventSubscribers.delete(setEvent);\n };\n }, []);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const mq = window.matchMedia?.(\"(display-mode: standalone)\");\n const handler = (): void => setStandalone(isStandalone());\n mq?.addEventListener?.(\"change\", handler);\n window.addEventListener(\"appinstalled\", handler);\n return () => {\n mq?.removeEventListener?.(\"change\", handler);\n window.removeEventListener(\"appinstalled\", handler);\n };\n }, []);\n\n useEffect(() => {\n if (event) {\n setPromptTimedOut(false);\n return;\n }\n const timer = window.setTimeout(() => {\n if (!cachedEvent) setPromptTimedOut(true);\n }, manualFallbackDelayMs);\n return () => window.clearTimeout(timer);\n }, [event, manualFallbackDelayMs]);\n\n const ios = isIOS();\n const manualAndroid = isAndroidWithoutPromptApi();\n\n const declineActive = readDecline(declineStorageKey, declineCooldownMs);\n\n let method: InstallMethod = \"none\";\n if (!standalone && !declineActive) {\n if (event) method = \"native\";\n else if (ios) method = \"ios\";\n else if (manualAndroid || promptTimedOut) method = \"manual\";\n }\n\n const recordDecline = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.setItem(declineStorageKey, String(Date.now()));\n };\n\n const install = async (): Promise<boolean> => {\n if (!event) return false;\n await event.prompt();\n const choice = await event.userChoice;\n if (choice.outcome === \"dismissed\") recordDecline();\n cachedEvent = null;\n eventSubscribers.forEach((fn) => fn(null));\n return choice.outcome === \"accepted\";\n };\n\n return {\n deferredPrompt: event,\n canInstall: method !== \"none\",\n isIOS: ios,\n isStandalone: standalone,\n isManualAndroid: manualAndroid,\n promptTimedOut,\n method,\n openInChromeIntent: buildOpenInChromeIntent(),\n install,\n recordDecline,\n };\n}\n"],"mappings":";;;AASA,IAAM,IAA8B,QAC9B,IAAmC,KACnC,IAA8B,+BAEhC,IAA+C,MAC7C,oBAAmB,IAAI,IAAsD;AAE/E,OAAO,SAAW,QAClB,OAAO,iBAAiB,wBAAwB,MAAiB;CAG7D,AAFA,EAAM,eAAe,GACrB,IAAc,GACd,EAAiB,SAAS,MAAO,EAAG,CAAW,CAAC;AACpD,CAAC,GACD,OAAO,iBAAiB,sBAAsB;CAE1C,AADA,IAAc,MACd,EAAiB,SAAS,MAAO,EAAG,IAAI,CAAC;AAC7C,CAAC;AA6DL,SAAS,EAAY,GAAa,GAA6B;CAC3D,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAM,IAAM,OAAO,aAAa,QAAQ,CAAG;CAC3C,IAAI,CAAC,GAAK,OAAO;CACjB,IAAM,IAAK,OAAO,CAAG;CAErB,OADK,OAAO,SAAS,CAAE,IAChB,KAAK,IAAI,IAAI,IAAK,IADQ;AAErC;AAsBA,SAAgB,EAAiB,IAAmC,CAAC,GAA2B;CAC5F,IAAM,EACF,uBAAoB,GACpB,uBAAoB,GACpB,2BAAwB,MACxB,GAEE,CAAC,GAAO,KAAY,EAA0C,CAAW,GACzE,CAAC,GAAY,KAAiB,EAAkB,EAAa,CAAC,GAC9D,CAAC,GAAgB,KAAqB,EAAkB,EAAK;CAqBnE,AAnBA,SACI,EAAiB,IAAI,CAAQ,SAChB;EACT,EAAiB,OAAO,CAAQ;CACpC,IACD,CAAC,CAAC,GAEL,QAAgB;EACZ,IAAI,OAAO,SAAW,KAAa;EACnC,IAAM,IAAK,OAAO,aAAa,4BAA4B,GACrD,UAAsB,EAAc,EAAa,CAAC;EAGxD,OAFA,GAAI,mBAAmB,UAAU,CAAO,GACxC,OAAO,iBAAiB,gBAAgB,CAAO,SAClC;GAET,AADA,GAAI,sBAAsB,UAAU,CAAO,GAC3C,OAAO,oBAAoB,gBAAgB,CAAO;EACtD;CACJ,GAAG,CAAC,CAAC,GAEL,QAAgB;EACZ,IAAI,GAAO;GACP,EAAkB,EAAK;GACvB;EACJ;EACA,IAAM,IAAQ,OAAO,iBAAiB;GAClC,AAAK,KAAa,EAAkB,EAAI;EAC5C,GAAG,CAAqB;EACxB,aAAa,OAAO,aAAa,CAAK;CAC1C,GAAG,CAAC,GAAO,CAAqB,CAAC;CAEjC,IAAM,IAAM,EAAM,GACZ,IAAgB,EAA0B,GAE1C,IAAgB,EAAY,GAAmB,CAAiB,GAElE,IAAwB;CAC5B,AAAI,CAAC,KAAc,CAAC,MACZ,IAAO,IAAS,WACX,IAAK,IAAS,SACd,KAAiB,OAAgB,IAAS;CAGvD,IAAM,UAA4B;EAC1B,OAAO,SAAW,OACtB,OAAO,aAAa,QAAQ,GAAmB,OAAO,KAAK,IAAI,CAAC,CAAC;CACrE;CAYA,OAAO;EACH,gBAAgB;EAChB,YAAY,MAAW;EACvB,OAAO;EACP,cAAc;EACd,iBAAiB;EACjB;EACA;EACA,oBAAoB,EAAwB;EAC5C,qBAnB0C;GAC1C,IAAI,CAAC,GAAO,OAAO;GACnB,MAAM,EAAM,OAAO;GACnB,IAAM,IAAS,MAAM,EAAM;GAI3B,OAHI,EAAO,YAAY,eAAa,EAAc,GAClD,IAAc,MACd,EAAiB,SAAS,MAAO,EAAG,IAAI,CAAC,GAClC,EAAO,YAAY;EAC9B;EAYI;CACJ;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-intersection-observer.cjs","names":[],"sources":["../../src/hooks/use-intersection-observer.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface UseIntersectionObserverOptions extends IntersectionObserverInit {\n /** Stop observing after the first intersection (one-shot). Default: false. */\n once?: boolean;\n}\n\n/**\n * Track whether the referenced element intersects the viewport. Useful for\n * lazy-loading images, \"load more\" sentinels, and animation triggers.\n *\n * @returns `IntersectionObserverEntry | null`. `null` until the first observation.\n */\nexport function useIntersectionObserver(\n ref: RefObject<Element | null>,\n options: UseIntersectionObserverOptions = {},\n): IntersectionObserverEntry | null {\n const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);\n const { once = false, root, rootMargin, threshold } = options;\n\n useEffect(() => {\n const target = ref.current;\n if (!target || typeof IntersectionObserver === \"undefined\") return;\n\n const observer = new IntersectionObserver(\n ([nextEntry]) => {\n if (!nextEntry) return;\n setEntry(nextEntry);\n if (once && nextEntry.isIntersecting) observer.unobserve(target);\n },\n { root, rootMargin, threshold },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [ref, once, root, rootMargin, threshold]);\n\n return entry;\n}\n"],"mappings":"uBAcA,SAAgB,EACZ,EACA,EAA0C,CAAC,EACX,CAChC,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAuD,IAAI,EACnE,CAAE,OAAO,GAAO,OAAM,aAAY,aAAc,EAmBtD,OAjBA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAS,EAAI,QACnB,GAAI,CAAC,GAAU,OAAO,qBAAyB,IAAa,OAE5D,IAAM,EAAW,IAAI,sBAChB,CAAC,KAAe,CACR,IACL,EAAS,CAAS,EACd,GAAQ,EAAU,gBAAgB,EAAS,UAAU,CAAM,EACnE,EACA,CAAE,OAAM,aAAY,WAAU,CAClC,EAGA,OADA,EAAS,QAAQ,CAAM,MACV,EAAS,WAAW,CACrC,EAAG,CAAC,EAAK,EAAM,EAAM,EAAY,CAAS,CAAC,EAEpC,CACX"}
1
+ {"version":3,"file":"use-intersection-observer.cjs","names":[],"sources":["../../src/hooks/use-intersection-observer.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface UseIntersectionObserverOptions extends IntersectionObserverInit {\n /** Stop observing after the first intersection (one-shot). Default: false. */\n once?: boolean;\n}\n\n/**\n * Track whether the referenced element intersects the viewport. Useful for\n * lazy-loading images, \"load more\" sentinels, and animation triggers.\n *\n * @returns `IntersectionObserverEntry | null`. `null` until the first observation.\n */\nexport function useIntersectionObserver(\n ref: RefObject<Element | null>,\n options: UseIntersectionObserverOptions = {},\n): IntersectionObserverEntry | null {\n const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);\n const { once = false, root, rootMargin, threshold } = options;\n\n useEffect(() => {\n const target = ref.current;\n if (!target || typeof IntersectionObserver === \"undefined\") return;\n\n const observer = new IntersectionObserver(\n ([nextEntry]) => {\n if (!nextEntry) return;\n setEntry(nextEntry);\n if (once && nextEntry.isIntersecting) observer.unobserve(target);\n },\n { root, rootMargin, threshold },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [ref, once, root, rootMargin, threshold]);\n\n return entry;\n}\n"],"mappings":"uBAcA,SAAgB,EACZ,EACA,EAA0C,CAAC,EACX,CAChC,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAA2C,IAAI,EACnE,CAAE,OAAO,GAAO,OAAM,aAAY,aAAc,EAmBtD,OAjBA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAS,EAAI,QACnB,GAAI,CAAC,GAAU,OAAO,qBAAyB,IAAa,OAE5D,IAAM,EAAW,IAAI,sBAChB,CAAC,KAAe,CACR,IACL,EAAS,CAAS,EACd,GAAQ,EAAU,gBAAgB,EAAS,UAAU,CAAM,EACnE,EACA,CAAE,OAAM,aAAY,WAAU,CAClC,EAGA,OADA,EAAS,QAAQ,CAAM,MACV,EAAS,WAAW,CACrC,EAAG,CAAC,EAAK,EAAM,EAAM,EAAY,CAAS,CAAC,EAEpC,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-interval.cjs","names":[],"sources":["../../src/hooks/use-interval.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\n\n/**\n * Reactive `setInterval`. Pass `null` as `delay` to pause. `fn` is stored in\n * a ref so inline callbacks don't reset the interval on every render.\n *\n * @example\n * useInterval(() => poll(), enabled ? 5000 : null);\n */\nexport function useInterval(fn: () => void, delay: number | null): void {\n const fnRef = useRef(fn);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n\n useEffect(() => {\n if (delay === null) return;\n const id = setInterval(() => fnRef.current(), delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n"],"mappings":"uBASA,SAAgB,EAAY,EAAgB,EAA4B,CACpE,IAAM,GAAA,EAAA,EAAA,OAAA,CAAe,CAAE,GACvB,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAM,QAAU,CACpB,EAAG,CAAC,CAAE,CAAC,GAEP,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAU,KAAM,OACpB,IAAM,EAAK,gBAAkB,EAAM,QAAQ,EAAG,CAAK,EACnD,UAAa,cAAc,CAAE,CACjC,EAAG,CAAC,CAAK,CAAC,CACd"}
1
+ {"version":3,"file":"use-interval.cjs","names":[],"sources":["../../src/hooks/use-interval.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\n\n/**\n * Reactive `setInterval`. Pass `null` as `delay` to pause. `fn` is stored in\n * a ref so inline callbacks don't reset the interval on every render.\n *\n * @example\n * useInterval(() => poll(), enabled ? 5000 : null);\n */\nexport function useInterval(fn: () => void, delay: number | null): void {\n const fnRef = useRef(fn);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n\n useEffect(() => {\n if (delay === null) return;\n const id = setInterval(() => fnRef.current(), delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n"],"mappings":"uBASA,SAAgB,EAAY,EAAgB,EAA4B,CACpE,IAAM,GAAA,EAAQ,EAAA,OAAA,CAAO,CAAE,GACvB,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAM,QAAU,CACpB,EAAG,CAAC,CAAE,CAAC,GAEP,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAU,KAAM,OACpB,IAAM,EAAK,gBAAkB,EAAM,QAAQ,EAAG,CAAK,EACnD,UAAa,cAAc,CAAE,CACjC,EAAG,CAAC,CAAK,CAAC,CACd"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-is-first-render.cjs","names":[],"sources":["../../src/hooks/use-is-first-render.ts"],"sourcesContent":["/* eslint-disable react-hooks/refs -- flipping the flag during render IS this hook */\nimport { useRef } from \"react\";\n\n/**\n * Return `true` on the first render of the component and `false` thereafter.\n *\n * Reads *and* flips a ref during render, which the React Compiler rules flag on\n * sight — here it is the entire hook. Under StrictMode's double render in dev the\n * second pass already sees `false`, so treat the result as \"first committed\n * render\" and not as a strict render counter.\n *\n * @returns Whether the current render is the first one.\n */\nexport function useIsFirstRender(): boolean {\n const isFirst = useRef<boolean>(true);\n if (isFirst.current) {\n isFirst.current = false;\n return true;\n }\n return false;\n}\n"],"mappings":"uBAaA,SAAgB,GAA4B,CACxC,IAAM,GAAA,EAAA,EAAA,OAAA,CAA0B,EAAI,EAKpC,OAJI,EAAQ,SACR,EAAQ,QAAU,GACX,IAEJ,EACX"}
1
+ {"version":3,"file":"use-is-first-render.cjs","names":[],"sources":["../../src/hooks/use-is-first-render.ts"],"sourcesContent":["/* eslint-disable react-hooks/refs -- flipping the flag during render IS this hook */\nimport { useRef } from \"react\";\n\n/**\n * Return `true` on the first render of the component and `false` thereafter.\n *\n * Reads *and* flips a ref during render, which the React Compiler rules flag on\n * sight — here it is the entire hook. Under StrictMode's double render in dev the\n * second pass already sees `false`, so treat the result as \"first committed\n * render\" and not as a strict render counter.\n *\n * @returns Whether the current render is the first one.\n */\nexport function useIsFirstRender(): boolean {\n const isFirst = useRef<boolean>(true);\n if (isFirst.current) {\n isFirst.current = false;\n return true;\n }\n return false;\n}\n"],"mappings":"uBAaA,SAAgB,GAA4B,CACxC,IAAM,GAAA,EAAU,EAAA,OAAA,CAAgB,EAAI,EAKpC,OAJI,EAAQ,SACR,EAAQ,QAAU,GACX,IAEJ,EACX"}
@@ -1,2 +1,2 @@
1
- let e=require("react");function t(e,t){if(e.key.toLowerCase()!==t.key.toLowerCase())return!1;let n=!!t.ctrl,r=!!t.meta,i=!!t.shift,a=!!t.alt;if(t.mod){if(!e.ctrlKey&&!e.metaKey)return!1}else if(e.ctrlKey!==n||e.metaKey!==r)return!1;return!(e.shiftKey!==i||e.altKey!==a)}function n(e){if(!(e instanceof HTMLElement))return!1;let t=e.tagName.toLowerCase();return t===`input`||t===`textarea`||t===`select`||e.isContentEditable}function r(r,i,a={}){let{disabled:o=!1,ignoreInput:s=!0}=a;(0,e.useEffect)(()=>{if(o||typeof window>`u`)return;let e=e=>{s&&n(e.target)||t(e,r)&&i(e)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[o,s,r.key,r.ctrl,r.meta,r.shift,r.alt,r.mod,i])}exports.useKeyboardShortcut=r;
1
+ let e=require("react");function t(e,t){if(e.key.toLowerCase()!==t.key.toLowerCase())return!1;let n=!!t.ctrl,r=!!t.meta,i=!!t.shift,a=!!t.alt;if(t.mod){if(!e.ctrlKey&&!e.metaKey)return!1}else if(e.ctrlKey!==n||e.metaKey!==r)return!1;return e.shiftKey===i&&e.altKey===a}function n(e){if(!(e instanceof HTMLElement))return!1;let t=e.tagName.toLowerCase();return t===`input`||t===`textarea`||t===`select`||e.isContentEditable}function r(r,i,a={}){let{disabled:o=!1,ignoreInput:s=!0}=a;(0,e.useEffect)(()=>{if(o||typeof window>`u`)return;let e=e=>{s&&n(e.target)||t(e,r)&&i(e)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[o,s,r.key,r.ctrl,r.meta,r.shift,r.alt,r.mod,i])}exports.useKeyboardShortcut=r;
2
2
  //# sourceMappingURL=use-keyboard-shortcut.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-keyboard-shortcut.cjs","names":[],"sources":["../../src/hooks/use-keyboard-shortcut.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nexport interface KeyboardShortcut {\n /** Key name (`\"k\"`, `\"Enter\"`, `\"Escape\"`, `\"ArrowDown\"`, etc.). Case-insensitive. */\n key: string;\n ctrl?: boolean;\n meta?: boolean;\n shift?: boolean;\n alt?: boolean;\n /** Match either Ctrl or Cmd. Useful for cross-OS shortcuts. */\n mod?: boolean;\n}\n\nexport interface UseKeyboardShortcutOptions {\n /** Disable the shortcut without unmounting. Default: false. */\n disabled?: boolean;\n /** Listen to the entire window. Pass `false` to scope to a specific element via the handler. Default: true. */\n global?: boolean;\n /** Suppress the event from firing inside `<input>`/`<textarea>`/`[contenteditable]`. Default: true. */\n ignoreInput?: boolean;\n}\n\nfunction matches(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {\n if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) return false;\n const ctrl = !!shortcut.ctrl;\n const meta = !!shortcut.meta;\n const shift = !!shortcut.shift;\n const alt = !!shortcut.alt;\n if (shortcut.mod) {\n if (!event.ctrlKey && !event.metaKey) return false;\n } else {\n if (event.ctrlKey !== ctrl) return false;\n if (event.metaKey !== meta) return false;\n }\n if (event.shiftKey !== shift) return false;\n if (event.altKey !== alt) return false;\n return true;\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") return true;\n return target.isContentEditable;\n}\n\n/**\n * Bind a global keyboard shortcut. Supports modifier combinations and a\n * cross-OS `mod` key (Ctrl on Windows/Linux, Cmd on macOS).\n *\n * The effect depends on the individual `shortcut` fields rather than on the\n * object itself: callers pass an inline literal (`{ key: \"k\", mod: true }`),\n * which is a fresh reference on every render and would otherwise tear down and\n * re-add the listener each time. `exhaustive-deps` is silenced for that line\n * because the destructured fields are the complete dependency set.\n *\n * @example\n * useKeyboardShortcut({ key: \"k\", mod: true }, () => openSearch());\n */\nexport function useKeyboardShortcut(\n shortcut: KeyboardShortcut,\n handler: (event: KeyboardEvent) => void,\n options: UseKeyboardShortcutOptions = {},\n): void {\n const { disabled = false, ignoreInput = true } = options;\n\n useEffect(() => {\n if (disabled || typeof window === \"undefined\") return;\n const listener = (event: KeyboardEvent): void => {\n if (ignoreInput && isEditable(event.target)) return;\n if (matches(event, shortcut)) handler(event);\n };\n window.addEventListener(\"keydown\", listener);\n return () => window.removeEventListener(\"keydown\", listener);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n disabled,\n ignoreInput,\n shortcut.key,\n shortcut.ctrl,\n shortcut.meta,\n shortcut.shift,\n shortcut.alt,\n shortcut.mod,\n handler,\n ]);\n}\n"],"mappings":"uBAsBA,SAAS,EAAQ,EAAsB,EAAqC,CACxE,GAAI,EAAM,IAAI,YAAY,IAAM,EAAS,IAAI,YAAY,EAAG,MAAO,GACnE,IAAM,EAAO,CAAC,CAAC,EAAS,KAClB,EAAO,CAAC,CAAC,EAAS,KAClB,EAAQ,CAAC,CAAC,EAAS,MACnB,EAAM,CAAC,CAAC,EAAS,IACvB,GAAI,EAAS,QACL,CAAC,EAAM,SAAW,CAAC,EAAM,QAAS,MAAO,EAAA,MAG7C,GADI,EAAM,UAAY,GAClB,EAAM,UAAY,EAAM,MAAO,GAIvC,MADA,EADI,EAAM,WAAa,GACnB,EAAM,SAAW,EAEzB,CAEA,SAAS,EAAW,EAAqC,CACrD,GAAI,EAAE,aAAkB,aAAc,MAAO,GAC7C,IAAM,EAAM,EAAO,QAAQ,YAAY,EAEvC,OADI,IAAQ,SAAW,IAAQ,YAAc,IAAQ,UAC9C,EAAO,iBAClB,CAeA,SAAgB,EACZ,EACA,EACA,EAAsC,CAAC,EACnC,CACJ,GAAM,CAAE,WAAW,GAAO,cAAc,IAAS,GAEjD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,GAAY,OAAO,OAAW,IAAa,OAC/C,IAAM,EAAY,GAA+B,CACzC,GAAe,EAAW,EAAM,MAAM,GACtC,EAAQ,EAAO,CAAQ,GAAG,EAAQ,CAAK,CAC/C,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAQ,MAC9B,OAAO,oBAAoB,UAAW,CAAQ,CAE/D,EAAG,CACC,EACA,EACA,EAAS,IACT,EAAS,KACT,EAAS,KACT,EAAS,MACT,EAAS,IACT,EAAS,IACT,CACJ,CAAC,CACL"}
1
+ {"version":3,"file":"use-keyboard-shortcut.cjs","names":[],"sources":["../../src/hooks/use-keyboard-shortcut.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nexport interface KeyboardShortcut {\n /** Key name (`\"k\"`, `\"Enter\"`, `\"Escape\"`, `\"ArrowDown\"`, etc.). Case-insensitive. */\n key: string;\n ctrl?: boolean;\n meta?: boolean;\n shift?: boolean;\n alt?: boolean;\n /** Match either Ctrl or Cmd. Useful for cross-OS shortcuts. */\n mod?: boolean;\n}\n\nexport interface UseKeyboardShortcutOptions {\n /** Disable the shortcut without unmounting. Default: false. */\n disabled?: boolean;\n /** Listen to the entire window. Pass `false` to scope to a specific element via the handler. Default: true. */\n global?: boolean;\n /** Suppress the event from firing inside `<input>`/`<textarea>`/`[contenteditable]`. Default: true. */\n ignoreInput?: boolean;\n}\n\nfunction matches(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {\n if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) return false;\n const ctrl = !!shortcut.ctrl;\n const meta = !!shortcut.meta;\n const shift = !!shortcut.shift;\n const alt = !!shortcut.alt;\n if (shortcut.mod) {\n if (!event.ctrlKey && !event.metaKey) return false;\n } else {\n if (event.ctrlKey !== ctrl) return false;\n if (event.metaKey !== meta) return false;\n }\n if (event.shiftKey !== shift) return false;\n if (event.altKey !== alt) return false;\n return true;\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") return true;\n return target.isContentEditable;\n}\n\n/**\n * Bind a global keyboard shortcut. Supports modifier combinations and a\n * cross-OS `mod` key (Ctrl on Windows/Linux, Cmd on macOS).\n *\n * The effect depends on the individual `shortcut` fields rather than on the\n * object itself: callers pass an inline literal (`{ key: \"k\", mod: true }`),\n * which is a fresh reference on every render and would otherwise tear down and\n * re-add the listener each time. `exhaustive-deps` is silenced for that line\n * because the destructured fields are the complete dependency set.\n *\n * @example\n * useKeyboardShortcut({ key: \"k\", mod: true }, () => openSearch());\n */\nexport function useKeyboardShortcut(\n shortcut: KeyboardShortcut,\n handler: (event: KeyboardEvent) => void,\n options: UseKeyboardShortcutOptions = {},\n): void {\n const { disabled = false, ignoreInput = true } = options;\n\n useEffect(() => {\n if (disabled || typeof window === \"undefined\") return;\n const listener = (event: KeyboardEvent): void => {\n if (ignoreInput && isEditable(event.target)) return;\n if (matches(event, shortcut)) handler(event);\n };\n window.addEventListener(\"keydown\", listener);\n return () => window.removeEventListener(\"keydown\", listener);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n disabled,\n ignoreInput,\n shortcut.key,\n shortcut.ctrl,\n shortcut.meta,\n shortcut.shift,\n shortcut.alt,\n shortcut.mod,\n handler,\n ]);\n}\n"],"mappings":"uBAsBA,SAAS,EAAQ,EAAsB,EAAqC,CACxE,GAAI,EAAM,IAAI,YAAY,IAAM,EAAS,IAAI,YAAY,EAAG,MAAO,GACnE,IAAM,EAAO,CAAC,CAAC,EAAS,KAClB,EAAO,CAAC,CAAC,EAAS,KAClB,EAAQ,CAAC,CAAC,EAAS,MACnB,EAAM,CAAC,CAAC,EAAS,IACvB,GAAI,EAAS,IACL,IAAA,CAAC,EAAM,SAAW,CAAC,EAAM,QAAS,MAAO,EAAA,MAG7C,GADI,EAAM,UAAY,GAClB,EAAM,UAAY,EAAM,MAAO,GAIvC,OAFI,EAAM,WAAa,GACnB,EAAM,SAAW,CAEzB,CAEA,SAAS,EAAW,EAAqC,CACrD,GAAI,EAAE,aAAkB,aAAc,MAAO,GAC7C,IAAM,EAAM,EAAO,QAAQ,YAAY,EAEvC,OADI,IAAQ,SAAW,IAAQ,YAAc,IAAQ,UAC9C,EAAO,iBAClB,CAeA,SAAgB,EACZ,EACA,EACA,EAAsC,CAAC,EACnC,CACJ,GAAM,CAAE,WAAW,GAAO,cAAc,IAAS,GAEjD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,GAAY,OAAO,OAAW,IAAa,OAC/C,IAAM,EAAY,GAA+B,CACzC,GAAe,EAAW,EAAM,MAAM,GACtC,EAAQ,EAAO,CAAQ,GAAG,EAAQ,CAAK,CAC/C,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAQ,MAC9B,OAAO,oBAAoB,UAAW,CAAQ,CAE/D,EAAG,CACC,EACA,EACA,EAAS,IACT,EAAS,KACT,EAAS,KACT,EAAS,MACT,EAAS,IACT,EAAS,IACT,CACJ,CAAC,CACL"}
@@ -6,7 +6,7 @@ function t(e, t) {
6
6
  if (t.mod) {
7
7
  if (!e.ctrlKey && !e.metaKey) return !1;
8
8
  } else if (e.ctrlKey !== n || e.metaKey !== r) return !1;
9
- return !(e.shiftKey !== i || e.altKey !== a);
9
+ return e.shiftKey === i && e.altKey === a;
10
10
  }
11
11
  function n(e) {
12
12
  if (!(e instanceof HTMLElement)) return !1;
@@ -1 +1 @@
1
- {"version":3,"file":"use-keyboard-shortcut.js","names":[],"sources":["../../src/hooks/use-keyboard-shortcut.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nexport interface KeyboardShortcut {\n /** Key name (`\"k\"`, `\"Enter\"`, `\"Escape\"`, `\"ArrowDown\"`, etc.). Case-insensitive. */\n key: string;\n ctrl?: boolean;\n meta?: boolean;\n shift?: boolean;\n alt?: boolean;\n /** Match either Ctrl or Cmd. Useful for cross-OS shortcuts. */\n mod?: boolean;\n}\n\nexport interface UseKeyboardShortcutOptions {\n /** Disable the shortcut without unmounting. Default: false. */\n disabled?: boolean;\n /** Listen to the entire window. Pass `false` to scope to a specific element via the handler. Default: true. */\n global?: boolean;\n /** Suppress the event from firing inside `<input>`/`<textarea>`/`[contenteditable]`. Default: true. */\n ignoreInput?: boolean;\n}\n\nfunction matches(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {\n if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) return false;\n const ctrl = !!shortcut.ctrl;\n const meta = !!shortcut.meta;\n const shift = !!shortcut.shift;\n const alt = !!shortcut.alt;\n if (shortcut.mod) {\n if (!event.ctrlKey && !event.metaKey) return false;\n } else {\n if (event.ctrlKey !== ctrl) return false;\n if (event.metaKey !== meta) return false;\n }\n if (event.shiftKey !== shift) return false;\n if (event.altKey !== alt) return false;\n return true;\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") return true;\n return target.isContentEditable;\n}\n\n/**\n * Bind a global keyboard shortcut. Supports modifier combinations and a\n * cross-OS `mod` key (Ctrl on Windows/Linux, Cmd on macOS).\n *\n * The effect depends on the individual `shortcut` fields rather than on the\n * object itself: callers pass an inline literal (`{ key: \"k\", mod: true }`),\n * which is a fresh reference on every render and would otherwise tear down and\n * re-add the listener each time. `exhaustive-deps` is silenced for that line\n * because the destructured fields are the complete dependency set.\n *\n * @example\n * useKeyboardShortcut({ key: \"k\", mod: true }, () => openSearch());\n */\nexport function useKeyboardShortcut(\n shortcut: KeyboardShortcut,\n handler: (event: KeyboardEvent) => void,\n options: UseKeyboardShortcutOptions = {},\n): void {\n const { disabled = false, ignoreInput = true } = options;\n\n useEffect(() => {\n if (disabled || typeof window === \"undefined\") return;\n const listener = (event: KeyboardEvent): void => {\n if (ignoreInput && isEditable(event.target)) return;\n if (matches(event, shortcut)) handler(event);\n };\n window.addEventListener(\"keydown\", listener);\n return () => window.removeEventListener(\"keydown\", listener);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n disabled,\n ignoreInput,\n shortcut.key,\n shortcut.ctrl,\n shortcut.meta,\n shortcut.shift,\n shortcut.alt,\n shortcut.mod,\n handler,\n ]);\n}\n"],"mappings":";;AAsBA,SAAS,EAAQ,GAAsB,GAAqC;CACxE,IAAI,EAAM,IAAI,YAAY,MAAM,EAAS,IAAI,YAAY,GAAG,OAAO;CACnE,IAAM,IAAO,CAAC,CAAC,EAAS,MAClB,IAAO,CAAC,CAAC,EAAS,MAClB,IAAQ,CAAC,CAAC,EAAS,OACnB,IAAM,CAAC,CAAC,EAAS;CACvB,IAAI,EAAS;MACL,CAAC,EAAM,WAAW,CAAC,EAAM,SAAS,OAAO;CAAA,OAG7C,IADI,EAAM,YAAY,KAClB,EAAM,YAAY,GAAM,OAAO;CAIvC,OADA,EADI,EAAM,aAAa,KACnB,EAAM,WAAW;AAEzB;AAEA,SAAS,EAAW,GAAqC;CACrD,IAAI,EAAE,aAAkB,cAAc,OAAO;CAC7C,IAAM,IAAM,EAAO,QAAQ,YAAY;CAEvC,OADI,MAAQ,WAAW,MAAQ,cAAc,MAAQ,YAC9C,EAAO;AAClB;AAeA,SAAgB,EACZ,GACA,GACA,IAAsC,CAAC,GACnC;CACJ,IAAM,EAAE,cAAW,IAAO,iBAAc,OAAS;CAEjD,QAAgB;EACZ,IAAI,KAAY,OAAO,SAAW,KAAa;EAC/C,IAAM,KAAY,MAA+B;GACzC,KAAe,EAAW,EAAM,MAAM,KACtC,EAAQ,GAAO,CAAQ,KAAG,EAAQ,CAAK;EAC/C;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAQ,SAC9B,OAAO,oBAAoB,WAAW,CAAQ;CAE/D,GAAG;EACC;EACA;EACA,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT;CACJ,CAAC;AACL"}
1
+ {"version":3,"file":"use-keyboard-shortcut.js","names":[],"sources":["../../src/hooks/use-keyboard-shortcut.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nexport interface KeyboardShortcut {\n /** Key name (`\"k\"`, `\"Enter\"`, `\"Escape\"`, `\"ArrowDown\"`, etc.). Case-insensitive. */\n key: string;\n ctrl?: boolean;\n meta?: boolean;\n shift?: boolean;\n alt?: boolean;\n /** Match either Ctrl or Cmd. Useful for cross-OS shortcuts. */\n mod?: boolean;\n}\n\nexport interface UseKeyboardShortcutOptions {\n /** Disable the shortcut without unmounting. Default: false. */\n disabled?: boolean;\n /** Listen to the entire window. Pass `false` to scope to a specific element via the handler. Default: true. */\n global?: boolean;\n /** Suppress the event from firing inside `<input>`/`<textarea>`/`[contenteditable]`. Default: true. */\n ignoreInput?: boolean;\n}\n\nfunction matches(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {\n if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) return false;\n const ctrl = !!shortcut.ctrl;\n const meta = !!shortcut.meta;\n const shift = !!shortcut.shift;\n const alt = !!shortcut.alt;\n if (shortcut.mod) {\n if (!event.ctrlKey && !event.metaKey) return false;\n } else {\n if (event.ctrlKey !== ctrl) return false;\n if (event.metaKey !== meta) return false;\n }\n if (event.shiftKey !== shift) return false;\n if (event.altKey !== alt) return false;\n return true;\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") return true;\n return target.isContentEditable;\n}\n\n/**\n * Bind a global keyboard shortcut. Supports modifier combinations and a\n * cross-OS `mod` key (Ctrl on Windows/Linux, Cmd on macOS).\n *\n * The effect depends on the individual `shortcut` fields rather than on the\n * object itself: callers pass an inline literal (`{ key: \"k\", mod: true }`),\n * which is a fresh reference on every render and would otherwise tear down and\n * re-add the listener each time. `exhaustive-deps` is silenced for that line\n * because the destructured fields are the complete dependency set.\n *\n * @example\n * useKeyboardShortcut({ key: \"k\", mod: true }, () => openSearch());\n */\nexport function useKeyboardShortcut(\n shortcut: KeyboardShortcut,\n handler: (event: KeyboardEvent) => void,\n options: UseKeyboardShortcutOptions = {},\n): void {\n const { disabled = false, ignoreInput = true } = options;\n\n useEffect(() => {\n if (disabled || typeof window === \"undefined\") return;\n const listener = (event: KeyboardEvent): void => {\n if (ignoreInput && isEditable(event.target)) return;\n if (matches(event, shortcut)) handler(event);\n };\n window.addEventListener(\"keydown\", listener);\n return () => window.removeEventListener(\"keydown\", listener);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n disabled,\n ignoreInput,\n shortcut.key,\n shortcut.ctrl,\n shortcut.meta,\n shortcut.shift,\n shortcut.alt,\n shortcut.mod,\n handler,\n ]);\n}\n"],"mappings":";;AAsBA,SAAS,EAAQ,GAAsB,GAAqC;CACxE,IAAI,EAAM,IAAI,YAAY,MAAM,EAAS,IAAI,YAAY,GAAG,OAAO;CACnE,IAAM,IAAO,CAAC,CAAC,EAAS,MAClB,IAAO,CAAC,CAAC,EAAS,MAClB,IAAQ,CAAC,CAAC,EAAS,OACnB,IAAM,CAAC,CAAC,EAAS;CACvB,IAAI,EAAS,KACL;MAAA,CAAC,EAAM,WAAW,CAAC,EAAM,SAAS,OAAO;CAAA,OAG7C,IADI,EAAM,YAAY,KAClB,EAAM,YAAY,GAAM,OAAO;CAIvC,OAFI,EAAM,aAAa,KACnB,EAAM,WAAW;AAEzB;AAEA,SAAS,EAAW,GAAqC;CACrD,IAAI,EAAE,aAAkB,cAAc,OAAO;CAC7C,IAAM,IAAM,EAAO,QAAQ,YAAY;CAEvC,OADI,MAAQ,WAAW,MAAQ,cAAc,MAAQ,YAC9C,EAAO;AAClB;AAeA,SAAgB,EACZ,GACA,GACA,IAAsC,CAAC,GACnC;CACJ,IAAM,EAAE,cAAW,IAAO,iBAAc,OAAS;CAEjD,QAAgB;EACZ,IAAI,KAAY,OAAO,SAAW,KAAa;EAC/C,IAAM,KAAY,MAA+B;GACzC,KAAe,EAAW,EAAM,MAAM,KACtC,EAAQ,GAAO,CAAQ,KAAG,EAAQ,CAAK;EAC/C;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAQ,SAC9B,OAAO,oBAAoB,WAAW,CAAQ;CAE/D,GAAG;EACC;EACA;EACA,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT,EAAS;EACT;CACJ,CAAC;AACL"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-list-state.cjs","names":[],"sources":["../../src/hooks/use-list-state.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface ReorderPayload {\n from: number;\n to: number;\n}\n\nexport interface ListStateHandlers<T> {\n append: (...items: T[]) => void;\n prepend: (...items: T[]) => void;\n insert: (index: number, ...items: T[]) => void;\n remove: (...indices: number[]) => void;\n reorder: (payload: ReorderPayload) => void;\n setItem: (index: number, item: T) => void;\n setState: (items: T[]) => void;\n apply: (fn: (item: T, index: number) => T) => void;\n clear: () => void;\n}\n\n/**\n * Manage an array as state with a rich set of immutable handlers.\n *\n * @typeParam T - element type.\n * @param initial - initial list (default `[]`).\n * @returns Tuple `[list, handlers]`.\n */\nexport function useListState<T>(initial: T[] = []): [T[], ListStateHandlers<T>] {\n const [list, setList] = useState<T[]>(initial);\n\n const append = useCallback((...items: T[]): void => {\n setList((current) => [...current, ...items]);\n }, []);\n\n const prepend = useCallback((...items: T[]): void => {\n setList((current) => [...items, ...current]);\n }, []);\n\n const insert = useCallback((index: number, ...items: T[]): void => {\n setList((current) => [...current.slice(0, index), ...items, ...current.slice(index)]);\n }, []);\n\n const remove = useCallback((...indices: number[]): void => {\n const drop = new Set(indices);\n setList((current) => current.filter((_, index) => !drop.has(index)));\n }, []);\n\n const reorder = useCallback(({ from, to }: ReorderPayload): void => {\n setList((current) => {\n const next = [...current];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n });\n }, []);\n\n const setItem = useCallback((index: number, item: T): void => {\n setList((current) => current.map((value, i) => (i === index ? item : value)));\n }, []);\n\n const setState = useCallback((items: T[]): void => setList(items), []);\n\n const apply = useCallback((fn: (item: T, index: number) => T): void => {\n setList((current) => current.map(fn));\n }, []);\n\n const clear = useCallback((): void => setList([]), []);\n\n const handlers = useMemo<ListStateHandlers<T>>(\n () => ({\n append,\n prepend,\n insert,\n remove,\n reorder,\n setItem,\n setState,\n apply,\n clear,\n }),\n [append, prepend, insert, remove, reorder, setItem, setState, apply, clear],\n );\n\n return [list, handlers];\n}\n"],"mappings":"uBA0BA,SAAgB,EAAgB,EAAe,CAAC,EAAgC,CAC5E,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAAyB,CAAO,EAEvC,GAAA,EAAA,EAAA,YAAA,EAAsB,GAAG,IAAqB,CAChD,EAAS,GAAY,CAAC,GAAG,EAAS,GAAG,CAAK,CAAC,CAC/C,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,EAAuB,GAAG,IAAqB,CACjD,EAAS,GAAY,CAAC,GAAG,EAAO,GAAG,CAAO,CAAC,CAC/C,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,EAAsB,EAAe,GAAG,IAAqB,CAC/D,EAAS,GAAY,CAAC,GAAG,EAAQ,MAAM,EAAG,CAAK,EAAG,GAAG,EAAO,GAAG,EAAQ,MAAM,CAAK,CAAC,CAAC,CACxF,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,EAAsB,GAAG,IAA4B,CACvD,IAAM,EAAO,IAAI,IAAI,CAAO,EAC5B,EAAS,GAAY,EAAQ,QAAQ,EAAG,IAAU,CAAC,EAAK,IAAI,CAAK,CAAC,CAAC,CACvE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,EAAuB,CAAE,OAAM,QAA+B,CAChE,EAAS,GAAY,CACjB,IAAM,EAAO,CAAC,GAAG,CAAO,EAClB,CAAC,GAAS,EAAK,OAAO,EAAM,CAAC,EAEnC,OADA,EAAK,OAAO,EAAI,EAAG,CAAK,EACjB,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,EAAuB,EAAe,IAAkB,CAC1D,EAAS,GAAY,EAAQ,KAAK,EAAO,IAAO,IAAM,EAAQ,EAAO,CAAM,CAAC,CAChF,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAAwB,GAAqB,EAAQ,CAAK,EAAG,CAAC,CAAC,EAE/D,GAAA,EAAA,EAAA,YAAA,CAAqB,GAA4C,CACnE,EAAS,GAAY,EAAQ,IAAI,CAAE,CAAC,CACxC,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,KAAgC,EAAQ,CAAC,CAAC,EAAG,CAAC,CAAC,EAiBrD,MAAO,CAAC,GAAA,EAAA,EAAA,QAAA,MAdG,CACH,SACA,UACA,SACA,SACA,UACA,UACA,WACA,QACA,OACJ,GACA,CAAC,EAAQ,EAAS,EAAQ,EAAQ,EAAS,EAAS,EAAU,EAAO,CAAK,CAGhE,CAAQ,CAC1B"}
1
+ {"version":3,"file":"use-list-state.cjs","names":[],"sources":["../../src/hooks/use-list-state.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface ReorderPayload {\n from: number;\n to: number;\n}\n\nexport interface ListStateHandlers<T> {\n append: (...items: T[]) => void;\n prepend: (...items: T[]) => void;\n insert: (index: number, ...items: T[]) => void;\n remove: (...indices: number[]) => void;\n reorder: (payload: ReorderPayload) => void;\n setItem: (index: number, item: T) => void;\n setState: (items: T[]) => void;\n apply: (fn: (item: T, index: number) => T) => void;\n clear: () => void;\n}\n\n/**\n * Manage an array as state with a rich set of immutable handlers.\n *\n * @typeParam T - element type.\n * @param initial - initial list (default `[]`).\n * @returns Tuple `[list, handlers]`.\n */\nexport function useListState<T>(initial: T[] = []): [T[], ListStateHandlers<T>] {\n const [list, setList] = useState<T[]>(initial);\n\n const append = useCallback((...items: T[]): void => {\n setList((current) => [...current, ...items]);\n }, []);\n\n const prepend = useCallback((...items: T[]): void => {\n setList((current) => [...items, ...current]);\n }, []);\n\n const insert = useCallback((index: number, ...items: T[]): void => {\n setList((current) => [...current.slice(0, index), ...items, ...current.slice(index)]);\n }, []);\n\n const remove = useCallback((...indices: number[]): void => {\n const drop = new Set(indices);\n setList((current) => current.filter((_, index) => !drop.has(index)));\n }, []);\n\n const reorder = useCallback(({ from, to }: ReorderPayload): void => {\n setList((current) => {\n const next = [...current];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n return next;\n });\n }, []);\n\n const setItem = useCallback((index: number, item: T): void => {\n setList((current) => current.map((value, i) => (i === index ? item : value)));\n }, []);\n\n const setState = useCallback((items: T[]): void => setList(items), []);\n\n const apply = useCallback((fn: (item: T, index: number) => T): void => {\n setList((current) => current.map(fn));\n }, []);\n\n const clear = useCallback((): void => setList([]), []);\n\n const handlers = useMemo<ListStateHandlers<T>>(\n () => ({\n append,\n prepend,\n insert,\n remove,\n reorder,\n setItem,\n setState,\n apply,\n clear,\n }),\n [append, prepend, insert, remove, reorder, setItem, setState, apply, clear],\n );\n\n return [list, handlers];\n}\n"],"mappings":"uBA0BA,SAAgB,EAAgB,EAAe,CAAC,EAAgC,CAC5E,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAc,CAAO,EAEvC,GAAA,EAAS,EAAA,YAAA,EAAa,GAAG,IAAqB,CAChD,EAAS,GAAY,CAAC,GAAG,EAAS,GAAG,CAAK,CAAC,CAC/C,EAAG,CAAC,CAAC,EAEC,GAAA,EAAU,EAAA,YAAA,EAAa,GAAG,IAAqB,CACjD,EAAS,GAAY,CAAC,GAAG,EAAO,GAAG,CAAO,CAAC,CAC/C,EAAG,CAAC,CAAC,EAEC,GAAA,EAAS,EAAA,YAAA,EAAa,EAAe,GAAG,IAAqB,CAC/D,EAAS,GAAY,CAAC,GAAG,EAAQ,MAAM,EAAG,CAAK,EAAG,GAAG,EAAO,GAAG,EAAQ,MAAM,CAAK,CAAC,CAAC,CACxF,EAAG,CAAC,CAAC,EAEC,GAAA,EAAS,EAAA,YAAA,EAAa,GAAG,IAA4B,CACvD,IAAM,EAAO,IAAI,IAAI,CAAO,EAC5B,EAAS,GAAY,EAAQ,QAAQ,EAAG,IAAU,CAAC,EAAK,IAAI,CAAK,CAAC,CAAC,CACvE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAU,EAAA,YAAA,EAAa,CAAE,OAAM,QAA+B,CAChE,EAAS,GAAY,CACjB,IAAM,EAAO,CAAC,GAAG,CAAO,EAClB,CAAC,GAAS,EAAK,OAAO,EAAM,CAAC,EAEnC,OADA,EAAK,OAAO,EAAI,EAAG,CAAK,EACjB,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAU,EAAA,YAAA,EAAa,EAAe,IAAkB,CAC1D,EAAS,GAAY,EAAQ,KAAK,EAAO,IAAO,IAAM,EAAQ,EAAO,CAAM,CAAC,CAChF,EAAG,CAAC,CAAC,EAEC,GAAA,EAAW,EAAA,YAAA,CAAa,GAAqB,EAAQ,CAAK,EAAG,CAAC,CAAC,EAE/D,GAAA,EAAQ,EAAA,YAAA,CAAa,GAA4C,CACnE,EAAS,GAAY,EAAQ,IAAI,CAAE,CAAC,CACxC,EAAG,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,KAAwB,EAAQ,CAAC,CAAC,EAAG,CAAC,CAAC,EAiBrD,MAAO,CAAC,GAAA,EAfS,EAAA,QAAA,MACN,CACH,SACA,UACA,SACA,SACA,UACA,UACA,WACA,QACA,OACJ,GACA,CAAC,EAAQ,EAAS,EAAQ,EAAQ,EAAS,EAAS,EAAU,EAAO,CAAK,CAGhE,CAAQ,CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-local-storage.cjs","names":[],"sources":["../../src/hooks/use-local-storage.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\nexport type LocalStorageOptions<T> = {\n serialize?: (value: T) => string;\n deserialize?: (raw: string) => T;\n};\n\nconst defaultSerialize = JSON.stringify;\nconst defaultDeserialize = <T>(raw: string): T => JSON.parse(raw) as T;\n\n/**\n * State synced with `localStorage`. SSR-safe — initial render returns the\n * provided default; the stored value is hydrated after mount. Updates to the\n * same key in other tabs are picked up via the `storage` event.\n *\n * @tempest-limits empty-catch — the three swallows here are the hook's contract.\n * A write that hits the quota (or Safari private mode) still updates React state,\n * so the session keeps working and only persistence is lost; a `storage` event\n * carrying a value this tab cannot deserialize is another tab's bug, and the\n * useful response is to keep the value already rendered rather than tear the\n * component down over a foreign write.\n *\n * @param key - localStorage key.\n * @param defaultValue - value used when nothing is stored or in SSR.\n * @param options - custom `serialize` / `deserialize` (default JSON).\n * @returns Tuple `[value, setValue, remove]`.\n */\nexport function useLocalStorage<T>(\n key: string,\n defaultValue: T,\n options: LocalStorageOptions<T> = {},\n): [T, (value: T | ((prev: T) => T)) => void, () => void] {\n const serialize = options.serialize ?? defaultSerialize;\n const deserialize = options.deserialize ?? defaultDeserialize;\n\n const read = useCallback((): T => {\n if (typeof window === \"undefined\") return defaultValue;\n try {\n const raw = window.localStorage.getItem(key);\n if (raw === null) return defaultValue;\n return deserialize(raw);\n } catch {\n return defaultValue;\n }\n }, [key, defaultValue, deserialize]);\n\n const [value, setStored] = useState<T>(read);\n\n useEffect(() => {\n setStored(read());\n }, [read]);\n\n const setValue = useCallback(\n (next: T | ((prev: T) => T)): void => {\n setStored((current) => {\n const resolved =\n typeof next === \"function\" ? (next as (prev: T) => T)(current) : next;\n try {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(key, serialize(resolved));\n }\n } catch {\n /* empty */\n }\n return resolved;\n });\n },\n [key, serialize],\n );\n\n const remove = useCallback((): void => {\n try {\n if (typeof window !== \"undefined\") {\n window.localStorage.removeItem(key);\n }\n } catch {\n /* empty */\n }\n setStored(defaultValue);\n }, [key, defaultValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const onStorage = (event: StorageEvent): void => {\n if (event.key !== key) return;\n if (event.newValue === null) {\n setStored(defaultValue);\n return;\n }\n try {\n setStored(deserialize(event.newValue));\n } catch {\n /* empty */\n }\n };\n window.addEventListener(\"storage\", onStorage);\n return () => window.removeEventListener(\"storage\", onStorage);\n }, [key, defaultValue, deserialize]);\n\n return [value, setValue, remove];\n}\n"],"mappings":"uBAOA,IAAM,EAAmB,KAAK,UACxB,EAAyB,GAAmB,KAAK,MAAM,CAAG,EAmBhE,SAAgB,EACZ,EACA,EACA,EAAkC,CAAC,EACmB,CACtD,IAAM,EAAY,EAAQ,WAAa,EACjC,EAAc,EAAQ,aAAe,EAErC,GAAA,EAAA,EAAA,YAAA,KAA4B,CAC9B,GAAI,OAAO,OAAW,IAAa,OAAO,EAC1C,GAAI,CACA,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAG,EAE3C,OADI,IAAQ,KAAa,EAClB,EAAY,CAAG,CAC1B,MAAQ,CACJ,OAAO,CACX,CACJ,EAAG,CAAC,EAAK,EAAc,CAAW,CAAC,EAE7B,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAyB,CAAI,GAE3C,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAU,EAAK,CAAC,CACpB,EAAG,CAAC,CAAI,CAAC,EAET,IAAM,GAAA,EAAA,EAAA,YAAA,CACD,GAAqC,CAClC,EAAW,GAAY,CACnB,IAAM,EACF,OAAO,GAAS,WAAc,EAAwB,CAAO,EAAI,EACrE,GAAI,CACI,OAAO,OAAW,KAClB,OAAO,aAAa,QAAQ,EAAK,EAAU,CAAQ,CAAC,CAE5D,MAAQ,CAER,CACA,OAAO,CACX,CAAC,CACL,EACA,CAAC,EAAK,CAAS,CACnB,EAEM,GAAA,EAAA,EAAA,YAAA,KAAiC,CACnC,GAAI,CACI,OAAO,OAAW,KAClB,OAAO,aAAa,WAAW,CAAG,CAE1C,MAAQ,CAER,CACA,EAAU,CAAY,CAC1B,EAAG,CAAC,EAAK,CAAY,CAAC,EAoBtB,OAlBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAa,GAA8B,CACzC,KAAM,MAAQ,EAClB,IAAI,EAAM,WAAa,KAAM,CACzB,EAAU,CAAY,EACtB,MACJ,CACA,GAAI,CACA,EAAU,EAAY,EAAM,QAAQ,CAAC,CACzC,MAAQ,CAER,CALA,CAMJ,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,OAAO,oBAAoB,UAAW,CAAS,CAChE,EAAG,CAAC,EAAK,EAAc,CAAW,CAAC,EAE5B,CAAC,EAAO,EAAU,CAAM,CACnC"}
1
+ {"version":3,"file":"use-local-storage.cjs","names":[],"sources":["../../src/hooks/use-local-storage.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\nexport type LocalStorageOptions<T> = {\n serialize?: (value: T) => string;\n deserialize?: (raw: string) => T;\n};\n\nconst defaultSerialize = JSON.stringify;\nconst defaultDeserialize = <T>(raw: string): T => JSON.parse(raw) as T;\n\n/**\n * State synced with `localStorage`. SSR-safe — initial render returns the\n * provided default; the stored value is hydrated after mount. Updates to the\n * same key in other tabs are picked up via the `storage` event.\n *\n * @tempest-limits empty-catch — the three swallows here are the hook's contract.\n * A write that hits the quota (or Safari private mode) still updates React state,\n * so the session keeps working and only persistence is lost; a `storage` event\n * carrying a value this tab cannot deserialize is another tab's bug, and the\n * useful response is to keep the value already rendered rather than tear the\n * component down over a foreign write.\n *\n * @param key - localStorage key.\n * @param defaultValue - value used when nothing is stored or in SSR.\n * @param options - custom `serialize` / `deserialize` (default JSON).\n * @returns Tuple `[value, setValue, remove]`.\n */\nexport function useLocalStorage<T>(\n key: string,\n defaultValue: T,\n options: LocalStorageOptions<T> = {},\n): [T, (value: T | ((prev: T) => T)) => void, () => void] {\n const serialize = options.serialize ?? defaultSerialize;\n const deserialize = options.deserialize ?? defaultDeserialize;\n\n const read = useCallback((): T => {\n if (typeof window === \"undefined\") return defaultValue;\n try {\n const raw = window.localStorage.getItem(key);\n if (raw === null) return defaultValue;\n return deserialize(raw);\n } catch {\n return defaultValue;\n }\n }, [key, defaultValue, deserialize]);\n\n const [value, setStored] = useState<T>(read);\n\n useEffect(() => {\n setStored(read());\n }, [read]);\n\n const setValue = useCallback(\n (next: T | ((prev: T) => T)): void => {\n setStored((current) => {\n const resolved =\n typeof next === \"function\" ? (next as (prev: T) => T)(current) : next;\n try {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(key, serialize(resolved));\n }\n } catch {\n /* empty */\n }\n return resolved;\n });\n },\n [key, serialize],\n );\n\n const remove = useCallback((): void => {\n try {\n if (typeof window !== \"undefined\") {\n window.localStorage.removeItem(key);\n }\n } catch {\n /* empty */\n }\n setStored(defaultValue);\n }, [key, defaultValue]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const onStorage = (event: StorageEvent): void => {\n if (event.key !== key) return;\n if (event.newValue === null) {\n setStored(defaultValue);\n return;\n }\n try {\n setStored(deserialize(event.newValue));\n } catch {\n /* empty */\n }\n };\n window.addEventListener(\"storage\", onStorage);\n return () => window.removeEventListener(\"storage\", onStorage);\n }, [key, defaultValue, deserialize]);\n\n return [value, setValue, remove];\n}\n"],"mappings":"uBAOA,IAAM,EAAmB,KAAK,UACxB,EAAyB,GAAmB,KAAK,MAAM,CAAG,EAmBhE,SAAgB,EACZ,EACA,EACA,EAAkC,CAAC,EACmB,CACtD,IAAM,EAAY,EAAQ,WAAa,EACjC,EAAc,EAAQ,aAAe,EAErC,GAAA,EAAO,EAAA,YAAA,KAAqB,CAC9B,GAAI,OAAO,OAAW,IAAa,OAAO,EAC1C,GAAI,CACA,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAG,EAE3C,OADI,IAAQ,KAAa,EAClB,EAAY,CAAG,CAC1B,MAAQ,CACJ,OAAO,CACX,CACJ,EAAG,CAAC,EAAK,EAAc,CAAW,CAAC,EAE7B,CAAC,EAAO,IAAA,EAAa,EAAA,SAAA,CAAY,CAAI,GAE3C,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAU,EAAK,CAAC,CACpB,EAAG,CAAC,CAAI,CAAC,EAET,IAAM,GAAA,EAAW,EAAA,YAAA,CACZ,GAAqC,CAClC,EAAW,GAAY,CACnB,IAAM,EACF,OAAO,GAAS,WAAc,EAAwB,CAAO,EAAI,EACrE,GAAI,CACI,OAAO,OAAW,KAClB,OAAO,aAAa,QAAQ,EAAK,EAAU,CAAQ,CAAC,CAE5D,MAAQ,CAER,CACA,OAAO,CACX,CAAC,CACL,EACA,CAAC,EAAK,CAAS,CACnB,EAEM,GAAA,EAAS,EAAA,YAAA,KAAwB,CACnC,GAAI,CACI,OAAO,OAAW,KAClB,OAAO,aAAa,WAAW,CAAG,CAE1C,MAAQ,CAER,CACA,EAAU,CAAY,CAC1B,EAAG,CAAC,EAAK,CAAY,CAAC,EAoBtB,OAlBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAa,GAA8B,CACzC,KAAM,MAAQ,EAClB,IAAI,EAAM,WAAa,KAAM,CACzB,EAAU,CAAY,EACtB,MACJ,CACA,GAAI,CACA,EAAU,EAAY,EAAM,QAAQ,CAAC,CACzC,MAAQ,CAER,CALA,CAMJ,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,OAAO,oBAAoB,UAAW,CAAS,CAChE,EAAG,CAAC,EAAK,EAAc,CAAW,CAAC,EAE5B,CAAC,EAAO,EAAU,CAAM,CACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-long-press-handlers.cjs","names":[],"sources":["../../src/hooks/use-long-press-handlers.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from \"react\";\n\n/**\n * DOM event handlers produced by {@link useLongPressHandlers}, ready to spread\n * onto a JSX element (`<div {...handlers}>`).\n */\nexport interface LongPressHandlers {\n /** Starts the hold timer on mouse press. */\n onMouseDown: (event: ReactMouseEvent) => void;\n /** Cancels the pending hold when the mouse is released. */\n onMouseUp: () => void;\n /** Cancels the pending hold when the pointer leaves the element. */\n onMouseLeave: () => void;\n /** Starts the hold timer on touch start. */\n onTouchStart: (event: ReactTouchEvent) => void;\n /** Cancels the pending hold when the touch ends. */\n onTouchEnd: () => void;\n /** Cancels the pending hold when the finger moves. */\n onTouchMove: () => void;\n /** Fires the long-press immediately (desktop right-click parity). */\n onContextMenu: (event: ReactMouseEvent) => void;\n}\n\n/** Options accepted by {@link useLongPressHandlers}. */\nexport interface UseLongPressHandlersOptions {\n /** Hold duration in ms before `onLongPress` fires. Defaults to `500`. */\n delayMs?: number;\n /** When `true`, the hook is inert and every handler is a no-op. */\n disabled?: boolean;\n}\n\n/** Return shape of {@link useLongPressHandlers}. */\nexport type UseLongPressHandlersResult = LongPressHandlers & {\n /**\n * Reads whether the most recent interaction fired a long-press. Use it in\n * the element's `onClick` to suppress the click that follows a long-press.\n */\n wasLongPress: () => boolean;\n};\n\n/**\n * Returns DOM event handlers that detect a long press (touch or mouse hold)\n * and invoke `onLongPress` once after `delayMs`.\n *\n * Cancels on touch movement or pointer release. Also wires `contextmenu` to\n * fire `onLongPress` immediately so a right-click on desktop opens selection\n * mode the same way an Android long-press does on mobile.\n *\n * The hook does not `preventDefault` on the underlying click — the consumer\n * decides whether the subsequent click should still fire. Track that with the\n * returned `wasLongPress()` guard to tell \"long-press fired\" apart from a\n * \"regular click\".\n *\n * Unlike {@link useLongPress}, which attaches pointer listeners to a `ref`,\n * this variant returns handler props you spread directly onto an element and\n * exposes a `wasLongPress()` guard.\n *\n * @param onLongPress - Callback invoked once the hold threshold is reached.\n * @param options - Optional `delayMs` and `disabled` flags.\n * @returns The spreadable handlers plus a `wasLongPress()` guard.\n *\n * @example\n * const longPress = useLongPressHandlers(() => enterSelectionMode(), { delayMs: 600 });\n * <button {...longPress} onClick={() => { if (!longPress.wasLongPress()) navigate(); }}>\n * Item\n * </button>\n */\nexport function useLongPressHandlers(\n onLongPress: () => void,\n options: UseLongPressHandlersOptions = {},\n): UseLongPressHandlersResult {\n const { delayMs = 500, disabled = false } = options;\n const timerRef = useRef<number | null>(null);\n const firedRef = useRef<boolean>(false);\n const callbackRef = useRef<() => void>(onLongPress);\n useEffect(() => {\n callbackRef.current = onLongPress;\n }, [onLongPress]);\n\n const clear = useCallback((): void => {\n if (timerRef.current !== null) {\n window.clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const start = useCallback((): void => {\n if (disabled) return;\n firedRef.current = false;\n clear();\n timerRef.current = window.setTimeout(() => {\n firedRef.current = true;\n callbackRef.current();\n }, delayMs);\n }, [disabled, clear, delayMs]);\n\n const onContextMenu = useCallback(\n (event: ReactMouseEvent): void => {\n if (disabled) return;\n event.preventDefault();\n firedRef.current = true;\n callbackRef.current();\n },\n [disabled],\n );\n\n return {\n onMouseDown: start,\n onMouseUp: clear,\n onMouseLeave: clear,\n onTouchStart: start,\n onTouchEnd: clear,\n onTouchMove: clear,\n onContextMenu,\n wasLongPress: () => firedRef.current,\n };\n}\n"],"mappings":"uBAoEA,SAAgB,EACZ,EACA,EAAuC,CAAC,EACd,CAC1B,GAAM,CAAE,UAAU,IAAK,WAAW,IAAU,EACtC,GAAA,EAAA,EAAA,OAAA,CAAiC,IAAI,EACrC,GAAA,EAAA,EAAA,OAAA,CAA2B,EAAK,EAChC,GAAA,EAAA,EAAA,OAAA,CAAiC,CAAW,GAClD,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAY,QAAU,CAC1B,EAAG,CAAC,CAAW,CAAC,EAEhB,IAAM,GAAA,EAAA,EAAA,YAAA,KAAgC,CAC9B,EAAS,UAAY,OACrB,OAAO,aAAa,EAAS,OAAO,EACpC,EAAS,QAAU,KAE3B,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,KAAgC,CAC9B,IACJ,EAAS,QAAU,GACnB,EAAM,EACN,EAAS,QAAU,OAAO,eAAiB,CACvC,EAAS,QAAU,GACnB,EAAY,QAAQ,CACxB,EAAG,CAAO,EACd,EAAG,CAAC,EAAU,EAAO,CAAO,CAAC,EAY7B,MAAO,CACH,YAAa,EACb,UAAW,EACX,aAAc,EACd,aAAc,EACd,WAAY,EACZ,YAAa,EACb,eAAA,EAAA,EAAA,YAAA,CAhBC,GAAiC,CAC1B,IACJ,EAAM,eAAe,EACrB,EAAS,QAAU,GACnB,EAAY,QAAQ,EACxB,EACA,CAAC,CAAQ,CAUT,EACA,iBAAoB,EAAS,OACjC,CACJ"}
1
+ {"version":3,"file":"use-long-press-handlers.cjs","names":[],"sources":["../../src/hooks/use-long-press-handlers.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from \"react\";\n\n/**\n * DOM event handlers produced by {@link useLongPressHandlers}, ready to spread\n * onto a JSX element (`<div {...handlers}>`).\n */\nexport interface LongPressHandlers {\n /** Starts the hold timer on mouse press. */\n onMouseDown: (event: ReactMouseEvent) => void;\n /** Cancels the pending hold when the mouse is released. */\n onMouseUp: () => void;\n /** Cancels the pending hold when the pointer leaves the element. */\n onMouseLeave: () => void;\n /** Starts the hold timer on touch start. */\n onTouchStart: (event: ReactTouchEvent) => void;\n /** Cancels the pending hold when the touch ends. */\n onTouchEnd: () => void;\n /** Cancels the pending hold when the finger moves. */\n onTouchMove: () => void;\n /** Fires the long-press immediately (desktop right-click parity). */\n onContextMenu: (event: ReactMouseEvent) => void;\n}\n\n/** Options accepted by {@link useLongPressHandlers}. */\nexport interface UseLongPressHandlersOptions {\n /** Hold duration in ms before `onLongPress` fires. Defaults to `500`. */\n delayMs?: number;\n /** When `true`, the hook is inert and every handler is a no-op. */\n disabled?: boolean;\n}\n\n/** Return shape of {@link useLongPressHandlers}. */\nexport type UseLongPressHandlersResult = LongPressHandlers & {\n /**\n * Reads whether the most recent interaction fired a long-press. Use it in\n * the element's `onClick` to suppress the click that follows a long-press.\n */\n wasLongPress: () => boolean;\n};\n\n/**\n * Returns DOM event handlers that detect a long press (touch or mouse hold)\n * and invoke `onLongPress` once after `delayMs`.\n *\n * Cancels on touch movement or pointer release. Also wires `contextmenu` to\n * fire `onLongPress` immediately so a right-click on desktop opens selection\n * mode the same way an Android long-press does on mobile.\n *\n * The hook does not `preventDefault` on the underlying click — the consumer\n * decides whether the subsequent click should still fire. Track that with the\n * returned `wasLongPress()` guard to tell \"long-press fired\" apart from a\n * \"regular click\".\n *\n * Unlike {@link useLongPress}, which attaches pointer listeners to a `ref`,\n * this variant returns handler props you spread directly onto an element and\n * exposes a `wasLongPress()` guard.\n *\n * @param onLongPress - Callback invoked once the hold threshold is reached.\n * @param options - Optional `delayMs` and `disabled` flags.\n * @returns The spreadable handlers plus a `wasLongPress()` guard.\n *\n * @example\n * const longPress = useLongPressHandlers(() => enterSelectionMode(), { delayMs: 600 });\n * <button {...longPress} onClick={() => { if (!longPress.wasLongPress()) navigate(); }}>\n * Item\n * </button>\n */\nexport function useLongPressHandlers(\n onLongPress: () => void,\n options: UseLongPressHandlersOptions = {},\n): UseLongPressHandlersResult {\n const { delayMs = 500, disabled = false } = options;\n const timerRef = useRef<number | null>(null);\n const firedRef = useRef<boolean>(false);\n const callbackRef = useRef<() => void>(onLongPress);\n useEffect(() => {\n callbackRef.current = onLongPress;\n }, [onLongPress]);\n\n const clear = useCallback((): void => {\n if (timerRef.current !== null) {\n window.clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const start = useCallback((): void => {\n if (disabled) return;\n firedRef.current = false;\n clear();\n timerRef.current = window.setTimeout(() => {\n firedRef.current = true;\n callbackRef.current();\n }, delayMs);\n }, [disabled, clear, delayMs]);\n\n const onContextMenu = useCallback(\n (event: ReactMouseEvent): void => {\n if (disabled) return;\n event.preventDefault();\n firedRef.current = true;\n callbackRef.current();\n },\n [disabled],\n );\n\n return {\n onMouseDown: start,\n onMouseUp: clear,\n onMouseLeave: clear,\n onTouchStart: start,\n onTouchEnd: clear,\n onTouchMove: clear,\n onContextMenu,\n wasLongPress: () => firedRef.current,\n };\n}\n"],"mappings":"uBAoEA,SAAgB,EACZ,EACA,EAAuC,CAAC,EACd,CAC1B,GAAM,CAAE,UAAU,IAAK,WAAW,IAAU,EACtC,GAAA,EAAW,EAAA,OAAA,CAAsB,IAAI,EACrC,GAAA,EAAW,EAAA,OAAA,CAAgB,EAAK,EAChC,GAAA,EAAc,EAAA,OAAA,CAAmB,CAAW,GAClD,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAY,QAAU,CAC1B,EAAG,CAAC,CAAW,CAAC,EAEhB,IAAM,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAC9B,EAAS,UAAY,OACrB,OAAO,aAAa,EAAS,OAAO,EACpC,EAAS,QAAU,KAE3B,EAAG,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAC9B,IACJ,EAAS,QAAU,GACnB,EAAM,EACN,EAAS,QAAU,OAAO,eAAiB,CACvC,EAAS,QAAU,GACnB,EAAY,QAAQ,CACxB,EAAG,CAAO,EACd,EAAG,CAAC,EAAU,EAAO,CAAO,CAAC,EAY7B,MAAO,CACH,YAAa,EACb,UAAW,EACX,aAAc,EACd,aAAc,EACd,WAAY,EACZ,YAAa,EACb,eAAA,EAjBkB,EAAA,YAAA,CACjB,GAAiC,CAC1B,IACJ,EAAM,eAAe,EACrB,EAAS,QAAU,GACnB,EAAY,QAAQ,EACxB,EACA,CAAC,CAAQ,CAUT,EACA,iBAAoB,EAAS,OACjC,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-long-press.cjs","names":[],"sources":["../../src/hooks/use-long-press.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface UseLongPressOptions {\n /** Press duration in ms. Default `500`. */\n delay?: number;\n /** Pixel threshold — finger/mouse movement beyond this cancels the press. Default `10`. */\n moveThreshold?: number;\n}\n\n/**\n * Detect long-press / long-tap gestures. Fires `fn` after `delay` ms\n * while the element is held. Cancels on move beyond `moveThreshold` or\n * pointer up before `delay`.\n *\n * @example\n * const ref = useRef<HTMLDivElement>(null);\n * useLongPress(ref, () => openContextMenu(), { delay: 600 });\n */\nexport function useLongPress<T extends HTMLElement>(\n ref: RefObject<T | null>,\n fn: () => void,\n options: UseLongPressOptions = {},\n): void {\n const { delay = 500, moveThreshold = 10 } = options;\n const fnRef = useRef(fn);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n\n useEffect(() => {\n const node = ref.current;\n if (!node) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let startX = 0;\n let startY = 0;\n\n const cancel = (): void => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const onDown = (event: PointerEvent): void => {\n startX = event.clientX;\n startY = event.clientY;\n cancel();\n timer = setTimeout(() => {\n timer = null;\n fnRef.current();\n }, delay);\n };\n\n const onMove = (event: PointerEvent): void => {\n if (timer === null) return;\n const dx = event.clientX - startX;\n const dy = event.clientY - startY;\n if (Math.hypot(dx, dy) > moveThreshold) cancel();\n };\n\n node.addEventListener(\"pointerdown\", onDown);\n node.addEventListener(\"pointermove\", onMove);\n node.addEventListener(\"pointerup\", cancel);\n node.addEventListener(\"pointercancel\", cancel);\n node.addEventListener(\"pointerleave\", cancel);\n return () => {\n cancel();\n node.removeEventListener(\"pointerdown\", onDown);\n node.removeEventListener(\"pointermove\", onMove);\n node.removeEventListener(\"pointerup\", cancel);\n node.removeEventListener(\"pointercancel\", cancel);\n node.removeEventListener(\"pointerleave\", cancel);\n };\n }, [ref, delay, moveThreshold]);\n}\n"],"mappings":"uBAmBA,SAAgB,EACZ,EACA,EACA,EAA+B,CAAC,EAC5B,CACJ,GAAM,CAAE,QAAQ,IAAK,gBAAgB,IAAO,EACtC,GAAA,EAAA,EAAA,OAAA,CAAe,CAAE,GACvB,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAM,QAAU,CACpB,EAAG,CAAC,CAAE,CAAC,GAEP,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAO,EAAI,QACjB,GAAI,CAAC,EAAM,OACX,IAAI,EAA8C,KAC9C,EAAS,EACT,EAAS,EAEP,MAAqB,CACvB,AAEI,KADA,aAAa,CAAK,EACV,KAEhB,EAEM,EAAU,GAA8B,CAC1C,EAAS,EAAM,QACf,EAAS,EAAM,QACf,EAAO,EACP,EAAQ,eAAiB,CACrB,EAAQ,KACR,EAAM,QAAQ,CAClB,EAAG,CAAK,CACZ,EAEM,EAAU,GAA8B,CAC1C,GAAI,IAAU,KAAM,OACpB,IAAM,EAAK,EAAM,QAAU,EACrB,EAAK,EAAM,QAAU,EACvB,KAAK,MAAM,EAAI,CAAE,EAAI,GAAe,EAAO,CACnD,EAOA,OALA,EAAK,iBAAiB,cAAe,CAAM,EAC3C,EAAK,iBAAiB,cAAe,CAAM,EAC3C,EAAK,iBAAiB,YAAa,CAAM,EACzC,EAAK,iBAAiB,gBAAiB,CAAM,EAC7C,EAAK,iBAAiB,eAAgB,CAAM,MAC/B,CACT,EAAO,EACP,EAAK,oBAAoB,cAAe,CAAM,EAC9C,EAAK,oBAAoB,cAAe,CAAM,EAC9C,EAAK,oBAAoB,YAAa,CAAM,EAC5C,EAAK,oBAAoB,gBAAiB,CAAM,EAChD,EAAK,oBAAoB,eAAgB,CAAM,CACnD,CACJ,EAAG,CAAC,EAAK,EAAO,CAAa,CAAC,CAClC"}
1
+ {"version":3,"file":"use-long-press.cjs","names":[],"sources":["../../src/hooks/use-long-press.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface UseLongPressOptions {\n /** Press duration in ms. Default `500`. */\n delay?: number;\n /** Pixel threshold — finger/mouse movement beyond this cancels the press. Default `10`. */\n moveThreshold?: number;\n}\n\n/**\n * Detect long-press / long-tap gestures. Fires `fn` after `delay` ms\n * while the element is held. Cancels on move beyond `moveThreshold` or\n * pointer up before `delay`.\n *\n * @example\n * const ref = useRef<HTMLDivElement>(null);\n * useLongPress(ref, () => openContextMenu(), { delay: 600 });\n */\nexport function useLongPress<T extends HTMLElement>(\n ref: RefObject<T | null>,\n fn: () => void,\n options: UseLongPressOptions = {},\n): void {\n const { delay = 500, moveThreshold = 10 } = options;\n const fnRef = useRef(fn);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n\n useEffect(() => {\n const node = ref.current;\n if (!node) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let startX = 0;\n let startY = 0;\n\n const cancel = (): void => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const onDown = (event: PointerEvent): void => {\n startX = event.clientX;\n startY = event.clientY;\n cancel();\n timer = setTimeout(() => {\n timer = null;\n fnRef.current();\n }, delay);\n };\n\n const onMove = (event: PointerEvent): void => {\n if (timer === null) return;\n const dx = event.clientX - startX;\n const dy = event.clientY - startY;\n if (Math.hypot(dx, dy) > moveThreshold) cancel();\n };\n\n node.addEventListener(\"pointerdown\", onDown);\n node.addEventListener(\"pointermove\", onMove);\n node.addEventListener(\"pointerup\", cancel);\n node.addEventListener(\"pointercancel\", cancel);\n node.addEventListener(\"pointerleave\", cancel);\n return () => {\n cancel();\n node.removeEventListener(\"pointerdown\", onDown);\n node.removeEventListener(\"pointermove\", onMove);\n node.removeEventListener(\"pointerup\", cancel);\n node.removeEventListener(\"pointercancel\", cancel);\n node.removeEventListener(\"pointerleave\", cancel);\n };\n }, [ref, delay, moveThreshold]);\n}\n"],"mappings":"uBAmBA,SAAgB,EACZ,EACA,EACA,EAA+B,CAAC,EAC5B,CACJ,GAAM,CAAE,QAAQ,IAAK,gBAAgB,IAAO,EACtC,GAAA,EAAQ,EAAA,OAAA,CAAO,CAAE,GACvB,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAM,QAAU,CACpB,EAAG,CAAC,CAAE,CAAC,GAEP,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAO,EAAI,QACjB,GAAI,CAAC,EAAM,OACX,IAAI,EAA8C,KAC9C,EAAS,EACT,EAAS,EAEP,MAAqB,CACvB,AAEI,KADA,aAAa,CAAK,EACV,KAEhB,EAEM,EAAU,GAA8B,CAC1C,EAAS,EAAM,QACf,EAAS,EAAM,QACf,EAAO,EACP,EAAQ,eAAiB,CACrB,EAAQ,KACR,EAAM,QAAQ,CAClB,EAAG,CAAK,CACZ,EAEM,EAAU,GAA8B,CAC1C,GAAI,IAAU,KAAM,OACpB,IAAM,EAAK,EAAM,QAAU,EACrB,EAAK,EAAM,QAAU,EACvB,KAAK,MAAM,EAAI,CAAE,EAAI,GAAe,EAAO,CACnD,EAOA,OALA,EAAK,iBAAiB,cAAe,CAAM,EAC3C,EAAK,iBAAiB,cAAe,CAAM,EAC3C,EAAK,iBAAiB,YAAa,CAAM,EACzC,EAAK,iBAAiB,gBAAiB,CAAM,EAC7C,EAAK,iBAAiB,eAAgB,CAAM,MAC/B,CACT,EAAO,EACP,EAAK,oBAAoB,cAAe,CAAM,EAC9C,EAAK,oBAAoB,cAAe,CAAM,EAC9C,EAAK,oBAAoB,YAAa,CAAM,EAC5C,EAAK,oBAAoB,gBAAiB,CAAM,EAChD,EAAK,oBAAoB,eAAgB,CAAM,CACnD,CACJ,EAAG,CAAC,EAAK,EAAO,CAAa,CAAC,CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-map.cjs","names":[],"sources":["../../src/hooks/use-map.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseMapResult<K, V> {\n map: ReadonlyMap<K, V>;\n set: (key: K, value: V) => void;\n delete: (key: K) => void;\n clear: () => void;\n get: (key: K) => V | undefined;\n has: (key: K) => boolean;\n size: number;\n}\n\n/**\n * Reactive `Map` wrapper. Mutating via `set` / `delete` / `clear` triggers a\n * re-render and yields a fresh `map` reference each time.\n *\n * @typeParam K - key type.\n * @typeParam V - value type.\n * @param initial - initial entries.\n * @returns `{ map, set, delete, clear, get, has, size }`.\n */\nexport function useMap<K, V>(initial?: Iterable<readonly [K, V]>): UseMapResult<K, V> {\n const [map, setMap] = useState<Map<K, V>>(() => new Map(initial));\n\n const set = useCallback((key: K, value: V): void => {\n setMap((current) => {\n const next = new Map(current);\n next.set(key, value);\n return next;\n });\n }, []);\n\n const remove = useCallback((key: K): void => {\n setMap((current) => {\n if (!current.has(key)) return current;\n const next = new Map(current);\n next.delete(key);\n return next;\n });\n }, []);\n\n const clear = useCallback((): void => {\n setMap((current) => (current.size === 0 ? current : new Map<K, V>()));\n }, []);\n\n const get = useCallback((key: K): V | undefined => map.get(key), [map]);\n const has = useCallback((key: K): boolean => map.has(key), [map]);\n\n return useMemo<UseMapResult<K, V>>(\n () => ({\n map,\n set,\n delete: remove,\n clear,\n get,\n has,\n size: map.size,\n }),\n [map, set, remove, clear, get, has],\n );\n}\n"],"mappings":"uBAqBA,SAAgB,EAAa,EAAyD,CAClF,GAAM,CAAC,EAAK,IAAA,EAAA,EAAA,SAAA,KAAoC,IAAI,IAAI,CAAO,CAAC,EAE1D,GAAA,EAAA,EAAA,YAAA,EAAmB,EAAQ,IAAmB,CAChD,EAAQ,GAAY,CAChB,IAAM,EAAO,IAAI,IAAI,CAAO,EAE5B,OADA,EAAK,IAAI,EAAK,CAAK,EACZ,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAAsB,GAAiB,CACzC,EAAQ,GAAY,CAChB,GAAI,CAAC,EAAQ,IAAI,CAAG,EAAG,OAAO,EAC9B,IAAM,EAAO,IAAI,IAAI,CAAO,EAE5B,OADA,EAAK,OAAO,CAAG,EACR,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,KAAgC,CAClC,EAAQ,GAAa,EAAQ,OAAS,EAAI,EAAU,IAAI,GAAY,CACxE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAAmB,GAA0B,EAAI,IAAI,CAAG,EAAG,CAAC,CAAG,CAAC,EAChE,GAAA,EAAA,EAAA,YAAA,CAAmB,GAAoB,EAAI,IAAI,CAAG,EAAG,CAAC,CAAG,CAAC,EAEhE,OAAA,EAAA,EAAA,QAAA,MACW,CACH,MACA,MACA,OAAQ,EACR,QACA,MACA,MACA,KAAM,EAAI,IACd,GACA,CAAC,EAAK,EAAK,EAAQ,EAAO,EAAK,CAAG,CACtC,CACJ"}
1
+ {"version":3,"file":"use-map.cjs","names":[],"sources":["../../src/hooks/use-map.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseMapResult<K, V> {\n map: ReadonlyMap<K, V>;\n set: (key: K, value: V) => void;\n delete: (key: K) => void;\n clear: () => void;\n get: (key: K) => V | undefined;\n has: (key: K) => boolean;\n size: number;\n}\n\n/**\n * Reactive `Map` wrapper. Mutating via `set` / `delete` / `clear` triggers a\n * re-render and yields a fresh `map` reference each time.\n *\n * @typeParam K - key type.\n * @typeParam V - value type.\n * @param initial - initial entries.\n * @returns `{ map, set, delete, clear, get, has, size }`.\n */\nexport function useMap<K, V>(initial?: Iterable<readonly [K, V]>): UseMapResult<K, V> {\n const [map, setMap] = useState<Map<K, V>>(() => new Map(initial));\n\n const set = useCallback((key: K, value: V): void => {\n setMap((current) => {\n const next = new Map(current);\n next.set(key, value);\n return next;\n });\n }, []);\n\n const remove = useCallback((key: K): void => {\n setMap((current) => {\n if (!current.has(key)) return current;\n const next = new Map(current);\n next.delete(key);\n return next;\n });\n }, []);\n\n const clear = useCallback((): void => {\n setMap((current) => (current.size === 0 ? current : new Map<K, V>()));\n }, []);\n\n const get = useCallback((key: K): V | undefined => map.get(key), [map]);\n const has = useCallback((key: K): boolean => map.has(key), [map]);\n\n return useMemo<UseMapResult<K, V>>(\n () => ({\n map,\n set,\n delete: remove,\n clear,\n get,\n has,\n size: map.size,\n }),\n [map, set, remove, clear, get, has],\n );\n}\n"],"mappings":"uBAqBA,SAAgB,EAAa,EAAyD,CAClF,GAAM,CAAC,EAAK,IAAA,EAAU,EAAA,SAAA,KAA0B,IAAI,IAAI,CAAO,CAAC,EAE1D,GAAA,EAAM,EAAA,YAAA,EAAa,EAAQ,IAAmB,CAChD,EAAQ,GAAY,CAChB,IAAM,EAAO,IAAI,IAAI,CAAO,EAE5B,OADA,EAAK,IAAI,EAAK,CAAK,EACZ,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAS,EAAA,YAAA,CAAa,GAAiB,CACzC,EAAQ,GAAY,CAChB,GAAI,CAAC,EAAQ,IAAI,CAAG,EAAG,OAAO,EAC9B,IAAM,EAAO,IAAI,IAAI,CAAO,EAE5B,OADA,EAAK,OAAO,CAAG,EACR,CACX,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAQ,GAAa,EAAQ,OAAS,EAAI,EAAU,IAAI,GAAY,CACxE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAM,EAAA,YAAA,CAAa,GAA0B,EAAI,IAAI,CAAG,EAAG,CAAC,CAAG,CAAC,EAChE,GAAA,EAAM,EAAA,YAAA,CAAa,GAAoB,EAAI,IAAI,CAAG,EAAG,CAAC,CAAG,CAAC,EAEhE,OAAA,EAAO,EAAA,QAAA,MACI,CACH,MACA,MACA,OAAQ,EACR,QACA,MACA,MACA,KAAM,EAAI,IACd,GACA,CAAC,EAAK,EAAK,EAAQ,EAAO,EAAK,CAAG,CACtC,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-media-devices.cjs","names":[],"sources":["../../src/hooks/use-media-devices.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\n/** One device the browser is willing to tell us about. */\nexport interface MediaDeviceOption {\n deviceId: string;\n /**\n * Human label, or `\"\"` until permission has been granted.\n *\n * See {@link UseMediaDevicesResult.labelsAvailable} — an empty label is not a\n * bug, it is the browser refusing to fingerprint the machine.\n */\n label: string;\n kind: MediaDeviceKind;\n groupId: string;\n}\n\n/** Value returned by {@link useMediaDevices}. */\nexport interface UseMediaDevicesResult {\n /** Every device, in the order the browser listed them. */\n devices: MediaDeviceOption[];\n /** Microphones and other audio sources. */\n audioInputs: MediaDeviceOption[];\n /** Speakers and headsets. Empty on browsers with no output routing (Safari, Firefox). */\n audioOutputs: MediaDeviceOption[];\n /** Cameras. */\n videoInputs: MediaDeviceOption[];\n /**\n * Whether labels are filled in.\n *\n * `false` means the list is real but anonymous — render a picker only after a\n * capture permission has been granted, or the user sees \"\" repeated N times.\n */\n labelsAvailable: boolean;\n /** `enumerateDevices` is unavailable in this browser. */\n supported: boolean;\n /** Re-enumerate. The hook already does this on `devicechange`. */\n refresh: () => void;\n}\n\nconst EMPTY: MediaDeviceOption[] = [];\n\n/**\n * List capture and playback devices, and keep the list fresh.\n *\n * Two things about `enumerateDevices` that decide how a picker must be built:\n *\n * 1. **Labels are gated behind permission.** Before the user grants a capture\n * permission, every `label` is `\"\"` — the list length and ids are real, the names\n * are not. A device picker rendered at that point is a column of blanks, so\n * `labelsAvailable` is exposed to gate it. Ask for the microphone first, then\n * show the picker.\n * 2. **The list changes while the page is open.** Plugging in a headset mid-recording\n * is the normal case, not an edge case, so the hook subscribes to `devicechange`\n * instead of enumerating once on mount.\n *\n * `audioOutputs` is empty on browsers with no output routing at all (Safari and\n * Firefox do not implement `setSinkId`); an empty array there means \"you cannot\n * offer this choice\", not \"no speakers\".\n *\n * @returns The device lists, whether labels are filled in, and a `refresh()`.\n *\n * @example\n * const { audioInputs, labelsAvailable } = useMediaDevices();\n * // ...after the mic permission is granted:\n * {labelsAvailable && <Select options={audioInputs.map((d) => ({ value: d.deviceId, label: d.label }))} />}\n */\nexport function useMediaDevices(): UseMediaDevicesResult {\n const [devices, setDevices] = useState<MediaDeviceOption[]>(EMPTY);\n const [supported, setSupported] = useState(false);\n const [token, setToken] = useState(0);\n\n const refresh = useCallback(() => setToken((value) => value + 1), []);\n\n useEffect(() => {\n let cancelled = false;\n\n if (\n typeof navigator === \"undefined\" ||\n !navigator.mediaDevices ||\n typeof navigator.mediaDevices.enumerateDevices !== \"function\"\n ) {\n setSupported(false);\n setDevices(EMPTY);\n return;\n }\n setSupported(true);\n const media = navigator.mediaDevices;\n\n async function read(): Promise<void> {\n try {\n const list = await media.enumerateDevices();\n if (cancelled) return;\n setDevices(\n list.map((device) => ({\n deviceId: device.deviceId,\n label: device.label,\n kind: device.kind,\n groupId: device.groupId,\n })),\n );\n } catch {\n if (!cancelled) setDevices(EMPTY);\n }\n }\n\n void read();\n const onChange = (): void => void read();\n media.addEventListener?.(\"devicechange\", onChange);\n\n return () => {\n cancelled = true;\n media.removeEventListener?.(\"devicechange\", onChange);\n };\n }, [token]);\n\n return {\n devices,\n audioInputs: devices.filter((device) => device.kind === \"audioinput\"),\n audioOutputs: devices.filter((device) => device.kind === \"audiooutput\"),\n videoInputs: devices.filter((device) => device.kind === \"videoinput\"),\n labelsAvailable: devices.length > 0 && devices.some((device) => device.label !== \"\"),\n supported,\n refresh,\n };\n}\n"],"mappings":"uBAuCA,IAAM,EAA6B,CAAC,EA2BpC,SAAgB,GAAyC,CACrD,GAAM,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAA4C,CAAK,EAC3D,CAAC,EAAW,IAAA,EAAA,EAAA,SAAA,CAAyB,EAAK,EAC1C,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAqB,CAAC,EAE9B,GAAA,EAAA,EAAA,YAAA,KAA4B,EAAU,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EA4CpE,OA1CA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GAEhB,GACI,OAAO,UAAc,KACrB,CAAC,UAAU,cACX,OAAO,UAAU,aAAa,kBAAqB,WACrD,CACE,EAAa,EAAK,EAClB,EAAW,CAAK,EAChB,MACJ,CACA,EAAa,EAAI,EACjB,IAAM,EAAQ,UAAU,aAExB,eAAe,GAAsB,CACjC,GAAI,CACA,IAAM,EAAO,MAAM,EAAM,iBAAiB,EAC1C,GAAI,EAAW,OACf,EACI,EAAK,IAAK,IAAY,CAClB,SAAU,EAAO,SACjB,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QAAS,EAAO,OACpB,EAAE,CACN,CACJ,MAAQ,CACC,GAAW,EAAW,CAAK,CACpC,CACJ,CAEA,EAAU,EACV,IAAM,MAAuB,KAAK,EAAK,EAGvC,OAFA,EAAM,mBAAmB,eAAgB,CAAQ,MAEpC,CACT,EAAY,GACZ,EAAM,sBAAsB,eAAgB,CAAQ,CACxD,CACJ,EAAG,CAAC,CAAK,CAAC,EAEH,CACH,UACA,YAAa,EAAQ,OAAQ,GAAW,EAAO,OAAS,YAAY,EACpE,aAAc,EAAQ,OAAQ,GAAW,EAAO,OAAS,aAAa,EACtE,YAAa,EAAQ,OAAQ,GAAW,EAAO,OAAS,YAAY,EACpE,gBAAiB,EAAQ,OAAS,GAAK,EAAQ,KAAM,GAAW,EAAO,QAAU,EAAE,EACnF,YACA,SACJ,CACJ"}
1
+ {"version":3,"file":"use-media-devices.cjs","names":[],"sources":["../../src/hooks/use-media-devices.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\n/** One device the browser is willing to tell us about. */\nexport interface MediaDeviceOption {\n deviceId: string;\n /**\n * Human label, or `\"\"` until permission has been granted.\n *\n * See {@link UseMediaDevicesResult.labelsAvailable} — an empty label is not a\n * bug, it is the browser refusing to fingerprint the machine.\n */\n label: string;\n kind: MediaDeviceKind;\n groupId: string;\n}\n\n/** Value returned by {@link useMediaDevices}. */\nexport interface UseMediaDevicesResult {\n /** Every device, in the order the browser listed them. */\n devices: MediaDeviceOption[];\n /** Microphones and other audio sources. */\n audioInputs: MediaDeviceOption[];\n /** Speakers and headsets. Empty on browsers with no output routing (Safari, Firefox). */\n audioOutputs: MediaDeviceOption[];\n /** Cameras. */\n videoInputs: MediaDeviceOption[];\n /**\n * Whether labels are filled in.\n *\n * `false` means the list is real but anonymous — render a picker only after a\n * capture permission has been granted, or the user sees \"\" repeated N times.\n */\n labelsAvailable: boolean;\n /** `enumerateDevices` is unavailable in this browser. */\n supported: boolean;\n /** Re-enumerate. The hook already does this on `devicechange`. */\n refresh: () => void;\n}\n\nconst EMPTY: MediaDeviceOption[] = [];\n\n/**\n * List capture and playback devices, and keep the list fresh.\n *\n * Two things about `enumerateDevices` that decide how a picker must be built:\n *\n * 1. **Labels are gated behind permission.** Before the user grants a capture\n * permission, every `label` is `\"\"` — the list length and ids are real, the names\n * are not. A device picker rendered at that point is a column of blanks, so\n * `labelsAvailable` is exposed to gate it. Ask for the microphone first, then\n * show the picker.\n * 2. **The list changes while the page is open.** Plugging in a headset mid-recording\n * is the normal case, not an edge case, so the hook subscribes to `devicechange`\n * instead of enumerating once on mount.\n *\n * `audioOutputs` is empty on browsers with no output routing at all (Safari and\n * Firefox do not implement `setSinkId`); an empty array there means \"you cannot\n * offer this choice\", not \"no speakers\".\n *\n * @returns The device lists, whether labels are filled in, and a `refresh()`.\n *\n * @example\n * const { audioInputs, labelsAvailable } = useMediaDevices();\n * // ...after the mic permission is granted:\n * {labelsAvailable && <Select options={audioInputs.map((d) => ({ value: d.deviceId, label: d.label }))} />}\n */\nexport function useMediaDevices(): UseMediaDevicesResult {\n const [devices, setDevices] = useState<MediaDeviceOption[]>(EMPTY);\n const [supported, setSupported] = useState(false);\n const [token, setToken] = useState(0);\n\n const refresh = useCallback(() => setToken((value) => value + 1), []);\n\n useEffect(() => {\n let cancelled = false;\n\n if (\n typeof navigator === \"undefined\" ||\n !navigator.mediaDevices ||\n typeof navigator.mediaDevices.enumerateDevices !== \"function\"\n ) {\n setSupported(false);\n setDevices(EMPTY);\n return;\n }\n setSupported(true);\n const media = navigator.mediaDevices;\n\n async function read(): Promise<void> {\n try {\n const list = await media.enumerateDevices();\n if (cancelled) return;\n setDevices(\n list.map((device) => ({\n deviceId: device.deviceId,\n label: device.label,\n kind: device.kind,\n groupId: device.groupId,\n })),\n );\n } catch {\n if (!cancelled) setDevices(EMPTY);\n }\n }\n\n void read();\n const onChange = (): void => void read();\n media.addEventListener?.(\"devicechange\", onChange);\n\n return () => {\n cancelled = true;\n media.removeEventListener?.(\"devicechange\", onChange);\n };\n }, [token]);\n\n return {\n devices,\n audioInputs: devices.filter((device) => device.kind === \"audioinput\"),\n audioOutputs: devices.filter((device) => device.kind === \"audiooutput\"),\n videoInputs: devices.filter((device) => device.kind === \"videoinput\"),\n labelsAvailable: devices.length > 0 && devices.some((device) => device.label !== \"\"),\n supported,\n refresh,\n };\n}\n"],"mappings":"uBAuCA,IAAM,EAA6B,CAAC,EA2BpC,SAAgB,GAAyC,CACrD,GAAM,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAA8B,CAAK,EAC3D,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAS,EAAK,EAC1C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,CAAC,EAE9B,GAAA,EAAU,EAAA,YAAA,KAAkB,EAAU,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EA4CpE,OA1CA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GAEhB,GACI,OAAO,UAAc,KACrB,CAAC,UAAU,cACX,OAAO,UAAU,aAAa,kBAAqB,WACrD,CACE,EAAa,EAAK,EAClB,EAAW,CAAK,EAChB,MACJ,CACA,EAAa,EAAI,EACjB,IAAM,EAAQ,UAAU,aAExB,eAAe,GAAsB,CACjC,GAAI,CACA,IAAM,EAAO,MAAM,EAAM,iBAAiB,EAC1C,GAAI,EAAW,OACf,EACI,EAAK,IAAK,IAAY,CAClB,SAAU,EAAO,SACjB,MAAO,EAAO,MACd,KAAM,EAAO,KACb,QAAS,EAAO,OACpB,EAAE,CACN,CACJ,MAAQ,CACC,GAAW,EAAW,CAAK,CACpC,CACJ,CAEA,EAAU,EACV,IAAM,MAAuB,KAAK,EAAK,EAGvC,OAFA,EAAM,mBAAmB,eAAgB,CAAQ,MAEpC,CACT,EAAY,GACZ,EAAM,sBAAsB,eAAgB,CAAQ,CACxD,CACJ,EAAG,CAAC,CAAK,CAAC,EAEH,CACH,UACA,YAAa,EAAQ,OAAQ,GAAW,EAAO,OAAS,YAAY,EACpE,aAAc,EAAQ,OAAQ,GAAW,EAAO,OAAS,aAAa,EACtE,YAAa,EAAQ,OAAQ,GAAW,EAAO,OAAS,YAAY,EACpE,gBAAiB,EAAQ,OAAS,GAAK,EAAQ,KAAM,GAAW,EAAO,QAAU,EAAE,EACnF,YACA,SACJ,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-media-permission.cjs","names":[],"sources":["../../src/hooks/use-media-permission.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\n/** Which capture permission to observe. */\nexport type MediaPermissionName = \"microphone\" | \"camera\";\n\n/**\n * Permission state, including the two cases the browser prompt cannot tell you\n * apart on its own.\n *\n * - `\"unknown\"` — the Permissions API is unavailable (Safari does not report\n * `microphone`), so the only way to learn the answer is to ask for the device.\n * - `\"prompt\"` — never asked. Asking now shows the browser prompt.\n * - `\"granted\"` / `\"denied\"` — settled. `\"denied\"` is **sticky**: calling\n * `getUserMedia` again does not re-prompt, it rejects immediately.\n */\nexport type MediaPermissionState = \"unknown\" | \"prompt\" | \"granted\" | \"denied\";\n\n/** Value returned by {@link useMediaPermission}. */\nexport interface UseMediaPermissionResult {\n /** Current state. Updates live when the user changes it in browser settings. */\n state: MediaPermissionState;\n /** Whether the Permissions API could answer at all. */\n supported: boolean;\n /** Re-read the permission. Rarely needed — the hook subscribes to changes. */\n refresh: () => void;\n}\n\n/**\n * Read a capture permission **without triggering the prompt**.\n *\n * This is the piece that makes a good permission flow possible. `getUserMedia` is\n * the only way to *get* a device, but calling it is also the only way to *ask*, and\n * a prompt fired on page load — before the user has pressed anything — is the\n * single most reliable way to earn a permanent \"Block\". Reading the state first\n * lets a page explain why it needs the microphone, and only then ask.\n *\n * It also separates \"never asked\" from \"denied\", which matters because they need\n * opposite UI: `\"prompt\"` gets a button, `\"denied\"` gets instructions for the\n * browser's site settings, since no amount of clicking will re-prompt.\n *\n * Safari does not expose `microphone` to the Permissions API and throws on the\n * query, which surfaces as `state: \"unknown\"` and `supported: false` — treat that\n * as \"you will have to ask to find out\", not as an error.\n *\n * @param name - Which permission to observe.\n * @returns The live state, whether it could be read, and a manual `refresh()`.\n *\n * @example\n * const { state } = useMediaPermission(\"microphone\");\n * if (state === \"denied\") return <p>Libere o microfone nas configurações do site.</p>;\n * return <button onClick={start}>Gravar</button>;\n */\nexport function useMediaPermission(name: MediaPermissionName): UseMediaPermissionResult {\n const [state, setState] = useState<MediaPermissionState>(\"unknown\");\n const [supported, setSupported] = useState(false);\n const [token, setToken] = useState(0);\n\n const refresh = useCallback(() => setToken((value) => value + 1), []);\n\n useEffect(() => {\n let cancelled = false;\n let status: PermissionStatus | null = null;\n const onChange = (): void => {\n if (!cancelled && status) setState(status.state as MediaPermissionState);\n };\n\n async function read(): Promise<void> {\n if (typeof navigator === \"undefined\" || !navigator.permissions?.query) {\n if (!cancelled) {\n setSupported(false);\n setState(\"unknown\");\n }\n return;\n }\n try {\n status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n if (cancelled) return;\n setSupported(true);\n setState(status.state as MediaPermissionState);\n status.addEventListener(\"change\", onChange);\n } catch {\n if (!cancelled) {\n setSupported(false);\n setState(\"unknown\");\n }\n }\n }\n\n void read();\n\n return () => {\n cancelled = true;\n status?.removeEventListener(\"change\", onChange);\n };\n }, [name, token]);\n\n return { state, supported, refresh };\n}\n"],"mappings":"uBAoDA,SAAgB,EAAmB,EAAqD,CACpF,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAA2C,SAAS,EAC5D,CAAC,EAAW,IAAA,EAAA,EAAA,SAAA,CAAyB,EAAK,EAC1C,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAqB,CAAC,EAE9B,GAAA,EAAA,EAAA,YAAA,KAA4B,EAAU,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EAyCpE,OAvCA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GACZ,EAAkC,KAChC,MAAuB,CACrB,CAAC,GAAa,GAAQ,EAAS,EAAO,KAA6B,CAC3E,EAEA,eAAe,GAAsB,CACjC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,aAAa,MAAO,CAC9D,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,GAEtB,MACJ,CACA,GAAI,CAIA,GAHA,EAAS,MAAM,UAAU,YAAY,MAAM,CACjC,MACV,CAAC,EACG,EAAW,OACf,EAAa,EAAI,EACjB,EAAS,EAAO,KAA6B,EAC7C,EAAO,iBAAiB,SAAU,CAAQ,CAC9C,MAAQ,CACC,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,EAE1B,CACJ,CAIA,OAFA,EAAU,MAEG,CACT,EAAY,GACZ,GAAQ,oBAAoB,SAAU,CAAQ,CAClD,CACJ,EAAG,CAAC,EAAM,CAAK,CAAC,EAET,CAAE,QAAO,YAAW,SAAQ,CACvC"}
1
+ {"version":3,"file":"use-media-permission.cjs","names":[],"sources":["../../src/hooks/use-media-permission.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\n/** Which capture permission to observe. */\nexport type MediaPermissionName = \"microphone\" | \"camera\";\n\n/**\n * Permission state, including the two cases the browser prompt cannot tell you\n * apart on its own.\n *\n * - `\"unknown\"` — the Permissions API is unavailable (Safari does not report\n * `microphone`), so the only way to learn the answer is to ask for the device.\n * - `\"prompt\"` — never asked. Asking now shows the browser prompt.\n * - `\"granted\"` / `\"denied\"` — settled. `\"denied\"` is **sticky**: calling\n * `getUserMedia` again does not re-prompt, it rejects immediately.\n */\nexport type MediaPermissionState = \"unknown\" | \"prompt\" | \"granted\" | \"denied\";\n\n/** Value returned by {@link useMediaPermission}. */\nexport interface UseMediaPermissionResult {\n /** Current state. Updates live when the user changes it in browser settings. */\n state: MediaPermissionState;\n /** Whether the Permissions API could answer at all. */\n supported: boolean;\n /** Re-read the permission. Rarely needed — the hook subscribes to changes. */\n refresh: () => void;\n}\n\n/**\n * Read a capture permission **without triggering the prompt**.\n *\n * This is the piece that makes a good permission flow possible. `getUserMedia` is\n * the only way to *get* a device, but calling it is also the only way to *ask*, and\n * a prompt fired on page load — before the user has pressed anything — is the\n * single most reliable way to earn a permanent \"Block\". Reading the state first\n * lets a page explain why it needs the microphone, and only then ask.\n *\n * It also separates \"never asked\" from \"denied\", which matters because they need\n * opposite UI: `\"prompt\"` gets a button, `\"denied\"` gets instructions for the\n * browser's site settings, since no amount of clicking will re-prompt.\n *\n * Safari does not expose `microphone` to the Permissions API and throws on the\n * query, which surfaces as `state: \"unknown\"` and `supported: false` — treat that\n * as \"you will have to ask to find out\", not as an error.\n *\n * @param name - Which permission to observe.\n * @returns The live state, whether it could be read, and a manual `refresh()`.\n *\n * @example\n * const { state } = useMediaPermission(\"microphone\");\n * if (state === \"denied\") return <p>Libere o microfone nas configurações do site.</p>;\n * return <button onClick={start}>Gravar</button>;\n */\nexport function useMediaPermission(name: MediaPermissionName): UseMediaPermissionResult {\n const [state, setState] = useState<MediaPermissionState>(\"unknown\");\n const [supported, setSupported] = useState(false);\n const [token, setToken] = useState(0);\n\n const refresh = useCallback(() => setToken((value) => value + 1), []);\n\n useEffect(() => {\n let cancelled = false;\n let status: PermissionStatus | null = null;\n const onChange = (): void => {\n if (!cancelled && status) setState(status.state as MediaPermissionState);\n };\n\n async function read(): Promise<void> {\n if (typeof navigator === \"undefined\" || !navigator.permissions?.query) {\n if (!cancelled) {\n setSupported(false);\n setState(\"unknown\");\n }\n return;\n }\n try {\n status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n if (cancelled) return;\n setSupported(true);\n setState(status.state as MediaPermissionState);\n status.addEventListener(\"change\", onChange);\n } catch {\n if (!cancelled) {\n setSupported(false);\n setState(\"unknown\");\n }\n }\n }\n\n void read();\n\n return () => {\n cancelled = true;\n status?.removeEventListener(\"change\", onChange);\n };\n }, [name, token]);\n\n return { state, supported, refresh };\n}\n"],"mappings":"uBAoDA,SAAgB,EAAmB,EAAqD,CACpF,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAA+B,SAAS,EAC5D,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAS,EAAK,EAC1C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,CAAC,EAE9B,GAAA,EAAU,EAAA,YAAA,KAAkB,EAAU,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EAyCpE,OAvCA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GACZ,EAAkC,KAChC,MAAuB,CACrB,CAAC,GAAa,GAAQ,EAAS,EAAO,KAA6B,CAC3E,EAEA,eAAe,GAAsB,CACjC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,aAAa,MAAO,CAC9D,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,GAEtB,MACJ,CACA,GAAI,CAIA,GAHA,EAAS,MAAM,UAAU,YAAY,MAAM,CACjC,MACV,CAAC,EACG,EAAW,OACf,EAAa,EAAI,EACjB,EAAS,EAAO,KAA6B,EAC7C,EAAO,iBAAiB,SAAU,CAAQ,CAC9C,MAAQ,CACC,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,EAE1B,CACJ,CAIA,OAFA,EAAU,MAEG,CACT,EAAY,GACZ,GAAQ,oBAAoB,SAAU,CAAQ,CAClD,CACJ,EAAG,CAAC,EAAM,CAAK,CAAC,EAET,CAAE,QAAO,YAAW,SAAQ,CACvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-media-query.cjs","names":[],"sources":["../../src/hooks/use-media-query.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/**\n * Subscribe to a CSS media query and re-render on match changes.\n *\n * @param query - A standard CSS media query string, e.g. \"(max-width: 768px)\".\n * @returns True when the query matches.\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const list = window.matchMedia(query);\n const handler = (event: MediaQueryListEvent): void => setMatches(event.matches);\n setMatches(list.matches);\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n }, [query]);\n\n return matches;\n}\n"],"mappings":"uBAQA,SAAgB,EAAc,EAAwB,CAClD,GAAM,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,KACR,OAAO,OAAW,IAAoB,GACnC,OAAO,WAAW,CAAK,CAAC,CAAC,OACnC,EAWD,OATA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAO,OAAO,WAAW,CAAK,EAC9B,EAAW,GAAqC,EAAW,EAAM,OAAO,EAG9E,OAFA,EAAW,EAAK,OAAO,EACvB,EAAK,iBAAiB,SAAU,CAAO,MAC1B,EAAK,oBAAoB,SAAU,CAAO,CAC3D,EAAG,CAAC,CAAK,CAAC,EAEH,CACX"}
1
+ {"version":3,"file":"use-media-query.cjs","names":[],"sources":["../../src/hooks/use-media-query.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/**\n * Subscribe to a CSS media query and re-render on match changes.\n *\n * @param query - A standard CSS media query string, e.g. \"(max-width: 768px)\".\n * @returns True when the query matches.\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const list = window.matchMedia(query);\n const handler = (event: MediaQueryListEvent): void => setMatches(event.matches);\n setMatches(list.matches);\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n }, [query]);\n\n return matches;\n}\n"],"mappings":"uBAQA,SAAgB,EAAc,EAAwB,CAClD,GAAM,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,KACtB,OAAO,OAAW,IAAoB,GACnC,OAAO,WAAW,CAAK,CAAC,CAAC,OACnC,EAWD,OATA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,EAAO,OAAO,WAAW,CAAK,EAC9B,EAAW,GAAqC,EAAW,EAAM,OAAO,EAG9E,OAFA,EAAW,EAAK,OAAO,EACvB,EAAK,iBAAiB,SAAU,CAAO,MAC1B,EAAK,oBAAoB,SAAU,CAAO,CAC3D,EAAG,CAAC,CAAK,CAAC,EAEH,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-object-url.cjs","names":[],"sources":["../../src/hooks/use-object-url.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/**\n * Create an object URL for a `Blob` (or `File`) and revoke it automatically on\n * unmount or whenever the blob changes. Returns `null` for nullish input.\n *\n * @param blob - the blob to expose as an object URL, or `null`/`undefined`.\n * @returns The object URL string, or `null` when there is no blob.\n */\nexport function useObjectUrl(blob: Blob | null | undefined): string | null {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (!blob) {\n setUrl(null);\n return;\n }\n const next = URL.createObjectURL(blob);\n setUrl(next);\n return () => URL.revokeObjectURL(next);\n }, [blob]);\n\n return url;\n}\n"],"mappings":"uBASA,SAAgB,EAAa,EAA8C,CACvE,GAAM,CAAC,EAAK,IAAA,EAAA,EAAA,SAAA,CAAkC,IAAI,EAYlD,OAVA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,CACP,EAAO,IAAI,EACX,MACJ,CACA,IAAM,EAAO,IAAI,gBAAgB,CAAI,EAErC,OADA,EAAO,CAAI,MACE,IAAI,gBAAgB,CAAI,CACzC,EAAG,CAAC,CAAI,CAAC,EAEF,CACX"}
1
+ {"version":3,"file":"use-object-url.cjs","names":[],"sources":["../../src/hooks/use-object-url.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/**\n * Create an object URL for a `Blob` (or `File`) and revoke it automatically on\n * unmount or whenever the blob changes. Returns `null` for nullish input.\n *\n * @param blob - the blob to expose as an object URL, or `null`/`undefined`.\n * @returns The object URL string, or `null` when there is no blob.\n */\nexport function useObjectUrl(blob: Blob | null | undefined): string | null {\n const [url, setUrl] = useState<string | null>(null);\n\n useEffect(() => {\n if (!blob) {\n setUrl(null);\n return;\n }\n const next = URL.createObjectURL(blob);\n setUrl(next);\n return () => URL.revokeObjectURL(next);\n }, [blob]);\n\n return url;\n}\n"],"mappings":"uBASA,SAAgB,EAAa,EAA8C,CACvE,GAAM,CAAC,EAAK,IAAA,EAAU,EAAA,SAAA,CAAwB,IAAI,EAYlD,OAVA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,CACP,EAAO,IAAI,EACX,MACJ,CACA,IAAM,EAAO,IAAI,gBAAgB,CAAI,EAErC,OADA,EAAO,CAAI,MACE,IAAI,gBAAgB,CAAI,CACzC,EAAG,CAAC,CAAI,CAAC,EAEF,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-online.cjs","names":[],"sources":["../../src/hooks/use-online.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\n/**\n * Options for {@link useOnline}. Omit them for the cheap `navigator.onLine`\n * behaviour; pass `pingUrl` to add a real-reachability probe on top.\n */\nexport interface UseOnlineOptions {\n /**\n * URL to probe for real connectivity. `navigator.onLine` reports `true`\n * behind a captive portal or when the machine has a link but no internet;\n * a periodic `HEAD` to this URL catches those cases. When set, the result\n * is `navigator.onLine && lastProbeSucceeded`.\n */\n pingUrl?: string;\n /** How often to probe, in ms. Default `30000`. */\n intervalMs?: number;\n /** Abort a probe after this many ms and treat it as offline. Default `5000`. */\n timeoutMs?: number;\n}\n\n/**\n * Track connectivity and re-render on changes.\n *\n * With no options it mirrors `navigator.onLine` (assuming online during SSR and\n * before the first event). Pass `pingUrl` to additionally probe real\n * reachability — the browser's `onLine` flag lies behind captive portals and\n * dead links, so the probe downgrades the result to `false` when the network is\n * unusable even though `navigator.onLine` is `true`.\n *\n * @param options - Optional reachability-probe configuration.\n * @returns `true` when the device is online (and reachable, when probing).\n *\n * @example\n * const online = useOnline(); // navigator.onLine only\n * const reallyOnline = useOnline({ pingUrl: \"/health\", intervalMs: 15_000 });\n */\nexport function useOnline(options: UseOnlineOptions = {}): boolean {\n const { pingUrl, intervalMs = 30_000, timeoutMs = 5_000 } = options;\n\n const [navOnline, setNavOnline] = useState<boolean>(() =>\n typeof navigator === \"undefined\" ? true : navigator.onLine,\n );\n const [reachable, setReachable] = useState<boolean>(true);\n\n const optionsRef = useRef({ pingUrl, timeoutMs });\n useEffect(() => {\n optionsRef.current = { pingUrl, timeoutMs };\n }, [pingUrl, timeoutMs]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => setNavOnline(true);\n const handleOffline = (): void => setNavOnline(false);\n window.addEventListener(\"online\", handleOnline);\n window.addEventListener(\"offline\", handleOffline);\n return () => {\n window.removeEventListener(\"online\", handleOnline);\n window.removeEventListener(\"offline\", handleOffline);\n };\n }, []);\n\n useEffect(() => {\n if (!pingUrl || typeof window === \"undefined\") return;\n\n let cancelled = false;\n const probe = async (): Promise<void> => {\n if (typeof navigator !== \"undefined\" && !navigator.onLine) {\n if (!cancelled) setReachable(false);\n return;\n }\n const controller = new AbortController();\n const timer = window.setTimeout(() => controller.abort(), optionsRef.current.timeoutMs);\n try {\n await fetch(optionsRef.current.pingUrl!, {\n method: \"HEAD\",\n cache: \"no-store\",\n signal: controller.signal,\n });\n if (!cancelled) setReachable(true);\n } catch {\n if (!cancelled) setReachable(false);\n } finally {\n window.clearTimeout(timer);\n }\n };\n\n void probe();\n const id = window.setInterval(() => void probe(), intervalMs);\n const onWake = (): void => void probe();\n window.addEventListener(\"online\", onWake);\n document.addEventListener(\"visibilitychange\", onWake);\n return () => {\n cancelled = true;\n window.clearInterval(id);\n window.removeEventListener(\"online\", onWake);\n document.removeEventListener(\"visibilitychange\", onWake);\n };\n }, [pingUrl, intervalMs]);\n\n return pingUrl ? navOnline && reachable : navOnline;\n}\n"],"mappings":"uBAoCA,SAAgB,EAAU,EAA4B,CAAC,EAAY,CAC/D,GAAM,CAAE,UAAS,aAAa,IAAQ,YAAY,KAAU,EAEtD,CAAC,EAAW,IAAA,EAAA,EAAA,SAAA,KACd,OAAO,UAAc,KAAqB,UAAU,MACxD,EACM,CAAC,EAAW,IAAA,EAAA,EAAA,SAAA,CAAkC,EAAI,EAElD,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAE,UAAS,WAAU,CAAC,EAuDhD,OAtDA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CAAE,UAAS,WAAU,CAC9C,EAAG,CAAC,EAAS,CAAS,CAAC,GAEvB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,MAA2B,EAAa,EAAI,EAC5C,MAA4B,EAAa,EAAK,EAGpD,OAFA,OAAO,iBAAiB,SAAU,CAAY,EAC9C,OAAO,iBAAiB,UAAW,CAAa,MACnC,CACT,OAAO,oBAAoB,SAAU,CAAY,EACjD,OAAO,oBAAoB,UAAW,CAAa,CACvD,CACJ,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,OAAO,OAAW,IAAa,OAE/C,IAAI,EAAY,GACV,EAAQ,SAA2B,CACrC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,OAAQ,CAClD,GAAW,EAAa,EAAK,EAClC,MACJ,CACA,IAAM,EAAa,IAAI,gBACjB,EAAQ,OAAO,eAAiB,EAAW,MAAM,EAAG,EAAW,QAAQ,SAAS,EACtF,GAAI,CACA,MAAM,MAAM,EAAW,QAAQ,QAAU,CACrC,OAAQ,OACR,MAAO,WACP,OAAQ,EAAW,MACvB,CAAC,EACI,GAAW,EAAa,EAAI,CACrC,MAAQ,CACC,GAAW,EAAa,EAAK,CACtC,QAAU,CACN,OAAO,aAAa,CAAK,CAC7B,CACJ,EAEA,EAAW,EACX,IAAM,EAAK,OAAO,gBAAkB,KAAK,EAAM,EAAG,CAAU,EACtD,MAAqB,KAAK,EAAM,EAGtC,OAFA,OAAO,iBAAiB,SAAU,CAAM,EACxC,SAAS,iBAAiB,mBAAoB,CAAM,MACvC,CACT,EAAY,GACZ,OAAO,cAAc,CAAE,EACvB,OAAO,oBAAoB,SAAU,CAAM,EAC3C,SAAS,oBAAoB,mBAAoB,CAAM,CAC3D,CACJ,EAAG,CAAC,EAAS,CAAU,CAAC,EAEjB,EAAU,GAAa,EAAY,CAC9C"}
1
+ {"version":3,"file":"use-online.cjs","names":[],"sources":["../../src/hooks/use-online.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\n/**\n * Options for {@link useOnline}. Omit them for the cheap `navigator.onLine`\n * behaviour; pass `pingUrl` to add a real-reachability probe on top.\n */\nexport interface UseOnlineOptions {\n /**\n * URL to probe for real connectivity. `navigator.onLine` reports `true`\n * behind a captive portal or when the machine has a link but no internet;\n * a periodic `HEAD` to this URL catches those cases. When set, the result\n * is `navigator.onLine && lastProbeSucceeded`.\n */\n pingUrl?: string;\n /** How often to probe, in ms. Default `30000`. */\n intervalMs?: number;\n /** Abort a probe after this many ms and treat it as offline. Default `5000`. */\n timeoutMs?: number;\n}\n\n/**\n * Track connectivity and re-render on changes.\n *\n * With no options it mirrors `navigator.onLine` (assuming online during SSR and\n * before the first event). Pass `pingUrl` to additionally probe real\n * reachability — the browser's `onLine` flag lies behind captive portals and\n * dead links, so the probe downgrades the result to `false` when the network is\n * unusable even though `navigator.onLine` is `true`.\n *\n * @param options - Optional reachability-probe configuration.\n * @returns `true` when the device is online (and reachable, when probing).\n *\n * @example\n * const online = useOnline(); // navigator.onLine only\n * const reallyOnline = useOnline({ pingUrl: \"/health\", intervalMs: 15_000 });\n */\nexport function useOnline(options: UseOnlineOptions = {}): boolean {\n const { pingUrl, intervalMs = 30_000, timeoutMs = 5_000 } = options;\n\n const [navOnline, setNavOnline] = useState<boolean>(() =>\n typeof navigator === \"undefined\" ? true : navigator.onLine,\n );\n const [reachable, setReachable] = useState<boolean>(true);\n\n const optionsRef = useRef({ pingUrl, timeoutMs });\n useEffect(() => {\n optionsRef.current = { pingUrl, timeoutMs };\n }, [pingUrl, timeoutMs]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => setNavOnline(true);\n const handleOffline = (): void => setNavOnline(false);\n window.addEventListener(\"online\", handleOnline);\n window.addEventListener(\"offline\", handleOffline);\n return () => {\n window.removeEventListener(\"online\", handleOnline);\n window.removeEventListener(\"offline\", handleOffline);\n };\n }, []);\n\n useEffect(() => {\n if (!pingUrl || typeof window === \"undefined\") return;\n\n let cancelled = false;\n const probe = async (): Promise<void> => {\n if (typeof navigator !== \"undefined\" && !navigator.onLine) {\n if (!cancelled) setReachable(false);\n return;\n }\n const controller = new AbortController();\n const timer = window.setTimeout(() => controller.abort(), optionsRef.current.timeoutMs);\n try {\n await fetch(optionsRef.current.pingUrl!, {\n method: \"HEAD\",\n cache: \"no-store\",\n signal: controller.signal,\n });\n if (!cancelled) setReachable(true);\n } catch {\n if (!cancelled) setReachable(false);\n } finally {\n window.clearTimeout(timer);\n }\n };\n\n void probe();\n const id = window.setInterval(() => void probe(), intervalMs);\n const onWake = (): void => void probe();\n window.addEventListener(\"online\", onWake);\n document.addEventListener(\"visibilitychange\", onWake);\n return () => {\n cancelled = true;\n window.clearInterval(id);\n window.removeEventListener(\"online\", onWake);\n document.removeEventListener(\"visibilitychange\", onWake);\n };\n }, [pingUrl, intervalMs]);\n\n return pingUrl ? navOnline && reachable : navOnline;\n}\n"],"mappings":"uBAoCA,SAAgB,EAAU,EAA4B,CAAC,EAAY,CAC/D,GAAM,CAAE,UAAS,aAAa,IAAQ,YAAY,KAAU,EAEtD,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,KAC9B,OAAO,UAAc,KAAqB,UAAU,MACxD,EACM,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAkB,EAAI,EAElD,GAAA,EAAa,EAAA,OAAA,CAAO,CAAE,UAAS,WAAU,CAAC,EAuDhD,OAtDA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CAAE,UAAS,WAAU,CAC9C,EAAG,CAAC,EAAS,CAAS,CAAC,GAEvB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,MAA2B,EAAa,EAAI,EAC5C,MAA4B,EAAa,EAAK,EAGpD,OAFA,OAAO,iBAAiB,SAAU,CAAY,EAC9C,OAAO,iBAAiB,UAAW,CAAa,MACnC,CACT,OAAO,oBAAoB,SAAU,CAAY,EACjD,OAAO,oBAAoB,UAAW,CAAa,CACvD,CACJ,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,OAAO,OAAW,IAAa,OAE/C,IAAI,EAAY,GACV,EAAQ,SAA2B,CACrC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,OAAQ,CAClD,GAAW,EAAa,EAAK,EAClC,MACJ,CACA,IAAM,EAAa,IAAI,gBACjB,EAAQ,OAAO,eAAiB,EAAW,MAAM,EAAG,EAAW,QAAQ,SAAS,EACtF,GAAI,CACA,MAAM,MAAM,EAAW,QAAQ,QAAU,CACrC,OAAQ,OACR,MAAO,WACP,OAAQ,EAAW,MACvB,CAAC,EACI,GAAW,EAAa,EAAI,CACrC,MAAQ,CACC,GAAW,EAAa,EAAK,CACtC,QAAU,CACN,OAAO,aAAa,CAAK,CAC7B,CACJ,EAEA,EAAW,EACX,IAAM,EAAK,OAAO,gBAAkB,KAAK,EAAM,EAAG,CAAU,EACtD,MAAqB,KAAK,EAAM,EAGtC,OAFA,OAAO,iBAAiB,SAAU,CAAM,EACxC,SAAS,iBAAiB,mBAAoB,CAAM,MACvC,CACT,EAAY,GACZ,OAAO,cAAc,CAAE,EACvB,OAAO,oBAAoB,SAAU,CAAM,EAC3C,SAAS,oBAAoB,mBAAoB,CAAM,CAC3D,CACJ,EAAG,CAAC,EAAS,CAAU,CAAC,EAEjB,EAAU,GAAa,EAAY,CAC9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-pagination.cjs","names":[],"sources":["../../src/hooks/use-pagination.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface UsePaginationResult {\n page: number;\n size: number;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n reset: () => void;\n}\n\n/**\n * Manage page/size state for paginated lists. Reset goes back to page 1\n * without touching the page size.\n *\n * @param initialPage - Starting page (default 1).\n * @param initialSize - Starting page size (default 50).\n * @returns Pagination state and setters.\n */\nexport function usePagination(initialPage = 1, initialSize = 50): UsePaginationResult {\n const [page, setPage] = useState<number>(initialPage);\n const [size, setSize] = useState<number>(initialSize);\n\n const reset = useCallback(() => setPage(1), []);\n\n return { page, size, setPage, setSize, reset };\n}\n"],"mappings":"uBAkBA,SAAgB,EAAc,EAAc,EAAG,EAAc,GAAyB,CAClF,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA4B,CAAW,EAC9C,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA4B,CAAW,EAIpD,MAAO,CAAE,OAAM,OAAM,UAAS,UAAS,OAAA,EAAA,EAAA,YAAA,KAFP,EAAQ,CAAC,EAAG,CAAC,CAEN,CAAM,CACjD"}
1
+ {"version":3,"file":"use-pagination.cjs","names":[],"sources":["../../src/hooks/use-pagination.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface UsePaginationResult {\n page: number;\n size: number;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n reset: () => void;\n}\n\n/**\n * Manage page/size state for paginated lists. Reset goes back to page 1\n * without touching the page size.\n *\n * @param initialPage - Starting page (default 1).\n * @param initialSize - Starting page size (default 50).\n * @returns Pagination state and setters.\n */\nexport function usePagination(initialPage = 1, initialSize = 50): UsePaginationResult {\n const [page, setPage] = useState<number>(initialPage);\n const [size, setSize] = useState<number>(initialSize);\n\n const reset = useCallback(() => setPage(1), []);\n\n return { page, size, setPage, setSize, reset };\n}\n"],"mappings":"uBAkBA,SAAgB,EAAc,EAAc,EAAG,EAAc,GAAyB,CAClF,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAiB,CAAW,EAC9C,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAiB,CAAW,EAIpD,MAAO,CAAE,OAAM,OAAM,UAAS,UAAS,OAAA,EAFzB,EAAA,YAAA,KAAkB,EAAQ,CAAC,EAAG,CAAC,CAEN,CAAM,CACjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-previous.cjs","names":[],"sources":["../../src/hooks/use-previous.ts"],"sourcesContent":["/* eslint-disable react-hooks/refs -- returning last render's value IS this hook's contract */\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Return the value from the previous render. `undefined` on the first render.\n *\n * @example\n * const previousCount = usePrevious(count);\n * useEffect(() => {\n * if (previousCount !== undefined && previousCount !== count) {\n * analytics.track(\"count.changed\", { from: previousCount, to: count });\n * }\n * }, [count, previousCount]);\n */\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T | undefined>(undefined);\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n"],"mappings":"uBAcA,SAAgB,EAAe,EAAyB,CACpD,IAAM,GAAA,EAAA,EAAA,OAAA,CAA4B,IAAA,EAAS,EAI3C,OAHA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAI,QAAU,CAClB,EAAG,CAAC,CAAK,CAAC,EACH,EAAI,OACf"}
1
+ {"version":3,"file":"use-previous.cjs","names":[],"sources":["../../src/hooks/use-previous.ts"],"sourcesContent":["/* eslint-disable react-hooks/refs -- returning last render's value IS this hook's contract */\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Return the value from the previous render. `undefined` on the first render.\n *\n * @example\n * const previousCount = usePrevious(count);\n * useEffect(() => {\n * if (previousCount !== undefined && previousCount !== count) {\n * analytics.track(\"count.changed\", { from: previousCount, to: count });\n * }\n * }, [count, previousCount]);\n */\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T | undefined>(undefined);\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n"],"mappings":"uBAcA,SAAgB,EAAe,EAAyB,CACpD,IAAM,GAAA,EAAM,EAAA,OAAA,CAAsB,IAAA,EAAS,EAI3C,OAHA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAI,QAAU,CAClB,EAAG,CAAC,CAAK,CAAC,EACH,EAAI,OACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-queue.cjs","names":[],"sources":["../../src/hooks/use-queue.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseQueueOptions<T> {\n initialValues?: T[];\n limit?: number;\n}\n\nexport interface UseQueueResult<T> {\n /** Items currently held within the queue (up to `limit`). */\n queue: T[];\n /** Append items; overflow beyond `limit` is held back, not dropped. */\n add: (...items: T[]) => void;\n /** Replace the held queue via a mapper over the current held items. */\n update: (fn: (state: T[]) => T[]) => void;\n /** Drop all currently held (visible) items, keeping any overflow. */\n cleanQueue: () => void;\n /** Number of visible items in the queue. */\n size: number;\n}\n\n/**\n * FIFO queue with an optional `limit`. When more items are added than `limit`\n * allows, the surplus is kept in an internal overflow buffer and surfaces into\n * `queue` as room frees up (e.g. after `cleanQueue`). Loosely mirrors Mantine's\n * `useQueue` shape.\n *\n * @typeParam T - element type.\n * @param options - `initialValues` and `limit` (default limit `Infinity`).\n * @returns `{ queue, add, update, cleanQueue, size }`.\n */\nexport function useQueue<T>(options: UseQueueOptions<T> = {}): UseQueueResult<T> {\n const { initialValues = [], limit = Infinity } = options;\n\n const [state, setState] = useState<{ queue: T[]; overflow: T[] }>(() => ({\n queue: initialValues.slice(0, limit),\n overflow: initialValues.slice(limit),\n }));\n\n const add = useCallback(\n (...items: T[]): void => {\n setState((current) => {\n const all = [...current.queue, ...current.overflow, ...items];\n return {\n queue: all.slice(0, limit),\n overflow: all.slice(limit),\n };\n });\n },\n [limit],\n );\n\n const update = useCallback(\n (fn: (queueState: T[]) => T[]): void => {\n setState((current) => {\n const all = [...fn(current.queue), ...current.overflow];\n return {\n queue: all.slice(0, limit),\n overflow: all.slice(limit),\n };\n });\n },\n [limit],\n );\n\n const cleanQueue = useCallback((): void => {\n setState((current) => ({\n queue: current.overflow.slice(0, limit),\n overflow: current.overflow.slice(limit),\n }));\n }, [limit]);\n\n return useMemo<UseQueueResult<T>>(\n () => ({\n queue: state.queue,\n add,\n update,\n cleanQueue,\n size: state.queue.length,\n }),\n [state.queue, add, update, cleanQueue],\n );\n}\n"],"mappings":"uBA8BA,SAAgB,EAAY,EAA8B,CAAC,EAAsB,CAC7E,GAAM,CAAE,gBAAgB,CAAC,EAAG,QAAQ,KAAa,EAE3C,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,MAA2D,CACrE,MAAO,EAAc,MAAM,EAAG,CAAK,EACnC,SAAU,EAAc,MAAM,CAAK,CACvC,EAAE,EAEI,GAAA,EAAA,EAAA,YAAA,EACD,GAAG,IAAqB,CACrB,EAAU,GAAY,CAClB,IAAM,EAAM,CAAC,GAAG,EAAQ,MAAO,GAAG,EAAQ,SAAU,GAAG,CAAK,EAC5D,MAAO,CACH,MAAO,EAAI,MAAM,EAAG,CAAK,EACzB,SAAU,EAAI,MAAM,CAAK,CAC7B,CACJ,CAAC,CACL,EACA,CAAC,CAAK,CACV,EAEM,GAAA,EAAA,EAAA,YAAA,CACD,GAAuC,CACpC,EAAU,GAAY,CAClB,IAAM,EAAM,CAAC,GAAG,EAAG,EAAQ,KAAK,EAAG,GAAG,EAAQ,QAAQ,EACtD,MAAO,CACH,MAAO,EAAI,MAAM,EAAG,CAAK,EACzB,SAAU,EAAI,MAAM,CAAK,CAC7B,CACJ,CAAC,CACL,EACA,CAAC,CAAK,CACV,EAEM,GAAA,EAAA,EAAA,YAAA,KAAqC,CACvC,EAAU,IAAa,CACnB,MAAO,EAAQ,SAAS,MAAM,EAAG,CAAK,EACtC,SAAU,EAAQ,SAAS,MAAM,CAAK,CAC1C,EAAE,CACN,EAAG,CAAC,CAAK,CAAC,EAEV,OAAA,EAAA,EAAA,QAAA,MACW,CACH,MAAO,EAAM,MACb,MACA,SACA,aACA,KAAM,EAAM,MAAM,MACtB,GACA,CAAC,EAAM,MAAO,EAAK,EAAQ,CAAU,CACzC,CACJ"}
1
+ {"version":3,"file":"use-queue.cjs","names":[],"sources":["../../src/hooks/use-queue.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport interface UseQueueOptions<T> {\n initialValues?: T[];\n limit?: number;\n}\n\nexport interface UseQueueResult<T> {\n /** Items currently held within the queue (up to `limit`). */\n queue: T[];\n /** Append items; overflow beyond `limit` is held back, not dropped. */\n add: (...items: T[]) => void;\n /** Replace the held queue via a mapper over the current held items. */\n update: (fn: (state: T[]) => T[]) => void;\n /** Drop all currently held (visible) items, keeping any overflow. */\n cleanQueue: () => void;\n /** Number of visible items in the queue. */\n size: number;\n}\n\n/**\n * FIFO queue with an optional `limit`. When more items are added than `limit`\n * allows, the surplus is kept in an internal overflow buffer and surfaces into\n * `queue` as room frees up (e.g. after `cleanQueue`). Loosely mirrors Mantine's\n * `useQueue` shape.\n *\n * @typeParam T - element type.\n * @param options - `initialValues` and `limit` (default limit `Infinity`).\n * @returns `{ queue, add, update, cleanQueue, size }`.\n */\nexport function useQueue<T>(options: UseQueueOptions<T> = {}): UseQueueResult<T> {\n const { initialValues = [], limit = Infinity } = options;\n\n const [state, setState] = useState<{ queue: T[]; overflow: T[] }>(() => ({\n queue: initialValues.slice(0, limit),\n overflow: initialValues.slice(limit),\n }));\n\n const add = useCallback(\n (...items: T[]): void => {\n setState((current) => {\n const all = [...current.queue, ...current.overflow, ...items];\n return {\n queue: all.slice(0, limit),\n overflow: all.slice(limit),\n };\n });\n },\n [limit],\n );\n\n const update = useCallback(\n (fn: (queueState: T[]) => T[]): void => {\n setState((current) => {\n const all = [...fn(current.queue), ...current.overflow];\n return {\n queue: all.slice(0, limit),\n overflow: all.slice(limit),\n };\n });\n },\n [limit],\n );\n\n const cleanQueue = useCallback((): void => {\n setState((current) => ({\n queue: current.overflow.slice(0, limit),\n overflow: current.overflow.slice(limit),\n }));\n }, [limit]);\n\n return useMemo<UseQueueResult<T>>(\n () => ({\n queue: state.queue,\n add,\n update,\n cleanQueue,\n size: state.queue.length,\n }),\n [state.queue, add, update, cleanQueue],\n );\n}\n"],"mappings":"uBA8BA,SAAgB,EAAY,EAA8B,CAAC,EAAsB,CAC7E,GAAM,CAAE,gBAAgB,CAAC,EAAG,QAAQ,KAAa,EAE3C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,MAA+C,CACrE,MAAO,EAAc,MAAM,EAAG,CAAK,EACnC,SAAU,EAAc,MAAM,CAAK,CACvC,EAAE,EAEI,GAAA,EAAM,EAAA,YAAA,EACP,GAAG,IAAqB,CACrB,EAAU,GAAY,CAClB,IAAM,EAAM,CAAC,GAAG,EAAQ,MAAO,GAAG,EAAQ,SAAU,GAAG,CAAK,EAC5D,MAAO,CACH,MAAO,EAAI,MAAM,EAAG,CAAK,EACzB,SAAU,EAAI,MAAM,CAAK,CAC7B,CACJ,CAAC,CACL,EACA,CAAC,CAAK,CACV,EAEM,GAAA,EAAS,EAAA,YAAA,CACV,GAAuC,CACpC,EAAU,GAAY,CAClB,IAAM,EAAM,CAAC,GAAG,EAAG,EAAQ,KAAK,EAAG,GAAG,EAAQ,QAAQ,EACtD,MAAO,CACH,MAAO,EAAI,MAAM,EAAG,CAAK,EACzB,SAAU,EAAI,MAAM,CAAK,CAC7B,CACJ,CAAC,CACL,EACA,CAAC,CAAK,CACV,EAEM,GAAA,EAAa,EAAA,YAAA,KAAwB,CACvC,EAAU,IAAa,CACnB,MAAO,EAAQ,SAAS,MAAM,EAAG,CAAK,EACtC,SAAU,EAAQ,SAAS,MAAM,CAAK,CAC1C,EAAE,CACN,EAAG,CAAC,CAAK,CAAC,EAEV,OAAA,EAAO,EAAA,QAAA,MACI,CACH,MAAO,EAAM,MACb,MACA,SACA,aACA,KAAM,EAAM,MAAM,MACtB,GACA,CAAC,EAAM,MAAO,EAAK,EAAQ,CAAU,CACzC,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-resize-observer.cjs","names":[],"sources":["../../src/hooks/use-resize-observer.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface ElementSize {\n width: number;\n height: number;\n}\n\n/**\n * Track size changes of a DOM element via `ResizeObserver`.\n * Returns `null` until the first measurement.\n */\nexport function useResizeObserver(ref: RefObject<Element | null>): ElementSize | null {\n const [size, setSize] = useState<ElementSize | null>(null);\n\n useEffect(() => {\n const target = ref.current;\n if (!target || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const box = entry.contentRect;\n setSize({ width: box.width, height: box.height });\n });\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [ref]);\n\n return size;\n}\n"],"mappings":"uBAYA,SAAgB,EAAkB,EAAoD,CAClF,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAAwC,IAAI,EAiBzD,OAfA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAS,EAAI,QACnB,GAAI,CAAC,GAAU,OAAO,eAAmB,IAAa,OAEtD,IAAM,EAAW,IAAI,eAAgB,GAAY,CAC7C,IAAM,EAAQ,EAAQ,GACtB,GAAI,CAAC,EAAO,OACZ,IAAM,EAAM,EAAM,YAClB,EAAQ,CAAE,MAAO,EAAI,MAAO,OAAQ,EAAI,MAAO,CAAC,CACpD,CAAC,EAGD,OADA,EAAS,QAAQ,CAAM,MACV,EAAS,WAAW,CACrC,EAAG,CAAC,CAAG,CAAC,EAED,CACX"}
1
+ {"version":3,"file":"use-resize-observer.cjs","names":[],"sources":["../../src/hooks/use-resize-observer.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport interface ElementSize {\n width: number;\n height: number;\n}\n\n/**\n * Track size changes of a DOM element via `ResizeObserver`.\n * Returns `null` until the first measurement.\n */\nexport function useResizeObserver(ref: RefObject<Element | null>): ElementSize | null {\n const [size, setSize] = useState<ElementSize | null>(null);\n\n useEffect(() => {\n const target = ref.current;\n if (!target || typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const box = entry.contentRect;\n setSize({ width: box.width, height: box.height });\n });\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [ref]);\n\n return size;\n}\n"],"mappings":"uBAYA,SAAgB,EAAkB,EAAoD,CAClF,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAA6B,IAAI,EAiBzD,OAfA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAS,EAAI,QACnB,GAAI,CAAC,GAAU,OAAO,eAAmB,IAAa,OAEtD,IAAM,EAAW,IAAI,eAAgB,GAAY,CAC7C,IAAM,EAAQ,EAAQ,GACtB,GAAI,CAAC,EAAO,OACZ,IAAM,EAAM,EAAM,YAClB,EAAQ,CAAE,MAAO,EAAI,MAAO,OAAQ,EAAI,MAAO,CAAC,CACpD,CAAC,EAGD,OADA,EAAS,QAAQ,CAAM,MACV,EAAS,WAAW,CACrC,EAAG,CAAC,CAAG,CAAC,EAED,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-scroll-overflow.cjs","names":[],"sources":["../../src/hooks/use-scroll-overflow.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\n/** Which axis to watch for overflow. */\nexport type ScrollOverflowAxis = \"horizontal\" | \"vertical\" | \"both\";\n\n/**\n * Track whether an element's content currently overflows its box.\n *\n * The use this exists for is keyboard access. A scroll container holding no\n * focusable content is unreachable by keyboard: the user can see there is more\n * content past the edge and has no way to get to it, because focus never lands\n * anywhere the arrow keys would scroll. Giving the container `tabIndex={0}`\n * fixes it — but doing that unconditionally puts a tab stop on every such\n * container on the page, including the ones whose content fits and have nothing\n * to scroll. The stop is only wanted while the overflow is real, which is what\n * this reports. (It is also what axe checks as `scrollable-region-focusable`.)\n *\n * Both the container and its content are observed: content can outgrow a box\n * whose own dimensions never change, and a box can shrink around content that\n * never changes. Watching one alone misses half the transitions.\n *\n * A one-pixel difference is ignored — that is layout rounding, not overflow.\n *\n * @param ref - The scroll container.\n * @param axis - Which axis to measure. Defaults to `\"both\"`.\n * @returns `true` while the content is larger than the box on that axis.\n *\n * @example\n * const ref = useRef<HTMLDivElement>(null);\n * const scrollable = useScrollOverflow(ref, \"horizontal\");\n *\n * <div\n * ref={ref}\n * tabIndex={scrollable ? 0 : undefined}\n * role={scrollable ? \"group\" : undefined}\n * aria-label={scrollable ? \"Tabela rolável\" : undefined}\n * >\n */\nexport function useScrollOverflow(\n ref: RefObject<HTMLElement | null>,\n axis: ScrollOverflowAxis = \"both\",\n): boolean {\n const [overflowing, setOverflowing] = useState(false);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const measure = () => {\n const horizontal = element.scrollWidth - element.clientWidth > 1;\n const vertical = element.scrollHeight - element.clientHeight > 1;\n if (axis === \"horizontal\") setOverflowing(horizontal);\n else if (axis === \"vertical\") setOverflowing(vertical);\n else setOverflowing(horizontal || vertical);\n };\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n const content = element.firstElementChild;\n if (content) observer.observe(content);\n return () => observer.disconnect();\n }, [ref, axis]);\n\n return overflowing;\n}\n"],"mappings":"uBAuCA,SAAgB,EACZ,EACA,EAA2B,OACpB,CACP,GAAM,CAAC,EAAa,IAAA,EAAA,EAAA,SAAA,CAA2B,EAAK,EAwBpD,OAtBA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAU,EAAI,QACpB,GAAI,CAAC,EAAS,OAEd,IAAM,MAAgB,CAClB,IAAM,EAAa,EAAQ,YAAc,EAAQ,YAAc,EACzD,EAAW,EAAQ,aAAe,EAAQ,aAAe,EACpC,EAAvB,IAAS,aAA6B,EACjC,IAAS,WAA2B,EACzB,GAAc,CAAQ,CAC9C,EAGA,GAFA,EAAQ,EAEJ,OAAO,eAAmB,IAAa,OAE3C,IAAM,EAAW,IAAI,eAAe,CAAO,EAC3C,EAAS,QAAQ,CAAO,EACxB,IAAM,EAAU,EAAQ,kBAExB,OADI,GAAS,EAAS,QAAQ,CAAO,MACxB,EAAS,WAAW,CACrC,EAAG,CAAC,EAAK,CAAI,CAAC,EAEP,CACX"}
1
+ {"version":3,"file":"use-scroll-overflow.cjs","names":[],"sources":["../../src/hooks/use-scroll-overflow.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\n/** Which axis to watch for overflow. */\nexport type ScrollOverflowAxis = \"horizontal\" | \"vertical\" | \"both\";\n\n/**\n * Track whether an element's content currently overflows its box.\n *\n * The use this exists for is keyboard access. A scroll container holding no\n * focusable content is unreachable by keyboard: the user can see there is more\n * content past the edge and has no way to get to it, because focus never lands\n * anywhere the arrow keys would scroll. Giving the container `tabIndex={0}`\n * fixes it — but doing that unconditionally puts a tab stop on every such\n * container on the page, including the ones whose content fits and have nothing\n * to scroll. The stop is only wanted while the overflow is real, which is what\n * this reports. (It is also what axe checks as `scrollable-region-focusable`.)\n *\n * Both the container and its content are observed: content can outgrow a box\n * whose own dimensions never change, and a box can shrink around content that\n * never changes. Watching one alone misses half the transitions.\n *\n * A one-pixel difference is ignored — that is layout rounding, not overflow.\n *\n * @param ref - The scroll container.\n * @param axis - Which axis to measure. Defaults to `\"both\"`.\n * @returns `true` while the content is larger than the box on that axis.\n *\n * @example\n * const ref = useRef<HTMLDivElement>(null);\n * const scrollable = useScrollOverflow(ref, \"horizontal\");\n *\n * <div\n * ref={ref}\n * tabIndex={scrollable ? 0 : undefined}\n * role={scrollable ? \"group\" : undefined}\n * aria-label={scrollable ? \"Tabela rolável\" : undefined}\n * >\n */\nexport function useScrollOverflow(\n ref: RefObject<HTMLElement | null>,\n axis: ScrollOverflowAxis = \"both\",\n): boolean {\n const [overflowing, setOverflowing] = useState(false);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const measure = () => {\n const horizontal = element.scrollWidth - element.clientWidth > 1;\n const vertical = element.scrollHeight - element.clientHeight > 1;\n if (axis === \"horizontal\") setOverflowing(horizontal);\n else if (axis === \"vertical\") setOverflowing(vertical);\n else setOverflowing(horizontal || vertical);\n };\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n const content = element.firstElementChild;\n if (content) observer.observe(content);\n return () => observer.disconnect();\n }, [ref, axis]);\n\n return overflowing;\n}\n"],"mappings":"uBAuCA,SAAgB,EACZ,EACA,EAA2B,OACpB,CACP,GAAM,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAS,EAAK,EAwBpD,OAtBA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAU,EAAI,QACpB,GAAI,CAAC,EAAS,OAEd,IAAM,MAAgB,CAClB,IAAM,EAAa,EAAQ,YAAc,EAAQ,YAAc,EACzD,EAAW,EAAQ,aAAe,EAAQ,aAAe,EACpC,EAAvB,IAAS,aAA6B,EACjC,IAAS,WAA2B,EACzB,GAAc,CAAQ,CAC9C,EAGA,GAFA,EAAQ,EAEJ,OAAO,eAAmB,IAAa,OAE3C,IAAM,EAAW,IAAI,eAAe,CAAO,EAC3C,EAAS,QAAQ,CAAO,EACxB,IAAM,EAAU,EAAQ,kBAExB,OADI,GAAS,EAAS,QAAQ,CAAO,MACxB,EAAS,WAAW,CACrC,EAAG,CAAC,EAAK,CAAI,CAAC,EAEP,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-service-worker-update.cjs","names":[],"sources":["../../src/hooks/use-service-worker-update.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n registerServiceWorker,\n skipWaiting,\n type RegisterServiceWorkerOptions,\n} from \"../sw/register-service-worker\";\n\n/**\n * Options for {@link useServiceWorkerUpdate}. Mirrors the registration options\n * minus the callbacks the hook owns internally (`onUpdate`); `onReady` and\n * `onError` still pass through for host-side logging.\n *\n * Leave `autoUpdate` off (the default): this hook exists to hand the reload\n * decision to the user, so a silent auto-reload would defeat the prompt.\n */\nexport type UseServiceWorkerUpdateOptions = Omit<RegisterServiceWorkerOptions, \"onUpdate\">;\n\n/** Reactive service-worker update state plus the apply action. */\nexport interface UseServiceWorkerUpdateResult {\n /** `true` once a new worker has installed and is waiting to activate. */\n updateAvailable: boolean;\n /**\n * Activate the waiting worker and reload the page once it takes control.\n * No-op when no update is waiting.\n */\n applyUpdate: () => void;\n /** The active registration, or `null` before it resolves / when unsupported. */\n registration: ServiceWorkerRegistration | null;\n}\n\n/**\n * Register a service worker and expose a user-driven update flow.\n *\n * Wraps {@link registerServiceWorker}: when a fresh worker finishes installing\n * while the page is still controlled by the old one, `updateAvailable` flips to\n * `true`. Call `applyUpdate` — typically from an \"atualizar\" toast — to tell the\n * waiting worker to activate and reload the page once it takes control.\n *\n * Skips cleanly when the runtime has no service-worker support (`registration`\n * stays `null`, `updateAvailable` stays `false`).\n *\n * @param options - Registration options (`url` required); `autoUpdate` should\n * stay off so the reload is user-confirmed.\n * @returns The update state and `applyUpdate` action.\n *\n * @example\n * const { updateAvailable, applyUpdate } = useServiceWorkerUpdate({ url: \"/sw.js\" });\n * return (\n * <UpdatePrompt open={updateAvailable} onUpdate={applyUpdate} />\n * );\n */\nexport function useServiceWorkerUpdate(\n options: UseServiceWorkerUpdateOptions,\n): UseServiceWorkerUpdateResult {\n const [updateAvailable, setUpdateAvailable] = useState<boolean>(false);\n const [registration, setRegistration] = useState<ServiceWorkerRegistration | null>(null);\n const waitingRef = useRef<ServiceWorker | null>(null);\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n let cancelled = false;\n void registerServiceWorker({\n ...optionsRef.current,\n onUpdate: (waiting, reg) => {\n waitingRef.current = waiting;\n setRegistration(reg);\n setUpdateAvailable(true);\n },\n onReady: (reg) => {\n if (cancelled) return;\n setRegistration(reg);\n if (reg.waiting && navigator.serviceWorker.controller) {\n waitingRef.current = reg.waiting;\n setUpdateAvailable(true);\n }\n optionsRef.current.onReady?.(reg);\n },\n }).then((reg) => {\n if (!cancelled && reg) setRegistration(reg);\n });\n return () => {\n cancelled = true;\n };\n }, [options.url]);\n\n const applyUpdate = useCallback(() => {\n const waiting = waitingRef.current;\n if (!waiting || typeof navigator === \"undefined\") return;\n let reloading = false;\n navigator.serviceWorker.addEventListener(\"controllerchange\", () => {\n if (reloading) return;\n reloading = true;\n window.location.reload();\n });\n skipWaiting(waiting);\n }, []);\n\n return { updateAvailable, applyUpdate, registration };\n}\n"],"mappings":"4EAmDA,SAAgB,EACZ,EAC4B,CAC5B,GAAM,CAAC,EAAiB,IAAA,EAAA,EAAA,SAAA,CAAwC,EAAK,EAC/D,CAAC,EAAc,IAAA,EAAA,EAAA,SAAA,CAA8D,IAAI,EACjF,GAAA,EAAA,EAAA,OAAA,CAA0C,IAAI,EAE9C,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAO,EA2CjC,OA1CA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CACzB,EAAG,CAAC,CAAO,CAAC,GAEZ,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GAoBhB,OAnBA,EAAK,sBAAsB,CACvB,GAAG,EAAW,QACd,UAAW,EAAS,IAAQ,CACxB,EAAW,QAAU,EACrB,EAAgB,CAAG,EACnB,EAAmB,EAAI,CAC3B,EACA,QAAU,GAAQ,CACV,IACJ,EAAgB,CAAG,EACf,EAAI,SAAW,UAAU,cAAc,aACvC,EAAW,QAAU,EAAI,QACzB,EAAmB,EAAI,GAE3B,EAAW,QAAQ,UAAU,CAAG,EACpC,CACJ,CAAC,CAAC,CAAC,KAAM,GAAQ,CACT,CAAC,GAAa,GAAK,EAAgB,CAAG,CAC9C,CAAC,MACY,CACT,EAAY,EAChB,CACJ,EAAG,CAAC,EAAQ,GAAG,CAAC,EAcT,CAAE,kBAAiB,aAAA,EAAA,EAAA,YAAA,KAZY,CAClC,IAAM,EAAU,EAAW,QAC3B,GAAI,CAAC,GAAW,OAAO,UAAc,IAAa,OAClD,IAAI,EAAY,GAChB,UAAU,cAAc,iBAAiB,uBAA0B,CAC3D,IACJ,EAAY,GACZ,OAAO,SAAS,OAAO,EAC3B,CAAC,EACD,EAAA,YAAY,CAAO,CACvB,EAAG,CAAC,CAEsB,EAAa,cAAa,CACxD"}
1
+ {"version":3,"file":"use-service-worker-update.cjs","names":[],"sources":["../../src/hooks/use-service-worker-update.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n registerServiceWorker,\n skipWaiting,\n type RegisterServiceWorkerOptions,\n} from \"../sw/register-service-worker\";\n\n/**\n * Options for {@link useServiceWorkerUpdate}. Mirrors the registration options\n * minus the callbacks the hook owns internally (`onUpdate`); `onReady` and\n * `onError` still pass through for host-side logging.\n *\n * Leave `autoUpdate` off (the default): this hook exists to hand the reload\n * decision to the user, so a silent auto-reload would defeat the prompt.\n */\nexport type UseServiceWorkerUpdateOptions = Omit<RegisterServiceWorkerOptions, \"onUpdate\">;\n\n/** Reactive service-worker update state plus the apply action. */\nexport interface UseServiceWorkerUpdateResult {\n /** `true` once a new worker has installed and is waiting to activate. */\n updateAvailable: boolean;\n /**\n * Activate the waiting worker and reload the page once it takes control.\n * No-op when no update is waiting.\n */\n applyUpdate: () => void;\n /** The active registration, or `null` before it resolves / when unsupported. */\n registration: ServiceWorkerRegistration | null;\n}\n\n/**\n * Register a service worker and expose a user-driven update flow.\n *\n * Wraps {@link registerServiceWorker}: when a fresh worker finishes installing\n * while the page is still controlled by the old one, `updateAvailable` flips to\n * `true`. Call `applyUpdate` — typically from an \"atualizar\" toast — to tell the\n * waiting worker to activate and reload the page once it takes control.\n *\n * Skips cleanly when the runtime has no service-worker support (`registration`\n * stays `null`, `updateAvailable` stays `false`).\n *\n * @param options - Registration options (`url` required); `autoUpdate` should\n * stay off so the reload is user-confirmed.\n * @returns The update state and `applyUpdate` action.\n *\n * @example\n * const { updateAvailable, applyUpdate } = useServiceWorkerUpdate({ url: \"/sw.js\" });\n * return (\n * <UpdatePrompt open={updateAvailable} onUpdate={applyUpdate} />\n * );\n */\nexport function useServiceWorkerUpdate(\n options: UseServiceWorkerUpdateOptions,\n): UseServiceWorkerUpdateResult {\n const [updateAvailable, setUpdateAvailable] = useState<boolean>(false);\n const [registration, setRegistration] = useState<ServiceWorkerRegistration | null>(null);\n const waitingRef = useRef<ServiceWorker | null>(null);\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n let cancelled = false;\n void registerServiceWorker({\n ...optionsRef.current,\n onUpdate: (waiting, reg) => {\n waitingRef.current = waiting;\n setRegistration(reg);\n setUpdateAvailable(true);\n },\n onReady: (reg) => {\n if (cancelled) return;\n setRegistration(reg);\n if (reg.waiting && navigator.serviceWorker.controller) {\n waitingRef.current = reg.waiting;\n setUpdateAvailable(true);\n }\n optionsRef.current.onReady?.(reg);\n },\n }).then((reg) => {\n if (!cancelled && reg) setRegistration(reg);\n });\n return () => {\n cancelled = true;\n };\n }, [options.url]);\n\n const applyUpdate = useCallback(() => {\n const waiting = waitingRef.current;\n if (!waiting || typeof navigator === \"undefined\") return;\n let reloading = false;\n navigator.serviceWorker.addEventListener(\"controllerchange\", () => {\n if (reloading) return;\n reloading = true;\n window.location.reload();\n });\n skipWaiting(waiting);\n }, []);\n\n return { updateAvailable, applyUpdate, registration };\n}\n"],"mappings":"4EAmDA,SAAgB,EACZ,EAC4B,CAC5B,GAAM,CAAC,EAAiB,IAAA,EAAsB,EAAA,SAAA,CAAkB,EAAK,EAC/D,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAA2C,IAAI,EACjF,GAAA,EAAa,EAAA,OAAA,CAA6B,IAAI,EAE9C,GAAA,EAAa,EAAA,OAAA,CAAO,CAAO,EA2CjC,OA1CA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CACzB,EAAG,CAAC,CAAO,CAAC,GAEZ,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GAoBhB,OAnBA,EAAK,sBAAsB,CACvB,GAAG,EAAW,QACd,UAAW,EAAS,IAAQ,CACxB,EAAW,QAAU,EACrB,EAAgB,CAAG,EACnB,EAAmB,EAAI,CAC3B,EACA,QAAU,GAAQ,CACV,IACJ,EAAgB,CAAG,EACf,EAAI,SAAW,UAAU,cAAc,aACvC,EAAW,QAAU,EAAI,QACzB,EAAmB,EAAI,GAE3B,EAAW,QAAQ,UAAU,CAAG,EACpC,CACJ,CAAC,CAAC,CAAC,KAAM,GAAQ,CACT,CAAC,GAAa,GAAK,EAAgB,CAAG,CAC9C,CAAC,MACY,CACT,EAAY,EAChB,CACJ,EAAG,CAAC,EAAQ,GAAG,CAAC,EAcT,CAAE,kBAAiB,aAAA,EAZN,EAAA,YAAA,KAAkB,CAClC,IAAM,EAAU,EAAW,QAC3B,GAAI,CAAC,GAAW,OAAO,UAAc,IAAa,OAClD,IAAI,EAAY,GAChB,UAAU,cAAc,iBAAiB,uBAA0B,CAC3D,IACJ,EAAY,GACZ,OAAO,SAAS,OAAO,EAC3B,CAAC,EACD,EAAA,YAAY,CAAO,CACvB,EAAG,CAAC,CAEsB,EAAa,cAAa,CACxD"}