Map
fancy-mapControlled map surface — view/markers/selection, live tracking (follow + useGeolocationTrack), stable data-map-marker-id handles, SSR-safe. Swap providers (leafletProvider / googleProvider) with one prop.
Why
Maps are where "put a pin on it" meets a pile of engine-specific ceremony: Leaflet and Google have different APIs, different coordinate orders, different marker plumbing, and both fight React's render cycle — so most apps hard-wire one engine, hand-roll a controlled wrapper, and then have no clean way to move markers live or to let an agent touch the map without scraping the DOM.
Map collapses all of that into one controlled surface: swap OpenStreetMap for Google with a one-line provider change, drive live tracking off plain state, and cohabit the same map with an agent over an MCP bridge.What
An engine-agnostic
<Map> whose view + markers + selectedId are controlled, JSON-friendly props (an agent can emit a marker array directly). Providers are swappable — leafletProvider() (OpenStreetMap or any XYZ raster source, no key) and googleProvider({ apiKey }) — with no map-engine dependency in the core. follow keeps the camera on a moving marker; useGeolocationTrack() wires the browser's live position; every pin carries a stable data-map-marker-id handle. SSR-safe — a sized placeholder renders on the server and the engine mounts in an effect. registerMapBridge makes it inhabitable: an agent pans, drops/updates/removes markers, selects, fits bounds, and follows a track, with every mutation broadcasting AgentActivity and pushing an undo entry.How
Install
@particle-academy/fancy-map plus the engine you want (leaflet or @googlemaps/js-api-loader). Give the map a container with a real height and, for Leaflet, import its stylesheet once: import "leaflet/dist/leaflet.css". Then <Map provider={leafletProvider()} view={view} onViewChange={setView} markers={markers} selectedId={id} onSelect={setId} />. Move to Google by swapping only the provider. For live tracking, feed markers new positions (a websocket, an Echo channel, or useGeolocationTrack) and set follow to the moving marker's id. For a cohabited human/agent map, wire registerMapBridge(host, { adapter: { getView, setView, getMarkers, setMarkers, getSelected, setSelected, fitBounds, setFollow } }) from @particle-academy/agent-integrations/bridges/map against the same state — see the demo at /react-demos/map-agent.