{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "x-ray-scan",
  "title": "X-Ray Scan",
  "description": "A glowing scanner bar sweeps across a device, turning the shell dark and revealing white-line internals with one part in red.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/x-ray-scan/index.tsx",
      "content": "\"use client\";\n\nimport { type ReactNode, useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport type XRayScanColors = {\n  background: string;\n  shell: string;\n  detail: string;\n  xray: string;\n  line: string;\n  accent: string;\n};\n\nexport type XRayScanProps = {\n  /** Exterior shown ahead of the bar. Defaults to the back of a phone. */\n  outside?: ReactNode;\n  /** Interior revealed behind the bar. Defaults to white-line phone internals. */\n  inside?: ReactNode;\n  colors?: Partial<XRayScanColors>;\n  /** Phone shell color. Overrides `colors.shell` when set. */\n  shell?: string;\n  /** Highlighted internal part. Overrides `colors.accent` when set. */\n  accent?: string;\n  /** Scan line and interior line art. Overrides `colors.line` when set. */\n  line?: string;\n  /** Background. Overrides `colors.background` when set. */\n  background?: string;\n  /** \"horizontal\" sweeps left to right, \"vertical\" top to bottom. */\n  direction?: \"horizontal\" | \"vertical\";\n  speed?: number;\n};\n\nexport const xRayScanDefaults = {\n  colors: {\n    background: \"#111111\",\n    shell: \"#E5322D\",\n    detail: \"#111111\",\n    xray: \"#07080A\",\n    line: \"#FFFFFF\",\n    accent: \"#E5322D\",\n  } as XRayScanColors,\n  direction: \"horizontal\" as const,\n  speed: 1,\n};\n\nexport const xRayScanLength = 32;\n\nconst MONO = \"ui-monospace, 'SF Mono', Menlo, Consolas, monospace\";\n\ntype Device = { x: number; y: number; w: number; h: number; r: number };\n\nfunction Outside({ id, d, c }: { id: string; d: Device; c: XRayScanColors }) {\n  const lens = d.w * 0.085;\n  return (\n    <>\n      <defs>\n        <linearGradient id={`${id}-gloss`} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">\n          <stop offset=\"0\" stopColor=\"#FFFFFF\" stopOpacity={0.22} />\n          <stop offset=\"0.45\" stopColor=\"#FFFFFF\" stopOpacity={0} />\n          <stop offset=\"1\" stopColor=\"#000000\" stopOpacity={0.18} />\n        </linearGradient>\n      </defs>\n      <rect x={d.x} y={d.y} width={d.w} height={d.h} rx={d.r} fill={c.shell} />\n      <rect\n        x={d.x}\n        y={d.y}\n        width={d.w}\n        height={d.h}\n        rx={d.r}\n        fill={`url(#${id}-gloss)`}\n      />\n      <rect\n        x={d.x + d.w * 0.07}\n        y={d.y + d.w * 0.07}\n        width={d.w * 0.4}\n        height={d.w * 0.4}\n        rx={d.w * 0.1}\n        fill={c.detail}\n        opacity={0.92}\n      />\n      {[\n        [0.17, 0.17],\n        [0.37, 0.17],\n        [0.17, 0.37],\n      ].map(([fx, fy]) => (\n        <g key={`${fx}${fy}`}>\n          <circle\n            cx={d.x + d.w * fx}\n            cy={d.y + d.w * fy}\n            r={lens}\n            fill=\"#000\"\n            stroke=\"#2A2A2A\"\n            strokeWidth={lens * 0.25}\n          />\n          <circle\n            cx={d.x + d.w * fx - lens * 0.3}\n            cy={d.y + d.w * fy - lens * 0.3}\n            r={lens * 0.18}\n            fill=\"#FFFFFF\"\n            opacity={0.6}\n          />\n        </g>\n      ))}\n      <circle\n        cx={d.x + d.w / 2}\n        cy={d.y + d.h * 0.5}\n        r={d.w * 0.07}\n        fill={c.detail}\n        opacity={0.85}\n      />\n    </>\n  );\n}\n\nfunction Inside({ d, c, u }: { d: Device; c: XRayScanColors; u: number }) {\n  const X = (f: number) => d.x + d.w * f;\n  const Y = (f: number) => d.y + d.h * f;\n  const pins = (x: number, y: number, w: number, h: number, n: number) =>\n    Array.from({ length: n }, (_, i) => {\n      const px = x + ((i + 0.5) / n) * w;\n      return `M ${px} ${y - 4 * u} V ${y} M ${px} ${y + h} V ${y + h + 4 * u}`;\n    }).join(\" \");\n  return (\n    <>\n      <rect x={d.x} y={d.y} width={d.w} height={d.h} rx={d.r} fill={c.xray} />\n      <g fill=\"none\" stroke={c.line} strokeWidth={2 * u} strokeLinejoin=\"round\">\n        <rect x={d.x} y={d.y} width={d.w} height={d.h} rx={d.r} />\n        <rect\n          x={X(0.04)}\n          y={Y(0.02)}\n          width={d.w * 0.92}\n          height={d.h * 0.96}\n          rx={d.r * 0.82}\n          strokeOpacity={0.4}\n        />\n        {/* Camera ring */}\n        {[\n          [0.17, 0.17],\n          [0.37, 0.17],\n          [0.17, 0.37],\n        ].map(([fx, fy]) => (\n          <g key={`${fx}${fy}`}>\n            <circle cx={X(fx)} cy={d.y + d.w * fy} r={d.w * 0.085} />\n            <circle\n              cx={X(fx)}\n              cy={d.y + d.w * fy}\n              r={d.w * 0.045}\n              strokeOpacity={0.6}\n            />\n          </g>\n        ))}\n        {/* Logic board */}\n        <path\n          d={`M ${X(0.52)} ${Y(0.05)} H ${X(0.9)} V ${Y(0.36)} H ${X(0.1)} V ${Y(0.27)} H ${X(0.52)} Z`}\n        />\n        <rect\n          x={X(0.6)}\n          y={Y(0.08)}\n          width={d.w * 0.22}\n          height={d.h * 0.07}\n          rx={2 * u}\n        />\n        <path\n          d={pins(X(0.6), Y(0.08), d.w * 0.22, d.h * 0.07, 6)}\n          strokeOpacity={0.6}\n          strokeWidth={1.3 * u}\n        />\n        <rect\n          x={X(0.16)}\n          y={Y(0.295)}\n          width={d.w * 0.16}\n          height={d.h * 0.04}\n          rx={2 * u}\n          strokeOpacity={0.8}\n        />\n        <rect\n          x={X(0.38)}\n          y={Y(0.295)}\n          width={d.w * 0.12}\n          height={d.h * 0.04}\n          rx={2 * u}\n          strokeOpacity={0.8}\n        />\n        {/* Battery with wireless coil */}\n        <rect\n          x={X(0.12)}\n          y={Y(0.4)}\n          width={d.w * 0.76}\n          height={d.h * 0.44}\n          rx={6 * u}\n        />\n        <path\n          d={`M ${X(0.42)} ${Y(0.4)} V ${Y(0.385)} H ${X(0.58)} V ${Y(0.4)}`}\n        />\n        {[0.26, 0.2, 0.14].map((f) => (\n          <circle\n            key={f}\n            cx={X(0.5)}\n            cy={Y(0.62)}\n            r={d.w * f}\n            strokeOpacity={0.45}\n          />\n        ))}\n        <path\n          d={`M ${X(0.5)} ${Y(0.47)} V ${Y(0.5)} M ${X(0.485)} ${Y(0.485)} H ${X(0.515)}`}\n        />\n        {/* Flex cable, speaker and port */}\n        <path\n          d={`M ${X(0.86)} ${Y(0.36)} V ${Y(0.38)} H ${X(0.92)} V ${Y(0.89)} H ${X(0.66)}`}\n          strokeOpacity={0.6}\n        />\n        <rect\n          x={X(0.38)}\n          y={Y(0.935)}\n          width={d.w * 0.24}\n          height={d.h * 0.025}\n          rx={d.h * 0.0125}\n        />\n        {Array.from({ length: 6 }, (_, i) => (\n          <circle key={i} cx={X(0.14 + i * 0.035)} cy={Y(0.947)} r={1.8 * u} />\n        ))}\n      </g>\n      {/* The one part in red: the SoC */}\n      <g stroke={c.accent} strokeWidth={2.4 * u} fill=\"none\">\n        <rect\n          x={X(0.6)}\n          y={Y(0.17)}\n          width={d.w * 0.22}\n          height={d.w * 0.22}\n          rx={3 * u}\n          fill={c.accent}\n          fillOpacity={0.22}\n        />\n        <path\n          d={pins(X(0.6), Y(0.17), d.w * 0.22, d.w * 0.22, 5)}\n          strokeWidth={1.4 * u}\n        />\n      </g>\n      <text\n        x={X(0.5)}\n        y={Y(0.79)}\n        textAnchor=\"middle\"\n        fontFamily={MONO}\n        fontSize={12 * u}\n        letterSpacing={2 * u}\n        fill={c.line}\n        opacity={0.7}\n      >\n        Li-ion 4500 mAh\n      </text>\n    </>\n  );\n}\n\nexport function XRayScan({\n  outside,\n  inside,\n  colors,\n  shell,\n  accent,\n  line,\n  background,\n  direction = xRayScanDefaults.direction,\n  speed = xRayScanDefaults.speed,\n}: XRayScanProps) {\n  const frame = useCurrentFrame();\n  const { width: W, height: H, fps } = useVideoConfig();\n  const id = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const merged = { ...xRayScanDefaults.colors, ...colors };\n  const c: XRayScanColors = {\n    ...merged,\n    shell: shell ?? merged.shell,\n    accent: accent ?? merged.accent,\n    line: line ?? merged.line,\n    background: background ?? merged.background,\n  };\n  const t = (frame / fps) * 30 * speed;\n  const u = Math.min(W, H) / 720;\n  const vertical = direction === \"vertical\";\n\n  const dh = 0.8 * H;\n  const dw = dh * 0.49;\n  const d: Device = {\n    x: (W - dw) / 2,\n    y: (H - dh) / 2,\n    w: dw,\n    h: dh,\n    r: dw * 0.17,\n  };\n  const span = vertical ? H : W;\n  const start = vertical ? d.y - 0.1 * H : d.x - 0.12 * W;\n  const end = vertical ? d.y + d.h + 0.1 * H : d.x + d.w + 0.12 * W;\n  const bar = interpolate(t, [5, 32], [start, end], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.inOut(Easing.sin),\n  });\n  const scanning = t > 5 && t < 32;\n  const band = 0.12 * span;\n  const behind = vertical\n    ? `inset(0 0 ${H - bar}px 0)`\n    : `inset(0 ${W - bar}px 0 0)`;\n  const ahead = vertical ? `inset(${bar}px 0 0 0)` : `inset(0 0 0 ${bar}px)`;\n  const hot = vertical\n    ? `inset(${Math.max(0, bar - band)}px 0 ${H - bar}px 0)`\n    : `inset(0 ${W - bar}px 0 ${Math.max(0, bar - band)}px)`;\n  const trail = vertical ? \"to top\" : \"to left\";\n\n  const insideLayer = inside ?? (\n    <svg width={W} height={H} aria-hidden>\n      <Inside d={d} c={c} u={u} />\n    </svg>\n  );\n\n  return (\n    <AbsoluteFill style={{ backgroundColor: c.background, overflow: \"hidden\" }}>\n      <svg width={0} height={0} style={{ position: \"absolute\" }} aria-hidden>\n        <filter id={`${id}-glow`} x=\"-10%\" y=\"-10%\" width=\"120%\" height=\"120%\">\n          <feGaussianBlur stdDeviation={3 * u} result=\"blur\" />\n          <feMerge>\n            <feMergeNode in=\"blur\" />\n            <feMergeNode in=\"blur\" />\n            <feMergeNode in=\"SourceGraphic\" />\n          </feMerge>\n        </filter>\n      </svg>\n      <AbsoluteFill style={{ clipPath: ahead }}>\n        {outside ?? (\n          <svg width={W} height={H} aria-hidden>\n            <Outside id={id} d={d} c={c} />\n          </svg>\n        )}\n      </AbsoluteFill>\n      <AbsoluteFill style={{ clipPath: behind }}>{insideLayer}</AbsoluteFill>\n      {/* Fresh exposure glows brighter just behind the bar. */}\n      <AbsoluteFill\n        style={{\n          clipPath: hot,\n          filter: `url(#${id}-glow)`,\n          opacity: scanning ? 0.9 : 0,\n        }}\n      >\n        {insideLayer}\n      </AbsoluteFill>\n      <AbsoluteFill\n        style={{\n          clipPath: behind,\n          opacity: scanning ? 1 : 0,\n          background: `linear-gradient(${trail}, ${c.line}2E 0px, transparent ${band}px)`,\n          backgroundPosition: vertical ? `0 ${bar - H}px` : `${bar - W}px 0`,\n          backgroundRepeat: \"no-repeat\",\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: vertical ? 0 : bar - 1.5 * u,\n          top: vertical ? bar - 1.5 * u : 0,\n          width: vertical ? W : 3 * u,\n          height: vertical ? 3 * u : H,\n          backgroundColor: c.line,\n          boxShadow: `0 0 ${16 * u}px ${4 * u}px ${c.line}AA, 0 0 ${60 * u}px ${12 * u}px ${c.line}33`,\n          opacity: scanning ? 1 : 0,\n        }}\n      />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/x-ray-scan.tsx"
    }
  ],
  "type": "registry:component"
}
