{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "table-of-contents",
  "title": "Table of Contents",
  "description": "Auto-scrolling table of contents with intersection observer for active section.",
  "files": [
    {
      "path": "registry/blocks/table-of-contents/table-of-contents.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useState, useCallback } from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport { TableOfContentsIcon } from \"lucide-react\"\n\nexport interface TocItem {\n  id: string\n  title: string\n  level?: number\n}\n\ninterface TableOfContentsProps {\n  items: TocItem[]\n  className?: string\n}\n\nfunction useActiveSection(itemIds: string[]) {\n  const [activeId, setActiveId] = useState<string>(\"\")\n\n  useEffect(() => {\n    if (itemIds.length === 0) return\n\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            setActiveId(entry.target.id)\n          }\n        })\n      },\n      {\n        rootMargin: \"-80px 0px -80% 0px\",\n        threshold: 0,\n      }\n    )\n\n    itemIds.forEach((id) => {\n      const element = document.getElementById(id)\n      if (element) {\n        observer.observe(element)\n      }\n    })\n\n    return () => observer.disconnect()\n  }, [itemIds])\n\n  return activeId\n}\n\nfunction TableOfContents({ items, className }: TableOfContentsProps) {\n  const itemIds = items.map((item) => item.id)\n  const activeId = useActiveSection(itemIds)\n\n  const scrollToSection = useCallback((id: string) => {\n    const element = document.getElementById(id)\n    if (element) {\n      element.scrollIntoView({ behavior: \"smooth\" })\n    }\n  }, [])\n\n  if (items.length === 0) {\n    return null\n  }\n\n  return (\n    <nav className={cn(\"relative\", className)}>\n      <div className=\"flex items-center gap-2 mb-3\">\n        <TableOfContentsIcon className=\"h-4 w-4 text-muted-foreground\" />\n        <p className=\"text-xs font-semibold text-muted-foreground tracking-wide\">\n          on this page\n        </p>\n      </div>\n\n      <ul className=\"space-y-1 border-l border-border\">\n        {items.map(({ id, title, level = 1 }) => {\n          const isActive = activeId === id\n          const isChild = level > 1\n\n          return (\n            <li key={id}>\n              <button\n                onClick={() => scrollToSection(id)}\n                className={cn(\n                  \"flex items-center w-full text-left text-[13px] py-1 -ml-px border-l transition-colors\",\n                  level === 1 && \"pl-3 font-medium\",\n                  level === 2 && \"pl-5 text-xs\",\n                  isActive\n                    ? \"border-foreground text-foreground\"\n                    : \"border-transparent text-muted-foreground hover:text-foreground hover:border-muted-foreground\"\n                )}\n              >\n                {isChild && (\n                  <span\n                    className={cn(\n                      \"mr-1.5 text-[10px] font-mono\",\n                      isActive ? \"text-foreground\" : \"text-muted-foreground/50\"\n                    )}\n                  >\n                    └\n                  </span>\n                )}\n                {title}\n              </button>\n            </li>\n          )\n        })}\n      </ul>\n    </nav>\n  )\n}\n\nexport { TableOfContents, useActiveSection }\nexport type { TableOfContentsProps }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}