{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bounding-boxes",
  "title": "Bounding Boxes",
  "description": "Selectable regions drawn over a page image from normalized coordinates.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/bounding-boxes/bounding-boxes.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\n\nexport type BoundingBoxTone = \"default\" | \"accent\" | \"warning\"\n\nexport type BoundingBox = {\n  id: string\n  label?: string\n  tone?: BoundingBoxTone\n  x: number\n  y: number\n  width: number\n  height: number\n}\n\nexport type BoundingBoxesProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\" | \"onSelect\"\n> & {\n  src: string\n  alt: string\n  boxes: BoundingBox[]\n  activeId?: string | null\n  defaultActiveId?: string | null\n  onActiveChange?: (id: string | null) => void\n  showLabels?: boolean\n  groupLabel?: string\n  renderImage?: (props: {\n    src: string\n    alt: string\n    className: string\n  }) => React.ReactNode\n}\n\nconst toneClasses: Record<BoundingBoxTone, string> = {\n  default: \"border-primary bg-primary/10\",\n  accent: \"border-accent bg-accent/10\",\n  warning: \"border-destructive bg-destructive/10\",\n}\n\nfunction toPercent(value: number) {\n  return `${Math.max(0, Math.min(1, value)) * 100}%`\n}\n\nexport function BoundingBoxes({\n  src,\n  alt,\n  boxes,\n  activeId,\n  defaultActiveId = null,\n  onActiveChange,\n  showLabels = true,\n  groupLabel = \"Highlighted regions\",\n  renderImage,\n  className,\n  ...rootProps\n}: BoundingBoxesProps) {\n  const [uncontrolledId, setUncontrolledId] = React.useState(defaultActiveId)\n  const selected = activeId === undefined ? uncontrolledId : activeId\n\n  const select = (id: string) => {\n    const next = selected === id ? null : id\n    if (activeId === undefined) setUncontrolledId(next)\n    onActiveChange?.(next)\n  }\n\n  const imageClassName = \"block h-auto w-full select-none\"\n\n  return (\n    <div\n      data-slot=\"bounding-boxes\"\n      className={cn(\n        \"border-border bg-muted relative overflow-hidden rounded-[var(--radius)] border\",\n        className\n      )}\n      {...rootProps}\n    >\n      {renderImage ? (\n        renderImage({ src, alt, className: imageClassName })\n      ) : (\n        // eslint-disable-next-line @next/next/no-img-element\n        <img alt={alt} className={imageClassName} src={src} />\n      )}\n\n      <ul\n        data-slot=\"bounding-boxes-list\"\n        aria-label={groupLabel}\n        className=\"absolute inset-0 m-0 list-none p-0\"\n      >\n        {boxes.map((box) => {\n          const isActive = box.id === selected\n          const tone = toneClasses[box.tone ?? \"default\"]\n\n          return (\n            <li\n              key={box.id}\n              className=\"absolute\"\n              style={{\n                left: toPercent(box.x),\n                top: toPercent(box.y),\n                width: toPercent(box.width),\n                height: toPercent(box.height),\n              }}\n            >\n              <button\n                type=\"button\"\n                data-slot=\"bounding-box\"\n                data-active={isActive || undefined}\n                aria-pressed={isActive}\n                className={cn(\n                  \"focus-visible:ring-ring size-full rounded-[3px] border-2 transition-opacity duration-150 focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\",\n                  tone,\n                  isActive ? \"opacity-100\" : \"opacity-60 hover:opacity-100\"\n                )}\n                onClick={() => select(box.id)}\n              >\n                <span className=\"sr-only\">{box.label ?? \"Region\"}</span>\n              </button>\n\n              {showLabels && box.label ? (\n                <span\n                  aria-hidden=\"true\"\n                  data-slot=\"bounding-box-label\"\n                  className={cn(\n                    \"bg-foreground text-background pointer-events-none absolute -top-0.5 left-0 -translate-y-full rounded-t-[3px] px-1.5 py-0.5 text-[0.65rem] font-semibold whitespace-nowrap\",\n                    !isActive && \"opacity-70\"\n                  )}\n                >\n                  {box.label}\n                </span>\n              ) : null}\n            </li>\n          )\n        })}\n      </ul>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "document",
    "citations",
    "ocr",
    "ai"
  ],
  "type": "registry:ui"
}