@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/lib/poi/types.ts CHANGED
@@ -3,22 +3,27 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Types for the POI explorer. The intent path (classification → subject → OverpassQL) is fully
7
- * self-contained over the pure `@mailwoman/*` packages; the live poi.db path is expressed only as an
8
- * INJECTED async function ({@link POILiveSearch}) so the httpvfs/worker implementation stays out of this
9
- * package's browser graph (it lives in the docs site, which knows where the layer is served).
6
+ * Types for the POI explorer. Live poi.db search is injected as {@link POILiveSearch} so the httpvfs
7
+ * worker stays out of this package's browser graph.
10
8
  */
11
9
 
12
10
  import type { QueryKindResult } from "@mailwoman/core/pipeline"
11
+ import type { POIDistanceHit } from "@mailwoman/core/resolver"
13
12
  import type { POIPhraseLookup, createKindClassifier } from "@mailwoman/kind-classifier"
14
13
  import type { createPOITaxonomyLookup } from "@mailwoman/poi-taxonomy/table"
15
14
 
15
+ /**
16
+ * The POI taxonomy lookup that the explorer runtime loads.
17
+ */
16
18
  export type TaxonomyLookup = ReturnType<typeof createPOITaxonomyLookup>
17
19
 
20
+ /**
21
+ * One taxonomy category record.
22
+ */
18
23
  export type CategoryRecord = NonNullable<ReturnType<TaxonomyLookup["getPOICategory"]>>
19
24
 
20
25
  /**
21
- * The lazily-loaded POI runtime: the taxonomy lookup, the adapted lexicon, and the kind classifier over it.
26
+ * The lazily loaded POI runtime holds the taxonomy lookup, the lexicon and the kind classifier.
22
27
  */
