Full Code of SameerJS6/lina for AI

main 6c1934820003 cached
86 files
208.3 KB
56.9k tokens
140 symbols
1 requests
Download .txt
Showing preview only (228K chars total). Download the full file or copy to clipboard to get everything.
Repository: SameerJS6/lina
Branch: main
Commit: 6c1934820003
Files: 86
Total size: 208.3 KB

Directory structure:
gitextract_dybf963_/

├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│   └── settings.json
├── LICENSE
├── README.md
├── app/
│   ├── globals.css
│   ├── layout.tsx
│   ├── manifest.ts
│   ├── page.tsx
│   ├── robots.ts
│   └── sitemap.ts
├── components/
│   ├── adaptive-mask.tsx
│   ├── anchor-heading.tsx
│   ├── cli-block.tsx
│   ├── code-block.tsx
│   ├── code-display-block.tsx
│   ├── component-preview.tsx
│   ├── copy-button.tsx
│   ├── examples.tsx
│   ├── features.tsx
│   ├── installation.tsx
│   ├── manual-installation-block.tsx
│   ├── micro-interactions.tsx
│   ├── native-touch.tsx
│   ├── navbar.tsx
│   ├── page-wide-scroll-mask.tsx
│   ├── posthog-provider.tsx
│   ├── render-preview.tsx
│   ├── theme-provider.tsx
│   ├── theme-toggle.tsx
│   ├── ui/
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── dialog.tsx
│   │   ├── label.tsx
│   │   ├── popover.tsx
│   │   ├── revola.tsx
│   │   ├── select.tsx
│   │   ├── separator.tsx
│   │   ├── skeleton.tsx
│   │   ├── tabs.tsx
│   │   └── tooltip.tsx
│   ├── usage.tsx
│   ├── variant-select.tsx
│   └── why.tsx
├── components.json
├── eslint.config.mjs
├── hooks/
│   ├── use-copy-button.ts
│   ├── use-has-primary-touch.tsx
│   ├── use-media-query.tsx
│   └── use-meta-color.ts
├── lib/
│   ├── adaptive-mask-animation.ts
│   ├── code-highlight.ts
│   ├── package-manager-store.ts
│   ├── package-manager-utils.ts
│   ├── utils.ts
│   └── variant-store.ts
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public/
│   ├── r/
│   │   ├── command-base.json
│   │   ├── command-radix.json
│   │   ├── horizontal-scroll-demo-base.json
│   │   ├── horizontal-scroll-demo-radix.json
│   │   ├── lina-base.json
│   │   ├── lina-radix.json
│   │   ├── timezone-select-demo-base.json
│   │   ├── timezone-select-demo-radix.json
│   │   ├── vertical-scroll-demo-base.json
│   │   └── vertical-scroll-demo-radix.json
│   └── site.webmanifest
├── registry/
│   ├── base-ui/
│   │   ├── examples/
│   │   │   ├── command.tsx
│   │   │   ├── horizontal-scroll.tsx
│   │   │   ├── timezone-select.tsx
│   │   │   ├── usage-demo.tsx
│   │   │   └── vertical-scroll.tsx
│   │   └── scroll-area.tsx
│   ├── radix-ui/
│   │   ├── examples/
│   │   │   ├── command.tsx
│   │   │   ├── horizontal-scroll.tsx
│   │   │   ├── timezone-select.tsx
│   │   │   ├── usage-demo.tsx
│   │   │   └── vertical-scroll.tsx
│   │   └── scroll-area.tsx
│   └── registry.ts
├── registry.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts


================================================
FILE: .prettierignore
================================================
.next
package-lock.json
pnpm-lock.yaml
components.json


================================================
FILE: .prettierrc.json
================================================
{
  "semi": true,
  "singleQuote": false,
  "tabWidth": 2,
  "printWidth": 120,
  "bracketSpacing": true,
  "trailingComma": "es5",
  "endOfLine": "auto",
  "plugins": ["@ianvs/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
  "importOrder": [
    "^(react/(.*)$)|^(react$)",
    "^(next/(.*)$)|^(next$)",
    "<THIRD_PARTY_MODULES>",
    "",
    "^@base-ui-components/(.*)$",
    "^@radix-ui/(.*)$",
    "^cmdk$",
    "^lucide-react$",
    "^motion$",
    "^next-themes$",
    "^posthog-js$",
    "^posthog-js/(.*)$",
    "^vaul$",
    "^zustand$",
    "^class-variance-authority$",
    "^clsx$",
    "^tailwind-merge$",
    "",
    "^@/app/(.*)$",
    "^@/components/(.*)$",
    "",
    "^@/components/ui/(.*)$",
    "^@/lib/(.*)$",
    "",
    "^@/hooks/(.*)$",
    "",
    "^[./]"
  ],
  "importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"],
  "importOrderTypeScriptVersion": "5.0.0",
  "importOrderCaseSensitive": false,
  "importOrderSeparation": true,
  "importOrderSortSpecifiers": true,
  "importOrderGroupNamespaceSpecifiers": true
}


================================================
FILE: .vscode/settings.json
================================================
{
  "typescript.tsdk": "node_modules\\typescript\\lib",
  "typescript.preferences.importModuleSpecifier": "non-relative",
  "javascript.preferences.importModuleSpecifier": "non-relative",
  "typescript.updateImportsOnFileMove.enabled": "always",
  "javascript.updateImportsOnFileMove.enabled": "always",
  "typescript.tsserver.experimental.enableProjectDiagnostics": true,
  "typescript.preferences.importModuleSpecifierEnding": "minimal",
  "javascript.preferences.importModuleSpecifierEnding": "minimal",
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.removeUnusedImports": "explicit"
  }
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2025 Sameer Singh

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Lina

The Adaptive Scroll Area for Modern UIs

A responsive scroll area that feels native on touch devices, offering custom styling and enhanced interactions where it matters most.

Lina is a drop‑in replacement for shadcn/ui's `ScrollArea` with better defaults and a more native feel.

![](/main-mockup.png)

## Features

- **Adaptive mask** - Subtle edge fades appear only when content is scrollable and adapt in real time to scroll position, axis, and container shape.
- **Micro‑interactions** - Hover/press effects for the custom scrollbar on non‑touch (desktop) devices, with responsive thumb behavior.
- **Native touch optimization** - Uses the right touch‑action, momentum scrolling, and passive listeners so scrolling feels truly native on iOS/Android while staying precise on desktop.
- **Drop‑in replacement** - Matches shadcn/ui's `ScrollArea` API, same props, slots, and `className` ergonomics—so you can swap it in with zero extra step.
- **Radix and Base UI variants** - Choose Radix or Base UI primitives while keeping a consistent API and visual design.

## Installation

```bash
npx shadcn@latest add https://lina.sameer.sh/r/lina-radix.json
```

## Usage

```tsx
import { ScrollArea } from "@/components/ui/scroll-area";

export default function Example() {
  return (
    <ScrollArea className="h-[200px] w-[350px] rounded-md border p-4">
      Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the
      king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester.
      And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they
      couldn't help but laugh. And once they started laughing, they couldn't stop.
    </ScrollArea>
  );
}
```

## Documentation

Visit [Docs](https://lina.sameer.sh)

## Credits

Built on top of:

- [Radix UI's Scroll Area](https://radix-ui.com/primitives/docs/components/scroll-area) by [Radix UI](https://radix-ui.com)
- [Base UI's Scroll Area](https://base-ui.com/react/components/scroll-area) by [Base UI](https://base-ui.com/)

## License

MIT


================================================
FILE: app/globals.css
================================================
@import "tailwindcss";
@import "tw-animate-css";

@custom-variant dark (&:is(.dark *));

@theme inline {
  --breakpoint-3xl: 1600px;
  --breakpoint-4xl: 2000px;

  --color-background: var(--background);
  --color-foreground: var(--foreground);

  --font-sans: var(--font-geist-sans);
  --font-mono: var(--font-geist-mono);
  --font-inter: var(--font-inter);

  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);

  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);

  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);

  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);

  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);

  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);

  --color-surface: var(--surface);
  --color-surface-foreground: var(--surface-foreground);

  --color-ring: var(--ring);
  --color-input: var(--input);
  --color-border: var(--border);
  --color-destructive: var(--destructive);

  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
}

:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.141 0.005 285.823);

  --primary: oklch(0.21 0.006 285.885);
  --primary-foreground: oklch(0.985 0 0);

  --secondary: oklch(0.967 0.001 286.375);
  --secondary-foreground: oklch(0.21 0.006 285.885);

  --card: oklch(1 0 0);
  --card-foreground: oklch(0.141 0.005 285.823);

  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.141 0.005 285.823);

  --muted: oklch(0.967 0.001 286.375);
  --muted-foreground: oklch(0.552 0.016 285.938);

  --accent: oklch(0.967 0.001 286.375);
  --accent-foreground: oklch(0.21 0.006 285.885);

  --surface: oklch(0.98 0 0);
  --surface-foreground: var(--foreground);

  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.92 0.004 286.32);
  --input: oklch(0.92 0.004 286.32);
  --ring: oklch(0.705 0.015 286.067);
}

.dark {
  --background: oklch(0.141 0.005 285.823);
  --foreground: oklch(0.985 0 0);

  --primary: oklch(0.92 0.004 286.32);
  --primary-foreground: oklch(0.21 0.006 285.885);

  --secondary: oklch(0.274 0.006 286.033);
  --secondary-foreground: oklch(0.985 0 0);

  --card: oklch(0.21 0.006 285.885);
  --card-foreground: oklch(0.985 0 0);

  --popover: oklch(0.21 0.006 285.885);
  --popover-foreground: oklch(0.985 0 0);

  --muted: oklch(0.274 0.006 286.033);
  --muted-foreground: oklch(0.705 0.015 286.067);

  --accent: oklch(0.274 0.006 286.033);
  --accent-foreground: oklch(0.985 0 0);

  --surface: oklch(0.2 0 0);
  --surface-foreground: oklch(0.708 0 0);

  --destructive: oklch(0.704 0.191 22.216);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.552 0.016 285.938);
}

@layer base {
  * {
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
    @apply border-border outline-ring/50;
  }

  html {
    @apply scroll-smooth;
  }

  body {
    @apply bg-background selection:bg-primary selection:text-primary-foreground text-foreground font-sans;
    font-synthesis-weight: none;
    text-rendering: optimizeLegibility;
  }

  img {
    @apply select-none;
  }
}

@utility container {
  @apply 3xl:max-w-screen-2xl mx-auto max-w-[1400px] px-4 lg:px-8;
}

@utility focus-ring {
  @apply focus-visible:border-ring focus-visible:ring-ring/50 outline-none focus-visible:ring-[3px];
}

@layer components {
  [data-line-numbers] {
    display: grid;
    min-width: 100%;
    white-space: pre;
    border: 0;
    background: transparent;
    padding: 0;
    counter-reset: line;
    box-decoration-break: clone;
  }

  [data-line-numbers] [data-line]::before {
    font-size: var(--text-sm);
    counter-increment: line;
    content: counter(line);
    display: inline-block;
    width: calc(var(--spacing) * 16);
    padding-right: calc(var(--spacing) * 6);
    text-align: right;
    color: var(--color-foreground);
    background-color: var(--color-surface);
    position: sticky;
    left: 0;
  }

  [data-line] {
    padding-top: calc(var(--spacing) * 0.5);
    padding-bottom: calc(var(--spacing) * 0.5);
    min-height: calc(var(--spacing) * 1);
    width: 100%;
    display: inline-block;
  }

  [data-line] span {
    color: var(--shiki-light);
    @apply selection:bg-primary/10 selection:text-(--shiki-light) dark:selection:text-(--shiki-dark);
    @variant dark {
      color: var(--shiki-dark) !important;
    }
  }

  [data-highlighted-line] {
    &:after {
      position: absolute;
      top: 0;
      left: 0;
      width: 2px;
      height: 100%;
      content: "";
      background-color: var(--border);
    }
  }
}

/* Custom Scrollbar Styling  */
::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-track {
  width: 10px;
  margin-block: 0.25rem;
  background: var(--background);
}
::-webkit-scrollbar-thumb {
  border-radius: 100vw;
  background: color-mix(in oklab, var(--primary) 25%, transparent);
  transition: background 0.15s ease-in-out;
  &:hover {
    background: color-mix(in oklab, var(--primary) 30%, transparent);
  }
  &:active {
    background: color-mix(in oklab, var(--primary) 50%, transparent);
  }
}


================================================
FILE: app/layout.tsx
================================================
import type { Metadata } from "next";
import { Geist, Geist_Mono, Inter } from "next/font/google";

import "@/app/globals.css";

import Navbar from "@/components/navbar";
import PostHogProvider from "@/components/posthog-provider";
import { ThemeProvider } from "@/components/theme-provider";

import { META_THEME_COLORS } from "@/hooks/use-meta-color";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Refined & Responsive Scroll Area - Lina",
  description: "A responsive scroll area that feels native on touch, and custom where it matters.",
  keywords: [
    "radix ui",
    "Next.js",
    "React",
    "Tailwind CSS",
    "Components",
    "shadcn",
    "scroll-area",
    "ScrollArea",
    "base ui",
    "adaptive",
    "responsive",
  ],
  creator: "SameerJS6",
  authors: [
    {
      name: "SameerJS6",
      url: "https://sameer.sh",
    },
  ],
  openGraph: {
    title: "Refined & Responsive Scroll Area - Lina",
    description: "A responsive scroll area that feels native on touch, and custom where it matters.",
    type: "website",
    url: process.env.NEXT_PUBLIC_BASE_URL,
    images: [
      {
        url: `${process.env.NEXT_PUBLIC_BASE_URL}/og-image.png`,
        width: 1280,
        height: 630,
        alt: "Lina",
      },
    ],
  },
  twitter: {
    card: "summary_large_image",
    title: "Refined & Responsive Scroll Area - Lina",
    description: "A responsive scroll area that feels native on touch, and custom where it matters.",
    images: [`${process.env.NEXT_PUBLIC_BASE_URL}/og-image.png`],
    creator: "@sameerjs6",
  },
  alternates: {
    canonical: process.env.NEXT_PUBLIC_BASE_URL,
  },
  manifest: `${process.env.NEXT_PUBLIC_BASE_URL}/site.webmanifest`,
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              try {
                if (localStorage.theme === 'dark' || ((!('theme' in localStorage) || localStorage.theme === 'system') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
                  document.querySelector('meta[name="theme-color"]').setAttribute('content', '${META_THEME_COLORS.dark}')
                }
              } catch (_) {}
            `,
          }}
        />
        <meta name="theme-color" content={META_THEME_COLORS.light} />
      </head>
      <body className={`${geistSans.variable} ${geistMono.variable} ${inter.variable} antialiased`}>
        <PostHogProvider>
          <ThemeProvider defaultTheme="system" attribute="class" enableSystem disableTransitionOnChange>
            <Navbar />
            {children}
          </ThemeProvider>
        </PostHogProvider>
      </body>
    </html>
  );
}


================================================
FILE: app/manifest.ts
================================================
import type { MetadataRoute } from "next";

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: "Lina",
    short_name: "Lina",
    description: "A responsive scroll area that feels native on touch, and custom where it matters.",
    start_url: "/new",
    display: "standalone",
    background_color: "#ffffff",
    theme_color: "#000000",
    icons: [
      {
        src: "/android-chrome-192x192.png",
        sizes: "192x192",
        type: "image/png",
      },
      {
        src: "/android-chrome-512x512.png",
        sizes: "512x512",
        type: "image/png",
      },
    ],
  };
}


================================================
FILE: app/page.tsx
================================================
import Link from "next/link";

import Examples from "@/components/examples";
import Features from "@/components/features";
import Installation from "@/components/installation";
import PageWideScrollMask from "@/components/page-wide-scroll-mask";
import { buttonVariants } from "@/components/ui/button";
import Usage from "@/components/usage";
import Why from "@/components/why";

export default async function Home() {
  return (
    <main className="container space-y-8">
      <section className="flex flex-col items-center gap-2 py-8 text-center sm:px-6 md:py-16 lg:py-20 xl:gap-4">
        <div className="bg-card text-card-foreground mb-2 rounded-full border px-4 py-1 lg:mb-0">
          <a
            target="_blank"
            rel="noopener noreferrer"
            className="focus-ring inline items-center gap-1 rounded"
            href="https://ui.shadcn.com/docs/components/scroll-area"
          >
            Shadcn UI
          </a>{" "}
          Drop-in Replacement
        </div>
        <h1 className="font-inter leading-tighter text-primary w-full text-4xl font-semibold tracking-tight text-balance lg:leading-[1.1] lg:font-semibold xl:text-5xl xl:tracking-tighter">
          The Adaptive Scroll Area for Modern UIs
        </h1>

        <p className="text-foreground max-w-4xl text-base text-balance sm:text-lg">
          A responsive scroll area that feels native on touch devices, offering custom styling and enhanced interactions
          where it matters most.
        </p>
        <div className="flex w-full items-center justify-center gap-2 pt-2 **:data-[slot=button]:shadow-none">
          <Link href="#examples" className={buttonVariants({ size: "sm" })}>
            See it in Action
          </Link>
          <Link href="#installation" className={buttonVariants({ variant: "ghost", size: "sm" })}>
            Install Now
          </Link>
        </div>
      </section>
      <section className="space-y-10 sm:space-y-12 md:space-y-14 lg:space-y-16 xl:space-y-24">
        <Features />
        <section className="grid w-full min-w-0 gap-10 lg:grid-cols-2 lg:gap-4">
          <Installation />
          <Usage />
        </section>
        <Why />
        <Examples />
      </section>
      <PageWideScrollMask />
    </main>
  );
}


================================================
FILE: app/robots.ts
================================================
import type { MetadataRoute } from "next";

const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
const siteUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [{ userAgent: "*", allow: "/" }],
    sitemap: `${siteUrl}/sitemap.xml`,
    host: siteUrl,
  };
}


================================================
FILE: app/sitemap.ts
================================================
import type { MetadataRoute } from "next";

const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
const siteUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;

export default function sitemap(): MetadataRoute.Sitemap {
  const now = new Date();

  const routes: MetadataRoute.Sitemap = [
    {
      url: `${siteUrl}/`,
      lastModified: now,
      changeFrequency: "weekly",
      priority: 1,
    },
  ];

  return routes;
}


================================================
FILE: components/adaptive-mask.tsx
================================================
"use client";

import { useEffect, useRef, useState } from "react";
import { motion, useMotionValue, useSpring } from "motion/react";

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

import {
  calculateContentPosition,
  getCurrentPhase,
  getTotalDuration,
  SCROLL_CONFIG,
  springConfigs,
  updateMasksBasedOnPosition,
} from "@/lib/adaptive-mask-animation";

export default function AdaptiveMask() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = useState(370); // Default fallback

  const contentXTarget = useMotionValue(0);
  const contentYTarget = useMotionValue(0);
  const topMaskTarget = useMotionValue(0);
  const bottomMaskTarget = useMotionValue(1);
  const leftMaskTarget = useMotionValue(0);
  const rightMaskTarget = useMotionValue(1);

  const contentX = useSpring(contentXTarget, springConfigs.content);
  const contentY = useSpring(contentYTarget, springConfigs.content);
  const topMaskOpacity = useSpring(topMaskTarget, springConfigs.masks);
  const bottomMaskOpacity = useSpring(bottomMaskTarget, springConfigs.masks);
  const leftMaskOpacity = useSpring(leftMaskTarget, springConfigs.masks);
  const rightMaskOpacity = useSpring(rightMaskTarget, springConfigs.masks);

  useEffect(() => {
    if (!containerRef.current) return;

    const updateContainerWidth = () => {
      if (containerRef.current) {
        const rect = containerRef.current.getBoundingClientRect();
        setContainerWidth(rect.width);
      }
    };

    updateContainerWidth();

    const resizeObserver = new ResizeObserver(updateContainerWidth);
    resizeObserver.observe(containerRef.current);

    return () => {
      resizeObserver.disconnect();
    };
  }, []);

  useEffect(() => {
    let currentStep = 0;
    const totalDuration = getTotalDuration();

    const animationInterval = setInterval(() => {
      currentStep = (currentStep + 1) % totalDuration;

      const { phase: currentPhase, progress: phaseProgress } = getCurrentPhase(currentStep);
      if (!currentPhase) return;

      const { x: targetX, y: targetY } = calculateContentPosition(currentPhase, phaseProgress, containerWidth);

      contentXTarget.set(targetX);
      contentYTarget.set(targetY);

      updateMasksBasedOnPosition(
        targetX,
        targetY,
        topMaskTarget,
        bottomMaskTarget,
        leftMaskTarget,
        rightMaskTarget,
        containerWidth
      );
    }, SCROLL_CONFIG.timing.fps);

    return () => clearInterval(animationInterval);
  }, [
    contentXTarget,
    contentYTarget,
    topMaskTarget,
    bottomMaskTarget,
    leftMaskTarget,
    rightMaskTarget,
    containerWidth,
  ]);

  return (
    <Card className="group relative overflow-hidden border border-zinc-200/50 bg-white/50 backdrop-blur-sm transition-all duration-300 hover:bg-white/80 dark:border-zinc-800/50 dark:bg-zinc-900/50">
      <CardHeader className="relative">
        <CardTitle className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Adaptive Mask</CardTitle>
        <CardDescription className="text-zinc-600 dark:text-zinc-400">
          Dynamic masking that responds to content.
        </CardDescription>
      </CardHeader>
      <CardContent className="relative space-y-4">
        <div ref={containerRef} className="bg-secondary dark:bg-card relative h-80 overflow-hidden rounded-lg border">
          <motion.div
            className="absolute"
            style={{
              x: contentX,
              y: contentY,
              width: "200%",
              height: "220%",
              padding: "16px",
            }}
          >
            {/* Content Grid */}
            <div className="space-y-3">
              {Array.from({ length: 20 }, (_, i) => (
                <div key={i} className="h-2 flex-1 rounded bg-emerald-300 dark:bg-emerald-600"></div>
              ))}
            </div>
          </motion.div>

          {/* Adaptive Masks */}
          <motion.div
            className="from-secondary dark:from-card pointer-events-none absolute inset-x-0 top-0 h-8 bg-gradient-to-b to-transparent"
            style={{ opacity: topMaskOpacity }}
          />
          <motion.div
            className="from-secondary dark:from-card pointer-events-none absolute inset-x-0 bottom-0 h-8 bg-gradient-to-t to-transparent"
            style={{ opacity: bottomMaskOpacity }}
          />
          <motion.div
            className="from-secondary dark:from-card pointer-events-none absolute inset-y-0 left-0 w-8 bg-gradient-to-r to-transparent"
            style={{ opacity: leftMaskOpacity }}
          />
          <motion.div
            className="from-secondary dark:from-card pointer-events-none absolute inset-y-0 right-0 w-8 bg-gradient-to-l to-transparent"
            style={{ opacity: rightMaskOpacity }}
          />
        </div>

        <p className="text-sm leading-relaxed text-zinc-700 dark:text-zinc-300">
          Automatic edge masks that appear and fade based on scroll position and direction, creating seamless visual
          boundaries.
        </p>
      </CardContent>
    </Card>
  );
}


================================================
FILE: components/anchor-heading.tsx
================================================
import type { ComponentProps } from "react";

import { cn } from "@/lib/utils";

type AnchorHeadingProps = ComponentProps<"h2">;

export default function AnchorHeading({ className, id, children, ...props }: AnchorHeadingProps) {
  return (
    <h2 id={id} className={cn("group relative scroll-m-20 text-3xl font-medium tracking-tight", className)} {...props}>
      <a href={"#" + id} className="focus-ring inline-block cursor-pointer rounded">
        <span className="bg-muted pointer-events-none absolute top-1/2 -left-9 -translate-y-1/2 rounded border px-2 font-sans text-base opacity-0 transition-opacity group-hover:opacity-100">
          #
        </span>
        {children}
      </a>
    </h2>
  );
}


================================================
FILE: components/cli-block.tsx
================================================
"use client";

import { Suspense, useEffect, useMemo } from "react";
import { ScrollArea, ScrollBar } from "@/registry/radix-ui/scroll-area";
import { type RegistryKeys } from "@/registry/registry";

import posthog from "posthog-js";

import CopyButton from "@/components/copy-button";
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger } from "@/components/ui/tabs";
import VariantSelect from "@/components/variant-select";

import { usePackageManagerStore, type PackageManager } from "@/lib/package-manager-store";
import { convertNpmCommand } from "@/lib/package-manager-utils";
import { cn } from "@/lib/utils";
import { useVariantStore } from "@/lib/variant-store";

type CLIBlockProps = {
  name?: RegistryKeys;
  command?: string;
  className?: string;
};

export default function CLIBlock({ name, command }: CLIBlockProps) {
  return (
    <Suspense>
      <CLIBlockContent name={name} command={command} />
    </Suspense>
  );
}

function CLIBlockContent({ name, command }: CLIBlockProps) {
  const { currentVariant } = useVariantStore();
  const { selectedPackageManager, setPackageManager, isLoading, setLoading } = usePackageManagerStore();

  useEffect(() => {
    setLoading(false);
  }, [setLoading]);

  const componentName = name === "lina" ? `${name}-${currentVariant}` : `${name}-demo-${currentVariant}`;
  const registryURL = `https://lina.sameer.sh/r/${componentName}.json`;

  const commands = useMemo(
    () => convertNpmCommand(command ? command : `npx shadcn@latest add ${registryURL}`),
    [registryURL, command]
  );

  const handleCopy = () => {
    const text = commands[selectedPackageManager];
    navigator.clipboard.writeText(text);
    try {
      posthog.capture("cli_copy", {
        component: name,
        variant: currentVariant,
        package_manager: selectedPackageManager,
      });
    } catch {}
  };

  return (
    <Tabs value={selectedPackageManager} onValueChange={setPackageManager} className={cn("mt-4 flex flex-col gap-0")}>
      <div className="bg-surface flex items-center justify-between rounded-t-xl px-4 pt-4 pb-2">
        <div className="space-y-3">
          <div>
            <VariantSelect size="sm" />
          </div>
          <TabsList className="relative bg-transparent p-0">
            {Object.keys(commands).map((key, index) =>
              isLoading ? (
                <Skeleton key={index} className="mr-2 h-6 w-16 rounded-sm" />
              ) : (
                <TabsTrigger key={index} value={key} className="z-10 flex h-full items-end">
                  {key}
                </TabsTrigger>
              )
            )}
            <TabsIndicator className="z-5" />
          </TabsList>
        </div>
        <CopyButton className="z-10 !opacity-100" onCopy={handleCopy} />
      </div>
      {Object.keys(commands).map((key, index) => (
        <TabsContent key={index} value={key} className="mt-0">
          <ScrollArea
            maskClassName="after:from-surface before:from-surface"
            className="bg-surface max-h-[600px] w-full rounded-b-xl border-t"
          >
            <pre className={cn("pt-2 pb-4", isLoading && "p-4")}>
              <code className={cn("font-mono text-[13px] leading-tight whitespace-nowrap", !isLoading && "px-4")}>
                {isLoading ? <Skeleton className="h-5 w-full" /> : commands[key as PackageManager]}
              </code>
            </pre>
            <ScrollBar orientation="horizontal" />
          </ScrollArea>
        </TabsContent>
      ))}
    </Tabs>
  );
}


