@mailwoman/react 8.0.0 → 8.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 (192) hide show
  1. package/common/ClientOnly.tsx +6 -2
  2. package/common/CopyButton.tsx +9 -3
  3. package/common/KindBadge.tsx +1 -1
  4. package/common/LoadingIndicator.tsx +22 -7
  5. package/common/PresetChips.tsx +6 -2
  6. package/common/cx.ts +3 -1
  7. package/common/useClipboard.ts +7 -2
  8. package/index.ts +5 -0
  9. package/map/BackendControl.tsx +12 -4
  10. package/map/CompareToggle.tsx +27 -9
  11. package/map/DemoControls.tsx +31 -11
  12. package/map/DemoMap.tsx +33 -11
  13. package/map/GeocoderDemo.tsx +25 -8
  14. package/map/OverlayLayers.tsx +7 -3
  15. package/map/PlaceAutocomplete.tsx +25 -9
  16. package/map/PlaceMarker.tsx +18 -6
  17. package/map/ResolvedPlaceLayers.tsx +15 -5
  18. package/map/ResultCamera.tsx +6 -2
  19. package/map/ResultOverlay.tsx +18 -6
  20. package/map/ResultPanel.tsx +21 -7
  21. package/map/VersionPicker.tsx +18 -6
  22. package/map/geometry.ts +28 -8
  23. package/map/index.ts +3 -0
  24. package/map/place-render.ts +36 -12
  25. package/map/types.ts +132 -44
  26. package/map/useCompareState.ts +15 -5
  27. package/map/useDemoGeocode.ts +12 -4
  28. package/map/useMapPlaceRender.ts +3 -1
  29. package/map/usePlaceAutocomplete.ts +52 -18
  30. package/out/common/ClientOnly.d.ts +6 -2
  31. package/out/common/ClientOnly.d.ts.map +1 -1
  32. package/out/common/ClientOnly.js.map +1 -1
  33. package/out/common/CopyButton.d.ts +9 -3
  34. package/out/common/CopyButton.d.ts.map +1 -1
  35. package/out/common/CopyButton.js.map +1 -1
  36. package/out/common/KindBadge.js +1 -1
  37. package/out/common/KindBadge.js.map +1 -1
  38. package/out/common/LoadingIndicator.d.ts +21 -6
  39. package/out/common/LoadingIndicator.d.ts.map +1 -1
  40. package/out/common/LoadingIndicator.js +4 -1
  41. package/out/common/LoadingIndicator.js.map +1 -1
  42. package/out/common/PresetChips.d.ts +6 -2
  43. package/out/common/PresetChips.d.ts.map +1 -1
  44. package/out/common/PresetChips.js.map +1 -1
  45. package/out/common/cx.d.ts +3 -1
  46. package/out/common/cx.d.ts.map +1 -1
  47. package/out/common/cx.js +3 -1
  48. package/out/common/cx.js.map +1 -1
  49. package/out/common/useClipboard.d.ts +3 -1
  50. package/out/common/useClipboard.d.ts.map +1 -1
  51. package/out/common/useClipboard.js +6 -2
  52. package/out/common/useClipboard.js.map +1 -1
  53. package/out/index.d.ts.map +1 -1
  54. package/out/index.js.map +1 -1
  55. package/out/map/BackendControl.d.ts +12 -4
  56. package/out/map/BackendControl.d.ts.map +1 -1
  57. package/out/map/BackendControl.js +3 -1
  58. package/out/map/BackendControl.js.map +1 -1
  59. package/out/map/CompareToggle.d.ts +27 -9
  60. package/out/map/CompareToggle.d.ts.map +1 -1
  61. package/out/map/CompareToggle.js +3 -1
  62. package/out/map/CompareToggle.js.map +1 -1
  63. package/out/map/DemoControls.d.ts +30 -10
  64. package/out/map/DemoControls.d.ts.map +1 -1
  65. package/out/map/DemoControls.js +4 -2
  66. package/out/map/DemoControls.js.map +1 -1
  67. package/out/map/DemoMap.d.ts +33 -11
  68. package/out/map/DemoMap.d.ts.map +1 -1
  69. package/out/map/DemoMap.js.map +1 -1
  70. package/out/map/GeocoderDemo.d.ts +16 -6
  71. package/out/map/GeocoderDemo.d.ts.map +1 -1
  72. package/out/map/GeocoderDemo.js +9 -3
  73. package/out/map/GeocoderDemo.js.map +1 -1
  74. package/out/map/OverlayLayers.d.ts +6 -2
  75. package/out/map/OverlayLayers.d.ts.map +1 -1
  76. package/out/map/OverlayLayers.js +4 -2
  77. package/out/map/OverlayLayers.js.map +1 -1
  78. package/out/map/PlaceAutocomplete.d.ts +24 -8
  79. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  80. package/out/map/PlaceAutocomplete.js +4 -2
  81. package/out/map/PlaceAutocomplete.js.map +1 -1
  82. package/out/map/PlaceMarker.d.ts +15 -5
  83. package/out/map/PlaceMarker.d.ts.map +1 -1
  84. package/out/map/PlaceMarker.js +6 -2
  85. package/out/map/PlaceMarker.js.map +1 -1
  86. package/out/map/ResolvedPlaceLayers.d.ts +15 -5
  87. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  88. package/out/map/ResolvedPlaceLayers.js +3 -1
  89. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  90. package/out/map/ResultCamera.d.ts +6 -2
  91. package/out/map/ResultCamera.d.ts.map +1 -1
  92. package/out/map/ResultCamera.js +3 -1
  93. package/out/map/ResultCamera.js.map +1 -1
  94. package/out/map/ResultOverlay.d.ts +15 -5
  95. package/out/map/ResultOverlay.d.ts.map +1 -1
  96. package/out/map/ResultOverlay.js +3 -1
  97. package/out/map/ResultOverlay.js.map +1 -1
  98. package/out/map/ResultPanel.d.ts +21 -7
  99. package/out/map/ResultPanel.d.ts.map +1 -1
  100. package/out/map/ResultPanel.js +3 -1
  101. package/out/map/ResultPanel.js.map +1 -1
  102. package/out/map/VersionPicker.d.ts +18 -6
  103. package/out/map/VersionPicker.d.ts.map +1 -1
  104. package/out/map/VersionPicker.js +3 -1
  105. package/out/map/VersionPicker.js.map +1 -1
  106. package/out/map/geometry.d.ts +12 -4
  107. package/out/map/geometry.d.ts.map +1 -1
  108. package/out/map/geometry.js +17 -5
  109. package/out/map/geometry.js.map +1 -1
  110. package/out/map/index.d.ts.map +1 -1
  111. package/out/map/index.js.map +1 -1
  112. package/out/map/place-render.d.ts +27 -9
  113. package/out/map/place-render.d.ts.map +1 -1
  114. package/out/map/place-render.js +9 -3
  115. package/out/map/place-render.js.map +1 -1
  116. package/out/map/types.d.ts +132 -44
  117. package/out/map/types.d.ts.map +1 -1
  118. package/out/map/useCompareState.d.ts +15 -5
  119. package/out/map/useCompareState.d.ts.map +1 -1
  120. package/out/map/useCompareState.js.map +1 -1
  121. package/out/map/useDemoGeocode.d.ts +12 -4
  122. package/out/map/useDemoGeocode.d.ts.map +1 -1
  123. package/out/map/useDemoGeocode.js.map +1 -1
  124. package/out/map/useMapPlaceRender.d.ts +3 -1
  125. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  126. package/out/map/useMapPlaceRender.js +3 -1
  127. package/out/map/useMapPlaceRender.js.map +1 -1
  128. package/out/map/usePlaceAutocomplete.d.ts +45 -15
  129. package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
  130. package/out/map/usePlaceAutocomplete.js +7 -3
  131. package/out/map/usePlaceAutocomplete.js.map +1 -1
  132. package/out/pipeline/ConfidenceCell.d.ts.map +1 -1
  133. package/out/pipeline/ConfidenceCell.js +9 -1
  134. package/out/pipeline/ConfidenceCell.js.map +1 -1
  135. package/out/pipeline/PipelineExplorer.d.ts +12 -4
  136. package/out/pipeline/PipelineExplorer.d.ts.map +1 -1
  137. package/out/pipeline/PipelineExplorer.js +6 -2
  138. package/out/pipeline/PipelineExplorer.js.map +1 -1
  139. package/out/pipeline/QueryForm.d.ts +6 -2
  140. package/out/pipeline/QueryForm.d.ts.map +1 -1
  141. package/out/pipeline/QueryForm.js +1 -1
  142. package/out/pipeline/QueryForm.js.map +1 -1
  143. package/out/pipeline/ResolvedPlace.js +1 -1
  144. package/out/pipeline/ResolvedPlace.js.map +1 -1
  145. package/out/pipeline/presets.d.ts +7 -0
  146. package/out/pipeline/presets.d.ts.map +1 -1
  147. package/out/pipeline/presets.js +7 -0
  148. package/out/pipeline/presets.js.map +1 -1
  149. package/out/pipeline/types.d.ts +60 -20
  150. package/out/pipeline/types.d.ts.map +1 -1
  151. package/out/pipeline/useParsePipeline.d.ts +12 -4
  152. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  153. package/out/pipeline/useParsePipeline.js +2 -2
  154. package/out/pipeline/useParsePipeline.js.map +1 -1
  155. package/out/poi/LiveResultsBlock.d.ts +6 -2
  156. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  157. package/out/poi/LiveResultsBlock.js +1 -1
  158. package/out/poi/LiveResultsBlock.js.map +1 -1
  159. package/out/poi/POIExplorer.d.ts +12 -4
  160. package/out/poi/POIExplorer.d.ts.map +1 -1
  161. package/out/poi/POIExplorer.js.map +1 -1
  162. package/out/poi/SubjectPanel.d.ts.map +1 -1
  163. package/out/poi/SubjectPanel.js +4 -2
  164. package/out/poi/SubjectPanel.js.map +1 -1
  165. package/out/poi/runtime.d.ts +9 -2
  166. package/out/poi/runtime.d.ts.map +1 -1
  167. package/out/poi/runtime.js +10 -3
  168. package/out/poi/runtime.js.map +1 -1
  169. package/out/poi/types.d.ts +40 -15
  170. package/out/poi/types.d.ts.map +1 -1
  171. package/out/poi/usePOISearch.d.ts +27 -9
  172. package/out/poi/usePOISearch.d.ts.map +1 -1
  173. package/out/poi/usePOISearch.js +5 -3
  174. package/out/poi/usePOISearch.js.map +1 -1
  175. package/out/runtime/useDemoRuntime.d.ts +81 -27
  176. package/out/runtime/useDemoRuntime.d.ts.map +1 -1
  177. package/out/runtime/useDemoRuntime.js.map +1 -1
  178. package/package.json +4 -4
  179. package/pipeline/ConfidenceCell.tsx +11 -1
  180. package/pipeline/PipelineExplorer.tsx +19 -6
  181. package/pipeline/QueryForm.tsx +7 -3
  182. package/pipeline/ResolvedPlace.tsx +2 -2
  183. package/pipeline/presets.ts +7 -0
  184. package/pipeline/types.ts +60 -20
  185. package/pipeline/useParsePipeline.ts +14 -6
  186. package/poi/LiveResultsBlock.tsx +7 -3
  187. package/poi/POIExplorer.tsx +14 -4
  188. package/poi/SubjectPanel.tsx +4 -2
  189. package/poi/runtime.ts +11 -3
  190. package/poi/types.ts +41 -15
  191. package/poi/usePOISearch.ts +34 -12
  192. package/runtime/useDemoRuntime.ts +82 -27
