{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ability-slot",
  "title": "Ability slot",
  "description": "A single subclass/loadout socket — an ability, aspect, fragment, super, or class ability.",
  "dependencies": [
    "@engram/core"
  ],
  "registryDependencies": [
    "@engram/bungie-image",
    "@engram/cn",
    "@engram/item-hover-card",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/ability-slot.tsx",
      "content": "import type { PlugProps } from \"@engram/core\";\nimport type { ButtonHTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { BungieImage } from \"./bungie-image.js\";\nimport { ItemHoverCard } from \"./item-hover-card.js\";\n\nexport type AbilityKind = \"super\" | \"ability\" | \"aspect\" | \"fragment\" | \"class\";\nexport type AbilityShape = \"square\" | \"round\";\n\nexport interface AbilitySlotProps\n  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"type\" | \"name\"> {\n  /** The slotted ability/aspect/fragment; omit for an empty slot. */\n  plug?: PlugProps;\n  /** Force the empty placeholder even without a plug. */\n  empty?: boolean;\n  /** Fired when the slot is clicked (open a picker). */\n  onPick?: () => void;\n  /** Inspect content shown on hover (e.g. a {@link PlugPopup}). When set, the\n   *  slot becomes the trigger of an {@link ItemHoverCard}. */\n  popup?: ReactNode;\n  /** What this slot holds (used for the accessible label). */\n  kind?: AbilityKind;\n  /**\n   * Socket shape. Square (default) holds the square ability/aspect/fragment art\n   * cleanly; `round` is for subclass nodes whose icon art is circular-friendly\n   * (square art would be cropped to the circle).\n   */\n  shape?: AbilityShape;\n  /** Pixel size. */\n  size?: number;\n  /** Highlight as selected. */\n  selected?: boolean;\n}\n\n/**\n * A single subclass/loadout socket — an ability, aspect, fragment, super, or\n * class ability. Shows the inserted plug's icon or an empty \"+\" placeholder.\n * Square by default (the icon fits the box); pass `shape=\"round\"` for round\n * subclass nodes. Clickable to pick.\n */\nexport function AbilitySlot({\n  plug,\n  empty = false,\n  onPick,\n  popup,\n  kind = \"ability\",\n  shape = \"square\",\n  size = 44,\n  selected = false,\n  className,\n  ...props\n}: AbilitySlotProps) {\n  const round = shape === \"round\";\n  const isEmpty = empty || !plug;\n  const slot = (\n    <button\n      type=\"button\"\n      onClick={onPick}\n      title={plug?.name}\n      aria-label={plug?.name ?? `Empty ${kind} slot`}\n      aria-pressed={selected}\n      className={cn(\n        \"grid shrink-0 place-items-center overflow-hidden border transition-colors\",\n        round ? \"rounded-full\" : \"rounded-none\",\n        selected\n          ? \"border-engram-fg bg-engram-raised-2 shadow-[0_0_8px_rgba(255,255,255,0.3)]\"\n          : \"border-engram-border-strong bg-engram-raised hover:border-engram-fg/60\",\n        onPick && \"cursor-pointer\",\n        className,\n      )}\n      style={{ width: size, height: size }}\n      {...props}\n    >\n      {!isEmpty && plug?.icon ? (\n        <BungieImage\n          src={plug.icon}\n          alt=\"\"\n          aria-hidden\n          // Square slots fit the whole icon (no crop); round slots fill the\n          // circle (cover) since their art is circular-friendly.\n          className={cn(\n            \"h-full w-full\",\n            round ? \"object-cover\" : \"object-contain\",\n          )}\n        />\n      ) : (\n        <span\n          aria-hidden\n          className=\"text-engram-faint\"\n          style={{ fontSize: Math.round(size * 0.4), lineHeight: 1 }}\n        >\n          +\n        </span>\n      )}\n    </button>\n  );\n\n  // The slot is itself a <button>, so the hover wrapper renders a <span> to\n  // avoid nesting interactive elements; the slot's own focus/click bubble up.\n  if (popup && !isEmpty)\n    return (\n      <ItemHoverCard as=\"span\" popup={popup}>\n        {slot}\n      </ItemHoverCard>\n    );\n  return slot;\n}\n",
      "type": "registry:component",
      "target": "components/engram/ability-slot.tsx"
    }
  ],
  "meta": {
    "level": "component"
  },
  "docs": "Extend without forking: edit the copied source, use `asChild` (Radix Slot) to change the rendered element, pass the typed `annotations` prop for curated data (verdict/tags/per-plug), or use slot / render-prop props for arbitrary content. Requires the @engram/tokens theme (--engram-* CSS variables).",
  "categories": [
    "loadout"
  ],
  "type": "registry:component"
}