================================================
FILE: components/code-block.tsx
================================================
"use client";

import { useLayoutEffect, useRef, useState, type JSX } from "react";
import { ScrollArea, ScrollBar } from "@/registry/radix-ui/scroll-area";
import type { BundledLanguage } from "shiki/bundle/web";

import posthog from "posthog-js";

import CopyButton from "@/components/copy-button";

import { highlight } from "@/lib/code-highlight";
import { cn } from "@/lib/utils";

type CodeBlockProps = {
  code: string | null;
  lang: BundledLanguage;
  initial?: JSX.Element;
  preHighlighted?: JSX.Element | null;
  className?: string;
};

export default function CodeBlock({ code, lang, initial, preHighlighted, className }: CodeBlockProps) {
  const [content, setContent] = useState<JSX.Element | null>(preHighlighted || initial || null);
  const areaRef = useRef<HTMLDivElement>(null);

  const onCopy = () => {
    const pre = areaRef.current?.getElementsByTagName("pre").item(0);

    if (!pre) return;

    const clone = pre.cloneNode(true) as HTMLElement;
    navigator.clipboard.writeText(clone.textContent || "");
    try {
      posthog.capture("code_copy", { lang, length: (clone.textContent || "").length });
    } catch {}
  };

  useLayoutEffect(() => {
    if (preHighlighted) {
      setContent(preHighlighted);
      return;
    }

    let isMounted = true;

    if (code) {
      highlight(code, lang).then((result) => {
        if (isMounted) setContent(result);
      });
    } else {
      setContent(<pre className="bg-secondary/50 rounded-md p-4">No code available</pre>);
    }

    return () => {
      isMounted = false;
    };
  }, [code, lang, preHighlighted]);

  return content ? (
    <div
      className={cn(
        "group focus-ring bg-surface relative overflow-hidden rounded-xl [&_code]:font-mono [&_code]:text-[13px] [&_pre]:p-4 [&_pre]:!leading-tight",
        className
      )}
    >
      <ScrollArea
        ref={areaRef}
        dir="ltr"
        maskClassName="after:from-surface before:from-surface"
        className="focus-ring relative size-full rounded-xl"
      >
        {content}
        <ScrollBar orientation="horizontal" className="focus-ring" />
        <CopyButton className="absolute top-4 right-4 z-[2] backdrop-blur-md" onCopy={onCopy} />
      </ScrollArea>
    </div>
  ) : (
    <div className="bg-secondary/50 relative my-6 min-h-[500px] overflow-hidden rounded-xl border text-sm">
      <pre className="rounded-md p-4 text-center text-sm">Loading...</pre>
    </div>
  );
}


================================================
FILE: components/code-display-block.tsx
================================================
"use client";

import type { JSX } from "react";
import type { BundledLanguage } from "shiki/bundle/web";

import CodeBlock from "@/components/code-block";

import { useVariantStore } from "@/lib/variant-store";

type CodeData = {
  code: string;
  highlightedCode: JSX.Element;
} | null;

type VariantData = {
  base: {
    code: CodeData;
  };
  radix: {
    code: CodeData;
  };
};

type CodeDisplaySectionProps = {
  variantData: VariantData;
  language: BundledLanguage;
  maxHeight?: string;
};

export default function CodeDisplaySection({
  variantData,
  language,
  maxHeight = "[&_pre]:max-h-[400px]",
}: CodeDisplaySectionProps) {
  const { currentVariant } = useVariantStore();
  const codeData = variantData[currentVariant]?.code;

  return (
    <>
      {!codeData ? (
        <p className="text-muted-foreground text-sm">
          No code available. If you think this is an error, please{" "}
          <a
            target="_blank"
            rel="noopener noreferrer"
            href="https://github.com/SameerJS6/lina/issues"
            className="text-foreground font-medium underline hover:no-underline"
          >
            open an issue
          </a>
          .
        </p>
      ) : (
        <CodeBlock
          lang={language}
          code={codeData.code}
          preHighlighted={codeData.highlightedCode}
          className={maxHeight}
        />
      )}
    </>
  );
}


================================================
FILE: components/component-preview.tsx
================================================
import React from "react";
import { Index, type RegistryKeys } from "@/registry/registry";
import type { BundledLanguage } from "shiki/bundle/web";

import { Loader } from "lucide-react";

import CodeDisplaySection from "@/components/code-display-block";
import RenderPreview from "@/components/render-preview";
import { Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger } from "@/components/ui/tabs";
import VariantSelect from "@/components/variant-select";

import { getComponentCode } from "@/lib/code-highlight";
import { cn } from "@/lib/utils";

type ComponentDetailsProps = {
  name: RegistryKeys;
  hideCode?: boolean;
  className?: string;
  lang?: BundledLanguage;
  align?: "center" | "start" | "end";
};

export default async function ComponentPreview({
  name,
  className,
  lang = "tsx",
  hideCode = false,
  align = "center",
}: ComponentDetailsProps) {
  const registryEntry = Index[name];
  const [radixCodeData, baseCodeData] = await Promise.all([
    getComponentCode(`${registryEntry.name}-radix`, lang),
    getComponentCode(`${registryEntry.name}-base`, lang),
  ]);

  const variantData = {
    base: { code: baseCodeData },
    radix: { code: radixCodeData },
  } as const;

  return (
    <div className={cn("group relative flex min-w-0 flex-col space-y-2", className)}>
      <Tabs defaultValue="preview" className="relative w-full">
        <div className="flex items-center justify-between pb-3">
          {!hideCode && (
            <TabsList className="relative z-0 justify-start bg-transparent p-0">
              <TabsTrigger value="preview">Preview</TabsTrigger>
              <TabsTrigger value="code">Code</TabsTrigger>
              <TabsIndicator />
            </TabsList>
          )}
        </div>
        <TabsContent value="preview" className="focus-ring relative rounded-xl border p-4">
          <VariantSelect />
          <div
            className={cn("flex min-h-[350px] w-full justify-center p-10", {
              "items-center": align === "center",
              "items-start": align === "start",
              "items-end": align === "end",
            })}
          >
            <React.Suspense
              fallback={
                <div className="text-muted-foreground flex w-full items-center justify-center text-sm">
                  <Loader className="mr-2 h-4 w-4 animate-spin" />
                  Loading...
                </div>
              }
            >
              <RenderPreview name={name} />
            </React.Suspense>
          </div>
        </TabsContent>
        <TabsContent value="code" className="focus-ring rounded-xl">
          <CodeDisplaySection variantData={variantData} language={lang} maxHeight="[&_pre]:max-h-[435px]" />
        </TabsContent>
      </Tabs>
    </div>
  );
}


================================================
FILE: components/copy-button.tsx
================================================
"use client";

import type { ButtonHTMLAttributes } from "react";

import { Check, Copy } from "lucide-react";

import { buttonVariants } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

import { cn } from "@/lib/utils";

import { useCopyButton } from "@/hooks/use-copy-button";

export default function CopyButton({
  className,
  onCopy,
  ...props
}: ButtonHTMLAttributes<HTMLButtonElement> & {
  onCopy: () => void;
}) {
  const [checked, onClick] = useCopyButton(onCopy);

  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <button
          type="button"
          className={cn(
            buttonVariants({
              variant: "ghost",
              size: "icon",
            }),
            "size-[1.875rem] transition-opacity group-hover:opacity-100 [&_svg]:size-3.5",
            checked && "hover:bg-green-600/10 dark:hover:bg-green-500/10",
            !checked && "[@media(hover:hover)]:opacity-0",
            className
          )}
          aria-label={checked ? "Copied Text" : "Copy Text"}
          onClick={onClick}
          {...props}
        >
          <Check className={cn("text-green-600 transition-transform dark:text-green-500", !checked && "scale-0")} />
          <Copy className={cn("absolute transition-transform", checked && "scale-0")} />
        </button>
      </TooltipTrigger>
      <TooltipContent>{checked ? "Copied!" : "Copy to clipboard"}</TooltipContent>
    </Tooltip>
  );
}


================================================
FILE: components/examples.tsx
================================================
import AnchorHeading from "@/components/anchor-heading";
import CLIBlock from "@/components/cli-block";
import ComponentPreview from "@/components/component-preview";

export default function Examples() {
  return (
    <section className="space-y-6 sm:space-y-8 sm:px-6 lg:space-y-10">
      <div className="space-y-1">
        <AnchorHeading id="examples" className="scroll-m-16">
          Examples
        </AnchorHeading>
        <p className="text-muted-foreground leading-7">
          Explore various examples showcasing the capabilities of the Adaptive Scroll Area component.
        </p>
      </div>
      <div className="space-y-10 sm:space-y-12 lg:space-y-24">
        <div className="grid gap-6 lg:grid-cols-2">
          <div className="flex min-w-0 flex-col justify-between">
            <div>
              <h2 className="scroll-m-20 text-2xl font-medium tracking-tight">Vertical Scroll Demo</h2>
              <p className="text-muted-foreground mt-6 leading-7">
                Experience smooth vertical scrolling with customizable scrollbars, perfect for long content sections and
                lists.
              </p>
            </div>
            <CLIBlock name="vertical-scroll" />
          </div>
          <ComponentPreview name="vertical-scroll" className="my-0" />
        </div>
        <div className="grid gap-6 lg:grid-cols-2">
          <div className="flex min-w-0 flex-col justify-between">
            <div>
              <h2 className="scroll-m-20 text-2xl font-medium tracking-tight">Horizontal Scroll Demo</h2>
              <p className="text-muted-foreground mt-6 leading-7">
                Implement elegant horizontal scrolling for carousels, galleries, and overflow content with intuitive
                controls.
              </p>
            </div>
            <CLIBlock name="horizontal-scroll" />
          </div>
          <ComponentPreview name="horizontal-scroll" className="my-0" />
        </div>
        <div className="grid gap-6 lg:grid-cols-2">
          <div className="flex min-w-0 flex-col justify-between">
            <div>
              <h2 className="scroll-m-20 text-2xl font-semibold tracking-tight">Timezone Select Example</h2>
              <p className="text-muted-foreground mt-6 leading-7">
                A customizable timezone select component demonstrating advanced scroll functionality with search and
                filtering capabilities.
              </p>
            </div>
            <CLIBlock name="timezone-select" />
          </div>
          <ComponentPreview name="timezone-select" />
        </div>
      </div>
    </section>
  );
}


================================================
FILE: components/features.tsx
================================================
import AdaptiveMask from "@/components/adaptive-mask";
import MicroInteractions from "@/components/micro-interactions";
import NativeTouch from "@/components/native-touch";

export default function Features() {
  return (
    <section className="grid w-full grid-cols-1 gap-4 sm:p-4 md:grid-cols-2 xl:grid-cols-3 md:[&>*:nth-child(3)]:max-xl:col-span-2">
      <MicroInteractions />
      <AdaptiveMask />
      <NativeTouch />
    </section>
  );
}


================================================
FILE: components/installation.tsx
================================================
import AnchorHeading from "@/components/anchor-heading";
import CLIBlock from "@/components/cli-block";
import ManualInstallationSection from "@/components/manual-installation-block";
import { Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger } from "@/components/ui/tabs";

import { getComponentCode } from "@/lib/code-highlight";

export default async function Installation() {
  const pathToPrimaryHook = "hooks/use-has-primary-touch.tsx";

  const language = "tsx";
  const name = "lina";

  const [radixCodeData, baseCodeData, hookCodeData] = await Promise.all([
    getComponentCode(`${name}-radix`, language),
    getComponentCode(`${name}-base`, language),
    getComponentCode("", "tsx", pathToPrimaryHook),
  ]);

  const variantData = {
    base: {
      code: baseCodeData,
      mainDependency: "@base-ui-components/react",
    },
    radix: {
      code: radixCodeData,
      mainDependency: "@radix-ui/react-scroll-area",
    },
  } as const;

  return (
    <section className="min-w-0 space-y-6 sm:px-6">
      <div className="space-y-1">
        <AnchorHeading id="installation">Installation</AnchorHeading>
        <p className="text-muted-foreground leading-7">
          Add the adaptive scroll area component to your project with ease.
        </p>
      </div>

      <div>
        <Tabs defaultValue="cli" className="w-full min-w-0">
          <TabsList className="relative h-8 bg-transparent p-0">
            <TabsTrigger value="cli" className="text-base">
              CLI
            </TabsTrigger>
            <TabsTrigger value="manual" className="text-base">
              Manual
            </TabsTrigger>
            <TabsIndicator />
          </TabsList>

          <TabsContent value="cli" className="mt-6">
            <div className="space-y-6">
              <div className="space-y-0">
                <h4 className="font-heading font- mt-12 scroll-m-28 text-lg font-medium tracking-tight first:mt-0 sm:text-2xl lg:mt-20 [&+p]:!mt-4 *:[code]:text-2xl">
                  Quick Installation
                </h4>
                <p className="text-muted-foreground text-sm">
                  Use the CLI to add the component to your project. This will automatically install all dependencies and
                  copy the necessary files.
                </p>
              </div>
              <CLIBlock name="lina" className="mt-0" />
            </div>
          </TabsContent>
          <TabsContent value="manual" className="mt-6">
            <ManualInstallationSection variantData={variantData} hookCodeData={hookCodeData} language={language} />
          </TabsContent>
        </Tabs>
      </div>
    </section>
  );
}


================================================
FILE: components/manual-installation-block.tsx
================================================
"use client";

import type { JSX } from "react";
import type { BundledLanguage } from "shiki/bundle/web";

import CLIBlock from "@/components/cli-block";
import CodeDisplaySection from "@/components/code-display-block";

import { useVariantStore } from "@/lib/variant-store";

type CodeData = {
  code: string;
  highlightedCode: JSX.Element;
} | null;

type VariantData = {
  base: {
    code: CodeData;
    mainDependency: string;
  };
  radix: {
    code: CodeData;
    mainDependency: string;
  };
};

type ManualInstallationSectionProps = {
  variantData: VariantData;
  hookCodeData: CodeData;
  language: BundledLanguage;
};

export default function ManualInstallationBlock({
  variantData,
  hookCodeData,
  language,
}: ManualInstallationSectionProps) {
  const { currentVariant } = useVariantStore();

  const devDependency = "tw-animate-css";
  const mainDependency = variantData[currentVariant]?.mainDependency;

  return (
    <div className="space-y-6 sm:space-y-10 lg:space-y-12">
      <div className="space-y-6">
        <div className="space-y-0">
          <h4 className="mt-12 scroll-m-28 text-lg font-medium tracking-tight first:mt-0 sm:text-2xl lg:mt-20 [&+p]:!mt-4">
            Install main dependencies
          </h4>
          <p className="text-muted-foreground text-sm">
            Install the required {currentVariant === "base" ? "Base UI" : "Radix UI"} dependencies for the scroll area
            component.
          </p>
        </div>
        <CLIBlock command={`npm install ${mainDependency}`} />
      </div>

      <div className="space-y-6">
        <div className="space-y-2">
          <h4 className="font-heading font- mt-12 scroll-m-28 text-lg font-medium tracking-tight first:mt-0 sm:text-2xl lg:mt-20 [&+p]:!mt-4 *:[code]:text-2xl">
            Install development dependencies
          </h4>
          <p className="text-muted-foreground text-sm">Install the animation utilities for smooth transitions.</p>
        </div>
        <CLIBlock command={`npm install -D ${devDependency}`} />
      </div>

      <div className="space-y-6">
        <div className="space-y-2">
          <h4 className="font-heading font- mt-12 scroll-m-28 text-lg font-medium tracking-tight first:mt-0 sm:text-2xl lg:mt-20 [&+p]:!mt-4 *:[code]:text-2xl">
            Copy the touch detection hook
          </h4>
          <p className="text-muted-foreground text-sm">
            Add the hook that detects touch-primary devices for adaptive behavior.
          </p>
        </div>
        <CodeDisplaySection
          variantData={{
            base: { code: hookCodeData },
            radix: { code: hookCodeData },
          }}
          language={language}
          maxHeight="[&_pre]:max-h-[400px]"
        />
      </div>

      <div className="space-y-6">
        <div className="space-y-2">
          <h4 className="font-heading font- mt-12 scroll-m-28 text-lg font-medium tracking-tight first:mt-0 sm:text-2xl lg:mt-20 [&+p]:!mt-4 *:[code]:text-2xl">
            Copy the component
          </h4>
          <p className="text-muted-foreground text-sm">
            Add the main scroll area component with adaptive masking and touch-optimized behavior.
          </p>
        </div>
        <CodeDisplaySection variantData={variantData} language={language} maxHeight="[&_pre]:max-h-[400px]" />
      </div>
    </div>
  );
}


================================================
FILE: components/micro-interactions.tsx
================================================
"use client";

import { useEffect, useState } from "react";
import { motion } from "motion/react";

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

export default function MicroInteractions() {
  const [animationPhase, setAnimationPhase] = useState(0);

  const CURSOR_POSITION = {
    INITIAL: { x: 140, y: 140, scale: 1 },
    HOVER_ON_PILL: { x: 13, y: -100, scale: 1 },
    PRESS_ON_PILL: { x: 13, y: -90, scale: 0.8 },
    END_POSITION_FOR_PILL: { x: 13, y: 50, scale: 0.8 },
    INITIAL_POSITION_FOR_PILL: { x: 13, y: -100, scale: 0.8 },
  };

  useEffect(() => {
    const interval = setInterval(() => {
      setAnimationPhase((prev) => (prev + 1) % 7);
    }, 1200);
    return () => clearInterval(interval);
  }, []);

  const getCursorPosition = () => {
    switch (animationPhase) {
      case 0:
        return CURSOR_POSITION.INITIAL;
      case 1:
        return CURSOR_POSITION.HOVER_ON_PILL;
      case 2:
        return CURSOR_POSITION.PRESS_ON_PILL;
      case 3:
        return CURSOR_POSITION.END_POSITION_FOR_PILL;
      case 4:
      case 5:
        return CURSOR_POSITION.INITIAL_POSITION_FOR_PILL;
      default:
        return CURSOR_POSITION.INITIAL;
    }
  };

  const THUMB_SCALE = {
    INITIAL: 1,
    HOVER: 1.1,
    PRESS: 0.95,
  };

  const getThumbScale = () => {
    switch (animationPhase) {
      case 1:
        return THUMB_SCALE.HOVER;
      case 2:
        return THUMB_SCALE.PRESS;
      case 5:
        return THUMB_SCALE.INITIAL;
      default:
        return THUMB_SCALE.INITIAL;
    }
  };

  const THUMB_Y_POSITION = {
    INITIAL: -42,
    PRE_INITIAL: -40,
    END_POSITION: 90,
  };

  const getThumbY = () => {
    switch (animationPhase) {
      case 3:
        return THUMB_Y_POSITION.END_POSITION;
      case 4:
      case 5:
        return THUMB_Y_POSITION.PRE_INITIAL;
      default:
        return THUMB_Y_POSITION.INITIAL;
    }
  };

  const getBarOpacity = () => {
    switch (animationPhase) {
      case 1:
      case 2:
        return 1;
      case 3:
        return 1;
      case 4:
        return 1;
      case 5:
        return 1;
      default:
        return 0;
    }
  };

  return (
    <Card className="group relative overflow-hidden border border-zinc-200/50 bg-white/50 backdrop-blur-sm transition-all duration-300 hover:bg-white/80 dark:border-zinc-800/50 dark:bg-zinc-900/50">
      <CardHeader className="relative">
        <CardTitle className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Micro Interactions</CardTitle>
        <CardDescription className="text-zinc-600 dark:text-zinc-400">
          Polished interactions that enhance usability.
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="bg-secondary dark:bg-card relative flex h-80 items-center justify-center rounded-xl border">
          <motion.div
            className="bar bg-card dark:bg-secondary absolute h-[310px] w-6 rounded-full"
            initial={false}
            animate={{
              opacity: getBarOpacity(),
            }}
            transition={{ duration: 0.6, ease: "easeInOut" }}
          />

          <motion.div
            className="bg-primary relative z-10 h-20 w-4 rounded-full shadow-lg"
            animate={{
              scaleY: getThumbScale(),
              y: getThumbY(),
            }}
            initial={false}
            transition={{ duration: 1.5, type: "spring", bounce: 0.25 }}
            style={{ marginTop: "-130px" }}
          />

          <motion.div
            className="pointer-events-none absolute z-20"
            initial={false}
            animate={getCursorPosition()}
            transition={{ duration: 1.55, type: "spring", bounce: 0.25 }}
            style={{
              transformOrigin: "center center",
              marginLeft: "-10px",
              marginTop: "-12px",
            }}
          >
            <svg
              width="24"
              height="30"
              viewBox="0 0 30 38"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              className="stroke-secondary fill-primary size-6"
            >
              <path
                d="M3.58385 1.69742C2.57836 0.865603 1.05859 1.58076 1.05859 2.88572V35.6296C1.05859 37.1049 2.93111 37.7381 3.8265 36.5656L12.5863 25.0943C12.6889 24.96 12.8483 24.8812 13.0173 24.8812H27.3245C28.7697 24.8812 29.4211 23.0719 28.3076 22.1507L3.58385 1.69742Z"
                strokeLinejoin="round"
              />
            </svg>
          </motion.div>
        </div>

        <p className="text-sm leading-relaxed text-zinc-700 dark:text-zinc-300">
          Refined micro-interactions including scrollbar fade effects, hover states, and thumb interactions for polished
          desktop experience.
        </p>
      </CardContent>
    </Card>
  );
}


