@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
@@ -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"
@@ -19,58 +14,73 @@ import { useDebouncedValue } from "#common/useDebouncedValue"
19
14
  import { loadPOIRuntime } from "#poi/runtime"
20
15
  import type { LiveSearchState, LoadPOIRuntime, POIExplorerResult, POILiveSearch, POIRuntime } from "#poi/types"
21
16
 
17
+ /**
18
+ * Options for {@link usePOISearch}.
19
+ */
22
20
  export interface UsePOISearchOptions {
23
21
  /**
24
- * The current query text (controlled by the caller).
22
+ * The current query text, owned and updated by the caller.
25
23
  */
26
24
  text: string
25
+
27
26
  /**
28
- * Runtime loader. @default loadPOIRuntime
27
+ * The loader that the hook calls once on mount.
28
+ * It defaults to `loadPOIRuntime`.
29
29
  */
30
30
  loadRuntime?: LoadPOIRuntime
31
+
31
32
  /**
32
- * Injected live-search probe. Absent ⇒ the live-results affordance is disabled.
33
+ * The live-search probe.
34
+ * Live search is unavailable when it is absent.
33
35
  */
34
36
  runLiveSearch?: POILiveSearch
37
+
35
38
  /**
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.
39
+ * Whether the probe can search for brand subjects by Wikidata ID, defaulting to
40
+ * false. enable it only for a server-side backend, since fetching every row for
41
+ * a brand over an HTTP range-request database is too slow.
40
42
  */
41
43
  brandLiveSearch?: boolean
44
+
42
45
  /**
43
- * Debounce before (re)classifying. @default 250
46
+ * The delay in milliseconds before the text is classified.
47
+ * Defaults to 250.
44
48
  */
45
49
  debounceMs?: number
46
50
  }
47
51
 
52
+ /**
53
+ * The state that {@link usePOISearch} returns.
54
+ */
48
55
  export interface UsePOISearch {
49
56
  /**
50
57
  * True once the taxonomy runtime has loaded.
51
58
  */
52
59
  runtimeReady: boolean
60
+
53
61
  /**
54
- * The intent result for the current (debounced) query, or null for empty input.
62
+ * The classification for the current debounced text, `null` for empty text
63
+ * and while classification is pending.
55
64
  */
56
65
  result: POIExplorerResult | null
66
+
57
67
  /**
58
- * State of the on-demand live poi.db search.
68
+ * The live-search state for the current debounced text, `idle` until a search runs.
59
69
  */
60
70
  liveSearch: LiveSearchState
71
+
61
72
  /**
62
- * Whether a live search can run right now (a probe is wired + there's a resolved subject with an anchor).
73
+ * Whether a probe is wired and the subject supports live search with a non-empty place anchor.
63
74
  */
64
75
  canSearchLive: boolean
76
+
65
77
  /**
66
- * Kick off a live search for the current subject. No-op when {@link canSearchLive} is false.
78
+ * Starts a live search for the current subject.
79
+ * It starts no search when `canSearchLive` is false.
67
80
  */
68
81
  searchLive: () => Promise<void>
69
82
  }
70
83
 
71
- /**
72
- * Compute the OverpassQL export for a matched subject, capturing any emitter error rather than throwing.
73
- */
74
84
  function buildOverpass(
75
85
  runtime: POIRuntime,
76
86
  categoryID: string,
@@ -95,6 +105,12 @@ function buildOverpass(
95
105
  }
96
106
  }
97
107
 
