{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-instructions",
  "type": "registry:ui",
  "description": "Manage and toggle AI instructions with a popover, search, and create-dialog",
  "dependencies": [
    "@hugeicons/core-free-icons",
    "@hugeicons/react"
  ],
  "registryDependencies": [
    "button",
    "command",
    "dialog",
    "hover-card",
    "input",
    "popover",
    "textarea"
  ],
  "files": [
    {
      "path": "registry/default/ui/ai-instructions.tsx",
      "content": "\"use client\"\n\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useState,\n  type ComponentProps,\n  type PropsWithChildren,\n  type ReactNode,\n} from \"react\"\nimport {\n  CheckmarkSquare02Icon,\n  PlusSignIcon,\n  SettingsIcon,\n} from \"@hugeicons/core-free-icons\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n} from \"@/components/ui/command\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  type DialogDescriptionProps,\n  type DialogTitleProps,\n} from \"@/components/ui/dialog\"\nimport {\n  HoverCard,\n  HoverCardContent,\n  HoverCardTrigger,\n} from \"@/components/ui/hover-card\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { Textarea } from \"@/components/ui/textarea\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Instruction {\n  /** Unique identifier for the instruction */\n  id: string\n  /** Display title for the instruction */\n  title: string\n  /** Short description shown in the list */\n  description: string\n  /** Full instruction content shown in hover preview */\n  content?: string\n  /** Whether this is a user-created instruction */\n  isCustom?: boolean\n}\n\nexport interface InstructionsContextValue {\n  /** Currently active instruction IDs */\n  activeIds: string[]\n  /** Toggle an instruction's active state */\n  toggle: (id: string) => void\n  /** Check if an instruction is active */\n  isActive: (id: string) => boolean\n  /** All available instructions */\n  instructions: Instruction[]\n  /** Add a custom instruction */\n  addCustom: (instruction: Omit<Instruction, \"id\" | \"isCustom\">) => void\n  /** Remove a custom instruction */\n  removeCustom: (id: string) => void\n  /** Open state for the popover */\n  open: boolean\n  /** Set open state for the popover */\n  setOpen: (open: boolean) => void\n  /** Open state for the create dialog */\n  createDialogOpen: boolean\n  /** Set open state for the create dialog */\n  setCreateDialogOpen: (open: boolean) => void\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst InstructionsContext = createContext<InstructionsContextValue | null>(null)\n\n/**\n * Hook to access the Instructions context\n * @throws Error if used outside of Instructions provider\n */\nexport const useInstructions = () => {\n  const ctx = useContext(InstructionsContext)\n  if (!ctx) {\n    throw new Error(\n      \"useInstructions must be used within an Instructions provider\"\n    )\n  }\n  return ctx\n}\n\n// ============================================================================\n// Controllable State Hook\n// ============================================================================\n\nfunction useControllableState<T>(\n  controlledValue: T | undefined,\n  defaultValue: T,\n  onChange?: (value: T) => void\n): [T, (value: T | ((prev: T) => T)) => void] {\n  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue)\n  const isControlled = controlledValue !== undefined\n  const value = isControlled ? controlledValue : uncontrolledValue\n\n  const setValue = useCallback(\n    (nextValue: T | ((prev: T) => T)) => {\n      const resolvedValue =\n        typeof nextValue === \"function\"\n          ? (nextValue as (prev: T) => T)(value)\n          : nextValue\n\n      if (!isControlled) {\n        setUncontrolledValue(resolvedValue)\n      }\n      onChange?.(resolvedValue)\n    },\n    [isControlled, onChange, value]\n  )\n\n  return [value, setValue]\n}\n\n// ============================================================================\n// Root Component\n// ============================================================================\n\nexport type InstructionsProps = PropsWithChildren<{\n  /** Controlled active instruction IDs */\n  value?: string[]\n  /** Callback when active instructions change */\n  onValueChange?: (value: string[]) => void\n  /** Default active instruction IDs (uncontrolled) */\n  defaultValue?: string[]\n  /** Initial set of instructions */\n  instructions?: Instruction[]\n  /** Callback when instructions change (for custom instructions) */\n  onInstructionsChange?: (instructions: Instruction[]) => void\n  /** Controlled open state for popover */\n  open?: boolean\n  /** Callback when open state changes */\n  onOpenChange?: (open: boolean) => void\n}>\n\n/**\n * Root component for the Instructions widget.\n * Provides context for all child components.\n */\nexport function Instructions({\n  value,\n  onValueChange,\n  defaultValue = [],\n  instructions: controlledInstructions,\n  onInstructionsChange,\n  open: controlledOpen,\n  onOpenChange,\n  children,\n}: InstructionsProps) {\n  const [activeIds, setActiveIds] = useControllableState(\n    value,\n    defaultValue,\n    onValueChange\n  )\n\n  const [internalInstructions, setInternalInstructions] = useState<\n    Instruction[]\n  >(controlledInstructions ?? [])\n\n  const instructions = controlledInstructions ?? internalInstructions\n\n  const [open, setOpen] = useControllableState(\n    controlledOpen,\n    false,\n    onOpenChange\n  )\n  const [createDialogOpen, setCreateDialogOpen] = useState(false)\n\n  const toggle = useCallback(\n    (id: string) => {\n      setActiveIds((prev) =>\n        prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]\n      )\n    },\n    [setActiveIds]\n  )\n\n  const isActive = useCallback(\n    (id: string) => activeIds.includes(id),\n    [activeIds]\n  )\n\n  const addCustom = useCallback(\n    (instruction: Omit<Instruction, \"id\" | \"isCustom\">) => {\n      const newInstruction: Instruction = {\n        ...instruction,\n        id: `custom-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n        isCustom: true,\n      }\n\n      if (controlledInstructions) {\n        onInstructionsChange?.([...controlledInstructions, newInstruction])\n      } else {\n        setInternalInstructions((prev) => [...prev, newInstruction])\n      }\n    },\n    [controlledInstructions, onInstructionsChange]\n  )\n\n  const removeCustom = useCallback(\n    (id: string) => {\n      // Also remove from active if it was active\n      setActiveIds((prev) => prev.filter((i) => i !== id))\n\n      if (controlledInstructions) {\n        onInstructionsChange?.(\n          controlledInstructions.filter((i) => i.id !== id)\n        )\n      } else {\n        setInternalInstructions((prev) => prev.filter((i) => i.id !== id))\n      }\n    },\n    [controlledInstructions, onInstructionsChange, setActiveIds]\n  )\n\n  const contextValue = useMemo<InstructionsContextValue>(\n    () => ({\n      activeIds,\n      toggle,\n      isActive,\n      instructions,\n      addCustom,\n      removeCustom,\n      open,\n      setOpen,\n      createDialogOpen,\n      setCreateDialogOpen,\n    }),\n    [\n      activeIds,\n      toggle,\n      isActive,\n      instructions,\n      addCustom,\n      removeCustom,\n      open,\n      setOpen,\n      createDialogOpen,\n    ]\n  )\n\n  return (\n    <InstructionsContext.Provider value={contextValue}>\n      <Popover onOpenChange={setOpen} open={open}>\n        {children}\n      </Popover>\n    </InstructionsContext.Provider>\n  )\n}\n\n// ============================================================================\n// Trigger Component\n// ============================================================================\n\nexport type InstructionsTriggerProps = ComponentProps<typeof Button> & {\n  /** Custom label for the trigger button */\n  label?: ReactNode\n}\n\n/**\n * Button that opens the instructions popover.\n * Shows active count badge when instructions are selected.\n */\nexport function InstructionsTrigger({\n  className,\n  label,\n  children,\n  ...props\n}: InstructionsTriggerProps) {\n  const { activeIds } = useInstructions()\n  const activeCount = activeIds.length\n\n  return (\n    <PopoverTrigger asChild>\n      <Button\n        className={cn(\n          \"gap-1.5\",\n          activeCount > 0 && \"text-foreground\",\n          className\n        )}\n        size=\"sm\"\n        type=\"button\"\n        variant=\"ghost\"\n        {...props}\n      >\n        {children ?? (\n          <>\n            <HugeiconsIcon\n              className=\"size-3.5\"\n              icon={SettingsIcon}\n              strokeWidth={2}\n            />\n            {label ?? \"Instructions\"}\n            {activeCount > 0 && (\n              <span\n                className=\"bg-primary text-primary-foreground inline-flex size-4 items-center justify-center rounded-full text-[0.625rem]\"\n                data-slot=\"instructions-count\"\n              >\n                {activeCount}\n              </span>\n            )}\n          </>\n        )}\n      </Button>\n    </PopoverTrigger>\n  )\n}\n\n// ============================================================================\n// Content Component\n// ============================================================================\n\nexport type InstructionsContentProps = ComponentProps<typeof PopoverContent>\n\n/**\n * Popover content container for the instructions list.\n */\nexport function InstructionsContent({\n  className,\n  children,\n  ...props\n}: InstructionsContentProps) {\n  return (\n    <PopoverContent\n      align=\"start\"\n      className={cn(\"w-80 p-0\", className)}\n      side=\"top\"\n      sideOffset={8}\n      {...props}\n    >\n      <Command className=\"rounded-lg\" data-slot=\"instructions-content\">\n        {children}\n      </Command>\n    </PopoverContent>\n  )\n}\n\n// ============================================================================\n// Search Component\n// ============================================================================\n\nexport type InstructionsSearchProps = ComponentProps<typeof CommandInput>\n\n/**\n * Search input for filtering instructions.\n */\nexport function InstructionsSearch({\n  placeholder = \"Search instructions...\",\n  className,\n  ...props\n}: InstructionsSearchProps) {\n  return (\n    <CommandInput\n      className={cn(className)}\n      data-slot=\"instructions-search\"\n      placeholder={placeholder}\n      {...props}\n    />\n  )\n}\n\n// ============================================================================\n// List Component\n// ============================================================================\n\nexport type InstructionsListProps = ComponentProps<typeof CommandList>\n\n/**\n * Scrollable list container for instructions.\n */\nexport function InstructionsList({\n  className,\n  children,\n  ...props\n}: InstructionsListProps) {\n  return (\n    <CommandList\n      className={cn(\"max-h-64\", className)}\n      data-slot=\"instructions-list\"\n      {...props}\n    >\n      {children}\n    </CommandList>\n  )\n}\n\n// ============================================================================\n// Empty Component\n// ============================================================================\n\nexport type InstructionsEmptyProps = ComponentProps<typeof CommandEmpty>\n\n/**\n * Empty state shown when no instructions match the search.\n */\nexport function InstructionsEmpty({\n  children = \"No instructions found.\",\n  className,\n  ...props\n}: InstructionsEmptyProps) {\n  return (\n    <CommandEmpty\n      className={cn(className)}\n      data-slot=\"instructions-empty\"\n      {...props}\n    >\n      {children}\n    </CommandEmpty>\n  )\n}\n\n// ============================================================================\n// Group Component\n// ============================================================================\n\nexport type InstructionsGroupProps = ComponentProps<typeof CommandGroup>\n\n/**\n * Group container for categorizing instructions.\n * Applies rounded corners to first/last items only.\n */\nexport function InstructionsGroup({\n  className,\n  ...props\n}: InstructionsGroupProps) {\n  return (\n    <CommandGroup\n      className={cn(\n        // Remove default rounding from all items\n        \"**:[[cmdk-item]]:rounded-none\",\n        // First child in group gets rounded top\n        \"[&_[cmdk-group-items]>:first-child_[cmdk-item]]:rounded-t-md\",\n        // Last child in group gets rounded bottom\n        \"[&_[cmdk-group-items]>:last-child_[cmdk-item]]:rounded-b-md\",\n        className\n      )}\n      data-slot=\"instructions-group\"\n      {...props}\n    />\n  )\n}\n\n// ============================================================================\n// Separator Component\n// ============================================================================\n\nexport type InstructionsSeparatorProps = ComponentProps<typeof CommandSeparator>\n\n/**\n * Visual separator between instruction groups.\n */\nexport function InstructionsSeparator({\n  className,\n  ...props\n}: InstructionsSeparatorProps) {\n  return (\n    <CommandSeparator\n      className={cn(className)}\n      data-slot=\"instructions-separator\"\n      {...props}\n    />\n  )\n}\n\n// ============================================================================\n// Item Component\n// ============================================================================\n\nexport type InstructionsItemProps = Omit<\n  ComponentProps<typeof CommandItem>,\n  \"value\" | \"onSelect\"\n> & {\n  /** The instruction data */\n  instruction: Instruction\n  /** Disable hover card preview */\n  disablePreview?: boolean\n}\n\n/**\n * Individual instruction item with toggle state and optional hover preview.\n */\nexport function InstructionsItem({\n  instruction,\n  disablePreview = false,\n  className,\n  children,\n  ...props\n}: InstructionsItemProps) {\n  const { toggle, isActive } = useInstructions()\n  const active = isActive(instruction.id)\n\n  const handleSelect = () => {\n    toggle(instruction.id)\n  }\n\n  const itemContent = (\n    <CommandItem\n      className={cn(\n        \"group/instruction-item flex cursor-pointer items-start gap-3 py-2\",\n        active && \"bg-accent\",\n        className\n      )}\n      data-checked={active}\n      data-slot=\"instructions-item\"\n      data-state={active ? \"active\" : \"inactive\"}\n      onSelect={handleSelect}\n      value={instruction.title}\n      {...props}\n    >\n      <div\n        className={cn(\n          \"mt-0.5 flex size-4 shrink-0 items-center justify-center border first:rounded-t-lg last:rounded-b-lg\",\n          active\n            ? \"border-primary bg-primary text-primary-foreground\"\n            : \"border-muted-foreground/30\"\n        )}\n        data-slot=\"instructions-item-checkbox\"\n      >\n        {active && (\n          <HugeiconsIcon\n            className=\"size-3\"\n            icon={CheckmarkSquare02Icon}\n            strokeWidth={2.5}\n          />\n        )}\n      </div>\n      <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n        {children ?? (\n          <>\n            <InstructionsItemTitle>{instruction.title}</InstructionsItemTitle>\n            <InstructionsItemDescription>\n              {instruction.description}\n            </InstructionsItemDescription>\n          </>\n        )}\n      </div>\n    </CommandItem>\n  )\n\n  if (disablePreview || !instruction.content) {\n    return itemContent\n  }\n\n  return (\n    <InstructionsHoverCard instruction={instruction}>\n      {itemContent}\n    </InstructionsHoverCard>\n  )\n}\n\n// ============================================================================\n// Item Title Component\n// ============================================================================\n\nexport type InstructionsItemTitleProps = ComponentProps<\"span\">\n\n/**\n * Title text for an instruction item.\n */\nexport function InstructionsItemTitle({\n  className,\n  ...props\n}: InstructionsItemTitleProps) {\n  return (\n    <span\n      className={cn(\"text-foreground text-xs font-medium\", className)}\n      data-slot=\"instructions-item-title\"\n      {...props}\n    />\n  )\n}\n\n// ============================================================================\n// Item Description Component\n// ============================================================================\n\nexport type InstructionsItemDescriptionProps = ComponentProps<\"span\">\n\n/**\n * Description text for an instruction item.\n */\nexport function InstructionsItemDescription({\n  className,\n  ...props\n}: InstructionsItemDescriptionProps) {\n  return (\n    <span\n      className={cn(\"text-muted-foreground line-clamp-2 text-xs\", className)}\n      data-slot=\"instructions-item-description\"\n      {...props}\n    />\n  )\n}\n\n// ============================================================================\n// Hover Card Component\n// ============================================================================\n\nexport interface InstructionsHoverCardProps {\n  /** The instruction to preview */\n  instruction: Instruction\n  /** The trigger element (instruction item) */\n  children: ReactNode\n}\n\n/**\n * Hover card that shows the full instruction content on hover.\n */\nexport function InstructionsHoverCard({\n  instruction,\n  children,\n}: InstructionsHoverCardProps) {\n  return (\n    <HoverCard openDelay={300} closeDelay={100}>\n      <HoverCardTrigger>{children}</HoverCardTrigger>\n      <HoverCardContent\n        align=\"start\"\n        className=\"w-72\"\n        data-slot=\"instructions-hover-card\"\n        side=\"right\"\n        sideOffset={8}\n      >\n        <div className=\"flex flex-col gap-2\">\n          <div className=\"flex flex-col gap-1\">\n            <span className=\"text-sm font-medium\">{instruction.title}</span>\n            <span className=\"text-muted-foreground text-xs\">\n              {instruction.description}\n            </span>\n          </div>\n          {instruction.content && (\n            <div className=\"bg-muted/50 rounded-md p-2\">\n              <p className=\"text-muted-foreground text-xs whitespace-pre-wrap\">\n                {instruction.content}\n              </p>\n            </div>\n          )}\n          {instruction.isCustom && (\n            <span className=\"text-muted-foreground text-[0.625rem]\">\n              Custom instruction\n            </span>\n          )}\n        </div>\n      </HoverCardContent>\n    </HoverCard>\n  )\n}\n\n// ============================================================================\n// Footer Component\n// ============================================================================\n\nexport type InstructionsFooterProps = ComponentProps<\"div\">\n\n/**\n * Footer container that stays fixed at the bottom of the popover.\n * Use this to place the create trigger outside the scrollable list.\n */\nexport function InstructionsFooter({\n  className,\n  children,\n  ...props\n}: InstructionsFooterProps) {\n  return (\n    <div\n      className={cn(\"border-t p-1\", className)}\n      data-slot=\"instructions-footer\"\n      {...props}\n    >\n      {children}\n    </div>\n  )\n}\n\n// ============================================================================\n// Create Trigger Component\n// ============================================================================\n\nexport type InstructionsCreateTriggerProps = Omit<\n  ComponentProps<typeof CommandItem>,\n  \"onSelect\"\n> & {\n  /** Custom label for the create button */\n  label?: ReactNode\n}\n\n/**\n * Button that opens the create instruction dialog.\n */\nexport function InstructionsCreateTrigger({\n  label = \"New Instruction\",\n  className,\n  children,\n  ...props\n}: InstructionsCreateTriggerProps) {\n  const { setCreateDialogOpen, setOpen } = useInstructions()\n\n  const handleSelect = () => {\n    setOpen(false)\n    setCreateDialogOpen(true)\n  }\n\n  return (\n    <CommandItem\n      className={cn(\n        \"text-muted-foreground flex cursor-pointer items-center gap-2\",\n        className\n      )}\n      data-slot=\"instructions-create-trigger\"\n      onSelect={handleSelect}\n      value=\"__new_instruction__\"\n      {...props}\n    >\n      {children ?? (\n        <>\n          <HugeiconsIcon\n            className=\"size-3.5\"\n            icon={PlusSignIcon}\n            strokeWidth={2}\n          />\n          {label}\n        </>\n      )}\n    </CommandItem>\n  )\n}\n\n// ============================================================================\n// Create Dialog Component\n// ============================================================================\n\nexport type InstructionsCreateDialogProps = Omit<\n  ComponentProps<typeof Dialog>,\n  \"open\" | \"onOpenChange\" | \"children\"\n> & {\n  /** Dialog title */\n  title?: DialogTitleProps[\"children\"]\n  /** Dialog description */\n  description?: DialogDescriptionProps[\"children\"]\n  /** Custom form content to replace the default form */\n  children?: ReactNode\n}\n\n/**\n * Modal dialog for creating custom instructions.\n */\nexport function InstructionsCreateDialog({\n  title = \"Create Instruction\",\n  description = \"Create a custom instruction to guide the AI's behavior.\",\n  children,\n  ...props\n}: InstructionsCreateDialogProps) {\n  const { createDialogOpen, setCreateDialogOpen, addCustom } = useInstructions()\n  const [formTitle, setFormTitle] = useState(\"\")\n  const [formDescription, setFormDescription] = useState(\"\")\n  const [formContent, setFormContent] = useState(\"\")\n\n  const handleSubmit = (e: React.FormEvent) => {\n    e.preventDefault()\n\n    if (!(formTitle.trim() && formDescription.trim())) {\n      return\n    }\n\n    addCustom({\n      title: formTitle.trim(),\n      description: formDescription.trim(),\n      content: formContent.trim() || undefined,\n    })\n\n    // Reset form\n    setFormTitle(\"\")\n    setFormDescription(\"\")\n    setFormContent(\"\")\n    setCreateDialogOpen(false)\n  }\n\n  const handleOpenChange = (open: boolean) => {\n    setCreateDialogOpen(open)\n    if (!open) {\n      // Reset form when closing\n      setFormTitle(\"\")\n      setFormDescription(\"\")\n      setFormContent(\"\")\n    }\n  }\n\n  return (\n    <Dialog\n      data-slot=\"instructions-create-dialog\"\n      onOpenChange={handleOpenChange}\n      open={createDialogOpen}\n      {...props}\n    >\n      <DialogContent className=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>{title}</DialogTitle>\n          <DialogDescription>{description}</DialogDescription>\n        </DialogHeader>\n        {children ?? (\n          <form className=\"flex flex-col gap-4\" onSubmit={handleSubmit}>\n            <div className=\"flex flex-col gap-2\">\n              <label\n                className=\"text-xs font-medium\"\n                htmlFor=\"instruction-title\"\n              >\n                Title\n              </label>\n              <Input\n                id=\"instruction-title\"\n                onChange={(e) => setFormTitle(e.target.value)}\n                placeholder=\"e.g., Be Concise\"\n                required\n                value={formTitle}\n              />\n            </div>\n            <div className=\"flex flex-col gap-2\">\n              <label\n                className=\"text-xs font-medium\"\n                htmlFor=\"instruction-description\"\n              >\n                Description\n              </label>\n              <Input\n                id=\"instruction-description\"\n                onChange={(e) => setFormDescription(e.target.value)}\n                placeholder=\"e.g., Keep responses short and to the point\"\n                required\n                value={formDescription}\n              />\n            </div>\n            <div className=\"flex flex-col gap-2\">\n              <label\n                className=\"text-xs font-medium\"\n                htmlFor=\"instruction-content\"\n              >\n                Full Instruction (optional)\n              </label>\n              <Textarea\n                className=\"min-h-24 resize-none\"\n                id=\"instruction-content\"\n                onChange={(e) => setFormContent(e.target.value)}\n                placeholder=\"Detailed instruction text that will be shown in the preview...\"\n                value={formContent}\n              />\n            </div>\n            <DialogFooter>\n              <DialogClose asChild>\n                <Button type=\"button\" variant=\"outline\">\n                  Cancel\n                </Button>\n              </DialogClose>\n              <Button type=\"submit\">Create</Button>\n            </DialogFooter>\n          </form>\n        )}\n      </DialogContent>\n    </Dialog>\n  )\n}\n\n// ============================================================================\n// Compound Export\n// ============================================================================\n\nexport const InstructionsWidget = Object.assign(Instructions, {\n  Trigger: InstructionsTrigger,\n  Content: InstructionsContent,\n  Search: InstructionsSearch,\n  List: InstructionsList,\n  Empty: InstructionsEmpty,\n  Group: InstructionsGroup,\n  Separator: InstructionsSeparator,\n  Item: InstructionsItem,\n  ItemTitle: InstructionsItemTitle,\n  ItemDescription: InstructionsItemDescription,\n  HoverCard: InstructionsHoverCard,\n  Footer: InstructionsFooter,\n  CreateTrigger: InstructionsCreateTrigger,\n  CreateDialog: InstructionsCreateDialog,\n})\n",
      "type": "registry:ui"
    }
  ]
}