The gap
The native primitive set — View, Text, Button, Link, Pressable, TextInput, Picker, Modal, Switch, Slider, ActivityIndicator, Image, ScrollView, FlatList, Icon, Svg — has no map view.
craft-native/craft#338 set this aside deliberately, and for HQ.training that is the right call: "We do not need an interactive map view — no tiles, no panning, no gestures." Wildloop is the other case. It is a trail app, the map is not an illustration of the screen, it is the screen, and no amount of server-drawn SVG substitutes for one you can pan.
What it blocks
8 of wildloop's 39 views mount a real map through createTrailMap (resources/composables/useTrailMap.ts):
| View | route line | marker | territory polygon | fit to bounds | camera-moved event |
|---|---|---|---|---|---|
trails (catalog) | ✓ | ✓ | ✓ | ✓ | |
trail/[id] | ✓ | ✓ | |||
activity/[id] | ✓ | ✓ | |||
effort/[id] | ✓ | ✓ | ✓ | ||
event/[id] | ✓ | ✓ | |||
territory/[id] | ✓ | ||||
routes (planner) | ✓ | ||||
plans |
These are not peripheral screens. /trails is the catalog's main surface, territory/[id] is the territory game the product exists for, and activity/[id] is what somebody opens the moment a run ends. On the catalog the camera-moved event is load-bearing rather than cosmetic: panning re-queries the catalog for what is now in view.
What would be enough
A minimum that unblocks all eight:
- Vector tiles from a style. We already serve our own —
https://tiles.wildloop.org/tiles.json, MVT, with the style built client-side inbuildStyle(). So this is a renderer question, not a data or licensing one. - A polyline overlay with per-feature colour and width. Difficulty colours and a casing stroke under the line are what
drawTrailRoutedraws today. - Fit to bounds, with padding.
fitPointsis used by six of the eight. - Pan and zoom, and an event when the camera settles. That is all
watchUserMovesneeds.
Markers and polygons can come later if the host exposes a projection (lat/lng → screen point), because then a marker is an ordinary View positioned over the map. Tiles and the polyline are the two that cannot be worked around.
What this is not asking for
No 3D terrain, no offline tile packs, no clustering, no turn-by-turn. And explicitly not a web view: wildloop ships a WKWebView app today and the point of the exercise is to leave it.
One scope note
If the answer is MapKit on iOS and the Maps SDK on Android, that is two renderers to hold at parity — craft-native/craft#334's problem again, for the largest primitive in the set. MapLibre Native is worth weighing first: one engine on both platforms, and it already speaks the MVT and style JSON we serve.
Reproducing the need
# wildloop, on main
grep -rln createTrailMap resources/views/ # the eight screens aboveFiled from the wildloop screen inventory for stacksjs/stx#1992: of 39 views, 23 are expressible in today's primitives, 10 are blocked by craft-native/craft#338 or craft-native/craft#339, and these 8 are blocked by the absence of a map. It is the largest single gap between wildloop and a native app, and the only one with no issue behind it.