@mailwoman/react 10.0.0 → 10.1.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 (376) hide show
  1. package/README.md +17 -16
  2. package/fonts.css +97 -22
  3. package/lib/common/About.tsx +7 -7
  4. package/lib/common/ClientOnly.tsx +2 -14
  5. package/lib/common/CopyButton.tsx +3 -1
  6. package/lib/common/LoadingIndicator.tsx +4 -2
  7. package/lib/common/PresetChips.tsx +9 -8
  8. package/lib/common/confidence-tiers.ts +7 -6
  9. package/lib/common/cx.ts +3 -1
  10. package/lib/common/text-tokens.ts +7 -4
  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 -17
  15. package/lib/map/AppIdentity.tsx +85 -0
  16. package/lib/map/BackendControl.tsx +9 -10
  17. package/lib/map/CompareToggle.tsx +8 -31
  18. package/lib/map/DebugInfo.tsx +88 -0
  19. package/lib/map/Geocoder.tsx +60 -46
  20. package/lib/map/GeocoderControls.tsx +430 -134
  21. package/lib/map/GraticuleLayer.tsx +54 -0
  22. package/lib/map/MapCanvas.tsx +23 -12
  23. package/lib/map/MapChipRow.tsx +4 -8
  24. package/lib/map/MapCompass.tsx +15 -24
  25. package/lib/map/MapControlStack.tsx +8 -9
  26. package/lib/map/MapFooter.tsx +2 -16
  27. package/lib/map/MapProgressBar.tsx +10 -13
  28. package/lib/map/MapSearchBar.tsx +31 -17
  29. package/lib/map/MapSheet.tsx +24 -15
  30. package/lib/map/OverlayLayers.tsx +1 -1
  31. package/lib/map/PlaceAutocomplete.tsx +10 -21
  32. package/lib/map/PlaceMarker.tsx +4 -2
  33. package/lib/map/ResolvedPlaceLayers.tsx +5 -14
  34. package/lib/map/ResultCamera.tsx +8 -31
  35. package/lib/map/ResultOverlay.tsx +2 -12
  36. package/lib/map/ResultPanel.tsx +1 -20
  37. package/lib/map/VersionPicker.tsx +10 -19
  38. package/lib/map/fake-runtime.ts +10 -12
  39. package/lib/map/geometry.ts +14 -47
  40. package/lib/map/graticule.ts +80 -0
  41. package/lib/map/map-debug.ts +1 -1
  42. package/lib/map/place-render.ts +28 -60
  43. package/lib/map/types.ts +58 -104
  44. package/lib/map/useCompareState.ts +5 -4
  45. package/lib/map/useGeocode.ts +7 -5
  46. package/lib/map/useMapBearing.ts +13 -13
  47. package/lib/map/useMapLabelPick.ts +21 -30
  48. package/lib/map/useMapPlaceRender.ts +2 -6
  49. package/lib/map/usePlaceAutocomplete.ts +44 -50
  50. package/lib/map.ts +95 -0
  51. package/lib/pipeline/CandidatePicker.tsx +13 -5
  52. package/lib/pipeline/ConfidenceCell.tsx +1 -1
  53. package/lib/pipeline/FailureDiagnostic.tsx +8 -2
  54. package/lib/pipeline/QueryForm.tsx +5 -2
  55. package/lib/pipeline/ResolvedPlace.tsx +1 -1
  56. package/lib/pipeline/SpanHighlight.tsx +7 -5
  57. package/lib/pipeline/TimingPanel.tsx +1 -1
  58. package/lib/pipeline/TreeView.tsx +1 -1
  59. package/lib/pipeline/copy.ts +22 -25
  60. package/lib/pipeline/presets.ts +15 -13
  61. package/lib/pipeline/types.ts +21 -14
  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 +24 -15
  67. package/lib/poi/QueryInput.tsx +1 -1
  68. package/lib/poi/SubjectPanel.tsx +4 -3
  69. package/lib/poi/runtime.ts +5 -16
  70. package/lib/poi/types.ts +39 -27
  71. package/lib/poi/usePOISearch.ts +39 -44
  72. package/lib/poi.ts +39 -0
  73. package/lib/runtime/useReleaseRuntime.ts +125 -74
  74. package/lib/search/SearchModal.tsx +317 -0
  75. package/lib/search/search.css +144 -0
  76. package/lib/search/types.ts +67 -0
  77. package/lib/search.ts +11 -0
  78. package/out/common/About.d.ts +5 -5
  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 +3 -1
  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 +4 -2
  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 +9 -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 +7 -6
  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 +4 -3
  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 +42 -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/BackendControl.d.ts +9 -10
  128. package/out/map/BackendControl.d.ts.map +1 -1
  129. package/out/map/BackendControl.js +1 -1
  130. package/out/map/BackendControl.js.map +1 -1
  131. package/out/map/CompareToggle.d.ts +8 -31
  132. package/out/map/CompareToggle.d.ts.map +1 -1
  133. package/out/map/CompareToggle.js +4 -1
  134. package/out/map/CompareToggle.js.map +1 -1
  135. package/out/map/DebugInfo.d.ts +34 -0
  136. package/out/map/DebugInfo.d.ts.map +1 -0
  137. package/out/map/DebugInfo.js +39 -0
  138. package/out/map/DebugInfo.js.map +1 -0
  139. package/out/map/Geocoder.d.ts +41 -30
  140. package/out/map/Geocoder.d.ts.map +1 -1
  141. package/out/map/Geocoder.js +20 -36
  142. package/out/map/Geocoder.js.map +1 -1
  143. package/out/map/GeocoderControls.d.ts +14 -42
  144. package/out/map/GeocoderControls.d.ts.map +1 -1
  145. package/out/map/GeocoderControls.js +250 -38
  146. package/out/map/GeocoderControls.js.map +1 -1
  147. package/out/map/GraticuleLayer.d.ts +28 -0
  148. package/out/map/GraticuleLayer.d.ts.map +1 -0
  149. package/out/map/GraticuleLayer.js +25 -0
  150. package/out/map/GraticuleLayer.js.map +1 -0
  151. package/out/map/MapCanvas.d.ts +23 -12
  152. package/out/map/MapCanvas.d.ts.map +1 -1
  153. package/out/map/MapCanvas.js +5 -2
  154. package/out/map/MapCanvas.js.map +1 -1
  155. package/out/map/MapChipRow.d.ts +4 -8
  156. package/out/map/MapChipRow.d.ts.map +1 -1
  157. package/out/map/MapChipRow.js.map +1 -1
  158. package/out/map/MapCompass.d.ts +11 -20
  159. package/out/map/MapCompass.d.ts.map +1 -1
  160. package/out/map/MapCompass.js +5 -1
  161. package/out/map/MapCompass.js.map +1 -1
  162. package/out/map/MapControlStack.d.ts +8 -9
  163. package/out/map/MapControlStack.d.ts.map +1 -1
  164. package/out/map/MapControlStack.js +4 -1
  165. package/out/map/MapControlStack.js.map +1 -1
  166. package/out/map/MapFooter.d.ts +2 -15
  167. package/out/map/MapFooter.d.ts.map +1 -1
  168. package/out/map/MapFooter.js +0 -14
  169. package/out/map/MapFooter.js.map +1 -1
  170. package/out/map/MapProgressBar.d.ts +10 -13
  171. package/out/map/MapProgressBar.d.ts.map +1 -1
  172. package/out/map/MapProgressBar.js.map +1 -1
  173. package/out/map/MapSearchBar.d.ts +16 -17
  174. package/out/map/MapSearchBar.d.ts.map +1 -1
  175. package/out/map/MapSearchBar.js +6 -0
  176. package/out/map/MapSearchBar.js.map +1 -1
  177. package/out/map/MapSheet.d.ts +15 -12
  178. package/out/map/MapSheet.d.ts.map +1 -1
  179. package/out/map/MapSheet.js +8 -12
  180. package/out/map/MapSheet.js.map +1 -1
  181. package/out/map/OverlayLayers.d.ts +1 -1
  182. package/out/map/OverlayLayers.js +1 -1
  183. package/out/map/PlaceAutocomplete.d.ts +10 -21
  184. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  185. package/out/map/PlaceAutocomplete.js +2 -1
  186. package/out/map/PlaceAutocomplete.js.map +1 -1
  187. package/out/map/PlaceMarker.d.ts +4 -2
  188. package/out/map/PlaceMarker.d.ts.map +1 -1
  189. package/out/map/PlaceMarker.js +3 -1
  190. package/out/map/PlaceMarker.js.map +1 -1
  191. package/out/map/ResolvedPlaceLayers.d.ts +5 -14
  192. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  193. package/out/map/ResolvedPlaceLayers.js +1 -1
  194. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  195. package/out/map/ResultCamera.d.ts +8 -31
  196. package/out/map/ResultCamera.d.ts.map +1 -1
  197. package/out/map/ResultCamera.js +3 -28
  198. package/out/map/ResultCamera.js.map +1 -1
  199. package/out/map/ResultOverlay.d.ts +2 -12
  200. package/out/map/ResultOverlay.d.ts.map +1 -1
  201. package/out/map/ResultOverlay.js +1 -2
  202. package/out/map/ResultOverlay.js.map +1 -1
  203. package/out/map/ResultPanel.d.ts +1 -20
  204. package/out/map/ResultPanel.d.ts.map +1 -1
  205. package/out/map/ResultPanel.js.map +1 -1
  206. package/out/map/VersionPicker.d.ts +7 -19
  207. package/out/map/VersionPicker.d.ts.map +1 -1
  208. package/out/map/VersionPicker.js +5 -2
  209. package/out/map/VersionPicker.js.map +1 -1
  210. package/out/map/fake-runtime.d.ts +9 -11
  211. package/out/map/fake-runtime.d.ts.map +1 -1
  212. package/out/map/fake-runtime.js +9 -12
  213. package/out/map/fake-runtime.js.map +1 -1
  214. package/out/map/geometry.d.ts +13 -34
  215. package/out/map/geometry.d.ts.map +1 -1
  216. package/out/map/geometry.js +9 -34
  217. package/out/map/geometry.js.map +1 -1
  218. package/out/map/graticule.d.ts +17 -0
  219. package/out/map/graticule.d.ts.map +1 -0
  220. package/out/map/graticule.js +67 -0
  221. package/out/map/graticule.js.map +1 -0
  222. package/out/map/map-debug.d.ts +1 -1
  223. package/out/map/map-debug.js +1 -1
  224. package/out/map/place-render.d.ts +23 -43
  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 +58 -102
  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 +2 -6
  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 +69 -0
  257. package/out/map.d.ts.map +1 -0
  258. package/out/map.js +46 -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 +1 -1
  265. package/out/pipeline/ConfidenceCell.js +1 -1
  266. package/out/pipeline/FailureDiagnostic.d.ts +8 -2
  267. package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
  268. package/out/pipeline/FailureDiagnostic.js +3 -0
  269. package/out/pipeline/FailureDiagnostic.js.map +1 -1
  270. package/out/pipeline/QueryForm.d.ts +5 -2
  271. package/out/pipeline/QueryForm.d.ts.map +1 -1
  272. package/out/pipeline/QueryForm.js.map +1 -1
  273. package/out/pipeline/SpanHighlight.d.ts +4 -3
  274. package/out/pipeline/SpanHighlight.d.ts.map +1 -1
  275. package/out/pipeline/SpanHighlight.js +3 -2
  276. package/out/pipeline/SpanHighlight.js.map +1 -1
  277. package/out/pipeline/TimingPanel.d.ts +1 -1
  278. package/out/pipeline/TreeView.d.ts +1 -1
  279. package/out/pipeline/copy.d.ts +1 -1
  280. package/out/pipeline/copy.d.ts.map +1 -1
  281. package/out/pipeline/copy.js +4 -3
  282. package/out/pipeline/copy.js.map +1 -1
  283. package/out/pipeline/presets.d.ts +5 -4
  284. package/out/pipeline/presets.d.ts.map +1 -1
  285. package/out/pipeline/presets.js +15 -13
  286. package/out/pipeline/presets.js.map +1 -1
  287. package/out/pipeline/types.d.ts +21 -14
  288. package/out/pipeline/types.d.ts.map +1 -1
  289. package/out/pipeline/types.js +2 -4
  290. package/out/pipeline/types.js.map +1 -1
  291. package/out/pipeline/useParsePipeline.d.ts +14 -9
  292. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  293. package/out/pipeline/useParsePipeline.js +7 -8
  294. package/out/pipeline/useParsePipeline.js.map +1 -1
  295. package/out/pipeline.d.ts +33 -0
  296. package/out/pipeline.d.ts.map +1 -0
  297. package/out/pipeline.js +21 -0
  298. package/out/pipeline.js.map +1 -0
  299. package/out/poi/AbstainPanel.d.ts +1 -1
  300. package/out/poi/LiveResultsBlock.d.ts +2 -1
  301. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  302. package/out/poi/LiveResultsBlock.js.map +1 -1
  303. package/out/poi/POIExplorer.d.ts +22 -12
  304. package/out/poi/POIExplorer.d.ts.map +1 -1
  305. package/out/poi/POIExplorer.js +9 -8
  306. package/out/poi/POIExplorer.js.map +1 -1
  307. package/out/poi/QueryInput.d.ts +1 -1
  308. package/out/poi/SubjectPanel.d.ts +2 -2
  309. package/out/poi/SubjectPanel.d.ts.map +1 -1
  310. package/out/poi/SubjectPanel.js +2 -1
  311. package/out/poi/SubjectPanel.js.map +1 -1
  312. package/out/poi/runtime.d.ts +5 -14
  313. package/out/poi/runtime.d.ts.map +1 -1
  314. package/out/poi/runtime.js +5 -16
  315. package/out/poi/runtime.js.map +1 -1
  316. package/out/poi/types.d.ts +39 -27
  317. package/out/poi/types.d.ts.map +1 -1
  318. package/out/poi/types.js +2 -4
  319. package/out/poi/types.js.map +1 -1
  320. package/out/poi/usePOISearch.d.ts +28 -17
  321. package/out/poi/usePOISearch.d.ts.map +1 -1
  322. package/out/poi/usePOISearch.js +6 -32
  323. package/out/poi/usePOISearch.js.map +1 -1
  324. package/out/poi.d.ts +23 -0
  325. package/out/poi.d.ts.map +1 -0
  326. package/out/poi.js +15 -0
  327. package/out/poi.js.map +1 -0
  328. package/out/runtime/useReleaseRuntime.d.ts +64 -65
  329. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  330. package/out/runtime/useReleaseRuntime.js +31 -26
  331. package/out/runtime/useReleaseRuntime.js.map +1 -1
  332. package/out/{runtime/index.d.ts → runtime.d.ts} +1 -1
  333. package/out/runtime.d.ts.map +1 -0
  334. package/out/{runtime/index.js → runtime.js} +1 -1
  335. package/out/runtime.js.map +1 -0
  336. package/out/search/SearchModal.d.ts +33 -0
  337. package/out/search/SearchModal.d.ts.map +1 -0
  338. package/out/search/SearchModal.js +187 -0
  339. package/out/search/SearchModal.js.map +1 -0
  340. package/out/search/types.d.ts +64 -0
  341. package/out/search/types.d.ts.map +1 -0
  342. package/out/search/types.js +13 -0
  343. package/out/search/types.js.map +1 -0
  344. package/out/search.d.ts +11 -0
  345. package/out/search.d.ts.map +1 -0
  346. package/out/search.js +9 -0
  347. package/out/search.js.map +1 -0
  348. package/package.json +60 -142
  349. package/styles.css +482 -266
  350. package/tokens/index.css +24 -6
  351. package/tokens/tokens.json +114 -6
  352. package/lib/common/index.ts +0 -22
  353. package/lib/map/index.ts +0 -95
  354. package/lib/pipeline/index.ts +0 -36
  355. package/lib/poi/index.ts +0 -39
  356. package/out/common/index.d.ts +0 -22
  357. package/out/common/index.d.ts.map +0 -1
  358. package/out/common/index.js +0 -15
  359. package/out/common/index.js.map +0 -1
  360. package/out/map/index.d.ts +0 -69
  361. package/out/map/index.d.ts.map +0 -1
  362. package/out/map/index.js +0 -46
  363. package/out/map/index.js.map +0 -1
  364. package/out/pipeline/index.d.ts +0 -33
  365. package/out/pipeline/index.d.ts.map +0 -1
  366. package/out/pipeline/index.js +0 -21
  367. package/out/pipeline/index.js.map +0 -1
  368. package/out/poi/index.d.ts +0 -23
  369. package/out/poi/index.d.ts.map +0 -1
  370. package/out/poi/index.js +0 -15
  371. package/out/poi/index.js.map +0 -1
  372. package/out/runtime/index.d.ts.map +0 -1
  373. package/out/runtime/index.js.map +0 -1
  374. package/styleframe.config.ts +0 -329
  375. package/vitest.node.config.ts +0 -20
  376. /package/lib/{runtime/index.ts → runtime.ts} +0 -0
