{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "error-boundary-ui",
  "title": "Error Boundary UI",
  "description": "Error display component for React error boundaries with stack trace and retry",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/devtools/error-boundary-ui/components/elements/error-boundary-ui.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  AlertTriangle,\n  ChevronDown,\n  ChevronUp,\n  Copy,\n  RefreshCw,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface ErrorBoundaryUiProps {\n  error: Error;\n  resetError?: () => void;\n  componentStack?: string | null;\n  isDev?: boolean;\n  className?: string;\n}\n\nfunction parseStackTrace(\n  stack: string,\n): { file: string; line: string; column: string; fn: string }[] {\n  const lines = stack.split(\"\\n\").slice(1);\n  return lines\n    .map((line) => {\n      const match =\n        line.match(/at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)/) ||\n        line.match(/at\\s+(.+?):(\\d+):(\\d+)/);\n      if (match) {\n        if (match.length === 5) {\n          return {\n            fn: match[1],\n            file: match[2],\n            line: match[3],\n            column: match[4],\n          };\n        }\n        return {\n          fn: \"anonymous\",\n          file: match[1],\n          line: match[2],\n          column: match[3],\n        };\n      }\n      return null;\n    })\n    .filter((x): x is NonNullable<typeof x> => x !== null);\n}\n\nexport function ErrorBoundaryUi({\n  error,\n  resetError,\n  componentStack,\n  isDev = process.env.NODE_ENV === \"development\",\n  className,\n}: ErrorBoundaryUiProps) {\n  const [showStack, setShowStack] = React.useState(isDev);\n  const [showComponentStack, setShowComponentStack] = React.useState(false);\n  const [copied, setCopied] = React.useState(false);\n\n  const stackFrames = React.useMemo(\n    () => (error.stack ? parseStackTrace(error.stack) : []),\n    [error.stack],\n  );\n\n  const handleCopy = React.useCallback(async () => {\n    const errorText = [\n      `Error: ${error.message}`,\n      \"\",\n      \"Stack Trace:\",\n      error.stack,\n      componentStack ? `\\nComponent Stack:\\n${componentStack}` : \"\",\n    ].join(\"\\n\");\n\n    await navigator.clipboard.writeText(errorText);\n    setCopied(true);\n    setTimeout(() => setCopied(false), 1500);\n  }, [error.message, error.stack, componentStack]);\n\n  const handleToggleStack = React.useCallback(() => {\n    setShowStack((prev) => !prev);\n  }, []);\n\n  const handleToggleComponentStack = React.useCallback(() => {\n    setShowComponentStack((prev) => !prev);\n  }, []);\n\n  return (\n    <div\n      data-slot=\"error-boundary-ui\"\n      role=\"alert\"\n      aria-live=\"assertive\"\n      aria-atomic=\"true\"\n      className={cn(\n        \"rounded-lg border border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-950/30 overflow-hidden\",\n        className,\n      )}\n    >\n      <div className=\"flex items-start gap-3 p-4\">\n        <div className=\"shrink-0 mt-0.5\">\n          <AlertTriangle className=\"w-5 h-5 text-red-500\" />\n        </div>\n        <div className=\"flex-1 min-w-0\">\n          <h3 className=\"font-semibold text-red-700 dark:text-red-300\">\n            {isDev ? error.name || \"Error\" : \"Something went wrong\"}\n          </h3>\n          <p className=\"mt-1 text-sm text-red-600 dark:text-red-400 break-words\">\n            {isDev\n              ? error.message\n              : \"An unexpected error occurred. Please try again.\"}\n          </p>\n        </div>\n      </div>\n\n      <div className=\"flex items-center gap-2 px-4 pb-4\">\n        {resetError && (\n          <button\n            type=\"button\"\n            onClick={resetError}\n            aria-label=\"Try again\"\n            className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 rounded hover:bg-red-200 dark:hover:bg-red-900 transition-colors\"\n          >\n            <RefreshCw className=\"w-3.5 h-3.5\" />\n            Try again\n          </button>\n        )}\n        <button\n          type=\"button\"\n          onClick={handleCopy}\n          aria-label={copied ? \"Copied to clipboard\" : \"Copy error details\"}\n          className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 rounded hover:bg-red-200 dark:hover:bg-red-900 transition-colors\"\n        >\n          <Copy className=\"w-3.5 h-3.5\" />\n          {copied ? \"Copied!\" : \"Copy error\"}\n        </button>\n      </div>\n\n      {isDev && error.stack && (\n        <div className=\"border-t border-red-200 dark:border-red-900\">\n          <button\n            type=\"button\"\n            onClick={handleToggleStack}\n            aria-expanded={showStack}\n            aria-controls=\"stack-trace-content\"\n            aria-label=\"Toggle stack trace\"\n            className=\"w-full flex items-center justify-between px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors\"\n          >\n            <span className=\"font-medium\">Stack Trace</span>\n            {showStack ? (\n              <ChevronUp className=\"w-4 h-4\" />\n            ) : (\n              <ChevronDown className=\"w-4 h-4\" />\n            )}\n          </button>\n          {showStack && (\n            <div\n              id=\"stack-trace-content\"\n              className=\"px-4 pb-4 overflow-auto\"\n              aria-label=\"Error stack trace\"\n            >\n              <div className=\"font-mono text-xs space-y-1\">\n                {stackFrames.map((frame, idx) => (\n                  <div\n                    key={idx}\n                    className=\"flex gap-2 text-red-600 dark:text-red-400\"\n                  >\n                    <span className=\"text-red-400 dark:text-red-600 shrink-0\">\n                      at\n                    </span>\n                    <span className=\"text-red-700 dark:text-red-300\">\n                      {frame.fn}\n                    </span>\n                    <span className=\"text-red-500 truncate\">\n                      ({frame.file}:{frame.line}:{frame.column})\n                    </span>\n                  </div>\n                ))}\n              </div>\n            </div>\n          )}\n        </div>\n      )}\n\n      {isDev && componentStack && (\n        <div className=\"border-t border-red-200 dark:border-red-900\">\n          <button\n            type=\"button\"\n            onClick={handleToggleComponentStack}\n            aria-expanded={showComponentStack}\n            aria-controls=\"component-stack-content\"\n            aria-label=\"Toggle component stack\"\n            className=\"w-full flex items-center justify-between px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors\"\n          >\n            <span className=\"font-medium\">Component Stack</span>\n            {showComponentStack ? (\n              <ChevronUp className=\"w-4 h-4\" />\n            ) : (\n              <ChevronDown className=\"w-4 h-4\" />\n            )}\n          </button>\n          {showComponentStack && (\n            <div\n              id=\"component-stack-content\"\n              className=\"px-4 pb-4 overflow-auto\"\n              aria-label=\"Component stack trace\"\n            >\n              <pre className=\"font-mono text-xs text-red-600 dark:text-red-400 whitespace-pre-wrap\">\n                {componentStack}\n              </pre>\n            </div>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport type { ErrorBoundaryUiProps };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Stack trace parsing, retry button, copy error, dev/prod modes with different verbosity.",
  "categories": [
    "devtools"
  ],
  "type": "registry:ui"
}