package/pipeline/types.ts CHANGED
@@ -13,7 +13,9 @@ import type { ReactNode } from "react"
13
13
 
14
14
  import type { KindBadgeResult } from "../common/KindBadge.tsx"
15
15
 
16
- /** One decoded component, as the table + span views render it. Offsets index into `ParseResult.input`. */
16
+ /**
17
+ * One decoded component, as the table + span views render it. Offsets index into `ParseResult.input`.
18
+ */
17
19
  export interface ParsedComponent {
18
20
  tag: string
19
21
  value?: unknown
@@ -22,7 +24,9 @@ export interface ParsedComponent {
22
24
  end?: number
23
25
  }
24
26
 
25
- /** A resolved gazetteer place, projected to what the "Resolved place" panel + copy payload need. */
27
+ /**
28
+ * A resolved gazetteer place, projected to what the "Resolved place" panel + copy payload need.
29
+ */
26
30
  export interface ResolvedPlaceView {
27
31
  id: number
28
32
  name: string
@@ -32,7 +36,9 @@ export interface ResolvedPlaceView {
32
36
  score: number
33
37
  }
34
38
 
35
- /** An additional admin role a resolved place also fulfils (the dual-role / city-state relation, #402). */
39
+ /**
40
+ * An additional admin role a resolved place also fulfils (the dual-role / city-state relation, #402).
41
+ */
36
42
  export interface DualRoleView {
37
43
  id: number
38
44
  name: string
@@ -41,14 +47,18 @@ export interface DualRoleView {
41
47
  role: string
42
48
  }
43
49
 
44
- /** Per-stage wall-clock (ms) for one parse. `resolve` is absent when the lookup is skipped. */
50
+ /**
51
+ * Per-stage wall-clock (ms) for one parse. `resolve` is absent when the lookup is skipped.
52
+ */
45
53
  export interface StageTiming {
46
54
  shape: number
47
55
  classify: number
48
56
  resolve?: number
49
57
  }
50
58
 
51
- /** FST gazetteer-prior provenance, as the "FST prior" disclosure renders it. */
59
+ /**
60
+ * FST gazetteer-prior provenance, as the "FST prior" disclosure renders it.
61
+ */
52
62
  export interface FSTProvenance {
53
63
  builtAt: string
54
64
  stateCount: number
@@ -56,10 +66,14 @@ export interface FSTProvenance {
56
66
  importanceMatches: number
57
67
  }
58
68
 
59
- /** The presentational projection of one parse+resolve. The host's `runParse` produces it. */
69
+ /**
70
+ * The presentational projection of one parse+resolve. The host's `runParse` produces it.
71
+ */
60
72
  export interface ParseResult {
61
73
  input: string
62
- /** Opaque hierarchy — handed straight to a host-injected tree/visualizer panel. */
74
+ /**
75
+ * Opaque hierarchy — handed straight to a host-injected tree/visualizer panel.
76
+ */
63
77
  tree: unknown
64
78
  nodes: ParsedComponent[]
65
79
  kindResult?: KindBadgeResult
@@ -71,7 +85,9 @@ export interface ParseResult {
71
85
  dualRoles?: DualRoleView[]
72
86
  }
73
87
 
74
- /** Bundle-load progress surfaced before the runtime is `ready`. */
88
+ /**
89
+ * Bundle-load progress surfaced before the runtime is `ready`.
90
+ */
75
91
  export interface PipelineLoadingState {
76
92
  progress?: string
77
93
  stepLabels: string[]
@@ -83,15 +99,25 @@ export interface PipelineLoadingState {
83
99
  * progress + errors. This package never imports the model or gazetteer — it only calls this contract.
84
100
  */
85
101
  export interface PipelineRuntime {
86
- /** Whether the model/gazetteer bundle is ready to parse. */
102
+ /**
103
+ * Whether the model/gazetteer bundle is ready to parse.
104
+ */
87
105
  ready: boolean
88
- /** Execute a full parse+resolve, reporting stage progress via `onStage` (0-based index into `parseStageLabels`). */
106
+ /**
107
+ * Execute a full parse+resolve, reporting stage progress via `onStage` (0-based index into `parseStageLabels`).
108
+ */
89
109
  runParse: (input: string, hooks: { onStage: (stage: number) => void }) => Promise<ParseResult>
90
- /** Per-parse progress labels (differ by whether a gazetteer lookup is wired). */
110
+ /**
111
+ * Per-parse progress labels (differ by whether a gazetteer lookup is wired).
112
+ */
91
113
  parseStageLabels: string[]
92
- /** Bundle-load progress (before `ready`). */
114
+ /**
115
+ * Bundle-load progress (before `ready`).
116
+ */
93
117
  loading?: PipelineLoadingState | null
94
- /** Error surfaced by the bundle load, distinct from a per-parse error. */
118
+ /**
119
+ * Error surfaced by the bundle load, distinct from a per-parse error.
120
+ */
95
121
  errorMessage?: string | null
96
122
  }
97
123
 
@@ -101,18 +127,32 @@ export interface PipelineRuntime {
101
127
  * components nor their heavy data types.
102
128
  */
103
129
  export interface PipelinePanels {
104
- /** Rendered above the form (e.g. the docs "About this demo"). */
130
+ /**
131
+ * Rendered above the form (e.g. the docs "About this demo").
132
+ */
105
133
  header?: ReactNode
106
- /** Rendered below everything (e.g. a guided tour). */
134
+ /**
135
+ * Rendered below everything (e.g. a guided tour).
136
+ */
107
137
  footer?: ReactNode
108
- /** Model-version selector, wired to the host's bundle state. */
138
+ /**
139
+ * Model-version selector, wired to the host's bundle state.
140
+ */
109
141
  versionControl?: ReactNode
110
- /** Backend indicator + WASM toggle. */
142
+ /**
143
+ * Backend indicator + WASM toggle.
144
+ */
111
145
  backendControl?: ReactNode
112
- /** One-line release blurb. */
146
+ /**
147
+ * One-line release blurb.
148
+ */
113
149
  releaseInfo?: ReactNode
114
- /** Heavy visualizers (span highlight, tree, timing, BIO, …), rendered from the result. */
150
+ /**
151
+ * Heavy visualizers (span highlight, tree, timing, BIO, …), rendered from the result.
152
+ */
115
153
  extras?: (result: ParseResult) => ReactNode
116
- /** Rendered in place of the resolved-place panel when nothing resolved (host's FailureDiagnostic). */
154
+ /**
155
+ * Rendered in place of the resolved-place panel when nothing resolved (host's FailureDiagnostic).
156
+ */
117
157
  failure?: (result: ParseResult) => ReactNode
118
158
  }
@@ -22,17 +22,25 @@ export interface UseParsePipeline {
22
22
  text: string
23
23
  setText: (text: string) => void
24
24
  busy: boolean
25
- /** 0-based index into `runtime.parseStageLabels`; -1 when idle. */
25
+ /**
26
+ * 0-based index into `runtime.parseStageLabels`; -1 when idle.
27
+ */
26
28
  parseStage: number
27
29
  result: ParseResult | null
28
30
  parseError: string | null
29
31
  selectedCandidateIndex: number
30
32
  selectCandidate: (index: number) => void
31
- /** The currently-selected candidate (falls back to the first), or null. */
33
+ /**
34
+ * The currently-selected candidate (falls back to the first), or null.
35
+ */
32
36
  selectedCandidate: ResolvedPlaceView | null
33
- /** Run a parse for the current text. Safe to bind to a form's `onSubmit`. */
37
+ /**
38
+ * Run a parse for the current text. Safe to bind to a form's `onSubmit`.
39
+ */
34
40
  submit: () => Promise<void>
35
- /** Clear the result (used when a preset replaces the input). */
41
+ /**
42
+ * Clear the result (used when a preset replaces the input).
43
+ */
36
44
  reset: () => void
37
45
  }
38
46
 
@@ -55,8 +63,8 @@ export function useParsePipeline({ runtime, defaultText }: UseParsePipelineOptio
55
63
  const parsed = await runtime.runParse(text, { onStage: setParseStage })
56
64
  setSelectedCandidateIndex(0)
57
65
  setResult(parsed)
58
- } catch (err) {
59
- setParseError(err instanceof Error ? err.message : String(err))
66
+ } catch (error) {
67
+ setParseError(error instanceof Error ? error.message : String(error))
60
68
  } finally {
61
69
  setBusy(false)
62
70
  setParseStage(-1)
@@ -14,9 +14,13 @@ import { formatDistance } from "./runtime.ts"
14
14
  import type { LiveSearchState } from "./types.ts"
15
15
 
16
16
  export interface LiveResultsBlockProps {
17
- /** Subject label for empty-state copy — a category ("No hospital results near …") or a brand ("No chevron results …"). */
17
+ /**
18
+ * Subject label for empty-state copy — a category ("No hospital results near …") or a brand ("No chevron results …").
19
+ */
18
20
  subjectLabel: string
19
- /** The location anchor the search runs against (empty ⇒ the button is disabled with a hint). */
21
+ /**
22
+ * The location anchor the search runs against (empty ⇒ the button is disabled with a hint).
23
+ */
20
24
  anchor: string
21
25
  state: LiveSearchState
22
26
  onSearch: () => void
@@ -45,7 +49,7 @@ export function LiveResultsBlock({ subjectLabel, anchor, state, onSearch }: Live
45
49
  ) : state.status === "error" ? (
46
50
  <p className="mw-error">{state.message}</p>
47
51
  ) : state.status === "success" ? (
48
- state.hits.length === 0 ? (
52
+ !state.hits.length ? (
49
53
  <p className="mw-muted">
50
54
  No {subjectLabel.toLowerCase()} results near {state.centerName}.
51
55
  </p>
@@ -24,13 +24,21 @@ import type { LoadPOIRuntime, POILiveSearch } from "./types.ts"
24
24
  import { usePOISearch } from "./usePOISearch.ts"
25
25
 
26
26
  export interface POIExplorerProps {
27
- /** Query to pre-fill in the input. */
27
+ /**
28
+ * Query to pre-fill in the input.
29
+ */
28
30
  defaultText?: string
29
- /** Example chips. @default the built-in POI presets */
31
+ /**
32
+ * Example chips. @default the built-in POI presets
33
+ */
30
34
  presets?: ReadonlyArray<Preset>
31
- /** Override the taxonomy-runtime loader (stories/tests inject a mock). */
35
+ /**
36
+ * Override the taxonomy-runtime loader (stories/tests inject a mock).
37
+ */
32
38
  loadRuntime?: LoadPOIRuntime
33
- /** Live poi.db probe. Absent ⇒ intent-only (no live-results affordance). */
39
+ /**
40
+ * Live poi.db probe. Absent ⇒ intent-only (no live-results affordance).
41
+ */
34
42
  runLiveSearch?: POILiveSearch
35
43
  /**
36
44
  * Whether {@link runLiveSearch} can serve BRAND subjects (fetch by QID). Default false: a brand subject shows the
@@ -53,6 +61,7 @@ function POIExplorerInner({
53
61
  brandLiveSearch,
54
62
  }: POIExplorerInnerProps): ReactNode {
55
63
  const [text, setText] = useState(defaultText)
64
+
56
65
  const { result, liveSearch, searchLive } = usePOISearch({
57
66
  text,
58
67
  loadRuntime,
@@ -61,6 +70,7 @@ function POIExplorerInner({
61
70
  })
62
71
 
63
72
  const subject = result?.subject
73
+
64
74
  // The live block appears whenever the subject COULD be live-searched with an anchor — i.e. capable minus the
65
75
  // anchor-present requirement (`canSearchLive` also requires an anchor; the block itself renders the "add an anchor"
66
76
  // hint, so it must show one step earlier). Category: not build-local. Brand: brand-capable probe + a QID.
@@ -16,7 +16,9 @@ export interface SubjectPanelProps {
16
16
  subject: POISubject
17
17
  }
18
18
 
19
- /** The shared match-detail list (matched phrase, confidence, anchor) — identical for category + brand subjects. */
19
+ /**
20
+ * The shared match-detail list (matched phrase, confidence, anchor) — identical for category + brand subjects.
21
+ */
20
22
  function MatchDetail({ subject }: SubjectPanelProps): ReactNode {
21
23
  return (
22
24
  <dl className="mw-subject__detail">
@@ -27,7 +29,7 @@ function MatchDetail({ subject }: SubjectPanelProps): ReactNode {
27
29
  <dt>confidence</dt>
28
30
  <dd>{Math.round(subject.confidence * 100)}%</dd>
29
31
  <dt>anchor</dt>
30
- <dd>{subject.remainder ? subject.remainder : <em>none — global query</em>}</dd>
32
+ <dd>{subject.remainder || <em>none — global query</em>}</dd>
31
33
  </dl>
32
34
  )
33
35
  }
package/poi/runtime.ts CHANGED
@@ -30,6 +30,7 @@ export async function loadPOIRuntime(): Promise<POIRuntime> {
30
30
  import("@mailwoman/poi-taxonomy/data/taxonomy.json").then((m) => m.default),
31
31
  import("@mailwoman/poi-taxonomy/data/brands.json").then((m) => m.default),
32
32
  ])
33
+
33
34
  const lookup = createPOITaxonomyLookup(table as unknown as Parameters<typeof createPOITaxonomyLookup>[0])
34
35
  const brands = createPOIBrandLookup(brandTable as unknown as Parameters<typeof createPOIBrandLookup>[0])
35
36
 
@@ -38,7 +39,7 @@ export async function loadPOIRuntime(): Promise<POIRuntime> {
38
39
  const lexicon: POIPhraseLookup = (phrase, locale) => {
39
40
  const categoryHits = lookup.lookupPOICategory(phrase, locale)
40
41
 
41
- if (categoryHits.length > 0) {
42
+ if (categoryHits.length) {
42
43
  return categoryHits.map((match) => ({
43
44
  kind: "category",
44
45
  categoryID: match.category.id,
@@ -59,7 +60,9 @@ export async function loadPOIRuntime(): Promise<POIRuntime> {
59
60
  return { lookup, lexicon, classify: createKindClassifier({ poiLexicon: lexicon }) }
60
61
  }
61
62
 
62
- /** Default example queries for the POI explorer — a mix of category, build-local, and chain-brand subjects. */
63
+ /**
64
+ * Default example queries for the POI explorer — a mix of category, build-local, and chain-brand subjects.
65
+ */
63
66
  export const POI_PRESETS = [
64
67
  { label: "Drinking fountain", value: "drinking fountain near Springfield" },
65
68
  { label: "Fire hydrant", value: "fire hydrant" },
@@ -68,9 +71,14 @@ export const POI_PRESETS = [
68
71
  { label: "Applebee's (brand)", value: "applebee's near Chicago" },
69
72
  ] as const
70
73
 
74
+ /**
75
+ * Query the POI explorer opens on — the first preset, so the two stay in step.
76
+ */
71
77
  export const POI_DEFAULT_TEXT = POI_PRESETS[0].value
72
78
 
73
- /** `742 m` under 1 km, `1.9 km` past it — matches the demo's distance captions. */
79
+ /**
80
+ * `742 m` under 1 km, `1.9 km` past it — matches the demo's distance captions.
81
+ */
74
82
  export function formatDistance(distanceM: number): string {
75
83
  if (distanceM < 1000) return `${Math.round(distanceM)} m`
76
84
 
package/poi/types.ts CHANGED
@@ -9,33 +9,43 @@
9
9
  * package's browser graph (it lives in the docs site, which knows where the layer is served).
10
10
  */
11
11
 
12
- import type { POIPhraseLookup, QueryKindResult } from "@mailwoman/kind-classifier"
13
- import type { createKindClassifier } from "@mailwoman/kind-classifier"
12
+ import type { POIPhraseLookup, QueryKindResult, createKindClassifier } from "@mailwoman/kind-classifier"
14
13
  import type { createPOITaxonomyLookup } from "@mailwoman/poi-taxonomy/table"
15
14
 
16
15
  export type TaxonomyLookup = ReturnType<typeof createPOITaxonomyLookup>
16
+
17
17
  export type CategoryRecord = NonNullable<ReturnType<TaxonomyLookup["getPOICategory"]>>
18
18
 
19
- /** The lazily-loaded POI runtime: the taxonomy lookup, the adapted lexicon, and the kind classifier over it. */
19
+ /**
20
+ * The lazily-loaded POI runtime: the taxonomy lookup, the adapted lexicon, and the kind classifier over it.
21
+ */
20
22
  export interface POIRuntime {
21
23
  lookup: TaxonomyLookup
22
24
  lexicon: POIPhraseLookup
23
25
  classify: ReturnType<typeof createKindClassifier>
24
26
  }
25
27
 
26
- /** Fields shared by every resolved POI subject, category or brand. */
28
+ /**
29
+ * Fields shared by every resolved POI subject, category or brand.
30
+ */
27
31
  export interface POISubjectBase {
28
32
  matchedPhrase: string
29
33
  confidence: number
30
- /** The non-subject remainder of the query (the location anchor, e.g. "near Springfield"). */
34
+ /**
35
+ * The non-subject remainder of the query (the location anchor, e.g. "near Springfield").
36
+ */
31
37
  remainder: string
32
38
  }
33
39
 
34
- /** A resolved POI subject that names a taxonomy CATEGORY (`cafe`, `hospital`, `drinking fountain`). */
40
+ /**
41
+ * A resolved POI subject that names a taxonomy CATEGORY (`cafe`, `hospital`, `drinking fountain`).
42
+ */
35
43
  export interface POICategorySubject extends POISubjectBase {
36
44
  kind: "category"
37
45
  category: CategoryRecord
38
- /** Whether this category needs the locally-built OSM (ODbL) layer — precomputed off the runtime. */
46
+ /**
47
+ * Whether this category needs the locally-built OSM (ODbL) layer — precomputed off the runtime.
48
+ */
39
49
  buildLocal: boolean
40
50
  }
41
51
 
@@ -46,16 +56,24 @@ export interface POICategorySubject extends POISubjectBase {
46
56
  */
47
57
  export interface POIBrandSubject extends POISubjectBase {
48
58
  kind: "brand"
49
- /** The brand's canonical display name. */
59
+ /**
60
+ * The brand's canonical display name.
61
+ */
50
62
  name: string
51
- /** Wikidata QID, when the lexicon carried one (`Q319642` = Chevron). Absent ⇒ matched by name alone. */
63
+ /**
64
+ * Wikidata QID, when the lexicon carried one (`Q319642` = Chevron). Absent ⇒ matched by name alone.
65
+ */
52
66
  wikidata?: string
53
67
  }
54
68
 
55
- /** A resolved POI subject — a taxonomy category or a chain brand — plus its match metadata. */
69
+ /**
70
+ * A resolved POI subject — a taxonomy category or a chain brand — plus its match metadata.
71
+ */
56
72
  export type POISubject = POICategorySubject | POIBrandSubject
57
73
 
58
- /** The intent-only result: the kind verdict plus (when a subject was detected) its OverpassQL export (category only). */
74
+ /**
75
+ * The intent-only result: the kind verdict plus (when a subject was detected) its OverpassQL export (category only).
76
+ */
59
77
  export interface POIExplorerResult {
60
78
  kindResult: QueryKindResult
61
79
  subject?: POISubject
@@ -63,7 +81,9 @@ export interface POIExplorerResult {
63
81
  overpassError?: string
64
82
  }
65
83
 
66
- /** One live poi.db hit, as the results list renders it. */
84
+ /**
85
+ * One live poi.db hit, as the results list renders it.
86
+ */
67
87
  export interface POISearchHit {
68
88
  name: string
69
89
  lat: number
@@ -97,16 +117,22 @@ export type POILiveSearch = (params: {
97
117
  categoryID: string
98
118
  overtureCategoryIDs: string[]
99
119
  anchor: string
100
- /** Present when the subject is a chain brand — the probe fetches by this QID, not a category k-ring. */
120
+ /**
121
+ * Present when the subject is a chain brand — the probe fetches by this QID, not a category k-ring.
122
+ */
101
123
  brandWikidata?: string
102
124
  }) => Promise<POILiveSearchResult>
103
125
 
104
- /** "Search live" state machine. */
126
+ /**
127
+ * "Search live" state machine.
128
+ */
105
129
  export type LiveSearchState =
106
130
  | { status: "idle" }
107
131
  | { status: "loading" }
108
132
  | { status: "error"; message: string }
109
133
  | { status: "success"; hits: POISearchHit[]; centerName: string }
110
134
 
111
- /** A single-argument runtime loader — injectable so stories/tests can substitute a mock taxonomy. */
135
+ /**
136
+ * A single-argument runtime loader — injectable so stories/tests can substitute a mock taxonomy.
137
+ */
112
138
  export type LoadPOIRuntime = () => Promise<POIRuntime>
@@ -20,11 +20,17 @@ import { loadPOIRuntime } from "./runtime.ts"
20
20
  import type { LiveSearchState, LoadPOIRuntime, POIExplorerResult, POILiveSearch, POIRuntime } from "./types.ts"
21
21
 
22
22
  export interface UsePOISearchOptions {
23
- /** The current query text (controlled by the caller). */
23
+ /**
24
+ * The current query text (controlled by the caller).
25
+ */
24
26
  text: string
25
- /** Runtime loader. @default loadPOIRuntime */
27
+ /**
28
+ * Runtime loader. @default loadPOIRuntime
29
+ */
26
30
  loadRuntime?: LoadPOIRuntime
27
- /** Injected live-search probe. Absent ⇒ the live-results affordance is disabled. */
31
+ /**
32
+ * Injected live-search probe. Absent ⇒ the live-results affordance is disabled.
33
+ */
28
34
  runLiveSearch?: POILiveSearch
29
35
  /**
30
36
  * Whether the injected probe can serve BRAND subjects (fetch by Wikidata QID). Default false: brand subjects show the
@@ -33,24 +39,38 @@ export interface UsePOISearchOptions {
33
39
  * live search is unaffected either way.
34
40
  */
35
41
  brandLiveSearch?: boolean
36
- /** Debounce before (re)classifying. @default 250 */
42
+ /**
43
+ * Debounce before (re)classifying. @default 250
44
+ */
37
45
  debounceMs?: number
38
46
  }
39
47
 
40
48
  export interface UsePOISearch {
41
- /** True once the taxonomy runtime has loaded. */
49
+ /**
50
+ * True once the taxonomy runtime has loaded.
51
+ */
42
52
  runtimeReady: boolean
43
- /** The intent result for the current (debounced) query, or null for empty input. */
53
+ /**
54
+ * The intent result for the current (debounced) query, or null for empty input.
55
+ */
44
56
  result: POIExplorerResult | null
45
- /** State of the on-demand live poi.db search. */
57
+ /**
58
+ * State of the on-demand live poi.db search.
59
+ */
46
60
  liveSearch: LiveSearchState
47
- /** Whether a live search can run right now (a probe is wired + there's a resolved subject with an anchor). */
61
+ /**
62
+ * Whether a live search can run right now (a probe is wired + there's a resolved subject with an anchor).
63
+ */
48
64
  canSearchLive: boolean
49
- /** Kick off a live search for the current subject. No-op when {@link canSearchLive} is false. */
65
+ /**
66
+ * Kick off a live search for the current subject. No-op when {@link canSearchLive} is false.
67
+ */
50
68
  searchLive: () => Promise<void>
51
69
  }
52
70
 
53
- /** Compute the OverpassQL export for a matched subject, capturing any emitter error rather than throwing. */
71
+ /**
72
+ * Compute the OverpassQL export for a matched subject, capturing any emitter error rather than throwing.
73
+ */
54
74
  function buildOverpass(
55
75
  runtime: POIRuntime,
56
76
  categoryID: string,
@@ -70,8 +90,8 @@ function buildOverpass(
70
90
 
71
91
  try {
72
92
  return { overpassQL: emitOverpassQL(intent, category.osmTag ? { osmTag: category.osmTag } : {}) }
73
- } catch (err) {
74
- return { overpassError: err instanceof Error ? err.message : String(err) }
93
+ } catch (error) {
94
+ return { overpassError: error instanceof Error ? error.message : String(error) }
75
95
  }
76
96
  }
77
97
 
@@ -184,12 +204,14 @@ export function usePOISearch({
184
204
  }, [debouncedText, runtime])
185
205
 
186
206
  const subject = result?.subject
207
+
187
208
  // A subject is live-searchable when a probe is wired, it has an anchor, and: a CATEGORY that isn't build-local, or a
188
209
  // BRAND with a QID AND a brand-capable probe (`brandLiveSearch`). Brands without a QID / without a brand probe show
189
210
  // the intent + QID chip but no live affordance.
190
211
  const subjectLiveCapable =
191
212
  subject !== undefined &&
192
213
  (subject.kind === "brand" ? brandLiveSearch && subject.wikidata !== undefined : !subject.buildLocal)
214
+
193
215
  const canSearchLive = Boolean(
194
216
  runLiveSearch && runtime && subject && subjectLiveCapable && subject.remainder.trim().length > 0
195
217
  )