================================================
FILE: components/native-touch.tsx
================================================
"use client";

import { useEffect, useState } from "react";
import { motion } from "motion/react";

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

export default function NativeTouch() {
  const [isTouch, setIsTouch] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => {
      setIsTouch((prev) => !prev);
    }, 5000);

    return () => clearInterval(interval);
  }, []);

  return (
    <Card className="group relative overflow-hidden border border-zinc-200/50 bg-white/50 backdrop-blur-sm transition-all duration-300 hover:bg-white/80 dark:border-zinc-800/50 dark:bg-zinc-900/50">
      <CardHeader className="relative">
        <CardTitle className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Adaptive Scrollbars</CardTitle>
        <CardDescription className="text-zinc-600 dark:text-zinc-400">
          Native experience that adapts to devices.
        </CardDescription>
      </CardHeader>
      <CardContent className="relative space-y-4">
        <div className="bg-secondary dark:bg-card relative flex h-80 items-center justify-center overflow-hidden rounded-lg border">
          {/* Desktop mockup */}
          <motion.div
            className="relative"
            animate={{
              opacity: isTouch ? 0 : 1,
              scale: isTouch ? 0.8 : 1,
              x: isTouch ? -80 : 0,
            }}
            transition={{
              type: "spring",
              stiffness: 120,
              damping: 25,
              duration: 2,
            }}
          >
            <div className="relative h-28 w-48 rounded-lg border border-zinc-300 bg-zinc-200 p-0.5 sm:h-32 sm:w-60 md:h-40 md:w-72 dark:border-zinc-600 dark:bg-zinc-700">
              {/* Screen bezel */}
              <div className="relative h-full w-full overflow-hidden rounded-md bg-zinc-900 p-0.5 dark:bg-zinc-800">
                {/* Actual screen */}
                <div className="relative h-full w-full overflow-hidden rounded-sm bg-white dark:bg-zinc-900">
                  <div className="absolute top-1 right-1 bottom-1 w-1.5 rounded-full bg-zinc-200 dark:bg-zinc-700">
                    {/* Desktop scrollbar thumb */}
                    <motion.div
                      className="absolute top-1 h-8 w-full rounded-full bg-zinc-400 dark:bg-zinc-500"
                      animate={{
                        y: [0, 50, 0],
                      }}
                      transition={{
                        duration: 4,
                        repeat: Number.POSITIVE_INFINITY,
                        ease: "easeInOut",
                        repeatDelay: 1,
                      }}
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="mx-auto -mt-0.5 h-1 w-full rounded-b-lg bg-zinc-300 lg:h-1.5 dark:bg-zinc-600" />
          </motion.div>

          {/* Mobile mockup */}
          <motion.div
            className="absolute"
            animate={{
              opacity: isTouch ? 1 : 0,
              scale: isTouch ? 1 : 0.8,
              x: isTouch ? 0 : 80,
            }}
            transition={{
              type: "spring",
              stiffness: 120,
              damping: 25,
              duration: 2,
            }}
          >
            <div className="relative aspect-[8/16] h-56 w-28 overflow-hidden">
              <div
                className="relative h-full w-full rounded-[24px] border border-black bg-black p-[1.5px]"
                style={{
                  background: "linear-gradient(180deg, #EEEAE1 0%, #D1CCC2 100%)",
                  boxShadow: "0px 0px 1px 1px rgba(0, 0, 0, 0.20) inset, 0px 0px 2px 1px rgba(0, 0, 0, 0.40) inset",
                }}
              >
                <div
                  className="h-full w-full rounded-[21px] border border-black bg-black p-[3px]"
                  style={{
                    boxShadow: "0px 0px 2px 1px rgba(255, 255, 255, 0.25), 0px 0px 0.5px 2px #3C3C3C inset",
                  }}
                >
                  <div className="relative h-full w-full overflow-hidden rounded-[18px] bg-white dark:bg-zinc-900">
                    {/* Screen content area */}
                    <div className="relative h-full">
                      <motion.div
                        className="absolute top-1 right-0.5 bottom-1 w-0.5 rounded-sm"
                        animate={{
                          opacity: [0, 1, 1, 0],
                        }}
                        transition={{
                          duration: 4,
                          repeat: Number.POSITIVE_INFINITY,
                          times: [0, 0.1, 0.9, 1],
                          repeatDelay: 1,
                        }}
                      >
                        {/* Mobile scrollbar thumb */}
                        <motion.div
                          className="absolute top-0 h-6 w-full rounded-sm bg-zinc-500/80 dark:bg-zinc-400/80"
                          animate={{
                            y: [0, 100, 0],
                          }}
                          transition={{
                            duration: 4,
                            repeat: Number.POSITIVE_INFINITY,
                            ease: "easeInOut",
                            repeatDelay: 1,
                          }}
                        />
                      </motion.div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </motion.div>
        </div>

        <p className="text-sm leading-relaxed text-zinc-700 md:max-w-[45ch] dark:text-zinc-300">
          Automatic scrollbar adaptation between native and custom styles based on device type and touch capabilities.
        </p>
      </CardContent>
    </Card>
  );
}


================================================
FILE: components/navbar.tsx
================================================
import Link from "next/link";

import ThemeToggle from "@/components/theme-toggle";
import { Button, buttonVariants } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";

export default function Navbar() {
  return (
    <header className="container">
      <nav className="flex items-center justify-between gap-4 py-4">
        <Link href="/" className={buttonVariants({ variant: "ghost", size: "sm", className: "h-10 max-lg:-ml-4" })}>
          <svg
            width="270"
            height="270"
            viewBox="0 0 270 270"
            fill="none"
            className="size-8"
            xmlns="http://www.w3.org/2000/svg"
          >
            <rect width="270" height="270" />
            <rect x="109" y="35" width="20" height="200" rx="10" fill="currentColor" />
            <rect x="149" y="85" width="13" height="100" rx="6.5" fill="currentColor" />
          </svg>
          <span className="sr-only">Home</span>
        </Link>
        <div className="flex h-5 items-center">
          <Button variant="ghost" size="icon" asChild>
            <Link
              target="_blank"
              href="https://github.com/SameerJS6/lina"
              rel="noopener noreferrer"
              aria-label="View project on GitHub"
            >
              <svg
                width="1024"
                height="1024"
                viewBox="0 0 1024 1024"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
                className="fill-primary size-4"
              >
                <path
                  fillRule="evenodd"
                  clipRule="evenodd"
                  d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
                  transform="scale(64)"
                />
              </svg>
            </Link>
          </Button>
          <div className="h-4">
            <Separator orientation="vertical" className="mx-2" />
          </div>
          <ThemeToggle />
        </div>
      </nav>
    </header>
  );
}


================================================
FILE: components/page-wide-scroll-mask.tsx
================================================
"use client";

import { useEffect, useState } from "react";
import { motion } from "motion/react";

export default function PageWideScrollMask() {
  const [showTopMask, setShowTopMask] = useState(false);
  const [showBottomMask, setShowBottomMask] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const documentHeight = document.documentElement.scrollHeight;

      const isScrollable = documentHeight > windowHeight;

      if (!isScrollable) {
        setShowTopMask(false);
        setShowBottomMask(false);
        return;
      }

      const topThreshold = 50;
      setShowTopMask(scrollTop > topThreshold);

      const bottomThreshold = 50;
      const distanceFromBottom = documentHeight - (scrollTop + windowHeight);
      setShowBottomMask(distanceFromBottom > bottomThreshold);
    };

    handleScroll();

    window.addEventListener("scroll", handleScroll, { passive: true });
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <>
      <motion.div
        aria-hidden="true"
        className="from-background pointer-events-none fixed top-0 z-50 h-16 w-full bg-gradient-to-b to-transparent"
        initial={{
          opacity: 0,
          y: -16,
        }}
        animate={{
          opacity: showTopMask ? 1 : 0,
          y: showTopMask ? 0 : -16,
        }}
        transition={{
          duration: 0.3,
          ease: "easeInOut",
        }}
        style={{
          pointerEvents: "none",
        }}
      />
      <motion.div
        aria-hidden="true"
        className="from-background pointer-events-none fixed bottom-0 z-50 h-16 w-full bg-gradient-to-t to-transparent"
        initial={{
          opacity: 0,
          y: 16,
        }}
        animate={{
          opacity: showBottomMask ? 1 : 0,
          y: showBottomMask ? 0 : 16,
        }}
        transition={{
          duration: 0.3,
          ease: "easeInOut",
        }}
        style={{
          pointerEvents: "none",
        }}
      />
    </>
  );
}


================================================
FILE: components/posthog-provider.tsx
================================================
"use client";

import { useEffect } from "react";

import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";

export default function PostHogProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    if (process.env.NODE_ENV === "development") return;

    if (typeof window === "undefined") return;

    const key = "phc_if0JhFzod0CXwQO92EQ0z5fjQgGT2xXPLPVJmu43nZl";
    if (!key) return;

    posthog.init(key, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://us.i.posthog.com",
      capture_pageview: true,
      capture_pageleave: true,
      name: "Lina",
      defaults: "2025-05-24",
      person_profiles: "always",
    });
  }, []);

  return <PHProvider client={posthog}>{children}</PHProvider>;
}


================================================
FILE: components/render-preview.tsx
================================================
"use client";

import React from "react";
import { Index, type RegistryKeys } from "@/registry/registry";

import { useVariantStore } from "@/lib/variant-store";

type RenderPreviewProps = {
  name: RegistryKeys;
};

export default function RenderPreview({ name }: RenderPreviewProps) {
  const { currentVariant } = useVariantStore();
  const registryEntry = Index[name];

  return (
    <>
      {!registryEntry ? (
        <p className="text-muted-foreground text-sm">
          Component <code className="bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm">{name}</code>{" "}
          not found in special registry.
        </p>
      ) : (
        React.createElement(registryEntry.variants[currentVariant])
      )}
    </>
  );
}


================================================
FILE: components/theme-provider.tsx
================================================
"use client";

import * as React from "react";

import { ThemeProvider as NextThemesProvider } from "next-themes";

export function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}


================================================
FILE: components/theme-toggle.tsx
================================================
"use client";

import * as React from "react";

import { useTheme } from "next-themes";

import { Button } from "@/components/ui/button";

import { useMetaColor } from "@/hooks/use-meta-color";

export default function ThemeToggle() {
  const { setTheme, resolvedTheme } = useTheme();
  const { metaColor, setMetaColor } = useMetaColor();

  React.useEffect(() => {
    setMetaColor(metaColor);
  }, [metaColor, setMetaColor]);

  const toggleTheme = React.useCallback(() => {
    setTheme(resolvedTheme === "dark" ? "light" : "dark");
  }, [resolvedTheme, setTheme]);

  return (
    <Button variant="ghost" size="icon" className="group/toggle size-8" onClick={toggleTheme} title="Toggle theme">
      <div className="border-primary aspect-square size-4 rounded-full border-2 [background:linear-gradient(90deg,var(--background)_50%,var(--primary)_50%)]" />
      <span className="sr-only">Toggle theme</span>
    </Button>
  );
}


================================================
FILE: components/ui/button.tsx
================================================
import * as React from "react";

import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring select-none focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
        destructive:
          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
        outline:
          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
        secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
        icon: "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

function Button({
  className,
  variant,
  size,
  asChild = false,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean;
  }) {
  const Comp = asChild ? Slot : "button";

  return <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} />;
}

export { Button, buttonVariants };


================================================
FILE: components/ui/card.tsx
================================================
import * as React from "react";

import { cn } from "@/lib/utils";

function Card({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card"
      className={cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className)}
      {...props}
    />
  );
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
        className
      )}
      {...props}
    />
  );
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="card-title" className={cn("leading-none font-semibold", className)} {...props} />;
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} />;
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
      {...props}
    />
  );
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="card-content" className={cn("px-6", className)} {...props} />;
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div data-slot="card-footer" className={cn("flex items-center px-6 [.border-t]:pt-6", className)} {...props} />
  );
}

export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };


================================================
FILE: components/ui/dialog.tsx
================================================
"use client";

import * as React from "react";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";

import { cn } from "@/lib/utils";

function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
  return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}

function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}

function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}

function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}

function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
  return (
    <DialogPrimitive.Overlay
      data-slot="dialog-overlay"
      className={cn(
        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
        className
      )}
      {...props}
    />
  );
}

function DialogContent({
  className,
  children,
  showCloseButton = true,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
  showCloseButton?: boolean;
}) {
  return (
    <DialogPortal data-slot="dialog-portal">
      <DialogOverlay />
      <DialogPrimitive.Content
        data-slot="dialog-content"
        className={cn(
          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
          className
        )}
        {...props}
      >
        {children}
        {showCloseButton && (
          <DialogPrimitive.Close
            data-slot="dialog-close"
            className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
          >
            <XIcon />
            <span className="sr-only">Close</span>
          </DialogPrimitive.Close>
        )}
      </DialogPrimitive.Content>
    </DialogPortal>
  );
}

function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="dialog-header"
      className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
      {...props}
    />
  );
}

function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="dialog-footer"
      className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
      {...props}
    />
  );
}

function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
  return (
    <DialogPrimitive.Title
      data-slot="dialog-title"
      className={cn("text-lg leading-none font-semibold", className)}
      {...props}
    />
  );
}

function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {
  return (
    <DialogPrimitive.Description
      data-slot="dialog-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  );
}

export {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogOverlay,
  DialogPortal,
  DialogTitle,
  DialogTrigger,
};


================================================
FILE: components/ui/label.tsx
================================================
"use client";

import * as React from "react";
import { Label as LabelPrimitive } from "radix-ui";

import { cn } from "@/lib/utils";

function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
  return (
    <LabelPrimitive.Root
      data-slot="label"
      className={cn(
        "text-foreground text-sm leading-4 font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
        className
      )}
      {...props}
    />
  );
}

export { Label };


================================================
FILE: components/ui/popover.tsx
================================================
"use client";

import * as React from "react";
import { Popover as PopoverPrimitive } from "radix-ui";

import { cn } from "@/lib/utils";

function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
  return <PopoverPrimitive.Root data-slot="popover" {...props} />;
}

function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
}

function PopoverContent({
  className,
  align = "center",
  sideOffset = 4,
  showArrow = false,
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content> & {
  showArrow?: boolean;
}) {
  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Content
        data-slot="popover-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-hidden",
          className
        )}
        {...props}
      >
        {props.children}
        {showArrow && <PopoverPrimitive.Arrow className="fill-popover -my-px drop-shadow-[0_1px_0_var(--border)]" />}
      </PopoverPrimitive.Content>
    </PopoverPrimitive.Portal>
  );
}

function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
}

export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };


================================================
FILE: components/ui/revola.tsx
================================================
"use client";

import * as React from "react";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import { Drawer as DrawerPrimitive, Content as VaulDrawerContent } from "vaul";
import { cva } from "class-variance-authority";

import { cn } from "@/lib/utils";

import useMediaQuery from "@/hooks/use-media-query";

export type ResponsiveDialogProps = React.ComponentProps<typeof DrawerPrimitive.Root>;

type ResponsiveDialogContextProps = {
  modal?: boolean;
  dismissible?: boolean;
  direction?: "top" | "right" | "bottom" | "left";
  onlyDrawer?: boolean;
  onlyDialog?: boolean;
  alert?: boolean;
};

type ResponsiveDialogProviderProps = {
  children: React.ReactNode;
} & ResponsiveDialogContextProps;

const ResponsiveDialogContext = React.createContext<ResponsiveDialogContextProps | null>(null);
const MOBILE_BREAKPOINT = "(min-width: 640px)";

const ResponsiveDialogProvider = ({
  modal = true,
  dismissible = true,
  direction = "bottom",
  onlyDrawer = false,
  onlyDialog = false,
  alert = false,
  children,
}: ResponsiveDialogProviderProps) => {
  return (
    <ResponsiveDialogContext.Provider value={{ modal, dismissible, direction, onlyDrawer, onlyDialog, alert }}>
      {children}
    </ResponsiveDialogContext.Provider>
  );
};

export const useResponsiveDialog = () => {
  const context = React.useContext(ResponsiveDialogContext);

  if (!context) {
    throw new Error("useResponsiveDialog must be used within a <ResponsiveDialog />");
  }

  return context;
};

const ResponsiveDialog = ({
  modal = true,
  dismissible = true,
  direction = "bottom",
  onlyDrawer = false,
  onlyDialog = false,
  alert = false,
  shouldScaleBackground = true,
  open: controlledOpen,
  onOpenChange: controlledOnOpenChange,
  ...props
}: ResponsiveDialogProps & { onlyDrawer?: boolean; onlyDialog?: boolean; alert?: boolean }) => {
  const [internalState, setInternalState] = React.useState<boolean>(false);

  const isControlledOpen = typeof controlledOpen === "undefined";
  const toggleInternalState = () => setInternalState((prev) => !prev);

  const open = isControlledOpen ? internalState : controlledOpen;
  const onOpenChange = isControlledOpen ? toggleInternalState : controlledOnOpenChange;

  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialog = shouldUseDialog ? DialogPrimitive.Root : DrawerPrimitive.Root;

  const effectiveModal = alert ? true : modal;
  const effectiveDismissible = alert ? true : dismissible;

  return (
    <ResponsiveDialogProvider
      modal={effectiveModal}
      dismissible={effectiveDismissible}
      direction={direction}
      onlyDrawer={onlyDrawer}
      onlyDialog={onlyDialog}
      alert={alert}
    >
      <ResponsiveDialog
        modal={effectiveModal}
        direction={direction}
        dismissible={effectiveDismissible}
        shouldScaleBackground={shouldScaleBackground}
        open={open}
        onOpenChange={onOpenChange}
        {...props}
      />
    </ResponsiveDialogProvider>
  );
};
ResponsiveDialog.displayName = "ResponsiveDialog";

const ResponsiveDialogTrigger = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) => {
  const { onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogTrigger = shouldUseDialog ? DialogPrimitive.Trigger : DrawerPrimitive.Trigger;
  return <ResponsiveDialogTrigger {...props} />;
};
ResponsiveDialogTrigger.displayName = "ResponsiveDialogTrigger";

const ResponsiveDialogPortal = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) => {
  const { onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogPortal = shouldUseDialog ? DialogPrimitive.Portal : DrawerPrimitive.Portal;
  return <ResponsiveDialogPortal {...props} />;
};
ResponsiveDialogPortal.displayName = "ResponsiveDialogPortal";

const ResponsiveDialogOverlay = ({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) => {
  const { onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogOverlay = shouldUseDialog ? DialogPrimitive.Overlay : DrawerPrimitive.Overlay;
  return (
    <ResponsiveDialogOverlay
      {...props}
      className={cn(
        "sm:data-[state=open]:animate-in sm:data-[state=closed]:animate-out sm:data-[state=closed]:fade-out-0 sm:data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
        className
      )}
    />
  );
};
ResponsiveDialogOverlay.displayName = "ResponsiveDialogOverlay";

const ResponsiveDialogClose = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) => {
  const { dismissible, alert, onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogClose = shouldUseDialog ? DialogPrimitive.Close : DrawerPrimitive.Close;

  const shouldPreventClose = !dismissible && !alert;

  return (
    <ResponsiveDialogClose
      aria-label="Close"
      {...(shouldPreventClose && { onClick: (e) => e.preventDefault() })}
      {...props}
    />
  );
};
ResponsiveDialogClose.displayName = "ResponsiveDialogClose";

const ResponsiveDialogContentVariants = cva("fixed z-[9999] bg-background", {
  variants: {
    device: {
      desktop:
        "left-1/2 top-1/2 grid max-h-[calc(100%-4rem)] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:max-w-lg",
      mobile: "flex ",
    },
    direction: {
      bottom: "",
      top: "",
      left: "",
      right: "",
    },
  },
  defaultVariants: {
    device: "desktop",
    direction: "bottom",
  },
  compoundVariants: [
    {
      device: "mobile",
      direction: "bottom",
      className:
        "inset-x-0 bottom-0 mt-24 h-fit max-h-[65%] flex-col rounded-t-[10px] border border-b-0 border-primary/10",
    },
    {
      device: "mobile",
      direction: "top",
      className:
        "inset-x-0 top-0 mb-24 h-fit max-h-[65%] flex-col rounded-b-[10px] border border-b-0 border-primary/10",
    },
    {
      device: "mobile",
      direction: "left",
      className:
        "bottom-2 left-2 top-2 flex w-[310px] bg-transparent outline-none [--initial-transform:calc(100%+8px)]",
    },
    {
      device: "mobile",
      direction: "right",
      className: "bottom-2 right-2 top-2 w-[310px] bg-transparent outline-none [--initial-transform:calc(100%+8px)]",
    },
  ],
});

const ResponsiveDialogContent = React.forwardRef<
  HTMLDivElement,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
    showCloseButton?: boolean;
    /** Styles for the built in close button */
    closeButtonClassName?: string;
    /** Styles for the drag handle */
    dragHandleClassName?: string;
  }
>(({ className, children, showCloseButton = true, closeButtonClassName, dragHandleClassName, ...props }, ref) => {
  const { direction, modal, dismissible, alert, onlyDrawer, onlyDialog } = useResponsiveDialog();

  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);
  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogContent = shouldUseDialog ? DialogPrimitive.Content : VaulDrawerContent;

  const shouldShowCloseButton = !alert && showCloseButton;
  const shouldPreventEscape = !dismissible && !alert;
  const shouldPreventOutsideInteraction = !modal || (!dismissible && !alert) || alert;

  return (
    <ResponsiveDialogPortal>
      <ResponsiveDialogOverlay />
      <ResponsiveDialogContent
        ref={ref}
        {...props}
        {...(shouldPreventEscape && shouldUseDialog && { onEscapeKeyDown: (e) => e.preventDefault() })}
        {...(shouldPreventOutsideInteraction &&
          shouldUseDialog && {
            onInteractOutside: (e) => e.preventDefault(),
          })}
        {...(!shouldUseDialog &&
          shouldPreventOutsideInteraction && {
            onPointerDownOutside: (e) => e.preventDefault(),
            onInteractOutside: (e) => e.preventDefault(),
          })}
        className={cn(
          ResponsiveDialogContentVariants({
            device: shouldUseDialog ? "desktop" : "mobile",
            direction,
          }),
          className
        )}
      >
        {!shouldUseDialog && direction === "bottom" && (
          <div
            className={cn(
              "bg-muted-foreground/25 dark:bg-muted mx-auto my-4 h-1.5 w-14 rounded-full pb-1.5 data-[vaul-handle]:h-1.5 data-[vaul-handle]:w-14 data-[vaul-handle]:pb-1.5",
              dragHandleClassName
            )}
          />
        )}
        {children}
        {shouldShowCloseButton && (
          <ResponsiveDialogClose
            className={cn(
              "ring-offset-background focus-visible:ring-ring data-[state=open]:bg-accent absolute top-4 right-4 rounded-sm opacity-70 backdrop-blur-sm transition-opacity hover:opacity-100 focus:ring-offset-2 focus:outline-none focus-visible:ring-2 disabled:pointer-events-none data-[state=open]:text-white",
              closeButtonClassName
            )}
          >
            <X className="size-4" />
            <span className="sr-only">close</span>
          </ResponsiveDialogClose>
        )}
      </ResponsiveDialogContent>
    </ResponsiveDialogPortal>
  );
});
ResponsiveDialogContent.displayName = "ResponsiveDialogContent";

const ResponsiveDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
  return <div className={cn("flex flex-col gap-1.5 text-center sm:text-left", className)} {...props} />;
};
ResponsiveDialogHeader.displayName = "ResponsiveDialogHeader";

const ResponsiveDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
  return <footer className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)} {...props} />;
};
ResponsiveDialogFooter.displayName = "ResponsiveDialogFooter";

const ResponsiveDialogTitle = React.forwardRef<
  React.ComponentRef<typeof DialogPrimitive.Title>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => {
  const { onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogTitle = shouldUseDialog ? DialogPrimitive.Title : DrawerPrimitive.Title;
  return (
    <ResponsiveDialogTitle
      ref={ref}
      className={cn("text-lg leading-none font-semibold tracking-tight", className)}
      {...props}
    />
  );
});

