{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "markdown-output",
  "title": "Markdown Output",
  "description": "Component for rendering Markdown content in notebook outputs. Supports GitHub Flavored Markdown (GFM) with tables, task lists, and syntax-highlighted code blocks.",
  "dependencies": [
    "react-markdown",
    "remark-gfm",
    "remark-math",
    "rehype-katex",
    "rehype-raw",
    "react-syntax-highlighter",
    "@types/react-syntax-highlighter",
    "katex"
  ],
  "files": [
    {
      "path": "registry/outputs/markdown-output.tsx",
      "content": "\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport { useState } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\nimport {\n  oneDark,\n  oneLight,\n} from \"react-syntax-highlighter/dist/esm/styles/prism\";\nimport rehypeKatex from \"rehype-katex\";\nimport rehypeRaw from \"rehype-raw\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkMath from \"remark-math\";\nimport { useDarkMode } from \"@/lib/dark-mode\";\nimport { cn } from \"@/lib/utils\";\n\nimport \"katex/dist/katex.min.css\";\n\ninterface MarkdownOutputProps {\n  /**\n   * The markdown content to render\n   */\n  content: string;\n  /**\n   * Additional CSS classes\n   */\n  className?: string;\n  /**\n   * Enable copy button on code blocks\n   */\n  enableCopyCode?: boolean;\n}\n\n/**\n * Check if the current window is inside an iframe\n */\nfunction isInIframe(): boolean {\n  try {\n    return window.self !== window.top;\n  } catch {\n    return true;\n  }\n}\n\ninterface CodeBlockProps {\n  children: string;\n  language?: string;\n  enableCopy?: boolean;\n  isDark?: boolean;\n}\n\nfunction CodeBlock({\n  children,\n  language = \"\",\n  enableCopy = true,\n  isDark = false,\n}: CodeBlockProps) {\n  const [copied, setCopied] = useState(false);\n\n  const handleCopy = async () => {\n    try {\n      await navigator.clipboard.writeText(children);\n      setCopied(true);\n      setTimeout(() => setCopied(false), 2000);\n    } catch (err) {\n      console.error(\"Failed to copy code:\", err);\n    }\n  };\n\n  return (\n    <div className=\"group/codeblock relative\">\n      <SyntaxHighlighter\n        language={language}\n        style={isDark ? oneDark : oneLight}\n        PreTag=\"div\"\n        customStyle={{\n          margin: 0,\n          padding: \"0.75rem\",\n          fontSize: \"0.875rem\",\n          overflow: \"auto\",\n          background: isDark ? \"#282c34\" : \"#fafafa\",\n          borderRadius: \"0.375rem\",\n        }}\n      >\n        {children}\n      </SyntaxHighlighter>\n      {enableCopy && (\n        <button\n          onClick={handleCopy}\n          className=\"absolute top-2 right-2 z-10 rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-1.5 text-gray-600 dark:text-gray-400 opacity-0 shadow-sm transition-opacity group-hover/codeblock:opacity-100 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-gray-200\"\n          title={copied ? \"Copied!\" : \"Copy code\"}\n          type=\"button\"\n        >\n          {copied ? (\n            <Check className=\"h-3 w-3\" />\n          ) : (\n            <Copy className=\"h-3 w-3\" />\n          )}\n        </button>\n      )}\n    </div>\n  );\n}\n\n/**\n * MarkdownOutput component for rendering Markdown content in notebook outputs.\n *\n * Supports:\n * - GitHub Flavored Markdown (tables, strikethrough, task lists, autolinks)\n * - Math/LaTeX via KaTeX\n * - Syntax highlighted code blocks with copy button\n * - Raw HTML in markdown\n *\n * SECURITY: This component MUST be rendered inside a sandboxed iframe.\n * Use OutputArea (with isolated=\"auto\") or IsolatedFrame directly.\n * Throws an error if rendered in the main DOM.\n */\nexport function MarkdownOutput({\n  content,\n  className = \"\",\n  enableCopyCode = true,\n}: MarkdownOutputProps) {\n  const isDark = useDarkMode();\n\n  if (!content) {\n    return null;\n  }\n\n  // Require iframe for security - markdown can contain raw HTML\n  if (typeof window !== \"undefined\" && !isInIframe()) {\n    throw new Error(\n      \"MarkdownOutput must be rendered inside an iframe. \" +\n        \"Use OutputArea or IsolatedFrame for markdown content.\",\n    );\n  }\n\n  const remarkPlugins = [remarkGfm, remarkMath];\n  const rehypePlugins = [rehypeKatex, rehypeRaw];\n\n  return (\n    <div\n      data-slot=\"markdown-output\"\n      className={cn(\"not-prose py-2\", className)}\n    >\n      <ReactMarkdown\n        remarkPlugins={remarkPlugins}\n        rehypePlugins={rehypePlugins}\n        components={{\n          // Code blocks with syntax highlighting\n          code({ className, children, ...props }) {\n            const match = /language-(\\w+)/.exec(className || \"\");\n            const language = match ? match[1] : \"\";\n            const codeContent = String(children).replace(/\\n$/, \"\");\n\n            // Block code has newlines or a language class\n            const isBlockCode = codeContent.includes(\"\\n\") || className;\n\n            if (isBlockCode) {\n              return (\n                <CodeBlock\n                  language={language}\n                  enableCopy={enableCopyCode}\n                  isDark={isDark}\n                >\n                  {codeContent}\n                </CodeBlock>\n              );\n            }\n\n            // Inline code\n            return (\n              <code\n                className=\"rounded bg-gray-100 dark:bg-gray-800 px-1 py-0.5 text-sm text-gray-800 dark:text-gray-200\"\n                {...props}\n              >\n                {children}\n              </code>\n            );\n          },\n\n          // Links open in new tab\n          a({ href, children, ...props }) {\n            return (\n              <a\n                href={href}\n                className=\"text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:underline\"\n                rel=\"noopener noreferrer\"\n                target=\"_blank\"\n                {...props}\n              >\n                {children}\n              </a>\n            );\n          },\n\n          // Tables\n          table({ children, ...props }) {\n            return (\n              <div className=\"my-4 overflow-x-auto\">\n                <table\n                  className=\"min-w-full border-collapse border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 text-sm\"\n                  {...props}\n                >\n                  {children}\n                </table>\n              </div>\n            );\n          },\n          thead({ children, ...props }) {\n            return (\n              <thead className=\"bg-gray-50 dark:bg-gray-800\" {...props}>\n                {children}\n              </thead>\n            );\n          },\n          tbody({ children, ...props }) {\n            return (\n              <tbody\n                className=\"divide-y divide-gray-200 dark:divide-gray-700\"\n                {...props}\n              >\n                {children}\n              </tbody>\n            );\n          },\n          tr({ children, ...props }) {\n            return (\n              <tr\n                className=\"hover:bg-gray-50 dark:hover:bg-gray-800\"\n                {...props}\n              >\n                {children}\n              </tr>\n            );\n          },\n          th({ children, ...props }) {\n            return (\n              <th\n                className=\"border border-gray-300 dark:border-gray-700 px-3 py-2 text-left font-semibold text-gray-900 dark:text-gray-100\"\n                {...props}\n              >\n                {children}\n              </th>\n            );\n          },\n          td({ children, ...props }) {\n            return (\n              <td\n                className=\"border border-gray-300 dark:border-gray-700 px-3 py-2 text-gray-700 dark:text-gray-300\"\n                {...props}\n              >\n                {children}\n              </td>\n            );\n          },\n\n          // Headings\n          h1({ children, ...props }) {\n            return (\n              <h1 className=\"mb-4 mt-6 text-2xl font-bold\" {...props}>\n                {children}\n              </h1>\n            );\n          },\n          h2({ children, ...props }) {\n            return (\n              <h2 className=\"mb-3 mt-5 text-xl font-bold\" {...props}>\n                {children}\n              </h2>\n            );\n          },\n          h3({ children, ...props }) {\n            return (\n              <h3 className=\"mb-2 mt-4 text-lg font-semibold\" {...props}>\n                {children}\n              </h3>\n            );\n          },\n          h4({ children, ...props }) {\n            return (\n              <h4 className=\"mb-2 mt-3 text-base font-semibold\" {...props}>\n                {children}\n              </h4>\n            );\n          },\n          h5({ children, ...props }) {\n            return (\n              <h5 className=\"mb-1 mt-2 text-sm font-semibold\" {...props}>\n                {children}\n              </h5>\n            );\n          },\n          h6({ children, ...props }) {\n            return (\n              <h6 className=\"mb-1 mt-2 text-sm font-medium\" {...props}>\n                {children}\n              </h6>\n            );\n          },\n\n          // Paragraphs\n          p({ children, ...props }) {\n            return (\n              <p className=\"my-2 leading-relaxed\" {...props}>\n                {children}\n              </p>\n            );\n          },\n\n          // Lists\n          ul({ children, ...props }) {\n            return (\n              <ul className=\"my-2 ml-6 list-disc\" {...props}>\n                {children}\n              </ul>\n            );\n          },\n          ol({ children, ...props }) {\n            return (\n              <ol className=\"my-2 ml-6 list-decimal\" {...props}>\n                {children}\n              </ol>\n            );\n          },\n          li({ children, ...props }) {\n            return (\n              <li className=\"my-1\" {...props}>\n                {children}\n              </li>\n            );\n          },\n\n          // Blockquotes\n          blockquote({ children, ...props }) {\n            return (\n              <blockquote\n                className=\"my-4 border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-600 dark:text-gray-400\"\n                {...props}\n              >\n                {children}\n              </blockquote>\n            );\n          },\n\n          // Horizontal rule\n          hr({ ...props }) {\n            return (\n              <hr\n                className=\"my-6 border-t border-gray-300 dark:border-gray-600\"\n                {...props}\n              />\n            );\n          },\n\n          // Images\n          img({ src, alt, ...props }) {\n            if (!src) return null;\n            return (\n              <img\n                src={src}\n                alt={alt || \"\"}\n                className=\"my-4 max-w-full h-auto\"\n                {...props}\n              />\n            );\n          },\n        }}\n      >\n        {content}\n      </ReactMarkdown>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/outputs/markdown-output.tsx"
    },
    {
      "path": "lib/dark-mode.ts",
      "content": "/**\n * Dark mode detection utilities for components.\n * Checks document state and system preference to determine current mode.\n */\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * Check if the current environment prefers dark mode via system preference\n */\nexport function prefersDarkMode(): boolean {\n  if (typeof window === \"undefined\") {\n    return false;\n  }\n  return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\n/**\n * Check if the document has dark mode enabled\n * Checks multiple common patterns:\n * - class=\"dark\" or class=\"... dark ...\" on <html>\n * - color-scheme: dark on <html>\n * - data-theme=\"dark\" attribute\n * - data-mode=\"dark\" attribute\n */\nexport function documentHasDarkMode(): boolean {\n  if (typeof document === \"undefined\") {\n    return false;\n  }\n\n  const html = document.documentElement;\n\n  // Check for 'dark' in classList (Tailwind / fumadocs pattern)\n  if (html.classList.contains(\"dark\")) {\n    return true;\n  }\n\n  // Check color-scheme style\n  const colorScheme =\n    html.style.colorScheme || getComputedStyle(html).colorScheme;\n  if (colorScheme === \"dark\") {\n    return true;\n  }\n\n  // Check data-theme attribute (another common pattern)\n  if (html.getAttribute(\"data-theme\") === \"dark\") {\n    return true;\n  }\n\n  // Check data-mode attribute\n  if (html.getAttribute(\"data-mode\") === \"dark\") {\n    return true;\n  }\n\n  return false;\n}\n\n/**\n * Detect dark mode from either document state or system preference.\n * Prioritizes document state (site-level toggle) over system preference.\n */\nexport function isDarkMode(): boolean {\n  // Check document state first (site-level dark mode toggle)\n  if (documentHasDarkMode()) {\n    return true;\n  }\n\n  // Check if document explicitly has light mode set\n  if (typeof document !== \"undefined\") {\n    const html = document.documentElement;\n\n    // If 'light' class is present, respect it\n    if (html.classList.contains(\"light\")) {\n      return false;\n    }\n\n    // If color-scheme is explicitly light, respect it\n    const colorScheme =\n      html.style.colorScheme || getComputedStyle(html).colorScheme;\n    if (colorScheme === \"light\") {\n      return false;\n    }\n  }\n\n  // Fall back to system preference\n  return prefersDarkMode();\n}\n\n/**\n * React hook to detect dark mode from document state or system preference.\n * Watches for theme changes via MutationObserver and media query.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n *   const isDark = useDarkMode();\n *   return <div className={isDark ? \"bg-gray-900\" : \"bg-white\"}>...</div>;\n * }\n * ```\n */\nexport function useDarkMode(): boolean {\n  const [isDark, setIsDark] = useState(() =>\n    typeof window !== \"undefined\" ? isDarkMode() : false,\n  );\n\n  useEffect(() => {\n    setIsDark(isDarkMode());\n\n    const mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n    const handleChange = () => setIsDark(isDarkMode());\n    mediaQuery.addEventListener(\"change\", handleChange);\n\n    const observer = new MutationObserver(() => setIsDark(isDarkMode()));\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\", \"style\", \"data-theme\", \"data-mode\"],\n    });\n\n    return () => {\n      mediaQuery.removeEventListener(\"change\", handleChange);\n      observer.disconnect();\n    };\n  }, []);\n\n  return isDark;\n}\n",
      "type": "registry:lib",
      "target": "lib/dark-mode.ts"
    }
  ],
  "type": "registry:component"
}