{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "state-badge-overlay",
  "type": "registry:component",
  "title": "State Badge Overlay",
  "description": "State chip pinned to a canvas object's corner — idle, queued, running, complete, failed, stopped.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/state-badge-overlay/state-badge-overlay.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * State name — drives the badge tone.\n *\n * @public\n */\nexport type StateBadgeState =\n  | \"complete\"\n  | \"failed\"\n  | \"idle\"\n  | \"queued\"\n  | \"running\"\n  | \"stopped\";\n\nconst STATE_LABEL: Record<StateBadgeState, string> = {\n  complete: \"Complete\",\n  failed: \"Failed\",\n  idle: \"Idle\",\n  queued: \"Queued\",\n  running: \"Running\",\n  stopped: \"Stopped\",\n};\n\nconst STATE_TONE: Record<StateBadgeState, string> = {\n  complete:\n    \"border-emerald-500/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300\",\n  failed: \"border-red-500/40 bg-red-500/15 text-red-700 dark:text-red-300\",\n  idle: \"border-border bg-muted/40 text-muted-foreground\",\n  queued:\n    \"border-amber-500/40 bg-amber-500/15 text-amber-700 dark:text-amber-300\",\n  running: \"border-blue-500/40 bg-blue-500/15 text-blue-700 dark:text-blue-300\",\n  stopped: \"border-border bg-background text-foreground\",\n};\n\nconst STATE_DOT: Record<StateBadgeState, string> = {\n  complete: \"bg-emerald-500\",\n  failed: \"bg-red-500\",\n  idle: \"bg-muted-foreground\",\n  queued: \"bg-amber-500\",\n  running: \"bg-blue-500 animate-pulse\",\n  stopped: \"bg-muted-foreground\",\n};\n\n/**\n * Anchor corner relative to the canvas object the badge attaches to.\n *\n * @public\n */\nexport type StateBadgeAnchor =\n  | \"bottom-left\"\n  | \"bottom-right\"\n  | \"top-left\"\n  | \"top-right\";\n\nconst ANCHOR_TRANSFORM: Record<StateBadgeAnchor, string> = {\n  \"bottom-left\": \"translate(-100%, 100%)\",\n  \"bottom-right\": \"translate(0%, 100%)\",\n  \"top-left\": \"translate(-100%, -100%)\",\n  \"top-right\": \"translate(0%, -100%)\",\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type StateBadgeOverlayLabels = {\n  /** Aria-label override. Defaults to `\"State\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  region: \"State\",\n} as const satisfies Required<StateBadgeOverlayLabels>;\n\n/**\n * Props for {@link StateBadgeOverlay}.\n *\n * @public\n */\nexport type StateBadgeOverlayProps = {\n  /** Anchor corner. Defaults to `\"top-right\"`. */\n  anchor?: StateBadgeAnchor;\n  /** Optional override label (defaults to humanized state name). */\n  label?: ReactNode;\n  /** Localizable strings. */\n  labels?: StateBadgeOverlayLabels;\n  /** State to display. */\n  state: StateBadgeState;\n  /** Anchor X in canvas pixels. */\n  x: number;\n  /** Anchor Y in canvas pixels. */\n  y: number;\n} & ComponentPropsWithoutRef<\"div\">;\n\n/**\n * State chip pinned to a canvas object's corner. Use when a single-\n * letter glyph in `ObjectCard` doesn't carry enough signal — for\n * runs that have transitioned, jobs that failed, agents idling. Pure\n * presentation; the host computes the anchor from the object's\n * bounding box.\n *\n * The wrapper is `pointer-events: none` — host gestures pass through.\n *\n * @example\n * ```tsx\n * <div className=\"relative h-screen w-screen\">\n *   <Canvas />\n *   <StateBadgeOverlay state=\"running\" x={420} y={180} anchor=\"top-right\" />\n * </div>\n * ```\n *\n * @public\n */\nexport const StateBadgeOverlay = forwardRef<\n  HTMLDivElement,\n  StateBadgeOverlayProps\n>((props, ref) => {\n  const {\n    anchor = \"top-right\",\n    className,\n    label,\n    labels,\n    state,\n    x,\n    y,\n    ...rest\n  } = props;\n  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n  const text = label ?? STATE_LABEL[state];\n  return (\n    <div\n      aria-label={`${resolvedLabels.region}: ${STATE_LABEL[state]}`}\n      className={cn(\n        \"pointer-events-none absolute z-20 inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide shadow-sm backdrop-blur\",\n        STATE_TONE[state],\n        className,\n      )}\n      data-state-anchor={anchor}\n      data-state-badge-overlay\n      data-state-name={state}\n      ref={ref}\n      role=\"status\"\n      style={{\n        left: x,\n        top: y,\n        transform: ANCHOR_TRANSFORM[anchor],\n      }}\n      {...rest}\n    >\n      <span\n        aria-hidden=\"true\"\n        className={cn(\"size-1.5 rounded-full\", STATE_DOT[state])}\n        data-state-badge-dot\n      />\n      {text}\n    </div>\n  );\n});\nStateBadgeOverlay.displayName = \"StateBadgeOverlay\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
