{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "weather-chip",
  "title": "Weather Chip",
  "description": "Open-Meteo weather chip — no API key required.",
  "registryDependencies": [
    "@atroui/utils"
  ],
  "files": [
    {
      "path": "registry/default/blocks/weather-chip.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/** Edit lat/lon after install. Open-Meteo — no API key. */\nconst CONTENT = {\n  lat: 40.7128,\n  lon: -74.006,\n  label: \"Local\",\n}\n\ntype WeatherData = {\n  temperature: number\n  feelsLike: number\n  weatherCode: number\n  isDay: boolean\n}\n\nfunction weatherGlyph(code: number, isDay = true): string {\n  if (code === 0) return isDay ? \"☀️\" : \"🌙\"\n  if (code <= 3) return \"⛅\"\n  if (code <= 48) return \"🌫️\"\n  if (code <= 67) return \"🌧️\"\n  if (code <= 77) return \"🌨️\"\n  if (code <= 82) return \"🌦️\"\n  if (code <= 86) return \"❄️\"\n  if (code <= 99) return \"⛈️\"\n  return \"🌡️\"\n}\n\nfunction useWeather(lat: number | null, lon: number | null) {\n  const [data, setData] = useState<WeatherData | null>(null)\n  const [error, setError] = useState<string | null>(null)\n  const [isLoading, setIsLoading] = useState(false)\n\n  useEffect(() => {\n    if (lat === null || lon === null) return\n\n    let cancelled = false\n\n    const fetchWeather = async () => {\n      setIsLoading(true)\n      setError(null)\n      try {\n        const response = await fetch(\n          `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=temperature_2m,apparent_temperature,weather_code,is_day`\n        )\n        if (!response.ok) throw new Error(\"Failed to fetch weather data\")\n        const result = await response.json()\n        if (cancelled) return\n        setData({\n          temperature: Math.round(result.current.temperature_2m),\n          feelsLike: Math.round(result.current.apparent_temperature),\n          weatherCode: result.current.weather_code,\n          isDay: result.current.is_day === 1,\n        })\n      } catch (err: unknown) {\n        if (cancelled) return\n        setError(err instanceof Error ? err.message : \"Weather unavailable\")\n      } finally {\n        if (!cancelled) setIsLoading(false)\n      }\n    }\n\n    fetchWeather()\n    return () => {\n      cancelled = true\n    }\n  }, [lat, lon])\n\n  return { data, error, isLoading }\n}\n\nexport function WeatherChip({\n  lat = CONTENT.lat,\n  lon = CONTENT.lon,\n  label = CONTENT.label,\n  className,\n}: {\n  lat?: number\n  lon?: number\n  label?: string\n  className?: string\n} = {}) {\n  const { data: weather, isLoading, error } = useWeather(lat, lon)\n\n  if (isLoading) {\n    return (\n      <span\n        className={cn(\n          \"inline-flex animate-pulse items-center gap-1 font-mono text-[11.5px] text-muted-foreground tabular-nums\",\n          className\n        )}\n        aria-label=\"Weather loading\"\n      >\n        ···\n      </span>\n    )\n  }\n\n  if (error || !weather) {\n    return (\n      <span\n        className={cn(\n          \"inline-flex items-center gap-1 font-mono text-[11.5px] text-muted-foreground tabular-nums\",\n          className\n        )}\n        aria-label=\"Weather unavailable\"\n        title=\"Weather offline\"\n      >\n        —\n      </span>\n    )\n  }\n\n  const glyph = weatherGlyph(weather.weatherCode, weather.isDay)\n\n  return (\n    <span\n      className={cn(\n        \"inline-flex items-center gap-1 font-mono text-[11.5px] text-muted-foreground tabular-nums\",\n        className\n      )}\n      aria-label={`Weather in ${label}, ${weather.temperature} degrees`}\n      title={`${label} · feels ${weather.feelsLike}°`}\n    >\n      <span aria-hidden>{glyph}</span>\n      <span className=\"text-foreground\">{weather.temperature}°</span>\n    </span>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/weather-chip.tsx"
    }
  ],
  "type": "registry:block"
}