ResponsiveDialogTitle.displayName = "ResponsiveDialogTitle";

const ResponsiveDialogDescription = React.forwardRef<
  React.ComponentRef<typeof DialogPrimitive.Description>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => {
  const { onlyDrawer, onlyDialog } = useResponsiveDialog();
  const isDesktop = useMediaQuery(MOBILE_BREAKPOINT);

  const shouldUseDialog = onlyDialog || (!onlyDrawer && isDesktop);
  const ResponsiveDialogDescription = shouldUseDialog ? DialogPrimitive.Description : DrawerPrimitive.Description;
  return (
    <ResponsiveDialogDescription ref={ref} className={cn("text-muted-foreground text-sm", className)} {...props} />
  );
});

ResponsiveDialogDescription.displayName = "ResponsiveDialogDescription";

export {
  ResponsiveDialog,
  ResponsiveDialogClose,
  ResponsiveDialogContent,
  ResponsiveDialogDescription,
  ResponsiveDialogFooter,
  ResponsiveDialogHeader,
  ResponsiveDialogOverlay,
  ResponsiveDialogPortal,
  ResponsiveDialogTitle,
  ResponsiveDialogTrigger,
};


================================================
FILE: components/ui/select.tsx
================================================
"use client";

import * as React from "react";

import * as SelectPrimitive from "@radix-ui/react-select";
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";

import { cn } from "@/lib/utils";

function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {
  return <SelectPrimitive.Root data-slot="select" {...props} />;
}

function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>) {
  return <SelectPrimitive.Group data-slot="select-group" {...props} />;
}

function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>) {
  return <SelectPrimitive.Value data-slot="select-value" {...props} />;
}

function SelectTrigger({
  className,
  size = "default",
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
  size?: "sm" | "default";
}) {
  return (
    <SelectPrimitive.Trigger
      data-slot="select-trigger"
      data-size={size}
      className={cn(
        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >
      {children}
      <SelectPrimitive.Icon asChild>
        <ChevronDownIcon className="size-4 opacity-50" />
      </SelectPrimitive.Icon>
    </SelectPrimitive.Trigger>
  );
}

function SelectContent({
  className,
  children,
  position = "popper",
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
  return (
    <SelectPrimitive.Portal>
      <SelectPrimitive.Content
        data-slot="select-content"
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
          position === "popper" &&
            "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
          className
        )}
        position={position}
        {...props}
      >
        <SelectScrollUpButton />
        <SelectPrimitive.Viewport
          className={cn(
            "p-1",
            position === "popper" &&
              "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
          )}
        >
          {children}
        </SelectPrimitive.Viewport>
        <SelectScrollDownButton />
      </SelectPrimitive.Content>
    </SelectPrimitive.Portal>
  );
}

function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>) {
  return (
    <SelectPrimitive.Label
      data-slot="select-label"
      className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
      {...props}
    />
  );
}

function SelectItem({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Item>) {
  return (
    <SelectPrimitive.Item
      data-slot="select-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
        className
      )}
      {...props}
    >
      <span className="absolute right-2 flex size-3.5 items-center justify-center">
        <SelectPrimitive.ItemIndicator>
          <CheckIcon className="size-4" />
        </SelectPrimitive.ItemIndicator>
      </span>
      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
    </SelectPrimitive.Item>
  );
}

function SelectSeparator({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
  return (
    <SelectPrimitive.Separator
      data-slot="select-separator"
      className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
      {...props}
    />
  );
}

function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
  return (
    <SelectPrimitive.ScrollUpButton
      data-slot="select-scroll-up-button"
      className={cn("flex cursor-default items-center justify-center py-1", className)}
      {...props}
    >
      <ChevronUpIcon className="size-4" />
    </SelectPrimitive.ScrollUpButton>
  );
}

function SelectScrollDownButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
  return (
    <SelectPrimitive.ScrollDownButton
      data-slot="select-scroll-down-button"
      className={cn("flex cursor-default items-center justify-center py-1", className)}
      {...props}
    >
      <ChevronDownIcon className="size-4" />
    </SelectPrimitive.ScrollDownButton>
  );
}

export {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectScrollDownButton,
  SelectScrollUpButton,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
};


================================================
FILE: components/ui/separator.tsx
================================================
"use client";

import * as React from "react";

import * as SeparatorPrimitive from "@radix-ui/react-separator";

import { cn } from "@/lib/utils";

function Separator({
  className,
  orientation = "horizontal",
  decorative = true,
  ...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
  return (
    <SeparatorPrimitive.Root
      data-slot="separator"
      decorative={decorative}
      orientation={orientation}
      className={cn(
        "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
        className
      )}
      {...props}
    />
  );
}

export { Separator };


================================================
FILE: components/ui/skeleton.tsx
================================================
import { cn } from "@/lib/utils";

function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="skeleton" className={cn("bg-accent animate-pulse rounded-md", className)} {...props} />;
}

export { Skeleton };


================================================
FILE: components/ui/tabs.tsx
================================================
"use client";

import * as React from "react";

import { Tabs as TabsPrimitive } from "@base-ui-components/react/tabs";
import posthog from "posthog-js";

import { cn } from "@/lib/utils";

function Tabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
  return <TabsPrimitive.Root data-slot="tabs" className={cn("flex flex-col gap-2", className)} {...props} />;
}

function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {
  return (
    <TabsPrimitive.List
      data-slot="tabs-list"
      className={cn(
        "bg-muted text-muted-foreground inline-flex h-7 w-fit items-center justify-center rounded-lg p-[3px]",
        className
      )}
      {...props}
    />
  );
}

function TabsTrigger({ className, onClick, value, ...props }: React.ComponentProps<typeof TabsPrimitive.Tab>) {
  return (
    <TabsPrimitive.Tab
      data-slot="tabs-trigger"
      className={cn(
        "dark:data-[selected]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      onClick={(e) => {
        try {
          if (value) {
            posthog.capture("tabs_select", { value });
          }
        } catch {}
        onClick?.(e);
      }}
      value={value}
      {...props}
    />
  );
}

function TabsIndicator({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Indicator>) {
  return (
    <TabsPrimitive.Indicator
      data-slot="tabs-indicator"
      className={cn(
        "border-border bg-surface absolute top-1/2 left-0 z-[-1] h-[calc(100%-1px)] w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-1/2 rounded border transition-all duration-200 ease-in-out",
        className
      )}
      {...props}
    />
  );
}

function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Panel>) {
  return <TabsPrimitive.Panel data-slot="tabs-content" className={cn("flex-1 outline-none", className)} {...props} />;
}

export { Tabs, TabsList, TabsTrigger, TabsIndicator, TabsContent };


================================================
FILE: components/ui/tooltip.tsx
================================================
"use client";

import * as React from "react";

import * as TooltipPrimitive from "@radix-ui/react-tooltip";

import { cn } from "@/lib/utils";

function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
  return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />;
}

function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
  return (
    <TooltipProvider>
      <TooltipPrimitive.Root data-slot="tooltip" {...props} />
    </TooltipProvider>
  );
}

function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}

function TooltipContent({
  className,
  sideOffset = 0,
  children,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        sideOffset={sideOffset}
        className={cn(
          "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
          className
        )}
        {...props}
      >
        {children}
        <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  );
}

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };


================================================
FILE: components/usage.tsx
================================================
import AnchorHeading from "@/components/anchor-heading";
import CodeDisplaySection from "@/components/code-display-block";

import { getComponentCode } from "@/lib/code-highlight";

export default async function Usage() {
  const language = "tsx";

  const [radixCodeData, baseCodeData] = await Promise.all([
    getComponentCode("", language, "registry/radix-ui/examples/usage-demo.tsx"),
    getComponentCode("", language, "registry/base-ui/examples/usage-demo.tsx"),
  ]);

  const variantData = {
    base: {
      code: baseCodeData,
    },
    radix: {
      code: radixCodeData,
    },
  } as const;

  return (
    <section className="min-w-0 space-y-6 sm:px-6">
      <div className="space-y-1">
        <AnchorHeading id="usage">Usage</AnchorHeading>
        <p className="text-muted-foreground leading-7">
          Add the adaptive scroll area component to your project with ease.
        </p>
      </div>
      <CodeDisplaySection variantData={variantData} language={language} maxHeight="[&_pre]:max-h-[400px]" />
    </section>
  );
}


================================================
FILE: components/variant-select.tsx
================================================
"use client";

import { useEffect, useState } from "react";

import posthog from "posthog-js";

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";

import { cn } from "@/lib/utils";
import { useVariantStore, type Variant } from "@/lib/variant-store";

type VariantSelectProps = {
  size?: "sm" | "default";
};

export default function VariantSelect({ size = "default" }: VariantSelectProps) {
  const { setVariant, currentVariant } = useVariantStore();
  const [isHydrated, setIsHydrated] = useState(false);

  useEffect(() => {
    setIsHydrated(true);
  }, []);

  const handleVariantChange = (value: Variant) => {
    try {
      setVariant(value);
      posthog.capture("variant_change", { to: value });
    } catch {}
  };

  return (
    <Select value={isHydrated ? currentVariant : undefined} onValueChange={handleVariantChange}>
      <SelectTrigger size={size} className={cn("h-7 text-sm font-medium [&_svg]:h-4 [&_svg]:w-4")}>
        <span className="text-muted-foreground font-normal">Variant: </span>
        {isHydrated ? <SelectValue placeholder="Configuration:" /> : <Skeleton className="h-4 w-16" />}
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="radix" aria-label="Switch to Radix UI variant">
          Radix UI
        </SelectItem>
        <SelectItem value="base" aria-label="Switch to Base UI variant">
          Base UI
        </SelectItem>
      </SelectContent>
    </Select>
  );
}


================================================
FILE: components/why.tsx
================================================
import AnchorHeading from "@/components/anchor-heading";

export default function Why() {
  return (
    <section className="sm:px-6 lg:max-w-[750px]">
      <div className="space-y-6">
        <div className="space-y-1">
          <AnchorHeading id="why" className="scroll-m-12">
            Why
          </AnchorHeading>
          <p className="text-muted-foreground leading-7">Why did I create Lina in the first place?</p>
        </div>
        <div className="space-y-8">
          <div>
            <h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">The Problem</h3>
            <p className="leading-7 [&:not(:first-child)]:mt-6">
              The main reason behind it was that custom scrollbar/scrollarea components looked amazing on desktop, but
              on mobile or general touch devices, the native scrollbars were just better, and I wanted to use those
              instead of a custom scrollbar.
            </p>
          </div>

          <div>
            <h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">From Quick Fix to Full Component</h3>
            <p className="leading-7 [&:not(:first-child)]:mt-6">
              So I wrote a simple version back at the end of 2024 while working on one of our office projects. Fast
              forward to now, while building scrollable examples for{" "}
              <a href="https://revola.sameerjs.com" target="_blank" rel="" className="underline underline-offset-2">
                Revola
              </a>
              , I decided to turn that simple version into a full-fledged separate component. So I did.
            </p>
          </div>

          <div>
            <h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">The Details</h3>
            <p className="leading-7 [&:not(:first-child)]:mt-6">
              The initial version of Lina only had micro-interactions and native scrollable adaptation. But then I
              started noticing the details that make interfaces feel premium.
            </p>
            <p className="leading-7 [&:not(:first-child)]:mt-4">
              While using{" "}
              <a href="https://v0.app" target="_blank" className="underline underline-offset-2">
                v0
              </a>
              , I spotted this smooth mask just above the chat input, content would fade in so elegantly. That visual
              caught my eye, and I knew I had to add it. These little touches make all the difference.
            </p>
          </div>

          <div>
            <h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">The Goal</h3>
            <blockquote className="mt-6 border-l-2 pl-6 italic">
              &quot;If wer&rsquo;e going the custom ScrollArea route, why not make it look and feel purely custom,
              refined and detailed?.&quot;
            </blockquote>
            <p className="leading-7 [&:not(:first-child)]:mt-6">
              That&rsquo;s the philosophy behind Lina. No half-measures, no compromises, just a scrolling experience
              that feels intentionally well crafted.
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}


================================================
FILE: components.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

================================================
FILE: eslint.config.mjs
================================================
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript"),
];

export default eslintConfig;


================================================
FILE: hooks/use-copy-button.ts
================================================
"use client";

import { useCallback, useEffect, useRef, useState, type MouseEventHandler } from "react";

export function useCopyButton(onCopy: () => void): [checked: boolean, onClick: MouseEventHandler] {
  const [checked, setChecked] = useState(false);
  const timeoutRef = useRef<number | null>(null);
  const callbackRef = useRef(onCopy);
  callbackRef.current = onCopy;

  const onClick: MouseEventHandler = useCallback(() => {
    if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
    timeoutRef.current = window.setTimeout(() => {
      setChecked(false);
    }, 1500);
    callbackRef.current();
    setChecked(true);
  }, []);

  // Avoid updates after being unmounted
  useEffect(() => {
    return () => {
      if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
    };
  }, []);

  return [checked, onClick];
}


================================================
FILE: hooks/use-has-primary-touch.tsx
================================================
import { useEffect, useState } from "react";

export function useTouchPrimary() {
  const [isTouchPrimary, setIsTouchPrimary] = useState(false);

  useEffect(() => {
    if (typeof window === "undefined") return;

    const controller = new AbortController();
    const { signal } = controller;

    const handleTouch = () => {
      const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
      const prefersTouch = window.matchMedia("(pointer: coarse)").matches;
      setIsTouchPrimary(hasTouch && prefersTouch);
    };

    const mq = window.matchMedia("(pointer: coarse)");
    mq.addEventListener("change", handleTouch, { signal });
    window.addEventListener("pointerdown", handleTouch, { signal });

    handleTouch();

    return () => controller.abort();
  }, []);

  return isTouchPrimary;
}


================================================
FILE: hooks/use-media-query.tsx
================================================
"use client";

import { useEffect, useState } from "react";

const useMediaQuery = (query: string) => {
  const [matches, setMatches] = useState<boolean | null>(null);

  useEffect(() => {
    const media = window.matchMedia(query);

    if (media.matches !== matches) setMatches(media.matches);

    const listener = () => setMatches(media.matches);

    window.addEventListener("resize", listener);
    return () => window.removeEventListener("resize", listener);
  }, [query, matches]);

  return matches;
};

export default useMediaQuery;


================================================
FILE: hooks/use-meta-color.ts
================================================
import * as React from "react";

import { useTheme } from "next-themes";

export const META_THEME_COLORS = {
  light: "#ffffff",
  dark: "#0a0a0a",
};

export function useMetaColor() {
  const { resolvedTheme } = useTheme();

  const metaColor = React.useMemo(() => {
    return resolvedTheme !== "dark" ? META_THEME_COLORS.light : META_THEME_COLORS.dark;
  }, [resolvedTheme]);

  const setMetaColor = React.useCallback((color: string) => {
    document.querySelector('meta[name="theme-color"]')?.setAttribute("content", color);
  }, []);

  return {
    metaColor,
    setMetaColor,
  };
}


================================================
FILE: lib/adaptive-mask-animation.ts
================================================
import { MotionValue } from "motion/react";

// Configuration constants
export const SCROLL_CONFIG = {
  vertical: { max: 105, hold: 105, return: 40 },
  horizontal: { max: 370, curve: 10 },
  timing: { cycle: 280, fps: 16 },
} as const;

// Content bounds for mask calculations
export const getContentBounds = (containerWidth: number = SCROLL_CONFIG.horizontal.max) => ({
  top: 0,
  bottom: -SCROLL_CONFIG.vertical.max,
  left: 0,
  right: -containerWidth,
});

// Animation phases configuration
export type AnimationPhase = {
  name: string;
  duration: number;
};

export const animationPhases: AnimationPhase[] = [
  { name: "scrollDown", duration: 50 },
  { name: "holdBottom", duration: 10 },
  { name: "scrollUp", duration: 20 },
  { name: "scrollRight", duration: 50 },
  { name: "holdRight", duration: 10 },
  { name: "scrollLeft", duration: 50 },
  { name: "holdLeft", duration: 10 },
  { name: "scrollUp", duration: 40 },
  { name: "reset", duration: 40 },
];

// Easing functions
export const easeOut = (t: number) => 1 - Math.pow(1 - t, 2.5);
export const curvedEasing = (t: number) => {
  const spring = 1 - Math.pow(1 - t, 1.8);
  return spring * (1 + 0.15 * Math.sin(t * Math.PI));
};

// Spring configurations
export const springConfigs = {
  content: { stiffness: 180, damping: 25, mass: 0.6 },
  masks: { stiffness: 400, damping: 30, mass: 0.3 },
} as const;

// Position-based mask logic
export const updateMasksBasedOnPosition = (
  x: number,
  y: number,
  topMaskTarget: MotionValue<number>,
  bottomMaskTarget: MotionValue<number>,
  leftMaskTarget: MotionValue<number>,
  rightMaskTarget: MotionValue<number>,
  containerWidth: number = SCROLL_CONFIG.horizontal.max
) => {
  const threshold = 2; // Smaller threshold for faster mask response
  const contentBounds = getContentBounds(containerWidth);

  // Top mask: visible when scrolled down (y is negative)
  const topMaskVisible = y < contentBounds.top - threshold;
  topMaskTarget.set(topMaskVisible ? 1 : 0);

  // Bottom mask: visible when NOT at bottom (can scroll down more)
  const bottomMaskVisible = y > contentBounds.bottom + threshold;
  bottomMaskTarget.set(bottomMaskVisible ? 1 : 0);

  // Left mask: visible when scrolled right (x is negative)
  const leftMaskVisible = x < contentBounds.left - threshold;
  leftMaskTarget.set(leftMaskVisible ? 1 : 0);

  // Right mask: visible when NOT at right edge (can scroll right more)
  const rightMaskVisible = x > contentBounds.right + threshold;
  rightMaskTarget.set(rightMaskVisible ? 1 : 0);
};

// Calculate content position for current animation phase
export const calculateContentPosition = (
  currentPhase: AnimationPhase,
  phaseProgress: number,
  containerWidth: number = SCROLL_CONFIG.horizontal.max
): { x: number; y: number } => {
  const easeProgress = easeOut(phaseProgress);
  let targetX = 0;
  let targetY = 0;

  switch (currentPhase.name) {
    case "scrollDown":
      targetX = 0;
      targetY = -easeProgress * SCROLL_CONFIG.vertical.max;
      break;
    case "holdBottom":
      targetX = 0;
      targetY = -SCROLL_CONFIG.vertical.hold;
      break;
    case "scrollUp":
      targetX = 0;
      targetY = -SCROLL_CONFIG.vertical.return;
      break;
    case "scrollRight":
      const curvedProgress = curvedEasing(phaseProgress);
      const verticalCurve = Math.sin(phaseProgress * Math.PI) * SCROLL_CONFIG.horizontal.curve;
      targetX = -curvedProgress * containerWidth;
      targetY = -SCROLL_CONFIG.vertical.return - verticalCurve;
      break;
    case "holdRight":
      targetX = -containerWidth;
      targetY = -SCROLL_CONFIG.vertical.return;
      break;
    case "scrollLeft":
      const leftProgress = curvedEasing(phaseProgress);
      const leftCurve = Math.sin(phaseProgress * Math.PI) * (SCROLL_CONFIG.horizontal.curve * 0.75);
      targetX = -containerWidth + leftProgress * containerWidth;
      targetY = -SCROLL_CONFIG.vertical.return + leftCurve;
      break;
    case "holdLeft":
      targetX = 0;
      targetY = -SCROLL_CONFIG.vertical.return;
      break;
    case "scrollUp":
      targetX = 0;
      targetY = -180 + easeProgress * 180;
      break;
    case "reset":
      targetX = 0;
      targetY = 0;
      break;
  }

  return { x: targetX, y: targetY };
};

// Find current animation phase based on step
export const getCurrentPhase = (currentStep: number): { phase: AnimationPhase | null; progress: number } => {
  let accumulatedDuration = 0;
  let currentPhase = null;
  let phaseStart = 0;

  for (const phase of animationPhases) {
    const phaseEnd = accumulatedDuration + phase.duration;
    if (currentStep >= accumulatedDuration && currentStep < phaseEnd) {
      currentPhase = phase;
      phaseStart = accumulatedDuration;
      break;
    }
    accumulatedDuration = phaseEnd;
  }

  if (!currentPhase) {
    return { phase: null, progress: 0 };
  }

  const phaseProgress = (currentStep - phaseStart) / currentPhase.duration;
  return { phase: currentPhase, progress: phaseProgress };
};

// Get total animation duration
export const getTotalDuration = () => animationPhases.reduce((sum, phase) => sum + phase.duration, 0);


================================================
FILE: lib/code-highlight.ts
================================================
"use server";

import "server-only";

import fs from "fs/promises";
import path from "path";
import type { JSX } from "react";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
import { codeToHast, type BundledLanguage } from "shiki/bundle/web";

async function highlight(code: string, lang: BundledLanguage) {
  const hast = await codeToHast(code, {
    lang,
    defaultColor: false,
    themes: {
      light: "github-light",
      dark: "github-dark",
    },
    transformers: [
      {
        pre(node) {
          node.properties["class"] =
            "min-w-0 px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0 !bg-transparent";
        },
        code(node) {
          node.properties["data-line-numbers"] = "";
        },
        line(node) {
          node.properties["data-line"] = "";
        },
      },
    ],
  });

  return toJsxRuntime(hast, {
    Fragment,
    jsx,
    jsxs,
  }) as JSX.Element;
}

// Simple in-memory cache for component code
const codeCache = new Map<string, { code: string; highlightedCode: JSX.Element } | null>();

async function getComponentCode(
  name: string,
  lang: BundledLanguage,
  customFilePath?: string
): Promise<{ code: string; highlightedCode: JSX.Element } | null> {
  const cacheKey = `${name}-${lang}-${customFilePath || ""}`;

  // Check cache first
  if (codeCache.has(cacheKey)) {
    return codeCache.get(cacheKey)!;
  }

  try {
    let rawContent = "";

    if (customFilePath) {
      const filePath = path.join(process.cwd(), customFilePath);
      rawContent = await fs.readFile(filePath, "utf-8");
    } else {
      const publicPath = path.join("public", "r", `${name}.json`);
      const filePath = path.join(process.cwd(), publicPath);
      const fileContent = await fs.readFile(filePath, "utf-8");
      const data = JSON.parse(fileContent);
      rawContent = data.files[0].content || "";
    }

    if (!rawContent.trim()) {
      codeCache.set(cacheKey, null);
      return null;
    }

    const highlightedCode = await highlight(rawContent, lang);
    const result = { code: rawContent, highlightedCode };

    // Cache the result
    codeCache.set(cacheKey, result);

    return result;
  } catch (error) {
    console.error("Error getting component code", error);
    codeCache.set(cacheKey, null);
    return null;
  }
}

export { highlight, getComponentCode };


================================================
FILE: lib/package-manager-store.ts
================================================
"use client";

import { persist } from "zustand/middleware";

import { create } from "zustand";

type PackageManager = "pnpm" | "npm" | "bun" | "yarn";

type PackageManagerState = {
  selectedPackageManager: PackageManager;
  isLoading: boolean;
  setPackageManager: (pm: PackageManager) => void;
  setLoading: (loading: boolean) => void;
};

const usePackageManagerStore = create<PackageManagerState>()(
  persist(
    (set) => ({
      selectedPackageManager: "pnpm",
      isLoading: true,
      setPackageManager: (pm) => set({ selectedPackageManager: pm }),
      setLoading: (loading) => set({ isLoading: loading }),
    }),
    {
      name: "package-manager-store",
    }
  )
);

export { usePackageManagerStore, type PackageManager };


================================================
FILE: lib/package-manager-utils.ts
================================================
import type { PackageManager } from "@/lib/package-manager-store";

type CommandMapping = {
  npm: string[];
  pnpm: string[];
  yarn: string[];
  bun: string[];
};

const NPX_COMMAND_MAPPINGS: CommandMapping = {
  npm: ["npx"],
  pnpm: ["pnpm", "dlx"],
  yarn: ["yarn", "dlx"],
  bun: ["bun", "x"],
};

const PACKAGE_COMMAND_MAPPINGS: CommandMapping = {
  npm: ["npm", "install"],
  pnpm: ["pnpm", "add"],
  yarn: ["yarn", "add"],
  bun: ["bun", "install"],
};

