import { useEffect, useState, type ReactNode } from 'react'; import { ScrollArea } from '@/components/ui/scroll-area'; import { cn } from '@/lib/utils'; export interface SectionRailItem { id: string; label: ReactNode; badge?: ReactNode; } interface SectionRailProps { sections: SectionRailItem[]; /** ID of the FormSection element to scroll into view on click. Section's `id` prop must match. */ onJump?: (id: string) => void; /** Optional: container element to observe for scroll-spy. Defaults to scrollable ancestor. */ observeRoot?: HTMLElement | null; header?: ReactNode; className?: string; } /** * SectionRail - Anchor nav for single-entity Config pages. * * Replaces ListPane on pages that configure ONE entity (codex, copilot, cursor, droid…). * Each item maps to a in the FormPane. Scroll-spy via * IntersectionObserver auto-highlights the active section. * * Click → smooth-scrolls the matching FormSection into view. */ export function SectionRail({ sections, onJump, observeRoot, header, className, }: SectionRailProps) { const [activeId, setActiveId] = useState(sections[0]?.id ?? ''); // Scroll-spy: observe each FormSection element by id useEffect(() => { const els = sections .map((s) => document.getElementById(s.id)) .filter((el): el is HTMLElement => el !== null); if (els.length === 0) return; const observer = new IntersectionObserver( (entries) => { // Pick the section closest to top that's intersecting const visible = entries .filter((e) => e.isIntersecting) .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top); if (visible[0]) setActiveId(visible[0].target.id); }, { root: observeRoot ?? null, rootMargin: '-20% 0px -60% 0px', threshold: 0, } ); els.forEach((el) => observer.observe(el)); return () => observer.disconnect(); }, [sections, observeRoot]); const handleJump = (id: string) => { setActiveId(id); if (onJump) { onJump(id); return; } document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; return (
{header &&
{header}
}
    {sections.map((s) => { const active = s.id === activeId; return (
  • ); })}
); }