{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"agent-track-toggle","title":"Agent Track Toggle","description":"A toggle button for controlling media tracks (microphone, camera, screen share) with appropriate icons and loading states.","dependencies":["livekit-client@^2.0.0","@livekit/components-react@^2.0.0","class-variance-authority","lucide-react"],"registryDependencies":["toggle","utils"],"files":[{"path":"components/agents-ui/agent-track-toggle.tsx","content":"import { Fragment, type ComponentProps, useMemo, useState } from 'react';\nimport { type VariantProps, cva } from 'class-variance-authority';\nimport { Track } from 'livekit-client';\nimport {\n  MicIcon,\n  MicOffIcon,\n  MonitorUpIcon,\n  MonitorOffIcon,\n  LoaderIcon,\n  VideoIcon,\n  VideoOffIcon,\n} from 'lucide-react';\nimport { Toggle } from '@/components/ui/toggle';\nimport { cn } from '@/lib/utils';\n\nexport const agentTrackToggleVariants = cva(['size-9'], {\n  variants: {\n    size: {\n      default: 'h-9 px-2 min-w-9',\n      sm: 'h-8 px-1.5 min-w-8',\n      lg: 'h-10 px-2.5 min-w-10',\n    },\n    variant: {\n      default: [\n        'data-[state=off]:bg-destructive/10 data-[state=off]:text-destructive',\n        'data-[state=off]:hover:bg-destructive/15',\n        'data-[state=off]:focus-visible:ring-destructive/30',\n        'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',\n        'data-[state=on]:hover:bg-foreground/10',\n      ],\n      outline: [\n        'data-[state=off]:bg-destructive/10 data-[state=off]:text-destructive data-[state=off]:border-destructive/20',\n        'data-[state=off]:hover:bg-destructive/15 data-[state=off]:hover:text-destructive',\n        'data-[state=off]:focus:text-destructive',\n        'data-[state=off]:focus-visible:border-destructive data-[state=off]:focus-visible:ring-destructive/30',\n        'data-[state=on]:hover:bg-foreground/10 data-[state=on]:hover:border-foreground/12',\n        'dark:data-[state=on]:hover:bg-foreground/10',\n      ],\n    },\n  },\n  defaultVariants: {\n    variant: 'default',\n  },\n});\n\nfunction getSourceIcon(source: Track.Source, enabled: boolean, pending = false) {\n  if (pending) {\n    return LoaderIcon;\n  }\n\n  switch (source) {\n    case Track.Source.Microphone:\n      return enabled ? MicIcon : MicOffIcon;\n    case Track.Source.Camera:\n      return enabled ? VideoIcon : VideoOffIcon;\n    case Track.Source.ScreenShare:\n      return enabled ? MonitorUpIcon : MonitorOffIcon;\n    default:\n      return Fragment;\n  }\n}\n\n/**\n * Props for the AgentTrackToggle component.\n */\nexport type AgentTrackToggleProps = VariantProps<typeof agentTrackToggleVariants> &\n  ComponentProps<'button'> & {\n    /**\n     * The size of the toggle.\n     */\n    size?: 'sm' | 'default' | 'lg';\n    /**\n     * The variant of the toggle.\n     * @defaultValue 'default'\n     */\n    variant?: 'default' | 'outline';\n    /**\n     * The track source to toggle (Microphone, Camera, or ScreenShare).\n     */\n    source: 'camera' | 'microphone' | 'screen_share';\n    /**\n     * Whether the toggle is in a pending/loading state.\n     * When true, displays a loading spinner icon.\n     * @defaultValue false\n     */\n    pending?: boolean;\n    /**\n     * Whether the toggle is currently pressed/enabled.\n     * @defaultValue false\n     */\n    pressed?: boolean;\n    /**\n     * The default pressed state when uncontrolled.\n     * @defaultValue false\n     */\n    defaultPressed?: boolean;\n    /**\n     * Callback fired when the pressed state changes.\n     */\n    onPressedChange?: (pressed: boolean) => void;\n  };\n\n/**\n * A toggle button for controlling track publishing state.\n * Displays appropriate icons based on the track source and state.\n *\n * @extends ComponentProps<'button'>\n *\n * @example\n * ```tsx\n * <AgentTrackToggle\n *   source={Track.Source.Microphone}\n *   pressed={isMicEnabled}\n *   onPressedChange={(pressed) => setMicEnabled(pressed)}\n * />\n * ```\n */\nexport function AgentTrackToggle({\n  size = 'default',\n  variant = 'default',\n  source,\n  pending = false,\n  pressed,\n  defaultPressed = false,\n  className,\n  onPressedChange,\n  ...props\n}: AgentTrackToggleProps) {\n  const [uncontrolledPressed, setUncontrolledPressed] = useState(defaultPressed ?? false);\n  const isControlled = pressed !== undefined;\n  const resolvedPressed = useMemo(\n    () => (isControlled ? pressed : uncontrolledPressed) ?? false,\n    [isControlled, pressed, uncontrolledPressed],\n  );\n  const IconComponent = getSourceIcon(source as Track.Source, resolvedPressed, pending);\n  const handlePressedChange = (nextPressed: boolean) => {\n    if (!isControlled) {\n      setUncontrolledPressed(nextPressed);\n    }\n    onPressedChange?.(nextPressed);\n  };\n\n  return (\n    <Toggle\n      size={size}\n      variant={variant}\n      pressed={isControlled ? pressed : undefined}\n      defaultPressed={isControlled ? undefined : defaultPressed}\n      aria-label={`Toggle ${source}`}\n      onPressedChange={handlePressedChange}\n      className={cn(\n        agentTrackToggleVariants({\n          size,\n          variant: variant ?? 'default',\n          className,\n        }),\n      )}\n      {...props}\n    >\n      <IconComponent className={cn(pending && 'animate-spin')} />\n      {props.children}\n    </Toggle>\n  );\n}\n","type":"registry:component"}],"type":"registry:component"}