function isNpxCommand(command: string): boolean {
  return command.startsWith("npx ");
}

export function convertNpmCommand(npmCommand: string): Record<PackageManager, string> {
  const parts = npmCommand.split(" ");
  const isNpx = isNpxCommand(npmCommand);

  if (isNpx) {
    return convertNpxStyle(parts);
  }

  const mappings = PACKAGE_COMMAND_MAPPINGS;

  if (!mappings) {
    // If we don't have specific mappings, keep the command as is for all managers
    return {
      npm: npmCommand,
      pnpm: npmCommand.replace("npm", "pnpm"),
      yarn: npmCommand.replace("npm", "yarn"),
      bun: npmCommand.replace("npm", "bun"),
    };
  }

  const commands: Record<PackageManager, string> = {} as Record<PackageManager, string>;

  Object.entries(mappings).forEach(([pm, prefixParts]) => {
    commands[pm as PackageManager] = [...prefixParts, ...parts.slice(2)].join(" ");
  });

  return commands;
}

function convertNpxStyle(parts: string[]): Record<PackageManager, string> {
  const commands: Record<PackageManager, string> = {
    npm: parts.join(" "),
    pnpm: "",
    yarn: "",
    bun: "",
  };

  Object.entries(NPX_COMMAND_MAPPINGS).forEach(([pm, prefixParts]) => {
    if (pm === "npm") return;

    commands[pm as PackageManager] = [...prefixParts, ...parts.slice(1)].join(" ");
  });

  return commands;
}


================================================
FILE: lib/utils.ts
================================================
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}


================================================
FILE: lib/variant-store.ts
================================================
"use client";

import { persist } from "zustand/middleware";

import { create } from "zustand";

type Variant = "base" | "radix";

type VariantState = {
  currentVariant: Variant;
  setVariant: (variant: Variant) => void;
};

const useVariantStore = create<VariantState>()(
  persist(
    (set) => ({
      currentVariant: "radix",
      setVariant: (variant) =>
        set({
          currentVariant: variant,
        }),
    }),
    {
      name: "variant-store",
    }
  )
);

export { useVariantStore, type Variant };


================================================
FILE: next.config.ts
================================================
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [{ hostname: "images.unsplash.com" }],
  },
  // Ensure non-imported files read at runtime (e.g. with fs) are included in the output
  // so they are available on Vercel's serverless filesystem.
  outputFileTracingIncludes: {
    "/(app|pages|api)/**": ["./registry/**", "./hooks/**"],
  },
};

export default nextConfig;


================================================
FILE: package.json
================================================
{
  "name": "lina",
  "version": "1.0.0",
  "description": "Lina is a drop‑in replacement for shadcn/ui's `ScrollArea` with better defaults and a more native feel.",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "typecheck": "tsc --noEmit",
    "registry:build": "shadcn build",
    "changeset": "changeset",
    "changeset:version": "changeset version",
    "changeset:publish": "changeset publish",
    "format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
    "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache"
  },
  "dependencies": {
    "@base-ui-components/react": "1.0.0-beta.2",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-scroll-area": "^1.2.9",
    "@radix-ui/react-select": "^2.2.6",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tooltip": "^1.2.7",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "hast-util-to-jsx-runtime": "^2.3.6",
    "lucide-react": "^0.536.0",
    "motion": "^12.23.12",
    "next": "15.4.5",
    "next-themes": "^0.4.6",
    "posthog-js": "^1.260.2",
    "radix-ui": "^1.4.3",
    "react": "19.1.0",
    "react-dom": "19.1.0",
    "server-only": "^0.0.1",
    "shadcn": "2.9.3-canary.0",
    "shiki": "^3.9.1",
    "tailwind-merge": "^3.3.1",
    "vaul": "^1.1.2",
    "zustand": "^5.0.7"
  },
  "devDependencies": {
    "@changesets/cli": "^2.29.5",
    "@eslint/eslintrc": "^3",
    "@ianvs/prettier-plugin-sort-imports": "^4.5.1",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.4.5",
    "postcss": "^8.5.6",
    "prettier": "^3.6.2",
    "prettier-plugin-tailwindcss": "^0.6.14",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.3.6",
    "typescript": "^5"
  }
}


================================================
FILE: postcss.config.mjs
================================================
const config = {
  plugins: ["@tailwindcss/postcss"],
};

export default config;


================================================
FILE: public/r/command-base.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-base",
  "type": "registry:ui",
  "title": "Command based on Lina's Base UI variant",
  "description": "A command component built using Lina's Base UI",
  "dependencies": [
    "lucide-react",
    "cmdk"
  ],
  "registryDependencies": [
    "button",
    "dialog",
    "https://lina.sameer.sh/r/lina-base.json"
  ],
  "files": [
    {
      "path": "registry/base-ui/examples/command.tsx",
      "content": "\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { ScrollArea } from \"@/registry/base-ui/scroll-area\";\r\n\r\nimport { Command as CommandPrimitive } from \"cmdk\";\r\nimport { SearchIcon } from \"lucide-react\";\r\n\r\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@/components/ui/dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\r\n  return (\r\n    <CommandPrimitive\r\n      data-slot=\"command\"\r\n      className={cn(\"bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-md\", className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandDialog({\r\n  title = \"Command Palette\",\r\n  description = \"Search for a command to run...\",\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<typeof Dialog> & {\r\n  title?: string;\r\n  description?: string;\r\n}) {\r\n  return (\r\n    <Dialog {...props}>\r\n      <DialogHeader className=\"sr-only\">\r\n        <DialogTitle>{title}</DialogTitle>\r\n        <DialogDescription>{description}</DialogDescription>\r\n      </DialogHeader>\r\n      <DialogContent className=\"overflow-hidden p-0 sm:max-w-lg [&>button:last-child]:hidden\">\r\n        <Command className=\"[&_[cmdk-group-heading]]:text-muted-foreground max-h-[100svh] **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-3 [&_[cmdk-item]]:py-2\">\r\n          {children}\r\n        </Command>\r\n      </DialogContent>\r\n    </Dialog>\r\n  );\r\n}\r\n\r\nfunction CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {\r\n  return (\r\n    <div className=\"border-input flex items-center border-b px-5\" cmdk-input-wrapper=\"\">\r\n      <SearchIcon size={20} className=\"text-muted-foreground/80 me-3\" />\r\n      <CommandPrimitive.Input\r\n        data-slot=\"command-input-wrapper\"\r\n        className={cn(\r\n          \"placeholder:text-muted-foreground/70 flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50\",\r\n          className\r\n        )}\r\n        {...props}\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\r\n  return (\r\n    <ScrollArea\r\n      maskClassName=\"before:from-popover after:from-popover\"\r\n      className=\"flex max-h-full flex-col overflow-hidden\"\r\n    >\r\n      <CommandPrimitive.List data-slot=\"command-list\" className={cn(\"max-h-80 flex-1\", className)} {...props} />\r\n    </ScrollArea>\r\n  );\r\n}\r\n\r\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\r\n  return <CommandPrimitive.Empty data-slot=\"command-empty\" className=\"py-6 text-center text-sm\" {...props} />;\r\n}\r\n\r\nfunction CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {\r\n  return (\r\n    <CommandPrimitive.Group\r\n      data-slot=\"command-group\"\r\n      className={cn(\r\n        \"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-2 [&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {\r\n  return (\r\n    <CommandPrimitive.Separator\r\n      data-slot=\"command-separator\"\r\n      className={cn(\"bg-border -mx-1 h-px\", className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\r\n  return (\r\n    <CommandPrimitive.Item\r\n      data-slot=\"command-item\"\r\n      className={cn(\r\n        \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-3 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\r\n  return (\r\n    <kbd\r\n      data-slot=\"command-shortcut\"\r\n      className={cn(\r\n        \"bg-background text-muted-foreground/70 ms-auto -me-1 inline-flex h-5 max-h-full items-center rounded border px-1 font-[inherit] text-[0.625rem] font-medium\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Command,\r\n  CommandDialog,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandInput,\r\n  CommandItem,\r\n  CommandList,\r\n  CommandSeparator,\r\n  CommandShortcut,\r\n};\r\n",
      "type": "registry:ui"
    }
  ]
}

================================================
FILE: public/r/command-radix.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-radix",
  "type": "registry:ui",
  "title": "Command based on Lina's Radix UI variant",
  "description": "A command component built using Lina's Radix UI",
  "dependencies": [
    "lucide-react",
    "cmdk"
  ],
  "registryDependencies": [
    "button",
    "dialog",
    "https://lina.sameer.sh/r/lina-radix.json"
  ],
  "files": [
    {
      "path": "registry/radix-ui/examples/command.tsx",
      "content": "\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { ScrollArea } from \"@/registry/radix-ui/scroll-area\";\r\n\r\nimport { Command as CommandPrimitive } from \"cmdk\";\r\nimport { SearchIcon } from \"lucide-react\";\r\n\r\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@/components/ui/dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\r\n  return (\r\n    <CommandPrimitive\r\n      data-slot=\"command\"\r\n      className={cn(\"bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-md\", className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandDialog({\r\n  title = \"Command Palette\",\r\n  description = \"Search for a command to run...\",\r\n  children,\r\n  ...props\r\n}: React.ComponentProps<typeof Dialog> & {\r\n  title?: string;\r\n  description?: string;\r\n}) {\r\n  return (\r\n    <Dialog {...props}>\r\n      <DialogHeader className=\"sr-only\">\r\n        <DialogTitle>{title}</DialogTitle>\r\n        <DialogDescription>{description}</DialogDescription>\r\n      </DialogHeader>\r\n      <DialogContent className=\"overflow-hidden p-0 sm:max-w-lg [&>button:last-child]:hidden\">\r\n        <Command className=\"[&_[cmdk-group-heading]]:text-muted-foreground max-h-[100svh] **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-3 [&_[cmdk-item]]:py-2\">\r\n          {children}\r\n        </Command>\r\n      </DialogContent>\r\n    </Dialog>\r\n  );\r\n}\r\n\r\nfunction CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {\r\n  return (\r\n    <div className=\"border-input flex items-center border-b px-5\" cmdk-input-wrapper=\"\">\r\n      <SearchIcon size={20} className=\"text-muted-foreground/80 me-3\" />\r\n      <CommandPrimitive.Input\r\n        data-slot=\"command-input-wrapper\"\r\n        className={cn(\r\n          \"placeholder:text-muted-foreground/70 flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50\",\r\n          className\r\n        )}\r\n        {...props}\r\n      />\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\r\n  return (\r\n    <ScrollArea\r\n      maskClassName=\"before:from-popover after:from-popover\"\r\n      className=\"flex max-h-full flex-col overflow-hidden\"\r\n    >\r\n      <CommandPrimitive.List data-slot=\"command-list\" className={cn(\"max-h-80 flex-1\", className)} {...props} />\r\n    </ScrollArea>\r\n  );\r\n}\r\n\r\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\r\n  return <CommandPrimitive.Empty data-slot=\"command-empty\" className=\"py-6 text-center text-sm\" {...props} />;\r\n}\r\n\r\nfunction CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {\r\n  return (\r\n    <CommandPrimitive.Group\r\n      data-slot=\"command-group\"\r\n      className={cn(\r\n        \"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-2 [&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {\r\n  return (\r\n    <CommandPrimitive.Separator\r\n      data-slot=\"command-separator\"\r\n      className={cn(\"bg-border -mx-1 h-px\", className)}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\r\n  return (\r\n    <CommandPrimitive.Item\r\n      data-slot=\"command-item\"\r\n      className={cn(\r\n        \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-3 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\r\n  return (\r\n    <kbd\r\n      data-slot=\"command-shortcut\"\r\n      className={cn(\r\n        \"bg-background text-muted-foreground/70 ms-auto -me-1 inline-flex h-5 max-h-full items-center rounded border px-1 font-[inherit] text-[0.625rem] font-medium\",\r\n        className\r\n      )}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport {\r\n  Command,\r\n  CommandDialog,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandInput,\r\n  CommandItem,\r\n  CommandList,\r\n  CommandSeparator,\r\n  CommandShortcut,\r\n};\r\n",
      "type": "registry:ui"
    }
  ]
}

