{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "globe",
  "type": "registry:ui",
  "title": "Globe",
  "description": "An autorotating, interactive, and highly performant globe made using WebGL.",
  "dependencies": [
    "cobe@^0.6.4",
    "motion"
  ],
  "files": [
    {
      "path": "registry/magicui/globe.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef } from \"react\"\nimport createGlobe, { type COBEOptions } from \"cobe\"\nimport { useMotionValue, useSpring } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst MOVEMENT_DAMPING = 1400\n\nconst GLOBE_CONFIG: COBEOptions = {\n  width: 800,\n  height: 800,\n  onRender: () => {},\n  devicePixelRatio: 2,\n  phi: 0,\n  theta: 0.3,\n  dark: 0,\n  diffuse: 0.4,\n  mapSamples: 16000,\n  mapBrightness: 1.2,\n  baseColor: [1, 1, 1],\n  markerColor: [251 / 255, 100 / 255, 21 / 255],\n  glowColor: [1, 1, 1],\n  markers: [\n    { location: [14.5995, 120.9842], size: 0.03 },\n    { location: [19.076, 72.8777], size: 0.1 },\n    { location: [23.8103, 90.4125], size: 0.05 },\n    { location: [30.0444, 31.2357], size: 0.07 },\n    { location: [39.9042, 116.4074], size: 0.08 },\n    { location: [-23.5505, -46.6333], size: 0.1 },\n    { location: [19.4326, -99.1332], size: 0.1 },\n    { location: [40.7128, -74.006], size: 0.1 },\n    { location: [34.6937, 135.5022], size: 0.05 },\n    { location: [41.0082, 28.9784], size: 0.06 },\n  ],\n}\n\nexport function Globe({\n  className,\n  config = GLOBE_CONFIG,\n}: {\n  className?: string\n  config?: COBEOptions\n}) {\n  const canvasRef = useRef<HTMLCanvasElement>(null)\n  const phiRef = useRef(0)\n  const widthRef = useRef(0)\n  const pointerInteracting = useRef<number | null>(null)\n  const pointerInteractionMovement = useRef(0)\n\n  const r = useMotionValue(0)\n  const rs = useSpring(r, {\n    mass: 1,\n    damping: 30,\n    stiffness: 100,\n  })\n\n  const updatePointerInteraction = (value: number | null) => {\n    pointerInteracting.current = value\n    if (canvasRef.current) {\n      canvasRef.current.style.cursor = value !== null ? \"grabbing\" : \"grab\"\n    }\n  }\n\n  const updateMovement = (clientX: number) => {\n    if (pointerInteracting.current !== null) {\n      const delta = clientX - pointerInteracting.current\n      pointerInteractionMovement.current = delta\n      r.set(r.get() + delta / MOVEMENT_DAMPING)\n    }\n  }\n\n  useEffect(() => {\n    const onResize = () => {\n      if (canvasRef.current) {\n        widthRef.current = canvasRef.current.offsetWidth\n      }\n    }\n\n    window.addEventListener(\"resize\", onResize)\n    onResize()\n\n    const globe = createGlobe(canvasRef.current!, {\n      ...config,\n      width: widthRef.current * 2,\n      height: widthRef.current * 2,\n      onRender: (state) => {\n        if (!pointerInteracting.current) phiRef.current += 0.005\n        state.phi = phiRef.current + rs.get()\n        state.width = widthRef.current * 2\n        state.height = widthRef.current * 2\n      },\n    })\n\n    setTimeout(() => (canvasRef.current!.style.opacity = \"1\"), 0)\n    return () => {\n      globe.destroy()\n      window.removeEventListener(\"resize\", onResize)\n    }\n  }, [rs, config])\n\n  return (\n    <div\n      className={cn(\n        \"absolute inset-0 mx-auto aspect-square w-full max-w-150\",\n        className\n      )}\n    >\n      <canvas\n        className={cn(\n          \"size-full opacity-0 transition-opacity duration-500 contain-[layout_paint_size]\"\n        )}\n        ref={canvasRef}\n        onPointerDown={(e) => {\n          pointerInteracting.current = e.clientX\n          updatePointerInteraction(e.clientX)\n        }}\n        onPointerUp={() => updatePointerInteraction(null)}\n        onPointerOut={() => updatePointerInteraction(null)}\n        onMouseMove={(e) => updateMovement(e.clientX)}\n        onTouchMove={(e) =>\n          e.touches[0] && updateMovement(e.touches[0].clientX)\n        }\n      />\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}