{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "nextjs-ship",
  "title": "Next.js Ship",
  "description": "Animated chip/CPU circuit board visualization with gradient pulse traces traveling along PCB-style paths.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/unlumen/nextjs-ship/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\n/* ------------------------------------------------------------------ */\n/*  Types                                                             */\n/* ------------------------------------------------------------------ */\n\nexport interface TraceConfig {\n  /** SVG path `d` attribute */\n  path: string;\n  /** Direction the gradient pulse travels along the path */\n  direction: \"left\" | \"right\" | \"up\" | \"down\";\n  /**\n   * Bounding box [x0, y0, x1, y1] of the path in SVG user-space coords.\n   * Used to place the gradient start/end entirely outside the path so\n   * the infinite-repeat reset jump is invisible.\n   */\n  bbox: [number, number, number, number];\n}\n\nexport interface NextjsShipProps {\n  /** Text displayed inside the central chip.\n   * @default \"Powered By\"\n   */\n  text?: string;\n  /** Primary gradient pulse colour.\n   * @default \"#2EB9DF\"\n   */\n  traceColor?: string;\n  /** Secondary gradient pulse colour.\n   * @default \"#9E00FF\"\n   */\n  traceSecondaryColor?: string;\n  /** Opacity of the static (background) traces.\n   * @default 0.2\n   */\n  traceOpacity?: number;\n  /** Stroke width of the animated pulse.\n   * @default 2\n   */\n  traceWidth?: number;\n  /** Duration of one pulse cycle in seconds.\n   * @default 2\n   */\n  duration?: number;\n  /** Stagger delay between each trace animation in seconds.\n   * @default 0.2\n   */\n  stagger?: number;\n  /** Override the built-in trace paths. */\n  traces?: TraceConfig[];\n  className?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/*  Default trace paths — 7 non-overlapping PCB-style routes          */\n/*  SVG: 600×400   Chip: x=[238,362], y=[158,242]                    */\n/* ------------------------------------------------------------------ */\n\nconst DEFAULT_TRACES: TraceConfig[] = [\n  // 1 — top-center (up)\n  {\n    path: \"M 300 158 V 40 Q 300 25 315 25 H 355\",\n    direction: \"up\",\n    bbox: [298, 25, 355, 158],\n  },\n  // 2 — left-top (left)\n  {\n    path: \"M 238 182 H 130 Q 110 182 110 162 V 70 Q 110 50 90 50 H 20\",\n    direction: \"left\",\n    bbox: [20, 50, 238, 182],\n  },\n  // 3 — left-bottom (left)\n  {\n    path: \"M 238 218 H 100 Q 80 218 80 238 V 340 Q 80 360 60 360 H 20\",\n    direction: \"left\",\n    bbox: [20, 218, 238, 360],\n  },\n  // 4 — right-top (right)\n  {\n    path: \"M 362 182 H 470 Q 490 182 490 162 V 80 Q 490 60 510 60 H 580\",\n    direction: \"right\",\n    bbox: [362, 60, 580, 182],\n  },\n  // 5 — right-bottom (right)\n  {\n    path: \"M 362 218 H 500 Q 520 218 520 238 V 320 Q 520 340 540 340 H 580\",\n    direction: \"right\",\n    bbox: [362, 218, 580, 340],\n  },\n  // 6 — bottom-left (down)\n  {\n    path: \"M 275 242 V 340 Q 275 360 255 360 H 70 Q 50 360 50 380 V 395\",\n    direction: \"down\",\n    bbox: [50, 242, 275, 395],\n  },\n  // 7 — bottom-right (down)\n  {\n    path: \"M 325 242 V 300 Q 325 320 345 320 H 530 Q 550 320 550 340 V 395\",\n    direction: \"down\",\n    bbox: [325, 242, 550, 395],\n  },\n];\n\n/* ------------------------------------------------------------------ */\n/*  Pin positions on chip edges (must match trace start points)       */\n/* ------------------------------------------------------------------ */\n\nconst PINS = {\n  top: [300],\n  bottom: [275, 325],\n  left: [182, 202],\n  right: [182, 218],\n};\n\n/* ------------------------------------------------------------------ */\n/*  Gradient coordinate helpers                                       */\n/*                                                                    */\n/*  Both `from` and `to` positions are entirely outside the path     */\n/*  bounding box, so the infinite-repeat reset jump is invisible.    */\n/* ------------------------------------------------------------------ */\n\ntype GradientCoords = { x1: number; y1: number; x2: number; y2: number };\n\nfunction getGradientCoords(\n  direction: TraceConfig[\"direction\"],\n  bbox: TraceConfig[\"bbox\"],\n): { from: GradientCoords; to: GradientCoords } {\n  const [x0, y0, x1, y1] = bbox;\n  const bw = x1 - x0;\n  const bh = y1 - y0;\n\n  switch (direction) {\n    case \"up\":\n      return {\n        from: { x1: x1, y1: y1 + bh, x2: x0, y2: y1 },\n        to: { x1: x1, y1: y0, x2: x0, y2: y0 - bh },\n      };\n    case \"down\":\n      return {\n        from: { x1: x0, y1: y0 - bh, x2: x1, y2: y0 },\n        to: { x1: x0, y1: y1, x2: x1, y2: y1 + bh },\n      };\n    case \"left\":\n      return {\n        from: { x1: x1 + bw, y1: y0, x2: x1, y2: y1 },\n        to: { x1: x0, y1: y0, x2: x0 - bw, y2: y1 },\n      };\n    case \"right\":\n      return {\n        from: { x1: x0 - bw, y1: y0, x2: x0, y2: y1 },\n        to: { x1: x1, y1: y0, x2: x1 + bw, y2: y1 },\n      };\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/*  Single animated trace                                             */\n/* ------------------------------------------------------------------ */\n\nfunction AnimatedTrace({\n  trace,\n  index,\n  traceColor,\n  traceSecondaryColor,\n  traceOpacity,\n  traceWidth,\n  duration,\n  stagger,\n  gradientId,\n}: {\n  trace: TraceConfig;\n  index: number;\n  traceColor: string;\n  traceSecondaryColor: string;\n  traceOpacity: number;\n  traceWidth: number;\n  duration: number;\n  stagger: number;\n  gradientId: string;\n}) {\n  const { from, to } = getGradientCoords(trace.direction, trace.bbox);\n\n  return (\n    <g>\n      <defs>\n        <motion.linearGradient\n          id={gradientId}\n          gradientUnits=\"userSpaceOnUse\"\n          initial={from}\n          animate={to}\n          transition={{\n            duration,\n            delay: index * stagger,\n            repeat: Infinity,\n            ease: \"linear\",\n          }}\n        >\n          <stop stopColor={traceColor} stopOpacity=\"0\" offset=\"0%\" />\n          <stop stopColor={traceColor} offset=\"30%\" />\n          <stop stopColor={traceSecondaryColor} offset=\"70%\" />\n          <stop stopColor={traceSecondaryColor} stopOpacity=\"0\" offset=\"100%\" />\n        </motion.linearGradient>\n      </defs>\n\n      {/* Static background trace */}\n      <path\n        d={trace.path}\n        stroke=\"currentColor\"\n        strokeOpacity={traceOpacity}\n        strokeWidth={1}\n        fill=\"none\"\n      />\n\n      {/* Animated gradient trace */}\n      <path\n        d={trace.path}\n        stroke={`url(#${gradientId})`}\n        strokeWidth={traceWidth}\n        strokeLinecap=\"round\"\n        fill=\"none\"\n      />\n    </g>\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/*  Main component                                                    */\n/* ------------------------------------------------------------------ */\n\nexport function NextjsShip({\n  text = \"Powered By\",\n  traceColor = \"#2EB9DF\",\n  traceSecondaryColor = \"#9E00FF\",\n  traceOpacity = 0.2,\n  traceWidth = 2,\n  duration = 2,\n  stagger = 0.2,\n  traces = DEFAULT_TRACES,\n  className,\n}: NextjsShipProps) {\n  const id = React.useId().replace(/:/g, \"\");\n\n  return (\n    <div className={cn(\"relative w-full max-w-[600px]\", className)}>\n      <svg\n        viewBox=\"0 0 600 400\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        className=\"h-auto w-full\"\n      >\n        {/* Animated traces */}\n        {traces.map((trace, i) => (\n          <AnimatedTrace\n            key={i}\n            trace={trace}\n            index={i}\n            traceColor={traceColor}\n            traceSecondaryColor={traceSecondaryColor}\n            traceOpacity={traceOpacity}\n            traceWidth={traceWidth}\n            duration={duration}\n            stagger={stagger}\n            gradientId={`pulse-${id}-${i}`}\n          />\n        ))}\n\n        {/* Pin circles — top */}\n        {PINS.top.map((x) => (\n          <circle\n            key={`t-${x}`}\n            cx={x}\n            cy={158}\n            r={3}\n            fill=\"currentColor\"\n            fillOpacity={0.25}\n          />\n        ))}\n        {/* Pin circles — bottom */}\n        {PINS.bottom.map((x) => (\n          <circle\n            key={`b-${x}`}\n            cx={x}\n            cy={212}\n            r={3}\n            fill=\"currentColor\"\n            fillOpacity={0.25}\n          />\n        ))}\n        {/* Pin circles — left */}\n        {PINS.left.map((y) => (\n          <circle\n            key={`l-${y}`}\n            cx={238}\n            cy={y}\n            r={3}\n            fill=\"currentColor\"\n            fillOpacity={0.25}\n          />\n        ))}\n        {/* Pin circles — right */}\n        {PINS.right.map((y) => (\n          <circle\n            key={`r-${y}`}\n            cx={362}\n            cy={y}\n            r={3}\n            fill=\"currentColor\"\n            fillOpacity={0.25}\n          />\n        ))}\n\n        {/* Central chip body — foreignObject for HTML button */}\n        <foreignObject x=\"238\" y=\"158\" width=\"124\" height=\"54\">\n          <div\n            className=\"flex h-full w-full items-center justify-center rounded-xl\"\n            style={{\n              background:\n                \"linear-gradient(145deg, #1a1a1a 0%, #0a0a0a 50%, #1a1a1a 100%)\",\n              boxShadow:\n                \"0 2px 8px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.06), inset 0 -1px 0 rgba(0,0,0,0.4)\",\n              border: \"1px solid rgba(255,255,255,0.08)\",\n            }}\n          >\n            <span\n              className=\"select-none text-base font-medium tracking-wide\"\n              style={{ color: \"rgba(255,255,255,0.55)\" }}\n            >\n              {text}\n            </span>\n          </div>\n        </foreignObject>\n      </svg>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/nextjs-ship.tsx"
    }
  ],
  "type": "registry:ui"
}