{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "type": "registry:component",
  "title": "Code Block",
  "description": "A code viewer with file tabs, hand-rolled syntax colour, click-to-highlight lines, a diff view that works out the +/− itself, wrap and copy, and long files that fold behind a fade.",
  "categories": [
    "component",
    "Dev Tools"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://coss.com/ui/r/toggle.json",
    "https://coss.com/ui/r/tooltip.json"
  ],
  "files": [
    {
      "path": "registry/devl/code-block.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport {\n  CheckIcon,\n  ChevronDownIcon,\n  CopyIcon,\n  FileBracesIcon,\n  FileCodeIcon,\n  FileIcon,\n  FileTerminalIcon,\n  GitCompareArrowsIcon,\n  type LucideIcon,\n  WrapTextIcon,\n} from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { type KeyboardEvent, type ReactNode, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { Toggle } from \"@/components/ui/toggle\";\nimport { Tooltip, TooltipPopup, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\nimport { cn } from \"@/lib/utils\";\n\nexport type CodeLanguage = \"ts\" | \"tsx\" | \"js\" | \"jsx\" | \"json\" | \"bash\" | \"text\";\n\nexport interface CodeFile {\n  name: string;\n  code: string;\n  language?: CodeLanguage;\n  previous?: string;\n  highlight?: (number | [number, number])[];\n}\n\nexport interface CodeBlockProps {\n  files: CodeFile[];\n  defaultFile?: string;\n  defaultDiff?: boolean;\n  defaultWrap?: boolean;\n  maxLines?: number;\n  className?: string;\n}\n\nconst LINE = 24;\nconst PAD = 12;\nconst spring = { type: \"spring\", stiffness: 520, damping: 42, mass: 0.8 } as const;\n\ntype Tone = \"plain\" | \"keyword\" | \"string\" | \"number\" | \"literal\" | \"comment\" | \"fn\" | \"type\" | \"tag\" | \"attr\" | \"punct\" | \"prop\" | \"muted\";\n\nconst toneClass: Record<Tone, string> = {\n  plain: \"\",\n  keyword: \"text-violet-700 dark:text-violet-400\",\n  string: \"text-emerald-700 dark:text-emerald-400\",\n  number: \"text-amber-700 dark:text-amber-300\",\n  literal: \"text-rose-700 dark:text-rose-400\",\n  comment: \"text-muted-foreground italic\",\n  fn: \"text-sky-700 dark:text-sky-400\",\n  type: \"text-orange-700 dark:text-orange-300\",\n  tag: \"text-sky-700 dark:text-sky-400\",\n  attr: \"text-cyan-700 dark:text-cyan-300\",\n  punct: \"text-muted-foreground\",\n  prop: \"text-sky-800 dark:text-sky-300\",\n  muted: \"text-muted-foreground\",\n};\n\ntype Token = { text: string; tone: Tone };\n\nconst KEYWORDS = new Set(\n  \"import export from default function return const let var if else for while do switch case break continue new try catch finally throw await async class extends interface type enum implements public private protected readonly static as in of typeof instanceof void yield this super satisfies keyof declare\".split(\n    \" \",\n  ),\n);\nconst LITERALS = new Set([\"true\", \"false\", \"null\", \"undefined\", \"NaN\"]);\n\nfunction tokenizeScript(line: string, state: { inComment: boolean }): Token[] {\n  const out: Token[] = [];\n  let i = 0;\n  let inTag = false;\n  const push = (text: string, tone: Tone) => {\n    if (text) out.push({ text, tone });\n  };\n  if (state.inComment) {\n    const end = line.indexOf(\"*/\");\n    if (end === -1) return [{ text: line, tone: \"comment\" }];\n    push(line.slice(0, end + 2), \"comment\");\n    i = end + 2;\n    state.inComment = false;\n  }\n  while (i < line.length) {\n    const rest = line.slice(i);\n    let m: RegExpMatchArray | null;\n    if (rest.startsWith(\"//\")) {\n      push(rest, \"comment\");\n      break;\n    }\n    if (rest.startsWith(\"/*\")) {\n      const end = rest.indexOf(\"*/\", 2);\n      if (end === -1) {\n        push(rest, \"comment\");\n        state.inComment = true;\n        break;\n      }\n      push(rest.slice(0, end + 2), \"comment\");\n      i += end + 2;\n      continue;\n    }\n    if ((m = rest.match(/^([\"'`])(?:\\\\.|(?!\\1).)*\\1?/))) {\n      push(m[0], \"string\");\n      i += m[0].length;\n      continue;\n    }\n    if ((m = rest.match(/^<\\/?([A-Za-z][\\w.]*)/)) && !/[\\w)\\]]\\s*$/.test(line.slice(0, i))) {\n      const lead = m[0].startsWith(\"</\") ? \"</\" : \"<\";\n      push(lead, \"punct\");\n      push(m[1], /^[A-Z]/.test(m[1]) ? \"type\" : \"tag\");\n      i += m[0].length;\n      inTag = true;\n      continue;\n    }\n    if ((m = rest.match(/^\\d[\\d_]*(?:\\.\\d+)?(?:e[+-]?\\d+)?n?/i))) {\n      push(m[0], \"number\");\n      i += m[0].length;\n      continue;\n    }\n    if ((m = rest.match(/^[A-Za-z_$][\\w$]*/))) {\n      const word = m[0];\n      const after = line.slice(i + word.length);\n      const before = line.slice(0, i);\n      let tone: Tone = \"plain\";\n      if (LITERALS.has(word)) tone = \"literal\";\n      else if (KEYWORDS.has(word) && !before.endsWith(\".\")) tone = \"keyword\";\n      else if (inTag && /^=(?!=)/.test(after)) tone = \"attr\";\n      else if (/^\\s*\\(/.test(after) || /^\\s*=\\s*(async\\s*)?\\(/.test(after)) tone = \"fn\";\n      else if (/^[A-Z]/.test(word)) tone = \"type\";\n      else if (before.endsWith(\".\")) tone = \"prop\";\n      else if (/^\\??:(?!:)/.test(after) && /[{,]\\s*$|^\\s*$/.test(before)) tone = \"prop\";\n      push(word, tone);\n      i += word.length;\n      continue;\n    }\n    const ch = line[i];\n    if (ch === \">\" || (ch === \"/\" && line[i + 1] === \">\")) inTag = false;\n    push(ch, /[\\s]/.test(ch) ? \"plain\" : \"punct\");\n    i += 1;\n  }\n  return out;\n}\n\nfunction tokenizeJson(line: string): Token[] {\n  const out: Token[] = [];\n  const re = /(\"(?:\\\\.|[^\"\\\\])*\")(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?|[{}[\\],]/gi;\n  let last = 0;\n  for (const m of line.matchAll(re)) {\n    const at = m.index ?? 0;\n    if (at > last) out.push({ text: line.slice(last, at), tone: \"plain\" });\n    if (m[1]) {\n      out.push({ text: m[1], tone: m[2] ? \"prop\" : \"string\" });\n      if (m[2]) out.push({ text: m[2], tone: \"punct\" });\n    } else if (m[3]) out.push({ text: m[0], tone: \"literal\" });\n    else if (/[{}[\\],]/.test(m[0])) out.push({ text: m[0], tone: \"punct\" });\n    else out.push({ text: m[0], tone: \"number\" });\n    last = at + m[0].length;\n  }\n  if (last < line.length) out.push({ text: line.slice(last), tone: \"plain\" });\n  return out;\n}\n\nfunction tokenizeBash(line: string): Token[] {\n  const out: Token[] = [];\n  const re = /(#.*$)|(\"(?:\\\\.|[^\"\\\\])*\"|'[^']*')|(\\$\\{?\\w+\\}?)|(\\s--?[\\w-]+)|(&&|\\|\\||[|;>])|(\\S+)|(\\s+)/g;\n  let expectCommand = true;\n  for (const m of line.matchAll(re)) {\n    const [text, comment, str, variable, flag, op, word] = m;\n    if (comment && (m.index === 0 || /\\s/.test(line[(m.index ?? 1) - 1]))) out.push({ text, tone: \"comment\" });\n    else if (str) out.push({ text, tone: \"string\" });\n    else if (variable) out.push({ text, tone: \"attr\" });\n    else if (flag) out.push({ text, tone: \"muted\" });\n    else if (op) {\n      out.push({ text, tone: \"punct\" });\n      expectCommand = true;\n      continue;\n    } else if (word) {\n      if (expectCommand && /^\\w+=/.test(word)) out.push({ text, tone: \"attr\" });\n      else if (expectCommand) {\n        out.push({ text, tone: \"fn\" });\n        expectCommand = false;\n      } else out.push({ text, tone: /^\\d+$/.test(word) ? \"number\" : \"plain\" });\n    } else out.push({ text, tone: \"plain\" });\n  }\n  return out;\n}\n\nfunction tokenize(code: string, lang: CodeLanguage): Token[][] {\n  const lines = code.split(\"\\n\");\n  const state = { inComment: false };\n  return lines.map((line) => {\n    if (lang === \"json\") return tokenizeJson(line);\n    if (lang === \"bash\") return tokenizeBash(line);\n    if (lang === \"text\") return [{ text: line, tone: \"plain\" as Tone }];\n    return tokenizeScript(line, state);\n  });\n}\n\nfunction languageOf(file: CodeFile): CodeLanguage {\n  if (file.language) return file.language;\n  const ext = file.name.split(\".\").pop()?.toLowerCase() ?? \"\";\n  if ([\"ts\", \"mts\", \"cts\"].includes(ext)) return \"ts\";\n  if (ext === \"tsx\") return \"tsx\";\n  if ([\"js\", \"mjs\", \"cjs\"].includes(ext)) return \"js\";\n  if (ext === \"jsx\") return \"jsx\";\n  if (ext === \"json\") return \"json\";\n  if ([\"sh\", \"bash\", \"zsh\"].includes(ext) || file.name.startsWith(\".env\")) return \"bash\";\n  return \"text\";\n}\n\nfunction iconOf(lang: CodeLanguage): { Icon: LucideIcon; tone: string } {\n  if (lang === \"json\") return { Icon: FileBracesIcon, tone: \"text-amber-600 dark:text-amber-400\" };\n  if (lang === \"bash\") return { Icon: FileTerminalIcon, tone: \"text-muted-foreground\" };\n  if (lang === \"text\") return { Icon: FileIcon, tone: \"text-muted-foreground\" };\n  return { Icon: FileCodeIcon, tone: \"text-sky-600 dark:text-sky-400\" };\n}\n\ntype DiffKind = \"same\" | \"add\" | \"del\";\ninterface Row {\n  kind: DiffKind;\n  tokens: Token[];\n  n?: number;\n}\n\nfunction diffLines(before: string[], after: string[]): { kind: DiffKind; from: \"a\" | \"b\"; i: number }[] {\n  const a = before.length;\n  const b = after.length;\n  const lcs = Array.from({ length: a + 1 }, () => new Uint16Array(b + 1));\n  for (let i = a - 1; i >= 0; i--) {\n    for (let j = b - 1; j >= 0; j--) {\n      lcs[i][j] = before[i] === after[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);\n    }\n  }\n  const out: { kind: DiffKind; from: \"a\" | \"b\"; i: number }[] = [];\n  let i = 0;\n  let j = 0;\n  while (i < a || j < b) {\n    if (i < a && j < b && before[i] === after[j]) {\n      out.push({ kind: \"same\", from: \"b\", i: j });\n      i++;\n      j++;\n    } else if (i < a && (j >= b || lcs[i + 1][j] >= lcs[i][j + 1])) {\n      out.push({ kind: \"del\", from: \"a\", i });\n      i++;\n    } else {\n      out.push({ kind: \"add\", from: \"b\", i: j });\n      j++;\n    }\n  }\n  return out;\n}\n\nfunction expandHighlight(spec: CodeFile[\"highlight\"]): Set<number> {\n  const set = new Set<number>();\n  for (const s of spec ?? []) {\n    if (typeof s === \"number\") set.add(s);\n    else for (let n = Math.min(...s); n <= Math.max(...s); n++) set.add(n);\n  }\n  return set;\n}\n\nfunction IconTip({ label, children }: { label: string; children: ReactNode }) {\n  return (\n    <Tooltip>\n      <TooltipTrigger render={children as React.ReactElement} />\n      <TooltipPopup>{label}</TooltipPopup>\n    </Tooltip>\n  );\n}\n\nfunction CopyButton({ text }: { text: string }) {\n  const [copied, setCopied] = useState(false);\n  const reduce = useReducedMotion();\n  useEffect(() => {\n    if (!copied) return;\n    const t = setTimeout(() => setCopied(false), 1500);\n    return () => clearTimeout(t);\n  }, [copied]);\n  return (\n    <IconTip label={copied ? \"Copied\" : \"Copy code\"}>\n      <button\n        aria-label={copied ? \"Copied\" : \"Copy code\"}\n        className=\"relative grid size-8 shrink-0 place-items-center rounded-lg text-muted-foreground outline-none transition-[color,background-color,scale] duration-150 focus-visible:ring-2 focus-visible:ring-ring active:scale-[0.97] [@media(hover:hover)]:hover:bg-accent [@media(hover:hover)]:hover:text-foreground\"\n        onClick={async () => {\n          try {\n            await navigator.clipboard.writeText(text);\n          } catch {}\n          setCopied(true);\n        }}\n        type=\"button\"\n      >\n        <AnimatePresence initial={false} mode=\"popLayout\">\n          <motion.span\n            animate={{ opacity: 1, scale: 1, filter: \"blur(0px)\" }}\n            className={cn(\"grid place-items-center\", copied && \"text-success-foreground\")}\n            exit={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6, filter: \"blur(2px)\" }}\n            initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6, filter: \"blur(2px)\" }}\n            key={copied ? \"done\" : \"copy\"}\n            transition={{ duration: 0.16, ease: [0.23, 1, 0.32, 1] }}\n          >\n            {copied ? <CheckIcon className=\"size-4\" /> : <CopyIcon className=\"size-4\" />}\n          </motion.span>\n        </AnimatePresence>\n      </button>\n    </IconTip>\n  );\n}\n\nexport function CodeBlock({\n  files,\n  defaultFile,\n  defaultDiff = false,\n  defaultWrap = false,\n  maxLines = 14,\n  className,\n}: CodeBlockProps) {\n  const reduce = useReducedMotion();\n  const uid = useId();\n  const [active, setActive] = useState(() => Math.max(0, files.findIndex((f) => f.name === defaultFile)));\n  const [diff, setDiff] = useState(defaultDiff);\n  const [wrap, setWrap] = useState(defaultWrap);\n  const [expanded, setExpanded] = useState(false);\n  const [marks, setMarks] = useState<Record<string, Set<number>>>(() =>\n    Object.fromEntries(files.map((f) => [f.name, expandHighlight(f.highlight)])),\n  );\n  const anchor = useRef<number | null>(null);\n  const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const [fullHeight, setFullHeight] = useState<number | null>(null);\n\n  const file = files[active] ?? files[0];\n  const lang = languageOf(file);\n  const canDiff = file.previous !== undefined;\n  const showDiff = diff && canDiff;\n  const highlighted = marks[file.name] ?? new Set<number>();\n\n  const rows: Row[] = useMemo(() => {\n    const now = tokenize(file.code, lang);\n    if (!showDiff) return now.map((tokens, i) => ({ kind: \"same\", tokens, n: i + 1 }));\n    const was = tokenize(file.previous ?? \"\", lang);\n    return diffLines((file.previous ?? \"\").split(\"\\n\"), file.code.split(\"\\n\")).map((d) =>\n      d.from === \"a\" ? { kind: d.kind, tokens: was[d.i] } : { kind: d.kind, tokens: now[d.i], n: d.i + 1 },\n    );\n  }, [file, lang, showDiff]);\n\n  const stats = useMemo(() => {\n    if (!canDiff) return null;\n    const d = diffLines((file.previous ?? \"\").split(\"\\n\"), file.code.split(\"\\n\"));\n    return { add: d.filter((x) => x.kind === \"add\").length, del: d.filter((x) => x.kind === \"del\").length };\n  }, [file, canDiff]);\n\n  const collapsible = maxLines > 0 && rows.length > maxLines + 2;\n  const collapsedHeight = maxLines * LINE + PAD;\n\n  useLayoutEffect(() => {\n    const el = bodyRef.current;\n    if (!el) return;\n    const measure = () => setFullHeight(el.scrollHeight);\n    measure();\n    const ro = new ResizeObserver(measure);\n    ro.observe(el);\n    return () => ro.disconnect();\n  }, []);\n\n  const toggleMark = (n: number, extend: boolean) => {\n    setMarks((prev) => {\n      const next = new Set(prev[file.name]);\n      if (extend && anchor.current !== null) {\n        for (let k = Math.min(anchor.current, n); k <= Math.max(anchor.current, n); k++) next.add(k);\n      } else if (next.has(n)) next.delete(n);\n      else next.add(n);\n      return { ...prev, [file.name]: next };\n    });\n    anchor.current = n;\n  };\n\n  const selectTab = (i: number) => {\n    setActive(i);\n    setExpanded(false);\n    anchor.current = null;\n  };\n\n  const onTabKey = (e: KeyboardEvent) => {\n    const step = e.key === \"ArrowRight\" ? 1 : e.key === \"ArrowLeft\" ? -1 : 0;\n    if (!step) return;\n    e.preventDefault();\n    const next = (active + step + files.length) % files.length;\n    selectTab(next);\n    tabRefs.current[next]?.focus();\n  };\n\n  const isCollapsed = collapsible && !expanded;\n  const height = isCollapsed ? collapsedHeight : (fullHeight ?? undefined);\n\n  return (\n    <TooltipProvider delay={500}>\n      <div className={cn(\"w-full min-w-0 max-w-[560px] overflow-hidden rounded-2xl border bg-card text-sm shadow-xs/5\", className)}>\n        <div className=\"flex h-11 items-center gap-1 border-b ps-1.5 pe-1.5\">\n          <div\n            aria-label=\"Files\"\n            className=\"-my-1 flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto py-1 [mask-image:linear-gradient(to_right,black_calc(100%-20px),transparent)] [scrollbar-width:none]\"\n            onKeyDown={onTabKey}\n            role=\"tablist\"\n          >\n            {files.map((f, i) => {\n              const { Icon, tone } = iconOf(languageOf(f));\n              const selected = i === active;\n              return (\n                <button\n                  aria-controls={`${uid}-panel`}\n                  aria-selected={selected}\n                  className={cn(\n                    \"relative flex h-8 shrink-0 items-center gap-1.5 rounded-lg px-2.5 font-mono text-xs outline-none transition-colors duration-150 focus-visible:ring-2 focus-visible:ring-ring\",\n                    selected ? \"text-foreground\" : \"text-muted-foreground [@media(hover:hover)]:hover:text-foreground\",\n                  )}\n                  key={f.name}\n                  onClick={() => selectTab(i)}\n                  ref={(el) => {\n                    tabRefs.current[i] = el;\n                  }}\n                  role=\"tab\"\n                  tabIndex={selected ? 0 : -1}\n                  type=\"button\"\n                >\n                  {selected && (\n                    <motion.span\n                      className=\"absolute inset-0 -z-0 rounded-lg bg-muted\"\n                      layoutId={`${uid}-tab`}\n                      transition={reduce ? { duration: 0 } : spring}\n                    />\n                  )}\n                  <Icon className={cn(\"relative size-3.5\", tone)} />\n                  <span className=\"relative\">{f.name}</span>\n                </button>\n              );\n            })}\n          </div>\n\n          {canDiff && stats && (\n            <IconTip label={showDiff ? \"Show file\" : \"Show changes\"}>\n              <Toggle\n                aria-label=\"Diff view\"\n                className=\"h-8 gap-1.5 rounded-lg px-2 font-mono text-xs tabular-nums\"\n                onPressedChange={setDiff}\n                pressed={showDiff}\n              >\n                <GitCompareArrowsIcon className=\"size-3.5\" />\n                <span className=\"text-success-foreground\">+{stats.add}</span>\n                <span className=\"text-destructive-foreground\">−{stats.del}</span>\n              </Toggle>\n            </IconTip>\n          )}\n          <IconTip label={wrap ? \"Don’t wrap lines\" : \"Wrap lines\"}>\n            <Toggle aria-label=\"Wrap lines\" className=\"size-8 min-w-8 rounded-lg p-0\" onPressedChange={setWrap} pressed={wrap}>\n              <WrapTextIcon className=\"size-4\" />\n            </Toggle>\n          </IconTip>\n          <CopyButton text={file.code} />\n        </div>\n\n        <motion.div\n          animate={{ height }}\n          className=\"relative overflow-hidden bg-code\"\n          id={`${uid}-panel`}\n          initial={false}\n          role=\"tabpanel\"\n          transition={reduce ? { duration: 0 } : { type: \"spring\", stiffness: 380, damping: 40 }}\n        >\n          <div ref={bodyRef}>\n            <div className={cn(\"overflow-x-auto\", isCollapsed && \"overflow-x-hidden\")}>\n              <pre\n                aria-label={`${file.name}${showDiff ? \" changes\" : \"\"}`}\n                className={cn(\"py-1.5 font-mono text-[12.5px] leading-6\", wrap ? \"w-full\" : \"w-max min-w-full\")}\n              >\n                <code className=\"block\">\n                  {rows.map((row, idx) => {\n                    const marked = row.n !== undefined && !showDiff && highlighted.has(row.n);\n                    return (\n                      <div\n                        className={cn(\n                          \"group/line flex\",\n                          row.kind === \"add\" && \"bg-success/10\",\n                          row.kind === \"del\" && \"bg-destructive/10\",\n                          marked && \"bg-info/10\",\n                        )}\n                        key={`${file.name}-${showDiff}-${idx}`}\n                      >\n                        <span\n                          aria-hidden\n                          className={cn(\n                            \"sticky left-0 w-11 shrink-0 cursor-pointer select-none pe-3 text-end text-muted-foreground tabular-nums transition-colors [@media(hover:hover)]:hover:text-foreground\",\n                            marked && \"text-info-foreground shadow-[inset_2px_0_0_var(--color-info)]\",\n                            !wrap && \"bg-code\",\n                            !wrap && row.kind === \"add\" && \"bg-[color-mix(in_srgb,var(--color-success)_10%,var(--color-code))]\",\n                            !wrap && row.kind === \"del\" && \"bg-[color-mix(in_srgb,var(--color-destructive)_10%,var(--color-code))]\",\n                            !wrap && marked && \"bg-[color-mix(in_srgb,var(--color-info)_10%,var(--color-code))]\",\n                          )}\n                          onClick={(e) => row.n !== undefined && !showDiff && toggleMark(row.n, e.shiftKey)}\n                        >\n                          {row.n ?? \"\"}\n                        </span>\n                        {showDiff && (\n                          <span\n                            aria-hidden\n                            className={cn(\n                              \"w-5 shrink-0 select-none text-center\",\n                              row.kind === \"add\" && \"text-success-foreground\",\n                              row.kind === \"del\" && \"text-destructive-foreground\",\n                            )}\n                          >\n                            {row.kind === \"add\" ? \"+\" : row.kind === \"del\" ? \"−\" : \"\"}\n                          </span>\n                        )}\n                        {showDiff && row.kind !== \"same\" && (\n                          <span className=\"sr-only\">{row.kind === \"add\" ? \"added: \" : \"removed: \"}</span>\n                        )}\n                        <span className={cn(\"min-w-0 flex-1 pe-4\", wrap ? \"whitespace-pre-wrap break-words\" : \"whitespace-pre\")}>\n                          {row.tokens.length === 0 || (row.tokens.length === 1 && row.tokens[0].text === \"\") ? (\n                            \" \"\n                          ) : (\n                            row.tokens.map((t, k) => (\n                              <span className={toneClass[t.tone]} key={k}>\n                                {t.text}\n                              </span>\n                            ))\n                          )}\n                        </span>\n                      </div>\n                    );\n                  })}\n                </code>\n              </pre>\n            </div>\n          </div>\n\n          <AnimatePresence>\n            {isCollapsed && (\n              <motion.div\n                animate={{ opacity: 1 }}\n                className=\"pointer-events-none absolute inset-x-0 bottom-0 h-20 bg-linear-to-t from-code via-code/80 to-transparent\"\n                exit={{ opacity: 0 }}\n                initial={{ opacity: 0 }}\n                transition={{ duration: 0.18 }}\n              />\n            )}\n          </AnimatePresence>\n        </motion.div>\n\n        {collapsible && (\n          <button\n            aria-expanded={expanded}\n            className=\"flex h-9 w-full items-center justify-center gap-1.5 border-t text-muted-foreground text-xs outline-none transition-colors duration-150 focus-visible:bg-accent focus-visible:text-foreground [@media(hover:hover)]:hover:bg-accent/50 [@media(hover:hover)]:hover:text-foreground\"\n            onClick={() => setExpanded((v) => !v)}\n            type=\"button\"\n          >\n            {expanded ? \"Collapse\" : `Show all ${rows.length} lines`}\n            <motion.span animate={{ rotate: expanded ? 180 : 0 }} transition={{ duration: 0.2, ease: [0.23, 1, 0.32, 1] }}>\n              <ChevronDownIcon className=\"size-3.5\" />\n            </motion.span>\n          </button>\n        )}\n      </div>\n    </TooltipProvider>\n  );\n}\n"
    }
  ]
}