{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "trigger-menu",
  "title": "Trigger Menu",
  "description": "Typing @ or / opens a menu at the cursor, with rows cascading in and a gliding highlight.",
  "dependencies": [
    "@ui-sdk/core@0.1.0-beta.2",
    "@ui-sdk/react@0.1.0-beta.2"
  ],
  "registryDependencies": [
    "@keyframery/motion"
  ],
  "files": [
    {
      "path": "registry/components/keyframery/primitives/trigger-menu.tsx",
      "content": "\"use client\"\n\n/* Animated drop-in for `@ui-sdk/react/trigger-menu`: same parts, same props. Swap the import and keep your code.\n   This file adds motion only. Colours, radii, shadows and spacing stay yours.\n\n   - Content: Unfold from the caret (scale 0.86, 6px blur, 8px toward the line; spring in, quick out).\n   - List: rows Cascade in as the list opens, and one highlight Glides between them as you\n     arrow through (CSS anchor positioning). Its neutral surface follows the text colour; give\n     `before:bg-…` and `before:rounded-…` to restyle it. */\n\nimport {\n  TriggerMenuContent as TriggerMenuContentPart,\n  TriggerMenuItem as TriggerMenuItemPart,\n  TriggerMenuList as TriggerMenuListPart,\n} from \"@ui-sdk/react/trigger-menu\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport * from \"@ui-sdk/react/trigger-menu\"\n\nfunction TriggerMenuContent({ className, ...props }: React.ComponentProps<typeof TriggerMenuContentPart>) {\n  return (\n    <TriggerMenuContentPart\n      className={cn(\n        \"group/kf-tm origin-(--transform-origin)\",\n        \"[transition:opacity_calc(380ms*var(--kf-speed,1))_var(--ease-spring),scale_calc(380ms*var(--kf-speed,1))_var(--ease-spring),translate_calc(380ms*var(--kf-speed,1))_var(--ease-spring),filter_calc(380ms*var(--kf-speed,1))_var(--ease-spring)]\",\n        \"data-[side=bottom]:[--kf-from:0_-8px] data-[side=top]:[--kf-from:0_8px] data-[side=left]:[--kf-from:8px_0] data-[side=right]:[--kf-from:-8px_0]\",\n        \"data-starting-style:[translate:var(--kf-from,0_0)] data-starting-style:scale-[0.86] data-starting-style:opacity-0 data-starting-style:blur-[6px]\",\n        \"data-ending-style:scale-[0.96] data-ending-style:opacity-0 data-ending-style:blur-[2px]\",\n        \"data-ending-style:[transition:opacity_calc(150ms*var(--kf-speed,1))_var(--ease-house),scale_calc(150ms*var(--kf-speed,1))_var(--ease-house),translate_calc(150ms*var(--kf-speed,1))_var(--ease-house),filter_calc(150ms*var(--kf-speed,1))_var(--ease-house)]\",\n        \"motion-reduce:[transition:none]!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TriggerMenuList({ className, ...props }: React.ComponentProps<typeof TriggerMenuListPart>) {\n  return (\n    <TriggerMenuListPart\n      className={cn(\n        /* Cascade: the list's children, one after another, while the content unfolds. */\n        \"[&>*]:transition-[opacity,translate,filter] [&>*]:duration-[calc(420ms*var(--kf-speed,1))] [&>*]:ease-spring\",\n        \"[&>*]:[transition-delay:calc((min(sibling-index(),10)-1)*calc(32ms*var(--kf-speed,1)))]\",\n        \"group-data-starting-style/kf-tm:[&>*]:translate-y-2 group-data-starting-style/kf-tm:[&>*]:opacity-0 group-data-starting-style/kf-tm:[&>*]:blur-[4px]\",\n        \"group-data-ending-style/kf-tm:[&>*]:[transition-delay:0ms]\",\n        \"group-data-starting-style/kf-tm:[&>*]:[transition:none]\",\n        /* Glide */\n        \"relative isolate [anchor-scope:--kf-hl]\",\n        \"before:pointer-events-none before:absolute before:-z-10 before:hidden before:bg-[color-mix(in_oklch,currentColor_9%,transparent)]\",\n        \"before:[position-anchor:--kf-hl] before:top-[anchor(top)] before:right-[anchor(right)] before:bottom-[anchor(bottom)] before:left-[anchor(left)]\",\n        \"before:[transition:top_calc(260ms*var(--kf-speed,1))_var(--ease-spring),right_calc(260ms*var(--kf-speed,1))_var(--ease-spring),bottom_calc(260ms*var(--kf-speed,1))_var(--ease-spring),left_calc(260ms*var(--kf-speed,1))_var(--ease-spring),opacity_calc(150ms*var(--kf-speed,1))_var(--ease-house)]\",\n        \"has-[[data-highlighted]]:before:block starting:before:opacity-0\",\n        \"motion-reduce:[&>*]:[transition:none] motion-reduce:before:[transition:none]!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TriggerMenuItem<T = string>({ className, ...props }: React.ComponentProps<typeof TriggerMenuItemPart<T>>) {\n  return <TriggerMenuItemPart<T> className={cn(\"data-highlighted:[anchor-name:--kf-hl]\", className)} {...props} />\n}\n\nexport { TriggerMenuContent, TriggerMenuItem, TriggerMenuList }\n",
      "type": "registry:component",
      "target": "components/keyframery/primitives/trigger-menu.tsx"
    },
    {
      "path": "registry/components/keyframery/trigger-menu.tsx",
      "content": "\"use client\"\n\n/* Styled trigger menu: the animated primitive plus the Keyframery look. */\n\nimport {\n  createTriggerMenuController,\n  textFieldAdapter,\n  TriggerMenu as TriggerMenuRoot,\n  TriggerMenuContent as TriggerMenuContentPart,\n  type TriggerMenuData,\n  TriggerMenuEmpty as TriggerMenuEmptyPart,\n  TriggerMenuGroup as TriggerMenuGroupPart,\n  TriggerMenuGroupLabel as TriggerMenuGroupLabelPart,\n  TriggerMenuHint as TriggerMenuHintPart,\n  TriggerMenuInput as TriggerMenuInputPart,\n  TriggerMenuItem as TriggerMenuItemPart,\n  TriggerMenuItemIcon as TriggerMenuItemIconPart,\n  TriggerMenuList as TriggerMenuListPart,\n  type TriggerMenuProps,\n  TriggerMenuSeparator as TriggerMenuSeparatorPart,\n  TriggerMenuStatus as TriggerMenuStatusPart,\n} from \"@/components/keyframery/primitives/trigger-menu\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* The sheet. Its motion comes from the primitive. */\nconst sheet = cn(\n  \"group/content m-0 flex w-64 flex-col border-0 p-0 outline-none\",\n  \"[--list-height:var(--available-height)]\",\n  \"rounded-[14px] bg-popover text-popover-foreground\",\n  \"shadow-[0_0_0_1px_rgb(0_0_0/0.06),0_2px_6px_-2px_rgb(0_0_0/0.08),0_12px_32px_-8px_rgb(0_0_0/0.18)]\",\n  \"dark:shadow-[0_0_0_1px_rgb(255_255_255/0.08),0_12px_32px_-8px_rgb(0_0_0/0.7)]\",\n)\n\n/* One row per suggestion. Focus stays in the field; the list's gliding highlight is the cursor. */\nconst row = cn(\n  \"group/item relative flex min-h-8 cursor-default items-center gap-2 rounded-[8px] px-2 py-1.5 text-[13px] leading-4 outline-none select-none\",\n  \"data-highlighted:text-accent-foreground\",\n  \"data-disabled:text-muted-foreground/60 data-disabled:data-highlighted:text-muted-foreground/60\",\n  /* Rows without an icon keep the icon column when another row in the list has one. */\n  \"[[data-has-icons]_&:not([data-has-icon])]:ps-8\",\n)\n\nfunction TriggerMenu<T = string>(props: TriggerMenuProps<T>) {\n  return <TriggerMenuRoot<T> {...props} />\n}\n\n/* The field people type in: a textarea by default, an input through `render`. */\nfunction TriggerMenuInput({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuInputPart>) {\n  return (\n    <TriggerMenuInputPart\n      className={cn(\n        \"block min-h-9 w-full min-w-0 resize-none rounded-[10px] bg-background px-3 py-2 text-[13px] leading-5 text-foreground outline-none placeholder:text-muted-foreground\",\n        \"[field-sizing:content] [&:is(textarea)]:min-h-[84px]\",\n        \"shadow-[0_0_0_1px_rgb(0_0_0/0.09),0_1px_2px_rgb(0_0_0/0.05)] dark:shadow-[0_0_0_1px_rgb(255_255_255/0.1)]\",\n        \"transition-shadow duration-[calc(150ms*var(--kf-speed,1))] motion-reduce:transition-none!\",\n        \"hover:shadow-[0_0_0_1px_rgb(0_0_0/0.16),0_1px_2px_rgb(0_0_0/0.05)] dark:hover:shadow-[0_0_0_1px_rgb(255_255_255/0.16)]\",\n        \"focus-visible:shadow-[0_0_0_1px_var(--ring)] focus-visible:ring-[3px] focus-visible:ring-ring/25 dark:focus-visible:shadow-[0_0_0_1px_var(--ring)]\",\n        \"aria-invalid:shadow-[0_0_0_1px_var(--destructive)] aria-invalid:focus-visible:ring-destructive/25 dark:aria-invalid:shadow-[0_0_0_1px_var(--destructive)]\",\n        \"disabled:cursor-not-allowed disabled:opacity-50 read-only:bg-muted/40\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TriggerMenuContent({\n  className,\n  children,\n  sideOffset = 6,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuContentPart>) {\n  return (\n    <TriggerMenuContentPart\n      sideOffset={sideOffset}\n      className={cn(sheet, className)}\n      {...props}\n    >\n      {children ?? <TriggerMenuList />}\n    </TriggerMenuContentPart>\n  )\n}\n\nfunction TriggerMenuList({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuListPart>) {\n  return (\n    <TriggerMenuListPart\n      className={cn(\n        \"max-h-[min(calc(var(--list-height,100vh)-8px),18rem)] scroll-py-1.5 overflow-y-auto overscroll-contain p-1.5 outline-none\",\n        \"aria-busy:opacity-60 before:rounded-[8px] before:bg-accent\",\n        \"has-[[data-disabled][data-highlighted]]:before:bg-accent/50\",\n        \"group-has-[[data-slot=trigger-menu-empty][data-empty]]/content:hidden\",\n        className,\n      )}\n      {...props}\n    >\n      {children ?? ((state) => <TriggerMenuItems items={state.items} />)}\n    </TriggerMenuListPart>\n  )\n}\n\nfunction TriggerMenuItem<T = string>({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuItemPart<T>>) {\n  return <TriggerMenuItemPart<T> className={cn(row, className)} {...props} />\n}\n\nfunction TriggerMenuItemIcon({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuItemIconPart>) {\n  return (\n    <TriggerMenuItemIconPart\n      className={cn(\n        \"flex size-4 shrink-0 items-center justify-center text-muted-foreground [&>svg]:size-4\",\n        \"group-data-highlighted/item:text-current\",\n        \"rtl:data-mirror:-scale-x-100\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TriggerMenuGroup({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuGroupPart>) {\n  return <TriggerMenuGroupPart className={cn(\"flex flex-col\", className)} {...props} />\n}\n\nfunction TriggerMenuGroupLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuGroupLabelPart>) {\n  return (\n    <TriggerMenuGroupLabelPart\n      className={cn(\n        \"px-2 pt-1.5 pb-1 text-[11px] leading-4 font-medium text-muted-foreground select-none\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TriggerMenuSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuSeparatorPart>) {\n  return (\n    <TriggerMenuSeparatorPart\n      className={cn(\"-mx-1.5 my-1.5 h-px bg-border\", className)}\n      {...props}\n    />\n  )\n}\n\n/* Shown while nothing matches. Rendering it keeps the menu open with no matches. */\nfunction TriggerMenuEmpty({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuEmptyPart>) {\n  return (\n    <TriggerMenuEmptyPart\n      className={cn(\n        \"hidden px-3 py-5 text-center text-[13px] text-muted-foreground data-empty:block data-loading:block\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/* A polite live region. Hidden from sight by default, heard by screen readers. */\nfunction TriggerMenuStatus({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuStatusPart>) {\n  return <TriggerMenuStatusPart className={cn(\"sr-only\", className)} {...props} />\n}\n\n/* Ghost text at the caret for the arguments still to type. */\nfunction TriggerMenuHint({\n  className,\n  ...props\n}: React.ComponentProps<typeof TriggerMenuHintPart>) {\n  return (\n    <TriggerMenuHintPart\n      className={cn(\"pointer-events-none text-[13px] text-muted-foreground/70 select-none\", className)}\n      {...props}\n    />\n  )\n}\n\n/* Renders suggestions passed as data with the styled parts above. */\nfunction TriggerMenuItems({ items }: { items?: TriggerMenuData<unknown> }) {\n  return (items ?? []).map((entry, index) => {\n    if (typeof entry === \"string\") {\n      return (\n        <TriggerMenuItem key={entry} value={entry} label={entry}>\n          {entry}\n        </TriggerMenuItem>\n      )\n    }\n    if (\"type\" in entry && entry.type === \"separator\") {\n      return <TriggerMenuSeparator key={`separator-${index}`} />\n    }\n    if (\"type\" in entry && entry.type === \"group\") {\n      return (\n        <TriggerMenuGroup key={`group-${entry.label ?? index}`}>\n          {entry.label && <TriggerMenuGroupLabel>{entry.label}</TriggerMenuGroupLabel>}\n          <TriggerMenuItems items={entry.items} />\n        </TriggerMenuGroup>\n      )\n    }\n    const { value, label, disabled, keywords } = entry\n    return (\n      <TriggerMenuItem<unknown>\n        key={typeof value === \"string\" ? value : index}\n        value={value}\n        label={label}\n        disabled={disabled}\n        keywords={keywords}\n      >\n        {label}\n      </TriggerMenuItem>\n    )\n  })\n}\n\nexport type { TriggerMenuData, TriggerMenuDataItem } from \"@/components/keyframery/primitives/trigger-menu\"\nexport {\n  createTriggerMenuController,\n  textFieldAdapter,\n  TriggerMenu,\n  TriggerMenuContent,\n  TriggerMenuEmpty,\n  TriggerMenuGroup,\n  TriggerMenuGroupLabel,\n  TriggerMenuHint,\n  TriggerMenuInput,\n  TriggerMenuItem,\n  TriggerMenuItemIcon,\n  TriggerMenuItems,\n  TriggerMenuList,\n  TriggerMenuSeparator,\n  TriggerMenuStatus,\n}\n",
      "type": "registry:component",
      "target": "components/keyframery/trigger-menu.tsx"
    }
  ],
  "type": "registry:component"
}