@mailwoman/react 10.0.0 → 10.2.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 (389) hide show
  1. package/README.md +18 -16
  2. package/fonts.css +97 -22
  3. package/lib/common/About.tsx +10 -10
  4. package/lib/common/ClientOnly.tsx +2 -14
  5. package/lib/common/CopyButton.tsx +9 -3
  6. package/lib/common/LoadingIndicator.tsx +13 -5
  7. package/lib/common/PresetChips.tsx +11 -8
  8. package/lib/common/confidence-tiers.ts +8 -7
  9. package/lib/common/cx.ts +3 -1
  10. package/lib/common/text-tokens.ts +10 -13
  11. package/lib/common/useBuildInfo.ts +54 -0
  12. package/lib/common/useClipboard.ts +2 -11
  13. package/lib/common.ts +22 -0
  14. package/lib/index.ts +27 -18
  15. package/lib/map/AppIdentity.tsx +89 -0
  16. package/lib/map/CompareToggle.tsx +8 -31
  17. package/lib/map/DebugInfo.tsx +75 -0
  18. package/lib/map/Geocoder.tsx +65 -50
  19. package/lib/map/GeocoderControls.tsx +425 -141
  20. package/lib/map/GraticuleLayer.tsx +56 -0
  21. package/lib/map/MapCanvas.tsx +26 -13
  22. package/lib/map/MapChipRow.tsx +4 -8
  23. package/lib/map/MapCompass.tsx +18 -25
  24. package/lib/map/MapControlStack.tsx +11 -10
  25. package/lib/map/MapFooter.tsx +5 -17
  26. package/lib/map/MapProgressBar.tsx +10 -13
  27. package/lib/map/MapSearchBar.tsx +31 -17
  28. package/lib/map/MapSheet.tsx +24 -15
  29. package/lib/map/OverlayLayers.tsx +14 -3
  30. package/lib/map/PlaceAutocomplete.tsx +12 -21
  31. package/lib/map/PlaceMarker.tsx +10 -4
  32. package/lib/map/ResolvedPlaceLayers.tsx +14 -15
  33. package/lib/map/ResultCamera.tsx +10 -31
  34. package/lib/map/ResultOverlay.tsx +14 -16
  35. package/lib/map/ResultPanel.tsx +1 -20
  36. package/lib/map/VersionPicker.tsx +10 -19
  37. package/lib/map/fake-runtime.ts +46 -19
  38. package/lib/map/geometry.ts +14 -47
  39. package/lib/map/graticule.ts +80 -0
  40. package/lib/map/map-debug.ts +4 -2
  41. package/lib/map/place-render.ts +30 -62
  42. package/lib/map/types.ts +57 -116
  43. package/lib/map/useCompareState.ts +5 -4
  44. package/lib/map/useGeocode.ts +7 -5
  45. package/lib/map/useMapBearing.ts +13 -13
  46. package/lib/map/useMapLabelPick.ts +21 -30
  47. package/lib/map/useMapPlaceRender.ts +3 -7
  48. package/lib/map/usePlaceAutocomplete.ts +44 -50
  49. package/lib/map.ts +92 -0
  50. package/lib/pipeline/CandidatePicker.tsx +13 -5
  51. package/lib/pipeline/ConfidenceCell.tsx +2 -2
  52. package/lib/pipeline/FailureDiagnostic.tsx +8 -2
  53. package/lib/pipeline/PipelineExplorer.tsx +6 -3
  54. package/lib/pipeline/QueryForm.tsx +5 -2
  55. package/lib/pipeline/ResolvedPlace.tsx +2 -2
  56. package/lib/pipeline/SpanHighlight.tsx +7 -5
  57. package/lib/pipeline/TimingPanel.tsx +1 -1
  58. package/lib/pipeline/TreeView.tsx +2 -2
  59. package/lib/pipeline/copy.ts +22 -25
  60. package/lib/pipeline/presets.ts +15 -13
  61. package/lib/pipeline/types.ts +20 -17
  62. package/lib/pipeline/useParsePipeline.ts +16 -13
  63. package/lib/pipeline.ts +36 -0
  64. package/lib/poi/AbstainPanel.tsx +1 -1
  65. package/lib/poi/LiveResultsBlock.tsx +2 -1
  66. package/lib/poi/POIExplorer.tsx +29 -17
  67. package/lib/poi/QueryInput.tsx +1 -1
  68. package/lib/poi/SubjectPanel.tsx +4 -3
  69. package/lib/poi/runtime.ts +14 -16
  70. package/lib/poi/types.ts +58 -47
  71. package/lib/poi/usePOISearch.ts +91 -82
  72. package/lib/poi.ts +40 -0
  73. package/lib/runtime/useReleaseRuntime.ts +122 -90
  74. package/lib/search/SearchModal.tsx +478 -0
  75. package/lib/search/search.css +282 -0
  76. package/lib/search/types.ts +68 -0
  77. package/lib/search.ts +11 -0
  78. package/out/common/About.d.ts +6 -6
  79. package/out/common/About.d.ts.map +1 -1
  80. package/out/common/About.js +1 -1
  81. package/out/common/About.js.map +1 -1
  82. package/out/common/ClientOnly.d.ts +2 -11
  83. package/out/common/ClientOnly.d.ts.map +1 -1
  84. package/out/common/ClientOnly.js +1 -13
  85. package/out/common/ClientOnly.js.map +1 -1
  86. package/out/common/CopyButton.d.ts +9 -3
  87. package/out/common/CopyButton.d.ts.map +1 -1
  88. package/out/common/CopyButton.js.map +1 -1
  89. package/out/common/LoadingIndicator.d.ts +13 -5
  90. package/out/common/LoadingIndicator.d.ts.map +1 -1
  91. package/out/common/LoadingIndicator.js.map +1 -1
  92. package/out/common/PresetChips.d.ts +11 -8
  93. package/out/common/PresetChips.d.ts.map +1 -1
  94. package/out/common/PresetChips.js.map +1 -1
  95. package/out/common/confidence-tiers.d.ts +8 -7
  96. package/out/common/confidence-tiers.d.ts.map +1 -1
  97. package/out/common/confidence-tiers.js +7 -6
  98. package/out/common/confidence-tiers.js.map +1 -1
  99. package/out/common/cx.d.ts +3 -1
  100. package/out/common/cx.d.ts.map +1 -1
  101. package/out/common/cx.js +3 -1
  102. package/out/common/cx.js.map +1 -1
  103. package/out/common/text-tokens.d.ts +6 -11
  104. package/out/common/text-tokens.d.ts.map +1 -1
  105. package/out/common/text-tokens.js +6 -3
  106. package/out/common/text-tokens.js.map +1 -1
  107. package/out/common/useBuildInfo.d.ts +27 -0
  108. package/out/common/useBuildInfo.d.ts.map +1 -0
  109. package/out/common/useBuildInfo.js +32 -0
  110. package/out/common/useBuildInfo.js.map +1 -0
  111. package/out/common/useClipboard.d.ts +1 -3
  112. package/out/common/useClipboard.d.ts.map +1 -1
  113. package/out/common/useClipboard.js +2 -11
  114. package/out/common/useClipboard.js.map +1 -1
  115. package/out/common.d.ts +22 -0
  116. package/out/common.d.ts.map +1 -0
  117. package/out/common.js +15 -0
  118. package/out/common.js.map +1 -0
  119. package/out/index.d.ts +26 -16
  120. package/out/index.d.ts.map +1 -1
  121. package/out/index.js +21 -15
  122. package/out/index.js.map +1 -1
  123. package/out/map/AppIdentity.d.ts +46 -0
  124. package/out/map/AppIdentity.d.ts.map +1 -0
  125. package/out/map/AppIdentity.js +16 -0
  126. package/out/map/AppIdentity.js.map +1 -0
  127. package/out/map/CompareToggle.d.ts +8 -31
  128. package/out/map/CompareToggle.d.ts.map +1 -1
  129. package/out/map/CompareToggle.js +4 -1
  130. package/out/map/CompareToggle.js.map +1 -1
  131. package/out/map/DebugInfo.d.ts +27 -0
  132. package/out/map/DebugInfo.d.ts.map +1 -0
  133. package/out/map/DebugInfo.js +36 -0
  134. package/out/map/DebugInfo.js.map +1 -0
  135. package/out/map/Geocoder.d.ts +41 -30
  136. package/out/map/Geocoder.d.ts.map +1 -1
  137. package/out/map/Geocoder.js +23 -37
  138. package/out/map/Geocoder.js.map +1 -1
  139. package/out/map/GeocoderControls.d.ts +15 -44
  140. package/out/map/GeocoderControls.d.ts.map +1 -1
  141. package/out/map/GeocoderControls.js +250 -39
  142. package/out/map/GeocoderControls.js.map +1 -1
  143. package/out/map/GraticuleLayer.d.ts +30 -0
  144. package/out/map/GraticuleLayer.d.ts.map +1 -0
  145. package/out/map/GraticuleLayer.js +25 -0
  146. package/out/map/GraticuleLayer.js.map +1 -0
  147. package/out/map/MapCanvas.d.ts +26 -13
  148. package/out/map/MapCanvas.d.ts.map +1 -1
  149. package/out/map/MapCanvas.js +5 -2
  150. package/out/map/MapCanvas.js.map +1 -1
  151. package/out/map/MapChipRow.d.ts +4 -8
  152. package/out/map/MapChipRow.d.ts.map +1 -1
  153. package/out/map/MapChipRow.js.map +1 -1
  154. package/out/map/MapCompass.d.ts +14 -21
  155. package/out/map/MapCompass.d.ts.map +1 -1
  156. package/out/map/MapCompass.js +5 -1
  157. package/out/map/MapCompass.js.map +1 -1
  158. package/out/map/MapControlStack.d.ts +11 -10
  159. package/out/map/MapControlStack.d.ts.map +1 -1
  160. package/out/map/MapControlStack.js +4 -1
  161. package/out/map/MapControlStack.js.map +1 -1
  162. package/out/map/MapFooter.d.ts +5 -16
  163. package/out/map/MapFooter.d.ts.map +1 -1
  164. package/out/map/MapFooter.js +0 -14
  165. package/out/map/MapFooter.js.map +1 -1
  166. package/out/map/MapProgressBar.d.ts +10 -13
  167. package/out/map/MapProgressBar.d.ts.map +1 -1
  168. package/out/map/MapProgressBar.js.map +1 -1
  169. package/out/map/MapSearchBar.d.ts +16 -17
  170. package/out/map/MapSearchBar.d.ts.map +1 -1
  171. package/out/map/MapSearchBar.js +6 -0
  172. package/out/map/MapSearchBar.js.map +1 -1
  173. package/out/map/MapSheet.d.ts +15 -12
  174. package/out/map/MapSheet.d.ts.map +1 -1
  175. package/out/map/MapSheet.js +8 -12
  176. package/out/map/MapSheet.js.map +1 -1
  177. package/out/map/OverlayLayers.d.ts +8 -2
  178. package/out/map/OverlayLayers.d.ts.map +1 -1
  179. package/out/map/OverlayLayers.js +3 -3
  180. package/out/map/OverlayLayers.js.map +1 -1
  181. package/out/map/PlaceAutocomplete.d.ts +12 -21
  182. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  183. package/out/map/PlaceAutocomplete.js +2 -1
  184. package/out/map/PlaceAutocomplete.js.map +1 -1
  185. package/out/map/PlaceMarker.d.ts +10 -4
  186. package/out/map/PlaceMarker.d.ts.map +1 -1
  187. package/out/map/PlaceMarker.js +3 -1
  188. package/out/map/PlaceMarker.js.map +1 -1
  189. package/out/map/ResolvedPlaceLayers.d.ts +14 -15
  190. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  191. package/out/map/ResolvedPlaceLayers.js +1 -1
  192. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  193. package/out/map/ResultCamera.d.ts +10 -31
  194. package/out/map/ResultCamera.d.ts.map +1 -1
  195. package/out/map/ResultCamera.js +3 -28
  196. package/out/map/ResultCamera.js.map +1 -1
  197. package/out/map/ResultOverlay.d.ts +14 -16
  198. package/out/map/ResultOverlay.d.ts.map +1 -1
  199. package/out/map/ResultOverlay.js +1 -2
  200. package/out/map/ResultOverlay.js.map +1 -1
  201. package/out/map/ResultPanel.d.ts +1 -20
  202. package/out/map/ResultPanel.d.ts.map +1 -1
  203. package/out/map/ResultPanel.js.map +1 -1
  204. package/out/map/VersionPicker.d.ts +7 -19
  205. package/out/map/VersionPicker.d.ts.map +1 -1
  206. package/out/map/VersionPicker.js +5 -2
  207. package/out/map/VersionPicker.js.map +1 -1
  208. package/out/map/fake-runtime.d.ts +9 -11
  209. package/out/map/fake-runtime.d.ts.map +1 -1
  210. package/out/map/fake-runtime.js +45 -19
  211. package/out/map/fake-runtime.js.map +1 -1
  212. package/out/map/geometry.d.ts +13 -34
  213. package/out/map/geometry.d.ts.map +1 -1
  214. package/out/map/geometry.js +9 -34
  215. package/out/map/geometry.js.map +1 -1
  216. package/out/map/graticule.d.ts +17 -0
  217. package/out/map/graticule.d.ts.map +1 -0
  218. package/out/map/graticule.js +67 -0
  219. package/out/map/graticule.js.map +1 -0
  220. package/out/map/map-debug.d.ts +4 -2
  221. package/out/map/map-debug.d.ts.map +1 -1
  222. package/out/map/map-debug.js +1 -1
  223. package/out/map/map-debug.js.map +1 -1
  224. package/out/map/place-render.d.ts +25 -45
  225. package/out/map/place-render.d.ts.map +1 -1
  226. package/out/map/place-render.js +4 -39
  227. package/out/map/place-render.js.map +1 -1
  228. package/out/map/types.d.ts +57 -113
  229. package/out/map/types.d.ts.map +1 -1
  230. package/out/map/types.js +1 -10
  231. package/out/map/types.js.map +1 -1
  232. package/out/map/useCompareState.d.ts +5 -4
  233. package/out/map/useCompareState.d.ts.map +1 -1
  234. package/out/map/useCompareState.js +3 -3
  235. package/out/map/useCompareState.js.map +1 -1
  236. package/out/map/useGeocode.d.ts +5 -3
  237. package/out/map/useGeocode.d.ts.map +1 -1
  238. package/out/map/useGeocode.js +4 -4
  239. package/out/map/useGeocode.js.map +1 -1
  240. package/out/map/useMapBearing.d.ts +11 -11
  241. package/out/map/useMapBearing.d.ts.map +1 -1
  242. package/out/map/useMapBearing.js +6 -11
  243. package/out/map/useMapBearing.js.map +1 -1
  244. package/out/map/useMapLabelPick.d.ts +4 -14
  245. package/out/map/useMapLabelPick.d.ts.map +1 -1
  246. package/out/map/useMapLabelPick.js +21 -30
  247. package/out/map/useMapLabelPick.js.map +1 -1
  248. package/out/map/useMapPlaceRender.d.ts +3 -7
  249. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  250. package/out/map/useMapPlaceRender.js +2 -6
  251. package/out/map/useMapPlaceRender.js.map +1 -1
  252. package/out/map/usePlaceAutocomplete.d.ts +30 -28
  253. package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
  254. package/out/map/usePlaceAutocomplete.js +6 -33
  255. package/out/map/usePlaceAutocomplete.js.map +1 -1
  256. package/out/map.d.ts +67 -0
  257. package/out/map.d.ts.map +1 -0
  258. package/out/map.js +45 -0
  259. package/out/map.js.map +1 -0
  260. package/out/pipeline/CandidatePicker.d.ts +12 -2
  261. package/out/pipeline/CandidatePicker.d.ts.map +1 -1
  262. package/out/pipeline/CandidatePicker.js +1 -1
  263. package/out/pipeline/CandidatePicker.js.map +1 -1
  264. package/out/pipeline/ConfidenceCell.d.ts +2 -2
  265. package/out/pipeline/ConfidenceCell.d.ts.map +1 -1
  266. package/out/pipeline/ConfidenceCell.js +1 -1
  267. package/out/pipeline/FailureDiagnostic.d.ts +8 -2
  268. package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
  269. package/out/pipeline/FailureDiagnostic.js +3 -0
  270. package/out/pipeline/FailureDiagnostic.js.map +1 -1
  271. package/out/pipeline/PipelineExplorer.d.ts +6 -2
  272. package/out/pipeline/PipelineExplorer.d.ts.map +1 -1
  273. package/out/pipeline/PipelineExplorer.js +1 -1
  274. package/out/pipeline/PipelineExplorer.js.map +1 -1
  275. package/out/pipeline/QueryForm.d.ts +5 -2
  276. package/out/pipeline/QueryForm.d.ts.map +1 -1
  277. package/out/pipeline/QueryForm.js.map +1 -1
  278. package/out/pipeline/ResolvedPlace.d.ts +1 -1
  279. package/out/pipeline/ResolvedPlace.d.ts.map +1 -1
  280. package/out/pipeline/SpanHighlight.d.ts +4 -3
  281. package/out/pipeline/SpanHighlight.d.ts.map +1 -1
  282. package/out/pipeline/SpanHighlight.js +3 -2
  283. package/out/pipeline/SpanHighlight.js.map +1 -1
  284. package/out/pipeline/TimingPanel.d.ts +1 -1
  285. package/out/pipeline/TreeView.d.ts +1 -1
  286. package/out/pipeline/TreeView.js.map +1 -1
  287. package/out/pipeline/copy.d.ts +1 -1
  288. package/out/pipeline/copy.d.ts.map +1 -1
  289. package/out/pipeline/copy.js +4 -3
  290. package/out/pipeline/copy.js.map +1 -1
  291. package/out/pipeline/presets.d.ts +5 -4
  292. package/out/pipeline/presets.d.ts.map +1 -1
  293. package/out/pipeline/presets.js +15 -13
  294. package/out/pipeline/presets.js.map +1 -1
  295. package/out/pipeline/types.d.ts +20 -17
  296. package/out/pipeline/types.d.ts.map +1 -1
  297. package/out/pipeline/types.js +2 -4
  298. package/out/pipeline/types.js.map +1 -1
  299. package/out/pipeline/useParsePipeline.d.ts +14 -9
  300. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  301. package/out/pipeline/useParsePipeline.js +7 -8
  302. package/out/pipeline/useParsePipeline.js.map +1 -1
  303. package/out/pipeline.d.ts +33 -0
  304. package/out/pipeline.d.ts.map +1 -0
  305. package/out/pipeline.js +21 -0
  306. package/out/pipeline.js.map +1 -0
  307. package/out/poi/AbstainPanel.d.ts +1 -1
  308. package/out/poi/LiveResultsBlock.d.ts +2 -1
  309. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  310. package/out/poi/LiveResultsBlock.js.map +1 -1
  311. package/out/poi/POIExplorer.d.ts +22 -12
  312. package/out/poi/POIExplorer.d.ts.map +1 -1
  313. package/out/poi/POIExplorer.js +11 -10
  314. package/out/poi/POIExplorer.js.map +1 -1
  315. package/out/poi/QueryInput.d.ts +1 -1
  316. package/out/poi/SubjectPanel.d.ts +2 -2
  317. package/out/poi/SubjectPanel.d.ts.map +1 -1
  318. package/out/poi/SubjectPanel.js +2 -1
  319. package/out/poi/SubjectPanel.js.map +1 -1
  320. package/out/poi/runtime.d.ts +5 -14
  321. package/out/poi/runtime.d.ts.map +1 -1
  322. package/out/poi/runtime.js +14 -16
  323. package/out/poi/runtime.js.map +1 -1
  324. package/out/poi/types.d.ts +53 -45
  325. package/out/poi/types.d.ts.map +1 -1
  326. package/out/poi/types.js +2 -4
  327. package/out/poi/types.js.map +1 -1
  328. package/out/poi/usePOISearch.d.ts +30 -17
  329. package/out/poi/usePOISearch.d.ts.map +1 -1
  330. package/out/poi/usePOISearch.js +48 -64
  331. package/out/poi/usePOISearch.js.map +1 -1
  332. package/out/poi.d.ts +23 -0
  333. package/out/poi.d.ts.map +1 -0
  334. package/out/poi.js +15 -0
  335. package/out/poi.js.map +1 -0
  336. package/out/runtime/useReleaseRuntime.d.ts +63 -74
  337. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  338. package/out/runtime/useReleaseRuntime.js +31 -32
  339. package/out/runtime/useReleaseRuntime.js.map +1 -1
  340. package/out/{runtime/index.d.ts → runtime.d.ts} +1 -1
  341. package/out/runtime.d.ts.map +1 -0
  342. package/out/{runtime/index.js → runtime.js} +1 -1
  343. package/out/runtime.js.map +1 -0
  344. package/out/search/SearchModal.d.ts +40 -0
  345. package/out/search/SearchModal.d.ts.map +1 -0
  346. package/out/search/SearchModal.js +255 -0
  347. package/out/search/SearchModal.js.map +1 -0
  348. package/out/search/types.d.ts +65 -0
  349. package/out/search/types.d.ts.map +1 -0
  350. package/out/search/types.js +13 -0
  351. package/out/search/types.js.map +1 -0
  352. package/out/search.d.ts +11 -0
  353. package/out/search.d.ts.map +1 -0
  354. package/out/search.js +9 -0
  355. package/out/search.js.map +1 -0
  356. package/package.json +73 -165
  357. package/styles.css +482 -266
  358. package/tokens/index.css +24 -6
  359. package/tokens/tokens.json +114 -6
  360. package/lib/common/index.ts +0 -22
  361. package/lib/map/BackendControl.tsx +0 -51
  362. package/lib/map/index.ts +0 -95
  363. package/lib/pipeline/index.ts +0 -36
  364. package/lib/poi/index.ts +0 -39
  365. package/out/common/index.d.ts +0 -22
  366. package/out/common/index.d.ts.map +0 -1
  367. package/out/common/index.js +0 -15
  368. package/out/common/index.js.map +0 -1
  369. package/out/map/BackendControl.d.ts +0 -32
  370. package/out/map/BackendControl.d.ts.map +0 -1
  371. package/out/map/BackendControl.js +0 -8
  372. package/out/map/BackendControl.js.map +0 -1
  373. package/out/map/index.d.ts +0 -69
  374. package/out/map/index.d.ts.map +0 -1
  375. package/out/map/index.js +0 -46
  376. package/out/map/index.js.map +0 -1
  377. package/out/pipeline/index.d.ts +0 -33
  378. package/out/pipeline/index.d.ts.map +0 -1
  379. package/out/pipeline/index.js +0 -21
  380. package/out/pipeline/index.js.map +0 -1
  381. package/out/poi/index.d.ts +0 -23
  382. package/out/poi/index.d.ts.map +0 -1
  383. package/out/poi/index.js +0 -15
  384. package/out/poi/index.js.map +0 -1
  385. package/out/runtime/index.d.ts.map +0 -1
  386. package/out/runtime/index.js.map +0 -1
  387. package/styleframe.config.ts +0 -329
  388. package/vitest.node.config.ts +0 -20
  389. /package/lib/{runtime/index.ts → runtime.ts} +0 -0
