{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "response",
  "title": "Response",
  "description": "An assistant's answer as markdown, including while it is still half-written and briefly invalid.",
  "dependencies": [
    "react-markdown@^10.1.0",
    "remark-gfm@^4.0.1"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/response/response.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport Markdown from \"react-markdown\"\nimport remarkGfm from \"remark-gfm\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type ResponseProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  children: string\n  /** Still arriving. Half-written markdown is closed off rather than shown raw. */\n  streaming?: boolean\n  /**\n   * Renders a fenced block. The default is a plain panel, so this component\n   * installs on its own; pass Code Block here if you have it and want the copy\n   * control and the line numbers with it.\n   */\n  renderCode?: (code: string, language?: string) => React.ReactNode\n}\n\n/**\n * An assistant's answer rendered as markdown, including while it is still\n * being written.\n *\n * Streaming markdown is markdown that is briefly invalid: a fence opened and\n * not yet closed, a bold marker with nothing after it. Rendering it as it\n * arrives leaves stray asterisks and a code block that swallows the rest of\n * the reply, so the unterminated parts are closed for the duration of the\n * render and reopened by the next token.\n */\nfunction closeOpenMarkers(text: string) {\n  let closed = text\n\n  const fences = (closed.match(/^```/gm) ?? []).length\n  if (fences % 2 === 1) closed += \"\\n```\"\n\n  // Inline markers only matter on the last line, which is the one still growing.\n  const lastBreak = closed.lastIndexOf(\"\\n\")\n  const head = lastBreak === -1 ? \"\" : closed.slice(0, lastBreak + 1)\n  let tail = lastBreak === -1 ? closed : closed.slice(lastBreak + 1)\n\n  if (!tail.startsWith(\"```\")) {\n    for (const marker of [\"**\", \"`\", \"_\"]) {\n      const parts = tail.split(marker).length - 1\n      if (parts % 2 === 1) tail += marker\n    }\n  }\n\n  return head + tail\n}\n\nexport function Response({\n  children,\n  streaming = false,\n  renderCode,\n  className,\n  ...rootProps\n}: ResponseProps) {\n  const text = React.useMemo(\n    () => (streaming ? closeOpenMarkers(children) : children),\n    [children, streaming]\n  )\n\n  return (\n    <div\n      data-slot=\"response\"\n      data-streaming={streaming ? \"\" : undefined}\n      className={cn(\n        \"text-sm leading-relaxed [&_a]:underline [&_a]:underline-offset-4 [&_li]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_ul]:list-disc [&_ul]:pl-5 [&>*+*]:mt-3\",\n        className\n      )}\n      {...rootProps}\n    >\n      <Markdown\n        remarkPlugins={[remarkGfm]}\n        components={{\n          code({ className: language, children: code, ...props }) {\n            const name = /language-(\\w+)/.exec(language ?? \"\")?.[1]\n            const body = String(code).replace(/\\n$/, \"\")\n\n            // Inline code stays inline; only a fenced block becomes a panel.\n            if (!name && !body.includes(\"\\n\")) {\n              return (\n                <code\n                  className=\"bg-muted rounded px-1.5 py-0.5 font-mono text-[0.85em]\"\n                  {...props}\n                >\n                  {code}\n                </code>\n              )\n            }\n\n            if (renderCode) return <>{renderCode(body, name)}</>\n\n            return (\n              <span className=\"border-border bg-muted/40 my-3 block overflow-x-auto rounded-[calc(var(--radius))] border p-3\">\n                <code className=\"font-mono text-xs whitespace-pre\">{body}</code>\n              </span>\n            )\n          },\n          pre({ children: inner }) {\n            return <>{inner}</>\n          },\n        }}\n      >\n        {text}\n      </Markdown>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "ai",
    "agent",
    "chat",
    "markdown",
    "streaming"
  ],
  "type": "registry:ui"
}