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":"RichTextEditor.cjs","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RichTextEditor.cjs","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-error-handler.cjs","names":[],"sources":["../../src/error-boundary/use-error-handler.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\n/**\n * Hook that propagates errors to the nearest {@link ErrorBoundary}.\n *\n * React error boundaries only catch errors thrown during render. Async errors\n * (failed mutations, websocket failures, etc.) need to be re-thrown in a\n * render pass — that is what `throwError` does.\n *\n * @example\n * const throwError = useErrorHandler();\n * useEffect(() => {\n * stream.on(\"error\", throwError);\n * }, []);\n */\nexport function useErrorHandler(): (error: unknown) => void {\n const [, setState] = useState<Error | null>(null);\n return useCallback((error: unknown) => {\n setState(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n }, []);\n}\n"],"mappings":"uBAeA,SAAgB,GAA4C,CACxD,GAAM,EAAG,IAAA,EAAA,EAAA,SAAA,CAAmC,IAAI,EAChD,OAAA,EAAA,EAAA,YAAA,CAAoB,GAAmB,CACnC,MAAe,CACX,MAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAClE,CAAC,CACL,EAAG,CAAC,CAAC,CACT"}
1
+ {"version":3,"file":"use-error-handler.cjs","names":[],"sources":["../../src/error-boundary/use-error-handler.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\n/**\n * Hook that propagates errors to the nearest {@link ErrorBoundary}.\n *\n * React error boundaries only catch errors thrown during render. Async errors\n * (failed mutations, websocket failures, etc.) need to be re-thrown in a\n * render pass — that is what `throwError` does.\n *\n * @example\n * const throwError = useErrorHandler();\n * useEffect(() => {\n * stream.on(\"error\", throwError);\n * }, []);\n */\nexport function useErrorHandler(): (error: unknown) => void {\n const [, setState] = useState<Error | null>(null);\n return useCallback((error: unknown) => {\n setState(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n }, []);\n}\n"],"mappings":"uBAeA,SAAgB,GAA4C,CACxD,GAAM,EAAG,IAAA,EAAY,EAAA,SAAA,CAAuB,IAAI,EAChD,OAAA,EAAO,EAAA,YAAA,CAAa,GAAmB,CACnC,MAAe,CACX,MAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAClE,CAAC,CACL,EAAG,CAAC,CAAC,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"FeatureFlagsProvider.cjs","names":[],"sources":["../../src/feature-flags/FeatureFlagsProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { FeatureFlagsAdapter, FlagValue } from \"./types\";\n\nconst FeatureFlagsContext = createContext<FeatureFlagsAdapter | null>(null);\n\nexport interface FeatureFlagsProviderProps {\n adapter: FeatureFlagsAdapter;\n children: ReactNode;\n}\n\n/** Inject a feature flags adapter into the tree. */\nexport function FeatureFlagsProvider({ adapter, children }: FeatureFlagsProviderProps) {\n const value = useMemo(() => adapter, [adapter]);\n useEffect(() => undefined, [value]);\n return <FeatureFlagsContext.Provider value={value}>{children}</FeatureFlagsContext.Provider>;\n}\n\nfunction useAdapter(): FeatureFlagsAdapter {\n const ctx = useContext(FeatureFlagsContext);\n if (!ctx) throw new Error(\"useFeatureFlag requires <FeatureFlagsProvider>\");\n return ctx;\n}\n\n/**\n * Read a boolean flag and re-render when the adapter fires `onChange`.\n *\n * @example\n * const showNewFeed = useFeatureFlag(\"new-feed\", false);\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): boolean => adapter.isEnabled(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n\n/**\n * Read a typed flag value (string / number / boolean / null) and re-render\n * on change.\n */\nexport function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): T => adapter.get<T>(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,EAAA,EAAA,cAAA,CAAgE,IAAI,EAQ1E,SAAgB,EAAqB,CAAE,UAAS,YAAuC,CACnF,IAAM,GAAA,EAAA,EAAA,QAAA,KAAsB,EAAS,CAAC,CAAO,CAAC,EAE9C,OADA,EAAA,EAAA,UAAA,KAAgB,IAAA,GAAW,CAAC,CAAK,CAAC,GAC3B,EAAA,EAAA,IAAA,CAAC,EAAoB,SAArB,CAAqC,QAAQ,UAAuC,CAAA,CAC/F,CAEA,SAAS,GAAkC,CACvC,IAAM,GAAA,EAAA,EAAA,WAAA,CAAiB,CAAmB,EAC1C,GAAI,CAAC,EAAK,MAAU,MAAM,gDAAgD,EAC1E,OAAO,CACX,CAQA,SAAgB,EAAe,EAAa,EAAe,GAAgB,CACvE,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAqB,EAAQ,UAAU,EAAK,CAAY,EAC9D,OAAA,EAAA,EAAA,qBAAA,CAA4B,EAAW,EAAK,CAAG,CACnD,CAMA,SAAgB,EAA8C,EAAa,EAAqB,CAC5F,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAe,EAAQ,IAAO,EAAK,CAAY,EACrD,OAAA,EAAA,EAAA,qBAAA,CAA4B,EAAW,EAAK,CAAG,CACnD"}
1
+ {"version":3,"file":"FeatureFlagsProvider.cjs","names":[],"sources":["../../src/feature-flags/FeatureFlagsProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { FeatureFlagsAdapter, FlagValue } from \"./types\";\n\nconst FeatureFlagsContext = createContext<FeatureFlagsAdapter | null>(null);\n\nexport interface FeatureFlagsProviderProps {\n adapter: FeatureFlagsAdapter;\n children: ReactNode;\n}\n\n/** Inject a feature flags adapter into the tree. */\nexport function FeatureFlagsProvider({ adapter, children }: FeatureFlagsProviderProps) {\n const value = useMemo(() => adapter, [adapter]);\n useEffect(() => undefined, [value]);\n return <FeatureFlagsContext.Provider value={value}>{children}</FeatureFlagsContext.Provider>;\n}\n\nfunction useAdapter(): FeatureFlagsAdapter {\n const ctx = useContext(FeatureFlagsContext);\n if (!ctx) throw new Error(\"useFeatureFlag requires <FeatureFlagsProvider>\");\n return ctx;\n}\n\n/**\n * Read a boolean flag and re-render when the adapter fires `onChange`.\n *\n * @example\n * const showNewFeed = useFeatureFlag(\"new-feed\", false);\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): boolean => adapter.isEnabled(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n\n/**\n * Read a typed flag value (string / number / boolean / null) and re-render\n * on change.\n */\nexport function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): T => adapter.get<T>(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,EAAsB,EAAA,cAAA,CAA0C,IAAI,EAQ1E,SAAgB,EAAqB,CAAE,UAAS,YAAuC,CACnF,IAAM,GAAA,EAAQ,EAAA,QAAA,KAAc,EAAS,CAAC,CAAO,CAAC,EAE9C,OADA,EAAA,EAAA,UAAA,KAAgB,IAAA,GAAW,CAAC,CAAK,CAAC,GAC3B,EAAA,EAAA,IAAA,CAAC,EAAoB,SAArB,CAAqC,QAAQ,UAAuC,CAAA,CAC/F,CAEA,SAAS,GAAkC,CACvC,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,CAAmB,EAC1C,GAAI,CAAC,EAAK,MAAU,MAAM,gDAAgD,EAC1E,OAAO,CACX,CAQA,SAAgB,EAAe,EAAa,EAAe,GAAgB,CACvE,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAqB,EAAQ,UAAU,EAAK,CAAY,EAC9D,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAW,EAAK,CAAG,CACnD,CAMA,SAAgB,EAA8C,EAAa,EAAqB,CAC5F,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAe,EAAQ,IAAO,EAAK,CAAY,EACrD,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAW,EAAK,CAAG,CACnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"FormField.cjs","names":[],"sources":["../../src/forms/FormField.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — FormFieldChildProps is not a component's props — it\n * is the contract handed to whatever control the field wraps, and it is exactly what\n * react-hook-form's register returns (name, value, onChange, onBlur, ref) plus what\n * the field adds around it (error, label, helperText, required, id). Trimming it\n * breaks the wiring it exists to describe.\n */\nimport { cloneElement, isValidElement } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n Controller,\n useFormContext,\n type Control,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\n\nexport interface FormFieldChildProps {\n name: string;\n value: unknown;\n onChange: (...args: unknown[]) => void;\n onBlur: () => void;\n ref: ControllerRenderProps[\"ref\"];\n error?: string;\n \"aria-invalid\"?: boolean;\n \"aria-describedby\"?: string;\n label?: ReactNode;\n helperText?: ReactNode;\n required?: boolean;\n id?: string;\n}\n\nexport interface FormFieldProps<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n> {\n /** Field name (dot path supported, e.g. `\"address.city\"`). */\n name: TName;\n /** Field label rendered by the wrapped control. */\n label?: ReactNode;\n /** Helper text rendered by the wrapped control when there is no error. */\n helperText?: ReactNode;\n /** When `true`, marks the control as required (visual hint + native attribute). */\n required?: boolean;\n /**\n * Explicit `control` from `useForm()` / `useZodForm()`. Optional when a\n * `FormProvider` is in the tree.\n */\n control?: Control<TValues>;\n /**\n * The control to render. Receives `{ value, onChange, onBlur, ref, error, ... }`\n * via `cloneElement`. Pass `<Input />`, `<Select />`, masked inputs, etc.\n */\n children: ReactElement;\n}\n\n/**\n * Glue between `react-hook-form` `Controller` and the SDK's controlled\n * components. Wraps any control that accepts `{ value, onChange, label,\n * error }` and routes RHF state into it — eliminating the per-field\n * `<Controller render={...} />` boilerplate.\n *\n * @example\n * const form = useZodForm(schema);\n * <FormProvider {...form}>\n * <Form>\n * <FormField name=\"email\" label=\"Email\" required>\n * <Input type=\"email\" />\n * </FormField>\n * <FormField name=\"cep\" label=\"CEP\">\n * <CEPInput />\n * </FormField>\n * </Form>\n * </FormProvider>;\n */\nexport function FormField<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n>({ name, label, helperText, required, control, children }: FormFieldProps<TValues, TName>) {\n const context = useFormContext<TValues>();\n const resolvedControl = control ?? context?.control;\n if (!resolvedControl) {\n throw new Error(\n \"FormField requires either a `control` prop or a <FormProvider> in the tree.\",\n );\n }\n\n return (\n <Controller\n name={name}\n control={resolvedControl}\n render={({ field, fieldState }) => {\n if (!isValidElement(children)) return children;\n const errorMessage = fieldState.error?.message;\n return cloneElement(children as ReactElement<FormFieldChildProps>, {\n name: field.name,\n value: field.value,\n onChange: field.onChange,\n onBlur: field.onBlur,\n ref: field.ref,\n label,\n helperText: errorMessage ? undefined : helperText,\n error: errorMessage,\n required,\n \"aria-invalid\": !!errorMessage,\n });\n }}\n />\n );\n}\n"],"mappings":"mFA4EA,SAAgB,EAGd,CAAE,OAAM,QAAO,aAAY,WAAU,UAAS,YAA4C,CACxF,IAAM,GAAA,EAAA,EAAA,eAAA,CAAkC,EAClC,EAAkB,GAAW,GAAS,QAC5C,GAAI,CAAC,EACD,MAAU,MACN,6EACJ,EAGJ,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CACU,OACN,QAAS,EACT,QAAS,CAAE,QAAO,gBAAiB,CAC/B,GAAI,EAAA,EAAA,EAAA,eAAA,CAAgB,CAAQ,EAAG,OAAO,EACtC,IAAM,EAAe,EAAW,OAAO,QACvC,OAAA,EAAA,EAAA,aAAA,CAAoB,EAA+C,CAC/D,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,QACA,WAAY,EAAe,IAAA,GAAY,EACvC,MAAO,EACP,WACA,eAAgB,CAAC,CAAC,CACtB,CAAC,CACL,CACH,CAAA,CAET"}
1
+ {"version":3,"file":"FormField.cjs","names":[],"sources":["../../src/forms/FormField.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — FormFieldChildProps is not a component's props — it\n * is the contract handed to whatever control the field wraps, and it is exactly what\n * react-hook-form's register returns (name, value, onChange, onBlur, ref) plus what\n * the field adds around it (error, label, helperText, required, id). Trimming it\n * breaks the wiring it exists to describe.\n */\nimport { cloneElement, isValidElement } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n Controller,\n useFormContext,\n type Control,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\n\nexport interface FormFieldChildProps {\n name: string;\n value: unknown;\n onChange: (...args: unknown[]) => void;\n onBlur: () => void;\n ref: ControllerRenderProps[\"ref\"];\n error?: string;\n \"aria-invalid\"?: boolean;\n \"aria-describedby\"?: string;\n label?: ReactNode;\n helperText?: ReactNode;\n required?: boolean;\n id?: string;\n}\n\nexport interface FormFieldProps<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n> {\n /** Field name (dot path supported, e.g. `\"address.city\"`). */\n name: TName;\n /** Field label rendered by the wrapped control. */\n label?: ReactNode;\n /** Helper text rendered by the wrapped control when there is no error. */\n helperText?: ReactNode;\n /** When `true`, marks the control as required (visual hint + native attribute). */\n required?: boolean;\n /**\n * Explicit `control` from `useForm()` / `useZodForm()`. Optional when a\n * `FormProvider` is in the tree.\n */\n control?: Control<TValues>;\n /**\n * The control to render. Receives `{ value, onChange, onBlur, ref, error, ... }`\n * via `cloneElement`. Pass `<Input />`, `<Select />`, masked inputs, etc.\n */\n children: ReactElement;\n}\n\n/**\n * Glue between `react-hook-form` `Controller` and the SDK's controlled\n * components. Wraps any control that accepts `{ value, onChange, label,\n * error }` and routes RHF state into it — eliminating the per-field\n * `<Controller render={...} />` boilerplate.\n *\n * @example\n * const form = useZodForm(schema);\n * <FormProvider {...form}>\n * <Form>\n * <FormField name=\"email\" label=\"Email\" required>\n * <Input type=\"email\" />\n * </FormField>\n * <FormField name=\"cep\" label=\"CEP\">\n * <CEPInput />\n * </FormField>\n * </Form>\n * </FormProvider>;\n */\nexport function FormField<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n>({ name, label, helperText, required, control, children }: FormFieldProps<TValues, TName>) {\n const context = useFormContext<TValues>();\n const resolvedControl = control ?? context?.control;\n if (!resolvedControl) {\n throw new Error(\n \"FormField requires either a `control` prop or a <FormProvider> in the tree.\",\n );\n }\n\n return (\n <Controller\n name={name}\n control={resolvedControl}\n render={({ field, fieldState }) => {\n if (!isValidElement(children)) return children;\n const errorMessage = fieldState.error?.message;\n return cloneElement(children as ReactElement<FormFieldChildProps>, {\n name: field.name,\n value: field.value,\n onChange: field.onChange,\n onBlur: field.onBlur,\n ref: field.ref,\n label,\n helperText: errorMessage ? undefined : helperText,\n error: errorMessage,\n required,\n \"aria-invalid\": !!errorMessage,\n });\n }}\n />\n );\n}\n"],"mappings":"mFA4EA,SAAgB,EAGd,CAAE,OAAM,QAAO,aAAY,WAAU,UAAS,YAA4C,CACxF,IAAM,GAAA,EAAU,EAAA,eAAA,CAAwB,EAClC,EAAkB,GAAW,GAAS,QAC5C,GAAI,CAAC,EACD,MAAU,MACN,6EACJ,EAGJ,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CACU,OACN,QAAS,EACT,QAAS,CAAE,QAAO,gBAAiB,CAC/B,GAAI,EAAA,EAAC,EAAA,eAAA,CAAe,CAAQ,EAAG,OAAO,EACtC,IAAM,EAAe,EAAW,OAAO,QACvC,OAAA,EAAO,EAAA,aAAA,CAAa,EAA+C,CAC/D,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,QACA,WAAY,EAAe,IAAA,GAAY,EACvC,MAAO,EACP,WACA,eAAgB,CAAC,CAAC,CACtB,CAAC,CACL,CACH,CAAA,CAET"}
@@ -1,2 +1,2 @@
1
- const e=require("../components/Input/Input.cjs"),t=require("../utils/format.cjs"),n=require("./br-validators.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(t,n=`numeric`){return(0,r.forwardRef)(function({value:r,onChange:a,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,value:t(r??``),inputMode:n,onChange:e=>a(t(e.target.value))})})}var o=a(t.formatCPF),s=a(n.formatCNPJ),c=a(t.formatPhone,`tel`),l=a(n.formatCEP);function u(e,t,n){return new Intl.NumberFormat(t,{style:`currency`,currency:n}).format(e/100)}function d(e){let t=e.replace(/\D/g,``);return t?Number.parseInt(t,10):0}var f=(0,r.forwardRef)(function({value:t,onChange:n,currency:r=`BRL`,locale:a=`pt-BR`,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,type:`text`,inputMode:`numeric`,value:u(t||0,a,r),onChange:e=>n(d(e.target.value))})});exports.CEPInput=l,exports.CNPJInput=s,exports.CPFInput=o,exports.MoneyInput=f,exports.PhoneInput=c;
1
+ const e=require("../components/Input/Input.cjs"),t=require("../utils/format.cjs"),n=require("./br-validators.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(t,n=`numeric`){return(0,r.forwardRef)(function({value:r,onChange:a,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,value:t(r??``),inputMode:n,onChange:e=>a?.(t(e.target.value))})})}var o=a(t.formatCPF),s=a(n.formatCNPJ),c=a(t.formatPhone,`tel`),l=a(n.formatCEP);function u(e,t,n){return new Intl.NumberFormat(t,{style:`currency`,currency:n}).format(e/100)}function d(e){let t=e.replace(/\D/g,``);return t?Number.parseInt(t,10):0}var f=(0,r.forwardRef)(function({value:t,onChange:n,currency:r=`BRL`,locale:a=`pt-BR`,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,type:`text`,inputMode:`numeric`,value:u(t||0,a,r),onChange:e=>n(d(e.target.value))})});exports.CEPInput=l,exports.CNPJInput=s,exports.CPFInput=o,exports.MoneyInput=f,exports.PhoneInput=c;
2
2
  //# sourceMappingURL=masked-inputs.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"masked-inputs.cjs","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value: string;\n onChange: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":"yKAWA,SAAS,EACL,EACA,EAAgE,UAClE,CACE,OAAA,EAAA,EAAA,WAAA,CAAsD,SAClD,CAAE,QAAO,WAAU,GAAG,GACtB,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,MAAO,EAAK,GAAS,EAAE,EACZ,YACX,SAAW,GAAU,EAAS,EAAK,EAAM,OAAO,KAAK,CAAC,CACzD,CAAA,CAET,CAAC,CACL,CAEA,IAAa,EAAW,EAAY,EAAA,SAAS,EAChC,EAAY,EAAY,EAAA,UAAU,EAClC,EAAa,EAAY,EAAA,YAAa,KAAK,EAC3C,EAAW,EAAY,EAAA,SAAS,EAY7C,SAAS,EAAY,EAAe,EAAgB,EAA0B,CAC1E,OAAO,IAAI,KAAK,aAAa,EAAQ,CAAE,MAAO,WAAY,UAAS,CAAC,CAAC,CAAC,OAAO,EAAQ,GAAG,CAC5F,CAEA,SAAS,EAAW,EAAsB,CACtC,IAAM,EAAS,EAAK,QAAQ,MAAO,EAAE,EAErC,OADK,EACE,OAAO,SAAS,EAAQ,EAAE,EADb,CAExB,CAOA,IAAa,GAAA,EAAA,EAAA,WAAA,CAA2D,SACpE,CAAE,QAAO,WAAU,WAAW,MAAO,SAAS,QAAS,GAAG,GAC1D,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,KAAK,OACL,UAAU,UACV,MAAO,EAAY,GAAS,EAAG,EAAQ,CAAQ,EAC/C,SAAW,GAAU,EAAS,EAAW,EAAM,OAAO,KAAK,CAAC,CAC/D,CAAA,CAET,CAAC"}
1
+ {"version":3,"file":"masked-inputs.cjs","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\n/**\n * Props of every masked input.\n *\n * `value` and `onChange` are optional because the primary documented usage is\n * inside `<FormField>`, which supplies both through `cloneElement` — declaring\n * them required made `<FormField name=\"cpf\"><CPFInput /></FormField>` fail to\n * type-check while working perfectly at runtime. Standalone usage still passes\n * both; an uncontrolled input without `onChange` simply reports nothing.\n */\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value?: string;\n onChange?: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange?.(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":"yKAoBA,SAAS,EACL,EACA,EAAgE,UAClE,CACE,OAAA,EAAO,EAAA,WAAA,CAA+C,SAClD,CAAE,QAAO,WAAU,GAAG,GACtB,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,MAAO,EAAK,GAAS,EAAE,EACZ,YACX,SAAW,GAAU,IAAW,EAAK,EAAM,OAAO,KAAK,CAAC,CAC3D,CAAA,CAET,CAAC,CACL,CAEA,IAAa,EAAW,EAAY,EAAA,SAAS,EAChC,EAAY,EAAY,EAAA,UAAU,EAClC,EAAa,EAAY,EAAA,YAAa,KAAK,EAC3C,EAAW,EAAY,EAAA,SAAS,EAY7C,SAAS,EAAY,EAAe,EAAgB,EAA0B,CAC1E,OAAO,IAAI,KAAK,aAAa,EAAQ,CAAE,MAAO,WAAY,UAAS,CAAC,CAAC,CAAC,OAAO,EAAQ,GAAG,CAC5F,CAEA,SAAS,EAAW,EAAsB,CACtC,IAAM,EAAS,EAAK,QAAQ,MAAO,EAAE,EAErC,OADK,EACE,OAAO,SAAS,EAAQ,EAAE,EADb,CAExB,CAOA,IAAa,GAAA,EAAa,EAAA,WAAA,CAA8C,SACpE,CAAE,QAAO,WAAU,WAAW,MAAO,SAAS,QAAS,GAAG,GAC1D,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,KAAK,OACL,UAAU,UACV,MAAO,EAAY,GAAS,EAAG,EAAQ,CAAQ,EAC/C,SAAW,GAAU,EAAS,EAAW,EAAM,OAAO,KAAK,CAAC,CAC/D,CAAA,CAET,CAAC"}
@@ -11,7 +11,7 @@ function s(t, n = "numeric") {
11
11
  ref: s,
12
12
  value: t(r ?? ""),
13
13
  inputMode: n,
14
- onChange: (e) => i(t(e.target.value))
14
+ onChange: (e) => i?.(t(e.target.value))
15
15
  });
16
16
  });
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"masked-inputs.js","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value: string;\n onChange: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":";;;;;;AAWA,SAAS,EACL,GACA,IAAgE,WAClE;CACE,OAAO,EAA+C,SAClD,EAAE,UAAO,aAAU,GAAG,KACtB,GACF;EACE,OACI,kBAAC,GAAD;GACI,GAAI;GACC;GACL,OAAO,EAAK,KAAS,EAAE;GACZ;GACX,WAAW,MAAU,EAAS,EAAK,EAAM,OAAO,KAAK,CAAC;EACzD,CAAA;CAET,CAAC;AACL;AAEA,IAAa,IAAW,EAAY,CAAS,GAChC,IAAY,EAAY,CAAU,GAClC,IAAa,EAAY,GAAa,KAAK,GAC3C,IAAW,EAAY,CAAS;AAY7C,SAAS,EAAY,GAAe,GAAgB,GAA0B;CAC1E,OAAO,IAAI,KAAK,aAAa,GAAQ;EAAE,OAAO;EAAY;CAAS,CAAC,CAAC,CAAC,OAAO,IAAQ,GAAG;AAC5F;AAEA,SAAS,EAAW,GAAsB;CACtC,IAAM,IAAS,EAAK,QAAQ,OAAO,EAAE;CAErC,OADK,IACE,OAAO,SAAS,GAAQ,EAAE,IADb;AAExB;AAOA,IAAa,IAAa,EAA8C,SACpE,EAAE,UAAO,aAAU,cAAW,OAAO,YAAS,SAAS,GAAG,KAC1D,GACF;CACE,OACI,kBAAC,GAAD;EACI,GAAI;EACC;EACL,MAAK;EACL,WAAU;EACV,OAAO,EAAY,KAAS,GAAG,GAAQ,CAAQ;EAC/C,WAAW,MAAU,EAAS,EAAW,EAAM,OAAO,KAAK,CAAC;CAC/D,CAAA;AAET,CAAC"}
1
+ {"version":3,"file":"masked-inputs.js","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\n/**\n * Props of every masked input.\n *\n * `value` and `onChange` are optional because the primary documented usage is\n * inside `<FormField>`, which supplies both through `cloneElement` — declaring\n * them required made `<FormField name=\"cpf\"><CPFInput /></FormField>` fail to\n * type-check while working perfectly at runtime. Standalone usage still passes\n * both; an uncontrolled input without `onChange` simply reports nothing.\n */\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value?: string;\n onChange?: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange?.(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":";;;;;;AAoBA,SAAS,EACL,GACA,IAAgE,WAClE;CACE,OAAO,EAA+C,SAClD,EAAE,UAAO,aAAU,GAAG,KACtB,GACF;EACE,OACI,kBAAC,GAAD;GACI,GAAI;GACC;GACL,OAAO,EAAK,KAAS,EAAE;GACZ;GACX,WAAW,MAAU,IAAW,EAAK,EAAM,OAAO,KAAK,CAAC;EAC3D,CAAA;CAET,CAAC;AACL;AAEA,IAAa,IAAW,EAAY,CAAS,GAChC,IAAY,EAAY,CAAU,GAClC,IAAa,EAAY,GAAa,KAAK,GAC3C,IAAW,EAAY,CAAS;AAY7C,SAAS,EAAY,GAAe,GAAgB,GAA0B;CAC1E,OAAO,IAAI,KAAK,aAAa,GAAQ;EAAE,OAAO;EAAY;CAAS,CAAC,CAAC,CAAC,OAAO,IAAQ,GAAG;AAC5F;AAEA,SAAS,EAAW,GAAsB;CACtC,IAAM,IAAS,EAAK,QAAQ,OAAO,EAAE;CAErC,OADK,IACE,OAAO,SAAS,GAAQ,EAAE,IADb;AAExB;AAOA,IAAa,IAAa,EAA8C,SACpE,EAAE,UAAO,aAAU,cAAW,OAAO,YAAS,SAAS,GAAG,KAC1D,GACF;CACE,OACI,kBAAC,GAAD;EACI,GAAI;EACC;EACL,MAAK;EACL,WAAU;EACV,OAAO,EAAY,KAAS,GAAG,GAAQ,CAAQ;EAC/C,WAAW,MAAU,EAAS,EAAW,EAAM,OAAO,KAAK,CAAC;CAC/D,CAAA;AAET,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-viacep.cjs","names":[],"sources":["../../src/forms/use-viacep.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface ViaCEPResult {\n cep: string;\n logradouro: string;\n complemento: string;\n bairro: string;\n localidade: string;\n uf: string;\n ibge?: string;\n gia?: string;\n ddd?: string;\n siafi?: string;\n}\n\nexport interface UseViaCEPResult {\n loading: boolean;\n error: string | null;\n data: ViaCEPResult | null;\n /** Fetch the given CEP (8 digits or masked). Sets `data`/`error`. Returns the result. */\n lookup: (cep: string) => Promise<ViaCEPResult | null>;\n reset: () => void;\n}\n\n/**\n * React hook for the public ViaCEP service (`viacep.com.br`). No backend\n * required. Returns address fields when the CEP exists, or sets `error` for\n * invalid CEPs.\n */\nexport function useViaCEP(): UseViaCEPResult {\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [data, setData] = useState<ViaCEPResult | null>(null);\n\n const lookup = useCallback(async (cep: string): Promise<ViaCEPResult | null> => {\n const digits = cep.replace(/\\D/g, \"\");\n if (digits.length !== 8) {\n setError(\"CEP inválido.\");\n return null;\n }\n setLoading(true);\n setError(null);\n try {\n const response = await fetch(`https://viacep.com.br/ws/${digits}/json/`);\n const json = (await response.json()) as ViaCEPResult & { erro?: boolean };\n if (\"erro\" in json && json.erro) {\n setError(\"CEP não encontrado.\");\n setData(null);\n return null;\n }\n setData(json);\n return json;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return null;\n } finally {\n setLoading(false);\n }\n }, []);\n\n const reset = useCallback(() => {\n setLoading(false);\n setError(null);\n setData(null);\n }, []);\n\n return { loading, error, data, lookup, reset };\n}\n"],"mappings":"uBA6BA,SAAgB,GAA6B,CACzC,GAAM,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAAgC,EAAK,EAC/C,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAoC,IAAI,EAChD,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAAyC,IAAI,EAkC1D,MAAO,CAAE,UAAS,QAAO,OAAM,QAAA,EAAA,EAAA,YAAA,CAhCJ,KAAO,IAA8C,CAC5E,IAAM,EAAS,EAAI,QAAQ,MAAO,EAAE,EACpC,GAAI,EAAO,SAAW,EAElB,OADA,EAAS,eAAe,EACjB,KAEX,EAAW,EAAI,EACf,EAAS,IAAI,EACb,GAAI,CAEA,IAAM,EAAQ,MAAM,MADG,MAAM,4BAA4B,EAAO,OAAO,EAAA,CAC1C,KAAK,EAOlC,MANI,SAAU,GAAQ,EAAK,MACvB,EAAS,qBAAqB,EAC9B,EAAQ,IAAI,EACL,OAEX,EAAQ,CAAI,EACL,EACX,OAAS,EAAK,CAEV,OADA,EAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAAC,EAClD,IACX,QAAU,CACN,EAAW,EAAK,CACpB,CACJ,EAAG,CAAC,CAQ2B,EAAQ,OAAA,EAAA,EAAA,YAAA,KANP,CAC5B,EAAW,EAAK,EAChB,EAAS,IAAI,EACb,EAAQ,IAAI,CAChB,EAAG,CAAC,CAEmC,CAAM,CACjD"}
1
+ {"version":3,"file":"use-viacep.cjs","names":[],"sources":["../../src/forms/use-viacep.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface ViaCEPResult {\n cep: string;\n logradouro: string;\n complemento: string;\n bairro: string;\n localidade: string;\n uf: string;\n ibge?: string;\n gia?: string;\n ddd?: string;\n siafi?: string;\n}\n\nexport interface UseViaCEPResult {\n loading: boolean;\n error: string | null;\n data: ViaCEPResult | null;\n /** Fetch the given CEP (8 digits or masked). Sets `data`/`error`. Returns the result. */\n lookup: (cep: string) => Promise<ViaCEPResult | null>;\n reset: () => void;\n}\n\n/**\n * React hook for the public ViaCEP service (`viacep.com.br`). No backend\n * required. Returns address fields when the CEP exists, or sets `error` for\n * invalid CEPs.\n */\nexport function useViaCEP(): UseViaCEPResult {\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [data, setData] = useState<ViaCEPResult | null>(null);\n\n const lookup = useCallback(async (cep: string): Promise<ViaCEPResult | null> => {\n const digits = cep.replace(/\\D/g, \"\");\n if (digits.length !== 8) {\n setError(\"CEP inválido.\");\n return null;\n }\n setLoading(true);\n setError(null);\n try {\n const response = await fetch(`https://viacep.com.br/ws/${digits}/json/`);\n const json = (await response.json()) as ViaCEPResult & { erro?: boolean };\n if (\"erro\" in json && json.erro) {\n setError(\"CEP não encontrado.\");\n setData(null);\n return null;\n }\n setData(json);\n return json;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return null;\n } finally {\n setLoading(false);\n }\n }, []);\n\n const reset = useCallback(() => {\n setLoading(false);\n setError(null);\n setData(null);\n }, []);\n\n return { loading, error, data, lookup, reset };\n}\n"],"mappings":"uBA6BA,SAAgB,GAA6B,CACzC,GAAM,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAkB,EAAK,EAC/C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAwB,IAAI,EAChD,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAA8B,IAAI,EAkC1D,MAAO,CAAE,UAAS,QAAO,OAAM,QAAA,EAhChB,EAAA,YAAA,CAAY,KAAO,IAA8C,CAC5E,IAAM,EAAS,EAAI,QAAQ,MAAO,EAAE,EACpC,GAAI,EAAO,SAAW,EAElB,OADA,EAAS,eAAe,EACjB,KAEX,EAAW,EAAI,EACf,EAAS,IAAI,EACb,GAAI,CAEA,IAAM,EAAQ,MAAM,MADG,MAAM,4BAA4B,EAAO,OAAO,EAAA,CAC1C,KAAK,EAOlC,MANI,SAAU,GAAQ,EAAK,MACvB,EAAS,qBAAqB,EAC9B,EAAQ,IAAI,EACL,OAEX,EAAQ,CAAI,EACL,EACX,OAAS,EAAK,CAEV,OADA,EAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAAC,EAClD,IACX,QAAU,CACN,EAAW,EAAK,CACpB,CACJ,EAAG,CAAC,CAQ2B,EAAQ,OAAA,EANzB,EAAA,YAAA,KAAkB,CAC5B,EAAW,EAAK,EAChB,EAAS,IAAI,EACb,EAAQ,IAAI,CAChB,EAAG,CAAC,CAEmC,CAAM,CACjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-zod-form.cjs","names":[],"sources":["../../src/forms/use-zod-form.ts"],"sourcesContent":["import { useForm } from \"react-hook-form\";\nimport type { FieldValues, UseFormProps, UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { zodResolver } from \"./zod-resolver\";\n\n/**\n * Convenience wrapper around `react-hook-form`'s `useForm` that wires a zod\n * resolver and infers the form's value type from the schema.\n *\n * Both `react-hook-form` and `zod` are optional peer dependencies — install\n * them when your app uses forms.\n *\n * @example\n * const form = useZodForm(loginSchema, { defaultValues: { email: \"\" } });\n * form.register(\"email\");\n */\nexport function useZodForm<\n TSchema extends z.ZodTypeAny,\n TValues extends FieldValues = z.infer<TSchema> & FieldValues,\n>(schema: TSchema, options: Omit<UseFormProps<TValues>, \"resolver\"> = {}): UseFormReturn<TValues> {\n return useForm<TValues>({\n ...options,\n resolver: zodResolver(schema) as unknown as UseFormProps<TValues>[\"resolver\"],\n });\n}\n"],"mappings":"uEAgBA,SAAgB,EAGd,EAAiB,EAAmD,CAAC,EAA2B,CAC9F,OAAA,EAAA,EAAA,QAAA,CAAwB,CACpB,GAAG,EACH,SAAU,EAAA,YAAY,CAAM,CAChC,CAAC,CACL"}
1
+ {"version":3,"file":"use-zod-form.cjs","names":[],"sources":["../../src/forms/use-zod-form.ts"],"sourcesContent":["import { useForm } from \"react-hook-form\";\nimport type { FieldValues, UseFormProps, UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { zodResolver } from \"./zod-resolver\";\n\n/**\n * Convenience wrapper around `react-hook-form`'s `useForm` that wires a zod\n * resolver and infers the form's value type from the schema.\n *\n * Both `react-hook-form` and `zod` are optional peer dependencies — install\n * them when your app uses forms.\n *\n * @example\n * const form = useZodForm(loginSchema, { defaultValues: { email: \"\" } });\n * form.register(\"email\");\n */\nexport function useZodForm<\n TSchema extends z.ZodTypeAny,\n TValues extends FieldValues = z.infer<TSchema> & FieldValues,\n>(schema: TSchema, options: Omit<UseFormProps<TValues>, \"resolver\"> = {}): UseFormReturn<TValues> {\n return useForm<TValues>({\n ...options,\n resolver: zodResolver(schema) as unknown as UseFormProps<TValues>[\"resolver\"],\n });\n}\n"],"mappings":"uEAgBA,SAAgB,EAGd,EAAiB,EAAmD,CAAC,EAA2B,CAC9F,OAAA,EAAO,EAAA,QAAA,CAAiB,CACpB,GAAG,EACH,SAAU,EAAA,YAAY,CAAM,CAChC,CAAC,CACL"}
@@ -1 +1 @@
1
- {"version":3,"file":"zod-resolver.cjs","names":[],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\ninterface ResolverOutput<T> {\n values: T | object;\n errors: Record<string, ResolverError | object>;\n}\n\ntype Resolver<T> = (\n values: unknown,\n context: unknown,\n options: { criteriaMode?: \"firstError\" | \"all\" },\n) => Promise<ResolverOutput<T>>;\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema>> {\n return async (values, _context, options) => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors };\n };\n}\n"],"mappings":"AA0BA,SAAgB,EACZ,EAC0B,CAC1B,OAAO,MAAO,EAAQ,EAAU,IAAY,CACxC,IAAM,EAAS,EAAO,UAAU,CAAM,EACtC,GAAI,EAAO,QACP,MAAO,CAAE,OAAQ,EAAO,KAAM,OAAQ,CAAC,CAAE,EAG7C,IAAM,EAAiD,CAAC,EAClD,EAAe,EAAQ,cAAgB,aAE7C,IAAK,IAAM,KAAS,EAAO,MAAM,OAAQ,CACrC,IAAM,EAAO,EAAM,KAAK,SAAW,EAAI,QAAU,EAAM,KAAK,KAAK,GAAG,EAChE,IAAiB,cAAgB,EAAO,KAC5C,EAAO,GAAQ,CAAE,KAAM,EAAM,KAAM,QAAS,EAAM,OAAQ,EAC9D,CAEA,MAAO,CAAE,OAAQ,CAAC,EAAG,QAAO,CAChC,CACJ"}
1
+ {"version":3,"file":"zod-resolver.cjs","names":[],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":"AA+BA,SAAgB,EACZ,EACwC,CAGxC,OAAO,MACH,EACA,EACA,IACkC,CAClC,IAAM,EAAS,EAAO,UAAU,CAAM,EACtC,GAAI,EAAO,QACP,MAAO,CAAE,OAAQ,EAAO,KAAgB,OAAQ,CAAC,CAAE,EAGvD,IAAM,EAAiD,CAAC,EAClD,EAAe,EAAQ,cAAgB,aAE7C,IAAK,IAAM,KAAS,EAAO,MAAM,OAAQ,CACrC,IAAM,EAAO,EAAM,KAAK,SAAW,EAAI,QAAU,EAAM,KAAK,KAAK,GAAG,EAChE,IAAiB,cAAgB,EAAO,KAC5C,EAAO,GAAQ,CAAE,KAAM,EAAM,KAAM,QAAS,EAAM,OAAQ,EAC9D,CAEA,MAAO,CAAE,OAAQ,CAAC,EAAW,QAA8B,CAC/D,CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"zod-resolver.js","names":[],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\ninterface ResolverOutput<T> {\n values: T | object;\n errors: Record<string, ResolverError | object>;\n}\n\ntype Resolver<T> = (\n values: unknown,\n context: unknown,\n options: { criteriaMode?: \"firstError\" | \"all\" },\n) => Promise<ResolverOutput<T>>;\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema>> {\n return async (values, _context, options) => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors };\n };\n}\n"],"mappings":";AA0BA,SAAgB,EACZ,GAC0B;CAC1B,OAAO,OAAO,GAAQ,GAAU,MAAY;EACxC,IAAM,IAAS,EAAO,UAAU,CAAM;EACtC,IAAI,EAAO,SACP,OAAO;GAAE,QAAQ,EAAO;GAAM,QAAQ,CAAC;EAAE;EAG7C,IAAM,IAAiD,CAAC,GAClD,IAAe,EAAQ,gBAAgB;EAE7C,KAAK,IAAM,KAAS,EAAO,MAAM,QAAQ;GACrC,IAAM,IAAO,EAAM,KAAK,WAAW,IAAI,UAAU,EAAM,KAAK,KAAK,GAAG;GAChE,MAAiB,gBAAgB,EAAO,OAC5C,EAAO,KAAQ;IAAE,MAAM,EAAM;IAAM,SAAS,EAAM;GAAQ;EAC9D;EAEA,OAAO;GAAE,QAAQ,CAAC;GAAG;EAAO;CAChC;AACJ"}
1
+ {"version":3,"file":"zod-resolver.js","names":[],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":";AA+BA,SAAgB,EACZ,GACwC;CAGxC,OAAO,OACH,GACA,GACA,MACkC;EAClC,IAAM,IAAS,EAAO,UAAU,CAAM;EACtC,IAAI,EAAO,SACP,OAAO;GAAE,QAAQ,EAAO;GAAgB,QAAQ,CAAC;EAAE;EAGvD,IAAM,IAAiD,CAAC,GAClD,IAAe,EAAQ,gBAAgB;EAE7C,KAAK,IAAM,KAAS,EAAO,MAAM,QAAQ;GACrC,IAAM,IAAO,EAAM,KAAK,WAAW,IAAI,UAAU,EAAM,KAAK,KAAK,GAAG;GAChE,MAAiB,gBAAgB,EAAO,OAC5C,EAAO,KAAQ;IAAE,MAAM,EAAM;IAAM,SAAS,EAAM;GAAQ;EAC9D;EAEA,OAAO;GAAE,QAAQ,CAAC;GAAW;EAA8B;CAC/D;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"TrajectoryMap.cjs","names":[],"sources":["../../src/geo/TrajectoryMap.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the SVG plot (points,\n * current, height, padding, showGrid, showScale, strokeColor, label), the optional\n * leaflet tile layer (tileUrl, tileAttribution) and the overlay (markers,\n * onMarkerClick). The body projects the path, fits the bounds and hands the same\n * bounds to the tile layer when there is one.\n */\nimport { lazy, Suspense, useEffect, useMemo, useRef, useState, type HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { boundingBox, expandBounds } from \"./bounds\";\nimport { haversineKm } from \"./distance\";\nimport { fitProjection, type PixelPoint } from \"./projection\";\nimport type { Coordinate, GeoMarker } from \"./types\";\nimport styles from \"./TrajectoryMap.module.css\";\n\n// Leaflet layer is loaded on demand — only when a `tileUrl` is supplied, so\n// apps that stick to the tile-free SVG never pull leaflet into their bundle.\nconst LeafletTrajectory = lazy(() =>\n import(\"./leaflet-map\").then((m) => ({ default: m.LeafletTrajectory })),\n);\n\nexport interface TrajectoryMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Ordered coordinates forming the trajectory. */\n points: readonly Coordinate[];\n /** Optional \"current position\" marker, drawn on top of the path. */\n current?: Coordinate | null;\n /** Viewport height in pixels. Default: `320`. */\n height?: number;\n /** Inner padding in pixels kept clear on every edge. Default: `24`. */\n padding?: number;\n /** Draw a light reference grid behind the path (SVG mode). Default: `true`. */\n showGrid?: boolean;\n /** Draw a distance scale bar (SVG mode). Default: `true`. */\n showScale?: boolean;\n /** Path stroke color. Default: the primary token. */\n strokeColor?: string;\n /**\n * Tile URL template (`{z}/{x}/{y}`) of a map server **you host**. When set,\n * the map upgrades to a real Leaflet tile layer (requires `leaflet` to be\n * installed). Omit for the zero-dependency, zero-network SVG plot.\n */\n tileUrl?: string;\n /** Attribution text shown over the tile layer. */\n tileAttribution?: string;\n /** Accessible label for the map region. Default: `\"Trajetória\"`. */\n label?: string;\n /** Point markers to overlay (e.g. stops, POIs). Included in the auto-fit. */\n markers?: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\nconst NICE_KM_STEPS = [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000] as const;\n\n/** Pick the largest \"nice\" distance whose bar fits within `maxPixels`. */\nfunction pickScaleKm(kmPerPixel: number, maxPixels: number): { km: number; pixels: number } {\n const maxKm = kmPerPixel * maxPixels;\n let chosen: number = NICE_KM_STEPS[0];\n for (const step of NICE_KM_STEPS) {\n if (step <= maxKm) chosen = step;\n }\n return { km: chosen, pixels: chosen / kmPerPixel };\n}\n\n/** Format a distance in km for the scale bar (meters below 1 km). */\nfunction formatScale(km: number): string {\n return km < 1 ? `${Math.round(km * 1000)} m` : `${km % 1 === 0 ? km : km.toFixed(1)} km`;\n}\n\n/**\n * Plot a GPS trajectory. By default it renders a **tile-free SVG** map (Web\n * Mercator projection, auto-fit to the points, optional grid + scale bar) —\n * 100% self-contained, no external tiles or paid API. Pass `tileUrl` pointing\n * at a map server **you host** to upgrade to a real Leaflet tile layer.\n *\n * @example\n * // Zero-dependency SVG plot\n * <TrajectoryMap points={points} current={lastPoint} height={360} />\n *\n * @example\n * // Real tiles from your own server (requires `leaflet` installed)\n * <TrajectoryMap points={points} tileUrl=\"https://tiles.internal/{z}/{x}/{y}.png\" />\n */\nexport function TrajectoryMap({\n points,\n current,\n height = 320,\n padding = 24,\n showGrid = true,\n showScale = true,\n strokeColor,\n tileUrl,\n tileAttribution,\n label = \"Trajetória\",\n markers,\n onMarkerClick,\n className,\n style,\n ...rest\n}: TrajectoryMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const allPoints = useMemo<Coordinate[]>(\n () => [...points, ...(current ? [current] : []), ...(markers ?? [])],\n [points, current, markers],\n );\n\n const svg = useMemo(() => {\n const box = boundingBox(allPoints);\n if (!box) return null;\n\n const projection = fitProjection(expandBounds(box, 0.12), width, height, { padding });\n const pixels: PixelPoint[] = points.map((p) => projection.project(p));\n const polyline = pixels.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(\" \");\n\n const startPixel = pixels[0] ?? null;\n const endPixel = pixels.length > 0 ? pixels[pixels.length - 1] : null;\n const currentPixel = current ? projection.project(current) : null;\n\n // Distance-per-pixel at the map center, for the scale bar: measure the\n // center → east-edge span in both km (haversine) and pixels (projected).\n const centerCoord = {\n latitude: (box.minLatitude + box.maxLatitude) / 2,\n longitude: (box.minLongitude + box.maxLongitude) / 2,\n };\n const center = projection.project(centerCoord);\n const eastRef = projection.project({ ...centerCoord, longitude: box.maxLongitude });\n const edgeKm = haversineKm(centerCoord, { ...centerCoord, longitude: box.maxLongitude });\n const edgePixels = Math.abs(eastRef.x - center.x) || 1;\n const kmPerPixel = edgeKm / edgePixels || 0;\n\n const markerPixels = (markers ?? []).map((m) => ({ marker: m, p: projection.project(m) }));\n\n return { polyline, startPixel, endPixel, currentPixel, kmPerPixel, markerPixels };\n }, [allPoints, points, current, markers, width, height, padding]);\n\n // ── Leaflet tile mode ────────────────────────────────────────────────────\n if (tileUrl) {\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n <Suspense fallback={<div className={styles.loading}>Carregando mapa…</div>}>\n <LeafletTrajectory\n points={points}\n current={current}\n tileUrl={tileUrl}\n tileAttribution={tileAttribution}\n strokeColor={strokeColor}\n />\n </Suspense>\n </div>\n );\n }\n\n // ── Tile-free SVG mode ─────────────────────────────────────────────────────\n const scale =\n showScale && svg && svg.kmPerPixel > 0\n ? pickScaleKm(svg.kmPerPixel, Math.min(120, width * 0.35))\n : null;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n {!svg ? (\n <div className={styles.empty}>Sem pontos de trajetória ainda.</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {showGrid && (\n <g className={styles.grid}>\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`h${f}`}\n x1={0}\n y1={height * f}\n x2={width}\n y2={height * f}\n />\n ))}\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`v${f}`}\n x1={width * f}\n y1={0}\n x2={width * f}\n y2={height}\n />\n ))}\n </g>\n )}\n\n {svg.polyline && (\n <polyline\n className={styles.path}\n points={svg.polyline}\n fill=\"none\"\n stroke={strokeColor}\n data-testid=\"trajectory-path\"\n />\n )}\n\n {svg.startPixel && (\n <circle\n className={styles.start}\n cx={svg.startPixel.x}\n cy={svg.startPixel.y}\n r={5}\n />\n )}\n {svg.endPixel && !current && (\n <circle\n className={styles.end}\n cx={svg.endPixel.x}\n cy={svg.endPixel.y}\n r={5}\n />\n )}\n {svg.currentPixel && (\n <>\n <circle\n className={styles.pulse}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={10}\n />\n <circle\n className={styles.current}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={5}\n />\n </>\n )}\n\n {svg.markerPixels.map(({ marker, p }, i) => {\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? i}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n style={marker.color ? { fill: marker.color } : undefined}\n data-marker-id={marker.id ?? i}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, i) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, i);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n\n {scale && (\n <g\n className={styles.scale}\n transform={`translate(${padding}, ${height - padding})`}\n >\n <line x1={0} y1={0} x2={scale.pixels} y2={0} />\n <line x1={0} y1={-4} x2={0} y2={4} />\n <line x1={scale.pixels} y1={-4} x2={scale.pixels} y2={4} />\n <text x={scale.pixels / 2} y={-8} textAnchor=\"middle\">\n {formatScale(scale.km)}\n </text>\n </g>\n )}\n </svg>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrajectoryMap.cjs","names":[],"sources":["../../src/geo/TrajectoryMap.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the SVG plot (points,\n * current, height, padding, showGrid, showScale, strokeColor, label), the optional\n * leaflet tile layer (tileUrl, tileAttribution) and the overlay (markers,\n * onMarkerClick). The body projects the path, fits the bounds and hands the same\n * bounds to the tile layer when there is one.\n */\nimport { lazy, Suspense, useEffect, useMemo, useRef, useState, type HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { boundingBox, expandBounds } from \"./bounds\";\nimport { haversineKm } from \"./distance\";\nimport { fitProjection, type PixelPoint } from \"./projection\";\nimport type { Coordinate, GeoMarker } from \"./types\";\nimport styles from \"./TrajectoryMap.module.css\";\n\n// Leaflet layer is loaded on demand — only when a `tileUrl` is supplied, so\n// apps that stick to the tile-free SVG never pull leaflet into their bundle.\nconst LeafletTrajectory = lazy(() =>\n import(\"./leaflet-map\").then((m) => ({ default: m.LeafletTrajectory })),\n);\n\nexport interface TrajectoryMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Ordered coordinates forming the trajectory. */\n points: readonly Coordinate[];\n /** Optional \"current position\" marker, drawn on top of the path. */\n current?: Coordinate | null;\n /** Viewport height in pixels. Default: `320`. */\n height?: number;\n /** Inner padding in pixels kept clear on every edge. Default: `24`. */\n padding?: number;\n /** Draw a light reference grid behind the path (SVG mode). Default: `true`. */\n showGrid?: boolean;\n /** Draw a distance scale bar (SVG mode). Default: `true`. */\n showScale?: boolean;\n /** Path stroke color. Default: the primary token. */\n strokeColor?: string;\n /**\n * Tile URL template (`{z}/{x}/{y}`) of a map server **you host**. When set,\n * the map upgrades to a real Leaflet tile layer (requires `leaflet` to be\n * installed). Omit for the zero-dependency, zero-network SVG plot.\n */\n tileUrl?: string;\n /** Attribution text shown over the tile layer. */\n tileAttribution?: string;\n /** Accessible label for the map region. Default: `\"Trajetória\"`. */\n label?: string;\n /** Point markers to overlay (e.g. stops, POIs). Included in the auto-fit. */\n markers?: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\nconst NICE_KM_STEPS = [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000] as const;\n\n/** Pick the largest \"nice\" distance whose bar fits within `maxPixels`. */\nfunction pickScaleKm(kmPerPixel: number, maxPixels: number): { km: number; pixels: number } {\n const maxKm = kmPerPixel * maxPixels;\n let chosen: number = NICE_KM_STEPS[0];\n for (const step of NICE_KM_STEPS) {\n if (step <= maxKm) chosen = step;\n }\n return { km: chosen, pixels: chosen / kmPerPixel };\n}\n\n/** Format a distance in km for the scale bar (meters below 1 km). */\nfunction formatScale(km: number): string {\n return km < 1 ? `${Math.round(km * 1000)} m` : `${km % 1 === 0 ? km : km.toFixed(1)} km`;\n}\n\n/**\n * Plot a GPS trajectory. By default it renders a **tile-free SVG** map (Web\n * Mercator projection, auto-fit to the points, optional grid + scale bar) —\n * 100% self-contained, no external tiles or paid API. Pass `tileUrl` pointing\n * at a map server **you host** to upgrade to a real Leaflet tile layer.\n *\n * @example\n * // Zero-dependency SVG plot\n * <TrajectoryMap points={points} current={lastPoint} height={360} />\n *\n * @example\n * // Real tiles from your own server (requires `leaflet` installed)\n * <TrajectoryMap points={points} tileUrl=\"https://tiles.internal/{z}/{x}/{y}.png\" />\n */\nexport function TrajectoryMap({\n points,\n current,\n height = 320,\n padding = 24,\n showGrid = true,\n showScale = true,\n strokeColor,\n tileUrl,\n tileAttribution,\n label = \"Trajetória\",\n markers,\n onMarkerClick,\n className,\n style,\n ...rest\n}: TrajectoryMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const allPoints = useMemo<Coordinate[]>(\n () => [...points, ...(current ? [current] : []), ...(markers ?? [])],\n [points, current, markers],\n );\n\n const svg = useMemo(() => {\n const box = boundingBox(allPoints);\n if (!box) return null;\n\n const projection = fitProjection(expandBounds(box, 0.12), width, height, { padding });\n const pixels: PixelPoint[] = points.map((p) => projection.project(p));\n const polyline = pixels.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(\" \");\n\n const startPixel = pixels[0] ?? null;\n const endPixel = pixels.length > 0 ? pixels[pixels.length - 1] : null;\n const currentPixel = current ? projection.project(current) : null;\n\n // Distance-per-pixel at the map center, for the scale bar: measure the\n // center → east-edge span in both km (haversine) and pixels (projected).\n const centerCoord = {\n latitude: (box.minLatitude + box.maxLatitude) / 2,\n longitude: (box.minLongitude + box.maxLongitude) / 2,\n };\n const center = projection.project(centerCoord);\n const eastRef = projection.project({ ...centerCoord, longitude: box.maxLongitude });\n const edgeKm = haversineKm(centerCoord, { ...centerCoord, longitude: box.maxLongitude });\n const edgePixels = Math.abs(eastRef.x - center.x) || 1;\n const kmPerPixel = edgeKm / edgePixels || 0;\n\n const markerPixels = (markers ?? []).map((m) => ({ marker: m, p: projection.project(m) }));\n\n return { polyline, startPixel, endPixel, currentPixel, kmPerPixel, markerPixels };\n }, [allPoints, points, current, markers, width, height, padding]);\n\n // ── Leaflet tile mode ────────────────────────────────────────────────────\n if (tileUrl) {\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n <Suspense fallback={<div className={styles.loading}>Carregando mapa…</div>}>\n <LeafletTrajectory\n points={points}\n current={current}\n tileUrl={tileUrl}\n tileAttribution={tileAttribution}\n strokeColor={strokeColor}\n />\n </Suspense>\n </div>\n );\n }\n\n // ── Tile-free SVG mode ─────────────────────────────────────────────────────\n const scale =\n showScale && svg && svg.kmPerPixel > 0\n ? pickScaleKm(svg.kmPerPixel, Math.min(120, width * 0.35))\n : null;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n {!svg ? (\n <div className={styles.empty}>Sem pontos de trajetória ainda.</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {showGrid && (\n <g className={styles.grid}>\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`h${f}`}\n x1={0}\n y1={height * f}\n x2={width}\n y2={height * f}\n />\n ))}\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`v${f}`}\n x1={width * f}\n y1={0}\n x2={width * f}\n y2={height}\n />\n ))}\n </g>\n )}\n\n {svg.polyline && (\n <polyline\n className={styles.path}\n points={svg.polyline}\n fill=\"none\"\n stroke={strokeColor}\n data-testid=\"trajectory-path\"\n />\n )}\n\n {svg.startPixel && (\n <circle\n className={styles.start}\n cx={svg.startPixel.x}\n cy={svg.startPixel.y}\n r={5}\n />\n )}\n {svg.endPixel && !current && (\n <circle\n className={styles.end}\n cx={svg.endPixel.x}\n cy={svg.endPixel.y}\n r={5}\n />\n )}\n {svg.currentPixel && (\n <>\n <circle\n className={styles.pulse}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={10}\n />\n <circle\n className={styles.current}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={5}\n />\n </>\n )}\n\n {svg.markerPixels.map(({ marker, p }, i) => {\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? i}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n style={marker.color ? { fill: marker.color } : undefined}\n data-marker-id={marker.id ?? i}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, i) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, i);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n\n {scale && (\n <g\n className={styles.scale}\n transform={`translate(${padding}, ${height - padding})`}\n >\n <line x1={0} y1={0} x2={scale.pixels} y2={0} />\n <line x1={0} y1={-4} x2={0} y2={4} />\n <line x1={scale.pixels} y1={-4} x2={scale.pixels} y2={4} />\n <text x={scale.pixels / 2} y={-8} textAnchor=\"middle\">\n {formatScale(scale.km)}\n </text>\n </g>\n )}\n </svg>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TrajectoryMap.js","names":[],"sources":["../../src/geo/TrajectoryMap.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the SVG plot (points,\n * current, height, padding, showGrid, showScale, strokeColor, label), the optional\n * leaflet tile layer (tileUrl, tileAttribution) and the overlay (markers,\n * onMarkerClick). The body projects the path, fits the bounds and hands the same\n * bounds to the tile layer when there is one.\n */\nimport { lazy, Suspense, useEffect, useMemo, useRef, useState, type HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { boundingBox, expandBounds } from \"./bounds\";\nimport { haversineKm } from \"./distance\";\nimport { fitProjection, type PixelPoint } from \"./projection\";\nimport type { Coordinate, GeoMarker } from \"./types\";\nimport styles from \"./TrajectoryMap.module.css\";\n\n// Leaflet layer is loaded on demand — only when a `tileUrl` is supplied, so\n// apps that stick to the tile-free SVG never pull leaflet into their bundle.\nconst LeafletTrajectory = lazy(() =>\n import(\"./leaflet-map\").then((m) => ({ default: m.LeafletTrajectory })),\n);\n\nexport interface TrajectoryMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Ordered coordinates forming the trajectory. */\n points: readonly Coordinate[];\n /** Optional \"current position\" marker, drawn on top of the path. */\n current?: Coordinate | null;\n /** Viewport height in pixels. Default: `320`. */\n height?: number;\n /** Inner padding in pixels kept clear on every edge. Default: `24`. */\n padding?: number;\n /** Draw a light reference grid behind the path (SVG mode). Default: `true`. */\n showGrid?: boolean;\n /** Draw a distance scale bar (SVG mode). Default: `true`. */\n showScale?: boolean;\n /** Path stroke color. Default: the primary token. */\n strokeColor?: string;\n /**\n * Tile URL template (`{z}/{x}/{y}`) of a map server **you host**. When set,\n * the map upgrades to a real Leaflet tile layer (requires `leaflet` to be\n * installed). Omit for the zero-dependency, zero-network SVG plot.\n */\n tileUrl?: string;\n /** Attribution text shown over the tile layer. */\n tileAttribution?: string;\n /** Accessible label for the map region. Default: `\"Trajetória\"`. */\n label?: string;\n /** Point markers to overlay (e.g. stops, POIs). Included in the auto-fit. */\n markers?: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\nconst NICE_KM_STEPS = [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000] as const;\n\n/** Pick the largest \"nice\" distance whose bar fits within `maxPixels`. */\nfunction pickScaleKm(kmPerPixel: number, maxPixels: number): { km: number; pixels: number } {\n const maxKm = kmPerPixel * maxPixels;\n let chosen: number = NICE_KM_STEPS[0];\n for (const step of NICE_KM_STEPS) {\n if (step <= maxKm) chosen = step;\n }\n return { km: chosen, pixels: chosen / kmPerPixel };\n}\n\n/** Format a distance in km for the scale bar (meters below 1 km). */\nfunction formatScale(km: number): string {\n return km < 1 ? `${Math.round(km * 1000)} m` : `${km % 1 === 0 ? km : km.toFixed(1)} km`;\n}\n\n/**\n * Plot a GPS trajectory. By default it renders a **tile-free SVG** map (Web\n * Mercator projection, auto-fit to the points, optional grid + scale bar) —\n * 100% self-contained, no external tiles or paid API. Pass `tileUrl` pointing\n * at a map server **you host** to upgrade to a real Leaflet tile layer.\n *\n * @example\n * // Zero-dependency SVG plot\n * <TrajectoryMap points={points} current={lastPoint} height={360} />\n *\n * @example\n * // Real tiles from your own server (requires `leaflet` installed)\n * <TrajectoryMap points={points} tileUrl=\"https://tiles.internal/{z}/{x}/{y}.png\" />\n */\nexport function TrajectoryMap({\n points,\n current,\n height = 320,\n padding = 24,\n showGrid = true,\n showScale = true,\n strokeColor,\n tileUrl,\n tileAttribution,\n label = \"Trajetória\",\n markers,\n onMarkerClick,\n className,\n style,\n ...rest\n}: TrajectoryMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const allPoints = useMemo<Coordinate[]>(\n () => [...points, ...(current ? [current] : []), ...(markers ?? [])],\n [points, current, markers],\n );\n\n const svg = useMemo(() => {\n const box = boundingBox(allPoints);\n if (!box) return null;\n\n const projection = fitProjection(expandBounds(box, 0.12), width, height, { padding });\n const pixels: PixelPoint[] = points.map((p) => projection.project(p));\n const polyline = pixels.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(\" \");\n\n const startPixel = pixels[0] ?? null;\n const endPixel = pixels.length > 0 ? pixels[pixels.length - 1] : null;\n const currentPixel = current ? projection.project(current) : null;\n\n // Distance-per-pixel at the map center, for the scale bar: measure the\n // center → east-edge span in both km (haversine) and pixels (projected).\n const centerCoord = {\n latitude: (box.minLatitude + box.maxLatitude) / 2,\n longitude: (box.minLongitude + box.maxLongitude) / 2,\n };\n const center = projection.project(centerCoord);\n const eastRef = projection.project({ ...centerCoord, longitude: box.maxLongitude });\n const edgeKm = haversineKm(centerCoord, { ...centerCoord, longitude: box.maxLongitude });\n const edgePixels = Math.abs(eastRef.x - center.x) || 1;\n const kmPerPixel = edgeKm / edgePixels || 0;\n\n const markerPixels = (markers ?? []).map((m) => ({ marker: m, p: projection.project(m) }));\n\n return { polyline, startPixel, endPixel, currentPixel, kmPerPixel, markerPixels };\n }, [allPoints, points, current, markers, width, height, padding]);\n\n // ── Leaflet tile mode ────────────────────────────────────────────────────\n if (tileUrl) {\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n <Suspense fallback={<div className={styles.loading}>Carregando mapa…</div>}>\n <LeafletTrajectory\n points={points}\n current={current}\n tileUrl={tileUrl}\n tileAttribution={tileAttribution}\n strokeColor={strokeColor}\n />\n </Suspense>\n </div>\n );\n }\n\n // ── Tile-free SVG mode ─────────────────────────────────────────────────────\n const scale =\n showScale && svg && svg.kmPerPixel > 0\n ? pickScaleKm(svg.kmPerPixel, Math.min(120, width * 0.35))\n : null;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n {!svg ? (\n <div className={styles.empty}>Sem pontos de trajetória ainda.</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {showGrid && (\n <g className={styles.grid}>\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`h${f}`}\n x1={0}\n y1={height * f}\n x2={width}\n y2={height * f}\n />\n ))}\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`v${f}`}\n x1={width * f}\n y1={0}\n x2={width * f}\n y2={height}\n />\n ))}\n </g>\n )}\n\n {svg.polyline && (\n <polyline\n className={styles.path}\n points={svg.polyline}\n fill=\"none\"\n stroke={strokeColor}\n data-testid=\"trajectory-path\"\n />\n )}\n\n {svg.startPixel && (\n <circle\n className={styles.start}\n cx={svg.startPixel.x}\n cy={svg.startPixel.y}\n r={5}\n />\n )}\n {svg.endPixel && !current && (\n <circle\n className={styles.end}\n cx={svg.endPixel.x}\n cy={svg.endPixel.y}\n r={5}\n />\n )}\n {svg.currentPixel && (\n <>\n <circle\n className={styles.pulse}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={10}\n />\n <circle\n className={styles.current}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={5}\n />\n </>\n )}\n\n {svg.markerPixels.map(({ marker, p }, i) => {\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? i}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n style={marker.color ? { fill: marker.color } : undefined}\n data-marker-id={marker.id ?? i}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, i) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, i);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n\n {scale && (\n <g\n className={styles.scale}\n transform={`translate(${padding}, ${height - padding})`}\n >\n <line x1={0} y1={0} x2={scale.pixels} y2={0} />\n <line x1={0} y1={-4} x2={0} y2={4} />\n <line x1={scale.pixels} y1={-4} x2={scale.pixels} y2={4} />\n <text x={scale.pixels / 2} y={-8} textAnchor=\"middle\">\n {formatScale(scale.km)}\n </text>\n </g>\n )}\n </svg>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrajectoryMap.js","names":[],"sources":["../../src/geo/TrajectoryMap.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the SVG plot (points,\n * current, height, padding, showGrid, showScale, strokeColor, label), the optional\n * leaflet tile layer (tileUrl, tileAttribution) and the overlay (markers,\n * onMarkerClick). The body projects the path, fits the bounds and hands the same\n * bounds to the tile layer when there is one.\n */\nimport { lazy, Suspense, useEffect, useMemo, useRef, useState, type HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { boundingBox, expandBounds } from \"./bounds\";\nimport { haversineKm } from \"./distance\";\nimport { fitProjection, type PixelPoint } from \"./projection\";\nimport type { Coordinate, GeoMarker } from \"./types\";\nimport styles from \"./TrajectoryMap.module.css\";\n\n// Leaflet layer is loaded on demand — only when a `tileUrl` is supplied, so\n// apps that stick to the tile-free SVG never pull leaflet into their bundle.\nconst LeafletTrajectory = lazy(() =>\n import(\"./leaflet-map\").then((m) => ({ default: m.LeafletTrajectory })),\n);\n\nexport interface TrajectoryMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Ordered coordinates forming the trajectory. */\n points: readonly Coordinate[];\n /** Optional \"current position\" marker, drawn on top of the path. */\n current?: Coordinate | null;\n /** Viewport height in pixels. Default: `320`. */\n height?: number;\n /** Inner padding in pixels kept clear on every edge. Default: `24`. */\n padding?: number;\n /** Draw a light reference grid behind the path (SVG mode). Default: `true`. */\n showGrid?: boolean;\n /** Draw a distance scale bar (SVG mode). Default: `true`. */\n showScale?: boolean;\n /** Path stroke color. Default: the primary token. */\n strokeColor?: string;\n /**\n * Tile URL template (`{z}/{x}/{y}`) of a map server **you host**. When set,\n * the map upgrades to a real Leaflet tile layer (requires `leaflet` to be\n * installed). Omit for the zero-dependency, zero-network SVG plot.\n */\n tileUrl?: string;\n /** Attribution text shown over the tile layer. */\n tileAttribution?: string;\n /** Accessible label for the map region. Default: `\"Trajetória\"`. */\n label?: string;\n /** Point markers to overlay (e.g. stops, POIs). Included in the auto-fit. */\n markers?: readonly GeoMarker[];\n /** Fired when a marker is clicked. */\n onMarkerClick?: (marker: GeoMarker, index: number) => void;\n}\n\nconst NICE_KM_STEPS = [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000] as const;\n\n/** Pick the largest \"nice\" distance whose bar fits within `maxPixels`. */\nfunction pickScaleKm(kmPerPixel: number, maxPixels: number): { km: number; pixels: number } {\n const maxKm = kmPerPixel * maxPixels;\n let chosen: number = NICE_KM_STEPS[0];\n for (const step of NICE_KM_STEPS) {\n if (step <= maxKm) chosen = step;\n }\n return { km: chosen, pixels: chosen / kmPerPixel };\n}\n\n/** Format a distance in km for the scale bar (meters below 1 km). */\nfunction formatScale(km: number): string {\n return km < 1 ? `${Math.round(km * 1000)} m` : `${km % 1 === 0 ? km : km.toFixed(1)} km`;\n}\n\n/**\n * Plot a GPS trajectory. By default it renders a **tile-free SVG** map (Web\n * Mercator projection, auto-fit to the points, optional grid + scale bar) —\n * 100% self-contained, no external tiles or paid API. Pass `tileUrl` pointing\n * at a map server **you host** to upgrade to a real Leaflet tile layer.\n *\n * @example\n * // Zero-dependency SVG plot\n * <TrajectoryMap points={points} current={lastPoint} height={360} />\n *\n * @example\n * // Real tiles from your own server (requires `leaflet` installed)\n * <TrajectoryMap points={points} tileUrl=\"https://tiles.internal/{z}/{x}/{y}.png\" />\n */\nexport function TrajectoryMap({\n points,\n current,\n height = 320,\n padding = 24,\n showGrid = true,\n showScale = true,\n strokeColor,\n tileUrl,\n tileAttribution,\n label = \"Trajetória\",\n markers,\n onMarkerClick,\n className,\n style,\n ...rest\n}: TrajectoryMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const allPoints = useMemo<Coordinate[]>(\n () => [...points, ...(current ? [current] : []), ...(markers ?? [])],\n [points, current, markers],\n );\n\n const svg = useMemo(() => {\n const box = boundingBox(allPoints);\n if (!box) return null;\n\n const projection = fitProjection(expandBounds(box, 0.12), width, height, { padding });\n const pixels: PixelPoint[] = points.map((p) => projection.project(p));\n const polyline = pixels.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(\" \");\n\n const startPixel = pixels[0] ?? null;\n const endPixel = pixels.length > 0 ? pixels[pixels.length - 1] : null;\n const currentPixel = current ? projection.project(current) : null;\n\n // Distance-per-pixel at the map center, for the scale bar: measure the\n // center → east-edge span in both km (haversine) and pixels (projected).\n const centerCoord = {\n latitude: (box.minLatitude + box.maxLatitude) / 2,\n longitude: (box.minLongitude + box.maxLongitude) / 2,\n };\n const center = projection.project(centerCoord);\n const eastRef = projection.project({ ...centerCoord, longitude: box.maxLongitude });\n const edgeKm = haversineKm(centerCoord, { ...centerCoord, longitude: box.maxLongitude });\n const edgePixels = Math.abs(eastRef.x - center.x) || 1;\n const kmPerPixel = edgeKm / edgePixels || 0;\n\n const markerPixels = (markers ?? []).map((m) => ({ marker: m, p: projection.project(m) }));\n\n return { polyline, startPixel, endPixel, currentPixel, kmPerPixel, markerPixels };\n }, [allPoints, points, current, markers, width, height, padding]);\n\n // ── Leaflet tile mode ────────────────────────────────────────────────────\n if (tileUrl) {\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n <Suspense fallback={<div className={styles.loading}>Carregando mapa…</div>}>\n <LeafletTrajectory\n points={points}\n current={current}\n tileUrl={tileUrl}\n tileAttribution={tileAttribution}\n strokeColor={strokeColor}\n />\n </Suspense>\n </div>\n );\n }\n\n // ── Tile-free SVG mode ─────────────────────────────────────────────────────\n const scale =\n showScale && svg && svg.kmPerPixel > 0\n ? pickScaleKm(svg.kmPerPixel, Math.min(120, width * 0.35))\n : null;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"img\"\n aria-label={label}\n {...rest}\n >\n {!svg ? (\n <div className={styles.empty}>Sem pontos de trajetória ainda.</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {showGrid && (\n <g className={styles.grid}>\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`h${f}`}\n x1={0}\n y1={height * f}\n x2={width}\n y2={height * f}\n />\n ))}\n {[0.25, 0.5, 0.75].map((f) => (\n <line\n key={`v${f}`}\n x1={width * f}\n y1={0}\n x2={width * f}\n y2={height}\n />\n ))}\n </g>\n )}\n\n {svg.polyline && (\n <polyline\n className={styles.path}\n points={svg.polyline}\n fill=\"none\"\n stroke={strokeColor}\n data-testid=\"trajectory-path\"\n />\n )}\n\n {svg.startPixel && (\n <circle\n className={styles.start}\n cx={svg.startPixel.x}\n cy={svg.startPixel.y}\n r={5}\n />\n )}\n {svg.endPixel && !current && (\n <circle\n className={styles.end}\n cx={svg.endPixel.x}\n cy={svg.endPixel.y}\n r={5}\n />\n )}\n {svg.currentPixel && (\n <>\n <circle\n className={styles.pulse}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={10}\n />\n <circle\n className={styles.current}\n cx={svg.currentPixel.x}\n cy={svg.currentPixel.y}\n r={5}\n />\n </>\n )}\n\n {svg.markerPixels.map(({ marker, p }, i) => {\n const interactive = Boolean(onMarkerClick);\n return (\n <circle\n key={marker.id ?? i}\n className={styles.marker}\n cx={p.x}\n cy={p.y}\n r={marker.radius ?? 6}\n style={marker.color ? { fill: marker.color } : undefined}\n data-marker-id={marker.id ?? i}\n tabIndex={interactive ? 0 : undefined}\n role={interactive ? \"button\" : undefined}\n aria-label={marker.label}\n onClick={interactive ? () => onMarkerClick?.(marker, i) : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onMarkerClick?.(marker, i);\n }\n }\n : undefined\n }\n >\n {marker.label ? <title>{marker.label}</title> : null}\n </circle>\n );\n })}\n\n {scale && (\n <g\n className={styles.scale}\n transform={`translate(${padding}, ${height - padding})`}\n >\n <line x1={0} y1={0} x2={scale.pixels} y2={0} />\n <line x1={0} y1={-4} x2={0} y2={4} />\n <line x1={scale.pixels} y1={-4} x2={scale.pixels} y2={4} />\n <text x={scale.pixels / 2} y={-8} textAnchor=\"middle\">\n {formatScale(scale.km)}\n </text>\n </g>\n )}\n </svg>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"leaflet-map.cjs","names":[],"sources":["../../src/geo/leaflet-map.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { boundingBox } from \"./bounds\";\nimport type { Coordinate } from \"./types\";\n\n/**\n * Minimal structural typing for the slice of the Leaflet API this layer uses.\n * Declared locally so the SDK type-checks and builds even when `leaflet` (an\n * optional peer) is not installed — the real module is loaded at runtime.\n */\ninterface LeafletMapInstance {\n setView: (center: [number, number], zoom: number) => LeafletMapInstance;\n fitBounds: (bounds: [[number, number], [number, number]], options?: unknown) => void;\n remove: () => void;\n}\ninterface LeafletLayer {\n addTo: (map: LeafletMapInstance) => LeafletLayer;\n}\ninterface LeafletModule {\n map: (el: HTMLElement, options?: unknown) => LeafletMapInstance;\n tileLayer: (url: string, options?: unknown) => LeafletLayer;\n polyline: (latlngs: [number, number][], options?: unknown) => LeafletLayer;\n circleMarker: (latlng: [number, number], options?: unknown) => LeafletLayer;\n}\n\nexport interface LeafletTrajectoryProps {\n points: readonly Coordinate[];\n current?: Coordinate | null;\n tileUrl: string;\n tileAttribution?: string;\n strokeColor?: string;\n}\n\n/**\n * Leaflet-backed tile layer for {@link TrajectoryMap}. Loaded lazily and only\n * when a `tileUrl` is provided. Requires `leaflet` to be installed by the app\n * (optional peer dependency) plus its stylesheet imported once:\n * `import \"leaflet/dist/leaflet.css\"`.\n */\nexport function LeafletTrajectory({\n points,\n current,\n tileUrl,\n tileAttribution,\n strokeColor,\n}: LeafletTrajectoryProps) {\n const nodeRef = useRef<HTMLDivElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let map: LeafletMapInstance | null = null;\n let cancelled = false;\n\n const specifier = \"leaflet\";\n import(/* @vite-ignore */ specifier)\n .then((mod: { default?: LeafletModule } & LeafletModule) => {\n if (cancelled || !nodeRef.current) return;\n const L: LeafletModule = mod.default ?? mod;\n\n map = L.map(nodeRef.current, { attributionControl: true });\n L.tileLayer(tileUrl, {\n attribution: tileAttribution ?? \"\",\n maxZoom: 19,\n }).addTo(map);\n\n const latlngs = points.map((p): [number, number] => [p.latitude, p.longitude]);\n if (latlngs.length > 0) {\n L.polyline(latlngs, {\n color: strokeColor ?? \"#2563eb\",\n weight: 4,\n }).addTo(map);\n L.circleMarker(latlngs[0], { radius: 6, color: \"#16a34a\" }).addTo(map);\n }\n\n const marker = current ?? points[points.length - 1];\n if (marker) {\n L.circleMarker([marker.latitude, marker.longitude], {\n radius: 6,\n color: strokeColor ?? \"#2563eb\",\n }).addTo(map);\n }\n\n const box = boundingBox(current ? [...points, current] : [...points]);\n if (box) {\n map.fitBounds(\n [\n [box.minLatitude, box.minLongitude],\n [box.maxLatitude, box.maxLongitude],\n ],\n { padding: [24, 24] },\n );\n } else {\n map.setView([0, 0], 2);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n \"Leaflet não encontrado. Instale `leaflet` para usar tiles, ou remova `tileUrl` para o plot SVG.\",\n );\n }\n });\n\n return () => {\n cancelled = true;\n map?.remove();\n };\n }, [points, current, tileUrl, tileAttribution, strokeColor]);\n\n if (error) {\n return (\n <div style={{ padding: 16, fontSize: 13, color: \"var(--tempest-danger-fg)\" }}>\n {error}\n </div>\n );\n }\n\n return <div ref={nodeRef} style={{ width: \"100%\", height: \"100%\" }} />;\n}\n"],"mappings":"sFAsCA,SAAgB,EAAkB,CAC9B,SACA,UACA,UACA,kBACA,eACuB,CACvB,IAAM,GAAA,EAAA,EAAA,OAAA,CAAiC,IAAI,EACrC,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAoC,IAAI,EAsEtD,OApEA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAiC,KACjC,EAAY,GAoDhB,OAjDA,OAA0B,UAAU,CAC/B,KAAM,GAAqD,CACxD,GAAI,GAAa,CAAC,EAAQ,QAAS,OACnC,IAAM,EAAmB,EAAI,SAAW,EAExC,EAAM,EAAE,IAAI,EAAQ,QAAS,CAAE,mBAAoB,EAAK,CAAC,EACzD,EAAE,UAAU,EAAS,CACjB,YAAa,GAAmB,GAChC,QAAS,EACb,CAAC,CAAC,CAAC,MAAM,CAAG,EAEZ,IAAM,EAAU,EAAO,IAAK,GAAwB,CAAC,EAAE,SAAU,EAAE,SAAS,CAAC,EACzE,EAAQ,OAAS,IACjB,EAAE,SAAS,EAAS,CAChB,MAAO,GAAe,UACtB,OAAQ,CACZ,CAAC,CAAC,CAAC,MAAM,CAAG,EACZ,EAAE,aAAa,EAAQ,GAAI,CAAE,OAAQ,EAAG,MAAO,SAAU,CAAC,CAAC,CAAC,MAAM,CAAG,GAGzE,IAAM,EAAS,GAAW,EAAO,EAAO,OAAS,GAC7C,GACA,EAAE,aAAa,CAAC,EAAO,SAAU,EAAO,SAAS,EAAG,CAChD,OAAQ,EACR,MAAO,GAAe,SAC1B,CAAC,CAAC,CAAC,MAAM,CAAG,EAGhB,IAAM,EAAM,EAAA,YAAY,EAAU,CAAC,GAAG,EAAQ,CAAO,EAAI,CAAC,GAAG,CAAM,CAAC,EAChE,EACA,EAAI,UACA,CACI,CAAC,EAAI,YAAa,EAAI,YAAY,EAClC,CAAC,EAAI,YAAa,EAAI,YAAY,CACtC,EACA,CAAE,QAAS,CAAC,GAAI,EAAE,CAAE,CACxB,EAEA,EAAI,QAAQ,CAAC,EAAG,CAAC,EAAG,CAAC,CAE7B,CAAC,CAAC,CACD,UAAY,CACJ,GACD,EACI,iGACJ,CAER,CAAC,MAEQ,CACT,EAAY,GACZ,GAAK,OAAO,CAChB,CACJ,EAAG,CAAC,EAAQ,EAAS,EAAS,EAAiB,CAAW,CAAC,EAEvD,GAEI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAO,CAAE,QAAS,GAAI,SAAU,GAAI,MAAO,0BAA2B,WACtE,CACA,CAAA,GAIN,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,CAAI,CAAA,CACzE"}
1
+ {"version":3,"file":"leaflet-map.cjs","names":[],"sources":["../../src/geo/leaflet-map.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { boundingBox } from \"./bounds\";\nimport type { Coordinate } from \"./types\";\n\n/**\n * Minimal structural typing for the slice of the Leaflet API this layer uses.\n * Declared locally so the SDK type-checks and builds even when `leaflet` (an\n * optional peer) is not installed — the real module is loaded at runtime.\n */\ninterface LeafletMapInstance {\n setView: (center: [number, number], zoom: number) => LeafletMapInstance;\n fitBounds: (bounds: [[number, number], [number, number]], options?: unknown) => void;\n remove: () => void;\n}\ninterface LeafletLayer {\n addTo: (map: LeafletMapInstance) => LeafletLayer;\n}\ninterface LeafletModule {\n map: (el: HTMLElement, options?: unknown) => LeafletMapInstance;\n tileLayer: (url: string, options?: unknown) => LeafletLayer;\n polyline: (latlngs: [number, number][], options?: unknown) => LeafletLayer;\n circleMarker: (latlng: [number, number], options?: unknown) => LeafletLayer;\n}\n\nexport interface LeafletTrajectoryProps {\n points: readonly Coordinate[];\n current?: Coordinate | null;\n tileUrl: string;\n tileAttribution?: string;\n strokeColor?: string;\n}\n\n/**\n * Leaflet-backed tile layer for {@link TrajectoryMap}. Loaded lazily and only\n * when a `tileUrl` is provided. Requires `leaflet` to be installed by the app\n * (optional peer dependency) plus its stylesheet imported once:\n * `import \"leaflet/dist/leaflet.css\"`.\n */\nexport function LeafletTrajectory({\n points,\n current,\n tileUrl,\n tileAttribution,\n strokeColor,\n}: LeafletTrajectoryProps) {\n const nodeRef = useRef<HTMLDivElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let map: LeafletMapInstance | null = null;\n let cancelled = false;\n\n const specifier = \"leaflet\";\n import(/* @vite-ignore */ specifier)\n .then((mod: { default?: LeafletModule } & LeafletModule) => {\n if (cancelled || !nodeRef.current) return;\n const L: LeafletModule = mod.default ?? mod;\n\n map = L.map(nodeRef.current, { attributionControl: true });\n L.tileLayer(tileUrl, {\n attribution: tileAttribution ?? \"\",\n maxZoom: 19,\n }).addTo(map);\n\n const latlngs = points.map((p): [number, number] => [p.latitude, p.longitude]);\n if (latlngs.length > 0) {\n L.polyline(latlngs, {\n color: strokeColor ?? \"#2563eb\",\n weight: 4,\n }).addTo(map);\n L.circleMarker(latlngs[0], { radius: 6, color: \"#16a34a\" }).addTo(map);\n }\n\n const marker = current ?? points[points.length - 1];\n if (marker) {\n L.circleMarker([marker.latitude, marker.longitude], {\n radius: 6,\n color: strokeColor ?? \"#2563eb\",\n }).addTo(map);\n }\n\n const box = boundingBox(current ? [...points, current] : [...points]);\n if (box) {\n map.fitBounds(\n [\n [box.minLatitude, box.minLongitude],\n [box.maxLatitude, box.maxLongitude],\n ],\n { padding: [24, 24] },\n );\n } else {\n map.setView([0, 0], 2);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n \"Leaflet não encontrado. Instale `leaflet` para usar tiles, ou remova `tileUrl` para o plot SVG.\",\n );\n }\n });\n\n return () => {\n cancelled = true;\n map?.remove();\n };\n }, [points, current, tileUrl, tileAttribution, strokeColor]);\n\n if (error) {\n return (\n <div style={{ padding: 16, fontSize: 13, color: \"var(--tempest-danger-fg)\" }}>\n {error}\n </div>\n );\n }\n\n return <div ref={nodeRef} style={{ width: \"100%\", height: \"100%\" }} />;\n}\n"],"mappings":"sFAsCA,SAAgB,EAAkB,CAC9B,SACA,UACA,UACA,kBACA,eACuB,CACvB,IAAM,GAAA,EAAU,EAAA,OAAA,CAAuB,IAAI,EACrC,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAwB,IAAI,EAsEtD,OApEA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAiC,KACjC,EAAY,GAoDhB,OAjDA,OAA0B,UAAU,CAC/B,KAAM,GAAqD,CACxD,GAAI,GAAa,CAAC,EAAQ,QAAS,OACnC,IAAM,EAAmB,EAAI,SAAW,EAExC,EAAM,EAAE,IAAI,EAAQ,QAAS,CAAE,mBAAoB,EAAK,CAAC,EACzD,EAAE,UAAU,EAAS,CACjB,YAAa,GAAmB,GAChC,QAAS,EACb,CAAC,CAAC,CAAC,MAAM,CAAG,EAEZ,IAAM,EAAU,EAAO,IAAK,GAAwB,CAAC,EAAE,SAAU,EAAE,SAAS,CAAC,EACzE,EAAQ,OAAS,IACjB,EAAE,SAAS,EAAS,CAChB,MAAO,GAAe,UACtB,OAAQ,CACZ,CAAC,CAAC,CAAC,MAAM,CAAG,EACZ,EAAE,aAAa,EAAQ,GAAI,CAAE,OAAQ,EAAG,MAAO,SAAU,CAAC,CAAC,CAAC,MAAM,CAAG,GAGzE,IAAM,EAAS,GAAW,EAAO,EAAO,OAAS,GAC7C,GACA,EAAE,aAAa,CAAC,EAAO,SAAU,EAAO,SAAS,EAAG,CAChD,OAAQ,EACR,MAAO,GAAe,SAC1B,CAAC,CAAC,CAAC,MAAM,CAAG,EAGhB,IAAM,EAAM,EAAA,YAAY,EAAU,CAAC,GAAG,EAAQ,CAAO,EAAI,CAAC,GAAG,CAAM,CAAC,EAChE,EACA,EAAI,UACA,CACI,CAAC,EAAI,YAAa,EAAI,YAAY,EAClC,CAAC,EAAI,YAAa,EAAI,YAAY,CACtC,EACA,CAAE,QAAS,CAAC,GAAI,EAAE,CAAE,CACxB,EAEA,EAAI,QAAQ,CAAC,EAAG,CAAC,EAAG,CAAC,CAE7B,CAAC,CAAC,CACD,UAAY,CACJ,GACD,EACI,iGACJ,CAER,CAAC,MAEQ,CACT,EAAY,GACZ,GAAK,OAAO,CAChB,CACJ,EAAG,CAAC,EAAQ,EAAS,EAAS,EAAiB,CAAW,CAAC,EAEvD,GAEI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAO,CAAE,QAAS,GAAI,SAAU,GAAI,MAAO,0BAA2B,EACtE,SAAA,CACA,CAAA,GAIN,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,CAAI,CAAA,CACzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"leaflet-map.js","names":[],"sources":["../../src/geo/leaflet-map.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { boundingBox } from \"./bounds\";\nimport type { Coordinate } from \"./types\";\n\n/**\n * Minimal structural typing for the slice of the Leaflet API this layer uses.\n * Declared locally so the SDK type-checks and builds even when `leaflet` (an\n * optional peer) is not installed — the real module is loaded at runtime.\n */\ninterface LeafletMapInstance {\n setView: (center: [number, number], zoom: number) => LeafletMapInstance;\n fitBounds: (bounds: [[number, number], [number, number]], options?: unknown) => void;\n remove: () => void;\n}\ninterface LeafletLayer {\n addTo: (map: LeafletMapInstance) => LeafletLayer;\n}\ninterface LeafletModule {\n map: (el: HTMLElement, options?: unknown) => LeafletMapInstance;\n tileLayer: (url: string, options?: unknown) => LeafletLayer;\n polyline: (latlngs: [number, number][], options?: unknown) => LeafletLayer;\n circleMarker: (latlng: [number, number], options?: unknown) => LeafletLayer;\n}\n\nexport interface LeafletTrajectoryProps {\n points: readonly Coordinate[];\n current?: Coordinate | null;\n tileUrl: string;\n tileAttribution?: string;\n strokeColor?: string;\n}\n\n/**\n * Leaflet-backed tile layer for {@link TrajectoryMap}. Loaded lazily and only\n * when a `tileUrl` is provided. Requires `leaflet` to be installed by the app\n * (optional peer dependency) plus its stylesheet imported once:\n * `import \"leaflet/dist/leaflet.css\"`.\n */\nexport function LeafletTrajectory({\n points,\n current,\n tileUrl,\n tileAttribution,\n strokeColor,\n}: LeafletTrajectoryProps) {\n const nodeRef = useRef<HTMLDivElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let map: LeafletMapInstance | null = null;\n let cancelled = false;\n\n const specifier = \"leaflet\";\n import(/* @vite-ignore */ specifier)\n .then((mod: { default?: LeafletModule } & LeafletModule) => {\n if (cancelled || !nodeRef.current) return;\n const L: LeafletModule = mod.default ?? mod;\n\n map = L.map(nodeRef.current, { attributionControl: true });\n L.tileLayer(tileUrl, {\n attribution: tileAttribution ?? \"\",\n maxZoom: 19,\n }).addTo(map);\n\n const latlngs = points.map((p): [number, number] => [p.latitude, p.longitude]);\n if (latlngs.length > 0) {\n L.polyline(latlngs, {\n color: strokeColor ?? \"#2563eb\",\n weight: 4,\n }).addTo(map);\n L.circleMarker(latlngs[0], { radius: 6, color: \"#16a34a\" }).addTo(map);\n }\n\n const marker = current ?? points[points.length - 1];\n if (marker) {\n L.circleMarker([marker.latitude, marker.longitude], {\n radius: 6,\n color: strokeColor ?? \"#2563eb\",\n }).addTo(map);\n }\n\n const box = boundingBox(current ? [...points, current] : [...points]);\n if (box) {\n map.fitBounds(\n [\n [box.minLatitude, box.minLongitude],\n [box.maxLatitude, box.maxLongitude],\n ],\n { padding: [24, 24] },\n );\n } else {\n map.setView([0, 0], 2);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n \"Leaflet não encontrado. Instale `leaflet` para usar tiles, ou remova `tileUrl` para o plot SVG.\",\n );\n }\n });\n\n return () => {\n cancelled = true;\n map?.remove();\n };\n }, [points, current, tileUrl, tileAttribution, strokeColor]);\n\n if (error) {\n return (\n <div style={{ padding: 16, fontSize: 13, color: \"var(--tempest-danger-fg)\" }}>\n {error}\n </div>\n );\n }\n\n return <div ref={nodeRef} style={{ width: \"100%\", height: \"100%\" }} />;\n}\n"],"mappings":";;;;AAsCA,SAAgB,EAAkB,EAC9B,WACA,YACA,YACA,oBACA,kBACuB;CACvB,IAAM,IAAU,EAAuB,IAAI,GACrC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAsEtD,OApEA,QAAgB;EACZ,IAAI,IAAiC,MACjC,IAAY;EAoDhB,OAjDA;;GAA0B;CAAU,CAC/B,MAAM,MAAqD;GACxD,IAAI,KAAa,CAAC,EAAQ,SAAS;GACnC,IAAM,IAAmB,EAAI,WAAW;GAGxC,AADA,IAAM,EAAE,IAAI,EAAQ,SAAS,EAAE,oBAAoB,GAAK,CAAC,GACzD,EAAE,UAAU,GAAS;IACjB,aAAa,KAAmB;IAChC,SAAS;GACb,CAAC,CAAC,CAAC,MAAM,CAAG;GAEZ,IAAM,IAAU,EAAO,KAAK,MAAwB,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC;GAC7E,AAAI,EAAQ,SAAS,MACjB,EAAE,SAAS,GAAS;IAChB,OAAO,KAAe;IACtB,QAAQ;GACZ,CAAC,CAAC,CAAC,MAAM,CAAG,GACZ,EAAE,aAAa,EAAQ,IAAI;IAAE,QAAQ;IAAG,OAAO;GAAU,CAAC,CAAC,CAAC,MAAM,CAAG;GAGzE,IAAM,IAAS,KAAW,EAAO,EAAO,SAAS;GACjD,AAAI,KACA,EAAE,aAAa,CAAC,EAAO,UAAU,EAAO,SAAS,GAAG;IAChD,QAAQ;IACR,OAAO,KAAe;GAC1B,CAAC,CAAC,CAAC,MAAM,CAAG;GAGhB,IAAM,IAAM,EAAY,IAAU,CAAC,GAAG,GAAQ,CAAO,IAAI,CAAC,GAAG,CAAM,CAAC;GACpE,AAAI,IACA,EAAI,UACA,CACI,CAAC,EAAI,aAAa,EAAI,YAAY,GAClC,CAAC,EAAI,aAAa,EAAI,YAAY,CACtC,GACA,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CACxB,IAEA,EAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;EAE7B,CAAC,CAAC,CACD,YAAY;GACT,AAAK,KACD,EACI,iGACJ;EAER,CAAC,SAEQ;GAET,AADA,IAAY,IACZ,GAAK,OAAO;EAChB;CACJ,GAAG;EAAC;EAAQ;EAAS;EAAS;EAAiB;CAAW,CAAC,GAEvD,IAEI,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAI,UAAU;GAAI,OAAO;EAA2B;YACtE;CACA,CAAA,IAIN,kBAAC,OAAD;EAAK,KAAK;EAAS,OAAO;GAAE,OAAO;GAAQ,QAAQ;EAAO;CAAI,CAAA;AACzE"}
1
+ {"version":3,"file":"leaflet-map.js","names":[],"sources":["../../src/geo/leaflet-map.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { boundingBox } from \"./bounds\";\nimport type { Coordinate } from \"./types\";\n\n/**\n * Minimal structural typing for the slice of the Leaflet API this layer uses.\n * Declared locally so the SDK type-checks and builds even when `leaflet` (an\n * optional peer) is not installed — the real module is loaded at runtime.\n */\ninterface LeafletMapInstance {\n setView: (center: [number, number], zoom: number) => LeafletMapInstance;\n fitBounds: (bounds: [[number, number], [number, number]], options?: unknown) => void;\n remove: () => void;\n}\ninterface LeafletLayer {\n addTo: (map: LeafletMapInstance) => LeafletLayer;\n}\ninterface LeafletModule {\n map: (el: HTMLElement, options?: unknown) => LeafletMapInstance;\n tileLayer: (url: string, options?: unknown) => LeafletLayer;\n polyline: (latlngs: [number, number][], options?: unknown) => LeafletLayer;\n circleMarker: (latlng: [number, number], options?: unknown) => LeafletLayer;\n}\n\nexport interface LeafletTrajectoryProps {\n points: readonly Coordinate[];\n current?: Coordinate | null;\n tileUrl: string;\n tileAttribution?: string;\n strokeColor?: string;\n}\n\n/**\n * Leaflet-backed tile layer for {@link TrajectoryMap}. Loaded lazily and only\n * when a `tileUrl` is provided. Requires `leaflet` to be installed by the app\n * (optional peer dependency) plus its stylesheet imported once:\n * `import \"leaflet/dist/leaflet.css\"`.\n */\nexport function LeafletTrajectory({\n points,\n current,\n tileUrl,\n tileAttribution,\n strokeColor,\n}: LeafletTrajectoryProps) {\n const nodeRef = useRef<HTMLDivElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let map: LeafletMapInstance | null = null;\n let cancelled = false;\n\n const specifier = \"leaflet\";\n import(/* @vite-ignore */ specifier)\n .then((mod: { default?: LeafletModule } & LeafletModule) => {\n if (cancelled || !nodeRef.current) return;\n const L: LeafletModule = mod.default ?? mod;\n\n map = L.map(nodeRef.current, { attributionControl: true });\n L.tileLayer(tileUrl, {\n attribution: tileAttribution ?? \"\",\n maxZoom: 19,\n }).addTo(map);\n\n const latlngs = points.map((p): [number, number] => [p.latitude, p.longitude]);\n if (latlngs.length > 0) {\n L.polyline(latlngs, {\n color: strokeColor ?? \"#2563eb\",\n weight: 4,\n }).addTo(map);\n L.circleMarker(latlngs[0], { radius: 6, color: \"#16a34a\" }).addTo(map);\n }\n\n const marker = current ?? points[points.length - 1];\n if (marker) {\n L.circleMarker([marker.latitude, marker.longitude], {\n radius: 6,\n color: strokeColor ?? \"#2563eb\",\n }).addTo(map);\n }\n\n const box = boundingBox(current ? [...points, current] : [...points]);\n if (box) {\n map.fitBounds(\n [\n [box.minLatitude, box.minLongitude],\n [box.maxLatitude, box.maxLongitude],\n ],\n { padding: [24, 24] },\n );\n } else {\n map.setView([0, 0], 2);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n \"Leaflet não encontrado. Instale `leaflet` para usar tiles, ou remova `tileUrl` para o plot SVG.\",\n );\n }\n });\n\n return () => {\n cancelled = true;\n map?.remove();\n };\n }, [points, current, tileUrl, tileAttribution, strokeColor]);\n\n if (error) {\n return (\n <div style={{ padding: 16, fontSize: 13, color: \"var(--tempest-danger-fg)\" }}>\n {error}\n </div>\n );\n }\n\n return <div ref={nodeRef} style={{ width: \"100%\", height: \"100%\" }} />;\n}\n"],"mappings":";;;;AAsCA,SAAgB,EAAkB,EAC9B,WACA,YACA,YACA,oBACA,kBACuB;CACvB,IAAM,IAAU,EAAuB,IAAI,GACrC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAsEtD,OApEA,QAAgB;EACZ,IAAI,IAAiC,MACjC,IAAY;EAoDhB,OAjDA;;GAA0B;CAAU,CAC/B,MAAM,MAAqD;GACxD,IAAI,KAAa,CAAC,EAAQ,SAAS;GACnC,IAAM,IAAmB,EAAI,WAAW;GAGxC,AADA,IAAM,EAAE,IAAI,EAAQ,SAAS,EAAE,oBAAoB,GAAK,CAAC,GACzD,EAAE,UAAU,GAAS;IACjB,aAAa,KAAmB;IAChC,SAAS;GACb,CAAC,CAAC,CAAC,MAAM,CAAG;GAEZ,IAAM,IAAU,EAAO,KAAK,MAAwB,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC;GAC7E,AAAI,EAAQ,SAAS,MACjB,EAAE,SAAS,GAAS;IAChB,OAAO,KAAe;IACtB,QAAQ;GACZ,CAAC,CAAC,CAAC,MAAM,CAAG,GACZ,EAAE,aAAa,EAAQ,IAAI;IAAE,QAAQ;IAAG,OAAO;GAAU,CAAC,CAAC,CAAC,MAAM,CAAG;GAGzE,IAAM,IAAS,KAAW,EAAO,EAAO,SAAS;GACjD,AAAI,KACA,EAAE,aAAa,CAAC,EAAO,UAAU,EAAO,SAAS,GAAG;IAChD,QAAQ;IACR,OAAO,KAAe;GAC1B,CAAC,CAAC,CAAC,MAAM,CAAG;GAGhB,IAAM,IAAM,EAAY,IAAU,CAAC,GAAG,GAAQ,CAAO,IAAI,CAAC,GAAG,CAAM,CAAC;GACpE,AAAI,IACA,EAAI,UACA,CACI,CAAC,EAAI,aAAa,EAAI,YAAY,GAClC,CAAC,EAAI,aAAa,EAAI,YAAY,CACtC,GACA,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CACxB,IAEA,EAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;EAE7B,CAAC,CAAC,CACD,YAAY;GACT,AAAK,KACD,EACI,iGACJ;EAER,CAAC,SAEQ;GAET,AADA,IAAY,IACZ,GAAK,OAAO;EAChB;CACJ,GAAG;EAAC;EAAQ;EAAS;EAAS;EAAiB;CAAW,CAAC,GAEvD,IAEI,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAI,UAAU;GAAI,OAAO;EAA2B;EACtE,UAAA;CACA,CAAA,IAIN,kBAAC,OAAD;EAAK,KAAK;EAAS,OAAO;GAAE,OAAO;GAAQ,QAAQ;EAAO;CAAI,CAAA;AACzE"}