23
28
  export interface POIRuntime {
24
29
  lookup: TaxonomyLookup
@@ -33,27 +38,27 @@ export interface POISubjectBase {
33
38
  matchedPhrase: string
34
39
  confidence: number
35
40
  /**
36
- * The non-subject remainder of the query (the location anchor, e.g. "near Springfield").
41
+ * The rest of the query after the subject, usually the location anchor such as "near Springfield".
37
42
  */
38
43
  remainder: string
39
44
  }
40
45
 
41
46
  /**
42
- * A resolved POI subject that names a taxonomy CATEGORY (`cafe`, `hospital`, `drinking fountain`).
47
+ * A resolved POI subject that matched a taxonomy category such as `cafe` or `hospital`.
43
48
  */
44
49
  export interface POICategorySubject extends POISubjectBase {
45
50
  kind: "category"
46
51
  category: CategoryRecord
47
52
  /**
48
- * Whether this category needs the locally-built OSM (ODbL) layer — precomputed off the runtime.
53
+ * Whether this category requires the locally built OSM (ODbL) layer.
49
54
  */
50
55
  buildLocal: boolean
51
56
  }
52
57
 
53
58
  /**
54
- * A resolved POI subject that names a chain BRAND (`chevron`, `applebee's`). Brands carry a Wikidata QID and are
55
- * searched by that QID, NOT by category k-ring — see `@mailwoman/poi-taxonomy`'s brand table + the layer's
56
- * `brand_wikidata` index.
59
+ * A resolved POI subject that matched a chain brand such as `chevron`.
60
+ *
61
+ * Live search looks up a brand by its Wikidata QID instead of by category k-ring.
57
62
  */
58
63
  export interface POIBrandSubject extends POISubjectBase {
59
64
  kind: "brand"
@@ -62,41 +67,39 @@ export interface POIBrandSubject extends POISubjectBase {
62
67
  */
63
68
  name: string
64
69
  /**
65
- * Wikidata QID, when the lexicon carried one (`Q319642` = Chevron). Absent ⇒ matched by name alone.
70
+ * The brand's Wikidata QID.
71
+ * It is `null` when the lexicon had no QID for the brand.
66
72
  */
67
- wikidata?: string
73
+ wikidata: string | null
68
74
  }
69
75
 
70
76
  /**
71
- * A resolved POI subject — a taxonomy category or a chain brand — plus its match metadata.
77
+ * A resolved POI subject, either a taxonomy category or a chain brand.
72
78
  */
73
79
  export type POISubject = POICategorySubject | POIBrandSubject
74
80
 
75
81
  /**
76
- * The intent-only result: the kind verdict plus (when a subject was detected) its OverpassQL export (category only).
82
+ * The intent-only result.
83
+ *
84
+ * It holds the kind verdict and, when a category subject was detected, its OverpassQL export.
77
85
  */
78
86
  export interface POIExplorerResult {
79
87
  kindResult: QueryKindResult
80
- subject?: POISubject
81
- overpassQL?: string
82
- overpassError?: string
88
+ subject: POISubject | null
89
+ overpassQL: string | null
90
+ overpassError: string | null
83
91
  }
84
92
 
85
93
  /**
86
94
  * One live poi.db hit, as the results list renders it.
87
95
  */
88
- export interface POISearchHit {
89
- name: string
90
- lat: number
91
- lon: number
92
- distanceM: number
93
- country: string
94
- confidence: number
95
- }
96
+ export type POISearchHit = POIDistanceHit
96
97
 
97
98
  /**
98
- * Result of an injected live search. Preserves the original tester's two failure modes — the anchor not resolving vs
99
- * the published layer being unreachable — so the UI can word them differently.
99
+ * The result of an injected live search.
100
+ *
101
+ * The `unplaced` and `unavailable` states stay separate so the UI can tell an
102
+ * unresolved anchor apart from an unreachable layer.
100
103
  */
101
104
  export type POILiveSearchResult =
102
105
  | { status: "success"; hits: POISearchHit[]; centerName: string }
@@ -104,28 +107,35 @@ export type POILiveSearchResult =
104
107
  | { status: "unavailable" }
105
108
 
106
109
  /**
107
- * The injected live-search function. Given the resolved category (+ its Overture leaf fan-out) and the anchor text, it
108
- * probes the published poi.db and returns hits. Absent ⇒ the explorer runs intent-only (no "Search live" button).
110
+ * What a live search looks for near `anchor`: a taxonomy category with its Overture
111
+ * leaf categories, or a chain brand identified by its Wikidata QID.
112
+ */
113
+ export type POILiveSearchRequest =
114
+ | {
115
+ kind: "category"
116
+ categoryID: string
117
+ overtureCategoryIDs: string[]
118
+ anchor: string
119
+ }
120
+ | {
121
+ kind: "brand"
122
+ brandName: string
123
+ wikidata: string
124
+ anchor: string
125
+ }
126
+
127
+ /**
128
+ * The injected live-search function.
129
+ *
130
+ * It probes the published poi.db for the request's subject and the anchor text.
131
+ * The explorer runs intent-only when this function is absent.
109
132
  *
110
- * Brand support is ADDITIVE: when the resolved subject is a chain brand, `brandWikidata` carries its QID and the probe
111
- * fetches by that QID instead of a category k-ring (`categoryID`/`overtureCategoryIDs` are then the brand name / empty
112
- * and unused). The category path is byte-identical to before. A probe that can't serve brands simply leaves brand live
113
- * search unwired at the call site (see `usePOISearch`'s `brandLiveSearch` option) — the docs' httpvfs probe does
114
- * exactly that, brand-wide row hydration being pathological over byte-range (measured; the brand path is server-side
115
- * only).
133
+ * A probe that cannot serve brands should leave `usePOISearch`'s `brandLiveSearch` option unset.
116
134
  */
117
- export type POILiveSearch = (params: {
118
- categoryID: string
119
- overtureCategoryIDs: string[]
120
- anchor: string
121
- /**
122
- * Present when the subject is a chain brand — the probe fetches by this QID, not a category k-ring.
123
- */
124
- brandWikidata?: string
125
- }) => Promise<POILiveSearchResult>
135
+ export type POILiveSearch = (request: POILiveSearchRequest) => Promise<POILiveSearchResult>
126
136
 
127
137
  /**
128
- * "Search live" state machine.
138
+ * The state of a "Search live" request.
129
139
  */
130
140
  export type LiveSearchState =
131
141
  | { status: "idle" }
@@ -134,6 +144,7 @@ export type LiveSearchState =
134
144
  | { status: "success"; hits: POISearchHit[]; centerName: string }
135
145
 
136
146
  /**
137
- * A single-argument runtime loader — injectable so stories/tests can substitute a mock taxonomy.
147
+ * A runtime loader.
148
+ * Stories and tests inject one to substitute a mock taxonomy.
138
149
  */
139
150
  export type LoadPOIRuntime = () => Promise<POIRuntime>
@@ -2,11 +2,6 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * `usePOISearch` — the headless core of the POI explorer. Owns the taxonomy-runtime load, the
7
- * debounced classify → subject → OverpassQL derivation, and the "Search live" state machine. The
8
- * runtime loader and the live-search probe are both INJECTABLE, so stories/tests drive it with mocks
9
- * and no network or db. Presentation is entirely the caller's concern.
10
5
  */
11
6
 
12
7
  import { matchPOISubject } from "@mailwoman/kind-classifier"
@@ -17,84 +12,115 @@ import { useCallback, useEffect, useEffectEvent, useState } from "react"
17
12
 
18
13
  import { useDebouncedValue } from "#common/useDebouncedValue"
19
14
  import { loadPOIRuntime } from "#poi/runtime"
20
- import type { LiveSearchState, LoadPOIRuntime, POIExplorerResult, POILiveSearch, POIRuntime } from "#poi/types"
15
+ import type {
16
+ LiveSearchState,
17
+ LoadPOIRuntime,
18
+ POIExplorerResult,
19
+ POILiveSearch,
20
+ POILiveSearchRequest,
21
+ POIRuntime,
22
+ } from "#poi/types"
21
23
 
24
+ /**
25
+ * Options for {@link usePOISearch}.
26
+ */
22
27
  export interface UsePOISearchOptions {
23
28
  /**
24
- * The current query text (controlled by the caller).
29
+ * The current query text, owned and updated by the caller.
25
30
  */
26
31
  text: string
32
+
27
33
  /**
28
- * Runtime loader. @default loadPOIRuntime
34
+ * The loader that the hook calls once on mount.
35
+ *
36
+ * @defaultValue `loadPOIRuntime`
29
37
  */
30
38
  loadRuntime?: LoadPOIRuntime
39
+
31
40
  /**
32
- * Injected live-search probe. Absent ⇒ the live-results affordance is disabled.
41
+ * The live-search probe.
42
+ * Live search is unavailable when it is absent.
33
43
  */
34
44
  runLiveSearch?: POILiveSearch
45
+
35
46
  /**
36
- * Whether the injected probe can serve BRAND subjects (fetch by Wikidata QID). Default false: brand subjects show the
37
- * intent + QID chip but no live-search affordance. The docs' httpvfs probe leaves this off — brand-wide row hydration
38
- * is pathological over byte-range (measured) — so brand live search is a server-side-backend capability. Category
39
- * live search is unaffected either way.
47
+ * Whether the probe can search for brand subjects by Wikidata ID, defaulting to
48
+ * false. enable it only for a server-side backend, since fetching every row for
49
+ * a brand over an HTTP range-request database is too slow.
40
50
  */
41
51
  brandLiveSearch?: boolean
52
+
42
53
  /**
43
- * Debounce before (re)classifying. @default 250
54
+ * The delay in milliseconds before the text is classified.
55
+ *
56
+ * @defaultValue `250`
44
57
  */
45
58
  debounceMs?: number
46
59
  }
47
60
 
61
+ /**
62
+ * The state that {@link usePOISearch} returns.
63
+ */
48
64
  export interface UsePOISearch {
49
65
  /**
50
66
  * True once the taxonomy runtime has loaded.
51
67
  */
52
68
  runtimeReady: boolean
69
+
53
70
  /**
54
- * The intent result for the current (debounced) query, or null for empty input.
71
+ * The classification for the current debounced text, `null` for empty text
72
+ * and while classification is pending.
55
73
  */
56
74
  result: POIExplorerResult | null
75
+
57
76
  /**
58
- * State of the on-demand live poi.db search.
77
+ * The live-search state for the current debounced text, `idle` until a search runs.
59
78
  */
60
79
  liveSearch: LiveSearchState
80
+
61
81
  /**
62
- * Whether a live search can run right now (a probe is wired + there's a resolved subject with an anchor).
82
+ * Whether a probe is wired and the subject supports live search with a non-empty place anchor.
63
83
  */
64
84
  canSearchLive: boolean
85
+
65
86
  /**
66
- * Kick off a live search for the current subject. No-op when {@link canSearchLive} is false.
87
+ * Starts a live search for the current subject.
88
+ * It starts no search when `canSearchLive` is false.
67
89
  */
68
90
  searchLive: () => Promise<void>
69
91
  }
70
92
 
71
- /**
72
- * Compute the OverpassQL export for a matched subject, capturing any emitter error rather than throwing.
73
- */
74
93
  function buildOverpass(
75
94
  runtime: POIRuntime,
76
- categoryID: string,
77
- matchedPhrase: string,
78
- remainder: string
79
- ): { overpassQL?: string; overpassError?: string } {
95
+ categoryID: string
96
+ ): { overpassQL: string | null; overpassError: string | null } {
80
97
  const category = runtime.lookup.getPOICategory(categoryID)
81
98
 
82
99
  if (!category) {
83
- return {}
100
+ return { overpassQL: null, overpassError: null }
84
101
  }
85
102
 
86
103
  const intent: OverpassIntentLike = {
87
- subject: { kind: "category", categoryIDs: [categoryID], matched: matchedPhrase },
88
- ...(remainder ? { anchor: { text: remainder } } : {}),
104
+ subject: { kind: "category", categoryIDs: [categoryID] },
105
+ anchor: null,
89
106
  }
90
107
 
91
108
  try {
92
- return { overpassQL: emitOverpassQL(intent, category.osmTag ? { osmTags: [category.osmTag] } : {}) }
109
+ return {
110
+ overpassQL: emitOverpassQL(intent, category.osmTag ? { osmTags: [category.osmTag] } : {}),
111
+ overpassError: null,
112
+ }
93
113
  } catch (error) {
94
- return { overpassError: error instanceof Error ? error.message : String(error) }
114
+ return { overpassQL: null, overpassError: error instanceof Error ? error.message : String(error) }
95
115
  }
96
116
  }
97
117
 
118
+ /**
119
+ * Classifies debounced query text as a POI category or brand request and runs live searches on demand.
120
+ *
121
+ * Each result is keyed to the query that produced it, live search requires a non-empty anchor.
122
+ * Brands need `brandLiveSearch` plus a Wikidata ID.
123
+ */
98
124
  export function usePOISearch({
99
125
  text,
100
126
  loadRuntime = loadPOIRuntime,
@@ -103,25 +129,14 @@ export function usePOISearch({
103
129
  debounceMs = 250,
104
130
  }: UsePOISearchOptions): UsePOISearch {
105
131
  const [runtime, setRuntime] = useState<POIRuntime | null>(null)
106
- /**
107
- * The classify result KEYED BY the query that produced it. The visible result is derived during render
108
- * (`storedResult.query === trimmedText ? … : null`), so a new query invalidates the old answer by derivation — the
109
- * effect never writes state synchronously to "reset", which is the react(set-state-in-effect) shape the lint bump
110
- * rightly flags.
111
- */
132
+
112
133
  const [storedResult, setStoredResult] = useState<{ query: string; value: POIExplorerResult } | null>(null)
113
- /**
114
- * Live-search state, keyed the same way: launched FOR a query, visible only while that query stands.
115
- */
134
+
116
135
  const [storedLive, setStoredLive] = useState<{ query: string; state: LiveSearchState } | null>(null)
117
136
 
118
137
  const debouncedText = useDebouncedValue(text, debounceMs)
119
138
  const trimmedText = debouncedText.trim()
120
139
 
121
- // The load fires exactly ONCE on mount regardless of whether the caller passes a fresh `loadRuntime`
122
- // closure each render (an inline `async () => …` would otherwise retrigger the effect → reload →
123
- // re-render loop). `useEffectEvent` reads the LATEST closure without joining the dependency list —
124
- // the runtime is a load-once resource.
125
140
  const loadRuntimeEvent = useEffectEvent(() => loadRuntime())
126
141
 
127
142
  useEffect(() => {
@@ -138,9 +153,6 @@ export function usePOISearch({
138
153
  }
139
154
  }, [])
140
155
 
141
- // Classify the debounced query and derive the subject + OverpassQL (async, so it lives in an effect).
142
- // Every state write below happens after an await — invalidation on query change is handled by the
143
- // key derivation above, not by a synchronous reset here.
144
156
  useEffect(() => {
145
157
  if (!runtime) return
146
158
 
@@ -156,16 +168,14 @@ export function usePOISearch({
156
168
  runtime.classify(input, shape).then((kindResult) => {
157
169
  if (cancelled) return
158
170
 
159
- const matched = kindResult.kind === "poi_query" ? matchPOISubject(trimmed, undefined, runtime.lexicon) : null
171
+ const matched = kindResult.kind === "poi_query" ? matchPOISubject(trimmed, null, runtime.lexicon) : null
160
172
 
161
173
  if (!matched) {
162
- setStoredResult({ query: trimmed, value: { kindResult } })
174
+ setStoredResult({ query: trimmed, value: { kindResult, subject: null, overpassQL: null, overpassError: null } })
163
175
 
164
176
  return
165
177
  }
166
178
 
167
- // Brand subject: the lexicon carries the brand's canonical name as `categoryID` + its Wikidata QID. No category
168
- // record, no OverpassQL (brands are searched by QID against the layer's `brand_wikidata` index, not OSM tags).
169
179
  if ((matched.match.kind ?? "category") === "brand") {
170
180
  setStoredResult({
171
181
  query: trimmed,
@@ -174,11 +184,13 @@ export function usePOISearch({
174
184
  subject: {
175
185
  kind: "brand",
176
186
  name: matched.match.categoryID,
177
- ...(matched.match.wikidata ? { wikidata: matched.match.wikidata } : {}),
187
+ wikidata: matched.match.wikidata || null,
178
188
  matchedPhrase: matched.match.matchedPhrase,
179
189
  confidence: matched.match.confidence,
180
190
  remainder: matched.remainder,
181
191
  },
192
+ overpassQL: null,
193
+ overpassError: null,
182
194
  },
183
195
  })
184
196
 
@@ -188,7 +200,7 @@ export function usePOISearch({
188
200
  const category = runtime.lookup.getPOICategory(matched.match.categoryID)
189
201
 
190
202
  if (!category) {
191
- setStoredResult({ query: trimmed, value: { kindResult } })
203
+ setStoredResult({ query: trimmed, value: { kindResult, subject: null, overpassQL: null, overpassError: null } })
192
204
 
193
205
  return
194
206
  }
@@ -205,7 +217,7 @@ export function usePOISearch({
205
217
  remainder: matched.remainder,
206
218
  buildLocal: runtime.lookup.requiresBuildLocalLayer(category),
207
219
  },
208
- ...buildOverpass(runtime, matched.match.categoryID, matched.match.matchedPhrase, matched.remainder),
220
+ ...buildOverpass(runtime, matched.match.categoryID),
209
221
  },
210
222
  })
211
223
  })
@@ -215,19 +227,13 @@ export function usePOISearch({
215
227
  }
216
228
  }, [trimmedText, runtime])
217
229
 
218
- // Derived visibility: an answer stands only while its query does. A new query reads as null/idle
219
- // with no reset write anywhere.
220
230
  const result = storedResult?.query === trimmedText ? storedResult.value : null
221
231
  const liveSearch: LiveSearchState = storedLive?.query === trimmedText ? storedLive.state : { status: "idle" }
222
232
 
223
- const subject = result?.subject
233
+ const subject = result?.subject ?? null
224
234
 
225
- // A subject is live-searchable when a probe is wired, it has an anchor, and: a CATEGORY that isn't build-local, or a
226
- // BRAND with a QID AND a brand-capable probe (`brandLiveSearch`). Brands without a QID / without a brand probe show
227
- // the intent + QID chip but no live affordance.
228
235
  const subjectLiveCapable =
229
- subject !== undefined &&
230
- (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== undefined : !subject.buildLocal)
236
+ subject !== null && (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== null : !subject.buildLocal)
231
237
 
232
238
  const canSearchLive = Boolean(
233
239
  runLiveSearch && runtime && subject && subjectLiveCapable && subject.remainder.trim().length > 0
@@ -236,34 +242,37 @@ export function usePOISearch({
236
242
  const searchLive = useCallback(async () => {
237
243
  if (!runLiveSearch || !runtime || !subject || !subject.remainder.trim()) return
238
244
 
239
- if (subject.kind === "category" ? subject.buildLocal : !(brandLiveSearch && subject.wikidata)) return
245
+ let request: POILiveSearchRequest
246
+
247
+ if (subject.kind === "brand") {
248
+ if (!brandLiveSearch || !subject.wikidata) return
249
+ request = { kind: "brand", brandName: subject.name, wikidata: subject.wikidata, anchor: subject.remainder }
250
+ } else {
251
+ if (subject.buildLocal) return
252
+
253
+ request = {
254
+ kind: "category",
255
+ categoryID: subject.category.id,
256
+ overtureCategoryIDs: runtime.lookup.resolveOvertureCategories(subject.category.id),
257
+ anchor: subject.remainder,
258
+ }
259
+ }
240
260
 
241
261
  setStoredLive({ query: trimmedText, state: { status: "loading" } })
242
262
 
243
263
  try {
244
- const outcome = await runLiveSearch(
245
- subject.kind === "brand"
246
- ? {
247
- categoryID: subject.name,
248
- overtureCategoryIDs: [],
249
- anchor: subject.remainder,
250
- brandWikidata: subject.wikidata,
251
- }
252
- : {
253
- categoryID: subject.category.id,
254
- // Fan the canonical seed id out over its Overture leaves — the same translation the Node reader uses.
255
- overtureCategoryIDs: runtime.lookup.resolveOvertureCategories(subject.category.id),
256
- anchor: subject.remainder,
257
- }
258
- )
259
-
260
- if (outcome.status === "success") {
264
+ const response = await runLiveSearch(request)
265
+
266
+ if (response.status === "success") {
267
+ setStoredLive({
268
+ query: trimmedText,
269
+ state: { status: "success", hits: response.hits, centerName: response.centerName },
270
+ })
271
+ } else if (response.status === "unplaced") {
261
272
  setStoredLive({
262
273
  query: trimmedText,
263
- state: { status: "success", hits: outcome.hits, centerName: outcome.centerName },
274
+ state: { status: "error", message: `couldn't place "${response.anchor}"` },
264
275
  })
265
- } else if (outcome.status === "unplaced") {
266
- setStoredLive({ query: trimmedText, state: { status: "error", message: `couldn't place "${outcome.anchor}"` } })
267
276
  } else {
268
277
  setStoredLive({
269
278
  query: trimmedText,
package/lib/poi.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file POI explorer components, hooks, runtime and interfaces.
6
+ */
7
+
8
+ export { AbstainPanel } from "./poi/AbstainPanel.tsx"
9
+ export type { AbstainPanelProps } from "./poi/AbstainPanel.tsx"
10
+ export { LiveResultsBlock } from "./poi/LiveResultsBlock.tsx"
11
+ export type { LiveResultsBlockProps } from "./poi/LiveResultsBlock.tsx"
12
+ export { OverpassBlock } from "./poi/OverpassBlock.tsx"
13
+ export type { OverpassBlockProps } from "./poi/OverpassBlock.tsx"
14
+ export { POIExplorer } from "./poi/POIExplorer.tsx"
15
+ export type { POIExplorerProps } from "./poi/POIExplorer.tsx"
16
+ export { QueryInput } from "./poi/QueryInput.tsx"
17
+ export type { QueryInputProps } from "./poi/QueryInput.tsx"
18
+ export { formatDistance, loadPOIRuntime, POI_DEFAULT_TEXT, POI_PRESETS } from "#poi/runtime"
19
+ export { SubjectPanel } from "./poi/SubjectPanel.tsx"
20
+ export type { SubjectPanelProps } from "./poi/SubjectPanel.tsx"
21
+
22
+ export type {
23
+ CategoryRecord,
24
+ LiveSearchState,
25
+ LoadPOIRuntime,
26
+ POIBrandSubject,
27
+ POICategorySubject,
28
+ POIExplorerResult,
29
+ POILiveSearch,
30
+ POILiveSearchRequest,
31
+ POILiveSearchResult,
32
+ POIRuntime,
33
+ POISearchHit,
34
+ POISubject,
35
+ POISubjectBase,
36
+ TaxonomyLookup,
37
+ } from "#poi/types"
38
+
39
+ export { usePOISearch } from "#poi/usePOISearch"
40
+ export type { UsePOISearch, UsePOISearchOptions } from "#poi/usePOISearch"