@mailwoman/react 9.3.0 → 10.0.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.
- package/fonts.css +59 -0
- package/lib/common/About.tsx +105 -0
- package/lib/common/KindBadge.tsx +2 -7
- package/lib/common/confidence-tiers.ts +50 -0
- package/lib/common/index.ts +22 -0
- package/lib/common/text-tokens.ts +88 -0
- package/lib/index.ts +10 -103
- package/lib/map/BackendControl.tsx +7 -3
- package/lib/map/CompareToggle.tsx +5 -4
- package/lib/map/{GeocoderDemo.tsx → Geocoder.tsx} +81 -61
- package/lib/map/GeocoderControls.tsx +352 -0
- package/lib/map/{DemoMap.tsx → MapCanvas.tsx} +15 -15
- package/lib/map/MapChipRow.tsx +77 -0
- package/lib/map/MapCompass.tsx +77 -0
- package/lib/map/MapControlStack.tsx +98 -0
- package/lib/map/MapFooter.tsx +115 -0
- package/lib/map/MapProgressBar.tsx +58 -0
- package/lib/map/MapSearchBar.tsx +68 -0
- package/lib/map/MapSheet.tsx +62 -0
- package/lib/map/PlaceMarker.tsx +1 -1
- package/lib/map/ResolvedPlaceLayers.tsx +3 -3
- package/lib/map/ResultCamera.tsx +1 -1
- package/lib/map/ResultPanel.tsx +2 -2
- package/lib/map/VersionPicker.tsx +3 -3
- package/lib/map/fake-runtime.ts +122 -0
- package/lib/map/index.ts +35 -16
- package/lib/map/map-debug.ts +51 -0
- package/lib/map/place-render.ts +3 -2
- package/lib/map/types.ts +58 -42
- package/lib/map/{useDemoGeocode.ts → useGeocode.ts} +8 -8
- package/lib/map/useMapBearing.ts +56 -0
- package/lib/map/useMapLabelPick.ts +123 -0
- package/lib/pipeline/CandidatePicker.tsx +1 -1
- package/lib/pipeline/ComponentTable.tsx +1 -2
- package/lib/pipeline/ConfidenceCell.tsx +7 -19
- package/lib/pipeline/FailureDiagnostic.tsx +56 -0
- package/lib/pipeline/ResolvedPlace.tsx +17 -4
- package/lib/pipeline/SpanHighlight.tsx +97 -0
- package/lib/pipeline/TimingPanel.tsx +65 -0
- package/lib/pipeline/TreeView.tsx +55 -0
- package/lib/pipeline/copy.ts +1 -1
- package/lib/pipeline/index.ts +36 -0
- package/lib/pipeline/types.ts +2 -75
- package/lib/pipeline/useParsePipeline.ts +31 -19
- package/lib/poi/index.ts +39 -0
- package/lib/poi/types.ts +2 -1
- package/lib/runtime/index.ts +16 -0
- package/lib/runtime/{useDemoRuntime.ts → useReleaseRuntime.ts} +22 -24
- package/out/common/About.d.ts +21 -0
- package/out/common/About.d.ts.map +1 -0
- package/out/common/About.js +8 -0
- package/out/common/About.js.map +1 -0
- package/out/common/KindBadge.d.ts +2 -9
- package/out/common/KindBadge.d.ts.map +1 -1
- package/out/common/KindBadge.js.map +1 -1
- package/out/common/confidence-tiers.d.ts +36 -0
- package/out/common/confidence-tiers.d.ts.map +1 -0
- package/out/common/confidence-tiers.js +42 -0
- package/out/common/confidence-tiers.js.map +1 -0
- package/out/common/index.d.ts +22 -0
- package/out/common/index.d.ts.map +1 -0
- package/out/common/index.js +15 -0
- package/out/common/index.js.map +1 -0
- package/out/common/text-tokens.d.ts +46 -0
- package/out/common/text-tokens.d.ts.map +1 -0
- package/out/common/text-tokens.js +52 -0
- package/out/common/text-tokens.js.map +1 -0
- package/out/index.d.ts +5 -51
- package/out/index.d.ts.map +1 -1
- package/out/index.js +4 -30
- package/out/index.js.map +1 -1
- package/out/map/BackendControl.d.ts.map +1 -1
- package/out/map/BackendControl.js +1 -1
- package/out/map/BackendControl.js.map +1 -1
- package/out/map/CompareToggle.d.ts +2 -2
- package/out/map/CompareToggle.d.ts.map +1 -1
- package/out/map/CompareToggle.js +1 -1
- package/out/map/CompareToggle.js.map +1 -1
- package/out/map/Geocoder.d.ts +67 -0
- package/out/map/Geocoder.d.ts.map +1 -0
- package/out/map/Geocoder.js +98 -0
- package/out/map/Geocoder.js.map +1 -0
- package/out/map/GeocoderControls.d.ts +81 -0
- package/out/map/GeocoderControls.d.ts.map +1 -0
- package/out/map/GeocoderControls.js +79 -0
- package/out/map/GeocoderControls.js.map +1 -0
- package/out/map/{DemoMap.d.ts → MapCanvas.d.ts} +15 -15
- package/out/map/MapCanvas.d.ts.map +1 -0
- package/out/map/{DemoMap.js → MapCanvas.js} +2 -2
- package/out/map/MapCanvas.js.map +1 -0
- package/out/map/MapChipRow.d.ts +47 -0
- package/out/map/MapChipRow.d.ts.map +1 -0
- package/out/map/MapChipRow.js +12 -0
- package/out/map/MapChipRow.js.map +1 -0
- package/out/map/MapCompass.d.ts +42 -0
- package/out/map/MapCompass.d.ts.map +1 -0
- package/out/map/MapCompass.js +11 -0
- package/out/map/MapCompass.js.map +1 -0
- package/out/map/MapControlStack.d.ts +57 -0
- package/out/map/MapControlStack.d.ts.map +1 -0
- package/out/map/MapControlStack.js +15 -0
- package/out/map/MapControlStack.js.map +1 -0
- package/out/map/MapFooter.d.ts +40 -0
- package/out/map/MapFooter.d.ts.map +1 -0
- package/out/map/MapFooter.js +51 -0
- package/out/map/MapFooter.js.map +1 -0
- package/out/map/MapProgressBar.d.ts +34 -0
- package/out/map/MapProgressBar.d.ts.map +1 -0
- package/out/map/MapProgressBar.js +8 -0
- package/out/map/MapProgressBar.js.map +1 -0
- package/out/map/MapSearchBar.d.ts +49 -0
- package/out/map/MapSearchBar.d.ts.map +1 -0
- package/out/map/MapSearchBar.js +6 -0
- package/out/map/MapSearchBar.js.map +1 -0
- package/out/map/MapSheet.d.ts +32 -0
- package/out/map/MapSheet.d.ts.map +1 -0
- package/out/map/MapSheet.js +33 -0
- package/out/map/MapSheet.js.map +1 -0
- package/out/map/OverlayLayers.js +1 -1
- package/out/map/PlaceMarker.d.ts +1 -1
- package/out/map/ResolvedPlaceLayers.d.ts +3 -3
- package/out/map/ResolvedPlaceLayers.js +1 -1
- package/out/map/ResultCamera.d.ts +1 -1
- package/out/map/ResultCamera.js +1 -1
- package/out/map/ResultPanel.d.ts +2 -2
- package/out/map/ResultPanel.d.ts.map +1 -1
- package/out/map/ResultPanel.js +1 -1
- package/out/map/ResultPanel.js.map +1 -1
- package/out/map/VersionPicker.d.ts +3 -3
- package/out/map/VersionPicker.d.ts.map +1 -1
- package/out/map/fake-runtime.d.ts +38 -0
- package/out/map/fake-runtime.d.ts.map +1 -0
- package/out/map/fake-runtime.js +107 -0
- package/out/map/fake-runtime.js.map +1 -0
- package/out/map/index.d.ts +27 -10
- package/out/map/index.d.ts.map +1 -1
- package/out/map/index.js +16 -6
- package/out/map/index.js.map +1 -1
- package/out/map/map-debug.d.ts +31 -0
- package/out/map/map-debug.d.ts.map +1 -0
- package/out/map/map-debug.js +31 -0
- package/out/map/map-debug.js.map +1 -0
- package/out/map/place-render.d.ts +2 -2
- package/out/map/place-render.d.ts.map +1 -1
- package/out/map/place-render.js +1 -1
- package/out/map/place-render.js.map +1 -1
- package/out/map/types.d.ts +60 -42
- package/out/map/types.d.ts.map +1 -1
- package/out/map/types.js +5 -5
- package/out/map/{useDemoGeocode.d.ts → useGeocode.d.ts} +9 -9
- package/out/map/useGeocode.d.ts.map +1 -0
- package/out/map/{useDemoGeocode.js → useGeocode.js} +4 -4
- package/out/map/useGeocode.js.map +1 -0
- package/out/map/useMapBearing.d.ts +28 -0
- package/out/map/useMapBearing.d.ts.map +1 -0
- package/out/map/useMapBearing.js +35 -0
- package/out/map/useMapBearing.js.map +1 -0
- package/out/map/useMapLabelPick.d.ts +23 -0
- package/out/map/useMapLabelPick.d.ts.map +1 -0
- package/out/map/useMapLabelPick.js +104 -0
- package/out/map/useMapLabelPick.js.map +1 -0
- package/out/pipeline/CandidatePicker.d.ts +1 -1
- package/out/pipeline/CandidatePicker.d.ts.map +1 -1
- package/out/pipeline/CandidatePicker.js +1 -1
- package/out/pipeline/CandidatePicker.js.map +1 -1
- package/out/pipeline/ComponentTable.d.ts +1 -1
- package/out/pipeline/ComponentTable.d.ts.map +1 -1
- package/out/pipeline/ComponentTable.js.map +1 -1
- package/out/pipeline/ConfidenceCell.d.ts +2 -2
- package/out/pipeline/ConfidenceCell.d.ts.map +1 -1
- package/out/pipeline/ConfidenceCell.js +4 -16
- package/out/pipeline/ConfidenceCell.js.map +1 -1
- package/out/pipeline/FailureDiagnostic.d.ts +15 -0
- package/out/pipeline/FailureDiagnostic.d.ts.map +1 -0
- package/out/pipeline/FailureDiagnostic.js +21 -0
- package/out/pipeline/FailureDiagnostic.js.map +1 -0
- package/out/pipeline/PipelineExplorer.js +1 -1
- package/out/pipeline/QueryForm.js +1 -1
- package/out/pipeline/ResolvedPlace.d.ts +1 -1
- package/out/pipeline/ResolvedPlace.d.ts.map +1 -1
- package/out/pipeline/ResolvedPlace.js +4 -10
- package/out/pipeline/ResolvedPlace.js.map +1 -1
- package/out/pipeline/SpanHighlight.d.ts +23 -0
- package/out/pipeline/SpanHighlight.d.ts.map +1 -0
- package/out/pipeline/SpanHighlight.js +31 -0
- package/out/pipeline/SpanHighlight.js.map +1 -0
- package/out/pipeline/TimingPanel.d.ts +16 -0
- package/out/pipeline/TimingPanel.d.ts.map +1 -0
- package/out/pipeline/TimingPanel.js +20 -0
- package/out/pipeline/TimingPanel.js.map +1 -0
- package/out/pipeline/TreeView.d.ts +17 -0
- package/out/pipeline/TreeView.d.ts.map +1 -0
- package/out/pipeline/TreeView.js +15 -0
- package/out/pipeline/TreeView.js.map +1 -0
- package/out/pipeline/copy.d.ts +1 -1
- package/out/pipeline/copy.d.ts.map +1 -1
- package/out/pipeline/index.d.ts +33 -0
- package/out/pipeline/index.d.ts.map +1 -0
- package/out/pipeline/index.js +21 -0
- package/out/pipeline/index.js.map +1 -0
- package/out/pipeline/types.d.ts +2 -68
- package/out/pipeline/types.d.ts.map +1 -1
- package/out/pipeline/types.js +1 -1
- package/out/pipeline/useParsePipeline.d.ts +7 -3
- package/out/pipeline/useParsePipeline.d.ts.map +1 -1
- package/out/pipeline/useParsePipeline.js +6 -2
- package/out/pipeline/useParsePipeline.js.map +1 -1
- package/out/poi/LiveResultsBlock.js +1 -1
- package/out/poi/POIExplorer.js +1 -1
- package/out/poi/SubjectPanel.js +1 -1
- package/out/poi/index.d.ts +23 -0
- package/out/poi/index.d.ts.map +1 -0
- package/out/poi/index.js +15 -0
- package/out/poi/index.js.map +1 -0
- package/out/poi/runtime.d.ts.map +1 -1
- package/out/poi/types.d.ts +2 -1
- package/out/poi/types.d.ts.map +1 -1
- package/out/runtime/index.d.ts +9 -0
- package/out/runtime/index.d.ts.map +1 -0
- package/out/runtime/index.js +8 -0
- package/out/runtime/index.js.map +1 -0
- package/out/runtime/{useDemoRuntime.d.ts → useReleaseRuntime.d.ts} +18 -20
- package/out/runtime/useReleaseRuntime.d.ts.map +1 -0
- package/out/runtime/{useDemoRuntime.js → useReleaseRuntime.js} +4 -4
- package/out/runtime/useReleaseRuntime.js.map +1 -0
- package/package.json +222 -31
- package/styleframe.config.ts +329 -0
- package/styles.css +1995 -663
- package/tokens/index.css +116 -0
- package/tokens/tokens.json +528 -0
- package/tokens/tokens.resolver.json +185 -0
- package/lib/map/DemoControls.tsx +0 -194
- package/out/map/DemoControls.d.ts +0 -64
- package/out/map/DemoControls.d.ts.map +0 -1
- package/out/map/DemoControls.js +0 -30
- package/out/map/DemoControls.js.map +0 -1
- package/out/map/DemoMap.d.ts.map +0 -1
- package/out/map/DemoMap.js.map +0 -1
- package/out/map/GeocoderDemo.d.ts +0 -57
- package/out/map/GeocoderDemo.d.ts.map +0 -1
- package/out/map/GeocoderDemo.js +0 -98
- package/out/map/GeocoderDemo.js.map +0 -1
- package/out/map/useDemoGeocode.d.ts.map +0 -1
- package/out/map/useDemoGeocode.js.map +0 -1
- package/out/runtime/useDemoRuntime.d.ts.map +0 -1
- package/out/runtime/useDemoRuntime.js.map +0 -1
package/lib/map/ResultPanel.tsx
CHANGED
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
* presentational units (it does not re-implement any of them): `KindBadge`, `ComponentTable`,
|
|
8
8
|
* `ResolvedPlace`, `CandidatePicker`, plus the `CopyButton` + `buildParsePayload` copy affordance. This
|
|
9
9
|
* is the standalone, story-testable unit the demo's floating panel renders (the map analogue of the
|
|
10
|
-
* result block PipelineExplorer inlines), so `
|
|
10
|
+
* result block PipelineExplorer inlines), so `Geocoder` composes ONE panel instead of duplicating
|
|
11
11
|
* the marker/table/candidate glue. Heavy host visualizers (span highlight, tree, timing) slot in via
|
|
12
12
|
* `extras`; the no-resolve diagnostic via `failure`.
|
|
13
13
|
*
|
|
14
14
|
* NODE-SAFE: pure React + the shared units, no maplibre.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import type { ParseResult, ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
|
|
17
18
|
import type { ReactNode } from "react"
|
|
18
19
|
|
|
19
20
|
import { buildParsePayload } from "#pipeline/copy"
|
|
20
|
-
import type { ParseResult, ResolvedPlaceView } from "#pipeline/types"
|
|
21
21
|
|
|
22
22
|
import { CopyButton } from "../common/CopyButton.tsx"
|
|
23
23
|
import { KindBadge } from "../common/KindBadge.tsx"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* `<VersionPicker>` — the model-version `<select>` from the demo's control panel (`_app.tsx:1230-1254`),
|
|
7
7
|
* as a dumb presentational unit: the available bundles + current selection come in as props, the choice
|
|
8
|
-
* goes out through `onSelect`. No load state, no manifest fetch — the host (via `
|
|
8
|
+
* goes out through `onSelect`. No load state, no manifest fetch — the host (via `useReleaseRuntime`) owns
|
|
9
9
|
* that. Renders nothing when there is fewer than two versions to choose between.
|
|
10
10
|
*
|
|
11
11
|
* NODE-SAFE: pure React + a `<select>`, no maplibre. It rides the `@mailwoman/react/map` subpath only
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
import type { ReactNode } from "react"
|
|
16
16
|
|
|
17
|
-
import type {
|
|
17
|
+
import type { VersionOption } from "#map/types"
|
|
18
18
|
|
|
19
19
|
export interface VersionPickerProps {
|
|
20
20
|
/**
|
|
21
21
|
* The selectable model bundles.
|
|
22
22
|
*/
|
|
23
|
-
versions: ReadonlyArray<
|
|
23
|
+
versions: ReadonlyArray<VersionOption>
|
|
24
24
|
/**
|
|
25
25
|
* The currently-selected version tag.
|
|
26
26
|
*/
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* A fake geocoder runtime: canned parse + resolve, an offline stub map style, canned autocomplete, a version list.
|
|
7
|
+
* No network, no ONNX, no maplibre at run time — everything is data. It is what the stories and component tests
|
|
8
|
+
* mount, and what a host mounts to show the UI without the model.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ParseResult } from "@mailwoman/core/pipeline/client-result"
|
|
12
|
+
|
|
13
|
+
import type { ResolvedMapPlace } from "#map/place-render"
|
|
14
|
+
import type { GeocoderRuntime, Suggestion } from "#map/types"
|
|
15
|
+
import type { PipelineRuntime } from "#pipeline/types"
|
|
16
|
+
|
|
17
|
+
import type { MapCanvasStyle } from "./MapCanvas.tsx"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* An offline stub map style — one solid `background` layer, zero sources, zero network. Safe for headless Storybook.
|
|
21
|
+
*/
|
|
22
|
+
export const STUB_MAP_STYLE: MapCanvasStyle = {
|
|
23
|
+
version: 8,
|
|
24
|
+
name: "demo-runtime-stub",
|
|
25
|
+
sources: {},
|
|
26
|
+
layers: [{ id: "background", type: "background", paint: { "background-color": "#dfe7ee" } }],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Canned place-autocomplete suggestions — a synchronous fake for the FST prefix-walk.
|
|
31
|
+
*/
|
|
32
|
+
export const FAKE_SUGGESTIONS: Suggestion[] = [
|
|
33
|
+
{ value: "New York", placetype: "locality" },
|
|
34
|
+
{ value: "New Orleans", placetype: "locality" },
|
|
35
|
+
{ value: "Newark", placetype: "locality" },
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A fake DEMO runtime — the map analogue of {@link makePipelineRuntime}. It composes the pipeline fake (canned
|
|
40
|
+
* parse+resolve) with the map surface: the offline stub style, a version list, a backend, an injected autocomplete, and
|
|
41
|
+
* a `resolveMapPlace` that hands the selected candidate a bbox so the declarative overlays draw a marker + outline. No
|
|
42
|
+
* network, no ONNX, no maplibre-at-runtime — everything is data. `runParseWithBias` delegates to the base parse (the
|
|
43
|
+
* bias is ignored by the fake but present so the parameter is exercised).
|
|
44
|
+
*/
|
|
45
|
+
export function makeFakeGeocoderRuntime(overrides: Partial<GeocoderRuntime> = {}): GeocoderRuntime {
|
|
46
|
+
const base = makePipelineRuntime()
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
...base,
|
|
50
|
+
mapStyle: STUB_MAP_STYLE,
|
|
51
|
+
initialCenter: [-74.006, 40.7128],
|
|
52
|
+
initialZoom: 3,
|
|
53
|
+
overlays: [],
|
|
54
|
+
runParseWithBias: (input, _bias, hooks) => base.runParse(input, hooks),
|
|
55
|
+
autocomplete: async (query: string) =>
|
|
56
|
+
FAKE_SUGGESTIONS.filter((s) => s.value.toLowerCase().startsWith(query.toLowerCase())),
|
|
57
|
+
availableVersions: [
|
|
58
|
+
{ version: "v7.2.0", label: "v7.2.0 (latest)" },
|
|
59
|
+
{ version: "v7.1.0", label: "v7.1.0" },
|
|
60
|
+
{ version: "v6.4.0", label: "v6.4.0" },
|
|
61
|
+
],
|
|
62
|
+
selectedVersion: "v7.2.0",
|
|
63
|
+
selectVersion: () => {},
|
|
64
|
+
activeBackend: "webgpu (28 MB int8)",
|
|
65
|
+
forceWASM: false,
|
|
66
|
+
setForceWASM: () => {},
|
|
67
|
+
// Enrich the selected candidate with a bbox so an outline renders (case 4: bbox → approximate circle + fit).
|
|
68
|
+
resolveMapPlace: (candidate): ResolvedMapPlace => ({
|
|
69
|
+
...candidate,
|
|
70
|
+
bbox: {
|
|
71
|
+
minLat: candidate.lat - 0.15,
|
|
72
|
+
maxLat: candidate.lat + 0.15,
|
|
73
|
+
minLon: candidate.lon - 0.15,
|
|
74
|
+
maxLon: candidate.lon + 0.15,
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
...overrides,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A fixed, fully-populated parse+resolve result — the shared fixture behind the pipeline runtime + the result panel.
|
|
83
|
+
*/
|
|
84
|
+
export function makeFakeParseResult(input = "350 5th Ave, New York, NY 10118"): ParseResult {
|
|
85
|
+
return {
|
|
86
|
+
input,
|
|
87
|
+
tree: { roots: [{ tag: "locality", value: "New York" }] },
|
|
88
|
+
nodes: [
|
|
89
|
+
{ tag: "house_number", value: "350", confidence: 0.97, start: 0, end: 3 },
|
|
90
|
+
{ tag: "street", value: "5th Ave", confidence: 0.88, start: 4, end: 11 },
|
|
91
|
+
{ tag: "locality", value: "New York", confidence: 0.71 },
|
|
92
|
+
],
|
|
93
|
+
kindResult: { kind: "structured_address", confidence: 0.95, alternatives: [] },
|
|
94
|
+
timing: { shape: 0.4, classify: 12.1, resolve: 4.2 },
|
|
95
|
+
resolved: { id: 85_977_539, name: "New York", placetype: "locality", lat: 40.7128, lon: -74.006, score: 0.82 },
|
|
96
|
+
candidates: [
|
|
97
|
+
{ id: 85_977_539, name: "New York", placetype: "locality", lat: 40.7128, lon: -74.006, score: 0.82 },
|
|
98
|
+
{ id: 101_715_829, name: "New York", placetype: "region", lat: 43, lon: -75, score: 0.55 },
|
|
99
|
+
],
|
|
100
|
+
fstActive: true,
|
|
101
|
+
fstProvenance: { builtAt: "2026-07-01T00:00:00Z", stateCount: 51, placeCount: 94_000, importanceMatches: 12_000 },
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A fake parse+resolve runtime that returns a fixed, fully-populated result.
|
|
107
|
+
*/
|
|
108
|
+
export function makePipelineRuntime(overrides: Partial<PipelineRuntime> = {}): PipelineRuntime {
|
|
109
|
+
return {
|
|
110
|
+
ready: true,
|
|
111
|
+
parseStageLabels: ["Analyzing input shape…", "Running neural classifier…", "Resolving in gazetteer…"],
|
|
112
|
+
errorMessage: null,
|
|
113
|
+
loading: null,
|
|
114
|
+
runParse: async (input, { onStage }): Promise<ParseResult> => {
|
|
115
|
+
onStage(1)
|
|
116
|
+
onStage(2)
|
|
117
|
+
|
|
118
|
+
return makeFakeParseResult(input)
|
|
119
|
+
},
|
|
120
|
+
...overrides,
|
|
121
|
+
}
|
|
122
|
+
}
|
package/lib/map/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* `@mailwoman/react/map` — the geocoder
|
|
6
|
+
* `@mailwoman/react/map` — the geocoder map surface, kept behind its OWN subpath so `maplibre-gl`
|
|
7
7
|
* / `react-map-gl` (WebGL + DOM at import) never enter the package-root graph. Importing this subpath
|
|
8
8
|
* pulls the map deps; importing `@mailwoman/react` (root) does not. Consumers who only want the
|
|
9
9
|
* parse/POI explorers never pay for maplibre.
|
|
@@ -12,20 +12,39 @@
|
|
|
12
12
|
* `maplibre-gl/dist/maplibre-gl.css` + `@mailwoman/react/styles.css` itself.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
export {
|
|
16
|
-
export type {
|
|
15
|
+
export { MapCanvas } from "./MapCanvas.tsx"
|
|
16
|
+
export type { MapCanvasExtraProps, MapCanvasProps, MapCanvasStyle } from "./MapCanvas.tsx"
|
|
17
|
+
|
|
18
|
+
// ── Map chrome (node-safe presentation; the host supplies the input and reads its own map) ──
|
|
19
|
+
export { MapChipRow } from "./MapChipRow.tsx"
|
|
20
|
+
export type { MapChip, MapChipRowProps } from "./MapChipRow.tsx"
|
|
21
|
+
export { MapCompass } from "./MapCompass.tsx"
|
|
22
|
+
export type { MapCompassProps } from "./MapCompass.tsx"
|
|
23
|
+
export { useMapBearing } from "./useMapBearing.ts"
|
|
24
|
+
export type { UseMapBearing } from "./useMapBearing.ts"
|
|
25
|
+
export { useMapLabelPick } from "./useMapLabelPick.ts"
|
|
26
|
+
export { MapControlButton, MapControlGroup, MapControlStack } from "./MapControlStack.tsx"
|
|
27
|
+
export type { MapControlButtonProps, MapControlGroupProps, MapControlStackProps } from "./MapControlStack.tsx"
|
|
28
|
+
export { MapFooter } from "./MapFooter.tsx"
|
|
29
|
+
export type { MapFooterProps } from "./MapFooter.tsx"
|
|
30
|
+
export { MapProgressBar } from "./MapProgressBar.tsx"
|
|
31
|
+
export type { MapProgressBarProps } from "./MapProgressBar.tsx"
|
|
32
|
+
export { MapSheet } from "./MapSheet.tsx"
|
|
33
|
+
export type { MapSheetProps } from "./MapSheet.tsx"
|
|
34
|
+
export { MapSearchBar } from "./MapSearchBar.tsx"
|
|
35
|
+
export type { MapSearchBarProps } from "./MapSearchBar.tsx"
|
|
17
36
|
|
|
18
37
|
export type {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
DemoRuntime,
|
|
24
|
-
DemoVersionOption,
|
|
38
|
+
CompareContext,
|
|
39
|
+
GeocoderPanels,
|
|
40
|
+
GeocoderRuntime,
|
|
41
|
+
InferenceBackend,
|
|
25
42
|
LngLatTuple,
|
|
26
43
|
MapBias,
|
|
27
44
|
OverlaySpec,
|
|
45
|
+
ResultContext,
|
|
28
46
|
Suggestion,
|
|
47
|
+
VersionOption,
|
|
29
48
|
} from "#map/types"
|
|
30
49
|
|
|
31
50
|
// ── Pure geometry + render spec (node-safe; no react-map-gl at runtime) ──────
|
|
@@ -47,23 +66,23 @@ export type { ResultCameraProps } from "./ResultCamera.tsx"
|
|
|
47
66
|
export { ResultOverlay } from "./ResultOverlay.tsx"
|
|
48
67
|
export type { ResultOverlayProps } from "./ResultOverlay.tsx"
|
|
49
68
|
|
|
50
|
-
// ──
|
|
69
|
+
// ── Geocoder controls + the composed geocoder ────────────────────────────────
|
|
51
70
|
export { BackendControl } from "./BackendControl.tsx"
|
|
52
71
|
export type { BackendControlProps } from "./BackendControl.tsx"
|
|
53
72
|
export { CompareToggle } from "./CompareToggle.tsx"
|
|
54
73
|
export type { CompareToggleProps } from "./CompareToggle.tsx"
|
|
55
|
-
export {
|
|
56
|
-
export type {
|
|
57
|
-
export {
|
|
58
|
-
export type {
|
|
74
|
+
export { Geocoder } from "./Geocoder.tsx"
|
|
75
|
+
export type { GeocoderProps } from "./Geocoder.tsx"
|
|
76
|
+
export { GeocoderControls } from "./GeocoderControls.tsx"
|
|
77
|
+
export type { GeocoderControlsProps } from "./GeocoderControls.tsx"
|
|
59
78
|
export { PlaceAutocomplete } from "./PlaceAutocomplete.tsx"
|
|
60
79
|
export type { PlaceAutocompleteProps } from "./PlaceAutocomplete.tsx"
|
|
61
80
|
export { ResultPanel } from "./ResultPanel.tsx"
|
|
62
81
|
export type { ResultPanelProps } from "./ResultPanel.tsx"
|
|
63
82
|
export { useCompareState } from "#map/useCompareState"
|
|
64
83
|
export type { UseCompareState } from "#map/useCompareState"
|
|
65
|
-
export {
|
|
66
|
-
export type {
|
|
84
|
+
export { useGeocode } from "#map/useGeocode"
|
|
85
|
+
export type { UseGeocode, UseGeocodeOptions } from "#map/useGeocode"
|
|
67
86
|
export { usePlaceAutocomplete } from "#map/usePlaceAutocomplete"
|
|
68
87
|
|
|
69
88
|
export type {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file The bare MapLibre control both debug panels portal into.
|
|
6
|
+
*
|
|
7
|
+
* `DashboardMap`'s inspector and the demo page's `_debug.tsx` panel each need an `IControl` whose
|
|
8
|
+
* only job is to own a container element for `createPortal`. The class was duplicated in both;
|
|
9
|
+
* the position and container class are the only differences, so they are parameters here.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ControlPosition } from "maplibre-gl"
|
|
13
|
+
import type { IControl, MapInstance } from "react-map-gl/maplibre"
|
|
14
|
+
|
|
15
|
+
export interface DebugControlBaseOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Class applied to the portal container.
|
|
18
|
+
*/
|
|
19
|
+
className?: string
|
|
20
|
+
/**
|
|
21
|
+
* Where MapLibre docks the control. @default "bottom-left"
|
|
22
|
+
*/
|
|
23
|
+
position?: ControlPosition
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class DebugControlBase implements IControl {
|
|
27
|
+
public readonly container: HTMLElement
|
|
28
|
+
readonly #position: ControlPosition
|
|
29
|
+
|
|
30
|
+
constructor({ className, position = "bottom-left" }: DebugControlBaseOptions = {}) {
|
|
31
|
+
this.container = document.createElement("div")
|
|
32
|
+
|
|
33
|
+
if (className) {
|
|
34
|
+
this.container.classList.add(className)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.#position = position
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public onAdd(_map: MapInstance): HTMLElement {
|
|
41
|
+
return this.container
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public onRemove(_map: MapInstance): void {
|
|
45
|
+
this.container.remove()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public getDefaultPosition(): ControlPosition {
|
|
49
|
+
return this.#position
|
|
50
|
+
}
|
|
51
|
+
}
|
package/lib/map/place-render.ts
CHANGED
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
* 5. bare point → no outline, fly to zoom 12
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import type { ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
|
|
23
|
+
|
|
22
24
|
import { approxCircleGeometry, bboxToBounds, geomBounds, radiusCircleGeometry } from "#map/geometry"
|
|
23
25
|
import type { BoundsTuple, PlaceBBox, PlaceGeometry } from "#map/geometry"
|
|
24
|
-
import type { ResolvedPlaceView } from "#pipeline/types"
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* `[longitude, latitude]`.
|
|
@@ -167,7 +168,7 @@ export function computeMapPlaceRenderSpec(place: ResolvedMapPlace): MapPlaceRend
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
170
|
-
* The DECLARATIVE camera path: reshape a `center` target into a `viewState` patch a controlled `<
|
|
171
|
+
* The DECLARATIVE camera path: reshape a `center` target into a `viewState` patch a controlled `<MapCanvas viewState>`
|
|
171
172
|
* can apply directly (a hard jump, no animation). Returns `null` for a `bounds` target — fitting a box to the viewport
|
|
172
173
|
* needs the map's pixel dimensions, which only the live map has, so that case is applied imperatively by
|
|
173
174
|
* `<ResultCamera>`. Pure + node-testable.
|
package/lib/map/types.ts
CHANGED
|
@@ -3,25 +3,26 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* Types for the geocoder
|
|
7
|
-
* machine + the declarative map, while the host injects a {@link
|
|
8
|
-
* / R2 and the composed map style. {@link
|
|
6
|
+
* Types for the geocoder map surface. Mirrors the pipeline boundary: the package owns the UI state
|
|
7
|
+
* machine + the declarative map, while the host injects a {@link GeocoderRuntime} that owns ONNX / httpvfs
|
|
8
|
+
* / R2 and the composed map style. {@link GeocoderRuntime} EXTENDS {@link PipelineRuntime} so the shared
|
|
9
9
|
* `runParse` / `parseStageLabels` / `loading` contract is reused, and adds the map-specific surface
|
|
10
10
|
* (style, overlays, initial center, viewport bias, backend/version selection). Phase 4 adds the
|
|
11
|
-
* `resolveMapPlace` enricher, the {@link
|
|
11
|
+
* `resolveMapPlace` enricher, the {@link GeocoderPanels} injection bag, and the {@link CompareContext}.
|
|
12
12
|
*
|
|
13
13
|
* The map-spec types are imported type-only from `react-map-gl/maplibre`; nothing here loads maplibre at
|
|
14
|
-
* runtime, so this module stays node-safe (its concrete-value CONSUMERS — `
|
|
14
|
+
* runtime, so this module stays node-safe (its concrete-value CONSUMERS — `MapCanvas`, `Geocoder` —
|
|
15
15
|
* are the ones behind the `@mailwoman/react/map` subpath).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import type { ParseResult, ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
|
|
18
19
|
import type { ReactNode } from "react"
|
|
19
|
-
import type { LayerSpecification, SourceSpecification } from "react-map-gl/maplibre"
|
|
20
|
+
import type { LayerSpecification, MapInstance, SourceSpecification } from "react-map-gl/maplibre"
|
|
20
21
|
|
|
21
22
|
import type { LngLat, ResolvedMapPlace } from "#map/place-render"
|
|
22
|
-
import type {
|
|
23
|
+
import type { PipelineRuntime } from "#pipeline/types"
|
|
23
24
|
|
|
24
|
-
import type {
|
|
25
|
+
import type { MapCanvasStyle } from "./MapCanvas.tsx"
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* `[longitude, latitude]`.
|
|
@@ -92,7 +93,7 @@ export interface Suggestion {
|
|
|
92
93
|
/**
|
|
93
94
|
* A selectable model bundle (version tag + a display label the picker shows).
|
|
94
95
|
*/
|
|
95
|
-
export interface
|
|
96
|
+
export interface VersionOption {
|
|
96
97
|
/**
|
|
97
98
|
* The version tag (e.g. a git tag or model-card version).
|
|
98
99
|
*/
|
|
@@ -104,23 +105,23 @@ export interface DemoVersionOption {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
/**
|
|
107
|
-
* Which neural backend the
|
|
108
|
+
* Which neural backend the geocoder is currently running on.
|
|
108
109
|
*/
|
|
109
|
-
export type
|
|
110
|
+
export type InferenceBackend = "webgpu" | "wasm"
|
|
110
111
|
|
|
111
112
|
/**
|
|
112
|
-
* The injected
|
|
113
|
-
* `ready`) with the map + version/backend surface the
|
|
113
|
+
* The injected geocoder runtime. Extends {@link PipelineRuntime} (shared `runParse` / `parseStageLabels` / `loading` /
|
|
114
|
+
* `ready`) with the map + version/backend surface the geocoder needs. The host composes `mapStyle` (via cartographer's
|
|
114
115
|
* `StyleSpecificationComposer` + the tile-worker TileJSON), supplies the overlay specs, the initial center (from
|
|
115
116
|
* geolocation), the FST autocomplete, and the calibrator — nothing in the package imports `@mailwoman/cartographer`,
|
|
116
|
-
* `@mailwoman/neural
|
|
117
|
+
* `@mailwoman/neural`'s web loader, httpvfs, or Docusaurus.
|
|
117
118
|
*/
|
|
118
|
-
export interface
|
|
119
|
+
export interface GeocoderRuntime extends PipelineRuntime {
|
|
119
120
|
// ── Map ────────────────────────────────────────────────────────────────
|
|
120
121
|
/**
|
|
121
122
|
* The composed basemap style (URL or `StyleSpecification`).
|
|
122
123
|
*/
|
|
123
|
-
mapStyle:
|
|
124
|
+
mapStyle: MapCanvasStyle
|
|
124
125
|
/**
|
|
125
126
|
* Host-supplied overlays (coverage, race-dots, …).
|
|
126
127
|
*/
|
|
@@ -136,7 +137,7 @@ export interface DemoRuntime extends PipelineRuntime {
|
|
|
136
137
|
|
|
137
138
|
// ── Parse extras layered over PipelineRuntime.runParse ──────────────────
|
|
138
139
|
/**
|
|
139
|
-
* A bias-aware parse. The
|
|
140
|
+
* A bias-aware parse. The geocoder feeds the current viewport center as a soft prior; when absent the host falls back
|
|
140
141
|
* to the base {@link PipelineRuntime.runParse}. Kept separate so the shared `runParse` contract is unchanged.
|
|
141
142
|
*/
|
|
142
143
|
runParseWithBias?: (
|
|
@@ -154,9 +155,9 @@ export interface DemoRuntime extends PipelineRuntime {
|
|
|
154
155
|
calibrator?: (raw: number) => number | null
|
|
155
156
|
/**
|
|
156
157
|
* Enrich the selected candidate into the richer {@link ResolvedMapPlace} the declarative map render consumes (bbox,
|
|
157
|
-
* street tier + uncertainty, a pre-fetched crisp polygon) — the fields that live on the
|
|
158
|
+
* street tier + uncertainty, a pre-fetched crisp polygon) — the fields that live on the host's `ResolvedHit` but not
|
|
158
159
|
* on the shared {@link ResolvedPlaceView}. The host owns this because those extras (and the async polygon fetch in the
|
|
159
|
-
* real
|
|
160
|
+
* real runtime) are host/gazetteer concerns; the package keeps {@link ParseResult} unpolluted. Absent → the candidate
|
|
160
161
|
* renders as a bare point (marker + a mid-zoom fly-to). Returning `null` also renders nothing.
|
|
161
162
|
*/
|
|
162
163
|
resolveMapPlace?: (candidate: ResolvedPlaceView, result: ParseResult) => ResolvedMapPlace | null
|
|
@@ -165,7 +166,7 @@ export interface DemoRuntime extends PipelineRuntime {
|
|
|
165
166
|
/**
|
|
166
167
|
* The selectable model bundles the version picker offers.
|
|
167
168
|
*/
|
|
168
|
-
availableVersions?:
|
|
169
|
+
availableVersions?: VersionOption[]
|
|
169
170
|
/**
|
|
170
171
|
* The currently-selected model version.
|
|
171
172
|
*/
|
|
@@ -189,9 +190,10 @@ export interface DemoRuntime extends PipelineRuntime {
|
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
/**
|
|
192
|
-
* The compare-mode state a {@link
|
|
193
|
+
* The compare-mode state a {@link GeocoderPanels.compare} render-prop receives (the second parse itself stays
|
|
194
|
+
* host-side).
|
|
193
195
|
*/
|
|
194
|
-
export interface
|
|
196
|
+
export interface CompareContext {
|
|
195
197
|
/**
|
|
196
198
|
* The current primary parse result, or `null` before the first submit.
|
|
197
199
|
*/
|
|
@@ -207,12 +209,12 @@ export interface DemoCompareContext {
|
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
/**
|
|
210
|
-
* The state a {@link
|
|
212
|
+
* The state a {@link GeocoderPanels.result} render-prop receives, so a host can render its OWN result block (a
|
|
211
213
|
* `<ResultPanel>` with its span-highlight / timing / hierarchy / precision detail) in place of the package's default
|
|
212
214
|
* {@link ResultPanel}. Everything the default panel needs is passed through; the candidate-selection state stays owned
|
|
213
|
-
* by the package (`
|
|
215
|
+
* by the package (`useGeocode`).
|
|
214
216
|
*/
|
|
215
|
-
export interface
|
|
217
|
+
export interface ResultContext {
|
|
216
218
|
/**
|
|
217
219
|
* The current parse+resolve result.
|
|
218
220
|
*/
|
|
@@ -232,14 +234,14 @@ export interface DemoResultContext {
|
|
|
232
234
|
}
|
|
233
235
|
|
|
234
236
|
/**
|
|
235
|
-
* Host-injected panels for {@link
|
|
236
|
-
* `ReactNode` (or a thunk of the parse result / compare state) so the package needs neither the heavy
|
|
237
|
-
* (ModelVisualizer, VersionCompare,
|
|
238
|
-
* fake-runtime Storybook stories pass none and still render the whole
|
|
237
|
+
* Host-injected panels for {@link Geocoder}, the map analogue of `PipelinePanels`. Each is an already-rendered
|
|
238
|
+
* `ReactNode` (or a thunk of the parse result / compare state) so the package needs neither the heavy host visualizers
|
|
239
|
+
* (ModelVisualizer, VersionCompare, About, PermalinkButton) nor their data types. Every field is optional — the
|
|
240
|
+
* fake-runtime Storybook stories pass none and still render the whole geocoder.
|
|
239
241
|
*/
|
|
240
|
-
export interface
|
|
242
|
+
export interface GeocoderPanels {
|
|
241
243
|
/**
|
|
242
|
-
* Rendered at the top of the control panel (e.g. the
|
|
244
|
+
* Rendered at the top of the control panel (e.g. the host's "About this geocoder").
|
|
243
245
|
*/
|
|
244
246
|
header?: ReactNode
|
|
245
247
|
/**
|
|
@@ -251,9 +253,9 @@ export interface DemoPanels {
|
|
|
251
253
|
*/
|
|
252
254
|
footer?: ReactNode
|
|
253
255
|
/**
|
|
254
|
-
* A device-location / proximity-bias control, rendered between the query form and the autocomplete list (the
|
|
255
|
-
*
|
|
256
|
-
* {@link
|
|
256
|
+
* A device-location / proximity-bias control, rendered between the query form and the autocomplete list (the "📍 Use
|
|
257
|
+
* my location" row). Host-owned so the geolocation permission + the bias it feeds into the host's
|
|
258
|
+
* {@link GeocoderRuntime.runParseWithBias} stay a host concern.
|
|
257
259
|
*/
|
|
258
260
|
bias?: ReactNode
|
|
259
261
|
/**
|
|
@@ -261,17 +263,23 @@ export interface DemoPanels {
|
|
|
261
263
|
*/
|
|
262
264
|
extras?: (result: ParseResult) => ReactNode
|
|
263
265
|
/**
|
|
264
|
-
* Rendered just above the result block (present or empty)
|
|
265
|
-
*
|
|
266
|
-
*
|
|
266
|
+
* Rendered just above the result block (present or empty), for content that reads on THIS answer.
|
|
267
|
+
*
|
|
268
|
+
* A control that reads on the model rather than on an address belongs in {@link developerExtras}: above the result it
|
|
269
|
+
* is the first thing a visitor meets, and on a phone it pushes the answer below the fold.
|
|
267
270
|
*/
|
|
268
271
|
aboveResult?: (context: { result: ParseResult | null }) => ReactNode
|
|
272
|
+
/**
|
|
273
|
+
* Host controls appended to the Developer sheet — the opt-in display toggles, whose state and the renderers they
|
|
274
|
+
* drive both belong to the host.
|
|
275
|
+
*/
|
|
276
|
+
developerExtras?: ReactNode
|
|
269
277
|
/**
|
|
270
278
|
* Replace the package's default {@link ResultPanel} entirely. When provided, the host renders its own result block
|
|
271
|
-
* (
|
|
272
|
-
*
|
|
279
|
+
* (span highlight, timing, hierarchy, precision detail, calibrated confidences) from the {@link ResultContext}. Absent
|
|
280
|
+
* → the built-in panel renders.
|
|
273
281
|
*/
|
|
274
|
-
result?: (context:
|
|
282
|
+
result?: (context: ResultContext) => ReactNode
|
|
275
283
|
/**
|
|
276
284
|
* Rendered in place of the resolved-place panel when nothing resolved (host's FailureDiagnostic). Ignored when
|
|
277
285
|
* {@link result} is set.
|
|
@@ -280,7 +288,7 @@ export interface DemoPanels {
|
|
|
280
288
|
/**
|
|
281
289
|
* The version-compare view — the host renders its own diff from the compare state it owns.
|
|
282
290
|
*/
|
|
283
|
-
compare?: (context:
|
|
291
|
+
compare?: (context: CompareContext) => ReactNode
|
|
284
292
|
/**
|
|
285
293
|
* The model-visualizer / debug drawer, mounted beside the map (host's ModelVisualizer). A render-prop so the host can
|
|
286
294
|
* trace the CURRENT result (its input) — the package passes the live parse result; the host decides on its own
|
|
@@ -288,9 +296,17 @@ export interface DemoPanels {
|
|
|
288
296
|
*/
|
|
289
297
|
debugDrawer?: (context: { result: ParseResult | null }) => ReactNode
|
|
290
298
|
/**
|
|
291
|
-
* Extra map controls mounted as `<
|
|
299
|
+
* Extra map controls mounted as `<MapCanvas>` children (host's DebugControl / LayerToggle via `useControl`).
|
|
292
300
|
*/
|
|
293
301
|
mapControls?: ReactNode
|
|
302
|
+
/**
|
|
303
|
+
* A layer control, rendered in the chrome's top column under the example chips rather than in a MapLibre corner.
|
|
304
|
+
*
|
|
305
|
+
* A render-prop taking the live map handle, because a layer control reads `getStyle().layers` and writes
|
|
306
|
+
* `setLayoutProperty` — it cannot be a static node. The handle is `null` until the map instantiates, so the host
|
|
307
|
+
* returns `null` for that first frame rather than rendering an empty control.
|
|
308
|
+
*/
|
|
309
|
+
layers?: (context: { map: MapInstance | null }) => ReactNode
|
|
294
310
|
/**
|
|
295
311
|
* A permalink control for the current address (host's PermalinkButton).
|
|
296
312
|
*/
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* `
|
|
6
|
+
* `useGeocode` — the parse+resolve state machine for the geocoder map. It REUSES the pipeline's
|
|
7
7
|
* `useParsePipeline` (text / busy / stage / result / candidate selection) and layers on the two map-only
|
|
8
|
-
* concerns the
|
|
8
|
+
* concerns the geocoder adds over the base explorer:
|
|
9
9
|
*
|
|
10
10
|
* 1. Viewport bias — when the runtime exposes `runParseWithBias`, the current map center (read through
|
|
11
11
|
* an injected `getBias`, itself reading the `MapRef`) rides along as a soft proximity prior. The
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
import { useMemo } from "react"
|
|
22
22
|
|
|
23
23
|
import type { ResolvedMapPlace } from "#map/place-render"
|
|
24
|
-
import type {
|
|
24
|
+
import type { GeocoderRuntime, MapBias } from "#map/types"
|
|
25
25
|
import type { PipelineRuntime } from "#pipeline/types"
|
|
26
26
|
import { useParsePipeline, type UseParsePipeline } from "#pipeline/useParsePipeline"
|
|
27
27
|
|
|
28
|
-
export interface
|
|
28
|
+
export interface UseGeocodeOptions {
|
|
29
29
|
/**
|
|
30
|
-
* The injected
|
|
30
|
+
* The injected geocoder runtime (extends `PipelineRuntime` with the map + bias surface).
|
|
31
31
|
*/
|
|
32
|
-
runtime:
|
|
32
|
+
runtime: GeocoderRuntime
|
|
33
33
|
/**
|
|
34
34
|
* Address to pre-fill.
|
|
35
35
|
*/
|
|
@@ -40,14 +40,14 @@ export interface UseDemoGeocodeOptions {
|
|
|
40
40
|
getBias?: () => MapBias | null
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export interface
|
|
43
|
+
export interface UseGeocode extends UseParsePipeline {
|
|
44
44
|
/**
|
|
45
45
|
* The selected candidate enriched into the map-render shape (bbox / tier / polygon), or `null`.
|
|
46
46
|
*/
|
|
47
47
|
mapPlace: ResolvedMapPlace | null
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export function
|
|
50
|
+
export function useGeocode({ runtime, defaultText, getBias }: UseGeocodeOptions): UseGeocode {
|
|
51
51
|
// Bind the viewport bias into a derived `runParse` so `useParsePipeline` is reused unchanged. When the runtime has no
|
|
52
52
|
// bias-aware parse, pass it straight through.
|
|
53
53
|
const geoRuntime = useMemo<PipelineRuntime>(() => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `useMapBearing` — the map's rotation as React state, for a compass that appears off north and fades back.
|
|
7
|
+
*
|
|
8
|
+
* The map is an external store, so it is read through `useSyncExternalStore` rather than mirrored into state by an
|
|
9
|
+
* effect: the bearing is a number, which makes a stable snapshot, and React tears nothing during a concurrent
|
|
10
|
+
* render.
|
|
11
|
+
*
|
|
12
|
+
* It listens on `rotate` rather than `rotateend`, because the needle has to track the gesture rather than snap once
|
|
13
|
+
* the gesture is over. `move` is subscribed too: a `flyTo` or an `easeTo` carrying a bearing rotates the map without
|
|
14
|
+
* firing a rotate event, and a compass that misses those stays pointing north over a turned map.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useCallback, useSyncExternalStore } from "react"
|
|
18
|
+
import type { MapInstance } from "react-map-gl/maplibre"
|
|
19
|
+
|
|
20
|
+
export interface UseMapBearing {
|
|
21
|
+
/**
|
|
22
|
+
* Degrees off north, as MapLibre reports it. Zero while there is no map.
|
|
23
|
+
*/
|
|
24
|
+
bearing: number
|
|
25
|
+
/**
|
|
26
|
+
* Rotate the map back to north. A no-op while there is no map.
|
|
27
|
+
*/
|
|
28
|
+
resetNorth: () => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useMapBearing(map: MapInstance | null): UseMapBearing {
|
|
32
|
+
const subscribe = useCallback(
|
|
33
|
+
(onChange: () => void) => {
|
|
34
|
+
if (!map) return () => undefined
|
|
35
|
+
|
|
36
|
+
map.on("rotate", onChange)
|
|
37
|
+
map.on("move", onChange)
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
map.off("rotate", onChange)
|
|
41
|
+
map.off("move", onChange)
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
[map]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
// The server snapshot is the same reading: there is no map during a server render, and north is what a compass
|
|
48
|
+
// shows when it has nothing to report.
|
|
49
|
+
const readBearing = useCallback(() => map?.getBearing() ?? 0, [map])
|
|
50
|
+
|
|
51
|
+
const bearing = useSyncExternalStore(subscribe, readBearing, () => 0)
|
|
52
|
+
|
|
53
|
+
const resetNorth = useCallback(() => map?.easeTo({ bearing: 0, pitch: 0 }), [map])
|
|
54
|
+
|
|
55
|
+
return { bearing, resetNorth }
|
|
56
|
+
}
|