================================================
FILE: public/r/horizontal-scroll-demo-base.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "horizontal-scroll-demo-base",
  "type": "registry:component",
  "title": "Horizontal Scroll Area Base Demo",
  "description": "A demo showcasing the horizontal scroll area component using Lina's Base UI",
  "registryDependencies": [
    "https://lina.sameer.sh/r/lina-base.json"
  ],
  "files": [
    {
      "path": "registry/base-ui/examples/horizontal-scroll.tsx",
      "content": "import Image from \"next/image\";\r\nimport { ScrollArea, ScrollBar } from \"@/registry/base-ui/scroll-area\";\r\n\r\nexport interface Artwork {\r\n  artist: string;\r\n  art: string;\r\n}\r\n\r\nconst works: Artwork[] = [\r\n  {\r\n    artist: \"Ornella Binni\",\r\n    art: \"https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n  {\r\n    artist: \"Tom Byrom\",\r\n    art: \"https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n  {\r\n    artist: \"Vladimir Malyavko\",\r\n    art: \"https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n];\r\n\r\nexport function ScrollAreaHorizontalDemo() {\r\n  return (\r\n    <ScrollArea className=\"bg-background w-96 rounded-md border whitespace-nowrap\">\r\n      <div className=\"flex w-max space-x-4 p-4\">\r\n        {works.map((artwork) => (\r\n          <figure key={artwork.artist} className=\"shrink-0\">\r\n            <div className=\"overflow-hidden rounded-md\">\r\n              <Image\r\n                src={artwork.art}\r\n                alt={`Photo by ${artwork.artist}`}\r\n                className=\"aspect-[3/4] h-fit w-fit object-cover\"\r\n                width={300}\r\n                height={400}\r\n              />\r\n            </div>\r\n            <figcaption className=\"text-muted-foreground pt-2 text-xs\">\r\n              Photo by <span className=\"text-foreground font-semibold\">{artwork.artist}</span>\r\n            </figcaption>\r\n          </figure>\r\n        ))}\r\n      </div>\r\n      <ScrollBar orientation=\"horizontal\" />\r\n    </ScrollArea>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/r/horizontal-scroll-demo-radix.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "horizontal-scroll-demo-radix",
  "type": "registry:component",
  "title": "Horizontal Scroll Area Radix Demo",
  "description": "A demo showcasing the horizontal scroll area component using Lina's Radix UI",
  "registryDependencies": [
    "https://lina.sameer.sh/r/lina-radix.json"
  ],
  "files": [
    {
      "path": "registry/radix-ui/examples/horizontal-scroll.tsx",
      "content": "import Image from \"next/image\";\r\nimport { ScrollArea, ScrollBar } from \"@/registry/radix-ui/scroll-area\";\r\n\r\nexport interface Artwork {\r\n  artist: string;\r\n  art: string;\r\n}\r\n\r\nconst works: Artwork[] = [\r\n  {\r\n    artist: \"Ornella Binni\",\r\n    art: \"https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n  {\r\n    artist: \"Tom Byrom\",\r\n    art: \"https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n  {\r\n    artist: \"Vladimir Malyavko\",\r\n    art: \"https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80\",\r\n  },\r\n];\r\n\r\nexport function ScrollAreaHorizontalDemo() {\r\n  return (\r\n    <ScrollArea className=\"bg-background w-96 rounded-md border whitespace-nowrap\">\r\n      <div className=\"flex w-max space-x-4 p-4\">\r\n        {works.map((artwork) => (\r\n          <figure key={artwork.artist} className=\"shrink-0\">\r\n            <div className=\"overflow-hidden rounded-md\">\r\n              <Image\r\n                src={artwork.art}\r\n                alt={`Photo by ${artwork.artist}`}\r\n                className=\"aspect-[3/4] h-fit w-fit object-cover\"\r\n                width={300}\r\n                height={400}\r\n              />\r\n            </div>\r\n            <figcaption className=\"text-muted-foreground pt-2 text-xs\">\r\n              Photo by <span className=\"text-foreground font-semibold\">{artwork.artist}</span>\r\n            </figcaption>\r\n          </figure>\r\n        ))}\r\n      </div>\r\n      <ScrollBar orientation=\"horizontal\" />\r\n    </ScrollArea>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/r/lina-base.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lina-base",
  "type": "registry:ui",
  "title": "Base UI Adaptive Scroll Area",
  "description": "A responsive scroll area that feel native on touch devices, offereing custom styling and enchanced interactions where it matter most.",
  "dependencies": [
    "@base-ui-components/react"
  ],
  "devDependencies": [
    "tw-animate-css"
  ],
  "files": [
    {
      "path": "registry/base-ui/scroll-area.tsx",
      "content": "\"use client\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui-components/react/scroll-area\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nimport { useTouchPrimary } from \"@/hooks/use-has-primary-touch\";\r\n\r\ntype Mask = {\r\n  top: boolean;\r\n  bottom: boolean;\r\n  left: boolean;\r\n  right: boolean;\r\n};\r\n\r\nexport type ScrollAreaContextProps = {\r\n  isTouch: boolean;\r\n  type: \"auto\" | \"always\" | \"scroll\" | \"hover\";\r\n};\r\n\r\nconst ScrollAreaContext = React.createContext<ScrollAreaContextProps>({\r\n  isTouch: false,\r\n  type: \"hover\",\r\n});\r\n\r\nconst ScrollArea = React.forwardRef<\r\n  React.ComponentRef<typeof ScrollAreaPrimitive.Root>,\r\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {\r\n    type?: \"auto\" | \"always\" | \"scroll\" | \"hover\";\r\n    viewportClassName?: string;\r\n    /**\r\n     * `maskHeight` is the height of the mask in pixels.\r\n     * pass `0` to disable the mask\r\n     * @default 30\r\n     */\r\n    maskHeight?: number;\r\n    maskClassName?: string;\r\n  }\r\n>(({ className, children, type = \"hover\", maskHeight = 30, maskClassName, viewportClassName, ...props }, ref) => {\r\n  const [showMask, setShowMask] = React.useState<Mask>({\r\n    top: false,\r\n    bottom: false,\r\n    left: false,\r\n    right: false,\r\n  });\r\n\r\n  const viewportRef = React.useRef<HTMLDivElement>(null);\r\n  const isTouch = useTouchPrimary();\r\n\r\n  const checkScrollability = React.useCallback(() => {\r\n    const element = viewportRef.current;\r\n    if (!element) return;\r\n\r\n    const { scrollTop, scrollLeft, scrollWidth, clientWidth, scrollHeight, clientHeight } = element;\r\n    setShowMask((prev) => ({\r\n      ...prev,\r\n      top: scrollTop > 0,\r\n      bottom: scrollTop + clientHeight < scrollHeight - 1,\r\n      left: scrollLeft > 0,\r\n      right: scrollLeft + clientWidth < scrollWidth - 1,\r\n    }));\r\n  }, []);\r\n\r\n  React.useEffect(() => {\r\n    if (typeof window === \"undefined\") return;\r\n\r\n    const element = viewportRef.current;\r\n    if (!element) return;\r\n\r\n    const controller = new AbortController();\r\n    const { signal } = controller;\r\n\r\n    const resizeObserver = new ResizeObserver(checkScrollability);\r\n    resizeObserver.observe(element);\r\n\r\n    element.addEventListener(\"scroll\", checkScrollability, { signal });\r\n    window.addEventListener(\"resize\", checkScrollability, { signal });\r\n\r\n    // Run an initial check whenever dependencies change (including pointer mode)\r\n    checkScrollability();\r\n\r\n    return () => {\r\n      controller.abort();\r\n      resizeObserver.disconnect();\r\n    };\r\n  }, [checkScrollability, isTouch]);\r\n\r\n  return (\r\n    <ScrollAreaContext.Provider value={{ isTouch, type }}>\r\n      {isTouch ? (\r\n        <div\r\n          ref={ref}\r\n          {...props}\r\n          role=\"group\"\r\n          data-slot=\"scroll-area\"\r\n          aria-roledescription=\"scroll area\"\r\n          className={cn(\"relative overflow-hidden\", className)}\r\n        >\r\n          <div ref={viewportRef} className={cn(\"size-full overflow-auto\", viewportClassName)} tabIndex={0}>\r\n            {children}\r\n          </div>\r\n          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}\r\n        </div>\r\n      ) : (\r\n        <ScrollAreaPrimitive.Root\r\n          ref={ref}\r\n          data-slot=\"scroll-area\"\r\n          className={cn(\"relative overflow-hidden\", viewportClassName, className)}\r\n          {...props}\r\n        >\r\n          <ScrollAreaPrimitive.Viewport\r\n            ref={viewportRef}\r\n            data-slot=\"scroll-area-viewport\"\r\n            className={cn(\"focus-ring size-full rounded-[inherit]\", viewportClassName)}\r\n          >\r\n            {children}\r\n          </ScrollAreaPrimitive.Viewport>\r\n          <ScrollBar />\r\n          <ScrollAreaPrimitive.Corner />\r\n          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}\r\n        </ScrollAreaPrimitive.Root>\r\n      )}\r\n    </ScrollAreaContext.Provider>\r\n  );\r\n});\r\n\r\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\r\n\r\nconst ScrollBar = React.forwardRef<\r\n  React.ComponentRef<typeof ScrollAreaPrimitive.Scrollbar>,\r\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Scrollbar>\r\n>(({ className, orientation = \"vertical\", ...props }, ref) => {\r\n  const { isTouch, type } = React.useContext(ScrollAreaContext);\r\n\r\n  if (isTouch) return null;\r\n\r\n  return (\r\n    <ScrollAreaPrimitive.Scrollbar\r\n      ref={ref}\r\n      orientation={orientation}\r\n      data-slot=\"scroll-area-scrollbar\"\r\n      className={cn(\r\n        \"hover:bg-muted dark:hover:bg-muted/50 flex touch-none p-px transition-[colors,opacity] duration-150 ease-out select-none\",\r\n        orientation === \"vertical\" && \"h-full w-2.5 border-l border-l-transparent\",\r\n        orientation === \"horizontal\" && \"h-2.5 flex-col border-t border-t-transparent px-1 pr-1.25\",\r\n        type === \"hover\" && \"opacity-0 data-[hovering]:opacity-100\",\r\n        type === \"scroll\" && \"opacity-0 data-[scrolling]:opacity-100\",\r\n        className\r\n      )}\r\n      {...props}\r\n    >\r\n      <ScrollAreaPrimitive.Thumb\r\n        data-slot=\"scroll-area-thumb\"\r\n        className={cn(\r\n          \"bg-border relative flex-1 rounded-full transition-[scale]\",\r\n          orientation === \"vertical\" && \"my-1 active:scale-y-95\",\r\n          orientation === \"horizontal\" && \"active:scale-x-98\"\r\n        )}\r\n      />\r\n    </ScrollAreaPrimitive.Scrollbar>\r\n  );\r\n});\r\n\r\nScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;\r\n\r\nconst ScrollMask = ({\r\n  showMask,\r\n  maskHeight,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<\"div\"> & {\r\n  showMask: Mask;\r\n  maskHeight: number;\r\n}) => {\r\n  return (\r\n    <>\r\n      <div\r\n        {...props}\r\n        aria-hidden=\"true\"\r\n        style={\r\n          {\r\n            \"--top-fade-height\": showMask.top ? `${maskHeight}px` : \"0px\",\r\n            \"--bottom-fade-height\": showMask.bottom ? `${maskHeight}px` : \"0px\",\r\n          } as React.CSSProperties\r\n        }\r\n        className={cn(\r\n          \"pointer-events-none absolute inset-0 z-10\",\r\n          \"before:absolute before:inset-x-0 before:top-0 before:transition-[height,opacity] before:duration-300 before:content-['']\",\r\n          \"after:absolute after:inset-x-0 after:bottom-0 after:transition-[height,opacity] after:duration-300 after:content-['']\",\r\n          \"before:h-(--top-fade-height) after:h-(--bottom-fade-height)\",\r\n          showMask.top ? \"before:opacity-100\" : \"before:opacity-0\",\r\n          showMask.bottom ? \"after:opacity-100\" : \"after:opacity-0\",\r\n          \"before:from-background before:bg-gradient-to-b before:to-transparent\",\r\n          \"after:from-background after:bg-gradient-to-t after:to-transparent\",\r\n          className\r\n        )}\r\n      />\r\n      <div\r\n        {...props}\r\n        aria-hidden=\"true\"\r\n        style={\r\n          {\r\n            \"--left-fade-width\": showMask.left ? `${maskHeight}px` : \"0px\",\r\n            \"--right-fade-width\": showMask.right ? `${maskHeight}px` : \"0px\",\r\n          } as React.CSSProperties\r\n        }\r\n        className={cn(\r\n          \"pointer-events-none absolute inset-0 z-10\",\r\n          \"before:absolute before:inset-y-0 before:left-0 before:transition-[width,opacity] before:duration-300 before:content-['']\",\r\n          \"after:absolute after:inset-y-0 after:right-0 after:transition-[width,opacity] after:duration-300 after:content-['']\",\r\n          \"before:w-(--left-fade-width) after:w-(--right-fade-width)\",\r\n          showMask.left ? \"before:opacity-100\" : \"before:opacity-0\",\r\n          showMask.right ? \"after:opacity-100\" : \"after:opacity-0\",\r\n          \"before:from-background before:bg-gradient-to-r before:to-transparent\",\r\n          \"after:from-background after:bg-gradient-to-l after:to-transparent\",\r\n          className\r\n        )}\r\n      />\r\n    </>\r\n  );\r\n};\r\n\r\nexport { ScrollArea, ScrollBar };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "hooks/use-has-primary-touch.tsx",
      "content": "import { useEffect, useState } from \"react\";\r\n\r\nexport function useTouchPrimary() {\r\n  const [isTouchPrimary, setIsTouchPrimary] = useState(false);\r\n\r\n  useEffect(() => {\r\n    if (typeof window === \"undefined\") return;\r\n\r\n    const controller = new AbortController();\r\n    const { signal } = controller;\r\n\r\n    const handleTouch = () => {\r\n      const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\r\n      const prefersTouch = window.matchMedia(\"(pointer: coarse)\").matches;\r\n      setIsTouchPrimary(hasTouch && prefersTouch);\r\n    };\r\n\r\n    const mq = window.matchMedia(\"(pointer: coarse)\");\r\n    mq.addEventListener(\"change\", handleTouch, { signal });\r\n    window.addEventListener(\"pointerdown\", handleTouch, { signal });\r\n\r\n    handleTouch();\r\n\r\n    return () => controller.abort();\r\n  }, []);\r\n\r\n  return isTouchPrimary;\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}

================================================
FILE: public/r/lina-radix.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lina-radix",
  "type": "registry:ui",
  "title": "Radix Adaptive Scroll Area",
  "description": "A responsive scroll area that feels native on touch devices, offering custom styling and enhanced interactions where it matters most.",
  "dependencies": [
    "@radix-ui/react-scroll-area"
  ],
  "devDependencies": [
    "tw-animate-css"
  ],
  "files": [
    {
      "path": "registry/radix-ui/scroll-area.tsx",
      "content": "\"use client\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nimport { useTouchPrimary } from \"@/hooks/use-has-primary-touch\";\r\n\r\nconst ScrollAreaContext = React.createContext<boolean>(false);\r\ntype Mask = {\r\n  top: boolean;\r\n  bottom: boolean;\r\n  left: boolean;\r\n  right: boolean;\r\n};\r\n\r\nconst ScrollArea = React.forwardRef<\r\n  React.ComponentRef<typeof ScrollAreaPrimitive.Root>,\r\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {\r\n    viewportClassName?: string;\r\n    /**\r\n     * `maskHeight` is the height of the mask in pixels.\r\n     * pass `0` to disable the mask\r\n     * @default 30\r\n     */\r\n    maskHeight?: number;\r\n    maskClassName?: string;\r\n  }\r\n>(({ className, children, scrollHideDelay = 0, viewportClassName, maskClassName, maskHeight = 30, ...props }, ref) => {\r\n  const [showMask, setShowMask] = React.useState<Mask>({\r\n    top: false,\r\n    bottom: false,\r\n    left: false,\r\n    right: false,\r\n  });\r\n  const viewportRef = React.useRef<HTMLDivElement>(null);\r\n  const isTouch = useTouchPrimary();\r\n\r\n  const checkScrollability = React.useCallback(() => {\r\n    const element = viewportRef.current;\r\n    if (!element) return;\r\n\r\n    const { scrollTop, scrollLeft, scrollWidth, clientWidth, scrollHeight, clientHeight } = element;\r\n    setShowMask((prev) => ({\r\n      ...prev,\r\n      top: scrollTop > 0,\r\n      bottom: scrollTop + clientHeight < scrollHeight - 1,\r\n      left: scrollLeft > 0,\r\n      right: scrollLeft + clientWidth < scrollWidth - 1,\r\n    }));\r\n  }, []);\r\n\r\n  React.useEffect(() => {\r\n    if (typeof window === \"undefined\") return;\r\n\r\n    const element = viewportRef.current;\r\n    if (!element) return;\r\n\r\n    const controller = new AbortController();\r\n    const { signal } = controller;\r\n\r\n    const resizeObserver = new ResizeObserver(checkScrollability);\r\n    resizeObserver.observe(element);\r\n\r\n    element.addEventListener(\"scroll\", checkScrollability, { signal });\r\n    window.addEventListener(\"resize\", checkScrollability, { signal });\r\n\r\n    // Run an initial check whenever dependencies change (including pointer mode)\r\n    checkScrollability();\r\n\r\n    return () => {\r\n      controller.abort();\r\n      resizeObserver.disconnect();\r\n    };\r\n  }, [checkScrollability, isTouch]);\r\n\r\n  return (\r\n    <ScrollAreaContext.Provider value={isTouch}>\r\n      {isTouch ? (\r\n        <div\r\n          ref={ref}\r\n          role=\"group\"\r\n          data-slot=\"scroll-area\"\r\n          aria-roledescription=\"scroll area\"\r\n          className={cn(\"relative overflow-hidden\", className)}\r\n          {...props}\r\n        >\r\n          <div\r\n            ref={viewportRef}\r\n            data-slot=\"scroll-area-viewport\"\r\n            className={cn(\"size-full overflow-auto rounded-[inherit]\", viewportClassName)}\r\n            tabIndex={0}\r\n          >\r\n            {children}\r\n          </div>\r\n\r\n          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}\r\n        </div>\r\n      ) : (\r\n        <ScrollAreaPrimitive.Root\r\n          ref={ref}\r\n          data-slot=\"scroll-area\"\r\n          scrollHideDelay={scrollHideDelay}\r\n          className={cn(\"relative overflow-hidden\", className)}\r\n          {...props}\r\n        >\r\n          <ScrollAreaPrimitive.Viewport\r\n            ref={viewportRef}\r\n            data-slot=\"scroll-area-viewport\"\r\n            className={cn(\"size-full rounded-[inherit]\", viewportClassName)}\r\n          >\r\n            {children}\r\n          </ScrollAreaPrimitive.Viewport>\r\n\r\n          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}\r\n          <ScrollBar />\r\n          <ScrollAreaPrimitive.Corner />\r\n        </ScrollAreaPrimitive.Root>\r\n      )}\r\n    </ScrollAreaContext.Provider>\r\n  );\r\n});\r\n\r\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\r\n\r\nconst ScrollBar = React.forwardRef<\r\n  React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\r\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\r\n>(({ className, orientation = \"vertical\", ...props }, ref) => {\r\n  const isTouch = React.useContext(ScrollAreaContext);\r\n\r\n  if (isTouch) return null;\r\n\r\n  return (\r\n    <ScrollAreaPrimitive.ScrollAreaScrollbar\r\n      ref={ref}\r\n      orientation={orientation}\r\n      data-slot=\"scroll-area-scrollbar\"\r\n      className={cn(\r\n        \"hover:bg-muted dark:hover:bg-muted/50 data-[state=visible]:fade-in-0 data-[state=hidden]:fade-out-0 data-[state=visible]:animate-in data-[state=hidden]:animate-out flex touch-none p-px transition-[colors] duration-150 select-none\",\r\n        orientation === \"vertical\" && \"h-full w-2.5 border-l border-l-transparent\",\r\n        orientation === \"horizontal\" && \"h-2.5 flex-col border-t border-t-transparent px-1 pr-1.25\",\r\n        className\r\n      )}\r\n      {...props}\r\n    >\r\n      <ScrollAreaPrimitive.ScrollAreaThumb\r\n        data-slot=\"scroll-area-thumb\"\r\n        className={cn(\r\n          \"bg-border relative flex-1 origin-center rounded-full transition-[scale]\",\r\n          orientation === \"vertical\" && \"my-1 active:scale-y-95\",\r\n          orientation === \"horizontal\" && \"active:scale-x-98\"\r\n        )}\r\n      />\r\n    </ScrollAreaPrimitive.ScrollAreaScrollbar>\r\n  );\r\n});\r\n\r\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;\r\n\r\nconst ScrollMask = ({\r\n  showMask,\r\n  maskHeight,\r\n  className,\r\n  ...props\r\n}: React.ComponentProps<\"div\"> & {\r\n  showMask: Mask;\r\n  maskHeight: number;\r\n}) => {\r\n  return (\r\n    <>\r\n      <div\r\n        {...props}\r\n        aria-hidden=\"true\"\r\n        style={\r\n          {\r\n            \"--top-fade-height\": showMask.top ? `${maskHeight}px` : \"0px\",\r\n            \"--bottom-fade-height\": showMask.bottom ? `${maskHeight}px` : \"0px\",\r\n          } as React.CSSProperties\r\n        }\r\n        className={cn(\r\n          \"pointer-events-none absolute inset-0 z-10\",\r\n          \"before:absolute before:inset-x-0 before:top-0 before:transition-[height,opacity] before:duration-300 before:content-['']\",\r\n          \"after:absolute after:inset-x-0 after:bottom-0 after:transition-[height,opacity] after:duration-300 after:content-['']\",\r\n          \"before:h-(--top-fade-height) after:h-(--bottom-fade-height)\",\r\n          showMask.top ? \"before:opacity-100\" : \"before:opacity-0\",\r\n          showMask.bottom ? \"after:opacity-100\" : \"after:opacity-0\",\r\n          \"before:from-background before:bg-gradient-to-b before:to-transparent\",\r\n          \"after:from-background after:bg-gradient-to-t after:to-transparent\",\r\n          className\r\n        )}\r\n      />\r\n      <div\r\n        {...props}\r\n        aria-hidden=\"true\"\r\n        style={\r\n          {\r\n            \"--left-fade-width\": showMask.left ? `${maskHeight}px` : \"0px\",\r\n            \"--right-fade-width\": showMask.right ? `${maskHeight}px` : \"0px\",\r\n          } as React.CSSProperties\r\n        }\r\n        className={cn(\r\n          \"pointer-events-none absolute inset-0 z-10\",\r\n          \"before:absolute before:inset-y-0 before:left-0 before:transition-[width,opacity] before:duration-300 before:content-['']\",\r\n          \"after:absolute after:inset-y-0 after:right-0 after:transition-[width,opacity] after:duration-300 after:content-['']\",\r\n          \"before:w-(--left-fade-width) after:w-(--right-fade-width)\",\r\n          showMask.left ? \"before:opacity-100\" : \"before:opacity-0\",\r\n          showMask.right ? \"after:opacity-100\" : \"after:opacity-0\",\r\n          \"before:from-background before:bg-gradient-to-r before:to-transparent\",\r\n          \"after:from-background after:bg-gradient-to-l after:to-transparent\",\r\n          className\r\n        )}\r\n      />\r\n    </>\r\n  );\r\n};\r\n\r\nexport { ScrollArea, ScrollBar };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "hooks/use-has-primary-touch.tsx",
      "content": "import { useEffect, useState } from \"react\";\r\n\r\nexport function useTouchPrimary() {\r\n  const [isTouchPrimary, setIsTouchPrimary] = useState(false);\r\n\r\n  useEffect(() => {\r\n    if (typeof window === \"undefined\") return;\r\n\r\n    const controller = new AbortController();\r\n    const { signal } = controller;\r\n\r\n    const handleTouch = () => {\r\n      const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\r\n      const prefersTouch = window.matchMedia(\"(pointer: coarse)\").matches;\r\n      setIsTouchPrimary(hasTouch && prefersTouch);\r\n    };\r\n\r\n    const mq = window.matchMedia(\"(pointer: coarse)\");\r\n    mq.addEventListener(\"change\", handleTouch, { signal });\r\n    window.addEventListener(\"pointerdown\", handleTouch, { signal });\r\n\r\n    handleTouch();\r\n\r\n    return () => controller.abort();\r\n  }, []);\r\n\r\n  return isTouchPrimary;\r\n}\r\n",
      "type": "registry:hook"
    }
  ]
}

================================================
FILE: public/r/timezone-select-demo-base.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "timezone-select-demo-base",
  "type": "registry:component",
  "title": "Timezone Select Base UI Demo",
  "description": "A demo showcasing the timezone select component using Lina based on Base UI",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "label",
    "popover",
    "https://lina.sameer.sh/r/lina-base.json",
    "https://lina.sameer.sh/r/command-base.json"
  ],
  "files": [
    {
      "path": "registry/base-ui/examples/timezone-select.tsx",
      "content": "\"use client\";\r\n\r\nimport { useId, useMemo, useState } from \"react\";\r\nimport {\r\n  Command,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandInput,\r\n  CommandItem,\r\n  CommandList,\r\n} from \"@/registry/base-ui/examples/command\";\r\n\r\nimport { CheckIcon, ChevronDownIcon } from \"lucide-react\";\r\n\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nexport function TimezonSelectExample() {\r\n  const id = useId();\r\n  const [open, setOpen] = useState<boolean>(false);\r\n  const [value, setValue] = useState<string>(\"Asia/Calcutta\");\r\n\r\n  const timezones = Intl.supportedValuesOf(\"timeZone\");\r\n\r\n  const formattedTimezones = useMemo(() => {\r\n    return timezones\r\n      .map((timezone) => {\r\n        const formatter = new Intl.DateTimeFormat(\"en\", {\r\n          timeZone: timezone,\r\n          timeZoneName: \"shortOffset\",\r\n        });\r\n        const parts = formatter.formatToParts(new Date());\r\n        const offset = parts.find((part) => part.type === \"timeZoneName\")?.value || \"\";\r\n        const modifiedOffset = offset === \"GMT\" ? \"GMT+0\" : offset;\r\n\r\n        return {\r\n          value: timezone,\r\n          label: `(${modifiedOffset}) ${timezone.replace(/_/g, \" \")}`,\r\n          numericOffset: parseInt(offset.replace(\"GMT\", \"\").replace(\"+\", \"\") || \"0\"),\r\n        };\r\n      })\r\n      .sort((a, b) => a.numericOffset - b.numericOffset);\r\n  }, [timezones]);\r\n\r\n  return (\r\n    <div className=\"*:not-first:mt-2\">\r\n      <Label htmlFor={id}>Timezone select with search</Label>\r\n      <Popover open={open} onOpenChange={setOpen}>\r\n        <PopoverTrigger asChild>\r\n          <Button\r\n            id={id}\r\n            variant=\"outline\"\r\n            role=\"combobox\"\r\n            aria-expanded={open}\r\n            className=\"bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]\"\r\n          >\r\n            <span className={cn(\"truncate\", !value && \"text-muted-foreground\")}>\r\n              {value ? formattedTimezones.find((timezone) => timezone.value === value)?.label : \"Select timezone\"}\r\n            </span>\r\n            <ChevronDownIcon size={16} className=\"text-muted-foreground/80 shrink-0\" aria-hidden=\"true\" />\r\n          </Button>\r\n        </PopoverTrigger>\r\n        <PopoverContent className=\"border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0\" align=\"start\">\r\n          <Command\r\n            filter={(value, search) => {\r\n              const normalizedValue = value.toLowerCase();\r\n              const normalizedSearch = search.toLowerCase().replace(/\\s+/g, \"\");\r\n              return normalizedValue.includes(normalizedSearch) ? 1 : 0;\r\n            }}\r\n          >\r\n            <CommandInput placeholder=\"Search timezone...\" />\r\n            <CommandList>\r\n              <CommandEmpty>No timezone found.</CommandEmpty>\r\n              <CommandGroup>\r\n                {formattedTimezones.map(({ value: itemValue, label }) => (\r\n                  <CommandItem\r\n                    key={itemValue}\r\n                    value={itemValue}\r\n                    onSelect={(currentValue) => {\r\n                      setValue(currentValue === value ? \"\" : currentValue);\r\n                      setOpen(false);\r\n                    }}\r\n                  >\r\n                    {label}\r\n                    {value === itemValue && <CheckIcon size={16} className=\"ml-auto\" />}\r\n                  </CommandItem>\r\n                ))}\r\n              </CommandGroup>\r\n            </CommandList>\r\n          </Command>\r\n        </PopoverContent>\r\n      </Popover>\r\n    </div>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/r/timezone-select-demo-radix.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "timezone-select-demo-radix",
  "type": "registry:component",
  "title": "Timezone Select Radix Demo",
  "description": "A demo showcasing the timezone select component using Lina based on Radix UI",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "label",
    "popover",
    "https://lina.sameer.sh/r/lina-radix.json",
    "https://lina.sameer.sh/r/command-radix.json"
  ],
  "files": [
    {
      "path": "registry/radix-ui/examples/timezone-select.tsx",
      "content": "\"use client\";\r\n\r\nimport { useId, useMemo, useState } from \"react\";\r\nimport {\r\n  Command,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandInput,\r\n  CommandItem,\r\n  CommandList,\r\n} from \"@/registry/radix-ui/examples/command\";\r\n\r\nimport { CheckIcon, ChevronDownIcon } from \"lucide-react\";\r\n\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nexport function TimezoneSelectExample() {\r\n  const id = useId();\r\n  const [open, setOpen] = useState<boolean>(false);\r\n  const [value, setValue] = useState<string>(\"Asia/Calcutta\");\r\n\r\n  const timezones = Intl.supportedValuesOf(\"timeZone\");\r\n\r\n  const formattedTimezones = useMemo(() => {\r\n    return timezones\r\n      .map((timezone) => {\r\n        const formatter = new Intl.DateTimeFormat(\"en\", {\r\n          timeZone: timezone,\r\n          timeZoneName: \"shortOffset\",\r\n        });\r\n        const parts = formatter.formatToParts(new Date());\r\n        const offset = parts.find((part) => part.type === \"timeZoneName\")?.value || \"\";\r\n        const modifiedOffset = offset === \"GMT\" ? \"GMT+0\" : offset;\r\n\r\n        return {\r\n          value: timezone,\r\n          label: `(${modifiedOffset}) ${timezone.replace(/_/g, \" \")}`,\r\n          numericOffset: parseInt(offset.replace(\"GMT\", \"\").replace(\"+\", \"\") || \"0\"),\r\n        };\r\n      })\r\n      .sort((a, b) => a.numericOffset - b.numericOffset);\r\n  }, [timezones]);\r\n\r\n  return (\r\n    <div className=\"*:not-first:mt-2\">\r\n      <Label htmlFor={id}>Timezone select with search</Label>\r\n      <Popover open={open} onOpenChange={setOpen}>\r\n        <PopoverTrigger asChild>\r\n          <Button\r\n            id={id}\r\n            variant=\"outline\"\r\n            role=\"combobox\"\r\n            aria-expanded={open}\r\n            className=\"bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]\"\r\n          >\r\n            <span className={cn(\"truncate\", !value && \"text-muted-foreground\")}>\r\n              {value ? formattedTimezones.find((timezone) => timezone.value === value)?.label : \"Select timezone\"}\r\n            </span>\r\n            <ChevronDownIcon size={16} className=\"text-muted-foreground/80 shrink-0\" aria-hidden=\"true\" />\r\n          </Button>\r\n        </PopoverTrigger>\r\n        <PopoverContent className=\"border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0\" align=\"start\">\r\n          <Command\r\n            filter={(value, search) => {\r\n              const normalizedValue = value.toLowerCase();\r\n              const normalizedSearch = search.toLowerCase().replace(/\\s+/g, \"\");\r\n              return normalizedValue.includes(normalizedSearch) ? 1 : 0;\r\n            }}\r\n          >\r\n            <CommandInput placeholder=\"Search timezone...\" />\r\n            <CommandList>\r\n              <CommandEmpty>No timezone found.</CommandEmpty>\r\n              <CommandGroup>\r\n                {formattedTimezones.map(({ value: itemValue, label }) => (\r\n                  <CommandItem\r\n                    key={itemValue}\r\n                    value={itemValue}\r\n                    onSelect={(currentValue) => {\r\n                      setValue(currentValue === value ? \"\" : currentValue);\r\n                      setOpen(false);\r\n                    }}\r\n                  >\r\n                    {label}\r\n                    {value === itemValue && <CheckIcon size={16} className=\"ml-auto\" />}\r\n                  </CommandItem>\r\n                ))}\r\n              </CommandGroup>\r\n            </CommandList>\r\n          </Command>\r\n        </PopoverContent>\r\n      </Popover>\r\n    </div>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/r/vertical-scroll-demo-base.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "vertical-scroll-demo-base",
  "type": "registry:component",
  "title": "Vertical Scroll Area Base Demo",
  "description": "A demo showcasing the vertical scroll area component using Lina's Base UI",
  "registryDependencies": [
    "https://lina.sameer.sh/r/lina-base.json"
  ],
  "files": [
    {
      "path": "registry/base-ui/examples/vertical-scroll.tsx",
      "content": "import * as React from \"react\";\r\nimport { ScrollArea } from \"@/registry/base-ui/scroll-area\";\r\n\r\nimport { Separator } from \"@/components/ui/separator\";\r\n\r\nconst tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);\r\n\r\nexport function ScrollAreaDemo() {\r\n  return (\r\n    <ScrollArea className=\"bg-background h-72 w-48 rounded-md border\">\r\n      <div className=\"p-4\">\r\n        <h4 className=\"mb-4 text-sm leading-none font-medium\">Tags</h4>\r\n        {tags.map((tag) => (\r\n          <React.Fragment key={tag}>\r\n            <div className=\"text-sm\">{tag}</div>\r\n            <Separator className=\"my-2\" />\r\n          </React.Fragment>\r\n        ))}\r\n      </div>\r\n    </ScrollArea>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/r/vertical-scroll-demo-radix.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "vertical-scroll-demo-radix",
  "type": "registry:component",
  "title": "Vertical Scroll Area Radix Demo",
  "description": "A demo showcasing the vertical scroll area component using Lina's Radix UI",
  "registryDependencies": [
    "https://lina.sameer.sh/r/lina-radix.json"
  ],
  "files": [
    {
      "path": "registry/radix-ui/examples/vertical-scroll.tsx",
      "content": "import * as React from \"react\";\r\nimport { ScrollArea } from \"@/registry/radix-ui/scroll-area\";\r\n\r\nimport { Separator } from \"@/components/ui/separator\";\r\n\r\nconst tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);\r\n\r\nexport function ScrollAreaDemo() {\r\n  return (\r\n    <ScrollArea className=\"bg-background h-72 w-48 rounded-md border\">\r\n      <div className=\"p-4\">\r\n        <h4 className=\"mb-4 text-sm leading-none font-medium\">Tags</h4>\r\n        {tags.map((tag) => (\r\n          <React.Fragment key={tag}>\r\n            <div className=\"text-sm\">{tag}</div>\r\n            <Separator className=\"my-2\" />\r\n          </React.Fragment>\r\n        ))}\r\n      </div>\r\n    </ScrollArea>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}

================================================
FILE: public/site.webmanifest
================================================
{
  "name2": "Lina",
  "short_name": "Lina",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ],
  "theme_color": "#ffffff",
  "background_color": "#000000",
  "display": "standalone"
}


================================================
FILE: registry/base-ui/examples/command.tsx
================================================
"use client";

import * as React from "react";
import { ScrollArea } from "@/registry/base-ui/scroll-area";

import { Command as CommandPrimitive } from "cmdk";
import { SearchIcon } from "lucide-react";

import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";

import { cn } from "@/lib/utils";

function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
  return (
    <CommandPrimitive
      data-slot="command"
      className={cn("bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-md", className)}
      {...props}
    />
  );
}

function CommandDialog({
  title = "Command Palette",
  description = "Search for a command to run...",
  children,
  ...props
}: React.ComponentProps<typeof Dialog> & {
  title?: string;
  description?: string;
}) {
  return (
    <Dialog {...props}>
      <DialogHeader className="sr-only">
        <DialogTitle>{title}</DialogTitle>
        <DialogDescription>{description}</DialogDescription>
      </DialogHeader>
      <DialogContent className="overflow-hidden p-0 sm:max-w-lg [&>button:last-child]:hidden">
        <Command className="[&_[cmdk-group-heading]]:text-muted-foreground max-h-[100svh] **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-3 [&_[cmdk-item]]:py-2">
          {children}
        </Command>
      </DialogContent>
    </Dialog>
  );
}

function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
  return (
    <div className="border-input flex items-center border-b px-5" cmdk-input-wrapper="">
      <SearchIcon size={20} className="text-muted-foreground/80 me-3" />
      <CommandPrimitive.Input
        data-slot="command-input-wrapper"
        className={cn(
          "placeholder:text-muted-foreground/70 flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        {...props}
      />
    </div>
  );
}

function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
  return (
    <ScrollArea
      maskClassName="before:from-popover after:from-popover"
      className="flex max-h-full flex-col overflow-hidden"
    >
      <CommandPrimitive.List data-slot="command-list" className={cn("max-h-80 flex-1", className)} {...props} />
    </ScrollArea>
  );
}

function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
  return <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} />;
}

function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
  return (
    <CommandPrimitive.Group
      data-slot="command-group"
      className={cn(
        "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-2 [&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
        className
      )}
      {...props}
    />
  );
}

function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
  return (
    <CommandPrimitive.Separator
      data-slot="command-separator"
      className={cn("bg-border -mx-1 h-px", className)}
      {...props}
    />
  );
}

function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
  return (
    <CommandPrimitive.Item
      data-slot="command-item"
      className={cn(
        "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-3 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
        className
      )}
      {...props}
    />
  );
}

function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
  return (
    <kbd
      data-slot="command-shortcut"
      className={cn(
        "bg-background text-muted-foreground/70 ms-auto -me-1 inline-flex h-5 max-h-full items-center rounded border px-1 font-[inherit] text-[0.625rem] font-medium",
        className
      )}
      {...props}
    />
  );
}

export {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
};


================================================
FILE: registry/base-ui/examples/horizontal-scroll.tsx
================================================
import Image from "next/image";
import { ScrollArea, ScrollBar } from "@/registry/base-ui/scroll-area";

export interface Artwork {
  artist: string;
  art: string;
}

const works: Artwork[] = [
  {
    artist: "Ornella Binni",
    art: "https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80",
  },
  {
    artist: "Tom Byrom",
    art: "https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80",
  },
  {
    artist: "Vladimir Malyavko",
    art: "https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80",
  },
];

export function ScrollAreaHorizontalDemo() {
  return (
    <ScrollArea className="bg-background w-96 rounded-md border whitespace-nowrap">
      <div className="flex w-max space-x-4 p-4">
        {works.map((artwork) => (
          <figure key={artwork.artist} className="shrink-0">
            <div className="overflow-hidden rounded-md">
              <Image
                src={artwork.art}
                alt={`Photo by ${artwork.artist}`}
                className="aspect-[3/4] h-fit w-fit object-cover"
                width={300}
                height={400}
              />
            </div>
            <figcaption className="text-muted-foreground pt-2 text-xs">
              Photo by <span className="text-foreground font-semibold">{artwork.artist}</span>
            </figcaption>
          </figure>
        ))}
      </div>
      <ScrollBar orientation="horizontal" />
    </ScrollArea>
  );
}


================================================
FILE: registry/base-ui/examples/timezone-select.tsx
================================================
"use client";

import { useId, useMemo, useState } from "react";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/registry/base-ui/examples/command";

import { CheckIcon, ChevronDownIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";

import { cn } from "@/lib/utils";

export function TimezonSelectExample() {
  const id = useId();
  const [open, setOpen] = useState<boolean>(false);
  const [value, setValue] = useState<string>("Asia/Calcutta");

  const timezones = Intl.supportedValuesOf("timeZone");

  const formattedTimezones = useMemo(() => {
    return timezones
      .map((timezone) => {
        const formatter = new Intl.DateTimeFormat("en", {
          timeZone: timezone,
          timeZoneName: "shortOffset",
        });
        const parts = formatter.formatToParts(new Date());
        const offset = parts.find((part) => part.type === "timeZoneName")?.value || "";
        const modifiedOffset = offset === "GMT" ? "GMT+0" : offset;

        return {
          value: timezone,
          label: `(${modifiedOffset}) ${timezone.replace(/_/g, " ")}`,
          numericOffset: parseInt(offset.replace("GMT", "").replace("+", "") || "0"),
        };
      })
      .sort((a, b) => a.numericOffset - b.numericOffset);
  }, [timezones]);

  return (
    <div className="*:not-first:mt-2">
      <Label htmlFor={id}>Timezone select with search</Label>
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <Button
            id={id}
            variant="outline"
            role="combobox"
            aria-expanded={open}
            className="bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]"
          >
            <span className={cn("truncate", !value && "text-muted-foreground")}>
              {value ? formattedTimezones.find((timezone) => timezone.value === value)?.label : "Select timezone"}
            </span>
            <ChevronDownIcon size={16} className="text-muted-foreground/80 shrink-0" aria-hidden="true" />
          </Button>
        </PopoverTrigger>
        <PopoverContent className="border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0" align="start">
          <Command
            filter={(value, search) => {
              const normalizedValue = value.toLowerCase();
              const normalizedSearch = search.toLowerCase().replace(/\s+/g, "");
              return normalizedValue.includes(normalizedSearch) ? 1 : 0;
            }}
          >
            <CommandInput placeholder="Search timezone..." />
            <CommandList>
              <CommandEmpty>No timezone found.</CommandEmpty>
              <CommandGroup>
                {formattedTimezones.map(({ value: itemValue, label }) => (
                  <CommandItem
                    key={itemValue}
                    value={itemValue}
                    onSelect={(currentValue) => {
                      setValue(currentValue === value ? "" : currentValue);
                      setOpen(false);
                    }}
                  >
                    {label}
                    {value === itemValue && <CheckIcon size={16} className="ml-auto" />}
                  </CommandItem>
                ))}
              </CommandGroup>
            </CommandList>
          </Command>
        </PopoverContent>
      </Popover>
    </div>
  );
}


================================================
FILE: registry/base-ui/examples/usage-demo.tsx
================================================
import { ScrollArea } from "@/registry/base-ui/scroll-area";

export default function Example() {
  return (
    <ScrollArea className="h-[200px] w-[350px] rounded-md border p-4">
      Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the
      king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester.
      And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they
      couldn't help but laugh. And once they started laughing, they couldn't stop.
    </ScrollArea>
  );
}


================================================
FILE: registry/base-ui/examples/vertical-scroll.tsx
================================================
import * as React from "react";
import { ScrollArea } from "@/registry/base-ui/scroll-area";

import { Separator } from "@/components/ui/separator";

const tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);

export function ScrollAreaDemo() {
  return (
    <ScrollArea className="bg-background h-72 w-48 rounded-md border">
      <div className="p-4">
        <h4 className="mb-4 text-sm leading-none font-medium">Tags</h4>
        {tags.map((tag) => (
          <React.Fragment key={tag}>
            <div className="text-sm">{tag}</div>
            <Separator className="my-2" />
          </React.Fragment>
        ))}
      </div>
    </ScrollArea>
  );
}


================================================
FILE: registry/base-ui/scroll-area.tsx
================================================
"use client";

import * as React from "react";

import { ScrollArea as ScrollAreaPrimitive } from "@base-ui-components/react/scroll-area";

import { cn } from "@/lib/utils";

import { useTouchPrimary } from "@/hooks/use-has-primary-touch";

type Mask = {
  top: boolean;
  bottom: boolean;
  left: boolean;
  right: boolean;
};

export type ScrollAreaContextProps = {
  isTouch: boolean;
  type: "auto" | "always" | "scroll" | "hover";
};

const ScrollAreaContext = React.createContext<ScrollAreaContextProps>({
  isTouch: false,
  type: "hover",
});

const ScrollArea = React.forwardRef<
  React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
    type?: "auto" | "always" | "scroll" | "hover";
    viewportClassName?: string;
    /**
     * `maskHeight` is the height of the mask in pixels.
     * pass `0` to disable the mask
     * @default 30
     */
    maskHeight?: number;
    maskClassName?: string;
  }
>(({ className, children, type = "hover", maskHeight = 30, maskClassName, viewportClassName, ...props }, ref) => {
  const [showMask, setShowMask] = React.useState<Mask>({
    top: false,
    bottom: false,
    left: false,
    right: false,
  });

  const viewportRef = React.useRef<HTMLDivElement>(null);
  const isTouch = useTouchPrimary();

  const checkScrollability = React.useCallback(() => {
    const element = viewportRef.current;
    if (!element) return;

    const { scrollTop, scrollLeft, scrollWidth, clientWidth, scrollHeight, clientHeight } = element;
    setShowMask((prev) => ({
      ...prev,
      top: scrollTop > 0,
      bottom: scrollTop + clientHeight < scrollHeight - 1,
      left: scrollLeft > 0,
      right: scrollLeft + clientWidth < scrollWidth - 1,
    }));
  }, []);

  React.useEffect(() => {
    if (typeof window === "undefined") return;

    const element = viewportRef.current;
    if (!element) return;

    const controller = new AbortController();
    const { signal } = controller;

    const resizeObserver = new ResizeObserver(checkScrollability);
    resizeObserver.observe(element);

    element.addEventListener("scroll", checkScrollability, { signal });
    window.addEventListener("resize", checkScrollability, { signal });

    // Run an initial check whenever dependencies change (including pointer mode)
    checkScrollability();

    return () => {
      controller.abort();
      resizeObserver.disconnect();
    };
  }, [checkScrollability, isTouch]);

  return (
    <ScrollAreaContext.Provider value={{ isTouch, type }}>
      {isTouch ? (
        <div
          ref={ref}
          {...props}
          role="group"
          data-slot="scroll-area"
          aria-roledescription="scroll area"
          className={cn("relative overflow-hidden", className)}
        >
          <div ref={viewportRef} className={cn("size-full overflow-auto", viewportClassName)} tabIndex={0}>
            {children}
          </div>
          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}
        </div>
      ) : (
        <ScrollAreaPrimitive.Root
          ref={ref}
          data-slot="scroll-area"
          className={cn("relative overflow-hidden", viewportClassName, className)}
          {...props}
        >
          <ScrollAreaPrimitive.Viewport
            ref={viewportRef}
            data-slot="scroll-area-viewport"
            className={cn("focus-ring size-full rounded-[inherit]", viewportClassName)}
          >
            {children}
          </ScrollAreaPrimitive.Viewport>
          <ScrollBar />
          <ScrollAreaPrimitive.Corner />
          {maskHeight > 0 && <ScrollMask showMask={showMask} className={maskClassName} maskHeight={maskHeight} />}
        </ScrollAreaPrimitive.Root>
      )}
    </ScrollAreaContext.Provider>
  );
});

ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;

const ScrollBar = React.forwardRef<
  React.ComponentRef<typeof ScrollAreaPrimitive.Scrollbar>,
  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Scrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => {
  const { isTouch, type } = React.useContext(ScrollAreaContext);

  if (isTouch) return null;

  return (
    <ScrollAreaPrimitive.Scrollbar
      ref={ref}
      orientation={orientation}
      data-slot="scroll-area-scrollbar"
      className={cn(
        "hover:bg-muted dark:hover:bg-muted/50 flex touch-none p-px transition-[colors,opacity] duration-150 ease-out select-none",
        orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
        orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent px-1 pr-1.25",
        type === "hover" && "opacity-0 data-[hovering]:opacity-100",
        type === "scroll" && "opacity-0 data-[scrolling]:opacity-100",
        className
      )}
      {...props}
    >
      <ScrollAreaPrimitive.Thumb
        data-slot="scroll-area-thumb"
        className={cn(
          "bg-border relative flex-1 rounded-full transition-[scale]",
          orientation === "vertical" && "my-1 active:scale-y-95",
          orientation === "horizontal" && "active:scale-x-98"
        )}
      />
    </ScrollAreaPrimitive.Scrollbar>
  );
});

ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;