108
+ /**
109
+ * Classifies debounced query text as a POI category or brand request and runs live searches on demand.
110
+ *
111
+ * Each result is keyed to the query that produced it, live search requires a non-empty anchor.
112
+ * Brands need `brandLiveSearch` plus a Wikidata ID.
113
+ */
98
114
  export function usePOISearch({
99
115
  text,
100
116
  loadRuntime = loadPOIRuntime,
@@ -103,25 +119,14 @@ export function usePOISearch({
103
119
  debounceMs = 250,
104
120
  }: UsePOISearchOptions): UsePOISearch {
105
121
  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
- */
122
+
112
123
  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
- */
124
+
116
125
  const [storedLive, setStoredLive] = useState<{ query: string; state: LiveSearchState } | null>(null)
117
126
 
118
127
  const debouncedText = useDebouncedValue(text, debounceMs)
119
128
  const trimmedText = debouncedText.trim()
120
129
 
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
130
  const loadRuntimeEvent = useEffectEvent(() => loadRuntime())
126
131
 
127
132
  useEffect(() => {
@@ -138,9 +143,6 @@ export function usePOISearch({
138
143
  }
139
144
  }, [])
140
145
 
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
146
  useEffect(() => {
145
147
  if (!runtime) return
146
148
 
@@ -164,8 +166,6 @@ export function usePOISearch({
164
166
  return
165
167
  }
166
168
 
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
169
  if ((matched.match.kind ?? "category") === "brand") {
170
170
  setStoredResult({
171
171
  query: trimmed,
@@ -215,16 +215,11 @@ export function usePOISearch({
215
215
  }
216
216
  }, [trimmedText, runtime])
217
217
 
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
218
  const result = storedResult?.query === trimmedText ? storedResult.value : null
221
219
  const liveSearch: LiveSearchState = storedLive?.query === trimmedText ? storedLive.state : { status: "idle" }
222
220
 
223
221
  const subject = result?.subject
224
222
 
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
223
  const subjectLiveCapable =
229
224
  subject !== undefined &&
230
225
  (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== undefined : !subject.buildLocal)
@@ -251,7 +246,7 @@ export function usePOISearch({
251
246
  }
252
247
  : {
253
248
  categoryID: subject.category.id,
254
- // Fan the canonical seed id out over its Overture leaves — the same translation the Node reader uses.
249
+
255
250
  overtureCategoryIDs: runtime.lookup.resolveOvertureCategories(subject.category.id),
256
251
  anchor: subject.remainder,
257
252
  }
package/lib/poi.ts ADDED
@@ -0,0 +1,39 @@
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
+ POILiveSearchResult,
31
+ POIRuntime,
32
+ POISearchHit,
33
+ POISubject,
34
+ POISubjectBase,
35
+ TaxonomyLookup,
36
+ } from "#poi/types"
37
+
38
+ export { usePOISearch } from "#poi/usePOISearch"
39
+ export type { UsePOISearch, UsePOISearchOptions } from "#poi/usePOISearch"
@@ -2,172 +2,195 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * `useReleaseRuntime` — the headless load-orchestration hook shared by every mailwoman browser surface (the
7
- * inline doc-embeds via `RuntimeEmbed`, and the geocoder). It owns the version-selection state
8
- * machine, the per-version load SEQUENCING, cancellation, and the ready / loading / error state — but
9
- * NOTHING model- or map-specific. The actual asset fetchers (the ONNX classifier factory, the httpvfs
10
- * WOF opener, the FST fetch, the releases.json fetch) are INJECTED by the host as async functions, so
11
- * this module imports only React: no `onnxruntime-web`, no `sql.js-httpvfs`, no `maplibre-gl`, no
12
- * `fetch`-specific plumbing. That keeps it node-import-safe and root-exportable from
13
- * `@mailwoman/react` — the exact interface `PipelineRuntime` established, generalized to the loader itself.
14
- *
15
- * The hook is generic over `TAssets` (the opaque bundle the host's `loadAssets` returns — classifier,
16
- * FST, WOF lookup, calibrator, …) and `TRelease` (the host's release-manifest entry). The package
17
- * never inspects either; it just holds, reveals, and re-loads them across version/backend switches.
18
5
  */
19
6
 
20
7
  import { useCallback, useEffect, useMemo, useRef, useState } from "react"
21
8
 
22
9
  /**
23
- * The minimal contract a release-manifest entry must satisfy. Hosts extend this with their own fields.
10
+ * The minimal fields of a release-manifest entry.
11
+ * Hosts can add their own fields.
24
12
  */
25
13
  export interface ReleaseBase {
26
- /**
27
- * The version tag this entry describes (matched against `selectedVersion`).
28
- */
29
14
  version: string
15
+
30
16
  /**
31
- * Optional display label a version picker shows; falls back to `version`.
17
+ * The version picker's display label, falling back to `version`.
32
18
  */
33
19
  label?: string
34
20
  }
35
21
 
36
22
  /**
37
- * The releases manifest the host fetches — the default version plus the selectable release entries.
23
+ * The releases manifest that the host fetches.
38
24
  */
39
25
  export interface ReleaseManifest<TRelease extends ReleaseBase = ReleaseBase> {
40
26
  /**
41
- * The version selected on first load (before the user picks another).
27
+ * The version that the hook selects when the manifest first loads.
42
28
  */
43
29
  defaultVersion: string
44
- /**
45
- * Every selectable release.
46
- */
30
+
47
31
  releases: TRelease[]
48
32
  }
49
33
 
50
34
  /**
51
- * The progress channel handed to the host's `loadAssets`. The host reports load progress + the resolved backend + the
52
- * staged step labels/index THROUGH these setters (all no-op once the load is superseded/aborted), while the hook owns
53
- * the terminal state (revealing the assets + clearing progress on success, surfacing the error on failure).
35
+ * The abort signal and progress setters that the hook passes to a host's `loadAssets`.
36
+ *
37
+ * The setters stop affecting state after abort or supersession.
38
+ * The hook sets the final ready or error state.
54
39
  */
55
40
  export interface AssetsLoadContext {
56
41
  /**
57
- * Aborts when this load is superseded (version/backend switch) or the provider unmounts.
42
+ * The signal aborts when a version or backend switch supersedes this load or when the component unmounts.
58
43
  */
59
44
  signal: AbortSignal
45
+
60
46
  /**
61
- * Whether the host should force the CPU/WASM backend (opt out of WebGPU) for this load.
47
+ * Whether this load should use the CPU WASM backend instead of WebGPU.
62
48
  */
63
49
  forceWASM: boolean
50
+
64
51
  /**
65
- * Set the human-readable progress line (e.g. `Loading v7 model (~28 MB)…`).
52
+ * Sets the progress line, such as `Loading v7 model (~28 MB)…`.
66
53
  */
67
54
  setProgress: (progress: string) => void
68
- /**
69
- * Set the staged-loader step labels (e.g. `["Loading classifier", "Loading FST gazetteer"]`).
70
- */
55
+
71
56
  setStepLabels: (labels: string[]) => void
57
+
72
58
  /**
73
- * Advance the staged-loader step index (0-based).
59
+ * Sets the zero-based index of the current loader step.
74
60
  */
75
61
  setStepIndex: (index: number) => void
62
+
76
63
  /**
77
- * Report the backend the neural runtime resolved to (e.g. `webgpu (27 MB int8)`).
64
+ * Reports the backend that the neural runtime resolved to, such as `webgpu (27 MB int8)`.
78
65
  */
79
66
  setBackend: (backend: string) => void
67
+
68
+ /**
69
+ * Reports the fraction of the current download received so far, in [0, 1], or `null`
70
+ * when no download is in progress or the response declares no length.
71
+ *
72
+ * The step index cannot show it because the model is fetched before the first step begins.
73
+ */
74
+ setByteFraction: (fraction: number | null) => void
80
75
  }
81
76
 
82
77
  /**
83
- * The injected loaders the hook orchestrates. Nothing here is model- or map-aware — the host owns all that.
78
+ * The host's manifest loader, asset loader and optional asset disposer for {@link useReleaseRuntime}.
84
79
  */
85
80
  export interface ReleaseRuntimeConfig<TAssets, TRelease extends ReleaseBase = ReleaseBase> {
86
81
  /**
87
- * Fetch + normalize the releases manifest. Returns `null` when no manifest is available (the surface then shows
88
- * nothing selectable). Rejecting surfaces `errorMessage`. Runs once on mount.
82
+ * Fetches the releases manifest once on mount.
83
+ *
84
+ * It returns `null` when the manifest is unavailable.
85
+ * The hook reports a rejection through `errorMessage`.
89
86
  */
90
87
  loadManifest: (signal: AbortSignal) => Promise<ReleaseManifest<TRelease> | null>
88
+
91
89
  /**
92
- * Load the full asset bundle for one release — the classifier, FST, WOF lookup, calibrator, whatever the host needs.
93
- * Runs on every version or `forceWASM` change. Report progress via `ctx`; return the bundle. Rejecting surfaces
94
- * `errorMessage`. Bail early when `ctx.signal.aborted` — the hook discards a superseded result regardless.
90
+ * Loads the asset bundle for one release and reports progress through `ctx`;
91
+ * the hook calls it on every version or `forceWASM` change, reports a rejection through
92
+ * `errorMessage`, and disposes a result that resolves after `ctx.signal` aborts.
95
93
  */
96
94
  loadAssets: (release: TRelease, ctx: AssetsLoadContext) => Promise<TAssets>
95
+
96
+ /**
97
+ * Releases resources the garbage collector does not own, such as an ONNX
98
+ * session's WASM heap or a GPU buffer.
99
+ *
100
+ * The hook calls it when replacing a bundle.
101
+ * It also calls it if a bundle resolves after abort or when unmounting.
102
+ *
103
+ * Without this function, each reload leaves the model resident.
104
+ */
105
+ disposeAssets?: (assets: TAssets) => void | Promise<void>
106
+
97
107
  /**
98
- * The progress line shown before the manifest arrives. @default "Loading releases…"
108
+ * The progress line shown before the manifest arrives.
109
+ * Defaults to `Loading releases…`.
99
110
  */
100
111
  initialProgress?: string
101
112
  }
102
113
 
103
114
  /**
104
- * The state `useReleaseRuntime` produces — the load-orchestration state a surface renders + re-projects.
115
+ * The release-loading state that {@link useReleaseRuntime} returns.
105
116
  *
106
- * This is the LOADER STATE, deliberately distinct from {@link GeocoderRuntime} (the injected runtime contract
107
- * `<Geocoder>` consumes). A host builds a {@link GeocoderRuntime} by pairing this loader state (assets + backend +
108
- * version) with the map surface (style, overlays, bias, parse) — see the map subpath's `GeocoderRuntime`.
117
+ * A host pairs it with its map surface to build a `GeocoderRuntime`.
109
118
  */
110
119
  export interface ReleaseLoaderState<TAssets, TRelease extends ReleaseBase = ReleaseBase> {
111
120
  /**
112
- * The releases manifest, once fetched.
121
+ * The releases manifest, `null` until it loads or when none is available.
113
122
  */
114
123
  manifest: ReleaseManifest<TRelease> | null
124
+
115
125
  /**
116
- * The currently-selected version, or `null` before the manifest resolves.
126
+ * The selected version, `null` before the manifest loads.
117
127
  */
118
128
  selectedVersion: string | null
129
+
119
130
  /**
120
- * The release entry matching `selectedVersion` (convenience over `manifest.releases.find`).
131
+ * The manifest entry that matches `selectedVersion`, or `null` when none matches.
121
132
  */
122
133
  selectedRelease: TRelease | null
134
+
123
135
  /**
124
- * The loaded asset bundle for the selected version, or `null` while (re)loading.
136
+ * The asset bundle for the selected version, `null` while it loads.
125
137
  */
126
138
  assets: TAssets | null
127
- /**
128
- * Whether the asset bundle is loaded and ready to use.
129
- */
139
+
130
140
  ready: boolean
141
+
131
142
  /**
132
- * Human-readable load progress (`""` when idle/ready).
143
+ * The load progress line, empty once loading finishes or fails.
133
144
  */
134
145
  loadingProgress: string
146
+
135
147
  /**
136
- * Staged-loader step index (0-based; `-1` before the first step).
148
+ * The zero-based loader step index, `-1` before the first step.
137
149
  */
138
150
  loadingStepIndex: number
151
+
152
+ loadingStepLabels: string[]
153
+
139
154
  /**
140
- * Staged-loader step labels.
155
+ * The fraction of the current download received so far, in [0, 1], or `null`
156
+ * when no download is in progress or the length is unknown.
141
157
  */
142
- loadingStepLabels: string[]
158
+ loadingByteFraction: number | null
159
+
143
160
  /**
144
- * A load error (manifest or asset), distinct from any per-parse error a consumer tracks separately.
161
+ * A manifest or asset load error, separate from any parse error a consumer tracks.
145
162
  */
146
163
  errorMessage: string | null
164
+
147
165
  /**
148
- * The backend the neural runtime resolved to (e.g. `webgpu (27 MB int8)`), or `""` before it's known.
166
+ * The backend the neural runtime resolved to, such as `webgpu (27 MB int8)`;
167
+ * it is `""` before the backend is known.
149
168
  */
150
169
  activeBackend: string
170
+
151
171
  /**
152
- * Whether the CPU/WASM backend is currently forced.
172
+ * Whether the CPU WASM backend is forced instead of WebGPU.
153
173
  */
154
174
  forceWASM: boolean
175
+
155
176
  /**
156
- * Switch to a different version (clears any error, then reloads the asset bundle).
177
+ * Switches to another version, clearing any error and reloading the asset bundle.
157
178
  */
158
179
  selectVersion: (version: string) => void
180
+
159
181
  /**
160
- * Force (or unforce) the CPU/WASM backend — reloads the asset bundle.
182
+ * Forces or releases the CPU WASM backend.
183
+ * Either change reloads the asset bundle.
161
184
  */
162
185
  setForceWASM: (forceWASM: boolean) => void
163
186
  }
164
187
 
165
188
  /**
166
- * Drive the shared version → asset-bundle load state machine over a host-injected loader.
189
+ * Loads the release manifest on mount and the selected release's assets whenever
190
+ * the version or `forceWASM` changes.
167
191
  *
168
- * Sequence: on mount `loadManifest` runs and its `defaultVersion` becomes the selection; each version (or `forceWASM`)
169
- * change reloads the bundle via `loadAssets`, the previous load aborted first. The assets are revealed ATOMICALLY when
170
- * `loadAssets` resolves (so `ready` flips exactly once per load), and consumers wait on `ready`.
192
+ * Each reload aborts the previous load and disposes the old assets.
193
+ * `ready` becomes true after the new assets load.
171
194
  */
172
195
  export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = ReleaseBase>(
173
196
  config: ReleaseRuntimeConfig<TAssets, TRelease>
@@ -182,25 +205,24 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
182
205
  const [loadingStepLabels, setLoadingStepLabels] = useState<string[]>([])
183
206
  const [errorMessage, setErrorMessage] = useState<string | null>(null)
184
207
  const [activeBackend, setActiveBackend] = useState<string>("")
208
+ const [loadingByteFraction, setLoadingByteFraction] = useState<number | null>(null)
185
209
  const [forceWASM, setForceWASMState] = useState(false)
186
210
 
187
- // Latest-ref the injected loaders: a host that re-creates them each render (an inline arrow) must NOT retrigger the
188
- // load effects, which key ONLY on version/backend. The effects read `.current` at run time.
189
211
  const loadManifestRef = useRef(config.loadManifest)
190
212
  const loadAssetsRef = useRef(config.loadAssets)
213
+ const disposeAssetsRef = useRef(config.disposeAssets)
214
+
215
+ const liveAssetsRef = useRef<TAssets | null>(null)
191
216
 
192
- // Latest manifest for the version-load effect, so it can resolve the release WITHOUT depending on `manifest`
193
- // identity — which would double-fire the load the instant the manifest first arrives (the selection transition
194
- // null → defaultVersion already fires it once).
195
217
  const manifestRef = useRef<ReleaseManifest<TRelease> | null>(null)
196
218
 
197
219
  useEffect(() => {
198
220
  loadManifestRef.current = config.loadManifest
199
221
  loadAssetsRef.current = config.loadAssets
222
+ disposeAssetsRef.current = config.disposeAssets
200
223
  manifestRef.current = manifest
201
- }, [config.loadAssets, config.loadManifest, manifest])
224
+ }, [config.disposeAssets, config.loadAssets, config.loadManifest, manifest])
202
225
 
203
- // Mount: fetch the manifest, then select the default version.
204
226
  useEffect(() => {
205
227
  const controller = new AbortController()
206
228
 
@@ -223,7 +245,6 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
223
245
  return () => controller.abort()
224
246
  }, [])
225
247
 
226
- // Load the per-version asset bundle when the version (or the backend force) changes.
227
248
  useEffect(() => {
228
249
  if (!selectedVersion) return
229
250
 
@@ -242,10 +263,19 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
242
263
 
243
264
  void (async () => {
244
265
  try {
266
+ const outgoing = liveAssetsRef.current
267
+
268
+ liveAssetsRef.current = null
269
+
270
+ if (outgoing) {
271
+ await disposeAssetsRef.current?.(outgoing)
272
+ }
273
+
245
274
  setAssets(null)
246
275
  setLoadingStepIndex(-1)
247
276
  setLoadingStepLabels([])
248
277
  setActiveBackend("")
278
+ setLoadingByteFraction(null)
249
279
 
250
280
  const ctx: AssetsLoadContext = {
251
281
  signal,
@@ -254,13 +284,21 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
254
284
  setStepLabels: (labels) => guard(() => setLoadingStepLabels(labels)),
255
285
  setStepIndex: (index) => guard(() => setLoadingStepIndex(index)),
256
286
  setBackend: (backend) => guard(() => setActiveBackend(backend)),
287
+ setByteFraction: (fraction) => guard(() => setLoadingByteFraction(fraction)),
257
288
  }
258
289
 
259
290
  const loaded = await loadAssetsRef.current(release, ctx)
260
291
 
261
- if (signal.aborted) return
292
+ if (signal.aborted) {
293
+ await disposeAssetsRef.current?.(loaded)
294
+
295
+ return
296
+ }
297
+
298
+ liveAssetsRef.current = loaded
262
299
  setAssets(loaded)
263
300
  setLoadingProgress("")
301
+ setLoadingByteFraction(null)
264
302
  } catch (error) {
265
303
  if (signal.aborted) return
266
304
  setErrorMessage(error instanceof Error ? error.message : String(error))
@@ -271,6 +309,18 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
271
309
  return () => controller.abort()
272
310
  }, [selectedVersion, forceWASM])
273
311
 
312
+ useEffect(() => {
313
+ return () => {
314
+ const live = liveAssetsRef.current
315
+
316
+ liveAssetsRef.current = null
317
+
318
+ if (live) {
319
+ void disposeAssetsRef.current?.(live)
320
+ }
321
+ }
322
+ }, [])
323
+
274
324
  const selectVersion = useCallback((version: string) => {
275
325
  setSelectedVersion(version)
276
326
  setErrorMessage(null)
@@ -292,6 +342,7 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
292
342
  loadingProgress,
293
343
  loadingStepIndex,
294
344
  loadingStepLabels,
345
+ loadingByteFraction,
295
346
  errorMessage,
296
347
  activeBackend,
297
348
  forceWASM,