@mailwoman/react 10.1.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 (178) hide show
  1. package/README.md +1 -0
  2. package/lib/common/About.tsx +3 -3
  3. package/lib/common/CopyButton.tsx +6 -2
  4. package/lib/common/LoadingIndicator.tsx +10 -4
  5. package/lib/common/PresetChips.tsx +3 -1
  6. package/lib/common/confidence-tiers.ts +1 -1
  7. package/lib/common/text-tokens.ts +3 -9
  8. package/lib/index.ts +0 -1
  9. package/lib/map/AppIdentity.tsx +6 -2
  10. package/lib/map/DebugInfo.tsx +3 -16
  11. package/lib/map/Geocoder.tsx +9 -8
  12. package/lib/map/GeocoderControls.tsx +1 -13
  13. package/lib/map/GraticuleLayer.tsx +3 -1
  14. package/lib/map/MapCanvas.tsx +3 -1
  15. package/lib/map/MapChipRow.tsx +1 -1
  16. package/lib/map/MapCompass.tsx +3 -1
  17. package/lib/map/MapControlStack.tsx +3 -1
  18. package/lib/map/MapFooter.tsx +3 -1
  19. package/lib/map/OverlayLayers.tsx +13 -2
  20. package/lib/map/PlaceAutocomplete.tsx +3 -1
  21. package/lib/map/PlaceMarker.tsx +6 -2
  22. package/lib/map/ResolvedPlaceLayers.tsx +13 -5
  23. package/lib/map/ResultCamera.tsx +3 -1
  24. package/lib/map/ResultOverlay.tsx +12 -4
  25. package/lib/map/VersionPicker.tsx +1 -1
  26. package/lib/map/fake-runtime.ts +36 -7
  27. package/lib/map/map-debug.ts +3 -1
  28. package/lib/map/place-render.ts +3 -3
  29. package/lib/map/types.ts +2 -15
  30. package/lib/map/useMapPlaceRender.ts +1 -1
  31. package/lib/map/usePlaceAutocomplete.ts +2 -2
  32. package/lib/map.ts +0 -3
  33. package/lib/pipeline/ConfidenceCell.tsx +1 -1
  34. package/lib/pipeline/PipelineExplorer.tsx +6 -3
  35. package/lib/pipeline/ResolvedPlace.tsx +1 -1
  36. package/lib/pipeline/TreeView.tsx +1 -1
  37. package/lib/pipeline/types.ts +0 -4
  38. package/lib/poi/POIExplorer.tsx +6 -3
  39. package/lib/poi/runtime.ts +9 -0
  40. package/lib/poi/types.ts +27 -28
  41. package/lib/poi/usePOISearch.ts +55 -41
  42. package/lib/poi.ts +1 -0
  43. package/lib/runtime/useReleaseRuntime.ts +14 -33
  44. package/lib/search/SearchModal.tsx +227 -66
  45. package/lib/search/search.css +160 -22
  46. package/lib/search/types.ts +2 -1
  47. package/out/common/About.d.ts +1 -1
  48. package/out/common/About.js +1 -1
  49. package/out/common/About.js.map +1 -1
  50. package/out/common/CopyButton.d.ts +6 -2
  51. package/out/common/CopyButton.d.ts.map +1 -1
  52. package/out/common/CopyButton.js.map +1 -1
  53. package/out/common/LoadingIndicator.d.ts +10 -4
  54. package/out/common/LoadingIndicator.d.ts.map +1 -1
  55. package/out/common/LoadingIndicator.js.map +1 -1
  56. package/out/common/PresetChips.d.ts +3 -1
  57. package/out/common/PresetChips.d.ts.map +1 -1
  58. package/out/common/PresetChips.js.map +1 -1
  59. package/out/common/confidence-tiers.d.ts +1 -1
  60. package/out/common/confidence-tiers.d.ts.map +1 -1
  61. package/out/common/confidence-tiers.js.map +1 -1
  62. package/out/common/text-tokens.d.ts +2 -8
  63. package/out/common/text-tokens.d.ts.map +1 -1
  64. package/out/common/text-tokens.js.map +1 -1
  65. package/out/index.d.ts +1 -1
  66. package/out/index.d.ts.map +1 -1
  67. package/out/map/AppIdentity.d.ts +6 -2
  68. package/out/map/AppIdentity.d.ts.map +1 -1
  69. package/out/map/AppIdentity.js.map +1 -1
  70. package/out/map/DebugInfo.d.ts +2 -9
  71. package/out/map/DebugInfo.d.ts.map +1 -1
  72. package/out/map/DebugInfo.js +3 -6
  73. package/out/map/DebugInfo.js.map +1 -1
  74. package/out/map/Geocoder.d.ts +4 -4
  75. package/out/map/Geocoder.d.ts.map +1 -1
  76. package/out/map/Geocoder.js +4 -2
  77. package/out/map/Geocoder.js.map +1 -1
  78. package/out/map/GeocoderControls.d.ts +2 -3
  79. package/out/map/GeocoderControls.d.ts.map +1 -1
  80. package/out/map/GeocoderControls.js +2 -3
  81. package/out/map/GeocoderControls.js.map +1 -1
  82. package/out/map/GraticuleLayer.d.ts +3 -1
  83. package/out/map/GraticuleLayer.d.ts.map +1 -1
  84. package/out/map/GraticuleLayer.js.map +1 -1
  85. package/out/map/MapCanvas.d.ts +3 -1
  86. package/out/map/MapCanvas.d.ts.map +1 -1
  87. package/out/map/MapCanvas.js.map +1 -1
  88. package/out/map/MapChipRow.d.ts +1 -1
  89. package/out/map/MapCompass.d.ts +3 -1
  90. package/out/map/MapCompass.d.ts.map +1 -1
  91. package/out/map/MapCompass.js.map +1 -1
  92. package/out/map/MapControlStack.d.ts +3 -1
  93. package/out/map/MapControlStack.d.ts.map +1 -1
  94. package/out/map/MapControlStack.js.map +1 -1
  95. package/out/map/MapFooter.d.ts +3 -1
  96. package/out/map/MapFooter.d.ts.map +1 -1
  97. package/out/map/MapFooter.js.map +1 -1
  98. package/out/map/OverlayLayers.d.ts +7 -1
  99. package/out/map/OverlayLayers.d.ts.map +1 -1
  100. package/out/map/OverlayLayers.js +2 -2
  101. package/out/map/OverlayLayers.js.map +1 -1
  102. package/out/map/PlaceAutocomplete.d.ts +3 -1
  103. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  104. package/out/map/PlaceAutocomplete.js.map +1 -1
  105. package/out/map/PlaceMarker.d.ts +6 -2
  106. package/out/map/PlaceMarker.d.ts.map +1 -1
  107. package/out/map/PlaceMarker.js.map +1 -1
  108. package/out/map/ResolvedPlaceLayers.d.ts +13 -5
  109. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  110. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  111. package/out/map/ResultCamera.d.ts +3 -1
  112. package/out/map/ResultCamera.d.ts.map +1 -1
  113. package/out/map/ResultCamera.js.map +1 -1
  114. package/out/map/ResultOverlay.d.ts +12 -4
  115. package/out/map/ResultOverlay.d.ts.map +1 -1
  116. package/out/map/ResultOverlay.js.map +1 -1
  117. package/out/map/VersionPicker.d.ts +1 -1
  118. package/out/map/fake-runtime.d.ts.map +1 -1
  119. package/out/map/fake-runtime.js +36 -7
  120. package/out/map/fake-runtime.js.map +1 -1
  121. package/out/map/map-debug.d.ts +3 -1
  122. package/out/map/map-debug.d.ts.map +1 -1
  123. package/out/map/map-debug.js.map +1 -1
  124. package/out/map/place-render.d.ts +3 -3
  125. package/out/map/place-render.d.ts.map +1 -1
  126. package/out/map/types.d.ts +2 -14
  127. package/out/map/types.d.ts.map +1 -1
  128. package/out/map/useMapPlaceRender.d.ts +1 -1
  129. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  130. package/out/map/useMapPlaceRender.js.map +1 -1
  131. package/out/map/usePlaceAutocomplete.d.ts +2 -2
  132. package/out/map.d.ts +1 -3
  133. package/out/map.d.ts.map +1 -1
  134. package/out/map.js +0 -1
  135. package/out/map.js.map +1 -1
  136. package/out/pipeline/ConfidenceCell.d.ts +1 -1
  137. package/out/pipeline/ConfidenceCell.d.ts.map +1 -1
  138. package/out/pipeline/PipelineExplorer.d.ts +6 -2
  139. package/out/pipeline/PipelineExplorer.d.ts.map +1 -1
  140. package/out/pipeline/PipelineExplorer.js +1 -1
  141. package/out/pipeline/PipelineExplorer.js.map +1 -1
  142. package/out/pipeline/ResolvedPlace.d.ts +1 -1
  143. package/out/pipeline/ResolvedPlace.d.ts.map +1 -1
  144. package/out/pipeline/TreeView.js.map +1 -1
  145. package/out/pipeline/types.d.ts +0 -4
  146. package/out/pipeline/types.d.ts.map +1 -1
  147. package/out/poi/POIExplorer.d.ts +1 -1
  148. package/out/poi/POIExplorer.d.ts.map +1 -1
  149. package/out/poi/POIExplorer.js +2 -2
  150. package/out/poi/POIExplorer.js.map +1 -1
  151. package/out/poi/runtime.d.ts.map +1 -1
  152. package/out/poi/runtime.js +9 -0
  153. package/out/poi/runtime.js.map +1 -1
  154. package/out/poi/types.d.ts +24 -28
  155. package/out/poi/types.d.ts.map +1 -1
  156. package/out/poi/usePOISearch.d.ts +4 -2
  157. package/out/poi/usePOISearch.d.ts.map +1 -1
  158. package/out/poi/usePOISearch.js +42 -32
  159. package/out/poi/usePOISearch.js.map +1 -1
  160. package/out/poi.d.ts +1 -1
  161. package/out/poi.d.ts.map +1 -1
  162. package/out/poi.js.map +1 -1
  163. package/out/runtime/useReleaseRuntime.d.ts +13 -23
  164. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  165. package/out/runtime/useReleaseRuntime.js +2 -8
  166. package/out/runtime/useReleaseRuntime.js.map +1 -1
  167. package/out/search/SearchModal.d.ts +10 -3
  168. package/out/search/SearchModal.d.ts.map +1 -1
  169. package/out/search/SearchModal.js +83 -15
  170. package/out/search/SearchModal.js.map +1 -1
  171. package/out/search/types.d.ts +2 -1
  172. package/out/search/types.d.ts.map +1 -1
  173. package/package.json +19 -29
  174. package/lib/map/BackendControl.tsx +0 -50
  175. package/out/map/BackendControl.d.ts +0 -31
  176. package/out/map/BackendControl.d.ts.map +0 -1
  177. package/out/map/BackendControl.js +0 -8
  178. package/out/map/BackendControl.js.map +0 -1