const ScrollMask = ({
  showMask,
  maskHeight,
  className,
  ...props
}: React.ComponentProps<"div"> & {
  showMask: Mask;
  maskHeight: number;
}) => {
  return (
    <>
      <div
        {...props}
        aria-hidden="true"
        style={
          {
            "--top-fade-height": showMask.top ? `${maskHeight}px` : "0px",
            "--bottom-fade-height": showMask.bottom ? `${maskHeight}px` : "0px",
          } as React.CSSProperties
        }
        className={cn(
          "pointer-events-none absolute inset-0 z-10",
          "before:absolute before:inset-x-0 before:top-0 before:transition-[height,opacity] before:duration-300 before:content-['']",
          "after:absolute after:inset-x-0 after:bottom-0 after:transition-[height,opacity] after:duration-300 after:content-['']",
          "before:h-(--top-fade-height) after:h-(--bottom-fade-height)",
          showMask.top ? "before:opacity-100" : "before:opacity-0",
          showMask.bottom ? "after:opacity-100" : "after:opacity-0",
          "before:from-background before:bg-gradient-to-b before:to-transparent",
          "after:from-background after:bg-gradient-to-t after:to-transparent",
          className
        )}
      />
      <div
        {...props}
        aria-hidden="true"
        style={
          {
            "--left-fade-width": showMask.left ? `${maskHeight}px` : "0px",
            "--right-fade-width": showMask.right ? `${maskHeight}px` : "0px",
          } as React.CSSProperties
        }
        className={cn(
          "pointer-events-none absolute inset-0 z-10",
          "before:absolute before:inset-y-0 before:left-0 before:transition-[width,opacity] before:duration-300 before:content-['']",
          "after:absolute after:inset-y-0 after:right-0 after:transition-[width,opacity] after:duration-300 after:content-['']",
          "before:w-(--left-fade-width) after:w-(--right-fade-width)",
          showMask.left ? "before:opacity-100" : "before:opacity-0",
          showMask.right ? "after:opacity-100" : "after:opacity-0",
          "before:from-background before:bg-gradient-to-r before:to-transparent",
          "after:from-background after:bg-gradient-to-l after:to-transparent",
          className
        )}
      />
    </>
  );
};

export { ScrollArea, ScrollBar };


================================================
FILE: registry/radix-ui/examples/command.tsx
================================================
"use client";

import * as React from "react";
import { ScrollArea } from "@/registry/radix-ui/scroll-area";

import { Command as CommandPrimitive } from "cmdk";
import { SearchIcon } from "lucide-react";

import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";

import { cn } from "@/lib/utils";

function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
  return (
    <CommandPrimitive
      data-slot="command"
      className={cn("bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-md", className)}
      {...props}
    />
  );
}

function CommandDialog({
  title = "Command Palette",
  description = "Search for a command to run...",
  children,
  ...props
}: React.ComponentProps<typeof Dialog> & {
  title?: string;
  description?: string;
}) {
  return (
    <Dialog {...props}>
      <DialogHeader className="sr-only">
        <DialogTitle>{title}</DialogTitle>
        <DialogDescription>{description}</DialogDescription>
      </DialogHeader>
      <DialogContent className="overflow-hidden p-0 sm:max-w-lg [&>button:last-child]:hidden">
        <Command className="[&_[cmdk-group-heading]]:text-muted-foreground max-h-[100svh] **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-3 [&_[cmdk-item]]:py-2">
          {children}
        </Command>
      </DialogContent>
    </Dialog>
  );
}

function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
  return (
    <div className="border-input flex items-center border-b px-5" cmdk-input-wrapper="">
      <SearchIcon size={20} className="text-muted-foreground/80 me-3" />
      <CommandPrimitive.Input
        data-slot="command-input-wrapper"
        className={cn(
          "placeholder:text-muted-foreground/70 flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        {...props}
      />
    </div>
  );
}

function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
  return (
    <ScrollArea
      maskClassName="before:from-popover after:from-popover"
      className="flex max-h-full flex-col overflow-hidden"
    >
      <CommandPrimitive.List data-slot="command-list" className={cn("max-h-80 flex-1", className)} {...props} />
    </ScrollArea>
  );
}

function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
  return <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} />;
}

