{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "playback-ghost",
  "type": "registry:component",
  "title": "Playback Ghost",
  "description": "Translucent overlay marking where a canvas object was at a previous timestamp during playback.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/playback-ghost/playback-ghost.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 * Object kind — drives the ghost glyph.\n *\n * @public\n */\nexport type PlaybackGhostKind =\n  | \"agent\"\n  | \"artifact\"\n  | \"input\"\n  | \"output\"\n  | \"run\"\n  | \"task\";\n\nconst KIND_GLYPH: Record<PlaybackGhostKind, string> = {\n  agent: \"◇\",\n  artifact: \"◌\",\n  input: \"↘\",\n  output: \"↗\",\n  run: \"▶\",\n  task: \"▢\",\n};\n\nconst KIND_LABEL: Record<PlaybackGhostKind, string> = {\n  agent: \"Agent\",\n  artifact: \"Artifact\",\n  input: \"Input\",\n  output: \"Output\",\n  run: \"Run\",\n  task: \"Task\",\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type PlaybackGhostLabels = {\n  /** Aria-label override. Defaults to `\"Playback ghost\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  region: \"Playback ghost\",\n} as const satisfies Required<PlaybackGhostLabels>;\n\n/**\n * Props for {@link PlaybackGhost}.\n *\n * @public\n */\nexport type PlaybackGhostProps = {\n  /** Optional kind glyph + tooltip. */\n  kind?: PlaybackGhostKind;\n  /** Optional label rendered next to the glyph (e.g. id, run name). */\n  label?: ReactNode;\n  /** Localizable strings. */\n  labels?: PlaybackGhostLabels;\n  /** Ghost opacity `0..1`. Defaults to `0.4`. */\n  opacity?: number;\n  /** Ghost size in pixels. Defaults to `40`. */\n  size?: number;\n  /** Center X in canvas pixels. */\n  x: number;\n  /** Center Y in canvas pixels. */\n  y: number;\n} & ComponentPropsWithoutRef<\"div\">;\n\nconst clamp01 = (v: number): number => {\n  if (v < 0) {\n    return 0;\n  }\n  if (v > 1) {\n    return 1;\n  }\n  return v;\n};\n\n/**\n * Translucent overlay marking where a canvas object was at a previous\n * timestamp during state playback. Renders a kind glyph (and optional\n * label) at the historical position so the user can compare the\n * present canvas against earlier state without losing context.\n *\n * Pure presentation; the host computes the historical position from the\n * scrubbed timestamp. The wrapper is `pointer-events: none` so host\n * gestures pass through.\n *\n * @example\n * ```tsx\n * <div className=\"relative h-screen w-screen\">\n *   <Canvas />\n *   <PlaybackGhost x={420} y={180} kind=\"run\" label=\"research-2025\" />\n * </div>\n * ```\n *\n * @public\n */\nexport const PlaybackGhost = forwardRef<HTMLDivElement, PlaybackGhostProps>(\n  (props, ref) => {\n    const {\n      className,\n      kind,\n      label,\n      labels,\n      opacity = 0.4,\n      size = 40,\n      x,\n      y,\n      ...rest\n    } = props;\n    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n    const safeOpacity = clamp01(opacity);\n    const safeSize = size < 16 ? 16 : size;\n    const ariaLabel = kind\n      ? `${resolvedLabels.region}: ${KIND_LABEL[kind]}`\n      : resolvedLabels.region;\n    return (\n      <div\n        aria-label={ariaLabel}\n        className={cn(\n          \"pointer-events-none absolute z-10 inline-flex -translate-x-1/2 -translate-y-1/2 items-center justify-center gap-1.5 rounded-md border border-dashed border-border/70 bg-background/40 px-2 py-1 text-xs text-foreground backdrop-blur-sm\",\n          className,\n        )}\n        data-playback-ghost\n        data-playback-kind={kind ?? \"unknown\"}\n        ref={ref}\n        role=\"img\"\n        style={{\n          left: x,\n          minHeight: safeSize,\n          minWidth: safeSize,\n          opacity: safeOpacity,\n          top: y,\n        }}\n        {...rest}\n      >\n        {kind ? (\n          <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n            {KIND_GLYPH[kind]}\n          </span>\n        ) : null}\n        {label ? (\n          <span className=\"truncate\" data-playback-ghost-label>\n            {label}\n          </span>\n        ) : null}\n      </div>\n    );\n  },\n);\nPlaybackGhost.displayName = \"PlaybackGhost\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
