tempest-react-sdk 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  2. package/dist/access/access-control-context.cjs.map +1 -1
  3. package/dist/access/use-can.cjs.map +1 -1
  4. package/dist/app/AppProviders.cjs.map +1 -1
  5. package/dist/app/AppProviders.js.map +1 -1
  6. package/dist/audio/use-audio-recorder.cjs.map +1 -1
  7. package/dist/audio/use-audio.cjs.map +1 -1
  8. package/dist/audio/use-microphone.cjs.map +1 -1
  9. package/dist/auth/create-auth-store.cjs.map +1 -1
  10. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  11. package/dist/auth/passkey.cjs.map +1 -1
  12. package/dist/auth/passkey.js.map +1 -1
  13. package/dist/auth/use-passkey.cjs.map +1 -1
  14. package/dist/br/BrazilMap.cjs +1 -1
  15. package/dist/br/BrazilMap.cjs.map +1 -1
  16. package/dist/br/BrazilMap.js +2 -2
  17. package/dist/br/BrazilMap.js.map +1 -1
  18. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  19. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  20. package/dist/br/BrazilStateMap.cjs +1 -1
  21. package/dist/br/BrazilStateMap.cjs.map +1 -1
  22. package/dist/br/BrazilStateMap.js +2 -2
  23. package/dist/br/BrazilStateMap.js.map +1 -1
  24. package/dist/br/MapLegend.cjs.map +1 -1
  25. package/dist/br/MapLegend.js.map +1 -1
  26. package/dist/br/MapMarkers.cjs.map +1 -1
  27. package/dist/br/MapMarkers.js.map +1 -1
  28. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  29. package/dist/br/MunicipalitySearch.js.map +1 -1
  30. package/dist/br/PixQRCode.cjs.map +1 -1
  31. package/dist/br/PixQRCode.js.map +1 -1
  32. package/dist/br/use-map-hover.cjs.map +1 -1
  33. package/dist/br/use-map-zoom.cjs.map +1 -1
  34. package/dist/capture/media-recorder.cjs +1 -1
  35. package/dist/capture/media-recorder.js +2 -2
  36. package/dist/capture/use-barcode-scanner.cjs +1 -1
  37. package/dist/capture/use-barcode-scanner.cjs.map +1 -1
  38. package/dist/capture/use-barcode-scanner.js +2 -2
  39. package/dist/capture/use-barcode-scanner.js.map +1 -1
  40. package/dist/capture/use-screen-capture.cjs.map +1 -1
  41. package/dist/capture/use-speech-recognition.cjs.map +1 -1
  42. package/dist/capture/use-torch.cjs.map +1 -1
  43. package/dist/capture/use-video-recorder.cjs.map +1 -1
  44. package/dist/charts/AreaChart.cjs.map +1 -1
  45. package/dist/charts/AreaChart.js.map +1 -1
  46. package/dist/charts/BarChart.cjs.map +1 -1
  47. package/dist/charts/BarChart.js.map +1 -1
  48. package/dist/charts/LineChart.cjs.map +1 -1
  49. package/dist/charts/LineChart.js.map +1 -1
  50. package/dist/charts/PieChart.cjs.map +1 -1
  51. package/dist/charts/PieChart.js.map +1 -1
  52. package/dist/charts/RadarChart.cjs.map +1 -1
  53. package/dist/charts/RadarChart.js.map +1 -1
  54. package/dist/charts/use-chart-colors.cjs.map +1 -1
  55. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  56. package/dist/components/AIChat/AIChat.js.map +1 -1
  57. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  58. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  59. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
  60. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  61. package/dist/components/Accordion/Accordion.cjs.map +1 -1
  62. package/dist/components/Accordion/Accordion.js.map +1 -1
  63. package/dist/components/Alert/Alert.cjs.map +1 -1
  64. package/dist/components/Alert/Alert.js.map +1 -1
  65. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  66. package/dist/components/AppBar/AppBar.js.map +1 -1
  67. package/dist/components/AppShell/AppShell.cjs.map +1 -1
  68. package/dist/components/AppShell/AppShell.js.map +1 -1
  69. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  70. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
  71. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
  72. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
  73. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  74. package/dist/components/Avatar/Avatar.js.map +1 -1
  75. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
  76. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
  77. package/dist/components/Badge/Badge.cjs.map +1 -1
  78. package/dist/components/Badge/Badge.js.map +1 -1
  79. package/dist/components/Banner/Banner.cjs.map +1 -1
  80. package/dist/components/Banner/Banner.js.map +1 -1
  81. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
  82. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
  83. package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  84. package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
  85. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  86. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  87. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  88. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  89. package/dist/components/Button/Button.cjs.map +1 -1
  90. package/dist/components/Button/Button.js.map +1 -1
  91. package/dist/components/Calendar/Calendar.cjs +1 -1
  92. package/dist/components/Calendar/Calendar.cjs.map +1 -1
  93. package/dist/components/Calendar/Calendar.js +38 -41
  94. package/dist/components/Calendar/Calendar.js.map +1 -1
  95. package/dist/components/Card/Card.cjs.map +1 -1
  96. package/dist/components/Card/Card.js.map +1 -1
  97. package/dist/components/Carousel/Carousel.cjs.map +1 -1
  98. package/dist/components/Carousel/Carousel.js.map +1 -1
  99. package/dist/components/Chat/Chat.cjs.map +1 -1
  100. package/dist/components/Chat/Chat.js.map +1 -1
  101. package/dist/components/Chat/ChatComposer.cjs.map +1 -1
  102. package/dist/components/Chat/ChatComposer.js.map +1 -1
  103. package/dist/components/Chat/chat-groups.cjs +1 -1
  104. package/dist/components/Chat/chat-groups.cjs.map +1 -1
  105. package/dist/components/Chat/chat-groups.js +2 -2
  106. package/dist/components/Chat/chat-groups.js.map +1 -1
  107. package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
  108. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  109. package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
  110. package/dist/components/ChipInput/ChipInput.js.map +1 -1
  111. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  112. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
  113. package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
  114. package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
  115. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  116. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  117. package/dist/components/Combobox/Combobox.js.map +1 -1
  118. package/dist/components/Command/Command.cjs.map +1 -1
  119. package/dist/components/Command/Command.js.map +1 -1
  120. package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
  121. package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
  122. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  123. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  124. package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
  125. package/dist/components/CopyButton/CopyButton.js.map +1 -1
  126. package/dist/components/DataList/DataList.cjs.map +1 -1
  127. package/dist/components/DataList/DataList.js.map +1 -1
  128. package/dist/components/DataTable/DataTable.cjs +1 -1
  129. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  130. package/dist/components/DataTable/DataTable.js +2 -2
  131. package/dist/components/DataTable/DataTable.js.map +1 -1
  132. package/dist/components/DataTable/EditableCell.cjs.map +1 -1
  133. package/dist/components/DataTable/EditableCell.js.map +1 -1
  134. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  135. package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
  136. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  137. package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
  138. package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
  139. package/dist/components/Divider/Divider.cjs.map +1 -1
  140. package/dist/components/Divider/Divider.js.map +1 -1
  141. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  142. package/dist/components/Drawer/Drawer.js.map +1 -1
  143. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  144. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  145. package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
  146. package/dist/components/Dropzone/Dropzone.js.map +1 -1
  147. package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
  148. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  149. package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
  150. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  151. package/dist/components/FileUpload/FileUpload.cjs +1 -1
  152. package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
  153. package/dist/components/FileUpload/FileUpload.js +1 -1
  154. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  155. package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
  156. package/dist/components/FilterBar/FilterBar.js.map +1 -1
  157. package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
  158. package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
  159. package/dist/components/Form/Form.cjs.map +1 -1
  160. package/dist/components/Form/Form.js.map +1 -1
  161. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  162. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  163. package/dist/components/Image/Image.cjs.map +1 -1
  164. package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
  165. package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
  166. package/dist/components/ImageCropper/ImageCropper.js +1 -1
  167. package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
  168. package/dist/components/Input/Input.cjs.map +1 -1
  169. package/dist/components/Input/Input.js.map +1 -1
  170. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  171. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  172. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  173. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  174. package/dist/components/Kanban/Kanban.cjs.map +1 -1
  175. package/dist/components/Kanban/Kanban.js.map +1 -1
  176. package/dist/components/Label/Label.cjs.map +1 -1
  177. package/dist/components/Label/Label.js.map +1 -1
  178. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  179. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  180. package/dist/components/ListTile/ListTile.cjs.map +1 -1
  181. package/dist/components/ListTile/ListTile.js.map +1 -1
  182. package/dist/components/Markdown/Markdown.cjs +1 -1
  183. package/dist/components/Markdown/Markdown.cjs.map +1 -1
  184. package/dist/components/Markdown/Markdown.js +2 -2
  185. package/dist/components/Markdown/Markdown.js.map +1 -1
  186. package/dist/components/Masonry/Masonry.cjs.map +1 -1
  187. package/dist/components/Masonry/Masonry.js.map +1 -1
  188. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  189. package/dist/components/Menubar/Menubar.js.map +1 -1
  190. package/dist/components/Modal/Modal.cjs.map +1 -1
  191. package/dist/components/Modal/Modal.js.map +1 -1
  192. package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
  193. package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
  194. package/dist/components/Money/Money.cjs.map +1 -1
  195. package/dist/components/Money/Money.js.map +1 -1
  196. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  197. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  198. package/dist/components/NProgress/NProgress.cjs.map +1 -1
  199. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  200. package/dist/components/Navbar/Navbar.js.map +1 -1
  201. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  202. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  203. package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
  204. package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
  205. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
  206. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  207. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
  208. package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
  209. package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
  210. package/dist/components/Page/Page.cjs.map +1 -1
  211. package/dist/components/Page/Page.js.map +1 -1
  212. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  213. package/dist/components/Pagination/Pagination.js.map +1 -1
  214. package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
  215. package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
  216. package/dist/components/PinInput/PinInput.cjs.map +1 -1
  217. package/dist/components/PinInput/PinInput.js.map +1 -1
  218. package/dist/components/Popover/Popover.cjs.map +1 -1
  219. package/dist/components/Popover/Popover.js.map +1 -1
  220. package/dist/components/Portal/Portal.cjs.map +1 -1
  221. package/dist/components/Progress/Progress.cjs.map +1 -1
  222. package/dist/components/Progress/Progress.js.map +1 -1
  223. package/dist/components/QRCode/QRCode.cjs.map +1 -1
  224. package/dist/components/QRCode/QRCode.js.map +1 -1
  225. package/dist/components/Radio/Radio.cjs.map +1 -1
  226. package/dist/components/Radio/Radio.js.map +1 -1
  227. package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
  228. package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
  229. package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
  230. package/dist/components/RatingStars/RatingStars.js.map +1 -1
  231. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  232. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  233. package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
  234. package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
  235. package/dist/components/Resizable/Resizable.cjs.map +1 -1
  236. package/dist/components/Resizable/Resizable.js.map +1 -1
  237. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  238. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  239. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  240. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  241. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  242. package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  243. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  244. package/dist/components/Select/Select.cjs.map +1 -1
  245. package/dist/components/Select/Select.js.map +1 -1
  246. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  247. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  248. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
  249. package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
  250. package/dist/components/Slider/Slider.cjs.map +1 -1
  251. package/dist/components/Slider/Slider.js.map +1 -1
  252. package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
  253. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  254. package/dist/components/Spinner/Spinner.cjs.map +1 -1
  255. package/dist/components/Spinner/Spinner.js.map +1 -1
  256. package/dist/components/Stat/Stat.cjs.map +1 -1
  257. package/dist/components/Stat/Stat.js.map +1 -1
  258. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  259. package/dist/components/Stepper/Stepper.js.map +1 -1
  260. package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
  261. package/dist/components/StepperInput/StepperInput.js.map +1 -1
  262. package/dist/components/Switch/Switch.cjs.map +1 -1
  263. package/dist/components/Switch/Switch.js.map +1 -1
  264. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  265. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  266. package/dist/components/Table/Table.cjs.map +1 -1
  267. package/dist/components/Table/Table.js.map +1 -1
  268. package/dist/components/Tabs/Tabs.cjs.map +1 -1
  269. package/dist/components/Tabs/Tabs.js.map +1 -1
  270. package/dist/components/Tag/Tag.cjs.map +1 -1
  271. package/dist/components/Tag/Tag.js.map +1 -1
  272. package/dist/components/Textarea/Textarea.cjs.map +1 -1
  273. package/dist/components/Textarea/Textarea.js.map +1 -1
  274. package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
  275. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  276. package/dist/components/Timeline/Timeline.cjs.map +1 -1
  277. package/dist/components/Timeline/Timeline.js.map +1 -1
  278. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  279. package/dist/components/Toast/ToastProvider.js.map +1 -1
  280. package/dist/components/Toggle/Toggle.cjs.map +1 -1
  281. package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
  282. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  283. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  284. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  285. package/dist/components/Tour/Tour.cjs.map +1 -1
  286. package/dist/components/Tour/Tour.js.map +1 -1
  287. package/dist/components/Transfer/Transfer.cjs.map +1 -1
  288. package/dist/components/Transfer/Transfer.js.map +1 -1
  289. package/dist/components/TreeView/TreeView.cjs +1 -1
  290. package/dist/components/TreeView/TreeView.cjs.map +1 -1
  291. package/dist/components/TreeView/TreeView.js +1 -4
  292. package/dist/components/TreeView/TreeView.js.map +1 -1
  293. package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
  294. package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
  295. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  296. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  297. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  298. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  299. package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
  300. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  301. package/dist/components/Wizard/Wizard.js.map +1 -1
  302. package/dist/data/data-provider-context.cjs.map +1 -1
  303. package/dist/data/use-resource.cjs.map +1 -1
  304. package/dist/editor/RichTextEditor.cjs.map +1 -1
  305. package/dist/editor/RichTextEditor.js.map +1 -1
  306. package/dist/error-boundary/use-error-handler.cjs.map +1 -1
  307. package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
  308. package/dist/forms/FormField.cjs.map +1 -1
  309. package/dist/forms/masked-inputs.cjs +1 -1
  310. package/dist/forms/masked-inputs.cjs.map +1 -1
  311. package/dist/forms/masked-inputs.js +1 -1
  312. package/dist/forms/masked-inputs.js.map +1 -1
  313. package/dist/forms/use-viacep.cjs.map +1 -1
  314. package/dist/forms/use-zod-form.cjs.map +1 -1
  315. package/dist/forms/zod-resolver.cjs.map +1 -1
  316. package/dist/forms/zod-resolver.js.map +1 -1
  317. package/dist/geo/TrajectoryMap.cjs.map +1 -1
  318. package/dist/geo/TrajectoryMap.js.map +1 -1
  319. package/dist/geo/leaflet-map.cjs.map +1 -1
  320. package/dist/geo/leaflet-map.js.map +1 -1
  321. package/dist/geo/use-position-tracker.cjs.map +1 -1
  322. package/dist/hooks/use-announce.cjs.map +1 -1
  323. package/dist/hooks/use-async.cjs.map +1 -1
  324. package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
  325. package/dist/hooks/use-breakpoint.cjs.map +1 -1
  326. package/dist/hooks/use-click-outside.cjs.map +1 -1
  327. package/dist/hooks/use-client-filter.cjs.map +1 -1
  328. package/dist/hooks/use-clipboard.cjs.map +1 -1
  329. package/dist/hooks/use-counter.cjs.map +1 -1
  330. package/dist/hooks/use-debounce.cjs.map +1 -1
  331. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  332. package/dist/hooks/use-disclosure.cjs.map +1 -1
  333. package/dist/hooks/use-document-visibility.cjs.map +1 -1
  334. package/dist/hooks/use-event-listener.cjs.map +1 -1
  335. package/dist/hooks/use-geolocation.cjs.map +1 -1
  336. package/dist/hooks/use-hover.cjs.map +1 -1
  337. package/dist/hooks/use-idle.cjs.map +1 -1
  338. package/dist/hooks/use-install-prompt.cjs +1 -1
  339. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  340. package/dist/hooks/use-install-prompt.js +1 -1
  341. package/dist/hooks/use-install-prompt.js.map +1 -1
  342. package/dist/hooks/use-intersection-observer.cjs.map +1 -1
  343. package/dist/hooks/use-interval.cjs.map +1 -1
  344. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  345. package/dist/hooks/use-keyboard-shortcut.cjs +1 -1
  346. package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
  347. package/dist/hooks/use-keyboard-shortcut.js +1 -1
  348. package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
  349. package/dist/hooks/use-list-state.cjs.map +1 -1
  350. package/dist/hooks/use-local-storage.cjs.map +1 -1
  351. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  352. package/dist/hooks/use-long-press.cjs.map +1 -1
  353. package/dist/hooks/use-map.cjs.map +1 -1
  354. package/dist/hooks/use-media-devices.cjs.map +1 -1
  355. package/dist/hooks/use-media-permission.cjs.map +1 -1
  356. package/dist/hooks/use-media-query.cjs.map +1 -1
  357. package/dist/hooks/use-object-url.cjs.map +1 -1
  358. package/dist/hooks/use-online.cjs.map +1 -1
  359. package/dist/hooks/use-pagination.cjs.map +1 -1
  360. package/dist/hooks/use-previous.cjs.map +1 -1
  361. package/dist/hooks/use-queue.cjs.map +1 -1
  362. package/dist/hooks/use-resize-observer.cjs.map +1 -1
  363. package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
  364. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  365. package/dist/hooks/use-set.cjs.map +1 -1
  366. package/dist/hooks/use-sortable.cjs +1 -1
  367. package/dist/hooks/use-sortable.cjs.map +1 -1
  368. package/dist/hooks/use-sortable.js +0 -2
  369. package/dist/hooks/use-sortable.js.map +1 -1
  370. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  371. package/dist/hooks/use-storage-estimate.cjs.map +1 -1
  372. package/dist/hooks/use-throttle.cjs.map +1 -1
  373. package/dist/hooks/use-timeout.cjs.map +1 -1
  374. package/dist/hooks/use-toggle.cjs.map +1 -1
  375. package/dist/hooks/use-window-size.cjs.map +1 -1
  376. package/dist/http/api-client.cjs +1 -1
  377. package/dist/http/api-client.cjs.map +1 -1
  378. package/dist/http/api-client.js +7 -5
  379. package/dist/http/api-client.js.map +1 -1
  380. package/dist/http/resumable-upload.cjs +1 -1
  381. package/dist/http/resumable-upload.cjs.map +1 -1
  382. package/dist/http/resumable-upload.js +1 -1
  383. package/dist/http/resumable-upload.js.map +1 -1
  384. package/dist/http/use-poll.cjs.map +1 -1
  385. package/dist/i18n/I18nProvider.cjs.map +1 -1
  386. package/dist/icons/Icon.cjs.map +1 -1
  387. package/dist/icons/IconProvider.cjs.map +1 -1
  388. package/dist/icons/icon-context.cjs.map +1 -1
  389. package/dist/icons/use-icon.cjs.map +1 -1
  390. package/dist/imaging/use-image-processing.cjs.map +1 -1
  391. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  392. package/dist/oauth/GoogleSignIn.js.map +1 -1
  393. package/dist/oauth/use-oauth-callback.cjs.map +1 -1
  394. package/dist/offline/use-offline-sync.cjs.map +1 -1
  395. package/dist/push/use-push-subscription.cjs.map +1 -1
  396. package/dist/query/QueryProvider.cjs.map +1 -1
  397. package/dist/query/constants.cjs +1 -1
  398. package/dist/query/constants.cjs.map +1 -1
  399. package/dist/query/constants.js +10 -10
  400. package/dist/query/constants.js.map +1 -1
  401. package/dist/query/create-query-keys.cjs +1 -1
  402. package/dist/query/create-query-keys.cjs.map +1 -1
  403. package/dist/query/create-query-keys.js +1 -1
  404. package/dist/query/create-query-keys.js.map +1 -1
  405. package/dist/query/offline-persistence.cjs.map +1 -1
  406. package/dist/query/use-cursor-query.cjs.map +1 -1
  407. package/dist/query/use-offline-mutation.cjs.map +1 -1
  408. package/dist/query/use-paginated-query.cjs.map +1 -1
  409. package/dist/router/AppRouter.cjs +1 -1
  410. package/dist/router/AppRouter.cjs.map +1 -1
  411. package/dist/router/AppRouter.js +7 -2
  412. package/dist/router/AppRouter.js.map +1 -1
  413. package/dist/sse/use-event-stream.cjs.map +1 -1
  414. package/dist/store/create-store.cjs.map +1 -1
  415. package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
  416. package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
  417. package/dist/tempest-react-sdk.d.ts +25 -26
  418. package/dist/theme/ThemeProvider.cjs.map +1 -1
  419. package/dist/theme/color.cjs +1 -1
  420. package/dist/theme/color.cjs.map +1 -1
  421. package/dist/theme/color.js +1 -1
  422. package/dist/theme/color.js.map +1 -1
  423. package/dist/utils/cn.cjs +1 -1
  424. package/dist/utils/cn.cjs.map +1 -1
  425. package/dist/utils/cn.js +1 -1
  426. package/dist/utils/cn.js.map +1 -1
  427. package/dist/utils/objects.cjs +1 -1
  428. package/dist/utils/objects.cjs.map +1 -1
  429. package/dist/utils/objects.js +1 -1
  430. package/dist/utils/objects.js.map +1 -1
  431. package/dist/utils/xlsx.cjs +1 -1
  432. package/dist/utils/xlsx.cjs.map +1 -1
  433. package/dist/utils/xlsx.js +3 -3
  434. package/dist/utils/xlsx.js.map +1 -1
  435. package/dist/vision/core/exceptions.cjs.map +1 -1
  436. package/dist/vision/core/exceptions.js.map +1 -1
  437. package/dist/vision/core/metadata.cjs +1 -1
  438. package/dist/vision/core/metadata.cjs.map +1 -1
  439. package/dist/vision/core/metadata.js +1 -1
  440. package/dist/vision/core/metadata.js.map +1 -1
  441. package/dist/vision/io/image.cjs +1 -1
  442. package/dist/vision/io/image.cjs.map +1 -1
  443. package/dist/vision/io/image.js +4 -2
  444. package/dist/vision/io/image.js.map +1 -1
  445. package/dist/vision/preprocess/image.cjs +1 -1
  446. package/dist/vision/preprocess/image.cjs.map +1 -1
  447. package/dist/vision/preprocess/image.js +4 -2
  448. package/dist/vision/preprocess/image.js.map +1 -1
  449. package/dist/vision/tasks/base.cjs +1 -1
  450. package/dist/vision/tasks/base.cjs.map +1 -1
  451. package/dist/vision/tasks/base.js +3 -1
  452. package/dist/vision/tasks/base.js.map +1 -1
  453. package/dist/vision/tasks/classifier.cjs +1 -1
  454. package/dist/vision/tasks/classifier.cjs.map +1 -1
  455. package/dist/vision/tasks/classifier.js +2 -2
  456. package/dist/vision/tasks/classifier.js.map +1 -1
  457. package/dist/vision/tasks/detectClassify.cjs +1 -1
  458. package/dist/vision/tasks/detectClassify.cjs.map +1 -1
  459. package/dist/vision/tasks/detectClassify.js +16 -15
  460. package/dist/vision/tasks/detectClassify.js.map +1 -1
  461. package/dist/vision/use-camera-stream.cjs.map +1 -1
  462. package/dist/vision/use-live-luminance.cjs.map +1 -1
  463. package/dist/vite/create-vite-config.cjs +1 -1
  464. package/dist/vite/create-vite-config.cjs.map +1 -1
  465. package/dist/vite/create-vite-config.js +3 -3
  466. package/dist/vite/create-vite-config.js.map +1 -1
  467. package/dist/vite/tempest-icons.cjs.map +1 -1
  468. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  469. package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
  470. package/dist/ws/use-web-socket.cjs +1 -1
  471. package/dist/ws/use-web-socket.cjs.map +1 -1
  472. package/dist/ws/use-web-socket.js +5 -3
  473. package/dist/ws/use-web-socket.js.map +1 -1
  474. package/package.json +2 -2
  475. package/template/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"MapLegend.cjs","names":[],"sources":["../../src/br/MapLegend.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { SEQUENTIAL_BLUES } from \"./scales\";\nimport styles from \"./MapLegend.module.css\";\n\n/** A discrete legend entry. */\nexport interface LegendItem {\n color: string;\n label: ReactNode;\n}\n\nexport interface MapLegendProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Legend heading. */\n title?: ReactNode;\n /**\n * Discrete entries (swatch + label). When provided the legend renders in\n * \"steps\" mode; otherwise it renders a continuous gradient from `min`/`max`.\n */\n items?: readonly LegendItem[];\n /** Continuous mode: low end of the domain. */\n min?: number;\n /** Continuous mode: high end of the domain. */\n max?: number;\n /** Continuous mode: palette (light → dark). Default: sequential blues. */\n palette?: readonly string[];\n /** Continuous mode: format the min/mid/max tick labels. */\n format?: (value: number) => string;\n}\n\n/**\n * Legend for a choropleth map. Renders a **continuous gradient** (with\n * min/mid/max ticks) from `min`/`max`/`palette`, or **discrete swatches** when\n * `items` is given. Pair it with a `BrazilMap`/`BrazilStateMap` `colorScale`.\n *\n * @example\n * // Continuous, matching a sequentialScale(0, 100)\n * <MapLegend title=\"Vendas\" min={0} max={100} palette={SEQUENTIAL_BLUES} />\n *\n * @example\n * // Discrete buckets\n * <MapLegend\n * title=\"Faixa\"\n * items={[\n * { color: \"#c6dbef\", label: \"< 10\" },\n * { color: \"#6baed6\", label: \"10–50\" },\n * { color: \"#08519c\", label: \"> 50\" },\n * ]}\n * />\n */\nexport function MapLegend({\n title,\n items,\n min = 0,\n max = 1,\n palette = SEQUENTIAL_BLUES,\n format = (v) => String(Math.round(v)),\n className,\n ...rest\n}: MapLegendProps) {\n return (\n <div className={cn(styles.legend, className)} {...rest}>\n {title != null && <div className={styles.title}>{title}</div>}\n\n {items ? (\n <ul className={styles.items}>\n {items.map((item, i) => (\n <li key={i} className={styles.item}>\n <span className={styles.swatch} style={{ background: item.color }} />\n <span className={styles.itemLabel}>{item.label}</span>\n </li>\n ))}\n </ul>\n ) : (\n <>\n <div\n className={styles.gradient}\n style={{ background: `linear-gradient(to right, ${palette.join(\", \")})` }}\n />\n <div className={styles.ticks}>\n <span>{format(min)}</span>\n <span>{format((min + max) / 2)}</span>\n <span>{format(max)}</span>\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"oIAiDA,SAAgB,EAAU,CACtB,QACA,QACA,MAAM,EACN,MAAM,EACN,UAAU,EAAA,iBACV,SAAU,GAAM,OAAO,KAAK,MAAM,CAAC,CAAC,EACpC,YACA,GAAG,GACY,CACf,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EAAG,GAAI,WAAlD,CACK,GAAS,OAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,eAAQ,CAAW,CAAA,EAE3D,GACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,eACjB,EAAM,KAAK,EAAM,KACd,EAAA,EAAA,KAAA,CAAC,KAAD,CAAY,UAAW,EAAA,QAAO,cAA9B,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAQ,MAAO,CAAE,WAAY,EAAK,KAAM,CAAI,CAAA,GACpE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,mBAAY,EAAK,KAAY,CAAA,CACrD,GAHK,CAGL,CACP,CACD,CAAA,GAEJ,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,SAClB,MAAO,CAAE,WAAY,6BAA6B,EAAQ,KAAK,IAAI,EAAE,EAAG,CAC3E,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,eAAvB,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAO,CAAG,CAAQ,CAAA,GACzB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,GAAQ,EAAM,GAAO,CAAC,CAAQ,CAAA,GACrC,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAO,CAAG,CAAQ,CAAA,CACxB,GACP,CAAA,CAAA,CAEL,GAEb"}
1
+ {"version":3,"file":"MapLegend.cjs","names":[],"sources":["../../src/br/MapLegend.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { SEQUENTIAL_BLUES } from \"./scales\";\nimport styles from \"./MapLegend.module.css\";\n\n/** A discrete legend entry. */\nexport interface LegendItem {\n color: string;\n label: ReactNode;\n}\n\nexport interface MapLegendProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Legend heading. */\n title?: ReactNode;\n /**\n * Discrete entries (swatch + label). When provided the legend renders in\n * \"steps\" mode; otherwise it renders a continuous gradient from `min`/`max`.\n */\n items?: readonly LegendItem[];\n /** Continuous mode: low end of the domain. */\n min?: number;\n /** Continuous mode: high end of the domain. */\n max?: number;\n /** Continuous mode: palette (light → dark). Default: sequential blues. */\n palette?: readonly string[];\n /** Continuous mode: format the min/mid/max tick labels. */\n format?: (value: number) => string;\n}\n\n/**\n * Legend for a choropleth map. Renders a **continuous gradient** (with\n * min/mid/max ticks) from `min`/`max`/`palette`, or **discrete swatches** when\n * `items` is given. Pair it with a `BrazilMap`/`BrazilStateMap` `colorScale`.\n *\n * @example\n * // Continuous, matching a sequentialScale(0, 100)\n * <MapLegend title=\"Vendas\" min={0} max={100} palette={SEQUENTIAL_BLUES} />\n *\n * @example\n * // Discrete buckets\n * <MapLegend\n * title=\"Faixa\"\n * items={[\n * { color: \"#c6dbef\", label: \"< 10\" },\n * { color: \"#6baed6\", label: \"10–50\" },\n * { color: \"#08519c\", label: \"> 50\" },\n * ]}\n * />\n */\nexport function MapLegend({\n title,\n items,\n min = 0,\n max = 1,\n palette = SEQUENTIAL_BLUES,\n format = (v) => String(Math.round(v)),\n className,\n ...rest\n}: MapLegendProps) {\n return (\n <div className={cn(styles.legend, className)} {...rest}>\n {title != null && <div className={styles.title}>{title}</div>}\n\n {items ? (\n <ul className={styles.items}>\n {items.map((item, i) => (\n <li key={i} className={styles.item}>\n <span className={styles.swatch} style={{ background: item.color }} />\n <span className={styles.itemLabel}>{item.label}</span>\n </li>\n ))}\n </ul>\n ) : (\n <>\n <div\n className={styles.gradient}\n style={{ background: `linear-gradient(to right, ${palette.join(\", \")})` }}\n />\n <div className={styles.ticks}>\n <span>{format(min)}</span>\n <span>{format((min + max) / 2)}</span>\n <span>{format(max)}</span>\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"oIAiDA,SAAgB,EAAU,CACtB,QACA,QACA,MAAM,EACN,MAAM,EACN,UAAU,EAAA,iBACV,SAAU,GAAM,OAAO,KAAK,MAAM,CAAC,CAAC,EACpC,YACA,GAAG,GACY,CACf,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EAAG,GAAI,EAAlD,SAAA,CACK,GAAS,OAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAW,CAAA,EAE3D,GACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MACjB,SAAA,EAAM,KAAK,EAAM,KACd,EAAA,EAAA,KAAA,CAAC,KAAD,CAAY,UAAW,EAAA,QAAO,KAA9B,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAQ,MAAO,CAAE,WAAY,EAAK,KAAM,CAAI,CAAA,GACpE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UAAY,SAAA,EAAK,KAAY,CAAA,CACrD,CAHK,EAAA,CAGL,CACP,CACD,CAAA,GAEJ,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,SAClB,MAAO,CAAE,WAAY,6BAA6B,EAAQ,KAAK,IAAI,EAAE,EAAG,CAC3E,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,MAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAO,CAAG,CAAQ,CAAA,GACzB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,GAAQ,EAAM,GAAO,CAAC,CAAQ,CAAA,GACrC,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAO,CAAG,CAAQ,CAAA,CACxB,CACP,CAAA,CAAA,CAAA,CAAA,CAEL,GAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"MapLegend.js","names":[],"sources":["../../src/br/MapLegend.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { SEQUENTIAL_BLUES } from \"./scales\";\nimport styles from \"./MapLegend.module.css\";\n\n/** A discrete legend entry. */\nexport interface LegendItem {\n color: string;\n label: ReactNode;\n}\n\nexport interface MapLegendProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Legend heading. */\n title?: ReactNode;\n /**\n * Discrete entries (swatch + label). When provided the legend renders in\n * \"steps\" mode; otherwise it renders a continuous gradient from `min`/`max`.\n */\n items?: readonly LegendItem[];\n /** Continuous mode: low end of the domain. */\n min?: number;\n /** Continuous mode: high end of the domain. */\n max?: number;\n /** Continuous mode: palette (light → dark). Default: sequential blues. */\n palette?: readonly string[];\n /** Continuous mode: format the min/mid/max tick labels. */\n format?: (value: number) => string;\n}\n\n/**\n * Legend for a choropleth map. Renders a **continuous gradient** (with\n * min/mid/max ticks) from `min`/`max`/`palette`, or **discrete swatches** when\n * `items` is given. Pair it with a `BrazilMap`/`BrazilStateMap` `colorScale`.\n *\n * @example\n * // Continuous, matching a sequentialScale(0, 100)\n * <MapLegend title=\"Vendas\" min={0} max={100} palette={SEQUENTIAL_BLUES} />\n *\n * @example\n * // Discrete buckets\n * <MapLegend\n * title=\"Faixa\"\n * items={[\n * { color: \"#c6dbef\", label: \"< 10\" },\n * { color: \"#6baed6\", label: \"10–50\" },\n * { color: \"#08519c\", label: \"> 50\" },\n * ]}\n * />\n */\nexport function MapLegend({\n title,\n items,\n min = 0,\n max = 1,\n palette = SEQUENTIAL_BLUES,\n format = (v) => String(Math.round(v)),\n className,\n ...rest\n}: MapLegendProps) {\n return (\n <div className={cn(styles.legend, className)} {...rest}>\n {title != null && <div className={styles.title}>{title}</div>}\n\n {items ? (\n <ul className={styles.items}>\n {items.map((item, i) => (\n <li key={i} className={styles.item}>\n <span className={styles.swatch} style={{ background: item.color }} />\n <span className={styles.itemLabel}>{item.label}</span>\n </li>\n ))}\n </ul>\n ) : (\n <>\n <div\n className={styles.gradient}\n style={{ background: `linear-gradient(to right, ${palette.join(\", \")})` }}\n />\n <div className={styles.ticks}>\n <span>{format(min)}</span>\n <span>{format((min + max) / 2)}</span>\n <span>{format(max)}</span>\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAiDA,SAAgB,EAAU,EACtB,UACA,UACA,SAAM,GACN,SAAM,GACN,aAAU,GACV,aAAU,MAAM,OAAO,KAAK,MAAM,CAAC,CAAC,GACpC,cACA,GAAG,KACY;CACf,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,QAAQ,CAAS;EAAG,GAAI;YAAlD,CACK,KAAS,QAAQ,kBAAC,OAAD;GAAK,WAAW,EAAO;aAAQ;EAAW,CAAA,GAE3D,IACG,kBAAC,MAAD;GAAI,WAAW,EAAO;aACjB,EAAM,KAAK,GAAM,MACd,kBAAC,MAAD;IAAY,WAAW,EAAO;cAA9B,CACI,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAQ,OAAO,EAAE,YAAY,EAAK,MAAM;IAAI,CAAA,GACpE,kBAAC,QAAD;KAAM,WAAW,EAAO;eAAY,EAAK;IAAY,CAAA,CACrD;MAHK,CAGL,CACP;EACD,CAAA,IAEJ,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;GACI,WAAW,EAAO;GAClB,OAAO,EAAE,YAAY,6BAA6B,EAAQ,KAAK,IAAI,EAAE,GAAG;EAC3E,CAAA,GACD,kBAAC,OAAD;GAAK,WAAW,EAAO;aAAvB;IACI,kBAAC,QAAD,EAAA,UAAO,EAAO,CAAG,EAAQ,CAAA;IACzB,kBAAC,QAAD,EAAA,UAAO,GAAQ,IAAM,KAAO,CAAC,EAAQ,CAAA;IACrC,kBAAC,QAAD,EAAA,UAAO,EAAO,CAAG,EAAQ,CAAA;GACxB;IACP,EAAA,CAAA,CAEL;;AAEb"}
1
+ {"version":3,"file":"MapLegend.js","names":[],"sources":["../../src/br/MapLegend.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { SEQUENTIAL_BLUES } from \"./scales\";\nimport styles from \"./MapLegend.module.css\";\n\n/** A discrete legend entry. */\nexport interface LegendItem {\n color: string;\n label: ReactNode;\n}\n\nexport interface MapLegendProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Legend heading. */\n title?: ReactNode;\n /**\n * Discrete entries (swatch + label). When provided the legend renders in\n * \"steps\" mode; otherwise it renders a continuous gradient from `min`/`max`.\n */\n items?: readonly LegendItem[];\n /** Continuous mode: low end of the domain. */\n min?: number;\n /** Continuous mode: high end of the domain. */\n max?: number;\n /** Continuous mode: palette (light → dark). Default: sequential blues. */\n palette?: readonly string[];\n /** Continuous mode: format the min/mid/max tick labels. */\n format?: (value: number) => string;\n}\n\n/**\n * Legend for a choropleth map. Renders a **continuous gradient** (with\n * min/mid/max ticks) from `min`/`max`/`palette`, or **discrete swatches** when\n * `items` is given. Pair it with a `BrazilMap`/`BrazilStateMap` `colorScale`.\n *\n * @example\n * // Continuous, matching a sequentialScale(0, 100)\n * <MapLegend title=\"Vendas\" min={0} max={100} palette={SEQUENTIAL_BLUES} />\n *\n * @example\n * // Discrete buckets\n * <MapLegend\n * title=\"Faixa\"\n * items={[\n * { color: \"#c6dbef\", label: \"< 10\" },\n * { color: \"#6baed6\", label: \"10–50\" },\n * { color: \"#08519c\", label: \"> 50\" },\n * ]}\n * />\n */\nexport function MapLegend({\n title,\n items,\n min = 0,\n max = 1,\n palette = SEQUENTIAL_BLUES,\n format = (v) => String(Math.round(v)),\n className,\n ...rest\n}: MapLegendProps) {\n return (\n <div className={cn(styles.legend, className)} {...rest}>\n {title != null && <div className={styles.title}>{title}</div>}\n\n {items ? (\n <ul className={styles.items}>\n {items.map((item, i) => (\n <li key={i} className={styles.item}>\n <span className={styles.swatch} style={{ background: item.color }} />\n <span className={styles.itemLabel}>{item.label}</span>\n </li>\n ))}\n </ul>\n ) : (\n <>\n <div\n className={styles.gradient}\n style={{ background: `linear-gradient(to right, ${palette.join(\", \")})` }}\n />\n <div className={styles.ticks}>\n <span>{format(min)}</span>\n <span>{format((min + max) / 2)}</span>\n <span>{format(max)}</span>\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAiDA,SAAgB,EAAU,EACtB,UACA,UACA,SAAM,GACN,SAAM,GACN,aAAU,GACV,aAAU,MAAM,OAAO,KAAK,MAAM,CAAC,CAAC,GACpC,cACA,GAAG,KACY;CACf,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,QAAQ,CAAS;EAAG,GAAI;EAAlD,UAAA,CACK,KAAS,QAAQ,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAQ,UAAA;EAAW,CAAA,GAE3D,IACG,kBAAC,MAAD;GAAI,WAAW,EAAO;GACjB,UAAA,EAAM,KAAK,GAAM,MACd,kBAAC,MAAD;IAAY,WAAW,EAAO;IAA9B,UAAA,CACI,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAQ,OAAO,EAAE,YAAY,EAAK,MAAM;IAAI,CAAA,GACpE,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAY,UAAA,EAAK;IAAY,CAAA,CACrD;GAHK,GAAA,CAGL,CACP;EACD,CAAA,IAEJ,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;GACI,WAAW,EAAO;GAClB,OAAO,EAAE,YAAY,6BAA6B,EAAQ,KAAK,IAAI,EAAE,GAAG;EAC3E,CAAA,GACD,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAvB,UAAA;IACI,kBAAC,QAAD,EAAA,UAAO,EAAO,CAAG,EAAQ,CAAA;IACzB,kBAAC,QAAD,EAAA,UAAO,GAAQ,IAAM,KAAO,CAAC,EAAQ,CAAA;IACrC,kBAAC,QAAD,EAAA,UAAO,EAAO,CAAG,EAAQ,CAAA;GACxB;EACP,CAAA,CAAA,EAAA,CAAA,CAEL;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"MapMarkers.cjs","names":[],"sources":["../../src/br/MapMarkers.tsx"],"sourcesContent":["import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoMarker } from \"@/geo/types\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface MapMarkersProps {\n /** Projection used by the host map (markers must share it to line up). */\n projection: FittedProjection;\n /** Pins to plot. */\n markers: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\n/**\n * SVG overlay of point markers, projected with the host map's projection so\n * they align with its shapes. Each marker carries a native `<title>` (its\n * `label`) and is clickable when `onMarkerClick` is set.\n */\nexport function MapMarkers({ projection, markers, onMarkerClick }: MapMarkersProps) {\n return (\n <g className={styles.markers}>\n {markers.map((marker, index) => {\n const p = projection.project(marker);\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? index}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n fill={marker.color ?? \"var(--tempest-primary)\"}\n data-marker-id={marker.id ?? index}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, index) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, index);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n </g>\n );\n}\n"],"mappings":"6EAkBA,SAAgB,EAAW,CAAE,aAAY,UAAS,iBAAkC,CAChF,OACI,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,iBAChB,EAAQ,KAAK,EAAQ,IAAU,CAC5B,IAAM,EAAI,EAAW,QAAQ,CAAM,EAC7B,EAAc,EAAQ,EAC5B,OACI,EAAA,EAAA,IAAA,CAAC,SAAD,CAEI,UAAW,EAAA,QAAO,OAClB,GAAI,EAAE,EACN,GAAI,EAAE,EACN,EAAG,EAAO,QAAU,EACpB,KAAM,EAAO,OAAS,yBACtB,iBAAgB,EAAO,IAAM,EAC7B,SAAU,EAAc,EAAI,IAAA,GAC5B,KAAM,EAAc,SAAW,IAAA,GAC/B,aAAY,EAAO,MACnB,QAAS,MAAoB,IAAgB,EAAQ,CAAK,EAAI,IAAA,GAC9D,UACI,EACO,GAAM,EACC,EAAE,MAAQ,SAAW,EAAE,MAAQ,OAC/B,EAAE,eAAe,EACjB,IAAgB,EAAQ,CAAK,EAErC,EACA,IAAA,YAGT,EAAO,OAAQ,EAAA,EAAA,IAAA,CAAC,QAAD,CAAA,SAAQ,EAAO,KAAa,CAAA,EAAI,IAC5C,EAvBC,EAAO,IAAM,CAuBd,CAEhB,CAAC,CACF,CAAA,CAEX"}
1
+ {"version":3,"file":"MapMarkers.cjs","names":[],"sources":["../../src/br/MapMarkers.tsx"],"sourcesContent":["import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoMarker } from \"@/geo/types\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface MapMarkersProps {\n /** Projection used by the host map (markers must share it to line up). */\n projection: FittedProjection;\n /** Pins to plot. */\n markers: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\n/**\n * SVG overlay of point markers, projected with the host map's projection so\n * they align with its shapes. Each marker carries a native `<title>` (its\n * `label`) and is clickable when `onMarkerClick` is set.\n */\nexport function MapMarkers({ projection, markers, onMarkerClick }: MapMarkersProps) {\n return (\n <g className={styles.markers}>\n {markers.map((marker, index) => {\n const p = projection.project(marker);\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? index}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n fill={marker.color ?? \"var(--tempest-primary)\"}\n data-marker-id={marker.id ?? index}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, index) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, index);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n </g>\n );\n}\n"],"mappings":"6EAkBA,SAAgB,EAAW,CAAE,aAAY,UAAS,iBAAkC,CAChF,OACI,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,QAChB,SAAA,EAAQ,KAAK,EAAQ,IAAU,CAC5B,IAAM,EAAI,EAAW,QAAQ,CAAM,EAC7B,EAAc,EAAQ,EAC5B,OACI,EAAA,EAAA,IAAA,CAAC,SAAD,CAEI,UAAW,EAAA,QAAO,OAClB,GAAI,EAAE,EACN,GAAI,EAAE,EACN,EAAG,EAAO,QAAU,EACpB,KAAM,EAAO,OAAS,yBACtB,iBAAgB,EAAO,IAAM,EAC7B,SAAU,EAAc,EAAI,IAAA,GAC5B,KAAM,EAAc,SAAW,IAAA,GAC/B,aAAY,EAAO,MACnB,QAAS,MAAoB,IAAgB,EAAQ,CAAK,EAAI,IAAA,GAC9D,UACI,EACO,GAAM,EACC,EAAE,MAAQ,SAAW,EAAE,MAAQ,OAC/B,EAAE,eAAe,EACjB,IAAgB,EAAQ,CAAK,EAErC,EACA,IAAA,GAGT,SAAA,EAAO,OAAQ,EAAA,EAAA,IAAA,CAAC,QAAD,CAAA,SAAQ,EAAO,KAAa,CAAA,EAAI,IAC5C,EAvBC,EAAO,IAAM,CAuBd,CAEhB,CAAC,CACF,CAAA,CAEX"}
@@ -1 +1 @@
1
- {"version":3,"file":"MapMarkers.js","names":[],"sources":["../../src/br/MapMarkers.tsx"],"sourcesContent":["import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoMarker } from \"@/geo/types\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface MapMarkersProps {\n /** Projection used by the host map (markers must share it to line up). */\n projection: FittedProjection;\n /** Pins to plot. */\n markers: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\n/**\n * SVG overlay of point markers, projected with the host map's projection so\n * they align with its shapes. Each marker carries a native `<title>` (its\n * `label`) and is clickable when `onMarkerClick` is set.\n */\nexport function MapMarkers({ projection, markers, onMarkerClick }: MapMarkersProps) {\n return (\n <g className={styles.markers}>\n {markers.map((marker, index) => {\n const p = projection.project(marker);\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? index}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n fill={marker.color ?? \"var(--tempest-primary)\"}\n data-marker-id={marker.id ?? index}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, index) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, index);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n </g>\n );\n}\n"],"mappings":";;;AAkBA,SAAgB,EAAW,EAAE,eAAY,YAAS,oBAAkC;CAChF,OACI,kBAAC,KAAD;EAAG,WAAW,EAAO;YAChB,EAAQ,KAAK,GAAQ,MAAU;GAC5B,IAAM,IAAI,EAAW,QAAQ,CAAM,GAC7B,IAAc,EAAQ;GAC5B,OACI,kBAAC,UAAD;IAEI,WAAW,EAAO;IAClB,IAAI,EAAE;IACN,IAAI,EAAE;IACN,GAAG,EAAO,UAAU;IACpB,MAAM,EAAO,SAAS;IACtB,kBAAgB,EAAO,MAAM;IAC7B,UAAU,IAAc,IAAI,KAAA;IAC5B,MAAM,IAAc,WAAW,KAAA;IAC/B,cAAY,EAAO;IACnB,SAAS,UAAoB,IAAgB,GAAQ,CAAK,IAAI,KAAA;IAC9D,WACI,KACO,MAAM;KACH,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC/B,EAAE,eAAe,GACjB,IAAgB,GAAQ,CAAK;IAErC,IACA,KAAA;cAGT,EAAO,QAAQ,kBAAC,SAAD,EAAA,UAAQ,EAAO,MAAa,CAAA,IAAI;GAC5C,GAvBC,EAAO,MAAM,CAuBd;EAEhB,CAAC;CACF,CAAA;AAEX"}
1
+ {"version":3,"file":"MapMarkers.js","names":[],"sources":["../../src/br/MapMarkers.tsx"],"sourcesContent":["import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoMarker } from \"@/geo/types\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface MapMarkersProps {\n /** Projection used by the host map (markers must share it to line up). */\n projection: FittedProjection;\n /** Pins to plot. */\n markers: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\n/**\n * SVG overlay of point markers, projected with the host map's projection so\n * they align with its shapes. Each marker carries a native `<title>` (its\n * `label`) and is clickable when `onMarkerClick` is set.\n */\nexport function MapMarkers({ projection, markers, onMarkerClick }: MapMarkersProps) {\n return (\n <g className={styles.markers}>\n {markers.map((marker, index) => {\n const p = projection.project(marker);\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? index}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n fill={marker.color ?? \"var(--tempest-primary)\"}\n data-marker-id={marker.id ?? index}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, index) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, index);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n </g>\n );\n}\n"],"mappings":";;;AAkBA,SAAgB,EAAW,EAAE,eAAY,YAAS,oBAAkC;CAChF,OACI,kBAAC,KAAD;EAAG,WAAW,EAAO;EAChB,UAAA,EAAQ,KAAK,GAAQ,MAAU;GAC5B,IAAM,IAAI,EAAW,QAAQ,CAAM,GAC7B,IAAc,EAAQ;GAC5B,OACI,kBAAC,UAAD;IAEI,WAAW,EAAO;IAClB,IAAI,EAAE;IACN,IAAI,EAAE;IACN,GAAG,EAAO,UAAU;IACpB,MAAM,EAAO,SAAS;IACtB,kBAAgB,EAAO,MAAM;IAC7B,UAAU,IAAc,IAAI,KAAA;IAC5B,MAAM,IAAc,WAAW,KAAA;IAC/B,cAAY,EAAO;IACnB,SAAS,UAAoB,IAAgB,GAAQ,CAAK,IAAI,KAAA;IAC9D,WACI,KACO,MAAM;KACH,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC/B,EAAE,eAAe,GACjB,IAAgB,GAAQ,CAAK;IAErC,IACA,KAAA;IAGT,UAAA,EAAO,QAAQ,kBAAC,SAAD,EAAA,UAAQ,EAAO,MAAa,CAAA,IAAI;GAC5C,GAvBC,EAAO,MAAM,CAuBd;EAEhB,CAAC;CACF,CAAA;AAEX"}
@@ -1 +1 @@
1
- {"version":3,"file":"MunicipalitySearch.cjs","names":[],"sources":["../../src/br/MunicipalitySearch.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type ReactElement } from \"react\";\nimport { Input } from \"@/components/Input\";\nimport { useDebounce } from \"@/hooks\";\nimport { searchMunicipalities, type MunicipalityCentroid } from \"./geocode\";\nimport type { UF } from \"./locations\";\nimport styles from \"./MunicipalitySearch.module.css\";\n\nexport interface MunicipalitySearchProps {\n /** Restrict the search to a single UF. */\n uf?: UF;\n /** Fired when a municipality is picked from the results. */\n onSelect: (municipality: MunicipalityCentroid) => void;\n /** Field label. */\n label?: string;\n /** Input placeholder. Default: `\"Buscar município…\"`. */\n placeholder?: string;\n /** Max results shown. Default: `8`. */\n limit?: number;\n /** Debounce in ms before searching. Default: `250`. */\n debounceMs?: number;\n /** Disable the field. */\n disabled?: boolean;\n}\n\n/**\n * Debounced municipality autocomplete backed by the offline\n * {@link searchMunicipalities} index — no network. Type a partial name, pick a\n * result, and `onSelect` fires with its `{ id, name, uf, latitude, longitude }`.\n * Wire `onSelect` to a `BrazilStateMap` `selected` to highlight it on the map.\n *\n * @example\n * const [uf, setUf] = useState<UF>(\"SP\");\n * const [city, setCity] = useState<string | null>(null);\n * <MunicipalitySearch uf={uf} onSelect={(m) => setCity(m.name)} />\n * <BrazilStateMap uf={uf} selected={city} />\n */\nexport function MunicipalitySearch({\n uf,\n onSelect,\n label,\n placeholder = \"Buscar município…\",\n limit = 8,\n debounceMs = 250,\n disabled = false,\n}: MunicipalitySearchProps): ReactElement {\n const [query, setQuery] = useState<string>(\"\");\n const [results, setResults] = useState<MunicipalityCentroid[]>([]);\n const [open, setOpen] = useState<boolean>(false);\n const debounced = useDebounce(query, debounceMs);\n const listId = useId();\n const blurTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n let active = true;\n if (!debounced.trim()) {\n setResults([]);\n return;\n }\n void searchMunicipalities(debounced, { uf, limit }).then((hits) => {\n if (active) setResults(hits);\n });\n return () => {\n active = false;\n };\n }, [debounced, uf, limit]);\n\n function choose(municipality: MunicipalityCentroid): void {\n onSelect(municipality);\n setQuery(municipality.name);\n setOpen(false);\n }\n\n return (\n <div className={styles.search}>\n <Input\n label={label}\n value={query}\n placeholder={placeholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={open && results.length > 0}\n aria-controls={listId}\n autoComplete=\"off\"\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n onBlur={() => {\n // Delay so a result click registers before the list closes.\n blurTimer.current = setTimeout(() => setOpen(false), 120);\n }}\n />\n {open && results.length > 0 && (\n <ul id={listId} className={styles.results} role=\"listbox\">\n {results.map((m) => (\n <li key={m.id}>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n className={styles.result}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (blurTimer.current) clearTimeout(blurTimer.current);\n choose(m);\n }}\n >\n <span>{m.name}</span>\n <span className={styles.uf}>{m.uf}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"sNAoCA,SAAgB,EAAmB,CAC/B,KACA,WACA,QACA,cAAc,oBACd,QAAQ,EACR,aAAa,IACb,WAAW,IAC2B,CACtC,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAA6B,EAAE,EACvC,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAA+C,CAAC,CAAC,EAC3D,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA6B,EAAK,EACzC,EAAY,EAAA,YAAY,EAAO,CAAU,EACzC,GAAA,EAAA,EAAA,MAAA,CAAe,EACf,GAAA,EAAA,EAAA,OAAA,CAAyD,IAAI,GAEnE,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAS,GACb,GAAI,CAAC,EAAU,KAAK,EAAG,CACnB,EAAW,CAAC,CAAC,EACb,MACJ,CAIA,OAHA,EAAK,qBAAqB,EAAW,CAAE,KAAI,OAAM,CAAC,CAAC,CAAC,KAAM,GAAS,CAC3D,GAAQ,EAAW,CAAI,CAC/B,CAAC,MACY,CACT,EAAS,EACb,CACJ,EAAG,CAAC,EAAW,EAAI,CAAK,CAAC,EAEzB,SAAS,EAAO,EAA0C,CACtD,EAAS,CAAY,EACrB,EAAS,EAAa,IAAI,EAC1B,EAAQ,EAAK,CACjB,CAEA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,gBAAvB,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACW,QACP,MAAO,EACM,cACH,WACV,KAAK,WACL,gBAAe,GAAQ,EAAQ,OAAS,EACxC,gBAAe,EACf,aAAa,MACb,SAAW,GAAM,CACb,EAAS,EAAE,OAAO,KAAK,EACvB,EAAQ,EAAI,CAChB,EACA,YAAe,EAAQ,EAAI,EAC3B,WAAc,CAEV,EAAU,QAAU,eAAiB,EAAQ,EAAK,EAAG,GAAG,CAC5D,CACH,CAAA,EACA,GAAQ,EAAQ,OAAS,IACtB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAQ,UAAW,EAAA,QAAO,QAAS,KAAK,mBAC3C,EAAQ,IAAK,IACV,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,UACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAK,SACL,gBAAe,GACf,UAAW,EAAA,QAAO,OAClB,YAAc,GAAM,EAAE,eAAe,EACrC,YAAe,CACP,EAAU,SAAS,aAAa,EAAU,OAAO,EACrD,EAAO,CAAC,CACZ,WATJ,EAWI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAE,IAAW,CAAA,GACpB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,YAAK,EAAE,EAAS,CAAA,CACpC,GACR,EAfK,EAAE,EAeP,CACP,CACD,CAAA,CAEP,GAEb"}
1
+ {"version":3,"file":"MunicipalitySearch.cjs","names":[],"sources":["../../src/br/MunicipalitySearch.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type ReactElement } from \"react\";\nimport { Input } from \"@/components/Input\";\nimport { useDebounce } from \"@/hooks\";\nimport { searchMunicipalities, type MunicipalityCentroid } from \"./geocode\";\nimport type { UF } from \"./locations\";\nimport styles from \"./MunicipalitySearch.module.css\";\n\nexport interface MunicipalitySearchProps {\n /** Restrict the search to a single UF. */\n uf?: UF;\n /** Fired when a municipality is picked from the results. */\n onSelect: (municipality: MunicipalityCentroid) => void;\n /** Field label. */\n label?: string;\n /** Input placeholder. Default: `\"Buscar município…\"`. */\n placeholder?: string;\n /** Max results shown. Default: `8`. */\n limit?: number;\n /** Debounce in ms before searching. Default: `250`. */\n debounceMs?: number;\n /** Disable the field. */\n disabled?: boolean;\n}\n\n/**\n * Debounced municipality autocomplete backed by the offline\n * {@link searchMunicipalities} index — no network. Type a partial name, pick a\n * result, and `onSelect` fires with its `{ id, name, uf, latitude, longitude }`.\n * Wire `onSelect` to a `BrazilStateMap` `selected` to highlight it on the map.\n *\n * @example\n * const [uf, setUf] = useState<UF>(\"SP\");\n * const [city, setCity] = useState<string | null>(null);\n * <MunicipalitySearch uf={uf} onSelect={(m) => setCity(m.name)} />\n * <BrazilStateMap uf={uf} selected={city} />\n */\nexport function MunicipalitySearch({\n uf,\n onSelect,\n label,\n placeholder = \"Buscar município…\",\n limit = 8,\n debounceMs = 250,\n disabled = false,\n}: MunicipalitySearchProps): ReactElement {\n const [query, setQuery] = useState<string>(\"\");\n const [results, setResults] = useState<MunicipalityCentroid[]>([]);\n const [open, setOpen] = useState<boolean>(false);\n const debounced = useDebounce(query, debounceMs);\n const listId = useId();\n const blurTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n let active = true;\n if (!debounced.trim()) {\n setResults([]);\n return;\n }\n void searchMunicipalities(debounced, { uf, limit }).then((hits) => {\n if (active) setResults(hits);\n });\n return () => {\n active = false;\n };\n }, [debounced, uf, limit]);\n\n function choose(municipality: MunicipalityCentroid): void {\n onSelect(municipality);\n setQuery(municipality.name);\n setOpen(false);\n }\n\n return (\n <div className={styles.search}>\n <Input\n label={label}\n value={query}\n placeholder={placeholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={open && results.length > 0}\n aria-controls={listId}\n autoComplete=\"off\"\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n onBlur={() => {\n // Delay so a result click registers before the list closes.\n blurTimer.current = setTimeout(() => setOpen(false), 120);\n }}\n />\n {open && results.length > 0 && (\n <ul id={listId} className={styles.results} role=\"listbox\">\n {results.map((m) => (\n <li key={m.id}>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n className={styles.result}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (blurTimer.current) clearTimeout(blurTimer.current);\n choose(m);\n }}\n >\n <span>{m.name}</span>\n <span className={styles.uf}>{m.uf}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"sNAoCA,SAAgB,EAAmB,CAC/B,KACA,WACA,QACA,cAAc,oBACd,QAAQ,EACR,aAAa,IACb,WAAW,IAC2B,CACtC,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAiB,EAAE,EACvC,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAiC,CAAC,CAAC,EAC3D,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAkB,EAAK,EACzC,EAAY,EAAA,YAAY,EAAO,CAAU,EACzC,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,GAAA,EAAY,EAAA,OAAA,CAA6C,IAAI,GAEnE,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAS,GACb,GAAI,CAAC,EAAU,KAAK,EAAG,CACnB,EAAW,CAAC,CAAC,EACb,MACJ,CAIA,OAHA,EAAK,qBAAqB,EAAW,CAAE,KAAI,OAAM,CAAC,CAAC,CAAC,KAAM,GAAS,CAC3D,GAAQ,EAAW,CAAI,CAC/B,CAAC,MACY,CACT,EAAS,EACb,CACJ,EAAG,CAAC,EAAW,EAAI,CAAK,CAAC,EAEzB,SAAS,EAAO,EAA0C,CACtD,EAAS,CAAY,EACrB,EAAS,EAAa,IAAI,EAC1B,EAAQ,EAAK,CACjB,CAEA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACW,QACP,MAAO,EACM,cACH,WACV,KAAK,WACL,gBAAe,GAAQ,EAAQ,OAAS,EACxC,gBAAe,EACf,aAAa,MACb,SAAW,GAAM,CACb,EAAS,EAAE,OAAO,KAAK,EACvB,EAAQ,EAAI,CAChB,EACA,YAAe,EAAQ,EAAI,EAC3B,WAAc,CAEV,EAAU,QAAU,eAAiB,EAAQ,EAAK,EAAG,GAAG,CAC5D,CACH,CAAA,EACA,GAAQ,EAAQ,OAAS,IACtB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAQ,UAAW,EAAA,QAAO,QAAS,KAAK,UAC3C,SAAA,EAAQ,IAAK,IACV,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,UACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAK,SACL,gBAAe,GACf,UAAW,EAAA,QAAO,OAClB,YAAc,GAAM,EAAE,eAAe,EACrC,YAAe,CACP,EAAU,SAAS,aAAa,EAAU,OAAO,EACrD,EAAO,CAAC,CACZ,EATJ,SAAA,EAWI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAE,IAAW,CAAA,GACpB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,GAAK,SAAA,EAAE,EAAS,CAAA,CACpC,CACR,CAAA,CAAA,EAfK,EAAE,EAeP,CACP,CACD,CAAA,CAEP,GAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"MunicipalitySearch.js","names":[],"sources":["../../src/br/MunicipalitySearch.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type ReactElement } from \"react\";\nimport { Input } from \"@/components/Input\";\nimport { useDebounce } from \"@/hooks\";\nimport { searchMunicipalities, type MunicipalityCentroid } from \"./geocode\";\nimport type { UF } from \"./locations\";\nimport styles from \"./MunicipalitySearch.module.css\";\n\nexport interface MunicipalitySearchProps {\n /** Restrict the search to a single UF. */\n uf?: UF;\n /** Fired when a municipality is picked from the results. */\n onSelect: (municipality: MunicipalityCentroid) => void;\n /** Field label. */\n label?: string;\n /** Input placeholder. Default: `\"Buscar município…\"`. */\n placeholder?: string;\n /** Max results shown. Default: `8`. */\n limit?: number;\n /** Debounce in ms before searching. Default: `250`. */\n debounceMs?: number;\n /** Disable the field. */\n disabled?: boolean;\n}\n\n/**\n * Debounced municipality autocomplete backed by the offline\n * {@link searchMunicipalities} index — no network. Type a partial name, pick a\n * result, and `onSelect` fires with its `{ id, name, uf, latitude, longitude }`.\n * Wire `onSelect` to a `BrazilStateMap` `selected` to highlight it on the map.\n *\n * @example\n * const [uf, setUf] = useState<UF>(\"SP\");\n * const [city, setCity] = useState<string | null>(null);\n * <MunicipalitySearch uf={uf} onSelect={(m) => setCity(m.name)} />\n * <BrazilStateMap uf={uf} selected={city} />\n */\nexport function MunicipalitySearch({\n uf,\n onSelect,\n label,\n placeholder = \"Buscar município…\",\n limit = 8,\n debounceMs = 250,\n disabled = false,\n}: MunicipalitySearchProps): ReactElement {\n const [query, setQuery] = useState<string>(\"\");\n const [results, setResults] = useState<MunicipalityCentroid[]>([]);\n const [open, setOpen] = useState<boolean>(false);\n const debounced = useDebounce(query, debounceMs);\n const listId = useId();\n const blurTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n let active = true;\n if (!debounced.trim()) {\n setResults([]);\n return;\n }\n void searchMunicipalities(debounced, { uf, limit }).then((hits) => {\n if (active) setResults(hits);\n });\n return () => {\n active = false;\n };\n }, [debounced, uf, limit]);\n\n function choose(municipality: MunicipalityCentroid): void {\n onSelect(municipality);\n setQuery(municipality.name);\n setOpen(false);\n }\n\n return (\n <div className={styles.search}>\n <Input\n label={label}\n value={query}\n placeholder={placeholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={open && results.length > 0}\n aria-controls={listId}\n autoComplete=\"off\"\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n onBlur={() => {\n // Delay so a result click registers before the list closes.\n blurTimer.current = setTimeout(() => setOpen(false), 120);\n }}\n />\n {open && results.length > 0 && (\n <ul id={listId} className={styles.results} role=\"listbox\">\n {results.map((m) => (\n <li key={m.id}>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n className={styles.result}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (blurTimer.current) clearTimeout(blurTimer.current);\n choose(m);\n }}\n >\n <span>{m.name}</span>\n <span className={styles.uf}>{m.uf}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAmB,EAC/B,OACA,aACA,UACA,iBAAc,qBACd,WAAQ,GACR,gBAAa,KACb,cAAW,MAC2B;CACtC,IAAM,CAAC,GAAO,KAAY,EAAiB,EAAE,GACvC,CAAC,GAAS,KAAc,EAAiC,CAAC,CAAC,GAC3D,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAY,GAAO,CAAU,GACzC,IAAS,EAAM,GACf,IAAY,EAA6C,IAAI;CAEnE,QAAgB;EACZ,IAAI,IAAS;EACb,IAAI,CAAC,EAAU,KAAK,GAAG;GACnB,EAAW,CAAC,CAAC;GACb;EACJ;EAIA,OAHA,EAA0B,GAAW;GAAE;GAAI;EAAM,CAAC,CAAC,CAAC,MAAM,MAAS;GAC/D,AAAI,KAAQ,EAAW,CAAI;EAC/B,CAAC,SACY;GACT,IAAS;EACb;CACJ,GAAG;EAAC;EAAW;EAAI;CAAK,CAAC;CAEzB,SAAS,EAAO,GAA0C;EAGtD,AAFA,EAAS,CAAY,GACrB,EAAS,EAAa,IAAI,GAC1B,EAAQ,EAAK;CACjB;CAEA,OACI,kBAAC,OAAD;EAAK,WAAW,EAAO;YAAvB,CACI,kBAAC,GAAD;GACW;GACP,OAAO;GACM;GACH;GACV,MAAK;GACL,iBAAe,KAAQ,EAAQ,SAAS;GACxC,iBAAe;GACf,cAAa;GACb,WAAW,MAAM;IAEb,AADA,EAAS,EAAE,OAAO,KAAK,GACvB,EAAQ,EAAI;GAChB;GACA,eAAe,EAAQ,EAAI;GAC3B,cAAc;IAEV,EAAU,UAAU,iBAAiB,EAAQ,EAAK,GAAG,GAAG;GAC5D;EACH,CAAA,GACA,KAAQ,EAAQ,SAAS,KACtB,kBAAC,MAAD;GAAI,IAAI;GAAQ,WAAW,EAAO;GAAS,MAAK;aAC3C,EAAQ,KAAK,MACV,kBAAC,MAAD,EAAA,UACI,kBAAC,UAAD;IACI,MAAK;IACL,MAAK;IACL,iBAAe;IACf,WAAW,EAAO;IAClB,cAAc,MAAM,EAAE,eAAe;IACrC,eAAe;KAEX,AADI,EAAU,WAAS,aAAa,EAAU,OAAO,GACrD,EAAO,CAAC;IACZ;cATJ,CAWI,kBAAC,QAAD,EAAA,UAAO,EAAE,KAAW,CAAA,GACpB,kBAAC,QAAD;KAAM,WAAW,EAAO;eAAK,EAAE;IAAS,CAAA,CACpC;MACR,GAfK,EAAE,EAeP,CACP;EACD,CAAA,CAEP;;AAEb"}
1
+ {"version":3,"file":"MunicipalitySearch.js","names":[],"sources":["../../src/br/MunicipalitySearch.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type ReactElement } from \"react\";\nimport { Input } from \"@/components/Input\";\nimport { useDebounce } from \"@/hooks\";\nimport { searchMunicipalities, type MunicipalityCentroid } from \"./geocode\";\nimport type { UF } from \"./locations\";\nimport styles from \"./MunicipalitySearch.module.css\";\n\nexport interface MunicipalitySearchProps {\n /** Restrict the search to a single UF. */\n uf?: UF;\n /** Fired when a municipality is picked from the results. */\n onSelect: (municipality: MunicipalityCentroid) => void;\n /** Field label. */\n label?: string;\n /** Input placeholder. Default: `\"Buscar município…\"`. */\n placeholder?: string;\n /** Max results shown. Default: `8`. */\n limit?: number;\n /** Debounce in ms before searching. Default: `250`. */\n debounceMs?: number;\n /** Disable the field. */\n disabled?: boolean;\n}\n\n/**\n * Debounced municipality autocomplete backed by the offline\n * {@link searchMunicipalities} index — no network. Type a partial name, pick a\n * result, and `onSelect` fires with its `{ id, name, uf, latitude, longitude }`.\n * Wire `onSelect` to a `BrazilStateMap` `selected` to highlight it on the map.\n *\n * @example\n * const [uf, setUf] = useState<UF>(\"SP\");\n * const [city, setCity] = useState<string | null>(null);\n * <MunicipalitySearch uf={uf} onSelect={(m) => setCity(m.name)} />\n * <BrazilStateMap uf={uf} selected={city} />\n */\nexport function MunicipalitySearch({\n uf,\n onSelect,\n label,\n placeholder = \"Buscar município…\",\n limit = 8,\n debounceMs = 250,\n disabled = false,\n}: MunicipalitySearchProps): ReactElement {\n const [query, setQuery] = useState<string>(\"\");\n const [results, setResults] = useState<MunicipalityCentroid[]>([]);\n const [open, setOpen] = useState<boolean>(false);\n const debounced = useDebounce(query, debounceMs);\n const listId = useId();\n const blurTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n let active = true;\n if (!debounced.trim()) {\n setResults([]);\n return;\n }\n void searchMunicipalities(debounced, { uf, limit }).then((hits) => {\n if (active) setResults(hits);\n });\n return () => {\n active = false;\n };\n }, [debounced, uf, limit]);\n\n function choose(municipality: MunicipalityCentroid): void {\n onSelect(municipality);\n setQuery(municipality.name);\n setOpen(false);\n }\n\n return (\n <div className={styles.search}>\n <Input\n label={label}\n value={query}\n placeholder={placeholder}\n disabled={disabled}\n role=\"combobox\"\n aria-expanded={open && results.length > 0}\n aria-controls={listId}\n autoComplete=\"off\"\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n onBlur={() => {\n // Delay so a result click registers before the list closes.\n blurTimer.current = setTimeout(() => setOpen(false), 120);\n }}\n />\n {open && results.length > 0 && (\n <ul id={listId} className={styles.results} role=\"listbox\">\n {results.map((m) => (\n <li key={m.id}>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n className={styles.result}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (blurTimer.current) clearTimeout(blurTimer.current);\n choose(m);\n }}\n >\n <span>{m.name}</span>\n <span className={styles.uf}>{m.uf}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAmB,EAC/B,OACA,aACA,UACA,iBAAc,qBACd,WAAQ,GACR,gBAAa,KACb,cAAW,MAC2B;CACtC,IAAM,CAAC,GAAO,KAAY,EAAiB,EAAE,GACvC,CAAC,GAAS,KAAc,EAAiC,CAAC,CAAC,GAC3D,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAY,GAAO,CAAU,GACzC,IAAS,EAAM,GACf,IAAY,EAA6C,IAAI;CAEnE,QAAgB;EACZ,IAAI,IAAS;EACb,IAAI,CAAC,EAAU,KAAK,GAAG;GACnB,EAAW,CAAC,CAAC;GACb;EACJ;EAIA,OAHA,EAA0B,GAAW;GAAE;GAAI;EAAM,CAAC,CAAC,CAAC,MAAM,MAAS;GAC/D,AAAI,KAAQ,EAAW,CAAI;EAC/B,CAAC,SACY;GACT,IAAS;EACb;CACJ,GAAG;EAAC;EAAW;EAAI;CAAK,CAAC;CAEzB,SAAS,EAAO,GAA0C;EAGtD,AAFA,EAAS,CAAY,GACrB,EAAS,EAAa,IAAI,GAC1B,EAAQ,EAAK;CACjB;CAEA,OACI,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAvB,UAAA,CACI,kBAAC,GAAD;GACW;GACP,OAAO;GACM;GACH;GACV,MAAK;GACL,iBAAe,KAAQ,EAAQ,SAAS;GACxC,iBAAe;GACf,cAAa;GACb,WAAW,MAAM;IAEb,AADA,EAAS,EAAE,OAAO,KAAK,GACvB,EAAQ,EAAI;GAChB;GACA,eAAe,EAAQ,EAAI;GAC3B,cAAc;IAEV,EAAU,UAAU,iBAAiB,EAAQ,EAAK,GAAG,GAAG;GAC5D;EACH,CAAA,GACA,KAAQ,EAAQ,SAAS,KACtB,kBAAC,MAAD;GAAI,IAAI;GAAQ,WAAW,EAAO;GAAS,MAAK;GAC3C,UAAA,EAAQ,KAAK,MACV,kBAAC,MAAD,EAAA,UACI,kBAAC,UAAD;IACI,MAAK;IACL,MAAK;IACL,iBAAe;IACf,WAAW,EAAO;IAClB,cAAc,MAAM,EAAE,eAAe;IACrC,eAAe;KAEX,AADI,EAAU,WAAS,aAAa,EAAU,OAAO,GACrD,EAAO,CAAC;IACZ;IATJ,UAAA,CAWI,kBAAC,QAAD,EAAA,UAAO,EAAE,KAAW,CAAA,GACpB,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAK,UAAA,EAAE;IAAS,CAAA,CACpC;GACR,CAAA,EAAA,GAfK,EAAE,EAeP,CACP;EACD,CAAA,CAEP;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"PixQRCode.cjs","names":[],"sources":["../../src/br/PixQRCode.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — `pix` and `payload` are the two ways to supply the\n * same code (build it, or bring one already built), `size`/`level` are the symbol,\n * and amountLabel/payeeLabel/labels exist so the surrounding copy can be translated\n * without wrapping the component.\n */\nimport type { HTMLAttributes } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { CopyButton } from \"@/components/CopyButton\";\nimport { QRCode } from \"@/components/QRCode\";\nimport type { QRErrorCorrection } from \"@/components/QRCode\";\nimport { cn } from \"@/utils/cn\";\n\nimport { PixError, pixPayload } from \"./pix\";\nimport type { PixInput } from \"./pix\";\nimport styles from \"./PixQRCode.module.css\";\n\n/** Strings the component renders, so an app can translate them. */\nexport interface PixQRCodeLabels {\n /** Accessible name of the symbol. */\n qr: string;\n /** Copy button, idle. */\n copy: string;\n /** Copy button, just clicked. */\n copied: string;\n}\n\nconst DEFAULT_LABELS: PixQRCodeLabels = {\n qr: \"QR code do Pix\",\n copy: \"Copiar código\",\n copied: \"Copiado\",\n};\n\nexport interface PixQRCodeProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * Build the payload from its parts. Mutually exclusive with {@link payload}.\n */\n pix?: PixInput;\n /**\n * A payload your PSP already produced, used verbatim.\n *\n * Prefer this for a dynamic charge: the PSP signed that string, and rebuilding\n * it here from parsed parts would only add a way to get it wrong.\n */\n payload?: string;\n /** Rendered side of the symbol in px, quiet zone included. Default `192`. */\n size?: number;\n /**\n * Error-correction level. Default `\"M\"`.\n *\n * A printed QR that will be photographed at an angle scans better at `\"Q\"` or\n * `\"H\"`; the cost is a denser symbol, so raise the size with it.\n */\n level?: QRErrorCorrection;\n /** Render the copia-e-cola line and its copy button. Default `true`. */\n showCopy?: boolean;\n /** Amount caption, e.g. `\"R$ 25,50\"`. Rendered above the symbol when set. */\n amountLabel?: string;\n /** Payee caption. Rendered under the amount when set. */\n payeeLabel?: string;\n labels?: Partial<PixQRCodeLabels>;\n /** Called after the payload reaches the clipboard. */\n onCopied?: () => void;\n}\n\n/**\n * A Pix QR code with the copia-e-cola string next to it.\n *\n * The two affordances belong together: a QR is unusable on the device that is\n * *showing* it, which is exactly where a mobile checkout puts it, so every real\n * Pix screen ends up needing the copyable string as well. Rendering only the\n * symbol is the single most common mistake in a Pix flow.\n *\n * Everything is computed locally — {@link pixPayload} builds the string and the\n * SDK's own encoder draws the symbol — so the key, the amount and the txid never\n * leave the page. A QR-image service would receive all three.\n *\n * @throws {PixError} When neither `pix` nor `payload` is given, when both are, or\n * when `pix` fails validation. A bad key is a programming or data error rather\n * than a state to render, so it surfaces; wrap the screen in `ErrorBoundary` when\n * the values come from user input.\n *\n * @example\n * <PixQRCode\n * pix={{\n * key: \"loja@tempest.dev\",\n * merchantName: \"Loja Tempest\",\n * merchantCity: \"São Paulo\",\n * amount: 25.5,\n * txid: \"PEDIDO123\",\n * }}\n * amountLabel=\"R$ 25,50\"\n * payeeLabel=\"Loja Tempest\"\n * />\n *\n * @example\n * <PixQRCode payload={charge.brcode} level=\"Q\" size={220} />\n */\nexport function PixQRCode({\n pix,\n payload,\n size = 192,\n level = \"M\",\n showCopy = true,\n amountLabel,\n payeeLabel,\n labels,\n onCopied,\n className,\n ...rest\n}: PixQRCodeProps) {\n const value = useMemo(() => {\n if (payload !== undefined && pix !== undefined) {\n throw new PixError(\"Pass either `pix` or `payload` to PixQRCode, not both.\");\n }\n if (payload !== undefined) return payload.trim();\n if (pix !== undefined) return pixPayload(pix);\n throw new PixError(\"PixQRCode needs either `pix` or `payload`.\");\n }, [pix, payload]);\n\n const text = { ...DEFAULT_LABELS, ...labels };\n\n return (\n <div className={cn(styles.pix, className)} {...rest}>\n {amountLabel !== undefined && <span className={styles.amount}>{amountLabel}</span>}\n {payeeLabel !== undefined && <span className={styles.payee}>{payeeLabel}</span>}\n <div className={styles.symbol}>\n <QRCode value={value} size={size} level={level} label={text.qr} margin={2} />\n </div>\n {showCopy && (\n <div className={styles.copy}>\n <code className={styles.payload} data-testid=\"pix-payload\">\n {value}\n </code>\n <CopyButton value={value} onCopied={onCopied}>\n <span className={styles.copyIdle}>{text.copy}</span>\n <span className={styles.copyDone}>{text.copied}</span>\n </CopyButton>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"sPA4BA,IAAM,EAAkC,CACpC,GAAI,iBACJ,KAAM,gBACN,OAAQ,SACZ,EAmEA,SAAgB,EAAU,CACtB,MACA,UACA,OAAO,IACP,QAAQ,IACR,WAAW,GACX,cACA,aACA,SACA,WACA,YACA,GAAG,GACY,CACf,IAAM,GAAA,EAAA,EAAA,QAAA,KAAsB,CACxB,GAAI,IAAY,IAAA,IAAa,IAAQ,IAAA,GACjC,MAAM,IAAI,EAAA,SAAS,wDAAwD,EAE/E,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,EAC/C,GAAI,IAAQ,IAAA,GAAW,OAAO,EAAA,WAAW,CAAG,EAC5C,MAAM,IAAI,EAAA,SAAS,4CAA4C,CACnE,EAAG,CAAC,EAAK,CAAO,CAAC,EAEX,EAAO,CAAE,GAAG,EAAgB,GAAG,CAAO,EAE5C,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,CAAS,EAAG,GAAI,WAA/C,CACK,IAAgB,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,gBAAS,CAAkB,CAAA,EAChF,IAAe,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,CAAiB,CAAA,GAC9E,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,iBACnB,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAe,QAAa,OAAa,QAAO,MAAO,EAAK,GAAI,OAAQ,CAAI,CAAA,CAC3E,CAAA,EACJ,IACG,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,cAAvB,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAS,cAAY,uBACxC,CACC,CAAA,GACN,EAAA,EAAA,KAAA,CAAC,EAAA,WAAD,CAAmB,QAAiB,oBAApC,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBAAW,EAAK,IAAW,CAAA,GACnD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBAAW,EAAK,MAAa,CAAA,CAC7C,GACX,GAER,GAEb"}
1
+ {"version":3,"file":"PixQRCode.cjs","names":[],"sources":["../../src/br/PixQRCode.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — `pix` and `payload` are the two ways to supply the\n * same code (build it, or bring one already built), `size`/`level` are the symbol,\n * and amountLabel/payeeLabel/labels exist so the surrounding copy can be translated\n * without wrapping the component.\n */\nimport type { HTMLAttributes } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { CopyButton } from \"@/components/CopyButton\";\nimport { QRCode } from \"@/components/QRCode\";\nimport type { QRErrorCorrection } from \"@/components/QRCode\";\nimport { cn } from \"@/utils/cn\";\n\nimport { PixError, pixPayload } from \"./pix\";\nimport type { PixInput } from \"./pix\";\nimport styles from \"./PixQRCode.module.css\";\n\n/** Strings the component renders, so an app can translate them. */\nexport interface PixQRCodeLabels {\n /** Accessible name of the symbol. */\n qr: string;\n /** Copy button, idle. */\n copy: string;\n /** Copy button, just clicked. */\n copied: string;\n}\n\nconst DEFAULT_LABELS: PixQRCodeLabels = {\n qr: \"QR code do Pix\",\n copy: \"Copiar código\",\n copied: \"Copiado\",\n};\n\nexport interface PixQRCodeProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * Build the payload from its parts. Mutually exclusive with {@link payload}.\n */\n pix?: PixInput;\n /**\n * A payload your PSP already produced, used verbatim.\n *\n * Prefer this for a dynamic charge: the PSP signed that string, and rebuilding\n * it here from parsed parts would only add a way to get it wrong.\n */\n payload?: string;\n /** Rendered side of the symbol in px, quiet zone included. Default `192`. */\n size?: number;\n /**\n * Error-correction level. Default `\"M\"`.\n *\n * A printed QR that will be photographed at an angle scans better at `\"Q\"` or\n * `\"H\"`; the cost is a denser symbol, so raise the size with it.\n */\n level?: QRErrorCorrection;\n /** Render the copia-e-cola line and its copy button. Default `true`. */\n showCopy?: boolean;\n /** Amount caption, e.g. `\"R$ 25,50\"`. Rendered above the symbol when set. */\n amountLabel?: string;\n /** Payee caption. Rendered under the amount when set. */\n payeeLabel?: string;\n labels?: Partial<PixQRCodeLabels>;\n /** Called after the payload reaches the clipboard. */\n onCopied?: () => void;\n}\n\n/**\n * A Pix QR code with the copia-e-cola string next to it.\n *\n * The two affordances belong together: a QR is unusable on the device that is\n * *showing* it, which is exactly where a mobile checkout puts it, so every real\n * Pix screen ends up needing the copyable string as well. Rendering only the\n * symbol is the single most common mistake in a Pix flow.\n *\n * Everything is computed locally — {@link pixPayload} builds the string and the\n * SDK's own encoder draws the symbol — so the key, the amount and the txid never\n * leave the page. A QR-image service would receive all three.\n *\n * @throws {PixError} When neither `pix` nor `payload` is given, when both are, or\n * when `pix` fails validation. A bad key is a programming or data error rather\n * than a state to render, so it surfaces; wrap the screen in `ErrorBoundary` when\n * the values come from user input.\n *\n * @example\n * <PixQRCode\n * pix={{\n * key: \"loja@tempest.dev\",\n * merchantName: \"Loja Tempest\",\n * merchantCity: \"São Paulo\",\n * amount: 25.5,\n * txid: \"PEDIDO123\",\n * }}\n * amountLabel=\"R$ 25,50\"\n * payeeLabel=\"Loja Tempest\"\n * />\n *\n * @example\n * <PixQRCode payload={charge.brcode} level=\"Q\" size={220} />\n */\nexport function PixQRCode({\n pix,\n payload,\n size = 192,\n level = \"M\",\n showCopy = true,\n amountLabel,\n payeeLabel,\n labels,\n onCopied,\n className,\n ...rest\n}: PixQRCodeProps) {\n const value = useMemo(() => {\n if (payload !== undefined && pix !== undefined) {\n throw new PixError(\"Pass either `pix` or `payload` to PixQRCode, not both.\");\n }\n if (payload !== undefined) return payload.trim();\n if (pix !== undefined) return pixPayload(pix);\n throw new PixError(\"PixQRCode needs either `pix` or `payload`.\");\n }, [pix, payload]);\n\n const text = { ...DEFAULT_LABELS, ...labels };\n\n return (\n <div className={cn(styles.pix, className)} {...rest}>\n {amountLabel !== undefined && <span className={styles.amount}>{amountLabel}</span>}\n {payeeLabel !== undefined && <span className={styles.payee}>{payeeLabel}</span>}\n <div className={styles.symbol}>\n <QRCode value={value} size={size} level={level} label={text.qr} margin={2} />\n </div>\n {showCopy && (\n <div className={styles.copy}>\n <code className={styles.payload} data-testid=\"pix-payload\">\n {value}\n </code>\n <CopyButton value={value} onCopied={onCopied}>\n <span className={styles.copyIdle}>{text.copy}</span>\n <span className={styles.copyDone}>{text.copied}</span>\n </CopyButton>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"sPA4BA,IAAM,EAAkC,CACpC,GAAI,iBACJ,KAAM,gBACN,OAAQ,SACZ,EAmEA,SAAgB,EAAU,CACtB,MACA,UACA,OAAO,IACP,QAAQ,IACR,WAAW,GACX,cACA,aACA,SACA,WACA,YACA,GAAG,GACY,CACf,IAAM,GAAA,EAAQ,EAAA,QAAA,KAAc,CACxB,GAAI,IAAY,IAAA,IAAa,IAAQ,IAAA,GACjC,MAAM,IAAI,EAAA,SAAS,wDAAwD,EAE/E,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,EAC/C,GAAI,IAAQ,IAAA,GAAW,OAAO,EAAA,WAAW,CAAG,EAC5C,MAAM,IAAI,EAAA,SAAS,4CAA4C,CACnE,EAAG,CAAC,EAAK,CAAO,CAAC,EAEX,EAAO,CAAE,GAAG,EAAgB,GAAG,CAAO,EAE5C,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,CAAS,EAAG,GAAI,EAA/C,SAAA,CACK,IAAgB,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAS,SAAA,CAAkB,CAAA,EAChF,IAAe,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAiB,CAAA,GAC9E,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OACnB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAe,QAAa,OAAa,QAAO,MAAO,EAAK,GAAI,OAAQ,CAAI,CAAA,CAC3E,CAAA,EACJ,IACG,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAS,cAAY,cACxC,SAAA,CACC,CAAA,GACN,EAAA,EAAA,KAAA,CAAC,EAAA,WAAD,CAAmB,QAAiB,WAApC,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAW,SAAA,EAAK,IAAW,CAAA,GACnD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAW,SAAA,EAAK,MAAa,CAAA,CAC7C,CACX,CAAA,CAAA,GAER,GAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"PixQRCode.js","names":[],"sources":["../../src/br/PixQRCode.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — `pix` and `payload` are the two ways to supply the\n * same code (build it, or bring one already built), `size`/`level` are the symbol,\n * and amountLabel/payeeLabel/labels exist so the surrounding copy can be translated\n * without wrapping the component.\n */\nimport type { HTMLAttributes } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { CopyButton } from \"@/components/CopyButton\";\nimport { QRCode } from \"@/components/QRCode\";\nimport type { QRErrorCorrection } from \"@/components/QRCode\";\nimport { cn } from \"@/utils/cn\";\n\nimport { PixError, pixPayload } from \"./pix\";\nimport type { PixInput } from \"./pix\";\nimport styles from \"./PixQRCode.module.css\";\n\n/** Strings the component renders, so an app can translate them. */\nexport interface PixQRCodeLabels {\n /** Accessible name of the symbol. */\n qr: string;\n /** Copy button, idle. */\n copy: string;\n /** Copy button, just clicked. */\n copied: string;\n}\n\nconst DEFAULT_LABELS: PixQRCodeLabels = {\n qr: \"QR code do Pix\",\n copy: \"Copiar código\",\n copied: \"Copiado\",\n};\n\nexport interface PixQRCodeProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * Build the payload from its parts. Mutually exclusive with {@link payload}.\n */\n pix?: PixInput;\n /**\n * A payload your PSP already produced, used verbatim.\n *\n * Prefer this for a dynamic charge: the PSP signed that string, and rebuilding\n * it here from parsed parts would only add a way to get it wrong.\n */\n payload?: string;\n /** Rendered side of the symbol in px, quiet zone included. Default `192`. */\n size?: number;\n /**\n * Error-correction level. Default `\"M\"`.\n *\n * A printed QR that will be photographed at an angle scans better at `\"Q\"` or\n * `\"H\"`; the cost is a denser symbol, so raise the size with it.\n */\n level?: QRErrorCorrection;\n /** Render the copia-e-cola line and its copy button. Default `true`. */\n showCopy?: boolean;\n /** Amount caption, e.g. `\"R$ 25,50\"`. Rendered above the symbol when set. */\n amountLabel?: string;\n /** Payee caption. Rendered under the amount when set. */\n payeeLabel?: string;\n labels?: Partial<PixQRCodeLabels>;\n /** Called after the payload reaches the clipboard. */\n onCopied?: () => void;\n}\n\n/**\n * A Pix QR code with the copia-e-cola string next to it.\n *\n * The two affordances belong together: a QR is unusable on the device that is\n * *showing* it, which is exactly where a mobile checkout puts it, so every real\n * Pix screen ends up needing the copyable string as well. Rendering only the\n * symbol is the single most common mistake in a Pix flow.\n *\n * Everything is computed locally — {@link pixPayload} builds the string and the\n * SDK's own encoder draws the symbol — so the key, the amount and the txid never\n * leave the page. A QR-image service would receive all three.\n *\n * @throws {PixError} When neither `pix` nor `payload` is given, when both are, or\n * when `pix` fails validation. A bad key is a programming or data error rather\n * than a state to render, so it surfaces; wrap the screen in `ErrorBoundary` when\n * the values come from user input.\n *\n * @example\n * <PixQRCode\n * pix={{\n * key: \"loja@tempest.dev\",\n * merchantName: \"Loja Tempest\",\n * merchantCity: \"São Paulo\",\n * amount: 25.5,\n * txid: \"PEDIDO123\",\n * }}\n * amountLabel=\"R$ 25,50\"\n * payeeLabel=\"Loja Tempest\"\n * />\n *\n * @example\n * <PixQRCode payload={charge.brcode} level=\"Q\" size={220} />\n */\nexport function PixQRCode({\n pix,\n payload,\n size = 192,\n level = \"M\",\n showCopy = true,\n amountLabel,\n payeeLabel,\n labels,\n onCopied,\n className,\n ...rest\n}: PixQRCodeProps) {\n const value = useMemo(() => {\n if (payload !== undefined && pix !== undefined) {\n throw new PixError(\"Pass either `pix` or `payload` to PixQRCode, not both.\");\n }\n if (payload !== undefined) return payload.trim();\n if (pix !== undefined) return pixPayload(pix);\n throw new PixError(\"PixQRCode needs either `pix` or `payload`.\");\n }, [pix, payload]);\n\n const text = { ...DEFAULT_LABELS, ...labels };\n\n return (\n <div className={cn(styles.pix, className)} {...rest}>\n {amountLabel !== undefined && <span className={styles.amount}>{amountLabel}</span>}\n {payeeLabel !== undefined && <span className={styles.payee}>{payeeLabel}</span>}\n <div className={styles.symbol}>\n <QRCode value={value} size={size} level={level} label={text.qr} margin={2} />\n </div>\n {showCopy && (\n <div className={styles.copy}>\n <code className={styles.payload} data-testid=\"pix-payload\">\n {value}\n </code>\n <CopyButton value={value} onCopied={onCopied}>\n <span className={styles.copyIdle}>{text.copy}</span>\n <span className={styles.copyDone}>{text.copied}</span>\n </CopyButton>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA4BA,IAAM,IAAkC;CACpC,IAAI;CACJ,MAAM;CACN,QAAQ;AACZ;AAmEA,SAAgB,EAAU,EACtB,QACA,YACA,UAAO,KACP,WAAQ,KACR,cAAW,IACX,gBACA,eACA,WACA,aACA,cACA,GAAG,KACY;CACf,IAAM,IAAQ,QAAc;EACxB,IAAI,MAAY,KAAA,KAAa,MAAQ,KAAA,GACjC,MAAM,IAAI,EAAS,wDAAwD;EAE/E,IAAI,MAAY,KAAA,GAAW,OAAO,EAAQ,KAAK;EAC/C,IAAI,MAAQ,KAAA,GAAW,OAAO,EAAW,CAAG;EAC5C,MAAM,IAAI,EAAS,4CAA4C;CACnE,GAAG,CAAC,GAAK,CAAO,CAAC,GAEX,IAAO;EAAE,GAAG;EAAgB,GAAG;CAAO;CAE5C,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,KAAK,CAAS;EAAG,GAAI;YAA/C;GACK,MAAgB,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAS;GAAkB,CAAA;GAChF,MAAe,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAiB,CAAA;GAC9E,kBAAC,OAAD;IAAK,WAAW,EAAO;cACnB,kBAAC,GAAD;KAAe;KAAa;KAAa;KAAO,OAAO,EAAK;KAAI,QAAQ;IAAI,CAAA;GAC3E,CAAA;GACJ,KACG,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAvB,CACI,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAS,eAAY;eACxC;IACC,CAAA,GACN,kBAAC,GAAD;KAAmB;KAAiB;eAApC,CACI,kBAAC,QAAD;MAAM,WAAW,EAAO;gBAAW,EAAK;KAAW,CAAA,GACnD,kBAAC,QAAD;MAAM,WAAW,EAAO;gBAAW,EAAK;KAAa,CAAA,CAC7C;MACX;;EAER;;AAEb"}
1
+ {"version":3,"file":"PixQRCode.js","names":[],"sources":["../../src/br/PixQRCode.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — `pix` and `payload` are the two ways to supply the\n * same code (build it, or bring one already built), `size`/`level` are the symbol,\n * and amountLabel/payeeLabel/labels exist so the surrounding copy can be translated\n * without wrapping the component.\n */\nimport type { HTMLAttributes } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { CopyButton } from \"@/components/CopyButton\";\nimport { QRCode } from \"@/components/QRCode\";\nimport type { QRErrorCorrection } from \"@/components/QRCode\";\nimport { cn } from \"@/utils/cn\";\n\nimport { PixError, pixPayload } from \"./pix\";\nimport type { PixInput } from \"./pix\";\nimport styles from \"./PixQRCode.module.css\";\n\n/** Strings the component renders, so an app can translate them. */\nexport interface PixQRCodeLabels {\n /** Accessible name of the symbol. */\n qr: string;\n /** Copy button, idle. */\n copy: string;\n /** Copy button, just clicked. */\n copied: string;\n}\n\nconst DEFAULT_LABELS: PixQRCodeLabels = {\n qr: \"QR code do Pix\",\n copy: \"Copiar código\",\n copied: \"Copiado\",\n};\n\nexport interface PixQRCodeProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * Build the payload from its parts. Mutually exclusive with {@link payload}.\n */\n pix?: PixInput;\n /**\n * A payload your PSP already produced, used verbatim.\n *\n * Prefer this for a dynamic charge: the PSP signed that string, and rebuilding\n * it here from parsed parts would only add a way to get it wrong.\n */\n payload?: string;\n /** Rendered side of the symbol in px, quiet zone included. Default `192`. */\n size?: number;\n /**\n * Error-correction level. Default `\"M\"`.\n *\n * A printed QR that will be photographed at an angle scans better at `\"Q\"` or\n * `\"H\"`; the cost is a denser symbol, so raise the size with it.\n */\n level?: QRErrorCorrection;\n /** Render the copia-e-cola line and its copy button. Default `true`. */\n showCopy?: boolean;\n /** Amount caption, e.g. `\"R$ 25,50\"`. Rendered above the symbol when set. */\n amountLabel?: string;\n /** Payee caption. Rendered under the amount when set. */\n payeeLabel?: string;\n labels?: Partial<PixQRCodeLabels>;\n /** Called after the payload reaches the clipboard. */\n onCopied?: () => void;\n}\n\n/**\n * A Pix QR code with the copia-e-cola string next to it.\n *\n * The two affordances belong together: a QR is unusable on the device that is\n * *showing* it, which is exactly where a mobile checkout puts it, so every real\n * Pix screen ends up needing the copyable string as well. Rendering only the\n * symbol is the single most common mistake in a Pix flow.\n *\n * Everything is computed locally — {@link pixPayload} builds the string and the\n * SDK's own encoder draws the symbol — so the key, the amount and the txid never\n * leave the page. A QR-image service would receive all three.\n *\n * @throws {PixError} When neither `pix` nor `payload` is given, when both are, or\n * when `pix` fails validation. A bad key is a programming or data error rather\n * than a state to render, so it surfaces; wrap the screen in `ErrorBoundary` when\n * the values come from user input.\n *\n * @example\n * <PixQRCode\n * pix={{\n * key: \"loja@tempest.dev\",\n * merchantName: \"Loja Tempest\",\n * merchantCity: \"São Paulo\",\n * amount: 25.5,\n * txid: \"PEDIDO123\",\n * }}\n * amountLabel=\"R$ 25,50\"\n * payeeLabel=\"Loja Tempest\"\n * />\n *\n * @example\n * <PixQRCode payload={charge.brcode} level=\"Q\" size={220} />\n */\nexport function PixQRCode({\n pix,\n payload,\n size = 192,\n level = \"M\",\n showCopy = true,\n amountLabel,\n payeeLabel,\n labels,\n onCopied,\n className,\n ...rest\n}: PixQRCodeProps) {\n const value = useMemo(() => {\n if (payload !== undefined && pix !== undefined) {\n throw new PixError(\"Pass either `pix` or `payload` to PixQRCode, not both.\");\n }\n if (payload !== undefined) return payload.trim();\n if (pix !== undefined) return pixPayload(pix);\n throw new PixError(\"PixQRCode needs either `pix` or `payload`.\");\n }, [pix, payload]);\n\n const text = { ...DEFAULT_LABELS, ...labels };\n\n return (\n <div className={cn(styles.pix, className)} {...rest}>\n {amountLabel !== undefined && <span className={styles.amount}>{amountLabel}</span>}\n {payeeLabel !== undefined && <span className={styles.payee}>{payeeLabel}</span>}\n <div className={styles.symbol}>\n <QRCode value={value} size={size} level={level} label={text.qr} margin={2} />\n </div>\n {showCopy && (\n <div className={styles.copy}>\n <code className={styles.payload} data-testid=\"pix-payload\">\n {value}\n </code>\n <CopyButton value={value} onCopied={onCopied}>\n <span className={styles.copyIdle}>{text.copy}</span>\n <span className={styles.copyDone}>{text.copied}</span>\n </CopyButton>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA4BA,IAAM,IAAkC;CACpC,IAAI;CACJ,MAAM;CACN,QAAQ;AACZ;AAmEA,SAAgB,EAAU,EACtB,QACA,YACA,UAAO,KACP,WAAQ,KACR,cAAW,IACX,gBACA,eACA,WACA,aACA,cACA,GAAG,KACY;CACf,IAAM,IAAQ,QAAc;EACxB,IAAI,MAAY,KAAA,KAAa,MAAQ,KAAA,GACjC,MAAM,IAAI,EAAS,wDAAwD;EAE/E,IAAI,MAAY,KAAA,GAAW,OAAO,EAAQ,KAAK;EAC/C,IAAI,MAAQ,KAAA,GAAW,OAAO,EAAW,CAAG;EAC5C,MAAM,IAAI,EAAS,4CAA4C;CACnE,GAAG,CAAC,GAAK,CAAO,CAAC,GAEX,IAAO;EAAE,GAAG;EAAgB,GAAG;CAAO;CAE5C,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,KAAK,CAAS;EAAG,GAAI;EAA/C,UAAA;GACK,MAAgB,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAS,UAAA;GAAkB,CAAA;GAChF,MAAe,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAQ,UAAA;GAAiB,CAAA;GAC9E,kBAAC,OAAD;IAAK,WAAW,EAAO;IACnB,UAAA,kBAAC,GAAD;KAAe;KAAa;KAAa;KAAO,OAAO,EAAK;KAAI,QAAQ;IAAI,CAAA;GAC3E,CAAA;GACJ,KACG,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAvB,UAAA,CACI,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAS,eAAY;KACxC,UAAA;IACC,CAAA,GACN,kBAAC,GAAD;KAAmB;KAAiB;KAApC,UAAA,CACI,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAW,UAAA,EAAK;KAAW,CAAA,GACnD,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAW,UAAA,EAAK;KAAa,CAAA,CAC7C;IACX,CAAA,CAAA;;EAER;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-map-hover.cjs","names":[],"sources":["../../src/br/use-map-hover.ts"],"sourcesContent":["import { useCallback, useState, type MouseEvent, type RefObject } from \"react\";\n\n/** The currently hovered item plus cursor position within the map container. */\nexport interface MapHover<T> {\n item: T;\n x: number;\n y: number;\n}\n\n/**\n * Track which shape the cursor is over and where, for a floating map tooltip.\n * Positions are computed relative to `containerRef` so the tooltip can be\n * placed with plain `left`/`top` inside the (relatively positioned) container.\n */\nexport function useMapHover<T>(containerRef: RefObject<HTMLDivElement | null>): {\n hover: MapHover<T> | null;\n onMove: (item: T, event: MouseEvent) => void;\n onLeave: () => void;\n} {\n const [hover, setHover] = useState<MapHover<T> | null>(null);\n\n const onMove = useCallback(\n (item: T, event: MouseEvent): void => {\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n setHover({ item, x: event.clientX - rect.left, y: event.clientY - rect.top });\n },\n [containerRef],\n );\n\n const onLeave = useCallback((): void => setHover(null), []);\n\n return { hover, onMove, onLeave };\n}\n"],"mappings":"uBAcA,SAAgB,EAAe,EAI7B,CACE,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAyC,IAAI,EAa3D,MAAO,CAAE,QAAO,QAAA,EAAA,EAAA,YAAA,EAVX,EAAS,IAA4B,CAClC,IAAM,EAAO,EAAa,SAAS,sBAAsB,EACpD,GACL,EAAS,CAAE,OAAM,EAAG,EAAM,QAAU,EAAK,KAAM,EAAG,EAAM,QAAU,EAAK,GAAI,CAAC,CAChF,EACA,CAAC,CAAY,CAKD,EAAQ,SAAA,EAAA,EAAA,YAAA,KAFgB,EAAS,IAAI,EAAG,CAAC,CAEjC,CAAQ,CACpC"}
1
+ {"version":3,"file":"use-map-hover.cjs","names":[],"sources":["../../src/br/use-map-hover.ts"],"sourcesContent":["import { useCallback, useState, type MouseEvent, type RefObject } from \"react\";\n\n/** The currently hovered item plus cursor position within the map container. */\nexport interface MapHover<T> {\n item: T;\n x: number;\n y: number;\n}\n\n/**\n * Track which shape the cursor is over and where, for a floating map tooltip.\n * Positions are computed relative to `containerRef` so the tooltip can be\n * placed with plain `left`/`top` inside the (relatively positioned) container.\n */\nexport function useMapHover<T>(containerRef: RefObject<HTMLDivElement | null>): {\n hover: MapHover<T> | null;\n onMove: (item: T, event: MouseEvent) => void;\n onLeave: () => void;\n} {\n const [hover, setHover] = useState<MapHover<T> | null>(null);\n\n const onMove = useCallback(\n (item: T, event: MouseEvent): void => {\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n setHover({ item, x: event.clientX - rect.left, y: event.clientY - rect.top });\n },\n [containerRef],\n );\n\n const onLeave = useCallback((): void => setHover(null), []);\n\n return { hover, onMove, onLeave };\n}\n"],"mappings":"uBAcA,SAAgB,EAAe,EAI7B,CACE,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAA6B,IAAI,EAa3D,MAAO,CAAE,QAAO,QAAA,EAXD,EAAA,YAAA,EACV,EAAS,IAA4B,CAClC,IAAM,EAAO,EAAa,SAAS,sBAAsB,EACpD,GACL,EAAS,CAAE,OAAM,EAAG,EAAM,QAAU,EAAK,KAAM,EAAG,EAAM,QAAU,EAAK,GAAI,CAAC,CAChF,EACA,CAAC,CAAY,CAKD,EAAQ,SAAA,EAFR,EAAA,YAAA,KAAwB,EAAS,IAAI,EAAG,CAAC,CAEjC,CAAQ,CACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-map-zoom.cjs","names":[],"sources":["../../src/br/use-map-zoom.ts"],"sourcesContent":["import { useCallback, useRef, useState, type PointerEvent, type WheelEvent } from \"react\";\n\n/** Pan/zoom transform state. */\nexport interface MapTransform {\n /** Zoom factor (1 = fit). */\n k: number;\n /** X translation in pixels. */\n x: number;\n /** Y translation in pixels. */\n y: number;\n}\n\nconst MIN_K = 1;\nconst MAX_K = 12;\n\nexport interface UseMapZoom {\n /** SVG `transform` string for the content `<g>`. */\n transform: string;\n /** Current zoom factor. */\n zoom: number;\n /** True when panned/zoomed away from the initial fit. */\n isTransformed: boolean;\n /** Reset to the initial fit. */\n reset: () => void;\n /** Handlers to spread on the `<svg>` element. */\n handlers: {\n onWheel?: (e: WheelEvent) => void;\n onPointerDown?: (e: PointerEvent) => void;\n onPointerMove?: (e: PointerEvent) => void;\n onPointerUp?: (e: PointerEvent) => void;\n onPointerLeave?: (e: PointerEvent) => void;\n onDoubleClick?: () => void;\n style?: { cursor: string; touchAction: string };\n };\n}\n\n/**\n * Wheel-zoom (anchored at the cursor) + drag-pan for an SVG map. Returns a\n * `transform` to apply to a content `<g>` and handlers for the `<svg>`. A no-op\n * (identity transform, empty handlers) when `enabled` is false.\n */\nexport function useMapZoom(enabled: boolean): UseMapZoom {\n const [t, setT] = useState<MapTransform>({ k: 1, x: 0, y: 0 });\n const drag = useRef<{ x: number; y: number; ox: number; oy: number } | null>(null);\n\n const reset = useCallback(() => setT({ k: 1, x: 0, y: 0 }), []);\n\n const onWheel = useCallback((e: WheelEvent) => {\n e.preventDefault();\n const rect = e.currentTarget.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n setT((prev) => {\n const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15;\n const k = Math.max(MIN_K, Math.min(MAX_K, prev.k * factor));\n const scale = k / prev.k;\n return { k, x: cx - (cx - prev.x) * scale, y: cy - (cy - prev.y) * scale };\n });\n }, []);\n\n const onPointerDown = useCallback((e: PointerEvent) => {\n drag.current = { x: e.clientX, y: e.clientY, ox: 0, oy: 0 };\n e.currentTarget.setPointerCapture?.(e.pointerId);\n }, []);\n\n const onPointerMove = useCallback((e: PointerEvent) => {\n if (!drag.current) return;\n const dx = e.clientX - drag.current.x;\n const dy = e.clientY - drag.current.y;\n drag.current.x = e.clientX;\n drag.current.y = e.clientY;\n setT((prev) => ({ ...prev, x: prev.x + dx, y: prev.y + dy }));\n }, []);\n\n const endDrag = useCallback((e: PointerEvent) => {\n drag.current = null;\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n }, []);\n\n if (!enabled) {\n return {\n transform: \"\",\n zoom: 1,\n isTransformed: false,\n reset,\n handlers: {},\n };\n }\n\n const isTransformed = t.k !== 1 || t.x !== 0 || t.y !== 0;\n return {\n transform: `translate(${t.x} ${t.y}) scale(${t.k})`,\n zoom: t.k,\n isTransformed,\n reset,\n handlers: {\n onWheel,\n onPointerDown,\n onPointerMove,\n onPointerUp: endDrag,\n onPointerLeave: endDrag,\n onDoubleClick: reset,\n style: { cursor: isTransformed ? \"grab\" : \"default\", touchAction: \"none\" },\n },\n };\n}\n"],"mappings":"uBAYA,IAAM,EAAQ,EACR,EAAQ,GA4Bd,SAAgB,EAAW,EAA8B,CACrD,GAAM,CAAC,EAAG,IAAA,EAAA,EAAA,SAAA,CAA+B,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,CAAE,CAAC,EACvD,GAAA,EAAA,EAAA,OAAA,CAAuE,IAAI,EAE3E,GAAA,EAAA,EAAA,YAAA,KAA0B,EAAK,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,CAAE,CAAC,EAAG,CAAC,CAAC,EAExD,GAAA,EAAA,EAAA,YAAA,CAAuB,GAAkB,CAC3C,EAAE,eAAe,EACjB,IAAM,EAAO,EAAE,cAAc,sBAAsB,EAC7C,EAAK,EAAE,QAAU,EAAK,KACtB,EAAK,EAAE,QAAU,EAAK,IAC5B,EAAM,GAAS,CACX,IAAM,EAAS,EAAE,OAAS,EAAI,KAAO,EAAI,KACnC,EAAI,KAAK,IAAI,EAAO,KAAK,IAAI,EAAO,EAAK,EAAI,CAAM,CAAC,EACpD,EAAQ,EAAI,EAAK,EACvB,MAAO,CAAE,IAAG,EAAG,GAAM,EAAK,EAAK,GAAK,EAAO,EAAG,GAAM,EAAK,EAAK,GAAK,CAAM,CAC7E,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAA6B,GAAoB,CACnD,EAAK,QAAU,CAAE,EAAG,EAAE,QAAS,EAAG,EAAE,QAAS,GAAI,EAAG,GAAI,CAAE,EAC1D,EAAE,cAAc,oBAAoB,EAAE,SAAS,CACnD,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAA6B,GAAoB,CACnD,GAAI,CAAC,EAAK,QAAS,OACnB,IAAM,EAAK,EAAE,QAAU,EAAK,QAAQ,EAC9B,EAAK,EAAE,QAAU,EAAK,QAAQ,EACpC,EAAK,QAAQ,EAAI,EAAE,QACnB,EAAK,QAAQ,EAAI,EAAE,QACnB,EAAM,IAAU,CAAE,GAAG,EAAM,EAAG,EAAK,EAAI,EAAI,EAAG,EAAK,EAAI,CAAG,EAAE,CAChE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAAuB,GAAoB,CAC7C,EAAK,QAAU,KACf,EAAE,cAAc,wBAAwB,EAAE,SAAS,CACvD,EAAG,CAAC,CAAC,EAEL,GAAI,CAAC,EACD,MAAO,CACH,UAAW,GACX,KAAM,EACN,cAAe,GACf,QACA,SAAU,CAAC,CACf,EAGJ,IAAM,EAAgB,EAAE,IAAM,GAAK,EAAE,IAAM,GAAK,EAAE,IAAM,EACxD,MAAO,CACH,UAAW,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,GACjD,KAAM,EAAE,EACR,gBACA,QACA,SAAU,CACN,UACA,gBACA,gBACA,YAAa,EACb,eAAgB,EAChB,cAAe,EACf,MAAO,CAAE,OAAQ,EAAgB,OAAS,UAAW,YAAa,MAAO,CAC7E,CACJ,CACJ"}
1
+ {"version":3,"file":"use-map-zoom.cjs","names":[],"sources":["../../src/br/use-map-zoom.ts"],"sourcesContent":["import { useCallback, useRef, useState, type PointerEvent, type WheelEvent } from \"react\";\n\n/** Pan/zoom transform state. */\nexport interface MapTransform {\n /** Zoom factor (1 = fit). */\n k: number;\n /** X translation in pixels. */\n x: number;\n /** Y translation in pixels. */\n y: number;\n}\n\nconst MIN_K = 1;\nconst MAX_K = 12;\n\nexport interface UseMapZoom {\n /** SVG `transform` string for the content `<g>`. */\n transform: string;\n /** Current zoom factor. */\n zoom: number;\n /** True when panned/zoomed away from the initial fit. */\n isTransformed: boolean;\n /** Reset to the initial fit. */\n reset: () => void;\n /** Handlers to spread on the `<svg>` element. */\n handlers: {\n onWheel?: (e: WheelEvent) => void;\n onPointerDown?: (e: PointerEvent) => void;\n onPointerMove?: (e: PointerEvent) => void;\n onPointerUp?: (e: PointerEvent) => void;\n onPointerLeave?: (e: PointerEvent) => void;\n onDoubleClick?: () => void;\n style?: { cursor: string; touchAction: string };\n };\n}\n\n/**\n * Wheel-zoom (anchored at the cursor) + drag-pan for an SVG map. Returns a\n * `transform` to apply to a content `<g>` and handlers for the `<svg>`. A no-op\n * (identity transform, empty handlers) when `enabled` is false.\n */\nexport function useMapZoom(enabled: boolean): UseMapZoom {\n const [t, setT] = useState<MapTransform>({ k: 1, x: 0, y: 0 });\n const drag = useRef<{ x: number; y: number; ox: number; oy: number } | null>(null);\n\n const reset = useCallback(() => setT({ k: 1, x: 0, y: 0 }), []);\n\n const onWheel = useCallback((e: WheelEvent) => {\n e.preventDefault();\n const rect = e.currentTarget.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n setT((prev) => {\n const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15;\n const k = Math.max(MIN_K, Math.min(MAX_K, prev.k * factor));\n const scale = k / prev.k;\n return { k, x: cx - (cx - prev.x) * scale, y: cy - (cy - prev.y) * scale };\n });\n }, []);\n\n const onPointerDown = useCallback((e: PointerEvent) => {\n drag.current = { x: e.clientX, y: e.clientY, ox: 0, oy: 0 };\n e.currentTarget.setPointerCapture?.(e.pointerId);\n }, []);\n\n const onPointerMove = useCallback((e: PointerEvent) => {\n if (!drag.current) return;\n const dx = e.clientX - drag.current.x;\n const dy = e.clientY - drag.current.y;\n drag.current.x = e.clientX;\n drag.current.y = e.clientY;\n setT((prev) => ({ ...prev, x: prev.x + dx, y: prev.y + dy }));\n }, []);\n\n const endDrag = useCallback((e: PointerEvent) => {\n drag.current = null;\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n }, []);\n\n if (!enabled) {\n return {\n transform: \"\",\n zoom: 1,\n isTransformed: false,\n reset,\n handlers: {},\n };\n }\n\n const isTransformed = t.k !== 1 || t.x !== 0 || t.y !== 0;\n return {\n transform: `translate(${t.x} ${t.y}) scale(${t.k})`,\n zoom: t.k,\n isTransformed,\n reset,\n handlers: {\n onWheel,\n onPointerDown,\n onPointerMove,\n onPointerUp: endDrag,\n onPointerLeave: endDrag,\n onDoubleClick: reset,\n style: { cursor: isTransformed ? \"grab\" : \"default\", touchAction: \"none\" },\n },\n };\n}\n"],"mappings":"uBAYA,IAAM,EAAQ,EACR,EAAQ,GA4Bd,SAAgB,EAAW,EAA8B,CACrD,GAAM,CAAC,EAAG,IAAA,EAAQ,EAAA,SAAA,CAAuB,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,CAAE,CAAC,EACvD,GAAA,EAAO,EAAA,OAAA,CAAgE,IAAI,EAE3E,GAAA,EAAQ,EAAA,YAAA,KAAkB,EAAK,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,CAAE,CAAC,EAAG,CAAC,CAAC,EAExD,GAAA,EAAU,EAAA,YAAA,CAAa,GAAkB,CAC3C,EAAE,eAAe,EACjB,IAAM,EAAO,EAAE,cAAc,sBAAsB,EAC7C,EAAK,EAAE,QAAU,EAAK,KACtB,EAAK,EAAE,QAAU,EAAK,IAC5B,EAAM,GAAS,CACX,IAAM,EAAS,EAAE,OAAS,EAAI,KAAO,EAAI,KACnC,EAAI,KAAK,IAAI,EAAO,KAAK,IAAI,EAAO,EAAK,EAAI,CAAM,CAAC,EACpD,EAAQ,EAAI,EAAK,EACvB,MAAO,CAAE,IAAG,EAAG,GAAM,EAAK,EAAK,GAAK,EAAO,EAAG,GAAM,EAAK,EAAK,GAAK,CAAM,CAC7E,CAAC,CACL,EAAG,CAAC,CAAC,EAEC,GAAA,EAAgB,EAAA,YAAA,CAAa,GAAoB,CACnD,EAAK,QAAU,CAAE,EAAG,EAAE,QAAS,EAAG,EAAE,QAAS,GAAI,EAAG,GAAI,CAAE,EAC1D,EAAE,cAAc,oBAAoB,EAAE,SAAS,CACnD,EAAG,CAAC,CAAC,EAEC,GAAA,EAAgB,EAAA,YAAA,CAAa,GAAoB,CACnD,GAAI,CAAC,EAAK,QAAS,OACnB,IAAM,EAAK,EAAE,QAAU,EAAK,QAAQ,EAC9B,EAAK,EAAE,QAAU,EAAK,QAAQ,EACpC,EAAK,QAAQ,EAAI,EAAE,QACnB,EAAK,QAAQ,EAAI,EAAE,QACnB,EAAM,IAAU,CAAE,GAAG,EAAM,EAAG,EAAK,EAAI,EAAI,EAAG,EAAK,EAAI,CAAG,EAAE,CAChE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAU,EAAA,YAAA,CAAa,GAAoB,CAC7C,EAAK,QAAU,KACf,EAAE,cAAc,wBAAwB,EAAE,SAAS,CACvD,EAAG,CAAC,CAAC,EAEL,GAAI,CAAC,EACD,MAAO,CACH,UAAW,GACX,KAAM,EACN,cAAe,GACf,QACA,SAAU,CAAC,CACf,EAGJ,IAAM,EAAgB,EAAE,IAAM,GAAK,EAAE,IAAM,GAAK,EAAE,IAAM,EACxD,MAAO,CACH,UAAW,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,GACjD,KAAM,EAAE,EACR,gBACA,QACA,SAAU,CACN,UACA,gBACA,gBACA,YAAa,EACb,eAAgB,EAChB,cAAe,EACf,MAAO,CAAE,OAAQ,EAAgB,OAAS,UAAW,YAAa,MAAO,CAC7E,CACJ,CACJ"}
@@ -1,2 +1,2 @@
1
- function e(e){return typeof MediaRecorder>`u`?null:typeof MediaRecorder.isTypeSupported==`function`?e.find(e=>MediaRecorder.isTypeSupported(e))??null:e[0]??null}function t(t,n){let{candidates:r,kind:i,mimeType:a,audioBitsPerSecond:o,videoBitsPerSecond:s,timesliceMs:c,onChunk:l,onError:u}=n;if(typeof MediaRecorder>`u`)throw Error(`MediaRecorder is not available in this environment.`);let d=a??e(r);if(d===null)throw Error(`This browser cannot record any supported ${i} container.`);if(a!==void 0&&typeof MediaRecorder.isTypeSupported==`function`&&!MediaRecorder.isTypeSupported(a))throw Error(`This browser cannot record "${a}".`);let f=new MediaRecorder(t,{mimeType:d,...o===void 0?{}:{audioBitsPerSecond:o},...s===void 0?{}:{videoBitsPerSecond:s}}),p=[],m=`idle`,h=0,g=0,_=null,v=!1,y=()=>h+(m===`recording`?Date.now()-g:0);return f.ondataavailable=e=>{e.data.size!==0&&(v||(p.push(e.data),l?.(e.data)))},f.onerror=e=>{u?.(e.error??e)},f.onstop=()=>{let e=y();m=`stopped`;let t=_;if(_=null,v){p=[];return}let n=f.mimeType||d;t?.({blob:new Blob(p,{type:n}),mimeType:n,durationMs:e}),p=[]},{mimeType:d,status:()=>m,durationMs:y,start(){m===`recording`||m===`paused`||(p=[],h=0,v=!1,g=Date.now(),m=`recording`,c===void 0?f.start():f.start(c))},pause(){m===`recording`&&(h+=Date.now()-g,m=`paused`,f.pause())},resume(){m===`paused`&&(g=Date.now(),m=`recording`,f.resume())},stop(){return m===`idle`||m===`stopped`?Promise.resolve({blob:new Blob([],{type:d}),mimeType:d,durationMs:0}):new Promise(e=>{_=e,f.stop()})},cancel(){m===`idle`||m===`stopped`||(v=!0,_=null,f.stop())}}}exports.createMediaRecorder=t,exports.pickRecordingMimeType=e;
1
+ function e(e){return typeof MediaRecorder>`u`?null:typeof MediaRecorder.isTypeSupported==`function`?e.find(e=>MediaRecorder.isTypeSupported(e))??null:e[0]??null}function t(t,n){let{candidates:r,kind:i,mimeType:a,audioBitsPerSecond:o,videoBitsPerSecond:s,timesliceMs:c,onChunk:l,onError:u}=n;if(typeof MediaRecorder>`u`)throw Error(`MediaRecorder is not available in this environment.`);let d=a??e(r);if(d===null)throw Error(`This browser cannot record any supported ${i} container.`);if(a!==void 0&&typeof MediaRecorder.isTypeSupported==`function`&&!MediaRecorder.isTypeSupported(a))throw Error(`This browser cannot record "${a}".`);let f=new MediaRecorder(t,{mimeType:d,...o===void 0?{}:{audioBitsPerSecond:o},...s===void 0?{}:{videoBitsPerSecond:s}}),p=[],m=`idle`,h=0,g=0,_=null,v=!1,y=()=>h+(m===`recording`?Date.now()-g:0);return f.ondataavailable=e=>{e.data.size!==0&&(v||(p.push(e.data),l?.(e.data)))},f.onerror=e=>{u?.(e.error??e)},f.onstop=()=>{let e=y();m=`stopped`;let t=_;if(_=null,v){p=[];return}let n=f.mimeType||d;t?.({blob:new Blob(p,{type:n}),mimeType:n,durationMs:e}),p=[]},{mimeType:d,status:()=>m,durationMs:y,start(){m!==`recording`&&m!==`paused`&&(p=[],h=0,v=!1,g=Date.now(),m=`recording`,c===void 0?f.start():f.start(c))},pause(){m===`recording`&&(h+=Date.now()-g,m=`paused`,f.pause())},resume(){m===`paused`&&(g=Date.now(),m=`recording`,f.resume())},stop(){return m===`idle`||m===`stopped`?Promise.resolve({blob:new Blob([],{type:d}),mimeType:d,durationMs:0}):new Promise(e=>{_=e,f.stop()})},cancel(){m!==`idle`&&m!==`stopped`&&(v=!0,_=null,f.stop())}}}exports.createMediaRecorder=t,exports.pickRecordingMimeType=e;
2
2
  //# sourceMappingURL=media-recorder.cjs.map
@@ -36,7 +36,7 @@ function t(t, n) {
36
36
  status: () => m,
37
37
  durationMs: y,
38
38
  start() {
39
- m === "recording" || m === "paused" || (p = [], h = 0, v = !1, g = Date.now(), m = "recording", c === void 0 ? f.start() : f.start(c));
39
+ m !== "recording" && m !== "paused" && (p = [], h = 0, v = !1, g = Date.now(), m = "recording", c === void 0 ? f.start() : f.start(c));
40
40
  },
41
41
  pause() {
42
42
  m === "recording" && (h += Date.now() - g, m = "paused", f.pause());
@@ -54,7 +54,7 @@ function t(t, n) {
54
54
  });
55
55
  },
56
56
  cancel() {
57
- m === "idle" || m === "stopped" || (v = !0, _ = null, f.stop());
57
+ m !== "idle" && m !== "stopped" && (v = !0, _ = null, f.stop());
58
58
  }
59
59
  };
60
60
  }
@@ -1,2 +1,2 @@
1
- const e=require("../hooks/use-stable-callback.cjs"),t=require("../vision/use-camera-stream.cjs"),n=require("./barcode.cjs"),r=require("./use-torch.cjs");let i=require("react");function a(e){return e.readyState>=2&&e.videoWidth>0&&e.videoHeight>0}function o(o={}){let{formats:s=n.DEFAULT_BARCODE_FORMATS,onScan:c,intervalMs:l=200,repeatDelayMs:u=2500,paused:d=!1,detector:f,constraints:p,onError:m}=o,[h,g]=(0,i.useState)(()=>f!==void 0||n.isBarcodeDetectionSupported()),_=t.useCameraStream({constraints:p,enabled:h}),v=r.useTorch(_.stream),[y,b]=(0,i.useState)(f===void 0?[]:s),[x,S]=(0,i.useState)(!1),[C,w]=(0,i.useState)(null),T=(0,i.useRef)(null),E=(0,i.useRef)(null),D=(0,i.useRef)(0),O=e.useStableCallback(e=>c?.(e)),k=e.useStableCallback(e=>m?.(e));return(0,i.useEffect)(()=>{if(f){T.current=f,g(!0),b(s);return}if(!n.isBarcodeDetectionSupported()){T.current=null,g(!1),b([]);return}let e=!1;return n.getSupportedBarcodeFormats().then(t=>{if(e)return;let r=t.length===0?s:s.filter(e=>t.includes(e)),i=r.length>0?n.createBarcodeDetector(r):null;T.current=i,b(i?r:[]),g(i!==null)}),()=>{e=!0}},[f,s.join(`,`)]),(0,i.useEffect)(()=>{if(!h||d||_.status!==`ready`){S(!1);return}let e=!1,t;S(!0);let r=e=>{let t=Date.now();e.rawValue===E.current&&t-D.current<u||(E.current=e.rawValue,D.current=t,w(e),O(e))},i=async()=>{let t=_.videoRef.current,i=T.current;if(!(!t||!i||!a(t)))try{let a=await i.detect(t);if(e)return;for(let e of a){let t=n.normalizeBarcode(e);t.rawValue!==``&&r(t)}}catch(t){e||k(t)}},o=()=>{i().finally(()=>{e||(t=setTimeout(o,l))})};return o(),()=>{e=!0,t!==void 0&&clearTimeout(t),S(!1)}},[h,d,_.status,_.videoRef,l,u,O,k]),{videoRef:_.videoRef,status:_.status,error:_.error,supported:h,formats:y,scanning:x,result:C,torch:v,retry:_.retry}}exports.useBarcodeScanner=o;
1
+ const e=require("../hooks/use-stable-callback.cjs"),t=require("../vision/use-camera-stream.cjs"),n=require("./barcode.cjs"),r=require("./use-torch.cjs");let i=require("react");function a(e){return e.readyState>=2&&e.videoWidth>0&&e.videoHeight>0}function o(o={}){let{formats:s=n.DEFAULT_BARCODE_FORMATS,onScan:c,intervalMs:l=200,repeatDelayMs:u=2500,paused:d=!1,detector:f,constraints:p,onError:m}=o,[h,g]=(0,i.useState)(()=>f!==void 0||n.isBarcodeDetectionSupported()),_=t.useCameraStream({constraints:p,enabled:h}),v=r.useTorch(_.stream),[y,b]=(0,i.useState)(f===void 0?[]:s),[x,S]=(0,i.useState)(!1),[C,w]=(0,i.useState)(null),T=(0,i.useRef)(null),E=(0,i.useRef)(null),D=(0,i.useRef)(0),O=e.useStableCallback(e=>c?.(e)),k=e.useStableCallback(e=>m?.(e)),A=s.join(`,`);return(0,i.useEffect)(()=>{if(f){T.current=f,g(!0),b(s);return}if(!n.isBarcodeDetectionSupported()){T.current=null,g(!1),b([]);return}let e=!1;return n.getSupportedBarcodeFormats().then(t=>{if(e)return;let r=t.length===0?s:s.filter(e=>t.includes(e)),i=r.length>0?n.createBarcodeDetector(r):null;T.current=i,b(i?r:[]),g(i!==null)}),()=>{e=!0}},[f,A]),(0,i.useEffect)(()=>{if(!h||d||_.status!==`ready`){S(!1);return}let e=!1,t;S(!0);let r=e=>{let t=Date.now();e.rawValue===E.current&&t-D.current<u||(E.current=e.rawValue,D.current=t,w(e),O(e))},i=async()=>{let t=_.videoRef.current,i=T.current;if(!(!t||!i||!a(t)))try{let a=await i.detect(t);if(e)return;for(let e of a){let t=n.normalizeBarcode(e);t.rawValue!==``&&r(t)}}catch(t){e||k(t)}},o=()=>{i().finally(()=>{e||(t=setTimeout(o,l))})};return o(),()=>{e=!0,t!==void 0&&clearTimeout(t),S(!1)}},[h,d,_.status,_.videoRef,l,u,O,k]),{videoRef:_.videoRef,status:_.status,error:_.error,supported:h,formats:y,scanning:x,result:C,torch:v,retry:_.retry}}exports.useBarcodeScanner=o;
2
2
  //# sourceMappingURL=use-barcode-scanner.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-barcode-scanner.cjs","names":[],"sources":["../../src/capture/use-barcode-scanner.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the scan loop, the repeat suppression window and the\n * torch control share the same track and the same interval handle; splitting them\n * would leave a timer running against a track another hook stopped.\n */\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\nimport {\n useCameraStream,\n type CameraStreamError,\n type CameraStreamStatus,\n} from \"@/vision/use-camera-stream\";\n\nimport {\n createBarcodeDetector,\n getSupportedBarcodeFormats,\n isBarcodeDetectionSupported,\n normalizeBarcode,\n DEFAULT_BARCODE_FORMATS,\n type BarcodeDetectorLike,\n type BarcodeFormat,\n type BarcodeScanResult,\n} from \"./barcode\";\nimport { useTorch, type UseTorchResult } from \"./use-torch\";\n\n/** Options for {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerOptions {\n /** Symbologies to look for. Defaults to {@link DEFAULT_BARCODE_FORMATS}. */\n formats?: readonly BarcodeFormat[];\n /** Called for every accepted read — that is, after repeat suppression. */\n onScan?: (result: BarcodeScanResult) => void;\n /**\n * How often a frame is examined, in ms. Default 200.\n *\n * Not `requestAnimationFrame`: decoding is 10–40 ms of main-thread work on a\n * phone, so running it per frame competes with the preview it is reading from and\n * makes the video stutter. Five looks per second is faster than a human can aim.\n */\n intervalMs?: number;\n /**\n * Ignore the **same** value again for this long, in ms. Default 2500.\n *\n * A symbol stays in frame for as long as the user holds the camera there, so a\n * scanner without this fires the same code five times a second — which, wired to\n * \"add item to cart\", is a bug the user pays for. A *different* value is never\n * suppressed.\n */\n repeatDelayMs?: number;\n /** Stop looking without releasing the camera — a confirmation sheet is open. */\n paused?: boolean;\n /**\n * A decoder to use instead of the native one.\n *\n * The way to support Safari and Firefox: hand in a polyfill and everything else\n * here works unchanged. See {@link isBarcodeDetectionSupported} for why the SDK\n * does not bundle one.\n */\n detector?: BarcodeDetectorLike;\n /** Camera constraints, forwarded to `useCameraStream`. Defaults to the rear camera. */\n constraints?: MediaStreamConstraints;\n /**\n * A frame the engine refused to decode.\n *\n * Not \"nothing found\" — that resolves to an empty list and is the normal case.\n * This is the engine itself failing, which the loop survives because it is\n * usually transient (a frame arriving between two resolutions).\n */\n onError?: (error: unknown) => void;\n}\n\n/** Value returned by {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerResult {\n /** Attach to a `<video ref={…} muted playsInline />`. */\n videoRef: RefObject<HTMLVideoElement | null>;\n /** Camera lifecycle. `\"ready\"` means the preview is running. */\n status: CameraStreamStatus;\n /** Classified camera error, or `null`. */\n error: CameraStreamError | null;\n /** `false` when there is no decoder — no native API and none injected. */\n supported: boolean;\n /**\n * Formats actually in use: the requested ones intersected with what the engine\n * reports. Empty while the probe is in flight, or when nothing matched.\n */\n formats: readonly BarcodeFormat[];\n /** Whether the detect loop is running right now. */\n scanning: boolean;\n /** The most recent accepted read, or `null`. */\n result: BarcodeScanResult | null;\n /** The LED torch of this camera, when it has one. */\n torch: UseTorchResult;\n /** Re-open the camera after an error (the user changed the permission). */\n retry: () => void;\n}\n\n/** A video is decodable once it has data for the current frame and a real size. */\nfunction frameIsReady(video: HTMLVideoElement): boolean {\n return video.readyState >= 2 && video.videoWidth > 0 && video.videoHeight > 0;\n}\n\n/**\n * Read barcodes and QR codes from the camera.\n *\n * The camera and its classified errors come from `useCameraStream`, so this hook is\n * only the decoding half: it drives a `BarcodeDetector` over the preview on an\n * interval, suppresses the same value repeating, and exposes the torch.\n *\n * **Mounting this opens the camera.** It inherits that from `useCameraStream`, which\n * acquires on mount — so mount it *after* the user asks to scan (a button that reveals\n * the scanner), never on a page that merely contains one. A permission prompt nobody\n * provoked is the most reliable way to earn a permanent block, after which\n * `getUserMedia` rejects without ever prompting again.\n *\n * `supported` deserves a branch in the UI, not an assertion: `BarcodeDetector` is\n * Chromium-only and missing on Windows/Linux desktop, Firefox and everything on iOS.\n * Inject a `detector` to cover those, or tell the user to type the code.\n *\n * @param options - See {@link UseBarcodeScannerOptions}.\n * @returns The camera plumbing plus the scan state.\n *\n * @example\n * const scanner = useBarcodeScanner({\n * formats: [\"ean_13\"],\n * onScan: ({ rawValue }) => addToCart(rawValue),\n * });\n * return <video ref={scanner.videoRef} muted playsInline />;\n */\nexport function useBarcodeScanner(options: UseBarcodeScannerOptions = {}): UseBarcodeScannerResult {\n const {\n formats: requested = DEFAULT_BARCODE_FORMATS,\n onScan,\n intervalMs = 200,\n repeatDelayMs = 2500,\n paused = false,\n detector: injected,\n constraints,\n onError,\n } = options;\n\n const [supported, setSupported] = useState(\n () => injected !== undefined || isBarcodeDetectionSupported(),\n );\n\n /**\n * No decoder, no camera.\n *\n * Opening the camera only to report \"this browser cannot decode barcodes\" spends a\n * permission prompt on nothing — and a refusal is permanent, so it also spends the\n * *next* feature that needs the camera.\n */\n const camera = useCameraStream({ constraints, enabled: supported });\n const torch = useTorch(camera.stream);\n\n const [formats, setFormats] = useState<readonly BarcodeFormat[]>(\n injected !== undefined ? requested : [],\n );\n const [scanning, setScanning] = useState(false);\n const [result, setResult] = useState<BarcodeScanResult | null>(null);\n\n const detectorRef = useRef<BarcodeDetectorLike | null>(null);\n const lastValue = useRef<string | null>(null);\n const lastAt = useRef(0);\n\n const emitScan = useStableCallback((scan: BarcodeScanResult) => onScan?.(scan));\n const emitError = useStableCallback((error: unknown) => onError?.(error));\n\n const requestedKey = requested.join(\",\");\n\n /**\n * Resolve which formats the engine will take, then build the detector.\n *\n * The intersection is not defensive coding: `new BarcodeDetector({ formats })`\n * throws `NotSupportedError` when any entry is unknown to the platform decoder, and\n * that list differs between two Chromium builds on two operating systems. Asking\n * for the intersection is the only way one call site works everywhere.\n */\n useEffect(() => {\n if (injected) {\n detectorRef.current = injected;\n setSupported(true);\n setFormats(requested);\n return;\n }\n if (!isBarcodeDetectionSupported()) {\n detectorRef.current = null;\n setSupported(false);\n setFormats([]);\n return;\n }\n let cancelled = false;\n void getSupportedBarcodeFormats().then((available) => {\n if (cancelled) return;\n const usable =\n available.length === 0\n ? requested\n : requested.filter((format) => available.includes(format));\n const detector = usable.length > 0 ? createBarcodeDetector(usable) : null;\n detectorRef.current = detector;\n setFormats(detector ? usable : []);\n setSupported(detector !== null);\n });\n return () => {\n cancelled = true;\n };\n // `requestedKey` stands in for the array identity, so a caller passing an\n // inline `formats={[\"ean_13\"]}` does not rebuild the detector every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, requestedKey]);\n\n /**\n * Look at a frame every `intervalMs`, and never overlap two looks.\n *\n * The loop re-arms itself *after* each `detect()` settles rather than running on a\n * fixed `setInterval`: decoding sometimes takes longer than the interval, and an\n * interval would then queue calls faster than the engine drains them until the tab\n * is unusable.\n */\n useEffect(() => {\n if (!supported || paused || camera.status !== \"ready\") {\n setScanning(false);\n return;\n }\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n setScanning(true);\n\n const accept = (scan: BarcodeScanResult): void => {\n const now = Date.now();\n const isRepeat =\n scan.rawValue === lastValue.current && now - lastAt.current < repeatDelayMs;\n if (isRepeat) return;\n lastValue.current = scan.rawValue;\n lastAt.current = now;\n setResult(scan);\n emitScan(scan);\n };\n\n const look = async (): Promise<void> => {\n const video = camera.videoRef.current;\n const detector = detectorRef.current;\n if (!video || !detector || !frameIsReady(video)) return;\n try {\n const found = await detector.detect(video);\n if (stopped) return;\n for (const raw of found) {\n const scan = normalizeBarcode(raw);\n if (scan.rawValue !== \"\") accept(scan);\n }\n } catch (error) {\n if (!stopped) emitError(error);\n }\n };\n\n const tick = (): void => {\n void look().finally(() => {\n if (!stopped) timer = setTimeout(tick, intervalMs);\n });\n };\n tick();\n\n return () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n setScanning(false);\n };\n }, [\n supported,\n paused,\n camera.status,\n camera.videoRef,\n intervalMs,\n repeatDelayMs,\n emitScan,\n emitError,\n ]);\n\n return {\n videoRef: camera.videoRef,\n status: camera.status,\n error: camera.error,\n supported,\n formats,\n scanning,\n result,\n torch,\n retry: camera.retry,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-barcode-scanner.cjs","names":[],"sources":["../../src/capture/use-barcode-scanner.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the scan loop, the repeat suppression window and the\n * torch control share the same track and the same interval handle; splitting them\n * would leave a timer running against a track another hook stopped.\n */\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\nimport {\n useCameraStream,\n type CameraStreamError,\n type CameraStreamStatus,\n} from \"@/vision/use-camera-stream\";\n\nimport {\n createBarcodeDetector,\n getSupportedBarcodeFormats,\n isBarcodeDetectionSupported,\n normalizeBarcode,\n DEFAULT_BARCODE_FORMATS,\n type BarcodeDetectorLike,\n type BarcodeFormat,\n type BarcodeScanResult,\n} from \"./barcode\";\nimport { useTorch, type UseTorchResult } from \"./use-torch\";\n\n/** Options for {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerOptions {\n /** Symbologies to look for. Defaults to {@link DEFAULT_BARCODE_FORMATS}. */\n formats?: readonly BarcodeFormat[];\n /** Called for every accepted read — that is, after repeat suppression. */\n onScan?: (result: BarcodeScanResult) => void;\n /**\n * How often a frame is examined, in ms. Default 200.\n *\n * Not `requestAnimationFrame`: decoding is 10–40 ms of main-thread work on a\n * phone, so running it per frame competes with the preview it is reading from and\n * makes the video stutter. Five looks per second is faster than a human can aim.\n */\n intervalMs?: number;\n /**\n * Ignore the **same** value again for this long, in ms. Default 2500.\n *\n * A symbol stays in frame for as long as the user holds the camera there, so a\n * scanner without this fires the same code five times a second — which, wired to\n * \"add item to cart\", is a bug the user pays for. A *different* value is never\n * suppressed.\n */\n repeatDelayMs?: number;\n /** Stop looking without releasing the camera — a confirmation sheet is open. */\n paused?: boolean;\n /**\n * A decoder to use instead of the native one.\n *\n * The way to support Safari and Firefox: hand in a polyfill and everything else\n * here works unchanged. See {@link isBarcodeDetectionSupported} for why the SDK\n * does not bundle one.\n */\n detector?: BarcodeDetectorLike;\n /** Camera constraints, forwarded to `useCameraStream`. Defaults to the rear camera. */\n constraints?: MediaStreamConstraints;\n /**\n * A frame the engine refused to decode.\n *\n * Not \"nothing found\" — that resolves to an empty list and is the normal case.\n * This is the engine itself failing, which the loop survives because it is\n * usually transient (a frame arriving between two resolutions).\n */\n onError?: (error: unknown) => void;\n}\n\n/** Value returned by {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerResult {\n /** Attach to a `<video ref={…} muted playsInline />`. */\n videoRef: RefObject<HTMLVideoElement | null>;\n /** Camera lifecycle. `\"ready\"` means the preview is running. */\n status: CameraStreamStatus;\n /** Classified camera error, or `null`. */\n error: CameraStreamError | null;\n /** `false` when there is no decoder — no native API and none injected. */\n supported: boolean;\n /**\n * Formats actually in use: the requested ones intersected with what the engine\n * reports. Empty while the probe is in flight, or when nothing matched.\n */\n formats: readonly BarcodeFormat[];\n /** Whether the detect loop is running right now. */\n scanning: boolean;\n /** The most recent accepted read, or `null`. */\n result: BarcodeScanResult | null;\n /** The LED torch of this camera, when it has one. */\n torch: UseTorchResult;\n /** Re-open the camera after an error (the user changed the permission). */\n retry: () => void;\n}\n\n/** A video is decodable once it has data for the current frame and a real size. */\nfunction frameIsReady(video: HTMLVideoElement): boolean {\n return video.readyState >= 2 && video.videoWidth > 0 && video.videoHeight > 0;\n}\n\n/**\n * Read barcodes and QR codes from the camera.\n *\n * The camera and its classified errors come from `useCameraStream`, so this hook is\n * only the decoding half: it drives a `BarcodeDetector` over the preview on an\n * interval, suppresses the same value repeating, and exposes the torch.\n *\n * **Mounting this opens the camera.** It inherits that from `useCameraStream`, which\n * acquires on mount — so mount it *after* the user asks to scan (a button that reveals\n * the scanner), never on a page that merely contains one. A permission prompt nobody\n * provoked is the most reliable way to earn a permanent block, after which\n * `getUserMedia` rejects without ever prompting again.\n *\n * `supported` deserves a branch in the UI, not an assertion: `BarcodeDetector` is\n * Chromium-only and missing on Windows/Linux desktop, Firefox and everything on iOS.\n * Inject a `detector` to cover those, or tell the user to type the code.\n *\n * @param options - See {@link UseBarcodeScannerOptions}.\n * @returns The camera plumbing plus the scan state.\n *\n * @example\n * const scanner = useBarcodeScanner({\n * formats: [\"ean_13\"],\n * onScan: ({ rawValue }) => addToCart(rawValue),\n * });\n * return <video ref={scanner.videoRef} muted playsInline />;\n */\nexport function useBarcodeScanner(options: UseBarcodeScannerOptions = {}): UseBarcodeScannerResult {\n const {\n formats: requested = DEFAULT_BARCODE_FORMATS,\n onScan,\n intervalMs = 200,\n repeatDelayMs = 2500,\n paused = false,\n detector: injected,\n constraints,\n onError,\n } = options;\n\n const [supported, setSupported] = useState(\n () => injected !== undefined || isBarcodeDetectionSupported(),\n );\n\n /**\n * No decoder, no camera.\n *\n * Opening the camera only to report \"this browser cannot decode barcodes\" spends a\n * permission prompt on nothing — and a refusal is permanent, so it also spends the\n * *next* feature that needs the camera.\n */\n const camera = useCameraStream({ constraints, enabled: supported });\n const torch = useTorch(camera.stream);\n\n const [formats, setFormats] = useState<readonly BarcodeFormat[]>(\n injected !== undefined ? requested : [],\n );\n const [scanning, setScanning] = useState(false);\n const [result, setResult] = useState<BarcodeScanResult | null>(null);\n\n const detectorRef = useRef<BarcodeDetectorLike | null>(null);\n const lastValue = useRef<string | null>(null);\n const lastAt = useRef(0);\n\n const emitScan = useStableCallback((scan: BarcodeScanResult) => onScan?.(scan));\n const emitError = useStableCallback((error: unknown) => onError?.(error));\n\n const requestedKey = requested.join(\",\");\n\n /**\n * Resolve which formats the engine will take, then build the detector.\n *\n * The intersection is not defensive coding: `new BarcodeDetector({ formats })`\n * throws `NotSupportedError` when any entry is unknown to the platform decoder, and\n * that list differs between two Chromium builds on two operating systems. Asking\n * for the intersection is the only way one call site works everywhere.\n */\n useEffect(() => {\n if (injected) {\n detectorRef.current = injected;\n setSupported(true);\n setFormats(requested);\n return;\n }\n if (!isBarcodeDetectionSupported()) {\n detectorRef.current = null;\n setSupported(false);\n setFormats([]);\n return;\n }\n let cancelled = false;\n void getSupportedBarcodeFormats().then((available) => {\n if (cancelled) return;\n const usable =\n available.length === 0\n ? requested\n : requested.filter((format) => available.includes(format));\n const detector = usable.length > 0 ? createBarcodeDetector(usable) : null;\n detectorRef.current = detector;\n setFormats(detector ? usable : []);\n setSupported(detector !== null);\n });\n return () => {\n cancelled = true;\n };\n // `requestedKey` stands in for the array identity, so a caller passing an\n // inline `formats={[\"ean_13\"]}` does not rebuild the detector every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, requestedKey]);\n\n /**\n * Look at a frame every `intervalMs`, and never overlap two looks.\n *\n * The loop re-arms itself *after* each `detect()` settles rather than running on a\n * fixed `setInterval`: decoding sometimes takes longer than the interval, and an\n * interval would then queue calls faster than the engine drains them until the tab\n * is unusable.\n */\n useEffect(() => {\n if (!supported || paused || camera.status !== \"ready\") {\n setScanning(false);\n return;\n }\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n setScanning(true);\n\n const accept = (scan: BarcodeScanResult): void => {\n const now = Date.now();\n const isRepeat =\n scan.rawValue === lastValue.current && now - lastAt.current < repeatDelayMs;\n if (isRepeat) return;\n lastValue.current = scan.rawValue;\n lastAt.current = now;\n setResult(scan);\n emitScan(scan);\n };\n\n const look = async (): Promise<void> => {\n const video = camera.videoRef.current;\n const detector = detectorRef.current;\n if (!video || !detector || !frameIsReady(video)) return;\n try {\n const found = await detector.detect(video);\n if (stopped) return;\n for (const raw of found) {\n const scan = normalizeBarcode(raw);\n if (scan.rawValue !== \"\") accept(scan);\n }\n } catch (error) {\n if (!stopped) emitError(error);\n }\n };\n\n const tick = (): void => {\n void look().finally(() => {\n if (!stopped) timer = setTimeout(tick, intervalMs);\n });\n };\n tick();\n\n return () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n setScanning(false);\n };\n }, [\n supported,\n paused,\n camera.status,\n camera.videoRef,\n intervalMs,\n repeatDelayMs,\n emitScan,\n emitError,\n ]);\n\n return {\n videoRef: camera.videoRef,\n status: camera.status,\n error: camera.error,\n supported,\n formats,\n scanning,\n result,\n torch,\n retry: camera.retry,\n };\n}\n"],"mappings":"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"}
@@ -11,7 +11,7 @@ function f(f = {}) {
11
11
  let { formats: p = n, onScan: m, intervalMs: h = 200, repeatDelayMs: g = 2500, paused: _ = !1, detector: v, constraints: y, onError: b } = f, [x, S] = u(() => v !== void 0 || a()), C = t({
12
12
  constraints: y,
13
13
  enabled: x
14
- }), w = s(C.stream), [T, E] = u(v === void 0 ? [] : p), [D, O] = u(!1), [k, A] = u(null), j = l(null), M = l(null), N = l(0), P = e((e) => m?.(e)), F = e((e) => b?.(e));
14
+ }), w = s(C.stream), [T, E] = u(v === void 0 ? [] : p), [D, O] = u(!1), [k, A] = u(null), j = l(null), M = l(null), N = l(0), P = e((e) => m?.(e)), F = e((e) => b?.(e)), I = p.join(",");
15
15
  return c(() => {
16
16
  if (v) {
17
17
  j.current = v, S(!0), E(p);
@@ -29,7 +29,7 @@ function f(f = {}) {
29
29
  }), () => {
30
30
  e = !0;
31
31
  };
32
- }, [v, p.join(",")]), c(() => {
32
+ }, [v, I]), c(() => {
33
33
  if (!x || _ || C.status !== "ready") {
34
34
  O(!1);
35
35
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"use-barcode-scanner.js","names":[],"sources":["../../src/capture/use-barcode-scanner.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the scan loop, the repeat suppression window and the\n * torch control share the same track and the same interval handle; splitting them\n * would leave a timer running against a track another hook stopped.\n */\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\nimport {\n useCameraStream,\n type CameraStreamError,\n type CameraStreamStatus,\n} from \"@/vision/use-camera-stream\";\n\nimport {\n createBarcodeDetector,\n getSupportedBarcodeFormats,\n isBarcodeDetectionSupported,\n normalizeBarcode,\n DEFAULT_BARCODE_FORMATS,\n type BarcodeDetectorLike,\n type BarcodeFormat,\n type BarcodeScanResult,\n} from \"./barcode\";\nimport { useTorch, type UseTorchResult } from \"./use-torch\";\n\n/** Options for {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerOptions {\n /** Symbologies to look for. Defaults to {@link DEFAULT_BARCODE_FORMATS}. */\n formats?: readonly BarcodeFormat[];\n /** Called for every accepted read — that is, after repeat suppression. */\n onScan?: (result: BarcodeScanResult) => void;\n /**\n * How often a frame is examined, in ms. Default 200.\n *\n * Not `requestAnimationFrame`: decoding is 10–40 ms of main-thread work on a\n * phone, so running it per frame competes with the preview it is reading from and\n * makes the video stutter. Five looks per second is faster than a human can aim.\n */\n intervalMs?: number;\n /**\n * Ignore the **same** value again for this long, in ms. Default 2500.\n *\n * A symbol stays in frame for as long as the user holds the camera there, so a\n * scanner without this fires the same code five times a second — which, wired to\n * \"add item to cart\", is a bug the user pays for. A *different* value is never\n * suppressed.\n */\n repeatDelayMs?: number;\n /** Stop looking without releasing the camera — a confirmation sheet is open. */\n paused?: boolean;\n /**\n * A decoder to use instead of the native one.\n *\n * The way to support Safari and Firefox: hand in a polyfill and everything else\n * here works unchanged. See {@link isBarcodeDetectionSupported} for why the SDK\n * does not bundle one.\n */\n detector?: BarcodeDetectorLike;\n /** Camera constraints, forwarded to `useCameraStream`. Defaults to the rear camera. */\n constraints?: MediaStreamConstraints;\n /**\n * A frame the engine refused to decode.\n *\n * Not \"nothing found\" — that resolves to an empty list and is the normal case.\n * This is the engine itself failing, which the loop survives because it is\n * usually transient (a frame arriving between two resolutions).\n */\n onError?: (error: unknown) => void;\n}\n\n/** Value returned by {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerResult {\n /** Attach to a `<video ref={…} muted playsInline />`. */\n videoRef: RefObject<HTMLVideoElement | null>;\n /** Camera lifecycle. `\"ready\"` means the preview is running. */\n status: CameraStreamStatus;\n /** Classified camera error, or `null`. */\n error: CameraStreamError | null;\n /** `false` when there is no decoder — no native API and none injected. */\n supported: boolean;\n /**\n * Formats actually in use: the requested ones intersected with what the engine\n * reports. Empty while the probe is in flight, or when nothing matched.\n */\n formats: readonly BarcodeFormat[];\n /** Whether the detect loop is running right now. */\n scanning: boolean;\n /** The most recent accepted read, or `null`. */\n result: BarcodeScanResult | null;\n /** The LED torch of this camera, when it has one. */\n torch: UseTorchResult;\n /** Re-open the camera after an error (the user changed the permission). */\n retry: () => void;\n}\n\n/** A video is decodable once it has data for the current frame and a real size. */\nfunction frameIsReady(video: HTMLVideoElement): boolean {\n return video.readyState >= 2 && video.videoWidth > 0 && video.videoHeight > 0;\n}\n\n/**\n * Read barcodes and QR codes from the camera.\n *\n * The camera and its classified errors come from `useCameraStream`, so this hook is\n * only the decoding half: it drives a `BarcodeDetector` over the preview on an\n * interval, suppresses the same value repeating, and exposes the torch.\n *\n * **Mounting this opens the camera.** It inherits that from `useCameraStream`, which\n * acquires on mount — so mount it *after* the user asks to scan (a button that reveals\n * the scanner), never on a page that merely contains one. A permission prompt nobody\n * provoked is the most reliable way to earn a permanent block, after which\n * `getUserMedia` rejects without ever prompting again.\n *\n * `supported` deserves a branch in the UI, not an assertion: `BarcodeDetector` is\n * Chromium-only and missing on Windows/Linux desktop, Firefox and everything on iOS.\n * Inject a `detector` to cover those, or tell the user to type the code.\n *\n * @param options - See {@link UseBarcodeScannerOptions}.\n * @returns The camera plumbing plus the scan state.\n *\n * @example\n * const scanner = useBarcodeScanner({\n * formats: [\"ean_13\"],\n * onScan: ({ rawValue }) => addToCart(rawValue),\n * });\n * return <video ref={scanner.videoRef} muted playsInline />;\n */\nexport function useBarcodeScanner(options: UseBarcodeScannerOptions = {}): UseBarcodeScannerResult {\n const {\n formats: requested = DEFAULT_BARCODE_FORMATS,\n onScan,\n intervalMs = 200,\n repeatDelayMs = 2500,\n paused = false,\n detector: injected,\n constraints,\n onError,\n } = options;\n\n const [supported, setSupported] = useState(\n () => injected !== undefined || isBarcodeDetectionSupported(),\n );\n\n /**\n * No decoder, no camera.\n *\n * Opening the camera only to report \"this browser cannot decode barcodes\" spends a\n * permission prompt on nothing — and a refusal is permanent, so it also spends the\n * *next* feature that needs the camera.\n */\n const camera = useCameraStream({ constraints, enabled: supported });\n const torch = useTorch(camera.stream);\n\n const [formats, setFormats] = useState<readonly BarcodeFormat[]>(\n injected !== undefined ? requested : [],\n );\n const [scanning, setScanning] = useState(false);\n const [result, setResult] = useState<BarcodeScanResult | null>(null);\n\n const detectorRef = useRef<BarcodeDetectorLike | null>(null);\n const lastValue = useRef<string | null>(null);\n const lastAt = useRef(0);\n\n const emitScan = useStableCallback((scan: BarcodeScanResult) => onScan?.(scan));\n const emitError = useStableCallback((error: unknown) => onError?.(error));\n\n const requestedKey = requested.join(\",\");\n\n /**\n * Resolve which formats the engine will take, then build the detector.\n *\n * The intersection is not defensive coding: `new BarcodeDetector({ formats })`\n * throws `NotSupportedError` when any entry is unknown to the platform decoder, and\n * that list differs between two Chromium builds on two operating systems. Asking\n * for the intersection is the only way one call site works everywhere.\n */\n useEffect(() => {\n if (injected) {\n detectorRef.current = injected;\n setSupported(true);\n setFormats(requested);\n return;\n }\n if (!isBarcodeDetectionSupported()) {\n detectorRef.current = null;\n setSupported(false);\n setFormats([]);\n return;\n }\n let cancelled = false;\n void getSupportedBarcodeFormats().then((available) => {\n if (cancelled) return;\n const usable =\n available.length === 0\n ? requested\n : requested.filter((format) => available.includes(format));\n const detector = usable.length > 0 ? createBarcodeDetector(usable) : null;\n detectorRef.current = detector;\n setFormats(detector ? usable : []);\n setSupported(detector !== null);\n });\n return () => {\n cancelled = true;\n };\n // `requestedKey` stands in for the array identity, so a caller passing an\n // inline `formats={[\"ean_13\"]}` does not rebuild the detector every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, requestedKey]);\n\n /**\n * Look at a frame every `intervalMs`, and never overlap two looks.\n *\n * The loop re-arms itself *after* each `detect()` settles rather than running on a\n * fixed `setInterval`: decoding sometimes takes longer than the interval, and an\n * interval would then queue calls faster than the engine drains them until the tab\n * is unusable.\n */\n useEffect(() => {\n if (!supported || paused || camera.status !== \"ready\") {\n setScanning(false);\n return;\n }\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n setScanning(true);\n\n const accept = (scan: BarcodeScanResult): void => {\n const now = Date.now();\n const isRepeat =\n scan.rawValue === lastValue.current && now - lastAt.current < repeatDelayMs;\n if (isRepeat) return;\n lastValue.current = scan.rawValue;\n lastAt.current = now;\n setResult(scan);\n emitScan(scan);\n };\n\n const look = async (): Promise<void> => {\n const video = camera.videoRef.current;\n const detector = detectorRef.current;\n if (!video || !detector || !frameIsReady(video)) return;\n try {\n const found = await detector.detect(video);\n if (stopped) return;\n for (const raw of found) {\n const scan = normalizeBarcode(raw);\n if (scan.rawValue !== \"\") accept(scan);\n }\n } catch (error) {\n if (!stopped) emitError(error);\n }\n };\n\n const tick = (): void => {\n void look().finally(() => {\n if (!stopped) timer = setTimeout(tick, intervalMs);\n });\n };\n tick();\n\n return () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n setScanning(false);\n };\n }, [\n supported,\n paused,\n camera.status,\n camera.videoRef,\n intervalMs,\n repeatDelayMs,\n emitScan,\n emitError,\n ]);\n\n return {\n videoRef: camera.videoRef,\n status: camera.status,\n error: camera.error,\n supported,\n formats,\n scanning,\n result,\n torch,\n retry: camera.retry,\n };\n}\n"],"mappings":";;;;;;AAiGA,SAAS,EAAa,GAAkC;CACpD,OAAO,EAAM,cAAc,KAAK,EAAM,aAAa,KAAK,EAAM,cAAc;AAChF;AA6BA,SAAgB,EAAkB,IAAoC,CAAC,GAA4B;CAC/F,IAAM,EACF,SAAS,IAAY,GACrB,WACA,gBAAa,KACb,mBAAgB,MAChB,YAAS,IACT,UAAU,GACV,gBACA,eACA,GAEE,CAAC,GAAW,KAAgB,QACxB,MAAa,KAAA,KAAa,EAA4B,CAChE,GASM,IAAS,EAAgB;EAAE;EAAa,SAAS;CAAU,CAAC,GAC5D,IAAQ,EAAS,EAAO,MAAM,GAE9B,CAAC,GAAS,KAAc,EAC1B,MAAa,KAAA,IAAwB,CAAC,IAAb,CAC7B,GACM,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,CAAC,GAAQ,KAAa,EAAmC,IAAI,GAE7D,IAAc,EAAmC,IAAI,GACrD,IAAY,EAAsB,IAAI,GACtC,IAAS,EAAO,CAAC,GAEjB,IAAW,GAAmB,MAA4B,IAAS,CAAI,CAAC,GACxE,IAAY,GAAmB,MAAmB,IAAU,CAAK,CAAC;CAgHxE,OApGA,QAAgB;EACZ,IAAI,GAAU;GAGV,AAFA,EAAY,UAAU,GACtB,EAAa,EAAI,GACjB,EAAW,CAAS;GACpB;EACJ;EACA,IAAI,CAAC,EAA4B,GAAG;GAGhC,AAFA,EAAY,UAAU,MACtB,EAAa,EAAK,GAClB,EAAW,CAAC,CAAC;GACb;EACJ;EACA,IAAI,IAAY;EAYhB,OAXA,EAAgC,CAAC,CAAC,MAAM,MAAc;GAClD,IAAI,GAAW;GACf,IAAM,IACF,EAAU,WAAW,IACf,IACA,EAAU,QAAQ,MAAW,EAAU,SAAS,CAAM,CAAC,GAC3D,IAAW,EAAO,SAAS,IAAI,EAAsB,CAAM,IAAI;GAGrE,AAFA,EAAY,UAAU,GACtB,EAAW,IAAW,IAAS,CAAC,CAAC,GACjC,EAAa,MAAa,IAAI;EAClC,CAAC,SACY;GACT,IAAY;EAChB;CAIJ,GAAG,CAAC,GAzCiB,EAAU,KAAK,GAyCtB,CAAY,CAAC,GAU3B,QAAgB;EACZ,IAAI,CAAC,KAAa,KAAU,EAAO,WAAW,SAAS;GACnD,EAAY,EAAK;GACjB;EACJ;EACA,IAAI,IAAU,IACV;EACJ,EAAY,EAAI;EAEhB,IAAM,KAAU,MAAkC;GAC9C,IAAM,IAAM,KAAK,IAAI;GAEjB,EAAK,aAAa,EAAU,WAAW,IAAM,EAAO,UAAU,MAElE,EAAU,UAAU,EAAK,UACzB,EAAO,UAAU,GACjB,EAAU,CAAI,GACd,EAAS,CAAI;EACjB,GAEM,IAAO,YAA2B;GACpC,IAAM,IAAQ,EAAO,SAAS,SACxB,IAAW,EAAY;GACzB,OAAC,KAAS,CAAC,KAAY,CAAC,EAAa,CAAK,IAC9C,IAAI;IACA,IAAM,IAAQ,MAAM,EAAS,OAAO,CAAK;IACzC,IAAI,GAAS;IACb,KAAK,IAAM,KAAO,GAAO;KACrB,IAAM,IAAO,EAAiB,CAAG;KACjC,AAAI,EAAK,aAAa,MAAI,EAAO,CAAI;IACzC;GACJ,SAAS,GAAO;IACZ,AAAK,KAAS,EAAU,CAAK;GACjC;EACJ,GAEM,UAAmB;GACrB,EAAU,CAAC,CAAC,cAAc;IACtB,AAAK,MAAS,IAAQ,WAAW,GAAM,CAAU;GACrD,CAAC;EACL;EAGA,OAFA,EAAK,SAEQ;GAGT,AAFA,IAAU,IACN,MAAU,KAAA,KAAW,aAAa,CAAK,GAC3C,EAAY,EAAK;EACrB;CACJ,GAAG;EACC;EACA;EACA,EAAO;EACP,EAAO;EACP;EACA;EACA;EACA;CACJ,CAAC,GAEM;EACH,UAAU,EAAO;EACjB,QAAQ,EAAO;EACf,OAAO,EAAO;EACd;EACA;EACA;EACA;EACA;EACA,OAAO,EAAO;CAClB;AACJ"}
1
+ {"version":3,"file":"use-barcode-scanner.js","names":[],"sources":["../../src/capture/use-barcode-scanner.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — the scan loop, the repeat suppression window and the\n * torch control share the same track and the same interval handle; splitting them\n * would leave a timer running against a track another hook stopped.\n */\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\n\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\nimport {\n useCameraStream,\n type CameraStreamError,\n type CameraStreamStatus,\n} from \"@/vision/use-camera-stream\";\n\nimport {\n createBarcodeDetector,\n getSupportedBarcodeFormats,\n isBarcodeDetectionSupported,\n normalizeBarcode,\n DEFAULT_BARCODE_FORMATS,\n type BarcodeDetectorLike,\n type BarcodeFormat,\n type BarcodeScanResult,\n} from \"./barcode\";\nimport { useTorch, type UseTorchResult } from \"./use-torch\";\n\n/** Options for {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerOptions {\n /** Symbologies to look for. Defaults to {@link DEFAULT_BARCODE_FORMATS}. */\n formats?: readonly BarcodeFormat[];\n /** Called for every accepted read — that is, after repeat suppression. */\n onScan?: (result: BarcodeScanResult) => void;\n /**\n * How often a frame is examined, in ms. Default 200.\n *\n * Not `requestAnimationFrame`: decoding is 10–40 ms of main-thread work on a\n * phone, so running it per frame competes with the preview it is reading from and\n * makes the video stutter. Five looks per second is faster than a human can aim.\n */\n intervalMs?: number;\n /**\n * Ignore the **same** value again for this long, in ms. Default 2500.\n *\n * A symbol stays in frame for as long as the user holds the camera there, so a\n * scanner without this fires the same code five times a second — which, wired to\n * \"add item to cart\", is a bug the user pays for. A *different* value is never\n * suppressed.\n */\n repeatDelayMs?: number;\n /** Stop looking without releasing the camera — a confirmation sheet is open. */\n paused?: boolean;\n /**\n * A decoder to use instead of the native one.\n *\n * The way to support Safari and Firefox: hand in a polyfill and everything else\n * here works unchanged. See {@link isBarcodeDetectionSupported} for why the SDK\n * does not bundle one.\n */\n detector?: BarcodeDetectorLike;\n /** Camera constraints, forwarded to `useCameraStream`. Defaults to the rear camera. */\n constraints?: MediaStreamConstraints;\n /**\n * A frame the engine refused to decode.\n *\n * Not \"nothing found\" — that resolves to an empty list and is the normal case.\n * This is the engine itself failing, which the loop survives because it is\n * usually transient (a frame arriving between two resolutions).\n */\n onError?: (error: unknown) => void;\n}\n\n/** Value returned by {@link useBarcodeScanner}. */\nexport interface UseBarcodeScannerResult {\n /** Attach to a `<video ref={…} muted playsInline />`. */\n videoRef: RefObject<HTMLVideoElement | null>;\n /** Camera lifecycle. `\"ready\"` means the preview is running. */\n status: CameraStreamStatus;\n /** Classified camera error, or `null`. */\n error: CameraStreamError | null;\n /** `false` when there is no decoder — no native API and none injected. */\n supported: boolean;\n /**\n * Formats actually in use: the requested ones intersected with what the engine\n * reports. Empty while the probe is in flight, or when nothing matched.\n */\n formats: readonly BarcodeFormat[];\n /** Whether the detect loop is running right now. */\n scanning: boolean;\n /** The most recent accepted read, or `null`. */\n result: BarcodeScanResult | null;\n /** The LED torch of this camera, when it has one. */\n torch: UseTorchResult;\n /** Re-open the camera after an error (the user changed the permission). */\n retry: () => void;\n}\n\n/** A video is decodable once it has data for the current frame and a real size. */\nfunction frameIsReady(video: HTMLVideoElement): boolean {\n return video.readyState >= 2 && video.videoWidth > 0 && video.videoHeight > 0;\n}\n\n/**\n * Read barcodes and QR codes from the camera.\n *\n * The camera and its classified errors come from `useCameraStream`, so this hook is\n * only the decoding half: it drives a `BarcodeDetector` over the preview on an\n * interval, suppresses the same value repeating, and exposes the torch.\n *\n * **Mounting this opens the camera.** It inherits that from `useCameraStream`, which\n * acquires on mount — so mount it *after* the user asks to scan (a button that reveals\n * the scanner), never on a page that merely contains one. A permission prompt nobody\n * provoked is the most reliable way to earn a permanent block, after which\n * `getUserMedia` rejects without ever prompting again.\n *\n * `supported` deserves a branch in the UI, not an assertion: `BarcodeDetector` is\n * Chromium-only and missing on Windows/Linux desktop, Firefox and everything on iOS.\n * Inject a `detector` to cover those, or tell the user to type the code.\n *\n * @param options - See {@link UseBarcodeScannerOptions}.\n * @returns The camera plumbing plus the scan state.\n *\n * @example\n * const scanner = useBarcodeScanner({\n * formats: [\"ean_13\"],\n * onScan: ({ rawValue }) => addToCart(rawValue),\n * });\n * return <video ref={scanner.videoRef} muted playsInline />;\n */\nexport function useBarcodeScanner(options: UseBarcodeScannerOptions = {}): UseBarcodeScannerResult {\n const {\n formats: requested = DEFAULT_BARCODE_FORMATS,\n onScan,\n intervalMs = 200,\n repeatDelayMs = 2500,\n paused = false,\n detector: injected,\n constraints,\n onError,\n } = options;\n\n const [supported, setSupported] = useState(\n () => injected !== undefined || isBarcodeDetectionSupported(),\n );\n\n /**\n * No decoder, no camera.\n *\n * Opening the camera only to report \"this browser cannot decode barcodes\" spends a\n * permission prompt on nothing — and a refusal is permanent, so it also spends the\n * *next* feature that needs the camera.\n */\n const camera = useCameraStream({ constraints, enabled: supported });\n const torch = useTorch(camera.stream);\n\n const [formats, setFormats] = useState<readonly BarcodeFormat[]>(\n injected !== undefined ? requested : [],\n );\n const [scanning, setScanning] = useState(false);\n const [result, setResult] = useState<BarcodeScanResult | null>(null);\n\n const detectorRef = useRef<BarcodeDetectorLike | null>(null);\n const lastValue = useRef<string | null>(null);\n const lastAt = useRef(0);\n\n const emitScan = useStableCallback((scan: BarcodeScanResult) => onScan?.(scan));\n const emitError = useStableCallback((error: unknown) => onError?.(error));\n\n const requestedKey = requested.join(\",\");\n\n /**\n * Resolve which formats the engine will take, then build the detector.\n *\n * The intersection is not defensive coding: `new BarcodeDetector({ formats })`\n * throws `NotSupportedError` when any entry is unknown to the platform decoder, and\n * that list differs between two Chromium builds on two operating systems. Asking\n * for the intersection is the only way one call site works everywhere.\n */\n useEffect(() => {\n if (injected) {\n detectorRef.current = injected;\n setSupported(true);\n setFormats(requested);\n return;\n }\n if (!isBarcodeDetectionSupported()) {\n detectorRef.current = null;\n setSupported(false);\n setFormats([]);\n return;\n }\n let cancelled = false;\n void getSupportedBarcodeFormats().then((available) => {\n if (cancelled) return;\n const usable =\n available.length === 0\n ? requested\n : requested.filter((format) => available.includes(format));\n const detector = usable.length > 0 ? createBarcodeDetector(usable) : null;\n detectorRef.current = detector;\n setFormats(detector ? usable : []);\n setSupported(detector !== null);\n });\n return () => {\n cancelled = true;\n };\n // `requestedKey` stands in for the array identity, so a caller passing an\n // inline `formats={[\"ean_13\"]}` does not rebuild the detector every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, requestedKey]);\n\n /**\n * Look at a frame every `intervalMs`, and never overlap two looks.\n *\n * The loop re-arms itself *after* each `detect()` settles rather than running on a\n * fixed `setInterval`: decoding sometimes takes longer than the interval, and an\n * interval would then queue calls faster than the engine drains them until the tab\n * is unusable.\n */\n useEffect(() => {\n if (!supported || paused || camera.status !== \"ready\") {\n setScanning(false);\n return;\n }\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n setScanning(true);\n\n const accept = (scan: BarcodeScanResult): void => {\n const now = Date.now();\n const isRepeat =\n scan.rawValue === lastValue.current && now - lastAt.current < repeatDelayMs;\n if (isRepeat) return;\n lastValue.current = scan.rawValue;\n lastAt.current = now;\n setResult(scan);\n emitScan(scan);\n };\n\n const look = async (): Promise<void> => {\n const video = camera.videoRef.current;\n const detector = detectorRef.current;\n if (!video || !detector || !frameIsReady(video)) return;\n try {\n const found = await detector.detect(video);\n if (stopped) return;\n for (const raw of found) {\n const scan = normalizeBarcode(raw);\n if (scan.rawValue !== \"\") accept(scan);\n }\n } catch (error) {\n if (!stopped) emitError(error);\n }\n };\n\n const tick = (): void => {\n void look().finally(() => {\n if (!stopped) timer = setTimeout(tick, intervalMs);\n });\n };\n tick();\n\n return () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n setScanning(false);\n };\n }, [\n supported,\n paused,\n camera.status,\n camera.videoRef,\n intervalMs,\n repeatDelayMs,\n emitScan,\n emitError,\n ]);\n\n return {\n videoRef: camera.videoRef,\n status: camera.status,\n error: camera.error,\n supported,\n formats,\n scanning,\n result,\n torch,\n retry: camera.retry,\n };\n}\n"],"mappings":";;;;;;AAiGA,SAAS,EAAa,GAAkC;CACpD,OAAO,EAAM,cAAc,KAAK,EAAM,aAAa,KAAK,EAAM,cAAc;AAChF;AA6BA,SAAgB,EAAkB,IAAoC,CAAC,GAA4B;CAC/F,IAAM,EACF,SAAS,IAAY,GACrB,WACA,gBAAa,KACb,mBAAgB,MAChB,YAAS,IACT,UAAU,GACV,gBACA,eACA,GAEE,CAAC,GAAW,KAAgB,QACxB,MAAa,KAAA,KAAa,EAA4B,CAChE,GASM,IAAS,EAAgB;EAAE;EAAa,SAAS;CAAU,CAAC,GAC5D,IAAQ,EAAS,EAAO,MAAM,GAE9B,CAAC,GAAS,KAAc,EAC1B,MAAa,KAAA,IAAwB,CAAC,IAAb,CAC7B,GACM,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,CAAC,GAAQ,KAAa,EAAmC,IAAI,GAE7D,IAAc,EAAmC,IAAI,GACrD,IAAY,EAAsB,IAAI,GACtC,IAAS,EAAO,CAAC,GAEjB,IAAW,GAAmB,MAA4B,IAAS,CAAI,CAAC,GACxE,IAAY,GAAmB,MAAmB,IAAU,CAAK,CAAC,GAElE,IAAe,EAAU,KAAK,GAAG;CA8GvC,OApGA,QAAgB;EACZ,IAAI,GAAU;GAGV,AAFA,EAAY,UAAU,GACtB,EAAa,EAAI,GACjB,EAAW,CAAS;GACpB;EACJ;EACA,IAAI,CAAC,EAA4B,GAAG;GAGhC,AAFA,EAAY,UAAU,MACtB,EAAa,EAAK,GAClB,EAAW,CAAC,CAAC;GACb;EACJ;EACA,IAAI,IAAY;EAYhB,OAXA,EAAgC,CAAC,CAAC,MAAM,MAAc;GAClD,IAAI,GAAW;GACf,IAAM,IACF,EAAU,WAAW,IACf,IACA,EAAU,QAAQ,MAAW,EAAU,SAAS,CAAM,CAAC,GAC3D,IAAW,EAAO,SAAS,IAAI,EAAsB,CAAM,IAAI;GAGrE,AAFA,EAAY,UAAU,GACtB,EAAW,IAAW,IAAS,CAAC,CAAC,GACjC,EAAa,MAAa,IAAI;EAClC,CAAC,SACY;GACT,IAAY;EAChB;CAIJ,GAAG,CAAC,GAAU,CAAY,CAAC,GAU3B,QAAgB;EACZ,IAAI,CAAC,KAAa,KAAU,EAAO,WAAW,SAAS;GACnD,EAAY,EAAK;GACjB;EACJ;EACA,IAAI,IAAU,IACV;EACJ,EAAY,EAAI;EAEhB,IAAM,KAAU,MAAkC;GAC9C,IAAM,IAAM,KAAK,IAAI;GAEjB,EAAK,aAAa,EAAU,WAAW,IAAM,EAAO,UAAU,MAElE,EAAU,UAAU,EAAK,UACzB,EAAO,UAAU,GACjB,EAAU,CAAI,GACd,EAAS,CAAI;EACjB,GAEM,IAAO,YAA2B;GACpC,IAAM,IAAQ,EAAO,SAAS,SACxB,IAAW,EAAY;GACzB,OAAC,KAAS,CAAC,KAAY,CAAC,EAAa,CAAK,IAC9C,IAAI;IACA,IAAM,IAAQ,MAAM,EAAS,OAAO,CAAK;IACzC,IAAI,GAAS;IACb,KAAK,IAAM,KAAO,GAAO;KACrB,IAAM,IAAO,EAAiB,CAAG;KACjC,AAAI,EAAK,aAAa,MAAI,EAAO,CAAI;IACzC;GACJ,SAAS,GAAO;IACZ,AAAK,KAAS,EAAU,CAAK;GACjC;EACJ,GAEM,UAAmB;GACrB,EAAU,CAAC,CAAC,cAAc;IACtB,AAAK,MAAS,IAAQ,WAAW,GAAM,CAAU;GACrD,CAAC;EACL;EAGA,OAFA,EAAK,SAEQ;GAGT,AAFA,IAAU,IACN,MAAU,KAAA,KAAW,aAAa,CAAK,GAC3C,EAAY,EAAK;EACrB;CACJ,GAAG;EACC;EACA;EACA,EAAO;EACP,EAAO;EACP;EACA;EACA;EACA;CACJ,CAAC,GAEM;EACH,UAAU,EAAO;EACjB,QAAQ,EAAO;EACf,OAAO,EAAO;EACd;EACA;EACA;EACA;EACA;EACA,OAAO,EAAO;CAClB;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-screen-capture.cjs","names":[],"sources":["../../src/capture/use-screen-capture.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — getDisplayMedia's cancellation is indistinguishable\n * from a policy block, so the hook holds the classification, the track's own `ended`\n * event (the user can stop sharing from the browser's bar) and the stop path in one\n * place — all three end the same session.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"@/audio/media-access\";\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\n/** Lifecycle of a screen share. */\nexport type ScreenCaptureStatus = \"idle\" | \"requesting\" | \"sharing\" | \"error\";\n\n/** Which surface to put first in the picker. */\nexport type DisplaySurfaceHint = \"monitor\" | \"window\" | \"browser\";\n\n/**\n * Hints `getDisplayMedia` takes that TypeScript's `DisplayMediaStreamOptions` does not\n * list yet.\n */\ntype DisplayMediaOptionsWithHints = DisplayMediaStreamOptions & {\n preferCurrentTab?: boolean;\n selfBrowserSurface?: \"include\" | \"exclude\";\n surfaceSwitching?: \"include\" | \"exclude\";\n systemAudio?: \"include\" | \"exclude\";\n};\n\n/** `displaySurface` is a constrainable property of a display track, but not in the DOM lib. */\ntype DisplayVideoConstraints = MediaTrackConstraints & { displaySurface?: DisplaySurfaceHint };\n\n/** Options for {@link useScreenCapture}. */\nexport interface UseScreenCaptureOptions {\n /**\n * Capture the tab's audio too. Default `false`.\n *\n * Chromium only offers this for a **tab** — sharing a window or a whole screen\n * yields no audio track no matter what you ask for, and Safari has no display\n * audio at all. Ask for it and check what you got.\n */\n audio?: boolean;\n /**\n * Which surface the picker should offer first — `\"browser\"` is a tab.\n *\n * A hint, never a guarantee: the user can always pick something else, and Firefox\n * ignores it. Read `surface` afterwards to learn what actually happened.\n */\n displaySurface?: DisplaySurfaceHint;\n /**\n * Put *this* tab at the top of the picker. Default `false`.\n *\n * The right setting for \"record what you are seeing right now\" in a support flow.\n * Chromium only.\n */\n preferCurrentTab?: boolean;\n /** Offer this tab in the list at all. `\"exclude\"` prevents the hall-of-mirrors capture. */\n selfBrowserSurface?: \"include\" | \"exclude\";\n /** Let the user switch to a different surface mid-share, without a new prompt. */\n surfaceSwitching?: \"include\" | \"exclude\";\n /** Include the system audio when a whole screen is shared. Chromium, Windows only. */\n systemAudio?: \"include\" | \"exclude\";\n /** Escape hatch: full options, replacing everything above. */\n options?: DisplayMediaStreamOptions;\n /**\n * The user stopped the share from the browser's own bar.\n *\n * The single most important callback here — see the note on\n * {@link useScreenCapture}.\n */\n onEnded?: () => void;\n /**\n * The user dismissed the picker. **Not an error.**\n *\n * Receives the rejection so an app that needs to tell a dismissal from an OS-level\n * block (macOS screen-recording permission) can look at the message. Most should\n * simply return the UI to its previous state.\n */\n onCancelled?: (reason: unknown) => void;\n}\n\n/** Value returned by {@link useScreenCapture}. */\nexport interface UseScreenCaptureResult {\n status: ScreenCaptureStatus;\n /** The live stream, or `null`. Feed it to {@link useVideoRecorder} or a `<video>`. */\n stream: MediaStream | null;\n /** Classified error, or `null`. A cancelled picker leaves this `null`. */\n error: MediaAccessError | null;\n /** What the user actually picked, when the browser reports it. */\n surface: string | null;\n /** Whether the stream carries an audio track — ask, do not assume. */\n hasAudio: boolean;\n /** `false` when `getDisplayMedia` is missing (every browser on iOS, insecure pages). */\n supported: boolean;\n /** Open the picker. Must be called from a user gesture. */\n start: () => void;\n /** Stop sharing from the app side. Fires nothing — you asked for it. */\n stop: () => void;\n}\n\n/** Whether `getDisplayMedia` is reachable at all. */\nexport function isScreenCaptureSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n navigator.mediaDevices !== undefined &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\"\n );\n}\n\n/**\n * A rejection from `getDisplayMedia` that means \"the user said no thanks\".\n *\n * There is no distinct exception for a dismissed picker: closing it produces the same\n * `NotAllowedError` as a policy block, and some builds report `AbortError` instead. The\n * useful default is therefore to treat both as a cancellation, because a\n * display-capture prompt is **always** user-initiated — nothing can open it behind\n * their back — so the overwhelmingly likely cause is that they changed their mind, and\n * a red error toast for that punishes them for it. The rejection is handed to\n * `onCancelled` so the rarer causes stay diagnosable.\n */\nfunction isCancellation(err: unknown): boolean {\n return (\n err instanceof DOMException && (err.name === \"NotAllowedError\" || err.name === \"AbortError\")\n );\n}\n\n/**\n * Capture a screen, a window or a tab with `getDisplayMedia`.\n *\n * Three states decide whether this feels right, and two of them are easy to miss:\n *\n * - **The user dismissed the picker.** A rejection, but not a failure. It leaves\n * `error` at `null` and the status back at `\"idle\"`, and calls `onCancelled`.\n * - **The user stopped the share from the browser's own bar.** Nothing in your UI was\n * clicked and no promise rejects — the *only* signal is the video track's `ended`\n * event, so the hook listens for it and clears the stream. Without that listener, an\n * app shows \"gravando\" over a stream that is already dead.\n * - **The share is live.** `surface` says what was picked and `hasAudio` says whether\n * audio actually came along, which is not what you asked for but what you got.\n *\n * The stream is owned here: `stop()` and unmount both release every track. A recorder\n * built on it (`useVideoRecorder`) deliberately does not, so stopping a recording\n * leaves the share running for the next take.\n *\n * @param options - See {@link UseScreenCaptureOptions}.\n * @returns The stream, its status, a classified error and `start`/`stop`.\n *\n * @example\n * const screen = useScreenCapture({ preferCurrentTab: true, onEnded: () => save() });\n * const rec = useVideoRecorder(screen.stream);\n * <button onClick={screen.start}>Compartilhar tela</button>\n */\nexport function useScreenCapture(options: UseScreenCaptureOptions = {}): UseScreenCaptureResult {\n const {\n audio = false,\n displaySurface,\n preferCurrentTab,\n selfBrowserSurface,\n surfaceSwitching,\n systemAudio,\n options: raw,\n onEnded,\n onCancelled,\n } = options;\n\n const [status, setStatus] = useState<ScreenCaptureStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const [surface, setSurface] = useState<string | null>(null);\n const [hasAudio, setHasAudio] = useState(false);\n const [supported] = useState(isScreenCaptureSupported);\n\n const streamRef = useRef<MediaStream | null>(null);\n const detach = useRef<(() => void) | null>(null);\n const generation = useRef(0);\n\n const emitEnded = useStableCallback(() => onEnded?.());\n const emitCancelled = useStableCallback((reason: unknown) => onCancelled?.(reason));\n\n const release = useCallback((): void => {\n detach.current?.();\n detach.current = null;\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const reset = useCallback((): void => {\n setStream(null);\n setSurface(null);\n setHasAudio(false);\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n reset();\n setStatus(\"idle\");\n setError(null);\n }, [release, reset]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isScreenCaptureSupported()) {\n setError(missingCaptureApiError(\"screen\"));\n setStatus(\"error\");\n return;\n }\n\n const video: DisplayVideoConstraints = displaySurface ? { displaySurface } : {};\n const request: DisplayMediaOptionsWithHints = raw ?? {\n video,\n audio,\n ...(preferCurrentTab !== undefined ? { preferCurrentTab } : {}),\n ...(selfBrowserSurface !== undefined ? { selfBrowserSurface } : {}),\n ...(surfaceSwitching !== undefined ? { surfaceSwitching } : {}),\n ...(systemAudio !== undefined ? { systemAudio } : {}),\n };\n\n void navigator.mediaDevices\n .getDisplayMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n const track = next.getVideoTracks()[0];\n const ended = (): void => {\n if (run !== generation.current) return;\n release();\n reset();\n setStatus(\"idle\");\n emitEnded();\n };\n track?.addEventListener(\"ended\", ended);\n detach.current = () => track?.removeEventListener(\"ended\", ended);\n\n setStream(next);\n setSurface(track?.getSettings().displaySurface ?? null);\n setHasAudio(next.getAudioTracks().length > 0);\n setStatus(\"sharing\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n if (isCancellation(err)) {\n setStatus(\"idle\");\n emitCancelled(err);\n return;\n }\n setError(classifyMediaError(err, \"screen\"));\n setStatus(\"error\");\n });\n }, [\n audio,\n displaySurface,\n preferCurrentTab,\n selfBrowserSurface,\n surfaceSwitching,\n systemAudio,\n raw,\n release,\n reset,\n emitEnded,\n emitCancelled,\n ]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, surface, hasAudio, supported, start, stop };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-screen-capture.cjs","names":[],"sources":["../../src/capture/use-screen-capture.ts"],"sourcesContent":["/**\n * @tempest-limits hook-lines — getDisplayMedia's cancellation is indistinguishable\n * from a policy block, so the hook holds the classification, the track's own `ended`\n * event (the user can stop sharing from the browser's bar) and the stop path in one\n * place — all three end the same session.\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n classifyMediaError,\n missingCaptureApiError,\n type MediaAccessError,\n} from \"@/audio/media-access\";\nimport { useStableCallback } from \"@/hooks/use-stable-callback\";\n\n/** Lifecycle of a screen share. */\nexport type ScreenCaptureStatus = \"idle\" | \"requesting\" | \"sharing\" | \"error\";\n\n/** Which surface to put first in the picker. */\nexport type DisplaySurfaceHint = \"monitor\" | \"window\" | \"browser\";\n\n/**\n * Hints `getDisplayMedia` takes that TypeScript's `DisplayMediaStreamOptions` does not\n * list yet.\n */\ntype DisplayMediaOptionsWithHints = DisplayMediaStreamOptions & {\n preferCurrentTab?: boolean;\n selfBrowserSurface?: \"include\" | \"exclude\";\n surfaceSwitching?: \"include\" | \"exclude\";\n systemAudio?: \"include\" | \"exclude\";\n};\n\n/** `displaySurface` is a constrainable property of a display track, but not in the DOM lib. */\ntype DisplayVideoConstraints = MediaTrackConstraints & { displaySurface?: DisplaySurfaceHint };\n\n/** Options for {@link useScreenCapture}. */\nexport interface UseScreenCaptureOptions {\n /**\n * Capture the tab's audio too. Default `false`.\n *\n * Chromium only offers this for a **tab** — sharing a window or a whole screen\n * yields no audio track no matter what you ask for, and Safari has no display\n * audio at all. Ask for it and check what you got.\n */\n audio?: boolean;\n /**\n * Which surface the picker should offer first — `\"browser\"` is a tab.\n *\n * A hint, never a guarantee: the user can always pick something else, and Firefox\n * ignores it. Read `surface` afterwards to learn what actually happened.\n */\n displaySurface?: DisplaySurfaceHint;\n /**\n * Put *this* tab at the top of the picker. Default `false`.\n *\n * The right setting for \"record what you are seeing right now\" in a support flow.\n * Chromium only.\n */\n preferCurrentTab?: boolean;\n /** Offer this tab in the list at all. `\"exclude\"` prevents the hall-of-mirrors capture. */\n selfBrowserSurface?: \"include\" | \"exclude\";\n /** Let the user switch to a different surface mid-share, without a new prompt. */\n surfaceSwitching?: \"include\" | \"exclude\";\n /** Include the system audio when a whole screen is shared. Chromium, Windows only. */\n systemAudio?: \"include\" | \"exclude\";\n /** Escape hatch: full options, replacing everything above. */\n options?: DisplayMediaStreamOptions;\n /**\n * The user stopped the share from the browser's own bar.\n *\n * The single most important callback here — see the note on\n * {@link useScreenCapture}.\n */\n onEnded?: () => void;\n /**\n * The user dismissed the picker. **Not an error.**\n *\n * Receives the rejection so an app that needs to tell a dismissal from an OS-level\n * block (macOS screen-recording permission) can look at the message. Most should\n * simply return the UI to its previous state.\n */\n onCancelled?: (reason: unknown) => void;\n}\n\n/** Value returned by {@link useScreenCapture}. */\nexport interface UseScreenCaptureResult {\n status: ScreenCaptureStatus;\n /** The live stream, or `null`. Feed it to {@link useVideoRecorder} or a `<video>`. */\n stream: MediaStream | null;\n /** Classified error, or `null`. A cancelled picker leaves this `null`. */\n error: MediaAccessError | null;\n /** What the user actually picked, when the browser reports it. */\n surface: string | null;\n /** Whether the stream carries an audio track — ask, do not assume. */\n hasAudio: boolean;\n /** `false` when `getDisplayMedia` is missing (every browser on iOS, insecure pages). */\n supported: boolean;\n /** Open the picker. Must be called from a user gesture. */\n start: () => void;\n /** Stop sharing from the app side. Fires nothing — you asked for it. */\n stop: () => void;\n}\n\n/** Whether `getDisplayMedia` is reachable at all. */\nexport function isScreenCaptureSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n navigator.mediaDevices !== undefined &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\"\n );\n}\n\n/**\n * A rejection from `getDisplayMedia` that means \"the user said no thanks\".\n *\n * There is no distinct exception for a dismissed picker: closing it produces the same\n * `NotAllowedError` as a policy block, and some builds report `AbortError` instead. The\n * useful default is therefore to treat both as a cancellation, because a\n * display-capture prompt is **always** user-initiated — nothing can open it behind\n * their back — so the overwhelmingly likely cause is that they changed their mind, and\n * a red error toast for that punishes them for it. The rejection is handed to\n * `onCancelled` so the rarer causes stay diagnosable.\n */\nfunction isCancellation(err: unknown): boolean {\n return (\n err instanceof DOMException && (err.name === \"NotAllowedError\" || err.name === \"AbortError\")\n );\n}\n\n/**\n * Capture a screen, a window or a tab with `getDisplayMedia`.\n *\n * Three states decide whether this feels right, and two of them are easy to miss:\n *\n * - **The user dismissed the picker.** A rejection, but not a failure. It leaves\n * `error` at `null` and the status back at `\"idle\"`, and calls `onCancelled`.\n * - **The user stopped the share from the browser's own bar.** Nothing in your UI was\n * clicked and no promise rejects — the *only* signal is the video track's `ended`\n * event, so the hook listens for it and clears the stream. Without that listener, an\n * app shows \"gravando\" over a stream that is already dead.\n * - **The share is live.** `surface` says what was picked and `hasAudio` says whether\n * audio actually came along, which is not what you asked for but what you got.\n *\n * The stream is owned here: `stop()` and unmount both release every track. A recorder\n * built on it (`useVideoRecorder`) deliberately does not, so stopping a recording\n * leaves the share running for the next take.\n *\n * @param options - See {@link UseScreenCaptureOptions}.\n * @returns The stream, its status, a classified error and `start`/`stop`.\n *\n * @example\n * const screen = useScreenCapture({ preferCurrentTab: true, onEnded: () => save() });\n * const rec = useVideoRecorder(screen.stream);\n * <button onClick={screen.start}>Compartilhar tela</button>\n */\nexport function useScreenCapture(options: UseScreenCaptureOptions = {}): UseScreenCaptureResult {\n const {\n audio = false,\n displaySurface,\n preferCurrentTab,\n selfBrowserSurface,\n surfaceSwitching,\n systemAudio,\n options: raw,\n onEnded,\n onCancelled,\n } = options;\n\n const [status, setStatus] = useState<ScreenCaptureStatus>(\"idle\");\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<MediaAccessError | null>(null);\n const [surface, setSurface] = useState<string | null>(null);\n const [hasAudio, setHasAudio] = useState(false);\n const [supported] = useState(isScreenCaptureSupported);\n\n const streamRef = useRef<MediaStream | null>(null);\n const detach = useRef<(() => void) | null>(null);\n const generation = useRef(0);\n\n const emitEnded = useStableCallback(() => onEnded?.());\n const emitCancelled = useStableCallback((reason: unknown) => onCancelled?.(reason));\n\n const release = useCallback((): void => {\n detach.current?.();\n detach.current = null;\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }, []);\n\n const reset = useCallback((): void => {\n setStream(null);\n setSurface(null);\n setHasAudio(false);\n }, []);\n\n const stop = useCallback((): void => {\n generation.current += 1;\n release();\n reset();\n setStatus(\"idle\");\n setError(null);\n }, [release, reset]);\n\n const start = useCallback((): void => {\n if (streamRef.current) return;\n const run = (generation.current += 1);\n setStatus(\"requesting\");\n setError(null);\n\n if (!isScreenCaptureSupported()) {\n setError(missingCaptureApiError(\"screen\"));\n setStatus(\"error\");\n return;\n }\n\n const video: DisplayVideoConstraints = displaySurface ? { displaySurface } : {};\n const request: DisplayMediaOptionsWithHints = raw ?? {\n video,\n audio,\n ...(preferCurrentTab !== undefined ? { preferCurrentTab } : {}),\n ...(selfBrowserSurface !== undefined ? { selfBrowserSurface } : {}),\n ...(surfaceSwitching !== undefined ? { surfaceSwitching } : {}),\n ...(systemAudio !== undefined ? { systemAudio } : {}),\n };\n\n void navigator.mediaDevices\n .getDisplayMedia(request)\n .then((next) => {\n if (run !== generation.current) {\n next.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = next;\n const track = next.getVideoTracks()[0];\n const ended = (): void => {\n if (run !== generation.current) return;\n release();\n reset();\n setStatus(\"idle\");\n emitEnded();\n };\n track?.addEventListener(\"ended\", ended);\n detach.current = () => track?.removeEventListener(\"ended\", ended);\n\n setStream(next);\n setSurface(track?.getSettings().displaySurface ?? null);\n setHasAudio(next.getAudioTracks().length > 0);\n setStatus(\"sharing\");\n })\n .catch((err: unknown) => {\n if (run !== generation.current) return;\n if (isCancellation(err)) {\n setStatus(\"idle\");\n emitCancelled(err);\n return;\n }\n setError(classifyMediaError(err, \"screen\"));\n setStatus(\"error\");\n });\n }, [\n audio,\n displaySurface,\n preferCurrentTab,\n selfBrowserSurface,\n surfaceSwitching,\n systemAudio,\n raw,\n release,\n reset,\n emitEnded,\n emitCancelled,\n ]);\n\n useEffect(() => release, [release]);\n\n return { status, stream, error, surface, hasAudio, supported, start, stop };\n}\n"],"mappings":"kHAwGA,SAAgB,GAAoC,CAChD,OACI,OAAO,UAAc,KACrB,UAAU,eAAiB,IAAA,IAC3B,OAAO,UAAU,aAAa,iBAAoB,UAE1D,CAaA,SAAS,EAAe,EAAuB,CAC3C,OACI,aAAe,eAAiB,EAAI,OAAS,mBAAqB,EAAI,OAAS,aAEvF,CA4BA,SAAgB,EAAiB,EAAmC,CAAC,EAA2B,CAC5F,GAAM,CACF,QAAQ,GACR,iBACA,mBACA,qBACA,mBACA,cACA,QAAS,EACT,UACA,eACA,EAEE,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA8B,MAAM,EAC1D,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA6B,IAAI,EACvD,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAkC,IAAI,EAC1D,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAwB,IAAI,EACpD,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAS,EAAK,EACxC,CAAC,IAAA,EAAa,EAAA,SAAA,CAAS,CAAwB,EAE/C,GAAA,EAAY,EAAA,OAAA,CAA2B,IAAI,EAC3C,GAAA,EAAS,EAAA,OAAA,CAA4B,IAAI,EACzC,GAAA,EAAa,EAAA,OAAA,CAAO,CAAC,EAErB,EAAY,EAAA,sBAAwB,IAAU,CAAC,EAC/C,EAAgB,EAAA,kBAAmB,GAAoB,IAAc,CAAM,CAAC,EAE5E,GAAA,EAAU,EAAA,YAAA,KAAwB,CACpC,EAAO,UAAU,EACjB,EAAO,QAAU,KACjB,EAAU,SAAS,UAAU,CAAC,CAAC,QAAS,GAAU,EAAM,KAAK,CAAC,EAC9D,EAAU,QAAU,IACxB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAU,IAAI,EACd,EAAW,IAAI,EACf,EAAY,EAAK,CACrB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAO,EAAA,YAAA,KAAwB,CACjC,EAAW,SAAW,EACtB,EAAQ,EACR,EAAM,EACN,EAAU,MAAM,EAChB,EAAS,IAAI,CACjB,EAAG,CAAC,EAAS,CAAK,CAAC,EAEb,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,GAAI,EAAU,QAAS,OACvB,IAAM,EAAO,EAAW,SAAW,EAInC,GAHA,EAAU,YAAY,EACtB,EAAS,IAAI,EAET,CAAC,EAAyB,EAAG,CAC7B,EAAS,EAAA,uBAAuB,QAAQ,CAAC,EACzC,EAAU,OAAO,EACjB,MACJ,CAGA,IAAM,EAAwC,GAAO,CACjD,MAFmC,EAAiB,CAAE,gBAAe,EAAI,CAAC,EAG1E,QACA,GAAI,IAAqB,IAAA,GAAmC,CAAC,EAAxB,CAAE,kBAAiB,EACxD,GAAI,IAAuB,IAAA,GAAqC,CAAC,EAA1B,CAAE,oBAAmB,EAC5D,GAAI,IAAqB,IAAA,GAAmC,CAAC,EAAxB,CAAE,kBAAiB,EACxD,GAAI,IAAgB,IAAA,GAA8B,CAAC,EAAnB,CAAE,aAAY,CAClD,EAEA,UAAe,aACV,gBAAgB,CAAO,CAAC,CACxB,KAAM,GAAS,CACZ,GAAI,IAAQ,EAAW,QAAS,CAC5B,EAAK,UAAU,CAAC,CAAC,QAAS,GAAU,EAAM,KAAK,CAAC,EAChD,MACJ,CACA,EAAU,QAAU,EACpB,IAAM,EAAQ,EAAK,eAAe,CAAC,CAAC,GAC9B,MAAoB,CAClB,IAAQ,EAAW,UACvB,EAAQ,EACR,EAAM,EACN,EAAU,MAAM,EAChB,EAAU,EACd,EACA,GAAO,iBAAiB,QAAS,CAAK,EACtC,EAAO,YAAgB,GAAO,oBAAoB,QAAS,CAAK,EAEhE,EAAU,CAAI,EACd,EAAW,GAAO,YAAY,CAAC,CAAC,gBAAkB,IAAI,EACtD,EAAY,EAAK,eAAe,CAAC,CAAC,OAAS,CAAC,EAC5C,EAAU,SAAS,CACvB,CAAC,CAAC,CACD,MAAO,GAAiB,CACjB,OAAQ,EAAW,QACvB,IAAI,EAAe,CAAG,EAAG,CACrB,EAAU,MAAM,EAChB,EAAc,CAAG,EACjB,MACJ,CACA,EAAS,EAAA,mBAAmB,EAAK,QAAQ,CAAC,EAC1C,EAAU,OAAO,CAFjB,CAGJ,CAAC,CACT,EAAG,CACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACJ,CAAC,EAID,OAFA,EAAA,EAAA,UAAA,KAAgB,EAAS,CAAC,CAAO,CAAC,EAE3B,CAAE,SAAQ,SAAQ,QAAO,UAAS,WAAU,YAAW,QAAO,MAAK,CAC9E"}