@@ -29,17 +29,17 @@ export interface ResolvedMapPlace extends ResolvedPlaceView {
29
29
  * The place's bounding box.
30
30
  * Postcodes located by an anchor centroid have none.
31
31
  */
32
- bbox?: LatLonBounds
32
+ bbox: LatLonBounds | null
33
33
 
34
34
  /**
35
35
  * The street-level tier, set only for results from address points or interpolation.
36
36
  */
37
- tier?: PlaceTier
37
+ tier: PlaceTier | null
38
38
 
39
39
  /**
40
40
  * The uncertainty radius in meters of a street-level result, drawn as a circle when `tier` is set.
41
41
  */
42
- uncertaintyM?: number
42
+ uncertaintyM: number | null
43
43
 
44
44
  /**
45
45
  * The admin polygon, if the host has already fetched it.
package/lib/map/types.ts CHANGED
@@ -42,7 +42,7 @@ export interface OverlaySpec {
42
42
  source: SourceSpecification
43
43
  layers: LayerSpecification[]
44
44
  /**
45
- * @default true
45
+ * @defaultValue `true`
46
46
  */
47
47
  visible?: boolean
48
48
  label?: string
@@ -81,11 +81,6 @@ export interface VersionOption {
81
81
  label?: string
82
82
  }
83
83
 
84
- /**
85
- * Which neural backend the geocoder is currently running on.
86
- */
87
- export type InferenceBackend = "webgpu" | "wasm"
88
-
89
84
  /**
90
85
  * The injected geocoder runtime extends {@link PipelineRuntime} with the map.
91
86
  * It also supplies version and backend controls.
@@ -148,17 +143,9 @@ export interface GeocoderRuntime extends PipelineRuntime {
148
143
  */
149
144
  selectVersion?: (version: string) => void
150
145
  /**
151
- * The backend the neural runtime resolved to (e.g. `webgpu (28 MB int8)`); free-form for the label.
146
+ * The neural runtime's backend and model size (e.g. `wasm (28 MB int8)`); free-form for the label.
152
147
  */
153
148
  activeBackend?: string
154
- /**
155
- * Whether the CPU/wasm backend is currently forced (the controlled value for the backend toggle).
156
- */
157
- forceWASM?: boolean
158
- /**
159
- * Force the wasm backend (opt out of WebGPU), for the backend toggle.
160
- */
161
- setForceWASM?: (forceWASM: boolean) => void
162
149
  }
