{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "response-markdown",
  "type": "registry:component",
  "title": "Response markdown",
  "description": "Renders streaming markdown, tables, code, math, and diagrams with Vlak typography and safe default links.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/highlighted-code.json",
    "https://vlak.dev/r/button.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex",
    "streamdown@^2.6.0",
    "shiki@^3.19.0",
    "@streamdown/math@^1.0.2",
    "@streamdown/cjk@^1.0.3",
    "mermaid@^11.12.2",
    "katex@^0.16.27"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.\nRequired stylesheet: import \"katex/dist/katex.min.css\";",
  "files": [
    {
      "path": "vlak/response-markdown.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { Streamdown, defaultRehypePlugins, useIsCodeFenceIncomplete, type Components } from \"streamdown\";\nimport { createMathPlugin } from \"@streamdown/math\";\nimport { cjk } from \"@streamdown/cjk\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { HighlightedCode } from \"./highlighted-code\";\nimport { Button } from \"./button\";\n\nexport interface ResponseMarkdownProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  children: string;\n  /** True while the application is receiving this response. */\n  streaming?: boolean;\n  math?: boolean;\n  diagrams?: boolean;\n  lineNumbers?: boolean;\n  /** Only images from these exact origins load. The default renders image descriptions. */\n  imageOrigins?: readonly string[];\n  /** Origin used to resolve relative image addresses. */\n  baseUrl?: string;\n  /** Trusted application components override Vlak's Markdown elements. */\n  components?: Components;\n}\n\nconst mathPlugin = createMathPlugin({ errorColor: \"currentColor\" });\n// Keep sanitization, omit raw HTML parsing, and validate links/images separately below.\nconst rehypePlugins = [defaultRehypePlugins.sanitize!];\nconst noImages: readonly string[] = [];\nconst noLinkModal = { enabled: false };\n\nfunction safeUrl(value: string | undefined, kind: \"link\" | \"image\", origins: readonly string[], baseUrl?: string): string | undefined {\n  if (!value || Array.from(value).some((character) => character.charCodeAt(0) <= 32 || character.charCodeAt(0) === 127)) return undefined;\n  try {\n    const parsed = new URL(value, baseUrl ?? \"https://vlak.invalid\");\n    if (kind === \"link\") return [\"https:\", \"http:\", \"mailto:\"].includes(parsed.protocol) ? value : undefined;\n    // Emit the address whose origin was checked, rather than letting the page resolve it again.\n    return [\"https:\", \"http:\"].includes(parsed.protocol) && origins.includes(parsed.origin) ? parsed.href : undefined;\n  } catch { return undefined; }\n}\n\nconst styles = stylex.create({\n  root: { minWidth: 0, width: \"100%\", color: vlak.ink, fontSize: \"0.9375rem\", lineHeight: 1.45, overflowWrap: \"anywhere\", whiteSpace: \"normal\" },\n  paragraph: { margin: \"0.75rem 0\" },\n  h1: { fontSize: \"1.5rem\", lineHeight: 1.25, fontWeight: 600, margin: \"1.25rem 0 0.75rem\" },\n  h2: { fontSize: \"1.25rem\", lineHeight: 1.3, fontWeight: 600, margin: \"1.25rem 0 0.75rem\" },\n  h3: { fontSize: \"1.0625rem\", lineHeight: 1.35, fontWeight: 600, margin: \"1rem 0 0.5rem\" },\n  h4: { fontSize: \"1rem\", lineHeight: 1.35, fontWeight: 600, margin: \"1rem 0 0.5rem\" },\n  h5: { fontSize: \"0.9375rem\", lineHeight: 1.4, fontWeight: 600, margin: \"1rem 0 0.5rem\" },\n  h6: { fontSize: \"0.9375rem\", lineHeight: 1.4, fontWeight: 600, margin: \"1rem 0 0.5rem\" },\n  strong: { fontWeight: 600 },\n  list: { margin: \"0.75rem 0\", paddingInlineStart: \"1.5rem\" },\n  item: { margin: \"0.25rem 0\", paddingInlineStart: \"0.125rem\" },\n  quote: { margin: \"1rem 0\", padding: \"0 1rem\", borderInlineStartWidth: 2, borderInlineStartStyle: \"solid\", borderInlineStartColor: vlak.divider, color: vlak.gray },\n  inline: { fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\", fontSize: \"0.875em\", backgroundColor: vlak.tableAlt, color: vlak.ink, borderRadius: vlak.radiusSm, padding: \"0.125rem 0.25rem\" },\n  link: { color: { default: vlak.gray, \":hover\": { default: null, [mq.hover]: vlak.ink }, [mq.forcedColors]: \"LinkText\" }, textDecoration: \"underline\", textUnderlineOffset: \"0.2em\", outlineWidth: { default: 0, \":focus-visible\": 2 }, outlineStyle: \"solid\", outlineColor: vlak.ink, outlineOffset: 2 },\n  image: { display: \"block\", maxWidth: \"100%\", height: \"auto\", margin: \"0.75rem 0\", borderRadius: vlak.radiusSm },\n  imageDescription: { color: vlak.gray, fontStyle: \"italic\" },\n  tableRegion: { margin: \"1rem 0\", maxWidth: \"100%\", overflow: \"auto\", borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: vlak.divider, borderRadius: vlak.radiusSm, outlineWidth: { default: 0, \":focus-visible\": 2 }, outlineStyle: \"solid\", outlineColor: vlak.ink, outlineOffset: 2 },\n  table: { borderCollapse: \"collapse\", width: \"100%\", fontSize: \"0.875rem\" },\n  th: { textAlign: \"start\", fontWeight: 600, backgroundColor: vlak.tableAlt, padding: \"0.75rem\", borderBottomWidth: vlak.hairline, borderBottomStyle: \"solid\", borderBottomColor: vlak.divider },\n  td: { padding: \"0.75rem\", verticalAlign: \"top\", borderBottomWidth: vlak.hairline, borderBottomStyle: \"solid\", borderBottomColor: vlak.divider },\n  rule: { borderWidth: 0, borderTopWidth: vlak.hairline, borderTopStyle: \"solid\", borderTopColor: vlak.divider, margin: \"1.25rem 0\" },\n  diagram: { margin: \"1rem 0\", minWidth: 0, borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: vlak.divider, borderRadius: vlak.radiusSm, overflow: \"hidden\" },\n  diagramViewport: { maxHeight: 480, overflow: \"auto\", padding: \"1rem\", outlineWidth: { default: 0, \":focus-visible\": 2 }, outlineStyle: \"solid\", outlineColor: vlak.ink, outlineOffset: -2 },\n  diagramArt: { minWidth: 0, width: \"100%\" },\n  diagramHeader: { display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: \"0.5rem\", padding: \"0.25rem 0.75rem\", minHeight: 44, borderBottomWidth: vlak.hairline, borderBottomStyle: \"solid\", borderBottomColor: vlak.divider },\n  diagramTitle: { flex: \"1 1 auto\", fontSize: \"0.8125rem\", color: vlak.gray },\n  diagramAction: { width: 44, minWidth: 44, maxWidth: 44, height: 44, padding: 0, flexShrink: 0 },\n  diagramIcon: { display: \"block\", width: 16, height: 16 },\n  summary: { cursor: \"pointer\", minHeight: 44, boxSizing: \"border-box\", padding: \"0.75rem\", color: { default: vlak.gray, \":hover\": { default: null, [mq.hover]: vlak.ink } }, fontSize: \"0.8125rem\", outlineWidth: { default: 0, \":focus-visible\": 2 }, outlineStyle: \"solid\", outlineColor: vlak.ink, outlineOffset: -2 },\n  feedback: { padding: \"0.75rem\", margin: 0, fontSize: \"0.875rem\", color: vlak.gray },\n});\n\nconst paragraph = rs([\"rs-response-markdown-paragraph\"], styles.paragraph);\nconst h1 = rs([\"rs-response-markdown-h1\"], styles.h1);\nconst h2 = rs([\"rs-response-markdown-h2\"], styles.h2);\nconst h3 = rs([\"rs-response-markdown-h3\"], styles.h3);\nconst h4 = rs([\"rs-response-markdown-h4\"], styles.h4);\nconst h5 = rs([\"rs-response-markdown-h5\"], styles.h5);\nconst h6 = rs([\"rs-response-markdown-h6\"], styles.h6);\nconst strong = rs([\"rs-response-markdown-strong\"], styles.strong);\nconst list = rs([\"rs-response-markdown-list\"], styles.list);\nconst item = rs([\"rs-response-markdown-item\"], styles.item);\nconst quote = rs([\"rs-response-markdown-quote\"], styles.quote);\nconst inline = rs([\"rs-response-markdown-inline\"], styles.inline);\nconst link = rs([\"rs-response-markdown-link\"], styles.link);\nconst image = rs([\"rs-response-markdown-image\"], styles.image);\nconst imageDescription = rs([\"rs-response-markdown-image-description\"], styles.imageDescription);\nconst tableRegion = rs([\"rs-response-markdown-table-region\"], styles.tableRegion);\nconst table = rs([\"rs-response-markdown-table\"], styles.table);\nconst th = rs([\"rs-response-markdown-th\"], styles.th);\nconst td = rs([\"rs-response-markdown-td\"], styles.td);\nconst rule = rs([\"rs-response-markdown-rule\"], styles.rule);\n\nlet mermaidQueue = Promise.resolve();\nasync function renderDiagram(source: string, id: string, dark: boolean): Promise<string> {\n  let result = \"\";\n  const pending = mermaidQueue.then(async () => {\n    const { default: mermaid } = await import(\"mermaid\");\n    mermaid.initialize({\n      startOnLoad: false, securityLevel: \"strict\", suppressErrorRendering: true,\n      maxTextSize: 30_000, maxEdges: 500, theme: \"base\", htmlLabels: false,\n      themeVariables: {\n        darkMode: dark, background: dark ? \"#111111\" : \"#ffffff\", fontFamily: \"sans-serif\",\n        primaryColor: dark ? \"#222222\" : \"#f5f5f5\", primaryTextColor: dark ? \"#f5f5f5\" : \"#111111\",\n        primaryBorderColor: dark ? \"#999999\" : \"#777777\", lineColor: dark ? \"#999999\" : \"#777777\",\n        secondaryColor: dark ? \"#1a1a1a\" : \"#eeeeee\", tertiaryColor: dark ? \"#2a2a2a\" : \"#ffffff\",\n        textColor: dark ? \"#f5f5f5\" : \"#111111\", actorTextColor: dark ? \"#f5f5f5\" : \"#111111\",\n        signalTextColor: dark ? \"#f5f5f5\" : \"#111111\", labelTextColor: dark ? \"#f5f5f5\" : \"#111111\",\n        noteTextColor: dark ? \"#f5f5f5\" : \"#111111\", noteBkgColor: dark ? \"#222222\" : \"#eeeeee\",\n        edgeLabelBackground: dark ? \"#222222\" : \"#eeeeee\",\n      },\n      flowchart: { htmlLabels: false },\n      secure: [\"securityLevel\", \"startOnLoad\", \"maxTextSize\", \"maxEdges\", \"htmlLabels\", \"flowchart\", \"theme\", \"themeVariables\", \"themeCSS\"],\n    });\n    result = (await mermaid.render(id, source)).svg;\n  });\n  mermaidQueue = pending.catch(() => undefined);\n  await pending;\n  return result;\n}\n\nfunction Diagram({ source, incomplete }: { source: string; incomplete: boolean }) {\n  const id = `vlak-diagram-${React.useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  const figureRef = React.useRef<HTMLElement>(null);\n  const [dark, setDark] = React.useState<boolean | null>(null);\n  const [rendered, setRendered] = React.useState<{ source: string; dark: boolean; svg?: string; error?: string } | null>(null);\n  const [scale, setScale] = React.useState(1);\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Completing a fence mounts the figure and changes its theme ancestors.\n  React.useEffect(() => {\n    const media = window.matchMedia?.(\"(prefers-color-scheme: dark)\");\n    const read = () => {\n      const explicit = (figureRef.current?.closest(\"[data-theme]\") ?? document.documentElement).getAttribute(\"data-theme\");\n      setDark(explicit === \"dark\" || (explicit !== \"light\" && !!media?.matches));\n    };\n    read();\n    const observer = new MutationObserver(read);\n    for (let element: Element | null = figureRef.current ?? document.documentElement; element; element = element.parentElement) observer.observe(element, { attributes: true, attributeFilter: [\"data-theme\"] });\n    media?.addEventListener(\"change\", read);\n    return () => { observer.disconnect(); media?.removeEventListener(\"change\", read); };\n  }, [incomplete]);\n  React.useEffect(() => {\n    let cancelled = false;\n    if (dark !== null && !incomplete && source.length <= 30_000) void renderDiagram(source, id, dark).then((svg) => {\n      if (!cancelled) setRendered({ source, dark, svg });\n    }).catch(() => { if (!cancelled && dark !== null) setRendered({ source, dark, error: \"The diagram could not be rendered. Its source is available below.\" }); });\n    return () => { cancelled = true; };\n  }, [source, id, incomplete, dark]);\n  const root = rs([\"rs-response-markdown-diagram\"], styles.diagram);\n  const viewport = rs([\"rs-response-markdown-diagram-viewport\"], styles.diagramViewport);\n  const art = rs([\"rs-response-markdown-diagram-art\"], styles.diagramArt);\n  const header = rs([\"rs-response-markdown-diagram-header\"], styles.diagramHeader);\n  const title = rs([\"rs-response-markdown-diagram-title\"], styles.diagramTitle);\n  const action = rs([\"rs-response-markdown-diagram-action\"], styles.diagramAction);\n  const icon = rs([\"rs-response-markdown-diagram-icon\"], styles.diagramIcon);\n  const summary = rs([\"rs-response-markdown-summary\"], styles.summary);\n  const feedback = rs([\"rs-response-markdown-feedback\"], styles.feedback);\n  if (incomplete) return <HighlightedCode code={source} language=\"mermaid\" streaming />;\n  const current = rendered?.source === source && rendered.dark === dark ? rendered : null;\n  return <figure {...root} ref={figureRef}>\n    <figcaption {...header}><span {...title}>Diagram</span>\n      <Button {...action} variant=\"subtle\" aria-label=\"Zoom out diagram\" title=\"Zoom out diagram\" disabled={!current?.svg || scale <= 0.5} onClick={() => setScale((value) => Math.max(0.5, value - 0.25))}><svg {...icon} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" aria-hidden=\"true\"><path d=\"M5 12h14\" /></svg></Button>\n      <Button {...action} variant=\"subtle\" aria-label=\"Zoom in diagram\" title=\"Zoom in diagram\" disabled={!current?.svg || scale >= 2} onClick={() => setScale((value) => Math.min(2, value + 0.25))}><svg {...icon} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" aria-hidden=\"true\"><path d=\"M5 12h14M12 5v14\" /></svg></Button>\n      <Button variant=\"subtle\" disabled={scale === 1} onClick={() => setScale(1)} aria-label=\"Reset diagram zoom\">{Math.round(scale * 100)}%</Button>\n    </figcaption>\n    {current?.svg ? <div {...viewport} tabIndex={0} role=\"group\" aria-label=\"Diagram preview\"><div {...art} style={{ ...art.style, width: `${scale * 100}%` }}\n      // Mermaid's strict renderer supplies sanitized SVG; model-authored HTML is never injected.\n      dangerouslySetInnerHTML={{ __html: current.svg }} /></div> : <p {...feedback} role=\"status\">{source.length > 30_000 ? \"The diagram is too large to preview. Its source is available below.\" : current?.error ?? \"Rendering diagram…\"}</p>}\n    <details><summary {...summary}>Diagram source</summary><HighlightedCode code={source} language=\"mermaid\" filename=\"diagram.mmd\" /></details>\n  </figure>;\n}\n\nfunction MarkdownCode({ children, className, diagrams, lineNumbers }: { children?: React.ReactNode; className?: string; diagrams: boolean; lineNumbers: boolean }) {\n  const incomplete = useIsCodeFenceIncomplete();\n  const source = typeof children === \"string\" ? children : \"\";\n  const language = /(?:^|\\s)language-([^\\s]+)/.exec(className ?? \"\")?.[1] ?? \"text\";\n  if (diagrams && language.toLowerCase() === \"mermaid\") return <Diagram source={source} incomplete={incomplete} />;\n  return <HighlightedCode code={source} language={language} streaming={incomplete} lineNumbers={lineNumbers} />;\n}\n\n/** Optional streaming Markdown, math, code, and diagrams with Vlak paint and safe default URLs. */\nexport const ResponseMarkdown = React.forwardRef<HTMLDivElement, ResponseMarkdownProps>(function ResponseMarkdown({\n  children, streaming = false, math = true, diagrams = true, lineNumbers = false,\n  imageOrigins = noImages, baseUrl, components, className, style, ...props\n}, ref) {\n  const root = rs([\"rs-response-markdown\", className], styles.root);\n  const renderers = React.useMemo<Components>(() => ({\n    p: ({ children }) => <p {...paragraph}>{children}</p>,\n    h1: ({ children, id }) => <h1 {...h1} id={id}>{children}</h1>, h2: ({ children, id }) => <h2 {...h2} id={id}>{children}</h2>,\n    h3: ({ children, id }) => <h3 {...h3} id={id}>{children}</h3>, h4: ({ children, id }) => <h4 {...h4} id={id}>{children}</h4>,\n    h5: ({ children, id }) => <h5 {...h5} id={id}>{children}</h5>, h6: ({ children, id }) => <h6 {...h6} id={id}>{children}</h6>,\n    strong: ({ children }) => <strong {...strong}>{children}</strong>,\n    em: ({ children }) => <em>{children}</em>, del: ({ children }) => <del>{children}</del>,\n    ul: ({ children }) => <ul {...list}>{children}</ul>, ol: ({ children, start }) => <ol {...list} start={start}>{children}</ol>,\n    li: ({ children, id }) => <li {...item} id={id}>{children}</li>,\n    blockquote: ({ children }) => <blockquote {...quote}>{children}</blockquote>,\n    inlineCode: ({ children }) => <code {...inline}>{children}</code>,\n    code: ({ children, className }) => <MarkdownCode diagrams={diagrams} lineNumbers={lineNumbers} className={className}>{children}</MarkdownCode>,\n    a: ({ children, href, title, id, ...native }) => {\n      const safe = safeUrl(href, \"link\", imageOrigins, baseUrl);\n      if (!safe) return <span>{children}</span>;\n      return <a {...link} href={safe} title={title ?? safe} id={id} aria-label={native[\"aria-label\"]} data-footnote-ref={native[\"data-footnote-ref\" as keyof typeof native]}>{children}</a>;\n    },\n    img: ({ src, alt, title }) => {\n      const safe = typeof src === \"string\" ? safeUrl(src, \"image\", imageOrigins, baseUrl) : undefined;\n      return safe ? <img {...image} src={safe} alt={alt ?? \"\"} title={title} loading=\"lazy\" referrerPolicy=\"no-referrer\" /> : <span {...imageDescription}>{alt ? `Image: ${alt}` : \"Image\"}</span>;\n    },\n    table: ({ children }) => <div {...tableRegion} role=\"group\" aria-label=\"Response table\" tabIndex={0}><table {...table}>{children}</table></div>,\n    thead: ({ children }) => <thead>{children}</thead>, tbody: ({ children }) => <tbody>{children}</tbody>, tr: ({ children }) => <tr>{children}</tr>,\n    th: ({ children, align }) => <th {...th} style={{ ...th.style, textAlign: align === \"char\" ? \"start\" : align ?? \"start\" }}>{children}</th>,\n    td: ({ children, align }) => <td {...td} style={{ ...td.style, textAlign: align === \"char\" ? \"start\" : align ?? \"start\" }}>{children}</td>,\n    hr: () => <hr {...rule} />,\n    input: ({ checked }) => <input type=\"checkbox\" checked={checked} disabled aria-label={checked ? \"Completed task\" : \"Incomplete task\"} />,\n    section: ({ children, id }) => <section id={id}>{children}</section>,\n    sup: ({ children }) => <sup>{children}</sup>, sub: ({ children }) => <sub>{children}</sub>,\n    ...components,\n    // Style objects contain stable compiled values; renderer identity follows configuration only.\n  }), [diagrams, lineNumbers, imageOrigins, baseUrl, components]);\n  const plugins = React.useMemo(() => ({ cjk, ...(math ? { math: mathPlugin } : {}) }), [math]);\n  const urlTransform = React.useCallback((url: string, key: string) => safeUrl(url, key === \"src\" ? \"image\" : \"link\", imageOrigins, baseUrl) ?? \"\", [imageOrigins, baseUrl]);\n  return <div {...props} ref={ref} className={root.className} style={{ ...root.style, ...style }}>\n    <Streamdown mode={streaming ? \"streaming\" : \"static\"} isAnimating={streaming} parseIncompleteMarkdown={streaming} components={renderers} plugins={plugins} rehypePlugins={rehypePlugins} skipHtml urlTransform={urlTransform} linkSafety={noLinkModal} controls={false}>{children}</Streamdown>\n  </div>;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/response-markdown.tsx"
    },
    {
      "path": "vlak/styles/response-markdown.css",
      "content": "/* ── response-markdown: generated from packages/react/src/components/response-markdown.tsx ── */\n.rs-response-markdown{min-width:0;width:100%;color:var(--text);font-size:0.9375rem;line-height:1.45;overflow-wrap:anywhere;white-space:normal}\n.rs-response-markdown-paragraph{margin:0.75rem 0}\n.rs-response-markdown-h1{font-size:1.5rem;line-height:1.25;font-weight:600;margin:1.25rem 0 0.75rem}\n.rs-response-markdown-h2{font-size:1.25rem;line-height:1.3;font-weight:600;margin:1.25rem 0 0.75rem}\n.rs-response-markdown-h3{font-size:1.0625rem;line-height:1.35;font-weight:600;margin:1rem 0 0.5rem}\n.rs-response-markdown-h4{font-size:1rem;line-height:1.35;font-weight:600;margin:1rem 0 0.5rem}\n.rs-response-markdown-h5{font-size:0.9375rem;line-height:1.4;font-weight:600;margin:1rem 0 0.5rem}\n.rs-response-markdown-h6{font-size:0.9375rem;line-height:1.4;font-weight:600;margin:1rem 0 0.5rem}\n.rs-response-markdown-strong{font-weight:600}\n.rs-response-markdown-list{margin:0.75rem 0;padding-inline-start:1.5rem}\n.rs-response-markdown-item{margin:0.25rem 0;padding-inline-start:0.125rem}\n.rs-response-markdown-quote{margin:1rem 0;padding:0 1rem;border-inline-start-width:2px;border-inline-start-style:solid;border-inline-start-color:var(--divider);color:var(--text-secondary)}\n.rs-response-markdown-inline{font-family:ui-monospace, SFMono-Regular, Menlo, monospace;font-size:0.875em;background-color:var(--table-alt);color:var(--text);border-radius:var(--radius-sm);padding:0.125rem 0.25rem}\n.rs-response-markdown-link{color:var(--text-secondary);text-decoration:underline;text-underline-offset:0.2em;outline-width:0;outline-style:solid;outline-color:var(--text);outline-offset:2px}\n.rs-response-markdown-link:focus-visible{outline-width:2px}\n@media (hover: hover) and (pointer: fine){.rs-response-markdown-link:hover{color:var(--text)}}\n@media (forced-colors: active){.rs-response-markdown-link{color:LinkText}}\n.rs-response-markdown-image{display:block;max-width:100%;height:auto;margin:0.75rem 0;border-radius:var(--radius-sm)}\n.rs-response-markdown-image-description{color:var(--text-secondary);font-style:italic}\n.rs-response-markdown-table-region{margin:1rem 0;max-width:100%;overflow:auto;border-width:1px;border-style:solid;border-color:var(--divider);border-radius:var(--radius-sm);outline-width:0;outline-style:solid;outline-color:var(--text);outline-offset:2px}\n.rs-response-markdown-table-region:focus-visible{outline-width:2px}\n.rs-response-markdown-table{border-collapse:collapse;width:100%;font-size:0.875rem}\n.rs-response-markdown-th{text-align:start;font-weight:600;background-color:var(--table-alt);padding:0.75rem;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--divider)}\n.rs-response-markdown-td{padding:0.75rem;vertical-align:top;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--divider)}\n.rs-response-markdown-rule{border-width:0;border-top-width:1px;border-top-style:solid;border-top-color:var(--divider);margin:1.25rem 0}\n.rs-response-markdown-diagram{margin:1rem 0;min-width:0;border-width:1px;border-style:solid;border-color:var(--divider);border-radius:var(--radius-sm);overflow:hidden}\n.rs-response-markdown-diagram-viewport{max-height:480px;overflow:auto;padding:1rem;outline-width:0;outline-style:solid;outline-color:var(--text);outline-offset:-2px}\n.rs-response-markdown-diagram-viewport:focus-visible{outline-width:2px}\n.rs-response-markdown-diagram-art{min-width:0;width:100%}\n.rs-response-markdown-diagram-header{display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem;padding:0.25rem 0.75rem;min-height:44px;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--divider)}\n.rs-response-markdown-diagram-title{flex:1 1 auto;font-size:0.8125rem;color:var(--text-secondary)}\n.rs-response-markdown-diagram-action{width:44px;min-width:44px;max-width:44px;height:44px;padding:0;flex-shrink:0}\n.rs-response-markdown-diagram-icon{display:block;width:16px;height:16px}\n.rs-response-markdown-summary{cursor:pointer;min-height:44px;box-sizing:border-box;padding:0.75rem;color:var(--text-secondary);font-size:0.8125rem;outline-width:0;outline-style:solid;outline-color:var(--text);outline-offset:-2px}\n.rs-response-markdown-summary:focus-visible{outline-width:2px}\n@media (hover: hover) and (pointer: fine){.rs-response-markdown-summary:hover{color:var(--text)}}\n.rs-response-markdown-feedback{padding:0.75rem;margin:0;font-size:0.875rem;color:var(--text-secondary)}\n",
      "type": "registry:file",
      "target": "styles/vlak/response-markdown.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "ai",
      "classes": [
        "rs-response-markdown-paragraph",
        "rs-response-markdown-strong",
        "rs-response-markdown-list",
        "rs-response-markdown-item",
        "rs-response-markdown-quote",
        "rs-response-markdown-inline",
        "rs-response-markdown-link",
        "rs-response-markdown-image",
        "rs-response-markdown-image-description",
        "rs-response-markdown-table-region",
        "rs-response-markdown-table",
        "rs-response-markdown-th",
        "rs-response-markdown-td",
        "rs-response-markdown-rule",
        "rs-response-markdown-diagram",
        "rs-response-markdown-diagram-viewport",
        "rs-response-markdown-diagram-art",
        "rs-response-markdown-diagram-header",
        "rs-response-markdown-diagram-title",
        "rs-response-markdown-diagram-action",
        "rs-response-markdown-diagram-icon",
        "rs-response-markdown-summary",
        "rs-response-markdown-feedback",
        "rs-response-markdown",
        "rs-response-markdown-h1",
        "rs-response-markdown-h2",
        "rs-response-markdown-h3",
        "rs-response-markdown-h4",
        "rs-response-markdown-h5",
        "rs-response-markdown-h6"
      ],
      "snippet": "<div class=\"rs-response-markdown\"><p class=\"rs-response-markdown-paragraph\">Give each decision a <strong class=\"rs-response-markdown-strong\">clear owner</strong> and a next step.</p></div>",
      "cssOnly": false,
      "registryDependencies": [
        "highlighted-code",
        "button",
        "vlak-lib"
      ],
      "reactImport": "@noorddev/vlak-react/components/response-markdown",
      "dependencies": [
        "streamdown@^2.6.0",
        "shiki@^3.19.0",
        "@streamdown/math@^1.0.2",
        "@streamdown/cjk@^1.0.3",
        "mermaid@^11.12.2",
        "katex@^0.16.27"
      ],
      "styles": [
        "katex/dist/katex.min.css"
      ],
      "aliases": [
        "AI Elements MessageResponse",
        "Streamdown",
        "Markdown response",
        "Streaming markdown",
        "Math response",
        "Mermaid response"
      ],
      "example": "import { Response } from \"@noorddev/vlak-react\";\nimport { ResponseMarkdown } from \"@noorddev/vlak-react/components/response-markdown\";\nimport \"katex/dist/katex.min.css\";\n\n<Response status=\"streaming\">\n  <ResponseMarkdown streaming>{\"Give each decision a **clear owner**.\"}</ResponseMarkdown>\n</Response>",
      "usage": {
        "use": [
          "Pass the current response text and streaming=true while receiving it. Completed content uses static parsing.",
          "Use the optional renderer subpath and install its documented dependencies. It needs no model vendor or Tailwind runtime.",
          "Import the documented KaTeX stylesheet to render equations and local math fonts correctly.",
          "Set imageOrigins to exact permitted origins when response images should load. Otherwise image descriptions remain readable.",
          "Use trusted components overrides for application-specific rendering. Native root attributes, styles, and refs are preserved."
        ],
        "avoid": [
          "Passing model-authored markup through a custom component that injects it as trusted content.",
          "Assuming the renderer starts a model request, persists a message, or executes displayed code.",
          "Importing renderer code into the root component package when a lightweight application does not use it."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Reaches links, scrollable tables and code, diagram controls, and the native source disclosure."
        },
        {
          "keys": "Enter, Space",
          "does": "Activates the focused code or diagram button; the native source summary expands or collapses."
        }
      ],
      "a11y": [
        "Changing response text stays outside a live region. Compose with Response for short response status announcements.",
        "Headings, lists, tables, readable code, and math retain semantic markup. Tables and code are keyboard-scrollable.",
        "Raw markup is disabled and sanitized. Links allow web and mail protocols; other protocols and incomplete links remain inert text.",
        "Math uses KaTeX with its default trust restrictions. Diagrams load only after a complete fence and use strict rendering with source fallback.",
        "Diagram zoom controls have descriptive names, 44px targets, and visible focus. Invalid or oversized diagrams preserve their source."
      ]
    }
  }
}
