{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "otp-input",
  "title": "OTP Input",
  "description": "One ring glides from slot to slot, digits pop into place and the slots pulse when the code is complete.",
  "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/otp-input.tsx",
      "content": "\"use client\"\n\n/* Animated drop-in for `@ui-sdk/react/otp-input`: same parts, same props. Swap the import and keep\n   your code. This file adds motion only. Colours, radii, shadows and spacing stay yours. */\n\nimport { OTPInput as OTPInputRoot, OTPInputSlot as OTPInputSlotPart } from \"@ui-sdk/react/otp-input\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport * from \"@ui-sdk/react/otp-input\"\n\nconst keyframes =\n  \"@keyframes kf-otp-wave{0%{translate:0 0;scale:1}35%{translate:0 -5px;scale:1.07}100%{translate:0 0;scale:1}}\"\n\n/* Glide: one ring, drawn by the root, glides from slot to slot under CSS anchors instead of\n   each slot lighting up. Its default look is neutral and follows the text colour; restyle it\n   with `before:` classes (radius, shadow). It fades out when the input loses focus. */\nfunction OTPInput({ className, ...props }: React.ComponentProps<typeof OTPInputRoot>) {\n  return (\n    <>\n      <style href=\"kf-otp-wave\" precedence=\"default\">\n        {keyframes}\n      </style>\n      <OTPInputRoot\n        className={cn(\n          \"group/kf-otp isolate [anchor-scope:--kf-otp]\",\n          \"before:pointer-events-none before:absolute before:z-10 before:[position-anchor:--kf-otp]\",\n          \"before:top-[anchor(top)] before:right-[anchor(right)] before:bottom-[anchor(bottom)] before:left-[anchor(left)]\",\n          \"before:rounded-[10px] before:shadow-[0_0_0_2px_color-mix(in_oklch,currentColor_40%,transparent)]\",\n          \"before:scale-75 before:opacity-0 has-data-active:before:scale-100 has-data-active:before:opacity-100\",\n          \"before:[transition:top_calc(340ms*var(--kf-speed,1))_var(--ease-spring),right_calc(340ms*var(--kf-speed,1))_var(--ease-spring),bottom_calc(340ms*var(--kf-speed,1))_var(--ease-spring),left_calc(340ms*var(--kf-speed,1))_var(--ease-spring),scale_calc(360ms*var(--kf-speed,1))_var(--ease-pop),opacity_calc(200ms*var(--kf-speed,1))_var(--ease-house)]\",\n          \"motion-reduce:before:[transition:none]!\",\n          className,\n        )}\n        {...props}\n      />\n    </>\n  )\n}\n\n/* Each typed character rises 12px into place from half size on `ease-pop`. When the code is\n   complete, the slots pulse once, left to right. */\nfunction OTPInputSlot({\n  className,\n  children,\n  index,\n  style,\n  ...props\n}: React.ComponentProps<typeof OTPInputSlotPart>) {\n  const order = { \"--kf-i\": index } as React.CSSProperties\n  return (\n    <OTPInputSlotPart\n      index={index}\n      style={typeof style === \"function\" ? (state) => ({ ...order, ...style(state) }) : { ...order, ...style }}\n      className={cn(\n        \"data-active:[anchor-name:--kf-otp]\",\n        \"group-data-complete/kf-otp:animate-[kf-otp-wave_calc(560ms*var(--kf-speed,1))_var(--ease-house)_calc(var(--kf-i)*calc(60ms*var(--kf-speed,1)))_both]\",\n        \"motion-reduce:animate-none!\",\n        className,\n      )}\n      {...props}\n    >\n      {children ??\n        ((state) =>\n          state.char ? (\n            <span\n              key={state.char}\n              className={cn(\n                \"inline-block [transition:translate_calc(460ms*var(--kf-speed,1))_var(--ease-pop),scale_calc(460ms*var(--kf-speed,1))_var(--ease-pop),opacity_calc(220ms*var(--kf-speed,1))_var(--ease-house),filter_calc(260ms*var(--kf-speed,1))_var(--ease-house)]\",\n                \"starting:translate-y-3 starting:scale-50 starting:opacity-0 starting:blur-[3px] motion-reduce:[transition:none]!\",\n              )}\n            >\n              {state.char}\n            </span>\n          ) : null)}\n    </OTPInputSlotPart>\n  )\n}\n\nexport { OTPInput, OTPInputSlot }\n",
      "type": "registry:component",
      "target": "components/keyframery/primitives/otp-input.tsx"
    },
    {
      "path": "registry/components/keyframery/otp-input.tsx",
      "content": "\"use client\"\n\n/* Styled OTP input: the animated primitive plus the Keyframery look. */\n\nimport * as React from \"react\"\n\nimport {\n  OTPInput as OTPInputPrimitive,\n  OTPInputGroup as OTPInputGroupPart,\n  OTPInputSeparator as OTPInputSeparatorPart,\n  OTPInputSlot as OTPInputSlotPrimitive,\n  createOTPInputController,\n} from \"@/components/keyframery/primitives/otp-input\"\nimport { cn } from \"@/lib/utils\"\n\n/* One invisible native input spans the root and takes every key, paste and autofill.\n   The slots only draw it. The focus ring is the primitive's gliding ring, given the\n   field's ring look here; it turns destructive while the code is invalid. */\nfunction OTPInput({ className, ...props }: React.ComponentProps<typeof OTPInputPrimitive>) {\n  return (\n    <OTPInputPrimitive\n      className={cn(\n        \"group/otp flex w-fit items-center gap-2\",\n        \"before:rounded-[10px] before:shadow-[0_0_0_1px_var(--ring),0_0_0_4px_color-mix(in_oklch,var(--ring)_25%,transparent)]\",\n        \"has-aria-invalid:before:shadow-[0_0_0_1px_var(--destructive),0_0_0_4px_color-mix(in_oklch,var(--destructive)_20%,transparent)]\",\n        \"data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction OTPInputGroup({ className, ...props }: React.ComponentProps<typeof OTPInputGroupPart>) {\n  return <OTPInputGroupPart className={cn(\"flex items-center gap-1.5\", className)} {...props} />\n}\n\n/* Each slot is a small well; the active one brightens under the gliding ring, and an empty\n   active slot shows a soft neutral caret. */\nfunction OTPInputSlot({ className, ...props }: React.ComponentProps<typeof OTPInputSlotPrimitive>) {\n  return (\n    <OTPInputSlotPrimitive\n      className={cn(\n        \"relative grid h-11 w-10 place-items-center rounded-[10px] bg-muted text-[17px] leading-none font-semibold text-foreground tabular-nums\",\n        \"[--well-line:rgb(0_0_0/0.035)] dark:[--well-line:rgb(255_255_255/0.04)]\",\n        \"shadow-[inset_0_1px_2px_rgb(0_0_0/0.07),inset_0_0_0_1px_var(--well-line)] dark:shadow-[inset_0_1px_2px_rgb(0_0_0/0.5),inset_0_0_0_1px_var(--well-line)]\",\n        \"transition-[box-shadow,background-color] duration-[calc(150ms*var(--kf-speed,1))] ease-house motion-reduce:transition-none!\",\n        \"group-hover/otp:[--well-line:rgb(0_0_0/0.09)] dark:group-hover/otp:[--well-line:rgb(255_255_255/0.09)]\",\n        \"group-has-aria-invalid/otp:bg-destructive/[0.06] group-has-aria-invalid/otp:[--well-line:var(--destructive)] dark:group-has-aria-invalid/otp:[--well-line:var(--destructive)]\",\n        \"data-active:bg-background dark:data-active:bg-background\",\n        \"data-disabled:bg-muted\",\n        \"after:pointer-events-none after:absolute after:top-1/2 after:left-1/2 after:h-[18px] after:w-[1.5px] after:-translate-1/2 after:rounded-full after:bg-foreground/45 after:opacity-0\",\n        \"data-fake-caret:after:animate-pulse data-fake-caret:after:opacity-100 data-fake-caret:after:[animation-duration:1.2s] motion-reduce:after:animate-none!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction OTPInputSeparator({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof OTPInputSeparatorPart>) {\n  return (\n    <OTPInputSeparatorPart\n      className={cn(\"grid place-items-center text-muted-foreground/60 [&_svg]:size-3.5\", className)}\n      {...props}\n    >\n      {children ?? (\n        <svg\n          viewBox=\"0 0 16 16\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth=\"1.5\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          aria-hidden=\"true\"\n        >\n          <path d=\"M5 8h6\" />\n        </svg>\n      )}\n    </OTPInputSeparatorPart>\n  )\n}\n\nexport { OTPInput, OTPInputGroup, OTPInputSeparator, OTPInputSlot, createOTPInputController }\n",
      "type": "registry:component",
      "target": "components/keyframery/otp-input.tsx"
    }
  ],
  "type": "registry:component"
}