163
150
 
164
151
  /**
@@ -14,6 +14,6 @@ import type { MapPlaceRenderSpec, ResolvedMapPlace } from "#map/place-render"
14
14
  /**
15
15
  * Memoize the render spec for a resolved place; `null` in → `null` out (no geometry to draw).
16
16
  */
17
- export function useMapPlaceRender(place: ResolvedMapPlace | null | undefined): MapPlaceRenderSpec | null {
17
+ export function useMapPlaceRender(place: ResolvedMapPlace | null): MapPlaceRenderSpec | null {
18
18
  return useMemo(() => (place ? computeMapPlaceRenderSpec(place) : null), [place])
19
19
  }
@@ -32,12 +32,12 @@ export interface UsePlaceAutocompleteOptions {
32
32
  autocomplete?: (query: string) => Promise<Suggestion[]>
33
33
 
34
34
  /**
35
- * @default 2
35
+ * @defaultValue `2`
36
36
  */
37
37
  minChars?: number
38
38
 
39
39
  /**
40
- * @default 150
40
+ * @defaultValue `150`
41
41
  */
42
42
  debounceMs?: number
43
43
  }
package/lib/map.ts CHANGED
@@ -38,7 +38,6 @@ export type {
38
38
  CompareContext,
39
39
  GeocoderPanels,
40
40
  GeocoderRuntime,
41
- InferenceBackend,
42
41
  LngLatTuple,
43
42
  MapBias,
44
43
  OverlaySpec,
@@ -67,8 +66,6 @@ export { ResultOverlay } from "./map/ResultOverlay.tsx"
67
66
  export type { ResultOverlayProps } from "./map/ResultOverlay.tsx"
68
67
 
69
68
  // ── Geocoder controls + the composed geocoder ────────────────────────────────
70
- export { BackendControl } from "./map/BackendControl.tsx"
71
- export type { BackendControlProps } from "./map/BackendControl.tsx"
72
69
  export { CompareToggle } from "./map/CompareToggle.tsx"
73
70
  export type { CompareToggleProps } from "./map/CompareToggle.tsx"
74
71
  export { Geocoder } from "./map/Geocoder.tsx"
@@ -11,7 +11,7 @@ import { confidenceTierOrMid } from "#common/confidence-tiers"
11
11
  import { cx } from "#common/cx"
12
12
 
13
13
  export interface ConfidenceCellProps {
14
- confidence?: number
14
+ confidence?: number | null
15
15
  }
16
16
 
17
17
  export function ConfidenceCell({ confidence }: ConfidenceCellProps) {
@@ -32,11 +32,15 @@ export interface PipelineExplorerProps {
32
32
  */
33
33
  runtime: PipelineRuntime
34
34
  /**
35
- * Address to pre-fill. @default the White House
35
+ * Address to pre-fill.
36
+ *
37
+ * @defaultValue the White House
36
38
  */
37
39
  defaultAddress?: string
38
40
  /**
39
- * Example chips. @default the built-in address presets
41
+ * Example chips.
42
+ *
43
+ * @defaultValue the built-in address presets
40
44
  */
41
45
  presets?: ReadonlyArray<Preset>
42
46
  /**
@@ -62,7 +66,6 @@ function PipelineExplorerInner({ runtime, defaultAddress, presets, panels }: Pip
62
66
  {panels.header}
63
67
  {panels.releaseInfo}
64
68
  {panels.versionControl}
65
- {panels.backendControl}
66
69
 
67
70
  <QueryForm
68
71
  value={text}
@@ -12,7 +12,7 @@ import { Fragment, type ReactNode } from "react"
12
12
 
13
13
  export interface ResolvedPlaceProps {
14
14
  place: ResolvedPlaceView
15
- dualRoles?: DualRoleView[]
15
+ dualRoles?: DualRoleView[] | null
16
16
  }
17
17
 
18
18
  export function ResolvedPlace({ place, dualRoles }: ResolvedPlaceProps): ReactNode {
@@ -47,7 +47,7 @@ function renderNode(node: TreeNode, path: string): ReactNode {
47
47
  }
48
48
 
49
49
  export function TreeView({ tree }: TreeViewProps): ReactNode {
50
- const roots = (tree as { roots?: unknown[] } | null | undefined)?.roots
50
+ const roots = (tree as { roots?: unknown[] } | null)?.roots
51
51
 
52
52
  if (!Array.isArray(roots) || !roots.length) return null
53
53
 
@@ -73,10 +73,6 @@ export interface PipelinePanels {
73
73
  * Model-version selector, wired to the host's bundle state.
74
74
  */
75
75
  versionControl?: ReactNode
76
- /**
77
- * Backend indicator + wasm toggle.
78
- */
79
- backendControl?: ReactNode
80
76
  /**
81
77
  * One-line release blurb.
82
78
  */
@@ -30,7 +30,7 @@ export interface POIExplorerProps {
30
30
  /**
31
31
  * The example chips.
32
32
  *
33
- * @default POI_PRESETS
33
+ * @defaultValue POI_PRESETS
34
34
  */
35
35
  presets?: ReadonlyArray<Preset>
36
36
  /**
@@ -80,7 +80,7 @@ function POIExplorerInner({
80
80
  const showLiveBlock = Boolean(
81
81
  runLiveSearch &&
82
82
  subject &&
83
- (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== undefined : !subject.buildLocal)
83
+ (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== null : !subject.buildLocal)
84
84
  )
85
85
 
86
86
  return (
@@ -96,7 +96,10 @@ function POIExplorerInner({
96
96
  <>
97
97
  <SubjectPanel subject={subject} />
98
98
  {subject.kind === "category" ? (
99
- <OverpassBlock overpassQL={result.overpassQL} overpassError={result.overpassError} />
99
+ <OverpassBlock
100
+ overpassQL={result.overpassQL ?? undefined}
101
+ overpassError={result.overpassError ?? undefined}
102
+ />
100
103
  ) : null}
101
104
 
102
105
  {showLiveBlock ? (
@@ -34,6 +34,11 @@ export async function loadPOIRuntime(): Promise<POIRuntime> {
34
34
  categoryID: match.category.id,
35
35
  matchedPhrase: match.matchedPhrase,
36
36
  confidence: match.confidence,
37
+ mechanism: "exact",
38
+ inputPhrase: phrase,
39
+ wikidata: null,
40
+ reading: "preference",
41
+ countryScope: null,
37
42
  }))
38
43
  }
39
44
 
@@ -43,6 +48,10 @@ export async function loadPOIRuntime(): Promise<POIRuntime> {
43
48
  wikidata: match.brand.wikidata,
44
49
  matchedPhrase: match.matchedPhrase,
45
50
  confidence: match.confidence,
51
+ mechanism: "exact",
52
+ inputPhrase: phrase,
53
+ reading: "preference",
54
+ countryScope: null,
46
55
  }))
47
56
  }
48
57
 
package/lib/poi/types.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { QueryKindResult } from "@mailwoman/core/pipeline"
11
+ import type { POIDistanceHit } from "@mailwoman/core/resolver"
11
12
  import type { POIPhraseLookup, createKindClassifier } from "@mailwoman/kind-classifier"
12
13
  import type { createPOITaxonomyLookup } from "@mailwoman/poi-taxonomy/table"
13
14
 
@@ -67,9 +68,9 @@ export interface POIBrandSubject extends POISubjectBase {
67
68
  name: string
68
69
  /**
69
70
  * The brand's Wikidata QID.
70
- * It is absent when the lexicon had no QID for the brand.
71
+ * It is `null` when the lexicon had no QID for the brand.
71
72
  */
72
- wikidata?: string
73
+ wikidata: string | null
73
74
  }
74
75
 
75
76
  /**
@@ -84,22 +85,15 @@ export type POISubject = POICategorySubject | POIBrandSubject
84
85
  */
85
86
  export interface POIExplorerResult {
86
87
  kindResult: QueryKindResult
87
- subject?: POISubject
88
- overpassQL?: string
89
- overpassError?: string
88
+ subject: POISubject | null
89
+ overpassQL: string | null
90
+ overpassError: string | null
90
91
  }
91
92
 
92
93
  /**
93
94
  * One live poi.db hit, as the results list renders it.
94
95
  */
95
- export interface POISearchHit {
96
- name: string
97
- lat: number
98
- lon: number
99
- distanceM: number
100
- country: string
101
- confidence: number
102
- }
96
+ export type POISearchHit = POIDistanceHit
103
97
 
104
98
  /**
105
99
  * The result of an injected live search.
@@ -112,28 +106,33 @@ export type POILiveSearchResult =
112
106
  | { status: "unplaced"; anchor: string }
113
107
  | { status: "unavailable" }
114
108
 
109
+ /**
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
+
115
127
  /**
116
128
  * The injected live-search function.
117
129
  *
118
- * It probes the published poi.db for the resolved category, its Overture leaf
119
- * categories and the anchor text.
130
+ * It probes the published poi.db for the request's subject and the anchor text.
120
131
  * The explorer runs intent-only when this function is absent.
121
132
  *
122
- * For a brand subject, `brandWikidata` contains the QID and the probe fetches by it.
123
- * In that case `categoryID` holds the brand name and `overtureCategoryIDs` is empty.
124
- *
125
133
  * A probe that cannot serve brands should leave `usePOISearch`'s `brandLiveSearch` option unset.
126
134
  */
127
- export type POILiveSearch = (params: {
128
- categoryID: string
129
- overtureCategoryIDs: string[]
130
- anchor: string
131
- /**
132
- * The brand's Wikidata QID.
133
- * It is present only when the subject is a chain brand.
134
- */
135
- brandWikidata?: string
136
- }) => Promise<POILiveSearchResult>
135
+ export type POILiveSearch = (request: POILiveSearchRequest) => Promise<POILiveSearchResult>
137
136
 
138
137
  /**
139
138
  * The state of a "Search live" request.
@@ -12,7 +12,14 @@ import { useCallback, useEffect, useEffectEvent, useState } from "react"
12
12
 
13
13
  import { useDebouncedValue } from "#common/useDebouncedValue"
14
14
  import { loadPOIRuntime } from "#poi/runtime"
15
- 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"
16
23
 
17
24
  /**
18
25
  * Options for {@link usePOISearch}.
@@ -25,7 +32,8 @@ export interface UsePOISearchOptions {
25
32
 
26
33
  /**
27
34
  * The loader that the hook calls once on mount.
28
- * It defaults to `loadPOIRuntime`.
35
+ *
36
+ * @defaultValue `loadPOIRuntime`
29
37
  */
30
38
  loadRuntime?: LoadPOIRuntime
31
39
 
@@ -44,7 +52,8 @@ export interface UsePOISearchOptions {
44
52
 
45
53
  /**
46
54
  * The delay in milliseconds before the text is classified.
47
- * Defaults to 250.
55
+ *
56
+ * @defaultValue `250`
48
57
  */
49
58
  debounceMs?: number
50
59
  }
@@ -83,25 +92,26 @@ export interface UsePOISearch {
83
92
 
84
93
  function buildOverpass(
85
94
  runtime: POIRuntime,
86
- categoryID: string,
87
- matchedPhrase: string,
88
- remainder: string
89
- ): { overpassQL?: string; overpassError?: string } {
95
+ categoryID: string
96
+ ): { overpassQL: string | null; overpassError: string | null } {
90
97
  const category = runtime.lookup.getPOICategory(categoryID)
91
98
 
92
99
  if (!category) {
93
- return {}
100
+ return { overpassQL: null, overpassError: null }
94
101
  }
95
102
 
96
103
  const intent: OverpassIntentLike = {
97
- subject: { kind: "category", categoryIDs: [categoryID], matched: matchedPhrase },
98
- ...(remainder ? { anchor: { text: remainder } } : {}),
104
+ subject: { kind: "category", categoryIDs: [categoryID] },
105
+ anchor: null,
99
106
  }
100
107
 
101
108
  try {
102
- return { overpassQL: emitOverpassQL(intent, category.osmTag ? { osmTags: [category.osmTag] } : {}) }
109
+ return {
110
+ overpassQL: emitOverpassQL(intent, category.osmTag ? { osmTags: [category.osmTag] } : {}),
111
+ overpassError: null,
112
+ }
103
113
  } catch (error) {
104
- return { overpassError: error instanceof Error ? error.message : String(error) }
114
+ return { overpassQL: null, overpassError: error instanceof Error ? error.message : String(error) }
105
115
  }
106
116
  }
107
117
 
@@ -158,10 +168,10 @@ export function usePOISearch({
158
168
  runtime.classify(input, shape).then((kindResult) => {
159
169
  if (cancelled) return
160
170
 
161
- 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
162
172
 
163
173
  if (!matched) {
164
- setStoredResult({ query: trimmed, value: { kindResult } })
174
+ setStoredResult({ query: trimmed, value: { kindResult, subject: null, overpassQL: null, overpassError: null } })
165
175
 
166
176
  return
167
177
  }
@@ -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
  })
@@ -218,11 +230,10 @@ export function usePOISearch({
218
230
  const result = storedResult?.query === trimmedText ? storedResult.value : null
219
231
  const liveSearch: LiveSearchState = storedLive?.query === trimmedText ? storedLive.state : { status: "idle" }
220
232
 
221
- const subject = result?.subject
233
+ const subject = result?.subject ?? null
222
234
 
223
235
  const subjectLiveCapable =
224
- subject !== undefined &&
225
- (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== undefined : !subject.buildLocal)
236
+ subject !== null && (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== null : !subject.buildLocal)
226
237
 
227
238
  const canSearchLive = Boolean(
228
239
  runLiveSearch && runtime && subject && subjectLiveCapable && subject.remainder.trim().length > 0
@@ -231,34 +242,37 @@ export function usePOISearch({
231
242
  const searchLive = useCallback(async () => {
232
243
  if (!runLiveSearch || !runtime || !subject || !subject.remainder.trim()) return
233
244
 
234
- 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
+ }
235
260
 
236
261
  setStoredLive({ query: trimmedText, state: { status: "loading" } })
237
262
 
238
263
  try {
239
- const outcome = await runLiveSearch(
240
- subject.kind === "brand"
241
- ? {
242
- categoryID: subject.name,
243
- overtureCategoryIDs: [],
244
- anchor: subject.remainder,
245
- brandWikidata: subject.wikidata,
246
- }
247
- : {
248
- categoryID: subject.category.id,
249
-
250
- overtureCategoryIDs: runtime.lookup.resolveOvertureCategories(subject.category.id),
251
- anchor: subject.remainder,
252
- }
253
- )
254
-
255
- 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") {
256
272
  setStoredLive({
257
273
  query: trimmedText,
258
- state: { status: "success", hits: outcome.hits, centerName: outcome.centerName },
274
+ state: { status: "error", message: `couldn't place "${response.anchor}"` },
259
275
  })
260
- } else if (outcome.status === "unplaced") {
261
- setStoredLive({ query: trimmedText, state: { status: "error", message: `couldn't place "${outcome.anchor}"` } })
262
276
  } else {
263
277
  setStoredLive({
264
278
  query: trimmedText,
package/lib/poi.ts CHANGED
@@ -27,6 +27,7 @@ export type {
27
27
  POICategorySubject,
28
28
  POIExplorerResult,
29
29
  POILiveSearch,
30
+ POILiveSearchRequest,
30
31
  POILiveSearchResult,
31
32
  POIRuntime,
32
33
  POISearchHit,
@@ -39,15 +39,10 @@ export interface ReleaseManifest<TRelease extends ReleaseBase = ReleaseBase> {
39
39
  */
40
40
  export interface AssetsLoadContext {
41
41
  /**
42
- * The signal aborts when a version or backend switch supersedes this load or when the component unmounts.
42
+ * The signal aborts when a version switch supersedes this load or when the component unmounts.
43
43
  */
44
44
  signal: AbortSignal
45
45
 
46
- /**
47
- * Whether this load should use the CPU WASM backend instead of WebGPU.
48
- */
49
- forceWASM: boolean
50
-
51
46
  /**
52
47
  * Sets the progress line, such as `Loading v7 model (~28 MB)…`.
53
48
  */
@@ -61,7 +56,7 @@ export interface AssetsLoadContext {
61
56
  setStepIndex: (index: number) => void
62
57
 
63
58
  /**
64
- * Reports the backend that the neural runtime resolved to, such as `webgpu (27 MB int8)`.
59
+ * Reports the neural runtime's backend and model size, such as `wasm (27 MB int8)`.
65
60
  */
66
61
  setBackend: (backend: string) => void
67
62
 
@@ -87,9 +82,12 @@ export interface ReleaseRuntimeConfig<TAssets, TRelease extends ReleaseBase = Re
87
82
  loadManifest: (signal: AbortSignal) => Promise<ReleaseManifest<TRelease> | null>
88
83
 
89
84
  /**
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.
85
+ * Loads the asset bundle for one release and reports progress through `ctx`.
86
+ *
87
+ * The hook calls it on every version change.
88
+ * It reports a rejection through `errorMessage`.
89
+ *
90
+ * It disposes a result that resolves after `ctx.signal` aborts.
93
91
  */
94
92
  loadAssets: (release: TRelease, ctx: AssetsLoadContext) => Promise<TAssets>
95
93
 
@@ -106,7 +104,8 @@ export interface ReleaseRuntimeConfig<TAssets, TRelease extends ReleaseBase = Re
106
104
 
107
105
  /**
108
106
  * The progress line shown before the manifest arrives.
109
- * Defaults to `Loading releases…`.
107
+ *
108
+ * @defaultValue `"Loading releases…"`
110
109
  */
111
110
  initialProgress?: string
112
111
  }
@@ -163,31 +162,19 @@ export interface ReleaseLoaderState<TAssets, TRelease extends ReleaseBase = Rele
163
162
  errorMessage: string | null
164
163
 
165
164
  /**
166
- * The backend the neural runtime resolved to, such as `webgpu (27 MB int8)`;
167
- * it is `""` before the backend is known.
165
+ * The neural runtime's backend and model size, such as `wasm (27 MB int8)`;
166
+ * it is `""` before the model loads.
168
167
  */
169
168
  activeBackend: string
170
169
 
171
- /**
172
- * Whether the CPU WASM backend is forced instead of WebGPU.
173
- */
174
- forceWASM: boolean
175
-
176
170
  /**
177
171
  * Switches to another version, clearing any error and reloading the asset bundle.
178
172
  */
179
173
  selectVersion: (version: string) => void
180
-
181
- /**
182
- * Forces or releases the CPU WASM backend.
183
- * Either change reloads the asset bundle.
184
- */
185
- setForceWASM: (forceWASM: boolean) => void
186
174
  }
187
175
 
188
176
  /**
189
- * Loads the release manifest on mount and the selected release's assets whenever
190
- * the version or `forceWASM` changes.
177
+ * Loads the release manifest on mount and the selected release's assets whenever the version changes.
191
178
  *
192
179
  * Each reload aborts the previous load and disposes the old assets.
193
180
  * `ready` becomes true after the new assets load.
@@ -206,7 +193,6 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
206
193
  const [errorMessage, setErrorMessage] = useState<string | null>(null)
207
194
  const [activeBackend, setActiveBackend] = useState<string>("")
208
195
  const [loadingByteFraction, setLoadingByteFraction] = useState<number | null>(null)
209
- const [forceWASM, setForceWASMState] = useState(false)
210
196
 
211
197
  const loadManifestRef = useRef(config.loadManifest)
212
198
  const loadAssetsRef = useRef(config.loadAssets)
@@ -279,7 +265,6 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
279
265
 
280
266
  const ctx: AssetsLoadContext = {
281
267
  signal,
282
- forceWASM,
283
268
  setProgress: (progress) => guard(() => setLoadingProgress(progress)),
284
269
  setStepLabels: (labels) => guard(() => setLoadingStepLabels(labels)),
285
270
  setStepIndex: (index) => guard(() => setLoadingStepIndex(index)),
@@ -307,7 +292,7 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
307
292
  })()
308
293
 
309
294
  return () => controller.abort()
310
- }, [selectedVersion, forceWASM])
295
+ }, [selectedVersion])
311
296
 
312
297
  useEffect(() => {
313
298
  return () => {
@@ -326,8 +311,6 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
326
311
  setErrorMessage(null)
327
312
  }, [])
328
313
 
329
- const setForceWASM = useCallback((next: boolean) => setForceWASMState(next), [])
330
-
331
314
  const selectedRelease = useMemo(
332
315
  () => manifest?.releases.find((r) => r.version === selectedVersion) ?? null,
333
316
  [manifest, selectedVersion]
@@ -345,8 +328,6 @@ export function useReleaseRuntime<TAssets, TRelease extends ReleaseBase = Releas
345
328
  loadingByteFraction,
346
329
  errorMessage,
347
330
  activeBackend,
348
- forceWASM,
349
331
  selectVersion,
350
- setForceWASM,
351
332
  }
352
333
  }