{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toc",
  "title": "Table of contents",
  "description": "TOCProvider, TOCScrollArea and the TOC items in three styles: the default, with a thumb that follows the headings in view, clerk and block.",
  "dependencies": [
    "cn",
    "@fuma-translate/react@^1.0.2",
    "fumadocs-core@^16.17.8"
  ],
  "registryDependencies": [
    "@docscn/docs-utils"
  ],
  "files": [
    {
      "path": "registry/base/docs/components/toc/index.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport * as Primitive from \"fumadocs-core/toc\";\nimport { type ComponentProps, createContext, use, useRef } from \"react\";\nimport { cn } from \"cn\";\nimport { mergeRefs } from \"../../utils/merge-refs\";\n\nconst TOCContext = createContext<Primitive.TOCItemType[]>([]);\n\nexport function useTOCItems(): Primitive.TOCItemType[] {\n  return use(TOCContext);\n}\n\nexport type TOCProviderProps = Primitive.AnchorProviderProps;\n\nexport const { useActiveAnchor, useActiveAnchors, useItems } = Primitive;\n\nexport function TOCProvider({ toc, children, ...props }: TOCProviderProps) {\n  return (\n    <TOCContext value={toc}>\n      <Primitive.AnchorProvider toc={toc} {...props}>\n        {children}\n      </Primitive.AnchorProvider>\n    </TOCContext>\n  );\n}\n\nexport function TOCScrollArea({\n  ref,\n  className,\n  ...props\n}: ComponentProps<\"div\">) {\n  const viewRef = useRef<HTMLDivElement>(null);\n\n  return (\n    <div\n      ref={mergeRefs(viewRef, ref)}\n      className={cn(\n        \"relative min-h-0 [scrollbar-width:none] overflow-x-clip overflow-y-auto overscroll-contain mask-[linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent)] py-3 text-sm\",\n        className,\n      )}\n      {...props}\n    >\n      <Primitive.ScrollProvider containerRef={viewRef}>\n        {props.children}\n      </Primitive.ScrollProvider>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/toc/index.tsx"
    },
    {
      "path": "registry/base/docs/components/toc/default.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport { useTranslations } from \"@fuma-translate/react\";\nimport * as Primitive from \"fumadocs-core/toc\";\nimport {\n  type ComponentProps,\n  type ReactNode,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { cn } from \"cn\";\nimport { useTOCItems } from \".\";\nimport { mergeRefs } from \"../../utils/merge-refs\";\nimport { findLastIndex } from \"../../utils/array\";\n\ninterface ComputedSVG {\n  width: number;\n  height: number;\n  content: ReactNode;\n  d: string;\n  positions: [top: number, bottom: number, x: number][];\n  itemLineLengths: [top: number, bottom: number][];\n}\n\nexport interface TOCItemsProps extends ComponentProps<\"div\"> {\n  thumbBox?: boolean;\n}\n\nexport function TOCItems({\n  ref,\n  className,\n  thumbBox = true,\n  children,\n  ...props\n}: TOCItemsProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const items = useTOCItems();\n  const [svg, setSvg] = useState<ComputedSVG | null>(null);\n\n  const onPrint = useCallback(() => {\n    const container = containerRef.current;\n    if (!container || container.clientHeight === 0) return;\n    if (items.length === 0) {\n      setSvg(null);\n      return;\n    }\n    let w = 0;\n    let h = 0;\n    let d = \"\";\n    const positions: [top: number, bottom: number, x: number][] = [];\n    const output: ReactNode[] = [];\n\n    for (let i = 0; i < items.length; i++) {\n      const item = items[i]!;\n      const element: HTMLElement | null = container.querySelector(\n        `a[href=\"${item.url}\"]`,\n      );\n      if (!element) continue;\n\n      const styles = getComputedStyle(element);\n      const x = getLineOffset(item.depth) + 0.5;\n      const top = element.offsetTop + parseFloat(styles.paddingTop);\n      const bottom =\n        element.offsetTop +\n        element.clientHeight -\n        parseFloat(styles.paddingBottom);\n\n      w = Math.max(x + 8, w);\n      h = Math.max(h, bottom);\n\n      if (i === 0) {\n        d += ` M${x} ${top} L${x} ${bottom}`;\n      } else {\n        const [, upperBottom, upperX] = positions[i - 1] ?? [0, 0, 0];\n\n        d += ` C ${upperX} ${top - 4} ${x} ${upperBottom + 4} ${x} ${top} L${x} ${bottom}`;\n      }\n\n      if (item._step !== undefined) {\n        output.push(\n          <g key={i} transform={`translate(${x}, ${(top + bottom) / 2})`}>\n            <circle cx=\"0\" cy=\"0\" r=\"8\" className=\"fill-primary\" />\n            <text\n              cx=\"0\"\n              cy=\"0\"\n              textAnchor=\"middle\"\n              alignmentBaseline=\"central\"\n              dominantBaseline=\"middle\"\n              className=\"fill-primary-foreground font-mono text-xs leading-none font-medium rtl:-scale-x-100\"\n            >\n              {item._step}\n            </text>\n          </g>,\n        );\n      }\n\n      positions.push([top, bottom, x]);\n    }\n\n    output.unshift(\n      <path\n        key=\"path\"\n        d={d}\n        className=\"stroke-primary\"\n        strokeWidth=\"1\"\n        fill=\"none\"\n      />,\n    );\n\n    const itemLineLengths: [top: number, bottom: number][] = [];\n\n    if (thumbBox) {\n      const path = document.createElementNS(\n        \"http://www.w3.org/2000/svg\",\n        \"path\",\n      );\n      path.setAttribute(\"d\", d);\n\n      const n = path.getTotalLength();\n      for (let i = 0; i < positions.length; i++) {\n        const [top, bottom] = positions[i]!;\n        let l =\n          i > 0\n            ? itemLineLengths[i - 1]![1] + (top - positions[i - 1]![1])\n            : top;\n        while (l < n && path.getPointAtLength(l).y < top) l++;\n\n        // vertical line distance = bottom - top\n        itemLineLengths.push([l, l + bottom - top]);\n      }\n    }\n\n    setSvg({\n      content: output,\n      width: w,\n      height: h,\n      d,\n      itemLineLengths,\n      positions,\n    });\n  }, [items, thumbBox]);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const observer = new ResizeObserver(onPrint);\n    observer.observe(container);\n    onPrint();\n    return () => {\n      observer.unobserve(container);\n    };\n  }, [onPrint]);\n\n  return (\n    <div\n      ref={mergeRefs(containerRef, ref)}\n      className={cn(\"relative flex flex-col\", className)}\n      {...props}\n    >\n      {svg && <ThumbTrack computed={svg} thumbBox={thumbBox} />}\n      {children}\n    </div>\n  );\n}\n\nexport function TOCEmpty() {\n  const t = useTranslations({ note: \"table of contents\" });\n\n  return (\n    <div className=\"rounded-lg border bg-card p-3 text-xs text-muted-foreground\">\n      {t(\"No Headings\")}\n    </div>\n  );\n}\n\ninterface ThumbBoxInfo {\n  startIdx: number;\n  endIdx: number;\n  isUp: boolean;\n}\n\nfunction ThumbTrack({\n  computed,\n  thumbBox,\n}: {\n  computed: ComputedSVG;\n  thumbBox: boolean;\n}) {\n  const ref = useRef<HTMLDivElement>(null);\n  const previousRef = useRef<ThumbBoxInfo>(null);\n  const tocInfo = Primitive.useTOC();\n\n  function calculate(\n    items: Primitive.TOCItemInfo[],\n    prev: ThumbBoxInfo | null,\n  ): { style: Record<string, string>; info: ThumbBoxInfo | null } {\n    const style: Record<string, string> = {};\n    const startIdx = items.findIndex((item) => item.active);\n    if (startIdx === -1) return { style, info: prev };\n\n    const endIdx = findLastIndex(items, (item) => item.active);\n    style[\"--track-top\"] = `${computed.positions[startIdx]![0]}px`;\n    style[\"--track-bottom\"] = `${computed.positions[endIdx]![1]}px`;\n    if (!thumbBox) return { style, info: prev };\n\n    let isUp = false;\n    if (prev) {\n      isUp =\n        prev.startIdx > startIdx ||\n        prev.endIdx > endIdx ||\n        (prev.startIdx === startIdx && prev.endIdx === endIdx && prev.isUp);\n    }\n\n    style[\"--offset-distance\"] = isUp\n      ? `${computed.itemLineLengths[startIdx]![0]}px`\n      : `${computed.itemLineLengths[endIdx]![1]}px`;\n    style[\"--opacity\"] =\n      items[isUp ? startIdx : endIdx]!.original._step !== undefined ? \"0\" : \"1\";\n\n    return { style, info: { startIdx, endIdx, isUp } };\n  }\n\n  Primitive.useTOCListener((items) => {\n    const element = ref.current;\n    if (!element) return;\n\n    const { style, info } = calculate(items, previousRef.current);\n    previousRef.current = info;\n    for (const [k, v] of Object.entries(style)) {\n      element.style.setProperty(k, v);\n    }\n  });\n\n  return (\n    <div\n      ref={ref}\n      className=\"absolute inset-s-0 top-0 origin-center rtl:-scale-x-100\"\n      style={{\n        width: computed.width,\n        height: computed.height,\n        ...calculate(tocInfo.get(), null).style,\n      }}\n    >\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox={`0 0 ${computed.width} ${computed.height}`}\n        className=\"absolute transition-[clip-path]\"\n        style={{\n          width: computed.width,\n          height: computed.height,\n          clipPath: `polygon(0 var(--track-top,0), 100% var(--track-top,0), 100% var(--track-bottom,0), 0 var(--track-bottom,0))`,\n        }}\n      >\n        {computed.content}\n      </svg>\n      {thumbBox && (\n        <div\n          className=\"absolute left-0 size-1 rounded-full bg-primary opacity-(--opacity,0) transition-[opacity,offset-distance] [offset-distance:var(--offset-distance,0)]\"\n          style={{\n            offsetPath: `path(\"${computed.d}\")`,\n          }}\n        />\n      )}\n    </div>\n  );\n}\n\nconst BASE = 8;\n\nfunction getItemOffset(depth: number): number {\n  if (depth <= 2) return 12 + BASE;\n  if (depth === 3) return 24 + BASE;\n  return 36 + BASE;\n}\n\nfunction getLineOffset(depth: number): number {\n  if (depth <= 2) return BASE;\n  if (depth === 3) return 8 + BASE;\n  return 16 + BASE;\n}\n\nexport function TOCItem({\n  item,\n  ...props\n}: Primitive.TOCItemProps & { item: Primitive.TOCItemType }) {\n  const items = useTOCItems();\n  const { isFirst, isLast, svg } = useMemo(() => {\n    const index = items.indexOf(item);\n    const isFirst = index === 0;\n    const isLast = index === items.length - 1;\n\n    const l1 = getLineOffset(item.depth);\n    const l0 = isFirst ? l1 : getLineOffset(items[index - 1]!.depth);\n    const l2 = isLast ? l1 : getLineOffset(items[index + 1]!.depth);\n\n    return {\n      isFirst,\n      isLast,\n      svg: (\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          className={cn(\n            \"absolute inset-s-0 -top-1.5 bottom-0 -z-1 h-[calc(100%+--spacing(1.5))] rtl:-scale-x-100\",\n            l1 !== l2 && \"bottom-1.5 h-full\",\n          )}\n          style={{\n            width: Math.max(l0, l1) + 9,\n          }}\n        >\n          {l0 !== l1 && (\n            <path\n              d={`M ${l0 + 0.5} 0 C ${l0 + 0.5} 8 ${l1 + 0.5} 4 ${l1 + 0.5} 12`}\n              stroke=\"black\"\n              strokeWidth=\"1\"\n              fill=\"none\"\n              className=\"stroke-foreground/10\"\n            />\n          )}\n          <line\n            x1={l1 + 0.5}\n            y1={l0 === l1 ? \"6\" : \"12\"}\n            x2={l1 + 0.5}\n            y2=\"100%\"\n            strokeWidth=\"1\"\n            className=\"stroke-foreground/10\"\n          />\n          {item._step !== undefined && (\n            <g transform={`translate(${l1 + 0.5}, ${l1 === l2 ? \"3\" : \"6\"})`}>\n              <circle cx=\"0\" cy=\"50%\" r=\"8\" className=\"fill-muted\" />\n              <text\n                x=\"0\"\n                y=\"50%\"\n                textAnchor=\"middle\"\n                alignmentBaseline=\"central\"\n                dominantBaseline=\"middle\"\n                className=\"fill-muted-foreground font-mono text-xs leading-none font-medium rtl:-scale-x-100\"\n              >\n                {item._step}\n              </text>\n            </g>\n          )}\n        </svg>\n      ),\n    };\n  }, [items, item]);\n\n  return (\n    <Primitive.TOCItem\n      href={item.url}\n      {...props}\n      className={cn(\n        \"relative scroll-m-4 py-1.5 text-sm wrap-anywhere text-muted-foreground transition-colors hover:text-foreground data-[active=true]:text-foreground\",\n        isFirst && \"pt-0\",\n        isLast && \"pb-0\",\n        props.className,\n      )}\n      style={{\n        paddingInlineStart: getItemOffset(item.depth),\n        ...props.style,\n      }}\n    >\n      {svg}\n      {item.title}\n    </Primitive.TOCItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/toc/default.tsx"
    },
    {
      "path": "registry/base/docs/components/toc/clerk.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport { useTranslations } from \"@fuma-translate/react\";\nimport * as Primitive from \"fumadocs-core/toc\";\nimport {\n  type ComponentProps,\n  type ReactNode,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { cn } from \"cn\";\nimport { useTOCItems } from \".\";\nimport { mergeRefs } from \"../../utils/merge-refs\";\nimport { findLastIndex } from \"../../utils/array\";\n\ninterface ComputedSVG {\n  width: number;\n  height: number;\n  content: ReactNode;\n  d: string;\n  positions: [top: number, bottom: number, x: number][];\n  itemLineLengths: [top: number, bottom: number][];\n}\n\nexport type TOCItemsProps = ComponentProps<\"div\">;\n\nexport function TOCItems({\n  ref,\n  className,\n  children,\n  ...props\n}: TOCItemsProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const items = useTOCItems();\n  const [svg, setSvg] = useState<ComputedSVG | null>(null);\n\n  const onPrint = useCallback(() => {\n    const container = containerRef.current;\n    if (!container || container.clientHeight === 0) return;\n    if (items.length === 0) {\n      setSvg(null);\n      return;\n    }\n    let w = 0;\n    let h = 0;\n    let d = \"\";\n    const positions: [top: number, bottom: number, x: number][] = [];\n    const output: ReactNode[] = [];\n\n    for (let i = 0; i < items.length; i++) {\n      const item = items[i]!;\n      const element: HTMLElement | null = container.querySelector(\n        `a[href=\"${item.url}\"]`,\n      );\n      if (!element) continue;\n\n      const styles = getComputedStyle(element);\n      const x = getLineOffset(item.depth) + 0.5;\n      const top = element.offsetTop + parseFloat(styles.paddingTop);\n      const bottom =\n        element.offsetTop +\n        element.clientHeight -\n        parseFloat(styles.paddingBottom);\n\n      w = Math.max(x + 8, w);\n      h = Math.max(h, bottom);\n\n      if (i === 0) {\n        d += ` M${x} ${top} L${x} ${bottom}`;\n      } else {\n        const [, upperBottom, upperX] = positions[i - 1] ?? [0, 0, 0];\n\n        d += ` L ${upperX} ${upperBottom} ${x} ${top} L${x} ${bottom}`;\n      }\n\n      if (item._step !== undefined) {\n        output.push(\n          <g key={i} transform={`translate(${x}, ${(top + bottom) / 2})`}>\n            <circle cx=\"0\" cy=\"0\" r=\"8\" className=\"fill-primary\" />\n            <text\n              cx=\"0\"\n              cy=\"0\"\n              textAnchor=\"middle\"\n              alignmentBaseline=\"central\"\n              dominantBaseline=\"middle\"\n              className=\"fill-primary-foreground font-mono text-xs leading-none font-medium rtl:-scale-x-100\"\n            >\n              {item._step}\n            </text>\n          </g>,\n        );\n      }\n\n      positions.push([top, bottom, x]);\n    }\n\n    output.unshift(\n      <path\n        key=\"path\"\n        d={d}\n        className=\"stroke-primary\"\n        strokeWidth=\"1\"\n        fill=\"none\"\n      />,\n    );\n\n    const itemLineLengths: [top: number, bottom: number][] = [];\n\n    setSvg({\n      content: output,\n      width: w,\n      height: h,\n      d,\n      itemLineLengths,\n      positions,\n    });\n  }, [items]);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const observer = new ResizeObserver(onPrint);\n    observer.observe(container);\n    onPrint();\n    return () => {\n      observer.unobserve(container);\n    };\n  }, [onPrint]);\n\n  return (\n    <div\n      ref={mergeRefs(containerRef, ref)}\n      className={cn(\"relative flex flex-col\", className)}\n      {...props}\n    >\n      {svg && <ThumbTrack computed={svg} />}\n      {children}\n    </div>\n  );\n}\n\nexport function TOCEmpty() {\n  const t = useTranslations({ note: \"table of contents\" });\n\n  return (\n    <div className=\"rounded-lg border bg-card p-3 text-xs text-muted-foreground\">\n      {t(\"No Headings\")}\n    </div>\n  );\n}\nfunction ThumbTrack({ computed }: { computed: ComputedSVG }) {\n  const ref = useRef<HTMLDivElement>(null);\n  const tocInfo = Primitive.useTOC();\n\n  function calculate(items: Primitive.TOCItemInfo[]) {\n    const out: Record<string, string> = {};\n    const startIdx = items.findIndex((item) => item.active);\n    if (startIdx === -1) return out;\n\n    const endIdx = findLastIndex(items, (item) => item.active);\n    out[\"--track-top\"] = `${computed.positions[startIdx]![0]}px`;\n    out[\"--track-bottom\"] = `${computed.positions[endIdx]![1]}px`;\n    return out;\n  }\n\n  Primitive.useTOCListener((items) => {\n    const element = ref.current;\n    if (!element) return;\n\n    for (const [k, v] of Object.entries(calculate(items))) {\n      element.style.setProperty(k, v);\n    }\n  });\n\n  return (\n    <div\n      ref={ref}\n      className=\"absolute inset-s-0 top-0 origin-center rtl:-scale-x-100\"\n      style={{\n        width: computed.width,\n        height: computed.height,\n        ...calculate(tocInfo.get()),\n      }}\n    >\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox={`0 0 ${computed.width} ${computed.height}`}\n        className=\"absolute transition-[clip-path]\"\n        style={{\n          width: computed.width,\n          height: computed.height,\n          clipPath: `polygon(0 var(--track-top,0), 100% var(--track-top,0), 100% var(--track-bottom,0), 0 var(--track-bottom,0))`,\n        }}\n      >\n        {computed.content}\n      </svg>\n    </div>\n  );\n}\n\nconst BASE = 8;\n\nfunction getItemOffset(depth: number): number {\n  if (depth <= 2) return 12 + BASE;\n  if (depth === 3) return 24 + BASE;\n  return 36 + BASE;\n}\n\nfunction getLineOffset(depth: number): number {\n  if (depth <= 2) return BASE;\n  if (depth === 3) return 12 + BASE;\n  return 24 + BASE;\n}\n\nexport function TOCItem({\n  item,\n  ...props\n}: Primitive.TOCItemProps & { item: Primitive.TOCItemType }) {\n  const items = useTOCItems();\n  const { isFirst, isLast, svg } = useMemo(() => {\n    const index = items.indexOf(item);\n    const isFirst = index === 0;\n    const isLast = index === items.length - 1;\n\n    const l1 = getLineOffset(item.depth);\n    const l0 = isFirst ? l1 : getLineOffset(items[index - 1]!.depth);\n    const l2 = isLast ? l1 : getLineOffset(items[index + 1]!.depth);\n\n    return {\n      isFirst,\n      isLast,\n      svg: (\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          className={cn(\n            \"absolute inset-s-0 -top-1.5 bottom-0 -z-1 h-[calc(100%+--spacing(1.5))] rtl:-scale-x-100\",\n            l1 !== l2 && \"bottom-1.5 h-full\",\n          )}\n          style={{\n            width: Math.max(l0, l1) + 9,\n          }}\n        >\n          {l0 !== l1 && (\n            <path\n              d={`M ${l0 + 0.5} 0 L ${l0 + 0.5} 0 ${l1 + 0.5} 12`}\n              stroke=\"black\"\n              strokeWidth=\"1\"\n              fill=\"none\"\n              className=\"stroke-foreground/10\"\n            />\n          )}\n          <line\n            x1={l1 + 0.5}\n            y1={l0 === l1 ? \"6\" : \"12\"}\n            x2={l1 + 0.5}\n            y2=\"100%\"\n            strokeWidth=\"1\"\n            className=\"stroke-foreground/10\"\n          />\n          {item._step !== undefined && (\n            <g transform={`translate(${l1 + 0.5}, ${l1 === l2 ? \"3\" : \"6\"})`}>\n              <circle cx=\"0\" cy=\"50%\" r=\"8\" className=\"fill-muted\" />\n              <text\n                x=\"0\"\n                y=\"50%\"\n                textAnchor=\"middle\"\n                alignmentBaseline=\"central\"\n                dominantBaseline=\"middle\"\n                className=\"fill-muted-foreground font-mono text-xs leading-none font-medium rtl:-scale-x-100\"\n              >\n                {item._step}\n              </text>\n            </g>\n          )}\n        </svg>\n      ),\n    };\n  }, [items, item]);\n\n  return (\n    <Primitive.TOCItem\n      href={item.url}\n      {...props}\n      className={cn(\n        \"relative scroll-m-4 py-1.5 text-sm wrap-anywhere text-muted-foreground transition-colors hover:text-foreground data-[active=true]:text-foreground\",\n        isFirst && \"pt-0\",\n        isLast && \"pb-0\",\n        props.className,\n      )}\n      style={{\n        paddingInlineStart: getItemOffset(item.depth),\n        ...props.style,\n      }}\n    >\n      {svg}\n      {item.title}\n    </Primitive.TOCItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/toc/clerk.tsx"
    },
    {
      "path": "registry/base/docs/components/toc/block.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport * as Primitive from \"fumadocs-core/toc\";\nimport { type ComponentProps, useEffect, useRef } from \"react\";\nimport { cn } from \"cn\";\nimport { mergeRefs } from \"../../utils/merge-refs\";\n\nexport { TOCEmpty } from \"./default\";\n\nexport type TOCItemsProps = ComponentProps<\"div\">;\n\nexport function TOCItems({\n  ref,\n  className,\n  children,\n  ...props\n}: TOCItemsProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const blockRef = useRef<HTMLDivElement>(null);\n  const tocInfo = Primitive.useTOC();\n\n  useEffect(() => {\n    const container = containerRef.current;\n    const block = blockRef.current;\n    if (!container || !block) return;\n    // measured on resize only, so active changes never read layout\n    const positions = new Map<string, [top: number, bottom: number]>();\n    let height = 0;\n\n    const update = (items: Primitive.TOCItemInfo[]) => {\n      let top = -1;\n      let bottom = -1;\n      for (const item of items) {\n        if (!item.active) continue;\n        const pos = positions.get(item.original.url);\n        if (!pos) continue;\n        if (top === -1) top = pos[0];\n        bottom = pos[1];\n      }\n\n      if (top === -1) return;\n      block.style.clipPath = `inset(${top}px 0 ${height - bottom}px round 8px)`;\n      // place the block instantly at first, only animate the changes after\n      if (!block.hasAttribute(\"data-ready\"))\n        requestAnimationFrame(() => block.setAttribute(\"data-ready\", \"\"));\n    };\n\n    const observer = new ResizeObserver(() => {\n      positions.clear();\n      for (const anchor of container.getElementsByTagName(\"a\")) {\n        const href = anchor.getAttribute(\"href\");\n        if (href)\n          positions.set(href, [\n            anchor.offsetTop,\n            anchor.offsetTop + anchor.offsetHeight,\n          ]);\n      }\n      height = container.clientHeight;\n      update(tocInfo.get());\n    });\n\n    observer.observe(container);\n    tocInfo.listen(update);\n    return () => {\n      observer.disconnect();\n      tocInfo.unlisten(update);\n    };\n  }, [tocInfo]);\n\n  return (\n    <div\n      ref={mergeRefs(containerRef, ref)}\n      className={cn(\"relative flex flex-col\", className)}\n      {...props}\n    >\n      <div\n        ref={blockRef}\n        className=\"absolute inset-0 bg-linear-to-r from-primary/10 duration-300 ease-out [clip-path:inset(0_0_100%)] data-ready:transition-[clip-path]\"\n      />\n      {children}\n    </div>\n  );\n}\n\nfunction getItemOffset(depth: number): number {\n  if (depth <= 2) return 8;\n  if (depth === 3) return 20;\n  return 32;\n}\n\nexport function TOCItem({\n  item,\n  ...props\n}: Primitive.TOCItemProps & { item: Primitive.TOCItemType }) {\n  return (\n    <Primitive.TOCItem\n      href={item.url}\n      {...props}\n      className={cn(\n        \"group relative scroll-m-4 py-1 pe-2 text-[0.8125rem]/5 wrap-anywhere text-muted-foreground transition-colors hover:text-foreground data-[active=true]:text-foreground\",\n        props.className,\n      )}\n      style={{\n        paddingInlineStart: getItemOffset(item.depth),\n        ...props.style,\n      }}\n    >\n      {item._step !== undefined && (\n        <span className=\"group-data-[active=true]:text-foreground-foreground me-1.5 inline-flex size-4 items-center justify-center rounded-full bg-muted align-text-bottom font-mono text-[10px] font-medium text-muted-foreground transition-colors group-data-[active=true]:bg-primary\">\n          {item._step}\n        </span>\n      )}\n      {item.title}\n    </Primitive.TOCItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/toc/block.tsx"
    }
  ],
  "type": "registry:component"
}