{
  "name": "map-canvas",
  "type": "registry:ui",
  "description": "Deterministic MapLibre map mount",
  "dependencies": [
    "remotion",
    "maplibre-gl"
  ],
  "registryDependencies": [
    "map-utils"
  ],
  "files": [
    {
      "path": "registry/bases/default/primitives/map-canvas.tsx",
      "type": "registry:ui",
      "content": "import { useEffect, useRef, useState } from \"react\";\nimport {\n  AbsoluteFill,\n  useDelayRender,\n  useVideoConfig,\n} from \"remotion\";\nimport type { Map } from \"maplibre-gl\";\nimport \"maplibre-gl/dist/maplibre-gl.css\";\nimport {\n  createMapInstance,\n  isMapStyleReady,\n  mapVignetteStyle,\n  MAP_THEME,\n  type LngLat,\n} from \"@/remotion/lib/map-utils\";\n\nexport type MapCanvasProps = {\n  center: LngLat;\n  zoom?: number;\n  style?: string;\n  onMapReady?: (map: Map) => void;\n  backgroundColor?: string;\n  showVignette?: boolean;\n};\n\nexport const MapCanvas: React.FC<MapCanvasProps> = ({\n  center,\n  zoom = 7,\n  style,\n  onMapReady,\n  backgroundColor = MAP_THEME.background,\n  showVignette = true,\n}) => {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const mapRef = useRef<Map | null>(null);\n  const onMapReadyRef = useRef(onMapReady);\n  const { delayRender, continueRender } = useDelayRender();\n  const { width, height } = useVideoConfig();\n  const [loadingHandle] = useState(() => delayRender(\"Loading map\"));\n\n  onMapReadyRef.current = onMapReady;\n\n  useEffect(() => {\n    if (!containerRef.current) {\n      return;\n    }\n\n    const mapInstance = createMapInstance(containerRef.current, width, height, {\n      center,\n      zoom,\n      style,\n    });\n    mapRef.current = mapInstance;\n\n    const notifyReady = () => {\n      if (!isMapStyleReady(mapInstance)) {\n        return;\n      }\n\n      mapInstance.jumpTo({ center, zoom });\n      mapInstance.once(\"idle\", () => {\n        onMapReadyRef.current?.(mapInstance);\n        continueRender(loadingHandle);\n      });\n    };\n\n    if (mapInstance.loaded()) {\n      notifyReady();\n    } else {\n      mapInstance.on(\"load\", notifyReady);\n    }\n  }, [continueRender, height, loadingHandle, style, width, zoom]);\n\n  useEffect(() => {\n    const mapInstance = mapRef.current;\n    if (!isMapStyleReady(mapInstance) || !mapInstance.loaded()) {\n      return;\n    }\n\n    mapInstance.jumpTo({ center, zoom });\n    mapInstance.triggerRepaint();\n  }, [center, zoom]);\n\n  return (\n    <AbsoluteFill style={{ backgroundColor }}>\n      <div\n        ref={containerRef}\n        style={{\n          width,\n          height,\n          position: \"absolute\",\n        }}\n      />\n      {showVignette ? <div style={mapVignetteStyle} /> : null}\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "spatial",
    "drive": "spatial",
    "tier": "advanced"
  }
}