{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-error-handler",
  "title": "AI Error Handler",
  "description": "Display and handle AI errors with retry functionality.",
  "registryDependencies": [
    "alert",
    "badge",
    "button",
    "separator"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/ai/ai-error-handler.tsx",
      "content": "\"use client\";\n\nimport { AlertTriangle, Loader2, RefreshCw, X } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Alert,\n  AlertDescription,\n  AlertTitle,\n} from \"@/registry/new-york/ui/alert\";\nimport { Button } from \"@/registry/new-york/ui/button\";\n\nexport interface AIErrorHandlerProps {\n  error?: string | Error | null;\n  title?: string;\n  onRetry?: () => Promise<void> | void;\n  onDismiss?: () => void;\n  className?: string;\n}\n\nfunction getErrorMessage(error: string | Error | null | undefined): string {\n  if (!error) return \"\";\n  if (typeof error === \"string\") return error;\n  return error.message || \"An error occurred\";\n}\n\nexport default function AIErrorHandler({\n  error,\n  title = \"An Error Occurred\",\n  onRetry,\n  onDismiss,\n  className,\n}: AIErrorHandlerProps) {\n  const [isRetrying, setIsRetrying] = useState(false);\n\n  const message = getErrorMessage(error);\n\n  const handleRetry = useCallback(async () => {\n    if (!onRetry) return;\n\n    setIsRetrying(true);\n    try {\n      await onRetry();\n    } finally {\n      setIsRetrying(false);\n    }\n  }, [onRetry]);\n\n  if (!(error && message)) return null;\n\n  return (\n    <Alert\n      className={cn(\"w-full\", className)}\n      live=\"assertive\"\n      variant=\"destructive\"\n    >\n      <AlertTriangle aria-hidden=\"true\" className=\"size-4\" />\n      <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n        <div className=\"flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between\">\n          <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n            <AlertTitle className=\"wrap-break-word font-medium text-base\">\n              {title}\n            </AlertTitle>\n            <AlertDescription className=\"wrap-break-word\">\n              {message}\n            </AlertDescription>\n          </div>\n          {onDismiss && (\n            <Button\n              aria-label=\"Dismiss error\"\n              className=\"min-h-[32px] min-w-[32px] shrink-0 touch-manipulation\"\n              onClick={onDismiss}\n              size=\"icon-sm\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X aria-hidden=\"true\" className=\"size-4\" />\n            </Button>\n          )}\n        </div>\n\n        {onRetry && (\n          <Button\n            aria-busy={isRetrying}\n            className=\"ml-auto min-h-[32px] w-fit touch-manipulation sm:ml-0\"\n            data-loading={isRetrying}\n            disabled={isRetrying}\n            onClick={handleRetry}\n            type=\"button\"\n            variant={\"secondary\"}\n          >\n            {isRetrying ? (\n              <>\n                <Loader2 aria-hidden=\"true\" className=\"size-4 animate-spin\" />\n                <span>Retrying…</span>\n              </>\n            ) : (\n              <>\n                <RefreshCw aria-hidden=\"true\" className=\"size-4\" />\n                <span>Retry</span>\n              </>\n            )}\n          </Button>\n        )}\n      </div>\n    </Alert>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "ai"
  ],
  "type": "registry:ui"
}