@@ -3,43 +3,31 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `useMapLabelPick` — clicking a place label on the map searches for it, the way the reference map apps behave.
7
- *
8
- * A label on a map looks like a link, so a visitor clicks it. Without this the click reaches the map's pan handler
9
- * and nothing happens, which reads as the label being decoration.
10
- *
11
- * It reads the label's OWN name from the rendered feature rather than reverse-geocoding the click point: the name
12
- * is what the visitor pointed at, and a lookup by coordinate answers whatever is nearest instead, which on a dense
13
- * basemap is regularly not the thing under the cursor.
14
- *
15
- * THE QUERY IS SCOPED, AND THE HOVER QUERY IS THROTTLED. `queryRenderedFeatures` with no `layers` walks the whole
16
- * style: measured at 64.3 ms per call returning 4,819 features over the 79-layer basemap at zoom 14 in Manhattan,
17
- * against 4.9 ms and 44 features scoped to that style's 11 label layers. At one call per pointer move the unscoped
18
- * form is the map's whole frame budget, so the layer list is resolved once per style and the hover query runs at
19
- * most once per animation frame. The click query is not throttled — there is one of those per click.
6
+ * A place-label click searches for its own name. The hook resolves the layer list once per style and throttles the hover query to one per animation frame.
20
7
  */