function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
  return (
    <CommandPrimitive.Group
      data-slot="command-group"
      className={cn(
        "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-2 [&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-g
Download .txt
gitextract_dybf963_/

├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│   └── settings.json
├── LICENSE
├── README.md
├── app/
│   ├── globals.css
│   ├── layout.tsx
│   ├── manifest.ts
│   ├── page.tsx
│   ├── robots.ts
│   └── sitemap.ts
├── components/
│   ├── adaptive-mask.tsx
│   ├── anchor-heading.tsx
│   ├── cli-block.tsx
│   ├── code-block.tsx
│   ├── code-display-block.tsx
│   ├── component-preview.tsx
│   ├── copy-button.tsx
│   ├── examples.tsx
│   ├── features.tsx
│   ├── installation.tsx
│   ├── manual-installation-block.tsx
│   ├── micro-interactions.tsx
│   ├── native-touch.tsx
│   ├── navbar.tsx
│   ├── page-wide-scroll-mask.tsx
│   ├── posthog-provider.tsx
│   ├── render-preview.tsx
│   ├── theme-provider.tsx
│   ├── theme-toggle.tsx
│   ├── ui/
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── dialog.tsx
│   │   ├── label.tsx
│   │   ├── popover.tsx
│   │   ├── revola.tsx
│   │   ├── select.tsx
│   │   ├── separator.tsx
│   │   ├── skeleton.tsx
│   │   ├── tabs.tsx
│   │   └── tooltip.tsx
│   ├── usage.tsx
│   ├── variant-select.tsx
│   └── why.tsx
├── components.json
├── eslint.config.mjs
├── hooks/
│   ├── use-copy-button.ts
│   ├── use-has-primary-touch.tsx
│   ├── use-media-query.tsx
│   └── use-meta-color.ts
├── lib/
│   ├── adaptive-mask-animation.ts
│   ├── code-highlight.ts
│   ├── package-manager-store.ts
│   ├── package-manager-utils.ts
│   ├── utils.ts
│   └── variant-store.ts
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public/
│   ├── r/
│   │   ├── command-base.json
│   │   ├── command-radix.json
│   │   ├── horizontal-scroll-demo-base.json
│   │   ├── horizontal-scroll-demo-radix.json
│   │   ├── lina-base.json
│   │   ├── lina-radix.json
│   │   ├── timezone-select-demo-base.json
│   │   ├── timezone-select-demo-radix.json
│   │   ├── vertical-scroll-demo-base.json
│   │   └── vertical-scroll-demo-radix.json
│   └── site.webmanifest
├── registry/
│   ├── base-ui/
│   │   ├── examples/
│   │   │   ├── command.tsx
│   │   │   ├── horizontal-scroll.tsx
│   │   │   ├── timezone-select.tsx
│   │   │   ├── usage-demo.tsx
│   │   │   └── vertical-scroll.tsx
│   │   └── scroll-area.tsx
│   ├── radix-ui/
│   │   ├── examples/
│   │   │   ├── command.tsx
│   │   │   ├── horizontal-scroll.tsx
│   │   │   ├── timezone-select.tsx
│   │   │   ├── usage-demo.tsx
│   │   │   └── vertical-scroll.tsx
│   │   └── scroll-area.tsx
│   └── registry.ts
├── registry.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (140 symbols across 60 files)

FILE: app/layout.tsx
  function RootLayout (line 77) | function RootLayout({

FILE: app/manifest.ts
  function manifest (line 3) | function manifest(): MetadataRoute.Manifest {

FILE: app/page.tsx
  function Home (line 11) | async function Home() {

FILE: app/robots.ts
  function robots (line 6) | function robots(): MetadataRoute.Robots {

FILE: app/sitemap.ts
  function sitemap (line 6) | function sitemap(): MetadataRoute.Sitemap {

FILE: components/adaptive-mask.tsx
  function AdaptiveMask (line 17) | function AdaptiveMask() {

FILE: components/anchor-heading.tsx
  type AnchorHeadingProps (line 5) | type AnchorHeadingProps = ComponentProps<"h2">;
  function AnchorHeading (line 7) | function AnchorHeading({ className, id, children, ...props }: AnchorHead...

FILE: components/cli-block.tsx
  type CLIBlockProps (line 19) | type CLIBlockProps = {
  function CLIBlock (line 25) | function CLIBlock({ name, command }: CLIBlockProps) {
  function CLIBlockContent (line 33) | function CLIBlockContent({ name, command }: CLIBlockProps) {

FILE: components/code-block.tsx
  type CodeBlockProps (line 14) | type CodeBlockProps = {
  function CodeBlock (line 22) | function CodeBlock({ code, lang, initial, preHighlighted, className }: C...

FILE: components/code-display-block.tsx
  type CodeData (line 10) | type CodeData = {
  type VariantData (line 15) | type VariantData = {
  type CodeDisplaySectionProps (line 24) | type CodeDisplaySectionProps = {
  function CodeDisplaySection (line 30) | function CodeDisplaySection({

FILE: components/component-preview.tsx
  type ComponentDetailsProps (line 15) | type ComponentDetailsProps = {
  function ComponentPreview (line 23) | async function ComponentPreview({

FILE: components/copy-button.tsx
  function CopyButton (line 14) | function CopyButton({

FILE: components/examples.tsx
  function Examples (line 5) | function Examples() {

FILE: components/features.tsx
  function Features (line 5) | function Features() {

FILE: components/installation.tsx
  function Installation (line 8) | async function Installation() {

FILE: components/manual-installation-block.tsx
  type CodeData (line 11) | type CodeData = {
  type VariantData (line 16) | type VariantData = {
  type ManualInstallationSectionProps (line 27) | type ManualInstallationSectionProps = {
  function ManualInstallationBlock (line 33) | function ManualInstallationBlock({

FILE: components/micro-interactions.tsx
  function MicroInteractions (line 8) | function MicroInteractions() {

FILE: components/native-touch.tsx
  function NativeTouch (line 8) | function NativeTouch() {

FILE: components/navbar.tsx
  function Navbar (line 7) | function Navbar() {

FILE: components/page-wide-scroll-mask.tsx
  function PageWideScrollMask (line 6) | function PageWideScrollMask() {

FILE: components/posthog-provider.tsx
  function PostHogProvider (line 8) | function PostHogProvider({ children }: { children: React.ReactNode }) {

FILE: components/render-preview.tsx
  type RenderPreviewProps (line 8) | type RenderPreviewProps = {
  function RenderPreview (line 12) | function RenderPreview({ name }: RenderPreviewProps) {

FILE: components/theme-provider.tsx
  function ThemeProvider (line 7) | function ThemeProvider({ children, ...props }: React.ComponentProps<type...

FILE: components/theme-toggle.tsx
  function ThemeToggle (line 11) | function ThemeToggle() {

FILE: components/ui/button.tsx
  function Button (line 36) | function Button({

FILE: components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 15) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 28) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 32) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 36) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 46) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 50) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: components/ui/dialog.tsx
  function Dialog (line 10) | function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitiv...
  function DialogTrigger (line 14) | function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogP...
  function DialogPortal (line 18) | function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPr...
  function DialogClose (line 22) | function DialogClose({ ...props }: React.ComponentProps<typeof DialogPri...
  function DialogOverlay (line 26) | function DialogOverlay({ className, ...props }: React.ComponentProps<typ...
  function DialogContent (line 39) | function DialogContent({
  function DialogHeader (line 73) | function DialogHeader({ className, ...props }: React.ComponentProps<"div...
  function DialogFooter (line 83) | function DialogFooter({ className, ...props }: React.ComponentProps<"div...
  function DialogTitle (line 93) | function DialogTitle({ className, ...props }: React.ComponentProps<typeo...
  function DialogDescription (line 103) | function DialogDescription({ className, ...props }: React.ComponentProps...

FILE: components/ui/label.tsx
  function Label (line 8) | function Label({ className, ...props }: React.ComponentProps<typeof Labe...

FILE: components/ui/popover.tsx
  function Popover (line 8) | function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimit...
  function PopoverTrigger (line 12) | function PopoverTrigger({ ...props }: React.ComponentProps<typeof Popove...
  function PopoverContent (line 16) | function PopoverContent({
  function PopoverAnchor (line 44) | function PopoverAnchor({ ...props }: React.ComponentProps<typeof Popover...

FILE: components/ui/revola.tsx
  type ResponsiveDialogProps (line 14) | type ResponsiveDialogProps = React.ComponentProps<typeof DrawerPrimitive...
  type ResponsiveDialogContextProps (line 16) | type ResponsiveDialogContextProps = {
  type ResponsiveDialogProviderProps (line 25) | type ResponsiveDialogProviderProps = {
  constant MOBILE_BREAKPOINT (line 30) | const MOBILE_BREAKPOINT = "(min-width: 640px)";

FILE: components/ui/select.tsx
  function Select (line 10) | function Select({ ...props }: React.ComponentProps<typeof SelectPrimitiv...
  function SelectGroup (line 14) | function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPri...
  function SelectValue (line 18) | function SelectValue({ ...props }: React.ComponentProps<typeof SelectPri...
  function SelectTrigger (line 22) | function SelectTrigger({
  function SelectContent (line 48) | function SelectContent({
  function SelectLabel (line 83) | function SelectLabel({ className, ...props }: React.ComponentProps<typeo...
  function SelectItem (line 93) | function SelectItem({ className, children, ...props }: React.ComponentPr...
  function SelectSeparator (line 113) | function SelectSeparator({ className, ...props }: React.ComponentProps<t...
  function SelectScrollUpButton (line 123) | function SelectScrollUpButton({ className, ...props }: React.ComponentPr...
  function SelectScrollDownButton (line 135) | function SelectScrollDownButton({

FILE: components/ui/separator.tsx
  function Separator (line 9) | function Separator({

FILE: components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {

FILE: components/ui/tabs.tsx
  function Tabs (line 10) | function Tabs({ className, ...props }: React.ComponentProps<typeof TabsP...
  function TabsList (line 14) | function TabsList({ className, ...props }: React.ComponentProps<typeof T...
  function TabsTrigger (line 27) | function TabsTrigger({ className, onClick, value, ...props }: React.Comp...
  function TabsIndicator (line 49) | function TabsIndicator({ className, ...props }: React.ComponentProps<typ...
  function TabsContent (line 62) | function TabsContent({ className, ...props }: React.ComponentProps<typeo...

FILE: components/ui/tooltip.tsx
  function TooltipProvider (line 9) | function TooltipProvider({ delayDuration = 0, ...props }: React.Componen...
  function Tooltip (line 13) | function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimit...
  function TooltipTrigger (line 21) | function TooltipTrigger({ ...props }: React.ComponentProps<typeof Toolti...
  function TooltipContent (line 25) | function TooltipContent({

FILE: components/usage.tsx
  function Usage (line 6) | async function Usage() {

FILE: components/variant-select.tsx
  type VariantSelectProps (line 13) | type VariantSelectProps = {
  function VariantSelect (line 17) | function VariantSelect({ size = "default" }: VariantSelectProps) {

FILE: components/why.tsx
  function Why (line 3) | function Why() {

FILE: hooks/use-copy-button.ts
  function useCopyButton (line 5) | function useCopyButton(onCopy: () => void): [checked: boolean, onClick: ...

FILE: hooks/use-has-primary-touch.tsx
  function useTouchPrimary (line 3) | function useTouchPrimary() {

FILE: hooks/use-meta-color.ts
  constant META_THEME_COLORS (line 5) | const META_THEME_COLORS = {
  function useMetaColor (line 10) | function useMetaColor() {

FILE: lib/adaptive-mask-animation.ts
  constant SCROLL_CONFIG (line 4) | const SCROLL_CONFIG = {
  type AnimationPhase (line 19) | type AnimationPhase = {

FILE: lib/code-highlight.ts
  function highlight (line 12) | async function highlight(code: string, lang: BundledLanguage) {
  function getComponentCode (line 46) | async function getComponentCode(

FILE: lib/package-manager-store.ts
  type PackageManager (line 7) | type PackageManager = "pnpm" | "npm" | "bun" | "yarn";
  type PackageManagerState (line 9) | type PackageManagerState = {

FILE: lib/package-manager-utils.ts
  type CommandMapping (line 3) | type CommandMapping = {
  constant NPX_COMMAND_MAPPINGS (line 10) | const NPX_COMMAND_MAPPINGS: CommandMapping = {
  constant PACKAGE_COMMAND_MAPPINGS (line 17) | const PACKAGE_COMMAND_MAPPINGS: CommandMapping = {
  function isNpxCommand (line 24) | function isNpxCommand(command: string): boolean {
  function convertNpmCommand (line 28) | function convertNpmCommand(npmCommand: string): Record<PackageManager, s...
  function convertNpxStyle (line 57) | function convertNpxStyle(parts: string[]): Record<PackageManager, string> {

FILE: lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: lib/variant-store.ts
  type Variant (line 7) | type Variant = "base" | "radix";
  type VariantState (line 9) | type VariantState = {

FILE: registry/base-ui/examples/command.tsx
  function Command (line 13) | function Command({ className, ...props }: React.ComponentProps<typeof Co...
  function CommandDialog (line 23) | function CommandDialog({
  function CommandInput (line 47) | function CommandInput({ className, ...props }: React.ComponentProps<type...
  function CommandList (line 63) | function CommandList({ className, ...props }: React.ComponentProps<typeo...
  function CommandEmpty (line 74) | function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandP...
  function CommandGroup (line 78) | function CommandGroup({ className, ...props }: React.ComponentProps<type...
  function CommandSeparator (line 91) | function CommandSeparator({ className, ...props }: React.ComponentProps<...
  function CommandItem (line 101) | function CommandItem({ className, ...props }: React.ComponentProps<typeo...
  function CommandShortcut (line 114) | function CommandShortcut({ className, ...props }: React.ComponentProps<"...

FILE: registry/base-ui/examples/horizontal-scroll.tsx
  type Artwork (line 4) | interface Artwork {
  function ScrollAreaHorizontalDemo (line 24) | function ScrollAreaHorizontalDemo() {

FILE: registry/base-ui/examples/timezone-select.tsx
  function TimezonSelectExample (line 21) | function TimezonSelectExample() {

FILE: registry/base-ui/examples/usage-demo.tsx
  function Example (line 3) | function Example() {

FILE: registry/base-ui/examples/vertical-scroll.tsx
  function ScrollAreaDemo (line 8) | function ScrollAreaDemo() {

FILE: registry/base-ui/scroll-area.tsx
  type Mask (line 11) | type Mask = {
  type ScrollAreaContextProps (line 18) | type ScrollAreaContextProps = {

FILE: registry/radix-ui/examples/command.tsx
  function Command (line 13) | function Command({ className, ...props }: React.ComponentProps<typeof Co...
  function CommandDialog (line 23) | function CommandDialog({
  function CommandInput (line 47) | function CommandInput({ className, ...props }: React.ComponentProps<type...
  function CommandList (line 63) | function CommandList({ className, ...props }: React.ComponentProps<typeo...
  function CommandEmpty (line 74) | function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandP...
  function CommandGroup (line 78) | function CommandGroup({ className, ...props }: React.ComponentProps<type...
  function CommandSeparator (line 91) | function CommandSeparator({ className, ...props }: React.ComponentProps<...
  function CommandItem (line 101) | function CommandItem({ className, ...props }: React.ComponentProps<typeo...
  function CommandShortcut (line 114) | function CommandShortcut({ className, ...props }: React.ComponentProps<"...

FILE: registry/radix-ui/examples/horizontal-scroll.tsx
  type Artwork (line 4) | interface Artwork {
  function ScrollAreaHorizontalDemo (line 24) | function ScrollAreaHorizontalDemo() {

FILE: registry/radix-ui/examples/timezone-select.tsx
  function TimezoneSelectExample (line 21) | function TimezoneSelectExample() {

FILE: registry/radix-ui/examples/usage-demo.tsx
  function Example (line 3) | function Example() {

FILE: registry/radix-ui/examples/vertical-scroll.tsx
  function ScrollAreaDemo (line 8) | function ScrollAreaDemo() {

FILE: registry/radix-ui/scroll-area.tsx
  type Mask (line 12) | type Mask = {

FILE: registry/registry.ts
  type RegistryKeys (line 10) | type RegistryKeys = (typeof registryKeys)[number];
  type Registry (line 12) | interface Registry {
Condensed preview — 86 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (231K chars).
[
  {
    "path": ".gitignore",
    "chars": 480,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": ".prettierignore",
    "chars": 55,
    "preview": ".next\npackage-lock.json\npnpm-lock.yaml\ncomponents.json\n"
  },
  {
    "path": ".prettierrc.json",
    "chars": 1077,
    "preview": "{\n  \"semi\": true,\n  \"singleQuote\": false,\n  \"tabWidth\": 2,\n  \"printWidth\": 120,\n  \"bracketSpacing\": true,\n  \"trailingCom"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 676,
    "preview": "{\n  \"typescript.tsdk\": \"node_modules\\\\typescript\\\\lib\",\n  \"typescript.preferences.importModuleSpecifier\": \"non-relative\""
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2025 Sameer Singh\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 2171,
    "preview": "# Lina\n\nThe Adaptive Scroll Area for Modern UIs\n\nA responsive scroll area that feels native on touch devices, offering c"
  },
  {
    "path": "app/globals.css",
    "chars": 5354,
    "preview": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n  --breakpoint-"
  },
  {
    "path": "app/layout.tsx",
    "chars": 3061,
    "preview": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono, Inter } from \"next/font/google\";\n\nimport \"@/app/global"
  },
  {
    "path": "app/manifest.ts",
    "chars": 626,
    "preview": "import type { MetadataRoute } from \"next\";\n\nexport default function manifest(): MetadataRoute.Manifest {\n  return {\n    "
  },
  {
    "path": "app/page.tsx",
    "chars": 2278,
    "preview": "import Link from \"next/link\";\n\nimport Examples from \"@/components/examples\";\nimport Features from \"@/components/features"
  },
  {
    "path": "app/robots.ts",
    "chars": 372,
    "preview": "import type { MetadataRoute } from \"next\";\n\nconst baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? \"http://localhost:3000\";"
  },
  {
    "path": "app/sitemap.ts",
    "chars": 463,
    "preview": "import type { MetadataRoute } from \"next\";\n\nconst baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? \"http://localhost:3000\";"
  },
  {
    "path": "components/adaptive-mask.tsx",
    "chars": 5170,
    "preview": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { motion, useMotionValue, useSpring } from \"m"
  },
  {
    "path": "components/anchor-heading.tsx",
    "chars": 711,
    "preview": "import type { ComponentProps } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype AnchorHeadingProps = ComponentProp"
  },
  {
    "path": "components/cli-block.tsx",
    "chars": 3587,
    "preview": "\"use client\";\n\nimport { Suspense, useEffect, useMemo } from \"react\";\nimport { ScrollArea, ScrollBar } from \"@/registry/r"
  },
  {
    "path": "components/code-block.tsx",
    "chars": 2449,
    "preview": "\"use client\";\n\nimport { useLayoutEffect, useRef, useState, type JSX } from \"react\";\nimport { ScrollArea, ScrollBar } fro"
  },
  {
    "path": "components/code-display-block.tsx",
    "chars": 1416,
    "preview": "\"use client\";\n\nimport type { JSX } from \"react\";\nimport type { BundledLanguage } from \"shiki/bundle/web\";\n\nimport CodeBl"
  },
  {
    "path": "components/component-preview.tsx",
    "chars": 2783,
    "preview": "import React from \"react\";\nimport { Index, type RegistryKeys } from \"@/registry/registry\";\nimport type { BundledLanguage"
  },
  {
    "path": "components/copy-button.tsx",
    "chars": 1503,
    "preview": "\"use client\";\n\nimport type { ButtonHTMLAttributes } from \"react\";\n\nimport { Check, Copy } from \"lucide-react\";\n\nimport {"
  },
  {
    "path": "components/examples.tsx",
    "chars": 2635,
    "preview": "import AnchorHeading from \"@/components/anchor-heading\";\nimport CLIBlock from \"@/components/cli-block\";\nimport Component"
  },
  {
    "path": "components/features.tsx",
    "chars": 450,
    "preview": "import AdaptiveMask from \"@/components/adaptive-mask\";\nimport MicroInteractions from \"@/components/micro-interactions\";\n"
  },
  {
    "path": "components/installation.tsx",
    "chars": 2676,
    "preview": "import AnchorHeading from \"@/components/anchor-heading\";\nimport CLIBlock from \"@/components/cli-block\";\nimport ManualIns"
  },
  {
    "path": "components/manual-installation-block.tsx",
    "chars": 3363,
    "preview": "\"use client\";\n\nimport type { JSX } from \"react\";\nimport type { BundledLanguage } from \"shiki/bundle/web\";\n\nimport CLIBlo"
  },
  {
    "path": "components/micro-interactions.tsx",
    "chars": 4898,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"motion/react\";\n\nimport { Card, CardC"
  },
  {
    "path": "components/native-touch.tsx",
    "chars": 5893,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"motion/react\";\n\nimport { Card, CardC"
  },
  {
    "path": "components/navbar.tsx",
    "chars": 2755,
    "preview": "import Link from \"next/link\";\n\nimport ThemeToggle from \"@/components/theme-toggle\";\nimport { Button, buttonVariants } fr"
  },
  {
    "path": "components/page-wide-scroll-mask.tsx",
    "chars": 2134,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"motion/react\";\n\nexport default funct"
  },
  {
    "path": "components/posthog-provider.tsx",
    "chars": 790,
    "preview": "\"use client\";\n\nimport { useEffect } from \"react\";\n\nimport posthog from \"posthog-js\";\nimport { PostHogProvider as PHProvi"
  },
  {
    "path": "components/render-preview.tsx",
    "chars": 754,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { Index, type RegistryKeys } from \"@/registry/registry\";\n\nimport { useV"
  },
  {
    "path": "components/theme-provider.tsx",
    "chars": 296,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { ThemeProvider as NextThemesProvider } from \"next-themes\";\n\nexpo"
  },
  {
    "path": "components/theme-toggle.tsx",
    "chars": 931,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useTheme } from \"next-themes\";\n\nimport { Button } from \"@/compo"
  },
  {
    "path": "components/ui/button.tsx",
    "chars": 2083,
    "preview": "import * as React from \"react\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"cl"
  },
  {
    "path": "components/ui/card.tsx",
    "chars": 1818,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Card({ className, ...props }: React.Compone"
  },
  {
    "path": "components/ui/dialog.tsx",
    "chars": 3956,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIco"
  },
  {
    "path": "components/ui/label.tsx",
    "chars": 597,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Label as LabelPrimitive } from \"radix-ui\";\n\nimport { cn } from \""
  },
  {
    "path": "components/ui/popover.tsx",
    "chars": 1809,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\n\nimport { cn } fr"
  },
  {
    "path": "components/ui/revola.tsx",
    "chars": 12423,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } "
  },
  {
    "path": "components/ui/select.tsx",
    "chars": 6199,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { Chec"
  },
  {
    "path": "components/ui/separator.tsx",
    "chars": 706,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\n\nimport"
  },
  {
    "path": "components/ui/skeleton.tsx",
    "chars": 247,
    "preview": "import { cn } from \"@/lib/utils\";\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n  return <d"
  },
  {
    "path": "components/ui/tabs.tsx",
    "chars": 2479,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Tabs as TabsPrimitive } from \"@base-ui-components/react/tabs\";\n"
  },
  {
    "path": "components/ui/tooltip.tsx",
    "chars": 1861,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { c"
  },
  {
    "path": "components/usage.tsx",
    "chars": 1050,
    "preview": "import AnchorHeading from \"@/components/anchor-heading\";\nimport CodeDisplaySection from \"@/components/code-display-block"
  },
  {
    "path": "components/variant-select.tsx",
    "chars": 1542,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport posthog from \"posthog-js\";\n\nimport { Select, SelectC"
  },
  {
    "path": "components/why.tsx",
    "chars": 3203,
    "preview": "import AnchorHeading from \"@/components/anchor-heading\";\n\nexport default function Why() {\n  return (\n    <section classN"
  },
  {
    "path": "components.json",
    "chars": 423,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {"
  },
  {
    "path": "eslint.config.mjs",
    "chars": 393,
    "preview": "import { dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { FlatCompat } from \"@eslint/eslintrc\";\n\ncon"
  },
  {
    "path": "hooks/use-copy-button.ts",
    "chars": 852,
    "preview": "\"use client\";\n\nimport { useCallback, useEffect, useRef, useState, type MouseEventHandler } from \"react\";\n\nexport functio"
  },
  {
    "path": "hooks/use-has-primary-touch.tsx",
    "chars": 823,
    "preview": "import { useEffect, useState } from \"react\";\n\nexport function useTouchPrimary() {\n  const [isTouchPrimary, setIsTouchPri"
  },
  {
    "path": "hooks/use-media-query.tsx",
    "chars": 543,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nconst useMediaQuery = (query: string) => {\n  const [matches"
  },
  {
    "path": "hooks/use-meta-color.ts",
    "chars": 592,
    "preview": "import * as React from \"react\";\n\nimport { useTheme } from \"next-themes\";\n\nexport const META_THEME_COLORS = {\n  light: \"#"
  },
  {
    "path": "lib/adaptive-mask-animation.ts",
    "chars": 5148,
    "preview": "import { MotionValue } from \"motion/react\";\n\n// Configuration constants\nexport const SCROLL_CONFIG = {\n  vertical: { max"
  },
  {
    "path": "lib/code-highlight.ts",
    "chars": 2478,
    "preview": "\"use server\";\n\nimport \"server-only\";\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport type { JSX } from \"r"
  },
  {
    "path": "lib/package-manager-store.ts",
    "chars": 744,
    "preview": "\"use client\";\n\nimport { persist } from \"zustand/middleware\";\n\nimport { create } from \"zustand\";\n\ntype PackageManager = \""
  },
  {
    "path": "lib/package-manager-utils.ts",
    "chars": 1793,
    "preview": "import type { PackageManager } from \"@/lib/package-manager-store\";\n\ntype CommandMapping = {\n  npm: string[];\n  pnpm: str"
  },
  {
    "path": "lib/utils.ts",
    "chars": 169,
    "preview": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: C"
  },
  {
    "path": "lib/variant-store.ts",
    "chars": 523,
    "preview": "\"use client\";\n\nimport { persist } from \"zustand/middleware\";\n\nimport { create } from \"zustand\";\n\ntype Variant = \"base\" |"
  },
  {
    "path": "next.config.ts",
    "chars": 428,
    "preview": "import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n  images: {\n    remotePatterns: [{ hostname: \""
  },
  {
    "path": "package.json",
    "chars": 2011,
    "preview": "{\n  \"name\": \"lina\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Lina is a drop‑in replacement for shadcn/ui's `ScrollArea` w"
  },
  {
    "path": "postcss.config.mjs",
    "chars": 81,
    "preview": "const config = {\n  plugins: [\"@tailwindcss/postcss\"],\n};\n\nexport default config;\n"
  },
  {
    "path": "public/r/command-base.json",
    "chars": 5622,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"command-base\",\n  \"type\": \"registry:ui\",\n  \""
  },
  {
    "path": "public/r/command-radix.json",
    "chars": 5628,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"command-radix\",\n  \"type\": \"registry:ui\",\n  "
  },
  {
    "path": "public/r/horizontal-scroll-demo-base.json",
    "chars": 2227,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"horizontal-scroll-demo-base\",\n  \"type\": \"re"
  },
  {
    "path": "public/r/horizontal-scroll-demo-radix.json",
    "chars": 2233,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"horizontal-scroll-demo-radix\",\n  \"type\": \"r"
  },
  {
    "path": "public/r/lina-base.json",
    "chars": 10033,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"lina-base\",\n  \"type\": \"registry:ui\",\n  \"tit"
  },
  {
    "path": "public/r/lina-radix.json",
    "chars": 9915,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"lina-radix\",\n  \"type\": \"registry:ui\",\n  \"ti"
  },
  {
    "path": "public/r/timezone-select-demo-base.json",
    "chars": 4643,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"timezone-select-demo-base\",\n  \"type\": \"regi"
  },
  {
    "path": "public/r/timezone-select-demo-radix.json",
    "chars": 4648,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"timezone-select-demo-radix\",\n  \"type\": \"reg"
  },
  {
    "path": "public/r/vertical-scroll-demo-base.json",
    "chars": 1279,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"vertical-scroll-demo-base\",\n  \"type\": \"regi"
  },
  {
    "path": "public/r/vertical-scroll-demo-radix.json",
    "chars": 1285,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n  \"name\": \"vertical-scroll-demo-radix\",\n  \"type\": \"reg"
  },
  {
    "path": "public/site.webmanifest",
    "chars": 427,
    "preview": "{\n  \"name2\": \"Lina\",\n  \"short_name\": \"Lina\",\n  \"icons\": [\n    {\n      \"src\": \"/android-chrome-192x192.png\",\n      \"sizes"
  },
  {
    "path": "registry/base-ui/examples/command.tsx",
    "chars": 4608,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ScrollArea } from \"@/registry/base-ui/scroll-area\";\n\nimport { Co"
  },
  {
    "path": "registry/base-ui/examples/horizontal-scroll.tsx",
    "chars": 1539,
    "preview": "import Image from \"next/image\";\nimport { ScrollArea, ScrollBar } from \"@/registry/base-ui/scroll-area\";\n\nexport interfac"
  },
  {
    "path": "registry/base-ui/examples/timezone-select.tsx",
    "chars": 3634,
    "preview": "\"use client\";\n\nimport { useId, useMemo, useState } from \"react\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  C"
  },
  {
    "path": "registry/base-ui/examples/usage-demo.tsx",
    "chars": 648,
    "preview": "import { ScrollArea } from \"@/registry/base-ui/scroll-area\";\n\nexport default function Example() {\n  return (\n    <Scroll"
  },
  {
    "path": "registry/base-ui/examples/vertical-scroll.tsx",
    "chars": 693,
    "preview": "import * as React from \"react\";\nimport { ScrollArea } from \"@/registry/base-ui/scroll-area\";\n\nimport { Separator } from "
  },
  {
    "path": "registry/base-ui/scroll-area.tsx",
    "chars": 7630,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui-components/r"
  },
  {
    "path": "registry/radix-ui/examples/command.tsx",
    "chars": 4609,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ScrollArea } from \"@/registry/radix-ui/scroll-area\";\n\nimport { C"
  },
  {
    "path": "registry/radix-ui/examples/horizontal-scroll.tsx",
    "chars": 1540,
    "preview": "import Image from \"next/image\";\nimport { ScrollArea, ScrollBar } from \"@/registry/radix-ui/scroll-area\";\n\nexport interfa"
  },
  {
    "path": "registry/radix-ui/examples/timezone-select.tsx",
    "chars": 3636,
    "preview": "\"use client\";\n\nimport { useId, useMemo, useState } from \"react\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  C"
  },
  {
    "path": "registry/radix-ui/examples/usage-demo.tsx",
    "chars": 649,
    "preview": "import { ScrollArea } from \"@/registry/radix-ui/scroll-area\";\n\nexport default function Example() {\n  return (\n    <Scrol"
  },
  {
    "path": "registry/radix-ui/examples/vertical-scroll.tsx",
    "chars": 694,
    "preview": "import * as React from \"react\";\nimport { ScrollArea } from \"@/registry/radix-ui/scroll-area\";\n\nimport { Separator } from"
  },
  {
    "path": "registry/radix-ui/scroll-area.tsx",
    "chars": 7549,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\";\n\nimp"
  },
  {
    "path": "registry/registry.ts",
    "chars": 2290,
    "preview": "import * as React from \"react\";\nimport { ScrollAreaHorizontalDemo as ScrollAreaHorizontalBaseUIDemo } from \"@/registry/b"
  },
  {
    "path": "registry.json",
    "chars": 5091,
    "preview": "{\n  \"name\": \"lina\",\n  \"homepage\": \"https://lina.sameer.sh\",\n  \"$schema\": \"https://ui.shadcn.com/schema/registry.json\",\n "
  },
  {
    "path": "tsconfig.json",
    "chars": 652,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    "
  }
]

About this extraction

This page contains the full source code of the SameerJS6/lina GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 86 files (208.3 KB), approximately 56.9k tokens, and a symbol index with 140 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!