{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "composer-trigger-popover",
  "type": "registry:component",
  "title": "Composer Trigger Popover",
  "description": "Character triggered picker for @ mentions, / commands, and similar popovers.",
  "dependencies": [
    "@assistant-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/composer-trigger-popover.aui.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/composer-trigger-popover.aui.tsx",
      "content": "\"use client\";\n\nimport { memo, useRef, type ComponentPropsWithoutRef, type FC } from \"react\";\nimport {\n  ComposerPrimitive,\n  unstable_defaultDirectiveFormatter,\n  unstable_useTriggerPopoverScopeContext,\n  type Unstable_DirectiveFormatter,\n  type Unstable_TriggerItem,\n} from \"@assistant-ui/react\";\nimport { ChevronLeftIcon, ChevronRightIcon, SparklesIcon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ntype IconComponent = FC<{ className?: string }>;\n\ntype DirectiveBehaviorProps = {\n  /** Formatter used to serialize the selected item into composer text. */\n  formatter?: Unstable_DirectiveFormatter | undefined;\n  /** Called after the directive text has been inserted into the composer. */\n  onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\ntype ActionBehaviorProps = {\n  /** Formatter used to serialize the audit-trail chip (when `removeOnExecute` is false). */\n  formatter?: Unstable_DirectiveFormatter | undefined;\n  /** Invoked with the selected item at the moment of selection. */\n  onExecute: (item: Unstable_TriggerItem) => void;\n  /** If `true`, strip the trigger text from the composer after executing. @default false */\n  removeOnExecute?: boolean | undefined;\n};\n\ntype ComposerTriggerPopoverBaseProps = Omit<\n  ComponentPropsWithoutRef<typeof ComposerPrimitive.Unstable_TriggerPopover>,\n  \"children\"\n> & {\n  /**\n   * Maps icon keys to components. Items look up via `item.metadata?.icon`\n   * (string); categories look up via their `id`.\n   */\n  iconMap?: Record<string, IconComponent>;\n  /** Fallback icon when no entry in `iconMap` matches. */\n  fallbackIcon?: IconComponent;\n  /** Label shown on the back button. @default \"Back\" */\n  backLabel?: string;\n  /** Label shown when no categories are available. @default \"No items available\" */\n  emptyCategoriesLabel?: string;\n  /** Label shown when no items match. @default \"No matching items\" */\n  emptyItemsLabel?: string;\n  /** Label shown while an async adapter is resolving items. @default \"Loading…\" */\n  loadingLabel?: string;\n};\n\ntype ComposerTriggerPopoverProps = ComposerTriggerPopoverBaseProps &\n  (\n    | {\n        /** Insert-directive behavior. */\n        directive: DirectiveBehaviorProps;\n        action?: never;\n      }\n    | {\n        /** Action behavior. */\n        action: ActionBehaviorProps;\n        directive?: never;\n      }\n  );\n\nfunction resolveIcon(\n  iconKey: string | undefined,\n  iconMap: Record<string, IconComponent> | undefined,\n  fallback: IconComponent,\n): IconComponent {\n  if (iconKey && iconMap?.[iconKey]) return iconMap[iconKey]!;\n  return fallback;\n}\n\ntype CategoriesProps = {\n  iconMap: Record<string, IconComponent> | undefined;\n  fallbackIcon: IconComponent;\n  emptyLabel: string;\n};\n\nconst Categories: FC<CategoriesProps> = ({\n  iconMap,\n  fallbackIcon,\n  emptyLabel,\n}) => (\n  <ComposerPrimitive.Unstable_TriggerPopoverCategories>\n    {(categories) => (\n      <div\n        data-slot=\"composer-trigger-popover-categories\"\n        className=\"flex flex-col py-1\"\n      >\n        {categories.map((cat) => {\n          const Icon = resolveIcon(cat.id, iconMap, fallbackIcon);\n          return (\n            <ComposerPrimitive.Unstable_TriggerPopoverCategoryItem\n              key={cat.id}\n              categoryId={cat.id}\n              className=\"hover:bg-accent focus:bg-accent data-[highlighted]:bg-accent flex cursor-pointer items-center justify-between gap-2 px-3 py-2 text-sm transition-colors outline-none\"\n            >\n              <span className=\"flex items-center gap-2\">\n                <Icon className=\"text-muted-foreground size-4\" />\n                {cat.label}\n              </span>\n              <ChevronRightIcon className=\"text-muted-foreground size-4\" />\n            </ComposerPrimitive.Unstable_TriggerPopoverCategoryItem>\n          );\n        })}\n        {categories.length === 0 && (\n          <div className=\"text-muted-foreground px-3 py-2 text-sm\">\n            {emptyLabel}\n          </div>\n        )}\n      </div>\n    )}\n  </ComposerPrimitive.Unstable_TriggerPopoverCategories>\n);\n\ntype ItemsProps = {\n  iconMap: Record<string, IconComponent> | undefined;\n  fallbackIcon: IconComponent;\n  backLabel: string;\n  emptyLabel: string;\n  loadingLabel: string;\n};\n\nconst Items: FC<ItemsProps> = ({\n  iconMap,\n  fallbackIcon,\n  backLabel,\n  emptyLabel,\n  loadingLabel,\n}) => {\n  const { isLoading } = unstable_useTriggerPopoverScopeContext();\n  return (\n    <ComposerPrimitive.Unstable_TriggerPopoverItems>\n      {(items) => (\n        <div\n          data-slot=\"composer-trigger-popover-items\"\n          className=\"flex flex-col\"\n        >\n          <ComposerPrimitive.Unstable_TriggerPopoverBack className=\"text-muted-foreground hover:bg-accent flex cursor-pointer items-center gap-1.5 border-b px-3 py-2 text-xs tracking-wide uppercase transition-colors\">\n            <ChevronLeftIcon className=\"size-3.5\" />\n            {backLabel}\n          </ComposerPrimitive.Unstable_TriggerPopoverBack>\n\n          <div className=\"py-1\">\n            {items.map((item, index) => {\n              const iconKey =\n                typeof item.metadata?.icon === \"string\"\n                  ? item.metadata.icon\n                  : undefined;\n              const Icon = resolveIcon(iconKey, iconMap, fallbackIcon);\n              return (\n                <ComposerPrimitive.Unstable_TriggerPopoverItem\n                  key={item.id}\n                  item={item}\n                  index={index}\n                  className=\"hover:bg-accent focus:bg-accent data-[highlighted]:bg-accent flex w-full cursor-pointer flex-col items-start gap-0.5 px-3 py-2 text-start transition-colors outline-none\"\n                >\n                  <span className=\"flex items-center gap-2 text-sm font-medium\">\n                    <Icon className=\"text-primary size-3.5\" />\n                    {item.label}\n                  </span>\n                  {item.description && (\n                    <span className=\"text-muted-foreground ms-5.5 text-xs leading-tight\">\n                      {item.description}\n                    </span>\n                  )}\n                </ComposerPrimitive.Unstable_TriggerPopoverItem>\n              );\n            })}\n            {items.length === 0 && (\n              <div className=\"text-muted-foreground px-3 py-2 text-sm\">\n                {isLoading ? loadingLabel : emptyLabel}\n              </div>\n            )}\n          </div>\n        </div>\n      )}\n    </ComposerPrimitive.Unstable_TriggerPopoverItems>\n  );\n};\n\n/**\n * Pre-built popover UI for a trigger-driven picker (mentions, slash commands, etc).\n * Pass exactly one of `directive` (inserts a chip) or `action` (fires a handler).\n */\nconst ComposerTriggerPopoverImpl: FC<ComposerTriggerPopoverProps> = ({\n  iconMap,\n  fallbackIcon = SparklesIcon,\n  backLabel = \"Back\",\n  emptyCategoriesLabel = \"No items available\",\n  emptyItemsLabel = \"No matching items\",\n  loadingLabel = \"Loading…\",\n  className,\n  directive,\n  action,\n  ...props\n}) => {\n  const warnedRef = useRef(false);\n  if (\n    process.env.NODE_ENV !== \"production\" &&\n    !warnedRef.current &&\n    Boolean(directive) === Boolean(action)\n  ) {\n    warnedRef.current = true;\n    console.warn(\n      \"[assistant-ui] ComposerTriggerPopover requires exactly one of `directive` or `action` props.\",\n    );\n  }\n\n  return (\n    <ComposerPrimitive.Unstable_TriggerPopover\n      data-slot=\"composer-trigger-popover\"\n      className={cn(\n        \"aui-composer-trigger-popover bg-popover text-popover-foreground absolute start-0 bottom-full z-50 mb-2 w-64 overflow-hidden rounded-xl border\",\n        className,\n      )}\n      {...props}\n    >\n      {directive ? (\n        <ComposerPrimitive.Unstable_TriggerPopover.Directive\n          formatter={directive.formatter ?? unstable_defaultDirectiveFormatter}\n          onInserted={directive.onInserted}\n        />\n      ) : action ? (\n        <ComposerPrimitive.Unstable_TriggerPopover.Action\n          formatter={action.formatter ?? unstable_defaultDirectiveFormatter}\n          onExecute={action.onExecute}\n          removeOnExecute={action.removeOnExecute}\n        />\n      ) : null}\n      <Categories\n        iconMap={iconMap}\n        fallbackIcon={fallbackIcon}\n        emptyLabel={emptyCategoriesLabel}\n      />\n      <Items\n        iconMap={iconMap}\n        fallbackIcon={fallbackIcon}\n        backLabel={backLabel}\n        emptyLabel={emptyItemsLabel}\n        loadingLabel={loadingLabel}\n      />\n    </ComposerPrimitive.Unstable_TriggerPopover>\n  );\n};\nComposerTriggerPopoverImpl.displayName = \"ComposerTriggerPopover\";\n\nexport const ComposerTriggerPopover = memo(\n  ComposerTriggerPopoverImpl,\n) as FC<ComposerTriggerPopoverProps>;\n"
    }
  ]
}