{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "response",
  "type": "registry:ui",
  "description": "Lightweight markdown renderer for AI responses with Tailwind Typography prose",
  "files": [
    {
      "path": "components/ui/Response.tsx",
      "content": "import { useMemo } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface ResponseProps {\n  /** Markdown content to render. Supports streaming — just append tokens. */\n  children: string;\n  className?: string;\n}\n\n/**\n * Lightweight markdown renderer for AI responses.\n * Handles bold, italic, inline code, code blocks, lists, and paragraphs.\n * No external dependencies — uses regex-based parsing.\n *\n * Applies Tailwind Typography `prose` class for consistent styling that\n * inherits the host app's shadcn theme.\n *\n * For streaming, just update the children string as tokens arrive:\n * ```tsx\n * const [text, setText] = useState(\"\");\n * setText(prev => prev + token);\n * return <Response>{text}</Response>;\n * ```\n */\nexport function Response({ children, className }: ResponseProps) {\n  const html = useMemo(() => renderMarkdown(children), [children]);\n\n  return (\n    <div\n      className={cn(\n        \"dg:prose dg:prose-sm dg:dark:prose-invert dg:max-w-none\",\n        \"dg:prose-a:text-primary dg:prose-code:text-foreground\",\n        \"dg:prose-pre:bg-input dg:prose-pre:border dg:prose-pre:border-border\",\n        className\n      )}\n      data-agent-response\n      dangerouslySetInnerHTML={{ __html: html }}\n    />\n  );\n}\n\nfunction renderMarkdown(md: string): string {\n  const lines = md.split(\"\\n\");\n  const out: string[] = [];\n  let inCodeBlock = false;\n  let codeLang = \"\";\n  let codeLines: string[] = [];\n  let inList = false;\n  let listType: \"ul\" | \"ol\" = \"ul\";\n\n  for (const line of lines) {\n    if (line.trimStart().startsWith(\"```\")) {\n      if (inCodeBlock) {\n        out.push(\n          `<pre data-lang=\"${esc(codeLang)}\"><code>${esc(codeLines.join(\"\\n\"))}</code></pre>`,\n        );\n        codeLines = [];\n        codeLang = \"\";\n        inCodeBlock = false;\n      } else {\n        if (inList) { out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\"); inList = false; }\n        codeLang = line.trimStart().slice(3).trim();\n        inCodeBlock = true;\n      }\n      continue;\n    }\n\n    if (inCodeBlock) {\n      codeLines.push(line);\n      continue;\n    }\n\n    const trimmed = line.trim();\n\n    if (!trimmed) {\n      if (inList) { out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\"); inList = false; }\n      continue;\n    }\n\n    const headingMatch = trimmed.match(/^(#{1,6})\\s+(.+)$/);\n    if (headingMatch) {\n      if (inList) { out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\"); inList = false; }\n      const level = headingMatch[1].length;\n      out.push(`<h${level}>${inline(headingMatch[2])}</h${level}>`);\n      continue;\n    }\n\n    if (/^[-*+]\\s/.test(trimmed)) {\n      if (!inList || listType !== \"ul\") {\n        if (inList) out.push(\"</ol>\");\n        out.push(\"<ul>\");\n        inList = true;\n        listType = \"ul\";\n      }\n      out.push(`<li>${inline(trimmed.replace(/^[-*+]\\s/, \"\"))}</li>`);\n      continue;\n    }\n\n    const olMatch = trimmed.match(/^\\d+\\.\\s(.+)$/);\n    if (olMatch) {\n      if (!inList || listType !== \"ol\") {\n        if (inList) out.push(\"</ul>\");\n        out.push(\"<ol>\");\n        inList = true;\n        listType = \"ol\";\n      }\n      out.push(`<li>${inline(olMatch[1])}</li>`);\n      continue;\n    }\n\n    if (/^[-*_]{3,}$/.test(trimmed)) {\n      if (inList) { out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\"); inList = false; }\n      out.push(\"<hr />\");\n      continue;\n    }\n\n    if (inList) { out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\"); inList = false; }\n    out.push(`<p>${inline(trimmed)}</p>`);\n  }\n\n  if (inCodeBlock) {\n    out.push(`<pre data-lang=\"${esc(codeLang)}\"><code>${esc(codeLines.join(\"\\n\"))}</code></pre>`);\n  }\n  if (inList) out.push(listType === \"ul\" ? \"</ul>\" : \"</ol>\");\n\n  return out.join(\"\");\n}\n\nfunction inline(text: string): string {\n  return esc(text)\n    .replace(/`([^`]+)`/g, \"<code>$1</code>\")\n    .replace(/\\*\\*([^*]+)\\*\\*/g, \"<strong>$1</strong>\")\n    .replace(/__([^_]+)__/g, \"<strong>$1</strong>\")\n    .replace(/\\*([^*]+)\\*/g, \"<em>$1</em>\")\n    .replace(/_([^_]+)_/g, \"<em>$1</em>\")\n    .replace(\n      /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n      '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>',\n    );\n}\n\nfunction esc(s: string): string {\n  return s\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\");\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "devDependencies": [
    "@tailwindcss/typography"
  ],
  "registryDependencies": [
    "utils"
  ]
}
