{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slide-swap",
  "title": "Slide Swap",
  "description": "A scene sequencer where the outgoing content is shoved off on an accelerating curve and the next springs in from the opposite edge, on one canvas, with no overlap.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/slide-swap/index.tsx",
      "content": "\"use client\";\n\nimport type { CSSProperties, ReactNode } from \"react\";\nimport {\n  AbsoluteFill,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport type SlideSwapConfig = {\n  sceneFrames: number;\n  slideFrames: number;\n  inDistance: number;\n  inDamping: number;\n  inStiffness: number;\n  inMass: number;\n  inFadeFrames: number;\n  outFrames: number;\n  outDistance: number;\n  outPower: number;\n};\n\nexport const SLIDE_SWAP_DEFAULTS: SlideSwapConfig = {\n  sceneFrames: 70,\n  slideFrames: 30,\n  inDistance: 0.28,\n  inDamping: 60,\n  inStiffness: 300,\n  inMass: 0.5,\n  inFadeFrames: 24,\n  outFrames: 22,\n  outDistance: 0.1,\n  outPower: 5,\n};\n\nexport type SlideAxis = \"x\" | \"y\";\n\nconst translate = (axis: SlideAxis, v: number) =>\n  axis === \"x\" ? `${v}px` : `0 ${v}px`;\n\nexport const slideInStyle = (\n  e: number,\n  size: number,\n  fps: number,\n  config?: Partial<SlideSwapConfig>,\n  axis: SlideAxis = \"x\",\n): CSSProperties => {\n  const cfg = { ...SLIDE_SWAP_DEFAULTS, ...config };\n  if (e >= cfg.slideFrames) return {};\n  const p = spring({\n    frame: Math.max(e, 0),\n    fps,\n    config: {\n      damping: cfg.inDamping,\n      stiffness: cfg.inStiffness,\n      mass: cfg.inMass,\n      overshootClamping: true,\n    },\n  });\n  const tFade = Math.min(Math.max(e / cfg.inFadeFrames, 0), 1);\n  return {\n    translate: translate(axis, cfg.inDistance * size * (1 - p)),\n    opacity: 1 - (1 - tFade) * (1 - tFade),\n    willChange: \"transform, opacity\",\n  };\n};\n\nexport const slideOutStyle = (\n  e: number,\n  size: number,\n  config?: Partial<SlideSwapConfig>,\n  axis: SlideAxis = \"x\",\n): CSSProperties => {\n  const cfg = { ...SLIDE_SWAP_DEFAULTS, ...config };\n  if (e <= 0) return {};\n  const t = Math.min(e / cfg.outFrames, 1);\n  return {\n    translate: translate(axis, -cfg.outDistance * size * t ** cfg.outPower),\n    opacity: 1 - t * t,\n    willChange: \"transform, opacity\",\n  };\n};\n\nexport type SlideSwapSceneDef = {\n  name?: string;\n  durationInFrames?: number;\n  content: ReactNode;\n};\n\nexport type SlideSwapPhase = \"slide\" | \"dwell\" | \"exit\";\n\nexport type SlideSwapTimeline = {\n  index: number;\n  local: number;\n  dwellFrames: number;\n  phase: SlideSwapPhase;\n};\n\nexport const slideSwapTimeline = (\n  frame: number,\n  scenes: SlideSwapSceneDef[],\n  config?: Partial<SlideSwapConfig>,\n  opts?: { loop?: boolean },\n): SlideSwapTimeline => {\n  const cfg = { ...SLIDE_SWAP_DEFAULTS, ...config };\n  const lens = scenes.map((s) => s.durationInFrames ?? cfg.sceneFrames);\n  const total = lens.reduce((a, b) => a + b, 0);\n  let w = opts?.loop\n    ? ((frame % total) + total) % total\n    : Math.min(frame, total - 1);\n  let index = 0;\n  while (index < scenes.length - 1 && w >= lens[index]) {\n    w -= lens[index];\n    index += 1;\n  }\n  const local = Math.min(w, lens[index] - 1);\n  const dwellFrames = lens[index];\n  const exits = index < scenes.length - 1 || Boolean(opts?.loop);\n  const enters = index > 0 || Boolean(opts?.loop);\n  const phase: SlideSwapPhase =\n    exits && local >= dwellFrames - cfg.outFrames\n      ? \"exit\"\n      : enters && local + 1 < cfg.slideFrames\n        ? \"slide\"\n        : \"dwell\";\n  return { index, local, dwellFrames, phase };\n};\n\nexport interface SlideSwapScenesProps {\n  scenes: SlideSwapSceneDef[];\n  bg?: string;\n  axis?: SlideAxis;\n  config?: Partial<SlideSwapConfig>;\n  loop?: boolean;\n  style?: CSSProperties;\n}\n\nexport function SlideSwapScenes({\n  scenes,\n  bg,\n  axis = \"x\",\n  config,\n  loop = false,\n  style,\n}: SlideSwapScenesProps) {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const cfg = { ...SLIDE_SWAP_DEFAULTS, ...config };\n  const size = axis === \"x\" ? width : height;\n\n  const { index, local, dwellFrames, phase } = slideSwapTimeline(\n    frame,\n    scenes,\n    cfg,\n    { loop },\n  );\n  const cur = scenes[index];\n  const enters = index > 0 || loop;\n\n  const sceneStyle =\n    phase === \"exit\"\n      ? slideOutStyle(local - (dwellFrames - cfg.outFrames), size, cfg, axis)\n      : enters\n        ? slideInStyle(local + 1, size, fps, cfg, axis)\n        : undefined;\n\n  return (\n    <AbsoluteFill style={{ background: bg, ...style }}>\n      <AbsoluteFill style={sceneStyle}>{cur.content}</AbsoluteFill>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/slide-swap.tsx"
    }
  ],
  "type": "registry:component"
}
