import { IconChevronDown, IconCheck } from "@tabler/icons-react"; import { createContext, useCallback, useContext, useEffect, useState, type ReactNode, type TransitionEvent, } from "react"; import { cn } from "../utils.js"; /** * Where a settings section is being rendered. * * - `sidebar`: the compact agent sidebar panel (dense, small type). * - `page`: the full-width settings page, styled as a polished card that * matches the shadcn `Card` surface used by app-owned settings tabs. */ export type SettingsSurface = "sidebar" | "page"; const SettingsSurfaceContext = createContext("sidebar"); export function SettingsSurfaceProvider({ surface, children, }: { surface: SettingsSurface; children: ReactNode; }) { return ( {children} ); } export function useSettingsSurface(): SettingsSurface { return useContext(SettingsSurfaceContext); } interface SettingsSectionProps { id?: string; icon: ReactNode; title: string; subtitle?: string; badge?: string; required?: boolean; connected?: boolean; open?: boolean; onToggle?: () => void; children: ReactNode; } /** * Collapsible settings section. Renders as a compact row in the agent sidebar * and as a polished, shadcn-style card on the full settings page (so the * framework tabs match app-owned General/Team cards). The visual surface is * read from `SettingsSurfaceContext`. */ export function SettingsSection(props: SettingsSectionProps) { const surface = useSettingsSurface(); return surface === "page" ? ( ) : ( ); } function ConnectedDot({ size }: { size: "sm" | "md" }) { const dim = size === "sm" ? "h-4 w-4" : "h-5 w-5"; const stroke = size === "sm" ? 3 : 2.5; const glyph = size === "sm" ? 10 : 12; return ( ); } function StatusBadge({ label, tone, size, }: { label: string; tone: "muted" | "required"; size: "sm" | "md"; }) { return ( {label} ); } function SettingsSectionBody({ open, children, }: { open: boolean; children: ReactNode; }) { const [present, setPresent] = useState(open); useEffect(() => { if (open) { setPresent(true); return; } if (!present) return; // Fallback for reduced-motion and older transition engines that may not // emit a height transitionend event. const timeout = window.setTimeout(() => setPresent(false), 260); return () => window.clearTimeout(timeout); }, [open, present]); const handleTransitionEnd = useCallback( (event: TransitionEvent) => { if (event.target !== event.currentTarget) return; if (open) return; if ( event.propertyName !== "height" && event.propertyName !== "max-height" ) { return; } setPresent(false); }, [open], ); if (!present) return null; return (
{children}
); } function PageSettingsSection({ id, icon, title, subtitle, badge, required, connected, open = false, onToggle, children, }: SettingsSectionProps) { return (
{children}
); } function SidebarSettingsSection({ id, icon, title, subtitle, badge, required, connected, open = false, onToggle, children, }: SettingsSectionProps) { return (
{subtitle && (

{subtitle}

)} {children}
); }