{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "anchor",
  "title": "Anchor",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/anchor/anchor.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface AnchorItem {\n  href: string\n  title: string\n  children?: AnchorItem[]\n}\n\ninterface AnchorContextValue {\n  activeHref: string | null\n  setActive: (href: string) => void\n  register: (href: string) => void\n  unregister: (href: string) => void\n  scrollContainerRef: React.MutableRefObject<HTMLElement | Window>\n  offsetTopRef: React.MutableRefObject<number>\n}\n\nconst AnchorContext = React.createContext<AnchorContextValue | null>(null)\n\nexport interface AnchorProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {\n  items?: AnchorItem[]\n  offsetTop?: number\n  bounds?: number\n  scrollContainer?: HTMLElement | string | null\n  affix?: boolean\n  onChange?: (href: string) => void\n}\n\nconst Anchor = React.forwardRef<HTMLElement, AnchorProps>(\n  (\n    {\n      className,\n      items = [],\n      offsetTop = 0,\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      bounds = 5,\n      scrollContainer = null,\n      affix = false,\n      onChange,\n      style,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const [activeHref, setActiveHref] = React.useState<string | null>(null)\n    const activeHrefRef = React.useRef<string | null>(null)\n    const registeredRef = React.useRef<Set<string>>(new Set())\n    const observerRef = React.useRef<IntersectionObserver | null>(null)\n    const observedRef = React.useRef<Map<string, Element>>(new Map())\n    const scrollContainerRef = React.useRef<HTMLElement | Window>(\n      typeof window !== 'undefined' ? window : (null as unknown as Window),\n    )\n    const offsetTopRef = React.useRef(offsetTop)\n    const onChangeRef = React.useRef(onChange)\n\n    React.useEffect(() => {\n      offsetTopRef.current = offsetTop\n    }, [offsetTop])\n    React.useEffect(() => {\n      onChangeRef.current = onChange\n    }, [onChange])\n\n    const setActive = React.useCallback((href: string) => {\n      activeHrefRef.current = href\n      setActiveHref(href)\n      onChangeRef.current?.(href)\n    }, [])\n\n    const resolveContainer = React.useCallback((): HTMLElement | Window => {\n      if (!scrollContainer) return window\n      if (typeof scrollContainer === 'string') {\n        return (document.querySelector(scrollContainer) as HTMLElement) ?? window\n      }\n      return scrollContainer\n    }, [scrollContainer])\n\n    const rebuildObserver = React.useCallback(() => {\n      if (observerRef.current) {\n        observerRef.current.disconnect()\n        observedRef.current.clear()\n      }\n      const container = scrollContainerRef.current\n      const root = container === window ? null : (container as HTMLElement)\n      observerRef.current = new IntersectionObserver(\n        (entries) => {\n          const intersecting = entries.filter((e) => e.isIntersecting)\n          if (intersecting.length === 0) return\n          intersecting.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)\n          const id = '#' + intersecting[0]!.target.id\n          if (id !== activeHrefRef.current) {\n            activeHrefRef.current = id\n            setActiveHref(id)\n            onChangeRef.current?.(id)\n          }\n        },\n        {\n          root,\n          rootMargin: `-${offsetTopRef.current}px 0px -60% 0px`,\n          threshold: [0, 1],\n        },\n      )\n      for (const href of registeredRef.current) {\n        const el = document.querySelector(href)\n        if (el) {\n          observedRef.current.set(href, el)\n          observerRef.current.observe(el)\n        }\n      }\n    }, [])\n\n    const register = React.useCallback((href: string) => {\n      registeredRef.current.add(href)\n      if (!observerRef.current) return\n      const el = document.querySelector(href)\n      if (el && !observedRef.current.has(href)) {\n        observedRef.current.set(href, el)\n        observerRef.current.observe(el)\n      }\n    }, [])\n\n    const unregister = React.useCallback((href: string) => {\n      registeredRef.current.delete(href)\n      const el = observedRef.current.get(href)\n      if (el && observerRef.current) observerRef.current.unobserve(el)\n      observedRef.current.delete(href)\n    }, [])\n\n    React.useEffect(() => {\n      scrollContainerRef.current = resolveContainer()\n      rebuildObserver()\n      return () => {\n        observerRef.current?.disconnect()\n        observerRef.current = null\n      }\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [scrollContainer])\n\n    const ctx = React.useMemo<AnchorContextValue>(\n      () => ({ activeHref, setActive, register, unregister, scrollContainerRef, offsetTopRef }),\n      [activeHref, setActive, register, unregister],\n    )\n\n    return (\n      <AnchorContext.Provider value={ctx}>\n        <nav\n          ref={ref}\n          data-uipkge=\"\"\n          data-slot=\"anchor\"\n          aria-label=\"Table of contents\"\n          className={cn('border-border flex flex-col gap-1 border-l text-sm', affix && 'sticky', className)}\n          style={affix ? { top: `${offsetTop}px`, ...style } : style}\n          {...props}\n        >\n          {items.length\n            ? items.map((item) => (\n                <AnchorLink key={item.href} href={item.href} title={item.title}>\n                  {(item.children ?? []).map((child) => (\n                    <AnchorLink key={child.href} href={child.href} title={child.title} />\n                  ))}\n                </AnchorLink>\n              ))\n            : children}\n        </nav>\n      </AnchorContext.Provider>\n    )\n  },\n)\nAnchor.displayName = 'Anchor'\n\nexport interface AnchorLinkProps {\n  href: string\n  title: string\n  className?: string\n  children?: React.ReactNode\n}\n\nconst AnchorLink = React.forwardRef<HTMLDivElement, AnchorLinkProps>(\n  ({ href, title, className, children }, ref) => {\n    const ctx = React.useContext(AnchorContext)\n    if (!ctx) throw new Error('AnchorLink must be used inside <Anchor>.')\n\n    const isActive = ctx.activeHref === href\n\n    React.useEffect(() => {\n      ctx.register(href)\n      return () => ctx.unregister(href)\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [href])\n\n    const onClick = (e: React.MouseEvent) => {\n      e.preventDefault()\n      const el = document.querySelector(href) as HTMLElement | null\n      if (!el) return\n      const offset = ctx.offsetTopRef.current\n      const container = ctx.scrollContainerRef.current\n      const smooth = !window.matchMedia('(prefers-reduced-motion: reduce)').matches\n      const behavior: ScrollBehavior = smooth ? 'smooth' : 'auto'\n      if (container === window) {\n        const top = el.getBoundingClientRect().top + window.scrollY - offset\n        window.scrollTo({ top, behavior })\n      } else {\n        const c = container as HTMLElement\n        const top = el.getBoundingClientRect().top - c.getBoundingClientRect().top + c.scrollTop - offset\n        c.scrollTo({ top, behavior })\n      }\n      history.replaceState(null, '', href)\n      ctx.setActive(href)\n    }\n\n    return (\n      <div ref={ref} data-uipkge=\"\" data-slot=\"anchor-link\" className=\"flex flex-col\">\n        <a\n          href={href}\n          aria-current={isActive ? 'location' : undefined}\n          className={cn(\n            'text-muted-foreground -ml-px block border-l-2 border-transparent py-2 pl-3 transition-colors',\n            'hover:text-foreground',\n            'focus-visible:ring-ring rounded-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',\n            isActive && 'border-primary text-foreground font-medium',\n            className,\n          )}\n          onClick={onClick}\n        >\n          {title}\n        </a>\n        {children ? <div className=\"ml-3\">{children}</div> : null}\n      </div>\n    )\n  },\n)\nAnchorLink.displayName = 'AnchorLink'\n\nexport { Anchor, AnchorLink }\n",
      "type": "registry:ui",
      "target": "~/components/ui/anchor/anchor.tsx"
    },
    {
      "path": "packages/registry-react/components/anchor/index.ts",
      "content": "export { Anchor, AnchorLink, type AnchorItem, type AnchorProps, type AnchorLinkProps } from './anchor'\n",
      "type": "registry:ui",
      "target": "~/components/ui/anchor/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "In-page navigation list with scroll-spy. Renders a vertical list of links; the active item highlights as the user scrolls through anchored sections.",
  "categories": [
    "navigation"
  ]
}