{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "marching-border",
  "title": "Marching Border",
  "description": "A marching-border component.",
  "files": [
    {
      "path": "registry/default/marching-border/marching-border.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nimport \"./marching-border.css\";\n\nexport type MarchingBorderProps = React.ComponentProps<\"svg\"> & {\n  /** Stroke thickness in pixels. */\n  strokeWidth?: number;\n  /** Dash length as a percentage of the path's perimeter. */\n  dash?: number;\n  /** Gap length as a percentage of the path's perimeter. */\n  gap?: number;\n  /**\n   * Seconds per dash-cycle. One cycle = one dash + one gap traversal,\n   * which is the minimum seamless time-loop.\n   */\n  duration?: number;\n};\n\n/**\n * Builds the `d` for a rounded-rect path starting at the midpoint of the top\n * edge, so any residual subpixel seam falls on a straight side (invisible)\n * rather than at a corner. `inset` shifts coordinates inward by half the stroke\n * so the stroke sits inside the SVG box instead of clipping at the edges.\n */\nfunction buildRoundedRectPath(\n  width: number,\n  height: number,\n  radius: number,\n  inset: number,\n): string {\n  const innerW = width - 2 * inset;\n  const innerH = height - 2 * inset;\n  if (innerW <= 0 || innerH <= 0) return \"\";\n  const r = Math.min(radius, innerW / 2, innerH / 2);\n  const left = inset;\n  const top = inset;\n  const right = inset + innerW;\n  const bottom = inset + innerH;\n  // Round to 2 decimals to keep `d` compact; 0.01px is sub-visible so corner\n  // alignment with the parent's border-radius is unaffected.\n  const f = (n: number) => Math.round(n * 100) / 100;\n  return [\n    `M ${f(left + innerW / 2)} ${f(top)}`,\n    `L ${f(right - r)} ${f(top)}`,\n    `A ${f(r)} ${f(r)} 0 0 1 ${f(right)} ${f(top + r)}`,\n    `L ${f(right)} ${f(bottom - r)}`,\n    `A ${f(r)} ${f(r)} 0 0 1 ${f(right - r)} ${f(bottom)}`,\n    `L ${f(left + r)} ${f(bottom)}`,\n    `A ${f(r)} ${f(r)} 0 0 1 ${f(left)} ${f(bottom - r)}`,\n    `L ${f(left)} ${f(top + r)}`,\n    `A ${f(r)} ${f(r)} 0 0 1 ${f(left + r)} ${f(top)}`,\n    \"Z\",\n  ].join(\" \");\n}\n\n/**\n * Marching-ants dashed border, drawn as an absolutely-positioned SVG overlay\n * outside the wrapped element's box model so toggling it causes zero layout\n * shift. Parent must be positioned and should have a `rounded-*` class to\n * conform to. The SVG defaults to `rounded-[inherit]`, so it adopts the\n * parent's corner radius automatically; pass a `rounded-*` class to override.\n *\n * Seam-free loop via two techniques: (1) `pathLength` is rounded to a whole\n * multiple of `(dash + gap)` so the pattern tiles an integer number of times at\n * any size — making `dash`/`gap` perimeter percentages; (2) the path's `M` sits\n * mid-top-edge so residual subpixel error falls on a straight side. The shared\n * `@keyframes dash-march` (in `marching-border.css`) reads the one-cycle end\n * offset from a CSS var, so any `(dash, gap)` loops without per-cycle keyframes.\n *\n * Under `prefers-reduced-motion` only the animation is suppressed — the dashed\n * border still renders as the primary signal of pending/staged state.\n */\nfunction MarchingBorder({\n  strokeWidth = 2,\n  dash = 1,\n  gap = 0.75,\n  duration = 0.75,\n  ref,\n  className,\n  ...rest\n}: MarchingBorderProps) {\n  const svgRef = React.useRef<SVGSVGElement | null>(null);\n  const pathRef = React.useRef<SVGPathElement | null>(null);\n  const cycle = dash + gap;\n  const pathLength = Math.max(cycle, Math.round(100 / cycle) * cycle);\n\n  const setSvgRef = React.useCallback(\n    (node: SVGSVGElement | null) => {\n      svgRef.current = node;\n      if (typeof ref === \"function\") {\n        ref(node);\n      } else if (ref) {\n        (ref as React.RefObject<SVGSVGElement | null>).current = node;\n      }\n    },\n    [ref],\n  );\n\n  // useLayoutEffect so the first paint already has the path's `d`; otherwise\n  // the wait for ResizeObserver's initial callback flashes on staged-state entry.\n  React.useLayoutEffect(() => {\n    const svg = svgRef.current;\n    const path = pathRef.current;\n    if (!svg || !path) return;\n\n    const inset = strokeWidth / 2;\n\n    const apply = () => {\n      const { width, height } = svg.getBoundingClientRect();\n      if (width <= 0 || height <= 0) return;\n\n      // Read the SVG's own computed border-radius. With the default\n      // `rounded-[inherit]` this resolves to the parent's radius; a `rounded-*`\n      // class overrides it. Re-read each resize to track theme / dynamic updates.\n      const detected = parseFloat(getComputedStyle(svg).borderTopLeftRadius);\n      const outerRadius = Number.isFinite(detected) ? detected : 0;\n      // pathRadius = outerRadius − inset: the stroke is centered on the path,\n      // so its outer edge sits at (pathRadius + inset) = the parent's\n      // border-radius. Without the subtraction the corners wouldn't align.\n      const pathRadius = Math.max(0, outerRadius - inset);\n\n      path.setAttribute(\n        \"d\",\n        buildRoundedRectPath(width, height, pathRadius, inset),\n      );\n    };\n\n    apply();\n\n    const observer = new ResizeObserver(apply);\n    observer.observe(svg);\n    return () => observer.disconnect();\n  }, [strokeWidth]);\n\n  return (\n    <svg\n      ref={setSvgRef}\n      aria-hidden\n      data-slot=\"marching-border\"\n      {...rest}\n      className={cn(\n        \"pointer-events-none absolute inset-0 size-full rounded-[inherit]\",\n        className,\n      )}\n    >\n      <path\n        ref={pathRef}\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth={strokeWidth}\n        strokeDasharray={`${dash} ${gap}`}\n        strokeLinecap=\"round\"\n        pathLength={pathLength}\n        // `!` needed to defeat the inline animation style below.\n        className=\"motion-reduce:animate-none!\"\n        style={\n          {\n            \"--march-offset\": -cycle,\n            animation: `dash-march ${duration}s linear infinite`,\n          } as React.CSSProperties\n        }\n      />\n    </svg>\n  );\n}\n\nexport { MarchingBorder };\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/marching-border/marching-border.tsx"
    },
    {
      "path": "registry/default/marching-border/marching-border.css",
      "content": "/* -------------------------------------------------------------------------------------------------\n * MarchingBorder dash animation.\n *\n * One static keyframe shared across every instance. The end offset\n * (one full dash + gap cycle) comes from the `--march-offset` CSS\n * custom property set per-instance on the <path>, so any\n * (dash, gap) combination loops seamlessly without generating\n * per-instance keyframes.\n * -------------------------------------------------------------------------------------------------*/\n\n@keyframes dash-march {\n  to {\n    stroke-dashoffset: var(--march-offset);\n  }\n}\n",
      "type": "registry:file",
      "target": "components/ui/cubby-ui/marching-border/marching-border.css"
    }
  ],
  "type": "registry:ui"
}