{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "page-transitions",
  "type": "registry:block",
  "title": "Page transitions",
  "description": "Page transitions",
  "files": [
    {
      "path": "components/usages/pagetransitionsusage.tsx",
      "content": "import HeroHome from \"@/registry/open-source/page-transitions\";\r\nimport { TransitionProvider } from \"@/registry/open-source/page-transitions/TransitionProvider\";\r\nimport { GsapProvider } from \"@/registry/open-source/scrolltrigger-replication/GsapProvider\";\r\nimport { LenisProvider } from \"@/registry/open-source/scrolltrigger-replication/LenisProvider\";\r\n\r\nexport default function Usage() {\r\n\treturn (\r\n\t\t<TransitionProvider>\r\n\t\t\t<LenisProvider>\r\n\t\t\t\t<div className=\"flex min-h-svh justify-between flex-col gap-y-12 lg:min-h-screen lg:gap-y-20\">\r\n\t\t\t\t\t<HeroHome />\r\n\t\t\t\t</div>\r\n\t\t\t</LenisProvider>\r\n\t\t\t<GsapProvider scrollTrigger />\r\n\t\t</TransitionProvider>\r\n\t);\r\n}\r\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/pagetransitionsusage.tsx",
      "content": "import HeroHome from \"@/registry/open-source/page-transitions\";\r\nimport { TransitionProvider } from \"@/registry/open-source/page-transitions/TransitionProvider\";\r\nimport { GsapProvider } from \"@/registry/open-source/scrolltrigger-replication/GsapProvider\";\r\nimport { LenisProvider } from \"@/registry/open-source/scrolltrigger-replication/LenisProvider\";\r\n\r\nexport default function Usage() {\r\n\treturn (\r\n\t\t<TransitionProvider>\r\n\t\t\t<LenisProvider>\r\n\t\t\t\t<div className=\"flex min-h-svh justify-between flex-col gap-y-12 lg:min-h-screen lg:gap-y-20\">\r\n\t\t\t\t\t<HeroHome />\r\n\t\t\t\t</div>\r\n\t\t\t</LenisProvider>\r\n\t\t\t<GsapProvider scrollTrigger />\r\n\t\t</TransitionProvider>\r\n\t);\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions.tsx",
      "content": "\"use client\";\n\nimport { useRef } from \"react\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\nimport SplitType, { TargetElement } from \"split-type\";\n\nimport { pages } from \"./page-transitions/data\";\n\nexport default function HeroHome() {\n\tconst sectionRef = useRef<HTMLDivElement>(null);\n\n\tuseGSAP(\n\t\t() => {\n\t\t\tconst pageTitle = sectionRef.current!.querySelector(\n\t\t\t\t\".page-title\"\n\t\t\t)! as TargetElement;\n\t\t\tconst subpageTitle =\n\t\t\t\tsectionRef.current!.querySelectorAll(\".subpage-title\")!;\n\n\t\t\tconst pageTitleSplit = new SplitType(pageTitle, {\n\t\t\t\ttypes: \"lines,words,chars\",\n\t\t\t\tlineClass: \"overflow-hidden\",\n\t\t\t});\n\n\t\t\tconst tl = gsap.timeline({\n\t\t\t\tpaused: true,\n\t\t\t\tdefaults: { ease: \"expo.out\", duration: 1.5 },\n\t\t\t\tdelay: 1.5,\n\t\t\t});\n\n\t\t\ttl.set(pageTitle, { opacity: 1 });\n\t\t\ttl.set(\".subpage-image\", { opacity: 1 });\n\t\t\ttl.set(\".subpage-divider\", {\n\t\t\t\topacity: 1,\n\t\t\t\ttransformOrigin: \"left\",\n\t\t\t});\n\n\t\t\ttl.fromTo(\n\t\t\t\tpageTitleSplit.words,\n\t\t\t\t{\n\t\t\t\t\tyPercent: 100,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tyPercent: 0,\n\t\t\t\t\tstagger: 0.05,\n\t\t\t\t}\n\t\t\t)\n\t\t\t\t.fromTo(\n\t\t\t\t\t\".subpage-image\",\n\t\t\t\t\t{ scale: 1.2, height: \"0%\" },\n\t\t\t\t\t{ scale: 1, height: \"100%\", stagger: 0.1 },\n\t\t\t\t\t\"<\"\n\t\t\t\t)\n\t\t\t\t.fromTo(\n\t\t\t\t\t\".subpage-divider\",\n\t\t\t\t\t{ scaleX: 0 },\n\t\t\t\t\t{ scaleX: 1, stagger: 0.1 },\n\t\t\t\t\t\"<\"\n\t\t\t\t)\n\t\t\t\t.fromTo(\n\t\t\t\t\tsubpageTitle,\n\t\t\t\t\t{ y: 20, opacity: 0 },\n\t\t\t\t\t{ y: 0, opacity: 1, stagger: 0.1 },\n\t\t\t\t\t\"<\"\n\t\t\t\t);\n\n\t\t\ttl.play();\n\t\t},\n\t\t{ scope: sectionRef }\n\t);\n\treturn (\n\t\t<section\n\t\t\tclassName=\"flex flex-col justify-end gap-y-12 overflow-hidden pb-[8vw] lg:gap-y-20 lg:pb-[2vw]\"\n\t\t\tref={sectionRef}\n\t\t>\n\t\t\t<h1 className=\"page-title mb-[5vw] max-w-6xl overflow-hidden font-roboto text-[clamp(6vw,8vw,80px)] uppercase leading-none opacity-0 lg:mb-[3vw]\">\n\t\t\t\tA creative platform built on depth, motion and precision\n\t\t\t</h1>\n\n\t\t\t<div className=\"grid grid-cols-2 gap-[2vw] lg:grid-cols-4 lg:gap-[1vw]\">\n\t\t\t\t{pages.map((page, index) => {\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\thref={`/${page.slug}`}\n\t\t\t\t\t\t\tkey={index + \"hero-home\"}\n\t\t\t\t\t\t\tclassName=\"group\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"relative aspect-square overflow-hidden transition-[border-radius] duration-500 group-hover:rounded-[50%]\">\n\t\t\t\t\t\t\t\t<Image\n\t\t\t\t\t\t\t\t\tsrc={page.image}\n\t\t\t\t\t\t\t\t\talt={page.slug}\n\t\t\t\t\t\t\t\t\tfill\n\t\t\t\t\t\t\t\t\tpriority\n\t\t\t\t\t\t\t\t\tsizes=\"25vw\"\n\t\t\t\t\t\t\t\t\tclassName=\"subpage-image h-0 object-cover opacity-0\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"subpage-divider my-2 h-px w-full scale-x-0 bg-charleston-green opacity-0 lg:my-3\"></div>\n\t\t\t\t\t\t\t<div className=\"subpage-title font-roboto text-[3vw] uppercase opacity-0 lg:text-[1.2vw]\">\n\t\t\t\t\t\t\t\t{page.slug} Effect\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/data.ts",
      "content": "export const pages = [\r\n    {\r\n        slug: \"layer\",\r\n        image: \"/itjustworks.jpg\",\r\n        title: \"Layer Depth\",\r\n        description:\r\n            \"A clean, stacked design that emphasizes depth and separation between content sections.\",\r\n    },\r\n    {\r\n        slug: \"slide\",\r\n        image: \"/itjustworks.jpg\",\r\n        title: \"Slide Motion\",\r\n        description:\r\n            \"A sleek style focusing on horizontal or vertical sliding transitions for dynamic navigation.\",\r\n    },\r\n    {\r\n        slug: \"pixel\",\r\n        image: \"/itjustworks.jpg\",\r\n        title: \"Pixel Grid\",\r\n        description:\r\n            \"A grid-based layout with sharp edges and precision, giving a modern and structured feel.\",\r\n    },\r\n    {\r\n        slug: \"zoom\",\r\n        image: \"/itjustworks.jpg\",\r\n        title: \"Zoom Focus\",\r\n        description:\r\n            \"A bold, interactive approach with zoom-in effects to highlight focal points and visuals.\",\r\n    },\r\n];",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/TransitionProvider.tsx",
      "content": "\"use client\";\r\n\r\nimport { useRef } from \"react\";\r\n\r\nimport { TransitionRouter } from \"next-transition-router\";\r\n\r\nimport getTransitionFunctions, { TransitionType } from \"./transitions\";\r\n\r\nexport function TransitionProvider({\r\n\tchildren,\r\n}: {\r\n\tchildren: React.ReactNode;\r\n}) {\r\n\tconst target = useRef<HTMLDivElement | null>(null);\r\n\tconst simpleLayer = useRef<HTMLDivElement | null>(null);\r\n\tconst slideLayer = useRef<HTMLDivElement | null>(null);\r\n\tconst pixelLayer = useRef<HTMLDivElement | null>(null);\r\n\tconst zoomLayer = useRef<HTMLDivElement | null>(null);\r\n\r\n\tconst transitionType = useRef<TransitionType>(\"\");\r\n\r\n\tconst leave = (next: () => void, from?: string, to?: string) => {\r\n\t\tif (from?.includes(\"layer\") || to?.includes(\"layer\")) {\r\n\t\t\ttransitionType.current = \"layer\";\r\n\t\t\ttarget.current = simpleLayer.current;\r\n\t\t}\r\n\r\n\t\tif (from?.includes(\"slide\") || to?.includes(\"slide\")) {\r\n\t\t\ttransitionType.current = \"slide\";\r\n\t\t\ttarget.current = slideLayer.current;\r\n\t\t}\r\n\r\n\t\tif (from?.includes(\"pixel\") || to?.includes(\"pixel\")) {\r\n\t\t\ttransitionType.current = \"pixel\";\r\n\t\t\ttarget.current = pixelLayer.current;\r\n\t\t}\r\n\r\n\t\tif (from?.includes(\"zoom\") || to?.includes(\"zoom\")) {\r\n\t\t\ttransitionType.current = \"zoom\";\r\n\t\t\ttarget.current = zoomLayer.current;\r\n\t\t}\r\n\r\n\t\treturn getTransitionFunctions(transitionType.current).leave(\r\n\t\t\tnext,\r\n\t\t\ttarget.current\r\n\t\t);\r\n\t};\r\n\r\n\tconst enter = (next: () => void) => {\r\n\t\tif (!transitionType.current) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\treturn getTransitionFunctions(transitionType.current).enter(\r\n\t\t\tnext,\r\n\t\t\ttarget.current\r\n\t\t);\r\n\t};\r\n\r\n\treturn (\r\n\t\t<TransitionRouter auto={true} leave={leave} enter={enter}>\r\n\t\t\t<main>{children}</main>\r\n\r\n\t\t\t{/* Layer transition */}\r\n\t\t\t<div\r\n\t\t\t\tref={simpleLayer}\r\n\t\t\t\tclassName=\"fixed inset-0 z-50 -translate-y-full\"\r\n\t\t\t>\r\n\t\t\t\t<svg\r\n\t\t\t\t\tclassName=\"h-full w-full\"\r\n\t\t\t\t\tviewBox=\"0 0 100 100\"\r\n\t\t\t\t\tpreserveAspectRatio=\"none\"\r\n\t\t\t\t>\r\n\t\t\t\t\t<path\r\n\t\t\t\t\t\tclassName=\"fill-charleston-green\"\r\n\t\t\t\t\t\tvectorEffect=\"non-scaling-stroke\"\r\n\t\t\t\t\t\td=\"M 0 0 V 100 Q 50 100 100 100 V 0 z\"\r\n\t\t\t\t\t/>\r\n\t\t\t\t</svg>\r\n\r\n\t\t\t\t<div className=\"absolute inset-0 grid place-items-center\">\r\n\t\t\t\t\t<h1 className=\"page-title overflow-hidden font-roboto text-[5vw] uppercase text-foreground\">\r\n\t\t\t\t\t\tLayer Effect\r\n\t\t\t\t\t</h1>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t{/* Slide transition */}\r\n\t\t\t<div\r\n\t\t\t\tref={slideLayer}\r\n\t\t\t\tclassName=\"fixed inset-0 z-50 overflow-hidden opacity-0 pointer-events-none\"\r\n\t\t\t>\r\n\t\t\t\t<div className=\"grid h-full w-full grid-cols-12\">\r\n\t\t\t\t\t{[...Array(12)].map((_, i) => (\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tclassName=\"relative h-full w-full\"\r\n\t\t\t\t\t\t\tkey={i + \"transition-provider\"}\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t{/* 101% to remove anti aliasing */}\r\n\t\t\t\t\t\t\t<div className=\"bg-charleston-green w-[101%)] column absolute inset-0\"></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t))}\r\n\t\t\t\t</div>\r\n\r\n\t\t\t\t<div className=\"absolute inset-0 grid place-items-center\">\r\n\t\t\t\t\t<h1 className=\"page-title overflow-hidden font-roboto text-[5vw] uppercase text-foreground\">\r\n\t\t\t\t\t\tSlide Effect\r\n\t\t\t\t\t</h1>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t{/* Pixel transition */}\r\n\t\t\t<div\r\n\t\t\t\tref={pixelLayer}\r\n\t\t\t\tclassName=\"pointer-events-none fixed inset-0 z-50 opacity-0\"\r\n\t\t\t>\r\n\t\t\t\t<div className=\"grid h-full w-full grid-cols-12 grid-rows-12\">\r\n\t\t\t\t\t{[...Array(12 * 12)].map((_, i) => (\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tclassName=\"bg-charleston-green box h-full w-full\"\r\n\t\t\t\t\t\t\tkey={i + \"pixel-transition\"}\r\n\t\t\t\t\t\t></div>\r\n\t\t\t\t\t))}\r\n\t\t\t\t</div>\r\n\r\n\t\t\t\t<div className=\"absolute inset-0 grid place-items-center\">\r\n\t\t\t\t\t<h1 className=\"page-title overflow-hidden font-roboto text-[5vw] uppercase text-foreground\">\r\n\t\t\t\t\t\tPixel Effect\r\n\t\t\t\t\t</h1>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t{/* Layer transition */}\r\n\t\t\t<div\r\n\t\t\t\tref={zoomLayer}\r\n\t\t\t\tclassName=\"bg-charleston-green fixed inset-0 z-50 translate-y-full\"\r\n\t\t\t>\r\n\t\t\t\t<div className=\"absolute inset-0 grid place-items-center\">\r\n\t\t\t\t\t<h1 className=\"page-title overflow-hidden font-roboto text-[5vw] uppercase text-foreground\">\r\n\t\t\t\t\t\tZoom Effect\r\n\t\t\t\t\t</h1>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</TransitionRouter>\r\n\t);\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/transitions.ts",
      "content": "import { layerEnter, layerLeave } from \"./layer\";\r\nimport { pixelEnter, pixelLeave } from \"./pixel\";\r\nimport { slideEnter, slideLeave } from \"./slide\";\r\nimport { zoomEnter, zoomLeave } from \"./zoom\";\r\n\r\nexport type TransitionType = \"layer\" | \"slide\" | \"pixel\" | \"zoom\" | \"\";\r\n\r\nconst getTransitionFunctions = (transitionType: TransitionType) => {\r\n    switch (transitionType) {\r\n        case \"layer\":\r\n            return { enter: layerEnter, leave: layerLeave };\r\n\r\n        case \"slide\":\r\n            return { enter: slideEnter, leave: slideLeave };\r\n\r\n        case \"pixel\":\r\n            return { enter: pixelEnter, leave: pixelLeave };\r\n\r\n        case \"zoom\":\r\n            return { enter: zoomEnter, leave: zoomLeave };\r\n\r\n        default:\r\n            return { enter: () => { }, leave: () => { } };\r\n    }\r\n};\r\n\r\nexport default getTransitionFunctions;",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/layer.ts",
      "content": "import gsap from \"gsap\";\nimport SplitType, { TargetElement } from \"split-type\";\n\nconst getPageTitle = (element: HTMLDivElement) => {\n    const pageTitle = element.querySelector(\".page-title\")! as TargetElement;\n    const pageTitleSplit = new SplitType(pageTitle, {\n        types: \"words,chars\",\n        wordClass: \"overflow-hidden\",\n    });\n\n    return pageTitleSplit;\n};\n\nconst getPath = (element: HTMLDivElement) => {\n    const path = element.querySelector(\"path\")!;\n\n    return path;\n};\n\nexport const layerLeave = (\n    next: () => void,\n    element: HTMLDivElement | null\n) => {\n    if (!element) {\n        return;\n    }\n\n    const ctx = gsap.context(() => {\n        const path = getPath(element);\n        const pageTitleSplit = getPageTitle(element);\n\n        gsap.set(pageTitleSplit.words, { opacity: 1 });\n\n        gsap.timeline({\n            onComplete: next,\n            defaults: { ease: \"power1.inOut\", duration: 0.5 },\n        })\n            .fromTo(\n                element,\n                { y: \"-100%\" },\n                {\n                    y: 0,\n                }\n            )\n            .fromTo(\n                path,\n                {\n                    attr: {\n                        d: \"M 0 0 V 70 Q 50 100 100 70 V 0 z\",\n                    },\n                },\n                {\n                    attr: {\n                        d: \"M 0 0 V 100 Q 50 100 100 100 V 0 z\",\n                    },\n                },\n                \"<0.25\"\n            )\n            .fromTo(\n                pageTitleSplit.words,\n                { yPercent: 150 },\n                { yPercent: 0, stagger: 0.1 }\n            );\n    }, element);\n\n    return () => {\n        ctx.revert();\n    };\n};\n\nexport const layerEnter = (\n    next: () => void,\n    element: HTMLDivElement | null\n) => {\n    if (!element) {\n        return;\n    }\n\n    const ctx = gsap.context(() => {\n        const path = getPath(element);\n        const pageTitleSplit = getPageTitle(element);\n\n        gsap.timeline({\n            defaults: { ease: \"power1.inOut\", duration: 0.5 },\n            onComplete: next,\n        })\n            .fromTo(\n                pageTitleSplit.words,\n                { yPercent: 0 },\n                {\n                    yPercent: -100,\n                    duration: 0.8,\n                    stagger: 0.1,\n                    ease: \"power2.inOut\",\n                }\n            )\n            .fromTo(\n                path,\n                {\n                    attr: {\n                        d: \"M 0 0 V 100 Q 50 100 100 100 V 0 z\",\n                    },\n                },\n                {\n                    attr: {\n                        d: \"M 0 0 V 25 Q 50 0 100 25 V 0 z\",\n                    },\n                    ease: \"power1.in\",\n                    duration: 0.4,\n                }\n            )\n            .to(path, {\n                attr: {\n                    d: \"M 0 0 V 0 Q 50 0 100 0 V 0 z\",\n                },\n                ease: \"power2.out\",\n                duration: 0.4,\n            })\n            .fromTo(\n                element,\n                { y: 0 },\n                {\n                    y: \"-100%\",\n                },\n                \"<50%\"\n            );\n    }, element);\n\n    return () => {\n        ctx.revert();\n    };\n};",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/pixel.ts",
      "content": "\r\nimport gsap from \"gsap\";\r\nimport SplitType, { TargetElement } from \"split-type\";\r\n\r\nconst getPageTitle = (element: HTMLDivElement) => {\r\n    const pageTitle = element.querySelector(\".page-title\")! as TargetElement;\r\n    const pageTitleSplit = new SplitType(pageTitle, {\r\n        types: \"words,chars\",\r\n        wordClass: \"overflow-hidden\",\r\n    });\r\n\r\n    return pageTitleSplit;\r\n};\r\n\r\nconst getBoxes = (element: HTMLDivElement) => {\r\n    const boxes = element.querySelectorAll(\".box\")!;\r\n\r\n    return boxes;\r\n};\r\n\r\nexport const pixelLeave = (\r\n    next: () => void,\r\n    element: HTMLDivElement | null\r\n) => {\r\n    if (!element) {\r\n        return;\r\n    }\r\n\r\n    const ctx = gsap.context(() => {\r\n        const boxes = getBoxes(element);\r\n        const pageTitleSplit = getPageTitle(element);\r\n\r\n        gsap.set(pageTitleSplit.words, { opacity: 1 });\r\n\r\n        gsap.timeline({\r\n            onComplete: next,\r\n            defaults: { ease: \"power1.inOut\", duration: 0.5 },\r\n        })\r\n            .fromTo(element, { opacity: 0 }, { opacity: 1, pointerEvents: \"auto\" })\r\n            .fromTo(\r\n                boxes,\r\n                {\r\n                    scale: 0,\r\n                    opacity: 0,\r\n                },\r\n                {\r\n                    scale: 1,\r\n                    opacity: 1,\r\n                    stagger: {\r\n                        grid: [12, 12],\r\n                        each: 0.05,\r\n                        from: \"start\",\r\n                    },\r\n                },\r\n                \"<0.25\"\r\n            )\r\n\r\n            .fromTo(\r\n                pageTitleSplit.words,\r\n                { yPercent: 150 },\r\n                { yPercent: 0, stagger: 0.1 }\r\n            );\r\n    }, element);\r\n\r\n    return () => {\r\n        ctx.revert();\r\n    };\r\n};\r\n\r\nexport const pixelEnter = (\r\n    next: () => void,\r\n    element: HTMLDivElement | null\r\n) => {\r\n    if (!element) {\r\n        return;\r\n    }\r\n\r\n    const ctx = gsap.context(() => {\r\n        const boxes = getBoxes(element);\r\n        const pageTitleSplit = getPageTitle(element);\r\n\r\n        gsap.set(element, { opacity: 1, pointerEvents: \"auto\" });\r\n\r\n        gsap.timeline({\r\n            defaults: { ease: \"power1.inOut\", duration: 0.7 },\r\n            onComplete: () => {\r\n                gsap.set(element, { pointerEvents: \"none\" });\r\n                next();\r\n            },\r\n        })\r\n            .fromTo(\r\n                pageTitleSplit.words,\r\n                { yPercent: 0 },\r\n                {\r\n                    yPercent: -100,\r\n                    duration: 0.8,\r\n                    stagger: 0.1,\r\n                    ease: \"power2.inOut\",\r\n                }\r\n            )\r\n            .fromTo(\r\n                boxes,\r\n                {\r\n                    scale: 1,\r\n                    opacity: 1,\r\n                },\r\n                {\r\n                    scale: 0,\r\n                    opacity: 0,\r\n                    stagger: {\r\n                        grid: [12, 12],\r\n                        each: 0.05,\r\n                        from: \"end\",\r\n                    },\r\n                }\r\n            )\r\n            .to(element, {\r\n                opacity: 0,\r\n            });\r\n    }, element);\r\n\r\n    return () => {\r\n        ctx.revert();\r\n    };\r\n};\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/slide.ts",
      "content": "import gsap from \"gsap\";\nimport SplitType, { TargetElement } from \"split-type\";\n\nconst getPageTitle = (element: HTMLDivElement) => {\n    const pageTitle = element.querySelector(\".page-title\")! as TargetElement;\n    const pageTitleSplit = new SplitType(pageTitle, {\n        types: \"words,chars\",\n        wordClass: \"overflow-hidden\",\n    });\n\n    return pageTitleSplit;\n};\n\nconst getColumns = (element: HTMLDivElement) => {\n    const columns = element.querySelectorAll(\".column\")!;\n\n    return columns;\n};\n\nexport const slideLeave = (\n    next: () => void,\n    element: HTMLDivElement | null\n) => {\n    if (!element) {\n        return;\n    }\n\n    const ctx = gsap.context(() => {\n        const columns = getColumns(element);\n        const pageTitleSplit = getPageTitle(element);\n\n        gsap.set(pageTitleSplit.words, { opacity: 1 });\n        gsap.set(element, { opacity: 1, pointerEvents: \"auto\" });\n\n        gsap.timeline({\n            onComplete: next,\n            defaults: { ease: \"power1.inOut\", duration: 0.7 },\n        })\n            .fromTo(\n                columns,\n                {\n                    scaleX: 0,\n                    transformOrigin: \"left\",\n                },\n                {\n                    scaleX: 1.1,\n                    stagger: 0.05,\n                },\n                \"<0.25\"\n            )\n\n            .fromTo(\n                pageTitleSplit.words,\n                { yPercent: 150 },\n                { yPercent: 0, stagger: 0.1 }\n            );\n    }, element);\n\n    return () => {\n        ctx.revert();\n    };\n};\n\nexport const slideEnter = (\n    next: () => void,\n    element: HTMLDivElement | null\n) => {\n    if (!element) {\n        return;\n    }\n\n    const ctx = gsap.context(() => {\n        const columns = getColumns(element);\n        const pageTitleSplit = getPageTitle(element);\n        gsap.set(element, { opacity: 1, pointerEvents: \"auto\" });\n\n        gsap.timeline({\n            defaults: { ease: \"power1.inOut\", duration: 0.7 },\n            onComplete: () => {\n                gsap.set(element, { pointerEvents: \"none\" });\n                next();\n            },\n        })\n            .fromTo(\n                pageTitleSplit.words,\n                { yPercent: 0 },\n                {\n                    yPercent: -100,\n                    duration: 0.8,\n                    stagger: 0.1,\n                    ease: \"power2.inOut\",\n                }\n            )\n            .fromTo(\n                columns,\n                { scaleX: 1.1 },\n                {\n                    scaleX: 0,\n                    stagger: 0.05,\n                    transformOrigin: \"right\",\n                }\n            )\n            .fromTo(element, { opacity: 1 }, { opacity: 0 });\n    }, element);\n\n    return () => {\n        ctx.revert();\n    };\n};",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/page-transitions/zoom.ts",
      "content": "\r\nimport gsap from \"gsap\";\r\nimport SplitType, { TargetElement } from \"split-type\";\r\n\r\nconst getPageTitle = (element: HTMLDivElement) => {\r\n    const pageTitle = element.querySelector(\".page-title\")! as TargetElement;\r\n    const pageTitleSplit = new SplitType(pageTitle, {\r\n        types: \"words,chars\",\r\n        wordClass: \"overflow-hidden\",\r\n    });\r\n\r\n    return pageTitleSplit;\r\n};\r\n\r\nconst getMainTag = () => {\r\n    const main = document.querySelector(\"main\");\r\n\r\n    return main;\r\n};\r\n\r\nexport const zoomLeave = (next: () => void, element: HTMLDivElement | null) => {\r\n    if (!element) {\r\n        return;\r\n    }\r\n\r\n    const ctx = gsap.context(() => {\r\n        const pageTitleSplit = getPageTitle(element);\r\n        const main = getMainTag();\r\n\r\n        gsap.set(pageTitleSplit.words, { opacity: 1 });\r\n\r\n        gsap.timeline({\r\n            onComplete: next,\r\n            defaults: { ease: \"power1.inOut\", duration: 0.5 },\r\n        })\r\n            .fromTo(\r\n                main,\r\n                {\r\n                    scale: 1,\r\n                    backgroundColor: \"white\",\r\n                    filter: \"brightness(1)\",\r\n                },\r\n                { scale: 0.9, filter: \"brightness(0.7)\" }\r\n            )\r\n            .fromTo(\r\n                element,\r\n                { y: \"100%\" },\r\n                {\r\n                    y: 0,\r\n                },\r\n                \"<50%\"\r\n            )\r\n            .fromTo(\r\n                pageTitleSplit.words,\r\n                { yPercent: -100 },\r\n                { yPercent: 0, stagger: 0.1 }\r\n            );\r\n    }, element);\r\n\r\n    return () => {\r\n        ctx.revert();\r\n    };\r\n};\r\n\r\nexport const zoomEnter = (next: () => void, element: HTMLDivElement | null) => {\r\n    if (!element) {\r\n        return;\r\n    }\r\n\r\n    const ctx = gsap.context(() => {\r\n        const pageTitleSplit = getPageTitle(element);\r\n        const main = getMainTag();\r\n\r\n        gsap.timeline({\r\n            defaults: { ease: \"power1.inOut\", duration: 0.5 },\r\n            onComplete: next,\r\n        })\r\n            .fromTo(\r\n                pageTitleSplit.words,\r\n                { yPercent: 0 },\r\n                {\r\n                    yPercent: 150,\r\n                    duration: 1,\r\n                    stagger: 0.1,\r\n                    ease: \"power2.inOut\",\r\n                }\r\n            )\r\n            .fromTo(\r\n                element,\r\n                { y: 0 },\r\n                {\r\n                    y: \"100%\",\r\n                }\r\n            )\r\n            .fromTo(\r\n                main,\r\n                {\r\n                    scale: 0.9,\r\n                    backgroundColor: \"white\",\r\n                    filter: \"brightness(0.7)\",\r\n                },\r\n                { scale: 1, filter: \"brightness(1)\" },\r\n                \"<50%\"\r\n            );\r\n    }, element);\r\n\r\n    return () => {\r\n        ctx.revert();\r\n    };\r\n};\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/scrolltrigger-replication/GsapProvider.tsx",
      "content": "\"use client\";\n\nimport { useLayoutEffect } from \"react\";\n\nimport Tempus from \"tempus\";\nimport gsap from \"gsap\";\n\nimport { ScrollTriggerConfig } from \"./ScrollTriggerConfig\";\n\nexport function GsapProvider({ scrollTrigger = false }) {\n\tuseLayoutEffect(() => {\n\t\tgsap.defaults({ ease: \"none\" });\n\n\t\t// merge rafs\n\t\tgsap.ticker.lagSmoothing(0);\n\t\tgsap.ticker.remove(gsap.updateRoot);\n\t\tTempus?.add((time: number) => {\n\t\t\tgsap.updateRoot(time / 1000);\n\t\t}, 0);\n\t}, []);\n\n\treturn scrollTrigger && <ScrollTriggerConfig />;\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/scrolltrigger-replication/ScrollTriggerConfig.tsx",
      "content": "\"use client\";\r\n\r\nimport { useEffect, useLayoutEffect } from \"react\";\r\n\r\nimport gsap from \"gsap\";\r\nimport { ScrollTrigger } from \"gsap/dist/ScrollTrigger\";\r\nimport { useLenis } from \"lenis/react\";\r\n\r\ngsap.registerPlugin(ScrollTrigger);\r\n\r\nexport function ScrollTriggerConfig() {\r\n\tuseLayoutEffect(() => {\r\n\t\tScrollTrigger.clearScrollMemory(\"manual\");\r\n\t}, []);\r\n\r\n\tconst lenis = useLenis(ScrollTrigger.update);\r\n\tuseEffect(() => ScrollTrigger.refresh(), [lenis]);\r\n\r\n\treturn null;\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/scrolltrigger-replication/LenisProvider.tsx",
      "content": "\"use client\";\n\nimport \"lenis/dist/lenis.css\";\nimport {ReactLenis} from \"lenis/react\";\nimport {PropsWithChildren} from \"react\";\n\nexport function LenisProvider({children}: PropsWithChildren) {\n    return <ReactLenis root>{children}</ReactLenis>;\n}",
      "type": "registry:ui"
    }
  ]
}