21
8
 
22
9
  import { useEffect, useEffectEvent } from "react"
23
10
  import type { MapInstance, MapLayerMouseEvent } from "react-map-gl/maplibre"
24
11
 
25
12
  /**
26
- * The layers whose features carry a place name. Protomaps names its label layers `<theme>_label` and its settlement
27
- * layers `places_*`; anything else in the style is geometry, not a label a visitor can read and point at.
13
+ * The protomaps label layers (`<theme>_label`, `places_*`); other layers are geometry
14
+ * rather than readable place names.
28
15
  */
29
16
  const LABEL_LAYER = /_label|^places_/
30
17
 
31
18
  /**
32
- * Properties a label carries its text under, in the order they are trusted. `name` is protomaps' own; the localized
33
- * variants appear on styles built for a specific script.
19
+ * Properties that may contain a label's text, in trust order.
20
+ *
21
+ * `name` comes from Protomaps.
22
+ * Localized variants appear in script-specific styles.
34
23
  */
35
24
  const NAME_KEYS = ["name", "name:en", "name_en"] as const
36
25
 
37
26
  /**
38
- * The style's label layers, by id.
27
+ * The style's label layer ids.
39
28
  *
40
- * A style with none answers an EMPTY ARRAY, and the caller must treat that as "no labels to pick" rather than passing
41
- * it to `queryRenderedFeatures` — an empty `layers` option is not the same as an absent one there, and the difference
42
- * between "this style has no labels" and "query everything" is the 64 ms this hook exists to avoid.
29
+ * An empty result must stay unqueried, since an empty `layers` option is not the
30
+ * same as an absent one in `queryRenderedFeatures`.
43
31
  */