package/README.md CHANGED
@@ -1,21 +1,22 @@
1
1
  # @mailwoman/react
2
2
 
3
- React components + headless hooks for [mailwoman](https://mailwoman.ai) — the
4
- parse/geocode/POI explorers from the docs site, decomposed and packaged for use in any React app.
3
+ This package provides React components and headless hooks for [mailwoman](https://mailwoman.ai). It
4
+ packages the parse/geocode/POI explorers from the docs site as smaller parts for use in any React app.
5
5
 
6
6
  ## What's in the box
7
7
 
8
- Two composed explorers, plus the small presentational units and headless hooks they decompose into:
8
+ The package contains two composed explorers, plus the small presentational units and headless hooks
9
+ they are built from:
9
10
 
10
- - **`POIExplorer`** — a self-contained POI-intent tester (classify → subject → OverpassQL). The intent
11
+ - **`POIExplorer`** is a self-contained POI-intent tester (classify → subject → OverpassQL). The intent
11
12
  path runs entirely offline over `@mailwoman/kind-classifier`, `@mailwoman/poi-taxonomy`, and
12
- `@mailwoman/query-shape` — no weights, no network. A live poi.db search is opt-in via an injected
13
- `runLiveSearch` probe, so the httpvfs/worker implementation never enters this package's graph.
14
- - **`PipelineExplorer`** — a parse + resolve tester driven by an **injected `PipelineRuntime`**. The
13
+ `@mailwoman/query-shape`, without weights or network access. A live poi.db search is opt-in through an
14
+ injected `runLiveSearch` probe, so the httpvfs/worker implementation never enters this package's graph.
15
+ - **`PipelineExplorer`** is a parse and resolve tester driven by an **injected `PipelineRuntime`**. The
15
16
  host supplies `runParse` (compute shape → classify → resolve) and the heavy visualizers as `panels`,
16
- keeping onnxruntime-web, sql.js-httpvfs, and node builtins out of this package entirely.
17
+ which keeps onnxruntime-web, sqlite-wasm, and node builtins out of this package entirely.
17
18
 
18
- The headless hooks — `usePOISearch`, `useParsePipeline` — own the state machines; the presentational
19
+ The headless hooks, `usePOISearch` and `useParsePipeline`, own the state machines. The presentational
19
20
  units (`QueryInput`, `SubjectPanel`, `OverpassBlock`, `LiveResultsBlock`, `ComponentTable`,
20
21
  `ResolvedPlace`, `CandidatePicker`, `KindBadge`, `LoadingIndicator`, …) are pure and prop-driven.
21
22
 
@@ -23,20 +24,21 @@ units (`QueryInput`, `SubjectPanel`, `OverpassBlock`, `LiveResultsBlock`, `Compo
23
24
 
24
25
  ```tsx
25
26
  import { POIExplorer, PipelineExplorer } from "@mailwoman/react"
27
+
26
28
  import "@mailwoman/react/styles.css"
27
29
  ```
28
30
 
29
- Styling ships as a standalone stylesheet (`@mailwoman/react/styles.css`) — plain, `mw-`-prefixed, and
30
- Infima-token-aware, so it looks right both inside Docusaurus and standalone. No component imports CSS,
31
- so the bare package import stays node-safe.
31
+ The stylesheet ships as a standalone file (`@mailwoman/react/styles.css`). It is plain CSS with a `mw-`
32
+ prefix and reads Infima tokens, so it renders correctly both inside Docusaurus and standalone. No
33
+ component imports CSS, so the bare package import stays safe to load in Node.
32
34
 
33
35
  ## Development
34
36
 
35
- - `yarn workspace @mailwoman/react storybook` — Storybook (Vite) for every unit + the composed
36
- explorers, with mocked runtimes (no network/db).
37
- - `yarn workspace @mailwoman/react test:browser` — Vitest browser-mode component + hook tests
37
+ - `yarn workspace @mailwoman/react storybook` runs Storybook (Vite) for every unit and the composed
38
+ explorers, with mocked runtimes that need no network or database.
39
+ - `yarn workspace @mailwoman/react test:browser` runs the Vitest browser-mode component and hook tests
38
40
  (Playwright / headless Chromium).
39
41
 
40
42
  ## License
41
43
 
42
- AGPL-3.0-only OR LicenseRef-Commercial — see the [mailwoman repository](https://github.com/sister-software/mailwoman).
44
+ AGPL-3.0-only OR LicenseRef-Commercial. See the [mailwoman repository](https://github.com/sister-software/mailwoman).
package/fonts.css CHANGED
@@ -1,59 +1,134 @@
1
1
  /**
2
- * The brand typeface, served from the same origin every app already preconnects to.
2
+ * The brand typefaces, served from the same origin every app already preconnects to.
3
3
  *
4
- * Six faces, not the twenty the family ships. Each `@font-face` is a network fetch on first paint, and the token
5
- * layer binds only these: regular and medium for running text, bold where a heading or a chip needs weight, italic
6
- * for a caption, and two mono weights for the `code`, `number` and `glyph` roles. Iosevka provides no 600, so
7
- * `--font-weight-semibold: 600` matches the 700 face by the CSS weight-matching rules rather than being synthesized.
4
+ * The token layer binds five faces: Bliss Pro regular, medium and bold for running text and headings, Bliss Pro
5
+ * italic for a caption, and the variable Fira Code face (weights 300 to 700) for the `code`, `number` and `glyph`
6
+ * roles. Bliss Pro's figures are proportional and it has no `tnum` feature, so `number` sets in Fira Code. Bliss Pro
7
+ * provides no 600, so `--font-weight-semibold: 600` matches the 700 face by the CSS weight-matching rules rather than
8
+ * being synthesized.
9
+ *
10
+ * Each Bliss Pro face is split by `unicode-range` into a Latin file and an extended file (Latin Extended-A, Greek,
11
+ * Cyrillic, math), and the browser downloads the extended file only when a page uses one of its codepoints.
12
+ * "Bliss Pro Fallback" scales Arial to Bliss Pro's average advance and vertical metrics, so text keeps its line
13
+ * breaks when the web font swaps in.
8
14
  *
9
15
  * A rule never names a family here — it names a scale role from `tokens.css`, which names one of the five face
10
16
  * roles. Replacing the typeface is an edit to `--font-family-*` in `styleframe.config.ts` plus this file.
11
17
  */
12
18
 
13
19
  @font-face {
14
- font-family: "Iosevka Nexus Web";
20
+ font-family: "Bliss Pro Fallback";
21
+ src: local("Arial");
22
+ size-adjust: 92%;
23
+ ascent-override: 103.6%;
24
+ descent-override: 26.2%;
25
+ line-gap-override: 0%;
26
+ }
27
+
28
+ @font-face {
29
+ font-family: "Bliss Pro Web";
15
30
  font-display: swap;
16
31
  font-weight: 400;
17
32
  font-style: normal;
18
- src: url("https://public.mailwoman.ai/fonts/IoveskaNexus/WOFF2/IosevkaNexus-Regular.woff2") format("woff2");
33
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Latin.woff2") format("woff2");
34
+ unicode-range:
35
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC,
36
+ U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+FB01-FB02;
19
37
  }
20
38
 
21
39
  @font-face {
22
- font-family: "Iosevka Nexus Web";
40
+ font-family: "Bliss Pro Web";
23
41
  font-display: swap;
24
- font-weight: 450;
42
+ font-weight: 400;
25
43
  font-style: normal;
26
- src: url("https://public.mailwoman.ai/fonts/IoveskaNexus/WOFF2/IosevkaNexus-Book.woff2") format("woff2");
44
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Extended.woff2") format("woff2");
45
+ unicode-range:
46
+ U+0100-0129, U+012B-012C, U+012E-0130, U+0132-0137, U+0139-0148, U+014A-0151, U+0154-017E, U+0192, U+01FA-01FF,
47
+ U+0218-0219, U+02C7, U+02D8-02D9, U+02DB, U+02DD, U+0315, U+0326, U+0386, U+0388-038A, U+038C, U+038E-0393,
48
+ U+0395-03A1, U+03A3-03CE, U+0400-045F, U+0462-0463, U+0472-0475, U+0490-0491, U+1E80-1E85, U+1EF2-1EF3, U+2070,
49
+ U+2074-2089, U+208D-208E, U+2113, U+2116-2117, U+212E, U+2153-2154, U+215B-215E, U+2202, U+2206, U+220F, U+2211,
50
+ U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA;
27
51
  }
28
52
 
29
53
  @font-face {
30
- font-family: "Iosevka Nexus Web";
54
+ font-family: "Bliss Pro Web";
31
55
  font-display: swap;
32
- font-weight: 700;
33
- font-style: normal;
34
- src: url("https://public.mailwoman.ai/fonts/IoveskaNexus/WOFF2/IosevkaNexus-Bold.woff2") format("woff2");
56
+ font-weight: 400;
57
+ font-style: italic;
58
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Italic-Latin.woff2") format("woff2");
59
+ unicode-range:
60
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC,
61
+ U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+FB01-FB02;
35
62
  }
36
63
 
37
64
  @font-face {
38
- font-family: "Iosevka Nexus Web";
65
+ font-family: "Bliss Pro Web";
39
66
  font-display: swap;
40
67
  font-weight: 400;
41
68
  font-style: italic;
42
- src: url("https://public.mailwoman.ai/fonts/IoveskaNexus/WOFF2/IosevkaNexus-Italic.woff2") format("woff2");
69
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Italic-Extended.woff2") format("woff2");
70
+ unicode-range:
71
+ U+0100-0129, U+012B-012C, U+012E-0130, U+0132-0137, U+0139-0148, U+014A-0151, U+0154-017E, U+0192, U+01FA-01FF,
72
+ U+0218-0219, U+02C7, U+02D8-02D9, U+02DB, U+02DD, U+0315, U+0326, U+0386, U+0388-038A, U+038C, U+038E-0393,
73
+ U+0395-03A1, U+03A3-03CE, U+0400-045F, U+0462-0463, U+0472-0475, U+0490-0491, U+1E80-1E85, U+1EF2-1EF3, U+2070,
74
+ U+2074-2089, U+208D-208E, U+2113, U+2116-2117, U+212E, U+2153-2154, U+215B-215E, U+2202, U+2206, U+220F, U+2211,
75
+ U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA;
43
76
  }
44
77
 
45
78
  @font-face {
46
- font-family: "Iosevka Nexus Mono Web";
79
+ font-family: "Bliss Pro Web";
47
80
  font-display: swap;
48
- font-weight: 400;
81
+ font-weight: 500;
82
+ font-style: normal;
83
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Medium-Latin.woff2") format("woff2");
84
+ unicode-range:
85
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC,
86
+ U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+FB01-FB02;
87
+ }
88
+
89
+ @font-face {
90
+ font-family: "Bliss Pro Web";
91
+ font-display: swap;
92
+ font-weight: 500;
93
+ font-style: normal;
94
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Medium-Extended.woff2") format("woff2");
95
+ unicode-range:
96
+ U+0100-0129, U+012B-012C, U+012E-0130, U+0132-0137, U+0139-0148, U+014A-0151, U+0154-017E, U+0192, U+01FA-01FF,
97
+ U+0218-0219, U+02C7, U+02D8-02D9, U+02DB, U+02DD, U+0315, U+0326, U+0386, U+0388-038A, U+038C, U+038E-0393,
98
+ U+0395-03A1, U+03A3-03CE, U+0400-045F, U+0462-0463, U+0472-0475, U+0490-0491, U+1E80-1E85, U+1EF2-1EF3, U+2070,
99
+ U+2074-2089, U+208D-208E, U+2113, U+2116-2117, U+212E, U+2153-2154, U+215B-215E, U+2202, U+2206, U+220F, U+2211,
100
+ U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA;
101
+ }
102
+
103
+ @font-face {
104
+ font-family: "Bliss Pro Web";
105
+ font-display: swap;
106
+ font-weight: 700;
107
+ font-style: normal;
108
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Bold-Latin.woff2") format("woff2");
109
+ unicode-range:
110
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC,
111
+ U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+FB01-FB02;
112
+ }
113
+
114
+ @font-face {
115
+ font-family: "Bliss Pro Web";
116
+ font-display: swap;
117
+ font-weight: 700;
49
118
  font-style: normal;
50
- src: url("https://public.mailwoman.ai/fonts/IosevkaNexusMono/WOFF2/IosevkaNexusMono-Regular.woff2") format("woff2");
119
+ src: url("https://public.mailwoman.ai/fonts/BlissPro/WOFF2/BlissPro-Bold-Extended.woff2") format("woff2");
120
+ unicode-range:
121
+ U+0100-0129, U+012B-012C, U+012E-0130, U+0132-0137, U+0139-0148, U+014A-0151, U+0154-017E, U+0192, U+01FA-01FF,
122
+ U+0218-0219, U+02C7, U+02D8-02D9, U+02DB, U+02DD, U+0315, U+0326, U+0386, U+0388-038A, U+038C, U+038E-0393,
123
+ U+0395-03A1, U+03A3-03CE, U+0400-045F, U+0462-0463, U+0472-0475, U+0490-0491, U+1E80-1E85, U+1EF2-1EF3, U+2070,
124
+ U+2074-2089, U+208D-208E, U+2113, U+2116-2117, U+212E, U+2153-2154, U+215B-215E, U+2202, U+2206, U+220F, U+2211,
125
+ U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA;
51
126
  }
52
127
 
53
128
  @font-face {
54
- font-family: "Iosevka Nexus Mono Web";
129
+ font-family: "Fira Code Web";
55
130
  font-display: swap;
56
- font-weight: 450;
131
+ font-weight: 300 700;
57
132
  font-style: normal;
58
- src: url("https://public.mailwoman.ai/fonts/IosevkaNexusMono/WOFF2/IosevkaNexusMono-Book.woff2") format("woff2");
133
+ src: url("https://public.mailwoman.ai/fonts/FiraCode/WOFF2/FiraCode-VF.woff2") format("woff2");
59
134
  }
@@ -3,19 +3,19 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `About` — the explainer for the browser geocoder: the neural model, the confidence colours, the gazetteer
7
- * resolver, the byte-range database and the FST prior. Prose about the product a host places above or beside the
8
- * geocoder; it renders nothing live.
6
+ * `About` — the explainer for the browser geocoder: the neural model, the confidence colors, the gazetteer resolver, the byte-range database and the FST prior. It renders no live content.
9
7
  */
10
8
 
11
9
  import type { ReactNode } from "react"
12
10
 
13
11
  export interface AboutProps {
14
12
  /**
15
- * Wrap the prose in its own `<details>` disclosure. Set false where the host is already the disclosure — inside a
16
- * sheet opened by an About button, a summary repeating that button's words is one control too many.
13
+ * Wraps the prose in its own `<details>` disclosure.
17
14
  *
18
- * @default true
15
+ * Set false where the host is already the disclosure, because a summary repeating
16
+ * an About button's words is one control too many.
17
+ *
18
+ * @defaultValue `true`
19
19
  */
20
20
  collapsible?: boolean
21
21
  }
@@ -28,8 +28,8 @@ export function About({ collapsible = true }: AboutProps = {}): ReactNode {
28
28
  <p>
29
29
  A ~29M-parameter BERT-style encoder classifies each token into one of 33 BIO labels covering 16 address
30
30
  components (street, house number, unit, locality, region, postcode, venue, country, intersection, etc.). It
31
- runs entirely in your browser via <strong>onnxruntime-web</strong> (WebGPU with WASM SIMD fallback). The model
32
- ships as a 37.6 MB int8-quantized ONNX bundle; fp32 weights are also available for higher accuracy.
31
+ runs entirely in your browser via <strong>onnxruntime-web</strong> on its WASM SIMD backend. The model ships
32
+ as a 37.6 MB int8-quantized ONNX bundle; fp32 weights are also available for higher accuracy.
33
33
  </p>
34
34
  </section>
35
35
 
@@ -73,8 +73,8 @@ export function About({ collapsible = true }: AboutProps = {}): ReactNode {
73
73
  <h3 className="mw-about__heading">Byte-range gazetteer</h3>
74
74
  <p>
75
75
  The gazetteer is a multi-gigabyte SQLite file served from a public bucket. It loads via{" "}
76
- <strong>sql.js-httpvfs</strong>, which range-requests only the pages a lookup touches, so your browser fetches
77
- a few hundred kilobytes per session instead of the whole file.
76
+ <strong>sqlite-wasm</strong> over a read-only HTTP file system. It range-requests only the pages a lookup
77
+ touches. Therefore, your browser fetches a few hundred kilobytes per session instead of the whole file.
78
78
  </p>
79
79
  </section>
80
80
 
@@ -3,30 +3,18 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `ClientOnly` — a portable SSR boundary. The docs explorers wrapped Docusaurus's `<BrowserOnly>`;
7
- * this is the framework-agnostic equivalent so the components stay usable in any React app (Next,
8
- * Remix, Docusaurus). It renders `fallback` on the server and the first client paint, then swaps to
9
- * `children()` once mounted — keeping timers, clipboard, and dynamic imports off the server render.
10
- *
11
- * The mount flag comes from `useSyncExternalStore` with a constant snapshot pair — `false` on the
12
- * server, `true` in the browser — which is the store-shaped statement of exactly this boundary. The
13
- * earlier `useState(false)` + `useEffect(() => setMounted(true))` said the same thing with a
14
- * render-cascade the react(set-state-in-effect) rule rightly flags: the second render was the
15
- * mechanism, not an accident, and the store form gets the same second paint without a set-state.
6
+ * `ClientOnly` — a portable SSR boundary that renders `fallback` on the server and first client paint. It swaps to `children()` after mounting, keeping timers, clipboard and dynamic imports off the server render.
16
7
  */
17
8
 
18
9
  import { type ReactNode, useSyncExternalStore } from "react"
19
10
 
20
- /**
21
- * The boundary never changes after mount, so nothing ever notifies.
22
- */
23
11
  function subscribeNever(): () => void {
24
12
  return () => {}
25
13
  }
26
14
 
27
15
  export interface ClientOnlyProps {
28
16
  /**
29
- * Rendered once mounted in the browser. A thunk so its (browser-only) imports never run on the server.
17
+ * Rendered once mounted in the browser, as a thunk so its browser-only imports never run on the server.
30
18
  */
31
19
  children: () => ReactNode
32
20
  /**
@@ -13,15 +13,21 @@ import { useClipboard } from "#common/useClipboard"
13
13
 
14
14
  export interface CopyButtonProps {
15
15
  /**
16
- * The text to copy. If a thunk, it's evaluated at click time (so callers can copy the latest value).
16
+ * The text to copy.
17
+ *
18
+ * If a thunk, it's evaluated at click time (so callers can copy the latest value).
17
19
  */
18
20
  value: string | (() => string)
19
21
  /**
20
- * Label in the idle state. @default "Copy"
22
+ * Label in the idle state.
23
+ *
24
+ * @defaultValue `"Copy"`
21
25
  */
22
26
  label?: ReactNode
23
27
  /**
24
- * Label shown briefly after a successful copy. @default "✓ Copied"
28
+ * Label shown briefly after a successful copy.
29
+ *
30
+ * @defaultValue `"✓ Copied"`
25
31
  */
26
32
  copiedLabel?: ReactNode
27
33
  className?: string
@@ -17,19 +17,24 @@ export type LoadingMode = "pulse" | "spinner" | "staged"
17
17
 
18
18
  export interface LoadingIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {
19
19
  /**
20
- * @default "spinner"
20
+ * @defaultValue `"spinner"`
21
21
  */
22
22
  mode?: LoadingMode
23
23
  /**
24
- * Optional label; in staged mode it renders above the step list.
24
+ * Optional label.
25
+ * In staged mode it renders above the step list.
25
26
  */
26
27
  label?: string
27
28
  /**
28
- * Number of skeleton bars (pulse mode). @default 3
29
+ * Number of skeleton bars (pulse mode).
30
+ *
31
+ * @defaultValue `3`
29
32
  */
30
33
  barCount?: number
31
34
  /**
32
- * Spinner ring size. @default "medium"
35
+ * Spinner ring size.
36
+ *
37
+ * @defaultValue `"medium"`
33
38
  */
34
39
  size?: "small" | "medium" | "large"
35
40
  /**
@@ -37,7 +42,10 @@ export interface LoadingIndicatorProps extends React.HTMLAttributes<HTMLDivEleme
37
42
  */
38
43
  steps?: string[]
39
44
  /**
40
- * 0-based index of the active step; earlier steps are complete, later ones pending. @default -1
45
+ * 0-based index of the active step.
46
+ * Earlier steps are complete, later ones pending.
47
+ *
48
+ * @defaultValue `-1`
41
49
  */
42
50
  activeStep?: number
43
51
  }
@@ -3,8 +3,8 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `PresetChips` — the "Try:" row of example buttons shared by both explorers. Presentational: it
7
- * renders a labelled list and calls `onPick` with the chosen preset's value.
6
+ * `PresetChips` is the "Try:" row of example buttons shared by both explorers. Presentational: it
7
+ * renders a labeled list and calls `onPick` with the chosen preset's value.
8
8
  */
9
9
 
10
10
  import type { ReactNode } from "react"
@@ -13,10 +13,10 @@ export interface Preset {
13
13
  label: string
14
14
  value: string
15
15
  /**
16
- * ISO country code for the placetype-pair country pin (#1278 phase 2's `{country}` override). Optional: a host that
17
- * wires the pair prior hands this to `selectPairIndexForText` when the input still equals this preset's text, so a
18
- * locale structural routing can't detect (NZ's 4-digit postcode isn't distinctive) still fires the dependent_locality
19
- * prior. Purely descriptive to `PresetChips` itself (which only emits `value`).
16
+ * ISO country code for the placetype-pair country pin.
17
+ *
18
+ * A host that wires the pair prior hands this to `selectPairIndexForText`
19
+ * when the input still equals this preset's text.
20
20
  */
21
21
  country?: string
22
22
  }
@@ -26,11 +26,14 @@ export interface PresetChipsProps {
26
26
  onPick: (value: string) => void
27
27
  disabled?: boolean
28
28
  /**
29
- * Leading label. @default "Try:"
29
+ * The label shown before the choices.
30
+ *
31
+ * @defaultValue `"Try:"`
30
32
  */
31
33
  caption?: string
32
34
  /**
33
- * Optional trailing content rendered inside the chip row after the presets (e.g. an inline permalink button).
35
+ * Optional trailing content rendered inside the chip row after the presets
36
+ * (e.g. an inline permalink button).
34
37
  */
35
38
  trailing?: ReactNode
36
39
  }
@@ -2,10 +2,10 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- * @file Confidence banding shared by every component that colours a span by confidence.
5
+ * @file Confidence banding shared by every component that colors a span by confidence.
6
6
  *
7
- * It is a presentation choice, not a model one: the parser emits a continuous confidence and these
8
- * bounds only decide which colour a span is drawn in, so they live here rather than anywhere the
7
+ * It is a presentation choice rather than a model one: the parser emits a continuous confidence and these
8
+ * bounds only decide which color a span is drawn in, so they live here rather than anywhere the
9
9
  * pipeline can see them.
10
10
  */
11
11
 
@@ -15,7 +15,8 @@
15
15
  export const HIGH_CONFIDENCE_MIN = 0.8
16
16
 
17
17
  /**
18
- * At or above this the span is drawn as medium-confidence; below it, low.
18
+ * At or above this the span is drawn as medium-confidence.
19
+ * Below it, low.
19
20
  */
20
21
  export const MID_CONFIDENCE_MIN = 0.5
21
22
 
@@ -40,10 +41,10 @@ export function confidenceTier(confidence: number): ConfidenceTier {
40
41
  }
41
42
 
42
43
  /**
43
- * {@link confidenceTier} for a possibly-absent confidence: an unmeasured span is drawn as `mid` rather than `low`, so an
44
- * older model that emits no confidence never reads as uniformly wrong.
44
+ * {@link confidenceTier} for a possibly-absent confidence: an unmeasured span is drawn as `mid`
45
+ * rather than `low`, so an older model that emits no confidence never reads as uniformly wrong.
45
46
  */
46
- export function confidenceTierOrMid(confidence?: number): ConfidenceTier {
47
+ export function confidenceTierOrMid(confidence?: number | null): ConfidenceTier {
47
48
  if (confidence == null) return "mid"
48
49
 
49
50
  return confidenceTier(confidence)
package/lib/common/cx.ts CHANGED
@@ -11,7 +11,9 @@
11
11
  export type ClassValue = string | number | false | null | undefined | Record<string, boolean>
12
12
 
13
13
  /**
14
- * Join truthy class tokens; object entries contribute their key when the value is truthy.
14
+ * Join truthy class tokens.
15
+ *
16
+ * Object entries contribute their key when the value is truthy.
15
17
  */
16
18
  export function cx(...values: ClassValue[]): string {
17
19
  const out: string[] = []
@@ -10,13 +10,15 @@
10
10
  * the owner walk live here.
11
11
  */
12
12
 
13
+ import type { CharRange } from "@mailwoman/query-shape"
14
+
13
15
  export interface WordToken {
14
16
  /**
15
17
  * The word text as it appears in the input.
16
18
  */
17
19
  text: string
18
20
  /**
19
- * Leading whitespace before this word.
21
+ * Whitespace before this word.
20
22
  */
21
23
  whitespace: string
22
24
  /**
@@ -36,7 +38,9 @@ export function tokenizeWords(input: string): WordToken[] {
36
38
  const words: WordToken[] = []
37
39
  let i = 0
38
40
 
39
- // `charAt` answers "" past the end, which no whitespace test matches, so both walks stop at the input's length.
41
+ // `charAt` answers "" past the end.
42
+ // It no whitespace test matches.
43
+ // Therefore, both walks stop at the input's length.
40
44
  while (i < input.length) {
41
45
  let ws = ""
42
46
 
@@ -60,18 +64,11 @@ export function tokenizeWords(input: string): WordToken[] {
60
64
  }
61
65
 
62
66
  /**
63
- * A half-open `[start, end)` character range into the input.
64
- */
65
- export interface CharSpan {
66
- start: number
67
- end: number
68
- }
69
-
70
- /**
71
- * Per-word index of the most specific (shortest) span covering it, or `-1` when no span overlaps the word. A word is
72
- * covered when any part of it falls within the span.
67
+ * Per-word index of the most specific (shortest) span covering it, or `-1` when no span overlaps the word.
68
+ *
69
+ * A word is covered when any part of it falls within the span.
73
70
  */
74
- export function shortestSpanOwners(words: readonly CharSpan[], spans: readonly CharSpan[]): number[] {
71
+ export function shortestSpanOwners(words: readonly CharRange[], spans: readonly CharRange[]): number[] {
75
72
  return words.map((word) => {
76
73
  let best = -1
77
74
  let bestLen = Infinity
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `useBuildInfo()` — reads the deployment's own `build.json`, resolving a failed fetch to `null` because a dev server has no such file and the page must not show an error strip.
7
+ */
8
+
9
+ import { useEffect, useState } from "react"
10
+
11
+ export interface BuildInfoRecord {
12
+ /**
13
+ * The deployment's name: `mailwoman-earth`, `mailwoman-moon`, `mailwoman-mars`.
14
+ */
15
+ app: string
16
+ revision: string
17
+ /**
18
+ * The same revision, full length — what a commit URL wants.
19
+ */
20
+ commit?: string
21
+ /**
22
+ * ISO-8601 seconds, `Z` suffix.
23
+ */
24
+ buildTime: string
25
+ }
26
+
27
+ /**
28
+ * The deployment record, or `null` while it loads and whenever it cannot be read.
29
+ */
30
+ export function useBuildInfo(url = "/build.json"): BuildInfoRecord | null {
31
+ const [info, setInfo] = useState<BuildInfoRecord | null>(null)
32
+
33
+ useEffect(() => {
34
+ const controller = new AbortController()
35
+
36
+ void (async () => {
37
+ try {
38
+ const response = await fetch(url, { signal: controller.signal })
39
+
40
+ if (!response.ok) return
41
+
42
+ const record = (await response.json()) as BuildInfoRecord
43
+
44
+ if (!controller.signal.aborted && record?.revision) {
45
+ setInfo(record)
46
+ }
47
+ } catch {}
48
+ })()
49
+
50
+ return () => controller.abort()
51
+ }, [url])
52
+
53
+ return info
54
+ }
@@ -3,24 +3,17 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `useClipboard` — headless copy-to-clipboard with the async Clipboard API and a legacy
7
- * `execCommand` fallback (for insecure contexts / older browsers), plus a transient "copied" flag
8
- * that auto-resets. Extracted from the two explorers' duplicated `onCopy` handlers.
6
+ * `useClipboard` — headless copy-to-clipboard with the async Clipboard API and a legacy `execCommand` fallback for insecure contexts, plus a transient "copied" flag that auto-resets.
9
7
  */
10
8
 
11
9
  import { useCallback, useEffect, useRef, useState } from "react"
12
10
 
13
- /**
14
- * Best-effort clipboard write: async Clipboard API first, hidden-textarea `execCommand` fallback.
15
- */
16
11
  async function writeToClipboard(value: string): Promise<void> {
17
12
  try {
18
13
  await navigator.clipboard.writeText(value)
19
14
 
20
15
  return
21
- } catch {
22
- // Fall through to the legacy path below.
23
- }
16
+ } catch {}
24
17
 
25
18
  const textarea = document.createElement("textarea")
26
19
  textarea.value = value
@@ -32,7 +25,6 @@ async function writeToClipboard(value: string): Promise<void> {
32
25
  try {
33
26
  document.execCommand("copy")
34
27
  } catch {
35
- // Nothing else to try — leave `copied` false via the caller's error path.
36
28
  } finally {
37
29
  document.body.removeChild(textarea)
38
30
  }
@@ -50,7 +42,6 @@ export function useClipboard(resetMs = 1500): UseClipboard {
50
42
  const [copied, setCopied] = useState(false)
51
43
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
52
44
 
53
- // Clear a pending reset timer on unmount so it never fires against a torn-down component.
54
45
  useEffect(
55
46
  () => () => {
56
47
  if (timer.current) {
package/lib/common.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file Common React primitives.
6
+ */
7
+
8
+ export { ClientOnly } from "./common/ClientOnly.tsx"
9
+ export type { ClientOnlyProps } from "./common/ClientOnly.tsx"
10
+ export { CopyButton } from "./common/CopyButton.tsx"
11
+ export type { CopyButtonProps } from "./common/CopyButton.tsx"
12
+ export { cx } from "#common/cx"
13
+ export type { ClassValue } from "#common/cx"
14
+ export { KindBadge } from "./common/KindBadge.tsx"
15
+ export type { KindBadgeProps } from "./common/KindBadge.tsx"
16
+ export { LoadingIndicator } from "./common/LoadingIndicator.tsx"
17
+ export type { LoadingIndicatorProps, LoadingMode } from "./common/LoadingIndicator.tsx"
18
+ export { PresetChips } from "./common/PresetChips.tsx"
19
+ export type { Preset, PresetChipsProps } from "./common/PresetChips.tsx"
20
+ export { useClipboard } from "#common/useClipboard"
21
+ export type { UseClipboard } from "#common/useClipboard"
22
+ export { useDebouncedValue } from "#common/useDebouncedValue"