44
32
  function labelLayerIDs(map: MapInstance): string[] {
45
33
  const layers = map.getStyle()?.layers ?? []
@@ -50,8 +38,7 @@ function labelLayerIDs(map: MapInstance): string[] {
50
38
  function labelNameAt(map: MapInstance, point: MapLayerMouseEvent["point"], layers: string[]): string | null {
51
39
  if (!layers.length) return null
52
40
 
53
- // `queryRenderedFeatures` answers in paint order with the topmost first, which is the label drawn over the others
54
- // and therefore the one a click landed on.
41
+ // `queryRenderedFeatures` answers topmost-first, so the first feature with a label is the one the click landed on.
55
42
  for (const feature of map.queryRenderedFeatures(point, { layers })) {
56
43
  for (const key of NAME_KEYS) {
57
44
  const value = feature.properties?.[key]
@@ -63,17 +50,20 @@ function labelNameAt(map: MapInstance, point: MapLayerMouseEvent["point"], layer
63
50
  return null
64
51
  }
65
52
 
53
+ /**
54
+ * Routes clicks on map labels to `onPick` with the clicked label's own name.
55
+ */
66
56
  export function useMapLabelPick(map: MapInstance | null, onPick: (name: string) => void): void {
67
- // The subscription depends on the MAP alone. `useGeocode` returns a fresh object every render, so a callback built
68
- // from it is new every render too — with `onPick` in the dependency list these map listeners were torn down and
69
- // re-added on every keystroke in the search field. `useEffectEvent` is the shape for exactly this: an event
70
- // handler that always sees the latest props without being a reactive dependency.
57
+ // The subscription depends only on the map.
58
+ // `onPick` changes on every render.
59
+ // `useEffectEvent` reads the latest callback without adding it as a reactive dependency.
71
60
  const pick = useEffectEvent((name: string) => onPick(name))
72
61
 
73
62
  useEffect(() => {
74
63
  if (!map) return
75
64
 
76
- // Recomputed when the style swaps (a theme change, a version switch) and not once per pointer move.
65
+ // Recomputed when the style swaps.
66
+ // Pointer moves read the cached list.
77
67
  let layers = labelLayerIDs(map)
78
68
 
79
69
  const readLayers = () => {
@@ -98,7 +88,8 @@ export function useMapLabelPick(map: MapInstance | null, onPick: (name: string)
98
88
 
99
89
  const canvas = map.getCanvas()
100
90
 
101
- // The drag cursor belongs to the pan gesture; overriding it mid-drag would fight the map for the pointer.
91
+ // The drag cursor belongs to the pan gesture.
92
+ // An override mid-drag would fight the map for the pointer.
102
93
  if (canvas.style.cursor === "grabbing") return
103
94
 
104
95
  canvas.style.cursor = labelNameAt(map, event.point, layers) ? "pointer" : ""
@@ -3,11 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `useMapPlaceRender` — the thin React memo wrapper over the pure {@link computeMapPlaceRenderSpec}. It
7
- * recomputes the render spec only when the resolved place changes, so the declarative overlays render a
8
- * stable spec object. The MATH lives in `place-render.ts` (pure, node-tested); this file adds nothing
9
- * but memoization, so it stays trivially correct. A `null` place (no result yet, or a result with no
10
- * candidate) yields `null` — the overlays render nothing.
6
+ * The thin React memo wrapper over the pure {@link computeMapPlaceRenderSpec}; a `null` place yields `null`.
11
7
  */
12
8
 
13
9
  import { useMemo } from "react"
@@ -16,7 +12,7 @@ import { computeMapPlaceRenderSpec } from "#map/place-render"
16
12
  import type { MapPlaceRenderSpec, ResolvedMapPlace } from "#map/place-render"
17
13
 
18
14
  /**
19
- * Memoize the render spec for a resolved place; `null` in → `null` out (nothing to draw).
15
+ * Memoize the render spec for a resolved place; `null` in → `null` out (no geometry to draw).
20
16
  */
21
17
  export function useMapPlaceRender(place: ResolvedMapPlace | null | undefined): MapPlaceRenderSpec | null {
22
18
  return useMemo(() => (place ? computeMapPlaceRenderSpec(place) : null), [place])
@@ -2,17 +2,6 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * `usePlaceAutocomplete` — the headless "Did you mean" combobox lifted from the demo god component
7
- * (`_app.tsx:820-900`). It walks the host-supplied `autocomplete` over the locality segment the visitor
8
- * is typing (the text after the last comma), owns the suggestion list + keyboard-highlighted active
9
- * descendant, and rewrites the input on pick (replacing just that segment). No FST, no fetch of its own
10
- * — the `autocomplete` fetcher is INJECTED (the package never imports `@mailwoman/resolver-wof-sqlite`),
11
- * so this stays node-safe and testable with a synchronous fake.
12
- *
13
- * Returns the `<input>` aria/combobox props to spread onto the reused {@link QueryForm} input plus an
14
- * `onInputKeyDown` (↑/↓ move the highlight, Enter accepts it AND suppresses submit, Esc dismisses), and
15
- * the presentational {@link PlaceAutocomplete} listbox renders `suggestions` / `activeIndex`.
16
5
  */
17
6
 
18
7
  import { type KeyboardEvent, useCallback, useEffect, useState } from "react"
@@ -20,31 +9,41 @@ import { type KeyboardEvent, useCallback, useEffect, useState } from "react"
20
9
  import { useDebouncedValue } from "#common/useDebouncedValue"
21
10
  import type { Suggestion } from "#map/types"
22
11
 
12
+ /**
13
+ * Options for {@linkcode usePlaceAutocomplete}.
14
+ */
23
15
  export interface UsePlaceAutocompleteOptions {
24
16
  /**
25
- * The current input text.
17
+ * The controlled input text.
18
+ * The segment after the last comma is the query.
26
19
  */
27
20
  text: string
21
+
28
22
  /**
29
- * Setter for the input text (a pick rewrites the last-comma segment).
23
+ * Sets the input text.
24
+ * A pick calls it with the last segment replaced.
30
25
  */
31
26
  setText: (text: string) => void
27
+
32
28
  /**
33
- * The host's autocomplete fetcher (FST prefix-walk). Absent → the combobox is inert.
29
+ * Fetches suggestions for a query.
30
+ * Without it the hook never suggests anything.
34
31
  */
35
32
  autocomplete?: (query: string) => Promise<Suggestion[]>
33
+
36
34
  /**
37
- * Minimum query length before suggesting. @default 2
35
+ * @default 2
38
36
  */
39
37
  minChars?: number
38
+
40
39
  /**
41
- * Debounce before firing the fetcher. @default 150
40
+ * @default 150
42
41
  */
43
42
  debounceMs?: number
44
43
  }
45
44
 
46
45
  /**
47
- * The combobox aria props to spread onto the input the suggestions describe.
46
+ * The combobox ARIA props to spread onto the input.
48
47
  */
49
48
  export interface AutocompleteInputProps {
50
49
  role: "combobox"
@@ -55,68 +54,73 @@ export interface AutocompleteInputProps {
55
54
  autoComplete: "off"
56
55
  }
57
56
 
57
+ /**
58
+ * The state and handlers that {@linkcode usePlaceAutocomplete} returns.
59
+ */
58
60
  export interface UsePlaceAutocomplete {
59
61
  /**
60
- * The current suggestions (empty when nothing matches — the listbox then hides).
62
+ * The current suggestions, empty when none match or the visitor dismissed the list.
61
63
  */
62
64
  suggestions: Suggestion[]
65
+
63
66
  /**
64
- * The keyboard-highlighted suggestion index; `-1` when none is highlighted.
67
+ * The index highlighted by the keyboard, or `-1` for none.
65
68
  */
66
69
  activeIndex: number
70
+
67
71
  /**
68
- * Set the highlighted index (the listbox calls this on mouse-enter).
72
+ * Sets the highlighted index, such as when the pointer enters an option.
69
73
  */
70
74
  setActiveIndex: (index: number) => void
75
+
71
76
  /**
72
- * Keydown handler for the input: ↑/↓ highlight, Enter accepts (+ suppresses submit), Esc dismisses.
77
+ * Handles input keys: arrows move the highlight, Enter picks instead of submitting and Escape dismisses.
73
78
  */
74
79
  onInputKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void
80
+
75
81
  /**
76
- * Accept a suggestion by value (rewrites the last-comma segment, closes the list).
82
+ * Replaces the text after the last comma with the suggestion and closes the list.
77
83
  */
78
84
  pick: (value: string) => void
85
+
79
86
  /**
80
- * Close the list without picking.
87
+ * Closes the list without picking until the query changes.
81
88
  */
82
89
  dismiss: () => void
90
+
83
91
  /**
84
- * Aria/combobox props for the input the suggestions describe.
92
+ * The combobox ARIA props to spread onto the input.
85
93
  */
86
94
  inputProps: AutocompleteInputProps
95
+
87
96
  /**
88
- * The listbox element id (matches `inputProps["aria-controls"]`).
97
+ * The listbox element ID.
98
+ * It matches `inputProps["aria-controls"]`.
89
99
  */
90
100
  listboxID: string
91
- /**
92
- * Build the option element id for suggestion `index`.
93
- */
101
+
94
102
  optionID: (index: number) => string
95
103
  }
96
104
 
97
105
  const LISTBOX_ID = "mw-demo-suggest-list"
98
106
 
99
- /**
100
- * Stable empty list for the derived no-suggestions state — a fresh `[]` per render would churn the identity every
101
- * consumer and every useCallback dependency list sees.
102
- */
103
107
  const NO_SUGGESTIONS: Suggestion[] = []
104
108
  const optionID = (index: number) => `mw-demo-suggest-${index}`
105
109
 
106
- /**
107
- * Extract the locality segment being typed — the text after the last comma, trimmed.
108
- */
109
110
  function localitySegment(text: string): string {
110
111
  return (text.includes(",") ? text.slice(text.lastIndexOf(",") + 1) : text).trim()
111
112
  }
112
113
 
113
- /**
114
- * Replace the locality segment (after the last comma) with `name`, preserving the address prefix.
115
- */
116
114
  function replaceSegment(current: string, name: string): string {
117
115
  return current.includes(",") ? `${current.slice(0, current.lastIndexOf(",") + 1)} ${name}` : name
118
116
  }
119
117
 
118
+ /**
119
+ * Fetches debounced place suggestions for the text after the input's last comma.
120
+ *
121
+ * A pick replaces only that segment.
122
+ * A digit-leading segment never reaches `autocomplete`.
123
+ */
120
124
  export function usePlaceAutocomplete({
121
125
  text,
122
126
  setText,
@@ -124,18 +128,8 @@ export function usePlaceAutocomplete({
124
128
  minChars = 2,
125
129
  debounceMs = 150,
126
130
  }: UsePlaceAutocompleteOptions): UsePlaceAutocomplete {
127
- /**
128
- * The last COMPLETED fetch, keyed by the query that produced it. Visibility is DERIVED from this during render rather
129
- * than pushed through sync setStates in the effect — the effect's only job is the async fetch, so every state write
130
- * in it happens after an await and the react(set-state-in-effect) rule is satisfied by structure, not by exception.
131
- */
132
131
  const [fetched, setFetched] = useState<{ query: string; suggestions: Suggestion[] } | null>(null)
133
- /**
134
- * The query whose list was dismissed or picked. Suppression as DATA, replacing the one-shot ref flag: a pick stores
135
- * the query the rewritten text will produce, so the post-pick fetch is skipped and the place just chosen is never
136
- * re-suggested. One divergence from the ref version, accepted and small: after Esc, retyping the exact same string
137
- * keeps the list hidden until the query changes.
138
- */
132
+
139
133
  const [dismissed, setDismissed] = useState<string | null>(null)
140
134
  const [activeIndex, setActiveIndex] = useState(-1)
141
135
 
@@ -178,7 +172,7 @@ export function usePlaceAutocomplete({
178
172
  const next = replaceSegment(text, value)
179
173
 
180
174
  setText(next)
181
- // Suppress the fetch the rewritten text would trigger — the segment being typed IS the picked name.
175
+
182
176
  setDismissed(localitySegment(next))
183
177
  setFetched(null)
184
178
  setActiveIndex(-1)
package/lib/map.ts ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `@mailwoman/react/map` — the geocoder map surface, kept behind its own subpath so `maplibre-gl`
7
+ * / `react-map-gl` (WebGL + DOM at import) never enter the package-root graph. This subpath import
8
+ * pulls the map deps. importing `@mailwoman/react` (root) does not. Consumers who only want the
9
+ * parse/POI explorers never pay for maplibre.
10
+ *
11
+ * The host must supply `maplibre-gl` + `react-map-gl` (peer deps) and import
12
+ * `maplibre-gl/dist/maplibre-gl.css` + `@mailwoman/react/styles.css` itself.
13
+ */
14
+
15
+ export { MapCanvas } from "./map/MapCanvas.tsx"
16
+ export type { MapCanvasExtraProps, MapCanvasProps, MapCanvasStyle } from "./map/MapCanvas.tsx"
17
+
18
+ // ── Map chrome (node-safe presentation. The host supplies the input and reads its own map) ──
19
+ export { MapChipRow } from "./map/MapChipRow.tsx"
20
+ export type { MapChip, MapChipRowProps } from "./map/MapChipRow.tsx"
21
+ export { MapCompass } from "./map/MapCompass.tsx"
22
+ export type { MapCompassProps } from "./map/MapCompass.tsx"
23
+ export { useMapBearing } from "./map/useMapBearing.ts"
24
+ export type { UseMapBearing } from "./map/useMapBearing.ts"
25
+ export { useMapLabelPick } from "./map/useMapLabelPick.ts"
26
+ export { MapControlButton, MapControlGroup, MapControlStack } from "./map/MapControlStack.tsx"
27
+ export type { MapControlButtonProps, MapControlGroupProps, MapControlStackProps } from "./map/MapControlStack.tsx"
28
+ export { MapFooter } from "./map/MapFooter.tsx"
29
+ export type { MapFooterProps } from "./map/MapFooter.tsx"
30
+ export { MapProgressBar } from "./map/MapProgressBar.tsx"
31
+ export type { MapProgressBarProps } from "./map/MapProgressBar.tsx"
32
+ export { MapSheet, SheetClose } from "./map/MapSheet.tsx"
33
+ export type { MapSheetProps } from "./map/MapSheet.tsx"
34
+ export { MapSearchBar, SearchGlyph } from "./map/MapSearchBar.tsx"
35
+ export type { MapSearchBarProps } from "./map/MapSearchBar.tsx"
36
+
37
+ export type {
38
+ CompareContext,
39
+ GeocoderPanels,
40
+ GeocoderRuntime,
41
+ InferenceBackend,
42
+ LngLatTuple,
43
+ MapBias,
44
+ OverlaySpec,
45
+ ResultContext,
46
+ Suggestion,
47
+ VersionOption,
48
+ } from "#map/types"
49
+
50
+ // ── Pure geometry + render spec (node-safe. No react-map-gl at runtime) ──────
51
+ export { approxCircleGeometry, bboxToBounds, geomBounds, radiusCircleGeometry } from "#map/geometry"
52
+ export type { BoundsTuple, PlaceGeometry } from "#map/geometry"
53
+ export { cameraToViewState, computeMapPlaceRenderSpec } from "#map/place-render"
54
+ export type { LngLat, MapCameraTarget, MapPlaceRenderSpec, PlaceTier, ResolvedMapPlace } from "#map/place-render"
55
+ export { useMapPlaceRender } from "#map/useMapPlaceRender"
56
+
57
+ // ── Declarative overlays (react-map-gl `<Marker>`/`<Source>`/`<Layer>`) ──────
58
+ export { OverlayLayers } from "./map/OverlayLayers.tsx"
59
+ export type { OverlayLayersProps } from "./map/OverlayLayers.tsx"
60
+ export { PlaceMarker } from "./map/PlaceMarker.tsx"
61
+ export type { PlaceMarkerProps } from "./map/PlaceMarker.tsx"
62
+ export { ResolvedPlaceLayers } from "./map/ResolvedPlaceLayers.tsx"
63
+ export type { ResolvedPlaceLayersProps } from "./map/ResolvedPlaceLayers.tsx"
64
+ export { ResultCamera } from "./map/ResultCamera.tsx"
65
+ export type { ResultCameraProps } from "./map/ResultCamera.tsx"
66
+ export { ResultOverlay } from "./map/ResultOverlay.tsx"
67
+ export type { ResultOverlayProps } from "./map/ResultOverlay.tsx"
68
+
69
+ // ── Geocoder controls + the composed geocoder ────────────────────────────────
70
+ export { BackendControl } from "./map/BackendControl.tsx"
71
+ export type { BackendControlProps } from "./map/BackendControl.tsx"
72
+ export { CompareToggle } from "./map/CompareToggle.tsx"
73
+ export type { CompareToggleProps } from "./map/CompareToggle.tsx"
74
+ export { Geocoder } from "./map/Geocoder.tsx"
75
+ export type { GeocoderProps } from "./map/Geocoder.tsx"
76
+ export { GeocoderControls } from "./map/GeocoderControls.tsx"
77
+ export type { GeocoderControlsProps } from "./map/GeocoderControls.tsx"
78
+ export { PlaceAutocomplete } from "./map/PlaceAutocomplete.tsx"
79
+ export type { PlaceAutocompleteProps } from "./map/PlaceAutocomplete.tsx"
80
+ export { ResultPanel } from "./map/ResultPanel.tsx"
81
+ export type { ResultPanelProps } from "./map/ResultPanel.tsx"
82
+ export { useCompareState } from "#map/useCompareState"
83
+ export type { UseCompareState } from "#map/useCompareState"
84
+ export { useGeocode } from "#map/useGeocode"
85
+ export type { UseGeocode, UseGeocodeOptions } from "#map/useGeocode"
86
+ export { usePlaceAutocomplete } from "#map/usePlaceAutocomplete"
87
+
88
+ export type {
89
+ AutocompleteInputProps,
90
+ UsePlaceAutocomplete,
91
+ UsePlaceAutocompleteOptions,
92
+ } from "#map/usePlaceAutocomplete"
93
+
94
+ export { VersionPicker } from "./map/VersionPicker.tsx"
95
+ export type { VersionPickerProps } from "./map/VersionPicker.tsx"
@@ -3,8 +3,18 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `CandidatePicker` — the ranked list of alternate gazetteer candidates; clicking one selects it.
7
- * Presentational; the selected index is owned by the caller (the pipeline hook).
6
+ * `CandidatePicker` — the ranked list of alternate gazetteer candidates. clicking one selects it.
7
+ * Presentational. the selected index is owned by the caller (the pipeline hook).
8
+ *
9
+ * The row shows the rank rather than the score. `ResolvedPlaceView.score` is implementation-defined. Its type specifies
10
+ * "callers should treat as ordinal" — and the backends do not agree on a scale: the FTS regime is a negated bm25
11
+ * plus additive tiers, while the candidate regime is exactly `log10(population + 1)`. Both regimes put
12
+ * "New York · 6.95" (log10 of 8.9M) directly under "350 5th Ave · 1.00" from a bounded blend. That reads as one
13
+ * number beating another by a factor of seven rather than as two numbers that never shared a scale.
14
+ * `docs/records/reviews/2026-08-04-resolver-score-abstention.md` measures this in full.
15
+ *
16
+ * The raw value stays on the button's `title`, at full precision, because it is exactly the right thing to have
17
+ * when you are debugging a ranking and exactly the wrong thing to put in a visitor's eyeline.
8
18
  */
9
19
 
10
20
  import type { ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
@@ -33,9 +43,7 @@ export function CandidatePicker({ candidates, selectedIndex, onSelect }: Candida
33
43
  >
34
44
  <span className="mw-candidates__rank">#{i + 1}</span>
35
45
  <span className="mw-candidates__name">{candidate.name}</span>
36
- <span className="mw-candidates__meta">
37
- {candidate.placetype} · {candidate.score.toFixed(2)}
38
- </span>
46
+ <span className="mw-candidates__meta">{candidate.placetype}</span>
39
47
  </button>
40
48
  </li>
41
49
  ))}
@@ -3,7 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `ConfidenceCell` — a compact confidence bar + value for the component table. The fill colour follows the shared
6
+ * `ConfidenceCell` — a compact confidence bar + value for the component table. The fill color follows the shared
7
7
  * confidence tiers. Presentational.
8
8
  */
9
9
 
@@ -3,17 +3,23 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `FailureDiagnostic` — what to say when nothing resolved. It reads the parsed components and names the missing
7
- * piece a person can add (a city, a state), so the answer is a next step rather than "no hit".
6
+ * Explains a query that resolved to no place and suggests which component, such as a city or a
7
+ * state, the visitor could add.
8
8
  */
9
9
 
10
10
  import type { ParsedComponent } from "@mailwoman/core/pipeline/client-result"
11
11
  import type { ReactNode } from "react"
12
12
 
13
+ /**
14
+ * Props for {@link FailureDiagnostic}.
15
+ */
13
16
  export interface FailureDiagnosticProps {
14
17
  nodes: ParsedComponent[]
15
18
  }
16
19
 
20
+ /**
21
+ * Renders hints based on which components the parser found.
22
+ */
17
23
  export function FailureDiagnostic({ nodes }: FailureDiagnosticProps): ReactNode {
18
24
  const hasLocality = nodes.some((n) => n.tag === "locality")
19
25
  const hasPostcode = nodes.some((n) => n.tag === "postcode")
@@ -19,11 +19,14 @@ export interface QueryFormProps {
19
19
  busy?: boolean
20
20
  placeholder?: string
21
21
  /**
22
- * Keydown on the input — the hook a combobox (the demo's place-autocomplete) needs for arrow/Enter/Esc nav.
22
+ * Keydown on the input — the hook a combobox (the demo's place-autocomplete)
23
+ * needs for arrow/Enter/Esc nav.
23
24
  */
24
25
  onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void
25
26
  /**
26
- * Override for extra `<input>` attributes (aria-combobox props, `autoComplete`, …). Merged last.
27
+ * Override for extra `<input>` attributes (aria-combobox props, `autoComplete`, …).
28
+ *
29
+ * Merged last.
27
30
  */
28
31
  inputProps?: InputHTMLAttributes<HTMLInputElement>
29
32
  }
@@ -25,7 +25,7 @@ export function ResolvedPlace({ place, dualRoles }: ResolvedPlaceProps): ReactNo
25
25
  <dt>placetype</dt>
26
26
  <dd>{place.placetype}</dd>
27
27
  {place.tier ? (
28
- // A street-tier answer is not a gazetteer place: it carries a precision instead of an id.
28
+ // A street-tier answer is not a gazetteer place: it has a precision instead of an id.
29
29
  <>
30
30
  <dt>precision</dt>
31
31
  <dd>
@@ -4,8 +4,8 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * `SpanHighlight` — a displaCy-style span ribbon over the raw input: each tagged span is a column with the text on
7
- * top, tinted by its confidence tier, and the tag beneath. Gaps (delimiters, unparsed characters) fall through as
8
- * plain text, so a dropped span is visible as a literal break in the colour.
7
+ * top, tinted by its confidence tier, with the tag beneath. Gaps (delimiters, unparsed characters) fall through as
8
+ * plain text, so a dropped span is visible as a literal break in the color.
9
9
  */
10
10
 
11
11
  import type { ParsedComponent } from "@mailwoman/core/pipeline/client-result"
@@ -20,7 +20,8 @@ export interface SpanHighlightProps {
20
20
  */
21
21
  input: string
22
22
  /**
23
- * Flattened parse nodes; only those with numeric `start`/`end` are rendered.
23
+ * Flattened parse nodes.
24
+ * Only those with numeric `start`/`end` are rendered.
24
25
  */
25
26
  nodes: ParsedComponent[]
26
27
  }
@@ -47,8 +48,9 @@ export function SpanHighlight({ input, nodes }: SpanHighlightProps): ReactNode {
47
48
 
48
49
  if (!spans.length) return null
49
50
 
50
- // Per-character owner: the most specific (shortest) span covering it, the same rule the word-level panels apply
51
- // per word. The leaf always wins whatever nesting the tree hands over, so every character renders once.
51
+ // Per-character owner: the most specific (shortest) span covering it,
52
+ // the same rule the word-level panels apply per word.
53
+ // The leaf always wins whatever nesting the tree hands over, so every character renders once.
52
54
  const characters = Array.from({ length: input.length }, (_, i) => ({ start: i, end: i + 1 }))
53
55
  const owner = shortestSpanOwners(characters, spans)
54
56
 
@@ -4,7 +4,7 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * `TimingPanel` — the per-stage timing breakdown: a stacked bar sized by each stage's wall-clock, plus a legend.
7
- * Stage colours are distinct hues, never the confidence red/amber/green, because they encode the pipeline stage and
7
+ * Stage colors are distinct hues, never the confidence red/amber/green, because they encode the pipeline stage and
8
8
  * not quality.
9
9
  */
10
10
 
@@ -4,7 +4,7 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * `TreeView` — the parse's containment tree as nested lists with a guide rail per level. The tag tint follows the
7
- * confidence tier; the value and score render in monospace.
7
+ * confidence tier. the value and score render in monospace.
8
8
  */
9
9
 
10
10
  import type { ReactNode } from "react"
@@ -4,34 +4,31 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Builds the "Copy JSON" payload for a parse result — the components + the selected resolved place,
7
- * pretty-printed. Pure; shared by the explorer's copy button.
7
+ * pretty-printed. Pure. shared by the explorer's copy button.
8
8
  */
9
9
 
10
+ import { prettyJSON } from "@mailwoman/core/json"
10
11
  import type { ParseResult, ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
11
12
 
12
13
  export function buildParsePayload(result: ParseResult, selected: ResolvedPlaceView | null): string {
13
- return JSON.stringify(
14
- {
15
- input: result.input,
16
- components: result.nodes.map((node) => ({
17
- tag: node.tag,
18
- value: node.value ?? null,
19
- confidence: node.confidence ?? null,
20
- start: node.start ?? null,
21
- end: node.end ?? null,
22
- })),
23
- resolved: selected
24
- ? {
25
- name: selected.name,
26
- placetype: selected.placetype,
27
- id: selected.id,
28
- lat: selected.lat,
29
- lon: selected.lon,
30
- score: selected.score,
31
- }
32
- : null,
33
- },
34
- null,
35
- 2
36
- )
14
+ return prettyJSON({
15
+ input: result.input,
16
+ components: result.nodes.map((node) => ({
17
+ tag: node.tag,
18
+ value: node.value ?? null,
19
+ confidence: node.confidence ?? null,
20
+ start: node.start ?? null,
21
+ end: node.end ?? null,
22
+ })),
23
+ resolved: selected
24
+ ? {
25
+ name: selected.name,
26
+ placetype: selected.placetype,
27
+ id: selected.id,
28
+ lat: selected.lat,
29
+ lon: selected.lon,
30
+ score: selected.score,
31
+ }
32
+ : null,
33
+ })
37
34
  }